// // Copyright 2002 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/translator/tree_ops/SeparateDeclarations.h" #include "common/hash_containers.h" #include "compiler/translator/IntermRebuild.h" #include "compiler/translator/SymbolTable.h" #include "compiler/translator/util.h" namespace sh { namespace { class Separator final : private TIntermRebuild { public: Separator(TCompiler &compiler, bool separateCompoundStructDeclarations) : TIntermRebuild(compiler, true, true), mSeparateCompoundStructDeclarations(separateCompoundStructDeclarations) {} using TIntermRebuild::rebuildRoot; private: void recordModifiedStructVariables(TIntermDeclaration &node) { ASSERT(!mNewStructure); // No nested struct declarations. TIntermSequence &sequence = *node.getSequence(); if (sequence.size() <= 1 && !mSeparateCompoundStructDeclarations) { return; } TIntermTyped *declarator = sequence.at(0)->getAsTyped(); const TType &declaratorType = declarator->getType(); const TStructure *structure = declaratorType.getStruct(); // Rewrite variable declarations that specify structs AND variable(s) at the same // time. if (!structure || !declaratorType.isStructSpecifier()) { return; } if (mSeparateCompoundStructDeclarations && sequence.size() == 1) { if (TIntermSymbol *symbol = declarator->getAsSymbolNode(); symbol != nullptr) { if (symbol->variable().symbolType() == SymbolType::Empty) { return; } } } // By default, struct specifier changes for all variables except the first one. uint32_t index = 1; if (structure->symbolType() == SymbolType::Empty) { TStructure *newStructure = new TStructure(&mSymbolTable, kEmptyImmutableString, &structure->fields(), SymbolType::AngleInternal); newStructure->setAtGlobalScope(structure->atGlobalScope()); structure = newStructure; // Adding name causes the struct type to change, so also the first variable variable // needs rewriting. index = 0; } if (mSeparateCompoundStructDeclarations) { mNewStructure = structure; // Separating struct and variable declaration causes the variable type to change // from specifying to not-specifying, so also the first variable needs rewriting. index = 0; } for (; index < sequence.size(); ++index) { Declaration decl = ViewDeclaration(node, index); const TVariable &var = decl.symbol.variable(); const TType &varType = var.getType(); const bool newTypeIsSpecifier = index == 0 && !mSeparateCompoundStructDeclarations; TType *newType = new TType(structure, newTypeIsSpecifier); newType->setQualifier(varType.getQualifier()); newType->makeArrays(varType.getArraySizes()); TVariable *newVar = new TVariable(&mSymbolTable, var.name(), newType, var.symbolType()); mStructVariables.insert(std::make_pair(&var, newVar)); } } PreResult visitDeclarationPre(TIntermDeclaration &node) override { recordModifiedStructVariables(node); return node; } PostResult visitDeclarationPost(TIntermDeclaration &node) override { TIntermSequence &sequence = *node.getSequence(); if (sequence.size() <= 1 && !mNewStructure) { return node; } std::vector replacements; if (mNewStructure) { TType *newType = new TType(mNewStructure, true); if (mNewStructure->atGlobalScope()) { newType->setQualifier(EvqGlobal); } TVariable *structVar = new TVariable(&mSymbolTable, kEmptyImmutableString, newType, SymbolType::Empty); TIntermDeclaration *replacement = new TIntermDeclaration({structVar}); replacement->setLine(node.getLine()); replacements.push_back(replacement); mNewStructure = nullptr; } for (uint32_t index = 0; index < sequence.size(); ++index) { TIntermTyped *declarator = sequence.at(index)->getAsTyped(); TIntermDeclaration *replacement = new TIntermDeclaration({declarator}); replacement->setLine(declarator->getLine()); replacements.push_back(replacement); } return PostResult::Multi(std::move(replacements)); } PreResult visitSymbolPre(TIntermSymbol &symbolNode) override { auto it = mStructVariables.find(&symbolNode.variable()); if (it == mStructVariables.end()) { return symbolNode; } return *new TIntermSymbol(it->second); } PreResult visitFunctionPrototypePre(TIntermFunctionPrototype &node) override { const TFunction *function = node.getFunction(); auto it = mFunctionsToReplace.find(function); if (it != mFunctionsToReplace.end()) { TIntermFunctionPrototype *newFuncProto = new TIntermFunctionPrototype(it->second); return newFuncProto; } else if (node.getType().isStructSpecifier()) { const TType &oldType = node.getType(); const TStructure *structure = oldType.getStruct(); // Name unnamed inline structs if (structure->symbolType() == SymbolType::Empty) { TStructure *newStructure = new TStructure(&mSymbolTable, kEmptyImmutableString, &structure->fields(), SymbolType::AngleInternal); newStructure->setAtGlobalScope(structure->atGlobalScope()); structure = newStructure; } TType *newType = new TType(structure, true); if (structure->atGlobalScope()) { newType->setQualifier(EvqGlobal); } TVariable *structVar = new TVariable(&mSymbolTable, ImmutableString(""), newType, SymbolType::Empty); TType *returnType = new TType(structure, false); if (oldType.isArray()) { returnType->makeArrays(oldType.getArraySizes()); } returnType->setQualifier(oldType.getQualifier()); const TFunction *oldFunc = function; ASSERT(oldFunc->symbolType() == SymbolType::UserDefined); const TFunction *newFunc = cloneFunctionAndChangeReturnType(oldFunc, returnType); mFunctionsToReplace[oldFunc] = newFunc; if (getParentNode()->getAsFunctionDefinition() != nullptr) { mNewFunctionReturnStructDeclaration = new TIntermDeclaration({structVar}); return new TIntermFunctionPrototype(newFunc); } return PreResult::Multi( {new TIntermDeclaration({structVar}), new TIntermFunctionPrototype(newFunc)}); } return node; } PostResult visitFunctionDefinitionPost(TIntermFunctionDefinition &node) override { if (mNewFunctionReturnStructDeclaration) { return PostResult::Multi( {std::exchange(mNewFunctionReturnStructDeclaration, nullptr), &node}); } return node; } PreResult visitAggregatePre(TIntermAggregate &node) override { const TFunction *function = node.getFunction(); auto it = mFunctionsToReplace.find(function); if (it != mFunctionsToReplace.end()) { TIntermAggregate *replacementNode = TIntermAggregate::CreateFunctionCall(*it->second, node.getSequence()); return PreResult(replacementNode, VisitBits::Children); } return node; } private: const TFunction *cloneFunctionAndChangeReturnType(const TFunction *oldFunc, const TType *newReturnType) { ASSERT(oldFunc->symbolType() == SymbolType::UserDefined); TFunction *newFunc = new TFunction(&mSymbolTable, oldFunc->name(), oldFunc->symbolType(), newReturnType, oldFunc->isKnownToNotHaveSideEffects()); if (oldFunc->isDefined()) { newFunc->setDefined(); } if (oldFunc->hasPrototypeDeclaration()) { newFunc->setHasPrototypeDeclaration(); } const size_t paramCount = oldFunc->getParamCount(); for (size_t i = 0; i < paramCount; ++i) { const TVariable *var = oldFunc->getParam(i); newFunc->addParameter(var); } return newFunc; } angle::HashMap mFunctionsToReplace; // New structure separated from function declaration. TIntermDeclaration *mNewFunctionReturnStructDeclaration = nullptr; // New structure from compound declaration. const TStructure *mNewStructure = nullptr; // Old struct variable to new struct variable mapping. angle::HashMap mStructVariables; const bool mSeparateCompoundStructDeclarations; }; } // namespace bool SeparateDeclarations(TCompiler &compiler, TIntermBlock &root, bool separateCompoundStructDeclarations) { Separator separator(compiler, separateCompoundStructDeclarations); return separator.rebuildRoot(root); } } // namespace sh