blob: 1335387f5399ffc2a50005e394244f5a0bfdf345 [file] [log] [blame]
//
// Copyright 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/tree_ops/WrapSwitchStatementsInBlocks.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
namespace sh
{
namespace
{
class WrapSwitchStatementsInBlocksTraverser : public TIntermTraverser
{
public:
WrapSwitchStatementsInBlocksTraverser() : TIntermTraverser(true, false, false) {}
bool visitSwitch(Visit visit, TIntermSwitch *node) override;
};
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());
// The declaration can't be the last statement inside the switch since unused variables
// should already have been pruned.
ASSERT(declaration != statementList->back());
}
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);
// 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(TCompiler *compiler, TIntermBlock *root)
{
WrapSwitchStatementsInBlocksTraverser traverser;
root->traverse(&traverser);
return traverser.updateTree(compiler, root);
}
} // namespace sh