blob: 7999cbf490f48c13d4a9b54c36928268e0751ec1 [file] [log] [blame]
//
// 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