alexcrichton opened issue #14323:
This test:
test interpret test run set opt_level=speed target x86_64 target aarch64 ;; A load with no alias region may alias any region, so it must observe a ;; preceding store that carries an alias region. Alias analysis instead keys ;; the regionless load on `last_fence` only and forwards the value of the ;; earlier region0 load (v1 = 1), dropping the intervening store of 42. function %regionless_load_after_region_store(i64) -> i32 { ss0 = explicit_slot 4 region0 = 0 "heap" block0(v0: i64): v9 = stack_addr.i64 ss0 v8 = iconst.i32 1 store notrap v8, v9 v1 = load.i32 notrap region0 v9 v2 = iconst.i32 42 store notrap region0 v2, v9 v3 = load.i32 notrap v9 v4 = iadd v3, v1 return v4 } ; run: %regionless_load_after_region_store(0) == 43fails with:
$ cargo run -p cranelift-tools test ./regionless-load.clif Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s Running `/home/alex/code/wasmtime2/target/debug/clif-util test ./regionless-load.clif` [2026-09-11T21:06:41Z ERROR cranelift_filetests::concurrent] FAIL: run FAIL ./regionless-load.clif: run Caused by: Failed test: run: %regionless_load_after_region_store(0) == 43, actual: 2 1 tests Error: 1 failureand a similar test case:
test interpret test run set opt_level=speed target x86_64 function %dse(i64) -> i32 { ss0 = explicit_slot 4 region0 = 0 "heap" block0(v0: i64): v9 = stack_addr.i64 ss0 v2 = iconst.i32 42 store notrap region0 v2, v9 v3 = load.i32 notrap v9 v5 = iconst.i32 7 store notrap region0 v5, v9 return v3 } ; run: %dse(0) == 42fails with:
$ cargo run -p cranelift-tools test ./regionless-load-dse.clif Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s Running `/home/alex/code/wasmtime2/target/debug/clif-util test ./regionless-load-dse.clif` [2026-09-11T21:07:29Z ERROR cranelift_filetests::concurrent] FAIL: run FAIL ./regionless-load-dse.clif: run Caused by: Failed test: run: %dse(0) == 42, actual: -2147474110 1 tests Error: 1 failurea full llm-generated report is here if that's useful. I'm relatively certain that this is not UB and the tests here are correct, but I'm not 100% certain. If this is IR-level UB then this can just be closed.
cc @fitzgen
alexcrichton added the cranelift label to Issue #14323.
agourakis82 commented on issue #14323:
Opened a fix: https://github.com/bytecodealliance/wasmtime/pull/$(gh pr list --repo bytecodealliance/wasmtime --head agourakis82:users/agourakis/cranelift-regionless-load-alias-fence --json number -q '.[0].number')
Root cause: regionless loads were keyed/observed only via
last_fence, while named-region stores update onlyregions[r]. That let forwarding/DSE ignore an interveningregion0store that a later regionless load can still see.The PR makes regionless loads meet all region slots for versioning and observe all region stores.
agourakis82 commented on issue #14323:
Concrete PR link: https://github.com/bytecodealliance/wasmtime/pull/14373
Root cause: regionless loads were keyed/observed only via
last_fence, while named-region stores update onlyregions[r]. That let forwarding/DSE ignore an interveningregion0store that a later regionless load can still see.The PR makes regionless loads meet all region slots for versioning and observe all region stores.
cfallin commented on issue #14323:
(Sorry, just saw this because of the recent comments bumping it)
In my understanding at least, "no region" is a separate region than a concrete region. The definition here is ambiguous, so happy to discuss at the Cranelift meeting and we can pin it down one way or another.
(I don't care too strongly either way, but I believe it's a little clearer to think of it in the above way, i.e. it's a simpler mental model not to have special "globally aliasing" operations; it fits how we use regions in Wasmtime just fine; and it is simpler and less error-prone to implement.)
agourakis82 commented on issue #14323:
Thanks @cfallin for the clarification.
Agreed the docs are ambiguous today. I opened #14374-adjacent work as #14373 under Alex’s reading; happy to park/close that PR if the meeting settles on “no region is a separate disjoint region” (which would make these tests invalid CLIF rather than an alias bug).
Will follow whatever invariant you all pin down.
agourakis82 commented on issue #14323:
Design pin-down attempt (store/load asymmetry)
@cfallin @fitzgen @alexcrichton — trying to make the meeting discussion cheap by writing down what the code already does.
Today, regionless stores and regionless loads are not symmetric:
Regionless store → fence / globally aliasing (in tree):
rust // A store with no memflags, and therefore no alias region, may alias // any region, so treat it like a fence.
And named-region loads already observelast_fencebecause regionless stores may alias them.Regionless load → only
last_fence/ "other" (in tree before #14373):
so a laterstore region0does not invalidate / get observed by a regionless load to the same address — which is exactly Alex's repro.So we currently have two half-models at once:
- stores: absence of region = may alias anything
- loads: absence of region = private "other" bucket
Two coherent end-states
Model Regionless store Regionless load Diff size Fits Wasmtime usage? G – globally-aliasing absence (Alex / #14373) fence (already) observe+version over all regions small, load-side only conservative; safe if a frontend forgets a tag D – disjoint other (Chris's preference) only touch "other" (must stop being a fence) only observe "other" (today's load behavior) larger: demote regionless stores from fence simpler mental model if frontends never omit tags on real aliases I don't want to force G. But I also don't think D is "what the code means today" — today's store path already chose G for writes.
Proposal
- Explicitly pick G or D in the meeting / here.
- If G: keep #14373 (maybe with doc wording tightened to "absence of region means may-alias-all", matching the store comment).
- If D: close #14373, change regionless stores to non-fence "other", update
no-region.clif/ docs, and treat Alex's tests as invalid CLIF under the frontend invariant.Happy to implement whichever side you pin. My only strong preference is ending the split-brain between load and store.
cfallin commented on issue #14323:
// A store with no memflags, and therefore no alias region, may alias // any region, so treat it like a fence.Interesting -- for whatever it's worth, this appears to be a behavior change with the new alias analysis work. Looking at the code as of here (Jan of this year), which is the original fixed-four-region design, no-region is explicitly a separate region when handling stores (and loads are consistent with this).
agourakis82 commented on issue #14323:
Update: Model D is falsified by an in-tree runtest
I tried implementing Chris’s Model D locally (regionless = disjoint
other, demote regionless stores from fence). It failsalias/issue-13508.clif:store notrap v3, v0 ;; regionless — comment says "clobbers cache" ... v8 = load.i32 region0 v0 ;; must NOT reuse the pre-store cached value ; run: %g() == [-1, -1, 42] ;; the 42 comes from the regionless storeThat test’s run expectation requires a regionless store to be visible to a later named-region load of the same address. Under Model D that result is impossible without calling the IR invalid — but the test is intentionally valid CLIF with a concrete semantic check.
So the code already committed to Model G for stores. The remaining bug in
#14323is just the load-side asymmetry.Conclusion: keep Model G (this PR): regionless means may-alias-all on both loads and stores. Model D would require rewriting
issue-13508and changing long-standing store-as-fence behavior.@cfallin @fitzgen — given
issue-13508, are you OK locking Model G as the invariant and taking#14373?
cfallin commented on issue #14323:
Well, the test encodes the new semantics. I wouldn't say that that "falsifies" the choice. If we make the other choice, we write a test that encodes those semantics. The test appears to have been added as part of the new alias analysis work (and I even approved it and missed this change, oops!). So I think I'd still like to have the discussion, especially since this diverges from what we originally had.
(As an aside, please (i) don't use AI to write comments, and (ii) let's not attach design points to specific people; it distracts from the technical points and the real reasons we might prefer one or the other. Let's say "explicit no-region" and "no-region as fence", with the original semantics being the former and current semantics being the latter.)
agourakis82 commented on issue #14323:
Fair point on the test — it encodes the current semantics, it doesn’t prove them. And thanks for the pointer to the January code; I hadn’t checked that the original fixed-four-region path treated no-region as its own store bucket.
Happy to wait for the meeting. I’ll use “explicit no-region” vs “no-region as fence” from here.
If the outcome is explicit no-region (restore the January behavior for stores, keep loads matching), I can rework #14373 that way instead of extending fence-to-loads. If it’s no-region as fence, the current patch direction stays.
Sorry about the earlier tone/shape of the comments.
agourakis82 edited a comment on issue #14323:
Opened #14373 for this.
agourakis82 edited a comment on issue #14323:
PR: https://github.com/bytecodealliance/wasmtime/pull/14373
Before the patch, a regionless load only looked at
last_fence, so it could miss a laterregion0store to the same address.
agourakis82 edited a comment on issue #14323:
Thanks. Docs are ambiguous either way.
#14373 follows the reading in the issue report. If the meeting lands on explicit no-region, I can close/rework it.
agourakis82 edited a comment on issue #14323:
One concrete asymmetry in the current code:
- regionless store is treated as a fence (“may alias any region”)
- regionless load only keys/observes
last_fenceSo loads and stores disagree today. The two clean options seem to be:
- no-region as fence — make loads match the current store behavior (#14373)
- explicit no-region — restore the older store behavior (own bucket), keep loads as they are
I’m fine implementing either once that’s pinned down.
agourakis82 edited a comment on issue #14323:
I did try the explicit-no-region store change locally. It breaks
alias/issue-13508.clifunder the current expectations ([-1,-1,42]), because that runtest wants a regionless store to be visible to a later named-region load.That doesn’t settle the design — as noted below, the test encodes the newer semantics. Just recording what I hit when I tried the other direction.
agourakis82 edited a comment on issue #14323:
Fair point on the test — it encodes the current semantics, it doesn’t prove them. Thanks for the January pointer; I hadn’t checked that the original path treated no-region as its own store bucket.
I’ll wait for the meeting and stick to “explicit no-region” vs “no-region as fence”.
If it’s explicit no-region, I can rework #14373 that way. If it’s no-region as fence, the current direction stays.
Sorry about the earlier comments.
alexcrichton commented on issue #14323:
I've personally got no horse in this race. This was an LLM-found issue which smelled fishy to me so I opened an issue, but if the conclusion is that this is working as intended then I have no objection to just closing this.
fitzgen commented on issue #14323:
I thought that we _always_ treated regionless stores as fences that alias everything, and regionless loads as limited to the implicit "anonymous" region, and I've been somewhat bending over backwards to preserve those semantics. But given this comment it seems like I misunderstood the existing semantics and made all this up. If that's the case, then it would be much easier for us long term to simplify the semanticsand stop treating regionless stores as fences and instead just as stores to the "anonymous" region.
agourakis82 commented on issue #14323:
Reworked #14373 to restore explicit no-region (loads and stores both use the disjoint “other” bucket; only real fences touch everything). That matches the older alias-analysis behavior.
Under that rule, the original same-address mixed-tag example is outside the frontend invariant. If that is the meeting outcome, we can close this when the PR lands.
Last updated: Oct 11 2026 at 02:20 UTC