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::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