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