blob: 9cc551bb79c939b5b1edcebf635d1d6ad6a63f95 [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
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010046 bool visitBlock(Visit, TIntermBlock *node) override
Corentin Wallezd4b50542015-09-28 12:19:26 -070047 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010048 // A well-formed AST can only have do-while inside TIntermBlock. By doing a prefix traversal
49 // we are able to replace the do-while in the sequence directly as the content of the
50 // do-while will be traversed later.
Corentin Wallezd4b50542015-09-28 12:19:26 -070051
52 TIntermSequence *statements = node->getSequence();
53
54 // The statements vector will have new statements inserted when we encounter a do-while,
55 // which prevents us from using a range-based for loop. Using the usual i++ works, as
56 // the (two) new statements inserted replace the statement at the current position.
57 for (size_t i = 0; i < statements->size(); i++)
58 {
59 TIntermNode *statement = (*statements)[i];
60 TIntermLoop *loop = statement->getAsLoopNode();
61
62 if (loop == nullptr || loop->getType() != ELoopDoWhile)
63 {
64 continue;
65 }
66
67 TType boolType = TType(EbtBool);
68
69 // bool temp = false;
70 TIntermAggregate *tempDeclaration = nullptr;
71 {
72 TConstantUnion *falseConstant = new TConstantUnion();
73 falseConstant->setBConst(false);
74 TIntermTyped *falseValue = new TIntermConstantUnion(falseConstant, boolType);
75
76 tempDeclaration = createTempInitDeclaration(falseValue);
77 }
78
79 // temp = true;
80 TIntermBinary *assignTrue = nullptr;
81 {
82 TConstantUnion *trueConstant = new TConstantUnion();
83 trueConstant->setBConst(true);
84 TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
85
86 assignTrue = createTempAssignment(trueValue);
87 }
88
89 // if (temp) {
90 // if (!CONDITION) {
91 // break;
92 // }
93 // }
Olli Etuaho57961272016-09-14 13:57:46 +030094 TIntermIfElse *breakIf = nullptr;
Corentin Wallezd4b50542015-09-28 12:19:26 -070095 {
96 TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr);
97
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010098 TIntermBlock *breakBlock = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -070099 breakBlock->getSequence()->push_back(breakStatement);
100
Olli Etuahoa2234302016-08-31 12:05:39 +0300101 TIntermUnary *negatedCondition =
102 new TIntermUnary(EOpLogicalNot, loop->getCondition());
Corentin Wallezd4b50542015-09-28 12:19:26 -0700103
Olli Etuaho57961272016-09-14 13:57:46 +0300104 TIntermIfElse *innerIf = new TIntermIfElse(negatedCondition, breakBlock, nullptr);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700105
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100106 TIntermBlock *innerIfBlock = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700107 innerIfBlock->getSequence()->push_back(innerIf);
108
Olli Etuaho57961272016-09-14 13:57:46 +0300109 breakIf = new TIntermIfElse(createTempSymbol(boolType), innerIfBlock, nullptr);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700110 }
111
112 // Assemble the replacement loops, reusing the do-while loop's body and inserting our
113 // statements at the front.
114 TIntermLoop *newLoop = nullptr;
115 {
116 TConstantUnion *trueConstant = new TConstantUnion();
117 trueConstant->setBConst(true);
118 TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
119
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100120 TIntermBlock *body = loop->getBody();
121 if (body == nullptr)
Corentin Wallezd4b50542015-09-28 12:19:26 -0700122 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100123 body = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700124 }
125 auto sequence = body->getSequence();
126 sequence->insert(sequence->begin(), assignTrue);
127 sequence->insert(sequence->begin(), breakIf);
128
129 newLoop = new TIntermLoop(ELoopWhile, nullptr, trueValue, nullptr, body);
130 }
131
132 TIntermSequence replacement;
133 replacement.push_back(tempDeclaration);
134 replacement.push_back(newLoop);
135
136 node->replaceChildNodeWithMultiple(loop, replacement);
137
138 nextTemporaryIndex();
139 }
140 return true;
141 }
142};
143
144} // anonymous namespace
145
146void RewriteDoWhile(TIntermNode *root, unsigned int *temporaryIndex)
147{
148 ASSERT(temporaryIndex != 0);
149
150 DoWhileRewriter rewriter;
151 rewriter.useTemporaryIndex(temporaryIndex);
152
153 root->traverse(&rewriter);
154}