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 1 commit 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
33 changes: 11 additions & 22 deletions Cargo.lock

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

20 changes: 10 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -307,16 +307,16 @@ wit-bindgen = { version = "0.41.0", default-features = false }
wit-bindgen-rust-macro = { version = "0.41.0", default-features = false }

# wasm-tools family:
wasmparser = { version = "0.229.0", default-features = false, features = ['simd'] }
wat = "1.229.0"
wast = "229.0.0"
wasmprinter = "0.229.0"
wasm-encoder = "0.229.0"
wasm-smith = "0.229.0"
wasm-mutate = "0.229.0"
wit-parser = "0.229.0"
wit-component = "0.229.0"
wasm-wave = "0.229.0"
wasmparser = { git = "https://github.com/cpetig/wasm-tools", branch = "fixed-length-list" }
wat = { git = "https://github.com/cpetig/wasm-tools", branch = "fixed-length-list" }
wast = { git = "https://github.com/cpetig/wasm-tools", branch = "fixed-length-list" }
wasmprinter = { git = "https://github.com/cpetig/wasm-tools", branch = "fixed-length-list" }
wasm-encoder = { git = "https://github.com/cpetig/wasm-tools", branch = "fixed-length-list" }
wasm-smith = { git = "https://github.com/cpetig/wasm-tools", branch = "fixed-length-list" }
wasm-mutate = { git = "https://github.com/cpetig/wasm-tools", branch = "fixed-length-list" }
wit-parser = { git = "https://github.com/cpetig/wasm-tools", branch = "fixed-length-list" }
wit-component = { git = "https://github.com/cpetig/wasm-tools", branch = "fixed-length-list" }
wasm-wave = { git = "https://github.com/cpetig/wasm-tools", branch = "fixed-length-list" }

# Non-Bytecode Alliance maintained dependencies:
# --------------------------
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 @@ -386,6 +386,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 @@ -1015,6 +1018,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)
("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 @@ -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 @@ -1129,6 +1135,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
29 changes: 29 additions & 0 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,6 +420,9 @@ impl ComponentTypesBuilder {
ComponentDefinedType::Stream(ty) => {
InterfaceType::Stream(self.stream_table_type(types, ty)?)
}
ComponentDefinedType::FixedSizeList(ty, size) => {
InterfaceType::FixedSizeList(self.fixed_size_list_type(types, ty, *size)?)
}
};
let info = self.type_information(&ret);
if info.depth > MAX_TYPE_DEPTH {
Expand Down Expand Up @@ -531,6 +536,19 @@ 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));
Comment on lines +547 to +548
Copy link
Member

Choose a reason for hiding this comment

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

Could this be done with a new helper? I'm wary of defining an O(n) thing here and relying on LLVM to optimize it. This could make list<T, BigNumber> excessively slow in debug builds of Wasmtime for example.

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 @@ -647,6 +665,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 @@ -782,6 +805,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 @@ -891,6 +915,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 @@ -1040,6 +1065,10 @@ 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)));
Copy link
Member

Choose a reason for hiding this comment

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

Same as above, I think it'd be best to avoid "feigning" a record here and using a dedicated computation for fixed-size lists to avoid O(n) in the length of the list

}

fn enums(&mut self, _types: &ComponentTypesBuilder, _ty: &TypeEnum) {
self.depth = 1;
self.flat.push(FlatType::I32, FlatType::I32);
Expand Down
35 changes: 30 additions & 5 deletions crates/environ/src/fact/trampoline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@
//! can be somewhat arbitrary, an intentional decision.

use crate::component::{
CanonicalAbiInfo, ComponentTypesBuilder, FixedEncoding as FE, FlatType, InterfaceType,
StringEncoding, Transcode, TypeComponentLocalErrorContextTableIndex, TypeEnumIndex,
TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeOptionIndex, TypeRecordIndex,
TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex,
TypeVariantIndex, VariantInfo, FLAG_MAY_ENTER, FLAG_MAY_LEAVE, MAX_FLAT_PARAMS,
CanonicalAbiInfo, ComponentTypesBuilder, FixedEncoding as FE, FlatType, InterfaceType, StringEncoding, Transcode, TypeComponentLocalErrorContextTableIndex, TypeEnumIndex, TypeFixedSizeListIndex, TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex, TypeVariantIndex, VariantInfo, FLAG_MAY_ENTER, FLAG_MAY_LEAVE, MAX_FLAT_PARAMS
};
use crate::fact::signature::Signature;
use crate::fact::transcode::Transcoder;
Expand Down Expand Up @@ -1044,6 +1040,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 @@ -1083,6 +1080,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 @@ -2628,6 +2628,31 @@ 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);
}
}
Comment on lines +2631 to +2654
Copy link
Member

Choose a reason for hiding this comment

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

This is a bit of an interesting case. Effectively what this is doing is unrolling list<T, N> unconditionally, but I don't think that's necessarily appropriate for large N. I think it's fine to unroll for small N (perhaps only when T is small?). Ideally this would have a budget of sorts where a fixed number Cost is divided by the cost of T , and that's the maximal unrolling. That way we don't generate exponential amounts of code here for list<list<T, N>, N>.

Basically what I'm saying is that I think we must implement the loop-based variant of translating lists, not just the fully-unrolled version of translating lists.


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
}

#[doc(hidden)] // FIXME(#3427) - if/when implemented then un-hide this
pub fn wasm_exceptions(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::EXCEPTIONS, enable);
Expand Down
Loading
Loading