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