Allow the optimizing code generator to call Math.pow with untagged doubles.
authorwhesse@chromium.org <whesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 8 Dec 2010 14:32:40 +0000 (14:32 +0000)
committerwhesse@chromium.org <whesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 8 Dec 2010 14:32:40 +0000 (14:32 +0000)
Review URL: http://codereview.chromium.org/5640004

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

12 files changed:
src/assembler.cc
src/assembler.h
src/hydrogen-instructions.h
src/hydrogen.cc
src/ia32/lithium-codegen-ia32.cc
src/ia32/lithium-codegen-ia32.h
src/ia32/lithium-ia32.cc
src/ia32/lithium-ia32.h
src/math.js
src/objects.h
src/runtime.cc
src/serialize.cc

index d71a35a..3b44efa 100644 (file)
@@ -66,6 +66,7 @@ namespace internal {
 
 const double DoubleConstant::min_int = kMinInt;
 const double DoubleConstant::one_half = 0.5;
+const double DoubleConstant::negative_infinity = -V8_INFINITY;
 
 
 // -----------------------------------------------------------------------------
@@ -722,6 +723,12 @@ ExternalReference ExternalReference::address_of_one_half() {
 }
 
 
+ExternalReference ExternalReference::address_of_negative_infinity() {
+  return ExternalReference(reinterpret_cast<void*>(
+      const_cast<double*>(&DoubleConstant::negative_infinity)));
+}
+
+
 #ifndef V8_INTERPRETED_REGEXP
 
 ExternalReference ExternalReference::re_check_stack_guard_state() {
@@ -793,6 +800,51 @@ static double mod_two_doubles(double x, double y) {
 }
 
 
+// Helper function to compute x^y, where y is known to be an
+// integer. Uses binary decomposition to limit the number of
+// multiplications; see the discussion in "Hacker's Delight" by Henry
+// S. Warren, Jr., figure 11-6, page 213.
+double power_double_int(double x, int y) {
+  double m = (y < 0) ? 1 / x : x;
+  unsigned n = (y < 0) ? -y : y;
+  double p = 1;
+  while (n != 0) {
+    if ((n & 1) != 0) p *= m;
+    m *= m;
+    if ((n & 2) != 0) p *= m;
+    m *= m;
+    n >>= 2;
+  }
+  return p;
+}
+
+
+double power_double_double(double x, double y) {
+  int y_int = static_cast<int>(y);
+  if (y == y_int) {
+    return power_double_int(x, y_int);  // Returns 1.0 for exponent 0.
+  }
+  if (!isinf(x)) {
+    if (y == 0.5) return sqrt(x);
+    if (y == -0.5) return 1.0 / sqrt(x);
+  }
+  if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
+    return OS::nan_value();
+  }
+  return pow(x, y);
+}
+
+
+ExternalReference ExternalReference::power_double_double_function() {
+  return ExternalReference(Redirect(FUNCTION_ADDR(power_double_double)));
+}
+
+
+ExternalReference ExternalReference::power_double_int_function() {
+  return ExternalReference(Redirect(FUNCTION_ADDR(power_double_int)));
+}
+
+
 static int native_compare_doubles(double y, double x) {
   if (x == y) return EQUAL;
   return x < y ? LESS : GREATER;
index 82c9fc2..72a9b15 100644 (file)
@@ -50,6 +50,7 @@ class DoubleConstant: public AllStatic {
  public:
   static const double min_int;
   static const double one_half;
+  static const double negative_infinity;
 };
 
 
@@ -539,6 +540,8 @@ class ExternalReference BASE_EMBEDDED {
 
   static ExternalReference double_fp_operation(Token::Value operation);
   static ExternalReference compare_doubles();
+  static ExternalReference power_double_double_function();
+  static ExternalReference power_double_int_function();
 
   static ExternalReference handle_scope_next_address();
   static ExternalReference handle_scope_limit_address();
@@ -549,6 +552,7 @@ class ExternalReference BASE_EMBEDDED {
   // Static variables containing common double constants.
   static ExternalReference address_of_min_int();
   static ExternalReference address_of_one_half();
+  static ExternalReference address_of_negative_infinity();
 
   Address address() const {return reinterpret_cast<Address>(address_);}
 
@@ -710,6 +714,10 @@ static inline int NumberOfBitsSet(uint32_t x) {
   return num_bits_set;
 }
 
+// Computes pow(x, y) with the special cases in the spec for Math.pow.
+double power_double_int(double x, int y);
+double power_double_double(double x, double y);
+
 } }  // namespace v8::internal
 
 #endif  // V8_ASSEMBLER_H_
index ff1ab1a..3431631 100644 (file)
@@ -77,6 +77,7 @@ class LChunkBuilder;
 //         HLoadKeyedFastElement
 //         HLoadKeyedGeneric
 //       HLoadNamedGeneric
+//       HPower
 //       HStoreNamed
 //         HStoreNamedField
 //         HStoreNamedGeneric
@@ -223,6 +224,7 @@ class LChunkBuilder;
   V(ObjectLiteral)                             \
   V(OsrEntry)                                  \
   V(Parameter)                                 \
+  V(Power)                                     \
   V(PushArgument)                              \
   V(RegExpLiteral)                             \
   V(Return)                                    \
@@ -1377,6 +1379,7 @@ class HUnaryMathOperation: public HUnaryOperation {
         SetFlag(kFlexibleRepresentation);
         break;
       case kMathSqrt:
+      case kMathPowHalf:
       default:
         set_representation(Representation::Double());
     }
@@ -1395,6 +1398,7 @@ class HUnaryMathOperation: public HUnaryOperation {
       case kMathRound:
       case kMathCeil:
       case kMathSqrt:
+      case kMathPowHalf:
         return Representation::Double();
         break;
       case kMathAbs:
@@ -2184,6 +2188,22 @@ class HInstanceOf: public HBinaryOperation {
 };
 
 
+class HPower: public HBinaryOperation {
+ public:
+  HPower(HValue* left, HValue* right)
+      : HBinaryOperation(left, right) {
+    set_representation(Representation::Double());
+    SetFlag(kUseGVN);
+  }
+
+  virtual Representation RequiredInputRepresentation(int index) const {
+    return (index == 1) ? Representation::None() : Representation::Double();
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(Power, "power")
+};
+
+
 class HAdd: public HArithmeticBinaryOperation {
  public:
   HAdd(HValue* left, HValue* right) : HArithmeticBinaryOperation(left, right) {
index 0e8c476..ad327c4 100644 (file)
@@ -4093,6 +4093,42 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) {
         return true;
       }
       break;
+    case kMathPow:
+      if (argument_count == 3) {
+        HValue* right = Pop();
+        HValue* left = Pop();
+        Pop();  // Pop receiver.
+        // Use sqrt() if exponent is 0.5 or -0.5.
+        if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
+          double exponent = HConstant::cast(right)->DoubleValue();
+          if (exponent == 0.5) {
+            PushAndAdd(new HUnaryMathOperation(left, kMathPowHalf));
+            return true;
+          } else if (exponent == -0.5) {
+            HConstant* double_one =
+                new HConstant(Handle<Object>(Smi::FromInt(1)),
+                              Representation::Double());
+            AddInstruction(double_one);
+            HUnaryMathOperation* square_root =
+                new HUnaryMathOperation(left, kMathPowHalf);
+            AddInstruction(square_root);
+            PushAndAdd(new HDiv(double_one, square_root));
+            return true;
+          } else if (exponent == 2.0) {
+            PushAndAdd(new HMul(left, left));
+            return true;
+          }
+        } else if (right->IsConstant() &&
+            HConstant::cast(right)->HasInteger32Value() &&
+            HConstant::cast(right)->Integer32Value() == 2) {
+          PushAndAdd(new HMul(left, left));
+          return true;
+        }
+
+        PushAndAdd(new HPower(left, right));
+        return true;
+      }
+      break;
     default:
       // Either not a special math function or not yet supported for inlining.
       break;
@@ -5020,9 +5056,9 @@ void HGraphBuilder::GenerateCallFunction(int argument_count) {
 // Fast call to math functions.
 void HGraphBuilder::GenerateMathPow(int argument_count) {
   ASSERT_EQ(2, argument_count);
-  PushArgumentsForStubCall(argument_count);
-  PushAndAdd(new HCallStub(CodeStub::MathPow, argument_count),
-             RelocInfo::kNoPosition);
+  HValue* right = Pop();
+  HValue* left = Pop();
+  PushAndAdd(new HPower(left, right));
 }
 
 
index dc0f5e9..b5c0289 100644 (file)
@@ -2174,6 +2174,67 @@ void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) {
 }
 
 
+void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) {
+  XMMRegister xmm_scratch = xmm0;
+  XMMRegister input_reg = ToDoubleRegister(instr->input());
+  ASSERT(ToDoubleRegister(instr->result()).is(input_reg));
+  ExternalReference negative_infinity =
+      ExternalReference::address_of_negative_infinity();
+  __ movdbl(xmm_scratch, Operand::StaticVariable(negative_infinity));
+  __ ucomisd(xmm_scratch, input_reg);
+  DeoptimizeIf(equal, instr->environment());
+  __ sqrtsd(input_reg, input_reg);
+}
+
+
+void LCodeGen::DoPower(LPower* instr) {
+  LOperand* left = instr->left();
+  LOperand* right = instr->right();
+  Representation exponent_type = instr->hydrogen()->right()->representation();
+  if (exponent_type.IsDouble()) {
+    // Pass two doubles as arguments on the stack.
+    __ PrepareCallCFunction(4, eax);
+    __ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left));
+    __ movdbl(Operand(esp, 1 * kDoubleSize), ToDoubleRegister(right));
+    __ CallCFunction(ExternalReference::power_double_double_function(), 4);
+  } else if (exponent_type.IsInteger32()) {
+    __ PrepareCallCFunction(4, ebx);
+    __ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left));
+    __ mov(Operand(esp, 1 * kDoubleSize), ToRegister(right));
+    __ CallCFunction(ExternalReference::power_double_int_function(), 4);
+  } else {
+    ASSERT(exponent_type.IsTagged());
+    __ PrepareCallCFunction(4, ebx);
+    __ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left));
+    Register right_reg = ToRegister(right);
+    Label non_smi;
+    Label done;
+    __ test(right_reg, Immediate(kSmiTagMask));
+    __ j(not_zero, &non_smi);
+    __ SmiUntag(right_reg);
+    __ mov(Operand(esp, 1 * kDoubleSize), ToRegister(right));
+    __ CallCFunction(ExternalReference::power_double_int_function(), 4);
+    __ jmp(&done);
+
+    __ bind(&non_smi);
+    __ CmpObjectType(right_reg, HEAP_NUMBER_TYPE , ebx);
+    DeoptimizeIf(not_equal, instr->environment());
+    __ movdbl(xmm1, FieldOperand(right_reg, HeapNumber::kValueOffset));
+    __ movdbl(Operand(esp, 1 * kDoubleSize), xmm1);
+    __ CallCFunction(ExternalReference::power_double_double_function(), 4);
+
+    __ bind(&done);
+  }
+
+  // Return value is in st(0) on ia32.
+  // Store it into the (fixed) result register.
+  __ sub(Operand(esp), Immediate(kDoubleSize));
+  __ fstp_d(Operand(esp, 0));
+  __ movdbl(ToDoubleRegister(instr->result()), Operand(esp, 0));
+  __ add(Operand(esp), Immediate(kDoubleSize));
+}
+
+
 void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
   switch (instr->op()) {
     case kMathAbs:
@@ -2188,6 +2249,9 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
     case kMathSqrt:
       DoMathSqrt(instr);
       break;
+    case kMathPowHalf:
+      DoMathPowHalf(instr);
+      break;
     default:
       UNREACHABLE();
   }
index 91b3fab..ca4e9b3 100644 (file)
@@ -175,6 +175,7 @@ class LCodeGen BASE_EMBEDDED {
   void DoMathFloor(LUnaryMathOperation* instr);
   void DoMathRound(LUnaryMathOperation* instr);
   void DoMathSqrt(LUnaryMathOperation* instr);
+  void DoMathPowHalf(LUnaryMathOperation* instr);
 
   // Support for recording safepoint and position information.
   void RecordSafepoint(LPointerMap* pointers, int deoptimization_index);
index 41f8036..1f97143 100644 (file)
@@ -1366,6 +1366,8 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
       return AssignEnvironment(DefineAsRegister(result));
     case kMathSqrt:
       return DefineSameAsFirst(result);
+    case kMathPowHalf:
+      return AssignEnvironment(DefineSameAsFirst(result));
     default:
       UNREACHABLE();
       return NULL;
@@ -1566,6 +1568,22 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) {
 }
 
 
+LInstruction* LChunkBuilder::DoPower(HPower* instr) {
+  ASSERT(instr->representation().IsDouble());
+  // We call a C function for double power. It can't trigger a GC.
+  // We need to use fixed result register for the call.
+  Representation exponent_type = instr->right()->representation();
+  ASSERT(instr->left()->representation().IsDouble());
+  LOperand* left = UseFixedDouble(instr->left(), xmm1);
+  LOperand* right = exponent_type.IsDouble() ?
+      UseFixedDouble(instr->right(), xmm2) :
+      UseFixed(instr->right(), eax);
+  LPower* result = new LPower(left, right);
+  return MarkAsCall(DefineFixedDouble(result, xmm1), instr,
+                    CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
 LInstruction* LChunkBuilder::DoCompare(HCompare* instr) {
   Token::Value op = instr->token();
   if (instr->left()->representation().IsInteger32()) {
index 1c901c0..c97859c 100644 (file)
@@ -67,6 +67,7 @@ class LGapNode;
 //     LLoadKeyedGeneric
 //     LModI
 //     LMulI
+//     LPower
 //     LShiftI
 //     LSubI
 //   LCallConstantFunction
@@ -229,6 +230,7 @@ class LGapNode;
   V(ObjectLiteral)                              \
   V(OsrEntry)                                   \
   V(Parameter)                                  \
+  V(Power)                                      \
   V(PushArgument)                               \
   V(RegExpLiteral)                              \
   V(Return)                                     \
@@ -1154,6 +1156,16 @@ class LAddI: public LBinaryOperation {
 };
 
 
+class LPower: public LBinaryOperation {
+ public:
+  LPower(LOperand* left, LOperand* right)
+      : LBinaryOperation(left, right) { }
+
+  DECLARE_CONCRETE_INSTRUCTION(Power, "power")
+  DECLARE_HYDROGEN_ACCESSOR(Power)
+};
+
+
 class LArithmeticD: public LBinaryOperation {
  public:
   LArithmeticD(Token::Value op, LOperand* left, LOperand* right)
index fa1934d..2e886d0 100644 (file)
@@ -264,6 +264,7 @@ function SetupMath() {
   %SetMathFunctionId($Math.round, 2);
   %SetMathFunctionId($Math.abs, 4);
   %SetMathFunctionId($Math.sqrt, 0xd);
+  %SetMathFunctionId($Math.pow, 0xe);
   // TODO(erikcorry): Set the id of the other functions so they can be
   // optimized.
 };
index 1827ab0..c3958e2 100644 (file)
@@ -3729,7 +3729,9 @@ enum MathFunctionId {
   kMathACos = 0xa,
   kMathATan = 0xb,
   kMathExp = 0xc,
-  kMathSqrt = 0xd
+  kMathSqrt = 0xd,
+  kMathPow = 0xe,
+  kMathPowHalf = 0xf
 };
 
 
@@ -4154,11 +4156,11 @@ class SharedFunctionInfo: public HeapObject {
   static const int kTryFullCodegen = 1;
   static const int kAllowLazyCompilation = 2;
   static const int kMathFunctionShift = 3;
-  static const int kMathFunctionMask = 0xf;
-  static const int kLiveObjectsMayExist = 7;
-  static const int kCodeAgeShift = 8;
+  static const int kMathFunctionMask = 0x1f;
+  static const int kLiveObjectsMayExist = 8;
+  static const int kCodeAgeShift = 9;
   static const int kCodeAgeMask = 0x7;
-  static const int kOptimizationDisabled = 11;
+  static const int kOptimizationDisabled = 12;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo);
 };
index d9b66a5..0f00a33 100644 (file)
@@ -5977,37 +5977,6 @@ static MaybeObject* Runtime_Math_log(Arguments args) {
 }
 
 
-// Helper function to compute x^y, where y is known to be an
-// integer. Uses binary decomposition to limit the number of
-// multiplications; see the discussion in "Hacker's Delight" by Henry
-// S. Warren, Jr., figure 11-6, page 213.
-static double powi(double x, int y) {
-  ASSERT(y != kMinInt);
-  unsigned n = (y < 0) ? -y : y;
-  double m = x;
-  double p = 1;
-  while (true) {
-    if ((n & 1) != 0) p *= m;
-    n >>= 1;
-    if (n == 0) {
-      if (y < 0) {
-        // Unfortunately, we have to be careful when p has reached
-        // infinity in the computation, because sometimes the higher
-        // internal precision in the pow() implementation would have
-        // given us a finite p. This happens very rarely.
-        double result = 1.0 / p;
-        return (result == 0 && isinf(p))
-            ? pow(x, static_cast<double>(y))  // Avoid pow(double, int).
-            : result;
-      } else {
-        return p;
-      }
-    }
-    m *= m;
-  }
-}
-
-
 static MaybeObject* Runtime_Math_pow(Arguments args) {
   NoHandleAllocation ha;
   ASSERT(args.length() == 2);
@@ -6019,31 +5988,11 @@ static MaybeObject* Runtime_Math_pow(Arguments args) {
   // custom powi() function than the generic pow().
   if (args[1]->IsSmi()) {
     int y = Smi::cast(args[1])->value();
-    return Heap::NumberFromDouble(powi(x, y));
+    return Heap::NumberFromDouble(power_double_int(x, y));
   }
 
   CONVERT_DOUBLE_CHECKED(y, args[1]);
-
-  if (!isinf(x)) {
-    if (y == 0.5) {
-      // It's not uncommon to use Math.pow(x, 0.5) to compute the
-      // square root of a number. To speed up such computations, we
-      // explictly check for this case and use the sqrt() function
-      // which is faster than pow().
-      return Heap::AllocateHeapNumber(sqrt(x));
-    } else if (y == -0.5) {
-      // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
-      return Heap::AllocateHeapNumber(1.0 / sqrt(x));
-    }
-  }
-
-  if (y == 0) {
-    return Smi::FromInt(1);
-  } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
-    return Heap::nan_value();
-  } else {
-    return Heap::AllocateHeapNumber(pow(x, y));
-  }
+  return Heap::AllocateHeapNumber(power_double_double(x, y));
 }
 
 // Fast version of Math.pow if we know that y is not an integer and
@@ -6054,11 +6003,11 @@ static MaybeObject* Runtime_Math_pow_cfunction(Arguments args) {
   CONVERT_DOUBLE_CHECKED(x, args[0]);
   CONVERT_DOUBLE_CHECKED(y, args[1]);
   if (y == 0) {
-      return Smi::FromInt(1);
+    return Smi::FromInt(1);
   } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
-      return Heap::nan_value();
+    return Heap::nan_value();
   } else {
-      return Heap::AllocateHeapNumber(pow(x, y));
+    return Heap::AllocateHeapNumber(pow(x, y));
   }
 }
 
index 6edc4fa..00a601f 100644 (file)
@@ -486,6 +486,18 @@ void ExternalReferenceTable::PopulateTable() {
       UNCLASSIFIED,
       36,
       "LDoubleConstant::one_half");
+  Add(ExternalReference::address_of_negative_infinity().address(),
+      UNCLASSIFIED,
+      37,
+      "LDoubleConstant::negative_infinity");
+  Add(ExternalReference::power_double_double_function().address(),
+      UNCLASSIFIED,
+      38,
+      "power_double_double_function");
+  Add(ExternalReference::power_double_int_function().address(),
+      UNCLASSIFIED,
+      39,
+      "power_double_int_function");
 }