@@ -206,32 +206,56 @@ impl JIT {
206206 // Get the block parameters (function arguments).
207207 let block_params: Vec < _ > = builder. block_params ( entry_block) . to_vec ( ) ;
208208
209+ // Track current parameter index.
210+ let mut param_idx = 0 ;
211+
209212 // If this function has globals, the first block param is the globals base pointer.
210- let ( globals_base, param_offset) = if has_globals {
211- ( Some ( block_params[ 0 ] ) , 1 )
213+ let globals_base = if has_globals {
214+ let base = block_params[ param_idx] ;
215+ param_idx += 1 ;
216+ Some ( base)
217+ } else {
218+ None
219+ } ;
220+
221+ // If return type is a pointer type, first param (after globals) is output pointer.
222+ let output_ptr = if returns_via_pointer ( decl. ret ) {
223+ let ptr = block_params[ param_idx] ;
224+ param_idx += 1 ;
225+ Some ( ptr)
212226 } else {
213- ( None , 0 )
227+ None
214228 } ;
215229
216- let mut trans = FunctionTranslator :: new ( builder, & mut self . module , & self . globals , globals_base) ;
230+ let mut trans = FunctionTranslator :: new ( builder, & mut self . module , & self . globals , globals_base, output_ptr ) ;
217231
218232 // Add variables for the function parameters and define them with block param values.
219233 for ( i, param) in decl. params . iter ( ) . enumerate ( ) {
220234 let ty = param. ty . expect ( "expected type" ) . cranelift_type ( ) ;
221235 let var = trans. declare_variable ( & param. name , ty) ;
222- trans. builder . def_var ( var, block_params[ i + param_offset ] ) ;
236+ trans. builder . def_var ( var, block_params[ i + param_idx ] ) ;
223237 // Function parameters are like let bindings - they hold values directly.
224238 trans. let_bindings . insert ( param. name . to_string ( ) ) ;
225239 }
226240
227241 let result = trans. translate_fn ( decl, decls) ;
228242
229243 // Need a return instruction at the end of the function's block.
230- // If the function returns void, return nothing; otherwise return the body's result.
231- if * decl. ret == crate :: Type :: Void {
232- trans. builder . ins ( ) . return_ ( & [ ] ) ;
233- } else {
234- trans. builder . ins ( ) . return_ ( & [ result] ) ;
244+ // Skip if the block is already unreachable (e.g., body ended with explicit return).
245+ if !trans. builder . is_unreachable ( ) {
246+ if returns_via_pointer ( decl. ret ) {
247+ // Copy result to output pointer and return void.
248+ let output = trans. output_ptr . unwrap ( ) ;
249+ let size = decl. ret . size ( decls) as i64 ;
250+ let size_val = trans. builder . ins ( ) . iconst ( I64 , size) ;
251+ // Use memcpy to copy the data.
252+ trans. builder . call_memcpy ( trans. module . target_config ( ) , output, result, size_val) ;
253+ trans. builder . ins ( ) . return_ ( & [ ] ) ;
254+ } else if * decl. ret == crate :: Type :: Void {
255+ trans. builder . ins ( ) . return_ ( & [ ] ) ;
256+ } else {
257+ trans. builder . ins ( ) . return_ ( & [ result] ) ;
258+ }
235259 }
236260
237261 // Indicate we're finished with the function.
@@ -292,18 +316,29 @@ impl crate::Type {
292316 }
293317}
294318
319+ /// Returns true if this type is returned via an output pointer parameter.
320+ fn returns_via_pointer ( ty : crate :: TypeID ) -> bool {
321+ ty. is_ptr ( )
322+ }
323+
295324fn fn_sig ( module : & JITModule , from : crate :: TypeID , to : crate :: TypeID ) -> Signature {
296325 let mut sig = module. make_signature ( ) ;
326+
327+ // If return type is a pointer type (array, struct, tuple), add output pointer as first param.
328+ if returns_via_pointer ( to) {
329+ sig. params . push ( AbiParam :: new ( I64 ) ) ;
330+ }
331+
297332 if let crate :: Type :: Tuple ( args) = & * from {
298- sig. params = args
299- . iter ( )
300- . map ( |t| AbiParam :: new ( t. cranelift_type ( ) ) )
301- . collect ( ) ;
333+ for t in args. iter ( ) {
334+ sig. params . push ( AbiParam :: new ( t. cranelift_type ( ) ) ) ;
335+ }
302336 } else {
303337 panic ! ( ) ;
304338 }
305339
306- if * to != crate :: Type :: Void {
340+ // Only add return value if not void and not returned via pointer.
341+ if * to != crate :: Type :: Void && !returns_via_pointer ( to) {
307342 sig. returns = vec ! [ AbiParam :: new( to. cranelift_type( ) ) ] ;
308343 }
309344 sig
@@ -331,10 +366,19 @@ struct FunctionTranslator<'a> {
331366
332367 /// Base pointer for globals (passed as first param to main).
333368 globals_base : Option < Value > ,
369+
370+ /// Output pointer for functions returning via pointer (arrays, structs, tuples).
371+ output_ptr : Option < Value > ,
334372}
335373
336374impl < ' a > FunctionTranslator < ' a > {
337- fn new ( builder : FunctionBuilder < ' a > , module : & ' a mut JITModule , globals : & ' a HashMap < Name , i32 > , globals_base : Option < Value > ) -> Self {
375+ fn new (
376+ builder : FunctionBuilder < ' a > ,
377+ module : & ' a mut JITModule ,
378+ globals : & ' a HashMap < Name , i32 > ,
379+ globals_base : Option < Value > ,
380+ output_ptr : Option < Value > ,
381+ ) -> Self {
338382 Self {
339383 builder,
340384 variables : HashMap :: new ( ) ,
@@ -345,6 +389,7 @@ impl<'a> FunctionTranslator<'a> {
345389 let_bindings : HashSet :: new ( ) ,
346390 globals,
347391 globals_base,
392+ output_ptr,
348393 }
349394 }
350395
@@ -438,16 +483,37 @@ impl<'a> FunctionTranslator<'a> {
438483 Expr :: Call ( fn_id, arg_ids) => {
439484 let f = self . translate_expr ( * fn_id, decl, decls) ;
440485
441- let mut args = vec ! [ ] ;
442- for arg_id in arg_ids {
443- args. push ( self . translate_expr ( * arg_id, decl, decls) )
444- }
445-
446486 if let crate :: Type :: Func ( from, to) = * ( decl. types [ * fn_id] ) {
487+ let mut args = vec ! [ ] ;
488+
489+ // If return type is pointer, allocate stack space and pass as first arg.
490+ let output_slot = if returns_via_pointer ( to) {
491+ let size = to. size ( decls) as u32 ;
492+ let slot = self . builder . create_sized_stack_slot ( StackSlotData {
493+ kind : StackSlotKind :: ExplicitSlot ,
494+ size,
495+ align_shift : 0 ,
496+ key : None ,
497+ } ) ;
498+ let addr = self . builder . ins ( ) . stack_addr ( I64 , slot, 0 ) ;
499+ args. push ( addr) ;
500+ Some ( addr)
501+ } else {
502+ None
503+ } ;
504+
505+ for arg_id in arg_ids {
506+ args. push ( self . translate_expr ( * arg_id, decl, decls) )
507+ }
508+
447509 let sig = fn_sig ( & self . module , from, to) ;
448510 let sref = self . builder . import_signature ( sig) ;
449511 let call = self . builder . ins ( ) . call_indirect ( sref, f, & args) ;
450- if let Some ( result) = self . builder . inst_results ( call) . first ( ) {
512+
513+ if let Some ( addr) = output_slot {
514+ // Return the address of the output buffer.
515+ addr
516+ } else if let Some ( result) = self . builder . inst_results ( call) . first ( ) {
451517 * result
452518 } else {
453519 self . builder . ins ( ) . iconst ( I32 , 0 )
@@ -538,18 +604,17 @@ impl<'a> FunctionTranslator<'a> {
538604 . iter ( )
539605 . map ( |e| self . translate_expr ( * e, decl, decls) )
540606 . collect ( ) ;
541-
542- let ty = decl. types [ expr] ;
543607
544- if let crate :: Type :: Array ( ty , _ ) = & * ty {
608+ let ty = decl . types [ expr ] ;
545609
546- let sz = ty. size ( decls) as u32 ;
547- let element_size = sz / elements. len ( ) as u32 ;
610+ if let crate :: Type :: Array ( elem_ty, _) = & * ty {
611+ let element_size = elem_ty. size ( decls) as u32 ;
612+ let total_size = element_size * elements. len ( ) as u32 ;
548613
549- // Allocate a new stack slot with a size of the variable .
614+ // Allocate a new stack slot with a size of the array .
550615 let slot = self . builder . create_sized_stack_slot ( StackSlotData {
551616 kind : StackSlotKind :: ExplicitSlot ,
552- size : sz ,
617+ size : total_size ,
553618 align_shift : 0 ,
554619 key : None ,
555620 } ) ;
@@ -662,13 +727,28 @@ impl<'a> FunctionTranslator<'a> {
662727 }
663728 Expr :: Return ( expr_id) => {
664729 let result = self . translate_expr ( * expr_id, decl, decls) ;
665- self . builder . ins ( ) . return_ ( & [ result] ) ;
730+ let ret_ty = decl. types [ * expr_id] ;
731+
732+ if returns_via_pointer ( ret_ty) {
733+ // Copy result to output pointer and return void.
734+ let output = self . output_ptr . expect ( "output_ptr not set for pointer return" ) ;
735+ let size = ret_ty. size ( decls) as i64 ;
736+ let size_val = self . builder . ins ( ) . iconst ( I64 , size) ;
737+ self . builder . call_memcpy ( self . module . target_config ( ) , output, result, size_val) ;
738+ self . builder . ins ( ) . return_ ( & [ ] ) ;
739+ } else {
740+ self . builder . ins ( ) . return_ ( & [ result] ) ;
741+ }
742+
666743 // Create an unreachable block for any code after return.
667744 let unreachable_block = self . builder . create_block ( ) ;
668745 self . builder . switch_to_block ( unreachable_block) ;
669746 self . builder . seal_block ( unreachable_block) ;
670- // Return a dummy value - this code is unreachable.
671- self . builder . ins ( ) . iconst ( I32 , 0 )
747+ // Create a dummy value before adding trap (trap terminates the block).
748+ let dummy = self . builder . ins ( ) . iconst ( I32 , 0 ) ;
749+ // Add a trap - this code is unreachable.
750+ self . builder . ins ( ) . trap ( TrapCode :: user ( 1 ) . unwrap ( ) ) ;
751+ dummy
672752 }
673753 Expr :: While ( cond_id, body_id) => {
674754 // Create blocks for header, body, and exit.
0 commit comments