blob: 4a0f542b6a37a3d6bde21096bd6c03db4134ecd0 [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 Etuahob60d30f2018-01-16 12:31:06 +020012#include "compiler/translator/StaticType.h"
Olli Etuahoc26214d2018-03-16 10:43:11 +020013#include "compiler/translator/tree_util/IntermNode_util.h"
14#include "compiler/translator/tree_util/IntermTraverse.h"
Corentin Wallezd4b50542015-09-28 12:19:26 -070015
Jamie Madill45bcc782016-11-07 13:58:48 -050016namespace sh
17{
18
Corentin Wallezd4b50542015-09-28 12:19:26 -070019namespace
20{
21
22// An AST traverser that rewrites loops of the form
23// do {
24// CODE;
25// } while (CONDITION)
26//
27// to loops of the form
28// bool temp = false;
29// while (true) {
30// if (temp) {
31// if (!CONDITION) {
32// break;
33// }
34// }
35// temp = true;
36// CODE;
37// }
38//
39// The reason we don't use a simpler form, with for example just (temp && !CONDITION) in the
40// while condition, is that short-circuit is often badly supported by driver shader compiler.
41// The double if has the same effect, but forces shader compilers to behave.
42//
43// TODO(cwallez) when UnfoldShortCircuitIntoIf handles loops correctly, revisit this as we might
44// be able to use while (temp || CONDITION) with temp initially set to true then run
45// UnfoldShortCircuitIntoIf
46class DoWhileRewriter : public TIntermTraverser
47{
48 public:
Olli Etuaho590f6232017-07-21 11:10:26 +030049 DoWhileRewriter(TSymbolTable *symbolTable) : TIntermTraverser(true, false, false, symbolTable)
50 {
51 }
Corentin Wallezd4b50542015-09-28 12:19:26 -070052
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010053 bool visitBlock(Visit, TIntermBlock *node) override
Corentin Wallezd4b50542015-09-28 12:19:26 -070054 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010055 // A well-formed AST can only have do-while inside TIntermBlock. By doing a prefix traversal
56 // we are able to replace the do-while in the sequence directly as the content of the
57 // do-while will be traversed later.
Corentin Wallezd4b50542015-09-28 12:19:26 -070058
59 TIntermSequence *statements = node->getSequence();
60
61 // The statements vector will have new statements inserted when we encounter a do-while,
62 // which prevents us from using a range-based for loop. Using the usual i++ works, as
63 // the (two) new statements inserted replace the statement at the current position.
64 for (size_t i = 0; i < statements->size(); i++)
65 {
66 TIntermNode *statement = (*statements)[i];
67 TIntermLoop *loop = statement->getAsLoopNode();
68
69 if (loop == nullptr || loop->getType() != ELoopDoWhile)
70 {
71 continue;
72 }
73
Olli Etuaho590f6232017-07-21 11:10:26 +030074 // Found a loop to change.
Olli Etuahob60d30f2018-01-16 12:31:06 +020075 const TType *boolType = StaticType::Get<EbtBool, EbpUndefined, EvqTemporary, 1, 1>();
Olli Etuaho195be942017-12-04 23:40:14 +020076 TVariable *conditionVariable = CreateTempVariable(mSymbolTable, boolType);
Corentin Wallezd4b50542015-09-28 12:19:26 -070077
78 // bool temp = false;
Olli Etuaho195be942017-12-04 23:40:14 +020079 TIntermDeclaration *tempDeclaration =
80 CreateTempInitDeclarationNode(conditionVariable, CreateBoolNode(false));
Corentin Wallezd4b50542015-09-28 12:19:26 -070081
82 // temp = true;
Olli Etuaho195be942017-12-04 23:40:14 +020083 TIntermBinary *assignTrue =
84 CreateTempAssignmentNode(conditionVariable, CreateBoolNode(true));
Corentin Wallezd4b50542015-09-28 12:19:26 -070085
86 // if (temp) {
87 // if (!CONDITION) {
88 // break;
89 // }
90 // }
Olli Etuaho57961272016-09-14 13:57:46 +030091 TIntermIfElse *breakIf = nullptr;
Corentin Wallezd4b50542015-09-28 12:19:26 -070092 {
93 TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr);
94
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010095 TIntermBlock *breakBlock = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -070096 breakBlock->getSequence()->push_back(breakStatement);
97
Olli Etuahoa2234302016-08-31 12:05:39 +030098 TIntermUnary *negatedCondition =
99 new TIntermUnary(EOpLogicalNot, loop->getCondition());
Corentin Wallezd4b50542015-09-28 12:19:26 -0700100
Olli Etuaho57961272016-09-14 13:57:46 +0300101 TIntermIfElse *innerIf = new TIntermIfElse(negatedCondition, breakBlock, nullptr);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700102
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100103 TIntermBlock *innerIfBlock = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700104 innerIfBlock->getSequence()->push_back(innerIf);
105
Olli Etuaho195be942017-12-04 23:40:14 +0200106 breakIf = new TIntermIfElse(CreateTempSymbolNode(conditionVariable), innerIfBlock,
107 nullptr);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700108 }
109
110 // Assemble the replacement loops, reusing the do-while loop's body and inserting our
111 // statements at the front.
112 TIntermLoop *newLoop = nullptr;
113 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100114 TIntermBlock *body = loop->getBody();
115 if (body == nullptr)
Corentin Wallezd4b50542015-09-28 12:19:26 -0700116 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100117 body = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700118 }
119 auto sequence = body->getSequence();
120 sequence->insert(sequence->begin(), assignTrue);
121 sequence->insert(sequence->begin(), breakIf);
122
Olli Etuaho195be942017-12-04 23:40:14 +0200123 newLoop = new TIntermLoop(ELoopWhile, nullptr, CreateBoolNode(true), nullptr, body);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700124 }
125
126 TIntermSequence replacement;
127 replacement.push_back(tempDeclaration);
128 replacement.push_back(newLoop);
129
130 node->replaceChildNodeWithMultiple(loop, replacement);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700131 }
132 return true;
133 }
134};
135
136} // anonymous namespace
137
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300138void RewriteDoWhile(TIntermNode *root, TSymbolTable *symbolTable)
Corentin Wallezd4b50542015-09-28 12:19:26 -0700139{
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300140 DoWhileRewriter rewriter(symbolTable);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700141
142 root->traverse(&rewriter);
143}
Jamie Madill45bcc782016-11-07 13:58:48 -0500144
145} // namespace sh