blob: cf7ebb5e884436e4cb132fb96351de214ed83236 [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 Etuaho3d932d82016-04-12 11:10:30 +030024class DeferGlobalInitializersTraverser : public TIntermTraverser
25{
26 public:
27 DeferGlobalInitializersTraverser();
28
29 bool visitBinary(Visit visit, TIntermBinary *node) override;
30
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010031 void insertInitFunction(TIntermBlock *root);
Olli Etuaho3d932d82016-04-12 11:10:30 +030032
33 private:
34 TIntermSequence mDeferredInitializers;
35};
36
37DeferGlobalInitializersTraverser::DeferGlobalInitializersTraverser()
38 : TIntermTraverser(true, false, false)
39{
40}
41
42bool DeferGlobalInitializersTraverser::visitBinary(Visit visit, TIntermBinary *node)
43{
44 if (node->getOp() == EOpInitialize)
45 {
46 TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
47 ASSERT(symbolNode);
48 TIntermTyped *expression = node->getRight();
49
Olli Etuahod4f4c112016-04-15 15:11:24 +030050 if (mInGlobalScope && (expression->getQualifier() != EvqConst ||
51 (expression->getAsConstantUnion() == nullptr &&
52 !expression->isConstructorWithOnlyConstantUnionParameters())))
Olli Etuaho3d932d82016-04-12 11:10:30 +030053 {
54 // For variables which are not constant, defer their real initialization until
55 // after we initialize uniforms.
56 // Deferral is done also in any cases where the variable has not been constant folded,
57 // since otherwise there's a chance that HLSL output will generate extra statements
58 // from the initializer expression.
Olli Etuaho3272a6d2016-08-29 17:54:50 +030059 TIntermBinary *deferredInit =
60 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), node->getRight());
Olli Etuaho3d932d82016-04-12 11:10:30 +030061 mDeferredInitializers.push_back(deferredInit);
62
Olli Etuahod4f4c112016-04-15 15:11:24 +030063 // Change const global to a regular global if its initialization is deferred.
64 // This can happen if ANGLE has not been able to fold the constant expression used
65 // as an initializer.
66 ASSERT(symbolNode->getQualifier() == EvqConst ||
67 symbolNode->getQualifier() == EvqGlobal);
68 if (symbolNode->getQualifier() == EvqConst)
69 {
70 // All of the siblings in the same declaration need to have consistent qualifiers.
Olli Etuaho13389b62016-10-16 11:48:18 +010071 auto *siblings = getParentNode()->getAsDeclarationNode()->getSequence();
Olli Etuahod4f4c112016-04-15 15:11:24 +030072 for (TIntermNode *siblingNode : *siblings)
73 {
74 TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
75 if (siblingBinary)
76 {
77 ASSERT(siblingBinary->getOp() == EOpInitialize);
78 siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
79 }
80 siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
81 }
82 // This node is one of the siblings.
83 ASSERT(symbolNode->getQualifier() == EvqGlobal);
84 }
Olli Etuaho3d932d82016-04-12 11:10:30 +030085 // Remove the initializer from the global scope and just declare the global instead.
Jamie Madill03d863c2016-07-27 18:15:53 -040086 queueReplacement(node, symbolNode, OriginalNode::IS_DROPPED);
Olli Etuaho3d932d82016-04-12 11:10:30 +030087 }
88 }
89 return false;
90}
91
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010092void DeferGlobalInitializersTraverser::insertInitFunction(TIntermBlock *root)
Olli Etuaho3d932d82016-04-12 11:10:30 +030093{
94 if (mDeferredInitializers.empty())
95 {
96 return;
97 }
Olli Etuahofe486322017-03-21 09:30:54 +000098 TSymbolUniqueId initFunctionId;
Olli Etuaho3d932d82016-04-12 11:10:30 +030099
100 const char *functionName = "initializeDeferredGlobals";
101
102 // Add function prototype to the beginning of the shader
Olli Etuaho16c745a2017-01-16 17:02:27 +0000103 TIntermFunctionPrototype *functionPrototypeNode =
Olli Etuahofe486322017-03-21 09:30:54 +0000104 CreateInternalFunctionPrototypeNode(TType(EbtVoid), functionName, initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100105 root->getSequence()->insert(root->getSequence()->begin(), functionPrototypeNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300106
107 // Add function definition to the end of the shader
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100108 TIntermBlock *functionBodyNode = new TIntermBlock();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500109 TIntermSequence *functionBody = functionBodyNode->getSequence();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300110 for (const auto &deferredInit : mDeferredInitializers)
111 {
112 functionBody->push_back(deferredInit);
113 }
Olli Etuahofe486322017-03-21 09:30:54 +0000114 TIntermFunctionDefinition *functionDefinition = CreateInternalFunctionDefinitionNode(
115 TType(EbtVoid), functionName, functionBodyNode, initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100116 root->getSequence()->push_back(functionDefinition);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300117
118 // Insert call into main function
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100119 for (TIntermNode *node : *root->getSequence())
Olli Etuaho3d932d82016-04-12 11:10:30 +0300120 {
Olli Etuaho336b1472016-10-05 16:37:55 +0100121 TIntermFunctionDefinition *nodeFunction = node->getAsFunctionDefinition();
122 if (nodeFunction != nullptr && nodeFunction->getFunctionSymbolInfo()->isMain())
Olli Etuaho3d932d82016-04-12 11:10:30 +0300123 {
Olli Etuahofe486322017-03-21 09:30:54 +0000124 TIntermAggregate *functionCallNode = CreateInternalFunctionCallNode(
125 TType(EbtVoid), functionName, initFunctionId, nullptr);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300126
Olli Etuaho336b1472016-10-05 16:37:55 +0100127 TIntermBlock *mainBody = nodeFunction->getBody();
128 ASSERT(mainBody != nullptr);
129 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), functionCallNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300130 }
131 }
132}
133
134} // namespace
135
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100136void DeferGlobalInitializers(TIntermBlock *root)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300137{
138 DeferGlobalInitializersTraverser traverser;
139 root->traverse(&traverser);
140
141 // Replace the initializers of the global variables.
142 traverser.updateTree();
143
144 // Add the function with initialization and the call to that.
145 traverser.insertInitFunction(root);
146}
Jamie Madill45bcc782016-11-07 13:58:48 -0500147
148} // namespace sh