From a0e291bd06c7db80dd29d18b01319caf8f3e66d2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 29 Apr 2020 11:29:49 +0200 Subject: [PATCH] Implement Yul IR generation for abi.encode* --- .../codegen/ir/IRGeneratorForStatements.cpp | 89 +++++++++++++++++++ libsolidity/codegen/ir/IRVariable.cpp | 2 +- .../semanticTests/abiEncoderV1/abi_encode.sol | 2 + .../abiEncoderV1/abi_encode_rational.sol | 2 + .../abi_encode_empty_string_v2.sol | 2 + .../abiEncoderV2/abi_encode_rational_v2.sol | 2 + .../abiEncoderV2/calldata_array_dynamic.sol | 1 + .../calldata_array_dynamic_index_access.sol | 1 + .../calldata_array_multi_dynamic.sol | 1 + .../abiEncoderV2/calldata_array_static.sol | 1 + .../calldata_array_static_index_access.sol | 1 + .../calldata_array_struct_dynamic.sol | 1 + .../calldata_array_two_dynamic.sol | 1 + .../calldata_array_two_static.sol | 1 + .../abiEncoderV2/calldata_struct_dynamic.sol | 1 + .../abiEncoderV2/calldata_struct_simple.sol | 1 + .../semanticTests/array/reusing_memory.sol | 2 + .../keccak256_multiple_arguments.sol | 2 + ...ltiple_arguments_with_numeric_literals.sol | 2 + ...ultiple_arguments_with_string_literals.sol | 2 + .../tryCatch/invalid_error_encoding.sol | 1 + 21 files changed, 117 insertions(+), 1 deletion(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 7415a0977..05cfcc2db 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -781,6 +781,94 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) break; } + case FunctionType::Kind::ABIEncode: + case FunctionType::Kind::ABIEncodePacked: + case FunctionType::Kind::ABIEncodeWithSelector: + case FunctionType::Kind::ABIEncodeWithSignature: + { + bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked; + solAssert(functionType->padArguments() != isPacked, ""); + bool const hasSelectorOrSignature = + functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector || + functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature; + + TypePointers argumentTypes; + TypePointers targetTypes; + vector argumentVars; + for (size_t i = 0; i < arguments.size(); ++i) + { + // ignore selector + if (hasSelectorOrSignature && i == 0) + continue; + argumentTypes.emplace_back(&type(*arguments[i])); + targetTypes.emplace_back(type(*arguments[i]).fullEncodingType(false, true, isPacked)); + argumentVars += IRVariable(*arguments[i]).stackSlots(); + } + + string selector; + if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature) + { + // hash the signature + Type const& selectorType = type(*arguments.front()); + if (auto const* stringType = dynamic_cast(&selectorType)) + { + FixedHash<4> hash(keccak256(stringType->value())); + selector = formatNumber(u256(FixedHash<4>::Arith(hash)) << (256 - 32)); + } + else + { + // Used to reset the free memory pointer later. + string freeMemoryPre = m_context.newYulVariable(); + m_code << "let " << freeMemoryPre << " := " << freeMemory() << "\n"; + IRVariable array = convert(*arguments[0], *TypeProvider::bytesMemory()); + IRVariable hashVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(32)); + + define(hashVariable) << + "keccak256(" << + m_utils.arrayDataAreaFunction(*TypeProvider::bytesMemory()) << + "(" << + array.commaSeparatedList() << + "), " << + m_utils.arrayLengthFunction(*TypeProvider::bytesMemory()) << + "(" << + array.commaSeparatedList() << + "))\n"; + IRVariable selectorVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(4)); + define(selectorVariable, hashVariable); + m_code << "mstore(" << to_string(CompilerUtils::freeMemoryPointer) << ", " << freeMemoryPre << ")\n"; + } + } + else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector) + selector = convert(*arguments.front(), *TypeProvider::fixedBytes(4)).name(); + + Whiskers templ(R"( + let := () + let := add(, 0x20) + + mstore(, ) + := add(, 4) + + let := () + mstore(, sub(, add(, 0x20))) + mstore(, ()) + )"); + templ("data", IRVariable(_functionCall).part("mpos").name()); + templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction()); + templ("mpos", m_context.newYulVariable()); + templ("mend", m_context.newYulVariable()); + templ("selector", selector); + templ("encode", + isPacked ? + m_context.abiFunctions().tupleEncoderPacked(argumentTypes, targetTypes) : + m_context.abiFunctions().tupleEncoder(argumentTypes, targetTypes, false) + ); + templ("arguments", joinHumanReadablePrefixed(argumentVars)); + templ("freeMemPtr", to_string(CompilerUtils::freeMemoryPointer)); + templ("roundUp", m_utils.roundUpFunction()); + + m_code << templ.render(); + break; + } case FunctionType::Kind::Revert: { solAssert(arguments.size() == parameterTypes.size(), ""); @@ -843,6 +931,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) solAssert(arguments.size() == 1, ""); ArrayType const* arrayType = TypeProvider::bytesMemory(); + auto array = convert(*arguments[0], *arrayType); define(_functionCall) << diff --git a/libsolidity/codegen/ir/IRVariable.cpp b/libsolidity/codegen/ir/IRVariable.cpp index dac1087f0..d92a5fb53 100644 --- a/libsolidity/codegen/ir/IRVariable.cpp +++ b/libsolidity/codegen/ir/IRVariable.cpp @@ -89,7 +89,7 @@ string IRVariable::name() const { solAssert(m_type.sizeOnStack() == 1, ""); auto const& [itemName, type] = m_type.stackItems().front(); - solAssert(!type, ""); + solAssert(!type, "Expected null type for name " + itemName); return suffixedName(itemName); } diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol index ac17cf80e..61c6658ac 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol @@ -28,6 +28,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f0() -> 0x20, 0x0 // f1() -> 0x20, 0x40, 0x1, 0x2 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol index 704fd54dc..133645cff 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol @@ -5,5 +5,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x20, 0x40, 0x1, -2 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol index 373334ee7..405d34c5a 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol @@ -9,5 +9,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x40, 0xa0, 0x40, 0x20, 0x0, 0x0 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol index 55047880a..1074a1d04 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol @@ -8,5 +8,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x20, 0x40, 0x1, -2 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol index b2076b370..c47af87f4 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol @@ -21,6 +21,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[]): 32, 3, 23, 42, 87 -> 32, 160, 32, 3, 23, 42, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol index f2224c803..92067f8db 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol @@ -21,6 +21,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[]): 32, 3, 42, 23, 87 -> 32, 160, 32, 3, 42, 23, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol index f981a6e46..f50f59d1f 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol @@ -21,6 +21,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[][]): 0x20, 2, 0x40, 0xC0, 3, 13, 17, 23, 4, 27, 31, 37, 41 -> 32, 416, 32, 2, 64, 192, 3, 13, 17, 23, 4, 27, 31, 37, 41 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol index 9c6386e31..3a6fd9151 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol @@ -15,6 +15,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[3]): 23, 42, 87 -> 32, 96, 23, 42, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol index 4bf181db3..d23dfecfb 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol @@ -15,6 +15,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[3]): 23, 42, 87 -> 32, 96, 23, 42, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol index 02322d504..ce2f7a390 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol @@ -10,6 +10,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f((uint256[])[]): 32, 1, 32, 32, 3, 17, 42, 23 -> 32, 256, 32, 1, 32, 32, 3, 17, 42, 23 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol index 16ba40630..51674081e 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol @@ -12,6 +12,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[],uint256[],bool): 0x60, 0xE0, true, 3, 23, 42, 87, 2, 51, 72 -> 32, 160, 0x20, 3, 23, 42, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol index a4d1af66d..45a89c483 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol @@ -12,6 +12,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[3],uint256[2],bool): 23, 42, 87, 51, 72, true -> 32, 96, 23, 42, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol index 29ff15692..0ae57dbed 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol @@ -12,6 +12,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f((uint256[])): 0x20, 0x20, 3, 42, 23, 17 -> 32, 192, 0x20, 0x20, 3, 42, 23, 17 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol index f369321c3..6d747fdf0 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol @@ -12,6 +12,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f((uint256)): 3 -> 32, 32, 3 diff --git a/test/libsolidity/semanticTests/array/reusing_memory.sol b/test/libsolidity/semanticTests/array/reusing_memory.sol index b2876eeb3..994a31db9 100644 --- a/test/libsolidity/semanticTests/array/reusing_memory.sol +++ b/test/libsolidity/semanticTests/array/reusing_memory.sol @@ -22,5 +22,7 @@ contract Main { return map[a]; } } +// ==== +// compileViaYul: also // ---- // f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol index 972aee839..4a31c6715 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol @@ -3,5 +3,7 @@ contract c { d = keccak256(abi.encodePacked(a, b, c)); } } +// ==== +// compileViaYul: also // ---- // foo(uint256,uint256,uint256): 0xa, 0xc, 0xd -> 0xbc740a98aae5923e8f04c9aa798c9ee82f69e319997699f2782c40828db9fd81 diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol index 01397f55f..958975966 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol @@ -3,5 +3,7 @@ contract c { d = keccak256(abi.encodePacked(a, b, uint8(145))); } } +// ==== +// compileViaYul: also // ---- // foo(uint256,uint16): 0xa, 0xc -> 0x88acd45f75907e7c560318bc1a5249850a0999c4896717b1167d05d116e6dbad diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol index b157178fb..c4e0b7d09 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol @@ -7,6 +7,8 @@ contract c { d = keccak256(abi.encodePacked(a, b, uint8(145), "foo")); } } +// ==== +// compileViaYul: also // ---- // foo() -> 0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d // bar(uint256,uint16): 0xa, 0xc -> 0x6990f36476dc412b1c4baa48e2d9f4aa4bb313f61fda367c8fdbbb2232dc6146 diff --git a/test/libsolidity/semanticTests/tryCatch/invalid_error_encoding.sol b/test/libsolidity/semanticTests/tryCatch/invalid_error_encoding.sol index 2bfc23191..1b89213cf 100644 --- a/test/libsolidity/semanticTests/tryCatch/invalid_error_encoding.sol +++ b/test/libsolidity/semanticTests/tryCatch/invalid_error_encoding.sol @@ -149,6 +149,7 @@ contract C { } // ==== // EVMVersion: >=byzantium +// compileViaYul: also // ---- // f1() -> 2 // f1a() -> 2