Stream: git-wasmtime

Topic: wasmtime / issue #14572 Cranelift: CLIF printer drops `Me...


view this post on Zulip Wasmtime GitHub notifications bot (Oct 06 2026 at 15:18):

fitzgen opened issue #14572:

The AtomicRmw and AtomicCas arms of write_operands in
cranelift/codegen/src/write.rs ignore the instruction's flags. The
printed text therefore loses the endianness, trap code, aligned/readonly
and alias region. The parser accepts all of these flags, so printed CLIF does
not round-trip. For example, a big RMW reparses as native-endian.

.clif Test Case

test interpret

function %f() -> i32 {
    ss0 = explicit_slot 4
block0:
    v0 = stack_addr.i64 ss0
    v1 = iconst.i32 0x01020304
    store v1, v0
    v2 = iconst.i32 0x10
    v3 = atomic_rmw.i32 big add v0, v2
    v4 = load.i32 v0
    return v4
}
; run: %f() == 0x11020304

Steps to Reproduce

clif-util test test.clif
clif-util cat test.clif | grep atomic_rmw

Expected Results

The test passes, and clif-util cat prints:

v3 = atomic_rmw.i32 big add v0, v2

Actual Results

The test passes, but clif-util cat prints:

v3 = atomic_rmw.i32 add v0, v2  ; v2 = 16

That text has no big flag. When it is parsed and interpreted, it returns
0x01020314 instead of 0x11020304.

Versions and Environment

Cranelift version or commit: 73b04cff33

Operating system: macOS 15.8.1

Architecture: aarch64

view this post on Zulip Wasmtime GitHub notifications bot (Oct 06 2026 at 15:18):

fitzgen added the bug label to Issue #14572.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 06 2026 at 15:18):

fitzgen added the cranelift label to Issue #14572.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 06 2026 at 15:18):

fitzgen commented on issue #14572:

<details><summary>Full LLM report</summary>

CLIF printer drops all MemFlags on atomic_rmw and atomic_cas

Date 2026-10-05
Wasmtime commit 73b04cff3317d1e308866eb24359483ac6116669 (main)
Host macOS 15.8.1 (Darwin 24.6.0), aarch64-apple-darwin
Model Claude Opus 5.5 (claude-opus-5-5)
Component cranelift/codegen/src/write.rs (write_operands)
Class CLIF text does not round-trip: the printed function has different semantics
Severity Low. No effect on compiled code. Printed CLIF is wrong, which misleads debugging, clif-util, test reductions and bugpoint output. The missing alias region, trap code, endianness, notrap and readonly flags are exactly what an alias-analysis audit needs to see.

Summary

cranelift/codegen/src/write.rs:397-398:

AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]),
AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),

Both arms ignore the instruction's flags field. Every other memory format
prints dfg.mem_flags[flags], including the neighbouring
LoadNoOffset/StoreNoOffset, which cover atomic_load and
atomic_store. As a result, the printed form of atomic_rmw and atomic_cas
loses all of the following:

The reader parses all of these flags for both instructions, so the text no
longer round-trips. The reparsed function is a different program: a big
RMW silently becomes native-endian. A trapping RMW becomes one with the
default trap code, and a notrap RMW becomes one that can trap. A
region-tagged RMW loses its region, which changes what alias analysis may
assume when the text is fed back in.

The omission has been there since these formats were first printed. The last
change to these lines was 18d9685eb3 (2022, "Fix pretty print of
atomic_rmw clif ops"). Wasmtime emits atomic_rmw/atomic_cas with alias
regions for every Wasm atomic RMW, so CLIF dumps of those instructions (for
example via WASMTIME_LOG or clif-util wasm) omit the region and trap code
that the compiler actually uses. No tests/disas expectation currently
contains these instructions, which is part of why this went unnoticed.

Existing precise-output filetest expectations already bake in the flag-less
form, so those tests cannot pin an atomic's region or trap code. Examples are
cranelift/filetests/filetests/alias/not-dead-fence-between.clif:87
(atomic_cas v0, v2, v3) and the atomics in alias/fence.clif. Backend error
messages misprint the instruction as well. Pulley reports a big RMW that it
refuses to lower as v4 = atomic_rmw.i32 add v1, v3.

A reparsed atomic that has lost its region is not just cosmetic. The alias
analysis treats a store with no region as a fence, so it reasons about the
reparsed function differently from the original.

Reproduction

original.clif uses a big-endian atomic_rmw.i32 big add on a stack slot
holding 0x01020304. roundtrip.clif is the output of
clif-util cat original.clif, with the same ; run: line.

$ clif-util test original.clif
1 tests                                   # passes: [0x04030201, 0x11020304]
$ clif-util cat original.clif | grep atomic
    v4 = atomic_rmw.i32 add v1, v3  ; v3 = 16      # `big` is gone
$ clif-util test roundtrip.clif
    Failed test: run: %f() == [67305985, 285344516], actual: [16909060, 16909076]
Error: 1 failure

all-atomics.clif shows every flag being dropped:

$ clif-util cat all-atomics.clif
    v3 = atomic_cas v0, v1, v2                       # input: atomic_cas.i32 user5 big region0
    v4 = atomic_rmw.i32 xchg v0, v1                  # input: atomic_rmw.i32 notrap little region0
    v5 = atomic_load.i32 user5 big region0 v0        # printed correctly
    atomic_store user5 big region0 v1, v0            # printed correctly

Suggested fix

AtomicRmw { op, args, flags, .. } => {
    write!(w, "{} {} {}, {}", dfg.mem_flags[flags], op, args[0], args[1])
}
AtomicCas { args, flags, .. } => {
    write!(w, "{} {}, {}, {}", dfg.mem_flags[flags], args[0], args[1], args[2])
}

Match the leading-space convention of the other MemFlags arms; the
MemFlags Display already prints its own leading spaces. Then re-bless any filetest
expectations that print atomic_rmw or atomic_cas. A parse→print→parse round-trip test over all memory formats
would prevent recurrences.

</details>

view this post on Zulip Wasmtime GitHub notifications bot (Oct 10 2026 at 04:33):

cfallin closed issue #14572:

The AtomicRmw and AtomicCas arms of write_operands in
cranelift/codegen/src/write.rs ignore the instruction's flags. The
printed text therefore loses the endianness, trap code, aligned/readonly
and alias region. The parser accepts all of these flags, so printed CLIF does
not round-trip. For example, a big RMW reparses as native-endian.

.clif Test Case

test interpret

function %f() -> i32 {
    ss0 = explicit_slot 4
block0:
    v0 = stack_addr.i64 ss0
    v1 = iconst.i32 0x01020304
    store v1, v0
    v2 = iconst.i32 0x10
    v3 = atomic_rmw.i32 big add v0, v2
    v4 = load.i32 v0
    return v4
}
; run: %f() == 0x11020304

Steps to Reproduce

clif-util test test.clif
clif-util cat test.clif | grep atomic_rmw

Expected Results

The test passes, and clif-util cat prints:

v3 = atomic_rmw.i32 big add v0, v2

Actual Results

The test passes, but clif-util cat prints:

v3 = atomic_rmw.i32 add v0, v2  ; v2 = 16

That text has no big flag. When it is parsed and interpreted, it returns
0x01020314 instead of 0x11020304.

Versions and Environment

Cranelift version or commit: 73b04cff33

Operating system: macOS 15.8.1

Architecture: aarch64

view this post on Zulip Wasmtime GitHub notifications bot (Oct 10 2026 at 04:33):

cfallin commented on issue #14572:

Closed by #14621.


Last updated: Oct 11 2026 at 04:10 UTC