From: ager@chromium.org Date: Mon, 15 Jun 2009 12:06:48 +0000 (+0000) Subject: Optimize Math.sin and Math.cos by avoiding runtime calls. X-Git-Tag: upstream/4.7.83~23911 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=225a6a82b007dea0b400e30ba27ce0da5095aa7b;p=platform%2Fupstream%2Fv8.git 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 --- diff --git a/src/arm/codegen-arm-inl.h b/src/arm/codegen-arm-inl.h index 544331a52..5a29a4500 100644 --- a/src/arm/codegen-arm-inl.h +++ b/src/arm/codegen-arm-inl.h @@ -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* args) { + GenerateFastMathOp(SIN, args); +} + + +void CodeGenerator::GenerateMathCos(ZoneList* args) { + GenerateFastMathOp(COS, args); +} + + #undef __ } } // namespace v8::internal diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index c8fd287a4..9b45bfedb 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -3414,6 +3414,21 @@ void CodeGenerator::GenerateRandomPositiveSmi(ZoneList* args) { } +void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList* 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* args) { VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 2); diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 4dfbdabed..0df793a4a 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -352,6 +352,12 @@ class CodeGenerator: public AstVisitor { // Fast support for Math.random(). void GenerateRandomPositiveSmi(ZoneList* args); + // Fast support for Math.sin and Math.cos. + enum MathOp { SIN, COS }; + void GenerateFastMathOp(MathOp op, ZoneList* args); + inline void GenerateMathSin(ZoneList* args); + inline void GenerateMathCos(ZoneList* args); + // Methods and constants for fast case switch statement support. // // Only allow fast-case switch if the range of labels is at most diff --git a/src/codegen.cc b/src/codegen.cc index 4a4567775..cb3b24f74 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -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"} }; diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index 5d9588df1..b5efe9e4c 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -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_; diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index 79f239d7c..ae16e700f 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -658,6 +658,8 @@ class Assembler : public Malloced { void fabs(); void fchs(); + void fcos(); + void fsin(); void fadd(int i); void fsub(int i); diff --git a/src/ia32/codegen-ia32-inl.h b/src/ia32/codegen-ia32-inl.h index 49c706d13..44e937af1 100644 --- a/src/ia32/codegen-ia32-inl.h +++ b/src/ia32/codegen-ia32-inl.h @@ -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* args) { + GenerateFastMathOp(SIN, args); +} + + +void CodeGenerator::GenerateMathCos(ZoneList* args) { + GenerateFastMathOp(COS, args); +} + + #undef __ } } // namespace v8::internal diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index f79cb96fb..bf022aecb 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -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* args) { } +void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList* 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. diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index aed6966b9..e40951348 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -521,6 +521,12 @@ class CodeGenerator: public AstVisitor { // Fast support for Math.random(). void GenerateRandomPositiveSmi(ZoneList* args); + // Fast support for Math.sin and Math.cos. + enum MathOp { SIN, COS }; + void GenerateFastMathOp(MathOp op, ZoneList* args); + inline void GenerateMathSin(ZoneList* args); + inline void GenerateMathCos(ZoneList* args); + // Methods and constants for fast case switch statement support. // // Only allow fast-case switch if the range of labels is at most diff --git a/src/math.js b/src/math.js index 81c15a714..1f5ce87af 100644 --- a/src/math.js +++ b/src/math.js @@ -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 diff --git a/src/x64/codegen-x64-inl.h b/src/x64/codegen-x64-inl.h index 0d5b0e21d..733378de2 100644 --- a/src/x64/codegen-x64-inl.h +++ b/src/x64/codegen-x64-inl.h @@ -37,6 +37,17 @@ namespace internal { void DeferredCode::Jump() { UNIMPLEMENTED(); } void DeferredCode::Branch(Condition cc) { UNIMPLEMENTED(); } + +void CodeGenerator::GenerateMathSin(ZoneList* args) { + GenerateFastMathOp(SIN, args); +} + + +void CodeGenerator::GenerateMathCos(ZoneList* args) { + GenerateFastMathOp(COS, args); +} + + } } // namespace v8::internal #endif // V8_X64_CODEGEN_X64_INL_H_ diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 90504afb2..b1f296c2a 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -377,6 +377,10 @@ void CodeGenerator::GenerateRandomPositiveSmi(ZoneList* a) { UNIMPLEMENTED(); } +void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList* args) { + UNIMPLEMENTED(); +} + void CodeGenerator::GenerateSetValueOf(ZoneList* a) { UNIMPLEMENTED(); } diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index fcabc6820..19ad8a366 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -527,6 +527,12 @@ class CodeGenerator: public AstVisitor { // Fast support for Math.random(). void GenerateRandomPositiveSmi(ZoneList* args); + // Fast support for Math.sin and Math.cos. + enum MathOp { SIN, COS }; + void GenerateFastMathOp(MathOp op, ZoneList* args); + inline void GenerateMathSin(ZoneList* args); + inline void GenerateMathCos(ZoneList* args); + // Methods and constants for fast case switch statement support. // // Only allow fast-case switch if the range of labels is at most diff --git a/test/mjsunit/sin-cos.js b/test/mjsunit/sin-cos.js new file mode 100644 index 000000000..dcee37e27 --- /dev/null +++ b/test/mjsunit/sin-cos.js @@ -0,0 +1,49 @@ +// 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");