| Index: src/x64/code-stubs-x64.cc
|
| ===================================================================
|
| --- src/x64/code-stubs-x64.cc (revision 7180)
|
| +++ src/x64/code-stubs-x64.cc (working copy)
|
| @@ -46,8 +46,8 @@
|
| __ Ret();
|
|
|
| __ bind(&check_heap_number);
|
| - __ Move(rbx, Factory::heap_number_map());
|
| - __ cmpq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
|
| + __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
|
| + Heap::kHeapNumberMapRootIndex);
|
| __ j(not_equal, &call_builtin);
|
| __ Ret();
|
|
|
| @@ -104,7 +104,7 @@
|
| __ pop(rdx);
|
| __ push(rsi);
|
| __ push(rdx);
|
| - __ Push(Factory::false_value());
|
| + __ PushRoot(Heap::kFalseValueRootIndex);
|
| __ push(rcx); // Restore return address.
|
| __ TailCallRuntime(Runtime::kNewClosure, 3, 1);
|
| }
|
| @@ -1375,54 +1375,33 @@
|
|
|
|
|
| void TypeRecordingBinaryOpStub::GenerateStringAddCode(MacroAssembler* masm) {
|
| - GenerateRegisterArgsPush(masm);
|
| + ASSERT(op_ == Token::ADD);
|
| + NearLabel left_not_string, call_runtime;
|
| +
|
| // Registers containing left and right operands respectively.
|
| - Register lhs = rdx;
|
| - Register rhs = rax;
|
| + Register left = rdx;
|
| + Register right = rax;
|
|
|
| - // Test for string arguments before calling runtime.
|
| - Label not_strings, both_strings, not_string1, string1, string1_smi2;
|
| + // Test if left operand is a string.
|
| + __ JumpIfSmi(left, &left_not_string);
|
| + __ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
|
| + __ j(above_equal, &left_not_string);
|
| + StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
|
| + GenerateRegisterArgsPush(masm);
|
| + __ TailCallStub(&string_add_left_stub);
|
|
|
| - __ JumpIfNotString(lhs, r8, ¬_string1);
|
| + // Left operand is not a string, test right.
|
| + __ bind(&left_not_string);
|
| + __ JumpIfSmi(right, &call_runtime);
|
| + __ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
|
| + __ j(above_equal, &call_runtime);
|
|
|
| - // First argument is a a string, test second.
|
| - __ JumpIfSmi(rhs, &string1_smi2);
|
| - __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, r9);
|
| - __ j(above_equal, &string1);
|
| + StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
|
| + GenerateRegisterArgsPush(masm);
|
| + __ TailCallStub(&string_add_right_stub);
|
|
|
| - // First and second argument are strings.
|
| - StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
|
| - __ TailCallStub(&string_add_stub);
|
| -
|
| - __ bind(&string1_smi2);
|
| - // First argument is a string, second is a smi. Try to lookup the number
|
| - // string for the smi in the number string cache.
|
| - NumberToStringStub::GenerateLookupNumberStringCache(
|
| - masm, rhs, rbx, rcx, r8, true, &string1);
|
| -
|
| - // Replace second argument on stack and tailcall string add stub to make
|
| - // the result.
|
| - __ movq(Operand(rsp, 1 * kPointerSize), rbx);
|
| - __ TailCallStub(&string_add_stub);
|
| -
|
| - // Only first argument is a string.
|
| - __ bind(&string1);
|
| - __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION);
|
| -
|
| - // First argument was not a string, test second.
|
| - __ bind(¬_string1);
|
| - __ JumpIfNotString(rhs, rhs, ¬_strings);
|
| -
|
| - // Only second argument is a string.
|
| - __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION);
|
| -
|
| - __ bind(¬_strings);
|
| // Neither argument is a string.
|
| - // Pop arguments, because CallRuntimeCode wants to push them again.
|
| - __ pop(rcx);
|
| - __ pop(rax);
|
| - __ pop(rdx);
|
| - __ push(rcx);
|
| + __ bind(&call_runtime);
|
| }
|
|
|
|
|
| @@ -1479,9 +1458,11 @@
|
|
|
|
|
| void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
|
| + ASSERT(operands_type_ == TRBinaryOpIC::STRING);
|
| ASSERT(op_ == Token::ADD);
|
| GenerateStringAddCode(masm);
|
| -
|
| + // Try to add arguments as strings, otherwise, transition to the generic
|
| + // TRBinaryOpIC type.
|
| GenerateTypeTransition(masm);
|
| }
|
|
|
| @@ -1564,40 +1545,59 @@
|
|
|
|
|
| void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
|
| - // Input on stack:
|
| - // rsp[8]: argument (should be number).
|
| - // rsp[0]: return address.
|
| + // TAGGED case:
|
| + // Input:
|
| + // rsp[8]: argument (should be number).
|
| + // rsp[0]: return address.
|
| + // Output:
|
| + // rax: tagged double result.
|
| + // UNTAGGED case:
|
| + // Input::
|
| + // rsp[0]: return address.
|
| + // xmm1: untagged double input argument
|
| + // Output:
|
| + // xmm1: untagged double result.
|
| +
|
| Label runtime_call;
|
| Label runtime_call_clear_stack;
|
| - Label input_not_smi;
|
| - NearLabel loaded;
|
| - // Test that rax is a number.
|
| - __ movq(rax, Operand(rsp, kPointerSize));
|
| - __ JumpIfNotSmi(rax, &input_not_smi);
|
| - // Input is a smi. Untag and load it onto the FPU stack.
|
| - // Then load the bits of the double into rbx.
|
| - __ SmiToInteger32(rax, rax);
|
| - __ subq(rsp, Immediate(kPointerSize));
|
| - __ cvtlsi2sd(xmm1, rax);
|
| - __ movsd(Operand(rsp, 0), xmm1);
|
| - __ movq(rbx, xmm1);
|
| - __ movq(rdx, xmm1);
|
| - __ fld_d(Operand(rsp, 0));
|
| - __ addq(rsp, Immediate(kPointerSize));
|
| - __ jmp(&loaded);
|
| + Label skip_cache;
|
| + const bool tagged = (argument_type_ == TAGGED);
|
| + if (tagged) {
|
| + NearLabel input_not_smi;
|
| + NearLabel loaded;
|
| + // Test that rax is a number.
|
| + __ movq(rax, Operand(rsp, kPointerSize));
|
| + __ JumpIfNotSmi(rax, &input_not_smi);
|
| + // Input is a smi. Untag and load it onto the FPU stack.
|
| + // Then load the bits of the double into rbx.
|
| + __ SmiToInteger32(rax, rax);
|
| + __ subq(rsp, Immediate(kDoubleSize));
|
| + __ cvtlsi2sd(xmm1, rax);
|
| + __ movsd(Operand(rsp, 0), xmm1);
|
| + __ movq(rbx, xmm1);
|
| + __ movq(rdx, xmm1);
|
| + __ fld_d(Operand(rsp, 0));
|
| + __ addq(rsp, Immediate(kDoubleSize));
|
| + __ jmp(&loaded);
|
|
|
| - __ bind(&input_not_smi);
|
| - // Check if input is a HeapNumber.
|
| - __ Move(rbx, Factory::heap_number_map());
|
| - __ cmpq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
|
| - __ j(not_equal, &runtime_call);
|
| - // Input is a HeapNumber. Push it on the FPU stack and load its
|
| - // bits into rbx.
|
| - __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset));
|
| - __ movq(rbx, FieldOperand(rax, HeapNumber::kValueOffset));
|
| - __ movq(rdx, rbx);
|
| - __ bind(&loaded);
|
| - // ST[0] == double value
|
| + __ bind(&input_not_smi);
|
| + // Check if input is a HeapNumber.
|
| + __ LoadRoot(rbx, Heap::kHeapNumberMapRootIndex);
|
| + __ cmpq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
|
| + __ j(not_equal, &runtime_call);
|
| + // Input is a HeapNumber. Push it on the FPU stack and load its
|
| + // bits into rbx.
|
| + __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset));
|
| + __ movq(rbx, FieldOperand(rax, HeapNumber::kValueOffset));
|
| + __ movq(rdx, rbx);
|
| +
|
| + __ bind(&loaded);
|
| + } else { // UNTAGGED.
|
| + __ movq(rbx, xmm1);
|
| + __ movq(rdx, xmm1);
|
| + }
|
| +
|
| + // ST[0] == double value, if TAGGED.
|
| // rbx = bits of double value.
|
| // rdx = also bits of double value.
|
| // Compute hash (h is 32 bits, bits are 64 and the shifts are arithmetic):
|
| @@ -1629,7 +1629,7 @@
|
| // rax points to the cache for the type type_.
|
| // If NULL, the cache hasn't been initialized yet, so go through runtime.
|
| __ testq(rax, rax);
|
| - __ j(zero, &runtime_call_clear_stack);
|
| + __ j(zero, &runtime_call_clear_stack); // Only clears stack if TAGGED.
|
| #ifdef DEBUG
|
| // Check that the layout of cache elements match expectations.
|
| { // NOLINT - doesn't like a single brace on a line.
|
| @@ -1655,30 +1655,70 @@
|
| __ j(not_equal, &cache_miss);
|
| // Cache hit!
|
| __ movq(rax, Operand(rcx, 2 * kIntSize));
|
| - __ fstp(0); // Clear FPU stack.
|
| - __ ret(kPointerSize);
|
| + if (tagged) {
|
| + __ fstp(0); // Clear FPU stack.
|
| + __ ret(kPointerSize);
|
| + } else { // UNTAGGED.
|
| + __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset));
|
| + __ Ret();
|
| + }
|
|
|
| __ bind(&cache_miss);
|
| // Update cache with new value.
|
| - Label nan_result;
|
| - GenerateOperation(masm, &nan_result);
|
| + if (tagged) {
|
| __ AllocateHeapNumber(rax, rdi, &runtime_call_clear_stack);
|
| + } else { // UNTAGGED.
|
| + __ AllocateHeapNumber(rax, rdi, &skip_cache);
|
| + __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm1);
|
| + __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset));
|
| + }
|
| + GenerateOperation(masm);
|
| __ movq(Operand(rcx, 0), rbx);
|
| __ movq(Operand(rcx, 2 * kIntSize), rax);
|
| __ fstp_d(FieldOperand(rax, HeapNumber::kValueOffset));
|
| - __ ret(kPointerSize);
|
| + if (tagged) {
|
| + __ ret(kPointerSize);
|
| + } else { // UNTAGGED.
|
| + __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset));
|
| + __ Ret();
|
|
|
| - __ bind(&runtime_call_clear_stack);
|
| - __ fstp(0);
|
| - __ bind(&runtime_call);
|
| - __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
|
| + // Skip cache and return answer directly, only in untagged case.
|
| + __ bind(&skip_cache);
|
| + __ subq(rsp, Immediate(kDoubleSize));
|
| + __ movsd(Operand(rsp, 0), xmm1);
|
| + __ fld_d(Operand(rsp, 0));
|
| + GenerateOperation(masm);
|
| + __ fstp_d(Operand(rsp, 0));
|
| + __ movsd(xmm1, Operand(rsp, 0));
|
| + __ addq(rsp, Immediate(kDoubleSize));
|
| + // We return the value in xmm1 without adding it to the cache, but
|
| + // we cause a scavenging GC so that future allocations will succeed.
|
| + __ EnterInternalFrame();
|
| + // Allocate an unused object bigger than a HeapNumber.
|
| + __ Push(Smi::FromInt(2 * kDoubleSize));
|
| + __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
|
| + __ LeaveInternalFrame();
|
| + __ Ret();
|
| + }
|
|
|
| - __ bind(&nan_result);
|
| - __ fstp(0); // Remove argument from FPU stack.
|
| - __ LoadRoot(rax, Heap::kNanValueRootIndex);
|
| - __ movq(Operand(rcx, 0), rbx);
|
| - __ movq(Operand(rcx, 2 * kIntSize), rax);
|
| - __ ret(kPointerSize);
|
| + // Call runtime, doing whatever allocation and cleanup is necessary.
|
| + if (tagged) {
|
| + __ bind(&runtime_call_clear_stack);
|
| + __ fstp(0);
|
| + __ bind(&runtime_call);
|
| + __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
|
| + } else { // UNTAGGED.
|
| + __ bind(&runtime_call_clear_stack);
|
| + __ bind(&runtime_call);
|
| + __ AllocateHeapNumber(rax, rdi, &skip_cache);
|
| + __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm1);
|
| + __ EnterInternalFrame();
|
| + __ push(rax);
|
| + __ CallRuntime(RuntimeFunction(), 1);
|
| + __ LeaveInternalFrame();
|
| + __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset));
|
| + __ Ret();
|
| + }
|
| }
|
|
|
|
|
| @@ -1695,9 +1735,9 @@
|
| }
|
|
|
|
|
| -void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm,
|
| - Label* on_nan_result) {
|
| +void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
|
| // Registers:
|
| + // rax: Newly allocated HeapNumber, which must be preserved.
|
| // rbx: Bits of input double. Must be preserved.
|
| // rcx: Pointer to cache entry. Must be preserved.
|
| // st(0): Input double
|
| @@ -1719,9 +1759,18 @@
|
| __ j(below, &in_range);
|
| // Check for infinity and NaN. Both return NaN for sin.
|
| __ cmpl(rdi, Immediate(0x7ff));
|
| - __ j(equal, on_nan_result);
|
| + NearLabel non_nan_result;
|
| + __ j(not_equal, &non_nan_result);
|
| + // Input is +/-Infinity or NaN. Result is NaN.
|
| + __ fstp(0);
|
| + __ LoadRoot(kScratchRegister, Heap::kNanValueRootIndex);
|
| + __ fld_d(FieldOperand(kScratchRegister, HeapNumber::kValueOffset));
|
| + __ jmp(&done);
|
|
|
| + __ bind(&non_nan_result);
|
| +
|
| // Use fpmod to restrict argument to the range +/-2*PI.
|
| + __ movq(rdi, rax); // Save rax before using fnstsw_ax.
|
| __ fldpi();
|
| __ fadd(0);
|
| __ fld(1);
|
| @@ -1754,6 +1803,7 @@
|
| // FPU Stack: input % 2*pi, 2*pi,
|
| __ fstp(0);
|
| // FPU Stack: input % 2*pi
|
| + __ movq(rax, rdi); // Restore rax, pointer to the new HeapNumber.
|
| __ bind(&in_range);
|
| switch (type_) {
|
| case TranscendentalCache::SIN:
|
| @@ -2006,8 +2056,8 @@
|
| __ AbortIfSmi(rax);
|
| }
|
|
|
| - __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
|
| - __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
|
| + __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
|
| + Heap::kHeapNumberMapRootIndex);
|
| __ j(not_equal, &slow);
|
| // Operand is a float, negate its value by flipping sign bit.
|
| __ movq(rdx, FieldOperand(rax, HeapNumber::kValueOffset));
|
| @@ -2036,8 +2086,8 @@
|
| }
|
|
|
| // Check if the operand is a heap number.
|
| - __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
|
| - __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
|
| + __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
|
| + Heap::kHeapNumberMapRootIndex);
|
| __ j(not_equal, &slow);
|
|
|
| // Convert the heap number in rax to an untagged integer in rcx.
|
| @@ -2070,6 +2120,157 @@
|
| }
|
|
|
|
|
| +void MathPowStub::Generate(MacroAssembler* masm) {
|
| + // Registers are used as follows:
|
| + // rdx = base
|
| + // rax = exponent
|
| + // rcx = temporary, result
|
| +
|
| + Label allocate_return, call_runtime;
|
| +
|
| + // Load input parameters.
|
| + __ movq(rdx, Operand(rsp, 2 * kPointerSize));
|
| + __ movq(rax, Operand(rsp, 1 * kPointerSize));
|
| +
|
| + // Save 1 in xmm3 - we need this several times later on.
|
| + __ movl(rcx, Immediate(1));
|
| + __ cvtlsi2sd(xmm3, rcx);
|
| +
|
| + Label exponent_nonsmi;
|
| + Label base_nonsmi;
|
| + // If the exponent is a heap number go to that specific case.
|
| + __ JumpIfNotSmi(rax, &exponent_nonsmi);
|
| + __ JumpIfNotSmi(rdx, &base_nonsmi);
|
| +
|
| + // Optimized version when both exponent and base are smis.
|
| + Label powi;
|
| + __ SmiToInteger32(rdx, rdx);
|
| + __ cvtlsi2sd(xmm0, rdx);
|
| + __ jmp(&powi);
|
| + // Exponent is a smi and base is a heapnumber.
|
| + __ bind(&base_nonsmi);
|
| + __ CompareRoot(FieldOperand(rdx, HeapObject::kMapOffset),
|
| + Heap::kHeapNumberMapRootIndex);
|
| + __ j(not_equal, &call_runtime);
|
| +
|
| + __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
|
| +
|
| + // Optimized version of pow if exponent is a smi.
|
| + // xmm0 contains the base.
|
| + __ bind(&powi);
|
| + __ SmiToInteger32(rax, rax);
|
| +
|
| + // Save exponent in base as we need to check if exponent is negative later.
|
| + // We know that base and exponent are in different registers.
|
| + __ movq(rdx, rax);
|
| +
|
| + // Get absolute value of exponent.
|
| + NearLabel no_neg;
|
| + __ cmpl(rax, Immediate(0));
|
| + __ j(greater_equal, &no_neg);
|
| + __ negl(rax);
|
| + __ bind(&no_neg);
|
| +
|
| + // Load xmm1 with 1.
|
| + __ movsd(xmm1, xmm3);
|
| + NearLabel while_true;
|
| + NearLabel no_multiply;
|
| +
|
| + __ bind(&while_true);
|
| + __ shrl(rax, Immediate(1));
|
| + __ j(not_carry, &no_multiply);
|
| + __ mulsd(xmm1, xmm0);
|
| + __ bind(&no_multiply);
|
| + __ mulsd(xmm0, xmm0);
|
| + __ j(not_zero, &while_true);
|
| +
|
| + // Base has the original value of the exponent - if the exponent is
|
| + // negative return 1/result.
|
| + __ testl(rdx, rdx);
|
| + __ j(positive, &allocate_return);
|
| + // Special case if xmm1 has reached infinity.
|
| + __ divsd(xmm3, xmm1);
|
| + __ movsd(xmm1, xmm3);
|
| + __ xorpd(xmm0, xmm0);
|
| + __ ucomisd(xmm0, xmm1);
|
| + __ j(equal, &call_runtime);
|
| +
|
| + __ jmp(&allocate_return);
|
| +
|
| + // Exponent (or both) is a heapnumber - no matter what we should now work
|
| + // on doubles.
|
| + __ bind(&exponent_nonsmi);
|
| + __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
|
| + Heap::kHeapNumberMapRootIndex);
|
| + __ j(not_equal, &call_runtime);
|
| + __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset));
|
| + // Test if exponent is nan.
|
| + __ ucomisd(xmm1, xmm1);
|
| + __ j(parity_even, &call_runtime);
|
| +
|
| + NearLabel base_not_smi;
|
| + NearLabel handle_special_cases;
|
| + __ JumpIfNotSmi(rdx, &base_not_smi);
|
| + __ SmiToInteger32(rdx, rdx);
|
| + __ cvtlsi2sd(xmm0, rdx);
|
| + __ jmp(&handle_special_cases);
|
| +
|
| + __ bind(&base_not_smi);
|
| + __ CompareRoot(FieldOperand(rdx, HeapObject::kMapOffset),
|
| + Heap::kHeapNumberMapRootIndex);
|
| + __ j(not_equal, &call_runtime);
|
| + __ movl(rcx, FieldOperand(rdx, HeapNumber::kExponentOffset));
|
| + __ andl(rcx, Immediate(HeapNumber::kExponentMask));
|
| + __ cmpl(rcx, Immediate(HeapNumber::kExponentMask));
|
| + // base is NaN or +/-Infinity
|
| + __ j(greater_equal, &call_runtime);
|
| + __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
|
| +
|
| + // base is in xmm0 and exponent is in xmm1.
|
| + __ bind(&handle_special_cases);
|
| + NearLabel not_minus_half;
|
| + // Test for -0.5.
|
| + // Load xmm2 with -0.5.
|
| + __ movq(rcx, V8_UINT64_C(0xBFE0000000000000), RelocInfo::NONE);
|
| + __ movq(xmm2, rcx);
|
| + // xmm2 now has -0.5.
|
| + __ ucomisd(xmm2, xmm1);
|
| + __ j(not_equal, ¬_minus_half);
|
| +
|
| + // Calculates reciprocal of square root.
|
| + // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
|
| + __ xorpd(xmm1, xmm1);
|
| + __ addsd(xmm1, xmm0);
|
| + __ sqrtsd(xmm1, xmm1);
|
| + __ divsd(xmm3, xmm1);
|
| + __ movsd(xmm1, xmm3);
|
| + __ jmp(&allocate_return);
|
| +
|
| + // Test for 0.5.
|
| + __ bind(¬_minus_half);
|
| + // Load xmm2 with 0.5.
|
| + // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3.
|
| + __ addsd(xmm2, xmm3);
|
| + // xmm2 now has 0.5.
|
| + __ ucomisd(xmm2, xmm1);
|
| + __ j(not_equal, &call_runtime);
|
| + // Calculates square root.
|
| + // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
|
| + __ xorpd(xmm1, xmm1);
|
| + __ addsd(xmm1, xmm0);
|
| + __ sqrtsd(xmm1, xmm1);
|
| +
|
| + __ bind(&allocate_return);
|
| + __ AllocateHeapNumber(rcx, rax, &call_runtime);
|
| + __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm1);
|
| + __ movq(rax, rcx);
|
| + __ ret(2 * kPointerSize);
|
| +
|
| + __ bind(&call_runtime);
|
| + __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
|
| +}
|
| +
|
| +
|
| void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
|
| // The key is in rdx and the parameter count is in rax.
|
|
|
| @@ -2082,11 +2283,14 @@
|
| Label slow;
|
| __ JumpIfNotSmi(rdx, &slow);
|
|
|
| - // Check if the calling frame is an arguments adaptor frame.
|
| + // Check if the calling frame is an arguments adaptor frame. We look at the
|
| + // context offset, and if the frame is not a regular one, then we find a
|
| + // Smi instead of the context. We can't use SmiCompare here, because that
|
| + // only works for comparing two smis.
|
| Label adaptor;
|
| __ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
|
| - __ SmiCompare(Operand(rbx, StandardFrameConstants::kContextOffset),
|
| - Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
|
| + __ Cmp(Operand(rbx, StandardFrameConstants::kContextOffset),
|
| + Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
|
| __ j(equal, &adaptor);
|
|
|
| // Check index against formal parameters count limit passed in
|
| @@ -2141,8 +2345,8 @@
|
| // Check if the calling frame is an arguments adaptor frame.
|
| Label adaptor_frame, try_allocate, runtime;
|
| __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
|
| - __ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset),
|
| - Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
|
| + __ Cmp(Operand(rdx, StandardFrameConstants::kContextOffset),
|
| + Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
|
| __ j(equal, &adaptor_frame);
|
|
|
| // Get the length from the frame.
|
| @@ -2261,7 +2465,6 @@
|
| static const int kJSRegExpOffset = 4 * kPointerSize;
|
|
|
| Label runtime;
|
| -
|
| // Ensure that a RegExp stack is allocated.
|
| ExternalReference address_of_regexp_stack_memory_address =
|
| ExternalReference::address_of_regexp_stack_memory_address();
|
| @@ -2279,32 +2482,32 @@
|
| __ CmpObjectType(rax, JS_REGEXP_TYPE, kScratchRegister);
|
| __ j(not_equal, &runtime);
|
| // Check that the RegExp has been compiled (data contains a fixed array).
|
| - __ movq(rcx, FieldOperand(rax, JSRegExp::kDataOffset));
|
| + __ movq(rax, FieldOperand(rax, JSRegExp::kDataOffset));
|
| if (FLAG_debug_code) {
|
| - Condition is_smi = masm->CheckSmi(rcx);
|
| + Condition is_smi = masm->CheckSmi(rax);
|
| __ Check(NegateCondition(is_smi),
|
| "Unexpected type for RegExp data, FixedArray expected");
|
| - __ CmpObjectType(rcx, FIXED_ARRAY_TYPE, kScratchRegister);
|
| + __ CmpObjectType(rax, FIXED_ARRAY_TYPE, kScratchRegister);
|
| __ Check(equal, "Unexpected type for RegExp data, FixedArray expected");
|
| }
|
|
|
| - // rcx: RegExp data (FixedArray)
|
| + // rax: RegExp data (FixedArray)
|
| // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
|
| - __ SmiToInteger32(rbx, FieldOperand(rcx, JSRegExp::kDataTagOffset));
|
| + __ SmiToInteger32(rbx, FieldOperand(rax, JSRegExp::kDataTagOffset));
|
| __ cmpl(rbx, Immediate(JSRegExp::IRREGEXP));
|
| __ j(not_equal, &runtime);
|
|
|
| - // rcx: RegExp data (FixedArray)
|
| + // rax: RegExp data (FixedArray)
|
| // Check that the number of captures fit in the static offsets vector buffer.
|
| __ SmiToInteger32(rdx,
|
| - FieldOperand(rcx, JSRegExp::kIrregexpCaptureCountOffset));
|
| + FieldOperand(rax, JSRegExp::kIrregexpCaptureCountOffset));
|
| // Calculate number of capture registers (number_of_captures + 1) * 2.
|
| __ leal(rdx, Operand(rdx, rdx, times_1, 2));
|
| // Check that the static offsets vector buffer is large enough.
|
| __ cmpl(rdx, Immediate(OffsetsVector::kStaticOffsetsVectorSize));
|
| __ j(above, &runtime);
|
|
|
| - // rcx: RegExp data (FixedArray)
|
| + // rax: RegExp data (FixedArray)
|
| // rdx: Number of capture registers
|
| // Check that the second argument is a string.
|
| __ movq(rdi, Operand(rsp, kSubjectOffset));
|
| @@ -2332,7 +2535,8 @@
|
| // Check that the JSArray is in fast case.
|
| __ movq(rbx, FieldOperand(rdi, JSArray::kElementsOffset));
|
| __ movq(rdi, FieldOperand(rbx, HeapObject::kMapOffset));
|
| - __ Cmp(rdi, Factory::fixed_array_map());
|
| + __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
|
| + Heap::kFixedArrayMapRootIndex);
|
| __ j(not_equal, &runtime);
|
| // Check that the last match info has space for the capture registers and the
|
| // additional information. Ensure no overflow in add.
|
| @@ -2367,8 +2571,8 @@
|
| __ testb(rbx, Immediate(kIsNotStringMask | kExternalStringTag));
|
| __ j(not_zero, &runtime);
|
| // String is a cons string.
|
| - __ movq(rdx, FieldOperand(rdi, ConsString::kSecondOffset));
|
| - __ Cmp(rdx, Factory::empty_string());
|
| + __ CompareRoot(FieldOperand(rdi, ConsString::kSecondOffset),
|
| + Heap::kEmptyStringRootIndex);
|
| __ j(not_equal, &runtime);
|
| __ movq(rdi, FieldOperand(rdi, ConsString::kFirstOffset));
|
| __ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset));
|
| @@ -2422,7 +2626,7 @@
|
| static const int kRegExpExecuteArguments = 7;
|
| int argument_slots_on_stack =
|
| masm->ArgumentStackSlotsForCFunctionCall(kRegExpExecuteArguments);
|
| - __ EnterApiExitFrame(argument_slots_on_stack); // Clobbers rax!
|
| + __ EnterApiExitFrame(argument_slots_on_stack);
|
|
|
| // Argument 7: Indicate that this is a direct call from JavaScript.
|
| __ movq(Operand(rsp, (argument_slots_on_stack - 1) * kPointerSize),
|
| @@ -2483,7 +2687,7 @@
|
| __ movq(arg2, rbx);
|
|
|
| // Argument 1: Subject string.
|
| -#ifdef WIN64_
|
| +#ifdef _WIN64
|
| __ movq(arg1, rdi);
|
| #else
|
| // Already there in AMD64 calling convention.
|
| @@ -2636,8 +2840,8 @@
|
| __ movq(FieldOperand(rax, HeapObject::kMapOffset), rdx);
|
|
|
| // Set empty properties FixedArray.
|
| - __ Move(FieldOperand(rax, JSObject::kPropertiesOffset),
|
| - Factory::empty_fixed_array());
|
| + __ LoadRoot(kScratchRegister, Heap::kEmptyFixedArrayRootIndex);
|
| + __ movq(FieldOperand(rax, JSObject::kPropertiesOffset), kScratchRegister);
|
|
|
| // Set elements to point to FixedArray allocated right after the JSArray.
|
| __ lea(rcx, Operand(rax, JSRegExpResult::kSize));
|
| @@ -2657,13 +2861,13 @@
|
| // rbx: Number of elements in array as int32.
|
|
|
| // Set map.
|
| - __ Move(FieldOperand(rcx, HeapObject::kMapOffset),
|
| - Factory::fixed_array_map());
|
| + __ LoadRoot(kScratchRegister, Heap::kFixedArrayMapRootIndex);
|
| + __ movq(FieldOperand(rcx, HeapObject::kMapOffset), kScratchRegister);
|
| // Set length.
|
| __ Integer32ToSmi(rdx, rbx);
|
| __ movq(FieldOperand(rcx, FixedArray::kLengthOffset), rdx);
|
| // Fill contents of fixed-array with the-hole.
|
| - __ Move(rdx, Factory::the_hole_value());
|
| + __ LoadRoot(rdx, Heap::kTheHoleValueRootIndex);
|
| __ lea(rcx, FieldOperand(rcx, FixedArray::kHeaderSize));
|
| // Fill fixed array elements with hole.
|
| // rax: JSArray.
|
| @@ -3142,6 +3346,11 @@
|
| }
|
|
|
|
|
| +bool CEntryStub::NeedsImmovableCode() {
|
| + return false;
|
| +}
|
| +
|
| +
|
| void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
|
| // Throw exception in eax.
|
| __ Throw(rax);
|
| @@ -3159,7 +3368,7 @@
|
| // rbp: frame pointer (restored after C call).
|
| // rsp: stack pointer (restored after C call).
|
| // r14: number of arguments including receiver (C callee-saved).
|
| - // r12: pointer to the first argument (C callee-saved).
|
| + // r15: pointer to the first argument (C callee-saved).
|
| // This pointer is reused in LeaveExitFrame(), so it is stored in a
|
| // callee-saved register.
|
|
|
| @@ -3200,7 +3409,7 @@
|
| // Windows 64-bit ABI passes arguments in rcx, rdx, r8, r9
|
| // Store Arguments object on stack, below the 4 WIN64 ABI parameter slots.
|
| __ movq(StackSpaceOperand(0), r14); // argc.
|
| - __ movq(StackSpaceOperand(1), r12); // argv.
|
| + __ movq(StackSpaceOperand(1), r15); // argv.
|
| if (result_size_ < 2) {
|
| // Pass a pointer to the Arguments object as the first argument.
|
| // Return result in single register (rax).
|
| @@ -3216,7 +3425,7 @@
|
| #else // _WIN64
|
| // GCC passes arguments in rdi, rsi, rdx, rcx, r8, r9.
|
| __ movq(rdi, r14); // argc.
|
| - __ movq(rsi, r12); // argv.
|
| + __ movq(rsi, r15); // argv.
|
| #endif
|
| __ call(rbx);
|
| // Result is in rax - do not destroy this register!
|
| @@ -3320,7 +3529,7 @@
|
| // rbp: frame pointer of exit frame (restored after C call).
|
| // rsp: stack pointer (restored after C call).
|
| // r14: number of arguments including receiver (C callee-saved).
|
| - // r12: argv pointer (C callee-saved).
|
| + // r15: argv pointer (C callee-saved).
|
|
|
| Label throw_normal_exception;
|
| Label throw_termination_exception;
|
| @@ -3403,8 +3612,7 @@
|
|
|
| // Set up the roots and smi constant registers.
|
| // Needs to be done before any further smi loads.
|
| - ExternalReference roots_address = ExternalReference::roots_address();
|
| - __ movq(kRootRegister, roots_address);
|
| + __ InitializeRootRegister();
|
| __ InitializeSmiConstantRegister();
|
|
|
| #ifdef ENABLE_LOGGING_AND_PROFILING
|
| @@ -3496,17 +3704,39 @@
|
|
|
| void InstanceofStub::Generate(MacroAssembler* masm) {
|
| // Implements "value instanceof function" operator.
|
| - // Expected input state:
|
| + // Expected input state with no inline cache:
|
| // rsp[0] : return address
|
| // rsp[1] : function pointer
|
| // rsp[2] : value
|
| + // Expected input state with an inline one-element cache:
|
| + // rsp[0] : return address
|
| + // rsp[1] : offset from return address to location of inline cache
|
| + // rsp[2] : function pointer
|
| + // rsp[3] : value
|
| // Returns a bitwise zero to indicate that the value
|
| // is and instance of the function and anything else to
|
| // indicate that the value is not an instance.
|
|
|
| + static const int kOffsetToMapCheckValue = 5;
|
| + static const int kOffsetToResultValue = 21;
|
| + // The last 4 bytes of the instruction sequence
|
| + // movq(rax, FieldOperand(rdi, HeapObject::kMapOffset)
|
| + // Move(kScratchRegister, Factory::the_hole_value)
|
| + // in front of the hole value address.
|
| + static const unsigned int kWordBeforeMapCheckValue = 0xBA49FF78;
|
| + // The last 4 bytes of the instruction sequence
|
| + // __ j(not_equal, &cache_miss);
|
| + // __ LoadRoot(ToRegister(instr->result()), Heap::kTheHoleValueRootIndex);
|
| + // before the offset of the hole value in the root array.
|
| + static const unsigned int kWordBeforeResultValue = 0x458B4909;
|
| + // Only the inline check flag is supported on X64.
|
| + ASSERT(flags_ == kNoFlags || HasCallSiteInlineCheck());
|
| + int extra_stack_space = HasCallSiteInlineCheck() ? kPointerSize : 0;
|
| +
|
| // Get the object - go slow case if it's a smi.
|
| Label slow;
|
| - __ movq(rax, Operand(rsp, 2 * kPointerSize));
|
| +
|
| + __ movq(rax, Operand(rsp, 2 * kPointerSize + extra_stack_space));
|
| __ JumpIfSmi(rax, &slow);
|
|
|
| // Check that the left hand is a JS object. Leave its map in rax.
|
| @@ -3516,19 +3746,23 @@
|
| __ j(above, &slow);
|
|
|
| // Get the prototype of the function.
|
| - __ movq(rdx, Operand(rsp, 1 * kPointerSize));
|
| + __ movq(rdx, Operand(rsp, 1 * kPointerSize + extra_stack_space));
|
| // rdx is function, rax is map.
|
|
|
| - // Look up the function and the map in the instanceof cache.
|
| - NearLabel miss;
|
| - __ CompareRoot(rdx, Heap::kInstanceofCacheFunctionRootIndex);
|
| - __ j(not_equal, &miss);
|
| - __ CompareRoot(rax, Heap::kInstanceofCacheMapRootIndex);
|
| - __ j(not_equal, &miss);
|
| - __ LoadRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
|
| - __ ret(2 * kPointerSize);
|
| + // If there is a call site cache don't look in the global cache, but do the
|
| + // real lookup and update the call site cache.
|
| + if (!HasCallSiteInlineCheck()) {
|
| + // Look up the function and the map in the instanceof cache.
|
| + NearLabel miss;
|
| + __ CompareRoot(rdx, Heap::kInstanceofCacheFunctionRootIndex);
|
| + __ j(not_equal, &miss);
|
| + __ CompareRoot(rax, Heap::kInstanceofCacheMapRootIndex);
|
| + __ j(not_equal, &miss);
|
| + __ LoadRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
|
| + __ ret(2 * kPointerSize);
|
| + __ bind(&miss);
|
| + }
|
|
|
| - __ bind(&miss);
|
| __ TryGetFunctionPrototype(rdx, rbx, &slow);
|
|
|
| // Check that the function prototype is a JS object.
|
| @@ -3542,8 +3776,19 @@
|
| // rax is object map.
|
| // rdx is function.
|
| // rbx is function prototype.
|
| - __ StoreRoot(rdx, Heap::kInstanceofCacheFunctionRootIndex);
|
| - __ StoreRoot(rax, Heap::kInstanceofCacheMapRootIndex);
|
| + if (!HasCallSiteInlineCheck()) {
|
| + __ StoreRoot(rdx, Heap::kInstanceofCacheFunctionRootIndex);
|
| + __ StoreRoot(rax, Heap::kInstanceofCacheMapRootIndex);
|
| + } else {
|
| + __ movq(kScratchRegister, Operand(rsp, 0 * kPointerSize));
|
| + __ subq(kScratchRegister, Operand(rsp, 1 * kPointerSize));
|
| + __ movq(Operand(kScratchRegister, kOffsetToMapCheckValue), rax);
|
| + if (FLAG_debug_code) {
|
| + __ movl(rdi, Immediate(kWordBeforeMapCheckValue));
|
| + __ cmpl(Operand(kScratchRegister, kOffsetToMapCheckValue - 4), rdi);
|
| + __ Assert(equal, "InstanceofStub unexpected call site cache.");
|
| + }
|
| + }
|
|
|
| __ movq(rcx, FieldOperand(rax, Map::kPrototypeOffset));
|
|
|
| @@ -3562,27 +3807,65 @@
|
| __ jmp(&loop);
|
|
|
| __ bind(&is_instance);
|
| - __ xorl(rax, rax);
|
| - // Store bitwise zero in the cache. This is a Smi in GC terms.
|
| - STATIC_ASSERT(kSmiTag == 0);
|
| - __ StoreRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
|
| - __ ret(2 * kPointerSize);
|
| + if (!HasCallSiteInlineCheck()) {
|
| + __ xorl(rax, rax);
|
| + // Store bitwise zero in the cache. This is a Smi in GC terms.
|
| + STATIC_ASSERT(kSmiTag == 0);
|
| + __ StoreRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
|
| + } else {
|
| + // Store offset of true in the root array at the inline check site.
|
| + ASSERT((Heap::kTrueValueRootIndex << kPointerSizeLog2) - kRootRegisterBias
|
| + == 0xB0 - 0x100);
|
| + __ movl(rax, Immediate(0xB0)); // TrueValue is at -10 * kPointerSize.
|
| + __ movq(kScratchRegister, Operand(rsp, 0 * kPointerSize));
|
| + __ subq(kScratchRegister, Operand(rsp, 1 * kPointerSize));
|
| + __ movb(Operand(kScratchRegister, kOffsetToResultValue), rax);
|
| + if (FLAG_debug_code) {
|
| + __ movl(rax, Immediate(kWordBeforeResultValue));
|
| + __ cmpl(Operand(kScratchRegister, kOffsetToResultValue - 4), rax);
|
| + __ Assert(equal, "InstanceofStub unexpected call site cache.");
|
| + }
|
| + __ xorl(rax, rax);
|
| + }
|
| + __ ret(2 * kPointerSize + extra_stack_space);
|
|
|
| __ bind(&is_not_instance);
|
| - // We have to store a non-zero value in the cache.
|
| - __ StoreRoot(kScratchRegister, Heap::kInstanceofCacheAnswerRootIndex);
|
| - __ ret(2 * kPointerSize);
|
| + if (!HasCallSiteInlineCheck()) {
|
| + // We have to store a non-zero value in the cache.
|
| + __ StoreRoot(kScratchRegister, Heap::kInstanceofCacheAnswerRootIndex);
|
| + } else {
|
| + // Store offset of false in the root array at the inline check site.
|
| + ASSERT((Heap::kFalseValueRootIndex << kPointerSizeLog2) - kRootRegisterBias
|
| + == 0xB8 - 0x100);
|
| + __ movl(rax, Immediate(0xB8)); // FalseValue is at -9 * kPointerSize.
|
| + __ movq(kScratchRegister, Operand(rsp, 0 * kPointerSize));
|
| + __ subq(kScratchRegister, Operand(rsp, 1 * kPointerSize));
|
| + __ movb(Operand(kScratchRegister, kOffsetToResultValue), rax);
|
| + if (FLAG_debug_code) {
|
| + __ movl(rax, Immediate(kWordBeforeResultValue));
|
| + __ cmpl(Operand(kScratchRegister, kOffsetToResultValue - 4), rax);
|
| + __ Assert(equal, "InstanceofStub unexpected call site cache (mov)");
|
| + }
|
| + }
|
| + __ ret(2 * kPointerSize + extra_stack_space);
|
|
|
| // Slow-case: Go through the JavaScript implementation.
|
| __ bind(&slow);
|
| + if (HasCallSiteInlineCheck()) {
|
| + // Remove extra value from the stack.
|
| + __ pop(rcx);
|
| + __ pop(rax);
|
| + __ push(rcx);
|
| + }
|
| __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
|
| }
|
|
|
|
|
| -Register InstanceofStub::left() { return rax; }
|
| +// Passing arguments in registers is not supported.
|
| +Register InstanceofStub::left() { return no_reg; }
|
|
|
|
|
| -Register InstanceofStub::right() { return rdx; }
|
| +Register InstanceofStub::right() { return no_reg; }
|
|
|
|
|
| int CompareStub::MinorKey() {
|
| @@ -3841,14 +4124,15 @@
|
|
|
|
|
| void StringAddStub::Generate(MacroAssembler* masm) {
|
| - Label string_add_runtime;
|
| + Label string_add_runtime, call_builtin;
|
| + Builtins::JavaScript builtin_id = Builtins::ADD;
|
|
|
| // Load the two arguments.
|
| - __ movq(rax, Operand(rsp, 2 * kPointerSize)); // First argument.
|
| - __ movq(rdx, Operand(rsp, 1 * kPointerSize)); // Second argument.
|
| + __ movq(rax, Operand(rsp, 2 * kPointerSize)); // First argument (left).
|
| + __ movq(rdx, Operand(rsp, 1 * kPointerSize)); // Second argument (right).
|
|
|
| // Make sure that both arguments are strings if not known in advance.
|
| - if (string_check_) {
|
| + if (flags_ == NO_STRING_ADD_FLAGS) {
|
| Condition is_smi;
|
| is_smi = masm->CheckSmi(rax);
|
| __ j(is_smi, &string_add_runtime);
|
| @@ -3860,6 +4144,20 @@
|
| __ j(is_smi, &string_add_runtime);
|
| __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9);
|
| __ j(above_equal, &string_add_runtime);
|
| + } else {
|
| + // Here at least one of the arguments is definitely a string.
|
| + // We convert the one that is not known to be a string.
|
| + if ((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) == 0) {
|
| + ASSERT((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) != 0);
|
| + GenerateConvertArgument(masm, 2 * kPointerSize, rax, rbx, rcx, rdi,
|
| + &call_builtin);
|
| + builtin_id = Builtins::STRING_ADD_RIGHT;
|
| + } else if ((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) == 0) {
|
| + ASSERT((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) != 0);
|
| + GenerateConvertArgument(masm, 1 * kPointerSize, rdx, rbx, rcx, rdi,
|
| + &call_builtin);
|
| + builtin_id = Builtins::STRING_ADD_LEFT;
|
| + }
|
| }
|
|
|
| // Both arguments are strings.
|
| @@ -3887,14 +4185,14 @@
|
| // rbx: length of first string
|
| // rcx: length of second string
|
| // rdx: second string
|
| - // r8: map of first string if string check was performed above
|
| - // r9: map of second string if string check was performed above
|
| + // r8: map of first string (if flags_ == NO_STRING_ADD_FLAGS)
|
| + // r9: map of second string (if flags_ == NO_STRING_ADD_FLAGS)
|
| Label string_add_flat_result, longer_than_two;
|
| __ bind(&both_not_zero_length);
|
|
|
| // If arguments where known to be strings, maps are not loaded to r8 and r9
|
| // by the code above.
|
| - if (!string_check_) {
|
| + if (flags_ != NO_STRING_ADD_FLAGS) {
|
| __ movq(r8, FieldOperand(rax, HeapObject::kMapOffset));
|
| __ movq(r9, FieldOperand(rdx, HeapObject::kMapOffset));
|
| }
|
| @@ -3905,8 +4203,8 @@
|
| // Look at the length of the result of adding the two strings.
|
| STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue / 2);
|
| __ SmiAdd(rbx, rbx, rcx);
|
| - // Use the runtime system when adding two one character strings, as it
|
| - // contains optimizations for this specific case using the symbol table.
|
| + // Use the symbol table when adding two one character strings, as it
|
| + // helps later optimizations to return a symbol here.
|
| __ SmiCompare(rbx, Smi::FromInt(2));
|
| __ j(not_equal, &longer_than_two);
|
|
|
| @@ -3922,7 +4220,7 @@
|
| // just allocate a new one.
|
| Label make_two_character_string, make_flat_ascii_string;
|
| StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
| - masm, rbx, rcx, r14, r11, rdi, r12, &make_two_character_string);
|
| + masm, rbx, rcx, r14, r11, rdi, r15, &make_two_character_string);
|
| __ IncrementCounter(&Counters::string_add_native, 1);
|
| __ ret(2 * kPointerSize);
|
|
|
| @@ -4080,9 +4378,57 @@
|
| // Just jump to runtime to add the two strings.
|
| __ bind(&string_add_runtime);
|
| __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
|
| +
|
| + if (call_builtin.is_linked()) {
|
| + __ bind(&call_builtin);
|
| + __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
|
| + }
|
| }
|
|
|
|
|
| +void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
|
| + int stack_offset,
|
| + Register arg,
|
| + Register scratch1,
|
| + Register scratch2,
|
| + Register scratch3,
|
| + Label* slow) {
|
| + // First check if the argument is already a string.
|
| + Label not_string, done;
|
| + __ JumpIfSmi(arg, ¬_string);
|
| + __ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1);
|
| + __ j(below, &done);
|
| +
|
| + // Check the number to string cache.
|
| + Label not_cached;
|
| + __ bind(¬_string);
|
| + // Puts the cached result into scratch1.
|
| + NumberToStringStub::GenerateLookupNumberStringCache(masm,
|
| + arg,
|
| + scratch1,
|
| + scratch2,
|
| + scratch3,
|
| + false,
|
| + ¬_cached);
|
| + __ movq(arg, scratch1);
|
| + __ movq(Operand(rsp, stack_offset), arg);
|
| + __ jmp(&done);
|
| +
|
| + // Check if the argument is a safe string wrapper.
|
| + __ bind(¬_cached);
|
| + __ JumpIfSmi(arg, slow);
|
| + __ CmpObjectType(arg, JS_VALUE_TYPE, scratch1); // map -> scratch1.
|
| + __ j(not_equal, slow);
|
| + __ testb(FieldOperand(scratch1, Map::kBitField2Offset),
|
| + Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
|
| + __ j(zero, slow);
|
| + __ movq(arg, FieldOperand(arg, JSValue::kValueOffset));
|
| + __ movq(Operand(rsp, stack_offset), arg);
|
| +
|
| + __ bind(&done);
|
| +}
|
| +
|
| +
|
| void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
|
| Register dest,
|
| Register src,
|
| @@ -4210,15 +4556,14 @@
|
| FieldOperand(symbol_table, SymbolTable::kCapacityOffset));
|
| __ decl(mask);
|
|
|
| - Register undefined = scratch4;
|
| - __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
|
| + Register map = scratch4;
|
|
|
| // Registers
|
| // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
|
| // hash: hash of two character string (32-bit int)
|
| // symbol_table: symbol table
|
| // mask: capacity mask (32-bit int)
|
| - // undefined: undefined value
|
| + // map: -
|
| // scratch: -
|
|
|
| // Perform a number of probes in the symbol table.
|
| @@ -4233,7 +4578,7 @@
|
| }
|
| __ andl(scratch, mask);
|
|
|
| - // Load the entry from the symble table.
|
| + // Load the entry from the symbol table.
|
| Register candidate = scratch; // Scratch register contains candidate.
|
| STATIC_ASSERT(SymbolTable::kEntrySize == 1);
|
| __ movq(candidate,
|
| @@ -4243,9 +4588,17 @@
|
| SymbolTable::kElementsStartOffset));
|
|
|
| // If entry is undefined no string with this hash can be found.
|
| - __ cmpq(candidate, undefined);
|
| + NearLabel is_string;
|
| + __ CmpObjectType(candidate, ODDBALL_TYPE, map);
|
| + __ j(not_equal, &is_string);
|
| +
|
| + __ CompareRoot(candidate, Heap::kUndefinedValueRootIndex);
|
| __ j(equal, not_found);
|
| + // Must be null (deleted entry).
|
| + __ jmp(&next_probe[i]);
|
|
|
| + __ bind(&is_string);
|
| +
|
| // If length is not 2 the string is not a candidate.
|
| __ SmiCompare(FieldOperand(candidate, String::kLengthOffset),
|
| Smi::FromInt(2));
|
| @@ -4256,8 +4609,7 @@
|
| Register temp = kScratchRegister;
|
|
|
| // Check that the candidate is a non-external ascii string.
|
| - __ movq(temp, FieldOperand(candidate, HeapObject::kMapOffset));
|
| - __ movzxbl(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
|
| + __ movzxbl(temp, FieldOperand(map, Map::kInstanceTypeOffset));
|
| __ JumpIfInstanceTypeIsNotSequentialAscii(
|
| temp, temp, &next_probe[i]);
|
|
|
| @@ -4608,6 +4960,7 @@
|
| __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
|
| }
|
|
|
| +
|
| void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
|
| ASSERT(state_ == CompareIC::SMIS);
|
| NearLabel miss;
|
| @@ -4725,144 +5078,6 @@
|
| }
|
|
|
|
|
| -void GenerateFastPixelArrayLoad(MacroAssembler* masm,
|
| - Register receiver,
|
| - Register key,
|
| - Register elements,
|
| - Register untagged_key,
|
| - Register result,
|
| - Label* not_pixel_array,
|
| - Label* key_not_smi,
|
| - Label* out_of_range) {
|
| - // Register use:
|
| - // receiver - holds the receiver and is unchanged.
|
| - // key - holds the key and is unchanged (must be a smi).
|
| - // elements - is set to the the receiver's element if
|
| - // the receiver doesn't have a pixel array or the
|
| - // key is not a smi, otherwise it's the elements'
|
| - // external pointer.
|
| - // untagged_key - is set to the untagged key
|
| -
|
| - // Some callers already have verified that the key is a smi. key_not_smi is
|
| - // set to NULL as a sentinel for that case. Otherwise, add an explicit check
|
| - // to ensure the key is a smi must be added.
|
| - if (key_not_smi != NULL) {
|
| - __ JumpIfNotSmi(key, key_not_smi);
|
| - } else {
|
| - if (FLAG_debug_code) {
|
| - __ AbortIfNotSmi(key);
|
| - }
|
| - }
|
| - __ SmiToInteger32(untagged_key, key);
|
| -
|
| - __ movq(elements, FieldOperand(receiver, JSObject::kElementsOffset));
|
| - // By passing NULL as not_pixel_array, callers signal that they have already
|
| - // verified that the receiver has pixel array elements.
|
| - if (not_pixel_array != NULL) {
|
| - __ CheckMap(elements, Factory::pixel_array_map(), not_pixel_array, true);
|
| - } else {
|
| - if (FLAG_debug_code) {
|
| - // Map check should have already made sure that elements is a pixel array.
|
| - __ Cmp(FieldOperand(elements, HeapObject::kMapOffset),
|
| - Factory::pixel_array_map());
|
| - __ Assert(equal, "Elements isn't a pixel array");
|
| - }
|
| - }
|
| -
|
| - // Check that the smi is in range.
|
| - __ cmpl(untagged_key, FieldOperand(elements, PixelArray::kLengthOffset));
|
| - __ j(above_equal, out_of_range); // unsigned check handles negative keys.
|
| -
|
| - // Load and tag the element as a smi.
|
| - __ movq(elements, FieldOperand(elements, PixelArray::kExternalPointerOffset));
|
| - __ movzxbq(result, Operand(elements, untagged_key, times_1, 0));
|
| - __ Integer32ToSmi(result, result);
|
| - __ ret(0);
|
| -}
|
| -
|
| -
|
| -// Stores an indexed element into a pixel array, clamping the stored value.
|
| -void GenerateFastPixelArrayStore(MacroAssembler* masm,
|
| - Register receiver,
|
| - Register key,
|
| - Register value,
|
| - Register elements,
|
| - Register scratch1,
|
| - bool load_elements_from_receiver,
|
| - bool key_is_untagged,
|
| - Label* key_not_smi,
|
| - Label* value_not_smi,
|
| - Label* not_pixel_array,
|
| - Label* out_of_range) {
|
| - // Register use:
|
| - // receiver - holds the receiver and is unchanged.
|
| - // key - holds the key (must be a smi) and is unchanged.
|
| - // value - holds the value (must be a smi) and is unchanged.
|
| - // elements - holds the element object of the receiver on entry if
|
| - // load_elements_from_receiver is false, otherwise used
|
| - // internally to store the pixel arrays elements and
|
| - // external array pointer.
|
| - //
|
| - Register external_pointer = elements;
|
| - Register untagged_key = scratch1;
|
| - Register untagged_value = receiver; // Only set once success guaranteed.
|
| -
|
| - // Fetch the receiver's elements if the caller hasn't already done so.
|
| - if (load_elements_from_receiver) {
|
| - __ movq(elements, FieldOperand(receiver, JSObject::kElementsOffset));
|
| - }
|
| -
|
| - // By passing NULL as not_pixel_array, callers signal that they have already
|
| - // verified that the receiver has pixel array elements.
|
| - if (not_pixel_array != NULL) {
|
| - __ CheckMap(elements, Factory::pixel_array_map(), not_pixel_array, true);
|
| - } else {
|
| - if (FLAG_debug_code) {
|
| - // Map check should have already made sure that elements is a pixel array.
|
| - __ Cmp(FieldOperand(elements, HeapObject::kMapOffset),
|
| - Factory::pixel_array_map());
|
| - __ Assert(equal, "Elements isn't a pixel array");
|
| - }
|
| - }
|
| -
|
| - // Key must be a smi and it must be in range.
|
| - if (key_is_untagged) {
|
| - untagged_key = key;
|
| - } else {
|
| - // Some callers already have verified that the key is a smi. key_not_smi is
|
| - // set to NULL as a sentinel for that case. Otherwise, add an explicit
|
| - // check to ensure the key is a smi.
|
| - if (key_not_smi != NULL) {
|
| - __ JumpIfNotSmi(key, key_not_smi);
|
| - } else {
|
| - if (FLAG_debug_code) {
|
| - __ AbortIfNotSmi(key);
|
| - }
|
| - }
|
| - __ SmiToInteger32(untagged_key, key);
|
| - }
|
| - __ cmpl(untagged_key, FieldOperand(elements, PixelArray::kLengthOffset));
|
| - __ j(above_equal, out_of_range); // unsigned check handles negative keys.
|
| -
|
| - // Value must be a smi.
|
| - __ JumpIfNotSmi(value, value_not_smi);
|
| - __ SmiToInteger32(untagged_value, value);
|
| -
|
| - { // Clamp the value to [0..255].
|
| - NearLabel done;
|
| - __ testl(untagged_value, Immediate(0xFFFFFF00));
|
| - __ j(zero, &done);
|
| - __ setcc(negative, untagged_value); // 1 if negative, 0 if positive.
|
| - __ decb(untagged_value); // 0 if negative, 255 if positive.
|
| - __ bind(&done);
|
| - }
|
| -
|
| - __ movq(external_pointer,
|
| - FieldOperand(elements, PixelArray::kExternalPointerOffset));
|
| - __ movb(Operand(external_pointer, untagged_key, times_1, 0), untagged_value);
|
| - __ ret(0); // Return value in eax.
|
| -}
|
| -
|
| #undef __
|
|
|
| } } // namespace v8::internal
|
|
|