blob: 0cf963b00f17219fde9a05e2cdbce40c866aa3e4 [file] [log] [blame]
Olli Etuaho87c35882017-10-19 15:19:38 +03001//
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/PruneNoOps.h"
15
16#include "compiler/translator/IntermTraverse.h"
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +020017#include "compiler/translator/Symbol.h"
Olli Etuaho87c35882017-10-19 15:19:38 +030018
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);
44
45 private:
46 PruneNoOpsTraverser();
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)
53{
54 PruneNoOpsTraverser prune;
55 root->traverse(&prune);
56 prune.updateTree();
57}
58
59PruneNoOpsTraverser::PruneNoOpsTraverser() : TIntermTraverser(true, false, false)
60{
61}
62
63bool PruneNoOpsTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
64{
65 TIntermSequence *sequence = node->getSequence();
66 if (sequence->size() >= 1)
67 {
68 TIntermSymbol *sym = sequence->front()->getAsSymbolNode();
69 // Prune declarations without a variable name, unless it's an interface block declaration.
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +020070 if (sym != nullptr && sym->variable().symbolType() == SymbolType::Empty &&
71 !sym->isInterfaceBlock())
Olli Etuaho87c35882017-10-19 15:19:38 +030072 {
73 if (sequence->size() > 1)
74 {
75 // Generate a replacement that will remove the empty declarator in the beginning of
76 // a declarator list. Example of a declaration that will be changed:
77 // float, a;
78 // will be changed to
79 // float a;
80 // This applies also to struct declarations.
81 TIntermSequence emptyReplacement;
82 mMultiReplacements.push_back(
83 NodeReplaceWithMultipleEntry(node, sym, emptyReplacement));
84 }
85 else if (sym->getBasicType() != EbtStruct)
86 {
87 // If there are entirely empty non-struct declarations, they result in
88 // TIntermDeclaration nodes without any children in the parsing stage. These are
89 // handled in visitBlock and visitLoop.
90 UNREACHABLE();
91 }
92 else if (sym->getType().getQualifier() != EvqGlobal &&
93 sym->getType().getQualifier() != EvqTemporary)
94 {
95 // Single struct declarations may just declare the struct type and no variables, so
96 // they should not be pruned. Here we handle an empty struct declaration with a
97 // qualifier, for example like this:
98 // const struct a { int i; };
99 // NVIDIA GL driver version 367.27 doesn't accept this kind of declarations, so we
100 // convert the declaration to a regular struct declaration. This is okay, since ESSL
101 // 1.00 spec section 4.1.8 says about structs that "The optional qualifiers only
102 // apply to any declarators, and are not part of the type being defined for name."
103
104 if (mInGlobalScope)
105 {
106 sym->getTypePointer()->setQualifier(EvqGlobal);
107 }
108 else
109 {
110 sym->getTypePointer()->setQualifier(EvqTemporary);
111 }
112 }
113 }
114 }
115 return false;
116}
117
118bool PruneNoOpsTraverser::visitBlock(Visit visit, TIntermBlock *node)
119{
120 TIntermSequence *statements = node->getSequence();
121
122 for (TIntermNode *statement : *statements)
123 {
124 if (IsNoOp(statement))
125 {
126 TIntermSequence emptyReplacement;
127 mMultiReplacements.push_back(
128 NodeReplaceWithMultipleEntry(node, statement, emptyReplacement));
129 }
130 }
131
132 return true;
133}
134
135bool PruneNoOpsTraverser::visitLoop(Visit visit, TIntermLoop *loop)
136{
137 TIntermTyped *expr = loop->getExpression();
138 if (expr != nullptr && IsNoOp(expr))
139 {
140 loop->setExpression(nullptr);
141 }
142 TIntermNode *init = loop->getInit();
143 if (init != nullptr && IsNoOp(init))
144 {
145 loop->setInit(nullptr);
146 }
147
148 return true;
149}
150
151} // namespace
152
153void PruneNoOps(TIntermBlock *root)
154{
155 PruneNoOpsTraverser::apply(root);
156}
157
158} // namespace sh