mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Remove EWASM backend.
This commit is contained in:
parent
0a0c389541
commit
c5673278a7
@ -4,6 +4,7 @@ Language Features:
|
|||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
* EWasm: Remove EWasm backend.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -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
|
|
@ -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
|
||||||
|
@ -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": ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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, "");
|
||||||
|
@ -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;
|
||||||
|
@ -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))
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -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");
|
|
||||||
}
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -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");
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
@ -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))
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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, ewasmVmFound};
|
return evmVmFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):
|
EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
|
@ -1 +0,0 @@
|
|||||||
--assemble --optimize --yul-dialect evm --machine ewasm
|
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
sstore(0, 1)
|
|
||||||
}
|
|
@ -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)
|
|
||||||
)
|
|
||||||
|
|
||||||
)
|
|
@ -1 +0,0 @@
|
|||||||
--assemble --optimize --yul-dialect evm --machine ewasm
|
|
@ -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)
|
|
||||||
}
|
|
@ -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))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
)
|
|
@ -1 +0,0 @@
|
|||||||
--assemble --optimize --yul-dialect evm --machine ewasm --asm
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
let x := 42
|
|
||||||
sstore(0, x)
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
======= evm_to_wasm_output_selection_asm_only/input.yul (Ewasm) =======
|
|
@ -1 +0,0 @@
|
|||||||
--assemble --optimize --yul-dialect evm --machine ewasm --ewasm-ir
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
let x := 42
|
|
||||||
sstore(0, x)
|
|
||||||
}
|
|
@ -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)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
--assemble --optimize --yul-dialect evm --machine ewasm --ewasm
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
let x := 42
|
|
||||||
sstore(0, x)
|
|
||||||
}
|
|
@ -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)
|
|
||||||
)
|
|
||||||
|
|
||||||
)
|
|
@ -1 +0,0 @@
|
|||||||
--assemble --machine ewasm
|
|
@ -1 +0,0 @@
|
|||||||
The selected input language is not directly supported when targeting the Ewasm machine and automatic translation is not available.
|
|
@ -1 +0,0 @@
|
|||||||
1
|
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
sstore(0, 1)
|
|
||||||
}
|
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -1 +0,0 @@
|
|||||||
--optimize --ewasm-ir
|
|
@ -1 +0,0 @@
|
|||||||
The following outputs are not supported in compiler mode: --ewasm-ir.
|
|
@ -1 +0,0 @@
|
|||||||
1
|
|
@ -1,4 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
pragma solidity *;
|
|
||||||
|
|
||||||
contract C {}
|
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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"] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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"] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"contracts":
|
|
||||||
{
|
|
||||||
"A":
|
|
||||||
{
|
|
||||||
"C":
|
|
||||||
{
|
|
||||||
"ewasm":
|
|
||||||
{
|
|
||||||
"wasm": "",
|
|
||||||
"wast": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sources":
|
|
||||||
{
|
|
||||||
"A":
|
|
||||||
{
|
|
||||||
"id": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +1 @@
|
|||||||
--yul-dialect evm --machine ewasm
|
--yul-dialect evm --machine evm
|
||||||
|
@ -1 +0,0 @@
|
|||||||
--strict-assembly --optimize --ewasm-ir
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
let x := 42
|
|
||||||
sstore(0, x)
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
======= strict_asm_output_selection_ewasm_ir_only/input.yul (EVM) =======
|
|
@ -1 +0,0 @@
|
|||||||
--strict-assembly --optimize --ewasm
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
let x := 42
|
|
||||||
sstore(0, x)
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
======= strict_asm_output_selection_ewasm_only/input.yul (EVM) =======
|
|
@ -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
|
||||||
|
@ -1 +0,0 @@
|
|||||||
--yul --yul-dialect ewasm --machine ewasm
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
||||||
)
|
|
||||||
|
|
||||||
)
|
|
@ -1 +0,0 @@
|
|||||||
--yul --yul-dialect ewasm --machine ewasm
|
|
@ -1,8 +0,0 @@
|
|||||||
object "object" {
|
|
||||||
code {
|
|
||||||
function main()
|
|
||||||
{
|
|
||||||
i64.store8(0x01:i32, 42:i64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
)
|
|
@ -1 +0,0 @@
|
|||||||
--strict-assembly --yul-dialect evm --machine ewasm --optimize --ewasm-ir
|
|
@ -1,4 +0,0 @@
|
|||||||
/// @use-src 0:"test.sol"
|
|
||||||
object "C" {
|
|
||||||
code { sstore(0,0) }
|
|
||||||
}
|
|
@ -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)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,5 +2,4 @@ abstract contract C {
|
|||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
|
@ -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
Loading…
Reference in New Issue
Block a user