Translate ATOMG.CAS (global compare-and-swap): 6 compute programs that did not translate #597

Merged
Joey-Magnus merged 1 commit from fix/global-atomic-cas into main 2026-11-08 16:49:22 +00:00
Joey-Magnus commented 2026-11-08 13:51:09 +00:00 (Migrated from github.com)

Six of OoT's compute programs did not translate on main (a598b9f):

vk: NVN2 program 9a6fe9d7a7ce2007 (stage 5) does not lower at +0x8830: unsupported native opcode (native op 0x3a9, 000073a9 00000008 001ee109 000ea400)

nvdisasm reads it as ATOMG.E.CAS.STRONG.GPU PT, R0, [R0], R8, R9. The same goes for 64978274654e224c, f4867235b77e1107, 3b018f504c4715a1, 9d3e6486df0904bf and 99b6a2a493b3ba10.

Change. A new IR operation carried through every layer, modeled on GlobalAtomicExchange32:

  • lower_to_maxwell.h: 0x3a9 lowers to Maxwell's ATOM.E.CAS Rd, [Ra + offset], Rb, Rb+1 (U32). The game already keeps the comparator and the value as an even pair (R8/R9, R38/R39, R30/R31). Any other pair is first copied to R250/R251. A 64-bit CAS, an odd address register and other modes still fail.
  • Maxwell frontend: ATOM_cas was a not-implemented stub. It now reads its address as ATOM does and emits GlobalAtomicCompareExchange32.
  • IR: GlobalAtomicCompareExchange32 (address, comparator, value) and StorageAtomicCompareExchange32 (binding, offset, comparator, value), both with side effects. META_TABLE and NUM_ARGS grow from 539 to 541.
  • global_memory_to_storage_buffer_pass.cpp: maps the global operation to the storage one (ReplaceAtomic passes the extra operand) and counts it as a write where pointers are followed, like the other 32-bit atomics.
  • SPIR-V: OpAtomicCompareExchange on the storage buffer, plus a fallback function written like the other 32-bit atomics' fallbacks (search the buffers, then the page table where stores follow it).

Tested on an RTX 4070 Ti (NVIDIA 595.99.02), Ubuntu 26.04, Linux build:

  • RX_VK_PREPARE_AGAIN=1: prepared 31985 shader programs (... 7 failed) drops to 1 failed. The one left is GlobalAtomicAddF32 with no SPIR-V fallback (program 499dbfd0721d2858), which is a separate change.
  • nx2/lower_test.py over the six dumped programs: 6 of 6 lower.
  • RX_VK_DUMP_SPV for that run: the six programs and the other 3812 dumped all pass spirv-val --target-env vulkan1.3 --scalar-block-layout. The fallback's OpAtomicCompareExchange passes the value, then the comparator, in SPIR-V's order.
  • Played from a save into Kokiri Forest (Link's house, out the door, Saria's introduction) at 60 fps with nothing new in the log.
  • tests/test_lower_atomg_cas.py (added to source-regressions): covers lowering of the game's three operand layouts, a copied pair, and two refused forms, and checks that the frontend, IR, pass and back-end pieces exist and the opcode tables match opcodes.inc. It fails on main and passes with g++ and clang++.

I didn't find which effect those six compute programs drive, so the in-game check above shows nothing regressed, not a visible fix.

Assisted-by: Claude Opus 5.5

Six of OoT's compute programs did not translate on `main` (a598b9f): ``` vk: NVN2 program 9a6fe9d7a7ce2007 (stage 5) does not lower at +0x8830: unsupported native opcode (native op 0x3a9, 000073a9 00000008 001ee109 000ea400) ``` nvdisasm reads it as `ATOMG.E.CAS.STRONG.GPU PT, R0, [R0], R8, R9`. The same goes for 64978274654e224c, f4867235b77e1107, 3b018f504c4715a1, 9d3e6486df0904bf and 99b6a2a493b3ba10. **Change.** A new IR operation carried through every layer, modeled on `GlobalAtomicExchange32`: - `lower_to_maxwell.h`: 0x3a9 lowers to Maxwell's `ATOM.E.CAS Rd, [Ra + offset], Rb, Rb+1` (U32). The game already keeps the comparator and the value as an even pair (R8/R9, R38/R39, R30/R31). Any other pair is first copied to R250/R251. A 64-bit CAS, an odd address register and other modes still fail. - Maxwell frontend: `ATOM_cas` was a not-implemented stub. It now reads its address as `ATOM` does and emits `GlobalAtomicCompareExchange32`. - IR: `GlobalAtomicCompareExchange32 (address, comparator, value)` and `StorageAtomicCompareExchange32 (binding, offset, comparator, value)`, both with side effects. `META_TABLE` and `NUM_ARGS` grow from 539 to 541. - `global_memory_to_storage_buffer_pass.cpp`: maps the global operation to the storage one (`ReplaceAtomic` passes the extra operand) and counts it as a write where pointers are followed, like the other 32-bit atomics. - SPIR-V: `OpAtomicCompareExchange` on the storage buffer, plus a fallback function written like the other 32-bit atomics' fallbacks (search the buffers, then the page table where stores follow it). **Tested** on an RTX 4070 Ti (NVIDIA 595.99.02), Ubuntu 26.04, Linux build: - `RX_VK_PREPARE_AGAIN=1`: `prepared 31985 shader programs (... 7 failed)` drops to `1 failed`. The one left is `GlobalAtomicAddF32` with no SPIR-V fallback (program 499dbfd0721d2858), which is a separate change. - `nx2/lower_test.py` over the six dumped programs: 6 of 6 lower. - `RX_VK_DUMP_SPV` for that run: the six programs and the other 3812 dumped all pass `spirv-val --target-env vulkan1.3 --scalar-block-layout`. The fallback's `OpAtomicCompareExchange` passes the value, then the comparator, in SPIR-V's order. - Played from a save into Kokiri Forest (Link's house, out the door, Saria's introduction) at 60 fps with nothing new in the log. - `tests/test_lower_atomg_cas.py` (added to source-regressions): covers lowering of the game's three operand layouts, a copied pair, and two refused forms, and checks that the frontend, IR, pass and back-end pieces exist and the opcode tables match `opcodes.inc`. It fails on `main` and passes with g++ and clang++. I didn't find which effect those six compute programs drive, so the in-game check above shows nothing regressed, not a visible fix. Assisted-by: Claude Opus 5.5
Commenting is not possible because the repository is archived.
No description provided.