DeferGlobalInitializers.cpp (7607B)
1 // 2 // Copyright 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 // 6 // DeferGlobalInitializers is an AST traverser that moves global initializers into a separate 7 // function that is called in the beginning of main(). This enables initialization of globals with 8 // uniforms or non-constant globals, as allowed by the WebGL spec. Some initializers referencing 9 // non-constants may need to be unfolded into if statements in HLSL - this kind of steps should be 10 // done after DeferGlobalInitializers is run. Note that it's important that the function definition 11 // is at the end of the shader, as some globals may be declared after main(). 12 // 13 // It can also initialize all uninitialized globals. 14 // 15 16 #include "compiler/translator/tree_ops/DeferGlobalInitializers.h" 17 18 #include <vector> 19 20 #include "compiler/translator/Compiler.h" 21 #include "compiler/translator/IntermNode.h" 22 #include "compiler/translator/StaticType.h" 23 #include "compiler/translator/SymbolTable.h" 24 #include "compiler/translator/tree_ops/InitializeVariables.h" 25 #include "compiler/translator/tree_util/FindMain.h" 26 #include "compiler/translator/tree_util/IntermNode_util.h" 27 #include "compiler/translator/tree_util/ReplaceVariable.h" 28 29 namespace sh 30 { 31 32 namespace 33 { 34 35 constexpr const ImmutableString kInitGlobalsString("initGlobals"); 36 37 void GetDeferredInitializers(TIntermDeclaration *declaration, 38 bool initializeUninitializedGlobals, 39 bool canUseLoopsToInitialize, 40 bool highPrecisionSupported, 41 bool forceDeferGlobalInitializers, 42 TIntermSequence *deferredInitializersOut, 43 std::vector<const TVariable *> *variablesToReplaceOut, 44 TSymbolTable *symbolTable) 45 { 46 // SeparateDeclarations should have already been run. 47 ASSERT(declaration->getSequence()->size() == 1); 48 49 TIntermNode *declarator = declaration->getSequence()->back(); 50 TIntermBinary *init = declarator->getAsBinaryNode(); 51 if (init) 52 { 53 TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode(); 54 ASSERT(symbolNode); 55 TIntermTyped *expression = init->getRight(); 56 57 if (expression->getQualifier() != EvqConst || !expression->hasConstantValue() || 58 forceDeferGlobalInitializers) 59 { 60 // For variables which are not constant, defer their real initialization until 61 // after we initialize uniforms. 62 // Deferral is done also in any cases where the variable can not be converted to a 63 // constant union, since otherwise there's a chance that HLSL output will generate extra 64 // statements from the initializer expression. 65 66 // Change const global to a regular global if its initialization is deferred. 67 // This can happen if ANGLE has not been able to fold the constant expression used 68 // as an initializer. 69 ASSERT(symbolNode->getQualifier() == EvqConst || 70 symbolNode->getQualifier() == EvqGlobal); 71 if (symbolNode->getQualifier() == EvqConst) 72 { 73 variablesToReplaceOut->push_back(&symbolNode->variable()); 74 } 75 76 TIntermBinary *deferredInit = 77 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight()); 78 deferredInitializersOut->push_back(deferredInit); 79 80 // Remove the initializer from the global scope and just declare the global instead. 81 declaration->replaceChildNode(init, symbolNode); 82 } 83 } 84 else if (initializeUninitializedGlobals) 85 { 86 TIntermSymbol *symbolNode = declarator->getAsSymbolNode(); 87 ASSERT(symbolNode); 88 89 // Ignore ANGLE internal variables and nameless declarations. 90 if (symbolNode->variable().symbolType() == SymbolType::AngleInternal || 91 symbolNode->variable().symbolType() == SymbolType::Empty) 92 return; 93 94 if (symbolNode->getQualifier() == EvqGlobal) 95 { 96 TIntermSequence initCode; 97 CreateInitCode(symbolNode, canUseLoopsToInitialize, highPrecisionSupported, &initCode, 98 symbolTable); 99 deferredInitializersOut->insert(deferredInitializersOut->end(), initCode.begin(), 100 initCode.end()); 101 } 102 } 103 } 104 105 void InsertInitCallToMain(TIntermBlock *root, 106 TIntermSequence *deferredInitializers, 107 TSymbolTable *symbolTable) 108 { 109 TIntermBlock *initGlobalsBlock = new TIntermBlock(); 110 initGlobalsBlock->getSequence()->swap(*deferredInitializers); 111 112 TFunction *initGlobalsFunction = 113 new TFunction(symbolTable, kInitGlobalsString, SymbolType::AngleInternal, 114 StaticType::GetBasic<EbtVoid, EbpUndefined>(), false); 115 116 TIntermFunctionPrototype *initGlobalsFunctionPrototype = 117 CreateInternalFunctionPrototypeNode(*initGlobalsFunction); 118 root->getSequence()->insert(root->getSequence()->begin(), initGlobalsFunctionPrototype); 119 TIntermFunctionDefinition *initGlobalsFunctionDefinition = 120 CreateInternalFunctionDefinitionNode(*initGlobalsFunction, initGlobalsBlock); 121 root->appendStatement(initGlobalsFunctionDefinition); 122 123 TIntermSequence emptySequence; 124 TIntermAggregate *initGlobalsCall = 125 TIntermAggregate::CreateFunctionCall(*initGlobalsFunction, &emptySequence); 126 127 TIntermBlock *mainBody = FindMainBody(root); 128 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsCall); 129 } 130 131 } // namespace 132 133 bool DeferGlobalInitializers(TCompiler *compiler, 134 TIntermBlock *root, 135 bool initializeUninitializedGlobals, 136 bool canUseLoopsToInitialize, 137 bool highPrecisionSupported, 138 bool forceDeferGlobalInitializers, 139 TSymbolTable *symbolTable) 140 { 141 TIntermSequence deferredInitializers; 142 std::vector<const TVariable *> variablesToReplace; 143 144 // Loop over all global statements and process the declarations. This is simpler than using a 145 // traverser. 146 for (TIntermNode *statement : *root->getSequence()) 147 { 148 TIntermDeclaration *declaration = statement->getAsDeclarationNode(); 149 if (declaration) 150 { 151 GetDeferredInitializers(declaration, initializeUninitializedGlobals, 152 canUseLoopsToInitialize, highPrecisionSupported, 153 forceDeferGlobalInitializers, &deferredInitializers, 154 &variablesToReplace, symbolTable); 155 } 156 } 157 158 // Add the function with initialization and the call to that. 159 if (!deferredInitializers.empty()) 160 { 161 InsertInitCallToMain(root, &deferredInitializers, symbolTable); 162 } 163 164 // Replace constant variables with non-constant global variables. 165 for (const TVariable *var : variablesToReplace) 166 { 167 TType *replacementType = new TType(var->getType()); 168 replacementType->setQualifier(EvqGlobal); 169 TVariable *replacement = 170 new TVariable(symbolTable, var->name(), replacementType, var->symbolType()); 171 if (!ReplaceVariable(compiler, root, var, replacement)) 172 { 173 return false; 174 } 175 } 176 177 return true; 178 } 179 180 } // namespace sh