Skip to content

fix(msl): reject unsupported 64-bit atomics#82

Merged
kolkov merged 1 commit into
gogpu:mainfrom
besmpl:besmpl/fix-msl-int64-atomics
Jul 26, 2026
Merged

fix(msl): reject unsupported 64-bit atomics#82
kolkov merged 1 commit into
gogpu:mainfrom
besmpl:besmpl/fix-msl-int64-atomics

Conversation

@besmpl

@besmpl besmpl commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • reject unsupported 64-bit buffer/workgroup atomic operations in the MSL backend instead of emitting intrinsics that Metal cannot resolve
  • match current Rust Naga's MSL capability boundary: keep only result-discarded min/max in storage address space, with no public feature flag
  • make MSL snapshot skips depend on an explicit checked-in Rust METAL target exclusion and remove the two invalid stale goldens
  • add focused WGSL-to-MSL policy coverage plus a Darwin xcrun metal regression for the supported Metal 2.4 path

Rust Naga parity

Rust Naga's MSL backend supports SHADER_INT64_ATOMIC_MIN_MAX but not SHADER_INT64_ATOMIC_ALL_OPS. This change enforces the same three-part exception locally:

  1. the operation is min or max;
  2. its result is discarded; and
  3. the pointer is in storage address space.

Loads, stores, arithmetic/bitwise operations, exchanges, compare-exchanges, result-producing min/max, and workgroup min/max now return a descriptive msl.Compile error with no partial source. Existing 32-bit and image-atomic paths are unchanged.

Verification

  • go test -count=1 ./msl/internal/codegen ./snapshot
  • go test -run TestRustReference -count=1 ./snapshot/
  • go test -race -count=1 ./...
  • go vet ./...
  • go build ./...
  • GOOS=linux golangci-lint v2.12.2 run --timeout=5m ./...
  • gofmt -l .
  • git diff --check

The Darwin regression is present and generates the supported Metal 2.4 shader, but this host's Xcode installation does not include the optional metal command-line utility, so the local xcrun metal compilation step skipped with its explicit environment message.

Fixes #79

@besmpl
besmpl requested a review from kolkov as a code owner July 24, 2026 10:37

@kolkov kolkov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: fix(msl): reject unsupported 64-bit atomics

Verdict: APPROVE — high-quality contribution that brings our MSL backend in line with Rust naga's capability boundary.

Rust naga parity — verified

Checked against Rust naga source (back/msl/writer.rs:3849-3863, valid/function.rs:453-496, valid/mod.rs:164-166):

  • Capability model matches. Rust naga defines SHADER_INT64_ATOMIC_MIN_MAX (1 << 20, min/max only, storage only, result discarded) and SHADER_INT64_ATOMIC_ALL_OPS (1 << 21, everything). The MSL backend explicitly documents it does NOT support ALL_OPS (back/msl/mod.rs:4). This PR enforces the same boundary.

  • Three-part rule matches. Rust validator (valid/function.rs:470-474) checks: (1) matches!(*fun, Min | Max), (2) matches!(pointer_space, Storage { .. }), (3) result.is_none(). The validateAtomicOperation method applies the identical three checks: scalar Width == 8, AtomicMin/AtomicMax with result == nil, space == ir.SpaceStorage.

  • TOML configs match. Both atomicOps-int64.toml and atomicCompareExchange-int64.toml in Rust reference have targets = "SPIRV | HLSL | WGSL" — METAL explicitly excluded. Golden deletion is correct.

  • Writer approach differs safely. Rust puts the check in the validator; the MSL writer assumes validation passed (writer.rs:3849-3852: "we can assume that if result is Some, we are not operating on a 64-bit value"). This PR puts the check directly in the MSL writer — defense-in-depth approach, correct for Go naga which doesn't yet have a separate capability-gated validator. Good call on adding validateAtomicOperation to writeUncheckedLoad/writeUncheckedStore as well — Rust relies on its upstream validator for those paths.

What's done well

  1. Stale golden cleanup — removed 380 lines of MSL output (180 + 200) that should never have existed. The old t.Skipf on compile error was masking invalid goldens.

  2. compileMSL refactoring — now returns error instead of silently skipping. Uses rustTargetForShader to check Rust TOML config: if METAL is excluded → t.Skipf, otherwise → t.Fatalf. No more hidden failures.

  3. Test coverage is comprehensive — 13 rejected operations (load, store, add, subtract, and, or, xor, exchange, compare-exchange, result-producing min/max, workgroup min/max), 3 positive cases (storage min, storage max, nested struct access chain), 32-bit control test, classifyRustTarget unit tests, Darwin xcrun regression.

  4. Error messages are actionable"64-bit atomic add is unsupported by MSL; only result-discarded min/max in the storage address space are supported" tells the caller exactly what's allowed.

Optional nit (non-blocking)

The rejection tests all use u64 — adding a single i64 rejection case (e.g. atomicAdd on atomic<i64>) would explicitly cover the signed branch. Since Width == 8 handles both uniformly this is cosmetic, but it would make the test contract more visible for future readers.

Notes

  • strings.SplitSeq in snapshot_test.go — Go 1.24+ iterator, compatible with our go 1.25 minimum.
  • currentFunction == nil guard in validateAtomicOperation — defensive, harmless, prevents nil panic in unexpected paths.
  • Future: when we add a capabilities validator (Rust naga model), the writer check becomes redundant but harmless.

LGTM.

@codecov

codecov Bot commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.83673% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
msl/internal/codegen/statements.go 91.48% 3 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@kolkov

kolkov commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

@besmpl CI is green, coverage at 91.8% — looks good. One optional nit from the review: all rejection tests use u64, adding a single i64 case (e.g. atomicAdd on atomic<i64>) would make the signed coverage explicit. Width == 8 handles both, so this is cosmetic — up to you.

Ready to merge as-is if you'd prefer not to add it.

@kolkov
kolkov merged commit c8a6ff3 into gogpu:main Jul 26, 2026
11 checks passed
@kolkov kolkov mentioned this pull request Jul 26, 2026
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MSL: shaders with 64-bit atomics fail Metal compilation

2 participants