Reland: Embed trigonometric lookup table.

R=danno@chromium.org

Review URL: https://codereview.chromium.org/78263005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17988 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yangguo@chromium.org 2013-11-22 08:25:50 +00:00
parent 870c32e4b1
commit a7d38f7a41
8 changed files with 287 additions and 144 deletions

View File

@ -40,6 +40,7 @@
#include "objects-visiting.h"
#include "platform.h"
#include "snapshot.h"
#include "trig-table.h"
#include "extensions/externalize-string-extension.h"
#include "extensions/gc-extension.h"
#include "extensions/statistics-extension.h"
@ -2635,6 +2636,44 @@ Genesis::Genesis(Isolate* isolate,
InitializeExperimentalGlobal();
if (!InstallExperimentalNatives()) return;
if (!Serializer::enabled()) {
Handle<JSBuiltinsObject> builtins(native_context()->builtins());
// Initialize trigonometric lookup tables and constants.
// The snapshot cannot contain typed arrays, and we don't need it to.
const int table_num_bytes = TrigonometricLookupTable::table_num_bytes();
v8::Local<v8::ArrayBuffer> sin_buffer = v8::ArrayBuffer::New(
TrigonometricLookupTable::sin_table(), table_num_bytes);
v8::Local<v8::ArrayBuffer> cos_buffer = v8::ArrayBuffer::New(
TrigonometricLookupTable::cos_x_interval_table(), table_num_bytes);
v8::Local<v8::Float64Array> sin_table = v8::Float64Array::New(
sin_buffer, 0, TrigonometricLookupTable::table_size());
v8::Local<v8::Float64Array> cos_table = v8::Float64Array::New(
cos_buffer, 0, TrigonometricLookupTable::table_size());
ForceSetProperty(builtins,
factory()->InternalizeOneByteString(
STATIC_ASCII_VECTOR("kSinTable")),
Utils::OpenHandle(*sin_table),
NONE);
ForceSetProperty(builtins,
factory()->InternalizeOneByteString(
STATIC_ASCII_VECTOR("kCosXIntervalTable")),
Utils::OpenHandle(*cos_table),
NONE);
ForceSetProperty(builtins,
factory()->InternalizeOneByteString(
STATIC_ASCII_VECTOR("kSamples")),
factory()->NewHeapNumber(
TrigonometricLookupTable::samples()),
NONE);
ForceSetProperty(builtins,
factory()->InternalizeOneByteString(
STATIC_ASCII_VECTOR("kIndexConvert")),
factory()->NewHeapNumber(
TrigonometricLookupTable::samples_over_pi_half()),
NONE);
}
// Initially seed the per-context random number generator
// using the per-isolate random number generator.
uint32_t* state = reinterpret_cast<uint32_t*>(

View File

@ -79,7 +79,8 @@ function MathCeil(x) {
// ECMA 262 - 15.8.2.7
function MathCos(x) {
return MathCosImpl(x);
x = MathAbs(x); // Convert to number and get rid of -0.
return TrigonometricInterpolation(x, 1);
}
// ECMA 262 - 15.8.2.8
@ -179,7 +180,9 @@ function MathRound(x) {
// ECMA 262 - 15.8.2.16
function MathSin(x) {
return MathSinImpl(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
@ -189,7 +192,7 @@ function MathSqrt(x) {
// ECMA 262 - 15.8.2.18
function MathTan(x) {
return MathSinImpl(x) / MathCosImpl(x);
return MathSin(x) / MathCos(x);
}
// Non-standard extension.
@ -198,119 +201,73 @@ function MathImul(x, y) {
}
var MathSinImpl = function(x) {
InitTrigonometricFunctions();
return MathSinImpl(x);
}
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.
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 = 1800; // Table size. Do not change arbitrarily.
var inverse_pi_half = 0.636619772367581343; // 2 / pi
var inverse_pi_half_s_26 = 9.48637384723993156e-9; // 2 / pi / (2^26)
var s_26 = 1 << 26;
var two_step_threshold = 1 << 27;
var index_convert = 1145.915590261646418; // samples / (pi / 2)
// pi / 2 rounded up
var pi_half = 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 pi_half_1 = 1.570796325802803040; // 0x00000054fb21f93f
var pi_half_2 = 9.920935796805404252e-10; // 0x3326a611460b113e
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.
// 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.
var Interpolation = function(x, phase) {
if (x < 0 || x > pi_half) {
var multiple;
while (x < -two_step_threshold || x > two_step_threshold) {
// 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 * inverse_pi_half_s_26) * s_26;
x = x - multiple * pi_half_1 - multiple * pi_half_2;
}
multiple = MathFloor(x * inverse_pi_half);
x = x - multiple * pi_half_1 - multiple * pi_half_2;
phase += multiple;
// 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;
}
var double_index = x * index_convert;
if (phase & 1) double_index = samples - double_index;
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))
* (1 - (phase & 2)) + 0;
}
var MathSinInterpolation = function(x) {
x = x * 1; // Convert to number and deal with -0.
if (%_IsMinusZero(x)) return x;
return Interpolation(x, 0);
}
// Cosine is sine with a phase offset.
var MathCosInterpolation = function(x) {
x = MathAbs(x); // Convert to number and get rid of -0.
return Interpolation(x, 1);
};
%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;
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;
}
SetupTrigonometricFunctions();
// -------------------------------------------------------------------
function SetUpMath() {
@ -387,6 +344,7 @@ function SetUpMath() {
%SetInlineBuiltinFlag(MathSin);
%SetInlineBuiltinFlag(MathCos);
%SetInlineBuiltinFlag(MathTan);
%SetInlineBuiltinFlag(TrigonometricInterpolation);
}
SetUpMath();

View File

@ -7848,35 +7848,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_tan) {
}
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) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 2);

View File

@ -190,7 +190,6 @@ namespace internal {
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) \

61
src/trig-table.h Normal file
View File

@ -0,0 +1,61 @@
// 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.
#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<double*>(&kSinTable[0]);
}
static void* cos_x_interval_table() {
return const_cast<double*>(&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_

View File

@ -89,7 +89,7 @@ TEST(WeakArrayBuffersFromApi) {
LocalContext context;
Isolate* isolate = GetIsolateFrom(&context);
CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap()));
int start = CountArrayBuffersInWeakList(isolate->heap());
{
v8::HandleScope s1(context->GetIsolate());
v8::Handle<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::New(256);
@ -99,12 +99,12 @@ TEST(WeakArrayBuffersFromApi) {
Handle<JSArrayBuffer> iab1 = v8::Utils::OpenHandle(*ab1);
Handle<JSArrayBuffer> iab2 = v8::Utils::OpenHandle(*ab2);
CHECK_EQ(2, CountArrayBuffersInWeakList(isolate->heap()));
CHECK_EQ(2, CountArrayBuffersInWeakList(isolate->heap()) - start);
CHECK(HasArrayBufferInWeakList(isolate->heap(), *iab1));
CHECK(HasArrayBufferInWeakList(isolate->heap(), *iab2));
}
isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()));
CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()) - start);
{
HandleScope scope2(isolate);
Handle<JSArrayBuffer> iab1 = v8::Utils::OpenHandle(*ab1);
@ -114,7 +114,7 @@ TEST(WeakArrayBuffersFromApi) {
}
isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap()));
CHECK_EQ(start, CountArrayBuffersInWeakList(isolate->heap()));
}

View File

@ -0,0 +1,84 @@
#!/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 "v8.h"
#include "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()

View File

@ -140,6 +140,7 @@
'sources': [
'<(SHARED_INTERMEDIATE_DIR)/libraries.cc',
'<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc',
'<(SHARED_INTERMEDIATE_DIR)/trig-table.cc',
'<(INTERMEDIATE_DIR)/snapshot.cc',
],
'actions': [
@ -182,6 +183,7 @@
'sources': [
'<(SHARED_INTERMEDIATE_DIR)/libraries.cc',
'<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc',
'<(SHARED_INTERMEDIATE_DIR)/trig-table.cc',
'../../src/snapshot-empty.cc',
],
'conditions': [
@ -200,9 +202,38 @@
}],
]
},
{ 'target_name': 'generate_trig_table',
'type': 'none',
'conditions': [
['want_separate_host_toolset==1', {
'toolsets': ['host', 'target'],
}, {
'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.<(v8_target_arch)',
'type': 'static_library',
'dependencies': [
'generate_trig_table',
],
'variables': {
'optimize': 'max',
},