|
@@ -1,19 +1,15 @@
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
-use crate::{codegen::Options, sema::ast};
|
|
|
|
|
|
|
+use crate::codegen::Options;
|
|
|
|
|
+use crate::sema::ast::{Contract, Namespace};
|
|
|
use inkwell::context::Context;
|
|
use inkwell::context::Context;
|
|
|
use inkwell::module::{Linkage, Module};
|
|
use inkwell::module::{Linkage, Module};
|
|
|
-use inkwell::types::BasicType;
|
|
|
|
|
-use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
|
|
|
|
|
|
|
+use inkwell::values::{BasicMetadataValueEnum, FunctionValue, IntValue, PointerValue};
|
|
|
use inkwell::AddressSpace;
|
|
use inkwell::AddressSpace;
|
|
|
-use inkwell::IntPredicate;
|
|
|
|
|
-use num_traits::ToPrimitive;
|
|
|
|
|
-use solang_parser::pt;
|
|
|
|
|
|
|
|
|
|
-use crate::emit::functions::{abort_if_value_transfer, emit_functions, emit_initializer};
|
|
|
|
|
|
|
+use crate::emit::functions::{emit_functions, emit_initializer};
|
|
|
use crate::emit::{Binary, TargetRuntime};
|
|
use crate::emit::{Binary, TargetRuntime};
|
|
|
|
|
|
|
|
-mod dispatch;
|
|
|
|
|
mod storage;
|
|
mod storage;
|
|
|
pub(super) mod target;
|
|
pub(super) mod target;
|
|
|
|
|
|
|
@@ -106,8 +102,8 @@ impl SubstrateTarget {
|
|
|
pub fn build<'a>(
|
|
pub fn build<'a>(
|
|
|
context: &'a Context,
|
|
context: &'a Context,
|
|
|
std_lib: &Module<'a>,
|
|
std_lib: &Module<'a>,
|
|
|
- contract: &'a ast::Contract,
|
|
|
|
|
- ns: &'a ast::Namespace,
|
|
|
|
|
|
|
+ contract: &'a Contract,
|
|
|
|
|
+ ns: &'a Namespace,
|
|
|
opt: &'a Options,
|
|
opt: &'a Options,
|
|
|
) -> Binary<'a> {
|
|
) -> Binary<'a> {
|
|
|
let filename = ns.files[contract.loc.file_no()].file_name();
|
|
let filename = ns.files[contract.loc.file_no()].file_name();
|
|
@@ -148,8 +144,9 @@ impl SubstrateTarget {
|
|
|
|
|
|
|
|
emit_functions(&mut target, &mut binary, contract, ns);
|
|
emit_functions(&mut target, &mut binary, contract, ns);
|
|
|
|
|
|
|
|
- target.emit_deploy(&mut binary, contract, ns);
|
|
|
|
|
- target.emit_call(&binary, contract, ns);
|
|
|
|
|
|
|
+ let storage_initializer = emit_initializer(&mut target, &mut binary, contract, ns);
|
|
|
|
|
+ target.emit_dispatch(Some(storage_initializer), &mut binary, ns);
|
|
|
|
|
+ target.emit_dispatch(None, &mut binary, ns);
|
|
|
|
|
|
|
|
binary.internalize(&[
|
|
binary.internalize(&[
|
|
|
"deploy",
|
|
"deploy",
|
|
@@ -189,19 +186,11 @@ impl SubstrateTarget {
|
|
|
&self,
|
|
&self,
|
|
|
binary: &Binary<'a>,
|
|
binary: &Binary<'a>,
|
|
|
function: FunctionValue,
|
|
function: FunctionValue,
|
|
|
- abort_value_transfers: bool,
|
|
|
|
|
- ns: &ast::Namespace,
|
|
|
|
|
- function_name: &str,
|
|
|
|
|
) -> (PointerValue<'a>, IntValue<'a>) {
|
|
) -> (PointerValue<'a>, IntValue<'a>) {
|
|
|
let entry = binary.context.append_basic_block(function, "entry");
|
|
let entry = binary.context.append_basic_block(function, "entry");
|
|
|
|
|
|
|
|
binary.builder.position_at_end(entry);
|
|
binary.builder.position_at_end(entry);
|
|
|
|
|
|
|
|
- // after copying stratch, first thing to do is abort value transfers if constructors not payable
|
|
|
|
|
- if abort_value_transfers {
|
|
|
|
|
- abort_if_value_transfer(self, binary, function, ns, function_name);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
// init our heap
|
|
// init our heap
|
|
|
binary
|
|
binary
|
|
|
.builder
|
|
.builder
|
|
@@ -329,1466 +318,24 @@ impl SubstrateTarget {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- fn emit_deploy(&mut self, binary: &mut Binary, contract: &ast::Contract, ns: &ast::Namespace) {
|
|
|
|
|
- let initializer = emit_initializer(self, binary, contract, ns);
|
|
|
|
|
-
|
|
|
|
|
- // create deploy function
|
|
|
|
|
- let function = binary.module.add_function(
|
|
|
|
|
- "deploy",
|
|
|
|
|
- binary.context.void_type().fn_type(&[], false),
|
|
|
|
|
- None,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- // deploy always receives an endowment so no value check here
|
|
|
|
|
- let (deploy_args, deploy_args_length) =
|
|
|
|
|
- self.public_function_prelude(binary, function, false, ns, "deploy");
|
|
|
|
|
-
|
|
|
|
|
- // init our storage vars
|
|
|
|
|
- binary.builder.build_call(initializer, &[], "");
|
|
|
|
|
-
|
|
|
|
|
- let fallback_block = binary.context.append_basic_block(function, "fallback");
|
|
|
|
|
-
|
|
|
|
|
- self.emit_function_dispatch(
|
|
|
|
|
- binary,
|
|
|
|
|
- contract,
|
|
|
|
|
- ns,
|
|
|
|
|
- pt::FunctionTy::Constructor,
|
|
|
|
|
- deploy_args,
|
|
|
|
|
- deploy_args_length,
|
|
|
|
|
- function,
|
|
|
|
|
- &binary.functions,
|
|
|
|
|
- Some(fallback_block),
|
|
|
|
|
- |_| false,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- // emit fallback code
|
|
|
|
|
- binary.builder.position_at_end(fallback_block);
|
|
|
|
|
-
|
|
|
|
|
- self.assert_failure(
|
|
|
|
|
- binary,
|
|
|
|
|
- binary
|
|
|
|
|
- .context
|
|
|
|
|
- .i8_type()
|
|
|
|
|
- .ptr_type(AddressSpace::default())
|
|
|
|
|
- .const_null(),
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn emit_call(&mut self, binary: &Binary, contract: &ast::Contract, ns: &ast::Namespace) {
|
|
|
|
|
- // create call function
|
|
|
|
|
- let function = binary.module.add_function(
|
|
|
|
|
- "call",
|
|
|
|
|
- binary.context.void_type().fn_type(&[], false),
|
|
|
|
|
- None,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- let (contract_args, contract_args_length) = self.public_function_prelude(
|
|
|
|
|
- binary,
|
|
|
|
|
- function,
|
|
|
|
|
- binary.function_abort_value_transfers,
|
|
|
|
|
- ns,
|
|
|
|
|
- "call",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- self.emit_function_dispatch(
|
|
|
|
|
- binary,
|
|
|
|
|
- contract,
|
|
|
|
|
- ns,
|
|
|
|
|
- pt::FunctionTy::Function,
|
|
|
|
|
- contract_args,
|
|
|
|
|
- contract_args_length,
|
|
|
|
|
- function,
|
|
|
|
|
- &binary.functions,
|
|
|
|
|
- None,
|
|
|
|
|
- |func| !binary.function_abort_value_transfers && func.nonpayable,
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// ABI decode a single primitive
|
|
|
|
|
- fn decode_primitive<'b>(
|
|
|
|
|
- &self,
|
|
|
|
|
- binary: &Binary<'b>,
|
|
|
|
|
- ty: &ast::Type,
|
|
|
|
|
- src: PointerValue<'b>,
|
|
|
|
|
- ns: &ast::Namespace,
|
|
|
|
|
- ) -> (BasicValueEnum<'b>, u64) {
|
|
|
|
|
- match ty {
|
|
|
|
|
- ast::Type::Bool => {
|
|
|
|
|
- let val = binary.builder.build_int_compare(
|
|
|
|
|
- IntPredicate::EQ,
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(binary.context.i8_type(), src, "abi_bool")
|
|
|
|
|
- .into_int_value(),
|
|
|
|
|
- binary.context.i8_type().const_int(1, false),
|
|
|
|
|
- "bool",
|
|
|
|
|
- );
|
|
|
|
|
- (val.into(), 1)
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Uint(bits) | ast::Type::Int(bits) => {
|
|
|
|
|
- let int_type = binary.context.custom_width_int_type(*bits as u32);
|
|
|
|
|
-
|
|
|
|
|
- let val = binary.builder.build_load(int_type, src, "");
|
|
|
|
|
-
|
|
|
|
|
- // substrate only supports power-of-two types; step over the
|
|
|
|
|
- // the remainer
|
|
|
|
|
-
|
|
|
|
|
- // FIXME: we should do some type-checking here and ensure that the
|
|
|
|
|
- // encoded value fits into our smaller type
|
|
|
|
|
- let len = bits.next_power_of_two() as u64 / 8;
|
|
|
|
|
-
|
|
|
|
|
- (val, len)
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Contract(_) | ast::Type::Address(_) => {
|
|
|
|
|
- let val = binary.builder.build_load(binary.address_type(ns), src, "");
|
|
|
|
|
-
|
|
|
|
|
- let len = ns.address_length as u64;
|
|
|
|
|
-
|
|
|
|
|
- (val, len)
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Bytes(len) => {
|
|
|
|
|
- let int_type = binary.context.custom_width_int_type(*len as u32 * 8);
|
|
|
|
|
-
|
|
|
|
|
- let buf = binary.builder.build_alloca(int_type, "buf");
|
|
|
|
|
-
|
|
|
|
|
- // byte order needs to be reversed. e.g. hex"11223344" should be 0x10 0x11 0x22 0x33 0x44
|
|
|
|
|
- binary.builder.build_call(
|
|
|
|
|
- binary.module.get_function("__beNtoleN").unwrap(),
|
|
|
|
|
- &[
|
|
|
|
|
- src.into(),
|
|
|
|
|
- buf.into(),
|
|
|
|
|
- binary
|
|
|
|
|
- .context
|
|
|
|
|
- .i32_type()
|
|
|
|
|
- .const_int(*len as u64, false)
|
|
|
|
|
- .into(),
|
|
|
|
|
- ],
|
|
|
|
|
- "",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- (
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(int_type, buf, &format!("bytes{len}")),
|
|
|
|
|
- *len as u64,
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- _ => unreachable!(),
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// Check that data has not overrun end, and whether end == data to check we do not have
|
|
|
|
|
- /// trailing data
|
|
|
|
|
- fn check_overrun(
|
|
|
|
|
- &self,
|
|
|
|
|
- binary: &Binary,
|
|
|
|
|
- function: FunctionValue,
|
|
|
|
|
- data: PointerValue,
|
|
|
|
|
- end: PointerValue,
|
|
|
|
|
- end_is_data: bool,
|
|
|
|
|
- ) {
|
|
|
|
|
- let in_bounds = binary.builder.build_int_compare(
|
|
|
|
|
- if end_is_data {
|
|
|
|
|
- IntPredicate::EQ
|
|
|
|
|
- } else {
|
|
|
|
|
- IntPredicate::ULE
|
|
|
|
|
- },
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_ptr_to_int(data, binary.context.i32_type(), "args"),
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_ptr_to_int(end, binary.context.i32_type(), "end"),
|
|
|
|
|
- "is_done",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- 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(in_bounds, success_block, bail_block);
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(bail_block);
|
|
|
|
|
-
|
|
|
|
|
- self.assert_failure(
|
|
|
|
|
- binary,
|
|
|
|
|
- binary
|
|
|
|
|
- .context
|
|
|
|
|
- .i8_type()
|
|
|
|
|
- .ptr_type(AddressSpace::default())
|
|
|
|
|
- .const_null(),
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(success_block);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// recursively encode a single ty
|
|
|
|
|
- fn decode_ty<'b>(
|
|
|
|
|
- &self,
|
|
|
|
|
- binary: &Binary<'b>,
|
|
|
|
|
- function: FunctionValue,
|
|
|
|
|
- ty: &ast::Type,
|
|
|
|
|
- data: &mut PointerValue<'b>,
|
|
|
|
|
- end: PointerValue<'b>,
|
|
|
|
|
- ns: &ast::Namespace,
|
|
|
|
|
- ) -> BasicValueEnum<'b> {
|
|
|
|
|
- match &ty {
|
|
|
|
|
- ast::Type::Bool
|
|
|
|
|
- | ast::Type::Address(_)
|
|
|
|
|
- | ast::Type::Contract(_)
|
|
|
|
|
- | ast::Type::Int(_)
|
|
|
|
|
- | ast::Type::Uint(_)
|
|
|
|
|
- | ast::Type::Bytes(_) => {
|
|
|
|
|
- let (arg, arglen) = self.decode_primitive(binary, ty, *data, ns);
|
|
|
|
|
-
|
|
|
|
|
- *data = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- binary.context.i8_type(),
|
|
|
|
|
- *data,
|
|
|
|
|
- &[binary.context.i32_type().const_int(arglen, false)],
|
|
|
|
|
- "abi_ptr",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- self.check_overrun(binary, function, *data, end, false);
|
|
|
|
|
-
|
|
|
|
|
- arg
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Enum(n) => self.decode_ty(binary, function, &ns.enums[*n].ty, data, end, ns),
|
|
|
|
|
- ast::Type::UserType(n) => {
|
|
|
|
|
- self.decode_ty(binary, function, &ns.user_types[*n].ty, data, end, ns)
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Struct(str_ty) => {
|
|
|
|
|
- let llvm_ty = binary.llvm_type(ty.deref_any(), ns);
|
|
|
|
|
-
|
|
|
|
|
- let size = llvm_ty
|
|
|
|
|
- .size_of()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .const_cast(binary.context.i32_type(), false);
|
|
|
|
|
-
|
|
|
|
|
- let new = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_call(
|
|
|
|
|
- binary.module.get_function("__malloc").unwrap(),
|
|
|
|
|
- &[size.into()],
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- .try_as_basic_value()
|
|
|
|
|
- .left()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .into_pointer_value();
|
|
|
|
|
-
|
|
|
|
|
- for (i, field) in str_ty.definition(ns).fields.iter().enumerate() {
|
|
|
|
|
- let elem = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- llvm_ty,
|
|
|
|
|
- new,
|
|
|
|
|
- &[
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- binary.context.i32_type().const_int(i as u64, false),
|
|
|
|
|
- ],
|
|
|
|
|
- field.name_as_str(),
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let val = self.decode_ty(binary, function, &field.ty, data, end, ns);
|
|
|
|
|
-
|
|
|
|
|
- let val = if field.ty.deref_memory().is_fixed_reference_type(ns) {
|
|
|
|
|
- let field_ty = binary.llvm_type(&field.ty, ns);
|
|
|
|
|
- binary.builder.build_load(
|
|
|
|
|
- field_ty,
|
|
|
|
|
- val.into_pointer_value(),
|
|
|
|
|
- field.name_as_str(),
|
|
|
|
|
- )
|
|
|
|
|
- } else {
|
|
|
|
|
- val
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_store(elem, val);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- new.into()
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Array(_, dim) => {
|
|
|
|
|
- if let Some(ast::ArrayLength::Fixed(d)) = dim.last() {
|
|
|
|
|
- let llvm_ty = binary.llvm_type(ty.deref_any(), ns);
|
|
|
|
|
-
|
|
|
|
|
- let size = llvm_ty
|
|
|
|
|
- .size_of()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .const_cast(binary.context.i32_type(), false);
|
|
|
|
|
-
|
|
|
|
|
- let ty = ty.array_deref();
|
|
|
|
|
-
|
|
|
|
|
- let new = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_call(
|
|
|
|
|
- binary.module.get_function("__malloc").unwrap(),
|
|
|
|
|
- &[size.into()],
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- .try_as_basic_value()
|
|
|
|
|
- .left()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .into_pointer_value();
|
|
|
|
|
-
|
|
|
|
|
- binary.emit_static_loop_with_pointer(
|
|
|
|
|
- function,
|
|
|
|
|
- binary.context.i64_type().const_zero(),
|
|
|
|
|
- binary
|
|
|
|
|
- .context
|
|
|
|
|
- .i64_type()
|
|
|
|
|
- .const_int(d.to_u64().unwrap(), false),
|
|
|
|
|
- data,
|
|
|
|
|
- |index: IntValue<'b>, data: &mut PointerValue<'b>| {
|
|
|
|
|
- let elem = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- llvm_ty,
|
|
|
|
|
- new,
|
|
|
|
|
- &[binary.context.i32_type().const_zero(), index],
|
|
|
|
|
- "index_access",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let val = self.decode_ty(binary, function, &ty, data, end, ns);
|
|
|
|
|
-
|
|
|
|
|
- let val = if ty.deref_memory().is_fixed_reference_type(ns) {
|
|
|
|
|
- let field_ty = binary.llvm_type(ty.deref_memory(), ns);
|
|
|
|
|
- binary.builder.build_load(
|
|
|
|
|
- field_ty,
|
|
|
|
|
- val.into_pointer_value(),
|
|
|
|
|
- "elem",
|
|
|
|
|
- )
|
|
|
|
|
- } else {
|
|
|
|
|
- val
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_store(elem, val);
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- new.into()
|
|
|
|
|
- } else {
|
|
|
|
|
- let len = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_alloca(binary.context.i32_type(), "length");
|
|
|
|
|
-
|
|
|
|
|
- *data = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_call(
|
|
|
|
|
- binary.module.get_function("compact_decode_u32").unwrap(),
|
|
|
|
|
- &[(*data).into(), len.into()],
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- .try_as_basic_value()
|
|
|
|
|
- .left()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .into_pointer_value();
|
|
|
|
|
-
|
|
|
|
|
- let len = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(binary.context.i32_type(), len, "array.len")
|
|
|
|
|
- .into_int_value();
|
|
|
|
|
-
|
|
|
|
|
- // details about our array elements
|
|
|
|
|
- let elem_ty = binary.llvm_field_ty(&ty.array_elem(), ns);
|
|
|
|
|
- let elem_size = elem_ty
|
|
|
|
|
- .size_of()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .const_cast(binary.context.i32_type(), false);
|
|
|
|
|
-
|
|
|
|
|
- let init = binary.builder.build_int_to_ptr(
|
|
|
|
|
- binary.context.i32_type().const_all_ones(),
|
|
|
|
|
- binary.context.i8_type().ptr_type(AddressSpace::default()),
|
|
|
|
|
- "invalid",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- let v = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_call(
|
|
|
|
|
- binary.module.get_function("vector_new").unwrap(),
|
|
|
|
|
- &[len.into(), elem_size.into(), init.into()],
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- .try_as_basic_value()
|
|
|
|
|
- .left()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .into_pointer_value();
|
|
|
|
|
-
|
|
|
|
|
- binary.emit_loop_cond_first_with_pointer(
|
|
|
|
|
- function,
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- len,
|
|
|
|
|
- data,
|
|
|
|
|
- |elem_no: IntValue<'b>, data: &mut PointerValue<'b>| {
|
|
|
|
|
- let index = binary.builder.build_int_mul(elem_no, elem_size, "");
|
|
|
|
|
-
|
|
|
|
|
- let element_start = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- binary.context.get_struct_type("struct.vector").unwrap(),
|
|
|
|
|
- v,
|
|
|
|
|
- &[
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- binary.context.i32_type().const_int(2, false),
|
|
|
|
|
- index,
|
|
|
|
|
- ],
|
|
|
|
|
- "data",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let ty = ty.array_deref();
|
|
|
|
|
-
|
|
|
|
|
- let val = self.decode_ty(binary, function, &ty, data, end, ns);
|
|
|
|
|
-
|
|
|
|
|
- let val = if ty.deref_memory().is_fixed_reference_type(ns) {
|
|
|
|
|
- let load_ty = binary.llvm_type(ty.deref_memory(), ns);
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(load_ty, val.into_pointer_value(), "elem")
|
|
|
|
|
- } else {
|
|
|
|
|
- val
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_store(element_start, val);
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
- v.into()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::String | ast::Type::DynamicBytes => {
|
|
|
|
|
- let from = binary.builder.build_alloca(
|
|
|
|
|
- binary.context.i8_type().ptr_type(AddressSpace::default()),
|
|
|
|
|
- "from",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_store(from, *data);
|
|
|
|
|
-
|
|
|
|
|
- let v = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_call(
|
|
|
|
|
- binary.module.get_function("scale_decode_string").unwrap(),
|
|
|
|
|
- &[from.into()],
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- .try_as_basic_value()
|
|
|
|
|
- .left()
|
|
|
|
|
- .unwrap();
|
|
|
|
|
-
|
|
|
|
|
- *data = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(
|
|
|
|
|
- binary.context.i8_type().ptr_type(AddressSpace::default()),
|
|
|
|
|
- from,
|
|
|
|
|
- "data",
|
|
|
|
|
- )
|
|
|
|
|
- .into_pointer_value();
|
|
|
|
|
-
|
|
|
|
|
- self.check_overrun(binary, function, *data, end, false);
|
|
|
|
|
-
|
|
|
|
|
- v
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Ref(ty) => self.decode_ty(binary, function, ty, data, end, ns),
|
|
|
|
|
- ast::Type::ExternalFunction { .. } => {
|
|
|
|
|
- let address =
|
|
|
|
|
- self.decode_ty(binary, function, &ast::Type::Address(false), data, end, ns);
|
|
|
|
|
- let selector =
|
|
|
|
|
- self.decode_ty(binary, function, &ast::Type::Bytes(4), data, end, ns);
|
|
|
|
|
-
|
|
|
|
|
- let ty = binary.llvm_type(ty, ns);
|
|
|
|
|
-
|
|
|
|
|
- let ef = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_call(
|
|
|
|
|
- binary.module.get_function("__malloc").unwrap(),
|
|
|
|
|
- &[ty.size_of()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .const_cast(binary.context.i32_type(), false)
|
|
|
|
|
- .into()],
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- .try_as_basic_value()
|
|
|
|
|
- .left()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .into_pointer_value();
|
|
|
|
|
-
|
|
|
|
|
- let address_member = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- ty,
|
|
|
|
|
- ef,
|
|
|
|
|
- &[
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- binary.context.i32_type().const_int(1, false),
|
|
|
|
|
- ],
|
|
|
|
|
- "address",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_store(address_member, address);
|
|
|
|
|
-
|
|
|
|
|
- let selector_member = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- ty,
|
|
|
|
|
- ef,
|
|
|
|
|
- &[
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- ],
|
|
|
|
|
- "selector",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_store(selector_member, selector);
|
|
|
|
|
-
|
|
|
|
|
- ef.into()
|
|
|
|
|
- }
|
|
|
|
|
- _ => unreachable!(),
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// ABI encode a single primitive
|
|
|
|
|
- fn encode_primitive(
|
|
|
|
|
- &self,
|
|
|
|
|
- binary: &Binary,
|
|
|
|
|
- load: bool,
|
|
|
|
|
- ty: &ast::Type,
|
|
|
|
|
- dest: PointerValue,
|
|
|
|
|
- arg: BasicValueEnum,
|
|
|
|
|
- ns: &ast::Namespace,
|
|
|
|
|
- ) -> u64 {
|
|
|
|
|
- match ty {
|
|
|
|
|
- ast::Type::Bool => {
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- let load_ty = binary.llvm_type(ty, ns);
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(load_ty, arg.into_pointer_value(), "")
|
|
|
|
|
- } else {
|
|
|
|
|
- arg
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_store(
|
|
|
|
|
- dest,
|
|
|
|
|
- binary.builder.build_int_z_extend(
|
|
|
|
|
- arg.into_int_value(),
|
|
|
|
|
- binary.context.i8_type(),
|
|
|
|
|
- "bool",
|
|
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- 1
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Uint(_) | ast::Type::Int(_) => {
|
|
|
|
|
- let len = match ty {
|
|
|
|
|
- ast::Type::Uint(n) | ast::Type::Int(n) => *n as u64 / 8,
|
|
|
|
|
- _ => ns.address_length as u64,
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- let load_ty = binary.llvm_type(ty, ns);
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(load_ty, arg.into_pointer_value(), "")
|
|
|
|
|
- } else {
|
|
|
|
|
- arg
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // substrate only supports power-of-two types; upcast to correct type
|
|
|
|
|
- let power_of_two_len = len.next_power_of_two();
|
|
|
|
|
-
|
|
|
|
|
- let arg = if len == power_of_two_len {
|
|
|
|
|
- arg.into_int_value()
|
|
|
|
|
- } else if ty.is_signed_int(ns) {
|
|
|
|
|
- binary.builder.build_int_s_extend(
|
|
|
|
|
- arg.into_int_value(),
|
|
|
|
|
- binary
|
|
|
|
|
- .context
|
|
|
|
|
- .custom_width_int_type(power_of_two_len as u32 * 8),
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- } else {
|
|
|
|
|
- binary.builder.build_int_z_extend(
|
|
|
|
|
- arg.into_int_value(),
|
|
|
|
|
- binary
|
|
|
|
|
- .context
|
|
|
|
|
- .custom_width_int_type(power_of_two_len as u32 * 8),
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_store(dest, arg);
|
|
|
|
|
-
|
|
|
|
|
- power_of_two_len
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Contract(_) | ast::Type::Address(_) => {
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(binary.address_type(ns), arg.into_pointer_value(), "")
|
|
|
|
|
- } else {
|
|
|
|
|
- arg
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_store(dest, arg.into_array_value());
|
|
|
|
|
-
|
|
|
|
|
- ns.address_length as u64
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Bytes(n) => {
|
|
|
|
|
- let val = if load {
|
|
|
|
|
- arg.into_pointer_value()
|
|
|
|
|
- } else {
|
|
|
|
|
- let temp = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_alloca(arg.into_int_value().get_type(), &format!("bytes{n}"));
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_store(temp, arg.into_int_value());
|
|
|
|
|
-
|
|
|
|
|
- temp
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // byte order needs to be reversed. e.g. hex"11223344" should be 0x10 0x11 0x22 0x33 0x44
|
|
|
|
|
- binary.builder.build_call(
|
|
|
|
|
- binary.module.get_function("__leNtobeN").unwrap(),
|
|
|
|
|
- &[
|
|
|
|
|
- val.into(),
|
|
|
|
|
- dest.into(),
|
|
|
|
|
- binary.context.i32_type().const_int(*n as u64, false).into(),
|
|
|
|
|
- ],
|
|
|
|
|
- "",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- *n as u64
|
|
|
|
|
- }
|
|
|
|
|
- _ => unimplemented!(),
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// recursively encode argument. The encoded data is written to the data pointer,
|
|
|
|
|
- /// and the pointer is updated point after the encoded data.
|
|
|
|
|
- ///
|
|
|
|
|
- /// FIXME: this function takes a "load" arguments, which tells the encoded whether the data should be
|
|
|
|
|
- /// dereferenced. However, this is already encoded by the fact it is a Type::Ref(..) type. So, the load
|
|
|
|
|
- /// argument should be removed from this function.
|
|
|
|
|
- pub fn encode_ty<'x>(
|
|
|
|
|
- &self,
|
|
|
|
|
- binary: &Binary<'x>,
|
|
|
|
|
- ns: &ast::Namespace,
|
|
|
|
|
- load: bool,
|
|
|
|
|
- packed: bool,
|
|
|
|
|
- function: FunctionValue,
|
|
|
|
|
- ty: &ast::Type,
|
|
|
|
|
- arg: BasicValueEnum<'x>,
|
|
|
|
|
- data: &mut PointerValue<'x>,
|
|
|
|
|
- ) {
|
|
|
|
|
- match &ty {
|
|
|
|
|
- ast::Type::Bool
|
|
|
|
|
- | ast::Type::Address(_)
|
|
|
|
|
- | ast::Type::Contract(_)
|
|
|
|
|
- | ast::Type::Int(_)
|
|
|
|
|
- | ast::Type::Uint(_)
|
|
|
|
|
- | ast::Type::Bytes(_) => {
|
|
|
|
|
- let arglen = self.encode_primitive(binary, load, ty, *data, arg, ns);
|
|
|
|
|
-
|
|
|
|
|
- *data = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- binary.context.i8_type(),
|
|
|
|
|
- *data,
|
|
|
|
|
- &[binary.context.i32_type().const_int(arglen, false)],
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::UserType(no) => self.encode_ty(
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- load,
|
|
|
|
|
- packed,
|
|
|
|
|
- function,
|
|
|
|
|
- &ns.user_types[*no].ty,
|
|
|
|
|
- arg,
|
|
|
|
|
- data,
|
|
|
|
|
- ),
|
|
|
|
|
- ast::Type::Enum(no) => self.encode_ty(
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- load,
|
|
|
|
|
- packed,
|
|
|
|
|
- function,
|
|
|
|
|
- &ns.enums[*no].ty,
|
|
|
|
|
- arg,
|
|
|
|
|
- data,
|
|
|
|
|
- ),
|
|
|
|
|
- ast::Type::Array(_, dim) if matches!(dim.last(), Some(ast::ArrayLength::Fixed(_))) => {
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(load_ty, arg.into_pointer_value(), "")
|
|
|
|
|
- .into_pointer_value()
|
|
|
|
|
- } else {
|
|
|
|
|
- arg.into_pointer_value()
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let null_array = binary.context.append_basic_block(function, "null_array");
|
|
|
|
|
- let normal_array = binary.context.append_basic_block(function, "normal_array");
|
|
|
|
|
- let done_array = binary.context.append_basic_block(function, "done_array");
|
|
|
|
|
-
|
|
|
|
|
- let dim = ty.array_length().unwrap().to_u64().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- let elem_ty = ty.array_deref();
|
|
|
|
|
-
|
|
|
|
|
- let is_null = binary.builder.build_is_null(arg, "is_null");
|
|
|
|
|
-
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_conditional_branch(is_null, null_array, normal_array);
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(normal_array);
|
|
|
|
|
-
|
|
|
|
|
- let mut normal_data = *data;
|
|
|
|
|
-
|
|
|
|
|
- binary.emit_static_loop_with_pointer(
|
|
|
|
|
- function,
|
|
|
|
|
- binary.context.i64_type().const_zero(),
|
|
|
|
|
- binary.context.i64_type().const_int(dim, false),
|
|
|
|
|
- &mut normal_data,
|
|
|
|
|
- |index, elem_data| {
|
|
|
|
|
- let elem = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- binary.llvm_type(ty, ns),
|
|
|
|
|
- arg,
|
|
|
|
|
- &[binary.context.i32_type().const_zero(), index],
|
|
|
|
|
- "index_access",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- self.encode_ty(
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- !elem_ty.is_fixed_reference_type(ns),
|
|
|
|
|
- packed,
|
|
|
|
|
- function,
|
|
|
|
|
- &elem_ty,
|
|
|
|
|
- elem.into(),
|
|
|
|
|
- elem_data,
|
|
|
|
|
- );
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_unconditional_branch(done_array);
|
|
|
|
|
-
|
|
|
|
|
- let normal_array = binary.builder.get_insert_block().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(null_array);
|
|
|
|
|
-
|
|
|
|
|
- let mut null_data = *data;
|
|
|
|
|
-
|
|
|
|
|
- let elem = binary.default_value(elem_ty.deref_any(), ns);
|
|
|
|
|
-
|
|
|
|
|
- binary.emit_static_loop_with_pointer(
|
|
|
|
|
- function,
|
|
|
|
|
- binary.context.i64_type().const_zero(),
|
|
|
|
|
- binary.context.i64_type().const_int(dim, false),
|
|
|
|
|
- &mut null_data,
|
|
|
|
|
- |_, elem_data| {
|
|
|
|
|
- self.encode_ty(
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- false,
|
|
|
|
|
- packed,
|
|
|
|
|
- function,
|
|
|
|
|
- elem_ty.deref_any(),
|
|
|
|
|
- elem,
|
|
|
|
|
- elem_data,
|
|
|
|
|
- );
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_unconditional_branch(done_array);
|
|
|
|
|
-
|
|
|
|
|
- let null_array = binary.builder.get_insert_block().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(done_array);
|
|
|
|
|
-
|
|
|
|
|
- let either_data = binary.builder.build_phi(
|
|
|
|
|
- binary.context.i8_type().ptr_type(AddressSpace::default()),
|
|
|
|
|
- "either_data",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- either_data.add_incoming(&[(&normal_data, normal_array), (&null_data, null_array)]);
|
|
|
|
|
-
|
|
|
|
|
- *data = either_data.as_basic_value().into_pointer_value()
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Array(..) => {
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(load_ty, arg.into_pointer_value(), "")
|
|
|
|
|
- } else {
|
|
|
|
|
- arg
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let len = binary.vector_len(arg);
|
|
|
|
|
-
|
|
|
|
|
- if !packed {
|
|
|
|
|
- *data = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_call(
|
|
|
|
|
- binary.module.get_function("compact_encode_u32").unwrap(),
|
|
|
|
|
- &[(*data).into(), len.into()],
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- .try_as_basic_value()
|
|
|
|
|
- .left()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .into_pointer_value();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let elem_ty = ty.array_deref();
|
|
|
|
|
-
|
|
|
|
|
- binary.emit_loop_cond_first_with_pointer(
|
|
|
|
|
- function,
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- len,
|
|
|
|
|
- data,
|
|
|
|
|
- |elem_no, data| {
|
|
|
|
|
- let elem =
|
|
|
|
|
- binary.array_subscript(ty, arg.into_pointer_value(), elem_no, ns);
|
|
|
|
|
-
|
|
|
|
|
- self.encode_ty(
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- !elem_ty.deref_any().is_fixed_reference_type(ns),
|
|
|
|
|
- packed,
|
|
|
|
|
- function,
|
|
|
|
|
- elem_ty.deref_any(),
|
|
|
|
|
- elem.into(),
|
|
|
|
|
- data,
|
|
|
|
|
- );
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Struct(str_ty) => {
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(
|
|
|
|
|
- load_ty,
|
|
|
|
|
- arg.into_pointer_value(),
|
|
|
|
|
- &format!("encode_{}", str_ty.definition(ns).name),
|
|
|
|
|
- )
|
|
|
|
|
- .into_pointer_value()
|
|
|
|
|
- } else {
|
|
|
|
|
- arg.into_pointer_value()
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let null_struct = binary.context.append_basic_block(function, "null_struct");
|
|
|
|
|
- let normal_struct = binary.context.append_basic_block(function, "normal_struct");
|
|
|
|
|
- let done_struct = binary.context.append_basic_block(function, "done_struct");
|
|
|
|
|
-
|
|
|
|
|
- let is_null = binary.builder.build_is_null(arg, "is_null");
|
|
|
|
|
-
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_conditional_branch(is_null, null_struct, normal_struct);
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(normal_struct);
|
|
|
|
|
-
|
|
|
|
|
- let mut normal_data = *data;
|
|
|
|
|
- for (i, field) in str_ty.definition(ns).fields.iter().enumerate() {
|
|
|
|
|
- let elem = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- binary.llvm_type(ty, ns),
|
|
|
|
|
- arg,
|
|
|
|
|
- &[
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- binary.context.i32_type().const_int(i as u64, false),
|
|
|
|
|
- ],
|
|
|
|
|
- field.name_as_str(),
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- self.encode_ty(
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- !field.ty.is_fixed_reference_type(ns),
|
|
|
|
|
- packed,
|
|
|
|
|
- function,
|
|
|
|
|
- &field.ty,
|
|
|
|
|
- elem.into(),
|
|
|
|
|
- &mut normal_data,
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_unconditional_branch(done_struct);
|
|
|
|
|
-
|
|
|
|
|
- let normal_struct = binary.builder.get_insert_block().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(null_struct);
|
|
|
|
|
-
|
|
|
|
|
- let mut null_data = *data;
|
|
|
|
|
-
|
|
|
|
|
- for field in &str_ty.definition(ns).fields {
|
|
|
|
|
- let elem = binary.default_value(&field.ty, ns);
|
|
|
|
|
-
|
|
|
|
|
- self.encode_ty(
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- false,
|
|
|
|
|
- packed,
|
|
|
|
|
- function,
|
|
|
|
|
- &field.ty,
|
|
|
|
|
- elem,
|
|
|
|
|
- &mut null_data,
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_unconditional_branch(done_struct);
|
|
|
|
|
-
|
|
|
|
|
- let null_struct = binary.builder.get_insert_block().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(done_struct);
|
|
|
|
|
-
|
|
|
|
|
- let either_data = binary.builder.build_phi(
|
|
|
|
|
- binary.context.i8_type().ptr_type(AddressSpace::default()),
|
|
|
|
|
- "either_data",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- either_data
|
|
|
|
|
- .add_incoming(&[(&normal_data, normal_struct), (&null_data, null_struct)]);
|
|
|
|
|
-
|
|
|
|
|
- *data = either_data.as_basic_value().into_pointer_value()
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Ref(ty) => {
|
|
|
|
|
- self.encode_ty(
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- !ty.is_fixed_reference_type(ns),
|
|
|
|
|
- packed,
|
|
|
|
|
- function,
|
|
|
|
|
- ty,
|
|
|
|
|
- arg,
|
|
|
|
|
- data,
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::String | ast::Type::DynamicBytes => {
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(load_ty, arg.into_pointer_value(), "")
|
|
|
|
|
- } else {
|
|
|
|
|
- arg
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let string_len = binary.vector_len(arg);
|
|
|
|
|
-
|
|
|
|
|
- let string_data = binary.vector_bytes(arg);
|
|
|
|
|
-
|
|
|
|
|
- if !packed {
|
|
|
|
|
- let function = binary.module.get_function("scale_encode_string").unwrap();
|
|
|
|
|
-
|
|
|
|
|
- *data = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_call(
|
|
|
|
|
- function,
|
|
|
|
|
- &[(*data).into(), string_data.into(), string_len.into()],
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- .try_as_basic_value()
|
|
|
|
|
- .left()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .into_pointer_value();
|
|
|
|
|
- } else {
|
|
|
|
|
- binary.builder.build_call(
|
|
|
|
|
- binary.module.get_function("__memcpy").unwrap(),
|
|
|
|
|
- &[(*data).into(), string_data.into(), string_len.into()],
|
|
|
|
|
- "",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- *data = unsafe {
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_gep(binary.context.i8_type(), *data, &[string_len], "")
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::ExternalFunction { .. } => {
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(load_ty, arg.into_pointer_value(), "")
|
|
|
|
|
- } else {
|
|
|
|
|
- arg
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let address_member = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- binary.llvm_type(ty, ns),
|
|
|
|
|
- arg.into_pointer_value(),
|
|
|
|
|
- &[
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- binary.context.i32_type().const_int(1, false),
|
|
|
|
|
- ],
|
|
|
|
|
- "address",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let address =
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(binary.address_type(ns), address_member, "address");
|
|
|
|
|
-
|
|
|
|
|
- self.encode_ty(
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- false,
|
|
|
|
|
- false,
|
|
|
|
|
- function,
|
|
|
|
|
- &ast::Type::Address(false),
|
|
|
|
|
- address,
|
|
|
|
|
- data,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- let selector_member = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- binary.llvm_type(ty, ns),
|
|
|
|
|
- arg.into_pointer_value(),
|
|
|
|
|
- &[
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- ],
|
|
|
|
|
- "selector",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let selector = binary.builder.build_load(
|
|
|
|
|
- binary.context.i32_type(),
|
|
|
|
|
- selector_member,
|
|
|
|
|
- "selector",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- self.encode_ty(
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- false,
|
|
|
|
|
- false,
|
|
|
|
|
- function,
|
|
|
|
|
- &ast::Type::Bytes(4),
|
|
|
|
|
- selector,
|
|
|
|
|
- data,
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::FunctionSelector => self.encode_ty(
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- load,
|
|
|
|
|
- packed,
|
|
|
|
|
- function,
|
|
|
|
|
- &ast::Type::Bytes(4),
|
|
|
|
|
- arg,
|
|
|
|
|
- data,
|
|
|
|
|
- ),
|
|
|
|
|
- _ => unreachable!(),
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// Calculate the maximum space a type will need when encoded. This is used for
|
|
|
|
|
- /// allocating enough space to do abi encoding. The length for vectors is always
|
|
|
|
|
- /// assumed to be five, even when it can be encoded in less bytes. The overhead
|
|
|
|
|
- /// of calculating the exact size is not worth reducing the malloc by a few bytes.
|
|
|
|
|
- ///
|
|
|
|
|
- /// FIXME: this function takes a "load" arguments, which tells the encoded whether the data should be
|
|
|
|
|
- /// dereferenced. However, this is already encoded by the fact it is a Type::Ref(..) type. So, the load
|
|
|
|
|
- /// argument should be removed from this function.
|
|
|
|
|
- pub fn encoded_length<'x>(
|
|
|
|
|
- arg: BasicValueEnum<'x>,
|
|
|
|
|
- load: bool,
|
|
|
|
|
- packed: bool,
|
|
|
|
|
- ty: &ast::Type,
|
|
|
|
|
- function: FunctionValue,
|
|
|
|
|
- binary: &Binary<'x>,
|
|
|
|
|
- ns: &ast::Namespace,
|
|
|
|
|
- ) -> IntValue<'x> {
|
|
|
|
|
- match ty {
|
|
|
|
|
- ast::Type::Bool => binary.context.i32_type().const_int(1, false),
|
|
|
|
|
- ast::Type::Uint(n) | ast::Type::Int(n) => {
|
|
|
|
|
- binary.context.i32_type().const_int(*n as u64 / 8, false)
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Bytes(n) => binary.context.i32_type().const_int(*n as u64, false),
|
|
|
|
|
- ast::Type::FunctionSelector => binary
|
|
|
|
|
- .context
|
|
|
|
|
- .i32_type()
|
|
|
|
|
- .const_int(ns.target.selector_length() as u64, false),
|
|
|
|
|
- ast::Type::Address(_) | ast::Type::Contract(_) => binary
|
|
|
|
|
- .context
|
|
|
|
|
- .i32_type()
|
|
|
|
|
- .const_int(ns.address_length as u64, false),
|
|
|
|
|
- ast::Type::Enum(n) => SubstrateTarget::encoded_length(
|
|
|
|
|
- arg,
|
|
|
|
|
- load,
|
|
|
|
|
- packed,
|
|
|
|
|
- &ns.enums[*n].ty,
|
|
|
|
|
- function,
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- ),
|
|
|
|
|
- ast::Type::Struct(str_ty) => {
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(
|
|
|
|
|
- load_ty,
|
|
|
|
|
- arg.into_pointer_value(),
|
|
|
|
|
- &format!("encoded_length_struct_{}", str_ty.definition(ns).name),
|
|
|
|
|
- )
|
|
|
|
|
- .into_pointer_value()
|
|
|
|
|
- } else {
|
|
|
|
|
- arg.into_pointer_value()
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let normal_struct = binary.context.append_basic_block(function, "normal_struct");
|
|
|
|
|
- let null_struct = binary.context.append_basic_block(function, "null_struct");
|
|
|
|
|
- let done_struct = binary.context.append_basic_block(function, "done_struct");
|
|
|
|
|
-
|
|
|
|
|
- let is_null = binary.builder.build_is_null(arg, "is_null");
|
|
|
|
|
-
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_conditional_branch(is_null, null_struct, normal_struct);
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(normal_struct);
|
|
|
|
|
-
|
|
|
|
|
- let mut normal_sum = binary.context.i32_type().const_zero();
|
|
|
|
|
-
|
|
|
|
|
- // avoid generating load instructions for structs with only fixed fields
|
|
|
|
|
- for (i, field) in str_ty.definition(ns).fields.iter().enumerate() {
|
|
|
|
|
- let elem = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- binary.llvm_type(ty, ns),
|
|
|
|
|
- arg,
|
|
|
|
|
- &[
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- binary.context.i32_type().const_int(i as u64, false),
|
|
|
|
|
- ],
|
|
|
|
|
- field.name_as_str(),
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- normal_sum = binary.builder.build_int_add(
|
|
|
|
|
- normal_sum,
|
|
|
|
|
- SubstrateTarget::encoded_length(
|
|
|
|
|
- elem.into(),
|
|
|
|
|
- !field.ty.is_fixed_reference_type(ns),
|
|
|
|
|
- packed,
|
|
|
|
|
- &field.ty,
|
|
|
|
|
- function,
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- ),
|
|
|
|
|
- "",
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_unconditional_branch(done_struct);
|
|
|
|
|
-
|
|
|
|
|
- let normal_struct = binary.builder.get_insert_block().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(null_struct);
|
|
|
|
|
-
|
|
|
|
|
- let mut null_sum = binary.context.i32_type().const_zero();
|
|
|
|
|
-
|
|
|
|
|
- for field in &str_ty.definition(ns).fields {
|
|
|
|
|
- null_sum = binary.builder.build_int_add(
|
|
|
|
|
- null_sum,
|
|
|
|
|
- SubstrateTarget::encoded_length(
|
|
|
|
|
- binary.default_value(&field.ty, ns),
|
|
|
|
|
- false,
|
|
|
|
|
- packed,
|
|
|
|
|
- &field.ty,
|
|
|
|
|
- function,
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- ),
|
|
|
|
|
- "",
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_unconditional_branch(done_struct);
|
|
|
|
|
-
|
|
|
|
|
- let null_struct = binary.builder.get_insert_block().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(done_struct);
|
|
|
|
|
-
|
|
|
|
|
- let sum = binary.builder.build_phi(binary.context.i32_type(), "sum");
|
|
|
|
|
-
|
|
|
|
|
- sum.add_incoming(&[(&normal_sum, normal_struct), (&null_sum, null_struct)]);
|
|
|
|
|
-
|
|
|
|
|
- sum.as_basic_value().into_int_value()
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Array(_, dims)
|
|
|
|
|
- if matches!(dims.last(), Some(ast::ArrayLength::Fixed(_))) =>
|
|
|
|
|
- {
|
|
|
|
|
- let array_length = binary
|
|
|
|
|
- .context
|
|
|
|
|
- .i32_type()
|
|
|
|
|
- .const_int(ty.array_length().unwrap().to_u64().unwrap(), false);
|
|
|
|
|
-
|
|
|
|
|
- let elem_ty = ty.array_deref();
|
|
|
|
|
-
|
|
|
|
|
- if elem_ty.is_dynamic(ns) {
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- let load_ty = binary.llvm_var_ty(ty, ns);
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(load_ty, arg.into_pointer_value(), "")
|
|
|
|
|
- .into_pointer_value()
|
|
|
|
|
- } else {
|
|
|
|
|
- arg.into_pointer_value()
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let normal_array = binary.context.append_basic_block(function, "normal_array");
|
|
|
|
|
- let null_array = binary.context.append_basic_block(function, "null_array");
|
|
|
|
|
- let done_array = binary.context.append_basic_block(function, "done_array");
|
|
|
|
|
-
|
|
|
|
|
- let is_null = binary.builder.build_is_null(arg, "is_null");
|
|
|
|
|
-
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_conditional_branch(is_null, null_array, normal_array);
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(normal_array);
|
|
|
|
|
-
|
|
|
|
|
- let mut normal_length = binary.context.i32_type().const_zero();
|
|
|
|
|
-
|
|
|
|
|
- // if the array contains dynamic elements, we have to iterate over
|
|
|
|
|
- // every one and calculate its length
|
|
|
|
|
- binary.emit_static_loop_with_int(
|
|
|
|
|
- function,
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- array_length,
|
|
|
|
|
- &mut normal_length,
|
|
|
|
|
- |index, sum| {
|
|
|
|
|
- let elem = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- binary.llvm_type(ty, ns),
|
|
|
|
|
- arg,
|
|
|
|
|
- &[binary.context.i32_type().const_zero(), index],
|
|
|
|
|
- "index_access",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- *sum = binary.builder.build_int_add(
|
|
|
|
|
- SubstrateTarget::encoded_length(
|
|
|
|
|
- elem.into(),
|
|
|
|
|
- !elem_ty.deref_memory().is_fixed_reference_type(ns),
|
|
|
|
|
- packed,
|
|
|
|
|
- &elem_ty,
|
|
|
|
|
- function,
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- ),
|
|
|
|
|
- *sum,
|
|
|
|
|
- "",
|
|
|
|
|
- );
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_unconditional_branch(done_array);
|
|
|
|
|
-
|
|
|
|
|
- let normal_array = binary.builder.get_insert_block().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(null_array);
|
|
|
|
|
-
|
|
|
|
|
- let elem = binary.default_value(elem_ty.deref_any(), ns);
|
|
|
|
|
-
|
|
|
|
|
- let null_length = binary.builder.build_int_mul(
|
|
|
|
|
- SubstrateTarget::encoded_length(
|
|
|
|
|
- elem,
|
|
|
|
|
- false,
|
|
|
|
|
- packed,
|
|
|
|
|
- elem_ty.deref_any(),
|
|
|
|
|
- function,
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- ),
|
|
|
|
|
- array_length,
|
|
|
|
|
- "",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_unconditional_branch(done_array);
|
|
|
|
|
-
|
|
|
|
|
- let null_array = binary.builder.get_insert_block().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.position_at_end(done_array);
|
|
|
|
|
-
|
|
|
|
|
- let encoded_length = binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_phi(binary.context.i32_type(), "encoded_length");
|
|
|
|
|
-
|
|
|
|
|
- encoded_length.add_incoming(&[
|
|
|
|
|
- (&normal_length, normal_array),
|
|
|
|
|
- (&null_length, null_array),
|
|
|
|
|
- ]);
|
|
|
|
|
-
|
|
|
|
|
- encoded_length.as_basic_value().into_int_value()
|
|
|
|
|
- } else {
|
|
|
|
|
- // elements have static length
|
|
|
|
|
- let elem = binary.default_value(elem_ty.deref_any(), ns);
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_int_mul(
|
|
|
|
|
- SubstrateTarget::encoded_length(
|
|
|
|
|
- elem,
|
|
|
|
|
- false,
|
|
|
|
|
- packed,
|
|
|
|
|
- elem_ty.deref_any(),
|
|
|
|
|
- function,
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- ),
|
|
|
|
|
- array_length,
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Array(_, dims) if dims.last() == Some(&ast::ArrayLength::Dynamic) => {
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(load_ty, arg.into_pointer_value(), "")
|
|
|
|
|
- } else {
|
|
|
|
|
- arg
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let mut encoded_length = binary.context.i32_type().const_int(5, false);
|
|
|
|
|
-
|
|
|
|
|
- let array_length = binary.vector_len(arg);
|
|
|
|
|
-
|
|
|
|
|
- let elem_ty = ty.array_deref();
|
|
|
|
|
- let llvm_elem_ty = binary.llvm_field_ty(&elem_ty, ns);
|
|
|
|
|
-
|
|
|
|
|
- if elem_ty.is_dynamic(ns) {
|
|
|
|
|
- // if the array contains elements of dynamic length, we have to iterate over all of them
|
|
|
|
|
- binary.emit_loop_cond_first_with_int(
|
|
|
|
|
- function,
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- array_length,
|
|
|
|
|
- &mut encoded_length,
|
|
|
|
|
- |index, sum| {
|
|
|
|
|
- let index = binary.builder.build_int_mul(
|
|
|
|
|
- index,
|
|
|
|
|
- llvm_elem_ty
|
|
|
|
|
- .size_of()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .const_cast(binary.context.i32_type(), false),
|
|
|
|
|
- "",
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- let p = unsafe {
|
|
|
|
|
- binary.builder.build_gep(
|
|
|
|
|
- binary.llvm_type(ty, ns),
|
|
|
|
|
- arg.into_pointer_value(),
|
|
|
|
|
- &[
|
|
|
|
|
- binary.context.i32_type().const_zero(),
|
|
|
|
|
- binary.context.i32_type().const_int(2, false),
|
|
|
|
|
- index,
|
|
|
|
|
- ],
|
|
|
|
|
- "index_access",
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- *sum = binary.builder.build_int_add(
|
|
|
|
|
- SubstrateTarget::encoded_length(
|
|
|
|
|
- p.into(),
|
|
|
|
|
- !elem_ty.deref_memory().is_fixed_reference_type(ns),
|
|
|
|
|
- packed,
|
|
|
|
|
- &elem_ty,
|
|
|
|
|
- function,
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- ),
|
|
|
|
|
- *sum,
|
|
|
|
|
- "",
|
|
|
|
|
- );
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- encoded_length
|
|
|
|
|
- } else {
|
|
|
|
|
- // elements have static length
|
|
|
|
|
- let elem = binary.default_value(elem_ty.deref_any(), ns);
|
|
|
|
|
-
|
|
|
|
|
- binary.builder.build_int_add(
|
|
|
|
|
- encoded_length,
|
|
|
|
|
- binary.builder.build_int_mul(
|
|
|
|
|
- SubstrateTarget::encoded_length(
|
|
|
|
|
- elem,
|
|
|
|
|
- false,
|
|
|
|
|
- packed,
|
|
|
|
|
- elem_ty.deref_any(),
|
|
|
|
|
- function,
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- ),
|
|
|
|
|
- array_length,
|
|
|
|
|
- "",
|
|
|
|
|
- ),
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::Ref(r) => {
|
|
|
|
|
- SubstrateTarget::encoded_length(arg, load, packed, r, function, binary, ns)
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::String | ast::Type::DynamicBytes => {
|
|
|
|
|
- let arg = if load {
|
|
|
|
|
- let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
|
|
|
|
|
- binary
|
|
|
|
|
- .builder
|
|
|
|
|
- .build_load(load_ty, arg.into_pointer_value(), "")
|
|
|
|
|
- } else {
|
|
|
|
|
- arg
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // A string or bytes type has to be encoded by: one compact integer for
|
|
|
|
|
- // the length, followed by the bytes themselves. Here we assume that the
|
|
|
|
|
- // length requires 5 bytes.
|
|
|
|
|
- let len = binary.vector_len(arg);
|
|
|
|
|
-
|
|
|
|
|
- if packed {
|
|
|
|
|
- len
|
|
|
|
|
- } else {
|
|
|
|
|
- binary.builder.build_int_add(
|
|
|
|
|
- len,
|
|
|
|
|
- binary.context.i32_type().const_int(5, false),
|
|
|
|
|
- "",
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::ExternalFunction { .. } => {
|
|
|
|
|
- // address + 4 bytes selector
|
|
|
|
|
- binary
|
|
|
|
|
- .context
|
|
|
|
|
- .i32_type()
|
|
|
|
|
- .const_int(ns.address_length as u64 + 4, false)
|
|
|
|
|
- }
|
|
|
|
|
- ast::Type::UserType(user_type) => Self::encoded_length(
|
|
|
|
|
- arg,
|
|
|
|
|
- load,
|
|
|
|
|
- packed,
|
|
|
|
|
- &ns.user_types[*user_type].ty,
|
|
|
|
|
- function,
|
|
|
|
|
- binary,
|
|
|
|
|
- ns,
|
|
|
|
|
- ),
|
|
|
|
|
- _ => unreachable!(),
|
|
|
|
|
|
|
+ /// Emits the "deploy" function if `init` is `Some`, otherwise emits the "call" function.
|
|
|
|
|
+ fn emit_dispatch(&mut self, init: Option<FunctionValue>, bin: &mut Binary, ns: &Namespace) {
|
|
|
|
|
+ let ty = bin.context.void_type().fn_type(&[], false);
|
|
|
|
|
+ let name = if init.is_some() { "deploy" } else { "call" };
|
|
|
|
|
+ let func = bin.module.add_function(name, ty, None);
|
|
|
|
|
+ let (input, input_length) = self.public_function_prelude(bin, func);
|
|
|
|
|
+ if let Some(initializer) = init {
|
|
|
|
|
+ bin.builder.build_call(initializer, &[], "");
|
|
|
}
|
|
}
|
|
|
|
|
+ let func = bin.module.get_function("substrate_dispatch").unwrap();
|
|
|
|
|
+ let args = vec![
|
|
|
|
|
+ BasicMetadataValueEnum::PointerValue(input),
|
|
|
|
|
+ BasicMetadataValueEnum::IntValue(input_length),
|
|
|
|
|
+ BasicMetadataValueEnum::IntValue(self.value_transferred(bin, ns)),
|
|
|
|
|
+ BasicMetadataValueEnum::PointerValue(bin.selector.as_pointer_value()),
|
|
|
|
|
+ ];
|
|
|
|
|
+ bin.builder.build_call(func, &args, "substrate_dispatch");
|
|
|
|
|
+ bin.builder.build_unreachable();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|