blob: dc3fb7a74ebb9633eac4f2503b4acf830816d0cf [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
Olli Etuahocccf2b02017-07-05 14:50:54 +030012#include "compiler/translator/IntermTraverse.h"
Corentin Wallezd4b50542015-09-28 12:19:26 -070013
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:
Olli Etuaho590f6232017-07-21 11:10:26 +030047 DoWhileRewriter(TSymbolTable *symbolTable) : TIntermTraverser(true, false, false, symbolTable)
48 {
49 }
Corentin Wallezd4b50542015-09-28 12:19:26 -070050
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010051 bool visitBlock(Visit, TIntermBlock *node) override
Corentin Wallezd4b50542015-09-28 12:19:26 -070052 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010053 // A well-formed AST can only have do-while inside TIntermBlock. By doing a prefix traversal
54 // we are able to replace the do-while in the sequence directly as the content of the
55 // do-while will be traversed later.
Corentin Wallezd4b50542015-09-28 12:19:26 -070056
57 TIntermSequence *statements = node->getSequence();
58
59 // The statements vector will have new statements inserted when we encounter a do-while,
60 // which prevents us from using a range-based for loop. Using the usual i++ works, as
61 // the (two) new statements inserted replace the statement at the current position.
62 for (size_t i = 0; i < statements->size(); i++)
63 {
64 TIntermNode *statement = (*statements)[i];
65 TIntermLoop *loop = statement->getAsLoopNode();
66
67 if (loop == nullptr || loop->getType() != ELoopDoWhile)
68 {
69 continue;
70 }
71
Olli Etuaho590f6232017-07-21 11:10:26 +030072 // Found a loop to change.
73 nextTemporaryId();
74
Corentin Wallezd4b50542015-09-28 12:19:26 -070075 TType boolType = TType(EbtBool);
76
77 // bool temp = false;
Olli Etuaho13389b62016-10-16 11:48:18 +010078 TIntermDeclaration *tempDeclaration = nullptr;
Corentin Wallezd4b50542015-09-28 12:19:26 -070079 {
80 TConstantUnion *falseConstant = new TConstantUnion();
81 falseConstant->setBConst(false);
82 TIntermTyped *falseValue = new TIntermConstantUnion(falseConstant, boolType);
83
84 tempDeclaration = createTempInitDeclaration(falseValue);
85 }
86
87 // temp = true;
88 TIntermBinary *assignTrue = nullptr;
89 {
90 TConstantUnion *trueConstant = new TConstantUnion();
91 trueConstant->setBConst(true);
92 TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
93
94 assignTrue = createTempAssignment(trueValue);
95 }
96
97 // if (temp) {
98 // if (!CONDITION) {
99 // break;
100 // }
101 // }
Olli Etuaho57961272016-09-14 13:57:46 +0300102 TIntermIfElse *breakIf = nullptr;
Corentin Wallezd4b50542015-09-28 12:19:26 -0700103 {
104 TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr);
105
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100106 TIntermBlock *breakBlock = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700107 breakBlock->getSequence()->push_back(breakStatement);
108
Olli Etuahoa2234302016-08-31 12:05:39 +0300109 TIntermUnary *negatedCondition =
110 new TIntermUnary(EOpLogicalNot, loop->getCondition());
Corentin Wallezd4b50542015-09-28 12:19:26 -0700111
Olli Etuaho57961272016-09-14 13:57:46 +0300112 TIntermIfElse *innerIf = new TIntermIfElse(negatedCondition, breakBlock, nullptr);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700113
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100114 TIntermBlock *innerIfBlock = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700115 innerIfBlock->getSequence()->push_back(innerIf);
116
Olli Etuaho57961272016-09-14 13:57:46 +0300117 breakIf = new TIntermIfElse(createTempSymbol(boolType), innerIfBlock, nullptr);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700118 }
119
120 // Assemble the replacement loops, reusing the do-while loop's body and inserting our
121 // statements at the front.
122 TIntermLoop *newLoop = nullptr;
123 {
124 TConstantUnion *trueConstant = new TConstantUnion();
125 trueConstant->setBConst(true);
126 TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
127
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100128 TIntermBlock *body = loop->getBody();
129 if (body == nullptr)
Corentin Wallezd4b50542015-09-28 12:19:26 -0700130 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100131 body = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700132 }
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);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700145 }
146 return true;
147 }
148};
149
150} // anonymous namespace
151
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300152void RewriteDoWhile(TIntermNode *root, TSymbolTable *symbolTable)
Corentin Wallezd4b50542015-09-28 12:19:26 -0700153{
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300154 DoWhileRewriter rewriter(symbolTable);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700155
156 root->traverse(&rewriter);
157}
Jamie Madill45bcc782016-11-07 13:58:48 -0500158
159} // namespace sh