1 | // |
2 | // Copyright (c) 2015 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 | |
7 | // RewriteDoWhile.cpp: rewrites do-while loops using another equivalent |
8 | // construct. |
9 | |
10 | #include "compiler/translator/tree_ops/RewriteDoWhile.h" |
11 | |
12 | #include "compiler/translator/StaticType.h" |
13 | #include "compiler/translator/tree_util/IntermNode_util.h" |
14 | #include "compiler/translator/tree_util/IntermTraverse.h" |
15 | |
16 | namespace sh |
17 | { |
18 | |
19 | namespace |
20 | { |
21 | |
22 | // An AST traverser that rewrites loops of the form |
23 | // do { |
24 | // CODE; |
25 | // } while (CONDITION) |
26 | // |
27 | // to loops of the form |
28 | // bool temp = false; |
29 | // while (true) { |
30 | // if (temp) { |
31 | // if (!CONDITION) { |
32 | // break; |
33 | // } |
34 | // } |
35 | // temp = true; |
36 | // CODE; |
37 | // } |
38 | // |
39 | // The reason we don't use a simpler form, with for example just (temp && !CONDITION) in the |
40 | // while condition, is that short-circuit is often badly supported by driver shader compiler. |
41 | // The double if has the same effect, but forces shader compilers to behave. |
42 | // |
43 | // TODO(cwallez) when UnfoldShortCircuitIntoIf handles loops correctly, revisit this as we might |
44 | // be able to use while (temp || CONDITION) with temp initially set to true then run |
45 | // UnfoldShortCircuitIntoIf |
46 | class DoWhileRewriter : public TIntermTraverser |
47 | { |
48 | public: |
49 | DoWhileRewriter(TSymbolTable *symbolTable) : TIntermTraverser(true, false, false, symbolTable) |
50 | {} |
51 | |
52 | bool visitBlock(Visit, TIntermBlock *node) override |
53 | { |
54 | // A well-formed AST can only have do-while inside TIntermBlock. By doing a prefix traversal |
55 | // we are able to replace the do-while in the sequence directly as the content of the |
56 | // do-while will be traversed later. |
57 | |
58 | TIntermSequence *statements = node->getSequence(); |
59 | |
60 | // The statements vector will have new statements inserted when we encounter a do-while, |
61 | // which prevents us from using a range-based for loop. Using the usual i++ works, as |
62 | // the (two) new statements inserted replace the statement at the current position. |
63 | for (size_t i = 0; i < statements->size(); i++) |
64 | { |
65 | TIntermNode *statement = (*statements)[i]; |
66 | TIntermLoop *loop = statement->getAsLoopNode(); |
67 | |
68 | if (loop == nullptr || loop->getType() != ELoopDoWhile) |
69 | { |
70 | continue; |
71 | } |
72 | |
73 | // Found a loop to change. |
74 | const TType *boolType = StaticType::Get<EbtBool, EbpUndefined, EvqTemporary, 1, 1>(); |
75 | TVariable *conditionVariable = CreateTempVariable(mSymbolTable, boolType); |
76 | |
77 | // bool temp = false; |
78 | TIntermDeclaration *tempDeclaration = |
79 | CreateTempInitDeclarationNode(conditionVariable, CreateBoolNode(false)); |
80 | |
81 | // temp = true; |
82 | TIntermBinary *assignTrue = |
83 | CreateTempAssignmentNode(conditionVariable, CreateBoolNode(true)); |
84 | |
85 | // if (temp) { |
86 | // if (!CONDITION) { |
87 | // break; |
88 | // } |
89 | // } |
90 | TIntermIfElse *breakIf = nullptr; |
91 | { |
92 | TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr); |
93 | |
94 | TIntermBlock *breakBlock = new TIntermBlock(); |
95 | breakBlock->getSequence()->push_back(breakStatement); |
96 | |
97 | TIntermUnary *negatedCondition = |
98 | new TIntermUnary(EOpLogicalNot, loop->getCondition(), nullptr); |
99 | |
100 | TIntermIfElse *innerIf = new TIntermIfElse(negatedCondition, breakBlock, nullptr); |
101 | |
102 | TIntermBlock *innerIfBlock = new TIntermBlock(); |
103 | innerIfBlock->getSequence()->push_back(innerIf); |
104 | |
105 | breakIf = new TIntermIfElse(CreateTempSymbolNode(conditionVariable), innerIfBlock, |
106 | nullptr); |
107 | } |
108 | |
109 | // Assemble the replacement loops, reusing the do-while loop's body and inserting our |
110 | // statements at the front. |
111 | TIntermLoop *newLoop = nullptr; |
112 | { |
113 | TIntermBlock *body = loop->getBody(); |
114 | if (body == nullptr) |
115 | { |
116 | body = new TIntermBlock(); |
117 | } |
118 | auto sequence = body->getSequence(); |
119 | sequence->insert(sequence->begin(), assignTrue); |
120 | sequence->insert(sequence->begin(), breakIf); |
121 | |
122 | newLoop = new TIntermLoop(ELoopWhile, nullptr, CreateBoolNode(true), nullptr, body); |
123 | } |
124 | |
125 | TIntermSequence replacement; |
126 | replacement.push_back(tempDeclaration); |
127 | replacement.push_back(newLoop); |
128 | |
129 | node->replaceChildNodeWithMultiple(loop, replacement); |
130 | } |
131 | return true; |
132 | } |
133 | }; |
134 | |
135 | } // anonymous namespace |
136 | |
137 | void RewriteDoWhile(TIntermNode *root, TSymbolTable *symbolTable) |
138 | { |
139 | DoWhileRewriter rewriter(symbolTable); |
140 | |
141 | root->traverse(&rewriter); |
142 | } |
143 | |
144 | } // namespace sh |
145 | |