mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Reorder functions in implementation to match order in header.
This commit is contained in:
parent
6037c4f26c
commit
74edc40a7e
@ -44,6 +44,17 @@ using namespace solidity;
|
|||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
StackLayout StackLayoutGenerator::run(CFG const& _cfg)
|
||||||
|
{
|
||||||
|
StackLayout stackLayout;
|
||||||
|
StackLayoutGenerator{stackLayout, {}}.processEntryPoint(*_cfg.entry);
|
||||||
|
|
||||||
|
for (auto& functionInfo: _cfg.functionInfo | ranges::views::values)
|
||||||
|
StackLayoutGenerator{stackLayout, functionInfo.returnVariables}.processEntryPoint(*functionInfo.entry);
|
||||||
|
|
||||||
|
return stackLayout;
|
||||||
|
}
|
||||||
|
|
||||||
StackLayoutGenerator::StackLayoutGenerator(StackLayout& _layout, vector<VariableSlot> _currentFunctionReturnVariables):
|
StackLayoutGenerator::StackLayoutGenerator(StackLayout& _layout, vector<VariableSlot> _currentFunctionReturnVariables):
|
||||||
m_layout(_layout),
|
m_layout(_layout),
|
||||||
m_currentFunctionReturnVariables(move(_currentFunctionReturnVariables))
|
m_currentFunctionReturnVariables(move(_currentFunctionReturnVariables))
|
||||||
@ -212,27 +223,6 @@ Stack StackLayoutGenerator::propagateStackThroughOperation(Stack _exitStack, CFG
|
|||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stack StackLayoutGenerator::compressStack(Stack _stack)
|
|
||||||
{
|
|
||||||
optional<size_t> firstDupOffset;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (firstDupOffset)
|
|
||||||
{
|
|
||||||
if (_stack.size() - *firstDupOffset - 1 > 1)
|
|
||||||
std::swap(_stack.at(*firstDupOffset + 1), _stack.back());
|
|
||||||
std::swap(_stack.at(*firstDupOffset), _stack.back());
|
|
||||||
_stack.pop_back();
|
|
||||||
firstDupOffset.reset();
|
|
||||||
}
|
|
||||||
for (auto&& [offset, slot]: _stack | ranges::views::enumerate)
|
|
||||||
if (canBeFreelyGenerated(slot) || util::findOffset(_stack | ranges::views::take(offset), slot))
|
|
||||||
firstDupOffset = offset;
|
|
||||||
}
|
|
||||||
while (firstDupOffset);
|
|
||||||
return _stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stack StackLayoutGenerator::propagateStackThroughBlock(Stack _exitStack, CFG::BasicBlock const& _block)
|
Stack StackLayoutGenerator::propagateStackThroughBlock(Stack _exitStack, CFG::BasicBlock const& _block)
|
||||||
{
|
{
|
||||||
Stack stack = std::move(_exitStack);
|
Stack stack = std::move(_exitStack);
|
||||||
@ -241,99 +231,6 @@ Stack StackLayoutGenerator::propagateStackThroughBlock(Stack _exitStack, CFG::Ba
|
|||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
list<pair<CFG::BasicBlock const*, CFG::BasicBlock const*>> StackLayoutGenerator::collectBackwardsJumps(CFG::BasicBlock const& _entry) const
|
|
||||||
{
|
|
||||||
list<pair<CFG::BasicBlock const*, CFG::BasicBlock const*>> backwardsJumps;
|
|
||||||
util::BreadthFirstSearch<CFG::BasicBlock const*>{{&_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<Stack> StackLayoutGenerator::getExitLayoutOrStageDependencies(
|
|
||||||
CFG::BasicBlock const& _block,
|
|
||||||
set<CFG::BasicBlock const*> const& _visited,
|
|
||||||
list<CFG::BasicBlock const*>& _toVisit
|
|
||||||
) const
|
|
||||||
{
|
|
||||||
return std::visit(util::GenericVisitor{
|
|
||||||
[&](CFG::BasicBlock::MainExit const&) -> std::optional<Stack>
|
|
||||||
{
|
|
||||||
// On the exit of the outermost block the stack can be empty.
|
|
||||||
return Stack{};
|
|
||||||
},
|
|
||||||
[&](CFG::BasicBlock::Jump const& _jump) -> std::optional<Stack>
|
|
||||||
{
|
|
||||||
if (_jump.backwards)
|
|
||||||
{
|
|
||||||
// Choose the best currently known entry layout of the jump target as initial exit.
|
|
||||||
// Note that this may not yet be the final layout.
|
|
||||||
if (auto* info = util::valueOrNullptr(m_layout.blockInfos, _jump.target))
|
|
||||||
return info->entryLayout;
|
|
||||||
return Stack{};
|
|
||||||
}
|
|
||||||
// If the current iteration has already visited the jump target, start from its entry layout.
|
|
||||||
if (_visited.count(_jump.target))
|
|
||||||
return m_layout.blockInfos.at(_jump.target).entryLayout;
|
|
||||||
// Otherwise stage the jump target for visit and defer the current block.
|
|
||||||
_toVisit.emplace_front(_jump.target);
|
|
||||||
return nullopt;
|
|
||||||
},
|
|
||||||
[&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) -> std::optional<Stack>
|
|
||||||
{
|
|
||||||
bool zeroVisited = _visited.count(_conditionalJump.zero);
|
|
||||||
bool nonZeroVisited = _visited.count(_conditionalJump.nonZero);
|
|
||||||
if (zeroVisited && nonZeroVisited)
|
|
||||||
{
|
|
||||||
// If the current iteration has already visited both jump targets, start from its entry layout.
|
|
||||||
Stack stack = combineStack(
|
|
||||||
m_layout.blockInfos.at(_conditionalJump.zero).entryLayout,
|
|
||||||
m_layout.blockInfos.at(_conditionalJump.nonZero).entryLayout
|
|
||||||
);
|
|
||||||
// Additionally, the jump condition has to be at the stack top at exit.
|
|
||||||
stack.emplace_back(_conditionalJump.condition);
|
|
||||||
return stack;
|
|
||||||
}
|
|
||||||
// If one of the jump targets has not been visited, stage it for visit and defer the current block.
|
|
||||||
if (!zeroVisited)
|
|
||||||
_toVisit.emplace_front(_conditionalJump.zero);
|
|
||||||
if (!nonZeroVisited)
|
|
||||||
_toVisit.emplace_front(_conditionalJump.nonZero);
|
|
||||||
return nullopt;
|
|
||||||
},
|
|
||||||
[&](CFG::BasicBlock::FunctionReturn const& _functionReturn) -> std::optional<Stack>
|
|
||||||
{
|
|
||||||
// A function return needs the return variables and the function return label slot on stack.
|
|
||||||
yulAssert(_functionReturn.info, "");
|
|
||||||
Stack stack = _functionReturn.info->returnVariables | ranges::views::transform([](auto const& _varSlot){
|
|
||||||
return StackSlot{_varSlot};
|
|
||||||
}) | ranges::to<Stack>;
|
|
||||||
stack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
|
|
||||||
return stack;
|
|
||||||
},
|
|
||||||
[&](CFG::BasicBlock::Terminated const&) -> std::optional<Stack>
|
|
||||||
{
|
|
||||||
// A terminating block can have an empty stack on exit.
|
|
||||||
return Stack{};
|
|
||||||
},
|
|
||||||
}, _block.exit);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry)
|
void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry)
|
||||||
{
|
{
|
||||||
list<CFG::BasicBlock const*> toVisit{&_entry};
|
list<CFG::BasicBlock const*> toVisit{&_entry};
|
||||||
@ -408,6 +305,145 @@ void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry)
|
|||||||
fixStackTooDeep(_entry);
|
fixStackTooDeep(_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optional<Stack> StackLayoutGenerator::getExitLayoutOrStageDependencies(
|
||||||
|
CFG::BasicBlock const& _block,
|
||||||
|
set<CFG::BasicBlock const*> const& _visited,
|
||||||
|
list<CFG::BasicBlock const*>& _toVisit
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
return std::visit(util::GenericVisitor{
|
||||||
|
[&](CFG::BasicBlock::MainExit const&) -> std::optional<Stack>
|
||||||
|
{
|
||||||
|
// On the exit of the outermost block the stack can be empty.
|
||||||
|
return Stack{};
|
||||||
|
},
|
||||||
|
[&](CFG::BasicBlock::Jump const& _jump) -> std::optional<Stack>
|
||||||
|
{
|
||||||
|
if (_jump.backwards)
|
||||||
|
{
|
||||||
|
// Choose the best currently known entry layout of the jump target as initial exit.
|
||||||
|
// Note that this may not yet be the final layout.
|
||||||
|
if (auto* info = util::valueOrNullptr(m_layout.blockInfos, _jump.target))
|
||||||
|
return info->entryLayout;
|
||||||
|
return Stack{};
|
||||||
|
}
|
||||||
|
// If the current iteration has already visited the jump target, start from its entry layout.
|
||||||
|
if (_visited.count(_jump.target))
|
||||||
|
return m_layout.blockInfos.at(_jump.target).entryLayout;
|
||||||
|
// Otherwise stage the jump target for visit and defer the current block.
|
||||||
|
_toVisit.emplace_front(_jump.target);
|
||||||
|
return nullopt;
|
||||||
|
},
|
||||||
|
[&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) -> std::optional<Stack>
|
||||||
|
{
|
||||||
|
bool zeroVisited = _visited.count(_conditionalJump.zero);
|
||||||
|
bool nonZeroVisited = _visited.count(_conditionalJump.nonZero);
|
||||||
|
if (zeroVisited && nonZeroVisited)
|
||||||
|
{
|
||||||
|
// If the current iteration has already visited both jump targets, start from its entry layout.
|
||||||
|
Stack stack = combineStack(
|
||||||
|
m_layout.blockInfos.at(_conditionalJump.zero).entryLayout,
|
||||||
|
m_layout.blockInfos.at(_conditionalJump.nonZero).entryLayout
|
||||||
|
);
|
||||||
|
// Additionally, the jump condition has to be at the stack top at exit.
|
||||||
|
stack.emplace_back(_conditionalJump.condition);
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
// If one of the jump targets has not been visited, stage it for visit and defer the current block.
|
||||||
|
if (!zeroVisited)
|
||||||
|
_toVisit.emplace_front(_conditionalJump.zero);
|
||||||
|
if (!nonZeroVisited)
|
||||||
|
_toVisit.emplace_front(_conditionalJump.nonZero);
|
||||||
|
return nullopt;
|
||||||
|
},
|
||||||
|
[&](CFG::BasicBlock::FunctionReturn const& _functionReturn) -> std::optional<Stack>
|
||||||
|
{
|
||||||
|
// A function return needs the return variables and the function return label slot on stack.
|
||||||
|
yulAssert(_functionReturn.info, "");
|
||||||
|
Stack stack = _functionReturn.info->returnVariables | ranges::views::transform([](auto const& _varSlot){
|
||||||
|
return StackSlot{_varSlot};
|
||||||
|
}) | ranges::to<Stack>;
|
||||||
|
stack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
|
||||||
|
return stack;
|
||||||
|
},
|
||||||
|
[&](CFG::BasicBlock::Terminated const&) -> std::optional<Stack>
|
||||||
|
{
|
||||||
|
// A terminating block can have an empty stack on exit.
|
||||||
|
return Stack{};
|
||||||
|
},
|
||||||
|
}, _block.exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
list<pair<CFG::BasicBlock const*, CFG::BasicBlock const*>> StackLayoutGenerator::collectBackwardsJumps(CFG::BasicBlock const& _entry) const
|
||||||
|
{
|
||||||
|
list<pair<CFG::BasicBlock const*, CFG::BasicBlock const*>> backwardsJumps;
|
||||||
|
util::BreadthFirstSearch<CFG::BasicBlock const*>{{&_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackLayoutGenerator::stitchConditionalJumps(CFG::BasicBlock const& _block)
|
||||||
|
{
|
||||||
|
util::BreadthFirstSearch<CFG::BasicBlock const*> breadthFirstSearch{{&_block}};
|
||||||
|
breadthFirstSearch.run([&](CFG::BasicBlock const* _block, auto _addChild) {
|
||||||
|
auto& info = m_layout.blockInfos.at(_block);
|
||||||
|
std::visit(util::GenericVisitor{
|
||||||
|
[&](CFG::BasicBlock::MainExit const&) {},
|
||||||
|
[&](CFG::BasicBlock::Jump const& _jump)
|
||||||
|
{
|
||||||
|
if (!_jump.backwards)
|
||||||
|
_addChild(_jump.target);
|
||||||
|
},
|
||||||
|
[&](CFG::BasicBlock::ConditionalJump const& _conditionalJump)
|
||||||
|
{
|
||||||
|
auto& zeroTargetInfo = m_layout.blockInfos.at(_conditionalJump.zero);
|
||||||
|
auto& nonZeroTargetInfo = m_layout.blockInfos.at(_conditionalJump.nonZero);
|
||||||
|
Stack exitLayout = info.exitLayout;
|
||||||
|
|
||||||
|
// The last block must have produced the condition at the stack top.
|
||||||
|
yulAssert(!exitLayout.empty(), "");
|
||||||
|
yulAssert(exitLayout.back() == _conditionalJump.condition, "");
|
||||||
|
// The condition is consumed by the jump.
|
||||||
|
exitLayout.pop_back();
|
||||||
|
|
||||||
|
auto fixJumpTargetEntry = [&](Stack const& _originalEntryLayout) -> Stack {
|
||||||
|
Stack newEntryLayout = exitLayout;
|
||||||
|
// Whatever the block being jumped to does not actually require, can be marked as junk.
|
||||||
|
for (auto& slot: newEntryLayout)
|
||||||
|
if (!util::findOffset(_originalEntryLayout, slot))
|
||||||
|
slot = JunkSlot{};
|
||||||
|
// Make sure everything the block being jumped to requires is actually present or can be generated.
|
||||||
|
for (auto const& slot: _originalEntryLayout)
|
||||||
|
yulAssert(canBeFreelyGenerated(slot) || util::findOffset(newEntryLayout, slot), "");
|
||||||
|
return newEntryLayout;
|
||||||
|
};
|
||||||
|
zeroTargetInfo.entryLayout = fixJumpTargetEntry(zeroTargetInfo.entryLayout);
|
||||||
|
nonZeroTargetInfo.entryLayout = fixJumpTargetEntry(nonZeroTargetInfo.entryLayout);
|
||||||
|
_addChild(_conditionalJump.zero);
|
||||||
|
_addChild(_conditionalJump.nonZero);
|
||||||
|
},
|
||||||
|
[&](CFG::BasicBlock::FunctionReturn const&) {},
|
||||||
|
[&](CFG::BasicBlock::Terminated const&) { },
|
||||||
|
}, _block->exit);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Stack StackLayoutGenerator::combineStack(Stack const& _stack1, Stack const& _stack2)
|
Stack StackLayoutGenerator::combineStack(Stack const& _stack1, Stack const& _stack2)
|
||||||
{
|
{
|
||||||
// TODO: there is probably a better way than brute-forcing. This has n! complexity or worse, so
|
// TODO: there is probably a better way than brute-forcing. This has n! complexity or worse, so
|
||||||
@ -494,64 +530,28 @@ Stack StackLayoutGenerator::combineStack(Stack const& _stack1, Stack const& _sta
|
|||||||
return commonPrefix + sortedCandidates.begin()->second;
|
return commonPrefix + sortedCandidates.begin()->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackLayoutGenerator::stitchConditionalJumps(CFG::BasicBlock const& _block)
|
|
||||||
{
|
|
||||||
util::BreadthFirstSearch<CFG::BasicBlock const*> breadthFirstSearch{{&_block}};
|
|
||||||
breadthFirstSearch.run([&](CFG::BasicBlock const* _block, auto _addChild) {
|
|
||||||
auto& info = m_layout.blockInfos.at(_block);
|
|
||||||
std::visit(util::GenericVisitor{
|
|
||||||
[&](CFG::BasicBlock::MainExit const&) {},
|
|
||||||
[&](CFG::BasicBlock::Jump const& _jump)
|
|
||||||
{
|
|
||||||
if (!_jump.backwards)
|
|
||||||
_addChild(_jump.target);
|
|
||||||
},
|
|
||||||
[&](CFG::BasicBlock::ConditionalJump const& _conditionalJump)
|
|
||||||
{
|
|
||||||
auto& zeroTargetInfo = m_layout.blockInfos.at(_conditionalJump.zero);
|
|
||||||
auto& nonZeroTargetInfo = m_layout.blockInfos.at(_conditionalJump.nonZero);
|
|
||||||
Stack exitLayout = info.exitLayout;
|
|
||||||
|
|
||||||
// The last block must have produced the condition at the stack top.
|
|
||||||
yulAssert(!exitLayout.empty(), "");
|
|
||||||
yulAssert(exitLayout.back() == _conditionalJump.condition, "");
|
|
||||||
// The condition is consumed by the jump.
|
|
||||||
exitLayout.pop_back();
|
|
||||||
|
|
||||||
auto fixJumpTargetEntry = [&](Stack const& _originalEntryLayout) -> Stack {
|
|
||||||
Stack newEntryLayout = exitLayout;
|
|
||||||
// Whatever the block being jumped to does not actually require, can be marked as junk.
|
|
||||||
for (auto& slot: newEntryLayout)
|
|
||||||
if (!util::findOffset(_originalEntryLayout, slot))
|
|
||||||
slot = JunkSlot{};
|
|
||||||
// Make sure everything the block being jumped to requires is actually present or can be generated.
|
|
||||||
for (auto const& slot: _originalEntryLayout)
|
|
||||||
yulAssert(canBeFreelyGenerated(slot) || util::findOffset(newEntryLayout, slot), "");
|
|
||||||
return newEntryLayout;
|
|
||||||
};
|
|
||||||
zeroTargetInfo.entryLayout = fixJumpTargetEntry(zeroTargetInfo.entryLayout);
|
|
||||||
nonZeroTargetInfo.entryLayout = fixJumpTargetEntry(nonZeroTargetInfo.entryLayout);
|
|
||||||
_addChild(_conditionalJump.zero);
|
|
||||||
_addChild(_conditionalJump.nonZero);
|
|
||||||
},
|
|
||||||
[&](CFG::BasicBlock::FunctionReturn const&) {},
|
|
||||||
[&](CFG::BasicBlock::Terminated const&) { },
|
|
||||||
}, _block->exit);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void StackLayoutGenerator::fixStackTooDeep(CFG::BasicBlock const&)
|
void StackLayoutGenerator::fixStackTooDeep(CFG::BasicBlock const&)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
StackLayout StackLayoutGenerator::run(CFG const& _cfg)
|
Stack StackLayoutGenerator::compressStack(Stack _stack)
|
||||||
{
|
{
|
||||||
StackLayout stackLayout;
|
optional<size_t> firstDupOffset;
|
||||||
StackLayoutGenerator{stackLayout, {}}.processEntryPoint(*_cfg.entry);
|
do
|
||||||
|
{
|
||||||
for (auto& functionInfo: _cfg.functionInfo | ranges::views::values)
|
if (firstDupOffset)
|
||||||
StackLayoutGenerator{stackLayout, functionInfo.returnVariables}.processEntryPoint(*functionInfo.entry);
|
{
|
||||||
|
if (_stack.size() - *firstDupOffset - 1 > 1)
|
||||||
return stackLayout;
|
std::swap(_stack.at(*firstDupOffset + 1), _stack.back());
|
||||||
|
std::swap(_stack.at(*firstDupOffset), _stack.back());
|
||||||
|
_stack.pop_back();
|
||||||
|
firstDupOffset.reset();
|
||||||
|
}
|
||||||
|
for (auto&& [offset, slot]: _stack | ranges::views::enumerate)
|
||||||
|
if (canBeFreelyGenerated(slot) || util::findOffset(_stack | ranges::views::take(offset), slot))
|
||||||
|
firstDupOffset = offset;
|
||||||
|
}
|
||||||
|
while (firstDupOffset);
|
||||||
|
return _stack;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user