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