1use crate::thunk::{ScanBodyPlan, compile_scan_body, execute_scan_host};
26use rlx_ir::{Graph, NodeId, Op};
27use std::sync::Arc;
28
29#[derive(Clone)]
32pub struct ScanHostDesc {
33 pub plan: Arc<ScanBodyPlan>,
34 pub outer_init_off: usize,
35 pub outer_final_off: usize,
36 pub length: u32,
37 pub save_trajectory: bool,
38 pub xs_outer: Vec<(usize, usize)>,
40 pub bcast_outer: Vec<(usize, usize)>,
42}
43
44impl std::fmt::Debug for ScanHostDesc {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("ScanHostDesc")
47 .field("carry_bytes", &self.plan.carry_bytes)
48 .field("length", &self.length)
49 .field("save_trajectory", &self.save_trajectory)
50 .field("num_xs", &self.xs_outer.len())
51 .field("num_bcast", &self.bcast_outer.len())
52 .finish()
53 }
54}
55
56pub fn scan_host_desc_from_node(
59 graph: &Graph,
60 node: &rlx_ir::Node,
61 mut byte_off: impl FnMut(NodeId) -> usize,
62) -> ScanHostDesc {
63 let (body, length, save_trajectory, num_bcast, num_xs) = match &node.op {
64 Op::Scan {
65 body,
66 length,
67 save_trajectory,
68 num_bcast,
69 num_xs,
70 ..
71 } => (
72 body,
73 *length,
74 *save_trajectory,
75 *num_bcast as usize,
76 *num_xs as usize,
77 ),
78 _ => panic!("scan_host_desc_from_node: expected Op::Scan"),
79 };
80 let plan = compile_scan_body(body, num_bcast, num_xs);
81 let bcast_outer: Vec<(usize, usize)> = (0..num_bcast)
82 .map(|i| {
83 let id = node.inputs[1 + i];
84 (byte_off(id), graph.node(id).shape.size_bytes().unwrap())
85 })
86 .collect();
87 let xs_outer: Vec<(usize, usize)> = (0..num_xs)
88 .map(|i| {
89 let id = node.inputs[1 + num_bcast + i];
90 let total = graph.node(id).shape.size_bytes().unwrap();
91 (byte_off(id), total / length as usize)
92 })
93 .collect();
94 ScanHostDesc {
95 plan: Arc::new(plan),
96 outer_init_off: byte_off(node.inputs[0]),
97 outer_final_off: byte_off(node.id),
98 length,
99 save_trajectory,
100 xs_outer,
101 bcast_outer,
102 }
103}
104
105pub unsafe fn execute_scan_host_desc(base: *mut u8, desc: &ScanHostDesc) {
110 unsafe {
111 execute_scan_host(
112 base,
113 &desc.plan,
114 desc.outer_init_off,
115 desc.outer_final_off,
116 desc.length,
117 desc.save_trajectory,
118 &desc.xs_outer,
119 &desc.bcast_outer,
120 );
121 }
122}
123
124#[derive(Clone)]
127pub struct ScanHostSpan {
128 pub lo: usize,
129 pub hi: usize,
130 pub desc: ScanHostDesc,
131}
132
133impl ScanHostSpan {
134 pub fn from_desc(desc: ScanHostDesc) -> Self {
135 let cb = desc.plan.carry_bytes;
136 let out_len = if desc.save_trajectory {
137 desc.length as usize * cb
138 } else {
139 cb
140 };
141 let mut lo = desc.outer_init_off.min(desc.outer_final_off);
142 let mut hi = (desc.outer_init_off + cb).max(desc.outer_final_off + out_len);
143 for &(o, t) in &desc.bcast_outer {
144 lo = lo.min(o);
145 hi = hi.max(o + t);
146 }
147 for &(o, ps) in &desc.xs_outer {
148 lo = lo.min(o);
149 hi = hi.max(o + desc.length as usize * ps);
150 }
151 let rebated = ScanHostDesc {
152 plan: desc.plan,
153 outer_init_off: desc.outer_init_off - lo,
154 outer_final_off: desc.outer_final_off - lo,
155 length: desc.length,
156 save_trajectory: desc.save_trajectory,
157 xs_outer: desc.xs_outer.iter().map(|&(o, ps)| (o - lo, ps)).collect(),
158 bcast_outer: desc.bcast_outer.iter().map(|&(o, t)| (o - lo, t)).collect(),
159 };
160 Self {
161 lo,
162 hi,
163 desc: rebated,
164 }
165 }
166
167 pub fn len(&self) -> usize {
168 self.hi - self.lo
169 }
170
171 pub fn is_empty(&self) -> bool {
172 self.len() == 0
173 }
174}
175
176pub fn eval_single_op_f32(
180 op: &Op,
181 out_shape: &rlx_ir::Shape,
182 inputs: &[(rlx_ir::Shape, &[f32])],
183) -> Vec<f32> {
184 let mut g = Graph::new("scan_eval_single_op");
185 let ids: Vec<NodeId> = inputs
186 .iter()
187 .enumerate()
188 .map(|(i, (sh, _))| {
189 g.append_node(
190 Op::Input {
191 name: format!("in{i}"),
192 },
193 vec![],
194 sh.clone(),
195 None,
196 )
197 })
198 .collect();
199 let out = g.append_node(op.clone(), ids.clone(), out_shape.clone(), None);
200 g.set_outputs(vec![out]);
201 let plan = rlx_opt::memory::plan_memory_aligned(&g, 16);
202 let mut arena = crate::arena::Arena::from_plan(plan);
203 for (i, (_, vals)) in inputs.iter().enumerate() {
204 let slot = arena.slice_mut(ids[i]);
205 let n = slot.len().min(vals.len());
206 slot[..n].copy_from_slice(&vals[..n]);
207 }
208 let schedule = crate::thunk::compile_thunks(&g, &arena);
209 crate::thunk::execute_thunks(&schedule, arena.raw_buf_mut());
210 let n = out_shape.num_elements().unwrap_or(0);
211 arena.slice_mut(out)[..n].to_vec()
212}
213
214pub fn maybe_unroll_scans(graph: Graph, max_length: u32) -> Graph {
216 rlx_opt::control_flow::maybe_unroll_scans(graph, max_length)
217}
218
219#[allow(clippy::too_many_arguments)]
223pub fn run_scan_packed_f32(
224 body: &Graph,
225 length: u32,
226 save_trajectory: bool,
227 num_bcast: usize,
228 num_xs: usize,
229 init: &[f32],
230 bcasts: &[Vec<f32>],
231 xs: &[Vec<f32>],
232 out_elems: usize,
233) -> Vec<f32> {
234 assert_eq!(bcasts.len(), num_bcast);
235 assert_eq!(xs.len(), num_xs);
236 let plan = compile_scan_body(body, num_bcast, num_xs);
237 let mut arena: Vec<u8> = Vec::new();
238 let init_off = arena.len();
239 arena.extend(init.iter().flat_map(|f| f.to_le_bytes()));
240 let mut bcast_outer: Vec<(usize, usize)> = Vec::with_capacity(num_bcast);
241 for v in bcasts {
242 let off = arena.len();
243 arena.extend(v.iter().flat_map(|f| f.to_le_bytes()));
244 bcast_outer.push((off, v.len() * 4));
245 }
246 let mut xs_outer: Vec<(usize, usize)> = Vec::with_capacity(num_xs);
247 for v in xs {
248 let total = v.len() * 4;
249 let off = arena.len();
250 arena.extend(v.iter().flat_map(|f| f.to_le_bytes()));
251 xs_outer.push((off, total / length as usize));
252 }
253 let final_off = arena.len();
254 arena.resize(final_off + out_elems * 4, 0);
255 let desc = ScanHostDesc {
256 plan: Arc::new(plan),
257 outer_init_off: init_off,
258 outer_final_off: final_off,
259 length,
260 save_trajectory,
261 xs_outer,
262 bcast_outer,
263 };
264 unsafe {
265 execute_scan_host_desc(arena.as_mut_ptr(), &desc);
266 }
267 arena[final_off..final_off + out_elems * 4]
268 .chunks_exact(4)
269 .map(|c| f32::from_le_bytes(c.try_into().unwrap()))
270 .collect()
271}
272
273#[macro_export]
275macro_rules! rlx_scan_host_desc {
276 ($graph:expr, $node:expr, $byte_off:expr) => {
277 $crate::thunk::scan_host_desc_from_node(&$graph, $node, $byte_off)
278 };
279}
280
281#[macro_export]
283macro_rules! rlx_execute_scan_on_bytes {
284 ($base:expr, $desc:expr) => {
285 $crate::thunk::execute_scan_host_desc($base, $desc)
286 };
287}
288
289#[macro_export]
294macro_rules! rlx_arena_stage_d2h {
295 (
296 arena_size_bytes = $nbytes:expr,
297 sync = $sync:block,
298 dtoh = |$host:ident| $dtoh:block,
299 on_host = |$host_mut:ident| $on_host:block,
300 htod = |$host_back:ident| $htod:block $(,)?
301 ) => {{
302 let n_f32 = ($nbytes) / 4;
303 $sync
304 let mut $host = vec![0f32; n_f32];
305 $dtoh
306 {
307 let $host_mut: &mut [f32] = &mut $host[..];
308 $on_host
309 }
310 let $host_back = $host;
311 $htod
312 }};
313}
314
315#[macro_export]
317macro_rules! rlx_scan_stage_d2h {
318 (
319 arena_size_bytes = $nbytes:expr,
320 desc = $desc:expr,
321 sync = $sync:block,
322 dtoh = |$host:ident| $dtoh:block,
323 htod = |$host_back:ident| $htod:block $(,)?
324 ) => {
325 $crate::rlx_arena_stage_d2h! {
326 arena_size_bytes = $nbytes,
327 sync = $sync,
328 dtoh = |$host| $dtoh,
329 on_host = |host_mut| {
330 unsafe {
331 $crate::thunk::execute_scan_host_desc(
332 host_mut.as_mut_ptr() as *mut u8,
333 $desc,
334 );
335 }
336 },
337 htod = |$host_back| $htod,
338 }
339 };
340}
341
342#[macro_export]
344macro_rules! rlx_maybe_unroll_scans {
345 ($graph:expr, $max_length:expr) => {
346 $crate::thunk::maybe_unroll_scans($graph, $max_length)
347 };
348}