From a756ec3e0ecc910ba420184a6d8e795e61b26092 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 15 Jul 2021 12:18:31 +0200 Subject: [PATCH] Attempt to split up the main algorithm a bit. --- libyul/backends/evm/StackLayoutGenerator.cpp | 159 +++++++++++-------- libyul/backends/evm/StackLayoutGenerator.h | 8 + 2 files changed, 103 insertions(+), 64 deletions(-) diff --git a/libyul/backends/evm/StackLayoutGenerator.cpp b/libyul/backends/evm/StackLayoutGenerator.cpp index 717d4a56a..ee7c5de5d 100644 --- a/libyul/backends/evm/StackLayoutGenerator.cpp +++ b/libyul/backends/evm/StackLayoutGenerator.cpp @@ -239,15 +239,103 @@ Stack StackLayoutGenerator::propagateStackThroughBlock(Stack _exitStack, CFG::Ba return stack; } +list> StackLayoutGenerator::collectBackwardsJumps(CFG::BasicBlock const& _entry) const +{ + list> backwardsJumps; + util::BreadthFirstSearch{{&_entry}}.run([&](CFG::BasicBlock const* _block, auto _addChild) { + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) {}, + [&](CFG::BasicBlock::Jump const& _jump) + { + if (_jump.backwards) + backwardsJumps.emplace_back(_block, _jump.target); + _addChild(_jump.target); + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + _addChild(_conditionalJump.zero); + _addChild(_conditionalJump.nonZero); + }, + [&](CFG::BasicBlock::FunctionReturn const&) {}, + [&](CFG::BasicBlock::Terminated const&) {}, + }, _block->exit); + }); + return backwardsJumps; +} + +optional StackLayoutGenerator::getExitLayoutOrStageDependencies( + CFG::BasicBlock const& _block, + set const& _blocksWithExitLayout, + list& _toVisit +) const +{ + return std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) -> std::optional + { + return Stack{}; + }, + [&](CFG::BasicBlock::Jump const& _jump) -> std::optional + { + if (_jump.backwards) + { + if (auto* info = util::valueOrNullptr(m_layout.blockInfos, _jump.target)) + return info->entryLayout; + return Stack{}; + } + if (_blocksWithExitLayout.count(_jump.target)) + { + return m_layout.blockInfos.at(_jump.target).entryLayout; + } + _toVisit.emplace_front(_jump.target); + return nullopt; + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) -> std::optional + { + bool zeroVisited = _blocksWithExitLayout.count(_conditionalJump.zero); + bool nonZeroVisited = _blocksWithExitLayout.count(_conditionalJump.nonZero); + if (zeroVisited && nonZeroVisited) + { + Stack stack = combineStack( + m_layout.blockInfos.at(_conditionalJump.zero).entryLayout, + m_layout.blockInfos.at(_conditionalJump.nonZero).entryLayout + ); + stack.emplace_back(_conditionalJump.condition); + return stack; + } + if (!zeroVisited) + _toVisit.emplace_front(_conditionalJump.zero); + if (!nonZeroVisited) + _toVisit.emplace_front(_conditionalJump.nonZero); + return nullopt; + }, + [&](CFG::BasicBlock::FunctionReturn const& _functionReturn) -> std::optional + { + yulAssert(_functionReturn.info, ""); + Stack stack = _functionReturn.info->returnVariables | ranges::views::transform([](auto const& _varSlot){ + return StackSlot{_varSlot}; + }) | ranges::to; + stack.emplace_back(FunctionReturnLabelSlot{}); + return stack; + }, + [&](CFG::BasicBlock::Terminated const&) -> std::optional + { + return Stack{}; + }, + }, _block.exit); +} + void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry) { - std::list toVisit{&_entry}; - std::set visited; + list toVisit{&_entry}; + set visited; + + // TODO: check whether visiting only a subset of these in the outer iteration below is enough. + list> backwardsJumps = collectBackwardsJumps(_entry); while (!toVisit.empty()) { - // TODO: calculate backwardsJumps only once. - std::list> backwardsJumps; + // First calculate stack layouts without walking backwards jumps, i.e. assuming the current preliminary + // entry layout of the backwards jump target as the initial exit layout of the backwards-jumping block. while (!toVisit.empty()) { CFG::BasicBlock const *block = *toVisit.begin(); @@ -256,67 +344,9 @@ void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry) if (visited.count(block)) continue; - if (std::optional exitLayout = std::visit(util::GenericVisitor{ - [&](CFG::BasicBlock::MainExit const&) -> std::optional - { - visited.emplace(block); - return Stack{}; - }, - [&](CFG::BasicBlock::Jump const& _jump) -> std::optional - { - if (_jump.backwards) - { - visited.emplace(block); - backwardsJumps.emplace_back(block, _jump.target); - if (auto* info = util::valueOrNullptr(m_layout.blockInfos, _jump.target)) - return info->entryLayout; - return Stack{}; - } - if (visited.count(_jump.target)) - { - visited.emplace(block); - return m_layout.blockInfos.at(_jump.target).entryLayout; - } - toVisit.emplace_front(_jump.target); - return nullopt; - }, - [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) -> std::optional - { - bool zeroVisited = visited.count(_conditionalJump.zero); - bool nonZeroVisited = visited.count(_conditionalJump.nonZero); - if (zeroVisited && nonZeroVisited) - { - Stack stack = combineStack( - m_layout.blockInfos.at(_conditionalJump.zero).entryLayout, - m_layout.blockInfos.at(_conditionalJump.nonZero).entryLayout - ); - stack.emplace_back(_conditionalJump.condition); - visited.emplace(block); - return stack; - } - if (!zeroVisited) - toVisit.emplace_front(_conditionalJump.zero); - if (!nonZeroVisited) - toVisit.emplace_front(_conditionalJump.nonZero); - return nullopt; - }, - [&](CFG::BasicBlock::FunctionReturn const& _functionReturn) -> std::optional - { - visited.emplace(block); - yulAssert(_functionReturn.info, ""); - Stack stack = _functionReturn.info->returnVariables | ranges::views::transform([](auto const& _varSlot){ - return StackSlot{_varSlot}; - }) | ranges::to; - stack.emplace_back(FunctionReturnLabelSlot{}); - return stack; - }, - [&](CFG::BasicBlock::Terminated const&) -> std::optional - { - visited.emplace(block); - return Stack{}; - }, - }, block->exit)) + if (std::optional exitLayout = getExitLayoutOrStageDependencies(*block, visited, toVisit)) { + visited.emplace(block); // We can skip the visit, if we have seen this precise exit layout already last time. // Note: if the entire graph is revisited in the backwards jump check below, doing // this seems to break things; not sure why. @@ -336,6 +366,7 @@ void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry) continue; } + // Determine which backwards jumps still require fixing and stage revisits of appropriate nodes. for (auto [block, target]: backwardsJumps) if (ranges::any_of( m_layout.blockInfos[target].entryLayout, diff --git a/libyul/backends/evm/StackLayoutGenerator.h b/libyul/backends/evm/StackLayoutGenerator.h index a229dbdb3..1a6b3ec73 100644 --- a/libyul/backends/evm/StackLayoutGenerator.h +++ b/libyul/backends/evm/StackLayoutGenerator.h @@ -64,6 +64,14 @@ private: /// Iteratively reruns itself along backwards jumps until the layout is stabilized. void processEntryPoint(CFG::BasicBlock const& _entry); + std::optional getExitLayoutOrStageDependencies( + CFG::BasicBlock const& _block, + std::set const& _blocksWithExitLayouts, + std::list& _dependencyList + ) const; + + std::list> collectBackwardsJumps(CFG::BasicBlock const& _entry) const; + /// After the main algorithms, layouts at conditional jumps are merely compatible, i.e. the exit layout of the /// jumping block is a superset of the entry layout of the target block. This function modifies the entry layouts /// of conditional jump targets, s.t. the entry layout of target blocks match the exit layout of the jumping block