blob: c1585a5b0f6fe5576304657a432d1a29f70de78c [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
Olli Etuaho9cbc07c2017-05-10 18:22:01 +030015#include "compiler/translator/FindMain.h"
Olli Etuaho3d932d82016-04-12 11:10:30 +030016#include "compiler/translator/IntermNode.h"
17#include "compiler/translator/SymbolTable.h"
18
Jamie Madill45bcc782016-11-07 13:58:48 -050019namespace sh
20{
21
Olli Etuaho3d932d82016-04-12 11:10:30 +030022namespace
23{
24
Olli Etuaho3d932d82016-04-12 11:10:30 +030025class DeferGlobalInitializersTraverser : public TIntermTraverser
26{
27 public:
28 DeferGlobalInitializersTraverser();
29
30 bool visitBinary(Visit visit, TIntermBinary *node) override;
31
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010032 void insertInitFunction(TIntermBlock *root);
Olli Etuaho3d932d82016-04-12 11:10:30 +030033
34 private:
35 TIntermSequence mDeferredInitializers;
36};
37
38DeferGlobalInitializersTraverser::DeferGlobalInitializersTraverser()
39 : TIntermTraverser(true, false, false)
40{
41}
42
43bool DeferGlobalInitializersTraverser::visitBinary(Visit visit, TIntermBinary *node)
44{
45 if (node->getOp() == EOpInitialize)
46 {
47 TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
48 ASSERT(symbolNode);
49 TIntermTyped *expression = node->getRight();
50
Olli Etuahod4f4c112016-04-15 15:11:24 +030051 if (mInGlobalScope && (expression->getQualifier() != EvqConst ||
52 (expression->getAsConstantUnion() == nullptr &&
53 !expression->isConstructorWithOnlyConstantUnionParameters())))
Olli Etuaho3d932d82016-04-12 11:10:30 +030054 {
55 // For variables which are not constant, defer their real initialization until
56 // after we initialize uniforms.
57 // Deferral is done also in any cases where the variable has not been constant folded,
58 // since otherwise there's a chance that HLSL output will generate extra statements
59 // from the initializer expression.
Olli Etuaho3272a6d2016-08-29 17:54:50 +030060 TIntermBinary *deferredInit =
61 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), node->getRight());
Olli Etuaho3d932d82016-04-12 11:10:30 +030062 mDeferredInitializers.push_back(deferredInit);
63
Olli Etuahod4f4c112016-04-15 15:11:24 +030064 // Change const global to a regular global if its initialization is deferred.
65 // This can happen if ANGLE has not been able to fold the constant expression used
66 // as an initializer.
67 ASSERT(symbolNode->getQualifier() == EvqConst ||
68 symbolNode->getQualifier() == EvqGlobal);
69 if (symbolNode->getQualifier() == EvqConst)
70 {
71 // All of the siblings in the same declaration need to have consistent qualifiers.
Olli Etuaho13389b62016-10-16 11:48:18 +010072 auto *siblings = getParentNode()->getAsDeclarationNode()->getSequence();
Olli Etuahod4f4c112016-04-15 15:11:24 +030073 for (TIntermNode *siblingNode : *siblings)
74 {
75 TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
76 if (siblingBinary)
77 {
78 ASSERT(siblingBinary->getOp() == EOpInitialize);
79 siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
80 }
81 siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
82 }
83 // This node is one of the siblings.
84 ASSERT(symbolNode->getQualifier() == EvqGlobal);
85 }
Olli Etuaho3d932d82016-04-12 11:10:30 +030086 // Remove the initializer from the global scope and just declare the global instead.
Jamie Madill03d863c2016-07-27 18:15:53 -040087 queueReplacement(node, symbolNode, OriginalNode::IS_DROPPED);
Olli Etuaho3d932d82016-04-12 11:10:30 +030088 }
89 }
90 return false;
91}
92
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010093void DeferGlobalInitializersTraverser::insertInitFunction(TIntermBlock *root)
Olli Etuaho3d932d82016-04-12 11:10:30 +030094{
95 if (mDeferredInitializers.empty())
96 {
97 return;
98 }
Olli Etuahofe486322017-03-21 09:30:54 +000099 TSymbolUniqueId initFunctionId;
Olli Etuaho3d932d82016-04-12 11:10:30 +0300100
101 const char *functionName = "initializeDeferredGlobals";
102
103 // Add function prototype to the beginning of the shader
Olli Etuaho16c745a2017-01-16 17:02:27 +0000104 TIntermFunctionPrototype *functionPrototypeNode =
Olli Etuahofe486322017-03-21 09:30:54 +0000105 CreateInternalFunctionPrototypeNode(TType(EbtVoid), functionName, initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100106 root->getSequence()->insert(root->getSequence()->begin(), functionPrototypeNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300107
108 // Add function definition to the end of the shader
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100109 TIntermBlock *functionBodyNode = new TIntermBlock();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500110 TIntermSequence *functionBody = functionBodyNode->getSequence();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300111 for (const auto &deferredInit : mDeferredInitializers)
112 {
113 functionBody->push_back(deferredInit);
114 }
Olli Etuahofe486322017-03-21 09:30:54 +0000115 TIntermFunctionDefinition *functionDefinition = CreateInternalFunctionDefinitionNode(
116 TType(EbtVoid), functionName, functionBodyNode, initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100117 root->getSequence()->push_back(functionDefinition);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300118
119 // Insert call into main function
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300120 TIntermFunctionDefinition *main = FindMain(root);
121 ASSERT(main != nullptr);
122 TIntermAggregate *functionCallNode =
123 CreateInternalFunctionCallNode(TType(EbtVoid), functionName, initFunctionId, nullptr);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300124
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300125 TIntermBlock *mainBody = main->getBody();
126 ASSERT(mainBody != nullptr);
127 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), functionCallNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300128}
129
130} // namespace
131
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100132void DeferGlobalInitializers(TIntermBlock *root)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300133{
134 DeferGlobalInitializersTraverser traverser;
135 root->traverse(&traverser);
136
137 // Replace the initializers of the global variables.
138 traverser.updateTree();
139
140 // Add the function with initialization and the call to that.
141 traverser.insertInitFunction(root);
142}
Jamie Madill45bcc782016-11-07 13:58:48 -0500143
144} // namespace sh