Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 1 | // |
| 2 | // Copyright (c) 2016 The ANGLE Project Authors. All rights reserved. |
| 3 | // Use of this source code is governed by a BSD-style license that can be |
| 4 | // found in the LICENSE file. |
| 5 | // |
Olli Etuaho | 8349843 | 2017-05-19 14:57:23 +0300 | [diff] [blame] | 6 | // DeferGlobalInitializers is an AST traverser that moves global initializers into a block in the |
| 7 | // beginning of main(). This enables initialization of globals with uniforms or non-constant |
| 8 | // globals, as allowed by the WebGL spec. Some initializers referencing non-constants may need to be |
| 9 | // unfolded into if statements in HLSL - this kind of steps should be done after |
| 10 | // DeferGlobalInitializers is run. |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 11 | // |
Olli Etuaho | 0ffc441 | 2017-05-19 14:18:55 +0300 | [diff] [blame] | 12 | // It can also initialize all uninitialized globals. |
| 13 | // |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 14 | |
| 15 | #include "compiler/translator/DeferGlobalInitializers.h" |
| 16 | |
Olli Etuaho | 9cbc07c | 2017-05-10 18:22:01 +0300 | [diff] [blame] | 17 | #include "compiler/translator/FindMain.h" |
Olli Etuaho | 0ffc441 | 2017-05-19 14:18:55 +0300 | [diff] [blame] | 18 | #include "compiler/translator/InitializeVariables.h" |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 19 | #include "compiler/translator/IntermNode.h" |
| 20 | #include "compiler/translator/SymbolTable.h" |
| 21 | |
Jamie Madill | 45bcc78 | 2016-11-07 13:58:48 -0500 | [diff] [blame] | 22 | namespace sh |
| 23 | { |
| 24 | |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 25 | namespace |
| 26 | { |
| 27 | |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 28 | void GetDeferredInitializers(TIntermDeclaration *declaration, |
Olli Etuaho | 0ffc441 | 2017-05-19 14:18:55 +0300 | [diff] [blame] | 29 | bool initializeUninitializedGlobals, |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 30 | TIntermSequence *deferredInitializersOut) |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 31 | { |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 32 | // We iterate with an index instead of using an iterator since we're replacing the children of |
| 33 | // declaration inside the loop. |
| 34 | for (size_t i = 0; i < declaration->getSequence()->size(); ++i) |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 35 | { |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 36 | TIntermNode *declarator = declaration->getSequence()->at(i); |
| 37 | TIntermBinary *init = declarator->getAsBinaryNode(); |
| 38 | if (init) |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 39 | { |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 40 | TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode(); |
| 41 | ASSERT(symbolNode); |
| 42 | TIntermTyped *expression = init->getRight(); |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 43 | |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 44 | if ((expression->getQualifier() != EvqConst || |
| 45 | (expression->getAsConstantUnion() == nullptr && |
| 46 | !expression->isConstructorWithOnlyConstantUnionParameters()))) |
Olli Etuaho | d4f4c11 | 2016-04-15 15:11:24 +0300 | [diff] [blame] | 47 | { |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 48 | // For variables which are not constant, defer their real initialization until |
| 49 | // after we initialize uniforms. |
| 50 | // Deferral is done also in any cases where the variable has not been constant |
| 51 | // folded, since otherwise there's a chance that HLSL output will generate extra |
| 52 | // statements from the initializer expression. |
| 53 | TIntermBinary *deferredInit = |
| 54 | new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight()); |
| 55 | deferredInitializersOut->push_back(deferredInit); |
| 56 | |
| 57 | // Change const global to a regular global if its initialization is deferred. |
| 58 | // This can happen if ANGLE has not been able to fold the constant expression used |
| 59 | // as an initializer. |
| 60 | ASSERT(symbolNode->getQualifier() == EvqConst || |
| 61 | symbolNode->getQualifier() == EvqGlobal); |
| 62 | if (symbolNode->getQualifier() == EvqConst) |
Olli Etuaho | d4f4c11 | 2016-04-15 15:11:24 +0300 | [diff] [blame] | 63 | { |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 64 | // All of the siblings in the same declaration need to have consistent |
| 65 | // qualifiers. |
| 66 | auto *siblings = declaration->getSequence(); |
| 67 | for (TIntermNode *siblingNode : *siblings) |
Olli Etuaho | d4f4c11 | 2016-04-15 15:11:24 +0300 | [diff] [blame] | 68 | { |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 69 | TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode(); |
| 70 | if (siblingBinary) |
| 71 | { |
| 72 | ASSERT(siblingBinary->getOp() == EOpInitialize); |
| 73 | siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal); |
| 74 | } |
| 75 | siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal); |
Olli Etuaho | d4f4c11 | 2016-04-15 15:11:24 +0300 | [diff] [blame] | 76 | } |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 77 | // This node is one of the siblings. |
| 78 | ASSERT(symbolNode->getQualifier() == EvqGlobal); |
Olli Etuaho | d4f4c11 | 2016-04-15 15:11:24 +0300 | [diff] [blame] | 79 | } |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 80 | // Remove the initializer from the global scope and just declare the global instead. |
| 81 | declaration->replaceChildNode(init, symbolNode); |
Olli Etuaho | d4f4c11 | 2016-04-15 15:11:24 +0300 | [diff] [blame] | 82 | } |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 83 | } |
Olli Etuaho | 0ffc441 | 2017-05-19 14:18:55 +0300 | [diff] [blame] | 84 | else if (initializeUninitializedGlobals) |
| 85 | { |
| 86 | TIntermSymbol *symbolNode = declarator->getAsSymbolNode(); |
| 87 | ASSERT(symbolNode); |
Martin Radev | 69056a1 | 2017-05-18 11:14:50 +0300 | [diff] [blame] | 88 | |
| 89 | // Ignore ANGLE internal variables. |
| 90 | if (symbolNode->getName().isInternal()) |
| 91 | continue; |
| 92 | |
Olli Etuaho | 0ffc441 | 2017-05-19 14:18:55 +0300 | [diff] [blame] | 93 | if (symbolNode->getQualifier() == EvqGlobal && symbolNode->getSymbol() != "") |
| 94 | { |
| 95 | TIntermSequence *initCode = CreateInitCode(symbolNode); |
| 96 | deferredInitializersOut->insert(deferredInitializersOut->end(), initCode->begin(), |
| 97 | initCode->end()); |
| 98 | } |
| 99 | } |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 100 | } |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 101 | } |
| 102 | |
Olli Etuaho | 8349843 | 2017-05-19 14:57:23 +0300 | [diff] [blame] | 103 | void InsertInitCodeToMain(TIntermBlock *root, TIntermSequence *deferredInitializers) |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 104 | { |
Olli Etuaho | 8349843 | 2017-05-19 14:57:23 +0300 | [diff] [blame] | 105 | // Insert init code as a block to the beginning of the main() function. |
| 106 | TIntermBlock *initGlobalsBlock = new TIntermBlock(); |
| 107 | initGlobalsBlock->getSequence()->swap(*deferredInitializers); |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 108 | |
Martin Radev | b50ccd3 | 2017-07-06 17:09:58 +0300 | [diff] [blame^] | 109 | TIntermBlock *mainBody = FindMainBody(root); |
Olli Etuaho | 8349843 | 2017-05-19 14:57:23 +0300 | [diff] [blame] | 110 | mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsBlock); |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | } // namespace |
| 114 | |
Olli Etuaho | 0ffc441 | 2017-05-19 14:18:55 +0300 | [diff] [blame] | 115 | void DeferGlobalInitializers(TIntermBlock *root, bool initializeUninitializedGlobals) |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 116 | { |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 117 | TIntermSequence *deferredInitializers = new TIntermSequence(); |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 118 | |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 119 | // Loop over all global statements and process the declarations. This is simpler than using a |
| 120 | // traverser. |
Olli Etuaho | 0ffc441 | 2017-05-19 14:18:55 +0300 | [diff] [blame] | 121 | for (TIntermNode *statement : *root->getSequence()) |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 122 | { |
Olli Etuaho | 0ffc441 | 2017-05-19 14:18:55 +0300 | [diff] [blame] | 123 | TIntermDeclaration *declaration = statement->getAsDeclarationNode(); |
| 124 | if (declaration) |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 125 | { |
Olli Etuaho | 0ffc441 | 2017-05-19 14:18:55 +0300 | [diff] [blame] | 126 | GetDeferredInitializers(declaration, initializeUninitializedGlobals, |
| 127 | deferredInitializers); |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 128 | } |
| 129 | } |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 130 | |
| 131 | // Add the function with initialization and the call to that. |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 132 | if (!deferredInitializers->empty()) |
| 133 | { |
Olli Etuaho | 8349843 | 2017-05-19 14:57:23 +0300 | [diff] [blame] | 134 | InsertInitCodeToMain(root, deferredInitializers); |
Olli Etuaho | 1dd07a6 | 2017-05-12 17:01:22 +0300 | [diff] [blame] | 135 | } |
Olli Etuaho | 3d932d8 | 2016-04-12 11:10:30 +0300 | [diff] [blame] | 136 | } |
Jamie Madill | 45bcc78 | 2016-11-07 13:58:48 -0500 | [diff] [blame] | 137 | |
| 138 | } // namespace sh |