blob: 1c58562fc215c22e0b5d449db0e84015c20ab5d2 [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
Jamie Madill45bcc782016-11-07 13:58:48 -050018namespace sh
19{
20
Olli Etuaho3d932d82016-04-12 11:10:30 +030021namespace
22{
23
Olli Etuahobd674552016-10-06 13:28:42 +010024void SetInternalFunctionName(TFunctionSymbolInfo *functionInfo, const char *name)
Olli Etuaho3d932d82016-04-12 11:10:30 +030025{
26 TString nameStr(name);
27 nameStr = TFunction::mangleName(nameStr);
28 TName nameObj(nameStr);
29 nameObj.setInternal(true);
Olli Etuahobd674552016-10-06 13:28:42 +010030 functionInfo->setNameObj(nameObj);
Olli Etuaho3d932d82016-04-12 11:10:30 +030031}
32
Olli Etuahod4f4c112016-04-15 15:11:24 +030033TIntermAggregate *CreateFunctionPrototypeNode(const char *name, const int functionId)
Olli Etuaho3d932d82016-04-12 11:10:30 +030034{
35 TIntermAggregate *functionNode = new TIntermAggregate(EOpPrototype);
36
Olli Etuahobd674552016-10-06 13:28:42 +010037 SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
Olli Etuaho3d932d82016-04-12 11:10:30 +030038 TType returnType(EbtVoid);
39 functionNode->setType(returnType);
Olli Etuahobd674552016-10-06 13:28:42 +010040 functionNode->getFunctionSymbolInfo()->setId(functionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +030041 return functionNode;
42}
43
Olli Etuaho336b1472016-10-05 16:37:55 +010044TIntermFunctionDefinition *CreateFunctionDefinitionNode(const char *name,
45 TIntermBlock *functionBody,
46 const int functionId)
Olli Etuaho3d932d82016-04-12 11:10:30 +030047{
Olli Etuaho336b1472016-10-05 16:37:55 +010048 TType returnType(EbtVoid);
Olli Etuaho3d932d82016-04-12 11:10:30 +030049 TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters);
Olli Etuaho336b1472016-10-05 16:37:55 +010050 TIntermFunctionDefinition *functionNode =
51 new TIntermFunctionDefinition(returnType, paramsNode, functionBody);
Olli Etuaho3d932d82016-04-12 11:10:30 +030052
Olli Etuahobd674552016-10-06 13:28:42 +010053 SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
Olli Etuahobd674552016-10-06 13:28:42 +010054 functionNode->getFunctionSymbolInfo()->setId(functionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +030055 return functionNode;
56}
57
Olli Etuahod4f4c112016-04-15 15:11:24 +030058TIntermAggregate *CreateFunctionCallNode(const char *name, const int functionId)
Olli Etuaho3d932d82016-04-12 11:10:30 +030059{
60 TIntermAggregate *functionNode = new TIntermAggregate(EOpFunctionCall);
61
Olli Etuahod4f4c112016-04-15 15:11:24 +030062 functionNode->setUserDefined();
Olli Etuahobd674552016-10-06 13:28:42 +010063 SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
Olli Etuaho3d932d82016-04-12 11:10:30 +030064 TType returnType(EbtVoid);
65 functionNode->setType(returnType);
Olli Etuahobd674552016-10-06 13:28:42 +010066 functionNode->getFunctionSymbolInfo()->setId(functionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +030067 return functionNode;
68}
69
70class DeferGlobalInitializersTraverser : public TIntermTraverser
71{
72 public:
73 DeferGlobalInitializersTraverser();
74
75 bool visitBinary(Visit visit, TIntermBinary *node) override;
76
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010077 void insertInitFunction(TIntermBlock *root);
Olli Etuaho3d932d82016-04-12 11:10:30 +030078
79 private:
80 TIntermSequence mDeferredInitializers;
81};
82
83DeferGlobalInitializersTraverser::DeferGlobalInitializersTraverser()
84 : TIntermTraverser(true, false, false)
85{
86}
87
88bool DeferGlobalInitializersTraverser::visitBinary(Visit visit, TIntermBinary *node)
89{
90 if (node->getOp() == EOpInitialize)
91 {
92 TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
93 ASSERT(symbolNode);
94 TIntermTyped *expression = node->getRight();
95
Olli Etuahod4f4c112016-04-15 15:11:24 +030096 if (mInGlobalScope && (expression->getQualifier() != EvqConst ||
97 (expression->getAsConstantUnion() == nullptr &&
98 !expression->isConstructorWithOnlyConstantUnionParameters())))
Olli Etuaho3d932d82016-04-12 11:10:30 +030099 {
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.
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300105 TIntermBinary *deferredInit =
106 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), node->getRight());
Olli Etuaho3d932d82016-04-12 11:10:30 +0300107 mDeferredInitializers.push_back(deferredInit);
108
Olli Etuahod4f4c112016-04-15 15:11:24 +0300109 // 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 ASSERT(symbolNode->getQualifier() == EvqConst ||
113 symbolNode->getQualifier() == EvqGlobal);
114 if (symbolNode->getQualifier() == EvqConst)
115 {
116 // All of the siblings in the same declaration need to have consistent qualifiers.
Olli Etuaho13389b62016-10-16 11:48:18 +0100117 auto *siblings = getParentNode()->getAsDeclarationNode()->getSequence();
Olli Etuahod4f4c112016-04-15 15:11:24 +0300118 for (TIntermNode *siblingNode : *siblings)
119 {
120 TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
121 if (siblingBinary)
122 {
123 ASSERT(siblingBinary->getOp() == EOpInitialize);
124 siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
125 }
126 siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
127 }
128 // This node is one of the siblings.
129 ASSERT(symbolNode->getQualifier() == EvqGlobal);
130 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300131 // Remove the initializer from the global scope and just declare the global instead.
Jamie Madill03d863c2016-07-27 18:15:53 -0400132 queueReplacement(node, symbolNode, OriginalNode::IS_DROPPED);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300133 }
134 }
135 return false;
136}
137
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100138void DeferGlobalInitializersTraverser::insertInitFunction(TIntermBlock *root)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300139{
140 if (mDeferredInitializers.empty())
141 {
142 return;
143 }
Olli Etuahod4f4c112016-04-15 15:11:24 +0300144 const int initFunctionId = TSymbolTable::nextUniqueId();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300145
146 const char *functionName = "initializeDeferredGlobals";
147
148 // Add function prototype to the beginning of the shader
Olli Etuahod4f4c112016-04-15 15:11:24 +0300149 TIntermAggregate *functionPrototypeNode =
150 CreateFunctionPrototypeNode(functionName, initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100151 root->getSequence()->insert(root->getSequence()->begin(), functionPrototypeNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300152
153 // Add function definition to the end of the shader
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100154 TIntermBlock *functionBodyNode = new TIntermBlock();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300155 TIntermSequence *functionBody = functionBodyNode->getSequence();
156 for (const auto &deferredInit : mDeferredInitializers)
157 {
158 functionBody->push_back(deferredInit);
159 }
Olli Etuaho336b1472016-10-05 16:37:55 +0100160 TIntermFunctionDefinition *functionDefinition =
Olli Etuahod4f4c112016-04-15 15:11:24 +0300161 CreateFunctionDefinitionNode(functionName, functionBodyNode, initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100162 root->getSequence()->push_back(functionDefinition);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300163
164 // Insert call into main function
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100165 for (TIntermNode *node : *root->getSequence())
Olli Etuaho3d932d82016-04-12 11:10:30 +0300166 {
Olli Etuaho336b1472016-10-05 16:37:55 +0100167 TIntermFunctionDefinition *nodeFunction = node->getAsFunctionDefinition();
168 if (nodeFunction != nullptr && nodeFunction->getFunctionSymbolInfo()->isMain())
Olli Etuaho3d932d82016-04-12 11:10:30 +0300169 {
Olli Etuahod4f4c112016-04-15 15:11:24 +0300170 TIntermAggregate *functionCallNode =
171 CreateFunctionCallNode(functionName, initFunctionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300172
Olli Etuaho336b1472016-10-05 16:37:55 +0100173 TIntermBlock *mainBody = nodeFunction->getBody();
174 ASSERT(mainBody != nullptr);
175 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), functionCallNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300176 }
177 }
178}
179
180} // namespace
181
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100182void DeferGlobalInitializers(TIntermBlock *root)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300183{
184 DeferGlobalInitializersTraverser traverser;
185 root->traverse(&traverser);
186
187 // Replace the initializers of the global variables.
188 traverser.updateTree();
189
190 // Add the function with initialization and the call to that.
191 traverser.insertInitFunction(root);
192}
Jamie Madill45bcc782016-11-07 13:58:48 -0500193
194} // namespace sh