Path: blob/main_old/src/compiler/translator/CallDAG.cpp
1693 views
//1// Copyright 2002 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//56// CallDAG.h: Implements a call graph DAG of functions to be re-used accross7// analyses, allows to efficiently traverse the functions in topological8// order.910#include "compiler/translator/CallDAG.h"1112#include "compiler/translator/Diagnostics.h"13#include "compiler/translator/SymbolTable.h"14#include "compiler/translator/tree_util/IntermTraverse.h"1516namespace sh17{1819// The CallDAGCreator does all the processing required to create the CallDAG20// structure so that the latter contains only the necessary variables.21class CallDAG::CallDAGCreator : public TIntermTraverser22{23public:24CallDAGCreator(TDiagnostics *diagnostics)25: TIntermTraverser(true, false, false),26mDiagnostics(diagnostics),27mCurrentFunction(nullptr),28mCurrentIndex(0)29{}3031InitResult assignIndices()32{33int skipped = 0;34for (auto &it : mFunctions)35{36// Skip unimplemented functions37if (it.second.definitionNode)38{39InitResult result = assignIndicesInternal(&it.second);40if (result != INITDAG_SUCCESS)41{42return result;43}44}45else46{47skipped++;48}49}5051ASSERT(mFunctions.size() == mCurrentIndex + skipped);52return INITDAG_SUCCESS;53}5455void fillDataStructures(std::vector<Record> *records, std::map<int, int> *idToIndex)56{57ASSERT(records->empty());58ASSERT(idToIndex->empty());5960records->resize(mCurrentIndex);6162for (auto &it : mFunctions)63{64CreatorFunctionData &data = it.second;65// Skip unimplemented functions66if (!data.definitionNode)67{68continue;69}70ASSERT(data.index < records->size());71Record &record = (*records)[data.index];7273record.node = data.definitionNode;7475record.callees.reserve(data.callees.size());76for (auto &callee : data.callees)77{78record.callees.push_back(static_cast<int>(callee->index));79}8081(*idToIndex)[it.first] = static_cast<int>(data.index);82}83}8485private:86struct CreatorFunctionData87{88CreatorFunctionData()89: definitionNode(nullptr), name(""), index(0), indexAssigned(false), visiting(false)90{}9192std::set<CreatorFunctionData *> callees;93TIntermFunctionDefinition *definitionNode;94ImmutableString name;95size_t index;96bool indexAssigned;97bool visiting;98};99100bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override101{102// Create the record if need be and remember the definition node.103mCurrentFunction = &mFunctions[node->getFunction()->uniqueId().get()];104// Name will be overwritten here. If we've already traversed the prototype of this function,105// it should have had the same name.106ASSERT(mCurrentFunction->name == "" ||107mCurrentFunction->name == node->getFunction()->name());108mCurrentFunction->name = node->getFunction()->name();109mCurrentFunction->definitionNode = node;110111node->getBody()->traverse(this);112mCurrentFunction = nullptr;113return false;114}115116void visitFunctionPrototype(TIntermFunctionPrototype *node) override117{118ASSERT(mCurrentFunction == nullptr);119120// Function declaration, create an empty record.121auto &record = mFunctions[node->getFunction()->uniqueId().get()];122record.name = node->getFunction()->name();123}124125// Track functions called from another function.126bool visitAggregate(Visit visit, TIntermAggregate *node) override127{128if (node->getOp() == EOpCallFunctionInAST)129{130// Function call, add the callees131auto it = mFunctions.find(node->getFunction()->uniqueId().get());132ASSERT(it != mFunctions.end());133134// We might be traversing the initializer of a global variable. Even though function135// calls in global scope are forbidden by the parser, some subsequent AST136// transformations can add them to emulate particular features.137if (mCurrentFunction)138{139mCurrentFunction->callees.insert(&it->second);140}141}142return true;143}144145// Recursively assigns indices to a sub DAG146InitResult assignIndicesInternal(CreatorFunctionData *root)147{148// Iterative implementation of the index assignment algorithm. A recursive version149// would be prettier but since the CallDAG creation runs before the limiting of the150// call depth, we might get stack overflows (computation of the call depth uses the151// CallDAG).152153ASSERT(root);154155if (root->indexAssigned)156{157return INITDAG_SUCCESS;158}159160// If we didn't have to detect recursion, functionsToProcess could be a simple queue161// in which we add the function being processed's callees. However in order to detect162// recursion we need to know which functions we are currently visiting. For that reason163// functionsToProcess will look like a concatenation of segments of the form164// [F visiting = true, subset of F callees with visiting = false] and the following165// segment (if any) will be start with a callee of F.166// This way we can remember when we started visiting a function, to put visiting back167// to false.168TVector<CreatorFunctionData *> functionsToProcess;169functionsToProcess.push_back(root);170171InitResult result = INITDAG_SUCCESS;172173std::stringstream errorStream = sh::InitializeStream<std::stringstream>();174175while (!functionsToProcess.empty())176{177CreatorFunctionData *function = functionsToProcess.back();178179if (function->visiting)180{181function->visiting = false;182function->index = mCurrentIndex++;183function->indexAssigned = true;184185functionsToProcess.pop_back();186continue;187}188189if (!function->definitionNode)190{191errorStream << "Undefined function '" << function->name192<< "()' used in the following call chain:";193result = INITDAG_UNDEFINED;194break;195}196197if (function->indexAssigned)198{199functionsToProcess.pop_back();200continue;201}202203function->visiting = true;204205for (auto callee : function->callees)206{207functionsToProcess.push_back(callee);208209// Check if the callee is already being visited after pushing it so that it appears210// in the chain printed in the info log.211if (callee->visiting)212{213errorStream << "Recursive function call in the following call chain:";214result = INITDAG_RECURSION;215break;216}217}218219if (result != INITDAG_SUCCESS)220{221break;222}223}224225// The call chain is made of the function we were visiting when the error was detected.226if (result != INITDAG_SUCCESS)227{228bool first = true;229for (auto function : functionsToProcess)230{231if (function->visiting)232{233if (!first)234{235errorStream << " -> ";236}237errorStream << function->name << ")";238first = false;239}240}241if (mDiagnostics)242{243std::string errorStr = errorStream.str();244mDiagnostics->globalError(errorStr.c_str());245}246}247248return result;249}250251TDiagnostics *mDiagnostics;252253std::map<int, CreatorFunctionData> mFunctions;254CreatorFunctionData *mCurrentFunction;255size_t mCurrentIndex;256};257258// CallDAG259260CallDAG::CallDAG() {}261262CallDAG::~CallDAG() {}263264const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max();265266size_t CallDAG::findIndex(const TSymbolUniqueId &id) const267{268auto it = mFunctionIdToIndex.find(id.get());269270if (it == mFunctionIdToIndex.end())271{272return InvalidIndex;273}274else275{276return it->second;277}278}279280const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const281{282ASSERT(index != InvalidIndex && index < mRecords.size());283return mRecords[index];284}285286size_t CallDAG::size() const287{288return mRecords.size();289}290291void CallDAG::clear()292{293mRecords.clear();294mFunctionIdToIndex.clear();295}296297CallDAG::InitResult CallDAG::init(TIntermNode *root, TDiagnostics *diagnostics)298{299CallDAGCreator creator(diagnostics);300301// Creates the mapping of functions to callees302root->traverse(&creator);303304// Does the topological sort and detects recursions305InitResult result = creator.assignIndices();306if (result != INITDAG_SUCCESS)307{308return result;309}310311creator.fillDataStructures(&mRecords, &mFunctionIdToIndex);312return INITDAG_SUCCESS;313}314315} // namespace sh316317318