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 Foointo 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.rsand 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
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 Foointo 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.rsand 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
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.
bjorn3 commented on issue #1598:
This has become a fair bit more important now that Cranelift has introduced memory optimizations.
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 ofx, 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_unarywould be translated as if it were an identity operation, andvolatile_rmwwould 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_rmwandvolatile_unarywould be handy besides for crafting compiler unit tests :-).
TastyCZ commented on issue #1598:
A concrete data point for this. Since #13806 (dead-store elimination) and #14114 (dead
notraploads 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_notrapcompiles to an empty body, even atopt_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_volatileare merged, forwarded and dropped at-Copt-level>=1(rust-lang/rustc_codegen_cranelift#1715).We implemented the simple version of this issue: a
volatileMemFlag (text formatvolatile) that:
- inst_predicates: a volatile load has a side effect, so it is lowered even when unused and is never pure for the egraph (no GVN, LICM or rematerialization).
- alias analysis: a volatile access is never replaced by a known value, never removed as a redundant or dead store, never the source of a forwarded value, and never makes another store dead. It acts as a barrier for what is known about memory.
- lowering: a volatile load is never sunk into its user's memory operand, so its count and width stay as written.
- verifier: rejects
volatiletogether withreadonlyorcan_move.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_rmwdesign 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.
cfallin commented on issue #1598:
@TastyCZ I think we'd be interested in a
volatilememory 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!
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 Foointo 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.rsand 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