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
GetDeferredInitializers(TIntermDeclaration * declaration,bool initializeUninitializedGlobals,bool canUseLoopsToInitialize,bool highPrecisionSupported,bool forceDeferNonConstGlobalInitializers,TIntermSequence * deferredInitializersOut,std::vector<const TVariable * > * variablesToReplaceOut,TSymbolTable * symbolTable)37 void GetDeferredInitializers(TIntermDeclaration *declaration,
38 bool initializeUninitializedGlobals,
39 bool canUseLoopsToInitialize,
40 bool highPrecisionSupported,
41 bool forceDeferNonConstGlobalInitializers,
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 (forceDeferNonConstGlobalInitializers && symbolNode->getQualifier() != EvqConst))
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
InsertInitCallToMain(TIntermBlock * root,TIntermSequence * deferredInitializers,TSymbolTable * symbolTable)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
DeferGlobalInitializers(TCompiler * compiler,TIntermBlock * root,bool initializeUninitializedGlobals,bool canUseLoopsToInitialize,bool highPrecisionSupported,bool forceDeferNonConstGlobalInitializers,TSymbolTable * symbolTable)133 bool DeferGlobalInitializers(TCompiler *compiler,
134 TIntermBlock *root,
135 bool initializeUninitializedGlobals,
136 bool canUseLoopsToInitialize,
137 bool highPrecisionSupported,
138 bool forceDeferNonConstGlobalInitializers,
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 forceDeferNonConstGlobalInitializers, &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
181