summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/angle/src/compiler/translator/WrapSwitchStatementsInBlocks.cpp
blob: 85a11c998daccf77a5b1881a521885e74fdcbc67 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//
// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// WrapSwitchStatementsInBlocks.cpp: Wrap switch statements in blocks and declare all switch-scoped
// variables there to make the AST compatible with HLSL output.
//
// switch (init)
// {
//     case 0:
//         float f;
//     default:
//         f = 1.0;
// }
//
// becomes
//
// {
//     float f;
//     switch (init)
//     {
//         case 0:
//         default:
//             f = 1.0;
//     }
// }

#include "compiler/translator/WrapSwitchStatementsInBlocks.h"

#include "compiler/translator/IntermNode.h"
#include "compiler/translator/IntermTraverse.h"

namespace sh
{

namespace
{

class WrapSwitchStatementsInBlocksTraverser : public TIntermTraverser
{
  public:
    WrapSwitchStatementsInBlocksTraverser() : TIntermTraverser(true, false, false), mDidWrap(false)
    {
    }

    bool visitSwitch(Visit visit, TIntermSwitch *node) override;

    bool didWrap() const { return mDidWrap; }

  private:
    bool mDidWrap;
};

bool WrapSwitchStatementsInBlocksTraverser::visitSwitch(Visit, TIntermSwitch *node)
{
    std::vector<TIntermDeclaration *> declarations;
    TIntermSequence *statementList = node->getStatementList()->getSequence();
    for (TIntermNode *statement : *statementList)
    {
        TIntermDeclaration *asDeclaration = statement->getAsDeclarationNode();
        if (asDeclaration)
        {
            declarations.push_back(asDeclaration);
        }
    }
    if (declarations.empty())
    {
        // We don't need to wrap the switch if it doesn't contain declarations as its direct
        // descendants.
        return true;
    }

    TIntermBlock *wrapperBlock = new TIntermBlock();
    for (TIntermDeclaration *declaration : declarations)
    {
        // SeparateDeclarations should have already been run.
        ASSERT(declaration->getSequence()->size() == 1);

        TIntermDeclaration *declarationInBlock = new TIntermDeclaration();
        TIntermSymbol *declaratorAsSymbol = declaration->getSequence()->at(0)->getAsSymbolNode();
        if (declaratorAsSymbol)
        {
            // This is a simple declaration like: "float f;"
            // Remove the declaration from inside the switch and put it in the wrapping block.
            TIntermSequence emptyReplacement;
            mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(
                node->getStatementList(), declaration, emptyReplacement));

            declarationInBlock->appendDeclarator(declaratorAsSymbol->deepCopy());
        }
        else
        {
            // This is an init declaration like: "float f = 0.0;"
            // Change the init declaration inside the switch into an assignment and put a plain
            // declaration in the wrapping block.
            TIntermBinary *declaratorAsBinary =
                declaration->getSequence()->at(0)->getAsBinaryNode();
            ASSERT(declaratorAsBinary);

            TIntermBinary *initAssignment = new TIntermBinary(
                EOpAssign, declaratorAsBinary->getLeft(), declaratorAsBinary->getRight());

            queueReplacementWithParent(node->getStatementList(), declaration, initAssignment,
                                       OriginalNode::IS_DROPPED);

            declarationInBlock->appendDeclarator(declaratorAsBinary->getLeft()->deepCopy());
        }
        wrapperBlock->appendStatement(declarationInBlock);
    }

    wrapperBlock->appendStatement(node);
    queueReplacement(wrapperBlock, OriginalNode::BECOMES_CHILD);
    mDidWrap = true;

    // Should be fine to process multiple switch statements, even nesting ones in the same
    // traversal.
    return true;
}

}  // anonymous namespace

// Wrap switch statements in the AST into blocks when needed.
bool WrapSwitchStatementsInBlocks(TIntermBlock *root)
{
    WrapSwitchStatementsInBlocksTraverser traverser;
    root->traverse(&traverser);
    traverser.updateTree();
    return traverser.didWrap();
}

}  // namespace sh