Source code
Revision control
Copy as Markdown
Other Tools
// Test case for fallible unsigned division and modulus instructions.
//
// Doesn't include test that bailouts are correctly implemented.
// |MBinaryInstruction::unsignedOperands()| calls MustBeUInt32, which only
// treats MUrsh as unsigned when bailouts are disabled.
//
// |MUrsh::collectRangeInfoPreTrunc()| sets |MUrsh::bailoutsDisabled_| if
// the lower bound of the lhs operand is >= 0 and the lower bound of the rhs
// operand is >= 1.
//
// Use |Math.max(v, 0)| below to ensure the operand is non-negative, so that
// the |MUrsh| is marked as bailouts disabled.
// MMod::computeRange sets |MMod::unsigned_| flag.
//
// Range Analysis uses IsUint32Type to check for Uint32 types. IsUint32Type
// doesn't require that |Ursh| is marked as bailouts disabled.
//
// These don't need an explicit bit-or operation to mark as truncated.
function umod_by_constant(x) {
return (x >>> 0) % 3;
}
function umod_by_constant_pow_two(x) {
return (x >>> 0) % 4;
}
function umod(dividend, divisor) {
// Ensure range of |divisor| doesn't include zero.
divisor = Math.min(Math.max(divisor, 1), 10);
// Make sure |IsUint32Type| returns true for both operands.
return (dividend >>> 0) % (divisor >>> 0);
}
// |MMod::computeRange| also marks the operation as unsigned when the lower
// bound of the dividend is >= 0 (and the divisor's range doesn't include zero.)
function umod_by_constant_no_ursh(dividend, divisor) {
// Ensure lower bound of |dividend| range is >= 0.
dividend = Math.max(dividend, 0);
return dividend % 5;
}
function umod_no_ursh(dividend, divisor) {
// Ensure lower bound of |dividend| range is >= 0.
dividend = Math.max(dividend, 0);
// Ensure range of |divisor| doesn't include zero.
divisor = Math.min(Math.max(divisor, 1), 10);
return dividend % divisor;
}
// MMod::truncate sets |MMod::unsigned_| flag. Bit-or is needed to mark as truncated.
function umod_truncate(dividend, divisor) {
dividend = Math.max(dividend, 0);
divisor = Math.max(divisor, 0);
return ((dividend >>> 0) % (divisor >>> 0))|0;
}
// MDiv::truncate sets |MDiv::unsigned_| flag. Bit-or is needed to mark as truncated.
//
// Note: MDiv::computeRange never sets |MDiv::unsigned_|.
function udiv_by_constant(dividend) {
dividend = Math.max(dividend, 0);
return ((dividend >>> 0) / 3)|0;
}
function udiv_by_constant_pow_two(dividend) {
dividend = Math.max(dividend, 0);
return ((dividend >>> 0) / 8)|0;
}
function udiv(dividend, divisor) {
dividend = Math.max(dividend, 0);
divisor = Math.max(divisor, 0);
return ((dividend >>> 0) / (divisor >>> 0))|0;
}
// Don't Ion compile the top-level script.
with ({});
for (let i = 0; i < 100; ++i) {
assertEq(umod_by_constant(i), i % 3);
assertEq(umod_by_constant_pow_two(i), i % 4);
assertEq(umod(i, 1), i % 1);
assertEq(umod(i, 3), i % 3);
assertEq(umod(i, 5), i % 5);
assertEq(umod_by_constant_no_ursh(i), i % 5);
assertEq(umod_no_ursh(i, 1), i % 1);
assertEq(umod_no_ursh(i, 7), i % 7);
assertEq(umod_no_ursh(i, 9), i % 9);
assertEq(umod_truncate(i, 1), i % 1);
assertEq(umod_truncate(i, 6), i % 6);
assertEq(umod_truncate(i, 11), i % 11);
assertEq(udiv_by_constant(i * 3), i);
assertEq(udiv_by_constant_pow_two(i * 8), i);
assertEq(udiv(i * 1, 1), i);
assertEq(udiv(i * 3, 3), i);
assertEq(udiv(i * 5, 5), i);
}