Edit

kc3-lang/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2016-12-12 14:42:19
    Hash : d7b1ab58
    Message : Fix up translator style. Using git cl format. BUG=angleproject:650 Change-Id: I7d3f98d2b0dcfb0a8de6c35327db74e55c28d761 Reviewed-on: https://chromium-review.googlesource.com/419059 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>

  • src/compiler/translator/UnfoldShortCircuitToIf.cpp
  • //
    // Copyright (c) 2002-2013 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.
    //
    // UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else
    // statements.
    // The results are assigned to s# temporaries, which are used by the main translator instead of
    // the original expression.
    //
    
    #include "compiler/translator/UnfoldShortCircuitToIf.h"
    
    #include "compiler/translator/IntermNode.h"
    #include "compiler/translator/IntermNodePatternMatcher.h"
    
    namespace sh
    {
    
    namespace
    {
    
    // Traverser that unfolds one short-circuiting operation at a time.
    class UnfoldShortCircuitTraverser : public TIntermTraverser
    {
      public:
        UnfoldShortCircuitTraverser();
    
        bool visitBinary(Visit visit, TIntermBinary *node) override;
        bool visitTernary(Visit visit, TIntermTernary *node) override;
    
        void nextIteration();
        bool foundShortCircuit() const { return mFoundShortCircuit; }
    
      protected:
        // Marked to true once an operation that needs to be unfolded has been found.
        // After that, no more unfolding is performed on that traversal.
        bool mFoundShortCircuit;
    
        IntermNodePatternMatcher mPatternToUnfoldMatcher;
    };
    
    UnfoldShortCircuitTraverser::UnfoldShortCircuitTraverser()
        : TIntermTraverser(true, false, true),
          mFoundShortCircuit(false),
          mPatternToUnfoldMatcher(IntermNodePatternMatcher::kUnfoldedShortCircuitExpression)
    {
    }
    
    bool UnfoldShortCircuitTraverser::visitBinary(Visit visit, TIntermBinary *node)
    {
        if (mFoundShortCircuit)
            return false;
    
        if (visit != PreVisit)
            return true;
    
        if (!mPatternToUnfoldMatcher.match(node, getParentNode()))
            return true;
    
        // If our right node doesn't have side effects, we know we don't need to unfold this
        // expression: there will be no short-circuiting side effects to avoid
        // (note: unfolding doesn't depend on the left node -- it will always be evaluated)
        ASSERT(node->getRight()->hasSideEffects());
    
        mFoundShortCircuit = true;
    
        switch (node->getOp())
        {
            case EOpLogicalOr:
            {
                // "x || y" is equivalent to "x ? true : y", which unfolds to "bool s; if(x) s = true;
                // else s = y;",
                // and then further simplifies down to "bool s = x; if(!s) s = y;".
    
                TIntermSequence insertions;
                TType boolType(EbtBool, EbpUndefined, EvqTemporary);
    
                ASSERT(node->getLeft()->getType() == boolType);
                insertions.push_back(createTempInitDeclaration(node->getLeft()));
    
                TIntermBlock *assignRightBlock = new TIntermBlock();
                ASSERT(node->getRight()->getType() == boolType);
                assignRightBlock->getSequence()->push_back(createTempAssignment(node->getRight()));
    
                TIntermUnary *notTempSymbol =
                    new TIntermUnary(EOpLogicalNot, createTempSymbol(boolType));
                TIntermIfElse *ifNode = new TIntermIfElse(notTempSymbol, assignRightBlock, nullptr);
                insertions.push_back(ifNode);
    
                insertStatementsInParentBlock(insertions);
    
                queueReplacement(node, createTempSymbol(boolType), OriginalNode::IS_DROPPED);
                return false;
            }
            case EOpLogicalAnd:
            {
                // "x && y" is equivalent to "x ? y : false", which unfolds to "bool s; if(x) s = y;
                // else s = false;",
                // and then further simplifies down to "bool s = x; if(s) s = y;".
                TIntermSequence insertions;
                TType boolType(EbtBool, EbpUndefined, EvqTemporary);
    
                ASSERT(node->getLeft()->getType() == boolType);
                insertions.push_back(createTempInitDeclaration(node->getLeft()));
    
                TIntermBlock *assignRightBlock = new TIntermBlock();
                ASSERT(node->getRight()->getType() == boolType);
                assignRightBlock->getSequence()->push_back(createTempAssignment(node->getRight()));
    
                TIntermIfElse *ifNode =
                    new TIntermIfElse(createTempSymbol(boolType), assignRightBlock, nullptr);
                insertions.push_back(ifNode);
    
                insertStatementsInParentBlock(insertions);
    
                queueReplacement(node, createTempSymbol(boolType), OriginalNode::IS_DROPPED);
                return false;
            }
            default:
                UNREACHABLE();
                return true;
        }
    }
    
    bool UnfoldShortCircuitTraverser::visitTernary(Visit visit, TIntermTernary *node)
    {
        if (mFoundShortCircuit)
            return false;
    
        if (visit != PreVisit)
            return true;
    
        if (!mPatternToUnfoldMatcher.match(node))
            return true;
    
        mFoundShortCircuit = true;
    
        // Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;"
        TIntermSequence insertions;
    
        TIntermDeclaration *tempDeclaration = createTempDeclaration(node->getType());
        insertions.push_back(tempDeclaration);
    
        TIntermBlock *trueBlock       = new TIntermBlock();
        TIntermBinary *trueAssignment = createTempAssignment(node->getTrueExpression());
        trueBlock->getSequence()->push_back(trueAssignment);
    
        TIntermBlock *falseBlock       = new TIntermBlock();
        TIntermBinary *falseAssignment = createTempAssignment(node->getFalseExpression());
        falseBlock->getSequence()->push_back(falseAssignment);
    
        TIntermIfElse *ifNode =
            new TIntermIfElse(node->getCondition()->getAsTyped(), trueBlock, falseBlock);
        insertions.push_back(ifNode);
    
        insertStatementsInParentBlock(insertions);
    
        TIntermSymbol *ternaryResult = createTempSymbol(node->getType());
        queueReplacement(node, ternaryResult, OriginalNode::IS_DROPPED);
    
        return false;
    }
    
    void UnfoldShortCircuitTraverser::nextIteration()
    {
        mFoundShortCircuit = false;
        nextTemporaryIndex();
    }
    
    }  // namespace
    
    void UnfoldShortCircuitToIf(TIntermNode *root, unsigned int *temporaryIndex)
    {
        UnfoldShortCircuitTraverser traverser;
        ASSERT(temporaryIndex != nullptr);
        traverser.useTemporaryIndex(temporaryIndex);
        // Unfold one operator at a time, and reset the traverser between iterations.
        do
        {
            traverser.nextIteration();
            root->traverse(&traverser);
            if (traverser.foundShortCircuit())
                traverser.updateTree();
        } while (traverser.foundShortCircuit());
    }
    
    }  // namespace sh