Edit

kc3-lang/angle/src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp

Branch :

  • Show log

    Commit

  • Author : Gregg Tavares
    Date : 2021-08-25 15:56:59
    Hash : 4c56534f
    Message : Include globals when defering global initializers Fixes these tests: GLSLTest.StructWithInitializer/ES2_Metal GLSLTest.StructWithInitializer/ES3_Metal GLSLTest.StructWithUniformInitializer/ES2_Metal GLSLTest.StructWithUniformInitializer/ES3_Metal GLSLTest_ES3.SequenceOperatorEvaluationOrderDynamicVectorIndexingInLValue/ES3_Metal WebGL2GLSLTest.InitUninitializedLocals/ES3_Metal Bug: angleproject:5505 Change-Id: Ib8258898c60b9e9ffbb71f0024f8189dc6cf4d5b Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3120093 Commit-Queue: Gregg Tavares <gman@chromium.org> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Kenneth Russell <kbr@chromium.org>

  • src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp
  • //
    // 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 <vector>
    
    #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<const TVariable *> *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<EbtVoid, EbpUndefined>(), 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<const TVariable *> 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