blob: dd995af47c2c8d0b3b56f6efbd601695564aa235 [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//
6
7#include "compiler/translator/RemoveSwitchFallThrough.h"
8
Jamie Madill45bcc782016-11-07 13:58:48 -05009namespace sh
10{
11
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010012TIntermBlock *RemoveSwitchFallThrough::removeFallThrough(TIntermBlock *statementList)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020013{
14 RemoveSwitchFallThrough rm(statementList);
15 ASSERT(statementList);
16 statementList->traverse(&rm);
17 bool lastStatementWasBreak = rm.mLastStatementWasBreak;
18 rm.mLastStatementWasBreak = true;
19 rm.handlePreviousCase();
20 if (!lastStatementWasBreak)
21 {
22 TIntermBranch *finalBreak = new TIntermBranch(EOpBreak, nullptr);
23 rm.mStatementListOut->getSequence()->push_back(finalBreak);
24 }
25 return rm.mStatementListOut;
26}
27
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010028RemoveSwitchFallThrough::RemoveSwitchFallThrough(TIntermBlock *statementList)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020029 : TIntermTraverser(true, false, false),
30 mStatementList(statementList),
31 mLastStatementWasBreak(false),
32 mPreviousCase(nullptr)
33{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010034 mStatementListOut = new TIntermBlock();
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020035}
36
37void RemoveSwitchFallThrough::visitSymbol(TIntermSymbol *node)
38{
39 // Note that this assumes that switch statements which don't begin by a case statement
40 // have already been weeded out in validation.
41 mPreviousCase->getSequence()->push_back(node);
42 mLastStatementWasBreak = false;
43}
44
45void RemoveSwitchFallThrough::visitConstantUnion(TIntermConstantUnion *node)
46{
47 // Conditions of case labels are not traversed, so this is some other constant
48 // Could be just a statement like "0;"
49 mPreviousCase->getSequence()->push_back(node);
50 mLastStatementWasBreak = false;
51}
52
53bool RemoveSwitchFallThrough::visitBinary(Visit, TIntermBinary *node)
54{
55 mPreviousCase->getSequence()->push_back(node);
56 mLastStatementWasBreak = false;
57 return false;
58}
59
60bool RemoveSwitchFallThrough::visitUnary(Visit, TIntermUnary *node)
61{
62 mPreviousCase->getSequence()->push_back(node);
63 mLastStatementWasBreak = false;
64 return false;
65}
66
Olli Etuahod0bad2c2016-09-09 18:01:16 +030067bool RemoveSwitchFallThrough::visitTernary(Visit, TIntermTernary *node)
68{
69 mPreviousCase->getSequence()->push_back(node);
70 mLastStatementWasBreak = false;
71 return false;
72}
73
Olli Etuaho57961272016-09-14 13:57:46 +030074bool RemoveSwitchFallThrough::visitIfElse(Visit, TIntermIfElse *node)
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +020075{
76 mPreviousCase->getSequence()->push_back(node);
77 mLastStatementWasBreak = false;
78 return false;
79}
80
81bool RemoveSwitchFallThrough::visitSwitch(Visit, TIntermSwitch *node)
82{
83 mPreviousCase->getSequence()->push_back(node);
84 mLastStatementWasBreak = false;
85 // Don't go into nested switch statements
86 return false;
87}
88
89void RemoveSwitchFallThrough::outputSequence(TIntermSequence *sequence, size_t startIndex)
90{
91 for (size_t i = startIndex; i < sequence->size(); ++i)
92 {
93 mStatementListOut->getSequence()->push_back(sequence->at(i));
94 }
95}
96
97void RemoveSwitchFallThrough::handlePreviousCase()
98{
99 if (mPreviousCase)
100 mCasesSharingBreak.push_back(mPreviousCase);
101 if (mLastStatementWasBreak)
102 {
103 bool labelsWithNoStatements = true;
104 for (size_t i = 0; i < mCasesSharingBreak.size(); ++i)
105 {
106 if (mCasesSharingBreak.at(i)->getSequence()->size() > 1)
107 {
108 labelsWithNoStatements = false;
109 }
110 if (labelsWithNoStatements)
111 {
112 // Fall-through is allowed in case the label has no statements.
113 outputSequence(mCasesSharingBreak.at(i)->getSequence(), 0);
114 }
115 else
116 {
117 // Include all the statements that this case can fall through under the same label.
118 for (size_t j = i; j < mCasesSharingBreak.size(); ++j)
119 {
120 size_t startIndex = j > i ? 1 : 0; // Add the label only from the first sequence.
121 outputSequence(mCasesSharingBreak.at(j)->getSequence(), startIndex);
122
123 }
124 }
125 }
126 mCasesSharingBreak.clear();
127 }
128 mLastStatementWasBreak = false;
129 mPreviousCase = nullptr;
130}
131
132bool RemoveSwitchFallThrough::visitCase(Visit, TIntermCase *node)
133{
134 handlePreviousCase();
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100135 mPreviousCase = new TIntermBlock();
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200136 mPreviousCase->getSequence()->push_back(node);
137 // Don't traverse the condition of the case statement
138 return false;
139}
140
141bool RemoveSwitchFallThrough::visitAggregate(Visit, TIntermAggregate *node)
142{
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100143 mPreviousCase->getSequence()->push_back(node);
144 mLastStatementWasBreak = false;
145 return false;
146}
147
148bool RemoveSwitchFallThrough::visitBlock(Visit, TIntermBlock *node)
149{
Olli Etuaho2cd7a0e2015-02-27 13:57:32 +0200150 if (node != mStatementList)
151 {
152 mPreviousCase->getSequence()->push_back(node);
153 mLastStatementWasBreak = false;
154 return false;
155 }
156 return true;
157}
158
159bool RemoveSwitchFallThrough::visitLoop(Visit, TIntermLoop *node)
160{
161 mPreviousCase->getSequence()->push_back(node);
162 mLastStatementWasBreak = false;
163 return false;
164}
165
166bool RemoveSwitchFallThrough::visitBranch(Visit, TIntermBranch *node)
167{
168 mPreviousCase->getSequence()->push_back(node);
169 // TODO: Verify that accepting return or continue statements here doesn't cause problems.
170 mLastStatementWasBreak = true;
171 return false;
172}
Jamie Madill45bcc782016-11-07 13:58:48 -0500173
174} // namespace sh