From fdeba12544d0b47e4566117db7f4b41ba80a3fa5 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 7 Feb 2025 10:20:18 -0800
Subject: [PATCH 01/21] Add `thread.spawn_indirect`

This change codifies the conclusions we arrived to in [#89]. It adds a
new way to spawn threads, `thread.spawn_indirect`, which retrieves the
thread start function from a table. This prompted me to rename
`thread.spawn` to `thread.spawn_ref`.

[#89]: https://github.com/WebAssembly/shared-everything-threads/issues/89
---
 design/mvp/Async.md        | 27 +++++++++++----------
 design/mvp/Binary.md       |  5 ++--
 design/mvp/CanonicalABI.md | 48 +++++++++++++++++++++++++++++++++++---
 design/mvp/Explainer.md    | 33 +++++++++++++++++++-------
 4 files changed, 86 insertions(+), 27 deletions(-)

diff --git a/design/mvp/Async.md b/design/mvp/Async.md
index f80fb4ca..db456cba 100644
--- a/design/mvp/Async.md
+++ b/design/mvp/Async.md
@@ -78,12 +78,11 @@ these languages' concurrency features are already bound (making the Component
 Model "just another OS" from the language toolchains' perspective).
 
 Moreover, this async ABI does not require components to use preemptive
-multi-threading ([`thread.spawn`]) in order to achieve concurrency. Instead,
-concurrency can be achieved by cooperatively switching between different
-logical tasks running on a single thread. This switching may require the use of
-[fibers] or a [CPS transform], but may also be avoided entirely when a
-component's producer toolchain is engineered to always return to an
-[event loop].
+multi-threading ([`thread.spawn*`]) in order to achieve concurrency. Instead,
+concurrency can be achieved by cooperatively switching between different logical
+tasks running on a single thread. This switching may require the use of [fibers]
+or a [CPS transform], but may also be avoided entirely when a component's
+producer toolchain is engineered to always return to an [event loop].
 
 To avoid partitioning the world along sync/async lines as mentioned in the
 Goals section, the Component Model allows *every* component-level function type
@@ -672,11 +671,11 @@ by declarative instantiation and `start` above.
 
 ## Interaction with multi-threading
 
-For now, the integration between multi-threading (via [`thread.spawn`]) and
-native async is limited. In particular, because all [lift and lower
-definitions] produce non-`shared` functions, any threads spawned by a component
-via `thread.spawn` will not be able to directly call imports (synchronously
-*or* asynchronously) and will thus have to use Core WebAssembly `atomics.*`
+For now, the integration between multi-threading (via [`thread.spawn*`]) and
+native async is limited. In particular, because all [lift and lower definitions]
+produce non-`shared` functions, any threads spawned by a component via
+`thread.spawn*` will not be able to directly call imports (synchronously *or*
+asynchronously) and will thus have to use Core WebAssembly `atomics.*`
 instructions to switch back to a non-`shared` function running on the "main"
 thread (i.e., whichever thread was used to call the component's exports).
 
@@ -693,8 +692,8 @@ composition story described above could naturally be extended to a
 sync+async+shared composition story, continuing to avoid the "what color is
 your function" problem (where `shared` is the [color]).
 
-Even without any use of `thread.new`, native async provides an opportunity to
-achieve some automatic parallelism "for free". In particular, due to the
+Even without any use of [`thread.spawn*`], native async provides an opportunity
+to achieve some automatic parallelism "for free". In particular, due to the
 shared-nothing nature of components, each component instance could be given a
 separate thread on which to interleave all tasks executing in that instance.
 Thus, in a cross-component call from `C1` to `C2`, `C2`'s task can run in a
@@ -750,7 +749,7 @@ comes after:
 [`yield`]: Explainer.md#-yield
 [`waitable-set.wait`]: Explainer.md#-waitable-setwait
 [`waitable-set.poll`]: Explainer.md#-waitable-setpoll
-[`thread.spawn`]: Explainer.md#-threadspawn
+[`thread.spawn*`]: Explainer.md#-threadspawnref
 [ESM-integration]: Explainer.md#ESM-integration
 
 [Canonical ABI Explainer]: CanonicalABI.md
diff --git a/design/mvp/Binary.md b/design/mvp/Binary.md
index 915afcc3..35c8ded0 100644
--- a/design/mvp/Binary.md
+++ b/design/mvp/Binary.md
@@ -286,9 +286,10 @@ canon    ::= 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx> => (canon lift
            | 0x01 0x00 f:<funcidx> opts:<opts>                   => (canon lower f opts (core func))
            | 0x02 rt:<typeidx>                                   => (canon resource.new rt (core func))
            | 0x03 rt:<typeidx>                                   => (canon resource.drop rt (core func))
-           | 0x07 rt:<typdidx>                                   => (canon resource.drop rt async (core func)) ๐Ÿ”€
+           | 0x07 rt:<typeidx>                                   => (canon resource.drop rt async (core func)) ๐Ÿ”€
            | 0x04 rt:<typeidx>                                   => (canon resource.rep rt (core func))
-           | 0x05 ft:<typeidx>                                   => (canon thread.spawn ft (core func)) ๐Ÿงต
+           | 0x05 ft:<typeidx>                                   => (canon thread.spawn_ref ft (core func)) ๐Ÿงต
+           | 0x24 ft:<typeidx> t:<core:tabidx>                   => (canon thread.spawn_indirect ft (table t) (core func)) ๐Ÿงต
            | 0x06                                                => (canon thread.available_parallelism (core func)) ๐Ÿงต
            | 0x08                                                => (canon backpressure.set (core func)) ๐Ÿ”€
            | 0x09 rs:<resultlist> opts:<opts>                    => (canon task.return rs opts (core func)) ๐Ÿ”€
diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index 27bd1f95..c7be3dcf 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3773,11 +3773,11 @@ async def canon_error_context_drop(task, i):
 ```
 
 
-### ๐Ÿงต `canon thread.spawn`
+### ๐Ÿงต `canon thread.spawn_ref`
 
 For a canonical definition:
 ```wat
-(canon thread.spawn (type $ft) (core func $st))
+(canon thread.spawn_ref (type $ft) (core func $st))
 ```
 validation specifies:
 * `$ft` must refer to a `shared` function type; initially, only the type `(func
@@ -3802,7 +3802,7 @@ thread which:
 In pseudocode, `$st` looks like:
 
 ```python
-def canon_thread_spawn(f, c):
+def canon_thread_spawn_ref(f, c):
   trap_if(f is None)
   if DETERMINISTIC_PROFILE:
     return [-1]
@@ -3820,6 +3820,48 @@ def canon_thread_spawn(f, c):
 ```
 
 
+### ๐Ÿงต `canon thread.spawn_indirect`
+
+For a canonical definition:
+```wat
+(canon thread.spawn_indirect (type $ft) (table $t) (core func $st))
+```
+validation specifies:
+* `$ft` must refer to a `shared` function type; initially, only the type `(func
+  shared (param $c i32))` is allowed (see explanation in `thread.spawn_ref`
+  above)
+* `$t` must refer to a table containing `ft`-typed items
+* `$st` is given type `(func (param $i i32) (param $c i32) (result $e
+  i32))`.
+
+Calling `$st` retrieves a function `$f` of type `$ft` from table `$t`. If that
+succeeds, it spawns a thread which:
+  - invokes `$f` with `$c`
+  - executes `$f` until completion or trap in a `shared` context as described by
+    the [shared-everything threads] proposal.
+
+In pseudocode, `$st` looks like:
+
+```python
+def canon_thread_spawn_indirect(t, i, c):
+  trap_if(t[i] is None)
+  f = t[i]
+  if DETERMINISTIC_PROFILE:
+    return [-1]
+
+  def thread_start():
+    try:
+      f(c)
+    except CoreWebAssemblyException:
+      trap()
+
+  if spawn(thread_start):
+    return [0]
+  else:
+    return [-1]
+```
+
+
 ### ๐Ÿงต `canon thread.available_parallelism`
 
 For a canonical definition:
diff --git a/design/mvp/Explainer.md b/design/mvp/Explainer.md
index b34146b8..bd2b174e 100644
--- a/design/mvp/Explainer.md
+++ b/design/mvp/Explainer.md
@@ -1438,7 +1438,8 @@ canon ::= ...
         | (canon error-context.new <canonopt>* (core func <id>?))
         | (canon error-context.debug-message <canonopt>* (core func <id>?))
         | (canon error-context.drop (core func <id>?))
-        | (canon thread.spawn <typeidx> (core func <id>?)) ๐Ÿงต
+        | (canon thread.spawn_ref <typeidx> (core func <id>?)) ๐Ÿงต
+        | (canon thread.spawn_indirect <typeidx> <core:tableidx> (core func <id>?)) ๐Ÿงต
         | (canon thread.available_parallelism (core func <id>?)) ๐Ÿงต
 ```
 
@@ -1945,19 +1946,34 @@ thread management. These are specified as built-ins and not core WebAssembly
 instructions because browsers expect this functionality to come from existing
 Web/JS APIs.
 
-###### ๐Ÿงต `thread.spawn`
+###### ๐Ÿงต `thread.spawn_ref`
 
 | Synopsis                   |                                                           |
 | -------------------------- | --------------------------------------------------------- |
 | Approximate WIT signature  | `func<FuncT>(f: FuncT, c: FuncT.params[0]) -> bool`       |
 | Canonical ABI signature    | `[f:(ref null (func shared (param i32))) c:i32] -> [i32]` |
 
-The `thread.spawn` built-in spawns a new thread by invoking the shared function
-`f` while passing `c` to it, returning whether a thread was successfully
-spawned. While it's designed to allow different types in the future, the type
-of `c` is currently hard-coded to always be `i32`.
+The `thread.spawn_ref` built-in spawns a new thread by invoking the shared
+function `f` while passing `c` to it, returning whether a thread was
+successfully spawned. While it's designed to allow different types in the
+future, the type of `c` is currently hard-coded to always be `i32`.
 
-(See also [`canon_thread_spawn`] in the Canonical ABI explainer.)
+(See also [`canon_thread_spawn_ref`] in the Canonical ABI explainer.)
+
+
+###### ๐Ÿงต `thread.spawn_indirect`
+
+| Synopsis                   |                                                   |
+| -------------------------- | ------------------------------------------------- |
+| Approximate WIT signature  | `func<FuncT>(i: i32, c: FuncT.params[0]) -> bool` |
+| Canonical ABI signature    | `[i:i32 c:i32] -> [i32]`                          |
+
+The `thread.spawn_indirect` built-in spawns a new thread by retrieving the
+shared function `f` from a table using index `i` (much like the `call_indirect`
+core instruction). Once `f` is retrieved, this built-in operates like
+`thread.spawn_ref` above, including the limitations on `f`'s parameters.
+
+(See also [`canon_thread_spawn_indirect`] in the Canonical ABI explainer.)
 
 ###### ๐Ÿงต `thread.available_parallelism`
 
@@ -2806,7 +2822,8 @@ For some use-case-focused, worked examples, see:
 [`canon_error_context_new`]: CanonicalABI.md#-canon-error-contextnew
 [`canon_error_context_debug_message`]: CanonicalABI.md#-canon-error-contextdebug-message
 [`canon_error_context_drop`]: CanonicalABI.md#-canon-error-contextdrop
-[`canon_thread_spawn`]: CanonicalABI.md#-canon-theadspawn
+[`canon_thread_spawn_ref`]: CanonicalABI.md#-canon-threadspawnref
+[`canon_thread_spawn_indirect`]: CanonicalABI.md#-canon-threadspawnindirect
 [`canon_thread_available_parallelism`]: CanonicalABI.md#-canon-threadavailable_parallelism
 [`pack_async_copy_result`]: CanonicalABI.md#-canon-streamfuturereadwrite
 [the `close` built-ins]: CanonicalABI.md#-canon-streamfutureclose-readablewritable

From dc325958637a6f5df2fc194dc15289e5e2547b86 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Thu, 27 Feb 2025 14:59:06 -0800
Subject: [PATCH 02/21] Fix `shared` syntax for ``

---
 design/mvp/CanonicalABI.md | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index c7be3dcf..ca240493 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3780,16 +3780,16 @@ For a canonical definition:
 (canon thread.spawn_ref (type $ft) (core func $st))
 ```
 validation specifies:
-* `$ft` must refer to a `shared` function type; initially, only the type `(func
-  shared (param $c i32))` is allowed (see explanation below)
+* `$ft` must refer to a `shared` function type; initially, only the type
+  `(shared (func (param $c i32)))` is allowed (see explanation below)
 * `$st` is given type `(func (param $f (ref null $ft)) (param $c i32) (result $e
   i32))`.
 
 > Note: ideally, a thread could be spawned with [arbitrary thread parameters].
 > Currently, that would require additional work in the toolchain to support so,
-> for simplicity, the current proposal simply fixes a single `i32` parameter type.
-> However, `thread.spawn` could be extended to allow arbitrary thread parameters
-> in the future, once it's concretely beneficial to the toolchain.
+> for simplicity, the current proposal simply fixes a single `i32` parameter
+> type. However, `thread.spawn_ref` could be extended to allow arbitrary thread
+> parameters in the future, once it's concretely beneficial to the toolchain.
 > The inclusion of `$ft` ensures backwards compatibility for when arbitrary
 > parameters are allowed.
 
@@ -3827,9 +3827,9 @@ For a canonical definition:
 (canon thread.spawn_indirect (type $ft) (table $t) (core func $st))
 ```
 validation specifies:
-* `$ft` must refer to a `shared` function type; initially, only the type `(func
-  shared (param $c i32))` is allowed (see explanation in `thread.spawn_ref`
-  above)
+* `$ft` must refer to a `shared` function type; initially, only the type
+  `(shared (func shared (param $c i32)))` is allowed (see explanation in
+  `thread.spawn_ref` above)
 * `$t` must refer to a table containing `ft`-typed items
 * `$st` is given type `(func (param $i i32) (param $c i32) (result $e
   i32))`.

From 903cdb10c9218238966c0a0761cb3ec29a39562f Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Thu, 27 Feb 2025 15:02:32 -0800
Subject: [PATCH 03/21] Fix table type: it contains shared `funcref`s

---
 design/mvp/CanonicalABI.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index ca240493..53daacb8 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3830,12 +3830,12 @@ validation specifies:
 * `$ft` must refer to a `shared` function type; initially, only the type
   `(shared (func shared (param $c i32)))` is allowed (see explanation in
   `thread.spawn_ref` above)
-* `$t` must refer to a table containing `ft`-typed items
+* `$t` must refer to a table with type `(table (ref null (shared func)) shared)`
 * `$st` is given type `(func (param $i i32) (param $c i32) (result $e
   i32))`.
 
-Calling `$st` retrieves a function `$f` of type `$ft` from table `$t`. If that
-succeeds, it spawns a thread which:
+Calling `$st` retrieves a reference to function `$f` from table `$t` and checks
+that `$f` is of type `$ft`. If that succeeds, it spawns a thread which:
   - invokes `$f` with `$c`
   - executes `$f` until completion or trap in a `shared` context as described by
     the [shared-everything threads] proposal.

From 03d166d099edbc7157a5544d1272ec728ad5fccb Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:50:01 -0800
Subject: [PATCH 04/21] Update design/mvp/CanonicalABI.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/CanonicalABI.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index 53daacb8..91250c96 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3834,7 +3834,7 @@ validation specifies:
 * `$st` is given type `(func (param $i i32) (param $c i32) (result $e
   i32))`.
 
-Calling `$st` retrieves a reference to function `$f` from table `$t` and checks
+Calling `$spawn_indirect` retrieves a reference to function `$f` from table `$tbl` and checks
 that `$f` is of type `$ft`. If that succeeds, it spawns a thread which:
   - invokes `$f` with `$c`
   - executes `$f` until completion or trap in a `shared` context as described by

From d394e76d84511e0aae21d48936b7dd3ba5d9c807 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:50:14 -0800
Subject: [PATCH 05/21] Update design/mvp/CanonicalABI.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/CanonicalABI.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index 91250c96..5192b192 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3830,7 +3830,7 @@ validation specifies:
 * `$ft` must refer to a `shared` function type; initially, only the type
   `(shared (func shared (param $c i32)))` is allowed (see explanation in
   `thread.spawn_ref` above)
-* `$t` must refer to a table with type `(table (ref null (shared func)) shared)`
+* `$tbl` must refer to a table with type `(table (ref null (shared func)) shared)`
 * `$st` is given type `(func (param $i i32) (param $c i32) (result $e
   i32))`.
 

From 90ac9d4a7a32b95a5d22832c3420050dd800aa81 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:52:08 -0800
Subject: [PATCH 06/21] Update design/mvp/CanonicalABI.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/CanonicalABI.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index 5192b192..d961afe6 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3828,7 +3828,7 @@ For a canonical definition:
 ```
 validation specifies:
 * `$ft` must refer to a `shared` function type; initially, only the type
-  `(shared (func shared (param $c i32)))` is allowed (see explanation in
+  `(shared (func (param $c i32)))` is allowed (see explanation in
   `thread.spawn_ref` above)
 * `$tbl` must refer to a table with type `(table (ref null (shared func)) shared)`
 * `$st` is given type `(func (param $i i32) (param $c i32) (result $e

From 0e4defbbe26b9a13adbc22542f21b4101902018e Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:52:17 -0800
Subject: [PATCH 07/21] Update design/mvp/CanonicalABI.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/CanonicalABI.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index d961afe6..f083545d 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3840,7 +3840,7 @@ that `$f` is of type `$ft`. If that succeeds, it spawns a thread which:
   - executes `$f` until completion or trap in a `shared` context as described by
     the [shared-everything threads] proposal.
 
-In pseudocode, `$st` looks like:
+In pseudocode, `$spawn_indirect` looks like:
 
 ```python
 def canon_thread_spawn_indirect(t, i, c):

From 7b9a6ba3181dfbea28ab0eebb15464cfd2a0712a Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:52:38 -0800
Subject: [PATCH 08/21] Update design/mvp/CanonicalABI.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/CanonicalABI.md | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index f083545d..ede8daad 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3843,9 +3843,10 @@ that `$f` is of type `$ft`. If that succeeds, it spawns a thread which:
 In pseudocode, `$spawn_indirect` looks like:
 
 ```python
-def canon_thread_spawn_indirect(t, i, c):
-  trap_if(t[i] is None)
-  f = t[i]
+def canon_thread_spawn_indirect(ft, tbl, i, c):
+  f = tbl[i]
+  trap_if(f is None)
+  trap_if(f.type != ft)
   if DETERMINISTIC_PROFILE:
     return [-1]
 

From 1c5f3c9457bc7ad72458d7f46d269b5db761256b Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:52:51 -0800
Subject: [PATCH 09/21] Update design/mvp/Explainer.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/Explainer.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/Explainer.md b/design/mvp/Explainer.md
index bd2b174e..eff0834f 100644
--- a/design/mvp/Explainer.md
+++ b/design/mvp/Explainer.md
@@ -1951,7 +1951,7 @@ Web/JS APIs.
 | Synopsis                   |                                                           |
 | -------------------------- | --------------------------------------------------------- |
 | Approximate WIT signature  | `func<FuncT>(f: FuncT, c: FuncT.params[0]) -> bool`       |
-| Canonical ABI signature    | `[f:(ref null (func shared (param i32))) c:i32] -> [i32]` |
+| Canonical ABI signature    | `[f:(ref null (shared (func (param i32))) c:i32] -> [i32]` |
 
 The `thread.spawn_ref` built-in spawns a new thread by invoking the shared
 function `f` while passing `c` to it, returning whether a thread was

From 4ac8c1cb84b94164e29818ead896fd1fdb78db82 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:53:20 -0800
Subject: [PATCH 10/21] Update design/mvp/Binary.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/Binary.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/Binary.md b/design/mvp/Binary.md
index 35c8ded0..beb1b419 100644
--- a/design/mvp/Binary.md
+++ b/design/mvp/Binary.md
@@ -289,7 +289,7 @@ canon    ::= 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx> => (canon lift
            | 0x07 rt:<typeidx>                                   => (canon resource.drop rt async (core func)) ๐Ÿ”€
            | 0x04 rt:<typeidx>                                   => (canon resource.rep rt (core func))
            | 0x05 ft:<typeidx>                                   => (canon thread.spawn_ref ft (core func)) ๐Ÿงต
-           | 0x24 ft:<typeidx> t:<core:tabidx>                   => (canon thread.spawn_indirect ft (table t) (core func)) ๐Ÿงต
+           | 0x24 ft:<typeidx> t:<core:tableidx>                 => (canon thread.spawn_indirect ft (table t) (core func)) ๐Ÿงต
            | 0x06                                                => (canon thread.available_parallelism (core func)) ๐Ÿงต
            | 0x08                                                => (canon backpressure.set (core func)) ๐Ÿ”€
            | 0x09 rs:<resultlist> opts:<opts>                    => (canon task.return rs opts (core func)) ๐Ÿ”€

From 34a557974cbbc0c30a1f2e31754d5c27621ad627 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:53:42 -0800
Subject: [PATCH 11/21] Update design/mvp/Explainer.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/Explainer.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/design/mvp/Explainer.md b/design/mvp/Explainer.md
index eff0834f..363def2f 100644
--- a/design/mvp/Explainer.md
+++ b/design/mvp/Explainer.md
@@ -1969,7 +1969,8 @@ future, the type of `c` is currently hard-coded to always be `i32`.
 | Canonical ABI signature    | `[i:i32 c:i32] -> [i32]`                          |
 
 The `thread.spawn_indirect` built-in spawns a new thread by retrieving the
-shared function `f` from a table using index `i` (much like the `call_indirect`
+shared function `f` from a table using index `i` and traps if the type of `f` is
+not equal to `FuncT` (much like the `call_indirect`
 core instruction). Once `f` is retrieved, this built-in operates like
 `thread.spawn_ref` above, including the limitations on `f`'s parameters.
 

From 9e9087e0ff9059438716f8a642f14e7d64580da1 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:53:51 -0800
Subject: [PATCH 12/21] Update design/mvp/CanonicalABI.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/CanonicalABI.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index ede8daad..cb2ec8a3 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3777,7 +3777,7 @@ async def canon_error_context_drop(task, i):
 
 For a canonical definition:
 ```wat
-(canon thread.spawn_ref (type $ft) (core func $st))
+(canon thread.spawn_ref (type $ft) (core func $spawn_ref))
 ```
 validation specifies:
 * `$ft` must refer to a `shared` function type; initially, only the type

From 1588a270a7929709ec7c521fae69bf244d9451e3 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:54:26 -0800
Subject: [PATCH 13/21] Update design/mvp/CanonicalABI.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/CanonicalABI.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index cb2ec8a3..48790ab5 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3782,7 +3782,7 @@ For a canonical definition:
 validation specifies:
 * `$ft` must refer to a `shared` function type; initially, only the type
   `(shared (func (param $c i32)))` is allowed (see explanation below)
-* `$st` is given type `(func (param $f (ref null $ft)) (param $c i32) (result $e
+* `$spawn_ref` is given type `(func (param $f (ref null $ft)) (param $c i32) (result $e
   i32))`.
 
 > Note: ideally, a thread could be spawned with [arbitrary thread parameters].

From deadb369ec8d1c93e3071f11c1497798d8c74699 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:54:35 -0800
Subject: [PATCH 14/21] Update design/mvp/CanonicalABI.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/CanonicalABI.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index 48790ab5..fe186408 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3831,7 +3831,7 @@ validation specifies:
   `(shared (func (param $c i32)))` is allowed (see explanation in
   `thread.spawn_ref` above)
 * `$tbl` must refer to a table with type `(table (ref null (shared func)) shared)`
-* `$st` is given type `(func (param $i i32) (param $c i32) (result $e
+* `$spawn_indirect` is given type `(func (param $i i32) (param $c i32) (result $e
   i32))`.
 
 Calling `$spawn_indirect` retrieves a reference to function `$f` from table `$tbl` and checks

From 83f20a741e8498565e5651c5beb15c2ea3042c4a Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 08:57:28 -0800
Subject: [PATCH 15/21] Move `thread.*` encodings to the 0x40+ space

---
 design/mvp/Binary.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/design/mvp/Binary.md b/design/mvp/Binary.md
index beb1b419..394b78fa 100644
--- a/design/mvp/Binary.md
+++ b/design/mvp/Binary.md
@@ -288,9 +288,6 @@ canon    ::= 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx> => (canon lift
            | 0x03 rt:<typeidx>                                   => (canon resource.drop rt (core func))
            | 0x07 rt:<typeidx>                                   => (canon resource.drop rt async (core func)) ๐Ÿ”€
            | 0x04 rt:<typeidx>                                   => (canon resource.rep rt (core func))
-           | 0x05 ft:<typeidx>                                   => (canon thread.spawn_ref ft (core func)) ๐Ÿงต
-           | 0x24 ft:<typeidx> t:<core:tableidx>                 => (canon thread.spawn_indirect ft (table t) (core func)) ๐Ÿงต
-           | 0x06                                                => (canon thread.available_parallelism (core func)) ๐Ÿงต
            | 0x08                                                => (canon backpressure.set (core func)) ๐Ÿ”€
            | 0x09 rs:<resultlist> opts:<opts>                    => (canon task.return rs opts (core func)) ๐Ÿ”€
            | 0x0a 0x7f i:<u32>                                   => (canon context.get i32 i (core func)) ๐Ÿ”€
@@ -319,6 +316,9 @@ canon    ::= 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx> => (canon lift
            | 0x21 async?:<async>? m:<core:memidx>                => (canon waitable-set.poll async? (memory m) (core func)) ๐Ÿ”€
            | 0x22                                                => (canon waitable-set.drop (core func)) ๐Ÿ”€
            | 0x23                                                => (canon waitable.join (core func)) ๐Ÿ”€
+           | 0x40 ft:<typeidx>                                   => (canon thread.spawn_ref ft (core func)) ๐Ÿงต
+           | 0x41 ft:<typeidx> t:<core:tableidx>                 => (canon thread.spawn_indirect ft (table t) (core func)) ๐Ÿงต
+           | 0x42                                                => (canon thread.available_parallelism (core func)) ๐Ÿงต
 async?   ::= 0x00                                                =>
            | 0x01                                                => async
 opts     ::= opt*:vec(<canonopt>)                                => opt*

From fbbceb17141f3a30fd48f52b4002857c46480ea7 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 09:00:21 -0800
Subject: [PATCH 16/21] Fix Markdown links

---
 design/mvp/Async.md        | 2 +-
 design/mvp/CanonicalABI.md | 3 ++-
 design/mvp/Explainer.md    | 4 ++--
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/design/mvp/Async.md b/design/mvp/Async.md
index db456cba..ff33b48a 100644
--- a/design/mvp/Async.md
+++ b/design/mvp/Async.md
@@ -749,7 +749,7 @@ comes after:
 [`yield`]: Explainer.md#-yield
 [`waitable-set.wait`]: Explainer.md#-waitable-setwait
 [`waitable-set.poll`]: Explainer.md#-waitable-setpoll
-[`thread.spawn*`]: Explainer.md#-threadspawnref
+[`thread.spawn*`]: Explainer.md#-threadspawn_ref
 [ESM-integration]: Explainer.md#ESM-integration
 
 [Canonical ABI Explainer]: CanonicalABI.md
diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index fe186408..f15d5542 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -54,7 +54,8 @@ being specified here.
   * [`canon error-context.new`](#-canon-error-contextnew) ๐Ÿ”€
   * [`canon error-context.debug-message`](#-canon-error-contextdebug-message) ๐Ÿ”€
   * [`canon error-context.drop`](#-canon-error-contextdrop) ๐Ÿ”€
-  * [`canon thread.spawn`](#-canon-threadspawn) ๐Ÿงต
+  * [`canon thread.spawn_ref`](#-canon-threadspawn_ref) ๐Ÿงต
+  * [`canon thread.spawn_indirect`](#-canon-threadspawn_indirect) ๐Ÿงต
   * [`canon thread.available_parallelism`](#-canon-threadavailable_parallelism) ๐Ÿงต
 
 ## Supporting definitions
diff --git a/design/mvp/Explainer.md b/design/mvp/Explainer.md
index 363def2f..980fe4e4 100644
--- a/design/mvp/Explainer.md
+++ b/design/mvp/Explainer.md
@@ -2823,8 +2823,8 @@ For some use-case-focused, worked examples, see:
 [`canon_error_context_new`]: CanonicalABI.md#-canon-error-contextnew
 [`canon_error_context_debug_message`]: CanonicalABI.md#-canon-error-contextdebug-message
 [`canon_error_context_drop`]: CanonicalABI.md#-canon-error-contextdrop
-[`canon_thread_spawn_ref`]: CanonicalABI.md#-canon-threadspawnref
-[`canon_thread_spawn_indirect`]: CanonicalABI.md#-canon-threadspawnindirect
+[`canon_thread_spawn_ref`]: CanonicalABI.md#-canon-threadspawn_ref
+[`canon_thread_spawn_indirect`]: CanonicalABI.md#-canon-threadspawn_indirect
 [`canon_thread_available_parallelism`]: CanonicalABI.md#-canon-threadavailable_parallelism
 [`pack_async_copy_result`]: CanonicalABI.md#-canon-streamfuturereadwrite
 [the `close` built-ins]: CanonicalABI.md#-canon-streamfutureclose-readablewritable

From cb429113b42004abd5ca28b2c2632f62bcb29bdd Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 09:03:39 -0800
Subject: [PATCH 17/21] Fix line wrapping

---
 design/mvp/CanonicalABI.md | 16 +++++++++-------
 design/mvp/Explainer.md    | 19 ++++++++++---------
 2 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index f15d5542..6a16fda4 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3783,8 +3783,8 @@ For a canonical definition:
 validation specifies:
 * `$ft` must refer to a `shared` function type; initially, only the type
   `(shared (func (param $c i32)))` is allowed (see explanation below)
-* `$spawn_ref` is given type `(func (param $f (ref null $ft)) (param $c i32) (result $e
-  i32))`.
+* `$spawn_ref` is given type `(func (param $f (ref null $ft)) (param $c i32)
+  (result $e i32))`.
 
 > Note: ideally, a thread could be spawned with [arbitrary thread parameters].
 > Currently, that would require additional work in the toolchain to support so,
@@ -3831,12 +3831,14 @@ validation specifies:
 * `$ft` must refer to a `shared` function type; initially, only the type
   `(shared (func (param $c i32)))` is allowed (see explanation in
   `thread.spawn_ref` above)
-* `$tbl` must refer to a table with type `(table (ref null (shared func)) shared)`
-* `$spawn_indirect` is given type `(func (param $i i32) (param $c i32) (result $e
-  i32))`.
+* `$tbl` must refer to a table with type `(table (ref null (shared func))
+  shared)`
+* `$spawn_indirect` is given type `(func (param $i i32) (param $c i32) (result
+  $e i32))`.
 
-Calling `$spawn_indirect` retrieves a reference to function `$f` from table `$tbl` and checks
-that `$f` is of type `$ft`. If that succeeds, it spawns a thread which:
+Calling `$spawn_indirect` retrieves a reference to function `$f` from table
+`$tbl` and checks that `$f` is of type `$ft`. If that succeeds, it spawns a
+thread which:
   - invokes `$f` with `$c`
   - executes `$f` until completion or trap in a `shared` context as described by
     the [shared-everything threads] proposal.
diff --git a/design/mvp/Explainer.md b/design/mvp/Explainer.md
index 980fe4e4..ae6dc677 100644
--- a/design/mvp/Explainer.md
+++ b/design/mvp/Explainer.md
@@ -1948,9 +1948,9 @@ Web/JS APIs.
 
 ###### ๐Ÿงต `thread.spawn_ref`
 
-| Synopsis                   |                                                           |
-| -------------------------- | --------------------------------------------------------- |
-| Approximate WIT signature  | `func<FuncT>(f: FuncT, c: FuncT.params[0]) -> bool`       |
+| Synopsis                   |                                                            |
+| -------------------------- | ---------------------------------------------------------- |
+| Approximate WIT signature  | `func<FuncT>(f: FuncT, c: FuncT.params[0]) -> bool`        |
 | Canonical ABI signature    | `[f:(ref null (shared (func (param i32))) c:i32] -> [i32]` |
 
 The `thread.spawn_ref` built-in spawns a new thread by invoking the shared
@@ -1970,9 +1970,9 @@ future, the type of `c` is currently hard-coded to always be `i32`.
 
 The `thread.spawn_indirect` built-in spawns a new thread by retrieving the
 shared function `f` from a table using index `i` and traps if the type of `f` is
-not equal to `FuncT` (much like the `call_indirect`
-core instruction). Once `f` is retrieved, this built-in operates like
-`thread.spawn_ref` above, including the limitations on `f`'s parameters.
+not equal to `FuncT` (much like the `call_indirect` core instruction). Once `f`
+is retrieved, this built-in operates like `thread.spawn_ref` above, including
+the limitations on `f`'s parameters.
 
 (See also [`canon_thread_spawn_indirect`] in the Canonical ABI explainer.)
 
@@ -1983,14 +1983,15 @@ core instruction). Once `f` is retrieved, this built-in operates like
 | Approximate WIT signature  | `func() -> u32` |
 | Canonical ABI signature    | `[] -> [i32]`   |
 
-The `thread.available_parallelism` built-in returns the number of threads that can be
-expected to execute in parallel.
+The `thread.available_parallelism` built-in returns the number of threads that
+can be expected to execute in parallel.
 
 The concept of "available parallelism" corresponds is sometimes referred to
 as "hardware concurrency", such as in [`navigator.hardwareConcurrency`] in
 JavaScript.
 
-(See also [`canon_thread_available_parallelism`] in the Canonical ABI explainer.)
+(See also [`canon_thread_available_parallelism`] in the Canonical ABI
+explainer.)
 
 ### ๐Ÿช™ Value Definitions
 

From 3189cccf8fffb19d75a42259b04316566f92409d Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 09:04:32 -0800
Subject: [PATCH 18/21] Update design/mvp/CanonicalABI.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/CanonicalABI.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index 6a16fda4..4dde1217 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3825,7 +3825,7 @@ def canon_thread_spawn_ref(f, c):
 
 For a canonical definition:
 ```wat
-(canon thread.spawn_indirect (type $ft) (table $t) (core func $st))
+(canon thread.spawn_indirect $ft $tbl (core func $spawn_indirect))
 ```
 validation specifies:
 * `$ft` must refer to a `shared` function type; initially, only the type

From 1c558c78a874c67bf919b2535c32d2fe94892cc3 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 09:35:03 -0800
Subject: [PATCH 19/21] Fix up `thread.spawn_ref` variables

---
 design/mvp/CanonicalABI.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index 4dde1217..0d1291f6 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -3778,7 +3778,7 @@ async def canon_error_context_drop(task, i):
 
 For a canonical definition:
 ```wat
-(canon thread.spawn_ref (type $ft) (core func $spawn_ref))
+(canon thread.spawn_ref $ft (core func $spawn_ref))
 ```
 validation specifies:
 * `$ft` must refer to a `shared` function type; initially, only the type
@@ -3794,13 +3794,13 @@ validation specifies:
 > The inclusion of `$ft` ensures backwards compatibility for when arbitrary
 > parameters are allowed.
 
-Calling `$st` checks that the reference `$f` is not null. Then, it spawns a
-thread which:
+Calling `$spawn_ref` checks that the reference `$f` is not null. Then, it spawns
+a thread which:
   - invokes `$f` with `$c`
   - executes `$f` until completion or trap in a `shared` context as described by
     the [shared-everything threads] proposal.
 
-In pseudocode, `$st` looks like:
+In pseudocode, `$spawn_ref` looks like:
 
 ```python
 def canon_thread_spawn_ref(f, c):

From e4ea201602e0160a96e5b327078ad9427cc3ca30 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 11:44:04 -0800
Subject: [PATCH 20/21] Update design/mvp/Explainer.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/Explainer.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/Explainer.md b/design/mvp/Explainer.md
index ae6dc677..b344cdfc 100644
--- a/design/mvp/Explainer.md
+++ b/design/mvp/Explainer.md
@@ -1965,7 +1965,7 @@ future, the type of `c` is currently hard-coded to always be `i32`.
 
 | Synopsis                   |                                                   |
 | -------------------------- | ------------------------------------------------- |
-| Approximate WIT signature  | `func<FuncT>(i: i32, c: FuncT.params[0]) -> bool` |
+| Approximate WIT signature  | `func<FuncT>(i: u32, c: FuncT.params[0]) -> bool` |
 | Canonical ABI signature    | `[i:i32 c:i32] -> [i32]`                          |
 
 The `thread.spawn_indirect` built-in spawns a new thread by retrieving the

From eb4f9c7555248e977aee8921de84e98d79e065f4 Mon Sep 17 00:00:00 2001
From: Andrew Brown <andrew.brown@intel.com>
Date: Fri, 28 Feb 2025 11:44:13 -0800
Subject: [PATCH 21/21] Update design/mvp/Binary.md

Co-authored-by: Luke Wagner <mail@lukewagner.name>
---
 design/mvp/Binary.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/design/mvp/Binary.md b/design/mvp/Binary.md
index 394b78fa..6501b450 100644
--- a/design/mvp/Binary.md
+++ b/design/mvp/Binary.md
@@ -317,7 +317,7 @@ canon    ::= 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx> => (canon lift
            | 0x22                                                => (canon waitable-set.drop (core func)) ๐Ÿ”€
            | 0x23                                                => (canon waitable.join (core func)) ๐Ÿ”€
            | 0x40 ft:<typeidx>                                   => (canon thread.spawn_ref ft (core func)) ๐Ÿงต
-           | 0x41 ft:<typeidx> t:<core:tableidx>                 => (canon thread.spawn_indirect ft (table t) (core func)) ๐Ÿงต
+           | 0x41 ft:<typeidx> tbl:<core:tableidx>               => (canon thread.spawn_indirect ft tbl (core func)) ๐Ÿงต
            | 0x42                                                => (canon thread.available_parallelism (core func)) ๐Ÿงต
 async?   ::= 0x00                                                =>
            | 0x01                                                => async