1//
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/tree_ops/RewriteDoWhile.h"
11
12#include "compiler/translator/StaticType.h"
13#include "compiler/translator/tree_util/IntermNode_util.h"
14#include "compiler/translator/tree_util/IntermTraverse.h"
15
16namespace sh
17{
18
19namespace
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:
49 DoWhileRewriter(TSymbolTable *symbolTable) : TIntermTraverser(true, false, false, symbolTable)
50 {}
51
52 bool visitBlock(Visit, TIntermBlock *node) override
53 {
54 // 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.
57
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
73 // Found a loop to change.
74 const TType *boolType = StaticType::Get<EbtBool, EbpUndefined, EvqTemporary, 1, 1>();
75 TVariable *conditionVariable = CreateTempVariable(mSymbolTable, boolType);
76
77 // bool temp = false;
78 TIntermDeclaration *tempDeclaration =
79 CreateTempInitDeclarationNode(conditionVariable, CreateBoolNode(false));
80
81 // temp = true;
82 TIntermBinary *assignTrue =
83 CreateTempAssignmentNode(conditionVariable, CreateBoolNode(true));
84
85 // if (temp) {
86 // if (!CONDITION) {
87 // break;
88 // }
89 // }
90 TIntermIfElse *breakIf = nullptr;
91 {
92 TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr);
93
94 TIntermBlock *breakBlock = new TIntermBlock();
95 breakBlock->getSequence()->push_back(breakStatement);
96
97 TIntermUnary *negatedCondition =
98 new TIntermUnary(EOpLogicalNot, loop->getCondition(), nullptr);
99
100 TIntermIfElse *innerIf = new TIntermIfElse(negatedCondition, breakBlock, nullptr);
101
102 TIntermBlock *innerIfBlock = new TIntermBlock();
103 innerIfBlock->getSequence()->push_back(innerIf);
104
105 breakIf = new TIntermIfElse(CreateTempSymbolNode(conditionVariable), innerIfBlock,
106 nullptr);
107 }
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 {
113 TIntermBlock *body = loop->getBody();
114 if (body == nullptr)
115 {
116 body = new TIntermBlock();
117 }
118 auto sequence = body->getSequence();
119 sequence->insert(sequence->begin(), assignTrue);
120 sequence->insert(sequence->begin(), breakIf);
121
122 newLoop = new TIntermLoop(ELoopWhile, nullptr, CreateBoolNode(true), nullptr, body);
123 }
124
125 TIntermSequence replacement;
126 replacement.push_back(tempDeclaration);
127 replacement.push_back(newLoop);
128
129 node->replaceChildNodeWithMultiple(loop, replacement);
130 }
131 return true;
132 }
133};
134
135} // anonymous namespace
136
137void RewriteDoWhile(TIntermNode *root, TSymbolTable *symbolTable)
138{
139 DoWhileRewriter rewriter(symbolTable);
140
141 root->traverse(&rewriter);
142}
143
144} // namespace sh
145