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/globals.h" // Needed here to get TARGET_ARCH_X64. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_X64. |
6 #if defined(TARGET_ARCH_X64) | 6 #if defined(TARGET_ARCH_X64) |
7 | 7 |
8 #include "vm/intermediate_language.h" | 8 #include "vm/intermediate_language.h" |
9 | 9 |
10 #include "vm/dart_entry.h" | 10 #include "vm/dart_entry.h" |
(...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 locs->set_in(0, Location::RegisterOrConstant(left())); | 400 locs->set_in(0, Location::RegisterOrConstant(left())); |
401 // Only one input can be a constant operand. The case of two constant | 401 // Only one input can be a constant operand. The case of two constant |
402 // operands should be handled by constant propagation. | 402 // operands should be handled by constant propagation. |
403 // Only right can be a stack slot. | 403 // Only right can be a stack slot. |
404 locs->set_in(1, locs->in(0).IsConstant() | 404 locs->set_in(1, locs->in(0).IsConstant() |
405 ? Location::RequiresRegister() | 405 ? Location::RequiresRegister() |
406 : Location::RegisterOrConstant(right())); | 406 : Location::RegisterOrConstant(right())); |
407 locs->set_out(Location::RequiresRegister()); | 407 locs->set_out(Location::RequiresRegister()); |
408 return locs; | 408 return locs; |
409 } | 409 } |
410 if (IsCheckedStrictEqual()) { | 410 UNREACHABLE(); |
411 const intptr_t kNumTemps = 1; | 411 return NULL; |
412 LocationSummary* locs = | |
413 new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall); | |
414 locs->set_in(0, Location::RequiresRegister()); | |
415 locs->set_in(1, Location::RequiresRegister()); | |
416 locs->set_temp(0, Location::RequiresRegister()); | |
417 locs->set_out(Location::RequiresRegister()); | |
418 return locs; | |
419 } | |
420 if (IsPolymorphic()) { | |
421 const intptr_t kNumTemps = 1; | |
422 LocationSummary* locs = | |
423 new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall); | |
424 locs->set_in(0, Location::RegisterLocation(RCX)); | |
425 locs->set_in(1, Location::RegisterLocation(RDX)); | |
426 locs->set_temp(0, Location::RegisterLocation(RBX)); | |
427 locs->set_out(Location::RegisterLocation(RAX)); | |
428 return locs; | |
429 } | |
430 const intptr_t kNumTemps = 1; | |
431 LocationSummary* locs = | |
432 new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall); | |
433 locs->set_in(0, Location::RegisterLocation(RCX)); | |
434 locs->set_in(1, Location::RegisterLocation(RDX)); | |
435 locs->set_temp(0, Location::RegisterLocation(RBX)); | |
436 locs->set_out(Location::RegisterLocation(RAX)); | |
437 return locs; | |
438 } | |
439 | |
440 | |
441 static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, | |
442 intptr_t deopt_id, | |
443 intptr_t token_pos, | |
444 Token::Kind kind, | |
445 LocationSummary* locs, | |
446 const ICData& original_ic_data) { | |
447 if (!compiler->is_optimizing()) { | |
448 compiler->AddCurrentDescriptor(PcDescriptors::kDeopt, | |
449 deopt_id, | |
450 token_pos); | |
451 } | |
452 const int kNumberOfArguments = 2; | |
453 const Array& kNoArgumentNames = Object::null_array(); | |
454 const int kNumArgumentsChecked = 2; | |
455 | |
456 ICData& equality_ic_data = ICData::ZoneHandle(original_ic_data.raw()); | |
457 if (compiler->is_optimizing() && FLAG_propagate_ic_data) { | |
458 ASSERT(!original_ic_data.IsNull()); | |
459 if (original_ic_data.NumberOfChecks() == 0) { | |
460 // IC call for reoptimization populates original ICData. | |
461 equality_ic_data = original_ic_data.raw(); | |
462 } else { | |
463 // Megamorphic call. | |
464 equality_ic_data = original_ic_data.AsUnaryClassChecks(); | |
465 } | |
466 } else { | |
467 const Array& arguments_descriptor = | |
468 Array::Handle(ArgumentsDescriptor::New(kNumberOfArguments, | |
469 kNoArgumentNames)); | |
470 equality_ic_data = ICData::New(compiler->parsed_function().function(), | |
471 Symbols::EqualOperator(), | |
472 arguments_descriptor, | |
473 deopt_id, | |
474 kNumArgumentsChecked); | |
475 } | |
476 compiler->GenerateInstanceCall(deopt_id, | |
477 token_pos, | |
478 kNumberOfArguments, | |
479 kNoArgumentNames, | |
480 locs, | |
481 equality_ic_data); | |
482 if (kind == Token::kNE) { | |
483 Label true_label, done; | |
484 // Negate the condition: true label returns false and vice versa. | |
485 __ CompareObject(RAX, Bool::True(), PP); | |
486 __ j(EQUAL, &true_label, Assembler::kNearJump); | |
487 __ LoadObject(RAX, Bool::True(), PP); | |
488 __ jmp(&done, Assembler::kNearJump); | |
489 __ Bind(&true_label); | |
490 __ LoadObject(RAX, Bool::False(), PP); | |
491 __ Bind(&done); | |
492 } | |
493 } | 412 } |
494 | 413 |
495 | 414 |
496 static void LoadValueCid(FlowGraphCompiler* compiler, | 415 static void LoadValueCid(FlowGraphCompiler* compiler, |
497 Register value_cid_reg, | 416 Register value_cid_reg, |
498 Register value_reg, | 417 Register value_reg, |
499 Label* value_is_smi = NULL) { | 418 Label* value_is_smi = NULL) { |
500 Label done; | 419 Label done; |
501 if (value_is_smi == NULL) { | 420 if (value_is_smi == NULL) { |
502 __ LoadImmediate(value_cid_reg, Immediate(kSmiCid), PP); | 421 __ LoadImmediate(value_cid_reg, Immediate(kSmiCid), PP); |
503 } | 422 } |
504 __ testq(value_reg, Immediate(kSmiTagMask)); | 423 __ testq(value_reg, Immediate(kSmiTagMask)); |
505 if (value_is_smi == NULL) { | 424 if (value_is_smi == NULL) { |
506 __ j(ZERO, &done, Assembler::kNearJump); | 425 __ j(ZERO, &done, Assembler::kNearJump); |
507 } else { | 426 } else { |
508 __ j(ZERO, value_is_smi); | 427 __ j(ZERO, value_is_smi); |
509 } | 428 } |
510 __ LoadClassId(value_cid_reg, value_reg); | 429 __ LoadClassId(value_cid_reg, value_reg); |
511 __ Bind(&done); | 430 __ Bind(&done); |
512 } | 431 } |
513 | 432 |
514 | 433 |
515 static void EmitEqualityAsPolymorphicCall(FlowGraphCompiler* compiler, | |
516 const ICData& orig_ic_data, | |
517 LocationSummary* locs, | |
518 BranchInstr* branch, | |
519 Token::Kind kind, | |
520 intptr_t deopt_id, | |
521 intptr_t token_pos) { | |
522 ASSERT((kind == Token::kEQ) || (kind == Token::kNE)); | |
523 const ICData& ic_data = ICData::Handle(orig_ic_data.AsUnaryClassChecks()); | |
524 ASSERT(ic_data.NumberOfChecks() > 0); | |
525 ASSERT(ic_data.num_args_tested() == 1); | |
526 Label* deopt = compiler->AddDeoptStub(deopt_id, kDeoptEquality); | |
527 Register left = locs->in(0).reg(); | |
528 Register right = locs->in(1).reg(); | |
529 Register temp = locs->temp(0).reg(); | |
530 LoadValueCid(compiler, temp, left, | |
531 (ic_data.GetReceiverClassIdAt(0) == kSmiCid) ? NULL : deopt); | |
532 // 'temp' contains class-id of the left argument. | |
533 ObjectStore* object_store = Isolate::Current()->object_store(); | |
534 Condition cond = TokenKindToSmiCondition(kind); | |
535 Label done; | |
536 const intptr_t len = ic_data.NumberOfChecks(); | |
537 for (intptr_t i = 0; i < len; i++) { | |
538 // Assert that the Smi is at position 0, if at all. | |
539 ASSERT((ic_data.GetReceiverClassIdAt(i) != kSmiCid) || (i == 0)); | |
540 Label next_test; | |
541 __ CompareImmediate(temp, Immediate(ic_data.GetReceiverClassIdAt(i)), PP); | |
542 if (i < len - 1) { | |
543 __ j(NOT_EQUAL, &next_test); | |
544 } else { | |
545 __ j(NOT_EQUAL, deopt); | |
546 } | |
547 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(i)); | |
548 if (target.Owner() == object_store->object_class()) { | |
549 // Object.== is same as ===. | |
550 __ Drop(2); | |
551 __ cmpq(left, right); | |
552 if (branch != NULL) { | |
553 branch->EmitBranchOnCondition(compiler, cond); | |
554 } else { | |
555 // This case should be rare. | |
556 Register result = locs->out().reg(); | |
557 Label load_true; | |
558 __ j(cond, &load_true, Assembler::kNearJump); | |
559 __ LoadObject(result, Bool::False(), PP); | |
560 __ jmp(&done); | |
561 __ Bind(&load_true); | |
562 __ LoadObject(result, Bool::True(), PP); | |
563 } | |
564 } else { | |
565 const int kNumberOfArguments = 2; | |
566 const Array& kNoArgumentNames = Object::null_array(); | |
567 compiler->GenerateStaticCall(deopt_id, | |
568 token_pos, | |
569 target, | |
570 kNumberOfArguments, | |
571 kNoArgumentNames, | |
572 locs); | |
573 if (branch == NULL) { | |
574 if (kind == Token::kNE) { | |
575 Label false_label; | |
576 __ CompareObject(RAX, Bool::True(), PP); | |
577 __ j(EQUAL, &false_label, Assembler::kNearJump); | |
578 __ LoadObject(RAX, Bool::True(), PP); | |
579 __ jmp(&done); | |
580 __ Bind(&false_label); | |
581 __ LoadObject(RAX, Bool::False(), PP); | |
582 } | |
583 } else { | |
584 if (branch->is_checked()) { | |
585 EmitAssertBoolean(RAX, token_pos, deopt_id, locs, compiler); | |
586 } | |
587 __ CompareObject(RAX, Bool::True(), PP); | |
588 branch->EmitBranchOnCondition(compiler, cond); | |
589 } | |
590 } | |
591 if (i < len - 1) { | |
592 __ jmp(&done); | |
593 __ Bind(&next_test); | |
594 } | |
595 } | |
596 __ Bind(&done); | |
597 } | |
598 | |
599 | |
600 // Emit code when ICData's targets are all Object == (which is ===). | |
601 static void EmitCheckedStrictEqual(FlowGraphCompiler* compiler, | |
602 const ICData& orig_ic_data, | |
603 const LocationSummary& locs, | |
604 Token::Kind kind, | |
605 BranchInstr* branch, | |
606 intptr_t deopt_id) { | |
607 ASSERT((kind == Token::kEQ) || (kind == Token::kNE)); | |
608 Register left = locs.in(0).reg(); | |
609 Register right = locs.in(1).reg(); | |
610 Register temp = locs.temp(0).reg(); | |
611 Label* deopt = compiler->AddDeoptStub(deopt_id, kDeoptEquality); | |
612 __ testq(left, Immediate(kSmiTagMask)); | |
613 __ j(ZERO, deopt); | |
614 // 'left' is not Smi. | |
615 | |
616 Label identity_compare; | |
617 __ CompareObject(right, Object::null_object(), PP); | |
618 __ j(EQUAL, &identity_compare); | |
619 __ CompareObject(left, Object::null_object(), PP); | |
620 __ j(EQUAL, &identity_compare); | |
621 | |
622 __ LoadClassId(temp, left); | |
623 const ICData& ic_data = ICData::Handle(orig_ic_data.AsUnaryClassChecks()); | |
624 const intptr_t len = ic_data.NumberOfChecks(); | |
625 for (intptr_t i = 0; i < len; i++) { | |
626 __ CompareImmediate(temp, Immediate(ic_data.GetReceiverClassIdAt(i)), PP); | |
627 if (i == (len - 1)) { | |
628 __ j(NOT_EQUAL, deopt); | |
629 } else { | |
630 __ j(EQUAL, &identity_compare); | |
631 } | |
632 } | |
633 __ Bind(&identity_compare); | |
634 __ cmpq(left, right); | |
635 if (branch == NULL) { | |
636 Label done, is_equal; | |
637 Register result = locs.out().reg(); | |
638 __ j(EQUAL, &is_equal, Assembler::kNearJump); | |
639 // Not equal. | |
640 __ LoadObject(result, Bool::Get(kind != Token::kEQ), PP); | |
641 __ jmp(&done, Assembler::kNearJump); | |
642 __ Bind(&is_equal); | |
643 __ LoadObject(result, Bool::Get(kind == Token::kEQ), PP); | |
644 __ Bind(&done); | |
645 } else { | |
646 Condition cond = TokenKindToSmiCondition(kind); | |
647 branch->EmitBranchOnCondition(compiler, cond); | |
648 } | |
649 } | |
650 | |
651 | |
652 // First test if receiver is NULL, in which case === is applied. | |
653 // If type feedback was provided (lists of <class-id, target>), do a | |
654 // type by type check (either === or static call to the operator. | |
655 static void EmitGenericEqualityCompare(FlowGraphCompiler* compiler, | |
656 LocationSummary* locs, | |
657 Token::Kind kind, | |
658 BranchInstr* branch, | |
659 const ICData& ic_data, | |
660 intptr_t deopt_id, | |
661 intptr_t token_pos) { | |
662 ASSERT((kind == Token::kEQ) || (kind == Token::kNE)); | |
663 ASSERT(!ic_data.IsNull() && (ic_data.NumberOfChecks() > 0)); | |
664 Register left = locs->in(0).reg(); | |
665 Register right = locs->in(1).reg(); | |
666 __ pushq(left); | |
667 __ pushq(right); | |
668 EmitEqualityAsPolymorphicCall(compiler, ic_data, locs, branch, kind, | |
669 deopt_id, token_pos); | |
670 } | |
671 | |
672 | |
673 static Condition FlipCondition(Condition condition) { | 434 static Condition FlipCondition(Condition condition) { |
674 switch (condition) { | 435 switch (condition) { |
675 case EQUAL: return EQUAL; | 436 case EQUAL: return EQUAL; |
676 case NOT_EQUAL: return NOT_EQUAL; | 437 case NOT_EQUAL: return NOT_EQUAL; |
677 case LESS: return GREATER; | 438 case LESS: return GREATER; |
678 case LESS_EQUAL: return GREATER_EQUAL; | 439 case LESS_EQUAL: return GREATER_EQUAL; |
679 case GREATER: return LESS; | 440 case GREATER: return LESS; |
680 case GREATER_EQUAL: return LESS_EQUAL; | 441 case GREATER_EQUAL: return LESS_EQUAL; |
681 case BELOW: return ABOVE; | 442 case BELOW: return ABOVE; |
682 case BELOW_EQUAL: return ABOVE_EQUAL; | 443 case BELOW_EQUAL: return ABOVE_EQUAL; |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
764 if (operation_cid() == kSmiCid) { | 525 if (operation_cid() == kSmiCid) { |
765 // Deoptimizes if both arguments not Smi. | 526 // Deoptimizes if both arguments not Smi. |
766 EmitSmiComparisonOp(compiler, *locs(), kind(), kNoBranch); | 527 EmitSmiComparisonOp(compiler, *locs(), kind(), kNoBranch); |
767 return; | 528 return; |
768 } | 529 } |
769 if (operation_cid() == kDoubleCid) { | 530 if (operation_cid() == kDoubleCid) { |
770 // Deoptimizes if both arguments are Smi, or if none is Double or Smi. | 531 // Deoptimizes if both arguments are Smi, or if none is Double or Smi. |
771 EmitDoubleComparisonOp(compiler, *locs(), kind(), kNoBranch); | 532 EmitDoubleComparisonOp(compiler, *locs(), kind(), kNoBranch); |
772 return; | 533 return; |
773 } | 534 } |
774 if (IsCheckedStrictEqual()) { | 535 UNREACHABLE(); |
775 EmitCheckedStrictEqual(compiler, *ic_data(), *locs(), kind(), kNoBranch, | |
776 deopt_id()); | |
777 return; | |
778 } | |
779 if (IsPolymorphic()) { | |
780 EmitGenericEqualityCompare(compiler, locs(), kind(), kNoBranch, *ic_data(), | |
781 deopt_id(), token_pos()); | |
782 return; | |
783 } | |
784 Register left = locs()->in(0).reg(); | |
785 Register right = locs()->in(1).reg(); | |
786 __ pushq(left); | |
787 __ pushq(right); | |
788 EmitEqualityAsInstanceCall(compiler, | |
789 deopt_id(), | |
790 token_pos(), | |
791 kind(), | |
792 locs(), | |
793 *ic_data()); | |
794 ASSERT(locs()->out().reg() == RAX); | |
795 } | 536 } |
796 | 537 |
797 | 538 |
798 void EqualityCompareInstr::EmitBranchCode(FlowGraphCompiler* compiler, | 539 void EqualityCompareInstr::EmitBranchCode(FlowGraphCompiler* compiler, |
799 BranchInstr* branch) { | 540 BranchInstr* branch) { |
800 ASSERT((kind() == Token::kNE) || (kind() == Token::kEQ)); | 541 ASSERT((kind() == Token::kNE) || (kind() == Token::kEQ)); |
801 if (operation_cid() == kSmiCid) { | 542 if (operation_cid() == kSmiCid) { |
802 // Deoptimizes if both arguments not Smi. | 543 // Deoptimizes if both arguments not Smi. |
803 EmitSmiComparisonOp(compiler, *locs(), kind(), branch); | 544 EmitSmiComparisonOp(compiler, *locs(), kind(), branch); |
804 return; | 545 return; |
805 } | 546 } |
806 if (operation_cid() == kDoubleCid) { | 547 if (operation_cid() == kDoubleCid) { |
807 // Deoptimizes if both arguments are Smi, or if none is Double or Smi. | 548 // Deoptimizes if both arguments are Smi, or if none is Double or Smi. |
808 EmitDoubleComparisonOp(compiler, *locs(), kind(), branch); | 549 EmitDoubleComparisonOp(compiler, *locs(), kind(), branch); |
809 return; | 550 return; |
810 } | 551 } |
811 if (IsCheckedStrictEqual()) { | 552 UNREACHABLE(); |
812 EmitCheckedStrictEqual(compiler, *ic_data(), *locs(), kind(), branch, | |
813 deopt_id()); | |
814 return; | |
815 } | |
816 if (IsPolymorphic()) { | |
817 EmitGenericEqualityCompare(compiler, locs(), kind(), branch, *ic_data(), | |
818 deopt_id(), token_pos()); | |
819 return; | |
820 } | |
821 Register left = locs()->in(0).reg(); | |
822 Register right = locs()->in(1).reg(); | |
823 __ pushq(left); | |
824 __ pushq(right); | |
825 EmitEqualityAsInstanceCall(compiler, | |
826 deopt_id(), | |
827 token_pos(), | |
828 Token::kEQ, // kNE reverse occurs at branch. | |
829 locs(), | |
830 *ic_data()); | |
831 if (branch->is_checked()) { | |
832 EmitAssertBoolean(RAX, token_pos(), deopt_id(), locs(), compiler); | |
833 } | |
834 Condition branch_condition = (kind() == Token::kNE) ? NOT_EQUAL : EQUAL; | |
835 __ CompareObject(RAX, Bool::True(), PP); | |
836 branch->EmitBranchOnCondition(compiler, branch_condition); | |
837 } | 553 } |
838 | 554 |
839 | 555 |
840 LocationSummary* RelationalOpInstr::MakeLocationSummary() const { | 556 LocationSummary* RelationalOpInstr::MakeLocationSummary() const { |
841 const intptr_t kNumInputs = 2; | 557 const intptr_t kNumInputs = 2; |
842 const intptr_t kNumTemps = 0; | 558 const intptr_t kNumTemps = 0; |
843 if (operation_cid() == kDoubleCid) { | 559 if (operation_cid() == kDoubleCid) { |
844 LocationSummary* summary = | 560 LocationSummary* summary = |
845 new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall); | 561 new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall); |
846 summary->set_in(0, Location::RequiresFpuRegister()); | 562 summary->set_in(0, Location::RequiresFpuRegister()); |
(...skipping 3960 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4807 PcDescriptors::kOther, | 4523 PcDescriptors::kOther, |
4808 locs()); | 4524 locs()); |
4809 __ Drop(2); // Discard type arguments and receiver. | 4525 __ Drop(2); // Discard type arguments and receiver. |
4810 } | 4526 } |
4811 | 4527 |
4812 } // namespace dart | 4528 } // namespace dart |
4813 | 4529 |
4814 #undef __ | 4530 #undef __ |
4815 | 4531 |
4816 #endif // defined TARGET_ARCH_X64 | 4532 #endif // defined TARGET_ARCH_X64 |
OLD | NEW |