Skip to content

Conversation

@graalvmbot
Copy link
Collaborator

This PR adds an implementation of the typed function references proposal (https://github.com/WebAssembly/function-references) behind the --wasm.TypedFunctionReferences option.

The biggest change is to GraalWasm's type system. The type system now, instead of being a closed enumeration of a few value types, is now open-ended and includes user-defined composite types (function types for now). In the engine, we still use scalars to represent types, but we switch from bytes to ints. Negative values represent predefined types (numerics, vectors and predefined abstract heap types), while non-negative values are type indices which point to a symbol table that holds the type's definition (see the WasmType javadoc for more details).

Aside from changing the data representation of value types, the way we compare them also changes. Since GraalWasm now has subtyping (typed function references are a subtype of funcref), it is not possible to compare types simply by equality but a subtype matching check must be made. At runtime, this happens in two places: when unchecked values come in from interop/JS and when calling a function using call_indirect. The subtype check is implemented by recursively traversing and comparing the structure of the types and the recursion does not sit well with PE, so I put it behind a @TruffleBoundary. In current benchmarks, we should not be hitting this, since we only invoke subtype matching when the types are not equal. I originally implemented the subtype check by constructing final tree-like data structures for the composite value types and then calling a virtual matches method (basically a mini AST interpreter) and I was hoping this would PE nicely, but I still ended up with non-inlined invokes in the compiled code so I dropped this. The code for that is still in the commit history.

Speaking of the commit history, I recommend reviewing the entire changeset. The commit history includes 3 reversions:

  • I originally dropped the use of type equivalence classes, since type equivalence is no longer sufficient for type checking (subtype checks must be made). I brought it back so that we have a fast path for call_indirect for cases where the types are equal.
  • I originally dropped exnref from ValueType, since the Wasm JS API spec does not list exnref as one of the possible values of ValueType. However, we will need to forbid JS from passing and receiving exnref values to/from wasm (that's part of the spec). For that, we will need JS to know whether something has an exnref value type and so that we are not forced to introduce another form of type reflection, I put exnref back into ValueType.
  • I originally implemented subtype matching by constructing tree representations of closed value types (with all type indices resolved). To avoid having to allocate and maintain more objects, I changed this back to using scalars for types. This is now restored.

As part of the PR, I also updated the revision of the official spec tests that we run and I fixed some minor unrelated issues. Since the main branch of the spec repo has now caught up with wasm-3.0, we now run only on a single revision of the spec tests.

Also fixed up code style in typed function references implementation.
Make the type checks around typed functions, global and table writes
more precise.
This makes sure we always dispatch on a compilation final constant.
The funcref type only admits wasm functions. External functions must go
through the import object, where they are wrapped in
ExecuteHostFunctionNode.
According to the wasm JS API spec, exnref is not a valid ValueType.
However, we will need to enforce that the types v128 and exnref never
cross the wasm and JS boundary and we will need to do so in JS (we allow
v128 values crossing from wasm to the spec test harness and other
embedders). Since we use ValueType as a simple form of type reflection,
we will need to have exnref so that GraalJS can detect exnref in wasm
type signatures.
…_indirect

This reverts back to using type equivalence classes. We use them for
fast type equality checks. In the optimistic case (expected type ==
actual type), this is enough and leads to a fast path that avoids
invoking the subtype matching on every indirect call. If types are not
equal, subtype matching is still performed.
@oracle-contributor-agreement oracle-contributor-agreement bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label Oct 25, 2025
@graalvmbot graalvmbot force-pushed the jmarsik/wasm-function-references branch from a59ac51 to ea73176 Compare October 25, 2025 13:48
This is called for by the official spec test "linking". We keep the
check only for modules eval'ed with EvalReturnsInstance, where it is
possible for the embedder to obtain a WasmInstance that will fail
linking. Without EvalReturnsInstance, instances must pass linking before
they are returned to the embedder.
These actions have side effects and their ordering can matter insofar as
one link action can fail and only the effects of those that came before
should be observable.
@graalvmbot graalvmbot force-pushed the jmarsik/wasm-function-references branch from ea73176 to 538847e Compare October 25, 2025 16:29
@graalvmbot graalvmbot merged commit 3d4eb2a into master Oct 27, 2025
13 checks passed
@graalvmbot graalvmbot deleted the jmarsik/wasm-function-references branch October 27, 2025 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Verified All contributors have signed the Oracle Contributor Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants