Hash :
2cd7a0ed
Author :
Date :
2015-02-27T13:57:32
Remove switch fall-through on HLSL Remove fall-through from non-empty labels in switch statements. Tested with dEQP-GLES3.functional.shaders.*switch*. All pass except for tests that require dFdx/dFdy, which fail for an unrelated reason. Additional test coverage could still be useful for cases where a label is terminated by a continue or return statement. BUG=angle:921 Change-Id: I4741867789a9308d66d0adeabdaf83907106e2d2 Reviewed-on: https://chromium-review.googlesource.com/254550 Tested-by: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: Zhenyao Mo <zmo@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
//
// 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"
TIntermAggregate *RemoveSwitchFallThrough::removeFallThrough(TIntermAggregate *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(TIntermAggregate *statementList)
: TIntermTraverser(true, false, false),
mStatementList(statementList),
mLastStatementWasBreak(false),
mPreviousCase(nullptr)
{
mStatementListOut = new TIntermAggregate();
mStatementListOut->setOp(EOpSequence);
}
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::visitSelection(Visit, TIntermSelection *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 TIntermAggregate();
mPreviousCase->setOp(EOpSequence);
mPreviousCase->getSequence()->push_back(node);
// Don't traverse the condition of the case statement
return false;
}
bool RemoveSwitchFallThrough::visitAggregate(Visit, TIntermAggregate *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;
}