Skip to main content

shape_jit/ffi/
math.rs

1// Heap allocation audit (PR-9 V8 Gap Closure):
2//   Category A (NaN-boxed returns): 7 sites
3//     jit_box(HK_TIME, ...) — generic_add (Time+Duration, Duration+Time),
4//       generic_sub (Time-Duration)
5//     jit_box(HK_DURATION, ...) — generic_add (Duration+Duration),
6//       generic_sub (Time-Time)
7//     jit_box(HK_STRING, ...) — generic_add (String+String)
8//   Category B (intermediate/consumed): 0 sites
9//   Category C (heap islands): 0 sites
10//!
11//! Math FFI Functions for JIT
12//!
13//! Trigonometric and mathematical functions for JIT-compiled code.
14//!
15//! ## SIMD Optimization
16//!
17//! Series arithmetic (+, -, *, /) uses SIMD-accelerated operations from
18//! shape-runtime for high performance vectorized computation.
19
20use super::super::nan_boxing::*;
21
22// SIMD threshold - use SIMD for arrays >= this size
23#[allow(dead_code)]
24const SIMD_THRESHOLD: usize = 16;
25
26// ============================================================================
27// Trigonometric Functions
28// ============================================================================
29
30pub extern "C" fn jit_sin(value_bits: u64) -> u64 {
31    let x = if is_number(value_bits) {
32        unbox_number(value_bits)
33    } else {
34        return box_number(f64::NAN);
35    };
36    box_number(x.sin())
37}
38
39pub extern "C" fn jit_cos(value_bits: u64) -> u64 {
40    let x = if is_number(value_bits) {
41        unbox_number(value_bits)
42    } else {
43        return box_number(f64::NAN);
44    };
45    box_number(x.cos())
46}
47
48pub extern "C" fn jit_tan(value_bits: u64) -> u64 {
49    let x = if is_number(value_bits) {
50        unbox_number(value_bits)
51    } else {
52        return box_number(f64::NAN);
53    };
54    box_number(x.tan())
55}
56
57pub extern "C" fn jit_asin(value_bits: u64) -> u64 {
58    let x = if is_number(value_bits) {
59        unbox_number(value_bits)
60    } else {
61        return box_number(f64::NAN);
62    };
63    box_number(x.asin())
64}
65
66pub extern "C" fn jit_acos(value_bits: u64) -> u64 {
67    let x = if is_number(value_bits) {
68        unbox_number(value_bits)
69    } else {
70        return box_number(f64::NAN);
71    };
72    box_number(x.acos())
73}
74
75pub extern "C" fn jit_atan(value_bits: u64) -> u64 {
76    let x = if is_number(value_bits) {
77        unbox_number(value_bits)
78    } else {
79        return box_number(f64::NAN);
80    };
81    box_number(x.atan())
82}
83
84// ============================================================================
85// Exponential and Logarithmic Functions
86// ============================================================================
87
88pub extern "C" fn jit_exp(value_bits: u64) -> u64 {
89    let x = if is_number(value_bits) {
90        unbox_number(value_bits)
91    } else {
92        return box_number(f64::NAN);
93    };
94    box_number(x.exp())
95}
96
97pub extern "C" fn jit_ln(value_bits: u64) -> u64 {
98    let x = if is_number(value_bits) {
99        unbox_number(value_bits)
100    } else {
101        return box_number(f64::NAN);
102    };
103    box_number(x.ln())
104}
105
106pub extern "C" fn jit_log(value_bits: u64, base_bits: u64) -> u64 {
107    let x = if is_number(value_bits) {
108        unbox_number(value_bits)
109    } else {
110        return box_number(f64::NAN);
111    };
112    let base = if is_number(base_bits) {
113        unbox_number(base_bits)
114    } else {
115        return box_number(f64::NAN);
116    };
117    box_number(x.log(base))
118}
119
120// ============================================================================
121// Power Function
122// ============================================================================
123
124pub extern "C" fn jit_pow(base_bits: u64, exp_bits: u64) -> u64 {
125    let base = if is_number(base_bits) {
126        unbox_number(base_bits)
127    } else {
128        return box_number(f64::NAN);
129    };
130    let exp = if is_number(exp_bits) {
131        unbox_number(exp_bits)
132    } else {
133        return box_number(f64::NAN);
134    };
135    box_number(base.powf(exp))
136}
137
138// ============================================================================
139// Generic Binary Operations (for non-numeric types)
140// ============================================================================
141
142/// Generic add that handles Time + Duration, Duration + Duration, etc.
143pub extern "C" fn jit_generic_add(a_bits: u64, b_bits: u64) -> u64 {
144    use super::super::context::JITDuration;
145
146    // Both numbers - fast path
147    if is_number(a_bits) && is_number(b_bits) {
148        return box_number(unbox_number(a_bits) + unbox_number(b_bits));
149    }
150
151    let a_kind = heap_kind(a_bits);
152    let b_kind = heap_kind(b_bits);
153
154    // Time + Duration or Duration + Time
155    if a_kind == Some(HK_TIME) && b_kind == Some(HK_DURATION) {
156        let timestamp = *unsafe { jit_unbox::<i64>(a_bits) };
157        let dur = unsafe { jit_unbox::<JITDuration>(b_bits) };
158        let seconds = duration_to_seconds(dur);
159        let new_timestamp = timestamp + seconds as i64;
160        return jit_box(HK_TIME, new_timestamp);
161    }
162
163    if a_kind == Some(HK_DURATION) && b_kind == Some(HK_TIME) {
164        let timestamp = *unsafe { jit_unbox::<i64>(b_bits) };
165        let dur = unsafe { jit_unbox::<JITDuration>(a_bits) };
166        let seconds = duration_to_seconds(dur);
167        let new_timestamp = timestamp + seconds as i64;
168        return jit_box(HK_TIME, new_timestamp);
169    }
170
171    // Duration + Duration
172    if a_kind == Some(HK_DURATION) && b_kind == Some(HK_DURATION) {
173        let a_dur = unsafe { jit_unbox::<JITDuration>(a_bits) };
174        let b_dur = unsafe { jit_unbox::<JITDuration>(b_bits) };
175        let a_secs = duration_to_seconds(a_dur);
176        let b_secs = duration_to_seconds(b_dur);
177        let total_secs = a_secs + b_secs;
178        return jit_box(
179            HK_DURATION,
180            JITDuration {
181                value: total_secs,
182                unit: 0,
183            },
184        );
185    }
186
187    // String concatenation
188    if a_kind == Some(HK_STRING) && b_kind == Some(HK_STRING) {
189        let a_str = unsafe { jit_unbox::<String>(a_bits) };
190        let b_str = unsafe { jit_unbox::<String>(b_bits) };
191        let result = format!("{}{}", a_str, b_str);
192        return jit_box(HK_STRING, result);
193    }
194
195    // Fallback for numbers (one might be boxed differently)
196    if is_number(a_bits) || is_number(b_bits) {
197        let a_num = if is_number(a_bits) {
198            unbox_number(a_bits)
199        } else {
200            0.0
201        };
202        let b_num = if is_number(b_bits) {
203            unbox_number(b_bits)
204        } else {
205            0.0
206        };
207        return box_number(a_num + b_num);
208    }
209
210    TAG_NULL
211}
212
213/// Generic subtract that handles Time - Duration, Duration - Duration, etc.
214pub extern "C" fn jit_generic_sub(a_bits: u64, b_bits: u64) -> u64 {
215    use super::super::context::JITDuration;
216
217    // Both numbers - fast path
218    if is_number(a_bits) && is_number(b_bits) {
219        return box_number(unbox_number(a_bits) - unbox_number(b_bits));
220    }
221
222    let a_kind = heap_kind(a_bits);
223    let b_kind = heap_kind(b_bits);
224
225    // Time - Duration
226    if a_kind == Some(HK_TIME) && b_kind == Some(HK_DURATION) {
227        let timestamp = *unsafe { jit_unbox::<i64>(a_bits) };
228        let dur = unsafe { jit_unbox::<JITDuration>(b_bits) };
229        let seconds = duration_to_seconds(dur);
230        let new_timestamp = timestamp - seconds as i64;
231        return jit_box(HK_TIME, new_timestamp);
232    }
233
234    // Time - Time = Duration (in seconds)
235    if a_kind == Some(HK_TIME) && b_kind == Some(HK_TIME) {
236        let a_ts = *unsafe { jit_unbox::<i64>(a_bits) };
237        let b_ts = *unsafe { jit_unbox::<i64>(b_bits) };
238        let diff_secs = (a_ts - b_ts) as f64;
239        return jit_box(
240            HK_DURATION,
241            JITDuration {
242                value: diff_secs,
243                unit: 0,
244            },
245        );
246    }
247
248    // Fallback for numbers
249    if is_number(a_bits) || is_number(b_bits) {
250        let a_num = if is_number(a_bits) {
251            unbox_number(a_bits)
252        } else {
253            0.0
254        };
255        let b_num = if is_number(b_bits) {
256            unbox_number(b_bits)
257        } else {
258            0.0
259        };
260        return box_number(a_num - b_num);
261    }
262
263    TAG_NULL
264}
265
266/// Generic multiplication for JIT (Series * Series, Series * number, number * Series, etc.)
267#[unsafe(no_mangle)]
268pub extern "C" fn jit_generic_mul(a_bits: u64, b_bits: u64) -> u64 {
269    // Both numbers - fast path
270    if is_number(a_bits) && is_number(b_bits) {
271        return box_number(unbox_number(a_bits) * unbox_number(b_bits));
272    }
273
274    // Fallback for numbers
275    if is_number(a_bits) || is_number(b_bits) {
276        let a_num = if is_number(a_bits) {
277            unbox_number(a_bits)
278        } else {
279            1.0
280        };
281        let b_num = if is_number(b_bits) {
282            unbox_number(b_bits)
283        } else {
284            1.0
285        };
286        return box_number(a_num * b_num);
287    }
288
289    TAG_NULL
290}
291
292/// Generic division for JIT (Series / Series, Series / number, number / Series, etc.)
293#[unsafe(no_mangle)]
294pub extern "C" fn jit_generic_div(a_bits: u64, b_bits: u64) -> u64 {
295    // Both numbers - fast path
296    if is_number(a_bits) && is_number(b_bits) {
297        let b = unbox_number(b_bits);
298        return box_number(if b == 0.0 {
299            f64::NAN
300        } else {
301            unbox_number(a_bits) / b
302        });
303    }
304
305    // Fallback for numbers
306    if is_number(a_bits) || is_number(b_bits) {
307        let a_num = if is_number(a_bits) {
308            unbox_number(a_bits)
309        } else {
310            0.0
311        };
312        let b_num = if is_number(b_bits) {
313            unbox_number(b_bits)
314        } else {
315            1.0
316        };
317        return box_number(if b_num == 0.0 {
318            f64::NAN
319        } else {
320            a_num / b_num
321        });
322    }
323
324    TAG_NULL
325}
326
327/// SIMD-accelerated Series addition
328#[allow(dead_code)]
329fn series_add_simd(a_bits: u64, b_bits: u64) -> u64 {
330    series_simd_binary_op(
331        a_bits,
332        b_bits,
333        super::simd::jit_simd_add,
334        super::simd::jit_simd_add_scalar,
335    )
336}
337
338/// SIMD-accelerated Series subtraction
339#[allow(dead_code)]
340fn series_sub_simd(a_bits: u64, b_bits: u64) -> u64 {
341    series_simd_binary_op(
342        a_bits,
343        b_bits,
344        super::simd::jit_simd_sub,
345        super::simd::jit_simd_sub_scalar,
346    )
347}
348
349/// SIMD-accelerated Series multiplication
350#[allow(dead_code)]
351fn series_mul_simd(a_bits: u64, b_bits: u64) -> u64 {
352    series_simd_binary_op(
353        a_bits,
354        b_bits,
355        super::simd::jit_simd_mul,
356        super::simd::jit_simd_mul_scalar,
357    )
358}
359
360/// SIMD-accelerated Series division
361#[allow(dead_code)]
362fn series_div_simd(a_bits: u64, b_bits: u64) -> u64 {
363    series_simd_binary_op(
364        a_bits,
365        b_bits,
366        super::simd::jit_simd_div,
367        super::simd::jit_simd_div_scalar,
368    )
369}
370
371/// Helper for SIMD series binary operations
372/// Uses raw pointer SIMD functions for maximum performance
373fn series_simd_binary_op(
374    _a_bits: u64,
375    _b_bits: u64,
376    _simd_binary: extern "C" fn(*const f64, *const f64, u64) -> *mut f64,
377    _simd_scalar: extern "C" fn(*const f64, f64, u64) -> *mut f64,
378) -> u64 {
379    TAG_NULL
380}
381
382/// Fallback helper for series binary operations (for non-SIMD ops)
383#[allow(dead_code)]
384fn series_binary_op<F>(_a_bits: u64, _b_bits: u64, _op: F) -> u64
385where
386    F: Fn(f64, f64) -> f64,
387{
388    TAG_NULL
389}
390
391/// Generic comparison for Series > Series, Series > number, etc.
392/// Returns a Series of 1.0/0.0 for series comparisons, or a boolean for scalars.
393pub extern "C" fn jit_series_gt(a_bits: u64, b_bits: u64) -> u64 {
394    series_comparison_op(a_bits, b_bits, |a, b| a > b)
395}
396
397pub extern "C" fn jit_series_lt(a_bits: u64, b_bits: u64) -> u64 {
398    series_comparison_op(a_bits, b_bits, |a, b| a < b)
399}
400
401pub extern "C" fn jit_series_gte(a_bits: u64, b_bits: u64) -> u64 {
402    series_comparison_op(a_bits, b_bits, |a, b| a >= b)
403}
404
405pub extern "C" fn jit_series_lte(a_bits: u64, b_bits: u64) -> u64 {
406    series_comparison_op(a_bits, b_bits, |a, b| a <= b)
407}
408
409/// Helper for series comparison operations
410fn series_comparison_op<F>(a_bits: u64, b_bits: u64, op: F) -> u64
411where
412    F: Fn(f64, f64) -> bool,
413{
414    // Fallback: numeric comparison
415    if is_number(a_bits) && is_number(b_bits) {
416        let a = unbox_number(a_bits);
417        let b = unbox_number(b_bits);
418        return if op(a, b) {
419            TAG_BOOL_TRUE
420        } else {
421            TAG_BOOL_FALSE
422        };
423    }
424    TAG_BOOL_FALSE
425}
426
427/// Generic equality that handles strings, booleans, and other non-numeric types.
428/// Compares string contents (not pointer identity), numbers by value, booleans by tag.
429pub extern "C" fn jit_generic_eq(a_bits: u64, b_bits: u64) -> u64 {
430    // Both numbers - fast path
431    if is_number(a_bits) && is_number(b_bits) {
432        return if unbox_number(a_bits) == unbox_number(b_bits) {
433            TAG_BOOL_TRUE
434        } else {
435            TAG_BOOL_FALSE
436        };
437    }
438
439    // Identical tags (bools, null, unit)
440    if a_bits == b_bits {
441        return TAG_BOOL_TRUE;
442    }
443
444    // Both heap values
445    let a_kind = heap_kind(a_bits);
446    let b_kind = heap_kind(b_bits);
447
448    if a_kind == Some(HK_STRING) && b_kind == Some(HK_STRING) {
449        let a_str = unsafe { jit_unbox::<String>(a_bits) };
450        let b_str = unsafe { jit_unbox::<String>(b_bits) };
451        return if a_str == b_str {
452            TAG_BOOL_TRUE
453        } else {
454            TAG_BOOL_FALSE
455        };
456    }
457
458    TAG_BOOL_FALSE
459}
460
461/// Generic inequality — inverse of jit_generic_eq.
462pub extern "C" fn jit_generic_neq(a_bits: u64, b_bits: u64) -> u64 {
463    if jit_generic_eq(a_bits, b_bits) == TAG_BOOL_TRUE {
464        TAG_BOOL_FALSE
465    } else {
466        TAG_BOOL_TRUE
467    }
468}
469
470/// Helper: convert JITDuration to seconds
471fn duration_to_seconds(dur: &super::super::context::JITDuration) -> f64 {
472    match dur.unit {
473        0 => dur.value,              // seconds
474        1 => dur.value * 60.0,       // minutes
475        2 => dur.value * 3600.0,     // hours
476        3 => dur.value * 86400.0,    // days
477        4 => dur.value * 604800.0,   // weeks
478        5 => dur.value * 2592000.0,  // months (30 days)
479        6 => dur.value * 31536000.0, // years (365 days)
480        _ => dur.value,
481    }
482}
483
484#[cfg(test)]
485mod tests {
486    use super::*;
487
488    #[test]
489    fn test_scalar_add() {
490        let a = box_number(10.0);
491        let b = box_number(32.0);
492        let result = jit_generic_add(a, b);
493        assert_eq!(unbox_number(result), 42.0);
494    }
495
496    #[test]
497    fn test_scalar_sub() {
498        let a = box_number(100.0);
499        let b = box_number(58.0);
500        let result = jit_generic_sub(a, b);
501        assert_eq!(unbox_number(result), 42.0);
502    }
503
504    #[test]
505    fn test_scalar_mul() {
506        let a = box_number(6.0);
507        let b = box_number(7.0);
508        let result = jit_generic_mul(a, b);
509        assert_eq!(unbox_number(result), 42.0);
510    }
511
512    #[test]
513    fn test_scalar_div() {
514        let a = box_number(84.0);
515        let b = box_number(2.0);
516        let result = jit_generic_div(a, b);
517        assert_eq!(unbox_number(result), 42.0);
518    }
519
520    #[test]
521    fn test_scalar_div_by_zero() {
522        let a = box_number(42.0);
523        let b = box_number(0.0);
524        let result = jit_generic_div(a, b);
525        // Division by zero should return infinity or NaN
526        let val = unbox_number(result);
527        assert!(val.is_infinite() || val.is_nan());
528    }
529}