blob: 067e6cea3c95e746bc0ac9579c8bebda8b025e49 [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
Olli Etuahobd674552016-10-06 13:28:42 +010021void SetInternalFunctionName(TFunctionSymbolInfo *functionInfo, const char *name)
Olli Etuaho3d932d82016-04-12 11:10:30 +030022{
23 TString nameStr(name);
24 nameStr = TFunction::mangleName(nameStr);
25 TName nameObj(nameStr);
26 nameObj.setInternal(true);
Olli Etuahobd674552016-10-06 13:28:42 +010027 functionInfo->setNameObj(nameObj);
Olli Etuaho3d932d82016-04-12 11:10:30 +030028}
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
Olli Etuahobd674552016-10-06 13:28:42 +010034 SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
Olli Etuaho3d932d82016-04-12 11:10:30 +030035 TType returnType(EbtVoid);
36 functionNode->setType(returnType);
Olli Etuahobd674552016-10-06 13:28:42 +010037 functionNode->getFunctionSymbolInfo()->setId(functionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +030038 return functionNode;
39}
40
Olli Etuaho336b1472016-10-05 16:37:55 +010041TIntermFunctionDefinition *CreateFunctionDefinitionNode(const char *name,
42 TIntermBlock *functionBody,
43 const int functionId)
Olli Etuaho3d932d82016-04-12 11:10:30 +030044{
Olli Etuaho336b1472016-10-05 16:37:55 +010045 TType returnType(EbtVoid);
Olli Etuaho3d932d82016-04-12 11:10:30 +030046 TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters);
Olli Etuaho336b1472016-10-05 16:37:55 +010047 TIntermFunctionDefinition *functionNode =
48 new TIntermFunctionDefinition(returnType, paramsNode, functionBody);
Olli Etuaho3d932d82016-04-12 11:10:30 +030049
Olli Etuahobd674552016-10-06 13:28:42 +010050 SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
Olli Etuahobd674552016-10-06 13:28:42 +010051 functionNode->getFunctionSymbolInfo()->setId(functionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +030052 return functionNode;
53}
54
Olli Etuahod4f4c112016-04-15 15:11:24 +030055TIntermAggregate *CreateFunctionCallNode(const char *name, const int functionId)
Olli Etuaho3d932d82016-04-12 11:10:30 +030056{
57 TIntermAggregate *functionNode = new TIntermAggregate(EOpFunctionCall);
58
Olli Etuahod4f4c112016-04-15 15:11:24 +030059 functionNode->setUserDefined();
Olli Etuahobd674552016-10-06 13:28:42 +010060 SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
Olli Etuaho3d932d82016-04-12 11:10:30 +030061 TType returnType(EbtVoid);
62 functionNode->setType(returnType);
Olli Etuahobd674552016-10-06 13:28:42 +010063 functionNode->getFunctionSymbolInfo()->setId(functionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +030064 return functionNode;
65}
66
67class DeferGlobalInitializersTraverser : public TIntermTraverser
68{
69 public:
70 DeferGlobalInitializersTraverser();
71
72 bool visitBinary(Visit visit, TIntermBinary *node) override;
73
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010074 void insertInitFunction(TIntermBlock *root);
Olli Etuaho3d932d82016-04-12 11:10:30 +030075
76 private:
77 TIntermSequence mDeferredInitializers;
78};
79
80DeferGlobalInitializersTraverser::DeferGlobalInitializersTraverser()
81 : TIntermTraverser(true, false, false)
82{
83}
84
85bool DeferGlobalInitializersTraverser::visitBinary(Visit visit, TIntermBinary *node)
86{
87 if (node->getOp() == EOpInitialize)
88 {
89 TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
90 ASSERT(symbolNode);
91 TIntermTyped *expression = node->getRight();
92
Olli Etuahod4f4c112016-04-15 15:11:24 +030093 if (mInGlobalScope && (expression->getQualifier() != EvqConst ||
94 (expression->getAsConstantUnion() == nullptr &&
95 !expression->isConstructorWithOnlyConstantUnionParameters())))
Olli Etuaho3d932d82016-04-12 11:10:30 +030096 {
97 // For variables which are not constant, defer their real initialization until
98 // after we initialize uniforms.
99 // Deferral is done also in any cases where the variable has not been constant folded,
100 // since otherwise there's a chance that HLSL output will generate extra statements
101 // from the initializer expression.
Olli Etuaho3272a6d2016-08-29 17:54:50 +0300102 TIntermBinary *deferredInit =
103 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), node->getRight());
Olli Etuaho3d932d82016-04-12 11:10:30 +0300104 mDeferredInitializers.push_back(deferredInit);
105
Olli Etuahod4f4c112016-04-15 15:11:24 +0300106 // Change const global to a regular global if its initialization is deferred.
107 // This can happen if ANGLE has not been able to fold the constant expression used
108 // as an initializer.
109 ASSERT(symbolNode->getQualifier() == EvqConst ||
110 symbolNode->getQualifier() == EvqGlobal);
111 if (symbolNode->getQualifier() == EvqConst)
112 {
113 // All of the siblings in the same declaration need to have consistent qualifiers.
114 auto *siblings = getParentNode()->getAsAggregate()->getSequence();
115 for (TIntermNode *siblingNode : *siblings)
116 {
117 TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
118 if (siblingBinary)
119 {
120 ASSERT(siblingBinary->getOp() == EOpInitialize);
121 siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
122 }
123 siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
124 }
125 // This node is one of the siblings.
126 ASSERT(symbolNode->getQualifier() == EvqGlobal);
127 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300128 // Remove the initializer from the global scope and just declare the global instead.
Jamie Madill03d863c2016-07-27 18:15:53 -0400129 queueReplacement(node, symbolNode, OriginalNode::IS_DROPPED);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300130 }
131 }
132 return false;
133}
134
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100135void DeferGlobalInitializersTraverser::insertInitFunction(TIntermBlock *root)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300136{
137 if (mDeferredInitializers.empty())
138 {
139 return;
140 }
Olli Etuahod4f4c112016-04-15 15:11:24 +0300141 const int initFunctionId = TSymbolTable::nextUniqueId();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300142
143 const char *functionName = "initializeDeferredGlobals";
144
145 // Add function prototype to the beginning of the shader
Olli Etuahod4f4c112016-04-15 15:11:24 +0300146 TIntermAggregate *functionPrototypeNode =
147 CreateFunctionPrototypeNode(functionName, initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100148 root->getSequence()->insert(root->getSequence()->begin(), functionPrototypeNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300149
150 // Add function definition to the end of the shader
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100151 TIntermBlock *functionBodyNode = new TIntermBlock();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300152 TIntermSequence *functionBody = functionBodyNode->getSequence();
153 for (const auto &deferredInit : mDeferredInitializers)
154 {
155 functionBody->push_back(deferredInit);
156 }
Olli Etuaho336b1472016-10-05 16:37:55 +0100157 TIntermFunctionDefinition *functionDefinition =
Olli Etuahod4f4c112016-04-15 15:11:24 +0300158 CreateFunctionDefinitionNode(functionName, functionBodyNode, initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100159 root->getSequence()->push_back(functionDefinition);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300160
161 // Insert call into main function
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100162 for (TIntermNode *node : *root->getSequence())
Olli Etuaho3d932d82016-04-12 11:10:30 +0300163 {
Olli Etuaho336b1472016-10-05 16:37:55 +0100164 TIntermFunctionDefinition *nodeFunction = node->getAsFunctionDefinition();
165 if (nodeFunction != nullptr && nodeFunction->getFunctionSymbolInfo()->isMain())
Olli Etuaho3d932d82016-04-12 11:10:30 +0300166 {
Olli Etuahod4f4c112016-04-15 15:11:24 +0300167 TIntermAggregate *functionCallNode =
168 CreateFunctionCallNode(functionName, initFunctionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300169
Olli Etuaho336b1472016-10-05 16:37:55 +0100170 TIntermBlock *mainBody = nodeFunction->getBody();
171 ASSERT(mainBody != nullptr);
172 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), functionCallNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300173 }
174 }
175}
176
177} // namespace
178
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100179void DeferGlobalInitializers(TIntermBlock *root)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300180{
181 DeferGlobalInitializersTraverser traverser;
182 root->traverse(&traverser);
183
184 // Replace the initializers of the global variables.
185 traverser.updateTree();
186
187 // Add the function with initialization and the call to that.
188 traverser.insertInitFunction(root);
189}