OLD | NEW |
1 // Copyright 2013 the V8 project authors. All rights reserved. | 1 // Copyright 2013 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 Isolate* isolate, | 148 Isolate* isolate, |
149 CodeStubInterfaceDescriptor* descriptor) { | 149 CodeStubInterfaceDescriptor* descriptor) { |
150 static Register registers[] = { rax, rbx }; | 150 static Register registers[] = { rax, rbx }; |
151 descriptor->register_param_count_ = 2; | 151 descriptor->register_param_count_ = 2; |
152 descriptor->register_params_ = registers; | 152 descriptor->register_params_ = registers; |
153 descriptor->deoptimization_handler_ = | 153 descriptor->deoptimization_handler_ = |
154 Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry; | 154 Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry; |
155 } | 155 } |
156 | 156 |
157 | 157 |
158 void BinaryOpStub::InitializeInterfaceDescriptor( | |
159 Isolate* isolate, | |
160 CodeStubInterfaceDescriptor* descriptor) { | |
161 static Register registers[] = { rdx, rax }; | |
162 descriptor->register_param_count_ = 2; | |
163 descriptor->register_params_ = registers; | |
164 descriptor->deoptimization_handler_ = FUNCTION_ADDR(BinaryOpIC_Miss); | |
165 descriptor->SetMissHandler( | |
166 ExternalReference(IC_Utility(IC::kBinaryOpIC_Miss), isolate)); | |
167 } | |
168 | |
169 | |
170 static void InitializeArrayConstructorDescriptor( | 158 static void InitializeArrayConstructorDescriptor( |
171 Isolate* isolate, | 159 Isolate* isolate, |
172 CodeStubInterfaceDescriptor* descriptor, | 160 CodeStubInterfaceDescriptor* descriptor, |
173 int constant_stack_parameter_count) { | 161 int constant_stack_parameter_count) { |
174 // register state | 162 // register state |
175 // rax -- number of arguments | 163 // rax -- number of arguments |
176 // rdi -- function | 164 // rdi -- function |
177 // rbx -- type info cell with elements kind | 165 // rbx -- type info cell with elements kind |
178 static Register registers[] = { rdi, rbx }; | 166 static Register registers[] = { rdi, rbx }; |
179 descriptor->register_param_count_ = 2; | 167 descriptor->register_param_count_ = 2; |
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
452 class FloatingPointHelper : public AllStatic { | 440 class FloatingPointHelper : public AllStatic { |
453 public: | 441 public: |
454 enum ConvertUndefined { | 442 enum ConvertUndefined { |
455 CONVERT_UNDEFINED_TO_ZERO, | 443 CONVERT_UNDEFINED_TO_ZERO, |
456 BAILOUT_ON_UNDEFINED | 444 BAILOUT_ON_UNDEFINED |
457 }; | 445 }; |
458 // Load the operands from rdx and rax into xmm0 and xmm1, as doubles. | 446 // Load the operands from rdx and rax into xmm0 and xmm1, as doubles. |
459 // If the operands are not both numbers, jump to not_numbers. | 447 // If the operands are not both numbers, jump to not_numbers. |
460 // Leaves rdx and rax unchanged. SmiOperands assumes both are smis. | 448 // Leaves rdx and rax unchanged. SmiOperands assumes both are smis. |
461 // NumberOperands assumes both are smis or heap numbers. | 449 // NumberOperands assumes both are smis or heap numbers. |
| 450 static void LoadSSE2SmiOperands(MacroAssembler* masm); |
462 static void LoadSSE2UnknownOperands(MacroAssembler* masm, | 451 static void LoadSSE2UnknownOperands(MacroAssembler* masm, |
463 Label* not_numbers); | 452 Label* not_numbers); |
| 453 |
| 454 // Takes the operands in rdx and rax and loads them as integers in rax |
| 455 // and rcx. |
| 456 static void LoadAsIntegers(MacroAssembler* masm, |
| 457 Label* operand_conversion_failure, |
| 458 Register heap_number_map); |
| 459 |
| 460 // Tries to convert two values to smis losslessly. |
| 461 // This fails if either argument is not a Smi nor a HeapNumber, |
| 462 // or if it's a HeapNumber with a value that can't be converted |
| 463 // losslessly to a Smi. In that case, control transitions to the |
| 464 // on_not_smis label. |
| 465 // On success, either control goes to the on_success label (if one is |
| 466 // provided), or it falls through at the end of the code (if on_success |
| 467 // is NULL). |
| 468 // On success, both first and second holds Smi tagged values. |
| 469 // One of first or second must be non-Smi when entering. |
| 470 static void NumbersToSmis(MacroAssembler* masm, |
| 471 Register first, |
| 472 Register second, |
| 473 Register scratch1, |
| 474 Register scratch2, |
| 475 Register scratch3, |
| 476 Label* on_success, |
| 477 Label* on_not_smis, |
| 478 ConvertUndefined convert_undefined); |
464 }; | 479 }; |
465 | 480 |
466 | 481 |
467 void DoubleToIStub::Generate(MacroAssembler* masm) { | 482 void DoubleToIStub::Generate(MacroAssembler* masm) { |
468 Register input_reg = this->source(); | 483 Register input_reg = this->source(); |
469 Register final_result_reg = this->destination(); | 484 Register final_result_reg = this->destination(); |
470 ASSERT(is_truncating()); | 485 ASSERT(is_truncating()); |
471 | 486 |
472 Label check_negative, process_64_bits, done; | 487 Label check_negative, process_64_bits, done; |
473 | 488 |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
541 if (!final_result_reg.is(result_reg)) { | 556 if (!final_result_reg.is(result_reg)) { |
542 ASSERT(final_result_reg.is(rcx)); | 557 ASSERT(final_result_reg.is(rcx)); |
543 __ movl(final_result_reg, result_reg); | 558 __ movl(final_result_reg, result_reg); |
544 } | 559 } |
545 __ pop(save_reg); | 560 __ pop(save_reg); |
546 __ pop(scratch1); | 561 __ pop(scratch1); |
547 __ ret(0); | 562 __ ret(0); |
548 } | 563 } |
549 | 564 |
550 | 565 |
| 566 void BinaryOpStub::Initialize() {} |
| 567 |
| 568 |
| 569 void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { |
| 570 __ PopReturnAddressTo(rcx); |
| 571 __ push(rdx); |
| 572 __ push(rax); |
| 573 // Left and right arguments are now on top. |
| 574 __ Push(Smi::FromInt(MinorKey())); |
| 575 |
| 576 __ PushReturnAddressFrom(rcx); |
| 577 |
| 578 // Patch the caller to an appropriate specialized stub and return the |
| 579 // operation result to the caller of the stub. |
| 580 __ TailCallExternalReference( |
| 581 ExternalReference(IC_Utility(IC::kBinaryOp_Patch), |
| 582 masm->isolate()), |
| 583 3, |
| 584 1); |
| 585 } |
| 586 |
| 587 |
| 588 static void BinaryOpStub_GenerateSmiCode( |
| 589 MacroAssembler* masm, |
| 590 Label* slow, |
| 591 BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results, |
| 592 Token::Value op) { |
| 593 |
| 594 // Arguments to BinaryOpStub are in rdx and rax. |
| 595 const Register left = rdx; |
| 596 const Register right = rax; |
| 597 |
| 598 // We only generate heapnumber answers for overflowing calculations |
| 599 // for the four basic arithmetic operations and logical right shift by 0. |
| 600 bool generate_inline_heapnumber_results = |
| 601 (allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS) && |
| 602 (op == Token::ADD || op == Token::SUB || |
| 603 op == Token::MUL || op == Token::DIV || op == Token::SHR); |
| 604 |
| 605 // Smi check of both operands. If op is BIT_OR, the check is delayed |
| 606 // until after the OR operation. |
| 607 Label not_smis; |
| 608 Label use_fp_on_smis; |
| 609 Label fail; |
| 610 |
| 611 if (op != Token::BIT_OR) { |
| 612 Comment smi_check_comment(masm, "-- Smi check arguments"); |
| 613 __ JumpIfNotBothSmi(left, right, ¬_smis); |
| 614 } |
| 615 |
| 616 Label smi_values; |
| 617 __ bind(&smi_values); |
| 618 // Perform the operation. |
| 619 Comment perform_smi(masm, "-- Perform smi operation"); |
| 620 switch (op) { |
| 621 case Token::ADD: |
| 622 ASSERT(right.is(rax)); |
| 623 __ SmiAdd(right, right, left, &use_fp_on_smis); // ADD is commutative. |
| 624 break; |
| 625 |
| 626 case Token::SUB: |
| 627 __ SmiSub(left, left, right, &use_fp_on_smis); |
| 628 __ movq(rax, left); |
| 629 break; |
| 630 |
| 631 case Token::MUL: |
| 632 ASSERT(right.is(rax)); |
| 633 __ SmiMul(right, right, left, &use_fp_on_smis); // MUL is commutative. |
| 634 break; |
| 635 |
| 636 case Token::DIV: |
| 637 // SmiDiv will not accept left in rdx or right in rax. |
| 638 __ movq(rbx, rax); |
| 639 __ movq(rcx, rdx); |
| 640 __ SmiDiv(rax, rcx, rbx, &use_fp_on_smis); |
| 641 break; |
| 642 |
| 643 case Token::MOD: |
| 644 // SmiMod will not accept left in rdx or right in rax. |
| 645 __ movq(rbx, rax); |
| 646 __ movq(rcx, rdx); |
| 647 __ SmiMod(rax, rcx, rbx, &use_fp_on_smis); |
| 648 break; |
| 649 |
| 650 case Token::BIT_OR: { |
| 651 ASSERT(right.is(rax)); |
| 652 __ SmiOrIfSmis(right, right, left, ¬_smis); // BIT_OR is commutative. |
| 653 break; |
| 654 } |
| 655 case Token::BIT_XOR: |
| 656 ASSERT(right.is(rax)); |
| 657 __ SmiXor(right, right, left); // BIT_XOR is commutative. |
| 658 break; |
| 659 |
| 660 case Token::BIT_AND: |
| 661 ASSERT(right.is(rax)); |
| 662 __ SmiAnd(right, right, left); // BIT_AND is commutative. |
| 663 break; |
| 664 |
| 665 case Token::SHL: |
| 666 __ SmiShiftLeft(left, left, right); |
| 667 __ movq(rax, left); |
| 668 break; |
| 669 |
| 670 case Token::SAR: |
| 671 __ SmiShiftArithmeticRight(left, left, right); |
| 672 __ movq(rax, left); |
| 673 break; |
| 674 |
| 675 case Token::SHR: |
| 676 __ SmiShiftLogicalRight(left, left, right, &use_fp_on_smis); |
| 677 __ movq(rax, left); |
| 678 break; |
| 679 |
| 680 default: |
| 681 UNREACHABLE(); |
| 682 } |
| 683 |
| 684 // 5. Emit return of result in rax. Some operations have registers pushed. |
| 685 __ ret(0); |
| 686 |
| 687 if (use_fp_on_smis.is_linked()) { |
| 688 // 6. For some operations emit inline code to perform floating point |
| 689 // operations on known smis (e.g., if the result of the operation |
| 690 // overflowed the smi range). |
| 691 __ bind(&use_fp_on_smis); |
| 692 if (op == Token::DIV || op == Token::MOD) { |
| 693 // Restore left and right to rdx and rax. |
| 694 __ movq(rdx, rcx); |
| 695 __ movq(rax, rbx); |
| 696 } |
| 697 |
| 698 if (generate_inline_heapnumber_results) { |
| 699 __ AllocateHeapNumber(rcx, rbx, slow); |
| 700 Comment perform_float(masm, "-- Perform float operation on smis"); |
| 701 if (op == Token::SHR) { |
| 702 __ SmiToInteger32(left, left); |
| 703 __ cvtqsi2sd(xmm0, left); |
| 704 } else { |
| 705 FloatingPointHelper::LoadSSE2SmiOperands(masm); |
| 706 switch (op) { |
| 707 case Token::ADD: __ addsd(xmm0, xmm1); break; |
| 708 case Token::SUB: __ subsd(xmm0, xmm1); break; |
| 709 case Token::MUL: __ mulsd(xmm0, xmm1); break; |
| 710 case Token::DIV: __ divsd(xmm0, xmm1); break; |
| 711 default: UNREACHABLE(); |
| 712 } |
| 713 } |
| 714 __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0); |
| 715 __ movq(rax, rcx); |
| 716 __ ret(0); |
| 717 } else { |
| 718 __ jmp(&fail); |
| 719 } |
| 720 } |
| 721 |
| 722 // 7. Non-smi operands reach the end of the code generated by |
| 723 // GenerateSmiCode, and fall through to subsequent code, |
| 724 // with the operands in rdx and rax. |
| 725 // But first we check if non-smi values are HeapNumbers holding |
| 726 // values that could be smi. |
| 727 __ bind(¬_smis); |
| 728 Comment done_comment(masm, "-- Enter non-smi code"); |
| 729 FloatingPointHelper::ConvertUndefined convert_undefined = |
| 730 FloatingPointHelper::BAILOUT_ON_UNDEFINED; |
| 731 // This list must be in sync with BinaryOpPatch() behavior in ic.cc. |
| 732 if (op == Token::BIT_AND || |
| 733 op == Token::BIT_OR || |
| 734 op == Token::BIT_XOR || |
| 735 op == Token::SAR || |
| 736 op == Token::SHL || |
| 737 op == Token::SHR) { |
| 738 convert_undefined = FloatingPointHelper::CONVERT_UNDEFINED_TO_ZERO; |
| 739 } |
| 740 FloatingPointHelper::NumbersToSmis(masm, left, right, rbx, rdi, rcx, |
| 741 &smi_values, &fail, convert_undefined); |
| 742 __ jmp(&smi_values); |
| 743 __ bind(&fail); |
| 744 } |
| 745 |
| 746 |
| 747 static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm, |
| 748 Label* alloc_failure, |
| 749 OverwriteMode mode); |
| 750 |
| 751 |
| 752 static void BinaryOpStub_GenerateFloatingPointCode(MacroAssembler* masm, |
| 753 Label* allocation_failure, |
| 754 Label* non_numeric_failure, |
| 755 Token::Value op, |
| 756 OverwriteMode mode) { |
| 757 switch (op) { |
| 758 case Token::ADD: |
| 759 case Token::SUB: |
| 760 case Token::MUL: |
| 761 case Token::DIV: { |
| 762 FloatingPointHelper::LoadSSE2UnknownOperands(masm, non_numeric_failure); |
| 763 |
| 764 switch (op) { |
| 765 case Token::ADD: __ addsd(xmm0, xmm1); break; |
| 766 case Token::SUB: __ subsd(xmm0, xmm1); break; |
| 767 case Token::MUL: __ mulsd(xmm0, xmm1); break; |
| 768 case Token::DIV: __ divsd(xmm0, xmm1); break; |
| 769 default: UNREACHABLE(); |
| 770 } |
| 771 BinaryOpStub_GenerateHeapResultAllocation( |
| 772 masm, allocation_failure, mode); |
| 773 __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0); |
| 774 __ ret(0); |
| 775 break; |
| 776 } |
| 777 case Token::MOD: { |
| 778 // For MOD we jump to the allocation_failure label, to call runtime. |
| 779 __ jmp(allocation_failure); |
| 780 break; |
| 781 } |
| 782 case Token::BIT_OR: |
| 783 case Token::BIT_AND: |
| 784 case Token::BIT_XOR: |
| 785 case Token::SAR: |
| 786 case Token::SHL: |
| 787 case Token::SHR: { |
| 788 Label non_smi_shr_result; |
| 789 Register heap_number_map = r9; |
| 790 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); |
| 791 FloatingPointHelper::LoadAsIntegers(masm, non_numeric_failure, |
| 792 heap_number_map); |
| 793 switch (op) { |
| 794 case Token::BIT_OR: __ orl(rax, rcx); break; |
| 795 case Token::BIT_AND: __ andl(rax, rcx); break; |
| 796 case Token::BIT_XOR: __ xorl(rax, rcx); break; |
| 797 case Token::SAR: __ sarl_cl(rax); break; |
| 798 case Token::SHL: __ shll_cl(rax); break; |
| 799 case Token::SHR: { |
| 800 __ shrl_cl(rax); |
| 801 // Check if result is negative. This can only happen for a shift |
| 802 // by zero. |
| 803 __ testl(rax, rax); |
| 804 __ j(negative, &non_smi_shr_result); |
| 805 break; |
| 806 } |
| 807 default: UNREACHABLE(); |
| 808 } |
| 809 STATIC_ASSERT(kSmiValueSize == 32); |
| 810 // Tag smi result and return. |
| 811 __ Integer32ToSmi(rax, rax); |
| 812 __ Ret(); |
| 813 |
| 814 // Logical shift right can produce an unsigned int32 that is not |
| 815 // an int32, and so is not in the smi range. Allocate a heap number |
| 816 // in that case. |
| 817 if (op == Token::SHR) { |
| 818 __ bind(&non_smi_shr_result); |
| 819 Label allocation_failed; |
| 820 __ movl(rbx, rax); // rbx holds result value (uint32 value as int64). |
| 821 // Allocate heap number in new space. |
| 822 // Not using AllocateHeapNumber macro in order to reuse |
| 823 // already loaded heap_number_map. |
| 824 __ Allocate(HeapNumber::kSize, rax, rdx, no_reg, &allocation_failed, |
| 825 TAG_OBJECT); |
| 826 // Set the map. |
| 827 __ AssertRootValue(heap_number_map, |
| 828 Heap::kHeapNumberMapRootIndex, |
| 829 kHeapNumberMapRegisterClobbered); |
| 830 __ movq(FieldOperand(rax, HeapObject::kMapOffset), |
| 831 heap_number_map); |
| 832 __ cvtqsi2sd(xmm0, rbx); |
| 833 __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0); |
| 834 __ Ret(); |
| 835 |
| 836 __ bind(&allocation_failed); |
| 837 // We need tagged values in rdx and rax for the following code, |
| 838 // not int32 in rax and rcx. |
| 839 __ Integer32ToSmi(rax, rcx); |
| 840 __ Integer32ToSmi(rdx, rbx); |
| 841 __ jmp(allocation_failure); |
| 842 } |
| 843 break; |
| 844 } |
| 845 default: UNREACHABLE(); break; |
| 846 } |
| 847 // No fall-through from this generated code. |
| 848 if (FLAG_debug_code) { |
| 849 __ Abort(kUnexpectedFallThroughInBinaryStubGenerateFloatingPointCode); |
| 850 } |
| 851 } |
| 852 |
| 853 |
| 854 static void BinaryOpStub_GenerateRegisterArgsPushUnderReturn( |
| 855 MacroAssembler* masm) { |
| 856 // Push arguments, but ensure they are under the return address |
| 857 // for a tail call. |
| 858 __ PopReturnAddressTo(rcx); |
| 859 __ push(rdx); |
| 860 __ push(rax); |
| 861 __ PushReturnAddressFrom(rcx); |
| 862 } |
| 863 |
| 864 |
| 865 void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) { |
| 866 ASSERT(op_ == Token::ADD); |
| 867 Label left_not_string, call_runtime; |
| 868 |
| 869 // Registers containing left and right operands respectively. |
| 870 Register left = rdx; |
| 871 Register right = rax; |
| 872 |
| 873 // Test if left operand is a string. |
| 874 __ JumpIfSmi(left, &left_not_string, Label::kNear); |
| 875 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx); |
| 876 __ j(above_equal, &left_not_string, Label::kNear); |
| 877 StringAddStub string_add_left_stub( |
| 878 (StringAddFlags)(STRING_ADD_CHECK_RIGHT | STRING_ADD_ERECT_FRAME)); |
| 879 BinaryOpStub_GenerateRegisterArgsPushUnderReturn(masm); |
| 880 __ TailCallStub(&string_add_left_stub); |
| 881 |
| 882 // Left operand is not a string, test right. |
| 883 __ bind(&left_not_string); |
| 884 __ JumpIfSmi(right, &call_runtime, Label::kNear); |
| 885 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx); |
| 886 __ j(above_equal, &call_runtime, Label::kNear); |
| 887 |
| 888 StringAddStub string_add_right_stub( |
| 889 (StringAddFlags)(STRING_ADD_CHECK_LEFT | STRING_ADD_ERECT_FRAME)); |
| 890 BinaryOpStub_GenerateRegisterArgsPushUnderReturn(masm); |
| 891 __ TailCallStub(&string_add_right_stub); |
| 892 |
| 893 // Neither argument is a string. |
| 894 __ bind(&call_runtime); |
| 895 } |
| 896 |
| 897 |
| 898 void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) { |
| 899 Label right_arg_changed, call_runtime; |
| 900 |
| 901 if (op_ == Token::MOD && encoded_right_arg_.has_value) { |
| 902 // It is guaranteed that the value will fit into a Smi, because if it |
| 903 // didn't, we wouldn't be here, see BinaryOp_Patch. |
| 904 __ Cmp(rax, Smi::FromInt(fixed_right_arg_value())); |
| 905 __ j(not_equal, &right_arg_changed); |
| 906 } |
| 907 |
| 908 if (result_type_ == BinaryOpIC::UNINITIALIZED || |
| 909 result_type_ == BinaryOpIC::SMI) { |
| 910 // Only allow smi results. |
| 911 BinaryOpStub_GenerateSmiCode(masm, NULL, NO_HEAPNUMBER_RESULTS, op_); |
| 912 } else { |
| 913 // Allow heap number result and don't make a transition if a heap number |
| 914 // cannot be allocated. |
| 915 BinaryOpStub_GenerateSmiCode( |
| 916 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_); |
| 917 } |
| 918 |
| 919 // Code falls through if the result is not returned as either a smi or heap |
| 920 // number. |
| 921 __ bind(&right_arg_changed); |
| 922 GenerateTypeTransition(masm); |
| 923 |
| 924 if (call_runtime.is_linked()) { |
| 925 __ bind(&call_runtime); |
| 926 { |
| 927 FrameScope scope(masm, StackFrame::INTERNAL); |
| 928 GenerateRegisterArgsPush(masm); |
| 929 GenerateCallRuntime(masm); |
| 930 } |
| 931 __ Ret(); |
| 932 } |
| 933 } |
| 934 |
| 935 |
| 936 void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) { |
| 937 // The int32 case is identical to the Smi case. We avoid creating this |
| 938 // ic state on x64. |
| 939 UNREACHABLE(); |
| 940 } |
| 941 |
| 942 |
| 943 void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) { |
| 944 Label call_runtime; |
| 945 ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING); |
| 946 ASSERT(op_ == Token::ADD); |
| 947 // If both arguments are strings, call the string add stub. |
| 948 // Otherwise, do a transition. |
| 949 |
| 950 // Registers containing left and right operands respectively. |
| 951 Register left = rdx; |
| 952 Register right = rax; |
| 953 |
| 954 // Test if left operand is a string. |
| 955 __ JumpIfSmi(left, &call_runtime); |
| 956 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx); |
| 957 __ j(above_equal, &call_runtime); |
| 958 |
| 959 // Test if right operand is a string. |
| 960 __ JumpIfSmi(right, &call_runtime); |
| 961 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx); |
| 962 __ j(above_equal, &call_runtime); |
| 963 |
| 964 StringAddStub string_add_stub( |
| 965 (StringAddFlags)(STRING_ADD_CHECK_NONE | STRING_ADD_ERECT_FRAME)); |
| 966 BinaryOpStub_GenerateRegisterArgsPushUnderReturn(masm); |
| 967 __ TailCallStub(&string_add_stub); |
| 968 |
| 969 __ bind(&call_runtime); |
| 970 GenerateTypeTransition(masm); |
| 971 } |
| 972 |
| 973 |
| 974 void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) { |
| 975 Label call_runtime; |
| 976 |
| 977 if (op_ == Token::ADD) { |
| 978 // Handle string addition here, because it is the only operation |
| 979 // that does not do a ToNumber conversion on the operands. |
| 980 GenerateAddStrings(masm); |
| 981 } |
| 982 |
| 983 // Convert oddball arguments to numbers. |
| 984 Label check, done; |
| 985 __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex); |
| 986 __ j(not_equal, &check, Label::kNear); |
| 987 if (Token::IsBitOp(op_)) { |
| 988 __ xor_(rdx, rdx); |
| 989 } else { |
| 990 __ LoadRoot(rdx, Heap::kNanValueRootIndex); |
| 991 } |
| 992 __ jmp(&done, Label::kNear); |
| 993 __ bind(&check); |
| 994 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); |
| 995 __ j(not_equal, &done, Label::kNear); |
| 996 if (Token::IsBitOp(op_)) { |
| 997 __ xor_(rax, rax); |
| 998 } else { |
| 999 __ LoadRoot(rax, Heap::kNanValueRootIndex); |
| 1000 } |
| 1001 __ bind(&done); |
| 1002 |
| 1003 GenerateNumberStub(masm); |
| 1004 } |
| 1005 |
| 1006 |
| 1007 static void BinaryOpStub_CheckSmiInput(MacroAssembler* masm, |
| 1008 Register input, |
| 1009 Label* fail) { |
| 1010 Label ok; |
| 1011 __ JumpIfSmi(input, &ok, Label::kNear); |
| 1012 Register heap_number_map = r8; |
| 1013 Register scratch1 = r9; |
| 1014 Register scratch2 = r10; |
| 1015 // HeapNumbers containing 32bit integer values are also allowed. |
| 1016 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); |
| 1017 __ cmpq(FieldOperand(input, HeapObject::kMapOffset), heap_number_map); |
| 1018 __ j(not_equal, fail); |
| 1019 __ movsd(xmm0, FieldOperand(input, HeapNumber::kValueOffset)); |
| 1020 // Convert, convert back, and compare the two doubles' bits. |
| 1021 __ cvttsd2siq(scratch2, xmm0); |
| 1022 __ Cvtlsi2sd(xmm1, scratch2); |
| 1023 __ movq(scratch1, xmm0); |
| 1024 __ movq(scratch2, xmm1); |
| 1025 __ cmpq(scratch1, scratch2); |
| 1026 __ j(not_equal, fail); |
| 1027 __ bind(&ok); |
| 1028 } |
| 1029 |
| 1030 |
| 1031 void BinaryOpStub::GenerateNumberStub(MacroAssembler* masm) { |
| 1032 Label gc_required, not_number; |
| 1033 |
| 1034 // It could be that only SMIs have been seen at either the left |
| 1035 // or the right operand. For precise type feedback, patch the IC |
| 1036 // again if this changes. |
| 1037 if (left_type_ == BinaryOpIC::SMI) { |
| 1038 BinaryOpStub_CheckSmiInput(masm, rdx, ¬_number); |
| 1039 } |
| 1040 if (right_type_ == BinaryOpIC::SMI) { |
| 1041 BinaryOpStub_CheckSmiInput(masm, rax, ¬_number); |
| 1042 } |
| 1043 |
| 1044 BinaryOpStub_GenerateFloatingPointCode( |
| 1045 masm, &gc_required, ¬_number, op_, mode_); |
| 1046 |
| 1047 __ bind(¬_number); |
| 1048 GenerateTypeTransition(masm); |
| 1049 |
| 1050 __ bind(&gc_required); |
| 1051 { |
| 1052 FrameScope scope(masm, StackFrame::INTERNAL); |
| 1053 GenerateRegisterArgsPush(masm); |
| 1054 GenerateCallRuntime(masm); |
| 1055 } |
| 1056 __ Ret(); |
| 1057 } |
| 1058 |
| 1059 |
| 1060 void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) { |
| 1061 Label call_runtime, call_string_add_or_runtime; |
| 1062 |
| 1063 BinaryOpStub_GenerateSmiCode( |
| 1064 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_); |
| 1065 |
| 1066 BinaryOpStub_GenerateFloatingPointCode( |
| 1067 masm, &call_runtime, &call_string_add_or_runtime, op_, mode_); |
| 1068 |
| 1069 __ bind(&call_string_add_or_runtime); |
| 1070 if (op_ == Token::ADD) { |
| 1071 GenerateAddStrings(masm); |
| 1072 } |
| 1073 |
| 1074 __ bind(&call_runtime); |
| 1075 { |
| 1076 FrameScope scope(masm, StackFrame::INTERNAL); |
| 1077 GenerateRegisterArgsPush(masm); |
| 1078 GenerateCallRuntime(masm); |
| 1079 } |
| 1080 __ Ret(); |
| 1081 } |
| 1082 |
| 1083 |
| 1084 static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm, |
| 1085 Label* alloc_failure, |
| 1086 OverwriteMode mode) { |
| 1087 Label skip_allocation; |
| 1088 switch (mode) { |
| 1089 case OVERWRITE_LEFT: { |
| 1090 // If the argument in rdx is already an object, we skip the |
| 1091 // allocation of a heap number. |
| 1092 __ JumpIfNotSmi(rdx, &skip_allocation); |
| 1093 // Allocate a heap number for the result. Keep rax and rdx intact |
| 1094 // for the possible runtime call. |
| 1095 __ AllocateHeapNumber(rbx, rcx, alloc_failure); |
| 1096 // Now rdx can be overwritten losing one of the arguments as we are |
| 1097 // now done and will not need it any more. |
| 1098 __ movq(rdx, rbx); |
| 1099 __ bind(&skip_allocation); |
| 1100 // Use object in rdx as a result holder |
| 1101 __ movq(rax, rdx); |
| 1102 break; |
| 1103 } |
| 1104 case OVERWRITE_RIGHT: |
| 1105 // If the argument in rax is already an object, we skip the |
| 1106 // allocation of a heap number. |
| 1107 __ JumpIfNotSmi(rax, &skip_allocation); |
| 1108 // Fall through! |
| 1109 case NO_OVERWRITE: |
| 1110 // Allocate a heap number for the result. Keep rax and rdx intact |
| 1111 // for the possible runtime call. |
| 1112 __ AllocateHeapNumber(rbx, rcx, alloc_failure); |
| 1113 // Now rax can be overwritten losing one of the arguments as we are |
| 1114 // now done and will not need it any more. |
| 1115 __ movq(rax, rbx); |
| 1116 __ bind(&skip_allocation); |
| 1117 break; |
| 1118 default: UNREACHABLE(); |
| 1119 } |
| 1120 } |
| 1121 |
| 1122 |
| 1123 void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) { |
| 1124 __ push(rdx); |
| 1125 __ push(rax); |
| 1126 } |
| 1127 |
| 1128 |
551 void TranscendentalCacheStub::Generate(MacroAssembler* masm) { | 1129 void TranscendentalCacheStub::Generate(MacroAssembler* masm) { |
552 // TAGGED case: | 1130 // TAGGED case: |
553 // Input: | 1131 // Input: |
554 // rsp[8] : argument (should be number). | 1132 // rsp[8] : argument (should be number). |
555 // rsp[0] : return address. | 1133 // rsp[0] : return address. |
556 // Output: | 1134 // Output: |
557 // rax: tagged double result. | 1135 // rax: tagged double result. |
558 // UNTAGGED case: | 1136 // UNTAGGED case: |
559 // Input:: | 1137 // Input:: |
560 // rsp[0] : return address. | 1138 // rsp[0] : return address. |
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
847 __ bind(&done); | 1425 __ bind(&done); |
848 } else { | 1426 } else { |
849 ASSERT(type == TranscendentalCache::LOG); | 1427 ASSERT(type == TranscendentalCache::LOG); |
850 __ fldln2(); | 1428 __ fldln2(); |
851 __ fxch(); | 1429 __ fxch(); |
852 __ fyl2x(); | 1430 __ fyl2x(); |
853 } | 1431 } |
854 } | 1432 } |
855 | 1433 |
856 | 1434 |
| 1435 // Input: rdx, rax are the left and right objects of a bit op. |
| 1436 // Output: rax, rcx are left and right integers for a bit op. |
| 1437 // Jump to conversion_failure: rdx and rax are unchanged. |
| 1438 void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm, |
| 1439 Label* conversion_failure, |
| 1440 Register heap_number_map) { |
| 1441 // Check float operands. |
| 1442 Label arg1_is_object, check_undefined_arg1; |
| 1443 Label arg2_is_object, check_undefined_arg2; |
| 1444 Label load_arg2, done; |
| 1445 |
| 1446 __ JumpIfNotSmi(rdx, &arg1_is_object); |
| 1447 __ SmiToInteger32(r8, rdx); |
| 1448 __ jmp(&load_arg2); |
| 1449 |
| 1450 // If the argument is undefined it converts to zero (ECMA-262, section 9.5). |
| 1451 __ bind(&check_undefined_arg1); |
| 1452 __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex); |
| 1453 __ j(not_equal, conversion_failure); |
| 1454 __ Set(r8, 0); |
| 1455 __ jmp(&load_arg2); |
| 1456 |
| 1457 __ bind(&arg1_is_object); |
| 1458 __ cmpq(FieldOperand(rdx, HeapObject::kMapOffset), heap_number_map); |
| 1459 __ j(not_equal, &check_undefined_arg1); |
| 1460 // Get the untagged integer version of the rdx heap number in r8. |
| 1461 __ TruncateHeapNumberToI(r8, rdx); |
| 1462 |
| 1463 // Here r8 has the untagged integer, rax has a Smi or a heap number. |
| 1464 __ bind(&load_arg2); |
| 1465 // Test if arg2 is a Smi. |
| 1466 __ JumpIfNotSmi(rax, &arg2_is_object); |
| 1467 __ SmiToInteger32(rcx, rax); |
| 1468 __ jmp(&done); |
| 1469 |
| 1470 // If the argument is undefined it converts to zero (ECMA-262, section 9.5). |
| 1471 __ bind(&check_undefined_arg2); |
| 1472 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); |
| 1473 __ j(not_equal, conversion_failure); |
| 1474 __ Set(rcx, 0); |
| 1475 __ jmp(&done); |
| 1476 |
| 1477 __ bind(&arg2_is_object); |
| 1478 __ cmpq(FieldOperand(rax, HeapObject::kMapOffset), heap_number_map); |
| 1479 __ j(not_equal, &check_undefined_arg2); |
| 1480 // Get the untagged integer version of the rax heap number in rcx. |
| 1481 __ TruncateHeapNumberToI(rcx, rax); |
| 1482 |
| 1483 __ bind(&done); |
| 1484 __ movl(rax, r8); |
| 1485 } |
| 1486 |
| 1487 |
| 1488 void FloatingPointHelper::LoadSSE2SmiOperands(MacroAssembler* masm) { |
| 1489 __ SmiToInteger32(kScratchRegister, rdx); |
| 1490 __ Cvtlsi2sd(xmm0, kScratchRegister); |
| 1491 __ SmiToInteger32(kScratchRegister, rax); |
| 1492 __ Cvtlsi2sd(xmm1, kScratchRegister); |
| 1493 } |
| 1494 |
| 1495 |
857 void FloatingPointHelper::LoadSSE2UnknownOperands(MacroAssembler* masm, | 1496 void FloatingPointHelper::LoadSSE2UnknownOperands(MacroAssembler* masm, |
858 Label* not_numbers) { | 1497 Label* not_numbers) { |
859 Label load_smi_rdx, load_nonsmi_rax, load_smi_rax, load_float_rax, done; | 1498 Label load_smi_rdx, load_nonsmi_rax, load_smi_rax, load_float_rax, done; |
860 // Load operand in rdx into xmm0, or branch to not_numbers. | 1499 // Load operand in rdx into xmm0, or branch to not_numbers. |
861 __ LoadRoot(rcx, Heap::kHeapNumberMapRootIndex); | 1500 __ LoadRoot(rcx, Heap::kHeapNumberMapRootIndex); |
862 __ JumpIfSmi(rdx, &load_smi_rdx); | 1501 __ JumpIfSmi(rdx, &load_smi_rdx); |
863 __ cmpq(FieldOperand(rdx, HeapObject::kMapOffset), rcx); | 1502 __ cmpq(FieldOperand(rdx, HeapObject::kMapOffset), rcx); |
864 __ j(not_equal, not_numbers); // Argument in rdx is not a number. | 1503 __ j(not_equal, not_numbers); // Argument in rdx is not a number. |
865 __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset)); | 1504 __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset)); |
866 // Load operand in rax into xmm1, or branch to not_numbers. | 1505 // Load operand in rax into xmm1, or branch to not_numbers. |
(...skipping 10 matching lines...) Expand all Loading... |
877 __ Cvtlsi2sd(xmm0, kScratchRegister); | 1516 __ Cvtlsi2sd(xmm0, kScratchRegister); |
878 __ JumpIfNotSmi(rax, &load_nonsmi_rax); | 1517 __ JumpIfNotSmi(rax, &load_nonsmi_rax); |
879 | 1518 |
880 __ bind(&load_smi_rax); | 1519 __ bind(&load_smi_rax); |
881 __ SmiToInteger32(kScratchRegister, rax); | 1520 __ SmiToInteger32(kScratchRegister, rax); |
882 __ Cvtlsi2sd(xmm1, kScratchRegister); | 1521 __ Cvtlsi2sd(xmm1, kScratchRegister); |
883 __ bind(&done); | 1522 __ bind(&done); |
884 } | 1523 } |
885 | 1524 |
886 | 1525 |
| 1526 void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm, |
| 1527 Register first, |
| 1528 Register second, |
| 1529 Register scratch1, |
| 1530 Register scratch2, |
| 1531 Register scratch3, |
| 1532 Label* on_success, |
| 1533 Label* on_not_smis, |
| 1534 ConvertUndefined convert_undefined) { |
| 1535 Register heap_number_map = scratch3; |
| 1536 Register smi_result = scratch1; |
| 1537 Label done, maybe_undefined_first, maybe_undefined_second, first_done; |
| 1538 |
| 1539 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); |
| 1540 |
| 1541 Label first_smi; |
| 1542 __ JumpIfSmi(first, &first_smi, Label::kNear); |
| 1543 __ cmpq(FieldOperand(first, HeapObject::kMapOffset), heap_number_map); |
| 1544 __ j(not_equal, |
| 1545 (convert_undefined == CONVERT_UNDEFINED_TO_ZERO) |
| 1546 ? &maybe_undefined_first |
| 1547 : on_not_smis); |
| 1548 // Convert HeapNumber to smi if possible. |
| 1549 __ movsd(xmm0, FieldOperand(first, HeapNumber::kValueOffset)); |
| 1550 __ movq(scratch2, xmm0); |
| 1551 __ cvttsd2siq(smi_result, xmm0); |
| 1552 // Check if conversion was successful by converting back and |
| 1553 // comparing to the original double's bits. |
| 1554 __ Cvtlsi2sd(xmm1, smi_result); |
| 1555 __ movq(kScratchRegister, xmm1); |
| 1556 __ cmpq(scratch2, kScratchRegister); |
| 1557 __ j(not_equal, on_not_smis); |
| 1558 __ Integer32ToSmi(first, smi_result); |
| 1559 |
| 1560 __ bind(&first_done); |
| 1561 __ JumpIfSmi(second, (on_success != NULL) ? on_success : &done); |
| 1562 __ bind(&first_smi); |
| 1563 __ AssertNotSmi(second); |
| 1564 __ cmpq(FieldOperand(second, HeapObject::kMapOffset), heap_number_map); |
| 1565 __ j(not_equal, |
| 1566 (convert_undefined == CONVERT_UNDEFINED_TO_ZERO) |
| 1567 ? &maybe_undefined_second |
| 1568 : on_not_smis); |
| 1569 // Convert second to smi, if possible. |
| 1570 __ movsd(xmm0, FieldOperand(second, HeapNumber::kValueOffset)); |
| 1571 __ movq(scratch2, xmm0); |
| 1572 __ cvttsd2siq(smi_result, xmm0); |
| 1573 __ Cvtlsi2sd(xmm1, smi_result); |
| 1574 __ movq(kScratchRegister, xmm1); |
| 1575 __ cmpq(scratch2, kScratchRegister); |
| 1576 __ j(not_equal, on_not_smis); |
| 1577 __ Integer32ToSmi(second, smi_result); |
| 1578 if (on_success != NULL) { |
| 1579 __ jmp(on_success); |
| 1580 } else { |
| 1581 __ jmp(&done); |
| 1582 } |
| 1583 |
| 1584 __ bind(&maybe_undefined_first); |
| 1585 __ CompareRoot(first, Heap::kUndefinedValueRootIndex); |
| 1586 __ j(not_equal, on_not_smis); |
| 1587 __ xor_(first, first); |
| 1588 __ jmp(&first_done); |
| 1589 |
| 1590 __ bind(&maybe_undefined_second); |
| 1591 __ CompareRoot(second, Heap::kUndefinedValueRootIndex); |
| 1592 __ j(not_equal, on_not_smis); |
| 1593 __ xor_(second, second); |
| 1594 if (on_success != NULL) { |
| 1595 __ jmp(on_success); |
| 1596 } |
| 1597 // Else: fall through. |
| 1598 |
| 1599 __ bind(&done); |
| 1600 } |
| 1601 |
| 1602 |
887 void MathPowStub::Generate(MacroAssembler* masm) { | 1603 void MathPowStub::Generate(MacroAssembler* masm) { |
888 const Register exponent = rdx; | 1604 const Register exponent = rdx; |
889 const Register base = rax; | 1605 const Register base = rax; |
890 const Register scratch = rcx; | 1606 const Register scratch = rcx; |
891 const XMMRegister double_result = xmm3; | 1607 const XMMRegister double_result = xmm3; |
892 const XMMRegister double_base = xmm2; | 1608 const XMMRegister double_base = xmm2; |
893 const XMMRegister double_exponent = xmm1; | 1609 const XMMRegister double_exponent = xmm1; |
894 const XMMRegister double_scratch = xmm4; | 1610 const XMMRegister double_scratch = xmm4; |
895 | 1611 |
896 Label call_runtime, done, exponent_not_smi, int_exponent; | 1612 Label call_runtime, done, exponent_not_smi, int_exponent; |
(...skipping 1846 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2743 | 3459 |
2744 | 3460 |
2745 void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { | 3461 void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { |
2746 CEntryStub::GenerateAheadOfTime(isolate); | 3462 CEntryStub::GenerateAheadOfTime(isolate); |
2747 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate); | 3463 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate); |
2748 StubFailureTrampolineStub::GenerateAheadOfTime(isolate); | 3464 StubFailureTrampolineStub::GenerateAheadOfTime(isolate); |
2749 // It is important that the store buffer overflow stubs are generated first. | 3465 // It is important that the store buffer overflow stubs are generated first. |
2750 RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate); | 3466 RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate); |
2751 ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); | 3467 ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); |
2752 CreateAllocationSiteStub::GenerateAheadOfTime(isolate); | 3468 CreateAllocationSiteStub::GenerateAheadOfTime(isolate); |
2753 BinaryOpStub::GenerateAheadOfTime(isolate); | |
2754 } | 3469 } |
2755 | 3470 |
2756 | 3471 |
2757 void CodeStub::GenerateFPStubs(Isolate* isolate) { | 3472 void CodeStub::GenerateFPStubs(Isolate* isolate) { |
2758 } | 3473 } |
2759 | 3474 |
2760 | 3475 |
2761 void CEntryStub::GenerateAheadOfTime(Isolate* isolate) { | 3476 void CEntryStub::GenerateAheadOfTime(Isolate* isolate) { |
2762 CEntryStub stub(1, kDontSaveFPRegs); | 3477 CEntryStub stub(1, kDontSaveFPRegs); |
2763 stub.GetCode(isolate)->set_is_pregenerated(true); | 3478 stub.GetCode(isolate)->set_is_pregenerated(true); |
(...skipping 3061 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5825 __ bind(&fast_elements_case); | 6540 __ bind(&fast_elements_case); |
5826 GenerateCase(masm, FAST_ELEMENTS); | 6541 GenerateCase(masm, FAST_ELEMENTS); |
5827 } | 6542 } |
5828 | 6543 |
5829 | 6544 |
5830 #undef __ | 6545 #undef __ |
5831 | 6546 |
5832 } } // namespace v8::internal | 6547 } } // namespace v8::internal |
5833 | 6548 |
5834 #endif // V8_TARGET_ARCH_X64 | 6549 #endif // V8_TARGET_ARCH_X64 |
OLD | NEW |