• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,TIntermSequence * deferredInitializersOut,std::vector<const TVariable * > * variablesToReplaceOut,TSymbolTable * symbolTable)37 void GetDeferredInitializers(TIntermDeclaration *declaration,
38                              bool initializeUninitializedGlobals,
39                              bool canUseLoopsToInitialize,
40                              bool highPrecisionSupported,
41                              TIntermSequence *deferredInitializersOut,
42                              std::vector<const TVariable *> *variablesToReplaceOut,
43                              TSymbolTable *symbolTable)
44 {
45     // SeparateDeclarations should have already been run.
46     ASSERT(declaration->getSequence()->size() == 1);
47 
48     TIntermNode *declarator = declaration->getSequence()->back();
49     TIntermBinary *init     = declarator->getAsBinaryNode();
50     if (init)
51     {
52         TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode();
53         ASSERT(symbolNode);
54         TIntermTyped *expression = init->getRight();
55 
56         if (expression->getQualifier() != EvqConst || !expression->hasConstantValue())
57         {
58             // For variables which are not constant, defer their real initialization until
59             // after we initialize uniforms.
60             // Deferral is done also in any cases where the variable can not be converted to a
61             // constant union, since otherwise there's a chance that HLSL output will generate extra
62             // statements from the initializer expression.
63 
64             // Change const global to a regular global if its initialization is deferred.
65             // This can happen if ANGLE has not been able to fold the constant expression used
66             // as an initializer.
67             ASSERT(symbolNode->getQualifier() == EvqConst ||
68                    symbolNode->getQualifier() == EvqGlobal);
69             if (symbolNode->getQualifier() == EvqConst)
70             {
71                 variablesToReplaceOut->push_back(&symbolNode->variable());
72             }
73 
74             TIntermBinary *deferredInit =
75                 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight());
76             deferredInitializersOut->push_back(deferredInit);
77 
78             // Remove the initializer from the global scope and just declare the global instead.
79             declaration->replaceChildNode(init, symbolNode);
80         }
81     }
82     else if (initializeUninitializedGlobals)
83     {
84         TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
85         ASSERT(symbolNode);
86 
87         // Ignore ANGLE internal variables and nameless declarations.
88         if (symbolNode->variable().symbolType() == SymbolType::AngleInternal ||
89             symbolNode->variable().symbolType() == SymbolType::Empty)
90             return;
91 
92         if (symbolNode->getQualifier() == EvqGlobal)
93         {
94             TIntermSequence *initCode = CreateInitCode(symbolNode, canUseLoopsToInitialize,
95                                                        highPrecisionSupported, symbolTable);
96             deferredInitializersOut->insert(deferredInitializersOut->end(), initCode->begin(),
97                                             initCode->end());
98         }
99     }
100 }
101 
InsertInitCallToMain(TIntermBlock * root,TIntermSequence * deferredInitializers,TSymbolTable * symbolTable)102 void InsertInitCallToMain(TIntermBlock *root,
103                           TIntermSequence *deferredInitializers,
104                           TSymbolTable *symbolTable)
105 {
106     TIntermBlock *initGlobalsBlock = new TIntermBlock();
107     initGlobalsBlock->getSequence()->swap(*deferredInitializers);
108 
109     TFunction *initGlobalsFunction =
110         new TFunction(symbolTable, kInitGlobalsString, SymbolType::AngleInternal,
111                       StaticType::GetBasic<EbtVoid>(), false);
112 
113     TIntermFunctionPrototype *initGlobalsFunctionPrototype =
114         CreateInternalFunctionPrototypeNode(*initGlobalsFunction);
115     root->getSequence()->insert(root->getSequence()->begin(), initGlobalsFunctionPrototype);
116     TIntermFunctionDefinition *initGlobalsFunctionDefinition =
117         CreateInternalFunctionDefinitionNode(*initGlobalsFunction, initGlobalsBlock);
118     root->appendStatement(initGlobalsFunctionDefinition);
119 
120     TIntermAggregate *initGlobalsCall =
121         TIntermAggregate::CreateFunctionCall(*initGlobalsFunction, new TIntermSequence());
122 
123     TIntermBlock *mainBody = FindMainBody(root);
124     mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsCall);
125 }
126 
127 }  // namespace
128 
DeferGlobalInitializers(TCompiler * compiler,TIntermBlock * root,bool initializeUninitializedGlobals,bool canUseLoopsToInitialize,bool highPrecisionSupported,TSymbolTable * symbolTable)129 bool DeferGlobalInitializers(TCompiler *compiler,
130                              TIntermBlock *root,
131                              bool initializeUninitializedGlobals,
132                              bool canUseLoopsToInitialize,
133                              bool highPrecisionSupported,
134                              TSymbolTable *symbolTable)
135 {
136     TIntermSequence *deferredInitializers = new TIntermSequence();
137     std::vector<const TVariable *> variablesToReplace;
138 
139     // Loop over all global statements and process the declarations. This is simpler than using a
140     // traverser.
141     for (TIntermNode *statement : *root->getSequence())
142     {
143         TIntermDeclaration *declaration = statement->getAsDeclarationNode();
144         if (declaration)
145         {
146             GetDeferredInitializers(declaration, initializeUninitializedGlobals,
147                                     canUseLoopsToInitialize, highPrecisionSupported,
148                                     deferredInitializers, &variablesToReplace, symbolTable);
149         }
150     }
151 
152     // Add the function with initialization and the call to that.
153     if (!deferredInitializers->empty())
154     {
155         InsertInitCallToMain(root, deferredInitializers, symbolTable);
156     }
157 
158     // Replace constant variables with non-constant global variables.
159     for (const TVariable *var : variablesToReplace)
160     {
161         TType *replacementType = new TType(var->getType());
162         replacementType->setQualifier(EvqGlobal);
163         TVariable *replacement =
164             new TVariable(symbolTable, var->name(), replacementType, var->symbolType());
165         if (!ReplaceVariable(compiler, root, var, replacement))
166         {
167             return false;
168         }
169     }
170 
171     return true;
172 }
173 
174 }  // namespace sh
175