use super::scan::{eval_single_op_f32, run_scan_packed_f32};
use rlx_ir::{Graph, NodeId, Op, Shape};
#[derive(Clone, Debug)]
pub struct HostOpDesc {
pub op: Op,
pub out_byte_off: usize,
pub out_shape: Shape,
pub inputs: Vec<(usize, Shape)>,
}
pub fn host_op_desc_from_node(
graph: &Graph,
node: &rlx_ir::Node,
mut byte_off: impl FnMut(NodeId) -> usize,
) -> HostOpDesc {
HostOpDesc {
op: node.op.clone(),
out_byte_off: byte_off(node.id),
out_shape: node.shape.clone(),
inputs: node
.inputs
.iter()
.map(|&id| (byte_off(id), graph.node(id).shape.clone()))
.collect(),
}
}
pub unsafe fn execute_host_op_on_bytes(base: *mut u8, desc: &HostOpDesc) {
let staged: Vec<(Shape, Vec<f32>)> = desc
.inputs
.iter()
.map(|(off, sh)| {
let n = sh.num_elements().unwrap_or(0);
let mut v = vec![0f32; n];
unsafe {
let src = base.add(*off) as *const f32;
std::ptr::copy_nonoverlapping(src, v.as_mut_ptr(), n);
}
(sh.clone(), v)
})
.collect();
let refs: Vec<(Shape, &[f32])> = staged
.iter()
.map(|(sh, v)| (sh.clone(), v.as_slice()))
.collect();
let y = eval_single_op_f32(&desc.op, &desc.out_shape, &refs);
unsafe {
let dst = base.add(desc.out_byte_off) as *mut f32;
std::ptr::copy_nonoverlapping(y.as_ptr(), dst, y.len());
}
}
pub fn eval_host_op_on_f32_arena(host: &mut [f32], desc: &HostOpDesc) {
debug_assert!(desc.out_byte_off.is_multiple_of(4));
let staged: Vec<(Shape, Vec<f32>)> = desc
.inputs
.iter()
.map(|(off, sh)| {
debug_assert!(off.is_multiple_of(4));
let off_f32 = *off / 4;
let n = sh.num_elements().unwrap_or(0);
let end = (off_f32 + n).min(host.len());
(sh.clone(), host[off_f32..end].to_vec())
})
.collect();
let refs: Vec<(Shape, &[f32])> = staged
.iter()
.map(|(sh, v)| (sh.clone(), v.as_slice()))
.collect();
let y = eval_single_op_f32(&desc.op, &desc.out_shape, &refs);
let out_f32 = desc.out_byte_off / 4;
let end = (out_f32 + y.len()).min(host.len());
host[out_f32..end].copy_from_slice(&y[..(end - out_f32)]);
}
pub fn run_scan_node_f32(node: &rlx_ir::Node, mut get: impl FnMut(NodeId) -> Vec<f32>) -> Vec<f32> {
let (body, length, save_trajectory, num_bcast, num_xs) = match &node.op {
Op::Scan {
body,
length,
save_trajectory,
num_bcast,
num_xs,
..
} => (
body,
*length,
*save_trajectory,
*num_bcast as usize,
*num_xs as usize,
),
_ => panic!("run_scan_node_f32: expected Op::Scan"),
};
let init = get(node.inputs[0]);
let mut bcasts = Vec::with_capacity(num_bcast);
for i in 0..num_bcast {
bcasts.push(get(node.inputs[1 + i]));
}
let mut xs = Vec::with_capacity(num_xs);
for i in 0..num_xs {
xs.push(get(node.inputs[1 + num_bcast + i]));
}
let out_len = node.shape.num_elements().unwrap_or(0);
run_scan_packed_f32(
body,
length,
save_trajectory,
num_bcast,
num_xs,
&init,
&bcasts,
&xs,
out_len,
)
}
pub fn run_host_op_node_f32(
graph: &Graph,
node: &rlx_ir::Node,
mut get: impl FnMut(NodeId) -> Vec<f32>,
) -> Vec<f32> {
let staged: Vec<(Shape, Vec<f32>)> = node
.inputs
.iter()
.map(|&id| (graph.node(id).shape.clone(), get(id)))
.collect();
let refs: Vec<(Shape, &[f32])> = staged
.iter()
.map(|(sh, v)| (sh.clone(), v.as_slice()))
.collect();
eval_single_op_f32(&node.op, &node.shape, &refs)
}
#[macro_export]
macro_rules! rlx_host_op_desc {
($graph:expr, $node:expr, $byte_off:expr) => {
$crate::thunk::host_op_desc_from_node(&$graph, $node, $byte_off)
};
}
#[macro_export]
macro_rules! rlx_execute_host_op_on_bytes {
($base:expr, $desc:expr) => {
$crate::thunk::execute_host_op_on_bytes($base, $desc)
};
}
#[macro_export]
macro_rules! rlx_host_op_stage_d2h {
(
arena_size_bytes = $nbytes:expr,
desc = $desc:expr,
sync = $sync:block,
dtoh = |$host:ident| $dtoh:block,
htod = |$host_back:ident| $htod:block $(,)?
) => {
$crate::rlx_arena_stage_d2h! {
arena_size_bytes = $nbytes,
sync = $sync,
dtoh = |$host| $dtoh,
on_host = |host_mut| {
$crate::thunk::eval_host_op_on_f32_arena(host_mut, $desc);
},
htod = |$host_back| $htod,
}
};
}
#[derive(Clone)]
pub struct HostOpSpan {
pub lo: usize,
pub hi: usize,
pub desc: HostOpDesc,
}
impl HostOpSpan {
pub fn from_desc(desc: HostOpDesc) -> Self {
let out_n = desc.out_shape.num_elements().unwrap_or(0) * 4;
let mut lo = desc.out_byte_off;
let mut hi = desc.out_byte_off + out_n;
for (off, sh) in &desc.inputs {
let n = sh.num_elements().unwrap_or(0) * 4;
lo = lo.min(*off);
hi = hi.max(*off + n);
}
let rebated = HostOpDesc {
op: desc.op,
out_byte_off: desc.out_byte_off - lo,
out_shape: desc.out_shape,
inputs: desc
.inputs
.into_iter()
.map(|(o, sh)| (o - lo, sh))
.collect(),
};
Self {
lo,
hi,
desc: rebated,
}
}
pub fn len(&self) -> usize {
self.hi - self.lo
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}