| // |
| // 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/InitializeVariables.h" |
| |
| #include "angle_gl.h" |
| #include "common/debug.h" |
| #include "compiler/translator/Compiler.h" |
| #include "compiler/translator/StaticType.h" |
| #include "compiler/translator/SymbolTable.h" |
| #include "compiler/translator/tree_util/FindMain.h" |
| #include "compiler/translator/tree_util/IntermNode_util.h" |
| #include "compiler/translator/tree_util/IntermTraverse.h" |
| #include "compiler/translator/util.h" |
| |
| namespace sh |
| { |
| |
| namespace |
| { |
| |
| void AddArrayZeroInitSequence(const TIntermTyped *initializedNode, |
| bool canUseLoopsToInitialize, |
| bool highPrecisionSupported, |
| TIntermSequence *initSequenceOut, |
| TSymbolTable *symbolTable); |
| |
| void AddStructZeroInitSequence(const TIntermTyped *initializedNode, |
| bool canUseLoopsToInitialize, |
| bool highPrecisionSupported, |
| TIntermSequence *initSequenceOut, |
| TSymbolTable *symbolTable); |
| |
| TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode) |
| { |
| TIntermTyped *zero = CreateZeroNode(initializedNode->getType()); |
| return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero); |
| } |
| |
| void AddZeroInitSequence(const TIntermTyped *initializedNode, |
| bool canUseLoopsToInitialize, |
| bool highPrecisionSupported, |
| TIntermSequence *initSequenceOut, |
| TSymbolTable *symbolTable) |
| { |
| if (initializedNode->isArray()) |
| { |
| AddArrayZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported, |
| initSequenceOut, symbolTable); |
| } |
| else if (initializedNode->getType().isStructureContainingArrays() || |
| initializedNode->getType().isNamelessStruct()) |
| { |
| AddStructZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported, |
| initSequenceOut, symbolTable); |
| } |
| else |
| { |
| initSequenceOut->push_back(CreateZeroInitAssignment(initializedNode)); |
| } |
| } |
| |
| void AddStructZeroInitSequence(const TIntermTyped *initializedNode, |
| bool canUseLoopsToInitialize, |
| bool highPrecisionSupported, |
| TIntermSequence *initSequenceOut, |
| TSymbolTable *symbolTable) |
| { |
| ASSERT(initializedNode->getBasicType() == EbtStruct); |
| const TStructure *structType = initializedNode->getType().getStruct(); |
| for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i) |
| { |
| TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct, |
| initializedNode->deepCopy(), CreateIndexNode(i)); |
| // Structs can't be defined inside structs, so the type of a struct field can't be a |
| // nameless struct. |
| ASSERT(!element->getType().isNamelessStruct()); |
| AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported, |
| initSequenceOut, symbolTable); |
| } |
| } |
| |
| void AddArrayZeroInitStatementList(const TIntermTyped *initializedNode, |
| bool canUseLoopsToInitialize, |
| bool highPrecisionSupported, |
| TIntermSequence *initSequenceOut, |
| TSymbolTable *symbolTable) |
| { |
| for (unsigned int i = 0; i < initializedNode->getOutermostArraySize(); ++i) |
| { |
| TIntermBinary *element = |
| new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(), CreateIndexNode(i)); |
| AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported, |
| initSequenceOut, symbolTable); |
| } |
| } |
| |
| void AddArrayZeroInitForLoop(const TIntermTyped *initializedNode, |
| bool highPrecisionSupported, |
| TIntermSequence *initSequenceOut, |
| TSymbolTable *symbolTable) |
| { |
| ASSERT(initializedNode->isArray()); |
| const TType *mediumpIndexType = StaticType::Get<EbtInt, EbpMedium, EvqTemporary, 1, 1>(); |
| const TType *highpIndexType = StaticType::Get<EbtInt, EbpHigh, EvqTemporary, 1, 1>(); |
| TVariable *indexVariable = |
| CreateTempVariable(symbolTable, highPrecisionSupported ? highpIndexType : mediumpIndexType); |
| |
| TIntermSymbol *indexSymbolNode = CreateTempSymbolNode(indexVariable); |
| TIntermDeclaration *indexInit = |
| CreateTempInitDeclarationNode(indexVariable, CreateZeroNode(indexVariable->getType())); |
| TIntermConstantUnion *arraySizeNode = CreateIndexNode(initializedNode->getOutermostArraySize()); |
| TIntermBinary *indexSmallerThanSize = |
| new TIntermBinary(EOpLessThan, indexSymbolNode->deepCopy(), arraySizeNode); |
| TIntermUnary *indexIncrement = |
| new TIntermUnary(EOpPreIncrement, indexSymbolNode->deepCopy(), nullptr); |
| |
| TIntermBlock *forLoopBody = new TIntermBlock(); |
| TIntermSequence *forLoopBodySeq = forLoopBody->getSequence(); |
| |
| TIntermBinary *element = new TIntermBinary(EOpIndexIndirect, initializedNode->deepCopy(), |
| indexSymbolNode->deepCopy()); |
| AddZeroInitSequence(element, true, highPrecisionSupported, forLoopBodySeq, symbolTable); |
| |
| TIntermLoop *forLoop = |
| new TIntermLoop(ELoopFor, indexInit, indexSmallerThanSize, indexIncrement, forLoopBody); |
| initSequenceOut->push_back(forLoop); |
| } |
| |
| void AddArrayZeroInitSequence(const TIntermTyped *initializedNode, |
| bool canUseLoopsToInitialize, |
| bool highPrecisionSupported, |
| TIntermSequence *initSequenceOut, |
| TSymbolTable *symbolTable) |
| { |
| // The array elements are assigned one by one to keep the AST compatible with ESSL 1.00 which |
| // doesn't have array assignment. We'll do this either with a for loop or just a list of |
| // statements assigning to each array index. Note that it is important to have the array init in |
| // the right order to workaround http://crbug.com/709317 |
| bool isSmallArray = initializedNode->getOutermostArraySize() <= 1u || |
| (initializedNode->getBasicType() != EbtStruct && |
| !initializedNode->getType().isArrayOfArrays() && |
| initializedNode->getOutermostArraySize() <= 3u); |
| if (initializedNode->getQualifier() == EvqFragData || |
| initializedNode->getQualifier() == EvqFragmentOut || isSmallArray || |
| !canUseLoopsToInitialize) |
| { |
| // Fragment outputs should not be indexed by non-constant indices. |
| // Also it doesn't make sense to use loops to initialize very small arrays. |
| AddArrayZeroInitStatementList(initializedNode, canUseLoopsToInitialize, |
| highPrecisionSupported, initSequenceOut, symbolTable); |
| } |
| else |
| { |
| AddArrayZeroInitForLoop(initializedNode, highPrecisionSupported, initSequenceOut, |
| symbolTable); |
| } |
| } |
| |
| void InsertInitCode(TCompiler *compiler, |
| TIntermSequence *mainBody, |
| const InitVariableList &variables, |
| TSymbolTable *symbolTable, |
| int shaderVersion, |
| const TExtensionBehavior &extensionBehavior, |
| bool canUseLoopsToInitialize, |
| bool highPrecisionSupported) |
| { |
| for (const auto &var : variables) |
| { |
| // Note that tempVariableName will reference a short-lived char array here - that's fine |
| // since we're only using it to find symbols. |
| ImmutableString tempVariableName(var.name.c_str(), var.name.length()); |
| |
| TIntermTyped *initializedSymbol = nullptr; |
| if (var.isBuiltIn()) |
| { |
| initializedSymbol = |
| ReferenceBuiltInVariable(tempVariableName, *symbolTable, shaderVersion); |
| if (initializedSymbol->getQualifier() == EvqFragData && |
| !IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers)) |
| { |
| // If GL_EXT_draw_buffers is disabled, only the 0th index of gl_FragData can be |
| // written to. |
| // TODO(oetuaho): This is a bit hacky and would be better to remove, if we came up |
| // with a good way to do it. Right now "gl_FragData" in symbol table is initialized |
| // to have the array size of MaxDrawBuffers, and the initialization happens before |
| // the shader sets the extensions it is using. |
| initializedSymbol = |
| new TIntermBinary(EOpIndexDirect, initializedSymbol, CreateIndexNode(0)); |
| } |
| } |
| else |
| { |
| initializedSymbol = ReferenceGlobalVariable(tempVariableName, *symbolTable); |
| } |
| ASSERT(initializedSymbol != nullptr); |
| |
| TIntermSequence *initCode = CreateInitCode(initializedSymbol, canUseLoopsToInitialize, |
| highPrecisionSupported, symbolTable); |
| mainBody->insert(mainBody->begin(), initCode->begin(), initCode->end()); |
| } |
| } |
| |
| class InitializeLocalsTraverser : public TIntermTraverser |
| { |
| public: |
| InitializeLocalsTraverser(int shaderVersion, |
| TSymbolTable *symbolTable, |
| bool canUseLoopsToInitialize, |
| bool highPrecisionSupported) |
| : TIntermTraverser(true, false, false, symbolTable), |
| mShaderVersion(shaderVersion), |
| mCanUseLoopsToInitialize(canUseLoopsToInitialize), |
| mHighPrecisionSupported(highPrecisionSupported) |
| {} |
| |
| protected: |
| bool visitDeclaration(Visit visit, TIntermDeclaration *node) override |
| { |
| for (TIntermNode *declarator : *node->getSequence()) |
| { |
| if (!mInGlobalScope && !declarator->getAsBinaryNode()) |
| { |
| TIntermSymbol *symbol = declarator->getAsSymbolNode(); |
| ASSERT(symbol); |
| if (symbol->variable().symbolType() == SymbolType::Empty) |
| { |
| continue; |
| } |
| |
| // Arrays may need to be initialized one element at a time, since ESSL 1.00 does not |
| // support array constructors or assigning arrays. |
| bool arrayConstructorUnavailable = |
| (symbol->isArray() || symbol->getType().isStructureContainingArrays()) && |
| mShaderVersion == 100; |
| // Nameless struct constructors can't be referred to, so they also need to be |
| // initialized one element at a time. |
| // TODO(oetuaho): Check if it makes sense to initialize using a loop, even if we |
| // could use an initializer. It could at least reduce code size for very large |
| // arrays, but could hurt runtime performance. |
| if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct()) |
| { |
| // SimplifyLoopConditions should have been run so the parent node of this node |
| // should not be a loop. |
| ASSERT(getParentNode()->getAsLoopNode() == nullptr); |
| // SeparateDeclarations should have already been run, so we don't need to worry |
| // about further declarators in this declaration depending on the effects of |
| // this declarator. |
| ASSERT(node->getSequence()->size() == 1); |
| insertStatementsInParentBlock( |
| TIntermSequence(), *CreateInitCode(symbol, mCanUseLoopsToInitialize, |
| mHighPrecisionSupported, mSymbolTable)); |
| } |
| else |
| { |
| TIntermBinary *init = |
| new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType())); |
| queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD); |
| } |
| } |
| } |
| return false; |
| } |
| |
| private: |
| int mShaderVersion; |
| bool mCanUseLoopsToInitialize; |
| bool mHighPrecisionSupported; |
| }; |
| |
| } // namespace |
| |
| TIntermSequence *CreateInitCode(const TIntermTyped *initializedSymbol, |
| bool canUseLoopsToInitialize, |
| bool highPrecisionSupported, |
| TSymbolTable *symbolTable) |
| { |
| TIntermSequence *initCode = new TIntermSequence(); |
| AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported, |
| initCode, symbolTable); |
| return initCode; |
| } |
| |
| bool InitializeUninitializedLocals(TCompiler *compiler, |
| TIntermBlock *root, |
| int shaderVersion, |
| bool canUseLoopsToInitialize, |
| bool highPrecisionSupported, |
| TSymbolTable *symbolTable) |
| { |
| InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize, |
| highPrecisionSupported); |
| root->traverse(&traverser); |
| return traverser.updateTree(compiler, root); |
| } |
| |
| bool InitializeVariables(TCompiler *compiler, |
| TIntermBlock *root, |
| const InitVariableList &vars, |
| TSymbolTable *symbolTable, |
| int shaderVersion, |
| const TExtensionBehavior &extensionBehavior, |
| bool canUseLoopsToInitialize, |
| bool highPrecisionSupported) |
| { |
| TIntermBlock *body = FindMainBody(root); |
| InsertInitCode(compiler, body->getSequence(), vars, symbolTable, shaderVersion, |
| extensionBehavior, canUseLoopsToInitialize, highPrecisionSupported); |
| |
| return compiler->validateAST(root); |
| } |
| |
| } // namespace sh |