| 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/jit_optimizer.h" | 5 #include "vm/jit_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 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 254 const bool complete = false; | 254 const bool complete = false; |
| 255 PolymorphicInstanceCallInstr* specialized = | 255 PolymorphicInstanceCallInstr* specialized = |
| 256 new(Z) PolymorphicInstanceCallInstr(call->instance_call(), | 256 new(Z) PolymorphicInstanceCallInstr(call->instance_call(), |
| 257 ic_data, | 257 ic_data, |
| 258 with_checks, | 258 with_checks, |
| 259 complete); | 259 complete); |
| 260 call->ReplaceWith(specialized, current_iterator()); | 260 call->ReplaceWith(specialized, current_iterator()); |
| 261 } | 261 } |
| 262 | 262 |
| 263 | 263 |
| 264 static BinarySmiOpInstr* AsSmiShiftLeftInstruction(Definition* d) { | |
| 265 BinarySmiOpInstr* instr = d->AsBinarySmiOp(); | |
| 266 if ((instr != NULL) && (instr->op_kind() == Token::kSHL)) { | |
| 267 return instr; | |
| 268 } | |
| 269 return NULL; | |
| 270 } | |
| 271 | |
| 272 | |
| 273 static bool IsPositiveOrZeroSmiConst(Definition* d) { | |
| 274 ConstantInstr* const_instr = d->AsConstant(); | |
| 275 if ((const_instr != NULL) && (const_instr->value().IsSmi())) { | |
| 276 return Smi::Cast(const_instr->value()).Value() >= 0; | |
| 277 } | |
| 278 return false; | |
| 279 } | |
| 280 | |
| 281 | |
| 282 void JitOptimizer::OptimizeLeftShiftBitAndSmiOp( | |
| 283 Definition* bit_and_instr, | |
| 284 Definition* left_instr, | |
| 285 Definition* right_instr) { | |
| 286 ASSERT(bit_and_instr != NULL); | |
| 287 ASSERT((left_instr != NULL) && (right_instr != NULL)); | |
| 288 | |
| 289 // Check for pattern, smi_shift_left must be single-use. | |
| 290 bool is_positive_or_zero = IsPositiveOrZeroSmiConst(left_instr); | |
| 291 if (!is_positive_or_zero) { | |
| 292 is_positive_or_zero = IsPositiveOrZeroSmiConst(right_instr); | |
| 293 } | |
| 294 if (!is_positive_or_zero) return; | |
| 295 | |
| 296 BinarySmiOpInstr* smi_shift_left = NULL; | |
| 297 if (bit_and_instr->InputAt(0)->IsSingleUse()) { | |
| 298 smi_shift_left = AsSmiShiftLeftInstruction(left_instr); | |
| 299 } | |
| 300 if ((smi_shift_left == NULL) && (bit_and_instr->InputAt(1)->IsSingleUse())) { | |
| 301 smi_shift_left = AsSmiShiftLeftInstruction(right_instr); | |
| 302 } | |
| 303 if (smi_shift_left == NULL) return; | |
| 304 | |
| 305 // Pattern recognized. | |
| 306 smi_shift_left->mark_truncating(); | |
| 307 ASSERT(bit_and_instr->IsBinarySmiOp() || bit_and_instr->IsBinaryMintOp()); | |
| 308 if (bit_and_instr->IsBinaryMintOp()) { | |
| 309 // Replace Mint op with Smi op. | |
| 310 BinarySmiOpInstr* smi_op = new(Z) BinarySmiOpInstr( | |
| 311 Token::kBIT_AND, | |
| 312 new(Z) Value(left_instr), | |
| 313 new(Z) Value(right_instr), | |
| 314 Thread::kNoDeoptId); // BIT_AND cannot deoptimize. | |
| 315 bit_and_instr->ReplaceWith(smi_op, current_iterator()); | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 | |
| 320 void JitOptimizer::AppendExtractNthOutputForMerged(Definition* instr, | |
| 321 intptr_t index, | |
| 322 Representation rep, | |
| 323 intptr_t cid) { | |
| 324 ExtractNthOutputInstr* extract = | |
| 325 new(Z) ExtractNthOutputInstr(new(Z) Value(instr), index, rep, cid); | |
| 326 instr->ReplaceUsesWith(extract); | |
| 327 flow_graph()->InsertAfter(instr, extract, NULL, FlowGraph::kValue); | |
| 328 } | |
| 329 | |
| 330 | |
| 331 // Dart: | |
| 332 // var x = d % 10; | |
| 333 // var y = d ~/ 10; | |
| 334 // var z = x + y; | |
| 335 // | |
| 336 // IL: | |
| 337 // v4 <- %(v2, v3) | |
| 338 // v5 <- ~/(v2, v3) | |
| 339 // v6 <- +(v4, v5) | |
| 340 // | |
| 341 // IL optimized: | |
| 342 // v4 <- DIVMOD(v2, v3); | |
| 343 // v5 <- LoadIndexed(v4, 0); // ~/ result | |
| 344 // v6 <- LoadIndexed(v4, 1); // % result | |
| 345 // v7 <- +(v5, v6) | |
| 346 // Because of the environment it is important that merged instruction replaces | |
| 347 // first original instruction encountered. | |
| 348 void JitOptimizer::TryMergeTruncDivMod( | |
| 349 GrowableArray<BinarySmiOpInstr*>* merge_candidates) { | |
| 350 if (merge_candidates->length() < 2) { | |
| 351 // Need at least a TRUNCDIV and a MOD. | |
| 352 return; | |
| 353 } | |
| 354 for (intptr_t i = 0; i < merge_candidates->length(); i++) { | |
| 355 BinarySmiOpInstr* curr_instr = (*merge_candidates)[i]; | |
| 356 if (curr_instr == NULL) { | |
| 357 // Instruction was merged already. | |
| 358 continue; | |
| 359 } | |
| 360 ASSERT((curr_instr->op_kind() == Token::kTRUNCDIV) || | |
| 361 (curr_instr->op_kind() == Token::kMOD)); | |
| 362 // Check if there is kMOD/kTRUNDIV binop with same inputs. | |
| 363 const intptr_t other_kind = (curr_instr->op_kind() == Token::kTRUNCDIV) ? | |
| 364 Token::kMOD : Token::kTRUNCDIV; | |
| 365 Definition* left_def = curr_instr->left()->definition(); | |
| 366 Definition* right_def = curr_instr->right()->definition(); | |
| 367 for (intptr_t k = i + 1; k < merge_candidates->length(); k++) { | |
| 368 BinarySmiOpInstr* other_binop = (*merge_candidates)[k]; | |
| 369 // 'other_binop' can be NULL if it was already merged. | |
| 370 if ((other_binop != NULL) && | |
| 371 (other_binop->op_kind() == other_kind) && | |
| 372 (other_binop->left()->definition() == left_def) && | |
| 373 (other_binop->right()->definition() == right_def)) { | |
| 374 (*merge_candidates)[k] = NULL; // Clear it. | |
| 375 ASSERT(curr_instr->HasUses()); | |
| 376 AppendExtractNthOutputForMerged( | |
| 377 curr_instr, | |
| 378 MergedMathInstr::OutputIndexOf(curr_instr->op_kind()), | |
| 379 kTagged, kSmiCid); | |
| 380 ASSERT(other_binop->HasUses()); | |
| 381 AppendExtractNthOutputForMerged( | |
| 382 other_binop, | |
| 383 MergedMathInstr::OutputIndexOf(other_binop->op_kind()), | |
| 384 kTagged, kSmiCid); | |
| 385 | |
| 386 ZoneGrowableArray<Value*>* args = new(Z) ZoneGrowableArray<Value*>(2); | |
| 387 args->Add(new(Z) Value(curr_instr->left()->definition())); | |
| 388 args->Add(new(Z) Value(curr_instr->right()->definition())); | |
| 389 | |
| 390 // Replace with TruncDivMod. | |
| 391 MergedMathInstr* div_mod = new(Z) MergedMathInstr( | |
| 392 args, | |
| 393 curr_instr->deopt_id(), | |
| 394 MergedMathInstr::kTruncDivMod); | |
| 395 curr_instr->ReplaceWith(div_mod, current_iterator()); | |
| 396 other_binop->ReplaceUsesWith(div_mod); | |
| 397 other_binop->RemoveFromGraph(); | |
| 398 // Only one merge possible. Because canonicalization happens later, | |
| 399 // more candidates are possible. | |
| 400 // TODO(srdjan): Allow merging of trunc-div/mod into truncDivMod. | |
| 401 break; | |
| 402 } | |
| 403 } | |
| 404 } | |
| 405 } | |
| 406 | |
| 407 | |
| 408 // Tries to merge MathUnary operations, in this case sinus and cosinus. | |
| 409 void JitOptimizer::TryMergeMathUnary( | |
| 410 GrowableArray<MathUnaryInstr*>* merge_candidates) { | |
| 411 if (!FlowGraphCompiler::SupportsSinCos() || !CanUnboxDouble() || | |
| 412 !FLAG_merge_sin_cos) { | |
| 413 return; | |
| 414 } | |
| 415 if (merge_candidates->length() < 2) { | |
| 416 // Need at least a SIN and a COS. | |
| 417 return; | |
| 418 } | |
| 419 for (intptr_t i = 0; i < merge_candidates->length(); i++) { | |
| 420 MathUnaryInstr* curr_instr = (*merge_candidates)[i]; | |
| 421 if (curr_instr == NULL) { | |
| 422 // Instruction was merged already. | |
| 423 continue; | |
| 424 } | |
| 425 const intptr_t kind = curr_instr->kind(); | |
| 426 ASSERT((kind == MathUnaryInstr::kSin) || | |
| 427 (kind == MathUnaryInstr::kCos)); | |
| 428 // Check if there is sin/cos binop with same inputs. | |
| 429 const intptr_t other_kind = (kind == MathUnaryInstr::kSin) ? | |
| 430 MathUnaryInstr::kCos : MathUnaryInstr::kSin; | |
| 431 Definition* def = curr_instr->value()->definition(); | |
| 432 for (intptr_t k = i + 1; k < merge_candidates->length(); k++) { | |
| 433 MathUnaryInstr* other_op = (*merge_candidates)[k]; | |
| 434 // 'other_op' can be NULL if it was already merged. | |
| 435 if ((other_op != NULL) && (other_op->kind() == other_kind) && | |
| 436 (other_op->value()->definition() == def)) { | |
| 437 (*merge_candidates)[k] = NULL; // Clear it. | |
| 438 ASSERT(curr_instr->HasUses()); | |
| 439 AppendExtractNthOutputForMerged(curr_instr, | |
| 440 MergedMathInstr::OutputIndexOf(kind), | |
| 441 kUnboxedDouble, kDoubleCid); | |
| 442 ASSERT(other_op->HasUses()); | |
| 443 AppendExtractNthOutputForMerged( | |
| 444 other_op, | |
| 445 MergedMathInstr::OutputIndexOf(other_kind), | |
| 446 kUnboxedDouble, kDoubleCid); | |
| 447 ZoneGrowableArray<Value*>* args = new(Z) ZoneGrowableArray<Value*>(1); | |
| 448 args->Add(new(Z) Value(curr_instr->value()->definition())); | |
| 449 // Replace with SinCos. | |
| 450 MergedMathInstr* sin_cos = | |
| 451 new(Z) MergedMathInstr(args, | |
| 452 curr_instr->DeoptimizationTarget(), | |
| 453 MergedMathInstr::kSinCos); | |
| 454 curr_instr->ReplaceWith(sin_cos, current_iterator()); | |
| 455 other_op->ReplaceUsesWith(sin_cos); | |
| 456 other_op->RemoveFromGraph(); | |
| 457 // Only one merge possible. Because canonicalization happens later, | |
| 458 // more candidates are possible. | |
| 459 // TODO(srdjan): Allow merging of sin/cos into sincos. | |
| 460 break; | |
| 461 } | |
| 462 } | |
| 463 } | |
| 464 } | |
| 465 | |
| 466 | |
| 467 // Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the | |
| 468 // shift can be a truncating Smi shift-left and result is always Smi. | |
| 469 // Merging occurs only per basic-block. | |
| 470 void JitOptimizer::TryOptimizePatterns() { | |
| 471 if (!FLAG_truncating_left_shift) return; | |
| 472 ASSERT(current_iterator_ == NULL); | |
| 473 GrowableArray<BinarySmiOpInstr*> div_mod_merge; | |
| 474 GrowableArray<MathUnaryInstr*> sin_cos_merge; | |
| 475 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | |
| 476 !block_it.Done(); | |
| 477 block_it.Advance()) { | |
| 478 // Merging only per basic-block. | |
| 479 div_mod_merge.Clear(); | |
| 480 sin_cos_merge.Clear(); | |
| 481 ForwardInstructionIterator it(block_it.Current()); | |
| 482 current_iterator_ = ⁢ | |
| 483 for (; !it.Done(); it.Advance()) { | |
| 484 if (it.Current()->IsBinarySmiOp()) { | |
| 485 BinarySmiOpInstr* binop = it.Current()->AsBinarySmiOp(); | |
| 486 if (binop->op_kind() == Token::kBIT_AND) { | |
| 487 OptimizeLeftShiftBitAndSmiOp(binop, | |
| 488 binop->left()->definition(), | |
| 489 binop->right()->definition()); | |
| 490 } else if ((binop->op_kind() == Token::kTRUNCDIV) || | |
| 491 (binop->op_kind() == Token::kMOD)) { | |
| 492 if (binop->HasUses()) { | |
| 493 div_mod_merge.Add(binop); | |
| 494 } | |
| 495 } | |
| 496 } else if (it.Current()->IsBinaryMintOp()) { | |
| 497 BinaryMintOpInstr* mintop = it.Current()->AsBinaryMintOp(); | |
| 498 if (mintop->op_kind() == Token::kBIT_AND) { | |
| 499 OptimizeLeftShiftBitAndSmiOp(mintop, | |
| 500 mintop->left()->definition(), | |
| 501 mintop->right()->definition()); | |
| 502 } | |
| 503 } else if (it.Current()->IsMathUnary()) { | |
| 504 MathUnaryInstr* math_unary = it.Current()->AsMathUnary(); | |
| 505 if ((math_unary->kind() == MathUnaryInstr::kSin) || | |
| 506 (math_unary->kind() == MathUnaryInstr::kCos)) { | |
| 507 if (math_unary->HasUses()) { | |
| 508 sin_cos_merge.Add(math_unary); | |
| 509 } | |
| 510 } | |
| 511 } | |
| 512 } | |
| 513 TryMergeTruncDivMod(&div_mod_merge); | |
| 514 TryMergeMathUnary(&sin_cos_merge); | |
| 515 current_iterator_ = NULL; | |
| 516 } | |
| 517 } | |
| 518 | |
| 519 | |
| 520 static bool ClassIdIsOneOf(intptr_t class_id, | 264 static bool ClassIdIsOneOf(intptr_t class_id, |
| 521 const GrowableArray<intptr_t>& class_ids) { | 265 const GrowableArray<intptr_t>& class_ids) { |
| 522 for (intptr_t i = 0; i < class_ids.length(); i++) { | 266 for (intptr_t i = 0; i < class_ids.length(); i++) { |
| 523 ASSERT(class_ids[i] != kIllegalCid); | 267 ASSERT(class_ids[i] != kIllegalCid); |
| 524 if (class_ids[i] == class_id) { | 268 if (class_ids[i] == class_id) { |
| 525 return true; | 269 return true; |
| 526 } | 270 } |
| 527 } | 271 } |
| 528 return false; | 272 return false; |
| 529 } | 273 } |
| (...skipping 801 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1331 for (Value::Iterator it(load->input_use_list()); | 1075 for (Value::Iterator it(load->input_use_list()); |
| 1332 !it.Done(); | 1076 !it.Done(); |
| 1333 it.Advance()) { | 1077 it.Advance()) { |
| 1334 it.Current()->SetReachingType(NULL); | 1078 it.Current()->SetReachingType(NULL); |
| 1335 } | 1079 } |
| 1336 } | 1080 } |
| 1337 return true; | 1081 return true; |
| 1338 } | 1082 } |
| 1339 | 1083 |
| 1340 | 1084 |
| 1341 bool JitOptimizer::InlineFloat64x2Getter(InstanceCallInstr* call, | |
| 1342 MethodRecognizer::Kind getter) { | |
| 1343 if (!ShouldInlineSimd()) { | |
| 1344 return false; | |
| 1345 } | |
| 1346 AddCheckClass(call->ArgumentAt(0), | |
| 1347 ICData::ZoneHandle( | |
| 1348 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
| 1349 call->deopt_id(), | |
| 1350 call->env(), | |
| 1351 call); | |
| 1352 if ((getter == MethodRecognizer::kFloat64x2GetX) || | |
| 1353 (getter == MethodRecognizer::kFloat64x2GetY)) { | |
| 1354 Simd64x2ShuffleInstr* instr = new(Z) Simd64x2ShuffleInstr( | |
| 1355 getter, | |
| 1356 new(Z) Value(call->ArgumentAt(0)), | |
| 1357 0, | |
| 1358 call->deopt_id()); | |
| 1359 ReplaceCall(call, instr); | |
| 1360 return true; | |
| 1361 } | |
| 1362 UNREACHABLE(); | |
| 1363 return false; | |
| 1364 } | |
| 1365 | |
| 1366 | |
| 1367 bool JitOptimizer::InlineFloat32x4BinaryOp(InstanceCallInstr* call, | 1085 bool JitOptimizer::InlineFloat32x4BinaryOp(InstanceCallInstr* call, |
| 1368 Token::Kind op_kind) { | 1086 Token::Kind op_kind) { |
| 1369 if (!ShouldInlineSimd()) { | 1087 if (!ShouldInlineSimd()) { |
| 1370 return false; | 1088 return false; |
| 1371 } | 1089 } |
| 1372 ASSERT(call->ArgumentCount() == 2); | 1090 ASSERT(call->ArgumentCount() == 2); |
| 1373 Definition* left = call->ArgumentAt(0); | 1091 Definition* left = call->ArgumentAt(0); |
| 1374 Definition* right = call->ArgumentAt(1); | 1092 Definition* right = call->ArgumentAt(1); |
| 1375 // Type check left. | 1093 // Type check left. |
| 1376 AddCheckClass(left, | 1094 AddCheckClass(left, |
| (...skipping 1070 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2447 | 2165 |
| 2448 // Discard the environment from the original instruction because the store | 2166 // Discard the environment from the original instruction because the store |
| 2449 // can't deoptimize. | 2167 // can't deoptimize. |
| 2450 instr->RemoveEnvironment(); | 2168 instr->RemoveEnvironment(); |
| 2451 ReplaceCall(instr, store); | 2169 ReplaceCall(instr, store); |
| 2452 return true; | 2170 return true; |
| 2453 } | 2171 } |
| 2454 | 2172 |
| 2455 | 2173 |
| 2456 } // namespace dart | 2174 } // namespace dart |
| OLD | NEW |