Skip to content

i32 fast-path: copying a local seeded with a >2^31 integer literal wraps (third face of #6072) #6319

Description

@proggeramlug

Severity: P1 (silent data corruption)
Found while fixing: #6072 repro 2 (PR #6318)

What

An integer literal beyond i32 range still seeds integer_locals, so a plain copy of such a local is judged "strictly i32-bounded" and gets a wrapping i32 shadow:

let x = 3000000000;
let y = 0;
y = x;
console.log("y:", y);   // node: 3000000000   perry: -1294967296

let z = 0;
z = x + 4;
console.log("z:", z);   // node: 3000000004   perry: 3000000004  (correct — Add isn't strict)

Reproduces on main (v0.5.1258, 0960415ad) and is unaffected by #6258 and #6318 — it is a third, distinct face of #6072's title ("i32 fast-path locals silently wrap at 2^31"), reached through the copy path rather than through x++ (#6258) or a dynamic loop bound (#6318).

Why

collect_integer_let_ids (crates/perry-codegen/src/collectors/i32_locals.rs:615) seeds any Expr::Integer(_) init into integer_locals, regardless of magnitude. is_strictly_i32_bounded_expr's Expr::LocalGet(id) arm then answers known_int_locals.contains(id) — so y = x counts as a strictly-i32-bounded write, y gets an i32 shadow at its Let site, and the write is lowered via lower_expr_as_i32, which ToInt32-wraps the 3e9 double. Reads then prefer the shadow (issue #48), so the wrapped value is what console.log sees.

x itself stays correct: its own Let-site slot is gated on init_in_i32_range. Only locals that copy it inherit the bogus "i32-bounded" judgement.

Fix sketch

Narrow the Expr::Integer seed in collect_integer_let_ids to literals that fit in i32 (i32::try_from(*n).is_ok()). That also removes a latent poison fptosi in the arr.length-hoist and static-i < n loop paths, which seed a counter's i32 shadow from integer_locals membership alone.

Not a drive-by: integer_locals also feeds can_lower_expr_as_i32 / lower_expr_as_i32, so a const SEED = 0x9E3779B9 (2654435769 > INT32_MAX) operand would drop out of the i32 arithmetic chain that the compiler_output_regression image-convolution / FNV-1a gate watches. Wants its own PR with that gate run.

Confidence: high (probe + source).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regression

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions