瀏覽代碼

Get whole contract storage string for substrate

Refactor getting contract storage to make this possible.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 5 年之前
父節點
當前提交
6e69c1dc6a
共有 10 個文件被更改,包括 376 次插入280 次删除
  1. 3 3
      src/emit/ethabiencoder.rs
  2. 52 71
      src/emit/ewasm.rs
  3. 160 105
      src/emit/mod.rs
  4. 29 39
      src/emit/sabre.rs
  5. 54 30
      src/emit/substrate.rs
  6. 22 30
      stdlib/stdlib.c
  7. 11 0
      stdlib/stdlib.h
  8. 二進制
      stdlib/substrate.bc
  9. 31 0
      stdlib/substrate.c
  10. 14 2
      tests/substrate_strings/mod.rs

+ 3 - 3
src/emit/ethabiencoder.rs

@@ -14,7 +14,7 @@ impl EthAbiEncoder {
     /// and the pointer is updated point after the encoded data.
     pub fn encode_ty<'a>(
         &self,
-        contract: &'a Contract,
+        contract: &Contract<'a>,
         function: FunctionValue,
         ty: &resolver::Type,
         arg: BasicValueEnum,
@@ -413,7 +413,7 @@ impl EthAbiEncoder {
     /// recursively encode a single ty
     fn decode_ty<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         function: FunctionValue,
         ty: &resolver::Type,
         to: Option<PointerValue<'b>>,
@@ -703,7 +703,7 @@ impl EthAbiEncoder {
     /// abi decode the encoded data into the BasicValueEnums
     pub fn decode<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         function: FunctionValue,
         args: &mut Vec<BasicValueEnum<'b>>,
         data: PointerValue<'b>,

+ 52 - 71
src/emit/ewasm.rs

@@ -5,7 +5,7 @@ use std::str;
 use inkwell::attributes::{Attribute, AttributeLoc};
 use inkwell::context::Context;
 use inkwell::module::Linkage;
-use inkwell::types::BasicTypeEnum;
+use inkwell::types::{BasicTypeEnum, IntType};
 use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
 use inkwell::AddressSpace;
 use inkwell::OptimizationLevel;
@@ -77,7 +77,7 @@ impl EwasmTarget {
 
     fn main_prelude<'a>(
         &self,
-        contract: &'a Contract,
+        contract: &Contract<'a>,
         function: FunctionValue,
     ) -> (PointerValue<'a>, IntValue<'a>) {
         let entry = contract.context.append_basic_block(function, "entry");
@@ -362,6 +362,15 @@ impl TargetRuntime for EwasmTarget {
         unimplemented!();
     }
 
+    fn get_storage_string<'a>(
+        &self,
+        _contract: &Contract<'a>,
+        _function: FunctionValue,
+        _slot: PointerValue,
+    ) -> PointerValue<'a> {
+        unimplemented!();
+    }
+
     fn set_storage<'a>(
         &self,
         contract: &'a Contract,
@@ -445,79 +454,51 @@ impl TargetRuntime for EwasmTarget {
         }
     }
 
-    fn get_storage<'a>(
+    fn get_storage_int<'a>(
         &self,
-        contract: &'a Contract,
+        contract: &Contract<'a>,
         _function: FunctionValue,
-        slot: PointerValue<'a>,
-        dest: PointerValue<'a>,
-    ) {
-        if dest
-            .get_type()
-            .get_element_type()
-            .into_int_type()
-            .get_bit_width()
-            == 256
-        {
-            contract.builder.build_call(
-                contract.module.get_function("storageLoad").unwrap(),
-                &[
-                    contract
-                        .builder
-                        .build_pointer_cast(
-                            slot,
-                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                            "",
-                        )
-                        .into(),
-                    contract
-                        .builder
-                        .build_pointer_cast(
-                            dest,
-                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                            "",
-                        )
-                        .into(),
-                ],
-                "",
-            );
-        } else {
-            let value = contract
-                .builder
-                .build_alloca(contract.context.custom_width_int_type(256), "value");
-
-            contract.builder.build_call(
-                contract.module.get_function("storageLoad").unwrap(),
-                &[
-                    contract
-                        .builder
-                        .build_pointer_cast(
-                            slot,
-                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                            "",
-                        )
-                        .into(),
-                    contract
-                        .builder
-                        .build_pointer_cast(
-                            value,
-                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                            "",
-                        )
-                        .into(),
-                ],
-                "",
-            );
+        slot: PointerValue,
+        ty: IntType<'a>,
+    ) -> IntValue<'a> {
+        let dest = contract.builder.build_array_alloca(
+            contract.context.i8_type(),
+            contract.context.i32_type().const_int(32, false),
+            "buf",
+        );
 
-            let val = contract.builder.build_load(
+        contract.builder.build_call(
+            contract.module.get_function("storageLoad").unwrap(),
+            &[
                 contract
                     .builder
-                    .build_pointer_cast(value, dest.get_type(), ""),
-                "",
-            );
+                    .build_pointer_cast(
+                        slot,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "",
+                    )
+                    .into(),
+                contract
+                    .builder
+                    .build_pointer_cast(
+                        dest,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "",
+                    )
+                    .into(),
+            ],
+            "",
+        );
 
-            contract.builder.build_store(dest, val);
-        }
+        contract
+            .builder
+            .build_load(
+                contract
+                    .builder
+                    .build_pointer_cast(dest, ty.ptr_type(AddressSpace::Generic), ""),
+                "loaded_int",
+            )
+            .into_int_value()
     }
 
     fn return_empty_abi(&self, contract: &Contract) {
@@ -570,7 +551,7 @@ impl TargetRuntime for EwasmTarget {
 
     fn abi_encode<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         function: FunctionValue,
         args: &[BasicValueEnum<'b>],
         spec: &resolver::FunctionDecl,
@@ -607,7 +588,7 @@ impl TargetRuntime for EwasmTarget {
 
     fn abi_decode<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         function: FunctionValue,
         args: &mut Vec<BasicValueEnum<'b>>,
         data: PointerValue<'b>,

+ 160 - 105
src/emit/mod.rs

@@ -7,6 +7,7 @@ use std::path::Path;
 use std::str;
 
 use num_bigint::BigInt;
+use num_traits::One;
 use num_traits::ToPrimitive;
 use std::collections::HashMap;
 use std::collections::VecDeque;
@@ -47,7 +48,7 @@ struct Variable<'a> {
 pub trait TargetRuntime {
     fn abi_decode<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         function: FunctionValue,
         args: &mut Vec<BasicValueEnum<'b>>,
         data: PointerValue<'b>,
@@ -56,7 +57,7 @@ pub trait TargetRuntime {
     );
     fn abi_encode<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         function: FunctionValue,
         args: &[BasicValueEnum<'b>],
         spec: &resolver::FunctionDecl,
@@ -77,13 +78,13 @@ pub trait TargetRuntime {
         slot: PointerValue<'a>,
         dest: PointerValue<'a>,
     );
-    fn get_storage<'a>(
+    fn get_storage_int<'a>(
         &self,
-        contract: &'a Contract,
+        contract: &Contract<'a>,
         function: FunctionValue,
-        slot: PointerValue<'a>,
-        dest: PointerValue<'a>,
-    );
+        slot: PointerValue,
+        ty: IntType<'a>,
+    ) -> IntValue<'a>;
 
     // Bytes and string have special storage layout
     fn set_storage_string<'a>(
@@ -93,6 +94,12 @@ pub trait TargetRuntime {
         slot: PointerValue<'a>,
         dest: PointerValue<'a>,
     );
+    fn get_storage_string<'a>(
+        &self,
+        contract: &Contract<'a>,
+        function: FunctionValue,
+        slot: PointerValue<'a>,
+    ) -> PointerValue<'a>;
 
     /// Return success without any result
     fn return_empty_abi(&self, contract: &Contract);
@@ -217,7 +224,7 @@ impl<'a> Contract<'a> {
         module.set_source_file_name(filename);
 
         // stdlib
-        let intr = load_stdlib(&context);
+        let intr = load_stdlib(&context, &contract.target);
         module.link_in_module(intr).unwrap();
 
         Contract {
@@ -259,15 +266,15 @@ impl<'a> Contract<'a> {
 
     /// Emit a loop from `from` to `to`. The closure exists to insert the body of the loop; the closure
     /// gets the loop variable passed to it as an IntValue, and a userdata PointerValue
-    pub fn emit_static_loop_with_pointer<'b, F>(
-        &'b self,
+    pub fn emit_static_loop_with_pointer<F>(
+        &self,
         function: FunctionValue,
-        from: IntValue<'b>,
-        to: IntValue<'b>,
-        data_ref: &mut PointerValue<'b>,
+        from: IntValue<'a>,
+        to: IntValue<'a>,
+        data_ref: &mut PointerValue<'a>,
         mut insert_body: F,
     ) where
-        F: FnMut(IntValue<'b>, &mut PointerValue<'b>),
+        F: FnMut(IntValue<'a>, &mut PointerValue<'a>),
     {
         let body = self.context.append_basic_block(function, "body");
         let done = self.context.append_basic_block(function, "done");
@@ -305,15 +312,15 @@ impl<'a> Contract<'a> {
 
     /// Emit a loop from `from` to `to`. The closure exists to insert the body of the loop; the closure
     /// gets the loop variable passed to it as an IntValue, and a userdata IntValue
-    pub fn emit_static_loop_with_int<'b, F>(
-        &'b self,
+    pub fn emit_static_loop_with_int<F>(
+        &self,
         function: FunctionValue,
-        from: IntValue<'b>,
-        to: IntValue<'b>,
-        data_ref: &mut IntValue<'b>,
+        from: IntValue<'a>,
+        to: IntValue<'a>,
+        data_ref: &mut IntValue<'a>,
         mut insert_body: F,
     ) where
-        F: FnMut(IntValue<'b>, &mut IntValue<'b>),
+        F: FnMut(IntValue<'a>, &mut IntValue<'a>),
     {
         let body = self.context.append_basic_block(function, "body");
         let done = self.context.append_basic_block(function, "done");
@@ -351,15 +358,15 @@ impl<'a> Contract<'a> {
     }
 
     /// Emit a loop from `from` to `to`, checking the condition _before_ the body.
-    pub fn emit_loop_cond_first_with_int<'b, F>(
-        &'b self,
+    pub fn emit_loop_cond_first_with_int<F>(
+        &self,
         function: FunctionValue,
-        from: IntValue<'b>,
-        to: IntValue<'b>,
-        data_ref: &mut IntValue<'b>,
+        from: IntValue<'a>,
+        to: IntValue<'a>,
+        data_ref: &mut IntValue<'a>,
         mut insert_body: F,
     ) where
-        F: FnMut(IntValue<'b>, &mut IntValue<'b>),
+        F: FnMut(IntValue<'a>, &mut IntValue<'a>),
     {
         let cond = self.context.append_basic_block(function, "cond");
         let body = self.context.append_basic_block(function, "body");
@@ -788,22 +795,13 @@ impl<'a> Contract<'a> {
                 self.builder.build_load(expr, "")
             }
             Expression::StorageLoad(_, ty, e) => {
-                let dest = self
-                    .builder
-                    .build_alloca(self.llvm_type(ty), "storage_load_temp");
                 // The storage slot is an i256 accessed through a pointer, so we need
                 // to store it
                 let mut slot = self
                     .expression(e, vartab, function, runtime)
                     .into_int_value();
                 let slot_ptr = self.builder.build_alloca(slot.get_type(), "slot");
-                self.storage_load(ty, &mut slot, slot_ptr, dest, function, runtime);
-
-                if ty.is_reference_type() {
-                    dest.into()
-                } else {
-                    self.builder.build_load(dest, "")
-                }
+                self.storage_load(ty, &mut slot, slot_ptr, function, runtime)
             }
             Expression::ZeroExt(_, t, e) => {
                 let e = self
@@ -1275,20 +1273,46 @@ impl<'a> Contract<'a> {
     }
 
     /// Recursively load a type from contract storage
-    fn storage_load<'b>(
-        &'b self,
+    fn storage_load(
+        &self,
         ty: &resolver::Type,
-        slot: &mut IntValue<'b>,
-        slot_ptr: PointerValue<'b>,
-        dest: PointerValue<'b>,
-        function: FunctionValue<'b>,
+        slot: &mut IntValue<'a>,
+        slot_ptr: PointerValue<'a>,
+        function: FunctionValue,
         runtime: &dyn TargetRuntime,
-    ) {
+    ) -> BasicValueEnum<'a> {
         match ty {
+            resolver::Type::Ref(ty) => self.storage_load(ty, slot, slot_ptr, function, runtime),
             resolver::Type::Array(_, dim) => {
-                let ty = ty.array_deref();
-
                 if let Some(d) = &dim[0] {
+                    let llvm_ty = self.llvm_type(ty.deref());
+                    // LLVMSizeOf() produces an i64
+                    let size = self.builder.build_int_truncate(
+                        llvm_ty.size_of().unwrap(),
+                        self.context.i32_type(),
+                        "size_of",
+                    );
+
+                    let ty = ty.array_deref();
+
+                    let new = self
+                        .builder
+                        .build_call(
+                            self.module.get_function("__malloc").unwrap(),
+                            &[size.into()],
+                            "",
+                        )
+                        .try_as_basic_value()
+                        .left()
+                        .unwrap()
+                        .into_pointer_value();
+
+                    let dest = self.builder.build_pointer_cast(
+                        new,
+                        llvm_ty.ptr_type(AddressSpace::Generic),
+                        "dest",
+                    );
+
                     self.emit_static_loop_with_int(
                         function,
                         self.context.i64_type().const_zero(),
@@ -1296,7 +1320,7 @@ impl<'a> Contract<'a> {
                             .i64_type()
                             .const_int(d.to_u64().unwrap(), false),
                         slot,
-                        |index: IntValue<'b>, slot: &mut IntValue<'b>| {
+                        |index: IntValue<'a>, slot: &mut IntValue<'a>| {
                             let elem = unsafe {
                                 self.builder.build_gep(
                                     dest,
@@ -1305,31 +1329,48 @@ impl<'a> Contract<'a> {
                                 )
                             };
 
-                            if ty.is_reference_type() {
-                                let ty = ty.deref();
-                                let val = self.builder.build_alloca(self.llvm_type(&ty), "");
-                                self.storage_load(&ty, slot, slot_ptr, val, function, runtime);
-
-                                self.builder.build_store(elem, val);
-                            } else {
-                                self.storage_load(&ty, slot, slot_ptr, elem, function, runtime);
-                            }
+                            let val = self.storage_load(&ty, slot, slot_ptr, function, runtime);
 
-                            if !ty.is_reference_type() {
-                                *slot = self.builder.build_int_add(
-                                    *slot,
-                                    self.number_literal(256, &ty.storage_slots(self.ns)),
-                                    "",
-                                );
-                            }
+                            self.builder.build_store(elem, val);
                         },
                     );
+
+                    dest.into()
                 } else {
                     // FIXME: iterate over dynamic array
+                    unimplemented!();
                 }
             }
             resolver::Type::Struct(n) => {
+                let llvm_ty = self.llvm_type(ty.deref());
+                // LLVMSizeOf() produces an i64
+                let size = self.builder.build_int_truncate(
+                    llvm_ty.size_of().unwrap(),
+                    self.context.i32_type(),
+                    "size_of",
+                );
+
+                let new = self
+                    .builder
+                    .build_call(
+                        self.module.get_function("__malloc").unwrap(),
+                        &[size.into()],
+                        "",
+                    )
+                    .try_as_basic_value()
+                    .left()
+                    .unwrap()
+                    .into_pointer_value();
+
+                let dest = self.builder.build_pointer_cast(
+                    new,
+                    llvm_ty.ptr_type(AddressSpace::Generic),
+                    "dest",
+                );
+
                 for (i, field) in self.ns.structs[*n].fields.iter().enumerate() {
+                    let val = self.storage_load(&field.ty, slot, slot_ptr, function, runtime);
+
                     let elem = unsafe {
                         self.builder.build_gep(
                             dest,
@@ -1341,46 +1382,53 @@ impl<'a> Contract<'a> {
                         )
                     };
 
-                    if field.ty.is_reference_type() {
-                        let val = self
-                            .builder
-                            .build_alloca(self.llvm_type(&field.ty.deref()), &field.name);
+                    self.builder.build_store(elem, val);
+                }
 
-                        self.storage_load(&field.ty, slot, slot_ptr, val, function, runtime);
+                dest.into()
+            }
+            resolver::Type::String | resolver::Type::DynamicBytes => {
+                self.builder.build_store(slot_ptr, *slot);
 
-                        self.builder.build_store(elem, val);
-                    } else {
-                        self.storage_load(&field.ty, slot, slot_ptr, elem, function, runtime);
-                    }
+                let ret = runtime.get_storage_string(&self, function, slot_ptr);
 
-                    if !field.ty.is_reference_type() {
-                        *slot = self.builder.build_int_add(
-                            *slot,
-                            self.number_literal(256, &field.ty.storage_slots(self.ns)),
-                            &field.name,
-                        );
-                    }
-                }
+                *slot = self.builder.build_int_add(
+                    *slot,
+                    self.number_literal(256, &BigInt::one()),
+                    "string",
+                );
+
+                ret.into()
             }
             _ => {
                 self.builder.build_store(slot_ptr, *slot);
 
-                // TODO ewasm allocates 32 bytes here, even though we have just
-                // allocated test. This can be folded into one allocation, if llvm
-                // does not already fold it into one.
-                runtime.get_storage(&self, function, slot_ptr, dest);
+                let ret = runtime.get_storage_int(
+                    &self,
+                    function,
+                    slot_ptr,
+                    self.llvm_type(ty.deref()).into_int_type(),
+                );
+
+                *slot = self.builder.build_int_add(
+                    *slot,
+                    self.number_literal(256, &BigInt::one()),
+                    "int",
+                );
+
+                ret.into()
             }
         }
     }
 
     /// Recursively store a type to contract storage
-    fn storage_store<'b>(
-        &'b self,
+    fn storage_store(
+        &self,
         ty: &resolver::Type,
-        slot: &mut IntValue<'b>,
-        slot_ptr: PointerValue<'b>,
-        dest: BasicValueEnum<'b>,
-        function: FunctionValue<'b>,
+        slot: &mut IntValue<'a>,
+        slot_ptr: PointerValue<'a>,
+        dest: BasicValueEnum<'a>,
+        function: FunctionValue<'a>,
         runtime: &dyn TargetRuntime,
     ) {
         match ty.deref() {
@@ -1395,7 +1443,7 @@ impl<'a> Contract<'a> {
                             .i64_type()
                             .const_int(d.to_u64().unwrap(), false),
                         slot,
-                        |index: IntValue<'b>, slot: &mut IntValue<'b>| {
+                        |index: IntValue<'a>, slot: &mut IntValue<'a>| {
                             let mut elem = unsafe {
                                 self.builder.build_gep(
                                     dest.into_pointer_value(),
@@ -1478,12 +1526,12 @@ impl<'a> Contract<'a> {
     }
 
     /// Recursively clear contract storage
-    fn storage_clear<'b>(
-        &'b self,
+    fn storage_clear(
+        &self,
         ty: &resolver::Type,
-        slot: &mut IntValue<'b>,
-        slot_ptr: PointerValue<'b>,
-        function: FunctionValue<'b>,
+        slot: &mut IntValue<'a>,
+        slot_ptr: PointerValue<'a>,
+        function: FunctionValue<'a>,
         runtime: &dyn TargetRuntime,
     ) {
         match ty.deref() {
@@ -1498,7 +1546,7 @@ impl<'a> Contract<'a> {
                             .i64_type()
                             .const_int(d.to_u64().unwrap(), false),
                         slot,
-                        |_index: IntValue<'b>, slot: &mut IntValue<'b>| {
+                        |_index: IntValue<'a>, slot: &mut IntValue<'a>| {
                             self.storage_clear(&ty, slot, slot_ptr, function, runtime);
 
                             if !ty.is_reference_type() {
@@ -1519,9 +1567,7 @@ impl<'a> Contract<'a> {
 
                     let buf = self.builder.build_alloca(slot_ty, "buf");
 
-                    runtime.get_storage(self, function, slot_ptr, buf);
-
-                    let length = self.builder.build_load(buf, "length").into_int_value();
+                    let length = runtime.get_storage_int(self, function, slot_ptr, slot_ty);
 
                     // we need to hash the length slot in order to get the slot of the first
                     // entry of the array
@@ -1560,7 +1606,7 @@ impl<'a> Contract<'a> {
                         length.get_type().const_zero(),
                         length,
                         &mut entry_slot,
-                        |_index: IntValue<'b>, slot: &mut IntValue<'b>| {
+                        |_index: IntValue<'a>, slot: &mut IntValue<'a>| {
                             self.storage_clear(&ty, slot, slot_ptr, function, runtime);
 
                             if !ty.is_reference_type() {
@@ -1983,10 +2029,10 @@ impl<'a> Contract<'a> {
     pub fn emit_function_dispatch(
         &self,
         resolver_functions: &[resolver::FunctionDecl],
-        functions: &[FunctionValue],
-        argsdata: inkwell::values::PointerValue,
-        argslen: inkwell::values::IntValue,
-        function: inkwell::values::FunctionValue,
+        functions: &[FunctionValue<'a>],
+        argsdata: inkwell::values::PointerValue<'a>,
+        argslen: inkwell::values::IntValue<'a>,
+        function: inkwell::values::FunctionValue<'a>,
         fallback_block: inkwell::basic_block::BasicBlock,
         runtime: &dyn TargetRuntime,
     ) {
@@ -2856,10 +2902,11 @@ impl resolver::Type {
 
 static STDLIB_IR: &[u8] = include_bytes!("../../stdlib/stdlib.bc");
 static SHA3_IR: &[u8] = include_bytes!("../../stdlib/sha3.bc");
+static SUBSTRATE_IR: &[u8] = include_bytes!("../../stdlib/substrate.bc");
 
 /// Return the stdlib as parsed llvm module. The solidity standard library is hardcoded into
 /// the solang library
-fn load_stdlib(context: &Context) -> Module {
+fn load_stdlib<'a>(context: &'a Context, target: &crate::Target) -> Module<'a> {
     let memory = MemoryBuffer::create_from_memory_range(STDLIB_IR, "stdlib");
 
     let module = Module::parse_bitcode_from_buffer(&memory, context).unwrap();
@@ -2870,5 +2917,13 @@ fn load_stdlib(context: &Context) -> Module {
         .link_in_module(Module::parse_bitcode_from_buffer(&memory, context).unwrap())
         .unwrap();
 
+    if let super::Target::Substrate = target {
+        let memory = MemoryBuffer::create_from_memory_range(SUBSTRATE_IR, "substrate");
+
+        module
+            .link_in_module(Module::parse_bitcode_from_buffer(&memory, context).unwrap())
+            .unwrap();
+    }
+
     module
 }

+ 29 - 39
src/emit/sabre.rs

@@ -3,6 +3,7 @@ use std::str;
 
 use inkwell::context::Context;
 use inkwell::module::Linkage;
+use inkwell::types::IntType;
 use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
 use inkwell::AddressSpace;
 use inkwell::IntPredicate;
@@ -354,13 +355,22 @@ impl TargetRuntime for SabreTarget {
         unimplemented!();
     }
 
-    fn get_storage<'a>(
+    fn get_storage_string<'a>(
         &self,
-        contract: &'a Contract,
+        _contract: &Contract<'a>,
+        _function: FunctionValue,
+        _slot: PointerValue<'a>,
+    ) -> PointerValue<'a> {
+        unimplemented!();
+    }
+
+    fn get_storage_int<'a>(
+        &self,
+        contract: &Contract<'a>,
         function: FunctionValue,
-        slot: PointerValue<'a>,
-        dest: PointerValue<'a>,
-    ) {
+        slot: PointerValue,
+        ty: IntType<'a>,
+    ) -> IntValue<'a> {
         let address = contract
             .builder
             .build_call(
@@ -420,12 +430,7 @@ impl TargetRuntime for SabreTarget {
             .unwrap()
             .into_int_value();
 
-        let data_size = dest
-            .get_type()
-            .get_element_type()
-            .into_int_type()
-            .size_of()
-            .const_cast(contract.context.i32_type(), false);
+        let data_size = ty.size_of();
 
         let exists = contract.builder.build_int_compare(
             IntPredicate::EQ,
@@ -434,9 +439,8 @@ impl TargetRuntime for SabreTarget {
             "storage_exists",
         );
 
-        let clear_block = contract
-            .context
-            .append_basic_block(function, "not_in_storage");
+        let entry = contract.builder.get_insert_block().unwrap();
+
         let retrieve_block = contract.context.append_basic_block(function, "in_storage");
         let done_storage = contract
             .context
@@ -444,38 +448,24 @@ impl TargetRuntime for SabreTarget {
 
         contract
             .builder
-            .build_conditional_branch(exists, retrieve_block, clear_block);
+            .build_conditional_branch(exists, retrieve_block, done_storage);
 
         contract.builder.position_at_end(retrieve_block);
 
-        let dest = contract.builder.build_pointer_cast(
-            dest,
-            contract.context.i8_type().ptr_type(AddressSpace::Generic),
-            "dest",
-        );
-
-        contract.builder.build_call(
-            contract.module.get_function("__memcpy").unwrap(),
-            &[dest.into(), res.into(), data_size.into()],
-            "copy_from_storage",
+        let loaded_int = contract.builder.build_load(
+            contract
+                .builder
+                .build_pointer_cast(res, ty.ptr_type(AddressSpace::Generic), ""),
+            "loaded_int",
         );
 
         contract.builder.build_unconditional_branch(done_storage);
 
-        contract.builder.position_at_end(clear_block);
+        let res = contract.builder.build_phi(ty, "storage_res");
 
-        contract.builder.build_call(
-            contract.module.get_function("__memset").unwrap(),
-            &[
-                dest.into(),
-                contract.context.i8_type().const_zero().into(),
-                data_size.into(),
-            ],
-            "clear_storage",
-        );
-        contract.builder.build_unconditional_branch(done_storage);
+        res.add_incoming(&[(&loaded_int, retrieve_block), (&ty.const_zero(), entry)]);
 
-        contract.builder.position_at_end(done_storage);
+        res.as_basic_value().into_int_value()
     }
 
     fn return_empty_abi(&self, contract: &Contract) {
@@ -499,7 +489,7 @@ impl TargetRuntime for SabreTarget {
 
     fn abi_encode<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         function: FunctionValue,
         args: &[BasicValueEnum<'b>],
         spec: &resolver::FunctionDecl,
@@ -536,7 +526,7 @@ impl TargetRuntime for SabreTarget {
 
     fn abi_decode<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         function: FunctionValue,
         args: &mut Vec<BasicValueEnum<'b>>,
         data: PointerValue<'b>,

+ 54 - 30
src/emit/substrate.rs

@@ -2,6 +2,7 @@ use resolver;
 
 use inkwell::context::Context;
 use inkwell::module::Linkage;
+use inkwell::types::IntType;
 use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
 use inkwell::AddressSpace;
 use inkwell::IntPredicate;
@@ -45,7 +46,7 @@ impl SubstrateTarget {
 
     fn public_function_prelude<'a>(
         &self,
-        contract: &'a Contract,
+        contract: &Contract<'a>,
         function: FunctionValue,
     ) -> (PointerValue<'a>, IntValue<'a>) {
         let entry = contract.context.append_basic_block(function, "entry");
@@ -270,7 +271,7 @@ impl SubstrateTarget {
     /// ABI decode a single primitive
     fn decode_primitive<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         ty: &resolver::Type,
         to: Option<PointerValue<'b>>,
         src: PointerValue<'b>,
@@ -388,7 +389,7 @@ impl SubstrateTarget {
     /// recursively encode a single ty
     fn decode_ty<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         function: FunctionValue,
         ty: &resolver::Type,
         to: Option<PointerValue<'b>>,
@@ -635,7 +636,7 @@ impl SubstrateTarget {
     /// and the pointer is updated point after the encoded data.
     pub fn encode_ty<'a>(
         &self,
-        contract: &'a Contract,
+        contract: &Contract<'a>,
         function: FunctionValue,
         ty: &resolver::Type,
         arg: BasicValueEnum<'a>,
@@ -740,7 +741,7 @@ impl SubstrateTarget {
         arg: BasicValueEnum<'a>,
         ty: &resolver::Type,
         function: FunctionValue,
-        contract: &'a Contract,
+        contract: &Contract<'a>,
     ) -> IntValue<'a> {
         match ty {
             resolver::Type::Bool => contract.context.i32_type().const_int(1, false),
@@ -1041,13 +1042,13 @@ impl TargetRuntime for SubstrateTarget {
     }
 
     /// Read from substrate storage
-    fn get_storage<'a>(
+    fn get_storage_int<'a>(
         &self,
-        contract: &'a Contract,
+        contract: &Contract<'a>,
         function: FunctionValue,
-        slot: PointerValue<'a>,
-        dest: PointerValue<'a>,
-    ) {
+        slot: PointerValue,
+        ty: IntType<'a>,
+    ) -> IntValue<'a> {
         let exists = contract
             .builder
             .build_call(
@@ -1073,9 +1074,7 @@ impl TargetRuntime for SubstrateTarget {
             "storage_exists",
         );
 
-        let clear_block = contract
-            .context
-            .append_basic_block(function, "not_in_storage");
+        let entry = contract.builder.get_insert_block().unwrap();
         let retrieve_block = contract.context.append_basic_block(function, "in_storage");
         let done_storage = contract
             .context
@@ -1083,10 +1082,12 @@ impl TargetRuntime for SubstrateTarget {
 
         contract
             .builder
-            .build_conditional_branch(exists, retrieve_block, clear_block);
+            .build_conditional_branch(exists, retrieve_block, done_storage);
 
         contract.builder.position_at_end(retrieve_block);
 
+        let dest = contract.builder.build_alloca(ty, "int");
+
         contract.builder.build_call(
             contract.module.get_function("ext_scratch_read").unwrap(),
             &[
@@ -1099,31 +1100,54 @@ impl TargetRuntime for SubstrateTarget {
                     )
                     .into(),
                 contract.context.i32_type().const_zero().into(),
-                dest.get_type()
-                    .get_element_type()
-                    .into_int_type()
-                    .size_of()
+                ty.size_of()
                     .const_cast(contract.context.i32_type(), false)
                     .into(),
             ],
             "",
         );
 
+        let loaded_int = contract.builder.build_load(dest, "int");
+
         contract.builder.build_unconditional_branch(done_storage);
 
-        contract.builder.position_at_end(clear_block);
+        contract.builder.position_at_end(done_storage);
+
+        let res = contract.builder.build_phi(ty, "storage_res");
 
-        contract.builder.build_store(
-            dest,
-            dest.get_type()
-                .get_element_type()
-                .into_int_type()
-                .const_zero(),
-        );
+        res.add_incoming(&[(&loaded_int, retrieve_block), (&ty.const_zero(), entry)]);
 
-        contract.builder.build_unconditional_branch(done_storage);
+        res.as_basic_value().into_int_value()
+    }
 
-        contract.builder.position_at_end(done_storage);
+    /// Read string from substrate storage
+    fn get_storage_string<'a>(
+        &self,
+        contract: &Contract<'a>,
+        _function: FunctionValue,
+        slot: PointerValue<'a>,
+    ) -> PointerValue<'a> {
+        contract
+            .builder
+            .build_call(
+                contract
+                    .module
+                    .get_function("substrate_get_string")
+                    .unwrap(),
+                &[contract
+                    .builder
+                    .build_pointer_cast(
+                        slot,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "",
+                    )
+                    .into()],
+                "",
+            )
+            .try_as_basic_value()
+            .left()
+            .unwrap()
+            .into_pointer_value()
     }
 
     fn return_empty_abi(&self, contract: &Contract) {
@@ -1165,7 +1189,7 @@ impl TargetRuntime for SubstrateTarget {
 
     fn abi_decode<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         function: FunctionValue,
         args: &mut Vec<BasicValueEnum<'b>>,
         data: PointerValue<'b>,
@@ -1186,7 +1210,7 @@ impl TargetRuntime for SubstrateTarget {
     ///  ABI encode the return values for the function
     fn abi_encode<'b>(
         &self,
-        contract: &'b Contract,
+        contract: &Contract<'b>,
         function: FunctionValue,
         args: &[BasicValueEnum<'b>],
         spec: &resolver::FunctionDecl,

+ 22 - 30
stdlib/stdlib.c

@@ -3,9 +3,11 @@
 #include <stddef.h>
 #include <stdbool.h>
 
+#include "stdlib.h"
+
 /*
  */
-__attribute__((visibility("hidden"))) void __memset8(void *_dest, uint64_t val, size_t length)
+void __memset8(void *_dest, uint64_t val, size_t length)
 {
 	uint64_t *dest = _dest;
 
@@ -15,7 +17,7 @@ __attribute__((visibility("hidden"))) void __memset8(void *_dest, uint64_t val,
 	} while (--length);
 }
 
-__attribute__((visibility("hidden"))) void __memset(uint8_t *dest, uint8_t val, size_t length)
+void __memset(uint8_t *dest, uint8_t val, size_t length)
 {
 	do
 	{
@@ -26,7 +28,7 @@ __attribute__((visibility("hidden"))) void __memset(uint8_t *dest, uint8_t val,
  * Our memcpy can only deal with multiples of 8 bytes. This is enough for
  * simple allocator below.
  */
-__attribute__((visibility("hidden"))) void __memcpy8(void *_dest, void *_src, size_t length)
+void __memcpy8(void *_dest, void *_src, size_t length)
 {
 	uint64_t *dest = _dest;
 	uint64_t *src = _src;
@@ -37,7 +39,7 @@ __attribute__((visibility("hidden"))) void __memcpy8(void *_dest, void *_src, si
 	} while (--length);
 }
 
-__attribute__((visibility("hidden"))) void __memcpy(uint8_t *dest, uint8_t *src, size_t length)
+void __memcpy(uint8_t *dest, uint8_t *src, size_t length)
 {
 	do
 	{
@@ -48,7 +50,7 @@ __attribute__((visibility("hidden"))) void __memcpy(uint8_t *dest, uint8_t *src,
 /*
  * Fast-ish clear, 8 bytes at a time.
  */
-__attribute__((visibility("hidden"))) void __bzero8(void *_dest, size_t length)
+void __bzero8(void *_dest, size_t length)
 {
 	uint64_t *dest = _dest;
 
@@ -60,7 +62,7 @@ __attribute__((visibility("hidden"))) void __bzero8(void *_dest, size_t length)
 /*
  * Fast-ish set, 8 bytes at a time.
  */
-__attribute__((visibility("hidden"))) void __bset8(void *_dest, size_t length)
+void __bset8(void *_dest, size_t length)
 {
 	int64_t *dest = _dest;
 
@@ -85,7 +87,7 @@ struct chunk
 	bool allocated;
 };
 
-__attribute__((visibility("hidden"))) void __init_heap()
+void __init_heap()
 {
 	struct chunk *first = (struct chunk *)0x10000;
 	first->next = first->prev = NULL;
@@ -94,7 +96,7 @@ __attribute__((visibility("hidden"))) void __init_heap()
 							 (size_t)first - sizeof(struct chunk));
 }
 
-__attribute__((visibility("hidden"))) void __attribute__((noinline)) __free(void *m)
+void __attribute__((noinline)) __free(void *m)
 {
 	struct chunk *cur = m;
 	cur--;
@@ -122,7 +124,7 @@ __attribute__((visibility("hidden"))) void __attribute__((noinline)) __free(void
 	}
 }
 
-__attribute__((visibility("hidden"))) static void shrink_chunk(struct chunk *cur, size_t size)
+static void shrink_chunk(struct chunk *cur, size_t size)
 {
 	// round up to nearest 8 bytes
 	size = (size + 7) & ~7;
@@ -142,7 +144,7 @@ __attribute__((visibility("hidden"))) static void shrink_chunk(struct chunk *cur
 	}
 }
 
-__attribute__((visibility("hidden"))) void *__attribute__((noinline)) __malloc(size_t size)
+void *__attribute__((noinline)) __malloc(size_t size)
 {
 	struct chunk *cur = (struct chunk *)0x10000;
 
@@ -162,7 +164,7 @@ __attribute__((visibility("hidden"))) void *__attribute__((noinline)) __malloc(s
 	}
 }
 
-__attribute__((visibility("hidden"))) void *__realloc(void *m, size_t size)
+void *__realloc(void *m, size_t size)
 {
 	struct chunk *cur = m;
 
@@ -193,7 +195,7 @@ __attribute__((visibility("hidden"))) void *__realloc(void *m, size_t size)
 // ABI encoding is big endian, and can have integers of 8 to 256 bits
 // (1 to 32 bytes). This function copies length bytes and reverses the
 // order since wasm is little endian.
-__attribute__((visibility("hidden"))) void __be32toleN(uint8_t *from, uint8_t *to, uint32_t length)
+void __be32toleN(uint8_t *from, uint8_t *to, uint32_t length)
 {
 	from += 31;
 
@@ -203,7 +205,7 @@ __attribute__((visibility("hidden"))) void __be32toleN(uint8_t *from, uint8_t *t
 	} while (--length);
 }
 
-__attribute__((visibility("hidden"))) void __beNtoleN(uint8_t *from, uint8_t *to, uint32_t length)
+void __beNtoleN(uint8_t *from, uint8_t *to, uint32_t length)
 {
 	from += length;
 
@@ -215,7 +217,7 @@ __attribute__((visibility("hidden"))) void __beNtoleN(uint8_t *from, uint8_t *to
 
 // This function is for used for abi encoding integers
 // ABI encoding is big endian.
-__attribute__((visibility("hidden"))) void __leNtobe32(uint8_t *from, uint8_t *to, uint32_t length)
+void __leNtobe32(uint8_t *from, uint8_t *to, uint32_t length)
 {
 	to += 31;
 
@@ -225,7 +227,7 @@ __attribute__((visibility("hidden"))) void __leNtobe32(uint8_t *from, uint8_t *t
 	} while (--length);
 }
 
-__attribute__((visibility("hidden"))) void __leNtobeN(uint8_t *from, uint8_t *to, uint32_t length)
+void __leNtobeN(uint8_t *from, uint8_t *to, uint32_t length)
 {
 	to += length;
 
@@ -252,7 +254,7 @@ __attribute__((visibility("hidden"))) void __leNtobeN(uint8_t *from, uint8_t *to
 	r5*l4	r4*l4	r3*l4	r2*l4 	r1*l4	0		0 		0  +
     ------------------------------------------------------------
 */
-__attribute__((visibility("hidden"))) void __mul32(uint32_t left[], uint32_t right[], uint32_t out[], int len)
+void __mul32(uint32_t left[], uint32_t right[], uint32_t out[], int len)
 {
 	uint64_t val1 = 0, carry = 0;
 
@@ -383,18 +385,8 @@ char *__u256ptohex(uint8_t *v, char *str)
 	return str;
 }
 
-/*
- * Vector is used for dynamic array
- */
-struct vector
-{
-	uint32_t len;
-	uint32_t size;
-	uint8_t data[];
-};
-
 // Create a new vector. If initial is -1 then clear the data. This is done since a null pointer valid in wasm
-__attribute__((visibility("hidden"))) struct vector *vector_new(uint32_t members, uint32_t size, uint8_t *initial)
+struct vector *vector_new(uint32_t members, uint32_t size, uint8_t *initial)
 {
 	struct vector *v;
 	size_t size_array = members * size;
@@ -423,7 +415,7 @@ __attribute__((visibility("hidden"))) struct vector *vector_new(uint32_t members
 	return v;
 }
 
-__attribute__((visibility("hidden"))) bool memcmp(uint8_t *left, uint32_t left_len, uint8_t *right, uint32_t right_len)
+bool memcmp(uint8_t *left, uint32_t left_len, uint8_t *right, uint32_t right_len)
 {
 	if (left_len != right_len)
 		return false;
@@ -437,7 +429,7 @@ __attribute__((visibility("hidden"))) bool memcmp(uint8_t *left, uint32_t left_l
 	return true;
 }
 
-__attribute__((visibility("hidden"))) struct vector *concat(uint8_t *left, uint32_t left_len, uint8_t *right, uint32_t right_len)
+struct vector *concat(uint8_t *left, uint32_t left_len, uint8_t *right, uint32_t right_len)
 {
 	size_t size_array = left_len + right_len;
 	struct vector *v = __malloc(sizeof(*v) + size_array);
@@ -541,4 +533,4 @@ struct vector *scale_decode_string(uint8_t **from)
 	*from = src;
 
 	return v;
-}
+}

+ 11 - 0
stdlib/stdlib.h

@@ -0,0 +1,11 @@
+/*
+ * Vector is used for dynamic array
+ */
+struct vector
+{
+    uint32_t len;
+    uint32_t size;
+    uint8_t data[];
+};
+
+void *__malloc(size_t size);

二進制
stdlib/substrate.bc


+ 31 - 0
stdlib/substrate.c

@@ -0,0 +1,31 @@
+#include <stdint.h>
+#include <stddef.h>
+
+#include "stdlib.h"
+
+extern uint32_t ext_get_storage(uint8_t *);
+extern uint32_t ext_scratch_size(void);
+extern void ext_scratch_read(uint8_t *dest, uint32_t offset, uint32_t size);
+
+struct vector *substrate_get_string(uint8_t *slot)
+{
+    struct vector *v;
+
+    if (ext_get_storage(slot))
+    {
+        v = __malloc(sizeof(*v) + 0);
+        v->size = 0;
+        v->len = 0;
+    }
+    else
+    {
+        uint32_t size = ext_scratch_size();
+        v = __malloc(sizeof(*v) + size);
+        v->size = size;
+        v->len = size;
+
+        ext_scratch_read(v->data, 0, size);
+    }
+
+    return v;
+}

+ 14 - 2
tests/substrate_strings/mod.rs

@@ -345,18 +345,30 @@ fn string_abi_decode() {
 
 #[test]
 fn string_storage() {
+    #[derive(Debug, PartialEq, Encode, Decode)]
+    struct Val(String);
+
     let (runtime, mut store) = build_solidity(
         r##"
         contract foo {
             string bar;
 
-            function test() public {
+            function set_bar() public {
                 bar = "foobar";
             }
+
+            function get_bar() public returns (string) {
+                return bar;
+            }
+
         }"##,
     );
 
-    runtime.function(&mut store, "test", Vec::new());
+    runtime.function(&mut store, "set_bar", Vec::new());
 
     assert_eq!(store.store.get(&[0u8; 32]).unwrap(), b"foobar");
+
+    runtime.function(&mut store, "get_bar", Vec::new());
+
+    assert_eq!(store.scratch, Val("foobar".to_string()).encode());
 }