| // Copyright 2016 The SwiftShader Authors. All Rights Reserved. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //    http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #include "AnalyzeCallDepth.h" | 
 |  | 
 | static TIntermSequence::iterator | 
 | traverseCaseBody(AnalyzeCallDepth* analysis, | 
 | 				 TIntermSequence::iterator& start, | 
 | 				 const TIntermSequence::iterator& end) { | 
 | 	TIntermSequence::iterator current = start; | 
 | 	for (++current; current != end; ++current) | 
 | 	{ | 
 | 		(*current)->traverse(analysis); | 
 | 		if((*current)->getAsBranchNode()) // Kill, Break, Continue or Return | 
 | 		{ | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	return current; | 
 | } | 
 |  | 
 |  | 
 | AnalyzeCallDepth::FunctionNode::FunctionNode(TIntermAggregate *node) : node(node) | 
 | { | 
 | 	visit = PreVisit; | 
 | 	callDepth = 0; | 
 | } | 
 |  | 
 | const TString &AnalyzeCallDepth::FunctionNode::getName() const | 
 | { | 
 | 	return node->getName(); | 
 | } | 
 |  | 
 | void AnalyzeCallDepth::FunctionNode::addCallee(AnalyzeCallDepth::FunctionNode *callee) | 
 | { | 
 | 	for(size_t i = 0; i < callees.size(); i++) | 
 | 	{ | 
 | 		if(callees[i] == callee) | 
 | 		{ | 
 | 			return; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	callees.push_back(callee); | 
 | } | 
 |  | 
 | unsigned int AnalyzeCallDepth::FunctionNode::analyzeCallDepth(AnalyzeCallDepth *analyzeCallDepth) | 
 | { | 
 | 	ASSERT(visit == PreVisit); | 
 | 	ASSERT(analyzeCallDepth); | 
 |  | 
 | 	callDepth = 0; | 
 | 	visit = InVisit; | 
 |  | 
 | 	for(size_t i = 0; i < callees.size(); i++) | 
 | 	{ | 
 | 		unsigned int calleeDepth = 0; | 
 | 		switch(callees[i]->visit) | 
 | 		{ | 
 | 		case InVisit: | 
 | 			// Cycle detected (recursion) | 
 | 			return UINT_MAX; | 
 | 		case PostVisit: | 
 | 			calleeDepth = callees[i]->getLastDepth(); | 
 | 			break; | 
 | 		case PreVisit: | 
 | 			calleeDepth = callees[i]->analyzeCallDepth(analyzeCallDepth); | 
 | 			break; | 
 | 		default: | 
 | 			UNREACHABLE(callees[i]->visit); | 
 | 			break; | 
 | 		} | 
 | 		if(calleeDepth != UINT_MAX) ++calleeDepth; | 
 | 		callDepth = std::max(callDepth, calleeDepth); | 
 | 	} | 
 |  | 
 | 	visit = PostVisit; | 
 | 	return callDepth; | 
 | } | 
 |  | 
 | unsigned int AnalyzeCallDepth::FunctionNode::getLastDepth() const | 
 | { | 
 | 	return callDepth; | 
 | } | 
 |  | 
 | void AnalyzeCallDepth::FunctionNode::removeIfUnreachable() | 
 | { | 
 | 	if(visit == PreVisit) | 
 | 	{ | 
 | 		node->setOp(EOpPrototype); | 
 | 		node->getSequence().resize(1);   // Remove function body | 
 | 	} | 
 | } | 
 |  | 
 | AnalyzeCallDepth::AnalyzeCallDepth(TIntermNode *root) | 
 | 	: TIntermTraverser(true, false, true, false), | 
 | 	  currentFunction(0) | 
 | { | 
 | 	root->traverse(this); | 
 | } | 
 |  | 
 | AnalyzeCallDepth::~AnalyzeCallDepth() | 
 | { | 
 | 	for(size_t i = 0; i < functions.size(); i++) | 
 | 	{ | 
 | 		delete functions[i]; | 
 | 	} | 
 | } | 
 |  | 
 | bool AnalyzeCallDepth::visitSwitch(Visit visit, TIntermSwitch *node) | 
 | { | 
 | 	TIntermTyped* switchValue = node->getInit(); | 
 | 	TIntermAggregate* opList = node->getStatementList(); | 
 |  | 
 | 	if(!switchValue || !opList) | 
 | 	{ | 
 | 		return false; | 
 | 	} | 
 |  | 
 | 	// TODO: We need to dig into switch statement cases from | 
 | 	// visitSwitch for all traversers. Is there a way to | 
 | 	// preserve existing functionality while moving the iteration | 
 | 	// to the general traverser? | 
 | 	TIntermSequence& sequence = opList->getSequence(); | 
 | 	TIntermSequence::iterator it = sequence.begin(); | 
 | 	TIntermSequence::iterator defaultIt = sequence.end(); | 
 | 	for(; it != sequence.end(); ++it) | 
 | 	{ | 
 | 		TIntermCase* currentCase = (*it)->getAsCaseNode(); | 
 | 		if(currentCase) | 
 | 		{ | 
 | 			TIntermSequence::iterator caseIt = it; | 
 | 			TIntermTyped* condition = currentCase->getCondition(); | 
 | 			if(condition) // non default case | 
 | 			{ | 
 | 				condition->traverse(this); | 
 | 				traverseCaseBody(this, caseIt, sequence.end()); | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				defaultIt = it; // The default case might not be the last case, keep it for last | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// If there's a default case, traverse it here | 
 | 	if(defaultIt != sequence.end()) | 
 | 	{ | 
 | 		traverseCaseBody(this, defaultIt, sequence.end()); | 
 | 	} | 
 | 	return false; | 
 | } | 
 |  | 
 | bool AnalyzeCallDepth::visitAggregate(Visit visit, TIntermAggregate *node) | 
 | { | 
 | 	switch(node->getOp()) | 
 | 	{ | 
 | 	case EOpFunction:   // Function definition | 
 | 		{ | 
 | 			if(visit == PreVisit) | 
 | 			{ | 
 | 				currentFunction = findFunctionByName(node->getName()); | 
 |  | 
 | 				if(!currentFunction) | 
 | 				{ | 
 | 					currentFunction = new FunctionNode(node); | 
 | 					functions.push_back(currentFunction); | 
 | 				} | 
 | 			} | 
 | 			else if(visit == PostVisit) | 
 | 			{ | 
 | 				currentFunction = 0; | 
 | 			} | 
 | 		} | 
 | 		break; | 
 | 	case EOpFunctionCall: | 
 | 		{ | 
 | 			if(!node->isUserDefined()) | 
 | 			{ | 
 | 				return true;   // Check the arguments for function calls | 
 | 			} | 
 |  | 
 | 			if(visit == PreVisit) | 
 | 			{ | 
 | 				FunctionNode *function = findFunctionByName(node->getName()); | 
 |  | 
 | 				if(!function) | 
 | 				{ | 
 | 					function = new FunctionNode(node); | 
 | 					functions.push_back(function); | 
 | 				} | 
 |  | 
 | 				if(currentFunction) | 
 | 				{ | 
 | 					currentFunction->addCallee(function); | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					globalFunctionCalls.insert(function); | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		break; | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | unsigned int AnalyzeCallDepth::analyzeCallDepth() | 
 | { | 
 | 	FunctionNode *main = findFunctionByName("main("); | 
 |  | 
 | 	if(!main) | 
 | 	{ | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	unsigned int depth = main->analyzeCallDepth(this); | 
 | 	if(depth != UINT_MAX) ++depth; | 
 |  | 
 | 	for(FunctionSet::iterator globalCall = globalFunctionCalls.begin(); globalCall != globalFunctionCalls.end(); globalCall++) | 
 | 	{ | 
 | 		unsigned int globalDepth = (*globalCall)->analyzeCallDepth(this); | 
 | 		if(globalDepth != UINT_MAX) ++globalDepth; | 
 |  | 
 | 		if(globalDepth > depth) | 
 | 		{ | 
 | 			depth = globalDepth; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	for(size_t i = 0; i < functions.size(); i++) | 
 | 	{ | 
 | 		functions[i]->removeIfUnreachable(); | 
 | 	} | 
 |  | 
 | 	return depth; | 
 | } | 
 |  | 
 | AnalyzeCallDepth::FunctionNode *AnalyzeCallDepth::findFunctionByName(const TString &name) | 
 | { | 
 | 	for(size_t i = 0; i < functions.size(); i++) | 
 | 	{ | 
 | 		if(functions[i]->getName() == name) | 
 | 		{ | 
 | 			return functions[i]; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  |