blob: 67ccdf65fc6d78199a400635232e99c5d58f4920 [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//
Olli Etuaho83498432017-05-19 14:57:23 +03006// DeferGlobalInitializers is an AST traverser that moves global initializers into a block in the
7// beginning of main(). This enables initialization of globals with uniforms or non-constant
8// globals, as allowed by the WebGL spec. Some initializers referencing non-constants may need to be
9// unfolded into if statements in HLSL - this kind of steps should be done after
10// DeferGlobalInitializers is run.
Olli Etuaho3d932d82016-04-12 11:10:30 +030011//
Olli Etuaho0ffc4412017-05-19 14:18:55 +030012// It can also initialize all uninitialized globals.
13//
Olli Etuaho3d932d82016-04-12 11:10:30 +030014
15#include "compiler/translator/DeferGlobalInitializers.h"
16
Olli Etuaho9cbc07c2017-05-10 18:22:01 +030017#include "compiler/translator/FindMain.h"
Olli Etuaho0ffc4412017-05-19 14:18:55 +030018#include "compiler/translator/InitializeVariables.h"
Olli Etuaho3d932d82016-04-12 11:10:30 +030019#include "compiler/translator/IntermNode.h"
20#include "compiler/translator/SymbolTable.h"
21
Jamie Madill45bcc782016-11-07 13:58:48 -050022namespace sh
23{
24
Olli Etuaho3d932d82016-04-12 11:10:30 +030025namespace
26{
27
Olli Etuaho1dd07a62017-05-12 17:01:22 +030028void GetDeferredInitializers(TIntermDeclaration *declaration,
Olli Etuaho0ffc4412017-05-19 14:18:55 +030029 bool initializeUninitializedGlobals,
Olli Etuaho1dd07a62017-05-12 17:01:22 +030030 TIntermSequence *deferredInitializersOut)
Olli Etuaho3d932d82016-04-12 11:10:30 +030031{
Olli Etuaho1dd07a62017-05-12 17:01:22 +030032 // We iterate with an index instead of using an iterator since we're replacing the children of
33 // declaration inside the loop.
34 for (size_t i = 0; i < declaration->getSequence()->size(); ++i)
Olli Etuaho3d932d82016-04-12 11:10:30 +030035 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030036 TIntermNode *declarator = declaration->getSequence()->at(i);
37 TIntermBinary *init = declarator->getAsBinaryNode();
38 if (init)
Olli Etuaho3d932d82016-04-12 11:10:30 +030039 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030040 TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode();
41 ASSERT(symbolNode);
42 TIntermTyped *expression = init->getRight();
Olli Etuaho3d932d82016-04-12 11:10:30 +030043
Olli Etuaho1dd07a62017-05-12 17:01:22 +030044 if ((expression->getQualifier() != EvqConst ||
45 (expression->getAsConstantUnion() == nullptr &&
46 !expression->isConstructorWithOnlyConstantUnionParameters())))
Olli Etuahod4f4c112016-04-15 15:11:24 +030047 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030048 // For variables which are not constant, defer their real initialization until
49 // after we initialize uniforms.
50 // Deferral is done also in any cases where the variable has not been constant
51 // folded, since otherwise there's a chance that HLSL output will generate extra
52 // statements from the initializer expression.
53 TIntermBinary *deferredInit =
54 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight());
55 deferredInitializersOut->push_back(deferredInit);
56
57 // Change const global to a regular global if its initialization is deferred.
58 // This can happen if ANGLE has not been able to fold the constant expression used
59 // as an initializer.
60 ASSERT(symbolNode->getQualifier() == EvqConst ||
61 symbolNode->getQualifier() == EvqGlobal);
62 if (symbolNode->getQualifier() == EvqConst)
Olli Etuahod4f4c112016-04-15 15:11:24 +030063 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030064 // All of the siblings in the same declaration need to have consistent
65 // qualifiers.
66 auto *siblings = declaration->getSequence();
67 for (TIntermNode *siblingNode : *siblings)
Olli Etuahod4f4c112016-04-15 15:11:24 +030068 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030069 TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
70 if (siblingBinary)
71 {
72 ASSERT(siblingBinary->getOp() == EOpInitialize);
73 siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
74 }
75 siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
Olli Etuahod4f4c112016-04-15 15:11:24 +030076 }
Olli Etuaho1dd07a62017-05-12 17:01:22 +030077 // This node is one of the siblings.
78 ASSERT(symbolNode->getQualifier() == EvqGlobal);
Olli Etuahod4f4c112016-04-15 15:11:24 +030079 }
Olli Etuaho1dd07a62017-05-12 17:01:22 +030080 // Remove the initializer from the global scope and just declare the global instead.
81 declaration->replaceChildNode(init, symbolNode);
Olli Etuahod4f4c112016-04-15 15:11:24 +030082 }
Olli Etuaho3d932d82016-04-12 11:10:30 +030083 }
Olli Etuaho0ffc4412017-05-19 14:18:55 +030084 else if (initializeUninitializedGlobals)
85 {
86 TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
87 ASSERT(symbolNode);
Martin Radev69056a12017-05-18 11:14:50 +030088
89 // Ignore ANGLE internal variables.
90 if (symbolNode->getName().isInternal())
91 continue;
92
Olli Etuaho0ffc4412017-05-19 14:18:55 +030093 if (symbolNode->getQualifier() == EvqGlobal && symbolNode->getSymbol() != "")
94 {
95 TIntermSequence *initCode = CreateInitCode(symbolNode);
96 deferredInitializersOut->insert(deferredInitializersOut->end(), initCode->begin(),
97 initCode->end());
98 }
99 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300100 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300101}
102
Olli Etuaho83498432017-05-19 14:57:23 +0300103void InsertInitCodeToMain(TIntermBlock *root, TIntermSequence *deferredInitializers)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300104{
Olli Etuaho83498432017-05-19 14:57:23 +0300105 // Insert init code as a block to the beginning of the main() function.
106 TIntermBlock *initGlobalsBlock = new TIntermBlock();
107 initGlobalsBlock->getSequence()->swap(*deferredInitializers);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300108
Martin Radevb50ccd32017-07-06 17:09:58 +0300109 TIntermBlock *mainBody = FindMainBody(root);
Olli Etuaho83498432017-05-19 14:57:23 +0300110 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsBlock);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300111}
112
113} // namespace
114
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300115void DeferGlobalInitializers(TIntermBlock *root, bool initializeUninitializedGlobals)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300116{
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300117 TIntermSequence *deferredInitializers = new TIntermSequence();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300118
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300119 // Loop over all global statements and process the declarations. This is simpler than using a
120 // traverser.
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300121 for (TIntermNode *statement : *root->getSequence())
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300122 {
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300123 TIntermDeclaration *declaration = statement->getAsDeclarationNode();
124 if (declaration)
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300125 {
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300126 GetDeferredInitializers(declaration, initializeUninitializedGlobals,
127 deferredInitializers);
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300128 }
129 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300130
131 // Add the function with initialization and the call to that.
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300132 if (!deferredInitializers->empty())
133 {
Olli Etuaho83498432017-05-19 14:57:23 +0300134 InsertInitCodeToMain(root, deferredInitializers);
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300135 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300136}
Jamie Madill45bcc782016-11-07 13:58:48 -0500137
138} // namespace sh