ソースを参照

Add casts for yul pointer to integer conversion and vice versa (#1134)

Sean Young 2 年 前
コミット
32c225b6f1

+ 34 - 0
src/codegen/mod.rs

@@ -912,6 +912,40 @@ impl Expression {
                 Expression::Cast(self.loc(), to.clone(), Box::new(self.clone()))
             }
 
+            _ if !from.is_contract_storage()
+                && !to.is_contract_storage()
+                && from.is_reference_type(ns)
+                && !to.is_reference_type(ns) =>
+            {
+                let expr = Expression::Cast(
+                    self.loc(),
+                    Type::Uint(ns.target.ptr_size()),
+                    self.clone().into(),
+                );
+
+                expr.cast(to, ns)
+            }
+
+            _ if !from.is_contract_storage()
+                && !to.is_contract_storage()
+                && !from.is_reference_type(ns)
+                && to.is_reference_type(ns) =>
+            {
+                // cast non-pointer to pointer
+                let ptr_ty = Type::Uint(ns.target.ptr_size());
+
+                Expression::Cast(self.loc(), to.clone(), self.cast(&ptr_ty, ns).into())
+            }
+
+            _ if !from.is_contract_storage()
+                && !to.is_contract_storage()
+                && !from.is_reference_type(ns)
+                && !to.is_reference_type(ns) =>
+            {
+                // cast pointer to different pointer
+                Expression::Cast(self.loc(), to.clone(), self.clone().into())
+            }
+
             _ => self.clone(),
         }
     }

+ 12 - 14
src/codegen/statements.rs

@@ -828,7 +828,7 @@ fn returns(
         .returns
         .iter()
         .zip(uncast_values.into_iter())
-        .map(|(left, right)| cast_and_try_load(&right.loc(), &right, &left.ty, ns, cfg, vartab))
+        .map(|(left, right)| try_load_and_cast(&right.loc(), &right, &left.ty, ns, cfg, vartab))
         .collect();
 
     cfg.add(vartab, Instr::Return { value: cast_values });
@@ -919,7 +919,7 @@ fn destructure(
                 // nothing to do
             }
             DestructureField::VariableDecl(res, param) => {
-                let expr = cast_and_try_load(&param.loc, &right, &param.ty, ns, cfg, vartab);
+                let expr = try_load_and_cast(&param.loc, &right, &param.ty, ns, cfg, vartab);
 
                 if should_remove_variable(res, func, opt) {
                     continue;
@@ -935,7 +935,7 @@ fn destructure(
                 );
             }
             DestructureField::Expression(left) => {
-                let expr = cast_and_try_load(&left.loc(), &right, &left.ty(), ns, cfg, vartab);
+                let expr = try_load_and_cast(&left.loc(), &right, &left.ty(), ns, cfg, vartab);
 
                 if should_remove_assignment(ns, left, func, opt) {
                     continue;
@@ -950,7 +950,7 @@ fn destructure(
 /// During a destructure statement, sema only checks if the cast is possible. During codegen, we
 /// perform the real cast and add an instruction to the CFG to load a value from the storage if want it.
 /// The existing codegen cast function does not manage the CFG, so the loads must be done here.
-fn cast_and_try_load(
+fn try_load_and_cast(
     loc: &pt::Loc,
     expr: &Expression,
     to_ty: &Type,
@@ -958,19 +958,17 @@ fn cast_and_try_load(
     cfg: &mut ControlFlowGraph,
     vartab: &mut Vartable,
 ) -> Expression {
-    let casted_expr = expr.cast(to_ty, ns);
-
-    match casted_expr.ty() {
+    match expr.ty() {
         Type::StorageRef(_, ty) => {
-            if let Expression::Subscript(_, _, ty, ..) = &casted_expr {
+            if let Expression::Subscript(_, _, ty, ..) = &expr {
                 if ty.is_storage_bytes() {
-                    return casted_expr;
+                    return expr.cast(to_ty, ns);
                 }
             }
 
             if matches!(to_ty, Type::StorageRef(..)) {
                 // If we want a storage reference, there is no need to load from storage
-                return casted_expr;
+                return expr.cast(to_ty, ns);
             }
 
             let anonymous_no = vartab.temp_anonymous(&ty);
@@ -979,17 +977,17 @@ fn cast_and_try_load(
                 Instr::LoadStorage {
                     res: anonymous_no,
                     ty: (*ty).clone(),
-                    storage: casted_expr,
+                    storage: expr.cast(to_ty, ns),
                 },
             );
 
             Expression::Variable(*loc, (*ty).clone(), anonymous_no)
         }
         Type::Ref(ty) => match *ty {
-            Type::Array(_, _) => casted_expr,
-            _ => Expression::Load(pt::Loc::Builtin, *ty, casted_expr.into()),
+            Type::Array(_, _) => expr.cast(to_ty, ns),
+            _ => Expression::Load(pt::Loc::Builtin, *ty, expr.clone().into()).cast(to_ty, ns),
         },
-        _ => casted_expr,
+        _ => expr.cast(to_ty, ns),
     }
 }
 

+ 26 - 0
src/codegen/yul/builtin.rs

@@ -221,6 +221,10 @@ fn process_arithmetic(
 ) -> Expression {
     let left = expression(&args[0], contract_no, ns, vartab, cfg, opt);
     let right = expression(&args[1], contract_no, ns, vartab, cfg, opt);
+
+    let left = cast_to_number(left, ns);
+    let right = cast_to_number(right, ns);
+
     let (left, right) = equalize_types(left, right, ns);
 
     match builtin_ty {
@@ -310,6 +314,28 @@ fn process_arithmetic(
     }
 }
 
+/// Arithmetic operations work on numbers, so addresses and pointers need to be
+/// converted to integers
+fn cast_to_number(expr: Expression, ns: &Namespace) -> Expression {
+    let ty = expr.ty();
+
+    if !ty.is_contract_storage() && ty.is_reference_type(ns) {
+        Expression::Cast(
+            pt::Loc::Codegen,
+            Type::Uint(ns.target.ptr_size()),
+            expr.into(),
+        )
+    } else if ty.is_address() {
+        Expression::Cast(
+            pt::Loc::Codegen,
+            Type::Uint((ns.address_length * 8) as u16),
+            expr.into(),
+        )
+    } else {
+        expr
+    }
+}
+
 /// This function matches the type between the right and left hand sides of operations
 fn equalize_types(
     mut left: Expression,

+ 12 - 1
src/emit/expression.rs

@@ -1889,7 +1889,10 @@ fn runtime_cast<'a>(
                 "bool_to_int_cast",
             )
             .into()
-    } else if from.is_reference_type(ns) && matches!(to, Type::Uint(_)) {
+    } else if !from.is_contract_storage()
+        && from.is_reference_type(ns)
+        && matches!(to, Type::Uint(_))
+    {
         bin.builder
             .build_ptr_to_int(
                 val.into_pointer_value(),
@@ -1897,6 +1900,14 @@ fn runtime_cast<'a>(
                 "ptr_to_int",
             )
             .into()
+    } else if to.is_reference_type(ns) && matches!(from, Type::Uint(_)) {
+        bin.builder
+            .build_int_to_ptr(
+                val.into_int_value(),
+                bin.llvm_type(to, ns).ptr_type(AddressSpace::default()),
+                "int_to_ptr",
+            )
+            .into()
     } else if matches!((from, to), (Type::DynamicBytes, Type::Slice(_))) {
         let slice = bin.build_alloca(function, bin.llvm_type(to, ns), "slice");
 

+ 9 - 9
tests/codegen_testcases/yul/expression.sol

@@ -2,7 +2,7 @@
 
 uint128 constant global_cte = 5;
 contract testing {
-    
+
 // BEGIN-CHECK: testing::testing::function::boolLiteral
     function boolLiteral() public pure {
         assembly {
@@ -96,25 +96,25 @@ contract testing {
             // CHECK: ty:uint256 %k = uint256 1
             let k := a
 
-            // CHECK: ty:uint256 %l = %vec
+            // CHECK: ty:uint256 %l = (zext uint256 uint64(%vec))
             let l := vec
 
-            // CHECK: ty:uint256 %m = %mem_vec
+            // CHECK: ty:uint256 %m = (zext uint256 uint64(%mem_vec))
             let m := mem_vec
 
-            // CHECK: ty:uint256 %n = %cte_vec
+            // CHECK: ty:uint256 %n = (zext uint256 uint64(%cte_vec))
             let n := cte_vec
 
-            // CHECK: ty:uint256 %o = %mem_cte_vec
+            // CHECK: ty:uint256 %o = (zext uint256 uint64(%mem_cte_vec))
             let o := mem_cte_vec
 
-            // CHECK: ty:uint256 %p = %b
+            // CHECK: ty:uint256 %p = (zext uint256 uint64(%b))
             let p := b
 
-            // CHECK: ty:uint256 %r = %struct_test
+            // CHECK: ty:uint256 %r = (zext uint256 uint64(%struct_test))
             let r := struct_test
 
-            // CHECK: ty:uint256 %s = %mem_struct_test
+            // CHECK: ty:uint256 %s = (zext uint256 uint64(%mem_struct_test))
             let s := mem_struct_test
         }
     }
@@ -150,7 +150,7 @@ contract testing {
             // CHECK: ty:uint256 %r = (zext uint256 bytes8((load (struct %fPtr field 0))))
             let r := fPtr.selector
 
-            // CHECK: ty:uint256 %s = (arg #1)
+            // CHECK: ty:uint256 %s = (zext uint256 uint64((arg #1)))
             let s := vl_2
         }
     }

+ 92 - 0
tests/contract_testcases/substrate/yul/ptr_to_int.dot

@@ -0,0 +1,92 @@
+strict digraph "tests/contract_testcases/substrate/yul/ptr_to_int.sol" {
+	contract [label="contract c\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:2:1-43:2"]
+	tags [label="notice: Tests various"]
+	add_pointer [label="function add_pointer\ncontract: c\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:3:5-6:31\nsignature add_pointer(bytes)\nvisibility public\nmutability pure"]
+	parameters [label="parameters\nbytes _returnData"]
+	returns [label="returns\nbytes "]
+	inline_assembly [label="inline assembly\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:8:9-10:10"]
+	yul_assignment [label="yul assignment\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:9:13-51"]
+	solidity_variable [label="solidity variable: _returnData\nbytes\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:9:13-24"]
+	yul_builtin_call [label="yul builtin call 'add'\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:9:29-51"]
+	solidity_variable_10 [label="solidity variable: _returnData\nbytes\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:9:33-44"]
+	yul_number_literal [label="uint256 literal: 4\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:9:46-50"]
+	inline_assembly_12 [label="inline assembly\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:11:2-13:3"]
+	yul_assignment_13 [label="yul assignment\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:12:6-22"]
+	solidity_variable_14 [label="solidity variable: _returnData\nbytes\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:12:6-17"]
+	yul_number_literal_15 [label="uint256 literal: 1\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:12:21-22"]
+	return [label="return\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:14:9-27"]
+	variable [label="variable: _returnData\nbytes\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:14:16-27"]
+	uint32_as_ptr [label="function uint32_as_ptr\ncontract: c\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:17:5-20:31\nsignature uint32_as_ptr(bytes)\nvisibility public\nmutability pure"]
+	parameters_19 [label="parameters\nbytes _returnData"]
+	returns_20 [label="returns\nbytes "]
+	var_decl [label="variable decl uint32 p\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:22:2-10"]
+	inline_assembly_22 [label="inline assembly\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:23:9-26:10"]
+	yul_assignment_23 [label="yul assignment\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:24:13-41"]
+	solidity_variable_24 [label="solidity variable: p\nuint32\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:24:13-14"]
+	yul_builtin_call_25 [label="yul builtin call 'add'\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:24:19-41"]
+	solidity_variable_26 [label="solidity variable: _returnData\nbytes\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:24:23-34"]
+	yul_number_literal_27 [label="uint256 literal: 4\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:24:36-40"]
+	yul_assignment_28 [label="yul assignment\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:25:6-22"]
+	solidity_variable_29 [label="solidity variable: _returnData\nbytes\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:25:6-17"]
+	solidity_variable_30 [label="solidity variable: p\nuint32\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:25:21-22"]
+	return_31 [label="return\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:27:9-27"]
+	variable_32 [label="variable: _returnData\nbytes\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:27:16-27"]
+	ptr_to_ptr [label="function ptr_to_ptr\ncontract: c\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:30:5-33:31\nsignature ptr_to_ptr(bytes)\nvisibility public\nmutability pure"]
+	parameters_34 [label="parameters\nbytes _returnData"]
+	returns_35 [label="returns\nbytes "]
+	var_decl_36 [label="variable decl uint32[] foo\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:35:2-32"]
+	alloc_array [label="alloc array uint32[]\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:35:17-32"]
+	number_literal [label="uint32 literal: 2\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:35:30-31"]
+	inline_assembly_39 [label="inline assembly\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:36:9-38:10"]
+	yul_assignment_40 [label="yul assignment\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:37:6-24"]
+	solidity_variable_41 [label="solidity variable: _returnData\nbytes\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:37:6-17"]
+	solidity_variable_42 [label="solidity variable: foo\nuint32[]\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:37:21-24"]
+	return_43 [label="return\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:39:9-27"]
+	variable_44 [label="variable: _returnData\nbytes\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:39:16-27"]
+	diagnostic [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/substrate/yul/ptr_to_int.sol:2:1-43:2"]
+	contracts -> contract
+	contract -> tags [label="tags"]
+	contract -> add_pointer [label="function"]
+	add_pointer -> parameters [label="parameters"]
+	add_pointer -> returns [label="returns"]
+	add_pointer -> inline_assembly [label="body"]
+	inline_assembly -> yul_assignment [label="statement #0"]
+	yul_assignment -> solidity_variable [label="rhs #0"]
+	yul_assignment -> yul_builtin_call [label="lhs"]
+	yul_builtin_call -> solidity_variable_10 [label="arg #0"]
+	yul_builtin_call -> yul_number_literal [label="arg #1"]
+	inline_assembly -> inline_assembly_12 [label="next"]
+	inline_assembly_12 -> yul_assignment_13 [label="statement #0"]
+	yul_assignment_13 -> solidity_variable_14 [label="rhs #0"]
+	yul_assignment_13 -> yul_number_literal_15 [label="lhs"]
+	inline_assembly_12 -> return [label="next"]
+	return -> variable [label="expr"]
+	contract -> uint32_as_ptr [label="function"]
+	uint32_as_ptr -> parameters_19 [label="parameters"]
+	uint32_as_ptr -> returns_20 [label="returns"]
+	uint32_as_ptr -> var_decl [label="body"]
+	var_decl -> inline_assembly_22 [label="next"]
+	inline_assembly_22 -> yul_assignment_23 [label="statement #0"]
+	yul_assignment_23 -> solidity_variable_24 [label="rhs #0"]
+	yul_assignment_23 -> yul_builtin_call_25 [label="lhs"]
+	yul_builtin_call_25 -> solidity_variable_26 [label="arg #0"]
+	yul_builtin_call_25 -> yul_number_literal_27 [label="arg #1"]
+	yul_assignment_23 -> yul_assignment_28 [label="statement #1"]
+	yul_assignment_28 -> solidity_variable_29 [label="rhs #0"]
+	yul_assignment_28 -> solidity_variable_30 [label="lhs"]
+	inline_assembly_22 -> return_31 [label="next"]
+	return_31 -> variable_32 [label="expr"]
+	contract -> ptr_to_ptr [label="function"]
+	ptr_to_ptr -> parameters_34 [label="parameters"]
+	ptr_to_ptr -> returns_35 [label="returns"]
+	ptr_to_ptr -> var_decl_36 [label="body"]
+	var_decl_36 -> alloc_array [label="init"]
+	alloc_array -> number_literal [label="length"]
+	var_decl_36 -> inline_assembly_39 [label="next"]
+	inline_assembly_39 -> yul_assignment_40 [label="statement #0"]
+	yul_assignment_40 -> solidity_variable_41 [label="rhs #0"]
+	yul_assignment_40 -> solidity_variable_42 [label="lhs"]
+	inline_assembly_39 -> return_43 [label="next"]
+	return_43 -> variable_44 [label="expr"]
+	diagnostics -> diagnostic [label="Debug"]
+}

+ 43 - 0
tests/contract_testcases/substrate/yul/ptr_to_int.sol

@@ -0,0 +1,43 @@
+/// Tests various 
+contract c {
+    function add_pointer(bytes memory _returnData)
+        public
+        pure
+        returns (bytes memory)
+    {
+        assembly {
+            _returnData :=  add(_returnData, 0x04)
+        }
+	assembly {
+	    _returnData := 1
+	}
+        return _returnData;
+    }
+
+    function uint32_as_ptr(bytes memory _returnData)
+        public
+        pure
+        returns (bytes memory)
+    {
+	uint32 p;
+        assembly {
+            p :=  add(_returnData, 0x04)
+	    _returnData := p
+        }
+        return _returnData;
+    }
+
+    function ptr_to_ptr(bytes memory _returnData)
+        public
+        pure
+        returns (bytes memory)
+    {
+	uint32[] foo = new uint32[](2);
+        assembly {
+	    _returnData := foo
+        }
+        return _returnData;
+    }
+
+
+}