1use std::collections::HashMap;
14use std::mem;
15
16use cranelift_codegen::ir::{self, AbiParam, InstBuilder, types};
17use cranelift_codegen::settings::{self, Configurable};
18use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
19use cranelift_jit::{JITBuilder, JITModule};
20use cranelift_module::{Linkage, Module};
21
22use crate::ast::PolydatNode;
23use crate::ast::SlotShape;
24
25use super::kernels::{JitCore, JitKernelPull, JitKernelPush, JitKernelPushPull, JitKernelRaw};
26
27extern "C" fn jit_xxh3_hash(value: u64) -> u64 {
31 guarded(|| xxhash_rust::xxh3::xxh3_64(&value.to_le_bytes()))
32}
33
34extern "C" fn jit_interleave(a: u64, b: u64) -> u64 {
36 guarded(|| {
37 let mut result: u64 = 0;
38 for i in 0..32 {
39 result |= ((a >> i) & 1) << (2 * i);
40 result |= ((b >> i) & 1) << (2 * i + 1);
41 }
42 result
43 })
44}
45
46extern "C" fn jit_lut_sample(input_bits: u64, lut_ptr: u64, lut_len: u64) -> u64 {
51 guarded(|| {
52 let u = f64::from_bits(input_bits).clamp(0.0, 1.0);
53 let n = (lut_len - 1) as f64;
54 let pos = u * n;
55 let idx = (pos as usize).min(lut_len as usize - 2);
56 let frac = pos - idx as f64;
57 let result = unsafe {
58 let ptr = lut_ptr as *const f64;
59 let a = *ptr.add(idx);
60 let b = *ptr.add(idx + 1);
61 a * (1.0 - frac) + b * frac
62 };
63 result.to_bits()
64 })
65}
66
67extern "C" fn jit_shuffle(input: u64, feedback: u64, size: u64, min: u64) -> u64 {
69 guarded(|| {
70 let mut register = (input % size) + 1;
71 loop {
72 let lsb = register & 1;
73 register >>= 1;
74 if lsb != 0 {
75 register ^= feedback;
76 }
77 if register <= size {
78 break;
79 }
80 }
81 (register - 1) + min
82 })
83}
84
85extern "C" fn jit_sin(bits: u64) -> u64 {
87 guarded(|| f64::from_bits(bits).sin().to_bits())
88}
89extern "C" fn jit_cos(bits: u64) -> u64 {
90 guarded(|| f64::from_bits(bits).cos().to_bits())
91}
92extern "C" fn jit_tan(bits: u64) -> u64 {
93 guarded(|| f64::from_bits(bits).tan().to_bits())
94}
95extern "C" fn jit_asin(bits: u64) -> u64 {
96 guarded(|| f64::from_bits(bits).asin().to_bits())
97}
98extern "C" fn jit_acos(bits: u64) -> u64 {
99 guarded(|| f64::from_bits(bits).acos().to_bits())
100}
101extern "C" fn jit_atan(bits: u64) -> u64 {
102 guarded(|| f64::from_bits(bits).atan().to_bits())
103}
104extern "C" fn jit_sqrt(bits: u64) -> u64 {
105 guarded(|| f64::from_bits(bits).sqrt().to_bits())
106}
107extern "C" fn jit_abs_f64(bits: u64) -> u64 {
108 guarded(|| f64::from_bits(bits).abs().to_bits())
109}
110extern "C" fn jit_ln(bits: u64) -> u64 {
111 guarded(|| f64::from_bits(bits).ln().to_bits())
112}
113extern "C" fn jit_exp(bits: u64) -> u64 {
114 guarded(|| f64::from_bits(bits).exp().to_bits())
115}
116extern "C" fn jit_floor_base10(bits: u64) -> u64 {
117 guarded(|| {
118 use crate::numeric::round_numbers::*;
119 let x = f64::from_bits(bits);
120 let r = if !positive_finite(x) {
121 0.0
122 } else {
123 floor_pow10(x)
124 };
125 r.to_bits()
126 })
127}
128extern "C" fn jit_ceiling_base10(bits: u64) -> u64 {
129 guarded(|| {
130 use crate::numeric::round_numbers::*;
131 let x = f64::from_bits(bits);
132 let r = if !positive_finite(x) {
133 0.0
134 } else {
135 let lo = floor_pow10(x);
136 if lo == x { lo } else { lo * 10.0 }
137 };
138 r.to_bits()
139 })
140}
141extern "C" fn jit_closest_base10(bits: u64) -> u64 {
142 guarded(|| {
143 use crate::numeric::round_numbers::*;
144 let x = f64::from_bits(bits);
145 let r = if !positive_finite(x) {
146 0.0
147 } else {
148 let lo = floor_pow10(x);
149 let hi = if lo == x { lo } else { lo * 10.0 };
150 pick_closest(x, lo, hi)
151 };
152 r.to_bits()
153 })
154}
155extern "C" fn jit_floor_decade(bits: u64) -> u64 {
156 guarded(|| {
157 use crate::numeric::round_numbers::*;
158 let x = f64::from_bits(bits);
159 let r = if !positive_finite(x) {
160 0.0
161 } else {
162 let base = floor_pow10(x);
163 (x / base).floor() * base
164 };
165 r.to_bits()
166 })
167}
168extern "C" fn jit_ceiling_decade(bits: u64) -> u64 {
169 guarded(|| {
170 use crate::numeric::round_numbers::*;
171 let x = f64::from_bits(bits);
172 let r = if !positive_finite(x) {
173 0.0
174 } else {
175 let base = floor_pow10(x);
176 (x / base).ceil() * base
177 };
178 r.to_bits()
179 })
180}
181extern "C" fn jit_closest_decade(bits: u64) -> u64 {
182 guarded(|| {
183 use crate::numeric::round_numbers::*;
184 let x = f64::from_bits(bits);
185 let r = if !positive_finite(x) {
186 0.0
187 } else {
188 let base = floor_pow10(x);
189 (x / base).round() * base
190 };
191 r.to_bits()
192 })
193}
194extern "C" fn jit_floor_binomial(bits: u64) -> u64 {
195 guarded(|| {
196 use crate::numeric::round_numbers::*;
197 let x = f64::from_bits(bits);
198 let r = if !positive_finite(x) {
199 0.0
200 } else {
201 floor_pow2(x)
202 };
203 r.to_bits()
204 })
205}
206extern "C" fn jit_ceiling_binomial(bits: u64) -> u64 {
207 guarded(|| {
208 use crate::numeric::round_numbers::*;
209 let x = f64::from_bits(bits);
210 let r = if !positive_finite(x) {
211 0.0
212 } else {
213 let lo = floor_pow2(x);
214 if lo == x { lo } else { lo * 2.0 }
215 };
216 r.to_bits()
217 })
218}
219extern "C" fn jit_closest_binomial(bits: u64) -> u64 {
220 guarded(|| {
221 use crate::numeric::round_numbers::*;
222 let x = f64::from_bits(bits);
223 let r = if !positive_finite(x) {
224 0.0
225 } else {
226 let lo = floor_pow2(x);
227 let hi = if lo == x { lo } else { lo * 2.0 };
228 pick_closest(x, lo, hi)
229 };
230 r.to_bits()
231 })
232}
233extern "C" fn jit_floor_fibonacci(bits: u64) -> u64 {
234 guarded(|| {
235 use crate::numeric::round_numbers::*;
236 let x = f64::from_bits(bits);
237 let r = if !positive_finite(x) {
238 0.0
239 } else {
240 floor_fibonacci_val(x)
241 };
242 r.to_bits()
243 })
244}
245extern "C" fn jit_ceiling_fibonacci(bits: u64) -> u64 {
246 guarded(|| {
247 use crate::numeric::round_numbers::*;
248 let x = f64::from_bits(bits);
249 let r = if !positive_finite(x) {
250 0.0
251 } else {
252 ceiling_fibonacci_val(x)
253 };
254 r.to_bits()
255 })
256}
257extern "C" fn jit_closest_fibonacci(bits: u64) -> u64 {
258 guarded(|| {
259 use crate::numeric::round_numbers::*;
260 let x = f64::from_bits(bits);
261 let r = if !positive_finite(x) {
262 0.0
263 } else {
264 pick_closest(x, floor_fibonacci_val(x), ceiling_fibonacci_val(x))
265 };
266 r.to_bits()
267 })
268}
269
270extern "C" fn jit_atan2(y_bits: u64, x_bits: u64) -> u64 {
271 guarded(|| {
272 f64::from_bits(y_bits)
273 .atan2(f64::from_bits(x_bits))
274 .to_bits()
275 })
276}
277extern "C" fn jit_pow(base_bits: u64, exp_bits: u64) -> u64 {
278 guarded(|| {
279 f64::from_bits(base_bits)
280 .powf(f64::from_bits(exp_bits))
281 .to_bits()
282 })
283}
284extern "C" fn jit_round_nearest(x_bits: u64, iv_bits: u64) -> u64 {
285 guarded(|| {
286 let x = f64::from_bits(x_bits);
287 let interval = f64::from_bits(iv_bits);
288 let r = if !(interval.is_finite() && interval > 0.0) {
289 x
290 } else {
291 (x / interval).round() * interval
292 };
293 r.to_bits()
294 })
295}
296extern "C" fn jit_round_floor(x_bits: u64, iv_bits: u64) -> u64 {
297 guarded(|| {
298 let x = f64::from_bits(x_bits);
299 let interval = f64::from_bits(iv_bits);
300 let r = if !(interval.is_finite() && interval > 0.0) {
301 x
302 } else {
303 (x / interval).floor() * interval
304 };
305 r.to_bits()
306 })
307}
308extern "C" fn jit_round_ceiling(x_bits: u64, iv_bits: u64) -> u64 {
309 guarded(|| {
310 let x = f64::from_bits(x_bits);
311 let interval = f64::from_bits(iv_bits);
312 let r = if !(interval.is_finite() && interval > 0.0) {
313 x
314 } else {
315 (x / interval).ceil() * interval
316 };
317 r.to_bits()
318 })
319}
320
321extern "C" fn jit_pcg(input: u64, seed: u64, stream: u64) -> u64 {
322 guarded(|| {
323 let inc = 2u64.wrapping_mul(stream).wrapping_add(1);
324 crate::numeric::pcg::pcg_seek(seed, inc, input)
325 })
326}
327extern "C" fn jit_pcg_stream(input: u64, stream: u64, seed: u64) -> u64 {
328 guarded(|| {
329 let inc = 2u64.wrapping_mul(stream).wrapping_add(1);
330 crate::numeric::pcg::pcg_seek(seed, inc, input)
331 })
332}
333extern "C" fn jit_n_of(input: u64, n: u64, m: u64) -> u64 {
334 guarded(|| {
335 if m == 0 {
336 return 0;
337 }
338 crate::numeric::n_of_m::n_of_m_eval(input, n, m)
339 })
340}
341
342extern "C" fn jit_cycle_walk(pos: u64, range: u64, seed: u64, inc: u64) -> u64 {
343 guarded(|| {
344 let stream = inc.saturating_sub(1) / 2;
345 let state = crate::numeric::pcg::build_cycle_walk_state(range, seed, stream);
346 crate::numeric::pcg::cycle_walk_inner(
347 pos,
348 range,
349 state.half_bits,
350 state.half_mask,
351 &state.round_keys,
352 )
353 })
354}
355
356extern "C" fn jit_perlin_1d(input: u64, perm_ptr: u64, freq_bits: u64) -> u64 {
357 guarded(|| {
358 let perm = unsafe { &*(perm_ptr as *const crate::numeric::noise::PermTable) };
359 let freq = f64::from_bits(freq_bits);
360 let r = crate::numeric::noise::perlin_1d_algo(perm, input as f64 * freq);
361 r.to_bits()
362 })
363}
364
365extern "C" fn jit_perlin_2d(x: u64, y: u64, perm_ptr: u64, freq_bits: u64) -> u64 {
366 guarded(|| {
367 let perm = unsafe { &*(perm_ptr as *const crate::numeric::noise::PermTable) };
368 let freq = f64::from_bits(freq_bits);
369 let r = crate::numeric::noise::perlin_2d_algo(perm, x as f64 * freq, y as f64 * freq);
370 r.to_bits()
371 })
372}
373
374extern "C" fn jit_simplex_2d(x: u64, y: u64, perm_ptr: u64, freq_bits: u64) -> u64 {
375 guarded(|| {
376 let perm = unsafe { &*(perm_ptr as *const crate::numeric::noise::PermTable) };
377 let freq = f64::from_bits(freq_bits);
378 let r = crate::numeric::noise::simplex_2d_algo(perm, x as f64 * freq, y as f64 * freq);
379 r.to_bits()
380 })
381}
382
383extern "C" fn jit_fractal_noise_1d(input: u64, perm_ptr: u64, freq_bits: u64, octaves: u64) -> u64 {
384 guarded(|| {
385 let perm = unsafe { &*(perm_ptr as *const crate::numeric::noise::PermTable) };
386 let freq = f64::from_bits(freq_bits);
387 let r = crate::numeric::noise::fbm_1d(perm, input as f64, freq, octaves as u32);
388 r.to_bits()
389 })
390}
391
392extern "C" fn jit_fractal_noise_2d(
393 x: u64,
394 y: u64,
395 perm_ptr: u64,
396 freq_bits: u64,
397 octaves: u64,
398) -> u64 {
399 guarded(|| {
400 let perm = unsafe { &*(perm_ptr as *const crate::numeric::noise::PermTable) };
401 let freq = f64::from_bits(freq_bits);
402 let r = crate::numeric::noise::fbm_2d(perm, x as f64, y as f64, freq, octaves as u32);
403 r.to_bits()
404 })
405}
406
407extern "C" fn jit_thread_id() -> u64 {
408 guarded(|| {
409 let id = std::thread::current().id();
410 let id_str = format!("{id:?}");
411 let num = id_str.trim_start_matches("ThreadId(").trim_end_matches(')');
412 num.parse().unwrap_or(0)
413 })
414}
415
416extern "C" fn jit_current_epoch_millis() -> u64 {
417 guarded(|| {
418 std::time::SystemTime::now()
419 .duration_since(std::time::UNIX_EPOCH)
420 .unwrap()
421 .as_millis() as u64
422 })
423}
424
425#[repr(C, align(16))]
462struct JitJmpBuf([u8; 512]);
463
464#[cfg(not(windows))]
465unsafe extern "C" {
466 fn _setjmp(env: *mut JitJmpBuf) -> i32;
467 fn _longjmp(env: *mut JitJmpBuf, val: i32) -> !;
468}
469
470#[cfg(windows)]
483unsafe extern "C" {
484 fn _setjmp(env: *mut JitJmpBuf, frame: *mut std::ffi::c_void) -> i32;
485 #[link_name = "longjmp"]
486 fn _longjmp(env: *mut JitJmpBuf, val: i32) -> !;
487}
488
489use std::cell::{Cell, RefCell};
490thread_local! {
491 static JIT_JMP_BUF: Cell<Option<*mut JitJmpBuf>> = const { Cell::new(None) };
498 static JIT_VIOLATION_MSG: RefCell<Option<String>> = const { RefCell::new(None) };
502}
503
504fn jit_violation_longjmp(msg: String) -> ! {
511 JIT_VIOLATION_MSG.with(|m| *m.borrow_mut() = Some(msg.clone()));
512 let buf_ptr: Option<*mut JitJmpBuf> = JIT_JMP_BUF.with(|b| b.get());
513 match buf_ptr {
514 Some(ptr) => unsafe { _longjmp(ptr, 1) },
515 None => {
516 let mut err = std::io::stderr().lock();
517 use std::io::Write;
518 let _ = writeln!(err, "{msg}");
519 let _ = err.flush();
520 std::process::abort();
521 }
522 }
523}
524
525struct JmpBufGuard {
533 prev: Option<*mut JitJmpBuf>,
534}
535
536impl Drop for JmpBufGuard {
537 fn drop(&mut self) {
538 JIT_JMP_BUF.with(|b| b.set(self.prev));
539 }
540}
541
542pub(crate) fn invoke_with_catch<F: FnOnce()>(f: F) {
553 use std::mem::MaybeUninit;
554 let mut buf: MaybeUninit<JitJmpBuf> = MaybeUninit::uninit();
555 let buf_ptr = buf.as_mut_ptr();
556 let prev: Option<*mut JitJmpBuf> = JIT_JMP_BUF.with(|b| b.replace(Some(buf_ptr)));
561 let _guard = JmpBufGuard { prev };
562 #[cfg(not(windows))]
563 let jmpval = unsafe { _setjmp(buf_ptr) };
564 #[cfg(windows)]
566 let jmpval = unsafe { _setjmp(buf_ptr, std::ptr::null_mut()) };
567 if jmpval == 0 {
568 f();
569 } else {
570 let msg = JIT_VIOLATION_MSG
575 .with(|m| m.borrow_mut().take())
576 .unwrap_or_else(|| "JIT predicate violation (no message)".into());
577 std::panic::resume_unwind(Box::new(msg));
581 }
582}
583
584extern "C" fn jit_is_positive_fail(value: u64, name_ptr: u64, name_len: u64) -> u64 {
588 let name = if name_ptr != 0 {
593 unsafe {
594 std::str::from_utf8_unchecked(std::slice::from_raw_parts(
595 name_ptr as *const u8,
596 name_len as usize,
597 ))
598 }
599 } else {
600 "value"
601 };
602 jit_violation_longjmp(format!(
603 "is_positive({name}): value must be > 0, got {value}"
604 ));
605}
606
607extern "C" fn jit_in_range_fail(value: u64, lo: u64, hi: u64) -> u64 {
610 jit_violation_longjmp(format!("in_range: value {value} outside [{lo}, {hi}]"));
611}
612
613extern "C" fn jit_div_zero_fail(kind: u64) -> u64 {
617 jit_violation_longjmp(
618 if kind == 0 {
619 "attempt to divide by zero"
620 } else {
621 "attempt to calculate the remainder with a divisor of zero"
622 }
623 .to_string(),
624 );
625}
626
627extern "C" fn jit_f64_mod(a_bits: u64, b_bits: u64) -> u64 {
631 let (a, b) = (f64::from_bits(a_bits), f64::from_bits(b_bits));
632 (if b != 0.0 { a % b } else { 0.0 }).to_bits()
633}
634
635extern "C" fn jit_is_one_of_fail(value: u64, set_ptr: u64, set_len: u64) -> u64 {
643 let msg = if set_ptr != 0 {
644 let set = unsafe { std::slice::from_raw_parts(set_ptr as *const u64, set_len as usize) };
645 format!("is_one_of: value {value} not in allowed set {set:?}")
646 } else {
647 format!("is_one_of: value {value} not in allowed set [..]")
648 };
649 jit_violation_longjmp(msg);
650}
651
652extern "C" fn jit_weighted_pick(
657 input: u64,
658 values_ptr: u64,
659 biases_ptr: u64,
660 primaries_ptr: u64,
661 aliases_ptr: u64,
662 n: u64,
663) -> u64 {
664 guarded(|| {
665 let n = n as usize;
666 let slot = (input as usize) % n;
667 let bias_test = ((input >> 32) as f64) / (u32::MAX as f64);
668 unsafe {
669 let biases = std::slice::from_raw_parts(biases_ptr as *const f64, n);
670 let primaries = std::slice::from_raw_parts(primaries_ptr as *const u64, n);
671 let aliases = std::slice::from_raw_parts(aliases_ptr as *const u64, n);
672 let values = std::slice::from_raw_parts(values_ptr as *const u64, n);
673 let index = if bias_test < biases[slot] {
674 primaries[slot]
675 } else {
676 aliases[slot]
677 };
678 values[index as usize]
679 }
680 })
681}
682
683fn guarded<T>(body: impl FnOnce() -> T) -> T {
688 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(body)) {
689 Ok(v) => v,
690 Err(payload) => {
691 let msg = payload
692 .downcast_ref::<String>()
693 .cloned()
694 .or_else(|| payload.downcast_ref::<&str>().map(|s| s.to_string()))
695 .unwrap_or_else(|| "panic in a compiled helper".to_string());
696 jit_violation_longjmp(msg)
697 }
698 }
699}
700
701#[derive(Clone)]
704pub struct SlotKitRef(pub std::sync::Arc<crate::ast::CompiledSlotKit>);
705
706impl SlotKitRef {
707 fn new(kit: crate::ast::CompiledSlotKit) -> Self {
708 SlotKitRef(std::sync::Arc::new(kit))
709 }
710}
711
712impl std::fmt::Debug for SlotKitRef {
713 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
714 write!(
715 f,
716 "SlotKitRef({:p}, {} scratch)",
717 std::sync::Arc::as_ptr(&self.0),
718 self.0.scratch.len()
719 )
720 }
721}
722
723impl PartialEq for SlotKitRef {
724 fn eq(&self, other: &Self) -> bool {
725 std::sync::Arc::ptr_eq(&self.0, &other.0)
726 }
727}
728
729extern "C" fn jit_slot_call(
741 kit: *const crate::ast::CompiledSlotKit,
742 inputs: *const u64,
743 n_in: u64,
744 outputs: *mut u64,
745 n_out: u64,
746 scratch: *mut crate::ast::ScratchBuf,
747 base: u64,
748 n_scratch: u64,
749) {
750 guarded(|| unsafe {
751 let kit = &*kit;
752 let ins = std::slice::from_raw_parts(inputs, n_in as usize);
753 let outs = std::slice::from_raw_parts_mut(outputs, n_out as usize);
754 let sc = std::slice::from_raw_parts_mut(scratch.add(base as usize), n_scratch as usize);
755 (kit.op)(ins, outs, sc)
756 })
757}
758
759impl JitOp {
760 pub(crate) fn slot_kit(&self) -> Option<&SlotKitRef> {
762 match self {
763 JitOp::SlotCall { kit, .. } => Some(kit),
764 _ => None,
765 }
766 }
767
768 pub(crate) fn scratch_elems(&self) -> &[crate::ast::ScratchElem] {
770 const STR_ENTRY: [crate::ast::ScratchElem; 1] = [crate::ast::ScratchElem::Str];
771 const F32_ENTRY: [crate::ast::ScratchElem; 1] = [crate::ast::ScratchElem::F32];
772 match self {
773 JitOp::SlotCall { kit, .. } => &kit.0.scratch,
774 JitOp::U64ToStr { .. }
775 | JitOp::I64ToStr { .. }
776 | JitOp::F64ToStr { .. }
777 | JitOp::StrConcat { .. }
778 | JitOp::JsonToStr { .. } => &STR_ENTRY,
779 JitOp::VecProduce { .. } => &F32_ENTRY,
780 _ => &[],
781 }
782 }
783
784 pub(crate) fn place_scratch(&mut self, base: usize) {
787 match self {
788 JitOp::SlotCall { scratch_base, .. }
789 | JitOp::U64ToStr { scratch_base }
790 | JitOp::I64ToStr { scratch_base }
791 | JitOp::F64ToStr { scratch_base }
792 | JitOp::StrConcat { scratch_base }
793 | JitOp::JsonToStr { scratch_base }
794 | JitOp::VecProduce { scratch_base, .. } => *scratch_base = base,
795 _ => {}
796 }
797 }
798}
799
800#[derive(Debug, Clone, Copy, PartialEq, Eq)]
804pub enum VecProducer {
805 Add,
807 Scale,
809 Norm,
811 HashVec,
813 XxHash3Vec,
815 RegToVec,
817}
818
819#[derive(Debug, Clone, Copy, PartialEq, Eq)]
822pub enum VecReducer {
823 Dot,
825 L2,
827 Cosine,
829 LidMle,
831}
832
833#[derive(Debug, Clone, Copy, PartialEq, Eq)]
836pub enum RegLaneRead {
837 F32,
839 I16,
841 I64,
843}
844
845#[derive(Debug, Clone, Copy, PartialEq, Eq)]
849pub enum RegProducer {
850 WithLaneF32,
852 GatherF32,
854 VecToRegF32,
856 MulI8,
858}
859
860unsafe fn vec_f32_of<'a>(ptr: u64, len: u64) -> &'a [f32] {
866 if len == 0 {
867 &[]
868 } else {
869 unsafe { std::slice::from_raw_parts(ptr as usize as *const f32, len as usize) }
870 }
871}
872
873unsafe fn write_f32_entry(
880 scratch: *mut crate::ast::ScratchBuf,
881 base: u64,
882 buffer: *mut u64,
883 out_slot: u64,
884 f: impl FnOnce(&mut Vec<f32>),
885) {
886 unsafe {
887 let entry = &mut *scratch.add(base as usize);
888 let crate::ast::ScratchBuf::F32(v) = entry else {
889 panic!("a vector lowering's scratch entry is not an f32 vector");
890 };
891 f(v);
892 *buffer.add(out_slot as usize) = v.as_ptr() as usize as u64;
893 *buffer.add(out_slot as usize + 1) = v.len() as u64;
894 }
895}
896
897macro_rules! vec_producer {
901 ($name:ident, |$out:ident, $w0:ident, $w1:ident, $w2:ident, $w3:ident| $body:expr) => {
902 extern "C" fn $name(
903 scratch: *mut crate::ast::ScratchBuf,
904 base: u64,
905 buffer: *mut u64,
906 out_slot: u64,
907 $w0: u64,
908 $w1: u64,
909 $w2: u64,
910 $w3: u64,
911 ) {
912 guarded(|| unsafe {
913 let _ = ($w2, $w3);
914 write_f32_entry(scratch, base, buffer, out_slot, |$out| $body)
915 })
916 }
917 };
918}
919
920vec_producer!(jit_vec_add, |out, a_ptr, a_len, b_ptr, b_len| {
921 let (a, b) = (vec_f32_of(a_ptr, a_len), vec_f32_of(b_ptr, b_len));
922 crate::numeric::vector::check_lens("vec_add", a.len(), b.len());
923 crate::numeric::vector::add_f32_into(a, b, out)
924});
925vec_producer!(jit_vec_scale, |out, a_ptr, a_len, k_bits, _z| {
926 let a = vec_f32_of(a_ptr, a_len);
927 crate::numeric::vector::scale_f32_into(a, f64::from_bits(k_bits) as f32, out)
928});
929vec_producer!(jit_vec_norm, |out, a_ptr, a_len, _y, _z| {
930 crate::numeric::vector::norm_f32_into(vec_f32_of(a_ptr, a_len), out)
931});
932vec_producer!(jit_hash_vec, |out, seed, dim, _y, _z| {
933 crate::numeric::vector::hash_vec_into(seed, dim, out)
934});
935vec_producer!(jit_xxhash3_vec, |out, seed, dim, _y, _z| {
936 crate::numeric::vector::xxhash3_vec_into(seed, dim, out)
937});
938vec_producer!(jit_reg_to_vec_f32, |out, lo, hi, _y, _z| {
939 out.clear();
940 out.extend_from_slice(&crate::ast::Bits128([lo, hi]).lanes_f32())
941});
942
943macro_rules! vec_reducer {
946 ($name:ident, |$w0:ident, $w1:ident, $w2:ident, $w3:ident| $body:expr) => {
947 extern "C" fn $name($w0: u64, $w1: u64, $w2: u64, $w3: u64) -> u64 {
948 guarded(|| unsafe {
949 let _ = ($w2, $w3);
950 let r: f64 = $body;
951 r.to_bits()
952 })
953 }
954 };
955}
956
957vec_reducer!(jit_vec_dot, |a_ptr, a_len, b_ptr, b_len| {
958 let (a, b) = (vec_f32_of(a_ptr, a_len), vec_f32_of(b_ptr, b_len));
959 crate::numeric::vector::check_lens("vec_dot", a.len(), b.len());
960 crate::numeric::vector::dot_f32(a, b) as f64
961});
962vec_reducer!(jit_vec_l2, |a_ptr, a_len, b_ptr, b_len| {
963 let (a, b) = (vec_f32_of(a_ptr, a_len), vec_f32_of(b_ptr, b_len));
964 crate::numeric::vector::check_lens("vec_l2", a.len(), b.len());
965 (crate::numeric::vector::l2sq_f32(a, b) as f64).sqrt()
966});
967vec_reducer!(jit_vec_cosine, |a_ptr, a_len, b_ptr, b_len| {
968 let (a, b) = (vec_f32_of(a_ptr, a_len), vec_f32_of(b_ptr, b_len));
969 crate::numeric::vector::check_lens("vec_cosine", a.len(), b.len());
970 crate::numeric::vector::cosine_f32(a, b)
971});
972vec_reducer!(jit_lid_mle, |d_ptr, d_len, k_bits, _z| {
973 crate::numeric::vector::lid_mle_of(vec_f32_of(d_ptr, d_len), f64::from_bits(k_bits))
974});
975
976extern "C" fn jit_reg_lane_f32(lo: u64, hi: u64, i: u64) -> u64 {
978 guarded(|| crate::numeric::register::lane_f32(crate::ast::Bits128([lo, hi]), i).to_bits())
979}
980
981extern "C" fn jit_reg_lane_i16(lo: u64, hi: u64, i: u64) -> u64 {
983 guarded(|| crate::numeric::register::lane_i16(crate::ast::Bits128([lo, hi]), i) as i64 as u64)
984}
985
986extern "C" fn jit_reg_lane_i64(lo: u64, hi: u64, i: u64) -> u64 {
988 guarded(|| crate::numeric::register::lane_i64(crate::ast::Bits128([lo, hi]), i) as u64)
989}
990
991macro_rules! reg_producer {
994 ($name:ident, |$w0:ident, $w1:ident, $w2:ident, $w3:ident| $body:expr) => {
995 extern "C" fn $name(
996 buffer: *mut u64,
997 out_slot: u64,
998 $w0: u64,
999 $w1: u64,
1000 $w2: u64,
1001 $w3: u64,
1002 ) {
1003 guarded(|| unsafe {
1004 let _ = ($w2, $w3);
1005 let r: crate::ast::Bits128 = $body;
1006 *buffer.add(out_slot as usize) = r.0[0];
1007 *buffer.add(out_slot as usize + 1) = r.0[1];
1008 })
1009 }
1010 };
1011}
1012
1013reg_producer!(jit_reg_with_lane_f32, |lo, hi, i, v_bits| {
1014 crate::numeric::register::with_lane_f32(
1015 crate::ast::Bits128([lo, hi]),
1016 i,
1017 f64::from_bits(v_bits),
1018 )
1019});
1020reg_producer!(jit_reg_gather_f32, |v_ptr, v_len, offset, _z| {
1021 crate::numeric::register::gather_f32(vec_f32_of(v_ptr, v_len), offset)
1022});
1023reg_producer!(jit_vec_to_reg_f32, |v_ptr, v_len, _y, _z| {
1024 crate::numeric::register::to_reg_f32(vec_f32_of(v_ptr, v_len))
1025});
1026reg_producer!(jit_reg_mul_i8, |a_lo, a_hi, b_lo, b_hi| {
1027 crate::numeric::register::mul_i8(
1028 crate::ast::Bits128([a_lo, a_hi]),
1029 crate::ast::Bits128([b_lo, b_hi]),
1030 )
1031});
1032
1033unsafe fn write_str_entry(
1042 scratch: *mut crate::ast::ScratchBuf,
1043 base: u64,
1044 buffer: *mut u64,
1045 out_slot: u64,
1046 f: impl FnOnce(&mut Vec<u8>),
1047) {
1048 unsafe {
1049 let entry = &mut *scratch.add(base as usize);
1050 let crate::ast::ScratchBuf::Str(v) = entry else {
1051 panic!("a string lowering's scratch entry is not a string");
1052 };
1053 v.clear();
1054 f(v);
1055 *buffer.add(out_slot as usize) = v.as_ptr() as usize as u64;
1056 *buffer.add(out_slot as usize + 1) = v.len() as u64;
1057 }
1058}
1059
1060extern "C" fn jit_u64_to_str(
1062 scratch: *mut crate::ast::ScratchBuf,
1063 base: u64,
1064 buffer: *mut u64,
1065 out_slot: u64,
1066 value: u64,
1067) {
1068 use std::io::Write;
1069 guarded(|| unsafe {
1070 write_str_entry(scratch, base, buffer, out_slot, |v| {
1071 write!(v, "{value}").expect("a vector accepts every write")
1072 })
1073 })
1074}
1075
1076extern "C" fn jit_i64_to_str(
1078 scratch: *mut crate::ast::ScratchBuf,
1079 base: u64,
1080 buffer: *mut u64,
1081 out_slot: u64,
1082 value: u64,
1083) {
1084 use std::io::Write;
1085 guarded(|| unsafe {
1086 write_str_entry(scratch, base, buffer, out_slot, |v| {
1087 write!(v, "{}", value as i64).expect("a vector accepts every write")
1088 })
1089 })
1090}
1091
1092extern "C" fn jit_f64_to_str(
1095 scratch: *mut crate::ast::ScratchBuf,
1096 base: u64,
1097 buffer: *mut u64,
1098 out_slot: u64,
1099 bits: u64,
1100) {
1101 use std::io::Write;
1102 guarded(|| unsafe {
1103 write_str_entry(scratch, base, buffer, out_slot, |v| {
1104 write!(v, "{}", f64::from_bits(bits)).expect("a vector accepts every write")
1105 })
1106 })
1107}
1108
1109extern "C" fn jit_str_concat(
1113 scratch: *mut crate::ast::ScratchBuf,
1114 base: u64,
1115 buffer: *mut u64,
1116 out_slot: u64,
1117 pairs: *const u64,
1118 n: u64,
1119) {
1120 guarded(|| unsafe {
1121 let words = std::slice::from_raw_parts(pairs, 2 * n as usize);
1122 write_str_entry(scratch, base, buffer, out_slot, |v| {
1123 for pair in words.as_chunks::<2>().0 {
1124 let bytes =
1128 std::slice::from_raw_parts(pair[0] as usize as *const u8, pair[1] as usize);
1129 v.extend_from_slice(bytes);
1130 }
1131 })
1132 })
1133}
1134
1135extern "C" fn jit_json_to_str(
1138 scratch: *mut crate::ast::ScratchBuf,
1139 base: u64,
1140 buffer: *mut u64,
1141 out_slot: u64,
1142 ptr: u64,
1143 len: u64,
1144) {
1145 guarded(|| unsafe {
1146 let pair = [ptr, len];
1147 let value = crate::derive_support::ref_value(&pair);
1148 let json = match value {
1149 crate::ast::Value::Json(j) => j.as_ref(),
1150 other => panic!("expected Json wire, got {other:?}"),
1151 };
1152 write_str_entry(scratch, base, buffer, out_slot, |v| {
1153 serde_json::to_writer(v, json).expect("a vector accepts every write")
1154 })
1155 })
1156}
1157
1158pub fn classify_node_typed(node: &dyn PolydatNode, wire_types: &[crate::ast::PortType]) -> JitOp {
1167 use crate::ast::PortType as PT;
1168 let is_ref = |t: &crate::ast::PortType| t.slot_color() == crate::ast::SlotColor::Ref2;
1169 let vec_produce = |kind: VecProducer| JitOp::VecProduce {
1170 kind,
1171 scratch_base: 0,
1172 };
1173 let ref_copy = |ty: crate::ast::PortType| {
1174 crate::compile::assembly::ref_copy_kit(ty)
1175 .map(|kit| JitOp::SlotCall {
1176 kit: SlotKitRef::new(kit),
1177 scratch_base: 0,
1178 })
1179 .unwrap_or(JitOp::Fallback)
1180 };
1181 let meta = node.meta();
1182 let named = match meta.name.as_str() {
1183 "__u64_to_string" => JitOp::U64ToStr { scratch_base: 0 },
1186 "__i64_to_string" => JitOp::I64ToStr { scratch_base: 0 },
1187 "__f64_to_string" => JitOp::F64ToStr { scratch_base: 0 },
1188 "json_to_str" if wire_types == [crate::ast::PortType::Json] => {
1189 JitOp::JsonToStr { scratch_base: 0 }
1190 }
1191 "str_concat"
1192 if !wire_types.is_empty()
1193 && wire_types.iter().all(|t| *t == crate::ast::PortType::Str) =>
1194 {
1195 JitOp::StrConcat { scratch_base: 0 }
1196 }
1197 "vec_add" if wire_types == [PT::VecF32, PT::VecF32] => vec_produce(VecProducer::Add),
1202 "vec_scale" if wire_types == [PT::VecF32, PT::F64] => vec_produce(VecProducer::Scale),
1203 "vec_norm" if wire_types == [PT::VecF32] => vec_produce(VecProducer::Norm),
1204 "hash_vec" if wire_types == [PT::U64, PT::U64] => vec_produce(VecProducer::HashVec),
1205 "xxhash3_vec" if wire_types == [PT::U64, PT::U64] => vec_produce(VecProducer::XxHash3Vec),
1206 "reg_to_vec_f32" if wire_types == [PT::RegF32x4] => vec_produce(VecProducer::RegToVec),
1207 "vec_dot" if wire_types == [PT::VecF32, PT::VecF32] => JitOp::VecReduce(VecReducer::Dot),
1208 "vec_l2" if wire_types == [PT::VecF32, PT::VecF32] => JitOp::VecReduce(VecReducer::L2),
1209 "vec_cosine" if wire_types == [PT::VecF32, PT::VecF32] => {
1210 JitOp::VecReduce(VecReducer::Cosine)
1211 }
1212 "lid_mle" if wire_types == [PT::VecF32, PT::F64] => JitOp::VecReduce(VecReducer::LidMle),
1213 "reg_lane_f32" if wire_types == [PT::RegF32x4, PT::U64] => JitOp::RegLane(RegLaneRead::F32),
1217 "reg_lane_i16" if wire_types == [PT::RegI16x8, PT::U64] => JitOp::RegLane(RegLaneRead::I16),
1218 "reg_lane_i64" if wire_types == [PT::RegI64x2, PT::U64] => JitOp::RegLane(RegLaneRead::I64),
1219 "reg_with_lane_f32" if wire_types == [PT::RegF32x4, PT::U64, PT::F64] => {
1220 JitOp::RegProduce(RegProducer::WithLaneF32)
1221 }
1222 "reg_gather_f32" if wire_types == [PT::VecF32, PT::U64] => {
1223 JitOp::RegProduce(RegProducer::GatherF32)
1224 }
1225 "vec_to_reg_f32" if wire_types == [PT::VecF32] => {
1226 JitOp::RegProduce(RegProducer::VecToRegF32)
1227 }
1228 "reg_mul_i8" if wire_types == [PT::RegI8x16, PT::RegI8x16] => {
1229 JitOp::RegProduce(RegProducer::MulI8)
1230 }
1231 "reg_dot_f32" if wire_types == [PT::RegF32x4, PT::RegF32x4] => JitOp::RegDotF32,
1232 n if n.starts_with("__port_") || n == "default_or" => match meta.outs.first() {
1238 Some(o) if is_ref(&o.typ) => return ref_copy(o.typ),
1239 _ => JitOp::Identity,
1240 },
1241 "select" | "select_u64" if wire_types.iter().skip(1).any(|t| t.slot_width() != 1) => {
1244 JitOp::Fallback
1245 }
1246 _ if wire_types.iter().any(is_ref) || meta.outs.iter().any(|o| is_ref(&o.typ)) => {
1247 JitOp::Fallback
1248 }
1249 _ => classify_node(node),
1250 };
1251 if !matches!(named, JitOp::Fallback) {
1252 return named;
1253 }
1254 if let Some(kit) = node.compiled_slot(wire_types) {
1255 return JitOp::SlotCall {
1256 kit: SlotKitRef::new(kit),
1257 scratch_base: 0,
1258 };
1259 }
1260 if let Some(op) = node.compiled_u64() {
1261 return JitOp::SlotCall {
1262 kit: SlotKitRef::new(crate::ast::CompiledSlotKit {
1263 scratch: Vec::new(),
1264 op: Box::new(move |inputs, outputs, _| op(inputs, outputs)),
1265 }),
1266 scratch_base: 0,
1267 };
1268 }
1269 JitOp::Fallback
1270}
1271
1272#[derive(Debug, Clone, PartialEq)]
1279pub enum JitOp {
1280 Identity,
1283 AddConst(u64),
1285 MulConst(u64),
1287 DivConst(u64),
1289 ModConst(u64),
1291 ClampConst(u64, u64),
1293 Interleave,
1295 MixedRadixConst(Vec<u64>),
1297 Hash,
1299 SplitMix64,
1301 Popcnt,
1303 Clz,
1305 Ctz,
1307 Bswap,
1309 ShuffleConst(u64, u64, u64),
1311
1312 UnitInterval,
1315 F64ToU64,
1317 RoundToU64,
1320 FloorToU64,
1322 CeilToU64,
1324 ClampF64Const(u64, u64), LerpConst(u64, u64), ScaleRangeConst(u64, u64), QuantizeConst(u64), DiscretizeConst(u64, u64), LutSampleConst(u64, u64), WeightedPickConst(u64, u64, u64, u64, u64), MathUnary(u8),
1346 MathBinary(u8),
1349
1350 U64Add2,
1353 U64Sub2,
1355 U64Mul2,
1357 U64Div2,
1359 U64Mod2,
1361 U64And,
1363 U64Or,
1365 U64Xor,
1367 U64Shl,
1369 U64Shr,
1371 U64Not,
1373
1374 ToF64,
1377
1378 F64Add,
1380 F64Sub,
1382 F64Mul,
1384 F64Div,
1386 F64Mod,
1388 U64DivWire,
1391 U64ModWire,
1394
1395 SlotCall {
1403 kit: SlotKitRef,
1406 scratch_base: usize,
1409 },
1410
1411 U64ToStr {
1417 scratch_base: usize,
1419 },
1420 I64ToStr {
1422 scratch_base: usize,
1424 },
1425 F64ToStr {
1427 scratch_base: usize,
1429 },
1430 StrConcat {
1433 scratch_base: usize,
1435 },
1436 JsonToStr {
1438 scratch_base: usize,
1440 },
1441
1442 VecProduce {
1446 kind: VecProducer,
1448 scratch_base: usize,
1450 },
1451 VecReduce(VecReducer),
1453 RegLane(RegLaneRead),
1456 RegProduce(RegProducer),
1458 RegDotF32,
1462 RegShuffleConst([u8; 16]),
1465
1466 IsPositiveCheck {
1473 name_ptr: u64,
1475 name_len: u64,
1477 },
1478 InRangeCheck(u64, u64),
1482 IsOneOfCheck {
1490 allowed: Vec<u64>,
1492 set_ptr: u64,
1494 set_len: u64,
1496 },
1497
1498 RegBinOp(u8, u8),
1506 RegCopy,
1510 RegSplat(u8),
1514
1515 U64Cmp(ir::condcodes::IntCC),
1518 F64Cmp(ir::condcodes::FloatCC),
1520 SelectU64,
1522 SelectF64,
1524
1525 I64ToF64,
1528 F64ToI64,
1530 SignExtendI32,
1532 SignExtendI16,
1534 SignExtendI8,
1536 ZeroExtendU32,
1538 ZeroExtendU16,
1540 ZeroExtendU8,
1542 ToBool,
1544 ConstU64(u64),
1546 ConstF64(u64),
1548
1549 HashRangeConst(u64),
1552 HashIntervalConst(u64, u64),
1554 InvLerpConst(u64, u64),
1556 RemapConst(u64, u64, u64, u64),
1558
1559 EpochOffsetConst(u64),
1562 EpochScaleConst(u64),
1564 ThreadId,
1566 CurrentEpochMillis,
1568
1569 Perlin1dConst(u64, u64),
1572 Perlin2dConst(u64, u64),
1574 Simplex2dConst(u64, u64),
1576 FractalNoise1dConst(u64, u64, u64),
1578 FractalNoise2dConst(u64, u64, u64),
1580
1581 VariadicSum,
1584 VariadicProduct,
1586 VariadicMin,
1588 VariadicMax,
1590 CheckedAdd,
1592 CheckedSub,
1594 CheckedMul,
1596 CeilToMultiple,
1598 MultiplesAtLeast,
1600
1601 FairCoin,
1604 BlendConst(u64),
1606 LfsrStepConst(u64),
1609 PcgConst(u64, u64),
1611 PcgStreamConst(u64),
1613 CycleWalkConst(u64, u64, u64),
1615 UnfairCoinConst(u64),
1617 CoinFlipConst(u64),
1620 ChanceConst(u64),
1622 NOfConst(u64, u64),
1624
1625 Fallback,
1628}
1629
1630pub fn classify_node(node: &dyn PolydatNode) -> JitOp {
1637 let name = node.meta().name.as_str();
1638 let consts = node.jit_constants();
1639
1640 match name {
1641 "identity" => JitOp::Identity,
1642 "hash" | "splitmix64" | "scatter" => JitOp::SplitMix64,
1643 "fair_coin" => JitOp::FairCoin,
1644 "unfair_coin" | "bernoulli" => {
1645 if let Some(&p) = consts.first() {
1646 JitOp::UnfairCoinConst(p)
1647 } else {
1648 JitOp::Fallback
1649 }
1650 }
1651 "chance" => {
1652 if let Some(&p) = consts.first() {
1653 JitOp::ChanceConst(p)
1654 } else {
1655 JitOp::Fallback
1656 }
1657 }
1658 "popcnt" | "count_ones" | "popcount" => JitOp::Popcnt,
1659 "clz" | "leading_zeros" => JitOp::Clz,
1660 "ctz" | "trailing_zeros" => JitOp::Ctz,
1661 "bswap" | "swap_bytes" => JitOp::Bswap,
1662 "xxhash3" | "xxh3" => JitOp::Hash,
1663 "hash_range" => {
1664 if let Some(&c) = consts.first() {
1665 JitOp::HashRangeConst(c)
1666 } else {
1667 JitOp::Fallback
1668 }
1669 }
1670 "hash_interval" => {
1671 if consts.len() >= 2 {
1672 JitOp::HashIntervalConst(consts[0], consts[1])
1673 } else {
1674 JitOp::Fallback
1675 }
1676 }
1677 "add" => {
1678 if let Some(&c) = consts.first() {
1679 JitOp::AddConst(c)
1680 } else {
1681 JitOp::Fallback
1682 }
1683 }
1684 "mul" => {
1685 if let Some(&c) = consts.first() {
1686 JitOp::MulConst(c)
1687 } else {
1688 JitOp::Fallback
1689 }
1690 }
1691 "div" => {
1692 if let Some(&c) = consts.first() {
1693 JitOp::DivConst(c)
1694 } else {
1695 JitOp::Fallback
1696 }
1697 }
1698 "mod" => {
1699 if let Some(&c) = consts.first() {
1700 JitOp::ModConst(c)
1701 } else {
1702 JitOp::Fallback
1703 }
1704 }
1705 "clamp" => {
1706 if consts.len() >= 2 {
1707 JitOp::ClampConst(consts[0], consts[1])
1708 } else {
1709 JitOp::Fallback
1710 }
1711 }
1712 "interleave" => JitOp::Interleave,
1713 "mixed_radix" => {
1714 if consts.is_empty() {
1715 JitOp::Fallback
1716 } else {
1717 JitOp::MixedRadixConst(consts)
1718 }
1719 }
1720 "shuffle" => {
1721 if consts.len() >= 3 {
1722 JitOp::ShuffleConst(consts[0], consts[1], consts[2])
1723 } else {
1724 JitOp::Fallback
1725 }
1726 }
1727 "unit_interval" => JitOp::UnitInterval,
1729 "f64_to_u64" => JitOp::F64ToU64,
1730 "round_to_u64" => JitOp::RoundToU64,
1731 "floor_to_u64" => JitOp::FloorToU64,
1732 "ceil_to_u64" => JitOp::CeilToU64,
1733 "clamp_f64" => {
1734 if consts.len() >= 2 {
1735 JitOp::ClampF64Const(consts[0], consts[1])
1736 } else {
1737 JitOp::Fallback
1738 }
1739 }
1740 "lerp" => {
1741 if consts.len() >= 2 {
1742 JitOp::LerpConst(consts[0], consts[1])
1743 } else {
1744 JitOp::Fallback
1745 }
1746 }
1747 "scale_range" => {
1748 if consts.len() >= 2 {
1749 JitOp::ScaleRangeConst(consts[0], consts[1])
1750 } else {
1751 JitOp::Fallback
1752 }
1753 }
1754 "quantize" => {
1755 if let Some(&c) = consts.first() {
1756 JitOp::QuantizeConst(c)
1757 } else {
1758 JitOp::Fallback
1759 }
1760 }
1761 "discretize" => {
1762 if consts.len() >= 2 {
1763 JitOp::DiscretizeConst(consts[0], consts[1])
1764 } else {
1765 JitOp::Fallback
1766 }
1767 }
1768 "lut_sample" | "dist_normal" | "icd_normal" | "dist_exponential" | "icd_exponential"
1769 | "dist_uniform" | "dist_pareto" | "dist_zipf" | "dist_empirical" => {
1770 if consts.len() >= 2 {
1771 JitOp::LutSampleConst(consts[0], consts[1])
1772 } else {
1773 JitOp::Fallback
1774 }
1775 }
1776 "sin" => JitOp::MathUnary(0),
1778 "cos" => JitOp::MathUnary(1),
1779 "tan" => JitOp::MathUnary(2),
1780 "asin" => JitOp::MathUnary(3),
1781 "acos" => JitOp::MathUnary(4),
1782 "atan" => JitOp::MathUnary(5),
1783 "sqrt" => JitOp::MathUnary(6),
1784 "abs_f64" => JitOp::MathUnary(7),
1785 "ln" => JitOp::MathUnary(8),
1786 "exp" => JitOp::MathUnary(9),
1787 "floor_base10" => JitOp::MathUnary(10),
1788 "ceiling_base10" => JitOp::MathUnary(11),
1789 "closest_base10" => JitOp::MathUnary(12),
1790 "floor_decade" => JitOp::MathUnary(13),
1791 "ceiling_decade" => JitOp::MathUnary(14),
1792 "closest_decade" => JitOp::MathUnary(15),
1793 "floor_binomial" => JitOp::MathUnary(16),
1794 "ceiling_binomial" => JitOp::MathUnary(17),
1795 "closest_binomial" => JitOp::MathUnary(18),
1796 "floor_fibonacci" => JitOp::MathUnary(19),
1797 "ceiling_fibonacci" => JitOp::MathUnary(20),
1798 "closest_fibonacci" => JitOp::MathUnary(21),
1799 "atan2" => JitOp::MathBinary(0),
1800 "pow" => JitOp::MathBinary(1),
1801 "round_nearest" => JitOp::MathBinary(2),
1802 "round_floor" => JitOp::MathBinary(3),
1803 "round_ceiling" => JitOp::MathBinary(4),
1804 "to_f64" => JitOp::ToF64,
1805 "u64_add" => JitOp::U64Add2,
1807 "u64_sub" => JitOp::U64Sub2,
1808 "u64_mul" => JitOp::U64Mul2,
1809 "u64_div" => JitOp::U64Div2,
1810 "u64_mod" => JitOp::U64Mod2,
1811 "u64_and" => JitOp::U64And,
1812 "u64_or" => JitOp::U64Or,
1813 "u64_xor" => JitOp::U64Xor,
1814 "u64_shl" => JitOp::U64Shl,
1815 "u64_shr" => JitOp::U64Shr,
1816 "u64_not" => JitOp::U64Not,
1817
1818 "reg_add_i8" => JitOp::RegBinOp(0, 0),
1820 "reg_sub_i8" => JitOp::RegBinOp(0, 1),
1821 "reg_shuffle_bytes" => {
1826 let mut mask = [0u8; 16];
1827 if consts.len() == 16 && consts.iter().all(|&m| m < 16) {
1828 for (m, &c) in mask.iter_mut().zip(consts.iter()) {
1829 *m = c as u8;
1830 }
1831 JitOp::RegShuffleConst(mask)
1832 } else {
1833 JitOp::Fallback
1834 }
1835 }
1836 "reg_add_i16" => JitOp::RegBinOp(1, 0),
1837 "reg_sub_i16" => JitOp::RegBinOp(1, 1),
1838 "reg_mul_i16" => JitOp::RegBinOp(1, 2),
1839 "reg_add_i32" => JitOp::RegBinOp(2, 0),
1840 "reg_sub_i32" => JitOp::RegBinOp(2, 1),
1841 "reg_mul_i32" => JitOp::RegBinOp(2, 2),
1842 "reg_add_i64" => JitOp::RegBinOp(3, 0),
1843 "reg_sub_i64" => JitOp::RegBinOp(3, 1),
1844 "reg_mul_i64" => JitOp::RegBinOp(3, 2),
1845 "reg_add_f32" => JitOp::RegBinOp(4, 0),
1846 "reg_sub_f32" => JitOp::RegBinOp(4, 1),
1847 "reg_mul_f32" => JitOp::RegBinOp(4, 2),
1848 "reg_add_f64" => JitOp::RegBinOp(5, 0),
1849 "reg_sub_f64" => JitOp::RegBinOp(5, 1),
1850 "reg_mul_f64" => JitOp::RegBinOp(5, 2),
1851 "__reg_view_raw" | "__reg_view_i8x16" | "__reg_view_i16x8" | "__reg_view_i32x4"
1852 | "__reg_view_i64x2" | "__reg_view_f16x8" | "__reg_view_f32x4" | "__reg_view_f64x2" => {
1853 JitOp::RegCopy
1854 }
1855 "reg_splat_i8" => JitOp::RegSplat(0),
1856 "reg_splat_i16" => JitOp::RegSplat(1),
1857 "reg_splat_i32" => JitOp::RegSplat(2),
1858 "reg_splat_i64" => JitOp::RegSplat(3),
1859 "reg_splat_f32" => JitOp::RegSplat(4),
1860 "reg_splat_f64" => JitOp::RegSplat(5),
1861
1862 "f64_add" => JitOp::F64Add,
1863 "f64_sub" => JitOp::F64Sub,
1864 "f64_mul" => JitOp::F64Mul,
1865 "f64_div" => JitOp::F64Div,
1866 "f64_mod" => JitOp::F64Mod,
1867
1868 "u64_eq" => JitOp::U64Cmp(ir::condcodes::IntCC::Equal),
1870 "u64_ne" => JitOp::U64Cmp(ir::condcodes::IntCC::NotEqual),
1871 "u64_lt" => JitOp::U64Cmp(ir::condcodes::IntCC::UnsignedLessThan),
1872 "u64_le" => JitOp::U64Cmp(ir::condcodes::IntCC::UnsignedLessThanOrEqual),
1873 "u64_gt" => JitOp::U64Cmp(ir::condcodes::IntCC::UnsignedGreaterThan),
1874 "u64_ge" => JitOp::U64Cmp(ir::condcodes::IntCC::UnsignedGreaterThanOrEqual),
1875 "f64_eq" => JitOp::F64Cmp(ir::condcodes::FloatCC::Equal),
1876 "f64_ne" => JitOp::F64Cmp(ir::condcodes::FloatCC::NotEqual),
1877 "f64_lt" => JitOp::F64Cmp(ir::condcodes::FloatCC::LessThan),
1878 "f64_le" => JitOp::F64Cmp(ir::condcodes::FloatCC::LessThanOrEqual),
1879 "f64_gt" => JitOp::F64Cmp(ir::condcodes::FloatCC::GreaterThan),
1880 "f64_ge" => JitOp::F64Cmp(ir::condcodes::FloatCC::GreaterThanOrEqual),
1881 "select_u64" | "select" => JitOp::SelectU64,
1882 "select_f64" => JitOp::SelectF64,
1883
1884 "div_wire" => JitOp::U64DivWire,
1886 "mod_wire" => JitOp::U64ModWire,
1887 "ceil_to_multiple" => JitOp::CeilToMultiple,
1888 "multiples_at_least" => JitOp::MultiplesAtLeast,
1889 "checked_add" => JitOp::CheckedAdd,
1890 "checked_sub" => JitOp::CheckedSub,
1891 "checked_mul" => JitOp::CheckedMul,
1892
1893 "sum" => JitOp::VariadicSum,
1895 "product" => JitOp::VariadicProduct,
1896 "min" => JitOp::VariadicMin,
1897 "max" => JitOp::VariadicMax,
1898
1899 "blend" => {
1901 if let Some(&c) = consts.first() {
1902 JitOp::BlendConst(c)
1903 } else {
1904 JitOp::Fallback
1905 }
1906 }
1907 "lfsr_step" => {
1908 if let Some(&fb) = consts.first() {
1909 JitOp::LfsrStepConst(fb)
1910 } else {
1911 JitOp::Fallback
1912 }
1913 }
1914 "pcg" => {
1915 if consts.len() >= 2 {
1916 JitOp::PcgConst(consts[0], consts[1])
1917 } else {
1918 JitOp::Fallback
1919 }
1920 }
1921 "pcg_stream" => {
1922 if let Some(&seed) = consts.first() {
1923 JitOp::PcgStreamConst(seed)
1924 } else {
1925 JitOp::Fallback
1926 }
1927 }
1928 "n_of" => {
1929 if consts.len() >= 2 {
1930 JitOp::NOfConst(consts[0], consts[1])
1931 } else {
1932 JitOp::Fallback
1933 }
1934 }
1935
1936 "cycle_walk" => {
1937 if consts.len() >= 3 {
1938 JitOp::CycleWalkConst(consts[0], consts[1], consts[2])
1939 } else {
1940 JitOp::Fallback
1941 }
1942 }
1943 "coin_flip" => {
1944 if let Some(&threshold) = consts.first() {
1949 JitOp::CoinFlipConst(threshold)
1950 } else {
1951 JitOp::Fallback
1952 }
1953 }
1954 "default_or" => JitOp::Identity,
1957 "const_u64" | "const_bool" | "session_start_millis" => {
1958 if let Some(&c) = consts.first() {
1959 JitOp::ConstU64(c)
1960 } else {
1961 JitOp::Fallback
1962 }
1963 }
1964 "const_f64" => {
1965 if let Some(&c) = consts.first() {
1966 JitOp::ConstF64(c)
1967 } else {
1968 JitOp::Fallback
1969 }
1970 }
1971 "inv_lerp" => {
1972 if consts.len() >= 2 {
1973 JitOp::InvLerpConst(consts[0], consts[1])
1974 } else {
1975 JitOp::Fallback
1976 }
1977 }
1978 "remap" => {
1979 if consts.len() >= 4 {
1980 JitOp::RemapConst(consts[0], consts[1], consts[2], consts[3])
1981 } else {
1982 JitOp::Fallback
1983 }
1984 }
1985 "epoch_offset" => {
1986 if let Some(&c) = consts.first() {
1987 JitOp::EpochOffsetConst(c)
1988 } else {
1989 JitOp::Fallback
1990 }
1991 }
1992 "epoch_scale" => {
1993 if let Some(&c) = consts.first() {
1994 JitOp::EpochScaleConst(c)
1995 } else {
1996 JitOp::Fallback
1997 }
1998 }
1999 "thread_id" => JitOp::ThreadId,
2000 "current_epoch_millis" => JitOp::CurrentEpochMillis,
2001 "perlin_1d" => {
2002 if consts.len() >= 2 {
2003 JitOp::Perlin1dConst(consts[0], consts[1])
2004 } else {
2005 JitOp::Fallback
2006 }
2007 }
2008 "perlin_2d" => {
2009 if consts.len() >= 2 {
2010 JitOp::Perlin2dConst(consts[0], consts[1])
2011 } else {
2012 JitOp::Fallback
2013 }
2014 }
2015 "simplex_2d" => {
2016 if consts.len() >= 2 {
2017 JitOp::Simplex2dConst(consts[0], consts[1])
2018 } else {
2019 JitOp::Fallback
2020 }
2021 }
2022 "fractal_noise_1d" => {
2023 if consts.len() >= 3 {
2024 JitOp::FractalNoise1dConst(consts[0], consts[1], consts[2])
2025 } else {
2026 JitOp::Fallback
2027 }
2028 }
2029 "fractal_noise_2d" => {
2030 if consts.len() >= 3 {
2031 JitOp::FractalNoise2dConst(consts[0], consts[1], consts[2])
2032 } else {
2033 JitOp::Fallback
2034 }
2035 }
2036
2037 "u64_to_f64" | "__u64_to_f64" | "u32_to_f64" | "__u32_to_f64" | "bool_to_f64"
2039 | "__bool_to_f64" | "bool_to_f32" | "__bool_to_f32" | "__f32_to_f64" | "f32_to_f64"
2040 | "__u64_to_f32" | "u64_to_f32" | "__u32_to_f32" | "u32_to_f32" | "__u16_to_f32"
2041 | "u16_to_f32" | "__u8_to_f32" | "u8_to_f32" | "__u16_to_f64" | "u16_to_f64"
2042 | "__u8_to_f64" | "u8_to_f64" | "__u128_to_f64" | "__u128_to_f32" | "__u128_to_f16" => {
2043 JitOp::ToF64
2044 }
2045
2046 "i64_to_f64" | "__i64_to_f64" | "i32_to_f64" | "__i32_to_f64" | "__i64_to_f32"
2047 | "i64_to_f32" | "__i32_to_f32" | "i32_to_f32" | "__i16_to_f32" | "i16_to_f32"
2048 | "__i8_to_f32" | "i8_to_f32" | "__i16_to_f64" | "i16_to_f64" | "__i8_to_f64"
2049 | "i8_to_f64" | "__i128_to_f64" | "__i128_to_f32" | "__i128_to_f16" => JitOp::I64ToF64,
2050
2051 "__f64_to_u64"
2052 | "__f64_to_u64_checked"
2053 | "f64_to_u32"
2054 | "__f64_to_u32"
2055 | "f32_to_u64"
2056 | "__f32_to_u64"
2057 | "f32_to_u32"
2058 | "__f32_to_u32"
2059 | "__f64_to_u16"
2060 | "f64_to_u16"
2061 | "__f64_to_u8"
2062 | "f64_to_u8"
2063 | "__f32_to_u16"
2064 | "f32_to_u16"
2065 | "__f32_to_u8"
2066 | "f32_to_u8"
2067 | "__f16_to_u64"
2068 | "__f16_to_u32"
2069 | "__f16_to_u16"
2070 | "__f16_to_u8"
2071 | "__f64_to_u128"
2072 | "__f32_to_u128"
2073 | "__f16_to_u128"
2074 | "trunc_u64" => JitOp::F64ToU64,
2075 "round_u64" => JitOp::RoundToU64,
2078
2079 "f64_to_i64" | "__f64_to_i64" | "f64_to_i32" | "__f64_to_i32" | "f32_to_i64"
2080 | "__f32_to_i64" | "f32_to_i32" | "__f32_to_i32" | "__f64_to_i16" | "f64_to_i16"
2081 | "__f64_to_i8" | "f64_to_i8" | "__f32_to_i16" | "f32_to_i16" | "__f32_to_i8"
2082 | "f32_to_i8" | "__f16_to_i64" | "__f16_to_i32" | "__f16_to_i16" | "__f16_to_i8"
2083 | "__f64_to_i128" | "__f32_to_i128" | "__f16_to_i128" => JitOp::F64ToI64,
2084
2085 "f64_to_f32" | "__f64_to_f32" | "__f16_to_f32" | "__f16_to_f64" | "__f32_to_f16"
2086 | "__f64_to_f16" => JitOp::Identity,
2087
2088 "u32_to_u64" | "__u32_to_u64" | "u64_to_u32" | "__u64_to_u32" | "u32_to_i32"
2089 | "__u32_to_i32" | "i32_to_u32" | "__i32_to_u32" | "u64_to_i64" | "__u64_to_i64"
2090 | "i64_to_u64" | "__i64_to_u64" | "bool_to_u64" | "__bool_to_u64" | "bool_to_i64"
2091 | "__bool_to_i64" | "bool_to_u32" | "__bool_to_u32" | "bool_to_i32" | "__bool_to_i32"
2092 | "__u64_to_u16" | "__u64_to_u8" | "__u64_to_i16" | "__u64_to_i8" | "__i64_to_u32"
2093 | "__i64_to_u16" | "__i64_to_u8" | "__i64_to_i16" | "__i64_to_i8" | "__u32_to_u16"
2094 | "__u32_to_u8" | "__u32_to_i16" | "__u32_to_i8" | "__i32_to_u16" | "__i32_to_u8"
2095 | "__i32_to_i16" | "__i32_to_i8" | "__u16_to_u8" | "__u16_to_i8" | "__i16_to_u8"
2096 | "__i16_to_i8" | "__u128_to_u64" | "__u128_to_i64" | "__i128_to_u64" | "__i128_to_i64"
2097 | "__u128_to_u32" | "__u128_to_u16" | "__u128_to_u8" | "__u128_to_i32"
2098 | "__u128_to_i16" | "__u128_to_i8" | "__i128_to_u32" | "__i128_to_u16" | "__i128_to_u8"
2099 | "__i128_to_i32" | "__i128_to_i16" | "__i128_to_i8" | "__u64_to_u128"
2100 | "__u64_to_i128" | "__i64_to_u128" | "__i64_to_i128" | "__u128_to_i128"
2101 | "__i128_to_u128" | "__bool_to_u16" | "__bool_to_u8" | "__bool_to_i16"
2102 | "__bool_to_i8" | "__bool_to_u128" | "__bool_to_i128" | "__u8_to_f16" | "__u16_to_f16"
2103 | "__i8_to_f16" | "__i16_to_f16" | "__u64_to_f16" | "__i64_to_f16" | "__u32_to_f16"
2104 | "__i32_to_f16" | "__bool_to_f16" => JitOp::Identity,
2105
2106 "i32_to_i64" | "__i32_to_i64" | "i32_to_u64" | "__i32_to_u64" | "__u32_to_i64"
2107 | "u32_to_i64" | "__u32_to_u128" | "__u32_to_i128" | "__i32_to_u128" | "__i32_to_i128" => {
2108 JitOp::SignExtendI32
2109 }
2110
2111 "__i16_to_i32" | "i16_to_i32" | "__i16_to_i64" | "i16_to_i64" | "__i16_to_u32"
2112 | "i16_to_u32" | "__i16_to_u64" | "i16_to_u64" | "__i16_to_u128" | "__i16_to_i128" => {
2113 JitOp::SignExtendI16
2114 }
2115
2116 "__i8_to_i16" | "i8_to_i16" | "__i8_to_i32" | "i8_to_i32" | "__i8_to_i64" | "i8_to_i64"
2117 | "__i8_to_u16" | "i8_to_u16" | "__i8_to_u32" | "i8_to_u32" | "__i8_to_u64"
2118 | "i8_to_u64" | "__i8_to_u128" | "__i8_to_i128" => JitOp::SignExtendI8,
2119
2120 "__u16_to_u32" | "u16_to_u32" | "__u16_to_u64" | "u16_to_u64" | "__u16_to_i32"
2121 | "u16_to_i32" | "__u16_to_i64" | "u16_to_i64" | "__u16_to_u128" | "__u16_to_i128"
2122 | "__u16_to_i16" | "u16_to_i16" | "__i16_to_u16" | "i16_to_u16" => JitOp::ZeroExtendU16,
2123
2124 "__u8_to_u16" | "u8_to_u16" | "__u8_to_u32" | "u8_to_u32" | "__u8_to_u64" | "u8_to_u64"
2125 | "__u8_to_i16" | "u8_to_i16" | "__u8_to_i32" | "u8_to_i32" | "__u8_to_i64"
2126 | "u8_to_i64" | "__u8_to_u128" | "__u8_to_i128" | "__u8_to_i8" | "u8_to_i8"
2127 | "__i8_to_u8" | "i8_to_u8" => JitOp::ZeroExtendU8,
2128
2129 "u64_to_i32" | "__u64_to_i32" | "i64_to_i32" | "__i64_to_i32" => JitOp::ZeroExtendU32,
2130
2131 "u64_to_bool" | "__u64_to_bool" | "i64_to_bool" | "__i64_to_bool" | "u32_to_bool"
2132 | "__u32_to_bool" | "i32_to_bool" | "__i32_to_bool" | "f64_to_bool" | "__f64_to_bool"
2133 | "f32_to_bool" | "__f32_to_bool" | "__f16_to_bool" | "f16_to_bool" | "__u8_to_bool"
2134 | "__u16_to_bool" | "__i8_to_bool" | "__i16_to_bool" | "__u128_to_bool"
2135 | "__i128_to_bool" => JitOp::ToBool,
2136
2137 "weighted_pick" => {
2138 if consts.len() >= 5 {
2139 JitOp::WeightedPickConst(consts[0], consts[1], consts[2], consts[3], consts[4])
2140 } else {
2141 JitOp::Fallback
2142 }
2143 }
2144
2145 "is_positive" => {
2151 let name = node.meta().ins.iter().find_map(|slot| match slot {
2152 crate::ast::Slot::Const {
2153 name,
2154 value: crate::ast::ConstValue::Str(v),
2155 } if name == "name" => Some(v),
2156 _ => None,
2157 });
2158 match name {
2159 Some(v) => JitOp::IsPositiveCheck {
2160 name_ptr: v.as_ptr() as u64,
2161 name_len: v.len() as u64,
2162 },
2163 None => JitOp::IsPositiveCheck {
2164 name_ptr: 0,
2165 name_len: 0,
2166 },
2167 }
2168 }
2169 "in_range" => {
2170 if consts.len() >= 2 {
2171 JitOp::InRangeCheck(consts[0], consts[1])
2172 } else {
2173 JitOp::Fallback
2174 }
2175 }
2176 "is_one_of" => {
2177 if consts.is_empty() {
2178 JitOp::Fallback
2179 } else {
2180 let set = node.meta().ins.iter().find_map(|slot| match slot {
2181 crate::ast::Slot::Const {
2182 name,
2183 value: crate::ast::ConstValue::VecU64(v),
2184 } if name == "allowed" => Some(v),
2185 _ => None,
2186 });
2187 let (set_ptr, set_len) = match set {
2188 Some(v) => (v.as_ptr() as u64, v.len() as u64),
2189 None => (0, 0),
2190 };
2191 JitOp::IsOneOfCheck {
2192 allowed: consts,
2193 set_ptr,
2194 set_len,
2195 }
2196 }
2197 }
2198 _ => JitOp::Fallback,
2203 }
2204}
2205
2206#[doc(hidden)]
2214pub fn compile_jit_raw(
2215 coord_count: usize,
2216 total_slots: usize,
2217 steps: Vec<(JitOp, Vec<usize>, Vec<usize>)>,
2218 output_map: HashMap<String, usize>,
2219 nodes: Vec<Box<dyn PolydatNode>>,
2220) -> Result<JitKernelRaw, String> {
2221 compile_jit_raw_with(
2222 coord_count,
2223 total_slots,
2224 steps,
2225 output_map,
2226 nodes,
2227 crate::compile::externs::Externs::default(),
2228 super::kernels::ScratchPlan::default(),
2229 Vec::new(),
2230 )
2231}
2232
2233#[allow(clippy::too_many_arguments)]
2236pub(crate) fn compile_jit_raw_with(
2237 coord_count: usize,
2238 total_slots: usize,
2239 steps: Vec<(JitOp, Vec<usize>, Vec<usize>)>,
2240 output_map: HashMap<String, usize>,
2241 nodes: Vec<Box<dyn PolydatNode>>,
2242 externs: crate::compile::externs::Externs,
2243 scratch: super::kernels::ScratchPlan,
2244 volatile: Vec<usize>,
2245) -> Result<JitKernelRaw, String> {
2246 let (raw_fn, _, code) = compile_jit_impl(&steps, false, Some(total_slots))?;
2247 let mut core = JitCore::new(
2248 total_slots,
2249 coord_count,
2250 output_map,
2251 code,
2252 nodes,
2253 scratch,
2254 volatile,
2255 );
2256 core.set_externs(externs);
2257 Ok(JitKernelRaw {
2258 core,
2259 code_fn: raw_fn,
2260 })
2261}
2262
2263pub(crate) type JitSegmentCode = (NativeFn, super::kernels::JitCode);
2267
2268pub(crate) fn compile_jit_entry(
2272 steps: &[(JitOp, Vec<usize>, Vec<usize>)],
2273 tracker: Option<usize>,
2274) -> Result<JitSegmentCode, String> {
2275 let (raw_fn, _, code) = compile_jit_impl(steps, false, tracker)?;
2276 Ok((raw_fn, code))
2277}
2278
2279#[allow(clippy::too_many_arguments)]
2281pub(crate) fn compile_jit_push(
2282 coord_count: usize,
2283 total_slots: usize,
2284 steps: Vec<(JitOp, Vec<usize>, Vec<usize>)>,
2285 output_map: HashMap<String, usize>,
2286 nodes: Vec<Box<dyn PolydatNode>>,
2287 input_dependents: Vec<Vec<usize>>,
2288 externs: crate::compile::externs::Externs,
2289 scratch: super::kernels::ScratchPlan,
2290 volatile: Vec<usize>,
2291) -> Result<JitKernelPush, String> {
2292 let step_count = steps.len();
2293 let (_, prov_fn, code) = compile_jit_impl(&steps, true, Some(total_slots))?;
2294 let mut core = JitCore::new(
2295 total_slots,
2296 coord_count,
2297 output_map,
2298 code,
2299 nodes,
2300 scratch,
2301 volatile,
2302 );
2303 core.set_externs(externs);
2304 Ok(JitKernelPush {
2305 core,
2306 code_fn_prov: prov_fn,
2307 node_clean: vec![0u8; step_count],
2308 input_dependents,
2309 })
2310}
2311
2312#[allow(clippy::too_many_arguments)]
2314pub(crate) fn compile_jit_pull(
2315 coord_count: usize,
2316 total_slots: usize,
2317 steps: Vec<(JitOp, Vec<usize>, Vec<usize>)>,
2318 output_map: HashMap<String, usize>,
2319 nodes: Vec<Box<dyn PolydatNode>>,
2320 input_dependents: &[Vec<usize>],
2321 externs: crate::compile::externs::Externs,
2322 scratch: super::kernels::ScratchPlan,
2323 volatile: Vec<usize>,
2324) -> Result<JitKernelPull, String> {
2325 let buffer_len = total_slots;
2326 let (raw_fn, _, code) = compile_jit_impl(&steps, false, Some(total_slots))?;
2328 let step_outs: Vec<&[usize]> = steps.iter().map(|(_, _, o)| o.as_slice()).collect();
2329 let slot_provenance =
2330 crate::compile::slot_provenance(coord_count, buffer_len, &step_outs, input_dependents);
2331 let mut core = JitCore::new(
2332 total_slots,
2333 coord_count,
2334 output_map,
2335 code,
2336 nodes,
2337 scratch,
2338 volatile,
2339 );
2340 core.set_externs(externs);
2341 Ok(JitKernelPull {
2342 core,
2343 code_fn: raw_fn,
2344 slot_provenance,
2345 changed_mask: crate::kernel::ProvMask::all_below(coord_count),
2346 force_run: false,
2347 })
2348}
2349
2350#[allow(clippy::too_many_arguments)]
2352pub(crate) fn compile_jit_push_pull(
2353 coord_count: usize,
2354 total_slots: usize,
2355 steps: Vec<(JitOp, Vec<usize>, Vec<usize>)>,
2356 output_map: HashMap<String, usize>,
2357 nodes: Vec<Box<dyn PolydatNode>>,
2358 input_dependents: Vec<Vec<usize>>,
2359 externs: crate::compile::externs::Externs,
2360 scratch: super::kernels::ScratchPlan,
2361 volatile: Vec<usize>,
2362) -> Result<JitKernelPushPull, String> {
2363 let step_count = steps.len();
2364 let buffer_len = total_slots;
2365 let (_, prov_fn, code) = compile_jit_impl(&steps, true, Some(total_slots))?;
2366 let step_outs: Vec<&[usize]> = steps.iter().map(|(_, _, o)| o.as_slice()).collect();
2367 let slot_provenance =
2368 crate::compile::slot_provenance(coord_count, buffer_len, &step_outs, &input_dependents);
2369 let mut core = JitCore::new(
2370 total_slots,
2371 coord_count,
2372 output_map,
2373 code,
2374 nodes,
2375 scratch,
2376 volatile,
2377 );
2378 core.set_externs(externs);
2379 Ok(JitKernelPushPull {
2380 core,
2381 code_fn_prov: prov_fn,
2382 node_clean: vec![0u8; step_count],
2383 input_dependents,
2384 slot_provenance,
2385 changed_mask: crate::kernel::ProvMask::all_below(coord_count),
2386 force_run: false,
2387 })
2388}
2389
2390pub type NativeFn = unsafe fn(*const u64, *mut u64, *mut crate::ast::ScratchBuf);
2394pub type NativeProvFn = unsafe fn(*const u64, *mut u64, *mut crate::ast::ScratchBuf, *mut u8);
2396
2397type JitCompiled = (NativeFn, NativeProvFn, super::kernels::JitCode);
2401
2402fn compile_jit_impl(
2406 steps: &[(JitOp, Vec<usize>, Vec<usize>)],
2407 provenance: bool,
2408 tracker: Option<usize>,
2409) -> Result<JitCompiled, String> {
2410 let mut flag_builder = settings::builder();
2411 flag_builder.set("opt_level", "speed").unwrap();
2412 flag_builder.set("unwind_info", "true").unwrap();
2417 flag_builder.set("preserve_frame_pointers", "true").unwrap();
2418 let isa = super::host_isa::build_host_isa(flag_builder)?;
2419
2420 let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
2421
2422 jit_builder.symbol("jit_xxh3_hash", jit_xxh3_hash as *const u8);
2424 jit_builder.symbol("jit_interleave", jit_interleave as *const u8);
2425 jit_builder.symbol("jit_shuffle", jit_shuffle as *const u8);
2426 jit_builder.symbol("jit_lut_sample", jit_lut_sample as *const u8);
2427 jit_builder.symbol("jit_weighted_pick", jit_weighted_pick as *const u8);
2428 jit_builder.symbol("jit_pcg", jit_pcg as *const u8);
2429 jit_builder.symbol("jit_pcg_stream", jit_pcg_stream as *const u8);
2430 jit_builder.symbol("jit_n_of", jit_n_of as *const u8);
2431 jit_builder.symbol("jit_cycle_walk", jit_cycle_walk as *const u8);
2432 jit_builder.symbol("jit_perlin_1d", jit_perlin_1d as *const u8);
2433 jit_builder.symbol("jit_perlin_2d", jit_perlin_2d as *const u8);
2434 jit_builder.symbol("jit_simplex_2d", jit_simplex_2d as *const u8);
2435 jit_builder.symbol("jit_fractal_noise_1d", jit_fractal_noise_1d as *const u8);
2436 jit_builder.symbol("jit_fractal_noise_2d", jit_fractal_noise_2d as *const u8);
2437 jit_builder.symbol("jit_thread_id", jit_thread_id as *const u8);
2438 jit_builder.symbol(
2439 "jit_current_epoch_millis",
2440 jit_current_epoch_millis as *const u8,
2441 );
2442 jit_builder.symbol("jit_is_positive_fail", jit_is_positive_fail as *const u8);
2446 jit_builder.symbol("jit_in_range_fail", jit_in_range_fail as *const u8);
2447 jit_builder.symbol("jit_is_one_of_fail", jit_is_one_of_fail as *const u8);
2448 jit_builder.symbol("jit_slot_call", jit_slot_call as *const u8);
2451 jit_builder.symbol("jit_u64_to_str", jit_u64_to_str as *const u8);
2452 jit_builder.symbol("jit_i64_to_str", jit_i64_to_str as *const u8);
2453 jit_builder.symbol("jit_f64_to_str", jit_f64_to_str as *const u8);
2454 jit_builder.symbol("jit_str_concat", jit_str_concat as *const u8);
2455 jit_builder.symbol("jit_json_to_str", jit_json_to_str as *const u8);
2456 jit_builder.symbol("jit_vec_add", jit_vec_add as *const u8);
2457 jit_builder.symbol("jit_vec_scale", jit_vec_scale as *const u8);
2458 jit_builder.symbol("jit_vec_norm", jit_vec_norm as *const u8);
2459 jit_builder.symbol("jit_hash_vec", jit_hash_vec as *const u8);
2460 jit_builder.symbol("jit_xxhash3_vec", jit_xxhash3_vec as *const u8);
2461 jit_builder.symbol("jit_reg_to_vec_f32", jit_reg_to_vec_f32 as *const u8);
2462 jit_builder.symbol("jit_vec_dot", jit_vec_dot as *const u8);
2463 jit_builder.symbol("jit_vec_l2", jit_vec_l2 as *const u8);
2464 jit_builder.symbol("jit_vec_cosine", jit_vec_cosine as *const u8);
2465 jit_builder.symbol("jit_lid_mle", jit_lid_mle as *const u8);
2466 jit_builder.symbol("jit_reg_lane_f32", jit_reg_lane_f32 as *const u8);
2467 jit_builder.symbol("jit_reg_lane_i16", jit_reg_lane_i16 as *const u8);
2468 jit_builder.symbol("jit_reg_lane_i64", jit_reg_lane_i64 as *const u8);
2469 jit_builder.symbol("jit_reg_with_lane_f32", jit_reg_with_lane_f32 as *const u8);
2470 jit_builder.symbol("jit_reg_gather_f32", jit_reg_gather_f32 as *const u8);
2471 jit_builder.symbol("jit_vec_to_reg_f32", jit_vec_to_reg_f32 as *const u8);
2472 jit_builder.symbol("jit_reg_mul_i8", jit_reg_mul_i8 as *const u8);
2473 jit_builder.symbol("jit_sin", jit_sin as *const u8);
2475 jit_builder.symbol("jit_cos", jit_cos as *const u8);
2476 jit_builder.symbol("jit_tan", jit_tan as *const u8);
2477 jit_builder.symbol("jit_asin", jit_asin as *const u8);
2478 jit_builder.symbol("jit_acos", jit_acos as *const u8);
2479 jit_builder.symbol("jit_atan", jit_atan as *const u8);
2480 jit_builder.symbol("jit_sqrt", jit_sqrt as *const u8);
2481 jit_builder.symbol("jit_abs_f64", jit_abs_f64 as *const u8);
2482 jit_builder.symbol("jit_ln", jit_ln as *const u8);
2483 jit_builder.symbol("jit_exp", jit_exp as *const u8);
2484 jit_builder.symbol("jit_floor_base10", jit_floor_base10 as *const u8);
2485 jit_builder.symbol("jit_ceiling_base10", jit_ceiling_base10 as *const u8);
2486 jit_builder.symbol("jit_closest_base10", jit_closest_base10 as *const u8);
2487 jit_builder.symbol("jit_floor_decade", jit_floor_decade as *const u8);
2488 jit_builder.symbol("jit_ceiling_decade", jit_ceiling_decade as *const u8);
2489 jit_builder.symbol("jit_closest_decade", jit_closest_decade as *const u8);
2490 jit_builder.symbol("jit_floor_binomial", jit_floor_binomial as *const u8);
2491 jit_builder.symbol("jit_ceiling_binomial", jit_ceiling_binomial as *const u8);
2492 jit_builder.symbol("jit_closest_binomial", jit_closest_binomial as *const u8);
2493 jit_builder.symbol("jit_floor_fibonacci", jit_floor_fibonacci as *const u8);
2494 jit_builder.symbol("jit_ceiling_fibonacci", jit_ceiling_fibonacci as *const u8);
2495 jit_builder.symbol("jit_closest_fibonacci", jit_closest_fibonacci as *const u8);
2496 jit_builder.symbol("jit_atan2", jit_atan2 as *const u8);
2497 jit_builder.symbol("jit_pow", jit_pow as *const u8);
2498 jit_builder.symbol("jit_round_nearest", jit_round_nearest as *const u8);
2499 jit_builder.symbol("jit_round_floor", jit_round_floor as *const u8);
2500 jit_builder.symbol("jit_round_ceiling", jit_round_ceiling as *const u8);
2501 jit_builder.symbol("jit_f64_mod", jit_f64_mod as *const u8);
2502 jit_builder.symbol("jit_div_zero_fail", jit_div_zero_fail as *const u8);
2503
2504 let mut module = JITModule::new(jit_builder);
2505
2506 let hash_func_id = {
2508 let mut sig = module.make_signature();
2509 sig.params.push(AbiParam::new(types::I64));
2510 sig.returns.push(AbiParam::new(types::I64));
2511 module
2512 .declare_function("jit_xxh3_hash", Linkage::Import, &sig)
2513 .map_err(|e| format!("declare hash: {e}"))?
2514 };
2515
2516 let interleave_func_id = {
2518 let mut sig = module.make_signature();
2519 sig.params.push(AbiParam::new(types::I64));
2520 sig.params.push(AbiParam::new(types::I64));
2521 sig.returns.push(AbiParam::new(types::I64));
2522 module
2523 .declare_function("jit_interleave", Linkage::Import, &sig)
2524 .map_err(|e| format!("declare interleave: {e}"))?
2525 };
2526
2527 let shuffle_func_id = {
2529 let mut sig = module.make_signature();
2530 for _ in 0..4 {
2531 sig.params.push(AbiParam::new(types::I64));
2532 }
2533 sig.returns.push(AbiParam::new(types::I64));
2534 module
2535 .declare_function("jit_shuffle", Linkage::Import, &sig)
2536 .map_err(|e| format!("declare shuffle: {e}"))?
2537 };
2538
2539 let lut_sample_func_id = {
2541 let mut sig = module.make_signature();
2542 for _ in 0..3 {
2543 sig.params.push(AbiParam::new(types::I64));
2544 }
2545 sig.returns.push(AbiParam::new(types::I64));
2546 module
2547 .declare_function("jit_lut_sample", Linkage::Import, &sig)
2548 .map_err(|e| format!("declare lut_sample: {e}"))?
2549 };
2550
2551 let weighted_pick_func_id = {
2553 let mut sig = module.make_signature();
2554 for _ in 0..6 {
2555 sig.params.push(AbiParam::new(types::I64));
2556 }
2557 sig.returns.push(AbiParam::new(types::I64));
2558 module
2559 .declare_function("jit_weighted_pick", Linkage::Import, &sig)
2560 .map_err(|e| format!("declare weighted_pick: {e}"))?
2561 };
2562
2563 let pcg_func_id = {
2564 let mut sig = module.make_signature();
2565 for _ in 0..3 {
2566 sig.params.push(AbiParam::new(types::I64));
2567 }
2568 sig.returns.push(AbiParam::new(types::I64));
2569 module
2570 .declare_function("jit_pcg", Linkage::Import, &sig)
2571 .map_err(|e| format!("declare pcg: {e}"))?
2572 };
2573 let pcg_stream_func_id = {
2574 let mut sig = module.make_signature();
2575 for _ in 0..3 {
2576 sig.params.push(AbiParam::new(types::I64));
2577 }
2578 sig.returns.push(AbiParam::new(types::I64));
2579 module
2580 .declare_function("jit_pcg_stream", Linkage::Import, &sig)
2581 .map_err(|e| format!("declare pcg_stream: {e}"))?
2582 };
2583 let n_of_func_id = {
2584 let mut sig = module.make_signature();
2585 for _ in 0..3 {
2586 sig.params.push(AbiParam::new(types::I64));
2587 }
2588 sig.returns.push(AbiParam::new(types::I64));
2589 module
2590 .declare_function("jit_n_of", Linkage::Import, &sig)
2591 .map_err(|e| format!("declare n_of: {e}"))?
2592 };
2593 let cycle_walk_func_id = {
2594 let mut sig = module.make_signature();
2595 for _ in 0..4 {
2596 sig.params.push(AbiParam::new(types::I64));
2597 }
2598 sig.returns.push(AbiParam::new(types::I64));
2599 module
2600 .declare_function("jit_cycle_walk", Linkage::Import, &sig)
2601 .map_err(|e| format!("declare cycle_walk: {e}"))?
2602 };
2603 let perlin_1d_func_id = {
2604 let mut sig = module.make_signature();
2605 for _ in 0..3 {
2606 sig.params.push(AbiParam::new(types::I64));
2607 }
2608 sig.returns.push(AbiParam::new(types::I64));
2609 module
2610 .declare_function("jit_perlin_1d", Linkage::Import, &sig)
2611 .map_err(|e| format!("declare perlin_1d: {e}"))?
2612 };
2613 let perlin_2d_func_id = {
2614 let mut sig = module.make_signature();
2615 for _ in 0..4 {
2616 sig.params.push(AbiParam::new(types::I64));
2617 }
2618 sig.returns.push(AbiParam::new(types::I64));
2619 module
2620 .declare_function("jit_perlin_2d", Linkage::Import, &sig)
2621 .map_err(|e| format!("declare perlin_2d: {e}"))?
2622 };
2623 let simplex_2d_func_id = {
2624 let mut sig = module.make_signature();
2625 for _ in 0..4 {
2626 sig.params.push(AbiParam::new(types::I64));
2627 }
2628 sig.returns.push(AbiParam::new(types::I64));
2629 module
2630 .declare_function("jit_simplex_2d", Linkage::Import, &sig)
2631 .map_err(|e| format!("declare simplex_2d: {e}"))?
2632 };
2633 let fractal_noise_1d_func_id = {
2634 let mut sig = module.make_signature();
2635 for _ in 0..4 {
2636 sig.params.push(AbiParam::new(types::I64));
2637 }
2638 sig.returns.push(AbiParam::new(types::I64));
2639 module
2640 .declare_function("jit_fractal_noise_1d", Linkage::Import, &sig)
2641 .map_err(|e| format!("declare fractal_noise_1d: {e}"))?
2642 };
2643 let fractal_noise_2d_func_id = {
2644 let mut sig = module.make_signature();
2645 for _ in 0..5 {
2646 sig.params.push(AbiParam::new(types::I64));
2647 }
2648 sig.returns.push(AbiParam::new(types::I64));
2649 module
2650 .declare_function("jit_fractal_noise_2d", Linkage::Import, &sig)
2651 .map_err(|e| format!("declare fractal_noise_2d: {e}"))?
2652 };
2653 let thread_id_func_id = {
2654 let mut sig = module.make_signature();
2655 sig.returns.push(AbiParam::new(types::I64));
2656 module
2657 .declare_function("jit_thread_id", Linkage::Import, &sig)
2658 .map_err(|e| format!("declare thread_id: {e}"))?
2659 };
2660 let current_epoch_millis_func_id = {
2661 let mut sig = module.make_signature();
2662 sig.returns.push(AbiParam::new(types::I64));
2663 module
2664 .declare_function("jit_current_epoch_millis", Linkage::Import, &sig)
2665 .map_err(|e| format!("declare current_epoch_millis: {e}"))?
2666 };
2667
2668 let math_unary_names = [
2670 "jit_sin",
2671 "jit_cos",
2672 "jit_tan",
2673 "jit_asin",
2674 "jit_acos",
2675 "jit_atan",
2676 "jit_sqrt",
2677 "jit_abs_f64",
2678 "jit_ln",
2679 "jit_exp",
2680 "jit_floor_base10",
2681 "jit_ceiling_base10",
2682 "jit_closest_base10",
2683 "jit_floor_decade",
2684 "jit_ceiling_decade",
2685 "jit_closest_decade",
2686 "jit_floor_binomial",
2687 "jit_ceiling_binomial",
2688 "jit_closest_binomial",
2689 "jit_floor_fibonacci",
2690 "jit_ceiling_fibonacci",
2691 "jit_closest_fibonacci",
2692 ];
2693 let mut math_unary_ids = Vec::new();
2694 for name in &math_unary_names {
2695 let mut sig = module.make_signature();
2696 sig.params.push(AbiParam::new(types::I64));
2697 sig.returns.push(AbiParam::new(types::I64));
2698 math_unary_ids.push(
2699 module
2700 .declare_function(name, Linkage::Import, &sig)
2701 .map_err(|e| format!("declare {name}: {e}"))?,
2702 );
2703 }
2704
2705 let is_positive_fail_id = {
2709 let mut sig = module.make_signature();
2710 sig.params.push(AbiParam::new(types::I64));
2711 sig.params.push(AbiParam::new(types::I64));
2712 sig.params.push(AbiParam::new(types::I64));
2713 sig.returns.push(AbiParam::new(types::I64));
2714 module
2715 .declare_function("jit_is_positive_fail", Linkage::Import, &sig)
2716 .map_err(|e| format!("declare is_positive_fail: {e}"))?
2717 };
2718
2719 let in_range_fail_id = {
2721 let mut sig = module.make_signature();
2722 for _ in 0..3 {
2723 sig.params.push(AbiParam::new(types::I64));
2724 }
2725 sig.returns.push(AbiParam::new(types::I64));
2726 module
2727 .declare_function("jit_in_range_fail", Linkage::Import, &sig)
2728 .map_err(|e| format!("declare in_range_fail: {e}"))?
2729 };
2730
2731 let is_one_of_fail_id = {
2734 let mut sig = module.make_signature();
2735 sig.params.push(AbiParam::new(types::I64));
2736 sig.params.push(AbiParam::new(types::I64));
2737 sig.params.push(AbiParam::new(types::I64));
2738 sig.returns.push(AbiParam::new(types::I64));
2739 module
2740 .declare_function("jit_is_one_of_fail", Linkage::Import, &sig)
2741 .map_err(|e| format!("declare is_one_of_fail: {e}"))?
2742 };
2743
2744 let math_binary_names = [
2746 "jit_atan2",
2747 "jit_pow",
2748 "jit_round_nearest",
2749 "jit_round_floor",
2750 "jit_round_ceiling",
2751 "jit_f64_mod",
2752 ];
2753 const F64_MOD_HELPER: usize = 5;
2754
2755 let div_zero_fail_id = {
2757 let mut sig = module.make_signature();
2758 sig.params.push(AbiParam::new(types::I64));
2759 sig.returns.push(AbiParam::new(types::I64));
2760 module
2761 .declare_function("jit_div_zero_fail", Linkage::Import, &sig)
2762 .map_err(|e| format!("declare div_zero_fail: {e}"))?
2763 };
2764 let mut math_binary_ids = Vec::new();
2765 for name in &math_binary_names {
2766 let mut sig = module.make_signature();
2767 sig.params.push(AbiParam::new(types::I64));
2768 sig.params.push(AbiParam::new(types::I64));
2769 sig.returns.push(AbiParam::new(types::I64));
2770 math_binary_ids.push(
2771 module
2772 .declare_function(name, Linkage::Import, &sig)
2773 .map_err(|e| format!("declare {name}: {e}"))?,
2774 );
2775 }
2776
2777 let slot_call_id = {
2780 let mut sig = module.make_signature();
2781 for _ in 0..8 {
2782 sig.params.push(AbiParam::new(types::I64));
2783 }
2784 module
2785 .declare_function("jit_slot_call", Linkage::Import, &sig)
2786 .map_err(|e| format!("declare jit_slot_call: {e}"))?
2787 };
2788
2789 let mut declare_str = |name: &str, args: usize| -> Result<cranelift_module::FuncId, String> {
2793 let mut sig = module.make_signature();
2794 for _ in 0..args {
2795 sig.params.push(AbiParam::new(types::I64));
2796 }
2797 module
2798 .declare_function(name, Linkage::Import, &sig)
2799 .map_err(|e| format!("declare {name}: {e}"))
2800 };
2801 let u64_to_str_id = declare_str("jit_u64_to_str", 5)?;
2802 let i64_to_str_id = declare_str("jit_i64_to_str", 5)?;
2803 let f64_to_str_id = declare_str("jit_f64_to_str", 5)?;
2804 let str_concat_id = declare_str("jit_str_concat", 6)?;
2805 let json_to_str_id = declare_str("jit_json_to_str", 6)?;
2806
2807 let mut declare_words =
2812 |name: &str, args: usize, returns: bool| -> Result<cranelift_module::FuncId, String> {
2813 let mut sig = module.make_signature();
2814 for _ in 0..args {
2815 sig.params.push(AbiParam::new(types::I64));
2816 }
2817 if returns {
2818 sig.returns.push(AbiParam::new(types::I64));
2819 }
2820 module
2821 .declare_function(name, Linkage::Import, &sig)
2822 .map_err(|e| format!("declare {name}: {e}"))
2823 };
2824 let vec_producer_ids = [
2825 (VecProducer::Add, declare_words("jit_vec_add", 8, false)?),
2826 (
2827 VecProducer::Scale,
2828 declare_words("jit_vec_scale", 8, false)?,
2829 ),
2830 (VecProducer::Norm, declare_words("jit_vec_norm", 8, false)?),
2831 (
2832 VecProducer::HashVec,
2833 declare_words("jit_hash_vec", 8, false)?,
2834 ),
2835 (
2836 VecProducer::XxHash3Vec,
2837 declare_words("jit_xxhash3_vec", 8, false)?,
2838 ),
2839 (
2840 VecProducer::RegToVec,
2841 declare_words("jit_reg_to_vec_f32", 8, false)?,
2842 ),
2843 ];
2844 let vec_reducer_ids = [
2845 (VecReducer::Dot, declare_words("jit_vec_dot", 4, true)?),
2846 (VecReducer::L2, declare_words("jit_vec_l2", 4, true)?),
2847 (
2848 VecReducer::Cosine,
2849 declare_words("jit_vec_cosine", 4, true)?,
2850 ),
2851 (VecReducer::LidMle, declare_words("jit_lid_mle", 4, true)?),
2852 ];
2853 let reg_lane_ids = [
2854 (
2855 RegLaneRead::F32,
2856 declare_words("jit_reg_lane_f32", 3, true)?,
2857 ),
2858 (
2859 RegLaneRead::I16,
2860 declare_words("jit_reg_lane_i16", 3, true)?,
2861 ),
2862 (
2863 RegLaneRead::I64,
2864 declare_words("jit_reg_lane_i64", 3, true)?,
2865 ),
2866 ];
2867 let reg_producer_ids = [
2868 (
2869 RegProducer::WithLaneF32,
2870 declare_words("jit_reg_with_lane_f32", 6, false)?,
2871 ),
2872 (
2873 RegProducer::GatherF32,
2874 declare_words("jit_reg_gather_f32", 6, false)?,
2875 ),
2876 (
2877 RegProducer::VecToRegF32,
2878 declare_words("jit_vec_to_reg_f32", 6, false)?,
2879 ),
2880 (
2881 RegProducer::MulI8,
2882 declare_words("jit_reg_mul_i8", 6, false)?,
2883 ),
2884 ];
2885
2886 let mut sig = module.make_signature();
2890 sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); if provenance {
2894 sig.params.push(AbiParam::new(types::I64)); }
2896 let func_id = module
2897 .declare_function("polydat_kernel", Linkage::Local, &sig)
2898 .map_err(|e| format!("declare kernel: {e}"))?;
2899
2900 let mut ctx = module.make_context();
2901 ctx.func.signature = sig;
2902
2903 let mut fb_ctx = FunctionBuilderContext::new();
2904 {
2905 let mut builder = FunctionBuilder::new(&mut ctx.func, &mut fb_ctx);
2906 let block = builder.create_block();
2907 builder.append_block_params_for_function_params(block);
2908 builder.switch_to_block(block);
2909 builder.seal_block(block);
2910
2911 let _coords_ptr = builder.block_params(block)[0];
2912 let buffer_ptr = builder.block_params(block)[1];
2913 let scratch_ptr = builder.block_params(block)[2];
2914 let clean_ptr = if provenance {
2915 Some(builder.block_params(block)[3])
2916 } else {
2917 None
2918 };
2919
2920 let hash_func_ref = module.declare_func_in_func(hash_func_id, builder.func);
2922 let interleave_func_ref = module.declare_func_in_func(interleave_func_id, builder.func);
2923 let shuffle_func_ref = module.declare_func_in_func(shuffle_func_id, builder.func);
2924 let lut_sample_func_ref = module.declare_func_in_func(lut_sample_func_id, builder.func);
2925 let weighted_pick_func_ref =
2926 module.declare_func_in_func(weighted_pick_func_id, builder.func);
2927 let is_positive_fail_ref = module.declare_func_in_func(is_positive_fail_id, builder.func);
2928 let in_range_fail_ref = module.declare_func_in_func(in_range_fail_id, builder.func);
2929 let div_zero_fail_ref = module.declare_func_in_func(div_zero_fail_id, builder.func);
2930 let is_one_of_fail_ref = module.declare_func_in_func(is_one_of_fail_id, builder.func);
2931 let slot_call_ref = module.declare_func_in_func(slot_call_id, builder.func);
2932 let u64_to_str_ref = module.declare_func_in_func(u64_to_str_id, builder.func);
2933 let i64_to_str_ref = module.declare_func_in_func(i64_to_str_id, builder.func);
2934 let f64_to_str_ref = module.declare_func_in_func(f64_to_str_id, builder.func);
2935 let str_concat_ref = module.declare_func_in_func(str_concat_id, builder.func);
2936 let json_to_str_ref = module.declare_func_in_func(json_to_str_id, builder.func);
2937 let vec_producer_refs: Vec<(VecProducer, ir::FuncRef)> = vec_producer_ids
2938 .iter()
2939 .map(|(k, id)| (*k, module.declare_func_in_func(*id, builder.func)))
2940 .collect();
2941 let vec_reducer_refs: Vec<(VecReducer, ir::FuncRef)> = vec_reducer_ids
2942 .iter()
2943 .map(|(k, id)| (*k, module.declare_func_in_func(*id, builder.func)))
2944 .collect();
2945 let reg_lane_refs: Vec<(RegLaneRead, ir::FuncRef)> = reg_lane_ids
2946 .iter()
2947 .map(|(k, id)| (*k, module.declare_func_in_func(*id, builder.func)))
2948 .collect();
2949 let reg_producer_refs: Vec<(RegProducer, ir::FuncRef)> = reg_producer_ids
2950 .iter()
2951 .map(|(k, id)| (*k, module.declare_func_in_func(*id, builder.func)))
2952 .collect();
2953 let pcg_func_ref = module.declare_func_in_func(pcg_func_id, builder.func);
2954 let pcg_stream_func_ref = module.declare_func_in_func(pcg_stream_func_id, builder.func);
2955 let n_of_func_ref = module.declare_func_in_func(n_of_func_id, builder.func);
2956 let cycle_walk_func_ref = module.declare_func_in_func(cycle_walk_func_id, builder.func);
2957 let perlin_1d_func_ref = module.declare_func_in_func(perlin_1d_func_id, builder.func);
2958 let perlin_2d_func_ref = module.declare_func_in_func(perlin_2d_func_id, builder.func);
2959 let simplex_2d_func_ref = module.declare_func_in_func(simplex_2d_func_id, builder.func);
2960 let fractal_noise_1d_func_ref =
2961 module.declare_func_in_func(fractal_noise_1d_func_id, builder.func);
2962 let fractal_noise_2d_func_ref =
2963 module.declare_func_in_func(fractal_noise_2d_func_id, builder.func);
2964 let thread_id_func_ref = module.declare_func_in_func(thread_id_func_id, builder.func);
2965 let current_epoch_millis_func_ref =
2966 module.declare_func_in_func(current_epoch_millis_func_id, builder.func);
2967 let math_unary_refs: Vec<_> = math_unary_ids
2968 .iter()
2969 .map(|id| module.declare_func_in_func(*id, builder.func))
2970 .collect();
2971 let math_binary_refs: Vec<_> = math_binary_ids
2972 .iter()
2973 .map(|id| module.declare_func_in_func(*id, builder.func))
2974 .collect();
2975 for (step_idx, (jit_op, input_slots, output_slots)) in steps.iter().enumerate() {
2977 let skip_block = if let Some(cp) = clean_ptr {
2979 let skip = builder.create_block();
2980 let cont = builder.create_block();
2981 let offset = builder.ins().iconst(types::I64, step_idx as i64);
2983 let addr = builder.ins().iadd(cp, offset);
2984 let flag = builder.ins().load(types::I8, ir::MemFlags::new(), addr, 0);
2985 let zero = builder.ins().iconst(types::I8, 0);
2986 let is_clean = builder
2987 .ins()
2988 .icmp(ir::condcodes::IntCC::NotEqual, flag, zero);
2989 builder.ins().brif(is_clean, skip, &[], cont, &[]);
2990 builder.switch_to_block(cont);
2991 builder.seal_block(cont);
2992 Some(skip)
2993 } else {
2994 None
2995 };
2996 let tracker_store = tracker.map(|t| {
3000 let idx = builder.ins().iconst(types::I64, step_idx as i64);
3001 let inst = store_slot(&mut builder, buffer_ptr, t, idx);
3002 (inst, builder.func.dfg.num_insts())
3003 });
3004 match jit_op {
3005 JitOp::Identity => {
3006 for (&i, &o) in input_slots.iter().zip(output_slots.iter()) {
3009 let val = load_slot(&mut builder, buffer_ptr, i);
3010 store_slot(&mut builder, buffer_ptr, o, val);
3011 }
3012 }
3013 JitOp::AddConst(c) => {
3014 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3015 let c_val = builder.ins().iconst(types::I64, *c as i64);
3016 let result = builder.ins().iadd(val, c_val);
3017 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3018 }
3019 JitOp::MulConst(c) => {
3020 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3021 let c_val = builder.ins().iconst(types::I64, *c as i64);
3022 let result = builder.ins().imul(val, c_val);
3023 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3024 }
3025 JitOp::DivConst(c) | JitOp::ModConst(c) => {
3026 let is_div = matches!(jit_op, JitOp::DivConst(_));
3029 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3030 if *c == 0 {
3031 let kind = builder.ins().iconst(types::I64, if is_div { 0 } else { 1 });
3032 let _ = builder.ins().call(div_zero_fail_ref, &[kind]);
3033 store_slot(&mut builder, buffer_ptr, output_slots[0], val);
3034 } else {
3035 let c_val = builder.ins().iconst(types::I64, *c as i64);
3036 let result = if is_div {
3037 builder.ins().udiv(val, c_val)
3038 } else {
3039 builder.ins().urem(val, c_val)
3040 };
3041 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3042 }
3043 }
3044 JitOp::U64DivWire | JitOp::U64ModWire => {
3045 let is_div = matches!(jit_op, JitOp::U64DivWire);
3049 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3050 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3051 let zero = builder.ins().iconst(types::I64, 0);
3052 let is_zero = builder.ins().icmp(ir::condcodes::IntCC::Equal, b, zero);
3053 let fail_block = builder.create_block();
3054 let ok_block = builder.create_block();
3055 builder.ins().brif(is_zero, fail_block, &[], ok_block, &[]);
3056 builder.switch_to_block(fail_block);
3057 builder.seal_block(fail_block);
3058 let kind = builder.ins().iconst(types::I64, if is_div { 0 } else { 1 });
3059 let _ = builder.ins().call(div_zero_fail_ref, &[kind]);
3060 builder.ins().jump(ok_block, &[]);
3061 builder.switch_to_block(ok_block);
3062 builder.seal_block(ok_block);
3063 let result = if is_div {
3064 builder.ins().udiv(a, b)
3065 } else {
3066 builder.ins().urem(a, b)
3067 };
3068 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3069 }
3070 JitOp::ClampConst(min, max) => {
3071 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3072 let min_val = builder.ins().iconst(types::I64, *min as i64);
3073 let max_val = builder.ins().iconst(types::I64, *max as i64);
3074 let clamped_lo = builder.ins().umax(val, min_val);
3075 let clamped = builder.ins().umin(clamped_lo, max_val);
3076 store_slot(&mut builder, buffer_ptr, output_slots[0], clamped);
3077 }
3078 JitOp::Interleave => {
3079 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3080 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3081 let call = builder.ins().call(interleave_func_ref, &[a, b]);
3082 let result = builder.inst_results(call)[0];
3083 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3084 }
3085 JitOp::MixedRadixConst(radixes) => {
3086 let mut remainder = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3088 for (i, &radix) in radixes.iter().enumerate() {
3089 if radix == 0 {
3090 store_slot(&mut builder, buffer_ptr, output_slots[i], remainder);
3092 } else {
3093 let r = builder.ins().iconst(types::I64, radix as i64);
3094 let digit = builder.ins().urem(remainder, r);
3095 store_slot(&mut builder, buffer_ptr, output_slots[i], digit);
3096 remainder = builder.ins().udiv(remainder, r);
3097 }
3098 }
3099 }
3100 JitOp::Hash => {
3101 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3102 let call = builder.ins().call(hash_func_ref, &[val]);
3103 let result = builder.inst_results(call)[0];
3104 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3105 }
3106 JitOp::SplitMix64 => {
3107 let x0 = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3108 let c_gamma = builder
3109 .ins()
3110 .iconst(types::I64, 0x9e3779b97f4a7c15u64 as i64);
3111 let x1 = builder.ins().iadd(x0, c_gamma);
3112 let s30 = builder.ins().ushr_imm(x1, 30);
3113 let x2 = builder.ins().bxor(x1, s30);
3114 let c_m1 = builder
3115 .ins()
3116 .iconst(types::I64, 0xbf58476d1ce4e5b9u64 as i64);
3117 let x3 = builder.ins().imul(x2, c_m1);
3118 let s27 = builder.ins().ushr_imm(x3, 27);
3119 let x4 = builder.ins().bxor(x3, s27);
3120 let c_m2 = builder
3121 .ins()
3122 .iconst(types::I64, 0x94d049bb133111ebu64 as i64);
3123 let x5 = builder.ins().imul(x4, c_m2);
3124 let s31 = builder.ins().ushr_imm(x5, 31);
3125 let result = builder.ins().bxor(x5, s31);
3126 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3127 }
3128 JitOp::FairCoin => {
3129 let x0 = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3130 let c_gamma = builder
3131 .ins()
3132 .iconst(types::I64, 0x9e3779b97f4a7c15u64 as i64);
3133 let x1 = builder.ins().iadd(x0, c_gamma);
3134 let s30 = builder.ins().ushr_imm(x1, 30);
3135 let x2 = builder.ins().bxor(x1, s30);
3136 let c_m1 = builder
3137 .ins()
3138 .iconst(types::I64, 0xbf58476d1ce4e5b9u64 as i64);
3139 let x3 = builder.ins().imul(x2, c_m1);
3140 let s27 = builder.ins().ushr_imm(x3, 27);
3141 let x4 = builder.ins().bxor(x3, s27);
3142 let c_m2 = builder
3143 .ins()
3144 .iconst(types::I64, 0x94d049bb133111ebu64 as i64);
3145 let x5 = builder.ins().imul(x4, c_m2);
3146 let s31 = builder.ins().ushr_imm(x5, 31);
3147 let h = builder.ins().bxor(x5, s31);
3148 let one = builder.ins().iconst(types::I64, 1);
3149 let result = builder.ins().band(h, one);
3150 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3151 }
3152 JitOp::CoinFlipConst(threshold) => {
3153 let x = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3154 let thr = builder.ins().iconst(types::I64, *threshold as i64);
3155 let cmp = builder
3156 .ins()
3157 .icmp(ir::condcodes::IntCC::UnsignedLessThan, x, thr);
3158 let zero = builder.ins().iconst(types::I64, 0);
3159 let one = builder.ins().iconst(types::I64, 1);
3160 let result = builder.ins().select(cmp, one, zero);
3161 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3162 }
3163 JitOp::UnfairCoinConst(p_bits) => {
3164 let x0 = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3165 let c_gamma = builder
3166 .ins()
3167 .iconst(types::I64, 0x9e3779b97f4a7c15u64 as i64);
3168 let x1 = builder.ins().iadd(x0, c_gamma);
3169 let s30 = builder.ins().ushr_imm(x1, 30);
3170 let x2 = builder.ins().bxor(x1, s30);
3171 let c_m1 = builder
3172 .ins()
3173 .iconst(types::I64, 0xbf58476d1ce4e5b9u64 as i64);
3174 let x3 = builder.ins().imul(x2, c_m1);
3175 let s27 = builder.ins().ushr_imm(x3, 27);
3176 let x4 = builder.ins().bxor(x3, s27);
3177 let c_m2 = builder
3178 .ins()
3179 .iconst(types::I64, 0x94d049bb133111ebu64 as i64);
3180 let x5 = builder.ins().imul(x4, c_m2);
3181 let s31 = builder.ins().ushr_imm(x5, 31);
3182 let h = builder.ins().bxor(x5, s31);
3183
3184 let fval = builder.ins().fcvt_from_uint(types::F64, h);
3185 let max_f = builder.ins().f64const(u64::MAX as f64);
3186 let unit = builder.ins().fdiv(fval, max_f);
3187 let p_f = builder.ins().f64const(f64::from_bits(*p_bits));
3188 let cmp = builder
3189 .ins()
3190 .fcmp(ir::condcodes::FloatCC::LessThan, unit, p_f);
3191 let zero = builder.ins().iconst(types::I64, 0);
3192 let one = builder.ins().iconst(types::I64, 1);
3193 let result = builder.ins().select(cmp, one, zero);
3194 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3195 }
3196 JitOp::ChanceConst(p_bits) => {
3197 let x0 = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3198 let c_gamma = builder
3199 .ins()
3200 .iconst(types::I64, 0x9e3779b97f4a7c15u64 as i64);
3201 let x1 = builder.ins().iadd(x0, c_gamma);
3202 let s30 = builder.ins().ushr_imm(x1, 30);
3203 let x2 = builder.ins().bxor(x1, s30);
3204 let c_m1 = builder
3205 .ins()
3206 .iconst(types::I64, 0xbf58476d1ce4e5b9u64 as i64);
3207 let x3 = builder.ins().imul(x2, c_m1);
3208 let s27 = builder.ins().ushr_imm(x3, 27);
3209 let x4 = builder.ins().bxor(x3, s27);
3210 let c_m2 = builder
3211 .ins()
3212 .iconst(types::I64, 0x94d049bb133111ebu64 as i64);
3213 let x5 = builder.ins().imul(x4, c_m2);
3214 let s31 = builder.ins().ushr_imm(x5, 31);
3215 let h = builder.ins().bxor(x5, s31);
3216
3217 let fval = builder.ins().fcvt_from_uint(types::F64, h);
3218 let max_f = builder.ins().f64const(u64::MAX as f64);
3219 let unit = builder.ins().fdiv(fval, max_f);
3220 let p_f = builder.ins().f64const(f64::from_bits(*p_bits));
3221 let cmp = builder
3222 .ins()
3223 .fcmp(ir::condcodes::FloatCC::LessThan, unit, p_f);
3224 let zero_bits = builder.ins().iconst(types::I64, 0.0_f64.to_bits() as i64);
3225 let one_bits = builder.ins().iconst(types::I64, 1.0_f64.to_bits() as i64);
3226 let result = builder.ins().select(cmp, one_bits, zero_bits);
3227 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3228 }
3229 JitOp::Popcnt => {
3230 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3231 let result = builder.ins().popcnt(val);
3232 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3233 }
3234 JitOp::Clz => {
3235 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3236 let result = builder.ins().clz(val);
3237 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3238 }
3239 JitOp::Ctz => {
3240 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3241 let result = builder.ins().ctz(val);
3242 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3243 }
3244 JitOp::Bswap => {
3245 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3246 let result = builder.ins().bswap(val);
3247 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3248 }
3249 JitOp::ShuffleConst(feedback, size, min) => {
3250 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3251 let fb = builder.ins().iconst(types::I64, *feedback as i64);
3252 let sz = builder.ins().iconst(types::I64, *size as i64);
3253 let mn = builder.ins().iconst(types::I64, *min as i64);
3254 let call = builder.ins().call(shuffle_func_ref, &[val, fb, sz, mn]);
3255 let result = builder.inst_results(call)[0];
3256 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3257 }
3258
3259 JitOp::UnitInterval => {
3261 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3263 let fval = builder.ins().fcvt_from_uint(types::F64, val);
3264 let max_f = builder.ins().f64const(u64::MAX as f64);
3265 let result = builder.ins().fdiv(fval, max_f);
3266 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], result);
3267 }
3268 JitOp::F64ToU64 => {
3269 let fval = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3270 let result = builder.ins().fcvt_to_uint_sat(types::I64, fval);
3271 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3272 }
3273 JitOp::RoundToU64 => {
3274 let fval = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3275 let rounded = round_half_away(&mut builder, fval);
3276 let result = builder.ins().fcvt_to_uint_sat(types::I64, rounded);
3277 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3278 }
3279 JitOp::FloorToU64 => {
3280 let fval = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3281 let floored = builder.ins().floor(fval);
3282 let result = builder.ins().fcvt_to_uint_sat(types::I64, floored);
3283 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3284 }
3285 JitOp::CeilToU64 => {
3286 let fval = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3287 let ceiled = builder.ins().ceil(fval);
3288 let result = builder.ins().fcvt_to_uint_sat(types::I64, ceiled);
3289 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3290 }
3291 JitOp::ClampF64Const(min_bits, max_bits) => {
3292 let fval = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3293 let fmin = builder.ins().f64const(f64::from_bits(*min_bits));
3294 let fmax = builder.ins().f64const(f64::from_bits(*max_bits));
3295 let clamped = clamp_ir(&mut builder, fval, fmin, fmax);
3296 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], clamped);
3297 }
3298 JitOp::LerpConst(a_bits, b_bits) => {
3299 let t = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3301 let a = builder.ins().f64const(f64::from_bits(*a_bits));
3302 let b = builder.ins().f64const(f64::from_bits(*b_bits));
3303 let diff = builder.ins().fsub(b, a);
3304 let scaled = builder.ins().fmul(t, diff);
3305 let result = builder.ins().fadd(a, scaled);
3306 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], result);
3307 }
3308 JitOp::ScaleRangeConst(min_bits, range_bits) => {
3309 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3311 let fval = builder.ins().fcvt_from_uint(types::F64, val);
3312 let max_f = builder.ins().f64const(u64::MAX as f64);
3313 let t = builder.ins().fdiv(fval, max_f);
3314 let fmin = builder.ins().f64const(f64::from_bits(*min_bits));
3315 let frange = builder.ins().f64const(f64::from_bits(*range_bits));
3316 let scaled = builder.ins().fmul(t, frange);
3317 let result = builder.ins().fadd(fmin, scaled);
3318 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], result);
3319 }
3320 JitOp::QuantizeConst(step_bits) => {
3321 let fval = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3323 let step = builder.ins().f64const(f64::from_bits(*step_bits));
3324 let divided = builder.ins().fdiv(fval, step);
3325 let rounded = round_half_away(&mut builder, divided);
3326 let result = builder.ins().fmul(rounded, step);
3327 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], result);
3328 }
3329
3330 JitOp::LutSampleConst(lut_ptr, lut_len) => {
3331 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3333 let ptr_val = builder.ins().iconst(types::I64, *lut_ptr as i64);
3334 let len_val = builder.ins().iconst(types::I64, *lut_len as i64);
3335 let call = builder
3336 .ins()
3337 .call(lut_sample_func_ref, &[input, ptr_val, len_val]);
3338 let result = builder.inst_results(call)[0];
3339 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3340 }
3341 JitOp::DiscretizeConst(range_bits, buckets) => {
3342 let fval = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3344 let range = f64::from_bits(*range_bits);
3345 let fzero = builder.ins().f64const(0.0);
3346 let frange_m_eps = builder.ins().f64const(range - f64::EPSILON);
3347 let frange = builder.ins().f64const(range);
3348 let fbuckets = builder.ins().f64const(*buckets as f64);
3349 let clamped = clamp_ir(&mut builder, fval, fzero, frange_m_eps);
3350 let divided = builder.ins().fdiv(clamped, frange);
3351 let scaled = builder.ins().fmul(divided, fbuckets);
3352 let as_u64 = builder.ins().fcvt_to_uint_sat(types::I64, scaled);
3353 let max_bucket = builder.ins().iconst(types::I64, (*buckets - 1) as i64);
3354 let result = builder.ins().umin(as_u64, max_bucket);
3355 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3356 }
3357
3358 JitOp::WeightedPickConst(values_ptr, biases_ptr, primaries_ptr, aliases_ptr, n) => {
3359 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3361 let v_ptr = builder.ins().iconst(types::I64, *values_ptr as i64);
3362 let b_ptr = builder.ins().iconst(types::I64, *biases_ptr as i64);
3363 let p_ptr = builder.ins().iconst(types::I64, *primaries_ptr as i64);
3364 let a_ptr = builder.ins().iconst(types::I64, *aliases_ptr as i64);
3365 let n_val = builder.ins().iconst(types::I64, *n as i64);
3366 let call = builder.ins().call(
3367 weighted_pick_func_ref,
3368 &[input, v_ptr, b_ptr, p_ptr, a_ptr, n_val],
3369 );
3370 let result = builder.inst_results(call)[0];
3371 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3372 }
3373
3374 JitOp::MathUnary(idx) => {
3375 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3376 let func_ref = math_unary_refs[*idx as usize];
3377 let call = builder.ins().call(func_ref, &[input]);
3378 let result = builder.inst_results(call)[0];
3379 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3380 }
3381
3382 JitOp::MathBinary(idx) => {
3383 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3384 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3385 let func_ref = math_binary_refs[*idx as usize];
3386 let call = builder.ins().call(func_ref, &[a, b]);
3387 let result = builder.inst_results(call)[0];
3388 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3389 }
3390
3391 JitOp::ToF64 => {
3392 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3393 let fval = builder.ins().fcvt_from_uint(types::F64, val);
3394 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], fval);
3395 }
3396
3397 JitOp::RegBinOp(lane, arith) => {
3399 let vt = reg_lane_type(*lane);
3400 let a = load_reg128(&mut builder, buffer_ptr, input_slots[0], vt);
3401 let b = load_reg128(&mut builder, buffer_ptr, input_slots[2], vt);
3402 let is_float = matches!(*lane, 4 | 5);
3403 let r = match (arith, is_float) {
3404 (0, false) => builder.ins().iadd(a, b),
3405 (1, false) => builder.ins().isub(a, b),
3406 (2, false) => builder.ins().imul(a, b),
3407 (0, true) => builder.ins().fadd(a, b),
3408 (1, true) => builder.ins().fsub(a, b),
3409 (2, true) => builder.ins().fmul(a, b),
3410 _ => unreachable!("RegBinOp arith index out of range"),
3411 };
3412 store_reg128(&mut builder, buffer_ptr, output_slots[0], r);
3413 }
3414 JitOp::RegCopy => {
3415 let v = load_reg128(&mut builder, buffer_ptr, input_slots[0], types::I64X2);
3416 store_reg128(&mut builder, buffer_ptr, output_slots[0], v);
3417 }
3418 JitOp::RegSplat(lane) => {
3419 let vt = reg_lane_type(*lane);
3420 let scalar = match *lane {
3421 0 => {
3423 let v = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3424 builder.ins().ireduce(types::I8, v)
3425 }
3426 1 => {
3427 let v = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3428 builder.ins().ireduce(types::I16, v)
3429 }
3430 2 => {
3431 let v = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3432 builder.ins().ireduce(types::I32, v)
3433 }
3434 3 => load_slot(&mut builder, buffer_ptr, input_slots[0]),
3435 4 => {
3437 let f = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3438 builder.ins().fdemote(types::F32, f)
3439 }
3440 5 => load_slot_f64(&mut builder, buffer_ptr, input_slots[0]),
3441 _ => unreachable!("RegSplat lane index out of range"),
3442 };
3443 let v = builder.ins().splat(vt, scalar);
3444 store_reg128(&mut builder, buffer_ptr, output_slots[0], v);
3445 }
3446
3447 JitOp::U64Add2 => {
3449 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3450 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3451 let result = builder.ins().iadd(a, b);
3452 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3453 }
3454 JitOp::U64Sub2 => {
3455 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3456 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3457 let result = builder.ins().isub(a, b);
3458 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3459 }
3460 JitOp::U64Mul2 => {
3461 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3462 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3463 let result = builder.ins().imul(a, b);
3464 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3465 }
3466 JitOp::U64Div2 => {
3467 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3468 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3469 let zero = builder.ins().iconst(types::I64, 0);
3472 let is_zero = builder.ins().icmp(ir::condcodes::IntCC::Equal, b, zero);
3473 let div_block = builder.create_block();
3474 let merge_block = builder.create_block();
3475 builder.append_block_param(merge_block, types::I64);
3476 builder
3477 .ins()
3478 .brif(is_zero, merge_block, &[zero], div_block, &[]);
3479 builder.switch_to_block(div_block);
3480 builder.seal_block(div_block);
3481 let div_result = builder.ins().udiv(a, b);
3482 builder.ins().jump(merge_block, &[div_result]);
3483 builder.switch_to_block(merge_block);
3484 builder.seal_block(merge_block);
3485 let result = builder.block_params(merge_block)[0];
3486 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3487 }
3488 JitOp::U64Mod2 => {
3489 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3490 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3491 let zero = builder.ins().iconst(types::I64, 0);
3494 let is_zero = builder.ins().icmp(ir::condcodes::IntCC::Equal, b, zero);
3495 let rem_block = builder.create_block();
3496 let merge_block = builder.create_block();
3497 builder.append_block_param(merge_block, types::I64);
3498 builder
3499 .ins()
3500 .brif(is_zero, merge_block, &[zero], rem_block, &[]);
3501 builder.switch_to_block(rem_block);
3502 builder.seal_block(rem_block);
3503 let rem_result = builder.ins().urem(a, b);
3504 builder.ins().jump(merge_block, &[rem_result]);
3505 builder.switch_to_block(merge_block);
3506 builder.seal_block(merge_block);
3507 let result = builder.block_params(merge_block)[0];
3508 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3509 }
3510 JitOp::U64And => {
3511 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3512 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3513 let result = builder.ins().band(a, b);
3514 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3515 }
3516 JitOp::U64Or => {
3517 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3518 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3519 let result = builder.ins().bor(a, b);
3520 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3521 }
3522 JitOp::U64Xor => {
3523 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3524 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3525 let result = builder.ins().bxor(a, b);
3526 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3527 }
3528 JitOp::U64Shl => {
3529 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3530 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3531 let result = builder.ins().ishl(a, b);
3532 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3533 }
3534 JitOp::U64Shr => {
3535 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3536 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3537 let result = builder.ins().ushr(a, b);
3538 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3539 }
3540 JitOp::U64Not => {
3541 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3542 let result = builder.ins().bnot(a);
3543 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3544 }
3545
3546 JitOp::F64Add => {
3548 let a = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3549 let b = load_slot_f64(&mut builder, buffer_ptr, input_slots[1]);
3550 let result = builder.ins().fadd(a, b);
3551 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], result);
3552 }
3553 JitOp::F64Sub => {
3554 let a = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3555 let b = load_slot_f64(&mut builder, buffer_ptr, input_slots[1]);
3556 let result = builder.ins().fsub(a, b);
3557 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], result);
3558 }
3559 JitOp::F64Mul => {
3560 let a = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3561 let b = load_slot_f64(&mut builder, buffer_ptr, input_slots[1]);
3562 let result = builder.ins().fmul(a, b);
3563 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], result);
3564 }
3565 JitOp::F64Div => {
3566 let a = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3567 let b = load_slot_f64(&mut builder, buffer_ptr, input_slots[1]);
3568 let zero = builder.ins().f64const(0.0);
3570 let is_zero = builder.ins().fcmp(ir::condcodes::FloatCC::Equal, b, zero);
3571 let div_result = builder.ins().fdiv(a, b);
3572 let result = builder.ins().select(is_zero, zero, div_result);
3573 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], result);
3574 }
3575 JitOp::F64Mod => {
3576 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3578 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3579 let call = builder
3580 .ins()
3581 .call(math_binary_refs[F64_MOD_HELPER], &[a, b]);
3582 let result = builder.inst_results(call)[0];
3583 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3584 }
3585
3586 JitOp::IsPositiveCheck { name_ptr, name_len } => {
3587 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3593 let zero = builder.ins().iconst(types::I64, 0);
3594 let is_zero = builder.ins().icmp(ir::condcodes::IntCC::Equal, val, zero);
3595 let fail_block = builder.create_block();
3596 let ok_block = builder.create_block();
3597 builder.ins().brif(is_zero, fail_block, &[], ok_block, &[]);
3598
3599 builder.switch_to_block(fail_block);
3600 builder.seal_block(fail_block);
3601 let np = builder.ins().iconst(types::I64, *name_ptr as i64);
3602 let nl = builder.ins().iconst(types::I64, *name_len as i64);
3603 let _ = builder.ins().call(is_positive_fail_ref, &[val, np, nl]);
3604 builder.ins().jump(ok_block, &[]);
3608
3609 builder.switch_to_block(ok_block);
3610 builder.seal_block(ok_block);
3611 store_slot(&mut builder, buffer_ptr, output_slots[0], val);
3612 }
3613
3614 JitOp::InRangeCheck(lo, hi) => {
3615 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3619 let lo_v = builder.ins().iconst(types::I64, *lo as i64);
3620 let hi_v = builder.ins().iconst(types::I64, *hi as i64);
3621 let below =
3622 builder
3623 .ins()
3624 .icmp(ir::condcodes::IntCC::UnsignedLessThan, val, lo_v);
3625 let above =
3626 builder
3627 .ins()
3628 .icmp(ir::condcodes::IntCC::UnsignedGreaterThan, val, hi_v);
3629 let out_of_range = builder.ins().bor(below, above);
3630
3631 let fail_block = builder.create_block();
3632 let ok_block = builder.create_block();
3633 builder
3634 .ins()
3635 .brif(out_of_range, fail_block, &[], ok_block, &[]);
3636
3637 builder.switch_to_block(fail_block);
3638 builder.seal_block(fail_block);
3639 let _ = builder.ins().call(in_range_fail_ref, &[val, lo_v, hi_v]);
3640 builder.ins().jump(ok_block, &[]);
3641
3642 builder.switch_to_block(ok_block);
3643 builder.seal_block(ok_block);
3644 store_slot(&mut builder, buffer_ptr, output_slots[0], val);
3645 }
3646
3647 JitOp::IsOneOfCheck {
3648 allowed,
3649 set_ptr,
3650 set_len,
3651 } => {
3652 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3658 let mut any_match = builder.ins().iconst(types::I8, 0);
3659 for allow in allowed.iter() {
3660 let c = builder.ins().iconst(types::I64, *allow as i64);
3661 let eq = builder.ins().icmp(ir::condcodes::IntCC::Equal, val, c);
3662 any_match = builder.ins().bor(any_match, eq);
3663 }
3664 let fail_block = builder.create_block();
3665 let ok_block = builder.create_block();
3666 builder
3670 .ins()
3671 .brif(any_match, ok_block, &[], fail_block, &[]);
3672
3673 builder.switch_to_block(fail_block);
3674 builder.seal_block(fail_block);
3675 let sp = builder.ins().iconst(types::I64, *set_ptr as i64);
3676 let sl = builder.ins().iconst(types::I64, *set_len as i64);
3677 let _ = builder.ins().call(is_one_of_fail_ref, &[val, sp, sl]);
3678 builder.ins().jump(ok_block, &[]);
3679
3680 builder.switch_to_block(ok_block);
3681 builder.seal_block(ok_block);
3682 store_slot(&mut builder, buffer_ptr, output_slots[0], val);
3683 }
3684
3685 JitOp::U64Cmp(cc) => {
3686 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3687 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3688 let cmp = builder.ins().icmp(*cc, a, b);
3689 let zero = builder.ins().iconst(types::I64, 0);
3690 let one = builder.ins().iconst(types::I64, 1);
3691 let result = builder.ins().select(cmp, one, zero);
3692 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3693 }
3694 JitOp::F64Cmp(cc) => {
3695 let a = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3696 let b = load_slot_f64(
3697 &mut builder,
3698 buffer_ptr,
3699 if input_slots.len() > 1 {
3700 input_slots[1]
3701 } else {
3702 input_slots[0]
3703 },
3704 );
3705 let cmp = builder.ins().fcmp(*cc, a, b);
3706 let zero = builder.ins().iconst(types::I64, 0);
3707 let one = builder.ins().iconst(types::I64, 1);
3708 let result = builder.ins().select(cmp, one, zero);
3709 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3710 }
3711 JitOp::SelectU64 => {
3712 let cond = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3713 let a = load_slot(
3714 &mut builder,
3715 buffer_ptr,
3716 if input_slots.len() > 1 {
3717 input_slots[1]
3718 } else {
3719 input_slots[0]
3720 },
3721 );
3722 let b = load_slot(
3723 &mut builder,
3724 buffer_ptr,
3725 if input_slots.len() > 2 {
3726 input_slots[2]
3727 } else {
3728 input_slots[0]
3729 },
3730 );
3731 let zero = builder.ins().iconst(types::I64, 0);
3732 let is_nonzero = builder
3733 .ins()
3734 .icmp(ir::condcodes::IntCC::NotEqual, cond, zero);
3735 let result = builder.ins().select(is_nonzero, a, b);
3736 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3737 }
3738 JitOp::SelectF64 => {
3739 let cond = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3740 let a = load_slot_f64(
3741 &mut builder,
3742 buffer_ptr,
3743 if input_slots.len() > 1 {
3744 input_slots[1]
3745 } else {
3746 input_slots[0]
3747 },
3748 );
3749 let b = load_slot_f64(
3750 &mut builder,
3751 buffer_ptr,
3752 if input_slots.len() > 2 {
3753 input_slots[2]
3754 } else {
3755 input_slots[0]
3756 },
3757 );
3758 let zero = builder.ins().iconst(types::I64, 0);
3759 let is_nonzero = builder
3760 .ins()
3761 .icmp(ir::condcodes::IntCC::NotEqual, cond, zero);
3762 let result = builder.ins().select(is_nonzero, a, b);
3763 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], result);
3764 }
3765
3766 JitOp::I64ToF64 => {
3767 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3768 let fval = builder.ins().fcvt_from_sint(types::F64, val);
3769 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], fval);
3770 }
3771 JitOp::F64ToI64 => {
3772 let fval = load_slot_f64(&mut builder, buffer_ptr, input_slots[0]);
3773 let ival = builder.ins().fcvt_to_sint(types::I64, fval);
3774 store_slot(&mut builder, buffer_ptr, output_slots[0], ival);
3775 }
3776 JitOp::SignExtendI32 => {
3777 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3778 let i32_val = builder.ins().ireduce(types::I32, val);
3779 let sext_val = builder.ins().sextend(types::I64, i32_val);
3780 store_slot(&mut builder, buffer_ptr, output_slots[0], sext_val);
3781 }
3782 JitOp::SignExtendI16 => {
3783 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3784 let i16_val = builder.ins().ireduce(types::I16, val);
3785 let sext_val = builder.ins().sextend(types::I64, i16_val);
3786 store_slot(&mut builder, buffer_ptr, output_slots[0], sext_val);
3787 }
3788 JitOp::SignExtendI8 => {
3789 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3790 let i8_val = builder.ins().ireduce(types::I8, val);
3791 let sext_val = builder.ins().sextend(types::I64, i8_val);
3792 store_slot(&mut builder, buffer_ptr, output_slots[0], sext_val);
3793 }
3794 JitOp::ZeroExtendU32 => {
3795 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3796 let mask = builder.ins().iconst(types::I64, 0xFFFFFFFFu64 as i64);
3797 let result = builder.ins().band(val, mask);
3798 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3799 }
3800 JitOp::ZeroExtendU16 => {
3801 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3802 let mask = builder.ins().iconst(types::I64, 0xFFFFu64 as i64);
3803 let result = builder.ins().band(val, mask);
3804 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3805 }
3806 JitOp::ZeroExtendU8 => {
3807 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3808 let mask = builder.ins().iconst(types::I64, 0xFFu64 as i64);
3809 let result = builder.ins().band(val, mask);
3810 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3811 }
3812 JitOp::ToBool => {
3813 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3814 let zero = builder.ins().iconst(types::I64, 0);
3815 let one = builder.ins().iconst(types::I64, 1);
3816 let cmp = builder
3817 .ins()
3818 .icmp(ir::condcodes::IntCC::NotEqual, val, zero);
3819 let result = builder.ins().select(cmp, one, zero);
3820 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3821 }
3822 JitOp::ConstU64(v) | JitOp::ConstF64(v) => {
3823 let result = builder.ins().iconst(types::I64, *v as i64);
3824 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
3825 }
3826 JitOp::HashRangeConst(max) => {
3827 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3828 let c_gamma = builder
3829 .ins()
3830 .iconst(types::I64, 0x9e3779b97f4a7c15u64 as i64);
3831 let x1 = builder.ins().iadd(input, c_gamma);
3832 let s30 = builder.ins().ushr_imm(x1, 30);
3833 let x2 = builder.ins().bxor(x1, s30);
3834 let c_m1 = builder
3835 .ins()
3836 .iconst(types::I64, 0xbf58476d1ce4e5b9u64 as i64);
3837 let x3 = builder.ins().imul(x2, c_m1);
3838 let s27 = builder.ins().ushr_imm(x3, 27);
3839 let x4 = builder.ins().bxor(x3, s27);
3840 let c_m2 = builder
3841 .ins()
3842 .iconst(types::I64, 0x94d049bb133111ebu64 as i64);
3843 let x5 = builder.ins().imul(x4, c_m2);
3844 let s31 = builder.ins().ushr_imm(x5, 31);
3845 let h = builder.ins().bxor(x5, s31);
3846 if *max == 0 {
3847 let zero = builder.ins().iconst(types::I64, 0);
3848 store_slot(&mut builder, buffer_ptr, output_slots[0], zero);
3849 } else {
3850 let m = builder.ins().iconst(types::I64, *max as i64);
3851 let rem = builder.ins().urem(h, m);
3852 store_slot(&mut builder, buffer_ptr, output_slots[0], rem);
3853 }
3854 }
3855 JitOp::HashIntervalConst(min_bits, max_bits) => {
3856 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3857 let c_gamma = builder
3858 .ins()
3859 .iconst(types::I64, 0x9e3779b97f4a7c15u64 as i64);
3860 let x1 = builder.ins().iadd(input, c_gamma);
3861 let s30 = builder.ins().ushr_imm(x1, 30);
3862 let x2 = builder.ins().bxor(x1, s30);
3863 let c_m1 = builder
3864 .ins()
3865 .iconst(types::I64, 0xbf58476d1ce4e5b9u64 as i64);
3866 let x3 = builder.ins().imul(x2, c_m1);
3867 let s27 = builder.ins().ushr_imm(x3, 27);
3868 let x4 = builder.ins().bxor(x3, s27);
3869 let c_m2 = builder
3870 .ins()
3871 .iconst(types::I64, 0x94d049bb133111ebu64 as i64);
3872 let x5 = builder.ins().imul(x4, c_m2);
3873 let s31 = builder.ins().ushr_imm(x5, 31);
3874 let h = builder.ins().bxor(x5, s31);
3875
3876 let h_f = builder.ins().fcvt_from_uint(types::F64, h);
3877 let denom = builder.ins().f64const(u64::MAX as f64);
3878 let unit = builder.ins().fdiv(h_f, denom);
3879 let min_f = f64::from_bits(*min_bits);
3880 let max_f = f64::from_bits(*max_bits);
3881 let span = builder.ins().f64const(max_f - min_f);
3882 let min_val = builder.ins().f64const(min_f);
3883 let scaled = builder.ins().fmul(unit, span);
3884 let res_f = builder.ins().fadd(min_val, scaled);
3885 let res = builder
3886 .ins()
3887 .bitcast(types::I64, ir::MemFlags::new(), res_f);
3888 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
3889 }
3890 JitOp::InvLerpConst(a_bits, b_bits) => {
3891 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3892 let in_f = builder
3893 .ins()
3894 .bitcast(types::F64, ir::MemFlags::new(), input);
3895 let a_f = f64::from_bits(*a_bits);
3896 let b_f = f64::from_bits(*b_bits);
3897 let a_val = builder.ins().f64const(a_f);
3898 let inv_span = builder.ins().f64const(1.0 / (b_f - a_f));
3902 let diff = builder.ins().fsub(in_f, a_val);
3903 let t = builder.ins().fmul(diff, inv_span);
3904 let zero = builder.ins().f64const(0.0);
3905 let one = builder.ins().f64const(1.0);
3906 let res_f = clamp_ir(&mut builder, t, zero, one);
3907 let res = builder
3908 .ins()
3909 .bitcast(types::I64, ir::MemFlags::new(), res_f);
3910 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
3911 }
3912 JitOp::RemapConst(in_min_bits, in_max_bits, out_min_bits, out_max_bits) => {
3913 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3914 let in_f = builder
3915 .ins()
3916 .bitcast(types::F64, ir::MemFlags::new(), input);
3917 let in_min = f64::from_bits(*in_min_bits);
3918 let in_max = f64::from_bits(*in_max_bits);
3919 let out_min = f64::from_bits(*out_min_bits);
3920 let out_max = f64::from_bits(*out_max_bits);
3921 let in_span_val = builder.ins().f64const(in_max - in_min);
3925 let in_min_val = builder.ins().f64const(in_min);
3926 let out_min_val = builder.ins().f64const(out_min);
3927 let out_span_val = builder.ins().f64const(out_max - out_min);
3928 let diff = builder.ins().fsub(in_f, in_min_val);
3929 let t = builder.ins().fdiv(diff, in_span_val);
3930 let scaled = builder.ins().fmul(t, out_span_val);
3931 let res_f = builder.ins().fadd(out_min_val, scaled);
3932 let res = builder
3933 .ins()
3934 .bitcast(types::I64, ir::MemFlags::new(), res_f);
3935 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
3936 }
3937 JitOp::EpochOffsetConst(base) => {
3938 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3939 let b = builder.ins().iconst(types::I64, *base as i64);
3940 let res = builder.ins().iadd(val, b);
3941 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
3942 }
3943 JitOp::EpochScaleConst(factor) => {
3944 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3945 let f = builder.ins().iconst(types::I64, *factor as i64);
3946 let res = builder.ins().imul(val, f);
3947 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
3948 }
3949 JitOp::ThreadId => {
3950 let call = builder.ins().call(thread_id_func_ref, &[]);
3951 let res = builder.inst_results(call)[0];
3952 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
3953 }
3954 JitOp::CurrentEpochMillis => {
3955 let call = builder.ins().call(current_epoch_millis_func_ref, &[]);
3956 let res = builder.inst_results(call)[0];
3957 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
3958 }
3959 JitOp::Perlin1dConst(perm_ptr, freq_bits) => {
3960 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3961 let p = builder.ins().iconst(types::I64, *perm_ptr as i64);
3962 let fb = builder.ins().iconst(types::I64, *freq_bits as i64);
3963 let call = builder.ins().call(perlin_1d_func_ref, &[input, p, fb]);
3964 let res = builder.inst_results(call)[0];
3965 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
3966 }
3967 JitOp::Perlin2dConst(perm_ptr, freq_bits) => {
3968 let x = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3969 let y = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3970 let p = builder.ins().iconst(types::I64, *perm_ptr as i64);
3971 let fb = builder.ins().iconst(types::I64, *freq_bits as i64);
3972 let call = builder.ins().call(perlin_2d_func_ref, &[x, y, p, fb]);
3973 let res = builder.inst_results(call)[0];
3974 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
3975 }
3976 JitOp::Simplex2dConst(perm_ptr, freq_bits) => {
3977 let x = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3978 let y = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3979 let p = builder.ins().iconst(types::I64, *perm_ptr as i64);
3980 let fb = builder.ins().iconst(types::I64, *freq_bits as i64);
3981 let call = builder.ins().call(simplex_2d_func_ref, &[x, y, p, fb]);
3982 let res = builder.inst_results(call)[0];
3983 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
3984 }
3985 JitOp::FractalNoise1dConst(perm_ptr, freq_bits, octaves) => {
3986 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3987 let p = builder.ins().iconst(types::I64, *perm_ptr as i64);
3988 let fb = builder.ins().iconst(types::I64, *freq_bits as i64);
3989 let oct = builder.ins().iconst(types::I64, *octaves as i64);
3990 let call = builder
3991 .ins()
3992 .call(fractal_noise_1d_func_ref, &[input, p, fb, oct]);
3993 let res = builder.inst_results(call)[0];
3994 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
3995 }
3996 JitOp::FractalNoise2dConst(perm_ptr, freq_bits, octaves) => {
3997 let x = load_slot(&mut builder, buffer_ptr, input_slots[0]);
3998 let y = load_slot(&mut builder, buffer_ptr, input_slots[1]);
3999 let p = builder.ins().iconst(types::I64, *perm_ptr as i64);
4000 let fb = builder.ins().iconst(types::I64, *freq_bits as i64);
4001 let oct = builder.ins().iconst(types::I64, *octaves as i64);
4002 let call = builder
4003 .ins()
4004 .call(fractal_noise_2d_func_ref, &[x, y, p, fb, oct]);
4005 let res = builder.inst_results(call)[0];
4006 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
4007 }
4008 JitOp::CycleWalkConst(range, seed, inc) => {
4009 let pos = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4010 let r = builder.ins().iconst(types::I64, *range as i64);
4011 let s = builder.ins().iconst(types::I64, *seed as i64);
4012 let i = builder.ins().iconst(types::I64, *inc as i64);
4013 let call = builder.ins().call(cycle_walk_func_ref, &[pos, r, s, i]);
4014 let res = builder.inst_results(call)[0];
4015 store_slot(&mut builder, buffer_ptr, output_slots[0], res);
4016 }
4017
4018 JitOp::VariadicSum => {
4019 if input_slots.is_empty() {
4020 let zero = builder.ins().iconst(types::I64, 0);
4021 store_slot(&mut builder, buffer_ptr, output_slots[0], zero);
4022 } else {
4023 let mut acc = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4024 for &slot in &input_slots[1..] {
4025 let v = load_slot(&mut builder, buffer_ptr, slot);
4026 acc = builder.ins().iadd(acc, v);
4027 }
4028 store_slot(&mut builder, buffer_ptr, output_slots[0], acc);
4029 }
4030 }
4031 JitOp::VariadicProduct => {
4032 if input_slots.is_empty() {
4033 let one = builder.ins().iconst(types::I64, 1);
4034 store_slot(&mut builder, buffer_ptr, output_slots[0], one);
4035 } else {
4036 let mut acc = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4037 for &slot in &input_slots[1..] {
4038 let v = load_slot(&mut builder, buffer_ptr, slot);
4039 acc = builder.ins().imul(acc, v);
4040 }
4041 store_slot(&mut builder, buffer_ptr, output_slots[0], acc);
4042 }
4043 }
4044 JitOp::VariadicMin => {
4045 if input_slots.is_empty() {
4046 let zero = builder.ins().iconst(types::I64, 0);
4047 store_slot(&mut builder, buffer_ptr, output_slots[0], zero);
4048 } else {
4049 let mut acc = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4050 for &slot in &input_slots[1..] {
4051 let v = load_slot(&mut builder, buffer_ptr, slot);
4052 let cmp =
4053 builder
4054 .ins()
4055 .icmp(ir::condcodes::IntCC::UnsignedLessThan, v, acc);
4056 acc = builder.ins().select(cmp, v, acc);
4057 }
4058 store_slot(&mut builder, buffer_ptr, output_slots[0], acc);
4059 }
4060 }
4061 JitOp::VariadicMax => {
4062 if input_slots.is_empty() {
4063 let zero = builder.ins().iconst(types::I64, 0);
4064 store_slot(&mut builder, buffer_ptr, output_slots[0], zero);
4065 } else {
4066 let mut acc = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4067 for &slot in &input_slots[1..] {
4068 let v = load_slot(&mut builder, buffer_ptr, slot);
4069 let cmp = builder.ins().icmp(
4070 ir::condcodes::IntCC::UnsignedGreaterThan,
4071 v,
4072 acc,
4073 );
4074 acc = builder.ins().select(cmp, v, acc);
4075 }
4076 store_slot(&mut builder, buffer_ptr, output_slots[0], acc);
4077 }
4078 }
4079
4080 JitOp::CeilToMultiple => {
4081 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4082 let m = load_slot(&mut builder, buffer_ptr, input_slots[1]);
4083 let zero = builder.ins().iconst(types::I64, 0);
4084 let one = builder.ins().iconst(types::I64, 1);
4085 let is_zero = builder.ins().icmp(ir::condcodes::IntCC::Equal, m, zero);
4086 let calc_block = builder.create_block();
4087 let merge_block = builder.create_block();
4088 builder.append_block_param(merge_block, types::I64);
4089 builder
4090 .ins()
4091 .brif(is_zero, merge_block, &[val], calc_block, &[]);
4092 builder.switch_to_block(calc_block);
4093 builder.seal_block(calc_block);
4094 let div = div_ceil(&mut builder, val, m, one);
4097 let high = builder.ins().umulhi(div, m);
4098 let low = builder.ins().imul(div, m);
4099 let zero_hi = builder.ins().iconst(types::I64, 0);
4100 let overflows =
4101 builder
4102 .ins()
4103 .icmp(ir::condcodes::IntCC::NotEqual, high, zero_hi);
4104 let max = builder.ins().iconst(types::I64, -1);
4105 let mul = builder.ins().select(overflows, max, low);
4106 builder.ins().jump(merge_block, &[mul]);
4107 builder.switch_to_block(merge_block);
4108 builder.seal_block(merge_block);
4109 let result = builder.block_params(merge_block)[0];
4110 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
4111 }
4112 JitOp::CheckedAdd => {
4113 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4114 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
4115 let sum = builder.ins().iadd(a, b);
4116 let is_overflow =
4117 builder
4118 .ins()
4119 .icmp(ir::condcodes::IntCC::UnsignedLessThan, sum, a);
4120 let zero = builder.ins().iconst(types::I64, 0);
4121 let result = builder.ins().select(is_overflow, zero, sum);
4122 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
4123 }
4124 JitOp::CheckedSub => {
4125 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4126 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
4127 let is_lt = builder
4128 .ins()
4129 .icmp(ir::condcodes::IntCC::UnsignedLessThan, a, b);
4130 let diff = builder.ins().isub(a, b);
4131 let zero = builder.ins().iconst(types::I64, 0);
4132 let result = builder.ins().select(is_lt, zero, diff);
4133 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
4134 }
4135 JitOp::CheckedMul => {
4136 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4137 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
4138 let prod = builder.ins().imul(a, b);
4139 let zero = builder.ins().iconst(types::I64, 0);
4140 let a_is_zero = builder.ins().icmp(ir::condcodes::IntCC::Equal, a, zero);
4141 let div_block = builder.create_block();
4142 let merge_block = builder.create_block();
4143 builder.append_block_param(merge_block, types::I64);
4144 builder
4145 .ins()
4146 .brif(a_is_zero, merge_block, &[zero], div_block, &[]);
4147 builder.switch_to_block(div_block);
4148 builder.seal_block(div_block);
4149 let div = builder.ins().udiv(prod, a);
4150 let ok = builder.ins().icmp(ir::condcodes::IntCC::Equal, div, b);
4151 let mul_res = builder.ins().select(ok, prod, zero);
4152 builder.ins().jump(merge_block, &[mul_res]);
4153 builder.switch_to_block(merge_block);
4154 builder.seal_block(merge_block);
4155 let result = builder.block_params(merge_block)[0];
4156 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
4157 }
4158 JitOp::MultiplesAtLeast => {
4159 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4160 let m = load_slot(&mut builder, buffer_ptr, input_slots[1]);
4161 let zero = builder.ins().iconst(types::I64, 0);
4162 let one = builder.ins().iconst(types::I64, 1);
4163 let is_zero = builder.ins().icmp(ir::condcodes::IntCC::Equal, m, zero);
4164 let calc_block = builder.create_block();
4165 let merge_block = builder.create_block();
4166 builder.append_block_param(merge_block, types::I64);
4167 builder
4168 .ins()
4169 .brif(is_zero, merge_block, &[zero], calc_block, &[]);
4170 builder.switch_to_block(calc_block);
4171 builder.seal_block(calc_block);
4172 let div = div_ceil(&mut builder, val, m, one);
4173 builder.ins().jump(merge_block, &[div]);
4174 builder.switch_to_block(merge_block);
4175 builder.seal_block(merge_block);
4176 let result = builder.block_params(merge_block)[0];
4177 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
4178 }
4179
4180 JitOp::BlendConst(mix_bits) => {
4181 let a = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4186 let b = load_slot(&mut builder, buffer_ptr, input_slots[1]);
4187 let fa = builder.ins().bitcast(types::F64, ir::MemFlags::new(), a);
4188 let fb = builder.ins().bitcast(types::F64, ir::MemFlags::new(), b);
4189 let mix_f64 = f64::from_bits(*mix_bits);
4190 let mix_val = builder.ins().f64const(mix_f64);
4191 let one = builder.ins().f64const(1.0);
4192 let one_minus_mix = builder.ins().fsub(one, mix_val);
4193 let a_part = builder.ins().fmul(fa, one_minus_mix);
4194 let b_part = builder.ins().fmul(fb, mix_val);
4195 let sum = builder.ins().fadd(a_part, b_part);
4196 let result = builder.ins().bitcast(types::I64, ir::MemFlags::new(), sum);
4197 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
4198 }
4199 JitOp::LfsrStepConst(feedback) => {
4200 let val = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4201 let feedback = builder.ins().iconst(types::I64, *feedback as i64);
4202 let one = builder.ins().iconst(types::I64, 1);
4203 let zero = builder.ins().iconst(types::I64, 0);
4204 let shifted = builder.ins().ushr(val, one);
4205 let lsb = builder.ins().band(val, one);
4206 let is_odd = builder
4207 .ins()
4208 .icmp(ir::condcodes::IntCC::NotEqual, lsb, zero);
4209 let fb_mask = builder.ins().select(is_odd, feedback, zero);
4210 let result = builder.ins().bxor(shifted, fb_mask);
4211 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
4212 }
4213 JitOp::PcgConst(seed, stream) => {
4214 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4215 let s = builder.ins().iconst(types::I64, *seed as i64);
4216 let st = builder.ins().iconst(types::I64, *stream as i64);
4217 let call = builder.ins().call(pcg_func_ref, &[input, s, st]);
4218 let result = builder.inst_results(call)[0];
4219 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
4220 }
4221 JitOp::PcgStreamConst(seed) => {
4222 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4223 let st = load_slot(&mut builder, buffer_ptr, input_slots[1]);
4224 let s = builder.ins().iconst(types::I64, *seed as i64);
4225 let call = builder.ins().call(pcg_stream_func_ref, &[input, st, s]);
4226 let result = builder.inst_results(call)[0];
4227 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
4228 }
4229 JitOp::NOfConst(n, m) => {
4230 let input = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4231 let n_val = builder.ins().iconst(types::I64, *n as i64);
4232 let m_val = builder.ins().iconst(types::I64, *m as i64);
4233 let call = builder.ins().call(n_of_func_ref, &[input, n_val, m_val]);
4234 let result = builder.inst_results(call)[0];
4235 store_slot(&mut builder, buffer_ptr, output_slots[0], result);
4236 }
4237
4238 JitOp::SlotCall { kit, scratch_base } => {
4239 let n_in = input_slots.len();
4245 let n_out = output_slots.len();
4246 let frame = |builder: &mut FunctionBuilder, n: usize| {
4247 builder.create_sized_stack_slot(ir::StackSlotData::new(
4248 ir::StackSlotKind::ExplicitSlot,
4249 (n.max(1) * 8) as u32,
4250 3,
4251 ))
4252 };
4253 let in_frame = frame(&mut builder, n_in);
4254 let out_frame = frame(&mut builder, n_out);
4255 for (k, &s) in input_slots.iter().enumerate() {
4256 let v = load_slot(&mut builder, buffer_ptr, s);
4257 builder.ins().stack_store(v, in_frame, (k * 8) as i32);
4258 }
4259 let kit_ptr = builder
4260 .ins()
4261 .iconst(types::I64, std::sync::Arc::as_ptr(&kit.0) as usize as i64);
4262 let in_ptr = builder.ins().stack_addr(types::I64, in_frame, 0);
4263 let n_in_v = builder.ins().iconst(types::I64, n_in as i64);
4264 let out_ptr = builder.ins().stack_addr(types::I64, out_frame, 0);
4265 let n_out_v = builder.ins().iconst(types::I64, n_out as i64);
4266 let base_v = builder.ins().iconst(types::I64, *scratch_base as i64);
4267 let n_sc_v = builder.ins().iconst(types::I64, kit.0.scratch.len() as i64);
4268 builder.ins().call(
4269 slot_call_ref,
4270 &[
4271 kit_ptr,
4272 in_ptr,
4273 n_in_v,
4274 out_ptr,
4275 n_out_v,
4276 scratch_ptr,
4277 base_v,
4278 n_sc_v,
4279 ],
4280 );
4281 for (k, &s) in output_slots.iter().enumerate() {
4282 let v = builder
4283 .ins()
4284 .stack_load(types::I64, out_frame, (k * 8) as i32);
4285 store_slot(&mut builder, buffer_ptr, s, v);
4286 }
4287 }
4288
4289 JitOp::U64ToStr { scratch_base }
4290 | JitOp::I64ToStr { scratch_base }
4291 | JitOp::F64ToStr { scratch_base } => {
4292 let func = match jit_op {
4295 JitOp::U64ToStr { .. } => u64_to_str_ref,
4296 JitOp::I64ToStr { .. } => i64_to_str_ref,
4297 _ => f64_to_str_ref,
4298 };
4299 let value = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4300 let base_v = builder.ins().iconst(types::I64, *scratch_base as i64);
4301 let out_v = builder.ins().iconst(types::I64, output_slots[0] as i64);
4302 builder
4303 .ins()
4304 .call(func, &[scratch_ptr, base_v, buffer_ptr, out_v, value]);
4305 }
4306 JitOp::JsonToStr { scratch_base } => {
4307 let ptr = load_slot(&mut builder, buffer_ptr, input_slots[0]);
4308 let len = load_slot(&mut builder, buffer_ptr, input_slots[1]);
4309 let base_v = builder.ins().iconst(types::I64, *scratch_base as i64);
4310 let out_v = builder.ins().iconst(types::I64, output_slots[0] as i64);
4311 builder.ins().call(
4312 json_to_str_ref,
4313 &[scratch_ptr, base_v, buffer_ptr, out_v, ptr, len],
4314 );
4315 }
4316 JitOp::StrConcat { scratch_base } => {
4317 let n_words = input_slots.len();
4320 let frame = builder.create_sized_stack_slot(ir::StackSlotData::new(
4321 ir::StackSlotKind::ExplicitSlot,
4322 (n_words.max(1) * 8) as u32,
4323 3,
4324 ));
4325 for (k, &s) in input_slots.iter().enumerate() {
4326 let v = load_slot(&mut builder, buffer_ptr, s);
4327 builder.ins().stack_store(v, frame, (k * 8) as i32);
4328 }
4329 let pairs_ptr = builder.ins().stack_addr(types::I64, frame, 0);
4330 let n_v = builder.ins().iconst(types::I64, (n_words / 2) as i64);
4331 let base_v = builder.ins().iconst(types::I64, *scratch_base as i64);
4332 let out_v = builder.ins().iconst(types::I64, output_slots[0] as i64);
4333 builder.ins().call(
4334 str_concat_ref,
4335 &[scratch_ptr, base_v, buffer_ptr, out_v, pairs_ptr, n_v],
4336 );
4337 }
4338
4339 JitOp::VecProduce { kind, scratch_base } => {
4340 let func = func_of(&vec_producer_refs, *kind);
4344 let base_v = builder.ins().iconst(types::I64, *scratch_base as i64);
4345 let out_v = builder.ins().iconst(types::I64, output_slots[0] as i64);
4346 let words = load_words(&mut builder, buffer_ptr, input_slots, 4);
4347 let mut args = vec![scratch_ptr, base_v, buffer_ptr, out_v];
4348 args.extend(words);
4349 builder.ins().call(func, &args);
4350 }
4351 JitOp::VecReduce(kind) => {
4352 let func = func_of(&vec_reducer_refs, *kind);
4353 let words = load_words(&mut builder, buffer_ptr, input_slots, 4);
4354 let call = builder.ins().call(func, &words);
4355 let bits = builder.inst_results(call)[0];
4356 store_slot(&mut builder, buffer_ptr, output_slots[0], bits);
4357 }
4358 JitOp::RegLane(kind) => {
4359 let func = func_of(®_lane_refs, *kind);
4360 let words = load_words(&mut builder, buffer_ptr, input_slots, 3);
4361 let call = builder.ins().call(func, &words);
4362 let word = builder.inst_results(call)[0];
4363 store_slot(&mut builder, buffer_ptr, output_slots[0], word);
4364 }
4365 JitOp::RegProduce(kind) => {
4366 let func = func_of(®_producer_refs, *kind);
4367 let out_v = builder.ins().iconst(types::I64, output_slots[0] as i64);
4368 let words = load_words(&mut builder, buffer_ptr, input_slots, 4);
4369 let mut args = vec![buffer_ptr, out_v];
4370 args.extend(words);
4371 builder.ins().call(func, &args);
4372 }
4373 JitOp::RegDotF32 => {
4374 let a = load_reg128(&mut builder, buffer_ptr, input_slots[0], types::F32X4);
4378 let b = load_reg128(&mut builder, buffer_ptr, input_slots[2], types::F32X4);
4379 let p = builder.ins().fmul(a, b);
4380 let p0 = builder.ins().extractlane(p, 0);
4381 let p1 = builder.ins().extractlane(p, 1);
4382 let p2 = builder.ins().extractlane(p, 2);
4383 let p3 = builder.ins().extractlane(p, 3);
4384 let s01 = builder.ins().fadd(p0, p1);
4385 let s23 = builder.ins().fadd(p2, p3);
4386 let s = builder.ins().fadd(s01, s23);
4387 let wide = builder.ins().fpromote(types::F64, s);
4388 store_slot_f64(&mut builder, buffer_ptr, output_slots[0], wide);
4389 }
4390 JitOp::RegShuffleConst(mask) => {
4391 let x = load_reg128(&mut builder, buffer_ptr, input_slots[0], types::I8X16);
4395 let imm = builder
4396 .func
4397 .dfg
4398 .immediates
4399 .push(ir::ConstantData::from(&mask[..]));
4400 let r = builder.ins().shuffle(x, x, imm);
4401 store_reg128(&mut builder, buffer_ptr, output_slots[0], r);
4402 }
4403
4404 JitOp::Fallback => {
4405 }
4408 }
4409 if let Some((inst, mark)) = tracker_store {
4410 let calls = (mark..builder.func.dfg.num_insts()).any(|i| {
4411 builder.func.dfg.insts[ir::Inst::from_u32(i as u32)]
4412 .opcode()
4413 .is_call()
4414 });
4415 if !calls {
4416 builder.func.layout.remove_inst(inst);
4417 }
4418 }
4419
4420 if let (Some(cp), Some(skip)) = (clean_ptr, skip_block) {
4422 let offset = builder.ins().iconst(types::I64, step_idx as i64);
4423 let addr = builder.ins().iadd(cp, offset);
4424 let one = builder.ins().iconst(types::I8, 1);
4425 builder.ins().store(ir::MemFlags::new(), one, addr, 0);
4426 builder.ins().jump(skip, &[]);
4427 builder.switch_to_block(skip);
4428 builder.seal_block(skip);
4429 }
4430 }
4431
4432 builder.ins().return_(&[]);
4433 builder.finalize();
4434 }
4435 let fallible = ctx.func.layout.blocks().any(|block| {
4438 ctx.func
4439 .layout
4440 .block_insts(block)
4441 .any(|inst| ctx.func.dfg.insts[inst].opcode().is_call())
4442 });
4443
4444 module
4445 .define_function(func_id, &mut ctx)
4446 .map_err(|e| format!("define function: {e}"))?;
4447 module.clear_context(&mut ctx);
4448 module
4449 .finalize_definitions()
4450 .map_err(|e| format!("finalize: {e}"))?;
4451
4452 let code_ptr = module.get_finalized_function(func_id);
4453 let kits: Vec<SlotKitRef> = steps
4455 .iter()
4456 .filter_map(|(op, _, _)| op.slot_kit().cloned())
4457 .collect();
4458 let code = super::kernels::JitCode::new(module, kits, fallible);
4459
4460 if provenance {
4461 let prov_fn: NativeProvFn = unsafe { mem::transmute(code_ptr) };
4462 let dummy_raw: NativeFn = unsafe { mem::transmute(code_ptr) };
4463 Ok((dummy_raw, prov_fn, code))
4464 } else {
4465 let raw_fn: NativeFn = unsafe { mem::transmute(code_ptr) };
4466 let dummy_prov: NativeProvFn = unsafe { mem::transmute(code_ptr) };
4467 Ok((raw_fn, dummy_prov, code))
4468 }
4469}
4470
4471fn load_slot(builder: &mut FunctionBuilder, buffer_ptr: ir::Value, slot: usize) -> ir::Value {
4475 let offset = (slot * 8) as i32;
4476 builder
4477 .ins()
4478 .load(types::I64, ir::MemFlags::trusted(), buffer_ptr, offset)
4479}
4480
4481fn store_slot(
4483 builder: &mut FunctionBuilder,
4484 buffer_ptr: ir::Value,
4485 slot: usize,
4486 value: ir::Value,
4487) -> ir::Inst {
4488 let offset = (slot * 8) as i32;
4489 builder
4490 .ins()
4491 .store(ir::MemFlags::trusted(), value, buffer_ptr, offset)
4492}
4493
4494fn reg_lane_type(lane: u8) -> ir::Type {
4497 match lane {
4498 0 => types::I8X16,
4499 1 => types::I16X8,
4500 2 => types::I32X4,
4501 3 => types::I64X2,
4502 4 => types::F32X4,
4503 5 => types::F64X2,
4504 _ => unreachable!("register lane index out of range"),
4505 }
4506}
4507
4508fn load_reg128(
4513 builder: &mut FunctionBuilder,
4514 buffer_ptr: ir::Value,
4515 first_slot: usize,
4516 vt: ir::Type,
4517) -> ir::Value {
4518 let offset = (first_slot * 8) as i32;
4519 builder
4520 .ins()
4521 .load(vt, ir::MemFlags::new(), buffer_ptr, offset)
4522}
4523
4524fn store_reg128(
4526 builder: &mut FunctionBuilder,
4527 buffer_ptr: ir::Value,
4528 first_slot: usize,
4529 value: ir::Value,
4530) {
4531 let offset = (first_slot * 8) as i32;
4532 builder
4533 .ins()
4534 .store(ir::MemFlags::new(), value, buffer_ptr, offset);
4535}
4536
4537fn round_half_away(builder: &mut FunctionBuilder, x: ir::Value) -> ir::Value {
4542 let t = builder.ins().trunc(x);
4543 let frac = builder.ins().fsub(x, t);
4544 let mag = builder.ins().fabs(frac);
4545 let half = builder.ins().f64const(0.5);
4546 let reaches = builder
4547 .ins()
4548 .fcmp(ir::condcodes::FloatCC::GreaterThanOrEqual, mag, half);
4549 let one = builder.ins().f64const(1.0);
4550 let step = builder.ins().fcopysign(one, x);
4551 let up = builder.ins().fadd(t, step);
4552 builder.ins().select(reaches, up, t)
4553}
4554
4555fn clamp_ir(
4560 builder: &mut FunctionBuilder,
4561 x: ir::Value,
4562 lo: ir::Value,
4563 hi: ir::Value,
4564) -> ir::Value {
4565 let below = builder.ins().fcmp(ir::condcodes::FloatCC::LessThan, x, lo);
4566 let above = builder
4567 .ins()
4568 .fcmp(ir::condcodes::FloatCC::GreaterThan, x, hi);
4569 let capped = builder.ins().select(above, hi, x);
4570 builder.ins().select(below, lo, capped)
4571}
4572
4573fn div_ceil(
4576 builder: &mut FunctionBuilder,
4577 val: ir::Value,
4578 m: ir::Value,
4579 one: ir::Value,
4580) -> ir::Value {
4581 let q = builder.ins().udiv(val, m);
4582 let r = builder.ins().urem(val, m);
4583 let zero = builder.ins().iconst(types::I64, 0);
4584 let inexact = builder.ins().icmp(ir::condcodes::IntCC::NotEqual, r, zero);
4585 let q1 = builder.ins().iadd(q, one);
4586 builder.ins().select(inexact, q1, q)
4587}
4588
4589fn func_of<K: PartialEq + Copy>(refs: &[(K, ir::FuncRef)], key: K) -> ir::FuncRef {
4591 refs.iter()
4592 .find(|(k, _)| *k == key)
4593 .map(|(_, r)| *r)
4594 .expect("every helper of the group is declared")
4595}
4596
4597fn load_words(
4600 builder: &mut FunctionBuilder,
4601 buffer_ptr: ir::Value,
4602 input_slots: &[usize],
4603 n: usize,
4604) -> Vec<ir::Value> {
4605 (0..n)
4606 .map(|k| match input_slots.get(k) {
4607 Some(&s) => load_slot(builder, buffer_ptr, s),
4608 None => builder.ins().iconst(types::I64, 0),
4609 })
4610 .collect()
4611}
4612
4613fn load_slot_f64(builder: &mut FunctionBuilder, buffer_ptr: ir::Value, slot: usize) -> ir::Value {
4615 let i64_val = load_slot(builder, buffer_ptr, slot);
4616 builder
4617 .ins()
4618 .bitcast(types::F64, ir::MemFlags::new(), i64_val)
4619}
4620
4621fn store_slot_f64(
4623 builder: &mut FunctionBuilder,
4624 buffer_ptr: ir::Value,
4625 slot: usize,
4626 value: ir::Value,
4627) {
4628 let i64_val = builder
4629 .ins()
4630 .bitcast(types::I64, ir::MemFlags::new(), value);
4631 store_slot(builder, buffer_ptr, slot, i64_val);
4632}
4633
4634#[cfg(test)]
4637mod tests {
4638 use super::*;
4639
4640 #[test]
4641 fn jit_identity() {
4642 let steps = vec![(JitOp::Identity, vec![0], vec![1])];
4643 let mut output_map = HashMap::new();
4644 output_map.insert("out".into(), 1);
4645 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4646 kernel.eval(&[42]);
4647 assert_eq!(kernel.get("out"), 42);
4648 }
4649
4650 #[test]
4651 fn jit_add_const() {
4652 let steps = vec![(JitOp::AddConst(100), vec![0], vec![1])];
4653 let mut output_map = HashMap::new();
4654 output_map.insert("out".into(), 1);
4655 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4656 kernel.eval(&[5]);
4657 assert_eq!(kernel.get("out"), 105);
4658 }
4659
4660 #[test]
4661 fn jit_mul_const() {
4662 let steps = vec![(JitOp::MulConst(7), vec![0], vec![1])];
4663 let mut output_map = HashMap::new();
4664 output_map.insert("out".into(), 1);
4665 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4666 kernel.eval(&[6]);
4667 assert_eq!(kernel.get("out"), 42);
4668 }
4669
4670 #[test]
4671 fn jit_mod_const() {
4672 let steps = vec![(JitOp::ModConst(100), vec![0], vec![1])];
4673 let mut output_map = HashMap::new();
4674 output_map.insert("out".into(), 1);
4675 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4676 kernel.eval(&[542]);
4677 assert_eq!(kernel.get("out"), 42);
4678 }
4679
4680 #[test]
4681 fn jit_hash() {
4682 let steps = vec![(JitOp::Hash, vec![0], vec![1])];
4683 let mut output_map = HashMap::new();
4684 output_map.insert("out".into(), 1);
4685 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4686
4687 kernel.eval(&[42]);
4688 let v1 = kernel.get("out");
4689
4690 let expected = xxhash_rust::xxh3::xxh3_64(&42u64.to_le_bytes());
4692 assert_eq!(v1, expected);
4693 }
4694
4695 #[test]
4696 fn jit_hash_deterministic() {
4697 let steps = vec![(JitOp::Hash, vec![0], vec![1])];
4698 let mut output_map = HashMap::new();
4699 output_map.insert("out".into(), 1);
4700 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4701
4702 kernel.eval(&[42]);
4703 let v1 = kernel.get("out");
4704 kernel.eval(&[42]);
4705 let v2 = kernel.get("out");
4706 assert_eq!(v1, v2);
4707 }
4708
4709 #[test]
4710 fn jit_chain_hash_mod() {
4711 let steps = vec![
4713 (JitOp::Hash, vec![0], vec![1]), (JitOp::ModConst(1_000_000), vec![1], vec![2]), ];
4716 let mut output_map = HashMap::new();
4717 output_map.insert("user_id".into(), 2);
4718 let mut kernel = compile_jit_raw(1, 3, steps, output_map, Vec::new()).unwrap();
4719
4720 kernel.eval(&[42]);
4721 let uid = kernel.get("user_id");
4722 assert!(uid < 1_000_000, "got {uid}");
4723 }
4724
4725 #[test]
4726 fn jit_clamp_const() {
4727 let steps = vec![(JitOp::ClampConst(10, 50), vec![0], vec![1])];
4728 let mut output_map = HashMap::new();
4729 output_map.insert("out".into(), 1);
4730 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4731
4732 kernel.eval(&[5]);
4733 assert_eq!(kernel.get("out"), 10); kernel.eval(&[30]);
4736 assert_eq!(kernel.get("out"), 30); kernel.eval(&[100]);
4739 assert_eq!(kernel.get("out"), 50); }
4741
4742 #[test]
4743 fn jit_interleave() {
4744 let steps = vec![(JitOp::Interleave, vec![0, 1], vec![2])];
4745 let mut output_map = HashMap::new();
4746 output_map.insert("out".into(), 2);
4747 let mut kernel = compile_jit_raw(2, 3, steps, output_map, Vec::new()).unwrap();
4748
4749 kernel.eval(&[0b101, 0b010]);
4750 assert_eq!(kernel.get("out"), 0b01_10_01);
4752 }
4753
4754 #[test]
4755 fn jit_mixed_radix() {
4756 let steps = vec![(
4758 JitOp::MixedRadixConst(vec![100, 1000, 0]),
4759 vec![0],
4760 vec![1, 2, 3],
4761 )];
4762 let mut output_map = HashMap::new();
4763 output_map.insert("d0".into(), 1);
4764 output_map.insert("d1".into(), 2);
4765 output_map.insert("d2".into(), 3);
4766 let mut kernel = compile_jit_raw(1, 4, steps, output_map, Vec::new()).unwrap();
4767
4768 kernel.eval(&[4_201_337]);
4770 assert_eq!(kernel.get("d0"), 37);
4771 assert_eq!(kernel.get("d1"), 13);
4772 assert_eq!(kernel.get("d2"), 42);
4773 }
4774
4775 #[test]
4776 fn jit_unit_interval() {
4777 let steps = vec![(JitOp::UnitInterval, vec![0], vec![1])];
4778 let mut output_map = HashMap::new();
4779 output_map.insert("out".into(), 1);
4780 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4781
4782 kernel.eval(&[0]);
4783 let v = f64::from_bits(kernel.get("out"));
4784 assert!((v - 0.0).abs() < 1e-10);
4785
4786 kernel.eval(&[u64::MAX]);
4787 let v = f64::from_bits(kernel.get("out"));
4788 assert!((v - 1.0).abs() < 1e-10);
4789 }
4790
4791 #[test]
4792 fn jit_f64_to_u64() {
4793 let steps = vec![(JitOp::F64ToU64, vec![0], vec![1])];
4795 let mut output_map = HashMap::new();
4796 output_map.insert("out".into(), 1);
4797 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4798
4799 kernel.eval(&[3.7f64.to_bits()]);
4800 assert_eq!(kernel.get("out"), 3); }
4802
4803 #[test]
4804 fn jit_round_to_u64() {
4805 let steps = vec![(JitOp::RoundToU64, vec![0], vec![1])];
4806 let mut output_map = HashMap::new();
4807 output_map.insert("out".into(), 1);
4808 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4809
4810 kernel.eval(&[3.7f64.to_bits()]);
4811 assert_eq!(kernel.get("out"), 4);
4812
4813 kernel.eval(&[3.2f64.to_bits()]);
4814 assert_eq!(kernel.get("out"), 3);
4815 }
4816
4817 #[test]
4818 fn jit_clamp_f64() {
4819 let steps = vec![(
4820 JitOp::ClampF64Const(0.0f64.to_bits(), 1.0f64.to_bits()),
4821 vec![0],
4822 vec![1],
4823 )];
4824 let mut output_map = HashMap::new();
4825 output_map.insert("out".into(), 1);
4826 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4827
4828 kernel.eval(&[(-0.5f64).to_bits()]);
4829 assert_eq!(f64::from_bits(kernel.get("out")), 0.0);
4830
4831 kernel.eval(&[0.5f64.to_bits()]);
4832 assert_eq!(f64::from_bits(kernel.get("out")), 0.5);
4833
4834 kernel.eval(&[1.5f64.to_bits()]);
4835 assert_eq!(f64::from_bits(kernel.get("out")), 1.0);
4836 }
4837
4838 #[test]
4839 fn jit_lerp() {
4840 let steps = vec![(
4841 JitOp::LerpConst(10.0f64.to_bits(), 20.0f64.to_bits()),
4842 vec![0],
4843 vec![1],
4844 )];
4845 let mut output_map = HashMap::new();
4846 output_map.insert("out".into(), 1);
4847 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4848
4849 kernel.eval(&[0.0f64.to_bits()]);
4850 assert_eq!(f64::from_bits(kernel.get("out")), 10.0);
4851
4852 kernel.eval(&[1.0f64.to_bits()]);
4853 assert_eq!(f64::from_bits(kernel.get("out")), 20.0);
4854
4855 kernel.eval(&[0.5f64.to_bits()]);
4856 assert_eq!(f64::from_bits(kernel.get("out")), 15.0);
4857 }
4858
4859 #[test]
4860 fn jit_scale_range() {
4861 let steps = vec![(
4862 JitOp::ScaleRangeConst(10.0f64.to_bits(), 10.0f64.to_bits()),
4863 vec![0],
4864 vec![1],
4865 )];
4866 let mut output_map = HashMap::new();
4867 output_map.insert("out".into(), 1);
4868 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4869
4870 kernel.eval(&[0]);
4871 let v = f64::from_bits(kernel.get("out"));
4872 assert!((v - 10.0).abs() < 0.001);
4873
4874 kernel.eval(&[u64::MAX]);
4875 let v = f64::from_bits(kernel.get("out"));
4876 assert!((v - 20.0).abs() < 0.001);
4877 }
4878
4879 #[test]
4880 fn jit_quantize() {
4881 let steps = vec![(JitOp::QuantizeConst(10.0f64.to_bits()), vec![0], vec![1])];
4882 let mut output_map = HashMap::new();
4883 output_map.insert("out".into(), 1);
4884 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4885
4886 kernel.eval(&[13.0f64.to_bits()]);
4887 assert_eq!(f64::from_bits(kernel.get("out")), 10.0);
4888
4889 kernel.eval(&[17.0f64.to_bits()]);
4890 assert_eq!(f64::from_bits(kernel.get("out")), 20.0);
4891 }
4892
4893 #[test]
4894 fn jit_discretize() {
4895 let steps = vec![(
4896 JitOp::DiscretizeConst(100.0f64.to_bits(), 10),
4897 vec![0],
4898 vec![1],
4899 )];
4900 let mut output_map = HashMap::new();
4901 output_map.insert("out".into(), 1);
4902 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4903
4904 kernel.eval(&[0.0f64.to_bits()]);
4905 assert_eq!(kernel.get("out"), 0);
4906
4907 kernel.eval(&[55.0f64.to_bits()]);
4908 assert_eq!(kernel.get("out"), 5);
4909
4910 kernel.eval(&[99.0f64.to_bits()]);
4911 assert_eq!(kernel.get("out"), 9);
4912
4913 kernel.eval(&[200.0f64.to_bits()]);
4915 assert_eq!(kernel.get("out"), 9);
4916 }
4917
4918 #[test]
4919 fn jit_chain_unit_interval_lerp() {
4920 let steps = vec![
4922 (JitOp::UnitInterval, vec![0], vec![1]),
4923 (
4924 JitOp::LerpConst(100.0f64.to_bits(), 200.0f64.to_bits()),
4925 vec![1],
4926 vec![2],
4927 ),
4928 ];
4929 let mut output_map = HashMap::new();
4930 output_map.insert("out".into(), 2);
4931 let mut kernel = compile_jit_raw(1, 3, steps, output_map, Vec::new()).unwrap();
4932
4933 kernel.eval(&[0]);
4934 let v = f64::from_bits(kernel.get("out"));
4935 assert!((v - 100.0).abs() < 0.001);
4936
4937 kernel.eval(&[u64::MAX]);
4938 let v = f64::from_bits(kernel.get("out"));
4939 assert!((v - 200.0).abs() < 0.001);
4940 }
4941
4942 #[test]
4943 fn jit_multi_step_chain() {
4944 let steps = vec![
4946 (JitOp::AddConst(10), vec![0], vec![1]),
4947 (JitOp::MulConst(3), vec![1], vec![2]),
4948 (JitOp::ModConst(100), vec![2], vec![3]),
4949 ];
4950 let mut output_map = HashMap::new();
4951 output_map.insert("out".into(), 3);
4952 let mut kernel = compile_jit_raw(1, 4, steps, output_map, Vec::new()).unwrap();
4953
4954 kernel.eval(&[5]);
4955 assert_eq!(kernel.get("out"), 45);
4957 }
4958
4959 #[test]
4962 fn jit_is_positive_check_passes_positive() {
4963 let steps = vec![(
4964 JitOp::IsPositiveCheck {
4965 name_ptr: 0,
4966 name_len: 0,
4967 },
4968 vec![0],
4969 vec![1],
4970 )];
4971 let mut output_map = HashMap::new();
4972 output_map.insert("out".into(), 1);
4973 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4974 kernel.eval(&[42]);
4975 assert_eq!(kernel.get("out"), 42);
4976 kernel.eval(&[u64::MAX]);
4979 assert_eq!(kernel.get("out"), u64::MAX);
4980 }
4981
4982 #[test]
4983 fn jit_in_range_check_passes_interior() {
4984 let steps = vec![(JitOp::InRangeCheck(10, 100), vec![0], vec![1])];
4985 let mut output_map = HashMap::new();
4986 output_map.insert("out".into(), 1);
4987 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
4988 kernel.eval(&[50]);
4989 assert_eq!(kernel.get("out"), 50);
4990 kernel.eval(&[10]);
4992 assert_eq!(kernel.get("out"), 10);
4993 kernel.eval(&[100]);
4994 assert_eq!(kernel.get("out"), 100);
4995 }
4996
4997 #[test]
5002 fn jit_is_one_of_check_passes_allowed_values() {
5003 let steps = vec![(
5004 JitOp::IsOneOfCheck {
5005 allowed: vec![1, 2, 3, 5, 8],
5006 set_ptr: 0,
5007 set_len: 0,
5008 },
5009 vec![0],
5010 vec![1],
5011 )];
5012 let mut output_map = HashMap::new();
5013 output_map.insert("out".into(), 1);
5014 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
5015 for v in [1u64, 2, 3, 5, 8] {
5017 kernel.eval(&[v]);
5018 assert_eq!(kernel.get("out"), v);
5019 }
5020 }
5021
5022 #[test]
5023 fn jit_is_one_of_check_accepts_single_element_allow_list() {
5024 let steps = vec![(
5027 JitOp::IsOneOfCheck {
5028 allowed: vec![42],
5029 set_ptr: 0,
5030 set_len: 0,
5031 },
5032 vec![0],
5033 vec![1],
5034 )];
5035 let mut output_map = HashMap::new();
5036 output_map.insert("out".into(), 1);
5037 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
5038 kernel.eval(&[42]);
5039 assert_eq!(kernel.get("out"), 42);
5040 }
5041
5042 fn extract_panic_msg(payload: Box<dyn std::any::Any + Send + 'static>) -> String {
5052 payload
5053 .downcast_ref::<String>()
5054 .cloned()
5055 .or_else(|| payload.downcast_ref::<&str>().map(|s| s.to_string()))
5056 .unwrap_or_else(|| "(non-string panic)".into())
5057 }
5058
5059 #[test]
5060 fn jit_is_positive_violation_is_catchable() {
5061 let steps = vec![(
5062 JitOp::IsPositiveCheck {
5063 name_ptr: 0,
5064 name_len: 0,
5065 },
5066 vec![0],
5067 vec![1],
5068 )];
5069 let mut output_map = HashMap::new();
5070 output_map.insert("out".into(), 1);
5071 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
5072 let err = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| kernel.eval(&[0])))
5073 .expect_err("JIT violation should panic");
5074 assert!(extract_panic_msg(err).contains("must be > 0"));
5075 }
5076
5077 #[test]
5078 fn jit_in_range_violation_is_catchable() {
5079 let steps = vec![(JitOp::InRangeCheck(10, 100), vec![0], vec![1])];
5080 let mut output_map = HashMap::new();
5081 output_map.insert("out".into(), 1);
5082 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
5083
5084 let err = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| kernel.eval(&[5])))
5085 .expect_err("below-range should panic");
5086 assert!(extract_panic_msg(err).contains("outside [10, 100]"));
5087
5088 let err = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| kernel.eval(&[500])))
5089 .expect_err("above-range should panic");
5090 assert!(extract_panic_msg(err).contains("outside [10, 100]"));
5091 }
5092
5093 #[test]
5094 fn jit_is_one_of_violation_is_catchable() {
5095 let steps = vec![(
5096 JitOp::IsOneOfCheck {
5097 allowed: vec![1, 3, 5],
5098 set_ptr: 0,
5099 set_len: 0,
5100 },
5101 vec![0],
5102 vec![1],
5103 )];
5104 let mut output_map = HashMap::new();
5105 output_map.insert("out".into(), 1);
5106 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
5107 let err = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| kernel.eval(&[2])))
5108 .expect_err("disallowed value should panic");
5109 assert!(extract_panic_msg(err).contains("not in allowed set"));
5110 }
5111
5112 #[test]
5113 fn invoke_with_catch_restores_slot_after_foreign_panic() {
5114 let caught = std::panic::catch_unwind(|| {
5122 invoke_with_catch(|| panic!("foreign panic"));
5123 });
5124 assert!(caught.is_err(), "foreign panic should propagate out");
5125
5126 let steps = vec![(
5128 JitOp::IsPositiveCheck {
5129 name_ptr: 0,
5130 name_len: 0,
5131 },
5132 vec![0],
5133 vec![1],
5134 )];
5135 let mut output_map = HashMap::new();
5136 output_map.insert("out".into(), 1);
5137 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
5138 let err = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| kernel.eval(&[0])))
5139 .expect_err("JIT violation should panic cleanly after foreign panic");
5140 assert!(extract_panic_msg(err).contains("must be > 0"));
5141
5142 kernel.eval(&[42]);
5144 assert_eq!(kernel.get("out"), 42);
5145 }
5146
5147 #[test]
5148 fn jit_kernel_survives_multiple_violations() {
5149 let steps = vec![(
5153 JitOp::IsPositiveCheck {
5154 name_ptr: 0,
5155 name_len: 0,
5156 },
5157 vec![0],
5158 vec![1],
5159 )];
5160 let mut output_map = HashMap::new();
5161 output_map.insert("out".into(), 1);
5162 let mut kernel = compile_jit_raw(1, 2, steps, output_map, Vec::new()).unwrap();
5163
5164 for _ in 0..3 {
5165 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| kernel.eval(&[0])))
5166 .expect_err("violation should still panic");
5167 }
5168 kernel.eval(&[42]);
5170 assert_eq!(kernel.get("out"), 42);
5171 }
5172}