Skip to content

Commit b781a7b

Browse files
authored
Initial work towards getting post-return functions (#310)
This adds a few new pseudo-instructions plus a new method on `Interface` to generate a `post-return` function. For now these are simply named `{name}_post_return` and the integration point there will probably change as the component model shapes up. The integration here is intended to still be relatively primitive in that the actual component model integration will likely look different in the future. Given how `wit-bindgen` works today, though, this should at least get things part of the way there.
1 parent 6519762 commit b781a7b

File tree

10 files changed

+602
-375
lines changed

10 files changed

+602
-375
lines changed

crates/gen-guest-c/src/lib.rs

+81-17
Original file line numberDiff line numberDiff line change
@@ -450,15 +450,6 @@ impl C {
450450
abort();
451451
return ret;
452452
}
453-
454-
__attribute__((weak, export_name(\"canonical_abi_free\")))
455-
void canonical_abi_free(
456-
void *ptr,
457-
size_t size,
458-
size_t align
459-
) {
460-
free(ptr);
461-
}
462453
");
463454
}
464455

@@ -518,12 +509,9 @@ impl C {
518509
self.free(iface, t, "&ptr->ptr[i]");
519510
self.src.c("}\n");
520511
}
521-
uwriteln!(
522-
self.src.c,
523-
"canonical_abi_free(ptr->ptr, ptr->len * {}, {});",
524-
self.sizes.size(t),
525-
self.sizes.align(t),
526-
);
512+
uwriteln!(self.src.c, "if (ptr->len > 0) {{");
513+
uwriteln!(self.src.c, "free(ptr->ptr);");
514+
uwriteln!(self.src.c, "}}");
527515
}
528516

529517
TypeDefKind::Variant(v) => {
@@ -1120,6 +1108,42 @@ impl Generator for C {
11201108
self.src.c(&src);
11211109
self.src.c("}\n");
11221110

1111+
if iface.guest_export_needs_post_return(func) {
1112+
uwriteln!(
1113+
self.src.c,
1114+
"__attribute__((export_name(\"cabi_post_{}\")))",
1115+
func.name
1116+
);
1117+
uwrite!(self.src.c, "void {import_name}_post_return(");
1118+
1119+
let mut params = Vec::new();
1120+
let mut c_sig = CSig {
1121+
name: String::from("INVALID"),
1122+
sig: String::from("INVALID"),
1123+
params: Vec::new(),
1124+
ret: Return {
1125+
return_multiple: false,
1126+
scalar: None,
1127+
retptrs: Vec::new(),
1128+
},
1129+
retptrs: Vec::new(),
1130+
};
1131+
for (i, result) in sig.results.iter().enumerate() {
1132+
let name = format!("arg{i}");
1133+
uwrite!(self.src.c, "{} {name}", wasm_type(*result));
1134+
c_sig.params.push((false, name.clone()));
1135+
params.push(name);
1136+
}
1137+
self.src.c.push_str(") {\n");
1138+
1139+
let mut f = FunctionBindgen::new(self, c_sig, &import_name);
1140+
f.params = params;
1141+
iface.post_return(func, &mut f);
1142+
let FunctionBindgen { src, .. } = f;
1143+
self.src.c(&src);
1144+
self.src.c("}\n");
1145+
}
1146+
11231147
let src = mem::replace(&mut self.src, prev);
11241148
self.funcs
11251149
.entry(iface.name.to_string())
@@ -1299,7 +1323,9 @@ impl Generator for C {
12991323
}}
13001324
13011325
void {0}_string_free({0}_string_t *ret) {{
1302-
canonical_abi_free(ret->ptr, ret->len, 1);
1326+
if (ret->len > 0) {{
1327+
free(ret->ptr);
1328+
}}
13031329
ret->ptr = NULL;
13041330
ret->len = 0;
13051331
}}
@@ -2204,9 +2230,47 @@ impl Bindgen for FunctionBindgen<'_> {
22042230
self.load_ext("int16_t", *offset, operands, results)
22052231
}
22062232

2207-
Instruction::Free { .. } => {
2233+
Instruction::GuestDeallocate { .. } => {
22082234
uwriteln!(self.src, "free((void*) ({}));", operands[0]);
22092235
}
2236+
Instruction::GuestDeallocateString => {
2237+
uwriteln!(self.src, "if (({}) > 0) {{", operands[1]);
2238+
uwriteln!(self.src, "free((void*) ({}));", operands[0]);
2239+
uwriteln!(self.src, "}}");
2240+
}
2241+
Instruction::GuestDeallocateVariant { blocks } => {
2242+
let blocks = self
2243+
.blocks
2244+
.drain(self.blocks.len() - blocks..)
2245+
.collect::<Vec<_>>();
2246+
2247+
uwriteln!(self.src, "switch ((int32_t) {}) {{", operands[0]);
2248+
for (i, (block, results)) in blocks.into_iter().enumerate() {
2249+
assert!(results.is_empty());
2250+
uwriteln!(self.src, "case {}: {{", i);
2251+
self.src.push_str(&block);
2252+
self.src.push_str("break;\n}\n");
2253+
}
2254+
self.src.push_str("}\n");
2255+
}
2256+
Instruction::GuestDeallocateList { element } => {
2257+
let (body, results) = self.blocks.pop().unwrap();
2258+
assert!(results.is_empty());
2259+
let ptr = self.locals.tmp("ptr");
2260+
let len = self.locals.tmp("len");
2261+
uwriteln!(self.src, "int32_t {ptr} = {};", operands[0]);
2262+
uwriteln!(self.src, "int32_t {len} = {};", operands[1]);
2263+
let i = self.locals.tmp("i");
2264+
uwriteln!(self.src, "for (int32_t {i} = 0; {i} < {len}; {i}++) {{");
2265+
let size = self.gen.sizes.size(element);
2266+
uwriteln!(self.src, "int32_t base = {ptr} + {i} * {size};");
2267+
uwriteln!(self.src, "(void) base;");
2268+
uwrite!(self.src, "{body}");
2269+
uwriteln!(self.src, "}}");
2270+
uwriteln!(self.src, "if ({len} > 0) {{");
2271+
uwriteln!(self.src, "free((void*) ({ptr}));");
2272+
uwriteln!(self.src, "}}");
2273+
}
22102274

22112275
i => unimplemented!("{:?}", i),
22122276
}

crates/gen-guest-rust/src/lib.rs

+106-31
Original file line numberDiff line numberDiff line change
@@ -528,23 +528,20 @@ impl Generator for RustWasm {
528528
fn export(&mut self, iface: &Interface, func: &Function) {
529529
let iface_name = iface.name.to_snake_case();
530530

531-
self.src.push_str("#[export_name = \"");
532-
match &iface.module {
531+
let name_mangled = iface.mangle_funcname(func);
532+
let name_snake = func.name.to_snake_case();
533+
let name = match &iface.module {
533534
Some(module) => {
534-
self.src.push_str(module);
535-
self.src.push_str("#");
536-
self.src.push_str(&iface.mangle_funcname(func));
535+
format!("{module}#{}", name_mangled)
537536
}
538-
None => {
539-
self.src.push_str(&self.opts.symbol_namespace);
540-
self.src.push_str(&iface.mangle_funcname(func));
541-
}
542-
}
543-
self.src.push_str("\"]\n");
537+
None => format!("{}{}", self.opts.symbol_namespace, name_mangled),
538+
};
539+
540+
self.src.push_str(&format!("#[export_name = \"{name}\"]\n"));
544541
self.src.push_str("unsafe extern \"C\" fn __wit_bindgen_");
545542
self.src.push_str(&iface_name);
546543
self.src.push_str("_");
547-
self.src.push_str(&func.name.to_snake_case());
544+
self.src.push_str(&name_snake);
548545
self.src.push_str("(");
549546
let sig = iface.wasm_signature(AbiVariant::GuestExport, func);
550547
let mut params = Vec::new();
@@ -594,6 +591,37 @@ impl Generator for RustWasm {
594591
self.src.push_str(&String::from(src));
595592
self.src.push_str("}\n");
596593

594+
if iface.guest_export_needs_post_return(func) {
595+
self.src.push_str(&format!(
596+
"#[export_name = \"{}cabi_post_{}\"]\n",
597+
self.opts.symbol_namespace, func.name,
598+
));
599+
self.src.push_str(&format!(
600+
"unsafe extern \"C\" fn __wit_bindgen_{iface_name}_{name_snake}_post_return("
601+
));
602+
let mut params = Vec::new();
603+
for (i, result) in sig.results.iter().enumerate() {
604+
let name = format!("arg{}", i);
605+
self.src.push_str(&name);
606+
self.src.push_str(": ");
607+
self.wasm_type(*result);
608+
self.src.push_str(", ");
609+
params.push(name);
610+
}
611+
self.src.push_str(") {\n");
612+
613+
let mut f = FunctionBindgen::new(self, params);
614+
iface.post_return(func, &mut f);
615+
let FunctionBindgen {
616+
needs_cleanup_list,
617+
src,
618+
..
619+
} = f;
620+
assert!(!needs_cleanup_list);
621+
self.src.push_str(&String::from(src));
622+
self.src.push_str("}\n");
623+
}
624+
597625
let prev = mem::take(&mut self.src);
598626
self.in_trait = true;
599627
let mut sig = FnSig::default();
@@ -1290,10 +1318,7 @@ impl Bindgen for FunctionBindgen<'_> {
12901318
results.push(len);
12911319
}
12921320

1293-
Instruction::ListCanonLift { free, .. } => {
1294-
// This only happens when we're receiving a list from the
1295-
// outside world, so `free` should always be `Some`.
1296-
assert!(free.is_some());
1321+
Instruction::ListCanonLift { .. } => {
12971322
let tmp = self.tmp();
12981323
let len = format!("len{}", tmp);
12991324
self.push_str(&format!("let {} = {} as usize;\n", len, operands[1]));
@@ -1324,10 +1349,7 @@ impl Bindgen for FunctionBindgen<'_> {
13241349
results.push(len);
13251350
}
13261351

1327-
Instruction::StringLift { free, .. } => {
1328-
// This only happens when we're receiving a string from the
1329-
// outside world, so `free` should always be `Some`.
1330-
assert!(free.is_some());
1352+
Instruction::StringLift => {
13311353
let tmp = self.tmp();
13321354
let len = format!("len{}", tmp);
13331355
self.push_str(&format!("let {} = {} as usize;\n", len, operands[1]));
@@ -1383,10 +1405,7 @@ impl Bindgen for FunctionBindgen<'_> {
13831405
}
13841406
}
13851407

1386-
Instruction::ListLift { element, free, .. } => {
1387-
// This only happens when we're receiving a list from the
1388-
// outside world, so `free` should always be `Some`.
1389-
assert!(free.is_some());
1408+
Instruction::ListLift { element, .. } => {
13901409
let body = self.blocks.pop().unwrap();
13911410
let tmp = self.tmp();
13921411
let size = self.gen.sizes.size(element);
@@ -1421,7 +1440,7 @@ impl Bindgen for FunctionBindgen<'_> {
14211440
self.push_str("}\n");
14221441
results.push(result);
14231442
self.push_str(&format!(
1424-
"if {len} != 0 {{\nstd::alloc::dealloc({base} as *mut _, std::alloc::Layout::from_size_align_unchecked(({len} as usize) * {size}, {align}));\n}}\n",
1443+
"wit_bindgen_guest_rust::rt::dealloc({base}, ({len} as usize) * {size}, {align});\n",
14251444
));
14261445
}
14271446

@@ -1575,16 +1594,72 @@ impl Bindgen for FunctionBindgen<'_> {
15751594
}
15761595

15771596
Instruction::Malloc { .. } => unimplemented!(),
1578-
Instruction::Free {
1579-
free: _,
1580-
size,
1581-
align,
1582-
} => {
1597+
1598+
Instruction::GuestDeallocate { size, align } => {
15831599
self.push_str(&format!(
1584-
"wit_bindgen_guest_rust::rt::canonical_abi_free({} as *mut u8, {}, {});\n",
1600+
"wit_bindgen_guest_rust::rt::dealloc({}, {}, {});\n",
15851601
operands[0], size, align
15861602
));
15871603
}
1604+
1605+
Instruction::GuestDeallocateString => {
1606+
self.push_str(&format!(
1607+
"wit_bindgen_guest_rust::rt::dealloc({}, ({}) as usize, 1);\n",
1608+
operands[0], operands[1],
1609+
));
1610+
}
1611+
1612+
Instruction::GuestDeallocateVariant { blocks } => {
1613+
let max = blocks - 1;
1614+
let blocks = self
1615+
.blocks
1616+
.drain(self.blocks.len() - blocks..)
1617+
.collect::<Vec<_>>();
1618+
let op0 = &operands[0];
1619+
self.src.push_str(&format!("match {op0} {{\n"));
1620+
for (i, block) in blocks.into_iter().enumerate() {
1621+
let pat = if i == max {
1622+
String::from("_")
1623+
} else {
1624+
i.to_string()
1625+
};
1626+
self.src.push_str(&format!("{pat} => {block},\n"));
1627+
}
1628+
self.src.push_str("}\n");
1629+
}
1630+
1631+
Instruction::GuestDeallocateList { element } => {
1632+
let body = self.blocks.pop().unwrap();
1633+
let tmp = self.tmp();
1634+
let size = self.gen.sizes.size(element);
1635+
let align = self.gen.sizes.align(element);
1636+
let len = format!("len{tmp}");
1637+
let base = format!("base{tmp}");
1638+
self.push_str(&format!(
1639+
"let {base} = {operand0};\n",
1640+
operand0 = operands[0]
1641+
));
1642+
self.push_str(&format!(
1643+
"let {len} = {operand1};\n",
1644+
operand1 = operands[1]
1645+
));
1646+
1647+
if body != "()" {
1648+
self.push_str("for i in 0..");
1649+
self.push_str(&len);
1650+
self.push_str(" {\n");
1651+
self.push_str("let base = ");
1652+
self.push_str(&base);
1653+
self.push_str(" + i *");
1654+
self.push_str(&size.to_string());
1655+
self.push_str(";\n");
1656+
self.push_str(&body);
1657+
self.push_str("\n}\n");
1658+
}
1659+
self.push_str(&format!(
1660+
"wit_bindgen_guest_rust::rt::dealloc({base}, ({len} as usize) * {size}, {align});\n",
1661+
));
1662+
}
15881663
}
15891664
}
15901665
}

0 commit comments

Comments
 (0)