From 67ea9e4b42dc6911b271ad523b1a31def1375bf1 Mon Sep 17 00:00:00 2001 From: "alexandre.rames@arm.com" Date: Wed, 30 Apr 2014 13:38:00 +0000 Subject: [PATCH] ARM64: Generate optimized code for Math.floor and Math.round with double outputs. 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 | 1 + src/arm64/lithium-arm64.cc | 28 +++++++++++++---------- src/arm64/lithium-arm64.h | 39 ++++++++++++++++++++++++------- src/arm64/lithium-codegen-arm64.cc | 47 +++++++++++++++++++++++++++++++++----- src/hydrogen-instructions.cc | 43 ++++++++++++++++++++++++++++++++++ src/hydrogen-instructions.h | 18 ++++++++++++++- test/mjsunit/math-floor-part1.js | 9 ++++++++ test/mjsunit/math-floor-part2.js | 9 ++++++++ test/mjsunit/math-floor-part3.js | 9 ++++++++ test/mjsunit/math-floor-part4.js | 9 ++++++++ test/mjsunit/math-round.js | 10 ++++++++ 11 files changed, 195 insertions(+), 27 deletions(-) diff --git a/src/arm64/constants-arm64.h b/src/arm64/constants-arm64.h index 377a017..1ce4c42 100644 --- a/src/arm64/constants-arm64.h +++ b/src/arm64/constants-arm64.h @@ -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; diff --git a/src/arm64/lithium-arm64.cc b/src/arm64/lithium-arm64.cc index bfc9029..a2c3a32 100644 --- a/src/arm64/lithium-arm64.cc +++ b/src/arm64/lithium-arm64.cc @@ -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()); diff --git a/src/arm64/lithium-arm64.h b/src/arm64/lithium-arm64.h index 1fc831f..e60dc05 100644 --- a/src/arm64/lithium-arm64.h +++ b/src/arm64/lithium-arm64.h @@ -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") }; diff --git a/src/arm64/lithium-codegen-arm64.cc b/src/arm64/lithium-codegen-arm64.cc index 3ecd240..bc57bc9 100644 --- a/src/arm64/lithium-codegen-arm64.cc +++ b/src/arm64/lithium-codegen-arm64.cc @@ -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); diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index 7e377e7..9e29ff7 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -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, diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 155ecc4..98b6fa2 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -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; diff --git a/test/mjsunit/math-floor-part1.js b/test/mjsunit/math-floor-part1.js index b57b3e2..a4fca7c 100644 --- a/test/mjsunit/math-floor-part1.js +++ b/test/mjsunit/math-floor-part1.js @@ -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() { diff --git a/test/mjsunit/math-floor-part2.js b/test/mjsunit/math-floor-part2.js index b6d51b2..d9bc410 100644 --- a/test/mjsunit/math-floor-part2.js +++ b/test/mjsunit/math-floor-part2.js @@ -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() { diff --git a/test/mjsunit/math-floor-part3.js b/test/mjsunit/math-floor-part3.js index db25923..0b0e687 100644 --- a/test/mjsunit/math-floor-part3.js +++ b/test/mjsunit/math-floor-part3.js @@ -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() { diff --git a/test/mjsunit/math-floor-part4.js b/test/mjsunit/math-floor-part4.js index c633623..94adc45 100644 --- a/test/mjsunit/math-floor-part4.js +++ b/test/mjsunit/math-floor-part4.js @@ -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() { diff --git a/test/mjsunit/math-round.js b/test/mjsunit/math-round.js index bf59069..12a9265 100644 --- a/test/mjsunit/math-round.js +++ b/test/mjsunit/math-round.js @@ -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); -- 2.7.4