1 //
2 // Copyright 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/glsl/apple/RewriteDoWhile.h"
11
12 #include "compiler/translator/Compiler.h"
13 #include "compiler/translator/StaticType.h"
14 #include "compiler/translator/tree_util/IntermNode_util.h"
15 #include "compiler/translator/tree_util/IntermTraverse.h"
16
17 namespace sh
18 {
19
20 namespace
21 {
22
23 // An AST traverser that rewrites loops of the form
24 // do {
25 // CODE;
26 // } while (CONDITION)
27 //
28 // to loops of the form
29 // bool temp = false;
30 // while (true) {
31 // if (temp) {
32 // if (!CONDITION) {
33 // break;
34 // }
35 // }
36 // temp = true;
37 // CODE;
38 // }
39 //
40 // The reason we don't use a simpler form, with for example just (temp && !CONDITION) in the
41 // while condition, is that short-circuit is often badly supported by driver shader compiler.
42 // The double if has the same effect, but forces shader compilers to behave.
43 //
44 // TODO(cwallez) when UnfoldShortCircuitIntoIf handles loops correctly, revisit this as we might
45 // be able to use while (temp || CONDITION) with temp initially set to true then run
46 // UnfoldShortCircuitIntoIf
47 class DoWhileRewriter : public TIntermTraverser
48 {
49 public:
DoWhileRewriter(TSymbolTable * symbolTable)50 DoWhileRewriter(TSymbolTable *symbolTable) : TIntermTraverser(true, false, false, symbolTable)
51 {}
52
visitBlock(Visit,TIntermBlock * node)53 bool visitBlock(Visit, TIntermBlock *node) override
54 {
55 // 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.
58
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
74 // Found a loop to change.
75 const TType *boolType = StaticType::Get<EbtBool, EbpUndefined, EvqTemporary, 1, 1>();
76 TVariable *conditionVariable = CreateTempVariable(mSymbolTable, boolType);
77
78 // bool temp = false;
79 TIntermDeclaration *tempDeclaration =
80 CreateTempInitDeclarationNode(conditionVariable, CreateBoolNode(false));
81
82 // temp = true;
83 TIntermBinary *assignTrue =
84 CreateTempAssignmentNode(conditionVariable, CreateBoolNode(true));
85
86 // if (temp) {
87 // if (!CONDITION) {
88 // break;
89 // }
90 // }
91 TIntermIfElse *breakIf = nullptr;
92 {
93 TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr);
94
95 TIntermBlock *breakBlock = new TIntermBlock();
96 breakBlock->getSequence()->push_back(breakStatement);
97
98 TIntermUnary *negatedCondition =
99 new TIntermUnary(EOpLogicalNot, loop->getCondition(), nullptr);
100
101 TIntermIfElse *innerIf = new TIntermIfElse(negatedCondition, breakBlock, nullptr);
102
103 TIntermBlock *innerIfBlock = new TIntermBlock();
104 innerIfBlock->getSequence()->push_back(innerIf);
105
106 breakIf = new TIntermIfElse(CreateTempSymbolNode(conditionVariable), innerIfBlock,
107 nullptr);
108 }
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 {
114 TIntermBlock *body = loop->getBody();
115 if (body == nullptr)
116 {
117 body = new TIntermBlock();
118 }
119 auto sequence = body->getSequence();
120 sequence->insert(sequence->begin(), assignTrue);
121 sequence->insert(sequence->begin(), breakIf);
122
123 newLoop = new TIntermLoop(ELoopWhile, nullptr, CreateBoolNode(true), nullptr, body);
124 }
125
126 TIntermSequence replacement;
127 replacement.push_back(tempDeclaration);
128 replacement.push_back(newLoop);
129
130 node->replaceChildNodeWithMultiple(loop, replacement);
131 }
132 return true;
133 }
134 };
135
136 } // anonymous namespace
137
RewriteDoWhile(TCompiler * compiler,TIntermNode * root,TSymbolTable * symbolTable)138 bool RewriteDoWhile(TCompiler *compiler, TIntermNode *root, TSymbolTable *symbolTable)
139 {
140 DoWhileRewriter rewriter(symbolTable);
141
142 root->traverse(&rewriter);
143
144 return compiler->validateAST(root);
145 }
146
147 } // namespace sh
148