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 //
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/DeferGlobalInitializers.h"
17
18 #include "compiler/translator/FindMain.h"
19 #include "compiler/translator/InitializeVariables.h"
20 #include "compiler/translator/IntermNode.h"
21 #include "compiler/translator/IntermNode_util.h"
22 #include "compiler/translator/SymbolTable.h"
23
24 namespace sh
25 {
26
27 namespace
28 {
29
GetDeferredInitializers(TIntermDeclaration * declaration,bool initializeUninitializedGlobals,bool canUseLoopsToInitialize,TIntermSequence * deferredInitializersOut,TSymbolTable * symbolTable)30 void GetDeferredInitializers(TIntermDeclaration *declaration,
31 bool initializeUninitializedGlobals,
32 bool canUseLoopsToInitialize,
33 TIntermSequence *deferredInitializersOut,
34 TSymbolTable *symbolTable)
35 {
36 // SeparateDeclarations should have already been run.
37 ASSERT(declaration->getSequence()->size() == 1);
38
39 TIntermNode *declarator = declaration->getSequence()->back();
40 TIntermBinary *init = declarator->getAsBinaryNode();
41 if (init)
42 {
43 TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode();
44 ASSERT(symbolNode);
45 TIntermTyped *expression = init->getRight();
46
47 if ((expression->getQualifier() != EvqConst ||
48 (expression->getAsConstantUnion() == nullptr &&
49 !expression->isConstructorWithOnlyConstantUnionParameters())))
50 {
51 // For variables which are not constant, defer their real initialization until
52 // after we initialize uniforms.
53 // Deferral is done also in any cases where the variable has not been constant
54 // folded, since otherwise there's a chance that HLSL output will generate extra
55 // statements from the initializer expression.
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)
63 {
64 symbolNode->getTypePointer()->setQualifier(EvqGlobal);
65 }
66
67 TIntermBinary *deferredInit =
68 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight());
69 deferredInitializersOut->push_back(deferredInit);
70
71 // Remove the initializer from the global scope and just declare the global instead.
72 declaration->replaceChildNode(init, symbolNode);
73 }
74 }
75 else if (initializeUninitializedGlobals)
76 {
77 TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
78 ASSERT(symbolNode);
79
80 // Ignore ANGLE internal variables.
81 if (symbolNode->getName().isInternal())
82 return;
83
84 if (symbolNode->getQualifier() == EvqGlobal && symbolNode->getSymbol() != "")
85 {
86 TIntermSequence *initCode =
87 CreateInitCode(symbolNode, canUseLoopsToInitialize, symbolTable);
88 deferredInitializersOut->insert(deferredInitializersOut->end(), initCode->begin(),
89 initCode->end());
90 }
91 }
92 }
93
InsertInitCallToMain(TIntermBlock * root,TIntermSequence * deferredInitializers,TSymbolTable * symbolTable)94 void InsertInitCallToMain(TIntermBlock *root,
95 TIntermSequence *deferredInitializers,
96 TSymbolTable *symbolTable)
97 {
98 TIntermBlock *initGlobalsBlock = new TIntermBlock();
99 initGlobalsBlock->getSequence()->swap(*deferredInitializers);
100
101 TSymbolUniqueId initGlobalsFunctionId(symbolTable);
102
103 const char *kInitGlobalsFunctionName = "initGlobals";
104
105 TIntermFunctionPrototype *initGlobalsFunctionPrototype =
106 CreateInternalFunctionPrototypeNode(TType(), kInitGlobalsFunctionName, initGlobalsFunctionId);
107 root->getSequence()->insert(root->getSequence()->begin(), initGlobalsFunctionPrototype);
108 TIntermFunctionDefinition *initGlobalsFunctionDefinition = CreateInternalFunctionDefinitionNode(
109 TType(), kInitGlobalsFunctionName, initGlobalsBlock, initGlobalsFunctionId);
110 root->appendStatement(initGlobalsFunctionDefinition);
111
112 TIntermAggregate *initGlobalsCall = CreateInternalFunctionCallNode(
113 TType(), kInitGlobalsFunctionName, initGlobalsFunctionId, new TIntermSequence());
114
115 TIntermBlock *mainBody = FindMainBody(root);
116 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsCall);
117 }
118
119 } // namespace
120
DeferGlobalInitializers(TIntermBlock * root,bool initializeUninitializedGlobals,bool canUseLoopsToInitialize,TSymbolTable * symbolTable)121 void DeferGlobalInitializers(TIntermBlock *root,
122 bool initializeUninitializedGlobals,
123 bool canUseLoopsToInitialize,
124 TSymbolTable *symbolTable)
125 {
126 TIntermSequence *deferredInitializers = new TIntermSequence();
127
128 // Loop over all global statements and process the declarations. This is simpler than using a
129 // traverser.
130 for (TIntermNode *statement : *root->getSequence())
131 {
132 TIntermDeclaration *declaration = statement->getAsDeclarationNode();
133 if (declaration)
134 {
135 GetDeferredInitializers(declaration, initializeUninitializedGlobals,
136 canUseLoopsToInitialize, deferredInitializers, symbolTable);
137 }
138 }
139
140 // Add the function with initialization and the call to that.
141 if (!deferredInitializers->empty())
142 {
143 InsertInitCallToMain(root, deferredInitializers, symbolTable);
144 }
145 }
146
147 } // namespace sh
148