Remove EWASM backend.

This commit is contained in:
Alexander Arlt 2023-02-22 10:32:29 -05:00
parent 0a0c389541
commit c5673278a7
1042 changed files with 97 additions and 10946 deletions

View File

@ -4,6 +4,7 @@ Language Features:
Compiler Features: Compiler Features:
* EWasm: Remove EWasm backend.
Bugfixes: Bugfixes:

View File

@ -1,13 +0,0 @@
// 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

View File

@ -93,8 +93,7 @@ Prerequisites
For running all compiler tests you may want to optionally install a few For running all compiler tests you may want to optionally install a few
dependencies (`evmone <https://github.com/ethereum/evmone/releases>`_, dependencies (`evmone <https://github.com/ethereum/evmone/releases>`_,
`libz3 <https://github.com/Z3Prover/z3>`_, and `libz3 <https://github.com/Z3Prover/z3>`_).
`libhera <https://github.com/ewasm/hera>`_).
On macOS systems, some of the testing scripts expect GNU coreutils to be installed. On macOS systems, some of the testing scripts expect GNU coreutils to be installed.
This can be easiest accomplished using Homebrew: ``brew install coreutils``. This can be easiest accomplished using Homebrew: ``brew install coreutils``.
@ -129,13 +128,7 @@ for the ``evmone`` shared object can be specified via the ``ETH_EVMONE`` environ
If you do not have it installed, you can skip these tests by passing the ``--no-semantic-tests`` If you do not have it installed, you can skip these tests by passing the ``--no-semantic-tests``
flag to ``scripts/soltest.sh``. flag to ``scripts/soltest.sh``.
Running Ewasm tests is disabled by default and can be explicitly enabled The ``evmone`` library should both end with the file name
via ``./scripts/soltest.sh --ewasm`` and requires `hera <https://github.com/ewasm/hera>`_
to be found by ``soltest``.
The mechanism for locating the ``hera`` library is the same as for ``evmone``, except that the
variable for specifying an explicit location is called ``ETH_HERA``.
The ``evmone`` and ``hera`` libraries should both end with the file name
extension ``.so`` on Linux, ``.dll`` on Windows systems and ``.dylib`` on macOS. extension ``.so`` on Linux, ``.dll`` on Windows systems and ``.dylib`` on macOS.
For running SMT tests, the ``libz3`` library must be installed and locatable For running SMT tests, the ``libz3`` library must be installed and locatable

View File

@ -404,10 +404,8 @@ Input Description
// evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables // evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables
// evm.methodIdentifiers - The list of function hashes // evm.methodIdentifiers - The list of function hashes
// evm.gasEstimates - Function gas estimates // evm.gasEstimates - Function gas estimates
// ewasm.wast - Ewasm in WebAssembly S-expressions format
// ewasm.wasm - Ewasm in WebAssembly binary format
// //
// Note that using a using `evm`, `evm.bytecode`, `ewasm`, etc. will select every // Note that using a using `evm`, `evm.bytecode`, etc. will select every
// target part of that output. Additionally, `*` can be used as a wildcard to request everything. // target part of that output. Additionally, `*` can be used as a wildcard to request everything.
// //
"outputSelection": { "outputSelection": {
@ -501,7 +499,7 @@ Output Description
// Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc. // Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc.
// See below for complete list of types. // See below for complete list of types.
"type": "TypeError", "type": "TypeError",
// Mandatory: Component where the error originated, such as "general", "ewasm", etc. // Mandatory: Component where the error originated, such as "general" etc.
"component": "general", "component": "general",
// Mandatory ("error", "warning" or "info", but please note that this may be extended in the future) // Mandatory ("error", "warning" or "info", but please note that this may be extended in the future)
"severity": "error", "severity": "error",
@ -617,13 +615,6 @@ Output Description
"heavyLifting()": "infinite" "heavyLifting()": "infinite"
} }
} }
},
// Ewasm related outputs
"ewasm": {
// S-expressions format
"wast": "",
// Binary format (hex string)
"wasm": ""
} }
} }
} }

View File

@ -1194,8 +1194,7 @@ An example Yul Object is shown below:
// executing code is the constructor code) // executing code is the constructor code)
size := datasize("Contract1_deployed") size := datasize("Contract1_deployed")
offset := allocate(size) offset := allocate(size)
// This will turn into a memory->memory copy for Ewasm and // This will turn into a codecopy for EVM
// a codecopy for EVM
datacopy(offset, dataoffset("Contract1_deployed"), size) datacopy(offset, dataoffset("Contract1_deployed"), size)
return(offset, size) return(offset, size)
} }

View File

@ -315,7 +315,6 @@ void CompilerStack::reset(bool _keepSettings)
m_evmVersion = langutil::EVMVersion(); m_evmVersion = langutil::EVMVersion();
m_modelCheckerSettings = ModelCheckerSettings{}; m_modelCheckerSettings = ModelCheckerSettings{};
m_generateIR = false; m_generateIR = false;
m_generateEwasm = false;
m_revertStrings = RevertStrings::Default; m_revertStrings = RevertStrings::Default;
m_optimiserSettings = OptimiserSettings::minimal(); m_optimiserSettings = OptimiserSettings::minimal();
m_metadataLiteralSources = false; m_metadataLiteralSources = false;
@ -681,7 +680,7 @@ bool CompilerStack::compile(State _stopAfter)
{ {
try try
{ {
if (m_viaIR || m_generateIR || m_generateEwasm) if (m_viaIR || m_generateIR)
generateIR(*contract); generateIR(*contract);
if (m_generateEvmBytecode) if (m_generateEvmBytecode)
{ {
@ -690,8 +689,6 @@ bool CompilerStack::compile(State _stopAfter)
else else
compileContract(*contract, otherCompilers); compileContract(*contract, otherCompilers);
} }
if (m_generateEwasm)
generateEwasm(*contract);
} }
catch (Error const& _error) catch (Error const& _error)
{ {
@ -892,22 +889,6 @@ string const& CompilerStack::yulIROptimized(string const& _contractName) const
return contract(_contractName).yulIROptimized; return contract(_contractName).yulIROptimized;
} }
string const& CompilerStack::ewasm(string const& _contractName) const
{
if (m_stackState != CompilationSuccessful)
solThrow(CompilerError, "Compilation was not successful.");
return contract(_contractName).ewasm;
}
evmasm::LinkerObject const& CompilerStack::ewasmObject(string const& _contractName) const
{
if (m_stackState != CompilationSuccessful)
solThrow(CompilerError, "Compilation was not successful.");
return contract(_contractName).ewasmObject;
}
evmasm::LinkerObject const& CompilerStack::object(string const& _contractName) const evmasm::LinkerObject const& CompilerStack::object(string const& _contractName) const
{ {
if (m_stackState != CompilationSuccessful) if (m_stackState != CompilationSuccessful)
@ -1479,42 +1460,6 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
assembleYul(_contract, compiledContract.evmAssembly, compiledContract.evmRuntimeAssembly); assembleYul(_contract, compiledContract.evmAssembly, compiledContract.evmRuntimeAssembly);
} }
void CompilerStack::generateEwasm(ContractDefinition const& _contract)
{
solAssert(m_stackState >= AnalysisPerformed, "");
if (m_hasError)
solThrow(CompilerError, "Called generateEwasm with errors.");
if (!_contract.canBeDeployed())
return;
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
solAssert(!compiledContract.yulIROptimized.empty(), "");
if (!compiledContract.ewasm.empty())
return;
// Re-parse the Yul IR in EVM dialect
yul::YulStack stack(
m_evmVersion,
m_eofVersion,
yul::YulStack::Language::StrictAssembly,
m_optimiserSettings,
m_debugInfoSelection
);
stack.parseAndAnalyze("", compiledContract.yulIROptimized);
stack.optimize();
stack.translate(yul::YulStack::Language::Ewasm);
stack.optimize();
//cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl;
// Turn into Ewasm text representation.
auto result = stack.assemble(yul::YulStack::Machine::Ewasm);
compiledContract.ewasm = std::move(result.assembly);
compiledContract.ewasmObject = std::move(*result.bytecode);
}
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
{ {
solAssert(m_stackState >= AnalysisPerformed, ""); solAssert(m_stackState >= AnalysisPerformed, "");

View File

@ -203,9 +203,6 @@ public:
/// Enable generation of Yul IR code. /// Enable generation of Yul IR code.
void enableIRGeneration(bool _enable = true) { m_generateIR = _enable; } void enableIRGeneration(bool _enable = true) { m_generateIR = _enable; }
/// Enable experimental generation of Ewasm code. If enabled, IR is also generated.
void enableEwasmGeneration(bool _enable = true) { m_generateEwasm = _enable; }
/// @arg _metadataLiteralSources When true, store sources as literals in the contract metadata. /// @arg _metadataLiteralSources When true, store sources as literals in the contract metadata.
/// Must be set before parsing. /// Must be set before parsing.
void useMetadataLiteralSources(bool _metadataLiteralSources); void useMetadataLiteralSources(bool _metadataLiteralSources);
@ -282,12 +279,6 @@ public:
/// @returns the optimized IR representation of a contract. /// @returns the optimized IR representation of a contract.
std::string const& yulIROptimized(std::string const& _contractName) const; std::string const& yulIROptimized(std::string const& _contractName) const;
/// @returns the Ewasm text representation of a contract.
std::string const& ewasm(std::string const& _contractName) const;
/// @returns the Ewasm representation of a contract.
evmasm::LinkerObject const& ewasmObject(std::string const& _contractName) const;
/// @returns the assembled object for a contract. /// @returns the assembled object for a contract.
evmasm::LinkerObject const& object(std::string const& _contractName) const; evmasm::LinkerObject const& object(std::string const& _contractName) const;
@ -389,8 +380,6 @@ private:
evmasm::LinkerObject runtimeObject; ///< Runtime object. evmasm::LinkerObject runtimeObject; ///< Runtime object.
std::string yulIR; ///< Yul IR code. std::string yulIR; ///< Yul IR code.
std::string yulIROptimized; ///< Optimized Yul IR code. std::string yulIROptimized; ///< Optimized Yul IR code.
std::string ewasm; ///< Experimental Ewasm text representation
evmasm::LinkerObject ewasmObject; ///< Experimental Ewasm code
util::LazyInit<std::string const> metadata; ///< The metadata json that will be hashed into the chain. util::LazyInit<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
util::LazyInit<Json::Value const> abi; util::LazyInit<Json::Value const> abi;
util::LazyInit<Json::Value const> storageLayout; util::LazyInit<Json::Value const> storageLayout;
@ -448,10 +437,6 @@ private:
/// Depends on output generated by generateIR. /// Depends on output generated by generateIR.
void generateEVMFromIR(ContractDefinition const& _contract); void generateEVMFromIR(ContractDefinition const& _contract);
/// Generate Ewasm representation for a single contract.
/// Depends on output generated by generateIR.
void generateEwasm(ContractDefinition const& _contract);
/// Links all the known library addresses in the available objects. Any unknown /// Links all the known library addresses in the available objects. Any unknown
/// library will still be kept as an unlinked placeholder in the objects. /// library will still be kept as an unlinked placeholder in the objects.
void link(); void link();
@ -510,7 +495,6 @@ private:
std::map<std::string, std::set<std::string>> m_requestedContractNames; std::map<std::string, std::set<std::string>> m_requestedContractNames;
bool m_generateEvmBytecode = true; bool m_generateEvmBytecode = true;
bool m_generateIR = false; bool m_generateIR = false;
bool m_generateEwasm = false;
std::map<std::string, util::h160> m_libraries; std::map<std::string, util::h160> m_libraries;
ImportRemapper m_importRemapper; ImportRemapper m_importRemapper;
std::map<std::string const, Source> m_sources; std::map<std::string const, Source> m_sources;

View File

@ -179,7 +179,7 @@ bool hashMatchesContent(string const& _hash, string const& _content)
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _artifact, bool _wildcardMatchesExperimental) bool isArtifactRequested(Json::Value const& _outputSelection, string const& _artifact, bool _wildcardMatchesExperimental)
{ {
static set<string> experimental{"ir", "irOptimized", "wast", "ewasm", "ewasm.wast"}; static set<string> experimental{"ir", "irOptimized"};
for (auto const& selectedArtifactJson: _outputSelection) for (auto const& selectedArtifactJson: _outputSelection)
{ {
string const& selectedArtifact = selectedArtifactJson.asString(); string const& selectedArtifact = selectedArtifactJson.asString();
@ -190,7 +190,7 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _art
return true; return true;
else if (selectedArtifact == "*") else if (selectedArtifact == "*")
{ {
// "ir", "irOptimized", "wast" and "ewasm.wast" can only be matched by "*" if activated. // "ir", "irOptimized" can only be matched by "*" if activated.
if (experimental.count(_artifact) == 0 || _wildcardMatchesExperimental) if (experimental.count(_artifact) == 0 || _wildcardMatchesExperimental)
return true; return true;
} }
@ -264,7 +264,6 @@ bool isBinaryRequested(Json::Value const& _outputSelection)
static vector<string> const outputsThatRequireBinaries = vector<string>{ static vector<string> const outputsThatRequireBinaries = vector<string>{
"*", "*",
"ir", "irOptimized", "ir", "irOptimized",
"wast", "wasm", "ewasm.wast", "ewasm.wasm",
"evm.gasEstimates", "evm.legacyAssembly", "evm.assembly" "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly"
} + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode"); } + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode");
@ -295,29 +294,10 @@ bool isEvmBytecodeRequested(Json::Value const& _outputSelection)
return false; return false;
} }
/// @returns true if any Ewasm code was requested. Note that as an exception, '*' does not
/// yet match "ewasm.wast" or "ewasm"
bool isEwasmRequested(Json::Value const& _outputSelection)
{
if (!_outputSelection.isObject())
return false;
for (auto const& fileRequests: _outputSelection)
for (auto const& requests: fileRequests)
for (auto const& request: requests)
if (request == "ewasm" || request == "ewasm.wast")
return true;
return false;
}
/// @returns true if any Yul IR was requested. Note that as an exception, '*' does not /// @returns true if any Yul IR was requested. Note that as an exception, '*' does not
/// yet match "ir" or "irOptimized" /// yet match "ir" or "irOptimized"
bool isIRRequested(Json::Value const& _outputSelection) bool isIRRequested(Json::Value const& _outputSelection)
{ {
if (isEwasmRequested(_outputSelection))
return true;
if (!_outputSelection.isObject()) if (!_outputSelection.isObject())
return false; return false;
@ -1175,7 +1155,6 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
compilerStack.enableEvmBytecodeGeneration(isEvmBytecodeRequested(_inputsAndSettings.outputSelection)); compilerStack.enableEvmBytecodeGeneration(isEvmBytecodeRequested(_inputsAndSettings.outputSelection));
compilerStack.enableIRGeneration(isIRRequested(_inputsAndSettings.outputSelection)); compilerStack.enableIRGeneration(isIRRequested(_inputsAndSettings.outputSelection));
compilerStack.enableEwasmGeneration(isEwasmRequested(_inputsAndSettings.outputSelection));
Json::Value errors = std::move(_inputsAndSettings.errors); Json::Value errors = std::move(_inputsAndSettings.errors);
@ -1374,12 +1353,6 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimized", wildcardMatchesExperimental)) if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimized", wildcardMatchesExperimental))
contractData["irOptimized"] = compilerStack.yulIROptimized(contractName); contractData["irOptimized"] = compilerStack.yulIROptimized(contractName);
// Ewasm
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wast", wildcardMatchesExperimental))
contractData["ewasm"]["wast"] = compilerStack.ewasm(contractName);
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wasm", wildcardMatchesExperimental))
contractData["ewasm"]["wasm"] = compilerStack.ewasmObject(contractName).toHex();
// EVM // EVM
Json::Value evmData(Json::objectValue); Json::Value evmData(Json::objectValue);
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly", wildcardMatchesExperimental)) if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly", wildcardMatchesExperimental))

View File

@ -1,19 +1,3 @@
# This will re-generate the polyfill headers, if any file within libyul/backends/wasm/polyfill/ was modified.
set_directory_properties(PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/libyul/backends/wasm/polyfill/)
set(POLYFILLS Arithmetic Bitwise Comparison Conversion Interface Keccak Logical Memory)
set(GENERATED_POLYFILL_HEADERS)
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)
list(APPEND GENERATED_POLYFILL_HEADERS ${CMAKE_BINARY_DIR}/include/ewasmPolyfills/${polyfill}.h)
endforeach()
add_library(yul add_library(yul
${GENERATED_POLYFILL_HEADERS} ${GENERATED_POLYFILL_HEADERS}
@ -80,20 +64,6 @@ add_library(yul
backends/evm/StackLayoutGenerator.h backends/evm/StackLayoutGenerator.h
backends/evm/VariableReferenceCounter.h backends/evm/VariableReferenceCounter.h
backends/evm/VariableReferenceCounter.cpp backends/evm/VariableReferenceCounter.cpp
backends/wasm/EVMToEwasmTranslator.cpp
backends/wasm/EVMToEwasmTranslator.h
backends/wasm/BinaryTransform.cpp
backends/wasm/BinaryTransform.h
backends/wasm/TextTransform.cpp
backends/wasm/TextTransform.h
backends/wasm/WasmCodeTransform.cpp
backends/wasm/WasmCodeTransform.h
backends/wasm/WasmDialect.cpp
backends/wasm/WasmDialect.h
backends/wasm/WasmObjectCompiler.cpp
backends/wasm/WasmObjectCompiler.h
backends/wasm/WordSizeTransform.cpp
backends/wasm/WordSizeTransform.h
optimiser/ASTCopier.cpp optimiser/ASTCopier.cpp
optimiser/ASTCopier.h optimiser/ASTCopier.h
optimiser/ASTWalker.cpp optimiser/ASTWalker.cpp

View File

@ -16,8 +16,7 @@
*/ */
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
/** /**
* Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5
* Ewasm as output.
*/ */
@ -30,9 +29,6 @@
#include <libyul/backends/evm/EVMDialect.h> #include <libyul/backends/evm/EVMDialect.h>
#include <libyul/backends/evm/EVMObjectCompiler.h> #include <libyul/backends/evm/EVMObjectCompiler.h>
#include <libyul/backends/evm/EVMMetrics.h> #include <libyul/backends/evm/EVMMetrics.h>
#include <libyul/backends/wasm/WasmDialect.h>
#include <libyul/backends/wasm/WasmObjectCompiler.h>
#include <libyul/backends/wasm/EVMToEwasmTranslator.h>
#include <libyul/ObjectParser.h> #include <libyul/ObjectParser.h>
#include <libyul/optimiser/Suite.h> #include <libyul/optimiser/Suite.h>
@ -57,8 +53,6 @@ Dialect const& languageToDialect(YulStack::Language _language, EVMVersion _versi
return EVMDialect::strictAssemblyForEVMObjects(_version); return EVMDialect::strictAssemblyForEVMObjects(_version);
case YulStack::Language::Yul: case YulStack::Language::Yul:
return EVMDialectTyped::instance(_version); return EVMDialectTyped::instance(_version);
case YulStack::Language::Ewasm:
return WasmDialect::instance();
} }
yulAssert(false, ""); yulAssert(false, "");
return Dialect::yulDeprecated(); return Dialect::yulDeprecated();
@ -102,24 +96,6 @@ void YulStack::optimize()
yulAssert(analyzeParsed(), "Invalid source code after optimization."); yulAssert(analyzeParsed(), "Invalid source code after optimization.");
} }
void YulStack::translate(YulStack::Language _targetLanguage)
{
if (m_language == _targetLanguage)
return;
yulAssert(
m_language == Language::StrictAssembly && _targetLanguage == Language::Ewasm,
"Invalid language combination"
);
*m_parserResult = EVMToEwasmTranslator(
languageToDialect(m_language, m_evmVersion),
*this
).run(*parserResult());
m_language = _targetLanguage;
}
bool YulStack::analyzeParsed() bool YulStack::analyzeParsed()
{ {
yulAssert(m_parserResult, ""); yulAssert(m_parserResult, "");
@ -205,18 +181,6 @@ MachineAssemblyObject YulStack::assemble(Machine _machine) const
{ {
case Machine::EVM: case Machine::EVM:
return assembleWithDeployed().first; return assembleWithDeployed().first;
case Machine::Ewasm:
{
yulAssert(m_language == Language::Ewasm, "");
Dialect const& dialect = languageToDialect(m_language, EVMVersion{});
MachineAssemblyObject object;
auto result = WasmObjectCompiler::compile(*m_parserResult, dialect);
object.assembly = std::move(result.first);
object.bytecode = make_shared<evmasm::LinkerObject>();
object.bytecode->bytecode = std::move(result.second);
return object;
}
} }
// unreachable // unreachable
return MachineAssemblyObject(); return MachineAssemblyObject();

View File

@ -16,8 +16,7 @@
*/ */
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
/** /**
* Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and * Full assembly stack that can support EVM-assembly and Yul as input and EVM.
* Ewasm as output.
*/ */
#pragma once #pragma once
@ -60,14 +59,13 @@ struct MachineAssemblyObject
}; };
/* /*
* Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and * Full assembly stack that can support EVM-assembly and Yul as input and EVM as output.
* Ewasm as output.
*/ */
class YulStack: public langutil::CharStreamProvider class YulStack: public langutil::CharStreamProvider
{ {
public: public:
enum class Language { Yul, Assembly, StrictAssembly, Ewasm }; enum class Language { Yul, Assembly, StrictAssembly };
enum class Machine { EVM, Ewasm }; enum class Machine { EVM };
YulStack(): YulStack():
YulStack( YulStack(
@ -105,9 +103,6 @@ public:
/// If the settings (see constructor) disabled the optimizer, nothing is done here. /// If the settings (see constructor) disabled the optimizer, nothing is done here.
void optimize(); void optimize();
/// Translate the source to a different language / dialect.
void translate(Language _targetLanguage);
/// Run the assembly step (should only be called after parseAndAnalyze). /// Run the assembly step (should only be called after parseAndAnalyze).
MachineAssemblyObject assemble(Machine _machine) const; MachineAssemblyObject assemble(Machine _machine) const;

View File

@ -1,722 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Component that transforms internal Wasm representation to binary.
*/
#include <libyul/backends/wasm/BinaryTransform.h>
#include <libyul/Exceptions.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/Visitor.h>
#include <libsolutil/LEB128.h>
#include <range/v3/view/map.hpp>
#include <range/v3/view/reverse.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::yul;
using namespace solidity::yul::wasm;
using namespace solidity::util;
namespace
{
bytes toBytes(uint8_t _b)
{
return bytes(1, _b);
}
enum class LimitsKind: uint8_t
{
Min = 0x00,
MinMax = 0x01,
};
enum class Mutability: uint8_t
{
Const = 0x00,
Var = 0x01,
};
enum class Section: uint8_t
{
CUSTOM = 0x00,
TYPE = 0x01,
IMPORT = 0x02,
FUNCTION = 0x03,
MEMORY = 0x05,
GLOBAL = 0x06,
EXPORT = 0x07,
CODE = 0x0a
};
bytes toBytes(Section _s)
{
return toBytes(uint8_t(_s));
}
enum class ValueType: uint8_t
{
Void = 0x40,
Function = 0x60,
I64 = 0x7e,
I32 = 0x7f
};
bytes toBytes(ValueType _vt)
{
return toBytes(uint8_t(_vt));
}
ValueType toValueType(wasm::Type _type)
{
if (_type == wasm::Type::i32)
return ValueType::I32;
else if (_type == wasm::Type::i64)
return ValueType::I64;
else
yulAssert(false, "Invalid wasm variable type");
}
enum class Export: uint8_t
{
Function = 0x0,
Memory = 0x2
};
bytes toBytes(Export _export)
{
return toBytes(uint8_t(_export));
}
// NOTE: This is a subset of WebAssembly opcodes.
// Those available as a builtin are listed further down.
enum class Opcode: uint8_t
{
Block = 0x02,
Loop = 0x03,
If = 0x04,
Else = 0x05,
End = 0x0b,
Br = 0x0c,
BrIf = 0x0d,
BrTable = 0x0e, // Not used yet.
Return = 0x0f,
Call = 0x10,
CallIndirect = 0x11, // Not used yet.
LocalGet = 0x20,
LocalSet = 0x21,
LocalTee = 0x22, // Not used yet.
GlobalGet = 0x23,
GlobalSet = 0x24,
I32Const = 0x41,
I64Const = 0x42,
};
bytes toBytes(Opcode _o)
{
return toBytes(uint8_t(_o));
}
Opcode constOpcodeFor(ValueType _type)
{
if (_type == ValueType::I32)
return Opcode::I32Const;
else if (_type == ValueType::I64)
return Opcode::I64Const;
else
yulAssert(false, "Values of this type cannot be used with const opcode");
}
static map<string, uint8_t> const builtins = {
{"unreachable", 0x00},
{"nop", 0x01},
{"i32.drop", 0x1a},
{"i64.drop", 0x1a},
{"i32.select", 0x1b},
{"i64.select", 0x1b},
{"i32.load", 0x28},
{"i64.load", 0x29},
{"i32.load8_s", 0x2c},
{"i32.load8_u", 0x2d},
{"i32.load16_s", 0x2e},
{"i32.load16_u", 0x2f},
{"i64.load8_s", 0x30},
{"i64.load8_u", 0x31},
{"i64.load16_s", 0x32},
{"i64.load16_u", 0x33},
{"i64.load32_s", 0x34},
{"i64.load32_u", 0x35},
{"i32.store", 0x36},
{"i64.store", 0x37},
{"i32.store8", 0x3a},
{"i32.store16", 0x3b},
{"i64.store8", 0x3c},
{"i64.store16", 0x3d},
{"i64.store32", 0x3e},
{"memory.size", 0x3f},
{"memory.grow", 0x40},
{"i32.eqz", 0x45},
{"i32.eq", 0x46},
{"i32.ne", 0x47},
{"i32.lt_s", 0x48},
{"i32.lt_u", 0x49},
{"i32.gt_s", 0x4a},
{"i32.gt_u", 0x4b},
{"i32.le_s", 0x4c},
{"i32.le_u", 0x4d},
{"i32.ge_s", 0x4e},
{"i32.ge_u", 0x4f},
{"i64.eqz", 0x50},
{"i64.eq", 0x51},
{"i64.ne", 0x52},
{"i64.lt_s", 0x53},
{"i64.lt_u", 0x54},
{"i64.gt_s", 0x55},
{"i64.gt_u", 0x56},
{"i64.le_s", 0x57},
{"i64.le_u", 0x58},
{"i64.ge_s", 0x59},
{"i64.ge_u", 0x5a},
{"i32.clz", 0x67},
{"i32.ctz", 0x68},
{"i32.popcnt", 0x69},
{"i32.add", 0x6a},
{"i32.sub", 0x6b},
{"i32.mul", 0x6c},
{"i32.div_s", 0x6d},
{"i32.div_u", 0x6e},
{"i32.rem_s", 0x6f},
{"i32.rem_u", 0x70},
{"i32.and", 0x71},
{"i32.or", 0x72},
{"i32.xor", 0x73},
{"i32.shl", 0x74},
{"i32.shr_s", 0x75},
{"i32.shr_u", 0x76},
{"i32.rotl", 0x77},
{"i32.rotr", 0x78},
{"i64.clz", 0x79},
{"i64.ctz", 0x7a},
{"i64.popcnt", 0x7b},
{"i64.add", 0x7c},
{"i64.sub", 0x7d},
{"i64.mul", 0x7e},
{"i64.div_s", 0x7f},
{"i64.div_u", 0x80},
{"i64.rem_s", 0x81},
{"i64.rem_u", 0x82},
{"i64.and", 0x83},
{"i64.or", 0x84},
{"i64.xor", 0x85},
{"i64.shl", 0x86},
{"i64.shr_s", 0x87},
{"i64.shr_u", 0x88},
{"i64.rotl", 0x89},
{"i64.rotr", 0x8a},
{"i32.wrap_i64", 0xa7},
{"i64.extend_i32_s", 0xac},
{"i64.extend_i32_u", 0xad},
};
bytes prefixSize(bytes _data)
{
size_t size = _data.size();
return lebEncode(size) + std::move(_data);
}
bytes makeSection(Section _section, bytes _data)
{
return toBytes(_section) + prefixSize(std::move(_data));
}
/// This is a kind of run-length-encoding of local types.
vector<pair<size_t, ValueType>> groupLocalVariables(vector<VariableDeclaration> _localVariables)
{
vector<pair<size_t, ValueType>> localEntries;
size_t entrySize = 0;
ValueType entryType = ValueType::I32; // Any type would work here
for (VariableDeclaration const& localVariable: _localVariables)
{
ValueType variableType = toValueType(localVariable.type);
if (variableType != entryType)
{
if (entrySize > 0)
localEntries.emplace_back(entrySize, entryType);
entryType = variableType;
entrySize = 0;
}
++entrySize;
}
if (entrySize > 0)
localEntries.emplace_back(entrySize, entryType);
return localEntries;
}
}
bytes BinaryTransform::run(Module const& _module)
{
map<Type, vector<string>> const types = typeToFunctionMap(_module.imports, _module.functions);
map<string, size_t> const globalIDs = enumerateGlobals(_module);
map<string, size_t> const functionIDs = enumerateFunctions(_module);
map<string, size_t> const functionTypes = enumerateFunctionTypes(types);
yulAssert(globalIDs.size() == _module.globals.size(), "");
yulAssert(functionIDs.size() == _module.imports.size() + _module.functions.size(), "");
yulAssert(functionTypes.size() == functionIDs.size(), "");
yulAssert(functionTypes.size() >= types.size(), "");
bytes ret{0, 'a', 's', 'm'};
// version
ret += bytes{1, 0, 0, 0};
ret += typeSection(types);
ret += importSection(_module.imports, functionTypes);
ret += functionSection(_module.functions, functionTypes);
ret += memorySection();
ret += globalSection(_module.globals);
ret += exportSection(functionIDs);
map<string, pair<size_t, size_t>> subModulePosAndSize;
for (auto const& [name, module]: _module.subModules)
{
// TODO should we prefix and / or shorten the name?
bytes data = BinaryTransform::run(module);
size_t const length = data.size();
ret += customSection(name, std::move(data));
// Skip all the previous sections and the size field of this current custom section.
size_t const offset = ret.size() - length;
subModulePosAndSize[name] = {offset, length};
}
for (auto const& [name, data]: _module.customSections)
{
size_t const length = data.size();
ret += customSection(name, data);
// Skip all the previous sections and the size field of this current custom section.
size_t const offset = ret.size() - length;
subModulePosAndSize[name] = {offset, length};
}
BinaryTransform bt(
std::move(globalIDs),
std::move(functionIDs),
std::move(functionTypes),
std::move(subModulePosAndSize)
);
ret += bt.codeSection(_module.functions);
return ret;
}
bytes BinaryTransform::operator()(Literal const& _literal)
{
return std::visit(GenericVisitor{
[&](uint32_t _value) -> bytes { return toBytes(Opcode::I32Const) + lebEncodeSigned(static_cast<int32_t>(_value)); },
[&](uint64_t _value) -> bytes { return toBytes(Opcode::I64Const) + lebEncodeSigned(static_cast<int64_t>(_value)); },
}, _literal.value);
}
bytes BinaryTransform::operator()(StringLiteral const&)
{
// StringLiteral is a special AST element used for certain builtins.
// It is not mapped to actual WebAssembly, and should be processed in visit(BuiltinCall).
yulAssert(false, "");
}
bytes BinaryTransform::operator()(LocalVariable const& _variable)
{
return toBytes(Opcode::LocalGet) + lebEncode(m_locals.at(_variable.name));
}
bytes BinaryTransform::operator()(GlobalVariable const& _variable)
{
return toBytes(Opcode::GlobalGet) + lebEncode(m_globalIDs.at(_variable.name));
}
bytes BinaryTransform::operator()(BuiltinCall const& _call)
{
// We need to avoid visiting the arguments of `dataoffset` and `datasize` because
// they are references to object names that should not end up in the code.
if (_call.functionName == "dataoffset")
{
string name = get<StringLiteral>(_call.arguments.at(0)).value;
// TODO: support the case where name refers to the current object
yulAssert(m_subModulePosAndSize.count(name), "");
return toBytes(Opcode::I64Const) + lebEncodeSigned(static_cast<int64_t>(m_subModulePosAndSize.at(name).first));
}
else if (_call.functionName == "datasize")
{
string name = get<StringLiteral>(_call.arguments.at(0)).value;
// TODO: support the case where name refers to the current object
yulAssert(m_subModulePosAndSize.count(name), "");
return toBytes(Opcode::I64Const) + lebEncodeSigned(static_cast<int64_t>(m_subModulePosAndSize.at(name).second));
}
yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found");
// NOTE: the dialect ensures we have the right amount of arguments
bytes args = visit(_call.arguments);
bytes ret = std::move(args) + toBytes(builtins.at(_call.functionName));
if (
_call.functionName.find(".load") != string::npos ||
_call.functionName.find(".store") != string::npos
)
// Alignment hint and offset. Interpreters ignore the alignment. JITs/AOTs can take it
// into account to generate more efficient code but if the hint is invalid it could
// actually be more expensive. It's best to hint at 1-byte alignment if we don't plan
// to control the memory layout accordingly.
ret += bytes{{0, 0}}; // 2^0 == 1-byte alignment
return ret;
}
bytes BinaryTransform::operator()(FunctionCall const& _call)
{
return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functionIDs.at(_call.functionName));
}
bytes BinaryTransform::operator()(LocalAssignment const& _assignment)
{
return
std::visit(*this, *_assignment.value) +
toBytes(Opcode::LocalSet) +
lebEncode(m_locals.at(_assignment.variableName));
}
bytes BinaryTransform::operator()(GlobalAssignment const& _assignment)
{
return
std::visit(*this, *_assignment.value) +
toBytes(Opcode::GlobalSet) +
lebEncode(m_globalIDs.at(_assignment.variableName));
}
bytes BinaryTransform::operator()(If const& _if)
{
bytes result =
std::visit(*this, *_if.condition) +
toBytes(Opcode::If) +
toBytes(ValueType::Void);
m_labels.emplace_back();
result += visit(_if.statements);
if (_if.elseStatements)
result += toBytes(Opcode::Else) + visit(*_if.elseStatements);
m_labels.pop_back();
result += toBytes(Opcode::End);
return result;
}
bytes BinaryTransform::operator()(Loop const& _loop)
{
bytes result = toBytes(Opcode::Loop) + toBytes(ValueType::Void);
m_labels.emplace_back(_loop.labelName);
result += visit(_loop.statements);
m_labels.pop_back();
result += toBytes(Opcode::End);
return result;
}
bytes BinaryTransform::operator()(Branch const& _branch)
{
return toBytes(Opcode::Br) + encodeLabelIdx(_branch.label.name);
}
bytes BinaryTransform::operator()(BranchIf const& _branchIf)
{
bytes result = std::visit(*this, *_branchIf.condition);
result += toBytes(Opcode::BrIf) + encodeLabelIdx(_branchIf.label.name);
return result;
}
bytes BinaryTransform::operator()(Return const&)
{
// Note that this does not work if the function returns a value.
return toBytes(Opcode::Return);
}
bytes BinaryTransform::operator()(Block const& _block)
{
m_labels.emplace_back(_block.labelName);
bytes result =
toBytes(Opcode::Block) +
toBytes(ValueType::Void) +
visit(_block.statements) +
toBytes(Opcode::End);
m_labels.pop_back();
return result;
}
bytes BinaryTransform::operator()(FunctionDefinition const& _function)
{
bytes ret;
vector<pair<size_t, ValueType>> localEntries = groupLocalVariables(_function.locals);
ret += lebEncode(localEntries.size());
for (pair<size_t, ValueType> const& entry: localEntries)
{
ret += lebEncode(entry.first);
ret += toBytes(entry.second);
}
m_locals.clear();
size_t varIdx = 0;
for (size_t i = 0; i < _function.parameters.size(); ++i)
m_locals[_function.parameters[i].name] = varIdx++;
for (size_t i = 0; i < _function.locals.size(); ++i)
m_locals[_function.locals[i].variableName] = varIdx++;
yulAssert(m_labels.empty(), "Stray labels.");
ret += visit(_function.body);
ret += toBytes(Opcode::End);
yulAssert(m_labels.empty(), "Stray labels.");
return prefixSize(std::move(ret));
}
BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import)
{
return {
encodeTypes(_import.paramTypes),
encodeTypes(_import.returnType ? vector<wasm::Type>(1, *_import.returnType) : vector<wasm::Type>())
};
}
BinaryTransform::Type BinaryTransform::typeOf(FunctionDefinition const& _funDef)
{
return {
encodeTypes(_funDef.parameters),
encodeTypes(_funDef.returnType ? vector<wasm::Type>(1, *_funDef.returnType) : vector<wasm::Type>())
};
}
uint8_t BinaryTransform::encodeType(wasm::Type _type)
{
return uint8_t(toValueType(_type));
}
vector<uint8_t> BinaryTransform::encodeTypes(vector<wasm::Type> const& _types)
{
vector<uint8_t> result;
for (wasm::Type t: _types)
result.emplace_back(encodeType(t));
return result;
}
vector<uint8_t> BinaryTransform::encodeTypes(wasm::TypedNameList const& _typedNameList)
{
vector<uint8_t> result;
for (TypedName const& typedName: _typedNameList)
result.emplace_back(encodeType(typedName.type));
return result;
}
map<BinaryTransform::Type, vector<string>> BinaryTransform::typeToFunctionMap(
vector<wasm::FunctionImport> const& _imports,
vector<wasm::FunctionDefinition> const& _functions
)
{
map<Type, vector<string>> types;
for (auto const& import: _imports)
types[typeOf(import)].emplace_back(import.internalName);
for (auto const& fun: _functions)
types[typeOf(fun)].emplace_back(fun.name);
return types;
}
map<string, size_t> BinaryTransform::enumerateGlobals(Module const& _module)
{
map<string, size_t> globals;
for (size_t i = 0; i < _module.globals.size(); ++i)
globals[_module.globals[i].variableName] = i;
return globals;
}
map<string, size_t> BinaryTransform::enumerateFunctions(Module const& _module)
{
map<string, size_t> functions;
size_t funID = 0;
for (FunctionImport const& fun: _module.imports)
functions[fun.internalName] = funID++;
for (FunctionDefinition const& fun: _module.functions)
functions[fun.name] = funID++;
return functions;
}
map<string, size_t> BinaryTransform::enumerateFunctionTypes(map<Type, vector<string>> const& _typeToFunctionMap)
{
map<string, size_t> functionTypes;
size_t typeID = 0;
for (vector<string> const& funNames: _typeToFunctionMap | ranges::views::values)
{
for (string const& name: funNames)
functionTypes[name] = typeID;
++typeID;
}
return functionTypes;
}
bytes BinaryTransform::typeSection(map<BinaryTransform::Type, vector<string>> const& _typeToFunctionMap)
{
bytes result;
size_t index = 0;
for (Type const& type: _typeToFunctionMap | ranges::views::keys)
{
result += toBytes(ValueType::Function);
result += lebEncode(type.first.size()) + type.first;
result += lebEncode(type.second.size()) + type.second;
index++;
}
return makeSection(Section::TYPE, lebEncode(index) + std::move(result));
}
bytes BinaryTransform::importSection(
vector<FunctionImport> const& _imports,
map<string, size_t> const& _functionTypes
)
{
bytes result = lebEncode(_imports.size());
for (FunctionImport const& import: _imports)
{
uint8_t importKind = 0; // function
result +=
encodeName(import.module) +
encodeName(import.externalName) +
toBytes(importKind) +
lebEncode(_functionTypes.at(import.internalName));
}
return makeSection(Section::IMPORT, std::move(result));
}
bytes BinaryTransform::functionSection(
vector<FunctionDefinition> const& _functions,
map<string, size_t> const& _functionTypes
)
{
bytes result = lebEncode(_functions.size());
for (auto const& fun: _functions)
result += lebEncode(_functionTypes.at(fun.name));
return makeSection(Section::FUNCTION, std::move(result));
}
bytes BinaryTransform::memorySection()
{
bytes result = lebEncode(1);
result.push_back(static_cast<uint8_t>(LimitsKind::Min));
result.push_back(1); // initial length
return makeSection(Section::MEMORY, std::move(result));
}
bytes BinaryTransform::globalSection(vector<wasm::GlobalVariableDeclaration> const& _globals)
{
bytes result = lebEncode(_globals.size());
for (wasm::GlobalVariableDeclaration const& global: _globals)
{
ValueType globalType = toValueType(global.type);
result +=
toBytes(globalType) +
lebEncode(static_cast<uint8_t>(Mutability::Var)) +
toBytes(constOpcodeFor(globalType)) +
lebEncodeSigned(0) +
toBytes(Opcode::End);
}
return makeSection(Section::GLOBAL, std::move(result));
}
bytes BinaryTransform::exportSection(map<string, size_t> const& _functionIDs)
{
bool hasMain = _functionIDs.count("main");
bytes result = lebEncode(hasMain ? 2 : 1);
result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0);
if (hasMain)
result += encodeName("main") + toBytes(Export::Function) + lebEncode(_functionIDs.at("main"));
return makeSection(Section::EXPORT, std::move(result));
}
bytes BinaryTransform::customSection(string const& _name, bytes _data)
{
bytes result = encodeName(_name) + std::move(_data);
return makeSection(Section::CUSTOM, std::move(result));
}
bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _functions)
{
bytes result = lebEncode(_functions.size());
for (FunctionDefinition const& fun: _functions)
result += (*this)(fun);
return makeSection(Section::CODE, std::move(result));
}
bytes BinaryTransform::visit(vector<Expression> const& _expressions)
{
bytes result;
for (auto const& expr: _expressions)
result += std::visit(*this, expr);
return result;
}
bytes BinaryTransform::visitReversed(vector<Expression> const& _expressions)
{
bytes result;
for (auto const& expr: _expressions | ranges::views::reverse)
result += std::visit(*this, expr);
return result;
}
bytes BinaryTransform::encodeLabelIdx(string const& _label) const
{
yulAssert(!_label.empty(), "Empty label.");
size_t depth = 0;
for (string const& label: m_labels | ranges::views::reverse)
if (label == _label)
return lebEncode(depth);
else
++depth;
yulAssert(false, "Label not found.");
}
bytes BinaryTransform::encodeName(string const& _name)
{
// UTF-8 is allowed here by the Wasm spec, but since all names here should stem from
// Solidity or Yul identifiers or similar, non-ascii characters ending up here
// is a very bad sign.
for (char c: _name)
yulAssert(uint8_t(c) <= 0x7f, "Non-ascii character found.");
return lebEncode(_name.size()) + asBytes(_name);
}

View File

@ -1,124 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Component that transforms internal Wasm representation to binary.
*/
#pragma once
#include <libyul/backends/wasm/WasmAST.h>
#include <libsolutil/Common.h>
#include <vector>
#include <stack>
namespace solidity::yul::wasm
{
/**
* Web assembly to binary transform.
*/
class BinaryTransform
{
public:
static bytes run(Module const& _module);
bytes operator()(wasm::Literal const& _literal);
bytes operator()(wasm::StringLiteral const& _literal);
bytes operator()(wasm::LocalVariable const& _identifier);
bytes operator()(wasm::GlobalVariable const& _identifier);
bytes operator()(wasm::BuiltinCall const& _builinCall);
bytes operator()(wasm::FunctionCall const& _functionCall);
bytes operator()(wasm::LocalAssignment const& _assignment);
bytes operator()(wasm::GlobalAssignment const& _assignment);
bytes operator()(wasm::If const& _if);
bytes operator()(wasm::Loop const& _loop);
bytes operator()(wasm::Branch const& _branch);
bytes operator()(wasm::BranchIf const& _branchIf);
bytes operator()(wasm::Return const& _return);
bytes operator()(wasm::Block const& _block);
bytes operator()(wasm::FunctionDefinition const& _function);
private:
BinaryTransform(
std::map<std::string, size_t> _globalIDs,
std::map<std::string, size_t> _functionIDs,
std::map<std::string, size_t> _functionTypes,
std::map<std::string, std::pair<size_t, size_t>> _subModulePosAndSize
):
m_globalIDs(std::move(_globalIDs)),
m_functionIDs(std::move(_functionIDs)),
m_functionTypes(std::move(_functionTypes)),
m_subModulePosAndSize(std::move(_subModulePosAndSize))
{}
using Type = std::pair<std::vector<std::uint8_t>, std::vector<std::uint8_t>>;
static Type typeOf(wasm::FunctionImport const& _import);
static Type typeOf(wasm::FunctionDefinition const& _funDef);
static uint8_t encodeType(wasm::Type _type);
static std::vector<uint8_t> encodeTypes(std::vector<wasm::Type> const& _types);
static std::vector<uint8_t> encodeTypes(wasm::TypedNameList const& _typedNameList);
static std::map<Type, std::vector<std::string>> typeToFunctionMap(
std::vector<wasm::FunctionImport> const& _imports,
std::vector<wasm::FunctionDefinition> const& _functions
);
static std::map<std::string, size_t> enumerateGlobals(Module const& _module);
static std::map<std::string, size_t> enumerateFunctions(Module const& _module);
static std::map<std::string, size_t> enumerateFunctionTypes(
std::map<Type, std::vector<std::string>> const& _typeToFunctionMap
);
static bytes typeSection(std::map<Type, std::vector<std::string>> const& _typeToFunctionMap);
static bytes importSection(
std::vector<wasm::FunctionImport> const& _imports,
std::map<std::string, size_t> const& _functionTypes
);
static bytes functionSection(
std::vector<wasm::FunctionDefinition> const& _functions,
std::map<std::string, size_t> const& _functionTypes
);
static bytes memorySection();
static bytes globalSection(std::vector<wasm::GlobalVariableDeclaration> const& _globals);
static bytes exportSection(std::map<std::string, size_t> const& _functionIDs);
static bytes customSection(std::string const& _name, bytes _data);
bytes codeSection(std::vector<wasm::FunctionDefinition> const& _functions);
bytes visit(std::vector<wasm::Expression> const& _expressions);
bytes visitReversed(std::vector<wasm::Expression> const& _expressions);
bytes encodeLabelIdx(std::string const& _label) const;
static bytes encodeName(std::string const& _name);
std::map<std::string, size_t> const m_globalIDs;
std::map<std::string, size_t> const m_functionIDs;
std::map<std::string, size_t> const m_functionTypes;
/// The map of submodules, where the pair refers to the [offset, length]. The offset is
/// an absolute offset within the resulting assembled bytecode.
std::map<std::string, std::pair<size_t, size_t>> const m_subModulePosAndSize;
std::map<std::string, size_t> m_locals;
std::vector<std::string> m_labels;
};
}

View File

@ -1,159 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Translates Yul code from EVM dialect to Ewasm dialect.
*/
#include <libyul/backends/wasm/EVMToEwasmTranslator.h>
#include <libyul/backends/wasm/WordSizeTransform.h>
#include <libyul/backends/wasm/WasmDialect.h>
#include <libyul/optimiser/ExpressionSplitter.h>
#include <libyul/optimiser/FunctionGrouper.h>
#include <libyul/optimiser/MainFunction.h>
#include <libyul/optimiser/FunctionHoister.h>
#include <libyul/optimiser/Disambiguator.h>
#include <libyul/optimiser/NameDisplacer.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
#include <libyul/AST.h>
#include <libyul/AsmParser.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/Object.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Scanner.h>
#include <liblangutil/SourceReferenceFormatter.h>
#include <liblangutil/CharStreamProvider.h>
#include <libsolidity/interface/OptimiserSettings.h>
// The following headers are generated from the
// yul files placed in libyul/backends/wasm/polyfill.
#include <ewasmPolyfills/Arithmetic.h>
#include <ewasmPolyfills/Bitwise.h>
#include <ewasmPolyfills/Comparison.h>
#include <ewasmPolyfills/Conversion.h>
#include <ewasmPolyfills/Interface.h>
#include <ewasmPolyfills/Keccak.h>
#include <ewasmPolyfills/Logical.h>
#include <ewasmPolyfills/Memory.h>
using namespace std;
using namespace solidity;
using namespace solidity::yul;
using namespace solidity::util;
using namespace solidity::langutil;
Object EVMToEwasmTranslator::run(Object const& _object)
{
if (!m_polyfill)
parsePolyfill();
Block ast = std::get<Block>(Disambiguator(m_dialect, *_object.analysisInfo)(*_object.code));
set<YulString> reservedIdentifiers;
NameDispenser nameDispenser{m_dialect, ast, reservedIdentifiers};
// expectedExecutionsPerDeployment is currently unused.
OptimiserStepContext context{
m_dialect,
nameDispenser,
reservedIdentifiers,
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment
};
FunctionHoister::run(context, ast);
FunctionGrouper::run(context, ast);
MainFunction::run(context, ast);
ForLoopConditionIntoBody::run(context, ast);
ExpressionSplitter::run(context, ast);
WordSizeTransform::run(m_dialect, WasmDialect::instance(), ast, nameDispenser);
NameDisplacer{nameDispenser, m_polyfillFunctions}(ast);
for (auto const& st: m_polyfill->statements)
ast.statements.emplace_back(ASTCopier{}.translate(st));
Object ret;
ret.name = _object.name;
ret.code = make_shared<Block>(std::move(ast));
ret.debugData = _object.debugData;
ret.analysisInfo = make_shared<AsmAnalysisInfo>();
ErrorList errors;
ErrorReporter errorReporter(errors);
AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, WasmDialect::instance(), {}, _object.qualifiedDataNames());
if (!analyzer.analyze(*ret.code))
{
string message = "Invalid code generated after EVM to wasm translation.\n";
message += "Note that the source locations in the errors below will reference the original, not the translated code.\n";
message += "Translated code:\n";
message += "----------------------------------\n";
message += ret.toString(&WasmDialect::instance());
message += "----------------------------------\n";
for (auto const& err: errors)
message += langutil::SourceReferenceFormatter::formatErrorInformation(*err, m_charStreamProvider);
yulAssert(false, message);
}
for (auto const& subObjectNode: _object.subObjects)
if (Object const* subObject = dynamic_cast<Object const*>(subObjectNode.get()))
ret.subObjects.push_back(make_shared<Object>(run(*subObject)));
else
ret.subObjects.push_back(make_shared<Data>(dynamic_cast<Data const&>(*subObjectNode)));
ret.subIndexByName = _object.subIndexByName;
return ret;
}
void EVMToEwasmTranslator::parsePolyfill()
{
ErrorList errors;
ErrorReporter errorReporter(errors);
CharStream 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) +
"}", "");
// Passing an empty SourceLocation() here is a workaround to prevent a crash
// when compiling from yul->ewasm. We're stripping nativeLocation and
// originLocation from the AST (but we only really need to strip nativeLocation)
m_polyfill = Parser(errorReporter, WasmDialect::instance(), langutil::SourceLocation()).parse(charStream);
if (!errors.empty())
{
string message;
for (auto const& err: errors)
message += langutil::SourceReferenceFormatter::formatErrorInformation(
*err,
SingletonCharStreamProvider(charStream)
);
yulAssert(false, message);
}
m_polyfillFunctions.clear();
for (auto const& statement: m_polyfill->statements)
m_polyfillFunctions.insert(std::get<FunctionDefinition>(statement).name);
}

View File

@ -1,55 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Translates Yul code from EVM dialect to Ewasm dialect.
*/
#pragma once
#include <libyul/ASTForward.h>
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/Dialect.h>
namespace solidity::langutil
{
class CharStreamProvider;
}
namespace solidity::yul
{
struct Object;
class EVMToEwasmTranslator: public ASTModifier
{
public:
EVMToEwasmTranslator(Dialect const& _evmDialect, langutil::CharStreamProvider const& _charStreamProvider):
m_dialect(_evmDialect),
m_charStreamProvider(_charStreamProvider)
{}
Object run(Object const& _object);
private:
void parsePolyfill();
Dialect const& m_dialect;
langutil::CharStreamProvider const& m_charStreamProvider;
std::shared_ptr<Block> m_polyfill;
std::set<YulString> m_polyfillFunctions;
};
}

View File

@ -1,246 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Component that transforms internal Wasm representation to text.
*/
#include <libyul/backends/wasm/BinaryTransform.h>
#include <libyul/backends/wasm/TextTransform.h>
#include <libyul/Exceptions.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/Numeric.h>
#include <libsolutil/Keccak256.h>
#include <libsolutil/StringUtils.h>
#include <libsolutil/Visitor.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <range/v3/view/transform.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::yul;
using namespace solidity::yul::wasm;
using namespace solidity::util;
string TextTransform::run(wasm::Module const& _module)
{
string ret = "(module\n";
for (auto const& [name, module]: _module.subModules)
ret +=
" ;; custom section for sub-module\n"
" ;; The Keccak-256 hash of the text representation of \"" +
name +
"\": " +
toHex(keccak256(run(module))) +
"\n"
" ;; (@custom \"" +
name +
"\" \"" +
util::toHex(BinaryTransform::run(module)) +
"\")\n";
for (auto const& [name, data]: _module.customSections)
ret +=
" ;; custom section for data\n"
" ;; (@custom \"" +
name +
"\" \"" +
util::toHex(data) +
"\")\n";
for (wasm::FunctionImport const& imp: _module.imports)
{
ret += " (import \"" + imp.module + "\" \"" + imp.externalName + "\" (func $" + imp.internalName;
if (!imp.paramTypes.empty())
ret += " (param" + joinHumanReadablePrefixed(imp.paramTypes | ranges::views::transform(encodeType), " ", " ") + ")";
if (imp.returnType)
ret += " (result " + encodeType(*imp.returnType) + ")";
ret += "))\n";
}
// allocate one 64k page of memory and make it available to the Ethereum client
ret += " (memory $memory (export \"memory\") 1)\n";
for (auto const& f: _module.functions)
if (f.name == "main")
{
// export the main function
ret += " (export \"main\" (func $main))\n";
break;
}
for (auto const& g: _module.globals)
ret += " (global $" + g.variableName + " (mut " + encodeType(g.type) + ") (" + encodeType(g.type) + ".const 0))\n";
ret += "\n";
for (auto const& f: _module.functions)
ret += transform(f) + "\n";
return std::move(ret) + ")\n";
}
string TextTransform::operator()(wasm::Literal const& _literal)
{
return std::visit(GenericVisitor{
[&](uint32_t _value) -> string { return "(i32.const " + to_string(_value) + ")"; },
[&](uint64_t _value) -> string { return "(i64.const " + to_string(_value) + ")"; },
}, _literal.value);
}
string TextTransform::operator()(wasm::StringLiteral const& _literal)
{
// StringLiteral is a special AST element used for certain builtins.
// The output of this will not be valid WebAssembly.
string quoted = boost::replace_all_copy(_literal.value, "\\", "\\\\");
boost::replace_all(quoted, "\"", "\\\"");
return "\"" + quoted + "\"";
}
string TextTransform::operator()(wasm::LocalVariable const& _identifier)
{
return "(local.get $" + _identifier.name + ")";
}
string TextTransform::operator()(wasm::GlobalVariable const& _identifier)
{
return "(global.get $" + _identifier.name + ")";
}
string TextTransform::operator()(wasm::BuiltinCall const& _builtinCall)
{
string args = joinTransformed(_builtinCall.arguments);
string funcName = _builtinCall.functionName;
// These are prefixed in the dialect, but are actually overloaded instructions in WebAssembly.
if (funcName == "i32.drop" || funcName == "i64.drop")
funcName = "drop";
else if (funcName == "i32.select" || funcName == "i64.select")
funcName = "select";
return "(" + funcName + (args.empty() ? "" : " " + args) + ")";
}
string TextTransform::operator()(wasm::FunctionCall const& _functionCall)
{
string args = joinTransformed(_functionCall.arguments);
return "(call $" + _functionCall.functionName + (args.empty() ? "" : " " + args) + ")";
}
string TextTransform::operator()(wasm::LocalAssignment const& _assignment)
{
return "(local.set $" + _assignment.variableName + " " + visit(*_assignment.value) + ")\n";
}
string TextTransform::operator()(wasm::GlobalAssignment const& _assignment)
{
return "(global.set $" + _assignment.variableName + " " + visit(*_assignment.value) + ")\n";
}
string TextTransform::operator()(wasm::If const& _if)
{
string text = "(if " + visit(*_if.condition) + " (then\n" + indented(joinTransformed(_if.statements, '\n')) + ")";
if (_if.elseStatements)
text += "(else\n" + indented(joinTransformed(*_if.elseStatements, '\n')) + ")";
return std::move(text) + ")\n";
}
string TextTransform::operator()(wasm::Loop const& _loop)
{
string label = _loop.labelName.empty() ? "" : " $" + _loop.labelName;
return "(loop" + std::move(label) + "\n" + indented(joinTransformed(_loop.statements, '\n')) + ")\n";
}
string TextTransform::operator()(wasm::Branch const& _branch)
{
return "(br $" + _branch.label.name + ")\n";
}
string TextTransform::operator()(wasm::BranchIf const& _branchIf)
{
return "(br_if $" + _branchIf.label.name + " " + visit(*_branchIf.condition) + ")\n";
}
string TextTransform::operator()(wasm::Return const&)
{
return "(return)\n";
}
string TextTransform::operator()(wasm::Block const& _block)
{
string label = _block.labelName.empty() ? "" : " $" + _block.labelName;
return "(block" + std::move(label) + "\n" + indented(joinTransformed(_block.statements, '\n')) + "\n)\n";
}
string TextTransform::indented(string const& _in)
{
string replacement;
if (!_in.empty())
{
replacement.reserve(_in.size() + 4);
replacement += " ";
for (auto it = _in.begin(); it != _in.end(); ++it)
if (*it == '\n' && it + 1 != _in.end() && *(it + 1) != '\n')
replacement += "\n ";
else
replacement += *it;
}
return replacement;
}
string TextTransform::transform(wasm::FunctionDefinition const& _function)
{
string ret = "(func $" + _function.name + "\n";
for (auto const& param: _function.parameters)
ret += " (param $" + param.name + " " + encodeType(param.type) + ")\n";
if (_function.returnType.has_value())
ret += " (result " + encodeType(_function.returnType.value()) + ")\n";
for (auto const& local: _function.locals)
ret += " (local $" + local.variableName + " " + encodeType(local.type) + ")\n";
ret += indented(joinTransformed(_function.body, '\n'));
if (ret.back() != '\n')
ret += '\n';
ret += ")\n";
return ret;
}
string TextTransform::visit(wasm::Expression const& _expression)
{
return std::visit(*this, _expression);
}
string TextTransform::joinTransformed(vector<wasm::Expression> const& _expressions, char _separator)
{
string ret;
for (auto const& e: _expressions)
{
string t = visit(e);
if (!t.empty() && !ret.empty() && ret.back() != '\n')
ret += _separator;
ret += std::move(t);
}
return ret;
}
string TextTransform::encodeType(wasm::Type _type)
{
if (_type == wasm::Type::i32)
return "i32";
else if (_type == wasm::Type::i64)
return "i64";
else
yulAssert(false, "Invalid wasm type");
}

View File

@ -1,71 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Component that transforms internal Wasm representation to text.
*/
#pragma once
#include <libyul/backends/wasm/WasmAST.h>
#include <vector>
namespace solidity::yul
{
struct AsmAnalysisInfo;
}
namespace solidity::yul::wasm
{
class TextTransform
{
public:
std::string run(wasm::Module const& _module);
public:
std::string operator()(wasm::Literal const& _literal);
std::string operator()(wasm::StringLiteral const& _literal);
std::string operator()(wasm::LocalVariable const& _identifier);
std::string operator()(wasm::GlobalVariable const& _identifier);
std::string operator()(wasm::BuiltinCall const& _builinCall);
std::string operator()(wasm::FunctionCall const& _functionCall);
std::string operator()(wasm::LocalAssignment const& _assignment);
std::string operator()(wasm::GlobalAssignment const& _assignment);
std::string operator()(wasm::If const& _if);
std::string operator()(wasm::Loop const& _loop);
std::string operator()(wasm::Branch const& _branch);
std::string operator()(wasm::BranchIf const& _branchIf);
std::string operator()(wasm::Return const& _return);
std::string operator()(wasm::Block const& _block);
private:
std::string indented(std::string const& _in);
std::string transform(wasm::FunctionDefinition const& _function);
std::string visit(wasm::Expression const& _expression);
std::string joinTransformed(
std::vector<wasm::Expression> const& _expressions,
char _separator = ' '
);
static std::string encodeType(wasm::Type _type);
};
}

View File

@ -1,117 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Simplified in-memory representation of a Wasm AST.
*/
#pragma once
#include <libsolutil/Common.h>
#include <variant>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <optional>
namespace solidity::yul::wasm
{
enum class Type
{
i32,
i64,
};
struct TypedName { std::string name; Type type; };
using TypedNameList = std::vector<TypedName>;
struct Literal;
struct StringLiteral;
struct LocalVariable;
struct GlobalVariable;
struct FunctionCall;
struct BuiltinCall;
struct LocalAssignment;
struct GlobalAssignment;
struct Block;
struct If;
struct Loop;
struct Branch;
struct BranchIf;
struct Return;
using Expression = std::variant<
Literal, StringLiteral, LocalVariable, GlobalVariable,
FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment,
Block, If, Loop, Branch, BranchIf, Return
>;
struct Literal { std::variant<uint32_t, uint64_t> value; };
// This is a special AST element used for certain builtins. It is not mapped to actual WebAssembly.
struct StringLiteral { std::string value; };
struct LocalVariable { std::string name; };
struct GlobalVariable { std::string name; };
struct Label { std::string name; };
struct FunctionCall { std::string functionName; std::vector<Expression> arguments; };
struct BuiltinCall { std::string functionName; std::vector<Expression> arguments; };
struct LocalAssignment { std::string variableName; std::unique_ptr<Expression> value; };
struct GlobalAssignment { std::string variableName; std::unique_ptr<Expression> value; };
struct Block { std::string labelName; std::vector<Expression> statements; };
struct If {
std::unique_ptr<Expression> condition;
std::vector<Expression> statements;
std::unique_ptr<std::vector<Expression>> elseStatements;
};
struct Loop { std::string labelName; std::vector<Expression> statements; };
struct Branch { Label label; };
struct Return {};
struct BranchIf { Label label; std::unique_ptr<Expression> condition; };
struct VariableDeclaration { std::string variableName; Type type; };
struct GlobalVariableDeclaration { std::string variableName; Type type; };
struct FunctionImport {
std::string module;
std::string externalName;
std::string internalName;
std::vector<Type> paramTypes;
std::optional<Type> returnType;
};
struct FunctionDefinition
{
std::string name;
std::vector<TypedName> parameters;
std::optional<Type> returnType;
std::vector<VariableDeclaration> locals;
std::vector<Expression> body;
};
/**
* Abstract representation of a wasm module.
*/
struct Module
{
std::vector<GlobalVariableDeclaration> globals;
std::vector<FunctionImport> imports;
std::vector<FunctionDefinition> functions;
std::map<std::string, Module> subModules;
std::map<std::string, bytes> customSections;
};
}

View File

@ -1,449 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Common code generator for translating Yul / inline assembly to Wasm.
*/
#include <libyul/backends/wasm/WasmCodeTransform.h>
#include <libyul/backends/wasm/WasmDialect.h>
#include <libyul/AST.h>
#include <libyul/Dialect.h>
#include <libyul/Utilities.h>
#include <libyul/Exceptions.h>
#include <liblangutil/Exceptions.h>
#include <optional>
#include <limits>
using namespace std;
using namespace solidity;
using namespace solidity::yul;
using namespace solidity::util;
wasm::Module WasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast)
{
wasm::Module module;
TypeInfo typeInfo(_dialect, _ast);
WasmCodeTransform transform(_dialect, _ast, typeInfo);
for (auto const& statement: _ast.statements)
{
yulAssert(
holds_alternative<yul::FunctionDefinition>(statement),
"Expected only function definitions at the highest level."
);
if (holds_alternative<yul::FunctionDefinition>(statement))
module.functions.emplace_back(transform.translateFunction(std::get<yul::FunctionDefinition>(statement)));
}
for (auto& imp: transform.m_functionsToImport)
module.imports.emplace_back(std::move(imp.second));
module.globals = transform.m_globalVariables;
return module;
}
wasm::Expression WasmCodeTransform::generateMultiAssignment(
vector<string> _variableNames,
unique_ptr<wasm::Expression> _firstValue
)
{
yulAssert(!_variableNames.empty(), "");
wasm::LocalAssignment assignment{std::move(_variableNames.front()), std::move(_firstValue)};
if (_variableNames.size() == 1)
return { std::move(assignment) };
vector<wasm::Type> typesForGlobals;
for (size_t i = 1; i < _variableNames.size(); ++i)
typesForGlobals.push_back(translatedType(m_typeInfo.typeOfVariable(YulString(_variableNames[i]))));
vector<size_t> allocatedIndices = allocateGlobals(typesForGlobals);
yulAssert(allocatedIndices.size() == _variableNames.size() - 1, "");
wasm::Block block;
block.statements.emplace_back(std::move(assignment));
for (size_t i = 1; i < _variableNames.size(); ++i)
block.statements.emplace_back(wasm::LocalAssignment{
std::move(_variableNames.at(i)),
make_unique<wasm::Expression>(wasm::GlobalVariable{m_globalVariables.at(allocatedIndices[i - 1]).variableName})
});
return { std::move(block) };
}
wasm::Expression WasmCodeTransform::operator()(yul::VariableDeclaration const& _varDecl)
{
vector<string> variableNames;
for (auto const& var: _varDecl.variables)
{
variableNames.emplace_back(var.name.str());
m_localVariables.emplace_back(wasm::VariableDeclaration{variableNames.back(), translatedType(var.type)});
}
if (_varDecl.value)
return generateMultiAssignment(std::move(variableNames), visit(*_varDecl.value));
else
return wasm::BuiltinCall{"nop", {}};
}
wasm::Expression WasmCodeTransform::operator()(yul::Assignment const& _assignment)
{
vector<string> variableNames;
for (auto const& var: _assignment.variableNames)
variableNames.emplace_back(var.name.str());
return generateMultiAssignment(std::move(variableNames), visit(*_assignment.value));
}
wasm::Expression WasmCodeTransform::operator()(yul::ExpressionStatement const& _statement)
{
return visitReturnByValue(_statement.expression);
}
void WasmCodeTransform::importBuiltinFunction(BuiltinFunction const* _builtin, string const& _module, string const& _externalName, string const& _internalName)
{
yulAssert(_builtin, "");
yulAssert(_builtin->returns.size() <= 1, "");
// Imported function, use regular call, but mark for import.
YulString internalName(_internalName);
if (!m_functionsToImport.count(internalName))
{
wasm::FunctionImport imp{
_module,
_externalName,
_internalName,
{},
_builtin->returns.empty() ? nullopt : make_optional<wasm::Type>(translatedType(_builtin->returns.front()))
};
for (auto const& param: _builtin->parameters)
imp.paramTypes.emplace_back(translatedType(param));
m_functionsToImport[internalName] = std::move(imp);
}
}
wasm::Expression WasmCodeTransform::operator()(yul::FunctionCall const& _call)
{
if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name))
{
if (_call.functionName.name.str().substr(0, 6) == "debug.")
importBuiltinFunction(builtin, "debug", builtin->name.str().substr(6), builtin->name.str());
else if (_call.functionName.name.str().substr(0, 4) == "eth.")
importBuiltinFunction(builtin, "ethereum", builtin->name.str().substr(4), builtin->name.str());
else
{
vector<wasm::Expression> arguments;
for (size_t i = 0; i < _call.arguments.size(); i++)
if (builtin->literalArgument(i))
{
yulAssert(builtin->literalArgument(i) == LiteralKind::String, "");
arguments.emplace_back(wasm::StringLiteral{std::get<Literal>(_call.arguments[i]).value.str()});
}
else
arguments.emplace_back(visitReturnByValue(_call.arguments[i]));
return wasm::BuiltinCall{_call.functionName.name.str(), std::move(arguments)};
}
}
// If this function returns multiple values, then the first one will
// be returned in the expression itself and the others in global variables.
// The values have to be used right away in an assignment or variable declaration,
// so it is handled there.
return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)};
}
wasm::Expression WasmCodeTransform::operator()(yul::Identifier const& _identifier)
{
return wasm::LocalVariable{_identifier.name.str()};
}
wasm::Expression WasmCodeTransform::operator()(yul::Literal const& _literal)
{
return makeLiteral(translatedType(_literal.type), valueOfLiteral(_literal));
}
wasm::Expression WasmCodeTransform::operator()(yul::If const& _if)
{
yul::Type conditionType = m_typeInfo.typeOf(*_if.condition);
wasm::Expression condition;
if (conditionType == "i32"_yulstring)
condition = visitReturnByValue(*_if.condition);
else if (conditionType == "i64"_yulstring)
{
vector<wasm::Expression> args;
args.emplace_back(visitReturnByValue(*_if.condition));
args.emplace_back(makeLiteral(translatedType("i64"_yulstring), 0));
// NOTE: `if` in wasm requires an i32 argument
condition = wasm::BuiltinCall{"i64.ne", std::move(args)};
}
else
yulAssert(false, "Invalid condition type");
return wasm::If{make_unique<wasm::Expression>(std::move(condition)), visit(_if.body.statements), {}};
}
wasm::Expression WasmCodeTransform::operator()(yul::Switch const& _switch)
{
yul::Type expressionType = m_typeInfo.typeOf(*_switch.expression);
YulString eq_instruction = YulString(expressionType.str() + ".eq");
yulAssert(WasmDialect::instance().builtin(eq_instruction), "");
wasm::Block block;
string condition = m_nameDispenser.newName("condition"_yulstring).str();
m_localVariables.emplace_back(wasm::VariableDeclaration{condition, translatedType(expressionType)});
block.statements.emplace_back(wasm::LocalAssignment{condition, visit(*_switch.expression)});
vector<wasm::Expression>* currentBlock = &block.statements;
for (size_t i = 0; i < _switch.cases.size(); ++i)
{
Case const& c = _switch.cases.at(i);
if (c.value)
{
wasm::BuiltinCall comparison{eq_instruction.str(), make_vector<wasm::Expression>(
wasm::LocalVariable{condition},
visitReturnByValue(*c.value)
)};
wasm::If ifStmnt{
make_unique<wasm::Expression>(std::move(comparison)),
visit(c.body.statements),
{}
};
vector<wasm::Expression>* nextBlock = nullptr;
if (i != _switch.cases.size() - 1)
{
ifStmnt.elseStatements = make_unique<vector<wasm::Expression>>();
nextBlock = ifStmnt.elseStatements.get();
}
currentBlock->emplace_back(std::move(ifStmnt));
currentBlock = nextBlock;
}
else
{
yulAssert(i == _switch.cases.size() - 1, "Default case must be last.");
*currentBlock += visit(c.body.statements);
}
}
return { std::move(block) };
}
wasm::Expression WasmCodeTransform::operator()(yul::FunctionDefinition const&)
{
yulAssert(false, "Should not have visited here.");
return {};
}
wasm::Expression WasmCodeTransform::operator()(yul::ForLoop const& _for)
{
string breakLabel = newLabel();
string continueLabel = newLabel();
m_breakContinueLabelNames.push({breakLabel, continueLabel});
yul::Type conditionType = m_typeInfo.typeOf(*_for.condition);
YulString eqz_instruction = YulString(conditionType.str() + ".eqz");
yulAssert(WasmDialect::instance().builtin(eqz_instruction), "");
std::vector<wasm::Expression> statements = visit(_for.pre.statements);
wasm::Loop loop;
loop.labelName = newLabel();
loop.statements.emplace_back(wasm::BranchIf{wasm::Label{breakLabel}, make_unique<wasm::Expression>(
wasm::BuiltinCall{eqz_instruction.str(), make_vector<wasm::Expression>(
visitReturnByValue(*_for.condition)
)}
)});
loop.statements.emplace_back(wasm::Block{continueLabel, visit(_for.body.statements)});
loop.statements += visit(_for.post.statements);
loop.statements.emplace_back(wasm::Branch{wasm::Label{loop.labelName}});
statements += make_vector<wasm::Expression>(std::move(loop));
return wasm::Block{breakLabel, std::move(statements)};
}
wasm::Expression WasmCodeTransform::operator()(yul::Break const&)
{
yulAssert(m_breakContinueLabelNames.size() > 0, "");
return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().first}};
}
wasm::Expression WasmCodeTransform::operator()(yul::Continue const&)
{
yulAssert(m_breakContinueLabelNames.size() > 0, "");
return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().second}};
}
wasm::Expression WasmCodeTransform::operator()(yul::Leave const&)
{
yulAssert(!m_functionBodyLabel.empty(), "");
return wasm::Branch{wasm::Label{m_functionBodyLabel}};
}
wasm::Expression WasmCodeTransform::operator()(yul::Block const& _block)
{
return wasm::Block{{}, visit(_block.statements)};
}
unique_ptr<wasm::Expression> WasmCodeTransform::visit(yul::Expression const& _expression)
{
return make_unique<wasm::Expression>(std::visit(*this, _expression));
}
wasm::Expression WasmCodeTransform::visitReturnByValue(yul::Expression const& _expression)
{
return std::visit(*this, _expression);
}
vector<wasm::Expression> WasmCodeTransform::visit(vector<yul::Expression> const& _expressions)
{
vector<wasm::Expression> ret;
for (auto const& e: _expressions)
ret.emplace_back(visitReturnByValue(e));
return ret;
}
wasm::Expression WasmCodeTransform::visit(yul::Statement const& _statement)
{
return std::visit(*this, _statement);
}
vector<wasm::Expression> WasmCodeTransform::visit(vector<yul::Statement> const& _statements)
{
vector<wasm::Expression> ret;
for (auto const& s: _statements)
ret.emplace_back(visit(s));
return ret;
}
wasm::FunctionDefinition WasmCodeTransform::translateFunction(yul::FunctionDefinition const& _fun)
{
wasm::FunctionDefinition fun;
fun.name = _fun.name.str();
for (auto const& param: _fun.parameters)
fun.parameters.push_back({param.name.str(), translatedType(param.type)});
for (auto const& retParam: _fun.returnVariables)
fun.locals.emplace_back(wasm::VariableDeclaration{retParam.name.str(), translatedType(retParam.type)});
if (!_fun.returnVariables.empty())
fun.returnType = translatedType(_fun.returnVariables[0].type);
yulAssert(m_localVariables.empty(), "");
yulAssert(m_functionBodyLabel.empty(), "");
m_functionBodyLabel = newLabel();
fun.body.emplace_back(wasm::Expression(wasm::Block{
m_functionBodyLabel,
visit(_fun.body.statements)
}));
fun.locals += m_localVariables;
m_localVariables.clear();
m_functionBodyLabel = {};
if (!_fun.returnVariables.empty())
{
// First return variable is returned directly, the others are stored
// in globals.
vector<wasm::Type> typesForGlobals;
for (size_t i = 1; i < _fun.returnVariables.size(); ++i)
typesForGlobals.push_back(translatedType(_fun.returnVariables[i].type));
vector<size_t> allocatedIndices = allocateGlobals(typesForGlobals);
yulAssert(allocatedIndices.size() == _fun.returnVariables.size() - 1, "");
for (size_t i = 1; i < _fun.returnVariables.size(); ++i)
fun.body.emplace_back(wasm::GlobalAssignment{
m_globalVariables.at(allocatedIndices[i - 1]).variableName,
make_unique<wasm::Expression>(wasm::LocalVariable{_fun.returnVariables.at(i).name.str()})
});
fun.body.emplace_back(wasm::LocalVariable{_fun.returnVariables.front().name.str()});
}
return fun;
}
string WasmCodeTransform::newLabel()
{
return m_nameDispenser.newName("label_"_yulstring).str();
}
vector<size_t> WasmCodeTransform::allocateGlobals(vector<wasm::Type> const& _typesForGlobals)
{
map<wasm::Type, size_t> availableGlobals;
for (wasm::GlobalVariableDeclaration const& global: m_globalVariables)
++availableGlobals[global.type];
map<wasm::Type, size_t> neededGlobals;
for (wasm::Type const& type: _typesForGlobals)
++neededGlobals[type];
for (auto [type, neededGlobalCount]: neededGlobals)
while (availableGlobals[type] < neededGlobalCount)
{
m_globalVariables.emplace_back(wasm::GlobalVariableDeclaration{
m_nameDispenser.newName("global_"_yulstring).str(),
type,
});
++availableGlobals[type];
}
vector<size_t> allocatedIndices;
map<wasm::Type, size_t> nextGlobal;
for (wasm::Type const& type: _typesForGlobals)
{
while (m_globalVariables[nextGlobal[type]].type != type)
++nextGlobal[type];
allocatedIndices.push_back(nextGlobal[type]++);
}
yulAssert(all_of(
allocatedIndices.begin(),
allocatedIndices.end(),
[this](size_t index){ return index < m_globalVariables.size(); }
), "");
yulAssert(allocatedIndices.size() == set<size_t>(allocatedIndices.begin(), allocatedIndices.end()).size(), "Indices not unique");
yulAssert(allocatedIndices.size() == _typesForGlobals.size(), "");
return allocatedIndices;
}
wasm::Type WasmCodeTransform::translatedType(yul::Type _yulType)
{
if (_yulType == "i32"_yulstring)
return wasm::Type::i32;
else if (_yulType == "i64"_yulstring)
return wasm::Type::i64;
else
yulAssert(false, "This Yul type does not have a corresponding type in Wasm.");
}
wasm::Literal WasmCodeTransform::makeLiteral(wasm::Type _type, u256 _value)
{
if (_type == wasm::Type::i32)
{
yulAssert(_value <= numeric_limits<uint32_t>::max(), "Literal too large: " + _value.str());
return wasm::Literal{static_cast<uint32_t>(_value)};
}
else if (_type == wasm::Type::i64)
{
yulAssert(_value <= numeric_limits<uint64_t>::max(), "Literal too large: " + _value.str());
return wasm::Literal{static_cast<uint64_t>(_value)};
}
else
yulAssert(false, "Invalid Wasm literal type");
}

View File

@ -1,115 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Common code generator for translating Yul / inline assembly to Wasm.
*/
#pragma once
#include <libyul/backends/wasm/WasmAST.h>
#include <libyul/ASTForward.h>
#include <libyul/Dialect.h>
#include <libyul/optimiser/NameDispenser.h>
#include <libyul/optimiser/TypeInfo.h>
#include <libsolutil/Common.h>
#include <libsolutil/Numeric.h>
#include <stack>
#include <map>
namespace solidity::yul
{
struct AsmAnalysisInfo;
class WasmCodeTransform
{
public:
static wasm::Module run(Dialect const& _dialect, yul::Block const& _ast);
public:
wasm::Expression operator()(yul::Literal const& _literal);
wasm::Expression operator()(yul::Identifier const& _identifier);
wasm::Expression operator()(yul::FunctionCall const&);
wasm::Expression operator()(yul::ExpressionStatement const& _statement);
wasm::Expression operator()(yul::Assignment const& _assignment);
wasm::Expression operator()(yul::VariableDeclaration const& _varDecl);
wasm::Expression operator()(yul::If const& _if);
wasm::Expression operator()(yul::Switch const& _switch);
wasm::Expression operator()(yul::FunctionDefinition const&);
wasm::Expression operator()(yul::ForLoop const&);
wasm::Expression operator()(yul::Break const&);
wasm::Expression operator()(yul::Continue const&);
wasm::Expression operator()(yul::Leave const&);
wasm::Expression operator()(yul::Block const& _block);
private:
WasmCodeTransform(
Dialect const& _dialect,
Block const& _ast,
TypeInfo& _typeInfo
):
m_dialect(_dialect),
m_nameDispenser(_dialect, _ast),
m_typeInfo(_typeInfo)
{}
std::unique_ptr<wasm::Expression> visit(yul::Expression const& _expression);
wasm::Expression visitReturnByValue(yul::Expression const& _expression);
std::vector<wasm::Expression> visit(std::vector<yul::Expression> const& _expressions);
wasm::Expression visit(yul::Statement const& _statement);
std::vector<wasm::Expression> visit(std::vector<yul::Statement> const& _statements);
/// Returns an assignment or a block containing multiple assignments.
/// @param _variableNames the names of the variables to assign to
/// @param _firstValue the value to be assigned to the first variable. If there
/// is more than one variable, the values are taken from m_globalVariables.
wasm::Expression generateMultiAssignment(
std::vector<std::string> _variableNames,
std::unique_ptr<wasm::Expression> _firstValue
);
wasm::FunctionDefinition translateFunction(yul::FunctionDefinition const& _funDef);
/// Imports an external function into the current module.
/// @param _builtin _builtin the builtin that will be imported into the current module.
/// @param _module _module the module name under which the external function can be found.
/// @param _externalName the name of the external function within the module _module.
/// @param _internalName the name of the internal function under that the external function is accessible.
void importBuiltinFunction(BuiltinFunction const* _builtin, std::string const& _module, std::string const& _externalName, std::string const& _internalName);
std::string newLabel();
/// Selects a subset of global variables matching specified sequence of variable types.
/// Defines more global variables of a given type if there's not enough.
std::vector<size_t> allocateGlobals(std::vector<wasm::Type> const& _typesForGlobals);
static wasm::Type translatedType(yul::Type _yulType);
static wasm::Literal makeLiteral(wasm::Type _type, u256 _value);
Dialect const& m_dialect;
NameDispenser m_nameDispenser;
std::vector<wasm::VariableDeclaration> m_localVariables;
std::vector<wasm::GlobalVariableDeclaration> m_globalVariables;
std::map<YulString, wasm::FunctionImport> m_functionsToImport;
std::string m_functionBodyLabel;
std::stack<std::pair<std::string, std::string>> m_breakContinueLabelNames;
TypeInfo& m_typeInfo;
};
}

View File

@ -1,284 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Dialects for Wasm.
*/
#include <libyul/backends/wasm/WasmDialect.h>
#include <libyul/AST.h>
#include <libyul/Exceptions.h>
using namespace std;
using namespace solidity::yul;
WasmDialect::WasmDialect()
{
YulString i64 = "i64"_yulstring;
YulString i32 = "i32"_yulstring;
defaultType = i64;
boolType = i32;
types = {i64, i32};
for (auto t: types)
for (auto const& name: {
"add",
"sub",
"mul",
// TODO: div_s
"div_u",
// TODO: rem_s
"rem_u",
"and",
"or",
"xor",
"shl",
// TODO: shr_s
"shr_u",
// TODO: rotl
// TODO: rotr
})
addFunction(t.str() + "." + name, {t, t}, {t});
for (auto t: types)
for (auto const& name: {
"eq",
"ne",
// TODO: lt_s
"lt_u",
// TODO: gt_s
"gt_u",
// TODO: le_s
"le_u",
// TODO: ge_s
"ge_u"
})
addFunction(t.str() + "." + name, {t, t}, {i32});
addFunction("i32.eqz", {i32}, {i32});
addFunction("i64.eqz", {i64}, {i32});
for (auto t: types)
for (auto const& name: {
"clz",
"ctz",
"popcnt",
})
addFunction(t.str() + "." + name, {t}, {t});
addFunction("i32.wrap_i64", {i64}, {i32});
addFunction("i64.extend_i32_u", {i32}, {i64});
addFunction("i32.store", {i32, i32}, {}, false);
m_functions["i32.store"_yulstring].sideEffects.storage = SideEffects::None;
m_functions["i32.store"_yulstring].sideEffects.otherState = SideEffects::None;
addFunction("i64.store", {i32, i64}, {}, false);
// TODO: add i32.store16, i64.store8, i64.store16, i64.store32
m_functions["i64.store"_yulstring].sideEffects.storage = SideEffects::None;
m_functions["i64.store"_yulstring].sideEffects.otherState = SideEffects::None;
addFunction("i32.store8", {i32, i32}, {}, false);
m_functions["i32.store8"_yulstring].sideEffects.storage = SideEffects::None;
m_functions["i32.store8"_yulstring].sideEffects.otherState = SideEffects::None;
addFunction("i64.store8", {i32, i64}, {}, false);
m_functions["i64.store8"_yulstring].sideEffects.storage = SideEffects::None;
m_functions["i64.store8"_yulstring].sideEffects.otherState = SideEffects::None;
addFunction("i32.load", {i32}, {i32}, false);
m_functions["i32.load"_yulstring].sideEffects.canBeRemoved = true;
m_functions["i32.load"_yulstring].sideEffects.canBeRemovedIfNoMSize = true;
m_functions["i32.load"_yulstring].sideEffects.storage = SideEffects::None;
m_functions["i32.load"_yulstring].sideEffects.memory = SideEffects::Read;
m_functions["i32.load"_yulstring].sideEffects.otherState = SideEffects::None;
addFunction("i64.load", {i32}, {i64}, false);
// TODO: add i32.load8, i32.load16, i64.load8, i64.load16, i64.load32
m_functions["i64.load"_yulstring].sideEffects.canBeRemoved = true;
m_functions["i64.load"_yulstring].sideEffects.canBeRemovedIfNoMSize = true;
m_functions["i64.load"_yulstring].sideEffects.storage = SideEffects::None;
m_functions["i64.load"_yulstring].sideEffects.memory = SideEffects::Read;
m_functions["i64.load"_yulstring].sideEffects.otherState = SideEffects::None;
// Drop is actually overloaded for all types, but Yul does not support that.
// Because of that, we introduce "i32.drop" and "i64.drop".
addFunction("i32.drop", {i32}, {});
addFunction("i64.drop", {i64}, {});
// Select is also overloaded.
addFunction("i32.select", {i32, i32, i32}, {i32});
addFunction("i64.select", {i64, i64, i32}, {i64});
addFunction("nop", {}, {});
addFunction("unreachable", {}, {}, false);
m_functions["unreachable"_yulstring].sideEffects.storage = SideEffects::None;
m_functions["unreachable"_yulstring].sideEffects.memory = SideEffects::None;
m_functions["unreachable"_yulstring].sideEffects.otherState = SideEffects::None;
m_functions["unreachable"_yulstring].controlFlowSideEffects.canTerminate = false;
m_functions["unreachable"_yulstring].controlFlowSideEffects.canRevert = true;
m_functions["unreachable"_yulstring].controlFlowSideEffects.canContinue = false;
addFunction("datasize", {i64}, {i64}, true, {LiteralKind::String});
addFunction("dataoffset", {i64}, {i64}, true, {LiteralKind::String});
addExternals();
}
BuiltinFunction const* WasmDialect::builtin(YulString _name) const
{
auto it = m_functions.find(_name);
if (it != m_functions.end())
return &it->second;
else
return nullptr;
}
BuiltinFunction const* WasmDialect::discardFunction(YulString _type) const
{
if (_type == "i32"_yulstring)
return builtin("i32.drop"_yulstring);
yulAssert(_type == "i64"_yulstring, "");
return builtin("i64.drop"_yulstring);
}
BuiltinFunction const* WasmDialect::equalityFunction(YulString _type) const
{
if (_type == "i32"_yulstring)
return builtin("i32.eq"_yulstring);
yulAssert(_type == "i64"_yulstring, "");
return builtin("i64.eq"_yulstring);
}
WasmDialect const& WasmDialect::instance()
{
static std::unique_ptr<WasmDialect> dialect;
static YulStringRepository::ResetCallback callback{[&] { dialect.reset(); }};
if (!dialect)
dialect = make_unique<WasmDialect>();
return *dialect;
}
void WasmDialect::addExternals()
{
// These are not YulStrings because that would be too complicated with regards
// to the YulStringRepository reset.
static string const i64{"i64"};
static string const i32{"i32"};
static string const i32ptr{"i32"}; // Uses "i32" on purpose.
struct External
{
string module;
string name;
vector<string> parameters;
vector<string> returns;
ControlFlowSideEffects controlFlowSideEffects = ControlFlowSideEffects{};
};
static vector<External> externals{
{"eth", "getAddress", {i32ptr}, {}},
{"eth", "getExternalBalance", {i32ptr, i32ptr}, {}},
{"eth", "getBlockBaseFee", {i32ptr}, {}},
{"eth", "getBlockHash", {i64, i32ptr}, {i32}},
{"eth", "call", {i64, i32ptr, i32ptr, i32ptr, i32}, {i32}},
{"eth", "callDataCopy", {i32ptr, i32, i32}, {}},
{"eth", "getCallDataSize", {}, {i32}},
{"eth", "callCode", {i64, i32ptr, i32ptr, i32ptr, i32}, {i32}},
{"eth", "callDelegate", {i64, i32ptr, i32ptr, i32}, {i32}},
{"eth", "callStatic", {i64, i32ptr, i32ptr, i32}, {i32}},
{"eth", "storageStore", {i32ptr, i32ptr}, {}},
{"eth", "storageLoad", {i32ptr, i32ptr}, {}},
{"eth", "getCaller", {i32ptr}, {}},
{"eth", "getCallValue", {i32ptr}, {}},
{"eth", "codeCopy", {i32ptr, i32, i32}, {}},
{"eth", "getCodeSize", {}, {i32}},
{"eth", "getBlockCoinbase", {i32ptr}, {}},
{"eth", "create", {i32ptr, i32ptr, i32, i32ptr}, {i32}},
{"eth", "getBlockDifficulty", {i32ptr}, {}},
{"eth", "externalCodeCopy", {i32ptr, i32ptr, i32, i32}, {}},
{"eth", "getExternalCodeSize", {i32ptr}, {i32}},
{"eth", "getGasLeft", {}, {i64}},
{"eth", "getBlockGasLimit", {}, {i64}},
{"eth", "getTxGasPrice", {i32ptr}, {}},
{"eth", "log", {i32ptr, i32, i32, i32ptr, i32ptr, i32ptr, i32ptr}, {}},
{"eth", "getBlockNumber", {}, {i64}},
{"eth", "getTxOrigin", {i32ptr}, {}},
{"eth", "finish", {i32ptr, i32}, {}, ControlFlowSideEffects{true, false, false}},
{"eth", "revert", {i32ptr, i32}, {}, ControlFlowSideEffects{false, true, false}},
{"eth", "getReturnDataSize", {}, {i32}},
{"eth", "returnDataCopy", {i32ptr, i32, i32}, {}},
{"eth", "selfDestruct", {i32ptr}, {}, ControlFlowSideEffects{false, true, false}},
{"eth", "getBlockTimestamp", {}, {i64}},
{"debug", "print32", {i32}, {}},
{"debug", "print64", {i64}, {}},
{"debug", "printMem", {i32, i32}, {}},
{"debug", "printMemHex", {i32, i32}, {}},
{"debug", "printStorage", {i32}, {}},
{"debug", "printStorageHex", {i32}, {}},
};
for (External const& ext: externals)
{
YulString name{ext.module + "." + ext.name};
BuiltinFunction& f = m_functions[name];
f.name = name;
for (string const& p: ext.parameters)
f.parameters.emplace_back(YulString(p));
for (string const& p: ext.returns)
f.returns.emplace_back(YulString(p));
// TODO some of them are side effect free.
f.sideEffects = SideEffects::worst();
f.sideEffects.cannotLoop = true;
f.sideEffects.movableApartFromEffects = !ext.controlFlowSideEffects.terminatesOrReverts();
f.controlFlowSideEffects = ext.controlFlowSideEffects;
f.isMSize = false;
f.literalArguments.clear();
static set<string> const writesToStorage{
"storageStore",
"call",
"callcode",
"callDelegate",
"create"
};
static set<string> const readsStorage{"storageLoad", "callStatic"};
if (readsStorage.count(ext.name))
f.sideEffects.storage = SideEffects::Read;
else if (!writesToStorage.count(ext.name))
f.sideEffects.storage = SideEffects::None;
}
}
void WasmDialect::addFunction(
string _name,
vector<YulString> _params,
vector<YulString> _returns,
bool _movable,
vector<optional<LiteralKind>> _literalArguments
)
{
YulString name{std::move(_name)};
BuiltinFunction& f = m_functions[name];
f.name = name;
f.parameters = std::move(_params);
yulAssert(_returns.size() <= 1, "The Wasm 1.0 specification only allows up to 1 return value.");
f.returns = std::move(_returns);
f.sideEffects = _movable ? SideEffects{} : SideEffects::worst();
f.sideEffects.cannotLoop = true;
// TODO This should be improved when LoopInvariantCodeMotion gets specialized for WASM
f.sideEffects.movableApartFromEffects = _movable;
f.isMSize = false;
f.literalArguments = std::move(_literalArguments);
}

View File

@ -1,71 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Dialects for Wasm.
*/
#pragma once
#include <libyul/Dialect.h>
#include <map>
namespace solidity::yul
{
class YulString;
using Type = YulString;
struct FunctionCall;
struct Object;
/**
* Yul dialect for Wasm as a backend.
*
* Builtin functions are a subset of the wasm instructions.
*
* There is a builtin function `i32.drop` that takes an i32, while `i64.drop` takes i64.
*
*/
struct WasmDialect: public Dialect
{
WasmDialect();
BuiltinFunction const* builtin(YulString _name) const override;
BuiltinFunction const* discardFunction(YulString _type) const override;
BuiltinFunction const* equalityFunction(YulString _type) const override;
BuiltinFunction const* booleanNegationFunction() const override { return builtin("i32.eqz"_yulstring); }
std::set<YulString> fixedFunctionNames() const override { return {"main"_yulstring}; }
static WasmDialect const& instance();
private:
void addExternals();
void addFunction(
std::string _name,
std::vector<YulString> _params,
std::vector<YulString> _returns,
bool _movable = true,
std::vector<std::optional<LiteralKind>> _literalArguments = std::vector<std::optional<LiteralKind>>{}
);
std::map<YulString, BuiltinFunction> m_functions;
};
}

View File

@ -1,60 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Compiler that transforms Yul Objects to Wasm text and binary representation (Ewasm flavoured).
*/
#include <libyul/backends/wasm/WasmObjectCompiler.h>
#include <libyul/backends/wasm/WasmCodeTransform.h>
#include <libyul/backends/wasm/BinaryTransform.h>
#include <libyul/backends/wasm/TextTransform.h>
#include <libyul/Object.h>
#include <libyul/Exceptions.h>
#include <libsolutil/CommonData.h>
using namespace solidity;
using namespace solidity::yul;
using namespace std;
pair<string, bytes> WasmObjectCompiler::compile(Object& _object, Dialect const& _dialect)
{
WasmObjectCompiler compiler(_dialect);
wasm::Module module = compiler.run(_object);
return {wasm::TextTransform().run(module), wasm::BinaryTransform::run(module)};
}
wasm::Module WasmObjectCompiler::run(Object& _object)
{
yulAssert(_object.analysisInfo, "No analysis info.");
yulAssert(_object.code, "No code.");
wasm::Module module = WasmCodeTransform::run(m_dialect, *_object.code);
for (auto& subNode: _object.subObjects)
if (Object* subObject = dynamic_cast<Object*>(subNode.get()))
module.subModules[subObject->name.str()] = run(*subObject);
else if (Data* subObject = dynamic_cast<Data*>(subNode.get()))
module.customSections[subObject->name.str()] = subObject->data;
else
yulAssert(false, "");
return module;
}

View File

@ -1,53 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Compiler that transforms Yul Objects to Wasm text and binary representation (Ewasm flavoured).
*/
#pragma once
#include <string>
#include <vector>
#include <tuple>
#include <libsolutil/Common.h> // solidity::bytes
namespace solidity::yul
{
struct Object;
struct Dialect;
namespace wasm
{
struct Module;
}
class WasmObjectCompiler
{
public:
/// Compiles the given object and returns the Wasm text and binary representation.
static std::pair<std::string, bytes> compile(Object& _object, Dialect const& _dialect);
private:
WasmObjectCompiler(Dialect const& _dialect):
m_dialect(_dialect)
{}
wasm::Module run(Object& _object);
Dialect const& m_dialect;
};
}

View File

@ -1,433 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <libyul/AST.h>
#include <libyul/backends/wasm/WordSizeTransform.h>
#include <libyul/Utilities.h>
#include <libyul/Dialect.h>
#include <libyul/optimiser/NameDisplacer.h>
#include <libsolutil/CommonData.h>
#include <array>
#include <map>
#include <variant>
#include <limits>
using namespace std;
using namespace solidity;
using namespace solidity::yul;
using namespace solidity::util;
void WordSizeTransform::operator()(FunctionDefinition& _fd)
{
rewriteVarDeclList(_fd.parameters);
rewriteVarDeclList(_fd.returnVariables);
(*this)(_fd.body);
}
void WordSizeTransform::operator()(FunctionCall& _fc)
{
vector<optional<LiteralKind>> const* literalArguments = nullptr;
if (BuiltinFunction const* fun = m_inputDialect.builtin(_fc.functionName.name))
if (!fun->literalArguments.empty())
literalArguments = &fun->literalArguments;
vector<Expression> newArgs;
for (size_t i = 0; i < _fc.arguments.size(); i++)
if (!literalArguments || !(*literalArguments)[i].has_value())
newArgs += expandValueToVector(_fc.arguments[i]);
else
{
get<Literal>(_fc.arguments[i]).type = m_targetDialect.defaultType;
newArgs.emplace_back(std::move(_fc.arguments[i]));
}
_fc.arguments = std::move(newArgs);
}
void WordSizeTransform::operator()(If& _if)
{
_if.condition = make_unique<Expression>(FunctionCall{
debugDataOf(*_if.condition),
Identifier{debugDataOf(*_if.condition), "or_bool"_yulstring},
expandValueToVector(*_if.condition)
});
(*this)(_if.body);
}
void WordSizeTransform::operator()(Switch&)
{
yulAssert(false, "Switch statement has to be handled inside the containing block.");
}
void WordSizeTransform::operator()(ForLoop& _for)
{
(*this)(_for.pre);
_for.condition = make_unique<Expression>(FunctionCall{
debugDataOf(*_for.condition),
Identifier{debugDataOf(*_for.condition), "or_bool"_yulstring},
expandValueToVector(*_for.condition)
});
(*this)(_for.post);
(*this)(_for.body);
}
void WordSizeTransform::operator()(Block& _block)
{
iterateReplacing(
_block.statements,
[&](Statement& _s) -> std::optional<vector<Statement>>
{
if (holds_alternative<VariableDeclaration>(_s))
{
VariableDeclaration& varDecl = std::get<VariableDeclaration>(_s);
if (!varDecl.value)
rewriteVarDeclList(varDecl.variables);
else if (holds_alternative<FunctionCall>(*varDecl.value))
{
visit(*varDecl.value);
// Special handling for datasize and dataoffset - they will only need one variable.
if (BuiltinFunction const* f = m_inputDialect.builtin(std::get<FunctionCall>(*varDecl.value).functionName.name))
if (f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring)
{
yulAssert(f->literalArguments.size() == 1, "");
yulAssert(f->literalArguments.at(0) == LiteralKind::String, "");
yulAssert(varDecl.variables.size() == 1, "");
auto newLhs = generateU64IdentifierNames(varDecl.variables[0].name);
vector<Statement> ret;
for (size_t i = 0; i < 3; i++)
ret.emplace_back(VariableDeclaration{
varDecl.debugData,
{TypedName{varDecl.debugData, newLhs[i], m_targetDialect.defaultType}},
make_unique<Expression>(Literal{
debugDataOf(*varDecl.value),
LiteralKind::Number,
"0"_yulstring,
m_targetDialect.defaultType
})
});
ret.emplace_back(VariableDeclaration{
varDecl.debugData,
{TypedName{varDecl.debugData, newLhs[3], m_targetDialect.defaultType}},
std::move(varDecl.value)
});
return {std::move(ret)};
}
rewriteVarDeclList(varDecl.variables);
return std::nullopt;
}
else if (
holds_alternative<Identifier>(*varDecl.value) ||
holds_alternative<Literal>(*varDecl.value)
)
{
yulAssert(varDecl.variables.size() == 1, "");
auto newRhs = expandValue(*varDecl.value);
auto newLhs = generateU64IdentifierNames(varDecl.variables[0].name);
vector<Statement> ret;
for (size_t i = 0; i < 4; i++)
ret.emplace_back(VariableDeclaration{
varDecl.debugData,
{TypedName{varDecl.debugData, newLhs[i], m_targetDialect.defaultType}},
std::move(newRhs[i])
}
);
return {std::move(ret)};
}
else
yulAssert(false, "");
}
else if (holds_alternative<Assignment>(_s))
{
Assignment& assignment = std::get<Assignment>(_s);
yulAssert(assignment.value, "");
if (holds_alternative<FunctionCall>(*assignment.value))
{
visit(*assignment.value);
// Special handling for datasize and dataoffset - they will only need one variable.
if (BuiltinFunction const* f = m_inputDialect.builtin(std::get<FunctionCall>(*assignment.value).functionName.name))
if (f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring)
{
yulAssert(f->literalArguments.size() == 1, "");
yulAssert(f->literalArguments[0] == LiteralKind::String, "");
yulAssert(assignment.variableNames.size() == 1, "");
auto newLhs = generateU64IdentifierNames(assignment.variableNames[0].name);
vector<Statement> ret;
for (size_t i = 0; i < 3; i++)
ret.emplace_back(Assignment{
assignment.debugData,
{Identifier{assignment.debugData, newLhs[i]}},
make_unique<Expression>(Literal{
debugDataOf(*assignment.value),
LiteralKind::Number,
"0"_yulstring,
m_targetDialect.defaultType
})
});
ret.emplace_back(Assignment{
assignment.debugData,
{Identifier{assignment.debugData, newLhs[3]}},
std::move(assignment.value)
});
return {std::move(ret)};
}
rewriteIdentifierList(assignment.variableNames);
return std::nullopt;
}
else if (
holds_alternative<Identifier>(*assignment.value) ||
holds_alternative<Literal>(*assignment.value)
)
{
yulAssert(assignment.variableNames.size() == 1, "");
auto newRhs = expandValue(*assignment.value);
YulString lhsName = assignment.variableNames[0].name;
vector<Statement> ret;
for (size_t i = 0; i < 4; i++)
ret.emplace_back(Assignment{
assignment.debugData,
{Identifier{assignment.debugData, m_variableMapping.at(lhsName)[i]}},
std::move(newRhs[i])
}
);
return {std::move(ret)};
}
else
yulAssert(false, "");
}
else if (holds_alternative<Switch>(_s))
return handleSwitch(std::get<Switch>(_s));
else
visit(_s);
return std::nullopt;
}
);
}
void WordSizeTransform::run(
Dialect const& _inputDialect,
Dialect const& _targetDialect,
Block& _ast,
NameDispenser& _nameDispenser
)
{
// Free the name `or_bool`.
NameDisplacer{_nameDispenser, {"or_bool"_yulstring}}(_ast);
WordSizeTransform{_inputDialect, _targetDialect, _nameDispenser}(_ast);
}
WordSizeTransform::WordSizeTransform(
Dialect const& _inputDialect,
Dialect const& _targetDialect,
NameDispenser& _nameDispenser
):
m_inputDialect(_inputDialect),
m_targetDialect(_targetDialect),
m_nameDispenser(_nameDispenser)
{
}
void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList)
{
iterateReplacing(
_nameList,
[&](TypedName const& _n) -> std::optional<TypedNameList>
{
TypedNameList ret;
for (auto newName: generateU64IdentifierNames(_n.name))
ret.emplace_back(TypedName{_n.debugData, newName, m_targetDialect.defaultType});
return ret;
}
);
}
void WordSizeTransform::rewriteIdentifierList(vector<Identifier>& _ids)
{
iterateReplacing(
_ids,
[&](Identifier const& _id) -> std::optional<vector<Identifier>>
{
vector<Identifier> ret;
for (auto newId: m_variableMapping.at(_id.name))
ret.push_back(Identifier{_id.debugData, newId});
return ret;
}
);
}
vector<Statement> WordSizeTransform::handleSwitchInternal(
shared_ptr<DebugData const> const& _debugData,
vector<YulString> const& _splitExpressions,
vector<Case> _cases,
YulString _runDefaultFlag,
size_t _depth
)
{
if (_depth == 4)
{
yulAssert(_cases.size() == 1, "");
return std::move(_cases.front().body.statements);
}
// Extract current 64 bit segment and group by it.
map<u256, vector<Case>> cases;
for (Case& c: _cases)
{
yulAssert(c.value, "Default case still present.");
cases[
(valueOfLiteral(*c.value) >> (256 - 64 * (_depth + 1))) &
std::numeric_limits<uint64_t>::max()
].emplace_back(std::move(c));
}
Switch ret{
_debugData,
make_unique<Expression>(Identifier{_debugData, _splitExpressions.at(_depth)}),
{}
};
for (auto& c: cases)
{
Literal label{_debugData, LiteralKind::Number, YulString(c.first.str()), m_targetDialect.defaultType};
ret.cases.emplace_back(Case{
c.second.front().debugData,
make_unique<Literal>(std::move(label)),
Block{_debugData, handleSwitchInternal(
_debugData,
_splitExpressions,
std::move(c.second),
_runDefaultFlag,
_depth + 1
)}
});
}
if (!_runDefaultFlag.empty())
ret.cases.emplace_back(Case{
_debugData,
nullptr,
Block{_debugData, make_vector<Statement>(
Assignment{
_debugData,
{{_debugData, _runDefaultFlag}},
make_unique<Expression>(Literal{_debugData, LiteralKind::Boolean, "true"_yulstring, m_targetDialect.boolType})
}
)}
});
return make_vector<Statement>(std::move(ret));
}
std::vector<Statement> WordSizeTransform::handleSwitch(Switch& _switch)
{
for (auto& c: _switch.cases)
(*this)(c.body);
// Turns the switch into a quadruply-nested switch plus
// a flag that tells to execute the default case after all the switches.
vector<Statement> ret;
YulString runDefaultFlag;
Case defaultCase;
if (!_switch.cases.back().value)
{
runDefaultFlag = m_nameDispenser.newName("run_default"_yulstring);
defaultCase = std::move(_switch.cases.back());
_switch.cases.pop_back();
ret.emplace_back(VariableDeclaration{
_switch.debugData,
{TypedName{_switch.debugData, runDefaultFlag, m_targetDialect.boolType}},
{}
});
}
vector<YulString> splitExpressions;
for (auto const& expr: expandValue(*_switch.expression))
splitExpressions.emplace_back(std::get<Identifier>(*expr).name);
ret += handleSwitchInternal(
_switch.debugData,
splitExpressions,
std::move(_switch.cases),
runDefaultFlag,
0
);
if (!runDefaultFlag.empty())
ret.emplace_back(If{
_switch.debugData,
make_unique<Expression>(Identifier{_switch.debugData, runDefaultFlag}),
std::move(defaultCase.body)
});
return ret;
}
array<YulString, 4> WordSizeTransform::generateU64IdentifierNames(YulString const& _s)
{
yulAssert(m_variableMapping.find(_s) == m_variableMapping.end(), "");
for (size_t i = 0; i < 4; i++)
m_variableMapping[_s][i] = m_nameDispenser.newName(YulString{_s.str() + "_" + to_string(i)});
return m_variableMapping[_s];
}
array<unique_ptr<Expression>, 4> WordSizeTransform::expandValue(Expression const& _e)
{
array<unique_ptr<Expression>, 4> ret;
if (holds_alternative<Identifier>(_e))
{
auto const& id = std::get<Identifier>(_e);
for (size_t i = 0; i < 4; i++)
ret[i] = make_unique<Expression>(Identifier{id.debugData, m_variableMapping.at(id.name)[i]});
}
else if (holds_alternative<Literal>(_e))
{
auto const& lit = std::get<Literal>(_e);
u256 val = valueOfLiteral(lit);
for (size_t exprIndex = 0; exprIndex < 4; ++exprIndex)
{
size_t exprIndexReverse = 3 - exprIndex;
u256 currentVal = val & std::numeric_limits<uint64_t>::max();
val >>= 64;
ret[exprIndexReverse] = make_unique<Expression>(
Literal{
lit.debugData,
LiteralKind::Number,
YulString(currentVal.str()),
m_targetDialect.defaultType
}
);
}
}
else
yulAssert(false, "Invalid expression to split.");
return ret;
}
vector<Expression> WordSizeTransform::expandValueToVector(Expression const& _e)
{
vector<Expression> ret;
for (unique_ptr<Expression>& val: expandValue(_e))
ret.emplace_back(std::move(*val));
return ret;
}

View File

@ -1,108 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Replace every u256 variable with four u64 variables.
*/
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/NameDispenser.h>
#include <liblangutil/SourceLocation.h>
#include <array>
#include <vector>
namespace solidity::yul
{
/**
* A stage that replace every u256 variable with four u64 variables.
* This transformation stage is required because values in EVM are 256 bits,
* but wasm only supports values up to 64 bits, so we use four u64 values to simulate
* one u256 value.
*
* For FunctionCall that accepts or returns u256 values, they accepts or returns
* four times the number of values after this transformation, with the order of significance,
* from the most significant to the least significant.
*
* For example, the FunctionCall MUL supplied by code generator
* should take 8 arguments and return 4 values (instead of 2 and 1) after this transformation.
*
* mul(a1, a2, a3, a4, b1, b2, b3, b4) -> c1, c2, c3, c4
*
* the value of c4 should be
* ((a1*(2^192) + a2*(2^128) + a3(2^64) + a4) * (b1*(2^192) + b2*(2^128) + b3(2^64) + b4)) & ((1<<64)-1)
*
* The resulting code still uses the EVM builtin functions but assumes that they
* take four times the parameters and each of type u64.
* In addition, it uses a single other builtin function called `or_bool` that
* takes four u64 parameters and is supposed to return the logical disjunction
* of them as a i32 value. If this name is already used somewhere, it is renamed.
*
* Prerequisite: Disambiguator, ForLoopConditionIntoBody, ExpressionSplitter
*/
class WordSizeTransform: public ASTModifier
{
public:
void operator()(FunctionDefinition&) override;
void operator()(FunctionCall&) override;
void operator()(If&) override;
void operator()(Switch&) override;
void operator()(ForLoop&) override;
void operator()(Block& _block) override;
static void run(
Dialect const& _inputDialect,
Dialect const& _targetDialect,
Block& _ast,
NameDispenser& _nameDispenser
);
private:
explicit WordSizeTransform(
Dialect const& _inputDialect,
Dialect const& _targetDialect,
NameDispenser& _nameDispenser
);
void rewriteVarDeclList(std::vector<TypedName>&);
void rewriteIdentifierList(std::vector<Identifier>&);
std::vector<Statement> handleSwitch(Switch& _switch);
std::vector<Statement> handleSwitchInternal(
std::shared_ptr<DebugData const> const& _debugData,
std::vector<YulString> const& _splitExpressions,
std::vector<Case> _cases,
YulString _runDefaultFlag,
size_t _depth
);
std::array<YulString, 4> generateU64IdentifierNames(YulString const& _s);
std::array<std::unique_ptr<Expression>, 4> expandValue(Expression const& _e);
std::vector<Expression> expandValueToVector(Expression const& _e);
Dialect const& m_inputDialect;
Dialect const& m_targetDialect;
NameDispenser& m_nameDispenser;
/// maps original u256 variable's name to corresponding u64 variables' names
std::map<YulString, std::array<YulString, 4>> m_variableMapping;
};
}

View File

@ -1,396 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// 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)
}
}

View File

@ -1,222 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// 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)
}
}

View File

@ -1,168 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// 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 {
r4 := i64.extend_i32_u(
i32.and(
i64.eq(x1, y1),
i32.and(
i64.eq(x2, y2),
i32.and(
i64.eq(x3, y3),
i64.eq(x4, y4)
)
)
)
)
}
// returns 0 if a == b, -1 if a < b and 1 if a > b
function cmp(a, b) -> r:i32 {
r := i32.select(0xffffffff:i32, i64.ne(a, b), i64.lt_u(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)
}

View File

@ -1,79 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// 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:i32) -> y:i32 {
let hi:i32 := i32.and(i32.shl(x, 8:i32), 0xff00:i32)
let lo:i32 := i32.and(i32.shr_u(x, 8:i32), 0xff:i32)
y := i32.or(hi, lo)
}
function bswap32(x:i32) -> y:i32 {
let hi:i32 := i32.shl(bswap16(x), 16:i32)
let lo:i32 := bswap16(i32.shr_u(x, 16:i32))
y := i32.or(hi, lo)
}
function bswap64(x) -> y {
let hi := i64.shl(i64.extend_i32_u(bswap32(i32.wrap_i64(x))), 32)
let lo := i64.extend_i32_u(bswap32(i32.wrap_i64(i64.shr_u(x, 32))))
y := i64.or(hi, lo)
}

View File

@ -1,413 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
// NOTE: This file is used to generate `ewasmPolyfills/Interface.h`.
function address() -> z1, z2, z3, z4 {
eth.getAddress(12:i32)
z1, z2, z3, z4 := mload_address(0:i32)
}
function balance(x1, x2, x3, x4) -> z1, z2, z3, z4 {
mstore_address(0:i32, x1, x2, x3, x4)
eth.getExternalBalance(12:i32, 32:i32)
z3 := i64.load(40:i32)
z4 := i64.load(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(12:i32)
z1, z2, z3, z4 := mload_address(0:i32)
}
function caller() -> z1, z2, z3, z4 {
eth.getCaller(12:i32)
z1, z2, z3, z4 := mload_address(0:i32)
}
function callvalue() -> z1, z2, z3, z4 {
eth.getCallValue(0:i32)
z3 := i64.load(8:i32)
z4 := i64.load(0:i32)
}
function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 {
calldatacopy(0, 0, 0, 0, x1, x2, x3, x4, 0, 0, 0, 32)
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) {
let cds:i32 := eth.getCallDataSize()
let destination:i32 := u256_to_i32(x1, x2, x3, x4)
let offset:i32 := u256_to_i32(y1, y2, y3, y4)
let requested_size:i32 := u256_to_i32(z1, z2, z3, z4)
// overflow?
if i32.gt_u(offset, i32.sub(0xffffffff:i32, requested_size)) {
eth.revert(0:i32, 0:i32)
}
let available_size:i32 := i32.sub(cds, offset)
if i32.gt_u(offset, cds) {
available_size := 0:i32
}
if i32.gt_u(available_size, 0:i32) {
eth.callDataCopy(
destination,
offset,
available_size
)
}
if i32.gt_u(requested_size, available_size) {
memset(i32.add(destination, available_size), 0:i32, i32.sub(requested_size, available_size))
}
}
// 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)
z3 := i64.load(8:i32)
z4 := i64.load(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(12:i32)
z1, z2, z3, z4 := mload_address(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 := i64.load(24:i32)
z2 := i64.load(16:i32)
z3 := i64.load(8:i32)
z4 := i64.load(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 mstore(x1, x2, x3, x4, y1, y2, y3, y4) {
mstore_internal(to_internal_i32ptr(x1, x2, x3, x4), y1, y2, y3, y4)
}
// 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)
}

View File

@ -1,24 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// 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()
}

View File

@ -1,31 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// 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))
}

View File

@ -1,109 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// 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
}
// Fill `length` bytes starting from `ptr` with `value`.
function memset(ptr:i32, value:i32, length:i32) {
for { let i:i32 := 0:i32 } i32.lt_u(i, length) { i := i32.add(i, 1:i32) }
{
i32.store8(i32.add(ptr, i), value)
}
}
// Writes 256-bits from `pos`, but only set the bottom 160-bits.
function mstore_address(pos:i32, a1, a2, a3, a4) {
a1, a2, a3 := u256_to_address(a1, a2, a3, a4)
i64.store(pos, 0:i64)
i32.store(i32.add(pos, 8:i32), 0:i32)
i32.store(i32.add(pos, 12:i32), bswap32(i32.wrap_i64(a1)))
i64.store(i32.add(pos, 16:i32), bswap64(a2))
i64.store(i32.add(pos, 24:i32), bswap64(a3))
}
// Reads 256-bits from `pos`, but only returns the bottom 160-bits.
function mload_address(pos:i32) -> z1, z2, z3, z4 {
z2 := i64.extend_i32_u(bswap32(i32.load(i32.add(pos, 12:i32))))
z3 := bswap64(i64.load(i32.add(pos, 16:i32)))
z4 := bswap64(i64.load(i32.add(pos, 24:i32)))
}
// Writes 256-bits from `pos`.
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))
}
// Reads 256-bits from `pos`.
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)))
}
// Stores one byte at position `x` of value `y`.
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)
}

View File

@ -70,7 +70,6 @@
#include <libyul/AST.h> #include <libyul/AST.h>
#include <libyul/Object.h> #include <libyul/Object.h>
#include <libyul/backends/wasm/WasmDialect.h>
#include <libyul/backends/evm/NoOutputAssembly.h> #include <libyul/backends/evm/NoOutputAssembly.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
@ -212,13 +211,6 @@ void OptimiserSuite::run(
else if (evmDialect->providesObjectAccess() && _optimizeStackAllocation) else if (evmDialect->providesObjectAccess() && _optimizeStackAllocation)
StackLimitEvader::run(suite.m_context, _object); StackLimitEvader::run(suite.m_context, _object);
} }
else if (dynamic_cast<WasmDialect const*>(&_dialect))
{
// If the first statement is an empty block, remove it.
// We should only have function definitions after that.
if (ast.statements.size() > 1 && std::get<Block>(ast.statements.front()).statements.empty())
ast.statements.erase(ast.statements.begin());
}
dispenser.reset(ast); dispenser.reset(ast);
NameSimplifier::run(suite.m_context, ast); NameSimplifier::run(suite.m_context, ast);

View File

@ -239,29 +239,6 @@ void CommandLineInterface::handleIROptimized(string const& _contractName)
} }
} }
void CommandLineInterface::handleEwasm(string const& _contractName)
{
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
if (!m_options.compiler.outputs.ewasm)
return;
if (!m_options.output.dir.empty())
{
createFile(m_compiler->filesystemFriendlyName(_contractName) + ".wast", m_compiler->ewasm(_contractName));
createFile(
m_compiler->filesystemFriendlyName(_contractName) + ".wasm",
asString(m_compiler->ewasmObject(_contractName).bytecode)
);
}
else
{
sout() << "Ewasm text:" << endl;
sout() << m_compiler->ewasm(_contractName) << endl;
sout() << "Ewasm binary (hex): " << m_compiler->ewasmObject(_contractName).toHex() << endl;
}
}
void CommandLineInterface::handleBytecode(string const& _contract) void CommandLineInterface::handleBytecode(string const& _contract)
{ {
solAssert(CompilerInputModes.count(m_options.input.mode) == 1); solAssert(CompilerInputModes.count(m_options.input.mode) == 1);
@ -715,7 +692,6 @@ void CommandLineInterface::compile()
// TODO: Perhaps we should not compile unless requested // TODO: Perhaps we should not compile unless requested
m_compiler->enableIRGeneration(m_options.compiler.outputs.ir || m_options.compiler.outputs.irOptimized); m_compiler->enableIRGeneration(m_options.compiler.outputs.ir || m_options.compiler.outputs.irOptimized);
m_compiler->enableEwasmGeneration(m_options.compiler.outputs.ewasm);
m_compiler->enableEvmBytecodeGeneration( m_compiler->enableEvmBytecodeGeneration(
m_options.compiler.estimateGas || m_options.compiler.estimateGas ||
m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asm_ ||
@ -1082,9 +1058,8 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y
for (auto const& src: m_fileReader.sourceUnits()) for (auto const& src: m_fileReader.sourceUnits())
{ {
string machine = solAssert(_targetMachine == yul::YulStack::Machine::EVM);
_targetMachine == yul::YulStack::Machine::EVM ? "EVM" : string machine = "EVM";
"Ewasm";
sout() << endl << "======= " << src.first << " (" << machine << ") =======" << endl; sout() << endl << "======= " << src.first << " (" << machine << ") =======" << endl;
yul::YulStack& stack = yulStacks[src.first]; yul::YulStack& stack = yulStacks[src.first];
@ -1097,19 +1072,6 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y
sout() << stack.print() << endl; sout() << stack.print() << endl;
} }
if (_language != yul::YulStack::Language::Ewasm && _targetMachine == yul::YulStack::Machine::Ewasm)
{
stack.translate(yul::YulStack::Language::Ewasm);
stack.optimize();
if (m_options.compiler.outputs.ewasmIR)
{
sout() << endl << "==========================" << endl;
sout() << endl << "Translated source:" << endl;
sout() << stack.print() << endl;
}
}
yul::MachineAssemblyObject object; yul::MachineAssemblyObject object;
object = stack.assemble(_targetMachine); object = stack.assemble(_targetMachine);
object.bytecode->link(m_options.linker.libraries); object.bytecode->link(m_options.linker.libraries);
@ -1123,11 +1085,8 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y
serr() << "No binary representation found." << endl; serr() << "No binary representation found." << endl;
} }
solAssert(_targetMachine == yul::YulStack::Machine::Ewasm || _targetMachine == yul::YulStack::Machine::EVM, ""); solAssert(_targetMachine == yul::YulStack::Machine::EVM, "");
if ( if (m_options.compiler.outputs.asm_)
(_targetMachine == yul::YulStack::Machine::EVM && m_options.compiler.outputs.asm_) ||
(_targetMachine == yul::YulStack::Machine::Ewasm && m_options.compiler.outputs.ewasm)
)
{ {
sout() << endl << "Text representation:" << endl; sout() << endl << "Text representation:" << endl;
if (!object.assembly.empty()) if (!object.assembly.empty())
@ -1183,7 +1142,6 @@ void CommandLineInterface::outputCompilationResults()
handleBytecode(contract); handleBytecode(contract);
handleIR(contract); handleIR(contract);
handleIROptimized(contract); handleIROptimized(contract);
handleEwasm(contract);
handleSignatureHashes(contract); handleSignatureHashes(contract);
handleMetadata(contract); handleMetadata(contract);
handleABI(contract); handleABI(contract);

View File

@ -102,7 +102,6 @@ private:
void handleOpcode(std::string const& _contract); void handleOpcode(std::string const& _contract);
void handleIR(std::string const& _contract); void handleIR(std::string const& _contract);
void handleIROptimized(std::string const& _contract); void handleIROptimized(std::string const& _contract);
void handleEwasm(std::string const& _contract);
void handleBytecode(std::string const& _contract); void handleBytecode(std::string const& _contract);
void handleSignatureHashes(std::string const& _contract); void handleSignatureHashes(std::string const& _contract);
void handleMetadata(std::string const& _contract); void handleMetadata(std::string const& _contract);

View File

@ -46,7 +46,6 @@ static string const g_strCombinedJson = "combined-json";
static string const g_strErrorRecovery = "error-recovery"; static string const g_strErrorRecovery = "error-recovery";
static string const g_strEVM = "evm"; static string const g_strEVM = "evm";
static string const g_strEVMVersion = "evm-version"; static string const g_strEVMVersion = "evm-version";
static string const g_strEwasm = "ewasm";
static string const g_strEOFVersion = "experimental-eof-version"; static string const g_strEOFVersion = "experimental-eof-version";
static string const g_strViaIR = "via-ir"; static string const g_strViaIR = "via-ir";
static string const g_strExperimentalViaIR = "experimental-via-ir"; static string const g_strExperimentalViaIR = "experimental-via-ir";
@ -114,15 +113,13 @@ static string const g_strErrorIds = "error-codes";
/// Possible arguments to for --machine /// Possible arguments to for --machine
static set<string> const g_machineArgs static set<string> const g_machineArgs
{ {
g_strEVM, g_strEVM
g_strEwasm
}; };
/// Possible arguments to for --yul-dialect /// Possible arguments to for --yul-dialect
static set<string> const g_yulDialectArgs static set<string> const g_yulDialectArgs
{ {
g_strEVM, g_strEVM
g_strEwasm
}; };
/// Possible arguments to for --metadata-hash /// Possible arguments to for --metadata-hash
@ -460,13 +457,11 @@ void CommandLineParser::parseOutputSelection()
CompilerOutputs::componentMap() | CompilerOutputs::componentMap() |
ranges::views::keys | ranges::views::keys |
ranges::to<set>() ranges::to<set>()
) - set<string>{CompilerOutputs::componentName(&CompilerOutputs::ewasmIR)}; );
static set<string> const assemblerModeOutputs = { static set<string> const assemblerModeOutputs = {
CompilerOutputs::componentName(&CompilerOutputs::asm_), CompilerOutputs::componentName(&CompilerOutputs::asm_),
CompilerOutputs::componentName(&CompilerOutputs::binary), CompilerOutputs::componentName(&CompilerOutputs::binary),
CompilerOutputs::componentName(&CompilerOutputs::irOptimized), CompilerOutputs::componentName(&CompilerOutputs::irOptimized)
CompilerOutputs::componentName(&CompilerOutputs::ewasm),
CompilerOutputs::componentName(&CompilerOutputs::ewasmIR),
}; };
switch (_mode) switch (_mode)
@ -499,8 +494,6 @@ void CommandLineParser::parseOutputSelection()
m_options.compiler.outputs.asm_ = true; m_options.compiler.outputs.asm_ = true;
m_options.compiler.outputs.binary = true; m_options.compiler.outputs.binary = true;
m_options.compiler.outputs.irOptimized = true; m_options.compiler.outputs.irOptimized = true;
m_options.compiler.outputs.ewasm = true;
m_options.compiler.outputs.ewasmIR = true;
} }
vector<string> unsupportedOutputs; vector<string> unsupportedOutputs;
@ -743,8 +736,6 @@ General Information)").c_str(),
(CompilerOutputs::componentName(&CompilerOutputs::abi).c_str(), "ABI specification of the contracts.") (CompilerOutputs::componentName(&CompilerOutputs::abi).c_str(), "ABI specification of the contracts.")
(CompilerOutputs::componentName(&CompilerOutputs::ir).c_str(), "Intermediate Representation (IR) of all contracts.") (CompilerOutputs::componentName(&CompilerOutputs::ir).c_str(), "Intermediate Representation (IR) of all contracts.")
(CompilerOutputs::componentName(&CompilerOutputs::irOptimized).c_str(), "Optimized intermediate Representation (IR) of all contracts.") (CompilerOutputs::componentName(&CompilerOutputs::irOptimized).c_str(), "Optimized intermediate Representation (IR) of all contracts.")
(CompilerOutputs::componentName(&CompilerOutputs::ewasm).c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).")
(CompilerOutputs::componentName(&CompilerOutputs::ewasmIR).c_str(), "Intermediate representation (IR) converted to a form that can be translated directly into Ewasm text representation (EXPERIMENTAL).")
(CompilerOutputs::componentName(&CompilerOutputs::signatureHashes).c_str(), "Function signature hashes of the contracts.") (CompilerOutputs::componentName(&CompilerOutputs::signatureHashes).c_str(), "Function signature hashes of the contracts.")
(CompilerOutputs::componentName(&CompilerOutputs::natspecUser).c_str(), "Natspec user documentation of all contracts.") (CompilerOutputs::componentName(&CompilerOutputs::natspecUser).c_str(), "Natspec user documentation of all contracts.")
(CompilerOutputs::componentName(&CompilerOutputs::natspecDev).c_str(), "Natspec developer documentation of all contracts.") (CompilerOutputs::componentName(&CompilerOutputs::natspecDev).c_str(), "Natspec developer documentation of all contracts.")
@ -1001,8 +992,6 @@ void CommandLineParser::processArgs()
CompilerOutputs::componentName(&CompilerOutputs::binary), CompilerOutputs::componentName(&CompilerOutputs::binary),
CompilerOutputs::componentName(&CompilerOutputs::ir), CompilerOutputs::componentName(&CompilerOutputs::ir),
CompilerOutputs::componentName(&CompilerOutputs::irOptimized), CompilerOutputs::componentName(&CompilerOutputs::irOptimized),
CompilerOutputs::componentName(&CompilerOutputs::ewasm),
CompilerOutputs::componentName(&CompilerOutputs::ewasmIR),
g_strGas, g_strGas,
CompilerOutputs::componentName(&CompilerOutputs::asm_), CompilerOutputs::componentName(&CompilerOutputs::asm_),
CompilerOutputs::componentName(&CompilerOutputs::asmJson), CompilerOutputs::componentName(&CompilerOutputs::asmJson),
@ -1215,42 +1204,22 @@ void CommandLineParser::processArgs()
string machine = m_args[g_strMachine].as<string>(); string machine = m_args[g_strMachine].as<string>();
if (machine == g_strEVM) if (machine == g_strEVM)
m_options.assembly.targetMachine = Machine::EVM; m_options.assembly.targetMachine = Machine::EVM;
else if (machine == g_strEwasm)
m_options.assembly.targetMachine = Machine::Ewasm;
else else
solThrow(CommandLineValidationError, "Invalid option for --" + g_strMachine + ": " + machine); solThrow(CommandLineValidationError, "Invalid option for --" + g_strMachine + ": " + machine);
} }
if (m_options.assembly.targetMachine == Machine::Ewasm && m_options.assembly.inputLanguage == Input::StrictAssembly)
m_options.assembly.inputLanguage = Input::Ewasm;
if (m_args.count(g_strYulDialect)) if (m_args.count(g_strYulDialect))
{ {
string dialect = m_args[g_strYulDialect].as<string>(); string dialect = m_args[g_strYulDialect].as<string>();
if (dialect == g_strEVM) if (dialect == g_strEVM)
m_options.assembly.inputLanguage = Input::StrictAssembly; m_options.assembly.inputLanguage = Input::StrictAssembly;
else if (dialect == g_strEwasm)
{
m_options.assembly.inputLanguage = Input::Ewasm;
if (m_options.assembly.targetMachine != Machine::Ewasm)
solThrow(
CommandLineValidationError,
"If you select Ewasm as --" + g_strYulDialect + ", "
"--" + g_strMachine + " has to be Ewasm as well."
);
}
else else
solThrow(CommandLineValidationError, "Invalid option for --" + g_strYulDialect + ": " + dialect); solThrow(CommandLineValidationError, "Invalid option for --" + g_strYulDialect + ": " + dialect);
} }
if (m_options.optimizer.enabled && (m_options.assembly.inputLanguage != Input::StrictAssembly && m_options.assembly.inputLanguage != Input::Ewasm)) if (m_options.optimizer.enabled && (m_options.assembly.inputLanguage != Input::StrictAssembly))
solThrow( solThrow(
CommandLineValidationError, CommandLineValidationError,
"Optimizer can only be used for strict assembly. Use --" + g_strStrictAssembly + "." "Optimizer can only be used for strict assembly. Use --" + g_strStrictAssembly + "."
); );
if (m_options.assembly.targetMachine == Machine::Ewasm && m_options.assembly.inputLanguage != Input::StrictAssembly && m_options.assembly.inputLanguage != Input::Ewasm)
solThrow(
CommandLineValidationError,
"The selected input language is not directly supported when targeting the Ewasm machine "
"and automatic translation is not available."
);
return; return;
} }
else if (countEnabledOptions({g_strYulDialect, g_strMachine}) >= 1) else if (countEnabledOptions({g_strYulDialect, g_strMachine}) >= 1)

View File

@ -78,8 +78,6 @@ struct CompilerOutputs
{"abi", &CompilerOutputs::abi}, {"abi", &CompilerOutputs::abi},
{"ir", &CompilerOutputs::ir}, {"ir", &CompilerOutputs::ir},
{"ir-optimized", &CompilerOutputs::irOptimized}, {"ir-optimized", &CompilerOutputs::irOptimized},
{"ewasm", &CompilerOutputs::ewasm},
{"ewasm-ir", &CompilerOutputs::ewasmIR},
{"hashes", &CompilerOutputs::signatureHashes}, {"hashes", &CompilerOutputs::signatureHashes},
{"userdoc", &CompilerOutputs::natspecUser}, {"userdoc", &CompilerOutputs::natspecUser},
{"devdoc", &CompilerOutputs::natspecDev}, {"devdoc", &CompilerOutputs::natspecDev},
@ -98,8 +96,6 @@ struct CompilerOutputs
bool abi = false; bool abi = false;
bool ir = false; bool ir = false;
bool irOptimized = false; bool irOptimized = false;
bool ewasm = false;
bool ewasmIR = false;
bool signatureHashes = false; bool signatureHashes = false;
bool natspecUser = false; bool natspecUser = false;
bool natspecDev = false; bool natspecDev = false;

View File

@ -135,8 +135,6 @@ set(libyul_sources
libyul/ControlFlowSideEffectsTest.h libyul/ControlFlowSideEffectsTest.h
libyul/EVMCodeTransformTest.cpp libyul/EVMCodeTransformTest.cpp
libyul/EVMCodeTransformTest.h libyul/EVMCodeTransformTest.h
libyul/EwasmTranslationTest.cpp
libyul/EwasmTranslationTest.h
libyul/FunctionSideEffects.cpp libyul/FunctionSideEffects.cpp
libyul/FunctionSideEffects.h libyul/FunctionSideEffects.h
libyul/Inliner.cpp libyul/Inliner.cpp

View File

@ -108,13 +108,11 @@ void CommonOptions::addOptions()
("eof-version", po::value<uint64_t>()->implicit_value(1u), "which EOF version to use") ("eof-version", po::value<uint64_t>()->implicit_value(1u), "which EOF version to use")
("testpath", po::value<fs::path>(&this->testPath)->default_value(solidity::test::testPath()), "path to test files") ("testpath", po::value<fs::path>(&this->testPath)->default_value(solidity::test::testPath()), "path to test files")
("vm", po::value<std::vector<fs::path>>(&vmPaths), "path to evmc library, can be supplied multiple times.") ("vm", po::value<std::vector<fs::path>>(&vmPaths), "path to evmc library, can be supplied multiple times.")
("ewasm", po::bool_switch(&ewasm)->default_value(ewasm), "tries to automatically find an ewasm vm and enable ewasm test-execution.")
("batches", po::value<size_t>(&this->batches)->default_value(1), "set number of batches to split the tests into") ("batches", po::value<size_t>(&this->batches)->default_value(1), "set number of batches to split the tests into")
("selected-batch", po::value<size_t>(&this->selectedBatch)->default_value(0), "zero-based number of batch to execute") ("selected-batch", po::value<size_t>(&this->selectedBatch)->default_value(0), "zero-based number of batch to execute")
("no-semantic-tests", po::bool_switch(&disableSemanticTests)->default_value(disableSemanticTests), "disable semantic tests") ("no-semantic-tests", po::bool_switch(&disableSemanticTests)->default_value(disableSemanticTests), "disable semantic tests")
("no-smt", po::bool_switch(&disableSMT)->default_value(disableSMT), "disable SMT checker") ("no-smt", po::bool_switch(&disableSMT)->default_value(disableSMT), "disable SMT checker")
("optimize", po::bool_switch(&optimize)->default_value(optimize), "enables optimization") ("optimize", po::bool_switch(&optimize)->default_value(optimize), "enables optimization")
("enforce-compile-to-ewasm", po::bool_switch(&enforceCompileToEwasm)->default_value(enforceCompileToEwasm), "Enforce compiling all tests to Ewasm to see if additional tests can be activated.")
("enforce-gas-cost", po::value<bool>(&enforceGasTest)->default_value(enforceGasTest)->implicit_value(true), "Enforce checking gas cost in semantic tests.") ("enforce-gas-cost", po::value<bool>(&enforceGasTest)->default_value(enforceGasTest)->implicit_value(true), "Enforce checking gas cost in semantic tests.")
("enforce-gas-cost-min-value", po::value(&enforceGasTestMinValue)->default_value(enforceGasTestMinValue), "Threshold value to enforce adding gas checks to a test.") ("enforce-gas-cost-min-value", po::value(&enforceGasTestMinValue)->default_value(enforceGasTestMinValue), "Threshold value to enforce adding gas checks to a test.")
("abiencoderv1", po::bool_switch(&useABIEncoderV1)->default_value(useABIEncoderV1), "enables abi encoder v1") ("abiencoderv1", po::bool_switch(&useABIEncoderV1)->default_value(useABIEncoderV1), "enables abi encoder v1")
@ -209,14 +207,6 @@ bool CommonOptions::parse(int argc, char const* const* argv)
vmPaths.emplace_back(*repoPath); vmPaths.emplace_back(*repoPath);
else else
vmPaths.emplace_back(evmoneFilename); vmPaths.emplace_back(evmoneFilename);
if (ewasm) {
if (auto envPath = getenv("ETH_HERA"))
vmPaths.emplace_back(envPath);
else if (auto repoPath = findInDefaultPath(heraFilename))
vmPaths.emplace_back(*repoPath);
else
vmPaths.emplace_back(heraFilename);
}
} }
return true; return true;
@ -234,8 +224,6 @@ string CommonOptions::toString(vector<string> const& _selectedOptions) const
{"optimize", boolToString(optimize)}, {"optimize", boolToString(optimize)},
{"useABIEncoderV1", boolToString(useABIEncoderV1)}, {"useABIEncoderV1", boolToString(useABIEncoderV1)},
{"batch", to_string(selectedBatch + 1) + "/" + to_string(batches)}, {"batch", to_string(selectedBatch + 1) + "/" + to_string(batches)},
{"ewasm", boolToString(ewasm)},
{"enforceCompileToEwasm", boolToString(enforceCompileToEwasm)},
{"enforceGasTest", boolToString(enforceGasTest)}, {"enforceGasTest", boolToString(enforceGasTest)},
{"enforceGasTestMinValue", enforceGasTestMinValue.str()}, {"enforceGasTestMinValue", enforceGasTestMinValue.str()},
{"disableSemanticTests", boolToString(disableSemanticTests)}, {"disableSemanticTests", boolToString(disableSemanticTests)},
@ -303,10 +291,10 @@ bool isValidSemanticTestPath(boost::filesystem::path const& _testPath)
bool loadVMs(CommonOptions const& _options) bool loadVMs(CommonOptions const& _options)
{ {
if (_options.disableSemanticTests && !_options.ewasm) if (_options.disableSemanticTests)
return true; return true;
auto [evmSupported, ewasmSupported] = solidity::test::EVMHost::checkVmPaths(_options.vmPaths); bool evmSupported = solidity::test::EVMHost::checkVmPaths(_options.vmPaths);
if (!_options.disableSemanticTests && !evmSupported) if (!_options.disableSemanticTests && !evmSupported)
{ {
std::cerr << "Unable to find " << solidity::test::evmoneFilename; std::cerr << "Unable to find " << solidity::test::evmoneFilename;
@ -315,14 +303,6 @@ bool loadVMs(CommonOptions const& _options)
std::cerr << solidity::test::evmoneDownloadLink << std::endl; std::cerr << solidity::test::evmoneDownloadLink << std::endl;
return false; return false;
} }
if (_options.ewasm && !ewasmSupported)
{
std::cerr << "Unable to find " << solidity::test::heraFilename;
std::cerr << ". To be able to enable ewasm tests, please provide the path using --vm <path>." << std::endl;
std::cerr << "You can download it at" << std::endl;
std::cerr << solidity::test::heraDownloadLink << std::endl;
return false;
}
return true; return true;
} }

View File

@ -34,18 +34,12 @@ namespace solidity::test
#ifdef _WIN32 #ifdef _WIN32
static constexpr auto evmoneFilename = "evmone.dll"; static constexpr auto evmoneFilename = "evmone.dll";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.10.0/evmone-0.10.0-windows-amd64.zip"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.10.0/evmone-0.10.0-windows-amd64.zip";
static constexpr auto heraFilename = "hera.dll";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.6.0.tar.gz";
#elif defined(__APPLE__) #elif defined(__APPLE__)
static constexpr auto evmoneFilename = "libevmone.dylib"; static constexpr auto evmoneFilename = "libevmone.dylib";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.10.0/evmone-0.10.0-darwin-x86_64.tar.gz"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.10.0/evmone-0.10.0-darwin-x86_64.tar.gz";
static constexpr auto heraFilename = "libhera.dylib";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.6.0/hera-0.6.0-darwin-x86_64.tar.gz";
#else #else
static constexpr auto evmoneFilename = "libevmone.so"; static constexpr auto evmoneFilename = "libevmone.so";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.10.0/evmone-0.10.0-linux-x86_64.tar.gz"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.10.0/evmone-0.10.0-linux-x86_64.tar.gz";
static constexpr auto heraFilename = "libhera.so";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.6.0/hera-0.6.0-linux-x86_64.tar.gz";
#endif #endif
struct ConfigException: public util::Exception {}; struct ConfigException: public util::Exception {};
@ -58,9 +52,7 @@ struct CommonOptions
std::vector<boost::filesystem::path> vmPaths; std::vector<boost::filesystem::path> vmPaths;
boost::filesystem::path testPath; boost::filesystem::path testPath;
bool ewasm = false;
bool optimize = false; bool optimize = false;
bool enforceCompileToEwasm = false;
bool enforceGasTest = false; bool enforceGasTest = false;
u256 enforceGasTestMinValue = 100000; u256 enforceGasTestMinValue = 100000;
bool disableSemanticTests = false; bool disableSemanticTests = false;

View File

@ -47,10 +47,10 @@ evmc::VM& EVMHost::getVM(string const& _path)
auto vm = evmc::VM{evmc_load_and_configure(_path.c_str(), &errorCode)}; auto vm = evmc::VM{evmc_load_and_configure(_path.c_str(), &errorCode)};
if (vm && errorCode == EVMC_LOADER_SUCCESS) if (vm && errorCode == EVMC_LOADER_SUCCESS)
{ {
if (vm.get_capabilities() & (EVMC_CAPABILITY_EVM1 | EVMC_CAPABILITY_EWASM)) if (vm.get_capabilities() & (EVMC_CAPABILITY_EVM1))
vms[_path] = make_unique<evmc::VM>(evmc::VM(std::move(vm))); vms[_path] = make_unique<evmc::VM>(evmc::VM(std::move(vm)));
else else
cerr << "VM loaded neither supports EVM1 nor EWASM" << endl; cerr << "VM loaded does not support EVM1" << endl;
} }
else else
{ {
@ -67,10 +67,9 @@ evmc::VM& EVMHost::getVM(string const& _path)
return NullVM; return NullVM;
} }
std::tuple<bool, bool> EVMHost::checkVmPaths(vector<boost::filesystem::path> const& _vmPaths) bool EVMHost::checkVmPaths(vector<boost::filesystem::path> const& _vmPaths)
{ {
bool evmVmFound = false; bool evmVmFound = false;
bool ewasmVmFound = false;
for (auto const& path: _vmPaths) for (auto const& path: _vmPaths)
{ {
evmc::VM& vm = EVMHost::getVM(path.string()); evmc::VM& vm = EVMHost::getVM(path.string());
@ -83,15 +82,8 @@ std::tuple<bool, bool> EVMHost::checkVmPaths(vector<boost::filesystem::path> con
BOOST_THROW_EXCEPTION(runtime_error("Multiple evm1 evmc vms defined. Please only define one evm1 evmc vm.")); BOOST_THROW_EXCEPTION(runtime_error("Multiple evm1 evmc vms defined. Please only define one evm1 evmc vm."));
evmVmFound = true; evmVmFound = true;
} }
if (vm.has_capability(EVMC_CAPABILITY_EWASM))
{
if (ewasmVmFound)
BOOST_THROW_EXCEPTION(runtime_error("Multiple ewasm evmc vms where defined. Please only define one ewasm evmc vm."));
ewasmVmFound = true;
} }
} return evmVmFound;
return {evmVmFound, ewasmVmFound};
} }
EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):

View File

@ -65,16 +65,15 @@ public:
// Solidity testing specific features. // Solidity testing specific features.
/// Tries to dynamically load an evmc vm supporting evm1 or ewasm and caches the loaded VM. /// Tries to dynamically load an evmc vm supporting evm1 and caches the loaded VM.
/// @returns vmc::VM(nullptr) on failure. /// @returns vmc::VM(nullptr) on failure.
static evmc::VM& getVM(std::string const& _path = {}); static evmc::VM& getVM(std::string const& _path = {});
/// Tries to load all defined evmc vm shared libraries. /// Tries to load all defined evmc vm shared libraries.
/// @param _vmPaths paths to multiple evmc shared libraries. /// @param _vmPaths paths to multiple evmc shared libraries.
/// @throw Exception if multiple evm1 or multiple ewasm evmc vms where loaded. /// @throw Exception if multiple evm1 vms where loaded.
/// @returns A pair of booleans, the first element being true, if an evmc vm supporting evm1 was loaded properly, /// @returns true, if an evmc vm supporting evm1 was loaded properly,
/// the second being true, if an evmc vm supporting ewasm was loaded properly. static bool checkVmPaths(std::vector<boost::filesystem::path> const& _vmPaths);
static std::tuple<bool, bool> checkVmPaths(std::vector<boost::filesystem::path> const& _vmPaths);
explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm); explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm);

View File

@ -60,11 +60,6 @@ ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion, vector<
{ {
if (solidity::test::CommonOptions::get().optimize) if (solidity::test::CommonOptions::get().optimize)
m_optimiserSettings = solidity::frontend::OptimiserSettings::standard(); m_optimiserSettings = solidity::frontend::OptimiserSettings::standard();
for (auto const& path: m_vmPaths)
if (EVMHost::getVM(path.string()).has_capability(EVMC_CAPABILITY_EWASM))
m_supportsEwasm = true;
selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1); selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1);
} }

View File

@ -295,7 +295,6 @@ protected:
solidity::frontend::RevertStrings m_revertStrings = solidity::frontend::RevertStrings::Default; solidity::frontend::RevertStrings m_revertStrings = solidity::frontend::RevertStrings::Default;
solidity::frontend::OptimiserSettings m_optimiserSettings = solidity::frontend::OptimiserSettings::minimal(); solidity::frontend::OptimiserSettings m_optimiserSettings = solidity::frontend::OptimiserSettings::minimal();
bool m_showMessages = false; bool m_showMessages = false;
bool m_supportsEwasm = false;
std::unique_ptr<EVMHost> m_evmcHost; std::unique_ptr<EVMHost> m_evmcHost;
std::vector<boost::filesystem::path> m_vmPaths; std::vector<boost::filesystem::path> m_vmPaths;

View File

@ -28,7 +28,6 @@
#include <test/libsolidity/SMTCheckerTest.h> #include <test/libsolidity/SMTCheckerTest.h>
#include <test/libyul/ControlFlowGraphTest.h> #include <test/libyul/ControlFlowGraphTest.h>
#include <test/libyul/EVMCodeTransformTest.h> #include <test/libyul/EVMCodeTransformTest.h>
#include <test/libyul/EwasmTranslationTest.h>
#include <test/libyul/YulOptimizerTest.h> #include <test/libyul/YulOptimizerTest.h>
#include <test/libyul/YulInterpreterTest.h> #include <test/libyul/YulInterpreterTest.h>
#include <test/libyul/ObjectCompilerTest.h> #include <test/libyul/ObjectCompilerTest.h>
@ -78,7 +77,6 @@ Testsuite const g_interactiveTestsuites[] = {
{"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create}, {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create},
{"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create}, {"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create},
{"Memory Guard Tests", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create}, {"Memory Guard Tests", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create},
{"Ewasm Translation", "libyul", "ewasmTranslationTests", false, false, &yul::test::EwasmTranslationTest::create}
}; };
} }

View File

@ -41,7 +41,6 @@ public:
langutil::EVMVersion evmVersion; langutil::EVMVersion evmVersion;
std::optional<uint8_t> eofVersion; std::optional<uint8_t> eofVersion;
std::vector<boost::filesystem::path> vmPaths; std::vector<boost::filesystem::path> vmPaths;
bool enforceCompileToEwasm = false;
bool enforceGasCost = false; bool enforceGasCost = false;
u256 enforceGasCostMinValue; u256 enforceGasCostMinValue;
}; };

View File

@ -209,8 +209,6 @@ EOF
sed -i.bak -E -e 's/([0-9a-f]{34}\$__)[0-9a-f]+(__\$[0-9a-f]{17})/\1<BYTECODE REMOVED>\2/g' "$stdout_path" sed -i.bak -E -e 's/([0-9a-f]{34}\$__)[0-9a-f]+(__\$[0-9a-f]{17})/\1<BYTECODE REMOVED>\2/g' "$stdout_path"
# Remove metadata in assembly output (see below about the magic numbers) # Remove metadata in assembly output (see below about the magic numbers)
sed -i.bak -E -e 's/"[0-9a-f]+64697066735822[0-9a-f]+64736f6c63[0-9a-f]+/"<BYTECODE REMOVED>/g' "$stdout_path" sed -i.bak -E -e 's/"[0-9a-f]+64697066735822[0-9a-f]+64736f6c63[0-9a-f]+/"<BYTECODE REMOVED>/g' "$stdout_path"
# Remove hash of text representation in ewasm
sed -i.bak -E -e 's/The Keccak-256 hash of the text representation of .*: [0-9a-f]+/The Keccak-256 hash of the text representation of <REMOVED>/g' "$stdout_path"
# Replace escaped newlines by actual newlines for readability # Replace escaped newlines by actual newlines for readability
# shellcheck disable=SC1003 # shellcheck disable=SC1003

View File

@ -1 +0,0 @@
--assemble --optimize --yul-dialect evm --machine ewasm

View File

@ -1,3 +0,0 @@
{
sstore(0, 1)
}

View File

@ -1,96 +0,0 @@
======= evm_to_wasm/input.yul (Ewasm) =======
Pretty printed source:
object "object" {
code { { sstore(0, 1) } }
}
==========================
Translated source:
object "object" {
code {
function main()
{
let hi := i64.shl(i64.extend_i32_u(bswap32(i32.wrap_i64(0))), 32)
let y := i64.or(hi, i64.extend_i32_u(bswap32(i32.wrap_i64(i64.shr_u(0, 32)))))
i64.store(0:i32, y)
i64.store(i32.add(0:i32, 8:i32), y)
i64.store(i32.add(0:i32, 16:i32), y)
i64.store(i32.add(0:i32, 24:i32), y)
i64.store(32:i32, y)
i64.store(i32.add(32:i32, 8:i32), y)
i64.store(i32.add(32:i32, 16:i32), y)
let hi_1 := i64.shl(i64.extend_i32_u(bswap32(i32.wrap_i64(1))), 32)
i64.store(i32.add(32:i32, 24:i32), i64.or(hi_1, i64.extend_i32_u(bswap32(i32.wrap_i64(i64.shr_u(1, 32))))))
eth.storageStore(0:i32, 32:i32)
}
function bswap16(x:i32) -> y:i32
{
y := i32.or(i32.and(i32.shl(x, 8:i32), 0xff00:i32), i32.and(i32.shr_u(x, 8:i32), 0xff:i32))
}
function bswap32(x:i32) -> y:i32
{
let hi:i32 := i32.shl(bswap16(x), 16:i32)
y := i32.or(hi, bswap16(i32.shr_u(x, 16:i32)))
}
}
}
Binary representation:
0061736d01000000010e0360000060017f017f60027f7f0002190108657468657265756d0c73746f7261676553746f726500020304030001010503010001060100071102066d656d6f72790200046d61696e00010ac70103850101037e02404200a71003ad422086210020004200422088a71003ad84210141002001370000410041086a2001370000410041106a2001370000410041186a200137000041202001370000412041086a2001370000412041106a20013700004201a71003ad4220862102412041186a20024201422088a71003ad843700004100412010000b0b1f01017f024020004108744180fe0371200041087641ff01717221010b20010b1e01027f02402000100241107421022002200041107610027221010b20010b
Text representation:
(module
(import "ethereum" "storageStore" (func $eth.storageStore (param i32 i32)))
(memory $memory (export "memory") 1)
(export "main" (func $main))
(func $main
(local $hi i64)
(local $y i64)
(local $hi_1 i64)
(block $label_
(local.set $hi (i64.shl (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.const 0)))) (i64.const 32)))
(local.set $y (i64.or (local.get $hi) (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.shr_u (i64.const 0) (i64.const 32)))))))
(i64.store (i32.const 0) (local.get $y))
(i64.store (i32.add (i32.const 0) (i32.const 8)) (local.get $y))
(i64.store (i32.add (i32.const 0) (i32.const 16)) (local.get $y))
(i64.store (i32.add (i32.const 0) (i32.const 24)) (local.get $y))
(i64.store (i32.const 32) (local.get $y))
(i64.store (i32.add (i32.const 32) (i32.const 8)) (local.get $y))
(i64.store (i32.add (i32.const 32) (i32.const 16)) (local.get $y))
(local.set $hi_1 (i64.shl (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.const 1)))) (i64.const 32)))
(i64.store (i32.add (i32.const 32) (i32.const 24)) (i64.or (local.get $hi_1) (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.shr_u (i64.const 1) (i64.const 32)))))))
(call $eth.storageStore (i32.const 0) (i32.const 32))
)
)
(func $bswap16
(param $x i32)
(result i32)
(local $y i32)
(block $label__1
(local.set $y (i32.or (i32.and (i32.shl (local.get $x) (i32.const 8)) (i32.const 65280)) (i32.and (i32.shr_u (local.get $x) (i32.const 8)) (i32.const 255))))
)
(local.get $y)
)
(func $bswap32
(param $x i32)
(result i32)
(local $y i32)
(local $hi i32)
(block $label__2
(local.set $hi (i32.shl (call $bswap16 (local.get $x)) (i32.const 16)))
(local.set $y (i32.or (local.get $hi) (call $bswap16 (i32.shr_u (local.get $x) (i32.const 16)))))
)
(local.get $y)
)
)

View File

@ -1 +0,0 @@
--assemble --optimize --yul-dialect evm --machine ewasm

View File

@ -1,8 +0,0 @@
{
let x := calldataload(0)
for { } lt(x, 10) { x := add(x, 1) } {
if eq(x, 2) { break }
if eq(x, 4) { continue }
}
sstore(0, x)
}

View File

@ -1,583 +0,0 @@
======= evm_to_wasm_break/input.yul (Ewasm) =======
Pretty printed source:
object "object" {
code {
{
let x := calldataload(0)
for { } lt(x, 10) { x := add(x, 1) }
{
if eq(x, 2) { break }
if eq(x, 4) { continue }
}
sstore(0, x)
}
}
}
==========================
Translated source:
object "object" {
code {
function main()
{
let x, x_1, x_2, x_3 := calldataload()
let x_4 := x
let x_5 := x_1
let x_6 := x_2
let x_7 := x_3
let _1:i32 := i32.eqz(i32.eqz(i64.eqz(i64.or(i64.or(0, 0), i64.or(0, 1)))))
for { }
i32.eqz(_1)
{
let x_8, x_9, x_10, x_11 := add(x_4, x_5, x_6, x_7)
x_4 := x_8
x_5 := x_9
x_6 := x_10
x_7 := x_11
}
{
let _2, _3, _4, _5 := iszero_1324_2108(lt(x_4, x_5, x_6, x_7))
if i32.eqz(i64.eqz(i64.or(i64.or(_2, _3), i64.or(_4, _5)))) { break }
let _6, _7, _8, _9 := eq_771_2109(x_4, x_5, x_6, x_7)
if i32.eqz(i64.eqz(i64.or(i64.or(_6, _7), i64.or(_8, _9)))) { break }
let _10, _11, _12, _13 := eq_772_2110(x_4, x_5, x_6, x_7)
if i32.eqz(i64.eqz(i64.or(i64.or(_10, _11), i64.or(_12, _13)))) { continue }
}
sstore(x_4, x_5, x_6, x_7)
}
function add(x1, x2, x3, x4) -> r1, r2, r3, r4
{
let t := i64.add(x4, 1)
r4 := i64.add(t, 0)
let t_1 := i64.add(x3, 0)
r3 := i64.add(t_1, i64.extend_i32_u(i32.or(i64.lt_u(t, x4), i64.lt_u(r4, t))))
let t_2 := i64.add(x2, 0)
r2 := i64.add(t_2, i64.extend_i32_u(i32.or(i64.lt_u(t_1, x3), i64.lt_u(r3, t_1))))
r1 := i64.add(i64.add(x1, 0), i64.extend_i32_u(i32.or(i64.lt_u(t_2, x2), i64.lt_u(r2, t_2))))
}
function iszero_1324_2108(x4) -> r1, r2, r3, r4
{
r4 := i64.extend_i32_u(i64.eqz(i64.or(i64.or(0, 0), i64.or(0, x4))))
}
function eq_771_2109(x1, x2, x3, x4) -> r1, r2, r3, r4
{
r4 := i64.extend_i32_u(i32.and(i64.eq(x1, 0), i32.and(i64.eq(x2, 0), i32.and(i64.eq(x3, 0), i64.eq(x4, 2)))))
}
function eq_772_2110(x1, x2, x3, x4) -> r1, r2, r3, r4
{
r4 := i64.extend_i32_u(i32.and(i64.eq(x1, 0), i32.and(i64.eq(x2, 0), i32.and(i64.eq(x3, 0), i64.eq(x4, 4)))))
}
function lt(x1, x2, x3, x4) -> z4
{
let z:i32 := false
let _1 := 0
let _2:i32 := 0xffffffff:i32
switch i32.select(_2, i64.ne(x1, _1), i64.lt_u(x1, _1))
case 0:i32 {
switch i32.select(_2, i64.ne(x2, _1), i64.lt_u(x2, _1))
case 0:i32 {
switch i32.select(_2, i64.ne(x3, _1), i64.lt_u(x3, _1))
case 0:i32 { z := i64.lt_u(x4, 10) }
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 }
z4 := i64.extend_i32_u(z)
}
function u256_to_i32_774() -> v:i32
{
let _1 := 0
if i64.ne(_1, i64.or(i64.or(_1, _1), _1)) { unreachable() }
if i64.ne(_1, i64.shr_u(_1, 32)) { unreachable() }
v := i32.wrap_i64(_1)
}
function u256_to_i32() -> v:i32
{
if i64.ne(0, i64.or(i64.or(0, 0), 0)) { unreachable() }
if i64.ne(0, i64.shr_u(32, 32)) { unreachable() }
v := i32.wrap_i64(32)
}
function bswap16(x:i32) -> y:i32
{
y := i32.or(i32.and(i32.shl(x, 8:i32), 0xff00:i32), i32.and(i32.shr_u(x, 8:i32), 0xff:i32))
}
function bswap32(x:i32) -> y:i32
{
let hi:i32 := i32.shl(bswap16(x), 16:i32)
y := i32.or(hi, bswap16(i32.shr_u(x, 16:i32)))
}
function bswap64(x) -> y
{
let hi := i64.shl(i64.extend_i32_u(bswap32(i32.wrap_i64(x))), 32)
y := i64.or(hi, i64.extend_i32_u(bswap32(i32.wrap_i64(i64.shr_u(x, 32)))))
}
function calldataload() -> z1, z2, z3, z4
{
let cds:i32 := eth.getCallDataSize()
let destination:i32 := u256_to_i32_774()
let offset:i32 := u256_to_i32_774()
let requested_size:i32 := u256_to_i32()
if i32.gt_u(offset, i32.sub(0xffffffff:i32, requested_size)) { eth.revert(0:i32, 0:i32) }
let available_size:i32 := i32.sub(cds, offset)
if i32.gt_u(offset, cds) { available_size := 0:i32 }
let _1:i32 := 0:i32
if i32.gt_u(available_size, _1)
{
eth.callDataCopy(destination, offset, available_size)
}
if i32.gt_u(requested_size, available_size)
{
let _2:i32 := i32.sub(requested_size, available_size)
let _3:i32 := i32.add(destination, available_size)
let i:i32 := _1
for { } i32.lt_u(i, _2) { i := i32.add(i, 1:i32) }
{
i32.store8(i32.add(_3, i), _1)
}
}
let z1_1 := bswap64(i64.load(_1))
let z2_1 := bswap64(i64.load(i32.add(_1, 8:i32)))
let z3_1 := bswap64(i64.load(i32.add(_1, 16:i32)))
let z4_1 := bswap64(i64.load(i32.add(_1, 24:i32)))
z1 := z1_1
z2 := z2_1
z3 := z3_1
z4 := z4_1
}
function sstore(y1, y2, y3, y4)
{
let hi := i64.shl(i64.extend_i32_u(bswap32(i32.wrap_i64(0))), 32)
let y := i64.or(hi, i64.extend_i32_u(bswap32(i32.wrap_i64(i64.shr_u(0, 32)))))
i64.store(0:i32, y)
i64.store(i32.add(0:i32, 8:i32), y)
i64.store(i32.add(0:i32, 16:i32), y)
i64.store(i32.add(0:i32, 24:i32), y)
i64.store(32:i32, bswap64(y1))
i64.store(i32.add(32:i32, 8:i32), bswap64(y2))
i64.store(i32.add(32:i32, 16:i32), bswap64(y3))
i64.store(i32.add(32:i32, 24:i32), bswap64(y4))
eth.storageStore(0:i32, 32:i32)
}
}
}
Binary representation:
0061736d010000000130096000006000017e6000017f60017e017e60047e7e7e7e0060047e7e7e7e017e60017f017f60027f7f0060037f7f7f00025e0408657468657265756d0c73746f7261676553746f7265000708657468657265756d06726576657274000708657468657265756d0f67657443616c6c4461746153697a65000208657468657265756d0c63616c6c44617461436f70790008030e0d0005030505050202060603010405030100010615047e0142000b7e0142000b7e0142000b7f0141000b071102066d656d6f72790200046d61696e00040abd090d8d0203087e017f107e02400240100f21002300210123012102230221030b200021042001210520022106200321074200420084420042018484504545210802400340200845450d010240024020042005200620071009100621092303210a2300210b2301210c0b2009200a84200b200c8484504504400c030b024020042005200620071007210d2300210e2301210f230221100b200d200e84200f20108484504504400c030b02402004200520062007100821112300211223012113230221140b2011201284201320148484504504400c010b0b02402004200520062007100521152300211623012117230221180b201521042016210520172106201821070c000b0b200420052006200710100b0b6701077e0240200342017c2108200842007c2107200242007c210920092008200354200720085472ad7c2106200142007c210a200a2009200254200620095472ad7c2105200042007c200a2001542005200a5472ad7c21040b20052400200624012007240220040b2401047e0240420042008442002000848450ad21040b20022400200324012004240220010b2f01047e02402000420051200142005120024200512003420251717171ad21070b20052400200624012007240220040b2f01047e02402000420051200142005120024200512003420451717171ad21070b20052400200624012007240220040bab0104017e017f017e047f02404100210542002106417f210702402007200020065220002006541b21082008410046044002402007200120065220012006541b21092009410046044002402007200220065220022006541b210a200a41004604402003420a54210505200a41014604404100210505410121050b0b0b05200941014604404100210505410121050b0b0b05200841014604404100210505410121050b0b0b2005ad21040b20040b2f02017f017e02404200210120012001200184200184520440000b20012001422088520440000b2001a721000b20000b2901017f024042004200420084420084520440000b42004220422088520440000b4220a721000b20000b1f01017f024020004108744180fe0371200041087641ff01717221010b20010b1e01027f02402000100c411074210220022000411076100c7221010b20010b2201027e02402000a7100dad422086210220022000422088a7100dad8421010b20010bdc0103047e097f047e024010022104100a2105100a2106100b21072006417f20076b4b04404100410010010b200420066b2108200620044b0440410021080b41002109200820094b044020052006200810030b200720084b0440200720086b210a200520086a210b2009210c02400340200c200a49450d010240200b200c6a20093a00000b200c41016a210c0c000b0b0b2009290000100e210d200941086a290000100e210e200941106a290000100e210f200941186a290000100e2110200d2100200e2101200f2102201021030b20012400200224012003240220000b7801027e02404200a7100dad422086210420044200422088a7100dad84210541002005370000410041086a2005370000410041106a2005370000410041186a200537000041202000100e370000412041086a2001100e370000412041106a2002100e370000412041186a2003100e3700004100412010000b0b
Text representation:
(module
(import "ethereum" "storageStore" (func $eth.storageStore (param i32 i32)))
(import "ethereum" "revert" (func $eth.revert (param i32 i32)))
(import "ethereum" "getCallDataSize" (func $eth.getCallDataSize (result i32)))
(import "ethereum" "callDataCopy" (func $eth.callDataCopy (param i32 i32 i32)))
(memory $memory (export "memory") 1)
(export "main" (func $main))
(global $global_ (mut i64) (i64.const 0))
(global $global__1 (mut i64) (i64.const 0))
(global $global__2 (mut i64) (i64.const 0))
(global $global__6 (mut i32) (i32.const 0))
(func $main
(local $x i64)
(local $x_1 i64)
(local $x_2 i64)
(local $x_3 i64)
(local $x_4 i64)
(local $x_5 i64)
(local $x_6 i64)
(local $x_7 i64)
(local $_1 i32)
(local $_2 i64)
(local $_3 i64)
(local $_4 i64)
(local $_5 i64)
(local $_6 i64)
(local $_7 i64)
(local $_8 i64)
(local $_9 i64)
(local $_10 i64)
(local $_11 i64)
(local $_12 i64)
(local $_13 i64)
(local $x_8 i64)
(local $x_9 i64)
(local $x_10 i64)
(local $x_11 i64)
(block $label_
(block
(local.set $x (call $calldataload))
(local.set $x_1 (global.get $global_))
(local.set $x_2 (global.get $global__1))
(local.set $x_3 (global.get $global__2))
)
(local.set $x_4 (local.get $x))
(local.set $x_5 (local.get $x_1))
(local.set $x_6 (local.get $x_2))
(local.set $x_7 (local.get $x_3))
(local.set $_1 (i32.eqz (i32.eqz (i64.eqz (i64.or (i64.or (i64.const 0) (i64.const 0)) (i64.or (i64.const 0) (i64.const 1)))))))
(block $label__3
(loop $label__5
(br_if $label__3 (i32.eqz (i32.eqz (local.get $_1))))
(block $label__4
(block
(local.set $_2 (call $iszero_1324_2108 (call $lt (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7))))
(local.set $_3 (global.get $global__6))
(local.set $_4 (global.get $global_))
(local.set $_5 (global.get $global__1))
)
(if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_2) (local.get $_3)) (i64.or (local.get $_4) (local.get $_5))))) (then
(br $label__3)
))
(block
(local.set $_6 (call $eq_771_2109 (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7)))
(local.set $_7 (global.get $global_))
(local.set $_8 (global.get $global__1))
(local.set $_9 (global.get $global__2))
)
(if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_6) (local.get $_7)) (i64.or (local.get $_8) (local.get $_9))))) (then
(br $label__3)
))
(block
(local.set $_10 (call $eq_772_2110 (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7)))
(local.set $_11 (global.get $global_))
(local.set $_12 (global.get $global__1))
(local.set $_13 (global.get $global__2))
)
(if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_10) (local.get $_11)) (i64.or (local.get $_12) (local.get $_13))))) (then
(br $label__4)
))
)
(block
(local.set $x_8 (call $add (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7)))
(local.set $x_9 (global.get $global_))
(local.set $x_10 (global.get $global__1))
(local.set $x_11 (global.get $global__2))
)
(local.set $x_4 (local.get $x_8))
(local.set $x_5 (local.get $x_9))
(local.set $x_6 (local.get $x_10))
(local.set $x_7 (local.get $x_11))
(br $label__5)
)
)
(call $sstore (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7))
)
)
(func $add
(param $x1 i64)
(param $x2 i64)
(param $x3 i64)
(param $x4 i64)
(result i64)
(local $r1 i64)
(local $r2 i64)
(local $r3 i64)
(local $r4 i64)
(local $t i64)
(local $t_1 i64)
(local $t_2 i64)
(block $label__7
(local.set $t (i64.add (local.get $x4) (i64.const 1)))
(local.set $r4 (i64.add (local.get $t) (i64.const 0)))
(local.set $t_1 (i64.add (local.get $x3) (i64.const 0)))
(local.set $r3 (i64.add (local.get $t_1) (i64.extend_i32_u (i32.or (i64.lt_u (local.get $t) (local.get $x4)) (i64.lt_u (local.get $r4) (local.get $t))))))
(local.set $t_2 (i64.add (local.get $x2) (i64.const 0)))
(local.set $r2 (i64.add (local.get $t_2) (i64.extend_i32_u (i32.or (i64.lt_u (local.get $t_1) (local.get $x3)) (i64.lt_u (local.get $r3) (local.get $t_1))))))
(local.set $r1 (i64.add (i64.add (local.get $x1) (i64.const 0)) (i64.extend_i32_u (i32.or (i64.lt_u (local.get $t_2) (local.get $x2)) (i64.lt_u (local.get $r2) (local.get $t_2))))))
)
(global.set $global_ (local.get $r2))
(global.set $global__1 (local.get $r3))
(global.set $global__2 (local.get $r4))
(local.get $r1)
)
(func $iszero_1324_2108
(param $x4 i64)
(result i64)
(local $r1 i64)
(local $r2 i64)
(local $r3 i64)
(local $r4 i64)
(block $label__8
(local.set $r4 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (i64.const 0) (i64.const 0)) (i64.or (i64.const 0) (local.get $x4))))))
)
(global.set $global_ (local.get $r2))
(global.set $global__1 (local.get $r3))
(global.set $global__2 (local.get $r4))
(local.get $r1)
)
(func $eq_771_2109
(param $x1 i64)
(param $x2 i64)
(param $x3 i64)
(param $x4 i64)
(result i64)
(local $r1 i64)
(local $r2 i64)
(local $r3 i64)
(local $r4 i64)
(block $label__9
(local.set $r4 (i64.extend_i32_u (i32.and (i64.eq (local.get $x1) (i64.const 0)) (i32.and (i64.eq (local.get $x2) (i64.const 0)) (i32.and (i64.eq (local.get $x3) (i64.const 0)) (i64.eq (local.get $x4) (i64.const 2)))))))
)
(global.set $global_ (local.get $r2))
(global.set $global__1 (local.get $r3))
(global.set $global__2 (local.get $r4))
(local.get $r1)
)
(func $eq_772_2110
(param $x1 i64)
(param $x2 i64)
(param $x3 i64)
(param $x4 i64)
(result i64)
(local $r1 i64)
(local $r2 i64)
(local $r3 i64)
(local $r4 i64)
(block $label__10
(local.set $r4 (i64.extend_i32_u (i32.and (i64.eq (local.get $x1) (i64.const 0)) (i32.and (i64.eq (local.get $x2) (i64.const 0)) (i32.and (i64.eq (local.get $x3) (i64.const 0)) (i64.eq (local.get $x4) (i64.const 4)))))))
)
(global.set $global_ (local.get $r2))
(global.set $global__1 (local.get $r3))
(global.set $global__2 (local.get $r4))
(local.get $r1)
)
(func $lt
(param $x1 i64)
(param $x2 i64)
(param $x3 i64)
(param $x4 i64)
(result i64)
(local $z4 i64)
(local $z i32)
(local $_1 i64)
(local $_2 i32)
(local $condition i32)
(local $condition_12 i32)
(local $condition_13 i32)
(block $label__11
(local.set $z (i32.const 0))
(local.set $_1 (i64.const 0))
(local.set $_2 (i32.const 4294967295))
(block
(local.set $condition (select (local.get $_2) (i64.ne (local.get $x1) (local.get $_1)) (i64.lt_u (local.get $x1) (local.get $_1))))
(if (i32.eq (local.get $condition) (i32.const 0)) (then
(block
(local.set $condition_12 (select (local.get $_2) (i64.ne (local.get $x2) (local.get $_1)) (i64.lt_u (local.get $x2) (local.get $_1))))
(if (i32.eq (local.get $condition_12) (i32.const 0)) (then
(block
(local.set $condition_13 (select (local.get $_2) (i64.ne (local.get $x3) (local.get $_1)) (i64.lt_u (local.get $x3) (local.get $_1))))
(if (i32.eq (local.get $condition_13) (i32.const 0)) (then
(local.set $z (i64.lt_u (local.get $x4) (i64.const 10)))
)(else
(if (i32.eq (local.get $condition_13) (i32.const 1)) (then
(local.set $z (i32.const 0))
)(else
(local.set $z (i32.const 1))
))
))
)
)(else
(if (i32.eq (local.get $condition_12) (i32.const 1)) (then
(local.set $z (i32.const 0))
)(else
(local.set $z (i32.const 1))
))
))
)
)(else
(if (i32.eq (local.get $condition) (i32.const 1)) (then
(local.set $z (i32.const 0))
)(else
(local.set $z (i32.const 1))
))
))
)
(local.set $z4 (i64.extend_i32_u (local.get $z)))
)
(local.get $z4)
)
(func $u256_to_i32_774
(result i32)
(local $v i32)
(local $_1 i64)
(block $label__14
(local.set $_1 (i64.const 0))
(if (i64.ne (local.get $_1) (i64.or (i64.or (local.get $_1) (local.get $_1)) (local.get $_1))) (then
(unreachable)))
(if (i64.ne (local.get $_1) (i64.shr_u (local.get $_1) (i64.const 32))) (then
(unreachable)))
(local.set $v (i32.wrap_i64 (local.get $_1)))
)
(local.get $v)
)
(func $u256_to_i32
(result i32)
(local $v i32)
(block $label__15
(if (i64.ne (i64.const 0) (i64.or (i64.or (i64.const 0) (i64.const 0)) (i64.const 0))) (then
(unreachable)))
(if (i64.ne (i64.const 0) (i64.shr_u (i64.const 32) (i64.const 32))) (then
(unreachable)))
(local.set $v (i32.wrap_i64 (i64.const 32)))
)
(local.get $v)
)
(func $bswap16
(param $x i32)
(result i32)
(local $y i32)
(block $label__16
(local.set $y (i32.or (i32.and (i32.shl (local.get $x) (i32.const 8)) (i32.const 65280)) (i32.and (i32.shr_u (local.get $x) (i32.const 8)) (i32.const 255))))
)
(local.get $y)
)
(func $bswap32
(param $x i32)
(result i32)
(local $y i32)
(local $hi i32)
(block $label__17
(local.set $hi (i32.shl (call $bswap16 (local.get $x)) (i32.const 16)))
(local.set $y (i32.or (local.get $hi) (call $bswap16 (i32.shr_u (local.get $x) (i32.const 16)))))
)
(local.get $y)
)
(func $bswap64
(param $x i64)
(result i64)
(local $y i64)
(local $hi i64)
(block $label__18
(local.set $hi (i64.shl (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (local.get $x)))) (i64.const 32)))
(local.set $y (i64.or (local.get $hi) (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.shr_u (local.get $x) (i64.const 32)))))))
)
(local.get $y)
)
(func $calldataload
(result i64)
(local $z1 i64)
(local $z2 i64)
(local $z3 i64)
(local $z4 i64)
(local $cds i32)
(local $destination i32)
(local $offset i32)
(local $requested_size i32)
(local $available_size i32)
(local $_1 i32)
(local $_2 i32)
(local $_3 i32)
(local $i i32)
(local $z1_1 i64)
(local $z2_1 i64)
(local $z3_1 i64)
(local $z4_1 i64)
(block $label__19
(local.set $cds (call $eth.getCallDataSize))
(local.set $destination (call $u256_to_i32_774))
(local.set $offset (call $u256_to_i32_774))
(local.set $requested_size (call $u256_to_i32))
(if (i32.gt_u (local.get $offset) (i32.sub (i32.const 4294967295) (local.get $requested_size))) (then
(call $eth.revert (i32.const 0) (i32.const 0))))
(local.set $available_size (i32.sub (local.get $cds) (local.get $offset)))
(if (i32.gt_u (local.get $offset) (local.get $cds)) (then
(local.set $available_size (i32.const 0))
))
(local.set $_1 (i32.const 0))
(if (i32.gt_u (local.get $available_size) (local.get $_1)) (then
(call $eth.callDataCopy (local.get $destination) (local.get $offset) (local.get $available_size))))
(if (i32.gt_u (local.get $requested_size) (local.get $available_size)) (then
(local.set $_2 (i32.sub (local.get $requested_size) (local.get $available_size)))
(local.set $_3 (i32.add (local.get $destination) (local.get $available_size)))
(local.set $i (local.get $_1))
(block $label__20
(loop $label__22
(br_if $label__20 (i32.eqz (i32.lt_u (local.get $i) (local.get $_2))))
(block $label__21
(i32.store8 (i32.add (local.get $_3) (local.get $i)) (local.get $_1))
)
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(br $label__22)
)
)
))
(local.set $z1_1 (call $bswap64 (i64.load (local.get $_1))))
(local.set $z2_1 (call $bswap64 (i64.load (i32.add (local.get $_1) (i32.const 8)))))
(local.set $z3_1 (call $bswap64 (i64.load (i32.add (local.get $_1) (i32.const 16)))))
(local.set $z4_1 (call $bswap64 (i64.load (i32.add (local.get $_1) (i32.const 24)))))
(local.set $z1 (local.get $z1_1))
(local.set $z2 (local.get $z2_1))
(local.set $z3 (local.get $z3_1))
(local.set $z4 (local.get $z4_1))
)
(global.set $global_ (local.get $z2))
(global.set $global__1 (local.get $z3))
(global.set $global__2 (local.get $z4))
(local.get $z1)
)
(func $sstore
(param $y1 i64)
(param $y2 i64)
(param $y3 i64)
(param $y4 i64)
(local $hi i64)
(local $y i64)
(block $label__23
(local.set $hi (i64.shl (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.const 0)))) (i64.const 32)))
(local.set $y (i64.or (local.get $hi) (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.shr_u (i64.const 0) (i64.const 32)))))))
(i64.store (i32.const 0) (local.get $y))
(i64.store (i32.add (i32.const 0) (i32.const 8)) (local.get $y))
(i64.store (i32.add (i32.const 0) (i32.const 16)) (local.get $y))
(i64.store (i32.add (i32.const 0) (i32.const 24)) (local.get $y))
(i64.store (i32.const 32) (call $bswap64 (local.get $y1)))
(i64.store (i32.add (i32.const 32) (i32.const 8)) (call $bswap64 (local.get $y2)))
(i64.store (i32.add (i32.const 32) (i32.const 16)) (call $bswap64 (local.get $y3)))
(i64.store (i32.add (i32.const 32) (i32.const 24)) (call $bswap64 (local.get $y4)))
(call $eth.storageStore (i32.const 0) (i32.const 32))
)
)
)

View File

@ -1 +0,0 @@
--assemble --optimize --yul-dialect evm --machine ewasm --asm

View File

@ -1,4 +0,0 @@
{
let x := 42
sstore(0, x)
}

View File

@ -1,2 +0,0 @@
======= evm_to_wasm_output_selection_asm_only/input.yul (Ewasm) =======

View File

@ -1 +0,0 @@
--assemble --optimize --yul-dialect evm --machine ewasm --ewasm-ir

View File

@ -1,4 +0,0 @@
{
let x := 42
sstore(0, x)
}

View File

@ -1,34 +0,0 @@
======= evm_to_wasm_output_selection_ewasm_ir_only/input.yul (Ewasm) =======
==========================
Translated source:
object "object" {
code {
function main()
{
let hi := i64.shl(i64.extend_i32_u(bswap32(i32.wrap_i64(0))), 32)
let y := i64.or(hi, i64.extend_i32_u(bswap32(i32.wrap_i64(i64.shr_u(0, 32)))))
i64.store(0:i32, y)
i64.store(i32.add(0:i32, 8:i32), y)
i64.store(i32.add(0:i32, 16:i32), y)
i64.store(i32.add(0:i32, 24:i32), y)
i64.store(32:i32, y)
i64.store(i32.add(32:i32, 8:i32), y)
i64.store(i32.add(32:i32, 16:i32), y)
let hi_1 := i64.shl(i64.extend_i32_u(bswap32(i32.wrap_i64(42))), 32)
i64.store(i32.add(32:i32, 24:i32), i64.or(hi_1, i64.extend_i32_u(bswap32(i32.wrap_i64(i64.shr_u(42, 32))))))
eth.storageStore(0:i32, 32:i32)
}
function bswap16(x:i32) -> y:i32
{
y := i32.or(i32.and(i32.shl(x, 8:i32), 0xff00:i32), i32.and(i32.shr_u(x, 8:i32), 0xff:i32))
}
function bswap32(x:i32) -> y:i32
{
let hi:i32 := i32.shl(bswap16(x), 16:i32)
y := i32.or(hi, bswap16(i32.shr_u(x, 16:i32)))
}
}
}

View File

@ -1 +0,0 @@
--assemble --optimize --yul-dialect evm --machine ewasm --ewasm

View File

@ -1,4 +0,0 @@
{
let x := 42
sstore(0, x)
}

View File

@ -1,54 +0,0 @@
======= evm_to_wasm_output_selection_ewasm_only/input.yul (Ewasm) =======
Text representation:
(module
(import "ethereum" "storageStore" (func $eth.storageStore (param i32 i32)))
(memory $memory (export "memory") 1)
(export "main" (func $main))
(func $main
(local $hi i64)
(local $y i64)
(local $hi_1 i64)
(block $label_
(local.set $hi (i64.shl (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.const 0)))) (i64.const 32)))
(local.set $y (i64.or (local.get $hi) (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.shr_u (i64.const 0) (i64.const 32)))))))
(i64.store (i32.const 0) (local.get $y))
(i64.store (i32.add (i32.const 0) (i32.const 8)) (local.get $y))
(i64.store (i32.add (i32.const 0) (i32.const 16)) (local.get $y))
(i64.store (i32.add (i32.const 0) (i32.const 24)) (local.get $y))
(i64.store (i32.const 32) (local.get $y))
(i64.store (i32.add (i32.const 32) (i32.const 8)) (local.get $y))
(i64.store (i32.add (i32.const 32) (i32.const 16)) (local.get $y))
(local.set $hi_1 (i64.shl (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.const 42)))) (i64.const 32)))
(i64.store (i32.add (i32.const 32) (i32.const 24)) (i64.or (local.get $hi_1) (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.shr_u (i64.const 42) (i64.const 32)))))))
(call $eth.storageStore (i32.const 0) (i32.const 32))
)
)
(func $bswap16
(param $x i32)
(result i32)
(local $y i32)
(block $label__1
(local.set $y (i32.or (i32.and (i32.shl (local.get $x) (i32.const 8)) (i32.const 65280)) (i32.and (i32.shr_u (local.get $x) (i32.const 8)) (i32.const 255))))
)
(local.get $y)
)
(func $bswap32
(param $x i32)
(result i32)
(local $y i32)
(local $hi i32)
(block $label__2
(local.set $hi (i32.shl (call $bswap16 (local.get $x)) (i32.const 16)))
(local.set $y (i32.or (local.get $hi) (call $bswap16 (i32.shr_u (local.get $x) (i32.const 16)))))
)
(local.get $y)
)
)

View File

@ -1 +0,0 @@
--assemble --machine ewasm

View File

@ -1 +0,0 @@
The selected input language is not directly supported when targeting the Ewasm machine and automatic translation is not available.

View File

@ -1,3 +0,0 @@
{
sstore(0, 1)
}

View File

@ -1 +1 @@
--link --asm --asm-json --opcodes --bin --bin-runtime --abi --ir --ir-optimized --ewasm --hashes --userdoc --devdoc --metadata --storage-layout --link --asm --asm-json --opcodes --bin --bin-runtime --abi --ir --ir-optimized --hashes --userdoc --devdoc --metadata --storage-layout

View File

@ -1 +1 @@
The following outputs are not supported in linker mode: --abi, --asm, --asm-json, --bin, --bin-runtime, --devdoc, --ewasm, --hashes, --ir, --ir-optimized, --metadata, --opcodes, --storage-layout, --userdoc. The following outputs are not supported in linker mode: --abi, --asm, --asm-json, --bin, --bin-runtime, --devdoc, --hashes, --ir, --ir-optimized, --metadata, --opcodes, --storage-layout, --userdoc.

View File

@ -1 +0,0 @@
--optimize --ewasm-ir

View File

@ -1 +0,0 @@
The following outputs are not supported in compiler mode: --ewasm-ir.

View File

@ -1,4 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
pragma solidity *;
contract C {}

View File

@ -1 +1 @@
--ast-compact-json --asm --asm-json --opcodes --bin --bin-runtime --abi --ir --ir-optimized --ewasm --ewasm-ir --hashes --userdoc --devdoc --metadata --storage-layout --ast-compact-json --asm --asm-json --opcodes --bin --bin-runtime --abi --ir --ir-optimized --hashes --userdoc --devdoc --metadata --storage-layout

View File

@ -1 +1 @@
The following outputs are not supported in standard JSON mode: --abi, --asm, --asm-json, --ast-compact-json, --bin, --bin-runtime, --devdoc, --ewasm, --ewasm-ir, --hashes, --ir, --ir-optimized, --metadata, --opcodes, --storage-layout, --userdoc. The following outputs are not supported in standard JSON mode: --abi, --asm, --asm-json, --ast-compact-json, --bin, --bin-runtime, --devdoc, --hashes, --ir, --ir-optimized, --metadata, --opcodes, --storage-layout, --userdoc.

View File

@ -1,22 +0,0 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; contract C { }"
}
},
"settings":
{
"optimizer":
{
"enabled": true,
"details": {"yul": true}
},
"outputSelection":
{
"*": { "*": ["ewasm.wast", "ewasm.wasm"] }
}
}
}

View File

@ -1,176 +0,0 @@
{
"contracts":
{
"A":
{
"C":
{
"ewasm":
{
"wasm": "<BYTECODE REMOVED>",
"wast": "(module
;; custom section for sub-module
;; The Keccak-256 hash of the text representation of <REMOVED>
;; (@custom \"C_3_deployed\" \"<BYTECODE REMOVED>\")
(import \"ethereum\" \"codeCopy\" (func $eth.codeCopy (param i32 i32 i32)))
(import \"ethereum\" \"revert\" (func $eth.revert (param i32 i32)))
(import \"ethereum\" \"getCallValue\" (func $eth.getCallValue (param i32)))
(import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32)))
(memory $memory (export \"memory\") 1)
(export \"main\" (func $main))
(func $main
(local $p i32)
(local $r i32)
(local $hi i64)
(local $y i64)
(local $z3 i64)
(local $_1 i64)
(block $label_
(local.set $p (call $u256_to_i32_716))
(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 $hi (i64.shl (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.const 0)))) (i64.const 32)))
(local.set $y (i64.or (local.get $hi) (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.shr_u (i64.const 0) (i64.const 32)))))))
(i64.store (local.get $r) (local.get $y))
(i64.store (i32.add (local.get $r) (i32.const 8)) (local.get $y))
(i64.store (i32.add (local.get $r) (i32.const 16)) (local.get $y))
(i64.store (i32.add (local.get $r) (i32.const 24)) (call $bswap64))
(call $eth.getCallValue (i32.const 0))
(local.set $z3 (i64.load (i32.const 8)))
(if (i32.eqz (i64.eqz (i64.or (i64.or (i64.const 0) (i64.const 0)) (i64.or (local.get $z3) (i64.load (i32.const 0)))))) (then
(call $eth.revert (call $to_internal_i32ptr_334) (call $u256_to_i32_333))))
(local.set $_1 (datasize \"C_3_deployed\"))
(call $eth.codeCopy (call $to_internal_i32ptr) (call $u256_to_i32 (dataoffset \"C_3_deployed\")) (call $u256_to_i32 (local.get $_1)))
(call $eth.finish (call $to_internal_i32ptr) (call $u256_to_i32 (local.get $_1)))
)
)
(func $u256_to_i32_333
(result i32)
(local $v i32)
(local $_1 i64)
(block $label__1
(local.set $_1 (i64.const 0))
(if (i64.ne (local.get $_1) (i64.or (i64.or (local.get $_1) (local.get $_1)) (local.get $_1))) (then
(unreachable)))
(if (i64.ne (local.get $_1) (i64.shr_u (local.get $_1) (i64.const 32))) (then
(unreachable)))
(local.set $v (i32.wrap_i64 (local.get $_1)))
)
(local.get $v)
)
(func $u256_to_i32
(param $x4 i64)
(result i32)
(local $v i32)
(block $label__2
(if (i64.ne (i64.const 0) (i64.or (i64.or (i64.const 0) (i64.const 0)) (i64.const 0))) (then
(unreachable)))
(if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then
(unreachable)))
(local.set $v (i32.wrap_i64 (local.get $x4)))
)
(local.get $v)
)
(func $u256_to_i32_716
(result i32)
(local $v i32)
(block $label__3
(if (i64.ne (i64.const 0) (i64.or (i64.or (i64.const 0) (i64.const 0)) (i64.const 0))) (then
(unreachable)))
(if (i64.ne (i64.const 0) (i64.shr_u (i64.const 64) (i64.const 32))) (then
(unreachable)))
(local.set $v (i32.wrap_i64 (i64.const 64)))
)
(local.get $v)
)
(func $to_internal_i32ptr_334
(result i32)
(local $r i32)
(local $p i32)
(block $label__4
(local.set $p (call $u256_to_i32_333))
(local.set $r (i32.add (local.get $p) (i32.const 64)))
(if (i32.lt_u (local.get $r) (local.get $p)) (then
(unreachable)))
)
(local.get $r)
)
(func $to_internal_i32ptr
(result i32)
(local $r i32)
(local $v i32)
(block $label__5
(if (i64.ne (i64.const 0) (i64.or (i64.or (i64.const 0) (i64.const 0)) (i64.const 0))) (then
(unreachable)))
(if (i64.ne (i64.const 0) (i64.shr_u (i64.const 128) (i64.const 32))) (then
(unreachable)))
(local.set $v (i32.wrap_i64 (i64.const 128)))
(local.set $r (i32.add (local.get $v) (i32.const 64)))
(if (i32.lt_u (local.get $r) (local.get $v)) (then
(unreachable)))
)
(local.get $r)
)
(func $bswap16
(param $x i32)
(result i32)
(local $y i32)
(block $label__6
(local.set $y (i32.or (i32.and (i32.shl (local.get $x) (i32.const 8)) (i32.const 65280)) (i32.and (i32.shr_u (local.get $x) (i32.const 8)) (i32.const 255))))
)
(local.get $y)
)
(func $bswap32
(param $x i32)
(result i32)
(local $y i32)
(local $hi i32)
(block $label__7
(local.set $hi (i32.shl (call $bswap16 (local.get $x)) (i32.const 16)))
(local.set $y (i32.or (local.get $hi) (call $bswap16 (i32.shr_u (local.get $x) (i32.const 16)))))
)
(local.get $y)
)
(func $bswap64
(result i64)
(local $y i64)
(local $hi i64)
(block $label__8
(local.set $hi (i64.shl (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.const 128)))) (i64.const 32)))
(local.set $y (i64.or (local.get $hi) (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.shr_u (i64.const 128) (i64.const 32)))))))
)
(local.get $y)
)
)
"
}
}
}
},
"sources":
{
"A":
{
"id": 0
}
}
}

View File

@ -1,22 +0,0 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; abstract contract C { }"
}
},
"settings":
{
"optimizer":
{
"enabled": true,
"details": {"yul": true}
},
"outputSelection":
{
"*": { "*": ["ewasm.wast", "ewasm.wasm"] }
}
}
}

View File

@ -1,23 +0,0 @@
{
"contracts":
{
"A":
{
"C":
{
"ewasm":
{
"wasm": "",
"wast": ""
}
}
}
},
"sources":
{
"A":
{
"id": 0
}
}
}

View File

@ -1 +1 @@
--yul-dialect evm --machine ewasm --yul-dialect evm --machine evm

View File

@ -1 +0,0 @@
--strict-assembly --optimize --ewasm-ir

View File

@ -1,4 +0,0 @@
{
let x := 42
sstore(0, x)
}

View File

@ -1,2 +0,0 @@
======= strict_asm_output_selection_ewasm_ir_only/input.yul (EVM) =======

View File

@ -1 +0,0 @@
--strict-assembly --optimize --ewasm

View File

@ -1,4 +0,0 @@
{
let x := 42
sstore(0, x)
}

View File

@ -1,2 +0,0 @@
======= strict_asm_output_selection_ewasm_only/input.yul (EVM) =======

View File

@ -1 +1 @@
--strict-assembly --asm --asm-json --opcodes --bin --bin-runtime --abi --ir --ir-optimized --ewasm --hashes --userdoc --devdoc --metadata --storage-layout --strict-assembly --asm --asm-json --opcodes --bin --bin-runtime --abi --ir --ir-optimized --hashes --userdoc --devdoc --metadata --storage-layout

View File

@ -1 +0,0 @@
--yul --yul-dialect ewasm --machine ewasm

View File

@ -1,17 +0,0 @@
object "object" {
code {
function main()
{
let m:i64, n:i32, p:i32, q:i64 := multireturn(1:i32, 2:i64, 3:i64, 4:i32)
}
function multireturn(a:i32, b:i64, c:i64, d:i32) -> x:i64, y:i32, z:i32, w:i64
{
x := b
w := c
y := a
z := d
}
}
}

View File

@ -1,73 +0,0 @@
======= wasm_to_wasm_function_returning_multiple_values/input.yul (Ewasm) =======
Pretty printed source:
object "object" {
code {
function main()
{
let m, n:i32, p:i32, q := multireturn(1:i32, 2, 3, 4:i32)
}
function multireturn(a:i32, b, c, d:i32) -> x, y:i32, z:i32, w
{
x := b
w := c
y := a
z := d
}
}
}
Binary representation:
0061736d01000000010c0260000060047f7e7e7f017e020100030302000105030100010610037f0141000b7f0141000b7e0142000b071102066d656d6f72790200046d61696e00000a52022603017e027f017e024002404101420242034104100121002300210123012102230221030b0b0b2903017e027f017e0240200121042002210720002105200321060b20052400200624012007240220040b
Text representation:
(module
(memory $memory (export "memory") 1)
(export "main" (func $main))
(global $global_ (mut i32) (i32.const 0))
(global $global__1 (mut i32) (i32.const 0))
(global $global__2 (mut i64) (i64.const 0))
(func $main
(local $m i64)
(local $n i32)
(local $p i32)
(local $q i64)
(block $label_
(block
(local.set $m (call $multireturn (i32.const 1) (i64.const 2) (i64.const 3) (i32.const 4)))
(local.set $n (global.get $global_))
(local.set $p (global.get $global__1))
(local.set $q (global.get $global__2))
)
)
)
(func $multireturn
(param $a i32)
(param $b i64)
(param $c i64)
(param $d i32)
(result i64)
(local $x i64)
(local $y i32)
(local $z i32)
(local $w i64)
(block $label__3
(local.set $x (local.get $b))
(local.set $w (local.get $c))
(local.set $y (local.get $a))
(local.set $z (local.get $d))
)
(global.set $global_ (local.get $y))
(global.set $global__1 (local.get $z))
(global.set $global__2 (local.get $w))
(local.get $x)
)
)

View File

@ -1 +0,0 @@
--yul --yul-dialect ewasm --machine ewasm

View File

@ -1,8 +0,0 @@
object "object" {
code {
function main()
{
i64.store8(0x01:i32, 42:i64)
}
}
}

View File

@ -1,27 +0,0 @@
======= wasm_to_wasm_memory_instructions_alignment/input.yul (Ewasm) =======
Pretty printed source:
object "object" {
code {
function main()
{ i64.store8(0x01:i32, 42) }
}
}
Binary representation:
0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a0e010c0002404101422a3c00000b0b
Text representation:
(module
(memory $memory (export "memory") 1)
(export "main" (func $main))
(func $main
(block $label_
(i64.store8 (i32.const 1) (i64.const 42))
)
)
)

View File

@ -1 +0,0 @@
--strict-assembly --yul-dialect evm --machine ewasm --optimize --ewasm-ir

View File

@ -1,4 +0,0 @@
/// @use-src 0:"test.sol"
object "C" {
code { sstore(0,0) }
}

View File

@ -1,34 +0,0 @@
======= yul_to_wasm_source_location_crash/input.yul (Ewasm) =======
==========================
Translated source:
/// @use-src 0:"test.sol"
object "C" {
code {
function main()
{
let hi := i64.shl(i64.extend_i32_u(bswap32(i32.wrap_i64(0))), 32)
let y := i64.or(hi, i64.extend_i32_u(bswap32(i32.wrap_i64(i64.shr_u(0, 32)))))
i64.store(0:i32, y)
i64.store(i32.add(0:i32, 8:i32), y)
i64.store(i32.add(0:i32, 16:i32), y)
i64.store(i32.add(0:i32, 24:i32), y)
i64.store(32:i32, y)
i64.store(i32.add(32:i32, 8:i32), y)
i64.store(i32.add(32:i32, 16:i32), y)
i64.store(i32.add(32:i32, 24:i32), y)
eth.storageStore(0:i32, 32:i32)
}
function bswap16(x:i32) -> y:i32
{
y := i32.or(i32.and(i32.shl(x, 8:i32), 0xff00:i32), i32.and(i32.shr_u(x, 8:i32), 0xff:i32))
}
function bswap32(x:i32) -> y:i32
{
let hi:i32 := i32.shl(bswap16(x), 16:i32)
y := i32.or(hi, bswap16(i32.shr_u(x, 16:i32)))
}
}
}

View File

@ -2,5 +2,4 @@ abstract contract C {
constructor() { constructor() {
} }
} }
// ---- // ----

View File

@ -7,5 +7,4 @@ contract C {
m[c] = payable(0); m[c] = payable(0);
} }
} }
// ---- // ----

Some files were not shown because too many files have changed in this diff Show More