• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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