Skip to content

Commit 0a302d8

Browse files
committed
feat: add map support across backends
Implement map type rendering plus lowering/lifting/deallocation support across the C, C++, C#, Go, MoonBit, and Markdown backends, and add map codegen/runtime tests. This aligns non-Rust generators with core map ABI support and fixes the Go test harness module replacement path needed for map codegen verification. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
1 parent ffbf78c commit 0a302d8

File tree

20 files changed

+1093
-20
lines changed

20 files changed

+1093
-20
lines changed

crates/c/src/lib.rs

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,7 @@ fn is_prim_type_id(resolve: &Resolve, id: TypeId) -> bool {
10881088
| TypeDefKind::Stream(_)
10891089
| TypeDefKind::Unknown => false,
10901090
TypeDefKind::FixedLengthList(..) => todo!(),
1091-
TypeDefKind::Map(..) => todo!(),
1091+
TypeDefKind::Map(key, value) => is_prim_type(resolve, key) && is_prim_type(resolve, value),
10921092
}
10931093
}
10941094

@@ -1174,7 +1174,12 @@ pub fn push_ty_name(resolve: &Resolve, ty: &Type, src: &mut String) {
11741174
}
11751175
TypeDefKind::Unknown => unreachable!(),
11761176
TypeDefKind::FixedLengthList(..) => todo!(),
1177-
TypeDefKind::Map(..) => todo!(),
1177+
TypeDefKind::Map(key, value) => {
1178+
src.push_str("map_");
1179+
push_ty_name(resolve, key, src);
1180+
src.push_str("_");
1181+
push_ty_name(resolve, value, src);
1182+
}
11781183
}
11791184
}
11801185
}
@@ -1383,12 +1388,12 @@ impl Return {
13831388
TypeDefKind::Tuple(_)
13841389
| TypeDefKind::Record(_)
13851390
| TypeDefKind::List(_)
1391+
| TypeDefKind::Map(_, _)
13861392
| TypeDefKind::Variant(_) => {}
13871393

13881394
TypeDefKind::Resource => todo!("return_single for resource"),
13891395
TypeDefKind::Unknown => unreachable!(),
13901396
TypeDefKind::FixedLengthList(..) => todo!(),
1391-
TypeDefKind::Map(..) => todo!(),
13921397
}
13931398

13941399
self.retptrs.push(*orig_ty);
@@ -1735,6 +1740,23 @@ void __wasm_export_{ns}_{snake}_dtor({ns}_{snake}_t* arg) {{
17351740
self.finish_typedef_struct(id);
17361741
}
17371742

1743+
fn type_map(&mut self, id: TypeId, _name: &str, key: &Type, value: &Type, docs: &Docs) {
1744+
self.src.h_defs("\n");
1745+
self.docs(docs, SourceType::HDefs);
1746+
let map_ty = self.r#gen.type_name(&Type::Id(id));
1747+
let entry_ty = format!("{map_ty}_entry_t");
1748+
uwriteln!(self.src.h_defs, "typedef struct {entry_ty} {{");
1749+
self.print_ty(SourceType::HDefs, key);
1750+
self.src.h_defs(" key;\n");
1751+
self.print_ty(SourceType::HDefs, value);
1752+
self.src.h_defs(" value;\n");
1753+
uwriteln!(self.src.h_defs, "}} {entry_ty};");
1754+
self.start_typedef_struct(id);
1755+
uwriteln!(self.src.h_defs, "{entry_ty} *ptr;");
1756+
self.src.h_defs("size_t len;\n");
1757+
self.finish_typedef_struct(id);
1758+
}
1759+
17381760
fn type_fixed_length_list(
17391761
&mut self,
17401762
_id: TypeId,
@@ -1844,6 +1866,24 @@ impl<'a> wit_bindgen_core::AnonymousTypeGenerator<'a> for InterfaceGenerator<'a>
18441866
self.print_typedef_target(id);
18451867
}
18461868

1869+
fn anonymous_type_map(&mut self, id: TypeId, key: &Type, value: &Type, _docs: &Docs) {
1870+
let map_ty = self.r#gen.type_name(&Type::Id(id));
1871+
let entry_ty = format!("{map_ty}_entry_t");
1872+
uwriteln!(self.src.h_defs, "\ntypedef struct {entry_ty} {{");
1873+
self.print_ty(SourceType::HDefs, key);
1874+
self.src.h_defs(" key;\n");
1875+
self.print_ty(SourceType::HDefs, value);
1876+
self.src.h_defs(" value;\n");
1877+
uwriteln!(self.src.h_defs, "}} {entry_ty};");
1878+
self.src.h_defs("\ntypedef ");
1879+
self.src.h_defs("struct {\n");
1880+
uwriteln!(self.src.h_defs, "{entry_ty} *ptr;");
1881+
self.src.h_defs("size_t len;\n");
1882+
self.src.h_defs("}");
1883+
self.src.h_defs(" ");
1884+
self.print_typedef_target(id);
1885+
}
1886+
18471887
fn anonymous_type_future(&mut self, id: TypeId, _ty: &Option<Type>, _docs: &Docs) {
18481888
self.src.h_defs("\ntypedef uint32_t ");
18491889
self.print_typedef_target(id);
@@ -2005,6 +2045,21 @@ impl InterfaceGenerator<'_> {
20052045
uwriteln!(self.src.c_helpers, "}}");
20062046
}
20072047

2048+
TypeDefKind::Map(key, value) => {
2049+
self.src.c_helpers("size_t map_len = ptr->len;\n");
2050+
uwriteln!(self.src.c_helpers, "if (map_len > 0) {{");
2051+
let map_ty = self.r#gen.type_name(&Type::Id(id));
2052+
let entry_ty = format!("{map_ty}_entry_t");
2053+
uwriteln!(self.src.c_helpers, "{entry_ty} *map_ptr = ptr->ptr;");
2054+
self.src
2055+
.c_helpers("for (size_t i = 0; i < map_len; i++) {\n");
2056+
self.free(key, "&map_ptr[i].key");
2057+
self.free(value, "&map_ptr[i].value");
2058+
self.src.c_helpers("}\n");
2059+
uwriteln!(self.src.c_helpers, "free(map_ptr);");
2060+
uwriteln!(self.src.c_helpers, "}}");
2061+
}
2062+
20082063
TypeDefKind::Variant(v) => {
20092064
self.src.c_helpers("switch ((int32_t) ptr->tag) {\n");
20102065
for (i, case) in v.cases.iter().enumerate() {
@@ -2045,7 +2100,6 @@ impl InterfaceGenerator<'_> {
20452100
}
20462101
TypeDefKind::Unknown => unreachable!(),
20472102
TypeDefKind::FixedLengthList(..) => todo!(),
2048-
TypeDefKind::Map(..) => todo!(),
20492103
}
20502104
if c_helpers_body_start == self.src.c_helpers.len() {
20512105
self.src.c_helpers.as_mut_string().truncate(c_helpers_start);
@@ -2739,7 +2793,9 @@ void {name}_return({return_ty}) {{
27392793

27402794
TypeDefKind::Unknown => false,
27412795
TypeDefKind::FixedLengthList(..) => todo!(),
2742-
TypeDefKind::Map(..) => todo!(),
2796+
TypeDefKind::Map(key, value) => {
2797+
self.contains_droppable_borrow(key) || self.contains_droppable_borrow(value)
2798+
}
27432799
}
27442800
} else {
27452801
false
@@ -3609,6 +3665,11 @@ impl Bindgen for FunctionBindgen<'_, '_> {
36093665
results.push(format!("(uint8_t *) ({}).ptr", operands[0]));
36103666
results.push(format!("({}).len", operands[0]));
36113667
}
3668+
Instruction::MapLower { .. } => {
3669+
let _body = self.blocks.pop().unwrap();
3670+
results.push(format!("(uint8_t *) ({}).ptr", operands[0]));
3671+
results.push(format!("({}).len", operands[0]));
3672+
}
36123673

36133674
Instruction::ListLift { element, ty, .. } => {
36143675
self.assert_no_droppable_borrows("list", &Type::Id(*ty));
@@ -3621,7 +3682,19 @@ impl Bindgen for FunctionBindgen<'_, '_> {
36213682
list_name, elem_name, operands[0], operands[1]
36223683
));
36233684
}
3685+
Instruction::MapLift { ty, .. } => {
3686+
let _body = self.blocks.pop().unwrap();
3687+
self.assert_no_droppable_borrows("map", &Type::Id(*ty));
3688+
let map_name = self.r#gen.r#gen.type_name(&Type::Id(*ty));
3689+
let entry_name = format!("{map_name}_entry_t");
3690+
results.push(format!(
3691+
"({}) {{ ({}*)({}), ({}) }}",
3692+
map_name, entry_name, operands[0], operands[1]
3693+
));
3694+
}
36243695
Instruction::IterElem { .. } => results.push("e".to_string()),
3696+
Instruction::IterMapKey { .. } => results.push("map_key".to_string()),
3697+
Instruction::IterMapValue { .. } => results.push("map_value".to_string()),
36253698
Instruction::IterBasePointer => results.push("base".to_string()),
36263699

36273700
Instruction::CallWasm { sig, .. } => {
@@ -3956,6 +4029,28 @@ impl Bindgen for FunctionBindgen<'_, '_> {
39564029
uwriteln!(self.src, "free({ptr});");
39574030
uwriteln!(self.src, "}}");
39584031
}
4032+
Instruction::GuestDeallocateMap { key, value } => {
4033+
let (body, results) = self.blocks.pop().unwrap();
4034+
assert!(results.is_empty());
4035+
let len = self.locals.tmp("len");
4036+
uwriteln!(self.src, "size_t {len} = {};", operands[1]);
4037+
uwriteln!(self.src, "if ({len} > 0) {{");
4038+
let ptr = self.locals.tmp("ptr");
4039+
uwriteln!(self.src, "uint8_t *{ptr} = {};", operands[0]);
4040+
let i = self.locals.tmp("i");
4041+
uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{");
4042+
let size = self.r#gen.r#gen.sizes.record([*key, *value]).size;
4043+
uwriteln!(
4044+
self.src,
4045+
"uint8_t *base = {ptr} + {i} * {};",
4046+
size.format(POINTER_SIZE_EXPRESSION)
4047+
);
4048+
uwriteln!(self.src, "(void) base;");
4049+
uwrite!(self.src, "{body}");
4050+
uwriteln!(self.src, "}}");
4051+
uwriteln!(self.src, "free({ptr});");
4052+
uwriteln!(self.src, "}}");
4053+
}
39594054

39604055
Instruction::Flush { amt } => {
39614056
results.extend(operands.iter().take(*amt).cloned());
@@ -4109,7 +4204,7 @@ pub fn is_arg_by_pointer(resolve: &Resolve, ty: &Type) -> bool {
41094204
TypeDefKind::Resource => todo!("is_arg_by_pointer for resource"),
41104205
TypeDefKind::Unknown => unreachable!(),
41114206
TypeDefKind::FixedLengthList(..) => todo!(),
4112-
TypeDefKind::Map(..) => todo!(),
4207+
TypeDefKind::Map(..) => true,
41134208
},
41144209
Type::String => true,
41154210
_ => false,

crates/core/src/abi.rs

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,28 @@ def_instruction! {
310310
ty: TypeId,
311311
} : [2] => [1],
312312

313+
/// Lowers a map into a canonical pointer/length pair.
314+
///
315+
/// This operation pops a map value from the stack and pushes pointer
316+
/// and length. A block is popped from the block stack to lower one
317+
/// key/value entry into linear memory.
318+
MapLower {
319+
key: &'a Type,
320+
value: &'a Type,
321+
realloc: Option<&'a str>,
322+
} : [1] => [2],
323+
324+
/// Lifts a canonical pointer/length pair into a map.
325+
///
326+
/// This operation consumes pointer and length from the stack. A block
327+
/// is popped from the block stack and must produce key/value for one
328+
/// map entry.
329+
MapLift {
330+
key: &'a Type,
331+
value: &'a Type,
332+
ty: TypeId,
333+
} : [2] => [1],
334+
313335
/// Pops all fields for a fixed list off the stack and then composes them
314336
/// into an array.
315337
FixedLengthListLift {
@@ -349,6 +371,14 @@ def_instruction! {
349371
/// This is only used inside of blocks related to lowering lists.
350372
IterElem { element: &'a Type } : [0] => [1],
351373

374+
/// Pushes an operand onto the stack representing the current map key
375+
/// for each map iteration.
376+
IterMapKey { key: &'a Type } : [0] => [1],
377+
378+
/// Pushes an operand onto the stack representing the current map value
379+
/// for each map iteration.
380+
IterMapValue { value: &'a Type } : [0] => [1],
381+
352382
/// Pushes an operand onto the stack representing the base pointer of
353383
/// the next element in a list.
354384
///
@@ -581,6 +611,17 @@ def_instruction! {
581611
element: &'a Type,
582612
} : [2] => [0],
583613

614+
/// Used exclusively for guest-code generation this indicates that a
615+
/// map is being deallocated. The ptr/length are on the stack and are
616+
/// popped off and used to deallocate the map entry buffer.
617+
///
618+
/// This variant also pops a block off the block stack to be used as
619+
/// the body of the deallocation loop over map entries.
620+
GuestDeallocateMap {
621+
key: &'a Type,
622+
value: &'a Type,
623+
} : [2] => [0],
624+
584625
/// Used exclusively for guest-code generation this indicates that
585626
/// a variant is being deallocated. The integer discriminant is popped
586627
/// off the stack as well as `blocks` number of blocks popped from the
@@ -875,7 +916,7 @@ fn needs_deallocate(resolve: &Resolve, ty: &Type, what: Deallocate) -> bool {
875916
TypeDefKind::Future(_) | TypeDefKind::Stream(_) => what.handles(),
876917
TypeDefKind::Unknown => unreachable!(),
877918
TypeDefKind::FixedLengthList(t, _) => needs_deallocate(resolve, t, what),
878-
TypeDefKind::Map(..) => todo!(),
919+
TypeDefKind::Map(_, _) => true,
879920
},
880921

881922
Type::Bool
@@ -1618,7 +1659,25 @@ impl<'a, B: Bindgen> Generator<'a, B> {
16181659
self.lower(ty);
16191660
}
16201661
}
1621-
TypeDefKind::Map(..) => todo!(),
1662+
TypeDefKind::Map(key, value) => {
1663+
let realloc = self.list_realloc();
1664+
let value_offset = self.bindgen.sizes().field_offsets([key, value])[1].0;
1665+
self.push_block();
1666+
self.emit(&IterMapKey { key });
1667+
self.emit(&IterBasePointer);
1668+
let key_addr = self.stack.pop().unwrap();
1669+
self.write_to_memory(key, key_addr, Default::default());
1670+
self.emit(&IterMapValue { value });
1671+
self.emit(&IterBasePointer);
1672+
let value_addr = self.stack.pop().unwrap();
1673+
self.write_to_memory(value, value_addr, value_offset);
1674+
self.finish_block(0);
1675+
self.emit(&MapLower {
1676+
key,
1677+
value,
1678+
realloc,
1679+
});
1680+
}
16221681
},
16231682
}
16241683
}
@@ -1819,7 +1878,16 @@ impl<'a, B: Bindgen> Generator<'a, B> {
18191878
id,
18201879
});
18211880
}
1822-
TypeDefKind::Map(..) => todo!(),
1881+
TypeDefKind::Map(key, value) => {
1882+
let value_offset = self.bindgen.sizes().field_offsets([key, value])[1].0;
1883+
self.push_block();
1884+
self.emit(&IterBasePointer);
1885+
let entry_addr = self.stack.pop().unwrap();
1886+
self.read_from_memory(key, entry_addr.clone(), Default::default());
1887+
self.read_from_memory(value, entry_addr, value_offset);
1888+
self.finish_block(2);
1889+
self.emit(&MapLift { key, value, ty: id });
1890+
}
18231891
},
18241892
}
18251893
}
@@ -1907,6 +1975,7 @@ impl<'a, B: Bindgen> Generator<'a, B> {
19071975
Type::Id(id) => match &self.resolve.types[id].kind {
19081976
TypeDefKind::Type(t) => self.write_to_memory(t, addr, offset),
19091977
TypeDefKind::List(_) => self.write_list_to_memory(ty, addr, offset),
1978+
TypeDefKind::Map(_, _) => self.write_list_to_memory(ty, addr, offset),
19101979

19111980
TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Handle(_) => {
19121981
self.lower_and_emit(ty, addr, &I32Store { offset })
@@ -2016,7 +2085,6 @@ impl<'a, B: Bindgen> Generator<'a, B> {
20162085
id,
20172086
});
20182087
}
2019-
TypeDefKind::Map(..) => todo!(),
20202088
},
20212089
}
20222090
}
@@ -2115,6 +2183,7 @@ impl<'a, B: Bindgen> Generator<'a, B> {
21152183
TypeDefKind::Type(t) => self.read_from_memory(t, addr, offset),
21162184

21172185
TypeDefKind::List(_) => self.read_list_from_memory(ty, addr, offset),
2186+
TypeDefKind::Map(_, _) => self.read_list_from_memory(ty, addr, offset),
21182187

21192188
TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Handle(_) => {
21202189
self.emit_and_lift(ty, addr, &I32Load { offset })
@@ -2216,7 +2285,6 @@ impl<'a, B: Bindgen> Generator<'a, B> {
22162285
id,
22172286
});
22182287
}
2219-
TypeDefKind::Map(..) => todo!(),
22202288
},
22212289
}
22222290
}
@@ -2339,6 +2407,18 @@ impl<'a, B: Bindgen> Generator<'a, B> {
23392407
self.emit(&Instruction::GuestDeallocateList { element });
23402408
}
23412409

2410+
TypeDefKind::Map(key, value) => {
2411+
let value_offset = self.bindgen.sizes().field_offsets([key, value])[1].0;
2412+
self.push_block();
2413+
self.emit(&IterBasePointer);
2414+
let entry_addr = self.stack.pop().unwrap();
2415+
self.deallocate_indirect(key, entry_addr.clone(), Default::default(), what);
2416+
self.deallocate_indirect(value, entry_addr, value_offset, what);
2417+
self.finish_block(0);
2418+
2419+
self.emit(&Instruction::GuestDeallocateMap { key, value });
2420+
}
2421+
23422422
TypeDefKind::Handle(Handle::Own(_))
23432423
| TypeDefKind::Future(_)
23442424
| TypeDefKind::Stream(_)
@@ -2405,7 +2485,6 @@ impl<'a, B: Bindgen> Generator<'a, B> {
24052485
TypeDefKind::Unknown => unreachable!(),
24062486

24072487
TypeDefKind::FixedLengthList(..) => todo!(),
2408-
TypeDefKind::Map(..) => todo!(),
24092488
},
24102489
}
24112490
}
@@ -2464,6 +2543,17 @@ impl<'a, B: Bindgen> Generator<'a, B> {
24642543
self.deallocate(ty, what);
24652544
}
24662545

2546+
TypeDefKind::Map(_, _) => {
2547+
self.stack.push(addr.clone());
2548+
self.emit(&Instruction::PointerLoad { offset });
2549+
self.stack.push(addr);
2550+
self.emit(&Instruction::LengthLoad {
2551+
offset: offset + self.bindgen.sizes().align(ty).into(),
2552+
});
2553+
2554+
self.deallocate(ty, what);
2555+
}
2556+
24672557
TypeDefKind::Handle(Handle::Own(_))
24682558
| TypeDefKind::Future(_)
24692559
| TypeDefKind::Stream(_)
@@ -2527,7 +2617,6 @@ impl<'a, B: Bindgen> Generator<'a, B> {
25272617
TypeDefKind::Stream(_) => unreachable!(),
25282618
TypeDefKind::Unknown => unreachable!(),
25292619
TypeDefKind::FixedLengthList(_, _) => {}
2530-
TypeDefKind::Map(..) => todo!(),
25312620
},
25322621
}
25332622
}

0 commit comments

Comments
 (0)