use crate::codegen::{
cfg::{ControlFlowGraph, Instr, InternalCallTy, ReturnCode},
Expression,
};
use crate::emit::binary::Binary;
use crate::emit::cfg::{create_block, BasicBlock, Work};
use crate::emit::expression::expression;
use crate::emit::TargetRuntime;
use crate::sema::ast::{Contract, Namespace, RetrieveType, Type};
use crate::Target;
use inkwell::types::BasicType;
use inkwell::values::{
BasicMetadataValueEnum, BasicValueEnum, CallableValue, FunctionValue, IntValue,
};
use inkwell::{AddressSpace, IntPredicate};
use num_traits::ToPrimitive;
use std::collections::{HashMap, VecDeque};
pub(super) fn process_instruction<'a, T: TargetRuntime<'a> + ?Sized>(
target: &mut T,
ins: &Instr,
bin: &Binary<'a>,
w: &mut Work<'a>,
function: FunctionValue<'a>,
ns: &Namespace,
cfg: &ControlFlowGraph,
work: &mut VecDeque<Work<'a>>,
blocks: &mut HashMap<usize, BasicBlock<'a>>,
contract: &Contract,
) {
match ins {
Instr::Nop => (),
Instr::Return { value } if value.is_empty() => {
bin.builder
.build_return(Some(&bin.return_values[&ReturnCode::Success]));
}
Instr::Return { value } => {
let returns_offset = cfg.params.len();
for (i, val) in value.iter().enumerate() {
let arg = function.get_nth_param((returns_offset + i) as u32).unwrap();
let retval = expression(target, bin, val, &w.vars, function, ns);
bin.builder.build_store(arg.into_pointer_value(), retval);
}
bin.builder
.build_return(Some(&bin.return_values[&ReturnCode::Success]));
}
Instr::Set { res, expr, .. } => {
if let Expression::Undefined(expr_type) = expr {
if let Some(default_expr) = expr_type.default(ns) {
w.vars.get_mut(res).unwrap().value =
expression(target, bin, &default_expr, &w.vars, function, ns);
}
} else {
w.vars.get_mut(res).unwrap().value =
expression(target, bin, expr, &w.vars, function, ns);
}
}
Instr::Branch { block: dest } => {
let pos = bin.builder.get_insert_block().unwrap();
let bb = add_or_retrieve_block(*dest, pos, bin, function, blocks, work, w, cfg, ns);
bin.builder.position_at_end(pos);
bin.builder.build_unconditional_branch(bb);
}
Instr::Store { dest, data } => {
let value_ref = expression(target, bin, data, &w.vars, function, ns);
let dest_ref =
expression(target, bin, dest, &w.vars, function, ns).into_pointer_value();
bin.builder.build_store(dest_ref, value_ref);
}
Instr::BranchCond {
cond,
true_block: true_,
false_block: false_,
} => {
let cond = expression(target, bin, cond, &w.vars, function, ns);
let pos = bin.builder.get_insert_block().unwrap();
let bb_true =
add_or_retrieve_block(*true_, pos, bin, function, blocks, work, w, cfg, ns);
let bb_false =
add_or_retrieve_block(*false_, pos, bin, function, blocks, work, w, cfg, ns);
bin.builder.position_at_end(pos);
bin.builder
.build_conditional_branch(cond.into_int_value(), bb_true, bb_false);
}
Instr::LoadStorage { res, ty, storage } => {
let mut slot = expression(target, bin, storage, &w.vars, function, ns).into_int_value();
w.vars.get_mut(res).unwrap().value =
target.storage_load(bin, ty, &mut slot, function, ns);
}
Instr::ClearStorage { ty, storage } => {
let mut slot = expression(target, bin, storage, &w.vars, function, ns).into_int_value();
target.storage_delete(bin, ty, &mut slot, function, ns);
}
Instr::SetStorage { ty, value, storage } => {
let value = expression(target, bin, value, &w.vars, function, ns);
let mut slot = expression(target, bin, storage, &w.vars, function, ns).into_int_value();
target.storage_store(bin, ty, true, &mut slot, value, function, ns);
}
Instr::SetStorageBytes {
storage,
value,
offset,
} => {
let value = expression(target, bin, value, &w.vars, function, ns);
let slot = expression(target, bin, storage, &w.vars, function, ns).into_int_value();
let offset = expression(target, bin, offset, &w.vars, function, ns).into_int_value();
target.set_storage_bytes_subscript(bin, function, slot, offset, value.into_int_value());
}
Instr::PushStorage {
res,
ty,
storage,
value,
} => {
let val = value
.as_ref()
.map(|expr| expression(target, bin, expr, &w.vars, function, ns));
let slot = expression(target, bin, storage, &w.vars, function, ns).into_int_value();
w.vars.get_mut(res).unwrap().value =
target.storage_push(bin, function, ty, slot, val, ns);
}
Instr::PopStorage { res, ty, storage } => {
let slot = expression(target, bin, storage, &w.vars, function, ns).into_int_value();
let value = target.storage_pop(bin, function, ty, slot, res.is_some(), ns);
if let Some(res) = res {
w.vars.get_mut(res).unwrap().value = value.unwrap();
}
}
Instr::PushMemory {
res,
ty,
array,
value,
} => {
let arr = w.vars[array].value;
let llvm_ty = bin.llvm_type(ty, ns);
let elem_ty = ty.array_elem();
let llvm_elem_ty = bin.llvm_field_ty(&elem_ty, ns);
let elem_size = llvm_elem_ty
.size_of()
.unwrap()
.const_cast(bin.context.i32_type(), false);
let len = bin.vector_len(arr);
let new_len =
bin.builder
.build_int_add(len, bin.context.i32_type().const_int(1, false), "");
let vec_size = bin
.module
.get_struct_type("struct.vector")
.unwrap()
.size_of()
.unwrap()
.const_cast(bin.context.i32_type(), false);
let size = bin.builder.build_int_mul(elem_size, new_len, "");
let size = bin.builder.build_int_add(size, vec_size, "");
let realloc_size = if ns.target == Target::Solana {
bin.builder
.build_int_z_extend(size, bin.context.i64_type(), "")
} else {
size
};
let new = bin
.builder
.build_call(
bin.module.get_function("__realloc").unwrap(),
&[
bin.builder
.build_pointer_cast(
arr.into_pointer_value(),
bin.context.i8_type().ptr_type(AddressSpace::Generic),
"a",
)
.into(),
realloc_size.into(),
],
"",
)
.try_as_basic_value()
.left()
.unwrap()
.into_pointer_value();
let dest = bin.builder.build_pointer_cast(
new,
llvm_ty.ptr_type(AddressSpace::Generic),
"dest",
);
w.vars.get_mut(array).unwrap().value = dest.into();
let slot_ptr = unsafe {
bin.builder.build_gep(
dest,
&[
bin.context.i32_type().const_zero(),
bin.context.i32_type().const_int(2, false),
bin.builder.build_int_mul(len, elem_size, ""),
],
"data",
)
};
let value = expression(target, bin, value, &w.vars, function, ns);
let elem_ptr = bin.builder.build_pointer_cast(
slot_ptr,
llvm_elem_ty.ptr_type(AddressSpace::Generic),
"element pointer",
);
let value = if elem_ty.is_fixed_reference_type() {
w.vars.get_mut(res).unwrap().value = elem_ptr.into();
bin.builder.build_load(value.into_pointer_value(), "elem")
} else {
w.vars.get_mut(res).unwrap().value = value;
value
};
bin.builder.build_store(elem_ptr, value);
let len_ptr = unsafe {
bin.builder.build_gep(
dest,
&[
bin.context.i32_type().const_zero(),
bin.context.i32_type().const_zero(),
],
"len",
)
};
let len_field = bin.builder.build_pointer_cast(
len_ptr,
bin.context.i32_type().ptr_type(AddressSpace::Generic),
"len field",
);
bin.builder.build_store(len_field, new_len);
let size_ptr = unsafe {
bin.builder.build_gep(
dest,
&[
bin.context.i32_type().const_zero(),
bin.context.i32_type().const_int(1, false),
],
"size",
)
};
let size_field = bin.builder.build_pointer_cast(
size_ptr,
bin.context.i32_type().ptr_type(AddressSpace::Generic),
"size field",
);
bin.builder.build_store(size_field, new_len);
}
Instr::PopMemory { res, ty, array } => {
let a = w.vars[array].value.into_pointer_value();
let len = unsafe {
bin.builder.build_gep(
a,
&[
bin.context.i32_type().const_zero(),
bin.context.i32_type().const_zero(),
],
"a_len",
)
};
let len = bin.builder.build_load(len, "a_len").into_int_value();
let is_array_empty = bin.builder.build_int_compare(
IntPredicate::EQ,
len,
bin.context.i32_type().const_zero(),
"is_array_empty",
);
let error = bin.context.append_basic_block(function, "error");
let pop = bin.context.append_basic_block(function, "pop");
bin.builder
.build_conditional_branch(is_array_empty, error, pop);
bin.builder.position_at_end(error);
target.assert_failure(
bin,
bin.context
.i8_type()
.ptr_type(AddressSpace::Generic)
.const_null(),
bin.context.i32_type().const_zero(),
);
bin.builder.position_at_end(pop);
let llvm_ty = bin.llvm_type(ty, ns);
let elem_ty = ty.array_elem();
let llvm_elem_ty = bin.llvm_field_ty(&elem_ty, ns);
let elem_size = llvm_elem_ty
.size_of()
.unwrap()
.const_cast(bin.context.i32_type(), false);
let new_len =
bin.builder
.build_int_sub(len, bin.context.i32_type().const_int(1, false), "");
let vec_size = bin
.module
.get_struct_type("struct.vector")
.unwrap()
.size_of()
.unwrap()
.const_cast(bin.context.i32_type(), false);
let size = bin.builder.build_int_mul(elem_size, new_len, "");
let size = bin.builder.build_int_add(size, vec_size, "");
let slot_ptr = unsafe {
bin.builder.build_gep(
a,
&[
bin.context.i32_type().const_zero(),
bin.context.i32_type().const_int(2, false),
bin.builder.build_int_mul(new_len, elem_size, ""),
],
"data",
)
};
let slot_ptr = bin.builder.build_pointer_cast(
slot_ptr,
llvm_elem_ty.ptr_type(AddressSpace::Generic),
"slot_ptr",
);
if elem_ty.is_fixed_reference_type() {
w.vars.get_mut(res).unwrap().value = slot_ptr.into();
} else {
let ret_val = bin.builder.build_load(slot_ptr, "");
w.vars.get_mut(res).unwrap().value = ret_val;
}
let a = bin.builder.build_pointer_cast(
a,
bin.context.i8_type().ptr_type(AddressSpace::Generic),
"a",
);
let realloc_size = if ns.target == Target::Solana {
bin.builder
.build_int_z_extend(size, bin.context.i64_type(), "")
} else {
size
};
let new = bin
.builder
.build_call(
bin.module.get_function("__realloc").unwrap(),
&[a.into(), realloc_size.into()],
"",
)
.try_as_basic_value()
.left()
.unwrap()
.into_pointer_value();
let dest = bin.builder.build_pointer_cast(
new,
llvm_ty.ptr_type(AddressSpace::Generic),
"dest",
);
w.vars.get_mut(array).unwrap().value = dest.into();
let len_ptr = unsafe {
bin.builder.build_gep(
dest,
&[
bin.context.i32_type().const_zero(),
bin.context.i32_type().const_zero(),
],
"len",
)
};
let len_field = bin.builder.build_pointer_cast(
len_ptr,
bin.context.i32_type().ptr_type(AddressSpace::Generic),
"len field",
);
bin.builder.build_store(len_field, new_len);
let size_ptr = unsafe {
bin.builder.build_gep(
dest,
&[
bin.context.i32_type().const_zero(),
bin.context.i32_type().const_int(1, false),
],
"size",
)
};
let size_field = bin.builder.build_pointer_cast(
size_ptr,
bin.context.i32_type().ptr_type(AddressSpace::Generic),
"size field",
);
bin.builder.build_store(size_field, new_len);
}
Instr::AssertFailure { encoded_args: None } => {
target.assert_failure(
bin,
bin.context
.i8_type()
.ptr_type(AddressSpace::Generic)
.const_null(),
bin.context.i32_type().const_zero(),
);
}
Instr::AssertFailure {
encoded_args: Some(expr),
} => {
let data = expression(target, bin, expr, &w.vars, function, ns);
let vector_bytes = bin.vector_bytes(data);
let len = bin.vector_len(data);
target.assert_failure(bin, vector_bytes, len);
}
Instr::Print { expr } => {
let expr = expression(target, bin, expr, &w.vars, function, ns);
target.print(bin, bin.vector_bytes(expr), bin.vector_len(expr));
}
Instr::Call {
res,
call: InternalCallTy::Static { cfg_no },
args,
..
} => {
let f = &contract.cfg[*cfg_no];
let mut parms = args
.iter()
.map(|p| expression(target, bin, p, &w.vars, function, ns).into())
.collect::<Vec<BasicMetadataValueEnum>>();
if !res.is_empty() {
for v in f.returns.iter() {
parms.push(if ns.target == Target::Solana {
bin.build_alloca(function, bin.llvm_var_ty(&v.ty, ns), v.name_as_str())
.into()
} else {
bin.builder
.build_alloca(bin.llvm_var_ty(&v.ty, ns), v.name_as_str())
.into()
});
}
}
if let Some(parameters) = bin.parameters {
parms.push(parameters.into());
}
let ret = bin
.builder
.build_call(bin.functions[cfg_no], &parms, "")
.try_as_basic_value()
.left()
.unwrap();
let success = bin.builder.build_int_compare(
IntPredicate::EQ,
ret.into_int_value(),
bin.return_values[&ReturnCode::Success],
"success",
);
let success_block = bin.context.append_basic_block(function, "success");
let bail_block = bin.context.append_basic_block(function, "bail");
bin.builder
.build_conditional_branch(success, success_block, bail_block);
bin.builder.position_at_end(bail_block);
bin.builder.build_return(Some(&ret));
bin.builder.position_at_end(success_block);
if !res.is_empty() {
for (i, v) in f.returns.iter().enumerate() {
let val = bin
.builder
.build_load(parms[args.len() + i].into_pointer_value(), v.name_as_str());
let dest = w.vars[&res[i]].value;
if dest.is_pointer_value()
&& !(v.ty.is_reference_type(ns)
|| matches!(v.ty, Type::ExternalFunction { .. }))
{
bin.builder.build_store(dest.into_pointer_value(), val);
} else {
w.vars.get_mut(&res[i]).unwrap().value = val;
}
}
}
}
Instr::Call {
res,
call: InternalCallTy::Builtin { ast_func_no },
args,
..
} => {
let mut parms = args
.iter()
.map(|p| expression(target, bin, p, &w.vars, function, ns).into())
.collect::<Vec<BasicMetadataValueEnum>>();
let callee = &ns.functions[*ast_func_no];
if !res.is_empty() {
for v in callee.returns.iter() {
parms.push(if ns.target == Target::Solana {
bin.build_alloca(function, bin.llvm_var_ty(&v.ty, ns), v.name_as_str())
.into()
} else {
bin.builder
.build_alloca(bin.llvm_var_ty(&v.ty, ns), v.name_as_str())
.into()
});
}
}
let ret = target.builtin_function(bin, function, callee, &parms, ns);
let success = bin.builder.build_int_compare(
IntPredicate::EQ,
ret.into_int_value(),
bin.return_values[&ReturnCode::Success],
"success",
);
let success_block = bin.context.append_basic_block(function, "success");
let bail_block = bin.context.append_basic_block(function, "bail");
bin.builder
.build_conditional_branch(success, success_block, bail_block);
bin.builder.position_at_end(bail_block);
bin.builder.build_return(Some(&ret));
bin.builder.position_at_end(success_block);
if !res.is_empty() {
for (i, v) in callee.returns.iter().enumerate() {
let val = bin
.builder
.build_load(parms[args.len() + i].into_pointer_value(), v.name_as_str());
let dest = w.vars[&res[i]].value;
if dest.is_pointer_value()
&& !(v.ty.is_reference_type(ns)
|| matches!(v.ty, Type::ExternalFunction { .. }))
{
bin.builder.build_store(dest.into_pointer_value(), val);
} else {
w.vars.get_mut(&res[i]).unwrap().value = val;
}
}
}
}
Instr::Call {
res,
call: InternalCallTy::Dynamic(call_expr),
args,
..
} => {
let ty = call_expr.ty();
let returns = if let Type::InternalFunction { returns, .. } = ty.deref_any() {
returns
} else {
panic!("should be Type::InternalFunction type");
};
let mut parms = args
.iter()
.map(|p| expression(target, bin, p, &w.vars, function, ns).into())
.collect::<Vec<BasicMetadataValueEnum>>();
if !res.is_empty() {
for ty in returns.iter() {
parms.push(
bin.build_alloca(function, bin.llvm_var_ty(ty, ns), "")
.into(),
);
}
}
if let Some(parameters) = bin.parameters {
parms.push(parameters.into());
}
let callable = CallableValue::try_from(
expression(target, bin, call_expr, &w.vars, function, ns).into_pointer_value(),
)
.unwrap();
let ret = bin
.builder
.build_call(callable, &parms, "")
.try_as_basic_value()
.left()
.unwrap();
let success = bin.builder.build_int_compare(
IntPredicate::EQ,
ret.into_int_value(),
bin.return_values[&ReturnCode::Success],
"success",
);
let success_block = bin.context.append_basic_block(function, "success");
let bail_block = bin.context.append_basic_block(function, "bail");
bin.builder
.build_conditional_branch(success, success_block, bail_block);
bin.builder.position_at_end(bail_block);
bin.builder.build_return(Some(&ret));
bin.builder.position_at_end(success_block);
if !res.is_empty() {
for (i, ty) in returns.iter().enumerate() {
let val = bin
.builder
.build_load(parms[args.len() + i].into_pointer_value(), "");
let dest = w.vars[&res[i]].value;
if dest.is_pointer_value() && !ty.is_reference_type(ns) {
bin.builder.build_store(dest.into_pointer_value(), val);
} else {
w.vars.get_mut(&res[i]).unwrap().value = val;
}
}
}
}
Instr::Constructor {
success,
res,
contract_no,
encoded_args,
encoded_args_len,
value,
gas,
salt,
address,
seeds,
} => {
let encoded_args = expression(target, bin, encoded_args, &w.vars, function, ns);
let encoded_args_len = expression(target, bin, encoded_args_len, &w.vars, function, ns);
let address_stack = bin.build_alloca(function, bin.address_type(ns), "address");
let gas = expression(target, bin, gas, &w.vars, function, ns).into_int_value();
let value = value
.as_ref()
.map(|v| expression(target, bin, v, &w.vars, function, ns).into_int_value());
let salt = salt
.as_ref()
.map(|v| expression(target, bin, v, &w.vars, function, ns).into_int_value());
if let Some(address) = address {
let address =
expression(target, bin, address, &w.vars, function, ns).into_array_value();
bin.builder.build_store(address_stack, address);
}
let seeds = if let Some(seeds) = seeds {
let len = seeds.ty().array_length().unwrap().to_u64().unwrap();
let seeds_ty = bin.llvm_type(
&Type::Slice(Box::new(Type::Slice(Box::new(Type::Bytes(1))))),
ns,
);
let output_seeds = bin.build_array_alloca(
function,
seeds_ty,
bin.context.i64_type().const_int(len, false),
"seeds",
);
if let Expression::ArrayLiteral(_, _, _, exprs) = seeds {
for i in 0..len {
let val =
expression(target, bin, &exprs[i as usize], &w.vars, function, ns);
let seed_count = val
.get_type()
.into_pointer_type()
.get_element_type()
.into_array_type()
.len();
let dest = unsafe {
bin.builder.build_gep(
output_seeds,
&[
bin.context.i32_type().const_int(i, false),
bin.context.i32_type().const_zero(),
],
"dest",
)
};
let val = bin.builder.build_pointer_cast(
val.into_pointer_value(),
dest.get_type().get_element_type().into_pointer_type(),
"seeds",
);
bin.builder.build_store(dest, val);
let dest = unsafe {
bin.builder.build_gep(
output_seeds,
&[
bin.context.i32_type().const_int(i, false),
bin.context.i32_type().const_int(1, false),
],
"dest",
)
};
let val = bin.context.i64_type().const_int(seed_count as u64, false);
bin.builder.build_store(dest, val);
}
}
Some((output_seeds, bin.context.i64_type().const_int(len, false)))
} else {
None
};
let success = match success {
Some(n) => Some(&mut w.vars.get_mut(n).unwrap().value),
None => None,
};
target.create_contract(
bin,
function,
success,
*contract_no,
bin.builder.build_pointer_cast(
address_stack,
bin.context.i8_type().ptr_type(AddressSpace::Generic),
"address",
),
encoded_args,
encoded_args_len,
gas,
value,
salt,
seeds,
ns,
);
w.vars.get_mut(res).unwrap().value = bin.builder.build_load(address_stack, "address");
}
Instr::ExternalCall {
success,
address,
payload,
value,
gas,
callty,
accounts,
seeds,
} => {
let gas = expression(target, bin, gas, &w.vars, function, ns).into_int_value();
let value = expression(target, bin, value, &w.vars, function, ns).into_int_value();
let payload_ty = payload.ty();
let payload = expression(target, bin, payload, &w.vars, function, ns);
let address = if let Some(address) = address {
let address = expression(target, bin, address, &w.vars, function, ns);
let addr = bin.build_array_alloca(
function,
bin.context.i8_type(),
bin.context
.i32_type()
.const_int(ns.address_length as u64, false),
"address",
);
bin.builder.build_store(
bin.builder.build_pointer_cast(
addr,
address.get_type().ptr_type(AddressSpace::Generic),
"address",
),
address,
);
Some(addr)
} else {
None
};
let accounts = if let Some(accounts) = accounts {
let ty = accounts.ty();
let expr = expression(target, bin, accounts, &w.vars, function, ns);
if let Some(n) = ty.array_length() {
let accounts = expr.into_pointer_value();
let len = bin.context.i32_type().const_int(n.to_u64().unwrap(), false);
Some((accounts, len))
} else {
let addr = bin.vector_bytes(expr);
let len = bin.vector_len(expr);
Some((addr, len))
}
} else {
None
};
let (payload_ptr, payload_len) = if payload_ty == Type::DynamicBytes {
(bin.vector_bytes(payload), bin.vector_len(payload))
} else {
let ptr = payload.into_pointer_value();
let len = ptr
.get_type()
.get_element_type()
.size_of()
.unwrap()
.const_cast(bin.context.i32_type(), false);
(ptr, len)
};
let seeds = if let Some(seeds) = seeds {
let len = seeds.ty().array_length().unwrap().to_u64().unwrap();
let seeds_ty = bin.llvm_type(
&Type::Slice(Box::new(Type::Slice(Box::new(Type::Bytes(1))))),
ns,
);
let output_seeds = bin.build_array_alloca(
function,
seeds_ty,
bin.context.i64_type().const_int(len, false),
"seeds",
);
if let Expression::ArrayLiteral(_, _, _, exprs) = seeds {
for i in 0..len {
let val =
expression(target, bin, &exprs[i as usize], &w.vars, function, ns);
let seed_count = val
.get_type()
.into_pointer_type()
.get_element_type()
.into_array_type()
.len();
let dest = unsafe {
bin.builder.build_gep(
output_seeds,
&[
bin.context.i32_type().const_int(i, false),
bin.context.i32_type().const_zero(),
],
"dest",
)
};
let val = bin.builder.build_pointer_cast(
val.into_pointer_value(),
dest.get_type().get_element_type().into_pointer_type(),
"seeds",
);
bin.builder.build_store(dest, val);
let dest = unsafe {
bin.builder.build_gep(
output_seeds,
&[
bin.context.i32_type().const_int(i, false),
bin.context.i32_type().const_int(1, false),
],
"dest",
)
};
let val = bin.context.i64_type().const_int(seed_count as u64, false);
bin.builder.build_store(dest, val);
}
}
Some((output_seeds, bin.context.i64_type().const_int(len, false)))
} else {
None
};
let success = match success {
Some(n) => Some(&mut w.vars.get_mut(n).unwrap().value),
None => None,
};
target.external_call(
bin,
function,
success,
payload_ptr,
payload_len,
address,
gas,
value,
accounts,
seeds,
callty.clone(),
ns,
);
}
Instr::ValueTransfer {
success,
address,
value,
} => {
let value = expression(target, bin, value, &w.vars, function, ns).into_int_value();
let address =
expression(target, bin, address, &w.vars, function, ns).into_array_value();
let addr = bin.build_alloca(function, bin.address_type(ns), "address");
bin.builder.build_store(
bin.builder.build_pointer_cast(
addr,
address.get_type().ptr_type(AddressSpace::Generic),
"address",
),
address,
);
let success = match success {
Some(n) => Some(&mut w.vars.get_mut(n).unwrap().value),
None => None,
};
target.value_transfer(
bin,
function,
success,
bin.builder.build_pointer_cast(
addr,
bin.context.i8_type().ptr_type(AddressSpace::Generic),
"address",
),
value,
ns,
);
}
Instr::AbiDecode {
res,
selector,
exception_block: exception,
tys,
data,
data_len,
} => {
let v = expression(target, bin, data, &w.vars, function, ns);
let mut data = if data.ty().is_reference_type(ns) {
bin.vector_bytes(v)
} else {
v.into_pointer_value()
};
let mut data_len = if let Some(len) = data_len {
expression(target, bin, len, &w.vars, function, ns).into_int_value()
} else {
bin.vector_len(v)
};
if let Some(selector) = selector {
let exception = exception.unwrap();
let pos = bin.builder.get_insert_block().unwrap();
blocks.entry(exception).or_insert({
work.push_back(Work {
block_no: exception,
vars: w.vars.clone(),
});
create_block(exception, bin, cfg, function, ns)
});
bin.builder.position_at_end(pos);
let exception_block = blocks.get(&exception).unwrap();
let has_selector = bin.builder.build_int_compare(
IntPredicate::UGT,
data_len,
bin.context.i32_type().const_int(4, false),
"has_selector",
);
let ok1 = bin.context.append_basic_block(function, "ok1");
bin.builder
.build_conditional_branch(has_selector, ok1, exception_block.bb);
bin.builder.position_at_end(ok1);
let selector_data = bin
.builder
.build_load(
bin.builder.build_pointer_cast(
data,
bin.context.i32_type().ptr_type(AddressSpace::Generic),
"selector",
),
"selector",
)
.into_int_value();
let selector = if ns.target.is_substrate() {
*selector
} else {
selector.to_be()
};
let correct_selector = bin.builder.build_int_compare(
IntPredicate::EQ,
selector_data,
bin.context.i32_type().const_int(selector as u64, false),
"correct_selector",
);
let ok2 = bin.context.append_basic_block(function, "ok2");
bin.builder
.build_conditional_branch(correct_selector, ok2, exception_block.bb);
bin.builder.position_at_end(ok2);
data_len = bin.builder.build_int_sub(
data_len,
bin.context.i32_type().const_int(4, false),
"data_len",
);
data = unsafe {
bin.builder.build_gep(
bin.builder.build_pointer_cast(
data,
bin.context.i8_type().ptr_type(AddressSpace::Generic),
"data",
),
&[bin.context.i32_type().const_int(4, false)],
"data",
)
};
}
let mut returns = Vec::new();
target.abi_decode(bin, function, &mut returns, data, data_len, tys, ns);
for (i, ret) in returns.into_iter().enumerate() {
w.vars.get_mut(&res[i]).unwrap().value = ret;
}
}
Instr::Unreachable => {
}
Instr::SelfDestruct { recipient } => {
let recipient =
expression(target, bin, recipient, &w.vars, function, ns).into_array_value();
target.selfdestruct(bin, recipient, ns);
}
Instr::EmitEvent {
event_no,
data,
data_tys,
topics,
topic_tys,
} => {
let data = data
.iter()
.map(|a| expression(target, bin, a, &w.vars, function, ns))
.collect::<Vec<BasicValueEnum>>();
let topics = topics
.iter()
.map(|a| expression(target, bin, a, &w.vars, function, ns))
.collect::<Vec<BasicValueEnum>>();
target.emit_event(
bin, contract, function, *event_no, &data, data_tys, &topics, topic_tys, ns,
);
}
Instr::WriteBuffer { buf, offset, value } => {
let v = expression(target, bin, buf, &w.vars, function, ns);
let data = bin.vector_bytes(v);
let offset = expression(target, bin, offset, &w.vars, function, ns).into_int_value();
let emit_value = expression(target, bin, value, &w.vars, function, ns);
let start = unsafe { bin.builder.build_gep(data, &[offset], "start") };
let is_bytes = if let Type::Bytes(n) = value.ty() {
n
} else if value.ty() == Type::FunctionSelector {
ns.target.selector_length()
} else {
0
};
if is_bytes > 1 {
let value_ptr = bin.build_alloca(
function,
emit_value.into_int_value().get_type(),
&format!("bytes{}", is_bytes),
);
bin.builder
.build_store(value_ptr, emit_value.into_int_value());
bin.builder.build_call(
bin.module.get_function("__leNtobeN").unwrap(),
&[
bin.builder
.build_pointer_cast(
value_ptr,
bin.context.i8_type().ptr_type(AddressSpace::Generic),
"store",
)
.into(),
bin.builder
.build_pointer_cast(
start,
bin.context.i8_type().ptr_type(AddressSpace::Generic),
"dest",
)
.into(),
bin.context
.i32_type()
.const_int(is_bytes as u64, false)
.into(),
],
"",
);
} else {
let start = bin.builder.build_pointer_cast(
start,
emit_value.get_type().ptr_type(AddressSpace::Generic),
"start",
);
bin.builder.build_store(start, emit_value);
}
}
Instr::MemCopy {
source: from,
destination: to,
bytes,
} => {
let src = if from.ty().is_dynamic_memory() {
bin.vector_bytes(expression(target, bin, from, &w.vars, function, ns))
} else {
expression(target, bin, from, &w.vars, function, ns).into_pointer_value()
};
let dest = if to.ty().is_dynamic_memory() {
bin.vector_bytes(expression(target, bin, to, &w.vars, function, ns))
} else {
expression(target, bin, to, &w.vars, function, ns).into_pointer_value()
};
let size = expression(target, bin, bytes, &w.vars, function, ns);
let arg_type = bin.context.i8_type().ptr_type(AddressSpace::Generic);
let src = if src.get_type() == arg_type {
src
} else {
bin.builder.build_pointer_cast(src, arg_type, "")
};
let dest = if dest.get_type() == arg_type {
dest
} else {
bin.builder.build_pointer_cast(dest, arg_type, "")
};
bin.builder.build_call(
bin.module.get_function("__memcpy").unwrap(),
&[dest.into(), src.into(), size.into()],
"",
);
}
Instr::Switch {
cond,
cases,
default,
} => {
let pos = bin.builder.get_insert_block().unwrap();
let cond = expression(target, bin, cond, &w.vars, function, ns);
let cases = cases
.iter()
.map(|(exp, block_no)| {
let exp = expression(target, bin, exp, &w.vars, function, ns);
let bb = add_or_retrieve_block(
*block_no, pos, bin, function, blocks, work, w, cfg, ns,
);
(exp.into_int_value(), bb)
})
.collect::<Vec<(IntValue, inkwell::basic_block::BasicBlock)>>();
let default_bb =
add_or_retrieve_block(*default, pos, bin, function, blocks, work, w, cfg, ns);
bin.builder.position_at_end(pos);
bin.builder
.build_switch(cond.into_int_value(), default_bb, cases.as_ref());
}
Instr::ReturnData { data, data_len } => {
let data = if data.ty().is_reference_type(ns) {
bin.vector_bytes(expression(target, bin, data, &w.vars, function, ns))
} else {
expression(target, bin, data, &w.vars, function, ns).into_pointer_value()
};
let data_len = expression(target, bin, data_len, &w.vars, function, ns);
target.return_abi_data(bin, data, data_len);
}
Instr::ReturnCode { code } => {
target.return_code(bin, bin.return_values[code]);
}
}
}
fn add_or_retrieve_block<'a>(
block_no: usize,
pos: inkwell::basic_block::BasicBlock<'a>,
bin: &Binary<'a>,
function: FunctionValue<'a>,
blocks: &mut HashMap<usize, BasicBlock<'a>>,
work: &mut VecDeque<Work<'a>>,
w: &mut Work<'a>,
cfg: &ControlFlowGraph,
ns: &Namespace,
) -> inkwell::basic_block::BasicBlock<'a> {
if let std::collections::hash_map::Entry::Vacant(e) = blocks.entry(block_no) {
e.insert(create_block(block_no, bin, cfg, function, ns));
work.push_back(Work {
block_no,
vars: w.vars.clone(),
});
}
let bb = blocks.get(&block_no).unwrap();
for (v, phi) in bb.phis.iter() {
phi.add_incoming(&[(&w.vars[v].value, pos)]);
}
bb.bb
}