Edit

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

Branch :

  • Show log

    Commit

  • Author : Shahbaz Youssefi
    Date : 2021-01-19 12:38:46
    Hash : f0286e63
    Message : Translator: Stop allocating TIntermSequence The functions that take a TIntermSequence always copy out / Swap the contents away. This change makes all TIntermSequences live on the stack instead of being newed. Bug: angleproject:5535 Change-Id: I942f1c5e57b00199d5308183f71bd9e18b0608bd Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2636679 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Tim Van Patten <timvp@google.com> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>

  • src/compiler/translator/tree_ops/InitializeVariables.cpp
  • //
    // Copyright 2002 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.
    //
    
    #include "compiler/translator/tree_ops/InitializeVariables.h"
    
    #include "angle_gl.h"
    #include "common/debug.h"
    #include "compiler/translator/Compiler.h"
    #include "compiler/translator/StaticType.h"
    #include "compiler/translator/SymbolTable.h"
    #include "compiler/translator/tree_util/FindMain.h"
    #include "compiler/translator/tree_util/IntermNode_util.h"
    #include "compiler/translator/tree_util/IntermTraverse.h"
    #include "compiler/translator/util.h"
    
    namespace sh
    {
    
    namespace
    {
    
    void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
                                  bool canUseLoopsToInitialize,
                                  bool highPrecisionSupported,
                                  TIntermSequence *initSequenceOut,
                                  TSymbolTable *symbolTable);
    
    void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
                                   bool canUseLoopsToInitialize,
                                   bool highPrecisionSupported,
                                   TIntermSequence *initSequenceOut,
                                   TSymbolTable *symbolTable);
    
    TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode)
    {
        TIntermTyped *zero = CreateZeroNode(initializedNode->getType());
        return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero);
    }
    
    void AddZeroInitSequence(const TIntermTyped *initializedNode,
                             bool canUseLoopsToInitialize,
                             bool highPrecisionSupported,
                             TIntermSequence *initSequenceOut,
                             TSymbolTable *symbolTable)
    {
        if (initializedNode->isArray())
        {
            AddArrayZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported,
                                     initSequenceOut, symbolTable);
        }
        else if (initializedNode->getType().isStructureContainingArrays() ||
                 initializedNode->getType().isNamelessStruct())
        {
            AddStructZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported,
                                      initSequenceOut, symbolTable);
        }
        else
        {
            initSequenceOut->push_back(CreateZeroInitAssignment(initializedNode));
        }
    }
    
    void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
                                   bool canUseLoopsToInitialize,
                                   bool highPrecisionSupported,
                                   TIntermSequence *initSequenceOut,
                                   TSymbolTable *symbolTable)
    {
        ASSERT(initializedNode->getBasicType() == EbtStruct);
        const TStructure *structType = initializedNode->getType().getStruct();
        for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i)
        {
            TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct,
                                                       initializedNode->deepCopy(), CreateIndexNode(i));
            // Structs can't be defined inside structs, so the type of a struct field can't be a
            // nameless struct.
            ASSERT(!element->getType().isNamelessStruct());
            AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported,
                                initSequenceOut, symbolTable);
        }
    }
    
    void AddArrayZeroInitStatementList(const TIntermTyped *initializedNode,
                                       bool canUseLoopsToInitialize,
                                       bool highPrecisionSupported,
                                       TIntermSequence *initSequenceOut,
                                       TSymbolTable *symbolTable)
    {
        for (unsigned int i = 0; i < initializedNode->getOutermostArraySize(); ++i)
        {
            TIntermBinary *element =
                new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(), CreateIndexNode(i));
            AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported,
                                initSequenceOut, symbolTable);
        }
    }
    
    void AddArrayZeroInitForLoop(const TIntermTyped *initializedNode,
                                 bool highPrecisionSupported,
                                 TIntermSequence *initSequenceOut,
                                 TSymbolTable *symbolTable)
    {
        ASSERT(initializedNode->isArray());
        const TType *mediumpIndexType = StaticType::Get<EbtInt, EbpMedium, EvqTemporary, 1, 1>();
        const TType *highpIndexType   = StaticType::Get<EbtInt, EbpHigh, EvqTemporary, 1, 1>();
        TVariable *indexVariable =
            CreateTempVariable(symbolTable, highPrecisionSupported ? highpIndexType : mediumpIndexType);
    
        TIntermSymbol *indexSymbolNode = CreateTempSymbolNode(indexVariable);
        TIntermDeclaration *indexInit =
            CreateTempInitDeclarationNode(indexVariable, CreateZeroNode(indexVariable->getType()));
        TIntermConstantUnion *arraySizeNode = CreateIndexNode(initializedNode->getOutermostArraySize());
        TIntermBinary *indexSmallerThanSize =
            new TIntermBinary(EOpLessThan, indexSymbolNode->deepCopy(), arraySizeNode);
        TIntermUnary *indexIncrement =
            new TIntermUnary(EOpPreIncrement, indexSymbolNode->deepCopy(), nullptr);
    
        TIntermBlock *forLoopBody       = new TIntermBlock();
        TIntermSequence *forLoopBodySeq = forLoopBody->getSequence();
    
        TIntermBinary *element = new TIntermBinary(EOpIndexIndirect, initializedNode->deepCopy(),
                                                   indexSymbolNode->deepCopy());
        AddZeroInitSequence(element, true, highPrecisionSupported, forLoopBodySeq, symbolTable);
    
        TIntermLoop *forLoop =
            new TIntermLoop(ELoopFor, indexInit, indexSmallerThanSize, indexIncrement, forLoopBody);
        initSequenceOut->push_back(forLoop);
    }
    
    void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
                                  bool canUseLoopsToInitialize,
                                  bool highPrecisionSupported,
                                  TIntermSequence *initSequenceOut,
                                  TSymbolTable *symbolTable)
    {
        // The array elements are assigned one by one to keep the AST compatible with ESSL 1.00 which
        // doesn't have array assignment. We'll do this either with a for loop or just a list of
        // statements assigning to each array index. Note that it is important to have the array init in
        // the right order to workaround http://crbug.com/709317
        bool isSmallArray = initializedNode->getOutermostArraySize() <= 1u ||
                            (initializedNode->getBasicType() != EbtStruct &&
                             !initializedNode->getType().isArrayOfArrays() &&
                             initializedNode->getOutermostArraySize() <= 3u);
        if (initializedNode->getQualifier() == EvqFragData ||
            initializedNode->getQualifier() == EvqFragmentOut || isSmallArray ||
            !canUseLoopsToInitialize)
        {
            // Fragment outputs should not be indexed by non-constant indices.
            // Also it doesn't make sense to use loops to initialize very small arrays.
            AddArrayZeroInitStatementList(initializedNode, canUseLoopsToInitialize,
                                          highPrecisionSupported, initSequenceOut, symbolTable);
        }
        else
        {
            AddArrayZeroInitForLoop(initializedNode, highPrecisionSupported, initSequenceOut,
                                    symbolTable);
        }
    }
    
    void InsertInitCode(TCompiler *compiler,
                        TIntermSequence *mainBody,
                        const InitVariableList &variables,
                        TSymbolTable *symbolTable,
                        int shaderVersion,
                        const TExtensionBehavior &extensionBehavior,
                        bool canUseLoopsToInitialize,
                        bool highPrecisionSupported)
    {
        for (const auto &var : variables)
        {
            // Note that tempVariableName will reference a short-lived char array here - that's fine
            // since we're only using it to find symbols.
            ImmutableString tempVariableName(var.name.c_str(), var.name.length());
    
            TIntermTyped *initializedSymbol = nullptr;
            if (var.isBuiltIn())
            {
                initializedSymbol =
                    ReferenceBuiltInVariable(tempVariableName, *symbolTable, shaderVersion);
                if (initializedSymbol->getQualifier() == EvqFragData &&
                    !IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
                {
                    // If GL_EXT_draw_buffers is disabled, only the 0th index of gl_FragData can be
                    // written to.
                    // TODO(oetuaho): This is a bit hacky and would be better to remove, if we came up
                    // with a good way to do it. Right now "gl_FragData" in symbol table is initialized
                    // to have the array size of MaxDrawBuffers, and the initialization happens before
                    // the shader sets the extensions it is using.
                    initializedSymbol =
                        new TIntermBinary(EOpIndexDirect, initializedSymbol, CreateIndexNode(0));
                }
            }
            else
            {
                initializedSymbol = ReferenceGlobalVariable(tempVariableName, *symbolTable);
            }
            ASSERT(initializedSymbol != nullptr);
    
            TIntermSequence initCode;
            CreateInitCode(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported,
                           &initCode, symbolTable);
            mainBody->insert(mainBody->begin(), initCode.begin(), initCode.end());
        }
    }
    
    class InitializeLocalsTraverser : public TIntermTraverser
    {
      public:
        InitializeLocalsTraverser(int shaderVersion,
                                  TSymbolTable *symbolTable,
                                  bool canUseLoopsToInitialize,
                                  bool highPrecisionSupported)
            : TIntermTraverser(true, false, false, symbolTable),
              mShaderVersion(shaderVersion),
              mCanUseLoopsToInitialize(canUseLoopsToInitialize),
              mHighPrecisionSupported(highPrecisionSupported)
        {}
    
      protected:
        bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
        {
            for (TIntermNode *declarator : *node->getSequence())
            {
                if (!mInGlobalScope && !declarator->getAsBinaryNode())
                {
                    TIntermSymbol *symbol = declarator->getAsSymbolNode();
                    ASSERT(symbol);
                    if (symbol->variable().symbolType() == SymbolType::Empty)
                    {
                        continue;
                    }
    
                    // Arrays may need to be initialized one element at a time, since ESSL 1.00 does not
                    // support array constructors or assigning arrays.
                    bool arrayConstructorUnavailable =
                        (symbol->isArray() || symbol->getType().isStructureContainingArrays()) &&
                        mShaderVersion == 100;
                    // Nameless struct constructors can't be referred to, so they also need to be
                    // initialized one element at a time.
                    // TODO(oetuaho): Check if it makes sense to initialize using a loop, even if we
                    // could use an initializer. It could at least reduce code size for very large
                    // arrays, but could hurt runtime performance.
                    if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct())
                    {
                        // SimplifyLoopConditions should have been run so the parent node of this node
                        // should not be a loop.
                        ASSERT(getParentNode()->getAsLoopNode() == nullptr);
                        // SeparateDeclarations should have already been run, so we don't need to worry
                        // about further declarators in this declaration depending on the effects of
                        // this declarator.
                        ASSERT(node->getSequence()->size() == 1);
                        TIntermSequence initCode;
                        CreateInitCode(symbol, mCanUseLoopsToInitialize, mHighPrecisionSupported,
                                       &initCode, mSymbolTable);
                        insertStatementsInParentBlock(TIntermSequence(), initCode);
                    }
                    else
                    {
                        TIntermBinary *init =
                            new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType()));
                        queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
                    }
                }
            }
            return false;
        }
    
      private:
        int mShaderVersion;
        bool mCanUseLoopsToInitialize;
        bool mHighPrecisionSupported;
    };
    
    }  // namespace
    
    void CreateInitCode(const TIntermTyped *initializedSymbol,
                        bool canUseLoopsToInitialize,
                        bool highPrecisionSupported,
                        TIntermSequence *initCode,
                        TSymbolTable *symbolTable)
    {
        AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported,
                            initCode, symbolTable);
    }
    
    bool InitializeUninitializedLocals(TCompiler *compiler,
                                       TIntermBlock *root,
                                       int shaderVersion,
                                       bool canUseLoopsToInitialize,
                                       bool highPrecisionSupported,
                                       TSymbolTable *symbolTable)
    {
        InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize,
                                            highPrecisionSupported);
        root->traverse(&traverser);
        return traverser.updateTree(compiler, root);
    }
    
    bool InitializeVariables(TCompiler *compiler,
                             TIntermBlock *root,
                             const InitVariableList &vars,
                             TSymbolTable *symbolTable,
                             int shaderVersion,
                             const TExtensionBehavior &extensionBehavior,
                             bool canUseLoopsToInitialize,
                             bool highPrecisionSupported)
    {
        TIntermBlock *body = FindMainBody(root);
        InsertInitCode(compiler, body->getSequence(), vars, symbolTable, shaderVersion,
                       extensionBehavior, canUseLoopsToInitialize, highPrecisionSupported);
    
        return compiler->validateAST(root);
    }
    
    }  // namespace sh