blob: 3e2224b6ef3bfeef3c8421f307e5105b03fcbd00 [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 Etuaho16c745a2017-01-16 17:02:27 +000033TIntermFunctionPrototype *CreateFunctionPrototypeNode(const char *name, const int functionId)
Olli Etuaho3d932d82016-04-12 11:10:30 +030034{
Olli Etuaho16c745a2017-01-16 17:02:27 +000035 TType returnType(EbtVoid);
36 TIntermFunctionPrototype *functionNode = new TIntermFunctionPrototype(returnType);
Olli Etuaho3d932d82016-04-12 11:10:30 +030037
Olli Etuahobd674552016-10-06 13:28:42 +010038 SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
Olli Etuahobd674552016-10-06 13:28:42 +010039 functionNode->getFunctionSymbolInfo()->setId(functionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +030040 return functionNode;
41}
42
Olli Etuaho336b1472016-10-05 16:37:55 +010043TIntermFunctionDefinition *CreateFunctionDefinitionNode(const char *name,
44 TIntermBlock *functionBody,
45 const int functionId)
Olli Etuaho3d932d82016-04-12 11:10:30 +030046{
Olli Etuaho8ad9e752017-01-16 19:55:20 +000047 TIntermFunctionPrototype *prototypeNode = CreateFunctionPrototypeNode(name, functionId);
48 return new TIntermFunctionDefinition(prototypeNode, functionBody);
Olli Etuaho3d932d82016-04-12 11:10:30 +030049}
50
Olli Etuahod4f4c112016-04-15 15:11:24 +030051TIntermAggregate *CreateFunctionCallNode(const char *name, const int functionId)
Olli Etuaho3d932d82016-04-12 11:10:30 +030052{
Olli Etuaho1ecd14b2017-01-26 13:54:15 -080053 TIntermAggregate *functionNode = new TIntermAggregate(EOpCallFunctionInAST);
Olli Etuahobd674552016-10-06 13:28:42 +010054 SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
Olli Etuaho3d932d82016-04-12 11:10:30 +030055 TType returnType(EbtVoid);
56 functionNode->setType(returnType);
Olli Etuahobd674552016-10-06 13:28:42 +010057 functionNode->getFunctionSymbolInfo()->setId(functionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +030058 return functionNode;
59}
60
61class DeferGlobalInitializersTraverser : public TIntermTraverser
62{
63 public:
64 DeferGlobalInitializersTraverser();
65
66 bool visitBinary(Visit visit, TIntermBinary *node) override;
67
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010068 void insertInitFunction(TIntermBlock *root);
Olli Etuaho3d932d82016-04-12 11:10:30 +030069
70 private:
71 TIntermSequence mDeferredInitializers;
72};
73
74DeferGlobalInitializersTraverser::DeferGlobalInitializersTraverser()
75 : TIntermTraverser(true, false, false)
76{
77}
78
79bool DeferGlobalInitializersTraverser::visitBinary(Visit visit, TIntermBinary *node)
80{
81 if (node->getOp() == EOpInitialize)
82 {
83 TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
84 ASSERT(symbolNode);
85 TIntermTyped *expression = node->getRight();
86
Olli Etuahod4f4c112016-04-15 15:11:24 +030087 if (mInGlobalScope && (expression->getQualifier() != EvqConst ||
88 (expression->getAsConstantUnion() == nullptr &&
89 !expression->isConstructorWithOnlyConstantUnionParameters())))
Olli Etuaho3d932d82016-04-12 11:10:30 +030090 {
91 // For variables which are not constant, defer their real initialization until
92 // after we initialize uniforms.
93 // Deferral is done also in any cases where the variable has not been constant folded,
94 // since otherwise there's a chance that HLSL output will generate extra statements
95 // from the initializer expression.
Olli Etuaho3272a6d2016-08-29 17:54:50 +030096 TIntermBinary *deferredInit =
97 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), node->getRight());
Olli Etuaho3d932d82016-04-12 11:10:30 +030098 mDeferredInitializers.push_back(deferredInit);
99
Olli Etuahod4f4c112016-04-15 15:11:24 +0300100 // Change const global to a regular global if its initialization is deferred.
101 // This can happen if ANGLE has not been able to fold the constant expression used
102 // as an initializer.
103 ASSERT(symbolNode->getQualifier() == EvqConst ||
104 symbolNode->getQualifier() == EvqGlobal);
105 if (symbolNode->getQualifier() == EvqConst)
106 {
107 // All of the siblings in the same declaration need to have consistent qualifiers.
Olli Etuaho13389b62016-10-16 11:48:18 +0100108 auto *siblings = getParentNode()->getAsDeclarationNode()->getSequence();
Olli Etuahod4f4c112016-04-15 15:11:24 +0300109 for (TIntermNode *siblingNode : *siblings)
110 {
111 TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
112 if (siblingBinary)
113 {
114 ASSERT(siblingBinary->getOp() == EOpInitialize);
115 siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
116 }
117 siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
118 }
119 // This node is one of the siblings.
120 ASSERT(symbolNode->getQualifier() == EvqGlobal);
121 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300122 // Remove the initializer from the global scope and just declare the global instead.
Jamie Madill03d863c2016-07-27 18:15:53 -0400123 queueReplacement(node, symbolNode, OriginalNode::IS_DROPPED);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300124 }
125 }
126 return false;
127}
128
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100129void DeferGlobalInitializersTraverser::insertInitFunction(TIntermBlock *root)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300130{
131 if (mDeferredInitializers.empty())
132 {
133 return;
134 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500135 const int initFunctionId = TSymbolTable::nextUniqueId();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300136
137 const char *functionName = "initializeDeferredGlobals";
138
139 // Add function prototype to the beginning of the shader
Olli Etuaho16c745a2017-01-16 17:02:27 +0000140 TIntermFunctionPrototype *functionPrototypeNode =
Olli Etuahod4f4c112016-04-15 15:11:24 +0300141 CreateFunctionPrototypeNode(functionName, initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100142 root->getSequence()->insert(root->getSequence()->begin(), functionPrototypeNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300143
144 // Add function definition to the end of the shader
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100145 TIntermBlock *functionBodyNode = new TIntermBlock();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500146 TIntermSequence *functionBody = functionBodyNode->getSequence();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300147 for (const auto &deferredInit : mDeferredInitializers)
148 {
149 functionBody->push_back(deferredInit);
150 }
Olli Etuaho336b1472016-10-05 16:37:55 +0100151 TIntermFunctionDefinition *functionDefinition =
Olli Etuahod4f4c112016-04-15 15:11:24 +0300152 CreateFunctionDefinitionNode(functionName, functionBodyNode, initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100153 root->getSequence()->push_back(functionDefinition);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300154
155 // Insert call into main function
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100156 for (TIntermNode *node : *root->getSequence())
Olli Etuaho3d932d82016-04-12 11:10:30 +0300157 {
Olli Etuaho336b1472016-10-05 16:37:55 +0100158 TIntermFunctionDefinition *nodeFunction = node->getAsFunctionDefinition();
159 if (nodeFunction != nullptr && nodeFunction->getFunctionSymbolInfo()->isMain())
Olli Etuaho3d932d82016-04-12 11:10:30 +0300160 {
Olli Etuahod4f4c112016-04-15 15:11:24 +0300161 TIntermAggregate *functionCallNode =
162 CreateFunctionCallNode(functionName, initFunctionId);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300163
Olli Etuaho336b1472016-10-05 16:37:55 +0100164 TIntermBlock *mainBody = nodeFunction->getBody();
165 ASSERT(mainBody != nullptr);
166 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), functionCallNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300167 }
168 }
169}
170
171} // namespace
172
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100173void DeferGlobalInitializers(TIntermBlock *root)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300174{
175 DeferGlobalInitializersTraverser traverser;
176 root->traverse(&traverser);
177
178 // Replace the initializers of the global variables.
179 traverser.updateTree();
180
181 // Add the function with initialization and the call to that.
182 traverser.insertInitFunction(root);
183}
Jamie Madill45bcc782016-11-07 13:58:48 -0500184
185} // namespace sh