Skip to content

Conversation

fitzgen
Copy link
Member

@fitzgen fitzgen commented Apr 1, 2025

Instead of bespoke code paths and structures for Wasm GC, this commit makes it so that we now reuse VM structures like VMMemoryDefinition and bounds-checking logic. Notably, we also reuse all the associated bounds-checking optimizations and, when possible, virtual-memory techniques to completely elide them.

Furthermore, this commit adds support for growing GC heaps, reusing the machinery for growing memories, and makes it so that GC heaps always start out empty. This allows us to properly delay allocating the GC heap's storage until a GC object is actually allocated.

Fixes #9350

@fitzgen fitzgen requested a review from alexcrichton April 1, 2025 18:28
@fitzgen fitzgen requested review from a team as code owners April 1, 2025 18:28
@fitzgen fitzgen requested review from cfallin and removed request for a team April 1, 2025 18:28
@fitzgen fitzgen force-pushed the gc-heap-config-knobs branch from f18aed4 to 087345c Compare April 1, 2025 18:30
@fitzgen fitzgen removed request for a team and cfallin April 1, 2025 18:30
@github-actions github-actions bot added cranelift Issues related to the Cranelift code generator cranelift:area:x64 Issues related to x64 codegen wasmtime:api Related to the API of the `wasmtime` crate itself wasmtime:ref-types Issues related to reference types and GC in Wasmtime labels Apr 1, 2025
Copy link

github-actions bot commented Apr 1, 2025

Subscribe to Label Action

cc @fitzgen

This issue or pull request has been labeled: "cranelift", "cranelift:area:x64", "wasmtime:api", "wasmtime:ref-types"

Thus the following users have been cc'd because of the following labels:

  • fitzgen: wasmtime:ref-types

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

Copy link
Member

@alexcrichton alexcrichton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've gotten through a chunk of this, but if possible I do really think it would be nice to split this apart. I realize it's extra work over what's already here to split this apart but it's pretty hard to review this all-at-once even though I was helping out for at least a chunk of it. If it's too hard it doesn't have to be split but I tend to get nervous when big changes to core runtime bits happen all in one large PR

@@ -92,7 +92,14 @@ pub struct InstanceAllocationRequest<'a> {
/// InstanceAllocationRequest, rather than on a &mut InstanceAllocationRequest
/// itself, because several use-sites require a split mut borrow on the
/// InstanceAllocationRequest.
pub struct StorePtr(Option<NonNull<dyn VMStore>>);
pub struct StorePtr(Option<crate::vm::SendSyncPtr<dyn VMStore>>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind leaving this as NonNull to avoid the cognitive overhead of using SendSyncPtr?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(un-resolving this since this is still SendSyncPtr?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh huh, I guess I lost this in a rebase or something, because I definitely undid this change at one point

Comment on lines 516 to 527
assert!(self.async_support());
if let Some(bytes_needed) = bytes_needed {
if self
.on_fiber(|store| store.maybe_async_grow_gc_heap(bytes_needed).is_ok())
.await
.unwrap_or(false)
{
return;
}
}

self.gc_async().await;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be inverted? First GC then maybe grow if necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to grow first because

  1. it is usually cheaper (just updating the accessible region of a mapping; even copying bytes to a new region is also cheaper) and

  2. if we swapped the order, then we could hit the following pathological case:

    // Assume: the GC heap has capacity for N objects, and we have N-1
    // objects allocated and held alive.
    
    // Allocate an object, now the GC heap is at capacity. We do not keep the
    // object live, however.
    drop(allocate());
    
    loop {
        // Upon entry of this loop, the GC heap is at capacity.
    
        // Allocate another object. This triggers a collect-or-else-grow, the
        // collection reclaims the previously allocated object, and then this
        // allocation succeeds without needing to grow the heap.
        drop(allocate());
    
        // At the end of this loop, the GC heap is still at capacity.
    }

    This scenario means we will trigger a GC on every allocation inside the loop which is super pathological. We should really only enter into this kind of behavior when there really is no more memory we can give the GC heap (and even then, the embedder probably wants to cancel the execution via async epochs/fuel at some point; may eventually make sense to add a GC limiter style API).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, but isn't this current behavior pathological too? GC heaps will, by default, balloon to 4G before a GC ever runs?

This makes me think that there's not an ideal answer one way or the other, but picking either extreme seems bad?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think growing to the limit before doing GC is actually what you want: you don't want to do GC if you can avoid it. Growing the heap is faster than doing a GC. There are knobs for limiting the max size of memories, including the GC heap, so unbounded growth isn't really a concern (also there is a hard bound of 4 GiB since the GC heap is a 32-bit memory). And we are likely going to throw away the whole store, and the GC heap along with it, before we ever need to collect, and doing that is also much faster than doing a collection.

fitzgen added a commit to fitzgen/wasmtime that referenced this pull request Apr 1, 2025
`VMRuntimeLimits` was renamed to `VMStoreContext` a little while back.

Split off from bytecodealliance#10503
fitzgen added a commit to fitzgen/wasmtime that referenced this pull request Apr 2, 2025
`VMRuntimeLimits` was renamed to `VMStoreContext` a little while back.

Split off from bytecodealliance#10503
github-merge-queue bot pushed a commit that referenced this pull request Apr 2, 2025
`VMRuntimeLimits` was renamed to `VMStoreContext` a little while back.

Split off from #10503
fitzgen added a commit to fitzgen/wasmtime that referenced this pull request Apr 2, 2025
fitzgen added a commit to fitzgen/wasmtime that referenced this pull request Apr 2, 2025
fitzgen added a commit to fitzgen/wasmtime that referenced this pull request Apr 2, 2025
fitzgen added a commit to fitzgen/wasmtime that referenced this pull request Apr 5, 2025
github-merge-queue bot pushed a commit that referenced this pull request Apr 5, 2025
…k` module to the top level of the crate (#10517)

Split off from #10503
fitzgen added a commit to fitzgen/wasmtime that referenced this pull request Apr 5, 2025
And let alias analysis dedupe and clean up multiple uses, instead of trying to
do that manually in our Wasm-to-CLIF frontend by loading the pointer once in the
entry block and trying to keep track of whether it is actually necessary to
pre-declare the pointer and all that.

Split off from bytecodealliance#10503
Gonzalosilvalde pushed a commit to Gonzalosilvalde/wasmtime that referenced this pull request Apr 5, 2025
github-merge-queue bot pushed a commit that referenced this pull request Apr 7, 2025
* Support adding additional capacity to `FreeList`s

Split out from #10503

* Add a test for `FreeList::add_capacity`

* Also test when old capacity is not a multiple of our alignment

* Add comments about alignment-rounding and free list capacity

* Don't run little example as a doc test
github-merge-queue bot pushed a commit that referenced this pull request Apr 7, 2025
…nt` (#10526)

And let alias analysis dedupe and clean up multiple uses, instead of trying to
do that manually in our Wasm-to-CLIF frontend by loading the pointer once in the
entry block and trying to keep track of whether it is actually necessary to
pre-declare the pointer and all that.

Split off from #10503
@fitzgen fitzgen force-pushed the gc-heap-config-knobs branch from f9470dc to bff10fe Compare April 11, 2025 19:16
@fitzgen fitzgen requested a review from alexcrichton April 11, 2025 19:41
@alexcrichton alexcrichton added this pull request to the merge queue Apr 11, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Apr 11, 2025
@fitzgen fitzgen added this pull request to the merge queue Apr 11, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Apr 11, 2025
@fitzgen fitzgen enabled auto-merge April 11, 2025 21:03
@fitzgen fitzgen added this pull request to the merge queue Apr 11, 2025
Merged via the queue into bytecodealliance:main with commit c22b3cb Apr 11, 2025
41 checks passed
@fitzgen fitzgen deleted the gc-heap-config-knobs branch April 11, 2025 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cranelift:area:x64 Issues related to x64 codegen cranelift Issues related to the Cranelift code generator fuzzing Issues related to our fuzzing infrastructure wasmtime:api Related to the API of the `wasmtime` crate itself wasmtime:c-api Issues pertaining to the C API. wasmtime:ref-types Issues related to reference types and GC in Wasmtime
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Reuse linear memories, and our code to emit their bound checks, for Wasm GC heaps
2 participants