Attempt to split up the main algorithm a bit.

This commit is contained in:
Daniel Kirchner 2021-07-15 12:18:31 +02:00
parent 1fd4cf2254
commit a756ec3e0e
2 changed files with 103 additions and 64 deletions

View File

@ -239,51 +239,60 @@ Stack StackLayoutGenerator::propagateStackThroughBlock(Stack _exitStack, CFG::Ba
return stack; return stack;
} }
void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry) list<pair<CFG::BasicBlock const*, CFG::BasicBlock const*>> StackLayoutGenerator::collectBackwardsJumps(CFG::BasicBlock const& _entry) const
{ {
std::list<CFG::BasicBlock const*> toVisit{&_entry}; list<pair<CFG::BasicBlock const*, CFG::BasicBlock const*>> backwardsJumps;
std::set<CFG::BasicBlock const*> visited; util::BreadthFirstSearch<CFG::BasicBlock const*>{{&_entry}}.run([&](CFG::BasicBlock const* _block, auto _addChild) {
std::visit(util::GenericVisitor{
while (!toVisit.empty()) [&](CFG::BasicBlock::MainExit const&) {},
[&](CFG::BasicBlock::Jump const& _jump)
{ {
// TODO: calculate backwardsJumps only once. if (_jump.backwards)
std::list<std::pair<CFG::BasicBlock const*, CFG::BasicBlock const*>> backwardsJumps; backwardsJumps.emplace_back(_block, _jump.target);
while (!toVisit.empty()) _addChild(_jump.target);
},
[&](CFG::BasicBlock::ConditionalJump const& _conditionalJump)
{ {
CFG::BasicBlock const *block = *toVisit.begin(); _addChild(_conditionalJump.zero);
toVisit.pop_front(); _addChild(_conditionalJump.nonZero);
},
[&](CFG::BasicBlock::FunctionReturn const&) {},
[&](CFG::BasicBlock::Terminated const&) {},
}, _block->exit);
});
return backwardsJumps;
}
if (visited.count(block)) optional<Stack> StackLayoutGenerator::getExitLayoutOrStageDependencies(
continue; CFG::BasicBlock const& _block,
set<CFG::BasicBlock const*> const& _blocksWithExitLayout,
if (std::optional<Stack> exitLayout = std::visit(util::GenericVisitor{ list<CFG::BasicBlock const*>& _toVisit
) const
{
return std::visit(util::GenericVisitor{
[&](CFG::BasicBlock::MainExit const&) -> std::optional<Stack> [&](CFG::BasicBlock::MainExit const&) -> std::optional<Stack>
{ {
visited.emplace(block);
return Stack{}; return Stack{};
}, },
[&](CFG::BasicBlock::Jump const& _jump) -> std::optional<Stack> [&](CFG::BasicBlock::Jump const& _jump) -> std::optional<Stack>
{ {
if (_jump.backwards) if (_jump.backwards)
{ {
visited.emplace(block);
backwardsJumps.emplace_back(block, _jump.target);
if (auto* info = util::valueOrNullptr(m_layout.blockInfos, _jump.target)) if (auto* info = util::valueOrNullptr(m_layout.blockInfos, _jump.target))
return info->entryLayout; return info->entryLayout;
return Stack{}; return Stack{};
} }
if (visited.count(_jump.target)) if (_blocksWithExitLayout.count(_jump.target))
{ {
visited.emplace(block);
return m_layout.blockInfos.at(_jump.target).entryLayout; return m_layout.blockInfos.at(_jump.target).entryLayout;
} }
toVisit.emplace_front(_jump.target); _toVisit.emplace_front(_jump.target);
return nullopt; return nullopt;
}, },
[&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) -> std::optional<Stack> [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) -> std::optional<Stack>
{ {
bool zeroVisited = visited.count(_conditionalJump.zero); bool zeroVisited = _blocksWithExitLayout.count(_conditionalJump.zero);
bool nonZeroVisited = visited.count(_conditionalJump.nonZero); bool nonZeroVisited = _blocksWithExitLayout.count(_conditionalJump.nonZero);
if (zeroVisited && nonZeroVisited) if (zeroVisited && nonZeroVisited)
{ {
Stack stack = combineStack( Stack stack = combineStack(
@ -291,18 +300,16 @@ void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry)
m_layout.blockInfos.at(_conditionalJump.nonZero).entryLayout m_layout.blockInfos.at(_conditionalJump.nonZero).entryLayout
); );
stack.emplace_back(_conditionalJump.condition); stack.emplace_back(_conditionalJump.condition);
visited.emplace(block);
return stack; return stack;
} }
if (!zeroVisited) if (!zeroVisited)
toVisit.emplace_front(_conditionalJump.zero); _toVisit.emplace_front(_conditionalJump.zero);
if (!nonZeroVisited) if (!nonZeroVisited)
toVisit.emplace_front(_conditionalJump.nonZero); _toVisit.emplace_front(_conditionalJump.nonZero);
return nullopt; return nullopt;
}, },
[&](CFG::BasicBlock::FunctionReturn const& _functionReturn) -> std::optional<Stack> [&](CFG::BasicBlock::FunctionReturn const& _functionReturn) -> std::optional<Stack>
{ {
visited.emplace(block);
yulAssert(_functionReturn.info, ""); yulAssert(_functionReturn.info, "");
Stack stack = _functionReturn.info->returnVariables | ranges::views::transform([](auto const& _varSlot){ Stack stack = _functionReturn.info->returnVariables | ranges::views::transform([](auto const& _varSlot){
return StackSlot{_varSlot}; return StackSlot{_varSlot};
@ -312,11 +319,34 @@ void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry)
}, },
[&](CFG::BasicBlock::Terminated const&) -> std::optional<Stack> [&](CFG::BasicBlock::Terminated const&) -> std::optional<Stack>
{ {
visited.emplace(block);
return Stack{}; return Stack{};
}, },
}, block->exit)) }, _block.exit);
}
void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry)
{
list<CFG::BasicBlock const*> toVisit{&_entry};
set<CFG::BasicBlock const*> visited;
// TODO: check whether visiting only a subset of these in the outer iteration below is enough.
list<pair<CFG::BasicBlock const*, CFG::BasicBlock const*>> backwardsJumps = collectBackwardsJumps(_entry);
while (!toVisit.empty())
{ {
// 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();
toVisit.pop_front();
if (visited.count(block))
continue;
if (std::optional<Stack> exitLayout = getExitLayoutOrStageDependencies(*block, visited, toVisit))
{
visited.emplace(block);
// We can skip the visit, if we have seen this precise exit layout already last time. // 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 // Note: if the entire graph is revisited in the backwards jump check below, doing
// this seems to break things; not sure why. // this seems to break things; not sure why.
@ -336,6 +366,7 @@ void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry)
continue; continue;
} }
// Determine which backwards jumps still require fixing and stage revisits of appropriate nodes.
for (auto [block, target]: backwardsJumps) for (auto [block, target]: backwardsJumps)
if (ranges::any_of( if (ranges::any_of(
m_layout.blockInfos[target].entryLayout, m_layout.blockInfos[target].entryLayout,

View File

@ -64,6 +64,14 @@ private:
/// Iteratively reruns itself along backwards jumps until the layout is stabilized. /// Iteratively reruns itself along backwards jumps until the layout is stabilized.
void processEntryPoint(CFG::BasicBlock const& _entry); void processEntryPoint(CFG::BasicBlock const& _entry);
std::optional<Stack> getExitLayoutOrStageDependencies(
CFG::BasicBlock const& _block,
std::set<CFG::BasicBlock const*> const& _blocksWithExitLayouts,
std::list<CFG::BasicBlock const*>& _dependencyList
) const;
std::list<std::pair<CFG::BasicBlock const*, CFG::BasicBlock const*>> collectBackwardsJumps(CFG::BasicBlock const& _entry) const;
/// After the main algorithms, layouts at conditional jumps are merely compatible, i.e. the exit layout of the /// 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 /// 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 /// of conditional jump targets, s.t. the entry layout of target blocks match the exit layout of the jumping block