Stream: git-wasmtime

Topic: wasmtime / issue #1598 Add volatile flag to MemFlags


view this post on Zulip Wasmtime GitHub notifications bot (May 04 2022 at 20:44):

cfallin labeled issue #1598:

Add Volatile flag to MemFlags.

Benefit

When some optimization pass knows about load/store type (is it volatile or not?) then it's possible to do a lot of optimizations. One of it is memory to register promotion. I tried to implement mem2reg pass there cranelift-mem2reg and this pass works but in some cases it fails e.g this simple C code:

// compiled with rcc
typedef struct Foo {
    int x;
    int y;
} Foo;

int main() {
    Foo foo;
    foo.y = 42;
}
Message:  definition error: Compilation error: Verifier errors
note: while compiling function u0:0() -> i32 system_v {
    ss0 = explicit_slot 8

block0:
    v1 = iconst.i64 4
    v2 = iadd.i64 v0, v1
    v3 = iconst.i32 42
    store v3, v2
    v4 = iconst.i32 0
    return v4
}

All loads and stores might be marked as volatile and mem2reg will not optimize struct Foo into registers.
Volatile flag also can help in SRoA when it's possible to replace allocation on stack with single 32 or 64 bit register.

Implementation

Just add additional enum case in codegen/ir/memflags.rs and add 3 simple functions to MemFlags: volatile() -> Self,is_volatile() -> bool, set_volatile(&mut self).

Alternatives

I do not see any alternatives.

UPD: This might be useful for this issue: Support volatile store/loads

view this post on Zulip Wasmtime GitHub notifications bot (May 04 2022 at 20:45):

cfallin labeled issue #1598:

Add Volatile flag to MemFlags.

Benefit

When some optimization pass knows about load/store type (is it volatile or not?) then it's possible to do a lot of optimizations. One of it is memory to register promotion. I tried to implement mem2reg pass there cranelift-mem2reg and this pass works but in some cases it fails e.g this simple C code:

// compiled with rcc
typedef struct Foo {
    int x;
    int y;
} Foo;

int main() {
    Foo foo;
    foo.y = 42;
}
Message:  definition error: Compilation error: Verifier errors
note: while compiling function u0:0() -> i32 system_v {
    ss0 = explicit_slot 8

block0:
    v1 = iconst.i64 4
    v2 = iadd.i64 v0, v1
    v3 = iconst.i32 42
    store v3, v2
    v4 = iconst.i32 0
    return v4
}

All loads and stores might be marked as volatile and mem2reg will not optimize struct Foo into registers.
Volatile flag also can help in SRoA when it's possible to replace allocation on stack with single 32 or 64 bit register.

Implementation

Just add additional enum case in codegen/ir/memflags.rs and add 3 simple functions to MemFlags: volatile() -> Self,is_volatile() -> bool, set_volatile(&mut self).

Alternatives

I do not see any alternatives.

UPD: This might be useful for this issue: Support volatile store/loads

view this post on Zulip Wasmtime GitHub notifications bot (Mar 21 2026 at 01:50):

wareya commented on issue #1598:

Has there been any movement on this? Something like it is a precondition to implementing any mem2reg-like thing safely, and the alternatives proposed so far seem to be research-project-worthy and hard to implement.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 19 2026 at 15:38):

bjorn3 commented on issue #1598:

This has become a fair bit more important now that Cranelift has introduced memory optimizations.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 19 2026 at 23:28):

sunfishcode edited a comment on issue #1598:

Giving frontends a way to provide explicit memory dependencies, and using that to implement volatile semantics would be cool. If anyone's interested in a designing a system to do this, it'd be good to start with a design document so we can look at how it'll impact compile times in fast-compilation use cases, and other tradeoffs involved.

We may want to do something simpler for now that just addresses volatile, and I have a possible alternative. Traditional volatile flags on regular memory accesses turn out to be very error prone. The recommended solution is to transform volatile into a call or separate opcode, and optionally inline the call after all optimizations. It's now how it systematically eliminates a common class of volatile bugs, however it does add complexity in the backend. So instead, how about the following design:

Add new two instructions to the IR:

- y = volatile_unary(x) - observe the value of x, do side effects, and return a new value
- volatile_rmw(p) - observe the value of *p, do side effects, and then clobber *p.

At a very late stage, volatile_unary would be translated as if it were an identity operation, and volatile_rmw would be translated as if it were a no-op. With those, frontends could then translate a volatile load like this:

   volatile_rmw(p)
   x0 = load(p)
   x = volatile_unary(x0)

and a volatile store like this:

   x0 = volatile_unary(x)
   store(p, x0)
   volatile_rmw(p)

That is, we'd use plain load and store instructions, but boxed in with volatile operations, both in control flow and data flow, to prevent any code motion or redundancy elimination. It'll get chatty, especially in the volatile-atomic case, but volatile is rare so it seems like an ok tradeoff. This should let most of the compiler automatically do the right thing, at low cost in overall complexity. And, volatile_rmw and volatile_unary would be handy besides for crafting compiler unit tests :-).

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

TastyCZ commented on issue #1598:

A concrete data point for this. Since #13806 (dead-store elimination) and #14114 (dead notrap loads aren't lowered), a frontend has no flag combination that keeps a volatile access intact. Even plain loads and stores with the default trapping flags get merged:

function %store_twice(i64) {
block0(v0: i64):
    v1 = iconst.i32 1
    store v1, v0
    v2 = iconst.i32 2
    store v2, v0
    return
}

function %load_twice(i64) -> i32 {
block0(v0: i64):
    v1 = load.i32 v0
    v2 = load.i32 v0
    v3 = iadd v1, v2
    return v3
}

function %store_then_load(i64) -> i32 {
block0(v0: i64):
    v1 = iconst.i32 7
    store v1, v0
    v2 = load.i32 v0
    return v2
}

function %unused_notrap(i64) {
block0(v0: i64):
    v1 = load.i32 notrap v0
    return
}

clif-util compile -p -D --target x86_64 --set opt_level=speed volatile.clif (main at 65e9dcc3e3) gives:

function %store_twice(i64) fast {
block0(v0: i64):
    v2 = iconst.i32 2
    store v2, v0  ; v2 = 2
    return
}

function %load_twice(i64) -> i32 fast {
block0(v0: i64):
    v1 = load.i32 v0
    v3 = iadd v1, v1
    return v3
}

function %store_then_load(i64) -> i32 fast {
block0(v0: i64):
    v1 = iconst.i32 7
    store v1, v0  ; v1 = 7
    return v1  ; v1 = 7
}

%unused_notrap compiles to an empty body, even at opt_level=none.

Each of these is correct for ordinary memory. For MMIO or other memory with side effects they aren't, and the frontend has nothing to say otherwise. rustc_codegen_cranelift is affected: read_volatile/write_volatile are merged, forwarded and dropped at -Copt-level>=1 (rust-lang/rustc_codegen_cranelift#1715).

We implemented the simple version of this issue: a volatile MemFlag (text format volatile) that:

It comes with filetests for alias analysis, the egraph, the verifier and load sinking on x64 and aarch64. Accesses without the flag are unaffected, so it costs nothing for code that doesn't use it.

The volatile_unary/volatile_rmw design suggested above would also work. The flag turned out small: about 130 lines outside the tests, mostly in alias analysis, which is now where memory accesses get merged. If a MemFlag is acceptable, I'm happy to open a PR.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 26 2026 at 20:19):

cfallin commented on issue #1598:

@TastyCZ I think we'd be interested in a volatile memory flag that means "do not change anything about this operation". Can you submit a PR with the change (that it sounds like you already have -- "we implemented ...") and we can review/refine as needed? Thanks!

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

cfallin closed issue #1598:

Add Volatile flag to MemFlags.

Benefit

When some optimization pass knows about load/store type (is it volatile or not?) then it's possible to do a lot of optimizations. One of it is memory to register promotion. I tried to implement mem2reg pass there cranelift-mem2reg and this pass works but in some cases it fails e.g this simple C code:

// compiled with rcc
typedef struct Foo {
    int x;
    int y;
} Foo;

int main() {
    Foo foo;
    foo.y = 42;
}
Message:  definition error: Compilation error: Verifier errors
note: while compiling function u0:0() -> i32 system_v {
    ss0 = explicit_slot 8

block0:
    v1 = iconst.i64 4
    v2 = iadd.i64 v0, v1
    v3 = iconst.i32 42
    store v3, v2
    v4 = iconst.i32 0
    return v4
}

All loads and stores might be marked as volatile and mem2reg will not optimize struct Foo into registers.
Volatile flag also can help in SRoA when it's possible to replace allocation on stack with single 32 or 64 bit register.

Implementation

Just add additional enum case in codegen/ir/memflags.rs and add 3 simple functions to MemFlags: volatile() -> Self,is_volatile() -> bool, set_volatile(&mut self).

Alternatives

I do not see any alternatives.

UPD: This might be useful for this issue: Support volatile store/loads


Last updated: Oct 31 2026 at 23:03 UTC