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.