Skip to content
2 changes: 2 additions & 0 deletions crates/c-api/src/component/types/val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub enum wasmtime_component_valtype_t {
Future(Box<wasmtime_component_future_type_t>),
Stream(Box<wasmtime_component_stream_type_t>),
ErrorContext,
FixedSizeList(Box<wasmtime_component_list_type_t>),
}

impl From<Type> for wasmtime_component_valtype_t {
Expand Down Expand Up @@ -61,6 +62,7 @@ impl From<Type> for wasmtime_component_valtype_t {
Type::Borrow(ty) => Self::Borrow(Box::new(ty.into())),
Type::Future(ty) => Self::Future(Box::new(ty.into())),
Type::Stream(ty) => Self::Stream(Box::new(ty.into())),
Type::FixedSizeList(ty) => Self::FixedSizeList(Box::new(ty.into())),
Type::ErrorContext => Self::ErrorContext,
}
}
Expand Down
5 changes: 5 additions & 0 deletions crates/c-api/src/component/val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ pub enum wasmtime_component_val_t {
Result(wasmtime_component_valresult_t),
Flags(wasmtime_component_valflags_t),
Resource(Box<wasmtime_component_resource_any_t>),
FixedSizeList(wasmtime_component_vallist_t),
}

impl Default for wasmtime_component_val_t {
Expand Down Expand Up @@ -276,6 +277,7 @@ impl From<&wasmtime_component_val_t> for Val {
wasmtime_component_val_t::Result(x) => Val::Result(x.into()),
wasmtime_component_val_t::Flags(x) => Val::Flags(x.into()),
wasmtime_component_val_t::Resource(x) => Val::Resource(x.resource),
wasmtime_component_val_t::FixedSizeList(x) => Val::FixedSizeList(x.into()),
}
}
}
Expand Down Expand Up @@ -317,6 +319,9 @@ impl From<&Val> for wasmtime_component_val_t {
Val::Future(_) => todo!(),
Val::Stream(_) => todo!(),
Val::ErrorContext(_) => todo!(),
Val::FixedSizeList(ty) => wasmtime_component_val_t::FixedSizeList(
wasmtime_component_vallist_t::from(ty.as_ref()),
),
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ wasmtime_option_group! {
pub exceptions: Option<bool>,
/// Whether or not any GC infrastructure in Wasmtime is enabled or not.
pub gc_support: Option<bool>,
/// Component model support for fixed size lists: this corresponds
/// to the 🔧 emoji in the component model specification
pub component_model_fixed_size_list: Option<bool>,
}

enum Wasm {
Expand Down Expand Up @@ -1067,6 +1070,7 @@ impl CommonOptions {
("component-model-async", component_model_async_stackful, wasm_component_model_async_stackful)
("component-model-async", component_model_threading, wasm_component_model_threading)
("component-model", component_model_error_context, wasm_component_model_error_context)
("component-model", component_model_fixed_size_list, wasm_component_model_fixed_size_lists)
("threads", threads, wasm_threads)
("gc", gc, wasm_gc)
("gc", reference_types, wasm_reference_types)
Expand Down
34 changes: 34 additions & 0 deletions crates/environ/src/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ indices! {
pub struct TypeResultIndex(u32);
/// Index pointing to a list type in the component model.
pub struct TypeListIndex(u32);
/// Index pointing to a fixed size list type in the component model.
pub struct TypeFixedSizeListIndex(u32);
/// Index pointing to a future type in the component model.
pub struct TypeFutureIndex(u32);

Expand Down Expand Up @@ -296,6 +298,7 @@ pub struct ComponentTypes {
pub(super) stream_tables: PrimaryMap<TypeStreamTableIndex, TypeStreamTable>,
pub(super) error_context_tables:
PrimaryMap<TypeComponentLocalErrorContextTableIndex, TypeErrorContextTable>,
pub(super) fixed_size_lists: PrimaryMap<TypeFixedSizeListIndex, TypeFixedSizeList>,
}

impl TypeTrace for ComponentTypes {
Expand Down Expand Up @@ -369,6 +372,7 @@ impl ComponentTypes {
InterfaceType::Enum(i) => &self[*i].abi,
InterfaceType::Option(i) => &self[*i].abi,
InterfaceType::Result(i) => &self[*i].abi,
InterfaceType::FixedSizeList(i) => &self[*i].abi,
}
}

Expand Down Expand Up @@ -418,6 +422,7 @@ impl_index! {
impl Index<TypeFutureTableIndex> for ComponentTypes { TypeFutureTable => future_tables }
impl Index<TypeStreamTableIndex> for ComponentTypes { TypeStreamTable => stream_tables }
impl Index<TypeComponentLocalErrorContextTableIndex> for ComponentTypes { TypeErrorContextTable => error_context_tables }
impl Index<TypeFixedSizeListIndex> for ComponentTypes { TypeFixedSizeList => fixed_size_lists }
}

// Additionally forward anything that can index `ModuleTypes` to `ModuleTypes`
Expand Down Expand Up @@ -595,6 +600,7 @@ pub enum InterfaceType {
Future(TypeFutureTableIndex),
Stream(TypeStreamTableIndex),
ErrorContext(TypeComponentLocalErrorContextTableIndex),
FixedSizeList(TypeFixedSizeListIndex),
}

/// Bye information about a type in the canonical ABI, with metadata for both
Expand Down Expand Up @@ -716,6 +722,23 @@ impl CanonicalAbiInfo {
return ret;
}

/// Returns the abi for a fixed size list
pub const fn fixed_size_list_static(
element: &CanonicalAbiInfo,
count: u32,
) -> CanonicalAbiInfo {
CanonicalAbiInfo {
size32: element.size32 * count,
align32: element.align32,
size64: element.size64 * count,
align64: element.align64,
flat_count: match element.flat_count {
None => None,
Some(c) => Some(c.saturating_mul(if count > 255 { 255u8 } else { count as u8 })),
},
}
}

/// Returns the delta from the current value of `offset` to align properly
/// and read the next record field of type `abi` for 32-bit memories.
pub fn next_field32(&self, offset: &mut u32) -> u32 {
Expand Down Expand Up @@ -1175,6 +1198,17 @@ pub struct TypeList {
pub element: InterfaceType,
}

/// Shape of a "fixed size list" interface type.
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeFixedSizeList {
/// The element type of the list.
pub element: InterfaceType,
/// The fixed length of the list.
pub size: u32,
/// Byte information about this type in the canonical ABI.
pub abi: CanonicalAbiInfo,
}

/// Maximum number of flat types, for either params or results.
pub const MAX_FLAT_TYPES: usize = if MAX_FLAT_PARAMS > MAX_FLAT_RESULTS {
MAX_FLAT_PARAMS
Expand Down
58 changes: 56 additions & 2 deletions crates/environ/src/component/types_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub struct ComponentTypesBuilder {
future_tables: HashMap<TypeFutureTable, TypeFutureTableIndex>,
stream_tables: HashMap<TypeStreamTable, TypeStreamTableIndex>,
error_context_tables: HashMap<TypeErrorContextTable, TypeComponentLocalErrorContextTableIndex>,
fixed_size_lists: HashMap<TypeFixedSizeList, TypeFixedSizeListIndex>,

component_types: ComponentTypes,
module_types: ModuleTypesBuilder,
Expand Down Expand Up @@ -118,6 +119,7 @@ impl ComponentTypesBuilder {
type_info: TypeInformationCache::default(),
resources: ResourcesBuilder::default(),
abstract_resources: 0,
fixed_size_lists: HashMap::default(),
}
}

Expand Down Expand Up @@ -456,8 +458,8 @@ impl ComponentTypesBuilder {
ComponentDefinedType::Stream(ty) => {
InterfaceType::Stream(self.stream_table_type(types, ty)?)
}
ComponentDefinedType::FixedSizeList(..) => {
bail!("support not implemented for fixed-size-lists");
ComponentDefinedType::FixedSizeList(ty, size) => {
InterfaceType::FixedSizeList(self.fixed_size_list_type(types, ty, *size)?)
}
ComponentDefinedType::Map(..) => {
bail!("support not implemented for map type");
Expand Down Expand Up @@ -575,6 +577,33 @@ impl ComponentTypesBuilder {
self.add_tuple_type(TypeTuple { types, abi })
}

fn fixed_size_list_type(
&mut self,
types: TypesRef<'_>,
ty: &ComponentValType,
size: u32,
) -> Result<TypeFixedSizeListIndex> {
assert_eq!(types.id(), self.module_types.validator_id());
let element = self.valtype(types, ty)?;
Ok(self.new_fixed_size_list_type(element, size))
}

pub(crate) fn new_fixed_size_list_type(
&mut self,
element: InterfaceType,
size: u32,
) -> TypeFixedSizeListIndex {
let element_abi = self.component_types.canonical_abi(&element);
let mut abi = element_abi.clone();
// this assumes that size32 is already rounded up to alignment
abi.size32 = element_abi.size32 * size;
abi.size64 = element_abi.size64 * size;
abi.flat_count = element_abi
.flat_count
.map(|c| c.saturating_mul(size.min(255) as u8));
self.add_fixed_size_list_type(TypeFixedSizeList { element, size, abi })
}

fn flags_type(&mut self, flags: &IndexSet<KebabString>) -> TypeFlagsIndex {
let flags = TypeFlags {
names: flags.iter().map(|s| s.to_string()).collect(),
Expand Down Expand Up @@ -691,6 +720,11 @@ impl ComponentTypesBuilder {
intern_and_fill_flat_types!(self, tuples, ty)
}

/// Interns a new tuple type within this type information.
pub fn add_fixed_size_list_type(&mut self, ty: TypeFixedSizeList) -> TypeFixedSizeListIndex {
intern_and_fill_flat_types!(self, fixed_size_lists, ty)
}

/// Interns a new variant type within this type information.
pub fn add_variant_type(&mut self, ty: TypeVariant) -> TypeVariantIndex {
intern_and_fill_flat_types!(self, variants, ty)
Expand Down Expand Up @@ -826,6 +860,7 @@ impl ComponentTypesBuilder {
InterfaceType::Enum(i) => &self.type_info.enums[*i],
InterfaceType::Option(i) => &self.type_info.options[*i],
InterfaceType::Result(i) => &self.type_info.results[*i],
InterfaceType::FixedSizeList(i) => &self.type_info.fixed_size_lists[*i],
}
}
}
Expand Down Expand Up @@ -935,6 +970,7 @@ struct TypeInformationCache {
options: PrimaryMap<TypeOptionIndex, TypeInformation>,
results: PrimaryMap<TypeResultIndex, TypeInformation>,
lists: PrimaryMap<TypeListIndex, TypeInformation>,
fixed_size_lists: PrimaryMap<TypeFixedSizeListIndex, TypeInformation>,
}

struct TypeInformation {
Expand Down Expand Up @@ -1084,6 +1120,24 @@ impl TypeInformation {
self.build_record(ty.types.iter().map(|t| types.type_information(t)));
}

fn fixed_size_lists(&mut self, types: &ComponentTypesBuilder, ty: &TypeFixedSizeList) {
let element_info = types.type_information(&ty.element);
self.depth = 1 + element_info.depth;
self.has_borrow = element_info.has_borrow;
match element_info.flat.as_flat_types() {
Some(types) => {
'outer: for _ in 0..ty.size {
for (t32, t64) in types.memory32.iter().zip(types.memory64) {
if !self.flat.push(*t32, *t64) {
break 'outer;
}
}
}
}
None => self.flat.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap(),
}
}

fn enums(&mut self, _types: &ComponentTypesBuilder, _ty: &TypeEnum) {
self.depth = 1;
self.flat.push(FlatType::I32, FlatType::I32);
Expand Down
Loading
Loading