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