Skip to main content

vyre_driver/backend/registry/
inventory_streams.rs

1//! Inventory streams contributed by linked backend crates.
2
3use std::collections::HashSet;
4use std::sync::{Arc, LazyLock};
5
6use vyre_foundation::ir::OpId;
7use vyre_foundation::operation::{TargetId, TargetOperationFacet};
8
9use super::grid_sync_split::wrap_grid_sync_split;
10use crate::backend::{ArtifactMaterializer, BackendError, VyreBackend};
11use vyre_megakernel::TargetCompiler;
12
13struct RegisteredOperationSupport {
14    id: &'static str,
15    operations: &'static HashSet<OpId>,
16}
17
18impl crate::backend::Backend for RegisteredOperationSupport {
19    fn id(&self) -> &'static str {
20        self.id
21    }
22
23    fn version(&self) -> &'static str {
24        "registered-target-compiler"
25    }
26
27    fn supported_ops(&self) -> &HashSet<OpId> {
28        self.operations
29    }
30}
31
32/// One backend constructor contributed by a linked backend crate.
33///
34/// Backend construction can fail (missing GPU adapter, unsupported driver),
35/// so the factory returns a [`BackendError`] rather than panicking. Callers
36/// iterate [`registered_backends`] and skip backends whose factory fails on
37/// this host.
38#[derive(Clone)]
39pub struct BackendRegistration {
40    /// Stable backend identifier, matching [`VyreBackend::id`].
41    pub id: &'static str,
42    /// Validated target identity owned by the concrete backend crate.
43    pub target_id: TargetId,
44    /// Stable target-payload format identity owned by the concrete backend.
45    ///
46    /// This is `None` only when the registration has no target compiler.
47    pub payload_format: Option<&'static str>,
48    /// Whether this backend is an explicit conformance oracle rather than an
49    /// eligible production autoroute target.
50    pub reference_oracle: bool,
51    /// Factory that constructs the backend implementation.
52    ///
53    /// Returns `Err(BackendError)` when the backend cannot initialize on
54    /// this host. The error message must include a `Fix:` remediation section
55    /// per the frozen `BackendError` contract.
56    pub factory: fn() -> Result<Box<dyn VyreBackend>, BackendError>,
57    /// Language-level IR operation IDs accepted by raw backend dispatch.
58    pub supported_ops: fn() -> &'static HashSet<OpId>,
59    /// Canonical semantic operation IDs supported by the target compiler.
60    ///
61    /// This owner-local projection is the target facet submission. The shared
62    /// driver joins it with `OperationRegistry` and never infers semantic
63    /// support from language-level node capability.
64    pub semantic_operations: fn() -> &'static HashSet<OpId>,
65    /// Pure compiler facet for this backend's immutable target payload.
66    pub target_compiler: Option<fn() -> Result<Box<dyn TargetCompiler>, BackendError>>,
67    /// Device acquisition and immutable payload materialization facet.
68    pub materializer: Option<fn() -> Result<Box<dyn ArtifactMaterializer>, BackendError>>,
69}
70
71impl BackendRegistration {
72    /// Construct this registered backend through the shared driver boundary.
73    ///
74    /// This preserves the raw factory ABI while ensuring registration-based
75    /// callers receive the same dispatch wrapper as [`crate::backend::acquire`]
76    /// and [`crate::backend::acquire_preferred_dispatch_backend`].
77    ///
78    /// # Errors
79    ///
80    /// Returns the backend factory error when the concrete backend cannot
81    /// initialize on this host.
82    pub fn acquire(&self) -> Result<Box<dyn VyreBackend>, BackendError> {
83        (self.factory)().map(wrap_grid_sync_split)
84    }
85
86    /// Acquire this backend's pure target compiler facet.
87    ///
88    /// # Errors
89    ///
90    /// Returns an explicit unsupported-feature error when the linked backend
91    /// does not provide native target compilation, or a contract error when
92    /// the constructed compiler disagrees with its registered payload format.
93    pub fn target_compiler(&self) -> Result<Box<dyn TargetCompiler>, BackendError> {
94        let factory = self
95            .target_compiler
96            .ok_or_else(|| BackendError::UnsupportedFeature {
97                name: "registered target compiler; Fix: link a backend crate that registers native artifact compilation instead of passing a raw Program".to_string(),
98                backend: self.id.to_string(),
99            })?;
100        let expected = self.payload_format.ok_or_else(|| {
101            BackendError::new(format!(
102                "backend `{}` registers a target compiler without a payload format. Fix: declare the concrete owner-local payload format in BackendRegistration.",
103                self.id
104            ))
105        })?;
106        let compiler = factory()?;
107        if compiler.format().identity() != expected {
108            return Err(BackendError::new(format!(
109                "backend `{}` registers payload format `{expected}` but constructed compiler format `{}`. Fix: keep the concrete target registration and compiler format identical.",
110                self.id,
111                compiler.format().identity()
112            )));
113        }
114        Ok(compiler)
115    }
116
117    /// Acquire this backend's device materializer facet.
118    ///
119    /// # Errors
120    ///
121    /// Returns an explicit unsupported-feature error when no native
122    /// materializer is registered, or the concrete device acquisition error.
123    pub fn materializer(&self) -> Result<Box<dyn ArtifactMaterializer>, BackendError> {
124        self.materializer
125            .ok_or_else(|| BackendError::UnsupportedFeature {
126                name: "registered artifact materializer; Fix: link the backend's native materializer instead of recompiling a raw Program at dispatch".to_string(),
127                backend: self.id.to_string(),
128            })?()
129    }
130}
131
132inventory::collect!(BackendRegistration);
133
134/// Return target compiler facets keyed by canonical semantic operation identity.
135///
136/// A compiler-capable backend contributes a facet when the canonical neutral
137/// program contains only operation IDs advertised by that backend.
138///
139/// # Errors
140///
141/// Returns [`BackendError`] when backend registry startup fails or a concrete
142/// target advertises an unknown semantic operation.
143pub fn registered_target_operation_facets() -> Result<&'static [TargetOperationFacet], BackendError>
144{
145    static FACETS: LazyLock<Result<Arc<[TargetOperationFacet]>, BackendError>> = LazyLock::new(
146        || {
147            let backends = registered_backends()?;
148            let mut facets = Vec::new();
149            let facet_count = backends.iter().fold(0usize, |count, backend| {
150                count.saturating_add((backend.semantic_operations)().len())
151            });
152            crate::allocation::reserve_vec_to_capacity(
153                &mut facets,
154                facet_count,
155                "Vyre target facet registry",
156                "target operation facet",
157                "reduce linked target operation declarations",
158            )?;
159            for backend in backends
160                .iter()
161                .filter(|backend| backend.target_compiler.is_some())
162            {
163                for operation_id in (backend.semantic_operations)() {
164                    let operation =
165                        vyre_foundation::operation::OperationRegistry::global()
166                            .get(operation_id)
167                            .ok_or_else(|| {
168                                BackendError::new(format!(
169                                    "target `{}` advertises unknown semantic operation `{operation_id}`. Fix: submit one canonical OperationRegistration or remove the stale target facet.",
170                                    backend.target_id
171                                ))
172                            })?;
173                    if operation.program().is_some() {
174                        facets.push(TargetOperationFacet {
175                            operation_id: operation.id,
176                            target_id: backend.target_id.clone(),
177                            version: 1,
178                        });
179                    }
180                }
181            }
182            facets.sort_unstable_by(|left, right| {
183                (left.operation_id, &left.target_id).cmp(&(right.operation_id, &right.target_id))
184            });
185            for pair in facets.windows(2) {
186                if pair[0].operation_id == pair[1].operation_id
187                    && pair[0].target_id == pair[1].target_id
188                {
189                    return Err(BackendError::new(format!(
190                        "duplicate target facet for operation `{}` and target `{}`. Fix: keep one concrete-driver semantic operation declaration per target.",
191                        pair[0].operation_id, pair[0].target_id
192                    )));
193                }
194            }
195            Ok(Arc::from(facets))
196        },
197    );
198    match &*FACETS {
199        Ok(facets) => Ok(facets.as_ref()),
200        Err(error) => Err(error.clone()),
201    }
202}
203
204/// Per-backend precedence rank registered alongside its
205/// [`BackendRegistration`]. Lower rank wins in router selection.
206///
207/// Conventional ranks are backend-owned. A backend that does not submit a
208/// `BackendPrecedence` entry is treated as `u32::MAX`.
209pub struct BackendPrecedence {
210    /// Backend identifier; must match the corresponding
211    /// [`BackendRegistration::id`].
212    pub id: &'static str,
213    /// Sort key. Lower means higher priority.
214    pub rank: u32,
215}
216
217inventory::collect!(BackendPrecedence);
218
219/// Backend capability declaration: whether a backend owns a live dispatch
220/// stack on this host.
221pub struct BackendCapability {
222    /// Backend identifier; must match the corresponding
223    /// [`BackendRegistration::id`].
224    pub id: &'static str,
225    /// `true` when this backend's `dispatch` can execute a Program and return
226    /// real outputs; `false` when the backend is emission-only.
227    pub dispatches: bool,
228}
229
230inventory::collect!(BackendCapability);
231
232/// Immutable validated view over linked backend registrations and metadata.
233struct BackendRegistry {
234    registrations: Arc<[BackendRegistration]>,
235    capabilities: Arc<[(&'static str, bool)]>,
236    precedence: Arc<[(&'static str, u32)]>,
237}
238
239impl BackendRegistry {
240    fn build() -> Result<Self, BackendError> {
241        let registration_count = inventory::iter::<BackendRegistration>.into_iter().count();
242        let mut registrations = Vec::new();
243        crate::allocation::reserve_vec_to_capacity(
244            &mut registrations,
245            registration_count,
246            "Vyre backend registry",
247            "backend registration",
248            "reduce linked backend inventory",
249        )?;
250        registrations.extend(inventory::iter::<BackendRegistration>.into_iter().cloned());
251        registrations.sort_unstable_by(|left, right| left.id.cmp(right.id));
252        for registration in &registrations {
253            validate_registration(registration)?;
254        }
255        for pair in registrations.windows(2) {
256            if pair[0].id == pair[1].id {
257                return Err(BackendError::new(format!(
258                    "duplicate backend registration `{}`. Fix: keep one concrete provider for each backend id.",
259                    pair[0].id
260                )));
261            }
262        }
263
264        let mut targets = Vec::new();
265        crate::allocation::reserve_vec_to_capacity(
266            &mut targets,
267            registrations.len(),
268            "Vyre backend registry",
269            "target identity",
270            "reduce linked backend inventory",
271        )?;
272        targets.extend(
273            registrations
274                .iter()
275                .map(|registration| (registration.target_id.as_str(), registration.id)),
276        );
277        targets.sort_unstable();
278        for pair in targets.windows(2) {
279            if pair[0].0 == pair[1].0 {
280                return Err(BackendError::new(format!(
281                    "target `{}` is claimed by backend providers `{}` and `{}`. Fix: keep one concrete provider for each target identity.",
282                    pair[0].0, pair[0].1, pair[1].1
283                )));
284            }
285        }
286
287        let capabilities = freeze_capabilities(&registrations)?;
288        let precedence = freeze_precedence(&registrations)?;
289        Ok(Self {
290            registrations: Arc::from(registrations),
291            capabilities,
292            precedence,
293        })
294    }
295
296    fn registration(&self, id: &str) -> Option<&BackendRegistration> {
297        self.registrations
298            .binary_search_by_key(&id, |registration| registration.id)
299            .ok()
300            .map(|index| &self.registrations[index])
301    }
302
303    fn dispatches(&self, id: &str) -> bool {
304        self.capabilities
305            .binary_search_by_key(&id, |(backend_id, _)| *backend_id)
306            .ok()
307            .is_some_and(|index| self.capabilities[index].1)
308    }
309
310    fn precedence(&self, id: &str) -> u32 {
311        self.precedence
312            .binary_search_by_key(&id, |(backend_id, _)| *backend_id)
313            .ok()
314            .map_or(u32::MAX, |index| self.precedence[index].1)
315    }
316}
317
318fn validate_registration(registration: &BackendRegistration) -> Result<(), BackendError> {
319    validate_registry_identity("backend", registration.id)?;
320    if let Some(format) = registration.payload_format {
321        validate_registry_identity("target payload format", format)?;
322    }
323    if registration.target_compiler.is_some() != registration.payload_format.is_some() {
324        return Err(BackendError::new(format!(
325            "backend `{}` must register its target compiler and payload format together. Fix: provide both target fields or leave both absent.",
326            registration.id
327        )));
328    }
329    Ok(())
330}
331
332fn validate_registry_identity(kind: &str, identity: &str) -> Result<(), BackendError> {
333    if identity.is_empty() || identity.trim() != identity {
334        return Err(BackendError::new(format!(
335            "{kind} identity `{identity}` is empty or whitespace-padded. Fix: declare a stable non-empty identity without surrounding whitespace."
336        )));
337    }
338    Ok(())
339}
340
341fn freeze_capabilities(
342    registrations: &[BackendRegistration],
343) -> Result<Arc<[(&'static str, bool)]>, BackendError> {
344    let count = inventory::iter::<BackendCapability>.into_iter().count();
345    let mut entries = Vec::new();
346    crate::allocation::reserve_vec_to_capacity(
347        &mut entries,
348        count,
349        "Vyre backend registry",
350        "dispatch capability",
351        "reduce linked backend capability declarations",
352    )?;
353    entries.extend(
354        inventory::iter::<BackendCapability>
355            .into_iter()
356            .map(|entry| (entry.id, entry.dispatches)),
357    );
358    entries.sort_unstable_by_key(|entry| entry.0);
359    validate_metadata_ids(registrations, &entries, "dispatch capability")?;
360    Ok(Arc::from(entries))
361}
362
363fn freeze_precedence(
364    registrations: &[BackendRegistration],
365) -> Result<Arc<[(&'static str, u32)]>, BackendError> {
366    let count = inventory::iter::<BackendPrecedence>.into_iter().count();
367    let mut entries = Vec::new();
368    crate::allocation::reserve_vec_to_capacity(
369        &mut entries,
370        count,
371        "Vyre backend registry",
372        "backend precedence",
373        "reduce linked backend precedence declarations",
374    )?;
375    entries.extend(
376        inventory::iter::<BackendPrecedence>
377            .into_iter()
378            .map(|entry| (entry.id, entry.rank)),
379    );
380    entries.sort_unstable_by_key(|entry| entry.0);
381    validate_metadata_ids(registrations, &entries, "backend precedence")?;
382    Ok(Arc::from(entries))
383}
384
385fn validate_metadata_ids<T>(
386    registrations: &[BackendRegistration],
387    entries: &[(&'static str, T)],
388    kind: &str,
389) -> Result<(), BackendError> {
390    for entry in entries {
391        validate_registry_identity(kind, entry.0)?;
392        if registrations
393            .binary_search_by_key(&entry.0, |registration| registration.id)
394            .is_err()
395        {
396            return Err(BackendError::new(format!(
397                "{kind} metadata names unregistered backend `{}`. Fix: submit one BackendRegistration with the same id or delete the orphaned metadata.",
398                entry.0
399            )));
400        }
401    }
402    for pair in entries.windows(2) {
403        if pair[0].0 == pair[1].0 {
404            return Err(BackendError::new(format!(
405                "duplicate {kind} metadata for backend `{}`. Fix: keep one owner-local metadata submission per backend.",
406                pair[0].0
407            )));
408        }
409    }
410    Ok(())
411}
412
413fn backend_registry() -> Result<&'static BackendRegistry, BackendError> {
414    static REGISTRY: LazyLock<Result<BackendRegistry, BackendError>> =
415        LazyLock::new(BackendRegistry::build);
416    match &*REGISTRY {
417        Ok(registry) => Ok(registry),
418        Err(error) => Err(error.clone()),
419    }
420}
421
422/// Return all backend registrations linked into the current binary.
423///
424/// Registrations are sorted by stable backend identity. The first call freezes
425/// one owned registry; subsequent calls return the same immutable slice.
426///
427/// # Errors
428///
429/// Returns [`BackendError`] when provider identities conflict, metadata is
430/// orphaned or duplicated, a compiler/format pair is incomplete, or registry
431/// allocation fails.
432pub fn registered_backends() -> Result<&'static [BackendRegistration], BackendError> {
433    Ok(backend_registry()?.registrations.as_ref())
434}
435
436pub(super) fn registered_backend(
437    id: &str,
438) -> Result<Option<&'static BackendRegistration>, BackendError> {
439    Ok(backend_registry()?.registration(id))
440}
441
442pub(super) fn registered_backend_dispatches(id: &str) -> Result<bool, BackendError> {
443    Ok(backend_registry()?.dispatches(id))
444}
445
446pub(super) fn registered_backend_precedence(id: &str) -> Result<u32, BackendError> {
447    Ok(backend_registry()?.precedence(id))
448}