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
This commit is contained in:
alexandre.rames@arm.com 2014-04-30 13:38:00 +00:00
parent 2008b820dc
commit 67ea9e4b42
11 changed files with 195 additions and 27 deletions

View 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;

View 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());

View 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:
LMathRound(LOperand* value, LOperand* temp1)
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:
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")
};

View 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);

View 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,

View 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;

View 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() {

View 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() {

View 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() {

View 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() {

View 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);