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