Yul Backend: Get rid of heuristics for finding the matching runtime

This commit is contained in:
Mathias Baumann 2021-02-03 11:47:16 +01:00
parent d393624384
commit e4f1257c83
13 changed files with 51 additions and 21 deletions

View File

@ -44,6 +44,8 @@ using AssemblyPointer = std::shared_ptr<Assembly>;
class Assembly class Assembly
{ {
public: public:
explicit Assembly(std::string _name = std::string()):m_name(std::move(_name)) { }
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); } AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
/// Returns a tag identified by the given name. Creates it if it does not yet exist. /// Returns a tag identified by the given name. Creates it if it does not yet exist.
@ -95,6 +97,7 @@ public:
int deposit() const { return m_deposit; } int deposit() const { return m_deposit; }
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); } void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
void setDeposit(int _deposit) { m_deposit = _deposit; assertThrow(m_deposit >= 0, InvalidDeposit, ""); } void setDeposit(int _deposit) { m_deposit = _deposit; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
std::string const& name() const { return m_name; }
/// Changes the source location used for each appended item. /// Changes the source location used for each appended item.
void setSourceLocation(langutil::SourceLocation const& _location) { m_currentSourceLocation = _location; } void setSourceLocation(langutil::SourceLocation const& _location) { m_currentSourceLocation = _location; }
@ -193,6 +196,9 @@ protected:
mutable std::vector<size_t> m_tagPositionsInBytecode; mutable std::vector<size_t> m_tagPositionsInBytecode;
int m_deposit = 0; int m_deposit = 0;
/// Internal name of the assembly object, only used with the Yul backend
/// currently
std::string m_name;
langutil::SourceLocation m_currentSourceLocation; langutil::SourceLocation m_currentSourceLocation;
public: public:

View File

@ -1302,7 +1302,7 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
// TODO: use stack.assemble here! // TODO: use stack.assemble here!
yul::MachineAssemblyObject init; yul::MachineAssemblyObject init;
yul::MachineAssemblyObject runtime; yul::MachineAssemblyObject runtime;
std::tie(init, runtime) = stack.assembleAndGuessRuntime(); std::tie(init, runtime) = stack.assembleWithDeployed(IRNames::runtimeObject(_contract));
compiledContract.object = std::move(*init.bytecode); compiledContract.object = std::move(*init.bytecode);
compiledContract.runtimeObject = std::move(*runtime.bytecode); compiledContract.runtimeObject = std::move(*runtime.bytecode);
// TODO: refactor assemblyItems, runtimeAssemblyItems, generatedSources, // TODO: refactor assemblyItems, runtimeAssemblyItems, generatedSources,

View File

@ -1273,7 +1273,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
MachineAssemblyObject object; MachineAssemblyObject object;
MachineAssemblyObject runtimeObject; MachineAssemblyObject runtimeObject;
tie(object, runtimeObject) = stack.assembleAndGuessRuntime(); tie(object, runtimeObject) = stack.assembleWithDeployed();
if (object.bytecode) if (object.bytecode)
object.bytecode->link(_inputsAndSettings.libraries); object.bytecode->link(_inputsAndSettings.libraries);

View File

@ -199,7 +199,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
switch (_machine) switch (_machine)
{ {
case Machine::EVM: case Machine::EVM:
return assembleAndGuessRuntime().first; return assembleWithDeployed().first;
case Machine::EVM15: case Machine::EVM15:
{ {
MachineAssemblyObject object; MachineAssemblyObject object;
@ -226,7 +226,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
return MachineAssemblyObject(); return MachineAssemblyObject();
} }
pair<MachineAssemblyObject, MachineAssemblyObject> AssemblyStack::assembleAndGuessRuntime() const std::pair<MachineAssemblyObject, MachineAssemblyObject> AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
{ {
yulAssert(m_analysisSuccessful, ""); yulAssert(m_analysisSuccessful, "");
yulAssert(m_parserResult, ""); yulAssert(m_parserResult, "");
@ -248,22 +248,39 @@ pair<MachineAssemblyObject, MachineAssemblyObject> AssemblyStack::assembleAndGue
) )
); );
MachineAssemblyObject runtimeObject; MachineAssemblyObject deployedObject;
// Heuristic: If there is a single sub-assembly, this is likely the runtime object. optional<size_t> subIndex;
if (assembly.numSubs() == 1)
// Pick matching assembly if name was given
if (_deployName.has_value())
{ {
evmasm::Assembly& runtimeAssembly = assembly.sub(0); for (size_t i = 0; i < assembly.numSubs(); i++)
runtimeObject.bytecode = make_shared<evmasm::LinkerObject>(runtimeAssembly.assemble()); if (assembly.sub(i).name() == _deployName)
runtimeObject.assembly = runtimeAssembly.assemblyString(); {
runtimeObject.sourceMappings = make_unique<string>( subIndex = i;
break;
}
solAssert(subIndex.has_value(), "Failed to find object to be deployed.");
}
// Otherwise use heuristic: If there is a single sub-assembly, this is likely the object to be deployed.
else if (assembly.numSubs() == 1)
subIndex = 0;
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( evmasm::AssemblyItem::computeSourceMapping(
runtimeAssembly.items(), runtimeAssembly.items(),
{{scanner().charStream() ? scanner().charStream()->name() : "", 0}} {{scanner().charStream() ? scanner().charStream()->name() : "", 0}}
) )
); );
} }
return {std::move(creationObject), std::move(runtimeObject)};
return {std::move(creationObject), std::move(deployedObject)};
} }
string AssemblyStack::print() const string AssemblyStack::print() const

View File

@ -95,6 +95,12 @@ public:
/// Only available for EVM. /// Only available for EVM.
std::pair<MachineAssemblyObject, MachineAssemblyObject> assembleAndGuessRuntime() const; 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;
/// @returns the errors generated during parsing, analysis (and potentially assembly). /// @returns the errors generated during parsing, analysis (and potentially assembly).
langutil::ErrorList const& errors() const { return m_errors; } langutil::ErrorList const& errors() const { return m_errors; }

View File

@ -100,7 +100,7 @@ public:
/// Append the assembled size as a constant. /// Append the assembled size as a constant.
virtual void appendAssemblySize() = 0; virtual void appendAssemblySize() = 0;
/// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset. /// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset.
virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() = 0; virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name = "") = 0;
/// Appends the offset of the given sub-assembly or data. /// Appends the offset of the given sub-assembly or data.
virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0; virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0;
/// Appends the size of the given sub-assembly or data. /// Appends the size of the given sub-assembly or data.

View File

@ -140,9 +140,9 @@ void EthAssemblyAdapter::appendAssemblySize()
m_assembly.appendProgramSize(); m_assembly.appendProgramSize();
} }
pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly() pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly(string _name)
{ {
shared_ptr<evmasm::Assembly> assembly{make_shared<evmasm::Assembly>()}; shared_ptr<evmasm::Assembly> assembly{make_shared<evmasm::Assembly>(std::move(_name))};
auto sub = m_assembly.newSub(assembly); auto sub = m_assembly.newSub(assembly);
return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())}; return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())};
} }

View File

@ -57,7 +57,7 @@ public:
void appendJumpsub(LabelID, int, int) override; void appendJumpsub(LabelID, int, int) override;
void appendReturnsub(int, int) override; void appendReturnsub(int, int) override;
void appendAssemblySize() override; void appendAssemblySize() override;
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override; std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name = {}) override;
void appendDataOffset(std::vector<SubID> const& _subPath) override; void appendDataOffset(std::vector<SubID> const& _subPath) override;
void appendDataSize(std::vector<SubID> const& _subPath) override; void appendDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override; SubID appendData(bytes const& _data) override;

View File

@ -196,7 +196,7 @@ void EVMAssembly::appendAssemblySize()
m_bytecode += bytes(assemblySizeReferenceSize); m_bytecode += bytes(assemblySizeReferenceSize);
} }
pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EVMAssembly::createSubAssembly() pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EVMAssembly::createSubAssembly(string)
{ {
yulAssert(false, "Sub assemblies not implemented."); yulAssert(false, "Sub assemblies not implemented.");
return {}; return {};

View File

@ -79,7 +79,7 @@ public:
/// Append the assembled size as a constant. /// Append the assembled size as a constant.
void appendAssemblySize() override; void appendAssemblySize() override;
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override; std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name = "") override;
void appendDataOffset(std::vector<SubID> const& _subPath) override; void appendDataOffset(std::vector<SubID> const& _subPath) override;
void appendDataSize(std::vector<SubID> const& _subPath) override; void appendDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override; SubID appendData(bytes const& _data) override;

View File

@ -42,10 +42,11 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
BuiltinContext context; BuiltinContext context;
context.currentObject = &_object; context.currentObject = &_object;
for (auto const& subNode: _object.subObjects) for (auto const& subNode: _object.subObjects)
if (auto* subObject = dynamic_cast<Object*>(subNode.get())) if (auto* subObject = dynamic_cast<Object*>(subNode.get()))
{ {
auto subAssemblyAndID = m_assembly.createSubAssembly(); auto subAssemblyAndID = m_assembly.createSubAssembly(subObject->name.str());
context.subIDs[subObject->name] = subAssemblyAndID.second; context.subIDs[subObject->name] = subAssemblyAndID.second;
subObject->subId = subAssemblyAndID.second; subObject->subId = subAssemblyAndID.second;
compile(*subObject, *subAssemblyAndID.first, m_dialect, m_evm15, _optimize); compile(*subObject, *subAssemblyAndID.first, m_dialect, m_evm15, _optimize);

View File

@ -127,7 +127,7 @@ void NoOutputAssembly::appendAssemblySize()
appendInstruction(evmasm::Instruction::PUSH1); appendInstruction(evmasm::Instruction::PUSH1);
} }
pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> NoOutputAssembly::createSubAssembly() pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> NoOutputAssembly::createSubAssembly(std::string)
{ {
yulAssert(false, "Sub assemblies not implemented."); yulAssert(false, "Sub assemblies not implemented.");
return {}; return {};

View File

@ -67,7 +67,7 @@ public:
void appendReturnsub(int _returns, int _stackDiffAfter) override; void appendReturnsub(int _returns, int _stackDiffAfter) override;
void appendAssemblySize() override; void appendAssemblySize() override;
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override; std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name = "") override;
void appendDataOffset(std::vector<SubID> const& _subPath) override; void appendDataOffset(std::vector<SubID> const& _subPath) override;
void appendDataSize(std::vector<SubID> const& _subPath) override; void appendDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override; SubID appendData(bytes const& _data) override;