1use super::scan::{eval_single_op_f32, run_scan_packed_f32};
9use rlx_ir::{Graph, NodeId, Op, Shape};
10
11#[derive(Clone, Debug)]
14pub struct HostOpDesc {
15 pub op: Op,
16 pub out_byte_off: usize,
17 pub out_shape: Shape,
18 pub inputs: Vec<(usize, Shape)>,
20}
21
22pub fn host_op_desc_from_node(
25 graph: &Graph,
26 node: &rlx_ir::Node,
27 mut byte_off: impl FnMut(NodeId) -> usize,
28) -> HostOpDesc {
29 HostOpDesc {
30 op: node.op.clone(),
31 out_byte_off: byte_off(node.id),
32 out_shape: node.shape.clone(),
33 inputs: node
34 .inputs
35 .iter()
36 .map(|&id| (byte_off(id), graph.node(id).shape.clone()))
37 .collect(),
38 }
39}
40
41pub unsafe fn execute_host_op_on_bytes(base: *mut u8, desc: &HostOpDesc) {
46 let staged: Vec<(Shape, Vec<f32>)> = desc
47 .inputs
48 .iter()
49 .map(|(off, sh)| {
50 let n = sh
51 .size_bytes()
52 .unwrap_or(0)
53 .div_ceil(4)
54 .max(sh.num_elements().unwrap_or(0));
55 let mut v = vec![0f32; n];
56 unsafe {
57 let src = base.add(*off) as *const f32;
58 std::ptr::copy_nonoverlapping(src, v.as_mut_ptr(), n);
59 }
60 (sh.clone(), v)
61 })
62 .collect();
63 let refs: Vec<(Shape, &[f32])> = staged
64 .iter()
65 .map(|(sh, v)| (sh.clone(), v.as_slice()))
66 .collect();
67 let y = eval_single_op_f32(&desc.op, &desc.out_shape, &refs);
68 unsafe {
69 let dst = base.add(desc.out_byte_off) as *mut f32;
70 std::ptr::copy_nonoverlapping(y.as_ptr(), dst, y.len());
71 }
72}
73
74pub fn eval_host_op_on_f32_arena(host: &mut [f32], desc: &HostOpDesc) {
77 debug_assert!(desc.out_byte_off.is_multiple_of(4));
78 let staged: Vec<(Shape, Vec<f32>)> = desc
79 .inputs
80 .iter()
81 .map(|(off, sh)| {
82 debug_assert!(off.is_multiple_of(4));
83 let off_f32 = *off / 4;
84 let n = sh
85 .size_bytes()
86 .unwrap_or(0)
87 .div_ceil(4)
88 .max(sh.num_elements().unwrap_or(0));
89 let end = (off_f32 + n).min(host.len());
90 (sh.clone(), host[off_f32..end].to_vec())
91 })
92 .collect();
93 let refs: Vec<(Shape, &[f32])> = staged
94 .iter()
95 .map(|(sh, v)| (sh.clone(), v.as_slice()))
96 .collect();
97 let y = eval_single_op_f32(&desc.op, &desc.out_shape, &refs);
98 let out_f32 = desc.out_byte_off / 4;
99 let end = (out_f32 + y.len()).min(host.len());
100 host[out_f32..end].copy_from_slice(&y[..(end - out_f32)]);
101}
102
103pub fn run_scan_node_f32(node: &rlx_ir::Node, mut get: impl FnMut(NodeId) -> Vec<f32>) -> Vec<f32> {
105 let (body, length, save_trajectory, num_bcast, num_xs) = match &node.op {
106 Op::Scan {
107 body,
108 length,
109 save_trajectory,
110 num_bcast,
111 num_xs,
112 ..
113 } => (
114 body,
115 *length,
116 *save_trajectory,
117 *num_bcast as usize,
118 *num_xs as usize,
119 ),
120 _ => panic!("run_scan_node_f32: expected Op::Scan"),
121 };
122 let init = get(node.inputs[0]);
123 let mut bcasts = Vec::with_capacity(num_bcast);
124 for i in 0..num_bcast {
125 bcasts.push(get(node.inputs[1 + i]));
126 }
127 let mut xs = Vec::with_capacity(num_xs);
128 for i in 0..num_xs {
129 xs.push(get(node.inputs[1 + num_bcast + i]));
130 }
131 let out_len = node.shape.num_elements().unwrap_or(0);
132 run_scan_packed_f32(
133 body,
134 length,
135 save_trajectory,
136 num_bcast,
137 num_xs,
138 &init,
139 &bcasts,
140 &xs,
141 out_len,
142 )
143}
144
145pub fn run_host_op_node_f32(
148 graph: &Graph,
149 node: &rlx_ir::Node,
150 mut get: impl FnMut(NodeId) -> Vec<f32>,
151) -> Vec<f32> {
152 let staged: Vec<(Shape, Vec<f32>)> = node
153 .inputs
154 .iter()
155 .map(|&id| (graph.node(id).shape.clone(), get(id)))
156 .collect();
157 let refs: Vec<(Shape, &[f32])> = staged
158 .iter()
159 .map(|(sh, v)| (sh.clone(), v.as_slice()))
160 .collect();
161 eval_single_op_f32(&node.op, &node.shape, &refs)
162}
163
164#[macro_export]
166macro_rules! rlx_host_op_desc {
167 ($graph:expr, $node:expr, $byte_off:expr) => {
168 $crate::thunk::host_op_desc_from_node(&$graph, $node, $byte_off)
169 };
170}
171
172#[macro_export]
174macro_rules! rlx_execute_host_op_on_bytes {
175 ($base:expr, $desc:expr) => {
176 $crate::thunk::execute_host_op_on_bytes($base, $desc)
177 };
178}
179
180#[macro_export]
185macro_rules! rlx_host_op_stage_d2h {
186 (
187 arena_size_bytes = $nbytes:expr,
188 desc = $desc:expr,
189 sync = $sync:block,
190 dtoh = |$host:ident| $dtoh:block,
191 htod = |$host_back:ident| $htod:block $(,)?
192 ) => {
193 $crate::rlx_arena_stage_d2h! {
194 arena_size_bytes = $nbytes,
195 sync = $sync,
196 dtoh = |$host| $dtoh,
197 on_host = |host_mut| {
198 $crate::thunk::eval_host_op_on_f32_arena(host_mut, $desc);
199 },
200 htod = |$host_back| $htod,
201 }
202 };
203}
204
205#[derive(Clone)]
208pub struct HostOpSpan {
209 pub lo: usize,
210 pub hi: usize,
211 pub desc: HostOpDesc,
212}
213
214impl HostOpSpan {
215 pub fn from_desc(desc: HostOpDesc) -> Self {
216 let out_n = desc.out_shape.num_elements().unwrap_or(0) * 4;
217 let mut lo = desc.out_byte_off;
218 let mut hi = desc.out_byte_off + out_n;
219 for (off, sh) in &desc.inputs {
220 let n = sh.num_elements().unwrap_or(0) * 4;
221 lo = lo.min(*off);
222 hi = hi.max(*off + n);
223 }
224 let rebated = HostOpDesc {
225 op: desc.op,
226 out_byte_off: desc.out_byte_off - lo,
227 out_shape: desc.out_shape,
228 inputs: desc
229 .inputs
230 .into_iter()
231 .map(|(o, sh)| (o - lo, sh))
232 .collect(),
233 };
234 Self {
235 lo,
236 hi,
237 desc: rebated,
238 }
239 }
240
241 pub fn len(&self) -> usize {
242 self.hi - self.lo
243 }
244
245 pub fn is_empty(&self) -> bool {
246 self.len() == 0
247 }
248}