OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/aot_optimizer.h" | 5 #include "vm/aot_optimizer.h" |
6 | 6 |
7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
8 #include "vm/branch_optimizer.h" | 8 #include "vm/branch_optimizer.h" |
9 #include "vm/cha.h" | 9 #include "vm/cha.h" |
10 #include "vm/compiler.h" | 10 #include "vm/compiler.h" |
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 ic_data.NumArgsTested(), false)); | 259 ic_data.NumArgsTested(), false)); |
260 new_ic_data.SetDeoptReasons(ic_data.DeoptReasons()); | 260 new_ic_data.SetDeoptReasons(ic_data.DeoptReasons()); |
261 new_ic_data.AddReceiverCheck(cid, function); | 261 new_ic_data.AddReceiverCheck(cid, function); |
262 return new_ic_data; | 262 return new_ic_data; |
263 } | 263 } |
264 | 264 |
265 return ic_data; | 265 return ic_data; |
266 } | 266 } |
267 | 267 |
268 | 268 |
269 static BinarySmiOpInstr* AsSmiShiftLeftInstruction(Definition* d) { | |
270 BinarySmiOpInstr* instr = d->AsBinarySmiOp(); | |
271 if ((instr != NULL) && (instr->op_kind() == Token::kSHL)) { | |
272 return instr; | |
273 } | |
274 return NULL; | |
275 } | |
276 | |
277 | |
278 static bool IsPositiveOrZeroSmiConst(Definition* d) { | |
279 ConstantInstr* const_instr = d->AsConstant(); | |
280 if ((const_instr != NULL) && (const_instr->value().IsSmi())) { | |
281 return Smi::Cast(const_instr->value()).Value() >= 0; | |
282 } | |
283 return false; | |
284 } | |
285 | |
286 | |
287 void AotOptimizer::OptimizeLeftShiftBitAndSmiOp( | |
288 Definition* bit_and_instr, | |
289 Definition* left_instr, | |
290 Definition* right_instr) { | |
291 ASSERT(bit_and_instr != NULL); | |
292 ASSERT((left_instr != NULL) && (right_instr != NULL)); | |
293 | |
294 // Check for pattern, smi_shift_left must be single-use. | |
295 bool is_positive_or_zero = IsPositiveOrZeroSmiConst(left_instr); | |
296 if (!is_positive_or_zero) { | |
297 is_positive_or_zero = IsPositiveOrZeroSmiConst(right_instr); | |
298 } | |
299 if (!is_positive_or_zero) return; | |
300 | |
301 BinarySmiOpInstr* smi_shift_left = NULL; | |
302 if (bit_and_instr->InputAt(0)->IsSingleUse()) { | |
303 smi_shift_left = AsSmiShiftLeftInstruction(left_instr); | |
304 } | |
305 if ((smi_shift_left == NULL) && (bit_and_instr->InputAt(1)->IsSingleUse())) { | |
306 smi_shift_left = AsSmiShiftLeftInstruction(right_instr); | |
307 } | |
308 if (smi_shift_left == NULL) return; | |
309 | |
310 // Pattern recognized. | |
311 smi_shift_left->mark_truncating(); | |
312 ASSERT(bit_and_instr->IsBinarySmiOp() || bit_and_instr->IsBinaryMintOp()); | |
313 if (bit_and_instr->IsBinaryMintOp()) { | |
314 // Replace Mint op with Smi op. | |
315 BinarySmiOpInstr* smi_op = new(Z) BinarySmiOpInstr( | |
316 Token::kBIT_AND, | |
317 new(Z) Value(left_instr), | |
318 new(Z) Value(right_instr), | |
319 Thread::kNoDeoptId); // BIT_AND cannot deoptimize. | |
320 bit_and_instr->ReplaceWith(smi_op, current_iterator()); | |
321 } | |
322 } | |
323 | |
324 | |
325 void AotOptimizer::AppendExtractNthOutputForMerged(Definition* instr, | |
326 intptr_t index, | |
327 Representation rep, | |
328 intptr_t cid) { | |
329 ExtractNthOutputInstr* extract = | |
330 new(Z) ExtractNthOutputInstr(new(Z) Value(instr), index, rep, cid); | |
331 instr->ReplaceUsesWith(extract); | |
332 flow_graph()->InsertAfter(instr, extract, NULL, FlowGraph::kValue); | |
333 } | |
334 | |
335 | |
336 // Dart: | |
337 // var x = d % 10; | |
338 // var y = d ~/ 10; | |
339 // var z = x + y; | |
340 // | |
341 // IL: | |
342 // v4 <- %(v2, v3) | |
343 // v5 <- ~/(v2, v3) | |
344 // v6 <- +(v4, v5) | |
345 // | |
346 // IL optimized: | |
347 // v4 <- DIVMOD(v2, v3); | |
348 // v5 <- LoadIndexed(v4, 0); // ~/ result | |
349 // v6 <- LoadIndexed(v4, 1); // % result | |
350 // v7 <- +(v5, v6) | |
351 // Because of the environment it is important that merged instruction replaces | |
352 // first original instruction encountered. | |
353 void AotOptimizer::TryMergeTruncDivMod( | |
354 GrowableArray<BinarySmiOpInstr*>* merge_candidates) { | |
355 if (merge_candidates->length() < 2) { | |
356 // Need at least a TRUNCDIV and a MOD. | |
357 return; | |
358 } | |
359 for (intptr_t i = 0; i < merge_candidates->length(); i++) { | |
360 BinarySmiOpInstr* curr_instr = (*merge_candidates)[i]; | |
361 if (curr_instr == NULL) { | |
362 // Instruction was merged already. | |
363 continue; | |
364 } | |
365 ASSERT((curr_instr->op_kind() == Token::kTRUNCDIV) || | |
366 (curr_instr->op_kind() == Token::kMOD)); | |
367 // Check if there is kMOD/kTRUNDIV binop with same inputs. | |
368 const intptr_t other_kind = (curr_instr->op_kind() == Token::kTRUNCDIV) ? | |
369 Token::kMOD : Token::kTRUNCDIV; | |
370 Definition* left_def = curr_instr->left()->definition(); | |
371 Definition* right_def = curr_instr->right()->definition(); | |
372 for (intptr_t k = i + 1; k < merge_candidates->length(); k++) { | |
373 BinarySmiOpInstr* other_binop = (*merge_candidates)[k]; | |
374 // 'other_binop' can be NULL if it was already merged. | |
375 if ((other_binop != NULL) && | |
376 (other_binop->op_kind() == other_kind) && | |
377 (other_binop->left()->definition() == left_def) && | |
378 (other_binop->right()->definition() == right_def)) { | |
379 (*merge_candidates)[k] = NULL; // Clear it. | |
380 ASSERT(curr_instr->HasUses()); | |
381 AppendExtractNthOutputForMerged( | |
382 curr_instr, | |
383 MergedMathInstr::OutputIndexOf(curr_instr->op_kind()), | |
384 kTagged, kSmiCid); | |
385 ASSERT(other_binop->HasUses()); | |
386 AppendExtractNthOutputForMerged( | |
387 other_binop, | |
388 MergedMathInstr::OutputIndexOf(other_binop->op_kind()), | |
389 kTagged, kSmiCid); | |
390 | |
391 ZoneGrowableArray<Value*>* args = new(Z) ZoneGrowableArray<Value*>(2); | |
392 args->Add(new(Z) Value(curr_instr->left()->definition())); | |
393 args->Add(new(Z) Value(curr_instr->right()->definition())); | |
394 | |
395 // Replace with TruncDivMod. | |
396 MergedMathInstr* div_mod = new(Z) MergedMathInstr( | |
397 args, | |
398 curr_instr->deopt_id(), | |
399 MergedMathInstr::kTruncDivMod); | |
400 curr_instr->ReplaceWith(div_mod, current_iterator()); | |
401 other_binop->ReplaceUsesWith(div_mod); | |
402 other_binop->RemoveFromGraph(); | |
403 // Only one merge possible. Because canonicalization happens later, | |
404 // more candidates are possible. | |
405 // TODO(srdjan): Allow merging of trunc-div/mod into truncDivMod. | |
406 break; | |
407 } | |
408 } | |
409 } | |
410 } | |
411 | |
412 | |
413 // Tries to merge MathUnary operations, in this case sinus and cosinus. | |
414 void AotOptimizer::TryMergeMathUnary( | |
415 GrowableArray<MathUnaryInstr*>* merge_candidates) { | |
416 if (!FlowGraphCompiler::SupportsSinCos() || !CanUnboxDouble() || | |
417 !FLAG_merge_sin_cos) { | |
418 return; | |
419 } | |
420 if (merge_candidates->length() < 2) { | |
421 // Need at least a SIN and a COS. | |
422 return; | |
423 } | |
424 for (intptr_t i = 0; i < merge_candidates->length(); i++) { | |
425 MathUnaryInstr* curr_instr = (*merge_candidates)[i]; | |
426 if (curr_instr == NULL) { | |
427 // Instruction was merged already. | |
428 continue; | |
429 } | |
430 const intptr_t kind = curr_instr->kind(); | |
431 ASSERT((kind == MathUnaryInstr::kSin) || | |
432 (kind == MathUnaryInstr::kCos)); | |
433 // Check if there is sin/cos binop with same inputs. | |
434 const intptr_t other_kind = (kind == MathUnaryInstr::kSin) ? | |
435 MathUnaryInstr::kCos : MathUnaryInstr::kSin; | |
436 Definition* def = curr_instr->value()->definition(); | |
437 for (intptr_t k = i + 1; k < merge_candidates->length(); k++) { | |
438 MathUnaryInstr* other_op = (*merge_candidates)[k]; | |
439 // 'other_op' can be NULL if it was already merged. | |
440 if ((other_op != NULL) && (other_op->kind() == other_kind) && | |
441 (other_op->value()->definition() == def)) { | |
442 (*merge_candidates)[k] = NULL; // Clear it. | |
443 ASSERT(curr_instr->HasUses()); | |
444 AppendExtractNthOutputForMerged(curr_instr, | |
445 MergedMathInstr::OutputIndexOf(kind), | |
446 kUnboxedDouble, kDoubleCid); | |
447 ASSERT(other_op->HasUses()); | |
448 AppendExtractNthOutputForMerged( | |
449 other_op, | |
450 MergedMathInstr::OutputIndexOf(other_kind), | |
451 kUnboxedDouble, kDoubleCid); | |
452 ZoneGrowableArray<Value*>* args = new(Z) ZoneGrowableArray<Value*>(1); | |
453 args->Add(new(Z) Value(curr_instr->value()->definition())); | |
454 // Replace with SinCos. | |
455 MergedMathInstr* sin_cos = | |
456 new(Z) MergedMathInstr(args, | |
457 curr_instr->DeoptimizationTarget(), | |
458 MergedMathInstr::kSinCos); | |
459 curr_instr->ReplaceWith(sin_cos, current_iterator()); | |
460 other_op->ReplaceUsesWith(sin_cos); | |
461 other_op->RemoveFromGraph(); | |
462 // Only one merge possible. Because canonicalization happens later, | |
463 // more candidates are possible. | |
464 // TODO(srdjan): Allow merging of sin/cos into sincos. | |
465 break; | |
466 } | |
467 } | |
468 } | |
469 } | |
470 | |
471 | |
472 // Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the | |
473 // shift can be a truncating Smi shift-left and result is always Smi. | |
474 // Merging occurs only per basic-block. | |
475 void AotOptimizer::TryOptimizePatterns() { | |
476 if (!FLAG_truncating_left_shift) return; | |
477 ASSERT(current_iterator_ == NULL); | |
478 GrowableArray<BinarySmiOpInstr*> div_mod_merge; | |
479 GrowableArray<MathUnaryInstr*> sin_cos_merge; | |
480 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | |
481 !block_it.Done(); | |
482 block_it.Advance()) { | |
483 // Merging only per basic-block. | |
484 div_mod_merge.Clear(); | |
485 sin_cos_merge.Clear(); | |
486 ForwardInstructionIterator it(block_it.Current()); | |
487 current_iterator_ = ⁢ | |
488 for (; !it.Done(); it.Advance()) { | |
489 if (it.Current()->IsBinarySmiOp()) { | |
490 BinarySmiOpInstr* binop = it.Current()->AsBinarySmiOp(); | |
491 if (binop->op_kind() == Token::kBIT_AND) { | |
492 OptimizeLeftShiftBitAndSmiOp(binop, | |
493 binop->left()->definition(), | |
494 binop->right()->definition()); | |
495 } else if ((binop->op_kind() == Token::kTRUNCDIV) || | |
496 (binop->op_kind() == Token::kMOD)) { | |
497 if (binop->HasUses()) { | |
498 div_mod_merge.Add(binop); | |
499 } | |
500 } | |
501 } else if (it.Current()->IsBinaryMintOp()) { | |
502 BinaryMintOpInstr* mintop = it.Current()->AsBinaryMintOp(); | |
503 if (mintop->op_kind() == Token::kBIT_AND) { | |
504 OptimizeLeftShiftBitAndSmiOp(mintop, | |
505 mintop->left()->definition(), | |
506 mintop->right()->definition()); | |
507 } | |
508 } else if (it.Current()->IsMathUnary()) { | |
509 MathUnaryInstr* math_unary = it.Current()->AsMathUnary(); | |
510 if ((math_unary->kind() == MathUnaryInstr::kSin) || | |
511 (math_unary->kind() == MathUnaryInstr::kCos)) { | |
512 if (math_unary->HasUses()) { | |
513 sin_cos_merge.Add(math_unary); | |
514 } | |
515 } | |
516 } | |
517 } | |
518 TryMergeTruncDivMod(&div_mod_merge); | |
519 TryMergeMathUnary(&sin_cos_merge); | |
520 current_iterator_ = NULL; | |
521 } | |
522 } | |
523 | |
524 | |
525 static bool ClassIdIsOneOf(intptr_t class_id, | 269 static bool ClassIdIsOneOf(intptr_t class_id, |
526 const GrowableArray<intptr_t>& class_ids) { | 270 const GrowableArray<intptr_t>& class_ids) { |
527 for (intptr_t i = 0; i < class_ids.length(); i++) { | 271 for (intptr_t i = 0; i < class_ids.length(); i++) { |
528 ASSERT(class_ids[i] != kIllegalCid); | 272 ASSERT(class_ids[i] != kIllegalCid); |
529 if (class_ids[i] == class_id) { | 273 if (class_ids[i] == class_id) { |
530 return true; | 274 return true; |
531 } | 275 } |
532 } | 276 } |
533 return false; | 277 return false; |
534 } | 278 } |
(...skipping 1993 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2528 flow_graph_->InsertBefore(check, new_check, | 2272 flow_graph_->InsertBefore(check, new_check, |
2529 check->env(), FlowGraph::kEffect); | 2273 check->env(), FlowGraph::kEffect); |
2530 current_iterator()->RemoveCurrentFromGraph(); | 2274 current_iterator()->RemoveCurrentFromGraph(); |
2531 } | 2275 } |
2532 } | 2276 } |
2533 } | 2277 } |
2534 } | 2278 } |
2535 | 2279 |
2536 | 2280 |
2537 } // namespace dart | 2281 } // namespace dart |
OLD | NEW |