| // | |
| // 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 |