summaryrefslogtreecommitdiffstats
path: root/gfx/angle/src/compiler/translator/RewriteDoWhile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/src/compiler/translator/RewriteDoWhile.cpp')
-rwxr-xr-xgfx/angle/src/compiler/translator/RewriteDoWhile.cpp159
1 files changed, 159 insertions, 0 deletions
diff --git a/gfx/angle/src/compiler/translator/RewriteDoWhile.cpp b/gfx/angle/src/compiler/translator/RewriteDoWhile.cpp
new file mode 100755
index 000000000..7999cbf49
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RewriteDoWhile.cpp
@@ -0,0 +1,159 @@
+//
+// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// RewriteDoWhile.cpp: rewrites do-while loops using another equivalent
+// construct.
+
+#include "compiler/translator/RewriteDoWhile.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// An AST traverser that rewrites loops of the form
+// do {
+// CODE;
+// } while (CONDITION)
+//
+// to loops of the form
+// bool temp = false;
+// while (true) {
+// if (temp) {
+// if (!CONDITION) {
+// break;
+// }
+// }
+// temp = true;
+// CODE;
+// }
+//
+// The reason we don't use a simpler form, with for example just (temp && !CONDITION) in the
+// while condition, is that short-circuit is often badly supported by driver shader compiler.
+// The double if has the same effect, but forces shader compilers to behave.
+//
+// TODO(cwallez) when UnfoldShortCircuitIntoIf handles loops correctly, revisit this as we might
+// be able to use while (temp || CONDITION) with temp initially set to true then run
+// UnfoldShortCircuitIntoIf
+class DoWhileRewriter : public TIntermTraverser
+{
+ public:
+ DoWhileRewriter() : TIntermTraverser(true, false, false) {}
+
+ bool visitBlock(Visit, TIntermBlock *node) override
+ {
+ // A well-formed AST can only have do-while inside TIntermBlock. By doing a prefix traversal
+ // we are able to replace the do-while in the sequence directly as the content of the
+ // do-while will be traversed later.
+
+ TIntermSequence *statements = node->getSequence();
+
+ // The statements vector will have new statements inserted when we encounter a do-while,
+ // which prevents us from using a range-based for loop. Using the usual i++ works, as
+ // the (two) new statements inserted replace the statement at the current position.
+ for (size_t i = 0; i < statements->size(); i++)
+ {
+ TIntermNode *statement = (*statements)[i];
+ TIntermLoop *loop = statement->getAsLoopNode();
+
+ if (loop == nullptr || loop->getType() != ELoopDoWhile)
+ {
+ continue;
+ }
+
+ TType boolType = TType(EbtBool);
+
+ // bool temp = false;
+ TIntermDeclaration *tempDeclaration = nullptr;
+ {
+ TConstantUnion *falseConstant = new TConstantUnion();
+ falseConstant->setBConst(false);
+ TIntermTyped *falseValue = new TIntermConstantUnion(falseConstant, boolType);
+
+ tempDeclaration = createTempInitDeclaration(falseValue);
+ }
+
+ // temp = true;
+ TIntermBinary *assignTrue = nullptr;
+ {
+ TConstantUnion *trueConstant = new TConstantUnion();
+ trueConstant->setBConst(true);
+ TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
+
+ assignTrue = createTempAssignment(trueValue);
+ }
+
+ // if (temp) {
+ // if (!CONDITION) {
+ // break;
+ // }
+ // }
+ TIntermIfElse *breakIf = nullptr;
+ {
+ TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr);
+
+ TIntermBlock *breakBlock = new TIntermBlock();
+ breakBlock->getSequence()->push_back(breakStatement);
+
+ TIntermUnary *negatedCondition =
+ new TIntermUnary(EOpLogicalNot, loop->getCondition());
+
+ TIntermIfElse *innerIf = new TIntermIfElse(negatedCondition, breakBlock, nullptr);
+
+ TIntermBlock *innerIfBlock = new TIntermBlock();
+ innerIfBlock->getSequence()->push_back(innerIf);
+
+ breakIf = new TIntermIfElse(createTempSymbol(boolType), innerIfBlock, nullptr);
+ }
+
+ // Assemble the replacement loops, reusing the do-while loop's body and inserting our
+ // statements at the front.
+ TIntermLoop *newLoop = nullptr;
+ {
+ TConstantUnion *trueConstant = new TConstantUnion();
+ trueConstant->setBConst(true);
+ TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
+
+ TIntermBlock *body = loop->getBody();
+ if (body == nullptr)
+ {
+ body = new TIntermBlock();
+ }
+ auto sequence = body->getSequence();
+ sequence->insert(sequence->begin(), assignTrue);
+ sequence->insert(sequence->begin(), breakIf);
+
+ newLoop = new TIntermLoop(ELoopWhile, nullptr, trueValue, nullptr, body);
+ }
+
+ TIntermSequence replacement;
+ replacement.push_back(tempDeclaration);
+ replacement.push_back(newLoop);
+
+ node->replaceChildNodeWithMultiple(loop, replacement);
+
+ nextTemporaryIndex();
+ }
+ return true;
+ }
+};
+
+} // anonymous namespace
+
+void RewriteDoWhile(TIntermNode *root, unsigned int *temporaryIndex)
+{
+ ASSERT(temporaryIndex != 0);
+
+ DoWhileRewriter rewriter;
+ rewriter.useTemporaryIndex(temporaryIndex);
+
+ root->traverse(&rewriter);
+}
+
+} // namespace sh