mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10866 from ethereum/ir-stack
Expose libevmasm Assembly in libyul for CompilerStack
This commit is contained in:
commit
a695089fec
@ -56,6 +56,7 @@
|
||||
#include <libsolidity/interface/Version.h>
|
||||
#include <libsolidity/parsing/Parser.h>
|
||||
|
||||
#include <libsolidity/codegen/ir/Common.h>
|
||||
#include <libsolidity/codegen/ir/IRGenerator.h>
|
||||
|
||||
#include <libyul/YulString.h>
|
||||
@ -1227,6 +1228,59 @@ bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& featu
|
||||
}
|
||||
}
|
||||
|
||||
void CompilerStack::assemble(
|
||||
ContractDefinition const& _contract,
|
||||
std::shared_ptr<evmasm::Assembly> _assembly,
|
||||
std::shared_ptr<evmasm::Assembly> _runtimeAssembly
|
||||
)
|
||||
{
|
||||
solAssert(m_stackState >= AnalysisPerformed, "");
|
||||
solAssert(!m_hasError, "");
|
||||
|
||||
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
||||
|
||||
compiledContract.evmAssembly = _assembly;
|
||||
solAssert(compiledContract.evmAssembly, "");
|
||||
try
|
||||
{
|
||||
// Assemble deployment (incl. runtime) object.
|
||||
compiledContract.object = compiledContract.evmAssembly->assemble();
|
||||
}
|
||||
catch (evmasm::AssemblyException const&)
|
||||
{
|
||||
solAssert(false, "Assembly exception for bytecode");
|
||||
}
|
||||
solAssert(compiledContract.object.immutableReferences.empty(), "Leftover immutables.");
|
||||
|
||||
compiledContract.evmRuntimeAssembly = _runtimeAssembly;
|
||||
solAssert(compiledContract.evmRuntimeAssembly, "");
|
||||
try
|
||||
{
|
||||
// Assemble runtime object.
|
||||
compiledContract.runtimeObject = compiledContract.evmRuntimeAssembly->assemble();
|
||||
}
|
||||
catch (evmasm::AssemblyException const&)
|
||||
{
|
||||
solAssert(false, "Assembly exception for deployed bytecode");
|
||||
}
|
||||
|
||||
// Throw a warning if EIP-170 limits are exceeded:
|
||||
// If contract creation returns data with length greater than 0x6000 (214 + 213) bytes,
|
||||
// contract creation fails with an out of gas error.
|
||||
if (
|
||||
m_evmVersion >= langutil::EVMVersion::spuriousDragon() &&
|
||||
compiledContract.runtimeObject.bytecode.size() > 0x6000
|
||||
)
|
||||
m_errorReporter.warning(
|
||||
5574_error,
|
||||
_contract.location(),
|
||||
"Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
|
||||
"This contract may not be deployable on mainnet. "
|
||||
"Consider enabling the optimizer (with a low \"runs\" value!), "
|
||||
"turning off revert strings, or using libraries."
|
||||
);
|
||||
}
|
||||
|
||||
void CompilerStack::compileContract(
|
||||
ContractDefinition const& _contract,
|
||||
map<ContractDefinition const*, shared_ptr<Compiler const>>& _otherCompilers
|
||||
@ -1262,48 +1316,9 @@ void CompilerStack::compileContract(
|
||||
solAssert(false, "Optimizer exception during compilation");
|
||||
}
|
||||
|
||||
compiledContract.evmAssembly = compiler->assemblyPtr();
|
||||
solAssert(compiledContract.evmAssembly, "");
|
||||
try
|
||||
{
|
||||
// Assemble deployment (incl. runtime) object.
|
||||
compiledContract.object = compiledContract.evmAssembly->assemble();
|
||||
}
|
||||
catch(evmasm::AssemblyException const&)
|
||||
{
|
||||
solAssert(false, "Assembly exception for bytecode");
|
||||
}
|
||||
solAssert(compiledContract.object.immutableReferences.empty(), "Leftover immutables.");
|
||||
|
||||
compiledContract.evmRuntimeAssembly = compiler->runtimeAssemblyPtr();
|
||||
solAssert(compiledContract.evmRuntimeAssembly, "");
|
||||
try
|
||||
{
|
||||
// Assemble runtime object.
|
||||
compiledContract.runtimeObject = compiledContract.evmRuntimeAssembly->assemble();
|
||||
}
|
||||
catch(evmasm::AssemblyException const&)
|
||||
{
|
||||
solAssert(false, "Assembly exception for deployed bytecode");
|
||||
}
|
||||
|
||||
// Throw a warning if EIP-170 limits are exceeded:
|
||||
// If contract creation returns data with length greater than 0x6000 (214 + 213) bytes,
|
||||
// contract creation fails with an out of gas error.
|
||||
if (
|
||||
m_evmVersion >= langutil::EVMVersion::spuriousDragon() &&
|
||||
compiledContract.runtimeObject.bytecode.size() > 0x6000
|
||||
)
|
||||
m_errorReporter.warning(
|
||||
5574_error,
|
||||
_contract.location(),
|
||||
"Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
|
||||
"This contract may not be deployable on mainnet. "
|
||||
"Consider enabling the optimizer (with a low \"runs\" value!), "
|
||||
"turning off revert strings, or using libraries."
|
||||
);
|
||||
|
||||
_otherCompilers[compiledContract.contract] = compiler;
|
||||
|
||||
assemble(_contract, compiler->assemblyPtr(), compiler->runtimeAssemblyPtr());
|
||||
}
|
||||
|
||||
void CompilerStack::generateIR(ContractDefinition const& _contract)
|
||||
@ -1361,30 +1376,11 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
|
||||
//cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl;
|
||||
|
||||
// TODO: support passing metadata
|
||||
// TODO: use stack.assemble here!
|
||||
yul::MachineAssemblyObject init;
|
||||
yul::MachineAssemblyObject runtime;
|
||||
std::tie(init, runtime) = stack.assembleWithDeployed(IRNames::deployedObject(_contract));
|
||||
compiledContract.object = std::move(*init.bytecode);
|
||||
compiledContract.runtimeObject = std::move(*runtime.bytecode);
|
||||
// TODO: refactor assemblyItems, runtimeAssemblyItems, generatedSources,
|
||||
// assemblyString, assemblyJSON, and functionEntryPoints to work with this code path
|
||||
|
||||
// Throw a warning if EIP-170 limits are exceeded:
|
||||
// If contract creation returns data with length greater than 0x6000 (214 + 213) bytes,
|
||||
// contract creation fails with an out of gas error.
|
||||
if (
|
||||
m_evmVersion >= langutil::EVMVersion::spuriousDragon() &&
|
||||
compiledContract.runtimeObject.bytecode.size() > 0x6000
|
||||
)
|
||||
m_errorReporter.warning(
|
||||
9609_error,
|
||||
_contract.location(),
|
||||
"Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
|
||||
"This contract may not be deployable on mainnet. "
|
||||
"Consider enabling the optimizer (with a low \"runs\" value!), "
|
||||
"turning off revert strings, or using libraries."
|
||||
);
|
||||
string deployedName = IRNames::deployedObject(_contract);
|
||||
solAssert(!deployedName.empty(), "");
|
||||
tie(compiledContract.evmAssembly, compiledContract.evmRuntimeAssembly) = stack.assembleEVMWithDeployed(deployedName);
|
||||
assemble(_contract, compiledContract.evmAssembly, compiledContract.evmRuntimeAssembly);
|
||||
}
|
||||
|
||||
void CompilerStack::generateEwasm(ContractDefinition const& _contract)
|
||||
|
@ -403,6 +403,14 @@ private:
|
||||
/// @returns true if the contract is requested to be compiled.
|
||||
bool isRequestedContract(ContractDefinition const& _contract) const;
|
||||
|
||||
/// Assembles the contract.
|
||||
/// This function should only be internally called by compileContract and generateEVMFromIR.
|
||||
void assemble(
|
||||
ContractDefinition const& _contract,
|
||||
std::shared_ptr<evmasm::Assembly> _assembly,
|
||||
std::shared_ptr<evmasm::Assembly> _runtimeAssembly
|
||||
);
|
||||
|
||||
/// Compile a single contract.
|
||||
/// @param _otherCompilers provides access to compilers of other contracts, to get
|
||||
/// their bytecode if needed. Only filled after they have been compiled.
|
||||
|
@ -216,7 +216,41 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
|
||||
return MachineAssemblyObject();
|
||||
}
|
||||
|
||||
std::pair<MachineAssemblyObject, MachineAssemblyObject> AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
|
||||
std::pair<MachineAssemblyObject, MachineAssemblyObject>
|
||||
AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
|
||||
{
|
||||
auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName);
|
||||
yulAssert(creationAssembly, "");
|
||||
|
||||
MachineAssemblyObject creationObject;
|
||||
creationObject.bytecode = make_shared<evmasm::LinkerObject>(creationAssembly->assemble());
|
||||
yulAssert(creationObject.bytecode->immutableReferences.empty(), "Leftover immutables.");
|
||||
creationObject.assembly = creationAssembly->assemblyString();
|
||||
creationObject.sourceMappings = make_unique<string>(
|
||||
evmasm::AssemblyItem::computeSourceMapping(
|
||||
creationAssembly->items(),
|
||||
{{scanner().charStream() ? scanner().charStream()->name() : "", 0}}
|
||||
)
|
||||
);
|
||||
|
||||
MachineAssemblyObject deployedObject;
|
||||
if (deployedAssembly)
|
||||
{
|
||||
deployedObject.bytecode = make_shared<evmasm::LinkerObject>(deployedAssembly->assemble());
|
||||
deployedObject.assembly = deployedAssembly->assemblyString();
|
||||
deployedObject.sourceMappings = make_unique<string>(
|
||||
evmasm::AssemblyItem::computeSourceMapping(
|
||||
deployedAssembly->items(),
|
||||
{{scanner().charStream() ? scanner().charStream()->name() : "", 0}}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return {std::move(creationObject), std::move(deployedObject)};
|
||||
}
|
||||
|
||||
std::pair<std::shared_ptr<evmasm::Assembly>, std::shared_ptr<evmasm::Assembly>>
|
||||
AssemblyStack::assembleEVMWithDeployed(optional<string_view> _deployName) const
|
||||
{
|
||||
yulAssert(m_analysisSuccessful, "");
|
||||
yulAssert(m_parserResult, "");
|
||||
@ -227,18 +261,6 @@ std::pair<MachineAssemblyObject, MachineAssemblyObject> AssemblyStack::assembleW
|
||||
EthAssemblyAdapter adapter(assembly);
|
||||
compileEVM(adapter, m_optimiserSettings.optimizeStackAllocation);
|
||||
|
||||
MachineAssemblyObject creationObject;
|
||||
creationObject.bytecode = make_shared<evmasm::LinkerObject>(assembly.assemble());
|
||||
yulAssert(creationObject.bytecode->immutableReferences.empty(), "Leftover immutables.");
|
||||
creationObject.assembly = assembly.assemblyString();
|
||||
creationObject.sourceMappings = make_unique<string>(
|
||||
evmasm::AssemblyItem::computeSourceMapping(
|
||||
assembly.items(),
|
||||
{{scanner().charStream() ? scanner().charStream()->name() : "", 0}}
|
||||
)
|
||||
);
|
||||
|
||||
MachineAssemblyObject deployedObject;
|
||||
optional<size_t> subIndex;
|
||||
|
||||
// Pick matching assembly if name was given
|
||||
@ -260,17 +282,10 @@ std::pair<MachineAssemblyObject, MachineAssemblyObject> AssemblyStack::assembleW
|
||||
if (subIndex.has_value())
|
||||
{
|
||||
evmasm::Assembly& runtimeAssembly = assembly.sub(*subIndex);
|
||||
deployedObject.bytecode = make_shared<evmasm::LinkerObject>(runtimeAssembly.assemble());
|
||||
deployedObject.assembly = runtimeAssembly.assemblyString();
|
||||
deployedObject.sourceMappings = make_unique<string>(
|
||||
evmasm::AssemblyItem::computeSourceMapping(
|
||||
runtimeAssembly.items(),
|
||||
{{scanner().charStream() ? scanner().charStream()->name() : "", 0}}
|
||||
)
|
||||
);
|
||||
return {make_shared<evmasm::Assembly>(assembly), make_shared<evmasm::Assembly>(runtimeAssembly)};
|
||||
}
|
||||
|
||||
return {std::move(creationObject), std::move(deployedObject)};
|
||||
return {make_shared<evmasm::Assembly>(assembly), {}};
|
||||
}
|
||||
|
||||
string AssemblyStack::print() const
|
||||
|
@ -35,6 +35,11 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace solidity::evmasm
|
||||
{
|
||||
class Assembly;
|
||||
}
|
||||
|
||||
namespace solidity::langutil
|
||||
{
|
||||
class Scanner;
|
||||
@ -89,17 +94,22 @@ public:
|
||||
/// Run the assembly step (should only be called after parseAndAnalyze).
|
||||
MachineAssemblyObject assemble(Machine _machine) const;
|
||||
|
||||
/// Run the assembly step (should only be called after parseAndAnalyze).
|
||||
/// In addition to the value returned by @a assemble, returns
|
||||
/// a second object that is guessed to be the runtime code.
|
||||
/// Only available for EVM.
|
||||
std::pair<MachineAssemblyObject, MachineAssemblyObject> assembleAndGuessRuntime() const;
|
||||
|
||||
/// Run the assembly step (should only be called after parseAndAnalyze).
|
||||
/// In addition to the value returned by @a assemble, returns
|
||||
/// a second object that is the runtime code.
|
||||
/// Only available for EVM.
|
||||
std::pair<MachineAssemblyObject, MachineAssemblyObject> assembleWithDeployed(std::optional<std::string_view> _deployeName = {}) const;
|
||||
std::pair<MachineAssemblyObject, MachineAssemblyObject>
|
||||
assembleWithDeployed(
|
||||
std::optional<std::string_view> _deployName = {}
|
||||
) const;
|
||||
|
||||
/// Run the assembly step (should only be called after parseAndAnalyze).
|
||||
/// Similar to @a assemblyWithDeployed, but returns EVM assembly objects.
|
||||
/// Only available for EVM.
|
||||
std::pair<std::shared_ptr<evmasm::Assembly>, std::shared_ptr<evmasm::Assembly>>
|
||||
assembleEVMWithDeployed(
|
||||
std::optional<std::string_view> _deployName = {}
|
||||
) const;
|
||||
|
||||
/// @returns the errors generated during parsing, analysis (and potentially assembly).
|
||||
langutil::ErrorList const& errors() const { return m_errors; }
|
||||
|
Loading…
Reference in New Issue
Block a user