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 |