// | |
// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style license that can be | |
// found in the LICENSE file. | |
// | |
#include "compiler/AnalyzeCallDepth.h" | |
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++) | |
{ | |
switch(callees[i]->visit) | |
{ | |
case InVisit: | |
// Cycle detected (recursion) | |
return UINT_MAX; | |
case PostVisit: | |
callDepth = std::max(callDepth, 1 + callees[i]->getLastDepth()); | |
break; | |
case PreVisit: | |
callDepth = std::max(callDepth, 1 + callees[i]->analyzeCallDepth(analyzeCallDepth)); | |
break; | |
default: | |
UNREACHABLE(); | |
break; | |
} | |
} | |
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::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 false; | |
} | |
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 = 1 + main->analyzeCallDepth(this); | |
for(FunctionSet::iterator globalCall = globalFunctionCalls.begin(); globalCall != globalFunctionCalls.end(); globalCall++) | |
{ | |
unsigned int globalDepth = 1 + (*globalCall)->analyzeCallDepth(this); | |
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; | |
} | |