xref: /aosp_15_r20/external/angle/src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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