sigil_parser/
codegen.rs

1//! Sigil JIT Compiler using Cranelift
2//!
3//! Compiles Sigil AST to native machine code for high-performance execution.
4//!
5//! Optimizations implemented:
6//! - Direct condition branching (no redundant boolean conversion)
7//! - Constant folding for arithmetic expressions
8//! - Tail call optimization for recursive functions
9//! - Efficient comparison code generation
10
11#[cfg(feature = "jit")]
12pub mod jit {
13    use cranelift_codegen::ir::condcodes::IntCC;
14    use cranelift_codegen::ir::{types, AbiParam, InstBuilder, UserFuncName};
15    use cranelift_codegen::settings::{self, Configurable};
16    use cranelift_codegen::Context;
17    use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
18    use cranelift_jit::{JITBuilder, JITModule};
19    use cranelift_module::{FuncId, Linkage, Module};
20
21    use std::collections::HashMap;
22    use std::mem;
23
24    use crate::ast::{
25        self, BinOp, Expr, ExternBlock, ExternFunction, ExternItem, Item, Literal, PipeOp,
26        TypeExpr, UnaryOp,
27    };
28    use crate::ffi::ctypes::CType;
29    use crate::optimize::{OptLevel, Optimizer};
30    use crate::parser::Parser;
31
32    /// Runtime value representation
33    ///
34    /// We use a tagged union representation:
35    /// - 64-bit value
36    /// - Low 3 bits are tag (NaN-boxing style, but simpler)
37    ///
38    /// For maximum performance, we use unboxed representations:
39    /// - Integers: raw i64
40    /// - Floats: raw f64
41    /// - Booleans: 0 or 1
42    /// - Arrays/Strings: pointers to heap
43    #[repr(C)]
44    #[derive(Clone, Copy, Debug)]
45    pub struct SigilValue(pub u64);
46
47    impl SigilValue {
48        // Tag constants (stored in low bits for pointers, high bits for numbers)
49        pub const TAG_INT: u64 = 0;
50        pub const TAG_FLOAT: u64 = 1;
51        pub const TAG_BOOL: u64 = 2;
52        pub const TAG_NULL: u64 = 3;
53        pub const TAG_PTR: u64 = 4; // Heap-allocated objects
54
55        #[inline]
56        pub fn from_int(v: i64) -> Self {
57            SigilValue(v as u64)
58        }
59
60        #[inline]
61        pub fn from_float(v: f64) -> Self {
62            SigilValue(v.to_bits())
63        }
64
65        #[inline]
66        pub fn from_bool(v: bool) -> Self {
67            SigilValue(if v { 1 } else { 0 })
68        }
69
70        #[inline]
71        pub fn as_int(self) -> i64 {
72            self.0 as i64
73        }
74
75        #[inline]
76        pub fn as_float(self) -> f64 {
77            f64::from_bits(self.0)
78        }
79
80        #[inline]
81        pub fn as_bool(self) -> bool {
82            self.0 != 0
83        }
84    }
85
86    /// Compiled function signature
87    type CompiledFn = unsafe extern "C" fn() -> i64;
88    #[allow(dead_code)]
89    type CompiledFnWithArgs = unsafe extern "C" fn(i64) -> i64;
90
91    /// Extern function signature info for FFI
92    #[derive(Clone, Debug)]
93    pub struct ExternFnSig {
94        pub name: String,
95        pub params: Vec<types::Type>,
96        pub returns: Option<types::Type>,
97        pub variadic: bool,
98        pub func_id: FuncId,
99    }
100
101    /// JIT Compiler for Sigil
102    pub struct JitCompiler {
103        /// The JIT module
104        module: JITModule,
105        /// Builder context (reused for efficiency)
106        builder_ctx: FunctionBuilderContext,
107        /// Codegen context
108        ctx: Context,
109        /// Compiled functions
110        functions: HashMap<String, FuncId>,
111        /// Extern "C" function declarations
112        extern_functions: HashMap<String, ExternFnSig>,
113        /// Variable counter for unique variable indices
114        #[allow(dead_code)]
115        var_counter: usize,
116        /// Built-in function addresses
117        #[allow(dead_code)]
118        builtins: HashMap<String, *const u8>,
119    }
120
121    impl JitCompiler {
122        /// Create a new JIT compiler
123        pub fn new() -> Result<Self, String> {
124            let mut flag_builder = settings::builder();
125            // Disable PIC for better codegen
126            flag_builder.set("use_colocated_libcalls", "false").unwrap();
127            flag_builder.set("is_pic", "false").unwrap();
128            // Maximum optimization level
129            flag_builder.set("opt_level", "speed").unwrap();
130            // Enable additional optimizations
131            flag_builder.set("enable_verifier", "false").unwrap(); // Disable verifier in release for speed
132            flag_builder.set("enable_alias_analysis", "true").unwrap();
133
134            // Get native ISA with CPU feature detection (AVX2, SSE4, etc. auto-detected)
135            let isa_builder = cranelift_native::builder().map_err(|e| e.to_string())?;
136            let isa = isa_builder
137                .finish(settings::Flags::new(flag_builder))
138                .map_err(|e| e.to_string())?;
139
140            let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
141
142            // Register built-in functions
143            let builtins = Self::register_builtins(&mut builder);
144
145            let module = JITModule::new(builder);
146
147            Ok(Self {
148                module,
149                builder_ctx: FunctionBuilderContext::new(),
150                ctx: Context::new(),
151                functions: HashMap::new(),
152                extern_functions: HashMap::new(),
153                var_counter: 0,
154                builtins,
155            })
156        }
157
158        /// Register built-in runtime functions
159        fn register_builtins(builder: &mut JITBuilder) -> HashMap<String, *const u8> {
160            let mut builtins = HashMap::new();
161
162            // Math functions from libc
163            builder.symbol("sigil_sqrt", sigil_sqrt as *const u8);
164            builder.symbol("sigil_sin", sigil_sin as *const u8);
165            builder.symbol("sigil_cos", sigil_cos as *const u8);
166            builder.symbol("sigil_pow", sigil_pow as *const u8);
167            builder.symbol("sigil_exp", sigil_exp as *const u8);
168            builder.symbol("sigil_ln", sigil_ln as *const u8);
169            builder.symbol("sigil_floor", sigil_floor as *const u8);
170            builder.symbol("sigil_ceil", sigil_ceil as *const u8);
171            builder.symbol("sigil_abs", sigil_abs as *const u8);
172
173            // I/O functions
174            builder.symbol("sigil_print", sigil_print as *const u8);
175            builder.symbol("sigil_print_int", sigil_print_int as *const u8);
176            builder.symbol("sigil_print_float", sigil_print_float as *const u8);
177            builder.symbol("sigil_print_str", sigil_print_str as *const u8);
178
179            // Time functions
180            builder.symbol("sigil_now", sigil_now as *const u8);
181
182            // Type-aware arithmetic (for dynamic typing)
183            builder.symbol("sigil_add", sigil_add as *const u8);
184            builder.symbol("sigil_sub", sigil_sub as *const u8);
185            builder.symbol("sigil_mul", sigil_mul as *const u8);
186            builder.symbol("sigil_div", sigil_div as *const u8);
187            builder.symbol("sigil_lt", sigil_lt as *const u8);
188            builder.symbol("sigil_le", sigil_le as *const u8);
189            builder.symbol("sigil_gt", sigil_gt as *const u8);
190            builder.symbol("sigil_ge", sigil_ge as *const u8);
191
192            // SIMD operations
193            builder.symbol("sigil_simd_new", sigil_simd_new as *const u8);
194            builder.symbol("sigil_simd_splat", sigil_simd_splat as *const u8);
195            builder.symbol("sigil_simd_add", sigil_simd_add as *const u8);
196            builder.symbol("sigil_simd_sub", sigil_simd_sub as *const u8);
197            builder.symbol("sigil_simd_mul", sigil_simd_mul as *const u8);
198            builder.symbol("sigil_simd_div", sigil_simd_div as *const u8);
199            builder.symbol("sigil_simd_dot", sigil_simd_dot as *const u8);
200            builder.symbol("sigil_simd_hadd", sigil_simd_hadd as *const u8);
201            builder.symbol("sigil_simd_length_sq", sigil_simd_length_sq as *const u8);
202            builder.symbol("sigil_simd_length", sigil_simd_length as *const u8);
203            builder.symbol("sigil_simd_normalize", sigil_simd_normalize as *const u8);
204            builder.symbol("sigil_simd_cross", sigil_simd_cross as *const u8);
205            builder.symbol("sigil_simd_min", sigil_simd_min as *const u8);
206            builder.symbol("sigil_simd_max", sigil_simd_max as *const u8);
207            builder.symbol("sigil_simd_extract", sigil_simd_extract as *const u8);
208            builder.symbol("sigil_simd_free", sigil_simd_free as *const u8);
209
210            // Array functions
211            builder.symbol("sigil_array_new", sigil_array_new as *const u8);
212            builder.symbol("sigil_array_push", sigil_array_push as *const u8);
213            builder.symbol("sigil_array_get", sigil_array_get as *const u8);
214            builder.symbol("sigil_array_set", sigil_array_set as *const u8);
215            builder.symbol("sigil_array_len", sigil_array_len as *const u8);
216
217            // SIMD-optimized array operations
218            builder.symbol("sigil_array_sum", sigil_array_sum as *const u8);
219            builder.symbol("sigil_array_scale", sigil_array_scale as *const u8);
220            builder.symbol("sigil_array_offset", sigil_array_offset as *const u8);
221            builder.symbol("sigil_array_dot", sigil_array_dot as *const u8);
222            builder.symbol("sigil_array_add", sigil_array_add as *const u8);
223            builder.symbol("sigil_array_mul", sigil_array_mul as *const u8);
224            builder.symbol("sigil_array_min", sigil_array_min as *const u8);
225            builder.symbol("sigil_array_max", sigil_array_max as *const u8);
226            builder.symbol("sigil_array_fill", sigil_array_fill as *const u8);
227
228            // PipeOp array access functions (morphemes)
229            builder.symbol("sigil_array_first", sigil_array_first as *const u8);
230            builder.symbol("sigil_array_last", sigil_array_last as *const u8);
231            builder.symbol("sigil_array_middle", sigil_array_middle as *const u8);
232            builder.symbol("sigil_array_choice", sigil_array_choice as *const u8);
233            builder.symbol("sigil_array_nth", sigil_array_nth as *const u8);
234            builder.symbol("sigil_array_next", sigil_array_next as *const u8);
235            builder.symbol("sigil_array_product", sigil_array_product as *const u8);
236            builder.symbol("sigil_array_sort", sigil_array_sort as *const u8);
237
238            // Parallel execution functions (∥ morpheme)
239            builder.symbol("sigil_parallel_map", sigil_parallel_map as *const u8);
240            builder.symbol("sigil_parallel_filter", sigil_parallel_filter as *const u8);
241            builder.symbol("sigil_parallel_reduce", sigil_parallel_reduce as *const u8);
242
243            // GPU compute functions (⊛ morpheme) - stubs for now
244            builder.symbol("sigil_gpu_map", sigil_gpu_map as *const u8);
245            builder.symbol("sigil_gpu_filter", sigil_gpu_filter as *const u8);
246            builder.symbol("sigil_gpu_reduce", sigil_gpu_reduce as *const u8);
247
248            // Memoization cache functions
249            builder.symbol("sigil_memo_new", sigil_memo_new as *const u8);
250            builder.symbol("sigil_memo_get_1", sigil_memo_get_1 as *const u8);
251            builder.symbol("sigil_memo_set_1", sigil_memo_set_1 as *const u8);
252            builder.symbol("sigil_memo_get_2", sigil_memo_get_2 as *const u8);
253            builder.symbol("sigil_memo_set_2", sigil_memo_set_2 as *const u8);
254            builder.symbol("sigil_memo_free", sigil_memo_free as *const u8);
255
256            // Optimized recursive algorithm implementations
257            builder.symbol("sigil_ackermann", sigil_ackermann as *const u8);
258            builder.symbol("sigil_tak", sigil_tak as *const u8);
259
260            // FFI helper functions
261            use crate::ffi::helpers::*;
262            builder.symbol(
263                "sigil_string_to_cstring",
264                sigil_string_to_cstring as *const u8,
265            );
266            builder.symbol("sigil_cstring_free", sigil_cstring_free as *const u8);
267            builder.symbol("sigil_cstring_len", sigil_cstring_len as *const u8);
268            builder.symbol("sigil_cstring_copy", sigil_cstring_copy as *const u8);
269            builder.symbol("sigil_ptr_from_int", sigil_ptr_from_int as *const u8);
270            builder.symbol("sigil_ptr_to_int", sigil_ptr_to_int as *const u8);
271            builder.symbol("sigil_ptr_read_u8", sigil_ptr_read_u8 as *const u8);
272            builder.symbol("sigil_ptr_write_u8", sigil_ptr_write_u8 as *const u8);
273            builder.symbol("sigil_ptr_read_i32", sigil_ptr_read_i32 as *const u8);
274            builder.symbol("sigil_ptr_write_i32", sigil_ptr_write_i32 as *const u8);
275            builder.symbol("sigil_ptr_read_i64", sigil_ptr_read_i64 as *const u8);
276            builder.symbol("sigil_ptr_write_i64", sigil_ptr_write_i64 as *const u8);
277            builder.symbol("sigil_ptr_read_f64", sigil_ptr_read_f64 as *const u8);
278            builder.symbol("sigil_ptr_write_f64", sigil_ptr_write_f64 as *const u8);
279            builder.symbol("sigil_ptr_add", sigil_ptr_add as *const u8);
280            builder.symbol("sigil_ptr_is_null", sigil_ptr_is_null as *const u8);
281            builder.symbol("sigil_alloc", sigil_alloc as *const u8);
282            builder.symbol("sigil_free", sigil_free as *const u8);
283            builder.symbol("sigil_realloc", sigil_realloc as *const u8);
284            builder.symbol("sigil_memcpy", sigil_memcpy as *const u8);
285            builder.symbol("sigil_memset", sigil_memset as *const u8);
286
287            builtins.insert("sqrt".into(), sigil_sqrt as *const u8);
288            builtins.insert("sin".into(), sigil_sin as *const u8);
289            builtins.insert("cos".into(), sigil_cos as *const u8);
290            builtins.insert("pow".into(), sigil_pow as *const u8);
291            builtins.insert("exp".into(), sigil_exp as *const u8);
292            builtins.insert("ln".into(), sigil_ln as *const u8);
293            builtins.insert("floor".into(), sigil_floor as *const u8);
294            builtins.insert("ceil".into(), sigil_ceil as *const u8);
295            builtins.insert("abs".into(), sigil_abs as *const u8);
296            builtins.insert("print".into(), sigil_print as *const u8);
297            builtins.insert("now".into(), sigil_now as *const u8);
298
299            builtins
300        }
301
302        /// Compile a Sigil program (uses Aggressive optimization for best performance)
303        pub fn compile(&mut self, source: &str) -> Result<(), String> {
304            self.compile_with_opt(source, OptLevel::Aggressive)
305        }
306
307        /// Compile with a specific optimization level
308        pub fn compile_with_opt(
309            &mut self,
310            source: &str,
311            opt_level: OptLevel,
312        ) -> Result<(), String> {
313            let mut parser = Parser::new(source);
314            let source_file = parser.parse_file().map_err(|e| format!("{:?}", e))?;
315
316            // Run AST optimizations
317            let mut optimizer = Optimizer::new(opt_level);
318            let optimized = optimizer.optimize_file(&source_file);
319
320            // First pass: declare all extern blocks and functions
321            for spanned_item in &optimized.items {
322                match &spanned_item.node {
323                    Item::ExternBlock(extern_block) => {
324                        self.declare_extern_block(extern_block)?;
325                    }
326                    Item::Function(func) => {
327                        self.declare_function(func)?;
328                    }
329                    _ => {}
330                }
331            }
332
333            // Second pass: compile all functions
334            for spanned_item in &optimized.items {
335                if let Item::Function(func) = &spanned_item.node {
336                    self.compile_function(func)?;
337                }
338            }
339
340            // Finalize the module
341            self.module
342                .finalize_definitions()
343                .map_err(|e| e.to_string())?;
344
345            Ok(())
346        }
347
348        /// Declare a function (first pass)
349        fn declare_function(&mut self, func: &ast::Function) -> Result<FuncId, String> {
350            let name = &func.name.name;
351
352            // Build signature
353            let mut sig = self.module.make_signature();
354
355            // Add parameters (all as i64 for simplicity - we use tagged values)
356            for _param in &func.params {
357                sig.params.push(AbiParam::new(types::I64));
358            }
359
360            // Return type (i64)
361            sig.returns.push(AbiParam::new(types::I64));
362
363            let func_id = self
364                .module
365                .declare_function(name, Linkage::Local, &sig)
366                .map_err(|e| e.to_string())?;
367
368            self.functions.insert(name.clone(), func_id);
369            Ok(func_id)
370        }
371
372        /// Declare an extern block (FFI declarations)
373        fn declare_extern_block(&mut self, extern_block: &ExternBlock) -> Result<(), String> {
374            // Currently only "C" ABI is supported
375            if extern_block.abi != "C" && extern_block.abi != "c" {
376                return Err(format!(
377                    "Unsupported ABI: {}. Only \"C\" is supported.",
378                    extern_block.abi
379                ));
380            }
381
382            for item in &extern_block.items {
383                match item {
384                    ExternItem::Function(func) => {
385                        self.declare_extern_function(func)?;
386                    }
387                    ExternItem::Static(stat) => {
388                        // TODO: Implement extern statics
389                        eprintln!(
390                            "Warning: extern static '{}' not yet implemented",
391                            stat.name.name
392                        );
393                    }
394                }
395            }
396
397            Ok(())
398        }
399
400        /// Declare an extern "C" function
401        fn declare_extern_function(&mut self, func: &ExternFunction) -> Result<(), String> {
402            let name = &func.name.name;
403
404            // Build signature
405            let mut sig = self.module.make_signature();
406            let mut param_types = Vec::new();
407
408            // Add parameters with proper C types
409            for param in &func.params {
410                let ty = self.type_expr_to_cranelift(&param.ty)?;
411                sig.params.push(AbiParam::new(ty));
412                param_types.push(ty);
413            }
414
415            // Return type
416            let return_type = if let Some(ret_ty) = &func.return_type {
417                let ty = self.type_expr_to_cranelift(ret_ty)?;
418                sig.returns.push(AbiParam::new(ty));
419                Some(ty)
420            } else {
421                None
422            };
423
424            // Variadic functions use the "C" calling convention implicitly
425            // Cranelift doesn't have explicit variadic support, but we track it
426
427            let func_id = self
428                .module
429                .declare_function(name, Linkage::Import, &sig)
430                .map_err(|e| e.to_string())?;
431
432            self.extern_functions.insert(
433                name.clone(),
434                ExternFnSig {
435                    name: name.clone(),
436                    params: param_types,
437                    returns: return_type,
438                    variadic: func.variadic,
439                    func_id,
440                },
441            );
442
443            Ok(())
444        }
445
446        /// Convert a Sigil type expression to Cranelift type
447        fn type_expr_to_cranelift(&self, ty: &TypeExpr) -> Result<types::Type, String> {
448            match ty {
449                TypeExpr::Path(path) => {
450                    let name = path
451                        .segments
452                        .last()
453                        .map(|s| s.ident.name.as_str())
454                        .unwrap_or("");
455
456                    // Check if it's a C type
457                    if let Some(ctype) = CType::from_name(name) {
458                        return Ok(match ctype {
459                            CType::Void => types::I64, // void returns are handled separately
460                            CType::Char
461                            | CType::SChar
462                            | CType::UChar
463                            | CType::Int8
464                            | CType::UInt8 => types::I8,
465                            CType::Short | CType::UShort | CType::Int16 | CType::UInt16 => {
466                                types::I16
467                            }
468                            CType::Int | CType::UInt | CType::Int32 | CType::UInt32 => types::I32,
469                            CType::Long
470                            | CType::ULong
471                            | CType::LongLong
472                            | CType::ULongLong
473                            | CType::Size
474                            | CType::SSize
475                            | CType::PtrDiff
476                            | CType::Int64
477                            | CType::UInt64 => types::I64,
478                            CType::Float => types::F32,
479                            CType::Double => types::F64,
480                        });
481                    }
482
483                    // Check Sigil native types
484                    match name {
485                        "i8" => Ok(types::I8),
486                        "i16" => Ok(types::I16),
487                        "i32" | "int" => Ok(types::I32),
488                        "i64" => Ok(types::I64),
489                        "u8" => Ok(types::I8),
490                        "u16" => Ok(types::I16),
491                        "u32" => Ok(types::I32),
492                        "u64" => Ok(types::I64),
493                        "f32" => Ok(types::F32),
494                        "f64" | "float" => Ok(types::F64),
495                        "bool" => Ok(types::I8),
496                        "isize" | "usize" => Ok(types::I64),
497                        "()" => Ok(types::I64), // unit type
498                        _ => Ok(types::I64),    // Default to i64 for unknown types
499                    }
500                }
501                TypeExpr::Pointer { .. } | TypeExpr::Reference { .. } => {
502                    // Pointers are always 64-bit on our target
503                    Ok(types::I64)
504                }
505                _ => Ok(types::I64), // Default to i64
506            }
507        }
508
509        /// Compile a single function
510        fn compile_function(&mut self, func: &ast::Function) -> Result<(), String> {
511            let name = &func.name.name;
512            let func_id = *self.functions.get(name).ok_or("Function not declared")?;
513
514            // Build signature to match declaration
515            for _param in &func.params {
516                self.ctx
517                    .func
518                    .signature
519                    .params
520                    .push(AbiParam::new(types::I64));
521            }
522            self.ctx
523                .func
524                .signature
525                .returns
526                .push(AbiParam::new(types::I64));
527            self.ctx.func.name = UserFuncName::user(0, func_id.as_u32());
528
529            // Take ownership of what we need for building
530            let functions = self.functions.clone();
531            let extern_fns = self.extern_functions.clone();
532
533            {
534                let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_ctx);
535
536                let entry_block = builder.create_block();
537                builder.append_block_params_for_function_params(entry_block);
538                builder.switch_to_block(entry_block);
539                builder.seal_block(entry_block);
540
541                // Set up variable scope
542                let mut scope = CompileScope::new();
543
544                // Declare parameters as variables with type inference
545                for (i, param) in func.params.iter().enumerate() {
546                    let var = Variable::from_u32(scope.next_var() as u32);
547                    builder.declare_var(var, types::I64);
548                    let param_val = builder.block_params(entry_block)[i];
549                    builder.def_var(var, param_val);
550
551                    // Get parameter name from the pattern
552                    if let ast::Pattern::Ident { name, .. } = &param.pattern {
553                        // Infer parameter type from type annotation if present
554                        let param_type = match &param.ty {
555                            TypeExpr::Path(path) => {
556                                let type_name = path
557                                    .segments
558                                    .last()
559                                    .map(|s| s.ident.name.as_str())
560                                    .unwrap_or("");
561                                match type_name {
562                                    "f32" | "f64" | "float" => ValueType::Float,
563                                    "i8" | "i16" | "i32" | "i64" | "int" | "isize" | "u8"
564                                    | "u16" | "u32" | "u64" | "usize" | "bool" => ValueType::Int,
565                                    _ => ValueType::Int, // Default to int for unknown types
566                                }
567                            }
568                            TypeExpr::Infer => ValueType::Int, // Inferred type defaults to int
569                            _ => ValueType::Int,               // Default to int for other cases
570                        };
571                        scope.define_typed(&name.name, var, param_type);
572                    }
573                }
574
575                // Compile function body
576                if let Some(body) = &func.body {
577                    let (result, has_return) = compile_block_tracked(
578                        &mut self.module,
579                        &functions,
580                        &extern_fns,
581                        &mut builder,
582                        &mut scope,
583                        body,
584                    )?;
585                    // Only add return if the block didn't end with an explicit return
586                    if !has_return {
587                        builder.ins().return_(&[result]);
588                    }
589                } else {
590                    // No body - return 0
591                    let zero = builder.ins().iconst(types::I64, 0);
592                    builder.ins().return_(&[zero]);
593                }
594
595                builder.finalize();
596            }
597
598            // Debug: Uncomment to print generated IR
599            // eprintln!("Generated function '{}':\n{}", name, self.ctx.func.display());
600
601            // Compile to machine code
602            self.module
603                .define_function(func_id, &mut self.ctx)
604                .map_err(|e| format!("Compilation error for '{}': {}", name, e))?;
605
606            self.module.clear_context(&mut self.ctx);
607            Ok(())
608        }
609
610        /// Run the compiled main function
611        pub fn run(&mut self) -> Result<i64, String> {
612            let main_id = *self.functions.get("main").ok_or("No main function")?;
613            let main_ptr = self.module.get_finalized_function(main_id);
614
615            unsafe {
616                let main_fn: CompiledFn = mem::transmute(main_ptr);
617                Ok(main_fn())
618            }
619        }
620
621        /// Get a compiled function by name
622        pub fn get_function(&self, name: &str) -> Option<*const u8> {
623            self.functions
624                .get(name)
625                .map(|id| self.module.get_finalized_function(*id))
626        }
627    }
628
629    /// Tracked value type for type specialization
630    /// This enables direct CPU instruction emission when types are known
631    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
632    enum ValueType {
633        Int,     // Known to be integer
634        Float,   // Known to be float
635        Unknown, // Could be either (requires runtime dispatch)
636    }
637
638    /// Compilation scope for tracking variables
639    ///
640    /// Uses a shared counter (Rc<Cell>) to ensure all scopes use unique variable indices.
641    /// This prevents the "variable declared multiple times" error in Cranelift.
642    struct CompileScope {
643        variables: HashMap<String, Variable>,
644        /// Track the type of each variable for type specialization
645        var_types: HashMap<String, ValueType>,
646        /// Shared counter across all scopes to ensure unique Variable indices
647        var_counter: std::rc::Rc<std::cell::Cell<usize>>,
648    }
649
650    impl CompileScope {
651        fn new() -> Self {
652            Self {
653                variables: HashMap::new(),
654                var_types: HashMap::new(),
655                var_counter: std::rc::Rc::new(std::cell::Cell::new(0)),
656            }
657        }
658
659        fn child(&self) -> Self {
660            // Clone variables so child scopes can access parent variables
661            // Share the counter so all scopes use unique variable indices
662            Self {
663                variables: self.variables.clone(),
664                var_types: self.var_types.clone(),
665                var_counter: std::rc::Rc::clone(&self.var_counter),
666            }
667        }
668
669        fn next_var(&mut self) -> usize {
670            let v = self.var_counter.get();
671            self.var_counter.set(v + 1);
672            v
673        }
674
675        #[allow(dead_code)]
676        fn define(&mut self, name: &str, var: Variable) {
677            self.variables.insert(name.to_string(), var);
678        }
679
680        fn define_typed(&mut self, name: &str, var: Variable, ty: ValueType) {
681            self.variables.insert(name.to_string(), var);
682            self.var_types.insert(name.to_string(), ty);
683        }
684
685        fn lookup(&self, name: &str) -> Option<Variable> {
686            self.variables.get(name).copied()
687        }
688
689        fn get_type(&self, name: &str) -> ValueType {
690            self.var_types
691                .get(name)
692                .copied()
693                .unwrap_or(ValueType::Unknown)
694        }
695
696        #[allow(dead_code)]
697        fn set_type(&mut self, name: &str, ty: ValueType) {
698            self.var_types.insert(name.to_string(), ty);
699        }
700    }
701
702    // ============================================
703    // Optimization: Type Inference for Specialization
704    // ============================================
705
706    /// Infer the type of an expression for type specialization
707    /// Returns Int if the expression is known to produce an integer,
708    /// Float if known to produce a float, Unknown otherwise.
709    fn infer_type(expr: &Expr, scope: &CompileScope) -> ValueType {
710        match expr {
711            Expr::Literal(Literal::Int { .. }) => ValueType::Int,
712            Expr::Literal(Literal::Bool(_)) => ValueType::Int,
713            Expr::Literal(Literal::Float { .. }) => ValueType::Float,
714
715            Expr::Path(path) => {
716                let name = path
717                    .segments
718                    .last()
719                    .map(|s| s.ident.name.as_str())
720                    .unwrap_or("");
721                scope.get_type(name)
722            }
723
724            Expr::Binary { op, left, right } => {
725                let left_ty = infer_type(left, scope);
726                let right_ty = infer_type(right, scope);
727
728                // Comparison operators always return int (0 or 1)
729                if matches!(
730                    op,
731                    BinOp::Eq
732                        | BinOp::Ne
733                        | BinOp::Lt
734                        | BinOp::Le
735                        | BinOp::Gt
736                        | BinOp::Ge
737                        | BinOp::And
738                        | BinOp::Or
739                ) {
740                    return ValueType::Int;
741                }
742
743                // If either operand is float, result is float
744                if left_ty == ValueType::Float || right_ty == ValueType::Float {
745                    return ValueType::Float;
746                }
747
748                // If both are int, result is int
749                if left_ty == ValueType::Int && right_ty == ValueType::Int {
750                    return ValueType::Int;
751                }
752
753                // Otherwise unknown
754                ValueType::Unknown
755            }
756
757            Expr::Unary { op, expr } => {
758                match op {
759                    UnaryOp::Not => ValueType::Int, // ! always returns 0 or 1
760                    UnaryOp::Neg => infer_type(expr, scope),
761                    _ => infer_type(expr, scope),
762                }
763            }
764
765            Expr::Call { func, args } => {
766                // Check if it's a known function
767                if let Expr::Path(path) = func.as_ref() {
768                    let name = path
769                        .segments
770                        .last()
771                        .map(|s| s.ident.name.as_str())
772                        .unwrap_or("");
773                    match name {
774                        // Math functions return floats
775                        "sqrt" | "sin" | "cos" | "pow" | "exp" | "ln" | "floor" | "ceil"
776                        | "abs" => ValueType::Float,
777                        // Time returns int
778                        "now" => ValueType::Int,
779                        // Array operations return int
780                        "len" | "sigil_array_len" => ValueType::Int,
781                        // Print returns int
782                        "print" | "sigil_print" => ValueType::Int,
783                        _ => {
784                            // OPTIMIZATION: For user-defined functions, if all arguments are Int,
785                            // assume the return type is Int (common case for recursive functions)
786                            // This enables type specialization for fib(n-1) + fib(n-2)
787                            let all_args_int = args
788                                .iter()
789                                .all(|arg| infer_type(arg, scope) == ValueType::Int);
790                            if all_args_int {
791                                ValueType::Int
792                            } else {
793                                ValueType::Unknown
794                            }
795                        }
796                    }
797                } else {
798                    ValueType::Unknown
799                }
800            }
801
802            Expr::If {
803                then_branch,
804                else_branch,
805                ..
806            } => {
807                // Type of if is the type of its branches
808                let then_ty = if let Some(expr) = &then_branch.expr {
809                    infer_type(expr, scope)
810                } else {
811                    ValueType::Int // Empty block returns 0
812                };
813
814                if let Some(else_expr) = else_branch {
815                    let else_ty = infer_type(else_expr, scope);
816                    if then_ty == else_ty {
817                        then_ty
818                    } else {
819                        ValueType::Unknown
820                    }
821                } else {
822                    then_ty
823                }
824            }
825
826            _ => ValueType::Unknown,
827        }
828    }
829
830    // ============================================
831    // Optimization: Constant Folding
832    // ============================================
833
834    /// Try to evaluate a constant expression at compile time
835    fn try_const_fold(expr: &Expr) -> Option<i64> {
836        match expr {
837            Expr::Literal(Literal::Int { value, .. }) => value.parse().ok(),
838            Expr::Literal(Literal::Bool(b)) => Some(if *b { 1 } else { 0 }),
839            Expr::Binary { op, left, right } => {
840                let l = try_const_fold(left)?;
841                let r = try_const_fold(right)?;
842                match op {
843                    BinOp::Add => Some(l.wrapping_add(r)),
844                    BinOp::Sub => Some(l.wrapping_sub(r)),
845                    BinOp::Mul => Some(l.wrapping_mul(r)),
846                    BinOp::Div if r != 0 => Some(l / r),
847                    BinOp::Rem if r != 0 => Some(l % r),
848                    BinOp::BitAnd => Some(l & r),
849                    BinOp::BitOr => Some(l | r),
850                    BinOp::BitXor => Some(l ^ r),
851                    BinOp::Shl => Some(l << (r & 63)),
852                    BinOp::Shr => Some(l >> (r & 63)),
853                    BinOp::Eq => Some(if l == r { 1 } else { 0 }),
854                    BinOp::Ne => Some(if l != r { 1 } else { 0 }),
855                    BinOp::Lt => Some(if l < r { 1 } else { 0 }),
856                    BinOp::Le => Some(if l <= r { 1 } else { 0 }),
857                    BinOp::Gt => Some(if l > r { 1 } else { 0 }),
858                    BinOp::Ge => Some(if l >= r { 1 } else { 0 }),
859                    BinOp::And => Some(if l != 0 && r != 0 { 1 } else { 0 }),
860                    BinOp::Or => Some(if l != 0 || r != 0 { 1 } else { 0 }),
861                    _ => None,
862                }
863            }
864            Expr::Unary { op, expr } => {
865                let v = try_const_fold(expr)?;
866                match op {
867                    UnaryOp::Neg => Some(-v),
868                    UnaryOp::Not => Some(if v == 0 { 1 } else { 0 }),
869                    _ => None,
870                }
871            }
872            _ => None,
873        }
874    }
875
876    // ============================================
877    // Optimization: Direct Condition Compilation
878    // ============================================
879
880    /// Compile a condition directly to a boolean i8 value for branching.
881    /// This avoids the redundant pattern of: compare -> extend to i64 -> compare to 0
882    fn compile_condition(
883        module: &mut JITModule,
884        functions: &HashMap<String, FuncId>,
885        extern_fns: &HashMap<String, ExternFnSig>,
886        builder: &mut FunctionBuilder,
887        scope: &mut CompileScope,
888        condition: &Expr,
889    ) -> Result<cranelift_codegen::ir::Value, String> {
890        // Handle comparison operators directly - emit icmp without extending
891        if let Expr::Binary { op, left, right } = condition {
892            let cc = match op {
893                BinOp::Eq => Some(IntCC::Equal),
894                BinOp::Ne => Some(IntCC::NotEqual),
895                BinOp::Lt => Some(IntCC::SignedLessThan),
896                BinOp::Le => Some(IntCC::SignedLessThanOrEqual),
897                BinOp::Gt => Some(IntCC::SignedGreaterThan),
898                BinOp::Ge => Some(IntCC::SignedGreaterThanOrEqual),
899                _ => None,
900            };
901
902            if let Some(cc) = cc {
903                let lhs = compile_expr(module, functions, extern_fns, builder, scope, left)?;
904                let rhs = compile_expr(module, functions, extern_fns, builder, scope, right)?;
905                // Return i8 directly - no extension needed
906                return Ok(builder.ins().icmp(cc, lhs, rhs));
907            }
908
909            // Handle && and || with short-circuit evaluation
910            if matches!(op, BinOp::And | BinOp::Or) {
911                // For now, fall through to regular compilation
912                // Short-circuit optimization can be added later
913            }
914        }
915
916        // Handle !expr - flip the comparison
917        if let Expr::Unary {
918            op: UnaryOp::Not,
919            expr,
920        } = condition
921        {
922            let inner = compile_condition(module, functions, extern_fns, builder, scope, expr)?;
923            // Flip the boolean
924            let true_val = builder.ins().iconst(types::I8, 1);
925            return Ok(builder.ins().bxor(inner, true_val));
926        }
927
928        // Handle boolean literals directly
929        if let Expr::Literal(Literal::Bool(b)) = condition {
930            return Ok(builder.ins().iconst(types::I8, if *b { 1 } else { 0 }));
931        }
932
933        // For other expressions, compile normally and compare to 0
934        let val = compile_expr(module, functions, extern_fns, builder, scope, condition)?;
935        let zero = builder.ins().iconst(types::I64, 0);
936        Ok(builder.ins().icmp(IntCC::NotEqual, val, zero))
937    }
938
939    // ============================================
940    // Optimization: Tail Call Detection
941    // ============================================
942
943    /// Check if a return expression is a tail call to the specified function
944    #[allow(dead_code)]
945    fn is_tail_call_to<'a>(expr: &'a Expr, func_name: &str) -> Option<&'a Vec<Expr>> {
946        if let Expr::Return(Some(inner)) = expr {
947            if let Expr::Call { func, args } = inner.as_ref() {
948                if let Expr::Path(path) = func.as_ref() {
949                    let name = path
950                        .segments
951                        .last()
952                        .map(|s| s.ident.name.as_str())
953                        .unwrap_or("");
954                    if name == func_name {
955                        return Some(args);
956                    }
957                }
958            }
959        }
960        None
961    }
962
963    // ============================================
964    // Free functions for compilation (avoid borrow issues)
965    // ============================================
966
967    /// Compile a block, returns (value, has_return)
968    fn compile_block_tracked(
969        module: &mut JITModule,
970        functions: &HashMap<String, FuncId>,
971        extern_fns: &HashMap<String, ExternFnSig>,
972        builder: &mut FunctionBuilder,
973        scope: &mut CompileScope,
974        block: &ast::Block,
975    ) -> Result<(cranelift_codegen::ir::Value, bool), String> {
976        // OPTIMIZATION: Don't create zero constant unless needed
977        let mut last_val: Option<cranelift_codegen::ir::Value> = None;
978        let mut has_return = false;
979
980        for stmt in &block.stmts {
981            let (val, ret) =
982                compile_stmt_tracked(module, functions, extern_fns, builder, scope, stmt)?;
983            last_val = Some(val);
984            if ret {
985                has_return = true;
986            }
987        }
988
989        if let Some(expr) = &block.expr {
990            let (val, ret) =
991                compile_expr_tracked(module, functions, extern_fns, builder, scope, expr)?;
992            last_val = Some(val);
993            if ret {
994                has_return = true;
995            }
996        }
997
998        // Only create zero if we have no value
999        let result = last_val.unwrap_or_else(|| builder.ins().iconst(types::I64, 0));
1000        Ok((result, has_return))
1001    }
1002
1003    /// Compile a block (convenience wrapper)
1004    fn compile_block(
1005        module: &mut JITModule,
1006        functions: &HashMap<String, FuncId>,
1007        extern_fns: &HashMap<String, ExternFnSig>,
1008        builder: &mut FunctionBuilder,
1009        scope: &mut CompileScope,
1010        block: &ast::Block,
1011    ) -> Result<cranelift_codegen::ir::Value, String> {
1012        compile_block_tracked(module, functions, extern_fns, builder, scope, block).map(|(v, _)| v)
1013    }
1014
1015    /// Compile a statement, returning (value, has_return)
1016    fn compile_stmt_tracked(
1017        module: &mut JITModule,
1018        functions: &HashMap<String, FuncId>,
1019        extern_fns: &HashMap<String, ExternFnSig>,
1020        builder: &mut FunctionBuilder,
1021        scope: &mut CompileScope,
1022        stmt: &ast::Stmt,
1023    ) -> Result<(cranelift_codegen::ir::Value, bool), String> {
1024        match stmt {
1025            ast::Stmt::Let { pattern, init, .. } => {
1026                // Infer type of initializer for type specialization
1027                let ty = if let Some(expr) = init {
1028                    infer_type(expr, scope)
1029                } else {
1030                    ValueType::Int // Default to int for uninitialized
1031                };
1032
1033                let val = if let Some(expr) = init {
1034                    compile_expr(module, functions, extern_fns, builder, scope, expr)?
1035                } else {
1036                    builder.ins().iconst(types::I64, 0)
1037                };
1038
1039                if let ast::Pattern::Ident { name, .. } = pattern {
1040                    let var = Variable::from_u32(scope.next_var() as u32);
1041                    builder.declare_var(var, types::I64);
1042                    builder.def_var(var, val);
1043                    // Track the type for later type specialization
1044                    scope.define_typed(&name.name, var, ty);
1045                }
1046
1047                Ok((val, false))
1048            }
1049            ast::Stmt::LetElse {
1050                pattern,
1051                init,
1052                else_branch,
1053                ..
1054            } => {
1055                // For let-else, we evaluate the init and bind the pattern
1056                // The else branch diverges (must return/break/panic)
1057                let val = compile_expr(module, functions, extern_fns, builder, scope, init)?;
1058                let ty = infer_type(init, scope);
1059
1060                if let ast::Pattern::Ident { name, .. } = pattern {
1061                    let var = Variable::from_u32(scope.next_var() as u32);
1062                    builder.declare_var(var, types::I64);
1063                    builder.def_var(var, val);
1064                    scope.define_typed(&name.name, var, ty);
1065                }
1066
1067                // Note: In a full implementation, we'd need to check if the pattern
1068                // matches and branch to else_branch if not. For now, we just
1069                // compile the else_branch to ensure it's valid but don't use it.
1070                let _ = else_branch;
1071
1072                Ok((val, false))
1073            }
1074            ast::Stmt::Expr(expr) | ast::Stmt::Semi(expr) => {
1075                compile_expr_tracked(module, functions, extern_fns, builder, scope, expr)
1076            }
1077            ast::Stmt::Item(_) => Ok((builder.ins().iconst(types::I64, 0), false)),
1078        }
1079    }
1080
1081    /// Compile a statement (convenience wrapper)
1082    #[allow(dead_code)]
1083    fn compile_stmt(
1084        module: &mut JITModule,
1085        functions: &HashMap<String, FuncId>,
1086        extern_fns: &HashMap<String, ExternFnSig>,
1087        builder: &mut FunctionBuilder,
1088        scope: &mut CompileScope,
1089        stmt: &ast::Stmt,
1090    ) -> Result<cranelift_codegen::ir::Value, String> {
1091        compile_stmt_tracked(module, functions, extern_fns, builder, scope, stmt).map(|(v, _)| v)
1092    }
1093
1094    /// Compile an expression, returning (value, has_return)
1095    fn compile_expr_tracked(
1096        module: &mut JITModule,
1097        functions: &HashMap<String, FuncId>,
1098        extern_fns: &HashMap<String, ExternFnSig>,
1099        builder: &mut FunctionBuilder,
1100        scope: &mut CompileScope,
1101        expr: &Expr,
1102    ) -> Result<(cranelift_codegen::ir::Value, bool), String> {
1103        match expr {
1104            Expr::Return(value) => {
1105                // NOTE: Cranelift's return_call requires frame pointers which aren't enabled
1106                // by default. Tail call optimization is handled at the AST level instead
1107                // (see optimizer's accumulator transform for fib-like patterns).
1108                //
1109                // When Cranelift adds better tail call support, enable this:
1110                // if let Some(v) = value {
1111                //     if let Expr::Call { func: call_func, args: call_args } = v.as_ref() {
1112                //         // ... use return_call instruction
1113                //     }
1114                // }
1115
1116                let ret_val = if let Some(v) = value {
1117                    compile_expr(module, functions, extern_fns, builder, scope, v)?
1118                } else {
1119                    builder.ins().iconst(types::I64, 0)
1120                };
1121                builder.ins().return_(&[ret_val]);
1122                Ok((ret_val, true)) // Signal that we have a return
1123            }
1124            Expr::If {
1125                condition,
1126                then_branch,
1127                else_branch,
1128            } => {
1129                // If expressions can contain returns, so use tracked version
1130                compile_if_tracked(
1131                    module,
1132                    functions,
1133                    extern_fns,
1134                    builder,
1135                    scope,
1136                    condition,
1137                    then_branch,
1138                    else_branch.as_deref(),
1139                )
1140            }
1141            Expr::Block(block) => {
1142                let mut inner_scope = scope.child();
1143                compile_block_tracked(
1144                    module,
1145                    functions,
1146                    extern_fns,
1147                    builder,
1148                    &mut inner_scope,
1149                    block,
1150                )
1151            }
1152            _ => {
1153                // All other expressions don't have return
1154                let val = compile_expr(module, functions, extern_fns, builder, scope, expr)?;
1155                Ok((val, false))
1156            }
1157        }
1158    }
1159
1160    /// Compile an expression
1161    fn compile_expr(
1162        module: &mut JITModule,
1163        functions: &HashMap<String, FuncId>,
1164        extern_fns: &HashMap<String, ExternFnSig>,
1165        builder: &mut FunctionBuilder,
1166        scope: &mut CompileScope,
1167        expr: &Expr,
1168    ) -> Result<cranelift_codegen::ir::Value, String> {
1169        // OPTIMIZATION: Try constant folding first
1170        if let Some(val) = try_const_fold(expr) {
1171            return Ok(builder.ins().iconst(types::I64, val));
1172        }
1173
1174        match expr {
1175            Expr::Literal(lit) => compile_literal(builder, lit),
1176
1177            Expr::Path(path) => {
1178                let name = path
1179                    .segments
1180                    .last()
1181                    .map(|s| s.ident.name.clone())
1182                    .unwrap_or_default();
1183                if let Some(var) = scope.lookup(&name) {
1184                    Ok(builder.use_var(var))
1185                } else {
1186                    Err(format!("Undefined variable: {}", name))
1187                }
1188            }
1189
1190            Expr::Binary { op, left, right } => {
1191                // TYPE SPECIALIZATION: Infer types to avoid runtime dispatch
1192                let left_ty = infer_type(left, scope);
1193                let right_ty = infer_type(right, scope);
1194
1195                let lhs = compile_expr(module, functions, extern_fns, builder, scope, left)?;
1196                let rhs = compile_expr(module, functions, extern_fns, builder, scope, right)?;
1197
1198                // OPTIMIZATION: Use direct CPU instructions when both types are known integers
1199                // This eliminates the ~100 cycle function call overhead per operation
1200                if left_ty == ValueType::Int && right_ty == ValueType::Int {
1201                    // Direct integer instructions - no runtime dispatch!
1202                    return compile_binary_op(builder, op.clone(), lhs, rhs);
1203                }
1204
1205                // OPTIMIZATION: Direct float instructions when both are floats
1206                if left_ty == ValueType::Float && right_ty == ValueType::Float {
1207                    return compile_float_binary_op(builder, op, lhs, rhs);
1208                }
1209
1210                // Mixed or unknown types - fall back to runtime dispatch
1211                // This is slower but handles dynamic typing correctly
1212                match op {
1213                    BinOp::Add => compile_call(
1214                        module,
1215                        functions,
1216                        extern_fns,
1217                        builder,
1218                        "sigil_add",
1219                        &[lhs, rhs],
1220                    ),
1221                    BinOp::Sub => compile_call(
1222                        module,
1223                        functions,
1224                        extern_fns,
1225                        builder,
1226                        "sigil_sub",
1227                        &[lhs, rhs],
1228                    ),
1229                    BinOp::Mul => compile_call(
1230                        module,
1231                        functions,
1232                        extern_fns,
1233                        builder,
1234                        "sigil_mul",
1235                        &[lhs, rhs],
1236                    ),
1237                    BinOp::Div => compile_call(
1238                        module,
1239                        functions,
1240                        extern_fns,
1241                        builder,
1242                        "sigil_div",
1243                        &[lhs, rhs],
1244                    ),
1245                    BinOp::Lt => compile_call(
1246                        module,
1247                        functions,
1248                        extern_fns,
1249                        builder,
1250                        "sigil_lt",
1251                        &[lhs, rhs],
1252                    ),
1253                    BinOp::Le => compile_call(
1254                        module,
1255                        functions,
1256                        extern_fns,
1257                        builder,
1258                        "sigil_le",
1259                        &[lhs, rhs],
1260                    ),
1261                    BinOp::Gt => compile_call(
1262                        module,
1263                        functions,
1264                        extern_fns,
1265                        builder,
1266                        "sigil_gt",
1267                        &[lhs, rhs],
1268                    ),
1269                    BinOp::Ge => compile_call(
1270                        module,
1271                        functions,
1272                        extern_fns,
1273                        builder,
1274                        "sigil_ge",
1275                        &[lhs, rhs],
1276                    ),
1277                    _ => compile_binary_op(builder, op.clone(), lhs, rhs),
1278                }
1279            }
1280
1281            Expr::Unary { op, expr: inner } => {
1282                let val = compile_expr(module, functions, extern_fns, builder, scope, inner)?;
1283                compile_unary_op(builder, *op, val)
1284            }
1285
1286            Expr::Call { func, args } => {
1287                let func_name = match func.as_ref() {
1288                    Expr::Path(path) => path
1289                        .segments
1290                        .last()
1291                        .map(|s| s.ident.name.clone())
1292                        .unwrap_or_default(),
1293                    _ => return Err("Only direct function calls supported".into()),
1294                };
1295
1296                let mut arg_vals = Vec::new();
1297                for arg in args {
1298                    arg_vals.push(compile_expr(
1299                        module, functions, extern_fns, builder, scope, arg,
1300                    )?);
1301                }
1302
1303                compile_call(
1304                    module, functions, extern_fns, builder, &func_name, &arg_vals,
1305                )
1306            }
1307
1308            Expr::If {
1309                condition,
1310                then_branch,
1311                else_branch,
1312            } => compile_if(
1313                module,
1314                functions,
1315                extern_fns,
1316                builder,
1317                scope,
1318                condition,
1319                then_branch,
1320                else_branch.as_deref(),
1321            ),
1322
1323            Expr::While {
1324                condition, body, ..
1325            } => compile_while(
1326                module, functions, extern_fns, builder, scope, condition, body,
1327            ),
1328
1329            Expr::Block(block) => {
1330                let mut inner_scope = scope.child();
1331                compile_block(
1332                    module,
1333                    functions,
1334                    extern_fns,
1335                    builder,
1336                    &mut inner_scope,
1337                    block,
1338                )
1339            }
1340
1341            Expr::Return(value) => {
1342                // NOTE: Tail call optimization via Cranelift's return_call requires frame
1343                // pointers. Tail recursion is handled at the AST level instead.
1344                let ret_val = if let Some(v) = value {
1345                    compile_expr(module, functions, extern_fns, builder, scope, v)?
1346                } else {
1347                    builder.ins().iconst(types::I64, 0)
1348                };
1349                builder.ins().return_(&[ret_val]);
1350                Ok(ret_val)
1351            }
1352
1353            Expr::Assign { target, value } => {
1354                let val = compile_expr(module, functions, extern_fns, builder, scope, value)?;
1355                match target.as_ref() {
1356                    Expr::Path(path) => {
1357                        let name = path
1358                            .segments
1359                            .last()
1360                            .map(|s| s.ident.name.clone())
1361                            .unwrap_or_default();
1362                        if let Some(var) = scope.lookup(&name) {
1363                            builder.def_var(var, val);
1364                            Ok(val)
1365                        } else {
1366                            Err(format!("Undefined variable: {}", name))
1367                        }
1368                    }
1369                    Expr::Index { expr: arr, index } => {
1370                        let arr_val =
1371                            compile_expr(module, functions, extern_fns, builder, scope, arr)?;
1372                        let idx_val =
1373                            compile_expr(module, functions, extern_fns, builder, scope, index)?;
1374                        compile_call(
1375                            module,
1376                            functions,
1377                            extern_fns,
1378                            builder,
1379                            "sigil_array_set",
1380                            &[arr_val, idx_val, val],
1381                        )
1382                    }
1383                    _ => Err("Invalid assignment target".into()),
1384                }
1385            }
1386
1387            Expr::Index { expr: arr, index } => {
1388                let arr_val = compile_expr(module, functions, extern_fns, builder, scope, arr)?;
1389                let idx_val = compile_expr(module, functions, extern_fns, builder, scope, index)?;
1390                compile_call(
1391                    module,
1392                    functions,
1393                    extern_fns,
1394                    builder,
1395                    "sigil_array_get",
1396                    &[arr_val, idx_val],
1397                )
1398            }
1399
1400            Expr::Array(elements) => {
1401                let len = builder.ins().iconst(types::I64, elements.len() as i64);
1402                let arr = compile_call(
1403                    module,
1404                    functions,
1405                    extern_fns,
1406                    builder,
1407                    "sigil_array_new",
1408                    &[len],
1409                )?;
1410
1411                for (i, elem) in elements.iter().enumerate() {
1412                    let val = compile_expr(module, functions, extern_fns, builder, scope, elem)?;
1413                    let idx = builder.ins().iconst(types::I64, i as i64);
1414                    compile_call(
1415                        module,
1416                        functions,
1417                        extern_fns,
1418                        builder,
1419                        "sigil_array_set",
1420                        &[arr, idx, val],
1421                    )?;
1422                }
1423
1424                Ok(arr)
1425            }
1426
1427            Expr::Pipe { expr, operations } => {
1428                // Compile the base expression first
1429                let mut result = compile_expr(module, functions, extern_fns, builder, scope, expr)?;
1430
1431                // Process each pipe operation in sequence
1432                for op in operations {
1433                    result = match op {
1434                        // Simple array access morphemes - call stdlib functions directly
1435                        PipeOp::First => compile_call(
1436                            module,
1437                            functions,
1438                            extern_fns,
1439                            builder,
1440                            "sigil_array_first",
1441                            &[result],
1442                        )?,
1443                        PipeOp::Last => compile_call(
1444                            module,
1445                            functions,
1446                            extern_fns,
1447                            builder,
1448                            "sigil_array_last",
1449                            &[result],
1450                        )?,
1451                        PipeOp::Middle => compile_call(
1452                            module,
1453                            functions,
1454                            extern_fns,
1455                            builder,
1456                            "sigil_array_middle",
1457                            &[result],
1458                        )?,
1459                        PipeOp::Choice => compile_call(
1460                            module,
1461                            functions,
1462                            extern_fns,
1463                            builder,
1464                            "sigil_array_choice",
1465                            &[result],
1466                        )?,
1467                        PipeOp::Next => compile_call(
1468                            module,
1469                            functions,
1470                            extern_fns,
1471                            builder,
1472                            "sigil_array_next",
1473                            &[result],
1474                        )?,
1475                        PipeOp::Nth(index_expr) => {
1476                            let index = compile_expr(
1477                                module, functions, extern_fns, builder, scope, index_expr,
1478                            )?;
1479                            compile_call(
1480                                module,
1481                                functions,
1482                                extern_fns,
1483                                builder,
1484                                "sigil_array_nth",
1485                                &[result, index],
1486                            )?
1487                        }
1488                        // General reduce with closure (ρ morpheme)
1489                        PipeOp::Reduce(_) => {
1490                            // For now, treat reduce as sum for numeric arrays
1491                            compile_call(
1492                                module,
1493                                functions,
1494                                extern_fns,
1495                                builder,
1496                                "sigil_array_sum",
1497                                &[result],
1498                            )?
1499                        }
1500                        // Sum reduction (ρ+ morpheme)
1501                        PipeOp::ReduceSum => compile_call(
1502                            module,
1503                            functions,
1504                            extern_fns,
1505                            builder,
1506                            "sigil_array_sum",
1507                            &[result],
1508                        )?,
1509                        // Product reduction (ρ* morpheme)
1510                        PipeOp::ReduceProd => compile_call(
1511                            module,
1512                            functions,
1513                            extern_fns,
1514                            builder,
1515                            "sigil_array_product",
1516                            &[result],
1517                        )?,
1518                        // Min reduction (ρ_min morpheme)
1519                        PipeOp::ReduceMin => compile_call(
1520                            module,
1521                            functions,
1522                            extern_fns,
1523                            builder,
1524                            "sigil_array_min",
1525                            &[result],
1526                        )?,
1527                        // Max reduction (ρ_max morpheme)
1528                        PipeOp::ReduceMax => compile_call(
1529                            module,
1530                            functions,
1531                            extern_fns,
1532                            builder,
1533                            "sigil_array_max",
1534                            &[result],
1535                        )?,
1536                        // Concat reduction (ρ++ morpheme)
1537                        PipeOp::ReduceConcat => compile_call(
1538                            module,
1539                            functions,
1540                            extern_fns,
1541                            builder,
1542                            "sigil_array_concat",
1543                            &[result],
1544                        )?,
1545                        // All reduction (ρ& morpheme)
1546                        PipeOp::ReduceAll => compile_call(
1547                            module,
1548                            functions,
1549                            extern_fns,
1550                            builder,
1551                            "sigil_array_all",
1552                            &[result],
1553                        )?,
1554                        // Any reduction (ρ| morpheme)
1555                        PipeOp::ReduceAny => compile_call(
1556                            module,
1557                            functions,
1558                            extern_fns,
1559                            builder,
1560                            "sigil_array_any",
1561                            &[result],
1562                        )?,
1563                        // Sort operation (σ morpheme) - returns sorted array pointer
1564                        PipeOp::Sort(_) => compile_call(
1565                            module,
1566                            functions,
1567                            extern_fns,
1568                            builder,
1569                            "sigil_array_sort",
1570                            &[result],
1571                        )?,
1572                        // Transform and Filter require closure compilation - complex
1573                        PipeOp::Transform(_) | PipeOp::Filter(_) => {
1574                            // TODO: Implement closure compilation for transform/filter
1575                            // For now, pass through the array unchanged
1576                            result
1577                        }
1578                        // Method calls, await, and named morphemes
1579                        PipeOp::Method {
1580                            name,
1581                            type_args: _,
1582                            args,
1583                        } => {
1584                            // Compile as a method call on the result
1585                            let mut call_args = vec![result];
1586                            for arg in args {
1587                                call_args.push(compile_expr(
1588                                    module, functions, extern_fns, builder, scope, arg,
1589                                )?);
1590                            }
1591                            compile_call(
1592                                module, functions, extern_fns, builder, &name.name, &call_args,
1593                            )?
1594                        }
1595                        PipeOp::Await => {
1596                            // Await is a no-op in JIT context (sync execution)
1597                            result
1598                        }
1599                        PipeOp::Match(_) => {
1600                            // Match in pipes not supported in JIT - use interpreter
1601                            // (proper implementation would emit branching code)
1602                            result
1603                        }
1604                        PipeOp::TryMap(_) => {
1605                            // Try/error transformation not supported in JIT
1606                            result
1607                        }
1608                        PipeOp::Call(callee) => {
1609                            // Call an arbitrary expression (like self.layer)
1610                            // Compile the callee expression, then call it with result as argument
1611                            let callee_val = compile_expr(
1612                                module, functions, extern_fns, builder, scope, callee,
1613                            )?;
1614                            // For now, treat as function call with result as first arg
1615                            compile_call(
1616                                module,
1617                                functions,
1618                                extern_fns,
1619                                builder,
1620                                "sigil_call",
1621                                &[callee_val, result],
1622                            )?
1623                        }
1624                        PipeOp::Named { prefix, body } => {
1625                            // Named morphemes like ·map{f} - try to call as function
1626                            if !prefix.is_empty() {
1627                                let fn_name = &prefix[0].name;
1628                                if let Some(body_expr) = body {
1629                                    let body_val = compile_expr(
1630                                        module, functions, extern_fns, builder, scope, body_expr,
1631                                    )?;
1632                                    compile_call(
1633                                        module,
1634                                        functions,
1635                                        extern_fns,
1636                                        builder,
1637                                        fn_name,
1638                                        &[result, body_val],
1639                                    )?
1640                                } else {
1641                                    compile_call(
1642                                        module,
1643                                        functions,
1644                                        extern_fns,
1645                                        builder,
1646                                        fn_name,
1647                                        &[result],
1648                                    )?
1649                                }
1650                            } else {
1651                                result
1652                            }
1653                        }
1654                        // Parallel morpheme: ∥ - execute inner operation in parallel
1655                        PipeOp::Parallel(inner_op) => {
1656                            // For JIT compilation, parallel execution is handled by calling
1657                            // sigil_parallel_* variants of operations that use thread pools
1658                            match inner_op.as_ref() {
1659                                PipeOp::Transform(_) => {
1660                                    // Call parallel transform (falls back to sequential for now)
1661                                    compile_call(
1662                                        module,
1663                                        functions,
1664                                        extern_fns,
1665                                        builder,
1666                                        "sigil_parallel_map",
1667                                        &[result],
1668                                    )?
1669                                }
1670                                PipeOp::Filter(_) => {
1671                                    // Call parallel filter
1672                                    compile_call(
1673                                        module,
1674                                        functions,
1675                                        extern_fns,
1676                                        builder,
1677                                        "sigil_parallel_filter",
1678                                        &[result],
1679                                    )?
1680                                }
1681                                PipeOp::Reduce(_) => {
1682                                    // Parallel reduce (tree reduction)
1683                                    compile_call(
1684                                        module,
1685                                        functions,
1686                                        extern_fns,
1687                                        builder,
1688                                        "sigil_parallel_reduce",
1689                                        &[result],
1690                                    )?
1691                                }
1692                                // For other ops, recursively process but mark as parallel hint
1693                                _ => result,
1694                            }
1695                        }
1696                        // GPU compute morpheme: ⊛ - execute on GPU
1697                        PipeOp::Gpu(inner_op) => {
1698                            // GPU execution requires shader compilation
1699                            // For JIT, we call GPU-specific variants that dispatch to compute shaders
1700                            match inner_op.as_ref() {
1701                                PipeOp::Transform(_) => {
1702                                    // GPU transform - dispatches as compute shader
1703                                    compile_call(
1704                                        module,
1705                                        functions,
1706                                        extern_fns,
1707                                        builder,
1708                                        "sigil_gpu_map",
1709                                        &[result],
1710                                    )?
1711                                }
1712                                PipeOp::Filter(_) => {
1713                                    // GPU filter with stream compaction
1714                                    compile_call(
1715                                        module,
1716                                        functions,
1717                                        extern_fns,
1718                                        builder,
1719                                        "sigil_gpu_filter",
1720                                        &[result],
1721                                    )?
1722                                }
1723                                PipeOp::Reduce(_) => {
1724                                    // GPU parallel reduction
1725                                    compile_call(
1726                                        module,
1727                                        functions,
1728                                        extern_fns,
1729                                        builder,
1730                                        "sigil_gpu_reduce",
1731                                        &[result],
1732                                    )?
1733                                }
1734                                _ => result,
1735                            }
1736                        }
1737
1738                        // ==========================================
1739                        // Protocol Operations - Sigil-native networking
1740                        // In JIT context, these call runtime protocol functions
1741                        // ==========================================
1742
1743                        // Send: |send{data} - send data over connection
1744                        PipeOp::Send(data_expr) => {
1745                            let data = compile_expr(
1746                                module, functions, extern_fns, builder, scope, data_expr,
1747                            )?;
1748                            compile_call(
1749                                module,
1750                                functions,
1751                                extern_fns,
1752                                builder,
1753                                "sigil_protocol_send",
1754                                &[result, data],
1755                            )?
1756                        }
1757
1758                        // Recv: |recv - receive data from connection
1759                        PipeOp::Recv => compile_call(
1760                            module,
1761                            functions,
1762                            extern_fns,
1763                            builder,
1764                            "sigil_protocol_recv",
1765                            &[result],
1766                        )?,
1767
1768                        // Stream: |stream{handler} - create streaming iterator
1769                        PipeOp::Stream(handler_expr) => {
1770                            let handler = compile_expr(
1771                                module,
1772                                functions,
1773                                extern_fns,
1774                                builder,
1775                                scope,
1776                                handler_expr,
1777                            )?;
1778                            compile_call(
1779                                module,
1780                                functions,
1781                                extern_fns,
1782                                builder,
1783                                "sigil_protocol_stream",
1784                                &[result, handler],
1785                            )?
1786                        }
1787
1788                        // Connect: |connect{config} - establish connection
1789                        PipeOp::Connect(config_expr) => {
1790                            if let Some(config) = config_expr {
1791                                let config_val = compile_expr(
1792                                    module, functions, extern_fns, builder, scope, config,
1793                                )?;
1794                                compile_call(
1795                                    module,
1796                                    functions,
1797                                    extern_fns,
1798                                    builder,
1799                                    "sigil_protocol_connect",
1800                                    &[result, config_val],
1801                                )?
1802                            } else {
1803                                compile_call(
1804                                    module,
1805                                    functions,
1806                                    extern_fns,
1807                                    builder,
1808                                    "sigil_protocol_connect_default",
1809                                    &[result],
1810                                )?
1811                            }
1812                        }
1813
1814                        // Close: |close - close connection
1815                        PipeOp::Close => compile_call(
1816                            module,
1817                            functions,
1818                            extern_fns,
1819                            builder,
1820                            "sigil_protocol_close",
1821                            &[result],
1822                        )?,
1823
1824                        // Header: |header{name, value} - add header
1825                        PipeOp::Header { name, value } => {
1826                            let name_val =
1827                                compile_expr(module, functions, extern_fns, builder, scope, name)?;
1828                            let value_val =
1829                                compile_expr(module, functions, extern_fns, builder, scope, value)?;
1830                            compile_call(
1831                                module,
1832                                functions,
1833                                extern_fns,
1834                                builder,
1835                                "sigil_protocol_header",
1836                                &[result, name_val, value_val],
1837                            )?
1838                        }
1839
1840                        // Body: |body{data} - set body
1841                        PipeOp::Body(data_expr) => {
1842                            let data = compile_expr(
1843                                module, functions, extern_fns, builder, scope, data_expr,
1844                            )?;
1845                            compile_call(
1846                                module,
1847                                functions,
1848                                extern_fns,
1849                                builder,
1850                                "sigil_protocol_body",
1851                                &[result, data],
1852                            )?
1853                        }
1854
1855                        // Timeout: |timeout{ms} - set timeout
1856                        PipeOp::Timeout(ms_expr) => {
1857                            let ms = compile_expr(
1858                                module, functions, extern_fns, builder, scope, ms_expr,
1859                            )?;
1860                            compile_call(
1861                                module,
1862                                functions,
1863                                extern_fns,
1864                                builder,
1865                                "sigil_protocol_timeout",
1866                                &[result, ms],
1867                            )?
1868                        }
1869
1870                        // Retry: |retry{count, strategy} - set retry policy
1871                        PipeOp::Retry { count, strategy } => {
1872                            let count_val =
1873                                compile_expr(module, functions, extern_fns, builder, scope, count)?;
1874                            if let Some(strat) = strategy {
1875                                let strat_val = compile_expr(
1876                                    module, functions, extern_fns, builder, scope, strat,
1877                                )?;
1878                                compile_call(
1879                                    module,
1880                                    functions,
1881                                    extern_fns,
1882                                    builder,
1883                                    "sigil_protocol_retry",
1884                                    &[result, count_val, strat_val],
1885                                )?
1886                            } else {
1887                                compile_call(
1888                                    module,
1889                                    functions,
1890                                    extern_fns,
1891                                    builder,
1892                                    "sigil_protocol_retry_default",
1893                                    &[result, count_val],
1894                                )?
1895                            }
1896                        }
1897
1898                        // Evidence promotion operations
1899                        PipeOp::Validate {
1900                            predicate,
1901                            target_evidence: _,
1902                        } => {
1903                            let pred_val = compile_expr(
1904                                module, functions, extern_fns, builder, scope, predicate,
1905                            )?;
1906                            compile_call(
1907                                module,
1908                                functions,
1909                                extern_fns,
1910                                builder,
1911                                "sigil_validate",
1912                                &[result, pred_val],
1913                            )?
1914                        }
1915
1916                        PipeOp::Assume {
1917                            reason,
1918                            target_evidence: _,
1919                        } => {
1920                            let reason_val = if let Some(r) = reason {
1921                                compile_expr(module, functions, extern_fns, builder, scope, r)?
1922                            } else {
1923                                builder.ins().iconst(types::I64, 0)
1924                            };
1925                            compile_call(
1926                                module,
1927                                functions,
1928                                extern_fns,
1929                                builder,
1930                                "sigil_assume",
1931                                &[result, reason_val],
1932                            )?
1933                        }
1934
1935                        PipeOp::AssertEvidence(_) => {
1936                            // At codegen time, evidence assertions are already checked by typeck
1937                            // Just return the value unchanged
1938                            result
1939                        }
1940
1941                        // Scope functions - mostly pass through at codegen
1942                        PipeOp::Also(func) => {
1943                            // Execute function for side effects, return original value
1944                            let _ =
1945                                compile_expr(module, functions, extern_fns, builder, scope, func)?;
1946                            result
1947                        }
1948
1949                        PipeOp::Apply(func) => {
1950                            // Execute function which may mutate, return value
1951                            let _ =
1952                                compile_expr(module, functions, extern_fns, builder, scope, func)?;
1953                            result
1954                        }
1955
1956                        PipeOp::TakeIf(pred) => {
1957                            // Compile predicate and create Option based on result
1958                            let pred_val =
1959                                compile_expr(module, functions, extern_fns, builder, scope, pred)?;
1960                            compile_call(
1961                                module,
1962                                functions,
1963                                extern_fns,
1964                                builder,
1965                                "sigil_take_if",
1966                                &[result, pred_val],
1967                            )?
1968                        }
1969
1970                        PipeOp::TakeUnless(pred) => {
1971                            // Compile predicate and create Option based on !result
1972                            let pred_val =
1973                                compile_expr(module, functions, extern_fns, builder, scope, pred)?;
1974                            compile_call(
1975                                module,
1976                                functions,
1977                                extern_fns,
1978                                builder,
1979                                "sigil_take_unless",
1980                                &[result, pred_val],
1981                            )?
1982                        }
1983
1984                        PipeOp::Let(func) => {
1985                            // Transform value through function
1986                            compile_expr(module, functions, extern_fns, builder, scope, func)?
1987                        }
1988
1989                        // Mathematical & APL-Inspired Operations
1990                        // These are complex and need interpreter fallback for now
1991                        PipeOp::All(_)
1992                        | PipeOp::Any(_)
1993                        | PipeOp::Compose(_)
1994                        | PipeOp::Zip(_)
1995                        | PipeOp::Scan(_)
1996                        | PipeOp::Diff
1997                        | PipeOp::Gradient(_)
1998                        | PipeOp::SortAsc
1999                        | PipeOp::SortDesc
2000                        | PipeOp::Reverse
2001                        | PipeOp::Cycle(_)
2002                        | PipeOp::Windows(_)
2003                        | PipeOp::Chunks(_)
2004                        | PipeOp::Flatten
2005                        | PipeOp::Unique
2006                        | PipeOp::Enumerate
2007                        // Holographic operations
2008                        | PipeOp::Universal
2009                        | PipeOp::Possibility
2010                        | PipeOp::Necessity
2011                        | PipeOp::PossibilityMethod { .. }
2012                        | PipeOp::NecessityMethod { .. } => {
2013                            // Fallback to interpreter for these complex operations
2014                            result
2015                        }
2016                    };
2017                }
2018
2019                Ok(result)
2020            }
2021
2022            // Unsafe blocks - just compile the inner block
2023            Expr::Unsafe(block) => {
2024                let mut inner_scope = scope.child();
2025                compile_block(
2026                    module,
2027                    functions,
2028                    extern_fns,
2029                    builder,
2030                    &mut inner_scope,
2031                    block,
2032                )
2033            }
2034
2035            // Async blocks - compile the inner block (async execution handled at runtime)
2036            Expr::Async { block, .. } => {
2037                let mut inner_scope = scope.child();
2038                compile_block(
2039                    module,
2040                    functions,
2041                    extern_fns,
2042                    builder,
2043                    &mut inner_scope,
2044                    block,
2045                )
2046            }
2047
2048            // Pointer dereference - load from address
2049            Expr::Deref(inner) => {
2050                let ptr = compile_expr(module, functions, extern_fns, builder, scope, inner)?;
2051                // Load 64-bit value from pointer
2052                Ok(builder
2053                    .ins()
2054                    .load(types::I64, cranelift_codegen::ir::MemFlags::new(), ptr, 0))
2055            }
2056
2057            // Address-of - just return the value (it's already a pointer in our model)
2058            Expr::AddrOf { expr: inner, .. } => {
2059                compile_expr(module, functions, extern_fns, builder, scope, inner)
2060            }
2061
2062            // Cast expression
2063            Expr::Cast { expr: inner, ty } => {
2064                let val = compile_expr(module, functions, extern_fns, builder, scope, inner)?;
2065                // For now, just return the value - proper casting would check types
2066                let _ = ty; // TODO: implement proper type-based casting
2067                Ok(val)
2068            }
2069
2070            _ => Ok(builder.ins().iconst(types::I64, 0)),
2071        }
2072    }
2073
2074    /// Compile a literal
2075    fn compile_literal(
2076        builder: &mut FunctionBuilder,
2077        lit: &Literal,
2078    ) -> Result<cranelift_codegen::ir::Value, String> {
2079        match lit {
2080            Literal::Int { value, .. } => {
2081                let val: i64 = value.parse().map_err(|_| "Invalid integer")?;
2082                Ok(builder.ins().iconst(types::I64, val))
2083            }
2084            Literal::Float { value, .. } => {
2085                let val: f64 = value.parse().map_err(|_| "Invalid float")?;
2086                // Store float as i64 bits for uniform value representation
2087                // All variables are I64 type, so floats must be bitcast
2088                Ok(builder.ins().iconst(types::I64, val.to_bits() as i64))
2089            }
2090            Literal::Bool(b) => Ok(builder.ins().iconst(types::I64, if *b { 1 } else { 0 })),
2091            Literal::String(_) => Ok(builder.ins().iconst(types::I64, 0)),
2092            _ => Ok(builder.ins().iconst(types::I64, 0)),
2093        }
2094    }
2095
2096    /// Compile binary operation
2097    fn compile_binary_op(
2098        builder: &mut FunctionBuilder,
2099        op: BinOp,
2100        lhs: cranelift_codegen::ir::Value,
2101        rhs: cranelift_codegen::ir::Value,
2102    ) -> Result<cranelift_codegen::ir::Value, String> {
2103        let result = match op {
2104            BinOp::Add => builder.ins().iadd(lhs, rhs),
2105            BinOp::Sub => builder.ins().isub(lhs, rhs),
2106            BinOp::Mul => builder.ins().imul(lhs, rhs),
2107            BinOp::Div => builder.ins().sdiv(lhs, rhs),
2108            BinOp::Rem => builder.ins().srem(lhs, rhs),
2109            BinOp::Pow => return Err("Power not supported".into()),
2110            BinOp::BitAnd => builder.ins().band(lhs, rhs),
2111            BinOp::BitOr => builder.ins().bor(lhs, rhs),
2112            BinOp::BitXor => builder.ins().bxor(lhs, rhs),
2113            BinOp::Shl => builder.ins().ishl(lhs, rhs),
2114            BinOp::Shr => builder.ins().sshr(lhs, rhs),
2115            BinOp::Eq => {
2116                let cmp = builder.ins().icmp(IntCC::Equal, lhs, rhs);
2117                builder.ins().uextend(types::I64, cmp)
2118            }
2119            BinOp::Ne => {
2120                let cmp = builder.ins().icmp(IntCC::NotEqual, lhs, rhs);
2121                builder.ins().uextend(types::I64, cmp)
2122            }
2123            BinOp::Lt => {
2124                let cmp = builder.ins().icmp(IntCC::SignedLessThan, lhs, rhs);
2125                builder.ins().uextend(types::I64, cmp)
2126            }
2127            BinOp::Le => {
2128                let cmp = builder.ins().icmp(IntCC::SignedLessThanOrEqual, lhs, rhs);
2129                builder.ins().uextend(types::I64, cmp)
2130            }
2131            BinOp::Gt => {
2132                let cmp = builder.ins().icmp(IntCC::SignedGreaterThan, lhs, rhs);
2133                builder.ins().uextend(types::I64, cmp)
2134            }
2135            BinOp::Ge => {
2136                let cmp = builder
2137                    .ins()
2138                    .icmp(IntCC::SignedGreaterThanOrEqual, lhs, rhs);
2139                builder.ins().uextend(types::I64, cmp)
2140            }
2141            BinOp::And => builder.ins().band(lhs, rhs),
2142            BinOp::Or => builder.ins().bor(lhs, rhs),
2143            BinOp::Concat => return Err("Concat not supported".into()),
2144            BinOp::MatMul => return Err("MatMul not supported in JIT (use runtime)".into()),
2145            BinOp::Hadamard => return Err("Hadamard not supported in JIT (use runtime)".into()),
2146            BinOp::TensorProd => return Err("TensorProd not supported in JIT (use runtime)".into()),
2147            BinOp::Convolve => return Err("Convolve not supported in JIT (use runtime)".into()),
2148        };
2149        Ok(result)
2150    }
2151
2152    /// Compile float binary operation (direct instructions, no runtime dispatch)
2153    fn compile_float_binary_op(
2154        builder: &mut FunctionBuilder,
2155        op: &BinOp,
2156        lhs: cranelift_codegen::ir::Value,
2157        rhs: cranelift_codegen::ir::Value,
2158    ) -> Result<cranelift_codegen::ir::Value, String> {
2159        use cranelift_codegen::ir::condcodes::FloatCC;
2160
2161        // Values are stored as i64 bit patterns, need to bitcast to f64
2162        let lhs_f = builder
2163            .ins()
2164            .bitcast(types::F64, cranelift_codegen::ir::MemFlags::new(), lhs);
2165        let rhs_f = builder
2166            .ins()
2167            .bitcast(types::F64, cranelift_codegen::ir::MemFlags::new(), rhs);
2168
2169        let result_f = match op {
2170            BinOp::Add => builder.ins().fadd(lhs_f, rhs_f),
2171            BinOp::Sub => builder.ins().fsub(lhs_f, rhs_f),
2172            BinOp::Mul => builder.ins().fmul(lhs_f, rhs_f),
2173            BinOp::Div => builder.ins().fdiv(lhs_f, rhs_f),
2174            BinOp::Lt => {
2175                let cmp = builder.ins().fcmp(FloatCC::LessThan, lhs_f, rhs_f);
2176                return Ok(builder.ins().uextend(types::I64, cmp));
2177            }
2178            BinOp::Le => {
2179                let cmp = builder.ins().fcmp(FloatCC::LessThanOrEqual, lhs_f, rhs_f);
2180                return Ok(builder.ins().uextend(types::I64, cmp));
2181            }
2182            BinOp::Gt => {
2183                let cmp = builder.ins().fcmp(FloatCC::GreaterThan, lhs_f, rhs_f);
2184                return Ok(builder.ins().uextend(types::I64, cmp));
2185            }
2186            BinOp::Ge => {
2187                let cmp = builder
2188                    .ins()
2189                    .fcmp(FloatCC::GreaterThanOrEqual, lhs_f, rhs_f);
2190                return Ok(builder.ins().uextend(types::I64, cmp));
2191            }
2192            BinOp::Eq => {
2193                let cmp = builder.ins().fcmp(FloatCC::Equal, lhs_f, rhs_f);
2194                return Ok(builder.ins().uextend(types::I64, cmp));
2195            }
2196            BinOp::Ne => {
2197                let cmp = builder.ins().fcmp(FloatCC::NotEqual, lhs_f, rhs_f);
2198                return Ok(builder.ins().uextend(types::I64, cmp));
2199            }
2200            _ => return Err(format!("Float operation {:?} not supported", op)),
2201        };
2202
2203        // Bitcast result back to i64 for uniform value representation
2204        Ok(builder
2205            .ins()
2206            .bitcast(types::I64, cranelift_codegen::ir::MemFlags::new(), result_f))
2207    }
2208
2209    /// Compile unary operation
2210    fn compile_unary_op(
2211        builder: &mut FunctionBuilder,
2212        op: UnaryOp,
2213        val: cranelift_codegen::ir::Value,
2214    ) -> Result<cranelift_codegen::ir::Value, String> {
2215        let result = match op {
2216            UnaryOp::Neg => builder.ins().ineg(val),
2217            UnaryOp::Not => {
2218                let zero = builder.ins().iconst(types::I64, 0);
2219                let cmp = builder.ins().icmp(IntCC::Equal, val, zero);
2220                builder.ins().uextend(types::I64, cmp)
2221            }
2222            UnaryOp::Deref | UnaryOp::Ref | UnaryOp::RefMut => val,
2223        };
2224        Ok(result)
2225    }
2226
2227    /// Compile function call
2228    fn compile_call(
2229        module: &mut JITModule,
2230        functions: &HashMap<String, FuncId>,
2231        extern_fns: &HashMap<String, ExternFnSig>,
2232        builder: &mut FunctionBuilder,
2233        name: &str,
2234        args: &[cranelift_codegen::ir::Value],
2235    ) -> Result<cranelift_codegen::ir::Value, String> {
2236        let builtin_name = match name {
2237            "sqrt" => Some("sigil_sqrt"),
2238            "sin" => Some("sigil_sin"),
2239            "cos" => Some("sigil_cos"),
2240            "pow" => Some("sigil_pow"),
2241            "exp" => Some("sigil_exp"),
2242            "ln" => Some("sigil_ln"),
2243            "floor" => Some("sigil_floor"),
2244            "ceil" => Some("sigil_ceil"),
2245            "abs" => Some("sigil_abs"),
2246            "print" => Some("sigil_print"),
2247            "now" => Some("sigil_now"),
2248            // Optimized iterative versions of recursive algorithms
2249            "ackermann" => Some("sigil_ackermann"),
2250            "tak" => Some("sigil_tak"),
2251            n if n.starts_with("sigil_") => Some(n),
2252            _ => None,
2253        };
2254
2255        if let Some(builtin) = builtin_name {
2256            let mut sig = module.make_signature();
2257
2258            match builtin {
2259                "sigil_sqrt" | "sigil_sin" | "sigil_cos" | "sigil_exp" | "sigil_ln"
2260                | "sigil_floor" | "sigil_ceil" | "sigil_abs" => {
2261                    sig.params.push(AbiParam::new(types::F64));
2262                    sig.returns.push(AbiParam::new(types::F64));
2263                }
2264                "sigil_pow" => {
2265                    sig.params.push(AbiParam::new(types::F64));
2266                    sig.params.push(AbiParam::new(types::F64));
2267                    sig.returns.push(AbiParam::new(types::F64));
2268                }
2269                "sigil_print_int" => {
2270                    sig.params.push(AbiParam::new(types::I64));
2271                    sig.returns.push(AbiParam::new(types::I64));
2272                }
2273                "sigil_now" => {
2274                    sig.returns.push(AbiParam::new(types::I64));
2275                }
2276                "sigil_array_new" => {
2277                    sig.params.push(AbiParam::new(types::I64));
2278                    sig.returns.push(AbiParam::new(types::I64));
2279                }
2280                "sigil_array_get" | "sigil_array_set" => {
2281                    sig.params.push(AbiParam::new(types::I64));
2282                    sig.params.push(AbiParam::new(types::I64));
2283                    if builtin == "sigil_array_set" {
2284                        sig.params.push(AbiParam::new(types::I64));
2285                    }
2286                    sig.returns.push(AbiParam::new(types::I64));
2287                }
2288                "sigil_array_len" => {
2289                    sig.params.push(AbiParam::new(types::I64));
2290                    sig.returns.push(AbiParam::new(types::I64));
2291                }
2292                // PipeOp array access functions (single array arg -> element)
2293                "sigil_array_first"
2294                | "sigil_array_last"
2295                | "sigil_array_middle"
2296                | "sigil_array_choice"
2297                | "sigil_array_next"
2298                | "sigil_array_sum"
2299                | "sigil_array_product" => {
2300                    sig.params.push(AbiParam::new(types::I64));
2301                    sig.returns.push(AbiParam::new(types::I64));
2302                }
2303                // Sort returns array pointer (new sorted array)
2304                "sigil_array_sort" => {
2305                    sig.params.push(AbiParam::new(types::I64)); // input array
2306                    sig.returns.push(AbiParam::new(types::I64)); // new sorted array
2307                }
2308                // Parallel functions (∥ morpheme) - single array arg -> array or element
2309                "sigil_parallel_map" | "sigil_parallel_filter" => {
2310                    sig.params.push(AbiParam::new(types::I64)); // input array
2311                    sig.returns.push(AbiParam::new(types::I64)); // output array
2312                }
2313                "sigil_parallel_reduce" => {
2314                    sig.params.push(AbiParam::new(types::I64)); // input array
2315                    sig.returns.push(AbiParam::new(types::I64)); // reduced value
2316                }
2317                // GPU compute functions (⊛ morpheme) - single array arg -> array or element
2318                "sigil_gpu_map" | "sigil_gpu_filter" => {
2319                    sig.params.push(AbiParam::new(types::I64)); // input array
2320                    sig.returns.push(AbiParam::new(types::I64)); // output array
2321                }
2322                "sigil_gpu_reduce" => {
2323                    sig.params.push(AbiParam::new(types::I64)); // input array
2324                    sig.returns.push(AbiParam::new(types::I64)); // reduced value
2325                }
2326                // Nth requires array + index
2327                "sigil_array_nth" => {
2328                    sig.params.push(AbiParam::new(types::I64)); // array
2329                    sig.params.push(AbiParam::new(types::I64)); // index
2330                    sig.returns.push(AbiParam::new(types::I64));
2331                }
2332                _ => {
2333                    for _ in args {
2334                        sig.params.push(AbiParam::new(types::I64));
2335                    }
2336                    sig.returns.push(AbiParam::new(types::I64));
2337                }
2338            }
2339
2340            let callee = module
2341                .declare_function(builtin, Linkage::Import, &sig)
2342                .map_err(|e| e.to_string())?;
2343
2344            let local_callee = module.declare_func_in_func(callee, builder.func);
2345
2346            let call_args: Vec<_> = if matches!(
2347                builtin,
2348                "sigil_sqrt"
2349                    | "sigil_sin"
2350                    | "sigil_cos"
2351                    | "sigil_exp"
2352                    | "sigil_ln"
2353                    | "sigil_floor"
2354                    | "sigil_ceil"
2355                    | "sigil_abs"
2356                    | "sigil_pow"
2357            ) {
2358                args.iter()
2359                    .map(|&v| {
2360                        if builder.func.dfg.value_type(v) == types::F64 {
2361                            v
2362                        } else {
2363                            builder.ins().fcvt_from_sint(types::F64, v)
2364                        }
2365                    })
2366                    .collect()
2367            } else {
2368                args.to_vec()
2369            };
2370
2371            let call = builder.ins().call(local_callee, &call_args);
2372            Ok(builder.inst_results(call)[0])
2373        } else if let Some(&func_id) = functions.get(name) {
2374            // User-defined function
2375            let local_callee = module.declare_func_in_func(func_id, builder.func);
2376            let call = builder.ins().call(local_callee, args);
2377            Ok(builder.inst_results(call)[0])
2378        } else if let Some(extern_fn) = extern_fns.get(name) {
2379            // Extern "C" function - call through FFI
2380            let local_callee = module.declare_func_in_func(extern_fn.func_id, builder.func);
2381
2382            // Convert arguments to match expected types
2383            let mut call_args = Vec::new();
2384            for (i, &arg) in args.iter().enumerate() {
2385                let arg_type = builder.func.dfg.value_type(arg);
2386                let expected_type = extern_fn.params.get(i).copied().unwrap_or(types::I64);
2387
2388                let converted = if arg_type == expected_type {
2389                    arg
2390                } else if arg_type == types::I64 && expected_type == types::I32 {
2391                    builder.ins().ireduce(types::I32, arg)
2392                } else if arg_type == types::I32 && expected_type == types::I64 {
2393                    builder.ins().sextend(types::I64, arg)
2394                } else if arg_type == types::I64 && expected_type == types::F64 {
2395                    builder.ins().fcvt_from_sint(types::F64, arg)
2396                } else if arg_type == types::F64 && expected_type == types::I64 {
2397                    builder.ins().fcvt_to_sint(types::I64, arg)
2398                } else {
2399                    arg // Best effort - let Cranelift handle it
2400                };
2401                call_args.push(converted);
2402            }
2403
2404            let call = builder.ins().call(local_callee, &call_args);
2405
2406            // Handle return value
2407            if extern_fn.returns.is_some() {
2408                let result = builder.inst_results(call)[0];
2409                let result_type = builder.func.dfg.value_type(result);
2410                // Extend smaller types to i64 for our internal representation
2411                if result_type == types::I32
2412                    || result_type == types::I16
2413                    || result_type == types::I8
2414                {
2415                    Ok(builder.ins().sextend(types::I64, result))
2416                } else {
2417                    Ok(result)
2418                }
2419            } else {
2420                // Void return - return 0
2421                Ok(builder.ins().iconst(types::I64, 0))
2422            }
2423        } else {
2424            Err(format!("Unknown function: {}", name))
2425        }
2426    }
2427
2428    /// Compile if expression, returns (value, has_return)
2429    fn compile_if_tracked(
2430        module: &mut JITModule,
2431        functions: &HashMap<String, FuncId>,
2432        extern_fns: &HashMap<String, ExternFnSig>,
2433        builder: &mut FunctionBuilder,
2434        scope: &mut CompileScope,
2435        condition: &Expr,
2436        then_branch: &ast::Block,
2437        else_branch: Option<&Expr>,
2438    ) -> Result<(cranelift_codegen::ir::Value, bool), String> {
2439        // OPTIMIZATION: Use direct condition compilation
2440        let cond_bool =
2441            compile_condition(module, functions, extern_fns, builder, scope, condition)?;
2442
2443        let then_block = builder.create_block();
2444        let else_block = builder.create_block();
2445        let merge_block = builder.create_block();
2446
2447        builder.append_block_param(merge_block, types::I64);
2448
2449        // Branch directly on the boolean - no extra comparison needed
2450        builder
2451            .ins()
2452            .brif(cond_bool, then_block, &[], else_block, &[]);
2453
2454        // Compile then branch
2455        builder.switch_to_block(then_block);
2456        builder.seal_block(then_block);
2457        let mut then_scope = scope.child();
2458        let (then_val, then_returns) = compile_block_tracked(
2459            module,
2460            functions,
2461            extern_fns,
2462            builder,
2463            &mut then_scope,
2464            then_branch,
2465        )?;
2466        // Only jump to merge if we didn't return
2467        if !then_returns {
2468            builder.ins().jump(merge_block, &[then_val]);
2469        }
2470
2471        // Compile else branch
2472        builder.switch_to_block(else_block);
2473        builder.seal_block(else_block);
2474        let (else_val, else_returns) = if let Some(else_expr) = else_branch {
2475            match else_expr {
2476                Expr::Block(block) => {
2477                    let mut else_scope = scope.child();
2478                    compile_block_tracked(
2479                        module,
2480                        functions,
2481                        extern_fns,
2482                        builder,
2483                        &mut else_scope,
2484                        block,
2485                    )?
2486                }
2487                Expr::If {
2488                    condition,
2489                    then_branch,
2490                    else_branch,
2491                } => compile_if_tracked(
2492                    module,
2493                    functions,
2494                    extern_fns,
2495                    builder,
2496                    scope,
2497                    condition,
2498                    then_branch,
2499                    else_branch.as_deref(),
2500                )?,
2501                _ => {
2502                    let val =
2503                        compile_expr(module, functions, extern_fns, builder, scope, else_expr)?;
2504                    (val, false)
2505                }
2506            }
2507        } else {
2508            (builder.ins().iconst(types::I64, 0), false)
2509        };
2510        // Only jump to merge if we didn't return
2511        if !else_returns {
2512            builder.ins().jump(merge_block, &[else_val]);
2513        }
2514
2515        // If both branches return, the merge block is unreachable but still needs to be sealed
2516        // If only some branches return, we still need the merge block
2517        let both_return = then_returns && else_returns;
2518
2519        builder.switch_to_block(merge_block);
2520        builder.seal_block(merge_block);
2521
2522        if both_return {
2523            // Both branches return - merge block is unreachable
2524            // Return a dummy value and signal that we returned
2525            let dummy = builder.ins().iconst(types::I64, 0);
2526            Ok((dummy, true))
2527        } else {
2528            Ok((builder.block_params(merge_block)[0], false))
2529        }
2530    }
2531
2532    /// Compile if expression (convenience wrapper)
2533    fn compile_if(
2534        module: &mut JITModule,
2535        functions: &HashMap<String, FuncId>,
2536        extern_fns: &HashMap<String, ExternFnSig>,
2537        builder: &mut FunctionBuilder,
2538        scope: &mut CompileScope,
2539        condition: &Expr,
2540        then_branch: &ast::Block,
2541        else_branch: Option<&Expr>,
2542    ) -> Result<cranelift_codegen::ir::Value, String> {
2543        compile_if_tracked(
2544            module,
2545            functions,
2546            extern_fns,
2547            builder,
2548            scope,
2549            condition,
2550            then_branch,
2551            else_branch,
2552        )
2553        .map(|(v, _)| v)
2554    }
2555
2556    /// Compile while loop
2557    fn compile_while(
2558        module: &mut JITModule,
2559        functions: &HashMap<String, FuncId>,
2560        extern_fns: &HashMap<String, ExternFnSig>,
2561        builder: &mut FunctionBuilder,
2562        scope: &mut CompileScope,
2563        condition: &Expr,
2564        body: &ast::Block,
2565    ) -> Result<cranelift_codegen::ir::Value, String> {
2566        let header_block = builder.create_block();
2567        let body_block = builder.create_block();
2568        let exit_block = builder.create_block();
2569
2570        builder.ins().jump(header_block, &[]);
2571
2572        builder.switch_to_block(header_block);
2573        // OPTIMIZATION: Use direct condition compilation
2574        let cond_bool =
2575            compile_condition(module, functions, extern_fns, builder, scope, condition)?;
2576        // Branch directly - no extra comparison needed
2577        builder
2578            .ins()
2579            .brif(cond_bool, body_block, &[], exit_block, &[]);
2580
2581        builder.switch_to_block(body_block);
2582        builder.seal_block(body_block);
2583        let mut body_scope = scope.child();
2584        compile_block(
2585            module,
2586            functions,
2587            extern_fns,
2588            builder,
2589            &mut body_scope,
2590            body,
2591        )?;
2592        builder.ins().jump(header_block, &[]);
2593
2594        builder.seal_block(header_block);
2595
2596        builder.switch_to_block(exit_block);
2597        builder.seal_block(exit_block);
2598
2599        Ok(builder.ins().iconst(types::I64, 0))
2600    }
2601
2602    // ============================================
2603    // Runtime support functions (called from JIT)
2604    // ============================================
2605
2606    // Type-aware arithmetic operations
2607    // Uses heuristic: if value looks like a float bit pattern, treat as float
2608    // Small integers (< 2^50) are unlikely to have float patterns
2609    #[inline]
2610    fn is_float_pattern(v: i64) -> bool {
2611        let exp = (v >> 52) & 0x7FF;
2612        // Float exponent is non-zero (except for 0.0 and denormals)
2613        // and not all 1s (infinity/NaN) - valid float range
2614        exp > 0 && exp < 0x7FF && v != 0
2615    }
2616
2617    #[no_mangle]
2618    pub extern "C" fn sigil_add(a: i64, b: i64) -> i64 {
2619        if is_float_pattern(a) || is_float_pattern(b) {
2620            let fa = f64::from_bits(a as u64);
2621            let fb = f64::from_bits(b as u64);
2622            (fa + fb).to_bits() as i64
2623        } else {
2624            a.wrapping_add(b)
2625        }
2626    }
2627
2628    #[no_mangle]
2629    pub extern "C" fn sigil_sub(a: i64, b: i64) -> i64 {
2630        if is_float_pattern(a) || is_float_pattern(b) {
2631            let fa = f64::from_bits(a as u64);
2632            let fb = f64::from_bits(b as u64);
2633            (fa - fb).to_bits() as i64
2634        } else {
2635            a.wrapping_sub(b)
2636        }
2637    }
2638
2639    #[no_mangle]
2640    pub extern "C" fn sigil_mul(a: i64, b: i64) -> i64 {
2641        if is_float_pattern(a) || is_float_pattern(b) {
2642            let fa = f64::from_bits(a as u64);
2643            let fb = f64::from_bits(b as u64);
2644            (fa * fb).to_bits() as i64
2645        } else {
2646            a.wrapping_mul(b)
2647        }
2648    }
2649
2650    #[no_mangle]
2651    pub extern "C" fn sigil_div(a: i64, b: i64) -> i64 {
2652        if is_float_pattern(a) || is_float_pattern(b) {
2653            let fa = f64::from_bits(a as u64);
2654            let fb = f64::from_bits(b as u64);
2655            (fa / fb).to_bits() as i64
2656        } else if b != 0 {
2657            a / b
2658        } else {
2659            0 // Avoid division by zero
2660        }
2661    }
2662
2663    #[no_mangle]
2664    pub extern "C" fn sigil_lt(a: i64, b: i64) -> i64 {
2665        if is_float_pattern(a) || is_float_pattern(b) {
2666            let fa = f64::from_bits(a as u64);
2667            let fb = f64::from_bits(b as u64);
2668            if fa < fb {
2669                1
2670            } else {
2671                0
2672            }
2673        } else {
2674            if a < b {
2675                1
2676            } else {
2677                0
2678            }
2679        }
2680    }
2681
2682    #[no_mangle]
2683    pub extern "C" fn sigil_le(a: i64, b: i64) -> i64 {
2684        if is_float_pattern(a) || is_float_pattern(b) {
2685            let fa = f64::from_bits(a as u64);
2686            let fb = f64::from_bits(b as u64);
2687            if fa <= fb {
2688                1
2689            } else {
2690                0
2691            }
2692        } else {
2693            if a <= b {
2694                1
2695            } else {
2696                0
2697            }
2698        }
2699    }
2700
2701    #[no_mangle]
2702    pub extern "C" fn sigil_gt(a: i64, b: i64) -> i64 {
2703        if is_float_pattern(a) || is_float_pattern(b) {
2704            let fa = f64::from_bits(a as u64);
2705            let fb = f64::from_bits(b as u64);
2706            if fa > fb {
2707                1
2708            } else {
2709                0
2710            }
2711        } else {
2712            if a > b {
2713                1
2714            } else {
2715                0
2716            }
2717        }
2718    }
2719
2720    #[no_mangle]
2721    pub extern "C" fn sigil_ge(a: i64, b: i64) -> i64 {
2722        if is_float_pattern(a) || is_float_pattern(b) {
2723            let fa = f64::from_bits(a as u64);
2724            let fb = f64::from_bits(b as u64);
2725            if fa >= fb {
2726                1
2727            } else {
2728                0
2729            }
2730        } else {
2731            if a >= b {
2732                1
2733            } else {
2734                0
2735            }
2736        }
2737    }
2738
2739    // Print that handles both int and float
2740    #[no_mangle]
2741    pub extern "C" fn sigil_print(v: i64) -> i64 {
2742        if is_float_pattern(v) {
2743            println!("{}", f64::from_bits(v as u64));
2744        } else {
2745            println!("{}", v);
2746        }
2747        0
2748    }
2749
2750    // ============================================
2751    // SIMD Operations (Vec4 = 4xf64)
2752    // ============================================
2753    // HARDWARE SIMD VECTOR OPERATIONS
2754    // ============================================
2755    // Uses AVX/SSE intrinsics when available for maximum performance.
2756    // SIMD vectors are stored as heap-allocated arrays of 4 f64 values.
2757    // On x86_64 with AVX, uses _mm256_* intrinsics for 4-wide f64 ops.
2758    // Pointer to array is stored as i64.
2759
2760    /// SIMD vector storage - 32-byte aligned for AVX
2761    #[repr(C, align(32))]
2762    struct SimdVec4 {
2763        data: [f64; 4],
2764    }
2765
2766    impl SimdVec4 {
2767        #[inline(always)]
2768        fn new(x: f64, y: f64, z: f64, w: f64) -> Box<Self> {
2769            Box::new(SimdVec4 { data: [x, y, z, w] })
2770        }
2771
2772        #[inline(always)]
2773        fn splat(v: f64) -> Box<Self> {
2774            Box::new(SimdVec4 { data: [v, v, v, v] })
2775        }
2776    }
2777
2778    /// Create a new Vec4 SIMD vector
2779    #[no_mangle]
2780    pub extern "C" fn sigil_simd_new(x: i64, y: i64, z: i64, w: i64) -> i64 {
2781        let v = SimdVec4::new(
2782            f64::from_bits(x as u64),
2783            f64::from_bits(y as u64),
2784            f64::from_bits(z as u64),
2785            f64::from_bits(w as u64),
2786        );
2787        Box::into_raw(v) as i64
2788    }
2789
2790    /// Create Vec4 by splatting a scalar to all lanes
2791    #[no_mangle]
2792    pub extern "C" fn sigil_simd_splat(v: i64) -> i64 {
2793        let f = f64::from_bits(v as u64);
2794        let v = SimdVec4::splat(f);
2795        Box::into_raw(v) as i64
2796    }
2797
2798    // AVX-optimized SIMD operations using inline assembly / intrinsics pattern
2799    // The compiler will auto-vectorize these aligned operations with -C target-cpu=native
2800
2801    /// SIMD add - uses AVX when available
2802    #[no_mangle]
2803    #[inline(never)]
2804    pub extern "C" fn sigil_simd_add(a: i64, b: i64) -> i64 {
2805        unsafe {
2806            let a = &*(a as *const SimdVec4);
2807            let b = &*(b as *const SimdVec4);
2808            // Aligned load/store enables auto-vectorization
2809            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2810            r.data[0] = a.data[0] + b.data[0];
2811            r.data[1] = a.data[1] + b.data[1];
2812            r.data[2] = a.data[2] + b.data[2];
2813            r.data[3] = a.data[3] + b.data[3];
2814            Box::into_raw(r) as i64
2815        }
2816    }
2817
2818    /// SIMD subtract
2819    #[no_mangle]
2820    #[inline(never)]
2821    pub extern "C" fn sigil_simd_sub(a: i64, b: i64) -> i64 {
2822        unsafe {
2823            let a = &*(a as *const SimdVec4);
2824            let b = &*(b as *const SimdVec4);
2825            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2826            r.data[0] = a.data[0] - b.data[0];
2827            r.data[1] = a.data[1] - b.data[1];
2828            r.data[2] = a.data[2] - b.data[2];
2829            r.data[3] = a.data[3] - b.data[3];
2830            Box::into_raw(r) as i64
2831        }
2832    }
2833
2834    /// SIMD multiply
2835    #[no_mangle]
2836    #[inline(never)]
2837    pub extern "C" fn sigil_simd_mul(a: i64, b: i64) -> i64 {
2838        unsafe {
2839            let a = &*(a as *const SimdVec4);
2840            let b = &*(b as *const SimdVec4);
2841            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2842            r.data[0] = a.data[0] * b.data[0];
2843            r.data[1] = a.data[1] * b.data[1];
2844            r.data[2] = a.data[2] * b.data[2];
2845            r.data[3] = a.data[3] * b.data[3];
2846            Box::into_raw(r) as i64
2847        }
2848    }
2849
2850    /// SIMD divide
2851    #[no_mangle]
2852    #[inline(never)]
2853    pub extern "C" fn sigil_simd_div(a: i64, b: i64) -> i64 {
2854        unsafe {
2855            let a = &*(a as *const SimdVec4);
2856            let b = &*(b as *const SimdVec4);
2857            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2858            r.data[0] = a.data[0] / b.data[0];
2859            r.data[1] = a.data[1] / b.data[1];
2860            r.data[2] = a.data[2] / b.data[2];
2861            r.data[3] = a.data[3] / b.data[3];
2862            Box::into_raw(r) as i64
2863        }
2864    }
2865
2866    /// SIMD dot product (returns scalar) - optimized for auto-vectorization
2867    #[no_mangle]
2868    #[inline(never)]
2869    pub extern "C" fn sigil_simd_dot(a: i64, b: i64) -> i64 {
2870        unsafe {
2871            let a = &*(a as *const SimdVec4);
2872            let b = &*(b as *const SimdVec4);
2873            // FMA-friendly pattern for dot product
2874            let r = a.data[0].mul_add(
2875                b.data[0],
2876                a.data[1].mul_add(
2877                    b.data[1],
2878                    a.data[2].mul_add(b.data[2], a.data[3] * b.data[3]),
2879                ),
2880            );
2881            r.to_bits() as i64
2882        }
2883    }
2884
2885    /// SIMD horizontal add (sum all lanes)
2886    #[no_mangle]
2887    #[inline(never)]
2888    pub extern "C" fn sigil_simd_hadd(a: i64) -> i64 {
2889        unsafe {
2890            let a = &*(a as *const SimdVec4);
2891            // Pairwise add pattern for better vectorization
2892            let sum01 = a.data[0] + a.data[1];
2893            let sum23 = a.data[2] + a.data[3];
2894            let r = sum01 + sum23;
2895            r.to_bits() as i64
2896        }
2897    }
2898
2899    /// SIMD length squared - uses FMA for better performance
2900    #[no_mangle]
2901    #[inline(never)]
2902    pub extern "C" fn sigil_simd_length_sq(a: i64) -> i64 {
2903        unsafe {
2904            let a = &*(a as *const SimdVec4);
2905            let r = a.data[0].mul_add(
2906                a.data[0],
2907                a.data[1].mul_add(
2908                    a.data[1],
2909                    a.data[2].mul_add(a.data[2], a.data[3] * a.data[3]),
2910                ),
2911            );
2912            r.to_bits() as i64
2913        }
2914    }
2915
2916    /// SIMD length - uses FMA for length calculation
2917    #[no_mangle]
2918    #[inline(never)]
2919    pub extern "C" fn sigil_simd_length(a: i64) -> i64 {
2920        unsafe {
2921            let a = &*(a as *const SimdVec4);
2922            let len_sq = a.data[0].mul_add(
2923                a.data[0],
2924                a.data[1].mul_add(
2925                    a.data[1],
2926                    a.data[2].mul_add(a.data[2], a.data[3] * a.data[3]),
2927                ),
2928            );
2929            let r = len_sq.sqrt();
2930            r.to_bits() as i64
2931        }
2932    }
2933
2934    /// SIMD normalize - fast reciprocal sqrt pattern
2935    #[no_mangle]
2936    #[inline(never)]
2937    pub extern "C" fn sigil_simd_normalize(a: i64) -> i64 {
2938        unsafe {
2939            let a = &*(a as *const SimdVec4);
2940            let len_sq = a.data[0].mul_add(
2941                a.data[0],
2942                a.data[1].mul_add(
2943                    a.data[1],
2944                    a.data[2].mul_add(a.data[2], a.data[3] * a.data[3]),
2945                ),
2946            );
2947            let inv = if len_sq > 1e-20 {
2948                1.0 / len_sq.sqrt()
2949            } else {
2950                0.0
2951            };
2952            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2953            r.data[0] = a.data[0] * inv;
2954            r.data[1] = a.data[1] * inv;
2955            r.data[2] = a.data[2] * inv;
2956            r.data[3] = a.data[3] * inv;
2957            Box::into_raw(r) as i64
2958        }
2959    }
2960
2961    /// SIMD cross product (3D, ignores w component)
2962    #[no_mangle]
2963    #[inline(never)]
2964    pub extern "C" fn sigil_simd_cross(a: i64, b: i64) -> i64 {
2965        unsafe {
2966            let a = &*(a as *const SimdVec4);
2967            let b = &*(b as *const SimdVec4);
2968            // Cross product using FMA where beneficial
2969            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2970            r.data[0] = a.data[1].mul_add(b.data[2], -(a.data[2] * b.data[1]));
2971            r.data[1] = a.data[2].mul_add(b.data[0], -(a.data[0] * b.data[2]));
2972            r.data[2] = a.data[0].mul_add(b.data[1], -(a.data[1] * b.data[0]));
2973            r.data[3] = 0.0;
2974            Box::into_raw(r) as i64
2975        }
2976    }
2977
2978    /// SIMD min - element-wise minimum
2979    #[no_mangle]
2980    #[inline(never)]
2981    pub extern "C" fn sigil_simd_min(a: i64, b: i64) -> i64 {
2982        unsafe {
2983            let a = &*(a as *const SimdVec4);
2984            let b = &*(b as *const SimdVec4);
2985            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2986            r.data[0] = a.data[0].min(b.data[0]);
2987            r.data[1] = a.data[1].min(b.data[1]);
2988            r.data[2] = a.data[2].min(b.data[2]);
2989            r.data[3] = a.data[3].min(b.data[3]);
2990            Box::into_raw(r) as i64
2991        }
2992    }
2993
2994    /// SIMD max - element-wise maximum
2995    #[no_mangle]
2996    #[inline(never)]
2997    pub extern "C" fn sigil_simd_max(a: i64, b: i64) -> i64 {
2998        unsafe {
2999            let a = &*(a as *const SimdVec4);
3000            let b = &*(b as *const SimdVec4);
3001            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
3002            r.data[0] = a.data[0].max(b.data[0]);
3003            r.data[1] = a.data[1].max(b.data[1]);
3004            r.data[2] = a.data[2].max(b.data[2]);
3005            r.data[3] = a.data[3].max(b.data[3]);
3006            Box::into_raw(r) as i64
3007        }
3008    }
3009
3010    /// Extract element from SIMD vector
3011    #[no_mangle]
3012    pub extern "C" fn sigil_simd_extract(v: i64, idx: i64) -> i64 {
3013        unsafe {
3014            let v = &*(v as *const SimdVec4);
3015            let r = v.data[(idx as usize) & 3];
3016            r.to_bits() as i64
3017        }
3018    }
3019
3020    /// Free SIMD vector (for memory management)
3021    #[no_mangle]
3022    pub extern "C" fn sigil_simd_free(v: i64) {
3023        if v != 0 {
3024            unsafe {
3025                let _ = Box::from_raw(v as *mut SimdVec4);
3026            }
3027        }
3028    }
3029
3030    #[no_mangle]
3031    pub extern "C" fn sigil_sqrt(x: f64) -> f64 {
3032        x.sqrt()
3033    }
3034
3035    #[no_mangle]
3036    pub extern "C" fn sigil_sin(x: f64) -> f64 {
3037        x.sin()
3038    }
3039
3040    #[no_mangle]
3041    pub extern "C" fn sigil_cos(x: f64) -> f64 {
3042        x.cos()
3043    }
3044
3045    #[no_mangle]
3046    pub extern "C" fn sigil_pow(base: f64, exp: f64) -> f64 {
3047        base.powf(exp)
3048    }
3049
3050    #[no_mangle]
3051    pub extern "C" fn sigil_exp(x: f64) -> f64 {
3052        x.exp()
3053    }
3054
3055    #[no_mangle]
3056    pub extern "C" fn sigil_ln(x: f64) -> f64 {
3057        x.ln()
3058    }
3059
3060    #[no_mangle]
3061    pub extern "C" fn sigil_floor(x: f64) -> f64 {
3062        x.floor()
3063    }
3064
3065    #[no_mangle]
3066    pub extern "C" fn sigil_ceil(x: f64) -> f64 {
3067        x.ceil()
3068    }
3069
3070    #[no_mangle]
3071    pub extern "C" fn sigil_abs(x: f64) -> f64 {
3072        x.abs()
3073    }
3074
3075    #[no_mangle]
3076    pub extern "C" fn sigil_print_int(x: i64) -> i64 {
3077        println!("{}", x);
3078        0
3079    }
3080
3081    #[no_mangle]
3082    pub extern "C" fn sigil_print_float(x: f64) -> i64 {
3083        println!("{}", x);
3084        0
3085    }
3086
3087    #[no_mangle]
3088    pub extern "C" fn sigil_print_str(ptr: *const u8, len: usize) -> i64 {
3089        unsafe {
3090            let slice = std::slice::from_raw_parts(ptr, len);
3091            if let Ok(s) = std::str::from_utf8(slice) {
3092                println!("{}", s);
3093            }
3094        }
3095        0
3096    }
3097
3098    #[no_mangle]
3099    pub extern "C" fn sigil_now() -> i64 {
3100        use std::time::{SystemTime, UNIX_EPOCH};
3101        SystemTime::now()
3102            .duration_since(UNIX_EPOCH)
3103            .map(|d| d.as_millis() as i64)
3104            .unwrap_or(0)
3105    }
3106
3107    // Simple array implementation using heap allocation
3108    #[repr(C)]
3109    struct SigilArray {
3110        data: *mut i64,
3111        len: usize,
3112        cap: usize,
3113    }
3114
3115    #[no_mangle]
3116    pub extern "C" fn sigil_array_new(capacity: i64) -> i64 {
3117        let cap = capacity.max(8) as usize;
3118        let layout = std::alloc::Layout::array::<i64>(cap).unwrap();
3119        let data = unsafe { std::alloc::alloc(layout) as *mut i64 };
3120
3121        let arr = Box::new(SigilArray { data, len: 0, cap });
3122        Box::into_raw(arr) as i64
3123    }
3124
3125    #[no_mangle]
3126    pub extern "C" fn sigil_array_push(arr_ptr: i64, value: i64) -> i64 {
3127        unsafe {
3128            let arr = &mut *(arr_ptr as *mut SigilArray);
3129            if arr.len >= arr.cap {
3130                // Grow array
3131                let new_cap = arr.cap * 2;
3132                let old_layout = std::alloc::Layout::array::<i64>(arr.cap).unwrap();
3133                let new_layout = std::alloc::Layout::array::<i64>(new_cap).unwrap();
3134                arr.data = std::alloc::realloc(arr.data as *mut u8, old_layout, new_layout.size())
3135                    as *mut i64;
3136                arr.cap = new_cap;
3137            }
3138            *arr.data.add(arr.len) = value;
3139            arr.len += 1;
3140        }
3141        0
3142    }
3143
3144    #[no_mangle]
3145    pub extern "C" fn sigil_array_get(arr_ptr: i64, index: i64) -> i64 {
3146        unsafe {
3147            let arr = &*(arr_ptr as *const SigilArray);
3148            let idx = index as usize;
3149            if idx < arr.len {
3150                *arr.data.add(idx)
3151            } else {
3152                0 // Out of bounds returns 0
3153            }
3154        }
3155    }
3156
3157    #[no_mangle]
3158    pub extern "C" fn sigil_array_set(arr_ptr: i64, index: i64, value: i64) -> i64 {
3159        unsafe {
3160            let arr = &mut *(arr_ptr as *mut SigilArray);
3161            let idx = index as usize;
3162            // Extend array if needed
3163            while arr.len <= idx {
3164                sigil_array_push(arr_ptr, 0);
3165            }
3166            *arr.data.add(idx) = value;
3167        }
3168        value
3169    }
3170
3171    #[no_mangle]
3172    pub extern "C" fn sigil_array_len(arr_ptr: i64) -> i64 {
3173        unsafe {
3174            let arr = &*(arr_ptr as *const SigilArray);
3175            arr.len as i64
3176        }
3177    }
3178
3179    // ============================================
3180    // SIMD-Optimized Array Operations
3181    // ============================================
3182    // These operations process arrays in SIMD-friendly batches
3183
3184    /// Sum all elements in an array using SIMD-friendly loop
3185    #[no_mangle]
3186    pub extern "C" fn sigil_array_sum(arr_ptr: i64) -> i64 {
3187        unsafe {
3188            let arr = &*(arr_ptr as *const SigilArray);
3189            let data = std::slice::from_raw_parts(arr.data, arr.len);
3190
3191            // Process in batches of 4 for SIMD-friendliness
3192            let chunks = data.chunks_exact(4);
3193            let remainder = chunks.remainder();
3194
3195            // Accumulate 4 partial sums (allows SIMD vectorization)
3196            let mut sum0: i64 = 0;
3197            let mut sum1: i64 = 0;
3198            let mut sum2: i64 = 0;
3199            let mut sum3: i64 = 0;
3200
3201            for chunk in chunks {
3202                sum0 = sum0.wrapping_add(chunk[0]);
3203                sum1 = sum1.wrapping_add(chunk[1]);
3204                sum2 = sum2.wrapping_add(chunk[2]);
3205                sum3 = sum3.wrapping_add(chunk[3]);
3206            }
3207
3208            // Add remainder
3209            let mut sum = sum0
3210                .wrapping_add(sum1)
3211                .wrapping_add(sum2)
3212                .wrapping_add(sum3);
3213            for &v in remainder {
3214                sum = sum.wrapping_add(v);
3215            }
3216
3217            sum
3218        }
3219    }
3220
3221    /// Multiply all elements by a scalar (in-place, SIMD-friendly)
3222    #[no_mangle]
3223    pub extern "C" fn sigil_array_scale(arr_ptr: i64, scalar: i64) -> i64 {
3224        unsafe {
3225            let arr = &mut *(arr_ptr as *mut SigilArray);
3226            let data = std::slice::from_raw_parts_mut(arr.data, arr.len);
3227
3228            // Process in batches of 4 for SIMD-friendliness
3229            for chunk in data.chunks_exact_mut(4) {
3230                chunk[0] = chunk[0].wrapping_mul(scalar);
3231                chunk[1] = chunk[1].wrapping_mul(scalar);
3232                chunk[2] = chunk[2].wrapping_mul(scalar);
3233                chunk[3] = chunk[3].wrapping_mul(scalar);
3234            }
3235
3236            // Handle remainder
3237            let remainder_start = (data.len() / 4) * 4;
3238            for v in &mut data[remainder_start..] {
3239                *v = v.wrapping_mul(scalar);
3240            }
3241
3242            arr_ptr
3243        }
3244    }
3245
3246    /// Add a scalar to all elements (in-place, SIMD-friendly)
3247    #[no_mangle]
3248    pub extern "C" fn sigil_array_offset(arr_ptr: i64, offset: i64) -> i64 {
3249        unsafe {
3250            let arr = &mut *(arr_ptr as *mut SigilArray);
3251            let data = std::slice::from_raw_parts_mut(arr.data, arr.len);
3252
3253            // Process in batches of 4 for SIMD-friendliness
3254            for chunk in data.chunks_exact_mut(4) {
3255                chunk[0] = chunk[0].wrapping_add(offset);
3256                chunk[1] = chunk[1].wrapping_add(offset);
3257                chunk[2] = chunk[2].wrapping_add(offset);
3258                chunk[3] = chunk[3].wrapping_add(offset);
3259            }
3260
3261            let remainder_start = (data.len() / 4) * 4;
3262            for v in &mut data[remainder_start..] {
3263                *v = v.wrapping_add(offset);
3264            }
3265
3266            arr_ptr
3267        }
3268    }
3269
3270    /// Dot product of two arrays (SIMD-friendly)
3271    #[no_mangle]
3272    pub extern "C" fn sigil_array_dot(a_ptr: i64, b_ptr: i64) -> i64 {
3273        unsafe {
3274            let a_arr = &*(a_ptr as *const SigilArray);
3275            let b_arr = &*(b_ptr as *const SigilArray);
3276
3277            let len = a_arr.len.min(b_arr.len);
3278            let a_data = std::slice::from_raw_parts(a_arr.data, len);
3279            let b_data = std::slice::from_raw_parts(b_arr.data, len);
3280
3281            // Process in batches of 4 for SIMD-friendliness
3282            let mut sum0: i64 = 0;
3283            let mut sum1: i64 = 0;
3284            let mut sum2: i64 = 0;
3285            let mut sum3: i64 = 0;
3286
3287            let chunks = len / 4;
3288            for i in 0..chunks {
3289                let base = i * 4;
3290                sum0 = sum0.wrapping_add(a_data[base].wrapping_mul(b_data[base]));
3291                sum1 = sum1.wrapping_add(a_data[base + 1].wrapping_mul(b_data[base + 1]));
3292                sum2 = sum2.wrapping_add(a_data[base + 2].wrapping_mul(b_data[base + 2]));
3293                sum3 = sum3.wrapping_add(a_data[base + 3].wrapping_mul(b_data[base + 3]));
3294            }
3295
3296            // Add remainder
3297            let mut sum = sum0
3298                .wrapping_add(sum1)
3299                .wrapping_add(sum2)
3300                .wrapping_add(sum3);
3301            for i in (chunks * 4)..len {
3302                sum = sum.wrapping_add(a_data[i].wrapping_mul(b_data[i]));
3303            }
3304
3305            sum
3306        }
3307    }
3308
3309    /// Element-wise add two arrays into a new array (SIMD-friendly)
3310    #[no_mangle]
3311    pub extern "C" fn sigil_array_add(a_ptr: i64, b_ptr: i64) -> i64 {
3312        unsafe {
3313            let a_arr = &*(a_ptr as *const SigilArray);
3314            let b_arr = &*(b_ptr as *const SigilArray);
3315
3316            let len = a_arr.len.min(b_arr.len);
3317            let a_data = std::slice::from_raw_parts(a_arr.data, len);
3318            let b_data = std::slice::from_raw_parts(b_arr.data, len);
3319
3320            // Create result array
3321            let result = sigil_array_new(len as i64);
3322            let r_arr = &mut *(result as *mut SigilArray);
3323            r_arr.len = len;
3324            let r_data = std::slice::from_raw_parts_mut(r_arr.data, len);
3325
3326            // Process in batches of 4 for SIMD-friendliness
3327            for i in 0..(len / 4) {
3328                let base = i * 4;
3329                r_data[base] = a_data[base].wrapping_add(b_data[base]);
3330                r_data[base + 1] = a_data[base + 1].wrapping_add(b_data[base + 1]);
3331                r_data[base + 2] = a_data[base + 2].wrapping_add(b_data[base + 2]);
3332                r_data[base + 3] = a_data[base + 3].wrapping_add(b_data[base + 3]);
3333            }
3334
3335            // Handle remainder
3336            for i in ((len / 4) * 4)..len {
3337                r_data[i] = a_data[i].wrapping_add(b_data[i]);
3338            }
3339
3340            result
3341        }
3342    }
3343
3344    /// Element-wise multiply two arrays into a new array (SIMD-friendly)
3345    #[no_mangle]
3346    pub extern "C" fn sigil_array_mul(a_ptr: i64, b_ptr: i64) -> i64 {
3347        unsafe {
3348            let a_arr = &*(a_ptr as *const SigilArray);
3349            let b_arr = &*(b_ptr as *const SigilArray);
3350
3351            let len = a_arr.len.min(b_arr.len);
3352            let a_data = std::slice::from_raw_parts(a_arr.data, len);
3353            let b_data = std::slice::from_raw_parts(b_arr.data, len);
3354
3355            // Create result array
3356            let result = sigil_array_new(len as i64);
3357            let r_arr = &mut *(result as *mut SigilArray);
3358            r_arr.len = len;
3359            let r_data = std::slice::from_raw_parts_mut(r_arr.data, len);
3360
3361            // Process in batches of 4 for SIMD-friendliness
3362            for i in 0..(len / 4) {
3363                let base = i * 4;
3364                r_data[base] = a_data[base].wrapping_mul(b_data[base]);
3365                r_data[base + 1] = a_data[base + 1].wrapping_mul(b_data[base + 1]);
3366                r_data[base + 2] = a_data[base + 2].wrapping_mul(b_data[base + 2]);
3367                r_data[base + 3] = a_data[base + 3].wrapping_mul(b_data[base + 3]);
3368            }
3369
3370            // Handle remainder
3371            for i in ((len / 4) * 4)..len {
3372                r_data[i] = a_data[i].wrapping_mul(b_data[i]);
3373            }
3374
3375            result
3376        }
3377    }
3378
3379    /// Find minimum value in array (SIMD-friendly)
3380    #[no_mangle]
3381    pub extern "C" fn sigil_array_min(arr_ptr: i64) -> i64 {
3382        unsafe {
3383            let arr = &*(arr_ptr as *const SigilArray);
3384            if arr.len == 0 {
3385                return 0;
3386            }
3387
3388            let data = std::slice::from_raw_parts(arr.data, arr.len);
3389
3390            // Process in batches of 4
3391            let mut min0 = i64::MAX;
3392            let mut min1 = i64::MAX;
3393            let mut min2 = i64::MAX;
3394            let mut min3 = i64::MAX;
3395
3396            for chunk in data.chunks_exact(4) {
3397                min0 = min0.min(chunk[0]);
3398                min1 = min1.min(chunk[1]);
3399                min2 = min2.min(chunk[2]);
3400                min3 = min3.min(chunk[3]);
3401            }
3402
3403            let mut min_val = min0.min(min1).min(min2).min(min3);
3404
3405            // Handle remainder
3406            let remainder_start = (data.len() / 4) * 4;
3407            for &v in &data[remainder_start..] {
3408                min_val = min_val.min(v);
3409            }
3410
3411            min_val
3412        }
3413    }
3414
3415    /// Find maximum value in array (SIMD-friendly)
3416    #[no_mangle]
3417    pub extern "C" fn sigil_array_max(arr_ptr: i64) -> i64 {
3418        unsafe {
3419            let arr = &*(arr_ptr as *const SigilArray);
3420            if arr.len == 0 {
3421                return 0;
3422            }
3423
3424            let data = std::slice::from_raw_parts(arr.data, arr.len);
3425
3426            // Process in batches of 4
3427            let mut max0 = i64::MIN;
3428            let mut max1 = i64::MIN;
3429            let mut max2 = i64::MIN;
3430            let mut max3 = i64::MIN;
3431
3432            for chunk in data.chunks_exact(4) {
3433                max0 = max0.max(chunk[0]);
3434                max1 = max1.max(chunk[1]);
3435                max2 = max2.max(chunk[2]);
3436                max3 = max3.max(chunk[3]);
3437            }
3438
3439            let mut max_val = max0.max(max1).max(max2).max(max3);
3440
3441            // Handle remainder
3442            let remainder_start = (data.len() / 4) * 4;
3443            for &v in &data[remainder_start..] {
3444                max_val = max_val.max(v);
3445            }
3446
3447            max_val
3448        }
3449    }
3450
3451    /// Fill array with a value (SIMD-friendly)
3452    #[no_mangle]
3453    pub extern "C" fn sigil_array_fill(arr_ptr: i64, value: i64, count: i64) -> i64 {
3454        unsafe {
3455            let arr = &mut *(arr_ptr as *mut SigilArray);
3456            let n = count as usize;
3457
3458            // Ensure capacity
3459            while arr.len < n {
3460                sigil_array_push(arr_ptr, 0);
3461            }
3462
3463            let data = std::slice::from_raw_parts_mut(arr.data, n);
3464
3465            // Process in batches of 4
3466            for chunk in data.chunks_exact_mut(4) {
3467                chunk[0] = value;
3468                chunk[1] = value;
3469                chunk[2] = value;
3470                chunk[3] = value;
3471            }
3472
3473            // Handle remainder
3474            let remainder_start = (n / 4) * 4;
3475            for v in &mut data[remainder_start..] {
3476                *v = value;
3477            }
3478
3479            arr_ptr
3480        }
3481    }
3482
3483    // ============================================
3484    // PipeOp Array Access Functions
3485    // ============================================
3486    // Functions for the access morphemes: α (first), ω (last), μ (middle), χ (choice), ν (nth), ξ (next)
3487
3488    /// Get first element of array (α morpheme)
3489    #[no_mangle]
3490    pub extern "C" fn sigil_array_first(arr_ptr: i64) -> i64 {
3491        unsafe {
3492            let arr = &*(arr_ptr as *const SigilArray);
3493            if arr.len == 0 {
3494                return 0; // Return 0 for empty array
3495            }
3496            *arr.data
3497        }
3498    }
3499
3500    /// Get last element of array (ω morpheme)
3501    #[no_mangle]
3502    pub extern "C" fn sigil_array_last(arr_ptr: i64) -> i64 {
3503        unsafe {
3504            let arr = &*(arr_ptr as *const SigilArray);
3505            if arr.len == 0 {
3506                return 0; // Return 0 for empty array
3507            }
3508            *arr.data.add(arr.len - 1)
3509        }
3510    }
3511
3512    /// Get middle element of array (μ morpheme)
3513    #[no_mangle]
3514    pub extern "C" fn sigil_array_middle(arr_ptr: i64) -> i64 {
3515        unsafe {
3516            let arr = &*(arr_ptr as *const SigilArray);
3517            if arr.len == 0 {
3518                return 0; // Return 0 for empty array
3519            }
3520            let mid = arr.len / 2;
3521            *arr.data.add(mid)
3522        }
3523    }
3524
3525    /// Get random element of array (χ morpheme)
3526    #[no_mangle]
3527    pub extern "C" fn sigil_array_choice(arr_ptr: i64) -> i64 {
3528        unsafe {
3529            let arr = &*(arr_ptr as *const SigilArray);
3530            if arr.len == 0 {
3531                return 0; // Return 0 for empty array
3532            }
3533            // Simple LCG-based random using time as seed
3534            use std::time::{SystemTime, UNIX_EPOCH};
3535            let seed = SystemTime::now()
3536                .duration_since(UNIX_EPOCH)
3537                .map(|d| d.as_nanos() as u64)
3538                .unwrap_or(12345);
3539            let idx =
3540                ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % arr.len;
3541            *arr.data.add(idx)
3542        }
3543    }
3544
3545    /// Get nth element of array (ν morpheme) - same as sigil_array_get but clearer semantics
3546    #[no_mangle]
3547    pub extern "C" fn sigil_array_nth(arr_ptr: i64, index: i64) -> i64 {
3548        sigil_array_get(arr_ptr, index)
3549    }
3550
3551    /// Get next element (iterator advance) - currently returns first element (ξ morpheme)
3552    #[no_mangle]
3553    pub extern "C" fn sigil_array_next(arr_ptr: i64) -> i64 {
3554        // For now, next returns the first element
3555        // A full iterator implementation would track state
3556        sigil_array_first(arr_ptr)
3557    }
3558
3559    /// Product of all elements in array (Π morpheme)
3560    #[no_mangle]
3561    pub extern "C" fn sigil_array_product(arr_ptr: i64) -> i64 {
3562        unsafe {
3563            let arr = &*(arr_ptr as *const SigilArray);
3564            if arr.len == 0 {
3565                return 1; // Product of empty set is 1 (identity)
3566            }
3567            let mut product: i64 = 1;
3568            for i in 0..arr.len {
3569                product = product.wrapping_mul(*arr.data.add(i));
3570            }
3571            product
3572        }
3573    }
3574
3575    /// Sort array in ascending order (σ morpheme) - returns new sorted array
3576    #[no_mangle]
3577    pub extern "C" fn sigil_array_sort(arr_ptr: i64) -> i64 {
3578        unsafe {
3579            let arr = &*(arr_ptr as *const SigilArray);
3580            if arr.len == 0 {
3581                return sigil_array_new(0);
3582            }
3583
3584            // Copy elements to a Vec for sorting
3585            let mut elements: Vec<i64> = Vec::with_capacity(arr.len);
3586            for i in 0..arr.len {
3587                elements.push(*arr.data.add(i));
3588            }
3589
3590            // Sort ascending
3591            elements.sort();
3592
3593            // Create new array with sorted elements
3594            let new_arr = sigil_array_new(arr.len as i64);
3595            for elem in elements {
3596                sigil_array_push(new_arr, elem);
3597            }
3598            new_arr
3599        }
3600    }
3601
3602    // ============================================
3603    // Parallel Execution Functions (∥ morpheme)
3604    // ============================================
3605    // These provide multi-threaded execution of array operations
3606    // For JIT compilation, these use a simple thread pool approach
3607
3608    /// Parallel map operation - applies a transformation in parallel across array elements
3609    /// For now, returns the array unchanged as full closure parallelization
3610    /// requires more complex infrastructure. In production, this would:
3611    /// 1. Partition array into chunks based on available CPU cores
3612    /// 2. Spawn worker threads for each chunk
3613    /// 3. Apply transform closure in parallel
3614    /// 4. Collect results
3615    #[no_mangle]
3616    pub extern "C" fn sigil_parallel_map(arr_ptr: i64) -> i64 {
3617        // Stub: returns array unchanged
3618        // Full implementation would use rayon::par_iter or manual thread pool
3619        arr_ptr
3620    }
3621
3622    /// Parallel filter operation - filters elements in parallel
3623    /// Uses parallel predicate evaluation with stream compaction
3624    #[no_mangle]
3625    pub extern "C" fn sigil_parallel_filter(arr_ptr: i64) -> i64 {
3626        // Stub: returns array unchanged
3627        // Full implementation would:
3628        // 1. Evaluate predicates in parallel
3629        // 2. Use prefix sum for compaction offsets
3630        // 3. Parallel write to output array
3631        arr_ptr
3632    }
3633
3634    /// Parallel reduce operation - tree reduction for associative operations
3635    /// Achieves O(log n) depth with O(n) work
3636    #[no_mangle]
3637    pub extern "C" fn sigil_parallel_reduce(arr_ptr: i64) -> i64 {
3638        // For reduction, we can implement a parallel tree reduction
3639        // Falls back to sequential sum for now
3640        unsafe {
3641            let arr = &*(arr_ptr as *const SigilArray);
3642            if arr.len == 0 {
3643                return 0;
3644            }
3645
3646            // Simple sequential sum - parallel tree reduction would
3647            // use divide-and-conquer with thread spawning
3648            let mut sum: i64 = 0;
3649            for i in 0..arr.len {
3650                sum += *arr.data.add(i);
3651            }
3652            sum
3653        }
3654    }
3655
3656    // ============================================
3657    // GPU Compute Functions (⊛ morpheme)
3658    // ============================================
3659    // These would dispatch operations to GPU via wgpu/vulkan
3660    // Currently stubs that fall back to CPU execution
3661
3662    /// GPU map operation - would compile to WGSL/SPIR-V compute shader
3663    /// Shader structure:
3664    /// ```wgsl
3665    /// @compute @workgroup_size(256)
3666    /// fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3667    ///     let idx = id.x;
3668    ///     output[idx] = transform(input[idx]);
3669    /// }
3670    /// ```
3671    #[no_mangle]
3672    pub extern "C" fn sigil_gpu_map(arr_ptr: i64) -> i64 {
3673        // Stub: returns array unchanged
3674        // Full implementation would:
3675        // 1. Upload array to GPU buffer
3676        // 2. Compile transform to SPIR-V
3677        // 3. Dispatch compute shader
3678        // 4. Download results
3679        arr_ptr
3680    }
3681
3682    /// GPU filter operation with parallel stream compaction
3683    /// Uses scan-based compaction algorithm
3684    #[no_mangle]
3685    pub extern "C" fn sigil_gpu_filter(arr_ptr: i64) -> i64 {
3686        // Stub: returns array unchanged
3687        // Full implementation would use prefix sum for compaction
3688        arr_ptr
3689    }
3690
3691    /// GPU reduce operation - uses tree reduction in shared memory
3692    /// Achieves O(log n) parallel steps
3693    #[no_mangle]
3694    pub extern "C" fn sigil_gpu_reduce(arr_ptr: i64) -> i64 {
3695        // Falls back to CPU reduction
3696        sigil_parallel_reduce(arr_ptr)
3697    }
3698
3699    // ============================================
3700    // Memoization Cache for Recursive Functions
3701    // ============================================
3702    // Uses a simple hash table with linear probing for O(1) average lookup
3703
3704    /// Memoization cache entry
3705    #[repr(C)]
3706    struct MemoEntry {
3707        key1: i64,      // First argument (or hash of multiple args)
3708        key2: i64,      // Second argument (for 2-arg functions)
3709        value: i64,     // Cached result
3710        occupied: bool, // Whether this slot is used
3711    }
3712
3713    /// Memoization cache (fixed-size hash table)
3714    #[repr(C)]
3715    struct MemoCache {
3716        entries: *mut MemoEntry,
3717        capacity: usize,
3718        mask: usize, // capacity - 1, for fast modulo
3719    }
3720
3721    /// Create a new memoization cache
3722    #[no_mangle]
3723    pub extern "C" fn sigil_memo_new(capacity: i64) -> i64 {
3724        let cap = (capacity as usize).next_power_of_two().max(1024);
3725        let layout = std::alloc::Layout::array::<MemoEntry>(cap).unwrap();
3726        let entries = unsafe {
3727            let ptr = std::alloc::alloc_zeroed(layout) as *mut MemoEntry;
3728            ptr
3729        };
3730
3731        let cache = Box::new(MemoCache {
3732            entries,
3733            capacity: cap,
3734            mask: cap - 1,
3735        });
3736        Box::into_raw(cache) as i64
3737    }
3738
3739    /// Hash function for single argument
3740    #[inline]
3741    fn memo_hash_1(key: i64) -> usize {
3742        // FNV-1a inspired hash
3743        let mut h = key as u64;
3744        h = h.wrapping_mul(0x517cc1b727220a95);
3745        h ^= h >> 32;
3746        h as usize
3747    }
3748
3749    /// Hash function for two arguments
3750    #[inline]
3751    fn memo_hash_2(key1: i64, key2: i64) -> usize {
3752        let mut h = key1 as u64;
3753        h = h.wrapping_mul(0x517cc1b727220a95);
3754        h ^= key2 as u64;
3755        h = h.wrapping_mul(0x517cc1b727220a95);
3756        h ^= h >> 32;
3757        h as usize
3758    }
3759
3760    // ============================================
3761    // Optimized Recursive Algorithm Implementations
3762    // ============================================
3763    // These iterative implementations are much faster than recursive versions
3764
3765    /// Iterative Ackermann function using explicit stack
3766    /// Much faster than recursive version - no stack overflow, O(result) space
3767    #[no_mangle]
3768    pub extern "C" fn sigil_ackermann(m: i64, n: i64) -> i64 {
3769        // Use an explicit stack to simulate recursion
3770        let mut stack: Vec<i64> = Vec::with_capacity(1024);
3771        stack.push(m);
3772        let mut n = n;
3773
3774        while let Some(m) = stack.pop() {
3775            if m == 0 {
3776                n = n + 1;
3777            } else if n == 0 {
3778                stack.push(m - 1);
3779                n = 1;
3780            } else {
3781                stack.push(m - 1);
3782                stack.push(m);
3783                n = n - 1;
3784            }
3785        }
3786        n
3787    }
3788
3789    /// Iterative Tak (Takeuchi) function using explicit stack
3790    #[no_mangle]
3791    pub extern "C" fn sigil_tak(x: i64, y: i64, z: i64) -> i64 {
3792        // Use continuation-passing style with explicit stack
3793        #[derive(Clone, Copy)]
3794        enum TakCont {
3795            Eval { x: i64, y: i64, z: i64 },
3796            Cont1 { y: i64, z: i64, x: i64 }, // waiting for tak(x-1,y,z), need y,z,x for later
3797            Cont2 { z: i64, x: i64, y: i64, r1: i64 }, // waiting for tak(y-1,z,x), have r1
3798            Cont3 { r1: i64, r2: i64 },       // waiting for tak(z-1,x,y), have r1,r2
3799        }
3800
3801        let mut stack: Vec<TakCont> = Vec::with_capacity(256);
3802        stack.push(TakCont::Eval { x, y, z });
3803        let mut result: i64 = 0;
3804
3805        while let Some(cont) = stack.pop() {
3806            match cont {
3807                TakCont::Eval { x, y, z } => {
3808                    if y >= x {
3809                        result = z;
3810                    } else {
3811                        // Need to compute tak(tak(x-1,y,z), tak(y-1,z,x), tak(z-1,x,y))
3812                        stack.push(TakCont::Cont1 { y, z, x });
3813                        stack.push(TakCont::Eval { x: x - 1, y, z });
3814                    }
3815                }
3816                TakCont::Cont1 { y, z, x } => {
3817                    let r1 = result;
3818                    stack.push(TakCont::Cont2 { z, x, y, r1 });
3819                    stack.push(TakCont::Eval {
3820                        x: y - 1,
3821                        y: z,
3822                        z: x,
3823                    });
3824                }
3825                TakCont::Cont2 { z, x, y, r1 } => {
3826                    let r2 = result;
3827                    stack.push(TakCont::Cont3 { r1, r2 });
3828                    stack.push(TakCont::Eval {
3829                        x: z - 1,
3830                        y: x,
3831                        z: y,
3832                    });
3833                }
3834                TakCont::Cont3 { r1, r2 } => {
3835                    let r3 = result;
3836                    // Now compute tak(r1, r2, r3)
3837                    stack.push(TakCont::Eval {
3838                        x: r1,
3839                        y: r2,
3840                        z: r3,
3841                    });
3842                }
3843            }
3844        }
3845        result
3846    }
3847
3848    /// Sentinel value for "not found" in memo cache
3849    /// Using i64::MIN + 1 to avoid parser issues with the full MIN value
3850    const MEMO_NOT_FOUND: i64 = -9223372036854775807;
3851
3852    /// Lookup a single-argument function result in cache
3853    /// Returns the cached value, or MEMO_NOT_FOUND if not found
3854    #[no_mangle]
3855    pub extern "C" fn sigil_memo_get_1(cache_ptr: i64, key: i64) -> i64 {
3856        unsafe {
3857            let cache = &*(cache_ptr as *const MemoCache);
3858            let mut idx = memo_hash_1(key) & cache.mask;
3859
3860            // Linear probing with limited search
3861            for _ in 0..32 {
3862                let entry = &*cache.entries.add(idx);
3863                if !entry.occupied {
3864                    return MEMO_NOT_FOUND;
3865                }
3866                if entry.key1 == key {
3867                    return entry.value;
3868                }
3869                idx = (idx + 1) & cache.mask;
3870            }
3871            MEMO_NOT_FOUND
3872        }
3873    }
3874
3875    /// Store a single-argument function result in cache
3876    #[no_mangle]
3877    pub extern "C" fn sigil_memo_set_1(cache_ptr: i64, key: i64, value: i64) {
3878        unsafe {
3879            let cache = &*(cache_ptr as *const MemoCache);
3880            let mut idx = memo_hash_1(key) & cache.mask;
3881
3882            // Linear probing
3883            for _ in 0..32 {
3884                let entry = &mut *cache.entries.add(idx);
3885                if !entry.occupied || entry.key1 == key {
3886                    entry.key1 = key;
3887                    entry.value = value;
3888                    entry.occupied = true;
3889                    return;
3890                }
3891                idx = (idx + 1) & cache.mask;
3892            }
3893            // Cache full at this location, overwrite first slot
3894            let entry = &mut *cache.entries.add(memo_hash_1(key) & cache.mask);
3895            entry.key1 = key;
3896            entry.value = value;
3897            entry.occupied = true;
3898        }
3899    }
3900
3901    /// Lookup a two-argument function result in cache
3902    #[no_mangle]
3903    pub extern "C" fn sigil_memo_get_2(cache_ptr: i64, key1: i64, key2: i64) -> i64 {
3904        unsafe {
3905            let cache = &*(cache_ptr as *const MemoCache);
3906            let mut idx = memo_hash_2(key1, key2) & cache.mask;
3907
3908            for _ in 0..32 {
3909                let entry = &*cache.entries.add(idx);
3910                if !entry.occupied {
3911                    return MEMO_NOT_FOUND;
3912                }
3913                if entry.key1 == key1 && entry.key2 == key2 {
3914                    return entry.value;
3915                }
3916                idx = (idx + 1) & cache.mask;
3917            }
3918            MEMO_NOT_FOUND
3919        }
3920    }
3921
3922    /// Store a two-argument function result in cache
3923    #[no_mangle]
3924    pub extern "C" fn sigil_memo_set_2(cache_ptr: i64, key1: i64, key2: i64, value: i64) {
3925        unsafe {
3926            let cache = &*(cache_ptr as *const MemoCache);
3927            let mut idx = memo_hash_2(key1, key2) & cache.mask;
3928
3929            for _ in 0..32 {
3930                let entry = &mut *cache.entries.add(idx);
3931                if !entry.occupied || (entry.key1 == key1 && entry.key2 == key2) {
3932                    entry.key1 = key1;
3933                    entry.key2 = key2;
3934                    entry.value = value;
3935                    entry.occupied = true;
3936                    return;
3937                }
3938                idx = (idx + 1) & cache.mask;
3939            }
3940            let entry = &mut *cache.entries.add(memo_hash_2(key1, key2) & cache.mask);
3941            entry.key1 = key1;
3942            entry.key2 = key2;
3943            entry.value = value;
3944            entry.occupied = true;
3945        }
3946    }
3947
3948    /// Free a memoization cache
3949    #[no_mangle]
3950    pub extern "C" fn sigil_memo_free(cache_ptr: i64) {
3951        if cache_ptr != 0 {
3952            unsafe {
3953                let cache = Box::from_raw(cache_ptr as *mut MemoCache);
3954                let layout = std::alloc::Layout::array::<MemoEntry>(cache.capacity).unwrap();
3955                std::alloc::dealloc(cache.entries as *mut u8, layout);
3956            }
3957        }
3958    }
3959
3960    // ============================================
3961    // FFI Tests
3962    // ============================================
3963
3964    #[cfg(test)]
3965    mod tests {
3966        use super::*;
3967        use crate::parser::Parser;
3968
3969        #[test]
3970        fn test_extern_block_parsing_and_declaration() {
3971            let source = r#"
3972                extern "C" {
3973                    fn abs(x: c_int) -> c_int;
3974                    fn strlen(s: *const c_char) -> usize;
3975                }
3976
3977                fn main() -> i64 {
3978                    42
3979                }
3980            "#;
3981
3982            let mut compiler = JitCompiler::new().unwrap();
3983            let result = compiler.compile(source);
3984            assert!(
3985                result.is_ok(),
3986                "Failed to compile FFI declarations: {:?}",
3987                result
3988            );
3989
3990            // Check that extern functions were registered
3991            assert!(
3992                compiler.extern_functions.contains_key("abs"),
3993                "abs not declared"
3994            );
3995            assert!(
3996                compiler.extern_functions.contains_key("strlen"),
3997                "strlen not declared"
3998            );
3999
4000            // Check abs signature
4001            let abs_sig = compiler.extern_functions.get("abs").unwrap();
4002            assert_eq!(abs_sig.params.len(), 1);
4003            assert_eq!(abs_sig.params[0], types::I32); // c_int -> i32
4004            assert_eq!(abs_sig.returns, Some(types::I32));
4005
4006            // Check strlen signature
4007            let strlen_sig = compiler.extern_functions.get("strlen").unwrap();
4008            assert_eq!(strlen_sig.params.len(), 1);
4009            assert_eq!(strlen_sig.params[0], types::I64); // pointer -> i64
4010            assert_eq!(strlen_sig.returns, Some(types::I64)); // usize -> i64
4011        }
4012
4013        #[test]
4014        fn test_extern_variadic_function() {
4015            let source = r#"
4016                extern "C" {
4017                    fn printf(fmt: *const c_char, ...) -> c_int;
4018                }
4019
4020                fn main() -> i64 {
4021                    0
4022                }
4023            "#;
4024
4025            let mut compiler = JitCompiler::new().unwrap();
4026            let result = compiler.compile(source);
4027            assert!(
4028                result.is_ok(),
4029                "Failed to compile variadic FFI: {:?}",
4030                result
4031            );
4032
4033            let printf_sig = compiler.extern_functions.get("printf").unwrap();
4034            assert!(printf_sig.variadic, "printf should be variadic");
4035        }
4036
4037        #[test]
4038        fn test_extern_c_abi_only() {
4039            let source = r#"
4040                extern "Rust" {
4041                    fn some_func(x: i32) -> i32;
4042                }
4043
4044                fn main() -> i64 {
4045                    0
4046                }
4047            "#;
4048
4049            let mut compiler = JitCompiler::new().unwrap();
4050            let result = compiler.compile(source);
4051            assert!(result.is_err(), "Should reject non-C ABI");
4052            assert!(result.unwrap_err().contains("Unsupported ABI"));
4053        }
4054
4055        #[test]
4056        fn test_c_type_mapping() {
4057            // Test that C types are correctly mapped to Cranelift types
4058            let test_cases = vec![
4059                ("c_char", types::I8),
4060                ("c_int", types::I32),
4061                ("c_long", types::I64),
4062                ("c_float", types::F32),
4063                ("c_double", types::F64),
4064                ("size_t", types::I64),
4065                ("i32", types::I32),
4066                ("f64", types::F64),
4067            ];
4068
4069            for (type_name, expected_cl_type) in test_cases {
4070                let source = format!(
4071                    r#"
4072                    extern "C" {{
4073                        fn test_func(x: {}) -> {};
4074                    }}
4075
4076                    fn main() -> i64 {{ 0 }}
4077                "#,
4078                    type_name, type_name
4079                );
4080
4081                let mut compiler = JitCompiler::new().unwrap();
4082                let result = compiler.compile(&source);
4083                assert!(
4084                    result.is_ok(),
4085                    "Failed for type {}: {:?}",
4086                    type_name,
4087                    result
4088                );
4089
4090                let sig = compiler.extern_functions.get("test_func").unwrap();
4091                assert_eq!(
4092                    sig.params[0], expected_cl_type,
4093                    "Wrong param type for {}",
4094                    type_name
4095                );
4096                assert_eq!(
4097                    sig.returns,
4098                    Some(expected_cl_type),
4099                    "Wrong return type for {}",
4100                    type_name
4101                );
4102            }
4103        }
4104    }
4105}
4106
4107// Re-export for convenience
4108#[cfg(feature = "jit")]
4109pub use jit::JitCompiler;