mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-16 13:56:29 +00:00
Bug 1416289 - Part 1: Add Ion-inline support for Math.trunc. r=jandem
This commit is contained in:
parent
5dfcfcd243
commit
80a4563cec
@ -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);
|
||||
|
@ -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]);
|
||||
|
@ -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]);
|
||||
|
76
js/src/jit-test/tests/ion/mathTrunc.js
Normal file
76
js/src/jit-test/tests/ion/mathTrunc.js
Normal 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();
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user