blob: 80b2cee91ae1620f87fce8e89cbb016c56e83ee8 [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 Etuaho195be942017-12-04 23:40:14 +020012#include "compiler/translator/IntermNode_util.h"
Olli Etuahocccf2b02017-07-05 14:50:54 +030013#include "compiler/translator/IntermTraverse.h"
Corentin Wallezd4b50542015-09-28 12:19:26 -070014
Jamie Madill45bcc782016-11-07 13:58:48 -050015namespace sh
16{
17
Corentin Wallezd4b50542015-09-28 12:19:26 -070018namespace
19{
20
21// An AST traverser that rewrites loops of the form
22// do {
23// CODE;
24// } while (CONDITION)
25//
26// to loops of the form
27// bool temp = false;
28// while (true) {
29// if (temp) {
30// if (!CONDITION) {
31// break;
32// }
33// }
34// temp = true;
35// CODE;
36// }
37//
38// The reason we don't use a simpler form, with for example just (temp && !CONDITION) in the
39// while condition, is that short-circuit is often badly supported by driver shader compiler.
40// The double if has the same effect, but forces shader compilers to behave.
41//
42// TODO(cwallez) when UnfoldShortCircuitIntoIf handles loops correctly, revisit this as we might
43// be able to use while (temp || CONDITION) with temp initially set to true then run
44// UnfoldShortCircuitIntoIf
45class DoWhileRewriter : public TIntermTraverser
46{
47 public:
Olli Etuaho590f6232017-07-21 11:10:26 +030048 DoWhileRewriter(TSymbolTable *symbolTable) : TIntermTraverser(true, false, false, symbolTable)
49 {
50 }
Corentin Wallezd4b50542015-09-28 12:19:26 -070051
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010052 bool visitBlock(Visit, TIntermBlock *node) override
Corentin Wallezd4b50542015-09-28 12:19:26 -070053 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010054 // A well-formed AST can only have do-while inside TIntermBlock. By doing a prefix traversal
55 // we are able to replace the do-while in the sequence directly as the content of the
56 // do-while will be traversed later.
Corentin Wallezd4b50542015-09-28 12:19:26 -070057
58 TIntermSequence *statements = node->getSequence();
59
60 // The statements vector will have new statements inserted when we encounter a do-while,
61 // which prevents us from using a range-based for loop. Using the usual i++ works, as
62 // the (two) new statements inserted replace the statement at the current position.
63 for (size_t i = 0; i < statements->size(); i++)
64 {
65 TIntermNode *statement = (*statements)[i];
66 TIntermLoop *loop = statement->getAsLoopNode();
67
68 if (loop == nullptr || loop->getType() != ELoopDoWhile)
69 {
70 continue;
71 }
72
Olli Etuaho590f6232017-07-21 11:10:26 +030073 // Found a loop to change.
Olli Etuaho195be942017-12-04 23:40:14 +020074 TType boolType(EbtBool);
75 TVariable *conditionVariable = CreateTempVariable(mSymbolTable, boolType);
Corentin Wallezd4b50542015-09-28 12:19:26 -070076
77 // bool temp = false;
Olli Etuaho195be942017-12-04 23:40:14 +020078 TIntermDeclaration *tempDeclaration =
79 CreateTempInitDeclarationNode(conditionVariable, CreateBoolNode(false));
Corentin Wallezd4b50542015-09-28 12:19:26 -070080
81 // temp = true;
Olli Etuaho195be942017-12-04 23:40:14 +020082 TIntermBinary *assignTrue =
83 CreateTempAssignmentNode(conditionVariable, CreateBoolNode(true));
Corentin Wallezd4b50542015-09-28 12:19:26 -070084
85 // if (temp) {
86 // if (!CONDITION) {
87 // break;
88 // }
89 // }
Olli Etuaho57961272016-09-14 13:57:46 +030090 TIntermIfElse *breakIf = nullptr;
Corentin Wallezd4b50542015-09-28 12:19:26 -070091 {
92 TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr);
93
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010094 TIntermBlock *breakBlock = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -070095 breakBlock->getSequence()->push_back(breakStatement);
96
Olli Etuahoa2234302016-08-31 12:05:39 +030097 TIntermUnary *negatedCondition =
98 new TIntermUnary(EOpLogicalNot, loop->getCondition());
Corentin Wallezd4b50542015-09-28 12:19:26 -070099
Olli Etuaho57961272016-09-14 13:57:46 +0300100 TIntermIfElse *innerIf = new TIntermIfElse(negatedCondition, breakBlock, nullptr);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700101
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100102 TIntermBlock *innerIfBlock = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700103 innerIfBlock->getSequence()->push_back(innerIf);
104
Olli Etuaho195be942017-12-04 23:40:14 +0200105 breakIf = new TIntermIfElse(CreateTempSymbolNode(conditionVariable), innerIfBlock,
106 nullptr);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700107 }
108
109 // Assemble the replacement loops, reusing the do-while loop's body and inserting our
110 // statements at the front.
111 TIntermLoop *newLoop = nullptr;
112 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100113 TIntermBlock *body = loop->getBody();
114 if (body == nullptr)
Corentin Wallezd4b50542015-09-28 12:19:26 -0700115 {
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100116 body = new TIntermBlock();
Corentin Wallezd4b50542015-09-28 12:19:26 -0700117 }
118 auto sequence = body->getSequence();
119 sequence->insert(sequence->begin(), assignTrue);
120 sequence->insert(sequence->begin(), breakIf);
121
Olli Etuaho195be942017-12-04 23:40:14 +0200122 newLoop = new TIntermLoop(ELoopWhile, nullptr, CreateBoolNode(true), nullptr, body);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700123 }
124
125 TIntermSequence replacement;
126 replacement.push_back(tempDeclaration);
127 replacement.push_back(newLoop);
128
129 node->replaceChildNodeWithMultiple(loop, replacement);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700130 }
131 return true;
132 }
133};
134
135} // anonymous namespace
136
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300137void RewriteDoWhile(TIntermNode *root, TSymbolTable *symbolTable)
Corentin Wallezd4b50542015-09-28 12:19:26 -0700138{
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300139 DoWhileRewriter rewriter(symbolTable);
Corentin Wallezd4b50542015-09-28 12:19:26 -0700140
141 root->traverse(&rewriter);
142}
Jamie Madill45bcc782016-11-07 13:58:48 -0500143
144} // namespace sh