From 9264135bef2365cf1a1caec4d7eca8a99de5d80a Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Wed, 6 May 2020 17:58:57 +0200 Subject: [PATCH 01/16] Add tests to validate clearing dirty bits via inline assembly operations --- .../semanticTests/dirty_calldata_bytes.sol | 12 ++++++++++ .../dirty_calldata_dynamic_array.sol | 12 ++++++++++ .../dirty_memory_bytes_to_storgage_copy.sol | 16 +++++++++++++ .../viaYul/dirty_calldata_struct.sol | 18 +++++++++++++++ .../viaYul/dirty_memory_dynamic_array.sol | 18 +++++++++++++++ .../viaYul/dirty_memory_int32.sol | 18 +++++++++++++++ .../viaYul/dirty_memory_read.sol | 15 ------------ .../viaYul/dirty_memory_static_array.sol | 18 +++++++++++++++ .../viaYul/dirty_memory_struct.sol | 22 ++++++++++++++++++ .../viaYul/dirty_memory_uint32.sol | 18 +++++++++++++++ .../viaYul/storage/dirty_storage_bytes.sol | 18 +++++++++++++++ .../storage/dirty_storage_bytes_long.sol | 20 ++++++++++++++++ .../storage/dirty_storage_dynamic_array.sol | 20 ++++++++++++++++ .../storage/dirty_storage_static_array.sol | 18 +++++++++++++++ .../viaYul/storage/dirty_storage_struct.sol | 23 +++++++++++++++++++ 15 files changed, 251 insertions(+), 15 deletions(-) create mode 100644 test/libsolidity/semanticTests/dirty_calldata_bytes.sol create mode 100644 test/libsolidity/semanticTests/dirty_calldata_dynamic_array.sol create mode 100644 test/libsolidity/semanticTests/dirty_memory_bytes_to_storgage_copy.sol create mode 100644 test/libsolidity/semanticTests/viaYul/dirty_calldata_struct.sol create mode 100644 test/libsolidity/semanticTests/viaYul/dirty_memory_dynamic_array.sol create mode 100644 test/libsolidity/semanticTests/viaYul/dirty_memory_int32.sol delete mode 100644 test/libsolidity/semanticTests/viaYul/dirty_memory_read.sol create mode 100644 test/libsolidity/semanticTests/viaYul/dirty_memory_static_array.sol create mode 100644 test/libsolidity/semanticTests/viaYul/dirty_memory_struct.sol create mode 100644 test/libsolidity/semanticTests/viaYul/dirty_memory_uint32.sol create mode 100644 test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes.sol create mode 100644 test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes_long.sol create mode 100644 test/libsolidity/semanticTests/viaYul/storage/dirty_storage_dynamic_array.sol create mode 100644 test/libsolidity/semanticTests/viaYul/storage/dirty_storage_static_array.sol create mode 100644 test/libsolidity/semanticTests/viaYul/storage/dirty_storage_struct.sol diff --git a/test/libsolidity/semanticTests/dirty_calldata_bytes.sol b/test/libsolidity/semanticTests/dirty_calldata_bytes.sol new file mode 100644 index 000000000..05a3793f6 --- /dev/null +++ b/test/libsolidity/semanticTests/dirty_calldata_bytes.sol @@ -0,0 +1,12 @@ +contract C { + function f(bytes calldata b) public returns (bool correct) { + byte a = b[3]; + uint r; + assembly { + r := a + } + correct = r == (0x64 << 248); + } +} +// ---- +// f(bytes): 0x20, 0x04, "dead" -> true \ No newline at end of file diff --git a/test/libsolidity/semanticTests/dirty_calldata_dynamic_array.sol b/test/libsolidity/semanticTests/dirty_calldata_dynamic_array.sol new file mode 100644 index 000000000..1c9d7db30 --- /dev/null +++ b/test/libsolidity/semanticTests/dirty_calldata_dynamic_array.sol @@ -0,0 +1,12 @@ +contract C { + function f(int16[] calldata a) external returns (bool correct) { + uint32 x = uint32(a[1]); + uint r; + assembly { + r := x + } + correct = r == 0x7fff; + } +} +// ---- +// f(int16[]): 0x20, 0x02, 0x7fff, 0x7fff -> true \ No newline at end of file diff --git a/test/libsolidity/semanticTests/dirty_memory_bytes_to_storgage_copy.sol b/test/libsolidity/semanticTests/dirty_memory_bytes_to_storgage_copy.sol new file mode 100644 index 000000000..8fc6c33ec --- /dev/null +++ b/test/libsolidity/semanticTests/dirty_memory_bytes_to_storgage_copy.sol @@ -0,0 +1,16 @@ +contract C { + bytes x; + function f() public returns (uint r) { + bytes memory m = "tmp"; + assembly { + mstore(m, 8) + mstore(add(m, 32), "deadbeef15dead") + } + x = m; + assembly { + r := sload(x_slot) + } + } +} +// ---- +// f() -> 0x6465616462656566313564656164000000000000000000000000000000000010 diff --git a/test/libsolidity/semanticTests/viaYul/dirty_calldata_struct.sol b/test/libsolidity/semanticTests/viaYul/dirty_calldata_struct.sol new file mode 100644 index 000000000..633fc45c6 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/dirty_calldata_struct.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; +contract C { + struct S { + uint16[] m; + } + function f(S calldata s) public pure returns (bool correct) { + int8 x = int8(s.m[0]); + uint r; + assembly { + r := x + } + correct = r == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80; + } +} +// ==== +// compileViaYul: true +// ---- +// f((uint16[])): 0x20, 0x20, 0x01, 0x0180 -> true diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_dynamic_array.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_dynamic_array.sol new file mode 100644 index 000000000..36a253b02 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/dirty_memory_dynamic_array.sol @@ -0,0 +1,18 @@ +contract C { + function f() public pure returns (bool correct) { + uint8[] memory m = new uint8[](1); + assembly { + mstore(add(m, 32), 258) + } + uint8 x = m[0]; + uint r; + assembly { + r := x + } + correct = (m[0] == 0x02) && (r == 0x02); + } +} +// ==== +// compileViaYul: true +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_int32.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_int32.sol new file mode 100644 index 000000000..f4f0525bd --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/dirty_memory_int32.sol @@ -0,0 +1,18 @@ +contract C { + function f() public pure returns (bool correct) { + uint256[1] memory m; + assembly { + mstore(m, 0xdeadbeef15dead) + } + int32 x = int32(m[0]); + uint r; + assembly { + r := x + } + correct = (m[0] == 0xdeadbeef15dead) && (r == (((2 ** 224 - 1) << 32) | 0xef15dead)); + } +} +// ==== +// compileViaYul: true +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_read.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_read.sol deleted file mode 100644 index d03aa489f..000000000 --- a/test/libsolidity/semanticTests/viaYul/dirty_memory_read.sol +++ /dev/null @@ -1,15 +0,0 @@ -contract C { - function f() public pure returns (uint8 x, bool a, bool b) { - uint8[1] memory m; - assembly { - mstore(m, 257) - } - x = m[0]; - a = (m[0] == 0x01); - b = (m[0] == 0x0101); - } -} -// ==== -// compileViaYul: also -// ---- -// f() -> 1, true, false diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_static_array.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_static_array.sol new file mode 100644 index 000000000..4742bae2d --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/dirty_memory_static_array.sol @@ -0,0 +1,18 @@ +contract C { + function f() public pure returns (bool correct) { + uint8[1] memory m; + assembly { + mstore(m, 257) + } + uint8 x = m[0]; + uint r; + assembly { + r := x + } + correct = (m[0] == 0x01) && (r == 0x01); + } +} +// ==== +// compileViaYul: true +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_struct.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_struct.sol new file mode 100644 index 000000000..411cbe069 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/dirty_memory_struct.sol @@ -0,0 +1,22 @@ +contract C { + struct S { + uint8[] m; + } + function f() public pure returns (bool correct) { + S memory s; + s.m = new uint8[](1); + assembly { + mstore(add(s, 64), 257) + } + uint8 x = s.m[0]; + uint r; + assembly { + r := x + } + correct = r == 0x01; + } +} +// ==== +// compileViaYul: true +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_uint32.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_uint32.sol new file mode 100644 index 000000000..a23a566c5 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/dirty_memory_uint32.sol @@ -0,0 +1,18 @@ +contract C { + function f() public pure returns (bool correct) { + uint256[1] memory m; + assembly { + mstore(m, 0xdeadbeef15dead) + } + uint32 x = uint32(m[0]); + uint r; + assembly { + r := x + } + correct = (r == 0xef15dead) && (m[0] == 0xdeadbeef15dead); + } +} +// ==== +// compileViaYul: true +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes.sol b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes.sol new file mode 100644 index 000000000..ea0112fe5 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes.sol @@ -0,0 +1,18 @@ +contract C { + bytes b; + function f() public returns (bool correct) { + assembly { + sstore(b_slot, or("deadbeef", 0x08)) + } + byte s = b[3]; + uint r; + assembly { + r := s + } + correct = r == (0x64 << 248); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes_long.sol b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes_long.sol new file mode 100644 index 000000000..9cb01d63a --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes_long.sol @@ -0,0 +1,20 @@ +contract C { + bytes b; + function f() public returns (bool correct) { + assembly { + sstore(b_slot, 0x41) + mstore(0, b_slot) + sstore(keccak256(0, 0x20), "deadbeefdeadbeefdeadbeefdeadbeef") + } + byte s = b[31]; + uint r; + assembly { + r := s + } + correct = r == (0x66 << 248); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> true \ No newline at end of file diff --git a/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_dynamic_array.sol b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_dynamic_array.sol new file mode 100644 index 000000000..bc886b93d --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_dynamic_array.sol @@ -0,0 +1,20 @@ +contract C { + uint8[] s; + function f() public returns (bool correct) { + s.push(); + assembly { + mstore(0, s_slot) + sstore(keccak256(0, 0x20), 257) + } + uint8 x = s[0]; + uint r; + assembly { + r := x + } + correct = (s[0] == 0x01) && (r == 0x01); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_static_array.sol b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_static_array.sol new file mode 100644 index 000000000..7d7298fee --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_static_array.sol @@ -0,0 +1,18 @@ +contract C { + uint8[1] s; + function f() public returns (bool correct) { + assembly { + sstore(s_slot, 257) + } + uint8 x = s[0]; + uint r; + assembly { + r := x + } + correct = (s[0] == 0x01) && (r == 0x01); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_struct.sol b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_struct.sol new file mode 100644 index 000000000..1cf19c5be --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_struct.sol @@ -0,0 +1,23 @@ +contract C { + struct S { + uint8[] m; + } + S s; + function f() public returns (bool correct) { + s.m.push(); + assembly { + mstore(0, s_slot) + sstore(keccak256(0, 0x20), 257) + } + uint8 x = s.m[0]; + uint r; + assembly { + r := x + } + correct = r == 0x01; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> true From 07c116713610e94eba4c1ae9004d2a3c3d24aa10 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 11 Jun 2020 17:17:07 +0200 Subject: [PATCH 02/16] Refactor name and type resolution. --- libsolidity/analysis/NameAndTypeResolver.cpp | 17 +++---- libsolidity/analysis/NameAndTypeResolver.h | 7 +-- libsolidity/analysis/TypeChecker.cpp | 4 +- libsolidity/analysis/TypeChecker.h | 4 +- libsolidity/interface/CompilerStack.cpp | 45 +++++++++---------- test/libsolidity/Assembly.cpp | 22 +++------ .../SolidityExpressionCompiler.cpp | 13 ++---- 7 files changed, 46 insertions(+), 66 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index fa1840d06..9fbd219f9 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -123,11 +123,13 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map const& node: _source.nodes()) + if (!resolveNamesAndTypesInternal(*node, true)) + return false; } catch (langutil::FatalError const&) { @@ -135,6 +137,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsi throw; // Something is weird here, rather throw again. return false; } + return true; } bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) @@ -227,13 +230,14 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res bool success = true; setScope(contract->scope()); solAssert(!!m_currentScope, ""); + solAssert(_resolveInsideCode, ""); m_globalContext.setCurrentContract(*contract); updateDeclaration(*m_globalContext.currentSuper()); updateDeclaration(*m_globalContext.currentThis()); for (ASTPointer const& baseContract: contract->baseContracts()) - if (!resolveNamesAndTypes(*baseContract, true)) + if (!resolveNamesAndTypesInternal(*baseContract, true)) success = false; setScope(contract); @@ -254,23 +258,20 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res for (ASTPointer const& node: contract->subNodes()) { setScope(contract); - if (!resolveNamesAndTypes(*node, false)) + if (!resolveNamesAndTypesInternal(*node, false)) success = false; } if (!success) return false; - if (!_resolveInsideCode) - return success; - setScope(contract); // now resolve references inside the code for (ASTPointer const& node: contract->subNodes()) { setScope(contract); - if (!resolveNamesAndTypes(*node, true)) + if (!resolveNamesAndTypesInternal(*node, true)) success = false; } return success; diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 8a315510f..efb404038 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -65,12 +65,9 @@ public: bool registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope = nullptr); /// Applies the effect of import directives. bool performImports(SourceUnit& _sourceUnit, std::map const& _sourceUnits); - /// Resolves all names and types referenced from the given AST Node. - /// This is usually only called at the contract level, but with a bit of care, it can also - /// be called at deeper levels. - /// @param _resolveInsideCode if false, does not descend into nodes that contain code. + /// Resolves all names and types referenced from the given Source Node. /// @returns false in case of error. - bool resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode = true); + bool resolveNamesAndTypes(SourceUnit& _source); /// Updates the given global declaration (used for "this"). Not to be used with declarations /// that create their own scope. /// @returns false in case of error. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c308fadd8..a85f4f9e9 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -62,9 +62,9 @@ bool TypeChecker::typeSupportedByOldABIEncoder(Type const& _type, bool _isLibrar return true; } -bool TypeChecker::checkTypeRequirements(ASTNode const& _contract) +bool TypeChecker::checkTypeRequirements(SourceUnit const& _source) { - _contract.accept(*this); + _source.accept(*this); return Error::containsOnlyWarnings(m_errorReporter.errors()); } diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 9997c700a..948d63648 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -51,9 +51,9 @@ public: m_errorReporter(_errorReporter) {} - /// Performs type checking on the given contract and all of its sub-nodes. + /// Performs type checking on the given source and all of its sub-nodes. /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings - bool checkTypeRequirements(ASTNode const& _contract); + bool checkTypeRequirements(SourceUnit const& _source); /// @returns the type of an expression and asserts that it is present. TypePointer const& type(Expression const& _expression) const; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index a8efdfb0b..eef773095 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -326,28 +326,28 @@ bool CompilerStack::analyze() if (source->ast && !resolver.performImports(*source->ast, sourceUnitsByName)) return false; - // This is the main name and type resolution loop. Needs to be run for every contract, because - // the special variables "this" and "super" must be set appropriately. + for (Source const* source: m_sourceOrder) + if (source->ast && !resolver.resolveNamesAndTypes(*source->ast)) + return false; + + // Store contract definitions. for (Source const* source: m_sourceOrder) if (source->ast) - for (ASTPointer const& node: source->ast->nodes()) + for ( + ContractDefinition const* contract: + ASTNode::filteredNodes(source->ast->nodes()) + ) { - if (!resolver.resolveNamesAndTypes(*node)) - return false; - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - // Note that we now reference contracts by their fully qualified names, and - // thus contracts can only conflict if declared in the same source file. This - // should already cause a double-declaration error elsewhere. - if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end()) - m_contracts[contract->fullyQualifiedName()].contract = contract; - else - solAssert( - m_errorReporter.hasErrors(), - "Contract already present (name clash?), but no error was reported." - ); - } - + // Note that we now reference contracts by their fully qualified names, and + // thus contracts can only conflict if declared in the same source file. This + // should already cause a double-declaration error elsewhere. + if (!m_contracts.count(contract->fullyQualifiedName())) + m_contracts[contract->fullyQualifiedName()].contract = contract; + else + solAssert( + m_errorReporter.hasErrors(), + "Contract already present (name clash?), but no error was reported." + ); } DeclarationTypeChecker declarationTypeChecker(m_errorReporter, m_evmVersion); @@ -376,11 +376,8 @@ bool CompilerStack::analyze() // which is only done one step later. TypeChecker typeChecker(m_evmVersion, m_errorReporter); for (Source const* source: m_sourceOrder) - if (source->ast) - for (ASTPointer const& node: source->ast->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - if (!typeChecker.checkTypeRequirements(*contract)) - noErrors = false; + if (source->ast && !typeChecker.checkTypeRequirements(*source->ast)) + noErrors = false; if (noErrors) { diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 0484bf95c..885f0b2d3 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -63,27 +63,19 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); solAssert(Error::containsOnlyWarnings(errorReporter.errors()), ""); resolver.registerDeclarations(*sourceUnit); - for (ASTPointer const& node: sourceUnit->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); - if (!Error::containsOnlyWarnings(errorReporter.errors())) - return AssemblyItems(); - } + BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*sourceUnit)); + if (!Error::containsOnlyWarnings(errorReporter.errors())) + return AssemblyItems(); for (ASTPointer const& node: sourceUnit->nodes()) { BOOST_REQUIRE_NO_THROW(declarationTypeChecker.check(*node)); if (!Error::containsOnlyWarnings(errorReporter.errors())) return AssemblyItems(); } - for (ASTPointer const& node: sourceUnit->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - TypeChecker checker(solidity::test::CommonOptions::get().evmVersion(), errorReporter); - BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract)); - if (!Error::containsOnlyWarnings(errorReporter.errors())) - return AssemblyItems(); - } + TypeChecker checker(solidity::test::CommonOptions::get().evmVersion(), errorReporter); + BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*sourceUnit)); + if (!Error::containsOnlyWarnings(errorReporter.errors())) + return AssemblyItems(); for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 41c850b4d..4d1e9e23a 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -118,19 +118,12 @@ bytes compileFirstExpression( GlobalContext globalContext; NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); resolver.registerDeclarations(*sourceUnit); - for (ASTPointer const& node: sourceUnit->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*contract), "Resolving names failed"); + BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed"); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); for (ASTPointer const& node: sourceUnit->nodes()) BOOST_REQUIRE(declarationTypeChecker.check(*node)); - for (ASTPointer const& node: sourceUnit->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - ErrorReporter errorReporter(errors); - TypeChecker typeChecker(solidity::test::CommonOptions::get().evmVersion(), errorReporter); - BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract)); - } + TypeChecker typeChecker(solidity::test::CommonOptions::get().evmVersion(), errorReporter); + BOOST_REQUIRE(typeChecker.checkTypeRequirements(*sourceUnit)); for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { From 194c2b8c9c53710826c84e5aa6b778eaa36875ef Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 12 Jun 2020 00:16:24 +0200 Subject: [PATCH 03/16] Directly determine if experimental feature active. --- libsolidity/analysis/TypeChecker.cpp | 29 +++++++++++++++++----------- libsolidity/analysis/TypeChecker.h | 3 +++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index a85f4f9e9..c6b9fcbf5 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -64,7 +64,9 @@ bool TypeChecker::typeSupportedByOldABIEncoder(Type const& _type, bool _isLibrar bool TypeChecker::checkTypeRequirements(SourceUnit const& _source) { + m_currentSourceUnit = &_source; _source.accept(*this); + m_currentSourceUnit = nullptr; return Error::containsOnlyWarnings(m_errorReporter.errors()); } @@ -373,7 +375,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) } if ( _function.isPublic() && - !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && + !experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) && !typeSupportedByOldABIEncoder(*type(var), _function.libraryFunction()) ) m_errorReporter.typeError( @@ -511,7 +513,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) else if (_variable.visibility() >= Visibility::Public) { FunctionType getter(_variable); - if (!_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2)) + if (!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) { vector unsupportedTypes; for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes()) @@ -622,7 +624,7 @@ bool TypeChecker::visit(EventDefinition const& _eventDef) if (!type(*var)->interfaceType(false)) m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type."); if ( - !_eventDef.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && + !experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) && !typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */) ) m_errorReporter.typeError( @@ -1912,9 +1914,7 @@ void TypeChecker::typeCheckABIEncodeFunctions( bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked; solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding"); - bool const abiEncoderV2 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count( - ExperimentalFeature::ABIEncoderV2 - ); + bool const abiEncoderV2 = experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2); // Check for named arguments if (!_functionCall.names().empty()) @@ -2311,11 +2311,10 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) { case FunctionType::Kind::ABIDecode: { - bool const abiEncoderV2 = - m_currentContract->sourceUnit().annotation().experimentalFeatures.count( - ExperimentalFeature::ABIEncoderV2 - ); - returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2); + returnTypes = typeCheckABIDecodeAndRetrieveReturnType( + _functionCall, + experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) + ); break; } case FunctionType::Kind::ABIEncode: @@ -3256,3 +3255,11 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss m_errorReporter.typeError(errorId, _expression.location(), description); } + +bool TypeChecker::experimentalFeatureActive(ExperimentalFeature _feature) const +{ + solAssert(m_currentSourceUnit, ""); + if (m_currentContract) + solAssert(m_currentSourceUnit == &m_currentContract->sourceUnit(), ""); + return m_currentSourceUnit->annotation().experimentalFeatures.count(_feature); +} diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 948d63648..732f6275a 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -165,6 +165,9 @@ private: /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. void requireLValue(Expression const& _expression, bool _ordinaryAssignment); + bool experimentalFeatureActive(ExperimentalFeature _feature) const; + + SourceUnit const* m_currentSourceUnit = nullptr; ContractDefinition const* m_currentContract = nullptr; langutil::EVMVersion m_evmVersion; From a806d48ad5f49a5f563c79e8c7d3f4a48ab54767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 30 May 2020 00:25:06 +0200 Subject: [PATCH 04/16] CommandLineInterface: Add countEnabledOptions() and joinOptionNames() helpers --- solc/CommandLineInterface.cpp | 18 ++++++++++++++++++ solc/CommandLineInterface.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 8b6117a2d..6cfcdc37e 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -58,6 +58,7 @@ #include #include +#include #include #ifdef _WIN32 // windows @@ -1879,4 +1880,21 @@ void CommandLineInterface::outputCompilationResults() } } +size_t CommandLineInterface::countEnabledOptions(vector const& _optionNames) const +{ + size_t count = 0; + for (string const& _option: _optionNames) + count += m_args.count(_option); + + return count; +} + +string CommandLineInterface::joinOptionNames(vector const& _optionNames, string _separator) +{ + return boost::algorithm::join( + _optionNames | boost::adaptors::transformed([](string const& _option){ return "--" + _option; }), + _separator + ); +} + } diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 2c871a670..1aed72821 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -103,6 +103,9 @@ private: /// @arg _json json string to be written void createJson(std::string const& _fileName, std::string const& _json); + size_t countEnabledOptions(std::vector const& _optionNames) const; + static std::string joinOptionNames(std::vector const& _optionNames, std::string _separator = ", "); + bool m_error = false; ///< If true, some error occurred. bool m_onlyAssemble = false; From 8396002fe7a6216ef60d37ea7087a2991147fffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 29 May 2020 21:38:19 +0200 Subject: [PATCH 05/16] CommandLineInterface: Disallow using --assemble, --strict-assembly, --yul, --link, --standard-json and --import-ast at the same time --- solc/CommandLineInterface.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 6cfcdc37e..23294cefb 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -1130,6 +1130,21 @@ bool CommandLineInterface::processInput() } } + vector const exclusiveModes = { + g_argStandardJSON, + g_argLink, + g_argAssemble, + g_argStrictAssembly, + g_argYul, + g_argImportAst, + }; + if (countEnabledOptions(exclusiveModes) > 1) + { + serr() << "The following options are mutually exclusive: " << joinOptionNames(exclusiveModes) << ". "; + serr() << "Select at most one." << endl; + return false; + } + if (m_args.count(g_argStandardJSON)) { vector inputFiles; From 0d7b3ae503e6d6b839e9cede329d9d21738ed697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 29 May 2020 23:28:09 +0200 Subject: [PATCH 06/16] CommandLineInterface: Disallow --yul-dialect and --machine when not in assembly mode --- solc/CommandLineInterface.cpp | 7 +++++++ test/cmdlineTests/strict_asm_options_in_non_asm_mode/args | 1 + test/cmdlineTests/strict_asm_options_in_non_asm_mode/err | 1 + test/cmdlineTests/strict_asm_options_in_non_asm_mode/exit | 1 + .../strict_asm_options_in_non_asm_mode/input.sol | 7 +++++++ 5 files changed, 17 insertions(+) create mode 100644 test/cmdlineTests/strict_asm_options_in_non_asm_mode/args create mode 100644 test/cmdlineTests/strict_asm_options_in_non_asm_mode/err create mode 100644 test/cmdlineTests/strict_asm_options_in_non_asm_mode/exit create mode 100644 test/cmdlineTests/strict_asm_options_in_non_asm_mode/input.sol diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 23294cefb..712740cd0 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -1289,6 +1289,13 @@ bool CommandLineInterface::processInput() return assemble(inputLanguage, targetMachine, optimize, yulOptimiserSteps); } + else if (countEnabledOptions({g_strYulDialect, g_argMachine}) >= 1) + { + serr() << "--" << g_strYulDialect << " and --" << g_argMachine << " "; + serr() << "are only valid in assembly mode." << endl; + return false; + } + if (m_args.count(g_argLink)) { // switch to linker mode diff --git a/test/cmdlineTests/strict_asm_options_in_non_asm_mode/args b/test/cmdlineTests/strict_asm_options_in_non_asm_mode/args new file mode 100644 index 000000000..6f9a273ab --- /dev/null +++ b/test/cmdlineTests/strict_asm_options_in_non_asm_mode/args @@ -0,0 +1 @@ +--yul-dialect evm --machine ewasm diff --git a/test/cmdlineTests/strict_asm_options_in_non_asm_mode/err b/test/cmdlineTests/strict_asm_options_in_non_asm_mode/err new file mode 100644 index 000000000..6d67f2b5b --- /dev/null +++ b/test/cmdlineTests/strict_asm_options_in_non_asm_mode/err @@ -0,0 +1 @@ +--yul-dialect and --machine are only valid in assembly mode. diff --git a/test/cmdlineTests/strict_asm_options_in_non_asm_mode/exit b/test/cmdlineTests/strict_asm_options_in_non_asm_mode/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/strict_asm_options_in_non_asm_mode/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/strict_asm_options_in_non_asm_mode/input.sol b/test/cmdlineTests/strict_asm_options_in_non_asm_mode/input.sol new file mode 100644 index 000000000..6923ca702 --- /dev/null +++ b/test/cmdlineTests/strict_asm_options_in_non_asm_mode/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C +{ + function f() public pure {} +} From 4bd078ed7e92698d1250a0d73cf66e4a02c0d000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 30 May 2020 00:32:35 +0200 Subject: [PATCH 07/16] CommandLineInterface: Disallow --output-dir, --gas and --combined-json in assembly mode --- solc/CommandLineInterface.cpp | 26 ++++++++++++------- .../strict_asm_invalid_option_output_dir/args | 1 + .../strict_asm_invalid_option_output_dir/err | 1 + .../strict_asm_invalid_option_output_dir/exit | 1 + .../input.yul | 3 +++ 5 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 test/cmdlineTests/strict_asm_invalid_option_output_dir/args create mode 100644 test/cmdlineTests/strict_asm_invalid_option_output_dir/err create mode 100644 test/cmdlineTests/strict_asm_invalid_option_output_dir/exit create mode 100644 test/cmdlineTests/strict_asm_invalid_option_output_dir/input.yul diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 712740cd0..bbf0eeb53 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -1190,6 +1190,22 @@ bool CommandLineInterface::processInput() if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argYul)) { + vector const nonAssemblyModeOptions = { + // TODO: The list is not complete. Add more. + g_argOutputDir, + g_argGas, + g_argCombinedJson, + g_strOptimizeYul, + g_strNoOptimizeYul, + }; + if (countEnabledOptions(nonAssemblyModeOptions) >= 1) + { + serr() << "The following options are invalid in assembly mode: "; + serr() << joinOptionNames(nonAssemblyModeOptions) << ". "; + serr() << "Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl; + return false; + } + // switch to assembly mode m_onlyAssemble = true; using Input = yul::AssemblyStack::Language; @@ -1197,16 +1213,6 @@ bool CommandLineInterface::processInput() Input inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly); Machine targetMachine = Machine::EVM; bool optimize = m_args.count(g_argOptimize); - if (m_args.count(g_strOptimizeYul)) - { - serr() << "--" << g_strOptimizeYul << " is invalid in assembly mode. Use --" << g_argOptimize << " instead." << endl; - return false; - } - if (m_args.count(g_strNoOptimizeYul)) - { - serr() << "--" << g_strNoOptimizeYul << " is invalid in assembly mode. Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl; - return false; - } optional yulOptimiserSteps; if (m_args.count(g_strYulOptimizations)) diff --git a/test/cmdlineTests/strict_asm_invalid_option_output_dir/args b/test/cmdlineTests/strict_asm_invalid_option_output_dir/args new file mode 100644 index 000000000..ba341ccc5 --- /dev/null +++ b/test/cmdlineTests/strict_asm_invalid_option_output_dir/args @@ -0,0 +1 @@ +--strict-assembly --output-dir /tmp/ diff --git a/test/cmdlineTests/strict_asm_invalid_option_output_dir/err b/test/cmdlineTests/strict_asm_invalid_option_output_dir/err new file mode 100644 index 000000000..a9b56e9c7 --- /dev/null +++ b/test/cmdlineTests/strict_asm_invalid_option_output_dir/err @@ -0,0 +1 @@ +The following options are invalid in assembly mode: --output-dir, --gas, --combined-json, --optimize-yul, --no-optimize-yul. Optimization is disabled by default and can be enabled with --optimize. diff --git a/test/cmdlineTests/strict_asm_invalid_option_output_dir/exit b/test/cmdlineTests/strict_asm_invalid_option_output_dir/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/strict_asm_invalid_option_output_dir/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/strict_asm_invalid_option_output_dir/input.yul b/test/cmdlineTests/strict_asm_invalid_option_output_dir/input.yul new file mode 100644 index 000000000..f21cd2b7e --- /dev/null +++ b/test/cmdlineTests/strict_asm_invalid_option_output_dir/input.yul @@ -0,0 +1,3 @@ +{ + sstore(0, 1) +} From c5fb5f6d575c8b002d8bd2bf031c7203d92b35c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 3 Jun 2020 18:58:52 +0200 Subject: [PATCH 08/16] List command-line interface changes in the changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 2510afc2e..874d861c1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: * NatSpec: Add fields "kind" and "version" to the JSON output. + * Commandline Interface: Prevent some incompatible commandline options from being used together. Bugfixes: From 00f280057075a4e315bfb00cf7df93342b0a99ce Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 15 Jun 2020 15:44:44 +0200 Subject: [PATCH 09/16] Explain how to build proper release. --- docs/installing-solidity.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 27a20b3bc..4f03a6b76 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -314,6 +314,16 @@ you should fork Solidity and add your personal fork as a second remote: git remote add personal git@github.com:[username]/solidity.git +.. note:: + This method will result in a prerelease build leading to e.g. a flag + being set in each bytecode produced by such a compiler. + If you want to re-build a released Solidity compiler, then + please use the source tarball on the github release page: + + https://github.com/ethereum/solidity/releases/download/v0.X.Y/solidity_0.X.Y.tar.gz + + (not the "Source code" provided by github). + Command-Line Build ------------------ From 5fdbb5c3e31c5369a1141e0a80cbbe8c645263a7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 11 Jun 2020 18:46:31 +0200 Subject: [PATCH 10/16] Simplify inline assembly resolver. --- libsolidity/analysis/TypeChecker.cpp | 51 +++++++++++++------------ libsolidity/codegen/CompilerContext.cpp | 7 ++-- libyul/AsmAnalysis.cpp | 20 +++------- libyul/backends/evm/AbstractAssembly.h | 2 +- 4 files changed, 36 insertions(+), 44 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c308fadd8..32a4666be 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -666,17 +666,18 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); if (ref == _inlineAssembly.annotation().externalReferences.end()) - return numeric_limits::max(); - Declaration const* declaration = ref->second.declaration; + return false; + InlineAssemblyAnnotation::ExternalIdentifierInfo& identifierInfo = ref->second; + Declaration const* declaration = identifierInfo.declaration; solAssert(!!declaration, ""); - bool requiresStorage = ref->second.isSlot || ref->second.isOffset; + bool requiresStorage = identifierInfo.isSlot || identifierInfo.isOffset; if (auto var = dynamic_cast(declaration)) { solAssert(var->type(), "Expected variable type!"); if (var->immutable()) { m_errorReporter.typeError(3773_error, _identifier.location, "Assembly access to immutable variables is not supported."); - return numeric_limits::max(); + return false; } if (var->isConstant()) { @@ -685,17 +686,17 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (var && !var->value()) { m_errorReporter.typeError(3224_error, _identifier.location, "Constant has no value."); - return numeric_limits::max(); + return false; } else if (_context == yul::IdentifierContext::LValue) { m_errorReporter.typeError(6252_error, _identifier.location, "Constant variables cannot be assigned to."); - return numeric_limits::max(); + return false; } else if (requiresStorage) { m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes _offset and _slot can only be used on non-constant storage variables."); - return numeric_limits::max(); + return false; } else if (var && var->value() && !var->value()->annotation().type && !dynamic_cast(var->value().get())) { @@ -704,7 +705,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) _identifier.location, "Constant variables with non-literal values cannot be forward referenced from inline assembly." ); - return size_t(-1); + return false; } else if (!var || !type(*var)->isValueType() || ( !dynamic_cast(var->value().get()) && @@ -712,7 +713,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) )) { m_errorReporter.typeError(7615_error, _identifier.location, "Only direct number constants and references to such constants are supported by inline assembly."); - return size_t(-1); + return false; } } @@ -723,33 +724,33 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) 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 numeric_limits::max(); + 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 numeric_limits::max(); + return false; } - else if (ref->second.isOffset) + else if (identifierInfo.isOffset) { m_errorReporter.typeError(9739_error, _identifier.location, "Only _slot can be assigned to."); - return numeric_limits::max(); + return false; } else - solAssert(ref->second.isSlot, ""); + solAssert(identifierInfo.isSlot, ""); } } 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."); - return numeric_limits::max(); + 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."); - return numeric_limits::max(); + return false; } else if (var->type()->sizeOnStack() != 1) { @@ -757,21 +758,21 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) 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."); else m_errorReporter.typeError(9857_error, _identifier.location, "Only types that use one stack slot are supported."); - return numeric_limits::max(); + return false; } } else if (requiresStorage) { m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); - return numeric_limits::max(); + return false; } else if (_context == yul::IdentifierContext::LValue) { if (dynamic_cast(declaration)) - return numeric_limits::max(); + return false; m_errorReporter.typeError(1990_error, _identifier.location, "Only local variables can be assigned to in inline assembly."); - return numeric_limits::max(); + return false; } if (_context == yul::IdentifierContext::RValue) @@ -780,7 +781,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (dynamic_cast(declaration)) { m_errorReporter.declarationError(2025_error, _identifier.location, "Access to functions is not allowed in inline assembly."); - return numeric_limits::max(); + return false; } else if (dynamic_cast(declaration)) { @@ -790,14 +791,14 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (!contract->isLibrary()) { m_errorReporter.typeError(4977_error, _identifier.location, "Expected a library."); - return numeric_limits::max(); + return false; } } else - return numeric_limits::max(); + return false; } - ref->second.valueSize = 1; - return size_t(1); + identifierInfo.valueSize = 1; + return true; }; solAssert(!_inlineAssembly.annotation().analysisInfo, ""); _inlineAssembly.annotation().analysisInfo = make_shared(); diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index fea449f7f..3af9a7c94 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -384,12 +384,11 @@ void CompilerContext::appendInlineAssembly( yul::Identifier const& _identifier, yul::IdentifierContext, bool _insideFunction - ) -> size_t + ) -> bool { if (_insideFunction) - return numeric_limits::max(); - auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name.str()); - return it == _localVariables.end() ? numeric_limits::max() : 1; + return false; + return contains(_localVariables, _identifier.name.str()); }; identifierAccess.generateCode = [&]( yul::Identifier const& _identifier, diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 07db82095..eeb80263e 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -132,17 +132,11 @@ vector AsmAnalyzer::operator()(Identifier const& _identifier) } else { - bool found = false; - if (m_resolver) - { - bool insideFunction = m_currentScope->insideFunction(); - size_t stackSize = m_resolver(_identifier, yul::IdentifierContext::RValue, insideFunction); - if (stackSize != numeric_limits::max()) - { - found = true; - yulAssert(stackSize == 1, "Invalid stack size of external reference."); - } - } + bool found = m_resolver && m_resolver( + _identifier, + yul::IdentifierContext::RValue, + m_currentScope->insideFunction() + ); if (!found && watcher.ok()) // Only add an error message if the callback did not do it. m_errorReporter.declarationError(8198_error, _identifier.location, "Identifier not found."); @@ -478,12 +472,10 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueT else if (m_resolver) { bool insideFunction = m_currentScope->insideFunction(); - size_t variableSize = m_resolver(_variable, yul::IdentifierContext::LValue, insideFunction); - if (variableSize != numeric_limits::max()) + if (m_resolver(_variable, yul::IdentifierContext::LValue, insideFunction)) { found = true; variableType = &m_dialect.defaultType; - yulAssert(variableSize == 1, "Invalid stack size of external reference."); } } diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index 2f86270b0..b46c2f734 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -118,7 +118,7 @@ enum class IdentifierContext { LValue, RValue, VariableDeclaration }; /// to inline assembly (not used in standalone assembly mode). struct ExternalIdentifierAccess { - using Resolver = std::function; + using Resolver = std::function; /// Resolve an external reference given by the identifier in the given context. /// @returns the size of the value (number of stack slots) or size_t(-1) if not found. Resolver resolve; From 645c3508a88b4d63d4f9263625691eb3b5d3c513 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 15 Jun 2020 19:11:03 +0200 Subject: [PATCH 11/16] Prepare for allowing bound functions and using for everywhere. --- libsolidity/ast/Types.cpp | 108 ++++++++++++++++++++++---------------- libsolidity/ast/Types.h | 28 +++++----- 2 files changed, 77 insertions(+), 59 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index bb202e875..5ea018107 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -311,10 +311,15 @@ TypePointer Type::commonType(Type const* _a, Type const* _b) return nullptr; } -MemberList const& Type::members(ContractDefinition const* _currentScope) const +MemberList const& Type::members(ASTNode const* _currentScope) const { if (!m_members[_currentScope]) { + solAssert( + _currentScope == nullptr || + dynamic_cast(_currentScope) || + dynamic_cast(_currentScope), + ""); MemberList::MemberMap members = nativeMembers(_currentScope); if (_currentScope) members += boundFunctions(*this, *_currentScope); @@ -344,8 +349,20 @@ TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c return encodingType; } -MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition const& _scope) +MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _scope) { + vector usingForDirectives; + if (auto const* sourceUnit = dynamic_cast(&_scope)) + usingForDirectives += ASTNode::filteredNodes(sourceUnit->nodes()); + else if (auto const* contract = dynamic_cast(&_scope)) + { + for (ContractDefinition const* contract: contract->annotation().linearizedBaseContracts) + usingForDirectives += contract->usingForDirectives(); + usingForDirectives += ASTNode::filteredNodes(contract->sourceUnit().nodes()); + } + else + solAssert(false, ""); + // Normalise data location of type. DataLocation typeLocation = DataLocation::Storage; if (auto refType = dynamic_cast(&_type)) @@ -353,38 +370,39 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition set seenFunctions; MemberList::MemberMap members; - for (ContractDefinition const* contract: _scope.annotation().linearizedBaseContracts) - for (UsingForDirective const* ufd: contract->usingForDirectives()) - { - // Convert both types to pointers for comparison to see if the `using for` - // directive applies. - // Further down, we check more detailed for each function if `_type` is - // convertible to the function parameter type. - if (ufd->typeName() && - *TypeProvider::withLocationIfReference(typeLocation, &_type, true) != - *TypeProvider::withLocationIfReference( - typeLocation, - ufd->typeName()->annotation().type, - true - ) + + for (UsingForDirective const* ufd: usingForDirectives) + { + // Convert both types to pointers for comparison to see if the `using for` + // directive applies. + // Further down, we check more detailed for each function if `_type` is + // convertible to the function parameter type. + if (ufd->typeName() && + *TypeProvider::withLocationIfReference(typeLocation, &_type, true) != + *TypeProvider::withLocationIfReference( + typeLocation, + ufd->typeName()->annotation().type, + true ) + ) + continue; + auto const& library = dynamic_cast( + *ufd->libraryName().annotation().referencedDeclaration + ); + for (FunctionDefinition const* function: library.definedFunctions()) + { + if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function)) continue; - auto const& library = dynamic_cast( - *ufd->libraryName().annotation().referencedDeclaration - ); - for (FunctionDefinition const* function: library.definedFunctions()) - { - if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function)) - continue; - seenFunctions.insert(function); - if (function->parameters().empty()) - continue; - FunctionTypePointer fun = - dynamic_cast(*function->typeViaContractName()).asBoundFunction(); - if (_type.isImplicitlyConvertibleTo(*fun->selfType())) - members.emplace_back(function->name(), fun, function); - } + seenFunctions.insert(function); + if (function->parameters().empty()) + continue; + FunctionTypePointer fun = + dynamic_cast(*function->typeViaContractName()).asBoundFunction(); + if (_type.isImplicitlyConvertibleTo(*fun->selfType())) + members.emplace_back(function->name(), fun, function); } + } + return members; } @@ -464,7 +482,7 @@ bool AddressType::operator==(Type const& _other) const return other.m_stateMutability == m_stateMutability; } -MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) const +MemberList::MemberMap AddressType::nativeMembers(ASTNode const*) const { MemberList::MemberMap members = { {"balance", TypeProvider::uint256()}, @@ -1400,7 +1418,7 @@ TypeResult FixedBytesType::binaryOperatorResult(Token _operator, Type const* _ot return nullptr; } -MemberList::MemberMap FixedBytesType::nativeMembers(ContractDefinition const*) const +MemberList::MemberMap FixedBytesType::nativeMembers(ASTNode const*) const { return MemberList::MemberMap{MemberList::Member{"length", TypeProvider::uint(8)}}; } @@ -1856,7 +1874,7 @@ string ArrayType::signatureInExternalFunction(bool _structsByName) const } } -MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const +MemberList::MemberMap ArrayType::nativeMembers(ASTNode const*) const { MemberList::MemberMap members; if (!isString()) @@ -2029,10 +2047,9 @@ string ContractType::canonicalName() const return m_contract.annotation().canonicalName; } -MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _contract) const +MemberList::MemberMap ContractType::nativeMembers(ASTNode const*) const { MemberList::MemberMap members; - solAssert(_contract, ""); if (m_super) { // add the most derived of all functions which are visible in derived contracts @@ -2063,14 +2080,13 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con } } else if (!m_contract.isLibrary()) - { for (auto const& it: m_contract.interfaceFunctions()) members.emplace_back( it.second->declaration().name(), it.second->asExternallyCallableFunction(m_contract.isLibrary()), &it.second->declaration() ); - } + return members; } @@ -2241,7 +2257,7 @@ string StructType::toString(bool _short) const return ret; } -MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const +MemberList::MemberMap StructType::nativeMembers(ASTNode const*) const { MemberList::MemberMap members; for (ASTPointer const& variable: m_struct.members()) @@ -3146,7 +3162,7 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const ); } -MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const* _scope) const +MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const { switch (m_kind) { @@ -3165,7 +3181,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const* _sco functionDefinition->isPartOfExternalInterface() ) { - solAssert(_scope->derivesFrom(*functionDefinition->annotation().contract), ""); + auto const* contractScope = dynamic_cast(_scope); + solAssert(contractScope && contractScope->derivesFrom(*functionDefinition->annotation().contract), ""); return {{"selector", TypeProvider::fixedBytes(4)}}; } else @@ -3640,13 +3657,14 @@ vector> TypeType::makeStackItems() const return {}; } -MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _currentScope) const +MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) const { MemberList::MemberMap members; if (m_actualType->category() == Category::Contract) { + auto const* contractScope = dynamic_cast(_currentScope); ContractDefinition const& contract = dynamic_cast(*m_actualType).contractDefinition(); - bool inDerivingScope = _currentScope && _currentScope->derivesFrom(contract); + bool inDerivingScope = contractScope && contractScope->derivesFrom(contract); for (auto const* declaration: contract.declarations()) { @@ -3748,7 +3766,7 @@ bool ModuleType::operator==(Type const& _other) const return &m_sourceUnit == &dynamic_cast(_other).m_sourceUnit; } -MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const +MemberList::MemberMap ModuleType::nativeMembers(ASTNode const*) const { MemberList::MemberMap symbols; for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols) @@ -3789,7 +3807,7 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const +MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const { switch (m_kind) { diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index a82700808..b902a1c77 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -315,9 +315,9 @@ public: /// Returns the list of all members of this type. Default implementation: no members apart from bound. /// @param _currentScope scope in which the members are accessed. - MemberList const& members(ContractDefinition const* _currentScope) const; + MemberList const& members(ASTNode const* _currentScope) const; /// Convenience method, returns the type of the given named member or an empty pointer if no such member exists. - TypePointer memberType(std::string const& _name, ContractDefinition const* _currentScope = nullptr) const + TypePointer memberType(std::string const& _name, ASTNode const* _currentScope = nullptr) const { return members(_currentScope).memberType(_name); } @@ -361,12 +361,12 @@ public: private: /// @returns a member list containing all members added to this type by `using for` directives. - static MemberList::MemberMap boundFunctions(Type const& _type, ContractDefinition const& _scope); + static MemberList::MemberMap boundFunctions(Type const& _type, ASTNode const& _scope); protected: /// @returns the members native to this type depending on the given context. This function /// is used (in conjunction with boundFunctions to fill m_members below. - virtual MemberList::MemberMap nativeMembers(ContractDefinition const* /*_currentScope*/) const + virtual MemberList::MemberMap nativeMembers(ASTNode const* /*_currentScope*/) const { return MemberList::MemberMap(); } @@ -379,7 +379,7 @@ protected: /// List of member types (parameterised by scape), will be lazy-initialized. - mutable std::map> m_members; + mutable std::map> m_members; mutable std::optional>> m_stackItems; mutable std::optional m_stackSize; }; @@ -408,7 +408,7 @@ public: bool isValueType() const override { return true; } bool nameable() const override { return true; } - MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; + MemberList::MemberMap nativeMembers(ASTNode const*) const override; std::string toString(bool _short) const override; std::string canonicalName() const override; @@ -649,7 +649,7 @@ public: bool nameable() const override { return true; } std::string toString(bool) const override { return "bytes" + util::toString(m_bytes); } - MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; + MemberList::MemberMap nativeMembers(ASTNode const*) const override; TypePointer encodingType() const override { return this; } TypeResult interfaceType(bool) const override { return this; } @@ -786,7 +786,7 @@ public: std::string toString(bool _short) const override; std::string canonicalName() const override; std::string signatureInExternalFunction(bool _structsByName) const override; - MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; TypePointer encodingType() const override; TypePointer decodingType() const override; TypeResult interfaceType(bool _inLibrary) const override; @@ -889,7 +889,7 @@ public: std::string toString(bool _short) const override; std::string canonicalName() const override; - MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; Type const* encodingType() const override; @@ -949,7 +949,7 @@ public: bool nameable() const override { return true; } std::string toString(bool _short) const override; - MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; Type const* encodingType() const override; TypeResult interfaceType(bool _inLibrary) const override; @@ -1220,7 +1220,7 @@ public: bool nameable() const override; bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } bool hasSimpleZeroValueInMemory() const override { return false; } - MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; TypePointer encodingType() const override; TypeResult interfaceType(bool _inLibrary) const override; @@ -1389,7 +1389,7 @@ public: bool canLiveOutsideStorage() const override { return false; } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } - MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; protected: @@ -1441,7 +1441,7 @@ public: bool canBeStored() const override { return false; } bool canLiveOutsideStorage() const override { return true; } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } - MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; + MemberList::MemberMap nativeMembers(ASTNode const*) const override; std::string toString(bool _short) const override; @@ -1481,7 +1481,7 @@ public: bool canBeStored() const override { return false; } bool canLiveOutsideStorage() const override { return true; } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } - MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; + MemberList::MemberMap nativeMembers(ASTNode const*) const override; std::string toString(bool _short) const override; From 5d63fa09e903afe0809ed94814126ffc9c19ea46 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Sat, 13 Jun 2020 02:02:32 +0200 Subject: [PATCH 12/16] Fix false ////- and /***-natspec --- Changelog.md | 3 +- liblangutil/Scanner.cpp | 23 +++++--- test/libsolidity/SolidityNatspecJSON.cpp | 68 ++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index 874d861c1..22f1d1e5d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,8 +9,7 @@ Compiler Features: Bugfixes: - - + * NatSpec: Do not consider ``////`` and ``/***`` as NatSpec comments. ### 0.6.10 (2020-06-11) diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index db0636fb9..64821ecb5 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -313,7 +313,6 @@ size_t Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); size_t endPosition = m_source->position(); - advance(); //consume the last '/' at /// skipWhitespaceExceptUnicodeLinebreak(); @@ -332,6 +331,8 @@ size_t Scanner::scanSingleLineDocComment() m_source->get(1) == '/' && m_source->get(2) == '/') { + if (!m_source->isPastEndOfInput(4) && m_source->get(3) == '/') + break; // "////" is not a documentation comment m_char = m_source->advanceAndGet(3); if (atEndOfLine()) continue; @@ -353,7 +354,6 @@ size_t Scanner::scanSingleLineDocComment() Token Scanner::skipMultiLineComment() { - advance(); while (!isSourcePastEndOfInput()) { char ch = m_char; @@ -437,6 +437,11 @@ Token Scanner::scanSlash() return Token::Whitespace; else if (m_char == '/') { + advance(); //consume the last '/' at /// + + // "////" + if (m_char == '/') + return skipSingleLineComment(); // doxygen style /// comment m_skippedComments[NextNext].location.start = firstSlashPosition; m_skippedComments[NextNext].location.source = m_source; @@ -462,11 +467,14 @@ Token Scanner::scanSlash() advance(); //skip the closing slash return Token::Whitespace; } + // "/***" + if (m_char == '*') + // "/***/" may be interpreted as empty natspec or skipped; skipping is simpler + return skipMultiLineComment(); // we actually have a multiline documentation comment - Token comment; m_skippedComments[NextNext].location.start = firstSlashPosition; m_skippedComments[NextNext].location.source = m_source; - comment = scanMultiLineDocComment(); + Token comment = scanMultiLineDocComment(); m_skippedComments[NextNext].location.end = static_cast(sourcePos()); m_skippedComments[NextNext].token = comment; if (comment == Token::Illegal) @@ -754,17 +762,16 @@ bool Scanner::isUnicodeLinebreak() if (0x0a <= m_char && m_char <= 0x0d) // line feed, vertical tab, form feed, carriage return return true; - else if (!m_source->isPastEndOfInput(1) && uint8_t(m_source->get(0)) == 0xc2 && uint8_t(m_source->get(1)) == 0x85) + if (!m_source->isPastEndOfInput(1) && uint8_t(m_source->get(0)) == 0xc2 && uint8_t(m_source->get(1)) == 0x85) // NEL - U+0085, C2 85 in utf8 return true; - else if (!m_source->isPastEndOfInput(2) && uint8_t(m_source->get(0)) == 0xe2 && uint8_t(m_source->get(1)) == 0x80 && ( + if (!m_source->isPastEndOfInput(2) && uint8_t(m_source->get(0)) == 0xe2 && uint8_t(m_source->get(1)) == 0x80 && ( uint8_t(m_source->get(2)) == 0xa8 || uint8_t(m_source->get(2)) == 0xa9 )) // LS - U+2028, E2 80 A8 in utf8 // PS - U+2029, E2 80 A9 in utf8 return true; - else - return false; + return false; } Token Scanner::scanString() diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 06a4fc4fb..6b69904d3 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -1146,6 +1146,74 @@ BOOST_AUTO_TEST_CASE(dev_constructor_and_function) checkNatspec(sourceCode, "test", natspec, false); } +BOOST_AUTO_TEST_CASE(slash4) +{ + char const* sourceCode = R"( + contract test { + //// @notice lorem ipsum + function f() public { } + } + )"; + + char const* natspec = R"( { "methods": {} } )"; + + checkNatspec(sourceCode, "test", natspec, true); +} + +BOOST_AUTO_TEST_CASE(star3) +{ + char const* sourceCode = R"( + contract test { + /*** + * @notice lorem ipsum + */ + function f() public { } + } + )"; + + char const* natspec = R"( { "methods": {} } )"; + + checkNatspec(sourceCode, "test", natspec, true); +} + +BOOST_AUTO_TEST_CASE(slash3_slash3) +{ + char const* sourceCode = R"( + contract test { + /// @notice lorem + /// ipsum + function f() public { } + } + )"; + + char const* natspec = R"ABCDEF({ + "methods": { + "f()": { "notice": "lorem ipsum" } + } + })ABCDEF"; + + checkNatspec(sourceCode, "test", natspec, true); +} + +BOOST_AUTO_TEST_CASE(slash3_slash4) +{ + char const* sourceCode = R"( + contract test { + /// @notice lorem + //// ipsum + function f() public { } + } + )"; + + char const* natspec = R"ABCDEF({ + "methods": { + "f()": { "notice": "lorem" } + } + })ABCDEF"; + + checkNatspec(sourceCode, "test", natspec, true); +} + BOOST_AUTO_TEST_SUITE_END() } From 50e8d6850f32d0c7c674009fd3ec46908cc18bc1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 4 Feb 2020 23:28:44 +0100 Subject: [PATCH 13/16] Remove wasm type conversions. --- libyul/backends/wasm/WasmCodeTransform.cpp | 57 ++-------------------- libyul/backends/wasm/WasmCodeTransform.h | 6 --- 2 files changed, 3 insertions(+), 60 deletions(-) diff --git a/libyul/backends/wasm/WasmCodeTransform.cpp b/libyul/backends/wasm/WasmCodeTransform.cpp index 8f987824c..8de291402 100644 --- a/libyul/backends/wasm/WasmCodeTransform.cpp +++ b/libyul/backends/wasm/WasmCodeTransform.cpp @@ -112,8 +112,6 @@ wasm::Expression WasmCodeTransform::operator()(ExpressionStatement const& _state wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) { - bool typeConversionNeeded = false; - if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name)) { if (_call.functionName.name.str().substr(0, 4) == "eth.") @@ -133,7 +131,6 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) imp.paramTypes.emplace_back(translatedType(param)); m_functionsToImport[builtin->name] = std::move(imp); } - typeConversionNeeded = true; } else if (builtin->literalArguments && contains(builtin->literalArguments.value(), true)) { @@ -147,18 +144,10 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) return wasm::BuiltinCall{_call.functionName.name.str(), std::move(literals)}; } else - { - wasm::BuiltinCall call{ + return wasm::BuiltinCall{ _call.functionName.name.str(), - injectTypeConversionIfNeeded(visit(_call.arguments), builtin->parameters) + visit(_call.arguments) }; - if (!builtin->returns.empty() && !builtin->returns.front().empty() && builtin->returns.front() != "i64"_yulstring) - { - yulAssert(builtin->returns.front() == "i32"_yulstring, "Invalid type " + builtin->returns.front().str()); - call = wasm::BuiltinCall{"i64.extend_i32_u", make_vector(std::move(call))}; - } - return {std::move(call)}; - } } // If this function returns multiple values, then the first one will @@ -166,13 +155,7 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) // The values have to be used right away in an assignment or variable declaration, // so it is handled there. - wasm::FunctionCall funCall{_call.functionName.name.str(), visit(_call.arguments)}; - if (typeConversionNeeded) - // Inject type conversion if needed on the fly. This is just a temporary measure - // and can be removed once we have proper types in Yul. - return injectTypeConversionIfNeeded(std::move(funCall)); - else - return {std::move(funCall)}; + return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)}; } wasm::Expression WasmCodeTransform::operator()(Identifier const& _identifier) @@ -358,40 +341,6 @@ wasm::FunctionDefinition WasmCodeTransform::translateFunction(yul::FunctionDefin return fun; } -wasm::Expression WasmCodeTransform::injectTypeConversionIfNeeded(wasm::FunctionCall _call) const -{ - wasm::FunctionImport const& import = m_functionsToImport.at(YulString{_call.functionName}); - for (size_t i = 0; i < _call.arguments.size(); ++i) - if (import.paramTypes.at(i) == wasm::Type::i32) - _call.arguments[i] = wasm::BuiltinCall{"i32.wrap_i64", make_vector(std::move(_call.arguments[i]))}; - else - yulAssert(import.paramTypes.at(i) == wasm::Type::i64, "Invalid Wasm type"); - - if (import.returnType && *import.returnType != wasm::Type::i64) - { - yulAssert(*import.returnType == wasm::Type::i32, "Invalid Wasm type"); - return wasm::BuiltinCall{"i64.extend_i32_u", make_vector(std::move(_call))}; - } - return {std::move(_call)}; -} - -vector WasmCodeTransform::injectTypeConversionIfNeeded( - vector _arguments, - vector const& _parameterTypes -) const -{ - for (size_t i = 0; i < _arguments.size(); ++i) - if (_parameterTypes.at(i) == "i32"_yulstring) - _arguments[i] = wasm::BuiltinCall{"i32.wrap_i64", make_vector(std::move(_arguments[i]))}; - else - yulAssert( - _parameterTypes.at(i).empty() || _parameterTypes.at(i) == "i64"_yulstring, - "Unknown type " + _parameterTypes.at(i).str() - ); - - return _arguments; -} - string WasmCodeTransform::newLabel() { return m_nameDispenser.newName("label_"_yulstring).str(); diff --git a/libyul/backends/wasm/WasmCodeTransform.h b/libyul/backends/wasm/WasmCodeTransform.h index 5680269f4..68aeb86a3 100644 --- a/libyul/backends/wasm/WasmCodeTransform.h +++ b/libyul/backends/wasm/WasmCodeTransform.h @@ -79,12 +79,6 @@ private: wasm::FunctionDefinition translateFunction(yul::FunctionDefinition const& _funDef); - wasm::Expression injectTypeConversionIfNeeded(wasm::FunctionCall _call) const; - std::vector injectTypeConversionIfNeeded( - std::vector _arguments, - std::vector const& _parameterTypes - ) const; - std::string newLabel(); /// Makes sure that there are at least @a _amount global variables. void allocateGlobals(size_t _amount); From d9ca02b47aad5d53cd7c38efeaceb143f90d2ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 28 May 2020 21:44:52 +0200 Subject: [PATCH 14/16] Enable translation of i32 Yul variables/literals to i32 wasm variables/literals - Until now they were being translated to i64 --- libyul/backends/wasm/WasmCodeTransform.cpp | 117 ++++++++++++++++----- libyul/backends/wasm/WasmCodeTransform.h | 16 ++- 2 files changed, 105 insertions(+), 28 deletions(-) diff --git a/libyul/backends/wasm/WasmCodeTransform.cpp b/libyul/backends/wasm/WasmCodeTransform.cpp index 8de291402..d04d84c0e 100644 --- a/libyul/backends/wasm/WasmCodeTransform.cpp +++ b/libyul/backends/wasm/WasmCodeTransform.cpp @@ -20,6 +20,8 @@ #include +#include + #include #include @@ -40,7 +42,8 @@ wasm::Module WasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ { wasm::Module module; - WasmCodeTransform transform(_dialect, _ast); + TypeInfo typeInfo(_dialect, _ast); + WasmCodeTransform transform(_dialect, _ast, typeInfo); for (auto const& statement: _ast.statements) { @@ -70,14 +73,18 @@ wasm::Expression WasmCodeTransform::generateMultiAssignment( if (_variableNames.size() == 1) return { std::move(assignment) }; - allocateGlobals(_variableNames.size() - 1); + vector typesForGlobals; + for (size_t i = 1; i < _variableNames.size(); ++i) + typesForGlobals.push_back(translatedType(m_typeInfo.typeOfVariable(YulString(_variableNames[i])))); + vector allocatedIndices = allocateGlobals(typesForGlobals); + yulAssert(allocatedIndices.size() == _variableNames.size() - 1, ""); wasm::Block block; block.statements.emplace_back(move(assignment)); for (size_t i = 1; i < _variableNames.size(); ++i) block.statements.emplace_back(wasm::LocalAssignment{ move(_variableNames.at(i)), - make_unique(wasm::GlobalVariable{m_globalVariables.at(i - 1).variableName}) + make_unique(wasm::GlobalVariable{m_globalVariables.at(allocatedIndices[i - 1]).variableName}) }); return { std::move(block) }; } @@ -88,7 +95,7 @@ wasm::Expression WasmCodeTransform::operator()(VariableDeclaration const& _varDe for (auto const& var: _varDecl.variables) { variableNames.emplace_back(var.name.str()); - m_localVariables.emplace_back(wasm::VariableDeclaration{variableNames.back(), wasm::Type::i64}); + m_localVariables.emplace_back(wasm::VariableDeclaration{variableNames.back(), translatedType(var.type)}); } if (_varDecl.value) @@ -165,20 +172,21 @@ wasm::Expression WasmCodeTransform::operator()(Identifier const& _identifier) wasm::Expression WasmCodeTransform::operator()(Literal const& _literal) { - u256 value = valueOfLiteral(_literal); - yulAssert(value <= numeric_limits::max(), "Literal too large: " + value.str()); - return wasm::Literal{static_cast(value)}; + return makeLiteral(translatedType(_literal.type), valueOfLiteral(_literal)); } wasm::Expression WasmCodeTransform::operator()(If const& _if) { - // TODO converting i64 to i32 might not always be needed. + yul::Type conditionType = m_typeInfo.typeOf(*_if.condition); + YulString ne_instruction = YulString(conditionType.str() + ".ne"); + yulAssert(WasmDialect::instance().builtin(ne_instruction), ""); + // TODO converting i64 to i32 might not always be needed. vector args; args.emplace_back(visitReturnByValue(*_if.condition)); - args.emplace_back(wasm::Literal{static_cast(0)}); + args.emplace_back(makeLiteral(translatedType(conditionType), 0)); return wasm::If{ - make_unique(wasm::BuiltinCall{"i64.ne", std::move(args)}), + make_unique(wasm::BuiltinCall{ne_instruction.str(), std::move(args)}), visit(_if.body.statements), {} }; @@ -186,9 +194,13 @@ wasm::Expression WasmCodeTransform::operator()(If const& _if) wasm::Expression WasmCodeTransform::operator()(Switch const& _switch) { + yul::Type expressionType = m_typeInfo.typeOf(*_switch.expression); + YulString eq_instruction = YulString(expressionType.str() + ".eq"); + yulAssert(WasmDialect::instance().builtin(eq_instruction), ""); + wasm::Block block; string condition = m_nameDispenser.newName("condition"_yulstring).str(); - m_localVariables.emplace_back(wasm::VariableDeclaration{condition, wasm::Type::i64}); + m_localVariables.emplace_back(wasm::VariableDeclaration{condition, translatedType(expressionType)}); block.statements.emplace_back(wasm::LocalAssignment{condition, visit(*_switch.expression)}); vector* currentBlock = &block.statements; @@ -197,7 +209,7 @@ wasm::Expression WasmCodeTransform::operator()(Switch const& _switch) Case const& c = _switch.cases.at(i); if (c.value) { - wasm::BuiltinCall comparison{"i64.eq", make_vector( + wasm::BuiltinCall comparison{eq_instruction.str(), make_vector( wasm::LocalVariable{condition}, visitReturnByValue(*c.value) )}; @@ -236,11 +248,15 @@ wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for) string continueLabel = newLabel(); m_breakContinueLabelNames.push({breakLabel, continueLabel}); + yul::Type conditionType = m_typeInfo.typeOf(*_for.condition); + YulString eqz_instruction = YulString(conditionType.str() + ".eqz"); + yulAssert(WasmDialect::instance().builtin(eqz_instruction), ""); + wasm::Loop loop; loop.labelName = newLabel(); loop.statements = visit(_for.pre.statements); loop.statements.emplace_back(wasm::BranchIf{wasm::Label{breakLabel}, make_unique( - wasm::BuiltinCall{"i64.eqz", make_vector( + wasm::BuiltinCall{eqz_instruction.str(), make_vector( visitReturnByValue(*_for.condition) )} )}); @@ -308,11 +324,11 @@ wasm::FunctionDefinition WasmCodeTransform::translateFunction(yul::FunctionDefin wasm::FunctionDefinition fun; fun.name = _fun.name.str(); for (auto const& param: _fun.parameters) - fun.parameters.push_back({param.name.str(), wasm::Type::i64}); + fun.parameters.push_back({param.name.str(), translatedType(param.type)}); for (auto const& retParam: _fun.returnVariables) - fun.locals.emplace_back(wasm::VariableDeclaration{retParam.name.str(), wasm::Type::i64}); + fun.locals.emplace_back(wasm::VariableDeclaration{retParam.name.str(), translatedType(retParam.type)}); if (!_fun.returnVariables.empty()) - fun.returnType = wasm::Type::i64; + fun.returnType = translatedType(_fun.returnVariables[0].type); yulAssert(m_localVariables.empty(), ""); yulAssert(m_functionBodyLabel.empty(), ""); @@ -330,10 +346,15 @@ wasm::FunctionDefinition WasmCodeTransform::translateFunction(yul::FunctionDefin { // First return variable is returned directly, the others are stored // in globals. - allocateGlobals(_fun.returnVariables.size() - 1); + vector typesForGlobals; + for (size_t i = 1; i < _fun.returnVariables.size(); ++i) + typesForGlobals.push_back(translatedType(_fun.returnVariables[i].type)); + vector allocatedIndices = allocateGlobals(typesForGlobals); + yulAssert(allocatedIndices.size() == _fun.returnVariables.size() - 1, ""); + for (size_t i = 1; i < _fun.returnVariables.size(); ++i) fun.body.emplace_back(wasm::GlobalAssignment{ - m_globalVariables.at(i - 1).variableName, + m_globalVariables.at(allocatedIndices[i - 1]).variableName, make_unique(wasm::LocalVariable{_fun.returnVariables.at(i).name.str()}) }); fun.body.emplace_back(wasm::LocalVariable{_fun.returnVariables.front().name.str()}); @@ -346,13 +367,45 @@ string WasmCodeTransform::newLabel() return m_nameDispenser.newName("label_"_yulstring).str(); } -void WasmCodeTransform::allocateGlobals(size_t _amount) +vector WasmCodeTransform::allocateGlobals(vector const& _typesForGlobals) { - while (m_globalVariables.size() < _amount) - m_globalVariables.emplace_back(wasm::GlobalVariableDeclaration{ - m_nameDispenser.newName("global_"_yulstring).str(), - wasm::Type::i64 - }); + map availableGlobals; + for (wasm::GlobalVariableDeclaration const& global: m_globalVariables) + ++availableGlobals[global.type]; + + map neededGlobals; + for (wasm::Type const& type: _typesForGlobals) + ++neededGlobals[type]; + + for (auto [type, neededGlobalCount]: neededGlobals) + while (availableGlobals[type] < neededGlobalCount) + { + m_globalVariables.emplace_back(wasm::GlobalVariableDeclaration{ + m_nameDispenser.newName("global_"_yulstring).str(), + type, + }); + + ++availableGlobals[type]; + } + + vector allocatedIndices; + map nextGlobal; + for (wasm::Type const& type: _typesForGlobals) + { + while (m_globalVariables[nextGlobal[type]].type != type) + ++nextGlobal[type]; + + allocatedIndices.push_back(nextGlobal[type]++); + } + + yulAssert(all_of( + allocatedIndices.begin(), + allocatedIndices.end(), + [this](size_t index){ return index < m_globalVariables.size(); } + ), ""); + yulAssert(allocatedIndices.size() == set(allocatedIndices.begin(), allocatedIndices.end()).size(), "Indices not unique"); + yulAssert(allocatedIndices.size() == _typesForGlobals.size(), ""); + return allocatedIndices; } wasm::Type WasmCodeTransform::translatedType(yul::Type _yulType) @@ -364,3 +417,19 @@ wasm::Type WasmCodeTransform::translatedType(yul::Type _yulType) else yulAssert(false, "This Yul type does not have a corresponding type in Wasm."); } + +wasm::Literal WasmCodeTransform::makeLiteral(wasm::Type _type, u256 _value) +{ + if (_type == wasm::Type::i32) + { + yulAssert(_value <= numeric_limits::max(), "Literal too large: " + _value.str()); + return wasm::Literal{static_cast(_value)}; + } + else if (_type == wasm::Type::i64) + { + yulAssert(_value <= numeric_limits::max(), "Literal too large: " + _value.str()); + return wasm::Literal{static_cast(_value)}; + } + else + yulAssert(false, "Invalid Wasm literal type"); +} diff --git a/libyul/backends/wasm/WasmCodeTransform.h b/libyul/backends/wasm/WasmCodeTransform.h index 68aeb86a3..326778b3d 100644 --- a/libyul/backends/wasm/WasmCodeTransform.h +++ b/libyul/backends/wasm/WasmCodeTransform.h @@ -24,6 +24,9 @@ #include #include #include +#include + +#include #include #include @@ -56,10 +59,12 @@ public: private: WasmCodeTransform( Dialect const& _dialect, - Block const& _ast + Block const& _ast, + TypeInfo& _typeInfo ): m_dialect(_dialect), - m_nameDispenser(_dialect, _ast) + m_nameDispenser(_dialect, _ast), + m_typeInfo(_typeInfo) {} std::unique_ptr visit(yul::Expression const& _expression); @@ -80,10 +85,12 @@ private: wasm::FunctionDefinition translateFunction(yul::FunctionDefinition const& _funDef); std::string newLabel(); - /// Makes sure that there are at least @a _amount global variables. - void allocateGlobals(size_t _amount); + /// Selects a subset of global variables matching specified sequence of variable types. + /// Defines more global variables of a given type if there's not enough. + std::vector allocateGlobals(std::vector const& _typesForGlobals); static wasm::Type translatedType(yul::Type _yulType); + static wasm::Literal makeLiteral(wasm::Type _type, u256 _value); Dialect const& m_dialect; NameDispenser m_nameDispenser; @@ -93,6 +100,7 @@ private: std::map m_functionsToImport; std::string m_functionBodyLabel; std::stack> m_breakContinueLabelNames; + TypeInfo& m_typeInfo; }; } From 50b426e37b39652db797511c66711df3a2034c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 5 Jun 2020 21:36:44 +0200 Subject: [PATCH 15/16] Update the expected output of command-line tests after wasm changes --- test/cmdlineTests/evm_to_wasm/output | 18 +-- test/cmdlineTests/evm_to_wasm_break/output | 110 +++++++++--------- .../standard_eWasm_requested/output.json | 52 ++++----- .../output | 20 ++-- .../output | 4 +- 5 files changed, 102 insertions(+), 102 deletions(-) diff --git a/test/cmdlineTests/evm_to_wasm/output b/test/cmdlineTests/evm_to_wasm/output index 7e8bd21cc..11b63c6a2 100644 --- a/test/cmdlineTests/evm_to_wasm/output +++ b/test/cmdlineTests/evm_to_wasm/output @@ -45,7 +45,7 @@ object "object" { Binary representation: -0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010acb01052b01017e0240420021004200200020002000200010054220200020002000420110054200a74220a710000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b3f0002402000a7200110043700002000a74208a76aada7200210043700002000a74210a76aada7200310043700002000a74218a76aada7200410043700000b0b +0061736d0100000001160460000060017e017e60057f7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010abc01052901017e0240420021004100200020002000200010054120200020002000420110054100412010000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b32000240200020011004370000200041086a20021004370000200041106a20031004370000200041186a200410043700000b0b Text representation: (module @@ -57,9 +57,9 @@ Text representation: (local $_1 i64) (block $label_ (local.set $_1 (i64.const 0)) - (call $mstore_internal (i64.const 0) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) - (call $mstore_internal (i64.const 32) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 1)) - (call $eth.storageStore (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 32))) + (call $mstore_internal (i32.const 0) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) + (call $mstore_internal (i32.const 32) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 1)) + (call $eth.storageStore (i32.const 0) (i32.const 32)) ) ) @@ -101,16 +101,16 @@ Text representation: ) (func $mstore_internal - (param $pos i64) + (param $pos i32) (param $y1 i64) (param $y2 i64) (param $y3 i64) (param $y4 i64) (block $label__4 - (i64.store (i32.wrap_i64 (local.get $pos)) (call $endian_swap (local.get $y1))) - (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 8))))) (call $endian_swap (local.get $y2))) - (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 16))))) (call $endian_swap (local.get $y3))) - (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 24))))) (call $endian_swap (local.get $y4))) + (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))) ) ) diff --git a/test/cmdlineTests/evm_to_wasm_break/output b/test/cmdlineTests/evm_to_wasm_break/output index 4d5e10921..ddeb04529 100644 --- a/test/cmdlineTests/evm_to_wasm_break/output +++ b/test/cmdlineTests/evm_to_wasm_break/output @@ -154,7 +154,7 @@ object "object" { Binary representation: -0061736d0100000001480a60000060017e017e60027e7e017e60037e7e7e017e60047e7e7e7e017e60057e7e7e7e7e0060087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003070407020704010101050605030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020aa5090df302011f7e02404200210002402000200020002000100921012300210223012103230221040b2001210520022106200321072004210842012109200020008420002009848450ada745ada745ad210a02400340200aa745ad500d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f20108420112012848450ada745ad42005204400c030b024020052006200720082000200020004202100621132300211423012115230221160b201320148420152016848450ada745ad42005204400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a848450ada745ad42005204400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2f01037e0240200020017c2105200520027c21032005200054ada72003200554ada772ada7ad21040b2004240020030b72010b7e0240200320077c210c200c42007c210b024020022006200c200354ada7200b200c54ada772ada7ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2601047e0240200020018420022003848450ada7ad21070b20052400200624012007240220040b4901047e02402000200451ad42005204402001200551ad42005204402002200651ad42005204402003200751ad42005204404201210b0b0b0b0b0b20092400200a2401200b240220080b2d01027e024002402000200154ad21032003420151044042ffffffff0f2102052000200152ad21020b0b0b20020b960101087e02404200210c0240200020041007210d200d42005104400240200120051007210e200e42005104400240200220061007210f200f42005104402003200754ad210c05200f42015104404200210c054201210c0b0b0b05200e42015104404200210c054201210c0b0b0b05200d42015104404200210c054201210c0b0b0b200ca7ad210b0b20092400200a2401200b240220080b8f0101087e02404200200020018420028452ad4200520440000b4200200342208852ad4200520440000b4200a72003a7ada74220a710014200a7290000100c21084200a74208a76aada7290000100c21094200a74210a76aada7290000100c210a4200a74218a76aada7290000100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b3f0002402000a72001100c3700002000a74208a76aada72002100c3700002000a74210a76aada72003100c3700002000a74218a76aada72004100c3700000b0b2500024042002000200120022003100d42202004200520062007100d4200a74220a710000b0b +0061736d0100000001480a60000060017e017e60027e7e017f60037e7e7e017e60047e7e7e7e017e60087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60057f7e7e7e7e0060027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003060406020604010101070505030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020ac3080dde02030a7e017f147e02404200210002402000200020002000100921012300210223012103230221040b20012105200221062003210720042108420121092000200084200020098484504545210a02400340200a45450d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f201084201120128484504504400c030b024020052006200720082000200020004202100621132300211423012115230221160b2013201484201520168484504504400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a8484504504400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2901037e0240200020017c2105200520027c21032005200054200320055472ad21040b2004240020030b6c010b7e0240200320077c210c200c42007c210b024020022006200c200354200b200c5472ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2401047e0240200020018420022003848450ad21070b20052400200624012007240220040b3901047e0240200020045104402001200551044020022006510440200320075104404201210b0b0b0b0b0b20092400200a2401200b240220080b2701027f024002402000200154210320034101460440417f210205200020015221020b0b0b20020b960102047e047f02404100210c0240200020041007210d200d41004604400240200120051007210e200e41004604400240200220061007210f200f41004604402003200754210c05200f41014604404100210c054101210c0b0b0b05200e41014604404100210c054101210c0b0b0b05200d41014604404100210c054101210c0b0b0b200cad210b0b20092400200a2401200b240220080b7601087e024042002000200184200284520440000b42002003422088520440000b41002003a7412010014100290000100c2108410041086a290000100c2109410041106a290000100c210a410041186a290000100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b3200024020002001100c370000200041086a2002100c370000200041106a2003100c370000200041186a2004100c3700000b0b2300024041002000200120022003100d41202004200520062007100d4100412010000b0b Text representation: (module @@ -177,7 +177,7 @@ Text representation: (local $x_6 i64) (local $x_7 i64) (local $_2 i64) - (local $_3 i64) + (local $_3 i32) (local $_4 i64) (local $_5 i64) (local $_6 i64) @@ -212,10 +212,10 @@ Text representation: (local.set $x_6 (local.get $x_2)) (local.set $x_7 (local.get $x_3)) (local.set $_2 (i64.const 1)) - (local.set $_3 (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $_1) (local.get $_1)) (i64.or (local.get $_1) (local.get $_2)))))))))))) + (local.set $_3 (i32.eqz (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_1) (local.get $_1)) (i64.or (local.get $_1) (local.get $_2))))))) (block $label__3 (loop $label__5 - (br_if $label__3 (i64.eqz (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (local.get $_3)))))) + (br_if $label__3 (i32.eqz (i32.eqz (local.get $_3)))) (block $label__4 (block (local.set $_4 (call $lt (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))) @@ -231,7 +231,7 @@ Text representation: (local.set $_11 (global.get $global__2)) ) - (if (i64.ne (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $_8) (local.get $_9)) (i64.or (local.get $_10) (local.get $_11)))))))) (i64.const 0)) (then + (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_8) (local.get $_9)) (i64.or (local.get $_10) (local.get $_11))))) (then (br $label__3) )) (block @@ -241,7 +241,7 @@ Text representation: (local.set $_15 (global.get $global__2)) ) - (if (i64.ne (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $_12) (local.get $_13)) (i64.or (local.get $_14) (local.get $_15)))))))) (i64.const 0)) (then + (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_12) (local.get $_13)) (i64.or (local.get $_14) (local.get $_15))))) (then (br $label__3) )) (block @@ -251,7 +251,7 @@ Text representation: (local.set $_19 (global.get $global__2)) ) - (if (i64.ne (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $_16) (local.get $_17)) (i64.or (local.get $_18) (local.get $_19)))))))) (i64.const 0)) (then + (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_16) (local.get $_17)) (i64.or (local.get $_18) (local.get $_19))))) (then (br $label__4) )) @@ -286,7 +286,7 @@ Text representation: (block $label__6 (local.set $t (i64.add (local.get $x) (local.get $y))) (local.set $r (i64.add (local.get $t) (local.get $c))) - (local.set $r_c (i64.extend_i32_u (i32.wrap_i64 (i64.extend_i32_u (i32.or (i32.wrap_i64 (i64.extend_i32_u (i64.lt_u (local.get $t) (local.get $x)))) (i32.wrap_i64 (i64.extend_i32_u (i64.lt_u (local.get $r) (local.get $t))))))))) + (local.set $r_c (i64.extend_i32_u (i32.or (i64.lt_u (local.get $t) (local.get $x)) (i64.lt_u (local.get $r) (local.get $t))))) ) (global.set $global_ (local.get $r_c)) @@ -318,7 +318,7 @@ Text representation: (local.set $t (i64.add (local.get $x4) (local.get $y4))) (local.set $r4 (i64.add (local.get $t) (i64.const 0))) (block - (local.set $r3_1 (call $add_carry (local.get $x3) (local.get $y3) (i64.extend_i32_u (i32.wrap_i64 (i64.extend_i32_u (i32.or (i32.wrap_i64 (i64.extend_i32_u (i64.lt_u (local.get $t) (local.get $x4)))) (i32.wrap_i64 (i64.extend_i32_u (i64.lt_u (local.get $r4) (local.get $t)))))))))) + (local.set $r3_1 (call $add_carry (local.get $x3) (local.get $y3) (i64.extend_i32_u (i32.or (i64.lt_u (local.get $t) (local.get $x4)) (i64.lt_u (local.get $r4) (local.get $t)))))) (local.set $carry (global.get $global_)) ) @@ -354,7 +354,7 @@ Text representation: (local $r3 i64) (local $r4 i64) (block $label__8 - (local.set $r4 (i64.extend_i32_u (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $x1) (local.get $x2)) (i64.or (local.get $x3) (local.get $x4)))))))) + (local.set $r4 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $x1) (local.get $x2)) (i64.or (local.get $x3) (local.get $x4)))))) ) (global.set $global_ (local.get $r2)) @@ -378,10 +378,10 @@ Text representation: (local $r3 i64) (local $r4 i64) (block $label__9 - (if (i64.ne (i64.extend_i32_u (i64.eq (local.get $x1) (local.get $y1))) (i64.const 0)) (then - (if (i64.ne (i64.extend_i32_u (i64.eq (local.get $x2) (local.get $y2))) (i64.const 0)) (then - (if (i64.ne (i64.extend_i32_u (i64.eq (local.get $x3) (local.get $y3))) (i64.const 0)) (then - (if (i64.ne (i64.extend_i32_u (i64.eq (local.get $x4) (local.get $y4))) (i64.const 0)) (then + (if (i64.eq (local.get $x1) (local.get $y1)) (then + (if (i64.eq (local.get $x2) (local.get $y2)) (then + (if (i64.eq (local.get $x3) (local.get $y3)) (then + (if (i64.eq (local.get $x4) (local.get $y4)) (then (local.set $r4 (i64.const 1)) )) )) @@ -398,16 +398,16 @@ Text representation: (func $cmp (param $a i64) (param $b i64) - (result i64) - (local $r i64) - (local $condition i64) + (result i32) + (local $r i32) + (local $condition i32) (block $label__10 (block - (local.set $condition (i64.extend_i32_u (i64.lt_u (local.get $a) (local.get $b)))) - (if (i64.eq (local.get $condition) (i64.const 1)) (then - (local.set $r (i64.const 4294967295)) + (local.set $condition (i64.lt_u (local.get $a) (local.get $b))) + (if (i32.eq (local.get $condition) (i32.const 1)) (then + (local.set $r (i32.const 4294967295)) )(else - (local.set $r (i64.extend_i32_u (i64.ne (local.get $a) (local.get $b)))) + (local.set $r (i64.ne (local.get $a) (local.get $b))) )) ) @@ -430,50 +430,50 @@ Text representation: (local $z2 i64) (local $z3 i64) (local $z4 i64) - (local $z i64) - (local $condition_12 i64) - (local $condition_13 i64) - (local $condition_14 i64) + (local $z i32) + (local $condition_12 i32) + (local $condition_13 i32) + (local $condition_14 i32) (block $label__11 - (local.set $z (i64.const 0)) + (local.set $z (i32.const 0)) (block (local.set $condition_12 (call $cmp (local.get $x1) (local.get $y1))) - (if (i64.eq (local.get $condition_12) (i64.const 0)) (then + (if (i32.eq (local.get $condition_12) (i32.const 0)) (then (block (local.set $condition_13 (call $cmp (local.get $x2) (local.get $y2))) - (if (i64.eq (local.get $condition_13) (i64.const 0)) (then + (if (i32.eq (local.get $condition_13) (i32.const 0)) (then (block (local.set $condition_14 (call $cmp (local.get $x3) (local.get $y3))) - (if (i64.eq (local.get $condition_14) (i64.const 0)) (then - (local.set $z (i64.extend_i32_u (i64.lt_u (local.get $x4) (local.get $y4)))) + (if (i32.eq (local.get $condition_14) (i32.const 0)) (then + (local.set $z (i64.lt_u (local.get $x4) (local.get $y4))) )(else - (if (i64.eq (local.get $condition_14) (i64.const 1)) (then - (local.set $z (i64.const 0)) + (if (i32.eq (local.get $condition_14) (i32.const 1)) (then + (local.set $z (i32.const 0)) )(else - (local.set $z (i64.const 1)) + (local.set $z (i32.const 1)) )) )) ) )(else - (if (i64.eq (local.get $condition_13) (i64.const 1)) (then - (local.set $z (i64.const 0)) + (if (i32.eq (local.get $condition_13) (i32.const 1)) (then + (local.set $z (i32.const 0)) )(else - (local.set $z (i64.const 1)) + (local.set $z (i32.const 1)) )) )) ) )(else - (if (i64.eq (local.get $condition_12) (i64.const 1)) (then - (local.set $z (i64.const 0)) + (if (i32.eq (local.get $condition_12) (i32.const 1)) (then + (local.set $z (i32.const 0)) )(else - (local.set $z (i64.const 1)) + (local.set $z (i32.const 1)) )) )) ) - (local.set $z4 (i64.extend_i32_u (i32.wrap_i64 (local.get $z)))) + (local.set $z4 (i64.extend_i32_u (local.get $z))) ) (global.set $global_ (local.get $z2)) @@ -497,15 +497,15 @@ Text representation: (local $z3_1 i64) (local $z4_1 i64) (block $label__15 - (if (i64.ne (i64.extend_i32_u (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3)))) (i64.const 0)) (then + (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.extend_i32_u (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32)))) (i64.const 0)) (then + (if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then (unreachable))) - (call $eth.callDataCopy (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.extend_i32_u (i32.wrap_i64 (local.get $x4)))) (i32.wrap_i64 (i64.const 32))) - (local.set $z1_1 (call $endian_swap (i64.load (i32.wrap_i64 (i64.const 0))))) - (local.set $z2_1 (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 8)))))))) - (local.set $z3_1 (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 16)))))))) - (local.set $z4_1 (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 24)))))))) + (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 (local.get $z1_1)) (local.set $z2 (local.get $z2_1)) (local.set $z3 (local.get $z3_1)) @@ -556,16 +556,16 @@ Text representation: ) (func $mstore_internal - (param $pos i64) + (param $pos i32) (param $y1 i64) (param $y2 i64) (param $y3 i64) (param $y4 i64) (block $label__19 - (i64.store (i32.wrap_i64 (local.get $pos)) (call $endian_swap (local.get $y1))) - (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 8))))) (call $endian_swap (local.get $y2))) - (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 16))))) (call $endian_swap (local.get $y3))) - (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 24))))) (call $endian_swap (local.get $y4))) + (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))) ) ) @@ -579,9 +579,9 @@ Text representation: (param $y3 i64) (param $y4 i64) (block $label__20 - (call $mstore_internal (i64.const 0) (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) - (call $mstore_internal (i64.const 32) (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)) - (call $eth.storageStore (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 32))) + (call $mstore_internal (i32.const 0) (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) + (call $mstore_internal (i32.const 32) (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)) + (call $eth.storageStore (i32.const 0) (i32.const 32)) ) ) diff --git a/test/cmdlineTests/standard_eWasm_requested/output.json b/test/cmdlineTests/standard_eWasm_requested/output.json index b604b384d..a3885d128 100644 --- a/test/cmdlineTests/standard_eWasm_requested/output.json +++ b/test/cmdlineTests/standard_eWasm_requested/output.json @@ -9,8 +9,8 @@ (func $main (local $_1 i64) - (local $p i64) - (local $r i64) + (local $p i32) + (local $r i32) (local $_2 i64) (local $z1 i64) (local $z2 i64) @@ -19,19 +19,19 @@ (block $label_ (local.set $_1 (i64.const 0)) (local.set $p (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 64))) - (local.set $r (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $p)) (i32.wrap_i64 (i64.const 64))))) - (if (i64.ne (i64.extend_i32_u (i32.lt_u (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (local.get $p)))) (i64.const 0)) (then + (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))) - (i64.store (i32.wrap_i64 (local.get $r)) (local.get $_2)) - (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (i64.const 8))))) (local.get $_2)) - (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (i64.const 16))))) (local.get $_2)) - (i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (i64.const 24))))) (call $endian_swap (i64.const 128))) - (call $eth.getCallValue (i32.wrap_i64 (i64.const 0))) - (local.set $z1 (call $endian_swap (i64.load (i32.wrap_i64 (i64.const 0))))) - (local.set $z2 (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 8)))))))) - (local.set $z3 (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 16)))))))) - (if (i64.ne (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 24)))))))))))))) (i64.const 0)) (then + (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))) + (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 (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)) @@ -44,14 +44,14 @@ (param $x2 i64) (param $x3 i64) (param $x4 i64) - (result i64) - (local $v i64) + (result i32) + (local $v i32) (block $label__1 - (if (i64.ne (i64.extend_i32_u (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3)))) (i64.const 0)) (then + (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.extend_i32_u (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32)))) (i64.const 0)) (then + (if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then (unreachable))) - (local.set $v (i64.extend_i32_u (i32.wrap_i64 (local.get $x4)))) + (local.set $v (i32.wrap_i64 (local.get $x4))) ) (local.get $v) @@ -62,13 +62,13 @@ (param $x2 i64) (param $x3 i64) (param $x4 i64) - (result i64) - (local $r i64) - (local $p i64) + (result i32) + (local $r i32) + (local $p i32) (block $label__2 (local.set $p (call $u256_to_i32 (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) - (local.set $r (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $p)) (i32.wrap_i64 (i64.const 64))))) - (if (i64.ne (i64.extend_i32_u (i32.lt_u (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (local.get $p)))) (i64.const 0)) (then + (local.set $r (i32.add (local.get $p) (i32.const 64))) + (if (i32.lt_u (local.get $r) (local.get $p)) (then (unreachable))) ) @@ -89,7 +89,7 @@ (param $z3 i64) (param $z4 i64) (block $label__3 - (call $eth.codeCopy (i32.wrap_i64 (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) (i32.wrap_i64 (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4))) (i32.wrap_i64 (call $u256_to_i32 (local.get $z1) (local.get $z2) (local.get $z3) (local.get $z4)))) + (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))) ) ) @@ -140,7 +140,7 @@ (param $y3 i64) (param $y4 i64) (block $label__7 - (call $eth.finish (i32.wrap_i64 (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) (i32.wrap_i64 (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)))) + (call $eth.finish (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))) ) ) @@ -154,7 +154,7 @@ (param $y3 i64) (param $y4 i64) (block $label__8 - (call $eth.revert (i32.wrap_i64 (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) (i32.wrap_i64 (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)))) + (call $eth.revert (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))) ) ) diff --git a/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/output b/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/output index a99808601..4a3ef4eba 100644 --- a/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/output +++ b/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/output @@ -20,24 +20,24 @@ object "object" { Binary representation: -0061736d01000000010c0260000060047e7e7e7e017e020100030302000105030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00000a4a022201047e024002404201420242034204100121002300210123012102230221030b0b0b2501047e0240200121042002210720002105200321060b20052400200624012007240220040b +0061736d01000000010c0260000060047f7e7e7f017e020100030302000105030100010610037f0141000b7f0141000b7e0142000b071102066d656d6f72790200046d61696e00000a52022603017e027f017e024002404101420242034104100121002300210123012102230221030b0b0b2903017e027f017e0240200121042002210720002105200321060b20052400200624012007240220040b Text representation: (module (memory $memory (export "memory") 1) (export "main" (func $main)) - (global $global_ (mut i64) (i64.const 0)) - (global $global__1 (mut i64) (i64.const 0)) + (global $global_ (mut i32) (i32.const 0)) + (global $global__1 (mut i32) (i32.const 0)) (global $global__2 (mut i64) (i64.const 0)) (func $main (local $m i64) - (local $n i64) - (local $p i64) + (local $n i32) + (local $p i32) (local $q i64) (block $label_ (block - (local.set $m (call $multireturn (i64.const 1) (i64.const 2) (i64.const 3) (i64.const 4))) + (local.set $m (call $multireturn (i32.const 1) (i64.const 2) (i64.const 3) (i32.const 4))) (local.set $n (global.get $global_)) (local.set $p (global.get $global__1)) (local.set $q (global.get $global__2)) @@ -48,14 +48,14 @@ Text representation: ) (func $multireturn - (param $a i64) + (param $a i32) (param $b i64) (param $c i64) - (param $d i64) + (param $d i32) (result i64) (local $x i64) - (local $y i64) - (local $z i64) + (local $y i32) + (local $z i32) (local $w i64) (block $label__3 (local.set $x (local.get $b)) diff --git a/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/output b/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/output index ef0b409fb..e0cc44e57 100644 --- a/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/output +++ b/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/output @@ -11,7 +11,7 @@ object "object" { Binary representation: -0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a0f010d0002404201a7422a3c00000b0b +0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a0e010c0002404101422a3c00000b0b Text representation: (module @@ -20,7 +20,7 @@ Text representation: (func $main (block $label_ - (i64.store8 (i32.wrap_i64 (i64.const 1)) (i64.const 42)) + (i64.store8 (i32.const 1) (i64.const 42)) ) ) From 06e1b38d1d24628bb728b1f26d359a420f10f8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 15 Jun 2020 19:30:06 +0200 Subject: [PATCH 16/16] WasmCodeTransform: Convert Yul 'if's with i32 argument directly to wasm 'if' without an extra comparison with zero --- libyul/backends/wasm/WasmCodeTransform.cpp | 27 +++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/libyul/backends/wasm/WasmCodeTransform.cpp b/libyul/backends/wasm/WasmCodeTransform.cpp index d04d84c0e..d2bceaa90 100644 --- a/libyul/backends/wasm/WasmCodeTransform.cpp +++ b/libyul/backends/wasm/WasmCodeTransform.cpp @@ -178,18 +178,23 @@ wasm::Expression WasmCodeTransform::operator()(Literal const& _literal) wasm::Expression WasmCodeTransform::operator()(If const& _if) { yul::Type conditionType = m_typeInfo.typeOf(*_if.condition); - YulString ne_instruction = YulString(conditionType.str() + ".ne"); - yulAssert(WasmDialect::instance().builtin(ne_instruction), ""); - // TODO converting i64 to i32 might not always be needed. - vector args; - args.emplace_back(visitReturnByValue(*_if.condition)); - args.emplace_back(makeLiteral(translatedType(conditionType), 0)); - return wasm::If{ - make_unique(wasm::BuiltinCall{ne_instruction.str(), std::move(args)}), - visit(_if.body.statements), - {} - }; + wasm::Expression condition; + if (conditionType == "i32"_yulstring) + condition = visitReturnByValue(*_if.condition); + else if (conditionType == "i64"_yulstring) + { + vector args; + args.emplace_back(visitReturnByValue(*_if.condition)); + args.emplace_back(makeLiteral(translatedType("i64"_yulstring), 0)); + + // NOTE: `if` in wasm requires an i32 argument + condition = wasm::BuiltinCall{"i64.ne", std::move(args)}; + } + else + yulAssert(false, "Invalid condition type"); + + return wasm::If{make_unique(move(condition)), visit(_if.body.statements), {}}; } wasm::Expression WasmCodeTransform::operator()(Switch const& _switch)