Skip to content

mutate breaks semantics: i32.eq is not associative #2766

Closed

Description

@tamaroning

With --preserve-semantics, the peephole mutator can rewrite with the rule i32.eq-associates:

rewrite!(
"i32.eq-associates";
"(i32.eq ?x (i32.eq ?y ?z))" <=> "(i32.eq (i32.eq ?x ?y) ?z)"
);

This identity does not hold, because i32.eq returns 0 or 1 rather than one of its operands. For example, with x = 1, y = z = 0x80000000:

  • (i32.eq x (i32.eq y z)) = (i32.eq 1 1) = 1
  • (i32.eq (i32.eq x y) z) = (i32.eq 0 0x80000000) = 0

The rule right below it, i64.eq-associates, is ill-typed. The inner i64.eq produces an i32, but the outer i64.eq expects i64 operands. Neither side can appear in a valid module, so it never fires. Even with the types fixed, it would be wrong for the same reason.

rewrite!(
"i64.eq-associates";
"(i64.eq ?x (i64.eq ?y ?z))" <=> "(i64.eq (i64.eq ?x ?y) ?z)"
);

Repro

test.wat:

(module
  (func (export "f") (param i32 i32 i32) (result i32)
    (i32.eq (local.get 0) (i32.eq (local.get 1) (local.get 2)))))
$ wasm-tools parse test.wat -o test.wasm
$ wasm-tools mutate --preserve-semantics -s 211 test.wasm -o mutated.wasm
$ wasmtime run --invoke f test.wasm 1 -2147483648 -2147483648
1
$ wasmtime run --invoke f mutated.wasm 1 -2147483648 -2147483648
0

Suggested fix

Both i32.eq-associates and i64.eq-associates should be removed.

Version

  • wasm-tools 1.261.0 (96376d3 2026-10-08).
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

    wasm-mutateIssues related to the `wasm-mutate` crate

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions