blob: eac67518daf9d1f9da68a80aef02a5ebeee0679b [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 // }
98 TIntermSelection *breakIf = nullptr;
99 {
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
108 TIntermSelection *innerIf =
109 new TIntermSelection(negatedCondition, breakBlock, nullptr);
110
111 TIntermAggregate *innerIfBlock = new TIntermAggregate(EOpSequence);
112 innerIfBlock->getSequence()->push_back(innerIf);
113
114 breakIf = new TIntermSelection(createTempSymbol(boolType), innerIfBlock, nullptr);
115 }
116
117 // Assemble the replacement loops, reusing the do-while loop's body and inserting our
118 // statements at the front.
119 TIntermLoop *newLoop = nullptr;
120 {
121 TConstantUnion *trueConstant = new TConstantUnion();
122 trueConstant->setBConst(true);
123 TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
124
125 TIntermAggregate *body = nullptr;
126 if (loop->getBody() != nullptr)
127 {
128 body = loop->getBody()->getAsAggregate();
129 }
130 else
131 {
132 body = new TIntermAggregate(EOpSequence);
133 }
134 auto sequence = body->getSequence();
135 sequence->insert(sequence->begin(), assignTrue);
136 sequence->insert(sequence->begin(), breakIf);
137
138 newLoop = new TIntermLoop(ELoopWhile, nullptr, trueValue, nullptr, body);
139 }
140
141 TIntermSequence replacement;
142 replacement.push_back(tempDeclaration);
143 replacement.push_back(newLoop);
144
145 node->replaceChildNodeWithMultiple(loop, replacement);
146
147 nextTemporaryIndex();
148 }
149 return true;
150 }
151};
152
153} // anonymous namespace
154
155void RewriteDoWhile(TIntermNode *root, unsigned int *temporaryIndex)
156{
157 ASSERT(temporaryIndex != 0);
158
159 DoWhileRewriter rewriter;
160 rewriter.useTemporaryIndex(temporaryIndex);
161
162 root->traverse(&rewriter);
163}