// 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; }