blob: 10da1dff818974437cee054f5f86435ff27d20c3 [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 Etuahoaf5070f2017-10-10 13:53:25 +030013#include "compiler/translator/IntermTraverse.h"
14
Jamie Madill45bcc782016-11-07 13:58:48 -050015namespace sh
16{
17
Olli Etuahoaf5070f2017-10-10 13:53:25 +030018namespace
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020019{
Olli Etuahoaf5070f2017-10-10 13:53:25 +030020
21class RemoveSwitchFallThroughTraverser : public TIntermTraverser
22{
23 public:
24 static TIntermBlock *removeFallThrough(TIntermBlock *statementList);
25
26 private:
27 RemoveSwitchFallThroughTraverser(TIntermBlock *statementList);
28
29 void visitSymbol(TIntermSymbol *node) override;
30 void visitConstantUnion(TIntermConstantUnion *node) override;
Olli Etuaho78507c62017-10-10 15:06:45 +030031 bool visitDeclaration(Visit, TIntermDeclaration *node) override;
Olli Etuahoaf5070f2017-10-10 13:53:25 +030032 bool visitBinary(Visit, TIntermBinary *node) override;
33 bool visitUnary(Visit, TIntermUnary *node) override;
34 bool visitTernary(Visit visit, TIntermTernary *node) override;
Olli Etuaho78507c62017-10-10 15:06:45 +030035 bool visitSwizzle(Visit, TIntermSwizzle *node) override;
Olli Etuahoaf5070f2017-10-10 13:53:25 +030036 bool visitIfElse(Visit visit, TIntermIfElse *node) override;
37 bool visitSwitch(Visit, TIntermSwitch *node) override;
38 bool visitCase(Visit, TIntermCase *node) override;
39 bool visitAggregate(Visit, TIntermAggregate *node) override;
40 bool visitBlock(Visit, TIntermBlock *node) override;
41 bool visitLoop(Visit, TIntermLoop *node) override;
42 bool visitBranch(Visit, TIntermBranch *node) override;
43
44 void outputSequence(TIntermSequence *sequence, size_t startIndex);
45 void handlePreviousCase();
46
47 TIntermBlock *mStatementList;
48 TIntermBlock *mStatementListOut;
49 bool mLastStatementWasBreak;
50 TIntermBlock *mPreviousCase;
51 std::vector<TIntermBlock *> mCasesSharingBreak;
52};
53
54TIntermBlock *RemoveSwitchFallThroughTraverser::removeFallThrough(TIntermBlock *statementList)
55{
56 RemoveSwitchFallThroughTraverser rm(statementList);
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020057 ASSERT(statementList);
58 statementList->traverse(&rm);
Olli Etuaho852fe872017-10-10 15:13:59 +030059 ASSERT(rm.mPreviousCase || statementList->getSequence()->empty());
60 if (!rm.mLastStatementWasBreak && rm.mPreviousCase)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020061 {
Olli Etuaho852fe872017-10-10 15:13:59 +030062 // Make sure that there's a branch at the end of the final case inside the switch statement.
63 // This also ensures that any cases that fall through to the final case will get the break.
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020064 TIntermBranch *finalBreak = new TIntermBranch(EOpBreak, nullptr);
Olli Etuaho852fe872017-10-10 15:13:59 +030065 rm.mPreviousCase->getSequence()->push_back(finalBreak);
66 rm.mLastStatementWasBreak = true;
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020067 }
Olli Etuaho852fe872017-10-10 15:13:59 +030068 rm.handlePreviousCase();
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020069 return rm.mStatementListOut;
70}
71
Olli Etuahoaf5070f2017-10-10 13:53:25 +030072RemoveSwitchFallThroughTraverser::RemoveSwitchFallThroughTraverser(TIntermBlock *statementList)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020073 : TIntermTraverser(true, false, false),
74 mStatementList(statementList),
75 mLastStatementWasBreak(false),
76 mPreviousCase(nullptr)
77{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010078 mStatementListOut = new TIntermBlock();
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020079}
80
Olli Etuahoaf5070f2017-10-10 13:53:25 +030081void RemoveSwitchFallThroughTraverser::visitSymbol(TIntermSymbol *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020082{
83 // Note that this assumes that switch statements which don't begin by a case statement
84 // have already been weeded out in validation.
85 mPreviousCase->getSequence()->push_back(node);
86 mLastStatementWasBreak = false;
87}
88
Olli Etuahoaf5070f2017-10-10 13:53:25 +030089void RemoveSwitchFallThroughTraverser::visitConstantUnion(TIntermConstantUnion *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020090{
91 // Conditions of case labels are not traversed, so this is some other constant
92 // Could be just a statement like "0;"
93 mPreviousCase->getSequence()->push_back(node);
94 mLastStatementWasBreak = false;
95}
96
Olli Etuaho78507c62017-10-10 15:06:45 +030097bool RemoveSwitchFallThroughTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
98{
99 mPreviousCase->getSequence()->push_back(node);
100 mLastStatementWasBreak = false;
101 return false;
102}
103
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300104bool RemoveSwitchFallThroughTraverser::visitBinary(Visit, TIntermBinary *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200105{
106 mPreviousCase->getSequence()->push_back(node);
107 mLastStatementWasBreak = false;
108 return false;
109}
110
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300111bool RemoveSwitchFallThroughTraverser::visitUnary(Visit, TIntermUnary *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200112{
113 mPreviousCase->getSequence()->push_back(node);
114 mLastStatementWasBreak = false;
115 return false;
116}
117
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300118bool RemoveSwitchFallThroughTraverser::visitTernary(Visit, TIntermTernary *node)
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300119{
120 mPreviousCase->getSequence()->push_back(node);
121 mLastStatementWasBreak = false;
122 return false;
123}
124
Olli Etuaho78507c62017-10-10 15:06:45 +0300125bool RemoveSwitchFallThroughTraverser::visitSwizzle(Visit, TIntermSwizzle *node)
126{
127 mPreviousCase->getSequence()->push_back(node);
128 mLastStatementWasBreak = false;
129 return false;
130}
131
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300132bool RemoveSwitchFallThroughTraverser::visitIfElse(Visit, TIntermIfElse *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200133{
134 mPreviousCase->getSequence()->push_back(node);
135 mLastStatementWasBreak = false;
136 return false;
137}
138
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300139bool RemoveSwitchFallThroughTraverser::visitSwitch(Visit, TIntermSwitch *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200140{
141 mPreviousCase->getSequence()->push_back(node);
142 mLastStatementWasBreak = false;
143 // Don't go into nested switch statements
144 return false;
145}
146
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300147void RemoveSwitchFallThroughTraverser::outputSequence(TIntermSequence *sequence, size_t startIndex)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200148{
149 for (size_t i = startIndex; i < sequence->size(); ++i)
150 {
151 mStatementListOut->getSequence()->push_back(sequence->at(i));
152 }
153}
154
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300155void RemoveSwitchFallThroughTraverser::handlePreviousCase()
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200156{
157 if (mPreviousCase)
158 mCasesSharingBreak.push_back(mPreviousCase);
159 if (mLastStatementWasBreak)
160 {
161 bool labelsWithNoStatements = true;
162 for (size_t i = 0; i < mCasesSharingBreak.size(); ++i)
163 {
164 if (mCasesSharingBreak.at(i)->getSequence()->size() > 1)
165 {
166 labelsWithNoStatements = false;
167 }
168 if (labelsWithNoStatements)
169 {
170 // Fall-through is allowed in case the label has no statements.
171 outputSequence(mCasesSharingBreak.at(i)->getSequence(), 0);
172 }
173 else
174 {
175 // Include all the statements that this case can fall through under the same label.
176 for (size_t j = i; j < mCasesSharingBreak.size(); ++j)
177 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500178 size_t startIndex =
179 j > i ? 1 : 0; // Add the label only from the first sequence.
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200180 outputSequence(mCasesSharingBreak.at(j)->getSequence(), startIndex);
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200181 }
182 }
183 }
184 mCasesSharingBreak.clear();
185 }
186 mLastStatementWasBreak = false;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500187 mPreviousCase = nullptr;
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200188}
189
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300190bool RemoveSwitchFallThroughTraverser::visitCase(Visit, TIntermCase *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200191{
192 handlePreviousCase();
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100193 mPreviousCase = new TIntermBlock();
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200194 mPreviousCase->getSequence()->push_back(node);
195 // Don't traverse the condition of the case statement
196 return false;
197}
198
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300199bool RemoveSwitchFallThroughTraverser::visitAggregate(Visit, TIntermAggregate *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200200{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100201 mPreviousCase->getSequence()->push_back(node);
202 mLastStatementWasBreak = false;
203 return false;
204}
205
Olli Etuaho4bd730c2017-10-10 14:14:19 +0300206bool DoesBlockAlwaysBreak(TIntermBlock *node)
207{
208 if (node->getSequence()->empty())
209 {
210 return false;
211 }
212
213 TIntermBlock *lastStatementAsBlock = node->getSequence()->back()->getAsBlock();
214 if (lastStatementAsBlock)
215 {
216 return DoesBlockAlwaysBreak(lastStatementAsBlock);
217 }
218
219 TIntermBranch *lastStatementAsBranch = node->getSequence()->back()->getAsBranchNode();
220 return lastStatementAsBranch != nullptr;
221}
222
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300223bool RemoveSwitchFallThroughTraverser::visitBlock(Visit, TIntermBlock *node)
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100224{
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200225 if (node != mStatementList)
226 {
227 mPreviousCase->getSequence()->push_back(node);
Olli Etuaho4bd730c2017-10-10 14:14:19 +0300228 mLastStatementWasBreak = DoesBlockAlwaysBreak(node);
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200229 return false;
230 }
231 return true;
232}
233
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300234bool RemoveSwitchFallThroughTraverser::visitLoop(Visit, TIntermLoop *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200235{
236 mPreviousCase->getSequence()->push_back(node);
237 mLastStatementWasBreak = false;
238 return false;
239}
240
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300241bool RemoveSwitchFallThroughTraverser::visitBranch(Visit, TIntermBranch *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200242{
243 mPreviousCase->getSequence()->push_back(node);
244 // TODO: Verify that accepting return or continue statements here doesn't cause problems.
245 mLastStatementWasBreak = true;
246 return false;
247}
Jamie Madill45bcc782016-11-07 13:58:48 -0500248
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300249} // anonymous namespace
250
251TIntermBlock *RemoveSwitchFallThrough(TIntermBlock *statementList)
252{
253 return RemoveSwitchFallThroughTraverser::removeFallThrough(statementList);
254}
255
Jamie Madill45bcc782016-11-07 13:58:48 -0500256} // namespace sh