logicaffeine_compile/codegen_cuda/chip.rs
1//! The GPU **chip / family** knob — which NVIDIA silicon the CUDA backend targets.
2//!
3//! This is the CUDA analog of the Tenstorrent backend's per-part [`crate::spatial::target::SpatialTarget`]
4//! (wormhole / blackhole): a described target part whose SM architecture, shared-memory budget, and realized
5//! numeric formats make the fastest kernel *chip-specific*. The SAME recognized LOGOS tensor op lowers to a
6//! kernel tuned for the local **RTX 3060** (Ampere, sm_86) — where we prove correctness — or for a cloud
7//! **B200** (Blackwell, sm_100), the target we submit to NVIDIA's SOL-ExecBench against.
8//!
9//! Choosing the chip is a genuine *trade-off* (hence a knob, not a constant): it trades **portability** against
10//! **hardware exploitation**. A kernel compiled for `sm_100` won't run on an `sm_86` host; a kernel compiled
11//! for `sm_86` leaves the B200's 5th-gen tensor cores, fp8/nvfp4, and larger shared memory unused.
12
13use crate::codegen_sva::fabric_target::Precision;
14
15/// An NVIDIA GPU architecture family — fixes the tensor-core generation and which numeric formats exist.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum GpuFamily {
18 /// sm_80 / sm_86 — 3rd-gen tensor cores: bf16 / fp16 / tf32, int8 IMMA. A100, RTX 30xx.
19 Ampere,
20 /// sm_89 — 4th-gen tensor cores + fp8 (e4m3/e5m2). Ada: RTX 40xx / L40.
21 Ada,
22 /// sm_90 — 4th-gen tensor cores, wgmma / TMA, fp8. Hopper: H100 / H200.
23 Hopper,
24 /// sm_100 — 5th-gen tensor cores, tcgen05, fp8 + NVFP4 16-element block scaling. Blackwell: B200 / GB200.
25 Blackwell,
26}
27
28/// A described CUDA target part — the hardware limits and realized numeric formats that make the fastest
29/// kernel chip-specific, plus the SM arch its kernels are compiled for. Construct with a named part
30/// ([`CudaChip::rtx3060`], [`CudaChip::b200`], …); the emitter reads it to pick the arch flag, the shared-memory
31/// budget, and which precisions are legal.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct CudaChip {
34 /// Human-readable part name — carried into the emitted kernel's provenance header.
35 pub name: &'static str,
36 /// The architecture family (tensor-core generation).
37 pub family: GpuFamily,
38 /// SM architecture number (80 / 86 / 89 / 90 / 100) — the `sm_XX` the kernel compiles for.
39 pub sm_arch: u32,
40 /// Opt-in dynamic shared-memory ceiling per block, in KiB (bounds the resident row/tile a kernel stages).
41 pub smem_kib_per_block: usize,
42 /// Threads per block ceiling.
43 pub max_threads_per_block: usize,
44 /// Streaming multiprocessors: the blocks a launch must offer to fill the part.
45 pub sms: usize,
46 /// The part's device memory, in bytes: what a lane plans its program's weights, caches and arrays on, less what the
47 /// driver and the program's context take (`lane::residence::CONTEXT_RESERVE`).
48 pub mem_bytes: usize,
49 /// The numeric formats this part's tensor / CUDA cores realize — the precision legality gate (a lowering
50 /// may pick a precision only if the target chip provides it).
51 pub precisions: &'static [Precision],
52}
53
54/// Ampere/Ada realize bf16/fp16/fp32 (+ the int formats, int32 the CUDA cores' own word) but no fp8; the tensor path
55/// picks among these.
56const AMPERE_PRECISIONS: &[Precision] = &[
57 Precision::Int8,
58 Precision::Int16,
59 Precision::Int32,
60 Precision::Int64,
61 Precision::Bf16,
62 Precision::Fp16,
63 Precision::Fp32,
64];
65/// Hopper/Blackwell add fp8. (NVFP4 joins here in Phase 5, once `Precision::Nvfp4` and its witness ship.)
66const HOPPER_PLUS_PRECISIONS: &[Precision] = &[
67 Precision::Int8,
68 Precision::Int16,
69 Precision::Int32,
70 Precision::Int64,
71 Precision::Bf16,
72 Precision::Fp16,
73 Precision::Fp32,
74 Precision::Fp8,
75];
76
77impl CudaChip {
78 /// **RTX 3060** — the local dev GPU: Ampere, sm_86, 28 SMs, ~99 KiB opt-in shared memory, 12 GiB. Where we prove
79 /// correctness and relative speed before spending B200 time.
80 pub fn rtx3060() -> Self {
81 CudaChip {
82 name: "rtx3060",
83 family: GpuFamily::Ampere,
84 sm_arch: 86,
85 smem_kib_per_block: 99,
86 max_threads_per_block: 1024,
87 sms: 28,
88 mem_bytes: 12 << 30,
89 precisions: AMPERE_PRECISIONS,
90 }
91 }
92
93 /// **A100** — Ampere data-center part, sm_80, 108 SMs, ~164 KiB opt-in shared memory, the 80 GB part.
94 pub fn a100() -> Self {
95 CudaChip {
96 name: "a100",
97 family: GpuFamily::Ampere,
98 sm_arch: 80,
99 smem_kib_per_block: 164,
100 max_threads_per_block: 1024,
101 sms: 108,
102 mem_bytes: 80 << 30,
103 precisions: AMPERE_PRECISIONS,
104 }
105 }
106
107 /// **H100** (SXM / NVL) — Hopper, sm_90, 132 SMs, ~228 KiB opt-in shared memory, 80 GB (the SXM part), fp8.
108 pub fn h100() -> Self {
109 CudaChip {
110 name: "h100",
111 family: GpuFamily::Hopper,
112 sm_arch: 90,
113 smem_kib_per_block: 228,
114 max_threads_per_block: 1024,
115 sms: 132,
116 mem_bytes: 80 << 30,
117 precisions: HOPPER_PLUS_PRECISIONS,
118 }
119 }
120
121 /// **B200** — Blackwell, sm_100, 148 SMs, ~228 KiB opt-in shared memory, 192 GB, fp8 (+ NVFP4 in Phase 5). The
122 /// SOL-ExecBench target.
123 pub fn b200() -> Self {
124 CudaChip {
125 name: "b200",
126 family: GpuFamily::Blackwell,
127 sm_arch: 100,
128 smem_kib_per_block: 228,
129 max_threads_per_block: 1024,
130 sms: 148,
131 mem_bytes: 192 << 30,
132 precisions: HOPPER_PLUS_PRECISIONS,
133 }
134 }
135
136 /// The `nvcc` arch flag that targets this part. Hopper/Blackwell get the architecture-specific `a` suffix
137 /// (required to unlock wgmma/TMA on Hopper and tcgen05 on Blackwell); Ampere/Ada use the plain form.
138 pub fn nvcc_arch_flag(&self) -> String {
139 let suffix = match self.family {
140 GpuFamily::Hopper | GpuFamily::Blackwell => "a",
141 GpuFamily::Ampere | GpuFamily::Ada => "",
142 };
143 format!("-arch=sm_{}{}", self.sm_arch, suffix)
144 }
145
146 /// Whether this part's tensor / CUDA path realizes a numeric format.
147 pub fn supports(&self, p: Precision) -> bool {
148 self.precisions.contains(&p)
149 }
150
151 /// Can a kernel built for THIS chip be *numerically* executed on a host GPU of SM arch `host_sm`? Kernels
152 /// are forward-compatible: a lower-arch kernel runs on a higher-arch host, never the reverse. This routes
153 /// the run-vs-compile-check split — everything sm_86-legal is proven on the local 3060; a B200 kernel can
154 /// only be compile-checked locally and is numerically validated on the cloud B200.
155 pub fn runs_numerically_on(&self, host_sm: u32) -> bool {
156 self.sm_arch <= host_sm
157 }
158}