blob: 8b2613aec98a9245162d99aa3242448b84526b48 [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
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +020016#include "compiler/translator/Symbol.h"
Olli Etuahoc26214d2018-03-16 10:43:11 +020017#include "compiler/translator/tree_util/IntermTraverse.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 Etuahofbb1c792018-01-19 16:26:59 +0200117 TVariable *variable =
118 new TVariable(mSymbolTable, ImmutableString(""), type, SymbolType::Empty);
Olli Etuahob38dfde2018-01-04 15:49:03 +0200119 queueReplacementWithParent(node, declaratorSymbol, new TIntermSymbol(variable),
120 OriginalNode::IS_DROPPED);
Olli Etuaho87c35882017-10-19 15:19:38 +0300121 }
122 }
123 }
124 return false;
125}
126
127bool PruneNoOpsTraverser::visitBlock(Visit visit, TIntermBlock *node)
128{
129 TIntermSequence *statements = node->getSequence();
130
131 for (TIntermNode *statement : *statements)
132 {
133 if (IsNoOp(statement))
134 {
135 TIntermSequence emptyReplacement;
136 mMultiReplacements.push_back(
137 NodeReplaceWithMultipleEntry(node, statement, emptyReplacement));
138 }
139 }
140
141 return true;
142}
143
144bool PruneNoOpsTraverser::visitLoop(Visit visit, TIntermLoop *loop)
145{
146 TIntermTyped *expr = loop->getExpression();
147 if (expr != nullptr && IsNoOp(expr))
148 {
149 loop->setExpression(nullptr);
150 }
151 TIntermNode *init = loop->getInit();
152 if (init != nullptr && IsNoOp(init))
153 {
154 loop->setInit(nullptr);
155 }
156
157 return true;
158}
159
160} // namespace
161
Olli Etuahob38dfde2018-01-04 15:49:03 +0200162void PruneNoOps(TIntermBlock *root, TSymbolTable *symbolTable)
Olli Etuaho87c35882017-10-19 15:19:38 +0300163{
Olli Etuahob38dfde2018-01-04 15:49:03 +0200164 PruneNoOpsTraverser::apply(root, symbolTable);
Olli Etuaho87c35882017-10-19 15:19:38 +0300165}
166
167} // namespace sh