use crate::codegen::cfg::{HashTy, ReturnCode};
use crate::emit::binary::Binary;
use crate::emit::expression::expression;
use crate::emit::storage::StorageSlot;
use crate::emit::substrate::{event_id, log_return_code, SubstrateTarget, SCRATCH_SIZE};
use crate::emit::{TargetRuntime, Variable};
use crate::sema::ast;
use crate::sema::ast::{Function, Namespace, Type};
use crate::{codegen, emit_context};
use inkwell::types::{BasicType, IntType};
use inkwell::values::{
ArrayValue, BasicMetadataValueEnum, BasicValueEnum, CallableValue, FunctionValue, IntValue,
PointerValue,
};
use inkwell::{AddressSpace, IntPredicate};
use solang_parser::pt;
use std::collections::HashMap;
impl<'a> TargetRuntime<'a> for SubstrateTarget {
fn set_storage_extfunc(
&self,
binary: &Binary,
_function: FunctionValue,
slot: PointerValue,
dest: PointerValue,
) {
emit_context!(binary);
let ret = seal_set_storage!(
cast_byte_ptr!(slot).into(),
i32_const!(32).into(),
cast_byte_ptr!(dest).into(),
dest.get_type()
.get_element_type()
.size_of()
.unwrap()
.const_cast(binary.context.i32_type(), false)
.into()
);
log_return_code(binary, "seal_set_storage", ret);
}
fn get_storage_extfunc(
&self,
binary: &Binary<'a>,
_function: FunctionValue,
slot: PointerValue<'a>,
ns: &ast::Namespace,
) -> PointerValue<'a> {
emit_context!(binary);
let ty = binary.llvm_type(
&ast::Type::ExternalFunction {
params: Vec::new(),
mutability: ast::Mutability::Nonpayable(pt::Loc::Codegen),
returns: Vec::new(),
},
ns,
);
let len = ty
.into_pointer_type()
.get_element_type()
.size_of()
.unwrap()
.const_cast(binary.context.i32_type(), false);
let ef = call!("__malloc", &[len.into()])
.try_as_basic_value()
.left()
.unwrap()
.into_pointer_value();
let scratch_len = binary.scratch_len.unwrap().as_pointer_value();
binary.builder.build_store(scratch_len, len);
let ret = call!(
"seal_get_storage",
&[
cast_byte_ptr!(slot).into(),
i32_const!(32).into(),
ef.into(),
scratch_len.into()
]
)
.try_as_basic_value()
.left()
.unwrap()
.into_int_value();
log_return_code(binary, "seal_get_storage: ", ret);
binary
.builder
.build_pointer_cast(ef, ty.into_pointer_type(), "function_type")
}
fn set_storage_string(
&self,
binary: &Binary<'a>,
function: FunctionValue<'a>,
slot: PointerValue<'a>,
dest: BasicValueEnum<'a>,
) {
emit_context!(binary);
let len = binary.vector_len(dest);
let data = binary.vector_bytes(dest);
let exists = binary
.builder
.build_int_compare(IntPredicate::NE, len, i32_zero!(), "exists");
let delete_block = binary.context.append_basic_block(function, "delete_block");
let set_block = binary.context.append_basic_block(function, "set_block");
let done_storage = binary.context.append_basic_block(function, "done_storage");
binary
.builder
.build_conditional_branch(exists, set_block, delete_block);
binary.builder.position_at_end(set_block);
let ret = seal_set_storage!(
cast_byte_ptr!(slot).into(),
i32_const!(32).into(),
cast_byte_ptr!(data).into(),
len.into()
);
log_return_code(binary, "seal_set_storage", ret);
binary.builder.build_unconditional_branch(done_storage);
binary.builder.position_at_end(delete_block);
let ret = call!(
"seal_clear_storage",
&[cast_byte_ptr!(slot).into(), i32_const!(32).into()]
)
.try_as_basic_value()
.left()
.unwrap()
.into_int_value();
log_return_code(binary, "seal_clear_storage", ret);
binary.builder.build_unconditional_branch(done_storage);
binary.builder.position_at_end(done_storage);
}
fn get_storage_int(
&self,
binary: &Binary<'a>,
function: FunctionValue,
slot: PointerValue<'a>,
ty: IntType<'a>,
) -> IntValue<'a> {
emit_context!(binary);
let (scratch_buf, scratch_len) = scratch_buf!();
let ty_len = ty.size_of().const_cast(binary.context.i32_type(), false);
binary.builder.build_store(scratch_len, ty_len);
let exists = seal_get_storage!(
cast_byte_ptr!(slot).into(),
i32_const!(32).into(),
scratch_buf.into(),
scratch_len.into()
);
log_return_code(binary, "seal_get_storage: ", exists);
let exists = binary.builder.build_int_compare(
IntPredicate::EQ,
exists,
i32_zero!(),
"storage_exists",
);
let entry = binary.builder.get_insert_block().unwrap();
let retrieve_block = binary.context.append_basic_block(function, "in_storage");
let done_storage = binary.context.append_basic_block(function, "done_storage");
binary
.builder
.build_conditional_branch(exists, retrieve_block, done_storage);
binary.builder.position_at_end(retrieve_block);
let dest = binary.builder.build_pointer_cast(
binary.scratch.unwrap().as_pointer_value(),
ty.ptr_type(AddressSpace::Generic),
"scratch_ty_buf",
);
let loaded_int = binary.builder.build_load(dest, "int");
binary.builder.build_unconditional_branch(done_storage);
binary.builder.position_at_end(done_storage);
let res = binary.builder.build_phi(ty, "storage_res");
res.add_incoming(&[(&loaded_int, retrieve_block), (&ty.const_zero(), entry)]);
res.as_basic_value().into_int_value()
}
fn get_storage_string(
&self,
binary: &Binary<'a>,
function: FunctionValue,
slot: PointerValue<'a>,
) -> PointerValue<'a> {
emit_context!(binary);
let (scratch_buf, scratch_len) = scratch_buf!();
binary
.builder
.build_store(scratch_len, i32_const!(SCRATCH_SIZE as u64));
let exists = seal_get_storage!(
cast_byte_ptr!(slot).into(),
i32_const!(32).into(),
scratch_buf.into(),
scratch_len.into()
);
log_return_code(binary, "seal_get_storage: ", exists);
let exists = binary.builder.build_int_compare(
IntPredicate::EQ,
exists,
i32_zero!(),
"storage_exists",
);
let ty = binary
.module
.get_struct_type("struct.vector")
.unwrap()
.ptr_type(AddressSpace::Generic);
let entry = binary.builder.get_insert_block().unwrap();
let retrieve_block = binary
.context
.append_basic_block(function, "retrieve_block");
let done_storage = binary.context.append_basic_block(function, "done_storage");
binary
.builder
.build_conditional_branch(exists, retrieve_block, done_storage);
binary.builder.position_at_end(retrieve_block);
let length = binary.builder.build_load(scratch_len, "string_len");
let loaded_string = call!(
"vector_new",
&[length.into(), i32_const!(1).into(), scratch_buf.into()]
)
.try_as_basic_value()
.left()
.unwrap()
.into_pointer_value();
binary.builder.build_unconditional_branch(done_storage);
binary.builder.position_at_end(done_storage);
let res = binary.builder.build_phi(ty, "storage_res");
res.add_incoming(&[
(&loaded_string, retrieve_block),
(
&binary
.module
.get_struct_type("struct.vector")
.unwrap()
.ptr_type(AddressSpace::Generic)
.const_null(),
entry,
),
]);
res.as_basic_value().into_pointer_value()
}
fn get_storage_bytes_subscript(
&self,
binary: &Binary<'a>,
function: FunctionValue,
slot: IntValue<'a>,
index: IntValue<'a>,
) -> IntValue<'a> {
emit_context!(binary);
let slot_ptr = binary.builder.build_alloca(slot.get_type(), "slot");
binary.builder.build_store(slot_ptr, slot);
let (scratch_buf, scratch_len) = scratch_buf!();
binary
.builder
.build_store(scratch_len, i32_const!(SCRATCH_SIZE as u64));
let exists = seal_get_storage!(
cast_byte_ptr!(slot_ptr).into(),
i32_const!(32).into(),
scratch_buf.into(),
scratch_len.into()
);
log_return_code(binary, "seal_get_storage", exists);
let exists = binary.builder.build_int_compare(
IntPredicate::EQ,
exists,
i32_zero!(),
"storage_exists",
);
let length = binary
.builder
.build_select(
exists,
binary.builder.build_load(scratch_len, "string_len"),
i32_zero!().into(),
"string_length",
)
.into_int_value();
let in_range =
binary
.builder
.build_int_compare(IntPredicate::ULT, index, length, "index_in_range");
let retrieve_block = binary.context.append_basic_block(function, "in_range");
let bang_block = binary.context.append_basic_block(function, "bang_block");
binary
.builder
.build_conditional_branch(in_range, retrieve_block, bang_block);
binary.builder.position_at_end(bang_block);
self.assert_failure(binary, byte_ptr!().const_null(), i32_zero!());
binary.builder.position_at_end(retrieve_block);
let offset = unsafe {
binary.builder.build_gep(
binary.scratch.unwrap().as_pointer_value(),
&[i32_zero!(), index],
"data_offset",
)
};
binary.builder.build_load(offset, "value").into_int_value()
}
fn set_storage_bytes_subscript(
&self,
binary: &Binary,
function: FunctionValue,
slot: IntValue,
index: IntValue,
val: IntValue,
) {
emit_context!(binary);
let slot_ptr = binary.builder.build_alloca(slot.get_type(), "slot");
binary.builder.build_store(slot_ptr, slot);
let (scratch_buf, scratch_len) = scratch_buf!();
binary
.builder
.build_store(scratch_len, i32_const!(SCRATCH_SIZE as u64));
let exists = seal_get_storage!(
cast_byte_ptr!(slot_ptr).into(),
i32_const!(32).into(),
scratch_buf.into(),
scratch_len.into()
);
log_return_code(binary, "seal_get_storage", exists);
let exists = binary.builder.build_int_compare(
IntPredicate::EQ,
exists,
i32_zero!(),
"storage_exists",
);
let length = binary
.builder
.build_select(
exists,
binary.builder.build_load(scratch_len, "string_len"),
i32_zero!().into(),
"string_length",
)
.into_int_value();
let in_range =
binary
.builder
.build_int_compare(IntPredicate::ULT, index, length, "index_in_range");
let retrieve_block = binary.context.append_basic_block(function, "in_range");
let bang_block = binary.context.append_basic_block(function, "bang_block");
binary
.builder
.build_conditional_branch(in_range, retrieve_block, bang_block);
binary.builder.position_at_end(bang_block);
self.assert_failure(binary, byte_ptr!().const_null(), i32_zero!());
binary.builder.position_at_end(retrieve_block);
let offset = unsafe {
binary.builder.build_gep(
binary.scratch.unwrap().as_pointer_value(),
&[i32_zero!(), index],
"data_offset",
)
};
binary.builder.build_store(offset, val);
let ret = seal_set_storage!(
cast_byte_ptr!(slot_ptr).into(),
i32_const!(32).into(),
scratch_buf.into(),
length.into()
);
log_return_code(binary, "seal_set_storage", ret);
}
fn storage_push(
&self,
binary: &Binary<'a>,
_function: FunctionValue,
_ty: &ast::Type,
slot: IntValue<'a>,
val: Option<BasicValueEnum<'a>>,
_ns: &ast::Namespace,
) -> BasicValueEnum<'a> {
emit_context!(binary);
let val = val.unwrap();
let slot_ptr = binary.builder.build_alloca(slot.get_type(), "slot");
binary.builder.build_store(slot_ptr, slot);
let (scratch_buf, scratch_len) = scratch_buf!();
binary
.builder
.build_store(scratch_len, i32_const!(SCRATCH_SIZE as u64 - 1));
let exists = seal_get_storage!(
cast_byte_ptr!(slot_ptr).into(),
i32_const!(32).into(),
scratch_buf.into(),
scratch_len.into()
);
log_return_code(binary, "seal_get_storage", exists);
let exists = binary.builder.build_int_compare(
IntPredicate::EQ,
exists,
i32_zero!(),
"storage_exists",
);
let length = binary
.builder
.build_select(
exists,
binary.builder.build_load(scratch_len, "string_len"),
i32_zero!().into(),
"string_length",
)
.into_int_value();
let offset = unsafe {
binary.builder.build_gep(
binary.scratch.unwrap().as_pointer_value(),
&[i32_zero!(), length],
"data_offset",
)
};
binary.builder.build_store(offset, val);
let length = binary
.builder
.build_int_add(length, i32_const!(1), "new_length");
let ret = seal_set_storage!(
cast_byte_ptr!(slot_ptr).into(),
i32_const!(32).into(),
scratch_buf.into(),
length.into()
);
log_return_code(binary, "seal_set_storage", ret);
val
}
fn storage_pop(
&self,
binary: &Binary<'a>,
function: FunctionValue<'a>,
_ty: &ast::Type,
slot: IntValue<'a>,
load: bool,
_ns: &ast::Namespace,
) -> Option<BasicValueEnum<'a>> {
emit_context!(binary);
let slot_ptr = binary.builder.build_alloca(slot.get_type(), "slot");
binary.builder.build_store(slot_ptr, slot);
let (scratch_buf, scratch_len) = scratch_buf!();
binary
.builder
.build_store(scratch_len, i32_const!(SCRATCH_SIZE as u64));
let exists = seal_get_storage!(
cast_byte_ptr!(slot_ptr).into(),
i32_const!(32).into(),
scratch_buf.into(),
scratch_len.into()
);
log_return_code(binary, "seal_get_storage", exists);
let exists = binary.builder.build_int_compare(
IntPredicate::EQ,
exists,
i32_zero!(),
"storage_exists",
);
let length = binary
.builder
.build_select(
exists,
binary.builder.build_load(scratch_len, "string_len"),
i32_zero!().into(),
"string_length",
)
.into_int_value();
let in_range = binary.builder.build_int_compare(
IntPredicate::NE,
i32_zero!(),
length,
"index_in_range",
);
let retrieve_block = binary.context.append_basic_block(function, "in_range");
let bang_block = binary.context.append_basic_block(function, "bang_block");
binary
.builder
.build_conditional_branch(in_range, retrieve_block, bang_block);
binary.builder.position_at_end(bang_block);
self.assert_failure(binary, byte_ptr!().const_null(), i32_zero!());
binary.builder.position_at_end(retrieve_block);
let new_length = binary
.builder
.build_int_sub(length, i32_const!(1), "new_length");
let val = if load {
let offset = unsafe {
binary.builder.build_gep(
binary.scratch.unwrap().as_pointer_value(),
&[i32_zero!(), new_length],
"data_offset",
)
};
Some(binary.builder.build_load(offset, "popped_value"))
} else {
None
};
let ret = seal_set_storage!(
cast_byte_ptr!(slot_ptr).into(),
i32_const!(32).into(),
scratch_buf.into(),
new_length.into()
);
log_return_code(binary, "seal_set_storage", ret);
val
}
fn storage_array_length(
&self,
binary: &Binary<'a>,
_function: FunctionValue,
slot: IntValue<'a>,
_ty: &ast::Type,
_ns: &ast::Namespace,
) -> IntValue<'a> {
emit_context!(binary);
let slot_ptr = binary.builder.build_alloca(slot.get_type(), "slot");
binary.builder.build_store(slot_ptr, slot);
let (scratch_buf, scratch_len) = scratch_buf!();
binary
.builder
.build_store(scratch_len, i32_const!(SCRATCH_SIZE as u64));
let exists = seal_get_storage!(
cast_byte_ptr!(slot_ptr).into(),
i32_const!(32).into(),
scratch_buf.into(),
scratch_len.into()
);
log_return_code(binary, "seal_get_storage", exists);
let exists = binary.builder.build_int_compare(
IntPredicate::EQ,
exists,
i32_zero!(),
"storage_exists",
);
binary
.builder
.build_select(
exists,
binary.builder.build_load(scratch_len, "string_len"),
i32_zero!().into(),
"string_length",
)
.into_int_value()
}
fn return_empty_abi(&self, binary: &Binary) {
emit_context!(binary);
call!(
"seal_return",
&[
i32_zero!().into(),
byte_ptr!().const_zero().into(),
i32_zero!().into()
]
);
binary.builder.build_unreachable();
}
fn return_code<'b>(&self, binary: &'b Binary, _ret: IntValue<'b>) {
emit_context!(binary);
self.assert_failure(binary, byte_ptr!().const_zero(), i32_zero!());
}
fn keccak256_hash(
&self,
binary: &Binary,
src: PointerValue,
length: IntValue,
dest: PointerValue,
_ns: &ast::Namespace,
) {
emit_context!(binary);
call!(
"seal_hash_keccak_256",
&[
cast_byte_ptr!(src).into(),
length.into(),
cast_byte_ptr!(dest).into()
]
);
}
fn return_abi<'b>(&self, binary: &'b Binary, data: PointerValue<'b>, length: IntValue) {
emit_context!(binary);
call!(
"seal_return",
&[i32_zero!().into(), data.into(), length.into()]
);
binary.builder.build_unreachable();
}
fn return_abi_data<'b>(
&self,
binary: &Binary<'b>,
data: PointerValue<'b>,
data_len: BasicValueEnum<'b>,
) {
emit_context!(binary);
call!(
"seal_return",
&[i32_zero!().into(), data.into(), data_len.into()]
);
binary
.builder
.build_return(Some(&binary.return_values[&ReturnCode::Success]));
}
fn assert_failure<'b>(&self, binary: &'b Binary, _data: PointerValue, _length: IntValue) {
let asm_fn = binary.context.void_type().fn_type(&[], false);
let asm = binary.context.create_inline_asm(
asm_fn,
"unreachable".to_string(),
"".to_string(),
true,
false,
None,
false,
);
let callable = CallableValue::try_from(asm).unwrap();
binary.builder.build_call(callable, &[], "unreachable");
binary.builder.build_unreachable();
}
fn abi_decode<'b>(
&self,
binary: &Binary<'b>,
function: FunctionValue,
args: &mut Vec<BasicValueEnum<'b>>,
data: PointerValue<'b>,
datalength: IntValue<'b>,
spec: &[ast::Parameter],
ns: &ast::Namespace,
) {
let mut argsdata = binary.builder.build_pointer_cast(
data,
binary.context.i8_type().ptr_type(AddressSpace::Generic),
"",
);
let argsend = unsafe { binary.builder.build_gep(argsdata, &[datalength], "argsend") };
for param in spec {
args.push(self.decode_ty(binary, function, ¶m.ty, &mut argsdata, argsend, ns));
}
self.check_overrun(binary, function, argsdata, argsend, true);
}
fn abi_encode_to_vector<'b>(
&self,
binary: &Binary<'b>,
function: FunctionValue<'b>,
packed: &[BasicValueEnum<'b>],
args: &[BasicValueEnum<'b>],
tys: &[ast::Type],
ns: &ast::Namespace,
) -> PointerValue<'b> {
emit_context!(binary);
let mut length = i32_zero!();
debug_assert_eq!(packed.len() + args.len(), tys.len());
let mut tys_iter = tys.iter();
for arg in packed {
let ty = tys_iter.next().unwrap();
length = binary.builder.build_int_add(
length,
SubstrateTarget::encoded_length(*arg, false, true, ty, function, binary, ns),
"",
);
}
for arg in args {
let ty = tys_iter.next().unwrap();
length = binary.builder.build_int_add(
length,
SubstrateTarget::encoded_length(*arg, false, false, ty, function, binary, ns),
"",
);
}
let malloc_length = binary.builder.build_int_add(
length,
binary
.module
.get_struct_type("struct.vector")
.unwrap()
.size_of()
.unwrap()
.const_cast(binary.context.i32_type(), false),
"size",
);
let p = call!("__malloc", &[malloc_length.into()])
.try_as_basic_value()
.left()
.unwrap()
.into_pointer_value();
let v = binary.builder.build_pointer_cast(
p,
binary
.module
.get_struct_type("struct.vector")
.unwrap()
.ptr_type(AddressSpace::Generic),
"string",
);
if args.is_empty() {
let data_len = unsafe {
binary
.builder
.build_gep(v, &[i32_zero!(), i32_zero!()], "data_len")
};
binary.builder.build_store(data_len, length);
}
let data_size = unsafe {
binary
.builder
.build_gep(v, &[i32_zero!(), i32_const!(1)], "data_size")
};
binary.builder.build_store(data_size, length);
let data = unsafe {
binary
.builder
.build_gep(v, &[i32_zero!(), i32_const!(2)], "data")
};
let data = binary.builder.build_pointer_cast(data, byte_ptr!(), "");
let mut argsdata = data;
let mut tys_iter = tys.iter();
for arg in packed {
let ty = tys_iter.next().unwrap();
self.encode_ty(binary, ns, false, true, function, ty, *arg, &mut argsdata);
}
for arg in args {
let ty = tys_iter.next().unwrap();
self.encode_ty(binary, ns, false, false, function, ty, *arg, &mut argsdata);
}
if !args.is_empty() {
let length = binary.builder.build_int_sub(
binary
.builder
.build_ptr_to_int(argsdata, binary.context.i32_type(), "end"),
binary
.builder
.build_ptr_to_int(data, binary.context.i32_type(), "begin"),
"datalength",
);
let data_len = unsafe {
binary
.builder
.build_gep(v, &[i32_zero!(), i32_zero!()], "data_len")
};
binary.builder.build_store(data_len, length);
}
v
}
fn abi_encode<'b>(
&self,
binary: &Binary<'b>,
selector: Option<IntValue<'b>>,
load: bool,
function: FunctionValue,
args: &[BasicValueEnum<'b>],
tys: &[ast::Type],
ns: &ast::Namespace,
) -> (PointerValue<'b>, IntValue<'b>) {
emit_context!(binary);
let mut length = i32_zero!();
for (i, ty) in tys.iter().enumerate() {
length = binary.builder.build_int_add(
length,
SubstrateTarget::encoded_length(args[i], load, false, ty, function, binary, ns),
"",
);
}
if let Some(selector) = selector {
length = binary.builder.build_int_add(
length,
selector
.get_type()
.size_of()
.const_cast(binary.context.i32_type(), false),
"",
);
}
let data = call!("__malloc", &[length.into()])
.try_as_basic_value()
.left()
.unwrap()
.into_pointer_value();
let mut argsdata = data;
if let Some(selector) = selector {
binary.builder.build_store(
binary.builder.build_pointer_cast(
data,
selector.get_type().ptr_type(AddressSpace::Generic),
"",
),
selector,
);
argsdata = unsafe {
binary.builder.build_gep(
data,
&[selector
.get_type()
.size_of()
.const_cast(binary.context.i32_type(), false)],
"",
)
};
}
for (i, ty) in tys.iter().enumerate() {
self.encode_ty(
binary,
ns,
load,
false,
function,
ty,
args[i],
&mut argsdata,
);
}
let length = binary.builder.build_int_sub(
binary
.builder
.build_ptr_to_int(argsdata, binary.context.i32_type(), "end"),
binary
.builder
.build_ptr_to_int(data, binary.context.i32_type(), "begin"),
"datalength",
);
(data, length)
}
fn print(&self, binary: &Binary, string_ptr: PointerValue, string_len: IntValue) {
emit_context!(binary);
let ret = call!(
"seal_debug_message",
&[string_ptr.into(), string_len.into()]
)
.try_as_basic_value()
.left()
.unwrap()
.into_int_value();
log_return_code(binary, "seal_debug_message", ret);
}
fn create_contract<'b>(
&mut self,
binary: &Binary<'b>,
function: FunctionValue<'b>,
success: Option<&mut BasicValueEnum<'b>>,
contract_no: usize,
address: PointerValue<'b>,
encoded_args: BasicValueEnum<'b>,
encoded_args_len: BasicValueEnum<'b>,
gas: IntValue<'b>,
value: Option<IntValue<'b>>,
salt: Option<IntValue<'b>>,
_seeds: Option<(PointerValue<'b>, IntValue<'b>)>,
ns: &ast::Namespace,
) {
emit_context!(binary);
let created_contract = &ns.contracts[contract_no];
let code = created_contract.emit(ns, binary.options);
let (scratch_buf, scratch_len) = scratch_buf!();
let salt_buf =
binary.build_alloca(function, binary.context.i8_type().array_type(36), "salt");
let salt_buf = binary
.builder
.build_pointer_cast(salt_buf, byte_ptr!(), "salt_buf");
let salt_len = i32_const!(32);
if let Some(salt) = salt {
let salt_ty = ast::Type::Uint(256);
binary.builder.build_store(
binary.builder.build_pointer_cast(
salt_buf,
binary
.llvm_type(&salt_ty, ns)
.ptr_type(AddressSpace::Generic),
"salt",
),
salt,
);
} else {
let (ptr, len) = self.contract_unique_salt(binary, contract_no, ns);
binary.builder.build_store(scratch_len, i32_const!(36));
call!(
"seal_random",
&[ptr.into(), len.into(), salt_buf.into(), scratch_len.into()],
"random"
);
}
let encoded_args = binary.vector_bytes(encoded_args);
let value_ptr = binary
.builder
.build_alloca(binary.value_type(ns), "balance");
if let Some(value) = value {
binary.builder.build_store(value_ptr, value);
} else {
let scratch_len = binary.scratch_len.unwrap().as_pointer_value();
binary
.builder
.build_store(scratch_len, i32_const!(ns.value_length as u64));
call!(
"seal_minimum_balance",
&[cast_byte_ptr!(value_ptr).into(), scratch_len.into()],
"minimum_balance"
);
}
let codehash = binary.emit_global_string(
&format!("binary_{}_codehash", created_contract.name),
blake2_rfc::blake2b::blake2b(32, &[], &code).as_bytes(),
true,
);
let address_len_ptr = binary
.builder
.build_alloca(binary.context.i32_type(), "address_len_ptr");
binary
.builder
.build_store(address_len_ptr, i32_const!(ns.address_length as u64 * 32));
binary
.builder
.build_store(scratch_len, i32_const!(SCRATCH_SIZE as u64 * 32));
let ret = call!(
"seal_instantiate",
&[
codehash.into(),
gas.into(),
cast_byte_ptr!(value_ptr, "value_transfer").into(),
encoded_args.into(),
encoded_args_len.into(),
address.into(),
address_len_ptr.into(),
scratch_buf.into(),
scratch_len.into(),
salt_buf.into(),
salt_len.into(),
]
)
.try_as_basic_value()
.left()
.unwrap()
.into_int_value();
log_return_code(binary, "seal_instantiate", ret);
let is_success =
binary
.builder
.build_int_compare(IntPredicate::EQ, ret, i32_zero!(), "success");
if let Some(success) = success {
*success = is_success.into();
} else {
let success_block = binary.context.append_basic_block(function, "success");
let bail_block = binary.context.append_basic_block(function, "bail");
binary
.builder
.build_conditional_branch(is_success, success_block, bail_block);
binary.builder.position_at_end(bail_block);
self.assert_failure(
binary,
scratch_buf,
binary
.builder
.build_load(scratch_len, "string_len")
.into_int_value(),
);
binary.builder.position_at_end(success_block);
}
}
fn external_call<'b>(
&self,
binary: &Binary<'b>,
function: FunctionValue<'b>,
success: Option<&mut BasicValueEnum<'b>>,
payload: PointerValue<'b>,
payload_len: IntValue<'b>,
address: Option<PointerValue<'b>>,
gas: IntValue<'b>,
value: IntValue<'b>,
_accounts: Option<(PointerValue<'b>, IntValue<'b>)>,
_seeds: Option<(PointerValue<'b>, IntValue<'b>)>,
_ty: ast::CallTy,
ns: &ast::Namespace,
) {
emit_context!(binary);
let value_ptr = binary
.builder
.build_alloca(binary.value_type(ns), "balance");
binary.builder.build_store(value_ptr, value);
let (scratch_buf, scratch_len) = scratch_buf!();
binary
.builder
.build_store(scratch_len, i32_const!(SCRATCH_SIZE as u64));
let ret = call!(
"seal_call",
&[
i32_zero!().into(), address.unwrap().into(),
gas.into(),
cast_byte_ptr!(value_ptr, "value_transfer").into(),
payload.into(),
payload_len.into(),
scratch_buf.into(),
scratch_len.into(),
]
)
.try_as_basic_value()
.left()
.unwrap()
.into_int_value();
log_return_code(binary, "seal_call", ret);
let is_success =
binary
.builder
.build_int_compare(IntPredicate::EQ, ret, i32_zero!(), "success");
if let Some(success) = success {
*success = is_success.into();
} else {
let success_block = binary.context.append_basic_block(function, "success");
let bail_block = binary.context.append_basic_block(function, "bail");
binary
.builder
.build_conditional_branch(is_success, success_block, bail_block);
binary.builder.position_at_end(bail_block);
self.assert_failure(
binary,
scratch_buf,
binary
.builder
.build_load(scratch_len, "string_len")
.into_int_value(),
);
binary.builder.position_at_end(success_block);
}
}
fn value_transfer<'b>(
&self,
binary: &Binary<'b>,
function: FunctionValue,
success: Option<&mut BasicValueEnum<'b>>,
address: PointerValue<'b>,
value: IntValue<'b>,
ns: &ast::Namespace,
) {
emit_context!(binary);
let value_ptr = binary
.builder
.build_alloca(binary.value_type(ns), "balance");
binary.builder.build_store(value_ptr, value);
let ret = call!(
"seal_transfer",
&[
address.into(),
i32_const!(ns.address_length as u64).into(),
cast_byte_ptr!(value_ptr, "value_transfer").into(),
i32_const!(ns.value_length as u64).into()
]
)
.try_as_basic_value()
.left()
.unwrap()
.into_int_value();
log_return_code(binary, "seal_transfer", ret);
let is_success =
binary
.builder
.build_int_compare(IntPredicate::EQ, ret, i32_zero!(), "success");
if let Some(success) = success {
*success = is_success.into();
} else {
let success_block = binary.context.append_basic_block(function, "success");
let bail_block = binary.context.append_basic_block(function, "bail");
binary
.builder
.build_conditional_branch(is_success, success_block, bail_block);
binary.builder.position_at_end(bail_block);
self.assert_failure(binary, byte_ptr!().const_null(), i32_zero!());
binary.builder.position_at_end(success_block);
}
}
fn return_data<'b>(&self, binary: &Binary<'b>, _function: FunctionValue) -> PointerValue<'b> {
emit_context!(binary);
let (scratch_buf, scratch_len) = scratch_buf!();
let length = binary.builder.build_load(scratch_len, "string_len");
call!(
"vector_new",
&[length.into(), i32_const!(1).into(), scratch_buf.into(),]
)
.try_as_basic_value()
.left()
.unwrap()
.into_pointer_value()
}
fn value_transferred<'b>(&self, binary: &Binary<'b>, ns: &ast::Namespace) -> IntValue<'b> {
emit_context!(binary);
let value = binary.builder.build_alloca(binary.value_type(ns), "value");
let value_len = binary
.builder
.build_alloca(binary.context.i32_type(), "value_len");
binary
.builder
.build_store(value_len, i32_const!(ns.value_length as u64));
call!(
"seal_value_transferred",
&[cast_byte_ptr!(value).into(), value_len.into()],
"value_transferred"
);
binary
.builder
.build_load(value, "value_transferred")
.into_int_value()
}
fn selfdestruct<'b>(&self, binary: &Binary<'b>, addr: ArrayValue<'b>, ns: &ast::Namespace) {
emit_context!(binary);
let address = binary
.builder
.build_alloca(binary.address_type(ns), "address");
binary.builder.build_store(address, addr);
call!(
"seal_terminate",
&[cast_byte_ptr!(address).into()],
"terminated"
);
binary.builder.build_unreachable();
}
fn hash<'b>(
&self,
binary: &Binary<'b>,
_function: FunctionValue<'b>,
hash: HashTy,
input: PointerValue<'b>,
input_len: IntValue<'b>,
ns: &ast::Namespace,
) -> IntValue<'b> {
emit_context!(binary);
let (fname, hashlen) = match hash {
HashTy::Keccak256 => ("seal_hash_keccak_256", 32),
HashTy::Ripemd160 => ("ripemd160", 20),
HashTy::Sha256 => ("seal_hash_sha2_256", 32),
HashTy::Blake2_128 => ("seal_hash_blake2_128", 16),
HashTy::Blake2_256 => ("seal_hash_blake2_256", 32),
};
let res =
binary
.builder
.build_array_alloca(binary.context.i8_type(), i32_const!(hashlen), "res");
call!(fname, &[input.into(), input_len.into(), res.into()], "hash");
let temp = binary.builder.build_alloca(
binary.llvm_type(&ast::Type::Bytes(hashlen as u8), ns),
"hash",
);
call!(
"__beNtoleN",
&[
res.into(),
cast_byte_ptr!(temp).into(),
i32_const!(hashlen).into()
]
);
binary.builder.build_load(temp, "hash").into_int_value()
}
fn emit_event<'b>(
&self,
binary: &Binary<'b>,
contract: &ast::Contract,
function: FunctionValue<'b>,
event_no: usize,
data: &[BasicValueEnum<'b>],
data_tys: &[ast::Type],
topics: &[BasicValueEnum<'b>],
_topic_tys: &[ast::Type],
ns: &ast::Namespace,
) {
emit_context!(binary);
let topic_count = topics.len();
let topic_size = i32_const!(if topic_count > 0 {
32 * topic_count as u64 + 1
} else {
0
});
let topic_buf = if topic_count > 0 {
let topic_buf =
binary
.builder
.build_array_alloca(binary.context.i8_type(), topic_size, "topic");
binary.builder.build_store(
topic_buf,
binary
.context
.i8_type()
.const_int(topic_count as u64 * 4, false),
);
let mut dest = unsafe {
binary
.builder
.build_gep(topic_buf, &[i32_const!(1)], "dest")
};
call!(
"__bzero8",
&[
cast_byte_ptr!(dest, "dest").into(),
i32_const!(topic_count as u64 * 4).into()
]
);
for topic in topics.iter() {
call!(
"__memcpy",
&[
dest.into(),
binary.vector_bytes(*topic).into(),
binary.vector_len(*topic).into(),
]
);
dest = unsafe { binary.builder.build_gep(dest, &[i32_const!(32)], "dest") };
}
topic_buf
} else {
byte_ptr!().const_null()
};
let (data_ptr, data_len) = self.abi_encode(
binary,
event_id(binary, contract, event_no),
false,
function,
data,
data_tys,
ns,
);
call!(
"seal_deposit_event",
&[
topic_buf.into(),
topic_size.into(),
data_ptr.into(),
data_len.into(),
]
);
}
fn builtin<'b>(
&self,
binary: &Binary<'b>,
expr: &codegen::Expression,
vartab: &HashMap<usize, Variable<'b>>,
function: FunctionValue<'b>,
ns: &ast::Namespace,
) -> BasicValueEnum<'b> {
emit_context!(binary);
macro_rules! get_seal_value {
($name:literal, $func:literal, $width:expr) => {{
let (scratch_buf, scratch_len) = scratch_buf!();
binary.builder.build_store(
scratch_len,
binary
.context
.i32_type()
.const_int($width as u64 / 8, false),
);
call!($func, &[scratch_buf.into(), scratch_len.into()], $name);
binary.builder.build_load(
binary.builder.build_pointer_cast(
scratch_buf,
binary
.context
.custom_width_int_type($width)
.ptr_type(AddressSpace::Generic),
"",
),
$name,
)
}};
}
match expr {
codegen::Expression::Builtin(_, _, codegen::Builtin::Calldata, _) => {
let v = call!(
"vector_new",
&[
binary
.builder
.build_load(binary.calldata_len.as_pointer_value(), "calldata_len")
.into(),
i32_const!(1).into(),
binary
.builder
.build_int_to_ptr(
binary.context.i32_type().const_all_ones(),
byte_ptr!(),
"no_initializer",
)
.into(),
]
)
.try_as_basic_value()
.left()
.unwrap();
let data = unsafe {
binary.builder.build_gep(
v.into_pointer_value(),
&[i32_zero!(), i32_const!(2)],
"",
)
};
let scratch_len = binary.scratch_len.unwrap().as_pointer_value();
binary
.builder
.build_store(scratch_len, i32_const!(SCRATCH_SIZE as u64));
call!(
"seal_input",
&[cast_byte_ptr!(data).into(), scratch_len.into()],
"data"
);
v
}
codegen::Expression::Builtin(_, _, codegen::Builtin::BlockNumber, _) => {
let block_number =
get_seal_value!("block_number", "seal_block_number", 32).into_int_value();
binary
.builder
.build_int_z_extend_or_bit_cast(
block_number,
binary.context.i64_type(),
"block_number",
)
.into()
}
codegen::Expression::Builtin(_, _, codegen::Builtin::Timestamp, _) => {
let milliseconds = get_seal_value!("timestamp", "seal_now", 64).into_int_value();
binary
.builder
.build_int_unsigned_div(
milliseconds,
binary.context.i64_type().const_int(1000, false),
"seconds",
)
.into()
}
codegen::Expression::Builtin(_, _, codegen::Builtin::Gasleft, _) => {
get_seal_value!("gas_left", "seal_gas_left", 64)
}
codegen::Expression::Builtin(_, _, codegen::Builtin::Gasprice, expr) => {
let gas = if expr.is_empty() {
binary.context.i64_type().const_int(1, false)
} else {
expression(self, binary, &expr[0], vartab, function, ns).into_int_value()
};
let (scratch_buf, scratch_len) = scratch_buf!();
binary
.builder
.build_store(scratch_len, i32_const!(ns.value_length as u64));
call!(
"seal_weight_to_fee",
&[gas.into(), scratch_buf.into(), scratch_len.into()],
"gas_price"
);
binary.builder.build_load(
binary.builder.build_pointer_cast(
scratch_buf,
binary
.context
.custom_width_int_type(ns.value_length as u32 * 8)
.ptr_type(AddressSpace::Generic),
"",
),
"price",
)
}
codegen::Expression::Builtin(_, _, codegen::Builtin::Sender, _) => {
let (scratch_buf, scratch_len) = scratch_buf!();
binary
.builder
.build_store(scratch_len, i32_const!(ns.address_length as u64));
call!(
"seal_caller",
&[scratch_buf.into(), scratch_len.into()],
"caller"
);
binary.builder.build_load(
binary.builder.build_pointer_cast(
scratch_buf,
binary.address_type(ns).ptr_type(AddressSpace::Generic),
"",
),
"caller",
)
}
codegen::Expression::Builtin(_, _, codegen::Builtin::Value, _) => {
self.value_transferred(binary, ns).into()
}
codegen::Expression::Builtin(_, _, codegen::Builtin::MinimumBalance, _) => {
get_seal_value!(
"minimum_balance",
"seal_minimum_balance",
ns.value_length as u32 * 8
)
}
codegen::Expression::Builtin(_, _, codegen::Builtin::Random, args) => {
let subject =
expression(self, binary, &args[0], vartab, function, ns).into_pointer_value();
let subject_data = unsafe {
binary
.builder
.build_gep(subject, &[i32_zero!(), i32_const!(2)], "subject_data")
};
let subject_len = unsafe {
binary
.builder
.build_gep(subject, &[i32_zero!(), i32_zero!()], "subject_len")
};
let (scratch_buf, scratch_len) = scratch_buf!();
binary.builder.build_store(scratch_len, i32_const!(36));
call!(
"seal_random",
&[
cast_byte_ptr!(subject_data, "subject_data").into(),
binary.builder.build_load(subject_len, "subject_len").into(),
scratch_buf.into(),
scratch_len.into()
],
"random"
);
binary.builder.build_load(
binary.builder.build_pointer_cast(
scratch_buf,
binary
.context
.custom_width_int_type(256)
.ptr_type(AddressSpace::Generic),
"",
),
"hash",
)
}
codegen::Expression::Builtin(_, _, codegen::Builtin::GetAddress, _) => {
let (scratch_buf, scratch_len) = scratch_buf!();
binary
.builder
.build_store(scratch_len, i32_const!(ns.address_length as u64));
call!(
"seal_address",
&[scratch_buf.into(), scratch_len.into()],
"address"
);
binary.builder.build_load(
binary.builder.build_pointer_cast(
scratch_buf,
binary.address_type(ns).ptr_type(AddressSpace::Generic),
"",
),
"self_address",
)
}
codegen::Expression::Builtin(_, _, codegen::Builtin::Balance, _) => {
let (scratch_buf, scratch_len) = scratch_buf!();
binary
.builder
.build_store(scratch_len, i32_const!(ns.value_length as u64));
call!(
"seal_balance",
&[scratch_buf.into(), scratch_len.into()],
"balance"
);
binary.builder.build_load(
binary.builder.build_pointer_cast(
scratch_buf,
binary.value_type(ns).ptr_type(AddressSpace::Generic),
"",
),
"balance",
)
}
_ => unreachable!("{:?}", expr),
}
}
fn storage_load(
&self,
binary: &Binary<'a>,
ty: &Type,
slot: &mut IntValue<'a>,
function: FunctionValue,
ns: &Namespace,
) -> BasicValueEnum<'a> {
let slot_ptr = binary.builder.build_alloca(slot.get_type(), "slot");
self.storage_load_slot(binary, ty, slot, slot_ptr, function, ns)
}
fn storage_store(
&self,
binary: &Binary<'a>,
ty: &Type,
_existing: bool,
slot: &mut IntValue<'a>,
dest: BasicValueEnum<'a>,
function: FunctionValue<'a>,
ns: &Namespace,
) {
let slot_ptr = binary.builder.build_alloca(slot.get_type(), "slot");
self.storage_store_slot(binary, ty, slot, slot_ptr, dest, function, ns);
}
fn storage_delete(
&self,
bin: &Binary<'a>,
ty: &Type,
slot: &mut IntValue<'a>,
function: FunctionValue<'a>,
ns: &Namespace,
) {
let slot_ptr = bin.builder.build_alloca(slot.get_type(), "slot");
self.storage_delete_slot(bin, ty, slot, slot_ptr, function, ns);
}
fn builtin_function(
&self,
_binary: &Binary<'a>,
_function: FunctionValue<'a>,
_builtin_func: &Function,
_args: &[BasicMetadataValueEnum<'a>],
_ns: &Namespace,
) -> BasicValueEnum<'a> {
unimplemented!()
}
fn storage_subscript(
&self,
_bin: &Binary<'a>,
_function: FunctionValue<'a>,
_ty: &Type,
_slot: IntValue<'a>,
_index: BasicValueEnum<'a>,
_ns: &Namespace,
) -> IntValue<'a> {
unimplemented!()
}
}