Line data Source code
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 function, and
7 : // adds a function call to that function in the beginning of main().
8 : // This enables initialization of globals with uniforms or non-constant globals, as allowed by
9 : // the WebGL spec. Some initializers referencing non-constants may need to be unfolded into if
10 : // statements in HLSL - this kind of steps should be done after DeferGlobalInitializers is run.
11 : //
12 :
13 : #include "compiler/translator/DeferGlobalInitializers.h"
14 :
15 : #include "compiler/translator/IntermNode.h"
16 : #include "compiler/translator/SymbolTable.h"
17 :
18 : namespace sh
19 : {
20 :
21 : namespace
22 : {
23 :
24 0 : void SetInternalFunctionName(TFunctionSymbolInfo *functionInfo, const char *name)
25 : {
26 0 : TString nameStr(name);
27 0 : nameStr = TFunction::mangleName(nameStr);
28 0 : TName nameObj(nameStr);
29 0 : nameObj.setInternal(true);
30 0 : functionInfo->setNameObj(nameObj);
31 0 : }
32 :
33 0 : TIntermAggregate *CreateFunctionPrototypeNode(const char *name, const int functionId)
34 : {
35 0 : TIntermAggregate *functionNode = new TIntermAggregate(EOpPrototype);
36 :
37 0 : SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
38 0 : TType returnType(EbtVoid);
39 0 : functionNode->setType(returnType);
40 0 : functionNode->getFunctionSymbolInfo()->setId(functionId);
41 0 : return functionNode;
42 : }
43 :
44 0 : TIntermFunctionDefinition *CreateFunctionDefinitionNode(const char *name,
45 : TIntermBlock *functionBody,
46 : const int functionId)
47 : {
48 0 : TType returnType(EbtVoid);
49 0 : TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters);
50 : TIntermFunctionDefinition *functionNode =
51 0 : new TIntermFunctionDefinition(returnType, paramsNode, functionBody);
52 :
53 0 : SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
54 0 : functionNode->getFunctionSymbolInfo()->setId(functionId);
55 0 : return functionNode;
56 : }
57 :
58 0 : TIntermAggregate *CreateFunctionCallNode(const char *name, const int functionId)
59 : {
60 0 : TIntermAggregate *functionNode = new TIntermAggregate(EOpFunctionCall);
61 :
62 0 : functionNode->setUserDefined();
63 0 : SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
64 0 : TType returnType(EbtVoid);
65 0 : functionNode->setType(returnType);
66 0 : functionNode->getFunctionSymbolInfo()->setId(functionId);
67 0 : return functionNode;
68 : }
69 :
70 0 : class DeferGlobalInitializersTraverser : public TIntermTraverser
71 : {
72 : public:
73 : DeferGlobalInitializersTraverser();
74 :
75 : bool visitBinary(Visit visit, TIntermBinary *node) override;
76 :
77 : void insertInitFunction(TIntermBlock *root);
78 :
79 : private:
80 : TIntermSequence mDeferredInitializers;
81 : };
82 :
83 0 : DeferGlobalInitializersTraverser::DeferGlobalInitializersTraverser()
84 0 : : TIntermTraverser(true, false, false)
85 : {
86 0 : }
87 :
88 0 : bool DeferGlobalInitializersTraverser::visitBinary(Visit visit, TIntermBinary *node)
89 : {
90 0 : if (node->getOp() == EOpInitialize)
91 : {
92 0 : TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
93 0 : ASSERT(symbolNode);
94 0 : TIntermTyped *expression = node->getRight();
95 :
96 0 : if (mInGlobalScope && (expression->getQualifier() != EvqConst ||
97 0 : (expression->getAsConstantUnion() == nullptr &&
98 0 : !expression->isConstructorWithOnlyConstantUnionParameters())))
99 : {
100 : // For variables which are not constant, defer their real initialization until
101 : // after we initialize uniforms.
102 : // Deferral is done also in any cases where the variable has not been constant folded,
103 : // since otherwise there's a chance that HLSL output will generate extra statements
104 : // from the initializer expression.
105 : TIntermBinary *deferredInit =
106 0 : new TIntermBinary(EOpAssign, symbolNode->deepCopy(), node->getRight());
107 0 : mDeferredInitializers.push_back(deferredInit);
108 :
109 : // Change const global to a regular global if its initialization is deferred.
110 : // This can happen if ANGLE has not been able to fold the constant expression used
111 : // as an initializer.
112 0 : ASSERT(symbolNode->getQualifier() == EvqConst ||
113 : symbolNode->getQualifier() == EvqGlobal);
114 0 : if (symbolNode->getQualifier() == EvqConst)
115 : {
116 : // All of the siblings in the same declaration need to have consistent qualifiers.
117 0 : auto *siblings = getParentNode()->getAsDeclarationNode()->getSequence();
118 0 : for (TIntermNode *siblingNode : *siblings)
119 : {
120 0 : TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
121 0 : if (siblingBinary)
122 : {
123 0 : ASSERT(siblingBinary->getOp() == EOpInitialize);
124 0 : siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
125 : }
126 0 : siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
127 : }
128 : // This node is one of the siblings.
129 0 : ASSERT(symbolNode->getQualifier() == EvqGlobal);
130 : }
131 : // Remove the initializer from the global scope and just declare the global instead.
132 0 : queueReplacement(node, symbolNode, OriginalNode::IS_DROPPED);
133 : }
134 : }
135 0 : return false;
136 : }
137 :
138 0 : void DeferGlobalInitializersTraverser::insertInitFunction(TIntermBlock *root)
139 : {
140 0 : if (mDeferredInitializers.empty())
141 : {
142 0 : return;
143 : }
144 0 : const int initFunctionId = TSymbolTable::nextUniqueId();
145 :
146 0 : const char *functionName = "initializeDeferredGlobals";
147 :
148 : // Add function prototype to the beginning of the shader
149 : TIntermAggregate *functionPrototypeNode =
150 0 : CreateFunctionPrototypeNode(functionName, initFunctionId);
151 0 : root->getSequence()->insert(root->getSequence()->begin(), functionPrototypeNode);
152 :
153 : // Add function definition to the end of the shader
154 0 : TIntermBlock *functionBodyNode = new TIntermBlock();
155 0 : TIntermSequence *functionBody = functionBodyNode->getSequence();
156 0 : for (const auto &deferredInit : mDeferredInitializers)
157 : {
158 0 : functionBody->push_back(deferredInit);
159 : }
160 : TIntermFunctionDefinition *functionDefinition =
161 0 : CreateFunctionDefinitionNode(functionName, functionBodyNode, initFunctionId);
162 0 : root->getSequence()->push_back(functionDefinition);
163 :
164 : // Insert call into main function
165 0 : for (TIntermNode *node : *root->getSequence())
166 : {
167 0 : TIntermFunctionDefinition *nodeFunction = node->getAsFunctionDefinition();
168 0 : if (nodeFunction != nullptr && nodeFunction->getFunctionSymbolInfo()->isMain())
169 : {
170 : TIntermAggregate *functionCallNode =
171 0 : CreateFunctionCallNode(functionName, initFunctionId);
172 :
173 0 : TIntermBlock *mainBody = nodeFunction->getBody();
174 0 : ASSERT(mainBody != nullptr);
175 0 : mainBody->getSequence()->insert(mainBody->getSequence()->begin(), functionCallNode);
176 : }
177 : }
178 : }
179 :
180 : } // namespace
181 :
182 0 : void DeferGlobalInitializers(TIntermBlock *root)
183 : {
184 0 : DeferGlobalInitializersTraverser traverser;
185 0 : root->traverse(&traverser);
186 :
187 : // Replace the initializers of the global variables.
188 0 : traverser.updateTree();
189 :
190 : // Add the function with initialization and the call to that.
191 0 : traverser.insertInitFunction(root);
192 0 : }
193 :
194 : } // namespace sh
|