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