blob: 72aa69ea55cdd0006746db88964a2632c0de1da7 [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
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300109 TIntermFunctionDefinition *main = FindMain(root);
110 ASSERT(main != nullptr);
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300111 TIntermBlock *mainBody = main->getBody();
112 ASSERT(mainBody != nullptr);
Olli Etuaho83498432017-05-19 14:57:23 +0300113 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsBlock);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300114}
115
116} // namespace
117
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300118void DeferGlobalInitializers(TIntermBlock *root, bool initializeUninitializedGlobals)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300119{
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300120 TIntermSequence *deferredInitializers = new TIntermSequence();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300121
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300122 // Loop over all global statements and process the declarations. This is simpler than using a
123 // traverser.
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300124 for (TIntermNode *statement : *root->getSequence())
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300125 {
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300126 TIntermDeclaration *declaration = statement->getAsDeclarationNode();
127 if (declaration)
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300128 {
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300129 GetDeferredInitializers(declaration, initializeUninitializedGlobals,
130 deferredInitializers);
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300131 }
132 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300133
134 // Add the function with initialization and the call to that.
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300135 if (!deferredInitializers->empty())
136 {
Olli Etuaho83498432017-05-19 14:57:23 +0300137 InsertInitCodeToMain(root, deferredInitializers);
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300138 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300139}
Jamie Madill45bcc782016-11-07 13:58:48 -0500140
141} // namespace sh