Skip to content

feat(gpu): O2 — arm the register_budget contract, vacuous since 2026-04-06 - #3064

Merged
noahgift merged 2 commits into
mainfrom
feat/o2-register-budget
Sep 9, 2026
Merged

feat(gpu): O2 — arm the register_budget contract, vacuous since 2026-04-06#3064
noahgift merged 2 commits into
mainfrom
feat/o2-register-budget

Conversation

@noahgift

@noahgift noahgift commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

O2 of the 0.67 CUDA Rust spec (#3062). Developed and validated on gx10 (GB10 Blackwell sm_121), the only host where the interesting arch lives.

The defect

contracts/trueno/ptx-codegen-safety-v1.yaml equation register_budget declares a register/shared-memory budget with postcondition cuOccupancyMaxActiveBlocksPerMultiprocessor > 0. Nothing enforced it:

  • the generated macro contract_register_budget! is invoked nowhere in the tree
  • its postcondition names cuOccupancyMaxActiveBlocksPerMultiprocessor, an unbound identifier that would not compile if the macro ever were invoked
  • FALSIFY-PTX-003's test: is prose — "Parse ptxas output for register count, assert <= 128" — and prose never runs
  • it carries registry: true, the exemption class retired 2026-08-21

Meanwhile aprender-gpu launches hand-emitted PTX with hardcoded block sizes (128/256) and validates nothing about the kernel the JIT actually produced.

Two deliberate design splits

1. The policy is ungated. crate::launch_budget::validate_launch is pure and lives at the crate root, not under driver. The whole driver module is #[cfg(feature = "cuda")] (lib.rs:138), so anything placed there runs only with --features cuda and would never reach the required check.

Measured while building this: that is exactly why driver::ptx_patch's GH-480 tests are dark today — despite its module comment claiming "NOT feature-gated so that its tests run without CUDA hardware." Putting the policy there would have made O2 theater.

aprender-gpu --lib goes 444 → 457 tests, all in the CPU-only path #3063 un-darks.

2. Limits are queried, not tabulated. A per-SM table goes stale every GPU generation and this one did — the hand-written CU_TARGET_COMPUTE_* list stops at 90, and so did the contract's own domain. driver/budget_query.rs reads limits from cuDeviceGetAttribute, so sm_100/120/121 and whatever ships next need no edit here.

Mechanism engaged, on Blackwell

Not "it compiled" — the actual values read back on GB10 sm_121 under ptxas 13.3:

cc=(12,1)  kernel{max_thr=1024 regs=8 smem=0 local=0}
           device{max_thr=1024 smem=49152 regs=65536 warp=32}
           occupancy@256=6

19/19 with --features cuda on sm_121, and 13/13 pure on the default build. Also 19/19 on RTX 4090 sm_89.

Mutation proof — and the test that failed it

Deleting the block-size comparison in validate_launch turns 4 tests RED, restoring turns them GREEN:

launch_budget::tests::rejects_block_above_kernel_max
launch_budget::tests::kernel_max_is_reported_before_device_max
launch_budget::tests::violations_name_both_sides_of_the_comparison
driver::cuda_tests::launch_budget_hw::budget_goes_red_on_an_oversized_block

The hardware test's first version SURVIVED the mutation. It asserted only that an error occurred — but with the kernel-max check deleted the oversized launch falls through to the device-wide check and still errors, so the test witnessed nothing. It now asserts the specific violation. That was found by running the mutation, not by reading the test, and the contract's red_witness records it.

Contract repaired in the same commit

  • registry: true dropped
  • FALSIFY-PTX-003 prose replaced with two executable cargo test commands
  • domain extended past sm_90 to 100/120/121
  • two pre-monorepo paths fixed (trueno/src/…, realizar/src/… — neither exists since APR-MONO)
  • enforced_by: names the two files that now implement it
  • pv validate0 errors, 0 warnings

Verification

cargo fmt clean · clippy -D warnings clean on both default and --features cuda — the raw-pointer entry points are unsafe fn with # Safety docs rather than an #[allow] · 457/457 default · 19/19 cuda on sm_89 and sm_121.

Refs #3062

🤖 Generated with Claude Code

https://claude.ai/code/session_01J9cSQynVPYeUkQ2i7ccvrs

…s since 2026-04-06

contracts/trueno/ptx-codegen-safety-v1.yaml equation `register_budget` declares

    forall kernel K:
      reg_count(K)  <= max_regs_per_thread(sm)
      shared_mem(K) <= max_shared_per_block(sm)
    postcondition: cuOccupancyMaxActiveBlocksPerMultiprocessor > 0

and NOTHING enforced it. The generated macro `contract_register_budget!` is invoked
nowhere in the tree, and its postcondition names an unbound identifier that would
not compile if it ever were. Meanwhile aprender launches hand-emitted PTX with
hardcoded block sizes (128/256) and validates nothing about the kernel the JIT
actually produced. This is that missing enforcement.

Design — two deliberate splits:

1. POLICY IS UNGATED. `crate::launch_budget::validate_launch` is pure and lives at
   the crate root, NOT under `driver`. The whole `driver` module is
   `#[cfg(feature = "cuda")]` (lib.rs:138), so anything placed there runs only with
   --features cuda and would never reach the required check. Measured: that is why
   driver::ptx_patch's GH-480 tests are dark today, despite its module comment
   claiming "NOT feature-gated so that its tests run without CUDA hardware".
   The 13-case table therefore runs everywhere; aprender-gpu --lib goes 444 -> 457.

2. LIMITS ARE QUERIED, NOT TABULATED. A per-SM limit table goes stale every GPU
   generation and this one did: the hand-written CU_TARGET_COMPUTE_* list stops at
   90, and so did the contract's own `domain`. driver/budget_query.rs reads the
   limits from cuDeviceGetAttribute, so sm_100/120/121 and whatever ships next
   need no edit here.

New FFI (driver/sys/mod.rs, via the existing load_sym! pattern):
  cuFuncGetAttribute, cuOccupancyMaxActiveBlocksPerMultiprocessor, plus the
  CU_FUNC_ATTRIBUTE_* / CU_DEVICE_ATTRIBUTE_* constants they need.

Contract repaired in the same commit:
  - `registry: true` dropped (operator ruling 2026-08-21)
  - FALSIFY-PTX-003's prose test ("Parse ptxas output...") replaced with two
    executable cargo commands; a prose test never runs
  - `domain` extended past sm_90 to 100/120/121
  - two pre-monorepo reference paths fixed (trueno/src/..., realizar/src/...)
  - `enforced_by:` names the two files that now implement it
  - pv validate: 0 errors, 0 warnings

MUTATION PROOF (measured, not asserted). Deleting the block-size comparison in
validate_launch turns 4 tests RED and restoring turns them GREEN:
  launch_budget::tests::rejects_block_above_kernel_max
  launch_budget::tests::kernel_max_is_reported_before_device_max
  launch_budget::tests::violations_name_both_sides_of_the_comparison
  driver::cuda_tests::launch_budget_hw::budget_goes_red_on_an_oversized_block

The hardware test earns its place only because it asserts the SPECIFIC violation.
Its first version asserted merely that an error occurred and SURVIVED the mutation
-- the oversized launch falls through to the device-wide check and still errors, so
the test witnessed nothing. That was found by running the mutation, not by review,
and the contract's red_witness records it.

Verified: fmt clean; clippy -D warnings clean on default AND --features cuda (the
raw-pointer entry points are `unsafe fn` with Safety docs rather than an #[allow]);
457/457 default tests; 19/19 with --features cuda on RTX 4090 sm_89.

Refs #3062

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J9cSQynVPYeUkQ2i7ccvrs
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

§13.11 rung 1 — quorum shadow verdict

S13-SHADOW pr=3064 head=c69bb00741f21326fb5f15e6e9f8c514daedb028 verdict=REFUSE class=Q1 arm_rc=1

Shadow mode: this records a verdict and merges nothing. A refusal
to arm is not a block (§13 adds zero rows to §7) — the pull request is
exactly as green as it was.

@noahgift
noahgift added this pull request to the merge queue Sep 9, 2026
Merged via the queue into main with commit 5706dde Sep 9, 2026
15 of 17 checks passed
@noahgift
noahgift deleted the feat/o2-register-budget branch September 9, 2026 22:20
noahgift added a commit that referenced this pull request Sep 9, 2026
…es re-derived from a cwd OUTSIDE the checkout — a worktree nested under /home/noah/src/aprender inherits its .cargo/config.toml [patch.crates-io], and the bump's regenerated locks carried 11 [[patch.unused]] entries CI's clean cargo refuses under --locked (guard-cargo + bump --check RED on run 34407841821)

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjhtNUSensCYpQb3mCYLod
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.

1 participant