Optimize Math.sin and Math.cos by avoiding runtime calls.
authorager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 15 Jun 2009 12:06:48 +0000 (12:06 +0000)
committerager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 15 Jun 2009 12:06:48 +0000 (12:06 +0000)
Review URL: http://codereview.chromium.org/125121

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2166 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

14 files changed:
src/arm/codegen-arm-inl.h
src/arm/codegen-arm.cc
src/arm/codegen-arm.h
src/codegen.cc
src/ia32/assembler-ia32.cc
src/ia32/assembler-ia32.h
src/ia32/codegen-ia32-inl.h
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/math.js
src/x64/codegen-x64-inl.h
src/x64/codegen-x64.cc
src/x64/codegen-x64.h
test/mjsunit/sin-cos.js [new file with mode: 0644]

index 544331a..5a29a45 100644 (file)
@@ -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
index c8fd287..9b45bfe 100644 (file)
@@ -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);
index 4dfbdab..0df793a 100644 (file)
@@ -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
index 4a45677..cb3b24f 100644 (file)
@@ -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"}
 };
 
 
index 5d9588d..b5efe9e 100644 (file)
@@ -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_;
index 79f239d..ae16e70 100644 (file)
@@ -658,6 +658,8 @@ class Assembler : public Malloced {
 
   void fabs();
   void fchs();
+  void fcos();
+  void fsin();
 
   void fadd(int i);
   void fsub(int i);
index 49c706d..44e937a 100644 (file)
@@ -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
index f79cb96..bf022ae 100644 (file)
@@ -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.
index aed6966..e409513 100644 (file)
@@ -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
index 81c15a7..1f5ce87 100644 (file)
@@ -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
index 0d5b0e2..733378d 100644 (file)
@@ -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_
index 90504af..b1f296c 100644 (file)
@@ -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();
 }
index fcabc68..19ad8a3 100644 (file)
@@ -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
diff --git a/test/mjsunit/sin-cos.js b/test/mjsunit/sin-cos.js
new file mode 100644 (file)
index 0000000..dcee37e
--- /dev/null
@@ -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");