mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #11824 from ethereum/yulControlFlowGraphRecursiveCalls
Mark recursive calls in yul control flow graph.
This commit is contained in:
commit
f4effe966e
@ -131,6 +131,9 @@ struct CFG
|
||||
std::shared_ptr<DebugData const> debugData;
|
||||
std::reference_wrapper<Scope::Function const> function;
|
||||
std::reference_wrapper<yul::FunctionCall const> functionCall;
|
||||
/// True, if the call is recursive, i.e. entering the function involves a control flow path (potentially involving
|
||||
/// more intermediate function calls) that leads back to this very call.
|
||||
bool recursive = false;
|
||||
};
|
||||
struct Assignment
|
||||
{
|
||||
|
@ -47,22 +47,14 @@ using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
using namespace std;
|
||||
|
||||
std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
|
||||
AsmAnalysisInfo const& _analysisInfo,
|
||||
Dialect const& _dialect,
|
||||
Block const& _block
|
||||
)
|
||||
namespace
|
||||
{
|
||||
// Removes edges to blocks that are not reachable.
|
||||
void cleanUnreachable(CFG& _cfg)
|
||||
{
|
||||
auto result = std::make_unique<CFG>();
|
||||
result->entry = &result->makeBlock();
|
||||
|
||||
ControlFlowGraphBuilder builder(*result, _analysisInfo, _dialect);
|
||||
builder.m_currentBlock = result->entry;
|
||||
builder(_block);
|
||||
|
||||
// Determine which blocks are reachable from the entry.
|
||||
util::BreadthFirstSearch<CFG::BasicBlock*> reachabilityCheck{{result->entry}};
|
||||
for (auto const& functionInfo: result->functionInfo | ranges::views::values)
|
||||
util::BreadthFirstSearch<CFG::BasicBlock*> reachabilityCheck{{_cfg.entry}};
|
||||
for (auto const& functionInfo: _cfg.functionInfo | ranges::views::values)
|
||||
reachabilityCheck.verticesToTraverse.emplace_back(functionInfo.entry);
|
||||
|
||||
reachabilityCheck.run([&](CFG::BasicBlock* _node, auto&& _addChild) {
|
||||
@ -85,6 +77,71 @@ std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
|
||||
cxx20::erase_if(node->entries, [&](CFG::BasicBlock* entry) -> bool {
|
||||
return !reachabilityCheck.visited.count(entry);
|
||||
});
|
||||
}
|
||||
// Sets the ``recursive`` member to ``true`` for all recursive function calls.
|
||||
void markRecursiveCalls(CFG& _cfg)
|
||||
{
|
||||
map<CFG::BasicBlock*, vector<CFG::FunctionCall*>> callsPerBlock;
|
||||
auto const& findCalls = [&](CFG::BasicBlock* _block)
|
||||
{
|
||||
if (auto* calls = util::valueOrNullptr(callsPerBlock, _block))
|
||||
return *calls;
|
||||
vector<CFG::FunctionCall*>& calls = callsPerBlock[_block];
|
||||
util::BreadthFirstSearch<CFG::BasicBlock*>{{_block}}.run([&](CFG::BasicBlock* _block, auto _addChild) {
|
||||
for (auto& operation: _block->operations)
|
||||
if (auto* functionCall = get_if<CFG::FunctionCall>(&operation.operation))
|
||||
calls.emplace_back(functionCall);
|
||||
std::visit(util::GenericVisitor{
|
||||
[&](CFG::BasicBlock::MainExit const&) {},
|
||||
[&](CFG::BasicBlock::Jump const& _jump)
|
||||
{
|
||||
_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 calls;
|
||||
};
|
||||
for (auto& functionInfo: _cfg.functionInfo | ranges::views::values)
|
||||
for (CFG::FunctionCall* call: findCalls(functionInfo.entry))
|
||||
{
|
||||
util::BreadthFirstSearch<CFG::FunctionCall*> breadthFirstSearch{{call}};
|
||||
breadthFirstSearch.run([&](CFG::FunctionCall* _call, auto _addChild) {
|
||||
auto& calledFunctionInfo = _cfg.functionInfo.at(&_call->function.get());
|
||||
if (&calledFunctionInfo == &functionInfo)
|
||||
{
|
||||
call->recursive = true;
|
||||
breadthFirstSearch.abort();
|
||||
return;
|
||||
}
|
||||
for (CFG::FunctionCall* nestedCall: findCalls(_cfg.functionInfo.at(&_call->function.get()).entry))
|
||||
_addChild(nestedCall);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
|
||||
AsmAnalysisInfo const& _analysisInfo,
|
||||
Dialect const& _dialect,
|
||||
Block const& _block
|
||||
)
|
||||
{
|
||||
auto result = std::make_unique<CFG>();
|
||||
result->entry = &result->makeBlock();
|
||||
|
||||
ControlFlowGraphBuilder builder(*result, _analysisInfo, _dialect);
|
||||
builder.m_currentBlock = result->entry;
|
||||
builder(_block);
|
||||
|
||||
cleanUnreachable(*result);
|
||||
markRecursiveCalls(*result);
|
||||
|
||||
// TODO: It might be worthwhile to run some further simplifications on the graph itself here.
|
||||
// E.g. if there is a jump to a node that has the jumping node as its only entry, the nodes can be fused, etc.
|
||||
|
Loading…
Reference in New Issue
Block a user