1//
2// Copyright (c) 2002-2015 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
14#include "compiler/translator/tree_ops/PruneNoOps.h"
15
16#include "compiler/translator/Symbol.h"
17#include "compiler/translator/tree_util/IntermTraverse.h"
18
19namespace sh
20{
21
22namespace
23{
24
25bool IsNoOp(TIntermNode *node)
26{
27 if (node->getAsConstantUnion() != nullptr)
28 {
29 return true;
30 }
31 bool isEmptyDeclaration = node->getAsDeclarationNode() != nullptr &&
32 node->getAsDeclarationNode()->getSequence()->empty();
33 if (isEmptyDeclaration)
34 {
35 return true;
36 }
37 return false;
38}
39
40class PruneNoOpsTraverser : private TIntermTraverser
41{
42 public:
43 static void apply(TIntermBlock *root, TSymbolTable *symbolTable);
44
45 private:
46 PruneNoOpsTraverser(TSymbolTable *symbolTable);
47 bool visitDeclaration(Visit, TIntermDeclaration *node) override;
48 bool visitBlock(Visit visit, TIntermBlock *node) override;
49 bool visitLoop(Visit visit, TIntermLoop *loop) override;
50};
51
52void PruneNoOpsTraverser::apply(TIntermBlock *root, TSymbolTable *symbolTable)
53{
54 PruneNoOpsTraverser prune(symbolTable);
55 root->traverse(&prune);
56 prune.updateTree();
57}
58
59PruneNoOpsTraverser::PruneNoOpsTraverser(TSymbolTable *symbolTable)
60 : TIntermTraverser(true, false, false, symbolTable)
61{}
62
63bool PruneNoOpsTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
64{
65 TIntermSequence *sequence = node->getSequence();
66 if (sequence->size() >= 1)
67 {
68 TIntermSymbol *declaratorSymbol = sequence->front()->getAsSymbolNode();
69 // Prune declarations without a variable name, unless it's an interface block declaration.
70 if (declaratorSymbol != nullptr &&
71 declaratorSymbol->variable().symbolType() == SymbolType::Empty &&
72 !declaratorSymbol->isInterfaceBlock())
73 {
74 if (sequence->size() > 1)
75 {
76 // Generate a replacement that will remove the empty declarator in the beginning of
77 // a declarator list. Example of a declaration that will be changed:
78 // float, a;
79 // will be changed to
80 // float a;
81 // This applies also to struct declarations.
82 TIntermSequence emptyReplacement;
83 mMultiReplacements.push_back(
84 NodeReplaceWithMultipleEntry(node, declaratorSymbol, emptyReplacement));
85 }
86 else if (declaratorSymbol->getBasicType() != EbtStruct)
87 {
88 // If there are entirely empty non-struct declarations, they result in
89 // TIntermDeclaration nodes without any children in the parsing stage. These are
90 // handled in visitBlock and visitLoop.
91 UNREACHABLE();
92 }
93 else if (declaratorSymbol->getQualifier() != EvqGlobal &&
94 declaratorSymbol->getQualifier() != EvqTemporary)
95 {
96 // Single struct declarations may just declare the struct type and no variables, so
97 // they should not be pruned. Here we handle an empty struct declaration with a
98 // qualifier, for example like this:
99 // const struct a { int i; };
100 // NVIDIA GL driver version 367.27 doesn't accept this kind of declarations, so we
101 // convert the declaration to a regular struct declaration. This is okay, since ESSL
102 // 1.00 spec section 4.1.8 says about structs that "The optional qualifiers only
103 // apply to any declarators, and are not part of the type being defined for name."
104
105 // Create a new variable to use in the declarator so that the variable and node
106 // types are kept consistent.
107 TType *type = new TType(declaratorSymbol->getType());
108 if (mInGlobalScope)
109 {
110 type->setQualifier(EvqGlobal);
111 }
112 else
113 {
114 type->setQualifier(EvqTemporary);
115 }
116 TVariable *variable =
117 new TVariable(mSymbolTable, kEmptyImmutableString, type, SymbolType::Empty);
118 queueReplacementWithParent(node, declaratorSymbol, new TIntermSymbol(variable),
119 OriginalNode::IS_DROPPED);
120 }
121 }
122 }
123 return false;
124}
125
126bool PruneNoOpsTraverser::visitBlock(Visit visit, TIntermBlock *node)
127{
128 TIntermSequence *statements = node->getSequence();
129
130 for (TIntermNode *statement : *statements)
131 {
132 if (IsNoOp(statement))
133 {
134 TIntermSequence emptyReplacement;
135 mMultiReplacements.push_back(
136 NodeReplaceWithMultipleEntry(node, statement, emptyReplacement));
137 }
138 }
139
140 return true;
141}
142
143bool PruneNoOpsTraverser::visitLoop(Visit visit, TIntermLoop *loop)
144{
145 TIntermTyped *expr = loop->getExpression();
146 if (expr != nullptr && IsNoOp(expr))
147 {
148 loop->setExpression(nullptr);
149 }
150 TIntermNode *init = loop->getInit();
151 if (init != nullptr && IsNoOp(init))
152 {
153 loop->setInit(nullptr);
154 }
155
156 return true;
157}
158
159} // namespace
160
161void PruneNoOps(TIntermBlock *root, TSymbolTable *symbolTable)
162{
163 PruneNoOpsTraverser::apply(root, symbolTable);
164}
165
166} // namespace sh
167