1pub mod collection_arc;
8pub mod typed_map;
9
10use shape_value::heap_value::TypedObjectStorage;
25use shape_value::v2::decimal_obj::DecimalObj;
26use shape_value::v2::heap_header::HeapHeader;
27use shape_value::v2::string_obj::StringObj;
28use shape_value::v2::typed_array::TypedArray;
29
30use shape_vm::executor::v2_handlers::v2_array_detect::{
51 stamp_elem_type, ELEM_TYPE_BOOL, ELEM_TYPE_CHAR, ELEM_TYPE_DECIMAL, ELEM_TYPE_F32,
52 ELEM_TYPE_F64, ELEM_TYPE_I16, ELEM_TYPE_I32, ELEM_TYPE_I64, ELEM_TYPE_I8,
53 ELEM_TYPE_STRING, ELEM_TYPE_TYPED_OBJECT, ELEM_TYPE_U16, ELEM_TYPE_U32, ELEM_TYPE_U8,
54};
55
56macro_rules! v2_typed_array_scalar {
101 (
102 new = $new_fn:ident,
103 get = $get_fn:ident,
104 set = $set_fn:ident,
105 ty = $native_ty:ty,
106 elem_byte = $elem_byte:expr,
107 kind_label = $kind_label:literal $(,)?
108 ) => {
109 #[doc = concat!(
110 "Allocate an empty `TypedArray<", stringify!($native_ty), ">` of given capacity. ",
111 "Stamps `", $kind_label, "` at `HeapHeader._pad` offset 7 per ADR-006 §2.7.5."
112 )]
113 #[unsafe(no_mangle)]
114 pub extern "C" fn $new_fn(capacity: u32) -> *mut TypedArray<$native_ty> {
115 let ptr = TypedArray::<$native_ty>::with_capacity(capacity);
116 unsafe { stamp_elem_type(ptr as *mut u8, $elem_byte) };
120 ptr
121 }
122
123 #[doc = concat!(
124 "Read the element at `index` from a `TypedArray<", stringify!($native_ty), ">`. ",
125 "Panics on out-of-bounds."
126 )]
127 #[unsafe(no_mangle)]
128 pub extern "C" fn $get_fn(
129 arr: *const TypedArray<$native_ty>,
130 index: i64,
131 ) -> $native_ty {
132 unsafe {
133 if index < 0 || index as u32 >= (*arr).len {
134 panic!(
135 concat!(
136 "v2 array ", $kind_label, " index {} out of bounds (len {})"
137 ),
138 index,
139 (*arr).len
140 );
141 }
142 TypedArray::get_unchecked(arr, index as u32)
143 }
144 }
145
146 #[doc = concat!(
147 "Write `val` to the element at `index` in a `TypedArray<",
148 stringify!($native_ty), ">`."
149 )]
150 #[unsafe(no_mangle)]
151 pub extern "C" fn $set_fn(
152 arr: *mut TypedArray<$native_ty>,
153 index: i64,
154 val: $native_ty,
155 ) {
156 unsafe {
157 TypedArray::set(arr, index as u32, val);
158 }
159 }
160 };
161}
162
163v2_typed_array_scalar! {
167 new = jit_v2_array_new_f64,
168 get = jit_v2_array_get_f64,
169 set = jit_v2_array_set_f64,
170 ty = f64,
171 elem_byte = ELEM_TYPE_F64,
172 kind_label = "f64",
173}
174
175v2_typed_array_scalar! {
176 new = jit_v2_array_new_i64,
177 get = jit_v2_array_get_i64,
178 set = jit_v2_array_set_i64,
179 ty = i64,
180 elem_byte = ELEM_TYPE_I64,
181 kind_label = "i64",
182}
183
184v2_typed_array_scalar! {
185 new = jit_v2_array_new_i32,
186 get = jit_v2_array_get_i32,
187 set = jit_v2_array_set_i32,
188 ty = i32,
189 elem_byte = ELEM_TYPE_I32,
190 kind_label = "i32",
191}
192
193v2_typed_array_scalar! {
198 new = jit_v2_array_new_bool,
199 get = jit_v2_array_get_bool,
200 set = jit_v2_array_set_bool,
201 ty = u8,
202 elem_byte = ELEM_TYPE_BOOL,
203 kind_label = "bool",
204}
205
206v2_typed_array_scalar! {
211 new = jit_v2_array_new_i8,
212 get = jit_v2_array_get_i8,
213 set = jit_v2_array_set_i8,
214 ty = i8,
215 elem_byte = ELEM_TYPE_I8,
216 kind_label = "i8",
217}
218
219v2_typed_array_scalar! {
220 new = jit_v2_array_new_u8,
221 get = jit_v2_array_get_u8,
222 set = jit_v2_array_set_u8,
223 ty = u8,
224 elem_byte = ELEM_TYPE_U8,
225 kind_label = "u8",
226}
227
228v2_typed_array_scalar! {
229 new = jit_v2_array_new_i16,
230 get = jit_v2_array_get_i16,
231 set = jit_v2_array_set_i16,
232 ty = i16,
233 elem_byte = ELEM_TYPE_I16,
234 kind_label = "i16",
235}
236
237v2_typed_array_scalar! {
238 new = jit_v2_array_new_u16,
239 get = jit_v2_array_get_u16,
240 set = jit_v2_array_set_u16,
241 ty = u16,
242 elem_byte = ELEM_TYPE_U16,
243 kind_label = "u16",
244}
245
246v2_typed_array_scalar! {
247 new = jit_v2_array_new_u32,
248 get = jit_v2_array_get_u32,
249 set = jit_v2_array_set_u32,
250 ty = u32,
251 elem_byte = ELEM_TYPE_U32,
252 kind_label = "u32",
253}
254
255v2_typed_array_scalar! {
257 new = jit_v2_array_new_f32,
258 get = jit_v2_array_get_f32,
259 set = jit_v2_array_set_f32,
260 ty = f32,
261 elem_byte = ELEM_TYPE_F32,
262 kind_label = "f32",
263}
264
265#[doc = "Allocate an empty `TypedArray<char>` of given capacity. \
276Stamps `ELEM_TYPE_CHAR` at `HeapHeader._pad` offset 7 per ADR-006 §2.7.5."]
277#[unsafe(no_mangle)]
278pub extern "C" fn jit_v2_array_new_char(capacity: u32) -> *mut TypedArray<char> {
279 let ptr = TypedArray::<char>::with_capacity(capacity);
280 unsafe { stamp_elem_type(ptr as *mut u8, ELEM_TYPE_CHAR) };
281 ptr
282}
283
284#[doc = "Read the codepoint at `index` from a `TypedArray<char>`. Panics on \
285out-of-bounds."]
286#[unsafe(no_mangle)]
287pub extern "C" fn jit_v2_array_get_char(
288 arr: *const TypedArray<char>,
289 index: i64,
290) -> u32 {
291 unsafe {
292 if index < 0 || index as u32 >= (*arr).len {
293 panic!(
294 "v2 array char index {} out of bounds (len {})",
295 index,
296 (*arr).len
297 );
298 }
299 TypedArray::<char>::get_unchecked(arr, index as u32) as u32
300 }
301}
302
303#[doc = "Write a Unicode codepoint to the element at `index` in a \
304`TypedArray<char>`. The caller must ensure `val` is a valid Unicode scalar \
305value (the JIT codegen site proves this at compile time)."]
306#[unsafe(no_mangle)]
307pub extern "C" fn jit_v2_array_set_char(
308 arr: *mut TypedArray<char>,
309 index: i64,
310 val: u32,
311) {
312 let c = unsafe { char::from_u32_unchecked(val) };
317 unsafe {
318 TypedArray::set(arr, index as u32, c);
319 }
320}
321
322macro_rules! v2_typed_array_heap_get_set {
348 (
349 get = $get_fn:ident,
350 set = $set_fn:ident,
351 elem_obj_ty = $elem_obj_ty:ty,
352 kind_label = $kind_label:literal $(,)?
353 ) => {
354 #[doc = concat!(
355 "Read the per-element heap pointer at `index` from a ",
356 "`TypedArray<*const ", stringify!($elem_obj_ty), ">`. ",
357 "Panics on out-of-bounds. Caller is responsible for per-element ",
358 "retain discipline before transferring the share."
359 )]
360 #[unsafe(no_mangle)]
361 pub extern "C" fn $get_fn(
362 arr: *const TypedArray<*const $elem_obj_ty>,
363 index: i64,
364 ) -> *const u8 {
365 unsafe {
366 if index < 0 || index as u32 >= (*arr).len {
367 panic!(
368 concat!(
369 "v2 array ", $kind_label, " index {} out of bounds (len {})"
370 ),
371 index,
372 (*arr).len
373 );
374 }
375 TypedArray::<*const $elem_obj_ty>::get_unchecked(arr, index as u32)
376 as *const u8
377 }
378 }
379
380 #[doc = concat!(
381 "Write a per-element heap pointer at `index` in a ",
382 "`TypedArray<*const ", stringify!($elem_obj_ty), ">`. The caller ",
383 "must have an owning refcount share for `val` and is responsible ",
384 "for releasing any previous element at `index` before this call."
385 )]
386 #[unsafe(no_mangle)]
387 pub extern "C" fn $set_fn(
388 arr: *mut TypedArray<*const $elem_obj_ty>,
389 index: i64,
390 val: *const u8,
391 ) {
392 unsafe {
393 TypedArray::set(arr, index as u32, val as *const $elem_obj_ty);
394 }
395 }
396 };
397}
398
399#[doc = "Allocate an empty `TypedArray<*const StringObj>` of given capacity. \
400Stamps `ELEM_TYPE_STRING` at `HeapHeader._pad` offset 7 per ADR-006 §2.7.5. \
401ckpt-6-prime Group X JIT FFI String/Decimal BUILD (2026-05-15)."]
402#[unsafe(no_mangle)]
403pub extern "C" fn jit_new_typed_array_string(
404 capacity: u32,
405) -> *mut TypedArray<*const StringObj> {
406 let ptr = TypedArray::<*const StringObj>::with_capacity(capacity);
407 unsafe { stamp_elem_type(ptr as *mut u8, ELEM_TYPE_STRING) };
408 ptr
409}
410
411#[unsafe(no_mangle)]
415pub extern "C" fn jit_v2_array_new_string(
416 capacity: u32,
417) -> *mut TypedArray<*const StringObj> {
418 jit_new_typed_array_string(capacity)
419}
420
421v2_typed_array_heap_get_set! {
422 get = jit_v2_array_get_string,
423 set = jit_v2_array_set_string,
424 elem_obj_ty = StringObj,
425 kind_label = "string",
426}
427
428#[doc = "Allocate an empty `TypedArray<*const DecimalObj>` of given capacity. \
429Stamps `ELEM_TYPE_DECIMAL` at `HeapHeader._pad` offset 7 per ADR-006 §2.7.5. \
430ckpt-6-prime Group X JIT FFI String/Decimal BUILD (2026-05-15)."]
431#[unsafe(no_mangle)]
432pub extern "C" fn jit_new_typed_array_decimal(
433 capacity: u32,
434) -> *mut TypedArray<*const DecimalObj> {
435 let ptr = TypedArray::<*const DecimalObj>::with_capacity(capacity);
436 unsafe { stamp_elem_type(ptr as *mut u8, ELEM_TYPE_DECIMAL) };
437 ptr
438}
439
440#[unsafe(no_mangle)]
443pub extern "C" fn jit_v2_array_new_decimal(
444 capacity: u32,
445) -> *mut TypedArray<*const DecimalObj> {
446 jit_new_typed_array_decimal(capacity)
447}
448
449v2_typed_array_heap_get_set! {
450 get = jit_v2_array_get_decimal,
451 set = jit_v2_array_set_decimal,
452 elem_obj_ty = DecimalObj,
453 kind_label = "decimal",
454}
455
456#[unsafe(no_mangle)]
471pub extern "C" fn jit_new_typed_array_typed_object(
472 capacity: u32,
473) -> *mut TypedArray<*const TypedObjectStorage> {
474 let ptr = TypedArray::<*const TypedObjectStorage>::with_capacity(capacity);
475 unsafe { stamp_elem_type(ptr as *mut u8, ELEM_TYPE_TYPED_OBJECT) };
476 ptr
477}
478
479#[unsafe(no_mangle)]
482pub extern "C" fn jit_v2_array_new_typed_object(
483 capacity: u32,
484) -> *mut TypedArray<*const TypedObjectStorage> {
485 jit_new_typed_array_typed_object(capacity)
486}
487
488v2_typed_array_heap_get_set! {
489 get = jit_v2_array_get_typed_object,
490 set = jit_v2_array_set_typed_object,
491 elem_obj_ty = TypedObjectStorage,
492 kind_label = "typed_object",
493}
494
495pub fn string_obj_constant(s: &str) -> *const StringObj {
512 let ptr = StringObj::new(s);
513 unsafe {
521 (*ptr).header.retain();
522 }
523 ptr as *const StringObj
524}
525
526pub fn decimal_obj_constant(value: rust_decimal::Decimal) -> *const DecimalObj {
537 let ptr = DecimalObj::new(value);
538 unsafe {
541 (*ptr).header.retain();
542 }
543 ptr as *const DecimalObj
544}
545
546#[unsafe(no_mangle)]
562pub extern "C" fn jit_v2_array_push(arr: *mut HeapHeader, bits: u64, elem_size: u8) {
563 unsafe {
564 match elem_size {
565 1 => TypedArray::<u8>::push(arr as *mut TypedArray<u8>, bits as u8),
566 4 => TypedArray::<i32>::push(arr as *mut TypedArray<i32>, bits as i32),
567 8 => TypedArray::<i64>::push(arr as *mut TypedArray<i64>, bits as i64),
568 _ => unreachable!("jit_v2_array_push: invalid elem_size {}", elem_size),
569 }
570 }
571}
572
573#[unsafe(no_mangle)]
574pub extern "C" fn jit_v2_array_len_f64(arr: *const TypedArray<f64>) -> u32 {
575 unsafe { TypedArray::len(arr) }
576}
577
578#[unsafe(no_mangle)]
587pub extern "C" fn jit_v2_array_sum_f64(arr: *const TypedArray<f64>) -> f64 {
588 if arr.is_null() {
589 return 0.0;
590 }
591 let (data, len) = unsafe { ((*arr).data as *const f64, (*arr).len as usize) };
592 if len == 0 || data.is_null() {
593 return 0.0;
594 }
595 unsafe { simd_sum_f64_inner(data, len) }
596}
597
598#[unsafe(no_mangle)]
604pub extern "C" fn jit_v2_array_sum_i64(arr: *const TypedArray<i64>) -> i64 {
605 if arr.is_null() {
606 return 0;
607 }
608 let (data, len) = unsafe { ((*arr).data as *const i64, (*arr).len as usize) };
609 if len == 0 || data.is_null() {
610 return 0;
611 }
612 unsafe { simd_sum_i64_inner(data, len) }
613}
614
615const SIMD_SUM_THRESHOLD: usize = 16;
617
618#[unsafe(no_mangle)]
628pub extern "C" fn jit_v2_array_min_f64(arr: *const TypedArray<f64>) -> f64 {
629 if arr.is_null() {
630 return f64::NAN;
631 }
632 let (data, len) = unsafe { ((*arr).data as *const f64, (*arr).len as usize) };
633 if len == 0 || data.is_null() {
634 return f64::NAN;
635 }
636 unsafe { simd_min_f64_inner(data, len) }
637}
638
639#[unsafe(no_mangle)]
645pub extern "C" fn jit_v2_array_max_f64(arr: *const TypedArray<f64>) -> f64 {
646 if arr.is_null() {
647 return f64::NAN;
648 }
649 let (data, len) = unsafe { ((*arr).data as *const f64, (*arr).len as usize) };
650 if len == 0 || data.is_null() {
651 return f64::NAN;
652 }
653 unsafe { simd_max_f64_inner(data, len) }
654}
655
656#[unsafe(no_mangle)]
662pub extern "C" fn jit_v2_array_mean_f64(arr: *const TypedArray<f64>) -> f64 {
663 if arr.is_null() {
664 return f64::NAN;
665 }
666 let (data, len) = unsafe { ((*arr).data as *const f64, (*arr).len as usize) };
667 if len == 0 || data.is_null() {
668 return f64::NAN;
669 }
670 let sum = unsafe { simd_sum_f64_inner(data, len) };
671 sum / (len as f64)
672}
673
674#[unsafe(no_mangle)]
682pub extern "C" fn jit_v2_array_sum_squares_f64(arr: *const TypedArray<f64>) -> f64 {
683 if arr.is_null() {
684 return 0.0;
685 }
686 let (data, len) = unsafe { ((*arr).data as *const f64, (*arr).len as usize) };
687 if len == 0 || data.is_null() {
688 return 0.0;
689 }
690 unsafe { simd_sum_squares_f64_inner(data, len) }
691}
692
693#[unsafe(no_mangle)]
702pub extern "C" fn jit_v2_array_scale_f64(
703 arr: *const TypedArray<f64>,
704 factor: f64,
705) -> *mut TypedArray<f64> {
706 if arr.is_null() {
707 return std::ptr::null_mut();
708 }
709 let (data, len) = unsafe { ((*arr).data as *const f64, (*arr).len as usize) };
710 let out = TypedArray::<f64>::with_capacity(len as u32);
711 if len == 0 || data.is_null() {
712 return out;
713 }
714 unsafe {
715 let out_data = (*out).data as *mut f64;
716 simd_scale_f64_inner(data, out_data, len, factor);
717 (*out).len = len as u32;
718 }
719 out
720}
721
722#[unsafe(no_mangle)]
729pub extern "C" fn jit_v2_array_add_scalar_f64(
730 arr: *const TypedArray<f64>,
731 offset: f64,
732) -> *mut TypedArray<f64> {
733 if arr.is_null() {
734 return std::ptr::null_mut();
735 }
736 let (data, len) = unsafe { ((*arr).data as *const f64, (*arr).len as usize) };
737 let out = TypedArray::<f64>::with_capacity(len as u32);
738 if len == 0 || data.is_null() {
739 return out;
740 }
741 unsafe {
742 let out_data = (*out).data as *mut f64;
743 simd_add_scalar_f64_inner(data, out_data, len, offset);
744 (*out).len = len as u32;
745 }
746 out
747}
748
749#[unsafe(no_mangle)]
758pub extern "C" fn jit_v2_array_add_f64(
759 a: *const TypedArray<f64>,
760 b: *const TypedArray<f64>,
761) -> *mut TypedArray<f64> {
762 if a.is_null() || b.is_null() {
763 return std::ptr::null_mut();
764 }
765 let (a_data, a_len) = unsafe { ((*a).data as *const f64, (*a).len as usize) };
766 let (b_data, b_len) = unsafe { ((*b).data as *const f64, (*b).len as usize) };
767 if a_len != b_len {
768 panic!(
769 "v2 array_add_f64: length mismatch ({} vs {})",
770 a_len, b_len
771 );
772 }
773 let out = TypedArray::<f64>::with_capacity(a_len as u32);
774 if a_len == 0 {
775 return out;
776 }
777 unsafe {
778 let out_data = (*out).data as *mut f64;
779 simd_binary_add_f64_inner(a_data, b_data, out_data, a_len);
780 (*out).len = a_len as u32;
781 }
782 out
783}
784
785#[unsafe(no_mangle)]
791pub extern "C" fn jit_v2_array_mul_f64(
792 a: *const TypedArray<f64>,
793 b: *const TypedArray<f64>,
794) -> *mut TypedArray<f64> {
795 if a.is_null() || b.is_null() {
796 return std::ptr::null_mut();
797 }
798 let (a_data, a_len) = unsafe { ((*a).data as *const f64, (*a).len as usize) };
799 let (b_data, b_len) = unsafe { ((*b).data as *const f64, (*b).len as usize) };
800 if a_len != b_len {
801 panic!(
802 "v2 array_mul_f64: length mismatch ({} vs {})",
803 a_len, b_len
804 );
805 }
806 let out = TypedArray::<f64>::with_capacity(a_len as u32);
807 if a_len == 0 {
808 return out;
809 }
810 unsafe {
811 let out_data = (*out).data as *mut f64;
812 simd_binary_mul_f64_inner(a_data, b_data, out_data, a_len);
813 (*out).len = a_len as u32;
814 }
815 out
816}
817
818#[inline]
819unsafe fn simd_sum_f64_inner(data: *const f64, len: usize) -> f64 {
820 use wide::f64x4;
821 if len < SIMD_SUM_THRESHOLD {
822 let mut s = 0.0_f64;
823 for i in 0..len {
824 s += unsafe { *data.add(i) };
825 }
826 return s;
827 }
828 let chunks = len / 4;
829 let mut acc = f64x4::splat(0.0);
830 for i in 0..chunks {
831 let b = i * 4;
832 let v = unsafe {
833 f64x4::from([
834 *data.add(b),
835 *data.add(b + 1),
836 *data.add(b + 2),
837 *data.add(b + 3),
838 ])
839 };
840 acc += v;
841 }
842 let parts = acc.to_array();
843 let mut s = parts[0] + parts[1] + parts[2] + parts[3];
844 for i in (chunks * 4)..len {
845 s += unsafe { *data.add(i) };
846 }
847 s
848}
849
850#[inline]
851unsafe fn simd_sum_i64_inner(data: *const i64, len: usize) -> i64 {
852 use wide::i64x4;
853 if len < SIMD_SUM_THRESHOLD {
854 let mut s: i64 = 0;
855 for i in 0..len {
856 s = s.wrapping_add(unsafe { *data.add(i) });
857 }
858 return s;
859 }
860 let chunks = len / 4;
861 let mut acc = i64x4::splat(0);
862 for i in 0..chunks {
863 let b = i * 4;
864 let v = unsafe {
865 i64x4::from([
866 *data.add(b),
867 *data.add(b + 1),
868 *data.add(b + 2),
869 *data.add(b + 3),
870 ])
871 };
872 acc = acc + v;
874 }
875 let parts = acc.to_array();
876 let mut s = parts[0]
877 .wrapping_add(parts[1])
878 .wrapping_add(parts[2])
879 .wrapping_add(parts[3]);
880 for i in (chunks * 4)..len {
881 s = s.wrapping_add(unsafe { *data.add(i) });
882 }
883 s
884}
885
886#[inline]
891unsafe fn load_f64x4(data: *const f64, base: usize) -> wide::f64x4 {
892 unsafe {
893 wide::f64x4::from([
894 *data.add(base),
895 *data.add(base + 1),
896 *data.add(base + 2),
897 *data.add(base + 3),
898 ])
899 }
900}
901
902#[inline]
909unsafe fn contains_nan_f64(data: *const f64, len: usize) -> bool {
910 if len < SIMD_SUM_THRESHOLD {
911 for i in 0..len {
912 if unsafe { *data.add(i) }.is_nan() {
913 return true;
914 }
915 }
916 return false;
917 }
918 let chunks = len / 4;
919 for i in 0..chunks {
920 let v = unsafe { load_f64x4(data, i * 4) };
921 let arr = v.to_array();
925 if arr[0].is_nan() || arr[1].is_nan() || arr[2].is_nan() || arr[3].is_nan() {
926 return true;
927 }
928 }
929 for i in (chunks * 4)..len {
930 if unsafe { *data.add(i) }.is_nan() {
931 return true;
932 }
933 }
934 false
935}
936
937#[inline]
938unsafe fn simd_min_f64_inner(data: *const f64, len: usize) -> f64 {
939 if unsafe { contains_nan_f64(data, len) } {
944 return f64::NAN;
945 }
946 if len < SIMD_SUM_THRESHOLD {
947 let mut m = unsafe { *data };
948 for i in 1..len {
949 let v = unsafe { *data.add(i) };
950 if v < m {
951 m = v;
952 }
953 }
954 return m;
955 }
956 let chunks = len / 4;
957 let mut acc = unsafe { load_f64x4(data, 0) };
958 for i in 1..chunks {
959 let v = unsafe { load_f64x4(data, i * 4) };
960 acc = acc.fast_min(v);
961 }
962 let parts = acc.to_array();
963 let mut m = parts[0];
964 for &p in &parts[1..] {
965 if p < m {
966 m = p;
967 }
968 }
969 for i in (chunks * 4)..len {
970 let v = unsafe { *data.add(i) };
971 if v < m {
972 m = v;
973 }
974 }
975 m
976}
977
978#[inline]
979unsafe fn simd_max_f64_inner(data: *const f64, len: usize) -> f64 {
980 if unsafe { contains_nan_f64(data, len) } {
981 return f64::NAN;
982 }
983 if len < SIMD_SUM_THRESHOLD {
984 let mut m = unsafe { *data };
985 for i in 1..len {
986 let v = unsafe { *data.add(i) };
987 if v > m {
988 m = v;
989 }
990 }
991 return m;
992 }
993 let chunks = len / 4;
994 let mut acc = unsafe { load_f64x4(data, 0) };
995 for i in 1..chunks {
996 let v = unsafe { load_f64x4(data, i * 4) };
997 acc = acc.fast_max(v);
998 }
999 let parts = acc.to_array();
1000 let mut m = parts[0];
1001 for &p in &parts[1..] {
1002 if p > m {
1003 m = p;
1004 }
1005 }
1006 for i in (chunks * 4)..len {
1007 let v = unsafe { *data.add(i) };
1008 if v > m {
1009 m = v;
1010 }
1011 }
1012 m
1013}
1014
1015#[inline]
1016unsafe fn simd_sum_squares_f64_inner(data: *const f64, len: usize) -> f64 {
1017 use wide::f64x4;
1018 if len < SIMD_SUM_THRESHOLD {
1019 let mut s = 0.0_f64;
1020 for i in 0..len {
1021 let v = unsafe { *data.add(i) };
1022 s += v * v;
1023 }
1024 return s;
1025 }
1026 let chunks = len / 4;
1027 let mut acc = f64x4::splat(0.0);
1028 for i in 0..chunks {
1029 let v = unsafe { load_f64x4(data, i * 4) };
1030 acc += v * v;
1031 }
1032 let parts = acc.to_array();
1033 let mut s = parts[0] + parts[1] + parts[2] + parts[3];
1034 for i in (chunks * 4)..len {
1035 let v = unsafe { *data.add(i) };
1036 s += v * v;
1037 }
1038 s
1039}
1040
1041#[inline]
1042unsafe fn simd_scale_f64_inner(src: *const f64, dst: *mut f64, len: usize, factor: f64) {
1043 use wide::f64x4;
1044 if len < SIMD_SUM_THRESHOLD {
1045 for i in 0..len {
1046 unsafe { *dst.add(i) = *src.add(i) * factor };
1047 }
1048 return;
1049 }
1050 let chunks = len / 4;
1051 let splat = f64x4::splat(factor);
1052 for i in 0..chunks {
1053 let base = i * 4;
1054 let v = unsafe { load_f64x4(src, base) };
1055 let r = (v * splat).to_array();
1056 unsafe {
1057 *dst.add(base) = r[0];
1058 *dst.add(base + 1) = r[1];
1059 *dst.add(base + 2) = r[2];
1060 *dst.add(base + 3) = r[3];
1061 }
1062 }
1063 for i in (chunks * 4)..len {
1064 unsafe { *dst.add(i) = *src.add(i) * factor };
1065 }
1066}
1067
1068#[inline]
1069unsafe fn simd_add_scalar_f64_inner(src: *const f64, dst: *mut f64, len: usize, offset: f64) {
1070 use wide::f64x4;
1071 if len < SIMD_SUM_THRESHOLD {
1072 for i in 0..len {
1073 unsafe { *dst.add(i) = *src.add(i) + offset };
1074 }
1075 return;
1076 }
1077 let chunks = len / 4;
1078 let splat = f64x4::splat(offset);
1079 for i in 0..chunks {
1080 let base = i * 4;
1081 let v = unsafe { load_f64x4(src, base) };
1082 let r = (v + splat).to_array();
1083 unsafe {
1084 *dst.add(base) = r[0];
1085 *dst.add(base + 1) = r[1];
1086 *dst.add(base + 2) = r[2];
1087 *dst.add(base + 3) = r[3];
1088 }
1089 }
1090 for i in (chunks * 4)..len {
1091 unsafe { *dst.add(i) = *src.add(i) + offset };
1092 }
1093}
1094
1095#[inline]
1096unsafe fn simd_binary_add_f64_inner(
1097 a: *const f64,
1098 b: *const f64,
1099 dst: *mut f64,
1100 len: usize,
1101) {
1102 if len < SIMD_SUM_THRESHOLD {
1103 for i in 0..len {
1104 unsafe { *dst.add(i) = *a.add(i) + *b.add(i) };
1105 }
1106 return;
1107 }
1108 let chunks = len / 4;
1109 for i in 0..chunks {
1110 let base = i * 4;
1111 let va = unsafe { load_f64x4(a, base) };
1112 let vb = unsafe { load_f64x4(b, base) };
1113 let r = (va + vb).to_array();
1114 unsafe {
1115 *dst.add(base) = r[0];
1116 *dst.add(base + 1) = r[1];
1117 *dst.add(base + 2) = r[2];
1118 *dst.add(base + 3) = r[3];
1119 }
1120 }
1121 for i in (chunks * 4)..len {
1122 unsafe { *dst.add(i) = *a.add(i) + *b.add(i) };
1123 }
1124}
1125
1126#[inline]
1127unsafe fn simd_binary_mul_f64_inner(
1128 a: *const f64,
1129 b: *const f64,
1130 dst: *mut f64,
1131 len: usize,
1132) {
1133 if len < SIMD_SUM_THRESHOLD {
1134 for i in 0..len {
1135 unsafe { *dst.add(i) = *a.add(i) * *b.add(i) };
1136 }
1137 return;
1138 }
1139 let chunks = len / 4;
1140 for i in 0..chunks {
1141 let base = i * 4;
1142 let va = unsafe { load_f64x4(a, base) };
1143 let vb = unsafe { load_f64x4(b, base) };
1144 let r = (va * vb).to_array();
1145 unsafe {
1146 *dst.add(base) = r[0];
1147 *dst.add(base + 1) = r[1];
1148 *dst.add(base + 2) = r[2];
1149 *dst.add(base + 3) = r[3];
1150 }
1151 }
1152 for i in (chunks * 4)..len {
1153 unsafe { *dst.add(i) = *a.add(i) * *b.add(i) };
1154 }
1155}
1156
1157#[unsafe(no_mangle)]
1165pub extern "C" fn jit_v2_array_len_i64(arr: *const TypedArray<i64>) -> u32 {
1166 unsafe { TypedArray::len(arr) }
1167}
1168
1169#[unsafe(no_mangle)]
1170pub extern "C" fn jit_v2_array_len_i32(arr: *const TypedArray<i32>) -> u32 {
1171 unsafe { TypedArray::len(arr) }
1172}
1173
1174#[unsafe(no_mangle)]
1175pub extern "C" fn jit_v2_array_len_bool(arr: *const TypedArray<u8>) -> u32 {
1176 unsafe { TypedArray::len(arr) }
1177}
1178
1179#[unsafe(no_mangle)]
1184pub extern "C" fn jit_v2_field_load_f64(ptr: *const u8, offset: u32) -> f64 {
1185 unsafe { (ptr.add(offset as usize) as *const f64).read_unaligned() }
1186}
1187
1188#[unsafe(no_mangle)]
1189pub extern "C" fn jit_v2_field_load_i64(ptr: *const u8, offset: u32) -> i64 {
1190 unsafe { (ptr.add(offset as usize) as *const i64).read_unaligned() }
1191}
1192
1193#[unsafe(no_mangle)]
1194pub extern "C" fn jit_v2_field_load_i32(ptr: *const u8, offset: u32) -> i32 {
1195 unsafe { (ptr.add(offset as usize) as *const i32).read_unaligned() }
1196}
1197
1198#[unsafe(no_mangle)]
1199pub extern "C" fn jit_v2_field_load_ptr(ptr: *const u8, offset: u32) -> *const u8 {
1200 unsafe { (ptr.add(offset as usize) as *const *const u8).read_unaligned() }
1201}
1202
1203#[unsafe(no_mangle)]
1204pub extern "C" fn jit_v2_field_store_f64(ptr: *mut u8, offset: u32, val: f64) {
1205 unsafe {
1206 (ptr.add(offset as usize) as *mut f64).write_unaligned(val);
1207 }
1208}
1209
1210#[unsafe(no_mangle)]
1211pub extern "C" fn jit_v2_field_store_i64(ptr: *mut u8, offset: u32, val: i64) {
1212 unsafe {
1213 (ptr.add(offset as usize) as *mut i64).write_unaligned(val);
1214 }
1215}
1216
1217#[unsafe(no_mangle)]
1218pub extern "C" fn jit_v2_field_store_i32(ptr: *mut u8, offset: u32, val: i32) {
1219 unsafe {
1220 (ptr.add(offset as usize) as *mut i32).write_unaligned(val);
1221 }
1222}
1223
1224#[unsafe(no_mangle)]
1225pub extern "C" fn jit_v2_field_store_ptr(ptr: *mut u8, offset: u32, val: *const u8) {
1226 unsafe {
1227 (ptr.add(offset as usize) as *mut *const u8).write_unaligned(val);
1228 }
1229}
1230
1231#[unsafe(no_mangle)]
1236pub extern "C" fn jit_v2_retain(ptr: *const u8) {
1237 unsafe {
1238 let header = ptr as *const HeapHeader;
1239 (*header).retain();
1240 }
1241}
1242
1243#[unsafe(no_mangle)]
1244pub extern "C" fn jit_v2_release(ptr: *const u8) {
1245 unsafe {
1246 let header = ptr as *const HeapHeader;
1247 if (*header).release() {
1248 let kind = (*header).kind();
1252 let _ = kind; std::alloc::dealloc(
1254 ptr as *mut u8,
1255 std::alloc::Layout::from_size_align(8, 8).unwrap(), );
1257 }
1258 }
1259}
1260
1261#[unsafe(no_mangle)]
1275pub extern "C" fn jit_v2_typed_array_retain(ptr: *const u8) {
1276 if ptr.is_null() {
1277 return;
1278 }
1279 unsafe { shape_value::v2::typed_array::retain_v2_typed_array(ptr as *mut u8) };
1280}
1281
1282#[unsafe(no_mangle)]
1286pub extern "C" fn jit_v2_typed_array_release(ptr: *const u8) {
1287 if ptr.is_null() {
1288 return;
1289 }
1290 unsafe { shape_value::v2::typed_array::release_v2_typed_array(ptr as *mut u8) };
1291}
1292
1293#[unsafe(no_mangle)]
1301pub extern "C" fn jit_v2_alloc_struct(size: u32, kind: u16) -> *mut u8 {
1302 let align = 8; let layout = std::alloc::Layout::from_size_align(size as usize, align).unwrap();
1304 let ptr = unsafe { std::alloc::alloc_zeroed(layout) };
1305 unsafe {
1307 let header = ptr as *mut HeapHeader;
1308 std::ptr::write(header, HeapHeader::new(kind));
1309 }
1310 ptr
1311}
1312
1313#[cfg(test)]
1314mod tests {
1315 use super::*;
1316 use shape_value::v2::heap_header::HEAP_KIND_V2_STRUCT;
1317
1318 fn jit_v2_array_push_f64(arr: *mut TypedArray<f64>, val: f64) {
1326 jit_v2_array_push(arr as *mut HeapHeader, val.to_bits(), 8);
1327 }
1328
1329 fn jit_v2_array_push_i64(arr: *mut TypedArray<i64>, val: i64) {
1330 jit_v2_array_push(arr as *mut HeapHeader, val as u64, 8);
1331 }
1332
1333 fn jit_v2_array_push_i32(arr: *mut TypedArray<i32>, val: i32) {
1334 jit_v2_array_push(arr as *mut HeapHeader, (val as u32) as u64, 4);
1335 }
1336
1337 fn jit_v2_array_push_bool(arr: *mut TypedArray<u8>, val: u8) {
1338 jit_v2_array_push(arr as *mut HeapHeader, val as u64, 1);
1339 }
1340
1341 #[test]
1344 fn test_simd_sum_f64_small_scalar_path() {
1345 let arr = jit_v2_array_new_f64(8);
1347 for i in 0..8 {
1348 jit_v2_array_push_f64(arr, (i + 1) as f64); }
1350 let sum = jit_v2_array_sum_f64(arr);
1351 assert!((sum - 36.0).abs() < 1e-12);
1352 unsafe { TypedArray::drop_array(arr) };
1353 }
1354
1355 #[test]
1356 fn test_simd_sum_f64_large_vector_path() {
1357 let arr = jit_v2_array_new_f64(128);
1360 let mut expected = 0.0_f64;
1361 for i in 0..101 {
1362 let v = i as f64 * 0.5;
1363 jit_v2_array_push_f64(arr, v);
1364 expected += v;
1365 }
1366 let sum = jit_v2_array_sum_f64(arr);
1367 assert!(
1368 (sum - expected).abs() < 1e-9,
1369 "sum={} expected={}",
1370 sum,
1371 expected
1372 );
1373 unsafe { TypedArray::drop_array(arr) };
1374 }
1375
1376 #[test]
1377 fn test_simd_sum_f64_empty() {
1378 let arr = jit_v2_array_new_f64(0);
1379 let sum = jit_v2_array_sum_f64(arr);
1380 assert_eq!(sum, 0.0);
1381 unsafe { TypedArray::drop_array(arr) };
1382 }
1383
1384 #[test]
1385 fn test_simd_sum_f64_null_safe() {
1386 assert_eq!(jit_v2_array_sum_f64(std::ptr::null()), 0.0);
1387 }
1388
1389 #[test]
1390 fn test_simd_sum_i64_small_scalar_path() {
1391 let arr = jit_v2_array_new_i64(16);
1392 for i in 0..10 {
1393 jit_v2_array_push_i64(arr, (i + 1) as i64);
1394 }
1395 let sum = jit_v2_array_sum_i64(arr);
1396 assert_eq!(sum, 55);
1397 unsafe { TypedArray::drop_array(arr) };
1398 }
1399
1400 #[test]
1401 fn test_simd_sum_i64_large_vector_path() {
1402 let arr = jit_v2_array_new_i64(128);
1403 let mut expected: i64 = 0;
1404 for i in 0..103 {
1405 let v = i as i64;
1406 jit_v2_array_push_i64(arr, v);
1407 expected = expected.wrapping_add(v);
1408 }
1409 let sum = jit_v2_array_sum_i64(arr);
1410 assert_eq!(sum, expected);
1411 unsafe { TypedArray::drop_array(arr) };
1412 }
1413
1414 #[test]
1415 fn test_simd_sum_i64_wrapping_overflow() {
1416 let arr = jit_v2_array_new_i64(16);
1419 jit_v2_array_push_i64(arr, i64::MAX);
1420 jit_v2_array_push_i64(arr, 1);
1421 for _ in 2..16 {
1422 jit_v2_array_push_i64(arr, 0);
1423 }
1424 let sum = jit_v2_array_sum_i64(arr);
1425 assert_eq!(sum, i64::MAX.wrapping_add(1));
1426 unsafe { TypedArray::drop_array(arr) };
1427 }
1428
1429 #[test]
1430 fn test_array_f64_roundtrip() {
1431 let arr = jit_v2_array_new_f64(4);
1432 jit_v2_array_push_f64(arr, 1.0);
1433 jit_v2_array_push_f64(arr, 2.5);
1434 jit_v2_array_push_f64(arr, 3.14);
1435 assert_eq!(jit_v2_array_len_f64(arr), 3);
1436 assert!((jit_v2_array_get_f64(arr, 0) - 1.0).abs() < f64::EPSILON);
1437 assert!((jit_v2_array_get_f64(arr, 1) - 2.5).abs() < f64::EPSILON);
1438 assert!((jit_v2_array_get_f64(arr, 2) - 3.14).abs() < f64::EPSILON);
1439 unsafe { TypedArray::drop_array(arr) };
1440 }
1441
1442 #[test]
1443 fn test_array_i64_roundtrip() {
1444 let arr = jit_v2_array_new_i64(4);
1445 jit_v2_array_push_i64(arr, 42);
1446 jit_v2_array_push_i64(arr, -100);
1447 assert_eq!(jit_v2_array_len_i64(arr), 2);
1448 assert_eq!(jit_v2_array_get_i64(arr, 0), 42);
1449 assert_eq!(jit_v2_array_get_i64(arr, 1), -100);
1450 unsafe { TypedArray::drop_array(arr) };
1451 }
1452
1453 #[test]
1454 fn test_array_i32_roundtrip() {
1455 let arr = jit_v2_array_new_i32(4);
1456 jit_v2_array_push_i32(arr, 7);
1457 jit_v2_array_push_i32(arr, -3);
1458 assert_eq!(jit_v2_array_len_i32(arr), 2);
1459 assert_eq!(jit_v2_array_get_i32(arr, 0), 7);
1460 assert_eq!(jit_v2_array_get_i32(arr, 1), -3);
1461 unsafe { TypedArray::drop_array(arr) };
1462 }
1463
1464 #[test]
1465 fn test_array_bool_roundtrip() {
1466 let arr = jit_v2_array_new_bool(4);
1468 jit_v2_array_push_bool(arr, 1);
1469 jit_v2_array_push_bool(arr, 0);
1470 jit_v2_array_push_bool(arr, 1);
1471 assert_eq!(jit_v2_array_len_bool(arr), 3);
1472 assert_eq!(jit_v2_array_get_bool(arr, 0), 1);
1473 assert_eq!(jit_v2_array_get_bool(arr, 1), 0);
1474 assert_eq!(jit_v2_array_get_bool(arr, 2), 1);
1475 unsafe { TypedArray::drop_array(arr) };
1476 }
1477
1478 #[test]
1479 fn test_array_set_bool() {
1480 let arr = jit_v2_array_new_bool(4);
1481 jit_v2_array_push_bool(arr, 0);
1482 jit_v2_array_push_bool(arr, 0);
1483 jit_v2_array_set_bool(arr, 0, 1);
1484 assert_eq!(jit_v2_array_get_bool(arr, 0), 1);
1485 assert_eq!(jit_v2_array_get_bool(arr, 1), 0);
1486 unsafe { TypedArray::drop_array(arr) };
1487 }
1488
1489 #[test]
1490 fn test_array_set_f64() {
1491 let arr = jit_v2_array_new_f64(4);
1492 jit_v2_array_push_f64(arr, 1.0);
1493 jit_v2_array_push_f64(arr, 2.0);
1494 jit_v2_array_set_f64(arr, 0, 99.0);
1495 assert!((jit_v2_array_get_f64(arr, 0) - 99.0).abs() < f64::EPSILON);
1496 assert!((jit_v2_array_get_f64(arr, 1) - 2.0).abs() < f64::EPSILON);
1497 unsafe { TypedArray::drop_array(arr) };
1498 }
1499
1500 #[test]
1501 fn test_array_get_oob_returns_none_via_typed_array() {
1502 let arr = jit_v2_array_new_f64(4);
1505 jit_v2_array_push_f64(arr, 1.0);
1506 unsafe {
1507 assert_eq!(TypedArray::get(arr, 5), None);
1508 TypedArray::drop_array(arr);
1509 }
1510 }
1511
1512 #[test]
1513 fn test_field_load_store_f64() {
1514 let ptr = jit_v2_alloc_struct(24, HEAP_KIND_V2_STRUCT);
1515 jit_v2_field_store_f64(ptr, 8, 3.14);
1516 let val = jit_v2_field_load_f64(ptr, 8);
1517 assert!((val - 3.14).abs() < f64::EPSILON);
1518 unsafe { std::alloc::dealloc(ptr, std::alloc::Layout::from_size_align(24, 8).unwrap()) };
1519 }
1520
1521 #[test]
1522 fn test_field_load_store_i64() {
1523 let ptr = jit_v2_alloc_struct(24, HEAP_KIND_V2_STRUCT);
1524 jit_v2_field_store_i64(ptr, 8, -42);
1525 assert_eq!(jit_v2_field_load_i64(ptr, 8), -42);
1526 unsafe { std::alloc::dealloc(ptr, std::alloc::Layout::from_size_align(24, 8).unwrap()) };
1527 }
1528
1529 #[test]
1530 fn test_field_load_store_i32() {
1531 let ptr = jit_v2_alloc_struct(16, HEAP_KIND_V2_STRUCT);
1532 jit_v2_field_store_i32(ptr, 8, 999);
1533 assert_eq!(jit_v2_field_load_i32(ptr, 8), 999);
1534 unsafe { std::alloc::dealloc(ptr, std::alloc::Layout::from_size_align(16, 8).unwrap()) };
1535 }
1536
1537 #[test]
1538 fn test_alloc_struct_initializes_header() {
1539 let ptr = jit_v2_alloc_struct(24, HEAP_KIND_V2_STRUCT);
1540 unsafe {
1541 let header = &*(ptr as *const HeapHeader);
1542 assert_eq!(header.kind(), HEAP_KIND_V2_STRUCT);
1543 assert_eq!(header.get_refcount(), 1);
1544 std::alloc::dealloc(ptr, std::alloc::Layout::from_size_align(24, 8).unwrap());
1545 }
1546 }
1547
1548 fn fill_f64(arr: *mut TypedArray<f64>, vals: &[f64]) {
1551 for &v in vals {
1552 jit_v2_array_push_f64(arr, v);
1553 }
1554 }
1555
1556 #[test]
1557 fn test_simd_min_f64_small_scalar_path() {
1558 let arr = jit_v2_array_new_f64(8);
1559 fill_f64(arr, &[3.0, 1.5, 4.0, -2.0, 0.5, 7.0, -9.0, 2.0]);
1560 let m = jit_v2_array_min_f64(arr);
1561 assert_eq!(m, -9.0);
1562 unsafe { TypedArray::drop_array(arr) };
1563 }
1564
1565 #[test]
1566 fn test_simd_min_f64_large_vector_path() {
1567 let arr = jit_v2_array_new_f64(64);
1568 let mut expected = f64::INFINITY;
1569 for i in 0..33 {
1570 let v = (17 - i) as f64 * 0.25;
1572 jit_v2_array_push_f64(arr, v);
1573 if v < expected {
1574 expected = v;
1575 }
1576 }
1577 let m = jit_v2_array_min_f64(arr);
1578 assert!((m - expected).abs() < 1e-12);
1579 unsafe { TypedArray::drop_array(arr) };
1580 }
1581
1582 #[test]
1583 fn test_simd_min_f64_empty_and_null() {
1584 let arr = jit_v2_array_new_f64(0);
1585 assert!(jit_v2_array_min_f64(arr).is_nan());
1586 unsafe { TypedArray::drop_array(arr) };
1587 assert!(jit_v2_array_min_f64(std::ptr::null()).is_nan());
1588 }
1589
1590 #[test]
1591 fn test_simd_min_f64_single_element() {
1592 let arr = jit_v2_array_new_f64(1);
1593 jit_v2_array_push_f64(arr, 42.5);
1594 assert_eq!(jit_v2_array_min_f64(arr), 42.5);
1595 unsafe { TypedArray::drop_array(arr) };
1596 }
1597
1598 #[test]
1599 fn test_simd_min_f64_nan_propagates() {
1600 let arr = jit_v2_array_new_f64(4);
1602 fill_f64(arr, &[1.0, 2.0, f64::NAN, 4.0]);
1603 assert!(jit_v2_array_min_f64(arr).is_nan());
1604 unsafe { TypedArray::drop_array(arr) };
1605 let arr = jit_v2_array_new_f64(20);
1607 let mut v = vec![1.0_f64; 20];
1608 v[7] = f64::NAN;
1609 fill_f64(arr, &v);
1610 assert!(jit_v2_array_min_f64(arr).is_nan());
1611 unsafe { TypedArray::drop_array(arr) };
1612 }
1613
1614 #[test]
1615 fn test_simd_max_f64_small_scalar_path() {
1616 let arr = jit_v2_array_new_f64(8);
1617 fill_f64(arr, &[3.0, 1.5, 4.0, -2.0, 0.5, 7.0, -9.0, 2.0]);
1618 let m = jit_v2_array_max_f64(arr);
1619 assert_eq!(m, 7.0);
1620 unsafe { TypedArray::drop_array(arr) };
1621 }
1622
1623 #[test]
1624 fn test_simd_max_f64_large_vector_path() {
1625 let arr = jit_v2_array_new_f64(64);
1626 let mut expected = f64::NEG_INFINITY;
1627 for i in 0..37 {
1628 let v = (i as f64 * 1.3) - 5.0;
1629 jit_v2_array_push_f64(arr, v);
1630 if v > expected {
1631 expected = v;
1632 }
1633 }
1634 let m = jit_v2_array_max_f64(arr);
1635 assert!((m - expected).abs() < 1e-12);
1636 unsafe { TypedArray::drop_array(arr) };
1637 }
1638
1639 #[test]
1640 fn test_simd_max_f64_nan_propagates() {
1641 let arr = jit_v2_array_new_f64(20);
1642 let mut v = vec![1.0_f64; 20];
1643 v[3] = f64::NAN;
1644 fill_f64(arr, &v);
1645 assert!(jit_v2_array_max_f64(arr).is_nan());
1646 unsafe { TypedArray::drop_array(arr) };
1647 }
1648
1649 #[test]
1650 fn test_simd_mean_f64_small() {
1651 let arr = jit_v2_array_new_f64(4);
1652 fill_f64(arr, &[1.0, 2.0, 3.0, 4.0]);
1653 let m = jit_v2_array_mean_f64(arr);
1654 assert!((m - 2.5).abs() < 1e-12);
1655 unsafe { TypedArray::drop_array(arr) };
1656 }
1657
1658 #[test]
1659 fn test_simd_mean_f64_large() {
1660 let arr = jit_v2_array_new_f64(64);
1661 let mut total = 0.0_f64;
1662 for i in 0..50 {
1663 let v = i as f64 + 0.5;
1664 jit_v2_array_push_f64(arr, v);
1665 total += v;
1666 }
1667 let expected = total / 50.0;
1668 let m = jit_v2_array_mean_f64(arr);
1669 assert!((m - expected).abs() < 1e-9, "mean={} expected={}", m, expected);
1670 unsafe { TypedArray::drop_array(arr) };
1671 }
1672
1673 #[test]
1674 fn test_simd_mean_f64_empty_is_nan() {
1675 let arr = jit_v2_array_new_f64(0);
1676 assert!(jit_v2_array_mean_f64(arr).is_nan());
1677 unsafe { TypedArray::drop_array(arr) };
1678 assert!(jit_v2_array_mean_f64(std::ptr::null()).is_nan());
1679 }
1680
1681 #[test]
1682 fn test_simd_sum_squares_f64_small() {
1683 let arr = jit_v2_array_new_f64(4);
1684 fill_f64(arr, &[1.0, 2.0, 3.0, 4.0]);
1685 let s = jit_v2_array_sum_squares_f64(arr);
1687 assert!((s - 30.0).abs() < 1e-12);
1688 unsafe { TypedArray::drop_array(arr) };
1689 }
1690
1691 #[test]
1692 fn test_simd_sum_squares_f64_large() {
1693 let arr = jit_v2_array_new_f64(64);
1694 let mut expected = 0.0_f64;
1695 for i in 0..50 {
1696 let v = (i as f64 - 25.0) * 0.5;
1697 jit_v2_array_push_f64(arr, v);
1698 expected += v * v;
1699 }
1700 let s = jit_v2_array_sum_squares_f64(arr);
1701 assert!((s - expected).abs() < 1e-8);
1702 unsafe { TypedArray::drop_array(arr) };
1703 }
1704
1705 #[test]
1706 fn test_simd_sum_squares_f64_empty() {
1707 let arr = jit_v2_array_new_f64(0);
1708 assert_eq!(jit_v2_array_sum_squares_f64(arr), 0.0);
1709 unsafe { TypedArray::drop_array(arr) };
1710 assert_eq!(jit_v2_array_sum_squares_f64(std::ptr::null()), 0.0);
1711 }
1712
1713 fn collect_f64(arr: *const TypedArray<f64>) -> Vec<f64> {
1716 let len = unsafe { (*arr).len } as usize;
1717 let mut out = Vec::with_capacity(len);
1718 for i in 0..len {
1719 out.push(unsafe { TypedArray::<f64>::get_unchecked(arr, i as u32) });
1720 }
1721 out
1722 }
1723
1724 #[test]
1725 fn test_simd_scale_f64_small() {
1726 let a = jit_v2_array_new_f64(4);
1727 fill_f64(a, &[1.0, 2.0, 3.0, 4.0]);
1728 let out = jit_v2_array_scale_f64(a, 2.5);
1729 assert_eq!(collect_f64(out), vec![2.5, 5.0, 7.5, 10.0]);
1730 unsafe {
1731 TypedArray::drop_array(a);
1732 TypedArray::drop_array(out);
1733 }
1734 }
1735
1736 #[test]
1737 fn test_simd_scale_f64_large() {
1738 let a = jit_v2_array_new_f64(32);
1739 for i in 0..20 {
1740 jit_v2_array_push_f64(a, i as f64);
1741 }
1742 let out = jit_v2_array_scale_f64(a, -0.5);
1743 let got = collect_f64(out);
1744 for i in 0..20 {
1745 assert!((got[i] - (i as f64 * -0.5)).abs() < 1e-12);
1746 }
1747 unsafe {
1748 TypedArray::drop_array(a);
1749 TypedArray::drop_array(out);
1750 }
1751 }
1752
1753 #[test]
1754 fn test_simd_scale_f64_empty() {
1755 let a = jit_v2_array_new_f64(0);
1756 let out = jit_v2_array_scale_f64(a, 3.0);
1757 assert_eq!(unsafe { (*out).len }, 0);
1758 unsafe {
1759 TypedArray::drop_array(a);
1760 TypedArray::drop_array(out);
1761 }
1762 }
1763
1764 #[test]
1765 fn test_simd_add_scalar_f64_small() {
1766 let a = jit_v2_array_new_f64(3);
1767 fill_f64(a, &[1.0, 2.0, 3.0]);
1768 let out = jit_v2_array_add_scalar_f64(a, 10.0);
1769 assert_eq!(collect_f64(out), vec![11.0, 12.0, 13.0]);
1770 unsafe {
1771 TypedArray::drop_array(a);
1772 TypedArray::drop_array(out);
1773 }
1774 }
1775
1776 #[test]
1777 fn test_simd_add_scalar_f64_large() {
1778 let a = jit_v2_array_new_f64(32);
1779 for i in 0..25 {
1780 jit_v2_array_push_f64(a, i as f64);
1781 }
1782 let out = jit_v2_array_add_scalar_f64(a, 100.0);
1783 let got = collect_f64(out);
1784 for i in 0..25 {
1785 assert!((got[i] - (i as f64 + 100.0)).abs() < 1e-12);
1786 }
1787 unsafe {
1788 TypedArray::drop_array(a);
1789 TypedArray::drop_array(out);
1790 }
1791 }
1792
1793 #[test]
1794 fn test_simd_add_f64_small() {
1795 let a = jit_v2_array_new_f64(4);
1796 let b = jit_v2_array_new_f64(4);
1797 fill_f64(a, &[1.0, 2.0, 3.0, 4.0]);
1798 fill_f64(b, &[10.0, 20.0, 30.0, 40.0]);
1799 let out = jit_v2_array_add_f64(a, b);
1800 assert_eq!(collect_f64(out), vec![11.0, 22.0, 33.0, 44.0]);
1801 unsafe {
1802 TypedArray::drop_array(a);
1803 TypedArray::drop_array(b);
1804 TypedArray::drop_array(out);
1805 }
1806 }
1807
1808 #[test]
1809 fn test_simd_add_f64_large() {
1810 let a = jit_v2_array_new_f64(32);
1811 let b = jit_v2_array_new_f64(32);
1812 for i in 0..23 {
1813 jit_v2_array_push_f64(a, i as f64);
1814 jit_v2_array_push_f64(b, (i * 2) as f64);
1815 }
1816 let out = jit_v2_array_add_f64(a, b);
1817 let got = collect_f64(out);
1818 for i in 0..23 {
1819 assert!((got[i] - (i as f64 + (i * 2) as f64)).abs() < 1e-12);
1820 }
1821 unsafe {
1822 TypedArray::drop_array(a);
1823 TypedArray::drop_array(b);
1824 TypedArray::drop_array(out);
1825 }
1826 }
1827
1828 #[test]
1829 fn test_simd_mul_f64_small() {
1830 let a = jit_v2_array_new_f64(4);
1831 let b = jit_v2_array_new_f64(4);
1832 fill_f64(a, &[1.0, 2.0, 3.0, 4.0]);
1833 fill_f64(b, &[10.0, 20.0, 30.0, 40.0]);
1834 let out = jit_v2_array_mul_f64(a, b);
1835 assert_eq!(collect_f64(out), vec![10.0, 40.0, 90.0, 160.0]);
1836 unsafe {
1837 TypedArray::drop_array(a);
1838 TypedArray::drop_array(b);
1839 TypedArray::drop_array(out);
1840 }
1841 }
1842
1843 #[test]
1844 fn test_simd_mul_f64_large() {
1845 let a = jit_v2_array_new_f64(32);
1846 let b = jit_v2_array_new_f64(32);
1847 for i in 0..20 {
1848 jit_v2_array_push_f64(a, (i as f64 + 1.0) * 0.5);
1849 jit_v2_array_push_f64(b, (i as f64 + 1.0) * 2.0);
1850 }
1851 let out = jit_v2_array_mul_f64(a, b);
1852 let got = collect_f64(out);
1853 for i in 0..20 {
1854 let expected = ((i as f64 + 1.0) * 0.5) * ((i as f64 + 1.0) * 2.0);
1855 assert!((got[i] - expected).abs() < 1e-12);
1856 }
1857 unsafe {
1858 TypedArray::drop_array(a);
1859 TypedArray::drop_array(b);
1860 TypedArray::drop_array(out);
1861 }
1862 }
1863
1864 #[test]
1865 fn test_simd_add_f64_empty() {
1866 let a = jit_v2_array_new_f64(0);
1867 let b = jit_v2_array_new_f64(0);
1868 let out = jit_v2_array_add_f64(a, b);
1869 assert_eq!(unsafe { (*out).len }, 0);
1870 unsafe {
1871 TypedArray::drop_array(a);
1872 TypedArray::drop_array(b);
1873 TypedArray::drop_array(out);
1874 }
1875 }
1876
1877 #[test]
1878 fn test_simd_add_f64_null_inputs() {
1879 assert!(jit_v2_array_add_f64(std::ptr::null(), std::ptr::null()).is_null());
1880 }
1881
1882 #[test]
1883 fn test_retain_increments_refcount() {
1884 let ptr = jit_v2_alloc_struct(24, HEAP_KIND_V2_STRUCT);
1885 unsafe {
1886 let header = &*(ptr as *const HeapHeader);
1887 assert_eq!(header.get_refcount(), 1);
1888 jit_v2_retain(ptr);
1889 assert_eq!(header.get_refcount(), 2);
1890 jit_v2_retain(ptr);
1891 assert_eq!(header.get_refcount(), 3);
1892 std::alloc::dealloc(ptr, std::alloc::Layout::from_size_align(24, 8).unwrap());
1894 }
1895 }
1896}