vyre_foundation/validate/
options.rs1use crate::ir::DataType;
2
3#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
9#[expect(
10 clippy::struct_excessive_bools,
11 reason = "backend capability snapshots are explicit feature bits; replacing them with enums would obscure capability checks and break the stable validation ABI"
12)]
13pub struct BackendCapabilities {
14 pub supports_subgroup_ops: bool,
17 pub supports_indirect_dispatch: bool,
19 pub supports_specialization_constants: bool,
21 pub supports_distributed_collectives: bool,
23 pub has_mul_high: bool,
25 pub has_dual_issue_fp32_int32: bool,
27 pub has_tensor_core_int: bool,
29 pub has_native_f16: bool,
31 pub has_warp_shuffle: bool,
33 pub has_shared_memory: bool,
35 pub has_transcendental_polynomial_emit: bool,
37 pub max_native_int_width: u32,
39}
40
41pub trait BackendValidationCapabilities {
43 fn backend_name(&self) -> &'static str;
45
46 fn supports_cast_target(&self, target: &DataType) -> bool;
49
50 #[inline]
52 fn supports_subgroup_ops(&self) -> bool {
53 false
54 }
55
56 #[inline]
58 fn supports_indirect_dispatch(&self) -> bool {
59 false
60 }
61
62 #[inline]
64 fn supports_specialization_constants(&self) -> bool {
65 false
66 }
67
68 #[inline]
70 fn supports_distributed_collectives(&self) -> bool {
71 false
72 }
73
74 #[must_use]
76 #[inline]
77 fn backend_capabilities(&self) -> BackendCapabilities {
78 BackendCapabilities {
79 supports_subgroup_ops: self.supports_subgroup_ops(),
80 supports_indirect_dispatch: self.supports_indirect_dispatch(),
81 supports_specialization_constants: self.supports_specialization_constants(),
82 supports_distributed_collectives: self.supports_distributed_collectives(),
83 ..BackendCapabilities::default()
84 }
85 }
86}
87
88#[derive(Clone, Copy, Default)]
94pub struct ValidationOptions<'a> {
95 pub backend: Option<&'a dyn BackendValidationCapabilities>,
97 pub backend_capabilities: Option<BackendCapabilities>,
99 pub allow_shadowing: bool,
101}
102
103impl<'a> ValidationOptions<'a> {
104 #[must_use]
106 #[inline]
107 pub fn universal() -> Self {
108 Self::default()
109 }
110
111 #[must_use]
113 #[inline]
114 pub fn with_backend(mut self, backend: &'a dyn BackendValidationCapabilities) -> Self {
115 self.backend = Some(backend);
116 self.backend_capabilities = Some(backend.backend_capabilities());
117 self
118 }
119
120 #[must_use]
122 #[inline]
123 pub fn with_backend_capabilities(mut self, backend_capabilities: BackendCapabilities) -> Self {
124 self.backend_capabilities = Some(backend_capabilities);
125 self
126 }
127
128 #[must_use]
130 #[inline]
131 pub fn with_shadowing(mut self, allow_shadowing: bool) -> Self {
132 self.allow_shadowing = allow_shadowing;
133 self
134 }
135
136 #[must_use]
138 #[inline]
139 pub fn backend_name(&self) -> &'static str {
140 self.backend.map_or(
141 "best-effort universal",
142 BackendValidationCapabilities::backend_name,
143 )
144 }
145
146 #[must_use]
148 #[inline]
149 pub fn supports_cast_target(&self, target: &DataType) -> bool {
150 self.backend
151 .is_none_or(|backend| backend.supports_cast_target(target))
152 }
153
154 #[must_use]
156 #[inline]
157 pub fn requires_subgroup_ops(&self) -> bool {
158 self.backend_capabilities
159 .is_some_and(|caps| caps.supports_subgroup_ops)
160 }
161
162 #[must_use]
164 #[inline]
165 pub fn supports_distributed_collectives(&self) -> bool {
166 self.backend_capabilities
167 .is_some_and(|caps| caps.supports_distributed_collectives)
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 struct CapabilityFixtureBackend;
176 impl BackendValidationCapabilities for CapabilityFixtureBackend {
177 fn backend_name(&self) -> &'static str {
178 "capability-fixture-gpu"
179 }
180 fn supports_cast_target(&self, target: &DataType) -> bool {
181 matches!(target, DataType::U32 | DataType::F32)
182 }
183 fn supports_subgroup_ops(&self) -> bool {
184 true
185 }
186 }
187
188 #[test]
189 fn universal_defaults() {
190 let opts = ValidationOptions::universal();
191 assert!(opts.backend.is_none());
192 assert!(!opts.allow_shadowing);
193 assert_eq!(opts.backend_name(), "best-effort universal");
194 }
195
196 #[test]
197 fn with_backend_sets_name_and_caps() {
198 let backend = CapabilityFixtureBackend;
199 let opts = ValidationOptions::universal().with_backend(&backend);
200 assert_eq!(opts.backend_name(), "capability-fixture-gpu");
201 assert!(opts.requires_subgroup_ops());
202 }
203
204 #[test]
205 fn supports_cast_target_delegates_to_backend() {
206 let backend = CapabilityFixtureBackend;
207 let opts = ValidationOptions::universal().with_backend(&backend);
208 assert!(opts.supports_cast_target(&DataType::U32));
209 assert!(!opts.supports_cast_target(&DataType::Bool));
210 }
211
212 #[test]
213 fn supports_cast_target_defaults_true_without_backend() {
214 let opts = ValidationOptions::universal();
215 assert!(opts.supports_cast_target(&DataType::Bool));
216 }
217
218 #[test]
219 fn with_shadowing_toggle() {
220 let opts = ValidationOptions::universal().with_shadowing(true);
221 assert!(opts.allow_shadowing);
222 }
223
224 #[test]
225 fn backend_capabilities_default() {
226 let caps = BackendCapabilities::default();
227 assert!(!caps.supports_subgroup_ops);
228 assert!(!caps.supports_indirect_dispatch);
229 assert!(!caps.supports_specialization_constants);
230 assert!(!caps.supports_distributed_collectives);
231 }
232
233 #[test]
234 fn with_backend_capabilities_snapshot() {
235 let caps = BackendCapabilities {
236 supports_subgroup_ops: true,
237 supports_indirect_dispatch: false,
238 supports_specialization_constants: false,
239 ..BackendCapabilities::default()
240 };
241 let opts = ValidationOptions::universal().with_backend_capabilities(caps);
242 assert!(opts.requires_subgroup_ops());
243 }
244}