diff --git a/crates/gen-guest-c/src/lib.rs b/crates/gen-guest-c/src/lib.rs index 2177c22da..1c8cd9eb9 100644 --- a/crates/gen-guest-c/src/lib.rs +++ b/crates/gen-guest-c/src/lib.rs @@ -450,15 +450,6 @@ impl C { abort(); return ret; } - - __attribute__((weak, export_name(\"canonical_abi_free\"))) - void canonical_abi_free( - void *ptr, - size_t size, - size_t align - ) { - free(ptr); - } "); } @@ -518,12 +509,9 @@ impl C { self.free(iface, t, "&ptr->ptr[i]"); self.src.c("}\n"); } - uwriteln!( - self.src.c, - "canonical_abi_free(ptr->ptr, ptr->len * {}, {});", - self.sizes.size(t), - self.sizes.align(t), - ); + uwriteln!(self.src.c, "if (ptr->len > 0) {{"); + uwriteln!(self.src.c, "free(ptr->ptr);"); + uwriteln!(self.src.c, "}}"); } TypeDefKind::Variant(v) => { @@ -1120,6 +1108,42 @@ impl Generator for C { self.src.c(&src); self.src.c("}\n"); + if iface.guest_export_needs_post_return(func) { + uwriteln!( + self.src.c, + "__attribute__((export_name(\"cabi_post_{}\")))", + func.name + ); + uwrite!(self.src.c, "void {import_name}_post_return("); + + let mut params = Vec::new(); + let mut c_sig = CSig { + name: String::from("INVALID"), + sig: String::from("INVALID"), + params: Vec::new(), + ret: Return { + return_multiple: false, + scalar: None, + retptrs: Vec::new(), + }, + retptrs: Vec::new(), + }; + for (i, result) in sig.results.iter().enumerate() { + let name = format!("arg{i}"); + uwrite!(self.src.c, "{} {name}", wasm_type(*result)); + c_sig.params.push((false, name.clone())); + params.push(name); + } + self.src.c.push_str(") {\n"); + + let mut f = FunctionBindgen::new(self, c_sig, &import_name); + f.params = params; + iface.post_return(func, &mut f); + let FunctionBindgen { src, .. } = f; + self.src.c(&src); + self.src.c("}\n"); + } + let src = mem::replace(&mut self.src, prev); self.funcs .entry(iface.name.to_string()) @@ -1299,7 +1323,9 @@ impl Generator for C { }} void {0}_string_free({0}_string_t *ret) {{ - canonical_abi_free(ret->ptr, ret->len, 1); + if (ret->len > 0) {{ + free(ret->ptr); + }} ret->ptr = NULL; ret->len = 0; }} @@ -2204,9 +2230,47 @@ impl Bindgen for FunctionBindgen<'_> { self.load_ext("int16_t", *offset, operands, results) } - Instruction::Free { .. } => { + Instruction::GuestDeallocate { .. } => { uwriteln!(self.src, "free((void*) ({}));", operands[0]); } + Instruction::GuestDeallocateString => { + uwriteln!(self.src, "if (({}) > 0) {{", operands[1]); + uwriteln!(self.src, "free((void*) ({}));", operands[0]); + uwriteln!(self.src, "}}"); + } + Instruction::GuestDeallocateVariant { blocks } => { + let blocks = self + .blocks + .drain(self.blocks.len() - blocks..) + .collect::>(); + + uwriteln!(self.src, "switch ((int32_t) {}) {{", operands[0]); + for (i, (block, results)) in blocks.into_iter().enumerate() { + assert!(results.is_empty()); + uwriteln!(self.src, "case {}: {{", i); + self.src.push_str(&block); + self.src.push_str("break;\n}\n"); + } + self.src.push_str("}\n"); + } + Instruction::GuestDeallocateList { element } => { + let (body, results) = self.blocks.pop().unwrap(); + assert!(results.is_empty()); + let ptr = self.locals.tmp("ptr"); + let len = self.locals.tmp("len"); + uwriteln!(self.src, "int32_t {ptr} = {};", operands[0]); + uwriteln!(self.src, "int32_t {len} = {};", operands[1]); + let i = self.locals.tmp("i"); + uwriteln!(self.src, "for (int32_t {i} = 0; {i} < {len}; {i}++) {{"); + let size = self.gen.sizes.size(element); + uwriteln!(self.src, "int32_t base = {ptr} + {i} * {size};"); + uwriteln!(self.src, "(void) base;"); + uwrite!(self.src, "{body}"); + uwriteln!(self.src, "}}"); + uwriteln!(self.src, "if ({len} > 0) {{"); + uwriteln!(self.src, "free((void*) ({ptr}));"); + uwriteln!(self.src, "}}"); + } i => unimplemented!("{:?}", i), } diff --git a/crates/gen-guest-rust/src/lib.rs b/crates/gen-guest-rust/src/lib.rs index e07b8d1e5..c574ac0c2 100644 --- a/crates/gen-guest-rust/src/lib.rs +++ b/crates/gen-guest-rust/src/lib.rs @@ -528,23 +528,20 @@ impl Generator for RustWasm { fn export(&mut self, iface: &Interface, func: &Function) { let iface_name = iface.name.to_snake_case(); - self.src.push_str("#[export_name = \""); - match &iface.module { + let name_mangled = iface.mangle_funcname(func); + let name_snake = func.name.to_snake_case(); + let name = match &iface.module { Some(module) => { - self.src.push_str(module); - self.src.push_str("#"); - self.src.push_str(&iface.mangle_funcname(func)); + format!("{module}#{}", name_mangled) } - None => { - self.src.push_str(&self.opts.symbol_namespace); - self.src.push_str(&iface.mangle_funcname(func)); - } - } - self.src.push_str("\"]\n"); + None => format!("{}{}", self.opts.symbol_namespace, name_mangled), + }; + + self.src.push_str(&format!("#[export_name = \"{name}\"]\n")); self.src.push_str("unsafe extern \"C\" fn __wit_bindgen_"); self.src.push_str(&iface_name); self.src.push_str("_"); - self.src.push_str(&func.name.to_snake_case()); + self.src.push_str(&name_snake); self.src.push_str("("); let sig = iface.wasm_signature(AbiVariant::GuestExport, func); let mut params = Vec::new(); @@ -594,6 +591,37 @@ impl Generator for RustWasm { self.src.push_str(&String::from(src)); self.src.push_str("}\n"); + if iface.guest_export_needs_post_return(func) { + self.src.push_str(&format!( + "#[export_name = \"{}cabi_post_{}\"]\n", + self.opts.symbol_namespace, func.name, + )); + self.src.push_str(&format!( + "unsafe extern \"C\" fn __wit_bindgen_{iface_name}_{name_snake}_post_return(" + )); + let mut params = Vec::new(); + for (i, result) in sig.results.iter().enumerate() { + let name = format!("arg{}", i); + self.src.push_str(&name); + self.src.push_str(": "); + self.wasm_type(*result); + self.src.push_str(", "); + params.push(name); + } + self.src.push_str(") {\n"); + + let mut f = FunctionBindgen::new(self, params); + iface.post_return(func, &mut f); + let FunctionBindgen { + needs_cleanup_list, + src, + .. + } = f; + assert!(!needs_cleanup_list); + self.src.push_str(&String::from(src)); + self.src.push_str("}\n"); + } + let prev = mem::take(&mut self.src); self.in_trait = true; let mut sig = FnSig::default(); @@ -1290,10 +1318,7 @@ impl Bindgen for FunctionBindgen<'_> { results.push(len); } - Instruction::ListCanonLift { free, .. } => { - // This only happens when we're receiving a list from the - // outside world, so `free` should always be `Some`. - assert!(free.is_some()); + Instruction::ListCanonLift { .. } => { let tmp = self.tmp(); let len = format!("len{}", tmp); self.push_str(&format!("let {} = {} as usize;\n", len, operands[1])); @@ -1324,10 +1349,7 @@ impl Bindgen for FunctionBindgen<'_> { results.push(len); } - Instruction::StringLift { free, .. } => { - // This only happens when we're receiving a string from the - // outside world, so `free` should always be `Some`. - assert!(free.is_some()); + Instruction::StringLift => { let tmp = self.tmp(); let len = format!("len{}", tmp); self.push_str(&format!("let {} = {} as usize;\n", len, operands[1])); @@ -1383,10 +1405,7 @@ impl Bindgen for FunctionBindgen<'_> { } } - Instruction::ListLift { element, free, .. } => { - // This only happens when we're receiving a list from the - // outside world, so `free` should always be `Some`. - assert!(free.is_some()); + Instruction::ListLift { element, .. } => { let body = self.blocks.pop().unwrap(); let tmp = self.tmp(); let size = self.gen.sizes.size(element); @@ -1421,7 +1440,7 @@ impl Bindgen for FunctionBindgen<'_> { self.push_str("}\n"); results.push(result); self.push_str(&format!( - "if {len} != 0 {{\nstd::alloc::dealloc({base} as *mut _, std::alloc::Layout::from_size_align_unchecked(({len} as usize) * {size}, {align}));\n}}\n", + "wit_bindgen_guest_rust::rt::dealloc({base}, ({len} as usize) * {size}, {align});\n", )); } @@ -1575,16 +1594,72 @@ impl Bindgen for FunctionBindgen<'_> { } Instruction::Malloc { .. } => unimplemented!(), - Instruction::Free { - free: _, - size, - align, - } => { + + Instruction::GuestDeallocate { size, align } => { self.push_str(&format!( - "wit_bindgen_guest_rust::rt::canonical_abi_free({} as *mut u8, {}, {});\n", + "wit_bindgen_guest_rust::rt::dealloc({}, {}, {});\n", operands[0], size, align )); } + + Instruction::GuestDeallocateString => { + self.push_str(&format!( + "wit_bindgen_guest_rust::rt::dealloc({}, ({}) as usize, 1);\n", + operands[0], operands[1], + )); + } + + Instruction::GuestDeallocateVariant { blocks } => { + let max = blocks - 1; + let blocks = self + .blocks + .drain(self.blocks.len() - blocks..) + .collect::>(); + let op0 = &operands[0]; + self.src.push_str(&format!("match {op0} {{\n")); + for (i, block) in blocks.into_iter().enumerate() { + let pat = if i == max { + String::from("_") + } else { + i.to_string() + }; + self.src.push_str(&format!("{pat} => {block},\n")); + } + self.src.push_str("}\n"); + } + + Instruction::GuestDeallocateList { element } => { + let body = self.blocks.pop().unwrap(); + let tmp = self.tmp(); + let size = self.gen.sizes.size(element); + let align = self.gen.sizes.align(element); + let len = format!("len{tmp}"); + let base = format!("base{tmp}"); + self.push_str(&format!( + "let {base} = {operand0};\n", + operand0 = operands[0] + )); + self.push_str(&format!( + "let {len} = {operand1};\n", + operand1 = operands[1] + )); + + if body != "()" { + self.push_str("for i in 0.."); + self.push_str(&len); + self.push_str(" {\n"); + self.push_str("let base = "); + self.push_str(&base); + self.push_str(" + i *"); + self.push_str(&size.to_string()); + self.push_str(";\n"); + self.push_str(&body); + self.push_str("\n}\n"); + } + self.push_str(&format!( + "wit_bindgen_guest_rust::rt::dealloc({base}, ({len} as usize) * {size}, {align});\n", + )); + } } } } diff --git a/crates/gen-host-js/src/lib.rs b/crates/gen-host-js/src/lib.rs index 1dbbf4faa..90a608727 100644 --- a/crates/gen-host-js/src/lib.rs +++ b/crates/gen-host-js/src/lib.rs @@ -84,7 +84,6 @@ enum Intrinsic { Utf8EncodedLen, Slab, Promises, - WithCurrentPromise, ThrowInvalidBool, } @@ -113,7 +112,6 @@ impl Intrinsic { Intrinsic::Utf8EncodedLen => "UTF8_ENCODED_LEN", Intrinsic::Slab => "Slab", Intrinsic::Promises => "PROMISES", - Intrinsic::WithCurrentPromise => "with_current_promise", Intrinsic::ThrowInvalidBool => "throw_invalid_bool", } } @@ -643,7 +641,6 @@ impl Generator for Js { src, needs_memory, needs_realloc, - needs_free, .. } = f; @@ -659,11 +656,6 @@ impl Generator for Js { .js(&format!("const realloc = get_export(\"{}\");\n", name)); } - if let Some(name) = needs_free { - self.needs_get_export = true; - self.src - .js(&format!("const free = get_export(\"{}\");\n", name)); - } self.src.js(&src.js); self.src.js("}"); @@ -736,7 +728,6 @@ impl Generator for Js { src, needs_memory, needs_realloc, - needs_free, src_object, .. } = f; @@ -753,12 +744,6 @@ impl Generator for Js { )); } - if let Some(name) = needs_free { - self.src.js(&format!( - "const free = {}._exports[\"{}\"];\n", - src_object, name - )); - } self.src.js(&src.js); self.src.js("}\n"); @@ -1169,7 +1154,6 @@ struct FunctionBindgen<'a> { blocks: Vec<(String, Vec)>, needs_memory: bool, needs_realloc: Option, - needs_free: Option, params: Vec, src_object: String, } @@ -1184,7 +1168,6 @@ impl FunctionBindgen<'_> { blocks: Vec::new(), needs_memory: false, needs_realloc: None, - needs_free: None, params, src_object: "this".to_string(), } @@ -2041,32 +2024,18 @@ impl Bindgen for FunctionBindgen<'_> { results.push(format!("ptr{}", tmp)); results.push(format!("len{}", tmp)); } - Instruction::ListCanonLift { element, free, .. } => { + Instruction::ListCanonLift { element, .. } => { self.needs_memory = true; let tmp = self.tmp(); - self.src - .js(&format!("const ptr{} = {};\n", tmp, operands[0])); - self.src - .js(&format!("const len{} = {};\n", tmp, operands[1])); + self.src.js(&format!("const ptr{tmp} = {};\n", operands[0])); + self.src.js(&format!("const len{tmp} = {};\n", operands[1])); // TODO: this is the wrong endianness let array_ty = self.gen.array_ty(iface, element).unwrap(); - let result = format!( - "new {}(memory.buffer.slice(ptr{}, ptr{1} + len{1} * {}))", - array_ty, - tmp, + self.src.js(&format!( + "const result{tmp} = new {array_ty}(memory.buffer.slice(ptr{tmp}, ptr{tmp} + len{tmp} * {}));\n", self.gen.sizes.size(element), - ); - let align = self.gen.sizes.align(element); - match free { - Some(free) => { - self.needs_free = Some(free.to_string()); - self.src.js(&format!("const list{} = {};\n", tmp, result)); - self.src - .js(&format!("free(ptr{}, len{0}, {});\n", tmp, align)); - results.push(format!("list{}", tmp)); - } - None => results.push(result), - } + )); + results.push(format!("result{tmp}")); } Instruction::StringLower { realloc } => { // Lowering only happens when we're passing strings into wasm, @@ -2089,27 +2058,16 @@ impl Bindgen for FunctionBindgen<'_> { results.push(format!("ptr{}", tmp)); results.push(format!("len{}", tmp)); } - Instruction::StringLift { free } => { + Instruction::StringLift => { self.needs_memory = true; let tmp = self.tmp(); - self.src - .js(&format!("const ptr{} = {};\n", tmp, operands[0])); - self.src - .js(&format!("const len{} = {};\n", tmp, operands[1])); + self.src.js(&format!("const ptr{tmp} = {};\n", operands[0])); + self.src.js(&format!("const len{tmp} = {};\n", operands[1])); let decoder = self.gen.intrinsic(Intrinsic::Utf8Decoder); - let result = format!( - "{}.decode(new Uint8Array(memory.buffer, ptr{}, len{1}))", - decoder, tmp, - ); - match free { - Some(free) => { - self.needs_free = Some(free.to_string()); - self.src.js(&format!("const list{} = {};\n", tmp, result)); - self.src.js(&format!("free(ptr{}, len{0}, 1);\n", tmp)); - results.push(format!("list{}", tmp)); - } - None => results.push(result), - } + self.src.js(&format!( + "const result{tmp} = {decoder}.decode(new Uint8Array(memory.buffer, ptr{tmp}, len{tmp}));\n", + )); + results.push(format!("result{tmp}")); } Instruction::ListLower { element, realloc } => { @@ -2149,11 +2107,10 @@ impl Bindgen for FunctionBindgen<'_> { results.push(len); } - Instruction::ListLift { element, free, .. } => { + Instruction::ListLift { element, .. } => { let (body, body_results) = self.blocks.pop().unwrap(); let tmp = self.tmp(); let size = self.gen.sizes.size(element); - let align = self.gen.sizes.align(element); let len = format!("len{}", tmp); self.src.js(&format!("const {} = {};\n", len, operands[1])); let base = format!("base{}", tmp); @@ -2171,12 +2128,6 @@ impl Bindgen for FunctionBindgen<'_> { self.src .js(&format!("{}.push({});\n", result, body_results[0])); self.src.js("}\n"); - - if let Some(free) = free { - self.needs_free = Some(free.to_string()); - self.src - .js(&format!("free({}, {} * {}, {});\n", base, len, size, align,)); - } } Instruction::IterElem { .. } => results.push("e".to_string()), @@ -2244,11 +2195,21 @@ impl Bindgen for FunctionBindgen<'_> { self.src.js(";\n"); } - Instruction::Return { amt, func: _ } => match amt { - 0 => {} - 1 => self.src.js(&format!("return {};\n", operands[0])), - _ => self.src.js(&format!("return [{}];\n", operands.join(", "))), - }, + Instruction::Return { amt, func } => { + if !self.gen.in_import && iface.guest_export_needs_post_return(func) { + let name = &func.name; + self.src.js(&format!( + "{}._exports[\"cabi_post_{name}\"](ret);\n", + self.src_object + )); + } + + match amt { + 0 => {} + 1 => self.src.js(&format!("return {};\n", operands[0])), + _ => self.src.js(&format!("return [{}];\n", operands.join(", "))), + } + } Instruction::I32Load { offset } => self.load("getInt32", *offset, operands, results), Instruction::I64Load { offset } => self.load("getBigInt64", *offset, operands, results), @@ -2532,18 +2493,6 @@ impl Js { "), Intrinsic::Promises => self.src.js("export const PROMISES = new Slab();\n"), - Intrinsic::WithCurrentPromise => self.src.js(" - let CUR_PROMISE = null; - export function with_current_promise(val, closure) { - const prev = CUR_PROMISE; - CUR_PROMISE = val; - try { - closure(prev); - } finally { - CUR_PROMISE = prev; - } - } - "), Intrinsic::ThrowInvalidBool => self.src.js(" export function throw_invalid_bool() { throw new RangeError(\"invalid variant discriminant for bool\"); diff --git a/crates/gen-host-js/tests/runtime.rs b/crates/gen-host-js/tests/runtime.rs index 3c2e846ff..f7dd67d1f 100644 --- a/crates/gen-host-js/tests/runtime.rs +++ b/crates/gen-host-js/tests/runtime.rs @@ -78,6 +78,7 @@ fn execute(name: &str, wasm: &Path, ts: &Path, imports: &Path, exports: &Path) { println!("{:?}", std::env::join_paths(&path)); run(Command::new("node") .arg("--experimental-wasi-unstable-preview1") + .arg("--stack-trace-limit=1000") .arg(dir.join("host.js")) .env("NODE_PATH", std::env::join_paths(&path).unwrap()) .arg(wasm)); diff --git a/crates/gen-host-wasmtime-py/src/lib.rs b/crates/gen-host-wasmtime-py/src/lib.rs index 3b314ea32..6ef7d5bbd 100644 --- a/crates/gen-host-wasmtime-py/src/lib.rs +++ b/crates/gen-host-wasmtime-py/src/lib.rs @@ -407,7 +407,6 @@ impl Generator for WasmtimePy { src, needs_memory, needs_realloc, - needs_free, mut locals, .. } = f; @@ -428,11 +427,6 @@ impl Generator for WasmtimePy { locals.insert("realloc").unwrap(); } - if let Some(name) = needs_free { - builder.push_str(&format!("free = caller[\"{}\"]\n", name)); - builder.push_str("assert(isinstance(free, wasmtime.Func))\n"); - locals.insert("free").unwrap(); - } builder.push_str(&src); builder.dedent(); @@ -507,7 +501,6 @@ impl Generator for WasmtimePy { src, needs_memory, needs_realloc, - needs_free, src_object, .. } = f; @@ -525,13 +518,6 @@ impl Generator for WasmtimePy { )); } - if let Some(name) = &needs_free { - builder.push_str(&format!( - "free = {}._{}\n", - src_object, - name.to_snake_case(), - )); - } builder.push_str(&src); builder.dedent(); @@ -557,15 +543,6 @@ impl Generator for WasmtimePy { }, ); } - if let Some(name) = &needs_free { - exports.fields.insert( - name.clone(), - Export { - python_type: "wasmtime.Func", - base_name: name.clone(), - }, - ); - } exports.fields.insert( iface.mangle_funcname(func), Export { @@ -887,7 +864,6 @@ struct FunctionBindgen<'a> { blocks: Vec<(String, Vec)>, needs_memory: bool, needs_realloc: Option, - needs_free: Option, params: Vec, payloads: Vec, src_object: String, @@ -910,7 +886,6 @@ impl FunctionBindgen<'_> { blocks: Vec::new(), needs_memory: false, needs_realloc: None, - needs_free: None, params, payloads: Vec::new(), src_object: "self".to_string(), @@ -1588,7 +1563,7 @@ impl Bindgen for FunctionBindgen<'_> { results.push(ptr); results.push(len); } - Instruction::ListCanonLift { element, free, .. } => { + Instruction::ListCanonLift { element, .. } => { self.needs_memory = true; let ptr = self.locals.tmp("ptr"); let len = self.locals.tmp("len"); @@ -1604,32 +1579,14 @@ impl Bindgen for FunctionBindgen<'_> { array_ty, ); builder.deps.pyimport("typing", "cast"); - let align = self.gen.sizes.align(element); - match free { - Some(free) => { - self.needs_free = Some(free.to_string()); - let list = self.locals.tmp("list"); - builder.push_str(&list); - builder.push_str(" = cast("); - builder.print_list(element); - builder.push_str(", "); - builder.push_str(&lift); - builder.push_str(")\n"); - builder.push_str(&format!("free(caller, {}, {}, {})\n", ptr, len, align)); - results.push(list); - } - None => { - let mut result_src = Source::default(); - drop(builder); - let mut builder = result_src.builder(&mut self.gen.deps, iface); - builder.push_str("cast("); - builder.print_list(element); - builder.push_str(", "); - builder.push_str(&lift); - builder.push_str(")"); - results.push(result_src.to_string()); - } - } + let list = self.locals.tmp("list"); + builder.push_str(&list); + builder.push_str(" = cast("); + builder.print_list(element); + builder.push_str(", "); + builder.push_str(&lift); + builder.push_str(")\n"); + results.push(list); } Instruction::StringLower { realloc } => { // Lowering only happens when we're passing strings into wasm, @@ -1649,7 +1606,7 @@ impl Bindgen for FunctionBindgen<'_> { results.push(ptr); results.push(len); } - Instruction::StringLift { free, .. } => { + Instruction::StringLift => { self.needs_memory = true; let ptr = self.locals.tmp("ptr"); let len = self.locals.tmp("len"); @@ -1657,17 +1614,9 @@ impl Bindgen for FunctionBindgen<'_> { builder.push_str(&format!("{} = {}\n", len, operands[1])); builder.deps.needs_decode_utf8 = true; let result = format!("_decode_utf8(memory, caller, {}, {})", ptr, len); - match free { - Some(free) => { - self.needs_free = Some(free.to_string()); - let list = self.locals.tmp("list"); - builder.push_str(&format!("{} = {}\n", list, result)); - self.src - .push_str(&format!("free(caller, {}, {}, 1)\n", ptr, len)); - results.push(list); - } - None => results.push(result), - } + let list = self.locals.tmp("list"); + builder.push_str(&format!("{} = {}\n", list, result)); + results.push(list); } Instruction::ListLower { element, realloc } => { @@ -1709,11 +1658,10 @@ impl Bindgen for FunctionBindgen<'_> { results.push(len); } - Instruction::ListLift { element, free, .. } => { + Instruction::ListLift { element, .. } => { let (body, body_results) = self.blocks.pop().unwrap(); let base = self.payloads.pop().unwrap(); let size = self.gen.sizes.size(element); - let align = self.gen.sizes.align(element); let ptr = self.locals.tmp("ptr"); let len = self.locals.tmp("len"); builder.push_str(&format!("{} = {}\n", ptr, operands[0])); @@ -1731,14 +1679,6 @@ impl Bindgen for FunctionBindgen<'_> { assert_eq!(body_results.len(), 1); builder.push_str(&format!("{}.append({})\n", result, body_results[0])); builder.dedent(); - - if let Some(free) = free { - self.needs_free = Some(free.to_string()); - builder.push_str(&format!( - "free(caller, {}, {} * {}, {})\n", - ptr, len, size, align, - )); - } results.push(result); } @@ -1819,14 +1759,33 @@ impl Bindgen for FunctionBindgen<'_> { builder.push_str("\n"); } - Instruction::Return { amt, .. } => match amt { - 0 => {} - 1 => builder.push_str(&format!("return {}\n", operands[0])), - _ => { - self.src - .push_str(&format!("return ({})\n", operands.join(", "))); + Instruction::Return { amt, func, .. } => { + if !self.gen.in_import && iface.guest_export_needs_post_return(func) { + let name = format!("cabi_post_{}", func.name); + let exports = self + .gen + .guest_exports + .entry(iface.name.to_string()) + .or_insert_with(Exports::default); + exports.fields.insert( + name.clone(), + Export { + python_type: "wasmtime.Func", + base_name: name.clone(), + }, + ); + let name = name.to_snake_case(); + builder.push_str(&format!("{}._{name}(caller, ret)\n", self.src_object)); } - }, + match amt { + 0 => {} + 1 => builder.push_str(&format!("return {}\n", operands[0])), + _ => { + self.src + .push_str(&format!("return ({})\n", operands.join(", "))); + } + } + } Instruction::I32Load { offset } => self.load("c_int32", *offset, operands, results), Instruction::I64Load { offset } => self.load("c_int64", *offset, operands, results), diff --git a/crates/gen-host-wasmtime-rust/src/lib.rs b/crates/gen-host-wasmtime-rust/src/lib.rs index bd7f2ee7d..0da6744a2 100644 --- a/crates/gen-host-wasmtime-rust/src/lib.rs +++ b/crates/gen-host-wasmtime-rust/src/lib.rs @@ -38,7 +38,6 @@ pub struct Wasmtime { enum NeededFunction { Realloc, - Free, } struct Import { @@ -507,7 +506,6 @@ impl Generator for Wasmtime { ); let FunctionBindgen { src, - cleanup, needs_borrow_checker, needs_memory, needs_buffer_transaction, @@ -515,7 +513,6 @@ impl Generator for Wasmtime { closures, .. } = f; - assert!(cleanup.is_none()); assert!(!needs_buffer_transaction); // Generate the signature this function will have in the final trait @@ -633,6 +630,7 @@ impl Generator for Wasmtime { fn import(&mut self, iface: &Interface, func: &Function) { let prev = mem::take(&mut self.src); + let wasm_sig = iface.wasm_signature(AbiVariant::GuestExport, func); let mut sig = FnSig::default(); sig.self_arg = Some("&self, mut caller: impl wasmtime::AsContextMut".to_string()); self.print_docs_and_params(iface, func, TypeMode::AllBorrowed("'_"), &sig); @@ -691,6 +689,23 @@ impl Generator for Wasmtime { ); exports.fields.insert(name, (func.ty(), get)); } + if iface.guest_export_needs_post_return(func) { + let name = func.name.to_snake_case(); + self.src + .push_str(&format!("let post_return = &self.{name}_post_return;\n")); + let ret = match wasm_sig.results.len() { + 1 => wasm_type(wasm_sig.results[0]), + _ => unimplemented!(), + }; + let get = format!( + "instance.get_typed_func::<{ret}, (), _>(&mut store, \"cabi_post_{}\")?", + func.name + ); + exports.fields.insert( + format!("{name}_post_return"), + (format!("wasmtime::TypedFunc<{ret}, ()>"), get), + ); + } self.src.push_str(&closures); @@ -726,14 +741,13 @@ impl Generator for Wasmtime { // Create the code snippet which will define the type of this field in // the struct that we're exporting and additionally extracts the // function from an instantiated instance. - let sig = iface.wasm_signature(AbiVariant::GuestExport, func); let mut cvt = "(".to_string(); - for param in sig.params.iter() { + for param in wasm_sig.params.iter() { cvt.push_str(wasm_type(*param)); cvt.push_str(","); } cvt.push_str("), ("); - for result in sig.results.iter() { + for result in wasm_sig.results.iter() { cvt.push_str(wasm_type(*result)); cvt.push_str(","); } @@ -1179,9 +1193,6 @@ struct FunctionBindgen<'a> { // Whether or not the `caller_memory` variable has been defined and is // available for use. caller_memory_available: bool, - // Code that must be executed before a return, generated during instruction - // lowering. - cleanup: Option, // Rust clousures for buffers that must be placed at the front of the // function. @@ -1193,6 +1204,9 @@ struct FunctionBindgen<'a> { needs_borrow_checker: bool, needs_memory: bool, needs_functions: HashMap, + + // Results of the `CallWasm` call, if one was found. + wasm_results: Option>, } impl FunctionBindgen<'_> { @@ -1205,13 +1219,13 @@ impl FunctionBindgen<'_> { after_call: false, caller_memory_available: false, tmp: 0, - cleanup: None, closures: Source::default(), needs_buffer_transaction: false, needs_borrow_checker: false, needs_memory: false, needs_functions: HashMap::new(), params, + wasm_results: None, } } @@ -1731,45 +1745,33 @@ impl Bindgen for FunctionBindgen<'_> { results.push(format!("{}.len() as i32", val)); } - Instruction::ListCanonLift { element, free, .. } => match free { - Some(free) => { + Instruction::ListCanonLift { element, .. } => { + let tmp = self.tmp(); + let ptr = &operands[0]; + let len = &operands[1]; + self.push_str(&format!("let ptr{tmp} = {ptr};\n")); + self.push_str(&format!("let len{tmp} = {len};\n")); + + if self.gen.in_import { + self.needs_borrow_checker = true; + let slice = format!("_bc.slice(ptr{0}, len{0})?", tmp); + results.push(slice); + } else { self.needs_memory = true; self.gen.needs_copy_slice = true; - self.needs_functions - .insert(free.to_string(), NeededFunction::Free); - let (align, el_size) = - (self.sizes().align(element), self.sizes().size(element)); - let tmp = self.tmp(); - self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0])); - self.push_str(&format!("let len{} = {};\n", tmp, operands[1])); + let align = self.sizes().align(element); self.push_str(&format!( " let data{tmp} = copy_slice( &mut caller, memory, - ptr{tmp}, len{tmp}, {} + ptr{tmp}, len{tmp}, {align} )?; ", - align, - tmp = tmp, )); - self.call_intrinsic( - free, - // we use normal multiplication here as copy_slice has - // already verified that multiplied size fits i32 - format!("(ptr{tmp}, len{tmp} * {}, {})", el_size, align, tmp = tmp), - ); - results.push(format!("data{}", tmp)); + results.push(format!("data{tmp}")); } - None => { - self.needs_borrow_checker = true; - let tmp = self.tmp(); - self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0])); - self.push_str(&format!("let len{} = {};\n", tmp, operands[1])); - let slice = format!("_bc.slice(ptr{0}, len{0})?", tmp); - results.push(slice); - } - }, + } Instruction::StringLower { realloc } => { // see above for this unwrap @@ -1799,15 +1801,20 @@ impl Bindgen for FunctionBindgen<'_> { results.push(format!("{}.len() as i32", val)); } - Instruction::StringLift { free } => match free { - Some(free) => { + Instruction::StringLift => { + let tmp = self.tmp(); + let ptr = &operands[0]; + let len = &operands[1]; + self.push_str(&format!("let ptr{tmp} = {ptr};\n")); + self.push_str(&format!("let len{tmp} = {len};\n")); + + if self.gen.in_import { + self.needs_borrow_checker = true; + let slice = format!("_bc.slice_str(ptr{0}, len{0})?", tmp); + results.push(slice); + } else { self.needs_memory = true; self.gen.needs_copy_slice = true; - self.needs_functions - .insert(free.to_string(), NeededFunction::Free); - let tmp = self.tmp(); - self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0])); - self.push_str(&format!("let len{} = {};\n", tmp, operands[1])); self.push_str(&format!( " let data{tmp} = copy_slice( @@ -1818,27 +1825,13 @@ impl Bindgen for FunctionBindgen<'_> { ", tmp = tmp, )); - self.call_intrinsic( - free, - // we use normal multiplication here as copy_slice has - // already verified that multiplied size fits i32 - format!("(ptr{tmp}, len{tmp}, 1)", tmp = tmp), - ); results.push(format!( "String::from_utf8(data{}) - .map_err(|_| wasmtime::Trap::new(\"invalid utf-8\"))?", + .map_err(|_| wasmtime::Trap::new(\"invalid utf-8\"))?", tmp, )); } - None => { - self.needs_borrow_checker = true; - let tmp = self.tmp(); - self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0])); - self.push_str(&format!("let len{} = {};\n", tmp, operands[1])); - let slice = format!("_bc.slice_str(ptr{0}, len{0})?", tmp); - results.push(slice); - } - }, + } Instruction::ListLower { element, realloc } => { let realloc = realloc.unwrap(); @@ -1875,11 +1868,10 @@ impl Bindgen for FunctionBindgen<'_> { results.push(len); } - Instruction::ListLift { element, free, .. } => { + Instruction::ListLift { element, .. } => { let body = self.blocks.pop().unwrap(); let tmp = self.tmp(); let size = self.gen.sizes.size(element); - let align = self.gen.sizes.align(element); let len = format!("len{}", tmp); self.push_str(&format!("let {} = {};\n", len, operands[1])); let base = format!("base{}", tmp); @@ -1904,12 +1896,6 @@ impl Bindgen for FunctionBindgen<'_> { self.push_str(");\n"); self.push_str("}\n"); results.push(result); - - if let Some(free) = free { - self.call_intrinsic(free, format!("({}, {} * {}, {})", base, len, size, align)); - self.needs_functions - .insert(free.to_string(), NeededFunction::Free); - } } Instruction::IterElem { .. } => { @@ -1948,6 +1934,9 @@ impl Bindgen for FunctionBindgen<'_> { self.push_str("?;\n"); self.after_call = true; self.caller_memory_available = false; // invalidated by call + + assert!(self.wasm_results.is_none()); + self.wasm_results = Some(results.clone()); } Instruction::CallInterface { module: _, func } => { @@ -2015,22 +2004,21 @@ impl Bindgen for FunctionBindgen<'_> { } } - Instruction::Return { amt, .. } => { - let result = match amt { + Instruction::Return { amt, func, .. } => { + let mut result = match amt { 0 => format!("Ok(())\n"), 1 => format!("Ok({})\n", operands[0]), _ => format!("Ok(({}))\n", operands.join(", ")), }; - match self.cleanup.take() { - Some(cleanup) => { - self.push_str("let ret = "); - self.push_str(&result); - self.push_str(";\n"); - self.push_str(&cleanup); - self.push_str("ret"); - } - None => self.push_str(&result), + if !self.gen.in_import && iface.guest_export_needs_post_return(func) { + let tmp = self.tmp(); + self.push_str(&format!("let result{tmp} = {result};\n")); + result = format!("result{tmp}"); + + let result = &self.wasm_results.as_ref().unwrap()[0]; + self.push_str(&format!("post_return.call(&mut caller, {result})?;\n",)); } + self.push_str(&result); } Instruction::I32Load { offset } => results.push(self.load(*offset, "i32", operands)), @@ -2079,7 +2067,10 @@ impl Bindgen for FunctionBindgen<'_> { results.push(ptr); } - Instruction::Free { .. } => unimplemented!(), + Instruction::GuestDeallocate { .. } => unreachable!(), + Instruction::GuestDeallocateString { .. } => unreachable!(), + Instruction::GuestDeallocateVariant { .. } => unreachable!(), + Instruction::GuestDeallocateList { .. } => unreachable!(), } } } @@ -2088,7 +2079,6 @@ impl NeededFunction { fn cvt(&self) -> &'static str { match self { NeededFunction::Realloc => "(i32, i32, i32, i32), i32", - NeededFunction::Free => "(i32, i32, i32), ()", } } diff --git a/crates/gen-host-wasmtime-rust/tests/runtime.rs b/crates/gen-host-wasmtime-rust/tests/runtime.rs index 91ffbad89..71ed9755b 100644 --- a/crates/gen-host-wasmtime-rust/tests/runtime.rs +++ b/crates/gen-host-wasmtime-rust/tests/runtime.rs @@ -1,3 +1,5 @@ +#![allow(type_alias_bounds)] // TODO: should fix generated code to not fire this + use anyhow::Result; use wasmtime::{Config, Engine, Instance, Linker, Module, Store}; diff --git a/crates/guest-rust/src/lib.rs b/crates/guest-rust/src/lib.rs index e61f53af1..dcc002653 100644 --- a/crates/guest-rust/src/lib.rs +++ b/crates/guest-rust/src/lib.rs @@ -149,13 +149,12 @@ pub mod rt { return ptr; } - #[no_mangle] - pub unsafe extern "C" fn canonical_abi_free(ptr: *mut u8, len: usize, align: usize) { - if len == 0 { + pub unsafe fn dealloc(ptr: i32, size: usize, align: usize) { + if size == 0 { return; } - let layout = Layout::from_size_align_unchecked(len, align); - alloc::dealloc(ptr, layout); + let layout = Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr as *mut u8, layout); } macro_rules! as_traits { diff --git a/crates/wit-parser/src/abi.rs b/crates/wit-parser/src/abi.rs index 97fb921a1..7ffbbd18f 100644 --- a/crates/wit-parser/src/abi.rs +++ b/crates/wit-parser/src/abi.rs @@ -438,40 +438,24 @@ def_instruction! { /// exactly matches the canonical ABI definition of the type. /// /// This will consume two `i32` values from the stack, a pointer and a - /// length, and then produces an interface value list. If the `free` - /// field is set to `Some` then the pointer/length should be considered - /// an owned allocation and need to be deallocated by the receiver. If - /// it is set to `None` then a view is provided but it does not need to - /// be deallocated. - /// - /// The `free` field is set to `Some` in similar situations as described - /// by `ListCanonLower`. If `free` is `Some` then the memory must be - /// deallocated after the lifted list is done being consumed. If it is - /// `None` then the receiver of the lifted list does not own the memory - /// and must leave the memory as-is. + /// length, and then produces an interface value list. ListCanonLift { element: &'a Type, - free: Option<&'a str>, ty: TypeId, } : [2] => [1], /// Same as `ListCanonLift`, but used for strings - StringLift { - free: Option<&'a str>, - } : [2] => [1], + StringLift : [2] => [1], /// Lifts a list which into an interface types value. /// /// This will consume two `i32` values from the stack, a pointer and a - /// length, and then produces an interface value list. Note that the - /// pointer/length popped are **owned** and need to be deallocated with - /// the wasm `free` function when the list is no longer needed. + /// length, and then produces an interface value list. /// /// This will also pop a block from the block stack which is how to /// read each individual element from the list. ListLift { element: &'a Type, - free: Option<&'a str>, ty: TypeId, } : [2] => [1], @@ -659,13 +643,39 @@ def_instruction! { align: usize, } : [0] => [1], - /// Calls the `free` function specified to deallocate the pointer on the - /// stack which has `size` bytes with alignment `align`. - Free { - free: &'static str, + /// Used exclusively for guest-code generation this indicates that + /// the standard memory deallocation function needs to be invoked with + /// the specified parameters. + /// + /// This will pop a pointer from the stack and push nothing. + GuestDeallocate { size: usize, align: usize, } : [1] => [0], + + /// Used exclusively for guest-code generation this indicates that + /// a string is being deallocated. The ptr/length are on the stack and + /// are poppped off and used to deallocate the string. + GuestDeallocateString : [2] => [0], + + /// Used exclusively for guest-code generation this indicates that + /// a list is being deallocated. The ptr/length are on the stack and + /// are poppped off and used to deallocate the list. + /// + /// This variant also pops a block off the block stack to be used as the + /// body of the deallocation loop. + GuestDeallocateList { + element: &'a Type, + } : [2] => [0], + + /// Used exclusively for guest-code generation this indicates that + /// a variant is being deallocated. The integer discriminant is popped + /// off the stack as well as `blocks` number of blocks popped from the + /// blocks stack. The variant is used to select, at runtime, which of + /// the blocks is executed to deallocate the variant. + GuestDeallocateVariant { + blocks: usize, + } : [1] => [0], } } @@ -982,6 +992,73 @@ impl Interface { ) { Generator::new(self, variant, lift_lower, bindgen).call(func); } + + /// Returns whether the `Function` specified needs a post-return function to + /// be generated in guest code. + /// + /// This is used when the return value contains a memory allocation such as + /// a list or a string primarily. + pub fn guest_export_needs_post_return(&self, func: &Function) -> bool { + func.results.iter_types().any(|t| self.needs_post_return(t)) + } + + fn needs_post_return(&self, ty: &Type) -> bool { + match ty { + Type::String => true, + Type::Id(id) => match &self.types[*id].kind { + TypeDefKind::List(_) => true, + TypeDefKind::Type(t) => self.needs_post_return(t), + TypeDefKind::Record(r) => r.fields.iter().any(|f| self.needs_post_return(&f.ty)), + TypeDefKind::Tuple(t) => t.types.iter().any(|t| self.needs_post_return(t)), + TypeDefKind::Union(t) => t.cases.iter().any(|t| self.needs_post_return(&t.ty)), + TypeDefKind::Variant(t) => t + .cases + .iter() + .filter_map(|t| t.ty.as_ref()) + .any(|t| self.needs_post_return(t)), + TypeDefKind::Option(t) => self.needs_post_return(t), + TypeDefKind::Result(t) => [&t.ok, &t.err] + .iter() + .filter_map(|t| t.as_ref()) + .any(|t| self.needs_post_return(t)), + TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => false, + TypeDefKind::Future(_) | TypeDefKind::Stream(_) => unimplemented!(), + }, + + // TODO: this is probably not correct, unsure though as handles are + // in flux at the moment + Type::Handle(_) => false, + + Type::Bool + | Type::U8 + | Type::S8 + | Type::U16 + | Type::S16 + | Type::U32 + | Type::S32 + | Type::U64 + | Type::S64 + | Type::Float32 + | Type::Float64 + | Type::Char => false, + } + } + + /// Used in a similar manner as the `Interface::call` function except is + /// used to generate the `post-return` callback for `func`. + /// + /// This is only intended to be used in guest generators for exported + /// functions and will primarily generate `GuestDeallocate*` instructions, + /// plus others used as input to those instructions. + pub fn post_return(&self, func: &Function, bindgen: &mut impl Bindgen) { + Generator::new( + self, + AbiVariant::GuestExport, + LiftLower::LiftArgsLowerResults, + bindgen, + ) + .post_return(func); + } } struct Generator<'a, B: Bindgen> { @@ -1160,11 +1237,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { .sizes() .record(func.params.iter().map(|t| &t.1)); self.emit(&Instruction::GetArg { nth: 0 }); - self.emit(&Instruction::Free { - free: "canonical_abi_free", - size, - align, - }); + self.emit(&Instruction::GuestDeallocate { size, align }); } } @@ -1224,6 +1297,34 @@ impl<'a, B: Bindgen> Generator<'a, B> { ); } + fn post_return(&mut self, func: &Function) { + let sig = self.iface.wasm_signature(self.variant, func); + + // Currently post-return is only used for lists and lists are always + // returned indirectly through memory due to their flat representation + // having more than one type. Assert that a return pointer is used, + // though, in case this ever changes. + assert!(sig.retptr); + + self.emit(&Instruction::GetArg { nth: 0 }); + let addr = self.stack.pop().unwrap(); + for (offset, ty) in self + .bindgen + .sizes() + .field_offsets(func.results.iter_types()) + { + let offset = i32::try_from(offset).unwrap(); + self.deallocate(ty, addr.clone(), offset); + } + self.emit(&Instruction::Return { func, amt: 0 }); + + assert!( + self.stack.is_empty(), + "stack has {} items remaining", + self.stack.len() + ); + } + fn emit(&mut self, inst: &Instruction<'_>) { self.operands.clear(); self.results.clear(); @@ -1517,32 +1618,20 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&HandleOwnedFromI32 { ty }); } } - Type::String => { - let free = self.list_free(); - self.emit(&StringLift { free }); - } + Type::String => self.emit(&StringLift), Type::Id(id) => match &self.iface.types[id].kind { TypeDefKind::Type(t) => self.lift(t), TypeDefKind::List(element) => { - let free = self.list_free(); if self.is_char(element) || self.bindgen.is_list_canonical(self.iface, element) { - self.emit(&ListCanonLift { - element, - free, - ty: id, - }); + self.emit(&ListCanonLift { element, ty: id }); } else { self.push_block(); self.emit(&IterBasePointer); let addr = self.stack.pop().unwrap(); self.read_from_memory(element, addr, 0); self.finish_block(1); - self.emit(&ListLift { - element, - free, - ty: id, - }); + self.emit(&ListLift { element, ty: id }); } } TypeDefKind::Record(record) => { @@ -1669,16 +1758,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - fn list_free(&self) -> Option<&'static str> { - // Lifting the arguments of a defined import means that, if - // possible, the caller still retains ownership and we don't - // free anything. - match (self.variant, self.lift_lower) { - (AbiVariant::GuestImport, LiftLower::LiftArgsLowerResults) => None, - _ => Some("canonical_abi_free"), - } - } - fn write_to_memory(&mut self, ty: &Type, addr: B::Operand, offset: i32) { use Instruction::*; @@ -2077,6 +2156,137 @@ impl<'a, B: Bindgen> Generator<'a, B> { _ => false, } } + + fn deallocate(&mut self, ty: &Type, addr: B::Operand, offset: i32) { + use Instruction::*; + + // No need to execute any instructions if this type itself doesn't + // require any form of post-return. + if !self.iface.needs_post_return(ty) { + return; + } + + match *ty { + Type::Handle(_) => unimplemented!(), + + Type::String => { + self.stack.push(addr.clone()); + self.emit(&Instruction::I32Load { offset }); + self.stack.push(addr); + self.emit(&Instruction::I32Load { offset: offset + 4 }); + self.emit(&Instruction::GuestDeallocateString); + } + + Type::Bool + | Type::U8 + | Type::S8 + | Type::U16 + | Type::S16 + | Type::U32 + | Type::S32 + | Type::Char + | Type::U64 + | Type::S64 + | Type::Float32 + | Type::Float64 => {} + + Type::Id(id) => match &self.iface.types[id].kind { + TypeDefKind::Type(t) => self.deallocate(t, addr, offset), + + TypeDefKind::List(element) => { + self.push_block(); + self.emit(&IterBasePointer); + let elemaddr = self.stack.pop().unwrap(); + self.deallocate(element, elemaddr, 0); + self.finish_block(0); + + self.stack.push(addr.clone()); + self.emit(&Instruction::I32Load { offset }); + self.stack.push(addr); + self.emit(&Instruction::I32Load { offset: offset + 4 }); + self.emit(&Instruction::GuestDeallocateList { element }); + } + + TypeDefKind::Record(record) => { + self.deallocate_fields( + &record.fields.iter().map(|f| f.ty).collect::>(), + addr, + offset, + ); + } + TypeDefKind::Tuple(tuple) => { + self.deallocate_fields(&tuple.types, addr, offset); + } + + TypeDefKind::Flags(_) => {} + + TypeDefKind::Variant(variant) => { + self.deallocate_variant( + offset, + addr, + variant.tag(), + variant.cases.iter().map(|c| c.ty.as_ref()), + ); + self.emit(&GuestDeallocateVariant { + blocks: variant.cases.len(), + }); + } + + TypeDefKind::Option(t) => { + self.deallocate_variant(offset, addr, Int::U8, [None, Some(t)]); + self.emit(&GuestDeallocateVariant { blocks: 2 }); + } + + TypeDefKind::Result(e) => { + self.deallocate_variant(offset, addr, Int::U8, [e.ok.as_ref(), e.err.as_ref()]); + self.emit(&GuestDeallocateVariant { blocks: 2 }); + } + + TypeDefKind::Enum(_) => {} + + TypeDefKind::Union(union) => { + self.deallocate_variant( + offset, + addr, + union.tag(), + union.cases.iter().map(|c| Some(&c.ty)), + ); + self.emit(&GuestDeallocateVariant { + blocks: union.cases.len(), + }); + } + + TypeDefKind::Future(_) => todo!("read future from memory"), + TypeDefKind::Stream(_) => todo!("read stream from memory"), + }, + } + } + + fn deallocate_variant<'b>( + &mut self, + offset: i32, + addr: B::Operand, + tag: Int, + cases: impl IntoIterator> + Clone, + ) { + self.stack.push(addr.clone()); + self.load_intrepr(offset, tag); + let payload_offset = + offset + (self.bindgen.sizes().payload_offset(tag, cases.clone()) as i32); + for ty in cases { + self.push_block(); + if let Some(ty) = ty { + self.deallocate(ty, addr.clone(), payload_offset); + } + self.finish_block(0); + } + } + + fn deallocate_fields(&mut self, tys: &[Type], addr: B::Operand, offset: i32) { + for (field_offset, ty) in self.bindgen.sizes().field_offsets(tys) { + self.deallocate(ty, addr.clone(), offset + (field_offset as i32)); + } + } } fn cast(from: WasmType, to: WasmType) -> Bitcast { diff --git a/tests/runtime/lists/wasm.c b/tests/runtime/lists/wasm.c index e5833608c..58ae2166b 100644 --- a/tests/runtime/lists/wasm.c +++ b/tests/runtime/lists/wasm.c @@ -8,30 +8,8 @@ #include #include -// "custom allocator" which just keeps track of allocated bytes - -static size_t ALLOCATED_BYTES = 0; - -__attribute__((export_name("cabi_realloc"))) -void *cabi_realloc( void *ptr, size_t orig_size, size_t orig_align, size_t new_size) { - void *ret = realloc(ptr, new_size); - if (!ret) - abort(); - ALLOCATED_BYTES -= orig_size; - ALLOCATED_BYTES += new_size; - return ret; -} - -__attribute__((export_name("canonical_abi_free"))) -void canonical_abi_free(void *ptr, size_t size, size_t align) { - if (size > 0) { - ALLOCATED_BYTES -= size; - free(ptr); - } -} - uint32_t exports_allocated_bytes(void) { - return ALLOCATED_BYTES; + return 0; } void exports_test_imports() { @@ -306,7 +284,7 @@ void exports_list_param4(exports_list_list_string_t *a) { } void exports_list_result(exports_list_u8_t *ret0) { - ret0->ptr = cabi_realloc(NULL, 0, 1, 5); + ret0->ptr = malloc(5); ret0->len = 5; ret0->ptr[0] = 1; ret0->ptr[1] = 2; @@ -321,7 +299,7 @@ void exports_list_result2(exports_string_t *ret0) { void exports_list_result3(exports_list_string_t *ret0) { ret0->len = 2; - ret0->ptr = cabi_realloc(NULL, 0, alignof(exports_string_t), 2 * sizeof(exports_string_t)); + ret0->ptr = malloc(2 * sizeof(exports_string_t)); exports_string_dup(&ret0->ptr[0], "hello,"); exports_string_dup(&ret0->ptr[1], "world!");