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