use crate::OpDefRegistration;
use crate::{Category, OpDef, Signature, TypedParam};
const OP_DMA_FROM_NVME: &str = "io.dma_from_nvme";
const OP_WRITE_BACK_TO_NVME: &str = "io.write_back_to_nvme";
const OP_ZEROCOPY_MAP: &str = "mem.zerocopy_map";
const OP_UNMAP: &str = "mem.unmap";
const SIG_DMA_FROM_NVME: Signature = Signature {
inputs: &[
TypedParam {
name: "fd",
ty: "i32",
},
TypedParam {
name: "offset",
ty: "u64",
},
TypedParam {
name: "length",
ty: "u64",
},
],
outputs: &[TypedParam {
name: "handle",
ty: "GpuBufferHandle",
}],
attrs: &[],
bytes_extraction: false,
};
const SIG_WRITE_BACK_TO_NVME: Signature = Signature {
inputs: &[
TypedParam {
name: "handle",
ty: "GpuBufferHandle",
},
TypedParam {
name: "fd",
ty: "i32",
},
TypedParam {
name: "offset",
ty: "u64",
},
],
outputs: &[],
attrs: &[],
bytes_extraction: false,
};
const SIG_ZEROCOPY_MAP: Signature = Signature {
inputs: &[TypedParam {
name: "fd",
ty: "i32",
}],
outputs: &[TypedParam {
name: "handle",
ty: "GpuBufferHandle",
}],
attrs: &[],
bytes_extraction: false,
};
const SIG_UNMAP: Signature = Signature {
inputs: &[TypedParam {
name: "handle",
ty: "GpuBufferHandle",
}],
outputs: &[],
attrs: &[],
bytes_extraction: false,
};
fn unsupported_io_cpu_ref(input: &[u8], output: &mut Vec<u8>) {
output.clear();
tracing::error!(
target: "vyre::io_cpu_ref",
input_len = input.len(),
"unsupported Category C io CPU reference dispatch. Category C io ops require \
a backend with zero-copy NVMe/GDS capability and have no portable CPU/reference \
lowering. Fix: select or register a backend that advertises the `io` dialect \
lowering, or reject the program during capability negotiation before invoking \
cpu_ref."
);
}
inventory::submit! {
OpDefRegistration::new(|| OpDef {
id: OP_DMA_FROM_NVME,
dialect: "io",
category: Category::Intrinsic,
signature: SIG_DMA_FROM_NVME,
lowerings: crate::LoweringTable::new(unsupported_io_cpu_ref),
laws: &[],
compose: None,
})
}
inventory::submit! {
OpDefRegistration::new(|| OpDef {
id: OP_WRITE_BACK_TO_NVME,
dialect: "io",
category: Category::Intrinsic,
signature: SIG_WRITE_BACK_TO_NVME,
lowerings: crate::LoweringTable::new(unsupported_io_cpu_ref),
laws: &[],
compose: None,
})
}
inventory::submit! {
OpDefRegistration::new(|| OpDef {
id: OP_ZEROCOPY_MAP,
dialect: "io",
category: Category::Intrinsic,
signature: SIG_ZEROCOPY_MAP,
lowerings: crate::LoweringTable::new(unsupported_io_cpu_ref),
laws: &[],
compose: None,
})
}
inventory::submit! {
OpDefRegistration::new(|| OpDef {
id: OP_UNMAP,
dialect: "io",
category: Category::Intrinsic,
signature: SIG_UNMAP,
lowerings: crate::LoweringTable::new(unsupported_io_cpu_ref),
laws: &[],
compose: None,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::registry::{DialectRegistry, Target};
#[test]
fn every_io_op_registers() -> Result<(), String> {
let reg = DialectRegistry::global();
for op in [
OP_DMA_FROM_NVME,
OP_WRITE_BACK_TO_NVME,
OP_ZEROCOPY_MAP,
OP_UNMAP,
] {
let id = reg.intern_op(op);
let def = reg
.lookup(id)
.ok_or_else(|| {
format!(
"Fix: op `{op}` must register via inventory::submit!(OpDefRegistration{{...}}); restore the registration in this dialect."
)
})?;
assert_eq!(def.id, op);
assert_eq!(def.category, Category::Intrinsic);
}
Ok(())
}
#[test]
fn io_ops_have_no_gpu_lowering() {
let reg = DialectRegistry::global();
for op in [
OP_DMA_FROM_NVME,
OP_WRITE_BACK_TO_NVME,
OP_ZEROCOPY_MAP,
OP_UNMAP,
] {
let id = reg.intern_op(op);
assert!(
reg.get_lowering(id, Target::PrimaryText).is_none(),
"{op} must not carry a primary-text lowering until a backend opts in"
);
}
}
#[test]
fn io_ops_cpu_ref_clears_output_without_panicking_if_called_directly() {
let reg = DialectRegistry::global();
for op in [
OP_DMA_FROM_NVME,
OP_WRITE_BACK_TO_NVME,
OP_ZEROCOPY_MAP,
OP_UNMAP,
] {
let id = reg.intern_op(op);
let def = reg.lookup(id).unwrap();
let mut out = vec![0xAA];
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
(def.lowerings.cpu_ref)(&[1, 2, 3], &mut out);
}))
.expect("Fix: Category C io cpu_ref must never panic inside reference dispatch; restore this invariant before continuing.");
assert!(
out.is_empty(),
"{op} cpu_ref must clear output before failing so callers cannot consume stale bytes"
);
}
}
#[test]
fn io_dialect_is_distinct_from_stdlib() {
let reg = DialectRegistry::global();
let id = reg.intern_op(OP_DMA_FROM_NVME);
let def = reg.lookup(id).unwrap();
assert_eq!(def.dialect, "io");
}
}