@@ -429,6 +429,31 @@ impl FactStyleGenerator {
429429 }
430430 }
431431
432+ // Resolve resource handles inside the params-ptr buffer.
433+ // For borrow<T> where callee defines T, the adapter must convert
434+ // handle → rep at the byte offset within the buffer.
435+ for op in & site. requirements . params_area_resource_positions {
436+ if op. is_owned {
437+ continue ; // own<T>: callee calls resource.rep internally
438+ }
439+
440+ if op. callee_defines_resource {
441+ // 2-component: use callee's [resource-rep]
442+ if let Some ( & rep_func) =
443+ resource_rep_imports. get ( & ( op. import_module . clone ( ) , op. import_field . clone ( ) ) )
444+ {
445+ options
446+ . params_area_borrow_fixups
447+ . push ( super :: ParamsAreaResourceFixup {
448+ byte_offset : op. byte_offset ,
449+ rep_func,
450+ is_owned : false ,
451+ } ) ;
452+ }
453+ }
454+ // 3-component chains for params-area borrows could be added here
455+ }
456+
432457 options
433458 }
434459
@@ -617,6 +642,29 @@ impl FactStyleGenerator {
617642 ) ;
618643 }
619644
645+ // --- Detect params-ptr calling convention ---
646+ // The canonical ABI uses params-ptr when flat params > MAX_FLAT_PARAMS (16):
647+ // caller (lowered): (params_ptr: i32) → result...
648+ // callee (lifted): (params_ptr: i32) → result...
649+ // Both sides use a single i32 pointer to a buffer in linear memory.
650+ // When memories differ, the adapter must copy the buffer across.
651+ let uses_params_ptr = site. requirements . params_area_byte_size . is_some ( ) ;
652+
653+ if uses_params_ptr && options. caller_memory != options. callee_memory {
654+ log:: debug!(
655+ "params-ptr adapter: generating for import={} (buffer={}B, {} ptr pairs, {} borrow fixups)" ,
656+ site. import_name,
657+ site. requirements. params_area_byte_size. unwrap_or( 0 ) ,
658+ site. requirements. params_area_pointer_pair_offsets. len( ) ,
659+ site. requirements
660+ . params_area_resource_positions
661+ . iter( )
662+ . filter( |p| !p. is_owned && p. callee_defines_resource)
663+ . count( ) ,
664+ ) ;
665+ return self . generate_params_ptr_adapter ( site, options, target_func, caller_type_idx) ;
666+ }
667+
620668 // --- Non-retptr path: use caller's type for declared signature ---
621669 let adapter_type_idx = caller_type_idx;
622670 let param_count = callee_param_count;
@@ -1155,6 +1203,295 @@ impl FactStyleGenerator {
11551203 Ok ( ( adapter_type_idx, func) )
11561204 }
11571205
1206+ /// Generate an adapter for the params-ptr calling convention.
1207+ ///
1208+ /// When flat param count > MAX_FLAT_PARAMS (16), the canonical ABI stores all
1209+ /// params in a buffer in linear memory. Both caller and callee use:
1210+ /// (params_ptr: i32) → result...
1211+ ///
1212+ /// The adapter bridges different memories:
1213+ /// 1. Allocate buffer in callee's memory via cabi_realloc
1214+ /// 2. Bulk copy the params buffer from caller to callee memory
1215+ /// 3. Fix up any (ptr, len) pairs inside the buffer — copy pointed-to data
1216+ /// from caller memory to callee memory and update the pointers
1217+ /// 4. Call callee with new pointer
1218+ /// 5. Return the result(s)
1219+ fn generate_params_ptr_adapter (
1220+ & self ,
1221+ site : & AdapterSite ,
1222+ options : & AdapterOptions ,
1223+ target_func : u32 ,
1224+ caller_type_idx : u32 ,
1225+ ) -> Result < ( u32 , Function ) > {
1226+ let params_area_size = site. requirements . params_area_byte_size . unwrap_or ( 0 ) ;
1227+ let params_area_align = site. requirements . params_area_max_align . max ( 1 ) ;
1228+ let ptr_pair_offsets = & site. requirements . params_area_pointer_pair_offsets ;
1229+ let copy_layouts = & site. requirements . params_area_copy_layouts ;
1230+
1231+ let callee_realloc = options. callee_realloc . unwrap_or_else ( || {
1232+ log:: warn!( "params-ptr adapter: no callee realloc, buffer copy may fail" ) ;
1233+ 0
1234+ } ) ;
1235+
1236+ // Check if any list copy layouts contain inner resources (borrow handles)
1237+ let has_inner_resources = copy_layouts. iter ( ) . any ( |cl| {
1238+ matches ! ( cl,
1239+ crate :: resolver:: CopyLayout :: Elements { inner_resources, .. }
1240+ if !inner_resources. is_empty( )
1241+ )
1242+ } ) ;
1243+
1244+ // Local layout:
1245+ // 0: params_ptr (the function parameter — pointer to caller's memory)
1246+ // 1: callee_ptr (allocated pointer in callee's memory)
1247+ // 2..2+N: dest_ptr for each pointer pair copy
1248+ // 2+N: loop_counter (if inner resources need fixup)
1249+ let num_ptr_pairs = ptr_pair_offsets. len ( ) as u32 ;
1250+ let loop_counter_count = if has_inner_resources { 1u32 } else { 0 } ;
1251+ let scratch_count = 1 + num_ptr_pairs + loop_counter_count; // callee_ptr + per-pair dest ptrs + loop counter
1252+
1253+ // Post-return needs result save locals
1254+ let has_post_return = options. callee_post_return . is_some ( ) ;
1255+ // For params-ptr, the results come from the callee directly.
1256+
1257+ let mut local_decls: Vec < ( u32 , wasm_encoder:: ValType ) > = Vec :: new ( ) ;
1258+ if scratch_count > 0 {
1259+ local_decls. push ( ( scratch_count, wasm_encoder:: ValType :: I32 ) ) ;
1260+ }
1261+
1262+ // We don't know result count from here, so we handle post-return simply:
1263+ // if there's a post-return, we'll save and restore results.
1264+ // But for params-ptr functions with resource results, result count should be 1 (i32).
1265+ // For simplicity: if has_post_return, add 1 i32 result save local.
1266+ let result_save_base = 1 + scratch_count; // after params_ptr(0) + scratch
1267+ if has_post_return {
1268+ local_decls. push ( ( 1 , wasm_encoder:: ValType :: I32 ) ) ;
1269+ }
1270+
1271+ let mut func = Function :: new ( local_decls) ;
1272+
1273+ let params_ptr_local: u32 = 0 ;
1274+ let callee_ptr_local: u32 = 1 ;
1275+ let pair_dest_base: u32 = 2 ;
1276+
1277+ // --- Phase 1: Allocate buffer in callee's memory ---
1278+ // callee_ptr = cabi_realloc(0, 0, align, size)
1279+ func. instruction ( & Instruction :: I32Const ( 0 ) ) ; // original_ptr
1280+ func. instruction ( & Instruction :: I32Const ( 0 ) ) ; // original_size
1281+ func. instruction ( & Instruction :: I32Const ( params_area_align as i32 ) ) ; // alignment
1282+ func. instruction ( & Instruction :: I32Const ( params_area_size as i32 ) ) ; // new_size
1283+ func. instruction ( & Instruction :: Call ( callee_realloc) ) ;
1284+ func. instruction ( & Instruction :: LocalSet ( callee_ptr_local) ) ;
1285+
1286+ // --- Phase 2: Bulk copy the entire params buffer ---
1287+ // memory.copy $callee_mem $caller_mem (callee_ptr, params_ptr, size)
1288+ func. instruction ( & Instruction :: LocalGet ( callee_ptr_local) ) ; // dst
1289+ func. instruction ( & Instruction :: LocalGet ( params_ptr_local) ) ; // src
1290+ func. instruction ( & Instruction :: I32Const ( params_area_size as i32 ) ) ; // size
1291+ func. instruction ( & Instruction :: MemoryCopy {
1292+ src_mem : options. caller_memory ,
1293+ dst_mem : options. callee_memory ,
1294+ } ) ;
1295+
1296+ // --- Phase 3: Fix up pointer pairs inside the buffer ---
1297+ // For each (ptr, len) pair in the params buffer:
1298+ // 1. Read ptr and len from callee's copy of the buffer
1299+ // 2. Compute byte_size from len and the copy layout's byte_multiplier
1300+ // 3. Allocate in callee's memory: new_ptr = cabi_realloc(0, 0, 1, byte_size)
1301+ // 4. Copy data from caller's memory at old_ptr to callee's memory at new_ptr
1302+ // 5. Write new_ptr back into callee's buffer at the same offset
1303+ for ( pair_idx, & byte_offset) in ptr_pair_offsets. iter ( ) . enumerate ( ) {
1304+ let dest_local = pair_dest_base + pair_idx as u32 ;
1305+ let byte_mult = copy_layouts
1306+ . get ( pair_idx)
1307+ . map ( |cl| match cl {
1308+ crate :: resolver:: CopyLayout :: Bulk { byte_multiplier } => * byte_multiplier,
1309+ crate :: resolver:: CopyLayout :: Elements { element_size, .. } => * element_size,
1310+ } )
1311+ . unwrap_or ( 1 ) ;
1312+
1313+ // Read old_ptr from callee's buffer: i32.load callee_mem (callee_ptr + byte_offset)
1314+ // Read old_len from callee's buffer: i32.load callee_mem (callee_ptr + byte_offset + 4)
1315+
1316+ // Allocate: new_ptr = cabi_realloc(0, 0, 1, len * byte_mult)
1317+ func. instruction ( & Instruction :: I32Const ( 0 ) ) ;
1318+ func. instruction ( & Instruction :: I32Const ( 0 ) ) ;
1319+ func. instruction ( & Instruction :: I32Const ( 1 ) ) ;
1320+ // Load len from callee's buffer
1321+ func. instruction ( & Instruction :: LocalGet ( callee_ptr_local) ) ;
1322+ func. instruction ( & Instruction :: I32Load ( wasm_encoder:: MemArg {
1323+ offset : ( byte_offset + 4 ) as u64 ,
1324+ align : 2 ,
1325+ memory_index : options. callee_memory ,
1326+ } ) ) ;
1327+ if byte_mult > 1 {
1328+ func. instruction ( & Instruction :: I32Const ( byte_mult as i32 ) ) ;
1329+ func. instruction ( & Instruction :: I32Mul ) ;
1330+ }
1331+ func. instruction ( & Instruction :: Call ( callee_realloc) ) ;
1332+ func. instruction ( & Instruction :: LocalSet ( dest_local) ) ;
1333+
1334+ // Copy data: memory.copy callee caller (new_ptr, old_ptr, len * byte_mult)
1335+ func. instruction ( & Instruction :: LocalGet ( dest_local) ) ; // dst (in callee mem)
1336+ // Load old_ptr from callee's buffer (this was copied from caller's buffer,
1337+ // so it points into caller's memory)
1338+ func. instruction ( & Instruction :: LocalGet ( callee_ptr_local) ) ;
1339+ func. instruction ( & Instruction :: I32Load ( wasm_encoder:: MemArg {
1340+ offset : byte_offset as u64 ,
1341+ align : 2 ,
1342+ memory_index : options. callee_memory ,
1343+ } ) ) ; // src (in caller mem)
1344+ // Load len from callee's buffer
1345+ func. instruction ( & Instruction :: LocalGet ( callee_ptr_local) ) ;
1346+ func. instruction ( & Instruction :: I32Load ( wasm_encoder:: MemArg {
1347+ offset : ( byte_offset + 4 ) as u64 ,
1348+ align : 2 ,
1349+ memory_index : options. callee_memory ,
1350+ } ) ) ;
1351+ if byte_mult > 1 {
1352+ func. instruction ( & Instruction :: I32Const ( byte_mult as i32 ) ) ;
1353+ func. instruction ( & Instruction :: I32Mul ) ;
1354+ }
1355+ func. instruction ( & Instruction :: MemoryCopy {
1356+ src_mem : options. caller_memory ,
1357+ dst_mem : options. callee_memory ,
1358+ } ) ;
1359+
1360+ // Write new_ptr back into callee's buffer at byte_offset
1361+ func. instruction ( & Instruction :: LocalGet ( callee_ptr_local) ) ;
1362+ func. instruction ( & Instruction :: LocalGet ( dest_local) ) ;
1363+ func. instruction ( & Instruction :: I32Store ( wasm_encoder:: MemArg {
1364+ offset : byte_offset as u64 ,
1365+ align : 2 ,
1366+ memory_index : options. callee_memory ,
1367+ } ) ) ;
1368+
1369+ // Fix up inner resource handles in list elements.
1370+ // After bulk copy, borrow handles in the list data still reference
1371+ // the caller's resource table. Convert each borrow handle → rep.
1372+ if let Some ( crate :: resolver:: CopyLayout :: Elements {
1373+ element_size,
1374+ inner_resources,
1375+ ..
1376+ } ) = copy_layouts. get ( pair_idx)
1377+ && !inner_resources. is_empty ( )
1378+ {
1379+ let element_size = * element_size;
1380+ let loop_local = pair_dest_base + num_ptr_pairs;
1381+
1382+ // Initialize loop counter to 0
1383+ func. instruction ( & Instruction :: I32Const ( 0 ) ) ;
1384+ func. instruction ( & Instruction :: LocalSet ( loop_local) ) ;
1385+
1386+ // block $exit { loop $cont {
1387+ func. instruction ( & Instruction :: Block ( wasm_encoder:: BlockType :: Empty ) ) ;
1388+ func. instruction ( & Instruction :: Loop ( wasm_encoder:: BlockType :: Empty ) ) ;
1389+
1390+ // if loop_counter >= len: break
1391+ func. instruction ( & Instruction :: LocalGet ( loop_local) ) ;
1392+ // Load len from callee's buffer
1393+ func. instruction ( & Instruction :: LocalGet ( callee_ptr_local) ) ;
1394+ func. instruction ( & Instruction :: I32Load ( wasm_encoder:: MemArg {
1395+ offset : ( byte_offset + 4 ) as u64 ,
1396+ align : 2 ,
1397+ memory_index : options. callee_memory ,
1398+ } ) ) ;
1399+ func. instruction ( & Instruction :: I32GeU ) ;
1400+ func. instruction ( & Instruction :: BrIf ( 1 ) ) ; // break to $exit
1401+
1402+ for & ( res_byte_offset, _resource_type_id, is_owned) in inner_resources {
1403+ if is_owned {
1404+ continue ; // own<T>: callee handles internally
1405+ }
1406+ // Find [resource-rep] for this resource
1407+ if let Some ( & rep_func) = options
1408+ . params_area_borrow_fixups
1409+ . first ( )
1410+ . map ( |f| & f. rep_func )
1411+ . or_else ( || options. resource_rep_calls . first ( ) . map ( |t| & t. rep_func ) )
1412+ {
1413+ // addr = dest_ptr + loop_counter * element_size + res_byte_offset
1414+ // Push addr for store
1415+ func. instruction ( & Instruction :: LocalGet ( dest_local) ) ;
1416+ func. instruction ( & Instruction :: LocalGet ( loop_local) ) ;
1417+ func. instruction ( & Instruction :: I32Const ( element_size as i32 ) ) ;
1418+ func. instruction ( & Instruction :: I32Mul ) ;
1419+ func. instruction ( & Instruction :: I32Add ) ;
1420+ // Load handle from same addr + offset
1421+ func. instruction ( & Instruction :: LocalGet ( dest_local) ) ;
1422+ func. instruction ( & Instruction :: LocalGet ( loop_local) ) ;
1423+ func. instruction ( & Instruction :: I32Const ( element_size as i32 ) ) ;
1424+ func. instruction ( & Instruction :: I32Mul ) ;
1425+ func. instruction ( & Instruction :: I32Add ) ;
1426+ func. instruction ( & Instruction :: I32Load ( wasm_encoder:: MemArg {
1427+ offset : res_byte_offset as u64 ,
1428+ align : 2 ,
1429+ memory_index : options. callee_memory ,
1430+ } ) ) ;
1431+ // Call [resource-rep](handle) → rep
1432+ func. instruction ( & Instruction :: Call ( rep_func) ) ;
1433+ // Store rep back
1434+ func. instruction ( & Instruction :: I32Store ( wasm_encoder:: MemArg {
1435+ offset : res_byte_offset as u64 ,
1436+ align : 2 ,
1437+ memory_index : options. callee_memory ,
1438+ } ) ) ;
1439+ }
1440+ }
1441+
1442+ // loop_counter++
1443+ func. instruction ( & Instruction :: LocalGet ( loop_local) ) ;
1444+ func. instruction ( & Instruction :: I32Const ( 1 ) ) ;
1445+ func. instruction ( & Instruction :: I32Add ) ;
1446+ func. instruction ( & Instruction :: LocalSet ( loop_local) ) ;
1447+ func. instruction ( & Instruction :: Br ( 0 ) ) ; // continue to $cont
1448+ func. instruction ( & Instruction :: End ) ; // end loop
1449+ func. instruction ( & Instruction :: End ) ; // end block
1450+ }
1451+ }
1452+
1453+ // --- Phase 3.5: Convert borrow resource handles inside the buffer ---
1454+ // For borrow<T> where callee defines T, the adapter must convert
1455+ // handle → rep by calling [resource-rep] and writing the rep back.
1456+ for fixup in & options. params_area_borrow_fixups {
1457+ // Stack: callee_ptr (for i32.store dest)
1458+ func. instruction ( & Instruction :: LocalGet ( callee_ptr_local) ) ;
1459+ // Load handle from callee's buffer at byte_offset
1460+ func. instruction ( & Instruction :: LocalGet ( callee_ptr_local) ) ;
1461+ func. instruction ( & Instruction :: I32Load ( wasm_encoder:: MemArg {
1462+ offset : fixup. byte_offset as u64 ,
1463+ align : 2 ,
1464+ memory_index : options. callee_memory ,
1465+ } ) ) ;
1466+ // Call [resource-rep](handle) → rep
1467+ func. instruction ( & Instruction :: Call ( fixup. rep_func ) ) ;
1468+ // Store rep back at the same offset
1469+ func. instruction ( & Instruction :: I32Store ( wasm_encoder:: MemArg {
1470+ offset : fixup. byte_offset as u64 ,
1471+ align : 2 ,
1472+ memory_index : options. callee_memory ,
1473+ } ) ) ;
1474+ }
1475+
1476+ // --- Phase 4: Call callee with the new pointer ---
1477+ func. instruction ( & Instruction :: LocalGet ( callee_ptr_local) ) ;
1478+ func. instruction ( & Instruction :: Call ( target_func) ) ;
1479+
1480+ // --- Phase 5: Handle post-return if needed ---
1481+ if has_post_return {
1482+ // Save result (assume i32)
1483+ func. instruction ( & Instruction :: LocalSet ( result_save_base) ) ;
1484+ // Call post-return (no args for params-ptr convention post-return)
1485+ func. instruction ( & Instruction :: Call ( options. callee_post_return . unwrap ( ) ) ) ;
1486+ // Push result back
1487+ func. instruction ( & Instruction :: LocalGet ( result_save_base) ) ;
1488+ }
1489+
1490+ func. instruction ( & Instruction :: End ) ;
1491+
1492+ Ok ( ( caller_type_idx, func) )
1493+ }
1494+
11581495 /// Generate an adapter for the retptr calling convention.
11591496 ///
11601497 /// In the canonical ABI, when a function returns heap-allocated types
0 commit comments