From 321bef463f1c438e39e68ae3cbeb3eff2275baf2 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 20 Dec 2022 16:35:23 +0100 Subject: [PATCH] new container --- libevmasm/Assembly.cpp | 41 +++++++------- libevmasm/Assembly.h | 10 +++- libyul/backends/evm/AbstractAssembly.h | 3 +- libyul/backends/evm/EthAssemblyAdapter.cpp | 9 +++- libyul/backends/evm/EthAssemblyAdapter.h | 3 +- libyul/backends/evm/NoOutputAssembly.cpp | 2 +- libyul/backends/evm/NoOutputAssembly.h | 3 +- .../evm/OptimizedEVMCodeTransform.cpp | 54 ++++++++++++++++++- test/libsolidity/SolidityExecutionFramework.h | 15 ++++-- 9 files changed, 105 insertions(+), 35 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 80f4bfe5b..375162aca 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -583,29 +583,34 @@ LinkerObject const& Assembly::assemble() const }; if (eof) { - bool needsTypeSection = m_codeSections.size() > 1; + bool needsTypeSection = true; // m_codeSections.size() > 1; // TODO: empty data is disallowed ret.bytecode.push_back(0xef); ret.bytecode.push_back(0x00); ret.bytecode.push_back(0x01); // version 1 if (needsTypeSection) { - ret.bytecode.push_back(0x03); // kind=type + ret.bytecode.push_back(0x01); // kind=type ret.bytecode.push_back(0x00); // length of type section ret.bytecode.push_back(0x00); - toBigEndian(m_codeSections.size() * 2, bytesRef(&ret.bytecode.back() + 1 - 2, 2)); + toBigEndian(m_codeSections.size() * 4, bytesRef(&ret.bytecode.back() + 1 - 2, 2)); + } + ret.bytecode.push_back(0x02); // kind=code + ret.bytecode.push_back(0x00); // placeholder for number of code sections + ret.bytecode.push_back(0x00); + { + bytesRef numCodeSections(&ret.bytecode.back() + 1 - 2, 2); + toBigEndian(m_codeSections.size(), numCodeSections); } for (auto const& codeSection: m_codeSections) { (void) codeSection; - ret.bytecode.push_back(0x01); // kind=code codeSectionSizeOffsets.emplace_back(ret.bytecode.size()); ret.bytecode.push_back(0x00); // placeholder for length of code ret.bytecode.push_back(0x00); } - if (bytesRequiredForDataAndSubsUpperBound > 0) { - ret.bytecode.push_back(0x02); // kind=data + ret.bytecode.push_back(0x03); // kind=data dataSectionSizeOffset = ret.bytecode.size(); ret.bytecode.push_back(0x00); // length of data ret.bytecode.push_back(0x00); @@ -617,6 +622,10 @@ LinkerObject const& Assembly::assemble() const { ret.bytecode.push_back(codeSection.inputs); ret.bytecode.push_back(codeSection.outputs); + ret.bytecode.push_back(0x00); // placeholder for max stack height + ret.bytecode.push_back(0x00); + // TODO: check why cast is necessary. + toBigEndian(size_t(codeSection.maxStackHeight), bytesRef(&ret.bytecode.back() + 1 - 2, 2)); } } @@ -903,13 +912,6 @@ LinkerObject const& Assembly::assemble() const ret.bytecode += m_auxiliaryData; - // TODO: remove this when transitioning to unified spec headers - if (eof && bytesRequiredForDataAndSubsUpperBound > 0 && ret.bytecode.size() == dataStart) - { - // We have committed to a data section, but not actually needed it, so create a fake one. - ret.bytecode.push_back(0); - } - for (unsigned pos: sizeRef) { bytesRef r(ret.bytecode.data() + pos, bytesPerDataRef); @@ -919,16 +921,9 @@ LinkerObject const& Assembly::assemble() const auto dataLength = ret.bytecode.size() - dataStart; if (eof) { - assertThrow( - bytesRequiredForDataAndSubsUpperBound >= dataLength, - AssemblyException, - "More data than expected. " + to_string(dataLength) + " > " + to_string(bytesRequiredForDataUpperBound) - ); - if (bytesRequiredForDataAndSubsUpperBound > 0) - { - assertThrow(0 < dataLength && dataLength <= 0xffff, AssemblyException, "Invalid data section size."); - setDataSectionSize(dataLength); - } + assertThrow(bytesRequiredForDataAndSubsUpperBound >= dataLength, AssemblyException, "More data than expected. " + to_string(dataLength) + " > " + to_string(bytesRequiredForDataUpperBound)); + assertThrow(dataLength <= 0xffff, AssemblyException, "Invalid data section size."); + setDataSectionSize(dataLength); } return ret; diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 40c61f458..860a810fc 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -77,14 +77,19 @@ public: 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); } - uint16_t createFunction(uint8_t _args, uint8_t _rets) + uint16_t createFunction(uint8_t _args, uint8_t _rets, uint16_t _maxStackHeight) { size_t functionID = m_codeSections.size(); assertThrow(functionID < 1024, AssemblyException, "Too many functions."); assertThrow(m_currentCodeSection == 0, AssemblyException, "Functions need to be declared from the main block."); - m_codeSections.emplace_back(CodeSection{_args, _rets, {}}); + m_codeSections.emplace_back(CodeSection{_args, _rets, _maxStackHeight, {}}); return static_cast(functionID); } + void setMaxStackHeight(uint16_t _functionID, uint16_t _maxStackHeight) + { + assertThrow(_functionID < m_codeSections.size(), AssemblyException, "Attempt to set the maximum stack height of an undeclared function."); + m_codeSections.at(_functionID).maxStackHeight = _maxStackHeight; + } void beginFunction(uint16_t _functionID) { assertThrow(m_currentCodeSection == 0, AssemblyException, "Atempted to begin a function before ending the last one."); @@ -209,6 +214,7 @@ public: { uint8_t inputs = 0; uint8_t outputs = 0; + uint16_t maxStackHeight = 0; AssemblyItems items{}; }; diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index 2825ed8c5..c9c878dac 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -101,7 +101,8 @@ public: /// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset. virtual std::pair, SubID> createSubAssembly(bool _creation, std::optional _eofVersion, std::string _name = "") = 0; - virtual FunctionID createFunction(uint8_t _args, uint8_t _rets) = 0; + virtual FunctionID createFunction(uint8_t _args, uint8_t _rets, uint16_t _maxStackHeight) = 0; + virtual void setMaxStackHeight(FunctionID _functionID, uint16_t _maxStackHeight) = 0; virtual void beginFunction(FunctionID _functionID) = 0; virtual void endFunction() = 0; diff --git a/libyul/backends/evm/EthAssemblyAdapter.cpp b/libyul/backends/evm/EthAssemblyAdapter.cpp index 27bcf4f97..7e5296918 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.cpp +++ b/libyul/backends/evm/EthAssemblyAdapter.cpp @@ -146,9 +146,14 @@ pair, AbstractAssembly::SubID> EthAssemblyAdapter:: return {make_shared(*assembly), static_cast(sub.data())}; } -AbstractAssembly::FunctionID EthAssemblyAdapter::createFunction(uint8_t _args, uint8_t _rets) +AbstractAssembly::FunctionID EthAssemblyAdapter::createFunction(uint8_t _args, uint8_t _rets, uint16_t _maxStackArgs) { - return m_assembly.createFunction(_args, _rets); + return m_assembly.createFunction(_args, _rets, _maxStackArgs); +} + +void EthAssemblyAdapter::setMaxStackHeight(FunctionID _functionID, uint16_t _maxStackHeight) +{ + m_assembly.setMaxStackHeight(_functionID, _maxStackHeight); } void EthAssemblyAdapter::beginFunction(AbstractAssembly::FunctionID _functionID) diff --git a/libyul/backends/evm/EthAssemblyAdapter.h b/libyul/backends/evm/EthAssemblyAdapter.h index 771c79a28..4ed6faa5e 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.h +++ b/libyul/backends/evm/EthAssemblyAdapter.h @@ -56,7 +56,8 @@ public: void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override; void appendAssemblySize() override; std::pair, SubID> createSubAssembly(bool _creation, std::optional _eofVersion, std::string _name = {}) override; - AbstractAssembly::FunctionID createFunction(uint8_t _args, uint8_t _rets) override; + AbstractAssembly::FunctionID createFunction(uint8_t _args, uint8_t _rets, uint16_t _maxStackHeight) override; + void setMaxStackHeight(FunctionID _functionID, uint16_t _maxStackHeight) override; void beginFunction(AbstractAssembly::FunctionID _functionID) override; void endFunction() override; void appendFunctionCall(FunctionID _functionID) override; diff --git a/libyul/backends/evm/NoOutputAssembly.cpp b/libyul/backends/evm/NoOutputAssembly.cpp index 63e9646c0..41699bb73 100644 --- a/libyul/backends/evm/NoOutputAssembly.cpp +++ b/libyul/backends/evm/NoOutputAssembly.cpp @@ -104,7 +104,7 @@ pair, AbstractAssembly::SubID> NoOutputAssembly::cr return {}; } -AbstractAssembly::FunctionID NoOutputAssembly::createFunction(uint8_t _args, uint8_t _rets) +AbstractAssembly::FunctionID NoOutputAssembly::createFunction(uint8_t _args, uint8_t _rets, uint16_t) { yulAssert(m_context->numFunctions <= std::numeric_limits::max()); AbstractAssembly::FunctionID id = static_cast(m_context->numFunctions++); diff --git a/libyul/backends/evm/NoOutputAssembly.h b/libyul/backends/evm/NoOutputAssembly.h index 5dee12da3..6e9ce5e7c 100644 --- a/libyul/backends/evm/NoOutputAssembly.h +++ b/libyul/backends/evm/NoOutputAssembly.h @@ -74,7 +74,8 @@ public: void appendAssemblySize() override; std::pair, SubID> createSubAssembly(bool _creation, std::optional _eofVersion, std::string _name = "") override; - FunctionID createFunction(uint8_t _args, uint8_t rets) override; + FunctionID createFunction(uint8_t _args, uint8_t rets, uint16_t _maxStackHeight) override; + void setMaxStackHeight(AbstractAssembly::FunctionID, uint16_t) override {} void beginFunction(FunctionID) override; void endFunction() override; void appendFunctionCall(FunctionID _functionID) override; diff --git a/libyul/backends/evm/OptimizedEVMCodeTransform.cpp b/libyul/backends/evm/OptimizedEVMCodeTransform.cpp index 5658c3ebc..40330e88d 100644 --- a/libyul/backends/evm/OptimizedEVMCodeTransform.cpp +++ b/libyul/backends/evm/OptimizedEVMCodeTransform.cpp @@ -40,6 +40,54 @@ using namespace solidity; using namespace solidity::yul; using namespace std; +namespace { + +uint16_t getMaxStackHeight(CFG::BasicBlock const& _block, StackLayout const& _stackLayout) +{ + size_t maxStackHeight = 0; + + std::list toVisit; + std::set visited; + toVisit.push_back(&_block); + + while(!toVisit.empty()) + { + CFG::BasicBlock const* block = toVisit.back(); + toVisit.pop_back(); + if (!visited.insert(block).second) + continue; + + auto& blockInfo = _stackLayout.blockInfos.at(block); + + maxStackHeight = std::max(maxStackHeight, blockInfo.entryLayout.size()); + for (auto const& operation: block->operations) + { + size_t entryLayout = _stackLayout.operationEntryLayout.at(&operation).size(); + maxStackHeight = std::max(maxStackHeight, entryLayout); + size_t exitLayout = entryLayout - operation.input.size() + operation.output.size(); + maxStackHeight = std::max(maxStackHeight, exitLayout); + } + maxStackHeight = std::max(maxStackHeight, blockInfo.exitLayout.size()); + + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) {}, + [&](CFG::BasicBlock::Jump const& _jump) { toVisit.emplace_back(_jump.target); }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + toVisit.emplace_back(_conditionalJump.zero); + toVisit.emplace_back(_conditionalJump.nonZero); + }, + [&](CFG::BasicBlock::FunctionReturn const&) {}, + [&](CFG::BasicBlock::Terminated const&) {} + }, block->exit); + } + + yulAssert(maxStackHeight <= 0xFFFF); + return static_cast(maxStackHeight); +} + +} + vector OptimizedEVMCodeTransform::run( AbstractAssembly& _assembly, AsmAnalysisInfo& _analysisInfo, @@ -55,6 +103,8 @@ vector OptimizedEVMCodeTransform::run( StackLayout stackLayout = StackLayoutGenerator::run(*dfg); if (dfg->useFunctions) + { + _assembly.setMaxStackHeight(0, getMaxStackHeight(*dfg->entry, stackLayout)); for (Scope::Function const* function: dfg->functions) { auto const& info = dfg->functionInfo.at(function); @@ -62,10 +112,12 @@ vector OptimizedEVMCodeTransform::run( yulAssert(info.returnVariables.size() <= 0xFF); auto functionID = _assembly.createFunction( static_cast(info.parameters.size()), - static_cast(info.returnVariables.size()) + static_cast(info.returnVariables.size()), + getMaxStackHeight(*info.entry, stackLayout) ); _builtinContext.functionIDs[function] = functionID; } + } OptimizedEVMCodeTransform optimizedCodeTransform( _assembly, diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 8a83da230..ccd821e58 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -78,10 +78,18 @@ public: case uint8_t(0x00): // terminator stop = true; break; - case uint8_t(0x01): // code section - i += 2; // skip code size section + case uint8_t(0x01): // type section + i += 2; // skip type size section break; - case uint8_t(0x02): // data section + case uint8_t(0x02): // code section + { + bytesRef numCodeSectionsRef(&bytecode[i + 1], 2); + size_t numCodeSections = fromBigEndian(numCodeSectionsRef); + i += numCodeSections * 2 + 2; // skip code section header + break; + } + case uint8_t(0x03): // data section + { auto dataSizeOffset = i + 1; bytesRef dataSizeRef(&bytecode[dataSizeOffset], 2); size_t dataSize = fromBigEndian(dataSizeRef); @@ -89,6 +97,7 @@ public: i += 2; // skip data size section break; } + } if (stop) break; }