Hash :
f6d242ed
Author :
Date :
2017-10-12T17:21:06
Wrap switch statements in blocks in HLSL If variables are declared inside a GLSL switch statement, they are scoped until the end of the switch statement. This is not compatible with HLSL rules, where the scoping is until the end of the case. To work around this, wrap switch statements in a block that declares the variables in HLSL. This is done after most other transformations done to the AST are complete, since some of the other transformations may introduce temporary variables. BUG=angleproject:2179 TEST=angle_end2end_tests Change-Id: Id0bb89affe103177fd3d6a6b2f3619b5e1ada0a6 Reviewed-on: https://chromium-review.googlesource.com/716381 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
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
//
// Copyright (c) 2017 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.
//
// WrapSwitchStatementsInBlocks.cpp: Wrap switch statements in blocks and declare all switch-scoped
// variables there to make the AST compatible with HLSL output.
//
// switch (init)
// {
// case 0:
// float f;
// default:
// f = 1.0;
// }
//
// becomes
//
// {
// float f;
// switch (init)
// {
// case 0:
// default:
// f = 1.0;
// }
// }
#include "compiler/translator/WrapSwitchStatementsInBlocks.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/IntermTraverse.h"
namespace sh
{
namespace
{
class WrapSwitchStatementsInBlocksTraverser : public TIntermTraverser
{
public:
WrapSwitchStatementsInBlocksTraverser() : TIntermTraverser(true, false, false), mDidWrap(false)
{
}
bool visitSwitch(Visit visit, TIntermSwitch *node) override;
bool didWrap() const { return mDidWrap; }
private:
bool mDidWrap;
};
bool WrapSwitchStatementsInBlocksTraverser::visitSwitch(Visit, TIntermSwitch *node)
{
std::vector<TIntermDeclaration *> declarations;
TIntermSequence *statementList = node->getStatementList()->getSequence();
for (TIntermNode *statement : *statementList)
{
TIntermDeclaration *asDeclaration = statement->getAsDeclarationNode();
if (asDeclaration)
{
declarations.push_back(asDeclaration);
}
}
if (declarations.empty())
{
// We don't need to wrap the switch if it doesn't contain declarations as its direct
// descendants.
return true;
}
TIntermBlock *wrapperBlock = new TIntermBlock();
for (TIntermDeclaration *declaration : declarations)
{
// SeparateDeclarations should have already been run.
ASSERT(declaration->getSequence()->size() == 1);
TIntermDeclaration *declarationInBlock = new TIntermDeclaration();
TIntermSymbol *declaratorAsSymbol = declaration->getSequence()->at(0)->getAsSymbolNode();
if (declaratorAsSymbol)
{
// This is a simple declaration like: "float f;"
// Remove the declaration from inside the switch and put it in the wrapping block.
TIntermSequence emptyReplacement;
mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(
node->getStatementList(), declaration, emptyReplacement));
declarationInBlock->appendDeclarator(declaratorAsSymbol->deepCopy());
}
else
{
// This is an init declaration like: "float f = 0.0;"
// Change the init declaration inside the switch into an assignment and put a plain
// declaration in the wrapping block.
TIntermBinary *declaratorAsBinary =
declaration->getSequence()->at(0)->getAsBinaryNode();
ASSERT(declaratorAsBinary);
TIntermBinary *initAssignment = new TIntermBinary(
EOpAssign, declaratorAsBinary->getLeft(), declaratorAsBinary->getRight());
queueReplacementWithParent(node->getStatementList(), declaration, initAssignment,
OriginalNode::IS_DROPPED);
declarationInBlock->appendDeclarator(declaratorAsBinary->getLeft()->deepCopy());
}
wrapperBlock->appendStatement(declarationInBlock);
}
wrapperBlock->appendStatement(node);
queueReplacement(wrapperBlock, OriginalNode::BECOMES_CHILD);
mDidWrap = true;
// Should be fine to process multiple switch statements, even nesting ones in the same
// traversal.
return true;
}
} // anonymous namespace
// Wrap switch statements in the AST into blocks when needed.
bool WrapSwitchStatementsInBlocks(TIntermBlock *root)
{
WrapSwitchStatementsInBlocksTraverser traverser;
root->traverse(&traverser);
traverser.updateTree();
return traverser.didWrap();
}
} // namespace sh