Skip to main content

nexus_rt/codegen_audit/
batch.rs

1//! Batch pipeline and DAG codegen audit cases.
2//!
3//! Categories 21-22 from the assembly audit plan.
4//!
5//! Batch variants own an input buffer. `run()` drains the buffer and processes
6//! each item through the chain. The codegen question: does the compiler
7//! generate the same quality inner loop as single-item dispatch?
8
9#![allow(clippy::type_complexity)]
10#![allow(unused_variables)]
11
12use super::helpers::*;
13use crate::dag::{DagArmSeed, DagBuilder};
14use crate::pipeline::PipelineBuilder;
15use crate::{IntoHandler, World};
16
17// ═══════════════════════════════════════════════════════════════════
18// 21. Batch pipeline
19// ═══════════════════════════════════════════════════════════════════
20
21#[inline(never)]
22pub fn batch_pipe_linear_3(world: &mut World) {
23    let reg = world.registry();
24    let mut bp = PipelineBuilder::<u64>::new()
25        .then(add_one, &reg)
26        .then(double, &reg)
27        .then(add_three, &reg)
28        .then(consume_val, &reg)
29        .build_batch(64);
30    bp.input_mut().extend(0..64);
31    bp.run(world);
32}
33
34#[inline(never)]
35pub fn batch_pipe_linear_10(world: &mut World) {
36    let reg = world.registry();
37    let mut bp = PipelineBuilder::<u64>::new()
38        .then(add_one, &reg)
39        .then(double, &reg)
40        .then(add_three, &reg)
41        .then(square, &reg)
42        .then(sub_ten, &reg)
43        .then(shr_one, &reg)
44        .then(xor_mask, &reg)
45        .then(add_seven, &reg)
46        .then(triple, &reg)
47        .then(add_forty_two, &reg)
48        .then(consume_val, &reg)
49        .build_batch(64);
50    bp.input_mut().extend(0..64);
51    bp.run(world);
52}
53
54#[inline(never)]
55pub fn batch_pipe_guard(world: &mut World) {
56    let reg = world.registry();
57    let mut bp = PipelineBuilder::<u64>::new()
58        .then(add_one, &reg)
59        .guard(|x: &u64| *x > 10, &reg)
60        .unwrap_or(0)
61        .then(consume_val, &reg)
62        .build_batch(64);
63    bp.input_mut().extend(0..64);
64    bp.run(world);
65}
66
67#[inline(never)]
68pub fn batch_pipe_option_chain(world: &mut World) {
69    let reg = world.registry();
70    let mut bp = PipelineBuilder::<u64>::new()
71        .then(maybe_positive, &reg)
72        .map(double, &reg)
73        .filter(|x: &u64| *x < 1000, &reg)
74        .unwrap_or(0)
75        .then(consume_val, &reg)
76        .build_batch(64);
77    bp.input_mut().extend(0..64);
78    bp.run(world);
79}
80
81#[inline(never)]
82pub fn batch_pipe_result_chain(world: &mut World) {
83    let reg = world.registry();
84    let mut bp = PipelineBuilder::<u64>::new()
85        .then(try_parse, &reg)
86        .map(double, &reg)
87        .unwrap_or(0)
88        .then(consume_val, &reg)
89        .build_batch(64);
90    bp.input_mut().extend(0..64);
91    bp.run(world);
92}
93
94#[inline(never)]
95pub fn batch_pipe_mixed_arity(world: &mut World) {
96    let reg = world.registry();
97    let mut bp = PipelineBuilder::<u64>::new()
98        .then(add_one, &reg)
99        .then(add_res_a, &reg)
100        .then(write_res_a, &reg)
101        .then(add_both, &reg)
102        .then(consume_val, &reg)
103        .build_batch(64);
104    bp.input_mut().extend(0..64);
105    bp.run(world);
106}
107
108#[inline(never)]
109pub fn batch_pipe_splat(world: &mut World) {
110    let reg = world.registry();
111    let mut bp = PipelineBuilder::<u64>::new()
112        .then(split_u64, &reg)
113        .splat()
114        .then(splat_add, &reg)
115        .then(consume_val, &reg)
116        .build_batch(64);
117    bp.input_mut().extend(0..64);
118    bp.run(world);
119}
120
121#[inline(never)]
122pub fn batch_pipe_route(world: &mut World) {
123    let reg = world.registry();
124
125    let on_true = PipelineBuilder::<u64>::new().then(double, &reg);
126    let on_false = PipelineBuilder::<u64>::new().then(add_one, &reg);
127
128    let mut bp = PipelineBuilder::<u64>::new()
129        .then(add_one, &reg)
130        .route(|x: &u64| *x > 32, &reg, on_true, on_false)
131        .then(consume_val, &reg)
132        .build_batch(64);
133    bp.input_mut().extend(0..64);
134    bp.run(world);
135}
136
137#[inline(never)]
138pub fn batch_pipe_large(world: &mut World) {
139    let reg = world.registry();
140    let mut bp = PipelineBuilder::<u64>::new()
141        .then(add_one, &reg)
142        .then(double, &reg)
143        .then(add_three, &reg)
144        .then(square, &reg)
145        .then(sub_ten, &reg)
146        .then(shr_one, &reg)
147        .then(xor_mask, &reg)
148        .then(add_seven, &reg)
149        .then(triple, &reg)
150        .then(add_forty_two, &reg)
151        .then(consume_val, &reg)
152        .build_batch(256);
153    bp.input_mut().extend(0..256);
154    bp.run(world);
155}
156
157// -- Batch early termination: guard/result → long dead chain per item --
158
159#[inline(never)]
160pub fn batch_pipe_guard_skip_10(world: &mut World) {
161    let reg = world.registry();
162    // Guard at step 1 → 10 maps. In the batch inner loop, items where
163    // guard returns None should skip all 10 maps. Does the compiler
164    // generate a tight branch per item?
165    let mut bp = PipelineBuilder::<u64>::new()
166        .then(|x: u64| x, &reg)
167        .guard(|x: &u64| *x > 32, &reg)
168        .map(add_one, &reg)
169        .map(double, &reg)
170        .map(add_three, &reg)
171        .map(square, &reg)
172        .map(sub_ten, &reg)
173        .map(shr_one, &reg)
174        .map(xor_mask, &reg)
175        .map(add_seven, &reg)
176        .map(triple, &reg)
177        .map(add_forty_two, &reg)
178        .unwrap_or(0)
179        .then(consume_val, &reg)
180        .build_batch(64);
181    // Half the items will be filtered (0..32 fail guard, 33..64 pass).
182    bp.input_mut().extend(0..64);
183    bp.run(world);
184}
185
186#[inline(never)]
187pub fn batch_pipe_res_skip_10(world: &mut World) {
188    let reg = world.registry();
189    // try_parse returns Err for input >= 10_000. In the batch loop,
190    // Err items should skip all 10 maps.
191    let mut bp = PipelineBuilder::<u64>::new()
192        .then(try_parse, &reg)
193        .map(add_one, &reg)
194        .map(double, &reg)
195        .map(add_three, &reg)
196        .map(square, &reg)
197        .map(sub_ten, &reg)
198        .map(shr_one, &reg)
199        .map(xor_mask, &reg)
200        .map(add_seven, &reg)
201        .map(triple, &reg)
202        .map(add_forty_two, &reg)
203        .unwrap_or(0)
204        .then(consume_val, &reg)
205        .build_batch(64);
206    bp.input_mut().extend(0..64);
207    bp.run(world);
208}
209
210// ═══════════════════════════════════════════════════════════════════
211// 22. Batch DAG
212// ═══════════════════════════════════════════════════════════════════
213
214#[inline(never)]
215pub fn batch_dag_linear_3(world: &mut World) {
216    let reg = world.registry();
217    let mut bd = DagBuilder::<u64>::new()
218        .root(add_one, &reg)
219        .then(ref_double, &reg)
220        .then(ref_add_three, &reg)
221        .then(ref_consume, &reg)
222        .build_batch(64);
223    bd.input_mut().extend(0..64);
224    bd.run(world);
225}
226
227#[inline(never)]
228pub fn batch_dag_linear_10(world: &mut World) {
229    let reg = world.registry();
230    let mut bd = DagBuilder::<u64>::new()
231        .root(add_one, &reg)
232        .then(ref_double, &reg)
233        .then(ref_add_three, &reg)
234        .then(ref_square, &reg)
235        .then(ref_sub_ten, &reg)
236        .then(ref_shr_one, &reg)
237        .then(ref_xor_mask, &reg)
238        .then(ref_add_seven, &reg)
239        .then(ref_triple, &reg)
240        .then(ref_add_forty_two, &reg)
241        .then(ref_consume, &reg)
242        .build_batch(64);
243    bd.input_mut().extend(0..64);
244    bd.run(world);
245}
246
247#[inline(never)]
248pub fn batch_dag_fork_merge(world: &mut World) {
249    let reg = world.registry();
250    let mut bd = DagBuilder::<u64>::new()
251        .root(add_one, &reg)
252        .fork()
253        .arm(|a| a.then(ref_double, &reg))
254        .arm(|a| a.then(ref_triple, &reg))
255        .merge(merge_add, &reg)
256        .then(ref_consume, &reg)
257        .build_batch(64);
258    bd.input_mut().extend(0..64);
259    bd.run(world);
260}
261
262#[inline(never)]
263pub fn batch_dag_guard(world: &mut World) {
264    let reg = world.registry();
265    let mut bd = DagBuilder::<u64>::new()
266        .root(add_one, &reg)
267        .guard(|x: &u64| *x > 10, &reg)
268        .unwrap_or(0)
269        .then(ref_consume, &reg)
270        .build_batch(64);
271    bd.input_mut().extend(0..64);
272    bd.run(world);
273}
274
275#[inline(never)]
276pub fn batch_dag_mixed_arity(world: &mut World) {
277    let reg = world.registry();
278    let mut bd = DagBuilder::<u64>::new()
279        .root(add_one, &reg)
280        .then(ref_add_res_a, &reg)
281        .then(ref_write_res_a, &reg)
282        .then(ref_add_both, &reg)
283        .then(ref_consume, &reg)
284        .build_batch(64);
285    bd.input_mut().extend(0..64);
286    bd.run(world);
287}
288
289#[inline(never)]
290pub fn batch_dag_diamond(world: &mut World) {
291    let reg = world.registry();
292    let mut bd = DagBuilder::<u64>::new()
293        .root(add_one, &reg)
294        .fork()
295        .arm(|a| a.then(ref_double, &reg))
296        .arm(|a| a.then(ref_triple, &reg))
297        .merge(merge_add, &reg)
298        .fork()
299        .arm(|a| a.then(ref_square, &reg))
300        .arm(|a| a.then(ref_shr_one, &reg))
301        .merge(merge_mul, &reg)
302        .then(ref_consume, &reg)
303        .build_batch(64);
304    bd.input_mut().extend(0..64);
305    bd.run(world);
306}
307
308#[inline(never)]
309pub fn batch_dag_large(world: &mut World) {
310    let reg = world.registry();
311    let mut bd = DagBuilder::<u64>::new()
312        .root(add_one, &reg)
313        .then(ref_double, &reg)
314        .then(ref_add_three, &reg)
315        .then(ref_square, &reg)
316        .then(ref_sub_ten, &reg)
317        .then(ref_shr_one, &reg)
318        .then(ref_xor_mask, &reg)
319        .then(ref_add_seven, &reg)
320        .then(ref_triple, &reg)
321        .then(ref_add_forty_two, &reg)
322        .then(ref_consume, &reg)
323        .build_batch(256);
324    bd.input_mut().extend(0..256);
325    bd.run(world);
326}
327
328// ═══════════════════════════════════════════════════════════════════
329// Remaining batch pipeline gaps
330// ═══════════════════════════════════════════════════════════════════
331
332// ---- 21.3: guard + filter in batch ----
333
334#[inline(never)]
335pub fn batch_pipe_guard_filter(world: &mut World) {
336    let reg = world.registry();
337    let mut bp = PipelineBuilder::<u64>::new()
338        .then(add_one, &reg)
339        .guard(|x: &u64| *x > 10, &reg)
340        .filter(|x: &u64| *x < 1000, &reg)
341        .unwrap_or(0)
342        .then(consume_val, &reg)
343        .build_batch(64);
344    bp.input_mut().extend(0..64);
345    bp.run(world);
346}
347
348// ---- 21.6: type transition in batch ----
349
350#[inline(never)]
351pub fn batch_pipe_transition(world: &mut World) {
352    let reg = world.registry();
353
354    fn log_error(_err: u32) {}
355
356    let mut bp = PipelineBuilder::<u64>::new()
357        .then(|x: u64| x, &reg)
358        .guard(|x: &u64| *x > 0, &reg)
359        .ok_or(0u32)
360        .catch(log_error, &reg)
361        .unwrap_or(0)
362        .then(consume_val, &reg)
363        .build_batch(64);
364    bp.input_mut().extend(0..64);
365    bp.run(world);
366}
367
368// ---- 21.8: 3-way switch in batch ----
369
370#[inline(never)]
371pub fn batch_pipe_switch(world: &mut World) {
372    let reg = world.registry();
373    let mut bp = PipelineBuilder::<u64>::new()
374        .then(
375            |x: u64| match x % 3 {
376                0 => x.wrapping_mul(2),
377                1 => x.wrapping_add(10),
378                _ => x.wrapping_sub(5),
379            },
380            &reg,
381        )
382        .then(consume_val, &reg)
383        .build_batch(64);
384    bp.input_mut().extend(0..64);
385    bp.run(world);
386}
387
388// ---- 21.11: dispatch in batch ----
389
390#[inline(never)]
391pub fn batch_pipe_dispatch(world: &mut World) {
392    let reg = world.registry();
393    let handler = consume_val.into_handler(&reg);
394    let mut bp = PipelineBuilder::<u64>::new()
395        .then(add_one, &reg)
396        .dispatch(handler)
397        .build_batch(64);
398    bp.input_mut().extend(0..64);
399    bp.run(world);
400}
401
402// ---- 21.12: buffer reuse (two runs) ----
403
404#[inline(never)]
405pub fn batch_pipe_buffer_reuse(world: &mut World) {
406    let reg = world.registry();
407    let mut bp = PipelineBuilder::<u64>::new()
408        .then(add_one, &reg)
409        .then(double, &reg)
410        .then(consume_val, &reg)
411        .build_batch(64);
412    // First run
413    bp.input_mut().extend(0..64);
414    bp.run(world);
415    // Second run — buffer already allocated, no new allocation
416    bp.input_mut().extend(0..64);
417    bp.run(world);
418}
419
420// ---- 21.13: empty input ----
421
422#[inline(never)]
423pub fn batch_pipe_empty(world: &mut World) {
424    let reg = world.registry();
425    let mut bp = PipelineBuilder::<u64>::new()
426        .then(add_one, &reg)
427        .then(double, &reg)
428        .then(add_three, &reg)
429        .then(consume_val, &reg)
430        .build_batch(64);
431    // No extend — zero items
432    bp.run(world);
433}
434
435// ---- 21.14: single item ----
436
437#[inline(never)]
438pub fn batch_pipe_single_item(world: &mut World) {
439    let reg = world.registry();
440    let mut bp = PipelineBuilder::<u64>::new()
441        .then(add_one, &reg)
442        .then(double, &reg)
443        .then(consume_val, &reg)
444        .build_batch(64);
445    bp.input_mut().push(42);
446    bp.run(world);
447}
448
449// ---- 21.15: drain codegen (trivial passthrough) ----
450
451#[inline(never)]
452pub fn batch_pipe_drain_codegen(world: &mut World) {
453    let reg = world.registry();
454    let mut bp = PipelineBuilder::<u64>::new()
455        .then(|x: u64| x, &reg)
456        .then(consume_val, &reg)
457        .build_batch(64);
458    bp.input_mut().extend(0..64);
459    bp.run(world);
460}
461
462// ═══════════════════════════════════════════════════════════════════
463// Remaining batch DAG gaps
464// ═══════════════════════════════════════════════════════════════════
465
466// ---- 22.3: fork-4 in batch DAG ----
467
468#[inline(never)]
469pub fn batch_dag_fork4(world: &mut World) {
470    let reg = world.registry();
471    let mut bd = DagBuilder::<u64>::new()
472        .root(add_one, &reg)
473        .fork()
474        .arm(|a| a.then(ref_double, &reg))
475        .arm(|a| a.then(ref_triple, &reg))
476        .arm(|a| a.then(ref_add_seven, &reg))
477        .arm(|a| a.then(ref_xor_mask, &reg))
478        .merge(merge_4, &reg)
479        .then(ref_consume, &reg)
480        .build_batch(64);
481    bd.input_mut().extend(0..64);
482    bd.run(world);
483}
484
485// ---- 22.4: nested fork in batch DAG ----
486
487#[inline(never)]
488pub fn batch_dag_nested_fork(world: &mut World) {
489    let reg = world.registry();
490    let mut bd = DagBuilder::<u64>::new()
491        .root(add_one, &reg)
492        .fork()
493        .arm(|a| {
494            a.then(ref_double, &reg)
495                .fork()
496                .arm(|b| b.then(ref_add_one, &reg))
497                .arm(|b| b.then(ref_triple, &reg))
498                .merge(merge_add, &reg)
499        })
500        .arm(|a| a.then(ref_add_seven, &reg))
501        .merge(merge_add, &reg)
502        .then(ref_consume, &reg)
503        .build_batch(64);
504    bd.input_mut().extend(0..64);
505    bd.run(world);
506}
507
508// ---- 22.5: option chain in batch DAG ----
509
510#[inline(never)]
511pub fn batch_dag_option_chain(world: &mut World) {
512    let reg = world.registry();
513    let mut bd = DagBuilder::<u64>::new()
514        .root(maybe_positive, &reg)
515        .map(ref_double, &reg)
516        .filter(|x: &u64| *x < 1000, &reg)
517        .unwrap_or(0)
518        .then(ref_consume, &reg)
519        .build_batch(64);
520    bd.input_mut().extend(0..64);
521    bd.run(world);
522}
523
524// ---- 22.6: result chain in batch DAG ----
525
526#[inline(never)]
527pub fn batch_dag_result_chain(world: &mut World) {
528    let reg = world.registry();
529    let mut bd = DagBuilder::<u64>::new()
530        .root(try_parse, &reg)
531        .map(ref_double, &reg)
532        .unwrap_or(0)
533        .then(ref_consume, &reg)
534        .build_batch(64);
535    bd.input_mut().extend(0..64);
536    bd.run(world);
537}
538
539// ---- 22.7: route in batch DAG ----
540
541#[inline(never)]
542pub fn batch_dag_route(world: &mut World) {
543    let reg = world.registry();
544
545    let on_true = DagArmSeed::<u64>::new().then(ref_double, &reg);
546    let on_false = DagArmSeed::<u64>::new().then(ref_add_one, &reg);
547
548    let mut bd = DagBuilder::<u64>::new()
549        .root(add_one, &reg)
550        .route(|x: &u64| *x > 32, &reg, on_true, on_false)
551        .then(ref_consume, &reg)
552        .build_batch(64);
553    bd.input_mut().extend(0..64);
554    bd.run(world);
555}
556
557// ---- 22.8: splat in batch DAG ----
558
559#[inline(never)]
560pub fn batch_dag_splat(world: &mut World) {
561    let reg = world.registry();
562    let mut bd = DagBuilder::<u64>::new()
563        .root(split_u64, &reg)
564        .splat()
565        .then(ref_splat_add, &reg)
566        .then(ref_consume, &reg)
567        .build_batch(64);
568    bd.input_mut().extend(0..64);
569    bd.run(world);
570}
571
572// ---- 22.9: heavy batch DAG (fork-4 with 5-step arms) ----
573
574#[inline(never)]
575pub fn batch_dag_heavy(world: &mut World) {
576    let reg = world.registry();
577    let mut bd = DagBuilder::<u64>::new()
578        .root(add_one, &reg)
579        .fork()
580        .arm(|a| {
581            a.then(ref_double, &reg)
582                .then(ref_add_three, &reg)
583                .then(ref_square, &reg)
584                .then(ref_sub_ten, &reg)
585                .then(ref_shr_one, &reg)
586        })
587        .arm(|a| {
588            a.then(ref_add_one, &reg)
589                .then(ref_triple, &reg)
590                .then(ref_xor_mask, &reg)
591                .then(ref_add_seven, &reg)
592                .then(ref_add_forty_two, &reg)
593        })
594        .arm(|a| {
595            a.then(ref_triple, &reg)
596                .then(ref_add_three, &reg)
597                .then(ref_double, &reg)
598                .then(ref_square, &reg)
599                .then(ref_add_one, &reg)
600        })
601        .arm(|a| {
602            a.then(ref_xor_mask, &reg)
603                .then(ref_shr_one, &reg)
604                .then(ref_add_seven, &reg)
605                .then(ref_triple, &reg)
606                .then(ref_sub_ten, &reg)
607        })
608        .merge(merge_4, &reg)
609        .then(ref_consume, &reg)
610        .build_batch(64);
611    bd.input_mut().extend(0..64);
612    bd.run(world);
613}
614
615// ---- 22.10: dispatch in batch DAG ----
616
617#[inline(never)]
618pub fn batch_dag_dispatch(world: &mut World) {
619    let reg = world.registry();
620    let handler = consume_val.into_handler(&reg);
621    let mut bd = DagBuilder::<u64>::new()
622        .root(add_one, &reg)
623        .then(ref_double, &reg)
624        .dispatch(handler)
625        .build_batch(64);
626    bd.input_mut().extend(0..64);
627    bd.run(world);
628}