From dfa0a0cdffa6435851ffa9b9e79ba140e7db7fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 27 Oct 2020 17:14:58 +0100 Subject: [PATCH 01/10] IR codegen: Handle address() with library type argument and external library calls --- libsolidity/codegen/YulUtilFunctions.cpp | 13 ++++ .../codegen/ir/IRGeneratorForStatements.cpp | 17 ++++-- .../codegen/ir/IRGeneratorForStatements.h | 2 + .../freeFunctions/libraries_from_free.sol | 4 +- ...l_call_with_function_pointer_parameter.sol | 26 ++++++++ ...rnal_call_with_storage_array_parameter.sol | 19 ++++++ .../libraries/library_address_homestead.sol | 2 + .../libraries/library_address_via_module.sol | 60 +++++++++++++++++++ .../semanticTests/libraries/stub.sol | 2 + ...ary_function_to_external_function_type.sol | 14 +++++ .../address/contract_type_to_address.sol | 21 +++++++ .../types/address/super_to_address.sol | 7 +++ .../types/address/type_type_to_address.sol | 46 ++++++++++++++ 13 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 test/libsolidity/semanticTests/libraries/external_call_with_function_pointer_parameter.sol create mode 100644 test/libsolidity/semanticTests/libraries/external_call_with_storage_array_parameter.sol create mode 100644 test/libsolidity/semanticTests/libraries/library_address_via_module.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/external_library_function_to_external_function_type.sol create mode 100644 test/libsolidity/syntaxTests/types/address/contract_type_to_address.sol create mode 100644 test/libsolidity/syntaxTests/types/address/super_to_address.sol create mode 100644 test/libsolidity/syntaxTests/types/address/type_type_to_address.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 5ccd8e285..8fe11757a 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -3059,6 +3059,19 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) solUnimplementedAssert(false, "Tuple conversion not implemented."); break; } + case Type::Category::TypeType: + { + TypeType const& typeType = dynamic_cast(_from); + if ( + auto const* contractType = dynamic_cast(typeType.actualType()); + contractType->contractDefinition().isLibrary() && + _to == *TypeProvider::address() + ) + body = "converted := value"; + else + solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName()); + break; + } default: solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName()); } diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 142660749..f81079d3e 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -838,10 +838,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { solAssert(!functionType->bound(), ""); if (auto contractType = dynamic_cast(expressionType->actualType())) - solUnimplementedAssert( - !contractType->contractDefinition().isLibrary() || functionType->kind() == FunctionType::Kind::Internal, - "Only internal function calls implemented for libraries" - ); + if (contractType->contractDefinition().isLibrary()) + solAssert(functionType->kind() == FunctionType::Kind::Internal || functionType->kind() == FunctionType::Kind::DelegateCall, ""); } } else @@ -2146,9 +2144,10 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) } else if (VariableDeclaration const* varDecl = dynamic_cast(declaration)) handleVariableReference(*varDecl, _identifier); - else if (dynamic_cast(declaration)) + else if (auto const* contract = dynamic_cast(declaration)) { - // no-op + if (contract->isLibrary()) + define(IRVariable(_identifier).part("address")) << linkerSymbol(*contract) << "\n"; } else if (dynamic_cast(declaration)) { @@ -2967,3 +2966,9 @@ void IRGeneratorForStatements::setLocation(ASTNode const& _node) { m_currentLocation = _node.location(); } + +string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const +{ + solAssert(_library.isLibrary(), ""); + return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")"; +} diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index 3d87eae57..21386dd6c 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -184,6 +184,8 @@ private: void setLocation(ASTNode const& _node); + std::string linkerSymbol(ContractDefinition const& _library) const; + std::ostringstream m_code; IRGenerationContext& m_context; YulUtilFunctions& m_utils; diff --git a/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol b/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol index 3632f6a6a..861313e92 100644 --- a/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol +++ b/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol @@ -16,6 +16,8 @@ contract C { return fu(); } } +// ==== +// compileViaYul: also // ---- // library: L -// f() -> 7, 8 \ No newline at end of file +// f() -> 7, 8 diff --git a/test/libsolidity/semanticTests/libraries/external_call_with_function_pointer_parameter.sol b/test/libsolidity/semanticTests/libraries/external_call_with_function_pointer_parameter.sol new file mode 100644 index 000000000..a0843bfcb --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/external_call_with_function_pointer_parameter.sol @@ -0,0 +1,26 @@ +library L { + function run( + function(uint256) external returns (uint256) _operation, + uint256 _a + ) + external + returns (uint256) + { + return _operation(_a); + } +} + +contract C { + function double(uint256 _a) external returns (uint256) { + return _a * _a; + } + + function g(uint256 _value) external returns (uint256) { + return L.run(this.double, _value); + } +} +// ==== +// compileViaYul: also +// ---- +// library: L +// g(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/libraries/external_call_with_storage_array_parameter.sol b/test/libsolidity/semanticTests/libraries/external_call_with_storage_array_parameter.sol new file mode 100644 index 000000000..81268af7a --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/external_call_with_storage_array_parameter.sol @@ -0,0 +1,19 @@ +library L { + function f(uint256[2] storage _a) external returns (uint256) { + return _a[0] * _a[1]; + } +} + +contract C { + uint256[2] x; + + function g(uint256 _value) external returns (uint256) { + x[0] = x[1] = _value; + return L.f(x); + } +} +// ==== +// compileViaYul: also +// ---- +// library: L +// g(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/libraries/library_address_homestead.sol b/test/libsolidity/semanticTests/libraries/library_address_homestead.sol index ce7344f4a..256b43d3a 100644 --- a/test/libsolidity/semanticTests/libraries/library_address_homestead.sol +++ b/test/libsolidity/semanticTests/libraries/library_address_homestead.sol @@ -12,6 +12,8 @@ contract C { return success; } } +// ==== +// compileViaYul: also // ---- // library: L // g(uint256,uint256): 1, 1 -> true diff --git a/test/libsolidity/semanticTests/libraries/library_address_via_module.sol b/test/libsolidity/semanticTests/libraries/library_address_via_module.sol new file mode 100644 index 000000000..1a6c3ee41 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_address_via_module.sol @@ -0,0 +1,60 @@ +==== Source: a.sol ==== + +import "a.sol" as M; + +library L { + function f(uint256 v) external pure returns (uint) { + return v * v; + } + function g(uint256 v) external returns (uint) { + return v * v; + } +} +contract C { + function addr() public view returns (bool) { + return address(M.L) == address(0); + } + function g(uint256 v) public view returns (uint256) { + return M.L.f(v); + } + function h(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(M.L).delegatecall(abi.encodeWithSignature("f(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function i(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(M.L).call(abi.encodeWithSignature("f(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function j(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(M.L).delegatecall(abi.encodeWithSignature("g(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function k(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(M.L).call(abi.encodeWithSignature("g(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// library: L +// addr() -> false +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 4 +// g(uint256): 4 -> 16 +// h(uint256): 1 -> 1 +// h(uint256): 2 -> 4 +// h(uint256): 4 -> 16 +// i(uint256): 1 -> 1 +// i(uint256): 2 -> 4 +// i(uint256): 4 -> 16 +// j(uint256): 1 -> 1 +// j(uint256): 2 -> 4 +// j(uint256): 4 -> 16 +// k(uint256): 1 -> FAILURE +// k(uint256): 2 -> FAILURE +// k(uint256): 4 -> FAILURE diff --git a/test/libsolidity/semanticTests/libraries/stub.sol b/test/libsolidity/semanticTests/libraries/stub.sol index 8bae3df27..4ed1f2673 100644 --- a/test/libsolidity/semanticTests/libraries/stub.sol +++ b/test/libsolidity/semanticTests/libraries/stub.sol @@ -6,6 +6,8 @@ contract C { return L.f(v); } } +// ==== +// compileViaYul: also // ---- // library: L // g(uint256): 1 -> 1 diff --git a/test/libsolidity/syntaxTests/functionTypes/external_library_function_to_external_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/external_library_function_to_external_function_type.sol new file mode 100644 index 000000000..dbc873b39 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/external_library_function_to_external_function_type.sol @@ -0,0 +1,14 @@ +library L { + function f(uint256 _a) external returns (uint256) {} +} + +contract C { + function run(function(uint256) external returns (uint256) _operation) internal returns (uint256) {} + function test() public { + run(L.f); + function(uint256) external returns (uint256) _operation = L.f; + } +} +// ---- +// TypeError 9553: (230-233): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) returns (uint256) to function (uint256) external returns (uint256) requested. +// TypeError 9574: (244-305): Type function (uint256) returns (uint256) is not implicitly convertible to expected type function (uint256) external returns (uint256). diff --git a/test/libsolidity/syntaxTests/types/address/contract_type_to_address.sol b/test/libsolidity/syntaxTests/types/address/contract_type_to_address.sol new file mode 100644 index 000000000..62b34a294 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/contract_type_to_address.sol @@ -0,0 +1,21 @@ +interface I {} + +library L {} + +contract C { + function f() public pure { + address(C); + address(I); + address(L); // This one is allowed + + address(type(C)); + address(type(I)); + address(type(L)); + } +} +// ---- +// TypeError 9640: (82-92): Explicit type conversion not allowed from "type(contract C)" to "address". +// TypeError 9640: (102-112): Explicit type conversion not allowed from "type(contract I)" to "address". +// TypeError 9640: (166-182): Explicit type conversion not allowed from "type(contract C)" to "address". +// TypeError 9640: (192-208): Explicit type conversion not allowed from "type(contract I)" to "address". +// TypeError 9640: (218-234): Explicit type conversion not allowed from "type(library L)" to "address". diff --git a/test/libsolidity/syntaxTests/types/address/super_to_address.sol b/test/libsolidity/syntaxTests/types/address/super_to_address.sol new file mode 100644 index 000000000..7ffcd4ece --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/super_to_address.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + address(super); + } +} +// ---- +// TypeError 9640: (52-66): Explicit type conversion not allowed from "contract super C" to "address". diff --git a/test/libsolidity/syntaxTests/types/address/type_type_to_address.sol b/test/libsolidity/syntaxTests/types/address/type_type_to_address.sol new file mode 100644 index 000000000..7f4666a54 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/type_type_to_address.sol @@ -0,0 +1,46 @@ +struct S { + uint x; +} + +enum E {A, B, C} + +contract C { + function f() public pure { + address(uint); + address(bytes16); + address(bool); + address(address); + address(fixed); + + address(S); + address(E); + + address(uint[]); + address(uint[][]); + address(uint[5]); + address(string); + address(bytes); + address(S[]); + address(E[]); + address((uint, uint)); + + address(type(uint)); + } +} +// ---- +// TypeError 9640: (96-109): Explicit type conversion not allowed from "type(uint256)" to "address". +// TypeError 9640: (119-135): Explicit type conversion not allowed from "type(bytes16)" to "address". +// TypeError 9640: (145-158): Explicit type conversion not allowed from "type(bool)" to "address". +// TypeError 9640: (168-184): Explicit type conversion not allowed from "type(address)" to "address". +// TypeError 9640: (194-208): Explicit type conversion not allowed from "type(fixed128x18)" to "address". +// TypeError 9640: (219-229): Explicit type conversion not allowed from "type(struct S storage pointer)" to "address". +// TypeError 9640: (239-249): Explicit type conversion not allowed from "type(enum E)" to "address". +// TypeError 9640: (260-275): Explicit type conversion not allowed from "type(uint256[] memory)" to "address". +// TypeError 9640: (285-302): Explicit type conversion not allowed from "type(uint256[] memory[] memory)" to "address". +// TypeError 9640: (312-328): Explicit type conversion not allowed from "type(uint256[5] memory)" to "address". +// TypeError 9640: (338-353): Explicit type conversion not allowed from "type(string storage pointer)" to "address". +// TypeError 9640: (363-377): Explicit type conversion not allowed from "type(bytes storage pointer)" to "address". +// TypeError 9640: (387-399): Explicit type conversion not allowed from "type(struct S memory[] memory)" to "address". +// TypeError 9640: (409-421): Explicit type conversion not allowed from "type(enum E[] memory)" to "address". +// TypeError 9640: (431-452): Explicit type conversion not allowed from "tuple(type(uint256),type(uint256))" to "address". +// TypeError 9640: (463-482): Explicit type conversion not allowed from "type(uint256)" to "address". From ced140615abbd321908b086c8e6f142fbb16a9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 9 Nov 2020 01:32:17 +0100 Subject: [PATCH 02/10] Test for calling external library functions with mapping parameters (does not work via Yul yet) --- ...rnal_call_with_storage_mapping_parameter.sol | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/libsolidity/semanticTests/libraries/external_call_with_storage_mapping_parameter.sol diff --git a/test/libsolidity/semanticTests/libraries/external_call_with_storage_mapping_parameter.sol b/test/libsolidity/semanticTests/libraries/external_call_with_storage_mapping_parameter.sol new file mode 100644 index 000000000..d009bed5c --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/external_call_with_storage_mapping_parameter.sol @@ -0,0 +1,17 @@ +library L { + function f(mapping(uint256 => uint256) storage _a) external returns (uint256) { + return _a[0] * _a[1]; + } +} + +contract C { + mapping(uint256 => uint256) x; + + function g(uint256 _value) external returns (uint256) { + x[0] = x[1] = _value; + return L.f(x); + } +} +// ---- +// library: L +// g(uint256): 4 -> 16 From 9a5fc1118f237f6328876db5df01c4cabccb6506 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 10 Nov 2020 14:49:34 +0000 Subject: [PATCH 03/10] Document coreutils as a testing dependency --- docs/contributing.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/contributing.rst b/docs/contributing.rst index eec3524e9..e1e2e2b62 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -94,6 +94,9 @@ in the current directory, installed on the system level, or the ``deps`` folder in the project top level. The required file is called ``libevmone.so`` on Linux systems, ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. +On macOS some of the testing scripts expect GNU coreutils to be installed. +This can be easiest accomplished using Homebrew: ``brew install coreutils``. + Running the Tests ----------------- From 19f764bf652d45b42f12653471a8457b6313edf4 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Tue, 17 Nov 2020 12:56:07 -0500 Subject: [PATCH 04/10] Split ewasm polyfill functions into different files. --- cmake/templates/ewasm_polyfill.in | 13 + libyul/CMakeLists.txt | 19 + libyul/backends/wasm/EVMToEwasmTranslator.cpp | 1199 +---------------- libyul/backends/wasm/polyfill/Arithmetic.yul | 396 ++++++ libyul/backends/wasm/polyfill/Bitwise.yul | 222 +++ libyul/backends/wasm/polyfill/Comparison.yul | 169 +++ libyul/backends/wasm/polyfill/Conversion.yul | 78 ++ libyul/backends/wasm/polyfill/Interface.yul | 411 ++++++ libyul/backends/wasm/polyfill/Keccak.yul | 24 + libyul/backends/wasm/polyfill/Logical.yul | 31 + libyul/backends/wasm/polyfill/Memory.yul | 62 + test/cmdlineTests/evm_to_wasm_break/output | 118 +- .../standard_ewasm_requested/output.json | 78 +- 13 files changed, 1546 insertions(+), 1274 deletions(-) create mode 100644 cmake/templates/ewasm_polyfill.in create mode 100644 libyul/backends/wasm/polyfill/Arithmetic.yul create mode 100644 libyul/backends/wasm/polyfill/Bitwise.yul create mode 100644 libyul/backends/wasm/polyfill/Comparison.yul create mode 100644 libyul/backends/wasm/polyfill/Conversion.yul create mode 100644 libyul/backends/wasm/polyfill/Interface.yul create mode 100644 libyul/backends/wasm/polyfill/Keccak.yul create mode 100644 libyul/backends/wasm/polyfill/Logical.yul create mode 100644 libyul/backends/wasm/polyfill/Memory.yul diff --git a/cmake/templates/ewasm_polyfill.in b/cmake/templates/ewasm_polyfill.in new file mode 100644 index 000000000..8ac364280 --- /dev/null +++ b/cmake/templates/ewasm_polyfill.in @@ -0,0 +1,13 @@ +// The generation of this file is defined in libyul/CMakeLists.txt. +// This file was generated by using the content of libyul/backends/wasm/polyfill/@EWASM_POLYFILL_NAME@.yul. + +#pragma once + +namespace solidity::yul::wasm::polyfill +{ + +static char const @EWASM_POLYFILL_NAME@[] = { + @EWASM_POLYFILL_CONTENT@, 0 +}; + +} // namespace solidity::yul::wasm::polyfill diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 187091278..14f0d6884 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -61,6 +61,14 @@ add_library(yul backends/wasm/WasmObjectCompiler.h backends/wasm/WordSizeTransform.cpp backends/wasm/WordSizeTransform.h + backends/wasm/polyfill/Arithmetic.yul + backends/wasm/polyfill/Bitwise.yul + backends/wasm/polyfill/Comparison.yul + backends/wasm/polyfill/Conversion.yul + backends/wasm/polyfill/Interface.yul + backends/wasm/polyfill/Keccak.yul + backends/wasm/polyfill/Logical.yul + backends/wasm/polyfill/Memory.yul optimiser/ASTCopier.cpp optimiser/ASTCopier.h optimiser/ASTWalker.cpp @@ -179,4 +187,15 @@ add_library(yul optimiser/VarNameCleaner.h ) +set(POLYFILLS Arithmetic Bitwise Comparison Conversion Interface Keccak Logical Memory) +foreach(polyfill IN LISTS POLYFILLS) + set(POLYFILL_FILE ${CMAKE_SOURCE_DIR}/libyul/backends/wasm/polyfill/${polyfill}.yul) + file(READ ${POLYFILL_FILE} EWASM_POLYFILL_CONTENT HEX) + string(REGEX MATCHALL ".." EWASM_POLYFILL_CONTENT "${EWASM_POLYFILL_CONTENT}") + string(REGEX REPLACE ";" ",\n\t0x" EWASM_POLYFILL_CONTENT "${EWASM_POLYFILL_CONTENT}") + set(EWASM_POLYFILL_CONTENT "0x${EWASM_POLYFILL_CONTENT}") + set(EWASM_POLYFILL_NAME ${polyfill}) + configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/ewasm_polyfill.in" ${CMAKE_BINARY_DIR}/include/ewasmPolyfills/${polyfill}.h @ONLY) +endforeach() + target_link_libraries(yul PUBLIC evmasm solutil langutil smtutil) \ No newline at end of file diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index c92cd6652..e6d74bd14 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -41,1187 +41,24 @@ #include #include +// The following headers are generated from the +// yul files placed in libyul/backends/wasm/polyfill. + +#include +#include +#include +#include +#include +#include +#include +#include + using namespace std; using namespace solidity; using namespace solidity::yul; using namespace solidity::util; using namespace solidity::langutil; -namespace -{ -static string const polyfill{R"( -{ -function or_bool(a, b, c, d) -> r:i32 { - r := i32.eqz(i64.eqz(i64.or(i64.or(a, b), i64.or(c, d)))) -} -function or_bool_320(a, b, c, d, e) -> r:i32 { - r := i32.or(or_bool(a, b, c, 0), or_bool(d, e, 0, 0)) -} -function or_bool_512(a, b, c, d, e, f, g, h) -> r:i32 { - r := i32.or(or_bool(a, b, c, d), or_bool(e, f, g, h)) -} -// returns a + y + c plus carry value on 64 bit values. -// c should be at most 1 -function add_carry(x, y, c) -> r, r_c { - let t := i64.add(x, y) - r := i64.add(t, c) - r_c := i64.extend_i32_u(i32.or( - i64.lt_u(t, x), - i64.lt_u(r, t) - )) -} -function add(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - let carry - r4, carry := add_carry(x4, y4, 0) - r3, carry := add_carry(x3, y3, carry) - r2, carry := add_carry(x2, y2, carry) - r1, carry := add_carry(x1, y1, carry) -} -function bit_negate(x) -> y { - y := i64.xor(x, 0xffffffffffffffff) -} -function sub(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // x - y = x + (~y + 1) - let carry - r4, carry := add_carry(x4, bit_negate(y4), 1) - r3, carry := add_carry(x3, bit_negate(y3), carry) - r2, carry := add_carry(x2, bit_negate(y2), carry) - r1, carry := add_carry(x1, bit_negate(y1), carry) -} -function sub320(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> r1, r2, r3, r4, r5 { - // x - y = x + (~y + 1) - let carry - r5, carry := add_carry(x5, bit_negate(y5), 1) - r4, carry := add_carry(x4, bit_negate(y4), carry) - r3, carry := add_carry(x3, bit_negate(y3), carry) - r2, carry := add_carry(x2, bit_negate(y2), carry) - r1, carry := add_carry(x1, bit_negate(y1), carry) -} -function sub512(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> r1, r2, r3, r4, r5, r6, r7, r8 { - // x - y = x + (~y + 1) - let carry - r8, carry := add_carry(x8, bit_negate(y8), 1) - r7, carry := add_carry(x7, bit_negate(y7), carry) - r6, carry := add_carry(x6, bit_negate(y6), carry) - r5, carry := add_carry(x5, bit_negate(y5), carry) - r4, carry := add_carry(x4, bit_negate(y4), carry) - r3, carry := add_carry(x3, bit_negate(y3), carry) - r2, carry := add_carry(x2, bit_negate(y2), carry) - r1, carry := add_carry(x1, bit_negate(y1), carry) -} -function split(x) -> hi, lo { - hi := i64.shr_u(x, 32) - lo := i64.and(x, 0xffffffff) -} -// Multiplies two 64 bit values resulting in a 128 bit -// value split into two 64 bit values. -function mul_64x64_128(x, y) -> hi, lo { - let xh, xl := split(x) - let yh, yl := split(y) - - let t0 := i64.mul(xl, yl) - let t1 := i64.mul(xh, yl) - let t2 := i64.mul(xl, yh) - let t3 := i64.mul(xh, yh) - - let t0h, t0l := split(t0) - let u1 := i64.add(t1, t0h) - let u1h, u1l := split(u1) - let u2 := i64.add(t2, u1l) - - lo := i64.or(i64.shl(u2, 32), t0l) - hi := i64.add(t3, i64.add(i64.shr_u(u2, 32), u1h)) -} -// Multiplies two 128 bit values resulting in a 256 bit -// value split into four 64 bit values. -function mul_128x128_256(x1, x2, y1, y2) -> r1, r2, r3, r4 { - let ah, al := mul_64x64_128(x1, y1) - let bh, bl := mul_64x64_128(x1, y2) - let ch, cl := mul_64x64_128(x2, y1) - let dh, dl := mul_64x64_128(x2, y2) - - r4 := dl - - let carry1, carry2 - let t1, t2 - - r3, carry1 := add_carry(bl, cl, 0) - r3, carry2 := add_carry(r3, dh, 0) - - t1, carry1 := add_carry(bh, ch, carry1) - r2, carry2 := add_carry(t1, al, carry2) - - r1 := i64.add(i64.add(ah, carry1), carry2) -} -// Multiplies two 256 bit values resulting in a 512 bit -// value split into eight 64 bit values. -function mul_256x256_512(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4, r5, r6, r7, r8 { - let a1, a2, a3, a4 := mul_128x128_256(x1, x2, y1, y2) - let b1, b2, b3, b4 := mul_128x128_256(x1, x2, y3, y4) - let c1, c2, c3, c4 := mul_128x128_256(x3, x4, y1, y2) - let d1, d2, d3, d4 := mul_128x128_256(x3, x4, y3, y4) - - r8 := d4 - r7 := d3 - - let carry1, carry2 - let t1, t2 - - r6, carry1 := add_carry(b4, c4, 0) - r6, carry2 := add_carry(r6, d2, 0) - - r5, carry1 := add_carry(b3, c3, carry1) - r5, carry2 := add_carry(r5, d1, carry2) - - r4, carry1 := add_carry(a4, b2, carry1) - r4, carry2 := add_carry(r4, c2, carry2) - - r3, carry1 := add_carry(a3, b1, carry1) - r3, carry2 := add_carry(r3, c1, carry2) - - r2, carry1 := add_carry(a2, carry1, carry2) - r1 := i64.add(a1, carry1) -} -function mul(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO it would actually suffice to have mul_128x128_128 for the first two. - let b1, b2, b3, b4 := mul_128x128_256(x3, x4, y1, y2) - let c1, c2, c3, c4 := mul_128x128_256(x1, x2, y3, y4) - let d1, d2, d3, d4 := mul_128x128_256(x3, x4, y3, y4) - r4 := d4 - r3 := d3 - let t1, t2 - t1, t2, r1, r2 := add(0, 0, b3, b4, 0, 0, c3, c4) - t1, t2, r1, r2 := add(0, 0, r1, r2, 0, 0, d1, d2) -} -function shl_internal(amount, x1, x2, x3, x4) -> r1, r2, r3, r4 { - // amount < 64 - r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) - r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) - r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) - r4 := i64.shl(x4, amount) -} -function shr_internal(amount, x1, x2, x3, x4) -> r1, r2, r3, r4 { - // amount < 64 - r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) - r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) - r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) - r1 := i64.shr_u(x1, amount) -} -function shl320_internal(amount, x1, x2, x3, x4, x5) -> r1, r2, r3, r4, r5 { - // amount < 64 - r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) - r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) - r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) - r4 := i64.add(i64.shl(x4, amount), i64.shr_u(x5, i64.sub(64, amount))) - r5 := i64.shl(x5, 1) -} -function shr320_internal(amount, x1, x2, x3, x4, x5) -> r1, r2, r3, r4, r5 { - // amount < 64 - r5 := i64.add(i64.shr_u(x5, amount), i64.shl(x4, i64.sub(64, amount))) - r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) - r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) - r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) - r1 := i64.shr_u(x1, 1) -} -function shl512_internal(amount, x1, x2, x3, x4, x5, x6, x7, x8) -> r1, r2, r3, r4, r5, r6, r7, r8 { - // amount < 64 - r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) - r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) - r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) - r4 := i64.add(i64.shl(x4, amount), i64.shr_u(x5, i64.sub(64, amount))) - r5 := i64.add(i64.shl(x5, amount), i64.shr_u(x6, i64.sub(64, amount))) - r6 := i64.add(i64.shl(x6, amount), i64.shr_u(x7, i64.sub(64, amount))) - r7 := i64.add(i64.shl(x7, amount), i64.shr_u(x8, i64.sub(64, amount))) - r8 := i64.shl(x8, amount) -} -function shr512_internal(amount, x1, x2, x3, x4, x5, x6, x7, x8) -> r1, r2, r3, r4, r5, r6, r7, r8 { - // amount < 64 - r8 := i64.add(i64.shr_u(x8, amount), i64.shl(x7, i64.sub(64, amount))) - r7 := i64.add(i64.shr_u(x7, amount), i64.shl(x6, i64.sub(64, amount))) - r6 := i64.add(i64.shr_u(x6, amount), i64.shl(x5, i64.sub(64, amount))) - r5 := i64.add(i64.shr_u(x5, amount), i64.shl(x4, i64.sub(64, amount))) - r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) - r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) - r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) - r1 := i64.shr_u(x1, amount) -} -function div(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/DIV.wast - if iszero256(y1, y2, y3, y4) { - leave - } - - let m1 := 0 - let m2 := 0 - let m3 := 0 - let m4 := 1 - - for {} true {} { - if i32.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, x1, x2, x3, x4)) { - break - } - y1, y2, y3, y4 := shl_internal(1, y1, y2, y3, y4) - m1, m2, m3, m4 := shl_internal(1, m1, m2, m3, m4) - } - - for {} or_bool(m1, m2, m3, m4) {} { - if gte_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) { - x1, x2, x3, x4 := sub(x1, x2, x3, x4, y1, y2, y3, y4) - r1, r2, r3, r4 := add(r1, r2, r3, r4, m1, m2, m3, m4) - } - - y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) - m1, m2, m3, m4 := shr_internal(1, m1, m2, m3, m4) - } - -} -function sdiv(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/SDIV.wast - - let sign:i32 := i32.wrap_i64(i64.shr_u(i64.xor(x1, y1), 63)) - - if i64.eqz(i64.clz(x1)) { - x1, x2, x3, x4 := xor( - x1, x2, x3, x4, - 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ) - x1, x2, x3, x4 := add(x1, x2, x3, x4, 0, 0, 0, 1) - } - - if i64.eqz(i64.clz(y1)) { - y1, y2, y3, y4 := xor( - y1, y2, y3, y4, - 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ) - y1, y2, y3, y4 := add(y1, y2, y3, y4, 0, 0, 0, 1) - } - - r1, r2, r3, r4 := div(x1, x2, x3, x4, y1, y2, y3, y4) - - if sign { - r1, r2, r3, r4 := xor( - r1, r2, r3, r4, - 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ) - r1, r2, r3, r4 := add(r1, r2, r3, r4, 0, 0, 0, 1) - } -} -function mod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/MOD.wast - if iszero256(y1, y2, y3, y4) { - leave - } - - r1 := x1 - r2 := x2 - r3 := x3 - r4 := x4 - - let m1 := 0 - let m2 := 0 - let m3 := 0 - let m4 := 1 - - for {} true {} { - if i32.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, r1, r2, r3, r4)) { - break - } - - y1, y2, y3, y4 := shl_internal(1, y1, y2, y3, y4) - m1, m2, m3, m4 := shl_internal(1, m1, m2, m3, m4) - } - - for {} or_bool(m1, m2, m3, m4) {} { - if gte_256x256_64(r1, r2, r3, r4, y1, y2, y3, y4) { - r1, r2, r3, r4 := sub(r1, r2, r3, r4, y1, y2, y3, y4) - } - - y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) - m1, m2, m3, m4 := shr_internal(1, m1, m2, m3, m4) - } -} -function mod320(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> r1, r2, r3, r4, r5 { - // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/mod_320.wast - if iszero320(y1, y2, y3, y4, y5) { - leave - } - - let m1 := 0 - let m2 := 0 - let m3 := 0 - let m4 := 0 - let m5 := 1 - - r1 := x1 - r2 := x2 - r3 := x3 - r4 := x4 - r5 := x5 - - for {} true {} { - if i32.or(i64.eqz(i64.clz(y1)), gte_320x320_64(y1, y2, y3, y4, y5, r1, r2, r3, r4, r5)) { - break - } - y1, y2, y3, y4, y5 := shl320_internal(1, y1, y2, y3, y4, y5) - m1, m2, m3, m4, m5 := shl320_internal(1, m1, m2, m3, m4, m5) - } - - for {} or_bool_320(m1, m2, m3, m4, m5) {} { - if gte_320x320_64(r1, r2, r3, r4, r5, y1, y2, y3, y4, y5) { - r1, r2, r3, r4, r5 := sub320(r1, r2, r3, r4, r5, y1, y2, y3, y4, y5) - } - - y1, y2, y3, y4, y5 := shr320_internal(1, y1, y2, y3, y4, y5) - m1, m2, m3, m4, m5 := shr320_internal(1, m1, m2, m3, m4, m5) - } -} -function mod512(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> r1, r2, r3, r4, r5, r6, r7, r8 { - // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/mod_512.wast - if iszero512(y1, y2, y3, y4, y5, y6, y7, y8) { - leave - } - - let m1 := 0 - let m2 := 0 - let m3 := 0 - let m4 := 0 - let m5 := 0 - let m6 := 0 - let m7 := 0 - let m8 := 1 - - r1 := x1 - r2 := x2 - r3 := x3 - r4 := x4 - r5 := x5 - r6 := x6 - r7 := x7 - r8 := x8 - - for {} true {} { - if i32.or( - i64.eqz(i64.clz(y1)), - gte_512x512_64(y1, y2, y3, y4, y5, y6, y7, y8, r1, r2, r3, r4, r5, r6, r7, r8) - ) - { - break - } - y1, y2, y3, y4, y5, y6, y7, y8 := shl512_internal(1, y1, y2, y3, y4, y5, y6, y7, y8) - m1, m2, m3, m4, m5, m6, m7, m8 := shl512_internal(1, m1, m2, m3, m4, m5, m6, m7, m8) - } - - for {} or_bool_512(m1, m2, m3, m4, m5, m6, m7, m8) {} { - if gte_512x512_64(r1, r2, r3, r4, r5, r6, r7, r8, y1, y2, y3, y4, y5, y6, y7, y8) { - r1, r2, r3, r4, r5, r6, r7, r8 := sub512(r1, r2, r3, r4, r5, r6, r7, r8, y1, y2, y3, y4, y5, y6, y7, y8) - } - - y1, y2, y3, y4, y5, y6, y7, y8 := shr512_internal(1, y1, y2, y3, y4, y5, y6, y7, y8) - m1, m2, m3, m4, m5, m6, m7, m8 := shr512_internal(1, m1, m2, m3, m4, m5, m6, m7, m8) - } -} -function smod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/SMOD.wast - let m1 := 0 - let m2 := 0 - let m3 := 0 - let m4 := 1 - - let sign:i32 := i32.wrap_i64(i64.shr_u(x1, 63)) - - if i64.eqz(i64.clz(x1)) { - x1, x2, x3, x4 := xor( - x1, x2, x3, x4, - 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ) - x1, x2, x3, x4 := add(x1, x2, x3, x4, 0, 0, 0, 1) - } - - if i64.eqz(i64.clz(y1)) { - y1, y2, y3, y4 := xor( - y1, y2, y3, y4, - 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ) - y1, y2, y3, y4 := add(y1, y2, y3, y4, 0, 0, 0, 1) - } - - r1, r2, r3, r4 := mod(x1, x2, x3, x4, y1, y2, y3, y4) - - if sign { - r1, r2, r3, r4 := xor( - r1, r2, r3, r4, - 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ) - r1, r2, r3, r4 := add(r1, r2, r3, r4, 0, 0, 0, 1) - } -} -function exp(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - r4 := 1 - for {} or_bool(y1, y2, y3, y4) {} { - if i32.wrap_i64(i64.and(y4, 1)) { - r1, r2, r3, r4 := mul(r1, r2, r3, r4, x1, x2, x3, x4) - } - x1, x2, x3, x4 := mul(x1, x2, x3, x4, x1, x2, x3, x4) - y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) - } -} - -function byte(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - if i64.eqz(i64.or(i64.or(x1, x2), x3)) { - let component - switch i64.div_u(x4, 8) - case 0 { component := y1 } - case 1 { component := y2 } - case 2 { component := y3 } - case 3 { component := y4 } - x4 := i64.mul(i64.rem_u(x4, 8), 8) - r4 := i64.shr_u(component, i64.sub(56, x4)) - r4 := i64.and(0xff, r4) - } -} -function xor(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - r1 := i64.xor(x1, y1) - r2 := i64.xor(x2, y2) - r3 := i64.xor(x3, y3) - r4 := i64.xor(x4, y4) -} -function or(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - r1 := i64.or(x1, y1) - r2 := i64.or(x2, y2) - r3 := i64.or(x3, y3) - r4 := i64.or(x4, y4) -} -function and(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - r1 := i64.and(x1, y1) - r2 := i64.and(x2, y2) - r3 := i64.and(x3, y3) - r4 := i64.and(x4, y4) -} -function not(x1, x2, x3, x4) -> r1, r2, r3, r4 { - let mask := 0xffffffffffffffff - r1, r2, r3, r4 := xor(x1, x2, x3, x4, mask, mask, mask, mask) -} -function iszero(x1, x2, x3, x4) -> r1, r2, r3, r4 { - r4 := i64.extend_i32_u(iszero256(x1, x2, x3, x4)) -} -function iszero256(x1, x2, x3, x4) -> r:i32 { - r := i64.eqz(i64.or(i64.or(x1, x2), i64.or(x3, x4))) -} -function iszero320(x1, x2, x3, x4, x5) -> r:i32 { - r := i64.eqz(i64.or(i64.or(i64.or(x1, x2), i64.or(x3, x4)), x5)) -} -function iszero512(x1, x2, x3, x4, x5, x6, x7, x8) -> r:i32 { - r := i32.and(iszero256(x1, x2, x3, x4), iszero256(x5, x6, x7, x8)) -} -function eq(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - if i64.eq(x1, y1) { - if i64.eq(x2, y2) { - if i64.eq(x3, y3) { - if i64.eq(x4, y4) { - r4 := 1 - } - } - } - } -} - -// returns 0 if a == b, -1 if a < b and 1 if a > b -function cmp(a, b) -> r:i32 { - switch i64.lt_u(a, b) - case 1:i32 { r := 0xffffffff:i32 } - default { - r := i64.ne(a, b) - } -} -function lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z:i32 { - switch cmp(x1, y1) - case 0:i32 { - switch cmp(x2, y2) - case 0:i32 { - switch cmp(x3, y3) - case 0:i32 { - switch cmp(x4, y4) - case 0:i32 { - z := i64.lt_u(x5, y5) - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } -} -function lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z:i32 { - switch cmp(x1, y1) - case 0:i32 { - switch cmp(x2, y2) - case 0:i32 { - switch cmp(x3, y3) - case 0:i32 { - switch cmp(x4, y4) - case 0:i32 { - switch cmp(x5, y5) - case 0:i32 { - switch cmp(x6, y6) - case 0:i32 { - switch cmp(x7, y7) - case 0:i32 { - z := i64.lt_u(x8, y8) - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } -}/* -)" -// Split long string to make it compilable on msvc -// https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?view=vs-2019 -R"( -*/ -function lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z:i32 { - switch cmp(x1, y1) - case 0:i32 { - switch cmp(x2, y2) - case 0:i32 { - switch cmp(x3, y3) - case 0:i32 { - z := i64.lt_u(x4, y4) - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } - } - case 1:i32 { z := 0:i32 } - default { z := 1:i32 } -} -function lt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - z4 := i64.extend_i32_u(lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4)) -} -function gte_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z:i32 { - z := i32.eqz(lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4)) -} -function gte_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z:i32 { - z := i32.eqz(lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5)) -} -function gte_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z:i32 { - z := i32.eqz(lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8)) -} -function gt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - z1, z2, z3, z4 := lt(y1, y2, y3, y4, x1, x2, x3, x4) -} -function slt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - // TODO correct? - x1 := i64.add(x1, 0x8000000000000000) - y1 := i64.add(y1, 0x8000000000000000) - z1, z2, z3, z4 := lt(x1, x2, x3, x4, y1, y2, y3, y4) -} -function sgt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - z1, z2, z3, z4 := slt(y1, y2, y3, y4, x1, x2, x3, x4) -} - -function shl_single(a, amount) -> x, y { - // amount < 64 - x := i64.shr_u(a, i64.sub(64, amount)) - y := i64.shl(a, amount) -} - -function shl(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - if i32.and(i64.eqz(x1), i64.eqz(x2)) { - if i64.eqz(x3) { - if i64.lt_u(x4, 256) { - if i64.ge_u(x4, 128) { - y1 := y3 - y2 := y4 - y3 := 0 - y4 := 0 - x4 := i64.sub(x4, 128) - } - if i64.ge_u(x4, 64) { - y1 := y2 - y2 := y3 - y3 := y4 - y4 := 0 - x4 := i64.sub(x4, 64) - } - let t, r - t, z4 := shl_single(y4, x4) - r, z3 := shl_single(y3, x4) - z3 := i64.or(z3, t) - t, z2 := shl_single(y2, x4) - z2 := i64.or(z2, r) - r, z1 := shl_single(y1, x4) - z1 := i64.or(z1, t) - } - } - } -} - -function shr_single(a, amount) -> x, y { - // amount < 64 - y := i64.shl(a, i64.sub(64, amount)) - x := i64.shr_u(a, amount) -} - -function shr(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - if i32.and(i64.eqz(x1), i64.eqz(x2)) { - if i64.eqz(x3) { - if i64.lt_u(x4, 256) { - if i64.ge_u(x4, 128) { - y4 := y2 - y3 := y1 - y2 := 0 - y1 := 0 - x4 := i64.sub(x4, 128) - } - if i64.ge_u(x4, 64) { - y4 := y3 - y3 := y2 - y2 := y1 - y1 := 0 - x4 := i64.sub(x4, 64) - } - let t - z4, t := shr_single(y4, x4) - z3, t := shr_single(y3, x4) - z4 := i64.or(z4, t) - z2, t := shr_single(y2, x4) - z3 := i64.or(z3, t) - z1, t := shr_single(y1, x4) - z2 := i64.or(z2, t) - } - } - } -} -function sar(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - if i64.gt_u(i64.clz(y1), 0) { - z1, z2, z3, z4 := shr(x1, x2, x3, x4, y1, y2, y3, y4) - leave - } - - if gte_256x256_64(x1, x2, x3, x4, 0, 0, 0, 256) { - z1 := 0xffffffffffffffff - z2 := 0xffffffffffffffff - z3 := 0xffffffffffffffff - z4 := 0xffffffffffffffff - } - if lt_256x256_64(x1, x2, x3, x4, 0, 0, 0, 256) { - y1, y2, y3, y4 := shr(0, 0, 0, x4, y1, y2, y3, y4) - z1, z2, z3, z4 := shl( - 0, 0, 0, i64.sub(256, x4), - 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ) - z1, z2, z3, z4 := or(y1, y2, y3, y4, z1, z2, z3, z4) - } -} -function addmod(x1, x2, x3, x4, y1, y2, y3, y4, m1, m2, m3, m4) -> z1, z2, z3, z4 { - let carry - z4, carry := add_carry(x4, y4, 0) - z3, carry := add_carry(x3, y3, carry) - z2, carry := add_carry(x2, y2, carry) - z1, carry := add_carry(x1, y1, carry) - - let z0 - z0, z1, z2, z3, z4 := mod320(carry, z1, z2, z3, z4, 0, m1, m2, m3, m4) -} -function mulmod(x1, x2, x3, x4, y1, y2, y3, y4, m1, m2, m3, m4) -> z1, z2, z3, z4 { - let r1, r2, r3, r4, r5, r6, r7, r8 := mul_256x256_512(x1, x2, x3, x4, y1, y2, y3, y4) - let t1 - let t2 - let t3 - let t4 - t1, t2, t3, t4, z1, z2, z3, z4 := mod512(r1, r2, r3, r4, r5, r6, r7, r8, 0, 0, 0, 0, m1, m2, m3, m4) -} -function signextend(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - z1 := y1 - z2 := y2 - z3 := y3 - z4 := y4 - if lt_256x256_64(x1, x2, x3, x4, 0, 0, 0, 32) { - let d := i64.mul(i64.sub(31, x4), 8) - z1, z2, z3, z4 := shl(0, 0, 0, d, z1, z2, z3, z4) - z1, z2, z3, z4 := sar(0, 0, 0, d, z1, z2, z3, z4) - } -} -function u256_to_u128(x1, x2, x3, x4) -> v1, v2 { - if i64.ne(0, i64.or(x1, x2)) { invalid() } - v2 := x4 - v1 := x3 -} - -function u256_to_i64(x1, x2, x3, x4) -> v { - if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } - v := x4 -} - -function u256_to_i32(x1, x2, x3, x4) -> v:i32 { - if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } - if i64.ne(0, i64.shr_u(x4, 32)) { invalid() } - v := i32.wrap_i64(x4) -} - -function u256_to_byte(x1, x2, x3, x4) -> v { - if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } - if i64.gt_u(x4, 255) { invalid() } - v := x4 -} - -function u256_to_i32ptr(x1, x2, x3, x4) -> v:i32 { - v := u256_to_i32(x1, x2, x3, x4) -} - -function to_internal_i32ptr(x1, x2, x3, x4) -> r:i32 { - let p:i32 := u256_to_i32ptr(x1, x2, x3, x4) - r := i32.add(p, 64:i32) - if i32.lt_u(r, p) { invalid() } -} - -function u256_to_address(x1, x2, x3, x4) -> r1, r2, r3 { - if i64.ne(0, x1) { invalid() } - if i64.ne(0, i64.shr_u(x2, 32)) { invalid() } - r1 := x2 - r2 := x3 - r3 := x4 -} - -function keccak256(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() -} - -function address() -> z1, z2, z3, z4 { - eth.getAddress(0:i32) - z1, z2, z3, z4 := mload_internal(0:i32) -} -function balance(x1, x2, x3, x4) -> z1, z2, z3, z4 { - mstore_address(0:i32, x1, x2, x3, x4) - eth.getExternalBalance(12:i32, 48:i32) - z1, z2, z3, z4 := mload_internal(32:i32) -} -function selfbalance() -> z1, z2, z3, z4 { - // TODO: not part of current Ewasm spec - unreachable() -} -function chainid() -> z1, z2, z3, z4 { - // TODO: not part of current Ewasm spec - unreachable() -} -function origin() -> z1, z2, z3, z4 { - eth.getTxOrigin(0:i32) - z1, z2, z3, z4 := mload_internal(0:i32) -} -function caller() -> z1, z2, z3, z4 { - eth.getCaller(0:i32) - z1, z2, z3, z4 := mload_internal(0:i32) -} -function callvalue() -> z1, z2, z3, z4 { - eth.getCallValue(0:i32) - z1, z2, z3, z4 := mload_internal(0:i32) -} -function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 { - eth.callDataCopy(0:i32, u256_to_i32(x1, x2, x3, x4), 32:i32) - z1, z2, z3, z4 := mload_internal(0:i32) -} -function calldatasize() -> z1, z2, z3, z4 { - z4 := i64.extend_i32_u(eth.getCallDataSize()) -} -function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { - eth.callDataCopy( - to_internal_i32ptr(x1, x2, x3, x4), - u256_to_i32(y1, y2, y3, y4), - u256_to_i32(z1, z2, z3, z4) - ) -} - -// Needed? -function codesize() -> z1, z2, z3, z4 { - z4 := i64.extend_i32_u(eth.getCodeSize()) -} -function codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { - eth.codeCopy( - to_internal_i32ptr(x1, x2, x3, x4), - u256_to_i32(y1, y2, y3, y4), - u256_to_i32(z1, z2, z3, z4) - ) -} -function datacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { - // TODO correct? - codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) -} - -function gasprice() -> z1, z2, z3, z4 { - eth.getTxGasPrice(0:i32) - z1, z2, z3, z4 := mload_internal(0:i32) -} -function extcodesize_internal(x1, x2, x3, x4) -> r:i32 { - mstore_address(0:i32, x1, x2, x3, x4) - r := eth.getExternalCodeSize(12:i32) -} -function extcodesize(x1, x2, x3, x4) -> z1, z2, z3, z4 { - z4 := i64.extend_i32_u(extcodesize_internal(x1, x2, x3, x4)) -} -function extcodehash(x1, x2, x3, x4) -> z1, z2, z3, z4 { - // TODO: not part of current Ewasm spec - unreachable() -} -function extcodecopy(a1, a2, a3, a4, p1, p2, p3, p4, o1, o2, o3, o4, l1, l2, l3, l4) { - mstore_address(0:i32, a1, a2, a3, a4) - let codeOffset:i32 := u256_to_i32(o1, o2, o3, o4) - let codeLength:i32 := u256_to_i32(l1, l2, l3, l4) - eth.externalCodeCopy(12:i32, to_internal_i32ptr(p1, p2, p3, p4), codeOffset, codeLength) -} - -function returndatasize() -> z1, z2, z3, z4 { - z4 := i64.extend_i32_u(eth.getReturnDataSize()) -} -function returndatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { - eth.returnDataCopy( - to_internal_i32ptr(x1, x2, x3, x4), - u256_to_i32(y1, y2, y3, y4), - u256_to_i32(z1, z2, z3, z4) - ) -} - -function blockhash(x1, x2, x3, x4) -> z1, z2, z3, z4 { - let r:i32 := eth.getBlockHash(u256_to_i64(x1, x2, x3, x4), 0:i32) - if i32.eqz(r) { - z1, z2, z3, z4 := mload_internal(0:i32) - } -} -function coinbase() -> z1, z2, z3, z4 { - eth.getBlockCoinbase(0:i32) - z1, z2, z3, z4 := mload_internal(0:i32) -} -function timestamp() -> z1, z2, z3, z4 { - z4 := eth.getBlockTimestamp() -} -function number() -> z1, z2, z3, z4 { - z4 := eth.getBlockNumber() -} -function difficulty() -> z1, z2, z3, z4 { - eth.getBlockDifficulty(0:i32) - z1, z2, z3, z4 := mload_internal(0:i32) -} -function gaslimit() -> z1, z2, z3, z4 { - z4 := eth.getBlockGasLimit() -} - -function pop(x1, x2, x3, x4) { -} - - -function endian_swap_16(x) -> y { - let hi := i64.and(i64.shl(x, 8), 0xff00) - let lo := i64.and(i64.shr_u(x, 8), 0xff) - y := i64.or(hi, lo) -} - -function endian_swap_32(x) -> y { - let hi := i64.shl(endian_swap_16(x), 16) - let lo := endian_swap_16(i64.shr_u(x, 16)) - y := i64.or(hi, lo) -} - -function endian_swap(x) -> y { - let hi := i64.shl(endian_swap_32(x), 32) - let lo := endian_swap_32(i64.shr_u(x, 32)) - y := i64.or(hi, lo) -} -function save_temp_mem_32() -> t1, t2, t3, t4 { - t1 := i64.load(0:i32) - t2 := i64.load(8:i32) - t3 := i64.load(16:i32) - t4 := i64.load(24:i32) -} -function restore_temp_mem_32(t1, t2, t3, t4) { - i64.store(0:i32, t1) - i64.store(8:i32, t2) - i64.store(16:i32, t3) - i64.store(24:i32, t4) -} -function save_temp_mem_64() -> t1, t2, t3, t4, t5, t6, t7, t8 { - t1 := i64.load(0:i32) - t2 := i64.load(8:i32) - t3 := i64.load(16:i32) - t4 := i64.load(24:i32) - t5 := i64.load(32:i32) - t6 := i64.load(40:i32) - t7 := i64.load(48:i32) - t8 := i64.load(54:i32) -} -function restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8) { - i64.store(0:i32, t1) - i64.store(8:i32, t2) - i64.store(16:i32, t3) - i64.store(24:i32, t4) - i64.store(32:i32, t5) - i64.store(40:i32, t6) - i64.store(48:i32, t7) - i64.store(54:i32, t8) -} -function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 { - z1, z2, z3, z4 := mload_internal(to_internal_i32ptr(x1, x2, x3, x4)) -} -function mload_internal(pos:i32) -> z1, z2, z3, z4 { - z1 := endian_swap(i64.load(pos)) - z2 := endian_swap(i64.load(i32.add(pos, 8:i32))) - z3 := endian_swap(i64.load(i32.add(pos, 16:i32))) - z4 := endian_swap(i64.load(i32.add(pos, 24:i32))) -} -function mstore(x1, x2, x3, x4, y1, y2, y3, y4) { - mstore_internal(to_internal_i32ptr(x1, x2, x3, x4), y1, y2, y3, y4) -} -function mstore_internal(pos:i32, y1, y2, y3, y4) { - i64.store(pos, endian_swap(y1)) - i64.store(i32.add(pos, 8:i32), endian_swap(y2)) - i64.store(i32.add(pos, 16:i32), endian_swap(y3)) - i64.store(i32.add(pos, 24:i32), endian_swap(y4)) -} -function mstore_address(pos:i32, a1, a2, a3, a4) { - a1, a2, a3 := u256_to_address(a1, a2, a3, a4) - mstore_internal(pos, 0, a1, a2, a3) -} -function mstore8(x1, x2, x3, x4, y1, y2, y3, y4) { - let v := u256_to_byte(y1, y2, y3, y4) - i64.store8(to_internal_i32ptr(x1, x2, x3, x4), v) -} -// Needed? -function msize() -> z1, z2, z3, z4 { - // TODO implement - unreachable() -} -function sload(x1, x2, x3, x4) -> z1, z2, z3, z4 { - mstore_internal(0:i32, x1, x2, x3, x4) - eth.storageLoad(0:i32, 32:i32) - z1, z2, z3, z4 := mload_internal(32:i32) -} - -function sstore(x1, x2, x3, x4, y1, y2, y3, y4) { - mstore_internal(0:i32, x1, x2, x3, x4) - mstore_internal(32:i32, y1, y2, y3, y4) - eth.storageStore(0:i32, 32:i32) -} - -function gas() -> z1, z2, z3, z4 { - z4 := eth.getGasLeft() -} - -function log0(p1, p2, p3, p4, s1, s2, s3, s4) { - eth.log( - to_internal_i32ptr(p1, p2, p3, p4), - u256_to_i32(s1, s2, s3, s4), - 0:i32, 0:i32, 0:i32, 0:i32, 0:i32 - ) -} -function log1( - p1, p2, p3, p4, s1, s2, s3, s4, - t1_1, t1_2, t1_3, t1_4 -) { - eth.log( - to_internal_i32ptr(p1, p2, p3, p4), - u256_to_i32(s1, s2, s3, s4), - 1:i32, - to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), - 0:i32, 0:i32, 0:i32 - ) -} -function log2( - p1, p2, p3, p4, s1, s2, s3, s4, - t1_1, t1_2, t1_3, t1_4, - t2_1, t2_2, t2_3, t2_4 -) { - eth.log( - to_internal_i32ptr(p1, p2, p3, p4), - u256_to_i32(s1, s2, s3, s4), - 2:i32, - to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), - to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), - 0:i32, 0:i32 - ) -} -function log3( - p1, p2, p3, p4, s1, s2, s3, s4, - t1_1, t1_2, t1_3, t1_4, - t2_1, t2_2, t2_3, t2_4, - t3_1, t3_2, t3_3, t3_4 -) { - eth.log( - to_internal_i32ptr(p1, p2, p3, p4), - u256_to_i32(s1, s2, s3, s4), - 3:i32, - to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), - to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), - to_internal_i32ptr(t3_1, t3_2, t3_3, t3_4), - 0:i32 - ) -} -function log4( - p1, p2, p3, p4, s1, s2, s3, s4, - t1_1, t1_2, t1_3, t1_4, - t2_1, t2_2, t2_3, t2_4, - t3_1, t3_2, t3_3, t3_4, - t4_1, t4_2, t4_3, t4_4, -) { - eth.log( - to_internal_i32ptr(p1, p2, p3, p4), - u256_to_i32(s1, s2, s3, s4), - 4:i32, - to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), - to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), - to_internal_i32ptr(t3_1, t3_2, t3_3, t3_4), - to_internal_i32ptr(t4_1, t4_2, t4_3, t4_4) - ) -} - -function create( - x1, x2, x3, x4, - y1, y2, y3, y4, - z1, z2, z3, z4 -) -> a1, a2, a3, a4 { - let v1, v2 := u256_to_u128(x1, x2, x3, x4) - mstore_internal(0:i32, 0, 0, v1, v2) - - let r:i32 := eth.create(0:i32, to_internal_i32ptr(y1, y2, y3, y4), u256_to_i32(z1, z2, z3, z4), 32:i32) - if i32.eqz(r) { - a1, a2, a3, a4 := mload_internal(32:i32) - } -} -function call( - a1, a2, a3, a4, - b1, b2, b3, b4, - c1, c2, c3, c4, - d1, d2, d3, d4, - e1, e2, e3, e4, - f1, f2, f3, f4, - g1, g2, g3, g4 -) -> x1, x2, x3, x4 { - let g := u256_to_i64(a1, a2, a3, a4) - mstore_address(0:i32, b1, b2, b3, b4) - - let v1, v2 := u256_to_u128(c1, c2, c3, c4) - mstore_internal(32:i32, 0, 0, v1, v2) - - x4 := i64.extend_i32_u(eth.call(g, 12:i32, 32:i32, to_internal_i32ptr(d1, d2, d3, d4), u256_to_i32(e1, e2, e3, e4))) -} -function callcode( - a1, a2, a3, a4, - b1, b2, b3, b4, - c1, c2, c3, c4, - d1, d2, d3, d4, - e1, e2, e3, e4, - f1, f2, f3, f4, - g1, g2, g3, g4 -) -> x1, x2, x3, x4 { - mstore_address(0:i32, b1, b2, b3, b4) - - let v1, v2 := u256_to_u128(c1, c2, c3, c4) - mstore_internal(32:i32, 0, 0, v1, v2) - - x4 := i64.extend_i32_u(eth.callCode( - u256_to_i64(a1, a2, a3, a4), - 12:i32, - 32:i32, - to_internal_i32ptr(d1, d2, d3, d4), - u256_to_i32(e1, e2, e3, e4) - )) -} -function delegatecall( - a1, a2, a3, a4, - b1, b2, b3, b4, - c1, c2, c3, c4, - d1, d2, d3, d4, - e1, e2, e3, e4, - f1, f2, f3, f4 -) -> x1, x2, x3, x4 { - mstore_address(0:i32, b1, b2, b3, b4) - - x4 := i64.extend_i32_u(eth.callDelegate( - u256_to_i64(a1, a2, a3, a4), - 12:i32, - to_internal_i32ptr(c1, c2, c3, c4), - u256_to_i32(d1, d2, d3, d4) - )) -} -function staticcall( - a1, a2, a3, a4, - b1, b2, b3, b4, - c1, c2, c3, c4, - d1, d2, d3, d4, - e1, e2, e3, e4, - f1, f2, f3, f4 -) -> x1, x2, x3, x4 { - mstore_address(0:i32, b1, b2, b3, b4) - - x4 := i64.extend_i32_u(eth.callStatic( - u256_to_i64(a1, a2, a3, a4), - 12:i32, - to_internal_i32ptr(c1, c2, c3, c4), - u256_to_i32(d1, d2, d3, d4) - )) -} -function create2( - a1, a2, a3, a4, - b1, b2, b3, b4, - c1, c2, c3, c4, - d1, d2, d3, d4 -) -> x1, x2, x3, x4 { - // TODO: not part of current Ewasm spec - unreachable() -} -function selfdestruct(a1, a2, a3, a4) { - mstore_address(0:i32, a1, a2, a3, a4) - // In EVM, addresses are padded to 32 bytes, so discard the first 12. - eth.selfDestruct(12:i32) -} - -function return(x1, x2, x3, x4, y1, y2, y3, y4) { - eth.finish( - to_internal_i32ptr(x1, x2, x3, x4), - u256_to_i32(y1, y2, y3, y4) - ) -} -function revert(x1, x2, x3, x4, y1, y2, y3, y4) { - eth.revert( - to_internal_i32ptr(x1, x2, x3, x4), - u256_to_i32(y1, y2, y3, y4) - ) -} -function invalid() { - unreachable() -} -function stop() { - eth.finish(0:i32, 0:i32) -} -function memoryguard(x:i64) -> y1, y2, y3, y4 { - y4 := x -} -} -)"}; - -} - Object EVMToEwasmTranslator::run(Object const& _object) { if (!m_polyfill) @@ -1278,7 +115,17 @@ void EVMToEwasmTranslator::parsePolyfill() { ErrorList errors; ErrorReporter errorReporter(errors); - shared_ptr scanner{make_shared(CharStream(polyfill, ""))}; + shared_ptr scanner{make_shared(CharStream( + "{" + + string(solidity::yul::wasm::polyfill::Arithmetic) + + string(solidity::yul::wasm::polyfill::Bitwise) + + string(solidity::yul::wasm::polyfill::Comparison) + + string(solidity::yul::wasm::polyfill::Conversion) + + string(solidity::yul::wasm::polyfill::Interface) + + string(solidity::yul::wasm::polyfill::Keccak) + + string(solidity::yul::wasm::polyfill::Logical) + + string(solidity::yul::wasm::polyfill::Memory) + + "}", ""))}; m_polyfill = Parser(errorReporter, WasmDialect::instance()).parse(scanner, false); if (!errors.empty()) { diff --git a/libyul/backends/wasm/polyfill/Arithmetic.yul b/libyul/backends/wasm/polyfill/Arithmetic.yul new file mode 100644 index 000000000..4a0e32bf3 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Arithmetic.yul @@ -0,0 +1,396 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Arithmetic.h`. + +// returns a + y + c plus carry value on 64 bit values. +// c should be at most 1 +function add_carry(x, y, c) -> r, r_c { + let t := i64.add(x, y) + r := i64.add(t, c) + r_c := i64.extend_i32_u(i32.or( + i64.lt_u(t, x), + i64.lt_u(r, t) + )) +} + +function add(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + let carry + r4, carry := add_carry(x4, y4, 0) + r3, carry := add_carry(x3, y3, carry) + r2, carry := add_carry(x2, y2, carry) + r1, carry := add_carry(x1, y1, carry) +} + +function sub(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // x - y = x + (~y + 1) + let carry + r4, carry := add_carry(x4, bit_negate(y4), 1) + r3, carry := add_carry(x3, bit_negate(y3), carry) + r2, carry := add_carry(x2, bit_negate(y2), carry) + r1, carry := add_carry(x1, bit_negate(y1), carry) +} + +function sub320(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> r1, r2, r3, r4, r5 { + // x - y = x + (~y + 1) + let carry + r5, carry := add_carry(x5, bit_negate(y5), 1) + r4, carry := add_carry(x4, bit_negate(y4), carry) + r3, carry := add_carry(x3, bit_negate(y3), carry) + r2, carry := add_carry(x2, bit_negate(y2), carry) + r1, carry := add_carry(x1, bit_negate(y1), carry) +} + +function sub512(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // x - y = x + (~y + 1) + let carry + r8, carry := add_carry(x8, bit_negate(y8), 1) + r7, carry := add_carry(x7, bit_negate(y7), carry) + r6, carry := add_carry(x6, bit_negate(y6), carry) + r5, carry := add_carry(x5, bit_negate(y5), carry) + r4, carry := add_carry(x4, bit_negate(y4), carry) + r3, carry := add_carry(x3, bit_negate(y3), carry) + r2, carry := add_carry(x2, bit_negate(y2), carry) + r1, carry := add_carry(x1, bit_negate(y1), carry) +} + +// Multiplies two 64 bit values resulting in a 128 bit +// value split into two 64 bit values. +function mul_64x64_128(x, y) -> hi, lo { + let xh, xl := split(x) + let yh, yl := split(y) + let t0 := i64.mul(xl, yl) + let t1 := i64.mul(xh, yl) + let t2 := i64.mul(xl, yh) + let t3 := i64.mul(xh, yh) + let t0h, t0l := split(t0) + let u1 := i64.add(t1, t0h) + let u1h, u1l := split(u1) + let u2 := i64.add(t2, u1l) + lo := i64.or(i64.shl(u2, 32), t0l) + hi := i64.add(t3, i64.add(i64.shr_u(u2, 32), u1h)) +} + +// Multiplies two 128 bit values resulting in a 256 bit +// value split into four 64 bit values. +function mul_128x128_256(x1, x2, y1, y2) -> r1, r2, r3, r4 { + let ah, al := mul_64x64_128(x1, y1) + let bh, bl := mul_64x64_128(x1, y2) + let ch, cl := mul_64x64_128(x2, y1) + let dh, dl := mul_64x64_128(x2, y2) + r4 := dl + let carry1, carry2 + let t1, t2 + r3, carry1 := add_carry(bl, cl, 0) + r3, carry2 := add_carry(r3, dh, 0) + t1, carry1 := add_carry(bh, ch, carry1) + r2, carry2 := add_carry(t1, al, carry2) + r1 := i64.add(i64.add(ah, carry1), carry2) +} + +// Multiplies two 256 bit values resulting in a 512 bit +// value split into eight 64 bit values. +function mul_256x256_512(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4, r5, r6, r7, r8 { + let a1, a2, a3, a4 := mul_128x128_256(x1, x2, y1, y2) + let b1, b2, b3, b4 := mul_128x128_256(x1, x2, y3, y4) + let c1, c2, c3, c4 := mul_128x128_256(x3, x4, y1, y2) + let d1, d2, d3, d4 := mul_128x128_256(x3, x4, y3, y4) + r8 := d4 + r7 := d3 + let carry1, carry2 + let t1, t2 + r6, carry1 := add_carry(b4, c4, 0) + r6, carry2 := add_carry(r6, d2, 0) + r5, carry1 := add_carry(b3, c3, carry1) + r5, carry2 := add_carry(r5, d1, carry2) + r4, carry1 := add_carry(a4, b2, carry1) + r4, carry2 := add_carry(r4, c2, carry2) + r3, carry1 := add_carry(a3, b1, carry1) + r3, carry2 := add_carry(r3, c1, carry2) + r2, carry1 := add_carry(a2, carry1, carry2) + r1 := i64.add(a1, carry1) +} + +function mul(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // TODO it would actually suffice to have mul_128x128_128 for the first two. + let b1, b2, b3, b4 := mul_128x128_256(x3, x4, y1, y2) + let c1, c2, c3, c4 := mul_128x128_256(x1, x2, y3, y4) + let d1, d2, d3, d4 := mul_128x128_256(x3, x4, y3, y4) + r4 := d4 + r3 := d3 + let t1, t2 + t1, t2, r1, r2 := add(0, 0, b3, b4, 0, 0, c3, c4) + t1, t2, r1, r2 := add(0, 0, r1, r2, 0, 0, d1, d2) +} + +function div(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/DIV.wast + if iszero256(y1, y2, y3, y4) { + leave + } + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 1 + + for {} true {} { + if i32.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, x1, x2, x3, x4)) { + break + } + y1, y2, y3, y4 := shl_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shl_internal(1, m1, m2, m3, m4) + } + + for {} or_bool(m1, m2, m3, m4) {} { + if gte_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) { + x1, x2, x3, x4 := sub(x1, x2, x3, x4, y1, y2, y3, y4) + r1, r2, r3, r4 := add(r1, r2, r3, r4, m1, m2, m3, m4) + } + y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shr_internal(1, m1, m2, m3, m4) + } +} + +function sdiv(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/SDIV.wast + + let sign:i32 := i32.wrap_i64(i64.shr_u(i64.xor(x1, y1), 63)) + + if i64.eqz(i64.clz(x1)) { + x1, x2, x3, x4 := xor( + x1, x2, x3, x4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + x1, x2, x3, x4 := add(x1, x2, x3, x4, 0, 0, 0, 1) + } + + if i64.eqz(i64.clz(y1)) { + y1, y2, y3, y4 := xor( + y1, y2, y3, y4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + y1, y2, y3, y4 := add(y1, y2, y3, y4, 0, 0, 0, 1) + } + + r1, r2, r3, r4 := div(x1, x2, x3, x4, y1, y2, y3, y4) + + if sign { + r1, r2, r3, r4 := xor( + r1, r2, r3, r4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + r1, r2, r3, r4 := add(r1, r2, r3, r4, 0, 0, 0, 1) + } +} + +function mod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/MOD.wast + if iszero256(y1, y2, y3, y4) { + leave + } + + r1 := x1 + r2 := x2 + r3 := x3 + r4 := x4 + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 1 + + for {} true {} { + if i32.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, r1, r2, r3, r4)) { + break + } + + y1, y2, y3, y4 := shl_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shl_internal(1, m1, m2, m3, m4) + } + + for {} or_bool(m1, m2, m3, m4) {} { + if gte_256x256_64(r1, r2, r3, r4, y1, y2, y3, y4) { + r1, r2, r3, r4 := sub(r1, r2, r3, r4, y1, y2, y3, y4) + } + + y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shr_internal(1, m1, m2, m3, m4) + } +} + +function mod320(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> r1, r2, r3, r4, r5 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/mod_320.wast + if iszero320(y1, y2, y3, y4, y5) { + leave + } + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 0 + let m5 := 1 + + r1 := x1 + r2 := x2 + r3 := x3 + r4 := x4 + r5 := x5 + + for {} true {} { + if i32.or(i64.eqz(i64.clz(y1)), gte_320x320_64(y1, y2, y3, y4, y5, r1, r2, r3, r4, r5)) { + break + } + y1, y2, y3, y4, y5 := shl320_internal(1, y1, y2, y3, y4, y5) + m1, m2, m3, m4, m5 := shl320_internal(1, m1, m2, m3, m4, m5) + } + + for {} or_bool_320(m1, m2, m3, m4, m5) {} { + if gte_320x320_64(r1, r2, r3, r4, r5, y1, y2, y3, y4, y5) { + r1, r2, r3, r4, r5 := sub320(r1, r2, r3, r4, r5, y1, y2, y3, y4, y5) + } + + y1, y2, y3, y4, y5 := shr320_internal(1, y1, y2, y3, y4, y5) + m1, m2, m3, m4, m5 := shr320_internal(1, m1, m2, m3, m4, m5) + } +} + +function mod512(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/mod_512.wast + if iszero512(y1, y2, y3, y4, y5, y6, y7, y8) { + leave + } + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 0 + let m5 := 0 + let m6 := 0 + let m7 := 0 + let m8 := 1 + + r1 := x1 + r2 := x2 + r3 := x3 + r4 := x4 + r5 := x5 + r6 := x6 + r7 := x7 + r8 := x8 + + for {} true {} { + if i32.or( + i64.eqz(i64.clz(y1)), + gte_512x512_64(y1, y2, y3, y4, y5, y6, y7, y8, r1, r2, r3, r4, r5, r6, r7, r8) + ) + { + break + } + y1, y2, y3, y4, y5, y6, y7, y8 := shl512_internal(1, y1, y2, y3, y4, y5, y6, y7, y8) + m1, m2, m3, m4, m5, m6, m7, m8 := shl512_internal(1, m1, m2, m3, m4, m5, m6, m7, m8) + } + + for {} or_bool_512(m1, m2, m3, m4, m5, m6, m7, m8) {} { + if gte_512x512_64(r1, r2, r3, r4, r5, r6, r7, r8, y1, y2, y3, y4, y5, y6, y7, y8) { + r1, r2, r3, r4, r5, r6, r7, r8 := sub512(r1, r2, r3, r4, r5, r6, r7, r8, y1, y2, y3, y4, y5, y6, y7, y8) + } + + y1, y2, y3, y4, y5, y6, y7, y8 := shr512_internal(1, y1, y2, y3, y4, y5, y6, y7, y8) + m1, m2, m3, m4, m5, m6, m7, m8 := shr512_internal(1, m1, m2, m3, m4, m5, m6, m7, m8) + } +} + +function smod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/SMOD.wast + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 1 + + let sign:i32 := i32.wrap_i64(i64.shr_u(x1, 63)) + + if i64.eqz(i64.clz(x1)) { + x1, x2, x3, x4 := xor( + x1, x2, x3, x4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + x1, x2, x3, x4 := add(x1, x2, x3, x4, 0, 0, 0, 1) + } + + if i64.eqz(i64.clz(y1)) { + y1, y2, y3, y4 := xor( + y1, y2, y3, y4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + y1, y2, y3, y4 := add(y1, y2, y3, y4, 0, 0, 0, 1) + } + + r1, r2, r3, r4 := mod(x1, x2, x3, x4, y1, y2, y3, y4) + + if sign { + r1, r2, r3, r4 := xor( + r1, r2, r3, r4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + r1, r2, r3, r4 := add(r1, r2, r3, r4, 0, 0, 0, 1) + } +} + +function exp(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + r4 := 1 + for {} or_bool(y1, y2, y3, y4) {} { + if i32.wrap_i64(i64.and(y4, 1)) { + r1, r2, r3, r4 := mul(r1, r2, r3, r4, x1, x2, x3, x4) + } + x1, x2, x3, x4 := mul(x1, x2, x3, x4, x1, x2, x3, x4) + y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) + } +} + +function addmod(x1, x2, x3, x4, y1, y2, y3, y4, m1, m2, m3, m4) -> z1, z2, z3, z4 { + let carry + z4, carry := add_carry(x4, y4, 0) + z3, carry := add_carry(x3, y3, carry) + z2, carry := add_carry(x2, y2, carry) + z1, carry := add_carry(x1, y1, carry) + + let z0 + z0, z1, z2, z3, z4 := mod320(carry, z1, z2, z3, z4, 0, m1, m2, m3, m4) +} + +function mulmod(x1, x2, x3, x4, y1, y2, y3, y4, m1, m2, m3, m4) -> z1, z2, z3, z4 { + let r1, r2, r3, r4, r5, r6, r7, r8 := mul_256x256_512(x1, x2, x3, x4, y1, y2, y3, y4) + let t1 + let t2 + let t3 + let t4 + t1, t2, t3, t4, z1, z2, z3, z4 := mod512(r1, r2, r3, r4, r5, r6, r7, r8, 0, 0, 0, 0, m1, m2, m3, m4) +} + +function signextend(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + z1 := y1 + z2 := y2 + z3 := y3 + z4 := y4 + if lt_256x256_64(x1, x2, x3, x4, 0, 0, 0, 32) { + let d := i64.mul(i64.sub(31, x4), 8) + z1, z2, z3, z4 := shl(0, 0, 0, d, z1, z2, z3, z4) + z1, z2, z3, z4 := sar(0, 0, 0, d, z1, z2, z3, z4) + } +} diff --git a/libyul/backends/wasm/polyfill/Bitwise.yul b/libyul/backends/wasm/polyfill/Bitwise.yul new file mode 100644 index 000000000..3efa07bf5 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Bitwise.yul @@ -0,0 +1,222 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Bitwise.h`. + +function bit_negate(x) -> y { + y := i64.xor(x, 0xffffffffffffffff) +} + +function split(x) -> hi, lo { + hi := i64.shr_u(x, 32) + lo := i64.and(x, 0xffffffff) +} + +function shl_internal(amount, x1, x2, x3, x4) -> r1, r2, r3, r4 { + // amount < 64 + r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) + r4 := i64.shl(x4, amount) +} + +function shr_internal(amount, x1, x2, x3, x4) -> r1, r2, r3, r4 { + // amount < 64 + r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) + r1 := i64.shr_u(x1, amount) +} + +function shl320_internal(amount, x1, x2, x3, x4, x5) -> r1, r2, r3, r4, r5 { + // amount < 64 + r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shl(x4, amount), i64.shr_u(x5, i64.sub(64, amount))) + r5 := i64.shl(x5, 1) +} + +function shr320_internal(amount, x1, x2, x3, x4, x5) -> r1, r2, r3, r4, r5 { + // amount < 64 + r5 := i64.add(i64.shr_u(x5, amount), i64.shl(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) + r1 := i64.shr_u(x1, 1) +} + +function shl512_internal(amount, x1, x2, x3, x4, x5, x6, x7, x8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // amount < 64 + r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shl(x4, amount), i64.shr_u(x5, i64.sub(64, amount))) + r5 := i64.add(i64.shl(x5, amount), i64.shr_u(x6, i64.sub(64, amount))) + r6 := i64.add(i64.shl(x6, amount), i64.shr_u(x7, i64.sub(64, amount))) + r7 := i64.add(i64.shl(x7, amount), i64.shr_u(x8, i64.sub(64, amount))) + r8 := i64.shl(x8, amount) +} + +function shr512_internal(amount, x1, x2, x3, x4, x5, x6, x7, x8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // amount < 64 + r8 := i64.add(i64.shr_u(x8, amount), i64.shl(x7, i64.sub(64, amount))) + r7 := i64.add(i64.shr_u(x7, amount), i64.shl(x6, i64.sub(64, amount))) + r6 := i64.add(i64.shr_u(x6, amount), i64.shl(x5, i64.sub(64, amount))) + r5 := i64.add(i64.shr_u(x5, amount), i64.shl(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) + r1 := i64.shr_u(x1, amount) +} + +function byte(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + if i64.eqz(i64.or(i64.or(x1, x2), x3)) { + let component + switch i64.div_u(x4, 8) + case 0 { component := y1 } + case 1 { component := y2 } + case 2 { component := y3 } + case 3 { component := y4 } + x4 := i64.mul(i64.rem_u(x4, 8), 8) + r4 := i64.shr_u(component, i64.sub(56, x4)) + r4 := i64.and(0xff, r4) + } +} + +function xor(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + r1 := i64.xor(x1, y1) + r2 := i64.xor(x2, y2) + r3 := i64.xor(x3, y3) + r4 := i64.xor(x4, y4) +} + +function or(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + r1 := i64.or(x1, y1) + r2 := i64.or(x2, y2) + r3 := i64.or(x3, y3) + r4 := i64.or(x4, y4) +} + +function and(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + r1 := i64.and(x1, y1) + r2 := i64.and(x2, y2) + r3 := i64.and(x3, y3) + r4 := i64.and(x4, y4) +} + +function not(x1, x2, x3, x4) -> r1, r2, r3, r4 { + let mask := 0xffffffffffffffff + r1, r2, r3, r4 := xor(x1, x2, x3, x4, mask, mask, mask, mask) +} + +function shl_single(a, amount) -> x, y { + // amount < 64 + x := i64.shr_u(a, i64.sub(64, amount)) + y := i64.shl(a, amount) +} + +function shl(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + if i32.and(i64.eqz(x1), i64.eqz(x2)) { + if i64.eqz(x3) { + if i64.lt_u(x4, 256) { + if i64.ge_u(x4, 128) { + y1 := y3 + y2 := y4 + y3 := 0 + y4 := 0 + x4 := i64.sub(x4, 128) + } + if i64.ge_u(x4, 64) { + y1 := y2 + y2 := y3 + y3 := y4 + y4 := 0 + x4 := i64.sub(x4, 64) + } + let t, r + t, z4 := shl_single(y4, x4) + r, z3 := shl_single(y3, x4) + z3 := i64.or(z3, t) + t, z2 := shl_single(y2, x4) + z2 := i64.or(z2, r) + r, z1 := shl_single(y1, x4) + z1 := i64.or(z1, t) + } + } + } +} + +function shr_single(a, amount) -> x, y { + // amount < 64 + y := i64.shl(a, i64.sub(64, amount)) + x := i64.shr_u(a, amount) +} + +function shr(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + if i32.and(i64.eqz(x1), i64.eqz(x2)) { + if i64.eqz(x3) { + if i64.lt_u(x4, 256) { + if i64.ge_u(x4, 128) { + y4 := y2 + y3 := y1 + y2 := 0 + y1 := 0 + x4 := i64.sub(x4, 128) + } + if i64.ge_u(x4, 64) { + y4 := y3 + y3 := y2 + y2 := y1 + y1 := 0 + x4 := i64.sub(x4, 64) + } + let t + z4, t := shr_single(y4, x4) + z3, t := shr_single(y3, x4) + z4 := i64.or(z4, t) + z2, t := shr_single(y2, x4) + z3 := i64.or(z3, t) + z1, t := shr_single(y1, x4) + z2 := i64.or(z2, t) + } + } + } +} + +function sar(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + if i64.gt_u(i64.clz(y1), 0) { + z1, z2, z3, z4 := shr(x1, x2, x3, x4, y1, y2, y3, y4) + leave + } + + if gte_256x256_64(x1, x2, x3, x4, 0, 0, 0, 256) { + z1 := 0xffffffffffffffff + z2 := 0xffffffffffffffff + z3 := 0xffffffffffffffff + z4 := 0xffffffffffffffff + } + if lt_256x256_64(x1, x2, x3, x4, 0, 0, 0, 256) { + y1, y2, y3, y4 := shr(0, 0, 0, x4, y1, y2, y3, y4) + z1, z2, z3, z4 := shl( + 0, 0, 0, i64.sub(256, x4), + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + z1, z2, z3, z4 := or(y1, y2, y3, y4, z1, z2, z3, z4) + } +} diff --git a/libyul/backends/wasm/polyfill/Comparison.yul b/libyul/backends/wasm/polyfill/Comparison.yul new file mode 100644 index 000000000..6f08c5b18 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Comparison.yul @@ -0,0 +1,169 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Comparison.h`. + +function iszero(x1, x2, x3, x4) -> r1, r2, r3, r4 { + r4 := i64.extend_i32_u(iszero256(x1, x2, x3, x4)) +} + +function iszero256(x1, x2, x3, x4) -> r:i32 { + r := i64.eqz(i64.or(i64.or(x1, x2), i64.or(x3, x4))) +} + +function iszero320(x1, x2, x3, x4, x5) -> r:i32 { + r := i64.eqz(i64.or(i64.or(i64.or(x1, x2), i64.or(x3, x4)), x5)) +} + +function iszero512(x1, x2, x3, x4, x5, x6, x7, x8) -> r:i32 { + r := i32.and(iszero256(x1, x2, x3, x4), iszero256(x5, x6, x7, x8)) +} + +function eq(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + if i64.eq(x1, y1) { + if i64.eq(x2, y2) { + if i64.eq(x3, y3) { + if i64.eq(x4, y4) { + r4 := 1 + } + } + } + } +} + +// returns 0 if a == b, -1 if a < b and 1 if a > b +function cmp(a, b) -> r:i32 { + switch i64.lt_u(a, b) + case 1:i32 { r := 0xffffffff:i32 } + default { + r := i64.ne(a, b) + } +} + +function lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z:i32 { + switch cmp(x1, y1) + case 0:i32 { + switch cmp(x2, y2) + case 0:i32 { + switch cmp(x3, y3) + case 0:i32 { + switch cmp(x4, y4) + case 0:i32 { + z := i64.lt_u(x5, y5) + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } +} + +function lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z:i32 { + switch cmp(x1, y1) + case 0:i32 { + switch cmp(x2, y2) + case 0:i32 { + switch cmp(x3, y3) + case 0:i32 { + switch cmp(x4, y4) + case 0:i32 { + switch cmp(x5, y5) + case 0:i32 { + switch cmp(x6, y6) + case 0:i32 { + switch cmp(x7, y7) + case 0:i32 { + z := i64.lt_u(x8, y8) + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } +} + +function lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z:i32 { + switch cmp(x1, y1) + case 0:i32 { + switch cmp(x2, y2) + case 0:i32 { + switch cmp(x3, y3) + case 0:i32 { + z := i64.lt_u(x4, y4) + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } +} + +function lt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + z4 := i64.extend_i32_u(lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4)) +} + +function gte_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z:i32 { + z := i32.eqz(lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4)) +} + +function gte_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z:i32 { + z := i32.eqz(lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5)) +} + +function gte_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z:i32 { + z := i32.eqz(lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8)) +} + +function gt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + z1, z2, z3, z4 := lt(y1, y2, y3, y4, x1, x2, x3, x4) +} + +function slt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + // TODO correct? + x1 := i64.add(x1, 0x8000000000000000) + y1 := i64.add(y1, 0x8000000000000000) + z1, z2, z3, z4 := lt(x1, x2, x3, x4, y1, y2, y3, y4) +} + +function sgt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + z1, z2, z3, z4 := slt(y1, y2, y3, y4, x1, x2, x3, x4) +} diff --git a/libyul/backends/wasm/polyfill/Conversion.yul b/libyul/backends/wasm/polyfill/Conversion.yul new file mode 100644 index 000000000..eef8173c2 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Conversion.yul @@ -0,0 +1,78 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Conversion.h`. + +function u256_to_u128(x1, x2, x3, x4) -> v1, v2 { + if i64.ne(0, i64.or(x1, x2)) { invalid() } + v2 := x4 + v1 := x3 +} + +function u256_to_i64(x1, x2, x3, x4) -> v { + if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } + v := x4 +} + +function u256_to_i32(x1, x2, x3, x4) -> v:i32 { + if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } + if i64.ne(0, i64.shr_u(x4, 32)) { invalid() } + v := i32.wrap_i64(x4) +} + +function u256_to_byte(x1, x2, x3, x4) -> v { + if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } + if i64.gt_u(x4, 255) { invalid() } + v := x4 +} + +function u256_to_i32ptr(x1, x2, x3, x4) -> v:i32 { + v := u256_to_i32(x1, x2, x3, x4) +} + +function to_internal_i32ptr(x1, x2, x3, x4) -> r:i32 { + let p:i32 := u256_to_i32ptr(x1, x2, x3, x4) + r := i32.add(p, 64:i32) + if i32.lt_u(r, p) { invalid() } +} + +function u256_to_address(x1, x2, x3, x4) -> r1, r2, r3 { + if i64.ne(0, x1) { invalid() } + if i64.ne(0, i64.shr_u(x2, 32)) { invalid() } + r1 := x2 + r2 := x3 + r3 := x4 +} + +function endian_swap_16(x) -> y { + let hi := i64.and(i64.shl(x, 8), 0xff00) + let lo := i64.and(i64.shr_u(x, 8), 0xff) + y := i64.or(hi, lo) +} + +function endian_swap_32(x) -> y { + let hi := i64.shl(endian_swap_16(x), 16) + let lo := endian_swap_16(i64.shr_u(x, 16)) + y := i64.or(hi, lo) +} + +function endian_swap(x) -> y { + let hi := i64.shl(endian_swap_32(x), 32) + let lo := endian_swap_32(i64.shr_u(x, 32)) + y := i64.or(hi, lo) +} diff --git a/libyul/backends/wasm/polyfill/Interface.yul b/libyul/backends/wasm/polyfill/Interface.yul new file mode 100644 index 000000000..0418c122d --- /dev/null +++ b/libyul/backends/wasm/polyfill/Interface.yul @@ -0,0 +1,411 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Interface.h`. + +function address() -> z1, z2, z3, z4 { + eth.getAddress(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function balance(x1, x2, x3, x4) -> z1, z2, z3, z4 { + mstore_address(0:i32, x1, x2, x3, x4) + eth.getExternalBalance(12:i32, 48:i32) + z1, z2, z3, z4 := mload_internal(32:i32) +} + +function selfbalance() -> z1, z2, z3, z4 { + // TODO: not part of current Ewasm spec + unreachable() +} + +function chainid() -> z1, z2, z3, z4 { + // TODO: not part of current Ewasm spec + unreachable() +} + +function origin() -> z1, z2, z3, z4 { + eth.getTxOrigin(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function caller() -> z1, z2, z3, z4 { + eth.getCaller(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function callvalue() -> z1, z2, z3, z4 { + eth.getCallValue(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 { + eth.callDataCopy(0:i32, u256_to_i32(x1, x2, x3, x4), 32:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function calldatasize() -> z1, z2, z3, z4 { + z4 := i64.extend_i32_u(eth.getCallDataSize()) +} + +function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { + eth.callDataCopy( + to_internal_i32ptr(x1, x2, x3, x4), + u256_to_i32(y1, y2, y3, y4), + u256_to_i32(z1, z2, z3, z4) + ) +} + +// Needed? +function codesize() -> z1, z2, z3, z4 { + z4 := i64.extend_i32_u(eth.getCodeSize()) +} + +function codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { + eth.codeCopy( + to_internal_i32ptr(x1, x2, x3, x4), + u256_to_i32(y1, y2, y3, y4), + u256_to_i32(z1, z2, z3, z4) + ) +} + +function datacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { + // TODO correct? + codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) +} + +function gasprice() -> z1, z2, z3, z4 { + eth.getTxGasPrice(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function extcodesize_internal(x1, x2, x3, x4) -> r:i32 { + mstore_address(0:i32, x1, x2, x3, x4) + r := eth.getExternalCodeSize(12:i32) +} + +function extcodesize(x1, x2, x3, x4) -> z1, z2, z3, z4 { + z4 := i64.extend_i32_u(extcodesize_internal(x1, x2, x3, x4)) +} + +function extcodehash(x1, x2, x3, x4) -> z1, z2, z3, z4 { + // TODO: not part of current Ewasm spec + unreachable() +} + +function extcodecopy(a1, a2, a3, a4, p1, p2, p3, p4, o1, o2, o3, o4, l1, l2, l3, l4) { + mstore_address(0:i32, a1, a2, a3, a4) + let codeOffset:i32 := u256_to_i32(o1, o2, o3, o4) + let codeLength:i32 := u256_to_i32(l1, l2, l3, l4) + eth.externalCodeCopy(12:i32, to_internal_i32ptr(p1, p2, p3, p4), codeOffset, codeLength) +} + +function returndatasize() -> z1, z2, z3, z4 { + z4 := i64.extend_i32_u(eth.getReturnDataSize()) +} + +function returndatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { + eth.returnDataCopy( + to_internal_i32ptr(x1, x2, x3, x4), + u256_to_i32(y1, y2, y3, y4), + u256_to_i32(z1, z2, z3, z4) + ) +} + +function blockhash(x1, x2, x3, x4) -> z1, z2, z3, z4 { + let r:i32 := eth.getBlockHash(u256_to_i64(x1, x2, x3, x4), 0:i32) + if i32.eqz(r) { + z1, z2, z3, z4 := mload_internal(0:i32) + } +} + +function coinbase() -> z1, z2, z3, z4 { + eth.getBlockCoinbase(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function timestamp() -> z1, z2, z3, z4 { + z4 := eth.getBlockTimestamp() +} + +function number() -> z1, z2, z3, z4 { + z4 := eth.getBlockNumber() +} + +function difficulty() -> z1, z2, z3, z4 { + eth.getBlockDifficulty(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function gaslimit() -> z1, z2, z3, z4 { + z4 := eth.getBlockGasLimit() +} + +function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 { + z1, z2, z3, z4 := mload_internal(to_internal_i32ptr(x1, x2, x3, x4)) +} + +function mload_internal(pos:i32) -> z1, z2, z3, z4 { + z1 := endian_swap(i64.load(pos)) + z2 := endian_swap(i64.load(i32.add(pos, 8:i32))) + z3 := endian_swap(i64.load(i32.add(pos, 16:i32))) + z4 := endian_swap(i64.load(i32.add(pos, 24:i32))) +} + +function mstore(x1, x2, x3, x4, y1, y2, y3, y4) { + mstore_internal(to_internal_i32ptr(x1, x2, x3, x4), y1, y2, y3, y4) +} + +function mstore_internal(pos:i32, y1, y2, y3, y4) { + i64.store(pos, endian_swap(y1)) + i64.store(i32.add(pos, 8:i32), endian_swap(y2)) + i64.store(i32.add(pos, 16:i32), endian_swap(y3)) + i64.store(i32.add(pos, 24:i32), endian_swap(y4)) +} + +function mstore_address(pos:i32, a1, a2, a3, a4) { + a1, a2, a3 := u256_to_address(a1, a2, a3, a4) + mstore_internal(pos, 0, a1, a2, a3) +} + +function mstore8(x1, x2, x3, x4, y1, y2, y3, y4) { + let v := u256_to_byte(y1, y2, y3, y4) + i64.store8(to_internal_i32ptr(x1, x2, x3, x4), v) +} + +// Needed? +function msize() -> z1, z2, z3, z4 { + // TODO implement + unreachable() +} + +function sload(x1, x2, x3, x4) -> z1, z2, z3, z4 { + mstore_internal(0:i32, x1, x2, x3, x4) + eth.storageLoad(0:i32, 32:i32) + z1, z2, z3, z4 := mload_internal(32:i32) +} + +function sstore(x1, x2, x3, x4, y1, y2, y3, y4) { + mstore_internal(0:i32, x1, x2, x3, x4) + mstore_internal(32:i32, y1, y2, y3, y4) + eth.storageStore(0:i32, 32:i32) +} + +function gas() -> z1, z2, z3, z4 { + z4 := eth.getGasLeft() +} + +function log0(p1, p2, p3, p4, s1, s2, s3, s4) { + eth.log( + to_internal_i32ptr(p1, p2, p3, p4), + u256_to_i32(s1, s2, s3, s4), + 0:i32, 0:i32, 0:i32, 0:i32, 0:i32 + ) +} + +function log1( + p1, p2, p3, p4, s1, s2, s3, s4, + t1_1, t1_2, t1_3, t1_4 +) { + eth.log( + to_internal_i32ptr(p1, p2, p3, p4), + u256_to_i32(s1, s2, s3, s4), + 1:i32, + to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), + 0:i32, 0:i32, 0:i32 + ) +} + +function log2( + p1, p2, p3, p4, s1, s2, s3, s4, + t1_1, t1_2, t1_3, t1_4, + t2_1, t2_2, t2_3, t2_4 +) { + eth.log( + to_internal_i32ptr(p1, p2, p3, p4), + u256_to_i32(s1, s2, s3, s4), + 2:i32, + to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), + to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), + 0:i32, 0:i32 + ) +} + +function log3( + p1, p2, p3, p4, s1, s2, s3, s4, + t1_1, t1_2, t1_3, t1_4, + t2_1, t2_2, t2_3, t2_4, + t3_1, t3_2, t3_3, t3_4 +) { + eth.log( + to_internal_i32ptr(p1, p2, p3, p4), + u256_to_i32(s1, s2, s3, s4), + 3:i32, + to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), + to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), + to_internal_i32ptr(t3_1, t3_2, t3_3, t3_4), + 0:i32 + ) +} + +function log4( + p1, p2, p3, p4, s1, s2, s3, s4, + t1_1, t1_2, t1_3, t1_4, + t2_1, t2_2, t2_3, t2_4, + t3_1, t3_2, t3_3, t3_4, + t4_1, t4_2, t4_3, t4_4, +) { + eth.log( + to_internal_i32ptr(p1, p2, p3, p4), + u256_to_i32(s1, s2, s3, s4), + 4:i32, + to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), + to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), + to_internal_i32ptr(t3_1, t3_2, t3_3, t3_4), + to_internal_i32ptr(t4_1, t4_2, t4_3, t4_4) + ) +} + +function create( + x1, x2, x3, x4, + y1, y2, y3, y4, + z1, z2, z3, z4 +) -> a1, a2, a3, a4 { + let v1, v2 := u256_to_u128(x1, x2, x3, x4) + mstore_internal(0:i32, 0, 0, v1, v2) + + let r:i32 := eth.create(0:i32, to_internal_i32ptr(y1, y2, y3, y4), u256_to_i32(z1, z2, z3, z4), 32:i32) + if i32.eqz(r) { + a1, a2, a3, a4 := mload_internal(32:i32) + } +} + +function call( + a1, a2, a3, a4, + b1, b2, b3, b4, + c1, c2, c3, c4, + d1, d2, d3, d4, + e1, e2, e3, e4, + f1, f2, f3, f4, + g1, g2, g3, g4 +) -> x1, x2, x3, x4 { + let g := u256_to_i64(a1, a2, a3, a4) + mstore_address(0:i32, b1, b2, b3, b4) + + let v1, v2 := u256_to_u128(c1, c2, c3, c4) + mstore_internal(32:i32, 0, 0, v1, v2) + + x4 := i64.extend_i32_u(eth.call(g, 12:i32, 32:i32, to_internal_i32ptr(d1, d2, d3, d4), u256_to_i32(e1, e2, e3, e4))) +} + +function callcode( + a1, a2, a3, a4, + b1, b2, b3, b4, + c1, c2, c3, c4, + d1, d2, d3, d4, + e1, e2, e3, e4, + f1, f2, f3, f4, + g1, g2, g3, g4 +) -> x1, x2, x3, x4 { + mstore_address(0:i32, b1, b2, b3, b4) + + let v1, v2 := u256_to_u128(c1, c2, c3, c4) + mstore_internal(32:i32, 0, 0, v1, v2) + + x4 := i64.extend_i32_u(eth.callCode( + u256_to_i64(a1, a2, a3, a4), + 12:i32, + 32:i32, + to_internal_i32ptr(d1, d2, d3, d4), + u256_to_i32(e1, e2, e3, e4) + )) +} + +function delegatecall( + a1, a2, a3, a4, + b1, b2, b3, b4, + c1, c2, c3, c4, + d1, d2, d3, d4, + e1, e2, e3, e4, + f1, f2, f3, f4 +) -> x1, x2, x3, x4 { + mstore_address(0:i32, b1, b2, b3, b4) + + x4 := i64.extend_i32_u(eth.callDelegate( + u256_to_i64(a1, a2, a3, a4), + 12:i32, + to_internal_i32ptr(c1, c2, c3, c4), + u256_to_i32(d1, d2, d3, d4) + )) +} + +function staticcall( + a1, a2, a3, a4, + b1, b2, b3, b4, + c1, c2, c3, c4, + d1, d2, d3, d4, + e1, e2, e3, e4, + f1, f2, f3, f4 +) -> x1, x2, x3, x4 { + mstore_address(0:i32, b1, b2, b3, b4) + + x4 := i64.extend_i32_u(eth.callStatic( + u256_to_i64(a1, a2, a3, a4), + 12:i32, + to_internal_i32ptr(c1, c2, c3, c4), + u256_to_i32(d1, d2, d3, d4) + )) +} + +function create2( + a1, a2, a3, a4, + b1, b2, b3, b4, + c1, c2, c3, c4, + d1, d2, d3, d4 +) -> x1, x2, x3, x4 { + // TODO: not part of current Ewasm spec + unreachable() +} + +function selfdestruct(a1, a2, a3, a4) { + mstore_address(0:i32, a1, a2, a3, a4) + // In EVM, addresses are padded to 32 bytes, so discard the first 12. + eth.selfDestruct(12:i32) +} + +function return(x1, x2, x3, x4, y1, y2, y3, y4) { + eth.finish( + to_internal_i32ptr(x1, x2, x3, x4), + u256_to_i32(y1, y2, y3, y4) + ) +} + +function revert(x1, x2, x3, x4, y1, y2, y3, y4) { + eth.revert( + to_internal_i32ptr(x1, x2, x3, x4), + u256_to_i32(y1, y2, y3, y4) + ) +} + +function invalid() { + unreachable() +} + +function stop() { + eth.finish(0:i32, 0:i32) +} diff --git a/libyul/backends/wasm/polyfill/Keccak.yul b/libyul/backends/wasm/polyfill/Keccak.yul new file mode 100644 index 000000000..2dae23394 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Keccak.yul @@ -0,0 +1,24 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Keccak.h`. + +function keccak256(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + // TODO implement + unreachable() +} diff --git a/libyul/backends/wasm/polyfill/Logical.yul b/libyul/backends/wasm/polyfill/Logical.yul new file mode 100644 index 000000000..8d297d354 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Logical.yul @@ -0,0 +1,31 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Logical.h`. + +function or_bool(a, b, c, d) -> r:i32 { + r := i32.eqz(i64.eqz(i64.or(i64.or(a, b), i64.or(c, d)))) +} + +function or_bool_320(a, b, c, d, e) -> r:i32 { + r := i32.or(or_bool(a, b, c, 0), or_bool(d, e, 0, 0)) +} + +function or_bool_512(a, b, c, d, e, f, g, h) -> r:i32 { + r := i32.or(or_bool(a, b, c, d), or_bool(e, f, g, h)) +} diff --git a/libyul/backends/wasm/polyfill/Memory.yul b/libyul/backends/wasm/polyfill/Memory.yul new file mode 100644 index 000000000..3074ad1e4 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Memory.yul @@ -0,0 +1,62 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Memory.h`. + +function save_temp_mem_32() -> t1, t2, t3, t4 { + t1 := i64.load(0:i32) + t2 := i64.load(8:i32) + t3 := i64.load(16:i32) + t4 := i64.load(24:i32) +} + +function restore_temp_mem_32(t1, t2, t3, t4) { + i64.store(0:i32, t1) + i64.store(8:i32, t2) + i64.store(16:i32, t3) + i64.store(24:i32, t4) +} + +function save_temp_mem_64() -> t1, t2, t3, t4, t5, t6, t7, t8 { + t1 := i64.load(0:i32) + t2 := i64.load(8:i32) + t3 := i64.load(16:i32) + t4 := i64.load(24:i32) + t5 := i64.load(32:i32) + t6 := i64.load(40:i32) + t7 := i64.load(48:i32) + t8 := i64.load(54:i32) +} + +function restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8) { + i64.store(0:i32, t1) + i64.store(8:i32, t2) + i64.store(16:i32, t3) + i64.store(24:i32, t4) + i64.store(32:i32, t5) + i64.store(40:i32, t6) + i64.store(48:i32, t7) + i64.store(54:i32, t8) +} + +function pop(x1, x2, x3, x4) { +} + +function memoryguard(x:i64) -> y1, y2, y3, y4 { + y4 := x +} diff --git a/test/cmdlineTests/evm_to_wasm_break/output b/test/cmdlineTests/evm_to_wasm_break/output index 0348dcccb..0385cea1f 100644 --- a/test/cmdlineTests/evm_to_wasm_break/output +++ b/test/cmdlineTests/evm_to_wasm_break/output @@ -43,7 +43,7 @@ object "object" { x_7 := x_11 } { - let _5, _6, _7, _8 := iszero_170_789(_1, _1, _1, lt_172(x_4, x_5, x_6, x_7, _1, _1, _1, 10)) + let _5, _6, _7, _8 := iszero_169_788(_1, _1, _1, lt_171(x_4, x_5, x_6, x_7, _1, _1, _1, 10)) if i32.eqz(i64.eqz(i64.or(i64.or(_5, _6), i64.or(_7, _8)))) { break } if i32.eqz(i64.eqz(i64.or(_3, i64.or(_1, eq(x_4, x_5, x_6, x_7, _1, _1, _1, 2))))) { break } if i32.eqz(i64.eqz(i64.or(_3, i64.or(_1, eq(x_4, x_5, x_6, x_7, _1, _1, _1, 4))))) { continue } @@ -67,7 +67,7 @@ object "object" { let r1_1, carry_2 := add_carry(x1, y1, carry_1) r1 := r1_1 } - function iszero_170_789(x1, x2, x3, x4) -> r1, r2, r3, r4 + function iszero_169_788(x1, x2, x3, x4) -> r1, r2, r3, r4 { r4 := i64.extend_i32_u(i64.eqz(i64.or(i64.or(x1, x2), i64.or(x3, x4)))) } @@ -87,7 +87,7 @@ object "object" { case 1:i32 { r := 0xffffffff:i32 } default { r := i64.ne(a, b) } } - function lt_172(x1, x2, x3, x4, y1, y2, y3, y4) -> z4 + function lt_171(x1, x2, x3, x4, y1, y2, y3, y4) -> z4 { let z:i32 := false switch cmp(x1, y1) @@ -106,20 +106,6 @@ object "object" { default { z := 1:i32 } z4 := i64.extend_i32_u(z) } - function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 - { - if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { unreachable() } - if i64.ne(0, i64.shr_u(x4, 32)) { unreachable() } - eth.callDataCopy(0:i32, i32.wrap_i64(x4), 32:i32) - let z1_1 := endian_swap(i64.load(0:i32)) - let z2_1 := endian_swap(i64.load(i32.add(0:i32, 8:i32))) - let z3_1 := endian_swap(i64.load(i32.add(0:i32, 16:i32))) - let z4_1 := endian_swap(i64.load(i32.add(0:i32, 24:i32))) - z1 := z1_1 - z2 := z2_1 - z3 := z3_1 - z4 := z4_1 - } function endian_swap_16(x) -> y { y := i64.or(i64.and(i64.shl(x, 8), 0xff00), i64.and(i64.shr_u(x, 8), 0xff)) @@ -134,6 +120,20 @@ object "object" { let hi := i64.shl(endian_swap_32(x), 32) y := i64.or(hi, endian_swap_32(i64.shr_u(x, 32))) } + function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 + { + if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { unreachable() } + if i64.ne(0, i64.shr_u(x4, 32)) { unreachable() } + eth.callDataCopy(0:i32, i32.wrap_i64(x4), 32:i32) + let z1_1 := endian_swap(i64.load(0:i32)) + let z2_1 := endian_swap(i64.load(i32.add(0:i32, 8:i32))) + let z3_1 := endian_swap(i64.load(i32.add(0:i32, 16:i32))) + let z4_1 := endian_swap(i64.load(i32.add(0:i32, 24:i32))) + z1 := z1_1 + z2 := z2_1 + z3 := z3_1 + z4 := z4_1 + } function mstore_internal(pos:i32, y1, y2, y3, y4) { i64.store(pos, endian_swap(y1)) @@ -152,7 +152,7 @@ object "object" { Binary representation: -0061736d0100000001480a60000060017e017e60027e7e017f60037e7e7e017e60047e7e7e7e017e60087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60057f7e7e7e7e0060027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003060406020604010101070505030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020af0070da302030b7e017f087e02404200210002402000200020002000100921012300210223012103230221040b20012105200221062003210720042108420121092000200084210a200a200020098484504545210b02400340200b45450d01024002402000200020002005200620072008200020002000420a10081005210c2300210d2301210e2302210f0b200c200d84200e200f8484504504400c030b200a20002005200620072008200020002000420210068484504504400c030b200a20002005200620072008200020002000420410068484504504400c010b0b024020052006200720082000200020002009100421102300211123012112230221130b201021052011210620122107201321080c000b0b20002000200020002005200620072008100e0b0b2901037e0240200020017c2105200520027c21032005200054200320055472ad21040b2004240020030b6c010b7e0240200320077c210c200c42007c210b024020022006200c200354200b200c5472ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2401047e0240200020018420022003848450ad21070b20052400200624012007240220040b2d01017e024020002004510440200120055104402002200651044020032007510440420121080b0b0b0b0b20080b2701027f024002402000200154210320034101460440417f210205200020015221020b0b0b20020b8a0102017e047f0240410021090240200020041007210a200a41004604400240200120051007210b200b41004604400240200220061007210c200c41004604402003200754210905200c41014604404100210905410121090b0b0b05200b41014604404100210905410121090b0b0b05200a41014604404100210905410121090b0b0b2009ad21080b20080b7601087e024042002000200184200284520440000b42002003422088520440000b41002003a7412010014100290000100c2108410041086a290000100c2109410041106a290000100c210a410041186a290000100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b3200024020002001100c370000200041086a2002100c370000200041106a2003100c370000200041186a2004100c3700000b0b2300024041002000200120022003100d41202004200520062007100d4100412010000b0b +0061736d0100000001480a60000060017e017e60027e7e017f60037e7e7e017e60047e7e7e7e017e60087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60057f7e7e7e7e0060027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003060406020601010104070505030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020af0070da302030b7e017f087e02404200210002402000200020002000100c21012300210223012103230221040b20012105200221062003210720042108420121092000200084210a200a200020098484504545210b02400340200b45450d01024002402000200020002005200620072008200020002000420a10081005210c2300210d2301210e2302210f0b200c200d84200e200f8484504504400c030b200a20002005200620072008200020002000420210068484504504400c030b200a20002005200620072008200020002000420410068484504504400c010b0b024020052006200720082000200020002009100421102300211123012112230221130b201021052011210620122107201321080c000b0b20002000200020002005200620072008100e0b0b2901037e0240200020017c2105200520027c21032005200054200320055472ad21040b2004240020030b6c010b7e0240200320077c210c200c42007c210b024020022006200c200354200b200c5472ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2401047e0240200020018420022003848450ad21070b20052400200624012007240220040b2d01017e024020002004510440200120055104402002200651044020032007510440420121080b0b0b0b0b20080b2701027f024002402000200154210320034101460440417f210205200020015221020b0b0b20020b8a0102017e047f0240410021090240200020041007210a200a41004604400240200120051007210b200b41004604400240200220061007210c200c41004604402003200754210905200c41014604404100210905410121090b0b0b05200b41014604404100210905410121090b0b0b05200a41014604404100210905410121090b0b0b2009ad21080b20080b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100942108621022002200042108810098421010b20010b1e01027e02402000100a422086210220022000422088100a8421010b20010b7601087e024042002000200184200284520440000b42002003422088520440000b41002003a7412010014100290000100b2108410041086a290000100b2109410041106a290000100b210a410041186a290000100b210b2008210420092105200a2106200b21070b20052400200624012007240220040b3200024020002001100b370000200041086a2002100b370000200041106a2003100b370000200041186a2004100b3700000b0b2300024041002000200120022003100d41202004200520062007100d4100412010000b0b Text representation: (module @@ -206,7 +206,7 @@ Text representation: (br_if $label__3 (i32.eqz (i32.eqz (local.get $_4)))) (block $label__4 (block - (local.set $_5 (call $iszero_170_789 (local.get $_1) (local.get $_1) (local.get $_1) (call $lt_172 (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 10)))) + (local.set $_5 (call $iszero_169_788 (local.get $_1) (local.get $_1) (local.get $_1) (call $lt_171 (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 10)))) (local.set $_6 (global.get $global_)) (local.set $_7 (global.get $global__1)) (local.set $_8 (global.get $global__2)) @@ -310,7 +310,7 @@ Text representation: (local.get $r1) ) -(func $iszero_170_789 +(func $iszero_169_788 (param $x1 i64) (param $x2 i64) (param $x3 i64) @@ -377,7 +377,7 @@ Text representation: (local.get $r) ) -(func $lt_172 +(func $lt_171 (param $x1 i64) (param $x2 i64) (param $x3 i64) @@ -437,6 +437,43 @@ Text representation: (local.get $z4) ) +(func $endian_swap_16 + (param $x i64) + (result i64) + (local $y i64) + (block $label__15 + (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) + + ) + (local.get $y) +) + +(func $endian_swap_32 + (param $x i64) + (result i64) + (local $y i64) + (local $hi i64) + (block $label__16 + (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) + (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) + + ) + (local.get $y) +) + +(func $endian_swap + (param $x i64) + (result i64) + (local $y i64) + (local $hi i64) + (block $label__17 + (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) + + ) + (local.get $y) +) + (func $calldataload (param $x1 i64) (param $x2 i64) @@ -451,7 +488,7 @@ Text representation: (local $z2_1 i64) (local $z3_1 i64) (local $z4_1 i64) - (block $label__15 + (block $label__18 (if (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3))) (then (unreachable))) (if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then @@ -473,43 +510,6 @@ Text representation: (local.get $z1) ) -(func $endian_swap_16 - (param $x i64) - (result i64) - (local $y i64) - (block $label__16 - (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) - - ) - (local.get $y) -) - -(func $endian_swap_32 - (param $x i64) - (result i64) - (local $y i64) - (local $hi i64) - (block $label__17 - (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) - - ) - (local.get $y) -) - -(func $endian_swap - (param $x i64) - (result i64) - (local $y i64) - (local $hi i64) - (block $label__18 - (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) - - ) - (local.get $y) -) - (func $mstore_internal (param $pos i32) (param $y1 i64) diff --git a/test/cmdlineTests/standard_ewasm_requested/output.json b/test/cmdlineTests/standard_ewasm_requested/output.json index a4224fbab..4a4487310 100644 --- a/test/cmdlineTests/standard_ewasm_requested/output.json +++ b/test/cmdlineTests/standard_ewasm_requested/output.json @@ -1,4 +1,4 @@ -{"contracts":{"A":{"C":{"ewasm":{"wasm":"0061736d01000000013a0860000060017e017e60047e7e7e7e017f60087e7e7e7e7e7e7e7e00600c7e7e7e7e7e7e7e7e7e7e7e7e0060017f0060027f7f0060037f7f7f0002510408657468657265756d08636f6465436f7079000708657468657265756d06726576657274000608657468657265756d0c67657443616c6c56616c7565000508657468657265756d0666696e6973680006030a090002020401010103030503010001060100071102066d656d6f72790200046d61696e0004009d030c435f325f6465706c6f7965640061736d0100000001160460000060017e017e60047e7e7e7e017f60027f7f0002130108657468657265756d067265766572740003030504000201010503010001060100071102066d656d6f72790200046d61696e00010ab60204ca0104017e027f057e037f02404200210020002000200042c00010022101200141c0006a210220022001490440000b20001003421086210320032000421088100384422086210420042000422088100484210520022005370000200241086a2005370000200241106a20053700004280011003421086210620064280014210881003844220862107200241186a2007428001422088100484370000200020002000200010022108200020002000200010022109200941c0006a210a200a2009490440000b200a200810000b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100342108621022002200042108810038421010b20010b0aec0309dc0103017e027f057e02404200210020002000200042c00010052101200141c0006a210220022001490440000b2000100a210320022003370000200241086a2003370000200241106a2003370000200241186a428001100a370000410010024100290000100a2104410041086a290000100a2105410041106a290000100a210620042005842006410041186a290000100a84845045044020002000200020002000200020002000100c0b4290032107200020002000200020002000200042ce012000200020002007100720002000200020002000200020002007100b0b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b2601027f0240200020012002200310052105200541c0006a210420042005490440000b0b20040b25000240200020012002200310062004200520062007100520082009200a200b100510000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100842108621022002200042108810088421010b20010b1e01027e02402000100942208621022002200042208810098421010b20010b1b000240200020012002200310062004200520062007100510030b0b1b000240200020012002200310062004200520062007100510010b0b","wast":"(module +{"contracts":{"A":{"C":{"ewasm":{"wasm":"0061736d01000000013a0860000060017e017e60047e7e7e7e017f60087e7e7e7e7e7e7e7e00600c7e7e7e7e7e7e7e7e7e7e7e7e0060017f0060027f7f0060037f7f7f0002510408657468657265756d08636f6465436f7079000708657468657265756d06726576657274000608657468657265756d0c67657443616c6c56616c7565000508657468657265756d0666696e6973680006030a090002020101010403030503010001060100071102066d656d6f72790200046d61696e0004009d030c435f325f6465706c6f7965640061736d0100000001160460000060017e017e60047e7e7e7e017f60027f7f0002130108657468657265756d067265766572740003030504000201010503010001060100071102066d656d6f72790200046d61696e00010ab60204ca0104017e027f057e037f02404200210020002000200042c00010022101200141c0006a210220022001490440000b20001003421086210320032000421088100384422086210420042000422088100484210520022005370000200241086a2005370000200241106a20053700004280011003421086210620064280014210881003844220862107200241186a2007428001422088100484370000200020002000200010022108200020002000200010022109200941c0006a210a200a2009490440000b200a200810000b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100342108621022002200042108810038421010b20010b0aec0309dc0103017e027f057e02404200210020002000200042c00010052101200141c0006a210220022001490440000b20001009210320022003370000200241086a2003370000200241106a2003370000200241186a428001100937000041001002410029000010092104410041086a29000010092105410041106a2900001009210620042005842006410041186a290000100984845045044020002000200020002000200020002000100c0b4290032107200020002000200020002000200042ce012000200020002007100a20002000200020002000200020002007100b0b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b2601027f0240200020012002200310052105200541c0006a210420042005490440000b0b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100742108621022002200042108810078421010b20010b1e01027e02402000100842208621022002200042208810088421010b20010b25000240200020012002200310062004200520062007100520082009200a200b100510000b0b1b000240200020012002200310062004200520062007100510030b0b1b000240200020012002200310062004200520062007100510010b0b","wast":"(module ;; custom section for sub-module ;; The Keccak-256 hash of the text representation of \"C_2_deployed\": f03f5b9154b9eb6803a947177e38e92e2860de95e90ba0e75eb71a58f18ed589 ;; (@custom \"C_2_deployed\" \"0061736d0100000001160460000060017e017e60047e7e7e7e017f60027f7f0002130108657468657265756d067265766572740003030504000201010503010001060100071102066d656d6f72790200046d61696e00010ab60204ca0104017e027f057e037f02404200210020002000200042c00010022101200141c0006a210220022001490440000b20001003421086210320032000421088100384422086210420042000422088100484210520022005370000200241086a2005370000200241106a20053700004280011003421086210620064280014210881003844220862107200241186a2007428001422088100484370000200020002000200010022108200020002000200010022109200941c0006a210a200a2009490440000b200a200810000b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100342108621022002200042108810038421010b20010b\") @@ -77,6 +77,43 @@ (local.get $r) ) +(func $endian_swap_16 + (param $x i64) + (result i64) + (local $y i64) + (block $label__3 + (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) + + ) + (local.get $y) +) + +(func $endian_swap_32 + (param $x i64) + (result i64) + (local $y i64) + (local $hi i64) + (block $label__4 + (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) + (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) + + ) + (local.get $y) +) + +(func $endian_swap + (param $x i64) + (result i64) + (local $y i64) + (local $hi i64) + (block $label__5 + (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) + + ) + (local.get $y) +) + (func $codecopy (param $x1 i64) (param $x2 i64) @@ -90,48 +127,11 @@ (param $z2 i64) (param $z3 i64) (param $z4 i64) - (block $label__3 + (block $label__6 (call $eth.codeCopy (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)) (call $u256_to_i32 (local.get $z1) (local.get $z2) (local.get $z3) (local.get $z4))) ) ) -(func $endian_swap_16 - (param $x i64) - (result i64) - (local $y i64) - (block $label__4 - (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) - - ) - (local.get $y) -) - -(func $endian_swap_32 - (param $x i64) - (result i64) - (local $y i64) - (local $hi i64) - (block $label__5 - (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) - - ) - (local.get $y) -) - -(func $endian_swap - (param $x i64) - (result i64) - (local $y i64) - (local $hi i64) - (block $label__6 - (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) - - ) - (local.get $y) -) - (func $return (param $x1 i64) (param $x2 i64) From 2665eaa4fac3d249fa3d892487ed7b5cfd562e49 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Nov 2020 14:39:39 +0100 Subject: [PATCH 05/10] Support .offset and .length for calldata bytes and string arrays. --- Changelog.md | 4 + docs/assembly.rst | 4 + libsolidity/analysis/ReferencesResolver.cpp | 20 ++- libsolidity/analysis/TypeChecker.cpp | 72 +++++++---- libsolidity/ast/ASTAnnotations.h | 4 +- libsolidity/ast/ASTJsonConverter.cpp | 6 +- libsolidity/codegen/ContractCompiler.cpp | 96 +++++++++----- .../codegen/ir/IRGeneratorForStatements.cpp | 120 ++++++++++-------- .../ASTJSON/assembly/slot_offset.json | 2 + .../ASTJSON/assembly/slot_offset_legacy.json | 2 + .../inlineAssembly/calldata_array_assign.sol | 8 ++ .../inlineAssembly/calldata_array_read.sol | 12 ++ .../inlineAssembly/calldata_assign.sol | 10 ++ .../calldata_assign_from_nowhere.sol | 9 ++ .../inlineAssembly/calldata_length_read.sol | 18 +++ .../inlineAssembly/calldata_offset_read.sol | 19 +++ .../calldata_offset_read_write.sol | 18 +++ .../inlineAssembly/slot_access.sol | 2 + .../inlineAssembly/invalid/calldata_array.sol | 9 ++ .../invalid/calldata_array_offset.sol | 8 ++ .../inlineAssembly/invalid/calldata_slot.sol | 9 ++ .../invalid/calldata_variables.sol | 2 +- .../invalid/storage_assignment.sol | 2 +- .../storage_assignment_in_modifier.sol | 2 +- .../invalid/storage_nonslot.sol | 12 ++ .../invalid/unbalanced_two_stack_load.sol | 2 +- .../inlineAssembly/storage_reference.sol | 2 +- .../inlineAssembly/storage_reference_old.sol | 4 +- .../storage_reference_on_function.sol | 2 +- .../storage_reference_on_memory.sol | 4 +- .../inlineAssembly/two_stack_slot_access.sol | 10 ++ 31 files changed, 366 insertions(+), 128 deletions(-) create mode 100644 test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/calldata_array_read.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/calldata_assign.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/calldata_assign_from_nowhere.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/calldata_length_read.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read_write.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array_offset.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_slot.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_nonslot.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/two_stack_slot_access.sol diff --git a/Changelog.md b/Changelog.md index 5c5d9d205..87678abbf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Language Features: * Ability to select the abi coder using ``pragma abicoder v1`` and ``pragma abicoder v2``. + * Inline Assembly: Use ``.offset`` and ``.length`` for calldata variables of dynamic array type to access their calldata offset and length (number of elements). Both of them can also be assigned to. * Immutable variables with literal number values are considered pure. Compiler Features: @@ -34,6 +35,9 @@ Bugfixes: * NatSpec: Fix internal error when inheriting return parameter documentation but the parameter names differ between base and inherited. * Standard JSON: Fix library addresses specified in ``libraries`` being used for linking even if the file names do not match. +AST Changes: + * New member ``suffix`` for inline assembly identifiers. Currently supported values are ``"slot"``, ``"offset"`` and ``"length"`` to access the components of a Solidity variable. + ### 0.7.4 (2020-10-19) diff --git a/docs/assembly.rst b/docs/assembly.rst index 778f90fe5..3e83fa75f 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -135,6 +135,10 @@ inside that slot. To retrieve the slot pointed to by the variable ``x``, you use ``x.slot``, and to retrieve the byte-offset you use ``x.offset``. Using ``x`` itself will result in an error. +For dynamic calldata arrays, you can access +their calldata offset (in bytes) and length (number of elements) using ``x.offset`` and ``x.length``. +Both expressions can also be assigned to. + Local Solidity variables are available for assignments, for example: .. code:: diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 75445f229..cd0d4852e 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -215,22 +215,21 @@ void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) void ReferencesResolver::operator()(yul::Identifier const& _identifier) { - bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), ".slot"); - bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), ".offset"); + static set suffixes{"slot", "offset", "length"}; + string suffix; + for (string const& s: suffixes) + if (boost::algorithm::ends_with(_identifier.name.str(), "." + s)) + suffix = s; // Could also use `pathFromCurrentScope`, split by '.' auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); - if (isSlot || isOffset) + if (!suffix.empty()) { // special mode to access storage variables if (!declarations.empty()) // the special identifier exists itself, we should not allow that. return; - string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - ( - isSlot ? - string(".slot").size() : - string(".offset").size() - )); + string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - suffix.size() - 1); solAssert(!realName.empty(), "Empty name."); declarations = m_resolver.nameFromCurrentScope(realName); if (!declarations.empty()) @@ -255,7 +254,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) m_errorReporter.declarationError( 9467_error, _identifier.location, - "Identifier not found. Use ``.slot`` and ``.offset`` to access storage variables." + "Identifier not found. Use \".slot\" and \".offset\" to access storage variables." ); return; } @@ -270,8 +269,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) return; } - m_yulAnnotation->externalReferences[&_identifier].isSlot = isSlot; - m_yulAnnotation->externalReferences[&_identifier].isOffset = isOffset; + m_yulAnnotation->externalReferences[&_identifier].suffix = move(suffix); m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front(); } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b3d9220b6..fb806a8a2 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -740,7 +740,6 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) InlineAssemblyAnnotation::ExternalIdentifierInfo& identifierInfo = ref->second; Declaration const* declaration = identifierInfo.declaration; solAssert(!!declaration, ""); - bool requiresStorage = identifierInfo.isSlot || identifierInfo.isOffset; if (auto var = dynamic_cast(declaration)) { solAssert(var->type(), "Expected variable type!"); @@ -763,7 +762,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) m_errorReporter.typeError(6252_error, _identifier.location, "Constant variables cannot be assigned to."); return false; } - else if (requiresStorage) + else if (!identifierInfo.suffix.empty()) { m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes .offset and .slot can only be used on non-constant storage variables."); return false; @@ -789,51 +788,80 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(!dynamic_cast(var->type()), "FixedPointType not implemented."); - if (requiresStorage) + if (!identifierInfo.suffix.empty()) { - if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage)) + string const& suffix = identifierInfo.suffix; + solAssert((set{"offset", "slot", "length"}).count(suffix), ""); + if (var->isStateVariable() || var->type()->dataStoredIn(DataLocation::Storage)) { - m_errorReporter.typeError(3622_error, _identifier.location, "The suffixes .offset and .slot can only be used on storage variables."); - return false; + if (suffix != "slot" && suffix != "offset") + { + m_errorReporter.typeError(4656_error, _identifier.location, "State variables only support \".slot\" and \".offset\"."); + return false; + } + else if (_context == yul::IdentifierContext::LValue) + { + if (var->isStateVariable()) + { + m_errorReporter.typeError(4713_error, _identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\"."); + return false; + } + else if (suffix != "slot") + { + m_errorReporter.typeError(9739_error, _identifier.location, "Only .slot can be assigned to."); + return false; + } + } } - else if (_context == yul::IdentifierContext::LValue) + else if ( + auto const* arrayType = dynamic_cast(var->type()); + arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData) + ) { - if (var->isStateVariable()) + if (suffix != "offset" && suffix != "length") { - m_errorReporter.typeError(4713_error, _identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\"."); + m_errorReporter.typeError(1536_error, _identifier.location, "Calldata variables only support \".offset\" and \".length\"."); return false; } - else if (identifierInfo.isOffset) - { - m_errorReporter.typeError(9739_error, _identifier.location, "Only .slot can be assigned to."); - return false; - } - else - solAssert(identifierInfo.isSlot, ""); + } + else + { + m_errorReporter.typeError(3622_error, _identifier.location, "The suffix \"." + suffix + "\" is not supported by this variable or type."); + return false; } } else if (!var->isConstant() && var->isStateVariable()) { - m_errorReporter.typeError(1408_error, _identifier.location, "Only local variables are supported. To access storage variables, use the .slot and .offset suffixes."); + m_errorReporter.typeError( + 1408_error, + _identifier.location, + "Only local variables are supported. To access storage variables, use the \".slot\" and \".offset\" suffixes." + ); return false; } else if (var->type()->dataStoredIn(DataLocation::Storage)) { - m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the .slot or .offset suffix to access storage reference variables."); + m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the \".slot\" or \".offset\" suffix to access storage reference variables."); return false; } else if (var->type()->sizeOnStack() != 1) { - if (var->type()->dataStoredIn(DataLocation::CallData)) - m_errorReporter.typeError(2370_error, _identifier.location, "Call data elements cannot be accessed directly. Copy to a local variable first or use \"calldataload\" or \"calldatacopy\" with manually determined offsets and sizes."); + if ( + auto const* arrayType = dynamic_cast(var->type()); + arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData) + ) + m_errorReporter.typeError(1397_error, _identifier.location, "Call data elements cannot be accessed directly. Use \".offset\" and \".length\" to access the calldata offset and length of this array and then use \"calldatacopy\"."); else + { + solAssert(!var->type()->dataStoredIn(DataLocation::CallData), ""); m_errorReporter.typeError(9857_error, _identifier.location, "Only types that use one stack slot are supported."); + } return false; } } - else if (requiresStorage) + else if (!identifierInfo.suffix.empty()) { - m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes .offset and .slot can only be used on storage variables."); + m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes \".offset\", \".slot\" and \".length\" can only be used with variables."); return false; } else if (_context == yul::IdentifierContext::LValue) diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index dbb77aafc..28beb0d45 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -198,8 +198,8 @@ struct InlineAssemblyAnnotation: StatementAnnotation struct ExternalIdentifierInfo { Declaration const* declaration = nullptr; - bool isSlot = false; ///< Whether the storage slot of a variable is queried. - bool isOffset = false; ///< Whether the intra-slot offset of a storage variable is queried. + /// Suffix used, one of "slot", "offset", "length" or empty. + std::string suffix; size_t valueSize = size_t(-1); }; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 43e572069..f95b5c378 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -224,8 +224,10 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pairlocation); tuple["declaration"] = idOrNull(_info.second.declaration); - tuple["isSlot"] = Json::Value(_info.second.isSlot); - tuple["isOffset"] = Json::Value(_info.second.isOffset); + tuple["isSlot"] = Json::Value(_info.second.suffix == "slot"); + tuple["isOffset"] = Json::Value(_info.second.suffix == "offset"); + if (!_info.second.suffix.empty()) + tuple["suffix"] = Json::Value(_info.second.suffix); tuple["valueSize"] = Json::Value(Json::LargestUInt(_info.second.valueSize)); return tuple; } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index cc872d009..d339e50b9 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -745,9 +745,9 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) else if (m_context.isStateVariable(decl)) { auto const& location = m_context.storageLocationOfVariable(*decl); - if (ref->second.isSlot) + if (ref->second.suffix == "slot") m_context << location.first; - else if (ref->second.isOffset) + else if (ref->second.suffix == "offset") m_context << u256(location.second); else solAssert(false, ""); @@ -755,26 +755,44 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) else if (m_context.isLocalVariable(decl)) { unsigned stackDiff = static_cast(_assembly.stackHeight()) - m_context.baseStackOffsetOfVariable(*variable); - if (ref->second.isSlot || ref->second.isOffset) + if (!ref->second.suffix.empty()) { - solAssert(variable->type()->dataStoredIn(DataLocation::Storage), ""); - unsigned size = variable->type()->sizeOnStack(); - if (size == 2) + string const& suffix = ref->second.suffix; + if (variable->type()->dataStoredIn(DataLocation::Storage)) { - // slot plus offset - if (ref->second.isOffset) + solAssert(suffix == "offset" || suffix == "slot", ""); + unsigned size = variable->type()->sizeOnStack(); + if (size == 2) + { + // slot plus offset + if (suffix == "offset") + stackDiff--; + } + else + { + solAssert(size == 1, ""); + // only slot, offset is zero + if (suffix == "offset") + { + _assembly.appendConstant(u256(0)); + return; + } + } + } + else if (variable->type()->dataStoredIn(DataLocation::CallData)) + { + auto const* arrayType = dynamic_cast(variable->type()); + solAssert( + arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData), + "" + ); + solAssert(suffix == "offset" || suffix == "length", ""); + solAssert(variable->type()->sizeOnStack() == 2, ""); + if (suffix == "length") stackDiff--; } else - { - solAssert(size == 1, ""); - // only slot, offset is zero - if (ref->second.isOffset) - { - _assembly.appendConstant(u256(0)); - return; - } - } + solAssert(false, ""); } else solAssert(variable->type()->sizeOnStack() == 1, ""); @@ -784,7 +802,6 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) errinfo_sourceLocation(_inlineAssembly.location()) << errinfo_comment("Stack too deep, try removing local variables.") ); - solAssert(variable->type()->sizeOnStack() == 1, ""); _assembly.appendInstruction(dupInstruction(stackDiff)); } else @@ -792,7 +809,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) } else if (auto contract = dynamic_cast(decl)) { - solAssert(!ref->second.isOffset && !ref->second.isSlot, ""); + solAssert(ref->second.suffix.empty(), ""); solAssert(contract->isLibrary(), ""); _assembly.appendLinkerSymbol(contract->fullyQualifiedName()); } @@ -803,20 +820,39 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) else { // lvalue context - solAssert(!ref->second.isOffset, ""); auto variable = dynamic_cast(decl); - solAssert( - !!variable && m_context.isLocalVariable(variable), - "Can only assign to stack variables in inline assembly." - ); - solAssert(variable->type()->sizeOnStack() == 1, ""); unsigned stackDiff = static_cast(_assembly.stackHeight()) - m_context.baseStackOffsetOfVariable(*variable) - 1; - if (stackDiff > 16 || stackDiff < 1) - BOOST_THROW_EXCEPTION( - StackTooDeepError() << - errinfo_sourceLocation(_inlineAssembly.location()) << - errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.") + string const& suffix = ref->second.suffix; + if (variable->type()->dataStoredIn(DataLocation::Storage)) + { + solAssert( + !!variable && m_context.isLocalVariable(variable), + "Can only assign to stack variables in inline assembly." ); + solAssert(variable->type()->sizeOnStack() == 1, ""); + solAssert(suffix == "slot", ""); + if (stackDiff > 16 || stackDiff < 1) + BOOST_THROW_EXCEPTION( + StackTooDeepError() << + errinfo_sourceLocation(_inlineAssembly.location()) << + errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.") + ); + } + else if (variable->type()->dataStoredIn(DataLocation::CallData)) + { + auto const* arrayType = dynamic_cast(variable->type()); + solAssert( + arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData), + "" + ); + solAssert(suffix == "offset" || suffix == "length", ""); + solAssert(variable->type()->sizeOnStack() == 2, ""); + if (suffix == "length") + stackDiff--; + } + else + solAssert(suffix.empty(), ""); + _assembly.appendInstruction(swapInstruction(stackDiff)); _assembly.appendInstruction(Instruction::POP); } diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 142660749..0ec2856be 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -68,43 +68,12 @@ struct CopyTranslate: public yul::ASTCopier yul::Expression operator()(yul::Identifier const& _identifier) override { + // The operator() function is only called in lvalue context. In rvalue context, + // only translate(yul::Identifier) is called. if (m_references.count(&_identifier)) - { - auto const& reference = m_references.at(&_identifier); - auto const varDecl = dynamic_cast(reference.declaration); - solUnimplementedAssert(varDecl, ""); - - if (reference.isOffset || reference.isSlot) - { - solAssert(reference.isOffset != reference.isSlot, ""); - - string value; - if (varDecl->isStateVariable()) - value = - reference.isSlot ? - m_context.storageLocationOfStateVariable(*varDecl).first.str() : - to_string(m_context.storageLocationOfStateVariable(*varDecl).second); - else - { - solAssert(varDecl->isLocalVariable(), ""); - if (reference.isSlot) - value = IRVariable{*varDecl}.part("slot").name(); - else if (varDecl->type()->isValueType()) - value = IRVariable{*varDecl}.part("offset").name(); - else - { - solAssert(!IRVariable{*varDecl}.hasPart("offset"), ""); - value = "0"; - } - } - - if (isdigit(value.front())) - return yul::Literal{_identifier.location, yul::LiteralKind::Number, yul::YulString{value}, {}}; - else - return yul::Identifier{_identifier.location, yul::YulString{value}}; - } - } - return ASTCopier::operator()(_identifier); + return translateReference(_identifier); + else + return ASTCopier::operator()(_identifier); } yul::YulString translateIdentifier(yul::YulString _name) override @@ -124,24 +93,73 @@ struct CopyTranslate: public yul::ASTCopier if (!m_references.count(&_identifier)) return ASTCopier::translate(_identifier); - auto const& reference = m_references.at(&_identifier); - auto const varDecl = dynamic_cast(reference.declaration); - solUnimplementedAssert(varDecl, ""); - - solAssert( - reference.isOffset == false && reference.isSlot == false, - "Should not be called for offset/slot" - ); - auto const& var = m_context.localVariable(*varDecl); - solAssert(var.type().sizeOnStack() == 1, ""); - - return yul::Identifier{ - _identifier.location, - yul::YulString{var.commaSeparatedList()} - }; + yul::Expression translated = translateReference(_identifier); + solAssert(holds_alternative(translated), ""); + return get(std::move(translated)); } private: + + /// Translates a reference to a local variable, potentially including + /// a suffix. Might return a literal, which causes this to be invalid in + /// lvalue-context. + yul::Expression translateReference(yul::Identifier const& _identifier) + { + auto const& reference = m_references.at(&_identifier); + auto const varDecl = dynamic_cast(reference.declaration); + solUnimplementedAssert(varDecl, ""); + string const& suffix = reference.suffix; + + if (suffix.empty()) + { + auto const& var = m_context.localVariable(*varDecl); + solAssert(var.type().sizeOnStack() == 1, ""); + + return yul::Identifier{ + _identifier.location, + yul::YulString{var.commaSeparatedList()} + }; + } + + string value; + if (varDecl->isStateVariable()) + { + if (suffix == "slot") + value = m_context.storageLocationOfStateVariable(*varDecl).first.str(); + else if (suffix == "offset") + value = to_string(m_context.storageLocationOfStateVariable(*varDecl).second); + else + solAssert(false, ""); + } + else if (varDecl->type()->dataStoredIn(DataLocation::Storage)) + { + solAssert(suffix == "slot" || suffix == "offset", ""); + solAssert(varDecl->isLocalVariable(), ""); + if (suffix == "slot") + value = IRVariable{*varDecl}.part("slot").name(); + else if (varDecl->type()->isValueType()) + value = IRVariable{*varDecl}.part("offset").name(); + else + { + solAssert(!IRVariable{*varDecl}.hasPart("offset"), ""); + value = "0"; + } + } + else if (varDecl->type()->dataStoredIn(DataLocation::CallData)) + { + solAssert(suffix == "offset" || suffix == "length", ""); + value = IRVariable{*varDecl}.part(suffix).name(); + } + else + solAssert(false, ""); + + if (isdigit(value.front())) + return yul::Literal{_identifier.location, yul::LiteralKind::Number, yul::YulString{value}, {}}; + else + return yul::Identifier{_identifier.location, yul::YulString{value}}; + } + + yul::Dialect const& m_dialect; IRGenerationContext& m_context; ExternalRefsMap const& m_references; diff --git a/test/libsolidity/ASTJSON/assembly/slot_offset.json b/test/libsolidity/ASTJSON/assembly/slot_offset.json index 0858fe617..fb2d22008 100644 --- a/test/libsolidity/ASTJSON/assembly/slot_offset.json +++ b/test/libsolidity/ASTJSON/assembly/slot_offset.json @@ -180,6 +180,7 @@ "isOffset": true, "isSlot": false, "src": "106:8:1", + "suffix": "offset", "valueSize": 1 }, { @@ -187,6 +188,7 @@ "isOffset": false, "isSlot": true, "src": "128:6:1", + "suffix": "slot", "valueSize": 1 } ], diff --git a/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json b/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json index 50747baaa..2f13dbf07 100644 --- a/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json @@ -168,6 +168,7 @@ "isOffset": true, "isSlot": false, "src": "106:8:1", + "suffix": "offset", "valueSize": 1 }, { @@ -175,6 +176,7 @@ "isOffset": false, "isSlot": true, "src": "128:6:1", + "suffix": "slot", "valueSize": 1 } ], diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign.sol new file mode 100644 index 000000000..631e61fe0 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint[2][] calldata x) public returns (uint[2][] memory r) { + assembly { x.offset := 0x44 x.length := 2 } + r = x; + } +} +// ---- +// f(uint256[2][]): 0x0, 1, 8, 7, 6, 5 -> 0x20, 2, 8, 7, 6, 5 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_array_read.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_array_read.sol new file mode 100644 index 000000000..bf5a2e75c --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_array_read.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint[2][] calldata x) public returns (uint o, uint l, uint s) { + assembly { l := x.length o := x.offset } + uint[2] calldata t = x[1]; + // statically-sized arrays only use one slot, so we read directly. + assembly { s := t } + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256[2][]): 0x20, 2, 1, 2, 3, 4 -> 0x44, 2, 0x84 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_assign.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_assign.sol new file mode 100644 index 000000000..f17dd975c --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_assign.sol @@ -0,0 +1,10 @@ +contract C { + function f(bytes calldata x) public returns (bytes memory) { + assembly { x.offset := 1 x.length := 3 } + return x; + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes): 0x20, 0, 0 -> 0x20, 3, 0x5754f80000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_assign_from_nowhere.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_assign_from_nowhere.sol new file mode 100644 index 000000000..1b559f27b --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_assign_from_nowhere.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure returns (bytes calldata x) { + assembly { x.offset := 0 x.length := 4 } + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x20, 4, 0x26121ff000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_length_read.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_length_read.sol new file mode 100644 index 000000000..e9d722212 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_length_read.sol @@ -0,0 +1,18 @@ +contract C { + function lenBytesRead(bytes calldata x) public returns (uint l) { + assembly { l := x.length } + } + + function lenStringRead(string calldata x) public returns (uint l) { + assembly { l := x.length } + } +} +// ==== +// compileViaYul: also +// ---- +// lenBytesRead(bytes): 0x20, 4, "abcd" -> 4 +// lenBytesRead(bytes): 0x20, 0, "abcd" -> 0x00 +// lenBytesRead(bytes): 0x20, 0x21, "abcd", "ef" -> 33 +// lenStringRead(string): 0x20, 4, "abcd" -> 4 +// lenStringRead(string): 0x20, 0, "abcd" -> 0x00 +// lenStringRead(string): 0x20, 0x21, "abcd", "ef" -> 33 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read.sol new file mode 100644 index 000000000..77550ae24 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read.sol @@ -0,0 +1,19 @@ +contract C { + function f(bytes calldata x) public returns (uint r) { + assembly { r := x.offset } + } + + function f(uint, bytes calldata x, uint) public returns (uint r, uint v) { + assembly { + r := x.offset + v := x.length + } + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes): 0x20, 0, 0 -> 0x44 +// f(bytes): 0x22, 0, 0, 0 -> 0x46 +// f(uint256,bytes,uint256): 7, 0x60, 8, 2, 0 -> 0x84, 2 +// f(uint256,bytes,uint256): 0, 0, 0 -> 0x24, 0x00 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read_write.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read_write.sol new file mode 100644 index 000000000..37711e9d5 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read_write.sol @@ -0,0 +1,18 @@ +contract C { + function f(uint, bytes calldata x, uint) public returns (uint r, uint v) { + assembly { + x.offset := 8 + x.length := 20 + } + assembly { + r := x.offset + v := x.length + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256,bytes,uint256): 7, 0x60, 8, 2, 0 -> 8, 0x14 +// f(uint256,bytes,uint256): 0, 0, 0 -> 8, 0x14 diff --git a/test/libsolidity/semanticTests/inlineAssembly/slot_access.sol b/test/libsolidity/semanticTests/inlineAssembly/slot_access.sol index abfc6d09b..a1f9874eb 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/slot_access.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/slot_access.sol @@ -25,6 +25,8 @@ contract C { return data().a; } } +// ==== +// compileViaYul: also // ---- // get() -> 0 // mappingAccess(uint256): 1 -> 0, 0 diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array.sol new file mode 100644 index 000000000..5262b68ec --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array.sol @@ -0,0 +1,9 @@ +contract C { + function f(uint[] calldata bytesAsCalldata) external { + assembly { + let x := bytesAsCalldata + } + } +} +// ---- +// TypeError 1397: (112-127): Call data elements cannot be accessed directly. Use ".offset" and ".length" to access the calldata offset and length of this array and then use "calldatacopy". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array_offset.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array_offset.sol new file mode 100644 index 000000000..b6a2844f7 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array_offset.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint[] calldata bytesAsCalldata) external pure { + assembly { + let x := bytesAsCalldata.offset + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_slot.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_slot.sol new file mode 100644 index 000000000..4af7fa91e --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_slot.sol @@ -0,0 +1,9 @@ +contract C { + function f(bytes calldata bytesAsCalldata) external { + assembly { + let x := bytesAsCalldata.slot + } + } +} +// ---- +// TypeError 1536: (111-131): Calldata variables only support ".offset" and ".length". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_variables.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_variables.sol index a7d9da8df..07562ad27 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_variables.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_variables.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// TypeError 2370: (111-126): Call data elements cannot be accessed directly. Copy to a local variable first or use "calldataload" or "calldatacopy" with manually determined offsets and sizes. +// TypeError 1397: (111-126): Call data elements cannot be accessed directly. Use ".offset" and ".length" to access the calldata offset and length of this array and then use "calldatacopy". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment.sol index f833d1740..b01f1ee21 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment.sol @@ -7,4 +7,4 @@ contract test { } } // ---- -// TypeError 1408: (89-90): Only local variables are supported. To access storage variables, use the .slot and .offset suffixes. +// TypeError 1408: (89-90): Only local variables are supported. To access storage variables, use the ".slot" and ".offset" suffixes. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment_in_modifier.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment_in_modifier.sol index a86fdc14b..64cfc8623 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment_in_modifier.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment_in_modifier.sol @@ -10,4 +10,4 @@ contract test { } } // ---- -// TypeError 1408: (80-81): Only local variables are supported. To access storage variables, use the .slot and .offset suffixes. +// TypeError 1408: (80-81): Only local variables are supported. To access storage variables, use the ".slot" and ".offset" suffixes. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_nonslot.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_nonslot.sol new file mode 100644 index 000000000..ba0ef195c --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_nonslot.sol @@ -0,0 +1,12 @@ +contract test { + uint x = 1; + function f() public { + assembly { + let t := x.length + x.length := 2 + } + } +} +// ---- +// TypeError 4656: (98-106): State variables only support ".slot" and ".offset". +// TypeError 4656: (119-127): State variables only support ".slot" and ".offset". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_two_stack_load.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_two_stack_load.sol index 9f51fe572..b1f8ba435 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_two_stack_load.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_two_stack_load.sol @@ -5,4 +5,4 @@ contract c { } } // ---- -// TypeError 1408: (75-76): Only local variables are supported. To access storage variables, use the .slot and .offset suffixes. +// TypeError 1408: (75-76): Only local variables are supported. To access storage variables, use the ".slot" and ".offset" suffixes. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference.sol index 91ea7aea4..47f898019 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// TypeError 9068: (118-119): You have to use the .slot or .offset suffix to access storage reference variables. +// TypeError 9068: (118-119): You have to use the ".slot" or ".offset" suffix to access storage reference variables. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_old.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_old.sol index 4b50fbd6e..d2ca4d7cb 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_old.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_old.sol @@ -9,5 +9,5 @@ contract C { } } // ---- -// DeclarationError 9467: (118-124): Identifier not found. Use ``.slot`` and ``.offset`` to access storage variables. -// DeclarationError 9467: (142-150): Identifier not found. Use ``.slot`` and ``.offset`` to access storage variables. +// DeclarationError 9467: (118-124): Identifier not found. Use ".slot" and ".offset" to access storage variables. +// DeclarationError 9467: (142-150): Identifier not found. Use ".slot" and ".offset" to access storage variables. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol index b5ac0b1d8..66bf81e6f 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// TypeError 7944: (84-90): The suffixes .offset and .slot can only be used on storage variables. +// TypeError 7944: (84-90): The suffixes ".offset", ".slot" and ".length" can only be used with variables. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_memory.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_memory.sol index d85bc8c28..a319c597c 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_memory.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_memory.sol @@ -9,5 +9,5 @@ contract C { } } // ---- -// TypeError 3622: (117-123): The suffixes .offset and .slot can only be used on storage variables. -// TypeError 3622: (141-149): The suffixes .offset and .slot can only be used on storage variables. +// TypeError 3622: (117-123): The suffix ".slot" is not supported by this variable or type. +// TypeError 3622: (141-149): The suffix ".offset" is not supported by this variable or type. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/two_stack_slot_access.sol b/test/libsolidity/syntaxTests/inlineAssembly/two_stack_slot_access.sol new file mode 100644 index 000000000..5b71b61f9 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/two_stack_slot_access.sol @@ -0,0 +1,10 @@ +contract C { + function f() pure external { + function() external two_stack_slots; + assembly { + let x := two_stack_slots + } + } +} +// ---- +// TypeError 9857: (132-147): Only types that use one stack slot are supported. From c0d28492c3d84eaafd7226590db91cb3e623ef09 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Tue, 17 Nov 2020 21:30:06 -0500 Subject: [PATCH 06/10] [ewasm] Polyfill: Rename endian_swap -> bswap. --- libyul/backends/wasm/polyfill/Conversion.yul | 14 ++--- libyul/backends/wasm/polyfill/Interface.yul | 16 ++--- test/cmdlineTests/evm_to_wasm/output | 44 +++++++------- test/cmdlineTests/evm_to_wasm_break/output | 60 +++++++++---------- .../standard_ewasm_requested/output.json | 28 ++++----- 5 files changed, 81 insertions(+), 81 deletions(-) diff --git a/libyul/backends/wasm/polyfill/Conversion.yul b/libyul/backends/wasm/polyfill/Conversion.yul index eef8173c2..3168b80ca 100644 --- a/libyul/backends/wasm/polyfill/Conversion.yul +++ b/libyul/backends/wasm/polyfill/Conversion.yul @@ -59,20 +59,20 @@ function u256_to_address(x1, x2, x3, x4) -> r1, r2, r3 { r3 := x4 } -function endian_swap_16(x) -> y { +function bswap16(x) -> y { let hi := i64.and(i64.shl(x, 8), 0xff00) let lo := i64.and(i64.shr_u(x, 8), 0xff) y := i64.or(hi, lo) } -function endian_swap_32(x) -> y { - let hi := i64.shl(endian_swap_16(x), 16) - let lo := endian_swap_16(i64.shr_u(x, 16)) +function bswap32(x) -> y { + let hi := i64.shl(bswap16(x), 16) + let lo := bswap16(i64.shr_u(x, 16)) y := i64.or(hi, lo) } -function endian_swap(x) -> y { - let hi := i64.shl(endian_swap_32(x), 32) - let lo := endian_swap_32(i64.shr_u(x, 32)) +function bswap64(x) -> y { + let hi := i64.shl(bswap32(x), 32) + let lo := bswap32(i64.shr_u(x, 32)) y := i64.or(hi, lo) } diff --git a/libyul/backends/wasm/polyfill/Interface.yul b/libyul/backends/wasm/polyfill/Interface.yul index 0418c122d..838ea5bba 100644 --- a/libyul/backends/wasm/polyfill/Interface.yul +++ b/libyul/backends/wasm/polyfill/Interface.yul @@ -161,10 +161,10 @@ function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 { } function mload_internal(pos:i32) -> z1, z2, z3, z4 { - z1 := endian_swap(i64.load(pos)) - z2 := endian_swap(i64.load(i32.add(pos, 8:i32))) - z3 := endian_swap(i64.load(i32.add(pos, 16:i32))) - z4 := endian_swap(i64.load(i32.add(pos, 24:i32))) + z1 := bswap64(i64.load(pos)) + z2 := bswap64(i64.load(i32.add(pos, 8:i32))) + z3 := bswap64(i64.load(i32.add(pos, 16:i32))) + z4 := bswap64(i64.load(i32.add(pos, 24:i32))) } function mstore(x1, x2, x3, x4, y1, y2, y3, y4) { @@ -172,10 +172,10 @@ function mstore(x1, x2, x3, x4, y1, y2, y3, y4) { } function mstore_internal(pos:i32, y1, y2, y3, y4) { - i64.store(pos, endian_swap(y1)) - i64.store(i32.add(pos, 8:i32), endian_swap(y2)) - i64.store(i32.add(pos, 16:i32), endian_swap(y3)) - i64.store(i32.add(pos, 24:i32), endian_swap(y4)) + i64.store(pos, bswap64(y1)) + i64.store(i32.add(pos, 8:i32), bswap64(y2)) + i64.store(i32.add(pos, 16:i32), bswap64(y3)) + i64.store(i32.add(pos, 24:i32), bswap64(y4)) } function mstore_address(pos:i32, a1, a2, a3, a4) { diff --git a/test/cmdlineTests/evm_to_wasm/output b/test/cmdlineTests/evm_to_wasm/output index 599380726..772149b0a 100644 --- a/test/cmdlineTests/evm_to_wasm/output +++ b/test/cmdlineTests/evm_to_wasm/output @@ -19,26 +19,26 @@ object "object" { mstore_internal(32:i32, _1, _1, _1, 1) eth.storageStore(0:i32, 32:i32) } - function endian_swap_16(x) -> y + function bswap16(x) -> y { y := i64.or(i64.and(i64.shl(x, 8), 0xff00), i64.and(i64.shr_u(x, 8), 0xff)) } - function endian_swap_32(x) -> y + function bswap32(x) -> y { - let hi := i64.shl(endian_swap_16(x), 16) - y := i64.or(hi, endian_swap_16(i64.shr_u(x, 16))) + let hi := i64.shl(bswap16(x), 16) + y := i64.or(hi, bswap16(i64.shr_u(x, 16))) } - function endian_swap(x) -> y + function bswap64(x) -> y { - let hi := i64.shl(endian_swap_32(x), 32) - y := i64.or(hi, endian_swap_32(i64.shr_u(x, 32))) + let hi := i64.shl(bswap32(x), 32) + y := i64.or(hi, bswap32(i64.shr_u(x, 32))) } function mstore_internal(pos:i32, y1, y2, y3, y4) { - i64.store(pos, endian_swap(y1)) - i64.store(i32.add(pos, 8:i32), endian_swap(y2)) - i64.store(i32.add(pos, 16:i32), endian_swap(y3)) - i64.store(i32.add(pos, 24:i32), endian_swap(y4)) + i64.store(pos, bswap64(y1)) + i64.store(i32.add(pos, 8:i32), bswap64(y2)) + i64.store(i32.add(pos, 16:i32), bswap64(y3)) + i64.store(i32.add(pos, 24:i32), bswap64(y4)) } } } @@ -63,7 +63,7 @@ Text representation: ) ) -(func $endian_swap_16 +(func $bswap16 (param $x i64) (result i64) (local $y i64) @@ -74,27 +74,27 @@ Text representation: (local.get $y) ) -(func $endian_swap_32 +(func $bswap32 (param $x i64) (result i64) (local $y i64) (local $hi i64) (block $label__2 - (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) + (local.set $hi (i64.shl (call $bswap16 (local.get $x)) (i64.const 16))) + (local.set $y (i64.or (local.get $hi) (call $bswap16 (i64.shr_u (local.get $x) (i64.const 16))))) ) (local.get $y) ) -(func $endian_swap +(func $bswap64 (param $x i64) (result i64) (local $y i64) (local $hi i64) (block $label__3 - (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) + (local.set $hi (i64.shl (call $bswap32 (local.get $x)) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $bswap32 (i64.shr_u (local.get $x) (i64.const 32))))) ) (local.get $y) @@ -107,10 +107,10 @@ Text representation: (param $y3 i64) (param $y4 i64) (block $label__4 - (i64.store (local.get $pos) (call $endian_swap (local.get $y1))) - (i64.store (i32.add (local.get $pos) (i32.const 8)) (call $endian_swap (local.get $y2))) - (i64.store (i32.add (local.get $pos) (i32.const 16)) (call $endian_swap (local.get $y3))) - (i64.store (i32.add (local.get $pos) (i32.const 24)) (call $endian_swap (local.get $y4))) + (i64.store (local.get $pos) (call $bswap64 (local.get $y1))) + (i64.store (i32.add (local.get $pos) (i32.const 8)) (call $bswap64 (local.get $y2))) + (i64.store (i32.add (local.get $pos) (i32.const 16)) (call $bswap64 (local.get $y3))) + (i64.store (i32.add (local.get $pos) (i32.const 24)) (call $bswap64 (local.get $y4))) ) ) diff --git a/test/cmdlineTests/evm_to_wasm_break/output b/test/cmdlineTests/evm_to_wasm_break/output index 0385cea1f..3c63bc830 100644 --- a/test/cmdlineTests/evm_to_wasm_break/output +++ b/test/cmdlineTests/evm_to_wasm_break/output @@ -106,29 +106,29 @@ object "object" { default { z := 1:i32 } z4 := i64.extend_i32_u(z) } - function endian_swap_16(x) -> y + function bswap16(x) -> y { y := i64.or(i64.and(i64.shl(x, 8), 0xff00), i64.and(i64.shr_u(x, 8), 0xff)) } - function endian_swap_32(x) -> y + function bswap32(x) -> y { - let hi := i64.shl(endian_swap_16(x), 16) - y := i64.or(hi, endian_swap_16(i64.shr_u(x, 16))) + let hi := i64.shl(bswap16(x), 16) + y := i64.or(hi, bswap16(i64.shr_u(x, 16))) } - function endian_swap(x) -> y + function bswap64(x) -> y { - let hi := i64.shl(endian_swap_32(x), 32) - y := i64.or(hi, endian_swap_32(i64.shr_u(x, 32))) + let hi := i64.shl(bswap32(x), 32) + y := i64.or(hi, bswap32(i64.shr_u(x, 32))) } function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 { if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { unreachable() } if i64.ne(0, i64.shr_u(x4, 32)) { unreachable() } eth.callDataCopy(0:i32, i32.wrap_i64(x4), 32:i32) - let z1_1 := endian_swap(i64.load(0:i32)) - let z2_1 := endian_swap(i64.load(i32.add(0:i32, 8:i32))) - let z3_1 := endian_swap(i64.load(i32.add(0:i32, 16:i32))) - let z4_1 := endian_swap(i64.load(i32.add(0:i32, 24:i32))) + let z1_1 := bswap64(i64.load(0:i32)) + let z2_1 := bswap64(i64.load(i32.add(0:i32, 8:i32))) + let z3_1 := bswap64(i64.load(i32.add(0:i32, 16:i32))) + let z4_1 := bswap64(i64.load(i32.add(0:i32, 24:i32))) z1 := z1_1 z2 := z2_1 z3 := z3_1 @@ -136,10 +136,10 @@ object "object" { } function mstore_internal(pos:i32, y1, y2, y3, y4) { - i64.store(pos, endian_swap(y1)) - i64.store(i32.add(pos, 8:i32), endian_swap(y2)) - i64.store(i32.add(pos, 16:i32), endian_swap(y3)) - i64.store(i32.add(pos, 24:i32), endian_swap(y4)) + i64.store(pos, bswap64(y1)) + i64.store(i32.add(pos, 8:i32), bswap64(y2)) + i64.store(i32.add(pos, 16:i32), bswap64(y3)) + i64.store(i32.add(pos, 24:i32), bswap64(y4)) } function sstore(x1, x2, x3, x4, y1, y2, y3, y4) { @@ -437,7 +437,7 @@ Text representation: (local.get $z4) ) -(func $endian_swap_16 +(func $bswap16 (param $x i64) (result i64) (local $y i64) @@ -448,27 +448,27 @@ Text representation: (local.get $y) ) -(func $endian_swap_32 +(func $bswap32 (param $x i64) (result i64) (local $y i64) (local $hi i64) (block $label__16 - (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) + (local.set $hi (i64.shl (call $bswap16 (local.get $x)) (i64.const 16))) + (local.set $y (i64.or (local.get $hi) (call $bswap16 (i64.shr_u (local.get $x) (i64.const 16))))) ) (local.get $y) ) -(func $endian_swap +(func $bswap64 (param $x i64) (result i64) (local $y i64) (local $hi i64) (block $label__17 - (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) + (local.set $hi (i64.shl (call $bswap32 (local.get $x)) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $bswap32 (i64.shr_u (local.get $x) (i64.const 32))))) ) (local.get $y) @@ -494,10 +494,10 @@ Text representation: (if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then (unreachable))) (call $eth.callDataCopy (i32.const 0) (i32.wrap_i64 (local.get $x4)) (i32.const 32)) - (local.set $z1_1 (call $endian_swap (i64.load (i32.const 0)))) - (local.set $z2_1 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 8))))) - (local.set $z3_1 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 16))))) - (local.set $z4_1 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 24))))) + (local.set $z1_1 (call $bswap64 (i64.load (i32.const 0)))) + (local.set $z2_1 (call $bswap64 (i64.load (i32.add (i32.const 0) (i32.const 8))))) + (local.set $z3_1 (call $bswap64 (i64.load (i32.add (i32.const 0) (i32.const 16))))) + (local.set $z4_1 (call $bswap64 (i64.load (i32.add (i32.const 0) (i32.const 24))))) (local.set $z1 (local.get $z1_1)) (local.set $z2 (local.get $z2_1)) (local.set $z3 (local.get $z3_1)) @@ -517,10 +517,10 @@ Text representation: (param $y3 i64) (param $y4 i64) (block $label__19 - (i64.store (local.get $pos) (call $endian_swap (local.get $y1))) - (i64.store (i32.add (local.get $pos) (i32.const 8)) (call $endian_swap (local.get $y2))) - (i64.store (i32.add (local.get $pos) (i32.const 16)) (call $endian_swap (local.get $y3))) - (i64.store (i32.add (local.get $pos) (i32.const 24)) (call $endian_swap (local.get $y4))) + (i64.store (local.get $pos) (call $bswap64 (local.get $y1))) + (i64.store (i32.add (local.get $pos) (i32.const 8)) (call $bswap64 (local.get $y2))) + (i64.store (i32.add (local.get $pos) (i32.const 16)) (call $bswap64 (local.get $y3))) + (i64.store (i32.add (local.get $pos) (i32.const 24)) (call $bswap64 (local.get $y4))) ) ) diff --git a/test/cmdlineTests/standard_ewasm_requested/output.json b/test/cmdlineTests/standard_ewasm_requested/output.json index 4a4487310..f8ed91e85 100644 --- a/test/cmdlineTests/standard_ewasm_requested/output.json +++ b/test/cmdlineTests/standard_ewasm_requested/output.json @@ -1,6 +1,6 @@ {"contracts":{"A":{"C":{"ewasm":{"wasm":"0061736d01000000013a0860000060017e017e60047e7e7e7e017f60087e7e7e7e7e7e7e7e00600c7e7e7e7e7e7e7e7e7e7e7e7e0060017f0060027f7f0060037f7f7f0002510408657468657265756d08636f6465436f7079000708657468657265756d06726576657274000608657468657265756d0c67657443616c6c56616c7565000508657468657265756d0666696e6973680006030a090002020101010403030503010001060100071102066d656d6f72790200046d61696e0004009d030c435f325f6465706c6f7965640061736d0100000001160460000060017e017e60047e7e7e7e017f60027f7f0002130108657468657265756d067265766572740003030504000201010503010001060100071102066d656d6f72790200046d61696e00010ab60204ca0104017e027f057e037f02404200210020002000200042c00010022101200141c0006a210220022001490440000b20001003421086210320032000421088100384422086210420042000422088100484210520022005370000200241086a2005370000200241106a20053700004280011003421086210620064280014210881003844220862107200241186a2007428001422088100484370000200020002000200010022108200020002000200010022109200941c0006a210a200a2009490440000b200a200810000b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100342108621022002200042108810038421010b20010b0aec0309dc0103017e027f057e02404200210020002000200042c00010052101200141c0006a210220022001490440000b20001009210320022003370000200241086a2003370000200241106a2003370000200241186a428001100937000041001002410029000010092104410041086a29000010092105410041106a2900001009210620042005842006410041186a290000100984845045044020002000200020002000200020002000100c0b4290032107200020002000200020002000200042ce012000200020002007100a20002000200020002000200020002007100b0b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b2601027f0240200020012002200310052105200541c0006a210420042005490440000b0b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100742108621022002200042108810078421010b20010b1e01027e02402000100842208621022002200042208810088421010b20010b25000240200020012002200310062004200520062007100520082009200a200b100510000b0b1b000240200020012002200310062004200520062007100510030b0b1b000240200020012002200310062004200520062007100510010b0b","wast":"(module ;; custom section for sub-module - ;; The Keccak-256 hash of the text representation of \"C_2_deployed\": f03f5b9154b9eb6803a947177e38e92e2860de95e90ba0e75eb71a58f18ed589 + ;; The Keccak-256 hash of the text representation of \"C_2_deployed\": 0289c074ac70ccfdbeb7817862087cc066a9f7707de1a981bb8b5b12dd2ce4e9 ;; (@custom \"C_2_deployed\" \"0061736d0100000001160460000060017e017e60047e7e7e7e017f60027f7f0002130108657468657265756d067265766572740003030504000201010503010001060100071102066d656d6f72790200046d61696e00010ab60204ca0104017e027f057e037f02404200210020002000200042c00010022101200141c0006a210220022001490440000b20001003421086210320032000421088100384422086210420042000422088100484210520022005370000200241086a2005370000200241106a20053700004280011003421086210620064280014210881003844220862107200241186a2007428001422088100484370000200020002000200010022108200020002000200010022109200941c0006a210a200a2009490440000b200a200810000b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100342108621022002200042108810038421010b20010b\") (import \"ethereum\" \"codeCopy\" (func $eth.codeCopy (param i32 i32 i32))) (import \"ethereum\" \"revert\" (func $eth.revert (param i32 i32))) @@ -24,16 +24,16 @@ (local.set $r (i32.add (local.get $p) (i32.const 64))) (if (i32.lt_u (local.get $r) (local.get $p)) (then (unreachable))) - (local.set $_2 (call $endian_swap (local.get $_1))) + (local.set $_2 (call $bswap64 (local.get $_1))) (i64.store (local.get $r) (local.get $_2)) (i64.store (i32.add (local.get $r) (i32.const 8)) (local.get $_2)) (i64.store (i32.add (local.get $r) (i32.const 16)) (local.get $_2)) - (i64.store (i32.add (local.get $r) (i32.const 24)) (call $endian_swap (i64.const 128))) + (i64.store (i32.add (local.get $r) (i32.const 24)) (call $bswap64 (i64.const 128))) (call $eth.getCallValue (i32.const 0)) - (local.set $z1 (call $endian_swap (i64.load (i32.const 0)))) - (local.set $z2 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 8))))) - (local.set $z3 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 16))))) - (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 24)))))))) (then + (local.set $z1 (call $bswap64 (i64.load (i32.const 0)))) + (local.set $z2 (call $bswap64 (i64.load (i32.add (i32.const 0) (i32.const 8))))) + (local.set $z3 (call $bswap64 (i64.load (i32.add (i32.const 0) (i32.const 16))))) + (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (call $bswap64 (i64.load (i32.add (i32.const 0) (i32.const 24)))))))) (then (call $revert (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)))) (local.set $_3 (datasize \"C_2_deployed\")) (call $codecopy (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\") (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3)) @@ -77,7 +77,7 @@ (local.get $r) ) -(func $endian_swap_16 +(func $bswap16 (param $x i64) (result i64) (local $y i64) @@ -88,27 +88,27 @@ (local.get $y) ) -(func $endian_swap_32 +(func $bswap32 (param $x i64) (result i64) (local $y i64) (local $hi i64) (block $label__4 - (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) + (local.set $hi (i64.shl (call $bswap16 (local.get $x)) (i64.const 16))) + (local.set $y (i64.or (local.get $hi) (call $bswap16 (i64.shr_u (local.get $x) (i64.const 16))))) ) (local.get $y) ) -(func $endian_swap +(func $bswap64 (param $x i64) (result i64) (local $y i64) (local $hi i64) (block $label__5 - (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) + (local.set $hi (i64.shl (call $bswap32 (local.get $x)) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $bswap32 (i64.shr_u (local.get $x) (i64.const 32))))) ) (local.get $y) From 1508b08f3ef817547f4d7b7e4b7ba085f160fa1e Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 11 Nov 2020 18:59:00 +0100 Subject: [PATCH 07/10] Implement inline assembly access to constants. --- .../codegen/ir/IRGeneratorForStatements.cpp | 46 ++++++++++++++++++- .../asm_address_constant_regression.sol | 2 + .../inlineAssembly/constant_access.sol | 2 + .../constant_access_referencing.sol | 2 + 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 92b80e875..88f3e7263 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -110,7 +111,7 @@ private: solUnimplementedAssert(varDecl, ""); string const& suffix = reference.suffix; - if (suffix.empty()) + if (suffix.empty() && !varDecl->isStateVariable()) { auto const& var = m_context.localVariable(*varDecl); solAssert(var.type().sizeOnStack() == 1, ""); @@ -122,7 +123,48 @@ private: } string value; - if (varDecl->isStateVariable()) + if (varDecl->isConstant()) + { + VariableDeclaration const* variable = rootConstVariableDeclaration(*varDecl); + solAssert(variable, ""); + + if (variable->value()->annotation().type->category() == Type::Category::RationalNumber) + { + u256 intValue = dynamic_cast(*variable->value()->annotation().type).literalValue(nullptr); + if (auto const* bytesType = dynamic_cast(variable->type())) + intValue <<= 256 - 8 * bytesType->numBytes(); + else + solAssert(variable->type()->category() == Type::Category::Integer, ""); + value = intValue.str(); + } + else if (auto const* literal = dynamic_cast(variable->value().get())) + { + TypePointer type = literal->annotation().type; + + switch (type->category()) + { + case Type::Category::Bool: + case Type::Category::Address: + solAssert(type->category() == variable->annotation().type->category(), ""); + value = toCompactHexWithPrefix(type->literalValue(literal)); + break; + case Type::Category::StringLiteral: + { + auto const& stringLiteral = dynamic_cast(*type); + solAssert(variable->type()->category() == Type::Category::FixedBytes, ""); + unsigned const numBytes = dynamic_cast(*variable->type()).numBytes(); + solAssert(stringLiteral.value().size() <= numBytes, ""); + value = formatNumber(u256(h256(stringLiteral.value(), h256::AlignLeft))); + break; + } + default: + solAssert(false, ""); + } + } + else + solAssert(false, "Invalid constant in inline assembly."); + } + else if (varDecl->isStateVariable()) { if (suffix == "slot") value = m_context.storageLocationOfStateVariable(*varDecl).first.str(); diff --git a/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol b/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol index 730b04746..823a94d30 100644 --- a/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol +++ b/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol @@ -8,5 +8,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x00 diff --git a/test/libsolidity/semanticTests/inlineAssembly/constant_access.sol b/test/libsolidity/semanticTests/inlineAssembly/constant_access.sol index 835048b85..9b3f4c93d 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/constant_access.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/constant_access.sol @@ -14,5 +14,7 @@ contract C { } } } +// ==== +// compileViaYul: also // ---- // f() -> 2, left(0xabcd), left(0x616263), true, 0x1212121212121212121212121212121212121212 diff --git a/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol b/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol index d2e24a70e..74d563222 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol @@ -22,5 +22,7 @@ contract C { } } } +// ==== +// compileViaYul: also // ---- // f() -> 2, left(0xabcd), left(0x616263), true, 0x1212121212121212121212121212121212121212 From 05b8d5590023b36cceae84ddd0456aaecaab2940 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 18 Nov 2020 11:07:56 +0100 Subject: [PATCH 08/10] Sort changelog. --- Changelog.md | 35 +++++++++++++++++------------------ docs/bugs_by_version.json | 4 ++++ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/Changelog.md b/Changelog.md index 87678abbf..1cb97d464 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -### 0.7.5 (unreleased) +### 0.7.5 (2020-11-18) Language Features: * Ability to select the abi coder using ``pragma abicoder v1`` and ``pragma abicoder v2``. @@ -6,33 +6,32 @@ Language Features: * Immutable variables with literal number values are considered pure. Compiler Features: - * Command Line Interface: New option ``--experimental-via-ir`` allows switching compilation process to go through - the Yul intermediate representation. This is highly experimental and is used for development purposes. - * Standard JSON: New option ``settings.viaIR`` allows the same switch as ``--experimental-via-ir`` on the commandline. + * Assembler: Perform linking in assembly mode when library addresses are provided. + * Command Line Interface: New option ``--experimental-via-ir`` allows switching compilation process to go through the Yul intermediate representation. This is highly experimental and is used for development purposes. + * Command Line Interface: New option ``--model-checker-timeout`` sets a timeout in milliseconds for each individual query performed by the SMTChecker. * Command Line Interface: Report error if file could not be read in ``--standard-json`` mode. * Command Line interface: Report proper error for each output file which could not be written. Previously an exception was thrown, and execution aborted, on the first error. * SMTChecker: Add division by zero checks in the CHC engine. - * SMTChecker: Support ``selector`` for expressions with value known at compile-time. * SMTChecker: More precise analysis of external calls using ``this``. - * Command Line Interface: New option ``--model-checker-timeout`` sets a timeout in milliseconds for each individual query performed by the SMTChecker. + * SMTChecker: Support ``selector`` for expressions with value known at compile-time. * Standard JSON: New option ``modelCheckerSettings.timeout`` sets a timeout in milliseconds for each individual query performed by the SMTChecker. - * Assembler: Perform linking in assembly mode when library addresses are provided. + * Standard JSON: New option ``settings.viaIR`` allows the same switch as ``--experimental-via-ir`` on the commandline. Bugfixes: - * Command Line Interface: Reject duplicate libraries in ``--libraries`` option instead of arbitrarily choosing one. - * SMTChecker: Fix lack of reporting potential violations when using only the CHC engine. - * SMTChecker: Fix internal error on conversion from string literal to byte. - * SMTChecker: Fix internal error when using tuples of rational literals inside the conditional operator. - * SMTChecker: Fix internal error when assigning state variable via contract's name. - * SMTChecker: Fix incorrect counterexamples reported by the CHC engine. - * SMTChecker: Fix false negative in modifier applied multiple times. - * SMTChecker: Fix internal error in the BMC engine when inherited contract from a different source unit has private state variables. - * SMTChecker: Fix internal error when ``array.push()`` is used as the LHS of an assignment. - * Command Line Interface: Fix write error when the directory passed to ``--output-dir`` ends with a slash. - * SMTChecker: Fix CHC false positives when branches are used inside modifiers. * Code generator: Fix missing creation dependency tracking for abstract contracts. + * Command Line Interface: Fix write error when the directory passed to ``--output-dir`` ends with a slash. + * Command Line Interface: Reject duplicate libraries in ``--libraries`` option instead of arbitrarily choosing one. * NatSpec: Fix internal error when inheriting return parameter documentation but the parameter names differ between base and inherited. + * SMTChecker: Fix CHC false positives when branches are used inside modifiers. + * SMTChecker: Fix false negative in modifier applied multiple times. + * SMTChecker: Fix incorrect counterexamples reported by the CHC engine. + * SMTChecker: Fix internal error in the BMC engine when inherited contract from a different source unit has private state variables. + * SMTChecker: Fix internal error on conversion from string literal to byte. + * SMTChecker: Fix internal error when ``array.push()`` is used as the LHS of an assignment. + * SMTChecker: Fix internal error when assigning state variable via contract's name. + * SMTChecker: Fix internal error when using tuples of rational literals inside the conditional operator. + * SMTChecker: Fix lack of reporting potential violations when using only the CHC engine. * Standard JSON: Fix library addresses specified in ``libraries`` being used for linking even if the file names do not match. AST Changes: diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index f41ff355f..8c0921ffd 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1369,5 +1369,9 @@ "0.7.4": { "bugs": [], "released": "2020-10-19" + }, + "0.7.5": { + "bugs": [], + "released": "2020-11-18" } } \ No newline at end of file From 5560239cf5d243e3c3d83adf129f2335fed6c549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 18 Nov 2020 15:20:34 +0100 Subject: [PATCH 09/10] Replace links to readthedocs.io with ones using the new domain --- .circleci/osx_install_dependencies.sh | 2 +- .circleci/soltest.sh | 2 +- .circleci/soltest_all.sh | 2 +- CONTRIBUTING.md | 2 +- README.md | 14 +++++++------- SECURITY.md | 2 +- docs/using-the-compiler.rst | 2 +- libsolidity/analysis/TypeChecker.cpp | 2 +- libsolidity/interface/ABI.cpp | 2 +- libsolidity/interface/ABI.h | 2 +- scripts/build_emscripten.sh | 2 +- scripts/ci/build_emscripten.sh | 2 +- scripts/docs.sh | 2 +- scripts/get_nightly_version.sh | 2 +- scripts/get_version.sh | 2 +- scripts/install_deps.bat | 2 +- scripts/install_deps.sh | 14 +++++++------- scripts/release.bat | 2 +- scripts/release.sh | 2 +- scripts/test_emscripten.sh | 2 +- scripts/tests.sh | 2 +- scripts/yul_coverage.sh | 2 +- test/cmdlineTests.sh | 2 +- .../406_invalid_address_checksum.sol | 2 +- .../407_invalid_address_no_checksum.sol | 2 +- .../408_invalid_address_length_short.sol | 2 +- .../409_invalid_address_length_long.sol | 2 +- 27 files changed, 39 insertions(+), 39 deletions(-) diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 64a83f56b..4041685b4 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -4,7 +4,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/.circleci/soltest.sh b/.circleci/soltest.sh index 41ab71875..3eb15d051 100755 --- a/.circleci/soltest.sh +++ b/.circleci/soltest.sh @@ -4,7 +4,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # Configuration Environment Variables: diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index 02b675aa1..8281131b8 100755 --- a/.circleci/soltest_all.sh +++ b/.circleci/soltest_all.sh @@ -4,7 +4,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 97a4be145..e071a2290 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ # Contribution Guidelines -Please see our contribution guidelines in [the Solidity documentation](https://solidity.readthedocs.io/en/latest/contributing.html). +Please see our contribution guidelines in [the Solidity documentation](https://docs.soliditylang.org/en/latest/contributing.html). Thank you for your help! diff --git a/README.md b/README.md index 293cc2583..2eeda0dcb 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ number [to indicate this fast pace of change](https://semver.org/#spec-item-4). ## Build and Install Instructions about how to build and install the Solidity compiler can be -found in the [Solidity documentation](https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source). +found in the [Solidity documentation](https://docs.soliditylang.org/en/latest/installing-solidity.html#building-from-source). ## Example @@ -49,20 +49,20 @@ contract HelloWorld { To get started with Solidity, you can use [Remix](https://remix.ethereum.org/), which is a browser-based IDE. Here are some example contracts: -1. [Voting](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#voting) -2. [Blind Auction](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#blind-auction) -3. [Safe remote purchase](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#safe-remote-purchase) -4. [Micropayment Channel](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#micropayment-channel) +1. [Voting](https://docs.soliditylang.org/en/latest/solidity-by-example.html#voting) +2. [Blind Auction](https://docs.soliditylang.org/en/latest/solidity-by-example.html#blind-auction) +3. [Safe remote purchase](https://docs.soliditylang.org/en/latest/solidity-by-example.html#safe-remote-purchase) +4. [Micropayment Channel](https://docs.soliditylang.org/en/latest/solidity-by-example.html#micropayment-channel) ## Documentation -The Solidity documentation is hosted at [Read the docs](https://solidity.readthedocs.io). +The Solidity documentation is hosted at [Read the docs](https://docs.soliditylang.org). ## Development Solidity is still under development. Contributions are always welcome! Please follow the -[Developers Guide](https://solidity.readthedocs.io/en/latest/contributing.html) +[Developers Guide](https://docs.soliditylang.org/en/latest/contributing.html) if you want to help. You can find our current feature and bug priorities for forthcoming diff --git a/SECURITY.md b/SECURITY.md index dd39453e8..d319b332f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -48,5 +48,5 @@ The Solidity team maintains the following JSON-formatted lists of patched securi [1]: https://bounty.ethereum.org/ [2]: https://bounty.ethereum.org/#rules -[3]: https://solidity.readthedocs.io/en/develop/bugs.html +[3]: https://docs.soliditylang.org/en/develop/bugs.html [4]: https://github.com/ethereum/solidity/blob/develop/docs/bugs_by_version.json diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 7bf4cfd5c..8504f3e0f 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -441,7 +441,7 @@ Output Description // If the language used has no contract names, this field should equal to an empty string. "ContractName": { // The Ethereum Contract ABI. If empty, it is represented as an empty array. - // See https://solidity.readthedocs.io/en/develop/abi-spec.html + // See https://docs.soliditylang.org/en/develop/abi-spec.html "abi": [], // See the Metadata Output documentation (serialised JSON string) "metadata": "{...}", diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index fb806a8a2..0ddd69229 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3255,7 +3255,7 @@ void TypeChecker::endVisit(Literal const& _literal) _literal.location(), msg + " If this is not used as an address, please prepend '00'. " + - "For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals" + "For more information please see https://docs.soliditylang.org/en/develop/types.html#address-literals" ); } diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index bed432cd6..70dbc1b3a 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 /** - * Utilities to handle the Contract ABI (https://solidity.readthedocs.io/en/develop/abi-spec.html) + * Utilities to handle the Contract ABI (https://docs.soliditylang.org/en/develop/abi-spec.html) */ #include diff --git a/libsolidity/interface/ABI.h b/libsolidity/interface/ABI.h index 76b1a4a75..d13bbdd8b 100644 --- a/libsolidity/interface/ABI.h +++ b/libsolidity/interface/ABI.h @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 /** - * Utilities to handle the Contract ABI (https://solidity.readthedocs.io/en/develop/abi-spec.html) + * Utilities to handle the Contract ABI (https://docs.soliditylang.org/en/develop/abi-spec.html) */ #pragma once diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index c1845b1c6..8f8208dd0 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/ci/build_emscripten.sh b/scripts/ci/build_emscripten.sh index e40ba3a89..0ebbe9e22 100755 --- a/scripts/ci/build_emscripten.sh +++ b/scripts/ci/build_emscripten.sh @@ -11,7 +11,7 @@ # # The documentation for solidity is hosted at: # -# http://solidity.readthedocs.io/ +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/docs.sh b/scripts/docs.sh index 91f619d89..4d31277da 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/get_nightly_version.sh b/scripts/get_nightly_version.sh index bbac84638..48e953966 100755 --- a/scripts/get_nightly_version.sh +++ b/scripts/get_nightly_version.sh @@ -6,7 +6,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/get_version.sh b/scripts/get_version.sh index 180ff3cd6..b2abc6e34 100755 --- a/scripts/get_version.sh +++ b/scripts/get_version.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/install_deps.bat b/scripts/install_deps.bat index d02005ccd..61f7022a5 100644 --- a/scripts/install_deps.bat +++ b/scripts/install_deps.bat @@ -37,7 +37,7 @@ REM for those packages. REM REM The documentation for solidity is hosted at: REM -REM http://solidity.readthedocs.org +REM https://docs.soliditylang.org REM REM --------------------------------------------------------------------------- REM This file is part of solidity. diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 87f889e8f..f71434e44 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -6,7 +6,7 @@ # # This is an "infrastucture-as-code" alternative to the manual build # instructions pages which we previously maintained at: -# http://solidity.readthedocs.io/en/latest/installing-solidity.html +# https://docs.soliditylang.org/en/latest/installing-solidity.html # # The aim of this script is to simplify things down to the following basic # flow for all supported operating systems: @@ -23,7 +23,7 @@ # # The documentation for solidity is hosted at: # -# http://solidity.readthedocs.io/ +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. @@ -179,7 +179,7 @@ case $(uname -s) in #wheezy echo "Installing solidity dependencies on Debian Wheezy (7.x)." echo "ERROR - 'install_deps.sh' doesn't have Debian Wheezy support yet." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get 'install_deps.sh' working for Debian Wheezy, that would be fantastic." echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support." @@ -254,7 +254,7 @@ case $(uname -s) in #openSUSE echo "Installing solidity dependencies on openSUSE." echo "ERROR - 'install_deps.sh' doesn't have openSUSE support yet." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get 'install_deps.sh' working for openSUSE, that would be fantastic." echo "See https://github.com/ethereum/webthree-umbrella/issues/552." exit 1 @@ -311,7 +311,7 @@ case $(uname -s) in #do not try anything for betsy. echo "Linux Mint Betsy is not supported at the moment as it runs off of Debian." echo "We only support Sylvia, Sonya, Serena, Sarah, Rosa, Rafaela, Rebecca, and Qiana." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get your distro working, that would be fantastic." echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." exit 1 @@ -396,7 +396,7 @@ case $(uname -s) in #other Linux echo "ERROR - Unsupported or unidentified Linux distro." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get your distro working, that would be fantastic." echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." exit 1 @@ -413,7 +413,7 @@ case $(uname -s) in *) #other echo "ERROR - Unsupported or unidentified operating system." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get your operating system working, that would be fantastic." echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." ;; diff --git a/scripts/release.bat b/scripts/release.bat index caa56fc9a..a1a909ae0 100644 --- a/scripts/release.bat +++ b/scripts/release.bat @@ -5,7 +5,7 @@ REM Batch file for implementing release flow for solidity for Windows. REM REM The documentation for solidity is hosted at: REM -REM https://solidity.readthedocs.org +REM https://docs.soliditylang.org REM REM --------------------------------------------------------------------------- REM This file is part of solidity. diff --git a/scripts/release.sh b/scripts/release.sh index 76d57e2ff..42e062fde 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -8,7 +8,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/test_emscripten.sh b/scripts/test_emscripten.sh index 980cee196..44045540a 100755 --- a/scripts/test_emscripten.sh +++ b/scripts/test_emscripten.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/tests.sh b/scripts/tests.sh index 8bb08d963..11191c86e 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/yul_coverage.sh b/scripts/yul_coverage.sh index c32dc9472..02b9a0476 100755 --- a/scripts/yul_coverage.sh +++ b/scripts/yul_coverage.sh @@ -30,7 +30,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index acf029d1c..86fb66670 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/406_invalid_address_checksum.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/406_invalid_address_checksum.sol index dd405ab72..2abd832f1 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/406_invalid_address_checksum.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/406_invalid_address_checksum.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// SyntaxError 9429: (64-106): This looks like an address but has an invalid checksum. Correct checksummed address: "0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E". If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals +// SyntaxError 9429: (64-106): This looks like an address but has an invalid checksum. Correct checksummed address: "0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E". If this is not used as an address, please prepend '00'. For more information please see https://docs.soliditylang.org/en/develop/types.html#address-literals diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/407_invalid_address_no_checksum.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/407_invalid_address_no_checksum.sol index 2a1a402c7..2299c4e85 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/407_invalid_address_no_checksum.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/407_invalid_address_no_checksum.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// SyntaxError 9429: (64-106): This looks like an address but has an invalid checksum. Correct checksummed address: "0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E". If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals +// SyntaxError 9429: (64-106): This looks like an address but has an invalid checksum. Correct checksummed address: "0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E". If this is not used as an address, please prepend '00'. For more information please see https://docs.soliditylang.org/en/develop/types.html#address-literals diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/408_invalid_address_length_short.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/408_invalid_address_length_short.sol index 49e115ee9..d0ee67e85 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/408_invalid_address_length_short.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/408_invalid_address_length_short.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// SyntaxError 9429: (64-105): This looks like an address but is not exactly 40 hex digits. It is 39 hex digits. If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals +// SyntaxError 9429: (64-105): This looks like an address but is not exactly 40 hex digits. It is 39 hex digits. If this is not used as an address, please prepend '00'. For more information please see https://docs.soliditylang.org/en/develop/types.html#address-literals diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/409_invalid_address_length_long.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/409_invalid_address_length_long.sol index 17eeb3b91..bc5fc8b99 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/409_invalid_address_length_long.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/409_invalid_address_length_long.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// SyntaxError 9429: (64-107): This looks like an address but is not exactly 40 hex digits. It is 41 hex digits. If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals +// SyntaxError 9429: (64-107): This looks like an address but is not exactly 40 hex digits. It is 41 hex digits. If this is not used as an address, please prepend '00'. For more information please see https://docs.soliditylang.org/en/develop/types.html#address-literals From 1f3da1d265a7b6390d02c379260a00dc23b97c69 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 18 Nov 2020 18:46:11 +0100 Subject: [PATCH 10/10] Set version to 0.7.6.. --- CMakeLists.txt | 2 +- Changelog.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f9e2f1b3..829f8ab24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.7.5") +set(PROJECT_VERSION "0.7.6") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) diff --git a/Changelog.md b/Changelog.md index 1cb97d464..9e8a65618 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,6 @@ +### 0.7.6 (unreleased) + + ### 0.7.5 (2020-11-18) Language Features: