blob: 7999cbf490f48c13d4a9b54c36928268e0751ec1 [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
Jamie Madill45bcc782016-11-07 13:58:48 -050014namespace sh
15{
16
Corentin Wallezd4b50542015-09-28 12:19:26 -070017namespace
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
44class DoWhileRewriter : public TIntermTraverser
45{
46 public:
47 DoWhileRewriter() : TIntermTraverser(true, false, false) {}
48
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010049 bool visitBlock(Visit, TIntermBlock *node) override
Corentin Wallezd4b50542015-09-28 12:19:26 -070050 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010051 // 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.
Corentin Wallezd4b50542015-09-28 12:19:26 -070054
55 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 for (size_t i = 0; i < statements->size(); i++)
61 {
62 TIntermNode *statement = (*statements)[i];
63 TIntermLoop *loop = statement->getAsLoopNode();
64
65 if (loop == nullptr || loop->getType() != ELoopDoWhile)
66 {
67 continue;
68 }
69
70 TType boolType = TType(EbtBool);
71
72 // bool temp = false;
Olli Etuaho13389b62016-10-16 11:48:18 +010073 TIntermDeclaration *tempDeclaration = nullptr;
Corentin Wallezd4b50542015-09-28 12:19:26 -070074 {
75 TConstantUnion *falseConstant = new TConstantUnion();
76 falseConstant->setBConst(false);
77 TIntermTyped *falseValue = new TIntermConstantUnion(falseConstant, boolType);
78
79 tempDeclaration = createTempInitDeclaration(falseValue);
80 }
81
82 // temp = true;
83 TIntermBinary *assignTrue = nullptr;
84 {
85 TConstantUnion *trueConstant = new TConstantUnion();
86 trueConstant->setBConst(true);
87 TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
88
89 assignTrue = createTempAssignment(trueValue);
90 }
91
92 // if (temp) {
93 // if (!CONDITION) {
94 // break;
95 // }
96 // }
Olli Etuaho57961272016-09-14 13:57:46 +030097 TIntermIfElse *breakIf = nullptr;
Corentin Wallezd4b50542015-09-28 12:19:26 -070098 {
99 TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr);
100
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100101 TIntermBlock *breakBlock = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700102 breakBlock->getSequence()->push_back(breakStatement);
103
Olli Etuahoa2234302016-08-31 12:05:39 +0300104 TIntermUnary *negatedCondition =
105 new TIntermUnary(EOpLogicalNot, loop->getCondition());
Corentin Wallezd4b50542015-09-28 12:19:26 -0700106
Olli Etuaho57961272016-09-14 13:57:46 +0300107 TIntermIfElse *innerIf = new TIntermIfElse(negatedCondition, breakBlock, nullptr);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700108
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100109 TIntermBlock *innerIfBlock = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700110 innerIfBlock->getSequence()->push_back(innerIf);
111
Olli Etuaho57961272016-09-14 13:57:46 +0300112 breakIf = new TIntermIfElse(createTempSymbol(boolType), innerIfBlock, nullptr);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700113 }
114
115 // Assemble the replacement loops, reusing the do-while loop's body and inserting our
116 // statements at the front.
117 TIntermLoop *newLoop = nullptr;
118 {
119 TConstantUnion *trueConstant = new TConstantUnion();
120 trueConstant->setBConst(true);
121 TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
122
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100123 TIntermBlock *body = loop->getBody();
124 if (body == nullptr)
Corentin Wallezd4b50542015-09-28 12:19:26 -0700125 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100126 body = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700127 }
128 auto sequence = body->getSequence();
129 sequence->insert(sequence->begin(), assignTrue);
130 sequence->insert(sequence->begin(), breakIf);
131
132 newLoop = new TIntermLoop(ELoopWhile, nullptr, trueValue, nullptr, body);
133 }
134
135 TIntermSequence replacement;
136 replacement.push_back(tempDeclaration);
137 replacement.push_back(newLoop);
138
139 node->replaceChildNodeWithMultiple(loop, replacement);
140
141 nextTemporaryIndex();
142 }
143 return true;
144 }
145};
146
147} // anonymous namespace
148
149void RewriteDoWhile(TIntermNode *root, unsigned int *temporaryIndex)
150{
151 ASSERT(temporaryIndex != 0);
152
153 DoWhileRewriter rewriter;
154 rewriter.useTemporaryIndex(temporaryIndex);
155
156 root->traverse(&rewriter);
157}
Jamie Madill45bcc782016-11-07 13:58:48 -0500158
159} // namespace sh