Skip to content

Commit

Permalink
Wasmtime(gc): Add support for supertypes and finality
Browse files Browse the repository at this point in the history
This is the final type system change for Wasm GC: the ability to explicitly
declare supertypes and finality. A final type may not be a supertype of another
type. A concrete heap type matches another concrete heap type if its concrete
type is a subtype (potentially transitively) of the other heap type's concrete
type.

Next, I'll begin support for allocating GC structs and arrays at runtime.

I've also implemented `O(1)` subtype checking in the types registry:

In a type system with single inheritance, the subtyping relationships between
all types form a set of trees. The root of each tree is a type that has no
supertype; each node's immediate children are the types that directly subtype
that node.

For example, consider these types:

    class Base {}
    class A subtypes Base {}
    class B subtypes Base {}
    class C subtypes A {}
    class D subtypes A {}
    class E subtypes C {}

These types produce the following tree:

               Base
              /    \
             A      B
            / \
           C   D
          /
         E

Note the following properties:

1. If `sub` is a subtype of `sup` (either directly or transitively) then
`sup` *must* be on the path from `sub` up to the root of `sub`'s tree.

2. Additionally, `sup` *must* be the `i`th node down from the root in that path,
where `i` is the length of the path from `sup` to its tree's root.

Therefore, if we maintain a vector containing the path to the root for each
type, then we can simply check if `sup` is at index `supertypes(sup).len()`
within `supertypes(sub)`.
  • Loading branch information
fitzgen committed May 13, 2024
1 parent 330eb20 commit c617f51
Show file tree
Hide file tree
Showing 14 changed files with 1,215 additions and 137 deletions.
5 changes: 5 additions & 0 deletions crates/cranelift/src/func_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1642,6 +1642,11 @@ impl TypeConvert for FuncEnvironment<'_> {
fn lookup_heap_type(&self, ty: wasmparser::UnpackedIndex) -> WasmHeapType {
wasmtime_environ::WasmparserTypeConverter::new(self.types, self.module).lookup_heap_type(ty)
}

fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex {
wasmtime_environ::WasmparserTypeConverter::new(self.types, self.module)
.lookup_type_index(index)
}
}

impl<'module_environment> TargetEnvironment for FuncEnvironment<'module_environment> {
Expand Down
7 changes: 7 additions & 0 deletions crates/environ/src/compile/module_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,13 @@ impl TypeConvert for ModuleEnvironment<'_, '_> {
fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType {
WasmparserTypeConverter::new(&self.types, &self.result.module).lookup_heap_type(index)
}

fn lookup_type_index(
&self,
index: wasmparser::UnpackedIndex,
) -> wasmtime_types::EngineOrModuleTypeIndex {
WasmparserTypeConverter::new(&self.types, &self.result.module).lookup_type_index(index)
}
}

impl ModuleTranslation<'_> {
Expand Down
19 changes: 18 additions & 1 deletion crates/environ/src/compile/module_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl ModuleTypesBuilder {
let ty = &validator_types[id];
let wasm_ty = WasmparserTypeConverter::new(self, module)
.with_rec_group(validator_types, rec_group_id)
.convert_sub_type(ty)?;
.convert_sub_type(ty);
self.wasm_sub_type_in_rec_group(id, wasm_ty);
}

Expand Down Expand Up @@ -161,6 +161,8 @@ impl ModuleTypesBuilder {
// `trampoline_types` so we can reuse it in the future.
Cow::Owned(f) => {
let idx = self.types.push(WasmSubType {
is_final: true,
supertype: None,
composite_type: WasmCompositeType::Func(f.clone()),
});

Expand Down Expand Up @@ -435,4 +437,19 @@ impl TypeConvert for WasmparserTypeConverter<'_> {
UnpackedIndex::RecGroup(_) => unreachable!(),
}
}

fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex {
match index {
UnpackedIndex::Id(id) => {
let interned = self.types.wasmparser_to_wasmtime[&id];
EngineOrModuleTypeIndex::Module(interned)
}
UnpackedIndex::Module(module_index) => {
let module_index = TypeIndex::from_u32(module_index);
let interned = self.module.types[module_index];
EngineOrModuleTypeIndex::Module(interned)
}
UnpackedIndex::RecGroup(_) => unreachable!(),
}
}
}
7 changes: 7 additions & 0 deletions crates/environ/src/component/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,13 @@ mod pre_inlining {
fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType {
self.types.lookup_heap_type(index)
}

fn lookup_type_index(
&self,
index: wasmparser::UnpackedIndex,
) -> wasmtime_types::EngineOrModuleTypeIndex {
self.types.lookup_type_index(index)
}
}
}
use pre_inlining::PreInliningComponentTypes;
7 changes: 7 additions & 0 deletions crates/environ/src/component/types_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,13 @@ impl TypeConvert for ComponentTypesBuilder {
fn lookup_heap_type(&self, _index: wasmparser::UnpackedIndex) -> WasmHeapType {
panic!("heap types are not supported yet")
}

fn lookup_type_index(
&self,
_index: wasmparser::UnpackedIndex,
) -> wasmtime_types::EngineOrModuleTypeIndex {
panic!("typed references are not supported yet")
}
}

fn intern<T, U>(map: &mut HashMap<T, U>, list: &mut PrimaryMap<U, T>, item: T) -> U
Expand Down
54 changes: 40 additions & 14 deletions crates/types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,11 +326,26 @@ pub enum EngineOrModuleTypeIndex {
}

impl From<ModuleInternedTypeIndex> for EngineOrModuleTypeIndex {
#[inline]
fn from(i: ModuleInternedTypeIndex) -> Self {
Self::Module(i)
}
}

impl From<VMSharedTypeIndex> for EngineOrModuleTypeIndex {
#[inline]
fn from(i: VMSharedTypeIndex) -> Self {
Self::Engine(i)
}
}

impl From<RecGroupRelativeTypeIndex> for EngineOrModuleTypeIndex {
#[inline]
fn from(i: RecGroupRelativeTypeIndex) -> Self {
Self::RecGroup(i)
}
}

impl fmt::Display for EngineOrModuleTypeIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expand Down Expand Up @@ -868,8 +883,12 @@ impl TypeTrace for WasmCompositeType {
/// A concrete, user-defined (or host-defined) Wasm type.
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct WasmSubType {
// TODO: is_final, supertype
//
/// Whether or not this type can be the supertype of any other type.
pub is_final: bool,

/// This type's supertype, if any.
pub supertype: Option<EngineOrModuleTypeIndex>,

/// The array, function, or struct that is defined.
pub composite_type: WasmCompositeType,
}
Expand Down Expand Up @@ -926,13 +945,19 @@ impl TypeTrace for WasmSubType {
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
if let Some(sup) = self.supertype {
func(sup)?;
}
self.composite_type.trace(func)
}

fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
if let Some(sup) = self.supertype.as_mut() {
func(sup)?;
}
self.composite_type.trace_mut(func)
}
}
Expand Down Expand Up @@ -1513,27 +1538,24 @@ pub trait TypeConvert {
})
}

fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> {
if ty.supertype_idx.is_some() {
return Err(wasm_unsupported!("wasm gc: explicit subtyping"));
fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmSubType {
WasmSubType {
is_final: ty.is_final,
supertype: ty.supertype_idx.map(|i| self.lookup_type_index(i.unpack())),
composite_type: self.convert_composite_type(&ty.composite_type),
}
let composite_type = self.convert_composite_type(&ty.composite_type)?;
Ok(WasmSubType { composite_type })
}

fn convert_composite_type(
&self,
ty: &wasmparser::CompositeType,
) -> WasmResult<WasmCompositeType> {
fn convert_composite_type(&self, ty: &wasmparser::CompositeType) -> WasmCompositeType {
match ty {
wasmparser::CompositeType::Func(f) => {
Ok(WasmCompositeType::Func(self.convert_func_type(f)))
WasmCompositeType::Func(self.convert_func_type(f))
}
wasmparser::CompositeType::Array(a) => {
Ok(WasmCompositeType::Array(self.convert_array_type(a)))
WasmCompositeType::Array(self.convert_array_type(a))
}
wasmparser::CompositeType::Struct(s) => {
Ok(WasmCompositeType::Struct(self.convert_struct_type(s)))
WasmCompositeType::Struct(self.convert_struct_type(s))
}
}
}
Expand Down Expand Up @@ -1626,4 +1648,8 @@ pub trait TypeConvert {
/// Converts the specified type index from a heap type into a canonicalized
/// heap type.
fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType;

/// Converts the specified type index from a heap type into a canonicalized
/// heap type.
fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex;
}
13 changes: 9 additions & 4 deletions crates/wasmtime/src/runtime/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -871,10 +871,15 @@ impl ComponentItem {
TypeDef::ComponentFunc(idx) => Self::ComponentFunc(ComponentFunc::from(*idx, ty)),
TypeDef::Interface(iface_ty) => Self::Type(Type::from(iface_ty, ty)),
TypeDef::Module(idx) => Self::Module(Module::from(*idx, ty)),
TypeDef::CoreFunc(idx) => Self::CoreFunc(FuncType::from_wasm_func_type(
engine,
ty.types[*idx].unwrap_func().clone(),
)),
TypeDef::CoreFunc(idx) => {
let subty = &ty.types[*idx];
Self::CoreFunc(FuncType::from_wasm_func_type(
engine,
subty.is_final,
subty.supertype,
subty.unwrap_func().clone(),
))
}
TypeDef::Resource(idx) => {
let resource_index = ty.types[*idx].ty;
let ty = match ty.resources.get(resource_index) {
Expand Down
9 changes: 7 additions & 2 deletions crates/wasmtime/src/runtime/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use std::path::Path;
use wasmparser::{Parser, ValidPayload, Validator};
use wasmtime_environ::{
CompiledModuleInfo, DefinedFuncIndex, DefinedMemoryIndex, EntityIndex, HostPtr, ModuleTypes,
ObjectKind, VMOffsets, VMSharedTypeIndex,
ObjectKind, TypeTrace, VMOffsets, VMSharedTypeIndex,
};
mod registry;

Expand Down Expand Up @@ -687,7 +687,12 @@ impl Module {
let engine = self.engine();
module
.imports()
.map(move |(module, field, ty)| ImportType::new(module, field, ty, types, engine))
.map(move |(imp_mod, imp_field, mut ty)| {
ty.canonicalize_for_runtime_usage(&mut |i| {
self.signatures().shared_type(i).unwrap()
});
ImportType::new(imp_mod, imp_field, ty, types, engine)
})
.collect::<Vec<_>>()
.into_iter()
}
Expand Down
Loading

0 comments on commit c617f51

Please sign in to comment.