@@ -620,58 +620,68 @@ which to use by default.
620620
621621### Async Import ABI
622622
623- Given an imported WIT function :
623+ Given these imported WIT functions (using the fixed-length-list feature 🔧) :
624624``` wit
625625world w {
626- import foo: func(s: string) -> string;
626+ import foo: func(s: string) -> u32;
627+ import bar: func(s: string) -> string;
628+ import baz: func(t: tuple<u64; 5>) -> string;
629+ import quux: func(t: tuple<u32; 17>) -> string;
627630}
628631```
629- the default sync import function signature is :
632+ the default/synchronous lowered import function signatures are :
630633``` wat
631634;; sync
632- (func (param $s-ptr i32) (param $s-len i32) (param $out i32))
635+ (func $foo (param $s-ptr i32) (param $s-len i32) (result i32))
636+ (func $bar (param $s-ptr i32) (param $s-len i32) (param $out-ptr i32))
637+ (func $baz (param i64 i64 i64 i64 i64) (param $out-ptr i32))
638+ (func $quux (param $in-ptr i32) (param $out-ptr i32))
633639```
634- where ` $out ` must be a 4-byte-aligned pointer into linear memory into which the
635- 8-byte (pointer, length) of the returned string will be stored.
636-
637- The new async import function signature is:
640+ Here: ` foo ` , ` bar ` and ` baz ` pass their parameters as "flattened" core value
641+ types while ` quux ` passes its parameters via the ` $in-ptr ` linear memory
642+ pointer (due to the Canonical ABI limitation of 16 maximum flattened
643+ parameters). Similarly, ` foo ` returns its result as a single core value while
644+ ` bar ` , ` baz ` and ` quux ` return their results via the ` $out-ptr ` linear memory
645+ pointer (due to the current Canonical ABI limitation of 1 maximum flattened
646+ result).
647+
648+ The corresponding asynchronous lowered import function signatures are:
638649``` wat
639650;; async
640- (func (param $in i32) (param $out i32) (result i32))
651+ (func $foo (param $s-ptr i32) (param $s-len i32) (param $out-ptr i32) (result i32))
652+ (func $bar (param $s-ptr i32) (param $s-len i32) (param $out-ptr i32) (result i32))
653+ (func $baz (param $in-ptr i32) (param $out-ptr i32) (result i32))
654+ (func $quux (param $in-ptr i32) (param $out-ptr i32) (result i32))
641655```
642- where ` $in ` must be a 4-byte-aligned pointer into linear memory from which the
643- 8-byte (pointer, length) of the string argument will be loaded and ` $out ` works
644- the same as in the synchronous case. What's different, however, is * when* ` $in `
645- and ` $out ` are read or written. In a synchronous call, they are always read or
646- written before the call returns. In an asynchronous call, there is a set of
647- possibilities indicated by the ` (result i32) ` value:
648- * If the returned ` i32 ` is ` 2 ` , then the call returned eagerly without
649- blocking and so ` $in ` has been read and ` $out ` has been written.
650- * Otherwise, the high 28 bits of the ` i32 ` are the index of a new ` Subtask `
651- in the current component instance's table. The low 4 bits indicate how far
652- the callee made it before blocking:
653- * If ` 1 ` , the callee didn't even start (due to backpressure), and thus
654- neither ` $in ` nor ` $out ` have been accessed yet.
655- * If ` 2 ` , the callee started by reading ` $in ` , but blocked before writing
656- ` $out ` .
657-
658- The async signature ` (func (param i32 i32) (result i32)) ` is the same for
659- almost all WIT function types since the ABI stores everything in linear memory.
660- However, there are three special cases:
661- * If the WIT parameter list is empty, ` $in ` is removed.
662- * If the WIT parameter list flattens to exactly 1 core value type (` i32 ` or
663- otherwise), ` $in ` uses that core value type and the argument is passed
664- by value.
665- * If the WIT result is empty, ` $out ` is removed.
666-
667- For example:
656+ Comparing signatures, the differences are:
657+ * Async-lowered functions have a maximum of 4 flat parameters (not 16).
658+ * Async-lowered functions always return their value via linear memory pointer.
659+ * Async-lowered functions always have a single ` i32 ` "status" code.
660+
661+ Additionally, * when* the parameter and result pointers are read/written depends
662+ on the status code:
663+ * If the low 4 bits of the status are ` 0 ` , the call didn't even start and so
664+ ` $in-ptr ` hasn't been read and ` $out-ptr ` hasn't been written and the high
665+ 28 bits are the index of a new async subtask to wait on.
666+ * If the low 4 bits of the status are ` 1 ` , the call started, ` $in-ptr ` was
667+ read, but ` $out-ptr ` hasn't been written and the high 28 bits are the index
668+ of a new async subtask to wait on.
669+ * If the low 4 bits of the status are ` 2 ` , the call returned and so ` $in-ptr `
670+ and ` $out-ptr ` have been read/written and the high 28 bits are ` 0 ` because
671+ there is no async subtask to wait on.
672+
673+ When a parameter/result pointer hasn't yet been read/written, the async caller
674+ must take care to keep the region of memory allocated to the call until
675+ receiving an event indicating that the async subtask has started/returned.
676+
677+ Other example asynchronous lowered signatures:
678+
668679| WIT function type | Async ABI |
669680| ----------------------------------------- | --------------------- |
670681| ` func() ` | ` (func (result i32)) ` |
671- | ` func() -> string ` | ` (func (param $out i32) (result i32)) ` |
672- | ` func(s: string) ` | ` (func (param $in i32) (result i32)) ` |
673- | ` func(x: f32) -> f32 ` | ` (func (param $in f32) (param $out i32) (result i32)) ` |
674- | ` func(x: list<list<u8>>) -> list<string> ` | ` (func (param $in i32) (param $out i32) (result i32)) ` |
682+ | ` func() -> string ` | ` (func (param $out-ptr i32) (result i32)) ` |
683+ | ` func(x: f32) -> f32 ` | ` (func (param $x f32) (param $out-ptr i32) (result i32)) ` |
684+ | ` func(s: string, t: string) ` | ` (func (param $s-ptr i32) (param $s-len i32) (result $t-ptr i32) (param $t-len i32) (result i32)) ` |
675685
676686` future ` and ` stream ` can appear anywhere in the parameter or result types. For example:
677687``` wit
@@ -689,11 +699,11 @@ the synchronous ABI has signature:
689699```
690700and the asynchronous ABI has the signature:
691701``` wat
692- (func (param $in i32) (param $out i32) (result i32))
702+ (func (param $f i32) (param $out-ptr i32) (result i32))
693703```
694- where, according to the above rules, ` $in ` is the index of a future in the
695- current component instance's table (not a pointer to one) while ` $out ` is a
696- pointer to a linear memory location that will receive an ` i32 ` index.
704+ where ` $f ` is the index of a future (not a pointer to one) while while
705+ ` $out-ptr ` is a pointer to a linear memory location that will receive an ` i32 `
706+ index.
697707
698708For the runtime semantics of this ` i32 ` index, see ` lift_stream ` ,
699709` lift_future ` , ` lower_stream ` and ` lower_future ` in the [ Canonical ABI
@@ -792,7 +802,7 @@ with `...` to focus on the overall flow of function calls:
792802 (core module $Main
793803 (import "libc" "mem" (memory 1))
794804 (import "libc" "realloc" (func (param i32 i32 i32 i32) (result i32)))
795- (import "" "fetch" (func $fetch (param i32 i32) (result i32)))
805+ (import "" "fetch" (func $fetch (param i32 i32 i32 ) (result i32)))
796806 (import "" "waitable-set.new" (func $new_waitable_set (result i32)))
797807 (import "" "waitable-set.wait" (func $wait (param i32 i32) (result i32)))
798808 (import "" "waitable.join" (func $join (param i32 i32)))
@@ -806,7 +816,7 @@ with `...` to focus on the overall flow of function calls:
806816 ...
807817 loop
808818 ...
809- call $fetch ;; pass a pointer-to- string and pointer-to-list-of-bytes outparam
819+ call $fetch ;; pass a string pointer, string length and pointer-to-list-of-bytes outparam
810820 ... ;; ... and receive the index of a new async subtask
811821 global.get $wsi
812822 call $join ;; ... and add it to the waitable set
@@ -884,7 +894,7 @@ not externally-visible behavior.
884894 (core module $Main
885895 (import "libc" "mem" (memory 1))
886896 (import "libc" "realloc" (func (param i32 i32 i32 i32) (result i32)))
887- (import "" "fetch" (func $fetch (param i32 i32) (result i32)))
897+ (import "" "fetch" (func $fetch (param i32 i32 i32 ) (result i32)))
888898 (import "" "waitable-set.new" (func $new_waitable_set (result i32)))
889899 (import "" "waitable.join" (func $join (param i32 i32)))
890900 (import "" "task.return" (func $task_return (param i32 i32)))
@@ -897,7 +907,7 @@ not externally-visible behavior.
897907 ...
898908 loop
899909 ...
900- call $fetch ;; pass a pointer-to- string and pointer-to-list-of-bytes outparam
910+ call $fetch ;; pass a string pointer, string length and pointer-to-list-of-bytes outparam
901911 ... ;; ... and receive the index of a new async subtask
902912 global.get $wsi
903913 call $join ;; ... and add it to the waitable set
0 commit comments