OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /** | |
6 * Instead of emitting each SSA instruction with a temporary variable | |
7 * mark instructions that can be emitted at their use-site. | |
8 * For example, in: | |
9 * t0 = 4; | |
10 * t1 = 3; | |
11 * t2 = add(t0, t1); | |
12 * t0 and t1 would be marked and the resulting code would then be: | |
13 * t2 = add(4, 3); | |
14 */ | |
15 class SsaInstructionMerger extends HBaseVisitor { | |
16 List<HInstruction> expectedInputs; | |
17 Set<HInstruction> generateAtUseSite; | |
18 | |
19 SsaInstructionMerger(this.generateAtUseSite); | |
20 | |
21 void visitGraph(HGraph graph) { | |
22 visitDominatorTree(graph); | |
23 } | |
24 | |
25 bool usedOnlyByPhis(instruction) { | |
26 for (HInstruction user in instruction.usedBy) { | |
27 if (user is !HPhi) return false; | |
28 } | |
29 return true; | |
30 } | |
31 | |
32 void visitInstruction(HInstruction instruction) { | |
33 // A code motion invariant instruction is dealt before visiting it. | |
34 assert(!instruction.isCodeMotionInvariant()); | |
35 for (HInstruction input in instruction.inputs) { | |
36 if (!generateAtUseSite.contains(input) | |
37 && !input.isCodeMotionInvariant() | |
38 && input.usedBy.length == 1) { | |
39 expectedInputs.add(input); | |
40 } | |
41 } | |
42 } | |
43 | |
44 // The codegen might use the input multiple times, so it must not be | |
45 // set generate at use site. | |
46 void visitIs(HIs instruction) {} | |
47 | |
48 // A check method must not have its input generate at use site, | |
49 // because it's using it multiple times. | |
50 void visitCheck(HCheck instruction) {} | |
51 | |
52 // A type guard should not generate its input at use site, otherwise | |
53 // they would not be alive. | |
54 void visitTypeGuard(HTypeGuard instruction) {} | |
55 | |
56 void tryGenerateAtUseSite(HInstruction instruction) { | |
57 // A type guard should never be generate at use site, otherwise we | |
58 // cannot bailout. | |
59 if (instruction is HTypeGuard) return; | |
60 | |
61 // A check should never be generate at use site, otherwise we | |
62 // cannot throw. | |
63 if (instruction is HCheck) return; | |
64 | |
65 generateAtUseSite.add(instruction); | |
66 } | |
67 | |
68 bool isBlockSinglePredecessor(HBasicBlock block) { | |
69 return block.successors.length === 1 | |
70 && block.successors[0].predecessors.length === 1; | |
71 } | |
72 | |
73 void visitBasicBlock(HBasicBlock block) { | |
74 // Compensate from not merging blocks: if the block is the | |
75 // single predecessor of its single successor, let the successor | |
76 // visit it. | |
77 if (isBlockSinglePredecessor(block)) return; | |
78 | |
79 tryMergingExpressions(block); | |
80 } | |
81 | |
82 void tryMergingExpressions(HBasicBlock block) { | |
83 // Visit each instruction of the basic block in last-to-first order. | |
84 // Keep a list of expected inputs of the current "expression" being | |
85 // merged. If instructions occur in the expected order, they are | |
86 // included in the expression. | |
87 | |
88 // The expectedInputs list holds non-trivial instructions that may | |
89 // be generated at their use site, if they occur in the correct order. | |
90 if (expectedInputs === null) expectedInputs = new List<HInstruction>(); | |
91 | |
92 // Pop instructions from expectedInputs until instruction is found. | |
93 // Return true if it is found, or false if not. | |
94 bool findInInputs(HInstruction instruction) { | |
95 while (!expectedInputs.isEmpty()) { | |
96 HInstruction nextInput = expectedInputs.removeLast(); | |
97 assert(!generateAtUseSite.contains(nextInput)); | |
98 assert(nextInput.usedBy.length == 1); | |
99 if (nextInput == instruction) { | |
100 return true; | |
101 } | |
102 } | |
103 return false; | |
104 } | |
105 | |
106 block.last.accept(this); | |
107 for (HInstruction instruction = block.last.previous; | |
108 instruction !== null; | |
109 instruction = instruction.previous) { | |
110 if (generateAtUseSite.contains(instruction)) { | |
111 continue; | |
112 } | |
113 if (instruction.isCodeMotionInvariant()) { | |
114 generateAtUseSite.add(instruction); | |
115 continue; | |
116 } | |
117 bool foundInInputs = false; | |
118 // See if the current instruction is the next non-trivial | |
119 // expected input. If not, drop the expectedInputs and | |
120 // start over. | |
121 if (findInInputs(instruction)) { | |
122 foundInInputs = true; | |
123 tryGenerateAtUseSite(instruction); | |
124 } else { | |
125 assert(expectedInputs.isEmpty()); | |
126 } | |
127 if (foundInInputs || usedOnlyByPhis(instruction)) { | |
128 // Try merging all non-trivial inputs. | |
129 instruction.accept(this); | |
130 } | |
131 } | |
132 | |
133 if (block.predecessors.length === 1 | |
134 && isBlockSinglePredecessor(block.predecessors[0])) { | |
135 tryMergingExpressions(block.predecessors[0]); | |
136 } else { | |
137 expectedInputs = null; | |
138 } | |
139 } | |
140 } | |
141 | |
142 /** | |
143 * Detect control flow arising from short-circuit logical operators, and | |
144 * prepare the program to be generated using these operators instead of | |
145 * nested ifs and boolean variables. | |
146 */ | |
147 class SsaConditionMerger extends HGraphVisitor { | |
148 Set<HInstruction> generateAtUseSite; | |
149 Map<HPhi, String> logicalOperations; | |
150 | |
151 SsaConditionMerger(this.generateAtUseSite, this.logicalOperations); | |
152 | |
153 void visitGraph(HGraph graph) { | |
154 visitDominatorTree(graph); | |
155 } | |
156 | |
157 /** | |
158 * Returns true if the given instruction is an expression that uses up all | |
159 * instructions up to the given [limit]. | |
160 * | |
161 * That is, all instructions starting after the [limit] block (at the branch | |
162 * leading to the [instruction]) down to the given [instruction] can be | |
163 * generated at use-site. | |
164 */ | |
165 bool isExpression(HInstruction instruction, HBasicBlock limit) { | |
166 if (instruction is HPhi && !logicalOperations.containsKey(instruction)) { | |
167 return false; | |
168 } | |
169 while (instruction.previous != null) { | |
170 instruction = instruction.previous; | |
171 if (!generateAtUseSite.contains(instruction)) { | |
172 return false; | |
173 } | |
174 } | |
175 HBasicBlock block = instruction.block; | |
176 if (!block.phis.isEmpty()) return false; | |
177 if (instruction is HPhi && logicalOperations.containsKey(instruction)) { | |
178 return isExpression(instruction.inputs[0], limit); | |
179 } | |
180 return block.predecessors.length == 1 && block.predecessors[0] == limit; | |
181 } | |
182 | |
183 void replaceWithLogicalOperator(HPhi phi, String type) { | |
184 if (canGenerateAtUseSite(phi)) generateAtUseSite.add(phi); | |
185 logicalOperations[phi] = type; | |
186 } | |
187 | |
188 bool canGenerateAtUseSite(HPhi phi) { | |
189 if (phi.usedBy.length != 1) return false; | |
190 assert(phi.next == null); | |
191 HInstruction use = phi.usedBy[0]; | |
192 | |
193 HInstruction current = phi.block.first; | |
194 while (current != use) { | |
195 if (!generateAtUseSite.contains(current)) return false; | |
196 if (current.next != null) { | |
197 current = current.next; | |
198 } else if (current is HPhi) { | |
199 current = current.block.first; | |
200 } else { | |
201 assert(current is HControlFlow); | |
202 if (current is !HGoto) return false; | |
203 HBasicBlock nextBlock = current.block.successors[0]; | |
204 if (!nextBlock.phis.isEmpty()) { | |
205 current = nextBlock.phis.first; | |
206 } else { | |
207 current = nextBlock.first; | |
208 } | |
209 } | |
210 } | |
211 return true; | |
212 } | |
213 | |
214 void detectLogicControlFlow(HPhi phi) { | |
215 // Check for the most common pattern for a short-circuit logic operation: | |
216 // B0 b0 = ...; if (b0) goto B1 else B2 (or: if (!b0) goto B2 else B1) | |
217 // |\ | |
218 // | B1 b1 = ...; goto B2 | |
219 // |/ | |
220 // B2 b2 = phi(b0,b1); if(b2) ... | |
221 // TODO(lrn): Also recognize ?:-flow? | |
222 | |
223 if (phi.inputs.length != 2) return; | |
224 HInstruction first = phi.inputs[0]; | |
225 HBasicBlock firstBlock = first.block; | |
226 HInstruction second = phi.inputs[1]; | |
227 HBasicBlock secondBlock = second.block; | |
228 // Check second input of phi being an expression followed by a goto. | |
229 if (second.usedBy.length != 1) return; | |
230 HInstruction secondNext = | |
231 (second is HPhi) ? secondBlock.first : second.next; | |
232 if (secondNext != secondBlock.last) return; | |
233 if (secondBlock.last is !HGoto) return; | |
234 if (secondBlock.successors[0] != phi.block) return; | |
235 if (!isExpression(second, firstBlock)) return; | |
236 // Check first input of phi being followed by a (possibly negated) | |
237 // conditional branch based on the same value. | |
238 if (firstBlock != phi.block.dominator) return; | |
239 if (firstBlock.last is !HConditionalBranch) return; | |
240 HConditionalBranch firstBranch = firstBlock.last; | |
241 // Must be used both for value and for control to avoid the second branch. | |
242 if (first.usedBy.length != 2) return; | |
243 if (firstBlock.successors[1] != phi.block) return; | |
244 HInstruction firstNext = (first is HPhi) ? firstBlock.first : first.next; | |
245 if (firstNext == firstBranch && | |
246 firstBranch.condition == first) { | |
247 replaceWithLogicalOperator(phi, "&&"); | |
248 } else if (firstNext is HNot && | |
249 firstNext.inputs[0] == first && | |
250 generateAtUseSite.contains(firstNext) && | |
251 firstNext.next == firstBlock.last && | |
252 firstBranch.condition == firstNext) { | |
253 replaceWithLogicalOperator(phi, "||"); | |
254 } else { | |
255 return; | |
256 } | |
257 // Detected as logic control flow. Mark the corresponding | |
258 // inputs as generated at use site. These will now be generated | |
259 // as part of an expression. | |
260 generateAtUseSite.add(first); | |
261 generateAtUseSite.add(firstBlock.last); | |
262 generateAtUseSite.add(second); | |
263 generateAtUseSite.add(secondBlock.last); | |
264 } | |
265 | |
266 void visitBasicBlock(HBasicBlock block) { | |
267 if (!block.phis.isEmpty() && | |
268 block.phis.first == block.phis.last) { | |
269 detectLogicControlFlow(block.phis.first); | |
270 } | |
271 } | |
272 } | |
273 | |
274 // Precedence information for JavaScript operators. | |
275 class JSPrecedence { | |
276 // Used as precedence for something that's not even an expression. | |
277 static final int STATEMENT_PRECEDENCE = 0; | |
278 // Precedences of JS operators. | |
279 static final int EXPRESSION_PRECEDENCE = 1; | |
280 static final int ASSIGNMENT_PRECEDENCE = 2; | |
281 static final int CONDITIONAL_PRECEDENCE = 3; | |
282 static final int LOGICAL_OR_PRECEDENCE = 4; | |
283 static final int LOGICAL_AND_PRECEDENCE = 5; | |
284 static final int BITWISE_OR_PRECEDENCE = 6; | |
285 static final int BITWISE_XOR_PRECEDENCE = 7; | |
286 static final int BITWISE_AND_PRECEDENCE = 8; | |
287 static final int EQUALITY_PRECEDENCE = 9; | |
288 static final int RELATIONAL_PRECEDENCE = 10; | |
289 static final int SHIFT_PRECEDENCE = 11; | |
290 static final int ADDITIVE_PRECEDENCE = 12; | |
291 static final int MULTIPLICATIVE_PRECEDENCE = 13; | |
292 static final int PREFIX_PRECEDENCE = 14; | |
293 static final int POSTFIX_PRECEDENCE = 15; | |
294 static final int CALL_PRECEDENCE = 16; | |
295 // We never use "new MemberExpression" without arguments, so we can | |
296 // combine CallExpression and MemberExpression without ambiguity. | |
297 static final int MEMBER_PRECEDENCE = CALL_PRECEDENCE; | |
298 static final int PRIMARY_PRECEDENCE = 17; | |
299 | |
300 // The operators that an occur in HBinaryOp. | |
301 static final Map<String, JSBinaryOperatorPrecedence> binary = const { | |
302 "||" : const JSBinaryOperatorPrecedence(LOGICAL_OR_PRECEDENCE, | |
303 LOGICAL_AND_PRECEDENCE), | |
304 "&&" : const JSBinaryOperatorPrecedence(LOGICAL_AND_PRECEDENCE, | |
305 BITWISE_OR_PRECEDENCE), | |
306 "|" : const JSBinaryOperatorPrecedence(BITWISE_OR_PRECEDENCE, | |
307 BITWISE_XOR_PRECEDENCE), | |
308 "^" : const JSBinaryOperatorPrecedence(BITWISE_XOR_PRECEDENCE, | |
309 BITWISE_AND_PRECEDENCE), | |
310 "&" : const JSBinaryOperatorPrecedence(BITWISE_AND_PRECEDENCE, | |
311 EQUALITY_PRECEDENCE), | |
312 "==" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE, | |
313 RELATIONAL_PRECEDENCE), | |
314 "!=" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE, | |
315 RELATIONAL_PRECEDENCE), | |
316 "===" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE, | |
317 RELATIONAL_PRECEDENCE), | |
318 "!==" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE, | |
319 RELATIONAL_PRECEDENCE), | |
320 "<" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE, | |
321 SHIFT_PRECEDENCE), | |
322 ">" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE, | |
323 SHIFT_PRECEDENCE), | |
324 "<=" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE, | |
325 SHIFT_PRECEDENCE), | |
326 ">=" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE, | |
327 SHIFT_PRECEDENCE), | |
328 "<<" : const JSBinaryOperatorPrecedence(SHIFT_PRECEDENCE, | |
329 ADDITIVE_PRECEDENCE), | |
330 ">>" : const JSBinaryOperatorPrecedence(SHIFT_PRECEDENCE, | |
331 ADDITIVE_PRECEDENCE), | |
332 ">>>" : const JSBinaryOperatorPrecedence(SHIFT_PRECEDENCE, | |
333 ADDITIVE_PRECEDENCE), | |
334 "+" : const JSBinaryOperatorPrecedence(ADDITIVE_PRECEDENCE, | |
335 MULTIPLICATIVE_PRECEDENCE), | |
336 "-" : const JSBinaryOperatorPrecedence(ADDITIVE_PRECEDENCE, | |
337 MULTIPLICATIVE_PRECEDENCE), | |
338 "*" : const JSBinaryOperatorPrecedence(MULTIPLICATIVE_PRECEDENCE, | |
339 PREFIX_PRECEDENCE), | |
340 "/" : const JSBinaryOperatorPrecedence(MULTIPLICATIVE_PRECEDENCE, | |
341 PREFIX_PRECEDENCE), | |
342 "%" : const JSBinaryOperatorPrecedence(MULTIPLICATIVE_PRECEDENCE, | |
343 PREFIX_PRECEDENCE), | |
344 }; | |
345 } | |
346 | |
347 class JSBinaryOperatorPrecedence { | |
348 final int left; | |
349 final int right; | |
350 const JSBinaryOperatorPrecedence(this.left, this.right); | |
351 // All binary operators (excluding assignment) are left associative. | |
352 int get precedence() => left; | |
353 } | |
OLD | NEW |