Skip to content

fix(intrinsics): admit scalar math on sm_75 - #1670

Open
praveenperera wants to merge 1 commit into
NVIDIA:mainfrom
praveenperera:fix/scalar-math-sm75
Open

praveenperera wants to merge 1 commit into
NVIDIA:mainfrom
praveenperera:fix/scalar-math-sm75

Conversation

@praveenperera

Copy link
Copy Markdown
Contributor

Summary

The 40 F32/F64 scalar_math intrinsics (ex2_approx_f32, rcp_rn_f32, sqrt_rn_f64, tanh_approx_f32, ...) carried a family-wide PTX 7.0 / sm_80 floor, so an sm_75 build that calls any of them fails target gating. The PTX ISA admits every one of them on Turing. This lowers the family floor to PTX 7.0 / sm_75, re-records the LLVM and libNVVM evidence at sm_75, and adds sm_75 build regressions.

Part of #1462. This PR covers scalar_math; #1512 covers scalar_arithmetic (fma_rn_f32, mul_rn_f32, ...). Neither depends on the other. Both regenerate the catalog digest banners, so whichever merges second needs a cuda-intrinsics-gen generate after rebasing.

before: cargo oxide build scalar_math_sm75 --arch sm_75
        error: CUDA target sm_75 cannot lower generated intrinsic `sin_approx_f32` (`v1:i0782`); requires sm_80 or newer

after:  cargo oxide build scalar_math_sm75 --arch sm_75
        .version 7.0 / .target sm_75, all 40 forms
        ptxas -arch=sm_75: MUFU.{SIN,COS,EX2,LG2,TANH,RCP,RCP64H,RSQ,RSQ64H,SQRT}

Floors

Forms PTX ISA requirement Catalog floor
sin, cos, lg2, ex2 .approx[.ftz].f32 all targets PTX 7.0, sm_75
rcp.approx.ftz.f32, rsqrt.approx[.ftz].f32, sqrt.approx[.ftz].f32 all targets PTX 7.0, sm_75
rcp/sqrt .{rn,rz,rm,rp}[.ftz].f32 sm_20 PTX 7.0, sm_75
rcp.rn.f64, sqrt.rn.f64, rsqrt.approx.f64 sm_13 PTX 7.0, sm_75
rcp/sqrt .{rz,rm,rp}.f64, rcp.approx.ftz.f64 sm_20 PTX 7.0, sm_75
tanh.approx.f32 PTX 7.0, sm_75 PTX 7.0, sm_75
ex2_approx_f16 (unchanged) PTX 7.0, sm_75 PTX 7.0, sm_75
  • sm_75 is the oldest target CUDA 13 ptxas assembles (sm_72 is not defined), so it is the lowest floor the evidence can attest.
  • PTX stays at 7.0. tanh.approx.f32 needs it, and the evidence fixtures put every form in one module, where ptxas checks unreferenced functions too (Feature 'tanh' requires PTX ISA .version 7.0).
  • Forms the family does not admit keep their own minimums (ex2.approx.ftz.bf16 at sm_90, the f16/bf16 tanh forms, and the deferred rsqrt.approx.ftz.f64).

Changes

  • Recipe: the floor in resolve/families/scalar_math.rs is PTX 7.0 / sm_75, with the ISA rule in a comment. The policy, both backend lowerings, the catalog, the generated target tables, the stub docs and the reference table follow by regeneration.
  • LLVM evidence (rust-llvm-23.1.3-aab11ee0-scalar-math.json): the three F32/F64 fixtures are renamed to *_sm75_ptx70 and their backend_codegen and ptx_assembly stages are re-captured at sm_75 / +ptx70 with the nightly-2026-10-11 llc and ptxas 13.3.73. The declaration-canonicalization stages are target-independent and unchanged. scalar_math_ex2_f16_sm75_ptx70 is untouched.
  • libNVVM evidence (cuda-13.3-libnvvm-13.3.33-scalar-math.json): one compute_75 fixture covers all 40 forms. It records libNVVM PTX and LTOIR, ptxas -arch=sm_75, and an nvJitLink -arch=sm_75 -lto link. The fixture keeps its kernel with @llvm.used. Without it, nvJitLink drops the unreferenced kernel; the previous appended sm_80 record linked a 1,464-byte cubin, most likely that empty module. The new link produces a 31,912-byte cubin with the expected MUFU instructions.
  • The new stage details cite their digests and name the refresh-2026-10-11/stages.jsonl sm_80 captures they replace. Those captures still reproduce at sm_80, and the ledger is left as the historical record.
  • Regenerated outputs: catalog.json changes only the 40 F32/F64 scalar_math records. The generated target tables change only scalar_math; other files change only the catalog digest banner. The book catalog stamp is refreshed. abi-v1.toml and the overlay are unchanged.

Testing

  • Generator test checked_in_scalar_math_floors_are_turing_and_fail_closed: every scalar_math policy and both lowerings carry PTX 7.0 / sm_75, and policies at sm_80 or sm_72 are rejected. It fails against the old recipe.
  • Codegen test generated_scalar_math_floors_admit_turing: on both backends, all 41 nvvm.scalar_math targets require PTX 7.0, are refused at sm_72 and accepted at sm_75 and sm_80, and a module using all of them validates at sm_75. It also resolves ex2_approx_f32 with CUDA_OXIDE_TARGET=sm_75 (LLVM) and through resolve_nvvm_target_with_generated (libNVVM). It fails against the old generated table.
  • New example scalar_math_sm75: one kernel calls all 40 forms through cuda_device::float, and the host checks the results against the PTX ISA bounds. rn must match the host exactly, and rz/rm/rp must bracket the exact result by residual sign. The ISA gives no bound for rcp.approx.ftz.f64 and rsqrt.approx.f64, so those two get a 2^-16 sanity check, plus the documented zero low word for rcp.
  • CI: examples-compile.yml builds the example at sm_75 as PTX (artifact kind 256) and as a libNVVM + nvJitLink cubin (kind 512). smoketest.sh always builds it at --arch=sm_75, so newer GPUs run JIT-compiled sm_75 PTX.

Checked at the head of this branch:

  • just -f cuda-oxide/Justfile check passes (fmt-check, clippy, test, test-cuda, check-guards including cargo-deny, check-intrinsics, doc-check)
  • cargo oxide run scalar_math_sm75 --arch sm_75 passes on an RTX 5090 (sm_120, the sm_75 PTX JIT-compiled by the driver). No Turing GPU was available, so the sm_75 cubin route was built and inspected but not executed.
  • New example added (scalar_math_sm75)
  • cargo oxide build scalar_math_sm75 --materialize-cubin --arch sm_75 embeds an sm_75 cubin; its SASS contains every expected MUFU op.
  • The LLVM PTX and cubin digests (sm_75, plus the historical sm_80 ones) and the libNVVM artifact digests reproduce from the probes with the recorded tool binaries. The CUDA 13.3 binaries came from NVIDIA's redistributable archives, and their SHA-256 values match the recorded tool identities.

Checklist

  • All commits signed off (git commit -s)
  • SPDX headers on new source files

The scalar_math recipe floored every F32/F64 form at PTX 7.0 / sm_80,
so `ex2_approx_f32`, `rcp_rn_f32`, `sqrt_rn_f32` and the rest failed
target gating on sm_75. The PTX ISA admits all 40 forms on sm_20 or
older, except `tanh.approx.f32` at PTX 7.0 / sm_75. sm_75 is also the
oldest target CUDA 13 ptxas assembles, so it is the lowest floor the
evidence can attest. The family floor is now PTX 7.0 / sm_75, matching
`ex2_approx_f16`.

The LLVM evidence is re-probed at sm_75 / +ptx70 with the pinned llc
and ptxas 13.3.73. The libNVVM evidence is re-captured at compute_75,
assembled with ptxas 13.3.33 and linked with nvJitLink at sm_75. The
regenerated outputs change only the scalar_math floors and the catalog
digest banners.

A generator test pins the sm_75 floor and rejects sm_80 and sm_72. A
codegen test checks every scalar_math target on both backends and the
`ex2_approx_f32` repro at CUDA_OXIDE_TARGET=sm_75. The new
scalar_math_sm75 example calls all 40 forms and checks them against
the PTX ISA error bounds. examples-compile builds it at sm_75 as PTX
and as a libNVVM + nvJitLink cubin.

scalar_arithmetic keeps its sm_80 floor here; NVIDIA#1512 covers it.

Refs NVIDIA#1462

Signed-off-by: Praveen Perera <me@praveenperera.com>
@copy-pr-bot

copy-pr-bot Bot commented Oct 29, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

This branch has not been deployed

No deployments
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