mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Initial IR generator.
This commit is contained in:
@@ -46,6 +46,8 @@
|
||||
#include <libsolidity/interface/Version.h>
|
||||
#include <libsolidity/parsing/Parser.h>
|
||||
|
||||
#include <libsolidity/codegen/ir/IRGenerator.h>
|
||||
|
||||
#include <libyul/YulString.h>
|
||||
|
||||
#include <liblangutil/Scanner.h>
|
||||
@@ -146,6 +148,7 @@ void CompilerStack::reset(bool _keepSettings)
|
||||
m_remappings.clear();
|
||||
m_libraries.clear();
|
||||
m_evmVersion = langutil::EVMVersion();
|
||||
m_generateIR = false;
|
||||
m_optimiserSettings = OptimiserSettings::minimal();
|
||||
m_metadataLiteralSources = false;
|
||||
}
|
||||
@@ -387,7 +390,11 @@ bool CompilerStack::compile()
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
|
||||
if (isRequestedContract(*contract))
|
||||
{
|
||||
compileContract(*contract, otherCompilers);
|
||||
if (m_generateIR)
|
||||
generateIR(*contract);
|
||||
}
|
||||
m_stackState = CompilationSuccessful;
|
||||
this->link();
|
||||
return true;
|
||||
@@ -496,6 +503,22 @@ std::string const CompilerStack::filesystemFriendlyName(string const& _contractN
|
||||
return matchContract.contract->name();
|
||||
}
|
||||
|
||||
string const& CompilerStack::yulIR(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
return contract(_contractName).yulIR;
|
||||
}
|
||||
|
||||
string const& CompilerStack::yulIROptimized(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
return contract(_contractName).yulIROptimized;
|
||||
}
|
||||
|
||||
eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
@@ -902,6 +925,24 @@ void CompilerStack::compileContract(
|
||||
_otherCompilers[compiledContract.contract] = compiler;
|
||||
}
|
||||
|
||||
void CompilerStack::generateIR(ContractDefinition const& _contract)
|
||||
{
|
||||
solAssert(m_stackState >= AnalysisSuccessful, "");
|
||||
|
||||
if (!_contract.canBeDeployed())
|
||||
return;
|
||||
|
||||
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
||||
if (!compiledContract.yulIR.empty())
|
||||
return;
|
||||
|
||||
for (auto const* dependency: _contract.annotation().contractDependencies)
|
||||
generateIR(*dependency);
|
||||
|
||||
IRGenerator generator(m_evmVersion, m_optimiserSettings);
|
||||
tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract);
|
||||
}
|
||||
|
||||
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
|
||||
{
|
||||
solAssert(m_stackState >= AnalysisSuccessful, "");
|
||||
|
||||
@@ -100,6 +100,7 @@ public:
|
||||
/// and must not emit exceptions.
|
||||
explicit CompilerStack(ReadCallback::Callback const& _readFile = ReadCallback::Callback()):
|
||||
m_readFile(_readFile),
|
||||
m_generateIR(false),
|
||||
m_errorList(),
|
||||
m_errorReporter(m_errorList) {}
|
||||
|
||||
@@ -143,6 +144,9 @@ public:
|
||||
m_requestedContractNames = _contractNames;
|
||||
}
|
||||
|
||||
/// Enable experimental generation of Yul IR code.
|
||||
void enableIRGeneration(bool _enable = true) { m_generateIR = _enable; }
|
||||
|
||||
/// @arg _metadataLiteralSources When true, store sources as literals in the contract metadata.
|
||||
/// Must be set before parsing.
|
||||
void useMetadataLiteralSources(bool _metadataLiteralSources);
|
||||
@@ -202,6 +206,12 @@ public:
|
||||
/// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use
|
||||
std::string const filesystemFriendlyName(std::string const& _contractName) const;
|
||||
|
||||
/// @returns the IR representation of a contract.
|
||||
std::string const& yulIR(std::string const& _contractName) const;
|
||||
|
||||
/// @returns the optimized IR representation of a contract.
|
||||
std::string const& yulIROptimized(std::string const& _contractName) const;
|
||||
|
||||
/// @returns the assembled object for a contract.
|
||||
eth::LinkerObject const& object(std::string const& _contractName) const;
|
||||
|
||||
@@ -273,6 +283,8 @@ private:
|
||||
std::shared_ptr<Compiler> compiler;
|
||||
eth::LinkerObject object; ///< Deployment object (includes the runtime sub-object).
|
||||
eth::LinkerObject runtimeObject; ///< Runtime object.
|
||||
std::string yulIR; ///< Experimental Yul IR code.
|
||||
std::string yulIROptimized; ///< Optimized experimental Yul IR code.
|
||||
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;
|
||||
@@ -299,6 +311,10 @@ private:
|
||||
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>>& _otherCompilers
|
||||
);
|
||||
|
||||
/// Generate Yul IR for a single contract.
|
||||
/// The IR is stored but otherwise unused.
|
||||
void generateIR(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();
|
||||
@@ -351,6 +367,7 @@ private:
|
||||
OptimiserSettings m_optimiserSettings;
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
std::set<std::string> m_requestedContractNames;
|
||||
bool m_generateIR;
|
||||
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,12 +129,18 @@ bool hashMatchesContent(string const& _hash, string const& _content)
|
||||
}
|
||||
}
|
||||
|
||||
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _artifact)
|
||||
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _artifact, bool _wildcardMatchesIR)
|
||||
{
|
||||
for (auto const& artifact: _outputSelection)
|
||||
/// @TODO support sub-matching, e.g "evm" matches "evm.assembly"
|
||||
if (artifact == "*" || artifact == _artifact)
|
||||
if (artifact == _artifact)
|
||||
return true;
|
||||
else if (artifact == "*")
|
||||
{
|
||||
// "ir" and "irOptimized" can only be matched by "*" if activated.
|
||||
if ((_artifact != "ir" && _artifact != "irOptimized") || _wildcardMatchesIR)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -151,7 +157,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 isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, string const& _artifact, bool _wildcardMatchesIR)
|
||||
{
|
||||
if (!_outputSelection.isObject())
|
||||
return false;
|
||||
@@ -168,7 +174,7 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _fil
|
||||
if (
|
||||
_outputSelection[file].isMember(contract) &&
|
||||
_outputSelection[file][contract].isArray() &&
|
||||
isArtifactRequested(_outputSelection[file][contract], _artifact)
|
||||
isArtifactRequested(_outputSelection[file][contract], _artifact, _wildcardMatchesIR)
|
||||
)
|
||||
return true;
|
||||
}
|
||||
@@ -176,10 +182,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 isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, vector<string> const& _artifacts, bool _wildcardMatchesIR)
|
||||
{
|
||||
for (auto const& artifact: _artifacts)
|
||||
if (isArtifactRequested(_outputSelection, _file, _contract, artifact))
|
||||
if (isArtifactRequested(_outputSelection, _file, _contract, artifact, _wildcardMatchesIR))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -193,6 +199,7 @@ bool isBinaryRequested(Json::Value const& _outputSelection)
|
||||
// This does not inculde "evm.methodIdentifiers" on purpose!
|
||||
static vector<string> const outputsThatRequireBinaries{
|
||||
"*",
|
||||
"ir", "irOptimized",
|
||||
"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",
|
||||
@@ -203,11 +210,28 @@ bool isBinaryRequested(Json::Value const& _outputSelection)
|
||||
for (auto const& fileRequests: _outputSelection)
|
||||
for (auto const& requests: fileRequests)
|
||||
for (auto const& output: outputsThatRequireBinaries)
|
||||
if (isArtifactRequested(requests, output))
|
||||
if (isArtifactRequested(requests, output, false))
|
||||
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 (!_outputSelection.isObject())
|
||||
return false;
|
||||
|
||||
for (auto const& fileRequests: _outputSelection)
|
||||
for (auto const& requests: fileRequests)
|
||||
for (auto const& request: requests)
|
||||
if (request == "ir" || request == "irOptimized")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
|
||||
{
|
||||
Json::Value ret(Json::objectValue);
|
||||
@@ -657,6 +681,10 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
compilerStack.useMetadataLiteralSources(_inputsAndSettings.metadataLiteralSources);
|
||||
compilerStack.setRequestedContractNames(requestedContractNames(_inputsAndSettings.outputSelection));
|
||||
|
||||
bool const irRequested = isIRRequested(_inputsAndSettings.outputSelection);
|
||||
|
||||
compilerStack.enableIRGeneration(irRequested);
|
||||
|
||||
Json::Value errors = std::move(_inputsAndSettings.errors);
|
||||
|
||||
bool const binariesRequested = isBinaryRequested(_inputsAndSettings.outputSelection);
|
||||
@@ -767,15 +795,17 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
for (string const& query: compilerStack.unhandledSMTLib2Queries())
|
||||
output["auxiliaryInputRequested"]["smtlib2queries"]["0x" + keccak256(query).hex()] = query;
|
||||
|
||||
bool const wildcardMatchesIR = false;
|
||||
|
||||
output["sources"] = Json::objectValue;
|
||||
unsigned sourceIndex = 0;
|
||||
for (string const& sourceName: analysisSuccess ? compilerStack.sourceNames() : vector<string>())
|
||||
{
|
||||
Json::Value sourceResult = Json::objectValue;
|
||||
sourceResult["id"] = sourceIndex++;
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "ast"))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "ast", wildcardMatchesIR))
|
||||
sourceResult["ast"] = ASTJsonConverter(false, compilerStack.sourceIndices()).toJson(compilerStack.ast(sourceName));
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "legacyAST"))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "legacyAST", wildcardMatchesIR))
|
||||
sourceResult["legacyAST"] = ASTJsonConverter(true, compilerStack.sourceIndices()).toJson(compilerStack.ast(sourceName));
|
||||
output["sources"][sourceName] = sourceResult;
|
||||
}
|
||||
@@ -790,32 +820,38 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
|
||||
// ABI, documentation and metadata
|
||||
Json::Value contractData(Json::objectValue);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi"))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesIR))
|
||||
contractData["abi"] = compilerStack.contractABI(contractName);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata"))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesIR))
|
||||
contractData["metadata"] = compilerStack.metadata(contractName);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc"))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesIR))
|
||||
contractData["userdoc"] = compilerStack.natspecUser(contractName);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "devdoc"))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "devdoc", wildcardMatchesIR))
|
||||
contractData["devdoc"] = compilerStack.natspecDev(contractName);
|
||||
|
||||
// IR
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ir", wildcardMatchesIR))
|
||||
contractData["ir"] = compilerStack.yulIR(contractName);
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimized", wildcardMatchesIR))
|
||||
contractData["irOptimized"] = compilerStack.yulIROptimized(contractName);
|
||||
|
||||
// EVM
|
||||
Json::Value evmData(Json::objectValue);
|
||||
// @TODO: add ir
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly"))
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly", wildcardMatchesIR))
|
||||
evmData["assembly"] = compilerStack.assemblyString(contractName, sourceList);
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.legacyAssembly"))
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.legacyAssembly", wildcardMatchesIR))
|
||||
evmData["legacyAssembly"] = compilerStack.assemblyJSON(contractName, sourceList);
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.methodIdentifiers"))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.methodIdentifiers", wildcardMatchesIR))
|
||||
evmData["methodIdentifiers"] = compilerStack.methodIdentifiers(contractName);
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.gasEstimates"))
|
||||
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.gasEstimates", wildcardMatchesIR))
|
||||
evmData["gasEstimates"] = compilerStack.gasEstimates(contractName);
|
||||
|
||||
if (compilationSuccess && isArtifactRequested(
|
||||
_inputsAndSettings.outputSelection,
|
||||
file,
|
||||
name,
|
||||
{ "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" }
|
||||
{ "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" },
|
||||
wildcardMatchesIR
|
||||
))
|
||||
evmData["bytecode"] = collectEVMObject(
|
||||
compilerStack.object(contractName),
|
||||
@@ -826,7 +862,8 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
_inputsAndSettings.outputSelection,
|
||||
file,
|
||||
name,
|
||||
{ "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences" }
|
||||
{ "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences" },
|
||||
wildcardMatchesIR
|
||||
))
|
||||
evmData["deployedBytecode"] = collectEVMObject(
|
||||
compilerStack.runtimeObject(contractName),
|
||||
@@ -900,7 +937,8 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
|
||||
|
||||
string contractName = stack.parserResult()->name.str();
|
||||
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ir"))
|
||||
bool const wildcardMatchesIR = true;
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ir", wildcardMatchesIR))
|
||||
output["contracts"][sourceName][contractName]["ir"] = stack.print();
|
||||
|
||||
stack.optimize();
|
||||
@@ -911,13 +949,14 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
|
||||
_inputsAndSettings.outputSelection,
|
||||
sourceName,
|
||||
contractName,
|
||||
{ "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" }
|
||||
{ "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" },
|
||||
wildcardMatchesIR
|
||||
))
|
||||
output["contracts"][sourceName][contractName]["evm"]["bytecode"] = collectEVMObject(*object.bytecode, nullptr);
|
||||
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized"))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesIR))
|
||||
output["contracts"][sourceName][contractName]["irOptimized"] = stack.print();
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "evm.assembly"))
|
||||
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "evm.assembly", wildcardMatchesIR))
|
||||
output["contracts"][sourceName][contractName]["evm"]["assembly"] = object.assembly;
|
||||
|
||||
return output;
|
||||
|
||||
Reference in New Issue
Block a user