| OLD | NEW |
| 1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 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 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 284 // Return 1/0 for true/false in eax. | 284 // Return 1/0 for true/false in eax. |
| 285 __ bind(&true_result); | 285 __ bind(&true_result); |
| 286 __ mov(eax, 1); | 286 __ mov(eax, 1); |
| 287 __ ret(1 * kPointerSize); | 287 __ ret(1 * kPointerSize); |
| 288 __ bind(&false_result); | 288 __ bind(&false_result); |
| 289 __ mov(eax, 0); | 289 __ mov(eax, 0); |
| 290 __ ret(1 * kPointerSize); | 290 __ ret(1 * kPointerSize); |
| 291 } | 291 } |
| 292 | 292 |
| 293 | 293 |
| 294 const char* GenericBinaryOpStub::GetName() { | |
| 295 if (name_ != NULL) return name_; | |
| 296 const int kMaxNameLength = 100; | |
| 297 name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray( | |
| 298 kMaxNameLength); | |
| 299 if (name_ == NULL) return "OOM"; | |
| 300 const char* op_name = Token::Name(op_); | |
| 301 const char* overwrite_name; | |
| 302 switch (mode_) { | |
| 303 case NO_OVERWRITE: overwrite_name = "Alloc"; break; | |
| 304 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break; | |
| 305 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break; | |
| 306 default: overwrite_name = "UnknownOverwrite"; break; | |
| 307 } | |
| 308 | |
| 309 OS::SNPrintF(Vector<char>(name_, kMaxNameLength), | |
| 310 "GenericBinaryOpStub_%s_%s%s_%s%s_%s_%s", | |
| 311 op_name, | |
| 312 overwrite_name, | |
| 313 (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "", | |
| 314 args_in_registers_ ? "RegArgs" : "StackArgs", | |
| 315 args_reversed_ ? "_R" : "", | |
| 316 static_operands_type_.ToString(), | |
| 317 BinaryOpIC::GetName(runtime_operands_type_)); | |
| 318 return name_; | |
| 319 } | |
| 320 | |
| 321 | |
| 322 void GenericBinaryOpStub::GenerateCall( | |
| 323 MacroAssembler* masm, | |
| 324 Register left, | |
| 325 Register right) { | |
| 326 if (!ArgsInRegistersSupported()) { | |
| 327 // Pass arguments on the stack. | |
| 328 __ push(left); | |
| 329 __ push(right); | |
| 330 } else { | |
| 331 // The calling convention with registers is left in edx and right in eax. | |
| 332 Register left_arg = edx; | |
| 333 Register right_arg = eax; | |
| 334 if (!(left.is(left_arg) && right.is(right_arg))) { | |
| 335 if (left.is(right_arg) && right.is(left_arg)) { | |
| 336 if (IsOperationCommutative()) { | |
| 337 SetArgsReversed(); | |
| 338 } else { | |
| 339 __ xchg(left, right); | |
| 340 } | |
| 341 } else if (left.is(left_arg)) { | |
| 342 __ mov(right_arg, right); | |
| 343 } else if (right.is(right_arg)) { | |
| 344 __ mov(left_arg, left); | |
| 345 } else if (left.is(right_arg)) { | |
| 346 if (IsOperationCommutative()) { | |
| 347 __ mov(left_arg, right); | |
| 348 SetArgsReversed(); | |
| 349 } else { | |
| 350 // Order of moves important to avoid destroying left argument. | |
| 351 __ mov(left_arg, left); | |
| 352 __ mov(right_arg, right); | |
| 353 } | |
| 354 } else if (right.is(left_arg)) { | |
| 355 if (IsOperationCommutative()) { | |
| 356 __ mov(right_arg, left); | |
| 357 SetArgsReversed(); | |
| 358 } else { | |
| 359 // Order of moves important to avoid destroying right argument. | |
| 360 __ mov(right_arg, right); | |
| 361 __ mov(left_arg, left); | |
| 362 } | |
| 363 } else { | |
| 364 // Order of moves is not important. | |
| 365 __ mov(left_arg, left); | |
| 366 __ mov(right_arg, right); | |
| 367 } | |
| 368 } | |
| 369 | |
| 370 // Update flags to indicate that arguments are in registers. | |
| 371 SetArgsInRegisters(); | |
| 372 __ IncrementCounter( | |
| 373 masm->isolate()->counters()->generic_binary_stub_calls_regs(), 1); | |
| 374 } | |
| 375 | |
| 376 // Call the stub. | |
| 377 __ CallStub(this); | |
| 378 } | |
| 379 | |
| 380 | |
| 381 void GenericBinaryOpStub::GenerateCall( | |
| 382 MacroAssembler* masm, | |
| 383 Register left, | |
| 384 Smi* right) { | |
| 385 if (!ArgsInRegistersSupported()) { | |
| 386 // Pass arguments on the stack. | |
| 387 __ push(left); | |
| 388 __ push(Immediate(right)); | |
| 389 } else { | |
| 390 // The calling convention with registers is left in edx and right in eax. | |
| 391 Register left_arg = edx; | |
| 392 Register right_arg = eax; | |
| 393 if (left.is(left_arg)) { | |
| 394 __ mov(right_arg, Immediate(right)); | |
| 395 } else if (left.is(right_arg) && IsOperationCommutative()) { | |
| 396 __ mov(left_arg, Immediate(right)); | |
| 397 SetArgsReversed(); | |
| 398 } else { | |
| 399 // For non-commutative operations, left and right_arg might be | |
| 400 // the same register. Therefore, the order of the moves is | |
| 401 // important here in order to not overwrite left before moving | |
| 402 // it to left_arg. | |
| 403 __ mov(left_arg, left); | |
| 404 __ mov(right_arg, Immediate(right)); | |
| 405 } | |
| 406 | |
| 407 // Update flags to indicate that arguments are in registers. | |
| 408 SetArgsInRegisters(); | |
| 409 __ IncrementCounter( | |
| 410 masm->isolate()->counters()->generic_binary_stub_calls_regs(), 1); | |
| 411 } | |
| 412 | |
| 413 // Call the stub. | |
| 414 __ CallStub(this); | |
| 415 } | |
| 416 | |
| 417 | |
| 418 void GenericBinaryOpStub::GenerateCall( | |
| 419 MacroAssembler* masm, | |
| 420 Smi* left, | |
| 421 Register right) { | |
| 422 if (!ArgsInRegistersSupported()) { | |
| 423 // Pass arguments on the stack. | |
| 424 __ push(Immediate(left)); | |
| 425 __ push(right); | |
| 426 } else { | |
| 427 // The calling convention with registers is left in edx and right in eax. | |
| 428 Register left_arg = edx; | |
| 429 Register right_arg = eax; | |
| 430 if (right.is(right_arg)) { | |
| 431 __ mov(left_arg, Immediate(left)); | |
| 432 } else if (right.is(left_arg) && IsOperationCommutative()) { | |
| 433 __ mov(right_arg, Immediate(left)); | |
| 434 SetArgsReversed(); | |
| 435 } else { | |
| 436 // For non-commutative operations, right and left_arg might be | |
| 437 // the same register. Therefore, the order of the moves is | |
| 438 // important here in order to not overwrite right before moving | |
| 439 // it to right_arg. | |
| 440 __ mov(right_arg, right); | |
| 441 __ mov(left_arg, Immediate(left)); | |
| 442 } | |
| 443 // Update flags to indicate that arguments are in registers. | |
| 444 SetArgsInRegisters(); | |
| 445 Counters* counters = masm->isolate()->counters(); | |
| 446 __ IncrementCounter(counters->generic_binary_stub_calls_regs(), 1); | |
| 447 } | |
| 448 | |
| 449 // Call the stub. | |
| 450 __ CallStub(this); | |
| 451 } | |
| 452 | |
| 453 | |
| 454 class FloatingPointHelper : public AllStatic { | 294 class FloatingPointHelper : public AllStatic { |
| 455 public: | 295 public: |
| 456 | 296 |
| 457 enum ArgLocation { | 297 enum ArgLocation { |
| 458 ARGS_ON_STACK, | 298 ARGS_ON_STACK, |
| 459 ARGS_IN_REGISTERS | 299 ARGS_IN_REGISTERS |
| 460 }; | 300 }; |
| 461 | 301 |
| 462 // Code pattern for loading a floating point value. Input value must | 302 // Code pattern for loading a floating point value. Input value must |
| 463 // be either a smi or a heap number object (fp value). Requirements: | 303 // be either a smi or a heap number object (fp value). Requirements: |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 527 static void LoadSSE2Smis(MacroAssembler* masm, Register scratch); | 367 static void LoadSSE2Smis(MacroAssembler* masm, Register scratch); |
| 528 | 368 |
| 529 // Checks that the two floating point numbers loaded into xmm0 and xmm1 | 369 // Checks that the two floating point numbers loaded into xmm0 and xmm1 |
| 530 // have int32 values. | 370 // have int32 values. |
| 531 static void CheckSSE2OperandsAreInt32(MacroAssembler* masm, | 371 static void CheckSSE2OperandsAreInt32(MacroAssembler* masm, |
| 532 Label* non_int32, | 372 Label* non_int32, |
| 533 Register scratch); | 373 Register scratch); |
| 534 }; | 374 }; |
| 535 | 375 |
| 536 | 376 |
| 537 void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) { | |
| 538 // 1. Move arguments into edx, eax except for DIV and MOD, which need the | |
| 539 // dividend in eax and edx free for the division. Use eax, ebx for those. | |
| 540 Comment load_comment(masm, "-- Load arguments"); | |
| 541 Register left = edx; | |
| 542 Register right = eax; | |
| 543 if (op_ == Token::DIV || op_ == Token::MOD) { | |
| 544 left = eax; | |
| 545 right = ebx; | |
| 546 if (HasArgsInRegisters()) { | |
| 547 __ mov(ebx, eax); | |
| 548 __ mov(eax, edx); | |
| 549 } | |
| 550 } | |
| 551 if (!HasArgsInRegisters()) { | |
| 552 __ mov(right, Operand(esp, 1 * kPointerSize)); | |
| 553 __ mov(left, Operand(esp, 2 * kPointerSize)); | |
| 554 } | |
| 555 | |
| 556 if (static_operands_type_.IsSmi()) { | |
| 557 if (FLAG_debug_code) { | |
| 558 __ AbortIfNotSmi(left); | |
| 559 __ AbortIfNotSmi(right); | |
| 560 } | |
| 561 if (op_ == Token::BIT_OR) { | |
| 562 __ or_(right, Operand(left)); | |
| 563 GenerateReturn(masm); | |
| 564 return; | |
| 565 } else if (op_ == Token::BIT_AND) { | |
| 566 __ and_(right, Operand(left)); | |
| 567 GenerateReturn(masm); | |
| 568 return; | |
| 569 } else if (op_ == Token::BIT_XOR) { | |
| 570 __ xor_(right, Operand(left)); | |
| 571 GenerateReturn(masm); | |
| 572 return; | |
| 573 } | |
| 574 } | |
| 575 | |
| 576 // 2. Prepare the smi check of both operands by oring them together. | |
| 577 Comment smi_check_comment(masm, "-- Smi check arguments"); | |
| 578 Label not_smis; | |
| 579 Register combined = ecx; | |
| 580 ASSERT(!left.is(combined) && !right.is(combined)); | |
| 581 switch (op_) { | |
| 582 case Token::BIT_OR: | |
| 583 // Perform the operation into eax and smi check the result. Preserve | |
| 584 // eax in case the result is not a smi. | |
| 585 ASSERT(!left.is(ecx) && !right.is(ecx)); | |
| 586 __ mov(ecx, right); | |
| 587 __ or_(right, Operand(left)); // Bitwise or is commutative. | |
| 588 combined = right; | |
| 589 break; | |
| 590 | |
| 591 case Token::BIT_XOR: | |
| 592 case Token::BIT_AND: | |
| 593 case Token::ADD: | |
| 594 case Token::SUB: | |
| 595 case Token::MUL: | |
| 596 case Token::DIV: | |
| 597 case Token::MOD: | |
| 598 __ mov(combined, right); | |
| 599 __ or_(combined, Operand(left)); | |
| 600 break; | |
| 601 | |
| 602 case Token::SHL: | |
| 603 case Token::SAR: | |
| 604 case Token::SHR: | |
| 605 // Move the right operand into ecx for the shift operation, use eax | |
| 606 // for the smi check register. | |
| 607 ASSERT(!left.is(ecx) && !right.is(ecx)); | |
| 608 __ mov(ecx, right); | |
| 609 __ or_(right, Operand(left)); | |
| 610 combined = right; | |
| 611 break; | |
| 612 | |
| 613 default: | |
| 614 break; | |
| 615 } | |
| 616 | |
| 617 // 3. Perform the smi check of the operands. | |
| 618 STATIC_ASSERT(kSmiTag == 0); // Adjust zero check if not the case. | |
| 619 __ test(combined, Immediate(kSmiTagMask)); | |
| 620 __ j(not_zero, ¬_smis, not_taken); | |
| 621 | |
| 622 // 4. Operands are both smis, perform the operation leaving the result in | |
| 623 // eax and check the result if necessary. | |
| 624 Comment perform_smi(masm, "-- Perform smi operation"); | |
| 625 Label use_fp_on_smis; | |
| 626 switch (op_) { | |
| 627 case Token::BIT_OR: | |
| 628 // Nothing to do. | |
| 629 break; | |
| 630 | |
| 631 case Token::BIT_XOR: | |
| 632 ASSERT(right.is(eax)); | |
| 633 __ xor_(right, Operand(left)); // Bitwise xor is commutative. | |
| 634 break; | |
| 635 | |
| 636 case Token::BIT_AND: | |
| 637 ASSERT(right.is(eax)); | |
| 638 __ and_(right, Operand(left)); // Bitwise and is commutative. | |
| 639 break; | |
| 640 | |
| 641 case Token::SHL: | |
| 642 // Remove tags from operands (but keep sign). | |
| 643 __ SmiUntag(left); | |
| 644 __ SmiUntag(ecx); | |
| 645 // Perform the operation. | |
| 646 __ shl_cl(left); | |
| 647 // Check that the *signed* result fits in a smi. | |
| 648 __ cmp(left, 0xc0000000); | |
| 649 __ j(sign, &use_fp_on_smis, not_taken); | |
| 650 // Tag the result and store it in register eax. | |
| 651 __ SmiTag(left); | |
| 652 __ mov(eax, left); | |
| 653 break; | |
| 654 | |
| 655 case Token::SAR: | |
| 656 // Remove tags from operands (but keep sign). | |
| 657 __ SmiUntag(left); | |
| 658 __ SmiUntag(ecx); | |
| 659 // Perform the operation. | |
| 660 __ sar_cl(left); | |
| 661 // Tag the result and store it in register eax. | |
| 662 __ SmiTag(left); | |
| 663 __ mov(eax, left); | |
| 664 break; | |
| 665 | |
| 666 case Token::SHR: | |
| 667 // Remove tags from operands (but keep sign). | |
| 668 __ SmiUntag(left); | |
| 669 __ SmiUntag(ecx); | |
| 670 // Perform the operation. | |
| 671 __ shr_cl(left); | |
| 672 // Check that the *unsigned* result fits in a smi. | |
| 673 // Neither of the two high-order bits can be set: | |
| 674 // - 0x80000000: high bit would be lost when smi tagging. | |
| 675 // - 0x40000000: this number would convert to negative when | |
| 676 // Smi tagging these two cases can only happen with shifts | |
| 677 // by 0 or 1 when handed a valid smi. | |
| 678 __ test(left, Immediate(0xc0000000)); | |
| 679 __ j(not_zero, slow, not_taken); | |
| 680 // Tag the result and store it in register eax. | |
| 681 __ SmiTag(left); | |
| 682 __ mov(eax, left); | |
| 683 break; | |
| 684 | |
| 685 case Token::ADD: | |
| 686 ASSERT(right.is(eax)); | |
| 687 __ add(right, Operand(left)); // Addition is commutative. | |
| 688 __ j(overflow, &use_fp_on_smis, not_taken); | |
| 689 break; | |
| 690 | |
| 691 case Token::SUB: | |
| 692 __ sub(left, Operand(right)); | |
| 693 __ j(overflow, &use_fp_on_smis, not_taken); | |
| 694 __ mov(eax, left); | |
| 695 break; | |
| 696 | |
| 697 case Token::MUL: | |
| 698 // If the smi tag is 0 we can just leave the tag on one operand. | |
| 699 STATIC_ASSERT(kSmiTag == 0); // Adjust code below if not the case. | |
| 700 // We can't revert the multiplication if the result is not a smi | |
| 701 // so save the right operand. | |
| 702 __ mov(ebx, right); | |
| 703 // Remove tag from one of the operands (but keep sign). | |
| 704 __ SmiUntag(right); | |
| 705 // Do multiplication. | |
| 706 __ imul(right, Operand(left)); // Multiplication is commutative. | |
| 707 __ j(overflow, &use_fp_on_smis, not_taken); | |
| 708 // Check for negative zero result. Use combined = left | right. | |
| 709 __ NegativeZeroTest(right, combined, &use_fp_on_smis); | |
| 710 break; | |
| 711 | |
| 712 case Token::DIV: | |
| 713 // We can't revert the division if the result is not a smi so | |
| 714 // save the left operand. | |
| 715 __ mov(edi, left); | |
| 716 // Check for 0 divisor. | |
| 717 __ test(right, Operand(right)); | |
| 718 __ j(zero, &use_fp_on_smis, not_taken); | |
| 719 // Sign extend left into edx:eax. | |
| 720 ASSERT(left.is(eax)); | |
| 721 __ cdq(); | |
| 722 // Divide edx:eax by right. | |
| 723 __ idiv(right); | |
| 724 // Check for the corner case of dividing the most negative smi by | |
| 725 // -1. We cannot use the overflow flag, since it is not set by idiv | |
| 726 // instruction. | |
| 727 STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); | |
| 728 __ cmp(eax, 0x40000000); | |
| 729 __ j(equal, &use_fp_on_smis); | |
| 730 // Check for negative zero result. Use combined = left | right. | |
| 731 __ NegativeZeroTest(eax, combined, &use_fp_on_smis); | |
| 732 // Check that the remainder is zero. | |
| 733 __ test(edx, Operand(edx)); | |
| 734 __ j(not_zero, &use_fp_on_smis); | |
| 735 // Tag the result and store it in register eax. | |
| 736 __ SmiTag(eax); | |
| 737 break; | |
| 738 | |
| 739 case Token::MOD: | |
| 740 // Check for 0 divisor. | |
| 741 __ test(right, Operand(right)); | |
| 742 __ j(zero, ¬_smis, not_taken); | |
| 743 | |
| 744 // Sign extend left into edx:eax. | |
| 745 ASSERT(left.is(eax)); | |
| 746 __ cdq(); | |
| 747 // Divide edx:eax by right. | |
| 748 __ idiv(right); | |
| 749 // Check for negative zero result. Use combined = left | right. | |
| 750 __ NegativeZeroTest(edx, combined, slow); | |
| 751 // Move remainder to register eax. | |
| 752 __ mov(eax, edx); | |
| 753 break; | |
| 754 | |
| 755 default: | |
| 756 UNREACHABLE(); | |
| 757 } | |
| 758 | |
| 759 // 5. Emit return of result in eax. | |
| 760 GenerateReturn(masm); | |
| 761 | |
| 762 // 6. For some operations emit inline code to perform floating point | |
| 763 // operations on known smis (e.g., if the result of the operation | |
| 764 // overflowed the smi range). | |
| 765 switch (op_) { | |
| 766 case Token::SHL: { | |
| 767 Comment perform_float(masm, "-- Perform float operation on smis"); | |
| 768 __ bind(&use_fp_on_smis); | |
| 769 if (runtime_operands_type_ != BinaryOpIC::UNINIT_OR_SMI) { | |
| 770 // Result we want is in left == edx, so we can put the allocated heap | |
| 771 // number in eax. | |
| 772 __ AllocateHeapNumber(eax, ecx, ebx, slow); | |
| 773 // Store the result in the HeapNumber and return. | |
| 774 if (CpuFeatures::IsSupported(SSE2)) { | |
| 775 CpuFeatures::Scope use_sse2(SSE2); | |
| 776 __ cvtsi2sd(xmm0, Operand(left)); | |
| 777 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); | |
| 778 } else { | |
| 779 // It's OK to overwrite the right argument on the stack because we | |
| 780 // are about to return. | |
| 781 __ mov(Operand(esp, 1 * kPointerSize), left); | |
| 782 __ fild_s(Operand(esp, 1 * kPointerSize)); | |
| 783 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | |
| 784 } | |
| 785 GenerateReturn(masm); | |
| 786 } else { | |
| 787 ASSERT(runtime_operands_type_ == BinaryOpIC::UNINIT_OR_SMI); | |
| 788 __ jmp(slow); | |
| 789 } | |
| 790 break; | |
| 791 } | |
| 792 | |
| 793 case Token::ADD: | |
| 794 case Token::SUB: | |
| 795 case Token::MUL: | |
| 796 case Token::DIV: { | |
| 797 Comment perform_float(masm, "-- Perform float operation on smis"); | |
| 798 __ bind(&use_fp_on_smis); | |
| 799 // Restore arguments to edx, eax. | |
| 800 switch (op_) { | |
| 801 case Token::ADD: | |
| 802 // Revert right = right + left. | |
| 803 __ sub(right, Operand(left)); | |
| 804 break; | |
| 805 case Token::SUB: | |
| 806 // Revert left = left - right. | |
| 807 __ add(left, Operand(right)); | |
| 808 break; | |
| 809 case Token::MUL: | |
| 810 // Right was clobbered but a copy is in ebx. | |
| 811 __ mov(right, ebx); | |
| 812 break; | |
| 813 case Token::DIV: | |
| 814 // Left was clobbered but a copy is in edi. Right is in ebx for | |
| 815 // division. | |
| 816 __ mov(edx, edi); | |
| 817 __ mov(eax, right); | |
| 818 break; | |
| 819 default: UNREACHABLE(); | |
| 820 break; | |
| 821 } | |
| 822 if (runtime_operands_type_ != BinaryOpIC::UNINIT_OR_SMI) { | |
| 823 __ AllocateHeapNumber(ecx, ebx, no_reg, slow); | |
| 824 if (CpuFeatures::IsSupported(SSE2)) { | |
| 825 CpuFeatures::Scope use_sse2(SSE2); | |
| 826 FloatingPointHelper::LoadSSE2Smis(masm, ebx); | |
| 827 switch (op_) { | |
| 828 case Token::ADD: __ addsd(xmm0, xmm1); break; | |
| 829 case Token::SUB: __ subsd(xmm0, xmm1); break; | |
| 830 case Token::MUL: __ mulsd(xmm0, xmm1); break; | |
| 831 case Token::DIV: __ divsd(xmm0, xmm1); break; | |
| 832 default: UNREACHABLE(); | |
| 833 } | |
| 834 __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm0); | |
| 835 } else { // SSE2 not available, use FPU. | |
| 836 FloatingPointHelper::LoadFloatSmis(masm, ebx); | |
| 837 switch (op_) { | |
| 838 case Token::ADD: __ faddp(1); break; | |
| 839 case Token::SUB: __ fsubp(1); break; | |
| 840 case Token::MUL: __ fmulp(1); break; | |
| 841 case Token::DIV: __ fdivp(1); break; | |
| 842 default: UNREACHABLE(); | |
| 843 } | |
| 844 __ fstp_d(FieldOperand(ecx, HeapNumber::kValueOffset)); | |
| 845 } | |
| 846 __ mov(eax, ecx); | |
| 847 GenerateReturn(masm); | |
| 848 } else { | |
| 849 ASSERT(runtime_operands_type_ == BinaryOpIC::UNINIT_OR_SMI); | |
| 850 __ jmp(slow); | |
| 851 } | |
| 852 break; | |
| 853 } | |
| 854 | |
| 855 default: | |
| 856 break; | |
| 857 } | |
| 858 | |
| 859 // 7. Non-smi operands, fall out to the non-smi code with the operands in | |
| 860 // edx and eax. | |
| 861 Comment done_comment(masm, "-- Enter non-smi code"); | |
| 862 __ bind(¬_smis); | |
| 863 switch (op_) { | |
| 864 case Token::BIT_OR: | |
| 865 case Token::SHL: | |
| 866 case Token::SAR: | |
| 867 case Token::SHR: | |
| 868 // Right operand is saved in ecx and eax was destroyed by the smi | |
| 869 // check. | |
| 870 __ mov(eax, ecx); | |
| 871 break; | |
| 872 | |
| 873 case Token::DIV: | |
| 874 case Token::MOD: | |
| 875 // Operands are in eax, ebx at this point. | |
| 876 __ mov(edx, eax); | |
| 877 __ mov(eax, ebx); | |
| 878 break; | |
| 879 | |
| 880 default: | |
| 881 break; | |
| 882 } | |
| 883 } | |
| 884 | |
| 885 | |
| 886 void GenericBinaryOpStub::Generate(MacroAssembler* masm) { | |
| 887 Label call_runtime; | |
| 888 | |
| 889 Counters* counters = masm->isolate()->counters(); | |
| 890 __ IncrementCounter(counters->generic_binary_stub_calls(), 1); | |
| 891 | |
| 892 if (runtime_operands_type_ == BinaryOpIC::UNINIT_OR_SMI) { | |
| 893 Label slow; | |
| 894 if (ShouldGenerateSmiCode()) GenerateSmiCode(masm, &slow); | |
| 895 __ bind(&slow); | |
| 896 GenerateTypeTransition(masm); | |
| 897 } | |
| 898 | |
| 899 // Generate fast case smi code if requested. This flag is set when the fast | |
| 900 // case smi code is not generated by the caller. Generating it here will speed | |
| 901 // up common operations. | |
| 902 if (ShouldGenerateSmiCode()) { | |
| 903 GenerateSmiCode(masm, &call_runtime); | |
| 904 } else if (op_ != Token::MOD) { // MOD goes straight to runtime. | |
| 905 if (!HasArgsInRegisters()) { | |
| 906 GenerateLoadArguments(masm); | |
| 907 } | |
| 908 } | |
| 909 | |
| 910 // Floating point case. | |
| 911 if (ShouldGenerateFPCode()) { | |
| 912 switch (op_) { | |
| 913 case Token::ADD: | |
| 914 case Token::SUB: | |
| 915 case Token::MUL: | |
| 916 case Token::DIV: { | |
| 917 if (runtime_operands_type_ == BinaryOpIC::DEFAULT && | |
| 918 HasSmiCodeInStub()) { | |
| 919 // Execution reaches this point when the first non-smi argument occurs | |
| 920 // (and only if smi code is generated). This is the right moment to | |
| 921 // patch to HEAP_NUMBERS state. The transition is attempted only for | |
| 922 // the four basic operations. The stub stays in the DEFAULT state | |
| 923 // forever for all other operations (also if smi code is skipped). | |
| 924 GenerateTypeTransition(masm); | |
| 925 break; | |
| 926 } | |
| 927 | |
| 928 Label not_floats; | |
| 929 if (CpuFeatures::IsSupported(SSE2)) { | |
| 930 CpuFeatures::Scope use_sse2(SSE2); | |
| 931 if (static_operands_type_.IsNumber()) { | |
| 932 if (FLAG_debug_code) { | |
| 933 // Assert at runtime that inputs are only numbers. | |
| 934 __ AbortIfNotNumber(edx); | |
| 935 __ AbortIfNotNumber(eax); | |
| 936 } | |
| 937 if (static_operands_type_.IsSmi()) { | |
| 938 if (FLAG_debug_code) { | |
| 939 __ AbortIfNotSmi(edx); | |
| 940 __ AbortIfNotSmi(eax); | |
| 941 } | |
| 942 FloatingPointHelper::LoadSSE2Smis(masm, ecx); | |
| 943 } else { | |
| 944 FloatingPointHelper::LoadSSE2Operands(masm); | |
| 945 } | |
| 946 } else { | |
| 947 FloatingPointHelper::LoadSSE2Operands(masm, ¬_floats); | |
| 948 } | |
| 949 | |
| 950 switch (op_) { | |
| 951 case Token::ADD: __ addsd(xmm0, xmm1); break; | |
| 952 case Token::SUB: __ subsd(xmm0, xmm1); break; | |
| 953 case Token::MUL: __ mulsd(xmm0, xmm1); break; | |
| 954 case Token::DIV: __ divsd(xmm0, xmm1); break; | |
| 955 default: UNREACHABLE(); | |
| 956 } | |
| 957 GenerateHeapResultAllocation(masm, &call_runtime); | |
| 958 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); | |
| 959 GenerateReturn(masm); | |
| 960 } else { // SSE2 not available, use FPU. | |
| 961 if (static_operands_type_.IsNumber()) { | |
| 962 if (FLAG_debug_code) { | |
| 963 // Assert at runtime that inputs are only numbers. | |
| 964 __ AbortIfNotNumber(edx); | |
| 965 __ AbortIfNotNumber(eax); | |
| 966 } | |
| 967 } else { | |
| 968 FloatingPointHelper::CheckFloatOperands(masm, ¬_floats, ebx); | |
| 969 } | |
| 970 FloatingPointHelper::LoadFloatOperands( | |
| 971 masm, | |
| 972 ecx, | |
| 973 FloatingPointHelper::ARGS_IN_REGISTERS); | |
| 974 switch (op_) { | |
| 975 case Token::ADD: __ faddp(1); break; | |
| 976 case Token::SUB: __ fsubp(1); break; | |
| 977 case Token::MUL: __ fmulp(1); break; | |
| 978 case Token::DIV: __ fdivp(1); break; | |
| 979 default: UNREACHABLE(); | |
| 980 } | |
| 981 Label after_alloc_failure; | |
| 982 GenerateHeapResultAllocation(masm, &after_alloc_failure); | |
| 983 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | |
| 984 GenerateReturn(masm); | |
| 985 __ bind(&after_alloc_failure); | |
| 986 __ ffree(); | |
| 987 __ jmp(&call_runtime); | |
| 988 } | |
| 989 __ bind(¬_floats); | |
| 990 if (runtime_operands_type_ == BinaryOpIC::DEFAULT && | |
| 991 !HasSmiCodeInStub()) { | |
| 992 // Execution reaches this point when the first non-number argument | |
| 993 // occurs (and only if smi code is skipped from the stub, otherwise | |
| 994 // the patching has already been done earlier in this case branch). | |
| 995 // Try patching to STRINGS for ADD operation. | |
| 996 if (op_ == Token::ADD) { | |
| 997 GenerateTypeTransition(masm); | |
| 998 } | |
| 999 } | |
| 1000 break; | |
| 1001 } | |
| 1002 case Token::MOD: { | |
| 1003 // For MOD we go directly to runtime in the non-smi case. | |
| 1004 break; | |
| 1005 } | |
| 1006 case Token::BIT_OR: | |
| 1007 case Token::BIT_AND: | |
| 1008 case Token::BIT_XOR: | |
| 1009 case Token::SAR: | |
| 1010 case Token::SHL: | |
| 1011 case Token::SHR: { | |
| 1012 Label non_smi_result; | |
| 1013 FloatingPointHelper::LoadAsIntegers(masm, | |
| 1014 static_operands_type_, | |
| 1015 use_sse3_, | |
| 1016 &call_runtime); | |
| 1017 switch (op_) { | |
| 1018 case Token::BIT_OR: __ or_(eax, Operand(ecx)); break; | |
| 1019 case Token::BIT_AND: __ and_(eax, Operand(ecx)); break; | |
| 1020 case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break; | |
| 1021 case Token::SAR: __ sar_cl(eax); break; | |
| 1022 case Token::SHL: __ shl_cl(eax); break; | |
| 1023 case Token::SHR: __ shr_cl(eax); break; | |
| 1024 default: UNREACHABLE(); | |
| 1025 } | |
| 1026 if (op_ == Token::SHR) { | |
| 1027 // Check if result is non-negative and fits in a smi. | |
| 1028 __ test(eax, Immediate(0xc0000000)); | |
| 1029 __ j(not_zero, &call_runtime); | |
| 1030 } else { | |
| 1031 // Check if result fits in a smi. | |
| 1032 __ cmp(eax, 0xc0000000); | |
| 1033 __ j(negative, &non_smi_result); | |
| 1034 } | |
| 1035 // Tag smi result and return. | |
| 1036 __ SmiTag(eax); | |
| 1037 GenerateReturn(masm); | |
| 1038 | |
| 1039 // All ops except SHR return a signed int32 that we load in | |
| 1040 // a HeapNumber. | |
| 1041 if (op_ != Token::SHR) { | |
| 1042 __ bind(&non_smi_result); | |
| 1043 // Allocate a heap number if needed. | |
| 1044 __ mov(ebx, Operand(eax)); // ebx: result | |
| 1045 NearLabel skip_allocation; | |
| 1046 switch (mode_) { | |
| 1047 case OVERWRITE_LEFT: | |
| 1048 case OVERWRITE_RIGHT: | |
| 1049 // If the operand was an object, we skip the | |
| 1050 // allocation of a heap number. | |
| 1051 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ? | |
| 1052 1 * kPointerSize : 2 * kPointerSize)); | |
| 1053 __ test(eax, Immediate(kSmiTagMask)); | |
| 1054 __ j(not_zero, &skip_allocation, not_taken); | |
| 1055 // Fall through! | |
| 1056 case NO_OVERWRITE: | |
| 1057 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime); | |
| 1058 __ bind(&skip_allocation); | |
| 1059 break; | |
| 1060 default: UNREACHABLE(); | |
| 1061 } | |
| 1062 // Store the result in the HeapNumber and return. | |
| 1063 if (CpuFeatures::IsSupported(SSE2)) { | |
| 1064 CpuFeatures::Scope use_sse2(SSE2); | |
| 1065 __ cvtsi2sd(xmm0, Operand(ebx)); | |
| 1066 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); | |
| 1067 } else { | |
| 1068 __ mov(Operand(esp, 1 * kPointerSize), ebx); | |
| 1069 __ fild_s(Operand(esp, 1 * kPointerSize)); | |
| 1070 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | |
| 1071 } | |
| 1072 GenerateReturn(masm); | |
| 1073 } | |
| 1074 break; | |
| 1075 } | |
| 1076 default: UNREACHABLE(); break; | |
| 1077 } | |
| 1078 } | |
| 1079 | |
| 1080 // If all else fails, use the runtime system to get the correct | |
| 1081 // result. If arguments was passed in registers now place them on the | |
| 1082 // stack in the correct order below the return address. | |
| 1083 | |
| 1084 // Avoid hitting the string ADD code below when allocation fails in | |
| 1085 // the floating point code above. | |
| 1086 if (op_ != Token::ADD) { | |
| 1087 __ bind(&call_runtime); | |
| 1088 } | |
| 1089 | |
| 1090 if (HasArgsInRegisters()) { | |
| 1091 GenerateRegisterArgsPush(masm); | |
| 1092 } | |
| 1093 | |
| 1094 switch (op_) { | |
| 1095 case Token::ADD: { | |
| 1096 // Test for string arguments before calling runtime. | |
| 1097 | |
| 1098 // If this stub has already generated FP-specific code then the arguments | |
| 1099 // are already in edx, eax | |
| 1100 if (!ShouldGenerateFPCode() && !HasArgsInRegisters()) { | |
| 1101 GenerateLoadArguments(masm); | |
| 1102 } | |
| 1103 | |
| 1104 // Registers containing left and right operands respectively. | |
| 1105 Register lhs, rhs; | |
| 1106 if (HasArgsReversed()) { | |
| 1107 lhs = eax; | |
| 1108 rhs = edx; | |
| 1109 } else { | |
| 1110 lhs = edx; | |
| 1111 rhs = eax; | |
| 1112 } | |
| 1113 | |
| 1114 // Test if left operand is a string. | |
| 1115 NearLabel lhs_not_string; | |
| 1116 __ test(lhs, Immediate(kSmiTagMask)); | |
| 1117 __ j(zero, &lhs_not_string); | |
| 1118 __ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, ecx); | |
| 1119 __ j(above_equal, &lhs_not_string); | |
| 1120 | |
| 1121 StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB); | |
| 1122 __ TailCallStub(&string_add_left_stub); | |
| 1123 | |
| 1124 NearLabel call_runtime_with_args; | |
| 1125 // Left operand is not a string, test right. | |
| 1126 __ bind(&lhs_not_string); | |
| 1127 __ test(rhs, Immediate(kSmiTagMask)); | |
| 1128 __ j(zero, &call_runtime_with_args); | |
| 1129 __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, ecx); | |
| 1130 __ j(above_equal, &call_runtime_with_args); | |
| 1131 | |
| 1132 StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB); | |
| 1133 __ TailCallStub(&string_add_right_stub); | |
| 1134 | |
| 1135 // Neither argument is a string. | |
| 1136 __ bind(&call_runtime); | |
| 1137 if (HasArgsInRegisters()) { | |
| 1138 GenerateRegisterArgsPush(masm); | |
| 1139 } | |
| 1140 __ bind(&call_runtime_with_args); | |
| 1141 __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION); | |
| 1142 break; | |
| 1143 } | |
| 1144 case Token::SUB: | |
| 1145 __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION); | |
| 1146 break; | |
| 1147 case Token::MUL: | |
| 1148 __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION); | |
| 1149 break; | |
| 1150 case Token::DIV: | |
| 1151 __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION); | |
| 1152 break; | |
| 1153 case Token::MOD: | |
| 1154 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION); | |
| 1155 break; | |
| 1156 case Token::BIT_OR: | |
| 1157 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION); | |
| 1158 break; | |
| 1159 case Token::BIT_AND: | |
| 1160 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION); | |
| 1161 break; | |
| 1162 case Token::BIT_XOR: | |
| 1163 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION); | |
| 1164 break; | |
| 1165 case Token::SAR: | |
| 1166 __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION); | |
| 1167 break; | |
| 1168 case Token::SHL: | |
| 1169 __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION); | |
| 1170 break; | |
| 1171 case Token::SHR: | |
| 1172 __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION); | |
| 1173 break; | |
| 1174 default: | |
| 1175 UNREACHABLE(); | |
| 1176 } | |
| 1177 } | |
| 1178 | |
| 1179 | |
| 1180 void GenericBinaryOpStub::GenerateHeapResultAllocation(MacroAssembler* masm, | |
| 1181 Label* alloc_failure) { | |
| 1182 Label skip_allocation; | |
| 1183 OverwriteMode mode = mode_; | |
| 1184 if (HasArgsReversed()) { | |
| 1185 if (mode == OVERWRITE_RIGHT) { | |
| 1186 mode = OVERWRITE_LEFT; | |
| 1187 } else if (mode == OVERWRITE_LEFT) { | |
| 1188 mode = OVERWRITE_RIGHT; | |
| 1189 } | |
| 1190 } | |
| 1191 switch (mode) { | |
| 1192 case OVERWRITE_LEFT: { | |
| 1193 // If the argument in edx is already an object, we skip the | |
| 1194 // allocation of a heap number. | |
| 1195 __ test(edx, Immediate(kSmiTagMask)); | |
| 1196 __ j(not_zero, &skip_allocation, not_taken); | |
| 1197 // Allocate a heap number for the result. Keep eax and edx intact | |
| 1198 // for the possible runtime call. | |
| 1199 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure); | |
| 1200 // Now edx can be overwritten losing one of the arguments as we are | |
| 1201 // now done and will not need it any more. | |
| 1202 __ mov(edx, Operand(ebx)); | |
| 1203 __ bind(&skip_allocation); | |
| 1204 // Use object in edx as a result holder | |
| 1205 __ mov(eax, Operand(edx)); | |
| 1206 break; | |
| 1207 } | |
| 1208 case OVERWRITE_RIGHT: | |
| 1209 // If the argument in eax is already an object, we skip the | |
| 1210 // allocation of a heap number. | |
| 1211 __ test(eax, Immediate(kSmiTagMask)); | |
| 1212 __ j(not_zero, &skip_allocation, not_taken); | |
| 1213 // Fall through! | |
| 1214 case NO_OVERWRITE: | |
| 1215 // Allocate a heap number for the result. Keep eax and edx intact | |
| 1216 // for the possible runtime call. | |
| 1217 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure); | |
| 1218 // Now eax can be overwritten losing one of the arguments as we are | |
| 1219 // now done and will not need it any more. | |
| 1220 __ mov(eax, ebx); | |
| 1221 __ bind(&skip_allocation); | |
| 1222 break; | |
| 1223 default: UNREACHABLE(); | |
| 1224 } | |
| 1225 } | |
| 1226 | |
| 1227 | |
| 1228 void GenericBinaryOpStub::GenerateLoadArguments(MacroAssembler* masm) { | |
| 1229 // If arguments are not passed in registers read them from the stack. | |
| 1230 ASSERT(!HasArgsInRegisters()); | |
| 1231 __ mov(eax, Operand(esp, 1 * kPointerSize)); | |
| 1232 __ mov(edx, Operand(esp, 2 * kPointerSize)); | |
| 1233 } | |
| 1234 | |
| 1235 | |
| 1236 void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) { | |
| 1237 // If arguments are not passed in registers remove them from the stack before | |
| 1238 // returning. | |
| 1239 if (!HasArgsInRegisters()) { | |
| 1240 __ ret(2 * kPointerSize); // Remove both operands | |
| 1241 } else { | |
| 1242 __ ret(0); | |
| 1243 } | |
| 1244 } | |
| 1245 | |
| 1246 | |
| 1247 void GenericBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) { | |
| 1248 ASSERT(HasArgsInRegisters()); | |
| 1249 __ pop(ecx); | |
| 1250 if (HasArgsReversed()) { | |
| 1251 __ push(eax); | |
| 1252 __ push(edx); | |
| 1253 } else { | |
| 1254 __ push(edx); | |
| 1255 __ push(eax); | |
| 1256 } | |
| 1257 __ push(ecx); | |
| 1258 } | |
| 1259 | |
| 1260 | |
| 1261 void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { | |
| 1262 // Ensure the operands are on the stack. | |
| 1263 if (HasArgsInRegisters()) { | |
| 1264 GenerateRegisterArgsPush(masm); | |
| 1265 } | |
| 1266 | |
| 1267 __ pop(ecx); // Save return address. | |
| 1268 | |
| 1269 // Left and right arguments are now on top. | |
| 1270 // Push this stub's key. Although the operation and the type info are | |
| 1271 // encoded into the key, the encoding is opaque, so push them too. | |
| 1272 __ push(Immediate(Smi::FromInt(MinorKey()))); | |
| 1273 __ push(Immediate(Smi::FromInt(op_))); | |
| 1274 __ push(Immediate(Smi::FromInt(runtime_operands_type_))); | |
| 1275 | |
| 1276 __ push(ecx); // Push return address. | |
| 1277 | |
| 1278 // Patch the caller to an appropriate specialized stub and return the | |
| 1279 // operation result to the caller of the stub. | |
| 1280 __ TailCallExternalReference( | |
| 1281 ExternalReference(IC_Utility(IC::kBinaryOp_Patch), masm->isolate()), | |
| 1282 5, | |
| 1283 1); | |
| 1284 } | |
| 1285 | |
| 1286 | |
| 1287 Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) { | |
| 1288 GenericBinaryOpStub stub(key, type_info); | |
| 1289 return stub.GetCode(); | |
| 1290 } | |
| 1291 | |
| 1292 | |
| 1293 Handle<Code> GetTypeRecordingBinaryOpStub(int key, | 377 Handle<Code> GetTypeRecordingBinaryOpStub(int key, |
| 1294 TRBinaryOpIC::TypeInfo type_info, | 378 TRBinaryOpIC::TypeInfo type_info, |
| 1295 TRBinaryOpIC::TypeInfo result_type_info) { | 379 TRBinaryOpIC::TypeInfo result_type_info) { |
| 1296 TypeRecordingBinaryOpStub stub(key, type_info, result_type_info); | 380 TypeRecordingBinaryOpStub stub(key, type_info, result_type_info); |
| 1297 return stub.GetCode(); | 381 return stub.GetCode(); |
| 1298 } | 382 } |
| 1299 | 383 |
| 1300 | 384 |
| 1301 void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { | 385 void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { |
| 1302 __ pop(ecx); // Save return address. | 386 __ pop(ecx); // Save return address. |
| (...skipping 5237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6540 // Do a tail call to the rewritten stub. | 5624 // Do a tail call to the rewritten stub. |
| 6541 __ jmp(Operand(edi)); | 5625 __ jmp(Operand(edi)); |
| 6542 } | 5626 } |
| 6543 | 5627 |
| 6544 | 5628 |
| 6545 #undef __ | 5629 #undef __ |
| 6546 | 5630 |
| 6547 } } // namespace v8::internal | 5631 } } // namespace v8::internal |
| 6548 | 5632 |
| 6549 #endif // V8_TARGET_ARCH_IA32 | 5633 #endif // V8_TARGET_ARCH_IA32 |
| OLD | NEW |