blob: 0e14054ee546354b57a705b70046537adbc1041e [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 Etuahocf180fc2018-01-04 16:25:40 +020018#include <vector>
19
Olli Etuaho9cbc07c2017-05-10 18:22:01 +030020#include "compiler/translator/FindMain.h"
Olli Etuaho0ffc4412017-05-19 14:18:55 +030021#include "compiler/translator/InitializeVariables.h"
Olli Etuaho3d932d82016-04-12 11:10:30 +030022#include "compiler/translator/IntermNode.h"
Olli Etuahoa16a84f2017-09-12 13:49:18 +030023#include "compiler/translator/IntermNode_util.h"
Olli Etuahocf180fc2018-01-04 16:25:40 +020024#include "compiler/translator/ReplaceVariable.h"
Olli Etuaho029e8ca2018-02-16 14:06:49 +020025#include "compiler/translator/StaticType.h"
Olli Etuaho3d932d82016-04-12 11:10:30 +030026#include "compiler/translator/SymbolTable.h"
27
Jamie Madill45bcc782016-11-07 13:58:48 -050028namespace sh
29{
30
Olli Etuaho3d932d82016-04-12 11:10:30 +030031namespace
32{
33
Olli Etuahofbb1c792018-01-19 16:26:59 +020034constexpr const ImmutableString kInitGlobalsString("initGlobals");
35
Olli Etuaho1dd07a62017-05-12 17:01:22 +030036void GetDeferredInitializers(TIntermDeclaration *declaration,
Olli Etuaho0ffc4412017-05-19 14:18:55 +030037 bool initializeUninitializedGlobals,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030038 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020039 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030040 TIntermSequence *deferredInitializersOut,
Olli Etuahocf180fc2018-01-04 16:25:40 +020041 std::vector<const TVariable *> *variablesToReplaceOut,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030042 TSymbolTable *symbolTable)
Olli Etuaho3d932d82016-04-12 11:10:30 +030043{
Olli Etuahob5601eb2017-11-15 18:08:04 +020044 // SeparateDeclarations should have already been run.
45 ASSERT(declaration->getSequence()->size() == 1);
46
47 TIntermNode *declarator = declaration->getSequence()->back();
48 TIntermBinary *init = declarator->getAsBinaryNode();
49 if (init)
Olli Etuaho3d932d82016-04-12 11:10:30 +030050 {
Olli Etuahob5601eb2017-11-15 18:08:04 +020051 TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode();
52 ASSERT(symbolNode);
53 TIntermTyped *expression = init->getRight();
54
Olli Etuahoea22b7a2018-01-04 17:09:11 +020055 if (expression->getQualifier() != EvqConst || !expression->hasConstantValue())
Olli Etuaho3d932d82016-04-12 11:10:30 +030056 {
Olli Etuahob5601eb2017-11-15 18:08:04 +020057 // For variables which are not constant, defer their real initialization until
58 // after we initialize uniforms.
Olli Etuahoea22b7a2018-01-04 17:09:11 +020059 // Deferral is done also in any cases where the variable can not be converted to a
60 // constant union, since otherwise there's a chance that HLSL output will generate extra
Olli Etuahob5601eb2017-11-15 18:08:04 +020061 // statements from the initializer expression.
Olli Etuaho3d932d82016-04-12 11:10:30 +030062
Olli Etuahob5601eb2017-11-15 18:08:04 +020063 // 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)
Olli Etuahod4f4c112016-04-15 15:11:24 +030069 {
Olli Etuahocf180fc2018-01-04 16:25:40 +020070 variablesToReplaceOut->push_back(&symbolNode->variable());
Olli Etuahod4f4c112016-04-15 15:11:24 +030071 }
Olli Etuahob5601eb2017-11-15 18:08:04 +020072
73 TIntermBinary *deferredInit =
74 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight());
75 deferredInitializersOut->push_back(deferredInit);
76
77 // Remove the initializer from the global scope and just declare the global instead.
78 declaration->replaceChildNode(init, symbolNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +030079 }
Olli Etuahob5601eb2017-11-15 18:08:04 +020080 }
81 else if (initializeUninitializedGlobals)
82 {
83 TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
84 ASSERT(symbolNode);
85
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +020086 // Ignore ANGLE internal variables and nameless declarations.
87 if (symbolNode->variable().symbolType() == SymbolType::AngleInternal ||
88 symbolNode->variable().symbolType() == SymbolType::Empty)
Olli Etuahob5601eb2017-11-15 18:08:04 +020089 return;
90
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +020091 if (symbolNode->getQualifier() == EvqGlobal)
Olli Etuaho0ffc4412017-05-19 14:18:55 +030092 {
Olli Etuaho87cc90d2017-12-12 15:28:06 +020093 TIntermSequence *initCode = CreateInitCode(symbolNode, canUseLoopsToInitialize,
94 highPrecisionSupported, symbolTable);
Olli Etuahob5601eb2017-11-15 18:08:04 +020095 deferredInitializersOut->insert(deferredInitializersOut->end(), initCode->begin(),
96 initCode->end());
Olli Etuaho0ffc4412017-05-19 14:18:55 +030097 }
Olli Etuaho3d932d82016-04-12 11:10:30 +030098 }
Olli Etuaho3d932d82016-04-12 11:10:30 +030099}
100
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300101void InsertInitCallToMain(TIntermBlock *root,
102 TIntermSequence *deferredInitializers,
103 TSymbolTable *symbolTable)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300104{
Olli Etuaho83498432017-05-19 14:57:23 +0300105 TIntermBlock *initGlobalsBlock = new TIntermBlock();
106 initGlobalsBlock->getSequence()->swap(*deferredInitializers);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300107
Olli Etuaho029e8ca2018-02-16 14:06:49 +0200108 TFunction *initGlobalsFunction =
109 new TFunction(symbolTable, kInitGlobalsString, SymbolType::AngleInternal,
110 StaticType::GetBasic<EbtVoid>(), false);
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300111
112 TIntermFunctionPrototype *initGlobalsFunctionPrototype =
Olli Etuaho0c371002017-12-13 17:00:25 +0400113 CreateInternalFunctionPrototypeNode(*initGlobalsFunction);
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300114 root->getSequence()->insert(root->getSequence()->begin(), initGlobalsFunctionPrototype);
Olli Etuaho0c371002017-12-13 17:00:25 +0400115 TIntermFunctionDefinition *initGlobalsFunctionDefinition =
116 CreateInternalFunctionDefinitionNode(*initGlobalsFunction, initGlobalsBlock);
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300117 root->appendStatement(initGlobalsFunctionDefinition);
118
Olli Etuaho0c371002017-12-13 17:00:25 +0400119 TIntermAggregate *initGlobalsCall =
120 TIntermAggregate::CreateFunctionCall(*initGlobalsFunction, new TIntermSequence());
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300121
Martin Radevb50ccd32017-07-06 17:09:58 +0300122 TIntermBlock *mainBody = FindMainBody(root);
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300123 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsCall);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300124}
125
126} // namespace
127
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300128void DeferGlobalInitializers(TIntermBlock *root,
129 bool initializeUninitializedGlobals,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300130 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200131 bool highPrecisionSupported,
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300132 TSymbolTable *symbolTable)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300133{
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300134 TIntermSequence *deferredInitializers = new TIntermSequence();
Olli Etuahocf180fc2018-01-04 16:25:40 +0200135 std::vector<const TVariable *> variablesToReplace;
Olli Etuaho3d932d82016-04-12 11:10:30 +0300136
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300137 // Loop over all global statements and process the declarations. This is simpler than using a
138 // traverser.
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300139 for (TIntermNode *statement : *root->getSequence())
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300140 {
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300141 TIntermDeclaration *declaration = statement->getAsDeclarationNode();
142 if (declaration)
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300143 {
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300144 GetDeferredInitializers(declaration, initializeUninitializedGlobals,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200145 canUseLoopsToInitialize, highPrecisionSupported,
Olli Etuahocf180fc2018-01-04 16:25:40 +0200146 deferredInitializers, &variablesToReplace, symbolTable);
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 Etuahocf180fc2018-01-04 16:25:40 +0200155
156 // Replace constant variables with non-constant global variables.
157 for (const TVariable *var : variablesToReplace)
158 {
Olli Etuahob60d30f2018-01-16 12:31:06 +0200159 TType *replacementType = new TType(var->getType());
160 replacementType->setQualifier(EvqGlobal);
Olli Etuahocf180fc2018-01-04 16:25:40 +0200161 TVariable *replacement =
Olli Etuahofbb1c792018-01-19 16:26:59 +0200162 new TVariable(symbolTable, var->name(), replacementType, var->symbolType());
Olli Etuahocf180fc2018-01-04 16:25:40 +0200163 ReplaceVariable(root, var, replacement);
164 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300165}
Jamie Madill45bcc782016-11-07 13:58:48 -0500166
167} // namespace sh