Skip to main content

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 { pattern, init, else_branch, .. } => {
1050                // For let-else, we evaluate the init and bind the pattern
1051                // The else branch diverges (must return/break/panic)
1052                let val = compile_expr(module, functions, extern_fns, builder, scope, init)?;
1053                let ty = infer_type(init, scope);
1054
1055                if let ast::Pattern::Ident { name, .. } = pattern {
1056                    let var = Variable::from_u32(scope.next_var() as u32);
1057                    builder.declare_var(var, types::I64);
1058                    builder.def_var(var, val);
1059                    scope.define_typed(&name.name, var, ty);
1060                }
1061
1062                // Note: In a full implementation, we'd need to check if the pattern
1063                // matches and branch to else_branch if not. For now, we just
1064                // compile the else_branch to ensure it's valid but don't use it.
1065                let _ = else_branch;
1066
1067                Ok((val, false))
1068            }
1069            ast::Stmt::Expr(expr) | ast::Stmt::Semi(expr) => {
1070                compile_expr_tracked(module, functions, extern_fns, builder, scope, expr)
1071            }
1072            ast::Stmt::Item(_) => Ok((builder.ins().iconst(types::I64, 0), false)),
1073        }
1074    }
1075
1076    /// Compile a statement (convenience wrapper)
1077    #[allow(dead_code)]
1078    fn compile_stmt(
1079        module: &mut JITModule,
1080        functions: &HashMap<String, FuncId>,
1081        extern_fns: &HashMap<String, ExternFnSig>,
1082        builder: &mut FunctionBuilder,
1083        scope: &mut CompileScope,
1084        stmt: &ast::Stmt,
1085    ) -> Result<cranelift_codegen::ir::Value, String> {
1086        compile_stmt_tracked(module, functions, extern_fns, builder, scope, stmt).map(|(v, _)| v)
1087    }
1088
1089    /// Compile an expression, returning (value, has_return)
1090    fn compile_expr_tracked(
1091        module: &mut JITModule,
1092        functions: &HashMap<String, FuncId>,
1093        extern_fns: &HashMap<String, ExternFnSig>,
1094        builder: &mut FunctionBuilder,
1095        scope: &mut CompileScope,
1096        expr: &Expr,
1097    ) -> Result<(cranelift_codegen::ir::Value, bool), String> {
1098        match expr {
1099            Expr::Return(value) => {
1100                // NOTE: Cranelift's return_call requires frame pointers which aren't enabled
1101                // by default. Tail call optimization is handled at the AST level instead
1102                // (see optimizer's accumulator transform for fib-like patterns).
1103                //
1104                // When Cranelift adds better tail call support, enable this:
1105                // if let Some(v) = value {
1106                //     if let Expr::Call { func: call_func, args: call_args } = v.as_ref() {
1107                //         // ... use return_call instruction
1108                //     }
1109                // }
1110
1111                let ret_val = if let Some(v) = value {
1112                    compile_expr(module, functions, extern_fns, builder, scope, v)?
1113                } else {
1114                    builder.ins().iconst(types::I64, 0)
1115                };
1116                builder.ins().return_(&[ret_val]);
1117                Ok((ret_val, true)) // Signal that we have a return
1118            }
1119            Expr::If {
1120                condition,
1121                then_branch,
1122                else_branch,
1123            } => {
1124                // If expressions can contain returns, so use tracked version
1125                compile_if_tracked(
1126                    module,
1127                    functions,
1128                    extern_fns,
1129                    builder,
1130                    scope,
1131                    condition,
1132                    then_branch,
1133                    else_branch.as_deref(),
1134                )
1135            }
1136            Expr::Block(block) => {
1137                let mut inner_scope = scope.child();
1138                compile_block_tracked(
1139                    module,
1140                    functions,
1141                    extern_fns,
1142                    builder,
1143                    &mut inner_scope,
1144                    block,
1145                )
1146            }
1147            _ => {
1148                // All other expressions don't have return
1149                let val = compile_expr(module, functions, extern_fns, builder, scope, expr)?;
1150                Ok((val, false))
1151            }
1152        }
1153    }
1154
1155    /// Compile an expression
1156    fn compile_expr(
1157        module: &mut JITModule,
1158        functions: &HashMap<String, FuncId>,
1159        extern_fns: &HashMap<String, ExternFnSig>,
1160        builder: &mut FunctionBuilder,
1161        scope: &mut CompileScope,
1162        expr: &Expr,
1163    ) -> Result<cranelift_codegen::ir::Value, String> {
1164        // OPTIMIZATION: Try constant folding first
1165        if let Some(val) = try_const_fold(expr) {
1166            return Ok(builder.ins().iconst(types::I64, val));
1167        }
1168
1169        match expr {
1170            Expr::Literal(lit) => compile_literal(builder, lit),
1171
1172            Expr::Path(path) => {
1173                let name = path
1174                    .segments
1175                    .last()
1176                    .map(|s| s.ident.name.clone())
1177                    .unwrap_or_default();
1178                if let Some(var) = scope.lookup(&name) {
1179                    Ok(builder.use_var(var))
1180                } else {
1181                    Err(format!("Undefined variable: {}", name))
1182                }
1183            }
1184
1185            Expr::Binary { op, left, right } => {
1186                // TYPE SPECIALIZATION: Infer types to avoid runtime dispatch
1187                let left_ty = infer_type(left, scope);
1188                let right_ty = infer_type(right, scope);
1189
1190                let lhs = compile_expr(module, functions, extern_fns, builder, scope, left)?;
1191                let rhs = compile_expr(module, functions, extern_fns, builder, scope, right)?;
1192
1193                // OPTIMIZATION: Use direct CPU instructions when both types are known integers
1194                // This eliminates the ~100 cycle function call overhead per operation
1195                if left_ty == ValueType::Int && right_ty == ValueType::Int {
1196                    // Direct integer instructions - no runtime dispatch!
1197                    return compile_binary_op(builder, op.clone(), lhs, rhs);
1198                }
1199
1200                // OPTIMIZATION: Direct float instructions when both are floats
1201                if left_ty == ValueType::Float && right_ty == ValueType::Float {
1202                    return compile_float_binary_op(builder, op, lhs, rhs);
1203                }
1204
1205                // Mixed or unknown types - fall back to runtime dispatch
1206                // This is slower but handles dynamic typing correctly
1207                match op {
1208                    BinOp::Add => compile_call(
1209                        module,
1210                        functions,
1211                        extern_fns,
1212                        builder,
1213                        "sigil_add",
1214                        &[lhs, rhs],
1215                    ),
1216                    BinOp::Sub => compile_call(
1217                        module,
1218                        functions,
1219                        extern_fns,
1220                        builder,
1221                        "sigil_sub",
1222                        &[lhs, rhs],
1223                    ),
1224                    BinOp::Mul => compile_call(
1225                        module,
1226                        functions,
1227                        extern_fns,
1228                        builder,
1229                        "sigil_mul",
1230                        &[lhs, rhs],
1231                    ),
1232                    BinOp::Div => compile_call(
1233                        module,
1234                        functions,
1235                        extern_fns,
1236                        builder,
1237                        "sigil_div",
1238                        &[lhs, rhs],
1239                    ),
1240                    BinOp::Lt => compile_call(
1241                        module,
1242                        functions,
1243                        extern_fns,
1244                        builder,
1245                        "sigil_lt",
1246                        &[lhs, rhs],
1247                    ),
1248                    BinOp::Le => compile_call(
1249                        module,
1250                        functions,
1251                        extern_fns,
1252                        builder,
1253                        "sigil_le",
1254                        &[lhs, rhs],
1255                    ),
1256                    BinOp::Gt => compile_call(
1257                        module,
1258                        functions,
1259                        extern_fns,
1260                        builder,
1261                        "sigil_gt",
1262                        &[lhs, rhs],
1263                    ),
1264                    BinOp::Ge => compile_call(
1265                        module,
1266                        functions,
1267                        extern_fns,
1268                        builder,
1269                        "sigil_ge",
1270                        &[lhs, rhs],
1271                    ),
1272                    _ => compile_binary_op(builder, op.clone(), lhs, rhs),
1273                }
1274            }
1275
1276            Expr::Unary { op, expr: inner } => {
1277                let val = compile_expr(module, functions, extern_fns, builder, scope, inner)?;
1278                compile_unary_op(builder, *op, val)
1279            }
1280
1281            Expr::Call { func, args } => {
1282                let func_name = match func.as_ref() {
1283                    Expr::Path(path) => path
1284                        .segments
1285                        .last()
1286                        .map(|s| s.ident.name.clone())
1287                        .unwrap_or_default(),
1288                    _ => return Err("Only direct function calls supported".into()),
1289                };
1290
1291                let mut arg_vals = Vec::new();
1292                for arg in args {
1293                    arg_vals.push(compile_expr(
1294                        module, functions, extern_fns, builder, scope, arg,
1295                    )?);
1296                }
1297
1298                compile_call(
1299                    module, functions, extern_fns, builder, &func_name, &arg_vals,
1300                )
1301            }
1302
1303            Expr::If {
1304                condition,
1305                then_branch,
1306                else_branch,
1307            } => compile_if(
1308                module,
1309                functions,
1310                extern_fns,
1311                builder,
1312                scope,
1313                condition,
1314                then_branch,
1315                else_branch.as_deref(),
1316            ),
1317
1318            Expr::While { condition, body, .. } => compile_while(
1319                module, functions, extern_fns, builder, scope, condition, body,
1320            ),
1321
1322            Expr::Block(block) => {
1323                let mut inner_scope = scope.child();
1324                compile_block(
1325                    module,
1326                    functions,
1327                    extern_fns,
1328                    builder,
1329                    &mut inner_scope,
1330                    block,
1331                )
1332            }
1333
1334            Expr::Return(value) => {
1335                // NOTE: Tail call optimization via Cranelift's return_call requires frame
1336                // pointers. Tail recursion is handled at the AST level instead.
1337                let ret_val = if let Some(v) = value {
1338                    compile_expr(module, functions, extern_fns, builder, scope, v)?
1339                } else {
1340                    builder.ins().iconst(types::I64, 0)
1341                };
1342                builder.ins().return_(&[ret_val]);
1343                Ok(ret_val)
1344            }
1345
1346            Expr::Assign { target, value } => {
1347                let val = compile_expr(module, functions, extern_fns, builder, scope, value)?;
1348                match target.as_ref() {
1349                    Expr::Path(path) => {
1350                        let name = path
1351                            .segments
1352                            .last()
1353                            .map(|s| s.ident.name.clone())
1354                            .unwrap_or_default();
1355                        if let Some(var) = scope.lookup(&name) {
1356                            builder.def_var(var, val);
1357                            Ok(val)
1358                        } else {
1359                            Err(format!("Undefined variable: {}", name))
1360                        }
1361                    }
1362                    Expr::Index { expr: arr, index } => {
1363                        let arr_val =
1364                            compile_expr(module, functions, extern_fns, builder, scope, arr)?;
1365                        let idx_val =
1366                            compile_expr(module, functions, extern_fns, builder, scope, index)?;
1367                        compile_call(
1368                            module,
1369                            functions,
1370                            extern_fns,
1371                            builder,
1372                            "sigil_array_set",
1373                            &[arr_val, idx_val, val],
1374                        )
1375                    }
1376                    _ => Err("Invalid assignment target".into()),
1377                }
1378            }
1379
1380            Expr::Index { expr: arr, index } => {
1381                let arr_val = compile_expr(module, functions, extern_fns, builder, scope, arr)?;
1382                let idx_val = compile_expr(module, functions, extern_fns, builder, scope, index)?;
1383                compile_call(
1384                    module,
1385                    functions,
1386                    extern_fns,
1387                    builder,
1388                    "sigil_array_get",
1389                    &[arr_val, idx_val],
1390                )
1391            }
1392
1393            Expr::Array(elements) => {
1394                let len = builder.ins().iconst(types::I64, elements.len() as i64);
1395                let arr = compile_call(
1396                    module,
1397                    functions,
1398                    extern_fns,
1399                    builder,
1400                    "sigil_array_new",
1401                    &[len],
1402                )?;
1403
1404                for (i, elem) in elements.iter().enumerate() {
1405                    let val = compile_expr(module, functions, extern_fns, builder, scope, elem)?;
1406                    let idx = builder.ins().iconst(types::I64, i as i64);
1407                    compile_call(
1408                        module,
1409                        functions,
1410                        extern_fns,
1411                        builder,
1412                        "sigil_array_set",
1413                        &[arr, idx, val],
1414                    )?;
1415                }
1416
1417                Ok(arr)
1418            }
1419
1420            Expr::Pipe { expr, operations } => {
1421                // Compile the base expression first
1422                let mut result = compile_expr(module, functions, extern_fns, builder, scope, expr)?;
1423
1424                // Process each pipe operation in sequence
1425                for op in operations {
1426                    result = match op {
1427                        // Simple array access morphemes - call stdlib functions directly
1428                        PipeOp::First => compile_call(
1429                            module,
1430                            functions,
1431                            extern_fns,
1432                            builder,
1433                            "sigil_array_first",
1434                            &[result],
1435                        )?,
1436                        PipeOp::Last => compile_call(
1437                            module,
1438                            functions,
1439                            extern_fns,
1440                            builder,
1441                            "sigil_array_last",
1442                            &[result],
1443                        )?,
1444                        PipeOp::Middle => compile_call(
1445                            module,
1446                            functions,
1447                            extern_fns,
1448                            builder,
1449                            "sigil_array_middle",
1450                            &[result],
1451                        )?,
1452                        PipeOp::Choice => compile_call(
1453                            module,
1454                            functions,
1455                            extern_fns,
1456                            builder,
1457                            "sigil_array_choice",
1458                            &[result],
1459                        )?,
1460                        PipeOp::Next => compile_call(
1461                            module,
1462                            functions,
1463                            extern_fns,
1464                            builder,
1465                            "sigil_array_next",
1466                            &[result],
1467                        )?,
1468                        PipeOp::Nth(index_expr) => {
1469                            let index = compile_expr(
1470                                module, functions, extern_fns, builder, scope, index_expr,
1471                            )?;
1472                            compile_call(
1473                                module,
1474                                functions,
1475                                extern_fns,
1476                                builder,
1477                                "sigil_array_nth",
1478                                &[result, index],
1479                            )?
1480                        }
1481                        // General reduce with closure (ρ morpheme)
1482                        PipeOp::Reduce(_) => {
1483                            // For now, treat reduce as sum for numeric arrays
1484                            compile_call(
1485                                module,
1486                                functions,
1487                                extern_fns,
1488                                builder,
1489                                "sigil_array_sum",
1490                                &[result],
1491                            )?
1492                        }
1493                        // Sum reduction (ρ+ morpheme)
1494                        PipeOp::ReduceSum => compile_call(
1495                            module,
1496                            functions,
1497                            extern_fns,
1498                            builder,
1499                            "sigil_array_sum",
1500                            &[result],
1501                        )?,
1502                        // Product reduction (ρ* morpheme)
1503                        PipeOp::ReduceProd => compile_call(
1504                            module,
1505                            functions,
1506                            extern_fns,
1507                            builder,
1508                            "sigil_array_product",
1509                            &[result],
1510                        )?,
1511                        // Min reduction (ρ_min morpheme)
1512                        PipeOp::ReduceMin => compile_call(
1513                            module,
1514                            functions,
1515                            extern_fns,
1516                            builder,
1517                            "sigil_array_min",
1518                            &[result],
1519                        )?,
1520                        // Max reduction (ρ_max morpheme)
1521                        PipeOp::ReduceMax => compile_call(
1522                            module,
1523                            functions,
1524                            extern_fns,
1525                            builder,
1526                            "sigil_array_max",
1527                            &[result],
1528                        )?,
1529                        // Concat reduction (ρ++ morpheme)
1530                        PipeOp::ReduceConcat => compile_call(
1531                            module,
1532                            functions,
1533                            extern_fns,
1534                            builder,
1535                            "sigil_array_concat",
1536                            &[result],
1537                        )?,
1538                        // All reduction (ρ& morpheme)
1539                        PipeOp::ReduceAll => compile_call(
1540                            module,
1541                            functions,
1542                            extern_fns,
1543                            builder,
1544                            "sigil_array_all",
1545                            &[result],
1546                        )?,
1547                        // Any reduction (ρ| morpheme)
1548                        PipeOp::ReduceAny => compile_call(
1549                            module,
1550                            functions,
1551                            extern_fns,
1552                            builder,
1553                            "sigil_array_any",
1554                            &[result],
1555                        )?,
1556                        // Sort operation (σ morpheme) - returns sorted array pointer
1557                        PipeOp::Sort(_) => compile_call(
1558                            module,
1559                            functions,
1560                            extern_fns,
1561                            builder,
1562                            "sigil_array_sort",
1563                            &[result],
1564                        )?,
1565                        // Transform and Filter require closure compilation - complex
1566                        PipeOp::Transform(_) | PipeOp::Filter(_) => {
1567                            // TODO: Implement closure compilation for transform/filter
1568                            // For now, pass through the array unchanged
1569                            result
1570                        }
1571                        // Method calls, await, and named morphemes
1572                        PipeOp::Method { name, type_args: _, args } => {
1573                            // Compile as a method call on the result
1574                            let mut call_args = vec![result];
1575                            for arg in args {
1576                                call_args.push(compile_expr(
1577                                    module, functions, extern_fns, builder, scope, arg,
1578                                )?);
1579                            }
1580                            compile_call(
1581                                module, functions, extern_fns, builder, &name.name, &call_args,
1582                            )?
1583                        }
1584                        PipeOp::Await => {
1585                            // Await is a no-op in JIT context (sync execution)
1586                            result
1587                        }
1588                        PipeOp::Match(_) => {
1589                            // Match in pipes not supported in JIT - use interpreter
1590                            // (proper implementation would emit branching code)
1591                            result
1592                        }
1593                        PipeOp::TryMap(_) => {
1594                            // Try/error transformation not supported in JIT
1595                            result
1596                        }
1597                        PipeOp::Call(callee) => {
1598                            // Call an arbitrary expression (like self.layer)
1599                            // Compile the callee expression, then call it with result as argument
1600                            let callee_val = compile_expr(
1601                                module, functions, extern_fns, builder, scope, callee,
1602                            )?;
1603                            // For now, treat as function call with result as first arg
1604                            compile_call(
1605                                module, functions, extern_fns, builder,
1606                                "sigil_call",
1607                                &[callee_val, result],
1608                            )?
1609                        }
1610                        PipeOp::Named { prefix, body } => {
1611                            // Named morphemes like ·map{f} - try to call as function
1612                            if !prefix.is_empty() {
1613                                let fn_name = &prefix[0].name;
1614                                if let Some(body_expr) = body {
1615                                    let body_val = compile_expr(
1616                                        module, functions, extern_fns, builder, scope, body_expr,
1617                                    )?;
1618                                    compile_call(
1619                                        module,
1620                                        functions,
1621                                        extern_fns,
1622                                        builder,
1623                                        fn_name,
1624                                        &[result, body_val],
1625                                    )?
1626                                } else {
1627                                    compile_call(
1628                                        module,
1629                                        functions,
1630                                        extern_fns,
1631                                        builder,
1632                                        fn_name,
1633                                        &[result],
1634                                    )?
1635                                }
1636                            } else {
1637                                result
1638                            }
1639                        }
1640                        // Parallel morpheme: ∥ - execute inner operation in parallel
1641                        PipeOp::Parallel(inner_op) => {
1642                            // For JIT compilation, parallel execution is handled by calling
1643                            // sigil_parallel_* variants of operations that use thread pools
1644                            match inner_op.as_ref() {
1645                                PipeOp::Transform(_) => {
1646                                    // Call parallel transform (falls back to sequential for now)
1647                                    compile_call(
1648                                        module,
1649                                        functions,
1650                                        extern_fns,
1651                                        builder,
1652                                        "sigil_parallel_map",
1653                                        &[result],
1654                                    )?
1655                                }
1656                                PipeOp::Filter(_) => {
1657                                    // Call parallel filter
1658                                    compile_call(
1659                                        module,
1660                                        functions,
1661                                        extern_fns,
1662                                        builder,
1663                                        "sigil_parallel_filter",
1664                                        &[result],
1665                                    )?
1666                                }
1667                                PipeOp::Reduce(_) => {
1668                                    // Parallel reduce (tree reduction)
1669                                    compile_call(
1670                                        module,
1671                                        functions,
1672                                        extern_fns,
1673                                        builder,
1674                                        "sigil_parallel_reduce",
1675                                        &[result],
1676                                    )?
1677                                }
1678                                // For other ops, recursively process but mark as parallel hint
1679                                _ => result,
1680                            }
1681                        }
1682                        // GPU compute morpheme: ⊛ - execute on GPU
1683                        PipeOp::Gpu(inner_op) => {
1684                            // GPU execution requires shader compilation
1685                            // For JIT, we call GPU-specific variants that dispatch to compute shaders
1686                            match inner_op.as_ref() {
1687                                PipeOp::Transform(_) => {
1688                                    // GPU transform - dispatches as compute shader
1689                                    compile_call(
1690                                        module,
1691                                        functions,
1692                                        extern_fns,
1693                                        builder,
1694                                        "sigil_gpu_map",
1695                                        &[result],
1696                                    )?
1697                                }
1698                                PipeOp::Filter(_) => {
1699                                    // GPU filter with stream compaction
1700                                    compile_call(
1701                                        module,
1702                                        functions,
1703                                        extern_fns,
1704                                        builder,
1705                                        "sigil_gpu_filter",
1706                                        &[result],
1707                                    )?
1708                                }
1709                                PipeOp::Reduce(_) => {
1710                                    // GPU parallel reduction
1711                                    compile_call(
1712                                        module,
1713                                        functions,
1714                                        extern_fns,
1715                                        builder,
1716                                        "sigil_gpu_reduce",
1717                                        &[result],
1718                                    )?
1719                                }
1720                                _ => result,
1721                            }
1722                        }
1723
1724                        // ==========================================
1725                        // Protocol Operations - Sigil-native networking
1726                        // In JIT context, these call runtime protocol functions
1727                        // ==========================================
1728
1729                        // Send: |send{data} - send data over connection
1730                        PipeOp::Send(data_expr) => {
1731                            let data = compile_expr(
1732                                module, functions, extern_fns, builder, scope, data_expr,
1733                            )?;
1734                            compile_call(
1735                                module,
1736                                functions,
1737                                extern_fns,
1738                                builder,
1739                                "sigil_protocol_send",
1740                                &[result, data],
1741                            )?
1742                        }
1743
1744                        // Recv: |recv - receive data from connection
1745                        PipeOp::Recv => compile_call(
1746                            module,
1747                            functions,
1748                            extern_fns,
1749                            builder,
1750                            "sigil_protocol_recv",
1751                            &[result],
1752                        )?,
1753
1754                        // Stream: |stream{handler} - create streaming iterator
1755                        PipeOp::Stream(handler_expr) => {
1756                            let handler = compile_expr(
1757                                module,
1758                                functions,
1759                                extern_fns,
1760                                builder,
1761                                scope,
1762                                handler_expr,
1763                            )?;
1764                            compile_call(
1765                                module,
1766                                functions,
1767                                extern_fns,
1768                                builder,
1769                                "sigil_protocol_stream",
1770                                &[result, handler],
1771                            )?
1772                        }
1773
1774                        // Connect: |connect{config} - establish connection
1775                        PipeOp::Connect(config_expr) => {
1776                            if let Some(config) = config_expr {
1777                                let config_val = compile_expr(
1778                                    module, functions, extern_fns, builder, scope, config,
1779                                )?;
1780                                compile_call(
1781                                    module,
1782                                    functions,
1783                                    extern_fns,
1784                                    builder,
1785                                    "sigil_protocol_connect",
1786                                    &[result, config_val],
1787                                )?
1788                            } else {
1789                                compile_call(
1790                                    module,
1791                                    functions,
1792                                    extern_fns,
1793                                    builder,
1794                                    "sigil_protocol_connect_default",
1795                                    &[result],
1796                                )?
1797                            }
1798                        }
1799
1800                        // Close: |close - close connection
1801                        PipeOp::Close => compile_call(
1802                            module,
1803                            functions,
1804                            extern_fns,
1805                            builder,
1806                            "sigil_protocol_close",
1807                            &[result],
1808                        )?,
1809
1810                        // Header: |header{name, value} - add header
1811                        PipeOp::Header { name, value } => {
1812                            let name_val =
1813                                compile_expr(module, functions, extern_fns, builder, scope, name)?;
1814                            let value_val =
1815                                compile_expr(module, functions, extern_fns, builder, scope, value)?;
1816                            compile_call(
1817                                module,
1818                                functions,
1819                                extern_fns,
1820                                builder,
1821                                "sigil_protocol_header",
1822                                &[result, name_val, value_val],
1823                            )?
1824                        }
1825
1826                        // Body: |body{data} - set body
1827                        PipeOp::Body(data_expr) => {
1828                            let data = compile_expr(
1829                                module, functions, extern_fns, builder, scope, data_expr,
1830                            )?;
1831                            compile_call(
1832                                module,
1833                                functions,
1834                                extern_fns,
1835                                builder,
1836                                "sigil_protocol_body",
1837                                &[result, data],
1838                            )?
1839                        }
1840
1841                        // Timeout: |timeout{ms} - set timeout
1842                        PipeOp::Timeout(ms_expr) => {
1843                            let ms = compile_expr(
1844                                module, functions, extern_fns, builder, scope, ms_expr,
1845                            )?;
1846                            compile_call(
1847                                module,
1848                                functions,
1849                                extern_fns,
1850                                builder,
1851                                "sigil_protocol_timeout",
1852                                &[result, ms],
1853                            )?
1854                        }
1855
1856                        // Retry: |retry{count, strategy} - set retry policy
1857                        PipeOp::Retry { count, strategy } => {
1858                            let count_val =
1859                                compile_expr(module, functions, extern_fns, builder, scope, count)?;
1860                            if let Some(strat) = strategy {
1861                                let strat_val = compile_expr(
1862                                    module, functions, extern_fns, builder, scope, strat,
1863                                )?;
1864                                compile_call(
1865                                    module,
1866                                    functions,
1867                                    extern_fns,
1868                                    builder,
1869                                    "sigil_protocol_retry",
1870                                    &[result, count_val, strat_val],
1871                                )?
1872                            } else {
1873                                compile_call(
1874                                    module,
1875                                    functions,
1876                                    extern_fns,
1877                                    builder,
1878                                    "sigil_protocol_retry_default",
1879                                    &[result, count_val],
1880                                )?
1881                            }
1882                        }
1883
1884                        // Evidence promotion operations
1885                        PipeOp::Validate {
1886                            predicate,
1887                            target_evidence: _,
1888                        } => {
1889                            let pred_val = compile_expr(
1890                                module, functions, extern_fns, builder, scope, predicate,
1891                            )?;
1892                            compile_call(
1893                                module,
1894                                functions,
1895                                extern_fns,
1896                                builder,
1897                                "sigil_validate",
1898                                &[result, pred_val],
1899                            )?
1900                        }
1901
1902                        PipeOp::Assume {
1903                            reason,
1904                            target_evidence: _,
1905                        } => {
1906                            let reason_val = if let Some(r) = reason {
1907                                compile_expr(module, functions, extern_fns, builder, scope, r)?
1908                            } else {
1909                                builder.ins().iconst(types::I64, 0)
1910                            };
1911                            compile_call(
1912                                module,
1913                                functions,
1914                                extern_fns,
1915                                builder,
1916                                "sigil_assume",
1917                                &[result, reason_val],
1918                            )?
1919                        }
1920
1921                        PipeOp::AssertEvidence(_) => {
1922                            // At codegen time, evidence assertions are already checked by typeck
1923                            // Just return the value unchanged
1924                            result
1925                        }
1926
1927                        // Scope functions - mostly pass through at codegen
1928                        PipeOp::Also(func) => {
1929                            // Execute function for side effects, return original value
1930                            let _ =
1931                                compile_expr(module, functions, extern_fns, builder, scope, func)?;
1932                            result
1933                        }
1934
1935                        PipeOp::Apply(func) => {
1936                            // Execute function which may mutate, return value
1937                            let _ =
1938                                compile_expr(module, functions, extern_fns, builder, scope, func)?;
1939                            result
1940                        }
1941
1942                        PipeOp::TakeIf(pred) => {
1943                            // Compile predicate and create Option based on result
1944                            let pred_val =
1945                                compile_expr(module, functions, extern_fns, builder, scope, pred)?;
1946                            compile_call(
1947                                module,
1948                                functions,
1949                                extern_fns,
1950                                builder,
1951                                "sigil_take_if",
1952                                &[result, pred_val],
1953                            )?
1954                        }
1955
1956                        PipeOp::TakeUnless(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_unless",
1966                                &[result, pred_val],
1967                            )?
1968                        }
1969
1970                        PipeOp::Let(func) => {
1971                            // Transform value through function
1972                            compile_expr(module, functions, extern_fns, builder, scope, func)?
1973                        }
1974
1975                        // Mathematical & APL-Inspired Operations
1976                        // These are complex and need interpreter fallback for now
1977                        PipeOp::All(_)
1978                        | PipeOp::Any(_)
1979                        | PipeOp::Compose(_)
1980                        | PipeOp::Zip(_)
1981                        | PipeOp::Scan(_)
1982                        | PipeOp::Diff
1983                        | PipeOp::Gradient(_)
1984                        | PipeOp::SortBy(_)
1985                        | PipeOp::SortAsc
1986                        | PipeOp::SortDesc
1987                        | PipeOp::Reverse
1988                        | PipeOp::Cycle(_)
1989                        | PipeOp::Windows(_)
1990                        | PipeOp::Chunks(_)
1991                        | PipeOp::Flatten
1992                        | PipeOp::Unique
1993                        | PipeOp::Enumerate
1994                        | PipeOp::ReduceWithInit(_, _)
1995                        // Holographic operations (Spec 11)
1996                        | PipeOp::Universal
1997                        | PipeOp::Possibility { .. }
1998                        | PipeOp::Necessity { .. }
1999                        | PipeOp::PossibilityExtract
2000                        | PipeOp::NecessityVerify => {
2001                            // Fallback to interpreter for these complex operations
2002                            result
2003                        }
2004                    };
2005                }
2006
2007                Ok(result)
2008            }
2009
2010            // Unsafe blocks - just compile the inner block
2011            Expr::Unsafe(block) => {
2012                let mut inner_scope = scope.child();
2013                compile_block(
2014                    module,
2015                    functions,
2016                    extern_fns,
2017                    builder,
2018                    &mut inner_scope,
2019                    block,
2020                )
2021            }
2022
2023            // Async blocks - compile the inner block (async execution handled at runtime)
2024            Expr::Async { block, .. } => {
2025                let mut inner_scope = scope.child();
2026                compile_block(
2027                    module,
2028                    functions,
2029                    extern_fns,
2030                    builder,
2031                    &mut inner_scope,
2032                    block,
2033                )
2034            }
2035
2036            // Pointer dereference - load from address
2037            Expr::Deref(inner) => {
2038                let ptr = compile_expr(module, functions, extern_fns, builder, scope, inner)?;
2039                // Load 64-bit value from pointer
2040                Ok(builder
2041                    .ins()
2042                    .load(types::I64, cranelift_codegen::ir::MemFlags::new(), ptr, 0))
2043            }
2044
2045            // Address-of - just return the value (it's already a pointer in our model)
2046            Expr::AddrOf { expr: inner, .. } => {
2047                compile_expr(module, functions, extern_fns, builder, scope, inner)
2048            }
2049
2050            // Cast expression
2051            Expr::Cast { expr: inner, ty } => {
2052                let val = compile_expr(module, functions, extern_fns, builder, scope, inner)?;
2053                // For now, just return the value - proper casting would check types
2054                let _ = ty; // TODO: implement proper type-based casting
2055                Ok(val)
2056            }
2057
2058            _ => Ok(builder.ins().iconst(types::I64, 0)),
2059        }
2060    }
2061
2062    /// Compile a literal
2063    fn compile_literal(
2064        builder: &mut FunctionBuilder,
2065        lit: &Literal,
2066    ) -> Result<cranelift_codegen::ir::Value, String> {
2067        match lit {
2068            Literal::Int { value, .. } => {
2069                let val: i64 = value.parse().map_err(|_| "Invalid integer")?;
2070                Ok(builder.ins().iconst(types::I64, val))
2071            }
2072            Literal::Float { value, .. } => {
2073                let val: f64 = value.parse().map_err(|_| "Invalid float")?;
2074                // Store float as i64 bits for uniform value representation
2075                // All variables are I64 type, so floats must be bitcast
2076                Ok(builder.ins().iconst(types::I64, val.to_bits() as i64))
2077            }
2078            Literal::Bool(b) => Ok(builder.ins().iconst(types::I64, if *b { 1 } else { 0 })),
2079            Literal::String(_) => Ok(builder.ins().iconst(types::I64, 0)),
2080            _ => Ok(builder.ins().iconst(types::I64, 0)),
2081        }
2082    }
2083
2084    /// Compile binary operation
2085    fn compile_binary_op(
2086        builder: &mut FunctionBuilder,
2087        op: BinOp,
2088        lhs: cranelift_codegen::ir::Value,
2089        rhs: cranelift_codegen::ir::Value,
2090    ) -> Result<cranelift_codegen::ir::Value, String> {
2091        let result = match op {
2092            BinOp::Add => builder.ins().iadd(lhs, rhs),
2093            BinOp::Sub => builder.ins().isub(lhs, rhs),
2094            BinOp::Mul => builder.ins().imul(lhs, rhs),
2095            BinOp::Div => builder.ins().sdiv(lhs, rhs),
2096            BinOp::Rem => builder.ins().srem(lhs, rhs),
2097            BinOp::Pow => return Err("Power not supported".into()),
2098            BinOp::BitAnd => builder.ins().band(lhs, rhs),
2099            BinOp::BitOr => builder.ins().bor(lhs, rhs),
2100            BinOp::BitXor => builder.ins().bxor(lhs, rhs),
2101            BinOp::Shl => builder.ins().ishl(lhs, rhs),
2102            BinOp::Shr => builder.ins().sshr(lhs, rhs),
2103            BinOp::Eq => {
2104                let cmp = builder.ins().icmp(IntCC::Equal, lhs, rhs);
2105                builder.ins().uextend(types::I64, cmp)
2106            }
2107            BinOp::Ne => {
2108                let cmp = builder.ins().icmp(IntCC::NotEqual, lhs, rhs);
2109                builder.ins().uextend(types::I64, cmp)
2110            }
2111            BinOp::Lt => {
2112                let cmp = builder.ins().icmp(IntCC::SignedLessThan, lhs, rhs);
2113                builder.ins().uextend(types::I64, cmp)
2114            }
2115            BinOp::Le => {
2116                let cmp = builder.ins().icmp(IntCC::SignedLessThanOrEqual, lhs, rhs);
2117                builder.ins().uextend(types::I64, cmp)
2118            }
2119            BinOp::Gt => {
2120                let cmp = builder.ins().icmp(IntCC::SignedGreaterThan, lhs, rhs);
2121                builder.ins().uextend(types::I64, cmp)
2122            }
2123            BinOp::Ge => {
2124                let cmp = builder
2125                    .ins()
2126                    .icmp(IntCC::SignedGreaterThanOrEqual, lhs, rhs);
2127                builder.ins().uextend(types::I64, cmp)
2128            }
2129            BinOp::And => builder.ins().band(lhs, rhs),
2130            BinOp::Or => builder.ins().bor(lhs, rhs),
2131            BinOp::Concat => return Err("Concat not supported".into()),
2132            BinOp::MatMul => return Err("MatMul not supported in JIT (use runtime)".into()),
2133            BinOp::Hadamard => return Err("Hadamard not supported in JIT (use runtime)".into()),
2134            BinOp::TensorProd => return Err("TensorProd not supported in JIT (use runtime)".into()),
2135            BinOp::Convolve => return Err("Convolve not supported in JIT (use runtime)".into()),
2136        };
2137        Ok(result)
2138    }
2139
2140    /// Compile float binary operation (direct instructions, no runtime dispatch)
2141    fn compile_float_binary_op(
2142        builder: &mut FunctionBuilder,
2143        op: &BinOp,
2144        lhs: cranelift_codegen::ir::Value,
2145        rhs: cranelift_codegen::ir::Value,
2146    ) -> Result<cranelift_codegen::ir::Value, String> {
2147        use cranelift_codegen::ir::condcodes::FloatCC;
2148
2149        // Values are stored as i64 bit patterns, need to bitcast to f64
2150        let lhs_f = builder
2151            .ins()
2152            .bitcast(types::F64, cranelift_codegen::ir::MemFlags::new(), lhs);
2153        let rhs_f = builder
2154            .ins()
2155            .bitcast(types::F64, cranelift_codegen::ir::MemFlags::new(), rhs);
2156
2157        let result_f = match op {
2158            BinOp::Add => builder.ins().fadd(lhs_f, rhs_f),
2159            BinOp::Sub => builder.ins().fsub(lhs_f, rhs_f),
2160            BinOp::Mul => builder.ins().fmul(lhs_f, rhs_f),
2161            BinOp::Div => builder.ins().fdiv(lhs_f, rhs_f),
2162            BinOp::Lt => {
2163                let cmp = builder.ins().fcmp(FloatCC::LessThan, lhs_f, rhs_f);
2164                return Ok(builder.ins().uextend(types::I64, cmp));
2165            }
2166            BinOp::Le => {
2167                let cmp = builder.ins().fcmp(FloatCC::LessThanOrEqual, lhs_f, rhs_f);
2168                return Ok(builder.ins().uextend(types::I64, cmp));
2169            }
2170            BinOp::Gt => {
2171                let cmp = builder.ins().fcmp(FloatCC::GreaterThan, lhs_f, rhs_f);
2172                return Ok(builder.ins().uextend(types::I64, cmp));
2173            }
2174            BinOp::Ge => {
2175                let cmp = builder
2176                    .ins()
2177                    .fcmp(FloatCC::GreaterThanOrEqual, lhs_f, rhs_f);
2178                return Ok(builder.ins().uextend(types::I64, cmp));
2179            }
2180            BinOp::Eq => {
2181                let cmp = builder.ins().fcmp(FloatCC::Equal, lhs_f, rhs_f);
2182                return Ok(builder.ins().uextend(types::I64, cmp));
2183            }
2184            BinOp::Ne => {
2185                let cmp = builder.ins().fcmp(FloatCC::NotEqual, lhs_f, rhs_f);
2186                return Ok(builder.ins().uextend(types::I64, cmp));
2187            }
2188            _ => return Err(format!("Float operation {:?} not supported", op)),
2189        };
2190
2191        // Bitcast result back to i64 for uniform value representation
2192        Ok(builder
2193            .ins()
2194            .bitcast(types::I64, cranelift_codegen::ir::MemFlags::new(), result_f))
2195    }
2196
2197    /// Compile unary operation
2198    fn compile_unary_op(
2199        builder: &mut FunctionBuilder,
2200        op: UnaryOp,
2201        val: cranelift_codegen::ir::Value,
2202    ) -> Result<cranelift_codegen::ir::Value, String> {
2203        let result = match op {
2204            UnaryOp::Neg => builder.ins().ineg(val),
2205            UnaryOp::Not => {
2206                let zero = builder.ins().iconst(types::I64, 0);
2207                let cmp = builder.ins().icmp(IntCC::Equal, val, zero);
2208                builder.ins().uextend(types::I64, cmp)
2209            }
2210            UnaryOp::Deref | UnaryOp::Ref | UnaryOp::RefMut => val,
2211        };
2212        Ok(result)
2213    }
2214
2215    /// Compile function call
2216    fn compile_call(
2217        module: &mut JITModule,
2218        functions: &HashMap<String, FuncId>,
2219        extern_fns: &HashMap<String, ExternFnSig>,
2220        builder: &mut FunctionBuilder,
2221        name: &str,
2222        args: &[cranelift_codegen::ir::Value],
2223    ) -> Result<cranelift_codegen::ir::Value, String> {
2224        let builtin_name = match name {
2225            "sqrt" => Some("sigil_sqrt"),
2226            "sin" => Some("sigil_sin"),
2227            "cos" => Some("sigil_cos"),
2228            "pow" => Some("sigil_pow"),
2229            "exp" => Some("sigil_exp"),
2230            "ln" => Some("sigil_ln"),
2231            "floor" => Some("sigil_floor"),
2232            "ceil" => Some("sigil_ceil"),
2233            "abs" => Some("sigil_abs"),
2234            "print" => Some("sigil_print"),
2235            "now" => Some("sigil_now"),
2236            // Optimized iterative versions of recursive algorithms
2237            "ackermann" => Some("sigil_ackermann"),
2238            "tak" => Some("sigil_tak"),
2239            n if n.starts_with("sigil_") => Some(n),
2240            _ => None,
2241        };
2242
2243        if let Some(builtin) = builtin_name {
2244            let mut sig = module.make_signature();
2245
2246            match builtin {
2247                "sigil_sqrt" | "sigil_sin" | "sigil_cos" | "sigil_exp" | "sigil_ln"
2248                | "sigil_floor" | "sigil_ceil" | "sigil_abs" => {
2249                    sig.params.push(AbiParam::new(types::F64));
2250                    sig.returns.push(AbiParam::new(types::F64));
2251                }
2252                "sigil_pow" => {
2253                    sig.params.push(AbiParam::new(types::F64));
2254                    sig.params.push(AbiParam::new(types::F64));
2255                    sig.returns.push(AbiParam::new(types::F64));
2256                }
2257                "sigil_print_int" => {
2258                    sig.params.push(AbiParam::new(types::I64));
2259                    sig.returns.push(AbiParam::new(types::I64));
2260                }
2261                "sigil_now" => {
2262                    sig.returns.push(AbiParam::new(types::I64));
2263                }
2264                "sigil_array_new" => {
2265                    sig.params.push(AbiParam::new(types::I64));
2266                    sig.returns.push(AbiParam::new(types::I64));
2267                }
2268                "sigil_array_get" | "sigil_array_set" => {
2269                    sig.params.push(AbiParam::new(types::I64));
2270                    sig.params.push(AbiParam::new(types::I64));
2271                    if builtin == "sigil_array_set" {
2272                        sig.params.push(AbiParam::new(types::I64));
2273                    }
2274                    sig.returns.push(AbiParam::new(types::I64));
2275                }
2276                "sigil_array_len" => {
2277                    sig.params.push(AbiParam::new(types::I64));
2278                    sig.returns.push(AbiParam::new(types::I64));
2279                }
2280                // PipeOp array access functions (single array arg -> element)
2281                "sigil_array_first"
2282                | "sigil_array_last"
2283                | "sigil_array_middle"
2284                | "sigil_array_choice"
2285                | "sigil_array_next"
2286                | "sigil_array_sum"
2287                | "sigil_array_product" => {
2288                    sig.params.push(AbiParam::new(types::I64));
2289                    sig.returns.push(AbiParam::new(types::I64));
2290                }
2291                // Sort returns array pointer (new sorted array)
2292                "sigil_array_sort" => {
2293                    sig.params.push(AbiParam::new(types::I64)); // input array
2294                    sig.returns.push(AbiParam::new(types::I64)); // new sorted array
2295                }
2296                // Parallel functions (∥ morpheme) - single array arg -> array or element
2297                "sigil_parallel_map" | "sigil_parallel_filter" => {
2298                    sig.params.push(AbiParam::new(types::I64)); // input array
2299                    sig.returns.push(AbiParam::new(types::I64)); // output array
2300                }
2301                "sigil_parallel_reduce" => {
2302                    sig.params.push(AbiParam::new(types::I64)); // input array
2303                    sig.returns.push(AbiParam::new(types::I64)); // reduced value
2304                }
2305                // GPU compute functions (⊛ morpheme) - single array arg -> array or element
2306                "sigil_gpu_map" | "sigil_gpu_filter" => {
2307                    sig.params.push(AbiParam::new(types::I64)); // input array
2308                    sig.returns.push(AbiParam::new(types::I64)); // output array
2309                }
2310                "sigil_gpu_reduce" => {
2311                    sig.params.push(AbiParam::new(types::I64)); // input array
2312                    sig.returns.push(AbiParam::new(types::I64)); // reduced value
2313                }
2314                // Nth requires array + index
2315                "sigil_array_nth" => {
2316                    sig.params.push(AbiParam::new(types::I64)); // array
2317                    sig.params.push(AbiParam::new(types::I64)); // index
2318                    sig.returns.push(AbiParam::new(types::I64));
2319                }
2320                _ => {
2321                    for _ in args {
2322                        sig.params.push(AbiParam::new(types::I64));
2323                    }
2324                    sig.returns.push(AbiParam::new(types::I64));
2325                }
2326            }
2327
2328            let callee = module
2329                .declare_function(builtin, Linkage::Import, &sig)
2330                .map_err(|e| e.to_string())?;
2331
2332            let local_callee = module.declare_func_in_func(callee, builder.func);
2333
2334            let call_args: Vec<_> = if matches!(
2335                builtin,
2336                "sigil_sqrt"
2337                    | "sigil_sin"
2338                    | "sigil_cos"
2339                    | "sigil_exp"
2340                    | "sigil_ln"
2341                    | "sigil_floor"
2342                    | "sigil_ceil"
2343                    | "sigil_abs"
2344                    | "sigil_pow"
2345            ) {
2346                args.iter()
2347                    .map(|&v| {
2348                        if builder.func.dfg.value_type(v) == types::F64 {
2349                            v
2350                        } else {
2351                            builder.ins().fcvt_from_sint(types::F64, v)
2352                        }
2353                    })
2354                    .collect()
2355            } else {
2356                args.to_vec()
2357            };
2358
2359            let call = builder.ins().call(local_callee, &call_args);
2360            Ok(builder.inst_results(call)[0])
2361        } else if let Some(&func_id) = functions.get(name) {
2362            // User-defined function
2363            let local_callee = module.declare_func_in_func(func_id, builder.func);
2364            let call = builder.ins().call(local_callee, args);
2365            Ok(builder.inst_results(call)[0])
2366        } else if let Some(extern_fn) = extern_fns.get(name) {
2367            // Extern "C" function - call through FFI
2368            let local_callee = module.declare_func_in_func(extern_fn.func_id, builder.func);
2369
2370            // Convert arguments to match expected types
2371            let mut call_args = Vec::new();
2372            for (i, &arg) in args.iter().enumerate() {
2373                let arg_type = builder.func.dfg.value_type(arg);
2374                let expected_type = extern_fn.params.get(i).copied().unwrap_or(types::I64);
2375
2376                let converted = if arg_type == expected_type {
2377                    arg
2378                } else if arg_type == types::I64 && expected_type == types::I32 {
2379                    builder.ins().ireduce(types::I32, arg)
2380                } else if arg_type == types::I32 && expected_type == types::I64 {
2381                    builder.ins().sextend(types::I64, arg)
2382                } else if arg_type == types::I64 && expected_type == types::F64 {
2383                    builder.ins().fcvt_from_sint(types::F64, arg)
2384                } else if arg_type == types::F64 && expected_type == types::I64 {
2385                    builder.ins().fcvt_to_sint(types::I64, arg)
2386                } else {
2387                    arg // Best effort - let Cranelift handle it
2388                };
2389                call_args.push(converted);
2390            }
2391
2392            let call = builder.ins().call(local_callee, &call_args);
2393
2394            // Handle return value
2395            if extern_fn.returns.is_some() {
2396                let result = builder.inst_results(call)[0];
2397                let result_type = builder.func.dfg.value_type(result);
2398                // Extend smaller types to i64 for our internal representation
2399                if result_type == types::I32
2400                    || result_type == types::I16
2401                    || result_type == types::I8
2402                {
2403                    Ok(builder.ins().sextend(types::I64, result))
2404                } else {
2405                    Ok(result)
2406                }
2407            } else {
2408                // Void return - return 0
2409                Ok(builder.ins().iconst(types::I64, 0))
2410            }
2411        } else {
2412            Err(format!("Unknown function: {}", name))
2413        }
2414    }
2415
2416    /// Compile if expression, returns (value, has_return)
2417    fn compile_if_tracked(
2418        module: &mut JITModule,
2419        functions: &HashMap<String, FuncId>,
2420        extern_fns: &HashMap<String, ExternFnSig>,
2421        builder: &mut FunctionBuilder,
2422        scope: &mut CompileScope,
2423        condition: &Expr,
2424        then_branch: &ast::Block,
2425        else_branch: Option<&Expr>,
2426    ) -> Result<(cranelift_codegen::ir::Value, bool), String> {
2427        // OPTIMIZATION: Use direct condition compilation
2428        let cond_bool =
2429            compile_condition(module, functions, extern_fns, builder, scope, condition)?;
2430
2431        let then_block = builder.create_block();
2432        let else_block = builder.create_block();
2433        let merge_block = builder.create_block();
2434
2435        builder.append_block_param(merge_block, types::I64);
2436
2437        // Branch directly on the boolean - no extra comparison needed
2438        builder
2439            .ins()
2440            .brif(cond_bool, then_block, &[], else_block, &[]);
2441
2442        // Compile then branch
2443        builder.switch_to_block(then_block);
2444        builder.seal_block(then_block);
2445        let mut then_scope = scope.child();
2446        let (then_val, then_returns) = compile_block_tracked(
2447            module,
2448            functions,
2449            extern_fns,
2450            builder,
2451            &mut then_scope,
2452            then_branch,
2453        )?;
2454        // Only jump to merge if we didn't return
2455        if !then_returns {
2456            builder.ins().jump(merge_block, &[then_val]);
2457        }
2458
2459        // Compile else branch
2460        builder.switch_to_block(else_block);
2461        builder.seal_block(else_block);
2462        let (else_val, else_returns) = if let Some(else_expr) = else_branch {
2463            match else_expr {
2464                Expr::Block(block) => {
2465                    let mut else_scope = scope.child();
2466                    compile_block_tracked(
2467                        module,
2468                        functions,
2469                        extern_fns,
2470                        builder,
2471                        &mut else_scope,
2472                        block,
2473                    )?
2474                }
2475                Expr::If {
2476                    condition,
2477                    then_branch,
2478                    else_branch,
2479                } => compile_if_tracked(
2480                    module,
2481                    functions,
2482                    extern_fns,
2483                    builder,
2484                    scope,
2485                    condition,
2486                    then_branch,
2487                    else_branch.as_deref(),
2488                )?,
2489                _ => {
2490                    let val =
2491                        compile_expr(module, functions, extern_fns, builder, scope, else_expr)?;
2492                    (val, false)
2493                }
2494            }
2495        } else {
2496            (builder.ins().iconst(types::I64, 0), false)
2497        };
2498        // Only jump to merge if we didn't return
2499        if !else_returns {
2500            builder.ins().jump(merge_block, &[else_val]);
2501        }
2502
2503        // If both branches return, the merge block is unreachable but still needs to be sealed
2504        // If only some branches return, we still need the merge block
2505        let both_return = then_returns && else_returns;
2506
2507        builder.switch_to_block(merge_block);
2508        builder.seal_block(merge_block);
2509
2510        if both_return {
2511            // Both branches return - merge block is unreachable
2512            // Return a dummy value and signal that we returned
2513            let dummy = builder.ins().iconst(types::I64, 0);
2514            Ok((dummy, true))
2515        } else {
2516            Ok((builder.block_params(merge_block)[0], false))
2517        }
2518    }
2519
2520    /// Compile if expression (convenience wrapper)
2521    fn compile_if(
2522        module: &mut JITModule,
2523        functions: &HashMap<String, FuncId>,
2524        extern_fns: &HashMap<String, ExternFnSig>,
2525        builder: &mut FunctionBuilder,
2526        scope: &mut CompileScope,
2527        condition: &Expr,
2528        then_branch: &ast::Block,
2529        else_branch: Option<&Expr>,
2530    ) -> Result<cranelift_codegen::ir::Value, String> {
2531        compile_if_tracked(
2532            module,
2533            functions,
2534            extern_fns,
2535            builder,
2536            scope,
2537            condition,
2538            then_branch,
2539            else_branch,
2540        )
2541        .map(|(v, _)| v)
2542    }
2543
2544    /// Compile while loop
2545    fn compile_while(
2546        module: &mut JITModule,
2547        functions: &HashMap<String, FuncId>,
2548        extern_fns: &HashMap<String, ExternFnSig>,
2549        builder: &mut FunctionBuilder,
2550        scope: &mut CompileScope,
2551        condition: &Expr,
2552        body: &ast::Block,
2553    ) -> Result<cranelift_codegen::ir::Value, String> {
2554        let header_block = builder.create_block();
2555        let body_block = builder.create_block();
2556        let exit_block = builder.create_block();
2557
2558        builder.ins().jump(header_block, &[]);
2559
2560        builder.switch_to_block(header_block);
2561        // OPTIMIZATION: Use direct condition compilation
2562        let cond_bool =
2563            compile_condition(module, functions, extern_fns, builder, scope, condition)?;
2564        // Branch directly - no extra comparison needed
2565        builder
2566            .ins()
2567            .brif(cond_bool, body_block, &[], exit_block, &[]);
2568
2569        builder.switch_to_block(body_block);
2570        builder.seal_block(body_block);
2571        let mut body_scope = scope.child();
2572        compile_block(
2573            module,
2574            functions,
2575            extern_fns,
2576            builder,
2577            &mut body_scope,
2578            body,
2579        )?;
2580        builder.ins().jump(header_block, &[]);
2581
2582        builder.seal_block(header_block);
2583
2584        builder.switch_to_block(exit_block);
2585        builder.seal_block(exit_block);
2586
2587        Ok(builder.ins().iconst(types::I64, 0))
2588    }
2589
2590    // ============================================
2591    // Runtime support functions (called from JIT)
2592    // ============================================
2593
2594    // Type-aware arithmetic operations
2595    // Uses heuristic: if value looks like a float bit pattern, treat as float
2596    // Small integers (< 2^50) are unlikely to have float patterns
2597    #[inline]
2598    fn is_float_pattern(v: i64) -> bool {
2599        let exp = (v >> 52) & 0x7FF;
2600        // Float exponent is non-zero (except for 0.0 and denormals)
2601        // and not all 1s (infinity/NaN) - valid float range
2602        exp > 0 && exp < 0x7FF && v != 0
2603    }
2604
2605    #[no_mangle]
2606    pub extern "C" fn sigil_add(a: i64, b: i64) -> i64 {
2607        if is_float_pattern(a) || is_float_pattern(b) {
2608            let fa = f64::from_bits(a as u64);
2609            let fb = f64::from_bits(b as u64);
2610            (fa + fb).to_bits() as i64
2611        } else {
2612            a.wrapping_add(b)
2613        }
2614    }
2615
2616    #[no_mangle]
2617    pub extern "C" fn sigil_sub(a: i64, b: i64) -> i64 {
2618        if is_float_pattern(a) || is_float_pattern(b) {
2619            let fa = f64::from_bits(a as u64);
2620            let fb = f64::from_bits(b as u64);
2621            (fa - fb).to_bits() as i64
2622        } else {
2623            a.wrapping_sub(b)
2624        }
2625    }
2626
2627    #[no_mangle]
2628    pub extern "C" fn sigil_mul(a: i64, b: i64) -> i64 {
2629        if is_float_pattern(a) || is_float_pattern(b) {
2630            let fa = f64::from_bits(a as u64);
2631            let fb = f64::from_bits(b as u64);
2632            (fa * fb).to_bits() as i64
2633        } else {
2634            a.wrapping_mul(b)
2635        }
2636    }
2637
2638    #[no_mangle]
2639    pub extern "C" fn sigil_div(a: i64, b: i64) -> i64 {
2640        if is_float_pattern(a) || is_float_pattern(b) {
2641            let fa = f64::from_bits(a as u64);
2642            let fb = f64::from_bits(b as u64);
2643            (fa / fb).to_bits() as i64
2644        } else if b != 0 {
2645            a / b
2646        } else {
2647            0 // Avoid division by zero
2648        }
2649    }
2650
2651    #[no_mangle]
2652    pub extern "C" fn sigil_lt(a: i64, b: i64) -> i64 {
2653        if is_float_pattern(a) || is_float_pattern(b) {
2654            let fa = f64::from_bits(a as u64);
2655            let fb = f64::from_bits(b as u64);
2656            if fa < fb {
2657                1
2658            } else {
2659                0
2660            }
2661        } else {
2662            if a < b {
2663                1
2664            } else {
2665                0
2666            }
2667        }
2668    }
2669
2670    #[no_mangle]
2671    pub extern "C" fn sigil_le(a: i64, b: i64) -> i64 {
2672        if is_float_pattern(a) || is_float_pattern(b) {
2673            let fa = f64::from_bits(a as u64);
2674            let fb = f64::from_bits(b as u64);
2675            if fa <= fb {
2676                1
2677            } else {
2678                0
2679            }
2680        } else {
2681            if a <= b {
2682                1
2683            } else {
2684                0
2685            }
2686        }
2687    }
2688
2689    #[no_mangle]
2690    pub extern "C" fn sigil_gt(a: i64, b: i64) -> i64 {
2691        if is_float_pattern(a) || is_float_pattern(b) {
2692            let fa = f64::from_bits(a as u64);
2693            let fb = f64::from_bits(b as u64);
2694            if fa > fb {
2695                1
2696            } else {
2697                0
2698            }
2699        } else {
2700            if a > b {
2701                1
2702            } else {
2703                0
2704            }
2705        }
2706    }
2707
2708    #[no_mangle]
2709    pub extern "C" fn sigil_ge(a: i64, b: i64) -> i64 {
2710        if is_float_pattern(a) || is_float_pattern(b) {
2711            let fa = f64::from_bits(a as u64);
2712            let fb = f64::from_bits(b as u64);
2713            if fa >= fb {
2714                1
2715            } else {
2716                0
2717            }
2718        } else {
2719            if a >= b {
2720                1
2721            } else {
2722                0
2723            }
2724        }
2725    }
2726
2727    // Print that handles both int and float
2728    #[no_mangle]
2729    pub extern "C" fn sigil_print(v: i64) -> i64 {
2730        if is_float_pattern(v) {
2731            println!("{}", f64::from_bits(v as u64));
2732        } else {
2733            println!("{}", v);
2734        }
2735        0
2736    }
2737
2738    // ============================================
2739    // SIMD Operations (Vec4 = 4xf64)
2740    // ============================================
2741    // HARDWARE SIMD VECTOR OPERATIONS
2742    // ============================================
2743    // Uses AVX/SSE intrinsics when available for maximum performance.
2744    // SIMD vectors are stored as heap-allocated arrays of 4 f64 values.
2745    // On x86_64 with AVX, uses _mm256_* intrinsics for 4-wide f64 ops.
2746    // Pointer to array is stored as i64.
2747
2748    /// SIMD vector storage - 32-byte aligned for AVX
2749    #[repr(C, align(32))]
2750    struct SimdVec4 {
2751        data: [f64; 4],
2752    }
2753
2754    impl SimdVec4 {
2755        #[inline(always)]
2756        fn new(x: f64, y: f64, z: f64, w: f64) -> Box<Self> {
2757            Box::new(SimdVec4 { data: [x, y, z, w] })
2758        }
2759
2760        #[inline(always)]
2761        fn splat(v: f64) -> Box<Self> {
2762            Box::new(SimdVec4 { data: [v, v, v, v] })
2763        }
2764    }
2765
2766    /// Create a new Vec4 SIMD vector
2767    #[no_mangle]
2768    pub extern "C" fn sigil_simd_new(x: i64, y: i64, z: i64, w: i64) -> i64 {
2769        let v = SimdVec4::new(
2770            f64::from_bits(x as u64),
2771            f64::from_bits(y as u64),
2772            f64::from_bits(z as u64),
2773            f64::from_bits(w as u64),
2774        );
2775        Box::into_raw(v) as i64
2776    }
2777
2778    /// Create Vec4 by splatting a scalar to all lanes
2779    #[no_mangle]
2780    pub extern "C" fn sigil_simd_splat(v: i64) -> i64 {
2781        let f = f64::from_bits(v as u64);
2782        let v = SimdVec4::splat(f);
2783        Box::into_raw(v) as i64
2784    }
2785
2786    // AVX-optimized SIMD operations using inline assembly / intrinsics pattern
2787    // The compiler will auto-vectorize these aligned operations with -C target-cpu=native
2788
2789    /// SIMD add - uses AVX when available
2790    #[no_mangle]
2791    #[inline(never)]
2792    pub extern "C" fn sigil_simd_add(a: i64, b: i64) -> i64 {
2793        unsafe {
2794            let a = &*(a as *const SimdVec4);
2795            let b = &*(b as *const SimdVec4);
2796            // Aligned load/store enables auto-vectorization
2797            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2798            r.data[0] = a.data[0] + b.data[0];
2799            r.data[1] = a.data[1] + b.data[1];
2800            r.data[2] = a.data[2] + b.data[2];
2801            r.data[3] = a.data[3] + b.data[3];
2802            Box::into_raw(r) as i64
2803        }
2804    }
2805
2806    /// SIMD subtract
2807    #[no_mangle]
2808    #[inline(never)]
2809    pub extern "C" fn sigil_simd_sub(a: i64, b: i64) -> i64 {
2810        unsafe {
2811            let a = &*(a as *const SimdVec4);
2812            let b = &*(b as *const SimdVec4);
2813            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2814            r.data[0] = a.data[0] - b.data[0];
2815            r.data[1] = a.data[1] - b.data[1];
2816            r.data[2] = a.data[2] - b.data[2];
2817            r.data[3] = a.data[3] - b.data[3];
2818            Box::into_raw(r) as i64
2819        }
2820    }
2821
2822    /// SIMD multiply
2823    #[no_mangle]
2824    #[inline(never)]
2825    pub extern "C" fn sigil_simd_mul(a: i64, b: i64) -> i64 {
2826        unsafe {
2827            let a = &*(a as *const SimdVec4);
2828            let b = &*(b as *const SimdVec4);
2829            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2830            r.data[0] = a.data[0] * b.data[0];
2831            r.data[1] = a.data[1] * b.data[1];
2832            r.data[2] = a.data[2] * b.data[2];
2833            r.data[3] = a.data[3] * b.data[3];
2834            Box::into_raw(r) as i64
2835        }
2836    }
2837
2838    /// SIMD divide
2839    #[no_mangle]
2840    #[inline(never)]
2841    pub extern "C" fn sigil_simd_div(a: i64, b: i64) -> i64 {
2842        unsafe {
2843            let a = &*(a as *const SimdVec4);
2844            let b = &*(b as *const SimdVec4);
2845            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2846            r.data[0] = a.data[0] / b.data[0];
2847            r.data[1] = a.data[1] / b.data[1];
2848            r.data[2] = a.data[2] / b.data[2];
2849            r.data[3] = a.data[3] / b.data[3];
2850            Box::into_raw(r) as i64
2851        }
2852    }
2853
2854    /// SIMD dot product (returns scalar) - optimized for auto-vectorization
2855    #[no_mangle]
2856    #[inline(never)]
2857    pub extern "C" fn sigil_simd_dot(a: i64, b: i64) -> i64 {
2858        unsafe {
2859            let a = &*(a as *const SimdVec4);
2860            let b = &*(b as *const SimdVec4);
2861            // FMA-friendly pattern for dot product
2862            let r = a.data[0].mul_add(
2863                b.data[0],
2864                a.data[1].mul_add(
2865                    b.data[1],
2866                    a.data[2].mul_add(b.data[2], a.data[3] * b.data[3]),
2867                ),
2868            );
2869            r.to_bits() as i64
2870        }
2871    }
2872
2873    /// SIMD horizontal add (sum all lanes)
2874    #[no_mangle]
2875    #[inline(never)]
2876    pub extern "C" fn sigil_simd_hadd(a: i64) -> i64 {
2877        unsafe {
2878            let a = &*(a as *const SimdVec4);
2879            // Pairwise add pattern for better vectorization
2880            let sum01 = a.data[0] + a.data[1];
2881            let sum23 = a.data[2] + a.data[3];
2882            let r = sum01 + sum23;
2883            r.to_bits() as i64
2884        }
2885    }
2886
2887    /// SIMD length squared - uses FMA for better performance
2888    #[no_mangle]
2889    #[inline(never)]
2890    pub extern "C" fn sigil_simd_length_sq(a: i64) -> i64 {
2891        unsafe {
2892            let a = &*(a as *const SimdVec4);
2893            let r = a.data[0].mul_add(
2894                a.data[0],
2895                a.data[1].mul_add(
2896                    a.data[1],
2897                    a.data[2].mul_add(a.data[2], a.data[3] * a.data[3]),
2898                ),
2899            );
2900            r.to_bits() as i64
2901        }
2902    }
2903
2904    /// SIMD length - uses FMA for length calculation
2905    #[no_mangle]
2906    #[inline(never)]
2907    pub extern "C" fn sigil_simd_length(a: i64) -> i64 {
2908        unsafe {
2909            let a = &*(a as *const SimdVec4);
2910            let len_sq = a.data[0].mul_add(
2911                a.data[0],
2912                a.data[1].mul_add(
2913                    a.data[1],
2914                    a.data[2].mul_add(a.data[2], a.data[3] * a.data[3]),
2915                ),
2916            );
2917            let r = len_sq.sqrt();
2918            r.to_bits() as i64
2919        }
2920    }
2921
2922    /// SIMD normalize - fast reciprocal sqrt pattern
2923    #[no_mangle]
2924    #[inline(never)]
2925    pub extern "C" fn sigil_simd_normalize(a: i64) -> i64 {
2926        unsafe {
2927            let a = &*(a as *const SimdVec4);
2928            let len_sq = a.data[0].mul_add(
2929                a.data[0],
2930                a.data[1].mul_add(
2931                    a.data[1],
2932                    a.data[2].mul_add(a.data[2], a.data[3] * a.data[3]),
2933                ),
2934            );
2935            let inv = if len_sq > 1e-20 {
2936                1.0 / len_sq.sqrt()
2937            } else {
2938                0.0
2939            };
2940            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2941            r.data[0] = a.data[0] * inv;
2942            r.data[1] = a.data[1] * inv;
2943            r.data[2] = a.data[2] * inv;
2944            r.data[3] = a.data[3] * inv;
2945            Box::into_raw(r) as i64
2946        }
2947    }
2948
2949    /// SIMD cross product (3D, ignores w component)
2950    #[no_mangle]
2951    #[inline(never)]
2952    pub extern "C" fn sigil_simd_cross(a: i64, b: i64) -> i64 {
2953        unsafe {
2954            let a = &*(a as *const SimdVec4);
2955            let b = &*(b as *const SimdVec4);
2956            // Cross product using FMA where beneficial
2957            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2958            r.data[0] = a.data[1].mul_add(b.data[2], -(a.data[2] * b.data[1]));
2959            r.data[1] = a.data[2].mul_add(b.data[0], -(a.data[0] * b.data[2]));
2960            r.data[2] = a.data[0].mul_add(b.data[1], -(a.data[1] * b.data[0]));
2961            r.data[3] = 0.0;
2962            Box::into_raw(r) as i64
2963        }
2964    }
2965
2966    /// SIMD min - element-wise minimum
2967    #[no_mangle]
2968    #[inline(never)]
2969    pub extern "C" fn sigil_simd_min(a: i64, b: i64) -> i64 {
2970        unsafe {
2971            let a = &*(a as *const SimdVec4);
2972            let b = &*(b as *const SimdVec4);
2973            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2974            r.data[0] = a.data[0].min(b.data[0]);
2975            r.data[1] = a.data[1].min(b.data[1]);
2976            r.data[2] = a.data[2].min(b.data[2]);
2977            r.data[3] = a.data[3].min(b.data[3]);
2978            Box::into_raw(r) as i64
2979        }
2980    }
2981
2982    /// SIMD max - element-wise maximum
2983    #[no_mangle]
2984    #[inline(never)]
2985    pub extern "C" fn sigil_simd_max(a: i64, b: i64) -> i64 {
2986        unsafe {
2987            let a = &*(a as *const SimdVec4);
2988            let b = &*(b as *const SimdVec4);
2989            let mut r = SimdVec4::new(0.0, 0.0, 0.0, 0.0);
2990            r.data[0] = a.data[0].max(b.data[0]);
2991            r.data[1] = a.data[1].max(b.data[1]);
2992            r.data[2] = a.data[2].max(b.data[2]);
2993            r.data[3] = a.data[3].max(b.data[3]);
2994            Box::into_raw(r) as i64
2995        }
2996    }
2997
2998    /// Extract element from SIMD vector
2999    #[no_mangle]
3000    pub extern "C" fn sigil_simd_extract(v: i64, idx: i64) -> i64 {
3001        unsafe {
3002            let v = &*(v as *const SimdVec4);
3003            let r = v.data[(idx as usize) & 3];
3004            r.to_bits() as i64
3005        }
3006    }
3007
3008    /// Free SIMD vector (for memory management)
3009    #[no_mangle]
3010    pub extern "C" fn sigil_simd_free(v: i64) {
3011        if v != 0 {
3012            unsafe {
3013                let _ = Box::from_raw(v as *mut SimdVec4);
3014            }
3015        }
3016    }
3017
3018    #[no_mangle]
3019    pub extern "C" fn sigil_sqrt(x: f64) -> f64 {
3020        x.sqrt()
3021    }
3022
3023    #[no_mangle]
3024    pub extern "C" fn sigil_sin(x: f64) -> f64 {
3025        x.sin()
3026    }
3027
3028    #[no_mangle]
3029    pub extern "C" fn sigil_cos(x: f64) -> f64 {
3030        x.cos()
3031    }
3032
3033    #[no_mangle]
3034    pub extern "C" fn sigil_pow(base: f64, exp: f64) -> f64 {
3035        base.powf(exp)
3036    }
3037
3038    #[no_mangle]
3039    pub extern "C" fn sigil_exp(x: f64) -> f64 {
3040        x.exp()
3041    }
3042
3043    #[no_mangle]
3044    pub extern "C" fn sigil_ln(x: f64) -> f64 {
3045        x.ln()
3046    }
3047
3048    #[no_mangle]
3049    pub extern "C" fn sigil_floor(x: f64) -> f64 {
3050        x.floor()
3051    }
3052
3053    #[no_mangle]
3054    pub extern "C" fn sigil_ceil(x: f64) -> f64 {
3055        x.ceil()
3056    }
3057
3058    #[no_mangle]
3059    pub extern "C" fn sigil_abs(x: f64) -> f64 {
3060        x.abs()
3061    }
3062
3063    #[no_mangle]
3064    pub extern "C" fn sigil_print_int(x: i64) -> i64 {
3065        println!("{}", x);
3066        0
3067    }
3068
3069    #[no_mangle]
3070    pub extern "C" fn sigil_print_float(x: f64) -> i64 {
3071        println!("{}", x);
3072        0
3073    }
3074
3075    #[no_mangle]
3076    pub extern "C" fn sigil_print_str(ptr: *const u8, len: usize) -> i64 {
3077        unsafe {
3078            let slice = std::slice::from_raw_parts(ptr, len);
3079            if let Ok(s) = std::str::from_utf8(slice) {
3080                println!("{}", s);
3081            }
3082        }
3083        0
3084    }
3085
3086    #[no_mangle]
3087    pub extern "C" fn sigil_now() -> i64 {
3088        use std::time::{SystemTime, UNIX_EPOCH};
3089        SystemTime::now()
3090            .duration_since(UNIX_EPOCH)
3091            .map(|d| d.as_millis() as i64)
3092            .unwrap_or(0)
3093    }
3094
3095    // Simple array implementation using heap allocation
3096    #[repr(C)]
3097    struct SigilArray {
3098        data: *mut i64,
3099        len: usize,
3100        cap: usize,
3101    }
3102
3103    #[no_mangle]
3104    pub extern "C" fn sigil_array_new(capacity: i64) -> i64 {
3105        let cap = capacity.max(8) as usize;
3106        let layout = std::alloc::Layout::array::<i64>(cap).unwrap();
3107        let data = unsafe { std::alloc::alloc(layout) as *mut i64 };
3108
3109        let arr = Box::new(SigilArray { data, len: 0, cap });
3110        Box::into_raw(arr) as i64
3111    }
3112
3113    #[no_mangle]
3114    pub extern "C" fn sigil_array_push(arr_ptr: i64, value: i64) -> i64 {
3115        unsafe {
3116            let arr = &mut *(arr_ptr as *mut SigilArray);
3117            if arr.len >= arr.cap {
3118                // Grow array
3119                let new_cap = arr.cap * 2;
3120                let old_layout = std::alloc::Layout::array::<i64>(arr.cap).unwrap();
3121                let new_layout = std::alloc::Layout::array::<i64>(new_cap).unwrap();
3122                arr.data = std::alloc::realloc(arr.data as *mut u8, old_layout, new_layout.size())
3123                    as *mut i64;
3124                arr.cap = new_cap;
3125            }
3126            *arr.data.add(arr.len) = value;
3127            arr.len += 1;
3128        }
3129        0
3130    }
3131
3132    #[no_mangle]
3133    pub extern "C" fn sigil_array_get(arr_ptr: i64, index: i64) -> i64 {
3134        unsafe {
3135            let arr = &*(arr_ptr as *const SigilArray);
3136            let idx = index as usize;
3137            if idx < arr.len {
3138                *arr.data.add(idx)
3139            } else {
3140                0 // Out of bounds returns 0
3141            }
3142        }
3143    }
3144
3145    #[no_mangle]
3146    pub extern "C" fn sigil_array_set(arr_ptr: i64, index: i64, value: i64) -> i64 {
3147        unsafe {
3148            let arr = &mut *(arr_ptr as *mut SigilArray);
3149            let idx = index as usize;
3150            // Extend array if needed
3151            while arr.len <= idx {
3152                sigil_array_push(arr_ptr, 0);
3153            }
3154            *arr.data.add(idx) = value;
3155        }
3156        value
3157    }
3158
3159    #[no_mangle]
3160    pub extern "C" fn sigil_array_len(arr_ptr: i64) -> i64 {
3161        unsafe {
3162            let arr = &*(arr_ptr as *const SigilArray);
3163            arr.len as i64
3164        }
3165    }
3166
3167    // ============================================
3168    // SIMD-Optimized Array Operations
3169    // ============================================
3170    // These operations process arrays in SIMD-friendly batches
3171
3172    /// Sum all elements in an array using SIMD-friendly loop
3173    #[no_mangle]
3174    pub extern "C" fn sigil_array_sum(arr_ptr: i64) -> i64 {
3175        unsafe {
3176            let arr = &*(arr_ptr as *const SigilArray);
3177            let data = std::slice::from_raw_parts(arr.data, arr.len);
3178
3179            // Process in batches of 4 for SIMD-friendliness
3180            let chunks = data.chunks_exact(4);
3181            let remainder = chunks.remainder();
3182
3183            // Accumulate 4 partial sums (allows SIMD vectorization)
3184            let mut sum0: i64 = 0;
3185            let mut sum1: i64 = 0;
3186            let mut sum2: i64 = 0;
3187            let mut sum3: i64 = 0;
3188
3189            for chunk in chunks {
3190                sum0 = sum0.wrapping_add(chunk[0]);
3191                sum1 = sum1.wrapping_add(chunk[1]);
3192                sum2 = sum2.wrapping_add(chunk[2]);
3193                sum3 = sum3.wrapping_add(chunk[3]);
3194            }
3195
3196            // Add remainder
3197            let mut sum = sum0
3198                .wrapping_add(sum1)
3199                .wrapping_add(sum2)
3200                .wrapping_add(sum3);
3201            for &v in remainder {
3202                sum = sum.wrapping_add(v);
3203            }
3204
3205            sum
3206        }
3207    }
3208
3209    /// Multiply all elements by a scalar (in-place, SIMD-friendly)
3210    #[no_mangle]
3211    pub extern "C" fn sigil_array_scale(arr_ptr: i64, scalar: i64) -> i64 {
3212        unsafe {
3213            let arr = &mut *(arr_ptr as *mut SigilArray);
3214            let data = std::slice::from_raw_parts_mut(arr.data, arr.len);
3215
3216            // Process in batches of 4 for SIMD-friendliness
3217            for chunk in data.chunks_exact_mut(4) {
3218                chunk[0] = chunk[0].wrapping_mul(scalar);
3219                chunk[1] = chunk[1].wrapping_mul(scalar);
3220                chunk[2] = chunk[2].wrapping_mul(scalar);
3221                chunk[3] = chunk[3].wrapping_mul(scalar);
3222            }
3223
3224            // Handle remainder
3225            let remainder_start = (data.len() / 4) * 4;
3226            for v in &mut data[remainder_start..] {
3227                *v = v.wrapping_mul(scalar);
3228            }
3229
3230            arr_ptr
3231        }
3232    }
3233
3234    /// Add a scalar to all elements (in-place, SIMD-friendly)
3235    #[no_mangle]
3236    pub extern "C" fn sigil_array_offset(arr_ptr: i64, offset: i64) -> i64 {
3237        unsafe {
3238            let arr = &mut *(arr_ptr as *mut SigilArray);
3239            let data = std::slice::from_raw_parts_mut(arr.data, arr.len);
3240
3241            // Process in batches of 4 for SIMD-friendliness
3242            for chunk in data.chunks_exact_mut(4) {
3243                chunk[0] = chunk[0].wrapping_add(offset);
3244                chunk[1] = chunk[1].wrapping_add(offset);
3245                chunk[2] = chunk[2].wrapping_add(offset);
3246                chunk[3] = chunk[3].wrapping_add(offset);
3247            }
3248
3249            let remainder_start = (data.len() / 4) * 4;
3250            for v in &mut data[remainder_start..] {
3251                *v = v.wrapping_add(offset);
3252            }
3253
3254            arr_ptr
3255        }
3256    }
3257
3258    /// Dot product of two arrays (SIMD-friendly)
3259    #[no_mangle]
3260    pub extern "C" fn sigil_array_dot(a_ptr: i64, b_ptr: i64) -> i64 {
3261        unsafe {
3262            let a_arr = &*(a_ptr as *const SigilArray);
3263            let b_arr = &*(b_ptr as *const SigilArray);
3264
3265            let len = a_arr.len.min(b_arr.len);
3266            let a_data = std::slice::from_raw_parts(a_arr.data, len);
3267            let b_data = std::slice::from_raw_parts(b_arr.data, len);
3268
3269            // Process in batches of 4 for SIMD-friendliness
3270            let mut sum0: i64 = 0;
3271            let mut sum1: i64 = 0;
3272            let mut sum2: i64 = 0;
3273            let mut sum3: i64 = 0;
3274
3275            let chunks = len / 4;
3276            for i in 0..chunks {
3277                let base = i * 4;
3278                sum0 = sum0.wrapping_add(a_data[base].wrapping_mul(b_data[base]));
3279                sum1 = sum1.wrapping_add(a_data[base + 1].wrapping_mul(b_data[base + 1]));
3280                sum2 = sum2.wrapping_add(a_data[base + 2].wrapping_mul(b_data[base + 2]));
3281                sum3 = sum3.wrapping_add(a_data[base + 3].wrapping_mul(b_data[base + 3]));
3282            }
3283
3284            // Add remainder
3285            let mut sum = sum0
3286                .wrapping_add(sum1)
3287                .wrapping_add(sum2)
3288                .wrapping_add(sum3);
3289            for i in (chunks * 4)..len {
3290                sum = sum.wrapping_add(a_data[i].wrapping_mul(b_data[i]));
3291            }
3292
3293            sum
3294        }
3295    }
3296
3297    /// Element-wise add two arrays into a new array (SIMD-friendly)
3298    #[no_mangle]
3299    pub extern "C" fn sigil_array_add(a_ptr: i64, b_ptr: i64) -> i64 {
3300        unsafe {
3301            let a_arr = &*(a_ptr as *const SigilArray);
3302            let b_arr = &*(b_ptr as *const SigilArray);
3303
3304            let len = a_arr.len.min(b_arr.len);
3305            let a_data = std::slice::from_raw_parts(a_arr.data, len);
3306            let b_data = std::slice::from_raw_parts(b_arr.data, len);
3307
3308            // Create result array
3309            let result = sigil_array_new(len as i64);
3310            let r_arr = &mut *(result as *mut SigilArray);
3311            r_arr.len = len;
3312            let r_data = std::slice::from_raw_parts_mut(r_arr.data, len);
3313
3314            // Process in batches of 4 for SIMD-friendliness
3315            for i in 0..(len / 4) {
3316                let base = i * 4;
3317                r_data[base] = a_data[base].wrapping_add(b_data[base]);
3318                r_data[base + 1] = a_data[base + 1].wrapping_add(b_data[base + 1]);
3319                r_data[base + 2] = a_data[base + 2].wrapping_add(b_data[base + 2]);
3320                r_data[base + 3] = a_data[base + 3].wrapping_add(b_data[base + 3]);
3321            }
3322
3323            // Handle remainder
3324            for i in ((len / 4) * 4)..len {
3325                r_data[i] = a_data[i].wrapping_add(b_data[i]);
3326            }
3327
3328            result
3329        }
3330    }
3331
3332    /// Element-wise multiply two arrays into a new array (SIMD-friendly)
3333    #[no_mangle]
3334    pub extern "C" fn sigil_array_mul(a_ptr: i64, b_ptr: i64) -> i64 {
3335        unsafe {
3336            let a_arr = &*(a_ptr as *const SigilArray);
3337            let b_arr = &*(b_ptr as *const SigilArray);
3338
3339            let len = a_arr.len.min(b_arr.len);
3340            let a_data = std::slice::from_raw_parts(a_arr.data, len);
3341            let b_data = std::slice::from_raw_parts(b_arr.data, len);
3342
3343            // Create result array
3344            let result = sigil_array_new(len as i64);
3345            let r_arr = &mut *(result as *mut SigilArray);
3346            r_arr.len = len;
3347            let r_data = std::slice::from_raw_parts_mut(r_arr.data, len);
3348
3349            // Process in batches of 4 for SIMD-friendliness
3350            for i in 0..(len / 4) {
3351                let base = i * 4;
3352                r_data[base] = a_data[base].wrapping_mul(b_data[base]);
3353                r_data[base + 1] = a_data[base + 1].wrapping_mul(b_data[base + 1]);
3354                r_data[base + 2] = a_data[base + 2].wrapping_mul(b_data[base + 2]);
3355                r_data[base + 3] = a_data[base + 3].wrapping_mul(b_data[base + 3]);
3356            }
3357
3358            // Handle remainder
3359            for i in ((len / 4) * 4)..len {
3360                r_data[i] = a_data[i].wrapping_mul(b_data[i]);
3361            }
3362
3363            result
3364        }
3365    }
3366
3367    /// Find minimum value in array (SIMD-friendly)
3368    #[no_mangle]
3369    pub extern "C" fn sigil_array_min(arr_ptr: i64) -> i64 {
3370        unsafe {
3371            let arr = &*(arr_ptr as *const SigilArray);
3372            if arr.len == 0 {
3373                return 0;
3374            }
3375
3376            let data = std::slice::from_raw_parts(arr.data, arr.len);
3377
3378            // Process in batches of 4
3379            let mut min0 = i64::MAX;
3380            let mut min1 = i64::MAX;
3381            let mut min2 = i64::MAX;
3382            let mut min3 = i64::MAX;
3383
3384            for chunk in data.chunks_exact(4) {
3385                min0 = min0.min(chunk[0]);
3386                min1 = min1.min(chunk[1]);
3387                min2 = min2.min(chunk[2]);
3388                min3 = min3.min(chunk[3]);
3389            }
3390
3391            let mut min_val = min0.min(min1).min(min2).min(min3);
3392
3393            // Handle remainder
3394            let remainder_start = (data.len() / 4) * 4;
3395            for &v in &data[remainder_start..] {
3396                min_val = min_val.min(v);
3397            }
3398
3399            min_val
3400        }
3401    }
3402
3403    /// Find maximum value in array (SIMD-friendly)
3404    #[no_mangle]
3405    pub extern "C" fn sigil_array_max(arr_ptr: i64) -> i64 {
3406        unsafe {
3407            let arr = &*(arr_ptr as *const SigilArray);
3408            if arr.len == 0 {
3409                return 0;
3410            }
3411
3412            let data = std::slice::from_raw_parts(arr.data, arr.len);
3413
3414            // Process in batches of 4
3415            let mut max0 = i64::MIN;
3416            let mut max1 = i64::MIN;
3417            let mut max2 = i64::MIN;
3418            let mut max3 = i64::MIN;
3419
3420            for chunk in data.chunks_exact(4) {
3421                max0 = max0.max(chunk[0]);
3422                max1 = max1.max(chunk[1]);
3423                max2 = max2.max(chunk[2]);
3424                max3 = max3.max(chunk[3]);
3425            }
3426
3427            let mut max_val = max0.max(max1).max(max2).max(max3);
3428
3429            // Handle remainder
3430            let remainder_start = (data.len() / 4) * 4;
3431            for &v in &data[remainder_start..] {
3432                max_val = max_val.max(v);
3433            }
3434
3435            max_val
3436        }
3437    }
3438
3439    /// Fill array with a value (SIMD-friendly)
3440    #[no_mangle]
3441    pub extern "C" fn sigil_array_fill(arr_ptr: i64, value: i64, count: i64) -> i64 {
3442        unsafe {
3443            let arr = &mut *(arr_ptr as *mut SigilArray);
3444            let n = count as usize;
3445
3446            // Ensure capacity
3447            while arr.len < n {
3448                sigil_array_push(arr_ptr, 0);
3449            }
3450
3451            let data = std::slice::from_raw_parts_mut(arr.data, n);
3452
3453            // Process in batches of 4
3454            for chunk in data.chunks_exact_mut(4) {
3455                chunk[0] = value;
3456                chunk[1] = value;
3457                chunk[2] = value;
3458                chunk[3] = value;
3459            }
3460
3461            // Handle remainder
3462            let remainder_start = (n / 4) * 4;
3463            for v in &mut data[remainder_start..] {
3464                *v = value;
3465            }
3466
3467            arr_ptr
3468        }
3469    }
3470
3471    // ============================================
3472    // PipeOp Array Access Functions
3473    // ============================================
3474    // Functions for the access morphemes: α (first), ω (last), μ (middle), χ (choice), ν (nth), ξ (next)
3475
3476    /// Get first element of array (α morpheme)
3477    #[no_mangle]
3478    pub extern "C" fn sigil_array_first(arr_ptr: i64) -> i64 {
3479        unsafe {
3480            let arr = &*(arr_ptr as *const SigilArray);
3481            if arr.len == 0 {
3482                return 0; // Return 0 for empty array
3483            }
3484            *arr.data
3485        }
3486    }
3487
3488    /// Get last element of array (ω morpheme)
3489    #[no_mangle]
3490    pub extern "C" fn sigil_array_last(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.add(arr.len - 1)
3497        }
3498    }
3499
3500    /// Get middle element of array (μ morpheme)
3501    #[no_mangle]
3502    pub extern "C" fn sigil_array_middle(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            let mid = arr.len / 2;
3509            *arr.data.add(mid)
3510        }
3511    }
3512
3513    /// Get random element of array (χ morpheme)
3514    #[no_mangle]
3515    pub extern "C" fn sigil_array_choice(arr_ptr: i64) -> i64 {
3516        unsafe {
3517            let arr = &*(arr_ptr as *const SigilArray);
3518            if arr.len == 0 {
3519                return 0; // Return 0 for empty array
3520            }
3521            // Simple LCG-based random using time as seed
3522            use std::time::{SystemTime, UNIX_EPOCH};
3523            let seed = SystemTime::now()
3524                .duration_since(UNIX_EPOCH)
3525                .map(|d| d.as_nanos() as u64)
3526                .unwrap_or(12345);
3527            let idx =
3528                ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % arr.len;
3529            *arr.data.add(idx)
3530        }
3531    }
3532
3533    /// Get nth element of array (ν morpheme) - same as sigil_array_get but clearer semantics
3534    #[no_mangle]
3535    pub extern "C" fn sigil_array_nth(arr_ptr: i64, index: i64) -> i64 {
3536        sigil_array_get(arr_ptr, index)
3537    }
3538
3539    /// Get next element (iterator advance) - currently returns first element (ξ morpheme)
3540    #[no_mangle]
3541    pub extern "C" fn sigil_array_next(arr_ptr: i64) -> i64 {
3542        // For now, next returns the first element
3543        // A full iterator implementation would track state
3544        sigil_array_first(arr_ptr)
3545    }
3546
3547    /// Product of all elements in array (Π morpheme)
3548    #[no_mangle]
3549    pub extern "C" fn sigil_array_product(arr_ptr: i64) -> i64 {
3550        unsafe {
3551            let arr = &*(arr_ptr as *const SigilArray);
3552            if arr.len == 0 {
3553                return 1; // Product of empty set is 1 (identity)
3554            }
3555            let mut product: i64 = 1;
3556            for i in 0..arr.len {
3557                product = product.wrapping_mul(*arr.data.add(i));
3558            }
3559            product
3560        }
3561    }
3562
3563    /// Sort array in ascending order (σ morpheme) - returns new sorted array
3564    #[no_mangle]
3565    pub extern "C" fn sigil_array_sort(arr_ptr: i64) -> i64 {
3566        unsafe {
3567            let arr = &*(arr_ptr as *const SigilArray);
3568            if arr.len == 0 {
3569                return sigil_array_new(0);
3570            }
3571
3572            // Copy elements to a Vec for sorting
3573            let mut elements: Vec<i64> = Vec::with_capacity(arr.len);
3574            for i in 0..arr.len {
3575                elements.push(*arr.data.add(i));
3576            }
3577
3578            // Sort ascending
3579            elements.sort();
3580
3581            // Create new array with sorted elements
3582            let new_arr = sigil_array_new(arr.len as i64);
3583            for elem in elements {
3584                sigil_array_push(new_arr, elem);
3585            }
3586            new_arr
3587        }
3588    }
3589
3590    // ============================================
3591    // Parallel Execution Functions (∥ morpheme)
3592    // ============================================
3593    // These provide multi-threaded execution of array operations
3594    // For JIT compilation, these use a simple thread pool approach
3595
3596    /// Parallel map operation - applies a transformation in parallel across array elements
3597    /// For now, returns the array unchanged as full closure parallelization
3598    /// requires more complex infrastructure. In production, this would:
3599    /// 1. Partition array into chunks based on available CPU cores
3600    /// 2. Spawn worker threads for each chunk
3601    /// 3. Apply transform closure in parallel
3602    /// 4. Collect results
3603    #[no_mangle]
3604    pub extern "C" fn sigil_parallel_map(arr_ptr: i64) -> i64 {
3605        // Stub: returns array unchanged
3606        // Full implementation would use rayon::par_iter or manual thread pool
3607        arr_ptr
3608    }
3609
3610    /// Parallel filter operation - filters elements in parallel
3611    /// Uses parallel predicate evaluation with stream compaction
3612    #[no_mangle]
3613    pub extern "C" fn sigil_parallel_filter(arr_ptr: i64) -> i64 {
3614        // Stub: returns array unchanged
3615        // Full implementation would:
3616        // 1. Evaluate predicates in parallel
3617        // 2. Use prefix sum for compaction offsets
3618        // 3. Parallel write to output array
3619        arr_ptr
3620    }
3621
3622    /// Parallel reduce operation - tree reduction for associative operations
3623    /// Achieves O(log n) depth with O(n) work
3624    #[no_mangle]
3625    pub extern "C" fn sigil_parallel_reduce(arr_ptr: i64) -> i64 {
3626        // For reduction, we can implement a parallel tree reduction
3627        // Falls back to sequential sum for now
3628        unsafe {
3629            let arr = &*(arr_ptr as *const SigilArray);
3630            if arr.len == 0 {
3631                return 0;
3632            }
3633
3634            // Simple sequential sum - parallel tree reduction would
3635            // use divide-and-conquer with thread spawning
3636            let mut sum: i64 = 0;
3637            for i in 0..arr.len {
3638                sum += *arr.data.add(i);
3639            }
3640            sum
3641        }
3642    }
3643
3644    // ============================================
3645    // GPU Compute Functions (⊛ morpheme)
3646    // ============================================
3647    // These would dispatch operations to GPU via wgpu/vulkan
3648    // Currently stubs that fall back to CPU execution
3649
3650    /// GPU map operation - would compile to WGSL/SPIR-V compute shader
3651    /// Shader structure:
3652    /// ```wgsl
3653    /// @compute @workgroup_size(256)
3654    /// fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3655    ///     let idx = id.x;
3656    ///     output[idx] = transform(input[idx]);
3657    /// }
3658    /// ```
3659    #[no_mangle]
3660    pub extern "C" fn sigil_gpu_map(arr_ptr: i64) -> i64 {
3661        // Stub: returns array unchanged
3662        // Full implementation would:
3663        // 1. Upload array to GPU buffer
3664        // 2. Compile transform to SPIR-V
3665        // 3. Dispatch compute shader
3666        // 4. Download results
3667        arr_ptr
3668    }
3669
3670    /// GPU filter operation with parallel stream compaction
3671    /// Uses scan-based compaction algorithm
3672    #[no_mangle]
3673    pub extern "C" fn sigil_gpu_filter(arr_ptr: i64) -> i64 {
3674        // Stub: returns array unchanged
3675        // Full implementation would use prefix sum for compaction
3676        arr_ptr
3677    }
3678
3679    /// GPU reduce operation - uses tree reduction in shared memory
3680    /// Achieves O(log n) parallel steps
3681    #[no_mangle]
3682    pub extern "C" fn sigil_gpu_reduce(arr_ptr: i64) -> i64 {
3683        // Falls back to CPU reduction
3684        sigil_parallel_reduce(arr_ptr)
3685    }
3686
3687    // ============================================
3688    // Memoization Cache for Recursive Functions
3689    // ============================================
3690    // Uses a simple hash table with linear probing for O(1) average lookup
3691
3692    /// Memoization cache entry
3693    #[repr(C)]
3694    struct MemoEntry {
3695        key1: i64,      // First argument (or hash of multiple args)
3696        key2: i64,      // Second argument (for 2-arg functions)
3697        value: i64,     // Cached result
3698        occupied: bool, // Whether this slot is used
3699    }
3700
3701    /// Memoization cache (fixed-size hash table)
3702    #[repr(C)]
3703    struct MemoCache {
3704        entries: *mut MemoEntry,
3705        capacity: usize,
3706        mask: usize, // capacity - 1, for fast modulo
3707    }
3708
3709    /// Create a new memoization cache
3710    #[no_mangle]
3711    pub extern "C" fn sigil_memo_new(capacity: i64) -> i64 {
3712        let cap = (capacity as usize).next_power_of_two().max(1024);
3713        let layout = std::alloc::Layout::array::<MemoEntry>(cap).unwrap();
3714        let entries = unsafe {
3715            let ptr = std::alloc::alloc_zeroed(layout) as *mut MemoEntry;
3716            ptr
3717        };
3718
3719        let cache = Box::new(MemoCache {
3720            entries,
3721            capacity: cap,
3722            mask: cap - 1,
3723        });
3724        Box::into_raw(cache) as i64
3725    }
3726
3727    /// Hash function for single argument
3728    #[inline]
3729    fn memo_hash_1(key: i64) -> usize {
3730        // FNV-1a inspired hash
3731        let mut h = key as u64;
3732        h = h.wrapping_mul(0x517cc1b727220a95);
3733        h ^= h >> 32;
3734        h as usize
3735    }
3736
3737    /// Hash function for two arguments
3738    #[inline]
3739    fn memo_hash_2(key1: i64, key2: i64) -> usize {
3740        let mut h = key1 as u64;
3741        h = h.wrapping_mul(0x517cc1b727220a95);
3742        h ^= key2 as u64;
3743        h = h.wrapping_mul(0x517cc1b727220a95);
3744        h ^= h >> 32;
3745        h as usize
3746    }
3747
3748    // ============================================
3749    // Optimized Recursive Algorithm Implementations
3750    // ============================================
3751    // These iterative implementations are much faster than recursive versions
3752
3753    /// Iterative Ackermann function using explicit stack
3754    /// Much faster than recursive version - no stack overflow, O(result) space
3755    #[no_mangle]
3756    pub extern "C" fn sigil_ackermann(m: i64, n: i64) -> i64 {
3757        // Use an explicit stack to simulate recursion
3758        let mut stack: Vec<i64> = Vec::with_capacity(1024);
3759        stack.push(m);
3760        let mut n = n;
3761
3762        while let Some(m) = stack.pop() {
3763            if m == 0 {
3764                n = n + 1;
3765            } else if n == 0 {
3766                stack.push(m - 1);
3767                n = 1;
3768            } else {
3769                stack.push(m - 1);
3770                stack.push(m);
3771                n = n - 1;
3772            }
3773        }
3774        n
3775    }
3776
3777    /// Iterative Tak (Takeuchi) function using explicit stack
3778    #[no_mangle]
3779    pub extern "C" fn sigil_tak(x: i64, y: i64, z: i64) -> i64 {
3780        // Use continuation-passing style with explicit stack
3781        #[derive(Clone, Copy)]
3782        enum TakCont {
3783            Eval { x: i64, y: i64, z: i64 },
3784            Cont1 { y: i64, z: i64, x: i64 }, // waiting for tak(x-1,y,z), need y,z,x for later
3785            Cont2 { z: i64, x: i64, y: i64, r1: i64 }, // waiting for tak(y-1,z,x), have r1
3786            Cont3 { r1: i64, r2: i64 },       // waiting for tak(z-1,x,y), have r1,r2
3787        }
3788
3789        let mut stack: Vec<TakCont> = Vec::with_capacity(256);
3790        stack.push(TakCont::Eval { x, y, z });
3791        let mut result: i64 = 0;
3792
3793        while let Some(cont) = stack.pop() {
3794            match cont {
3795                TakCont::Eval { x, y, z } => {
3796                    if y >= x {
3797                        result = z;
3798                    } else {
3799                        // Need to compute tak(tak(x-1,y,z), tak(y-1,z,x), tak(z-1,x,y))
3800                        stack.push(TakCont::Cont1 { y, z, x });
3801                        stack.push(TakCont::Eval { x: x - 1, y, z });
3802                    }
3803                }
3804                TakCont::Cont1 { y, z, x } => {
3805                    let r1 = result;
3806                    stack.push(TakCont::Cont2 { z, x, y, r1 });
3807                    stack.push(TakCont::Eval {
3808                        x: y - 1,
3809                        y: z,
3810                        z: x,
3811                    });
3812                }
3813                TakCont::Cont2 { z, x, y, r1 } => {
3814                    let r2 = result;
3815                    stack.push(TakCont::Cont3 { r1, r2 });
3816                    stack.push(TakCont::Eval {
3817                        x: z - 1,
3818                        y: x,
3819                        z: y,
3820                    });
3821                }
3822                TakCont::Cont3 { r1, r2 } => {
3823                    let r3 = result;
3824                    // Now compute tak(r1, r2, r3)
3825                    stack.push(TakCont::Eval {
3826                        x: r1,
3827                        y: r2,
3828                        z: r3,
3829                    });
3830                }
3831            }
3832        }
3833        result
3834    }
3835
3836    /// Sentinel value for "not found" in memo cache
3837    /// Using i64::MIN + 1 to avoid parser issues with the full MIN value
3838    const MEMO_NOT_FOUND: i64 = -9223372036854775807;
3839
3840    /// Lookup a single-argument function result in cache
3841    /// Returns the cached value, or MEMO_NOT_FOUND if not found
3842    #[no_mangle]
3843    pub extern "C" fn sigil_memo_get_1(cache_ptr: i64, key: i64) -> i64 {
3844        unsafe {
3845            let cache = &*(cache_ptr as *const MemoCache);
3846            let mut idx = memo_hash_1(key) & cache.mask;
3847
3848            // Linear probing with limited search
3849            for _ in 0..32 {
3850                let entry = &*cache.entries.add(idx);
3851                if !entry.occupied {
3852                    return MEMO_NOT_FOUND;
3853                }
3854                if entry.key1 == key {
3855                    return entry.value;
3856                }
3857                idx = (idx + 1) & cache.mask;
3858            }
3859            MEMO_NOT_FOUND
3860        }
3861    }
3862
3863    /// Store a single-argument function result in cache
3864    #[no_mangle]
3865    pub extern "C" fn sigil_memo_set_1(cache_ptr: i64, key: i64, value: i64) {
3866        unsafe {
3867            let cache = &*(cache_ptr as *const MemoCache);
3868            let mut idx = memo_hash_1(key) & cache.mask;
3869
3870            // Linear probing
3871            for _ in 0..32 {
3872                let entry = &mut *cache.entries.add(idx);
3873                if !entry.occupied || entry.key1 == key {
3874                    entry.key1 = key;
3875                    entry.value = value;
3876                    entry.occupied = true;
3877                    return;
3878                }
3879                idx = (idx + 1) & cache.mask;
3880            }
3881            // Cache full at this location, overwrite first slot
3882            let entry = &mut *cache.entries.add(memo_hash_1(key) & cache.mask);
3883            entry.key1 = key;
3884            entry.value = value;
3885            entry.occupied = true;
3886        }
3887    }
3888
3889    /// Lookup a two-argument function result in cache
3890    #[no_mangle]
3891    pub extern "C" fn sigil_memo_get_2(cache_ptr: i64, key1: i64, key2: i64) -> i64 {
3892        unsafe {
3893            let cache = &*(cache_ptr as *const MemoCache);
3894            let mut idx = memo_hash_2(key1, key2) & cache.mask;
3895
3896            for _ in 0..32 {
3897                let entry = &*cache.entries.add(idx);
3898                if !entry.occupied {
3899                    return MEMO_NOT_FOUND;
3900                }
3901                if entry.key1 == key1 && entry.key2 == key2 {
3902                    return entry.value;
3903                }
3904                idx = (idx + 1) & cache.mask;
3905            }
3906            MEMO_NOT_FOUND
3907        }
3908    }
3909
3910    /// Store a two-argument function result in cache
3911    #[no_mangle]
3912    pub extern "C" fn sigil_memo_set_2(cache_ptr: i64, key1: i64, key2: i64, value: i64) {
3913        unsafe {
3914            let cache = &*(cache_ptr as *const MemoCache);
3915            let mut idx = memo_hash_2(key1, key2) & cache.mask;
3916
3917            for _ in 0..32 {
3918                let entry = &mut *cache.entries.add(idx);
3919                if !entry.occupied || (entry.key1 == key1 && entry.key2 == key2) {
3920                    entry.key1 = key1;
3921                    entry.key2 = key2;
3922                    entry.value = value;
3923                    entry.occupied = true;
3924                    return;
3925                }
3926                idx = (idx + 1) & cache.mask;
3927            }
3928            let entry = &mut *cache.entries.add(memo_hash_2(key1, key2) & cache.mask);
3929            entry.key1 = key1;
3930            entry.key2 = key2;
3931            entry.value = value;
3932            entry.occupied = true;
3933        }
3934    }
3935
3936    /// Free a memoization cache
3937    #[no_mangle]
3938    pub extern "C" fn sigil_memo_free(cache_ptr: i64) {
3939        if cache_ptr != 0 {
3940            unsafe {
3941                let cache = Box::from_raw(cache_ptr as *mut MemoCache);
3942                let layout = std::alloc::Layout::array::<MemoEntry>(cache.capacity).unwrap();
3943                std::alloc::dealloc(cache.entries as *mut u8, layout);
3944            }
3945        }
3946    }
3947
3948    // ============================================
3949    // FFI Tests
3950    // ============================================
3951
3952    #[cfg(test)]
3953    mod tests {
3954        use super::*;
3955        use crate::parser::Parser;
3956
3957        #[test]
3958        fn test_extern_block_parsing_and_declaration() {
3959            let source = r#"
3960                extern "C" {
3961                    rite abs(x: c_int) -> c_int;
3962                    rite strlen(s: *const c_char) -> usize;
3963                }
3964
3965                rite main() -> i64 {
3966                    42
3967                }
3968            "#;
3969
3970            let mut compiler = JitCompiler::new().unwrap();
3971            let result = compiler.compile(source);
3972            assert!(
3973                result.is_ok(),
3974                "Failed to compile FFI declarations: {:?}",
3975                result
3976            );
3977
3978            // Check that extern functions were registered
3979            assert!(
3980                compiler.extern_functions.contains_key("abs"),
3981                "abs not declared"
3982            );
3983            assert!(
3984                compiler.extern_functions.contains_key("strlen"),
3985                "strlen not declared"
3986            );
3987
3988            // Check abs signature
3989            let abs_sig = compiler.extern_functions.get("abs").unwrap();
3990            assert_eq!(abs_sig.params.len(), 1);
3991            assert_eq!(abs_sig.params[0], types::I32); // c_int -> i32
3992            assert_eq!(abs_sig.returns, Some(types::I32));
3993
3994            // Check strlen signature
3995            let strlen_sig = compiler.extern_functions.get("strlen").unwrap();
3996            assert_eq!(strlen_sig.params.len(), 1);
3997            assert_eq!(strlen_sig.params[0], types::I64); // pointer -> i64
3998            assert_eq!(strlen_sig.returns, Some(types::I64)); // usize -> i64
3999        }
4000
4001        #[test]
4002        fn test_extern_variadic_function() {
4003            let source = r#"
4004                extern "C" {
4005                    rite printf(fmt: *const c_char, ...) -> c_int;
4006                }
4007
4008                rite main() -> i64 {
4009                    0
4010                }
4011            "#;
4012
4013            let mut compiler = JitCompiler::new().unwrap();
4014            let result = compiler.compile(source);
4015            assert!(
4016                result.is_ok(),
4017                "Failed to compile variadic FFI: {:?}",
4018                result
4019            );
4020
4021            let printf_sig = compiler.extern_functions.get("printf").unwrap();
4022            assert!(printf_sig.variadic, "printf should be variadic");
4023        }
4024
4025        #[test]
4026        fn test_extern_c_abi_only() {
4027            let source = r#"
4028                extern "Rust" {
4029                    rite some_func(x: i32) -> i32;
4030                }
4031
4032                rite main() -> i64 {
4033                    0
4034                }
4035            "#;
4036
4037            let mut compiler = JitCompiler::new().unwrap();
4038            let result = compiler.compile(source);
4039            assert!(result.is_err(), "Should reject non-C ABI");
4040            assert!(result.unwrap_err().contains("Unsupported ABI"));
4041        }
4042
4043        #[test]
4044        fn test_c_type_mapping() {
4045            // Test that C types are correctly mapped to Cranelift types
4046            let test_cases = vec![
4047                ("c_char", types::I8),
4048                ("c_int", types::I32),
4049                ("c_long", types::I64),
4050                ("c_float", types::F32),
4051                ("c_double", types::F64),
4052                ("size_t", types::I64),
4053                ("i32", types::I32),
4054                ("f64", types::F64),
4055            ];
4056
4057            for (type_name, expected_cl_type) in test_cases {
4058                let source = format!(
4059                    r#"
4060                    extern "C" {{
4061                        rite test_func(x: {}) -> {};
4062                    }}
4063
4064                    rite main() -> i64 {{ 0 }}
4065                "#,
4066                    type_name, type_name
4067                );
4068
4069                let mut compiler = JitCompiler::new().unwrap();
4070                let result = compiler.compile(&source);
4071                assert!(
4072                    result.is_ok(),
4073                    "Failed for type {}: {:?}",
4074                    type_name,
4075                    result
4076                );
4077
4078                let sig = compiler.extern_functions.get("test_func").unwrap();
4079                assert_eq!(
4080                    sig.params[0], expected_cl_type,
4081                    "Wrong param type for {}",
4082                    type_name
4083                );
4084                assert_eq!(
4085                    sig.returns,
4086                    Some(expected_cl_type),
4087                    "Wrong return type for {}",
4088                    type_name
4089                );
4090            }
4091        }
4092    }
4093}
4094
4095// Re-export for convenience
4096#[cfg(feature = "jit")]
4097pub use jit::JitCompiler;