From 77567d32f4fbf838348c0f050f63bc304e58cb26 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 4 Jan 2023 16:59:54 +0100 Subject: [PATCH] Disable deduplication to align with evmone validation. --- .circleci/config.yml | 2 +- libevmasm/Assembly.cpp | 59 ++++++++++++++++++- libevmasm/Assembly.h | 10 +--- libevmasm/AssemblyItem.cpp | 15 +++++ libevmasm/AssemblyItem.h | 1 + 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 | 57 ++---------------- 11 files changed, 87 insertions(+), 77 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 96e75b04c..51c9f824c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1046,7 +1046,7 @@ jobs: name: Build evmone based on EOF branch command: | ( cd /usr/src; \ - git clone --branch="eof-functions" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="eof" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ sed -i -e 's/GNULIKE TRUE/GNULIKE FALSE/g' cmake/cable/CableCompilerSettings.cmake; \ mkdir build; \ diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index f106eb01c..6a2131ee1 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -408,7 +408,7 @@ map const& Assembly::optimiseInternal( } // This only modifies PushTags, we have to run again to actually remove code. - if (_settings.runDeduplicate) + if (_settings.runDeduplicate && !m_eofVersion.has_value()) for (auto& section: m_codeSections) { BlockDeduplicator deduplicator{section.items}; @@ -500,6 +500,61 @@ map const& Assembly::optimiseInternal( return *m_tagReplacements; } +namespace +{ +uint16_t calcMaxStackHeight(vector const& _items, uint16_t _args) +{ + uint16_t maxStackHeight = 0; + std::stack worklist; + std::vector stack_heights(_items.size(), -1); + stack_heights[0] = _args; + worklist.push(0u); + while (!worklist.empty()) + { + size_t i = worklist.top(); + worklist.pop(); + AssemblyItem const& item = _items.at(i); + size_t stack_height_change = item.deposit(); + ptrdiff_t stackHeight = stack_heights.at(i); + assertThrow(stackHeight != -1, AssemblyException, ""); + + std::vector successors; + + if ( + item.type() != RelativeJump && + !(item.type() == Operation && SemanticInformation::terminatesControlFlow(item.instruction())) && + item.type() != RetF + ) + { + assertThrow(i < _items.size() - 1, AssemblyException, "No terminating instruction."); + successors.emplace_back(i + 1); + } + + if (item.type() == RelativeJump || item.type() == ConditionalRelativeJump) + { + auto it = std::find(_items.begin(), _items.end(), item.tag()); + assertThrow(it != _items.end(), AssemblyException, "Tag not found."); + successors.emplace_back(static_cast(std::distance(_items.begin(), it))); + } + + maxStackHeight = std::max(maxStackHeight, static_cast(stackHeight + static_cast(item.maxStackHeightDelta()))); + stackHeight += static_cast(stack_height_change); + + for (size_t s: successors) + { + if (stack_heights.at(s) == -1) + { + stack_heights[s] = static_cast(stackHeight); + worklist.push(s); + } + else + assertThrow(stack_heights.at(s) == stackHeight, AssemblyException, "Stack height mismatch."); + } + } + return maxStackHeight; +} +} + LinkerObject const& Assembly::assemble() const { assertThrow(!m_invalid, AssemblyException, "Attempted to assemble invalid Assembly object."); @@ -627,7 +682,7 @@ LinkerObject const& Assembly::assemble() const { ret.bytecode.push_back(codeSection.inputs); ret.bytecode.push_back(codeSection.outputs); - appendBigEndianUint16(ret.bytecode, codeSection.maxStackHeight); + appendBigEndianUint16(ret.bytecode, calcMaxStackHeight(codeSection.items, codeSection.inputs)); } } diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 860a810fc..40c61f458 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -77,19 +77,14 @@ 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 _maxStackHeight) + uint16_t createFunction(uint8_t _args, uint8_t _rets) { 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, _maxStackHeight, {}}); + m_codeSections.emplace_back(CodeSection{_args, _rets, {}}); 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."); @@ -214,7 +209,6 @@ public: { uint8_t inputs = 0; uint8_t outputs = 0; - uint16_t maxStackHeight = 0; AssemblyItems items{}; }; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index ff851e83d..ef56d4016 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -116,6 +116,21 @@ void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag) setData(data); } +size_t AssemblyItem::maxStackHeightDelta() const +{ + if (m_type == AssignImmutable) + { + assertThrow(m_immutableOccurrences.has_value(), util::Exception, ""); + if (*m_immutableOccurrences == 0) + return 0; + else + return (*m_immutableOccurrences - 1) * 2 + 1; + } + else + return deposit(); +} + + size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision) const { switch (m_type) diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index ce648cea4..3e26e6ebb 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -187,6 +187,7 @@ public: size_t arguments() const; size_t returnValues() const; size_t deposit() const { return returnValues() - arguments(); } + size_t maxStackHeightDelta() const; /// @returns true if the assembly item can be used in a functional context. bool canBeFunctional() const; diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index c9c878dac..2825ed8c5 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -101,8 +101,7 @@ 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, uint16_t _maxStackHeight) = 0; - virtual void setMaxStackHeight(FunctionID _functionID, uint16_t _maxStackHeight) = 0; + virtual FunctionID createFunction(uint8_t _args, uint8_t _rets) = 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 7e5296918..27bcf4f97 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.cpp +++ b/libyul/backends/evm/EthAssemblyAdapter.cpp @@ -146,14 +146,9 @@ pair, AbstractAssembly::SubID> EthAssemblyAdapter:: return {make_shared(*assembly), static_cast(sub.data())}; } -AbstractAssembly::FunctionID EthAssemblyAdapter::createFunction(uint8_t _args, uint8_t _rets, uint16_t _maxStackArgs) +AbstractAssembly::FunctionID EthAssemblyAdapter::createFunction(uint8_t _args, uint8_t _rets) { - return m_assembly.createFunction(_args, _rets, _maxStackArgs); -} - -void EthAssemblyAdapter::setMaxStackHeight(FunctionID _functionID, uint16_t _maxStackHeight) -{ - m_assembly.setMaxStackHeight(_functionID, _maxStackHeight); + return m_assembly.createFunction(_args, _rets); } void EthAssemblyAdapter::beginFunction(AbstractAssembly::FunctionID _functionID) diff --git a/libyul/backends/evm/EthAssemblyAdapter.h b/libyul/backends/evm/EthAssemblyAdapter.h index 4ed6faa5e..771c79a28 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.h +++ b/libyul/backends/evm/EthAssemblyAdapter.h @@ -56,8 +56,7 @@ 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, uint16_t _maxStackHeight) override; - void setMaxStackHeight(FunctionID _functionID, uint16_t _maxStackHeight) override; + AbstractAssembly::FunctionID createFunction(uint8_t _args, uint8_t _rets) 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 41699bb73..63e9646c0 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, uint16_t) +AbstractAssembly::FunctionID NoOutputAssembly::createFunction(uint8_t _args, uint8_t _rets) { 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 6e9ce5e7c..a805617b6 100644 --- a/libyul/backends/evm/NoOutputAssembly.h +++ b/libyul/backends/evm/NoOutputAssembly.h @@ -74,8 +74,7 @@ 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, uint16_t _maxStackHeight) override; - void setMaxStackHeight(AbstractAssembly::FunctionID, uint16_t) override {} + FunctionID createFunction(uint8_t _args, uint8_t _rets) 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 d9de7d840..1cbbdd0df 100644 --- a/libyul/backends/evm/OptimizedEVMCodeTransform.cpp +++ b/libyul/backends/evm/OptimizedEVMCodeTransform.cpp @@ -40,55 +40,6 @@ 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, @@ -105,7 +56,6 @@ vector OptimizedEVMCodeTransform::run( if (dfg->useFunctions) { - _assembly.setMaxStackHeight(0, getMaxStackHeight(*dfg->entry, stackLayout)); for (Scope::Function const* function: dfg->functions) { auto const& info = dfg->functionInfo.at(function); @@ -113,8 +63,7 @@ vector OptimizedEVMCodeTransform::run( yulAssert(info.returnVariables.size() <= 0xFF); auto functionID = _assembly.createFunction( static_cast(info.parameters.size()), - static_cast(info.returnVariables.size()), - getMaxStackHeight(*info.entry, stackLayout) + static_cast(info.returnVariables.size()) ); _builtinContext.functionIDs[function] = functionID; } @@ -162,7 +111,11 @@ void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call) { m_assembly.setSourceLocation(originLocationOf(_call)); if (m_dfg.useFunctions) + { m_assembly.appendFunctionCall(m_builtinContext.functionIDs.at(&_call.function.get())); + if (!_call.canContinue) + m_assembly.appendInstruction(evmasm::Instruction::INVALID); + } else m_assembly.appendJumpTo( getFunctionLabel(_call.function),