Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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-length lists: this corresponds
/// to the 🔧 emoji in the component model specification
pub component_model_fixed_length_lists: 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_length_lists, wasm_component_model_fixed_length_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 TypeFixedLengthListIndex(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_length_lists: PrimaryMap<TypeFixedLengthListIndex, TypeFixedLengthList>,
}

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::FixedLengthList(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<TypeFixedLengthListIndex> for ComponentTypes { TypeFixedLengthList => fixed_length_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),
FixedLengthList(TypeFixedLengthListIndex),
}

/// 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 TypeFixedLengthList {
/// 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
62 changes: 60 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_length_lists: HashMap<TypeFixedLengthList, TypeFixedLengthListIndex>,

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_length_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::FixedLengthList(self.fixed_length_list_type(types, ty, *size)?)
}
ComponentDefinedType::Map(..) => {
bail!("support not implemented for map type");
Expand Down Expand Up @@ -575,6 +577,34 @@ impl ComponentTypesBuilder {
self.add_tuple_type(TypeTuple { types, abi })
}

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

pub(crate) fn new_fixed_length_list_type(
&mut self,
element: InterfaceType,
size: u32,
) -> TypeFixedLengthListIndex {
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.saturating_mul(size);
abi.size64 = element_abi.size64.saturating_mul(size);
abi.flat_count = element_abi
.flat_count
.zip(u8::try_from(size).ok())
.and_then(|(flat_count, size)| flat_count.checked_mul(size));
self.add_fixed_length_list_type(TypeFixedLengthList { 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 +721,14 @@ impl ComponentTypesBuilder {
intern_and_fill_flat_types!(self, tuples, ty)
}

/// Interns a new tuple type within this type information.
pub fn add_fixed_length_list_type(
&mut self,
ty: TypeFixedLengthList,
) -> TypeFixedLengthListIndex {
intern_and_fill_flat_types!(self, fixed_length_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 +864,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::FixedLengthList(i) => &self.type_info.fixed_length_lists[*i],
}
}
}
Expand Down Expand Up @@ -935,6 +974,7 @@ struct TypeInformationCache {
options: PrimaryMap<TypeOptionIndex, TypeInformation>,
results: PrimaryMap<TypeResultIndex, TypeInformation>,
lists: PrimaryMap<TypeListIndex, TypeInformation>,
fixed_length_lists: PrimaryMap<TypeFixedLengthListIndex, TypeInformation>,
}

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

fn fixed_length_lists(&mut self, types: &ComponentTypesBuilder, ty: &TypeFixedLengthList) {
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, TypeFixedLengthListIndex,
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::FixedLengthList(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::FixedLengthList(t) => {
self.translate_fixed_length_list(*t, src, dst_ty, dst);
}
}
}

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

fn translate_fixed_length_list(
&mut self,
src_ty: TypeFixedLengthListIndex,
src: &Source<'_>,
dst_ty: &InterfaceType,
dst: &Destination,
) {
let src_ty = &self.types[src_ty];
let dst_ty = match dst_ty {
InterfaceType::FixedLengthList(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
5 changes: 5 additions & 0 deletions crates/fuzzing/src/generators/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ impl Config {
component_model_threading,
component_model_error_context,
component_model_gc,
component_model_fixed_length_lists,
simd,
exceptions,
legacy_exceptions: _,
Expand All @@ -165,6 +166,8 @@ impl Config {
self.module_config.component_model_error_context =
component_model_error_context.unwrap_or(false);
self.module_config.component_model_gc = component_model_gc.unwrap_or(false);
self.module_config.component_model_fixed_length_lists =
component_model_fixed_length_lists.unwrap_or(false);

// Enable/disable proposals that wasm-smith has knobs for which will be
// read when creating `wasmtime::Config`.
Expand Down Expand Up @@ -290,6 +293,8 @@ impl Config {
cfg.wasm.component_model_error_context =
Some(self.module_config.component_model_error_context);
cfg.wasm.component_model_gc = Some(self.module_config.component_model_gc);
cfg.wasm.component_model_fixed_length_lists =
Some(self.module_config.component_model_fixed_length_lists);
cfg.wasm.custom_page_sizes = Some(self.module_config.config.custom_page_sizes_enabled);
cfg.wasm.epoch_interruption = Some(self.wasmtime.epoch_interruption);
cfg.wasm.extended_const = Some(self.module_config.config.extended_const_enabled);
Expand Down
2 changes: 2 additions & 0 deletions crates/fuzzing/src/generators/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct ModuleConfig {
pub component_model_threading: bool,
pub component_model_error_context: bool,
pub component_model_gc: bool,
pub component_model_fixed_length_lists: bool,
pub legacy_exceptions: bool,
pub shared_memory: bool,
}
Expand Down Expand Up @@ -79,6 +80,7 @@ impl<'a> Arbitrary<'a> for ModuleConfig {
component_model_threading: false,
component_model_error_context: false,
component_model_gc: false,
component_model_fixed_length_lists: false,
legacy_exceptions: false,
shared_memory: false,
function_references_enabled: config.gc_enabled,
Expand Down
Loading