| Index: src/x64/code-stubs-x64.cc
|
| diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
|
| index 96f70bfa913e37148bb55f3f7aad46a59eb60b30..99d90aa7070e8b3229c996bc08339da31da4ffbe 100644
|
| --- a/src/x64/code-stubs-x64.cc
|
| +++ b/src/x64/code-stubs-x64.cc
|
| @@ -1991,152 +1991,274 @@ void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm,
|
|
|
|
|
| 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.
|
| - __ Set(rcx, 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);
|
| + // Choose register conforming to calling convention (when bailing out).
|
| +#ifdef _WIN64
|
| + const Register exponent = rdx;
|
| +#else
|
| + const Register exponent = rdi;
|
| +#endif
|
| + const Register base = rax;
|
| + const Register scratch = rcx;
|
| + const XMMRegister double_result = xmm3;
|
| + const XMMRegister double_base = xmm2;
|
| + const XMMRegister double_exponent = xmm1;
|
| + const XMMRegister double_scratch = xmm4;
|
| +
|
| + Label double_int_runtime, generic_runtime, done;
|
| + Label exponent_not_smi, int_exponent;
|
| +
|
| + // Save 1 in double_result - we need this several times later on.
|
| + __ mov(scratch, Immediate(1));
|
| + __ cvtlsi2sd(double_result, scratch);
|
| +
|
| + if (exponent_type_ == ON_STACK) {
|
| + Label base_is_smi, unpack_exponent;
|
| + // The exponent and base are supplied as arguments on the stack.
|
| + // This can only happen if the stub is called from non-optimized code.
|
| + // Load input parameters from stack.
|
| + __ movq(base, Operand(rsp, 2 * kPointerSize));
|
| + __ movq(exponent, Operand(rsp, 1 * kPointerSize));
|
| + __ JumpIfSmi(base, &base_is_smi, Label::kNear);
|
| + __ CompareRoot(FieldOperand(base, HeapObject::kMapOffset),
|
| + Heap::kHeapNumberMapRootIndex);
|
| + __ j(not_equal, &generic_runtime);
|
| +
|
| + __ movsd(double_base, FieldOperand(base, HeapNumber::kValueOffset));
|
| + __ jmp(&unpack_exponent, Label::kNear);
|
| +
|
| + __ bind(&base_is_smi);
|
| + __ SmiToInteger32(base, base);
|
| + __ cvtlsi2sd(double_base, base);
|
| + __ bind(&unpack_exponent);
|
| +
|
| + __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear);
|
| + __ SmiToInteger32(exponent, exponent);
|
| + __ jmp(&int_exponent);
|
| +
|
| + __ bind(&exponent_not_smi);
|
| + __ CompareRoot(FieldOperand(exponent, HeapObject::kMapOffset),
|
| + Heap::kHeapNumberMapRootIndex);
|
| + __ j(not_equal, &generic_runtime);
|
| + __ movsd(double_exponent, FieldOperand(exponent, HeapNumber::kValueOffset));
|
| + } else if (exponent_type_ == TAGGED) {
|
| + __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear);
|
| + __ SmiToInteger32(exponent, exponent);
|
| + __ jmp(&int_exponent);
|
| +
|
| + __ bind(&exponent_not_smi);
|
| + __ movsd(double_exponent, FieldOperand(exponent, HeapNumber::kValueOffset));
|
| + }
|
|
|
| - // 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);
|
| + if (exponent_type_ != INTEGER) {
|
| + Label fast_power;
|
| + // Detect integer exponents stored as double.
|
| + __ cvttsd2si(exponent, double_exponent);
|
| + // Skip to runtime if possibly NaN (indicated by the indefinite integer).
|
| + __ cmpl(exponent, Immediate(0x80000000u));
|
| + __ j(equal, &generic_runtime);
|
| + __ cvtlsi2sd(double_scratch, exponent);
|
| + // Already ruled out NaNs for exponent.
|
| + __ ucomisd(double_exponent, double_scratch);
|
| + __ j(equal, &int_exponent);
|
| +
|
| + if (exponent_type_ == ON_STACK) {
|
| + // Detect square root case. Crankshaft detects constant +/-0.5 at
|
| + // compile time and uses DoMathPowHalf instead. We then skip this check
|
| + // for non-constant cases of +/-0.5 as these hardly occur.
|
| + Label continue_sqrt, continue_rsqrt, not_plus_half;
|
| + // Test for 0.5.
|
| + // Load double_scratch with 0.5.
|
| + __ movq(scratch, V8_UINT64_C(0x3FE0000000000000), RelocInfo::NONE);
|
| + __ movq(double_scratch, scratch);
|
| + // Already ruled out NaNs for exponent.
|
| + __ ucomisd(double_scratch, double_exponent);
|
| + __ j(not_equal, ¬_plus_half, Label::kNear);
|
| +
|
| + // Calculates square root of base. Check for the special case of
|
| + // Math.pow(-Infinity, 0.5) == Infinity (ECMA spec, 15.8.2.13).
|
| + // According to IEEE-754, double-precision -Infinity has the highest
|
| + // 12 bits set and the lowest 52 bits cleared.
|
| + __ movq(scratch, V8_UINT64_C(0xFFF0000000000000), RelocInfo::NONE);
|
| + __ movq(double_scratch, scratch);
|
| + __ ucomisd(double_scratch, double_base);
|
| + // Comparing -Infinity with NaN results in "unordered", which sets the
|
| + // zero flag as if both were equal. However, it also sets the carry flag.
|
| + __ j(not_equal, &continue_sqrt, Label::kNear);
|
| + __ j(carry, &continue_sqrt, Label::kNear);
|
| +
|
| + // Set result to Infinity in the special case.
|
| + __ xorps(double_result, double_result);
|
| + __ subsd(double_result, double_scratch);
|
| + __ jmp(&done);
|
| +
|
| + __ bind(&continue_sqrt);
|
| + // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
|
| + __ xorps(double_scratch, double_scratch);
|
| + __ addsd(double_scratch, double_base); // Convert -0 to 0.
|
| + __ sqrtsd(double_result, double_scratch);
|
| + __ jmp(&done);
|
| +
|
| + // Test for -0.5.
|
| + __ bind(¬_plus_half);
|
| + // Load double_scratch with -0.5 by substracting 1.
|
| + __ subsd(double_scratch, double_result);
|
| + // Already ruled out NaNs for exponent.
|
| + __ ucomisd(double_scratch, double_exponent);
|
| + __ j(not_equal, &fast_power, Label::kNear);
|
| +
|
| + // Calculates reciprocal of square root of base. Check for the special
|
| + // case of Math.pow(-Infinity, -0.5) == 0 (ECMA spec, 15.8.2.13).
|
| + // According to IEEE-754, double-precision -Infinity has the highest
|
| + // 12 bits set and the lowest 52 bits cleared.
|
| + __ movq(scratch, V8_UINT64_C(0xFFF0000000000000), RelocInfo::NONE);
|
| + __ movq(double_scratch, scratch);
|
| + __ ucomisd(double_scratch, double_base);
|
| + // Comparing -Infinity with NaN results in "unordered", which sets the
|
| + // zero flag as if both were equal. However, it also sets the carry flag.
|
| + __ j(not_equal, &continue_rsqrt, Label::kNear);
|
| + __ j(carry, &continue_rsqrt, Label::kNear);
|
| +
|
| + // Set result to 0 in the special case.
|
| + __ xorps(double_result, double_result);
|
| + __ jmp(&done);
|
| +
|
| + __ bind(&continue_rsqrt);
|
| + // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
|
| + __ xorps(double_exponent, double_exponent);
|
| + __ addsd(double_exponent, double_base); // Convert -0 to +0.
|
| + __ sqrtsd(double_exponent, double_exponent);
|
| + __ divsd(double_result, double_exponent);
|
| + __ jmp(&done);
|
| + }
|
|
|
| - __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
|
| + // Using FPU instructions to calculate power.
|
| + Label fast_power_failed;
|
| + __ bind(&fast_power);
|
| + __ fnclex(); // Clear flags to catch exceptions later.
|
| + // Transfer (B)ase and (E)xponent onto the FPU register stack.
|
| + __ subq(rsp, Immediate(kDoubleSize));
|
| + __ movsd(Operand(rsp, 0), double_exponent);
|
| + __ fld_d(Operand(rsp, 0)); // E
|
| + __ movsd(Operand(rsp, 0), double_base);
|
| + __ fld_d(Operand(rsp, 0)); // B, E
|
| +
|
| + // Exponent is in st(1) and base is in st(0)
|
| + // B ^ E = (2^(E * log2(B)) - 1) + 1 = (2^X - 1) + 1 for X = E * log2(B)
|
| + // FYL2X calculates st(1) * log2(st(0))
|
| + __ fyl2x(); // X
|
| + __ fld(0); // X, X
|
| + __ frndint(); // rnd(X), X
|
| + __ fsub(1); // rnd(X), X-rnd(X)
|
| + __ fxch(1); // X - rnd(X), rnd(X)
|
| + // F2XM1 calculates 2^st(0) - 1 for -1 < st(0) < 1
|
| + __ f2xm1(); // 2^(X-rnd(X)) - 1, rnd(X)
|
| + __ fld1(); // 1, 2^(X-rnd(X)) - 1, rnd(X)
|
| + __ faddp(1); // 1, 2^(X-rnd(X)), rnd(X)
|
| + // FSCALE calculates st(0) * 2^st(1)
|
| + __ fscale(); // 2^X, rnd(X)
|
| + __ fstp(1);
|
| + // Bail out to runtime in case of exceptions in the status word.
|
| + __ fnstsw_ax();
|
| + __ testb(rax, Immediate(0x5F)); // Check for all but precision exception.
|
| + __ j(not_zero, &fast_power_failed, Label::kNear);
|
| + __ fstp_d(Operand(rsp, 0));
|
| + __ movsd(double_result, Operand(rsp, 0));
|
| + __ addq(rsp, Immediate(kDoubleSize));
|
| + __ jmp(&done);
|
|
|
| - // Optimized version of pow if exponent is a smi.
|
| - // xmm0 contains the base.
|
| - __ bind(&powi);
|
| - __ SmiToInteger32(rax, rax);
|
| + __ bind(&fast_power_failed);
|
| + __ fninit();
|
| + __ addq(rsp, Immediate(kDoubleSize));
|
| + __ jmp(&generic_runtime);
|
| + }
|
|
|
| - // 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);
|
| + // Calculate power with integer exponent.
|
| + __ bind(&int_exponent);
|
| + const XMMRegister double_scratch2 = double_exponent;
|
| + // Back up exponent as we need to check if exponent is negative later.
|
| + __ movq(scratch, exponent); // Back up exponent.
|
| + __ movsd(double_scratch, double_base); // Back up base.
|
| + __ movsd(double_scratch2, double_result); // Load double_exponent with 1.
|
|
|
| // Get absolute value of exponent.
|
| - Label no_neg;
|
| - __ cmpl(rax, Immediate(0));
|
| - __ j(greater_equal, &no_neg, Label::kNear);
|
| - __ negl(rax);
|
| + Label no_neg, while_true, no_multiply;
|
| + __ cmpl(scratch, Immediate(0));
|
| + __ j(positive, &no_neg, Label::kNear);
|
| + __ negl(scratch);
|
| __ bind(&no_neg);
|
|
|
| - // Load xmm1 with 1.
|
| - __ movaps(xmm1, xmm3);
|
| - Label while_true;
|
| - Label no_multiply;
|
| -
|
| __ bind(&while_true);
|
| - __ shrl(rax, Immediate(1));
|
| + __ shrl(scratch, Immediate(1));
|
| __ j(not_carry, &no_multiply, Label::kNear);
|
| - __ mulsd(xmm1, xmm0);
|
| + __ mulsd(double_result, double_scratch);
|
| __ 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);
|
| - __ movaps(xmm1, xmm3);
|
| - __ xorps(xmm0, xmm0);
|
| - __ ucomisd(xmm0, xmm1);
|
| - __ j(equal, &call_runtime);
|
| + __ mulsd(double_scratch, double_scratch);
|
| + __ j(not_zero, &while_true);
|
|
|
| - __ jmp(&allocate_return);
|
| + // scratch has the original value of the exponent - if the exponent is
|
| + // negative, return 1/result.
|
| + __ testl(exponent, exponent);
|
| + __ j(greater, &done);
|
| + __ divsd(double_scratch2, double_result);
|
| + __ movsd(double_result, double_scratch2);
|
| + // Test whether result is zero. Bail out to check for subnormal result.
|
| + // Due to subnormals, x^-y == (1/x)^y does not hold in all cases.
|
| + __ xorps(double_scratch2, double_scratch2);
|
| + __ ucomisd(double_scratch2, double_result);
|
| + __ j(equal, &double_int_runtime);
|
| +
|
| + // Returning or bailing out.
|
| + if (exponent_type_ == ON_STACK) {
|
| + // The stub is called from non-optimized code, which expects the result
|
| + // as heap number in eax.
|
| + __ bind(&done);
|
| + __ AllocateHeapNumber(rax, rcx, &generic_runtime);
|
| + __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), double_result);
|
| + __ ret(2 * kPointerSize);
|
|
|
| - // 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);
|
| + // The arguments are still on the stack.
|
| + __ bind(&generic_runtime);
|
| + __ bind(&double_int_runtime);
|
| + __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
|
| + } else {
|
| + __ jmp(&done);
|
|
|
| - Label base_not_smi, handle_special_cases;
|
| - __ JumpIfNotSmi(rdx, &base_not_smi, Label::kNear);
|
| - __ SmiToInteger32(rdx, rdx);
|
| - __ cvtlsi2sd(xmm0, rdx);
|
| - __ jmp(&handle_special_cases, Label::kNear);
|
| + Label return_from_runtime;
|
| + StubRuntimeCallHelper callhelper;
|
| + __ bind(&generic_runtime);
|
| + // Move base to the correct argument register. Exponent is already in xmm1.
|
| + __ movsd(xmm0, double_base);
|
| + ASSERT(exponent.is(xmm1));
|
| + {
|
| + AllowExternalCallThatCantCauseGC scope(masm);
|
| + __ PrepareCallCFunction(2);
|
| + __ CallCFunction(
|
| + ExternalReference::power_double_double_function(masm->isolate()), 2);
|
| + }
|
| + __ jmp(&return_from_runtime, Label::kNear);
|
|
|
| - __ 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));
|
| + __ bind(&double_int_runtime);
|
| + // Move base to the correct argument register.
|
| + __ movsd(xmm0, double_base);
|
| + // Exponent is already in the correct argument register:
|
| + // edi (not rdi) on Linux and edx on Windows.
|
| + {
|
| + AllowExternalCallThatCantCauseGC scope(masm);
|
| + __ PrepareCallCFunction(2);
|
| + __ CallCFunction(
|
| + ExternalReference::power_double_int_function(masm->isolate()), 2);
|
| + }
|
|
|
| - // base is in xmm0 and exponent is in xmm1.
|
| - __ bind(&handle_special_cases);
|
| - Label 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, Label::kNear);
|
| -
|
| - // Calculates reciprocal of square root.
|
| - // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
|
| - __ xorps(xmm1, xmm1);
|
| - __ addsd(xmm1, xmm0);
|
| - __ sqrtsd(xmm1, xmm1);
|
| - __ divsd(xmm3, xmm1);
|
| - __ movaps(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.
|
| - __ xorps(xmm1, xmm1);
|
| - __ addsd(xmm1, xmm0); // Convert -0 to 0.
|
| - __ sqrtsd(xmm1, xmm1);
|
| -
|
| - __ bind(&allocate_return);
|
| - __ AllocateHeapNumber(rcx, rax, &call_runtime);
|
| - __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm1);
|
| - __ movq(rax, rcx);
|
| - __ ret(2 * kPointerSize);
|
| + __ bind(&return_from_runtime);
|
| + // Return value is in xmm0.
|
| + __ movsd(double_result, xmm0);
|
| + // Restore context register.
|
| + __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
|
|
|
| - __ bind(&call_runtime);
|
| - __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
|
| + __ bind(&done);
|
| + __ ret(0);
|
| + }
|
| }
|
|
|
|
|
|
|