blob: 6abf7cd8ac41a40bcd6de211e9fd49988a3abdff [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;
31 bool visitBinary(Visit, TIntermBinary *node) override;
32 bool visitUnary(Visit, TIntermUnary *node) override;
33 bool visitTernary(Visit visit, TIntermTernary *node) override;
34 bool visitIfElse(Visit visit, TIntermIfElse *node) override;
35 bool visitSwitch(Visit, TIntermSwitch *node) override;
36 bool visitCase(Visit, TIntermCase *node) override;
37 bool visitAggregate(Visit, TIntermAggregate *node) override;
38 bool visitBlock(Visit, TIntermBlock *node) override;
39 bool visitLoop(Visit, TIntermLoop *node) override;
40 bool visitBranch(Visit, TIntermBranch *node) override;
41
42 void outputSequence(TIntermSequence *sequence, size_t startIndex);
43 void handlePreviousCase();
44
45 TIntermBlock *mStatementList;
46 TIntermBlock *mStatementListOut;
47 bool mLastStatementWasBreak;
48 TIntermBlock *mPreviousCase;
49 std::vector<TIntermBlock *> mCasesSharingBreak;
50};
51
52TIntermBlock *RemoveSwitchFallThroughTraverser::removeFallThrough(TIntermBlock *statementList)
53{
54 RemoveSwitchFallThroughTraverser rm(statementList);
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020055 ASSERT(statementList);
56 statementList->traverse(&rm);
57 bool lastStatementWasBreak = rm.mLastStatementWasBreak;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050058 rm.mLastStatementWasBreak = true;
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020059 rm.handlePreviousCase();
60 if (!lastStatementWasBreak)
61 {
62 TIntermBranch *finalBreak = new TIntermBranch(EOpBreak, nullptr);
63 rm.mStatementListOut->getSequence()->push_back(finalBreak);
64 }
65 return rm.mStatementListOut;
66}
67
Olli Etuahoaf5070f2017-10-10 13:53:25 +030068RemoveSwitchFallThroughTraverser::RemoveSwitchFallThroughTraverser(TIntermBlock *statementList)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020069 : TIntermTraverser(true, false, false),
70 mStatementList(statementList),
71 mLastStatementWasBreak(false),
72 mPreviousCase(nullptr)
73{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010074 mStatementListOut = new TIntermBlock();
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020075}
76
Olli Etuahoaf5070f2017-10-10 13:53:25 +030077void RemoveSwitchFallThroughTraverser::visitSymbol(TIntermSymbol *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020078{
79 // Note that this assumes that switch statements which don't begin by a case statement
80 // have already been weeded out in validation.
81 mPreviousCase->getSequence()->push_back(node);
82 mLastStatementWasBreak = false;
83}
84
Olli Etuahoaf5070f2017-10-10 13:53:25 +030085void RemoveSwitchFallThroughTraverser::visitConstantUnion(TIntermConstantUnion *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020086{
87 // Conditions of case labels are not traversed, so this is some other constant
88 // Could be just a statement like "0;"
89 mPreviousCase->getSequence()->push_back(node);
90 mLastStatementWasBreak = false;
91}
92
Olli Etuahoaf5070f2017-10-10 13:53:25 +030093bool RemoveSwitchFallThroughTraverser::visitBinary(Visit, TIntermBinary *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020094{
95 mPreviousCase->getSequence()->push_back(node);
96 mLastStatementWasBreak = false;
97 return false;
98}
99
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300100bool RemoveSwitchFallThroughTraverser::visitUnary(Visit, TIntermUnary *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200101{
102 mPreviousCase->getSequence()->push_back(node);
103 mLastStatementWasBreak = false;
104 return false;
105}
106
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300107bool RemoveSwitchFallThroughTraverser::visitTernary(Visit, TIntermTernary *node)
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300108{
109 mPreviousCase->getSequence()->push_back(node);
110 mLastStatementWasBreak = false;
111 return false;
112}
113
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300114bool RemoveSwitchFallThroughTraverser::visitIfElse(Visit, TIntermIfElse *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200115{
116 mPreviousCase->getSequence()->push_back(node);
117 mLastStatementWasBreak = false;
118 return false;
119}
120
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300121bool RemoveSwitchFallThroughTraverser::visitSwitch(Visit, TIntermSwitch *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200122{
123 mPreviousCase->getSequence()->push_back(node);
124 mLastStatementWasBreak = false;
125 // Don't go into nested switch statements
126 return false;
127}
128
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300129void RemoveSwitchFallThroughTraverser::outputSequence(TIntermSequence *sequence, size_t startIndex)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200130{
131 for (size_t i = startIndex; i < sequence->size(); ++i)
132 {
133 mStatementListOut->getSequence()->push_back(sequence->at(i));
134 }
135}
136
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300137void RemoveSwitchFallThroughTraverser::handlePreviousCase()
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200138{
139 if (mPreviousCase)
140 mCasesSharingBreak.push_back(mPreviousCase);
141 if (mLastStatementWasBreak)
142 {
143 bool labelsWithNoStatements = true;
144 for (size_t i = 0; i < mCasesSharingBreak.size(); ++i)
145 {
146 if (mCasesSharingBreak.at(i)->getSequence()->size() > 1)
147 {
148 labelsWithNoStatements = false;
149 }
150 if (labelsWithNoStatements)
151 {
152 // Fall-through is allowed in case the label has no statements.
153 outputSequence(mCasesSharingBreak.at(i)->getSequence(), 0);
154 }
155 else
156 {
157 // Include all the statements that this case can fall through under the same label.
158 for (size_t j = i; j < mCasesSharingBreak.size(); ++j)
159 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500160 size_t startIndex =
161 j > i ? 1 : 0; // Add the label only from the first sequence.
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200162 outputSequence(mCasesSharingBreak.at(j)->getSequence(), startIndex);
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200163 }
164 }
165 }
166 mCasesSharingBreak.clear();
167 }
168 mLastStatementWasBreak = false;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500169 mPreviousCase = nullptr;
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200170}
171
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300172bool RemoveSwitchFallThroughTraverser::visitCase(Visit, TIntermCase *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200173{
174 handlePreviousCase();
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100175 mPreviousCase = new TIntermBlock();
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200176 mPreviousCase->getSequence()->push_back(node);
177 // Don't traverse the condition of the case statement
178 return false;
179}
180
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300181bool RemoveSwitchFallThroughTraverser::visitAggregate(Visit, TIntermAggregate *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200182{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100183 mPreviousCase->getSequence()->push_back(node);
184 mLastStatementWasBreak = false;
185 return false;
186}
187
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300188bool RemoveSwitchFallThroughTraverser::visitBlock(Visit, TIntermBlock *node)
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100189{
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200190 if (node != mStatementList)
191 {
192 mPreviousCase->getSequence()->push_back(node);
193 mLastStatementWasBreak = false;
194 return false;
195 }
196 return true;
197}
198
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300199bool RemoveSwitchFallThroughTraverser::visitLoop(Visit, TIntermLoop *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200200{
201 mPreviousCase->getSequence()->push_back(node);
202 mLastStatementWasBreak = false;
203 return false;
204}
205
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300206bool RemoveSwitchFallThroughTraverser::visitBranch(Visit, TIntermBranch *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200207{
208 mPreviousCase->getSequence()->push_back(node);
209 // TODO: Verify that accepting return or continue statements here doesn't cause problems.
210 mLastStatementWasBreak = true;
211 return false;
212}
Jamie Madill45bcc782016-11-07 13:58:48 -0500213
Olli Etuahoaf5070f2017-10-10 13:53:25 +0300214} // anonymous namespace
215
216TIntermBlock *RemoveSwitchFallThrough(TIntermBlock *statementList)
217{
218 return RemoveSwitchFallThroughTraverser::removeFallThrough(statementList);
219}
220
Jamie Madill45bcc782016-11-07 13:58:48 -0500221} // namespace sh