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