mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-27 06:43:19 +08:00
Round a loop bound to an integer before taking the counter's offset off it
- Narrowing `x + c < b` and its other comparison forms subtracted the offset from the bound in floating point. A fractional bound could then round across an integer: `x + 2147483648 > -(2 ** -60)` gave the minimum -2147483647, which leaves out -2147483648. The bound now rounds to an integer first, and the offset comes off in exact integer arithmetic. Integer bounds give the same facts as before. - A unit test covers that comparison.
This commit is contained in:
@@ -941,6 +941,25 @@ test("a counter plus or minus a literal compared with a bound narrows the counte
|
||||
expect(gRanges.get(wide)).toEqual({ min: 0, max: 100 });
|
||||
});
|
||||
|
||||
test("an offset comes off a fractional bound after the bound rounds to an integer", () => {
|
||||
// x = unknown | 0; if (x + 2147483648 > -(2 ** -60)) observe(x): the sum is
|
||||
// an integer, so the condition holds exactly when x + 2^31 >= 0. Subtracting
|
||||
// the offset from the fractional bound first would round to -2^31 and drop
|
||||
// x = -2^31 from the range.
|
||||
const kept = ref();
|
||||
const f = fn([
|
||||
assign(bin("|", ref("unknown"), num(0))),
|
||||
{
|
||||
loc,
|
||||
kind: "if",
|
||||
cond: condition(">", bin("+", ref(), num(2147483648)), num(-(2 ** -60))),
|
||||
then: [observe(kept)],
|
||||
else_: null,
|
||||
},
|
||||
]);
|
||||
expect(analyzeIntegerRanges(f).get(kept)).toEqual({ min: -2147483648, max: 2147483647 });
|
||||
});
|
||||
|
||||
test("whole-program slot facts seed parameters, field reads and call results", () => {
|
||||
const field: IrExpr = {
|
||||
loc,
|
||||
|
||||
@@ -560,7 +560,6 @@ export function analyzeIntegerRanges(fn: IrFunction, slots: IntegerSlotFacts = {
|
||||
Math.abs(literalBound.max) > 2 ** 52)
|
||||
)
|
||||
return;
|
||||
const boundRange = { min: literalBound.min - offset, max: literalBound.max - offset };
|
||||
if (!truth)
|
||||
op =
|
||||
(
|
||||
@@ -569,18 +568,27 @@ export function analyzeIntegerRanges(fn: IrFunction, slots: IntegerSlotFacts = {
|
||||
string
|
||||
>
|
||||
)[op] ?? "";
|
||||
// The sum `x + c` is an integer, so the bound rounds to an integer first
|
||||
// and the offset comes off after: integer arithmetic below 2^53 is exact,
|
||||
// where subtracting the offset from a fractional bound could round
|
||||
// across an integer.
|
||||
let { min, max } = previous;
|
||||
if (op === "<") max = Math.min(max, Math.ceil(boundRange.max) - 1);
|
||||
else if (op === "<=") max = Math.min(max, Math.floor(boundRange.max));
|
||||
else if (op === ">") min = Math.max(min, Math.floor(boundRange.min) + 1);
|
||||
else if (op === ">=") min = Math.max(min, Math.ceil(boundRange.min));
|
||||
if (op === "<") max = Math.min(max, Math.ceil(literalBound.max) - 1 - offset);
|
||||
else if (op === "<=") max = Math.min(max, Math.floor(literalBound.max) - offset);
|
||||
else if (op === ">") min = Math.max(min, Math.floor(literalBound.min) + 1 - offset);
|
||||
else if (op === ">=") min = Math.max(min, Math.ceil(literalBound.min) - offset);
|
||||
else if (op === "===") {
|
||||
min = Math.max(min, boundRange.min);
|
||||
max = Math.min(max, boundRange.max);
|
||||
} else if (op === "!==" && boundRange.min === boundRange.max) {
|
||||
min = Math.max(min, Math.ceil(literalBound.min) - offset);
|
||||
max = Math.min(max, Math.floor(literalBound.max) - offset);
|
||||
} else if (
|
||||
op === "!==" &&
|
||||
literalBound.min === literalBound.max &&
|
||||
Number.isInteger(literalBound.min)
|
||||
) {
|
||||
// Excluding one value narrows a range that ends at it.
|
||||
if (min === boundRange.min) min += 1;
|
||||
else if (max === boundRange.max) max -= 1;
|
||||
const excluded = literalBound.min - offset;
|
||||
if (min === excluded) min += 1;
|
||||
else if (max === excluded) max -= 1;
|
||||
}
|
||||
if (min <= max && Number.isInteger(min) && Number.isInteger(max))
|
||||
facts.set(target.localId, { min, max });
|
||||
|
||||
Reference in New Issue
Block a user