1 //
2 // Copyright (c) 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/WrapSwitchStatementsInBlocks.h"
30
31 #include "compiler/translator/IntermNode.h"
32 #include "compiler/translator/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), mDidWrap(false)
44 {
45 }
46
47 bool visitSwitch(Visit visit, TIntermSwitch *node) override;
48
didWrap() const49 bool didWrap() const { return mDidWrap; }
50
51 private:
52 bool mDidWrap;
53 };
54
visitSwitch(Visit,TIntermSwitch * node)55 bool WrapSwitchStatementsInBlocksTraverser::visitSwitch(Visit, TIntermSwitch *node)
56 {
57 std::vector<TIntermDeclaration *> declarations;
58 TIntermSequence *statementList = node->getStatementList()->getSequence();
59 for (TIntermNode *statement : *statementList)
60 {
61 TIntermDeclaration *asDeclaration = statement->getAsDeclarationNode();
62 if (asDeclaration)
63 {
64 declarations.push_back(asDeclaration);
65 }
66 }
67 if (declarations.empty())
68 {
69 // We don't need to wrap the switch if it doesn't contain declarations as its direct
70 // descendants.
71 return true;
72 }
73
74 TIntermBlock *wrapperBlock = new TIntermBlock();
75 for (TIntermDeclaration *declaration : declarations)
76 {
77 // SeparateDeclarations should have already been run.
78 ASSERT(declaration->getSequence()->size() == 1);
79
80 TIntermDeclaration *declarationInBlock = new TIntermDeclaration();
81 TIntermSymbol *declaratorAsSymbol = declaration->getSequence()->at(0)->getAsSymbolNode();
82 if (declaratorAsSymbol)
83 {
84 // This is a simple declaration like: "float f;"
85 // Remove the declaration from inside the switch and put it in the wrapping block.
86 TIntermSequence emptyReplacement;
87 mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(
88 node->getStatementList(), declaration, emptyReplacement));
89
90 declarationInBlock->appendDeclarator(declaratorAsSymbol->deepCopy());
91 }
92 else
93 {
94 // This is an init declaration like: "float f = 0.0;"
95 // Change the init declaration inside the switch into an assignment and put a plain
96 // declaration in the wrapping block.
97 TIntermBinary *declaratorAsBinary =
98 declaration->getSequence()->at(0)->getAsBinaryNode();
99 ASSERT(declaratorAsBinary);
100
101 TIntermBinary *initAssignment = new TIntermBinary(
102 EOpAssign, declaratorAsBinary->getLeft(), declaratorAsBinary->getRight());
103
104 queueReplacementWithParent(node->getStatementList(), declaration, initAssignment,
105 OriginalNode::IS_DROPPED);
106
107 declarationInBlock->appendDeclarator(declaratorAsBinary->getLeft()->deepCopy());
108 }
109 wrapperBlock->appendStatement(declarationInBlock);
110 }
111
112 wrapperBlock->appendStatement(node);
113 queueReplacement(wrapperBlock, OriginalNode::BECOMES_CHILD);
114 mDidWrap = true;
115
116 // Should be fine to process multiple switch statements, even nesting ones in the same
117 // traversal.
118 return true;
119 }
120
121 } // anonymous namespace
122
123 // Wrap switch statements in the AST into blocks when needed.
WrapSwitchStatementsInBlocks(TIntermBlock * root)124 bool WrapSwitchStatementsInBlocks(TIntermBlock *root)
125 {
126 WrapSwitchStatementsInBlocksTraverser traverser;
127 root->traverse(&traverser);
128 traverser.updateTree();
129 return traverser.didWrap();
130 }
131
132 } // namespace sh
133