From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- .../tree_ops/DeferGlobalInitializers.cpp | 180 +++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 gfx/angle/checkout/src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp (limited to 'gfx/angle/checkout/src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp') diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp new file mode 100644 index 0000000000..035de73431 --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp @@ -0,0 +1,180 @@ +// +// Copyright 2016 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// DeferGlobalInitializers is an AST traverser that moves global initializers into a separate +// function that is called in the beginning of main(). This enables initialization of globals with +// uniforms or non-constant globals, as allowed by the WebGL spec. Some initializers referencing +// non-constants may need to be unfolded into if statements in HLSL - this kind of steps should be +// done after DeferGlobalInitializers is run. Note that it's important that the function definition +// is at the end of the shader, as some globals may be declared after main(). +// +// It can also initialize all uninitialized globals. +// + +#include "compiler/translator/tree_ops/DeferGlobalInitializers.h" + +#include + +#include "compiler/translator/Compiler.h" +#include "compiler/translator/IntermNode.h" +#include "compiler/translator/StaticType.h" +#include "compiler/translator/SymbolTable.h" +#include "compiler/translator/tree_ops/InitializeVariables.h" +#include "compiler/translator/tree_util/FindMain.h" +#include "compiler/translator/tree_util/IntermNode_util.h" +#include "compiler/translator/tree_util/ReplaceVariable.h" + +namespace sh +{ + +namespace +{ + +constexpr const ImmutableString kInitGlobalsString("initGlobals"); + +void GetDeferredInitializers(TIntermDeclaration *declaration, + bool initializeUninitializedGlobals, + bool canUseLoopsToInitialize, + bool highPrecisionSupported, + bool forceDeferGlobalInitializers, + TIntermSequence *deferredInitializersOut, + std::vector *variablesToReplaceOut, + TSymbolTable *symbolTable) +{ + // SeparateDeclarations should have already been run. + ASSERT(declaration->getSequence()->size() == 1); + + TIntermNode *declarator = declaration->getSequence()->back(); + TIntermBinary *init = declarator->getAsBinaryNode(); + if (init) + { + TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode(); + ASSERT(symbolNode); + TIntermTyped *expression = init->getRight(); + + if (expression->getQualifier() != EvqConst || !expression->hasConstantValue() || + forceDeferGlobalInitializers) + { + // For variables which are not constant, defer their real initialization until + // after we initialize uniforms. + // Deferral is done also in any cases where the variable can not be converted to a + // constant union, since otherwise there's a chance that HLSL output will generate extra + // statements from the initializer expression. + + // Change const global to a regular global if its initialization is deferred. + // This can happen if ANGLE has not been able to fold the constant expression used + // as an initializer. + ASSERT(symbolNode->getQualifier() == EvqConst || + symbolNode->getQualifier() == EvqGlobal); + if (symbolNode->getQualifier() == EvqConst) + { + variablesToReplaceOut->push_back(&symbolNode->variable()); + } + + TIntermBinary *deferredInit = + new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight()); + deferredInitializersOut->push_back(deferredInit); + + // Remove the initializer from the global scope and just declare the global instead. + declaration->replaceChildNode(init, symbolNode); + } + } + else if (initializeUninitializedGlobals) + { + TIntermSymbol *symbolNode = declarator->getAsSymbolNode(); + ASSERT(symbolNode); + + // Ignore ANGLE internal variables and nameless declarations. + if (symbolNode->variable().symbolType() == SymbolType::AngleInternal || + symbolNode->variable().symbolType() == SymbolType::Empty) + return; + + if (symbolNode->getQualifier() == EvqGlobal) + { + TIntermSequence initCode; + CreateInitCode(symbolNode, canUseLoopsToInitialize, highPrecisionSupported, &initCode, + symbolTable); + deferredInitializersOut->insert(deferredInitializersOut->end(), initCode.begin(), + initCode.end()); + } + } +} + +void InsertInitCallToMain(TIntermBlock *root, + TIntermSequence *deferredInitializers, + TSymbolTable *symbolTable) +{ + TIntermBlock *initGlobalsBlock = new TIntermBlock(); + initGlobalsBlock->getSequence()->swap(*deferredInitializers); + + TFunction *initGlobalsFunction = + new TFunction(symbolTable, kInitGlobalsString, SymbolType::AngleInternal, + StaticType::GetBasic(), false); + + TIntermFunctionPrototype *initGlobalsFunctionPrototype = + CreateInternalFunctionPrototypeNode(*initGlobalsFunction); + root->getSequence()->insert(root->getSequence()->begin(), initGlobalsFunctionPrototype); + TIntermFunctionDefinition *initGlobalsFunctionDefinition = + CreateInternalFunctionDefinitionNode(*initGlobalsFunction, initGlobalsBlock); + root->appendStatement(initGlobalsFunctionDefinition); + + TIntermSequence emptySequence; + TIntermAggregate *initGlobalsCall = + TIntermAggregate::CreateFunctionCall(*initGlobalsFunction, &emptySequence); + + TIntermBlock *mainBody = FindMainBody(root); + mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsCall); +} + +} // namespace + +bool DeferGlobalInitializers(TCompiler *compiler, + TIntermBlock *root, + bool initializeUninitializedGlobals, + bool canUseLoopsToInitialize, + bool highPrecisionSupported, + bool forceDeferGlobalInitializers, + TSymbolTable *symbolTable) +{ + TIntermSequence deferredInitializers; + std::vector variablesToReplace; + + // Loop over all global statements and process the declarations. This is simpler than using a + // traverser. + for (TIntermNode *statement : *root->getSequence()) + { + TIntermDeclaration *declaration = statement->getAsDeclarationNode(); + if (declaration) + { + GetDeferredInitializers(declaration, initializeUninitializedGlobals, + canUseLoopsToInitialize, highPrecisionSupported, + forceDeferGlobalInitializers, &deferredInitializers, + &variablesToReplace, symbolTable); + } + } + + // Add the function with initialization and the call to that. + if (!deferredInitializers.empty()) + { + InsertInitCallToMain(root, &deferredInitializers, symbolTable); + } + + // Replace constant variables with non-constant global variables. + for (const TVariable *var : variablesToReplace) + { + TType *replacementType = new TType(var->getType()); + replacementType->setQualifier(EvqGlobal); + TVariable *replacement = + new TVariable(symbolTable, var->name(), replacementType, var->symbolType()); + if (!ReplaceVariable(compiler, root, var, replacement)) + { + return false; + } + } + + return true; +} + +} // namespace sh -- cgit v1.2.3