| 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 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 274 // Return 1/0 for true/false in rax. | 274 // Return 1/0 for true/false in rax. |
| 275 __ bind(&true_result); | 275 __ bind(&true_result); |
| 276 __ movq(rax, Immediate(1)); | 276 __ movq(rax, Immediate(1)); |
| 277 __ ret(1 * kPointerSize); | 277 __ ret(1 * kPointerSize); |
| 278 __ bind(&false_result); | 278 __ bind(&false_result); |
| 279 __ Set(rax, 0); | 279 __ Set(rax, 0); |
| 280 __ ret(1 * kPointerSize); | 280 __ ret(1 * kPointerSize); |
| 281 } | 281 } |
| 282 | 282 |
| 283 | 283 |
| 284 const char* GenericBinaryOpStub::GetName() { | |
| 285 if (name_ != NULL) return name_; | |
| 286 const int kMaxNameLength = 100; | |
| 287 name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray( | |
| 288 kMaxNameLength); | |
| 289 if (name_ == NULL) return "OOM"; | |
| 290 const char* op_name = Token::Name(op_); | |
| 291 const char* overwrite_name; | |
| 292 switch (mode_) { | |
| 293 case NO_OVERWRITE: overwrite_name = "Alloc"; break; | |
| 294 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break; | |
| 295 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break; | |
| 296 default: overwrite_name = "UnknownOverwrite"; break; | |
| 297 } | |
| 298 | |
| 299 OS::SNPrintF(Vector<char>(name_, kMaxNameLength), | |
| 300 "GenericBinaryOpStub_%s_%s%s_%s%s_%s_%s", | |
| 301 op_name, | |
| 302 overwrite_name, | |
| 303 (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "", | |
| 304 args_in_registers_ ? "RegArgs" : "StackArgs", | |
| 305 args_reversed_ ? "_R" : "", | |
| 306 static_operands_type_.ToString(), | |
| 307 BinaryOpIC::GetName(runtime_operands_type_)); | |
| 308 return name_; | |
| 309 } | |
| 310 | |
| 311 | |
| 312 void GenericBinaryOpStub::GenerateCall( | |
| 313 MacroAssembler* masm, | |
| 314 Register left, | |
| 315 Register right) { | |
| 316 if (!ArgsInRegistersSupported()) { | |
| 317 // Pass arguments on the stack. | |
| 318 __ push(left); | |
| 319 __ push(right); | |
| 320 } else { | |
| 321 // The calling convention with registers is left in rdx and right in rax. | |
| 322 Register left_arg = rdx; | |
| 323 Register right_arg = rax; | |
| 324 if (!(left.is(left_arg) && right.is(right_arg))) { | |
| 325 if (left.is(right_arg) && right.is(left_arg)) { | |
| 326 if (IsOperationCommutative()) { | |
| 327 SetArgsReversed(); | |
| 328 } else { | |
| 329 __ xchg(left, right); | |
| 330 } | |
| 331 } else if (left.is(left_arg)) { | |
| 332 __ movq(right_arg, right); | |
| 333 } else if (right.is(right_arg)) { | |
| 334 __ movq(left_arg, left); | |
| 335 } else if (left.is(right_arg)) { | |
| 336 if (IsOperationCommutative()) { | |
| 337 __ movq(left_arg, right); | |
| 338 SetArgsReversed(); | |
| 339 } else { | |
| 340 // Order of moves important to avoid destroying left argument. | |
| 341 __ movq(left_arg, left); | |
| 342 __ movq(right_arg, right); | |
| 343 } | |
| 344 } else if (right.is(left_arg)) { | |
| 345 if (IsOperationCommutative()) { | |
| 346 __ movq(right_arg, left); | |
| 347 SetArgsReversed(); | |
| 348 } else { | |
| 349 // Order of moves important to avoid destroying right argument. | |
| 350 __ movq(right_arg, right); | |
| 351 __ movq(left_arg, left); | |
| 352 } | |
| 353 } else { | |
| 354 // Order of moves is not important. | |
| 355 __ movq(left_arg, left); | |
| 356 __ movq(right_arg, right); | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 // Update flags to indicate that arguments are in registers. | |
| 361 SetArgsInRegisters(); | |
| 362 Counters* counters = masm->isolate()->counters(); | |
| 363 __ IncrementCounter(counters->generic_binary_stub_calls_regs(), 1); | |
| 364 } | |
| 365 | |
| 366 // Call the stub. | |
| 367 __ CallStub(this); | |
| 368 } | |
| 369 | |
| 370 | |
| 371 void GenericBinaryOpStub::GenerateCall( | |
| 372 MacroAssembler* masm, | |
| 373 Register left, | |
| 374 Smi* right) { | |
| 375 if (!ArgsInRegistersSupported()) { | |
| 376 // Pass arguments on the stack. | |
| 377 __ push(left); | |
| 378 __ Push(right); | |
| 379 } else { | |
| 380 // The calling convention with registers is left in rdx and right in rax. | |
| 381 Register left_arg = rdx; | |
| 382 Register right_arg = rax; | |
| 383 if (left.is(left_arg)) { | |
| 384 __ Move(right_arg, right); | |
| 385 } else if (left.is(right_arg) && IsOperationCommutative()) { | |
| 386 __ Move(left_arg, right); | |
| 387 SetArgsReversed(); | |
| 388 } else { | |
| 389 // For non-commutative operations, left and right_arg might be | |
| 390 // the same register. Therefore, the order of the moves is | |
| 391 // important here in order to not overwrite left before moving | |
| 392 // it to left_arg. | |
| 393 __ movq(left_arg, left); | |
| 394 __ Move(right_arg, right); | |
| 395 } | |
| 396 | |
| 397 // Update flags to indicate that arguments are in registers. | |
| 398 SetArgsInRegisters(); | |
| 399 Counters* counters = masm->isolate()->counters(); | |
| 400 __ IncrementCounter(counters->generic_binary_stub_calls_regs(), 1); | |
| 401 } | |
| 402 | |
| 403 // Call the stub. | |
| 404 __ CallStub(this); | |
| 405 } | |
| 406 | |
| 407 | |
| 408 void GenericBinaryOpStub::GenerateCall( | |
| 409 MacroAssembler* masm, | |
| 410 Smi* left, | |
| 411 Register right) { | |
| 412 if (!ArgsInRegistersSupported()) { | |
| 413 // Pass arguments on the stack. | |
| 414 __ Push(left); | |
| 415 __ push(right); | |
| 416 } else { | |
| 417 // The calling convention with registers is left in rdx and right in rax. | |
| 418 Register left_arg = rdx; | |
| 419 Register right_arg = rax; | |
| 420 if (right.is(right_arg)) { | |
| 421 __ Move(left_arg, left); | |
| 422 } else if (right.is(left_arg) && IsOperationCommutative()) { | |
| 423 __ Move(right_arg, left); | |
| 424 SetArgsReversed(); | |
| 425 } else { | |
| 426 // For non-commutative operations, right and left_arg might be | |
| 427 // the same register. Therefore, the order of the moves is | |
| 428 // important here in order to not overwrite right before moving | |
| 429 // it to right_arg. | |
| 430 __ movq(right_arg, right); | |
| 431 __ Move(left_arg, left); | |
| 432 } | |
| 433 // Update flags to indicate that arguments are in registers. | |
| 434 SetArgsInRegisters(); | |
| 435 Counters* counters = masm->isolate()->counters(); | |
| 436 __ IncrementCounter(counters->generic_binary_stub_calls_regs(), 1); | |
| 437 } | |
| 438 | |
| 439 // Call the stub. | |
| 440 __ CallStub(this); | |
| 441 } | |
| 442 | |
| 443 | |
| 444 class FloatingPointHelper : public AllStatic { | 284 class FloatingPointHelper : public AllStatic { |
| 445 public: | 285 public: |
| 446 // Load the operands from rdx and rax into xmm0 and xmm1, as doubles. | 286 // Load the operands from rdx and rax into xmm0 and xmm1, as doubles. |
| 447 // If the operands are not both numbers, jump to not_numbers. | 287 // If the operands are not both numbers, jump to not_numbers. |
| 448 // Leaves rdx and rax unchanged. SmiOperands assumes both are smis. | 288 // Leaves rdx and rax unchanged. SmiOperands assumes both are smis. |
| 449 // NumberOperands assumes both are smis or heap numbers. | 289 // NumberOperands assumes both are smis or heap numbers. |
| 450 static void LoadSSE2SmiOperands(MacroAssembler* masm); | 290 static void LoadSSE2SmiOperands(MacroAssembler* masm); |
| 451 static void LoadSSE2NumberOperands(MacroAssembler* masm); | 291 static void LoadSSE2NumberOperands(MacroAssembler* masm); |
| 452 static void LoadSSE2UnknownOperands(MacroAssembler* masm, | 292 static void LoadSSE2UnknownOperands(MacroAssembler* masm, |
| 453 Label* not_numbers); | 293 Label* not_numbers); |
| 454 | 294 |
| 455 // Takes the operands in rdx and rax and loads them as integers in rax | 295 // Takes the operands in rdx and rax and loads them as integers in rax |
| 456 // and rcx. | 296 // and rcx. |
| 457 static void LoadAsIntegers(MacroAssembler* masm, | 297 static void LoadAsIntegers(MacroAssembler* masm, |
| 458 Label* operand_conversion_failure, | 298 Label* operand_conversion_failure, |
| 459 Register heap_number_map); | 299 Register heap_number_map); |
| 460 // As above, but we know the operands to be numbers. In that case, | 300 // As above, but we know the operands to be numbers. In that case, |
| 461 // conversion can't fail. | 301 // conversion can't fail. |
| 462 static void LoadNumbersAsIntegers(MacroAssembler* masm); | 302 static void LoadNumbersAsIntegers(MacroAssembler* masm); |
| 463 }; | 303 }; |
| 464 | 304 |
| 465 | 305 |
| 466 void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) { | |
| 467 // 1. Move arguments into rdx, rax except for DIV and MOD, which need the | |
| 468 // dividend in rax and rdx free for the division. Use rax, rbx for those. | |
| 469 Comment load_comment(masm, "-- Load arguments"); | |
| 470 Register left = rdx; | |
| 471 Register right = rax; | |
| 472 if (op_ == Token::DIV || op_ == Token::MOD) { | |
| 473 left = rax; | |
| 474 right = rbx; | |
| 475 if (HasArgsInRegisters()) { | |
| 476 __ movq(rbx, rax); | |
| 477 __ movq(rax, rdx); | |
| 478 } | |
| 479 } | |
| 480 if (!HasArgsInRegisters()) { | |
| 481 __ movq(right, Operand(rsp, 1 * kPointerSize)); | |
| 482 __ movq(left, Operand(rsp, 2 * kPointerSize)); | |
| 483 } | |
| 484 | |
| 485 Label not_smis; | |
| 486 // 2. Smi check both operands. | |
| 487 if (static_operands_type_.IsSmi()) { | |
| 488 // Skip smi check if we know that both arguments are smis. | |
| 489 if (FLAG_debug_code) { | |
| 490 __ AbortIfNotSmi(left); | |
| 491 __ AbortIfNotSmi(right); | |
| 492 } | |
| 493 if (op_ == Token::BIT_OR) { | |
| 494 // Handle OR here, since we do extra smi-checking in the or code below. | |
| 495 __ SmiOr(right, right, left); | |
| 496 GenerateReturn(masm); | |
| 497 return; | |
| 498 } | |
| 499 } else { | |
| 500 if (op_ != Token::BIT_OR) { | |
| 501 // Skip the check for OR as it is better combined with the | |
| 502 // actual operation. | |
| 503 Comment smi_check_comment(masm, "-- Smi check arguments"); | |
| 504 __ JumpIfNotBothSmi(left, right, ¬_smis); | |
| 505 } | |
| 506 } | |
| 507 | |
| 508 // 3. Operands are both smis (except for OR), perform the operation leaving | |
| 509 // the result in rax and check the result if necessary. | |
| 510 Comment perform_smi(masm, "-- Perform smi operation"); | |
| 511 Label use_fp_on_smis; | |
| 512 switch (op_) { | |
| 513 case Token::ADD: { | |
| 514 ASSERT(right.is(rax)); | |
| 515 __ SmiAdd(right, right, left, &use_fp_on_smis); // ADD is commutative. | |
| 516 break; | |
| 517 } | |
| 518 | |
| 519 case Token::SUB: { | |
| 520 __ SmiSub(left, left, right, &use_fp_on_smis); | |
| 521 __ movq(rax, left); | |
| 522 break; | |
| 523 } | |
| 524 | |
| 525 case Token::MUL: | |
| 526 ASSERT(right.is(rax)); | |
| 527 __ SmiMul(right, right, left, &use_fp_on_smis); // MUL is commutative. | |
| 528 break; | |
| 529 | |
| 530 case Token::DIV: | |
| 531 ASSERT(left.is(rax)); | |
| 532 __ SmiDiv(left, left, right, &use_fp_on_smis); | |
| 533 break; | |
| 534 | |
| 535 case Token::MOD: | |
| 536 ASSERT(left.is(rax)); | |
| 537 __ SmiMod(left, left, right, slow); | |
| 538 break; | |
| 539 | |
| 540 case Token::BIT_OR: | |
| 541 ASSERT(right.is(rax)); | |
| 542 __ movq(rcx, right); // Save the right operand. | |
| 543 __ SmiOr(right, right, left); // BIT_OR is commutative. | |
| 544 __ testb(right, Immediate(kSmiTagMask)); | |
| 545 __ j(not_zero, ¬_smis); | |
| 546 break; | |
| 547 | |
| 548 case Token::BIT_AND: | |
| 549 ASSERT(right.is(rax)); | |
| 550 __ SmiAnd(right, right, left); // BIT_AND is commutative. | |
| 551 break; | |
| 552 | |
| 553 case Token::BIT_XOR: | |
| 554 ASSERT(right.is(rax)); | |
| 555 __ SmiXor(right, right, left); // BIT_XOR is commutative. | |
| 556 break; | |
| 557 | |
| 558 case Token::SHL: | |
| 559 case Token::SHR: | |
| 560 case Token::SAR: | |
| 561 switch (op_) { | |
| 562 case Token::SAR: | |
| 563 __ SmiShiftArithmeticRight(left, left, right); | |
| 564 break; | |
| 565 case Token::SHR: | |
| 566 __ SmiShiftLogicalRight(left, left, right, slow); | |
| 567 break; | |
| 568 case Token::SHL: | |
| 569 __ SmiShiftLeft(left, left, right); | |
| 570 break; | |
| 571 default: | |
| 572 UNREACHABLE(); | |
| 573 } | |
| 574 __ movq(rax, left); | |
| 575 break; | |
| 576 | |
| 577 default: | |
| 578 UNREACHABLE(); | |
| 579 break; | |
| 580 } | |
| 581 | |
| 582 // 4. Emit return of result in rax. | |
| 583 GenerateReturn(masm); | |
| 584 | |
| 585 // 5. For some operations emit inline code to perform floating point | |
| 586 // operations on known smis (e.g., if the result of the operation | |
| 587 // overflowed the smi range). | |
| 588 switch (op_) { | |
| 589 case Token::ADD: | |
| 590 case Token::SUB: | |
| 591 case Token::MUL: | |
| 592 case Token::DIV: { | |
| 593 ASSERT(use_fp_on_smis.is_linked()); | |
| 594 __ bind(&use_fp_on_smis); | |
| 595 if (op_ == Token::DIV) { | |
| 596 __ movq(rdx, rax); | |
| 597 __ movq(rax, rbx); | |
| 598 } | |
| 599 // left is rdx, right is rax. | |
| 600 __ AllocateHeapNumber(rbx, rcx, slow); | |
| 601 FloatingPointHelper::LoadSSE2SmiOperands(masm); | |
| 602 switch (op_) { | |
| 603 case Token::ADD: __ addsd(xmm0, xmm1); break; | |
| 604 case Token::SUB: __ subsd(xmm0, xmm1); break; | |
| 605 case Token::MUL: __ mulsd(xmm0, xmm1); break; | |
| 606 case Token::DIV: __ divsd(xmm0, xmm1); break; | |
| 607 default: UNREACHABLE(); | |
| 608 } | |
| 609 __ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm0); | |
| 610 __ movq(rax, rbx); | |
| 611 GenerateReturn(masm); | |
| 612 } | |
| 613 default: | |
| 614 break; | |
| 615 } | |
| 616 | |
| 617 // 6. Non-smi operands, fall out to the non-smi code with the operands in | |
| 618 // rdx and rax. | |
| 619 Comment done_comment(masm, "-- Enter non-smi code"); | |
| 620 __ bind(¬_smis); | |
| 621 | |
| 622 switch (op_) { | |
| 623 case Token::DIV: | |
| 624 case Token::MOD: | |
| 625 // Operands are in rax, rbx at this point. | |
| 626 __ movq(rdx, rax); | |
| 627 __ movq(rax, rbx); | |
| 628 break; | |
| 629 | |
| 630 case Token::BIT_OR: | |
| 631 // Right operand is saved in rcx and rax was destroyed by the smi | |
| 632 // operation. | |
| 633 __ movq(rax, rcx); | |
| 634 break; | |
| 635 | |
| 636 default: | |
| 637 break; | |
| 638 } | |
| 639 } | |
| 640 | |
| 641 | |
| 642 void GenericBinaryOpStub::Generate(MacroAssembler* masm) { | |
| 643 Label call_runtime; | |
| 644 | |
| 645 if (ShouldGenerateSmiCode()) { | |
| 646 GenerateSmiCode(masm, &call_runtime); | |
| 647 } else if (op_ != Token::MOD) { | |
| 648 if (!HasArgsInRegisters()) { | |
| 649 GenerateLoadArguments(masm); | |
| 650 } | |
| 651 } | |
| 652 // Floating point case. | |
| 653 if (ShouldGenerateFPCode()) { | |
| 654 switch (op_) { | |
| 655 case Token::ADD: | |
| 656 case Token::SUB: | |
| 657 case Token::MUL: | |
| 658 case Token::DIV: { | |
| 659 if (runtime_operands_type_ == BinaryOpIC::DEFAULT && | |
| 660 HasSmiCodeInStub()) { | |
| 661 // Execution reaches this point when the first non-smi argument occurs | |
| 662 // (and only if smi code is generated). This is the right moment to | |
| 663 // patch to HEAP_NUMBERS state. The transition is attempted only for | |
| 664 // the four basic operations. The stub stays in the DEFAULT state | |
| 665 // forever for all other operations (also if smi code is skipped). | |
| 666 GenerateTypeTransition(masm); | |
| 667 break; | |
| 668 } | |
| 669 | |
| 670 Label not_floats; | |
| 671 // rax: y | |
| 672 // rdx: x | |
| 673 if (static_operands_type_.IsNumber()) { | |
| 674 if (FLAG_debug_code) { | |
| 675 // Assert at runtime that inputs are only numbers. | |
| 676 __ AbortIfNotNumber(rdx); | |
| 677 __ AbortIfNotNumber(rax); | |
| 678 } | |
| 679 FloatingPointHelper::LoadSSE2NumberOperands(masm); | |
| 680 } else { | |
| 681 FloatingPointHelper::LoadSSE2UnknownOperands(masm, &call_runtime); | |
| 682 } | |
| 683 | |
| 684 switch (op_) { | |
| 685 case Token::ADD: __ addsd(xmm0, xmm1); break; | |
| 686 case Token::SUB: __ subsd(xmm0, xmm1); break; | |
| 687 case Token::MUL: __ mulsd(xmm0, xmm1); break; | |
| 688 case Token::DIV: __ divsd(xmm0, xmm1); break; | |
| 689 default: UNREACHABLE(); | |
| 690 } | |
| 691 // Allocate a heap number, if needed. | |
| 692 Label skip_allocation; | |
| 693 OverwriteMode mode = mode_; | |
| 694 if (HasArgsReversed()) { | |
| 695 if (mode == OVERWRITE_RIGHT) { | |
| 696 mode = OVERWRITE_LEFT; | |
| 697 } else if (mode == OVERWRITE_LEFT) { | |
| 698 mode = OVERWRITE_RIGHT; | |
| 699 } | |
| 700 } | |
| 701 switch (mode) { | |
| 702 case OVERWRITE_LEFT: | |
| 703 __ JumpIfNotSmi(rdx, &skip_allocation); | |
| 704 __ AllocateHeapNumber(rbx, rcx, &call_runtime); | |
| 705 __ movq(rdx, rbx); | |
| 706 __ bind(&skip_allocation); | |
| 707 __ movq(rax, rdx); | |
| 708 break; | |
| 709 case OVERWRITE_RIGHT: | |
| 710 // If the argument in rax is already an object, we skip the | |
| 711 // allocation of a heap number. | |
| 712 __ JumpIfNotSmi(rax, &skip_allocation); | |
| 713 // Fall through! | |
| 714 case NO_OVERWRITE: | |
| 715 // Allocate a heap number for the result. Keep rax and rdx intact | |
| 716 // for the possible runtime call. | |
| 717 __ AllocateHeapNumber(rbx, rcx, &call_runtime); | |
| 718 __ movq(rax, rbx); | |
| 719 __ bind(&skip_allocation); | |
| 720 break; | |
| 721 default: UNREACHABLE(); | |
| 722 } | |
| 723 __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0); | |
| 724 GenerateReturn(masm); | |
| 725 __ bind(¬_floats); | |
| 726 if (runtime_operands_type_ == BinaryOpIC::DEFAULT && | |
| 727 !HasSmiCodeInStub()) { | |
| 728 // Execution reaches this point when the first non-number argument | |
| 729 // occurs (and only if smi code is skipped from the stub, otherwise | |
| 730 // the patching has already been done earlier in this case branch). | |
| 731 // A perfect moment to try patching to STRINGS for ADD operation. | |
| 732 if (op_ == Token::ADD) { | |
| 733 GenerateTypeTransition(masm); | |
| 734 } | |
| 735 } | |
| 736 break; | |
| 737 } | |
| 738 case Token::MOD: { | |
| 739 // For MOD we go directly to runtime in the non-smi case. | |
| 740 break; | |
| 741 } | |
| 742 case Token::BIT_OR: | |
| 743 case Token::BIT_AND: | |
| 744 case Token::BIT_XOR: | |
| 745 case Token::SAR: | |
| 746 case Token::SHL: | |
| 747 case Token::SHR: { | |
| 748 Label skip_allocation, non_smi_shr_result; | |
| 749 Register heap_number_map = r9; | |
| 750 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); | |
| 751 if (static_operands_type_.IsNumber()) { | |
| 752 if (FLAG_debug_code) { | |
| 753 // Assert at runtime that inputs are only numbers. | |
| 754 __ AbortIfNotNumber(rdx); | |
| 755 __ AbortIfNotNumber(rax); | |
| 756 } | |
| 757 FloatingPointHelper::LoadNumbersAsIntegers(masm); | |
| 758 } else { | |
| 759 FloatingPointHelper::LoadAsIntegers(masm, | |
| 760 &call_runtime, | |
| 761 heap_number_map); | |
| 762 } | |
| 763 switch (op_) { | |
| 764 case Token::BIT_OR: __ orl(rax, rcx); break; | |
| 765 case Token::BIT_AND: __ andl(rax, rcx); break; | |
| 766 case Token::BIT_XOR: __ xorl(rax, rcx); break; | |
| 767 case Token::SAR: __ sarl_cl(rax); break; | |
| 768 case Token::SHL: __ shll_cl(rax); break; | |
| 769 case Token::SHR: { | |
| 770 __ shrl_cl(rax); | |
| 771 // Check if result is negative. This can only happen for a shift | |
| 772 // by zero. | |
| 773 __ testl(rax, rax); | |
| 774 __ j(negative, &non_smi_shr_result); | |
| 775 break; | |
| 776 } | |
| 777 default: UNREACHABLE(); | |
| 778 } | |
| 779 | |
| 780 STATIC_ASSERT(kSmiValueSize == 32); | |
| 781 // Tag smi result and return. | |
| 782 __ Integer32ToSmi(rax, rax); | |
| 783 GenerateReturn(masm); | |
| 784 | |
| 785 // All bit-ops except SHR return a signed int32 that can be | |
| 786 // returned immediately as a smi. | |
| 787 // We might need to allocate a HeapNumber if we shift a negative | |
| 788 // number right by zero (i.e., convert to UInt32). | |
| 789 if (op_ == Token::SHR) { | |
| 790 ASSERT(non_smi_shr_result.is_linked()); | |
| 791 __ bind(&non_smi_shr_result); | |
| 792 // Allocate a heap number if needed. | |
| 793 __ movl(rbx, rax); // rbx holds result value (uint32 value as int64). | |
| 794 switch (mode_) { | |
| 795 case OVERWRITE_LEFT: | |
| 796 case OVERWRITE_RIGHT: | |
| 797 // If the operand was an object, we skip the | |
| 798 // allocation of a heap number. | |
| 799 __ movq(rax, Operand(rsp, mode_ == OVERWRITE_RIGHT ? | |
| 800 1 * kPointerSize : 2 * kPointerSize)); | |
| 801 __ JumpIfNotSmi(rax, &skip_allocation); | |
| 802 // Fall through! | |
| 803 case NO_OVERWRITE: | |
| 804 // Allocate heap number in new space. | |
| 805 // Not using AllocateHeapNumber macro in order to reuse | |
| 806 // already loaded heap_number_map. | |
| 807 __ AllocateInNewSpace(HeapNumber::kSize, | |
| 808 rax, | |
| 809 rcx, | |
| 810 no_reg, | |
| 811 &call_runtime, | |
| 812 TAG_OBJECT); | |
| 813 // Set the map. | |
| 814 if (FLAG_debug_code) { | |
| 815 __ AbortIfNotRootValue(heap_number_map, | |
| 816 Heap::kHeapNumberMapRootIndex, | |
| 817 "HeapNumberMap register clobbered."); | |
| 818 } | |
| 819 __ movq(FieldOperand(rax, HeapObject::kMapOffset), | |
| 820 heap_number_map); | |
| 821 __ bind(&skip_allocation); | |
| 822 break; | |
| 823 default: UNREACHABLE(); | |
| 824 } | |
| 825 // Store the result in the HeapNumber and return. | |
| 826 __ cvtqsi2sd(xmm0, rbx); | |
| 827 __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0); | |
| 828 GenerateReturn(masm); | |
| 829 } | |
| 830 | |
| 831 break; | |
| 832 } | |
| 833 default: UNREACHABLE(); break; | |
| 834 } | |
| 835 } | |
| 836 | |
| 837 // If all else fails, use the runtime system to get the correct | |
| 838 // result. If arguments was passed in registers now place them on the | |
| 839 // stack in the correct order below the return address. | |
| 840 __ bind(&call_runtime); | |
| 841 | |
| 842 if (HasArgsInRegisters()) { | |
| 843 GenerateRegisterArgsPush(masm); | |
| 844 } | |
| 845 | |
| 846 switch (op_) { | |
| 847 case Token::ADD: { | |
| 848 // Registers containing left and right operands respectively. | |
| 849 Register lhs, rhs; | |
| 850 | |
| 851 if (HasArgsReversed()) { | |
| 852 lhs = rax; | |
| 853 rhs = rdx; | |
| 854 } else { | |
| 855 lhs = rdx; | |
| 856 rhs = rax; | |
| 857 } | |
| 858 | |
| 859 // Test for string arguments before calling runtime. | |
| 860 Label not_strings, both_strings, not_string1, string1, string1_smi2; | |
| 861 | |
| 862 // If this stub has already generated FP-specific code then the arguments | |
| 863 // are already in rdx and rax. | |
| 864 if (!ShouldGenerateFPCode() && !HasArgsInRegisters()) { | |
| 865 GenerateLoadArguments(masm); | |
| 866 } | |
| 867 | |
| 868 Condition is_smi; | |
| 869 is_smi = masm->CheckSmi(lhs); | |
| 870 __ j(is_smi, ¬_string1); | |
| 871 __ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, r8); | |
| 872 __ j(above_equal, ¬_string1); | |
| 873 | |
| 874 // First argument is a a string, test second. | |
| 875 is_smi = masm->CheckSmi(rhs); | |
| 876 __ j(is_smi, &string1_smi2); | |
| 877 __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, r9); | |
| 878 __ j(above_equal, &string1); | |
| 879 | |
| 880 // First and second argument are strings. | |
| 881 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB); | |
| 882 __ TailCallStub(&string_add_stub); | |
| 883 | |
| 884 __ bind(&string1_smi2); | |
| 885 // First argument is a string, second is a smi. Try to lookup the number | |
| 886 // string for the smi in the number string cache. | |
| 887 NumberToStringStub::GenerateLookupNumberStringCache( | |
| 888 masm, rhs, rbx, rcx, r8, true, &string1); | |
| 889 | |
| 890 // Replace second argument on stack and tailcall string add stub to make | |
| 891 // the result. | |
| 892 __ movq(Operand(rsp, 1 * kPointerSize), rbx); | |
| 893 __ TailCallStub(&string_add_stub); | |
| 894 | |
| 895 // Only first argument is a string. | |
| 896 __ bind(&string1); | |
| 897 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION); | |
| 898 | |
| 899 // First argument was not a string, test second. | |
| 900 __ bind(¬_string1); | |
| 901 is_smi = masm->CheckSmi(rhs); | |
| 902 __ j(is_smi, ¬_strings); | |
| 903 __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, rhs); | |
| 904 __ j(above_equal, ¬_strings); | |
| 905 | |
| 906 // Only second argument is a string. | |
| 907 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION); | |
| 908 | |
| 909 __ bind(¬_strings); | |
| 910 // Neither argument is a string. | |
| 911 __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION); | |
| 912 break; | |
| 913 } | |
| 914 case Token::SUB: | |
| 915 __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION); | |
| 916 break; | |
| 917 case Token::MUL: | |
| 918 __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION); | |
| 919 break; | |
| 920 case Token::DIV: | |
| 921 __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION); | |
| 922 break; | |
| 923 case Token::MOD: | |
| 924 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION); | |
| 925 break; | |
| 926 case Token::BIT_OR: | |
| 927 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION); | |
| 928 break; | |
| 929 case Token::BIT_AND: | |
| 930 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION); | |
| 931 break; | |
| 932 case Token::BIT_XOR: | |
| 933 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION); | |
| 934 break; | |
| 935 case Token::SAR: | |
| 936 __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION); | |
| 937 break; | |
| 938 case Token::SHL: | |
| 939 __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION); | |
| 940 break; | |
| 941 case Token::SHR: | |
| 942 __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION); | |
| 943 break; | |
| 944 default: | |
| 945 UNREACHABLE(); | |
| 946 } | |
| 947 } | |
| 948 | |
| 949 | |
| 950 void GenericBinaryOpStub::GenerateLoadArguments(MacroAssembler* masm) { | |
| 951 ASSERT(!HasArgsInRegisters()); | |
| 952 __ movq(rax, Operand(rsp, 1 * kPointerSize)); | |
| 953 __ movq(rdx, Operand(rsp, 2 * kPointerSize)); | |
| 954 } | |
| 955 | |
| 956 | |
| 957 void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) { | |
| 958 // If arguments are not passed in registers remove them from the stack before | |
| 959 // returning. | |
| 960 if (!HasArgsInRegisters()) { | |
| 961 __ ret(2 * kPointerSize); // Remove both operands | |
| 962 } else { | |
| 963 __ ret(0); | |
| 964 } | |
| 965 } | |
| 966 | |
| 967 | |
| 968 void GenericBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) { | |
| 969 ASSERT(HasArgsInRegisters()); | |
| 970 __ pop(rcx); | |
| 971 if (HasArgsReversed()) { | |
| 972 __ push(rax); | |
| 973 __ push(rdx); | |
| 974 } else { | |
| 975 __ push(rdx); | |
| 976 __ push(rax); | |
| 977 } | |
| 978 __ push(rcx); | |
| 979 } | |
| 980 | |
| 981 | |
| 982 void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { | |
| 983 Label get_result; | |
| 984 | |
| 985 // Ensure the operands are on the stack. | |
| 986 if (HasArgsInRegisters()) { | |
| 987 GenerateRegisterArgsPush(masm); | |
| 988 } | |
| 989 | |
| 990 // Left and right arguments are already on stack. | |
| 991 __ pop(rcx); // Save the return address. | |
| 992 | |
| 993 // Push this stub's key. | |
| 994 __ Push(Smi::FromInt(MinorKey())); | |
| 995 | |
| 996 // Although the operation and the type info are encoded into the key, | |
| 997 // the encoding is opaque, so push them too. | |
| 998 __ Push(Smi::FromInt(op_)); | |
| 999 | |
| 1000 __ Push(Smi::FromInt(runtime_operands_type_)); | |
| 1001 | |
| 1002 __ push(rcx); // The return address. | |
| 1003 | |
| 1004 // Perform patching to an appropriate fast case and return the result. | |
| 1005 __ TailCallExternalReference( | |
| 1006 ExternalReference(IC_Utility(IC::kBinaryOp_Patch), masm->isolate()), | |
| 1007 5, | |
| 1008 1); | |
| 1009 } | |
| 1010 | |
| 1011 | |
| 1012 Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) { | |
| 1013 GenericBinaryOpStub stub(key, type_info); | |
| 1014 return stub.GetCode(); | |
| 1015 } | |
| 1016 | |
| 1017 | |
| 1018 Handle<Code> GetTypeRecordingBinaryOpStub(int key, | 306 Handle<Code> GetTypeRecordingBinaryOpStub(int key, |
| 1019 TRBinaryOpIC::TypeInfo type_info, | 307 TRBinaryOpIC::TypeInfo type_info, |
| 1020 TRBinaryOpIC::TypeInfo result_type_info) { | 308 TRBinaryOpIC::TypeInfo result_type_info) { |
| 1021 TypeRecordingBinaryOpStub stub(key, type_info, result_type_info); | 309 TypeRecordingBinaryOpStub stub(key, type_info, result_type_info); |
| 1022 return stub.GetCode(); | 310 return stub.GetCode(); |
| 1023 } | 311 } |
| 1024 | 312 |
| 1025 | 313 |
| 1026 void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { | 314 void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { |
| 1027 __ pop(rcx); // Save return address. | 315 __ pop(rcx); // Save return address. |
| (...skipping 4114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5142 // Do a tail call to the rewritten stub. | 4430 // Do a tail call to the rewritten stub. |
| 5143 __ jmp(rdi); | 4431 __ jmp(rdi); |
| 5144 } | 4432 } |
| 5145 | 4433 |
| 5146 | 4434 |
| 5147 #undef __ | 4435 #undef __ |
| 5148 | 4436 |
| 5149 } } // namespace v8::internal | 4437 } } // namespace v8::internal |
| 5150 | 4438 |
| 5151 #endif // V8_TARGET_ARCH_X64 | 4439 #endif // V8_TARGET_ARCH_X64 |
| OLD | NEW |