Commit 225a6a82 authored by ager@chromium.org's avatar ager@chromium.org

Optimize Math.sin and Math.cos by avoiding runtime calls.

Review URL: http://codereview.chromium.org/125121

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2166 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 0fcf27bc
......@@ -39,6 +39,16 @@ namespace internal {
void DeferredCode::Jump() { __ jmp(&entry_label_); }
void DeferredCode::Branch(Condition cc) { __ b(cc, &entry_label_); }
void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
GenerateFastMathOp(SIN, args);
}
void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
GenerateFastMathOp(COS, args);
}
#undef __
} } // namespace v8::internal
......
......@@ -3414,6 +3414,21 @@ void CodeGenerator::GenerateRandomPositiveSmi(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) {
VirtualFrame::SpilledScope spilled_scope;
Load(args->at(0));
switch (op) {
case SIN:
__ CallRuntime(Runtime::kMath_sin, 1);
break;
case COS:
__ CallRuntime(Runtime::kMath_cos, 1);
break;
}
frame_->EmitPush(r0);
}
void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
VirtualFrame::SpilledScope spilled_scope;
ASSERT(args->length() == 2);
......
......@@ -352,6 +352,12 @@ class CodeGenerator: public AstVisitor {
// Fast support for Math.random().
void GenerateRandomPositiveSmi(ZoneList<Expression*>* args);
// Fast support for Math.sin and Math.cos.
enum MathOp { SIN, COS };
void GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args);
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args);
// Methods and constants for fast case switch statement support.
//
// Only allow fast-case switch if the range of labels is at most
......
......@@ -423,7 +423,9 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
{&CodeGenerator::GenerateFastCharCodeAt, "_FastCharCodeAt"},
{&CodeGenerator::GenerateObjectEquals, "_ObjectEquals"},
{&CodeGenerator::GenerateLog, "_Log"},
{&CodeGenerator::GenerateRandomPositiveSmi, "_RandomPositiveSmi"}
{&CodeGenerator::GenerateRandomPositiveSmi, "_RandomPositiveSmi"},
{&CodeGenerator::GenerateMathSin, "_Math_sin"},
{&CodeGenerator::GenerateMathCos, "_Math_cos"}
};
......
......@@ -1656,6 +1656,22 @@ void Assembler::fchs() {
}
void Assembler::fcos() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xFF);
}
void Assembler::fsin() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xFE);
}
void Assembler::fadd(int i) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
......
......@@ -658,6 +658,8 @@ class Assembler : public Malloced {
void fabs();
void fchs();
void fcos();
void fsin();
void fadd(int i);
void fsub(int i);
......
......@@ -39,6 +39,16 @@ namespace internal {
void DeferredCode::Jump() { __ jmp(&entry_label_); }
void DeferredCode::Branch(Condition cc) { __ j(cc, &entry_label_); }
void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
GenerateFastMathOp(SIN, args);
}
void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
GenerateFastMathOp(COS, args);
}
#undef __
} } // namespace v8::internal
......
......@@ -714,6 +714,11 @@ void CodeGenerator::ToBoolean(ControlDestination* dest) {
class FloatingPointHelper : public AllStatic {
public:
// Code pattern for loading a floating point value. Input value must
// be either a smi or a heap number object (fp value). Requirements:
// operand on TOS+1. Returns operand as floating point number on FPU
// stack.
static void LoadFloatOperand(MacroAssembler* masm, Register scratch);
// Code pattern for loading floating point values. Input values must
// be either smi or heap number objects (fp values). Requirements:
// operand_1 on TOS+1 , operand_2 on TOS+2; Returns operands as
......@@ -730,7 +735,8 @@ class FloatingPointHelper : public AllStatic {
static void AllocateHeapNumber(MacroAssembler* masm,
Label* need_gc,
Register scratch1,
Register scratch2);
Register scratch2,
Register result);
};
......@@ -4943,6 +4949,76 @@ void CodeGenerator::GenerateRandomPositiveSmi(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) {
JumpTarget done;
JumpTarget call_runtime;
ASSERT(args->length() == 1);
// Load number and duplicate it.
Load(args->at(0));
frame_->Dup();
// Get the number into an unaliased register and load it onto the
// floating point stack still leaving one copy on the frame.
Result number = frame_->Pop();
number.ToRegister();
frame_->Spill(number.reg());
FloatingPointHelper::LoadFloatOperand(masm_, number.reg());
number.Unuse();
// Perform the operation on the number.
switch (op) {
case SIN:
__ fsin();
break;
case COS:
__ fcos();
break;
}
// Go slow case if argument to operation is out of range.
__ fnstsw_ax();
__ sahf();
call_runtime.Branch(parity_even, not_taken);
// Allocate heap number for result if possible.
Result scratch1 = allocator()->Allocate();
Result scratch2 = allocator()->Allocate();
Result heap_number = allocator()->Allocate();
FloatingPointHelper::AllocateHeapNumber(masm_,
call_runtime.entry_label(),
scratch1.reg(),
scratch2.reg(),
heap_number.reg());
scratch1.Unuse();
scratch2.Unuse();
// Store the result in the allocated heap number.
__ fstp_d(FieldOperand(heap_number.reg(), HeapNumber::kValueOffset));
// Pop the extra copy of the argument.
frame_->Pop();
// Push the result on the frame.
frame_->Push(&heap_number);
heap_number.Unuse();
done.Jump();
call_runtime.Bind();
// Free ST(0) which was not popped before calling into the runtime.
__ ffree(0);
Result answer;
switch (op) {
case SIN:
answer = frame_->CallRuntime(Runtime::kMath_sin, 1);
break;
case COS:
answer = frame_->CallRuntime(Runtime::kMath_cos, 1);
break;
}
frame_->Push(&answer);
done.Bind();
}
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
if (CheckForInlineRuntimeCall(node)) {
return;
......@@ -6446,7 +6522,8 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
FloatingPointHelper::AllocateHeapNumber(masm,
&call_runtime,
ecx,
edx);
edx,
eax);
__ bind(&skip_allocation);
break;
default: UNREACHABLE();
......@@ -6554,7 +6631,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
// Fall through!
case NO_OVERWRITE:
FloatingPointHelper::AllocateHeapNumber(masm, &call_runtime,
ecx, edx);
ecx, edx, eax);
__ bind(&skip_allocation);
break;
default: UNREACHABLE();
......@@ -6639,22 +6716,42 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
void FloatingPointHelper::AllocateHeapNumber(MacroAssembler* masm,
Label* need_gc,
Register scratch1,
Register scratch2) {
Register scratch2,
Register result) {
ExternalReference allocation_top =
ExternalReference::new_space_allocation_top_address();
ExternalReference allocation_limit =
ExternalReference::new_space_allocation_limit_address();
__ mov(Operand(scratch1), Immediate(allocation_top));
__ mov(eax, Operand(scratch1, 0));
__ lea(scratch2, Operand(eax, HeapNumber::kSize)); // scratch2: new top
__ mov(result, Operand(scratch1, 0));
__ lea(scratch2, Operand(result, HeapNumber::kSize)); // scratch2: new top
__ cmp(scratch2, Operand::StaticVariable(allocation_limit));
__ j(above, need_gc, not_taken);
__ mov(Operand(scratch1, 0), scratch2); // store new top
__ mov(Operand(eax, HeapObject::kMapOffset),
__ mov(Operand(result, HeapObject::kMapOffset),
Immediate(Factory::heap_number_map()));
// Tag old top and use as result.
__ add(Operand(eax), Immediate(kHeapObjectTag));
__ add(Operand(result), Immediate(kHeapObjectTag));
}
void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
Register scratch) {
Label load_smi, done;
__ test(scratch, Immediate(kSmiTagMask));
__ j(zero, &load_smi, not_taken);
__ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
__ jmp(&done);
__ bind(&load_smi);
__ sar(scratch, kSmiTagSize);
__ push(scratch);
__ fild_s(Operand(esp, 0));
__ pop(scratch);
__ bind(&done);
}
......@@ -6763,7 +6860,7 @@ void UnarySubStub::Generate(MacroAssembler* masm) {
} else {
__ mov(edx, Operand(eax));
// edx: operand
FloatingPointHelper::AllocateHeapNumber(masm, &undo, ebx, ecx);
FloatingPointHelper::AllocateHeapNumber(masm, &undo, ebx, ecx, eax);
// eax: allocated 'empty' number
__ mov(ecx, FieldOperand(edx, HeapNumber::kExponentOffset));
__ xor_(ecx, HeapNumber::kSignMask); // Flip sign.
......
......@@ -521,6 +521,12 @@ class CodeGenerator: public AstVisitor {
// Fast support for Math.random().
void GenerateRandomPositiveSmi(ZoneList<Expression*>* args);
// Fast support for Math.sin and Math.cos.
enum MathOp { SIN, COS };
void GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args);
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args);
// Methods and constants for fast case switch statement support.
//
// Only allow fast-case switch if the range of labels is at most
......
......@@ -83,7 +83,7 @@ function MathCeil(x) {
// ECMA 262 - 15.8.2.7
function MathCos(x) {
if (!IS_NUMBER(x)) x = ToNumber(x);
return %Math_cos(x);
return %_Math_cos(x);
}
// ECMA 262 - 15.8.2.8
......@@ -157,7 +157,7 @@ function MathRound(x) {
// ECMA 262 - 15.8.2.16
function MathSin(x) {
if (!IS_NUMBER(x)) x = ToNumber(x);
return %Math_sin(x);
return %_Math_sin(x);
}
// ECMA 262 - 15.8.2.17
......
......@@ -37,6 +37,17 @@ namespace internal {
void DeferredCode::Jump() { UNIMPLEMENTED(); }
void DeferredCode::Branch(Condition cc) { UNIMPLEMENTED(); }
void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
GenerateFastMathOp(SIN, args);
}
void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
GenerateFastMathOp(COS, args);
}
} } // namespace v8::internal
#endif // V8_X64_CODEGEN_X64_INL_H_
......@@ -377,6 +377,10 @@ void CodeGenerator::GenerateRandomPositiveSmi(ZoneList<Expression*>* a) {
UNIMPLEMENTED();
}
void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) {
UNIMPLEMENTED();
}
void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* a) {
UNIMPLEMENTED();
}
......
......@@ -527,6 +527,12 @@ class CodeGenerator: public AstVisitor {
// Fast support for Math.random().
void GenerateRandomPositiveSmi(ZoneList<Expression*>* args);
// Fast support for Math.sin and Math.cos.
enum MathOp { SIN, COS };
void GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args);
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args);
// Methods and constants for fast case switch statement support.
//
// Only allow fast-case switch if the range of labels is at most
......
// Copyright 2009 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.
// Test Math.sin and Math.cos.
var input_sin = [0, Math.PI, Math.PI / 6, Math.PI / 2, Math.pow(2, 70)];
var input_cos = [0, Math.PI, Math.PI / 6, Math.PI / 2, Math.pow(2, 70)];
var output_sin = input_sin.map(Math.sin);
var output_cos = input_sin.map(Math.cos);
var expected_sin = [0,
1.2246063538223773e-16,
0.49999999999999994,
1,
-0.9983193022079332];
var expected_cos = [1,
-1,
0.8660254037844387,
6.123031769111886e-17,
0.05795317798935058];
assertArrayEquals(expected_sin, output_sin, "sine");
assertArrayEquals(expected_cos, output_cos, "cosine");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment