diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index 51fbd9b1ea63..6cc61e6cfb07 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -409,6 +409,9 @@ wasmtime_option_group! { pub exceptions: Option, /// Whether or not any GC infrastructure in Wasmtime is enabled or not. pub gc_support: Option, + /// Component model support for fixed-length lists: this corresponds + /// to the 🔧 emoji in the component model specification + pub component_model_fixed_length_lists: Option, } enum Wasm { @@ -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) diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs index 8db6b800c4fe..0f1a811a0d0f 100644 --- a/crates/environ/src/component/types.rs +++ b/crates/environ/src/component/types.rs @@ -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); @@ -296,6 +298,7 @@ pub struct ComponentTypes { pub(super) stream_tables: PrimaryMap, pub(super) error_context_tables: PrimaryMap, + pub(super) fixed_length_lists: PrimaryMap, } impl TypeTrace for ComponentTypes { @@ -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, } } @@ -418,6 +422,7 @@ impl_index! { impl Index for ComponentTypes { TypeFutureTable => future_tables } impl Index for ComponentTypes { TypeStreamTable => stream_tables } impl Index for ComponentTypes { TypeErrorContextTable => error_context_tables } + impl Index for ComponentTypes { TypeFixedLengthList => fixed_length_lists } } // Additionally forward anything that can index `ModuleTypes` to `ModuleTypes` @@ -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 @@ -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 diff --git a/crates/environ/src/component/types_builder.rs b/crates/environ/src/component/types_builder.rs index 090081388a2b..ddfe291b0826 100644 --- a/crates/environ/src/component/types_builder.rs +++ b/crates/environ/src/component/types_builder.rs @@ -50,6 +50,7 @@ pub struct ComponentTypesBuilder { future_tables: HashMap, stream_tables: HashMap, error_context_tables: HashMap, + fixed_length_lists: HashMap, component_types: ComponentTypes, module_types: ModuleTypesBuilder, @@ -118,6 +119,7 @@ impl ComponentTypesBuilder { type_info: TypeInformationCache::default(), resources: ResourcesBuilder::default(), abstract_resources: 0, + fixed_length_lists: HashMap::default(), } } @@ -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"); @@ -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 { + 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) -> TypeFlagsIndex { let flags = TypeFlags { names: flags.iter().map(|s| s.to_string()).collect(), @@ -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) @@ -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], } } } @@ -935,6 +974,7 @@ struct TypeInformationCache { options: PrimaryMap, results: PrimaryMap, lists: PrimaryMap, + fixed_length_lists: PrimaryMap, } struct TypeInformation { @@ -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); diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index f75c30ccf34a..2df0b1e6babb 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -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; @@ -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) { @@ -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); + } } } @@ -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, diff --git a/crates/fuzzing/src/generators/config.rs b/crates/fuzzing/src/generators/config.rs index d4782a77c5de..1663e963c58a 100644 --- a/crates/fuzzing/src/generators/config.rs +++ b/crates/fuzzing/src/generators/config.rs @@ -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: _, @@ -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`. @@ -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); diff --git a/crates/fuzzing/src/generators/module.rs b/crates/fuzzing/src/generators/module.rs index 58e10cf90316..db89d56b5b82 100644 --- a/crates/fuzzing/src/generators/module.rs +++ b/crates/fuzzing/src/generators/module.rs @@ -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, } @@ -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, diff --git a/crates/test-util/src/wasmtime_wast.rs b/crates/test-util/src/wasmtime_wast.rs index a20f011b1261..d9a22a8684e8 100644 --- a/crates/test-util/src/wasmtime_wast.rs +++ b/crates/test-util/src/wasmtime_wast.rs @@ -43,6 +43,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wast::TestConfig) { component_model_threading, component_model_error_context, component_model_gc, + component_model_fixed_length_lists, nan_canonicalization, simd, exceptions, @@ -72,6 +73,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wast::TestConfig) { let component_model_threading = component_model_threading.unwrap_or(false); let component_model_error_context = component_model_error_context.unwrap_or(false); let component_model_gc = component_model_gc.unwrap_or(false); + let component_model_fixed_length_lists = component_model_fixed_length_lists.unwrap_or(false); let nan_canonicalization = nan_canonicalization.unwrap_or(false); let relaxed_simd = relaxed_simd.unwrap_or(false); let legacy_exceptions = legacy_exceptions.unwrap_or(false); @@ -111,6 +113,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wast::TestConfig) { .wasm_component_model_threading(component_model_threading) .wasm_component_model_error_context(component_model_error_context) .wasm_component_model_gc(component_model_gc) + .wasm_component_model_fixed_length_lists(component_model_fixed_length_lists) .wasm_exceptions(exceptions) .wasm_stack_switching(stack_switching) .cranelift_nan_canonicalization(nan_canonicalization); diff --git a/crates/test-util/src/wast.rs b/crates/test-util/src/wast.rs index c07dda1f8b52..5408ee33feb4 100644 --- a/crates/test-util/src/wast.rs +++ b/crates/test-util/src/wast.rs @@ -268,6 +268,7 @@ macro_rules! foreach_config_option { component_model_threading component_model_error_context component_model_gc + component_model_fixed_length_lists simd gc_types exceptions diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index d4b4485d9de1..ac6850648165 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -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_length_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 @@ -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; diff --git a/crates/wasmtime/src/runtime/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs index add17eddea40..b88ebdd5c6c9 100644 --- a/crates/wasmtime/src/runtime/component/func/typed.rs +++ b/crates/wasmtime/src/runtime/component/func/typed.rs @@ -2895,6 +2895,7 @@ pub fn desc(ty: &InterfaceType) -> &'static str { InterfaceType::Future(_) => "future", InterfaceType::Stream(_) => "stream", InterfaceType::ErrorContext(_) => "error-context", + InterfaceType::FixedLengthList(_) => "list<_, N>", } } diff --git a/crates/wasmtime/src/runtime/component/types.rs b/crates/wasmtime/src/runtime/component/types.rs index 948fc8e51f9d..63aff29672d1 100644 --- a/crates/wasmtime/src/runtime/component/types.rs +++ b/crates/wasmtime/src/runtime/component/types.rs @@ -158,6 +158,7 @@ impl TypeChecker<'_> { (InterfaceType::Stream(_), _) => false, (InterfaceType::ErrorContext(_), InterfaceType::ErrorContext(_)) => true, (InterfaceType::ErrorContext(_), _) => false, + (InterfaceType::FixedLengthList(_), _) => todo!(), // FIXME(#12279) } } @@ -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::FixedLengthList(_) => todo!(), // FIXME(#12279) } } diff --git a/crates/wasmtime/src/runtime/component/values.rs b/crates/wasmtime/src/runtime/component/values.rs index d0b1d0209db2..695cb140a864 100644 --- a/crates/wasmtime/src/runtime/component/values.rs +++ b/crates/wasmtime/src/runtime/component/values.rs @@ -215,6 +215,7 @@ impl Val { InterfaceType::ErrorContext(_) => { ErrorContext::linear_lift_from_flat(cx, ty, next(src))?.into_val() } + InterfaceType::FixedLengthList(_) => todo!(), // FIXME(#12279) }) } @@ -341,6 +342,7 @@ impl Val { InterfaceType::ErrorContext(_) => { ErrorContext::linear_lift_from_memory(cx, ty, bytes)?.into_val() } + InterfaceType::FixedLengthList(_) => todo!(), // FIXME(#12279) }) } @@ -491,6 +493,7 @@ impl Val { ) } (InterfaceType::ErrorContext(_), _) => unexpected(ty, self), + (InterfaceType::FixedLengthList(_), _) => todo!(), // FIXME(#12279) } } @@ -644,6 +647,7 @@ impl Val { ) } (InterfaceType::ErrorContext(_), _) => unexpected(ty, self), + (InterfaceType::FixedLengthList(_), _) => todo!(), // FIXME(#12279) } } diff --git a/tests/misc_testsuite/component-model/fixed_length_lists.wast b/tests/misc_testsuite/component-model/fixed_length_lists.wast new file mode 100644 index 000000000000..9e1d76563bd0 --- /dev/null +++ b/tests/misc_testsuite/component-model/fixed_length_lists.wast @@ -0,0 +1,422 @@ +;;! multi_memory = true +;;! component_model_fixed_length_lists = true + +;; This contains two components which exercise fixed-size-list +;; types. The first acts as a roundtrip, the second calls the first +;; and compares the results. + +;; As this was written with C++ and wit-bindgen the functions +;; are not expected to be easily human readable. + +;; The exported function run() from the second component +;; calls nested-roundtrip([[1, 2], [3, 4]], [[-1, -2], [-3, -4]]) +;; from the first module and compares the resulting tuple with a +;; concatenation of the inputs. + +;; Every mismatch increases the return value by 1. + +(component + (component (;0;) + (type $ty-test:fixed-size-lists/to-test (;0;) + (instance + (type (;0;) (list u32 2)) + (type (;1;) (list 0 2)) + (type (;2;) (list s32 2)) + (type (;3;) (list 2 2)) + (type (;4;) (tuple 1 3)) + (type (;5;) (func (param "a" 1) (param "b" 3) (result 4))) + (export (;0;) "nested-roundtrip" (func (type 5))) + ) + ) + (import "test:fixed-size-lists/to-test" (instance $test:fixed-size-lists/to-test (;0;) (type $ty-test:fixed-size-lists/to-test))) + (core module $main (;0;) + (type (;0;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;1;) (func (result i32))) + (import "test:fixed-size-lists/to-test" "nested-roundtrip" (func (;0;) (type 0))) + (memory (;0;) 2) + (global (;0;) (mut i32) (i32.const 67232)) + (export "memory" (memory 0)) + ;; test runner, pass the values from the address 1024ff in registers + ;; to the roundtrip function and + ;; return the number of non-matching results + (export "run" (func 1)) + (func (;1;) (type 1) (result i32) + (local i32 i32 i32 i32 i32 i32 i32 i32 i32) + (global.set 0 + (local.tee 0 + (i32.add + (global.get 0) + (i32.const -64)))) + (i64.store + (i32.add + (local.tee 1 + (i32.add + (local.get 0) + (i32.const 16))) + (i32.const 8)) + (i64.load align=4 + (i32.const 1032))) + (i64.store + (i32.add + (local.get 0) + (i32.const 8)) + (i64.load align=4 + (i32.const 1048))) + (i64.store offset=16 + (local.get 0) + (i64.load align=4 + (i32.const 1024))) + (i64.store + (local.get 0) + (i64.load align=4 + (i32.const 1040))) + (global.set 0 + (local.tee 2 + (i32.sub + (global.get 0) + (i32.const 32)))) + (call 0 + (i32.load + (local.get 1)) + (i32.load offset=4 + (local.get 1)) + (i32.load offset=8 + (local.get 1)) + (i32.load offset=12 + (local.get 1)) + (i32.load + (local.get 0)) + (i32.load offset=4 + (local.get 0)) + (i32.load offset=8 + (local.get 0)) + (i32.load offset=12 + (local.get 0)) + (local.get 2)) + (i64.store offset=24 align=4 + (local.tee 1 + (i32.add + (local.get 0) + (i32.const 32))) + (i64.load offset=24 + (local.get 2))) + (i64.store offset=16 align=4 + (local.get 1) + (i64.load offset=16 + (local.get 2))) + (i64.store offset=8 align=4 + (local.get 1) + (i64.load offset=8 + (local.get 2))) + (i64.store align=4 + (local.get 1) + (i64.load + (local.get 2))) + (global.set 0 + (i32.add + (local.get 2) + (i32.const 32))) + (local.set 2 + (i32.load offset=48 + (local.get 0))) + (i32.add + (i32.add + (i32.add + (i32.add + (i32.add + (i32.add + (i32.add + (i32.ne + (i32.load offset=32 + (local.get 0)) + (local.set 3 + (i32.load offset=36 + (local.get 0))) + (local.set 4 + (i32.load offset=52 + (local.get 0))) + (local.set 5 + (i32.load offset=40 + (local.get 0))) + (local.set 6 + (i32.load offset=56 + (local.get 0))) + (local.set 7 + (i32.load offset=44 + (local.get 0))) + (local.set 8 + (i32.load offset=60 + (local.get 0))) + ;; here the output values are checked + (global.set 0 + (i32.sub + (local.get 0) + (i32.const -64))) + (i32.const 1)) + (i32.ne + (local.get 2) + (i32.const -1))) + (i32.ne + (local.get 3) + (i32.const 2))) + (i32.ne + (local.get 4) + (i32.const -2))) + (i32.ne + (local.get 5) + (i32.const 3))) + (i32.ne + (local.get 6) + (i32.const -3))) + (i32.ne + (local.get 7) + (i32.const 4))) + (i32.ne + (local.get 8) + (i32.const -4))) + ) + ;; here are the input values + (data (;0;) (i32.const 1024) "\01\00\00\00\02\00\00\00\03\00\00\00\04\00\00\00\ff\ff\ff\ff\fe\ff\ff\ff\fd\ff\ff\ff\fc\ff\ff\ff") + (data (;1;) (i32.const 1056) "\ff\ff\ff\ff\00\00\02") + (@producers + (processed-by "wit-component" "0.243.0") + ) + ) + (core module $wit-component-shim-module (;1;) + (type (;0;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (table (;0;) 1 1 funcref) + (export "0" (func 0)) + (export "$imports" (table 0)) + (func (;0;) (type 0) (param i32 i32 i32 i32 i32 i32 i32 i32 i32) + (call_indirect (type 0) + (local.get 0) + (local.get 1) + (local.get 2) + (local.get 3) + (local.get 4) + (local.get 5) + (local.get 6) + (local.get 7) + (local.get 8) + (i32.const 0)) + ) + (@producers + (processed-by "wit-component" "0.243.0") + ) + ) + (core module $wit-component-fixup (;2;) + (type (;0;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (import "" "0" (func (;0;) (type 0))) + (import "" "$imports" (table (;0;) 1 1 funcref)) + (elem (;0;) (i32.const 0) func 0) + (@producers + (processed-by "wit-component" "0.243.0") + ) + ) + (core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module)) + (alias core export $wit-component-shim-instance "0" (core func $indirect-test:fixed-size-lists/to-test-nested-roundtrip (;0;))) + (core instance $test:fixed-size-lists/to-test (;1;) + (export "nested-roundtrip" (func $indirect-test:fixed-size-lists/to-test-nested-roundtrip)) + ) + (core instance $main (;2;) (instantiate $main + (with "test:fixed-size-lists/to-test" (instance $test:fixed-size-lists/to-test)) + ) + ) + (alias core export $main "memory" (core memory $memory (;0;))) + (alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;))) + (alias export $test:fixed-size-lists/to-test "nested-roundtrip" (func $nested-roundtrip (;0;))) + (core func $"#core-func1 indirect-test:fixed-size-lists/to-test-nested-roundtrip" (@name "indirect-test:fixed-size-lists/to-test-nested-roundtrip") (;1;) (canon lower (func $nested-roundtrip) (memory $memory))) + (core instance $fixup-args (;3;) + (export "$imports" (table $"shim table")) + (export "0" (func $"#core-func1 indirect-test:fixed-size-lists/to-test-nested-roundtrip")) + ) + (core instance $fixup (;4;) (instantiate $wit-component-fixup + (with "" (instance $fixup-args)) + ) + ) + (type (;1;) (func (result u32))) + (alias core export $main "run" (core func $run (;2;))) + (func $run (;1;) (type 1) (canon lift (core func $run))) + (export $"#func2 run" (@name "run") (;2;) "run" (func $run)) + (@producers + (processed-by "wit-component" "0.243.0") + ) + ) + (component (;1;) + (core module $main (;0;) + (type (;0;) (func (param i32 i32 i32 i32 i32 i32 i32 i32) (result i32))) + (memory (;0;) 2) + (global (;0;) (mut i32) (i32.const 67248)) + (export "memory" (memory 0)) + (export "test:fixed-size-lists/to-test#nested-roundtrip" (func 0)) + ;; This is an obfuscated store from the stack to address 1056, + ;; which is then returned + (func (;0;) (type 0) (param i32 i32 i32 i32 i32 i32 i32 i32) (result i32) + (local i32 i32 i64 i64) + (global.set 0 + (local.tee 8 + (i32.sub + (global.get 0) + (i32.const 96)))) + (i32.store + (local.tee 9 + (i32.add + (local.get 8) + (i32.const 56))) + (local.get 2)) + (i32.store + (local.tee 2 + (i32.add + (local.get 8) + (i32.const 40))) + (local.get 6)) + (i32.store offset=60 + (local.get 8) + (local.get 3)) + (i64.store + (local.tee 6 + (i32.add + (local.tee 3 + (i32.add + (local.get 8) + (i32.const 16))) + (i32.const 8))) + (i64.load + (local.get 9))) + (i32.store offset=44 + (local.get 8) + (local.get 7)) + (i64.store + (local.tee 7 + (i32.add + (local.get 8) + (i32.const 8))) + (i64.load + (local.get 2))) + (i64.store offset=48 + (local.get 8) + (local.tee 10 + (i64.or + (i64.extend_i32_u + (local.get 0)) + (i64.shl + (i64.extend_i32_u + (local.get 1)) + (i64.const 32))))) + (i64.store offset=32 + (local.get 8) + (local.tee 11 + (i64.or + (i64.extend_i32_u + (local.get 4)) + (i64.shl + (i64.extend_i32_u + (local.get 5)) + (i64.const 32))))) + (i64.store offset=16 + (local.get 8) + (local.get 10)) + (i64.store + (local.get 8) + (local.get 11)) + (i64.store align=4 + (local.tee 0 + (i32.sub + (local.get 8) + (i32.const -64))) + (i64.load align=4 + (local.get 3))) + (i64.store offset=16 align=4 + (local.get 0) + (i64.load align=4 + (local.get 8))) + (i64.store align=4 + (local.tee 1 + (i32.add + (local.get 0) + (i32.const 8))) + (i64.load align=4 + (local.get 6))) + (i64.store align=4 + (i32.add + (local.get 0) + (i32.const 24)) + (i64.load align=4 + (local.get 7))) + (i64.store + (i32.const 1064) + (i64.load align=4 + (local.get 1))) + (i64.store + (i32.const 1056) + (i64.load offset=64 align=4 + (local.get 8))) + (i64.store + (i32.const 1072) + (i64.load offset=80 align=4 + (local.get 8))) + (i64.store + (i32.const 1080) + (i64.load align=4 + (i32.add + (local.get 8) + (i32.const 88)))) + (global.set 0 + (i32.add + (local.get 8) + (i32.const 96))) + (i32.const 1056) + ) + (data (;0;) (i32.const 1024) "\ff\ff\ff\ff\00\00\02") + (@producers + (processed-by "wit-component" "0.243.0") + ) + ) + (core instance $main (;0;) (instantiate $main)) + (alias core export $main "memory" (core memory $memory (;0;))) + (type (;0;) (list u32 2)) + (type (;1;) (list 0 2)) + (type (;2;) (list s32 2)) + (type (;3;) (list 2 2)) + (type (;4;) (tuple 1 3)) + (type (;5;) (func (param "a" 1) (param "b" 3) (result 4))) + (alias core export $main "test:fixed-size-lists/to-test#nested-roundtrip" (core func $test:fixed-size-lists/to-test#nested-roundtrip (;0;))) + (func $nested-roundtrip (;0;) (type 5) (canon lift (core func $test:fixed-size-lists/to-test#nested-roundtrip) (memory $memory))) + (component $test:fixed-size-lists/to-test-shim-component (;0;) + (type (;0;) (list u32 2)) + (type (;1;) (list 0 2)) + (type (;2;) (list s32 2)) + (type (;3;) (list 2 2)) + (type (;4;) (tuple 1 3)) + (type (;5;) (func (param "a" 1) (param "b" 3) (result 4))) + (import "import-func-nested-roundtrip" (func (;0;) (type 5))) + (type (;6;) (list u32 2)) + (type (;7;) (list 6 2)) + (type (;8;) (list s32 2)) + (type (;9;) (list 8 2)) + (type (;10;) (tuple 7 9)) + (type (;11;) (func (param "a" 7) (param "b" 9) (result 10))) + (export (;1;) "nested-roundtrip" (func 0) (func (type 11))) + ) + (instance $test:fixed-size-lists/to-test-shim-instance (;0;) (instantiate $test:fixed-size-lists/to-test-shim-component + (with "import-func-nested-roundtrip" (func $nested-roundtrip)) + ) + ) + (export $test:fixed-size-lists/to-test (;1;) "test:fixed-size-lists/to-test" (instance $test:fixed-size-lists/to-test-shim-instance)) + (@producers + (processed-by "wit-component" "0.243.0") + ) + ) + (instance (;0;) (instantiate 1)) + (alias export 0 "test:fixed-size-lists/to-test" (instance (;1;))) + (instance (;2;) (instantiate 0 + (with "test:fixed-size-lists/to-test" (instance 1)) + ) + ) + (alias export 2 "run" (func (;0;))) + (export (;1;) "run" (func 0)) +) + +;; call run, it will return the number of mismatches in the test +(assert_return (invoke "run") + (u32.const 0))