Line data Source code
1 : //
2 : // Copyright (c) 2002-2013 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 : // UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else statements.
7 : // The results are assigned to s# temporaries, which are used by the main translator instead of
8 : // the original expression.
9 : //
10 :
11 : #include "compiler/translator/UnfoldShortCircuitToIf.h"
12 :
13 : #include "compiler/translator/IntermNode.h"
14 : #include "compiler/translator/IntermNodePatternMatcher.h"
15 :
16 : namespace sh
17 : {
18 :
19 : namespace
20 : {
21 :
22 : // Traverser that unfolds one short-circuiting operation at a time.
23 0 : class UnfoldShortCircuitTraverser : public TIntermTraverser
24 : {
25 : public:
26 : UnfoldShortCircuitTraverser();
27 :
28 : bool visitBinary(Visit visit, TIntermBinary *node) override;
29 : bool visitTernary(Visit visit, TIntermTernary *node) override;
30 :
31 : void nextIteration();
32 0 : bool foundShortCircuit() const { return mFoundShortCircuit; }
33 :
34 : protected:
35 : // Marked to true once an operation that needs to be unfolded has been found.
36 : // After that, no more unfolding is performed on that traversal.
37 : bool mFoundShortCircuit;
38 :
39 : IntermNodePatternMatcher mPatternToUnfoldMatcher;
40 : };
41 :
42 0 : UnfoldShortCircuitTraverser::UnfoldShortCircuitTraverser()
43 : : TIntermTraverser(true, false, true),
44 : mFoundShortCircuit(false),
45 0 : mPatternToUnfoldMatcher(IntermNodePatternMatcher::kUnfoldedShortCircuitExpression)
46 : {
47 0 : }
48 :
49 0 : bool UnfoldShortCircuitTraverser::visitBinary(Visit visit, TIntermBinary *node)
50 : {
51 0 : if (mFoundShortCircuit)
52 0 : return false;
53 :
54 0 : if (visit != PreVisit)
55 0 : return true;
56 :
57 0 : if (!mPatternToUnfoldMatcher.match(node, getParentNode()))
58 0 : return true;
59 :
60 : // If our right node doesn't have side effects, we know we don't need to unfold this
61 : // expression: there will be no short-circuiting side effects to avoid
62 : // (note: unfolding doesn't depend on the left node -- it will always be evaluated)
63 0 : ASSERT(node->getRight()->hasSideEffects());
64 :
65 0 : mFoundShortCircuit = true;
66 :
67 0 : switch (node->getOp())
68 : {
69 : case EOpLogicalOr:
70 : {
71 : // "x || y" is equivalent to "x ? true : y", which unfolds to "bool s; if(x) s = true;
72 : // else s = y;",
73 : // and then further simplifies down to "bool s = x; if(!s) s = y;".
74 :
75 0 : TIntermSequence insertions;
76 0 : TType boolType(EbtBool, EbpUndefined, EvqTemporary);
77 :
78 0 : ASSERT(node->getLeft()->getType() == boolType);
79 0 : insertions.push_back(createTempInitDeclaration(node->getLeft()));
80 :
81 0 : TIntermBlock *assignRightBlock = new TIntermBlock();
82 0 : ASSERT(node->getRight()->getType() == boolType);
83 0 : assignRightBlock->getSequence()->push_back(createTempAssignment(node->getRight()));
84 :
85 0 : TIntermUnary *notTempSymbol = new TIntermUnary(EOpLogicalNot, createTempSymbol(boolType));
86 0 : TIntermIfElse *ifNode = new TIntermIfElse(notTempSymbol, assignRightBlock, nullptr);
87 0 : insertions.push_back(ifNode);
88 :
89 0 : insertStatementsInParentBlock(insertions);
90 :
91 0 : queueReplacement(node, createTempSymbol(boolType), OriginalNode::IS_DROPPED);
92 0 : return false;
93 : }
94 : case EOpLogicalAnd:
95 : {
96 : // "x && y" is equivalent to "x ? y : false", which unfolds to "bool s; if(x) s = y;
97 : // else s = false;",
98 : // and then further simplifies down to "bool s = x; if(s) s = y;".
99 0 : TIntermSequence insertions;
100 0 : TType boolType(EbtBool, EbpUndefined, EvqTemporary);
101 :
102 0 : ASSERT(node->getLeft()->getType() == boolType);
103 0 : insertions.push_back(createTempInitDeclaration(node->getLeft()));
104 :
105 0 : TIntermBlock *assignRightBlock = new TIntermBlock();
106 0 : ASSERT(node->getRight()->getType() == boolType);
107 0 : assignRightBlock->getSequence()->push_back(createTempAssignment(node->getRight()));
108 :
109 : TIntermIfElse *ifNode =
110 0 : new TIntermIfElse(createTempSymbol(boolType), assignRightBlock, nullptr);
111 0 : insertions.push_back(ifNode);
112 :
113 0 : insertStatementsInParentBlock(insertions);
114 :
115 0 : queueReplacement(node, createTempSymbol(boolType), OriginalNode::IS_DROPPED);
116 0 : return false;
117 : }
118 : default:
119 0 : UNREACHABLE();
120 : return true;
121 : }
122 : }
123 :
124 0 : bool UnfoldShortCircuitTraverser::visitTernary(Visit visit, TIntermTernary *node)
125 : {
126 0 : if (mFoundShortCircuit)
127 0 : return false;
128 :
129 0 : if (visit != PreVisit)
130 0 : return true;
131 :
132 0 : if (!mPatternToUnfoldMatcher.match(node))
133 0 : return true;
134 :
135 0 : mFoundShortCircuit = true;
136 :
137 : // Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;"
138 0 : TIntermSequence insertions;
139 :
140 0 : TIntermDeclaration *tempDeclaration = createTempDeclaration(node->getType());
141 0 : insertions.push_back(tempDeclaration);
142 :
143 0 : TIntermBlock *trueBlock = new TIntermBlock();
144 0 : TIntermBinary *trueAssignment = createTempAssignment(node->getTrueExpression());
145 0 : trueBlock->getSequence()->push_back(trueAssignment);
146 :
147 0 : TIntermBlock *falseBlock = new TIntermBlock();
148 0 : TIntermBinary *falseAssignment = createTempAssignment(node->getFalseExpression());
149 0 : falseBlock->getSequence()->push_back(falseAssignment);
150 :
151 : TIntermIfElse *ifNode =
152 0 : new TIntermIfElse(node->getCondition()->getAsTyped(), trueBlock, falseBlock);
153 0 : insertions.push_back(ifNode);
154 :
155 0 : insertStatementsInParentBlock(insertions);
156 :
157 0 : TIntermSymbol *ternaryResult = createTempSymbol(node->getType());
158 0 : queueReplacement(node, ternaryResult, OriginalNode::IS_DROPPED);
159 :
160 0 : return false;
161 : }
162 :
163 0 : void UnfoldShortCircuitTraverser::nextIteration()
164 : {
165 0 : mFoundShortCircuit = false;
166 0 : nextTemporaryIndex();
167 0 : }
168 :
169 : } // namespace
170 :
171 0 : void UnfoldShortCircuitToIf(TIntermNode *root, unsigned int *temporaryIndex)
172 : {
173 0 : UnfoldShortCircuitTraverser traverser;
174 0 : ASSERT(temporaryIndex != nullptr);
175 0 : traverser.useTemporaryIndex(temporaryIndex);
176 : // Unfold one operator at a time, and reset the traverser between iterations.
177 0 : do
178 : {
179 0 : traverser.nextIteration();
180 0 : root->traverse(&traverser);
181 0 : if (traverser.foundShortCircuit())
182 0 : traverser.updateTree();
183 : }
184 : while (traverser.foundShortCircuit());
185 0 : }
186 :
187 : } // namespace sh
|