From 05d06d16fd55c15562f10be0fce1b321513709c4 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 15 Nov 2024 13:31:02 -0700 Subject: [PATCH] more wast async support and more tests This also fixes a bug in `wasmprinter` keeping track of core functions. Signed-off-by: Joel Dice --- crates/wasmprinter/src/component.rs | 48 ++++++++++++++++++- crates/wast/src/component/binary.rs | 4 ++ crates/wast/src/component/expand.rs | 9 +++- crates/wast/src/component/func.rs | 30 ++++++++++++ crates/wast/src/component/resolve.rs | 6 ++- crates/wast/src/lib.rs | 1 + tests/local/component-model-async/lift.wast | 35 ++++++++++++++ tests/local/component-model-async/lower.wast | 26 ++++++++++ .../component-model-async/lift.wast.json | 25 +++++++--- .../component-model-async/lift.wast/2.print | 32 +++++++++++++ .../component-model-async/lower.wast.json | 18 +++++++ .../component-model-async/lower.wast/0.print | 22 +++++++++ 12 files changed, 246 insertions(+), 10 deletions(-) create mode 100644 tests/local/component-model-async/lower.wast create mode 100644 tests/snapshots/local/component-model-async/lift.wast/2.print create mode 100644 tests/snapshots/local/component-model-async/lower.wast.json create mode 100644 tests/snapshots/local/component-model-async/lower.wast/0.print diff --git a/crates/wasmprinter/src/component.rs b/crates/wasmprinter/src/component.rs index afa3d64823..d8805da027 100644 --- a/crates/wasmprinter/src/component.rs +++ b/crates/wasmprinter/src/component.rs @@ -933,7 +933,9 @@ impl Printer<'_, '_> { self.print_idx(&state.component.type_names, type_index)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::TaskWait { async_, memory } => { self.start_group("core func ")?; @@ -946,7 +948,9 @@ impl Printer<'_, '_> { self.print_idx(&state.component.type_names, memory)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::TaskPoll { async_, memory } => { self.start_group("core func ")?; @@ -959,7 +963,9 @@ impl Printer<'_, '_> { self.print_idx(&state.component.type_names, memory)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::TaskYield { async_ } => { self.start_group("core func ")?; @@ -971,7 +977,9 @@ impl Printer<'_, '_> { } self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::SubtaskDrop => { self.start_group("core func ")?; @@ -980,7 +988,9 @@ impl Printer<'_, '_> { self.start_group("canon subtask.drop")?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::StreamNew { ty } => { self.start_group("core func ")?; @@ -990,7 +1000,9 @@ impl Printer<'_, '_> { self.print_idx(&state.component.type_names, ty)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::StreamRead { ty, options } => { self.start_group("core func ")?; @@ -1002,7 +1014,9 @@ impl Printer<'_, '_> { self.print_canonical_options(state, &options)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::StreamWrite { ty, options } => { self.start_group("core func ")?; @@ -1014,7 +1028,9 @@ impl Printer<'_, '_> { self.print_canonical_options(state, &options)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::StreamCancelRead { ty, async_ } => { self.start_group("core func ")?; @@ -1027,7 +1043,9 @@ impl Printer<'_, '_> { } self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::StreamCancelWrite { ty, async_ } => { self.start_group("core func ")?; @@ -1040,7 +1058,9 @@ impl Printer<'_, '_> { } self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::StreamCloseReadable { ty } => { self.start_group("core func ")?; @@ -1050,7 +1070,9 @@ impl Printer<'_, '_> { self.print_idx(&state.component.type_names, ty)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::StreamCloseWritable { ty } => { self.start_group("core func ")?; @@ -1060,7 +1082,9 @@ impl Printer<'_, '_> { self.print_idx(&state.component.type_names, ty)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::FutureNew { ty } => { self.start_group("core func ")?; @@ -1070,7 +1094,9 @@ impl Printer<'_, '_> { self.print_idx(&state.component.type_names, ty)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::FutureWrite { ty, options } => { self.start_group("core func ")?; @@ -1082,7 +1108,9 @@ impl Printer<'_, '_> { self.print_canonical_options(state, &options)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::FutureRead { ty, options } => { self.start_group("core func ")?; @@ -1094,7 +1122,9 @@ impl Printer<'_, '_> { self.print_canonical_options(state, &options)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::FutureCancelRead { ty, async_ } => { self.start_group("core func ")?; @@ -1107,7 +1137,9 @@ impl Printer<'_, '_> { } self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::FutureCancelWrite { ty, async_ } => { self.start_group("core func ")?; @@ -1120,7 +1152,9 @@ impl Printer<'_, '_> { } self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::FutureCloseReadable { ty } => { self.start_group("core func ")?; @@ -1130,7 +1164,9 @@ impl Printer<'_, '_> { self.print_idx(&state.component.type_names, ty)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::FutureCloseWritable { ty } => { self.start_group("core func ")?; @@ -1140,7 +1176,9 @@ impl Printer<'_, '_> { self.print_idx(&state.component.type_names, ty)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::ErrorContextNew { options } => { self.start_group("core func ")?; @@ -1150,7 +1188,9 @@ impl Printer<'_, '_> { self.print_canonical_options(state, &options)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::ErrorContextDebugMessage { options } => { self.start_group("core func ")?; @@ -1160,7 +1200,9 @@ impl Printer<'_, '_> { self.print_canonical_options(state, &options)?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } CanonicalFunction::ErrorContextDrop => { self.start_group("core func ")?; @@ -1169,7 +1211,9 @@ impl Printer<'_, '_> { self.start_group("canon error-context.drop")?; self.end_group()?; self.end_group()?; + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; + state.core.func_to_type.push(None); } } } @@ -1365,7 +1409,7 @@ impl Printer<'_, '_> { self.start_group("core func ")?; self.print_name(&state.core.func_names, state.core.funcs)?; self.end_group()?; - debug_assert!(state.core.func_to_type.len() == state.core.funcs as usize); + debug_assert_eq!(state.core.func_to_type.len(), state.core.funcs as usize); state.core.funcs += 1; state.core.func_to_type.push(None) } @@ -1391,7 +1435,7 @@ impl Printer<'_, '_> { self.start_group("core tag ")?; self.print_name(&state.core.tag_names, state.core.tags as u32)?; self.end_group()?; - debug_assert!(state.core.tag_to_type.len() == state.core.tags as usize); + debug_assert_eq!(state.core.tag_to_type.len(), state.core.tags as usize); state.core.tags += 1; state.core.tag_to_type.push(None) } diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs index be5effdc5d..ff26700162 100644 --- a/crates/wast/src/component/binary.rs +++ b/crates/wast/src/component/binary.rs @@ -355,6 +355,10 @@ impl<'a> Encoder<'a> { self.core_func_names.push(name); self.funcs.thread_hw_concurrency(); } + CanonicalFuncKind::TaskReturn(info) => { + self.core_func_names.push(name); + self.funcs.task_return(info.ty.into()); + } } self.flush(Some(self.funcs.id())); diff --git a/crates/wast/src/component/expand.rs b/crates/wast/src/component/expand.rs index 0f46157813..ee734ec31b 100644 --- a/crates/wast/src/component/expand.rs +++ b/crates/wast/src/component/expand.rs @@ -266,7 +266,8 @@ impl<'a> Expander<'a> { | CanonicalFuncKind::ResourceRep(_) | CanonicalFuncKind::ResourceDrop(_) | CanonicalFuncKind::ThreadSpawn(_) - | CanonicalFuncKind::ThreadHwConcurrency(_) => {} + | CanonicalFuncKind::ThreadHwConcurrency(_) + | CanonicalFuncKind::TaskReturn(_) => {} } } @@ -322,6 +323,12 @@ impl<'a> Expander<'a> { kind: CanonicalFuncKind::ThreadHwConcurrency(mem::take(info)), })) } + CoreFuncKind::TaskReturn(info) => Some(ComponentField::CanonicalFunc(CanonicalFunc { + span: func.span, + id: func.id, + name: func.name, + kind: CanonicalFuncKind::TaskReturn(mem::take(info)), + })), } } diff --git a/crates/wast/src/component/func.rs b/crates/wast/src/component/func.rs index 1f74fb15a1..53319eb513 100644 --- a/crates/wast/src/component/func.rs +++ b/crates/wast/src/component/func.rs @@ -53,6 +53,7 @@ pub enum CoreFuncKind<'a> { ResourceRep(CanonResourceRep<'a>), ThreadSpawn(CanonThreadSpawn<'a>), ThreadHwConcurrency(CanonThreadHwConcurrency), + TaskReturn(CanonTaskReturn<'a>), } impl<'a> Parse<'a> for CoreFuncKind<'a> { @@ -79,6 +80,8 @@ impl<'a> Parse<'a> for CoreFuncKind<'a> { Ok(CoreFuncKind::ThreadSpawn(parser.parse()?)) } else if l.peek::()? { Ok(CoreFuncKind::ThreadHwConcurrency(parser.parse()?)) + } else if l.peek::()? { + Ok(CoreFuncKind::TaskReturn(parser.parse()?)) } else { Err(l.error()) } @@ -270,6 +273,8 @@ pub enum CanonicalFuncKind<'a> { ThreadSpawn(CanonThreadSpawn<'a>), ThreadHwConcurrency(CanonThreadHwConcurrency), + + TaskReturn(CanonTaskReturn<'a>), } /// Information relating to lifting a core function. @@ -460,6 +465,31 @@ impl Default for CanonThreadHwConcurrency { } } +/// Information relating to the `task.return` intrinsic. +#[derive(Debug)] +pub struct CanonTaskReturn<'a> { + /// The core function type representing the signature of this intrinsic. + pub ty: Index<'a>, +} + +impl<'a> Parse<'a> for CanonTaskReturn<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parse::()?; + + Ok(Self { + ty: parser.parse()?, + }) + } +} + +impl Default for CanonTaskReturn<'_> { + fn default() -> Self { + Self { + ty: Index::Num(0, Span::from_offset(0)), + } + } +} + #[derive(Debug)] /// Canonical ABI options. pub enum CanonOpt<'a> { diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs index 7e97aa2bbf..741377e693 100644 --- a/crates/wast/src/component/resolve.rs +++ b/crates/wast/src/component/resolve.rs @@ -387,6 +387,9 @@ impl<'a> Resolver<'a> { self.resolve_ns(&mut info.ty, Ns::CoreType)?; } CanonicalFuncKind::ThreadHwConcurrency(_) => {} + CanonicalFuncKind::TaskReturn(info) => { + self.resolve_ns(&mut info.ty, Ns::CoreType)?; + } } Ok(()) @@ -887,7 +890,8 @@ impl<'a> ComponentState<'a> { | CanonicalFuncKind::ResourceRep(_) | CanonicalFuncKind::ResourceDrop(_) | CanonicalFuncKind::ThreadSpawn(_) - | CanonicalFuncKind::ThreadHwConcurrency(_) => { + | CanonicalFuncKind::ThreadHwConcurrency(_) + | CanonicalFuncKind::TaskReturn(_) => { self.core_funcs.register(f.id, "core func")? } }, diff --git a/crates/wast/src/lib.rs b/crates/wast/src/lib.rs index a34f202d23..ac64718daa 100644 --- a/crates/wast/src/lib.rs +++ b/crates/wast/src/lib.rs @@ -556,6 +556,7 @@ pub mod kw { custom_keyword!(thread); custom_keyword!(thread_spawn = "thread.spawn"); custom_keyword!(thread_hw_concurrency = "thread.hw_concurrency"); + custom_keyword!(task_return = "task.return"); custom_keyword!(wait); custom_keyword!(definition); custom_keyword!(r#async = "async"); diff --git a/tests/local/component-model-async/lift.wast b/tests/local/component-model-async/lift.wast index 82fd7f1aef..7291c56f47 100644 --- a/tests/local/component-model-async/lift.wast +++ b/tests/local/component-model-async/lift.wast @@ -23,6 +23,25 @@ ) ) +;; async lift; with callback and task.return +(component + (core module $m + (import "" "task.return" (func $task-return (param i32))) + (func (export "callback") (param i32 i32 i32 i32) (result i32) unreachable) + (func (export "foo") (param i32) (result i32) + (call $task-return (i32.const 0)) + (i32.const 0) + ) + ) + (core type $task-return-type (func (param i32))) + (core func $task-return (canon task.return $task-return-type)) + (core instance $i (instantiate $m (with "" (instance (export "task.return" (func $task-return)))))) + + (func (export "foo") (param "p1" u32) (result u32) + (canon lift (core func $i "foo") async (callback (func $i "callback"))) + ) +) + ;; async lift; with incorrectly-typed callback (assert_invalid (component @@ -39,6 +58,22 @@ "canonical option `callback` uses a core function with an incorrect signature" ) +;; async lift; with incorrectly-typed core function +(assert_invalid + (component + (core module $m + (func (export "callback") (param i32 i32 i32 i32) (result i32) unreachable) + (func (export "foo") (param i32 i32) (result i32) unreachable) + ) + (core instance $i (instantiate $m)) + + (func (export "foo") (param "p1" u32) (result u32) + (canon lift (core func $i "foo") async (callback (func $i "callback"))) + ) + ) + "lowered parameter types `[I32]` do not match parameter types `[I32, I32]` of core function 0" +) + ;; async lift; with missing callback (assert_invalid (component diff --git a/tests/local/component-model-async/lower.wast b/tests/local/component-model-async/lower.wast new file mode 100644 index 0000000000..b1f58bc29b --- /dev/null +++ b/tests/local/component-model-async/lower.wast @@ -0,0 +1,26 @@ +;; async lower +(component + (import "foo" (func $foo (param "p1" u32) (result u32))) + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core func $foo (canon lower (func $foo) async (memory $libc "memory"))) + (core module $m + (func (import "" "foo") (param i32 i32) (result i32)) + ) + (core instance $i (instantiate $m (with "" (instance (export "foo" (func $foo)))))) +) + +;; async lower; with incorrectly-typed core function +(assert_invalid + (component + (import "foo" (func $foo (param "p1" u32) (result u32))) + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core func $foo (canon lower (func $foo) async (memory $libc "memory"))) + (core module $m + (func (import "" "foo") (param i32) (result i32)) + ) + (core instance $i (instantiate $m (with "" (instance (export "foo" (func $foo)))))) + ) + "type mismatch for export `foo` of module instantiation argument ``" +) diff --git a/tests/snapshots/local/component-model-async/lift.wast.json b/tests/snapshots/local/component-model-async/lift.wast.json index 802b4e4744..e568643673 100644 --- a/tests/snapshots/local/component-model-async/lift.wast.json +++ b/tests/snapshots/local/component-model-async/lift.wast.json @@ -14,23 +14,36 @@ "module_type": "binary" }, { - "type": "assert_invalid", - "line": 28, + "type": "module", + "line": 27, "filename": "lift.2.wasm", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 47, + "filename": "lift.3.wasm", "module_type": "binary", "text": "canonical option `callback` uses a core function with an incorrect signature" }, { "type": "assert_invalid", - "line": 44, - "filename": "lift.3.wasm", + "line": 63, + "filename": "lift.4.wasm", + "module_type": "binary", + "text": "lowered parameter types `[I32]` do not match parameter types `[I32, I32]` of core function 0" + }, + { + "type": "assert_invalid", + "line": 79, + "filename": "lift.5.wasm", "module_type": "binary", "text": "core instance 0 has no export named `callback`" }, { "type": "assert_invalid", - "line": 59, - "filename": "lift.4.wasm", + "line": 94, + "filename": "lift.6.wasm", "module_type": "binary", "text": "cannot specify callback without lifting async" } diff --git a/tests/snapshots/local/component-model-async/lift.wast/2.print b/tests/snapshots/local/component-model-async/lift.wast/2.print new file mode 100644 index 0000000000..adb4858d29 --- /dev/null +++ b/tests/snapshots/local/component-model-async/lift.wast/2.print @@ -0,0 +1,32 @@ +(component + (core module $m (;0;) + (type (;0;) (func (param i32))) + (type (;1;) (func (param i32 i32 i32 i32) (result i32))) + (type (;2;) (func (param i32) (result i32))) + (import "" "task.return" (func $task-return (;0;) (type 0))) + (export "callback" (func 1)) + (export "foo" (func 2)) + (func (;1;) (type 1) (param i32 i32 i32 i32) (result i32) + unreachable + ) + (func (;2;) (type 2) (param i32) (result i32) + i32.const 0 + call $task-return + i32.const 0 + ) + ) + (core type $task-return-type (;0;) (func (param i32))) + (core func $task-return (;0;) (canon task.return 0)) + (core instance (;0;) + (export "task.return" (func $task-return)) + ) + (core instance $i (;1;) (instantiate $m + (with "" (instance 0)) + ) + ) + (type (;0;) (func (param "p1" u32) (result u32))) + (alias core export $i "foo" (core func (;1;))) + (alias core export $i "callback" (core func (;2;))) + (func (;0;) (type 0) (canon lift (core func 1) async (callback 2))) + (export (;1;) "foo" (func 0)) +) diff --git a/tests/snapshots/local/component-model-async/lower.wast.json b/tests/snapshots/local/component-model-async/lower.wast.json new file mode 100644 index 0000000000..0d9c577e81 --- /dev/null +++ b/tests/snapshots/local/component-model-async/lower.wast.json @@ -0,0 +1,18 @@ +{ + "source_filename": "tests/local/component-model-async/lower.wast", + "commands": [ + { + "type": "module", + "line": 2, + "filename": "lower.0.wasm", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 15, + "filename": "lower.1.wasm", + "module_type": "binary", + "text": "type mismatch for export `foo` of module instantiation argument ``" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/local/component-model-async/lower.wast/0.print b/tests/snapshots/local/component-model-async/lower.wast/0.print new file mode 100644 index 0000000000..fee9919556 --- /dev/null +++ b/tests/snapshots/local/component-model-async/lower.wast/0.print @@ -0,0 +1,22 @@ +(component + (type (;0;) (func (param "p1" u32) (result u32))) + (import "foo" (func $foo (;0;) (type 0))) + (core module $libc (;0;) + (memory (;0;) 1) + (export "memory" (memory 0)) + ) + (core instance $libc (;0;) (instantiate $libc)) + (alias core export $libc "memory" (core memory (;0;))) + (core func $foo (;0;) (canon lower (func $foo) async (memory 0))) + (core module $m (;1;) + (type (;0;) (func (param i32 i32) (result i32))) + (import "" "foo" (func (;0;) (type 0))) + ) + (core instance (;1;) + (export "foo" (func $foo)) + ) + (core instance $i (;2;) (instantiate $m + (with "" (instance 1)) + ) + ) +)