Repository navigation
Free the parameter block of async exports in every generator - #4
Open
Aditya1404Sal wants to merge 4 commits into
Open
Aditya1404Sal wants to merge 4 commits into
Aditya1404Sal wants to merge 4 commits into
Conversation
Aditya1404Sal
marked this pull request as ready for review
October 27, 2026 08:15
Aditya1404Sal
marked this pull request as draft
October 27, 2026 08:52
Aditya1404Sal
marked this pull request as ready for review
October 29, 2026 14:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of GOL-987.
This brings the fix from bytecodealliance#1758 to this branch. It replaces the Rust-only fix that this PR had before. The upstream issue is bytecodealliance#1757.
Problem
When the parameters of an export flatten to more than 16 core values, the caller writes them into a block of the callee's linear memory. The caller allocates this block with the callee's
cabi_realloc. The callee must free it.The generated code frees this block only for sync exports. For async exports, nothing frees it, so each call leaks the block. In Golem, each method call of a Rust agent goes through the async export
golem:agent/guest.invoke. Each call leaks 136 bytes, and linear memory does not shrink.Change
wit-bindgen-corecannot free the block of an async export itself:.await, so a cancelled call never reaches it. In C, C# and MoonBit it is in a different function, where the block pointer is not available.So core now tells the generator about the block.
Instruction::CallInterfacehas a new field,params_block: Option<(ArchitectureSize, Alignment)>. Core sets it only for an async export whose parameters arrive in a block. Then the block pointer is the last operand, and core does not free the block. Sync exports do not change.Each generator frees the block after it has read all arguments and before the task can wait:
.await. A call that is cancelled while it waits also frees the block.return ret;.var task = XAsync(...);. At that point the arguments are managed copies.arg.Unpin()releases it.The change also fixes two errors in the C# generator for async exports. Both gave invalid C#:
boolor a record. Now each parameter has a simple name.stream<T>orfuture<T>parameter without its namespace. In the interop class, that name can refer to a different class or to no class. Now the type is fully qualified.Instructionis public API. ABindgenoutside this repo that matches onCallInterfacemust add..to its pattern. If it generates async exports, it must remove the extra operand and free the block.Differences from the upstream PR:
crates/test/src/go.rsadds the new codegen test to the list of tests that fail to link on stock Go, as upstream does. The list on this branch has no--pkg-namecase.Tests
The runtime test
async-many-argumentsis the same as upstream. It has a Rust runner and test components in Rust, C, C#, MoonBit and Go. For each case, the runner makes 2000 calls, reads a heap mark, makes 2000 more calls, and checks that the mark did not change. The cases are:optionandresultpayloads.todowhen the caller cancels it bytecodealliance/go-pkg#17).When core never announces the block, the test fails in Rust (144,000 more live bytes after 2000 calls), C (4 to 6 pages), MoonBit (4 to 8 pages) and C#. With this change, it passes in all five languages.
Other results:
go.rsentry and is an expected failure with it.cargo clippy --workspace --all-targets -- -D warningsandcargo fmt -- --checkpass.C# and Go ran on Linux arm64. For C# with WASI SDK 34, the test tool needs the arm64 compiler package and
-Wl,--no-stack-first; this PR does not change the test tool for that. MoonBit ran with moon 0.1.20261023, aftermoon fmtconverts the generatedmoon.mod.json.These tests fail with and without this change: C++ and C# codegen
outlined-lowering.witandoutlined-resources.wit, C++ runtimefixed-length-lists, and MoonBit codegen and runtimevariants. Thenumbersrunner does not compile with Rust 1.99 (deprecatedmin_value).Measured in Golem
A local Golem server ran a Rust agent with a counting allocator. Its method returns the live heap bytes.
🤖 Generated with Claude Code