blob: 76addf293dda01befc133e2b3136dcaf0ee386c9 [file] [log] [blame]
Olli Etuaho3d932d82016-04-12 11:10:30 +03001//
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
18namespace
19{
20
21void SetInternalFunctionName(TIntermAggregate *functionNode, const char *name)
22{
23 TString nameStr(name);
24 nameStr = TFunction::mangleName(nameStr);
25 TName nameObj(nameStr);
26 nameObj.setInternal(true);
27 functionNode->setNameObj(nameObj);
28}
29
Olli Etuahod4f4c112016-04-15 15:11:24 +030030TIntermAggregate *CreateFunctionPrototypeNode(const char *name, const int functionId)
Olli Etuaho3d932d82016-04-12 11:10:30 +030031{
32 TIntermAggregate *functionNode = new TIntermAggregate(EOpPrototype);
33
34 SetInternalFunctionName(functionNode, name);
35 TType returnType(EbtVoid);
36 functionNode->setType(returnType);
Olli Etuahod4f4c112016-04-15 15:11:24 +030037 functionNode->setFunctionId(functionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +030038 return functionNode;
39}
40
Olli Etuahod4f4c112016-04-15 15:11:24 +030041TIntermAggregate *CreateFunctionDefinitionNode(const char *name,
42 TIntermAggregate *functionBody,
43 const int functionId)
Olli Etuaho3d932d82016-04-12 11:10:30 +030044{
45 TIntermAggregate *functionNode = new TIntermAggregate(EOpFunction);
46 TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters);
47 functionNode->getSequence()->push_back(paramsNode);
48 functionNode->getSequence()->push_back(functionBody);
49
50 SetInternalFunctionName(functionNode, name);
51 TType returnType(EbtVoid);
52 functionNode->setType(returnType);
Olli Etuahod4f4c112016-04-15 15:11:24 +030053 functionNode->setFunctionId(functionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +030054 return functionNode;
55}
56
Olli Etuahod4f4c112016-04-15 15:11:24 +030057TIntermAggregate *CreateFunctionCallNode(const char *name, const int functionId)
Olli Etuaho3d932d82016-04-12 11:10:30 +030058{
59 TIntermAggregate *functionNode = new TIntermAggregate(EOpFunctionCall);
60
Olli Etuahod4f4c112016-04-15 15:11:24 +030061 functionNode->setUserDefined();
Olli Etuaho3d932d82016-04-12 11:10:30 +030062 SetInternalFunctionName(functionNode, name);
63 TType returnType(EbtVoid);
64 functionNode->setType(returnType);
Olli Etuahod4f4c112016-04-15 15:11:24 +030065 functionNode->setFunctionId(functionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +030066 return functionNode;
67}
68
69class DeferGlobalInitializersTraverser : public TIntermTraverser
70{
71 public:
72 DeferGlobalInitializersTraverser();
73
74 bool visitBinary(Visit visit, TIntermBinary *node) override;
75
76 void insertInitFunction(TIntermNode *root);
77
78 private:
79 TIntermSequence mDeferredInitializers;
80};
81
82DeferGlobalInitializersTraverser::DeferGlobalInitializersTraverser()
83 : TIntermTraverser(true, false, false)
84{
85}
86
87bool DeferGlobalInitializersTraverser::visitBinary(Visit visit, TIntermBinary *node)
88{
89 if (node->getOp() == EOpInitialize)
90 {
91 TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
92 ASSERT(symbolNode);
93 TIntermTyped *expression = node->getRight();
94
Olli Etuahod4f4c112016-04-15 15:11:24 +030095 if (mInGlobalScope && (expression->getQualifier() != EvqConst ||
96 (expression->getAsConstantUnion() == nullptr &&
97 !expression->isConstructorWithOnlyConstantUnionParameters())))
Olli Etuaho3d932d82016-04-12 11:10:30 +030098 {
99 // For variables which are not constant, defer their real initialization until
100 // after we initialize uniforms.
101 // Deferral is done also in any cases where the variable has not been constant folded,
102 // since otherwise there's a chance that HLSL output will generate extra statements
103 // from the initializer expression.
104 TIntermBinary *deferredInit = new TIntermBinary(EOpAssign);
Olli Etuahod4f4c112016-04-15 15:11:24 +0300105 deferredInit->setLeft(symbolNode->deepCopy());
Olli Etuaho3d932d82016-04-12 11:10:30 +0300106 deferredInit->setRight(node->getRight());
107 deferredInit->setType(node->getType());
108 mDeferredInitializers.push_back(deferredInit);
109
Olli Etuahod4f4c112016-04-15 15:11:24 +0300110 // Change const global to a regular global if its initialization is deferred.
111 // This can happen if ANGLE has not been able to fold the constant expression used
112 // as an initializer.
113 ASSERT(symbolNode->getQualifier() == EvqConst ||
114 symbolNode->getQualifier() == EvqGlobal);
115 if (symbolNode->getQualifier() == EvqConst)
116 {
117 // All of the siblings in the same declaration need to have consistent qualifiers.
118 auto *siblings = getParentNode()->getAsAggregate()->getSequence();
119 for (TIntermNode *siblingNode : *siblings)
120 {
121 TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
122 if (siblingBinary)
123 {
124 ASSERT(siblingBinary->getOp() == EOpInitialize);
125 siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
126 }
127 siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
128 }
129 // This node is one of the siblings.
130 ASSERT(symbolNode->getQualifier() == EvqGlobal);
131 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300132 // Remove the initializer from the global scope and just declare the global instead.
Jamie Madill03d863c2016-07-27 18:15:53 -0400133 queueReplacement(node, symbolNode, OriginalNode::IS_DROPPED);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300134 }
135 }
136 return false;
137}
138
139void DeferGlobalInitializersTraverser::insertInitFunction(TIntermNode *root)
140{
141 if (mDeferredInitializers.empty())
142 {
143 return;
144 }
Olli Etuahod4f4c112016-04-15 15:11:24 +0300145 const int initFunctionId = TSymbolTable::nextUniqueId();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300146 TIntermAggregate *rootAgg = root->getAsAggregate();
147 ASSERT(rootAgg != nullptr && rootAgg->getOp() == EOpSequence);
148
149 const char *functionName = "initializeDeferredGlobals";
150
151 // Add function prototype to the beginning of the shader
Olli Etuahod4f4c112016-04-15 15:11:24 +0300152 TIntermAggregate *functionPrototypeNode =
153 CreateFunctionPrototypeNode(functionName, initFunctionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300154 rootAgg->getSequence()->insert(rootAgg->getSequence()->begin(), functionPrototypeNode);
155
156 // Add function definition to the end of the shader
157 TIntermAggregate *functionBodyNode = new TIntermAggregate(EOpSequence);
158 TIntermSequence *functionBody = functionBodyNode->getSequence();
159 for (const auto &deferredInit : mDeferredInitializers)
160 {
161 functionBody->push_back(deferredInit);
162 }
163 TIntermAggregate *functionDefinition =
Olli Etuahod4f4c112016-04-15 15:11:24 +0300164 CreateFunctionDefinitionNode(functionName, functionBodyNode, initFunctionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300165 rootAgg->getSequence()->push_back(functionDefinition);
166
167 // Insert call into main function
168 for (TIntermNode *node : *rootAgg->getSequence())
169 {
170 TIntermAggregate *nodeAgg = node->getAsAggregate();
171 if (nodeAgg != nullptr && nodeAgg->getOp() == EOpFunction &&
172 TFunction::unmangleName(nodeAgg->getName()) == "main")
173 {
Olli Etuahod4f4c112016-04-15 15:11:24 +0300174 TIntermAggregate *functionCallNode =
175 CreateFunctionCallNode(functionName, initFunctionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300176
177 TIntermNode *mainBody = nodeAgg->getSequence()->back();
178 TIntermAggregate *mainBodyAgg = mainBody->getAsAggregate();
179 ASSERT(mainBodyAgg != nullptr && mainBodyAgg->getOp() == EOpSequence);
180 mainBodyAgg->getSequence()->insert(mainBodyAgg->getSequence()->begin(),
181 functionCallNode);
182 }
183 }
184}
185
186} // namespace
187
188void DeferGlobalInitializers(TIntermNode *root)
189{
190 DeferGlobalInitializersTraverser traverser;
191 root->traverse(&traverser);
192
193 // Replace the initializers of the global variables.
194 traverser.updateTree();
195
196 // Add the function with initialization and the call to that.
197 traverser.insertInitFunction(root);
198}