tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

InitializeVariables.cpp (17704B)


      1 //
      2 // Copyright 2002 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 //
      6 
      7 #include "compiler/translator/tree_ops/InitializeVariables.h"
      8 
      9 #include "angle_gl.h"
     10 #include "common/debug.h"
     11 #include "compiler/translator/Compiler.h"
     12 #include "compiler/translator/StaticType.h"
     13 #include "compiler/translator/SymbolTable.h"
     14 #include "compiler/translator/tree_util/FindMain.h"
     15 #include "compiler/translator/tree_util/IntermNode_util.h"
     16 #include "compiler/translator/tree_util/IntermTraverse.h"
     17 #include "compiler/translator/util.h"
     18 
     19 namespace sh
     20 {
     21 
     22 namespace
     23 {
     24 
     25 void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
     26                              bool canUseLoopsToInitialize,
     27                              bool highPrecisionSupported,
     28                              TIntermSequence *initSequenceOut,
     29                              TSymbolTable *symbolTable);
     30 
     31 void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
     32                               bool canUseLoopsToInitialize,
     33                               bool highPrecisionSupported,
     34                               TIntermSequence *initSequenceOut,
     35                               TSymbolTable *symbolTable);
     36 
     37 TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode)
     38 {
     39    TIntermTyped *zero = CreateZeroNode(initializedNode->getType());
     40    return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero);
     41 }
     42 
     43 void AddZeroInitSequence(const TIntermTyped *initializedNode,
     44                         bool canUseLoopsToInitialize,
     45                         bool highPrecisionSupported,
     46                         TIntermSequence *initSequenceOut,
     47                         TSymbolTable *symbolTable)
     48 {
     49    if (initializedNode->isArray())
     50    {
     51        AddArrayZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported,
     52                                 initSequenceOut, symbolTable);
     53    }
     54    else if (initializedNode->getType().isStructureContainingArrays() ||
     55             initializedNode->getType().isNamelessStruct())
     56    {
     57        AddStructZeroInitSequence(initializedNode, canUseLoopsToInitialize, highPrecisionSupported,
     58                                  initSequenceOut, symbolTable);
     59    }
     60    else if (initializedNode->getType().isInterfaceBlock())
     61    {
     62        const TType &type                     = initializedNode->getType();
     63        const TInterfaceBlock &interfaceBlock = *type.getInterfaceBlock();
     64        const TFieldList &fieldList           = interfaceBlock.fields();
     65        for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex)
     66        {
     67            const TField &field         = *fieldList[fieldIndex];
     68            TIntermTyped *fieldIndexRef = CreateIndexNode(static_cast<int>(fieldIndex));
     69            TIntermTyped *fieldReference =
     70                new TIntermBinary(TOperator::EOpIndexDirectInterfaceBlock,
     71                                  initializedNode->deepCopy(), fieldIndexRef);
     72            TIntermTyped *fieldZero = CreateZeroNode(*field.type());
     73            TIntermTyped *assignment =
     74                new TIntermBinary(TOperator::EOpAssign, fieldReference, fieldZero);
     75            initSequenceOut->push_back(assignment);
     76        }
     77    }
     78    else
     79    {
     80        initSequenceOut->push_back(CreateZeroInitAssignment(initializedNode));
     81    }
     82 }
     83 
     84 void AddStructZeroInitSequence(const TIntermTyped *initializedNode,
     85                               bool canUseLoopsToInitialize,
     86                               bool highPrecisionSupported,
     87                               TIntermSequence *initSequenceOut,
     88                               TSymbolTable *symbolTable)
     89 {
     90    ASSERT(initializedNode->getBasicType() == EbtStruct);
     91    const TStructure *structType = initializedNode->getType().getStruct();
     92    for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i)
     93    {
     94        TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct,
     95                                                   initializedNode->deepCopy(), CreateIndexNode(i));
     96        // Structs can't be defined inside structs, so the type of a struct field can't be a
     97        // nameless struct.
     98        ASSERT(!element->getType().isNamelessStruct());
     99        AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported,
    100                            initSequenceOut, symbolTable);
    101    }
    102 }
    103 
    104 void AddArrayZeroInitStatementList(const TIntermTyped *initializedNode,
    105                                   bool canUseLoopsToInitialize,
    106                                   bool highPrecisionSupported,
    107                                   TIntermSequence *initSequenceOut,
    108                                   TSymbolTable *symbolTable)
    109 {
    110    for (unsigned int i = 0; i < initializedNode->getOutermostArraySize(); ++i)
    111    {
    112        TIntermBinary *element =
    113            new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(), CreateIndexNode(i));
    114        AddZeroInitSequence(element, canUseLoopsToInitialize, highPrecisionSupported,
    115                            initSequenceOut, symbolTable);
    116    }
    117 }
    118 
    119 void AddArrayZeroInitForLoop(const TIntermTyped *initializedNode,
    120                             bool highPrecisionSupported,
    121                             TIntermSequence *initSequenceOut,
    122                             TSymbolTable *symbolTable)
    123 {
    124    ASSERT(initializedNode->isArray());
    125    const TType *mediumpIndexType = StaticType::Get<EbtInt, EbpMedium, EvqTemporary, 1, 1>();
    126    const TType *highpIndexType   = StaticType::Get<EbtInt, EbpHigh, EvqTemporary, 1, 1>();
    127    TVariable *indexVariable =
    128        CreateTempVariable(symbolTable, highPrecisionSupported ? highpIndexType : mediumpIndexType);
    129 
    130    TIntermSymbol *indexSymbolNode = CreateTempSymbolNode(indexVariable);
    131    TIntermDeclaration *indexInit =
    132        CreateTempInitDeclarationNode(indexVariable, CreateZeroNode(indexVariable->getType()));
    133    TIntermConstantUnion *arraySizeNode = CreateIndexNode(initializedNode->getOutermostArraySize());
    134    TIntermBinary *indexSmallerThanSize =
    135        new TIntermBinary(EOpLessThan, indexSymbolNode->deepCopy(), arraySizeNode);
    136    TIntermUnary *indexIncrement =
    137        new TIntermUnary(EOpPreIncrement, indexSymbolNode->deepCopy(), nullptr);
    138 
    139    TIntermBlock *forLoopBody       = new TIntermBlock();
    140    TIntermSequence *forLoopBodySeq = forLoopBody->getSequence();
    141 
    142    TIntermBinary *element = new TIntermBinary(EOpIndexIndirect, initializedNode->deepCopy(),
    143                                               indexSymbolNode->deepCopy());
    144    AddZeroInitSequence(element, true, highPrecisionSupported, forLoopBodySeq, symbolTable);
    145 
    146    TIntermLoop *forLoop =
    147        new TIntermLoop(ELoopFor, indexInit, indexSmallerThanSize, indexIncrement, forLoopBody);
    148    initSequenceOut->push_back(forLoop);
    149 }
    150 
    151 void AddArrayZeroInitSequence(const TIntermTyped *initializedNode,
    152                              bool canUseLoopsToInitialize,
    153                              bool highPrecisionSupported,
    154                              TIntermSequence *initSequenceOut,
    155                              TSymbolTable *symbolTable)
    156 {
    157    // The array elements are assigned one by one to keep the AST compatible with ESSL 1.00 which
    158    // doesn't have array assignment. We'll do this either with a for loop or just a list of
    159    // statements assigning to each array index. Note that it is important to have the array init in
    160    // the right order to workaround http://crbug.com/709317
    161    bool isSmallArray = initializedNode->getOutermostArraySize() <= 1u ||
    162                        (initializedNode->getBasicType() != EbtStruct &&
    163                         !initializedNode->getType().isArrayOfArrays() &&
    164                         initializedNode->getOutermostArraySize() <= 3u);
    165    if (initializedNode->getQualifier() == EvqFragData ||
    166        initializedNode->getQualifier() == EvqFragmentOut || isSmallArray ||
    167        !canUseLoopsToInitialize)
    168    {
    169        // Fragment outputs should not be indexed by non-constant indices.
    170        // Also it doesn't make sense to use loops to initialize very small arrays.
    171        AddArrayZeroInitStatementList(initializedNode, canUseLoopsToInitialize,
    172                                      highPrecisionSupported, initSequenceOut, symbolTable);
    173    }
    174    else
    175    {
    176        AddArrayZeroInitForLoop(initializedNode, highPrecisionSupported, initSequenceOut,
    177                                symbolTable);
    178    }
    179 }
    180 
    181 void InsertInitCode(TCompiler *compiler,
    182                    TIntermSequence *mainBody,
    183                    const InitVariableList &variables,
    184                    TSymbolTable *symbolTable,
    185                    int shaderVersion,
    186                    const TExtensionBehavior &extensionBehavior,
    187                    bool canUseLoopsToInitialize,
    188                    bool highPrecisionSupported)
    189 {
    190    for (const ShaderVariable &var : variables)
    191    {
    192        // Note that tempVariableName will reference a short-lived char array here - that's fine
    193        // since we're only using it to find symbols.
    194        ImmutableString tempVariableName(var.name.c_str(), var.name.length());
    195 
    196        TIntermTyped *initializedSymbol = nullptr;
    197        if (var.isBuiltIn() && !symbolTable->findUserDefined(tempVariableName))
    198        {
    199            initializedSymbol =
    200                ReferenceBuiltInVariable(tempVariableName, *symbolTable, shaderVersion);
    201            if (initializedSymbol->getQualifier() == EvqFragData &&
    202                !IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers))
    203            {
    204                // If GL_EXT_draw_buffers is disabled, only the 0th index of gl_FragData can be
    205                // written to.
    206                // TODO(oetuaho): This is a bit hacky and would be better to remove, if we came up
    207                // with a good way to do it. Right now "gl_FragData" in symbol table is initialized
    208                // to have the array size of MaxDrawBuffers, and the initialization happens before
    209                // the shader sets the extensions it is using.
    210                initializedSymbol =
    211                    new TIntermBinary(EOpIndexDirect, initializedSymbol, CreateIndexNode(0));
    212            }
    213        }
    214        else
    215        {
    216            if (tempVariableName != "")
    217            {
    218                initializedSymbol = ReferenceGlobalVariable(tempVariableName, *symbolTable);
    219            }
    220            else
    221            {
    222                // Must be a nameless interface block.
    223                ASSERT(var.structOrBlockName != "");
    224                const TSymbol *symbol = symbolTable->findGlobal(var.structOrBlockName);
    225                ASSERT(symbol && symbol->isInterfaceBlock());
    226                const TInterfaceBlock *block = static_cast<const TInterfaceBlock *>(symbol);
    227 
    228                for (const TField *field : block->fields())
    229                {
    230                    initializedSymbol = ReferenceGlobalVariable(field->name(), *symbolTable);
    231 
    232                    TIntermSequence initCode;
    233                    CreateInitCode(initializedSymbol, canUseLoopsToInitialize,
    234                                   highPrecisionSupported, &initCode, symbolTable);
    235                    mainBody->insert(mainBody->begin(), initCode.begin(), initCode.end());
    236                }
    237                // Already inserted init code in this case
    238                continue;
    239            }
    240        }
    241        ASSERT(initializedSymbol != nullptr);
    242 
    243        TIntermSequence initCode;
    244        CreateInitCode(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported,
    245                       &initCode, symbolTable);
    246        mainBody->insert(mainBody->begin(), initCode.begin(), initCode.end());
    247    }
    248 }
    249 
    250 class InitializeLocalsTraverser : public TIntermTraverser
    251 {
    252  public:
    253    InitializeLocalsTraverser(int shaderVersion,
    254                              TSymbolTable *symbolTable,
    255                              bool canUseLoopsToInitialize,
    256                              bool highPrecisionSupported)
    257        : TIntermTraverser(true, false, false, symbolTable),
    258          mShaderVersion(shaderVersion),
    259          mCanUseLoopsToInitialize(canUseLoopsToInitialize),
    260          mHighPrecisionSupported(highPrecisionSupported)
    261    {}
    262 
    263  protected:
    264    bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
    265    {
    266        for (TIntermNode *declarator : *node->getSequence())
    267        {
    268            if (!mInGlobalScope && !declarator->getAsBinaryNode())
    269            {
    270                TIntermSymbol *symbol = declarator->getAsSymbolNode();
    271                ASSERT(symbol);
    272                if (symbol->variable().symbolType() == SymbolType::Empty)
    273                {
    274                    continue;
    275                }
    276 
    277                // Arrays may need to be initialized one element at a time, since ESSL 1.00 does not
    278                // support array constructors or assigning arrays.
    279                bool arrayConstructorUnavailable =
    280                    (symbol->isArray() || symbol->getType().isStructureContainingArrays()) &&
    281                    mShaderVersion == 100;
    282                // Nameless struct constructors can't be referred to, so they also need to be
    283                // initialized one element at a time.
    284                // TODO(oetuaho): Check if it makes sense to initialize using a loop, even if we
    285                // could use an initializer. It could at least reduce code size for very large
    286                // arrays, but could hurt runtime performance.
    287                if (arrayConstructorUnavailable || symbol->getType().isNamelessStruct())
    288                {
    289                    // SimplifyLoopConditions should have been run so the parent node of this node
    290                    // should not be a loop.
    291                    ASSERT(getParentNode()->getAsLoopNode() == nullptr);
    292                    // SeparateDeclarations should have already been run, so we don't need to worry
    293                    // about further declarators in this declaration depending on the effects of
    294                    // this declarator.
    295                    ASSERT(node->getSequence()->size() == 1);
    296                    TIntermSequence initCode;
    297                    CreateInitCode(symbol, mCanUseLoopsToInitialize, mHighPrecisionSupported,
    298                                   &initCode, mSymbolTable);
    299                    insertStatementsInParentBlock(TIntermSequence(), initCode);
    300                }
    301                else
    302                {
    303                    TIntermBinary *init =
    304                        new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType()));
    305                    queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
    306                }
    307            }
    308        }
    309        return false;
    310    }
    311 
    312    bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
    313    {
    314        // Initialize output function arguments as well, the parameter passed in at call time may be
    315        // clobbered if the function doesn't fully write to the argument.
    316 
    317        TIntermSequence initCode;
    318 
    319        const TFunction *function = node->getFunction();
    320        for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex)
    321        {
    322            const TVariable *paramVariable = function->getParam(paramIndex);
    323            const TType &paramType         = paramVariable->getType();
    324 
    325            if (paramType.getQualifier() != EvqParamOut)
    326            {
    327                continue;
    328            }
    329 
    330            CreateInitCode(new TIntermSymbol(paramVariable), mCanUseLoopsToInitialize,
    331                           mHighPrecisionSupported, &initCode, mSymbolTable);
    332        }
    333 
    334        if (!initCode.empty())
    335        {
    336            TIntermSequence *body = node->getBody()->getSequence();
    337            body->insert(body->begin(), initCode.begin(), initCode.end());
    338        }
    339 
    340        return true;
    341    }
    342 
    343  private:
    344    int mShaderVersion;
    345    bool mCanUseLoopsToInitialize;
    346    bool mHighPrecisionSupported;
    347 };
    348 
    349 }  // namespace
    350 
    351 void CreateInitCode(const TIntermTyped *initializedSymbol,
    352                    bool canUseLoopsToInitialize,
    353                    bool highPrecisionSupported,
    354                    TIntermSequence *initCode,
    355                    TSymbolTable *symbolTable)
    356 {
    357    AddZeroInitSequence(initializedSymbol, canUseLoopsToInitialize, highPrecisionSupported,
    358                        initCode, symbolTable);
    359 }
    360 
    361 bool InitializeUninitializedLocals(TCompiler *compiler,
    362                                   TIntermBlock *root,
    363                                   int shaderVersion,
    364                                   bool canUseLoopsToInitialize,
    365                                   bool highPrecisionSupported,
    366                                   TSymbolTable *symbolTable)
    367 {
    368    InitializeLocalsTraverser traverser(shaderVersion, symbolTable, canUseLoopsToInitialize,
    369                                        highPrecisionSupported);
    370    root->traverse(&traverser);
    371    return traverser.updateTree(compiler, root);
    372 }
    373 
    374 bool InitializeVariables(TCompiler *compiler,
    375                         TIntermBlock *root,
    376                         const InitVariableList &vars,
    377                         TSymbolTable *symbolTable,
    378                         int shaderVersion,
    379                         const TExtensionBehavior &extensionBehavior,
    380                         bool canUseLoopsToInitialize,
    381                         bool highPrecisionSupported)
    382 {
    383    TIntermBlock *body = FindMainBody(root);
    384    InsertInitCode(compiler, body->getSequence(), vars, symbolTable, shaderVersion,
    385                   extensionBehavior, canUseLoopsToInitialize, highPrecisionSupported);
    386 
    387    return compiler->validateAST(root);
    388 }
    389 
    390 }  // namespace sh