| // |
| // 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 |