Ignore products that only bound a loop's condition

A setup loop such as `for (i = 0; i < Math.floor(300000 * scale); i++) text.push(word())` multiplies only in its condition; its body does no integer work, so it keeps one copy. Also records the ts7 baseline of the speculative integer reads fixture.
This commit is contained in:
Malte Ubl
2026-10-16 11:40:54 -07:00
parent 945194919b
commit 2c048881c9
3 changed files with 18 additions and 2 deletions
@@ -182,6 +182,9 @@ test("invariants may come from bindings no shadow could be written to", () => {
test("loops that only count and compare keep one copy", () => {
const counter = loop([increment("i")]);
expect(planSpeculativeLoop(counter as never, all)).toBeNull();
// A product in the condition alone is not.
const bounded = loop([increment("i")], bin("<", ref("i"), bin("*", ref("n"), ref("scale"))));
expect(planSpeculativeLoop(bounded as never, all)).toBeNull();
// A variable step (a sieve's `j += i`) is worth a copy.
const sieve = loop([assign("i", bin("+", ref("i"), ref("step")))]);
expect(planSpeculativeLoop(sieve as never, all)?.locals.has("i")).toBe(true);
@@ -259,6 +259,9 @@ export function planSpeculativeLoop(
// double forms cost the most. A loop whose integers only count and compare
// gains nothing measurable from a second copy, so it keeps one.
const multiplied = new Set<string>();
// A product in the condition alone (`i < Math.floor(n * scale)`) does not
// make the body's work integer.
let inCondition = false;
const expr = (e: IrExpr): boolean => {
if (rejected) return false;
@@ -272,7 +275,7 @@ export function planSpeculativeLoop(
else if (e.kind === "bin" && CONSUMING_OPS.has(e.op)) {
consume(e.left);
consume(e.right);
if (e.op === "*" || e.op === "/" || e.op === "%")
if (!inCondition && (e.op === "*" || e.op === "/" || e.op === "%"))
for (const operand of [e.left, e.right])
if (operand.kind === "varRef") multiplied.add(operand.localId);
} else if (e.kind === "arrayGet" || e.kind === "arrayHas" || e.kind === "arrayState")
@@ -321,7 +324,11 @@ export function planSpeculativeLoop(
return everyStmtChild(s, expr, stmt);
};
if (loop.cond) expr(loop.cond);
if (loop.cond) {
inCondition = true;
expr(loop.cond);
inCondition = false;
}
if (loop.kind === "for" && loop.update) stmt(loop.update);
for (const s of loop.body) stmt(s);
if (rejected || nodes > MAX_LOOP_NODES) return null;
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/speculative-integer-reads.ts"
],
"diags": []
}