blob: 9ac55e96951aeafe7d2275a1b74282c122834db5 [file] [log] [blame]
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +02001//
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//
Olli Etuahoaf5070f2017-10-10 13:53:25 +03006// RemoveSwitchFallThrough.cpp: Remove fall-through from switch statements.
7// Note that it is unsafe to do further AST transformations on the AST generated
8// by this function. It leaves duplicate nodes in the AST making replacements
9// unreliable.
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020010
11#include "compiler/translator/RemoveSwitchFallThrough.h"
12
Olli Etuaho89a69a02017-10-23 12:20:45 +030013#include "compiler/translator/Diagnostics.h"
Olli Etuahoc26214d2018-03-16 10:43:11 +020014#include "compiler/translator/tree_util/IntermTraverse.h"
Olli Etuahoaf5070f2017-10-10 13:53:25 +030015
Jamie Madill45bcc782016-11-07 13:58:48 -050016namespace sh
17{
18
Olli Etuahoaf5070f2017-10-10 13:53:25 +030019namespace
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020020{
Olli Etuahoaf5070f2017-10-10 13:53:25 +030021
22class RemoveSwitchFallThroughTraverser : public TIntermTraverser
23{
24 public:
Olli Etuaho89a69a02017-10-23 12:20:45 +030025 static TIntermBlock *removeFallThrough(TIntermBlock *statementList,
26 PerformanceDiagnostics *perfDiagnostics);
Olli Etuahoaf5070f2017-10-10 13:53:25 +030027
28 private:
Olli Etuaho89a69a02017-10-23 12:20:45 +030029 RemoveSwitchFallThroughTraverser(TIntermBlock *statementList,
30 PerformanceDiagnostics *perfDiagnostics);
Olli Etuahoaf5070f2017-10-10 13:53:25 +030031
32 void visitSymbol(TIntermSymbol *node) override;
33 void visitConstantUnion(TIntermConstantUnion *node) override;
Olli Etuaho78507c62017-10-10 15:06:45 +030034 bool visitDeclaration(Visit, TIntermDeclaration *node) override;
Olli Etuahoaf5070f2017-10-10 13:53:25 +030035 bool visitBinary(Visit, TIntermBinary *node) override;
36 bool visitUnary(Visit, TIntermUnary *node) override;
37 bool visitTernary(Visit visit, TIntermTernary *node) override;
Olli Etuaho78507c62017-10-10 15:06:45 +030038 bool visitSwizzle(Visit, TIntermSwizzle *node) override;
Olli Etuahoaf5070f2017-10-10 13:53:25 +030039 bool visitIfElse(Visit visit, TIntermIfElse *node) override;
40 bool visitSwitch(Visit, TIntermSwitch *node) override;
41 bool visitCase(Visit, TIntermCase *node) override;
42 bool visitAggregate(Visit, TIntermAggregate *node) override;
43 bool visitBlock(Visit, TIntermBlock *node) override;
44 bool visitLoop(Visit, TIntermLoop *node) override;
45 bool visitBranch(Visit, TIntermBranch *node) override;
46
47 void outputSequence(TIntermSequence *sequence, size_t startIndex);
48 void handlePreviousCase();
49
50 TIntermBlock *mStatementList;
51 TIntermBlock *mStatementListOut;
52 bool mLastStatementWasBreak;
53 TIntermBlock *mPreviousCase;
54 std::vector<TIntermBlock *> mCasesSharingBreak;
Olli Etuaho89a69a02017-10-23 12:20:45 +030055 PerformanceDiagnostics *mPerfDiagnostics;
Olli Etuahoaf5070f2017-10-10 13:53:25 +030056};
57
Olli Etuaho89a69a02017-10-23 12:20:45 +030058TIntermBlock *RemoveSwitchFallThroughTraverser::removeFallThrough(
59 TIntermBlock *statementList,
60 PerformanceDiagnostics *perfDiagnostics)
Olli Etuahoaf5070f2017-10-10 13:53:25 +030061{
Olli Etuaho89a69a02017-10-23 12:20:45 +030062 RemoveSwitchFallThroughTraverser rm(statementList, perfDiagnostics);
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020063 ASSERT(statementList);
64 statementList->traverse(&rm);
Olli Etuaho852fe872017-10-10 15:13:59 +030065 ASSERT(rm.mPreviousCase || statementList->getSequence()->empty());
66 if (!rm.mLastStatementWasBreak && rm.mPreviousCase)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020067 {
Olli Etuaho852fe872017-10-10 15:13:59 +030068 // Make sure that there's a branch at the end of the final case inside the switch statement.
69 // This also ensures that any cases that fall through to the final case will get the break.
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020070 TIntermBranch *finalBreak = new TIntermBranch(EOpBreak, nullptr);
Olli Etuaho852fe872017-10-10 15:13:59 +030071 rm.mPreviousCase->getSequence()->push_back(finalBreak);
72 rm.mLastStatementWasBreak = true;
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020073 }
Olli Etuaho852fe872017-10-10 15:13:59 +030074 rm.handlePreviousCase();
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020075 return rm.mStatementListOut;
76}
77
Olli Etuaho89a69a02017-10-23 12:20:45 +030078RemoveSwitchFallThroughTraverser::RemoveSwitchFallThroughTraverser(
79 TIntermBlock *statementList,
80 PerformanceDiagnostics *perfDiagnostics)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020081 : TIntermTraverser(true, false, false),
82 mStatementList(statementList),
83 mLastStatementWasBreak(false),
Olli Etuaho89a69a02017-10-23 12:20:45 +030084 mPreviousCase(nullptr),
85 mPerfDiagnostics(perfDiagnostics)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020086{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010087 mStatementListOut = new TIntermBlock();
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020088}
89
Olli Etuahoaf5070f2017-10-10 13:53:25 +030090void RemoveSwitchFallThroughTraverser::visitSymbol(TIntermSymbol *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020091{
92 // Note that this assumes that switch statements which don't begin by a case statement
93 // have already been weeded out in validation.
94 mPreviousCase->getSequence()->push_back(node);
95 mLastStatementWasBreak = false;
96}
97
Olli Etuahoaf5070f2017-10-10 13:53:25 +030098void RemoveSwitchFallThroughTraverser::visitConstantUnion(TIntermConstantUnion *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020099{
Olli Etuaho87c35882017-10-19 15:19:38 +0300100 // Conditions of case labels are not traversed, so this is a constant statement like "0;".
101 // These are no-ops so there's no need to add them back to the statement list. Should have
102 // already been pruned out of the AST, in fact.
103 UNREACHABLE();
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200104}
105
Olli Etuaho78507c62017-10-10 15:06:45 +0300106bool RemoveSwitchFallThroughTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
107{
108 mPreviousCase->getSequence()->push_back(node);
109 mLastStatementWasBreak = false;
110 return false;
111}
112
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300113bool RemoveSwitchFallThroughTraverser::visitBinary(Visit, TIntermBinary *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200114{
115 mPreviousCase->getSequence()->push_back(node);
116 mLastStatementWasBreak = false;
117 return false;
118}
119
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300120bool RemoveSwitchFallThroughTraverser::visitUnary(Visit, TIntermUnary *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200121{
122 mPreviousCase->getSequence()->push_back(node);
123 mLastStatementWasBreak = false;
124 return false;
125}
126
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300127bool RemoveSwitchFallThroughTraverser::visitTernary(Visit, TIntermTernary *node)
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300128{
129 mPreviousCase->getSequence()->push_back(node);
130 mLastStatementWasBreak = false;
131 return false;
132}
133
Olli Etuaho78507c62017-10-10 15:06:45 +0300134bool RemoveSwitchFallThroughTraverser::visitSwizzle(Visit, TIntermSwizzle *node)
135{
136 mPreviousCase->getSequence()->push_back(node);
137 mLastStatementWasBreak = false;
138 return false;
139}
140
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300141bool RemoveSwitchFallThroughTraverser::visitIfElse(Visit, TIntermIfElse *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200142{
143 mPreviousCase->getSequence()->push_back(node);
144 mLastStatementWasBreak = false;
145 return false;
146}
147
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300148bool RemoveSwitchFallThroughTraverser::visitSwitch(Visit, TIntermSwitch *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200149{
150 mPreviousCase->getSequence()->push_back(node);
151 mLastStatementWasBreak = false;
152 // Don't go into nested switch statements
153 return false;
154}
155
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300156void RemoveSwitchFallThroughTraverser::outputSequence(TIntermSequence *sequence, size_t startIndex)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200157{
158 for (size_t i = startIndex; i < sequence->size(); ++i)
159 {
160 mStatementListOut->getSequence()->push_back(sequence->at(i));
161 }
162}
163
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300164void RemoveSwitchFallThroughTraverser::handlePreviousCase()
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200165{
166 if (mPreviousCase)
167 mCasesSharingBreak.push_back(mPreviousCase);
168 if (mLastStatementWasBreak)
169 {
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200170 for (size_t i = 0; i < mCasesSharingBreak.size(); ++i)
171 {
Olli Etuaho89a69a02017-10-23 12:20:45 +0300172 ASSERT(!mCasesSharingBreak.at(i)->getSequence()->empty());
173 if (mCasesSharingBreak.at(i)->getSequence()->size() == 1)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200174 {
175 // Fall-through is allowed in case the label has no statements.
176 outputSequence(mCasesSharingBreak.at(i)->getSequence(), 0);
177 }
178 else
179 {
180 // Include all the statements that this case can fall through under the same label.
Olli Etuaho89a69a02017-10-23 12:20:45 +0300181 if (mCasesSharingBreak.size() > i + 1u)
182 {
183 mPerfDiagnostics->warning(mCasesSharingBreak.at(i)->getLine(),
184 "Performance: non-empty fall-through cases in "
185 "switch statements generate extra code.",
186 "switch");
187 }
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200188 for (size_t j = i; j < mCasesSharingBreak.size(); ++j)
189 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500190 size_t startIndex =
191 j > i ? 1 : 0; // Add the label only from the first sequence.
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200192 outputSequence(mCasesSharingBreak.at(j)->getSequence(), startIndex);
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200193 }
194 }
195 }
196 mCasesSharingBreak.clear();
197 }
198 mLastStatementWasBreak = false;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500199 mPreviousCase = nullptr;
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200200}
201
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300202bool RemoveSwitchFallThroughTraverser::visitCase(Visit, TIntermCase *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200203{
204 handlePreviousCase();
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100205 mPreviousCase = new TIntermBlock();
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200206 mPreviousCase->getSequence()->push_back(node);
Olli Etuaho89a69a02017-10-23 12:20:45 +0300207 mPreviousCase->setLine(node->getLine());
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200208 // Don't traverse the condition of the case statement
209 return false;
210}
211
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300212bool RemoveSwitchFallThroughTraverser::visitAggregate(Visit, TIntermAggregate *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200213{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100214 mPreviousCase->getSequence()->push_back(node);
215 mLastStatementWasBreak = false;
216 return false;
217}
218
Olli Etuaho4bd730c2017-10-10 14:14:19 +0300219bool DoesBlockAlwaysBreak(TIntermBlock *node)
220{
221 if (node->getSequence()->empty())
222 {
223 return false;
224 }
225
226 TIntermBlock *lastStatementAsBlock = node->getSequence()->back()->getAsBlock();
227 if (lastStatementAsBlock)
228 {
229 return DoesBlockAlwaysBreak(lastStatementAsBlock);
230 }
231
232 TIntermBranch *lastStatementAsBranch = node->getSequence()->back()->getAsBranchNode();
233 return lastStatementAsBranch != nullptr;
234}
235
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300236bool RemoveSwitchFallThroughTraverser::visitBlock(Visit, TIntermBlock *node)
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100237{
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200238 if (node != mStatementList)
239 {
240 mPreviousCase->getSequence()->push_back(node);
Olli Etuaho4bd730c2017-10-10 14:14:19 +0300241 mLastStatementWasBreak = DoesBlockAlwaysBreak(node);
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200242 return false;
243 }
244 return true;
245}
246
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300247bool RemoveSwitchFallThroughTraverser::visitLoop(Visit, TIntermLoop *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200248{
249 mPreviousCase->getSequence()->push_back(node);
250 mLastStatementWasBreak = false;
251 return false;
252}
253
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300254bool RemoveSwitchFallThroughTraverser::visitBranch(Visit, TIntermBranch *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200255{
256 mPreviousCase->getSequence()->push_back(node);
257 // TODO: Verify that accepting return or continue statements here doesn't cause problems.
258 mLastStatementWasBreak = true;
259 return false;
260}
Jamie Madill45bcc782016-11-07 13:58:48 -0500261
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300262} // anonymous namespace
263
Olli Etuaho89a69a02017-10-23 12:20:45 +0300264TIntermBlock *RemoveSwitchFallThrough(TIntermBlock *statementList,
265 PerformanceDiagnostics *perfDiagnostics)
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300266{
Olli Etuaho89a69a02017-10-23 12:20:45 +0300267 return RemoveSwitchFallThroughTraverser::removeFallThrough(statementList, perfDiagnostics);
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300268}
269
Jamie Madill45bcc782016-11-07 13:58:48 -0500270} // namespace sh