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/hlsl/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:
WrapSwitchStatementsInBlocksTraverser()43 WrapSwitchStatementsInBlocksTraverser() : TIntermTraverser(true, false, false) {}
44
45 bool visitSwitch(Visit visit, TIntermSwitch *node) override;
46 };
47
visitSwitch(Visit,TIntermSwitch * node)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.
WrapSwitchStatementsInBlocks(TCompiler * compiler,TIntermBlock * root)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
127