use super::dtype::WeightDtype;
use super::gemm_bi_triad::GemmBiKernels;
use super::gemm_bi_triad::modules::inference_bundle::InferenceSm89Bundle;
use cudarc::driver::{CudaContext, CudaFunction, CudaModule};
use std::ops::Deref;
use std::sync::Arc;
#[derive(Clone)]
pub(crate) struct CudaModuleAnchors {
_modules: Arc<[Arc<CudaModule>]>,
}
impl CudaModuleAnchors {
pub(crate) fn new(modules: Vec<Arc<CudaModule>>) -> Self {
Self {
_modules: modules.into(),
}
}
}
pub struct TypedKernel {
pub f32: CudaFunction,
pub bf16: CudaFunction,
pub f16: CudaFunction,
}
impl TypedKernel {
pub fn get(&self, dt: WeightDtype) -> &CudaFunction {
match dt {
WeightDtype::F32 | WeightDtype::Tf32 => &self.f32,
WeightDtype::Bf16 => &self.bf16,
WeightDtype::F16 => &self.f16,
}
}
}
pub struct HalfKernel {
pub bf16: CudaFunction,
pub f16: CudaFunction,
}
impl HalfKernel {
pub fn get(&self, dt: WeightDtype) -> &CudaFunction {
match dt {
WeightDtype::Bf16 => &self.bf16,
WeightDtype::F16 => &self.f16,
WeightDtype::F32 | WeightDtype::Tf32 => {
panic!("HalfKernel has no f32 variant (use the TypedKernel f32 path instead)")
}
}
}
}
pub struct FixedTf32Kernels {
pub m128n64_s2: CudaFunction,
pub m128n64_s3: CudaFunction,
pub m64n64_s2: CudaFunction,
pub m64n64_s3: CudaFunction,
pub m16n32_s4: CudaFunction,
}
pub struct FixedSm120Tf32Kernels {
pub m128n64_s2: CudaFunction,
pub m128n64_s3: CudaFunction,
pub m64n128_s2: CudaFunction,
pub m64n128_s3: CudaFunction,
pub m64n64_s2_producer_warp: CudaFunction,
pub m64n64_s2: CudaFunction,
pub(crate) m64n64_s2_pair_store: CudaFunction,
}
pub struct FixedSm120HalfKernels {
pub m64n64_bk64_s2: HalfKernel,
pub m64n128_bk64_s2: HalfKernel,
pub m128n64_bk32_s3: HalfKernel,
pub m128n128_bk32_s2: HalfKernel,
pub m128n128_bk32_s3: HalfKernel,
}
pub struct FixedSm120FmaPostbiasKernels {
pub m128n64: CudaFunction,
pub m64n128: CudaFunction,
pub m128n96: CudaFunction,
pub m128n64_k4: Option<CudaFunction>,
pub m128n64_k4_rejection: Option<String>,
pub m128n64_t256: Option<CudaFunction>,
pub m128n64_t256_rejection: Option<String>,
pub nobias_m128n64_t256: Option<CudaFunction>,
pub nobias_m128n64_t256_rejection: Option<String>,
}
pub struct MambaKernels {
_modules: CudaModuleAnchors,
compiler_identity: super::kernel_identity::CompilerIdentity,
triad: GemmBiKernels,
pub state_cap: usize,
pub ssm_step_fwd: CudaFunction,
pub ssm_burnin_fwd: CudaFunction,
pub ssm_burnin_fwd_nosave: CudaFunction,
pub ssm_backward_local: CudaFunction,
pub ssm_reduce_d_bc_typed: TypedKernel,
pub ssm_reduce_d_bc_tmajor_typed: TypedKernel,
pub ssm_reduce_d_d: CudaFunction,
pub ssm_reduce_d_a_log: CudaFunction,
pub ssm_reduce_d_a_log_chunks: CudaFunction,
pub conv1d_burnin_fwd_nosave_tiled: CudaFunction,
pub conv1d_burnin_nosave_tiled_typed: TypedKernel,
pub softplus_fwd: CudaFunction,
pub softplus_bwd: CudaFunction,
pub rmsnorm_fwd: CudaFunction,
pub rmsnorm_bwd: CudaFunction,
pub bias_broadcast: CudaFunction,
pub colsum_accumulate: CudaFunction,
pub reduce_sum_axis0: CudaFunction,
pub vec_add_inplace: CudaFunction,
pub exp_negate: CudaFunction,
pub exp_negate2: CudaFunction,
pub gather_cols: CudaFunction,
pub gather_bc_cols: CudaFunction,
pub gather_bc_cols_tmajor: CudaFunction,
pub gather_bc_cols_tmajor_tiled: CudaFunction,
pub gate_mul_silu: CudaFunction,
pub gating_backward: CudaFunction,
pub residual_add: CudaFunction,
pub gather_last_timestep: CudaFunction,
pub cast_f32_to_bf16: CudaFunction,
pub cast_bf16_to_f32: CudaFunction,
pub cast_f16_to_f32: CudaFunction,
pub cast_f32_to_f16: CudaFunction,
pub ssm_burnin_fwd_bf16: CudaFunction,
pub ssm_burnin_fwd_f16: CudaFunction,
pub gating_bwd_typed: TypedKernel,
pub rmsnorm_bwd_typed: TypedKernel,
pub conv1d_burnin_fwd_tiled_typed: TypedKernel,
pub conv1d_bwd_tiled_typed: TypedKernel,
pub conv1d_burnin_bwd_typed: TypedKernel,
pub ssm_backward_local_typed: TypedKernel,
pub pack_xdbl_cols_typed: TypedKernel,
pub softplus_fwd_typed: TypedKernel,
pub rmsnorm_fwd_resadd_typed: TypedKernel,
pub bias_broadcast_typed: TypedKernel,
pub elementwise_mul_typed: TypedKernel,
pub residual_add_typed: TypedKernel,
pub gather_cols_typed: TypedKernel,
pub gather_bc_cols_typed: TypedKernel,
pub gather_bc_cols_tmajor_typed: TypedKernel,
pub gather_bc_cols_tmajor_tiled_typed: TypedKernel,
pub gate_mul_silu_typed: TypedKernel,
pub gate_mul_silu_v_typed: TypedKernel,
pub elementwise_mul_v_typed: TypedKernel,
pub softplus_copy_typed: TypedKernel,
pub ssm_step_fwd_fused_typed: TypedKernel,
pub conv1d_step_fwd_silu_typed: TypedKernel,
pub ssm_burnin_nosave_typed: TypedKernel,
pub softplus_bwd_typed: TypedKernel,
pub gather_last_timestep_typed: TypedKernel,
pub vec_cast_zplus_typed: TypedKernel,
pub concat_halves_typed: TypedKernel,
pub scatter_add_cols_typed: TypedKernel,
pub reduce_bias_typed: TypedKernel,
pub rmsnorm_fwd_f32in_typed: HalfKernel,
pub residual_add_f32_typed: HalfKernel,
pub rmsnorm_bwd_f32in_typed: HalfKernel,
pub ssm_parallel_fwd: CudaFunction,
pub ssm_parallel_fwd_nosave: CudaFunction,
pub ssm_parallel_fwd_typed: TypedKernel,
pub ssm_parallel_fwd_nosave_typed: TypedKernel,
pub ssm_parallel_bwd_typed: TypedKernel,
pub ssm_parallel_bwd_fold_typed: TypedKernel,
ssm_parallel_bwd_fold_short_typed: Option<HalfKernel>,
ssm_parallel_bwd_fold_staged_typed: Option<HalfKernel>,
pub check_inf_nan_f32: CudaFunction,
pub scale_grads_f32: CudaFunction,
pub scale_grads_skip_f32: CudaFunction,
pub grad_sumsq_partial_f32: CudaFunction,
pub grad_clip_coef_f32: CudaFunction,
pub scale_grads_dev_f32: CudaFunction,
pub grad_region_sumsq_partial_f32: CudaFunction,
pub grad_region_scale_dev_f32: CudaFunction,
pub adamw_step_f32: CudaFunction,
pub adamw_step_f32_capturable: CudaFunction,
pub adamw_step_multi: TypedKernel,
pub gemm_bi_bf16_bf16: CudaFunction,
pub gemm_bi_f16_f16: CudaFunction,
pub gemm_bi_bf16_f32: CudaFunction,
pub gemm_bi_f16_f32: CudaFunction,
pub gemm_bi_f32_f32: CudaFunction,
pub gemm_bi_f32_f32_s2: CudaFunction,
pub gemm_bi_f32_f32_n128_s2: CudaFunction,
pub gemm_bi_nn_tf32: FixedTf32Kernels,
pub gemm_bi_nn_tf32_sm120: Option<FixedSm120Tf32Kernels>,
pub gemm_bi_nn_half_sm120: Option<FixedSm120HalfKernels>,
pub gemm_bi_nn_half_sm120_f32out: Option<FixedSm120HalfKernels>,
pub fixed_sm89_half_pipeline: Option<HalfKernel>,
pub fixed_sm89_half_pipeline_rejection: Option<String>,
pub fixed_sm89_half_swizzle: Option<HalfKernel>,
pub fixed_sm89_half_swizzle_rejection: Option<String>,
pub fixed_sm89_half_s3: Option<HalfKernel>,
pub fixed_sm89_half_s3_rejection: Option<String>,
pub fixed_sm89_tf32_rna_wide: Option<CudaFunction>,
pub fixed_sm89_tf32_rna_wide_rejection: Option<String>,
pub fixed_sm89_tf32_rna_n96: Option<CudaFunction>,
pub fixed_sm89_tf32_rna_n96_rejection: Option<String>,
pub fixed_sm89_half_m64n64_s3_f16: Option<CudaFunction>,
pub fixed_sm89_half_m64n64_s3_f16_rejection: Option<String>,
pub fixed_sm89_half_m128n64_s2_f16: Option<CudaFunction>,
pub fixed_sm89_half_m128n64_s2_f16_rejection: Option<String>,
pub fixed_sm89_f32_n64_copyplan: Option<CudaFunction>,
pub fixed_sm89_f32_n64_copyplan_rejection: Option<String>,
pub(crate) fixed_sm89_cells: std::collections::HashMap<&'static str, CudaFunction>,
pub(crate) sm89_cells_compiler_identity: Option<super::kernel_identity::CompilerIdentity>,
pub(crate) inference_sm89_bundle: InferenceSm89Bundle,
pub fixed_sm120_f32_n64_copyplan: Option<CudaFunction>,
pub fixed_sm120_f32_n64_copyplan_rejection: Option<String>,
pub fixed_sm120_f32_n64_copyplan_t256: Option<CudaFunction>,
pub fixed_sm120_f32_n64_copyplan_t256_rejection: Option<String>,
pub fixed_sm120_f32_m128n64_copyplan_t256: Option<CudaFunction>,
pub fixed_sm120_f32_m128n64_copyplan_t256_rejection: Option<String>,
pub fixed_sm120_f32_n64_sliced: Option<CudaFunction>,
pub fixed_sm120_f32_n64_sliced_rejection: Option<String>,
pub fixed_sm120_fma_postbias: Option<FixedSm120FmaPostbiasKernels>,
pub fixed_sm120_fma_postbias_rejection: Option<String>,
pub matvec_bi_bf16_bf16: CudaFunction,
pub matvec_bi_f16_f16: CudaFunction,
pub matvec_bi_bf16_f32: CudaFunction,
pub matvec_bi_f16_f32: CudaFunction,
pub matvec_bi_f32_f32: CudaFunction,
pub gemm_bi_nn_tc128_typed: HalfKernel,
pub gemm_bi_nn_tc128_f32out: HalfKernel,
pub gemm_bi_nn_tc64_f32out: HalfKernel,
pub gemm_bi_nn_tc16_f32out: HalfKernel,
pub gemm_bi_nn_tc64_typed: HalfKernel,
pub gemm_bi_nn_tc16_typed: HalfKernel,
pub gemm_bi_nn_tcw64_typed: HalfKernel,
pub gemm_bi_nn_tcwn64_typed: HalfKernel,
pub gemm_bi_nn_sm90_typed: Option<HalfKernel>,
pub gemm_bi_nn_sm100_typed: Option<HalfKernel>,
}
impl Deref for MambaKernels {
type Target = GemmBiKernels;
fn deref(&self) -> &Self::Target {
&self.triad
}
}
pub(crate) fn nvrtc_version() -> (i32, i32) {
let mut major: core::ffi::c_int = 0;
let mut minor: core::ffi::c_int = 0;
let rc = unsafe { cudarc::nvrtc::sys::nvrtcVersion(&mut major, &mut minor) };
if rc == cudarc::nvrtc::sys::nvrtcResult::NVRTC_SUCCESS {
(major, minor)
} else {
(0, 0)
}
}
pub(crate) fn kernel_cache_dir() -> Option<std::path::PathBuf> {
let path = match std::env::var("MAMBA_RS_KERNEL_CACHE") {
Ok(v) if matches!(v.trim(), "0" | "off" | "OFF") => None,
Ok(v) if !v.trim().is_empty() => Some(std::path::PathBuf::from(v.trim())),
_ => {
let base = std::env::var("XDG_CACHE_HOME")
.map(std::path::PathBuf::from)
.or_else(|_| {
std::env::var("HOME").map(|home| std::path::PathBuf::from(home).join(".cache"))
})
.ok()?;
Some(base.join("mamba-rs").join("kernels"))
}
}?;
super::kernel_identity::prepare_private_cache_dir(&path)
}
pub fn state_capacity(d_state: usize) -> Result<usize, String> {
if d_state == 0 {
return Err("d_state must be positive".into());
}
if d_state > 256 {
return Err(format!(
"d_state {d_state} exceeds the supported range (reference \
implementations go to 256)"
));
}
Ok(d_state.div_ceil(16) * 16)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct TriadModulePlan {
pub common: bool,
pub architecture: Option<super::kernel_identity::ModuleKind>,
}
impl MambaKernels {
pub(in crate::mamba_ssm::gpu) fn inference_terminal_function(
&self,
symbol: &str,
) -> Option<&CudaFunction> {
match symbol {
"f32_f32_s2" => Some(&self.gemm_bi_f32_f32_s2),
"f32_f32_n128_s2" => Some(&self.gemm_bi_f32_f32_n128_s2),
"nn_sm89_f32_n64_copyplan" => self.fixed_sm89_f32_n64_copyplan.as_ref(),
"nn_sm89_tc128_f32out_s3_bf16" => {
self.inference_sm89_bundle.half_f32out_s3_bf16.as_ref().ok()
}
"nn_sm89_tc128_f32out_s3_f16" => {
self.inference_sm89_bundle.half_f32out_s3_f16.as_ref().ok()
}
"nn_sm89_f32_m128n64_tail_copyplan" => {
self.inference_sm89_bundle.exact_m128n64_tail.as_ref().ok()
}
"nn_sm120_f32_n64_copyplan" => self.fixed_sm120_f32_n64_copyplan.as_ref(),
"nn_sm120_f32_n64_copyplan_t256" => self.fixed_sm120_f32_n64_copyplan_t256.as_ref(),
"nn_sm120_f32_n64_copyplan_m128n64_t256" => {
self.fixed_sm120_f32_m128n64_copyplan_t256.as_ref()
}
"nn_sm120_f32_n64_sliced" => self.fixed_sm120_f32_n64_sliced.as_ref(),
"bf16_bf16" => Some(&self.gemm_bi_bf16_bf16),
"matvec_bi_bf16_bf16" => Some(&self.matvec_bi_bf16_bf16),
"bf16_f32" => Some(&self.gemm_bi_bf16_f32),
"matvec_bi_bf16_f32" => Some(&self.matvec_bi_bf16_f32),
"nn_tc128_bf16" => Some(&self.gemm_bi_nn_tc128_typed.bf16),
"nn_tc128_f32out_bf16" => Some(&self.gemm_bi_nn_tc128_f32out.bf16),
"nn_tcw64_bf16" => Some(&self.gemm_bi_nn_tcw64_typed.bf16),
"nn_tcwn64_bf16" => Some(&self.gemm_bi_nn_tcwn64_typed.bf16),
"nn_tc64_bf16" => Some(&self.gemm_bi_nn_tc64_typed.bf16),
"nn_tc64_f32out_bf16" => Some(&self.gemm_bi_nn_tc64_f32out.bf16),
"nn_tc16_bf16" => Some(&self.gemm_bi_nn_tc16_typed.bf16),
"nn_tc16_f32out_bf16" => Some(&self.gemm_bi_nn_tc16_f32out.bf16),
"nn_sm89_tc128_pipeline_bf16" => Some(&self.fixed_sm89_half_pipeline.as_ref()?.bf16),
"nn_sm89_tc128_swizzle_bf16" => Some(&self.fixed_sm89_half_swizzle.as_ref()?.bf16),
"nn_sm89_tc128_s3_bf16" => Some(&self.fixed_sm89_half_s3.as_ref()?.bf16),
"nn_sm120_tma_64x64_bk64_s2_bf16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m64n64_bk64_s2.bf16)
}
"nn_sm120_tma_64x64_bk64_s2_f32out_bf16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m64n64_bk64_s2
.bf16,
),
"nn_sm120_tma_64x128_bk64_s2_bf16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m64n128_bk64_s2.bf16)
}
"nn_sm120_tma_64x128_bk64_s2_f32out_bf16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m64n128_bk64_s2
.bf16,
),
"nn_sm120_tma_128x64_bk32_s3_bf16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n64_bk32_s3.bf16)
}
"nn_sm120_tma_128x64_bk32_s3_f32out_bf16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n64_bk32_s3
.bf16,
),
"nn_sm120_tma_128x128_bk32_s2_bf16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n128_bk32_s2.bf16)
}
"nn_sm120_tma_128x128_bk32_s2_f32out_bf16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n128_bk32_s2
.bf16,
),
"nn_sm120_tma_128x128_bk32_s3_bf16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n128_bk32_s3.bf16)
}
"nn_sm120_tma_128x128_bk32_s3_f32out_bf16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n128_bk32_s3
.bf16,
),
"nn_sm90a_wgmma_wg1_bf16" => Some(&self.gemm_bi_nn_sm90_typed.as_ref()?.bf16),
"nn_sm100_tcgen_c4_bf16" => Some(&self.gemm_bi_nn_sm100_typed.as_ref()?.bf16),
"f16_f16" => Some(&self.gemm_bi_f16_f16),
"matvec_bi_f16_f16" => Some(&self.matvec_bi_f16_f16),
"f16_f32" => Some(&self.gemm_bi_f16_f32),
"matvec_bi_f16_f32" => Some(&self.matvec_bi_f16_f32),
"nn_tc128_f16" => Some(&self.gemm_bi_nn_tc128_typed.f16),
"nn_tc128_f32out_f16" => Some(&self.gemm_bi_nn_tc128_f32out.f16),
"nn_tcw64_f16" => Some(&self.gemm_bi_nn_tcw64_typed.f16),
"nn_tcwn64_f16" => Some(&self.gemm_bi_nn_tcwn64_typed.f16),
"nn_tc64_f16" => Some(&self.gemm_bi_nn_tc64_typed.f16),
"nn_tc64_f32out_f16" => Some(&self.gemm_bi_nn_tc64_f32out.f16),
"nn_tc16_f16" => Some(&self.gemm_bi_nn_tc16_typed.f16),
"nn_tc16_f32out_f16" => Some(&self.gemm_bi_nn_tc16_f32out.f16),
"nn_sm89_tc128_pipeline_f16" => Some(&self.fixed_sm89_half_pipeline.as_ref()?.f16),
"nn_sm89_tc128_swizzle_f16" => Some(&self.fixed_sm89_half_swizzle.as_ref()?.f16),
"nn_sm89_tc128_s3_f16" => Some(&self.fixed_sm89_half_s3.as_ref()?.f16),
"nn_sm120_tma_64x64_bk64_s2_f16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m64n64_bk64_s2.f16)
}
"nn_sm120_tma_64x64_bk64_s2_f32out_f16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m64n64_bk64_s2
.f16,
),
"nn_sm120_tma_64x128_bk64_s2_f16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m64n128_bk64_s2.f16)
}
"nn_sm120_tma_64x128_bk64_s2_f32out_f16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m64n128_bk64_s2
.f16,
),
"nn_sm120_tma_128x64_bk32_s3_f16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n64_bk32_s3.f16)
}
"nn_sm120_tma_128x64_bk32_s3_f32out_f16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n64_bk32_s3
.f16,
),
"nn_sm120_tma_128x128_bk32_s2_f16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n128_bk32_s2.f16)
}
"nn_sm120_tma_128x128_bk32_s2_f32out_f16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n128_bk32_s2
.f16,
),
"nn_sm120_tma_128x128_bk32_s3_f16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n128_bk32_s3.f16)
}
"nn_sm120_tma_128x128_bk32_s3_f32out_f16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n128_bk32_s3
.f16,
),
"nn_sm90a_wgmma_wg1_f16" => Some(&self.gemm_bi_nn_sm90_typed.as_ref()?.f16),
"nn_sm100_tcgen_c4_f16" => Some(&self.gemm_bi_nn_sm100_typed.as_ref()?.f16),
"matvec_bi_f32_f32" => Some(&self.matvec_bi_f32_f32),
"nn_sm89_m64n64_bk64_s3_f16" => self.fixed_sm89_half_m64n64_s3_f16.as_ref(),
"nn_sm89_m128n64_bk64_s2_f16" => self.fixed_sm89_half_m128n64_s2_f16.as_ref(),
"nn_tf32_m128n64_bk32_s2" => Some(&self.gemm_bi_nn_tf32.m128n64_s2),
"nn_tf32_m128n64_bk32_s3" => Some(&self.gemm_bi_nn_tf32.m128n64_s3),
"nn_tf32_m64n64_bk32_s2" => Some(&self.gemm_bi_nn_tf32.m64n64_s2),
"nn_tf32_m64n64_bk32_s3" => Some(&self.gemm_bi_nn_tf32.m64n64_s3),
"nn_tf32_m16n32_bk32_s4" => Some(&self.gemm_bi_nn_tf32.m16n32_s4),
"nn_rna_wide_tf32_m128n128_bk32_s3" => self.fixed_sm89_tf32_rna_wide.as_ref(),
"nn_sm89_rna_tf32_m128n96_bk32_s3" => self.fixed_sm89_tf32_rna_n96.as_ref(),
"nn_sm80_mma_tf32_m128n128_bk32_s3" => self.triad_kernels().tf32_function(symbol),
"nn_sm120_tma_tf32_m128n64_bk32_s2" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m128n64_s2)
}
"nn_sm120_tma_tf32_m128n64_bk32_s3" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m128n64_s3)
}
"nn_sm120_tma_tf32_m64n128_bk32_s2" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m64n128_s2)
}
"nn_sm120_tma_tf32_m64n128_bk32_s3" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m64n128_s3)
}
"nn_sm120_tma_tf32_m64n64_bk32_s2_producer_warp" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m64n64_s2_producer_warp)
}
"nn_sm120_tma_tf32_m64n64_bk32_s2" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m64n64_s2)
}
"nn_sm120_tma_tf32_m64n64_bk32_s2_pair_store" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m64n64_s2_pair_store)
}
"nn_sm120_tma_fma_postbias_m128n64_bk16_s2" => {
Some(&self.fixed_sm120_fma_postbias.as_ref()?.m128n64)
}
"nn_sm120_tma_fma_postbias_m64n128_bk16_s2" => {
Some(&self.fixed_sm120_fma_postbias.as_ref()?.m64n128)
}
"nn_sm120_tma_fma_postbias_m128n96_bk16_s2" => {
Some(&self.fixed_sm120_fma_postbias.as_ref()?.m128n96)
}
"nn_sm120_tma_fma_postbias_m128n64_bk16_s2_k4" => {
self.fixed_sm120_fma_postbias.as_ref()?.m128n64_k4.as_ref()
}
"nn_sm120_tma_fma_postbias_m128n64_t256_bk16_s2" => self
.fixed_sm120_fma_postbias
.as_ref()?
.m128n64_t256
.as_ref(),
"nn_sm120_tma_fma_nobias_m128n64_t256_bk16_s2" => self
.fixed_sm120_fma_postbias
.as_ref()?
.nobias_m128n64_t256
.as_ref(),
other => self.fixed_sm89_cells.get(other),
}
}
pub(crate) fn triad_module_plan(
arch: &str,
device_cc: Option<(i32, i32)>,
nvrtc: (i32, i32),
) -> TriadModulePlan {
use super::kernel_identity::ModuleKind;
let common = super::gemm_bi_triad::modules::sm80_tier_module_compiles(arch, device_cc);
let architecture = match (arch, device_cc) {
("sm_90a", Some((9, 0))) if nvrtc >= (13, 3) => Some(ModuleKind::TriadSm90a),
("sm_90a", Some((9, 0))) => None,
("sm_100a", Some((10, 0)))
| ("sm_103a", Some((10, 3)))
| ("sm_107a", Some((10, 7)))
| ("sm_110a", Some((11, 0))) => Some(ModuleKind::TriadSm100),
(_, Some((12, _))) => Some(ModuleKind::TriadSm120),
_ => None,
};
TriadModulePlan {
common,
architecture,
}
}
pub fn compile(ctx: &Arc<CudaContext>, arch: &'static str) -> Result<Self, String> {
Self::compile_with_state_cap(ctx, arch, 64)
}
pub fn compile_with_state_cap(
ctx: &Arc<CudaContext>,
arch: &'static str,
state_cap: usize,
) -> Result<Self, String> {
let device_cc = match ctx.compute_capability() {
Ok(device_cc) => Some(device_cc),
Err(error) => {
static UNKNOWN_CC: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&UNKNOWN_CC, || {
format!(
"the driver did not report a compute capability ({error:?}); only the \
portable kernels are compiled"
)
});
None
}
};
let sm120_board = device_cc.is_some_and(|(major, minor)| {
u32::try_from(major)
.and_then(|major| Ok((major, u32::try_from(minor)?)))
.is_ok_and(super::device::is_sm120_family)
});
if !sm120_board && matches!(arch, "compute_120" | "compute_121") {
static UNQUALIFIED: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&UNQUALIFIED, || {
format!(
"compute capability {device_cc:?} is not a qualified family; only the \
portable kernels serve it"
)
});
}
let sm120_artifacts = match device_cc {
Some(device_cc) if sm120_board => {
super::gemm_bi_triad::modules::compile_sm120_artifact_set(
ctx,
state_cap,
device_cc,
nvrtc_version(),
)
}
_ => None,
};
let compile = |module_kind| {
super::gemm_bi_triad::modules::compile_module(
super::gemm_bi_triad::modules::CompileModuleRequest {
ctx,
arch,
state_cap,
module_kind,
},
)
};
let plan = Self::triad_module_plan(arch, device_cc, nvrtc_version());
let (
fixed,
scalar,
sm80,
finalist,
finalist_rejection,
sm89_half,
sm89_half_rejection,
sm89_exact_f32,
sm89_exact_f32_rejection,
sm89_exact_f32_d128,
sm89_exact_f32_d128_rejection,
sm89_tf32_joint,
sm89_tf32_joint_rejection,
specialized,
) = if let Some(artifacts) = sm120_artifacts {
let set_target = artifacts.fixed.compiler_identity.target;
let tier_arch = device_cc
.and_then(|device_cc| {
super::gemm_bi_triad::sm120_target_candidates(device_cc, nvrtc_version())
.iter()
.map(|candidate| candidate.nvrtc_arch)
.find(|candidate| *candidate == set_target.as_str())
})
.unwrap_or(arch);
let portable_tier = |module_kind| {
if Self::triad_module_plan(tier_arch, device_cc, nvrtc_version()).common {
match super::gemm_bi_triad::modules::compile_module(
super::gemm_bi_triad::modules::CompileModuleRequest {
ctx,
arch: tier_arch,
state_cap,
module_kind,
},
) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
}
};
let (finalist, finalist_rejection) =
portable_tier(super::kernel_identity::ModuleKind::TriadSm89Finalist);
let (sm89_half, sm89_half_rejection) =
portable_tier(super::kernel_identity::ModuleKind::TriadSm89Half);
let (sm89_exact_f32, sm89_exact_f32_rejection) =
portable_tier(super::kernel_identity::ModuleKind::TriadSm89ExactF32);
let (sm89_exact_f32_d128, sm89_exact_f32_d128_rejection) =
portable_tier(super::kernel_identity::ModuleKind::TriadSm89ExactF32D128);
let (sm89_tf32_joint, sm89_tf32_joint_rejection) =
portable_tier(super::kernel_identity::ModuleKind::TriadSm89Tf32Joint);
(
artifacts.fixed,
artifacts.scalar,
artifacts.sm80,
finalist,
finalist_rejection,
sm89_half,
sm89_half_rejection,
sm89_exact_f32,
sm89_exact_f32_rejection,
sm89_exact_f32_d128,
sm89_exact_f32_d128_rejection,
sm89_tf32_joint,
sm89_tf32_joint_rejection,
artifacts.specialized,
)
} else {
let fixed = compile(super::kernel_identity::ModuleKind::Fixed)?;
let scalar = compile(super::kernel_identity::ModuleKind::TriadScalar)?;
let sm80 = compile(super::kernel_identity::ModuleKind::TriadSm80)?;
let (finalist, finalist_rejection) = if plan.common {
match compile(super::kernel_identity::ModuleKind::TriadSm89Finalist) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
};
let (sm89_half, sm89_half_rejection) = if plan.common {
match compile(super::kernel_identity::ModuleKind::TriadSm89Half) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
};
let (sm89_exact_f32, sm89_exact_f32_rejection) = if plan.common {
match compile(super::kernel_identity::ModuleKind::TriadSm89ExactF32) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
};
let (sm89_exact_f32_d128, sm89_exact_f32_d128_rejection) = if plan.common {
match compile(super::kernel_identity::ModuleKind::TriadSm89ExactF32D128) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
};
let (sm89_tf32_joint, sm89_tf32_joint_rejection) = if plan.common {
match compile(super::kernel_identity::ModuleKind::TriadSm89Tf32Joint) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
};
let specialized = match (arch, device_cc) {
("sm_90a", Some((9, 0)))
if plan.architecture
== Some(super::kernel_identity::ModuleKind::TriadSm90a) =>
{
compile(super::kernel_identity::ModuleKind::TriadSm90a)
.ok()
.and_then(|module| {
super::gemm_bi_triad::modules::qualify_specialized_module(module).ok()
})
}
("sm_90a", Some((9, 0))) => {
static WGMMA_TOOLKIT: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&WGMMA_TOOLKIT, || {
format!(
"the SM90a WGMMA module needs CUDA 13.3 or newer (ptxas before 13.3 \
could drop the register moves after wgmma.wait_group); NVRTC {:?} \
serves this board with the common tier",
nvrtc_version()
)
});
None
}
("sm_100a", Some(device_cc @ (10, 0)))
| ("sm_103a", Some(device_cc @ (10, 3)))
| ("sm_107a", Some(device_cc @ (10, 7))) => {
super::gemm_bi_triad::modules::compile_sm100_optional(ctx, state_cap, device_cc)
}
("sm_110a", Some(device_cc @ (11, 0))) => {
super::gemm_bi_triad::modules::compile_sm100_optional(ctx, state_cap, device_cc)
}
_ => None,
};
(
fixed,
scalar,
sm80,
finalist,
finalist_rejection,
sm89_half,
sm89_half_rejection,
sm89_exact_f32,
sm89_exact_f32_rejection,
sm89_exact_f32_d128,
sm89_exact_f32_d128_rejection,
sm89_tf32_joint,
sm89_tf32_joint_rejection,
specialized,
)
};
let (sm89_cells_module, sm89_cells_rejection) = if plan.common
&& super::gemm_bi_triad::modules::fixed_portable_overlay_composed(arch)
{
match compile(super::kernel_identity::ModuleKind::InferenceSm89Cells) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
};
if let Some(error) = sm89_cells_rejection.as_ref() {
static CELLS: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&CELLS, || {
format!(
"the SM89 inference cells module did not compile ({error}); the Fixed \
overlay serves its shapes without the cells"
)
});
}
let compiler_identity = fixed.compiler_identity;
let fold_transport_admitted = super::fold_transport::compiler_admitted(
device_cc,
arch,
state_cap,
compiler_identity.nvrtc_version,
);
let (fixed_sm89_half_pipeline, fixed_sm89_half_pipeline_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_half_pipeline(ctx, &fixed);
let (fixed_sm89_half_swizzle, fixed_sm89_half_swizzle_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_half_swizzle(ctx, &fixed);
let (fixed_sm89_half_s3, fixed_sm89_half_s3_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_half_s3(ctx, &fixed);
let (fixed_sm89_tf32_rna_wide, fixed_sm89_tf32_rna_wide_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_rna_wide(ctx, &fixed);
let (fixed_sm89_tf32_rna_n96, fixed_sm89_tf32_rna_n96_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_rna_n96(ctx, &fixed);
let (fixed_sm89_half_m64n64_s3_f16, fixed_sm89_half_m64n64_s3_f16_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_half_m64n64_s3(ctx, &fixed);
let (fixed_sm89_half_m128n64_s2_f16, fixed_sm89_half_m128n64_s2_f16_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_half_m128n64_s2(ctx, &fixed);
let (fixed_sm89_f32_n64_copyplan, fixed_sm89_f32_n64_copyplan_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_f32_n64_copyplan(ctx, &fixed);
let (fixed_sm89_cells, fixed_sm89_cell_rejections) = match sm89_cells_module.as_ref() {
Some(module) => super::gemm_bi_triad::modules::load_fixed_sm89_cells(ctx, module),
None => (std::collections::HashMap::new(), Vec::new()),
};
let (fixed_sm120_f32_n64_copyplan, fixed_sm120_f32_n64_copyplan_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm120_f32_n64_copyplan(ctx, &fixed);
let (fixed_sm120_f32_n64_copyplan_t256, fixed_sm120_f32_n64_copyplan_t256_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm120_f32_n64_copyplan_t256(ctx, &fixed);
let (
fixed_sm120_f32_m128n64_copyplan_t256,
fixed_sm120_f32_m128n64_copyplan_t256_rejection,
) = super::gemm_bi_triad::modules::load_fixed_sm120_f32_m128n64_copyplan_t256(ctx, &fixed);
let (fixed_sm120_f32_n64_sliced, fixed_sm120_f32_n64_sliced_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm120_f32_n64_sliced(ctx, &fixed);
let (fixed_sm120_fma_postbias, fixed_sm120_fma_postbias_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm120_fma_postbias(ctx, &fixed);
let inference_sm89_bundle =
super::gemm_bi_triad::modules::inference_bundle::load_inference_sm89_bundle(
ctx, &fixed, state_cap,
);
let triad = GemmBiKernels::load(
ctx,
super::gemm_bi_triad::modules::GemmBiModuleSet {
fixed_artifact: fixed.artifact_identity,
scalar,
sm80,
finalist,
finalist_compile_rejection: finalist_rejection,
sm89_half,
sm89_half_compile_rejection: sm89_half_rejection,
sm89_exact_f32,
sm89_exact_f32_compile_rejection: sm89_exact_f32_rejection,
sm89_exact_f32_d128,
sm89_exact_f32_d128_compile_rejection: sm89_exact_f32_d128_rejection,
sm89_tf32_joint,
sm89_tf32_joint_compile_rejection: sm89_tf32_joint_rejection,
specialized,
sm89_cells: sm89_cells_module
.as_ref()
.map(|module| module.artifact_identity),
},
)?;
if let Some(reason) = triad.portable_tf32_rejection() {
static PORTABLE: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&PORTABLE, || {
format!(
"the portable TF32 routes are not bound on this board ({reason}); the \
exact f32 kernels serve every TF32 request"
)
});
}
for (member, slot) in [
(
"nn_sm89_tc128_f32out_s3_bf16",
&inference_sm89_bundle.half_f32out_s3_bf16,
),
(
"nn_sm89_tc128_f32out_s3_f16",
&inference_sm89_bundle.half_f32out_s3_f16,
),
(
"nn_sm89_f32_m128n64_tail_copyplan",
&inference_sm89_bundle.exact_m128n64_tail,
),
] {
if let Err(reason) = slot {
static RETAINED: std::sync::Once = std::sync::Once::new();
let reason = reason.clone();
super::diagnostics::warn_once(&RETAINED, || {
format!(
"the retained Inference member {member} is not bound on this board ({reason}); its shapes fall back to the legacy kernels"
)
});
}
}
if super::gemm_bi_triad::modules::fixed_portable_overlay_composed(arch)
&& !fixed_sm89_cell_rejections.is_empty()
{
static CELLS: std::sync::Once = std::sync::Once::new();
let listed = fixed_sm89_cell_rejections
.iter()
.map(|(symbol, reason)| format!("{symbol} ({reason})"))
.collect::<Vec<_>>()
.join(", ");
super::diagnostics::warn_once(&CELLS, || {
format!(
"the Ada inference cells {listed} are not bound on this board; the ladder \
serves their shapes"
)
});
}
let excluded = triad.tf32_excluded_symbols();
if !excluded.is_empty() {
static EXCLUDED: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&EXCLUDED, || {
format!(
"{} TF32 route(s) are excluded on this toolkit and decline to the exact \
family: {}",
excluded.len(),
excluded
.iter()
.map(|exclusion| exclusion.reason.as_str())
.collect::<Vec<_>>()
.join("; ")
)
});
}
if let Some(reason) = triad.specialized_tf32_rejection() {
static SPECIALIZED: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&SPECIALIZED, || {
format!(
"the specialized TF32 module is not bound on this board ({reason}); the \
portable or exact kernels serve every TF32 request"
)
});
}
if let Some(reason) = triad.finalist_tf32_rejection() {
static FINALIST: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&FINALIST, || {
format!(
"the optional Ada TF32 finalist is not bound ({reason}); the portable or exact kernels remain available"
)
});
}
if let Some(reason) = triad.sm89_half_rejection() {
static HALF_MODULE: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&HALF_MODULE, || {
format!(
"the optional Ada half-Triad module is not bound ({reason}); the existing typed kernels remain available"
)
});
}
let half_excluded = triad.sm89_half_exclusions();
if !half_excluded.is_empty() {
static HALF_EXCLUDED: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&HALF_EXCLUDED, || {
format!(
"{} Ada half-Triad symbol(s) failed resource admission while their siblings remain bound: {}",
half_excluded.len(),
half_excluded
.iter()
.map(|exclusion| format!("{}: {}", exclusion.symbol, exclusion.reason))
.collect::<Vec<_>>()
.join("; ")
)
});
}
if let Some(reason) = triad.sm89_exact_f32_rejection() {
static EXACT_F32_MODULE: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&EXACT_F32_MODULE, || {
format!(
"the optional Ada exact-F32 Triad module is not bound ({reason}); the existing exact kernels remain available"
)
});
}
for exclusion in triad.sm89_exact_f32_exclusions() {
static D768_IN: std::sync::Once = std::sync::Once::new();
static D768_OUT: std::sync::Once = std::sync::Once::new();
static PRISM: std::sync::Once = std::sync::Once::new();
let once = match exclusion.symbol {
super::gemm_bi_triad::D768_IN_FUSED_SYMBOL => &D768_IN,
super::gemm_bi_triad::D768_OUT_RAW_SYMBOL => &D768_OUT,
super::gemm_bi_triad::PRISM_RAW_SYMBOL => &PRISM,
_ => continue,
};
super::diagnostics::warn_once(once, || {
format!(
"Ada exact-F32 Triad symbol {} is excluded while its siblings remain available: {}",
exclusion.symbol, exclusion.reason
)
});
}
if let Some(reason) = triad.sm89_exact_f32_d128_rejection() {
static EXACT_F32_D128_MODULE: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&EXACT_F32_D128_MODULE, || {
format!(
"the optional Ada exact-F32 d128 Triad module is not bound ({reason}); the SplitM64 kernels remain available"
)
});
}
for exclusion in triad.sm89_exact_f32_d128_exclusions() {
static D128_IN: std::sync::Once = std::sync::Once::new();
static D128_OUT: std::sync::Once = std::sync::Once::new();
let once = match exclusion.symbol {
super::gemm_bi_triad::D128_IN_SYMBOL => &D128_IN,
super::gemm_bi_triad::D128_OUT_SYMBOL => &D128_OUT,
_ => continue,
};
super::diagnostics::warn_once(once, || {
format!(
"Ada exact-F32 d128 Triad symbol {} is excluded while its sibling remains available: {}",
exclusion.symbol, exclusion.reason
)
});
}
if let Some(reason) = triad.sm89_tf32_joint_rejection() {
static TF32_JOINT_MODULE: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&TF32_JOINT_MODULE, || {
format!(
"the optional Ada TF32 joint module is not bound ({reason}); the existing deterministic TF32 kernels remain available"
)
});
}
for exclusion in triad.sm89_tf32_joint_exclusions() {
static TRANSPOSE: std::sync::Once = std::sync::Once::new();
static TN_N96: std::sync::Once = std::sync::Once::new();
static TN_M64N64: std::sync::Once = std::sync::Once::new();
static TN_M64N96_S2: std::sync::Once = std::sync::Once::new();
static NN_N96: std::sync::Once = std::sync::Once::new();
static NN_N96_BASELINE: std::sync::Once = std::sync::Once::new();
static NT_A_LDMATRIX_N96: std::sync::Once = std::sync::Once::new();
static NT_RNA_M144N96_S2: std::sync::Once = std::sync::Once::new();
static NT_ROWSTAGE_M128N192_S2: std::sync::Once = std::sync::Once::new();
static TN_DIRECT_M192N192_S2: std::sync::Once = std::sync::Once::new();
static TN_M96N192_S2: std::sync::Once = std::sync::Once::new();
static TN_M96N96_S3: std::sync::Once = std::sync::Once::new();
let once = match exclusion.symbol {
super::gemm_bi_triad::TN_PRE_RNA_TRANSPOSE_SYMBOL => &TRANSPOSE,
super::gemm_bi_triad::TN_PRE_RNA_N96_SYMBOL => &TN_N96,
super::gemm_bi_triad::TN_PRE_RNA_M64N64_SYMBOL => &TN_M64N64,
super::gemm_bi_triad::TN_PRE_RNA_M64N96_S2_SYMBOL => &TN_M64N96_S2,
super::gemm_bi_triad::NN_ADD_HALF_DIRECT_N96_SYMBOL => &NN_N96,
super::gemm_bi_triad::NN_ADD_HALF_N96_SYMBOL => &NN_N96_BASELINE,
super::gemm_bi_triad::NT_A_LDMATRIX_N96_SYMBOL => &NT_A_LDMATRIX_N96,
super::gemm_bi_triad::NT_RNA_M144N96_S2_SYMBOL => &NT_RNA_M144N96_S2,
super::gemm_bi_triad::NT_ROWSTAGE_M128N192_S2_SYMBOL => &NT_ROWSTAGE_M128N192_S2,
super::gemm_bi_triad::TN_DIRECT_M192N192_S2_SYMBOL => &TN_DIRECT_M192N192_S2,
super::gemm_bi_triad::TN_PRE_RNA_M96N192_S2_SYMBOL => &TN_M96N192_S2,
super::gemm_bi_triad::TN_PRE_RNA_M96N96_S3_SYMBOL => &TN_M96N96_S3,
_ => continue,
};
super::diagnostics::warn_once(once, || {
format!(
"Ada TF32 joint symbol {} is excluded while its siblings remain available: {}",
exclusion.symbol, exclusion.reason
)
});
}
let module = fixed.module;
let get = |name: &str| -> Result<CudaFunction, String> {
module
.load_function(name)
.map_err(|e| format!("Kernel '{name}' not found: {e:?}"))
};
let load_typed = |base: &str| -> Result<TypedKernel, String> {
Ok(TypedKernel {
f32: get(&format!("{base}_f32"))?,
bf16: get(&format!("{base}_bf16"))?,
f16: get(&format!("{base}_f16"))?,
})
};
let load_half = |base: &str| -> Result<HalfKernel, String> {
Ok(HalfKernel {
bf16: get(&format!("{base}_bf16"))?,
f16: get(&format!("{base}_f16"))?,
})
};
let load_half_dynsmem = |base: &str, bytes: i32| -> Result<HalfKernel, String> {
let k = load_half(base)?;
for f in [&k.bf16, &k.f16] {
f.set_attribute(
cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
bytes,
)
.map_err(|e| format!("set MAX_DYNAMIC_SHARED for {base}: {e:?}"))?;
}
Ok(k)
};
let load_sm120_half = |suffix: &str| -> Result<FixedSm120HalfKernels, String> {
let kernels = FixedSm120HalfKernels {
m64n64_bk64_s2: load_half(&format!("nn_sm120_tma_64x64_bk64_s2{suffix}"))?,
m64n128_bk64_s2: load_half(&format!("nn_sm120_tma_64x128_bk64_s2{suffix}"))?,
m128n64_bk32_s3: load_half(&format!("nn_sm120_tma_128x64_bk32_s3{suffix}"))?,
m128n128_bk32_s2: load_half(&format!("nn_sm120_tma_128x128_bk32_s2{suffix}"))?,
m128n128_bk32_s3: load_half(&format!("nn_sm120_tma_128x128_bk32_s3{suffix}"))?,
};
for (tile, bytes) in [
(&kernels.m64n64_bk64_s2, 32_896),
(&kernels.m64n128_bk64_s2, 49_280),
(&kernels.m128n64_bk32_s3, 36_992),
(&kernels.m128n128_bk32_s2, 32_896),
(&kernels.m128n128_bk32_s3, 49_280),
] {
for function in [&tile.bf16, &tile.f16] {
function
.set_attribute(
cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
bytes,
)
.map_err(|error| {
format!(
"set MAX_DYNAMIC_SHARED for Fixed SM120 half{suffix}: {error:?}"
)
})?;
}
}
Ok(kernels)
};
Ok(Self {
state_cap,
compiler_identity,
triad,
ssm_step_fwd: get("ssm_step_forward")?,
ssm_burnin_fwd: get("ssm_burnin_forward")?,
ssm_burnin_fwd_nosave: get("ssm_burnin_forward_nosave")?,
ssm_backward_local: get("ssm_backward_local")?,
ssm_reduce_d_bc_typed: TypedKernel {
f32: get("ssm_reduce_d_BC_f32")?,
bf16: get("ssm_reduce_d_BC_bf16")?,
f16: get("ssm_reduce_d_BC_f16")?,
},
ssm_reduce_d_bc_tmajor_typed: TypedKernel {
f32: get("ssm_reduce_d_BC_tmajor_f32")?,
bf16: get("ssm_reduce_d_BC_tmajor_bf16")?,
f16: get("ssm_reduce_d_BC_tmajor_f16")?,
},
ssm_reduce_d_d: get("ssm_reduce_d_D")?,
ssm_reduce_d_a_log: get("ssm_reduce_d_a_log")?,
ssm_reduce_d_a_log_chunks: get("ssm_reduce_d_a_log_chunks")?,
conv1d_burnin_fwd_nosave_tiled: get("conv1d_burnin_forward_nosave_tiled_f32")?,
conv1d_burnin_nosave_tiled_typed: load_typed("conv1d_burnin_forward_nosave_tiled")?,
softplus_fwd: get("softplus_forward")?,
softplus_bwd: get("softplus_backward")?,
rmsnorm_fwd: get("rmsnorm_forward")?,
rmsnorm_bwd: get("rmsnorm_backward")?,
bias_broadcast: get("bias_broadcast")?,
colsum_accumulate: get("colsum_accumulate")?,
reduce_sum_axis0: get("reduce_sum_axis0")?,
vec_add_inplace: get("vec_add_inplace")?,
exp_negate: get("exp_negate")?,
exp_negate2: get("exp_negate2")?,
gather_cols: get("gather_cols")?,
gather_bc_cols: get("gather_bc_cols")?,
gather_bc_cols_tmajor: get("gather_bc_cols_tmajor")?,
gather_bc_cols_tmajor_tiled: get("gather_bc_cols_tmajor_tiled")?,
gate_mul_silu: get("gate_mul_silu")?,
gating_backward: get("gating_backward")?,
residual_add: get("residual_add")?,
gather_last_timestep: get("gather_last_timestep")?,
cast_f32_to_bf16: get("cast_f32_to_bf16")?,
cast_f32_to_f16: get("cast_f32_to_f16")?,
cast_bf16_to_f32: get("cast_bf16_to_f32")?,
cast_f16_to_f32: get("cast_f16_to_f32")?,
ssm_burnin_fwd_bf16: get("ssm_burnin_forward_bf16")?,
ssm_burnin_fwd_f16: get("ssm_burnin_forward_f16")?,
ssm_parallel_fwd: get("ssm_parallel_scan_fwd")?,
ssm_parallel_fwd_nosave: get("ssm_parallel_scan_fwd_nosave")?,
ssm_parallel_fwd_typed: TypedKernel {
f32: get("ssm_parallel_scan_fwd")?,
bf16: get("ssm_parallel_scan_fwd_bf16")?,
f16: get("ssm_parallel_scan_fwd_f16")?,
},
ssm_parallel_fwd_nosave_typed: TypedKernel {
f32: get("ssm_parallel_scan_fwd_nosave")?,
bf16: get("ssm_parallel_scan_fwd_nosave_bf16")?,
f16: get("ssm_parallel_scan_fwd_nosave_f16")?,
},
ssm_parallel_bwd_typed: TypedKernel {
f32: get("ssm_parallel_scan_bwd_f32")?,
bf16: get("ssm_parallel_scan_bwd_bf16")?,
f16: get("ssm_parallel_scan_bwd_f16")?,
},
ssm_parallel_bwd_fold_typed: {
let k = TypedKernel {
f32: get("ssm_parallel_scan_bwd_fold_f32")?,
bf16: get("ssm_parallel_scan_bwd_fold_bf16")?,
f16: get("ssm_parallel_scan_bwd_fold_f16")?,
};
for f in [&k.f32, &k.bf16, &k.f16] {
f.set_attribute(
cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
67_584,
)
.map_err(|e| {
format!("set MAX_DYNAMIC_SHARED for scan_bwd_fold: {e:?}")
})?;
}
k
},
ssm_parallel_bwd_fold_short_typed: if fold_transport_admitted {
Some(load_half_dynsmem(
"ssm_parallel_scan_bwd_fold_short",
67_584,
)?)
} else {
None
},
ssm_parallel_bwd_fold_staged_typed: if fold_transport_admitted {
Some(load_half_dynsmem(
"ssm_parallel_scan_bwd_fold_staged",
67_584,
)?)
} else {
None
},
check_inf_nan_f32: get("check_inf_nan_f32")?,
scale_grads_f32: get("scale_grads_f32")?,
scale_grads_skip_f32: get("scale_grads_skip_f32")?,
grad_sumsq_partial_f32: get("grad_sumsq_partial_f32")?,
grad_clip_coef_f32: get("grad_clip_coef_f32")?,
scale_grads_dev_f32: get("scale_grads_dev_f32")?,
grad_region_sumsq_partial_f32: get("grad_region_sumsq_partial_f32")?,
grad_region_scale_dev_f32: get("grad_region_scale_dev_f32")?,
adamw_step_f32: get("adamw_step_f32")?,
adamw_step_f32_capturable: get("adamw_step_f32_capturable")?,
adamw_step_multi: load_typed("adamw_step_multi")?,
gemm_bi_bf16_bf16: get("bf16_bf16")?,
gemm_bi_f16_f16: get("f16_f16")?,
gemm_bi_bf16_f32: get("bf16_f32")?,
gemm_bi_f16_f32: get("f16_f32")?,
gemm_bi_f32_f32: get("f32_f32")?,
gemm_bi_f32_f32_s2: get("f32_f32_s2")?,
gemm_bi_f32_f32_n128_s2: get("f32_f32_n128_s2")?,
fixed_sm89_half_pipeline,
fixed_sm89_half_pipeline_rejection,
fixed_sm89_half_swizzle,
fixed_sm89_half_swizzle_rejection,
fixed_sm89_half_s3,
fixed_sm89_half_s3_rejection,
fixed_sm89_tf32_rna_wide,
fixed_sm89_tf32_rna_wide_rejection,
fixed_sm89_tf32_rna_n96,
fixed_sm89_tf32_rna_n96_rejection,
fixed_sm89_half_m64n64_s3_f16,
fixed_sm89_half_m64n64_s3_f16_rejection,
fixed_sm89_half_m128n64_s2_f16,
fixed_sm89_half_m128n64_s2_f16_rejection,
fixed_sm89_f32_n64_copyplan,
fixed_sm89_f32_n64_copyplan_rejection,
fixed_sm89_cells,
sm89_cells_compiler_identity: sm89_cells_module
.as_ref()
.map(|module| module.compiler_identity),
inference_sm89_bundle,
fixed_sm120_f32_n64_copyplan,
fixed_sm120_f32_n64_copyplan_rejection,
fixed_sm120_f32_n64_copyplan_t256,
fixed_sm120_f32_n64_copyplan_t256_rejection,
fixed_sm120_f32_m128n64_copyplan_t256,
fixed_sm120_f32_m128n64_copyplan_t256_rejection,
fixed_sm120_f32_n64_sliced,
fixed_sm120_f32_n64_sliced_rejection,
fixed_sm120_fma_postbias,
fixed_sm120_fma_postbias_rejection,
gemm_bi_nn_tf32: {
let kernels = FixedTf32Kernels {
m128n64_s2: get("nn_tf32_m128n64_bk32_s2")?,
m128n64_s3: get("nn_tf32_m128n64_bk32_s3")?,
m64n64_s2: get("nn_tf32_m64n64_bk32_s2")?,
m64n64_s3: get("nn_tf32_m64n64_bk32_s3")?,
m16n32_s4: get("nn_tf32_m16n32_bk32_s4")?,
};
for (function, bytes) in [
(&kernels.m128n64_s2, 55_296),
(&kernels.m128n64_s3, 82_944),
(&kernels.m64n64_s2, 32_768),
(&kernels.m64n64_s3, 55_296),
(&kernels.m16n32_s4, 29_696),
] {
function
.set_attribute(
cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
bytes,
)
.map_err(|error| {
format!("set MAX_DYNAMIC_SHARED for Fixed TF32: {error:?}")
})?;
}
kernels
},
gemm_bi_nn_tf32_sm120: if sm120_board
&& matches!(arch, "sm_120" | "sm_121" | "compute_120" | "compute_121")
{
let kernels = FixedSm120Tf32Kernels {
m128n64_s2: get("nn_sm120_tma_tf32_m128n64_bk32_s2")?,
m128n64_s3: get("nn_sm120_tma_tf32_m128n64_bk32_s3")?,
m64n128_s2: get("nn_sm120_tma_tf32_m64n128_bk32_s2")?,
m64n128_s3: get("nn_sm120_tma_tf32_m64n128_bk32_s3")?,
m64n64_s2_producer_warp: get("nn_sm120_tma_tf32_m64n64_bk32_s2_producer_warp")?,
m64n64_s2: get("nn_sm120_tma_tf32_m64n64_bk32_s2")?,
m64n64_s2_pair_store: get("nn_sm120_tma_tf32_m64n64_bk32_s2_pair_store")?,
};
for (function, bytes) in [
(&kernels.m128n64_s2, 49_280),
(&kernels.m128n64_s3, 73_856),
(&kernels.m64n128_s2, 49_280),
(&kernels.m64n128_s3, 73_856),
(&kernels.m64n64_s2_producer_warp, 32_896),
(&kernels.m64n64_s2, 32_896),
(&kernels.m64n64_s2_pair_store, 32_896),
] {
function
.set_attribute(
cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
bytes,
)
.map_err(|error| {
format!("set MAX_DYNAMIC_SHARED for Fixed SM120 TF32: {error:?}")
})?;
}
Some(kernels)
} else {
None
},
gemm_bi_nn_half_sm120: if sm120_board
&& matches!(arch, "sm_120" | "sm_121" | "compute_120" | "compute_121")
{
Some(load_sm120_half("")?)
} else {
None
},
gemm_bi_nn_half_sm120_f32out: if sm120_board
&& matches!(arch, "sm_120" | "sm_121" | "compute_120" | "compute_121")
{
Some(load_sm120_half("_f32out")?)
} else {
None
},
matvec_bi_bf16_bf16: get("matvec_bi_bf16_bf16")?,
matvec_bi_f16_f16: get("matvec_bi_f16_f16")?,
matvec_bi_bf16_f32: get("matvec_bi_bf16_f32")?,
matvec_bi_f16_f32: get("matvec_bi_f16_f32")?,
matvec_bi_f32_f32: get("matvec_bi_f32_f32")?,
softplus_fwd_typed: load_typed("softplus_forward")?,
rmsnorm_fwd_resadd_typed: load_typed("rmsnorm_forward_resadd_f32in")?,
bias_broadcast_typed: load_typed("bias_broadcast")?,
elementwise_mul_typed: load_typed("elementwise_mul")?,
residual_add_typed: load_typed("residual_add")?,
gather_cols_typed: load_typed("gather_cols")?,
gather_bc_cols_typed: load_typed("gather_bc_cols")?,
gather_bc_cols_tmajor_typed: load_typed("gather_bc_cols_tmajor")?,
gather_bc_cols_tmajor_tiled_typed: load_typed("gather_bc_cols_tmajor_tiled")?,
gate_mul_silu_typed: load_typed("gate_mul_silu")?,
gate_mul_silu_v_typed: load_typed("gate_mul_silu_v")?,
elementwise_mul_v_typed: load_typed("elementwise_mul_v")?,
softplus_copy_typed: load_typed("softplus_copy")?,
ssm_step_fwd_fused_typed: load_typed("ssm_step_forward_fused")?,
conv1d_step_fwd_silu_typed: load_typed("conv1d_step_forward_silu")?,
ssm_burnin_nosave_typed: load_typed("ssm_burnin_forward_nosave")?,
softplus_bwd_typed: load_typed("softplus_backward")?,
gather_last_timestep_typed: load_typed("gather_last_timestep")?,
vec_cast_zplus_typed: load_typed("vec_cast_zplus")?,
concat_halves_typed: load_typed("concat_halves")?,
scatter_add_cols_typed: load_typed("scatter_add_cols")?,
reduce_bias_typed: load_typed("reduce_bias")?,
gating_bwd_typed: load_typed("gating_backward")?,
rmsnorm_bwd_typed: load_typed("rmsnorm_backward")?,
conv1d_burnin_bwd_typed: load_typed("conv1d_burnin_backward")?,
conv1d_burnin_fwd_tiled_typed: load_typed("conv1d_burnin_forward_tiled")?,
conv1d_bwd_tiled_typed: load_typed("conv1d_bwd_tiled")?,
ssm_backward_local_typed: load_typed("ssm_backward_local")?,
pack_xdbl_cols_typed: TypedKernel {
f32: get("pack_xdbl_cols_f32")?,
bf16: get("pack_xdbl_cols_bf16")?,
f16: get("pack_xdbl_cols_f16")?,
},
rmsnorm_fwd_f32in_typed: load_half("rmsnorm_forward_f32in")?,
rmsnorm_bwd_f32in_typed: load_half("rmsnorm_backward_f32in")?,
residual_add_f32_typed: load_half("residual_add_f32")?,
gemm_bi_nn_tc128_typed: load_half_dynsmem("nn_tc128", 71_680)?,
gemm_bi_nn_tc128_f32out: load_half_dynsmem("nn_tc128_f32out", 71_680)?,
gemm_bi_nn_tc64_f32out: load_half("nn_tc64_f32out")?,
gemm_bi_nn_tc16_f32out: load_half("nn_tc16_f32out")?,
gemm_bi_nn_tc64_typed: load_half("nn_tc64")?,
gemm_bi_nn_tc16_typed: load_half("nn_tc16")?,
gemm_bi_nn_tcw64_typed: load_half_dynsmem("nn_tcw64", 65_536)?,
gemm_bi_nn_tcwn64_typed: load_half_dynsmem("nn_tcwn64", 98_304)?,
gemm_bi_nn_sm90_typed: if arch == "sm_90a" {
Some(load_half_dynsmem("nn_sm90a_wgmma_wg1", 49_152)?)
} else {
None
},
gemm_bi_nn_sm100_typed: if matches!(arch, "sm_100a" | "sm_103a" | "sm_107a" | "sm_110a")
{
Some(load_half_dynsmem("nn_sm100_tcgen_c4", 65_536)?)
} else {
None
},
_modules: CudaModuleAnchors::new(vec![module]),
})
}
pub fn compiler_identity(&self) -> super::kernel_identity::CompilerIdentity {
self.compiler_identity
}
pub fn ssm_parallel_bwd_fold_for_shape(
&self,
dtype: WeightDtype,
batch: usize,
time: usize,
inner: usize,
state: usize,
) -> &CudaFunction {
let legacy = self.ssm_parallel_bwd_fold_typed.get(dtype);
match super::fold_transport::route_for_shape(dtype, batch, time, inner, state) {
super::fold_transport::FoldRoute::Short => self
.ssm_parallel_bwd_fold_short_typed
.as_ref()
.map_or(legacy, |kernel| kernel.get(dtype)),
super::fold_transport::FoldRoute::Staged => self
.ssm_parallel_bwd_fold_staged_typed
.as_ref()
.map_or(legacy, |kernel| kernel.get(dtype)),
super::fold_transport::FoldRoute::Legacy => legacy,
}
}
pub(crate) fn specialized_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad
.sm120_compiler_identity()
.or_else(|| self.triad.sm100_compiler_identity())
.or_else(|| self.triad.sm90a_compiler_identity())
}
pub fn artifact_set_identity(&self) -> super::kernel_identity::ArtifactSetIdentity {
self.triad.artifact_set_identity()
}
pub fn f32_triad_availability(&self) -> super::gemm_bi_triad::F32TriadAvailability {
self.triad.f32_triad_availability()
}
pub fn specialized_tf32_rejection(&self) -> Option<&str> {
self.triad.specialized_tf32_rejection()
}
pub fn finalist_tf32_rejection(&self) -> Option<&str> {
self.triad.finalist_tf32_rejection()
}
pub fn triad_scalar_compiler_identity(&self) -> super::kernel_identity::CompilerIdentity {
self.triad.scalar_compiler_identity()
}
pub(crate) fn triad_scalar_compute_capability(&self) -> (u32, u32) {
self.triad.compute_capability()
}
pub(crate) fn tc64_streamk_resident_ctas(&self) -> u32 {
self.triad.tc64_streamk_resident_ctas()
}
pub(crate) fn sm89_half_relay_resident_ctas(&self) -> u32 {
self.triad.sm89_half_relay_resident_ctas()
}
pub(crate) fn triad_sm80_compiler_identity(&self) -> super::kernel_identity::CompilerIdentity {
self.triad.sm80_compiler_identity()
}
pub(crate) fn triad_sm89_finalist_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad.sm89_finalist_compiler_identity()
}
pub fn triad_sm89_half_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad.sm89_half_compiler_identity()
}
pub fn triad_sm89_half_artifact_identity(
&self,
) -> Option<super::kernel_identity::ArtifactIdentity> {
self.triad.artifact_set_identity().sm89_half
}
pub fn triad_sm89_half_rejection(&self) -> Option<&str> {
self.triad.sm89_half_rejection()
}
pub fn triad_sm89_half_exclusions(&self) -> Vec<(&'static str, &str)> {
self.triad
.sm89_half_exclusions()
.iter()
.map(|excluded| (excluded.symbol, excluded.reason.as_str()))
.collect()
}
#[doc(hidden)]
pub fn triad_sm89_half_function(
&self,
route: super::gemm_bi_triad::Sm89HalfRoute,
dtype: super::dtype::WeightDtype,
) -> Option<&CudaFunction> {
self.triad.sm89_half_function(route, dtype)
}
pub(crate) fn fixed_sm89_cell_function(&self, symbol: &str) -> Option<&CudaFunction> {
self.fixed_sm89_cells.get(symbol)
}
pub(in crate::mamba_ssm::gpu) fn triad_sm89_half_runtime_function(
&self,
symbol: &str,
) -> Option<&CudaFunction> {
self.triad.sm89_half_runtime_function(symbol)
}
pub fn triad_sm89_exact_f32_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad.sm89_exact_f32_compiler_identity()
}
pub fn triad_sm89_exact_f32_artifact_identity(
&self,
) -> Option<super::kernel_identity::ArtifactIdentity> {
self.triad.artifact_set_identity().sm89_exact_f32
}
pub fn triad_sm89_exact_f32_rejection(&self) -> Option<&str> {
self.triad.sm89_exact_f32_rejection()
}
pub fn triad_sm89_exact_f32_exclusions(&self) -> Vec<(&'static str, &str)> {
self.triad
.sm89_exact_f32_exclusions()
.iter()
.map(|excluded| (excluded.symbol, excluded.reason.as_str()))
.collect()
}
#[doc(hidden)]
pub fn triad_sm89_exact_f32_function(&self, symbol: &str) -> Option<&CudaFunction> {
self.triad.sm89_exact_f32_function(symbol)
}
pub fn triad_sm89_exact_f32_d128_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad.sm89_exact_f32_d128_compiler_identity()
}
pub fn triad_sm89_exact_f32_d128_artifact_identity(
&self,
) -> Option<super::kernel_identity::ArtifactIdentity> {
self.triad.artifact_set_identity().sm89_exact_f32_d128
}
pub fn triad_sm89_exact_f32_d128_rejection(&self) -> Option<&str> {
self.triad.sm89_exact_f32_d128_rejection()
}
pub fn triad_sm89_exact_f32_d128_exclusions(&self) -> Vec<(&'static str, &str)> {
self.triad
.sm89_exact_f32_d128_exclusions()
.iter()
.map(|excluded| (excluded.symbol, excluded.reason.as_str()))
.collect()
}
#[doc(hidden)]
pub fn triad_sm89_exact_f32_d128_function(&self, symbol: &str) -> Option<&CudaFunction> {
self.triad.sm89_exact_f32_d128_function(symbol)
}
pub fn triad_sm89_tf32_joint_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad.sm89_tf32_joint_compiler_identity()
}
pub fn sm89_cells_compiler_identity(&self) -> Option<super::kernel_identity::CompilerIdentity> {
self.sm89_cells_compiler_identity
}
pub fn triad_sm89_tf32_joint_artifact_identity(
&self,
) -> Option<super::kernel_identity::ArtifactIdentity> {
self.triad.artifact_set_identity().sm89_tf32_joint
}
pub fn triad_sm89_tf32_joint_rejection(&self) -> Option<&str> {
self.triad.sm89_tf32_joint_rejection()
}
pub fn triad_sm89_tf32_joint_exclusions(&self) -> Vec<(&'static str, &str)> {
self.triad
.sm89_tf32_joint_exclusions()
.iter()
.map(|excluded| (excluded.symbol, excluded.reason.as_str()))
.collect()
}
#[doc(hidden)]
pub fn triad_sm89_tf32_joint_function(&self, symbol: &str) -> Option<&CudaFunction> {
self.triad.sm89_tf32_joint_function(symbol)
}
pub(crate) fn tf32_function(&self, symbol: &str) -> Option<&CudaFunction> {
self.triad.tf32_function(symbol)
}
pub(crate) fn triad_kernels(&self) -> &GemmBiKernels {
&self.triad
}
pub fn splitk_scratch_buf(
&self,
stream: &Arc<cudarc::driver::CudaStream>,
) -> Result<&cudarc::driver::CudaSlice<f32>, String> {
self.triad.splitk_scratch_buf(stream)
}
pub fn transpose_scratch_buf(
&self,
stream: &Arc<cudarc::driver::CudaStream>,
) -> Result<&cudarc::driver::CudaSlice<f32>, String> {
self.triad.transpose_scratch_buf(stream)
}
}
pub fn cuda_include_paths() -> Vec<String> {
let mut candidates: Vec<String> = Vec::new();
for var in ["CUDA_HOME", "CUDA_PATH", "CUDA_ROOT"] {
if let Ok(p) = std::env::var(var) {
candidates.push(format!("{p}/include"));
}
}
for std_path in [
"/usr/local/cuda/include",
"/usr/local/cuda-13.2/include",
"/usr/local/cuda-12.8/include",
"/usr/local/cuda-12.6/include",
"/usr/local/cuda-12.4/include",
"/usr/local/cuda-12.2/include",
"/opt/cuda/include",
] {
candidates.push(std_path.to_string());
}
candidates
.into_iter()
.filter(|p| std::path::Path::new(p).join("cuda_fp16.h").exists())
.collect()
}
#[cfg(test)]
mod module_plan_tests {
use super::super::device::GpuDevice;
use super::super::kernel_identity::ModuleKind;
use super::MambaKernels;
#[test]
fn every_sm80_board_loads_the_common_tier_beside_its_own_module() {
let expected = [
((8, 0), None),
((8, 6), None),
((8, 7), None),
((8, 9), None),
((9, 0), Some(ModuleKind::TriadSm90a)),
((10, 0), Some(ModuleKind::TriadSm100)),
((10, 3), Some(ModuleKind::TriadSm100)),
((10, 7), Some(ModuleKind::TriadSm100)),
((11, 0), Some(ModuleKind::TriadSm100)),
((12, 0), Some(ModuleKind::TriadSm120)),
((12, 1), Some(ModuleKind::TriadSm120)),
];
for (cc, architecture) in expected {
let arch = GpuDevice::resolve_nvrtc_target(cc).unwrap();
let device_cc = Some((cc.0 as i32, cc.1 as i32));
let plan = MambaKernels::triad_module_plan(arch, device_cc, (13, 4));
assert!(plan.common, "CC {cc:?} ({arch}) must load the common tier");
assert_eq!(plan.architecture, architecture, "CC {cc:?} ({arch})");
}
}
#[test]
fn the_wgmma_module_waits_for_the_fixed_ptxas() {
for nvrtc in [(13, 0), (13, 1), (13, 2)] {
let plan = MambaKernels::triad_module_plan("sm_90a", Some((9, 0)), nvrtc);
assert!(plan.common);
assert_eq!(plan.architecture, None, "NVRTC {nvrtc:?}");
}
let plan = MambaKernels::triad_module_plan("sm_90a", Some((9, 0)), (13, 3));
assert_eq!(plan.architecture, Some(ModuleKind::TriadSm90a));
}
#[test]
fn an_unknown_capability_loads_neither_tier() {
let plan = MambaKernels::triad_module_plan("sm_75", Some((7, 5)), (13, 2));
assert!(!plan.common);
assert_eq!(plan.architecture, None);
let plan = MambaKernels::triad_module_plan("sm_80", None, (13, 2));
assert!(!plan.common);
}
}