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