Respect MemoryAccessVolatileMask on OpLoad to prevent forwarding#2
Open
hughperkins wants to merge 3 commits intomainfrom
Open
Respect MemoryAccessVolatileMask on OpLoad to prevent forwarding#2hughperkins wants to merge 3 commits intomainfrom
hughperkins wants to merge 3 commits intomainfrom
Conversation
When an OpLoad carries the Volatile memory access flag, the loaded value must not be forwarded (inlined) at each use site. Without this, SPIRV-Cross re-evaluates the pointer dereference expression at every use, which produces wrong results when the pointed-to memory is modified between the original load and subsequent uses (e.g. an insertion sort shifting elements in the same array accessed via PhysicalStorageBuffer pointers). Made-with: Cursor
Add MSL and Vulkan GLSL test cases that verify an OpLoad with Volatile|Aligned from a PhysicalStorageBuffer pointer is not forwarded. Without the previous commit, the loaded value would be re-dereferenced at each use site. Made-with: Cursor
Co-authored-by: Hans-Kristian Arntzen <post@arntzen-software.no>
duburcqa
approved these changes
Mar 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
SPIRV-Cross has an expression forwarding optimization that inlines loaded values at each use site rather than materializing them into temporaries. For example, given:
Forwarding produces:
rather than:
This is done intentionally to produce cleaner, more readable generated code with fewer temporary variables. When a value is used only once, forwarding is always safe — it's the same single read, just without naming it. When a value is used multiple times, the duplicated dereferences look like extra memory I/O, but for normal storage buffer and uniform loads the downstream shader compiler (Metal, GLSL, etc.) can see that the pointer target hasn't been modified between uses and will CSE (common subexpression eliminate) them back into a single read. So in practice, forwarding doesn't cost extra I/O in the normal case.
However, for
PhysicalStorageBufferpointers (rawdevice T*in Metal,buffer_referencein GLSL), the downstream compiler cannot easily prove the pointer target is stable — especially when stores to the same address space happen between uses. If theOpLoadcarries theVolatilememory access flag, the SPIR-V is explicitly stating that the memory location may change unpredictably. Duplicating the read by forwarding can then produce incorrect results: the two use sites may observe different values from what should be a single load.Fix
When processing an
OpLoad, SPIRV-Cross already decides whether to forward the expression. This patch adds a check: if the load carriesMemoryAccessVolatileMask, forwarding is disabled, forcing the value into a temporary. This ensures the memory is read exactly once, matching SPIR-V semantics.Test plan
volatile-phys-buf-load-no-forward.asm.msl24.comp): volatile load from a PhysicalStorageBuffer pointer used in two stores; reference output shows a temporary rather than duplicated dereferencesvolatile-phys-buf-load-no-forward.nocompat.vk.asm.comp): same shader, verifying the GLSL backend also materializes a temporaryshaders-msl-no-optandshaders-no-opttest suites; no regressionsWhat the test does
The test is a minimal SPIR-V compute shader that:
OpLoadwithVolatile|Alignedfrom the source pointer, producing one valueThe reference output checks that SPIRV-Cross emits one read into a temporary and reuses it:
Without the fix, SPIRV-Cross forwards the load and emits two reads:
The test is a source-level regression test that verifies the shape of the generated code — it does not execute the shader at runtime.