Skip to content

Commit b80b41a

Browse files
committed
Fix map adapter trampoline compilation and alignment bugs
The translate_map function had two categories of bugs preventing map adapter trampolines from working: 1. Wasm stack discipline: local_set_new_tmp emits LocalSet which pops from the stack, but was called when the stack was empty (to "pre-allocate" locals). Fixed by computing values first, then calling local_set_new_tmp to consume them—matching translate_list's pattern. Also removed an erroneous LocalTee that left an orphan value on the stack. Affected: src_byte_len, dst_byte_len, cur_src_ptr, cur_dst_ptr. 2. Pointer advancement: after value translation, the pointer still points at the value start. The code only advanced by trailing padding instead of value_size + trailing_padding, causing every loop iteration to re-read the same memory. Also fixes entry layout to use proper record alignment rules (entry align = max(key_align, value_align), value at aligned offset).
1 parent a40bb64 commit b80b41a

2 files changed

Lines changed: 584 additions & 104 deletions

File tree

crates/environ/src/fact/trampoline.rs

Lines changed: 60 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -2699,8 +2699,6 @@ impl<'a, 'b> Compiler<'a, 'b> {
26992699
dst_ty: &InterfaceType,
27002700
dst: &Destination,
27012701
) {
2702-
// Extract memory configuration for source and destination
2703-
// Get linear memory options (32-bit vs 64-bit pointers, which memory, etc.)
27042702
let src_mem_opts = match &src.opts().data_model {
27052703
DataModel::Gc {} => todo!("CM+GC"),
27062704
DataModel::LinearMemory(opts) => opts,
@@ -2710,84 +2708,66 @@ impl<'a, 'b> Compiler<'a, 'b> {
27102708
DataModel::LinearMemory(opts) => opts,
27112709
};
27122710

2713-
// Get type information for source and destination maps
2714-
// Look up the TypeMap structs which contain the key and value InterfaceTypes
27152711
let src_map_ty = &self.types[src_ty];
27162712
let dst_map_ty = match dst_ty {
27172713
InterfaceType::Map(r) => &self.types[*r],
27182714
_ => panic!("expected a map"),
27192715
};
27202716

2721-
// Load the map's pointer and length into temporary locals
2722-
// A map is represented as (ptr, len) - we need both values in locals
2723-
// for later use in the translation loop.
27242717
match src {
27252718
Source::Stack(s) => {
2726-
// If map descriptor is passed on the stack (as 2 locals: ptr, len)
27272719
assert_eq!(s.locals.len(), 2);
2728-
self.stack_get(&s.slice(0..1), src_mem_opts.ptr()); // Push ptr to wasm stack
2729-
self.stack_get(&s.slice(1..2), src_mem_opts.ptr()); // Push len to wasm stack
2720+
self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
2721+
self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
27302722
}
27312723
Source::Memory(mem) => {
2732-
// If map descriptor is stored in memory, load ptr and len from there
2733-
self.ptr_load(mem); // Load ptr
2734-
self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into())); // Load len (next field)
2724+
self.ptr_load(mem);
2725+
self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
27352726
}
27362727
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
27372728
}
2738-
// Pop values from wasm stack into named locals (note: len is on top, then ptr)
27392729
let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
27402730
let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
27412731

2742-
// Calculate tuple sizes with proper alignment
2743-
// Each map entry is a (key, value) tuple. We need to know:
2744-
// - Size of key and value in bytes
2745-
// - Alignment requirements
2746-
// - Total tuple size including any padding
27472732
let src_opts = src.opts();
27482733
let dst_opts = dst.opts();
27492734

2750-
// Source tuple layout
2735+
// Each map entry is a tuple<K, V> following record layout rules:
2736+
// key at offset 0, padding to value_align, value, trailing padding
2737+
// to entry_align.
27512738
let (src_key_size, src_key_align) = self.types.size_align(src_mem_opts, &src_map_ty.key);
2752-
let (src_value_size, _) = self.types.size_align(src_mem_opts, &src_map_ty.value);
2753-
// Total tuple size = key + value + padding to alignment
2754-
// e.g., if key is 4 bytes, value is 8 bytes, align is 4:
2755-
// tuple_size = (4 + 8 + 3) & ~3 = 12 bytes
2739+
let (src_value_size, src_value_align) =
2740+
self.types.size_align(src_mem_opts, &src_map_ty.value);
2741+
let src_entry_align = src_key_align.max(src_value_align);
2742+
let src_value_offset =
2743+
(src_key_size + src_value_align - 1) & !(src_value_align - 1);
27562744
let src_tuple_size =
2757-
(src_key_size + src_value_size + src_key_align - 1) & !(src_key_align - 1);
2745+
(src_value_offset + src_value_size + src_entry_align - 1) & !(src_entry_align - 1);
27582746

2759-
// Destination tuple layout (may differ if types are converted)
27602747
let (dst_key_size, dst_key_align) = self.types.size_align(dst_mem_opts, &dst_map_ty.key);
2761-
let (dst_value_size, _) = self.types.size_align(dst_mem_opts, &dst_map_ty.value);
2748+
let (dst_value_size, dst_value_align) =
2749+
self.types.size_align(dst_mem_opts, &dst_map_ty.value);
2750+
let dst_entry_align = dst_key_align.max(dst_value_align);
2751+
let dst_value_offset =
2752+
(dst_key_size + dst_value_align - 1) & !(dst_value_align - 1);
27622753
let dst_tuple_size =
2763-
(dst_key_size + dst_value_size + dst_key_align - 1) & !(dst_key_align - 1);
2754+
(dst_value_offset + dst_value_size + dst_entry_align - 1) & !(dst_entry_align - 1);
27642755

2765-
// Create source memory operand and verify alignment
2766-
// This creates a Memory operand and verifies the source pointer is properly aligned
2767-
let src_mem = self.memory_operand(src_opts, src_ptr, src_key_align);
2756+
let src_mem = self.memory_operand(src_opts, src_ptr, src_entry_align);
27682757

2769-
// Calculate total byte lengths for source and destination
2770-
// total_bytes = count * tuple_size
2758+
// Calculate source/destination byte lengths and allocate destination.
2759+
self.instruction(LocalGet(src_len.idx));
2760+
self.ptr_uconst(src_mem_opts, src_tuple_size);
2761+
self.ptr_mul(src_mem_opts);
27712762
let src_byte_len = self.local_set_new_tmp(src_mem_opts.ptr());
2772-
self.instruction(LocalGet(src_len.idx)); // Push len
2773-
self.ptr_uconst(src_mem_opts, src_tuple_size); // Push tuple_size
2774-
self.ptr_mul(src_mem_opts); // len * tuple_size
2775-
self.instruction(LocalSet(src_byte_len.idx)); // Save to local
27762763

2764+
self.instruction(LocalGet(src_len.idx));
2765+
self.ptr_uconst(dst_mem_opts, dst_tuple_size);
2766+
self.ptr_mul(dst_mem_opts);
27772767
let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2778-
self.instruction(LocalGet(src_len.idx)); // Push len (same count)
2779-
self.ptr_uconst(dst_mem_opts, dst_tuple_size); // Push dst tuple_size
2780-
self.ptr_mul(dst_mem_opts); // len * tuple_size
2781-
self.instruction(LocalTee(dst_byte_len.idx)); // Save AND keep on stack for malloc
2782-
2783-
// Allocate destination buffer
2784-
// Call realloc in the destination component to allocate space.
2785-
// dst_byte_len is still on the stack from LocalTee above.
2786-
let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), dst_key_align);
2787-
2788-
// Validate memory bounds
2789-
// Verify that ptr + byte_len doesn't overflow or exceed memory bounds.
2790-
// Trap if invalid.
2768+
2769+
let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), dst_entry_align);
2770+
27912771
self.validate_memory_inbounds(
27922772
src_mem_opts,
27932773
src_mem.addr.idx,
@@ -2801,37 +2781,25 @@ impl<'a, 'b> Compiler<'a, 'b> {
28012781
Trap::ListOutOfBounds,
28022782
);
28032783

2804-
// Done with byte length locals
28052784
self.free_temp_local(src_byte_len);
28062785
self.free_temp_local(dst_byte_len);
28072786

2808-
// Main translation loop - copy each (key, value) tuple
2809-
// Skip loop entirely if tuples are zero-sized (nothing to copy)
28102787
if src_tuple_size > 0 || dst_tuple_size > 0 {
2811-
// Loop setup
2812-
// Create counter for remaining elements
2813-
let remaining = self.local_set_new_tmp(src_mem_opts.ptr());
2788+
self.instruction(Block(BlockType::Empty));
2789+
28142790
self.instruction(LocalGet(src_len.idx));
2815-
self.instruction(LocalSet(remaining.idx));
2791+
let remaining = self.local_tee_new_tmp(src_mem_opts.ptr());
2792+
self.ptr_eqz(src_mem_opts);
2793+
self.instruction(BrIf(0));
28162794

2817-
// Create pointer to current position in source
2818-
let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
28192795
self.instruction(LocalGet(src_mem.addr.idx));
2820-
self.instruction(LocalSet(cur_src_ptr.idx));
2796+
let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
28212797

2822-
// Create pointer to current position in destination
2823-
let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
28242798
self.instruction(LocalGet(dst_mem.addr.idx));
2825-
self.instruction(LocalSet(cur_dst_ptr.idx));
2799+
let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
28262800

2827-
// WebAssembly loop structure
2828-
// Block is the outer container (to break out of loop)
2829-
// Loop is what we branch back to for iteration
2830-
self.instruction(Block(BlockType::Empty));
28312801
self.instruction(Loop(BlockType::Empty));
28322802

2833-
// Translate the key
2834-
// Create Source pointing to current key location
28352803
let key_src = Source::Memory(self.memory_operand(
28362804
src_opts,
28372805
TempLocal {
@@ -2841,7 +2809,6 @@ impl<'a, 'b> Compiler<'a, 'b> {
28412809
},
28422810
src_key_align,
28432811
));
2844-
// Create Destination pointing to where key should go
28452812
let key_dst = Destination::Memory(self.memory_operand(
28462813
dst_opts,
28472814
TempLocal {
@@ -2851,32 +2818,30 @@ impl<'a, 'b> Compiler<'a, 'b> {
28512818
},
28522819
dst_key_align,
28532820
));
2854-
// Recursively translate the key (handles any type: primitives, strings, etc.)
28552821
self.translate(&src_map_ty.key, &key_src, &dst_map_ty.key, &key_dst);
28562822

2857-
// Advance pointers past the key to point at value
2858-
if src_key_size > 0 {
2823+
// Advance pointers from key start to value start
2824+
if src_value_offset > 0 {
28592825
self.instruction(LocalGet(cur_src_ptr.idx));
2860-
self.ptr_uconst(src_mem_opts, src_key_size);
2826+
self.ptr_uconst(src_mem_opts, src_value_offset);
28612827
self.ptr_add(src_mem_opts);
28622828
self.instruction(LocalSet(cur_src_ptr.idx));
28632829
}
2864-
if dst_key_size > 0 {
2830+
if dst_value_offset > 0 {
28652831
self.instruction(LocalGet(cur_dst_ptr.idx));
2866-
self.ptr_uconst(dst_mem_opts, dst_key_size);
2832+
self.ptr_uconst(dst_mem_opts, dst_value_offset);
28672833
self.ptr_add(dst_mem_opts);
28682834
self.instruction(LocalSet(cur_dst_ptr.idx));
28692835
}
28702836

2871-
// Translate the value
28722837
let value_src = Source::Memory(self.memory_operand(
28732838
src_opts,
28742839
TempLocal {
28752840
idx: cur_src_ptr.idx,
28762841
ty: src_mem_opts.ptr(),
28772842
needs_free: false,
28782843
},
2879-
src_key_align,
2844+
src_value_align,
28802845
));
28812846
let value_dst = Destination::Memory(self.memory_operand(
28822847
dst_opts,
@@ -2885,52 +2850,49 @@ impl<'a, 'b> Compiler<'a, 'b> {
28852850
ty: dst_mem_opts.ptr(),
28862851
needs_free: false,
28872852
},
2888-
dst_key_align,
2853+
dst_value_align,
28892854
));
2890-
// Recursively translate the value
28912855
self.translate(&src_map_ty.value, &value_src, &dst_map_ty.value, &value_dst);
28922856

2893-
// Advance pointers past the value (including any alignment padding)
2894-
// If tuple_size > key_size + value_size, there's padding we need to skip
2895-
if src_tuple_size > src_key_size + src_value_size {
2857+
// Advance pointers past the value and any trailing padding to
2858+
// reach the next entry. After value translation, cur_ptr still
2859+
// points at the value start, so advance by value_size + trailing
2860+
// padding (i.e. tuple_size - value_offset).
2861+
let src_advance_to_next = src_tuple_size - src_value_offset;
2862+
if src_advance_to_next > 0 {
28962863
self.instruction(LocalGet(cur_src_ptr.idx));
2897-
self.ptr_uconst(src_mem_opts, src_tuple_size - src_key_size - src_value_size);
2864+
self.ptr_uconst(src_mem_opts, src_advance_to_next);
28982865
self.ptr_add(src_mem_opts);
28992866
self.instruction(LocalSet(cur_src_ptr.idx));
29002867
}
2901-
if dst_tuple_size > dst_key_size + dst_value_size {
2868+
let dst_advance_to_next = dst_tuple_size - dst_value_offset;
2869+
if dst_advance_to_next > 0 {
29022870
self.instruction(LocalGet(cur_dst_ptr.idx));
2903-
self.ptr_uconst(dst_mem_opts, dst_tuple_size - dst_key_size - dst_value_size);
2871+
self.ptr_uconst(dst_mem_opts, dst_advance_to_next);
29042872
self.ptr_add(dst_mem_opts);
29052873
self.instruction(LocalSet(cur_dst_ptr.idx));
29062874
}
29072875

2908-
// Loop continuation: decrement counter and branch if not done
29092876
self.instruction(LocalGet(remaining.idx));
2910-
self.ptr_iconst(src_mem_opts, -1); // Push -1
2911-
self.ptr_add(src_mem_opts); // remaining - 1
2912-
self.instruction(LocalTee(remaining.idx)); // Save back AND keep on stack
2913-
self.ptr_br_if(src_mem_opts, 0); // If remaining != 0, branch back to Loop
2914-
self.instruction(End); // End Loop
2915-
self.instruction(End); // End Block
2916-
2917-
// Release loop locals
2877+
self.ptr_iconst(src_mem_opts, -1);
2878+
self.ptr_add(src_mem_opts);
2879+
self.instruction(LocalTee(remaining.idx));
2880+
self.ptr_br_if(src_mem_opts, 0);
2881+
self.instruction(End); // end of loop
2882+
self.instruction(End); // end of block
29182883
self.free_temp_local(cur_dst_ptr);
29192884
self.free_temp_local(cur_src_ptr);
29202885
self.free_temp_local(remaining);
29212886
}
29222887

2923-
// Store the result (ptr, len) to the destination
29242888
match dst {
29252889
Destination::Stack(s, _) => {
2926-
// Put ptr and len on the wasm stack for return
29272890
self.instruction(LocalGet(dst_mem.addr.idx));
29282891
self.stack_set(&s[..1], dst_mem_opts.ptr());
29292892
self.convert_src_len_to_dst(src_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
29302893
self.stack_set(&s[1..], dst_mem_opts.ptr());
29312894
}
29322895
Destination::Memory(mem) => {
2933-
// Store ptr and len to destination memory location
29342896
self.instruction(LocalGet(mem.addr.idx));
29352897
self.instruction(LocalGet(dst_mem.addr.idx));
29362898
self.ptr_store(mem);
@@ -2941,7 +2903,6 @@ impl<'a, 'b> Compiler<'a, 'b> {
29412903
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
29422904
}
29432905

2944-
// Cleanup - release all temporary locals
29452906
self.free_temp_local(src_len);
29462907
self.free_temp_local(src_mem.addr);
29472908
self.free_temp_local(dst_mem.addr);

0 commit comments

Comments
 (0)