Procházet zdrojové kódy

Encode arguments for constructor call in codegen (#1038)

Lucas Steuernagel před 3 roky
rodič
revize
d0f0881c5b

+ 11 - 14
src/codegen/cfg.rs

@@ -113,8 +113,8 @@ pub enum Instr {
         success: Option<usize>,
         res: usize,
         contract_no: usize,
-        constructor_no: Option<usize>,
-        args: Vec<Expression>,
+        encoded_args: Expression,
+        encoded_args_len: Expression,
         value: Option<Expression>,
         gas: Expression,
         salt: Option<Expression>,
@@ -262,16 +262,16 @@ impl Instr {
             }
 
             Instr::Constructor {
-                args,
+                encoded_args,
+                encoded_args_len,
                 value,
                 gas,
                 salt,
                 space,
                 ..
             } => {
-                for arg in args {
-                    arg.recurse(cx, f);
-                }
+                encoded_args.recurse(cx, f);
+                encoded_args_len.recurse(cx, f);
                 if let Some(expr) = value {
                     expr.recurse(cx, f);
                 }
@@ -1138,14 +1138,14 @@ impl ControlFlowGraph {
                 success,
                 res,
                 contract_no,
-                constructor_no,
-                args,
+                encoded_args,
+                encoded_args_len,
                 gas,
                 salt,
                 value,
                 space,
             } => format!(
-                "%{}, {} = constructor salt:{} value:{} gas:{} space:{} {} #{:?} ({})",
+                "%{}, {} = constructor salt:{} value:{} gas:{} space:{} {} (encoded buffer: {}, buffer len: {})",
                 self.vars[res].id.name,
                 match success {
                     Some(i) => format!("%{}", self.vars[i].id.name),
@@ -1165,11 +1165,8 @@ impl ControlFlowGraph {
                     None => "".to_string(),
                 },
                 ns.contracts[*contract_no].name,
-                constructor_no,
-                args.iter()
-                    .map(|expr| self.expr_to_string(contract, ns, expr))
-                    .collect::<Vec<String>>()
-                    .join(", ")
+                self.expr_to_string(contract, ns, encoded_args),
+                self.expr_to_string(contract, ns, encoded_args_len)
             ),
             Instr::Unreachable => "unreachable".to_string(),
             Instr::SelfDestruct { recipient } => format!(

+ 6 - 8
src/codegen/constant_folding.rs

@@ -186,17 +186,15 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
                     success,
                     res,
                     contract_no,
-                    constructor_no,
-                    args,
+                    encoded_args,
+                    encoded_args_len,
                     value,
                     gas,
                     salt,
                     space,
                 } => {
-                    let args = args
-                        .iter()
-                        .map(|e| expression(e, Some(&vars), cfg, ns).0)
-                        .collect();
+                    let encoded_args = expression(encoded_args, Some(&vars), cfg, ns).0;
+                    let encoded_args_len = expression(encoded_args_len, Some(&vars), cfg, ns).0;
                     let value = value
                         .as_ref()
                         .map(|expr| expression(expr, Some(&vars), cfg, ns).0);
@@ -212,8 +210,8 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
                         success: *success,
                         res: *res,
                         contract_no: *contract_no,
-                        constructor_no: *constructor_no,
-                        args,
+                        encoded_args,
+                        encoded_args_len,
                         value,
                         gas,
                         salt,

+ 135 - 0
src/codegen/constructor.rs

@@ -0,0 +1,135 @@
+use crate::codegen::cfg::{ControlFlowGraph, Instr};
+use crate::codegen::expression::{default_gas, expression};
+use crate::codegen::vartable::Vartable;
+use crate::codegen::{Builtin, Expression, Options};
+use crate::sema::ast;
+use crate::sema::ast::{CallArgs, Function, Namespace, RetrieveType, Type};
+use crate::Target;
+use num_bigint::{BigInt, Sign};
+use num_traits::Zero;
+use solang_parser::pt::Loc;
+
+/// This function encodes the constructor arguments and place an instruction in the CFG to
+/// call the constructor of a contract.
+pub(super) fn call_constructor(
+    loc: &Loc,
+    contract_no: &usize,
+    callee_contract_no: usize,
+    constructor_no: &Option<usize>,
+    constructor_args: &[ast::Expression],
+    call_args: &CallArgs,
+    address_res: usize,
+    success: Option<usize>,
+    func: Option<&Function>,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+    opt: &Options,
+) {
+    let value = call_args
+        .value
+        .as_ref()
+        .map(|v| expression(v, cfg, callee_contract_no, func, ns, vartab, opt));
+
+    let gas = if let Some(gas) = &call_args.gas {
+        expression(gas, cfg, callee_contract_no, func, ns, vartab, opt)
+    } else {
+        default_gas(ns)
+    };
+
+    let salt = call_args
+        .salt
+        .as_ref()
+        .map(|e| expression(e, cfg, callee_contract_no, func, ns, vartab, opt));
+    let space = call_args
+        .space
+        .as_ref()
+        .map(|e| expression(e, cfg, callee_contract_no, func, ns, vartab, opt));
+
+    let mut tys: Vec<Type> = Vec::new();
+    let mut packed: Vec<Expression> = Vec::new();
+    let mut args: Vec<Expression> = Vec::new();
+    if ns.target == Target::Solana {
+        tys.resize(3, Type::Bool);
+        tys[0] = Type::Uint(64);
+        tys[1] = Type::Uint(32);
+        tys[2] = Type::Bytes(1);
+
+        let value_arg = value.clone().unwrap_or_else(|| {
+            Expression::NumberLiteral(Loc::Codegen, Type::Uint(64), BigInt::zero())
+        });
+        let selector = ns.contracts[*contract_no].selector().to_be();
+        let padding = Expression::NumberLiteral(*loc, Type::Bytes(1), BigInt::zero());
+
+        packed.resize(3, Expression::Poison);
+        packed[0] = value_arg;
+        packed[1] = Expression::NumberLiteral(*loc, Type::Uint(32), BigInt::from(selector));
+        packed[2] = padding;
+    } else {
+        let selector = match constructor_no {
+            Some(func_no) => ns.functions[*func_no].selector(),
+            None => ns.contracts[*contract_no]
+                .default_constructor
+                .as_ref()
+                .unwrap()
+                .0
+                .selector(),
+        };
+
+        args.push(Expression::NumberLiteral(
+            *loc,
+            Type::Uint(32),
+            BigInt::from_bytes_le(Sign::Plus, &selector),
+        ));
+        tys.push(Type::Uint(32));
+    }
+
+    let mut constructor_args = constructor_args
+        .iter()
+        .map(|e| expression(e, cfg, callee_contract_no, func, ns, vartab, opt))
+        .collect::<Vec<Expression>>();
+    let mut arg_types = constructor_args
+        .iter()
+        .map(|e| e.ty())
+        .collect::<Vec<Type>>();
+    args.append(&mut constructor_args);
+    tys.append(&mut arg_types);
+
+    let encoded_buffer = vartab.temp_anonymous(&Type::DynamicBytes);
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: *loc,
+            res: encoded_buffer,
+            expr: Expression::AbiEncode {
+                loc: *loc,
+                tys,
+                packed,
+                args,
+            },
+        },
+    );
+
+    let encoded_args = Expression::Variable(*loc, Type::DynamicBytes, encoded_buffer);
+    let encoded_args_len = Expression::Builtin(
+        *loc,
+        vec![Type::Uint(32)],
+        Builtin::ArrayLength,
+        vec![encoded_args.clone()],
+    );
+
+    cfg.add(
+        vartab,
+        Instr::Constructor {
+            success,
+            res: address_res,
+            contract_no: *contract_no,
+            encoded_args,
+            encoded_args_len,
+            value,
+            gas,
+            salt,
+            space,
+        },
+    );
+}

+ 14 - 35
src/codegen/expression.rs

@@ -9,6 +9,7 @@ use super::{
     vartable::Vartable,
 };
 use crate::codegen::array_boundary::handle_array_assign;
+use crate::codegen::constructor::call_constructor;
 use crate::codegen::encoding::create_encoder;
 use crate::codegen::encoding::AbiEncoding;
 use crate::codegen::unused_variable::should_remove_assignment;
@@ -301,43 +302,21 @@ pub fn expression(
         } => {
             let address_res = vartab.temp_anonymous(&Type::Contract(*contract_no));
 
-            let args = args
-                .iter()
-                .map(|v| expression(v, cfg, *contract_no, func, ns, vartab, opt))
-                .collect();
-            let gas = if let Some(gas) = &call_args.gas {
-                expression(gas, cfg, *contract_no, func, ns, vartab, opt)
-            } else {
-                default_gas(ns)
-            };
-            let value = call_args
-                .value
-                .as_ref()
-                .map(|value| expression(value, cfg, *contract_no, func, ns, vartab, opt));
-            let salt = call_args
-                .salt
-                .as_ref()
-                .map(|salt| expression(salt, cfg, *contract_no, func, ns, vartab, opt));
-            let space = call_args
-                .space
-                .as_ref()
-                .map(|space| expression(space, cfg, *contract_no, func, ns, vartab, opt));
-
-            cfg.add(
+            call_constructor(
+                loc,
+                contract_no,
+                *contract_no,
+                constructor_no,
+                args,
+                call_args,
+                address_res,
+                None,
+                func,
+                ns,
                 vartab,
-                Instr::Constructor {
-                    success: None,
-                    res: address_res,
-                    contract_no: *contract_no,
-                    constructor_no: *constructor_no,
-                    args,
-                    value,
-                    gas,
-                    salt,
-                    space,
-                },
+                cfg,
+                opt,
             );
-
             Expression::Variable(*loc, Type::Contract(*contract_no), address_res)
         }
         ast::Expression::InternalFunction {

+ 1 - 0
src/codegen/mod.rs

@@ -3,6 +3,7 @@
 mod array_boundary;
 pub mod cfg;
 mod constant_folding;
+mod constructor;
 mod dead_storage;
 mod dispatch;
 mod encoding;

+ 15 - 34
src/codegen/statements.rs

@@ -9,6 +9,7 @@ use super::{
     cfg::{ControlFlowGraph, Instr},
     vartable::Vartable,
 };
+use crate::codegen::constructor::call_constructor;
 use crate::codegen::events::new_event_emitter;
 use crate::codegen::unused_variable::{
     should_remove_assignment, should_remove_variable, SideEffectsCheckParameters,
@@ -1116,6 +1117,7 @@ fn try_catch(
             }
         }
         ast::Expression::Constructor {
+            loc,
             contract_no,
             constructor_no,
             args,
@@ -1127,41 +1129,20 @@ fn try_catch(
                 _ => vartab.temp_anonymous(&Type::Contract(*contract_no)),
             };
 
-            let value = call_args.value.as_ref().map(|value| {
-                expression(value, cfg, callee_contract_no, Some(func), ns, vartab, opt)
-            });
-
-            let gas = if let Some(gas) = &call_args.gas {
-                expression(gas, cfg, callee_contract_no, Some(func), ns, vartab, opt)
-            } else {
-                default_gas(ns)
-            };
-            let salt = call_args
-                .salt
-                .as_ref()
-                .map(|salt| expression(salt, cfg, callee_contract_no, Some(func), ns, vartab, opt));
-            let space = call_args.space.as_ref().map(|space| {
-                expression(space, cfg, callee_contract_no, Some(func), ns, vartab, opt)
-            });
-
-            let args = args
-                .iter()
-                .map(|a| expression(a, cfg, callee_contract_no, Some(func), ns, vartab, opt))
-                .collect();
-
-            cfg.add(
+            call_constructor(
+                loc,
+                contract_no,
+                callee_contract_no,
+                constructor_no,
+                args,
+                call_args,
+                address_res,
+                Some(success),
+                Some(func),
+                ns,
                 vartab,
-                Instr::Constructor {
-                    success: Some(success),
-                    res: address_res,
-                    contract_no: *contract_no,
-                    constructor_no: *constructor_no,
-                    args,
-                    value,
-                    gas,
-                    salt,
-                    space,
-                },
+                cfg,
+                opt,
             );
 
             cfg.add(

+ 4 - 5
src/codegen/strength_reduce/mod.rs

@@ -158,16 +158,15 @@ fn block_reduce(
                 *value = Box::new(expression_reduce(value, &vars, ns));
             }
             Instr::Constructor {
-                args,
+                encoded_args,
+                encoded_args_len,
                 value,
                 gas,
                 salt,
                 ..
             } => {
-                *args = args
-                    .iter()
-                    .map(|e| expression_reduce(e, &vars, ns))
-                    .collect();
+                *encoded_args = expression_reduce(encoded_args, &vars, ns);
+                *encoded_args_len = expression_reduce(encoded_args_len, &vars, ns);
                 if let Some(value) = value {
                     *value = expression_reduce(value, &vars, ns);
                 }

+ 8 - 11
src/codegen/subexpression_elimination/instruction.rs

@@ -86,16 +86,16 @@ impl AvailableExpressionSet {
             }
 
             Instr::Constructor {
-                args,
+                encoded_args,
+                encoded_args_len,
                 value,
                 gas,
                 salt,
                 space,
                 ..
             } => {
-                for arg in args {
-                    let _ = self.gen_expression(arg, ave, cst);
-                }
+                let _ = self.gen_expression(encoded_args, ave, cst);
+                let _ = self.gen_expression(encoded_args_len, ave, cst);
                 if let Some(expr) = value {
                     let _ = self.gen_expression(expr, ave, cst);
                 }
@@ -310,8 +310,8 @@ impl AvailableExpressionSet {
                 success,
                 res,
                 contract_no,
-                constructor_no,
-                args,
+                encoded_args,
+                encoded_args_len,
                 value,
                 gas,
                 salt,
@@ -333,11 +333,8 @@ impl AvailableExpressionSet {
                     success: *success,
                     res: *res,
                     contract_no: *contract_no,
-                    constructor_no: *constructor_no,
-                    args: args
-                        .iter()
-                        .map(|v| self.regenerate_expression(v, ave, cst).1)
-                        .collect::<Vec<Expression>>(),
+                    encoded_args: self.regenerate_expression(encoded_args, ave, cst).1,
+                    encoded_args_len: self.regenerate_expression(encoded_args_len, ave, cst).1,
                     value: new_value,
                     gas: self.regenerate_expression(gas, ave, cst).1,
                     salt: new_salt,

+ 3 - 2
src/codegen/subexpression_elimination/tests.rs

@@ -8,6 +8,7 @@ use crate::codegen::subexpression_elimination::{AvailableExpression, AvailableEx
 use crate::codegen::Expression;
 use crate::sema::ast::{StringLocation, Type};
 use num_bigint::{BigInt, Sign};
+use num_traits::Zero;
 use solang_parser::pt::Loc;
 
 #[test]
@@ -334,8 +335,8 @@ fn string() {
         success: None,
         res: 0,
         contract_no: 0,
-        constructor_no: None,
-        args: vec![concat.clone()],
+        encoded_args: concat.clone(),
+        encoded_args_len: Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), BigInt::zero()),
         value: Some(compare.clone()),
         gas: concat2.clone(),
         salt: Some(compare2.clone()),

+ 6 - 8
src/emit/instructions.rs

@@ -675,17 +675,15 @@ pub(super) fn process_instruction<'a, T: TargetRuntime<'a> + ?Sized>(
             success,
             res,
             contract_no,
-            constructor_no,
-            args,
+            encoded_args,
+            encoded_args_len,
             value,
             gas,
             salt,
             space,
         } => {
-            let args = &args
-                .iter()
-                .map(|a| expression(target, bin, a, &w.vars, function, ns))
-                .collect::<Vec<BasicValueEnum>>();
+            let encoded_args = expression(target, bin, encoded_args, &w.vars, function, ns);
+            let encoded_args_len = expression(target, bin, encoded_args_len, &w.vars, function, ns);
 
             let address = bin.build_alloca(function, bin.address_type(ns), "address");
 
@@ -710,13 +708,13 @@ pub(super) fn process_instruction<'a, T: TargetRuntime<'a> + ?Sized>(
                 function,
                 success,
                 *contract_no,
-                *constructor_no,
                 bin.builder.build_pointer_cast(
                     address,
                     bin.context.i8_type().ptr_type(AddressSpace::Generic),
                     "address",
                 ),
-                args,
+                encoded_args,
+                encoded_args_len,
                 gas,
                 value,
                 salt,

+ 2 - 2
src/emit/mod.rs

@@ -260,9 +260,9 @@ pub trait TargetRuntime<'a> {
         function: FunctionValue<'b>,
         success: Option<&mut BasicValueEnum<'b>>,
         contract_no: usize,
-        constructor_no: Option<usize>,
         address: PointerValue<'b>,
-        args: &[BasicValueEnum<'b>],
+        encoded_args: BasicValueEnum<'b>,
+        encoded_args_len: BasicValueEnum<'b>,
         gas: IntValue<'b>,
         value: Option<IntValue<'b>>,
         salt: Option<IntValue<'b>>,

+ 15 - 42
src/emit/solana/target.rs

@@ -1282,63 +1282,31 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         function: FunctionValue<'b>,
         success: Option<&mut BasicValueEnum<'b>>,
         contract_no: usize,
-        constructor_no: Option<usize>,
         address: PointerValue<'b>,
-        args: &[BasicValueEnum<'b>],
+        encoded_args: BasicValueEnum<'b>,
+        encoded_args_len: BasicValueEnum<'b>,
         _gas: IntValue<'b>,
-        value: Option<IntValue<'b>>,
+        _value: Option<IntValue<'b>>,
         _salt: Option<IntValue<'b>>,
         space: Option<IntValue<'b>>,
         ns: &ast::Namespace,
     ) {
-        // abi encode the arguments. The
-        let mut tys = vec![
-            ast::Type::Uint(64),
-            ast::Type::Bytes(4),
-            ast::Type::Bytes(1),
-        ];
-
-        if let Some(function_no) = constructor_no {
-            for param in &*ns.functions[function_no].params {
-                tys.push(param.ty.clone());
-            }
-        };
-
-        let value = if let Some(value) = value {
-            value
-        } else {
-            binary.context.i64_type().const_zero()
-        };
-
-        let packed = [
-            value.into(),
-            binary
-                .context
-                .i32_type()
-                .const_int(ns.contracts[contract_no].selector().to_be() as u64, false)
-                .into(),
-            binary.context.i8_type().const_zero().into(),
-        ];
-
-        let encoder = ethabiencoder::EncoderBuilder::new(
-            binary, function, false, &packed, args, &tys, true, ns,
-        );
-
-        let length = encoder.encoded_length();
         let address_length = binary
             .context
             .i32_type()
             .const_int(ns.address_length as u64 * 2, false);
 
-        let malloc_length = binary
-            .builder
-            .build_int_add(length, address_length, "malloc_length");
+        let malloc_length = binary.builder.build_int_add(
+            encoded_args_len.into_int_value(),
+            address_length,
+            "malloc_length",
+        );
 
         // The format of the payload is:
         // 32 bytes recv (will be filled in by create_contract C function)
         // 32 bytes sender (will be filled in by create_contract C function)
         // 4 bytes contract selector/magic
-        // remainder: eth abi encoded constructor arguments
+        // remainder: abi encoded constructor arguments
         let payload = binary
             .builder
             .build_call(
@@ -1353,7 +1321,12 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
 
         let enc = unsafe { binary.builder.build_gep(payload, &[address_length], "enc") };
 
-        encoder.finish(binary, function, enc, ns);
+        let buffer_bytes = binary.vector_bytes(encoded_args);
+        binary.builder.build_call(
+            binary.module.get_function("__memcpy").unwrap(),
+            &[enc.into(), buffer_bytes.into(), encoded_args_len.into()],
+            "",
+        );
 
         let space = binary.builder.build_int_add(
             binary.context.i64_type().const_int(

+ 5 - 23
src/emit/substrate/target.rs

@@ -1012,9 +1012,9 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
         function: FunctionValue<'b>,
         success: Option<&mut BasicValueEnum<'b>>,
         contract_no: usize,
-        constructor_no: Option<usize>,
         address: PointerValue<'b>,
-        args: &[BasicValueEnum<'b>],
+        encoded_args: BasicValueEnum<'b>,
+        encoded_args_len: BasicValueEnum<'b>,
         gas: IntValue<'b>,
         value: Option<IntValue<'b>>,
         salt: Option<IntValue<'b>>,
@@ -1025,11 +1025,6 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
 
         let created_contract = &ns.contracts[contract_no];
 
-        let constructor = match constructor_no {
-            Some(function_no) => &ns.functions[function_no],
-            None => &created_contract.default_constructor.as_ref().unwrap().0,
-        };
-
         let (scratch_buf, scratch_len) = scratch_buf!();
 
         // salt
@@ -1065,20 +1060,7 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
             );
         }
 
-        let tys: Vec<ast::Type> = constructor.params.iter().map(|p| p.ty.clone()).collect();
-
-        // input
-        let (input, input_len) = self.abi_encode(
-            binary,
-            Some(i32_const!(
-                u32::from_le_bytes(constructor.selector().try_into().unwrap()) as u64
-            )),
-            false,
-            function,
-            args,
-            &tys,
-            ns,
-        );
+        let encoded_args = binary.vector_bytes(encoded_args);
 
         let value_ptr = binary
             .builder
@@ -1128,8 +1110,8 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
                 codehash.into(),
                 gas.into(),
                 cast_byte_ptr!(value_ptr, "value_transfer").into(),
-                input.into(),
-                input_len.into(),
+                encoded_args.into(),
+                encoded_args_len.into(),
                 address.into(),
                 address_len_ptr.into(),
                 scratch_buf.into(),

+ 5 - 5
tests/codegen_testcases/solidity/unused_variable_elimination.sol

@@ -76,13 +76,13 @@ contract c3 {
         c2 ct = new c2();
 
         return 3;
-// CHECK: constructor salt: value: gas:uint64 0 space: c2 #None ()
+// CHECK: constructor salt: value: gas:uint64 0 space: c2 (encoded buffer: %temp.78, buffer len: (builtin ArrayLength (%temp.78)))
     }
 
 // BEGIN-CHECK: c3::function::test7
     function test7() public returns (int32) {
         c2 ct = new c2();
-// CHECK: constructor salt: value: gas:uint64 0 space: c2 #None ()
+// CHECK: constructor salt: value: gas:uint64 0 space: c2 (encoded buffer: %temp.80, buffer len: (builtin ArrayLength (%temp.80)))
         address ad = address(ct);
         (bool p, ) = ad.call(hex'ba');
 // CHECK: external call::regular address:%ad payload:(alloc bytes uint32 1 hex"ba") value:uint128 0 gas:uint64 0
@@ -135,7 +135,7 @@ return 3;
         int f = 4;
 
         int c = 32 +4 *(f = it1+it2);
-// CHECK: ty:int256 %c = (int256 32 + (sext int256 (int64 4 * (trunc int64 (%temp.84 + %temp.85)))))
+// CHECK: ty:int256 %c = (int256 32 + (sext int256 (int64 4 * (trunc int64 (%temp.87 + %temp.88)))))
 // NOT-CHECK: ty:int256 %f = (%temp.10 + %temp.11)
         return c;
     }
@@ -164,7 +164,7 @@ return 3;
     function test13() public returns (int){
 
         int[] memory vec = new int[](5);
-        // CHECK: alloc int256[] len %array_length.temp.92
+        // CHECK: alloc int256[] len %array_length.temp.97
         vec[0] = 3;
 
         return vec[1];
@@ -176,7 +176,7 @@ return 3;
     function test14() public returns (int) {
         int[] storage ptrArr = testArr;
 
-// CHECK: store storage slot(%temp.98) ty:int256 storage = int256 3
+// CHECK: store storage slot(%temp.103) ty:int256 storage = int256 3
         ptrArr.push(3);
 
         return ptrArr[0];