blob: b278b53436e05aee5dcd10e339f569991feb38a7 [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
65bool RemoveSwitchFallThrough::visitSelection(Visit, TIntermSelection *node)
66{
67 mPreviousCase->getSequence()->push_back(node);
68 mLastStatementWasBreak = false;
69 return false;
70}
71
72bool RemoveSwitchFallThrough::visitSwitch(Visit, TIntermSwitch *node)
73{
74 mPreviousCase->getSequence()->push_back(node);
75 mLastStatementWasBreak = false;
76 // Don't go into nested switch statements
77 return false;
78}
79
80void RemoveSwitchFallThrough::outputSequence(TIntermSequence *sequence, size_t startIndex)
81{
82 for (size_t i = startIndex; i < sequence->size(); ++i)
83 {
84 mStatementListOut->getSequence()->push_back(sequence->at(i));
85 }
86}
87
88void RemoveSwitchFallThrough::handlePreviousCase()
89{
90 if (mPreviousCase)
91 mCasesSharingBreak.push_back(mPreviousCase);
92 if (mLastStatementWasBreak)
93 {
94 bool labelsWithNoStatements = true;
95 for (size_t i = 0; i < mCasesSharingBreak.size(); ++i)
96 {
97 if (mCasesSharingBreak.at(i)->getSequence()->size() > 1)
98 {
99 labelsWithNoStatements = false;
100 }
101 if (labelsWithNoStatements)
102 {
103 // Fall-through is allowed in case the label has no statements.
104 outputSequence(mCasesSharingBreak.at(i)->getSequence(), 0);
105 }
106 else
107 {
108 // Include all the statements that this case can fall through under the same label.
109 for (size_t j = i; j < mCasesSharingBreak.size(); ++j)
110 {
111 size_t startIndex = j > i ? 1 : 0; // Add the label only from the first sequence.
112 outputSequence(mCasesSharingBreak.at(j)->getSequence(), startIndex);
113
114 }
115 }
116 }
117 mCasesSharingBreak.clear();
118 }
119 mLastStatementWasBreak = false;
120 mPreviousCase = nullptr;
121}
122
123bool RemoveSwitchFallThrough::visitCase(Visit, TIntermCase *node)
124{
125 handlePreviousCase();
126 mPreviousCase = new TIntermAggregate();
127 mPreviousCase->setOp(EOpSequence);
128 mPreviousCase->getSequence()->push_back(node);
129 // Don't traverse the condition of the case statement
130 return false;
131}
132
133bool RemoveSwitchFallThrough::visitAggregate(Visit, TIntermAggregate *node)
134{
135 if (node != mStatementList)
136 {
137 mPreviousCase->getSequence()->push_back(node);
138 mLastStatementWasBreak = false;
139 return false;
140 }
141 return true;
142}
143
144bool RemoveSwitchFallThrough::visitLoop(Visit, TIntermLoop *node)
145{
146 mPreviousCase->getSequence()->push_back(node);
147 mLastStatementWasBreak = false;
148 return false;
149}
150
151bool RemoveSwitchFallThrough::visitBranch(Visit, TIntermBranch *node)
152{
153 mPreviousCase->getSequence()->push_back(node);
154 // TODO: Verify that accepting return or continue statements here doesn't cause problems.
155 mLastStatementWasBreak = true;
156 return false;
157}