blob: 6c108a12d9bf00cb01d3a6fa9971260eefa7fb6f [file] [log] [blame]
//
// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/RemoveSwitchFallThrough.h"
namespace sh
{
TIntermBlock *RemoveSwitchFallThrough::removeFallThrough(TIntermBlock *statementList)
{
RemoveSwitchFallThrough rm(statementList);
ASSERT(statementList);
statementList->traverse(&rm);
bool lastStatementWasBreak = rm.mLastStatementWasBreak;
rm.mLastStatementWasBreak = true;
rm.handlePreviousCase();
if (!lastStatementWasBreak)
{
TIntermBranch *finalBreak = new TIntermBranch(EOpBreak, nullptr);
rm.mStatementListOut->getSequence()->push_back(finalBreak);
}
return rm.mStatementListOut;
}
RemoveSwitchFallThrough::RemoveSwitchFallThrough(TIntermBlock *statementList)
: TIntermTraverser(true, false, false),
mStatementList(statementList),
mLastStatementWasBreak(false),
mPreviousCase(nullptr)
{
mStatementListOut = new TIntermBlock();
}
void RemoveSwitchFallThrough::visitSymbol(TIntermSymbol *node)
{
// Note that this assumes that switch statements which don't begin by a case statement
// have already been weeded out in validation.
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
}
void RemoveSwitchFallThrough::visitConstantUnion(TIntermConstantUnion *node)
{
// Conditions of case labels are not traversed, so this is some other constant
// Could be just a statement like "0;"
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
}
bool RemoveSwitchFallThrough::visitBinary(Visit, TIntermBinary *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
return false;
}
bool RemoveSwitchFallThrough::visitUnary(Visit, TIntermUnary *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
return false;
}
bool RemoveSwitchFallThrough::visitTernary(Visit, TIntermTernary *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
return false;
}
bool RemoveSwitchFallThrough::visitIfElse(Visit, TIntermIfElse *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
return false;
}
bool RemoveSwitchFallThrough::visitSwitch(Visit, TIntermSwitch *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
// Don't go into nested switch statements
return false;
}
void RemoveSwitchFallThrough::outputSequence(TIntermSequence *sequence, size_t startIndex)
{
for (size_t i = startIndex; i < sequence->size(); ++i)
{
mStatementListOut->getSequence()->push_back(sequence->at(i));
}
}
void RemoveSwitchFallThrough::handlePreviousCase()
{
if (mPreviousCase)
mCasesSharingBreak.push_back(mPreviousCase);
if (mLastStatementWasBreak)
{
bool labelsWithNoStatements = true;
for (size_t i = 0; i < mCasesSharingBreak.size(); ++i)
{
if (mCasesSharingBreak.at(i)->getSequence()->size() > 1)
{
labelsWithNoStatements = false;
}
if (labelsWithNoStatements)
{
// Fall-through is allowed in case the label has no statements.
outputSequence(mCasesSharingBreak.at(i)->getSequence(), 0);
}
else
{
// Include all the statements that this case can fall through under the same label.
for (size_t j = i; j < mCasesSharingBreak.size(); ++j)
{
size_t startIndex =
j > i ? 1 : 0; // Add the label only from the first sequence.
outputSequence(mCasesSharingBreak.at(j)->getSequence(), startIndex);
}
}
}
mCasesSharingBreak.clear();
}
mLastStatementWasBreak = false;
mPreviousCase = nullptr;
}
bool RemoveSwitchFallThrough::visitCase(Visit, TIntermCase *node)
{
handlePreviousCase();
mPreviousCase = new TIntermBlock();
mPreviousCase->getSequence()->push_back(node);
// Don't traverse the condition of the case statement
return false;
}
bool RemoveSwitchFallThrough::visitAggregate(Visit, TIntermAggregate *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
return false;
}
bool RemoveSwitchFallThrough::visitBlock(Visit, TIntermBlock *node)
{
if (node != mStatementList)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
return false;
}
return true;
}
bool RemoveSwitchFallThrough::visitLoop(Visit, TIntermLoop *node)
{
mPreviousCase->getSequence()->push_back(node);
mLastStatementWasBreak = false;
return false;
}
bool RemoveSwitchFallThrough::visitBranch(Visit, TIntermBranch *node)
{
mPreviousCase->getSequence()->push_back(node);
// TODO: Verify that accepting return or continue statements here doesn't cause problems.
mLastStatementWasBreak = true;
return false;
}
} // namespace sh