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