WrapSwitchStatementsInBlocks.cpp (4215B)
1 // 2 // Copyright 2017 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 // WrapSwitchStatementsInBlocks.cpp: Wrap switch statements in blocks and declare all switch-scoped 7 // variables there to make the AST compatible with HLSL output. 8 // 9 // switch (init) 10 // { 11 // case 0: 12 // float f; 13 // default: 14 // f = 1.0; 15 // } 16 // 17 // becomes 18 // 19 // { 20 // float f; 21 // switch (init) 22 // { 23 // case 0: 24 // default: 25 // f = 1.0; 26 // } 27 // } 28 29 #include "compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.h" 30 31 #include "compiler/translator/IntermNode.h" 32 #include "compiler/translator/tree_util/IntermTraverse.h" 33 34 namespace sh 35 { 36 37 namespace 38 { 39 40 class WrapSwitchStatementsInBlocksTraverser : public TIntermTraverser 41 { 42 public: 43 WrapSwitchStatementsInBlocksTraverser() : TIntermTraverser(true, false, false) {} 44 45 bool visitSwitch(Visit visit, TIntermSwitch *node) override; 46 }; 47 48 bool WrapSwitchStatementsInBlocksTraverser::visitSwitch(Visit, TIntermSwitch *node) 49 { 50 std::vector<TIntermDeclaration *> declarations; 51 TIntermSequence *statementList = node->getStatementList()->getSequence(); 52 for (TIntermNode *statement : *statementList) 53 { 54 TIntermDeclaration *asDeclaration = statement->getAsDeclarationNode(); 55 if (asDeclaration) 56 { 57 declarations.push_back(asDeclaration); 58 } 59 } 60 if (declarations.empty()) 61 { 62 // We don't need to wrap the switch if it doesn't contain declarations as its direct 63 // descendants. 64 return true; 65 } 66 67 TIntermBlock *wrapperBlock = new TIntermBlock(); 68 for (TIntermDeclaration *declaration : declarations) 69 { 70 // SeparateDeclarations should have already been run. 71 ASSERT(declaration->getSequence()->size() == 1); 72 73 TIntermDeclaration *declarationInBlock = new TIntermDeclaration(); 74 TIntermSymbol *declaratorAsSymbol = declaration->getSequence()->at(0)->getAsSymbolNode(); 75 if (declaratorAsSymbol) 76 { 77 // This is a simple declaration like: "float f;" 78 // Remove the declaration from inside the switch and put it in the wrapping block. 79 TIntermSequence emptyReplacement; 80 mMultiReplacements.emplace_back(node->getStatementList(), declaration, 81 std::move(emptyReplacement)); 82 83 declarationInBlock->appendDeclarator(declaratorAsSymbol->deepCopy()); 84 // The declaration can't be the last statement inside the switch since unused variables 85 // should already have been pruned. 86 ASSERT(declaration != statementList->back()); 87 } 88 else 89 { 90 // This is an init declaration like: "float f = 0.0;" 91 // Change the init declaration inside the switch into an assignment and put a plain 92 // declaration in the wrapping block. 93 TIntermBinary *declaratorAsBinary = 94 declaration->getSequence()->at(0)->getAsBinaryNode(); 95 ASSERT(declaratorAsBinary); 96 97 TIntermBinary *initAssignment = new TIntermBinary( 98 EOpAssign, declaratorAsBinary->getLeft(), declaratorAsBinary->getRight()); 99 100 queueReplacementWithParent(node->getStatementList(), declaration, initAssignment, 101 OriginalNode::IS_DROPPED); 102 103 declarationInBlock->appendDeclarator(declaratorAsBinary->getLeft()->deepCopy()); 104 } 105 wrapperBlock->appendStatement(declarationInBlock); 106 } 107 108 wrapperBlock->appendStatement(node); 109 queueReplacement(wrapperBlock, OriginalNode::BECOMES_CHILD); 110 111 // Should be fine to process multiple switch statements, even nesting ones in the same 112 // traversal. 113 return true; 114 } 115 116 } // anonymous namespace 117 118 // Wrap switch statements in the AST into blocks when needed. 119 bool WrapSwitchStatementsInBlocks(TCompiler *compiler, TIntermBlock *root) 120 { 121 WrapSwitchStatementsInBlocksTraverser traverser; 122 root->traverse(&traverser); 123 return traverser.updateTree(compiler, root); 124 } 125 126 } // namespace sh