blob: 12c7a9f73a395a8148ebf6a106aa434eca771cd6 [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
30TIntermAggregate *CreateFunctionPrototypeNode(const char *name)
31{
32 TIntermAggregate *functionNode = new TIntermAggregate(EOpPrototype);
33
34 SetInternalFunctionName(functionNode, name);
35 TType returnType(EbtVoid);
36 functionNode->setType(returnType);
37 return functionNode;
38}
39
40TIntermAggregate *CreateFunctionDefinitionNode(const char *name, TIntermAggregate *functionBody)
41{
42 TIntermAggregate *functionNode = new TIntermAggregate(EOpFunction);
43 TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters);
44 functionNode->getSequence()->push_back(paramsNode);
45 functionNode->getSequence()->push_back(functionBody);
46
47 SetInternalFunctionName(functionNode, name);
48 TType returnType(EbtVoid);
49 functionNode->setType(returnType);
50 return functionNode;
51}
52
53TIntermAggregate *CreateFunctionCallNode(const char *name)
54{
55 TIntermAggregate *functionNode = new TIntermAggregate(EOpFunctionCall);
56
57 SetInternalFunctionName(functionNode, name);
58 TType returnType(EbtVoid);
59 functionNode->setType(returnType);
60 return functionNode;
61}
62
63class DeferGlobalInitializersTraverser : public TIntermTraverser
64{
65 public:
66 DeferGlobalInitializersTraverser();
67
68 bool visitBinary(Visit visit, TIntermBinary *node) override;
69
70 void insertInitFunction(TIntermNode *root);
71
72 private:
73 TIntermSequence mDeferredInitializers;
74};
75
76DeferGlobalInitializersTraverser::DeferGlobalInitializersTraverser()
77 : TIntermTraverser(true, false, false)
78{
79}
80
81bool DeferGlobalInitializersTraverser::visitBinary(Visit visit, TIntermBinary *node)
82{
83 if (node->getOp() == EOpInitialize)
84 {
85 TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
86 ASSERT(symbolNode);
87 TIntermTyped *expression = node->getRight();
88
89 if (symbolNode->getQualifier() == EvqGlobal &&
90 (expression->getQualifier() != EvqConst || expression->getAsConstantUnion() == nullptr))
91 {
92 // For variables which are not constant, defer their real initialization until
93 // after we initialize uniforms.
94 // Deferral is done also in any cases where the variable has not been constant folded,
95 // since otherwise there's a chance that HLSL output will generate extra statements
96 // from the initializer expression.
97 TIntermBinary *deferredInit = new TIntermBinary(EOpAssign);
98 deferredInit->setLeft(node->getLeft()->deepCopy());
99 deferredInit->setRight(node->getRight());
100 deferredInit->setType(node->getType());
101 mDeferredInitializers.push_back(deferredInit);
102
103 // Remove the initializer from the global scope and just declare the global instead.
104 mReplacements.push_back(NodeUpdateEntry(getParentNode(), node, node->getLeft(), false));
105 }
106 }
107 return false;
108}
109
110void DeferGlobalInitializersTraverser::insertInitFunction(TIntermNode *root)
111{
112 if (mDeferredInitializers.empty())
113 {
114 return;
115 }
116 TIntermAggregate *rootAgg = root->getAsAggregate();
117 ASSERT(rootAgg != nullptr && rootAgg->getOp() == EOpSequence);
118
119 const char *functionName = "initializeDeferredGlobals";
120
121 // Add function prototype to the beginning of the shader
122 TIntermAggregate *functionPrototypeNode = CreateFunctionPrototypeNode(functionName);
123 rootAgg->getSequence()->insert(rootAgg->getSequence()->begin(), functionPrototypeNode);
124
125 // Add function definition to the end of the shader
126 TIntermAggregate *functionBodyNode = new TIntermAggregate(EOpSequence);
127 TIntermSequence *functionBody = functionBodyNode->getSequence();
128 for (const auto &deferredInit : mDeferredInitializers)
129 {
130 functionBody->push_back(deferredInit);
131 }
132 TIntermAggregate *functionDefinition =
133 CreateFunctionDefinitionNode(functionName, functionBodyNode);
134 rootAgg->getSequence()->push_back(functionDefinition);
135
136 // Insert call into main function
137 for (TIntermNode *node : *rootAgg->getSequence())
138 {
139 TIntermAggregate *nodeAgg = node->getAsAggregate();
140 if (nodeAgg != nullptr && nodeAgg->getOp() == EOpFunction &&
141 TFunction::unmangleName(nodeAgg->getName()) == "main")
142 {
143 TIntermAggregate *functionCallNode = CreateFunctionCallNode(functionName);
144
145 TIntermNode *mainBody = nodeAgg->getSequence()->back();
146 TIntermAggregate *mainBodyAgg = mainBody->getAsAggregate();
147 ASSERT(mainBodyAgg != nullptr && mainBodyAgg->getOp() == EOpSequence);
148 mainBodyAgg->getSequence()->insert(mainBodyAgg->getSequence()->begin(),
149 functionCallNode);
150 }
151 }
152}
153
154} // namespace
155
156void DeferGlobalInitializers(TIntermNode *root)
157{
158 DeferGlobalInitializersTraverser traverser;
159 root->traverse(&traverser);
160
161 // Replace the initializers of the global variables.
162 traverser.updateTree();
163
164 // Add the function with initialization and the call to that.
165 traverser.insertInitFunction(root);
166}