RewriteDoWhile.cpp (5115B)
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/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: 50 DoWhileRewriter(TSymbolTable *symbolTable) : TIntermTraverser(true, false, false, symbolTable) 51 {} 52 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 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