Skip to main content

bb_ops/syscalls/structural/tee/
mod.rs

1//! `Tee` syscall component - fans a single input out to N outputs.
2//! `fanout` attribute (int) sets N (default 2).
3
4use bb_ir::proto::onnx::NodeProto;
5use bb_runtime::atomic::DispatchResult;
6use bb_runtime::bus::OpError;
7use bb_runtime::registry::OpRegistration;
8use bb_runtime::runtime::RuntimeResourceRef;
9use bb_runtime::slot_value::SlotValue;
10
11pub use bb_ir::syscall_ids::OP_TEE as OP_TYPE;
12pub use bb_ir::syscall_ids::SYSCALL_DOMAIN as DOMAIN;
13
14/// Engine dispatch-table marker.
15pub struct TeeOp;
16
17/// Invoke fn - duplicates the single input into `fanout` outputs
18/// via polymorphic `SlotValue::clone_boxed`. Each output preserves
19/// the concrete type.
20pub fn invoke(
21    node: &NodeProto,
22    inputs: &[(&str, &dyn SlotValue)],
23    _ctx: &mut RuntimeResourceRef<'_>,
24) -> Result<DispatchResult, OpError> {
25    let Some((_, input)) = inputs.first() else {
26        return Err(OpError {
27            detail: "Tee requires one input".to_string(),
28            ..Default::default()
29        });
30    };
31    let fanout = node
32        .attribute
33        .iter()
34        .find(|a| a.name == "fanout")
35        .map(|a| a.i.max(1) as usize)
36        .unwrap_or(2);
37
38    let mut outs: Vec<(String, Box<dyn SlotValue>)> = Vec::with_capacity(fanout);
39    for i in 0..fanout {
40        outs.push((format!("out_{i}"), input.clone_boxed()));
41    }
42    Ok(DispatchResult::Immediate(outs))
43}
44
45inventory::submit! {
46    OpRegistration {
47        domain: DOMAIN,
48        op_type: OP_TYPE,
49        invoke,
50        kind: bb_runtime::registry::RegistrationKind::Syscall,
51    }
52}
53