mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6716 from ethereum/wasmTranslation
[Yul] EVM to Wasm translation
This commit is contained in:
@@ -49,6 +49,12 @@
|
||||
#include <libsolidity/codegen/ir/IRGenerator.h>
|
||||
|
||||
#include <libyul/YulString.h>
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/backends/wasm/EVMToEWasmTranslator.h>
|
||||
#include <libyul/backends/wasm/EWasmObjectCompiler.h>
|
||||
#include <libyul/backends/wasm/WasmDialect.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <libyul/AssemblyStack.h>
|
||||
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <liblangutil/SemVerHandler.h>
|
||||
@@ -73,6 +79,7 @@ static int g_compilerStackCounts = 0;
|
||||
CompilerStack::CompilerStack(ReadCallback::Callback const& _readFile):
|
||||
m_readFile{_readFile},
|
||||
m_generateIR{false},
|
||||
m_generateEWasm{false},
|
||||
m_errorList{},
|
||||
m_errorReporter{m_errorList}
|
||||
{
|
||||
@@ -171,6 +178,7 @@ void CompilerStack::reset(bool _keepSettings)
|
||||
m_libraries.clear();
|
||||
m_evmVersion = langutil::EVMVersion();
|
||||
m_generateIR = false;
|
||||
m_generateEWasm = false;
|
||||
m_optimiserSettings = OptimiserSettings::minimal();
|
||||
m_metadataLiteralSources = false;
|
||||
}
|
||||
@@ -413,8 +421,10 @@ bool CompilerStack::compile()
|
||||
if (isRequestedContract(*contract))
|
||||
{
|
||||
compileContract(*contract, otherCompilers);
|
||||
if (m_generateIR)
|
||||
if (m_generateIR || m_generateEWasm)
|
||||
generateIR(*contract);
|
||||
if (m_generateEWasm)
|
||||
generateEWasm(*contract);
|
||||
}
|
||||
m_stackState = CompilationSuccessful;
|
||||
this->link();
|
||||
@@ -540,6 +550,14 @@ string const& CompilerStack::yulIROptimized(string const& _contractName) const
|
||||
return contract(_contractName).yulIROptimized;
|
||||
}
|
||||
|
||||
string const& CompilerStack::eWasm(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
return contract(_contractName).eWasm;
|
||||
}
|
||||
|
||||
eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
@@ -971,6 +989,36 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
|
||||
tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract);
|
||||
}
|
||||
|
||||
void CompilerStack::generateEWasm(ContractDefinition const& _contract)
|
||||
{
|
||||
solAssert(m_stackState >= AnalysisSuccessful, "");
|
||||
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::AssemblyStack evmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
|
||||
evmStack.parseAndAnalyze("", compiledContract.yulIROptimized);
|
||||
|
||||
// Turn into eWasm dialect
|
||||
yul::Object ewasmObject = yul::EVMToEWasmTranslator(
|
||||
yul::EVMDialect::strictAssemblyForEVMObjects(m_evmVersion)
|
||||
).run(*evmStack.parserResult());
|
||||
|
||||
// Re-inject into an assembly stack for the eWasm dialect
|
||||
yul::AssemblyStack ewasmStack(m_evmVersion, yul::AssemblyStack::Language::EWasm, m_optimiserSettings);
|
||||
// TODO this is a hack for now - provide as structured AST!
|
||||
ewasmStack.parseAndAnalyze("", "{}");
|
||||
*ewasmStack.parserResult() = move(ewasmObject);
|
||||
ewasmStack.optimize();
|
||||
|
||||
//cout << yul::AsmPrinter{}(*ewasmStack.parserResult()->code) << endl;
|
||||
|
||||
// Turn into eWasm text representation.
|
||||
compiledContract.eWasm = ewasmStack.assemble(yul::AssemblyStack::Machine::eWasm).assembly;
|
||||
}
|
||||
|
||||
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
|
||||
{
|
||||
solAssert(m_stackState >= AnalysisSuccessful, "");
|
||||
|
||||
@@ -154,6 +154,9 @@ public:
|
||||
/// Enable experimental generation of Yul IR code.
|
||||
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.
|
||||
/// Must be set before parsing.
|
||||
void useMetadataLiteralSources(bool _metadataLiteralSources);
|
||||
@@ -219,6 +222,9 @@ public:
|
||||
/// @returns the optimized IR representation of a contract.
|
||||
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 assembled object for a contract.
|
||||
eth::LinkerObject const& object(std::string const& _contractName) const;
|
||||
|
||||
@@ -296,6 +302,7 @@ private:
|
||||
eth::LinkerObject runtimeObject; ///< Runtime object.
|
||||
std::string yulIR; ///< Experimental Yul IR code.
|
||||
std::string yulIROptimized; ///< Optimized experimental Yul IR code.
|
||||
std::string eWasm; ///< Experimental eWasm code (text representation).
|
||||
mutable std::unique_ptr<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
|
||||
mutable std::unique_ptr<Json::Value const> abi;
|
||||
mutable std::unique_ptr<Json::Value const> userDocumentation;
|
||||
@@ -326,6 +333,9 @@ private:
|
||||
/// The IR is stored but otherwise unused.
|
||||
void generateIR(ContractDefinition const& _contract);
|
||||
|
||||
/// Generate eWasm text representation for a single contract.
|
||||
void generateEWasm(ContractDefinition const& _contract);
|
||||
|
||||
/// Links all the known library addresses in the available objects. Any unknown
|
||||
/// library will still be kept as an unlinked placeholder in the objects.
|
||||
void link();
|
||||
@@ -379,6 +389,7 @@ private:
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
std::set<std::string> m_requestedContractNames;
|
||||
bool m_generateIR;
|
||||
bool m_generateEWasm;
|
||||
std::map<std::string, h160> m_libraries;
|
||||
/// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum
|
||||
/// "context:prefix=target"
|
||||
|
||||
@@ -129,16 +129,17 @@ bool hashMatchesContent(string const& _hash, string const& _content)
|
||||
}
|
||||
}
|
||||
|
||||
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _artifact, bool _wildcardMatchesIR)
|
||||
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _artifact, bool _wildcardMatchesExperimental)
|
||||
{
|
||||
static set<string> experimental{"ir", "irOptimized", "wast", "ewasm", "ewasm.wast"};
|
||||
for (auto const& artifact: _outputSelection)
|
||||
/// @TODO support sub-matching, e.g "evm" matches "evm.assembly"
|
||||
if (artifact == _artifact)
|
||||
return true;
|
||||
else if (artifact == "*")
|
||||
{
|
||||
// "ir" and "irOptimized" can only be matched by "*" if activated.
|
||||
if ((_artifact != "ir" && _artifact != "irOptimized") || _wildcardMatchesIR)
|
||||
// "ir", "irOptimized", "wast" and "ewasm.wast" can only be matched by "*" if activated.
|
||||
if (experimental.count(_artifact) == 0 || _wildcardMatchesExperimental)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -157,7 +158,7 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _art
|
||||
///
|
||||
/// @TODO optimise this. Perhaps flatten the structure upfront.
|
||||
///
|
||||
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, string const& _artifact, bool _wildcardMatchesIR)
|
||||
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, string const& _artifact, bool _wildcardMatchesExperimental)
|
||||
{
|
||||
if (!_outputSelection.isObject())
|
||||
return false;
|
||||
@@ -174,7 +175,7 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _fil
|
||||
if (
|
||||
_outputSelection[file].isMember(contract) &&
|
||||
_outputSelection[file][contract].isArray() &&
|
||||
isArtifactRequested(_outputSelection[file][contract], _artifact, _wildcardMatchesIR)
|
||||
isArtifactRequested(_outputSelection[file][contract], _artifact, _wildcardMatchesExperimental)
|
||||
)
|
||||
return true;
|
||||
}
|
||||
@@ -182,10 +183,10 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _fil
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, vector<string> const& _artifacts, bool _wildcardMatchesIR)
|
||||
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, vector<string> const& _artifacts, bool _wildcardMatchesExperimental)
|
||||
{
|
||||
for (auto const& artifact: _artifacts)
|
||||
if (isArtifactRequested(_outputSelection, _file, _contract, artifact, _wildcardMatchesIR))
|
||||
if (isArtifactRequested(_outputSelection, _file, _contract, artifact, _wildcardMatchesExperimental))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -200,6 +201,7 @@ bool isBinaryRequested(Json::Value const& _outputSelection)
|
||||
static vector<string> const outputsThatRequireBinaries{
|
||||
"*",
|
||||
"ir", "irOptimized",
|
||||
"wast", "wasm", "ewasm.wast", "ewasm.wasm",
|
||||
"evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes",
|
||||
"evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences",
|
||||
"evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap",
|
||||
@@ -215,10 +217,29 @@ bool isBinaryRequested(Json::Value const& _outputSelection)
|
||||
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
|
||||
/// yet match "ir" or "irOptimized"
|
||||
bool isIRRequested(Json::Value const& _outputSelection)
|
||||
{
|
||||
if (isEWasmRequested(_outputSelection))
|
||||
return true;
|
||||
|
||||
if (!_outputSelection.isObject())
|
||||
return false;
|
||||
|
||||
@@ -231,7 +252,6 @@ bool isIRRequested(Json::Value const& _outputSelection)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
|
||||
{
|
||||
Json::Value ret(Json::objectValue);
|
||||
@@ -689,9 +709,9 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
compilerStack.useMetadataLiteralSources(_inputsAndSettings.metadataLiteralSources);
|
||||
compilerStack.setRequestedContractNames(requestedContractNames(_inputsAndSettings.outputSelection));
|
||||
|
||||
bool const irRequested = isIRRequested(_inputsAndSettings.outputSelection);
|
||||
compilerStack.enableIRGeneration(isIRRequested(_inputsAndSettings.outputSelection));
|
||||
|
||||
compilerStack.enableIRGeneration(irRequested);
|
||||
compilerStack.enableEWasmGeneration(isEWasmRequested(_inputsAndSettings.outputSelection));
|
||||
|
||||
Json::Value errors = std::move(_inputsAndSettings.errors);
|
||||
|
||||
@@ -812,7 +832,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
for (string const& query: compilerStack.unhandledSMTLib2Queries())
|
||||
output["auxiliaryInputRequested"]["smtlib2queries"]["0x" + keccak256(query).hex()] = query;
|
||||
|
||||
bool const wildcardMatchesIR = false;
|
||||
bool const wildcardMatchesExperimental = false;
|
||||
|
||||
output["sources"] = Json::objectValue;
|
||||
unsigned sourceIndex = 0;
|
||||
@@ -820,9 +840,9 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
{
|
||||
Json::Value sourceResult = Json::objectValue;
|
||||
sourceResult["id"] = sourceIndex++;
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "ast", wildcardMatchesIR))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "ast", wildcardMatchesExperimental))
|
||||
sourceResult["ast"] = ASTJsonConverter(false, compilerStack.sourceIndices()).toJson(compilerStack.ast(sourceName));
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "legacyAST", wildcardMatchesIR))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "legacyAST", wildcardMatchesExperimental))
|
||||
sourceResult["legacyAST"] = ASTJsonConverter(true, compilerStack.sourceIndices()).toJson(compilerStack.ast(sourceName));
|
||||
output["sources"][sourceName] = sourceResult;
|
||||
}
|
||||
@@ -837,30 +857,34 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
|
||||
// ABI, documentation and metadata
|
||||
Json::Value contractData(Json::objectValue);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesIR))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental))
|
||||
contractData["abi"] = compilerStack.contractABI(contractName);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesIR))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental))
|
||||
contractData["metadata"] = compilerStack.metadata(contractName);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesIR))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental))
|
||||
contractData["userdoc"] = compilerStack.natspecUser(contractName);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "devdoc", wildcardMatchesIR))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "devdoc", wildcardMatchesExperimental))
|
||||
contractData["devdoc"] = compilerStack.natspecDev(contractName);
|
||||
|
||||
// IR
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ir", wildcardMatchesIR))
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ir", wildcardMatchesExperimental))
|
||||
contractData["ir"] = compilerStack.yulIR(contractName);
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimized", wildcardMatchesIR))
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimized", wildcardMatchesExperimental))
|
||||
contractData["irOptimized"] = compilerStack.yulIROptimized(contractName);
|
||||
|
||||
// eWasm
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wast", wildcardMatchesExperimental))
|
||||
contractData["ewasm"]["wast"] = compilerStack.eWasm(contractName);
|
||||
|
||||
// EVM
|
||||
Json::Value evmData(Json::objectValue);
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly", wildcardMatchesIR))
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly", wildcardMatchesExperimental))
|
||||
evmData["assembly"] = compilerStack.assemblyString(contractName, sourceList);
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.legacyAssembly", wildcardMatchesIR))
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.legacyAssembly", wildcardMatchesExperimental))
|
||||
evmData["legacyAssembly"] = compilerStack.assemblyJSON(contractName, sourceList);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.methodIdentifiers", wildcardMatchesIR))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.methodIdentifiers", wildcardMatchesExperimental))
|
||||
evmData["methodIdentifiers"] = compilerStack.methodIdentifiers(contractName);
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.gasEstimates", wildcardMatchesIR))
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.gasEstimates", wildcardMatchesExperimental))
|
||||
evmData["gasEstimates"] = compilerStack.gasEstimates(contractName);
|
||||
|
||||
if (compilationSuccess && isArtifactRequested(
|
||||
@@ -868,7 +892,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
file,
|
||||
name,
|
||||
{ "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" },
|
||||
wildcardMatchesIR
|
||||
wildcardMatchesExperimental
|
||||
))
|
||||
evmData["bytecode"] = collectEVMObject(
|
||||
compilerStack.object(contractName),
|
||||
@@ -880,7 +904,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
file,
|
||||
name,
|
||||
{ "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences" },
|
||||
wildcardMatchesIR
|
||||
wildcardMatchesExperimental
|
||||
))
|
||||
evmData["deployedBytecode"] = collectEVMObject(
|
||||
compilerStack.runtimeObject(contractName),
|
||||
@@ -954,8 +978,8 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
|
||||
|
||||
string contractName = stack.parserResult()->name.str();
|
||||
|
||||
bool const wildcardMatchesIR = true;
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ir", wildcardMatchesIR))
|
||||
bool const wildcardMatchesExperimental = true;
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ir", wildcardMatchesExperimental))
|
||||
output["contracts"][sourceName][contractName]["ir"] = stack.print();
|
||||
|
||||
stack.optimize();
|
||||
@@ -967,13 +991,13 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
|
||||
sourceName,
|
||||
contractName,
|
||||
{ "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" },
|
||||
wildcardMatchesIR
|
||||
wildcardMatchesExperimental
|
||||
))
|
||||
output["contracts"][sourceName][contractName]["evm"]["bytecode"] = collectEVMObject(*object.bytecode, nullptr);
|
||||
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesIR))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental))
|
||||
output["contracts"][sourceName][contractName]["irOptimized"] = stack.print();
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "evm.assembly", wildcardMatchesIR))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "evm.assembly", wildcardMatchesExperimental))
|
||||
output["contracts"][sourceName][contractName]["evm"]["assembly"] = object.assembly;
|
||||
|
||||
return output;
|
||||
|
||||
Reference in New Issue
Block a user