diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index 506f9b2d5d..6e18277783 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -37,6 +37,19 @@ namespace internal { #define __ ACCESS_MASM(masm) +TranscendentalFunction CreateTranscendentalFunction( + TranscendentalCache::Type type) { + switch (type) { + case TranscendentalCache::SIN: return &sin; + case TranscendentalCache::COS: return &cos; + case TranscendentalCache::TAN: return &tan; + case TranscendentalCache::LOG: return &log; + default: UNIMPLEMENTED(); + } + return NULL; +} + + // ------------------------------------------------------------------------- // Platform-specific RuntimeCallHelper functions. diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index b747ab8155..108a6de627 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1170,7 +1170,7 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { BuiltinFunctionId op = instr->op(); - if (op == kMathLog || op == kMathSin || op == kMathCos) { + if (op == kMathLog || op == kMathSin || op == kMathCos || op == kMathTan) { LOperand* input = UseFixedDouble(instr->value(), d2); LUnaryMathOperation* result = new(zone()) LUnaryMathOperation(input, NULL); return MarkAsCall(DefineFixedDouble(result, d2), instr); diff --git a/src/codegen.h b/src/codegen.h index 5360d3ef3c..28a3006e1c 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -84,6 +84,15 @@ enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF }; namespace v8 { namespace internal { +// Results of the library implementation of transcendental functions may differ +// from the one we use in our generated code. Therefore we use the same +// generated code both in runtime and compiled code. +typedef double (*TranscendentalFunction)(double x); + +TranscendentalFunction CreateTranscendentalFunction( + TranscendentalCache::Type type); + + class ElementsTransitionGenerator : public AllStatic { public: static void GenerateSmiOnlyToObject(MacroAssembler* masm); diff --git a/src/heap-inl.h b/src/heap-inl.h index 81ed448a17..706d2886b9 100644 --- a/src/heap-inl.h +++ b/src/heap-inl.h @@ -32,6 +32,7 @@ #include "isolate.h" #include "list-inl.h" #include "objects.h" +#include "platform.h" #include "v8-counters.h" #include "store-buffer.h" #include "store-buffer-inl.h" @@ -658,15 +659,15 @@ double TranscendentalCache::SubCache::Calculate(double input) { case ATAN: return atan(input); case COS: - return cos(input); + return fast_cos(input); case EXP: return exp(input); case LOG: - return log(input); + return fast_log(input); case SIN: - return sin(input); + return fast_sin(input); case TAN: - return tan(input); + return fast_tan(input); default: return 0.0; // Never happens. } diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index eb0e0fd7d5..a05fc776f8 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -1886,6 +1886,7 @@ class HUnaryMathOperation: public HTemplateInstruction<2> { case kMathLog: case kMathSin: case kMathCos: + case kMathTan: set_representation(Representation::Double()); break; default: @@ -1916,6 +1917,7 @@ class HUnaryMathOperation: public HTemplateInstruction<2> { case kMathLog: case kMathSin: case kMathCos: + case kMathTan: return Representation::Double(); case kMathAbs: return representation(); diff --git a/src/hydrogen.cc b/src/hydrogen.cc index ccddd88690..9c83380492 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -5420,6 +5420,7 @@ bool HGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr, bool drop_extra) { case kMathLog: case kMathSin: case kMathCos: + case kMathTan: if (expr->arguments()->length() == 1) { HValue* argument = Pop(); HValue* context = environment()->LookupContext(); @@ -5480,6 +5481,7 @@ bool HGraphBuilder::TryInlineBuiltinMethodCall(Call* expr, case kMathLog: case kMathSin: case kMathCos: + case kMathTan: if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) { AddCheckConstantFunction(expr, receiver, receiver_map, true); HValue* argument = Pop(); diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index b89cc14c66..5b6f6873c2 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -2510,7 +2510,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ fld_d(Operand(esp, 0)); __ add(esp, Immediate(kDoubleSize)); } - GenerateOperation(masm); + GenerateOperation(masm, type_); __ mov(Operand(ecx, 0), ebx); __ mov(Operand(ecx, kIntSize), edx); __ mov(Operand(ecx, 2 * kIntSize), eax); @@ -2526,7 +2526,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ sub(esp, Immediate(kDoubleSize)); __ movdbl(Operand(esp, 0), xmm1); __ fld_d(Operand(esp, 0)); - GenerateOperation(masm); + GenerateOperation(masm, type_); __ fstp_d(Operand(esp, 0)); __ movdbl(xmm1, Operand(esp, 0)); __ add(esp, Immediate(kDoubleSize)); @@ -2578,14 +2578,15 @@ Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() { } -void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { +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) { + 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. @@ -2649,7 +2650,7 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { // FPU Stack: input % 2*pi __ bind(&in_range); - switch (type_) { + switch (type) { case TranscendentalCache::SIN: __ fsin(); break; @@ -2667,7 +2668,7 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { } __ bind(&done); } else { - ASSERT(type_ == TranscendentalCache::LOG); + ASSERT(type == TranscendentalCache::LOG); __ fldln2(); __ fxch(); __ fyl2x(); diff --git a/src/ia32/code-stubs-ia32.h b/src/ia32/code-stubs-ia32.h index 4d23c3a174..803a711de9 100644 --- a/src/ia32/code-stubs-ia32.h +++ b/src/ia32/code-stubs-ia32.h @@ -49,6 +49,8 @@ class TranscendentalCacheStub: public CodeStub { ArgumentType argument_type) : type_(type), argument_type_(argument_type) {} void Generate(MacroAssembler* masm); + static void GenerateOperation(MacroAssembler* masm, + TranscendentalCache::Type type); private: TranscendentalCache::Type type_; ArgumentType argument_type_; @@ -56,7 +58,6 @@ class TranscendentalCacheStub: public CodeStub { Major MajorKey() { return TranscendentalCache; } int MinorKey() { return type_ | argument_type_; } Runtime::FunctionId RuntimeFunction(); - void GenerateOperation(MacroAssembler* masm); }; diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 3e085a245d..e1f689e19a 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -30,6 +30,7 @@ #if defined(V8_TARGET_ARCH_IA32) #include "codegen.h" +#include "heap.h" #include "macro-assembler.h" namespace v8 { @@ -55,6 +56,52 @@ void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { #define __ masm. + +TranscendentalFunction CreateTranscendentalFunction( + TranscendentalCache::Type type) { + size_t actual_size; + // Allocate buffer in executable space. + byte* buffer = static_cast(OS::Allocate(1 * KB, + &actual_size, + true)); + if (buffer == NULL) { + // Fallback to library function if function cannot be created. + switch (type) { + case TranscendentalCache::SIN: return &sin; + case TranscendentalCache::COS: return &cos; + case TranscendentalCache::TAN: return &tan; + case TranscendentalCache::LOG: return &log; + default: UNIMPLEMENTED(); + } + } + + MacroAssembler masm(NULL, buffer, static_cast(actual_size)); + // esp[1 * kPointerSize]: raw double input + // esp[0 * kPointerSize]: return address + // Move double input into registers. + __ fld_d(Operand(esp, kPointerSize)); + __ push(ebx); + __ push(edx); + __ push(edi); + __ mov(ebx, Operand(esp, kPointerSize)); + __ mov(edx, Operand(esp, 2* kPointerSize)); + TranscendentalCacheStub::GenerateOperation(&masm, type); + // The return value is expected to be on ST(0) of the FPU stack. + __ pop(edi); + __ pop(edx); + __ pop(ebx); + __ Ret(); + + CodeDesc desc; + masm.GetCode(&desc); + ASSERT(desc.reloc_size == 0); + + CPU::FlushICache(buffer, actual_size); + OS::ProtectCode(buffer, actual_size); + return FUNCTION_CAST(buffer); +} + + static void MemCopyWrapper(void* dest, const void* src, size_t size) { memcpy(dest, src, size); } diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 6db041319d..7587016520 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1195,7 +1195,7 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { LUnaryMathOperation* result = new(zone()) LUnaryMathOperation(context, input); return DefineSameAsFirst(result); - } else if (op == kMathSin || op == kMathCos) { + } else if (op == kMathSin || op == kMathCos || op == kMathTan) { LOperand* context = UseFixed(instr->context(), esi); LOperand* input = UseFixedDouble(instr->value(), xmm1); LUnaryMathOperation* result = new(zone()) LUnaryMathOperation(context, diff --git a/src/mips/codegen-mips.cc b/src/mips/codegen-mips.cc index d7bddaf125..8cbb771952 100644 --- a/src/mips/codegen-mips.cc +++ b/src/mips/codegen-mips.cc @@ -37,6 +37,19 @@ namespace internal { #define __ ACCESS_MASM(masm) +TranscendentalFunction CreateTranscendentalFunction( + TranscendentalCache::Type type) { + switch (type) { + case TranscendentalCache::SIN: return &sin; + case TranscendentalCache::COS: return &cos; + case TranscendentalCache::TAN: return &tan; + case TranscendentalCache::LOG: return &log; + default: UNIMPLEMENTED(); + } + return NULL; +} + + // ------------------------------------------------------------------------- // Platform-specific RuntimeCallHelper functions. diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc index ae07c1e456..1c4e1da3fb 100644 --- a/src/mips/lithium-mips.cc +++ b/src/mips/lithium-mips.cc @@ -1169,7 +1169,7 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { BuiltinFunctionId op = instr->op(); - if (op == kMathLog || op == kMathSin || op == kMathCos) { + if (op == kMathLog || op == kMathSin || op == kMathCos || op == kMathTan) { LOperand* input = UseFixedDouble(instr->value(), f4); LUnaryMathOperation* result = new(zone()) LUnaryMathOperation(input, NULL); return MarkAsCall(DefineFixedDouble(result, f4), instr); diff --git a/src/platform-nullos.cc b/src/platform-nullos.cc index 918327a983..e05345c80a 100644 --- a/src/platform-nullos.cc +++ b/src/platform-nullos.cc @@ -55,6 +55,30 @@ double modulo(double x, double y) { } +double fast_sin(double x) { + UNIMPLEMENTED(); + return 0; +} + + +double fast_cos(double x) { + UNIMPLEMENTED(); + return 0; +} + + +double fast_tan(double x) { + UNIMPLEMENTED(); + return 0; +} + + +double fast_log(double x) { + UNIMPLEMENTED(); + return 0; +} + + // Initialize OS class early in the V8 startup. void OS::SetUp() { // Seed the random number generator. diff --git a/src/platform-posix.cc b/src/platform-posix.cc index 34fd5c4498..4543a66e8c 100644 --- a/src/platform-posix.cc +++ b/src/platform-posix.cc @@ -53,6 +53,7 @@ #include "v8.h" +#include "codegen.h" #include "platform.h" namespace v8 { @@ -126,6 +127,29 @@ double modulo(double x, double y) { } +static Mutex* transcendental_function_mutex = OS::CreateMutex(); + +#define TRANSCENDENTAL_FUNCTION(name, type) \ +static TranscendentalFunction fast_##name##_function = NULL; \ +double fast_##name(double x) { \ + if (fast_##name##_function == NULL) { \ + ScopedLock lock(transcendental_function_mutex); \ + TranscendentalFunction temp = \ + CreateTranscendentalFunction(type); \ + MemoryBarrier(); \ + fast_##name##_function = temp; \ + } \ + return (*fast_##name##_function)(x); \ +} + +TRANSCENDENTAL_FUNCTION(sin, TranscendentalCache::SIN) +TRANSCENDENTAL_FUNCTION(cos, TranscendentalCache::COS) +TRANSCENDENTAL_FUNCTION(tan, TranscendentalCache::TAN) +TRANSCENDENTAL_FUNCTION(log, TranscendentalCache::LOG) + +#undef TRANSCENDENTAL_FUNCTION + + double OS::nan_value() { // NAN from math.h is defined in C99 and not in POSIX. return NAN; diff --git a/src/platform-win32.cc b/src/platform-win32.cc index e9e99246cb..224527adda 100644 --- a/src/platform-win32.cc +++ b/src/platform-win32.cc @@ -32,6 +32,7 @@ #include "v8.h" +#include "codegen.h" #include "platform.h" #include "vm-state-inl.h" @@ -201,6 +202,30 @@ double modulo(double x, double y) { #endif // _WIN64 + +static Mutex* transcendental_function_mutex = OS::CreateMutex(); + +#define TRANSCENDENTAL_FUNCTION(name, type) \ +static TranscendentalFunction fast_##name##_function = NULL; \ +double fast_##name(double x) { \ + if (fast_##name##_function == NULL) { \ + ScopedLock lock(transcendental_function_mutex); \ + TranscendentalFunction temp = \ + CreateTranscendentalFunction(type); \ + MemoryBarrier(); \ + fast_##name##_function = temp; \ + } \ + return (*fast_##name##_function)(x); \ +} + +TRANSCENDENTAL_FUNCTION(sin, TranscendentalCache::SIN) +TRANSCENDENTAL_FUNCTION(cos, TranscendentalCache::COS) +TRANSCENDENTAL_FUNCTION(tan, TranscendentalCache::TAN) +TRANSCENDENTAL_FUNCTION(log, TranscendentalCache::LOG) + +#undef TRANSCENDENTAL_FUNCTION + + // ---------------------------------------------------------------------------- // The Time class represents time on win32. A timestamp is represented as // a 64-bit integer in 100 nanoseconds since January 1, 1601 (UTC). JavaScript diff --git a/src/platform.h b/src/platform.h index 38e633a380..ccb4109b22 100644 --- a/src/platform.h +++ b/src/platform.h @@ -96,6 +96,12 @@ class Mutex; double ceiling(double x); double modulo(double x, double y); +// Custom implementation of sin, cos, tan and log. +double fast_sin(double input); +double fast_cos(double input); +double fast_tan(double input); +double fast_log(double input); + // Forward declarations. class Socket; diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 982639e9a5..307ade4330 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -1628,7 +1628,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm1); __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset)); } - GenerateOperation(masm); + GenerateOperation(masm, type_); __ movq(Operand(rcx, 0), rbx); __ movq(Operand(rcx, 2 * kIntSize), rax); __ fstp_d(FieldOperand(rax, HeapNumber::kValueOffset)); @@ -1643,7 +1643,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ subq(rsp, Immediate(kDoubleSize)); __ movsd(Operand(rsp, 0), xmm1); __ fld_d(Operand(rsp, 0)); - GenerateOperation(masm); + GenerateOperation(masm, type_); __ fstp_d(Operand(rsp, 0)); __ movsd(xmm1, Operand(rsp, 0)); __ addq(rsp, Immediate(kDoubleSize)); @@ -1695,16 +1695,17 @@ Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() { } -void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { +void TranscendentalCacheStub::GenerateOperation( + MacroAssembler* masm, TranscendentalCache::Type type) { // 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 Label done; - if (type_ == TranscendentalCache::SIN || - type_ == TranscendentalCache::COS || - type_ == TranscendentalCache::TAN) { + 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. @@ -1767,7 +1768,7 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { // FPU Stack: input % 2*pi __ movq(rax, rdi); // Restore rax, pointer to the new HeapNumber. __ bind(&in_range); - switch (type_) { + switch (type) { case TranscendentalCache::SIN: __ fsin(); break; @@ -1785,7 +1786,7 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { } __ bind(&done); } else { - ASSERT(type_ == TranscendentalCache::LOG); + ASSERT(type == TranscendentalCache::LOG); __ fldln2(); __ fxch(); __ fyl2x(); diff --git a/src/x64/code-stubs-x64.h b/src/x64/code-stubs-x64.h index 30ef3e8c53..6a1a18f830 100644 --- a/src/x64/code-stubs-x64.h +++ b/src/x64/code-stubs-x64.h @@ -48,6 +48,8 @@ class TranscendentalCacheStub: public CodeStub { ArgumentType argument_type) : type_(type), argument_type_(argument_type) {} void Generate(MacroAssembler* masm); + static void GenerateOperation(MacroAssembler* masm, + TranscendentalCache::Type type); private: TranscendentalCache::Type type_; ArgumentType argument_type_; @@ -55,7 +57,6 @@ class TranscendentalCacheStub: public CodeStub { Major MajorKey() { return TranscendentalCache; } int MinorKey() { return type_ | argument_type_; } Runtime::FunctionId RuntimeFunction(); - void GenerateOperation(MacroAssembler* masm); }; diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 8947f70055..2584889f08 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -54,6 +54,52 @@ void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { #define __ masm. + +TranscendentalFunction CreateTranscendentalFunction( + TranscendentalCache::Type type) { + size_t actual_size; + // Allocate buffer in executable space. + byte* buffer = static_cast(OS::Allocate(1 * KB, + &actual_size, + true)); + if (buffer == NULL) { + // Fallback to library function if function cannot be created. + switch (type) { + case TranscendentalCache::SIN: return &sin; + case TranscendentalCache::COS: return &cos; + case TranscendentalCache::TAN: return &tan; + case TranscendentalCache::LOG: return &log; + default: UNIMPLEMENTED(); + } + } + + MacroAssembler masm(NULL, buffer, static_cast(actual_size)); + // xmm0: raw double input. + // Move double input into registers. + __ push(rbx); + __ push(rdi); + __ movq(rbx, xmm0); + __ push(rbx); + __ fld_d(Operand(rsp, 0)); + TranscendentalCacheStub::GenerateOperation(&masm, type); + // The return value is expected to be in xmm0. + __ fstp_d(Operand(rsp, 0)); + __ pop(rbx); + __ movq(xmm0, rbx); + __ pop(rdi); + __ pop(rbx); + __ Ret(); + + CodeDesc desc; + masm.GetCode(&desc); + ASSERT(desc.reloc_size == 0); + + CPU::FlushICache(buffer, actual_size); + OS::ProtectCode(buffer, actual_size); + return FUNCTION_CAST(buffer); +} + + #ifdef _WIN64 typedef double (*ModuloFunction)(double, double); // Define custom fmod implementation. diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index 19bb58ae64..79ca5f59e7 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1163,7 +1163,7 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { BuiltinFunctionId op = instr->op(); - if (op == kMathLog || op == kMathSin || op == kMathCos) { + if (op == kMathLog || op == kMathSin || op == kMathCos || op == kMathTan) { LOperand* input = UseFixedDouble(instr->value(), xmm1); LUnaryMathOperation* result = new(zone()) LUnaryMathOperation(input); return MarkAsCall(DefineFixedDouble(result, xmm1), instr); diff --git a/test/mjsunit/regress/regress-transcendental.js b/test/mjsunit/regress/regress-transcendental.js new file mode 100644 index 0000000000..acaf11cf1f --- /dev/null +++ b/test/mjsunit/regress/regress-transcendental.js @@ -0,0 +1,48 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-gc + +// Test whether the runtime implementation and generated code of +// sine and tangens return the same results. + +function test(f, x, name) { + // Reset transcendental cache. + gc(); + // Initializing cache leads to a runtime call. + var runtime_result = f(x); + // Flush transcendental cache. + for (var i = 0; i < 100000; i++) f(i); + // Calculate using generated code. + var gencode_result = f(x); + print(name + " runtime function: " + runtime_result); + print(name + " generated code : " + gencode_result); + assertEquals(gencode_result, runtime_result); +} + +test(Math.tan, -1.57079632679489660000, "Math.tan"); +test(Math.sin, 6.283185307179586, "Math.sin");