Skip to content

Give each GC struct field and array element its own alias region - #14351

Merged
alexcrichton merged 2 commits into
bytecodealliance:mainfrom
fitzgen:gc-type-alias-regions
Sep 17, 2026
Merged

alexcrichton merged 2 commits into
bytecodealliance:mainfrom
fitzgen:gc-type-alias-regions

Conversation

@fitzgen

@fitzgen fitzgen commented Sep 16, 2026

Copy link
Copy Markdown
Member

We have to take care to name the same region for a field that is shared between sub- and supertypes.

This is a ~0.3% speed up (in terms of instructions retired; my laptop is too noisy to get stable wall time numbers) on execution for the GC-using sightglass benchmarks. Note that we don't do dead-store elimination for GC object accesses because they are marked as trapping accesses, so this is just enabling more redundant-load elimination and store-to-load forwarding.

This is also (surprisingly) a ~2-3% speed up for compile times for the GC-using sightglass benchmarks, presumably because we eliminate more loads and therefore have less instructions to process through the rest of compilation.

This commit does not introduce alias regions for the different fields inside a GC object header or the array length; that is left for follow up commits.

@fitzgen
fitzgen requested review from a team as code owners September 16, 2026 21:20
@fitzgen
fitzgen requested review from alexcrichton and removed request for a team September 16, 2026 21:20
Comment on lines +1516 to +1526
let access = if is_exn {
GcAccess::ExnPayload {
exn_ty: struct_ty,
field,
}
} else {
GcAccess::StructField {
ty: struct_ty,
field,
}
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With the current split/interning of types, is this split necessary? I'd have thought that struct_ty is already unique across exceptions/structs of the same structure. For example above the internal composite_type field is already different across exceptions/structs.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For structs we need to walk up the subtyping chain to find the type that introduced the field. For exceptions, there is no subtyping chain to walk. This split lets us funnel each type to the appropriate code path. I suppose we could try to unify them, and exceptions would just stop after one iteration of the subtype chain walk, since they never have a supertype, but I don't think we really gain anything from doing that other than removing just this single if, but then also we'd muddy the water a bit for exactly what we're accessing (which we also aren't gaining a ton from right now, admittedly, but I do appreciate not losing precision when there is no reason to, so I think changing anything here is a bit of a wash on either side, so I'd ultimately prefer to leave it as-is unless you feel strongly about this).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Definitely don't feel strong, it just felt weird to single out exceptions here when the desired properties otherwise fall out of not specializing around them.

Comment thread crates/cranelift/src/func_environ/gc.rs Outdated
/// retain internal assertion metadata to report if such a trap happens. This
/// is here to ensure that in the face of heap corruption that there's no
/// possible UB within Cranelift and/or the runtime.
fn gc_memflags(&mut self, func: &mut ir::Function) -> ir::MemFlagsData {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Perhaps rename this to gc_header_memflags to avoid mis-use?

Comment on lines +300 to +301
/// An access of one of an array's elements, by the type at the access site.
ArrayElements { ty: ModuleInternedTypeIndex },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it worth it trying to perhaps future-proof against the https://github.com/WebAssembly/multibyte-array-access proposal here? In that world I think we'll have to canonical all arrays-of-scalars to one GcAccess region, right?

We probably won't implement that proposal for some time, but if we were to implement that in ~6 months I'd be worried that we wouldn't have any reason to come back and handle this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Is it worth it trying to perhaps future-proof against the https://github.com/WebAssembly/multibyte-array-access proposal here? In that world I think we'll have to canonical all arrays-of-scalars to one GcAccess region, right?

I don't believe so, because we will still know the type of the array object even if we are accessing its elements as u64 instead of u8 or whatever, and we choose the alias region based on the array object's type, not the array's elements' type: with this PR today, we already use different alias regions for two different (array i8) (as long as they really are two different types in two different subtyping hierarchies after canonicalization, e.g. due to rec groups). So we should already be future proof, afaik.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh right, good point!

@fitzgen
fitzgen enabled auto-merge September 17, 2026 16:10
We have to take care to name the same region for a field that is shared between
sub- and supertypes.

This is a ~0.3% speed up (in terms of instructions retired; my laptop is too
noisy to get stable wall time numbers) on execution for the GC-using sightglass
benchmarks. Note that we don't do dead-store elimination for GC object accesses
because they are marked as trapping accesses, so this is just enabling more
redundant-load elimination and store-to-load forwarding.

This is also (surprisingly) a ~2-3% speed up for compile times for the GC-using
sightglass benchmarks, presumably because we eliminate more loads and therefore
have less instructions to process through the rest of compilation.

This commit does not introduce alias regions for the different fields inside a
GC object header or the array length; that is left for follow up commits.
@fitzgen
fitzgen force-pushed the gc-type-alias-regions branch from 0c5f68d to a63f13e Compare September 17, 2026 17:41
@fitzgen
fitzgen added this pull request to the merge queue Sep 17, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 17, 2026
@alexcrichton
alexcrichton added this pull request to the merge queue Sep 17, 2026
Merged via the queue into bytecodealliance:main with commit 6f3af14 Sep 17, 2026
54 checks passed
@fitzgen
fitzgen deleted the gc-type-alias-regions branch September 17, 2026 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants