Skip to content

Commit

Permalink
Deallocate allocs on exports
Browse files Browse the repository at this point in the history
Signed-off-by: James Sturtevant <[email protected]>
  • Loading branch information
jsturtevant committed Jan 30, 2025
1 parent ec93af4 commit 5c94410
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 54 deletions.
175 changes: 123 additions & 52 deletions crates/csharp/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
// );
}

Instruction::ListCanonLower { element, realloc } => {
Instruction::ListCanonLower { element, .. } => {
let list: &String = &operands[0];
match self.interface_gen.direction {
Direction::Import => {
Expand All @@ -738,29 +738,20 @@ impl Bindgen for FunctionBindgen<'_, '_> {
results.push(format!("({list}).Length"));
}
Direction::Export => {
let (_, ty) = list_element_info(element);
let address = self.locals.tmp("address");
let buffer = self.locals.tmp("buffer");
let gc_handle = self.locals.tmp("gcHandle");
let size = self.interface_gen.csharp_gen.sizes.size(element).size_wasm32();
let byte_length = self.locals.tmp("byteLength");
uwrite!(
self.src,
"
byte[] {buffer} = new byte[({size}) * {list}.Length];
Buffer.BlockCopy({list}.ToArray(), 0, {buffer}, 0, ({size}) * {list}.Length);
var {gc_handle} = GCHandle.Alloc({buffer}, GCHandleType.Pinned);
var {address} = {gc_handle}.AddrOfPinnedObject();
var {byte_length} = ({size}) * {list}.Length;
var {address} = NativeMemory.Alloc((nuint)({byte_length}));
{list}.AsSpan().CopyTo(new Span<{ty}>({address},{byte_length}));
"
);

if realloc.is_none() {
self.needs_cleanup = true;
uwrite!(
self.src,
"
cleanups.Add(()=> {gc_handle}.Free());
");
}
results.push(format!("((IntPtr)({address})).ToInt32()"));
results.push(format!("(int)({address})"));
results.push(format!("{list}.Length"));
}
}
Expand All @@ -785,33 +776,45 @@ impl Bindgen for FunctionBindgen<'_, '_> {

Instruction::StringLower { realloc } => {
let op = &operands[0];
let interop_string = self.locals.tmp("interopString");
let str_ptr = self.locals.tmp("strPtr");
let utf8_bytes = self.locals.tmp("utf8Bytes");
let length = self.locals.tmp("length");
let gc_handle = self.locals.tmp("gcHandle");
uwriteln!(
self.src,
"
var {utf8_bytes} = Encoding.UTF8.GetBytes({op});
var {length} = {utf8_bytes}.Length;
var {gc_handle} = GCHandle.Alloc({utf8_bytes}, GCHandleType.Pinned);
var {interop_string} = {gc_handle}.AddrOfPinnedObject();
"
);

if realloc.is_none() {
results.push(format!("{interop_string}.ToInt32()"));
uwriteln!(
self.src,
"
var {utf8_bytes} = Encoding.UTF8.GetBytes({op});
var {length} = {utf8_bytes}.Length;
var {gc_handle} = GCHandle.Alloc({utf8_bytes}, GCHandleType.Pinned);
var {str_ptr} = {gc_handle}.AddrOfPinnedObject();
"
);

self.needs_cleanup = true;
uwrite!(
self.src,
"
cleanups.Add(()=> {gc_handle}.Free());
");
"
);
results.push(format!("{str_ptr}.ToInt32()"));
} else {
results.push(format!("{interop_string}.ToInt32()"));
let string_span = self.locals.tmp("stringSpan");
uwriteln!(
self.src,
"
var {string_span} = {op}.AsSpan();
var {length} = Encoding.UTF8.GetByteCount({string_span});
var {str_ptr} = NativeMemory.Alloc((nuint){length});
Encoding.UTF8.GetBytes({string_span}, new Span<byte>({str_ptr}, {length}));
"
);
results.push(format!("(int){str_ptr}"));
}
results.push(format!("{length}"));

results.push(format!("{length}"));
if FunctionKind::Freestanding == *self.kind || self.interface_gen.direction == Direction::Export {
self.interface_gen.require_interop_using("System.Text");
self.interface_gen.require_interop_using("System.Runtime.InteropServices");
Expand All @@ -834,7 +837,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
));
}

Instruction::ListLower { element, .. } => {
Instruction::ListLower { element, realloc } => {
let Block {
body,
results: block_results,
Expand All @@ -859,22 +862,37 @@ impl Bindgen for FunctionBindgen<'_, '_> {
);
let ret_area = self.locals.tmp("retArea");

self.needs_cleanup = true;
uwrite!(
self.src,
"
void* {address};
if (({size} * {list}.Count) < 1024) {{
var {ret_area} = stackalloc {element_type}[({array_size}*{list}.Count)+1];
{address} = (void*)(((int){ret_area}) + ({align} - 1) & -{align});
}}
else
{{
var {buffer_size} = {size} * (nuint){list}.Count;
{address} = NativeMemory.AlignedAlloc({buffer_size}, {align});
cleanups.Add(()=> NativeMemory.AlignedFree({address}));
}}
match realloc {
None => {
self.needs_cleanup = true;
uwrite!(self.src,
"
void* {address};
if (({size} * {list}.Count) < 1024) {{
var {ret_area} = stackalloc {element_type}[({array_size}*{list}.Count)+1];
{address} = (void*)(((int){ret_area}) + ({align} - 1) & -{align});
}}
else
{{
var {buffer_size} = {size} * (nuint){list}.Count;
{address} = NativeMemory.AlignedAlloc({buffer_size}, {align});
}}
cleanups.Add(()=> NativeMemory.AlignedFree({address}));
"
);
}
Some(_) => {
uwrite!(self.src,
"
var {buffer_size} = {size} * (nuint){list}.Count;
void* {address} = NativeMemory.AlignedAlloc({buffer_size}, {align});
"
);
}
}

uwrite!(self.src,
"
for (int {index} = 0; {index} < {list}.Count; ++{index}) {{
{ty} {block_element} = {list}[{index}];
int {base} = (int){address} + ({index} * {size});
Expand Down Expand Up @@ -1047,19 +1065,72 @@ impl Bindgen for FunctionBindgen<'_, '_> {
Instruction::Malloc { .. } => unimplemented!(),

Instruction::GuestDeallocate { .. } => {
uwriteln!(self.src, r#"Console.WriteLine("TODO: deallocate buffer for indirect parameters");"#);
// the original alloc here comes from cabi_realloc implementation (wasi-libc in .net)
uwriteln!(self.src, r#"NativeMemory.Free((void*){});"#, operands[0]);
}

Instruction::GuestDeallocateString => {
uwriteln!(self.src, r#"Console.WriteLine("TODO: deallocate buffer for string");"#);
uwriteln!(self.src, r#"NativeMemory.Free((void*){});"#, operands[0]);
}

Instruction::GuestDeallocateVariant { .. } => {
uwriteln!(self.src, r#"Console.WriteLine("TODO: deallocate buffer for variant");"#);
Instruction::GuestDeallocateVariant { blocks } => {
let cases = self
.blocks
.drain(self.blocks.len() - blocks..)
.enumerate()
.map(|(i, Block { body, results, .. })| {
assert!(results.is_empty());

format!(
"case {i}: {{
{body}
break;
}}"
)
})
.collect::<Vec<_>>()
.join("\n");

let op = &operands[0];

uwrite!(
self.src,
"
switch ({op}) {{
{cases}
}}
"
);
}

Instruction::GuestDeallocateList { .. } => {
uwriteln!(self.src, r#"Console.WriteLine("TODO: deallocate buffer for list");"#);
Instruction::GuestDeallocateList { element: element_type } => {
let Block {
body,
results: block_results,
base,
element: _,
} = self.blocks.pop().unwrap();
assert!(block_results.is_empty());

let address = &operands[0];
let length = &operands[1];
let size = self.interface_gen.csharp_gen.sizes.size(element_type).size_wasm32();

if !body.trim().is_empty() {
let index = self.locals.tmp("index");

uwrite!(
self.src,
"
for (int {index} = 0; {index} < {length}; ++{index}) {{
int {base} = (int){address} + ({index} * {size});
{body}
}}
"
);
}

uwriteln!(self.src, r#"NativeMemory.Free((void*){});"#, operands[0]);
}

Instruction::HandleLower {
Expand Down
3 changes: 1 addition & 2 deletions crates/csharp/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use crate::function::FunctionBindgen;
use crate::function::ResourceInfo;
use crate::world_generator::CSharp;
use heck::{ToShoutySnakeCase, ToUpperCamelCase};
use std::any::type_name;
use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use std::ops::Deref;
Expand Down Expand Up @@ -489,7 +488,7 @@ impl InterfaceGenerator<'_> {
self.csharp_interop_src,
r#"
[UnmanagedCallersOnly(EntryPoint = "cabi_post_{export_name}")]
{access} static void cabi_post_{interop_name}({params}) {{
{access} static unsafe void cabi_post_{interop_name}({params}) {{
{src}
}}
"#
Expand Down

0 comments on commit 5c94410

Please sign in to comment.