ARM64: Generate optimized code for Math.floor and Math.round with double outputs.
authoralexandre.rames@arm.com <alexandre.rames@arm.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 30 Apr 2014 13:38:00 +0000 (13:38 +0000)
committeralexandre.rames@arm.com <alexandre.rames@arm.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 30 Apr 2014 13:38:00 +0000 (13:38 +0000)
R=jkummerow@chromium.org, ulan@chromium.org

Review URL: https://codereview.chromium.org/258793002

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

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

index 377a017..1ce4c42 100644 (file)
@@ -107,6 +107,7 @@ const unsigned kQuadWordSizeInBytes = kQuadWordSize >> 3;
 // 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;
 
index bfc9029..a2c3a32 100644 (file)
@@ -2445,14 +2445,16 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
       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 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
       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());
index 1fc831f..e60dc05 100644 (file)
@@ -115,11 +115,13 @@ class LCodeGen;
   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 LMathExp V8_FINAL : public LUnaryMathOperation<4> {
 };
 
 
-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 LMathPowHalf V8_FINAL : public LUnaryMathOperation<0> {
 };
 
 
-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")
 };
 
 
index 3ecd240..bc57bc9 100644 (file)
@@ -3761,9 +3761,15 @@ void LCodeGen::DoMathExp(LMathExp* instr) {
 }
 
 
-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::DoPower(LPower* instr) {
 }
 
 
-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 @@ void LCodeGen::DoMathRound(LMathRound* instr) {
   // 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);
index 7e377e7..9e29ff7 100644 (file)
@@ -3698,6 +3698,12 @@ HType HChange::CalculateInferredType() {
 
 
 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.
@@ -4166,6 +4172,43 @@ HInstruction* HUnaryMathOperation::New(
 }
 
 
+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,
                           HValue* context,
                           HValue* left,
index 155ecc4..98b6fa2 100644 (file)
@@ -949,7 +949,7 @@ class HValue : public ZoneObject {
   virtual Representation RepresentationFromInputs() {
     return representation();
   }
-  Representation RepresentationFromUses();
+  virtual Representation RepresentationFromUses();
   Representation RepresentationFromUseRequirements();
   bool HasNonSmiUse();
   virtual void UpdateRepresentation(Representation new_rep,
@@ -2638,6 +2638,7 @@ class HUnaryMathOperation V8_FINAL : public HTemplateInstruction<2> {
   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 @@ class HUnaryMathOperation V8_FINAL : public HTemplateInstruction<2> {
   }
 
  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 @@ class HUnaryMathOperation V8_FINAL : public HTemplateInstruction<2> {
     switch (op) {
       case kMathFloor:
       case kMathRound:
+        if (SupportsFlexibleFloorAndRound()) {
+          SetFlag(kFlexibleRepresentation);
+        } else {
+          set_representation(Representation::Integer32());
+        }
+        break;
       case kMathClz32:
         set_representation(Representation::Integer32());
         break;
index b57b3e2..a4fca7c 100644 (file)
@@ -37,6 +37,15 @@ function testFloor(expect, input) {
   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() {
index b6d51b2..d9bc410 100644 (file)
@@ -37,6 +37,15 @@ function testFloor(expect, input) {
   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() {
index db25923..0b0e687 100644 (file)
@@ -37,6 +37,15 @@ function testFloor(expect, input) {
   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() {
index c633623..94adc45 100644 (file)
@@ -37,6 +37,15 @@ function testFloor(expect, input) {
   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() {
index bf59069..12a9265 100644 (file)
@@ -38,6 +38,16 @@ function testRound(expect, input) {
   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);