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:
parent
2008b820dc
commit
67ea9e4b42
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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")
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user