diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs index f7227c8ab80c..36eff83dad08 100644 --- a/crates/c-api/src/extern.rs +++ b/crates/c-api/src/extern.rs @@ -23,6 +23,7 @@ pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t { Extern::SharedMemory(_) => panic!( "Shared Memory no implemented for wasm_* types. Please use wasmtime_* types instead" ), + Extern::Tag(_) => todo!(), // FIXME: #10252 C embedder API for exceptions and control tags. } } @@ -143,6 +144,7 @@ impl From for wasmtime_extern_t { sharedmemory: ManuallyDrop::new(Box::new(sharedmemory)), }, }, + Extern::Tag(_) => todo!(), // FIXME: #10252 C embedder API for exceptions and control tags. } } } diff --git a/crates/c-api/src/types/extern.rs b/crates/c-api/src/types/extern.rs index 66707b8f0979..a5ea4762477f 100644 --- a/crates/c-api/src/types/extern.rs +++ b/crates/c-api/src/types/extern.rs @@ -25,6 +25,7 @@ impl CExternType { ExternType::Global(f) => CExternType::Global(CGlobalType::new(f)), ExternType::Memory(f) => CExternType::Memory(CMemoryType::new(f)), ExternType::Table(f) => CExternType::Table(CTableType::new(f)), + ExternType::Tag(_) => todo!(), // FIXME: #10252 C embedder API for exceptions and control tags. } } } diff --git a/crates/environ/src/compile/module_environ.rs b/crates/environ/src/compile/module_environ.rs index f1fec0b2854a..9f0a71c1a943 100644 --- a/crates/environ/src/compile/module_environ.rs +++ b/crates/environ/src/compile/module_environ.rs @@ -7,8 +7,8 @@ use crate::{ ConstExpr, ConstOp, DataIndex, DefinedFuncIndex, ElemIndex, EngineOrModuleTypeIndex, EntityIndex, EntityType, FuncIndex, GlobalIndex, IndexType, InitMemory, MemoryIndex, ModuleInternedTypeIndex, ModuleTypesBuilder, PrimaryMap, SizeOverflow, StaticMemoryInitializer, - TableIndex, TableInitialValue, Tunables, TypeConvert, TypeIndex, Unsigned, WasmError, - WasmHeapTopType, WasmHeapType, WasmResult, WasmValType, WasmparserTypeConverter, + TableIndex, TableInitialValue, Tag, TagIndex, Tunables, TypeConvert, TypeIndex, Unsigned, + WasmError, WasmHeapTopType, WasmHeapType, WasmResult, WasmValType, WasmparserTypeConverter, }; use anyhow::{bail, Result}; use cranelift_entity::packed_option::ReservedValue; @@ -318,9 +318,13 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { self.result.module.num_imported_tables += 1; EntityType::Table(self.convert_table_type(&ty)?) } - - // doesn't get past validation - TypeRef::Tag(_) => unreachable!(), + TypeRef::Tag(ty) => { + let index = TypeIndex::from_u32(ty.func_type_idx); + let signature = self.result.module.types[index]; + let tag = Tag { signature }; + self.result.module.num_imported_tags += 1; + EntityType::Tag(tag) + } }; self.declare_import(import.module, import.name, ty); } @@ -384,9 +388,12 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { Payload::TagSection(tags) => { self.validator.tag_section(&tags)?; - // This feature isn't enabled at this time, so we should - // never get here. - unreachable!(); + for entry in tags { + let sigindex = entry?.func_type_idx; + let ty = TypeIndex::from_u32(sigindex); + let interned_index = self.result.module.types[ty]; + self.result.module.push_tag(interned_index); + } } Payload::GlobalSection(globals) => { @@ -424,9 +431,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(index)), ExternalKind::Memory => EntityIndex::Memory(MemoryIndex::from_u32(index)), ExternalKind::Global => EntityIndex::Global(GlobalIndex::from_u32(index)), - - // this never gets past validation - ExternalKind::Tag => unreachable!(), + ExternalKind::Tag => EntityIndex::Tag(TagIndex::from_u32(index)), }; self.result .module @@ -770,7 +775,7 @@ and for re-adding support for interface types you can see this issue: EntityType::Table(ty) => EntityIndex::Table(self.result.module.tables.push(ty)), EntityType::Memory(ty) => EntityIndex::Memory(self.result.module.memories.push(ty)), EntityType::Global(ty) => EntityIndex::Global(self.result.module.globals.push(ty)), - EntityType::Tag(_) => unimplemented!(), + EntityType::Tag(ty) => EntityIndex::Tag(self.result.module.tags.push(ty)), } } diff --git a/crates/environ/src/component/translate/inline.rs b/crates/environ/src/component/translate/inline.rs index d92603c0f323..aa2ca6f61e87 100644 --- a/crates/environ/src/component/translate/inline.rs +++ b/crates/environ/src/component/translate/inline.rs @@ -1229,6 +1229,7 @@ impl<'a> Inliner<'a> { EntityIndex::Table(i) => frame.tables[i].clone().into(), EntityIndex::Global(i) => frame.globals[i].clone().into(), EntityIndex::Memory(i) => frame.memories[i].clone().into(), + EntityIndex::Tag(_) => todo!(), // FIXME: #10252 support for tags in the component model }, } } diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs index c30fc46b9059..de0300cac5bc 100644 --- a/crates/environ/src/module.rs +++ b/crates/environ/src/module.rs @@ -333,6 +333,9 @@ pub struct Module { /// Number of imported or aliased globals in the module. pub num_imported_globals: usize, + /// Number of imported or aliased tags in the module. + pub num_imported_tags: usize, + /// Number of functions that "escape" from this module may need to have a /// `VMFuncRef` constructed for them. /// @@ -354,6 +357,9 @@ pub struct Module { /// WebAssembly global initializers for locally-defined globals. pub global_initializers: PrimaryMap, + + /// WebAssembly exception and control tags. + pub tags: PrimaryMap, } /// Initialization routines for creating an instance, encompassing imports, @@ -500,6 +506,29 @@ impl Module { index.index() < self.num_imported_globals } + /// Test whether the given tag index is for an imported tag. + #[inline] + pub fn is_imported_tag(&self, index: TagIndex) -> bool { + index.index() < self.num_imported_tags + } + + /// Convert a `DefinedTagIndex` into a `TagIndex`. + #[inline] + pub fn tag_index(&self, defined_tag: DefinedTagIndex) -> TagIndex { + TagIndex::new(self.num_imported_tags + defined_tag.index()) + } + + /// Convert a `TagIndex` into a `DefinedTagIndex`. Returns None if the + /// index is an imported tag. + #[inline] + pub fn defined_tag_index(&self, tag: TagIndex) -> Option { + if tag.index() < self.num_imported_tags { + None + } else { + Some(DefinedTagIndex::new(tag.index() - self.num_imported_tags)) + } + } + /// Returns an iterator of all the imports in this module, along with their /// module name, field name, and type that's being imported. pub fn imports(&self) -> impl ExactSizeIterator { @@ -517,9 +546,16 @@ impl Module { EntityIndex::Table(i) => EntityType::Table(self.tables[i]), EntityIndex::Memory(i) => EntityType::Memory(self.memories[i]), EntityIndex::Function(i) => EntityType::Function(self.functions[i].signature), + EntityIndex::Tag(i) => EntityType::Tag(self.tags[i]), } } + /// Appends a new tag to this module with the given type information. + pub fn push_tag(&mut self, signature: impl Into) -> TagIndex { + let signature = signature.into(); + self.tags.push(Tag { signature }) + } + /// Appends a new function to this module with the given type information, /// used for functions that either don't escape or aren't certain whether /// they escape yet. @@ -548,6 +584,12 @@ impl Module { pub fn num_defined_memories(&self) -> usize { self.memories.len() - self.num_imported_memories } + + /// Returns the number of tags defined by this module itself: all tags + /// minus imported tags. + pub fn num_defined_tags(&self) -> usize { + self.tags.len() - self.num_imported_tags + } } impl TypeTrace for Module { @@ -572,12 +614,14 @@ impl TypeTrace for Module { num_imported_tables: _, num_imported_memories: _, num_imported_globals: _, + num_imported_tags: _, num_escaped_funcs: _, functions, tables, memories: _, globals, global_initializers: _, + tags, } = self; for t in types.values().copied() { @@ -592,6 +636,9 @@ impl TypeTrace for Module { for g in globals.values() { g.trace(func)?; } + for t in tags.values() { + t.trace(func)?; + } Ok(()) } @@ -616,12 +663,14 @@ impl TypeTrace for Module { num_imported_tables: _, num_imported_memories: _, num_imported_globals: _, + num_imported_tags: _, num_escaped_funcs: _, functions, tables, memories: _, globals, global_initializers: _, + tags, } = self; for t in types.values_mut() { @@ -636,6 +685,9 @@ impl TypeTrace for Module { for g in globals.values_mut() { g.trace_mut(func)?; } + for t in tags.values_mut() { + t.trace_mut(func)?; + } Ok(()) } } diff --git a/crates/environ/src/types.rs b/crates/environ/src/types.rs index 8e0591bf31a4..0eeba147891a 100644 --- a/crates/environ/src/types.rs +++ b/crates/environ/src/types.rs @@ -1315,6 +1315,11 @@ entity_impl!(DataIndex); pub struct ElemIndex(u32); entity_impl!(ElemIndex); +/// Index type of a defined tag inside the WebAssembly module. +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] +pub struct DefinedTagIndex(u32); +entity_impl!(DefinedTagIndex); + /// Index type of an event inside the WebAssembly module. #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] pub struct TagIndex(u32); @@ -1339,6 +1344,8 @@ pub enum EntityIndex { Memory(MemoryIndex), /// Global index. Global(GlobalIndex), + /// Tag index. + Tag(TagIndex), } impl From for EntityIndex { @@ -1365,6 +1372,12 @@ impl From for EntityIndex { } } +impl From for EntityIndex { + fn from(idx: TagIndex) -> EntityIndex { + EntityIndex::Tag(idx) + } +} + /// A type of an item in a wasm module where an item is typically something that /// can be exported. #[derive(Clone, Debug, Serialize, Deserialize)] @@ -1373,7 +1386,7 @@ pub enum EntityType { Global(Global), /// A linear memory with the specified limits Memory(Memory), - /// An event definition. + /// An exception and control tag definition. Tag(Tag), /// A table with the specified element type and limits Table(Table), @@ -1391,7 +1404,8 @@ impl TypeTrace for EntityType { Self::Global(g) => g.trace(func), Self::Table(t) => t.trace(func), Self::Function(idx) => func(*idx), - Self::Memory(_) | Self::Tag(_) => Ok(()), + Self::Memory(_) => Ok(()), + Self::Tag(t) => t.trace(func), } } @@ -1403,7 +1417,8 @@ impl TypeTrace for EntityType { Self::Global(g) => g.trace_mut(func), Self::Table(t) => t.trace_mut(func), Self::Function(idx) => func(idx), - Self::Memory(_) | Self::Tag(_) => Ok(()), + Self::Memory(_) => Ok(()), + Self::Tag(t) => t.trace_mut(func), } } } @@ -1933,20 +1948,26 @@ impl From for Memory { } } -/// WebAssembly event. +/// WebAssembly exception and control tag. #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct Tag { - /// The event signature type. - pub ty: TypeIndex, + /// The tag signature type. + pub signature: EngineOrModuleTypeIndex, } -impl From for Tag { - fn from(ty: wasmparser::TagType) -> Tag { - match ty.kind { - wasmparser::TagKind::Exception => Tag { - ty: TypeIndex::from_u32(ty.func_type_idx), - }, - } +impl TypeTrace for Tag { + fn trace(&self, func: &mut F) -> Result<(), E> + where + F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, + { + func(self.signature) + } + + fn trace_mut(&mut self, func: &mut F) -> Result<(), E> + where + F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, + { + func(&mut self.signature) } } diff --git a/crates/environ/src/vmoffsets.rs b/crates/environ/src/vmoffsets.rs index c797d7088acf..98d72c56a513 100644 --- a/crates/environ/src/vmoffsets.rs +++ b/crates/environ/src/vmoffsets.rs @@ -35,8 +35,8 @@ // } use crate::{ - DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, FuncRefIndex, - GlobalIndex, MemoryIndex, Module, OwnedMemoryIndex, TableIndex, + DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, DefinedTagIndex, FuncIndex, + FuncRefIndex, GlobalIndex, MemoryIndex, Module, OwnedMemoryIndex, TableIndex, TagIndex, }; use cranelift_entity::packed_option::ReservedValue; @@ -69,6 +69,8 @@ pub struct VMOffsets

{ pub num_imported_memories: u32, /// The number of imported globals in the module. pub num_imported_globals: u32, + /// The number of imported tags in the module. + pub num_imported_tags: u32, /// The number of defined tables in the module. pub num_defined_tables: u32, /// The number of defined memories in the module. @@ -77,6 +79,8 @@ pub struct VMOffsets

{ pub num_owned_memories: u32, /// The number of defined globals in the module. pub num_defined_globals: u32, + /// The number of defined tags in the module. + pub num_defined_tags: u32, /// The number of escaped functions in the module, the size of the func_refs /// array. pub num_escaped_funcs: u32, @@ -86,10 +90,12 @@ pub struct VMOffsets

{ imported_tables: u32, imported_memories: u32, imported_globals: u32, + imported_tags: u32, defined_tables: u32, defined_memories: u32, owned_memories: u32, defined_globals: u32, + defined_tags: u32, defined_func_refs: u32, size: u32, } @@ -150,6 +156,12 @@ pub trait PtrSize { 16 } + /// Return the size of `VMTagDefinition`. + #[inline] + fn size_of_vmtag_definition(&self) -> u8 { + 4 + } + // Offsets within `VMRuntimeLimits` /// Return the offset of the `fuel_consumed` field of `VMRuntimeLimits` @@ -323,6 +335,8 @@ pub struct VMOffsetsFields

{ pub num_imported_memories: u32, /// The number of imported globals in the module. pub num_imported_globals: u32, + /// The number of imported tags in the module. + pub num_imported_tags: u32, /// The number of defined tables in the module. pub num_defined_tables: u32, /// The number of defined memories in the module. @@ -331,6 +345,8 @@ pub struct VMOffsetsFields

{ pub num_owned_memories: u32, /// The number of defined globals in the module. pub num_defined_globals: u32, + /// The number of defined tags in the module. + pub num_defined_tags: u32, /// The number of escaped functions in the module, the size of the function /// references array. pub num_escaped_funcs: u32, @@ -353,10 +369,12 @@ impl VMOffsets

{ num_imported_tables: cast_to_u32(module.num_imported_tables), num_imported_memories: cast_to_u32(module.num_imported_memories), num_imported_globals: cast_to_u32(module.num_imported_globals), + num_imported_tags: cast_to_u32(module.num_imported_tags), num_defined_tables: cast_to_u32(module.num_defined_tables()), num_defined_memories: cast_to_u32(module.num_defined_memories()), num_owned_memories, num_defined_globals: cast_to_u32(module.globals.len() - module.num_imported_globals), + num_defined_tags: cast_to_u32(module.tags.len() - module.num_imported_tags), num_escaped_funcs: cast_to_u32(module.num_escaped_funcs), }) } @@ -382,9 +400,11 @@ impl VMOffsets

{ num_imported_tables: _, num_imported_memories: _, num_imported_globals: _, + num_imported_tags: _, num_defined_tables: _, num_defined_globals: _, num_defined_memories: _, + num_defined_tags: _, num_owned_memories: _, num_escaped_funcs: _, @@ -416,8 +436,10 @@ impl VMOffsets

{ calculate_sizes! { defined_func_refs: "module functions", + defined_tags: "defined tags", defined_globals: "defined globals", defined_tables: "defined tables", + imported_tags: "imported tags", imported_globals: "imported globals", imported_tables: "imported tables", imported_functions: "imported functions", @@ -436,19 +458,23 @@ impl From> for VMOffsets

{ num_imported_tables: fields.num_imported_tables, num_imported_memories: fields.num_imported_memories, num_imported_globals: fields.num_imported_globals, + num_imported_tags: fields.num_imported_tags, num_defined_tables: fields.num_defined_tables, num_defined_memories: fields.num_defined_memories, num_owned_memories: fields.num_owned_memories, num_defined_globals: fields.num_defined_globals, + num_defined_tags: fields.num_defined_tags, num_escaped_funcs: fields.num_escaped_funcs, imported_functions: 0, imported_tables: 0, imported_memories: 0, imported_globals: 0, + imported_tags: 0, defined_tables: 0, defined_memories: 0, owned_memories: 0, defined_globals: 0, + defined_tags: 0, defined_func_refs: 0, size: 0, }; @@ -495,11 +521,15 @@ impl From> for VMOffsets

{ = cmul(ret.num_imported_tables, ret.size_of_vmtable_import()), size(imported_globals) = cmul(ret.num_imported_globals, ret.size_of_vmglobal_import()), + size(imported_tags) + = cmul(ret.num_imported_tags, ret.size_of_vmtag_import()), size(defined_tables) = cmul(ret.num_defined_tables, ret.size_of_vmtable_definition()), align(16), size(defined_globals) = cmul(ret.num_defined_globals, ret.ptr.size_of_vmglobal_definition()), + size(defined_tags) + = cmul(ret.num_defined_tags, ret.ptr.size_of_vmtag_definition()), size(defined_func_refs) = cmul( ret.num_escaped_funcs, ret.ptr.size_of_vm_func_ref(), @@ -638,6 +668,21 @@ impl VMOffsets

{ } } +/// Offsets for `VMTagImport`. +impl VMOffsets

{ + /// The offset of the `from` field. + #[inline] + pub fn vmtag_import_from(&self) -> u8 { + 0 * self.pointer_size() + } + + /// Return the size of `VMTagImport`. + #[inline] + pub fn size_of_vmtag_import(&self) -> u8 { + 1 * self.pointer_size() + } +} + /// Offsets for `VMContext`. impl VMOffsets

{ /// The offset of the `tables` array. @@ -664,6 +709,12 @@ impl VMOffsets

{ self.imported_globals } + /// The offset of the `tags` array. + #[inline] + pub fn vmctx_imported_tags_begin(&self) -> u32 { + self.imported_tags + } + /// The offset of the `tables` array. #[inline] pub fn vmctx_tables_begin(&self) -> u32 { @@ -688,6 +739,12 @@ impl VMOffsets

{ self.defined_globals } + /// The offset of the `tags` array. + #[inline] + pub fn vmctx_tags_begin(&self) -> u32 { + self.defined_tags + } + /// The offset of the `func_refs` array. #[inline] pub fn vmctx_func_refs_begin(&self) -> u32 { @@ -732,6 +789,13 @@ impl VMOffsets

{ + index.as_u32() * u32::from(self.size_of_vmglobal_import()) } + /// Return the offset to `VMTagImport` index `index`. + #[inline] + pub fn vmctx_vmtag_import(&self, index: TagIndex) -> u32 { + assert!(index.as_u32() < self.num_imported_tags); + self.vmctx_imported_tags_begin() + index.as_u32() * u32::from(self.size_of_vmtag_import()) + } + /// Return the offset to `VMTableDefinition` index `index`. #[inline] pub fn vmctx_vmtable_definition(&self, index: DefinedTableIndex) -> u32 { @@ -763,6 +827,13 @@ impl VMOffsets

{ + index.as_u32() * u32::from(self.ptr.size_of_vmglobal_definition()) } + /// Return the offset to the `VMTagDefinition` index `index`. + #[inline] + pub fn vmctx_vmtag_definition(&self, index: DefinedTagIndex) -> u32 { + assert!(index.as_u32() < self.num_defined_tags); + self.vmctx_tags_begin() + index.as_u32() * u32::from(self.ptr.size_of_vmtag_definition()) + } + /// Return the offset to the `VMFuncRef` for the given function /// index (either imported or defined). #[inline] @@ -838,6 +909,12 @@ impl VMOffsets

{ pub fn vmctx_vmglobal_import_from(&self, index: GlobalIndex) -> u32 { self.vmctx_vmglobal_import(index) + u32::from(self.vmglobal_import_from()) } + + /// Return the offset to the `from` field in `VMTagImport` index `index`. + #[inline] + pub fn vmctx_vmtag_import_from(&self, index: TagIndex) -> u32 { + self.vmctx_vmtag_import(index) + u32::from(self.vmtag_import_from()) + } } /// Offsets for `VMDrcHeader`. diff --git a/crates/fuzzing/src/oracles/dummy.rs b/crates/fuzzing/src/oracles/dummy.rs index 0687066cb8df..79dd25e685b0 100644 --- a/crates/fuzzing/src/oracles/dummy.rs +++ b/crates/fuzzing/src/oracles/dummy.rs @@ -23,6 +23,7 @@ pub fn dummy_extern(store: &mut Store, ty: ExternType) -> Result { ExternType::Global(global_ty) => Extern::Global(dummy_global(store, global_ty)?), ExternType::Table(table_ty) => Extern::Table(dummy_table(store, table_ty)?), ExternType::Memory(mem_ty) => Extern::Memory(dummy_memory(store, mem_ty)?), + ExternType::Tag(_tag_ty) => todo!(), // FIXME: #10252 }) } diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 2806f852adcb..75c1078c127a 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1068,6 +1068,30 @@ impl Config { self } + /// Configures whether the [WebAssembly stack switching + /// proposal][proposal] will be enabled for compilation. + /// + /// This feature gates the use of control tags. + /// + /// This feature depends on the `function_reference_types` and + /// `exceptions` features. + /// + /// This feature is `false` by default. + /// + /// # Errors + /// + /// [proposal]: https://github.com/webassembly/stack-switching + pub fn wasm_stack_switching(&mut self, enable: bool) -> &mut Self { + // FIXME(dhil): Once the config provides a handle + // for turning on/off exception handling proposal support, + // this ought to only enable stack switching. + self.wasm_feature( + WasmFeatures::EXCEPTIONS | WasmFeatures::STACK_SWITCHING, + enable, + ); + self + } + /// Configures whether the WebAssembly component-model [proposal] will /// be enabled for compilation. /// diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs index d25b6492134e..184cfd07b804 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/engine/serialization.rs @@ -203,6 +203,7 @@ struct WasmFeatures { component_model_async: bool, gc_types: bool, wide_arithmetic: bool, + stack_switching: bool, } impl Metadata<'_> { @@ -249,7 +250,6 @@ impl Metadata<'_> { assert!(!component_model_nested_names); assert!(!shared_everything_threads); assert!(!legacy_exceptions); - assert!(!stack_switching); Metadata { target: engine.compiler().triple().to_string(), @@ -275,6 +275,7 @@ impl Metadata<'_> { component_model_async, gc_types, wide_arithmetic, + stack_switching, }, } } @@ -484,6 +485,7 @@ impl Metadata<'_> { component_model_async, gc_types, wide_arithmetic, + stack_switching, } = self.features; use wasmparser::WasmFeatures as F; @@ -575,7 +577,11 @@ impl Metadata<'_> { other.contains(F::WIDE_ARITHMETIC), "WebAssembly wide-arithmetic support", )?; - + Self::check_bool( + stack_switching, + other.contains(F::STACK_SWITCHING), + "WebAssembly stack switching support", + )?; Ok(()) } diff --git a/crates/wasmtime/src/runtime/externals.rs b/crates/wasmtime/src/runtime/externals.rs index 67e9c04f588f..d9d4b156fed4 100644 --- a/crates/wasmtime/src/runtime/externals.rs +++ b/crates/wasmtime/src/runtime/externals.rs @@ -3,9 +3,11 @@ use crate::{AsContext, Engine, ExternType, Func, Memory, SharedMemory}; mod global; mod table; +mod tag; pub use global::Global; pub use table::Table; +pub use tag::Tag; // Externals @@ -30,6 +32,9 @@ pub enum Extern { /// A WebAssembly shared memory; these are handled separately from /// [`Memory`]. SharedMemory(SharedMemory), + /// A WebAssembly exception or control tag which can be referenced + /// when raising an exception or stack switching. + Tag(Tag), } impl Extern { @@ -84,6 +89,16 @@ impl Extern { } } + /// Returns the underlying `Tag`, if this external is a tag. + /// + /// Returns `None` if this is not a tag. + pub fn into_tag(self) -> Option { + match self { + Extern::Tag(tag) => Some(tag), + _ => None, + } + } + /// Returns the type associated with this `Extern`. /// /// The `store` argument provided must own this `Extern` and is used to look @@ -100,6 +115,7 @@ impl Extern { Extern::SharedMemory(ft) => ExternType::Memory(ft.ty()), Extern::Table(tt) => ExternType::Table(tt.ty(store)), Extern::Global(gt) => ExternType::Global(gt.ty(store)), + Extern::Tag(tt) => ExternType::Tag(tt.ty(store)), } } @@ -124,6 +140,7 @@ impl Extern { crate::runtime::vm::Export::Table(t) => { Extern::Table(Table::from_wasmtime_table(t, store)) } + crate::runtime::vm::Export::Tag(t) => Extern::Tag(Tag::from_wasmtime_tag(t, store)), } } @@ -134,6 +151,7 @@ impl Extern { Extern::Memory(m) => m.comes_from_same_store(store), Extern::SharedMemory(m) => Engine::same(m.engine(), store.engine()), Extern::Table(t) => store.store_data().contains(t.0), + Extern::Tag(t) => store.store_data().contains(t.0), } } } @@ -168,6 +186,12 @@ impl From for Extern { } } +impl From for Extern { + fn from(t: Tag) -> Self { + Extern::Tag(t) + } +} + // Exports /// An exported WebAssembly value. @@ -233,4 +257,10 @@ impl<'instance> Export<'instance> { pub fn into_global(self) -> Option { self.definition.into_global() } + + /// Consume this `Export` and return the contained `Tag`, if it's a tag, + /// or `None` otherwise. + pub fn into_tag(self) -> Option { + self.definition.into_tag() + } } diff --git a/crates/wasmtime/src/runtime/externals/tag.rs b/crates/wasmtime/src/runtime/externals/tag.rs new file mode 100644 index 000000000000..2d1186a9b7d7 --- /dev/null +++ b/crates/wasmtime/src/runtime/externals/tag.rs @@ -0,0 +1,62 @@ +use crate::runtime::types::TagType; +use crate::{ + store::{StoreData, StoreOpaque, Stored}, + AsContext, +}; +use wasmtime_environ::VMSharedTypeIndex; + +/// A WebAssembly `tag`. +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] // here for the C API +pub struct Tag(pub(super) Stored); + +impl Tag { + pub(crate) unsafe fn from_wasmtime_tag( + wasmtime_export: crate::runtime::vm::ExportTag, + store: &mut StoreOpaque, + ) -> Self { + debug_assert!( + wasmtime_export.tag.signature.unwrap_engine_type_index() + != VMSharedTypeIndex::default() + ); + Tag(store.store_data_mut().insert(wasmtime_export)) + } + + /// Returns the underlying type of this `tag`. + /// + /// # Panics + /// + /// Panics if `store` does not own this tag. + pub fn ty(&self, store: impl AsContext) -> TagType { + self._ty(store.as_context().0) + } + + pub(crate) fn _ty(&self, store: &StoreOpaque) -> TagType { + let ty = &store[self.0].tag; + TagType::from_wasmtime_tag(store.engine(), &ty) + } + + pub(crate) fn wasmtime_ty<'a>(&self, data: &'a StoreData) -> &'a wasmtime_environ::Tag { + &data[self.0].tag + } + + pub(crate) fn vmimport(&self, store: &StoreOpaque) -> crate::runtime::vm::VMTagImport { + let export = &store[self.0]; + crate::runtime::vm::VMTagImport { + from: export.definition.into(), + } + } + + /// Determines whether this tag is reference equal to the other + /// given tag in the given store. + /// + /// # Panics + /// + /// Panics if either tag do not belong to the given `store`. + pub fn eq(a: &Tag, b: &Tag, store: impl AsContext) -> bool { + let store = store.as_context().0; + let a = &store[a.0]; + let b = &store[b.0]; + a.definition.eq(&b.definition) + } +} diff --git a/crates/wasmtime/src/runtime/instance.rs b/crates/wasmtime/src/runtime/instance.rs index 279500925a4c..43ba3d883d67 100644 --- a/crates/wasmtime/src/runtime/instance.rs +++ b/crates/wasmtime/src/runtime/instance.rs @@ -2,19 +2,20 @@ use crate::linker::{Definition, DefinitionType}; use crate::prelude::*; use crate::runtime::vm::{ Imports, InstanceAllocationRequest, ModuleRuntimeInfo, StorePtr, VMFuncRef, VMFunctionImport, - VMGlobalImport, VMMemoryImport, VMOpaqueContext, VMTableImport, + VMGlobalImport, VMMemoryImport, VMOpaqueContext, VMTableImport, VMTagImport, }; use crate::store::{InstanceId, StoreOpaque, Stored}; use crate::types::matching; use crate::{ AsContextMut, Engine, Export, Extern, Func, Global, Memory, Module, ModuleExport, SharedMemory, - StoreContext, StoreContextMut, Table, TypedFunc, + StoreContext, StoreContextMut, Table, Tag, TypedFunc, }; use alloc::sync::Arc; use core::ptr::NonNull; use wasmparser::WasmFeatures; use wasmtime_environ::{ - EntityIndex, EntityType, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap, TableIndex, TypeTrace, + EntityIndex, EntityType, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap, TableIndex, TagIndex, + TypeTrace, }; /// An instantiated WebAssembly module. @@ -593,6 +594,18 @@ impl Instance { self.get_export(store, name)?.into_global() } + /// Looks up a tag [`Tag`] by name. + /// + /// Returns `None` if there was no export named `name`, or if there was but + /// it wasn't a tag. + /// + /// # Panics + /// + /// Panics if `store` does not own this instance. + pub fn get_tag(&self, store: impl AsContextMut, name: &str) -> Option { + self.get_export(store, name)?.into_tag() + } + #[cfg(feature = "component-model")] pub(crate) fn id(&self, store: &StoreOpaque) -> InstanceId { store[self.0].id @@ -646,6 +659,7 @@ pub(crate) struct OwnedImports { tables: PrimaryMap, memories: PrimaryMap, globals: PrimaryMap, + tags: PrimaryMap, } impl OwnedImports { @@ -661,6 +675,7 @@ impl OwnedImports { tables: PrimaryMap::new(), memories: PrimaryMap::new(), globals: PrimaryMap::new(), + tags: PrimaryMap::new(), } } @@ -670,6 +685,7 @@ impl OwnedImports { self.tables.reserve(raw.num_imported_tables); self.memories.reserve(raw.num_imported_memories); self.globals.reserve(raw.num_imported_globals); + self.tags.reserve(raw.num_imported_tags); } #[cfg(feature = "component-model")] @@ -678,6 +694,7 @@ impl OwnedImports { self.tables.clear(); self.memories.clear(); self.globals.clear(); + self.tags.clear(); } fn push(&mut self, item: &Extern, store: &mut StoreOpaque, module: &Module) { @@ -697,6 +714,9 @@ impl OwnedImports { Extern::SharedMemory(i) => { self.memories.push(i.vmimport(store)); } + Extern::Tag(i) => { + self.tags.push(i.vmimport(store)); + } } } @@ -731,6 +751,11 @@ impl OwnedImports { index: m.index, }); } + crate::runtime::vm::Export::Tag(t) => { + self.tags.push(VMTagImport { + from: t.definition.into(), + }); + } } } @@ -740,6 +765,7 @@ impl OwnedImports { globals: self.globals.values().as_slice(), memories: self.memories.values().as_slice(), functions: self.functions.values().as_slice(), + tags: self.tags.values().as_slice(), } } } diff --git a/crates/wasmtime/src/runtime/linker.rs b/crates/wasmtime/src/runtime/linker.rs index d9a9a4cb49f0..f54beb0e6867 100644 --- a/crates/wasmtime/src/runtime/linker.rs +++ b/crates/wasmtime/src/runtime/linker.rs @@ -129,6 +129,7 @@ pub(crate) enum DefinitionType { // no longer be the current size of the table/memory. Table(wasmtime_environ::Table, u64), Memory(wasmtime_environ::Memory, u64), + Tag(wasmtime_environ::Tag), } impl Linker { @@ -1388,6 +1389,7 @@ impl DefinitionType { DefinitionType::Memory(*t.wasmtime_ty(data), t.internal_size(store)) } Extern::SharedMemory(t) => DefinitionType::Memory(*t.ty().wasmtime_memory(), t.size()), + Extern::Tag(t) => DefinitionType::Tag(*t.wasmtime_ty(data)), } } @@ -1397,6 +1399,7 @@ impl DefinitionType { DefinitionType::Table(..) => "table", DefinitionType::Memory(..) => "memory", DefinitionType::Global(_) => "global", + DefinitionType::Tag(_) => "tag", } } } diff --git a/crates/wasmtime/src/runtime/store/data.rs b/crates/wasmtime/src/runtime/store/data.rs index 936e6780282f..d4934f9e7e23 100644 --- a/crates/wasmtime/src/runtime/store/data.rs +++ b/crates/wasmtime/src/runtime/store/data.rs @@ -26,6 +26,7 @@ pub struct StoreData { globals: Vec, instances: Vec, memories: Vec, + tags: Vec, #[cfg(feature = "component-model")] pub(crate) components: crate::component::ComponentStoreData, } @@ -52,6 +53,7 @@ impl_store_data! { globals => crate::runtime::vm::ExportGlobal, instances => crate::instance::InstanceData, memories => crate::runtime::vm::ExportMemory, + tags => crate::runtime::vm::ExportTag, } impl StoreData { @@ -63,6 +65,7 @@ impl StoreData { globals: Vec::new(), instances: Vec::new(), memories: Vec::new(), + tags: Vec::new(), #[cfg(feature = "component-model")] components: Default::default(), } diff --git a/crates/wasmtime/src/runtime/types.rs b/crates/wasmtime/src/runtime/types.rs index 0302482944df..851b0e74cd0f 100644 --- a/crates/wasmtime/src/runtime/types.rs +++ b/crates/wasmtime/src/runtime/types.rs @@ -3,7 +3,7 @@ use crate::{type_registry::RegisteredType, Engine}; use core::fmt::{self, Display, Write}; use wasmtime_environ::{ EngineOrModuleTypeIndex, EntityType, Global, IndexType, Limits, Memory, ModuleTypes, Table, - TypeTrace, VMSharedTypeIndex, WasmArrayType, WasmCompositeInnerType, WasmCompositeType, + Tag, TypeTrace, VMSharedTypeIndex, WasmArrayType, WasmCompositeInnerType, WasmCompositeType, WasmFieldType, WasmFuncType, WasmHeapType, WasmRefType, WasmStorageType, WasmStructType, WasmSubType, WasmValType, }; @@ -1156,6 +1156,8 @@ pub enum ExternType { Table(TableType), /// This external type is the type of a WebAssembly memory. Memory(MemoryType), + /// This external type is the type of a WebAssembly tag. + Tag(TagType), } macro_rules! extern_type_accessors { @@ -1188,6 +1190,7 @@ impl ExternType { (Global(GlobalType) global unwrap_global) (Table(TableType) table unwrap_table) (Memory(MemoryType) memory unwrap_memory) + (Tag(TagType) tag unwrap_tag) } pub(crate) fn from_wasmtime( @@ -1219,7 +1222,7 @@ impl ExternType { EntityType::Global(ty) => GlobalType::from_wasmtime_global(engine, ty).into(), EntityType::Memory(ty) => MemoryType::from_wasmtime_memory(ty).into(), EntityType::Table(ty) => TableType::from_wasmtime_table(engine, ty).into(), - EntityType::Tag(_) => unimplemented!("wasm tag support"), + EntityType::Tag(ty) => TagType::from_wasmtime_tag(engine, ty).into(), } } } @@ -1248,6 +1251,12 @@ impl From for ExternType { } } +impl From for ExternType { + fn from(ty: TagType) -> ExternType { + ExternType::Tag(ty) + } +} + /// The storage type of a `struct` field or `array` element. /// /// This is either a packed 8- or -16 bit integer, or else it is some unpacked @@ -2457,6 +2466,34 @@ impl GlobalType { } } +// Tag Types + +/// A descriptor for a tag in a WebAssembly module. +/// +/// This type describes an instance of a tag in a WebAssembly +/// module. Tags are local to an [`Instance`](crate::Instance). +#[derive(Debug, Clone, Hash)] +pub struct TagType { + ty: FuncType, +} + +impl TagType { + /// Creates a new global descriptor of the specified type. + pub fn new(ty: FuncType) -> TagType { + TagType { ty } + } + + /// Returns the underlying function type of this tag descriptor. + pub fn ty(&self) -> &FuncType { + &self.ty + } + + pub(crate) fn from_wasmtime_tag(engine: &Engine, tag: &Tag) -> TagType { + let ty = FuncType::from_shared_type_index(engine, tag.signature.unwrap_engine_type_index()); + TagType { ty } + } +} + // Table Types /// A descriptor for a table in a WebAssembly module. diff --git a/crates/wasmtime/src/runtime/types/matching.rs b/crates/wasmtime/src/runtime/types/matching.rs index fabe5b634158..e4d2557ba700 100644 --- a/crates/wasmtime/src/runtime/types/matching.rs +++ b/crates/wasmtime/src/runtime/types/matching.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use crate::{linker::DefinitionType, Engine}; use wasmtime_environ::{ - EntityType, Global, IndexType, Memory, Table, TypeTrace, VMSharedTypeIndex, WasmHeapType, + EntityType, Global, IndexType, Memory, Table, Tag, TypeTrace, VMSharedTypeIndex, WasmHeapType, WasmRefType, WasmSubType, WasmValType, }; @@ -40,7 +40,10 @@ impl MatchCx<'_> { } _ => bail!("expected func, but found {}", actual.desc()), }, - EntityType::Tag(_) => unimplemented!(), + EntityType::Tag(expected) => match actual { + DefinitionType::Tag(actual) => tag_ty(expected, actual), + _ => bail!("expected tag, but found {}", actual.desc()), + }, } } } @@ -90,7 +93,10 @@ pub fn entity_ty(engine: &Engine, expected: &EntityType, actual: &EntityType) -> } _ => bail!("expected func found {}", entity_desc(actual)), }, - EntityType::Tag(_) => unimplemented!(), + EntityType::Tag(expected) => match actual { + EntityType::Tag(actual) => tag_ty(expected, actual), + _ => bail!("expected tag found {}", entity_desc(actual)), + }, } } @@ -165,6 +171,14 @@ fn memory_ty(expected: &Memory, actual: &Memory, actual_runtime_size: Option Result<()> { + if expected.signature == actual.signature { + Ok(()) + } else { + bail!("incompatible tag types") + } +} + fn match_heap(expected: WasmHeapType, actual: WasmHeapType, desc: &str) -> Result<()> { use WasmHeapType as H; let result = match (actual, expected) { diff --git a/crates/wasmtime/src/runtime/vm.rs b/crates/wasmtime/src/runtime/vm.rs index fd76b465a78f..5392571d2278 100644 --- a/crates/wasmtime/src/runtime/vm.rs +++ b/crates/wasmtime/src/runtime/vm.rs @@ -93,7 +93,7 @@ pub use crate::runtime::vm::unwind::*; pub use crate::runtime::vm::vmcontext::{ VMArrayCallFunction, VMArrayCallHostFuncContext, VMContext, VMFuncRef, VMFunctionBody, VMFunctionImport, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, - VMOpaqueContext, VMRuntimeLimits, VMTableImport, VMWasmCallFunction, ValRaw, + VMOpaqueContext, VMRuntimeLimits, VMTableImport, VMTagImport, VMWasmCallFunction, ValRaw, }; pub use send_sync_ptr::SendSyncPtr; diff --git a/crates/wasmtime/src/runtime/vm/export.rs b/crates/wasmtime/src/runtime/vm/export.rs index 45c4638ed7b2..6145b1da053b 100644 --- a/crates/wasmtime/src/runtime/vm/export.rs +++ b/crates/wasmtime/src/runtime/vm/export.rs @@ -1,8 +1,9 @@ use crate::runtime::vm::vmcontext::{ VMContext, VMFuncRef, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition, + VMTagDefinition, }; use core::ptr::NonNull; -use wasmtime_environ::{DefinedMemoryIndex, Global, Memory, Table}; +use wasmtime_environ::{DefinedMemoryIndex, Global, Memory, Table, Tag}; /// The value of an export passed from one instance to another. pub enum Export { @@ -17,6 +18,9 @@ pub enum Export { /// A global export value. Global(ExportGlobal), + + /// A tag export value. + Tag(ExportTag), } /// A function export value. @@ -106,3 +110,22 @@ impl From for Export { Export::Global(func) } } + +/// A tag export value. +#[derive(Debug, Clone)] +pub struct ExportTag { + /// The address of the global storage. + pub definition: NonNull, + /// The global declaration, used for compatibility checking. + pub tag: Tag, +} + +// See docs on send/sync for `ExportFunction` above. +unsafe impl Send for ExportTag {} +unsafe impl Sync for ExportTag {} + +impl From for Export { + fn from(func: ExportTag) -> Export { + Export::Tag(func) + } +} diff --git a/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs b/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs index d7f4cb5d7fdf..3f1c19bc13fa 100644 --- a/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs +++ b/crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs @@ -1025,10 +1025,12 @@ mod tests { num_imported_tables: 0, num_imported_memories: 0, num_imported_globals: 0, + num_imported_tags: 0, num_defined_tables: 0, num_defined_memories: 0, num_owned_memories: 0, num_defined_globals: 0, + num_defined_tags: 0, num_escaped_funcs: 0, }); @@ -1053,10 +1055,12 @@ mod tests { num_imported_tables: 0, num_imported_memories: 0, num_imported_globals: 0, + num_imported_tags: 0, num_defined_tables: 0, num_defined_memories: 0, num_owned_memories: 0, num_defined_globals: 0, + num_defined_tags: 0, num_escaped_funcs: 0, }); assert_eq!( @@ -1080,10 +1084,12 @@ mod tests { num_imported_tables: 0, num_imported_memories: 0, num_imported_globals: 0, + num_imported_tags: 0, num_defined_tables: 0, num_defined_memories: 0, num_owned_memories: 0, num_defined_globals: 0, + num_defined_tags: 0, num_escaped_funcs: 0, }); assert_eq!( diff --git a/crates/wasmtime/src/runtime/vm/imports.rs b/crates/wasmtime/src/runtime/vm/imports.rs index a1b47e17d9f4..61082b673b58 100644 --- a/crates/wasmtime/src/runtime/vm/imports.rs +++ b/crates/wasmtime/src/runtime/vm/imports.rs @@ -1,5 +1,5 @@ use crate::runtime::vm::vmcontext::{ - VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport, + VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport, VMTagImport, }; /// Resolved import pointers. @@ -26,4 +26,7 @@ pub struct Imports<'a> { /// Resolved addresses for imported globals. pub globals: &'a [VMGlobalImport], + + /// Resolved addresses for imported tags. + pub tags: &'a [VMTagImport], } diff --git a/crates/wasmtime/src/runtime/vm/instance.rs b/crates/wasmtime/src/runtime/vm/instance.rs index 00d3283cdce5..f0e2e051bd12 100644 --- a/crates/wasmtime/src/runtime/vm/instance.rs +++ b/crates/wasmtime/src/runtime/vm/instance.rs @@ -9,11 +9,12 @@ use crate::runtime::vm::table::{Table, TableElement, TableElementType}; use crate::runtime::vm::vmcontext::{ VMBuiltinFunctionsArray, VMContext, VMFuncRef, VMFunctionImport, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMOpaqueContext, VMRuntimeLimits, - VMTableDefinition, VMTableImport, + VMTableDefinition, VMTableImport, VMTagDefinition, VMTagImport, }; use crate::runtime::vm::{ - ExportFunction, ExportGlobal, ExportMemory, ExportTable, GcStore, Imports, ModuleRuntimeInfo, - SendSyncPtr, VMFunctionBody, VMGcRef, VMStore, VMStoreRawPtr, VmPtr, VmSafe, WasmFault, + ExportFunction, ExportGlobal, ExportMemory, ExportTable, ExportTag, GcStore, Imports, + ModuleRuntimeInfo, SendSyncPtr, VMFunctionBody, VMGcRef, VMStore, VMStoreRawPtr, VmPtr, VmSafe, + WasmFault, }; use crate::store::{StoreInner, StoreOpaque}; use crate::{prelude::*, StoreContextMut}; @@ -30,9 +31,10 @@ use sptr::Strict; use wasmtime_environ::ModuleInternedTypeIndex; use wasmtime_environ::{ packed_option::ReservedValue, DataIndex, DefinedGlobalIndex, DefinedMemoryIndex, - DefinedTableIndex, ElemIndex, EntityIndex, EntityRef, EntitySet, FuncIndex, GlobalIndex, - HostPtr, MemoryIndex, Module, PrimaryMap, PtrSize, TableIndex, TableInitialValue, - TableSegmentElements, Trap, VMOffsets, VMSharedTypeIndex, WasmHeapTopType, VMCONTEXT_MAGIC, + DefinedTableIndex, DefinedTagIndex, ElemIndex, EntityIndex, EntityRef, EntitySet, FuncIndex, + GlobalIndex, HostPtr, MemoryIndex, Module, PrimaryMap, PtrSize, TableIndex, TableInitialValue, + TableSegmentElements, TagIndex, Trap, VMOffsets, VMSharedTypeIndex, WasmHeapTopType, + VMCONTEXT_MAGIC, }; #[cfg(feature = "wmemcheck")] use wasmtime_wmemcheck::Wmemcheck; @@ -441,6 +443,16 @@ impl Instance { unsafe { &*self.vmctx_plus_offset(self.offsets().vmctx_vmglobal_import(index)) } } + /// Return the indexed `VMTagImport`. + fn imported_tag(&self, index: TagIndex) -> &VMTagImport { + unsafe { &*self.vmctx_plus_offset(self.offsets().vmctx_vmtag_import(index)) } + } + + /// Return the indexed `VMTagDefinition`. + fn tag_ptr(&mut self, index: DefinedTagIndex) -> NonNull { + unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_vmtag_definition(index)) } + } + /// Return the indexed `VMTableDefinition`. #[allow(dead_code)] fn table(&mut self, index: DefinedTableIndex) -> VMTableDefinition { @@ -714,6 +726,17 @@ impl Instance { } } + fn get_exported_tag(&mut self, index: TagIndex) -> ExportTag { + ExportTag { + definition: if let Some(def_index) = self.env_module().defined_tag_index(index) { + self.tag_ptr(def_index) + } else { + self.imported_tag(index).from.as_non_null() + }, + tag: self.env_module().tags[index], + } + } + /// Return an iterator over the exports of this instance. /// /// Specifically, it provides access to the key-value pairs, where the keys @@ -1406,6 +1429,14 @@ impl Instance { imports.globals.len(), ); + debug_assert_eq!(imports.tags.len(), module.num_imported_tags); + ptr::copy_nonoverlapping( + imports.tags.as_ptr(), + self.vmctx_plus_offset_mut(offsets.vmctx_imported_tags_begin()) + .as_ptr(), + imports.tags.len(), + ); + // N.B.: there is no need to initialize the funcrefs array because we // eagerly construct each element in it whenever asked for a reference // to that element. In other words, there is no state needed to track @@ -1450,6 +1481,18 @@ impl Instance { for (index, _init) in module.global_initializers.iter() { self.global_ptr(index).write(VMGlobalDefinition::new()); } + + // Initialize the defined tags + let mut ptr = self.vmctx_plus_offset_mut(offsets.vmctx_tags_begin()); + for i in 0..module.num_defined_tags() { + let defined_index = DefinedTagIndex::new(i); + let tag_index = module.tag_index(defined_index); + let tag = module.tags[tag_index]; + ptr.write(VMTagDefinition::new( + tag.signature.unwrap_engine_type_index(), + )); + ptr = ptr.add(1); + } } fn wasm_fault(&self, addr: usize) -> Option { @@ -1504,6 +1547,11 @@ impl InstanceHandle { self.instance_mut().get_exported_global(export) } + /// Lookup a tag by index. + pub fn get_exported_tag(&mut self, export: TagIndex) -> ExportTag { + self.instance_mut().get_exported_tag(export) + } + /// Lookup a memory by index. pub fn get_exported_memory(&mut self, export: MemoryIndex) -> ExportMemory { self.instance_mut().get_exported_memory(export) @@ -1521,6 +1569,7 @@ impl InstanceHandle { EntityIndex::Global(i) => Export::Global(self.get_exported_global(i)), EntityIndex::Table(i) => Export::Table(self.get_exported_table(i)), EntityIndex::Memory(i) => Export::Memory(self.get_exported_memory(i)), + EntityIndex::Tag(i) => Export::Tag(self.get_exported_tag(i)), } } diff --git a/crates/wasmtime/src/runtime/vm/vmcontext.rs b/crates/wasmtime/src/runtime/vm/vmcontext.rs index 66e0ddbb91fa..53b45a95d65c 100644 --- a/crates/wasmtime/src/runtime/vm/vmcontext.rs +++ b/crates/wasmtime/src/runtime/vm/vmcontext.rs @@ -263,6 +263,39 @@ mod test_vmglobal_import { } } +/// The fields compiled code needs to access to utilize a WebAssembly +/// tag imported from another instance. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct VMTagImport { + /// A pointer to the imported tag description. + pub from: VmPtr, +} + +// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. +unsafe impl VmSafe for VMTagImport {} + +#[cfg(test)] +mod test_vmtag_import { + use super::VMTagImport; + use core::mem::{offset_of, size_of}; + use wasmtime_environ::{HostPtr, Module, VMOffsets}; + + #[test] + fn check_vmtag_import_offsets() { + let module = Module::new(); + let offsets = VMOffsets::new(HostPtr, &module); + assert_eq!( + size_of::(), + usize::from(offsets.size_of_vmtag_import()) + ); + assert_eq!( + offset_of!(VMTagImport, from), + usize::from(offsets.vmtag_import_from()) + ); + } +} + /// The fields compiled code needs to access to utilize a WebAssembly linear /// memory defined within the instance, namely the start address and the /// size in bytes. @@ -669,6 +702,49 @@ mod test_vmshared_type_index { } } +/// A WebAssembly tag defined within the instance. +/// +#[derive(Debug)] +#[repr(C)] +pub struct VMTagDefinition { + /// Function signature's type id. + pub type_index: VMSharedTypeIndex, +} + +impl VMTagDefinition { + pub fn new(type_index: VMSharedTypeIndex) -> Self { + Self { type_index } + } +} + +// SAFETY: the above structure is repr(C) and only contains VmSafe +// fields. +unsafe impl VmSafe for VMTagDefinition {} + +#[cfg(test)] +mod test_vmtag_definition { + use super::VMTagDefinition; + use std::mem::size_of; + use wasmtime_environ::{HostPtr, Module, PtrSize, VMOffsets}; + + #[test] + fn check_vmtag_definition_offsets() { + let module = Module::new(); + let offsets = VMOffsets::new(HostPtr, &module); + assert_eq!( + size_of::(), + usize::from(offsets.ptr.size_of_vmtag_definition()) + ); + } + + #[test] + fn check_vmtag_begins_aligned() { + let module = Module::new(); + let offsets = VMOffsets::new(HostPtr, &module); + assert_eq!(offsets.vmctx_tags_begin() % 16, 0); + } +} + /// The VM caller-checked "funcref" record, for caller-side signature checking. /// /// It consists of function pointer(s), a type id to be checked by the diff --git a/tests/all/main.rs b/tests/all/main.rs index 7329e1b24514..1922c1393a8d 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -42,6 +42,7 @@ mod stack_overflow; mod store; mod structs; mod table; +mod tags; mod threads; mod traps; mod types; diff --git a/tests/all/tags.rs b/tests/all/tags.rs new file mode 100644 index 000000000000..0130d176a49b --- /dev/null +++ b/tests/all/tags.rs @@ -0,0 +1,80 @@ +use wasmtime::*; + +#[test] +#[cfg_attr(miri, ignore)] +fn wasm_export_tags() -> Result<()> { + let source = r#" + (module + (tag (export "t1") (param i32) (result i32)) + (tag (export "t2") (param i32) (result i32)) + (tag (export "t3") (param i64) (result i32)) + ) + "#; + let _ = env_logger::try_init(); + let mut config = Config::new(); + config.wasm_stack_switching(true); + let engine = Engine::new(&config)?; + let mut store = Store::new(&engine, ()); + let module = Module::new(&engine, source)?; + + let instance = Instance::new(&mut store, &module, &[])?; + let t1 = instance.get_tag(&mut store, "t1"); + assert!(t1.is_some()); + let t1 = t1.unwrap(); + + let t2 = instance.get_tag(&mut store, "t2"); + assert!(t2.is_some()); + let t2 = t2.unwrap(); + + let t1_ty = t1.ty(&store); + let t2_ty = t2.ty(&store); + assert!(Tag::eq(&t1, &t1, &store)); + assert!(!Tag::eq(&t1, &t2, &store)); + assert!(FuncType::eq(t1_ty.ty(), t2_ty.ty())); + + let t3 = instance.get_tag(&mut store, "t3"); + assert!(t3.is_some()); + let t3 = t3.unwrap(); + let t3_ty = t3.ty(&store); + assert!(Tag::eq(&t3, &t3, &store)); + assert!(!Tag::eq(&t3, &t1, &store)); + assert!(!Tag::eq(&t3, &t2, &store)); + assert!(!FuncType::eq(t1_ty.ty(), t3_ty.ty())); + + return Ok(()); +} + +#[test] +#[cfg_attr(miri, ignore)] +fn wasm_import_tags() -> Result<()> { + let m1_src = r#" + (module + (tag (export "t1") (param i32) (result i32)) + ) + "#; + let m2_src = r#" + (module + (tag (export "t1_2") (import "" "") (param i32) (result i32)) + (tag (export "t1_22") (import "" "") (param i32) (result i32)) + (tag (export "t2") (param i32) (result i32)) + ) + "#; + let _ = env_logger::try_init(); + let mut config = Config::new(); + config.wasm_stack_switching(true); + let engine = Engine::new(&config)?; + let mut store = Store::new(&engine, ()); + let m1 = Module::new(&engine, m1_src)?; + let m2 = Module::new(&engine, m2_src)?; + + let m1_instance = Instance::new(&mut store, &m1, &[])?; + let t1 = m1_instance.get_tag(&mut store, "t1").unwrap(); + let m2_instance = Instance::new(&mut store, &m2, &[t1.into(), t1.into()])?; + let t1_2 = m2_instance.get_tag(&mut store, "t1_2").unwrap(); + assert!(Tag::eq(&t1, &t1_2, &store)); + let t1_22 = m2_instance.get_tag(&mut store, "t1_22").unwrap(); + assert!(Tag::eq(&t1, &t1_22, &store)); + assert!(Tag::eq(&t1_2, &t1_22, &store)); + + return Ok(()); +}