Revision: 17654
Author:   [email protected]
Date:     Tue Nov 12 14:43:18 2013 UTC
Log: Reland "Implement Math.sin, cos and tan using table lookup and spline interpolation."

This relands r17594 with necessary fixes.

[email protected]
BUG=

Review URL: https://codereview.chromium.org/70003004
http://code.google.com/p/v8/source/detail?r=17654

Modified:
 /branches/bleeding_edge/src/arm/full-codegen-arm.cc
 /branches/bleeding_edge/src/hydrogen.cc
 /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
 /branches/bleeding_edge/src/math.js
 /branches/bleeding_edge/src/mips/full-codegen-mips.cc
 /branches/bleeding_edge/src/objects-inl.h
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/runtime.h
 /branches/bleeding_edge/src/x64/full-codegen-x64.cc
 /branches/bleeding_edge/test/mjsunit/constant-folding-2.js
 /branches/bleeding_edge/test/mjsunit/sin-cos.js
 /branches/bleeding_edge/test/mozilla/mozilla.status
 /branches/bleeding_edge/test/test262/test262.status

=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Tue Nov 12 11:53:13 2013 UTC +++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Tue Nov 12 14:43:18 2013 UTC
@@ -3760,42 +3760,6 @@
   __ CallStub(&stub);
   context()->Plug(r0);
 }
-
-
-void FullCodeGenerator::EmitMathSin(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::SIN,
-                               TranscendentalCacheStub::TAGGED);
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-  VisitForStackValue(args->at(0));
-  __ CallStub(&stub);
-  context()->Plug(r0);
-}
-
-
-void FullCodeGenerator::EmitMathCos(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::COS,
-                               TranscendentalCacheStub::TAGGED);
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-  VisitForStackValue(args->at(0));
-  __ CallStub(&stub);
-  context()->Plug(r0);
-}
-
-
-void FullCodeGenerator::EmitMathTan(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::TAN,
-                               TranscendentalCacheStub::TAGGED);
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-  VisitForStackValue(args->at(0));
-  __ CallStub(&stub);
-  context()->Plug(r0);
-}


 void FullCodeGenerator::EmitMathLog(CallRuntime* expr) {
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc     Tue Nov 12 11:53:13 2013 UTC
+++ /branches/bleeding_edge/src/hydrogen.cc     Tue Nov 12 14:43:18 2013 UTC
@@ -6704,6 +6704,11 @@
   // appropriate arity.
   Handle<JSFunction> caller = current_info()->closure();
   Handle<SharedFunctionInfo> target_shared(target->shared());
+
+  // Always inline builtins marked for inlining.
+  if (target->IsBuiltin()) {
+    return target_shared->inline_builtin() ? 0 : kNotInlinable;
+  }

   // Do a quick check on source code length to avoid parsing large
   // inlining candidates.
@@ -6714,7 +6719,7 @@
   }

   // Target must be inlineable.
-  if (!target->IsInlineable()) {
+  if (!target_shared->IsInlineable()) {
     TraceInline(target, caller, "target not inlineable");
     return kNotInlinable;
   }
@@ -7094,9 +7099,6 @@
     case kMathAbs:
     case kMathSqrt:
     case kMathLog:
-    case kMathSin:
-    case kMathCos:
-    case kMathTan:
       if (expr->arguments()->length() == 1) {
         HValue* argument = Pop();
         Drop(1);  // Receiver.
@@ -7175,9 +7177,6 @@
     case kMathAbs:
     case kMathSqrt:
     case kMathLog:
-    case kMathSin:
-    case kMathCos:
-    case kMathTan:
       if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
         AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
         HValue* argument = Pop();
@@ -9504,36 +9503,6 @@
   HInstruction* result = NewUncasted<HPower>(left, right);
   return ast_context()->ReturnInstruction(result, call->id());
 }
-
-
-void HOptimizedGraphBuilder::GenerateMathSin(CallRuntime* call) {
-  ASSERT_EQ(1, call->arguments()->length());
-  CHECK_ALIVE(VisitArgumentList(call->arguments()));
-  HCallStub* result = New<HCallStub>(CodeStub::TranscendentalCache, 1);
-  result->set_transcendental_type(TranscendentalCache::SIN);
-  Drop(1);
-  return ast_context()->ReturnInstruction(result, call->id());
-}
-
-
-void HOptimizedGraphBuilder::GenerateMathCos(CallRuntime* call) {
-  ASSERT_EQ(1, call->arguments()->length());
-  CHECK_ALIVE(VisitArgumentList(call->arguments()));
-  HCallStub* result = New<HCallStub>(CodeStub::TranscendentalCache, 1);
-  result->set_transcendental_type(TranscendentalCache::COS);
-  Drop(1);
-  return ast_context()->ReturnInstruction(result, call->id());
-}
-
-
-void HOptimizedGraphBuilder::GenerateMathTan(CallRuntime* call) {
-  ASSERT_EQ(1, call->arguments()->length());
-  CHECK_ALIVE(VisitArgumentList(call->arguments()));
-  HCallStub* result = New<HCallStub>(CodeStub::TranscendentalCache, 1);
-  result->set_transcendental_type(TranscendentalCache::TAN);
-  Drop(1);
-  return ast_context()->ReturnInstruction(result, call->id());
-}


 void HOptimizedGraphBuilder::GenerateMathLog(CallRuntime* call) {
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Tue Nov 12 11:53:13 2013 UTC +++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Tue Nov 12 14:43:18 2013 UTC
@@ -3726,42 +3726,6 @@
   __ CallStub(&stub);
   context()->Plug(eax);
 }
-
-
-void FullCodeGenerator::EmitMathSin(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::SIN,
-                               TranscendentalCacheStub::TAGGED);
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-  VisitForStackValue(args->at(0));
-  __ CallStub(&stub);
-  context()->Plug(eax);
-}
-
-
-void FullCodeGenerator::EmitMathCos(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::COS,
-                               TranscendentalCacheStub::TAGGED);
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-  VisitForStackValue(args->at(0));
-  __ CallStub(&stub);
-  context()->Plug(eax);
-}
-
-
-void FullCodeGenerator::EmitMathTan(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::TAN,
-                               TranscendentalCacheStub::TAGGED);
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-  VisitForStackValue(args->at(0));
-  __ CallStub(&stub);
-  context()->Plug(eax);
-}


 void FullCodeGenerator::EmitMathLog(CallRuntime* expr) {
=======================================
--- /branches/bleeding_edge/src/math.js Tue Nov 12 14:20:53 2013 UTC
+++ /branches/bleeding_edge/src/math.js Tue Nov 12 14:43:18 2013 UTC
@@ -79,7 +79,7 @@

 // ECMA 262 - 15.8.2.7
 function MathCos(x) {
-  return %_MathCos(TO_NUMBER_INLINE(x));
+  return MathCosImpl(x);
 }

 // ECMA 262 - 15.8.2.8
@@ -179,7 +179,7 @@

 // ECMA 262 - 15.8.2.16
 function MathSin(x) {
-  return %_MathSin(TO_NUMBER_INLINE(x));
+  return MathSinImpl(x);
 }

 // ECMA 262 - 15.8.2.17
@@ -189,7 +189,7 @@

 // ECMA 262 - 15.8.2.18
 function MathTan(x) {
-  return %_MathTan(TO_NUMBER_INLINE(x));
+  return MathSinImpl(x) / MathCosImpl(x);
 }

 // Non-standard extension.
@@ -198,6 +198,92 @@
 }


+var MathSinImpl = function(x) {
+  InitTrigonometricFunctions();
+  return MathSinImpl(x);
+}
+
+
+var MathCosImpl = function(x) {
+  InitTrigonometricFunctions();
+  return MathCosImpl(x);
+}
+
+
+var InitTrigonometricFunctions;
+
+
+// Define constants and interpolation functions.
+// Also define the initialization function that populates the lookup table
+// and then wires up the function definitions.
+function SetupTrigonometricFunctions() {
+  var samples = 2048;  // Table size.
+  var pi = 3.1415926535897932;
+  var pi_half = pi / 2;
+  var inverse_pi_half = 1 / pi_half;
+  var two_pi = pi * 2;
+  var interval = pi_half / samples;
+  var inverse_interval = samples / pi_half;
+  var table_sin;
+  var table_cos_interval;
+
+  // This implements sine using the following algorithm.
+  // 1) Multiplication takes care of to-number conversion.
+  // 2) Reduce x to the first quadrant [0, pi/2].
+  //    Conveniently enough, in case of +/-Infinity, we get NaN.
+  // 3) Replace x by (pi/2-x) if x was in the 2nd or 4th quadrant.
+ // 4) Do a table lookup for the closest samples to the left and right of x.
+  // 5) Find the derivatives at those sampling points by table lookup:
+  //    dsin(x)/dx = cos(x) = sin(pi/2-x) for x in [0, pi/2].
+  // 6) Use cubic spline interpolation to approximate sin(x).
+  // 7) Negate the result if x was in the 3rd or 4th quadrant.
+  // 8) Get rid of -0 by adding 0.
+  var Interpolation = function(x) {
+    var double_index = x * inverse_interval;
+    var index = double_index | 0;
+    var t1 = double_index - index;
+    var t2 = 1 - t1;
+    var y1 = table_sin[index];
+    var y2 = table_sin[index + 1];
+    var dy = y2 - y1;
+    return (t2 * y1 + t1 * y2 +
+                t1 * t2 * ((table_cos_interval[index] - dy) * t2 +
+                           (dy - table_cos_interval[index + 1]) * t1));
+  }
+
+  var MathSinInterpolation = function(x) {
+    var multiple = MathFloor(x * inverse_pi_half);
+    if (%_IsMinusZero(multiple)) return multiple;
+    x = (multiple & 1) * pi_half +
+        (1 - ((multiple & 1) << 1)) * (x - multiple * pi_half);
+    return Interpolation(x) * (1 - (multiple & 2)) + 0;
+  }
+
+  // Cosine is sine with a phase offset of pi/2.
+  var MathCosInterpolation = function(x) {
+    var multiple = MathFloor(x * inverse_pi_half);
+    var phase = multiple + 1;
+    x = (phase & 1) * pi_half +
+        (1 - ((phase & 1) << 1)) * (x - multiple * pi_half);
+    return Interpolation(x) * (1 - (phase & 2)) + 0;
+  };
+
+  %SetInlineBuiltinFlag(Interpolation);
+  %SetInlineBuiltinFlag(MathSinInterpolation);
+  %SetInlineBuiltinFlag(MathCosInterpolation);
+
+  InitTrigonometricFunctions = function() {
+    table_sin = new global.Float64Array(samples + 2);
+    table_cos_interval = new global.Float64Array(samples + 2);
+    %PopulateTrigonometricTable(table_sin, table_cos_interval, samples);
+    MathSinImpl = MathSinInterpolation;
+    MathCosImpl = MathCosInterpolation;
+  }
+}
+
+SetupTrigonometricFunctions();
+
+
 // -------------------------------------------------------------------

 function SetUpMath() {
@@ -270,6 +356,10 @@
     "min", MathMin,
     "imul", MathImul
   ));
+
+  %SetInlineBuiltinFlag(MathSin);
+  %SetInlineBuiltinFlag(MathCos);
+  %SetInlineBuiltinFlag(MathTan);
 }

 SetUpMath();
=======================================
--- /branches/bleeding_edge/src/mips/full-codegen-mips.cc Fri Nov 8 13:44:27 2013 UTC +++ /branches/bleeding_edge/src/mips/full-codegen-mips.cc Tue Nov 12 14:43:18 2013 UTC
@@ -3762,9 +3762,9 @@
 }


-void FullCodeGenerator::EmitMathSin(CallRuntime* expr) {
+void FullCodeGenerator::EmitMathLog(CallRuntime* expr) {
   // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::SIN,
+  TranscendentalCacheStub stub(TranscendentalCache::LOG,
                                TranscendentalCacheStub::TAGGED);
   ZoneList<Expression*>* args = expr->arguments();
   ASSERT(args->length() == 1);
@@ -3775,51 +3775,22 @@
 }


-void FullCodeGenerator::EmitMathCos(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::COS,
-                               TranscendentalCacheStub::TAGGED);
+void FullCodeGenerator::EmitMathSqrt(CallRuntime* expr) {
+  // Load the argument on the stack and call the runtime function.
   ZoneList<Expression*>* args = expr->arguments();
   ASSERT(args->length() == 1);
   VisitForStackValue(args->at(0));
- __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos.
-  __ CallStub(&stub);
+  __ CallRuntime(Runtime::kMath_sqrt, 1);
   context()->Plug(v0);
 }


-void FullCodeGenerator::EmitMathTan(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::TAN,
-                               TranscendentalCacheStub::TAGGED);
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-  VisitForStackValue(args->at(0));
- __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos.
-  __ CallStub(&stub);
-  context()->Plug(v0);
-}
-
-
-void FullCodeGenerator::EmitMathLog(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::LOG,
-                               TranscendentalCacheStub::TAGGED);
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-  VisitForStackValue(args->at(0));
- __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos.
-  __ CallStub(&stub);
-  context()->Plug(v0);
-}
-
-
-void FullCodeGenerator::EmitMathSqrt(CallRuntime* expr) {
+void FullCodeGenerator::EmitMathFloor(CallRuntime* expr) {
   // Load the argument on the stack and call the runtime function.
   ZoneList<Expression*>* args = expr->arguments();
   ASSERT(args->length() == 1);
   VisitForStackValue(args->at(0));
-  __ CallRuntime(Runtime::kMath_sqrt, 1);
+  __ CallRuntime(Runtime::kMath_floor, 1);
   context()->Plug(v0);
 }

=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Fri Nov  8 19:33:05 2013 UTC
+++ /branches/bleeding_edge/src/objects-inl.h   Tue Nov 12 14:43:18 2013 UTC
@@ -4807,6 +4807,8 @@
 BOOL_GETTER(SharedFunctionInfo, compiler_hints, is_extended_mode,
             kExtendedModeFunction)
 BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, native, kNative)
+BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, inline_builtin,
+               kInlineBuiltin)
 BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints,
                name_should_print_as_anonymous,
                kNameShouldPrintAsAnonymous)
@@ -4867,6 +4869,7 @@


 void SharedFunctionInfo::set_code(Code* value, WriteBarrierMode mode) {
+  ASSERT(value->kind() != Code::OPTIMIZED_FUNCTION);
   WRITE_FIELD(this, kCodeOffset, value);
CONDITIONAL_WRITE_BARRIER(value->GetHeap(), this, kCodeOffset, value, mode);
 }
=======================================
--- /branches/bleeding_edge/src/objects.cc      Mon Nov 11 15:28:47 2013 UTC
+++ /branches/bleeding_edge/src/objects.cc      Tue Nov 12 14:43:18 2013 UTC
@@ -9740,20 +9740,6 @@
                                 ClearExceptionFlag flag) {
   return function->is_compiled() || CompileLazy(function, flag);
 }
-
-
-bool JSFunction::IsInlineable() {
-  if (IsBuiltin()) return false;
-  SharedFunctionInfo* shared_info = shared();
-  // Check that the function has a script associated with it.
-  if (!shared_info->script()->IsScript()) return false;
-  if (shared_info->optimization_disabled()) return false;
-  Code* code = shared_info->code();
-  if (code->kind() == Code::OPTIMIZED_FUNCTION) return true;
-  // If we never ran this (unlikely) then lets try to optimize it.
-  if (code->kind() != Code::FUNCTION) return true;
-  return code->optimizable();
-}


 void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
@@ -10022,6 +10008,16 @@
   return GetIsolate()->factory()->NewSubString(
       source, start_position(), end_position());
 }
+
+
+bool SharedFunctionInfo::IsInlineable() {
+  // Check that the function has a script associated with it.
+  if (!script()->IsScript()) return false;
+  if (optimization_disabled()) return false;
+  // If we never ran this (unlikely) then lets try to optimize it.
+  if (code()->kind() != Code::FUNCTION) return true;
+  return code()->optimizable();
+}


 int SharedFunctionInfo::SourceSize() {
=======================================
--- /branches/bleeding_edge/src/objects.h       Mon Nov 11 15:28:47 2013 UTC
+++ /branches/bleeding_edge/src/objects.h       Tue Nov 12 14:43:18 2013 UTC
@@ -6781,6 +6781,9 @@
   // global object.
   DECL_BOOLEAN_ACCESSORS(native)

+  // Indicate that this builtin needs to be inlined in crankshaft.
+  DECL_BOOLEAN_ACCESSORS(inline_builtin)
+
   // Indicates that the function was created by the Function function.
   // Though it's anonymous, toString should treat it as if it had the name
   // "anonymous".  We don't set the name itself so that the system does not
@@ -6869,6 +6872,9 @@
     set_bailout_reason(reason);
     set_dont_optimize(reason != kNoReason);
   }
+
+  // Check whether or not this function is inlineable.
+  bool IsInlineable();

   // Source size of this function.
   int SourceSize();
@@ -7020,6 +7026,7 @@
     kUsesArguments,
     kHasDuplicateParameters,
     kNative,
+    kInlineBuiltin,
     kBoundFunction,
     kIsAnonymous,
     kNameShouldPrintAsAnonymous,
@@ -7246,9 +7253,6 @@
// Tells whether or not the function is on the concurrent recompilation queue.
   inline bool IsInRecompileQueue();

-  // Check whether or not this function is inlineable.
-  bool IsInlineable();
-
   // [literals_or_bindings]: Fixed array holding either
   // the materialized literals or the bindings of a bound function.
   //
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Mon Nov 11 14:51:56 2013 UTC
+++ /branches/bleeding_edge/src/runtime.cc      Tue Nov 12 14:43:18 2013 UTC
@@ -5377,6 +5377,20 @@
   }
   return isolate->heap()->undefined_value();
 }
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetInlineBuiltinFlag) {
+  SealHandleScope shs(isolate);
+  RUNTIME_ASSERT(args.length() == 1);
+
+  Handle<Object> object = args.at<Object>(0);
+
+  if (object->IsJSFunction()) {
+    JSFunction* func = JSFunction::cast(*object);
+    func->shared()->set_inline_builtin(true);
+  }
+  return isolate->heap()->undefined_value();
+}


 RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreArrayLiteralElement) {
@@ -7800,6 +7814,35 @@
   CONVERT_DOUBLE_ARG_CHECKED(x, 0);
   return isolate->transcendental_cache()->Get(TranscendentalCache::TAN, x);
 }
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PopulateTrigonometricTable) {
+  HandleScope scope(isolate);
+  ASSERT(args.length() == 3);
+  CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sin_table, 0);
+  CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, cos_table, 1);
+  CONVERT_SMI_ARG_CHECKED(samples, 2);
+  RUNTIME_ASSERT(sin_table->type() == kExternalDoubleArray);
+  RUNTIME_ASSERT(cos_table->type() == kExternalDoubleArray);
+  double* sin_buffer = reinterpret_cast<double*>(
+      JSArrayBuffer::cast(sin_table->buffer())->backing_store());
+  double* cos_buffer = reinterpret_cast<double*>(
+      JSArrayBuffer::cast(cos_table->buffer())->backing_store());
+
+  static const double pi_half = 3.1415926535897932 / 2;
+  double interval = pi_half / samples;
+  for (int i = 0; i < samples + 1; i++) {
+    double sample = sin(i * interval);
+    sin_buffer[i] = sample;
+    cos_buffer[samples - i] = sample * interval;
+  }
+
+ // Fill this to catch out of bound accesses when calculating Math.sin(pi/2).
+  sin_buffer[samples + 1] = sin(pi_half + interval);
+  cos_buffer[samples + 1] = cos(pi_half + interval) * interval;
+
+  return isolate->heap()->undefined_value();
+}


 RUNTIME_FUNCTION(MaybeObject*, Runtime_DateMakeDay) {
=======================================
--- /branches/bleeding_edge/src/runtime.h       Tue Nov 12 11:53:13 2013 UTC
+++ /branches/bleeding_edge/src/runtime.h       Tue Nov 12 14:43:18 2013 UTC
@@ -106,6 +106,7 @@
   F(AllocateInOldPointerSpace, 1, 1) \
   F(AllocateInOldDataSpace, 1, 1) \
   F(SetNativeFlag, 1, 1) \
+  F(SetInlineBuiltinFlag, 1, 1) \
   F(StoreArrayLiteralElement, 5, 1) \
   F(DebugCallbackSupportsStepping, 1, 1) \
   F(DebugPrepareStepInIfStepping, 1, 1) \
@@ -189,6 +190,7 @@
   F(Math_sin, 1, 1) \
   F(Math_sqrt, 1, 1) \
   F(Math_tan, 1, 1) \
+  F(PopulateTrigonometricTable, 3, 1) \
   \
   /* Regular expressions */ \
   F(RegExpCompile, 3, 1) \
@@ -625,9 +627,6 @@
F(IsSpecObject, 1, 1) \ F(IsStringWrapperSafeForDefaultValueOf, 1, 1) \ F(MathPow, 2, 1) \ - F(MathSin, 1, 1) \ - F(MathCos, 1, 1) \ - F(MathTan, 1, 1) \ F(MathSqrt, 1, 1) \ F(MathLog, 1, 1) \ F(IsMinusZero, 1, 1) \
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Tue Nov 12 11:53:13 2013 UTC +++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Tue Nov 12 14:43:18 2013 UTC
@@ -3684,42 +3684,6 @@
   __ CallStub(&stub);
   context()->Plug(rax);
 }
-
-
-void FullCodeGenerator::EmitMathSin(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::SIN,
-                               TranscendentalCacheStub::TAGGED);
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-  VisitForStackValue(args->at(0));
-  __ CallStub(&stub);
-  context()->Plug(rax);
-}
-
-
-void FullCodeGenerator::EmitMathCos(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::COS,
-                               TranscendentalCacheStub::TAGGED);
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-  VisitForStackValue(args->at(0));
-  __ CallStub(&stub);
-  context()->Plug(rax);
-}
-
-
-void FullCodeGenerator::EmitMathTan(CallRuntime* expr) {
-  // Load the argument on the stack and call the stub.
-  TranscendentalCacheStub stub(TranscendentalCache::TAN,
-                               TranscendentalCacheStub::TAGGED);
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-  VisitForStackValue(args->at(0));
-  __ CallStub(&stub);
-  context()->Plug(rax);
-}


 void FullCodeGenerator::EmitMathLog(CallRuntime* expr) {
=======================================
--- /branches/bleeding_edge/test/mjsunit/constant-folding-2.js Mon Jul 22 09:16:33 2013 UTC +++ /branches/bleeding_edge/test/mjsunit/constant-folding-2.js Tue Nov 12 14:43:18 2013 UTC
@@ -128,30 +128,6 @@
   assertEquals("Infinity", String(1 / Math.max(0.0, -0.0)));
 });

-test(function mathSin() {
-  assertEquals(0.0, Math.sin(0.0));
-  assertTrue(0.8 < Math.sin(1) && Math.sin(1) < 0.9);
-  assertEquals("NaN", String(Math.sin(Infinity)));
-  assertEquals("NaN", String(Math.sin(-Infinity)));
-  assertEquals("NaN", String(Math.sin(NaN)));
-});
-
-test(function mathCos() {
-  assertEquals(1.0, Math.cos(0.0));
-  assertTrue(0.5 < Math.cos(1) && Math.cos(1) < 0.6);
-  assertEquals("NaN", String(Math.cos(Infinity)));
-  assertEquals("NaN", String(Math.cos(-Infinity)));
-  assertEquals("NaN", String(Math.cos(NaN)));
-});
-
-test(function mathTan() {
-  assertEquals(0.0, Math.tan(0.0));
-  assertTrue(1.5 < Math.tan(1) && Math.tan(1) < 1.6);
-  assertEquals("NaN", String(Math.tan(Infinity)));
-  assertEquals("NaN", String(Math.tan(-Infinity)));
-  assertEquals("NaN", String(Math.tan(NaN)));
-});
-
 test(function mathExp() {
   assertEquals(1.0, Math.exp(0.0));
   assertTrue(2.7 < Math.exp(1) && Math.exp(1) < 2.8);
=======================================
--- /branches/bleeding_edge/test/mjsunit/sin-cos.js Fri Nov 8 13:44:27 2013 UTC +++ /branches/bleeding_edge/test/mjsunit/sin-cos.js Tue Nov 12 14:43:18 2013 UTC
@@ -42,9 +42,102 @@

 // By accident, the slow case for sine and cosine were both sine at
 // some point.  This is a regression test for that issue.
-var x = Math.pow(2, 70);
+var x = Math.pow(2, 30);
 assertTrue(Math.sin(x) != Math.cos(x));

 // Ensure that sine and log are not the same.
 x = 0.5;
 assertTrue(Math.sin(x) != Math.log(x));
+
+// Test against approximation by series.
+var factorial = [1];
+var accuracy = 50;
+for (var i = 1; i < accuracy; i++) {
+  factorial[i] = factorial[i-1] * i;
+}
+
+// We sum up in the reverse order for higher precision, as we expect the terms
+// to grow smaller for x reasonably close to 0.
+function precision_sum(array) {
+  var result = 0;
+  while (array.length > 0) {
+    result += array.pop();
+  }
+  return result;
+}
+
+function sin(x) {
+  var sign = 1;
+  var x2 = x*x;
+  var terms = [];
+  for (var i = 1; i < accuracy; i += 2) {
+    terms.push(sign * x / factorial[i]);
+    x *= x2;
+    sign *= -1;
+  }
+  return precision_sum(terms);
+}
+
+function cos(x) {
+  var sign = -1;
+  var x2 = x*x;
+  x = x2;
+  var terms = [1];
+  for (var i = 2; i < accuracy; i += 2) {
+    terms.push(sign * x / factorial[i]);
+    x *= x2;
+    sign *= -1;
+  }
+  return precision_sum(terms);
+}
+
+function abs_error(fun, ref, x) {
+  return Math.abs(ref(x) - fun(x));
+}
+
+var test_inputs = [];
+for (var i = -10000; i < 10000; i += 177) test_inputs.push(i/1257);
+var epsilon = 0.000001;
+
+test_inputs.push(0);
+test_inputs.push(0 + epsilon);
+test_inputs.push(0 - epsilon);
+test_inputs.push(Math.PI/2);
+test_inputs.push(Math.PI/2 + epsilon);
+test_inputs.push(Math.PI/2 - epsilon);
+test_inputs.push(Math.PI);
+test_inputs.push(Math.PI + epsilon);
+test_inputs.push(Math.PI - epsilon);
+test_inputs.push(- 2*Math.PI);
+test_inputs.push(- 2*Math.PI + epsilon);
+test_inputs.push(- 2*Math.PI - epsilon);
+
+var squares = [];
+for (var i = 0; i < test_inputs.length; i++) {
+  var x = test_inputs[i];
+  var err_sin = abs_error(Math.sin, sin, x);
+  var err_cos = abs_error(Math.cos, cos, x)
+  assertTrue(err_sin < 1E-13);
+  assertTrue(err_cos < 1E-13);
+  squares.push(err_sin*err_sin + err_cos*err_cos);
+}
+
+// Sum squares up by adding them pairwise, to avoid losing precision.
+while (squares.length > 1) {
+  var reduced = [];
+  if (squares.length % 2 == 1) reduced.push(squares.pop());
+  // Remaining number of elements is even.
+  while(squares.length > 1) reduced.push(squares.pop() + squares.pop());
+  squares = reduced;
+}
+
+var err_rms = Math.sqrt(squares[0] / test_inputs.length / 2);
+assertTrue(err_rms < 1E-14);
+
+assertEquals(-1, Math.cos({ valueOf: function() { return Math.PI; } }));
+assertEquals(0, Math.sin("0x00000"));
+assertEquals(1, Math.cos("0x00000"));
+assertTrue(isNaN(Math.sin(Infinity)));
+assertTrue(isNaN(Math.cos("-Infinity")));
+assertEquals("Infinity", String(Math.tan(Math.PI/2)));
+assertEquals("-Infinity", String(Math.tan(-Math.PI/2)));
=======================================
--- /branches/bleeding_edge/test/mozilla/mozilla.status Tue Sep 24 12:08:33 2013 UTC +++ /branches/bleeding_edge/test/mozilla/mozilla.status Tue Nov 12 14:43:18 2013 UTC
@@ -600,6 +600,9 @@
   'ecma/TypeConversion/9.3.1-3': [FAIL_OK],


+  # Math.tan expectations are more strict than the spec.
+  'ecma/Math/15.8.2.18': [FAIL_OK],
+
   ##################### FAILING TESTS #####################

   # This section is for tests that fail in V8 and pass in JSC.
=======================================
--- /branches/bleeding_edge/test/test262/test262.status Tue Sep 24 12:08:33 2013 UTC +++ /branches/bleeding_edge/test/test262/test262.status Tue Nov 12 14:43:18 2013 UTC
@@ -73,6 +73,7 @@
# trigonometric functions are platform/compiler dependent. Furthermore, the
   # expectation values by far deviates from the actual result given by an
   # arbitrary-precision calculator, making those tests partly bogus.
+  'S15.8.2.7_A7': [PASS, FAIL_OK],  # Math.cos
'S15.8.2.8_A6': [PASS, FAIL_OK], # Math.exp (less precise with --fast-math)
   'S15.8.2.16_A7': [PASS, FAIL_OK],  # Math.sin
   'S15.8.2.18_A7': [PASS, FAIL_OK],  # Math.tan

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to