Implement Math.sin, cos and tan using table lookup and spline interpolation.
R=jkummerow@chromium.org BUG= Review URL: https://codereview.chromium.org/50563003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17594 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
412af94bbb
commit
063b7c4ebb
@ -3726,42 +3726,6 @@ void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
// Load the argument on the stack and call the stub.
|
||||
TranscendentalCacheStub stub(TranscendentalCache::LOG,
|
||||
@ -3784,6 +3748,16 @@ 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_floor, 1);
|
||||
context()->Plug(r0);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
ASSERT(args->length() >= 2);
|
||||
|
@ -6353,6 +6353,11 @@ int HOptimizedGraphBuilder::InliningAstSize(Handle<JSFunction> target) {
|
||||
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.
|
||||
if (target_shared->SourceSize() >
|
||||
@ -6362,7 +6367,7 @@ int HOptimizedGraphBuilder::InliningAstSize(Handle<JSFunction> target) {
|
||||
}
|
||||
|
||||
// Target must be inlineable.
|
||||
if (!target->IsInlineable()) {
|
||||
if (!target_shared->IsInlineable()) {
|
||||
TraceInline(target, caller, "target not inlineable");
|
||||
return kNotInlinable;
|
||||
}
|
||||
@ -6742,9 +6747,6 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr,
|
||||
case kMathAbs:
|
||||
case kMathSqrt:
|
||||
case kMathLog:
|
||||
case kMathSin:
|
||||
case kMathCos:
|
||||
case kMathTan:
|
||||
if (expr->arguments()->length() == 1) {
|
||||
HValue* argument = Pop();
|
||||
Drop(1); // Receiver.
|
||||
@ -6823,9 +6825,6 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
||||
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();
|
||||
@ -9145,36 +9144,6 @@ void HOptimizedGraphBuilder::GenerateMathPow(CallRuntime* call) {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
ASSERT_EQ(1, call->arguments()->length());
|
||||
CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
||||
@ -9194,6 +9163,15 @@ void HOptimizedGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
|
||||
}
|
||||
|
||||
|
||||
void HOptimizedGraphBuilder::GenerateMathFloor(CallRuntime* call) {
|
||||
ASSERT(call->arguments()->length() == 1);
|
||||
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
||||
HValue* value = Pop();
|
||||
HInstruction* result = New<HUnaryMathOperation>(value, kMathFloor);
|
||||
return ast_context()->ReturnInstruction(result, call->id());
|
||||
}
|
||||
|
||||
|
||||
// Check whether two RegExps are equivalent
|
||||
void HOptimizedGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
|
||||
return Bailout(kInlinedRuntimeFunctionIsRegExpEquivalent);
|
||||
|
@ -3693,42 +3693,6 @@ void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
// Load the argument on the stack and call the stub.
|
||||
TranscendentalCacheStub stub(TranscendentalCache::LOG,
|
||||
@ -3751,6 +3715,16 @@ 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_floor, 1);
|
||||
context()->Plug(eax);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
ASSERT(args->length() >= 2);
|
||||
|
72
src/math.js
72
src/math.js
@ -79,7 +79,7 @@ function MathCeil(x) {
|
||||
|
||||
// ECMA 262 - 15.8.2.7
|
||||
function MathCos(x) {
|
||||
return %_MathCos(TO_NUMBER_INLINE(x));
|
||||
return MathCosImpl(x);
|
||||
}
|
||||
|
||||
// ECMA 262 - 15.8.2.8
|
||||
@ -185,7 +185,7 @@ function MathRound(x) {
|
||||
|
||||
// ECMA 262 - 15.8.2.16
|
||||
function MathSin(x) {
|
||||
return %_MathSin(TO_NUMBER_INLINE(x));
|
||||
return MathSinImpl(x);
|
||||
}
|
||||
|
||||
// ECMA 262 - 15.8.2.17
|
||||
@ -195,7 +195,7 @@ function MathSqrt(x) {
|
||||
|
||||
// ECMA 262 - 15.8.2.18
|
||||
function MathTan(x) {
|
||||
return %_MathTan(TO_NUMBER_INLINE(x));
|
||||
return MathSinImpl(x) / MathCosImpl(x);
|
||||
}
|
||||
|
||||
// Non-standard extension.
|
||||
@ -204,6 +204,68 @@ function MathImul(x, y) {
|
||||
}
|
||||
|
||||
|
||||
var MathSinImpl = function(x) {
|
||||
InitTrigonometricFunctions();
|
||||
return MathSinImpl(x);
|
||||
}
|
||||
|
||||
|
||||
var MathCosImpl = function(x) {
|
||||
InitTrigonometricFunctions();
|
||||
return MathCosImpl(x);
|
||||
}
|
||||
|
||||
|
||||
function InitTrigonometricFunctions() {
|
||||
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 = new global.Float64Array(samples + 2);
|
||||
var table_cos_interval = new global.Float64Array(samples + 2);
|
||||
|
||||
%PopulateTrigonometricTable(table_sin, table_cos_interval, samples);
|
||||
|
||||
// This implements 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.
|
||||
MathSinImpl = function(x) {
|
||||
var multiple = %_MathFloor(x * inverse_pi_half);
|
||||
x = (multiple & 1) * pi_half +
|
||||
(1 - ((multiple & 1) << 1)) * (x - multiple * pi_half);
|
||||
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)) *
|
||||
(1 - (multiple & 2)) + 0;
|
||||
};
|
||||
|
||||
MathCosImpl = function(x) {
|
||||
return MathSinImpl(x + pi_half);
|
||||
};
|
||||
|
||||
%SetInlineBuiltinFlag(MathSinImpl);
|
||||
%SetInlineBuiltinFlag(MathCosImpl);
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
function SetUpMath() {
|
||||
@ -276,6 +338,10 @@ function SetUpMath() {
|
||||
"min", MathMin,
|
||||
"imul", MathImul
|
||||
));
|
||||
|
||||
%SetInlineBuiltinFlag(MathSin);
|
||||
%SetInlineBuiltinFlag(MathCos);
|
||||
%SetInlineBuiltinFlag(MathTan);
|
||||
}
|
||||
|
||||
SetUpMath();
|
||||
|
@ -3762,45 +3762,6 @@ void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) {
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
__ mov(a0, result_register()); // Stub requires parameter in a0 and on tos.
|
||||
__ CallStub(&stub);
|
||||
context()->Plug(v0);
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
__ mov(a0, result_register()); // Stub requires parameter in a0 and on tos.
|
||||
__ CallStub(&stub);
|
||||
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,
|
||||
@ -3824,6 +3785,16 @@ 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_floor, 1);
|
||||
context()->Plug(v0);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
ASSERT(args->length() >= 2);
|
||||
|
@ -4807,6 +4807,8 @@ bool SharedFunctionInfo::is_classic_mode() {
|
||||
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 @@ Code* SharedFunctionInfo::code() {
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -9747,20 +9747,6 @@ bool JSFunction::EnsureCompiled(Handle<JSFunction> function,
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
if (object->IsGlobalObject()) return;
|
||||
|
||||
@ -10029,6 +10015,16 @@ Handle<Object> SharedFunctionInfo::GetSourceCode() {
|
||||
}
|
||||
|
||||
|
||||
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() {
|
||||
return end_position() - start_position();
|
||||
}
|
||||
|
@ -6781,6 +6781,9 @@ class SharedFunctionInfo: public HeapObject {
|
||||
// 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
|
||||
@ -6870,6 +6873,9 @@ class SharedFunctionInfo: public HeapObject {
|
||||
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 @@ class SharedFunctionInfo: public HeapObject {
|
||||
kUsesArguments,
|
||||
kHasDuplicateParameters,
|
||||
kNative,
|
||||
kInlineBuiltin,
|
||||
kBoundFunction,
|
||||
kIsAnonymous,
|
||||
kNameShouldPrintAsAnonymous,
|
||||
@ -7246,9 +7253,6 @@ class JSFunction: public JSObject {
|
||||
// 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.
|
||||
//
|
||||
|
@ -5379,6 +5379,20 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetNativeFlag) {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
HandleScope scope(isolate);
|
||||
RUNTIME_ASSERT(args.length() == 5);
|
||||
@ -7802,6 +7816,35 @@ 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);
|
||||
|
@ -106,6 +106,7 @@ namespace internal {
|
||||
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 @@ 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) \
|
||||
@ -625,11 +627,9 @@ namespace internal {
|
||||
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(MathFloor, 1, 1) \
|
||||
F(IsRegExpEquivalent, 2, 1) \
|
||||
F(HasCachedArrayIndex, 1, 1) \
|
||||
F(GetCachedArrayIndex, 1, 1) \
|
||||
|
@ -3650,42 +3650,6 @@ void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
// Load the argument on the stack and call the stub.
|
||||
TranscendentalCacheStub stub(TranscendentalCache::LOG,
|
||||
@ -3708,6 +3672,16 @@ 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_floor, 1);
|
||||
context()->Plug(rax);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
ASSERT(args->length() >= 2);
|
||||
|
@ -42,9 +42,101 @@ cosTest();
|
||||
|
||||
// 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"));
|
||||
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)));
|
||||
|
Loading…
Reference in New Issue
Block a user