mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #9427 from ethereum/recursiveFunctionDetection
Extract recursive function detection from Semantics into the CallGraph.
This commit is contained in:
commit
59c6a1f57b
@ -30,6 +30,47 @@ using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
using namespace solidity::util;
|
||||
|
||||
namespace
|
||||
{
|
||||
// TODO: This algorithm is non-optimal.
|
||||
struct CallGraphCycleFinder
|
||||
{
|
||||
CallGraph const& callGraph;
|
||||
set<YulString> containedInCycle{};
|
||||
set<YulString> visited{};
|
||||
vector<YulString> currentPath{};
|
||||
|
||||
void visit(YulString _function)
|
||||
{
|
||||
if (visited.count(_function))
|
||||
return;
|
||||
if (
|
||||
auto it = find(currentPath.begin(), currentPath.end(), _function);
|
||||
it != currentPath.end()
|
||||
)
|
||||
containedInCycle.insert(it, currentPath.end());
|
||||
else
|
||||
{
|
||||
currentPath.emplace_back(_function);
|
||||
if (callGraph.functionCalls.count(_function))
|
||||
for (auto const& child: callGraph.functionCalls.at(_function))
|
||||
visit(child);
|
||||
currentPath.pop_back();
|
||||
visited.insert(_function);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
set<YulString> CallGraph::recursiveFunctions() const
|
||||
{
|
||||
CallGraphCycleFinder cycleFinder{*this};
|
||||
// Visiting the root only is not enough, since there may be disconnected recursive functions.
|
||||
for (auto const& call: functionCalls)
|
||||
cycleFinder.visit(call.first);
|
||||
return cycleFinder.containedInCycle;
|
||||
}
|
||||
|
||||
CallGraph CallGraphGenerator::callGraph(Block const& _ast)
|
||||
{
|
||||
CallGraphGenerator gen;
|
||||
|
@ -35,6 +35,10 @@ struct CallGraph
|
||||
{
|
||||
std::map<YulString, std::set<YulString>> functionCalls;
|
||||
std::set<YulString> functionsWithLoops;
|
||||
/// @returns the set of functions contained in cycles in the call graph, i.e.
|
||||
/// functions that are part of a (mutual) recursion.
|
||||
/// Note that this does not include functions that merely call recursive functions.
|
||||
std::set<YulString> recursiveFunctions() const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -102,33 +102,13 @@ map<YulString, SideEffects> SideEffectsPropagator::sideEffects(
|
||||
// is actually a bit different from "not movable".
|
||||
|
||||
map<YulString, SideEffects> ret;
|
||||
for (auto const& function: _directCallGraph.functionsWithLoops)
|
||||
for (auto const& function: _directCallGraph.functionsWithLoops + _directCallGraph.recursiveFunctions())
|
||||
{
|
||||
ret[function].movable = false;
|
||||
ret[function].sideEffectFree = false;
|
||||
ret[function].sideEffectFreeIfNoMSize = false;
|
||||
}
|
||||
|
||||
// Detect recursive functions.
|
||||
for (auto const& call: _directCallGraph.functionCalls)
|
||||
{
|
||||
// TODO we could shortcut the search as soon as we find a
|
||||
// function that has as bad side-effects as we can
|
||||
// ever achieve via recursion.
|
||||
auto search = [&](YulString const& _functionName, util::CycleDetector<YulString>& _cycleDetector, size_t) {
|
||||
for (auto const& callee: _directCallGraph.functionCalls.at(_functionName))
|
||||
if (!_dialect.builtin(callee))
|
||||
if (_cycleDetector.run(callee))
|
||||
return;
|
||||
};
|
||||
if (util::CycleDetector<YulString>(search).run(call.first))
|
||||
{
|
||||
ret[call.first].movable = false;
|
||||
ret[call.first].sideEffectFree = false;
|
||||
ret[call.first].sideEffectFreeIfNoMSize = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& call: _directCallGraph.functionCalls)
|
||||
{
|
||||
YulString funName = call.first;
|
||||
|
Loading…
Reference in New Issue
Block a user