mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Mark recursive calls in yul control flow graph.
This commit is contained in:
parent
cef0f1b9e1
commit
c82f9b9fab
@ -131,6 +131,9 @@ struct CFG
|
|||||||
std::shared_ptr<DebugData const> debugData;
|
std::shared_ptr<DebugData const> debugData;
|
||||||
std::reference_wrapper<Scope::Function const> function;
|
std::reference_wrapper<Scope::Function const> function;
|
||||||
std::reference_wrapper<yul::FunctionCall const> functionCall;
|
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
|
struct Assignment
|
||||||
{
|
{
|
||||||
|
@ -47,22 +47,14 @@ using namespace solidity;
|
|||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
|
namespace
|
||||||
AsmAnalysisInfo const& _analysisInfo,
|
{
|
||||||
Dialect const& _dialect,
|
// Removes edges to blocks that are not reachable.
|
||||||
Block const& _block
|
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.
|
// Determine which blocks are reachable from the entry.
|
||||||
util::BreadthFirstSearch<CFG::BasicBlock*> reachabilityCheck{{result->entry}};
|
util::BreadthFirstSearch<CFG::BasicBlock*> reachabilityCheck{{_cfg.entry}};
|
||||||
for (auto const& functionInfo: result->functionInfo | ranges::views::values)
|
for (auto const& functionInfo: _cfg.functionInfo | ranges::views::values)
|
||||||
reachabilityCheck.verticesToTraverse.emplace_back(functionInfo.entry);
|
reachabilityCheck.verticesToTraverse.emplace_back(functionInfo.entry);
|
||||||
|
|
||||||
reachabilityCheck.run([&](CFG::BasicBlock* _node, auto&& _addChild) {
|
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 {
|
cxx20::erase_if(node->entries, [&](CFG::BasicBlock* entry) -> bool {
|
||||||
return !reachabilityCheck.visited.count(entry);
|
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.
|
// 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.
|
// 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