Store whether a subassembly is creation code and optimize accordingly.

This commit is contained in:
Daniel Kirchner 2022-03-04 13:25:47 +01:00
parent 7c91dd05a7
commit d5b2d94cb7
13 changed files with 43 additions and 32 deletions

View File

@ -203,7 +203,7 @@ void Assembly::assemblyStream(
for (size_t i = 0; i < m_subs.size(); ++i)
{
_out << endl << _prefix << "sub_" << i << ": assembly {\n";
m_subs[i]->assemblyStream(_out, _debugInfoSelection, _prefix + " ", _sourceCodes);
m_subs[i].first->assemblyStream(_out, _debugInfoSelection, _prefix + " ", _sourceCodes);
_out << _prefix << "}" << endl;
}
}
@ -352,7 +352,7 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices)
{
std::stringstream hexStr;
hexStr << hex << i;
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices);
data[hexStr.str()] = m_subs[i].first->assemblyJSON(_sourceIndices);
}
}
@ -435,9 +435,8 @@ map<u256, u256> const& Assembly::optimiseInternal(
for (size_t subId = 0; subId < m_subs.size(); ++subId)
{
OptimiserSettings settings = _settings;
// Disable creation mode for sub-assemblies.
settings.isCreation = false;
map<u256, u256> const& subTagReplacements = m_subs[subId]->optimiseInternal(
settings.isCreation = m_subs[subId].second;
map<u256, u256> const& subTagReplacements = m_subs[subId].first->optimiseInternal(
settings,
JumpdestRemover::referencedTags(m_items, subId)
);
@ -582,7 +581,7 @@ LinkerObject const& Assembly::assemble() const
map<u256, pair<string, vector<size_t>>> immutableReferencesBySub;
for (auto const& sub: m_subs)
{
auto const& linkerObject = sub->assemble();
auto const& linkerObject = sub.first->assemble();
if (!linkerObject.immutableReferences.empty())
{
assertThrow(
@ -592,7 +591,7 @@ LinkerObject const& Assembly::assemble() const
);
immutableReferencesBySub = linkerObject.immutableReferences;
}
for (size_t tagPos: sub->m_tagPositionsInBytecode)
for (size_t tagPos: sub.first->m_tagPositionsInBytecode)
if (tagPos != numeric_limits<size_t>::max() && tagPos > subTagSize)
subTagSize = tagPos;
}
@ -626,7 +625,7 @@ LinkerObject const& Assembly::assemble() const
unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + static_cast<unsigned>(m_auxiliaryData.size());
for (auto const& sub: m_subs)
bytesRequiredIncludingData += static_cast<unsigned>(sub->assemble().bytecode.size());
bytesRequiredIncludingData += static_cast<unsigned>(sub.first->assemble().bytecode.size());
unsigned bytesPerDataRef = numberEncodingSize(bytesRequiredIncludingData);
uint8_t dataRefPush = static_cast<uint8_t>(pushInstruction(bytesPerDataRef));
@ -780,7 +779,7 @@ LinkerObject const& Assembly::assemble() const
vector<size_t> const& tagPositions =
subId == numeric_limits<size_t>::max() ?
m_tagPositionsInBytecode :
m_subs[subId]->m_tagPositionsInBytecode;
m_subs[subId].first->m_tagPositionsInBytecode;
assertThrow(tagId < tagPositions.size(), AssemblyException, "Reference to non-existing tag.");
size_t pos = tagPositions[tagId];
assertThrow(pos != numeric_limits<size_t>::max(), AssemblyException, "Reference to tag without position.");
@ -870,7 +869,7 @@ Assembly const* Assembly::subAssemblyById(size_t _subId) const
Assembly const* currentAssembly = this;
for (size_t currentSubId: subIds)
{
currentAssembly = currentAssembly->m_subs.at(currentSubId).get();
currentAssembly = currentAssembly->m_subs.at(currentSubId).first.get();
assertThrow(currentAssembly, AssemblyException, "");
}

View File

@ -56,9 +56,9 @@ public:
AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID);
AssemblyItem newData(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
bytes const& data(util::h256 const& _i) const { return m_data.at(_i); }
AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); }
Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
AssemblyItem newSub(AssemblyPointer const& _sub, bool _creation) { m_subs.emplace_back(_sub, _creation); return AssemblyItem(PushSub, m_subs.size() - 1); }
Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub).first; }
Assembly& sub(size_t _sub) { return *m_subs.at(_sub).first; }
size_t numSubs() const { return m_subs.size(); }
AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
@ -89,7 +89,7 @@ public:
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the pushsub assembly item.
AssemblyItem appendSubroutine(AssemblyPointer const& _assembly) { auto sub = newSub(_assembly); append(newPushSubSize(size_t(sub.data()))); return sub; }
AssemblyItem appendSubroutine(AssemblyPointer const& _assembly, bool _creation) { auto sub = newSub(_assembly, _creation); append(newPushSubSize(size_t(sub.data()))); return sub; }
void pushSubroutineSize(size_t _subRoutine) { append(newPushSubSize(_subRoutine)); }
/// Pushes the offset of the subroutine.
void pushSubroutineOffset(size_t _subRoutine) { append(AssemblyItem(PushSub, _subRoutine)); }
@ -204,7 +204,7 @@ protected:
std::map<util::h256, bytes> m_data;
/// Data that is appended to the very end of the contract.
bytes m_auxiliaryData;
std::vector<std::shared_ptr<Assembly>> m_subs;
std::vector<std::pair<std::shared_ptr<Assembly>, bool>> m_subs;
std::map<util::h256, std::string> m_strings;
std::map<util::h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
std::map<util::h256, std::string> m_immutables; ///< Identifiers of immutables.

View File

@ -74,7 +74,7 @@ public:
m_yulUtilFunctions(m_evmVersion, m_revertStrings, m_yulFunctionCollector)
{
if (m_runtimeContext)
m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm, false).data());
}
langutil::EVMVersion const& evmVersion() const { return m_evmVersion; }
@ -224,7 +224,7 @@ public:
}
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the pushsub assembly item.
evmasm::AssemblyItem addSubroutine(evmasm::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); }
evmasm::AssemblyItem addSubroutine(evmasm::AssemblyPointer const& _assembly, bool _creation) { return m_asm->appendSubroutine(_assembly, _creation); }
/// Pushes the size of the subroutine.
void pushSubroutineSize(size_t _subRoutine) { m_asm->pushSubroutineSize(_subRoutine); }
/// Pushes the offset of the subroutine.

View File

@ -1521,7 +1521,7 @@ void CompilerUtils::copyContractCodeToMemory(ContractDefinition const& contract,
_context.compiledContract(contract) :
_context.compiledContractRuntime(contract);
// pushes size
auto subroutine = _context.addSubroutine(assembly);
auto subroutine = _context.addSubroutine(assembly, _creation);
_context << Instruction::DUP1 << subroutine;
_context << Instruction::DUP4 << Instruction::CODECOPY;
_context << Instruction::ADD;

View File

@ -37,9 +37,13 @@
#include <libyul/optimiser/Suite.h>
#include <libevmasm/Assembly.h>
#include <liblangutil/Scanner.h>
#include <boost/algorithm/string.hpp>
#include <optional>
using namespace std;
using namespace solidity;
using namespace solidity::yul;
@ -194,7 +198,11 @@ void AssemblyStack::optimize(Object& _object, bool _isCreation)
yulAssert(_object.analysisInfo, "");
for (auto& subNode: _object.subObjects)
if (auto subObject = dynamic_cast<Object*>(subNode.get()))
optimize(*subObject, false);
{
// TODO: determine this more properly, resp. store it earlier when the subobject is created
bool isCreation = !boost::ends_with(subObject->name.str(), "_deployed");
optimize(*subObject, isCreation);
}
Dialect const& dialect = languageToDialect(m_language, m_evmVersion);
unique_ptr<GasMeter> meter;

View File

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

View File

@ -28,6 +28,8 @@
#include <libyul/Object.h>
#include <libyul/Exceptions.h>
#include <boost/algorithm/string.hpp>
using namespace solidity::yul;
using namespace std;
@ -46,7 +48,9 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
for (auto const& subNode: _object.subObjects)
if (auto* subObject = dynamic_cast<Object*>(subNode.get()))
{
auto subAssemblyAndID = m_assembly.createSubAssembly(subObject->name.str());
// TODO: determine this more properly, resp. store it earlier when the subobject is created
bool isCreation = !boost::ends_with(subObject->name.str(), "_deployed");
auto subAssemblyAndID = m_assembly.createSubAssembly(subObject->name.str(), isCreation);
context.subIDs[subObject->name] = subAssemblyAndID.second;
subObject->subId = subAssemblyAndID.second;
compile(*subObject, *subAssemblyAndID.first, m_dialect, _optimize);

View File

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

View File

@ -55,7 +55,7 @@ public:
void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override;
void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
void appendAssemblySize() override;
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name = {}) override;
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name, bool _creation) override;
void appendDataOffset(std::vector<SubID> const& _subPath) override;
void appendDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override;

View File

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

View File

@ -65,7 +65,7 @@ public:
void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
void appendAssemblySize() override;
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name = "") override;
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name, bool _creation) override;
void appendDataOffset(std::vector<SubID> const& _subPath) override;
void appendDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override;

View File

@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
// PushData
_assembly.append(bytes{0x1, 0x2, 0x3, 0x4});
// PushSubSize
auto sub = _assembly.appendSubroutine(_subAsmPtr);
auto sub = _assembly.appendSubroutine(_subAsmPtr, false);
// PushSub
_assembly.pushSubroutineOffset(static_cast<size_t>(sub.data()));
// PushDeployTimeAddress
@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
assembly.appendImmutableAssignment(string(1, char('a' + i - 1)));
}
assembly.appendSubroutine(subAsm);
assembly.appendSubroutine(subAsm, false);
checkCompilation(assembly);
@ -275,7 +275,7 @@ BOOST_AUTO_TEST_CASE(immutable)
_assembly.append(u256(0));
_assembly.appendImmutableAssignment("someOtherImmutable");
auto sub = _assembly.appendSubroutine(_subAsmPtr);
auto sub = _assembly.appendSubroutine(_subAsmPtr, false);
_assembly.pushSubroutineOffset(static_cast<size_t>(sub.data()));
checkCompilation(_assembly);
@ -354,8 +354,8 @@ BOOST_AUTO_TEST_CASE(subobject_encode_decode)
shared_ptr<Assembly> subAsmPtr = make_shared<Assembly>();
shared_ptr<Assembly> subSubAsmPtr = make_shared<Assembly>();
assembly.appendSubroutine(subAsmPtr);
subAsmPtr->appendSubroutine(subSubAsmPtr);
assembly.appendSubroutine(subAsmPtr, false);
subAsmPtr->appendSubroutine(subSubAsmPtr, false);
BOOST_CHECK(assembly.encodeSubPath({0}) == 0);
BOOST_REQUIRE_THROW(assembly.encodeSubPath({1}), solidity::evmasm::AssemblyException);

View File

@ -1272,7 +1272,7 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
sub->append(t4.pushTag());
sub->append(Instruction::JUMP);
size_t subId = static_cast<size_t>(main.appendSubroutine(sub).data());
size_t subId = static_cast<size_t>(main.appendSubroutine(sub, false).data());
main.append(t1.toSubAssemblyTag(subId));
main.append(t1.toSubAssemblyTag(subId));
main.append(u256(8));