Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
17 changes: 17 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 @@ -1175,6 +1181,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
118 changes: 115 additions & 3 deletions crates/environ/src/fact/trampoline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ use crate::component::{
CanonicalAbiInfo, ComponentTypesBuilder, FLAG_MAY_ENTER, FLAG_MAY_LEAVE, FixedEncoding as FE,
FlatType, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, PREPARE_ASYNC_NO_RESULT,
PREPARE_ASYNC_WITH_RESULT, START_FLAG_ASYNC_CALLEE, StringEncoding, Transcode,
TypeComponentLocalErrorContextTableIndex, TypeEnumIndex, TypeFlagsIndex, TypeFutureTableIndex,
TypeListIndex, TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex,
TypeStreamTableIndex, TypeTupleIndex, TypeVariantIndex, VariantInfo,
TypeComponentLocalErrorContextTableIndex, TypeEnumIndex, TypeFixedSizeListIndex,
TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeOptionIndex, TypeRecordIndex,
TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex,
TypeVariantIndex, VariantInfo,
};
use crate::fact::signature::Signature;
use crate::fact::transcode::Transcoder;
Expand Down Expand Up @@ -1126,6 +1127,7 @@ impl<'a, 'b> Compiler<'a, 'b> {
| InterfaceType::Future(_)
| InterfaceType::Stream(_)
| InterfaceType::ErrorContext(_) => 1,
InterfaceType::FixedSizeList(i) => self.types[*i].size as usize,
};

match self.fuel.checked_sub(cost) {
Expand Down Expand Up @@ -1165,6 +1167,9 @@ impl<'a, 'b> Compiler<'a, 'b> {
InterfaceType::ErrorContext(t) => {
self.translate_error_context(*t, src, dst_ty, dst)
}
InterfaceType::FixedSizeList(t) => {
self.translate_fixed_size_list(*t, src, dst_ty, dst);
}
}
}

Expand Down Expand Up @@ -2858,6 +2863,113 @@ impl<'a, 'b> Compiler<'a, 'b> {
}
}

fn translate_fixed_size_list(
&mut self,
src_ty: TypeFixedSizeListIndex,
src: &Source<'_>,
dst_ty: &InterfaceType,
dst: &Destination,
) {
let src_ty = &self.types[src_ty];
let dst_ty = match dst_ty {
InterfaceType::FixedSizeList(t) => &self.types[*t],
_ => panic!("expected a fixed size list"),
};

// TODO: subtyping
assert_eq!(src_ty.size, dst_ty.size);

match (&src, &dst) {
// Generate custom code for memory to memory copy
(Source::Memory(src_mem), Destination::Memory(dst_mem)) => {
let src_mem_opts = match &src_mem.opts.data_model {
DataModel::Gc {} => todo!("CM+GC"),
DataModel::LinearMemory(opts) => opts,
};
let dst_mem_opts = match &dst_mem.opts.data_model {
DataModel::Gc {} => todo!("CM+GC"),
DataModel::LinearMemory(opts) => opts,
};
let src_element_bytes = self.types.size_align(src_mem_opts, &src_ty.element).0;
let dst_element_bytes = self.types.size_align(dst_mem_opts, &dst_ty.element).0;
assert_ne!(src_element_bytes, 0);
assert_ne!(dst_element_bytes, 0);

// because data is stored in-line, we assume that source and destination memory have been validated upstream

self.instruction(LocalGet(src_mem.addr.idx));
if src_mem.offset != 0 {
self.ptr_uconst(src_mem_opts, src_mem.offset);
self.ptr_add(src_mem_opts);
}
let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
self.instruction(LocalGet(dst_mem.addr.idx));
if dst_mem.offset != 0 {
self.ptr_uconst(dst_mem_opts, dst_mem.offset);
self.ptr_add(dst_mem_opts);
}
let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());

self.instruction(I32Const(src_ty.size as i32));
let remaining = self.local_set_new_tmp(ValType::I32);

self.instruction(Loop(BlockType::Empty));

// Translate the next element in the list
let element_src = Source::Memory(Memory {
opts: src_mem.opts,
offset: 0,
addr: TempLocal::new(cur_src_ptr.idx, cur_src_ptr.ty),
});
let element_dst = Destination::Memory(Memory {
opts: dst_mem.opts,
offset: 0,
addr: TempLocal::new(cur_dst_ptr.idx, cur_dst_ptr.ty),
});
self.translate(&src_ty.element, &element_src, &dst_ty.element, &element_dst);

// Update the two loop pointers
self.instruction(LocalGet(cur_src_ptr.idx));
self.ptr_uconst(src_mem_opts, src_element_bytes);
self.ptr_add(src_mem_opts);
self.instruction(LocalSet(cur_src_ptr.idx));
self.instruction(LocalGet(cur_dst_ptr.idx));
self.ptr_uconst(dst_mem_opts, dst_element_bytes);
self.ptr_add(dst_mem_opts);
self.instruction(LocalSet(cur_dst_ptr.idx));

// Update the remaining count, falling through to break out if it's zero
// now.
self.instruction(LocalGet(remaining.idx));
self.ptr_iconst(src_mem_opts, -1);
self.ptr_add(src_mem_opts);
self.instruction(LocalTee(remaining.idx));
self.ptr_br_if(src_mem_opts, 0);
self.instruction(End); // end of loop

self.free_temp_local(cur_dst_ptr);
self.free_temp_local(cur_src_ptr);
self.free_temp_local(remaining);
return;
}
// for the non-memory-to-memory case fall back to using generic tuple translation
(_, _) => {
// Assumes that the number of elements are small enough for this unrolling
assert!(
src_ty.size as usize <= MAX_FLAT_PARAMS
&& dst_ty.size as usize <= MAX_FLAT_PARAMS
);
let srcs =
src.record_field_srcs(self.types, (0..src_ty.size).map(|_| src_ty.element));
let dsts =
dst.record_field_dsts(self.types, (0..dst_ty.size).map(|_| dst_ty.element));
for (src, dst) in srcs.zip(dsts) {
self.translate(&src_ty.element, &src, &dst_ty.element, &dst);
}
}
}
}

fn translate_variant(
&mut self,
src_ty: TypeVariantIndex,
Expand Down
13 changes: 12 additions & 1 deletion crates/wasmtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,16 @@ impl Config {
self
}

/// This corresponds to the 🔧 emoji in the component model specification.
///
/// Please note that Wasmtime's support for this feature is _very_
/// incomplete.
#[cfg(feature = "component-model")]
pub fn wasm_component_model_fixed_size_lists(&mut self, enable: bool) -> &mut Self {
self.wasm_features(WasmFeatures::CM_FIXED_SIZE_LIST, enable);
self
}

/// Configures whether the [Exception-handling proposal][proposal] is enabled or not.
///
/// [proposal]: https://github.com/WebAssembly/exception-handling
Expand Down Expand Up @@ -2264,7 +2274,8 @@ impl Config {
| WasmFeatures::CM_ASYNC_BUILTINS
| WasmFeatures::CM_THREADING
| WasmFeatures::CM_ERROR_CONTEXT
| WasmFeatures::CM_GC;
| WasmFeatures::CM_GC
| WasmFeatures::CM_FIXED_SIZE_LIST;

#[allow(unused_mut, reason = "easier to avoid #[cfg]")]
let mut unsupported = !features_known_to_wasmtime;
Expand Down
1 change: 1 addition & 0 deletions crates/wasmtime/src/runtime/component/func/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2895,6 +2895,7 @@ pub fn desc(ty: &InterfaceType) -> &'static str {
InterfaceType::Future(_) => "future",
InterfaceType::Stream(_) => "stream",
InterfaceType::ErrorContext(_) => "error-context",
InterfaceType::FixedSizeList(_) => "list<_, N>",
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/wasmtime/src/runtime/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ impl TypeChecker<'_> {
(InterfaceType::Stream(_), _) => false,
(InterfaceType::ErrorContext(_), InterfaceType::ErrorContext(_)) => true,
(InterfaceType::ErrorContext(_), _) => false,
(InterfaceType::FixedSizeList(_), _) => todo!(), // FIXME(#12279)
}
}

Expand Down Expand Up @@ -855,6 +856,7 @@ impl Type {
InterfaceType::Future(index) => Type::Future(instance.future_type(*index)),
InterfaceType::Stream(index) => Type::Stream(instance.stream_type(*index)),
InterfaceType::ErrorContext(_) => Type::ErrorContext,
InterfaceType::FixedSizeList(_) => todo!(), // FIXME(#12279)
}
}

Expand Down
Loading