blob: 260760f2b9d9630f53dc47d1162ec7de9cb4b542 [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:
Olli Etuahob38dfde2018-01-04 15:49:03 +020043 static void apply(TIntermBlock *root, TSymbolTable *symbolTable);
Olli Etuaho87c35882017-10-19 15:19:38 +030044
45 private:
Olli Etuahob38dfde2018-01-04 15:49:03 +020046 PruneNoOpsTraverser(TSymbolTable *symbolTable);
Olli Etuaho87c35882017-10-19 15:19:38 +030047 bool visitDeclaration(Visit, TIntermDeclaration *node) override;
48 bool visitBlock(Visit visit, TIntermBlock *node) override;
49 bool visitLoop(Visit visit, TIntermLoop *loop) override;
50};
51
Olli Etuahob38dfde2018-01-04 15:49:03 +020052void PruneNoOpsTraverser::apply(TIntermBlock *root, TSymbolTable *symbolTable)
Olli Etuaho87c35882017-10-19 15:19:38 +030053{
Olli Etuahob38dfde2018-01-04 15:49:03 +020054 PruneNoOpsTraverser prune(symbolTable);
Olli Etuaho87c35882017-10-19 15:19:38 +030055 root->traverse(&prune);
56 prune.updateTree();
57}
58
Olli Etuahob38dfde2018-01-04 15:49:03 +020059PruneNoOpsTraverser::PruneNoOpsTraverser(TSymbolTable *symbolTable)
60 : TIntermTraverser(true, false, false, symbolTable)
Olli Etuaho87c35882017-10-19 15:19:38 +030061{
62}
63
64bool PruneNoOpsTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
65{
66 TIntermSequence *sequence = node->getSequence();
67 if (sequence->size() >= 1)
68 {
Olli Etuahob38dfde2018-01-04 15:49:03 +020069 TIntermSymbol *declaratorSymbol = sequence->front()->getAsSymbolNode();
Olli Etuaho87c35882017-10-19 15:19:38 +030070 // Prune declarations without a variable name, unless it's an interface block declaration.
Olli Etuahob38dfde2018-01-04 15:49:03 +020071 if (declaratorSymbol != nullptr &&
72 declaratorSymbol->variable().symbolType() == SymbolType::Empty &&
73 !declaratorSymbol->isInterfaceBlock())
Olli Etuaho87c35882017-10-19 15:19:38 +030074 {
75 if (sequence->size() > 1)
76 {
77 // Generate a replacement that will remove the empty declarator in the beginning of
78 // a declarator list. Example of a declaration that will be changed:
79 // float, a;
80 // will be changed to
81 // float a;
82 // This applies also to struct declarations.
83 TIntermSequence emptyReplacement;
84 mMultiReplacements.push_back(
Olli Etuahob38dfde2018-01-04 15:49:03 +020085 NodeReplaceWithMultipleEntry(node, declaratorSymbol, emptyReplacement));
Olli Etuaho87c35882017-10-19 15:19:38 +030086 }
Olli Etuahob38dfde2018-01-04 15:49:03 +020087 else if (declaratorSymbol->getBasicType() != EbtStruct)
Olli Etuaho87c35882017-10-19 15:19:38 +030088 {
89 // If there are entirely empty non-struct declarations, they result in
90 // TIntermDeclaration nodes without any children in the parsing stage. These are
91 // handled in visitBlock and visitLoop.
92 UNREACHABLE();
93 }
Olli Etuahob38dfde2018-01-04 15:49:03 +020094 else if (declaratorSymbol->getQualifier() != EvqGlobal &&
95 declaratorSymbol->getQualifier() != EvqTemporary)
Olli Etuaho87c35882017-10-19 15:19:38 +030096 {
97 // Single struct declarations may just declare the struct type and no variables, so
98 // they should not be pruned. Here we handle an empty struct declaration with a
99 // qualifier, for example like this:
100 // const struct a { int i; };
101 // NVIDIA GL driver version 367.27 doesn't accept this kind of declarations, so we
102 // convert the declaration to a regular struct declaration. This is okay, since ESSL
103 // 1.00 spec section 4.1.8 says about structs that "The optional qualifiers only
104 // apply to any declarators, and are not part of the type being defined for name."
105
Olli Etuahob38dfde2018-01-04 15:49:03 +0200106 // Create a new variable to use in the declarator so that the variable and node
107 // types are kept consistent.
Olli Etuahob60d30f2018-01-16 12:31:06 +0200108 TType *type = new TType(declaratorSymbol->getType());
Olli Etuaho87c35882017-10-19 15:19:38 +0300109 if (mInGlobalScope)
110 {
Olli Etuahob60d30f2018-01-16 12:31:06 +0200111 type->setQualifier(EvqGlobal);
Olli Etuaho87c35882017-10-19 15:19:38 +0300112 }
113 else
114 {
Olli Etuahob60d30f2018-01-16 12:31:06 +0200115 type->setQualifier(EvqTemporary);
Olli Etuaho87c35882017-10-19 15:19:38 +0300116 }
Olli Etuahob38dfde2018-01-04 15:49:03 +0200117 TVariable *variable = new TVariable(mSymbolTable, nullptr, type, SymbolType::Empty);
118 queueReplacementWithParent(node, declaratorSymbol, new TIntermSymbol(variable),
119 OriginalNode::IS_DROPPED);
Olli Etuaho87c35882017-10-19 15:19:38 +0300120 }
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
Olli Etuahob38dfde2018-01-04 15:49:03 +0200161void PruneNoOps(TIntermBlock *root, TSymbolTable *symbolTable)
Olli Etuaho87c35882017-10-19 15:19:38 +0300162{
Olli Etuahob38dfde2018-01-04 15:49:03 +0200163 PruneNoOpsTraverser::apply(root, symbolTable);
Olli Etuaho87c35882017-10-19 15:19:38 +0300164}
165
166} // namespace sh