Bug 1416289 - Part 1: Add Ion-inline support for Math.trunc. r=jandem

This commit is contained in:
André Bargull 2018-05-08 05:41:18 -07:00
parent 5dfcfcd243
commit 80a4563cec
22 changed files with 577 additions and 23 deletions

View File

@ -515,7 +515,7 @@ function rceil_number(i) {
let uceFault_ceil_double = eval(uneval(uceFault).replace('uceFault', 'uceFault_ceil_double'));
function rceil_double(i) {
const x = Math.floor(i + (-1 >>> 0));
const x = Math.ceil(i + (-1 >>> 0));
if (uceFault_ceil_double(i) || uceFault_ceil_double(i))
assertEq(x, 99 + (-1 >>> 0)); /* = i + 2 ^ 32 - 1 */
assertRecoveredOnBailout(x, true);
@ -540,6 +540,24 @@ function rround_double(i) {
return i;
}
var uceFault_trunc_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_trunc_number'));
function rtrunc_number(i) {
var x = Math.trunc(-i - 0.12010799100);
if (uceFault_trunc_number(i) || uceFault_trunc_number(i))
assertEq(x, -i);
assertRecoveredOnBailout(x, true);
return i;
}
let uceFault_trunc_double = eval(uneval(uceFault).replace('uceFault', 'uceFault_trunc_double'));
function rtrunc_double(i) {
const x = Math.trunc(i + (-1 >>> 0));
if (uceFault_trunc_double(i) || uceFault_trunc_double(i))
assertEq(x, 99 + (-1 >>> 0)); /* = i + 2 ^ 32 - 1 */
assertRecoveredOnBailout(x, true);
return i;
}
var uceFault_Char_Code_At = eval(uneval(uceFault).replace('uceFault', 'uceFault_Char_Code_At'));
function rcharCodeAt(i) {
var s = "aaaaa";
@ -1415,6 +1433,8 @@ for (j = 100 - max; j < 100; j++) {
rceil_double(i);
rround_number(i);
rround_double(i);
rtrunc_number(i);
rtrunc_double(i);
rcharCodeAt(i);
rfrom_char_code(i);
rfrom_char_code_non_ascii(i);

View File

@ -38,7 +38,7 @@ function floorI(x) { return Math.floor(x); }
function test() {
// Always run this function in the interpreter.
try {} catch (e) {}
with ({}) {}
for (var i = 0; i < floorDTests.length; i++)
assertEq(floorD(floorDTests[i][0]), floorDTests[i][1]);

View File

@ -36,7 +36,7 @@ function roundI(x) { return Math.round(x); }
function test() {
// Always run this function in the interpreter.
try {} catch (e) {}
with ({}) {}
for (var i = 0; i < roundDTests.length; i++)
assertEq(roundD(roundDTests[i][0]), roundDTests[i][1]);

View File

@ -0,0 +1,76 @@
// Test Math.trunc() for IonMonkey.
// Requires --ion-eager to enter at the top of each loop.
var truncDITests = [
[0.49999999999999997, 0],
[0.5, 0],
[1.0, 1],
[1.5, 1],
[792.8, 792],
[-1.0001, -1],
[-3.14, -3],
[2137483649.5, 2137483649],
[2137483648.5, 2137483648],
[2137483647.1, 2137483647],
[-2147483647.8, -2147483647],
];
var truncDITests_bailout = [
...truncDITests,
// Bailout in bailoutCvttsd2si: https://bugzil.la/1379626#c1
[-2147483648.8, -2147483648],
];
var truncDTests = [
[-0, -0],
[0.49999999999999997, 0],
[0.5, 0],
[1.0, 1],
[1.5, 1],
[792.8, 792],
[-0.1, -0],
[-1.0001, -1],
[-3.14, -3],
[2137483649.5, 2137483649],
[2137483648.5, 2137483648],
[2137483647.1, 2137483647],
[900000000000, 900000000000],
[-0, -0],
[-Infinity, -Infinity],
[Infinity, Infinity],
[NaN, NaN],
[-2147483648.8, -2147483648],
[-2147483649.8, -2147483649],
];
var truncITests = [
[0, 0],
[4, 4],
[-1, -1],
[-7, -7],
[2147483647, 2147483647],
];
// Typed functions to be compiled by Ion.
function truncDI(x) { return Math.trunc(x); }
function truncDI_bailout(x) { return Math.trunc(x); }
function truncD(x) { return Math.trunc(x); }
function truncI(x) { return Math.trunc(x); }
function test() {
// Always run this function in the interpreter.
with ({}) {}
for (var i = 0; i < truncDITests.length; i++)
assertEq(truncDI(truncDITests[i][0]), truncDITests[i][1]);
for (var i = 0; i < truncDITests_bailout.length; i++)
assertEq(truncDI_bailout(truncDITests_bailout[i][0]), truncDITests_bailout[i][1]);
for (var i = 0; i < truncDTests.length; i++)
assertEq(truncD(truncDTests[i][0]), truncDTests[i][1]);
for (var i = 0; i < truncITests.length; i++)
assertEq(truncI(truncITests[i][0]), truncITests[i][1]);
}
for (var i = 0; i < 40; i++)
test();

View File

@ -330,7 +330,37 @@ function acceptFloor() {
}
test(acceptFloor);
function acceptRound() {
// Specialize for floating-point output.
f32[0] = NaN;
f32[1] = Infinity;
f32[2] = -0;
f32[3] = 0.5;
var res = Math.round(f32[0]);
f32[0] = res;
assertFloat32(res, true);
}
test(acceptRound);
function acceptTrunc() {
// Specialize for floating-point output.
f32[0] = NaN;
f32[1] = Infinity;
f32[2] = -0;
f32[3] = 0.5;
var res = Math.trunc(f32[0]);
f32[0] = res;
assertFloat32(res, true);
}
test(acceptTrunc);
function refuseMath() {
var res = Math.log(f32[0]);
f32[0] = res;
assertFloat32(res, false);
var res = Math.log10(f32[0]);
f32[0] = res;
assertFloat32(res, false);
@ -343,6 +373,10 @@ function refuseMath() {
f32[0] = res;
assertFloat32(res, false);
res = Math.exp(f32[0]);
f32[0] = res;
assertFloat32(res, false);
res = Math.expm1(f32[0]);
f32[0] = res;
assertFloat32(res, false);
@ -378,10 +412,6 @@ function refuseMath() {
res = Math.sign(f32[0]);
f32[0] = res;
assertFloat32(res, false);
res = Math.trunc(f32[0]);
f32[0] = res;
assertFloat32(res, false);
}
test(refuseMath);

View File

@ -7514,7 +7514,7 @@ CodeGenerator::visitMathFunctionD(LMathFunctionD* ins)
funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_sign));
break;
case MMathFunction::Trunc:
funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_trunc));
funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_trunc_uncached);
break;
case MMathFunction::Cbrt:
funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_cbrt));
@ -7557,6 +7557,9 @@ CodeGenerator::visitMathFunctionF(LMathFunctionF* ins)
case MMathFunction::Round:
funptr = JS_FUNC_TO_DATA_PTR(void*, math_roundf_impl);
break;
case MMathFunction::Trunc:
funptr = JS_FUNC_TO_DATA_PTR(void*, math_truncf_impl);
break;
case MMathFunction::Ceil:
funptr = JS_FUNC_TO_DATA_PTR(void*, ceilf);
check = CheckUnsafeCallWithABI::DontCheckOther;

View File

@ -656,6 +656,7 @@ class IonBuilder
InliningResult inlineMathRandom(CallInfo& callInfo);
InliningResult inlineMathImul(CallInfo& callInfo);
InliningResult inlineMathFRound(CallInfo& callInfo);
InliningResult inlineMathTrunc(CallInfo& callInfo);
InliningResult inlineMathFunction(CallInfo& callInfo, MMathFunction::Function function);
// String natives.

View File

@ -1424,6 +1424,22 @@ LIRGenerator::visitRound(MRound* ins)
define(lir, ins);
}
void
LIRGenerator::visitTrunc(MTrunc* ins)
{
MIRType type = ins->input()->type();
MOZ_ASSERT(IsFloatingPointType(type));
LInstructionHelper<1, 1, 0>* lir;
if (type == MIRType::Double)
lir = new (alloc()) LTrunc(useRegister(ins->input()));
else
lir = new (alloc()) LTruncF(useRegister(ins->input()));
assignSnapshot(lir, Bailout_Round);
define(lir, ins);
}
void
LIRGenerator::visitNearbyInt(MNearbyInt* ins)
{

View File

@ -159,6 +159,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
return inlineMathImul(callInfo);
case InlinableNative::MathFRound:
return inlineMathFRound(callInfo);
case InlinableNative::MathTrunc:
return inlineMathTrunc(callInfo);
case InlinableNative::MathSin:
return inlineMathFunction(callInfo, MMathFunction::Sin);
case InlinableNative::MathTan:
@ -197,8 +199,6 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
return inlineMathFunction(callInfo, MMathFunction::ATanH);
case InlinableNative::MathSign:
return inlineMathFunction(callInfo, MMathFunction::Sign);
case InlinableNative::MathTrunc:
return inlineMathFunction(callInfo, MMathFunction::Trunc);
case InlinableNative::MathCbrt:
return inlineMathFunction(callInfo, MMathFunction::Cbrt);
@ -1460,6 +1460,61 @@ IonBuilder::inlineMathFRound(CallInfo& callInfo)
return InliningStatus_Inlined;
}
IonBuilder::InliningResult
IonBuilder::inlineMathTrunc(CallInfo& callInfo)
{
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType argType = callInfo.getArg(0)->type();
MIRType returnType = getInlineReturnType();
// Math.trunc(int(x)) == int(x)
if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
callInfo.setImplicitlyUsedUnchecked();
// The int operand may be something which bails out if the actual value
// is not in the range of the result type of the MIR. We need to tell
// the optimizer to preserve this bailout even if the final result is
// fully truncated.
MLimitedTruncate* ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
MDefinition::IndirectTruncate);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
if (IsFloatingPointType(argType)) {
if (returnType == MIRType::Int32) {
callInfo.setImplicitlyUsedUnchecked();
MTrunc* ins = MTrunc::New(alloc(), callInfo.getArg(0));
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
if (returnType == MIRType::Double) {
callInfo.setImplicitlyUsedUnchecked();
MInstruction* ins = nullptr;
if (MNearbyInt::HasAssemblerSupport(RoundingMode::TowardsZero)) {
ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType,
RoundingMode::TowardsZero);
} else {
ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Trunc,
/* cache */ nullptr);
}
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
}
return InliningStatus_NotInlined;
}
IonBuilder::InliningResult
IonBuilder::inlineMathMinMax(CallInfo& callInfo, bool max)
{

View File

@ -2269,6 +2269,14 @@ MRound::trySpecializeFloat32(TempAllocator& alloc)
specialization_ = MIRType::Float32;
}
void
MTrunc::trySpecializeFloat32(TempAllocator& alloc)
{
MOZ_ASSERT(type() == MIRType::Int32);
if (EnsureFloatInputOrConvert(this, alloc))
specialization_ = MIRType::Float32;
}
void
MNearbyInt::trySpecializeFloat32(TempAllocator& alloc)
{

View File

@ -7119,7 +7119,7 @@ class MMathFunction
static const char* FunctionName(Function function);
bool isFloat32Commutative() const override {
return function_ == Floor || function_ == Ceil || function_ == Round;
return function_ == Floor || function_ == Ceil || function_ == Round || function_ == Trunc;
}
void trySpecializeFloat32(TempAllocator& alloc) override;
void computeRange(TempAllocator& alloc) override;
@ -7133,6 +7133,7 @@ class MMathFunction
case Ceil:
case Floor:
case Round:
case Trunc:
return true;
default:
return false;
@ -12681,6 +12682,48 @@ class MRound
ALLOW_CLONE(MRound)
};
// Inlined version of Math.trunc(double | float32) -> int32.
class MTrunc
: public MUnaryInstruction,
public FloatingPointPolicy<0>::Data
{
explicit MTrunc(MDefinition* num)
: MUnaryInstruction(classOpcode, num)
{
setResultType(MIRType::Int32);
specialization_ = MIRType::Double;
setMovable();
}
public:
INSTRUCTION_HEADER(Trunc)
TRIVIAL_NEW_WRAPPERS
AliasSet getAliasSet() const override {
return AliasSet::None();
}
bool isFloat32Commutative() const override {
return true;
}
void trySpecializeFloat32(TempAllocator& alloc) override;
#ifdef DEBUG
bool isConsistentFloat32Use(MUse* use) const override {
return true;
}
#endif
bool congruentTo(const MDefinition* ins) const override {
return congruentIfOperandsEqual(ins);
}
MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
bool canRecoverOnBailout() const override {
return true;
}
ALLOW_CLONE(MTrunc)
};
// NearbyInt rounds the floating-point input to the nearest integer, according
// to the RoundingMode.
class MNearbyInt
@ -12741,6 +12784,7 @@ class MNearbyInt
switch (roundingMode_) {
case RoundingMode::Up:
case RoundingMode::Down:
case RoundingMode::TowardsZero:
return true;
default:
return false;

View File

@ -708,6 +708,31 @@ RRound::recover(JSContext* cx, SnapshotIterator& iter) const
return true;
}
bool
MTrunc::writeRecoverData(CompactBufferWriter& writer) const
{
MOZ_ASSERT(canRecoverOnBailout());
writer.writeUnsigned(uint32_t(RInstruction::Recover_Trunc));
return true;
}
RTrunc::RTrunc(CompactBufferReader& reader)
{}
bool
RTrunc::recover(JSContext* cx, SnapshotIterator& iter) const
{
RootedValue arg(cx, iter.read());
RootedValue result(cx);
MOZ_ASSERT(!arg.isObject());
if(!js::math_trunc_handle(cx, arg, &result))
return false;
iter.storeInstructionResult(result);
return true;
}
bool
MCharCodeAt::writeRecoverData(CompactBufferWriter& writer) const
{
@ -965,6 +990,9 @@ MNearbyInt::writeRecoverData(CompactBufferWriter& writer) const
case RoundingMode::Down:
writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
return true;
case RoundingMode::TowardsZero:
writer.writeUnsigned(uint32_t(RInstruction::Recover_Trunc));
return true;
default:
MOZ_CRASH("Unsupported rounding mode.");
}
@ -995,6 +1023,9 @@ MMathFunction::writeRecoverData(CompactBufferWriter& writer) const
case Round:
writer.writeUnsigned(uint32_t(RInstruction::Recover_Round));
return true;
case Trunc:
writer.writeUnsigned(uint32_t(RInstruction::Recover_Trunc));
return true;
case Sin:
case Log:
writer.writeUnsigned(uint32_t(RInstruction::Recover_MathFunction));

View File

@ -76,6 +76,7 @@ namespace jit {
_(Floor) \
_(Ceil) \
_(Round) \
_(Trunc) \
_(CharCodeAt) \
_(FromCharCode) \
_(Pow) \
@ -381,6 +382,14 @@ class RRound final : public RInstruction
MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
};
class RTrunc final : public RInstruction
{
public:
RINSTRUCTION_HEADER_NUM_OP_(Trunc, 1)
MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
};
class RCharCodeAt final : public RInstruction
{
public:

View File

@ -1324,6 +1324,26 @@ CodeGenerator::visitRoundF(LRoundF* lir)
bailoutFrom(&bail, lir->snapshot());
}
void
CodeGenerator::visitTrunc(LTrunc* lir)
{
FloatRegister input = ToFloatRegister(lir->input());
Register output = ToRegister(lir->output());
Label bail;
masm.trunc(input, output, &bail);
bailoutFrom(&bail, lir->snapshot());
}
void
CodeGenerator::visitTruncF(LTruncF* lir)
{
FloatRegister input = ToFloatRegister(lir->input());
Register output = ToRegister(lir->output());
Label bail;
masm.truncf(input, output, &bail);
bailoutFrom(&bail, lir->snapshot());
}
void
CodeGeneratorARM::emitRoundDouble(FloatRegister src, Register dest, Label* fail)
{

View File

@ -4143,6 +4143,121 @@ MacroAssemblerARMCompat::roundf(FloatRegister input, Register output, Label* bai
bind(&fin);
}
void
MacroAssemblerARMCompat::trunc(FloatRegister input, Register output, Label* bail)
{
Label handleZero;
Label handlePos;
Label fin;
compareDouble(input, NoVFPRegister);
// NaN is always a bail condition, just bail directly.
ma_b(bail, Assembler::Overflow);
ma_b(&handleZero, Assembler::Equal);
ma_b(&handlePos, Assembler::NotSigned);
ScratchDoubleScope scratchDouble(asMasm());
// We are in the ]-Inf; 0[ range
// If we are in the ]-1; 0[ range => bailout
loadConstantDouble(-1.0, scratchDouble);
compareDouble(input, scratchDouble);
ma_b(bail, Assembler::GreaterThan);
// We are in the ]-Inf; -1] range: trunc(x) == -floor(-x) and floor can be
// computed with direct truncation here (x > 0).
ma_vneg(input, scratchDouble);
ma_vcvt_F64_U32(scratchDouble, scratchDouble.uintOverlay());
ma_vxfer(scratchDouble.uintOverlay(), output);
ma_neg(output, output, SetCC);
ma_b(bail, NotSigned);
ma_b(&fin);
// Test for 0.0 / -0.0: if the top word of the input double is not zero,
// then it was -0 and we need to bail out.
bind(&handleZero);
as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
as_cmp(output, Imm8(0));
ma_b(bail, NonZero);
ma_b(&fin);
// We are in the ]0; +inf] range: truncation is the path to glory. Since
// it is known to be > 0.0, explicitly convert to a larger range, then a
// value that rounds to INT_MAX is explicitly different from an argument
// that clamps to INT_MAX.
bind(&handlePos);
ma_vcvt_F64_U32(input, scratchDouble.uintOverlay());
ma_vxfer(scratchDouble.uintOverlay(), output);
ma_mov(output, output, SetCC);
ma_b(bail, Signed);
bind(&fin);
}
void
MacroAssemblerARMCompat::truncf(FloatRegister input, Register output, Label* bail)
{
Label handleZero;
Label handlePos;
Label fin;
compareFloat(input, NoVFPRegister);
// NaN is always a bail condition, just bail directly.
ma_b(bail, Assembler::Overflow);
ma_b(&handleZero, Assembler::Equal);
ma_b(&handlePos, Assembler::NotSigned);
// We are in the ]-Inf; 0[ range
// If we are in the ]-1; 0[ range => bailout
{
ScratchFloat32Scope scratch(asMasm());
loadConstantFloat32(-1.f, scratch);
compareFloat(input, scratch);
ma_b(bail, Assembler::GreaterThan);
}
// We are in the ]-Inf; -1] range: trunc(x) == -floor(-x) and floor can be
// computed with direct truncation here (x > 0).
{
ScratchDoubleScope scratchDouble(asMasm());
FloatRegister scratchFloat = scratchDouble.asSingle();
FloatRegister scratchUInt = scratchDouble.uintOverlay();
ma_vneg_f32(input, scratchFloat);
ma_vcvt_F32_U32(scratchFloat, scratchUInt);
ma_vxfer(scratchUInt, output);
ma_neg(output, output, SetCC);
ma_b(bail, NotSigned);
ma_b(&fin);
}
// Test for 0.0 / -0.0: if the top word of the input double is not zero,
// then it was -0 and we need to bail out.
bind(&handleZero);
as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0);
as_cmp(output, Imm8(0));
ma_b(bail, NonZero);
ma_b(&fin);
// We are in the ]0; +inf] range: truncation is the path to glory; Since
// it is known to be > 0.0, explicitly convert to a larger range, then a
// value that rounds to INT_MAX is explicitly different from an argument
bind(&handlePos);
{
// The argument is a positive number,
// that clamps to INT_MAX.
{
ScratchFloat32Scope scratch(asMasm());
ma_vcvt_F32_U32(input, scratch.uintOverlay());
ma_vxfer(VFPRegister(scratch).uintOverlay(), output);
}
ma_mov(output, output, SetCC);
ma_b(bail, Signed);
}
bind(&fin);
}
CodeOffsetJump
MacroAssemblerARMCompat::jumpWithPatch(RepatchLabel* label)
{

View File

@ -1245,6 +1245,8 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
void ceilf(FloatRegister input, Register output, Label* handleNotAnInt);
void round(FloatRegister input, Register output, Label* handleNotAnInt, FloatRegister tmp);
void roundf(FloatRegister input, Register output, Label* handleNotAnInt, FloatRegister tmp);
void trunc(FloatRegister input, Register output, Label* handleNotAnInt);
void truncf(FloatRegister input, Register output, Label* handleNotAnInt);
void clampCheck(Register r, Label* handleNotAnInt) {
// Check explicitly for r == INT_MIN || r == INT_MAX

View File

@ -345,6 +345,18 @@ CodeGenerator::visitRoundF(LRoundF* lir)
MOZ_CRASH("visitRoundF");
}
void
CodeGenerator::visitTrunc(LTrunc* lir)
{
MOZ_CRASH("visitTrunc");
}
void
CodeGenerator::visitTruncF(LTruncF* lir)
{
MOZ_CRASH("visitTruncF");
}
void
CodeGenerator::visitClzI(LClzI* lir)
{

View File

@ -1452,6 +1452,18 @@ CodeGenerator::visitRoundF(LRoundF* lir)
masm.bind(&end);
}
void
CodeGenerator::visitTrunc(LTrunc* lir)
{
MOZ_CRASH("visitTrunc");
}
void
CodeGenerator::visitTruncF(LTruncF* lir)
{
MOZ_CRASH("visitTruncF");
}
void
CodeGenerator::visitTruncateDToInt32(LTruncateDToInt32* ins)
{

View File

@ -7854,6 +7854,34 @@ class LRoundF : public LInstructionHelper<1, 1, 1>
}
};
// Truncates a double precision number and converts it to an int32.
// Implements Math.trunc().
class LTrunc : public LInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(Trunc)
explicit LTrunc(const LAllocation& num)
: LInstructionHelper(classOpcode)
{
setOperand(0, num);
}
};
// Truncates a single precision number and converts it to an int32.
// Implements Math.trunc().
class LTruncF : public LInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(TruncF)
explicit LTruncF(const LAllocation& num)
: LInstructionHelper(classOpcode)
{
setOperand(0, num);
}
};
// Rounds a double precision number accordingly to mir()->roundingMode(),
// and keeps a double output.
class LNearbyInt : public LInstructionHelper<1, 1, 0>

View File

@ -2356,6 +2356,58 @@ CodeGenerator::visitRoundF(LRoundF* lir)
masm.bind(&end);
}
void
CodeGenerator::visitTrunc(LTrunc* lir)
{
FloatRegister input = ToFloatRegister(lir->input());
Register output = ToRegister(lir->output());
Label bailout, lessThanMinusOne;
// Bail on ]-1; -0] range
{
ScratchDoubleScope scratch(masm);
masm.loadConstantDouble(-1, scratch);
masm.branchDouble(Assembler::DoubleLessThanOrEqualOrUnordered, input,
scratch, &lessThanMinusOne);
}
// Test for remaining values with the sign bit set, i.e. ]-1; -0]
masm.vmovmskpd(input, output);
masm.branchTest32(Assembler::NonZero, output, Imm32(1), &bailout);
bailoutFrom(&bailout, lir->snapshot());
// x <= -1 or x >= +0, truncation is the way to go.
masm.bind(&lessThanMinusOne);
bailoutCvttsd2si(input, output, lir->snapshot());
}
void
CodeGenerator::visitTruncF(LTruncF* lir)
{
FloatRegister input = ToFloatRegister(lir->input());
Register output = ToRegister(lir->output());
Label bailout, lessThanMinusOne;
// Bail on ]-1; -0] range
{
ScratchFloat32Scope scratch(masm);
masm.loadConstantFloat32(-1.f, scratch);
masm.branchFloat(Assembler::DoubleLessThanOrEqualOrUnordered, input,
scratch, &lessThanMinusOne);
}
// Test for remaining values with the sign bit set, i.e. ]-1; -0]
masm.vmovmskps(input, output);
masm.branchTest32(Assembler::NonZero, output, Imm32(1), &bailout);
bailoutFrom(&bailout, lir->snapshot());
// x <= -1 or x >= +0, truncation is the way to go.
masm.bind(&lessThanMinusOne);
bailoutCvttss2si(input, output, lir->snapshot());
}
void
CodeGenerator::visitNearbyInt(LNearbyInt* lir)
{

View File

@ -1357,13 +1357,6 @@ js::math_hypot_handle(JSContext* cx, HandleValueArray args, MutableHandleValue r
return true;
}
double
js::math_trunc_impl(MathCache* cache, double x)
{
AutoUnsafeCallWithABI unsafe;
return cache->lookup(fdlibm::trunc, x, MathCache::Trunc);
}
double
js::math_trunc_uncached(double x)
{
@ -1371,10 +1364,34 @@ js::math_trunc_uncached(double x)
return fdlibm::trunc(x);
}
float
js::math_truncf_impl(float x)
{
AutoUnsafeCallWithABI unsafe;
return fdlibm::truncf(x);
}
bool
js::math_trunc_handle(JSContext* cx, HandleValue v, MutableHandleValue r)
{
double x;
if (!ToNumber(cx, v, &x))
return false;
r.setNumber(math_trunc_uncached(x));
return true;
}
bool
js::math_trunc(JSContext* cx, unsigned argc, Value* vp)
{
return math_function<math_trunc_impl>(cx, argc, vp);
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() == 0) {
args.rval().setNaN();
return true;
}
return math_trunc_handle(cx, args[0], args.rval());
}
static double sign(double x)

View File

@ -26,7 +26,7 @@ class MathCache
enum MathFuncId {
Zero,
Sin, Cos, Tan, Sinh, Cosh, Tanh, Asin, Acos, Atan, Asinh, Acosh, Atanh,
Sqrt, Log, Log10, Log2, Log1p, Exp, Expm1, Cbrt, Trunc, Sign
Sqrt, Log, Log10, Log2, Log1p, Exp, Expm1, Cbrt, Sign
};
private:
@ -416,12 +416,15 @@ math_atanh_impl(MathCache* cache, double x);
extern double
math_atanh_uncached(double x);
extern double
math_trunc_impl(MathCache* cache, double x);
extern double
math_trunc_uncached(double x);
extern float
math_truncf_impl(float x);
extern bool
math_trunc_handle(JSContext* cx, HandleValue v, MutableHandleValue r);
extern double
math_sign_impl(MathCache* cache, double x);