Skip to main content

qualia_core_db/wgsl_forge/ir/
capabilities.rs

1use serde::{Deserialize, Serialize};
2
3use super::intrinsics::{Intrinsic, IntrinsicClass};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
6pub struct HardwareCapabilityMatrix {
7    /// **Reserved scaffolding (plan §1): not yet exercised by any emitter.** No IR
8    /// `ScalarType` variant emits native `f64`/`u64`; 64-bit values are carried as
9    /// portable paired-`u32` words (`ScalarType::U64Words`). This flag and the
10    /// [`LoweringContext::policy_64bit`] policy it drives exist so the native-64-bit
11    /// lowering can be wired the moment a kernel needs native 64-bit arithmetic — do
12    /// not assume any generated shader consults it today.
13    pub supports_f64: bool,
14    pub subgroup_size: Option<u32>,
15    /// Cooperative-matrix / Tensor-core matrix-multiply-accumulate support.
16    pub supports_coopmat: bool,
17    /// Ray-query / RT-core support (hardware ray-triangle intersection).
18    pub supports_rt_cores: bool,
19}
20
21/// Outcome of checking one [`Intrinsic`] against the local hardware (plan §6).
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum IntrinsicSupport {
24    /// The adapter executes the intrinsic natively.
25    Native,
26    /// No hardware path, but the Forge can lower it to a portable shared-memory
27    /// equivalent (e.g. a subgroup reduction → barrier-synchronised tree reduction).
28    LowerToSharedMemory,
29    /// No hardware and no safe lowering: schedules requiring it must be excluded
30    /// from the search on this adapter.
31    Exclude,
32}
33
34impl HardwareCapabilityMatrix {
35    /// Classifies how an intrinsic can be served on this hardware.
36    pub const fn intrinsic_support(&self, intrinsic: &Intrinsic) -> IntrinsicSupport {
37        match intrinsic.class() {
38            IntrinsicClass::Subgroup => {
39                if self.subgroup_size.is_some() {
40                    IntrinsicSupport::Native
41                } else {
42                    // Warp reductions/shuffles degrade to a shared-memory tree.
43                    IntrinsicSupport::LowerToSharedMemory
44                }
45            }
46            IntrinsicClass::CooperativeMatrix => {
47                if self.supports_coopmat {
48                    IntrinsicSupport::Native
49                } else {
50                    IntrinsicSupport::Exclude
51                }
52            }
53            IntrinsicClass::RayTracing => {
54                if self.supports_rt_cores {
55                    IntrinsicSupport::Native
56                } else {
57                    IntrinsicSupport::Exclude
58                }
59            }
60        }
61    }
62}
63
64/// How a 64-bit value would be lowered for a given adapter.
65///
66/// **Reserved scaffolding (plan §1): not yet exercised by any emitter.** Today every
67/// emitter takes the `PairedU32Emulation` shape implicitly via `ScalarType::U64Words`;
68/// the `Native` arm has no code path because no IR scalar requests native `f64`/`u64`.
69/// Kept so the native-64-bit policy can be selected once such a kernel exists.
70#[derive(Debug, Clone, PartialEq, Eq, Hash)]
71pub enum LoweringPolicy64Bit {
72    Native,
73    PairedU32Emulation,
74}
75
76use crate::wgsl_forge::Schedule;
77
78#[derive(Debug, Clone)]
79pub struct LoweringContext {
80    pub capabilities: HardwareCapabilityMatrix,
81    pub schedule: Schedule,
82}
83
84impl LoweringContext {
85    pub fn new(capabilities: HardwareCapabilityMatrix, schedule: Schedule) -> Self {
86        Self {
87            capabilities,
88            schedule,
89        }
90    }
91
92    /// Selects the 64-bit lowering policy for the current adapter.
93    ///
94    /// **Reserved scaffolding (plan §1): not yet exercised by any emitter.** No emitter
95    /// calls this — 64-bit data flows through `ScalarType::U64Words` (paired-`u32`)
96    /// unconditionally. It is retained so a future native-64-bit kernel can branch on
97    /// the adapter's `supports_f64` flag without re-introducing the policy from scratch.
98    pub fn policy_64bit(&self) -> LoweringPolicy64Bit {
99        if self.capabilities.supports_f64 {
100            LoweringPolicy64Bit::Native
101        } else {
102            LoweringPolicy64Bit::PairedU32Emulation
103        }
104    }
105
106    /// How the local hardware can serve `intrinsic` (native / lower / exclude).
107    pub const fn intrinsic_support(&self, intrinsic: &Intrinsic) -> IntrinsicSupport {
108        self.capabilities.intrinsic_support(intrinsic)
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115    use crate::wgsl_forge::ir::intrinsics::SubgroupReduceOp;
116
117    fn ray_query() -> Intrinsic {
118        Intrinsic::RayQuery {
119            acceleration_structure: "tlas".to_string(),
120            origin: "o".to_string(),
121            direction: "d".to_string(),
122            t_min: "tmin".to_string(),
123            t_max: "tmax".to_string(),
124            destination: "hit".to_string(),
125        }
126    }
127
128    #[test]
129    fn rt_intrinsic_excluded_without_rt_cores() {
130        let absent = HardwareCapabilityMatrix::default();
131        assert_eq!(
132            absent.intrinsic_support(&ray_query()),
133            IntrinsicSupport::Exclude
134        );
135
136        let present = HardwareCapabilityMatrix {
137            supports_rt_cores: true,
138            ..Default::default()
139        };
140        assert_eq!(
141            present.intrinsic_support(&ray_query()),
142            IntrinsicSupport::Native
143        );
144    }
145
146    #[test]
147    fn coopmat_excluded_but_subgroup_lowers() {
148        let caps = HardwareCapabilityMatrix::default();
149        assert_eq!(
150            caps.intrinsic_support(&Intrinsic::CoopMatMul {
151                m: 16,
152                n: 16,
153                k: 16
154            }),
155            IntrinsicSupport::Exclude
156        );
157        // No subgroup hardware → portable shared-memory lowering, not exclusion.
158        assert_eq!(
159            caps.intrinsic_support(&Intrinsic::SubgroupReduce {
160                op: SubgroupReduceOp::Add
161            }),
162            IntrinsicSupport::LowerToSharedMemory
163        );
164    }
165}