use std::collections::HashMap;
use std::path::Path;
use onnx_runtime_ir::{Node, Shape, TensorLayout};
use crate::error::Result;
use crate::kernel::{Kernel, KernelMatch};
use crate::provider::{EpConfig, EpId, ExecutionProvider};
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct OpKey {
pub op_type: String,
pub domain: String,
pub since_version: u64,
}
impl OpKey {
pub fn new(op_type: impl Into<String>, domain: impl Into<String>, since_version: u64) -> Self {
Self {
op_type: op_type.into(),
domain: domain.into(),
since_version,
}
}
}
fn norm_domain(domain: &str) -> &str {
if domain == "ai.onnx" { "" } else { domain }
}
pub trait KernelFactory: Send + Sync {
fn create(&self, node: &Node, input_shapes: &[Vec<usize>]) -> Result<Box<dyn Kernel>>;
}
#[derive(Default)]
pub struct OpRegistry {
entries: HashMap<OpKey, Box<dyn KernelFactory>>,
}
impl OpRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, key: OpKey, factory: Box<dyn KernelFactory>) {
self.entries.insert(key, factory);
}
pub fn lookup(&self, op_type: &str, domain: &str, opset: u64) -> Option<&dyn KernelFactory> {
let domain = norm_domain(domain);
self.entries
.iter()
.filter(|(k, _)| {
k.op_type == op_type && k.domain == domain && k.since_version <= opset
})
.max_by_key(|(k, _)| k.since_version)
.map(|(_, f)| f.as_ref())
}
pub fn supports(&self, op_type: &str, domain: &str) -> bool {
let domain = norm_domain(domain);
self.entries
.keys()
.any(|k| k.op_type == op_type && k.domain == domain)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[derive(Default)]
pub struct EpRegistry {
eps: Vec<Box<dyn ExecutionProvider>>,
priority: Vec<EpId>,
}
impl EpRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, ep: Box<dyn ExecutionProvider>) -> EpId {
let id = EpId(self.eps.len() as u32);
self.eps.push(ep);
self.priority.push(id);
id
}
pub fn load_legacy(&mut self, path: &Path, config: &EpConfig) -> Result<EpId> {
let _ = (path, config);
todo!("ort2-ep-api Phase 2: dlopen legacy ORT plugin EP and adapt its vtable")
}
pub fn set_priority(&mut self, order: Vec<EpId>) {
self.priority = order;
}
pub fn get(&self, id: EpId) -> Option<&dyn ExecutionProvider> {
self.eps.get(id.0 as usize).map(|b| b.as_ref())
}
pub fn priority(&self) -> &[EpId] {
&self.priority
}
pub fn candidates_for_op(
&self,
op: &Node,
shapes: &[Shape],
layouts: &[TensorLayout],
) -> Vec<(EpId, KernelMatch)> {
let mut out = Vec::new();
for &id in &self.priority {
if let Some(ep) = self.get(id) {
let m = ep.supports_op(op, shapes, layouts);
if m.is_supported() {
out.push((id, m));
}
}
}
out
}
}