| OLD | NEW |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 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 275 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 286 Isolate* isolate, | 286 Isolate* isolate, |
| 287 CodeStubInterfaceDescriptor* descriptor) { | 287 CodeStubInterfaceDescriptor* descriptor) { |
| 288 static Register registers[] = { eax, ebx, ecx, edx }; | 288 static Register registers[] = { eax, ebx, ecx, edx }; |
| 289 descriptor->register_param_count_ = 4; | 289 descriptor->register_param_count_ = 4; |
| 290 descriptor->register_params_ = registers; | 290 descriptor->register_params_ = registers; |
| 291 descriptor->deoptimization_handler_ = | 291 descriptor->deoptimization_handler_ = |
| 292 FUNCTION_ADDR(ElementsTransitionAndStoreIC_Miss); | 292 FUNCTION_ADDR(ElementsTransitionAndStoreIC_Miss); |
| 293 } | 293 } |
| 294 | 294 |
| 295 | 295 |
| 296 void BinaryOpStub::InitializeInterfaceDescriptor( |
| 297 Isolate* isolate, |
| 298 CodeStubInterfaceDescriptor* descriptor) { |
| 299 static Register registers[] = { edx, eax }; |
| 300 descriptor->register_param_count_ = 2; |
| 301 descriptor->register_params_ = registers; |
| 302 descriptor->deoptimization_handler_ = FUNCTION_ADDR(BinaryOpIC_Miss); |
| 303 descriptor->SetMissHandler( |
| 304 ExternalReference(IC_Utility(IC::kBinaryOpIC_Miss), isolate)); |
| 305 } |
| 306 |
| 307 |
| 296 #define __ ACCESS_MASM(masm) | 308 #define __ ACCESS_MASM(masm) |
| 297 | 309 |
| 298 | 310 |
| 299 void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) { | 311 void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) { |
| 300 // Update the static counter each time a new code stub is generated. | 312 // Update the static counter each time a new code stub is generated. |
| 301 Isolate* isolate = masm->isolate(); | 313 Isolate* isolate = masm->isolate(); |
| 302 isolate->counters()->code_stubs()->Increment(); | 314 isolate->counters()->code_stubs()->Increment(); |
| 303 | 315 |
| 304 CodeStubInterfaceDescriptor* descriptor = GetInterfaceDescriptor(isolate); | 316 CodeStubInterfaceDescriptor* descriptor = GetInterfaceDescriptor(isolate); |
| 305 int param_count = descriptor->register_param_count_; | 317 int param_count = descriptor->register_param_count_; |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 473 ARGS_ON_STACK, | 485 ARGS_ON_STACK, |
| 474 ARGS_IN_REGISTERS | 486 ARGS_IN_REGISTERS |
| 475 }; | 487 }; |
| 476 | 488 |
| 477 // Code pattern for loading a floating point value. Input value must | 489 // Code pattern for loading a floating point value. Input value must |
| 478 // be either a smi or a heap number object (fp value). Requirements: | 490 // be either a smi or a heap number object (fp value). Requirements: |
| 479 // operand in register number. Returns operand as floating point number | 491 // operand in register number. Returns operand as floating point number |
| 480 // on FPU stack. | 492 // on FPU stack. |
| 481 static void LoadFloatOperand(MacroAssembler* masm, Register number); | 493 static void LoadFloatOperand(MacroAssembler* masm, Register number); |
| 482 | 494 |
| 483 // Code pattern for loading floating point values. Input values must | |
| 484 // be either smi or heap number objects (fp values). Requirements: | |
| 485 // operand_1 on TOS+1 or in edx, operand_2 on TOS+2 or in eax. | |
| 486 // Returns operands as floating point numbers on FPU stack. | |
| 487 static void LoadFloatOperands(MacroAssembler* masm, | |
| 488 Register scratch, | |
| 489 ArgLocation arg_location = ARGS_ON_STACK); | |
| 490 | |
| 491 // Similar to LoadFloatOperand but assumes that both operands are smis. | |
| 492 // Expects operands in edx, eax. | |
| 493 static void LoadFloatSmis(MacroAssembler* masm, Register scratch); | |
| 494 | |
| 495 // Test if operands are smi or number objects (fp). Requirements: | 495 // Test if operands are smi or number objects (fp). Requirements: |
| 496 // operand_1 in eax, operand_2 in edx; falls through on float | 496 // operand_1 in eax, operand_2 in edx; falls through on float |
| 497 // operands, jumps to the non_float label otherwise. | 497 // operands, jumps to the non_float label otherwise. |
| 498 static void CheckFloatOperands(MacroAssembler* masm, | 498 static void CheckFloatOperands(MacroAssembler* masm, |
| 499 Label* non_float, | 499 Label* non_float, |
| 500 Register scratch); | 500 Register scratch); |
| 501 | 501 |
| 502 // Takes the operands in edx and eax and loads them as integers in eax | |
| 503 // and ecx. | |
| 504 static void LoadUnknownsAsIntegers(MacroAssembler* masm, | |
| 505 bool use_sse3, | |
| 506 BinaryOpIC::TypeInfo left_type, | |
| 507 BinaryOpIC::TypeInfo right_type, | |
| 508 Label* operand_conversion_failure); | |
| 509 | |
| 510 // Test if operands are numbers (smi or HeapNumber objects), and load | 502 // Test if operands are numbers (smi or HeapNumber objects), and load |
| 511 // them into xmm0 and xmm1 if they are. Jump to label not_numbers if | 503 // them into xmm0 and xmm1 if they are. Jump to label not_numbers if |
| 512 // either operand is not a number. Operands are in edx and eax. | 504 // either operand is not a number. Operands are in edx and eax. |
| 513 // Leaves operands unchanged. | 505 // Leaves operands unchanged. |
| 514 static void LoadSSE2Operands(MacroAssembler* masm, Label* not_numbers); | 506 static void LoadSSE2Operands(MacroAssembler* masm, Label* not_numbers); |
| 515 | |
| 516 // Similar to LoadSSE2Operands but assumes that both operands are smis. | |
| 517 // Expects operands in edx, eax. | |
| 518 static void LoadSSE2Smis(MacroAssembler* masm, Register scratch); | |
| 519 | |
| 520 // Checks that |operand| has an int32 value. If |int32_result| is different | |
| 521 // from |scratch|, it will contain that int32 value. | |
| 522 static void CheckSSE2OperandIsInt32(MacroAssembler* masm, | |
| 523 Label* non_int32, | |
| 524 XMMRegister operand, | |
| 525 Register int32_result, | |
| 526 Register scratch, | |
| 527 XMMRegister xmm_scratch); | |
| 528 }; | 507 }; |
| 529 | 508 |
| 530 | 509 |
| 531 void DoubleToIStub::Generate(MacroAssembler* masm) { | 510 void DoubleToIStub::Generate(MacroAssembler* masm) { |
| 532 Register input_reg = this->source(); | 511 Register input_reg = this->source(); |
| 533 Register final_result_reg = this->destination(); | 512 Register final_result_reg = this->destination(); |
| 534 ASSERT(is_truncating()); | 513 ASSERT(is_truncating()); |
| 535 | 514 |
| 536 Label check_negative, process_64_bits, done, done_no_stash; | 515 Label check_negative, process_64_bits, done, done_no_stash; |
| 537 | 516 |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 661 if (!final_result_reg.is(result_reg)) { | 640 if (!final_result_reg.is(result_reg)) { |
| 662 ASSERT(final_result_reg.is(ecx)); | 641 ASSERT(final_result_reg.is(ecx)); |
| 663 __ mov(final_result_reg, result_reg); | 642 __ mov(final_result_reg, result_reg); |
| 664 } | 643 } |
| 665 __ pop(save_reg); | 644 __ pop(save_reg); |
| 666 __ pop(scratch1); | 645 __ pop(scratch1); |
| 667 __ ret(0); | 646 __ ret(0); |
| 668 } | 647 } |
| 669 | 648 |
| 670 | 649 |
| 671 void BinaryOpStub::Initialize() { | |
| 672 platform_specific_bit_ = CpuFeatures::IsSupported(SSE3); | |
| 673 } | |
| 674 | |
| 675 | |
| 676 void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { | |
| 677 __ pop(ecx); // Save return address. | |
| 678 __ push(edx); | |
| 679 __ push(eax); | |
| 680 // Left and right arguments are now on top. | |
| 681 __ push(Immediate(Smi::FromInt(MinorKey()))); | |
| 682 | |
| 683 __ push(ecx); // Push return address. | |
| 684 | |
| 685 // Patch the caller to an appropriate specialized stub and return the | |
| 686 // operation result to the caller of the stub. | |
| 687 __ TailCallExternalReference( | |
| 688 ExternalReference(IC_Utility(IC::kBinaryOp_Patch), | |
| 689 masm->isolate()), | |
| 690 3, | |
| 691 1); | |
| 692 } | |
| 693 | |
| 694 | |
| 695 // Prepare for a type transition runtime call when the args are already on | |
| 696 // the stack, under the return address. | |
| 697 void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm) { | |
| 698 __ pop(ecx); // Save return address. | |
| 699 // Left and right arguments are already on top of the stack. | |
| 700 __ push(Immediate(Smi::FromInt(MinorKey()))); | |
| 701 | |
| 702 __ push(ecx); // Push return address. | |
| 703 | |
| 704 // Patch the caller to an appropriate specialized stub and return the | |
| 705 // operation result to the caller of the stub. | |
| 706 __ TailCallExternalReference( | |
| 707 ExternalReference(IC_Utility(IC::kBinaryOp_Patch), | |
| 708 masm->isolate()), | |
| 709 3, | |
| 710 1); | |
| 711 } | |
| 712 | |
| 713 | |
| 714 static void BinaryOpStub_GenerateRegisterArgsPop(MacroAssembler* masm) { | |
| 715 __ pop(ecx); | |
| 716 __ pop(eax); | |
| 717 __ pop(edx); | |
| 718 __ push(ecx); | |
| 719 } | |
| 720 | |
| 721 | |
| 722 static void BinaryOpStub_GenerateSmiCode( | |
| 723 MacroAssembler* masm, | |
| 724 Label* slow, | |
| 725 BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results, | |
| 726 Token::Value op) { | |
| 727 // 1. Move arguments into edx, eax except for DIV and MOD, which need the | |
| 728 // dividend in eax and edx free for the division. Use eax, ebx for those. | |
| 729 Comment load_comment(masm, "-- Load arguments"); | |
| 730 Register left = edx; | |
| 731 Register right = eax; | |
| 732 if (op == Token::DIV || op == Token::MOD) { | |
| 733 left = eax; | |
| 734 right = ebx; | |
| 735 __ mov(ebx, eax); | |
| 736 __ mov(eax, edx); | |
| 737 } | |
| 738 | |
| 739 | |
| 740 // 2. Prepare the smi check of both operands by oring them together. | |
| 741 Comment smi_check_comment(masm, "-- Smi check arguments"); | |
| 742 Label not_smis; | |
| 743 Register combined = ecx; | |
| 744 ASSERT(!left.is(combined) && !right.is(combined)); | |
| 745 switch (op) { | |
| 746 case Token::BIT_OR: | |
| 747 // Perform the operation into eax and smi check the result. Preserve | |
| 748 // eax in case the result is not a smi. | |
| 749 ASSERT(!left.is(ecx) && !right.is(ecx)); | |
| 750 __ mov(ecx, right); | |
| 751 __ or_(right, left); // Bitwise or is commutative. | |
| 752 combined = right; | |
| 753 break; | |
| 754 | |
| 755 case Token::BIT_XOR: | |
| 756 case Token::BIT_AND: | |
| 757 case Token::ADD: | |
| 758 case Token::SUB: | |
| 759 case Token::MUL: | |
| 760 case Token::DIV: | |
| 761 case Token::MOD: | |
| 762 __ mov(combined, right); | |
| 763 __ or_(combined, left); | |
| 764 break; | |
| 765 | |
| 766 case Token::SHL: | |
| 767 case Token::SAR: | |
| 768 case Token::SHR: | |
| 769 // Move the right operand into ecx for the shift operation, use eax | |
| 770 // for the smi check register. | |
| 771 ASSERT(!left.is(ecx) && !right.is(ecx)); | |
| 772 __ mov(ecx, right); | |
| 773 __ or_(right, left); | |
| 774 combined = right; | |
| 775 break; | |
| 776 | |
| 777 default: | |
| 778 break; | |
| 779 } | |
| 780 | |
| 781 // 3. Perform the smi check of the operands. | |
| 782 STATIC_ASSERT(kSmiTag == 0); // Adjust zero check if not the case. | |
| 783 __ JumpIfNotSmi(combined, ¬_smis); | |
| 784 | |
| 785 // 4. Operands are both smis, perform the operation leaving the result in | |
| 786 // eax and check the result if necessary. | |
| 787 Comment perform_smi(masm, "-- Perform smi operation"); | |
| 788 Label use_fp_on_smis; | |
| 789 switch (op) { | |
| 790 case Token::BIT_OR: | |
| 791 // Nothing to do. | |
| 792 break; | |
| 793 | |
| 794 case Token::BIT_XOR: | |
| 795 ASSERT(right.is(eax)); | |
| 796 __ xor_(right, left); // Bitwise xor is commutative. | |
| 797 break; | |
| 798 | |
| 799 case Token::BIT_AND: | |
| 800 ASSERT(right.is(eax)); | |
| 801 __ and_(right, left); // Bitwise and is commutative. | |
| 802 break; | |
| 803 | |
| 804 case Token::SHL: | |
| 805 // Remove tags from operands (but keep sign). | |
| 806 __ SmiUntag(left); | |
| 807 __ SmiUntag(ecx); | |
| 808 // Perform the operation. | |
| 809 __ shl_cl(left); | |
| 810 // Check that the *signed* result fits in a smi. | |
| 811 __ cmp(left, 0xc0000000); | |
| 812 __ j(sign, &use_fp_on_smis); | |
| 813 // Tag the result and store it in register eax. | |
| 814 __ SmiTag(left); | |
| 815 __ mov(eax, left); | |
| 816 break; | |
| 817 | |
| 818 case Token::SAR: | |
| 819 // Remove tags from operands (but keep sign). | |
| 820 __ SmiUntag(left); | |
| 821 __ SmiUntag(ecx); | |
| 822 // Perform the operation. | |
| 823 __ sar_cl(left); | |
| 824 // Tag the result and store it in register eax. | |
| 825 __ SmiTag(left); | |
| 826 __ mov(eax, left); | |
| 827 break; | |
| 828 | |
| 829 case Token::SHR: | |
| 830 // Remove tags from operands (but keep sign). | |
| 831 __ SmiUntag(left); | |
| 832 __ SmiUntag(ecx); | |
| 833 // Perform the operation. | |
| 834 __ shr_cl(left); | |
| 835 // Check that the *unsigned* result fits in a smi. | |
| 836 // Neither of the two high-order bits can be set: | |
| 837 // - 0x80000000: high bit would be lost when smi tagging. | |
| 838 // - 0x40000000: this number would convert to negative when | |
| 839 // Smi tagging these two cases can only happen with shifts | |
| 840 // by 0 or 1 when handed a valid smi. | |
| 841 __ test(left, Immediate(0xc0000000)); | |
| 842 __ j(not_zero, &use_fp_on_smis); | |
| 843 // Tag the result and store it in register eax. | |
| 844 __ SmiTag(left); | |
| 845 __ mov(eax, left); | |
| 846 break; | |
| 847 | |
| 848 case Token::ADD: | |
| 849 ASSERT(right.is(eax)); | |
| 850 __ add(right, left); // Addition is commutative. | |
| 851 __ j(overflow, &use_fp_on_smis); | |
| 852 break; | |
| 853 | |
| 854 case Token::SUB: | |
| 855 __ sub(left, right); | |
| 856 __ j(overflow, &use_fp_on_smis); | |
| 857 __ mov(eax, left); | |
| 858 break; | |
| 859 | |
| 860 case Token::MUL: | |
| 861 // If the smi tag is 0 we can just leave the tag on one operand. | |
| 862 STATIC_ASSERT(kSmiTag == 0); // Adjust code below if not the case. | |
| 863 // We can't revert the multiplication if the result is not a smi | |
| 864 // so save the right operand. | |
| 865 __ mov(ebx, right); | |
| 866 // Remove tag from one of the operands (but keep sign). | |
| 867 __ SmiUntag(right); | |
| 868 // Do multiplication. | |
| 869 __ imul(right, left); // Multiplication is commutative. | |
| 870 __ j(overflow, &use_fp_on_smis); | |
| 871 // Check for negative zero result. Use combined = left | right. | |
| 872 __ NegativeZeroTest(right, combined, &use_fp_on_smis); | |
| 873 break; | |
| 874 | |
| 875 case Token::DIV: | |
| 876 // We can't revert the division if the result is not a smi so | |
| 877 // save the left operand. | |
| 878 __ mov(edi, left); | |
| 879 // Check for 0 divisor. | |
| 880 __ test(right, right); | |
| 881 __ j(zero, &use_fp_on_smis); | |
| 882 // Sign extend left into edx:eax. | |
| 883 ASSERT(left.is(eax)); | |
| 884 __ cdq(); | |
| 885 // Divide edx:eax by right. | |
| 886 __ idiv(right); | |
| 887 // Check for the corner case of dividing the most negative smi by | |
| 888 // -1. We cannot use the overflow flag, since it is not set by idiv | |
| 889 // instruction. | |
| 890 STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); | |
| 891 __ cmp(eax, 0x40000000); | |
| 892 __ j(equal, &use_fp_on_smis); | |
| 893 // Check for negative zero result. Use combined = left | right. | |
| 894 __ NegativeZeroTest(eax, combined, &use_fp_on_smis); | |
| 895 // Check that the remainder is zero. | |
| 896 __ test(edx, edx); | |
| 897 __ j(not_zero, &use_fp_on_smis); | |
| 898 // Tag the result and store it in register eax. | |
| 899 __ SmiTag(eax); | |
| 900 break; | |
| 901 | |
| 902 case Token::MOD: | |
| 903 // Check for 0 divisor. | |
| 904 __ test(right, right); | |
| 905 __ j(zero, ¬_smis); | |
| 906 | |
| 907 // Sign extend left into edx:eax. | |
| 908 ASSERT(left.is(eax)); | |
| 909 __ cdq(); | |
| 910 // Divide edx:eax by right. | |
| 911 __ idiv(right); | |
| 912 // Check for negative zero result. Use combined = left | right. | |
| 913 __ NegativeZeroTest(edx, combined, slow); | |
| 914 // Move remainder to register eax. | |
| 915 __ mov(eax, edx); | |
| 916 break; | |
| 917 | |
| 918 default: | |
| 919 UNREACHABLE(); | |
| 920 } | |
| 921 | |
| 922 // 5. Emit return of result in eax. Some operations have registers pushed. | |
| 923 switch (op) { | |
| 924 case Token::ADD: | |
| 925 case Token::SUB: | |
| 926 case Token::MUL: | |
| 927 case Token::DIV: | |
| 928 __ ret(0); | |
| 929 break; | |
| 930 case Token::MOD: | |
| 931 case Token::BIT_OR: | |
| 932 case Token::BIT_AND: | |
| 933 case Token::BIT_XOR: | |
| 934 case Token::SAR: | |
| 935 case Token::SHL: | |
| 936 case Token::SHR: | |
| 937 __ ret(2 * kPointerSize); | |
| 938 break; | |
| 939 default: | |
| 940 UNREACHABLE(); | |
| 941 } | |
| 942 | |
| 943 // 6. For some operations emit inline code to perform floating point | |
| 944 // operations on known smis (e.g., if the result of the operation | |
| 945 // overflowed the smi range). | |
| 946 if (allow_heapnumber_results == BinaryOpStub::NO_HEAPNUMBER_RESULTS) { | |
| 947 __ bind(&use_fp_on_smis); | |
| 948 switch (op) { | |
| 949 // Undo the effects of some operations, and some register moves. | |
| 950 case Token::SHL: | |
| 951 // The arguments are saved on the stack, and only used from there. | |
| 952 break; | |
| 953 case Token::ADD: | |
| 954 // Revert right = right + left. | |
| 955 __ sub(right, left); | |
| 956 break; | |
| 957 case Token::SUB: | |
| 958 // Revert left = left - right. | |
| 959 __ add(left, right); | |
| 960 break; | |
| 961 case Token::MUL: | |
| 962 // Right was clobbered but a copy is in ebx. | |
| 963 __ mov(right, ebx); | |
| 964 break; | |
| 965 case Token::DIV: | |
| 966 // Left was clobbered but a copy is in edi. Right is in ebx for | |
| 967 // division. They should be in eax, ebx for jump to not_smi. | |
| 968 __ mov(eax, edi); | |
| 969 break; | |
| 970 default: | |
| 971 // No other operators jump to use_fp_on_smis. | |
| 972 break; | |
| 973 } | |
| 974 __ jmp(¬_smis); | |
| 975 } else { | |
| 976 ASSERT(allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS); | |
| 977 switch (op) { | |
| 978 case Token::SHL: | |
| 979 case Token::SHR: { | |
| 980 Comment perform_float(masm, "-- Perform float operation on smis"); | |
| 981 __ bind(&use_fp_on_smis); | |
| 982 // Result we want is in left == edx, so we can put the allocated heap | |
| 983 // number in eax. | |
| 984 __ AllocateHeapNumber(eax, ecx, ebx, slow); | |
| 985 // Store the result in the HeapNumber and return. | |
| 986 // It's OK to overwrite the arguments on the stack because we | |
| 987 // are about to return. | |
| 988 if (op == Token::SHR) { | |
| 989 __ mov(Operand(esp, 1 * kPointerSize), left); | |
| 990 __ mov(Operand(esp, 2 * kPointerSize), Immediate(0)); | |
| 991 __ fild_d(Operand(esp, 1 * kPointerSize)); | |
| 992 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | |
| 993 } else { | |
| 994 ASSERT_EQ(Token::SHL, op); | |
| 995 if (CpuFeatures::IsSupported(SSE2)) { | |
| 996 CpuFeatureScope use_sse2(masm, SSE2); | |
| 997 __ Cvtsi2sd(xmm0, left); | |
| 998 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); | |
| 999 } else { | |
| 1000 __ mov(Operand(esp, 1 * kPointerSize), left); | |
| 1001 __ fild_s(Operand(esp, 1 * kPointerSize)); | |
| 1002 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | |
| 1003 } | |
| 1004 } | |
| 1005 __ ret(2 * kPointerSize); | |
| 1006 break; | |
| 1007 } | |
| 1008 | |
| 1009 case Token::ADD: | |
| 1010 case Token::SUB: | |
| 1011 case Token::MUL: | |
| 1012 case Token::DIV: { | |
| 1013 Comment perform_float(masm, "-- Perform float operation on smis"); | |
| 1014 __ bind(&use_fp_on_smis); | |
| 1015 // Restore arguments to edx, eax. | |
| 1016 switch (op) { | |
| 1017 case Token::ADD: | |
| 1018 // Revert right = right + left. | |
| 1019 __ sub(right, left); | |
| 1020 break; | |
| 1021 case Token::SUB: | |
| 1022 // Revert left = left - right. | |
| 1023 __ add(left, right); | |
| 1024 break; | |
| 1025 case Token::MUL: | |
| 1026 // Right was clobbered but a copy is in ebx. | |
| 1027 __ mov(right, ebx); | |
| 1028 break; | |
| 1029 case Token::DIV: | |
| 1030 // Left was clobbered but a copy is in edi. Right is in ebx for | |
| 1031 // division. | |
| 1032 __ mov(edx, edi); | |
| 1033 __ mov(eax, right); | |
| 1034 break; | |
| 1035 default: UNREACHABLE(); | |
| 1036 break; | |
| 1037 } | |
| 1038 __ AllocateHeapNumber(ecx, ebx, no_reg, slow); | |
| 1039 if (CpuFeatures::IsSupported(SSE2)) { | |
| 1040 CpuFeatureScope use_sse2(masm, SSE2); | |
| 1041 FloatingPointHelper::LoadSSE2Smis(masm, ebx); | |
| 1042 switch (op) { | |
| 1043 case Token::ADD: __ addsd(xmm0, xmm1); break; | |
| 1044 case Token::SUB: __ subsd(xmm0, xmm1); break; | |
| 1045 case Token::MUL: __ mulsd(xmm0, xmm1); break; | |
| 1046 case Token::DIV: __ divsd(xmm0, xmm1); break; | |
| 1047 default: UNREACHABLE(); | |
| 1048 } | |
| 1049 __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm0); | |
| 1050 } else { // SSE2 not available, use FPU. | |
| 1051 FloatingPointHelper::LoadFloatSmis(masm, ebx); | |
| 1052 switch (op) { | |
| 1053 case Token::ADD: __ faddp(1); break; | |
| 1054 case Token::SUB: __ fsubp(1); break; | |
| 1055 case Token::MUL: __ fmulp(1); break; | |
| 1056 case Token::DIV: __ fdivp(1); break; | |
| 1057 default: UNREACHABLE(); | |
| 1058 } | |
| 1059 __ fstp_d(FieldOperand(ecx, HeapNumber::kValueOffset)); | |
| 1060 } | |
| 1061 __ mov(eax, ecx); | |
| 1062 __ ret(0); | |
| 1063 break; | |
| 1064 } | |
| 1065 | |
| 1066 default: | |
| 1067 break; | |
| 1068 } | |
| 1069 } | |
| 1070 | |
| 1071 // 7. Non-smi operands, fall out to the non-smi code with the operands in | |
| 1072 // edx and eax. | |
| 1073 Comment done_comment(masm, "-- Enter non-smi code"); | |
| 1074 __ bind(¬_smis); | |
| 1075 switch (op) { | |
| 1076 case Token::BIT_OR: | |
| 1077 case Token::SHL: | |
| 1078 case Token::SAR: | |
| 1079 case Token::SHR: | |
| 1080 // Right operand is saved in ecx and eax was destroyed by the smi | |
| 1081 // check. | |
| 1082 __ mov(eax, ecx); | |
| 1083 break; | |
| 1084 | |
| 1085 case Token::DIV: | |
| 1086 case Token::MOD: | |
| 1087 // Operands are in eax, ebx at this point. | |
| 1088 __ mov(edx, eax); | |
| 1089 __ mov(eax, ebx); | |
| 1090 break; | |
| 1091 | |
| 1092 default: | |
| 1093 break; | |
| 1094 } | |
| 1095 } | |
| 1096 | |
| 1097 | |
| 1098 void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) { | |
| 1099 Label right_arg_changed, call_runtime; | |
| 1100 | |
| 1101 switch (op_) { | |
| 1102 case Token::ADD: | |
| 1103 case Token::SUB: | |
| 1104 case Token::MUL: | |
| 1105 case Token::DIV: | |
| 1106 break; | |
| 1107 case Token::MOD: | |
| 1108 case Token::BIT_OR: | |
| 1109 case Token::BIT_AND: | |
| 1110 case Token::BIT_XOR: | |
| 1111 case Token::SAR: | |
| 1112 case Token::SHL: | |
| 1113 case Token::SHR: | |
| 1114 GenerateRegisterArgsPush(masm); | |
| 1115 break; | |
| 1116 default: | |
| 1117 UNREACHABLE(); | |
| 1118 } | |
| 1119 | |
| 1120 if (op_ == Token::MOD && encoded_right_arg_.has_value) { | |
| 1121 // It is guaranteed that the value will fit into a Smi, because if it | |
| 1122 // didn't, we wouldn't be here, see BinaryOp_Patch. | |
| 1123 __ cmp(eax, Immediate(Smi::FromInt(fixed_right_arg_value()))); | |
| 1124 __ j(not_equal, &right_arg_changed); | |
| 1125 } | |
| 1126 | |
| 1127 if (result_type_ == BinaryOpIC::UNINITIALIZED || | |
| 1128 result_type_ == BinaryOpIC::SMI) { | |
| 1129 BinaryOpStub_GenerateSmiCode( | |
| 1130 masm, &call_runtime, NO_HEAPNUMBER_RESULTS, op_); | |
| 1131 } else { | |
| 1132 BinaryOpStub_GenerateSmiCode( | |
| 1133 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_); | |
| 1134 } | |
| 1135 | |
| 1136 // Code falls through if the result is not returned as either a smi or heap | |
| 1137 // number. | |
| 1138 __ bind(&right_arg_changed); | |
| 1139 switch (op_) { | |
| 1140 case Token::ADD: | |
| 1141 case Token::SUB: | |
| 1142 case Token::MUL: | |
| 1143 case Token::DIV: | |
| 1144 GenerateTypeTransition(masm); | |
| 1145 break; | |
| 1146 case Token::MOD: | |
| 1147 case Token::BIT_OR: | |
| 1148 case Token::BIT_AND: | |
| 1149 case Token::BIT_XOR: | |
| 1150 case Token::SAR: | |
| 1151 case Token::SHL: | |
| 1152 case Token::SHR: | |
| 1153 GenerateTypeTransitionWithSavedArgs(masm); | |
| 1154 break; | |
| 1155 default: | |
| 1156 UNREACHABLE(); | |
| 1157 } | |
| 1158 | |
| 1159 __ bind(&call_runtime); | |
| 1160 switch (op_) { | |
| 1161 case Token::ADD: | |
| 1162 case Token::SUB: | |
| 1163 case Token::MUL: | |
| 1164 case Token::DIV: | |
| 1165 break; | |
| 1166 case Token::MOD: | |
| 1167 case Token::BIT_OR: | |
| 1168 case Token::BIT_AND: | |
| 1169 case Token::BIT_XOR: | |
| 1170 case Token::SAR: | |
| 1171 case Token::SHL: | |
| 1172 case Token::SHR: | |
| 1173 BinaryOpStub_GenerateRegisterArgsPop(masm); | |
| 1174 break; | |
| 1175 default: | |
| 1176 UNREACHABLE(); | |
| 1177 } | |
| 1178 | |
| 1179 { | |
| 1180 FrameScope scope(masm, StackFrame::INTERNAL); | |
| 1181 __ push(edx); | |
| 1182 __ push(eax); | |
| 1183 GenerateCallRuntime(masm); | |
| 1184 } | |
| 1185 __ ret(0); | |
| 1186 } | |
| 1187 | |
| 1188 | |
| 1189 void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) { | |
| 1190 Label call_runtime; | |
| 1191 ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING); | |
| 1192 ASSERT(op_ == Token::ADD); | |
| 1193 // If both arguments are strings, call the string add stub. | |
| 1194 // Otherwise, do a transition. | |
| 1195 | |
| 1196 // Registers containing left and right operands respectively. | |
| 1197 Register left = edx; | |
| 1198 Register right = eax; | |
| 1199 | |
| 1200 // Test if left operand is a string. | |
| 1201 __ JumpIfSmi(left, &call_runtime, Label::kNear); | |
| 1202 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx); | |
| 1203 __ j(above_equal, &call_runtime, Label::kNear); | |
| 1204 | |
| 1205 // Test if right operand is a string. | |
| 1206 __ JumpIfSmi(right, &call_runtime, Label::kNear); | |
| 1207 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx); | |
| 1208 __ j(above_equal, &call_runtime, Label::kNear); | |
| 1209 | |
| 1210 StringAddStub string_add_stub( | |
| 1211 (StringAddFlags)(STRING_ADD_CHECK_NONE | STRING_ADD_ERECT_FRAME)); | |
| 1212 GenerateRegisterArgsPush(masm); | |
| 1213 __ TailCallStub(&string_add_stub); | |
| 1214 | |
| 1215 __ bind(&call_runtime); | |
| 1216 GenerateTypeTransition(masm); | |
| 1217 } | |
| 1218 | |
| 1219 | |
| 1220 static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm, | |
| 1221 Label* alloc_failure, | |
| 1222 OverwriteMode mode); | |
| 1223 | |
| 1224 | |
| 1225 // Input: | |
| 1226 // edx: left operand (tagged) | |
| 1227 // eax: right operand (tagged) | |
| 1228 // Output: | |
| 1229 // eax: result (tagged) | |
| 1230 void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) { | |
| 1231 Label call_runtime; | |
| 1232 ASSERT(Max(left_type_, right_type_) == BinaryOpIC::INT32); | |
| 1233 | |
| 1234 // Floating point case. | |
| 1235 switch (op_) { | |
| 1236 case Token::ADD: | |
| 1237 case Token::SUB: | |
| 1238 case Token::MUL: | |
| 1239 case Token::DIV: | |
| 1240 case Token::MOD: { | |
| 1241 Label not_floats, not_int32, right_arg_changed; | |
| 1242 if (CpuFeatures::IsSupported(SSE2)) { | |
| 1243 CpuFeatureScope use_sse2(masm, SSE2); | |
| 1244 // It could be that only SMIs have been seen at either the left | |
| 1245 // or the right operand. For precise type feedback, patch the IC | |
| 1246 // again if this changes. | |
| 1247 // In theory, we would need the same check in the non-SSE2 case, | |
| 1248 // but since we don't support Crankshaft on such hardware we can | |
| 1249 // afford not to care about precise type feedback. | |
| 1250 if (left_type_ == BinaryOpIC::SMI) { | |
| 1251 __ JumpIfNotSmi(edx, ¬_int32); | |
| 1252 } | |
| 1253 if (right_type_ == BinaryOpIC::SMI) { | |
| 1254 __ JumpIfNotSmi(eax, ¬_int32); | |
| 1255 } | |
| 1256 FloatingPointHelper::LoadSSE2Operands(masm, ¬_floats); | |
| 1257 FloatingPointHelper::CheckSSE2OperandIsInt32( | |
| 1258 masm, ¬_int32, xmm0, ebx, ecx, xmm2); | |
| 1259 FloatingPointHelper::CheckSSE2OperandIsInt32( | |
| 1260 masm, ¬_int32, xmm1, edi, ecx, xmm2); | |
| 1261 if (op_ == Token::MOD) { | |
| 1262 if (encoded_right_arg_.has_value) { | |
| 1263 __ cmp(edi, Immediate(fixed_right_arg_value())); | |
| 1264 __ j(not_equal, &right_arg_changed); | |
| 1265 } | |
| 1266 GenerateRegisterArgsPush(masm); | |
| 1267 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION); | |
| 1268 } else { | |
| 1269 switch (op_) { | |
| 1270 case Token::ADD: __ addsd(xmm0, xmm1); break; | |
| 1271 case Token::SUB: __ subsd(xmm0, xmm1); break; | |
| 1272 case Token::MUL: __ mulsd(xmm0, xmm1); break; | |
| 1273 case Token::DIV: __ divsd(xmm0, xmm1); break; | |
| 1274 default: UNREACHABLE(); | |
| 1275 } | |
| 1276 // Check result type if it is currently Int32. | |
| 1277 if (result_type_ <= BinaryOpIC::INT32) { | |
| 1278 FloatingPointHelper::CheckSSE2OperandIsInt32( | |
| 1279 masm, ¬_int32, xmm0, ecx, ecx, xmm2); | |
| 1280 } | |
| 1281 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_); | |
| 1282 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); | |
| 1283 __ ret(0); | |
| 1284 } | |
| 1285 } else { // SSE2 not available, use FPU. | |
| 1286 FloatingPointHelper::CheckFloatOperands(masm, ¬_floats, ebx); | |
| 1287 FloatingPointHelper::LoadFloatOperands( | |
| 1288 masm, | |
| 1289 ecx, | |
| 1290 FloatingPointHelper::ARGS_IN_REGISTERS); | |
| 1291 if (op_ == Token::MOD) { | |
| 1292 // The operands are now on the FPU stack, but we don't need them. | |
| 1293 __ fstp(0); | |
| 1294 __ fstp(0); | |
| 1295 GenerateRegisterArgsPush(masm); | |
| 1296 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION); | |
| 1297 } else { | |
| 1298 switch (op_) { | |
| 1299 case Token::ADD: __ faddp(1); break; | |
| 1300 case Token::SUB: __ fsubp(1); break; | |
| 1301 case Token::MUL: __ fmulp(1); break; | |
| 1302 case Token::DIV: __ fdivp(1); break; | |
| 1303 default: UNREACHABLE(); | |
| 1304 } | |
| 1305 Label after_alloc_failure; | |
| 1306 BinaryOpStub_GenerateHeapResultAllocation( | |
| 1307 masm, &after_alloc_failure, mode_); | |
| 1308 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | |
| 1309 __ ret(0); | |
| 1310 __ bind(&after_alloc_failure); | |
| 1311 __ fstp(0); // Pop FPU stack before calling runtime. | |
| 1312 __ jmp(&call_runtime); | |
| 1313 } | |
| 1314 } | |
| 1315 | |
| 1316 __ bind(¬_floats); | |
| 1317 __ bind(¬_int32); | |
| 1318 __ bind(&right_arg_changed); | |
| 1319 GenerateTypeTransition(masm); | |
| 1320 break; | |
| 1321 } | |
| 1322 | |
| 1323 case Token::BIT_OR: | |
| 1324 case Token::BIT_AND: | |
| 1325 case Token::BIT_XOR: | |
| 1326 case Token::SAR: | |
| 1327 case Token::SHL: | |
| 1328 case Token::SHR: { | |
| 1329 GenerateRegisterArgsPush(masm); | |
| 1330 Label not_floats; | |
| 1331 Label not_int32; | |
| 1332 Label non_smi_result; | |
| 1333 bool use_sse3 = platform_specific_bit_; | |
| 1334 FloatingPointHelper::LoadUnknownsAsIntegers( | |
| 1335 masm, use_sse3, left_type_, right_type_, ¬_floats); | |
| 1336 switch (op_) { | |
| 1337 case Token::BIT_OR: __ or_(eax, ecx); break; | |
| 1338 case Token::BIT_AND: __ and_(eax, ecx); break; | |
| 1339 case Token::BIT_XOR: __ xor_(eax, ecx); break; | |
| 1340 case Token::SAR: __ sar_cl(eax); break; | |
| 1341 case Token::SHL: __ shl_cl(eax); break; | |
| 1342 case Token::SHR: __ shr_cl(eax); break; | |
| 1343 default: UNREACHABLE(); | |
| 1344 } | |
| 1345 if (op_ == Token::SHR) { | |
| 1346 // Check if result is non-negative and fits in a smi. | |
| 1347 __ test(eax, Immediate(0xc0000000)); | |
| 1348 __ j(not_zero, &call_runtime); | |
| 1349 } else { | |
| 1350 // Check if result fits in a smi. | |
| 1351 __ cmp(eax, 0xc0000000); | |
| 1352 __ j(negative, &non_smi_result, Label::kNear); | |
| 1353 } | |
| 1354 // Tag smi result and return. | |
| 1355 __ SmiTag(eax); | |
| 1356 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack. | |
| 1357 | |
| 1358 // All ops except SHR return a signed int32 that we load in | |
| 1359 // a HeapNumber. | |
| 1360 if (op_ != Token::SHR) { | |
| 1361 __ bind(&non_smi_result); | |
| 1362 // Allocate a heap number if needed. | |
| 1363 __ mov(ebx, eax); // ebx: result | |
| 1364 Label skip_allocation; | |
| 1365 switch (mode_) { | |
| 1366 case OVERWRITE_LEFT: | |
| 1367 case OVERWRITE_RIGHT: | |
| 1368 // If the operand was an object, we skip the | |
| 1369 // allocation of a heap number. | |
| 1370 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ? | |
| 1371 1 * kPointerSize : 2 * kPointerSize)); | |
| 1372 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear); | |
| 1373 // Fall through! | |
| 1374 case NO_OVERWRITE: | |
| 1375 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime); | |
| 1376 __ bind(&skip_allocation); | |
| 1377 break; | |
| 1378 default: UNREACHABLE(); | |
| 1379 } | |
| 1380 // Store the result in the HeapNumber and return. | |
| 1381 if (CpuFeatures::IsSupported(SSE2)) { | |
| 1382 CpuFeatureScope use_sse2(masm, SSE2); | |
| 1383 __ Cvtsi2sd(xmm0, ebx); | |
| 1384 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); | |
| 1385 } else { | |
| 1386 __ mov(Operand(esp, 1 * kPointerSize), ebx); | |
| 1387 __ fild_s(Operand(esp, 1 * kPointerSize)); | |
| 1388 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | |
| 1389 } | |
| 1390 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack. | |
| 1391 } | |
| 1392 | |
| 1393 __ bind(¬_floats); | |
| 1394 __ bind(¬_int32); | |
| 1395 GenerateTypeTransitionWithSavedArgs(masm); | |
| 1396 break; | |
| 1397 } | |
| 1398 default: UNREACHABLE(); break; | |
| 1399 } | |
| 1400 | |
| 1401 // If an allocation fails, or SHR hits a hard case, use the runtime system to | |
| 1402 // get the correct result. | |
| 1403 __ bind(&call_runtime); | |
| 1404 | |
| 1405 switch (op_) { | |
| 1406 case Token::ADD: | |
| 1407 case Token::SUB: | |
| 1408 case Token::MUL: | |
| 1409 case Token::DIV: | |
| 1410 break; | |
| 1411 case Token::MOD: | |
| 1412 return; // Handled above. | |
| 1413 case Token::BIT_OR: | |
| 1414 case Token::BIT_AND: | |
| 1415 case Token::BIT_XOR: | |
| 1416 case Token::SAR: | |
| 1417 case Token::SHL: | |
| 1418 case Token::SHR: | |
| 1419 BinaryOpStub_GenerateRegisterArgsPop(masm); | |
| 1420 break; | |
| 1421 default: | |
| 1422 UNREACHABLE(); | |
| 1423 } | |
| 1424 | |
| 1425 { | |
| 1426 FrameScope scope(masm, StackFrame::INTERNAL); | |
| 1427 __ push(edx); | |
| 1428 __ push(eax); | |
| 1429 GenerateCallRuntime(masm); | |
| 1430 } | |
| 1431 __ ret(0); | |
| 1432 } | |
| 1433 | |
| 1434 | |
| 1435 void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) { | |
| 1436 if (op_ == Token::ADD) { | |
| 1437 // Handle string addition here, because it is the only operation | |
| 1438 // that does not do a ToNumber conversion on the operands. | |
| 1439 GenerateAddStrings(masm); | |
| 1440 } | |
| 1441 | |
| 1442 Factory* factory = masm->isolate()->factory(); | |
| 1443 | |
| 1444 // Convert odd ball arguments to numbers. | |
| 1445 Label check, done; | |
| 1446 __ cmp(edx, factory->undefined_value()); | |
| 1447 __ j(not_equal, &check, Label::kNear); | |
| 1448 if (Token::IsBitOp(op_)) { | |
| 1449 __ xor_(edx, edx); | |
| 1450 } else { | |
| 1451 __ mov(edx, Immediate(factory->nan_value())); | |
| 1452 } | |
| 1453 __ jmp(&done, Label::kNear); | |
| 1454 __ bind(&check); | |
| 1455 __ cmp(eax, factory->undefined_value()); | |
| 1456 __ j(not_equal, &done, Label::kNear); | |
| 1457 if (Token::IsBitOp(op_)) { | |
| 1458 __ xor_(eax, eax); | |
| 1459 } else { | |
| 1460 __ mov(eax, Immediate(factory->nan_value())); | |
| 1461 } | |
| 1462 __ bind(&done); | |
| 1463 | |
| 1464 GenerateNumberStub(masm); | |
| 1465 } | |
| 1466 | |
| 1467 | |
| 1468 void BinaryOpStub::GenerateNumberStub(MacroAssembler* masm) { | |
| 1469 Label call_runtime; | |
| 1470 | |
| 1471 // Floating point case. | |
| 1472 switch (op_) { | |
| 1473 case Token::ADD: | |
| 1474 case Token::SUB: | |
| 1475 case Token::MUL: | |
| 1476 case Token::DIV: { | |
| 1477 Label not_floats; | |
| 1478 if (CpuFeatures::IsSupported(SSE2)) { | |
| 1479 CpuFeatureScope use_sse2(masm, SSE2); | |
| 1480 | |
| 1481 // It could be that only SMIs have been seen at either the left | |
| 1482 // or the right operand. For precise type feedback, patch the IC | |
| 1483 // again if this changes. | |
| 1484 // In theory, we would need the same check in the non-SSE2 case, | |
| 1485 // but since we don't support Crankshaft on such hardware we can | |
| 1486 // afford not to care about precise type feedback. | |
| 1487 if (left_type_ == BinaryOpIC::SMI) { | |
| 1488 __ JumpIfNotSmi(edx, ¬_floats); | |
| 1489 } | |
| 1490 if (right_type_ == BinaryOpIC::SMI) { | |
| 1491 __ JumpIfNotSmi(eax, ¬_floats); | |
| 1492 } | |
| 1493 FloatingPointHelper::LoadSSE2Operands(masm, ¬_floats); | |
| 1494 if (left_type_ == BinaryOpIC::INT32) { | |
| 1495 FloatingPointHelper::CheckSSE2OperandIsInt32( | |
| 1496 masm, ¬_floats, xmm0, ecx, ecx, xmm2); | |
| 1497 } | |
| 1498 if (right_type_ == BinaryOpIC::INT32) { | |
| 1499 FloatingPointHelper::CheckSSE2OperandIsInt32( | |
| 1500 masm, ¬_floats, xmm1, ecx, ecx, xmm2); | |
| 1501 } | |
| 1502 | |
| 1503 switch (op_) { | |
| 1504 case Token::ADD: __ addsd(xmm0, xmm1); break; | |
| 1505 case Token::SUB: __ subsd(xmm0, xmm1); break; | |
| 1506 case Token::MUL: __ mulsd(xmm0, xmm1); break; | |
| 1507 case Token::DIV: __ divsd(xmm0, xmm1); break; | |
| 1508 default: UNREACHABLE(); | |
| 1509 } | |
| 1510 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_); | |
| 1511 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); | |
| 1512 __ ret(0); | |
| 1513 } else { // SSE2 not available, use FPU. | |
| 1514 FloatingPointHelper::CheckFloatOperands(masm, ¬_floats, ebx); | |
| 1515 FloatingPointHelper::LoadFloatOperands( | |
| 1516 masm, | |
| 1517 ecx, | |
| 1518 FloatingPointHelper::ARGS_IN_REGISTERS); | |
| 1519 switch (op_) { | |
| 1520 case Token::ADD: __ faddp(1); break; | |
| 1521 case Token::SUB: __ fsubp(1); break; | |
| 1522 case Token::MUL: __ fmulp(1); break; | |
| 1523 case Token::DIV: __ fdivp(1); break; | |
| 1524 default: UNREACHABLE(); | |
| 1525 } | |
| 1526 Label after_alloc_failure; | |
| 1527 BinaryOpStub_GenerateHeapResultAllocation( | |
| 1528 masm, &after_alloc_failure, mode_); | |
| 1529 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | |
| 1530 __ ret(0); | |
| 1531 __ bind(&after_alloc_failure); | |
| 1532 __ fstp(0); // Pop FPU stack before calling runtime. | |
| 1533 __ jmp(&call_runtime); | |
| 1534 } | |
| 1535 | |
| 1536 __ bind(¬_floats); | |
| 1537 GenerateTypeTransition(masm); | |
| 1538 break; | |
| 1539 } | |
| 1540 | |
| 1541 case Token::MOD: { | |
| 1542 // For MOD we go directly to runtime in the non-smi case. | |
| 1543 break; | |
| 1544 } | |
| 1545 case Token::BIT_OR: | |
| 1546 case Token::BIT_AND: | |
| 1547 case Token::BIT_XOR: | |
| 1548 case Token::SAR: | |
| 1549 case Token::SHL: | |
| 1550 case Token::SHR: { | |
| 1551 GenerateRegisterArgsPush(masm); | |
| 1552 Label not_floats; | |
| 1553 Label non_smi_result; | |
| 1554 // We do not check the input arguments here, as any value is | |
| 1555 // unconditionally truncated to an int32 anyway. To get the | |
| 1556 // right optimized code, int32 type feedback is just right. | |
| 1557 bool use_sse3 = platform_specific_bit_; | |
| 1558 FloatingPointHelper::LoadUnknownsAsIntegers( | |
| 1559 masm, use_sse3, left_type_, right_type_, ¬_floats); | |
| 1560 switch (op_) { | |
| 1561 case Token::BIT_OR: __ or_(eax, ecx); break; | |
| 1562 case Token::BIT_AND: __ and_(eax, ecx); break; | |
| 1563 case Token::BIT_XOR: __ xor_(eax, ecx); break; | |
| 1564 case Token::SAR: __ sar_cl(eax); break; | |
| 1565 case Token::SHL: __ shl_cl(eax); break; | |
| 1566 case Token::SHR: __ shr_cl(eax); break; | |
| 1567 default: UNREACHABLE(); | |
| 1568 } | |
| 1569 if (op_ == Token::SHR) { | |
| 1570 // Check if result is non-negative and fits in a smi. | |
| 1571 __ test(eax, Immediate(0xc0000000)); | |
| 1572 __ j(not_zero, &call_runtime); | |
| 1573 } else { | |
| 1574 // Check if result fits in a smi. | |
| 1575 __ cmp(eax, 0xc0000000); | |
| 1576 __ j(negative, &non_smi_result, Label::kNear); | |
| 1577 } | |
| 1578 // Tag smi result and return. | |
| 1579 __ SmiTag(eax); | |
| 1580 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack. | |
| 1581 | |
| 1582 // All ops except SHR return a signed int32 that we load in | |
| 1583 // a HeapNumber. | |
| 1584 if (op_ != Token::SHR) { | |
| 1585 __ bind(&non_smi_result); | |
| 1586 // Allocate a heap number if needed. | |
| 1587 __ mov(ebx, eax); // ebx: result | |
| 1588 Label skip_allocation; | |
| 1589 switch (mode_) { | |
| 1590 case OVERWRITE_LEFT: | |
| 1591 case OVERWRITE_RIGHT: | |
| 1592 // If the operand was an object, we skip the | |
| 1593 // allocation of a heap number. | |
| 1594 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ? | |
| 1595 1 * kPointerSize : 2 * kPointerSize)); | |
| 1596 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear); | |
| 1597 // Fall through! | |
| 1598 case NO_OVERWRITE: | |
| 1599 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime); | |
| 1600 __ bind(&skip_allocation); | |
| 1601 break; | |
| 1602 default: UNREACHABLE(); | |
| 1603 } | |
| 1604 // Store the result in the HeapNumber and return. | |
| 1605 if (CpuFeatures::IsSupported(SSE2)) { | |
| 1606 CpuFeatureScope use_sse2(masm, SSE2); | |
| 1607 __ Cvtsi2sd(xmm0, ebx); | |
| 1608 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); | |
| 1609 } else { | |
| 1610 __ mov(Operand(esp, 1 * kPointerSize), ebx); | |
| 1611 __ fild_s(Operand(esp, 1 * kPointerSize)); | |
| 1612 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | |
| 1613 } | |
| 1614 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack. | |
| 1615 } | |
| 1616 | |
| 1617 __ bind(¬_floats); | |
| 1618 GenerateTypeTransitionWithSavedArgs(masm); | |
| 1619 break; | |
| 1620 } | |
| 1621 default: UNREACHABLE(); break; | |
| 1622 } | |
| 1623 | |
| 1624 // If an allocation fails, or SHR or MOD hit a hard case, | |
| 1625 // use the runtime system to get the correct result. | |
| 1626 __ bind(&call_runtime); | |
| 1627 | |
| 1628 switch (op_) { | |
| 1629 case Token::ADD: | |
| 1630 case Token::SUB: | |
| 1631 case Token::MUL: | |
| 1632 case Token::DIV: | |
| 1633 case Token::MOD: | |
| 1634 break; | |
| 1635 case Token::BIT_OR: | |
| 1636 case Token::BIT_AND: | |
| 1637 case Token::BIT_XOR: | |
| 1638 case Token::SAR: | |
| 1639 case Token::SHL: | |
| 1640 case Token::SHR: | |
| 1641 BinaryOpStub_GenerateRegisterArgsPop(masm); | |
| 1642 break; | |
| 1643 default: | |
| 1644 UNREACHABLE(); | |
| 1645 } | |
| 1646 | |
| 1647 { | |
| 1648 FrameScope scope(masm, StackFrame::INTERNAL); | |
| 1649 __ push(edx); | |
| 1650 __ push(eax); | |
| 1651 GenerateCallRuntime(masm); | |
| 1652 } | |
| 1653 __ ret(0); | |
| 1654 } | |
| 1655 | |
| 1656 | |
| 1657 void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) { | |
| 1658 Label call_runtime; | |
| 1659 | |
| 1660 Counters* counters = masm->isolate()->counters(); | |
| 1661 __ IncrementCounter(counters->generic_binary_stub_calls(), 1); | |
| 1662 | |
| 1663 switch (op_) { | |
| 1664 case Token::ADD: | |
| 1665 case Token::SUB: | |
| 1666 case Token::MUL: | |
| 1667 case Token::DIV: | |
| 1668 break; | |
| 1669 case Token::MOD: | |
| 1670 case Token::BIT_OR: | |
| 1671 case Token::BIT_AND: | |
| 1672 case Token::BIT_XOR: | |
| 1673 case Token::SAR: | |
| 1674 case Token::SHL: | |
| 1675 case Token::SHR: | |
| 1676 GenerateRegisterArgsPush(masm); | |
| 1677 break; | |
| 1678 default: | |
| 1679 UNREACHABLE(); | |
| 1680 } | |
| 1681 | |
| 1682 BinaryOpStub_GenerateSmiCode( | |
| 1683 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_); | |
| 1684 | |
| 1685 // Floating point case. | |
| 1686 switch (op_) { | |
| 1687 case Token::ADD: | |
| 1688 case Token::SUB: | |
| 1689 case Token::MUL: | |
| 1690 case Token::DIV: { | |
| 1691 Label not_floats; | |
| 1692 if (CpuFeatures::IsSupported(SSE2)) { | |
| 1693 CpuFeatureScope use_sse2(masm, SSE2); | |
| 1694 FloatingPointHelper::LoadSSE2Operands(masm, ¬_floats); | |
| 1695 | |
| 1696 switch (op_) { | |
| 1697 case Token::ADD: __ addsd(xmm0, xmm1); break; | |
| 1698 case Token::SUB: __ subsd(xmm0, xmm1); break; | |
| 1699 case Token::MUL: __ mulsd(xmm0, xmm1); break; | |
| 1700 case Token::DIV: __ divsd(xmm0, xmm1); break; | |
| 1701 default: UNREACHABLE(); | |
| 1702 } | |
| 1703 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_); | |
| 1704 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); | |
| 1705 __ ret(0); | |
| 1706 } else { // SSE2 not available, use FPU. | |
| 1707 FloatingPointHelper::CheckFloatOperands(masm, ¬_floats, ebx); | |
| 1708 FloatingPointHelper::LoadFloatOperands( | |
| 1709 masm, | |
| 1710 ecx, | |
| 1711 FloatingPointHelper::ARGS_IN_REGISTERS); | |
| 1712 switch (op_) { | |
| 1713 case Token::ADD: __ faddp(1); break; | |
| 1714 case Token::SUB: __ fsubp(1); break; | |
| 1715 case Token::MUL: __ fmulp(1); break; | |
| 1716 case Token::DIV: __ fdivp(1); break; | |
| 1717 default: UNREACHABLE(); | |
| 1718 } | |
| 1719 Label after_alloc_failure; | |
| 1720 BinaryOpStub_GenerateHeapResultAllocation( | |
| 1721 masm, &after_alloc_failure, mode_); | |
| 1722 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | |
| 1723 __ ret(0); | |
| 1724 __ bind(&after_alloc_failure); | |
| 1725 __ fstp(0); // Pop FPU stack before calling runtime. | |
| 1726 __ jmp(&call_runtime); | |
| 1727 } | |
| 1728 __ bind(¬_floats); | |
| 1729 break; | |
| 1730 } | |
| 1731 case Token::MOD: { | |
| 1732 // For MOD we go directly to runtime in the non-smi case. | |
| 1733 break; | |
| 1734 } | |
| 1735 case Token::BIT_OR: | |
| 1736 case Token::BIT_AND: | |
| 1737 case Token::BIT_XOR: | |
| 1738 case Token::SAR: | |
| 1739 case Token::SHL: | |
| 1740 case Token::SHR: { | |
| 1741 Label non_smi_result; | |
| 1742 bool use_sse3 = platform_specific_bit_; | |
| 1743 FloatingPointHelper::LoadUnknownsAsIntegers(masm, | |
| 1744 use_sse3, | |
| 1745 BinaryOpIC::GENERIC, | |
| 1746 BinaryOpIC::GENERIC, | |
| 1747 &call_runtime); | |
| 1748 switch (op_) { | |
| 1749 case Token::BIT_OR: __ or_(eax, ecx); break; | |
| 1750 case Token::BIT_AND: __ and_(eax, ecx); break; | |
| 1751 case Token::BIT_XOR: __ xor_(eax, ecx); break; | |
| 1752 case Token::SAR: __ sar_cl(eax); break; | |
| 1753 case Token::SHL: __ shl_cl(eax); break; | |
| 1754 case Token::SHR: __ shr_cl(eax); break; | |
| 1755 default: UNREACHABLE(); | |
| 1756 } | |
| 1757 if (op_ == Token::SHR) { | |
| 1758 // Check if result is non-negative and fits in a smi. | |
| 1759 __ test(eax, Immediate(0xc0000000)); | |
| 1760 __ j(not_zero, &call_runtime); | |
| 1761 } else { | |
| 1762 // Check if result fits in a smi. | |
| 1763 __ cmp(eax, 0xc0000000); | |
| 1764 __ j(negative, &non_smi_result, Label::kNear); | |
| 1765 } | |
| 1766 // Tag smi result and return. | |
| 1767 __ SmiTag(eax); | |
| 1768 __ ret(2 * kPointerSize); // Drop the arguments from the stack. | |
| 1769 | |
| 1770 // All ops except SHR return a signed int32 that we load in | |
| 1771 // a HeapNumber. | |
| 1772 if (op_ != Token::SHR) { | |
| 1773 __ bind(&non_smi_result); | |
| 1774 // Allocate a heap number if needed. | |
| 1775 __ mov(ebx, eax); // ebx: result | |
| 1776 Label skip_allocation; | |
| 1777 switch (mode_) { | |
| 1778 case OVERWRITE_LEFT: | |
| 1779 case OVERWRITE_RIGHT: | |
| 1780 // If the operand was an object, we skip the | |
| 1781 // allocation of a heap number. | |
| 1782 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ? | |
| 1783 1 * kPointerSize : 2 * kPointerSize)); | |
| 1784 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear); | |
| 1785 // Fall through! | |
| 1786 case NO_OVERWRITE: | |
| 1787 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime); | |
| 1788 __ bind(&skip_allocation); | |
| 1789 break; | |
| 1790 default: UNREACHABLE(); | |
| 1791 } | |
| 1792 // Store the result in the HeapNumber and return. | |
| 1793 if (CpuFeatures::IsSupported(SSE2)) { | |
| 1794 CpuFeatureScope use_sse2(masm, SSE2); | |
| 1795 __ Cvtsi2sd(xmm0, ebx); | |
| 1796 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); | |
| 1797 } else { | |
| 1798 __ mov(Operand(esp, 1 * kPointerSize), ebx); | |
| 1799 __ fild_s(Operand(esp, 1 * kPointerSize)); | |
| 1800 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | |
| 1801 } | |
| 1802 __ ret(2 * kPointerSize); | |
| 1803 } | |
| 1804 break; | |
| 1805 } | |
| 1806 default: UNREACHABLE(); break; | |
| 1807 } | |
| 1808 | |
| 1809 // If all else fails, use the runtime system to get the correct | |
| 1810 // result. | |
| 1811 __ bind(&call_runtime); | |
| 1812 switch (op_) { | |
| 1813 case Token::ADD: | |
| 1814 GenerateAddStrings(masm); | |
| 1815 // Fall through. | |
| 1816 case Token::SUB: | |
| 1817 case Token::MUL: | |
| 1818 case Token::DIV: | |
| 1819 break; | |
| 1820 case Token::MOD: | |
| 1821 case Token::BIT_OR: | |
| 1822 case Token::BIT_AND: | |
| 1823 case Token::BIT_XOR: | |
| 1824 case Token::SAR: | |
| 1825 case Token::SHL: | |
| 1826 case Token::SHR: | |
| 1827 BinaryOpStub_GenerateRegisterArgsPop(masm); | |
| 1828 break; | |
| 1829 default: | |
| 1830 UNREACHABLE(); | |
| 1831 } | |
| 1832 | |
| 1833 { | |
| 1834 FrameScope scope(masm, StackFrame::INTERNAL); | |
| 1835 __ push(edx); | |
| 1836 __ push(eax); | |
| 1837 GenerateCallRuntime(masm); | |
| 1838 } | |
| 1839 __ ret(0); | |
| 1840 } | |
| 1841 | |
| 1842 | |
| 1843 void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) { | |
| 1844 ASSERT(op_ == Token::ADD); | |
| 1845 Label left_not_string, call_runtime; | |
| 1846 | |
| 1847 // Registers containing left and right operands respectively. | |
| 1848 Register left = edx; | |
| 1849 Register right = eax; | |
| 1850 | |
| 1851 // Test if left operand is a string. | |
| 1852 __ JumpIfSmi(left, &left_not_string, Label::kNear); | |
| 1853 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx); | |
| 1854 __ j(above_equal, &left_not_string, Label::kNear); | |
| 1855 | |
| 1856 StringAddStub string_add_left_stub( | |
| 1857 (StringAddFlags)(STRING_ADD_CHECK_RIGHT | STRING_ADD_ERECT_FRAME)); | |
| 1858 GenerateRegisterArgsPush(masm); | |
| 1859 __ TailCallStub(&string_add_left_stub); | |
| 1860 | |
| 1861 // Left operand is not a string, test right. | |
| 1862 __ bind(&left_not_string); | |
| 1863 __ JumpIfSmi(right, &call_runtime, Label::kNear); | |
| 1864 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx); | |
| 1865 __ j(above_equal, &call_runtime, Label::kNear); | |
| 1866 | |
| 1867 StringAddStub string_add_right_stub( | |
| 1868 (StringAddFlags)(STRING_ADD_CHECK_LEFT | STRING_ADD_ERECT_FRAME)); | |
| 1869 GenerateRegisterArgsPush(masm); | |
| 1870 __ TailCallStub(&string_add_right_stub); | |
| 1871 | |
| 1872 // Neither argument is a string. | |
| 1873 __ bind(&call_runtime); | |
| 1874 } | |
| 1875 | |
| 1876 | |
| 1877 static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm, | |
| 1878 Label* alloc_failure, | |
| 1879 OverwriteMode mode) { | |
| 1880 Label skip_allocation; | |
| 1881 switch (mode) { | |
| 1882 case OVERWRITE_LEFT: { | |
| 1883 // If the argument in edx is already an object, we skip the | |
| 1884 // allocation of a heap number. | |
| 1885 __ JumpIfNotSmi(edx, &skip_allocation, Label::kNear); | |
| 1886 // Allocate a heap number for the result. Keep eax and edx intact | |
| 1887 // for the possible runtime call. | |
| 1888 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure); | |
| 1889 // Now edx can be overwritten losing one of the arguments as we are | |
| 1890 // now done and will not need it any more. | |
| 1891 __ mov(edx, ebx); | |
| 1892 __ bind(&skip_allocation); | |
| 1893 // Use object in edx as a result holder | |
| 1894 __ mov(eax, edx); | |
| 1895 break; | |
| 1896 } | |
| 1897 case OVERWRITE_RIGHT: | |
| 1898 // If the argument in eax is already an object, we skip the | |
| 1899 // allocation of a heap number. | |
| 1900 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear); | |
| 1901 // Fall through! | |
| 1902 case NO_OVERWRITE: | |
| 1903 // Allocate a heap number for the result. Keep eax and edx intact | |
| 1904 // for the possible runtime call. | |
| 1905 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure); | |
| 1906 // Now eax can be overwritten losing one of the arguments as we are | |
| 1907 // now done and will not need it any more. | |
| 1908 __ mov(eax, ebx); | |
| 1909 __ bind(&skip_allocation); | |
| 1910 break; | |
| 1911 default: UNREACHABLE(); | |
| 1912 } | |
| 1913 } | |
| 1914 | |
| 1915 | |
| 1916 void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) { | |
| 1917 __ pop(ecx); | |
| 1918 __ push(edx); | |
| 1919 __ push(eax); | |
| 1920 __ push(ecx); | |
| 1921 } | |
| 1922 | |
| 1923 | |
| 1924 void TranscendentalCacheStub::Generate(MacroAssembler* masm) { | 650 void TranscendentalCacheStub::Generate(MacroAssembler* masm) { |
| 1925 // TAGGED case: | 651 // TAGGED case: |
| 1926 // Input: | 652 // Input: |
| 1927 // esp[4]: tagged number input argument (should be number). | 653 // esp[4]: tagged number input argument (should be number). |
| 1928 // esp[0]: return address. | 654 // esp[0]: return address. |
| 1929 // Output: | 655 // Output: |
| 1930 // eax: tagged double result. | 656 // eax: tagged double result. |
| 1931 // UNTAGGED case: | 657 // UNTAGGED case: |
| 1932 // Input:: | 658 // Input:: |
| 1933 // esp[0]: return address. | 659 // esp[0]: return address. |
| (...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2224 __ bind(&done); | 950 __ bind(&done); |
| 2225 } else { | 951 } else { |
| 2226 ASSERT(type == TranscendentalCache::LOG); | 952 ASSERT(type == TranscendentalCache::LOG); |
| 2227 __ fldln2(); | 953 __ fldln2(); |
| 2228 __ fxch(); | 954 __ fxch(); |
| 2229 __ fyl2x(); | 955 __ fyl2x(); |
| 2230 } | 956 } |
| 2231 } | 957 } |
| 2232 | 958 |
| 2233 | 959 |
| 2234 // Input: edx, eax are the left and right objects of a bit op. | |
| 2235 // Output: eax, ecx are left and right integers for a bit op. | |
| 2236 // Warning: can clobber inputs even when it jumps to |conversion_failure|! | |
| 2237 void FloatingPointHelper::LoadUnknownsAsIntegers( | |
| 2238 MacroAssembler* masm, | |
| 2239 bool use_sse3, | |
| 2240 BinaryOpIC::TypeInfo left_type, | |
| 2241 BinaryOpIC::TypeInfo right_type, | |
| 2242 Label* conversion_failure) { | |
| 2243 // Check float operands. | |
| 2244 Label arg1_is_object, check_undefined_arg1; | |
| 2245 Label arg2_is_object, check_undefined_arg2; | |
| 2246 Label load_arg2, done; | |
| 2247 | |
| 2248 // Test if arg1 is a Smi. | |
| 2249 if (left_type == BinaryOpIC::SMI) { | |
| 2250 __ JumpIfNotSmi(edx, conversion_failure); | |
| 2251 } else { | |
| 2252 __ JumpIfNotSmi(edx, &arg1_is_object, Label::kNear); | |
| 2253 } | |
| 2254 | |
| 2255 __ SmiUntag(edx); | |
| 2256 __ jmp(&load_arg2); | |
| 2257 | |
| 2258 // If the argument is undefined it converts to zero (ECMA-262, section 9.5). | |
| 2259 __ bind(&check_undefined_arg1); | |
| 2260 Factory* factory = masm->isolate()->factory(); | |
| 2261 __ cmp(edx, factory->undefined_value()); | |
| 2262 __ j(not_equal, conversion_failure); | |
| 2263 __ mov(edx, Immediate(0)); | |
| 2264 __ jmp(&load_arg2); | |
| 2265 | |
| 2266 __ bind(&arg1_is_object); | |
| 2267 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); | |
| 2268 __ cmp(ebx, factory->heap_number_map()); | |
| 2269 __ j(not_equal, &check_undefined_arg1); | |
| 2270 | |
| 2271 __ TruncateHeapNumberToI(edx, edx); | |
| 2272 | |
| 2273 // Here edx has the untagged integer, eax has a Smi or a heap number. | |
| 2274 __ bind(&load_arg2); | |
| 2275 | |
| 2276 // Test if arg2 is a Smi. | |
| 2277 if (right_type == BinaryOpIC::SMI) { | |
| 2278 __ JumpIfNotSmi(eax, conversion_failure); | |
| 2279 } else { | |
| 2280 __ JumpIfNotSmi(eax, &arg2_is_object, Label::kNear); | |
| 2281 } | |
| 2282 | |
| 2283 __ SmiUntag(eax); | |
| 2284 __ mov(ecx, eax); | |
| 2285 __ jmp(&done); | |
| 2286 | |
| 2287 // If the argument is undefined it converts to zero (ECMA-262, section 9.5). | |
| 2288 __ bind(&check_undefined_arg2); | |
| 2289 __ cmp(eax, factory->undefined_value()); | |
| 2290 __ j(not_equal, conversion_failure); | |
| 2291 __ mov(ecx, Immediate(0)); | |
| 2292 __ jmp(&done); | |
| 2293 | |
| 2294 __ bind(&arg2_is_object); | |
| 2295 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); | |
| 2296 __ cmp(ebx, factory->heap_number_map()); | |
| 2297 __ j(not_equal, &check_undefined_arg2); | |
| 2298 // Get the untagged integer version of the eax heap number in ecx. | |
| 2299 | |
| 2300 __ TruncateHeapNumberToI(ecx, eax); | |
| 2301 | |
| 2302 __ bind(&done); | |
| 2303 __ mov(eax, edx); | |
| 2304 } | |
| 2305 | |
| 2306 | |
| 2307 void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm, | 960 void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm, |
| 2308 Register number) { | 961 Register number) { |
| 2309 Label load_smi, done; | 962 Label load_smi, done; |
| 2310 | 963 |
| 2311 __ JumpIfSmi(number, &load_smi, Label::kNear); | 964 __ JumpIfSmi(number, &load_smi, Label::kNear); |
| 2312 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset)); | 965 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset)); |
| 2313 __ jmp(&done, Label::kNear); | 966 __ jmp(&done, Label::kNear); |
| 2314 | 967 |
| 2315 __ bind(&load_smi); | 968 __ bind(&load_smi); |
| 2316 __ SmiUntag(number); | 969 __ SmiUntag(number); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 2346 __ SmiUntag(eax); // Untag smi before converting to float. | 999 __ SmiUntag(eax); // Untag smi before converting to float. |
| 2347 __ Cvtsi2sd(xmm1, eax); | 1000 __ Cvtsi2sd(xmm1, eax); |
| 2348 __ SmiTag(eax); // Retag smi for heap number overwriting test. | 1001 __ SmiTag(eax); // Retag smi for heap number overwriting test. |
| 2349 __ jmp(&done, Label::kNear); | 1002 __ jmp(&done, Label::kNear); |
| 2350 __ bind(&load_float_eax); | 1003 __ bind(&load_float_eax); |
| 2351 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset)); | 1004 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset)); |
| 2352 __ bind(&done); | 1005 __ bind(&done); |
| 2353 } | 1006 } |
| 2354 | 1007 |
| 2355 | 1008 |
| 2356 void FloatingPointHelper::LoadSSE2Smis(MacroAssembler* masm, | |
| 2357 Register scratch) { | |
| 2358 const Register left = edx; | |
| 2359 const Register right = eax; | |
| 2360 __ mov(scratch, left); | |
| 2361 ASSERT(!scratch.is(right)); // We're about to clobber scratch. | |
| 2362 __ SmiUntag(scratch); | |
| 2363 __ Cvtsi2sd(xmm0, scratch); | |
| 2364 | |
| 2365 __ mov(scratch, right); | |
| 2366 __ SmiUntag(scratch); | |
| 2367 __ Cvtsi2sd(xmm1, scratch); | |
| 2368 } | |
| 2369 | |
| 2370 | |
| 2371 void FloatingPointHelper::CheckSSE2OperandIsInt32(MacroAssembler* masm, | |
| 2372 Label* non_int32, | |
| 2373 XMMRegister operand, | |
| 2374 Register int32_result, | |
| 2375 Register scratch, | |
| 2376 XMMRegister xmm_scratch) { | |
| 2377 __ cvttsd2si(int32_result, Operand(operand)); | |
| 2378 __ Cvtsi2sd(xmm_scratch, int32_result); | |
| 2379 __ pcmpeqd(xmm_scratch, operand); | |
| 2380 __ movmskps(scratch, xmm_scratch); | |
| 2381 // Two least significant bits should be both set. | |
| 2382 __ not_(scratch); | |
| 2383 __ test(scratch, Immediate(3)); | |
| 2384 __ j(not_zero, non_int32); | |
| 2385 } | |
| 2386 | |
| 2387 | |
| 2388 void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm, | |
| 2389 Register scratch, | |
| 2390 ArgLocation arg_location) { | |
| 2391 Label load_smi_1, load_smi_2, done_load_1, done; | |
| 2392 if (arg_location == ARGS_IN_REGISTERS) { | |
| 2393 __ mov(scratch, edx); | |
| 2394 } else { | |
| 2395 __ mov(scratch, Operand(esp, 2 * kPointerSize)); | |
| 2396 } | |
| 2397 __ JumpIfSmi(scratch, &load_smi_1, Label::kNear); | |
| 2398 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset)); | |
| 2399 __ bind(&done_load_1); | |
| 2400 | |
| 2401 if (arg_location == ARGS_IN_REGISTERS) { | |
| 2402 __ mov(scratch, eax); | |
| 2403 } else { | |
| 2404 __ mov(scratch, Operand(esp, 1 * kPointerSize)); | |
| 2405 } | |
| 2406 __ JumpIfSmi(scratch, &load_smi_2, Label::kNear); | |
| 2407 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset)); | |
| 2408 __ jmp(&done, Label::kNear); | |
| 2409 | |
| 2410 __ bind(&load_smi_1); | |
| 2411 __ SmiUntag(scratch); | |
| 2412 __ push(scratch); | |
| 2413 __ fild_s(Operand(esp, 0)); | |
| 2414 __ pop(scratch); | |
| 2415 __ jmp(&done_load_1); | |
| 2416 | |
| 2417 __ bind(&load_smi_2); | |
| 2418 __ SmiUntag(scratch); | |
| 2419 __ push(scratch); | |
| 2420 __ fild_s(Operand(esp, 0)); | |
| 2421 __ pop(scratch); | |
| 2422 | |
| 2423 __ bind(&done); | |
| 2424 } | |
| 2425 | |
| 2426 | |
| 2427 void FloatingPointHelper::LoadFloatSmis(MacroAssembler* masm, | |
| 2428 Register scratch) { | |
| 2429 const Register left = edx; | |
| 2430 const Register right = eax; | |
| 2431 __ mov(scratch, left); | |
| 2432 ASSERT(!scratch.is(right)); // We're about to clobber scratch. | |
| 2433 __ SmiUntag(scratch); | |
| 2434 __ push(scratch); | |
| 2435 __ fild_s(Operand(esp, 0)); | |
| 2436 | |
| 2437 __ mov(scratch, right); | |
| 2438 __ SmiUntag(scratch); | |
| 2439 __ mov(Operand(esp, 0), scratch); | |
| 2440 __ fild_s(Operand(esp, 0)); | |
| 2441 __ pop(scratch); | |
| 2442 } | |
| 2443 | |
| 2444 | |
| 2445 void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm, | 1009 void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm, |
| 2446 Label* non_float, | 1010 Label* non_float, |
| 2447 Register scratch) { | 1011 Register scratch) { |
| 2448 Label test_other, done; | 1012 Label test_other, done; |
| 2449 // Test if both operands are floats or smi -> scratch=k_is_float; | 1013 // Test if both operands are floats or smi -> scratch=k_is_float; |
| 2450 // Otherwise scratch = k_not_float. | 1014 // Otherwise scratch = k_not_float. |
| 2451 __ JumpIfSmi(edx, &test_other, Label::kNear); | 1015 __ JumpIfSmi(edx, &test_other, Label::kNear); |
| 2452 __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset)); | 1016 __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset)); |
| 2453 Factory* factory = masm->isolate()->factory(); | 1017 Factory* factory = masm->isolate()->factory(); |
| 2454 __ cmp(scratch, factory->heap_number_map()); | 1018 __ cmp(scratch, factory->heap_number_map()); |
| (...skipping 1892 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4347 | 2911 |
| 4348 | 2912 |
| 4349 void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { | 2913 void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { |
| 4350 CEntryStub::GenerateAheadOfTime(isolate); | 2914 CEntryStub::GenerateAheadOfTime(isolate); |
| 4351 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate); | 2915 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate); |
| 4352 StubFailureTrampolineStub::GenerateAheadOfTime(isolate); | 2916 StubFailureTrampolineStub::GenerateAheadOfTime(isolate); |
| 4353 // It is important that the store buffer overflow stubs are generated first. | 2917 // It is important that the store buffer overflow stubs are generated first. |
| 4354 RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate); | 2918 RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate); |
| 4355 ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); | 2919 ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); |
| 4356 CreateAllocationSiteStub::GenerateAheadOfTime(isolate); | 2920 CreateAllocationSiteStub::GenerateAheadOfTime(isolate); |
| 2921 PlatformFeatureScope sse2(SSE2); |
| 2922 BinaryOpStub::GenerateAheadOfTime(isolate); |
| 4357 } | 2923 } |
| 4358 | 2924 |
| 4359 | 2925 |
| 4360 void CodeStub::GenerateFPStubs(Isolate* isolate) { | 2926 void CodeStub::GenerateFPStubs(Isolate* isolate) { |
| 4361 if (CpuFeatures::IsSupported(SSE2)) { | 2927 if (CpuFeatures::IsSupported(SSE2)) { |
| 4362 CEntryStub save_doubles(1, kSaveFPRegs); | 2928 CEntryStub save_doubles(1, kSaveFPRegs); |
| 4363 // Stubs might already be in the snapshot, detect that and don't regenerate, | 2929 // Stubs might already be in the snapshot, detect that and don't regenerate, |
| 4364 // which would lead to code stub initialization state being messed up. | 2930 // which would lead to code stub initialization state being messed up. |
| 4365 Code* save_doubles_code; | 2931 Code* save_doubles_code; |
| 4366 if (!save_doubles.FindCodeInCache(&save_doubles_code, isolate)) { | 2932 if (!save_doubles.FindCodeInCache(&save_doubles_code, isolate)) { |
| (...skipping 3085 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7452 __ bind(&fast_elements_case); | 6018 __ bind(&fast_elements_case); |
| 7453 GenerateCase(masm, FAST_ELEMENTS); | 6019 GenerateCase(masm, FAST_ELEMENTS); |
| 7454 } | 6020 } |
| 7455 | 6021 |
| 7456 | 6022 |
| 7457 #undef __ | 6023 #undef __ |
| 7458 | 6024 |
| 7459 } } // namespace v8::internal | 6025 } } // namespace v8::internal |
| 7460 | 6026 |
| 7461 #endif // V8_TARGET_ARCH_IA32 | 6027 #endif // V8_TARGET_ARCH_IA32 |
| OLD | NEW |