blob: 9a0073e6af2bcfa2aa80ea91930ff6fbf992d433 [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 Etuahoa16a84f2017-09-12 13:49:18 +03006// DeferGlobalInitializers is an AST traverser that moves global initializers into a separate
7// function that is called in the beginning of main(). This enables initialization of globals with
8// uniforms or non-constant globals, as allowed by the WebGL spec. Some initializers referencing
9// non-constants may need to be unfolded into if statements in HLSL - this kind of steps should be
10// done after DeferGlobalInitializers is run. Note that it's important that the function definition
11// is at the end of the shader, as some globals may be declared after main().
Olli Etuaho3d932d82016-04-12 11:10:30 +030012//
Olli Etuaho0ffc4412017-05-19 14:18:55 +030013// It can also initialize all uninitialized globals.
14//
Olli Etuaho3d932d82016-04-12 11:10:30 +030015
16#include "compiler/translator/DeferGlobalInitializers.h"
17
Olli Etuaho9cbc07c2017-05-10 18:22:01 +030018#include "compiler/translator/FindMain.h"
Olli Etuaho0ffc4412017-05-19 14:18:55 +030019#include "compiler/translator/InitializeVariables.h"
Olli Etuaho3d932d82016-04-12 11:10:30 +030020#include "compiler/translator/IntermNode.h"
Olli Etuahoa16a84f2017-09-12 13:49:18 +030021#include "compiler/translator/IntermNode_util.h"
Olli Etuaho3d932d82016-04-12 11:10:30 +030022#include "compiler/translator/SymbolTable.h"
23
Jamie Madill45bcc782016-11-07 13:58:48 -050024namespace sh
25{
26
Olli Etuaho3d932d82016-04-12 11:10:30 +030027namespace
28{
29
Olli Etuaho1dd07a62017-05-12 17:01:22 +030030void GetDeferredInitializers(TIntermDeclaration *declaration,
Olli Etuaho0ffc4412017-05-19 14:18:55 +030031 bool initializeUninitializedGlobals,
Olli Etuaho1dd07a62017-05-12 17:01:22 +030032 TIntermSequence *deferredInitializersOut)
Olli Etuaho3d932d82016-04-12 11:10:30 +030033{
Olli Etuaho1dd07a62017-05-12 17:01:22 +030034 // We iterate with an index instead of using an iterator since we're replacing the children of
35 // declaration inside the loop.
36 for (size_t i = 0; i < declaration->getSequence()->size(); ++i)
Olli Etuaho3d932d82016-04-12 11:10:30 +030037 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030038 TIntermNode *declarator = declaration->getSequence()->at(i);
39 TIntermBinary *init = declarator->getAsBinaryNode();
40 if (init)
Olli Etuaho3d932d82016-04-12 11:10:30 +030041 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030042 TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode();
43 ASSERT(symbolNode);
44 TIntermTyped *expression = init->getRight();
Olli Etuaho3d932d82016-04-12 11:10:30 +030045
Olli Etuaho1dd07a62017-05-12 17:01:22 +030046 if ((expression->getQualifier() != EvqConst ||
47 (expression->getAsConstantUnion() == nullptr &&
48 !expression->isConstructorWithOnlyConstantUnionParameters())))
Olli Etuahod4f4c112016-04-15 15:11:24 +030049 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030050 // For variables which are not constant, defer their real initialization until
51 // after we initialize uniforms.
52 // Deferral is done also in any cases where the variable has not been constant
53 // folded, since otherwise there's a chance that HLSL output will generate extra
54 // statements from the initializer expression.
55 TIntermBinary *deferredInit =
56 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight());
57 deferredInitializersOut->push_back(deferredInit);
58
59 // Change const global to a regular global if its initialization is deferred.
60 // This can happen if ANGLE has not been able to fold the constant expression used
61 // as an initializer.
62 ASSERT(symbolNode->getQualifier() == EvqConst ||
63 symbolNode->getQualifier() == EvqGlobal);
64 if (symbolNode->getQualifier() == EvqConst)
Olli Etuahod4f4c112016-04-15 15:11:24 +030065 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030066 // All of the siblings in the same declaration need to have consistent
67 // qualifiers.
68 auto *siblings = declaration->getSequence();
69 for (TIntermNode *siblingNode : *siblings)
Olli Etuahod4f4c112016-04-15 15:11:24 +030070 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030071 TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
72 if (siblingBinary)
73 {
74 ASSERT(siblingBinary->getOp() == EOpInitialize);
75 siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
76 }
77 siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
Olli Etuahod4f4c112016-04-15 15:11:24 +030078 }
Olli Etuaho1dd07a62017-05-12 17:01:22 +030079 // This node is one of the siblings.
80 ASSERT(symbolNode->getQualifier() == EvqGlobal);
Olli Etuahod4f4c112016-04-15 15:11:24 +030081 }
Olli Etuaho1dd07a62017-05-12 17:01:22 +030082 // Remove the initializer from the global scope and just declare the global instead.
83 declaration->replaceChildNode(init, symbolNode);
Olli Etuahod4f4c112016-04-15 15:11:24 +030084 }
Olli Etuaho3d932d82016-04-12 11:10:30 +030085 }
Olli Etuaho0ffc4412017-05-19 14:18:55 +030086 else if (initializeUninitializedGlobals)
87 {
88 TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
89 ASSERT(symbolNode);
Martin Radev69056a12017-05-18 11:14:50 +030090
91 // Ignore ANGLE internal variables.
92 if (symbolNode->getName().isInternal())
93 continue;
94
Olli Etuaho0ffc4412017-05-19 14:18:55 +030095 if (symbolNode->getQualifier() == EvqGlobal && symbolNode->getSymbol() != "")
96 {
97 TIntermSequence *initCode = CreateInitCode(symbolNode);
98 deferredInitializersOut->insert(deferredInitializersOut->end(), initCode->begin(),
99 initCode->end());
100 }
101 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300102 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300103}
104
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300105void InsertInitCallToMain(TIntermBlock *root,
106 TIntermSequence *deferredInitializers,
107 TSymbolTable *symbolTable)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300108{
Olli Etuaho83498432017-05-19 14:57:23 +0300109 TIntermBlock *initGlobalsBlock = new TIntermBlock();
110 initGlobalsBlock->getSequence()->swap(*deferredInitializers);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300111
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300112 TSymbolUniqueId initGlobalsFunctionId(symbolTable);
113
114 const char *kInitGlobalsFunctionName = "initGlobals";
115
116 TIntermFunctionPrototype *initGlobalsFunctionPrototype =
117 CreateInternalFunctionPrototypeNode(TType(), kInitGlobalsFunctionName, initGlobalsFunctionId);
118 root->getSequence()->insert(root->getSequence()->begin(), initGlobalsFunctionPrototype);
119 TIntermFunctionDefinition *initGlobalsFunctionDefinition = CreateInternalFunctionDefinitionNode(
120 TType(), kInitGlobalsFunctionName, initGlobalsBlock, initGlobalsFunctionId);
121 root->appendStatement(initGlobalsFunctionDefinition);
122
123 TIntermAggregate *initGlobalsCall = CreateInternalFunctionCallNode(
124 TType(), kInitGlobalsFunctionName, initGlobalsFunctionId, new TIntermSequence());
125
Martin Radevb50ccd32017-07-06 17:09:58 +0300126 TIntermBlock *mainBody = FindMainBody(root);
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300127 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsCall);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300128}
129
130} // namespace
131
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300132void DeferGlobalInitializers(TIntermBlock *root,
133 bool initializeUninitializedGlobals,
134 TSymbolTable *symbolTable)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300135{
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300136 TIntermSequence *deferredInitializers = new TIntermSequence();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300137
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300138 // Loop over all global statements and process the declarations. This is simpler than using a
139 // traverser.
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300140 for (TIntermNode *statement : *root->getSequence())
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300141 {
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300142 TIntermDeclaration *declaration = statement->getAsDeclarationNode();
143 if (declaration)
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300144 {
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300145 GetDeferredInitializers(declaration, initializeUninitializedGlobals,
146 deferredInitializers);
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300147 }
148 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300149
150 // Add the function with initialization and the call to that.
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300151 if (!deferredInitializers->empty())
152 {
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300153 InsertInitCallToMain(root, deferredInitializers, symbolTable);
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300154 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300155}
Jamie Madill45bcc782016-11-07 13:58:48 -0500156
157} // namespace sh