From: yangguo@chromium.org Date: Wed, 6 Aug 2014 11:39:39 +0000 (+0000) Subject: Reland "Implement trigonometric functions using a fdlibm port." X-Git-Tag: upstream/4.7.83~7788 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=71857295b4abaf05db18741ba08bc120f3a498a1;p=platform%2Fupstream%2Fv8.git Reland "Implement trigonometric functions using a fdlibm port." R=svenpanne@chromium.org Review URL: https://codereview.chromium.org/448643002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22923 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/.gitignore b/.gitignore index 158e493..707582c 100644 --- a/.gitignore +++ b/.gitignore @@ -63,7 +63,7 @@ shell_g /test/test262/tc39-test262-* /testing/gmock /testing/gtest -/third_party +/third_party/icu /tools/jsfunfuzz /tools/jsfunfuzz.zip /tools/oom_dump/oom_dump diff --git a/BUILD.gn b/BUILD.gn index 07772ab..b3813d2 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -194,6 +194,7 @@ action("js2c") { "src/string.js", "src/symbol.js", "src/uri.js", + "third_party/fdlibm/fdlibm.js", "src/math.js", "src/messages.js", "src/apinatives.js", @@ -865,6 +866,8 @@ source_set("v8_base") { "src/zone-inl.h", "src/zone.cc", "src/zone.h", + "third_party/fdlibm/fdlibm.cc', + "third_party/fdlibm/fdlibm.h', ] if (v8_target_arch == "x86") { diff --git a/DEPS b/DEPS index de9d013..d45080f 100644 --- a/DEPS +++ b/DEPS @@ -41,6 +41,7 @@ include_rules = [ # Everybody can use some things. "+include", "+unicode", + "+third_party/fdlibm", ] # checkdeps.py shouldn't check for includes in these directories: diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index a746c90..c297a37 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -14,7 +14,7 @@ #include "src/isolate-inl.h" #include "src/natives.h" #include "src/snapshot.h" -#include "src/trig-table.h" +#include "third_party/fdlibm/fdlibm.h" namespace v8 { namespace internal { @@ -121,7 +121,7 @@ char* Bootstrapper::AllocateAutoDeletedArray(int bytes) { void Bootstrapper::TearDown() { if (delete_these_non_arrays_on_tear_down_ != NULL) { int len = delete_these_non_arrays_on_tear_down_->length(); - DCHECK(len < 24); // Don't use this mechanism for unbounded allocations. + DCHECK(len < 25); // Don't use this mechanism for unbounded allocations. for (int i = 0; i < len; i++) { delete delete_these_non_arrays_on_tear_down_->at(i); delete_these_non_arrays_on_tear_down_->at(i) = NULL; @@ -2651,43 +2651,19 @@ Genesis::Genesis(Isolate* isolate, NONE).Assert(); // Initialize trigonometric lookup tables and constants. - const int table_num_bytes = TrigonometricLookupTable::table_num_bytes(); - v8::Local sin_buffer = v8::ArrayBuffer::New( + const int constants_size = ARRAY_SIZE(TrigonometricConstants::constants); + const int table_num_bytes = constants_size * kDoubleSize; + v8::Local trig_buffer = v8::ArrayBuffer::New( reinterpret_cast(isolate), - TrigonometricLookupTable::sin_table(), table_num_bytes); - v8::Local cos_buffer = v8::ArrayBuffer::New( - reinterpret_cast(isolate), - TrigonometricLookupTable::cos_x_interval_table(), table_num_bytes); - v8::Local sin_table = v8::Float64Array::New( - sin_buffer, 0, TrigonometricLookupTable::table_size()); - v8::Local cos_table = v8::Float64Array::New( - cos_buffer, 0, TrigonometricLookupTable::table_size()); + const_cast(TrigonometricConstants::constants), + table_num_bytes); + v8::Local trig_table = + v8::Float64Array::New(trig_buffer, 0, constants_size); - Runtime::DefineObjectProperty(builtins, - factory()->InternalizeOneByteString( - STATIC_ASCII_VECTOR("kSinTable")), - Utils::OpenHandle(*sin_table), - NONE).Assert(); - Runtime::DefineObjectProperty( - builtins, - factory()->InternalizeOneByteString( - STATIC_ASCII_VECTOR("kCosXIntervalTable")), - Utils::OpenHandle(*cos_table), - NONE).Assert(); - Runtime::DefineObjectProperty( - builtins, - factory()->InternalizeOneByteString( - STATIC_ASCII_VECTOR("kSamples")), - factory()->NewHeapNumber( - TrigonometricLookupTable::samples()), - NONE).Assert(); Runtime::DefineObjectProperty( builtins, - factory()->InternalizeOneByteString( - STATIC_ASCII_VECTOR("kIndexConvert")), - factory()->NewHeapNumber( - TrigonometricLookupTable::samples_over_pi_half()), - NONE).Assert(); + factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR("kTrig")), + Utils::OpenHandle(*trig_table), NONE).Assert(); } result_ = native_context(); diff --git a/src/math.js b/src/math.js index 9dc4b37..436a41f 100644 --- a/src/math.js +++ b/src/math.js @@ -56,12 +56,6 @@ function MathCeil(x) { return -MathFloor(-x); } -// ECMA 262 - 15.8.2.7 -function MathCos(x) { - x = MathAbs(x); // Convert to number and get rid of -0. - return TrigonometricInterpolation(x, 1); -} - // ECMA 262 - 15.8.2.8 function MathExp(x) { return %MathExpRT(TO_NUMBER_INLINE(x)); @@ -164,97 +158,16 @@ function MathRound(x) { return %RoundNumber(TO_NUMBER_INLINE(x)); } -// ECMA 262 - 15.8.2.16 -function MathSin(x) { - x = x * 1; // Convert to number and deal with -0. - if (%_IsMinusZero(x)) return x; - return TrigonometricInterpolation(x, 0); -} - // ECMA 262 - 15.8.2.17 function MathSqrt(x) { return %_MathSqrtRT(TO_NUMBER_INLINE(x)); } -// ECMA 262 - 15.8.2.18 -function MathTan(x) { - return MathSin(x) / MathCos(x); -} - // Non-standard extension. function MathImul(x, y) { return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y)); } - -var kInversePiHalf = 0.636619772367581343; // 2 / pi -var kInversePiHalfS26 = 9.48637384723993156e-9; // 2 / pi / (2^26) -var kS26 = 1 << 26; -var kTwoStepThreshold = 1 << 27; -// pi / 2 rounded up -var kPiHalf = 1.570796326794896780; // 0x192d4454fb21f93f -// We use two parts for pi/2 to emulate a higher precision. -// pi_half_1 only has 26 significant bits for mantissa. -// Note that pi_half > pi_half_1 + pi_half_2 -var kPiHalf1 = 1.570796325802803040; // 0x00000054fb21f93f -var kPiHalf2 = 9.920935796805404252e-10; // 0x3326a611460b113e - -var kSamples; // Initialized to a number during genesis. -var kIndexConvert; // Initialized to kSamples / (pi/2) during genesis. -var kSinTable; // Initialized to a Float64Array during genesis. -var kCosXIntervalTable; // Initialized to a Float64Array during genesis. - -// 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. -// Note that we try to use only 26 instead of 52 significant bits for -// mantissa to avoid rounding errors when multiplying. For very large -// input we therefore have additional steps. -// 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. -function TrigonometricInterpolation(x, phase) { - if (x < 0 || x > kPiHalf) { - var multiple; - while (x < -kTwoStepThreshold || x > kTwoStepThreshold) { - // Let's assume this loop does not terminate. - // All numbers x in each loop forms a set S. - // (1) abs(x) > 2^27 for all x in S. - // (2) abs(multiple) != 0 since (2^27 * inverse_pi_half_s26) > 1 - // (3) multiple is rounded down in 2^26 steps, so the rounding error is - // at most max(ulp, 2^26). - // (4) so for x > 2^27, we subtract at most (1+pi/4)x and at least - // (1-pi/4)x - // (5) The subtraction results in x' so that abs(x') <= abs(x)*pi/4. - // Note that this difference cannot be simply rounded off. - // Set S cannot exist since (5) violates (1). Loop must terminate. - multiple = MathFloor(x * kInversePiHalfS26) * kS26; - x = x - multiple * kPiHalf1 - multiple * kPiHalf2; - } - multiple = MathFloor(x * kInversePiHalf); - x = x - multiple * kPiHalf1 - multiple * kPiHalf2; - phase += multiple; - } - var double_index = x * kIndexConvert; - if (phase & 1) double_index = kSamples - double_index; - var index = double_index | 0; - var t1 = double_index - index; - var t2 = 1 - t1; - var y1 = kSinTable[index]; - var y2 = kSinTable[index + 1]; - var dy = y2 - y1; - return (t2 * y1 + t1 * y2 + - t1 * t2 * ((kCosXIntervalTable[index] - dy) * t2 + - (dy - kCosXIntervalTable[index + 1]) * t1)) - * (1 - (phase & 2)) + 0; -} - - // ES6 draft 09-27-13, section 20.2.2.28. function MathSign(x) { x = TO_NUMBER_INLINE(x); @@ -264,7 +177,6 @@ function MathSign(x) { return NAN; } - // ES6 draft 09-27-13, section 20.2.2.34. function MathTrunc(x) { x = TO_NUMBER_INLINE(x); @@ -274,7 +186,6 @@ function MathTrunc(x) { return NAN; } - // ES6 draft 09-27-13, section 20.2.2.30. function MathSinh(x) { if (!IS_NUMBER(x)) x = NonNumberToNumber(x); @@ -283,7 +194,6 @@ function MathSinh(x) { return (MathExp(x) - MathExp(-x)) / 2; } - // ES6 draft 09-27-13, section 20.2.2.12. function MathCosh(x) { if (!IS_NUMBER(x)) x = NonNumberToNumber(x); @@ -291,7 +201,6 @@ function MathCosh(x) { return (MathExp(x) + MathExp(-x)) / 2; } - // ES6 draft 09-27-13, section 20.2.2.33. function MathTanh(x) { if (!IS_NUMBER(x)) x = NonNumberToNumber(x); @@ -304,7 +213,6 @@ function MathTanh(x) { return (exp1 - exp2) / (exp1 + exp2); } - // ES6 draft 09-27-13, section 20.2.2.5. function MathAsinh(x) { if (!IS_NUMBER(x)) x = NonNumberToNumber(x); @@ -315,7 +223,6 @@ function MathAsinh(x) { return -MathLog(-x + MathSqrt(x * x + 1)); } - // ES6 draft 09-27-13, section 20.2.2.3. function MathAcosh(x) { if (!IS_NUMBER(x)) x = NonNumberToNumber(x); @@ -325,7 +232,6 @@ function MathAcosh(x) { return MathLog(x + MathSqrt(x + 1) * MathSqrt(x - 1)); } - // ES6 draft 09-27-13, section 20.2.2.7. function MathAtanh(x) { if (!IS_NUMBER(x)) x = NonNumberToNumber(x); @@ -336,7 +242,6 @@ function MathAtanh(x) { return 0.5 * MathLog((1 + x) / (1 - x)); } - // ES6 draft 09-27-13, section 20.2.2.21. function MathLog10(x) { return MathLog(x) * 0.434294481903251828; // log10(x) = log(x)/log(10). @@ -348,7 +253,6 @@ function MathLog2(x) { return MathLog(x) * 1.442695040888963407; // log2(x) = log(x)/log(2). } - // ES6 draft 09-27-13, section 20.2.2.17. function MathHypot(x, y) { // Function length is 2. // We may want to introduce fast paths for two arguments and when @@ -381,13 +285,12 @@ function MathHypot(x, y) { // Function length is 2. return MathSqrt(sum) * max; } - // ES6 draft 09-27-13, section 20.2.2.16. function MathFroundJS(x) { return %MathFround(TO_NUMBER_INLINE(x)); } - +// ES6 draft 07-18-14, section 20.2.2.11 function MathClz32(x) { x = ToUint32(TO_NUMBER_INLINE(x)); if (x == 0) return 32; @@ -401,7 +304,6 @@ function MathClz32(x) { return result; } - // ES6 draft 09-27-13, section 20.2.2.9. // Cube root approximation, refer to: http://metamerist.com/cbrt/cbrt.htm // Using initial approximation adapted from Kahan's cbrt and 4 iterations @@ -425,8 +327,6 @@ function CubeRoot(x) { return NEWTON_ITERATION_CBRT(x, approx); } - - // ES6 draft 09-27-13, section 20.2.2.14. // Use Taylor series to approximate. // exp(x) - 1 at 0 == -1 + exp(0) + exp'(0)*x/1! + exp''(0)*x^2/2! + ... @@ -447,7 +347,6 @@ function MathExpm1(x) { } } - // ES6 draft 09-27-13, section 20.2.2.20. // Use Taylor series to approximate. With y = x + 1; // log(y) at 1 == log(1) + log'(1)(y-1)/1! + log''(1)(y-1)^2/2! + ... @@ -502,14 +401,14 @@ function SetUpMath() { "asin", MathAsinJS, "atan", MathAtanJS, "ceil", MathCeil, - "cos", MathCos, + "cos", MathCos, // implemented by third_party/fdlibm "exp", MathExp, "floor", MathFloor, "log", MathLog, "round", MathRound, - "sin", MathSin, + "sin", MathSin, // implemented by third_party/fdlibm "sqrt", MathSqrt, - "tan", MathTan, + "tan", MathTan, // implemented by third_party/fdlibm "atan2", MathAtan2JS, "pow", MathPow, "max", MathMax, @@ -537,8 +436,6 @@ function SetUpMath() { %SetInlineBuiltinFlag(MathRandom); %SetInlineBuiltinFlag(MathSin); %SetInlineBuiltinFlag(MathCos); - %SetInlineBuiltinFlag(MathTan); - %SetInlineBuiltinFlag(TrigonometricInterpolation); } SetUpMath(); diff --git a/src/runtime.cc b/src/runtime.cc index 5e18c97..cc41117 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -45,6 +45,7 @@ #include "src/utils.h" #include "src/v8threads.h" #include "src/vm-state-inl.h" +#include "third_party/fdlibm/fdlibm.h" #ifdef V8_I18N_SUPPORT #include "src/i18n.h" @@ -7683,6 +7684,23 @@ RUNTIME_FUNCTION(Runtime_ConstructDouble) { } +RUNTIME_FUNCTION(Runtime_RemPiO2) { + HandleScope handle_scope(isolate); + DCHECK(args.length() == 1); + CONVERT_DOUBLE_ARG_CHECKED(x, 0); + Factory* factory = isolate->factory(); + double y[2]; + int n = rempio2(x, y); + Handle array = factory->NewFixedArray(3); + Handle y0 = factory->NewHeapNumber(y[0]); + Handle y1 = factory->NewHeapNumber(y[1]); + array->set(0, Smi::FromInt(n)); + array->set(1, *y0); + array->set(2, *y1); + return *factory->NewJSArrayWithElements(array); +} + + static const double kPiDividedBy4 = 0.78539816339744830962; diff --git a/src/runtime.h b/src/runtime.h index e8b7892..d0100c5 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -149,6 +149,7 @@ namespace internal { F(MathExpRT, 1, 1) \ F(RoundNumber, 1, 1) \ F(MathFround, 1, 1) \ + F(RemPiO2, 1, 1) \ \ /* Regular expressions */ \ F(RegExpCompile, 3, 1) \ diff --git a/src/trig-table.h b/src/trig-table.h deleted file mode 100644 index 7332152..0000000 --- a/src/trig-table.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2013 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef V8_TRIG_TABLE_H_ -#define V8_TRIG_TABLE_H_ - - -namespace v8 { -namespace internal { - -class TrigonometricLookupTable : public AllStatic { - public: - // Casting away const-ness to use as argument for typed array constructor. - static void* sin_table() { - return const_cast(&kSinTable[0]); - } - - static void* cos_x_interval_table() { - return const_cast(&kCosXIntervalTable[0]); - } - - static double samples_over_pi_half() { return kSamplesOverPiHalf; } - static int samples() { return kSamples; } - static int table_num_bytes() { return kTableSize * sizeof(*kSinTable); } - static int table_size() { return kTableSize; } - - private: - static const double kSinTable[]; - static const double kCosXIntervalTable[]; - static const int kSamples; - static const int kTableSize; - static const double kSamplesOverPiHalf; -}; - -} } // namespace v8::internal - -#endif // V8_TRIG_TABLE_H_ diff --git a/test/mjsunit/debug-script.js b/test/mjsunit/debug-script.js index 3a4361a..54e80e0 100644 --- a/test/mjsunit/debug-script.js +++ b/test/mjsunit/debug-script.js @@ -59,7 +59,7 @@ for (i = 0; i < scripts.length; i++) { } // This has to be updated if the number of native scripts change. -assertTrue(named_native_count == 22 || named_native_count == 23); +assertTrue(named_native_count == 23 || named_native_count == 24); // Only the 'gc' extension is loaded. assertEquals(1, extension_count); // This script and mjsunit.js has been loaded. If using d8, d8 loads diff --git a/test/mjsunit/mirror-script.js b/test/mjsunit/mirror-script.js index 1d64ac2..e545a61 100644 --- a/test/mjsunit/mirror-script.js +++ b/test/mjsunit/mirror-script.js @@ -84,7 +84,7 @@ function testScriptMirror(f, file_name, file_lines, type, compilation_type, // Test the script mirror for different functions. testScriptMirror(function(){}, 'mirror-script.js', 98, 2, 0); -testScriptMirror(Math.sin, 'native math.js', -1, 0, 0); +testScriptMirror(Math.round, 'native math.js', -1, 0, 0); testScriptMirror(eval('(function(){})'), null, 1, 2, 1, '(function(){})', 87); testScriptMirror(eval('(function(){\n })'), null, 2, 2, 1, '(function(){\n })', 88); diff --git a/test/mjsunit/runtime-gen/rempio2.js b/test/mjsunit/runtime-gen/rempio2.js new file mode 100644 index 0000000..03a84b3 --- /dev/null +++ b/test/mjsunit/runtime-gen/rempio2.js @@ -0,0 +1,5 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY +// Flags: --allow-natives-syntax --harmony +var _x = 1.5; +%RemPiO2(_x); diff --git a/test/mjsunit/sin-cos.js b/test/mjsunit/sin-cos.js index 02ae57b..71fae20 100644 --- a/test/mjsunit/sin-cos.js +++ b/test/mjsunit/sin-cos.js @@ -157,8 +157,8 @@ 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))); +assertTrue(Math.tan(Math.PI/2) > 1e16); +assertTrue(Math.tan(-Math.PI/2) < -1e16); assertEquals("-Infinity", String(1/Math.sin("-0"))); // Assert that the remainder after division by pi is reasonably precise. @@ -185,3 +185,96 @@ for (var i = -1024; i < 1024; i++) { assertFalse(isNaN(Math.cos(1.57079632679489700))); assertFalse(isNaN(Math.cos(-1e-100))); assertFalse(isNaN(Math.cos(-1e-323))); + +// Tests for specific values expected from the fdlibm implementation. + +var two_32 = Math.pow(2, -32); +var two_28 = Math.pow(2, -28); + +// Tests for Math.sin for |x| < pi/4 +assertEquals(Infinity, 1/Math.sin(+0.0)); +assertEquals(-Infinity, 1/Math.sin(-0.0)); +// sin(x) = x for x < 2^-27 +assertEquals(two_32, Math.sin(two_32)); +assertEquals(-two_32, Math.sin(-two_32)); +// sin(pi/8) = sqrt(sqrt(2)-1)/2^(3/4) +assertEquals(0.3826834323650898, Math.sin(Math.PI/8)); +assertEquals(-0.3826834323650898, -Math.sin(Math.PI/8)); + +// Tests for Math.cos for |x| < pi/4 +// cos(x) = 1 for |x| < 2^-27 +assertEquals(1, Math.cos(two_32)); +assertEquals(1, Math.cos(-two_32)); +// Test KERNELCOS for |x| < 0.3. +// cos(pi/20) = sqrt(sqrt(2)*sqrt(sqrt(5)+5)+4)/2^(3/2) +assertEquals(0.9876883405951378, Math.cos(Math.PI/20)); +// Test KERNELCOS for x ~= 0.78125 +assertEquals(0.7100335477927638, Math.cos(0.7812504768371582)); +assertEquals(0.7100338835660797, Math.cos(0.78125)); +// Test KERNELCOS for |x| > 0.3. +// cos(pi/8) = sqrt(sqrt(2)+1)/2^(3/4) +assertEquals(0.9238795325112867, Math.cos(Math.PI/8)); +// Test KERNELTAN for |x| < 0.67434. +assertEquals(0.9238795325112867, Math.cos(-Math.PI/8)); + +// Tests for Math.tan for |x| < pi/4 +assertEquals(Infinity, 1/Math.tan(0.0)); +assertEquals(-Infinity, 1/Math.tan(-0.0)); +// tan(x) = x for |x| < 2^-28 +assertEquals(two_32, Math.tan(two_32)); +assertEquals(-two_32, Math.tan(-two_32)); +// Test KERNELTAN for |x| > 0.67434. +assertEquals(0.8211418015898941, Math.tan(11/16)); +assertEquals(-0.8211418015898941, Math.tan(-11/16)); +assertEquals(0.41421356237309503, Math.tan(Math.PI / 8)); + +// Tests for Math.sin. +assertEquals(0.479425538604203, Math.sin(0.5)); +assertEquals(-0.479425538604203, Math.sin(-0.5)); +assertEquals(1, Math.sin(Math.PI/2)); +assertEquals(-1, Math.sin(-Math.PI/2)); +// Test that Math.sin(Math.PI) != 0 since Math.PI is not exact. +assertEquals(1.2246467991473532e-16, Math.sin(Math.PI)); +assertEquals(-7.047032979958965e-14, Math.sin(2200*Math.PI)); +// Test Math.sin for various phases. +assertEquals(-0.7071067811865477, Math.sin(7/4 * Math.PI)); +assertEquals(0.7071067811865474, Math.sin(9/4 * Math.PI)); +assertEquals(0.7071067811865483, Math.sin(11/4 * Math.PI)); +assertEquals(-0.7071067811865479, Math.sin(13/4 * Math.PI)); +assertEquals(-3.2103381051568376e-11, Math.sin(1048576/4 * Math.PI)); + +// Tests for Math.cos. +assertEquals(1, Math.cos(two_28)); +// Cover different code paths in KERNELCOS. +assertEquals(0.9689124217106447, Math.cos(0.25)); +assertEquals(0.8775825618903728, Math.cos(0.5)); +assertEquals(0.7073882691671998, Math.cos(0.785)); +// Test that Math.cos(Math.PI/2) != 0 since Math.PI is not exact. +assertEquals(6.123233995736766e-17, Math.cos(Math.PI/2)); +// Test Math.cos for various phases. +assertEquals(0.7071067811865474, Math.cos(7/4 * Math.PI)); +assertEquals(0.7071067811865477, Math.cos(9/4 * Math.PI)); +assertEquals(-0.7071067811865467, Math.cos(11/4 * Math.PI)); +assertEquals(-0.7071067811865471, Math.cos(13/4 * Math.PI)); +assertEquals(0.9367521275331447, Math.cos(1000000)); +assertEquals(-3.435757038074824e-12, Math.cos(1048575/2 * Math.PI)); + +// Tests for Math.tan. +assertEquals(two_28, Math.tan(two_28)); +// Test that Math.tan(Math.PI/2) != Infinity since Math.PI is not exact. +assertEquals(1.633123935319537e16, Math.tan(Math.PI/2)); +// Cover different code paths in KERNELTAN (tangent and cotangent) +assertEquals(0.5463024898437905, Math.tan(0.5)); +assertEquals(2.0000000000000027, Math.tan(1.107148717794091)); +assertEquals(-1.0000000000000004, Math.tan(7/4*Math.PI)); +assertEquals(0.9999999999999994, Math.tan(9/4*Math.PI)); +assertEquals(-6.420676210313675e-11, Math.tan(1048576/2*Math.PI)); +assertEquals(2.910566692924059e11, Math.tan(1048575/2*Math.PI)); + +// Test Hayne-Panek reduction. +assertEquals(0.377820109360752e0, Math.sin(Math.pow(2, 120))); +assertEquals(-0.9258790228548379e0, Math.cos(Math.pow(2, 120))); +assertEquals(-0.40806638884180424e0, Math.tan(Math.pow(2, 120))); +assertEquals(-0.377820109360752e0, Math.sin(-Math.pow(2, 120))); +assertEquals(-0.9258790228548379e0, Math.cos(-Math.pow(2, 120))); +assertEquals(0.40806638884180424e0, Math.tan(-Math.pow(2, 120))); diff --git a/test/test262/test262.status b/test/test262/test262.status index 18d65c4..dd075d9 100644 --- a/test/test262/test262.status +++ b/test/test262/test262.status @@ -68,14 +68,7 @@ ##################### DELIBERATE INCOMPATIBILITIES ##################### - # This tests precision of Math functions. The implementation for those - # 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 # Linux for ia32 (and therefore simulators) default to extended 80 bit # floating point formats, so these tests checking 64-bit FP precision fail. diff --git a/third_party/fdlibm/LICENSE b/third_party/fdlibm/LICENSE new file mode 100644 index 0000000..b024795 --- /dev/null +++ b/third_party/fdlibm/LICENSE @@ -0,0 +1,6 @@ +Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + +Developed at SunSoft, a Sun Microsystems, Inc. business. +Permission to use, copy, modify, and distribute this +software is freely granted, provided that this notice +is preserved. diff --git a/third_party/fdlibm/README.v8 b/third_party/fdlibm/README.v8 new file mode 100644 index 0000000..ea8fdb6 --- /dev/null +++ b/third_party/fdlibm/README.v8 @@ -0,0 +1,18 @@ +Name: Freely Distributable LIBM +Short Name: fdlibm +URL: http://www.netlib.org/fdlibm/ +Version: 5.3 +License: Freely Distributable. +License File: LICENSE. +Security Critical: yes. +License Android Compatible: yes. + +Description: +This is used to provide a accurate implementation for trigonometric functions +used in V8. + +Local Modifications: +For the use in V8, fdlibm has been reduced to include only sine, cosine and +tangent. To make inlining into generated code possible, a large portion of +that has been translated to Javascript. The rest remains in C, but has been +refactored and reformatted to interoperate with the rest of V8. diff --git a/third_party/fdlibm/fdlibm.cc b/third_party/fdlibm/fdlibm.cc new file mode 100644 index 0000000..6b8084f --- /dev/null +++ b/third_party/fdlibm/fdlibm.cc @@ -0,0 +1,262 @@ +// The following is adapted from fdlibm (http://www.netlib.org/fdlibm). +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunSoft, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// The original source code covered by the above license above has been +// modified significantly by Google Inc. +// Copyright 2014 the V8 project authors. All rights reserved. + +#include "src/v8.h" + +#include "src/double.h" +#include "third_party/fdlibm/fdlibm.h" + + +namespace v8 { +namespace internal { + +#ifdef _MSC_VER +inline double scalbn(double x, int y) { return _scalb(x, y); } +#endif // _MSC_VER + +const double TrigonometricConstants::constants[] = { + 6.36619772367581382433e-01, // invpio2 0 + 1.57079632673412561417e+00, // pio2_1 1 + 6.07710050650619224932e-11, // pio2_1t 2 + 6.07710050630396597660e-11, // pio2_2 3 + 2.02226624879595063154e-21, // pio2_2t 4 + 2.02226624871116645580e-21, // pio2_3 5 + 8.47842766036889956997e-32, // pio2_3t 6 + -1.66666666666666324348e-01, // S1 7 + 8.33333333332248946124e-03, // 8 + -1.98412698298579493134e-04, // 9 + 2.75573137070700676789e-06, // 10 + -2.50507602534068634195e-08, // 11 + 1.58969099521155010221e-10, // S6 12 + 4.16666666666666019037e-02, // C1 13 + -1.38888888888741095749e-03, // 14 + 2.48015872894767294178e-05, // 15 + -2.75573143513906633035e-07, // 16 + 2.08757232129817482790e-09, // 17 + -1.13596475577881948265e-11, // C6 18 + 3.33333333333334091986e-01, // T0 19 + 1.33333333333201242699e-01, // 20 + 5.39682539762260521377e-02, // 21 + 2.18694882948595424599e-02, // 22 + 8.86323982359930005737e-03, // 23 + 3.59207910759131235356e-03, // 24 + 1.45620945432529025516e-03, // 25 + 5.88041240820264096874e-04, // 26 + 2.46463134818469906812e-04, // 27 + 7.81794442939557092300e-05, // 28 + 7.14072491382608190305e-05, // 29 + -1.85586374855275456654e-05, // 30 + 2.59073051863633712884e-05, // T12 31 + 7.85398163397448278999e-01, // pio4 32 + 3.06161699786838301793e-17, // pio4lo 33 +}; + + +// Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi +static const int two_over_pi[] = { + 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, 0x95993C, + 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, 0x424DD2, 0xE00649, + 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, 0xA73EE8, 0x8235F5, 0x2EBB44, + 0x84E99C, 0x7026B4, 0x5F7E41, 0x3991D6, 0x398353, 0x39F49C, 0x845F8B, + 0xBDF928, 0x3B1FF8, 0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, + 0x367ECF, 0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5, + 0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08, 0x560330, + 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3, 0x91615E, 0xE61B08, + 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, 0x4D7327, 0x310606, 0x1556CA, + 0x73A8C9, 0x60E27B, 0xC08C6B}; + +static const double zero = 0.0; +static const double two24 = 1.6777216e+07; +static const double one = 1.0; +static const double twon24 = 5.9604644775390625e-08; + +static const double PIo2[] = { + 1.57079625129699707031e+00, // 0x3FF921FB, 0x40000000 + 7.54978941586159635335e-08, // 0x3E74442D, 0x00000000 + 5.39030252995776476554e-15, // 0x3CF84698, 0x80000000 + 3.28200341580791294123e-22, // 0x3B78CC51, 0x60000000 + 1.27065575308067607349e-29, // 0x39F01B83, 0x80000000 + 1.22933308981111328932e-36, // 0x387A2520, 0x40000000 + 2.73370053816464559624e-44, // 0x36E38222, 0x80000000 + 2.16741683877804819444e-51 // 0x3569F31D, 0x00000000 +}; + + +int __kernel_rem_pio2(double* x, double* y, int e0, int nx) { + static const int32_t jk = 3; + double fw; + int32_t jx = nx - 1; + int32_t jv = (e0 - 3) / 24; + if (jv < 0) jv = 0; + int32_t q0 = e0 - 24 * (jv + 1); + int32_t m = jx + jk; + + double f[10]; + for (int i = 0, j = jv - jx; i <= m; i++, j++) { + f[i] = (j < 0) ? zero : static_cast(two_over_pi[j]); + } + + double q[10]; + for (int i = 0; i <= jk; i++) { + fw = 0.0; + for (int j = 0; j <= jx; j++) fw += x[j] * f[jx + i - j]; + q[i] = fw; + } + + int32_t jz = jk; + +recompute: + + int32_t iq[10]; + double z = q[jz]; + for (int i = 0, j = jz; j > 0; i++, j--) { + fw = static_cast(static_cast(twon24 * z)); + iq[i] = static_cast(z - two24 * fw); + z = q[j - 1] + fw; + } + + z = scalbn(z, q0); + z -= 8.0 * std::floor(z * 0.125); + int32_t n = static_cast(z); + z -= static_cast(n); + int32_t ih = 0; + if (q0 > 0) { + int32_t i = (iq[jz - 1] >> (24 - q0)); + n += i; + iq[jz - 1] -= i << (24 - q0); + ih = iq[jz - 1] >> (23 - q0); + } else if (q0 == 0) { + ih = iq[jz - 1] >> 23; + } else if (z >= 0.5) { + ih = 2; + } + + if (ih > 0) { + n += 1; + int32_t carry = 0; + for (int i = 0; i < jz; i++) { + int32_t j = iq[i]; + if (carry == 0) { + if (j != 0) { + carry = 1; + iq[i] = 0x1000000 - j; + } + } else { + iq[i] = 0xffffff - j; + } + } + if (q0 == 1) { + iq[jz - 1] &= 0x7fffff; + } else if (q0 == 2) { + iq[jz - 1] &= 0x3fffff; + } + if (ih == 2) { + z = one - z; + if (carry != 0) z -= scalbn(one, q0); + } + } + + if (z == zero) { + int32_t j = 0; + for (int i = jz - 1; i >= jk; i--) j |= iq[i]; + if (j == 0) { + int32_t k = 1; + while (iq[jk - k] == 0) k++; + for (int i = jz + 1; i <= jz + k; i++) { + f[jx + i] = static_cast(two_over_pi[jv + i]); + for (j = 0, fw = 0.0; j <= jx; j++) fw += x[j] * f[jx + i - j]; + q[i] = fw; + } + jz += k; + goto recompute; + } + } + + if (z == 0.0) { + jz -= 1; + q0 -= 24; + while (iq[jz] == 0) { + jz--; + q0 -= 24; + } + } else { + z = scalbn(z, -q0); + if (z >= two24) { + fw = static_cast(static_cast(twon24 * z)); + iq[jz] = static_cast(z - two24 * fw); + jz += 1; + q0 += 24; + iq[jz] = static_cast(fw); + } else { + iq[jz] = static_cast(z); + } + } + + fw = scalbn(one, q0); + for (int i = jz; i >= 0; i--) { + q[i] = fw * static_cast(iq[i]); + fw *= twon24; + } + + double fq[10]; + for (int i = jz; i >= 0; i--) { + fw = 0.0; + for (int k = 0; k <= jk && k <= jz - i; k++) fw += PIo2[k] * q[i + k]; + fq[jz - i] = fw; + } + + fw = 0.0; + for (int i = jz; i >= 0; i--) fw += fq[i]; + y[0] = (ih == 0) ? fw : -fw; + fw = fq[0] - fw; + for (int i = 1; i <= jz; i++) fw += fq[i]; + y[1] = (ih == 0) ? fw : -fw; + return n & 7; +} + + +int rempio2(double x, double* y) { + int32_t hx = static_cast(double_to_uint64(x) >> 32); + int32_t ix = hx & 0x7fffffff; + + if (ix >= 0x7ff00000) { + *y = base::OS::nan_value(); + return 0; + } + + int32_t e0 = (ix >> 20) - 1046; + uint64_t zi = double_to_uint64(x) & 0xFFFFFFFFu; + zi |= static_cast(ix - (e0 << 20)) << 32; + double z = uint64_to_double(zi); + + double tx[3]; + for (int i = 0; i < 2; i++) { + tx[i] = static_cast(static_cast(z)); + z = (z - tx[i]) * two24; + } + tx[2] = z; + + int nx = 3; + while (tx[nx - 1] == zero) nx--; + int n = __kernel_rem_pio2(tx, y, e0, nx); + if (hx < 0) { + y[0] = -y[0]; + y[1] = -y[1]; + return -n; + } + return n; +} +} +} // namespace v8::internal diff --git a/third_party/fdlibm/fdlibm.h b/third_party/fdlibm/fdlibm.h new file mode 100644 index 0000000..4ce7794 --- /dev/null +++ b/third_party/fdlibm/fdlibm.h @@ -0,0 +1,31 @@ +// The following is adapted from fdlibm (http://www.netlib.org/fdlibm). +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunSoft, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// The original source code covered by the above license above has been +// modified significantly by Google Inc. +// Copyright 2014 the V8 project authors. All rights reserved. + +#ifndef V8_FDLIBM_H_ +#define V8_FDLIBM_H_ + +namespace v8 { +namespace internal { + +int rempio2(double x, double* y); + +// Constants to be exposed to builtins via Float64Array. +struct TrigonometricConstants { + static const double constants[34]; +}; +} +} // namespace v8::internal + +#endif // V8_FDLIBM_H_ diff --git a/third_party/fdlibm/fdlibm.js b/third_party/fdlibm/fdlibm.js new file mode 100644 index 0000000..d5dbb72 --- /dev/null +++ b/third_party/fdlibm/fdlibm.js @@ -0,0 +1,356 @@ +// The following is adapted from fdlibm (http://www.netlib.org/fdlibm), +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunSoft, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// The original source code covered by the above license above has been +// modified significantly by Google Inc. +// Copyright 2014 the V8 project authors. All rights reserved. +// +// The following is a straightforward translation of fdlibm routines for +// sin, cos, and tan, by Raymond Toy (rtoy@google.com). + + +var kTrig; // Initialized to a Float64Array during genesis and is not writable. + +const INVPIO2 = kTrig[0]; +const PIO2_1 = kTrig[1]; +const PIO2_1T = kTrig[2]; +const PIO2_2 = kTrig[3]; +const PIO2_2T = kTrig[4]; +const PIO2_3 = kTrig[5]; +const PIO2_3T = kTrig[6]; +const PIO4 = kTrig[32]; +const PIO4LO = kTrig[33]; + +// Compute k and r such that x - k*pi/2 = r where |r| < pi/4. For +// precision, r is returned as two values y0 and y1 such that r = y0 + y1 +// to more than double precision. +macro REMPIO2(X) + var n, y0, y1; + var hx = %_DoubleHi(X); + var ix = hx & 0x7fffffff; + + if (ix < 0x4002d97c) { + // |X| ~< 3*pi/4, special case with n = +/- 1 + if (hx > 0) { + var z = X - PIO2_1; + if (ix != 0x3ff921fb) { + // 33+53 bit pi is good enough + y0 = z - PIO2_1T; + y1 = (z - y0) - PIO2_1T; + } else { + // near pi/2, use 33+33+53 bit pi + z -= PIO2_2; + y0 = z - PIO2_2T; + y1 = (z - y0) - PIO2_2T; + } + n = 1; + } else { + // Negative X + var z = X + PIO2_1; + if (ix != 0x3ff921fb) { + // 33+53 bit pi is good enough + y0 = z + PIO2_1T; + y1 = (z - y0) + PIO2_1T; + } else { + // near pi/2, use 33+33+53 bit pi + z += PIO2_2; + y0 = z + PIO2_2T; + y1 = (z - y0) + PIO2_2T; + } + n = -1; + } + } else if (ix <= 0x413921fb) { + // |X| ~<= 2^19*(pi/2), medium size + var t = MathAbs(X); + n = (t * INVPIO2 + 0.5) | 0; + var r = t - n * PIO2_1; + var w = n * PIO2_1T; + // First round good to 85 bit + y0 = r - w; + if (ix - (%_DoubleHi(y0) & 0x7ff00000) > 0x1000000) { + // 2nd iteration needed, good to 118 + t = r; + w = n * PIO2_2; + r = t - w; + w = n * PIO2_2T - ((t - r) - w); + y0 = r - w; + if (ix - (%_DoubleHi(y0) & 0x7ff00000) > 0x3100000) { + // 3rd iteration needed. 151 bits accuracy + t = r; + w = n * PIO2_3; + r = t - w; + w = n * PIO2_3T - ((t - r) - w); + y0 = r - w; + } + } + y1 = (r - y0) - w; + if (hx < 0) { + n = -n; + y0 = -y0; + y1 = -y1; + } + } else { + // Need to do full Payne-Hanek reduction here. + var r = %RemPiO2(X); + n = r[0]; + y0 = r[1]; + y1 = r[2]; + } +endmacro + + +// __kernel_sin(X, Y, IY) +// kernel sin function on [-pi/4, pi/4], pi/4 ~ 0.7854 +// Input X is assumed to be bounded by ~pi/4 in magnitude. +// Input Y is the tail of X so that x = X + Y. +// +// Algorithm +// 1. Since ieee_sin(-x) = -ieee_sin(x), we need only to consider positive x. +// 2. ieee_sin(x) is approximated by a polynomial of degree 13 on +// [0,pi/4] +// 3 13 +// sin(x) ~ x + S1*x + ... + S6*x +// where +// +// |ieee_sin(x) 2 4 6 8 10 12 | -58 +// |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2 +// | x | +// +// 3. ieee_sin(X+Y) = ieee_sin(X) + sin'(X')*Y +// ~ ieee_sin(X) + (1-X*X/2)*Y +// For better accuracy, let +// 3 2 2 2 2 +// r = X *(S2+X *(S3+X *(S4+X *(S5+X *S6)))) +// then 3 2 +// sin(x) = X + (S1*X + (X *(r-Y/2)+Y)) +// +macro KSIN(x) +kTrig[7+x] +endmacro + +macro RETURN_KERNELSIN(X, Y, SIGN) + var z = X * X; + var v = z * X; + var r = KSIN(1) + z * (KSIN(2) + z * (KSIN(3) + + z * (KSIN(4) + z * KSIN(5)))); + return (X - ((z * (0.5 * Y - v * r) - Y) - v * KSIN(0))) SIGN; +endmacro + +// __kernel_cos(X, Y) +// kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164 +// Input X is assumed to be bounded by ~pi/4 in magnitude. +// Input Y is the tail of X so that x = X + Y. +// +// Algorithm +// 1. Since ieee_cos(-x) = ieee_cos(x), we need only to consider positive x. +// 2. ieee_cos(x) is approximated by a polynomial of degree 14 on +// [0,pi/4] +// 4 14 +// cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x +// where the remez error is +// +// | 2 4 6 8 10 12 14 | -58 +// |ieee_cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2 +// | | +// +// 4 6 8 10 12 14 +// 3. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then +// ieee_cos(x) = 1 - x*x/2 + r +// since ieee_cos(X+Y) ~ ieee_cos(X) - ieee_sin(X)*Y +// ~ ieee_cos(X) - X*Y, +// a correction term is necessary in ieee_cos(x) and hence +// cos(X+Y) = 1 - (X*X/2 - (r - X*Y)) +// For better accuracy when x > 0.3, let qx = |x|/4 with +// the last 32 bits mask off, and if x > 0.78125, let qx = 0.28125. +// Then +// cos(X+Y) = (1-qx) - ((X*X/2-qx) - (r-X*Y)). +// Note that 1-qx and (X*X/2-qx) is EXACT here, and the +// magnitude of the latter is at least a quarter of X*X/2, +// thus, reducing the rounding error in the subtraction. +// +macro KCOS(x) +kTrig[13+x] +endmacro + +macro RETURN_KERNELCOS(X, Y, SIGN) + var ix = %_DoubleHi(X) & 0x7fffffff; + var z = X * X; + var r = z * (KCOS(0) + z * (KCOS(1) + z * (KCOS(2)+ + z * (KCOS(3) + z * (KCOS(4) + z * KCOS(5)))))); + if (ix < 0x3fd33333) { // |x| ~< 0.3 + return (1 - (0.5 * z - (z * r - X * Y))) SIGN; + } else { + var qx; + if (ix > 0x3fe90000) { // |x| > 0.78125 + qx = 0.28125; + } else { + qx = %_ConstructDouble(%_DoubleHi(0.25 * X), 0); + } + var hz = 0.5 * z - qx; + return (1 - qx - (hz - (z * r - X * Y))) SIGN; + } +endmacro + +// kernel tan function on [-pi/4, pi/4], pi/4 ~ 0.7854 +// Input x is assumed to be bounded by ~pi/4 in magnitude. +// Input y is the tail of x. +// Input k indicates whether ieee_tan (if k = 1) or -1/tan (if k = -1) +// is returned. +// +// Algorithm +// 1. Since ieee_tan(-x) = -ieee_tan(x), we need only to consider positive x. +// 2. if x < 2^-28 (hx<0x3e300000 0), return x with inexact if x!=0. +// 3. ieee_tan(x) is approximated by a odd polynomial of degree 27 on +// [0,0.67434] +// 3 27 +// tan(x) ~ x + T1*x + ... + T13*x +// where +// +// |ieee_tan(x) 2 4 26 | -59.2 +// |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2 +// | x | +// +// Note: ieee_tan(x+y) = ieee_tan(x) + tan'(x)*y +// ~ ieee_tan(x) + (1+x*x)*y +// Therefore, for better accuracy in computing ieee_tan(x+y), let +// 3 2 2 2 2 +// r = x *(T2+x *(T3+x *(...+x *(T12+x *T13)))) +// then +// 3 2 +// tan(x+y) = x + (T1*x + (x *(r+y)+y)) +// +// 4. For x in [0.67434,pi/4], let y = pi/4 - x, then +// tan(x) = ieee_tan(pi/4-y) = (1-ieee_tan(y))/(1+ieee_tan(y)) +// = 1 - 2*(ieee_tan(y) - (ieee_tan(y)^2)/(1+ieee_tan(y))) +// +// Set returnTan to 1 for tan; -1 for cot. Anything else is illegal +// and will cause incorrect results. +// +macro KTAN(x) +kTrig[19+x] +endmacro + +function KernelTan(x, y, returnTan) { + var z; + var w; + var hx = %_DoubleHi(x); + var ix = hx & 0x7fffffff; + + if (ix < 0x3e300000) { // |x| < 2^-28 + if (((ix | %_DoubleLo(x)) | (returnTan + 1)) == 0) { + // x == 0 && returnTan = -1 + return 1 / MathAbs(x); + } else { + if (returnTan == 1) { + return x; + } else { + // Compute -1/(x + y) carefully + var w = x + y; + var z = %_ConstructDouble(%_DoubleHi(w), 0); + var v = y - (z - x); + var a = -1 / w; + var t = %_ConstructDouble(%_DoubleHi(a), 0); + var s = 1 + t * z; + return t + a * (s + t * v); + } + } + } + if (ix >= 0x3fe59429) { // |x| > .6744 + if (x < 0) { + x = -x; + y = -y; + } + z = PIO4 - x; + w = PIO4LO - y; + x = z + w; + y = 0; + } + z = x * x; + w = z * z; + + // Break x^5 * (T1 + x^2*T2 + ...) into + // x^5 * (T1 + x^4*T3 + ... + x^20*T11) + + // x^5 * (x^2 * (T2 + x^4*T4 + ... + x^22*T12)) + var r = KTAN(1) + w * (KTAN(3) + w * (KTAN(5) + + w * (KTAN(7) + w * (KTAN(9) + w * KTAN(11))))); + var v = z * (KTAN(2) + w * (KTAN(4) + w * (KTAN(6) + + w * (KTAN(8) + w * (KTAN(10) + w * KTAN(12)))))); + var s = z * x; + r = y + z * (s * (r + v) + y); + r = r + KTAN(0) * s; + w = x + r; + if (ix >= 0x3fe59428) { + return (1 - ((hx >> 30) & 2)) * + (returnTan - 2.0 * (x - (w * w / (w + returnTan) - r))); + } + if (returnTan == 1) { + return w; + } else { + z = %_ConstructDouble(%_DoubleHi(w), 0); + v = r - (z - x); + var a = -1 / w; + var t = %_ConstructDouble(%_DoubleHi(a), 0); + s = 1 + t * z; + return t + a * (s + t * v); + } +} + +function MathSinSlow(x) { + REMPIO2(x); + var sign = 1 - (n & 2); + if (n & 1) { + RETURN_KERNELCOS(y0, y1, * sign); + } else { + RETURN_KERNELSIN(y0, y1, * sign); + } +} + +function MathCosSlow(x) { + REMPIO2(x); + if (n & 1) { + var sign = (n & 2) - 1; + RETURN_KERNELSIN(y0, y1, * sign); + } else { + var sign = 1 - (n & 2); + RETURN_KERNELCOS(y0, y1, * sign); + } +} + +// ECMA 262 - 15.8.2.16 +function MathSin(x) { + x = x * 1; // Convert to number. + if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) { + // |x| < pi/4, approximately. No reduction needed. + RETURN_KERNELSIN(x, 0, /* empty */); + } + return MathSinSlow(x); +} + +// ECMA 262 - 15.8.2.7 +function MathCos(x) { + x = x * 1; // Convert to number. + if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) { + // |x| < pi/4, approximately. No reduction needed. + RETURN_KERNELCOS(x, 0, /* empty */); + } + return MathCosSlow(x); +} + +// ECMA 262 - 15.8.2.18 +function MathTan(x) { + x = x * 1; // Convert to number. + if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) { + // |x| < pi/4, approximately. No reduction needed. + return KernelTan(x, 0, 1); + } + REMPIO2(x); + return KernelTan(y0, y1, (n & 1) ? -1 : 1); +} diff --git a/tools/generate-runtime-tests.py b/tools/generate-runtime-tests.py index c37ecca..7bf8736 100755 --- a/tools/generate-runtime-tests.py +++ b/tools/generate-runtime-tests.py @@ -47,11 +47,11 @@ EXPAND_MACROS = [ # that the parser doesn't bit-rot. Change the values as needed when you add, # remove or change runtime functions, but make sure we don't lose our ability # to parse them! -EXPECTED_FUNCTION_COUNT = 426 -EXPECTED_FUZZABLE_COUNT = 329 +EXPECTED_FUNCTION_COUNT = 427 +EXPECTED_FUZZABLE_COUNT = 330 EXPECTED_CCTEST_COUNT = 7 EXPECTED_UNKNOWN_COUNT = 16 -EXPECTED_BUILTINS_COUNT = 813 +EXPECTED_BUILTINS_COUNT = 809 # Don't call these at all. diff --git a/tools/generate-trig-table.py b/tools/generate-trig-table.py deleted file mode 100644 index 0a4fe28..0000000 --- a/tools/generate-trig-table.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2013 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. - -# This is a utility for populating the lookup table for the -# approximation of trigonometric functions. - -import sys, math - -SAMPLES = 1800 - -TEMPLATE = """\ -// Copyright 2013 Google Inc. All Rights Reserved. - -// This file was generated from a python script. - -#include "src/v8.h" -#include "src/trig-table.h" - -namespace v8 { -namespace internal { - - const double TrigonometricLookupTable::kSinTable[] = - { %(sine_table)s }; - const double TrigonometricLookupTable::kCosXIntervalTable[] = - { %(cosine_table)s }; - const int TrigonometricLookupTable::kSamples = %(samples)i; - const int TrigonometricLookupTable::kTableSize = %(table_size)i; - const double TrigonometricLookupTable::kSamplesOverPiHalf = - %(samples_over_pi_half)s; - -} } // v8::internal -""" - -def main(): - pi_half = math.pi / 2 - interval = pi_half / SAMPLES - sin = [] - cos_times_interval = [] - table_size = SAMPLES + 2 - - for i in range(0, table_size): - sample = i * interval - sin.append(repr(math.sin(sample))) - cos_times_interval.append(repr(math.cos(sample) * interval)) - - output_file = sys.argv[1] - output = open(str(output_file), "w") - output.write(TEMPLATE % { - 'sine_table': ','.join(sin), - 'cosine_table': ','.join(cos_times_interval), - 'samples': SAMPLES, - 'table_size': table_size, - 'samples_over_pi_half': repr(SAMPLES / pi_half) - }) - -if __name__ == "__main__": - main() diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 097847e..967497b 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -121,14 +121,12 @@ 'dependencies': [ 'mksnapshot#host', 'js2c#host', - 'generate_trig_table#host', ], }, { 'toolsets': ['target'], 'dependencies': [ 'mksnapshot', 'js2c', - 'generate_trig_table', ], }], ['component=="shared_library"', { @@ -153,7 +151,6 @@ 'sources': [ '<(SHARED_INTERMEDIATE_DIR)/libraries.cc', '<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc', - '<(SHARED_INTERMEDIATE_DIR)/trig-table.cc', '<(INTERMEDIATE_DIR)/snapshot.cc', '../../src/snapshot-common.cc', ], @@ -197,17 +194,16 @@ 'sources': [ '<(SHARED_INTERMEDIATE_DIR)/libraries.cc', '<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc', - '<(SHARED_INTERMEDIATE_DIR)/trig-table.cc', '../../src/snapshot-common.cc', '../../src/snapshot-empty.cc', ], 'conditions': [ ['want_separate_host_toolset==1', { 'toolsets': ['host', 'target'], - 'dependencies': ['js2c#host', 'generate_trig_table#host'], + 'dependencies': ['js2c#host'], }, { 'toolsets': ['target'], - 'dependencies': ['js2c', 'generate_trig_table'], + 'dependencies': ['js2c'], }], ['component=="shared_library"', { 'defines': [ @@ -226,14 +222,12 @@ 'dependencies': [ 'mksnapshot#host', 'js2c#host', - 'generate_trig_table#host', 'natives_blob#host', ]}, { 'toolsets': ['target'], 'dependencies': [ 'mksnapshot', 'js2c', - 'generate_trig_table', 'natives_blob', ], }], @@ -257,7 +251,6 @@ '../..', ], 'sources': [ - '<(SHARED_INTERMEDIATE_DIR)/trig-table.cc', '../../src/natives-external.cc', '../../src/snapshot-external.cc', ], @@ -291,32 +284,6 @@ }, ], }, - { 'target_name': 'generate_trig_table', - 'type': 'none', - 'conditions': [ - ['want_separate_host_toolset==1', { - 'toolsets': ['host'], - }, { - 'toolsets': ['target'], - }], - ], - 'actions': [ - { - 'action_name': 'generate', - 'inputs': [ - '../../tools/generate-trig-table.py', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/trig-table.cc', - ], - 'action': [ - 'python', - '../../tools/generate-trig-table.py', - '<@(_outputs)', - ], - }, - ] - }, { 'target_name': 'v8_base', 'type': 'static_library', @@ -768,6 +735,8 @@ '../../src/zone-inl.h', '../../src/zone.cc', '../../src/zone.h', + '../../third_party/fdlibm/fdlibm.cc', + '../../third_party/fdlibm/fdlibm.h', ], 'conditions': [ ['want_separate_host_toolset==1', { @@ -1420,6 +1389,7 @@ '../../src/array.js', '../../src/string.js', '../../src/uri.js', + '../../third_party/fdlibm/fdlibm.js', '../../src/math.js', '../../src/messages.js', '../../src/apinatives.js', diff --git a/tools/js2c.py b/tools/js2c.py index 60f088b..77485f6 100755 --- a/tools/js2c.py +++ b/tools/js2c.py @@ -218,6 +218,27 @@ def ExpandInlineMacros(lines): lines = ExpandMacroDefinition(lines, pos, name_pattern, macro, non_expander) +INLINE_CONSTANT_PATTERN = re.compile(r'const\s+([a-zA-Z0-9_]+)\s*=\s*([^;\n]+)[;\n]') + +def ExpandInlineConstants(lines): + pos = 0 + while True: + const_match = INLINE_CONSTANT_PATTERN.search(lines, pos) + if const_match is None: + # no more constants + return lines + name = const_match.group(1) + replacement = const_match.group(2) + name_pattern = re.compile("\\b%s\\b" % name) + + # remove constant definition and replace + lines = (lines[:const_match.start()] + + re.sub(name_pattern, replacement, lines[const_match.end():])) + + # advance position to where the constant defintion was + pos = const_match.start() + + HEADER_TEMPLATE = """\ // Copyright 2011 Google Inc. All Rights Reserved. @@ -333,6 +354,7 @@ def BuildFilterChain(macro_filename): filter_chain.extend([ RemoveCommentsAndTrailingWhitespace, ExpandInlineMacros, + ExpandInlineConstants, Validate, jsmin.JavaScriptMinifier().JSMinify ])