tor-browser

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

PruneNoOps.cpp (7686B)


      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 // PruneNoOps.cpp: The PruneNoOps function prunes:
      7 //   1. Empty declarations "int;". Empty declarators will be pruned as well, so for example:
      8 //        int , a;
      9 //      is turned into
     10 //        int a;
     11 //   2. Literal statements: "1.0;". The ESSL output doesn't define a default precision for float,
     12 //      so float literal statements would end up with no precision which is invalid ESSL.
     13 //   3. Statements after discard, return, break and continue.
     14 
     15 #include "compiler/translator/tree_ops/PruneNoOps.h"
     16 
     17 #include "compiler/translator/Symbol.h"
     18 #include "compiler/translator/tree_util/IntermTraverse.h"
     19 
     20 namespace sh
     21 {
     22 
     23 namespace
     24 {
     25 
     26 bool IsNoOp(TIntermNode *node)
     27 {
     28    if (node->getAsConstantUnion() != nullptr)
     29    {
     30        return true;
     31    }
     32    bool isEmptyDeclaration = node->getAsDeclarationNode() != nullptr &&
     33                              node->getAsDeclarationNode()->getSequence()->empty();
     34    if (isEmptyDeclaration)
     35    {
     36        return true;
     37    }
     38    return false;
     39 }
     40 
     41 class PruneNoOpsTraverser : private TIntermTraverser
     42 {
     43  public:
     44    [[nodiscard]] static bool apply(TCompiler *compiler,
     45                                    TIntermBlock *root,
     46                                    TSymbolTable *symbolTable);
     47 
     48  private:
     49    PruneNoOpsTraverser(TSymbolTable *symbolTable);
     50    bool visitDeclaration(Visit, TIntermDeclaration *node) override;
     51    bool visitBlock(Visit visit, TIntermBlock *node) override;
     52    bool visitLoop(Visit visit, TIntermLoop *loop) override;
     53    bool visitBranch(Visit visit, TIntermBranch *node) override;
     54 
     55    bool mIsBranchVisited = false;
     56 };
     57 
     58 bool PruneNoOpsTraverser::apply(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
     59 {
     60    PruneNoOpsTraverser prune(symbolTable);
     61    root->traverse(&prune);
     62    return prune.updateTree(compiler, root);
     63 }
     64 
     65 PruneNoOpsTraverser::PruneNoOpsTraverser(TSymbolTable *symbolTable)
     66    : TIntermTraverser(true, true, true, symbolTable)
     67 {}
     68 
     69 bool PruneNoOpsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
     70 {
     71    if (visit != PreVisit)
     72    {
     73        return true;
     74    }
     75 
     76    TIntermSequence *sequence = node->getSequence();
     77    if (sequence->size() >= 1)
     78    {
     79        TIntermSymbol *declaratorSymbol = sequence->front()->getAsSymbolNode();
     80        // Prune declarations without a variable name, unless it's an interface block declaration.
     81        if (declaratorSymbol != nullptr &&
     82            declaratorSymbol->variable().symbolType() == SymbolType::Empty &&
     83            !declaratorSymbol->isInterfaceBlock())
     84        {
     85            if (sequence->size() > 1)
     86            {
     87                // Generate a replacement that will remove the empty declarator in the beginning of
     88                // a declarator list. Example of a declaration that will be changed:
     89                // float, a;
     90                // will be changed to
     91                // float a;
     92                // This applies also to struct declarations.
     93                TIntermSequence emptyReplacement;
     94                mMultiReplacements.emplace_back(node, declaratorSymbol,
     95                                                std::move(emptyReplacement));
     96            }
     97            else if (declaratorSymbol->getBasicType() != EbtStruct)
     98            {
     99                // If there are entirely empty non-struct declarations, they result in
    100                // TIntermDeclaration nodes without any children in the parsing stage. These are
    101                // handled in visitBlock and visitLoop.
    102                UNREACHABLE();
    103            }
    104            else if (declaratorSymbol->getQualifier() != EvqGlobal &&
    105                     declaratorSymbol->getQualifier() != EvqTemporary)
    106            {
    107                // Single struct declarations may just declare the struct type and no variables, so
    108                // they should not be pruned. Here we handle an empty struct declaration with a
    109                // qualifier, for example like this:
    110                //   const struct a { int i; };
    111                // NVIDIA GL driver version 367.27 doesn't accept this kind of declarations, so we
    112                // convert the declaration to a regular struct declaration. This is okay, since ESSL
    113                // 1.00 spec section 4.1.8 says about structs that "The optional qualifiers only
    114                // apply to any declarators, and are not part of the type being defined for name."
    115 
    116                // Create a new variable to use in the declarator so that the variable and node
    117                // types are kept consistent.
    118                TType *type = new TType(declaratorSymbol->getType());
    119                if (mInGlobalScope)
    120                {
    121                    type->setQualifier(EvqGlobal);
    122                }
    123                else
    124                {
    125                    type->setQualifier(EvqTemporary);
    126                }
    127                TVariable *variable =
    128                    new TVariable(mSymbolTable, kEmptyImmutableString, type, SymbolType::Empty);
    129                queueReplacementWithParent(node, declaratorSymbol, new TIntermSymbol(variable),
    130                                           OriginalNode::IS_DROPPED);
    131            }
    132        }
    133    }
    134    return false;
    135 }
    136 
    137 bool PruneNoOpsTraverser::visitBlock(Visit visit, TIntermBlock *node)
    138 {
    139    ASSERT(visit == PreVisit);
    140 
    141    TIntermSequence &statements = *node->getSequence();
    142 
    143    // Visit each statement in the block one by one.  Once a branch is visited (break, continue,
    144    // return or discard), drop the rest of the statements.
    145    for (size_t statementIndex = 0; statementIndex < statements.size(); ++statementIndex)
    146    {
    147        TIntermNode *statement = statements[statementIndex];
    148 
    149        // If the statement is a switch case label, stop pruning and continue visiting the children.
    150        if (statement->getAsCaseNode() != nullptr)
    151        {
    152            mIsBranchVisited = false;
    153        }
    154 
    155        // If a branch is visited, prune the statement.  If the statement is a no-op, also prune it.
    156        if (mIsBranchVisited || IsNoOp(statement))
    157        {
    158            TIntermSequence emptyReplacement;
    159            mMultiReplacements.emplace_back(node, statement, std::move(emptyReplacement));
    160            continue;
    161        }
    162 
    163        // Visit the statement if not pruned.
    164        statement->traverse(this);
    165    }
    166 
    167    // If the parent is a block and mIsBranchVisited is set, this is a nested block without any
    168    // condition (like if, loop or switch), so the rest of the parent block should also be pruned.
    169    // Otherwise the parent block should be unaffected.
    170    if (mIsBranchVisited && getParentNode()->getAsBlock() == nullptr)
    171    {
    172        mIsBranchVisited = false;
    173    }
    174 
    175    return false;
    176 }
    177 
    178 bool PruneNoOpsTraverser::visitLoop(Visit visit, TIntermLoop *loop)
    179 {
    180    if (visit != PreVisit)
    181    {
    182        return true;
    183    }
    184 
    185    TIntermTyped *expr = loop->getExpression();
    186    if (expr != nullptr && IsNoOp(expr))
    187    {
    188        loop->setExpression(nullptr);
    189    }
    190    TIntermNode *init = loop->getInit();
    191    if (init != nullptr && IsNoOp(init))
    192    {
    193        loop->setInit(nullptr);
    194    }
    195 
    196    return true;
    197 }
    198 
    199 bool PruneNoOpsTraverser::visitBranch(Visit visit, TIntermBranch *node)
    200 {
    201    ASSERT(visit == PreVisit);
    202 
    203    mIsBranchVisited = true;
    204 
    205    // Only possible child is the value of a return statement, which has nothing to prune.
    206    return false;
    207 }
    208 }  // namespace
    209 
    210 bool PruneNoOps(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
    211 {
    212    return PruneNoOpsTraverser::apply(compiler, root, symbolTable);
    213 }
    214 
    215 }  // namespace sh