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