Преглед изворни кода

solana: implement returning values from contract

For Solana, the return data should be passed in contract data.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young пре 5 година
родитељ
комит
c621c5b77a
10 измењених фајлова са 389 додато и 220 уклоњено
  1. 53 13
      src/emit/ethabiencoder.rs
  2. 2 28
      src/emit/ewasm.rs
  3. 3 29
      src/emit/generic.rs
  4. 31 8
      src/emit/mod.rs
  5. 3 29
      src/emit/sabre.rs
  6. 193 100
      src/emit/solana.rs
  7. BIN
      stdlib/solana.bc
  8. 16 4
      stdlib/solana.c
  9. 4 4
      stdlib/solana_sdk.h
  10. 84 5
      tests/solana.rs

+ 53 - 13
src/emit/ethabiencoder.rs

@@ -154,10 +154,10 @@ impl EthAbiEncoder {
 
                     let fixed_elems_length = contract.builder.build_int_add(
                         len,
-                        contract
-                            .context
-                            .i32_type()
-                            .const_int(self.encoded_fixed_length(&elem_ty, contract.ns), false),
+                        contract.context.i32_type().const_int(
+                            EthAbiEncoder::encoded_fixed_length(&elem_ty, contract.ns),
+                            false,
+                        ),
                         "",
                     );
 
@@ -681,7 +681,6 @@ impl EthAbiEncoder {
 
     /// Return the amount of fixed and dynamic storage required to store a type
     pub fn encoded_dynamic_length<'a>(
-        &self,
         arg: BasicValueEnum<'a>,
         load: bool,
         ty: &ast::Type,
@@ -710,7 +709,7 @@ impl EthAbiEncoder {
                         )
                     };
 
-                    let len = self.encoded_dynamic_length(
+                    let len = EthAbiEncoder::encoded_dynamic_length(
                         elem.into(),
                         true,
                         &field.ty,
@@ -764,7 +763,7 @@ impl EthAbiEncoder {
                             contract.builder.build_int_mul(
                                 array_len,
                                 contract.context.i32_type().const_int(
-                                    self.encoded_fixed_length(&elem_ty, contract.ns),
+                                    EthAbiEncoder::encoded_fixed_length(&elem_ty, contract.ns),
                                     false,
                                 ),
                                 "",
@@ -819,7 +818,7 @@ impl EthAbiEncoder {
                             );
 
                             *sum = contract.builder.build_int_add(
-                                self.encoded_dynamic_length(
+                                EthAbiEncoder::encoded_dynamic_length(
                                     elem.into(),
                                     true,
                                     &elem_ty,
@@ -874,7 +873,7 @@ impl EthAbiEncoder {
     }
 
     /// Return the encoded length of the given type, fixed part only
-    pub fn encoded_fixed_length(&self, ty: &ast::Type, ns: &ast::Namespace) -> u64 {
+    pub fn encoded_fixed_length(ty: &ast::Type, ns: &ast::Namespace) -> u64 {
         match ty {
             ast::Type::Bool
             | ast::Type::Contract(_)
@@ -889,7 +888,7 @@ impl EthAbiEncoder {
             ast::Type::Struct(n) => ns.structs[*n]
                 .fields
                 .iter()
-                .map(|f| self.encoded_fixed_length(&f.ty, ns))
+                .map(|f| EthAbiEncoder::encoded_fixed_length(&f.ty, ns))
                 .sum(),
             ast::Type::Array(ty, dims) => {
                 let mut product = 1;
@@ -903,10 +902,10 @@ impl EthAbiEncoder {
                     }
                 }
 
-                product * self.encoded_fixed_length(&ty, ns)
+                product * EthAbiEncoder::encoded_fixed_length(&ty, ns)
             }
-            ast::Type::Ref(r) => self.encoded_fixed_length(r, ns),
-            ast::Type::StorageRef(r) => self.encoded_fixed_length(r, ns),
+            ast::Type::Ref(r) => EthAbiEncoder::encoded_fixed_length(r, ns),
+            ast::Type::StorageRef(r) => EthAbiEncoder::encoded_fixed_length(r, ns),
             _ => unreachable!(),
         }
     }
@@ -1485,4 +1484,45 @@ impl EthAbiEncoder {
             ));
         }
     }
+
+    /// Calculate length of encoded data and the offset where the dynamic part starts
+    pub fn total_encoded_length<'b>(
+        contract: &Contract<'b>,
+        selector: Option<IntValue<'b>>,
+        load: bool,
+        function: FunctionValue,
+        args: &[BasicValueEnum<'b>],
+        spec: &[ast::Parameter],
+    ) -> (IntValue<'b>, IntValue<'b>) {
+        let offset = contract.context.i32_type().const_int(
+            spec.iter()
+                .map(|arg| EthAbiEncoder::encoded_fixed_length(&arg.ty, contract.ns))
+                .sum(),
+            false,
+        );
+
+        let mut length = offset;
+
+        // now add the dynamic lengths
+        for (i, s) in spec.iter().enumerate() {
+            length = contract.builder.build_int_add(
+                length,
+                EthAbiEncoder::encoded_dynamic_length(args[i], load, &s.ty, function, contract),
+                "",
+            );
+        }
+
+        if selector.is_some() {
+            length = contract.builder.build_int_add(
+                length,
+                contract
+                    .context
+                    .i32_type()
+                    .const_int(std::mem::size_of::<u32>() as u64, false),
+                "",
+            );
+        }
+
+        (length, offset)
+    }
 }

+ 2 - 28
src/emit/ewasm.rs

@@ -676,36 +676,10 @@ impl EwasmTarget {
         args: &[BasicValueEnum<'b>],
         spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>) {
-        let mut offset = contract.context.i32_type().const_int(
-            spec.iter()
-                .map(|arg| self.abi.encoded_fixed_length(&arg.ty, contract.ns))
-                .sum(),
-            false,
+        let (mut length, mut offset) = ethabiencoder::EthAbiEncoder::total_encoded_length(
+            contract, selector, load, function, args, spec,
         );
 
-        let mut length = offset;
-
-        // now add the dynamic lengths
-        for (i, s) in spec.iter().enumerate() {
-            length = contract.builder.build_int_add(
-                length,
-                self.abi
-                    .encoded_dynamic_length(args[i], load, &s.ty, function, contract),
-                "",
-            );
-        }
-
-        if selector.is_some() {
-            length = contract.builder.build_int_add(
-                length,
-                contract
-                    .context
-                    .i32_type()
-                    .const_int(std::mem::size_of::<u32>() as u64, false),
-                "",
-            );
-        }
-
         if let Some((_, len)) = constant {
             length = contract.builder.build_int_add(
                 length,

+ 3 - 29
src/emit/generic.rs

@@ -544,36 +544,10 @@ impl<'a> TargetRuntime<'a> for GenericTarget {
         args: &[BasicValueEnum<'b>],
         spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>) {
-        let mut offset = contract.context.i32_type().const_int(
-            spec.iter()
-                .map(|arg| self.abi.encoded_fixed_length(&arg.ty, contract.ns))
-                .sum(),
-            false,
+        let (length, mut offset) = ethabiencoder::EthAbiEncoder::total_encoded_length(
+            contract, selector, load, function, args, spec,
         );
 
-        let mut length = offset;
-
-        // now add the dynamic lengths
-        for (i, s) in spec.iter().enumerate() {
-            length = contract.builder.build_int_add(
-                length,
-                self.abi
-                    .encoded_dynamic_length(args[i], load, &s.ty, function, contract),
-                "",
-            );
-        }
-
-        if selector.is_some() {
-            length = contract.builder.build_int_add(
-                length,
-                contract
-                    .context
-                    .i32_type()
-                    .const_int(std::mem::size_of::<u32>() as u64, false),
-                "",
-            );
-        }
-
         let encoded_data = contract
             .builder
             .build_call(
@@ -605,7 +579,7 @@ impl<'a> TargetRuntime<'a> for GenericTarget {
                     &[contract
                         .context
                         .i32_type()
-                        .const_int(std::mem::size_of_val(&selector) as u64, false)],
+                        .const_int(std::mem::size_of::<u32>() as u64, false)],
                     "",
                 )
             };

+ 31 - 8
src/emit/mod.rs

@@ -63,15 +63,15 @@ pub trait TargetRuntime<'a> {
 
     /// Abi encode with optional four bytes selector. The load parameter should be set if the args are
     /// pointers to data, not the actual data  itself.
-    fn abi_encode<'b>(
+    fn abi_encode(
         &self,
-        contract: &Contract<'b>,
-        selector: Option<IntValue<'b>>,
+        contract: &Contract<'a>,
+        selector: Option<IntValue<'a>>,
         load: bool,
         function: FunctionValue,
-        args: &[BasicValueEnum<'b>],
+        args: &[BasicValueEnum<'a>],
         spec: &[ast::Parameter],
-    ) -> (PointerValue<'b>, IntValue<'b>);
+    ) -> (PointerValue<'a>, IntValue<'a>);
 
     /// ABI encode into a vector for abi.encode* style builtin functions
     fn abi_encode_to_vector<'b>(
@@ -4198,13 +4198,12 @@ pub trait TargetRuntime<'a> {
             for v in f.returns.iter() {
                 args.push(if !v.ty.is_reference_type() {
                     contract
-                        .builder
-                        .build_alloca(contract.llvm_type(&v.ty), &v.name)
+                        .build_alloca(function, contract.llvm_type(&v.ty), &v.name)
                         .into()
                 } else {
                     contract
-                        .builder
                         .build_alloca(
+                            function,
                             contract.llvm_type(&v.ty).ptr_type(AddressSpace::Generic),
                             &v.name,
                         )
@@ -5074,6 +5073,30 @@ impl<'a> Contract<'a> {
         )
     }
 
+    /// Wrapper for alloca. Ensures that the alloca is done on the first basic block.
+    /// If alloca is not on the first basic block, llvm will get to llvm_unreachable
+    /// for the BPF target.
+    fn build_alloca<T: BasicType<'a>>(
+        &self,
+        function: inkwell::values::FunctionValue<'a>,
+        ty: T,
+        name: &str,
+    ) -> PointerValue<'a> {
+        let entry = function
+            .get_first_basic_block()
+            .expect("function missing entry block");
+        let current = self.builder.get_insert_block().unwrap();
+
+        self.builder
+            .position_before(&entry.get_first_instruction().unwrap());
+
+        let res = self.builder.build_alloca(ty, name);
+
+        self.builder.position_at_end(current);
+
+        res
+    }
+
     /// 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<F>(

+ 3 - 29
src/emit/sabre.rs

@@ -609,36 +609,10 @@ impl<'a> TargetRuntime<'a> for SabreTarget {
         args: &[BasicValueEnum<'b>],
         spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>) {
-        let mut offset = contract.context.i32_type().const_int(
-            spec.iter()
-                .map(|arg| self.abi.encoded_fixed_length(&arg.ty, contract.ns))
-                .sum(),
-            false,
+        let (length, mut offset) = ethabiencoder::EthAbiEncoder::total_encoded_length(
+            contract, selector, load, function, args, spec,
         );
 
-        let mut length = offset;
-
-        // now add the dynamic lengths
-        for (i, s) in spec.iter().enumerate() {
-            length = contract.builder.build_int_add(
-                length,
-                self.abi
-                    .encoded_dynamic_length(args[i], load, &s.ty, function, contract),
-                "",
-            );
-        }
-
-        if selector.is_some() {
-            length = contract.builder.build_int_add(
-                length,
-                contract
-                    .context
-                    .i32_type()
-                    .const_int(std::mem::size_of::<u32>() as u64, false),
-                "",
-            );
-        }
-
         let encoded_data = contract
             .builder
             .build_call(
@@ -670,7 +644,7 @@ impl<'a> TargetRuntime<'a> for SabreTarget {
                     &[contract
                         .context
                         .i32_type()
-                        .const_int(std::mem::size_of_val(&selector) as u64, false)],
+                        .const_int(std::mem::size_of::<u32>() as u64, false)],
                     "",
                 )
             };

+ 193 - 100
src/emit/solana.rs

@@ -14,11 +14,12 @@ use inkwell::OptimizationLevel;
 use super::ethabiencoder;
 use super::{Contract, TargetRuntime, Variable};
 
-pub struct SolanaTarget {
-    abi: ethabiencoder::EthAbiEncoder,
+pub struct SolanaTarget<'a> {
+    output: PointerValue<'a>,
+    output_len: PointerValue<'a>,
 }
 
-impl SolanaTarget {
+impl<'s> SolanaTarget<'s> {
     pub fn build<'a>(
         context: &'a Context,
         contract: &'a ast::Contract,
@@ -26,8 +27,14 @@ impl SolanaTarget {
         filename: &'a str,
         opt: OptimizationLevel,
     ) -> Contract<'a> {
+        let undef = context
+            .i8_type()
+            .ptr_type(AddressSpace::Generic)
+            .get_undef();
+
         let mut b = SolanaTarget {
-            abi: ethabiencoder::EthAbiEncoder {},
+            output: undef,
+            output_len: undef,
         };
 
         let mut c = Contract::new(context, contract, ns, filename, opt, None);
@@ -58,6 +65,7 @@ impl SolanaTarget {
         function
             .as_global_value()
             .set_unnamed_address(UnnamedAddress::Local);
+
         let function = contract.module.add_function(
             "sol_log_",
             void_ty.fn_type(&[u8_ptr.into(), u64_ty.into()], false),
@@ -68,7 +76,7 @@ impl SolanaTarget {
             .set_unnamed_address(UnnamedAddress::Local);
     }
 
-    fn emit_constructor(&mut self, contract: &mut Contract) {
+    fn emit_constructor(&mut self, contract: &mut Contract<'s>) {
         let initializer = self.emit_initializer(contract);
 
         let function = contract.module.get_function("solang_constructor").unwrap();
@@ -79,6 +87,8 @@ impl SolanaTarget {
 
         let argsdata = function.get_nth_param(0).unwrap().into_pointer_value();
         let argslen = function.get_nth_param(1).unwrap().into_int_value();
+        self.output = function.get_nth_param(2).unwrap().into_pointer_value();
+        self.output_len = function.get_nth_param(3).unwrap().into_pointer_value();
 
         // init our storage vars
         contract.builder.build_call(initializer, &[], "");
@@ -118,27 +128,29 @@ impl SolanaTarget {
     }
 
     // emit function dispatch
-    fn emit_function<'s>(&'s mut self, contract: &'s mut Contract) {
+    fn emit_function(&mut self, contract: &mut Contract<'s>) {
         let function = contract.module.get_function("solang_function").unwrap();
 
         let entry = contract.context.append_basic_block(function, "entry");
 
         contract.builder.position_at_end(entry);
 
-        let argsdata = function.get_nth_param(0).unwrap().into_pointer_value();
-        let argslen = function.get_nth_param(1).unwrap().into_int_value();
+        let input = function.get_nth_param(0).unwrap().into_pointer_value();
+        let input_len = function.get_nth_param(1).unwrap().into_int_value();
+        self.output = function.get_nth_param(2).unwrap().into_pointer_value();
+        self.output_len = function.get_nth_param(3).unwrap().into_pointer_value();
 
-        let argsdata = contract.builder.build_pointer_cast(
-            argsdata,
+        let input = contract.builder.build_pointer_cast(
+            input,
             contract.context.i32_type().ptr_type(AddressSpace::Generic),
-            "argsdata32",
+            "input_ptr32",
         );
 
         self.emit_function_dispatch(
             contract,
             pt::FunctionTy::Function,
-            argsdata,
-            argslen,
+            input,
+            input_len,
             function,
             None,
             |_| false,
@@ -343,15 +355,122 @@ impl SolanaTarget {
 
                 val.into()
             }
-            ast::Type::Bytes(1) => {
-                let val = contract.builder.build_load(data, "bytes1");
+            _ => unreachable!(),
+        }
+    }
 
-                if let Some(p) = to {
-                    contract.builder.build_store(p, val);
-                }
-                val
+    /// ABI encode a single primitive
+    fn encode_primitive(
+        &self,
+        contract: &Contract,
+        load: bool,
+        ty: &ast::Type,
+        dest: PointerValue,
+        arg: BasicValueEnum,
+    ) {
+        match ty {
+            ast::Type::Bool => {
+                let arg = if load {
+                    contract.builder.build_load(arg.into_pointer_value(), "")
+                } else {
+                    arg
+                };
+
+                let value = contract.builder.build_select(
+                    arg.into_int_value(),
+                    contract.context.i8_type().const_int(1, false),
+                    contract.context.i8_type().const_zero(),
+                    "bool_val",
+                );
+
+                let dest8 = contract.builder.build_pointer_cast(
+                    dest,
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                    "destvoid",
+                );
+
+                let dest = unsafe {
+                    contract.builder.build_gep(
+                        dest8,
+                        &[contract.context.i32_type().const_int(31, false)],
+                        "",
+                    )
+                };
+
+                contract.builder.build_store(dest, value);
             }
-            _ => unreachable!(),
+            ast::Type::Uint(8) | ast::Type::Int(8) | ast::Type::Bytes(1) => {
+                let arg = if load {
+                    contract.builder.build_load(arg.into_pointer_value(), "")
+                } else {
+                    arg
+                };
+
+                let dest8 = contract.builder.build_pointer_cast(
+                    dest,
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                    "destvoid",
+                );
+
+                let dest = unsafe {
+                    contract.builder.build_gep(
+                        dest8,
+                        &[contract.context.i32_type().const_int(31, false)],
+                        "",
+                    )
+                };
+
+                contract.builder.build_store(dest, arg.into_int_value());
+            }
+            ast::Type::Uint(n) | ast::Type::Int(n) if *n == 16 || *n == 32 || *n == 64 => {
+                let arg = if load {
+                    contract.builder.build_load(arg.into_pointer_value(), "")
+                } else {
+                    arg
+                };
+
+                // now convert to be
+                let bswap = contract.llvm_bswap(*n as u32);
+
+                let val = contract
+                    .builder
+                    .build_call(bswap, &[arg], "")
+                    .try_as_basic_value()
+                    .left()
+                    .unwrap()
+                    .into_int_value();
+
+                let dest8 = contract.builder.build_pointer_cast(
+                    dest,
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                    "dest8",
+                );
+
+                // our value is big endian, 32 bytes. So, find the offset within the 32 bytes
+                // where our value starts
+                let int8_ptr = unsafe {
+                    contract.builder.build_gep(
+                        dest8,
+                        &[contract
+                            .context
+                            .i32_type()
+                            .const_int(32 - (*n as u64 / 8), false)],
+                        "uint_ptr",
+                    )
+                };
+
+                let int_type = contract.context.custom_width_int_type(*n as u32);
+
+                contract.builder.build_store(
+                    contract.builder.build_pointer_cast(
+                        int8_ptr,
+                        int_type.ptr_type(AddressSpace::Generic),
+                        "",
+                    ),
+                    val,
+                );
+            }
+            _ => unimplemented!(),
         }
     }
 
@@ -383,7 +502,7 @@ impl SolanaTarget {
     }
 }
 
-impl<'a> TargetRuntime<'a> for SolanaTarget {
+impl<'a> TargetRuntime<'a> for SolanaTarget<'a> {
     fn clear_storage(&self, _contract: &Contract, _function: FunctionValue, _slot: PointerValue) {
         unimplemented!();
     }
@@ -524,14 +643,23 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
     }
 
     fn return_empty_abi(&self, contract: &Contract) {
+        contract
+            .builder
+            .build_store(self.output_len, contract.context.i64_type().const_zero());
+
         // return 0 for success
         contract
             .builder
             .build_return(Some(&contract.context.i32_type().const_int(0, false)));
     }
 
-    fn return_abi<'b>(&self, _contract: &'b Contract, _data: PointerValue<'b>, _length: IntValue) {
-        unimplemented!();
+    fn return_abi<'b>(&self, contract: &'b Contract, _data: PointerValue<'b>, _length: IntValue) {
+        // return data already filled in output contract
+
+        // return 0 for success
+        contract
+            .builder
+            .build_return(Some(&contract.context.i32_type().const_int(0, false)));
     }
 
     fn assert_failure<'b>(&self, _contract: &'b Contract, _data: PointerValue, _length: IntValue) {
@@ -551,77 +679,45 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         unimplemented!();
     }
 
-    fn abi_encode<'b>(
+    fn abi_encode(
         &self,
-        contract: &Contract<'b>,
-        selector: Option<IntValue<'b>>,
+        contract: &Contract<'a>,
+        selector: Option<IntValue<'a>>,
         load: bool,
         function: FunctionValue,
-        args: &[BasicValueEnum<'b>],
+        args: &[BasicValueEnum<'a>],
         spec: &[ast::Parameter],
-    ) -> (PointerValue<'b>, IntValue<'b>) {
-        let mut offset = contract.context.i32_type().const_int(
-            spec.iter()
-                .map(|arg| self.abi.encoded_fixed_length(&arg.ty, contract.ns))
-                .sum(),
-            false,
+    ) -> (PointerValue<'a>, IntValue<'a>) {
+        let (length, _offset) = ethabiencoder::EthAbiEncoder::total_encoded_length(
+            contract, selector, load, function, args, spec,
         );
 
-        let mut length = offset;
-
-        // now add the dynamic lengths
-        for (i, s) in spec.iter().enumerate() {
-            length = contract.builder.build_int_add(
-                length,
-                self.abi
-                    .encoded_dynamic_length(args[i], load, &s.ty, function, contract),
-                "",
-            );
-        }
+        let length64 =
+            contract
+                .builder
+                .build_int_z_extend(length, contract.context.i64_type(), "length64");
 
-        if selector.is_some() {
-            length = contract.builder.build_int_add(
-                length,
-                contract
-                    .context
-                    .i32_type()
-                    .const_int(std::mem::size_of::<u32>() as u64, false),
-                "",
-            );
-        }
+        contract.builder.build_store(self.output_len, length64);
 
-        let encoded_data = contract
-            .builder
-            .build_call(
-                contract.module.get_function("solang_malloc").unwrap(),
-                &[length.into()],
-                "",
-            )
-            .try_as_basic_value()
-            .left()
-            .unwrap()
-            .into_pointer_value();
-
-        // malloc returns u8*
-        let mut data = encoded_data;
+        let mut output = self.output;
 
         if let Some(selector) = selector {
             contract.builder.build_store(
                 contract.builder.build_pointer_cast(
-                    data,
+                    output,
                     contract.context.i32_type().ptr_type(AddressSpace::Generic),
                     "",
                 ),
                 selector,
             );
 
-            data = unsafe {
+            output = unsafe {
                 contract.builder.build_gep(
-                    data,
+                    output,
                     &[contract
                         .context
                         .i32_type()
-                        .const_int(std::mem::size_of_val(&selector) as u64, false)],
+                        .const_int(std::mem::size_of::<u32>() as u64, false)],
                     "",
                 )
             };
@@ -630,38 +726,35 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         // We use a little trick here. The length might or might not include the selector.
         // The length will be a multiple of 32 plus the selector (4). So by dividing by 8,
         // we lose the selector.
-        // contract.builder.build_call(
-        //     contract.module.get_function("__bzero8").unwrap(),
-        //     &[
-        //         data.into(),
-        //         contract
-        //             .builder
-        //             .build_int_unsigned_div(
-        //                 length,
-        //                 contract.context.i32_type().const_int(8, false),
-        //                 "",
-        //             )
-        //             .into(),
-        //     ],
-        //     "",
-        // );
-
-        let mut dynamic = unsafe { contract.builder.build_gep(data, &[offset], "") };
+        contract.builder.build_call(
+            contract.module.get_function("__bzero8").unwrap(),
+            &[
+                output.into(),
+                contract
+                    .builder
+                    .build_int_unsigned_div(
+                        length,
+                        contract.context.i32_type().const_int(8, false),
+                        "",
+                    )
+                    .into(),
+            ],
+            "",
+        );
 
         for (i, arg) in spec.iter().enumerate() {
-            self.abi.encode_ty(
-                contract,
-                load,
-                function,
-                &arg.ty,
-                args[i],
-                &mut data,
-                &mut offset,
-                &mut dynamic,
-            );
+            self.encode_primitive(contract, load, &arg.ty, output, args[i]);
+
+            output = unsafe {
+                contract.builder.build_gep(
+                    output,
+                    &[contract.context.i32_type().const_int(32, false)],
+                    "",
+                )
+            };
         }
 
-        (encoded_data, length)
+        (output, length)
     }
 
     fn abi_decode<'b>(


+ 16 - 4
stdlib/solana.c

@@ -4,8 +4,8 @@
 
 #include "solana_sdk.h"
 
-extern int solang_constructor(const uint8_t *input, uint64_t input_len);
-extern int solang_function(const uint8_t *input, uint64_t input_len);
+extern int solang_constructor(const uint8_t *input, uint64_t input_len, uint8_t *ouyput, uint64_t *output_len);
+extern int solang_function(const uint8_t *input, uint64_t input_len, uint8_t *ouyput, uint64_t *output_len);
 
 uint64_t
 entrypoint(const uint8_t *input)
@@ -19,11 +19,11 @@ entrypoint(const uint8_t *input)
 
     if ((params.data_len % 32) == 0)
     {
-        return solang_constructor(params.data, params.data_len);
+        return solang_constructor(params.data, params.data_len, ka[0].data, ka[0].data_len);
     }
     else
     {
-        return solang_function(params.data, params.data_len);
+        return solang_function(params.data, params.data_len, ka[0].data, ka[0].data_len);
     }
 
     return SUCCESS;
@@ -44,6 +44,18 @@ void *__malloc(uint32_t size)
     return sol_alloc_free_(size, NULL);
 }
 
+/*
+ * Fast-ish clear, 8 bytes at a time.
+ */
+void __bzero8(void *_dest, uint32_t length)
+{
+    uint64_t *dest = _dest;
+
+    do
+        *dest++ = 0;
+    while (--length);
+}
+
 // Create a new vector. If initial is -1 then clear the data. This is done since a null pointer valid in wasm
 struct vector *vector_new(uint32_t members, uint32_t size, uint8_t *initial)
 {

+ 4 - 4
stdlib/solana_sdk.h

@@ -104,7 +104,7 @@ typedef struct
 {
   SolPubkey *key;      /** Public key of the account */
   uint64_t *lamports;  /** Number of lamports owned by this account */
-  uint64_t data_len;   /** Length of data in bytes */
+  uint64_t *data_len;  /** Length of data in bytes */
   uint8_t *data;       /** On-chain data within this account */
   SolPubkey *owner;    /** Program that owns this account */
   uint64_t rent_epoch; /** The epoch at which this account will next owe rent */
@@ -310,10 +310,10 @@ static bool sol_deserialize(
       input += sizeof(uint64_t);
 
       // account data
-      params->ka[i].data_len = *(uint64_t *)input;
+      params->ka[i].data_len = (uint64_t *)input;
       input += sizeof(uint64_t);
       params->ka[i].data = (uint8_t *)input;
-      input += params->ka[i].data_len;
+      input += *params->ka[i].data_len;
       input += MAX_PERMITTED_DATA_INCREASE;
       input = (uint8_t *)(((uint64_t)input + 8 - 1) & ~(8 - 1)); // padding
 
@@ -535,7 +535,7 @@ static void sol_log_params(const SolParameters *params)
     sol_log("  - Lamports");
     sol_log_64(0, 0, 0, 0, *params->ka[i].lamports);
     sol_log("  - data");
-    sol_log_array(params->ka[i].data, params->ka[i].data_len);
+    sol_log_array(params->ka[i].data, *params->ka[i].data_len);
     sol_log("  - Owner");
     sol_log_pubkey(params->ka[i].owner);
     sol_log("  - Executable");

+ 84 - 5
tests/solana.rs

@@ -8,7 +8,7 @@ extern crate solang;
 
 mod solana_helpers;
 
-use byteorder::{LittleEndian, WriteBytesExt};
+use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
 use ethabi::Token;
 use libc::c_char;
 use solana_helpers::allocator_bump::BPFAllocator;
@@ -21,7 +21,7 @@ use solana_rbpf::{
 use solang::{compile, file_cache::FileCache, sema::diagnostics, Target};
 use std::alloc::Layout;
 use std::io::Write;
-use std::mem::align_of;
+use std::mem::{align_of, size_of};
 
 fn build_solidity(src: &'static str) -> VM {
     let mut cache = FileCache::new();
@@ -50,6 +50,7 @@ fn build_solidity(src: &'static str) -> VM {
         code,
         abi: ethabi::Contract::load(abi.as_bytes()).unwrap(),
         printbuf: String::new(),
+        output: Vec::new(),
     }
 }
 
@@ -101,10 +102,34 @@ fn serialize_parameters(input: &[u8]) -> Vec<u8> {
     v
 }
 
+// We want to extract the account data
+fn deserialize_parameters(input: &[u8]) -> Vec<Vec<u8>> {
+    let mut start = 0;
+
+    let ka_num = LittleEndian::read_u64(&input[start..]);
+    start += size_of::<u64>();
+
+    let mut res = Vec::new();
+
+    for _ in 0..ka_num {
+        start += 8 + 32 + 32 + 8;
+
+        let data_len = LittleEndian::read_u64(&input[start..]);
+        start += size_of::<u64>();
+
+        res.push(input[start..start + data_len as usize].to_vec());
+
+        // FIXME this is broken for >1 account
+    }
+
+    res
+}
+
 struct VM {
     code: Vec<u8>,
     abi: ethabi::Contract,
     printbuf: String,
+    output: Vec<u8>,
 }
 
 struct Printer<'a> {
@@ -185,7 +210,7 @@ impl SyscallObject<UserError> for SyscallAllocFree {
 }
 
 impl VM {
-    fn execute(&self, buf: &mut String, calldata: &[u8]) {
+    fn execute(&mut self, buf: &mut String, calldata: &[u8]) {
         println!("running bpf with calldata:{}", hex::encode(calldata));
 
         let executable =
@@ -211,6 +236,12 @@ impl VM {
             .execute_program(&parameter_bytes, &[], &[heap_region])
             .unwrap();
 
+        let mut account_data = deserialize_parameters(&parameter_bytes);
+
+        self.output = account_data.remove(0);
+
+        println!("account: {}", hex::encode(&self.output));
+
         assert_eq!(res, 0);
     }
 
@@ -226,7 +257,7 @@ impl VM {
         self.printbuf = buf;
     }
 
-    fn function(&mut self, name: &str, args: &[Token]) {
+    fn function(&mut self, name: &str, args: &[Token]) -> Vec<Token> {
         let calldata = match self.abi.functions[name][0].encode_input(args) {
             Ok(n) => n,
             Err(x) => panic!(format!("{}", x)),
@@ -235,6 +266,10 @@ impl VM {
         let mut buf = String::new();
         self.execute(&mut buf, &calldata);
         self.printbuf = buf;
+
+        self.abi.functions[name][0]
+            .decode_output(&self.output)
+            .unwrap()
     }
 }
 
@@ -265,7 +300,7 @@ fn simple() {
 }
 
 #[test]
-fn basic() {
+fn parameters() {
     let mut vm = build_solidity(
         r#"
         contract foo {
@@ -300,3 +335,47 @@ fn basic() {
 
     assert_eq!(vm.printbuf, "y is 102");
 }
+
+#[test]
+fn returns() {
+    let mut vm = build_solidity(
+        r#"
+        contract foo {
+            function test(uint32 x) public returns (uint32) {
+                return x * x;
+            }
+        }"#,
+    );
+
+    let returns = vm.function(
+        "test",
+        &[ethabi::Token::Uint(ethereum_types::U256::from(10))],
+    );
+
+    assert_eq!(
+        returns,
+        vec![ethabi::Token::Uint(ethereum_types::U256::from(100))]
+    );
+
+    let mut vm = build_solidity(
+        r#"
+        contract foo {
+            function test(uint64 x) public returns (bool, uint64) {
+                return (true, x * 961748941);
+            }
+        }"#,
+    );
+
+    let returns = vm.function(
+        "test",
+        &[ethabi::Token::Uint(ethereum_types::U256::from(982451653))],
+    );
+
+    assert_eq!(
+        returns,
+        vec![
+            ethabi::Token::Bool(true),
+            ethabi::Token::Uint(ethereum_types::U256::from(961748941u64 * 982451653u64))
+        ]
+    );
+}