Skip to content

Minimal implementation of fixed size lists to enable wit-bindgen runtime tests #10619

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
11 changes: 1 addition & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,9 @@ wasmtime_option_group! {
pub exceptions: Option<bool>,
/// DEPRECATED: Configure support for the legacy exceptions proposal.
pub legacy_exceptions: 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 @@ -1038,6 +1041,7 @@ impl CommonOptions {
("component-model-async", component_model_async, wasm_component_model_async)
("component-model-async", component_model_async_builtins, wasm_component_model_async_builtins)
("component-model-async", component_model_async_stackful, wasm_component_model_async_stackful)
("component-model", component_model_fixed_size_list, wasm_component_model_fixed_size_lists)
("component-model", component_model_error_context, wasm_component_model_error_context)
("threads", threads, wasm_threads)
("gc", gc, wasm_gc)
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 @@ -285,6 +287,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 @@ -358,6 +361,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 @@ -407,6 +411,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 @@ -582,6 +587,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 @@ -1121,6 +1127,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
42 changes: 40 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 @@ -111,6 +112,7 @@ impl ComponentTypesBuilder {
component_types: ComponentTypes::default(),
type_info: TypeInformationCache::default(),
resources: ResourcesBuilder::default(),
fixed_size_lists: HashMap::default(),
}
}

Expand Down Expand Up @@ -418,8 +420,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)?)
}
};
let info = self.type_information(&ret);
Expand Down Expand Up @@ -534,6 +536,27 @@ 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 abi = CanonicalAbiInfo::record((0..size).into_iter().map(|_| element_abi));
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 @@ -650,6 +673,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 @@ -785,6 +813,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 @@ -894,6 +923,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 @@ -1043,6 +1073,14 @@ impl TypeInformation {
self.build_record(ty.types.iter().map(|t| types.type_information(t)));
}

fn fixed_size_lists(&mut self, types: &ComponentTypesBuilder, ty: &TypeFixedSizeList) {
self.build_record(
(0..ty.size)
.into_iter()
.map(|_| types.type_information(&ty.element)),
);
}

fn enums(&mut self, _types: &ComponentTypesBuilder, _ty: &TypeEnum) {
self.depth = 1;
self.flat.push(FlatType::I32, FlatType::I32);
Expand Down
39 changes: 36 additions & 3 deletions crates/environ/src/fact/trampoline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ 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, StringEncoding, Transcode, TypeComponentLocalErrorContextTableIndex,
TypeEnumIndex, TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeOptionIndex,
TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex,
TypeVariantIndex, VariantInfo,
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 @@ -1098,6 +1098,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 @@ -1137,6 +1138,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 @@ -2830,6 +2834,35 @@ 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);

let srcs = src.record_field_srcs(
self.types,
(0..src_ty.size).into_iter().map(|_| src_ty.element),
);
let dsts = dst.record_field_dsts(
self.types,
(0..dst_ty.size).into_iter().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
10 changes: 10 additions & 0 deletions crates/wasmtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,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_feature(WasmFeatures::CM_FIXED_SIZE_LIST, enable);
self
}

/// This corresponds to the 📝 emoji in the component model specification.
///
/// Please note that Wasmtime's support for this feature is _very_
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 @@ -2873,6 +2873,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 @@ -156,6 +156,7 @@ impl TypeChecker<'_> {
(InterfaceType::Stream(_), _) => false,
(InterfaceType::ErrorContext(_), InterfaceType::ErrorContext(_)) => true,
(InterfaceType::ErrorContext(_), _) => false,
(InterfaceType::FixedSizeList(_), _) => todo!(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these are relatively targeted todo!() items mind filing an issue as a tracking issue and tagging these with // FIXME(#NNNN)?

}
}

Expand Down Expand Up @@ -757,6 +758,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!(),
}
}

Expand Down
4 changes: 4 additions & 0 deletions crates/wasmtime/src/runtime/component/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ impl Val {
InterfaceType::ErrorContext(_) => {
ErrorContext::linear_lift_from_flat(cx, ty, next(src))?.into_val()
}
InterfaceType::FixedSizeList(_) => todo!(),
})
}

Expand Down Expand Up @@ -345,6 +346,7 @@ impl Val {
InterfaceType::ErrorContext(_) => {
ErrorContext::linear_lift_from_memory(cx, ty, bytes)?.into_val()
}
InterfaceType::FixedSizeList(_) => todo!(),
})
}

Expand Down Expand Up @@ -503,6 +505,7 @@ impl Val {
)
}
(InterfaceType::ErrorContext(_), _) => unexpected(ty, self),
(InterfaceType::FixedSizeList(_), _) => todo!(),
}
}

Expand Down Expand Up @@ -668,6 +671,7 @@ impl Val {
)
}
(InterfaceType::ErrorContext(_), _) => unexpected(ty, self),
(InterfaceType::FixedSizeList(_), _) => todo!(),
}
}

Expand Down
Loading