Skip to content

Generalize __wasm_init_task to a "task hook" - #2603

Merged
alexcrichton merged 1 commit into
bytecodealliance:mainfrom
alexcrichton:new-task-intrinsics
Aug 14, 2026
Merged

Generalize __wasm_init_task to a "task hook"#2603
alexcrichton merged 1 commit into
bytecodealliance:mainfrom
alexcrichton:new-task-intrinsics

Conversation

@alexcrichton

Copy link
Copy Markdown
Member

This commit refactors wit-component's handling of __wasm_init_task and __wasm_init_async_task into a "task hook" and adds it to more locations. The purpose of this refactor is to enable correct stack management in wasi-libc in the wasip3 ABI where stacks may need to be dynamically allocated in some situations. For this use case the previous init hooks were not sufficient because they didn't run in all contexts (such as post-return) nor did they hook completion of a task to deallocate a stack.

The new __wasm_task_hook function is still invoked in the same place as the previous initialization hooks, but it's additionally invoked at the end of a task and in more places. This intrinsic takes a single i32 parameter which is where the hook is being invoked. The locations are:

  • 0 - at the start of a synchronously lifted function.
  • 1 - at the end of a synchronously lifted function (before post-return if that's specified).
  • 2 - at the start of an async-lifted function, either stackful or not.
  • 3 - at the start of an async-lifted's callback option.
  • 4 - when an async-lifted entrypoint returns, or a callback option returns, and the task is blocking or yielding.
  • 5 - when an async-lifted function completes.
  • 6 - when _initialize is originally invoked.
  • 7 - when _initialize completes.
  • 8 - when a resource destructor is invoked.
  • 9 - when a resource destructor completes.
  • 10 - when a post-return hook is invoked.
  • 11 - when a post-return hook completes.

The goal of this change is to enable wasi-libc to correctly manage stacks in a WASIp3 world. This isn't necessary in a WASIp2 world with a global stack pointer because everything "falls out" of the stack pointer being in the main module, threads not existing, and the component model's reentrancy/blocking rules enable safe usage of the global. For WASIp3, however, a global stack pointer cannot be used due to threads, and so context.{get,set} slots are used instead, and these are fresh for all tasks and need initialization. The above hooks enable wasi-libc to primarily use the "main stack" where possible, but there are scenarios where that isn't possible such as for cabi_realloc and for sync-reentrant tasks.

The majority of this implementation is in the fixup module and this has additionally been tested against a local copy of wasi-libc. I've written previously-failing tests in wasi-libc which expose where stack management today is insufficient, and with this change, and an updated wasi-libc with this new intrinsic, all the tests now pass.

One final change I plan to do after this is to additionally add hooks for the cabi_realloc function. Right now that's required to do its own stack management, but it would be more consistent if it were folded into the same infrastructure for hooks. That'll be a bit of a broader change, though, so it's deferred to a future commit.

This commit refactors `wit-component`'s handling of `__wasm_init_task`
and `__wasm_init_async_task` into a "task hook" and adds it to more
locations. The purpose of this refactor is to enable correct stack
management in wasi-libc in the wasip3 ABI where stacks may need to be
dynamically allocated in some situations. For this use case the previous
init hooks were not sufficient because they didn't run in all contexts
(such as post-return) nor did they hook completion of a task to
deallocate a stack.

The new `__wasm_task_hook` function is still invoked in the same place
as the previous initialization hooks, but it's additionally invoked at
the end of a task and in more places. This intrinsic takes a single
`i32` parameter which is where the hook is being invoked. The locations
are:

* 0 - at the start of a synchronously lifted function.
* 1 - at the end of a synchronously lifted function (before post-return
  if that's specified).
* 2 - at the start of an async-lifted function, either stackful or not.
* 3 - at the start of an async-lifted's `callback` option.
* 4 - when an async-lifted entrypoint returns, or a `callback` option
  returns, and the task is blocking or yielding.
* 5 - when an async-lifted function completes.
* 6 - when `_initialize` is originally invoked.
* 7 - when `_initialize` completes.
* 8 - when a resource destructor is invoked.
* 9 - when a resource destructor completes.
* 10 - when a post-return hook is invoked.
* 11 - when a post-return hook completes.

The goal of this change is to enable wasi-libc to correctly manage
stacks in a WASIp3 world. This isn't necessary in a WASIp2 world with a
`global` stack pointer because everything "falls out" of the stack
pointer being in the main module, threads not existing, and the
component model's reentrancy/blocking rules enable safe usage of the
global. For WASIp3, however, a `global` stack pointer cannot be used due
to threads, and so `context.{get,set}` slots are used instead, and these
are fresh for all tasks and need initialization. The above hooks enable
wasi-libc to primarily use the "main stack" where possible, but there
are scenarios where that isn't possible such as for `cabi_realloc` and
for sync-reentrant tasks.

The majority of this implementation is in the `fixup` module and this
has additionally been tested against a local copy of wasi-libc. I've
written previously-failing tests in wasi-libc which expose where stack
management today is insufficient, and with this change, and an updated
wasi-libc with this new intrinsic, all the tests now pass.

One final change I plan to do after this is to additionally add hooks
for the `cabi_realloc` function. Right now that's required to do its own
stack management, but it would be more consistent if it were folded into
the same infrastructure for hooks. That'll be a bit of a broader change,
though, so it's deferred to a future commit.
@alexcrichton
alexcrichton requested a review from dicej August 14, 2026 15:55
@alexcrichton
alexcrichton requested a review from a team as a code owner August 14, 2026 15:55
@alexcrichton
alexcrichton added this pull request to the merge queue Aug 14, 2026
Merged via the queue into bytecodealliance:main with commit 5201ffe Aug 14, 2026
37 checks passed
@alexcrichton
alexcrichton deleted the new-task-intrinsics branch August 14, 2026 17:35
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