|
void TranscendentalCacheStub::GenerateOperation( |
|
MacroAssembler* masm, TranscendentalCache::Type type) { |
|
// Only free register is edi. |
|
// Input value is on FP stack, and also in ebx/edx. |
|
// Input value is possibly in xmm1. |
|
// Address of result (a newly allocated HeapNumber) may be in eax. |
|
if (type == TranscendentalCache::SIN || |
|
type == TranscendentalCache::COS || |
|
type == TranscendentalCache::TAN) { |
|
// Both fsin and fcos require arguments in the range +/-2^63 and |
|
// return NaN for infinities and NaN. They can share all code except |
|
// the actual fsin/fcos operation. |
|
Label in_range, done; |
|
// If argument is outside the range -2^63..2^63, fsin/cos doesn't |
|
// work. We must reduce it to the appropriate range. |
|
__ mov(edi, edx); |
|
__ and_(edi, Immediate(0x7ff00000)); // Exponent only. |
|
int supported_exponent_limit = |
|
(63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift; |
|
__ cmp(edi, Immediate(supported_exponent_limit)); |
|
__ j(below, &in_range, Label::kNear); |
|
// Check for infinity and NaN. Both return NaN for sin. |
|
__ cmp(edi, Immediate(0x7ff00000)); |
|
Label non_nan_result; |
|
__ j(not_equal, &non_nan_result, Label::kNear); |
|
// Input is +/-Infinity or NaN. Result is NaN. |
|
__ fstp(0); |
|
// NaN is represented by 0x7ff8000000000000. |
|
__ push(Immediate(0x7ff80000)); |
|
__ push(Immediate(0)); |
|
__ fld_d(Operand(esp, 0)); |
|
__ add(esp, Immediate(2 * kPointerSize)); |
|
__ jmp(&done, Label::kNear); |
|
|
|
__ bind(&non_nan_result); |
|
|
|
// Use fpmod to restrict argument to the range +/-2*PI. |
|
__ mov(edi, eax); // Save eax before using fnstsw_ax. |
|
__ fldpi(); |
|
__ fadd(0); |
|
__ fld(1); |
|
// FPU Stack: input, 2*pi, input. |
|
{ |
|
Label no_exceptions; |
|
__ fwait(); |
|
__ fnstsw_ax(); |
|
// Clear if Illegal Operand or Zero Division exceptions are set. |
|
__ test(eax, Immediate(5)); |
|
__ j(zero, &no_exceptions, Label::kNear); |
|
__ fnclex(); |
|
__ bind(&no_exceptions); |
|
} |
|
|
|
// Compute st(0) % st(1) |
|
{ |
|
Label partial_remainder_loop; |
|
__ bind(&partial_remainder_loop); |
|
__ fprem1(); |
|
__ fwait(); |
|
__ fnstsw_ax(); |
|
__ test(eax, Immediate(0x400 /* C2 */)); |
|
// If C2 is set, computation only has partial result. Loop to |
|
// continue computation. |
|
__ j(not_zero, &partial_remainder_loop); |
|
} |
|
// FPU Stack: input, 2*pi, input % 2*pi |
|
__ fstp(2); |
|
__ fstp(0); |
|
__ mov(eax, edi); // Restore eax (allocated HeapNumber pointer). |
|
|
|
// FPU Stack: input % 2*pi |
|
__ bind(&in_range); |
|
switch (type) { |
|
case TranscendentalCache::SIN: |
|
__ fsin(); |
|
break; |
|
case TranscendentalCache::COS: |
|
__ fcos(); |
|
break; |
|
case TranscendentalCache::TAN: |
|
// FPTAN calculates tangent onto st(0) and pushes 1.0 onto the |
|
// FP register stack. |
|
__ fptan(); |
|
__ fstp(0); // Pop FP register stack. |
|
break; |
|
default: |
|
UNREACHABLE(); |
|
} |
|
__ bind(&done); |
|
} else { |
|
ASSERT(type == TranscendentalCache::LOG); |
|
__ fldln2(); |
|
__ fxch(); |
|
__ fyl2x(); |
|
} |
|
} |