Add generated code to calculate Math.log and to search Transcendental cache for logs. Implemented on all platforms.

Review URL: http://codereview.chromium.org/5437002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5912 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
whesse@chromium.org 2010-12-02 11:20:44 +00:00
parent be20c55b3e
commit 66836efbba
20 changed files with 257 additions and 140 deletions

View File

@ -2290,6 +2290,7 @@ Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
// Add more cases when necessary.
case TranscendentalCache::SIN: return Runtime::kMath_sin;
case TranscendentalCache::COS: return Runtime::kMath_cos;
case TranscendentalCache::LOG: return Runtime::kMath_log;
default:
UNIMPLEMENTED();
return Runtime::kAbort;

View File

@ -5750,6 +5750,20 @@ void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateMathLog(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1);
Load(args->at(0));
if (CpuFeatures::IsSupported(VFP3)) {
TranscendentalCacheStub stub(TranscendentalCache::LOG);
frame_->SpillAllButCopyTOSToR0();
frame_->CallStub(&stub, 1);
} else {
frame_->CallRuntime(Runtime::kMath_log, 1);
}
frame_->EmitPush(r0);
}
void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ASSERT(args->length() == 2);

View File

@ -516,6 +516,7 @@ class CodeGenerator: public AstVisitor {
void GenerateMathSin(ZoneList<Expression*>* args);
void GenerateMathCos(ZoneList<Expression*>* args);
void GenerateMathSqrt(ZoneList<Expression*>* args);
void GenerateMathLog(ZoneList<Expression*>* args);
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);

View File

@ -2617,6 +2617,15 @@ void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) {
// Load the argument on the stack and call the runtime function.
ASSERT(args->length() == 1);
VisitForStackValue(args->at(0));
__ CallRuntime(Runtime::kMath_log, 1);
context()->Plug(r0);
}
void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
ASSERT(args->length() >= 2);

View File

@ -1772,6 +1772,14 @@ void Assembler::fldz() {
}
void Assembler::fldln2() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xED);
}
void Assembler::fld_s(const Operand& adr) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@ -1902,6 +1910,14 @@ void Assembler::fsin() {
}
void Assembler::fyl2x() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xF1);
}
void Assembler::fadd(int i) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;

View File

@ -720,6 +720,7 @@ class Assembler : public Malloced {
void fld1();
void fldz();
void fldpi();
void fldln2();
void fld_s(const Operand& adr);
void fld_d(const Operand& adr);
@ -744,6 +745,7 @@ class Assembler : public Malloced {
void fchs();
void fcos();
void fsin();
void fyl2x();
void fadd(int i);
void fsub(int i);

View File

@ -1330,6 +1330,7 @@ Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
// Add more cases when necessary.
case TranscendentalCache::SIN: return Runtime::kMath_sin;
case TranscendentalCache::COS: return Runtime::kMath_cos;
case TranscendentalCache::LOG: return Runtime::kMath_log;
default:
UNIMPLEMENTED();
return Runtime::kAbort;
@ -1339,85 +1340,90 @@ Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
// Only free register is edi.
// Input value is on FP stack, and also in ebx/edx. Address of result
// (a newly allocated HeapNumber) is in eax.
NearLabel done;
ASSERT(type_ == TranscendentalCache::SIN ||
type_ == TranscendentalCache::COS);
// More transcendental types can be added later.
if (type_ == TranscendentalCache::SIN || type_ == TranscendentalCache::COS) {
// 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.
NearLabel in_range;
// 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_(Operand(edi), Immediate(0x7ff00000)); // Exponent only.
int supported_exponent_limit =
(63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
__ cmp(Operand(edi), Immediate(supported_exponent_limit));
__ j(below, &in_range, taken);
// Check for infinity and NaN. Both return NaN for sin.
__ cmp(Operand(edi), Immediate(0x7ff00000));
NearLabel non_nan_result;
__ j(not_equal, &non_nan_result, taken);
// 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(Operand(esp), Immediate(2 * kPointerSize));
__ jmp(&done);
// 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.
NearLabel in_range;
// 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_(Operand(edi), Immediate(0x7ff00000)); // Exponent only.
int supported_exponent_limit =
(63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
__ cmp(Operand(edi), Immediate(supported_exponent_limit));
__ j(below, &in_range, taken);
// Check for infinity and NaN. Both return NaN for sin.
__ cmp(Operand(edi), Immediate(0x7ff00000));
NearLabel non_nan_result;
__ j(not_equal, &non_nan_result, taken);
// 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(Operand(esp), Immediate(2 * kPointerSize));
__ jmp(&done);
__ bind(&non_nan_result);
__ 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.
{
NearLabel no_exceptions;
__ fwait();
__ fnstsw_ax();
// Clear if Illegal Operand or Zero Division exceptions are set.
__ test(Operand(eax), Immediate(5));
__ j(zero, &no_exceptions);
__ fnclex();
__ bind(&no_exceptions);
}
// 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.
{
NearLabel no_exceptions;
__ fwait();
__ fnstsw_ax();
// Clear if Illegal Operand or Zero Division exceptions are set.
__ test(Operand(eax), Immediate(5));
__ j(zero, &no_exceptions);
__ fnclex();
__ bind(&no_exceptions);
// Compute st(0) % st(1)
{
NearLabel partial_remainder_loop;
__ bind(&partial_remainder_loop);
__ fprem1();
__ fwait();
__ fnstsw_ax();
__ test(Operand(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;
default:
UNREACHABLE();
}
__ bind(&done);
} else {
ASSERT(type_ == TranscendentalCache::LOG);
__ fldln2();
__ fxch();
__ fyl2x();
}
// Compute st(0) % st(1)
{
NearLabel partial_remainder_loop;
__ bind(&partial_remainder_loop);
__ fprem1();
__ fwait();
__ fnstsw_ax();
__ test(Operand(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;
default:
UNREACHABLE();
}
__ bind(&done);
}

View File

@ -7995,6 +7995,15 @@ void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateMathLog(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1);
Load(args->at(0));
TranscendentalCacheStub stub(TranscendentalCache::LOG);
Result result = frame_->CallStub(&stub, 1);
frame_->Push(&result);
}
// Generates the Math.sqrt method. Please note - this function assumes that
// the callsite has executed ToNumber on the argument.
void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {

View File

@ -705,8 +705,9 @@ class CodeGenerator: public AstVisitor {
void GenerateMathSin(ZoneList<Expression*>* args);
void GenerateMathCos(ZoneList<Expression*>* args);
void GenerateMathSqrt(ZoneList<Expression*>* args);
void GenerateMathLog(ZoneList<Expression*>* args);
// Check whether two RegExps are equivalent
// Check whether two RegExps are equivalent.
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);

View File

@ -733,7 +733,9 @@ int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode,
case 0xE4: mnem = "ftst"; break;
case 0xE8: mnem = "fld1"; break;
case 0xEB: mnem = "fldpi"; break;
case 0xED: mnem = "fldln2"; break;
case 0xEE: mnem = "fldz"; break;
case 0xF1: mnem = "fyl2x"; break;
case 0xF5: mnem = "fprem1"; break;
case 0xF7: mnem = "fincstp"; break;
case 0xF8: mnem = "fprem"; break;

View File

@ -2917,6 +2917,16 @@ void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) {
// Load the argument on the stack and call the stub.
TranscendentalCacheStub stub(TranscendentalCache::LOG);
ASSERT(args->length() == 1);
VisitForStackValue(args->at(0));
__ CallStub(&stub);
context()->Plug(eax);
}
void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
// Load the argument on the stack and call the runtime function.
ASSERT(args->length() == 1);

View File

@ -113,7 +113,7 @@ function MathFloor(x) {
// ECMA 262 - 15.8.2.10
function MathLog(x) {
if (!IS_NUMBER(x)) x = ToNumber(x);
return %Math_log(x);
return %_MathLog(x);
}
// ECMA 262 - 15.8.2.11

View File

@ -417,6 +417,7 @@ namespace internal {
F(MathSin, 1, 1) \
F(MathCos, 1, 1) \
F(MathSqrt, 1, 1) \
F(MathLog, 1, 1) \
F(IsRegExpEquivalent, 2, 1) \
F(HasCachedArrayIndex, 1, 1) \
F(GetCachedArrayIndex, 1, 1) \

View File

@ -2217,6 +2217,14 @@ void Assembler::fldpi() {
}
void Assembler::fldln2() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit(0xD9);
emit(0xED);
}
void Assembler::fld_s(const Operand& adr) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@ -2358,6 +2366,14 @@ void Assembler::fsin() {
}
void Assembler::fyl2x() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit(0xD9);
emit(0xF1);
}
void Assembler::fadd(int i) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;

View File

@ -1046,6 +1046,7 @@ class Assembler : public Malloced {
void fld1();
void fldz();
void fldpi();
void fldln2();
void fld_s(const Operand& adr);
void fld_d(const Operand& adr);
@ -1100,6 +1101,7 @@ class Assembler : public Malloced {
void fsin();
void fcos();
void fyl2x();
void frndint();

View File

@ -1107,6 +1107,7 @@ Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
// Add more cases when necessary.
case TranscendentalCache::SIN: return Runtime::kMath_sin;
case TranscendentalCache::COS: return Runtime::kMath_cos;
case TranscendentalCache::LOG: return Runtime::kMath_log;
default:
UNIMPLEMENTED();
return Runtime::kAbort;
@ -1121,73 +1122,76 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm,
// rcx: Pointer to cache entry. Must be preserved.
// st(0): Input double
Label done;
ASSERT(type_ == TranscendentalCache::SIN ||
type_ == TranscendentalCache::COS);
// More transcendental types can be added later.
if (type_ == TranscendentalCache::SIN || type_ == TranscendentalCache::COS) {
// 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;
// If argument is outside the range -2^63..2^63, fsin/cos doesn't
// work. We must reduce it to the appropriate range.
__ movq(rdi, rbx);
// Move exponent and sign bits to low bits.
__ shr(rdi, Immediate(HeapNumber::kMantissaBits));
// Remove sign bit.
__ andl(rdi, Immediate((1 << HeapNumber::kExponentBits) - 1));
int supported_exponent_limit = (63 + HeapNumber::kExponentBias);
__ cmpl(rdi, Immediate(supported_exponent_limit));
__ j(below, &in_range);
// Check for infinity and NaN. Both return NaN for sin.
__ cmpl(rdi, Immediate(0x7ff));
__ j(equal, on_nan_result);
// 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;
// If argument is outside the range -2^63..2^63, fsin/cos doesn't
// work. We must reduce it to the appropriate range.
__ movq(rdi, rbx);
// Move exponent and sign bits to low bits.
__ shr(rdi, Immediate(HeapNumber::kMantissaBits));
// Remove sign bit.
__ andl(rdi, Immediate((1 << HeapNumber::kExponentBits) - 1));
int supported_exponent_limit = (63 + HeapNumber::kExponentBias);
__ cmpl(rdi, Immediate(supported_exponent_limit));
__ j(below, &in_range);
// Check for infinity and NaN. Both return NaN for sin.
__ cmpl(rdi, Immediate(0x7ff));
__ j(equal, on_nan_result);
// Use fpmod to restrict argument to the range +/-2*PI.
__ 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.
__ testl(rax, Immediate(5)); // #IO and #ZD flags of FPU status word.
__ j(zero, &no_exceptions);
__ fnclex();
__ bind(&no_exceptions);
}
// Use fpmod to restrict argument to the range +/-2*PI.
__ 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.
__ testl(rax, Immediate(5)); // #IO and #ZD flags of FPU status word.
__ j(zero, &no_exceptions);
__ fnclex();
__ bind(&no_exceptions);
// Compute st(0) % st(1)
{
NearLabel partial_remainder_loop;
__ bind(&partial_remainder_loop);
__ fprem1();
__ fwait();
__ fnstsw_ax();
__ testl(rax, Immediate(0x400)); // Check C2 bit of FPU status word.
// If C2 is set, computation only has partial result. Loop to
// continue computation.
__ j(not_zero, &partial_remainder_loop);
}
// Compute st(0) % st(1)
{
NearLabel partial_remainder_loop;
__ bind(&partial_remainder_loop);
__ fprem1();
__ fwait();
__ fnstsw_ax();
__ testl(rax, Immediate(0x400)); // Check C2 bit of FPU status word.
// 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);
// FPU Stack: input % 2*pi, 2*pi,
__ fstp(0);
// FPU Stack: input % 2*pi
__ bind(&in_range);
switch (type_) {
case TranscendentalCache::SIN:
__ fsin();
break;
case TranscendentalCache::COS:
__ fcos();
break;
default:
UNREACHABLE();
}
__ bind(&done);
} else {
ASSERT(type_ == TranscendentalCache::LOG);
__ fldln2();
__ fxch();
__ fyl2x();
}
// FPU Stack: input, 2*pi, input % 2*pi
__ fstp(2);
// FPU Stack: input % 2*pi, 2*pi,
__ fstp(0);
// FPU Stack: input % 2*pi
__ bind(&in_range);
switch (type_) {
case TranscendentalCache::SIN:
__ fsin();
break;
case TranscendentalCache::COS:
__ fcos();
break;
default:
UNREACHABLE();
}
__ bind(&done);
}

View File

@ -7111,6 +7111,15 @@ void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateMathLog(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1);
Load(args->at(0));
TranscendentalCacheStub stub(TranscendentalCache::LOG);
Result result = frame_->CallStub(&stub, 1);
frame_->Push(&result);
}
// Generates the Math.sqrt method. Please note - this function assumes that
// the callsite has executed ToNumber on the argument.
void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {

View File

@ -664,14 +664,16 @@ class CodeGenerator: public AstVisitor {
void GenerateMathSin(ZoneList<Expression*>* args);
void GenerateMathCos(ZoneList<Expression*>* args);
void GenerateMathSqrt(ZoneList<Expression*>* args);
void GenerateMathLog(ZoneList<Expression*>* args);
// Check whether two RegExps are equivalent.
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
void GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args);
// Simple condition analysis.
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
ALWAYS_FALSE,

View File

@ -906,7 +906,9 @@ int DisassemblerX64::RegisterFPUInstruction(int escape_opcode,
case 0xE4: mnem = "ftst"; break;
case 0xE8: mnem = "fld1"; break;
case 0xEB: mnem = "fldpi"; break;
case 0xED: mnem = "fldln2"; break;
case 0xEE: mnem = "fldz"; break;
case 0xF1: mnem = "fyl2x"; break;
case 0xF5: mnem = "fprem1"; break;
case 0xF7: mnem = "fincstp"; break;
case 0xF8: mnem = "fprem"; break;

View File

@ -2623,6 +2623,16 @@ void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) {
// Load the argument on the stack and call the stub.
TranscendentalCacheStub stub(TranscendentalCache::LOG);
ASSERT(args->length() == 1);
VisitForStackValue(args->at(0));
__ CallStub(&stub);
context()->Plug(rax);
}
void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
// Load the argument on the stack and call the runtime function.
ASSERT(args->length() == 1);