// 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;
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());
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());
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) \
};
-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")
};
};
-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")
};
}
-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());
}
-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());
// 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);
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.
}
+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,
virtual Representation RepresentationFromInputs() {
return representation();
}
- Representation RepresentationFromUses();
+ virtual Representation RepresentationFromUses();
Representation RepresentationFromUseRequirements();
bool HasNonSmiUse();
virtual void UpdateRepresentation(Representation new_rep,
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_; }
}
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);
switch (op) {
case kMathFloor:
case kMathRound:
+ if (SupportsFlexibleFloorAndRound()) {
+ SetFlag(kFlexibleRepresentation);
+ } else {
+ set_representation(Representation::Integer32());
+ }
+ break;
case kMathClz32:
set_representation(Representation::Integer32());
break;
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() {
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() {
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() {
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() {
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);