Revision: 21091
Author:   [email protected]
Date:     Wed Apr 30 13:38:00 2014 UTC
Log: ARM64: Generate optimized code for Math.floor and Math.round with double outputs.

[email protected], [email protected]

Review URL: https://codereview.chromium.org/258793002
http://code.google.com/p/v8/source/detail?r=21091

Modified:
 /branches/bleeding_edge/src/arm64/constants-arm64.h
 /branches/bleeding_edge/src/arm64/lithium-arm64.cc
 /branches/bleeding_edge/src/arm64/lithium-arm64.h
 /branches/bleeding_edge/src/arm64/lithium-codegen-arm64.cc
 /branches/bleeding_edge/src/hydrogen-instructions.cc
 /branches/bleeding_edge/src/hydrogen-instructions.h
 /branches/bleeding_edge/test/mjsunit/math-floor-part1.js
 /branches/bleeding_edge/test/mjsunit/math-floor-part2.js
 /branches/bleeding_edge/test/mjsunit/math-floor-part3.js
 /branches/bleeding_edge/test/mjsunit/math-floor-part4.js
 /branches/bleeding_edge/test/mjsunit/math-round.js

=======================================
--- /branches/bleeding_edge/src/arm64/constants-arm64.h Tue Apr 29 06:42:26 2014 UTC +++ /branches/bleeding_edge/src/arm64/constants-arm64.h Wed Apr 30 13:38:00 2014 UTC
@@ -107,6 +107,7 @@
 // AArch64 floating-point specifics. These match IEEE-754.
 const unsigned kDoubleMantissaBits = 52;
 const unsigned kDoubleExponentBits = 11;
+const unsigned kDoubleExponentBias = 1023;
 const unsigned kFloatMantissaBits = 23;
 const unsigned kFloatExponentBits = 8;

=======================================
--- /branches/bleeding_edge/src/arm64/lithium-arm64.cc Wed Apr 30 09:50:58 2014 UTC +++ /branches/bleeding_edge/src/arm64/lithium-arm64.cc Wed Apr 30 13:38:00 2014 UTC
@@ -2445,14 +2445,16 @@
       return DefineAsRegister(result);
     }
     case kMathFloor: {
-      ASSERT(instr->representation().IsInteger32());
       ASSERT(instr->value()->representation().IsDouble());
- // TODO(jbramley): ARM64 can easily handle a double argument with frintm, - // but we're never asked for it here. At the moment, we fall back to the
-      // runtime if the result doesn't fit, like the other architectures.
       LOperand* input = UseRegisterAtStart(instr->value());
-      LMathFloor* result = new(zone()) LMathFloor(input);
-      return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+      if (instr->representation().IsInteger32()) {
+        LMathFloorI* result = new(zone()) LMathFloorI(input);
+ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+      } else {
+        ASSERT(instr->representation().IsDouble());
+        LMathFloorD* result = new(zone()) LMathFloorD(input);
+        return DefineAsRegister(result);
+      }
     }
     case kMathLog: {
       ASSERT(instr->representation().IsDouble());
@@ -2468,14 +2470,16 @@
       return DefineAsRegister(new(zone()) LMathPowHalf(input));
     }
     case kMathRound: {
-      ASSERT(instr->representation().IsInteger32());
       ASSERT(instr->value()->representation().IsDouble());
-      // TODO(jbramley): As with kMathFloor, we can probably handle double
-      // results fairly easily, but we are never asked for them.
       LOperand* input = UseRegister(instr->value());
-      LOperand* temp = FixedTemp(d24);  // Choosen arbitrarily.
-      LMathRound* result = new(zone()) LMathRound(input, temp);
-      return AssignEnvironment(DefineAsRegister(result));
+      if (instr->representation().IsInteger32()) {
+ LMathRoundI* result = new(zone()) LMathRoundI(input, FixedTemp(d24));
+        return AssignEnvironment(DefineAsRegister(result));
+      } else {
+        ASSERT(instr->representation().IsDouble());
+        LMathRoundD* result = new(zone()) LMathRoundD(input);
+        return DefineAsRegister(result);
+      }
     }
     case kMathSqrt: {
       ASSERT(instr->representation().IsDouble());
=======================================
--- /branches/bleeding_edge/src/arm64/lithium-arm64.h Wed Apr 30 10:24:03 2014 UTC +++ /branches/bleeding_edge/src/arm64/lithium-arm64.h Wed Apr 30 13:38:00 2014 UTC
@@ -115,11 +115,13 @@
   V(MathAbsTagged)                              \
   V(MathClz32)                                  \
   V(MathExp)                                    \
-  V(MathFloor)                                  \
+  V(MathFloorD)                                 \
+  V(MathFloorI)                                 \
   V(MathLog)                                    \
   V(MathMinMax)                                 \
   V(MathPowHalf)                                \
-  V(MathRound)                                  \
+  V(MathRoundD)                                 \
+  V(MathRoundI)                                 \
   V(MathSqrt)                                   \
   V(ModByConstI)                                \
   V(ModByPowerOf2I)                             \
@@ -1909,10 +1911,19 @@
 };


-class LMathFloor V8_FINAL : public LUnaryMathOperation<0> {
+// Math.floor with a double result.
+class LMathFloorD V8_FINAL : public LUnaryMathOperation<0> {
  public:
-  explicit LMathFloor(LOperand* value) : LUnaryMathOperation<0>(value) { }
-  DECLARE_CONCRETE_INSTRUCTION(MathFloor, "math-floor")
+  explicit LMathFloorD(LOperand* value) : LUnaryMathOperation<0>(value) { }
+  DECLARE_CONCRETE_INSTRUCTION(MathFloorD, "math-floor-d")
+};
+
+
+// Math.floor with an integer result.
+class LMathFloorI V8_FINAL : public LUnaryMathOperation<0> {
+ public:
+  explicit LMathFloorI(LOperand* value) : LUnaryMathOperation<0>(value) { }
+  DECLARE_CONCRETE_INSTRUCTION(MathFloorI, "math-floor-i")
 };


@@ -2008,16 +2019,28 @@
 };


-class LMathRound V8_FINAL : public LUnaryMathOperation<1> {
+// Math.round with an integer result.
+class LMathRoundD V8_FINAL : public LUnaryMathOperation<0> {
+ public:
+  explicit LMathRoundD(LOperand* value)
+      : LUnaryMathOperation<0>(value) {
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(MathRoundD, "math-round-d")
+};
+
+
+// Math.round with an integer result.
+class LMathRoundI V8_FINAL : public LUnaryMathOperation<1> {
  public:
-  LMathRound(LOperand* value, LOperand* temp1)
+  LMathRoundI(LOperand* value, LOperand* temp1)
       : LUnaryMathOperation<1>(value) {
     temps_[0] = temp1;
   }

   LOperand* temp1() { return temps_[0]; }

-  DECLARE_CONCRETE_INSTRUCTION(MathRound, "math-round")
+  DECLARE_CONCRETE_INSTRUCTION(MathRoundI, "math-round-i")
 };


=======================================
--- /branches/bleeding_edge/src/arm64/lithium-codegen-arm64.cc Tue Apr 29 06:42:26 2014 UTC +++ /branches/bleeding_edge/src/arm64/lithium-codegen-arm64.cc Wed Apr 30 13:38:00 2014 UTC
@@ -3761,9 +3761,15 @@
 }


-void LCodeGen::DoMathFloor(LMathFloor* instr) {
- // TODO(jbramley): If we could provide a double result, we could use frintm
-  // and produce a valid double result in a single instruction.
+void LCodeGen::DoMathFloorD(LMathFloorD* instr) {
+  DoubleRegister input = ToDoubleRegister(instr->value());
+  DoubleRegister result = ToDoubleRegister(instr->result());
+
+  __ Frintm(result, input);
+}
+
+
+void LCodeGen::DoMathFloorI(LMathFloorI* instr) {
   DoubleRegister input = ToDoubleRegister(instr->value());
   Register result = ToRegister(instr->result());

@@ -3996,8 +4002,37 @@
 }


-void LCodeGen::DoMathRound(LMathRound* instr) {
-  // TODO(jbramley): We could provide a double result here using frint.
+void LCodeGen::DoMathRoundD(LMathRoundD* instr) {
+  DoubleRegister input = ToDoubleRegister(instr->value());
+  DoubleRegister result = ToDoubleRegister(instr->result());
+  DoubleRegister scratch_d = double_scratch();
+
+  ASSERT(!AreAliased(input, result, scratch_d));
+
+  Label done;
+
+  __ Frinta(result, input);
+  __ Fcmp(input, 0.0);
+  __ Fccmp(result, input, ZFlag, lt);
+  // The result is correct if the input was in [-0, +infinity], or was a
+  // negative integral value.
+  __ B(eq, &done);
+
+ // Here the input is negative, non integral, with an exponent lower than 52. + // We do not have to worry about the 0.49999999999999994 (0x3fdfffffffffffff)
+  // case. So we can safely add 0.5.
+  __ Fmov(scratch_d, 0.5);
+  __ Fadd(result, input, scratch_d);
+  __ Frintm(result, result);
+  // The range [-0.5, -0.0[ yielded +0.0. Force the sign to negative.
+  __ Fabs(result, result);
+  __ Fneg(result, result);
+
+  __ Bind(&done);
+}
+
+
+void LCodeGen::DoMathRoundI(LMathRoundI* instr) {
   DoubleRegister input = ToDoubleRegister(instr->value());
   DoubleRegister temp1 = ToDoubleRegister(instr->temp1());
   Register result = ToRegister(instr->result());
@@ -4036,7 +4071,7 @@
// Since we're providing a 32-bit result, we can implement ties-to-infinity by // adding 0.5 to the input, then taking the floor of the result. This does not
   // work for very large positive doubles because adding 0.5 would cause an
- // intermediate rounding stage, so a different approach will be necessary if a + // intermediate rounding stage, so a different approach is necessary when a
   // double result is needed.
   __ Fadd(temp1, input, dot_five);
   __ Fcvtms(result, temp1);
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.cc Tue Apr 29 06:42:26 2014 UTC +++ /branches/bleeding_edge/src/hydrogen-instructions.cc Wed Apr 30 13:38:00 2014 UTC
@@ -3698,6 +3698,12 @@


 Representation HUnaryMathOperation::RepresentationFromInputs() {
+  if (SupportsFlexibleFloorAndRound() &&
+      (op_ == kMathFloor || op_ == kMathRound)) {
+ // Floor and Round always take a double input. The integral result can be + // used as an integer or a double. Infer the representation from the uses.
+    return Representation::None();
+  }
   Representation rep = representation();
   // If any of the actual input representation is more general than what we
   // have so far but not Tagged, use that representation instead.
@@ -4164,6 +4170,43 @@
   } while (false);
   return new(zone) HUnaryMathOperation(context, value, op);
 }
+
+
+Representation HUnaryMathOperation::RepresentationFromUses() {
+  if (op_ != kMathFloor && op_ != kMathRound) {
+    return HValue::RepresentationFromUses();
+  }
+
+  // The instruction can have an int32 or double output. Prefer a double
+  // representation if there are double uses.
+  bool use_double = false;
+
+  for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+    HValue* use = it.value();
+    int use_index = it.index();
+ Representation rep_observed = use->observed_input_representation(use_index); + Representation rep_required = use->RequiredInputRepresentation(use_index);
+    use_double |= (rep_observed.IsDouble() || rep_required.IsDouble());
+    if (use_double && !FLAG_trace_representation) {
+      // Having seen one double is enough.
+      break;
+    }
+    if (FLAG_trace_representation) {
+      if (!rep_required.IsDouble() || rep_observed.IsDouble()) {
+        PrintF("#%d %s is used by #%d %s as %s%s\n",
+               id(), Mnemonic(), use->id(),
+               use->Mnemonic(), rep_observed.Mnemonic(),
+               (use->CheckFlag(kTruncatingToInt32) ? "-trunc" : ""));
+      } else {
+        PrintF("#%d %s is required by #%d %s as %s%s\n",
+               id(), Mnemonic(), use->id(),
+               use->Mnemonic(), rep_required.Mnemonic(),
+               (use->CheckFlag(kTruncatingToInt32) ? "-trunc" : ""));
+      }
+    }
+  }
+ return use_double ? Representation::Double() : Representation::Integer32();
+}


 HInstruction* HPower::New(Zone* zone,
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.h Tue Apr 29 06:42:26 2014 UTC +++ /branches/bleeding_edge/src/hydrogen-instructions.h Wed Apr 30 13:38:00 2014 UTC
@@ -949,7 +949,7 @@
   virtual Representation RepresentationFromInputs() {
     return representation();
   }
-  Representation RepresentationFromUses();
+  virtual Representation RepresentationFromUses();
   Representation RepresentationFromUseRequirements();
   bool HasNonSmiUse();
   virtual void UpdateRepresentation(Representation new_rep,
@@ -2638,6 +2638,7 @@
   virtual Range* InferRange(Zone* zone) V8_OVERRIDE;

   virtual HValue* Canonicalize() V8_OVERRIDE;
+  virtual Representation RepresentationFromUses() V8_OVERRIDE;
   virtual Representation RepresentationFromInputs() V8_OVERRIDE;

   BuiltinFunctionId op() const { return op_; }
@@ -2652,6 +2653,15 @@
   }

  private:
+  // Indicates if we support a double (and int32) output for Math.floor and
+  // Math.round.
+  bool SupportsFlexibleFloorAndRound() const {
+#ifdef V8_TARGET_ARCH_ARM64
+    return true;
+#else
+    return false;
+#endif
+  }
   HUnaryMathOperation(HValue* context, HValue* value, BuiltinFunctionId op)
       : HTemplateInstruction<2>(HType::TaggedNumber()), op_(op) {
     SetOperandAt(0, context);
@@ -2659,6 +2669,12 @@
     switch (op) {
       case kMathFloor:
       case kMathRound:
+        if (SupportsFlexibleFloorAndRound()) {
+          SetFlag(kFlexibleRepresentation);
+        } else {
+          set_representation(Representation::Integer32());
+        }
+        break;
       case kMathClz32:
         set_representation(Representation::Integer32());
         break;
=======================================
--- /branches/bleeding_edge/test/mjsunit/math-floor-part1.js Wed Jan 8 09:09:49 2014 UTC +++ /branches/bleeding_edge/test/mjsunit/math-floor-part1.js Wed Apr 30 13:38:00 2014 UTC
@@ -37,6 +37,15 @@
   assertEquals(expect, test(input));
   %OptimizeFunctionOnNextCall(test);
   assertEquals(expect, test(input));
+
+  var test_double_output = new Function(
+      'n',
+      '"' + (test_id++) + '";return Math.floor(n) + -0.0');
+  assertEquals(expect, test_double_output(input));
+  assertEquals(expect, test_double_output(input));
+  assertEquals(expect, test_double_output(input));
+  %OptimizeFunctionOnNextCall(test_double_output);
+  assertEquals(expect, test_double_output(input));
 }

 function zero() {
=======================================
--- /branches/bleeding_edge/test/mjsunit/math-floor-part2.js Mon Sep 24 10:02:44 2012 UTC +++ /branches/bleeding_edge/test/mjsunit/math-floor-part2.js Wed Apr 30 13:38:00 2014 UTC
@@ -37,6 +37,15 @@
   assertEquals(expect, test(input));
   %OptimizeFunctionOnNextCall(test);
   assertEquals(expect, test(input));
+
+  var test_double_output = new Function(
+      'n',
+      '"' + (test_id++) + '";return Math.floor(n) + -0.0');
+  assertEquals(expect, test_double_output(input));
+  assertEquals(expect, test_double_output(input));
+  assertEquals(expect, test_double_output(input));
+  %OptimizeFunctionOnNextCall(test_double_output);
+  assertEquals(expect, test_double_output(input));
 }

 function zero() {
=======================================
--- /branches/bleeding_edge/test/mjsunit/math-floor-part3.js Mon Sep 24 10:02:44 2012 UTC +++ /branches/bleeding_edge/test/mjsunit/math-floor-part3.js Wed Apr 30 13:38:00 2014 UTC
@@ -37,6 +37,15 @@
   assertEquals(expect, test(input));
   %OptimizeFunctionOnNextCall(test);
   assertEquals(expect, test(input));
+
+  var test_double_output = new Function(
+      'n',
+      '"' + (test_id++) + '";return Math.floor(n) + -0.0');
+  assertEquals(expect, test_double_output(input));
+  assertEquals(expect, test_double_output(input));
+  assertEquals(expect, test_double_output(input));
+  %OptimizeFunctionOnNextCall(test_double_output);
+  assertEquals(expect, test_double_output(input));
 }

 function zero() {
=======================================
--- /branches/bleeding_edge/test/mjsunit/math-floor-part4.js Mon Sep 24 10:02:44 2012 UTC +++ /branches/bleeding_edge/test/mjsunit/math-floor-part4.js Wed Apr 30 13:38:00 2014 UTC
@@ -37,6 +37,15 @@
   assertEquals(expect, test(input));
   %OptimizeFunctionOnNextCall(test);
   assertEquals(expect, test(input));
+
+  var test_double_output = new Function(
+      'n',
+      '"' + (test_id++) + '";return Math.floor(n) + -0.0');
+  assertEquals(expect, test_double_output(input));
+  assertEquals(expect, test_double_output(input));
+  assertEquals(expect, test_double_output(input));
+  %OptimizeFunctionOnNextCall(test_double_output);
+  assertEquals(expect, test_double_output(input));
 }

 function zero() {
=======================================
--- /branches/bleeding_edge/test/mjsunit/math-round.js Wed Jan 8 09:09:49 2014 UTC +++ /branches/bleeding_edge/test/mjsunit/math-round.js Wed Apr 30 13:38:00 2014 UTC
@@ -38,6 +38,16 @@
   assertEquals(expect, doRound(input));
   %OptimizeFunctionOnNextCall(doRound);
   assertEquals(expect, doRound(input));
+
+ // Force the Math.round() representation to double to exercise the associated
+  // optimized code.
+  var doRoundToDouble = new Function('input',
+ '"' + (test_id++) + '";return Math.round(input) + -0.0');
+  assertEquals(expect, doRoundToDouble(input));
+  assertEquals(expect, doRoundToDouble(input));
+  assertEquals(expect, doRoundToDouble(input));
+  %OptimizeFunctionOnNextCall(doRoundToDouble);
+  assertEquals(expect, doRoundToDouble(input));
 }

 testRound(0, 0);

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to