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