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