mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Extract recursive function detection from Semantics into the CallGraph.
This commit is contained in:
parent
bd6cd027a3
commit
99d831d7d1
@ -30,6 +30,47 @@ using namespace solidity;
|
|||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace solidity::util;
|
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)
|
CallGraph CallGraphGenerator::callGraph(Block const& _ast)
|
||||||
{
|
{
|
||||||
CallGraphGenerator gen;
|
CallGraphGenerator gen;
|
||||||
|
@ -35,6 +35,10 @@ struct CallGraph
|
|||||||
{
|
{
|
||||||
std::map<YulString, std::set<YulString>> functionCalls;
|
std::map<YulString, std::set<YulString>> functionCalls;
|
||||||
std::set<YulString> functionsWithLoops;
|
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".
|
// is actually a bit different from "not movable".
|
||||||
|
|
||||||
map<YulString, SideEffects> ret;
|
map<YulString, SideEffects> ret;
|
||||||
for (auto const& function: _directCallGraph.functionsWithLoops)
|
for (auto const& function: _directCallGraph.functionsWithLoops + _directCallGraph.recursiveFunctions())
|
||||||
{
|
{
|
||||||
ret[function].movable = false;
|
ret[function].movable = false;
|
||||||
ret[function].sideEffectFree = false;
|
ret[function].sideEffectFree = false;
|
||||||
ret[function].sideEffectFreeIfNoMSize = 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)
|
for (auto const& call: _directCallGraph.functionCalls)
|
||||||
{
|
{
|
||||||
YulString funName = call.first;
|
YulString funName = call.first;
|
||||||
|
Loading…
Reference in New Issue
Block a user