diff --git a/Cargo.toml b/Cargo.toml index f1ce058..3e4a10e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,13 +16,13 @@ include = [ ] [dependencies] -wasm-encoder = { version = "0.217.0", features = ["wasmparser"]} -wasmparser = "0.217.0" +wasm-encoder = { version = "0.219.1", features = ["wasmparser"]} +wasmparser = "0.219.1" tempfile = "3.10.1" serde_json = "1.0.121" log = "0.4.22" gimli = "0.31.0" [dev-dependencies] -wasmprinter = "0.217.0" -wat = "1.214.0" +wasmprinter = "0.219.1" +wat = "1.219.1" diff --git a/src/ir/component.rs b/src/ir/component.rs index 94f7bb2..0fc963d 100644 --- a/src/ir/component.rs +++ b/src/ir/component.rs @@ -527,9 +527,11 @@ impl<'a> Component<'a> { let mut type_section = wasm_encoder::CoreTypeSection::new(); for cty_idx in last_processed_core_ty..last_processed_core_ty + num { match &self.core_types[cty_idx as usize] { - CoreType::Sub(subtype) => { - let enc = type_section.ty(); - encode_core_type_subtype(enc, subtype, &mut reencode); + CoreType::Rec(recgroup) => { + for subtype in recgroup.types() { + let enc = type_section.ty().core(); + encode_core_type_subtype(enc, subtype, &mut reencode); + } } CoreType::Module(module) => { let enc = type_section.ty(); @@ -612,9 +614,15 @@ impl<'a> Component<'a> { for c in comp.iter() { match c { ComponentTypeDeclaration::CoreType(core) => match core { - CoreType::Sub(sub) => { - let enc = new_comp.core_type(); - encode_core_type_subtype(enc, sub, &mut reencode); + CoreType::Rec(recgroup) => { + for sub in recgroup.types() { + let enc = new_comp.core_type().core(); + encode_core_type_subtype( + enc, + sub, + &mut reencode, + ); + } } CoreType::Module(module) => { let enc = new_comp.core_type(); @@ -804,6 +812,12 @@ impl<'a> Component<'a> { CanonicalFunction::ResourceRep { resource } => { canon_sec.resource_rep(*resource); } + CanonicalFunction::ThreadSpawn { func_ty_index } => { + canon_sec.thread_spawn(*func_ty_index); + } + CanonicalFunction::ThreadHwConcurrency => { + canon_sec.thread_hw_concurrency(); + } } last_processed_canon += 1; } diff --git a/src/ir/function.rs b/src/ir/function.rs index ad8dd0f..1360511 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -58,7 +58,7 @@ impl<'a> FunctionBuilder<'a> { let imp = module.imports.get(import_id); if let TypeRef::Func(imp_ty_id) = imp.ty { if let Some(ty) = module.types.get(TypeID(imp_ty_id)) { - if *ty.params == self.params && *ty.results == self.results { + if *ty.params() == self.params && *ty.results() == self.results { let mut local_func = LocalFunction::new( TypeID(imp_ty_id), FunctionID(*import_id), diff --git a/src/ir/helpers.rs b/src/ir/helpers.rs index d136506..fd45afe 100644 --- a/src/ir/helpers.rs +++ b/src/ir/helpers.rs @@ -29,15 +29,18 @@ pub fn print_subtype(ty: &SubType) { CompositeInnerType::Array(_) => eprintln!("SubType Array"), CompositeInnerType::Func(_) => eprintln!("SubType Func"), CompositeInnerType::Struct(_) => eprintln!("SubType Struct"), + CompositeInnerType::Cont(_) => eprintln!("SubType Cont"), } } pub fn print_module_ty_declaration(ty: &ModuleTypeDeclaration) { eprint!("Module: "); match ty { - ModuleTypeDeclaration::Type(subtype) => { - eprint!("SubType: "); - print_subtype(subtype) + ModuleTypeDeclaration::Type(recgroup) => { + for subtype in recgroup.types() { + eprint!("SubType: "); + print_subtype(subtype) + } } ModuleTypeDeclaration::Export { name: _name, @@ -55,7 +58,11 @@ pub fn print_module_ty_declaration(ty: &ModuleTypeDeclaration) { pub fn print_core_type(ty: &CoreType) { eprint!("CoreType: "); match ty { - CoreType::Sub(subtype) => print_subtype(subtype), + CoreType::Rec(recgroup) => { + for subtype in recgroup.types() { + print_subtype(subtype); + } + } CoreType::Module(module) => { for m in module.iter() { print_module_ty_declaration(m); diff --git a/src/ir/id.rs b/src/ir/id.rs index 584daa8..40d1b33 100644 --- a/src/ir/id.rs +++ b/src/ir/id.rs @@ -184,3 +184,31 @@ impl std::ops::DerefMut for MemoryID { &mut self.0 } } + +/// Field ID in a Struct or Array +pub struct FieldID(pub u32); +impl std::ops::Deref for FieldID { + type Target = u32; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl std::ops::DerefMut for FieldID { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// ID of an element in the Elements Section +pub struct ElementID(pub u32); +impl std::ops::Deref for ElementID { + type Target = u32; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl std::ops::DerefMut for ElementID { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/src/ir/module/mod.rs b/src/ir/module/mod.rs index eb41fe6..7bc4a09 100644 --- a/src/ir/module/mod.rs +++ b/src/ir/module/mod.rs @@ -1,6 +1,6 @@ //! Intermediate Representation of a wasm module. -use super::types::{DataType, Instruction, InstrumentationMode}; +use super::types::{DataType, InitExpr, Instruction, InstrumentationMode}; use crate::error::Error; use crate::ir::function::FunctionModifier; use crate::ir::id::{DataSegmentID, FunctionID, GlobalID, ImportsID, LocalID, MemoryID, TypeID}; @@ -13,7 +13,7 @@ use crate::ir::module::module_globals::{ }; use crate::ir::module::module_imports::{Import, ModuleImports}; use crate::ir::module::module_tables::ModuleTables; -use crate::ir::module::module_types::{FuncType, ModuleTypes}; +use crate::ir::module::module_types::{ModuleTypes, Types}; use crate::ir::types::InstrumentationMode::{BlockAlt, BlockEntry, BlockExit, SemanticAfter}; use crate::ir::types::{ BlockType, Body, CustomSections, DataSegment, DataSegmentKind, ElementItems, ElementKind, @@ -24,12 +24,15 @@ use crate::ir::wrappers::{ update_fn_instr, update_global_instr, }; use crate::opcode::{Inject, Instrumenter}; -use crate::{InitExpr, Location, Opcode}; +use crate::{Location, Opcode}; use log::{error, warn}; +use std::borrow::Cow; use std::collections::HashMap; use std::vec::IntoIter; use wasm_encoder::reencode::{Reencode, RoundtripReencoder}; -use wasmparser::{ExternalKind, GlobalType, MemoryType, Operator, Parser, Payload, TypeRef}; +use wasmparser::{ + CompositeInnerType, ExternalKind, GlobalType, MemoryType, Operator, Parser, Payload, TypeRef, +}; pub mod module_exports; pub mod module_functions; @@ -118,7 +121,7 @@ impl<'a> Module<'a> { parser: Parser, ) -> Result { let mut imports: ModuleImports = ModuleImports::default(); - let mut types: Vec = vec![]; + let mut types: Vec = vec![]; let mut data = vec![]; let mut tables = vec![]; let mut memories = vec![]; @@ -144,6 +147,7 @@ impl<'a> Module<'a> { let mut data_names = wasm_encoder::NameMap::new(); let mut field_names = wasm_encoder::IndirectNameMap::new(); let mut tag_names = wasm_encoder::NameMap::new(); + let mut recgroup_map = HashMap::new(); for payload in parser.parse_all(wasm) { let payload = payload?; @@ -158,22 +162,88 @@ impl<'a> Module<'a> { imports = ModuleImports::new(temp); } Payload::TypeSection(type_section_reader) => { - for ty in type_section_reader.into_iter_err_on_gc_types() { - let fun_ty = ty?; - let params = fun_ty - .params() - .iter() - .map(|x| DataType::from(*x)) - .collect::>() - .into_boxed_slice(); - let results = fun_ty - .results() - .iter() - .map(|x| DataType::from(*x)) - .collect::>() - .into_boxed_slice(); - - types.push(FuncType::new(params, results)); + let mut ty_idx: u32 = 0; + for (id, ty) in type_section_reader.into_iter().enumerate() { + let rec_group = ty.clone()?.is_explicit_rec_group(); + for subtype in ty?.types() { + match subtype.composite_type.inner.clone() { + CompositeInnerType::Func(fty) => { + let fun_ty = fty; + let params = fun_ty + .params() + .iter() + .map(|x| DataType::from(*x)) + .collect::>() + .into_boxed_slice(); + let results = fun_ty + .results() + .iter() + .map(|x| DataType::from(*x)) + .collect::>() + .into_boxed_slice(); + let final_ty = Types::FuncType { + params, + results, + super_type: subtype.supertype_idx, + is_final: subtype.is_final, + shared: subtype.composite_type.shared, + }; + types.push(final_ty.clone()); + + if rec_group { + recgroup_map.insert(ty_idx, id as u32); + } + } + CompositeInnerType::Array(aty) => { + let array_ty = Types::ArrayType { + mutable: aty.0.mutable, + fields: aty.0.element_type, + super_type: subtype.supertype_idx, + is_final: subtype.is_final, + shared: subtype.composite_type.shared, + }; + types.push(array_ty.clone()); + + if rec_group { + recgroup_map.insert(ty_idx, id as u32); + } + } + CompositeInnerType::Struct(sty) => { + let struct_ty = Types::StructType { + mutable: sty + .fields + .iter() + .map(|field| field.mutable) + .collect::>(), + fields: sty + .fields + .iter() + .map(|field| field.element_type) + .collect::>(), + super_type: subtype.supertype_idx, + is_final: subtype.is_final, + shared: subtype.composite_type.shared, + }; + types.push(struct_ty.clone()); + if rec_group { + recgroup_map.insert(ty_idx, id as u32); + } + } + CompositeInnerType::Cont(cty) => { + let cont_ty = Types::ContType { + packed_index: cty.0, + super_type: subtype.supertype_idx, + is_final: subtype.is_final, + shared: subtype.composite_type.shared, + }; + types.push(cont_ty.clone()); + if rec_group { + recgroup_map.insert(ty_idx, id as u32); + } + } + } + ty_idx += 1; + } } } Payload::DataSection(data_section_reader) => { @@ -408,6 +478,7 @@ impl<'a> Module<'a> { | Payload::ComponentImportSection(_) | Payload::ComponentExportSection(_) | Payload::End(_) => {} + _ => todo!(), } } if code_section_count != code_sections.len() || code_section_count != functions.len() { @@ -449,7 +520,7 @@ impl<'a> Module<'a> { functions[index], FunctionID(imports.num_funcs + index as u32), (*code_sec).clone(), - types[*functions[index] as usize].params.len(), + types[*functions[index] as usize].params().len(), )), (*code_sec).clone().name, )); @@ -460,7 +531,7 @@ impl<'a> Module<'a> { let num_tables = tables.len() as u32; let module_globals = ModuleGlobals::new(&imports, globals); Ok(Module { - types: ModuleTypes::new(types), + types: ModuleTypes::new(types, recgroup_map), imports, functions: Functions::new(final_funcs), tables: ModuleTables::new(tables), @@ -878,6 +949,92 @@ impl<'a> Module<'a> { id_mapping } + fn encode_type(&self, ty: &Types, reencode: &mut RoundtripReencoder) -> wasm_encoder::SubType { + match ty { + Types::FuncType { + params, + results, + super_type, + is_final, + shared, + } => { + let params = params + .iter() + .map(wasm_encoder::ValType::from) + .collect::>(); + let results = results + .iter() + .map(wasm_encoder::ValType::from) + .collect::>(); + let fty = wasm_encoder::FuncType::new(params, results); + wasm_encoder::SubType { + is_final: *is_final, + supertype_idx: match super_type { + None => None, + Some(idx) => idx.as_module_index(), + }, + composite_type: wasm_encoder::CompositeType { + inner: wasm_encoder::CompositeInnerType::Func(fty), + shared: *shared, + }, + } + } + Types::ArrayType { + fields, + mutable, + super_type, + is_final, + shared, + } => wasm_encoder::SubType { + is_final: *is_final, + supertype_idx: match super_type { + None => None, + Some(idx) => idx.as_module_index(), + }, + composite_type: wasm_encoder::CompositeType { + inner: wasm_encoder::CompositeInnerType::Array(wasm_encoder::ArrayType( + wasm_encoder::FieldType { + element_type: reencode.storage_type(*fields).unwrap(), + mutable: *mutable, + }, + )), + shared: *shared, + }, + }, + Types::StructType { + fields, + mutable, + super_type, + is_final, + shared, + } => { + let mut encoded_fields: Vec = vec![]; + for (idx, sty) in fields.iter().enumerate() { + encoded_fields.push(wasm_encoder::FieldType { + element_type: reencode.storage_type(*sty).unwrap(), + mutable: mutable[idx], + }); + } + wasm_encoder::SubType { + is_final: *is_final, + supertype_idx: match super_type { + None => None, + Some(idx) => idx.as_module_index(), + }, + composite_type: wasm_encoder::CompositeType { + inner: wasm_encoder::CompositeInnerType::Struct(wasm_encoder::StructType { + fields: Box::from(encoded_fields), + }), + shared: *shared, + }, + } + } + Types::ContType { .. } => { + todo!() + } + } + } + /// Encodes an Orca Module to a wasm_encoder Module. /// This requires a mutable reference to self due to the special instrumentation resolution step. pub(crate) fn encode_internal(&mut self) -> wasm_encoder::Module { @@ -906,20 +1063,35 @@ impl<'a> Module<'a> { if !self.types.is_empty() { let mut types = wasm_encoder::TypeSection::new(); - - for ty in self.types.iter() { - let params = ty - .params - .iter() - .map(wasm_encoder::ValType::from) - .collect::>(); - let results = ty - .results - .iter() - .map(wasm_encoder::ValType::from) - .collect::>(); - - types.function(params, results); + let mut last_rg = None; + let mut rg_types = vec![]; + for (idx, ty) in self.types.iter().enumerate() { + let curr_rg = self.types.recgroup_map.get(&(idx as u32)); + // If current one is not the same as last one and it is not the first rg, encode it + // If it is a new one + if curr_rg != last_rg { + // If the previous one was an explicit rec group + if let Some(_) = last_rg { + // Encode the last one as a recgroup + types.ty().rec(rg_types.clone()); + // Reset the vector + rg_types.clear(); + } + // If it was not, then it was already encoded + } + match curr_rg { + // If it is part of an explicit rec group + Some(_) => { + rg_types.push(self.encode_type(ty, &mut reencode)); + // first_rg = false; + } + None => types.ty().subtype(&self.encode_type(ty, &mut reencode)), + } + last_rg = curr_rg; + } + // If the last rg was a none, it was encoded in the binary, if it was an explicit rec group, was not encoded + if let Some(_) = last_rg { + types.ty().rec(rg_types.clone()); } module.section(&types); } @@ -1062,7 +1234,7 @@ impl<'a> Module<'a> { .iter() .map(|f| *func_mapping.get(f).unwrap()) .collect(); - wasm_encoder::Elements::Functions(element_items.as_slice()) + wasm_encoder::Elements::Functions(Cow::from(element_items.as_slice())) } ElementItems::ConstExprs { ty, exprs } => { temp_const_exprs.reserve(exprs.len()); @@ -1078,7 +1250,7 @@ impl<'a> Module<'a> { nullable: ty.is_nullable(), heap_type: reencode.heap_type(ty.heap_type()).unwrap(), }, - &temp_const_exprs, + Cow::from(&temp_const_exprs), ) } }; @@ -1371,7 +1543,7 @@ impl<'a> Module<'a> { results: &[DataType], body: Body<'a>, ) -> FunctionID { - let ty = self.types.add(params, results); + let ty = self.types.add_func_type(params, results); let local_func = LocalFunction::new( ty, FunctionID(0), // will be fixed diff --git a/src/ir/module/module_globals.rs b/src/ir/module/module_globals.rs index 078971f..b4bde82 100644 --- a/src/ir/module/module_globals.rs +++ b/src/ir/module/module_globals.rs @@ -4,7 +4,7 @@ use crate::error::Error; use crate::ir::id::{GlobalID, ImportsID}; use crate::ir::module::module_imports::ModuleImports; use crate::ir::module::{GetID, Iter, LocalOrImport, ReIndexable}; -use crate::InitExpr; +use crate::ir::types::InitExpr; use std::vec::IntoIter; use wasmparser::{GlobalType, TypeRef}; diff --git a/src/ir/module/module_types.rs b/src/ir/module/module_types.rs index ec47c52..3acb79c 100644 --- a/src/ir/module/module_types.rs +++ b/src/ir/module/module_types.rs @@ -4,38 +4,83 @@ use crate::ir::id::TypeID; use crate::DataType; use std::collections::HashMap; use std::hash::Hash; +use wasmparser::{PackedIndex, StorageType, UnpackedIndex}; + +// Orca's representation of function types, shortened from [Walrus' Representation]. +// +// [Walrus' Representation]: https://docs.rs/walrus/latest/walrus/struct.Type.html -/// Orca's representation of function types, shortened from [Walrus' Representation]. -/// -/// [Walrus' Representation]: https://docs.rs/walrus/latest/walrus/struct.Type.html #[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct FuncType { - pub params: Box<[DataType]>, - pub results: Box<[DataType]>, +pub enum Types { + FuncType { + params: Box<[DataType]>, + results: Box<[DataType]>, + super_type: Option, + is_final: bool, + shared: bool, + }, + ArrayType { + fields: StorageType, + mutable: bool, + super_type: Option, + is_final: bool, + shared: bool, + }, + StructType { + fields: Vec, + mutable: Vec, + super_type: Option, + is_final: bool, + shared: bool, + }, + ContType { + packed_index: PackedIndex, + super_type: Option, + is_final: bool, + shared: bool, + }, } -impl FuncType { - /// Create a new Function Type - pub fn new(params: Box<[DataType]>, results: Box<[DataType]>) -> Self { - Self { params, results } + +impl Types { + /// Return the params of a Function Type + pub fn params(&self) -> Vec { + match &self { + Types::FuncType { params, .. } => params.to_vec(), + _ => panic!("Not a function!"), + } + } + + /// Return the params of a Function Type + pub fn results(&self) -> Vec { + match &self { + Types::FuncType { results, .. } => results.to_vec(), + _ => panic!("Not a function!"), + } } } /// The Module Types Section #[derive(Clone, Debug, Default)] pub struct ModuleTypes { - pub types: Vec, + pub types: Vec, /// This enables us to quickly do a lookup to determine if a type has already been added - pub types_map: HashMap, + pub types_map: HashMap, + // Mapping between recursive group and TypeID + pub(crate) recgroup_map: HashMap, } impl ModuleTypes { /// Create a new Module Types section - pub fn new(types: Vec) -> Self { + pub fn new(types: Vec, recgroup_map: HashMap) -> Self { let mut types_map = HashMap::default(); for (id, ty) in types.iter().enumerate() { types_map.insert(ty.clone(), TypeID(id as u32)); } - ModuleTypes { types, types_map } + ModuleTypes { + types, + types_map, + recgroup_map, + } } /// Check if there are any types in this module @@ -43,13 +88,16 @@ impl ModuleTypes { self.types.is_empty() } - /// Add a new type to the module, returns the index of the new type. - pub fn add(&mut self, param: &[DataType], ret: &[DataType]) -> TypeID { + /// Add a new type to the module, returns the index of the new type. By default encodes the supertype as `None`, shared as `true`, and `is_final` as false for now. + pub fn add_func_type(&mut self, param: &[DataType], ret: &[DataType]) -> TypeID { let index = self.types.len(); - let ty = FuncType::new( - param.to_vec().into_boxed_slice(), - ret.to_vec().into_boxed_slice(), - ); + let ty = Types::FuncType { + params: param.to_vec().into_boxed_slice(), + results: ret.to_vec().into_boxed_slice(), + super_type: None, + is_final: true, + shared: false, + }; if !self.types_map.contains_key(&ty) { self.types.push(ty.clone()); @@ -66,12 +114,103 @@ impl ModuleTypes { } /// Create an iterable over the Type Section - pub fn iter(&self) -> std::slice::Iter<'_, FuncType> { + pub fn iter(&self) -> std::slice::Iter<'_, Types> { self.types.iter() } /// Get type from index of the type section - pub fn get(&self, index: TypeID) -> Option<&FuncType> { + pub fn get(&self, index: TypeID) -> Option<&Types> { self.types.get(*index as usize) } } + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum HeapType { + Abstract { shared: bool, ty: AbstractHeapType }, + // TODO: See to replace UnpackedIndex with `orca` specific implementation + Concrete(UnpackedIndex), +} + +impl From for HeapType { + fn from(value: wasmparser::HeapType) -> Self { + match value { + wasmparser::HeapType::Abstract { shared, ty } => HeapType::Abstract { + shared, + ty: AbstractHeapType::from(ty), + }, + wasmparser::HeapType::Concrete(idx) => HeapType::Concrete(idx), + } + } +} + +impl From for wasmparser::HeapType { + fn from(value: HeapType) -> Self { + match value { + HeapType::Abstract { shared, ty } => wasmparser::HeapType::Abstract { + shared, + ty: wasmparser::AbstractHeapType::from(ty), + }, + HeapType::Concrete(idx) => wasmparser::HeapType::Concrete(idx), + } + } +} + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum AbstractHeapType { + Func, + Extern, + Any, + None, + NoExtern, + NoFunc, + Eq, + Struct, + Array, + I31, + Exn, + NoExn, + Cont, + NoCont, +} + +impl From for AbstractHeapType { + fn from(value: wasmparser::AbstractHeapType) -> Self { + match value { + wasmparser::AbstractHeapType::Func => AbstractHeapType::Func, + wasmparser::AbstractHeapType::Extern => AbstractHeapType::Extern, + wasmparser::AbstractHeapType::Any => AbstractHeapType::Any, + wasmparser::AbstractHeapType::None => AbstractHeapType::None, + wasmparser::AbstractHeapType::NoExtern => AbstractHeapType::NoExtern, + wasmparser::AbstractHeapType::NoFunc => AbstractHeapType::NoFunc, + wasmparser::AbstractHeapType::Eq => AbstractHeapType::Eq, + wasmparser::AbstractHeapType::Struct => AbstractHeapType::Struct, + wasmparser::AbstractHeapType::Array => AbstractHeapType::Array, + wasmparser::AbstractHeapType::I31 => AbstractHeapType::I31, + wasmparser::AbstractHeapType::Exn => AbstractHeapType::Exn, + wasmparser::AbstractHeapType::NoExn => AbstractHeapType::NoExn, + wasmparser::AbstractHeapType::Cont => AbstractHeapType::Cont, + wasmparser::AbstractHeapType::NoCont => AbstractHeapType::NoCont, + } + } +} + +impl From for wasmparser::AbstractHeapType { + fn from(value: AbstractHeapType) -> Self { + match value { + AbstractHeapType::Func => wasmparser::AbstractHeapType::Func, + AbstractHeapType::Extern => wasmparser::AbstractHeapType::Extern, + AbstractHeapType::Any => wasmparser::AbstractHeapType::Any, + AbstractHeapType::None => wasmparser::AbstractHeapType::None, + AbstractHeapType::NoExtern => wasmparser::AbstractHeapType::NoExtern, + AbstractHeapType::NoFunc => wasmparser::AbstractHeapType::NoFunc, + AbstractHeapType::Eq => wasmparser::AbstractHeapType::Eq, + AbstractHeapType::Struct => wasmparser::AbstractHeapType::Struct, + AbstractHeapType::Array => wasmparser::AbstractHeapType::Array, + AbstractHeapType::I31 => wasmparser::AbstractHeapType::I31, + AbstractHeapType::Exn => wasmparser::AbstractHeapType::Exn, + AbstractHeapType::NoExn => wasmparser::AbstractHeapType::NoExn, + AbstractHeapType::Cont => wasmparser::AbstractHeapType::Cont, + AbstractHeapType::NoCont => wasmparser::AbstractHeapType::NoCont, + } + } +} diff --git a/src/ir/module/test.rs b/src/ir/module/test.rs index 8fa0e93..b8a6242 100644 --- a/src/ir/module/test.rs +++ b/src/ir/module/test.rs @@ -4,7 +4,8 @@ use crate::ir::function::FunctionBuilder; use crate::ir::id::{ExportsID, FunctionID, GlobalID, ImportsID, TypeID}; -use crate::{DataType, InitExpr, Module, Opcode}; +use crate::ir::types::InitExpr; +use crate::{DataType, Instructions, Module, Opcode}; use log::debug; use std::collections::HashMap; use std::path::PathBuf; @@ -417,7 +418,7 @@ fn test_create_and_add_global() { // add a local global let gid = module.add_global( - InitExpr::Value(crate::ir::types::Value::I32(0)), + InitExpr::new(vec![Instructions::Value(crate::ir::types::Value::I32(0))]), DataType::I32, true, false, diff --git a/src/ir/types.rs b/src/ir/types.rs index f0419ba..eced6ce 100644 --- a/src/ir/types.rs +++ b/src/ir/types.rs @@ -8,7 +8,7 @@ use std::fmt::{self}; use std::mem::discriminant; use std::slice::Iter; use wasm_encoder::reencode::Reencode; -use wasm_encoder::AbstractHeapType; +use wasm_encoder::{AbstractHeapType, Encode}; use wasmparser::{ConstExpr, Operator, RefType, ValType}; type Result = std::result::Result; @@ -24,20 +24,29 @@ pub enum DataType { F64, V128, FuncRef, + FuncRefNull, ExternRef, + ExternRefNull, Any, + AnyNull, None, NoExtern, NoFunc, Eq, + EqNull, Struct, + StructNull, Array, + ArrayNull, I31, + I31Null, Exn, NoExn, - Module(ModuleID), + Module { ty_id: u32, nullable: bool }, RecGroup(u32), CoreTypeId(u32), // TODO: Look at this + Cont, + NoCont, } impl fmt::Display for DataType { @@ -60,9 +69,20 @@ impl fmt::Display for DataType { DataType::I31 => write!(f, "DataType: I31"), DataType::Exn => write!(f, "DataType: Exn"), DataType::NoExn => write!(f, "DataType: NoExn"), - DataType::Module(idx) => write!(f, "DataType: Module {:?}", idx), + DataType::Module { ty_id, nullable } => { + write!(f, "DataType: Module {:?} Nullable: {:?}", ty_id, nullable) + } DataType::RecGroup(idx) => write!(f, "DataType: RecGroup {:?}", idx), DataType::CoreTypeId(idx) => write!(f, "DataType: CoreTypeId {:?}", idx), + DataType::Cont => write!(f, "DataType: Cont"), + DataType::NoCont => write!(f, "DataType: NoCont"), + DataType::FuncRefNull => write!(f, "DataType: FuncRef Null"), + DataType::ExternRefNull => write!(f, "DataType: ExternRef Null"), + DataType::AnyNull => write!(f, "DataType: Any Null"), + DataType::EqNull => write!(f, "DataType: Eq Null"), + DataType::StructNull => write!(f, "DataType: Struct Null"), + DataType::ArrayNull => write!(f, "DataType: Array Null"), + DataType::I31Null => write!(f, "DataType: I31 Null"), } } } @@ -77,21 +97,68 @@ impl From for DataType { ValType::V128 => DataType::V128, ValType::Ref(ref_type) => match ref_type.heap_type() { wasmparser::HeapType::Abstract { shared: _, ty } => match ty { - wasmparser::AbstractHeapType::Func => DataType::FuncRef, - wasmparser::AbstractHeapType::Extern => DataType::ExternRef, - wasmparser::AbstractHeapType::Any => DataType::Any, + wasmparser::AbstractHeapType::Func => { + if ref_type.is_nullable() { + DataType::FuncRefNull + } else { + DataType::FuncRef + } + } + wasmparser::AbstractHeapType::Extern => { + if ref_type.is_nullable() { + DataType::ExternRefNull + } else { + DataType::ExternRef + } + } + wasmparser::AbstractHeapType::Any => { + if ref_type.is_nullable() { + DataType::AnyNull + } else { + DataType::Any + } + } wasmparser::AbstractHeapType::None => DataType::None, wasmparser::AbstractHeapType::NoExtern => DataType::NoExtern, wasmparser::AbstractHeapType::NoFunc => DataType::NoFunc, - wasmparser::AbstractHeapType::Eq => DataType::Eq, - wasmparser::AbstractHeapType::Struct => DataType::Struct, - wasmparser::AbstractHeapType::Array => DataType::Array, - wasmparser::AbstractHeapType::I31 => DataType::I31, + wasmparser::AbstractHeapType::Eq => { + if ref_type.is_nullable() { + DataType::EqNull + } else { + DataType::Eq + } + } + wasmparser::AbstractHeapType::Struct => { + if ref_type.is_nullable() { + DataType::StructNull + } else { + DataType::Struct + } + } + wasmparser::AbstractHeapType::Array => { + if ref_type.is_nullable() { + DataType::ArrayNull + } else { + DataType::Array + } + } + wasmparser::AbstractHeapType::I31 => { + if ref_type.is_nullable() { + DataType::I31Null + } else { + DataType::I31 + } + } wasmparser::AbstractHeapType::Exn => DataType::Exn, wasmparser::AbstractHeapType::NoExn => DataType::NoExn, + wasmparser::AbstractHeapType::Cont => DataType::Cont, + wasmparser::AbstractHeapType::NoCont => DataType::NoCont, }, wasmparser::HeapType::Concrete(u) => match u { - wasmparser::UnpackedIndex::Module(idx) => DataType::Module(ModuleID(idx)), + wasmparser::UnpackedIndex::Module(idx) => DataType::Module { + ty_id: *ModuleID(idx), + nullable: ref_type.is_nullable(), + }, wasmparser::UnpackedIndex::RecGroup(idx) => DataType::RecGroup(idx), wasmparser::UnpackedIndex::Id(_id) => panic!("Not supported yet!"), }, @@ -111,8 +178,20 @@ impl From<&DataType> for wasm_encoder::ValType { DataType::F32 => wasm_encoder::ValType::F32, DataType::F64 => wasm_encoder::ValType::F64, DataType::V128 => wasm_encoder::ValType::V128, - DataType::FuncRef => wasm_encoder::ValType::FUNCREF, - DataType::ExternRef => wasm_encoder::ValType::EXTERNREF, + DataType::FuncRef => wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: false, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Func, + }, + }), + DataType::ExternRef => wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: false, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Extern, + }, + }), DataType::Any => wasm_encoder::ValType::Ref(wasm_encoder::RefType { nullable: false, heap_type: wasm_encoder::HeapType::Abstract { @@ -183,10 +262,12 @@ impl From<&DataType> for wasm_encoder::ValType { ty: AbstractHeapType::NoExn, }, }), - DataType::Module(idx) => wasm_encoder::ValType::Ref(wasm_encoder::RefType { - nullable: false, - heap_type: wasm_encoder::HeapType::Concrete(**idx), - }), + DataType::Module { ty_id, nullable } => { + wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: *nullable, + heap_type: wasm_encoder::HeapType::Concrete(*ty_id), + }) + } DataType::RecGroup(idx) => wasm_encoder::ValType::Ref(wasm_encoder::RefType { nullable: false, heap_type: wasm_encoder::HeapType::Concrete(*idx), @@ -195,6 +276,69 @@ impl From<&DataType> for wasm_encoder::ValType { nullable: false, heap_type: wasm_encoder::HeapType::Concrete(*idx), }), + DataType::Cont => wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: false, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Cont, + }, + }), + DataType::NoCont => wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: false, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::NoCont, + }, + }), + DataType::FuncRefNull => wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Func, + }, + }), + DataType::ExternRefNull => wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Extern, + }, + }), + DataType::AnyNull => wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Any, + }, + }), + DataType::EqNull => wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Eq, + }, + }), + DataType::StructNull => wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Struct, + }, + }), + DataType::ArrayNull => wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Array, + }, + }), + DataType::I31Null => wasm_encoder::ValType::Ref(wasm_encoder::RefType { + nullable: true, + heap_type: wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::I31, + }, + }), } } } @@ -309,10 +453,10 @@ impl From<&DataType> for ValType { ) .unwrap(), ), - DataType::Module(idx) => ValType::Ref( + DataType::Module { ty_id, nullable } => ValType::Ref( RefType::new( - false, - wasmparser::HeapType::Concrete(wasmparser::UnpackedIndex::Module(**idx)), + *nullable, + wasmparser::HeapType::Concrete(wasmparser::UnpackedIndex::Module(*ty_id)), ) .unwrap(), ), @@ -324,6 +468,96 @@ impl From<&DataType> for ValType { .unwrap(), ), DataType::CoreTypeId(_idx) => panic!("Not Supported Yet!"), + DataType::Cont => ValType::Ref( + RefType::new( + false, + wasmparser::HeapType::Abstract { + shared: false, + ty: wasmparser::AbstractHeapType::Cont, + }, + ) + .unwrap(), + ), + DataType::NoCont => ValType::Ref( + RefType::new( + false, + wasmparser::HeapType::Abstract { + shared: false, + ty: wasmparser::AbstractHeapType::NoCont, + }, + ) + .unwrap(), + ), + DataType::FuncRefNull => ValType::Ref( + RefType::new( + true, + wasmparser::HeapType::Abstract { + shared: false, + ty: wasmparser::AbstractHeapType::Func, + }, + ) + .unwrap(), + ), + DataType::ExternRefNull => ValType::Ref( + RefType::new( + true, + wasmparser::HeapType::Abstract { + shared: false, + ty: wasmparser::AbstractHeapType::Extern, + }, + ) + .unwrap(), + ), + DataType::AnyNull => ValType::Ref( + RefType::new( + true, + wasmparser::HeapType::Abstract { + shared: false, + ty: wasmparser::AbstractHeapType::Any, + }, + ) + .unwrap(), + ), + DataType::EqNull => ValType::Ref( + RefType::new( + true, + wasmparser::HeapType::Abstract { + shared: false, + ty: wasmparser::AbstractHeapType::Eq, + }, + ) + .unwrap(), + ), + DataType::StructNull => ValType::Ref( + RefType::new( + true, + wasmparser::HeapType::Abstract { + shared: false, + ty: wasmparser::AbstractHeapType::Struct, + }, + ) + .unwrap(), + ), + DataType::ArrayNull => ValType::Ref( + RefType::new( + true, + wasmparser::HeapType::Abstract { + shared: false, + ty: wasmparser::AbstractHeapType::Array, + }, + ) + .unwrap(), + ), + DataType::I31Null => ValType::Ref( + RefType::new( + true, + wasmparser::HeapType::Abstract { + shared: false, + ty: wasmparser::AbstractHeapType::I31, + }, + ) + .unwrap(), + ), } } } @@ -892,10 +1126,16 @@ where } } -/// A constant which is produced in WebAssembly, typically used in global +/// A constant expression which is produced in WebAssembly, typically used in global /// initializers or element/data offsets. +#[derive(Debug, Clone)] +pub struct InitExpr { + exprs: Vec, +} + +/// Set of instructions that can be used in Initialisatin Expressions #[derive(Debug, Copy, Clone)] -pub enum InitExpr { +pub enum Instructions { /// An immediate constant value Value(Value), /// A constant value referenced by the global specified @@ -904,57 +1144,178 @@ pub enum InitExpr { RefNull(RefType), /// A function initializer RefFunc(FunctionID), + /// Struct Initializer + StructNew(TypeID), + /// Struct Default + StructNewDefault(TypeID), + /// Array Initializer + ArrayNew(TypeID), + /// Default Initialisation + ArrayNewDefault(TypeID), + /// Fixed Array + RefArrayFixed { + array_type_index: u32, + array_size: u32, + }, + /// Array from Data + RefArrayData { + array_type_index: u32, + array_data_index: u32, + }, + /// Array from Elem + RefArrayElem { + array_type_index: u32, + array_elem_index: u32, + }, + RefI31, } impl InitExpr { + /// Create a new initialisation expression given a vector of Instructions + pub fn new(instructions: Vec) -> Self { + InitExpr { + exprs: instructions, + } + } + pub(crate) fn eval(init: &ConstExpr) -> InitExpr { use wasmparser::Operator::*; let mut reader = init.get_operators_reader(); - let val = match reader.read().unwrap() { - I32Const { value } => InitExpr::Value(Value::I32(value)), - I64Const { value } => InitExpr::Value(Value::I64(value)), - F32Const { value } => InitExpr::Value(Value::F32(f32::from_bits(value.bits()))), - F64Const { value } => InitExpr::Value(Value::F64(f64::from_bits(value.bits()))), - V128Const { value } => InitExpr::Value(Value::V128(v128_to_u128(&value))), - GlobalGet { global_index } => InitExpr::Global(GlobalID(global_index)), - // Marking nullable as true as it's a null reference - RefNull { hty } => InitExpr::RefNull(RefType::new(true, hty).unwrap()), - RefFunc { function_index } => InitExpr::RefFunc(FunctionID(function_index)), - _ => panic!("invalid constant expression"), - }; - match reader.read().unwrap() { - End => {} - _ => panic!("invalid constant expression"), + let mut instrs = vec![]; + loop { + let val = match reader.read().unwrap() { + I32Const { value } => Instructions::Value(Value::I32(value)), + I64Const { value } => Instructions::Value(Value::I64(value)), + F32Const { value } => Instructions::Value(Value::F32(f32::from_bits(value.bits()))), + F64Const { value } => Instructions::Value(Value::F64(f64::from_bits(value.bits()))), + V128Const { value } => Instructions::Value(Value::V128(v128_to_u128(&value))), + GlobalGet { global_index } => Instructions::Global(GlobalID(global_index)), + // Marking nullable as true as it's a null reference + RefNull { hty } => Instructions::RefNull(RefType::new(true, hty).unwrap()), + RefFunc { function_index } => Instructions::RefFunc(FunctionID(function_index)), + StructNew { struct_type_index } => { + Instructions::StructNew(TypeID(struct_type_index)) + } + StructNewDefault { struct_type_index } => { + Instructions::StructNewDefault(TypeID(struct_type_index)) + } + ArrayNew { array_type_index } => Instructions::ArrayNew(TypeID(array_type_index)), + ArrayNewDefault { array_type_index } => { + Instructions::ArrayNewDefault(TypeID(array_type_index)) + } + ArrayNewFixed { + array_type_index, + array_size, + } => Instructions::RefArrayFixed { + array_type_index, + array_size, + }, + ArrayNewData { + array_type_index, + array_data_index, + } => Instructions::RefArrayData { + array_data_index, + array_type_index, + }, + ArrayNewElem { + array_type_index, + array_elem_index, + } => Instructions::RefArrayElem { + array_type_index, + array_elem_index, + }, + RefI31 => Instructions::RefI31, + End => break, + _ => panic!("Invalid constant expression"), + }; + instrs.push(val); } reader.ensure_end().unwrap(); - val + InitExpr { exprs: instrs } } - pub(crate) fn to_wasmencoder_type(self) -> wasm_encoder::ConstExpr { - match self { - InitExpr::Value(v) => match v { - Value::I32(v) => wasm_encoder::ConstExpr::i32_const(v), - Value::I64(v) => wasm_encoder::ConstExpr::i64_const(v), - Value::F32(v) => wasm_encoder::ConstExpr::f32_const(v), - Value::F64(v) => wasm_encoder::ConstExpr::f64_const(v), - Value::V128(v) => wasm_encoder::ConstExpr::v128_const(v as i128), - }, - InitExpr::Global(g) => wasm_encoder::ConstExpr::global_get(*g), - InitExpr::RefNull(ty) => wasm_encoder::ConstExpr::ref_null(if ty.is_func_ref() { - wasm_encoder::HeapType::Abstract { - shared: false, - ty: AbstractHeapType::Func, + pub(crate) fn to_wasmencoder_type(&self) -> wasm_encoder::ConstExpr { + let mut bytes = vec![]; + for instr in self.exprs.iter() { + match instr { + Instructions::Value(v) => match v { + Value::I32(v) => wasm_encoder::Instruction::I32Const(*v).encode(&mut bytes), + Value::I64(v) => wasm_encoder::Instruction::I64Const(*v).encode(&mut bytes), + Value::F32(v) => wasm_encoder::Instruction::F32Const(*v).encode(&mut bytes), + Value::F64(v) => wasm_encoder::Instruction::F64Const(*v).encode(&mut bytes), + Value::V128(v) => { + wasm_encoder::Instruction::V128Const(*v as i128).encode(&mut bytes) + } + }, + Instructions::Global(g) => { + wasm_encoder::Instruction::GlobalGet(**g).encode(&mut bytes) } - } else if ty.is_extern_ref() { - wasm_encoder::HeapType::Abstract { - shared: false, - ty: AbstractHeapType::Extern, + Instructions::RefNull(ty) => { + wasm_encoder::Instruction::RefNull(if ty.is_func_ref() { + wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Func, + } + } else if ty.is_extern_ref() { + wasm_encoder::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Extern, + } + } else { + unreachable!() + }) + .encode(&mut bytes) } - } else { - unreachable!() - }), - InitExpr::RefFunc(f) => wasm_encoder::ConstExpr::ref_func(*f), + Instructions::RefFunc(f) => { + wasm_encoder::Instruction::RefFunc(**f).encode(&mut bytes) + } + Instructions::StructNew(id) => { + wasm_encoder::Instruction::StructNew(**id).encode(&mut bytes); + } + Instructions::ArrayNew(id) => { + wasm_encoder::Instruction::ArrayNew(**id).encode(&mut bytes); + } + + Instructions::StructNewDefault(id) => { + wasm_encoder::Instruction::StructNewDefault(**id).encode(&mut bytes); + } + Instructions::ArrayNewDefault(id) => { + wasm_encoder::Instruction::ArrayNewDefault(**id).encode(&mut bytes); + } + Instructions::RefArrayFixed { + array_type_index, + array_size, + } => { + wasm_encoder::Instruction::ArrayNewFixed { + array_size: *array_size, + array_type_index: *array_type_index, + } + .encode(&mut bytes); + } + Instructions::RefArrayData { + array_type_index, + array_data_index, + } => { + wasm_encoder::Instruction::ArrayNewData { + array_data_index: *array_data_index, + array_type_index: *array_type_index, + } + .encode(&mut bytes); + } + Instructions::RefArrayElem { + array_type_index, + array_elem_index, + } => { + wasm_encoder::Instruction::ArrayNewElem { + array_elem_index: *array_elem_index, + array_type_index: *array_type_index, + } + .encode(&mut bytes); + } + Instructions::RefI31 => wasm_encoder::Instruction::RefI31.encode(&mut bytes), + } } + wasm_encoder::ConstExpr::raw(bytes) } } diff --git a/src/ir/wrappers.rs b/src/ir/wrappers.rs index 6188993..e97f656 100644 --- a/src/ir/wrappers.rs +++ b/src/ir/wrappers.rs @@ -3,7 +3,8 @@ use std::collections::HashMap; use wasm_encoder::reencode::{Reencode, ReencodeComponent}; use wasm_encoder::{ - Alias, ComponentFuncTypeEncoder, ComponentTypeEncoder, CoreTypeEncoder, InstanceType, + Alias, ComponentCoreTypeEncoder, ComponentFuncTypeEncoder, ComponentTypeEncoder, + CoreTypeEncoder, InstanceType, }; use wasmparser::{ ComponentAlias, ComponentFuncResult, ComponentType, ComponentTypeDeclaration, CoreType, @@ -14,15 +15,17 @@ use wasmparser::{ /// Convert ModuleTypeDeclaration to ModuleType pub fn convert_module_type_declaration( module: &[wasmparser::ModuleTypeDeclaration], - enc: CoreTypeEncoder, + enc: ComponentCoreTypeEncoder, reencode: &mut wasm_encoder::reencode::RoundtripReencoder, ) { let mut mty = wasm_encoder::ModuleType::new(); for m in module.iter() { match m { - wasmparser::ModuleTypeDeclaration::Type(sub) => { - let enc_mty = mty.ty(); - encode_core_type_subtype(enc_mty, sub, reencode); + wasmparser::ModuleTypeDeclaration::Type(recgroup) => { + for subtype in recgroup.types() { + let enc_mty = mty.ty(); + encode_core_type_subtype(enc_mty, subtype, reencode); + } } wasmparser::ModuleTypeDeclaration::Export { name, ty } => { mty.export(name, reencode.entity_type(*ty).unwrap()); @@ -56,9 +59,11 @@ pub fn convert_instance_type( for value in instance.iter() { match value { InstanceTypeDeclaration::CoreType(core_type) => match core_type { - CoreType::Sub(sub) => { - let enc = ity.core_type(); - encode_core_type_subtype(enc, sub, reencode); + CoreType::Rec(recgroup) => { + for sub in recgroup.types() { + let enc = ity.core_type().core(); + encode_core_type_subtype(enc, sub, reencode); + } } CoreType::Module(module) => { let enc = ity.core_type(); @@ -137,7 +142,6 @@ pub fn encode_core_type_subtype( subtype: &SubType, reencode: &mut wasm_encoder::reencode::RoundtripReencoder, ) { - // TODO: Struct and Arrays once added to wasm_encoder - still in GC Proposal match &subtype.composite_type.inner { wasmparser::CompositeInnerType::Func(func) => { enc.function( @@ -151,8 +155,23 @@ pub fn encode_core_type_subtype( .collect::>(), ); } - wasmparser::CompositeInnerType::Array(_) | wasmparser::CompositeInnerType::Struct(_) => { - panic!("Still in GC Proposal") + wasmparser::CompositeInnerType::Array(array_ty) => { + enc.array( + &reencode.storage_type(array_ty.0.element_type).unwrap(), + array_ty.0.mutable, + ); + } + wasmparser::CompositeInnerType::Struct(struct_ty) => { + enc.struct_( + struct_ty + .fields + .iter() + .map(|field_ty| reencode.field_type(*field_ty).unwrap()) + .collect::>(), + ); + } + wasmparser::CompositeInnerType::Cont(_) => { + todo!() } } } @@ -259,9 +278,11 @@ pub fn convert_component_type( for c in comp.iter() { match c { ComponentTypeDeclaration::CoreType(core) => match core { - CoreType::Sub(sub) => { - let enc = new_comp.core_type(); - encode_core_type_subtype(enc, sub, reencode); + CoreType::Rec(recgroup) => { + for sub in recgroup.types() { + let enc = new_comp.core_type().core(); + encode_core_type_subtype(enc, sub, reencode); + } } CoreType::Module(module) => { let enc = new_comp.core_type(); diff --git a/src/lib.rs b/src/lib.rs index 97c82ac..1b6d6f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,5 +19,5 @@ pub use crate::ir::module::Module; pub use crate::ir::types::DataSegment; pub use crate::ir::types::DataSegmentKind; pub use crate::ir::types::DataType; -pub use crate::ir::types::InitExpr; +pub use crate::ir::types::Instructions; pub use crate::ir::types::Location; diff --git a/src/opcode.rs b/src/opcode.rs index 5a9634c..0fe2a15 100644 --- a/src/opcode.rs +++ b/src/opcode.rs @@ -4,7 +4,8 @@ // note that the location of the injection is handled specific implementation // for iterators, we inject at the location the iterator is pointing at (curr_loc) // for FunctionBuilder, we inject at the end of the function -use crate::ir::id::{FunctionID, GlobalID, LocalID}; +use crate::ir::id::{DataSegmentID, ElementID, FieldID, FunctionID, GlobalID, LocalID, TypeID}; +use crate::ir::module::module_types::HeapType; use crate::ir::types::{BlockType, FuncInstrMode, InstrumentationMode}; use crate::Location; use wasmparser::MemArg; @@ -970,6 +971,256 @@ pub trait Opcode<'a>: Inject<'a> { self.inject(Operator::GlobalSet { global_index: *idx }); self } + + // GC Instructions + fn ref_null(&mut self, heap_type: HeapType) -> &mut Self { + self.inject(Operator::RefNull { + hty: wasmparser::HeapType::from(heap_type), + }); + self + } + + fn ref_is_null(&mut self) -> &mut Self { + self.inject(Operator::RefIsNull); + self + } + + fn ref_func(&mut self, function_index: u32) -> &mut Self { + self.inject(Operator::RefFunc { function_index }); + self + } + + fn ref_eq(&mut self) -> &mut Self { + self.inject(Operator::RefEq); + self + } + + fn ref_as_non_null(&mut self) -> &mut Self { + self.inject(Operator::RefAsNonNull); + self + } + + fn struct_new(&mut self, struct_type_index: TypeID) -> &mut Self { + self.inject(Operator::StructNew { + struct_type_index: *struct_type_index, + }); + self + } + + fn struct_new_default(&mut self, struct_type_index: TypeID) -> &mut Self { + self.inject(Operator::StructNewDefault { + struct_type_index: *struct_type_index, + }); + self + } + + fn struct_get(&mut self, struct_type_index: TypeID, field_index: FieldID) -> &mut Self { + self.inject(Operator::StructGet { + struct_type_index: *struct_type_index, + field_index: *field_index, + }); + self + } + + fn struct_get_s(&mut self, struct_type_index: TypeID, field_index: FieldID) -> &mut Self { + self.inject(Operator::StructGetS { + struct_type_index: *struct_type_index, + field_index: *field_index, + }); + self + } + + fn struct_get_u(&mut self, struct_type_index: TypeID, field_index: FieldID) -> &mut Self { + self.inject(Operator::StructGetU { + struct_type_index: *struct_type_index, + field_index: *field_index, + }); + self + } + + fn struct_set(&mut self, struct_type_index: TypeID, field_index: FieldID) -> &mut Self { + self.inject(Operator::StructSet { + struct_type_index: *struct_type_index, + field_index: *field_index, + }); + self + } + + fn array_new(&mut self, array_type_index: TypeID) -> &mut Self { + self.inject(Operator::ArrayNew { + array_type_index: *array_type_index, + }); + self + } + + fn array_new_default(&mut self, array_type_index: TypeID) -> &mut Self { + self.inject(Operator::ArrayNewDefault { + array_type_index: *array_type_index, + }); + self + } + + fn array_new_fixed(&mut self, array_type_index: TypeID, array_size: u32) -> &mut Self { + self.inject(Operator::ArrayNewFixed { + array_type_index: *array_type_index, + array_size, + }); + self + } + + // TODO: Check the arguments + fn array_new_data( + &mut self, + array_type_index: TypeID, + array_data_index: DataSegmentID, + ) -> &mut Self { + self.inject(Operator::ArrayNewData { + array_type_index: *array_type_index, + array_data_index: *array_data_index, + }); + self + } + + fn array_new_elem( + &mut self, + array_type_index: TypeID, + array_elem_index: ElementID, + ) -> &mut Self { + self.inject(Operator::ArrayNewElem { + array_type_index: *array_type_index, + array_elem_index: *array_elem_index, + }); + self + } + + fn array_get(&mut self, array_type_index: TypeID) -> &mut Self { + self.inject(Operator::ArrayGet { + array_type_index: *array_type_index, + }); + self + } + + fn array_get_s(&mut self, array_type_index: TypeID) -> &mut Self { + self.inject(Operator::ArrayGetS { + array_type_index: *array_type_index, + }); + self + } + + fn array_get_u(&mut self, array_type_index: TypeID) -> &mut Self { + self.inject(Operator::ArrayGetU { + array_type_index: *array_type_index, + }); + self + } + + fn array_set(&mut self, array_type_index: TypeID) -> &mut Self { + self.inject(Operator::ArraySet { + array_type_index: *array_type_index, + }); + self + } + + fn array_len(&mut self) -> &mut Self { + self.inject(Operator::ArrayLen); + self + } + + fn array_fill(&mut self, array_type_index: TypeID) -> &mut Self { + self.inject(Operator::ArrayFill { + array_type_index: *array_type_index, + }); + self + } + + fn array_copy( + &mut self, + array_type_index_dest: TypeID, + array_type_index_src: TypeID, + ) -> &mut Self { + self.inject(Operator::ArrayCopy { + array_type_index_dst: *array_type_index_dest, + array_type_index_src: *array_type_index_src, + }); + self + } + + fn array_init_data( + &mut self, + array_type_index: TypeID, + array_data_index: DataSegmentID, + ) -> &mut Self { + self.inject(Operator::ArrayInitData { + array_type_index: *array_type_index, + array_data_index: *array_data_index, + }); + self + } + + fn array_init_elem( + &mut self, + array_type_index: TypeID, + array_elem_index: ElementID, + ) -> &mut Self { + self.inject(Operator::ArrayInitElem { + array_type_index: *array_type_index, + array_elem_index: *array_elem_index, + }); + self + } + + fn ref_test(&mut self, heap_type: HeapType) -> &mut Self { + self.inject(Operator::RefTestNonNull { + hty: wasmparser::HeapType::from(heap_type), + }); + self + } + + fn ref_test_null(&mut self, heap_type: HeapType) -> &mut Self { + self.inject(Operator::RefTestNullable { + hty: wasmparser::HeapType::from(heap_type), + }); + self + } + + fn ref_cast(&mut self, heap_type: HeapType) -> &mut Self { + self.inject(Operator::RefCastNonNull { + hty: wasmparser::HeapType::from(heap_type), + }); + self + } + + fn ref_cast_null(&mut self, heap_type: HeapType) -> &mut Self { + self.inject(Operator::RefCastNullable { + hty: wasmparser::HeapType::from(heap_type), + }); + self + } + + fn any_convert_extern(&mut self) -> &mut Self { + self.inject(Operator::AnyConvertExtern); + self + } + + fn extern_convert_any(&mut self) -> &mut Self { + self.inject(Operator::ExternConvertAny); + self + } + + fn ref_i31(&mut self) -> &mut Self { + self.inject(Operator::RefI31); + self + } + + fn i31_get_s(&mut self) -> &mut Self { + self.inject(Operator::I31GetS); + self + } + + fn i31_get_u(&mut self) -> &mut Self { + self.inject(Operator::I31GetU); + self + } } #[allow(dead_code)] diff --git a/tests/round_trip_wast.rs b/tests/round_trip_wast.rs index 525dbf9..d54f5d4 100644 --- a/tests/round_trip_wast.rs +++ b/tests/round_trip_wast.rs @@ -1,4 +1,5 @@ use orca_wasm::Component; +use orca_wasm::Module; use serde_json::Value; use std::fs; use std::path::Path; @@ -8,23 +9,28 @@ fn wasm_tools() -> Command { Command::new("wasm-tools") } -fn roundtrip(filename: String) { +fn roundtrip(filename: String, component: bool) { println!("filename: {:?}", filename); let buff = wat::parse_file(filename).expect("couldn't convert the input wat to Wasm"); - let mut component = Component::parse(&buff, false).expect("Unable to parse"); + if component { + let mut parser = Component::parse(&buff, false).expect("Unable to parse"); + let result = parser.encode(); + let out = wasmprinter::print_bytes(result.clone()).expect("couldn't translate Wasm to wat"); + let original = + wasmprinter::print_bytes(&buff).expect("couldn't convert original Wasm to wat"); + assert_eq!(out, original); + } else { + let mut parser = Module::parse(&buff, false).expect("Unable to parse"); + let result = parser.encode(); + let out = wasmprinter::print_bytes(result.clone()).expect("couldn't translate Wasm to wat"); + let original = + wasmprinter::print_bytes(&buff).expect("couldn't convert original Wasm to wat"); + assert_eq!(out, original); + } // component.print(); - let result = component.encode(); - let out = wasmprinter::print_bytes(result.clone()).expect("couldn't translate Wasm to wat"); - let original = wasmprinter::print_bytes(&buff).expect("couldn't convert original Wasm to wat"); - assert_eq!(out, original); } -#[test] -fn test_wast() { - let path = Path::new("./tests/wasm-tools/component-model/"); - // Generate the same output on windows and unix - let path = path.to_str().unwrap().replace("\\", "/"); - +fn test_wast(path: String, component: bool) { for entry in fs::read_dir(path).unwrap() { let file = entry.unwrap(); match file.path().extension() { @@ -44,13 +50,13 @@ fn test_wast() { .arg(td.path()) .arg("-o") .arg(td.path().join(format!( - "{:?}.json", - Path::new(&file.path()) - .file_stem() - .unwrap() - .to_str() - .unwrap() - ))); + "{:?}.json", + Path::new(&file.path()) + .file_stem() + .unwrap() + .to_str() + .unwrap() + ))); let output = cmd.output().unwrap(); let stdout = String::from_utf8_lossy(&output.stdout); if !output.status.success() { @@ -79,18 +85,21 @@ fn test_wast() { // If assert is not in the string, that means it is a valid test case if let Value::String(ty) = testcase.get_key_value("type").unwrap().1 { if !ty.contains("assert") { - if let Value::String(test_file) = - testcase.get_key_value("filename").unwrap().1 - { - // Do round-trip - roundtrip( - Path::new(td.path()) - .join(test_file) - .to_str() - .unwrap() - .parse() - .unwrap(), - ); + if testcase.contains_key("filename") { + if let Value::String(test_file) = + testcase.get_key_value("filename").unwrap().1 + { + // Do round-trip + roundtrip( + Path::new(td.path()) + .join(test_file) + .to_str() + .unwrap() + .parse() + .unwrap(), + component, + ); + } } } } @@ -101,3 +110,21 @@ fn test_wast() { } } } + +#[test] +fn test_wast_components() { + let path = Path::new("./tests/wasm-tools/component-model/"); + // Generate the same output on windows and unix + let path = path.to_str().unwrap().replace("\\", "/"); + + test_wast(path, true); +} + +#[test] +fn test_wast_gc() { + let path = Path::new("./tests/wasm-tools/gc/"); + // Generate the same output on windows and unix + let path = path.to_str().unwrap().replace("\\", "/"); + + test_wast(path, false); +} diff --git a/tests/test_module.rs b/tests/test_module.rs index 856f971..6d3d49f 100644 --- a/tests/test_module.rs +++ b/tests/test_module.rs @@ -3,8 +3,8 @@ use orca_wasm::ir::function::FunctionBuilder; use orca_wasm::ir::id::{ExportsID, FunctionID, ImportsID, TypeID}; use orca_wasm::ir::module::module_functions::FuncKind::{Import, Local}; use orca_wasm::ir::module::module_functions::{ImportedFunction, LocalFunction}; -use orca_wasm::ir::types::{Body, Value}; -use orca_wasm::{DataType, InitExpr, Module, Opcode}; +use orca_wasm::ir::types::{Body, InitExpr, Value}; +use orca_wasm::{DataType, Instructions, Module, Opcode}; use std::path::PathBuf; use std::process::Command; @@ -674,7 +674,12 @@ fn add_global_with_import() { let mut module = Module::parse(&buff, false).expect("Unable to parse module"); // add new global - let gid = module.add_global(InitExpr::Value(Value::I32(0)), DataType::I32, true, false); + let gid = module.add_global( + InitExpr::new(vec![Instructions::Value(Value::I32(0))]), + DataType::I32, + true, + false, + ); assert_eq!(1, *gid); let result = module.encode(); diff --git a/tests/wasm-tools/gc/array.wast b/tests/wasm-tools/gc/array.wast new file mode 100644 index 0000000..6ad95c0 --- /dev/null +++ b/tests/wasm-tools/gc/array.wast @@ -0,0 +1,315 @@ +;; Type syntax + +(module + (type (array i8)) + (type (array i16)) + (type (array i32)) + (type (array i64)) + (type (array f32)) + (type (array f64)) + (type (array anyref)) + (type (array (ref struct))) + (type (array (ref 0))) + (type (array (ref null 1))) + (type (array (mut i8))) + (type (array (mut i16))) + (type (array (mut i32))) + (type (array (mut i64))) + (type (array (mut i32))) + (type (array (mut i64))) + (type (array (mut anyref))) + (type (array (mut (ref struct)))) + (type (array (mut (ref 0)))) + (type (array (mut (ref null i31)))) +) + + +(assert_invalid + (module + (type (array (mut (ref null 10)))) + ) + "unknown type" +) + + +;; Binding structure + +(module + (rec + (type $s0 (array (ref $s1))) + (type $s1 (array (ref $s0))) + ) + + (func (param (ref $forward))) + + (type $forward (array i32)) +) + +(assert_invalid + (module (type (array (ref 1)))) + "unknown type" +) +(assert_invalid + (module (type (array (mut (ref 1))))) + "unknown type" +) + + +;; Basic instructions + +(module + (type $vec (array f32)) + (type $mvec (array (mut f32))) + + (global (ref $vec) (array.new $vec (f32.const 1) (i32.const 3))) + (global (ref $vec) (array.new_default $vec (i32.const 3))) + + (func $new (export "new") (result (ref $vec)) + (array.new_default $vec (i32.const 3)) + ) + + (func $get (param $i i32) (param $v (ref $vec)) (result f32) + (array.get $vec (local.get $v) (local.get $i)) + ) + (func (export "get") (param $i i32) (result f32) + (call $get (local.get $i) (call $new)) + ) + + (func $set_get (param $i i32) (param $v (ref $mvec)) (param $y f32) (result f32) + (array.set $mvec (local.get $v) (local.get $i) (local.get $y)) + (array.get $mvec (local.get $v) (local.get $i)) + ) + (func (export "set_get") (param $i i32) (param $y f32) (result f32) + (call $set_get (local.get $i) + (array.new_default $mvec (i32.const 3)) + (local.get $y) + ) + ) + + (func $len (param $v (ref array)) (result i32) + (array.len (local.get $v)) + ) + (func (export "len") (result i32) + (call $len (call $new)) + ) +) + +(assert_return (invoke "new") (ref.array)) +(assert_return (invoke "new") (ref.eq)) +(assert_return (invoke "get" (i32.const 0)) (f32.const 0)) +(assert_return (invoke "set_get" (i32.const 1) (f32.const 7)) (f32.const 7)) +(assert_return (invoke "len") (i32.const 3)) + +(assert_trap (invoke "get" (i32.const 10)) "out of bounds array access") +(assert_trap (invoke "set_get" (i32.const 10) (f32.const 7)) "out of bounds array access") + +(module + (type $vec (array f32)) + (type $mvec (array (mut f32))) + + (global (ref $vec) (array.new_fixed $vec 2 (f32.const 1) (f32.const 2))) + + (func $new (export "new") (result (ref $vec)) + (array.new_fixed $vec 2 (f32.const 1) (f32.const 2)) + ) + + (func $get (param $i i32) (param $v (ref $vec)) (result f32) + (array.get $vec (local.get $v) (local.get $i)) + ) + (func (export "get") (param $i i32) (result f32) + (call $get (local.get $i) (call $new)) + ) + + (func $set_get (param $i i32) (param $v (ref $mvec)) (param $y f32) (result f32) + (array.set $mvec (local.get $v) (local.get $i) (local.get $y)) + (array.get $mvec (local.get $v) (local.get $i)) + ) + (func (export "set_get") (param $i i32) (param $y f32) (result f32) + (call $set_get (local.get $i) + (array.new_fixed $mvec 3 (f32.const 1) (f32.const 2) (f32.const 3)) + (local.get $y) + ) + ) + + (func $len (param $v (ref array)) (result i32) + (array.len (local.get $v)) + ) + (func (export "len") (result i32) + (call $len (call $new)) + ) +) + +(assert_return (invoke "new") (ref.array)) +(assert_return (invoke "new") (ref.eq)) +(assert_return (invoke "get" (i32.const 0)) (f32.const 1)) +(assert_return (invoke "set_get" (i32.const 1) (f32.const 7)) (f32.const 7)) +(assert_return (invoke "len") (i32.const 2)) + +(assert_trap (invoke "get" (i32.const 10)) "out of bounds array access") +(assert_trap (invoke "set_get" (i32.const 10) (f32.const 7)) "out of bounds array access") + +(module + (type $vec (array i8)) + (type $mvec (array (mut i8))) + + (data $d "\00\01\02\ff\04") + + (func $new (export "new") (result (ref $vec)) + (array.new_data $vec $d (i32.const 1) (i32.const 3)) + ) + + (func $get_u (param $i i32) (param $v (ref $vec)) (result i32) + (array.get_u $vec (local.get $v) (local.get $i)) + ) + (func (export "get_u") (param $i i32) (result i32) + (call $get_u (local.get $i) (call $new)) + ) + + (func $get_s (param $i i32) (param $v (ref $vec)) (result i32) + (array.get_s $vec (local.get $v) (local.get $i)) + ) + (func (export "get_s") (param $i i32) (result i32) + (call $get_s (local.get $i) (call $new)) + ) + + (func $set_get (param $i i32) (param $v (ref $mvec)) (param $y i32) (result i32) + (array.set $mvec (local.get $v) (local.get $i) (local.get $y)) + (array.get_u $mvec (local.get $v) (local.get $i)) + ) + (func (export "set_get") (param $i i32) (param $y i32) (result i32) + (call $set_get (local.get $i) + (array.new_data $mvec $d (i32.const 1) (i32.const 3)) + (local.get $y) + ) + ) + + (func $len (param $v (ref array)) (result i32) + (array.len (local.get $v)) + ) + (func (export "len") (result i32) + (call $len (call $new)) + ) +) + +(assert_return (invoke "new") (ref.array)) +(assert_return (invoke "new") (ref.eq)) +(assert_return (invoke "get_u" (i32.const 2)) (i32.const 0xff)) +(assert_return (invoke "get_s" (i32.const 2)) (i32.const -1)) +(assert_return (invoke "set_get" (i32.const 1) (i32.const 7)) (i32.const 7)) +(assert_return (invoke "len") (i32.const 3)) + +(assert_trap (invoke "get_u" (i32.const 10)) "out of bounds array access") +(assert_trap (invoke "get_s" (i32.const 10)) "out of bounds array access") +(assert_trap (invoke "set_get" (i32.const 10) (i32.const 7)) "out of bounds array access") + +(module + (type $bvec (array i8)) + (type $vec (array (ref $bvec))) + (type $mvec (array (mut (ref $bvec)))) + (type $nvec (array (ref null $bvec))) + (type $avec (array (mut anyref))) + + (elem $e (ref $bvec) + (array.new $bvec (i32.const 7) (i32.const 3)) + (array.new_fixed $bvec 2 (i32.const 1) (i32.const 2)) + ) + + (func $new (export "new") (result (ref $vec)) + (array.new_elem $vec $e (i32.const 0) (i32.const 2)) + ) + + (func $sub1 (result (ref $nvec)) + (array.new_elem $nvec $e (i32.const 0) (i32.const 2)) + ) + (func $sub2 (result (ref $avec)) + (array.new_elem $avec $e (i32.const 0) (i32.const 2)) + ) + + (func $get (param $i i32) (param $j i32) (param $v (ref $vec)) (result i32) + (array.get_u $bvec (array.get $vec (local.get $v) (local.get $i)) (local.get $j)) + ) + (func (export "get") (param $i i32) (param $j i32) (result i32) + (call $get (local.get $i) (local.get $j) (call $new)) + ) + + (func $set_get (param $i i32) (param $j i32) (param $v (ref $mvec)) (param $y i32) (result i32) + (array.set $mvec (local.get $v) (local.get $i) (array.get $mvec (local.get $v) (local.get $y))) + (array.get_u $bvec (array.get $mvec (local.get $v) (local.get $i)) (local.get $j)) + ) + (func (export "set_get") (param $i i32) (param $j i32) (param $y i32) (result i32) + (call $set_get (local.get $i) (local.get $j) + (array.new_elem $mvec $e (i32.const 0) (i32.const 2)) + (local.get $y) + ) + ) + + (func $len (param $v (ref array)) (result i32) + (array.len (local.get $v)) + ) + (func (export "len") (result i32) + (call $len (call $new)) + ) +) + +(assert_return (invoke "new") (ref.array)) +(assert_return (invoke "new") (ref.eq)) +(assert_return (invoke "get" (i32.const 0) (i32.const 0)) (i32.const 7)) +(assert_return (invoke "get" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "set_get" (i32.const 0) (i32.const 1) (i32.const 1)) (i32.const 2)) +(assert_return (invoke "len") (i32.const 2)) + +(assert_trap (invoke "get" (i32.const 10) (i32.const 0)) "out of bounds array access") +(assert_trap (invoke "set_get" (i32.const 10) (i32.const 0) (i32.const 0)) "out of bounds array access") + +(assert_invalid + (module + (type $a (array i64)) + (func (export "array.set-immutable") (param $a (ref $a)) + (array.set $a (local.get $a) (i32.const 0) (i64.const 1)) + ) + ) + "array is immutable" +) + +(assert_invalid + (module + (type $bvec (array i8)) + + (data $d "\00\01\02\03\04") + + (global (ref $bvec) + (array.new_data $bvec $d (i32.const 1) (i32.const 3)) + ) + ) + "constant expression required" +) + +(assert_invalid + (module + (type $bvec (array i8)) + (type $vvec (array (ref $bvec))) + + (elem $e (ref $bvec) (ref.null $bvec)) + + (global (ref $vvec) + (array.new_elem $vvec $e (i32.const 0) (i32.const 1)) + ) + ) + "constant expression required" +) + + +;; Null dereference + +(module + (type $t (array (mut i32))) + (func (export "array.get-null") + (local (ref null $t)) (drop (array.get $t (local.get 0) (i32.const 0))) + ) + (func (export "array.set-null") + (local (ref null $t)) (array.set $t (local.get 0) (i32.const 0) (i32.const 0)) + ) +) + +(assert_trap (invoke "array.get-null") "null array reference") +(assert_trap (invoke "array.set-null") "null array reference") diff --git a/tests/wasm-tools/gc/array_copy.wast b/tests/wasm-tools/gc/array_copy.wast new file mode 100644 index 0000000..d1caf12 --- /dev/null +++ b/tests/wasm-tools/gc/array_copy.wast @@ -0,0 +1,139 @@ +;; Bulk instructions + +;; invalid uses + +(assert_invalid + (module + (type $a (array i8)) + (type $b (array (mut i8))) + + (func (export "array.copy-immutable") (param $1 (ref $a)) (param $2 (ref $b)) + (array.copy $a $b (local.get $1) (i32.const 0) (local.get $2) (i32.const 0) (i32.const 0)) + ) + ) + "array is immutable" +) + +(assert_invalid + (module + (type $a (array (mut i8))) + (type $b (array i16)) + + (func (export "array.copy-packed-invalid") (param $1 (ref $a)) (param $2 (ref $b)) + (array.copy $a $b (local.get $1) (i32.const 0) (local.get $2) (i32.const 0) (i32.const 0)) + ) + ) + "array types do not match" +) + +(assert_invalid + (module + (type $a (array (mut i8))) + (type $b (array (mut (ref $a)))) + + (func (export "array.copy-ref-invalid-1") (param $1 (ref $a)) (param $2 (ref $b)) + (array.copy $a $b (local.get $1) (i32.const 0) (local.get $2) (i32.const 0) (i32.const 0)) + ) + ) + "array types do not match" +) + +(assert_invalid + (module + (type $a (array (mut i8))) + (type $b (array (mut (ref $a)))) + (type $c (array (mut (ref $b)))) + + (func (export "array.copy-ref-invalid-1") (param $1 (ref $b)) (param $2 (ref $c)) + (array.copy $b $c (local.get $1) (i32.const 0) (local.get $2) (i32.const 0) (i32.const 0)) + ) + ) + "array types do not match" +) + +(module + (type $arr8 (array i8)) + (type $arr8_mut (array (mut i8))) + + (global $g_arr8 (ref $arr8) (array.new $arr8 (i32.const 10) (i32.const 12))) + (global $g_arr8_mut (mut (ref $arr8_mut)) (array.new_default $arr8_mut (i32.const 12))) + + (data $d1 "abcdefghijkl") + + (func (export "array_get_nth") (param $1 i32) (result i32) + (array.get_u $arr8_mut (global.get $g_arr8_mut) (local.get $1)) + ) + + (func (export "array_copy-null-left") + (array.copy $arr8_mut $arr8 (ref.null $arr8_mut) (i32.const 0) (global.get $g_arr8) (i32.const 0) (i32.const 0)) + ) + + (func (export "array_copy-null-right") + (array.copy $arr8_mut $arr8 (global.get $g_arr8_mut) (i32.const 0) (ref.null $arr8) (i32.const 0) (i32.const 0)) + ) + + (func (export "array_copy") (param $1 i32) (param $2 i32) (param $3 i32) + (array.copy $arr8_mut $arr8 (global.get $g_arr8_mut) (local.get $1) (global.get $g_arr8) (local.get $2) (local.get $3)) + ) + + (func (export "array_copy_overlap_test-1") + (local $1 (ref $arr8_mut)) + (array.new_data $arr8_mut $d1 (i32.const 0) (i32.const 12)) + (local.set $1) + (array.copy $arr8_mut $arr8_mut (local.get $1) (i32.const 1) (local.get $1) (i32.const 0) (i32.const 11)) + (global.set $g_arr8_mut (local.get $1)) + ) + + (func (export "array_copy_overlap_test-2") + (local $1 (ref $arr8_mut)) + (array.new_data $arr8_mut $d1 (i32.const 0) (i32.const 12)) + (local.set $1) + (array.copy $arr8_mut $arr8_mut (local.get $1) (i32.const 0) (local.get $1) (i32.const 1) (i32.const 11)) + (global.set $g_arr8_mut (local.get $1)) + ) +) + +;; null array argument traps +(assert_trap (invoke "array_copy-null-left") "null array reference") +(assert_trap (invoke "array_copy-null-right") "null array reference") + +;; OOB initial index traps +(assert_trap (invoke "array_copy" (i32.const 13) (i32.const 0) (i32.const 0)) "out of bounds array access") +(assert_trap (invoke "array_copy" (i32.const 0) (i32.const 13) (i32.const 0)) "out of bounds array access") + +;; OOB length traps +(assert_trap (invoke "array_copy" (i32.const 0) (i32.const 0) (i32.const 13)) "out of bounds array access") +(assert_trap (invoke "array_copy" (i32.const 0) (i32.const 0) (i32.const 13)) "out of bounds array access") + +;; start index = array size, len = 0 doesn't trap +(assert_return (invoke "array_copy" (i32.const 12) (i32.const 0) (i32.const 0))) +(assert_return (invoke "array_copy" (i32.const 0) (i32.const 12) (i32.const 0))) + +;; check arrays were not modified +(assert_return (invoke "array_get_nth" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "array_get_nth" (i32.const 5)) (i32.const 0)) +(assert_return (invoke "array_get_nth" (i32.const 11)) (i32.const 0)) +(assert_trap (invoke "array_get_nth" (i32.const 12)) "out of bounds array access") + +;; normal case +(assert_return (invoke "array_copy" (i32.const 0) (i32.const 0) (i32.const 2))) +(assert_return (invoke "array_get_nth" (i32.const 0)) (i32.const 10)) +(assert_return (invoke "array_get_nth" (i32.const 1)) (i32.const 10)) +(assert_return (invoke "array_get_nth" (i32.const 2)) (i32.const 0)) + +;; test that overlapping array.copy works as if intermediate copy taken +(assert_return (invoke "array_copy_overlap_test-1")) +(assert_return (invoke "array_get_nth" (i32.const 0)) (i32.const 97)) +(assert_return (invoke "array_get_nth" (i32.const 1)) (i32.const 97)) +(assert_return (invoke "array_get_nth" (i32.const 2)) (i32.const 98)) +(assert_return (invoke "array_get_nth" (i32.const 5)) (i32.const 101)) +(assert_return (invoke "array_get_nth" (i32.const 10)) (i32.const 106)) +(assert_return (invoke "array_get_nth" (i32.const 11)) (i32.const 107)) + +(assert_return (invoke "array_copy_overlap_test-2")) +(assert_return (invoke "array_get_nth" (i32.const 0)) (i32.const 98)) +(assert_return (invoke "array_get_nth" (i32.const 1)) (i32.const 99)) +(assert_return (invoke "array_get_nth" (i32.const 5)) (i32.const 103)) +(assert_return (invoke "array_get_nth" (i32.const 9)) (i32.const 107)) +(assert_return (invoke "array_get_nth" (i32.const 10)) (i32.const 108)) +(assert_return (invoke "array_get_nth" (i32.const 11)) (i32.const 108)) diff --git a/tests/wasm-tools/gc/array_fill.wast b/tests/wasm-tools/gc/array_fill.wast new file mode 100644 index 0000000..0379ad5 --- /dev/null +++ b/tests/wasm-tools/gc/array_fill.wast @@ -0,0 +1,81 @@ +;; Bulk instructions + +;; invalid uses + +(assert_invalid + (module + (type $a (array i8)) + + (func (export "array.fill-immutable") (param $1 (ref $a)) (param $2 i32) + (array.fill $a (local.get $1) (i32.const 0) (local.get $2) (i32.const 0)) + ) + ) + "array is immutable" +) + +(assert_invalid + (module + (type $a (array (mut i8))) + + (func (export "array.fill-invalid-1") (param $1 (ref $a)) (param $2 funcref) + (array.fill $a (local.get $1) (i32.const 0) (local.get $2) (i32.const 0)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $b (array (mut funcref))) + + (func (export "array.fill-invalid-1") (param $1 (ref $b)) (param $2 i32) + (array.fill $b (local.get $1) (i32.const 0) (local.get $2) (i32.const 0)) + ) + ) + "type mismatch" +) + +(module + (type $arr8 (array i8)) + (type $arr8_mut (array (mut i8))) + + (global $g_arr8 (ref $arr8) (array.new $arr8 (i32.const 10) (i32.const 12))) + (global $g_arr8_mut (mut (ref $arr8_mut)) (array.new_default $arr8_mut (i32.const 12))) + + (func (export "array_get_nth") (param $1 i32) (result i32) + (array.get_u $arr8_mut (global.get $g_arr8_mut) (local.get $1)) + ) + + (func (export "array_fill-null") + (array.fill $arr8_mut (ref.null $arr8_mut) (i32.const 0) (i32.const 0) (i32.const 0)) + ) + + (func (export "array_fill") (param $1 i32) (param $2 i32) (param $3 i32) + (array.fill $arr8_mut (global.get $g_arr8_mut) (local.get $1) (local.get $2) (local.get $3)) + ) +) + +;; null array argument traps +(assert_trap (invoke "array_fill-null") "null array reference") + +;; OOB initial index traps +(assert_trap (invoke "array_fill" (i32.const 13) (i32.const 0) (i32.const 0)) "out of bounds array access") + +;; OOB length traps +(assert_trap (invoke "array_fill" (i32.const 0) (i32.const 0) (i32.const 13)) "out of bounds array access") + +;; start index = array size, len = 0 doesn't trap +(assert_return (invoke "array_fill" (i32.const 12) (i32.const 0) (i32.const 0))) + +;; check arrays were not modified +(assert_return (invoke "array_get_nth" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "array_get_nth" (i32.const 5)) (i32.const 0)) +(assert_return (invoke "array_get_nth" (i32.const 11)) (i32.const 0)) +(assert_trap (invoke "array_get_nth" (i32.const 12)) "out of bounds array access") + +;; normal case +(assert_return (invoke "array_fill" (i32.const 2) (i32.const 11) (i32.const 2))) +(assert_return (invoke "array_get_nth" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "array_get_nth" (i32.const 2)) (i32.const 11)) +(assert_return (invoke "array_get_nth" (i32.const 3)) (i32.const 11)) +(assert_return (invoke "array_get_nth" (i32.const 4)) (i32.const 0)) diff --git a/tests/wasm-tools/gc/array_init_data.wast b/tests/wasm-tools/gc/array_init_data.wast new file mode 100644 index 0000000..3bee026 --- /dev/null +++ b/tests/wasm-tools/gc/array_init_data.wast @@ -0,0 +1,110 @@ +;; Bulk instructions + +;; invalid uses + +(assert_invalid + (module + (type $a (array i8)) + + (data $d1 "a") + + (func (export "array.init_data-immutable") (param $1 (ref $a)) + (array.init_data $a $d1 (local.get $1) (i32.const 0) (i32.const 0) (i32.const 0)) + ) + ) + "array is immutable" +) + +(assert_invalid + (module + (type $a (array (mut funcref))) + + (data $d1 "a") + + (func (export "array.init_data-invalid-1") (param $1 (ref $a)) + (array.init_data $a $d1 (local.get $1) (i32.const 0) (i32.const 0) (i32.const 0)) + ) + ) + "array type is not numeric or vector" +) + +(module + (type $arr8 (array i8)) + (type $arr8_mut (array (mut i8))) + (type $arr16_mut (array (mut i16))) + + (global $g_arr8 (ref $arr8) (array.new $arr8 (i32.const 10) (i32.const 12))) + (global $g_arr8_mut (mut (ref $arr8_mut)) (array.new_default $arr8_mut (i32.const 12))) + (global $g_arr16_mut (ref $arr16_mut) (array.new_default $arr16_mut (i32.const 6))) + + (data $d1 "abcdefghijkl") + + (func (export "array_get_nth") (param $1 i32) (result i32) + (array.get_u $arr8_mut (global.get $g_arr8_mut) (local.get $1)) + ) + + (func (export "array_get_nth_i16") (param $1 i32) (result i32) + (array.get_u $arr16_mut (global.get $g_arr16_mut) (local.get $1)) + ) + + (func (export "array_init_data-null") + (array.init_data $arr8_mut $d1 (ref.null $arr8_mut) (i32.const 0) (i32.const 0) (i32.const 0)) + ) + + (func (export "array_init_data") (param $1 i32) (param $2 i32) (param $3 i32) + (array.init_data $arr8_mut $d1 (global.get $g_arr8_mut) (local.get $1) (local.get $2) (local.get $3)) + ) + + (func (export "array_init_data_i16") (param $1 i32) (param $2 i32) (param $3 i32) + (array.init_data $arr16_mut $d1 (global.get $g_arr16_mut) (local.get $1) (local.get $2) (local.get $3)) + ) + + (func (export "drop_segs") + (data.drop $d1) + ) +) + +;; null array argument traps +(assert_trap (invoke "array_init_data-null") "null array reference") + +;; OOB initial index traps +(assert_trap (invoke "array_init_data" (i32.const 13) (i32.const 0) (i32.const 0)) "out of bounds array access") +(assert_trap (invoke "array_init_data" (i32.const 0) (i32.const 13) (i32.const 0)) "out of bounds memory access") + +;; OOB length traps +(assert_trap (invoke "array_init_data" (i32.const 0) (i32.const 0) (i32.const 13)) "out of bounds array access") +(assert_trap (invoke "array_init_data" (i32.const 0) (i32.const 0) (i32.const 13)) "out of bounds array access") +(assert_trap (invoke "array_init_data_i16" (i32.const 0) (i32.const 0) (i32.const 7)) "out of bounds array access") + +;; start index = array size, len = 0 doesn't trap +(assert_return (invoke "array_init_data" (i32.const 12) (i32.const 0) (i32.const 0))) +(assert_return (invoke "array_init_data" (i32.const 0) (i32.const 12) (i32.const 0))) +(assert_return (invoke "array_init_data_i16" (i32.const 0) (i32.const 6) (i32.const 0))) + +;; check arrays were not modified +(assert_return (invoke "array_get_nth" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "array_get_nth" (i32.const 5)) (i32.const 0)) +(assert_return (invoke "array_get_nth" (i32.const 11)) (i32.const 0)) +(assert_trap (invoke "array_get_nth" (i32.const 12)) "out of bounds array access") +(assert_return (invoke "array_get_nth_i16" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "array_get_nth_i16" (i32.const 2)) (i32.const 0)) +(assert_return (invoke "array_get_nth_i16" (i32.const 5)) (i32.const 0)) +(assert_trap (invoke "array_get_nth_i16" (i32.const 6)) "out of bounds array access") + +;; normal cases +(assert_return (invoke "array_init_data" (i32.const 4) (i32.const 2) (i32.const 2))) +(assert_return (invoke "array_get_nth" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "array_get_nth" (i32.const 4)) (i32.const 99)) +(assert_return (invoke "array_get_nth" (i32.const 5)) (i32.const 100)) +(assert_return (invoke "array_get_nth" (i32.const 6)) (i32.const 0)) + +(assert_return (invoke "array_init_data_i16" (i32.const 2) (i32.const 5) (i32.const 2))) +(assert_return (invoke "array_get_nth_i16" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "array_get_nth_i16" (i32.const 2)) (i32.const 0x6766)) +(assert_return (invoke "array_get_nth_i16" (i32.const 3)) (i32.const 0x6968)) +(assert_return (invoke "array_get_nth_i16" (i32.const 4)) (i32.const 0)) + +;; init_data/elem with dropped segments traps for non-zero length +(assert_return (invoke "drop_segs")) +(assert_return (invoke "array_init_data" (i32.const 0) (i32.const 0) (i32.const 0))) +(assert_trap (invoke "array_init_data" (i32.const 0) (i32.const 0) (i32.const 1)) "out of bounds memory access") diff --git a/tests/wasm-tools/gc/array_init_elem.wast b/tests/wasm-tools/gc/array_init_elem.wast new file mode 100644 index 0000000..34a1259 --- /dev/null +++ b/tests/wasm-tools/gc/array_init_elem.wast @@ -0,0 +1,108 @@ +;; Bulk instructions + +;; invalid uses + +(assert_invalid + (module + (type $a (array funcref)) + + (elem $e1 funcref) + + (func (export "array.init_elem-immutable") (param $1 (ref $a)) + (array.init_elem $a $e1 (local.get $1) (i32.const 0) (i32.const 0) (i32.const 0)) + ) + ) + "array is immutable" +) + +(assert_invalid + (module + (type $a (array (mut i8))) + + (elem $e1 funcref) + + (func (export "array.init_elem-invalid-1") (param $1 (ref $a)) + (array.init_elem $a $e1 (local.get $1) (i32.const 0) (i32.const 0) (i32.const 0)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $a (array (mut funcref))) + + (elem $e1 externref) + + (func (export "array.init_elem-invalid-2") (param $1 (ref $a)) + (array.init_elem $a $e1 (local.get $1) (i32.const 0) (i32.const 0) (i32.const 0)) + ) + ) + "type mismatch" +) + +(module + (type $t_f (func)) + (type $arrref (array (ref $t_f))) + (type $arrref_mut (array (mut funcref))) + + (global $g_arrref (ref $arrref) (array.new $arrref (ref.func $dummy) (i32.const 12))) + (global $g_arrref_mut (ref $arrref_mut) (array.new_default $arrref_mut (i32.const 12))) + + (table $t 1 funcref) + + (elem $e1 func $dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy) + + (func $dummy + ) + + (func (export "array_call_nth") (param $1 i32) + (table.set $t (i32.const 0) (array.get $arrref_mut (global.get $g_arrref_mut) (local.get $1))) + (call_indirect $t (i32.const 0)) + ) + + (func (export "array_init_elem-null") + (array.init_elem $arrref_mut $e1 (ref.null $arrref_mut) (i32.const 0) (i32.const 0) (i32.const 0)) + ) + + (func (export "array_init_elem") (param $1 i32) (param $2 i32) (param $3 i32) + (array.init_elem $arrref_mut $e1 (global.get $g_arrref_mut) (local.get $1) (local.get $2) (local.get $3)) + ) + + (func (export "drop_segs") + (elem.drop $e1) + ) +) + +;; null array argument traps +(assert_trap (invoke "array_init_elem-null") "null array reference") + +;; OOB initial index traps +(assert_trap (invoke "array_init_elem" (i32.const 13) (i32.const 0) (i32.const 0)) "out of bounds array access") +(assert_trap (invoke "array_init_elem" (i32.const 0) (i32.const 13) (i32.const 0)) "out of bounds table access") + +;; OOB length traps +(assert_trap (invoke "array_init_elem" (i32.const 0) (i32.const 0) (i32.const 13)) "out of bounds array access") +(assert_trap (invoke "array_init_elem" (i32.const 0) (i32.const 0) (i32.const 13)) "out of bounds array access") + +;; start index = array size, len = 0 doesn't trap +(assert_return (invoke "array_init_elem" (i32.const 12) (i32.const 0) (i32.const 0))) +(assert_return (invoke "array_init_elem" (i32.const 0) (i32.const 12) (i32.const 0))) + +;; check arrays were not modified +(assert_trap (invoke "array_call_nth" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "array_call_nth" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "array_call_nth" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "array_call_nth" (i32.const 12)) "out of bounds array access") + +;; normal cases +(assert_return (invoke "array_init_elem" (i32.const 2) (i32.const 3) (i32.const 2))) +(assert_trap (invoke "array_call_nth" (i32.const 1)) "uninitialized element") +(assert_return (invoke "array_call_nth" (i32.const 2))) +(assert_return (invoke "array_call_nth" (i32.const 3))) +(assert_trap (invoke "array_call_nth" (i32.const 4)) "uninitialized element") + +;; init_data/elem with dropped segments traps for non-zero length +(assert_return (invoke "drop_segs")) +(assert_return (invoke "array_init_elem" (i32.const 0) (i32.const 0) (i32.const 0))) +(assert_trap (invoke "array_init_elem" (i32.const 0) (i32.const 0) (i32.const 1)) "out of bounds table access") diff --git a/tests/wasm-tools/gc/array_new_data.wast b/tests/wasm-tools/gc/array_new_data.wast new file mode 100644 index 0000000..2af894c --- /dev/null +++ b/tests/wasm-tools/gc/array_new_data.wast @@ -0,0 +1,68 @@ +(module + (type $arr (array (mut i8))) + + (data $d "abcd") + + (func (export "array-new-data") (param i32 i32) (result (ref $arr)) + (array.new_data $arr $d (local.get 0) (local.get 1)) + ) +) + +;; In-bounds data segment accesses. +(assert_return (invoke "array-new-data" (i32.const 0) (i32.const 0)) (ref.array)) +(assert_return (invoke "array-new-data" (i32.const 0) (i32.const 4)) (ref.array)) +(assert_return (invoke "array-new-data" (i32.const 1) (i32.const 2)) (ref.array)) +(assert_return (invoke "array-new-data" (i32.const 4) (i32.const 0)) (ref.array)) + +;; Out-of-bounds data segment accesses. +(assert_trap (invoke "array-new-data" (i32.const 0) (i32.const 5)) "out of bounds memory access") +(assert_trap (invoke "array-new-data" (i32.const 5) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "array-new-data" (i32.const 1) (i32.const 4)) "out of bounds memory access") +(assert_trap (invoke "array-new-data" (i32.const 4) (i32.const 1)) "out of bounds memory access") + + +(module + (type $arr (array (mut i8))) + + (data $d "\aa\bb\cc\dd") + + (func (export "array-new-data-contents") (result i32 i32) + (local (ref $arr)) + (local.set 0 (array.new_data $arr $d (i32.const 1) (i32.const 2))) + (array.get_u $arr (local.get 0) (i32.const 0)) + (array.get_u $arr (local.get 0) (i32.const 1)) + ) +) + +;; Array is initialized with the correct contents. +(assert_return (invoke "array-new-data-contents") (i32.const 0xbb) (i32.const 0xcc)) + +(module + (type $arr (array (mut i32))) + + (data $d "\aa\bb\cc\dd") + + (func (export "array-new-data-little-endian") (result i32) + (array.get $arr + (array.new_data $arr $d (i32.const 0) (i32.const 1)) + (i32.const 0)) + ) +) + +;; Data segments are interpreted as little-endian. +(assert_return (invoke "array-new-data-little-endian") (i32.const 0xddccbbaa)) + +(module + (type $arr (array (mut i16))) + + (data $d "\00\11\22") + + (func (export "array-new-data-unaligned") (result i32) + (array.get_u $arr + (array.new_data $arr $d (i32.const 1) (i32.const 1)) + (i32.const 0)) + ) +) + +;; Data inside the segment doesn't need to be aligned to the element size. +(assert_return (invoke "array-new-data-unaligned") (i32.const 0x2211)) diff --git a/tests/wasm-tools/gc/array_new_elem.wast b/tests/wasm-tools/gc/array_new_elem.wast new file mode 100644 index 0000000..d71d981 --- /dev/null +++ b/tests/wasm-tools/gc/array_new_elem.wast @@ -0,0 +1,103 @@ +;;;; Expression-style element segments. + +(module + (type $arr (array i31ref)) + + (elem $e i31ref + (ref.i31 (i32.const 0xaa)) + (ref.i31 (i32.const 0xbb)) + (ref.i31 (i32.const 0xcc)) + (ref.i31 (i32.const 0xdd))) + + (func (export "array-new-elem") (param i32 i32) (result (ref $arr)) + (array.new_elem $arr $e (local.get 0) (local.get 1)) + ) +) + +;; In-bounds element segment accesses. +(assert_return (invoke "array-new-elem" (i32.const 0) (i32.const 0)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 0) (i32.const 4)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 1) (i32.const 2)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 4) (i32.const 0)) (ref.array)) + +;; Out-of-bounds element segment accesses. +(assert_trap (invoke "array-new-elem" (i32.const 0) (i32.const 5)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 5) (i32.const 0)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 1) (i32.const 4)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 4) (i32.const 1)) "out of bounds table access") + +(module + (type $arr (array i31ref)) + + (elem $e i31ref + (ref.i31 (i32.const 0xaa)) + (ref.i31 (i32.const 0xbb)) + (ref.i31 (i32.const 0xcc)) + (ref.i31 (i32.const 0xdd))) + + (func (export "array-new-elem-contents") (result i32 i32) + (local (ref $arr)) + (local.set 0 (array.new_elem $arr $e (i32.const 1) (i32.const 2))) + (i31.get_u (array.get $arr (local.get 0) (i32.const 0))) + (i31.get_u (array.get $arr (local.get 0) (i32.const 1))) + ) +) + +;; Array is initialized with the correct contents. +(assert_return (invoke "array-new-elem-contents") (i32.const 0xbb) (i32.const 0xcc)) + +;;;; MVP-style function-index segments. + +(module + (type $arr (array funcref)) + + (elem $e func $aa $bb $cc $dd) + (func $aa (result i32) (i32.const 0xaa)) + (func $bb (result i32) (i32.const 0xbb)) + (func $cc (result i32) (i32.const 0xcc)) + (func $dd (result i32) (i32.const 0xdd)) + + (func (export "array-new-elem") (param i32 i32) (result (ref $arr)) + (array.new_elem $arr $e (local.get 0) (local.get 1)) + ) +) + +;; In-bounds element segment accesses. +(assert_return (invoke "array-new-elem" (i32.const 0) (i32.const 0)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 0) (i32.const 4)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 1) (i32.const 2)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 4) (i32.const 0)) (ref.array)) + +;; Out-of-bounds element segment accesses. +(assert_trap (invoke "array-new-elem" (i32.const 0) (i32.const 5)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 5) (i32.const 0)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 1) (i32.const 4)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 4) (i32.const 1)) "out of bounds table access") + +(module + (type $f (func (result i32))) + (type $arr (array funcref)) + + (elem $e func $aa $bb $cc $dd) + (func $aa (result i32) (i32.const 0xaa)) + (func $bb (result i32) (i32.const 0xbb)) + (func $cc (result i32) (i32.const 0xcc)) + (func $dd (result i32) (i32.const 0xdd)) + + (table $t 2 2 funcref) + + (func (export "array-new-elem-contents") (result i32 i32) + (local (ref $arr)) + (local.set 0 (array.new_elem $arr $e (i32.const 1) (i32.const 2))) + + (table.set $t (i32.const 0) (array.get $arr (local.get 0) (i32.const 0))) + (table.set $t (i32.const 1) (array.get $arr (local.get 0) (i32.const 1))) + + (call_indirect (type $f) (i32.const 0)) + (call_indirect (type $f) (i32.const 1)) + + ) +) + +;; Array is initialized with the correct contents. +(assert_return (invoke "array-new-elem-contents") (i32.const 0xbb) (i32.const 0xcc)) diff --git a/tests/wasm-tools/gc/binary-gc.wast b/tests/wasm-tools/gc/binary-gc.wast new file mode 100644 index 0000000..589573f --- /dev/null +++ b/tests/wasm-tools/gc/binary-gc.wast @@ -0,0 +1,12 @@ +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01" ;; Type section id + "\04" ;; Type section length + "\01" ;; Types vector length + "\5e" ;; Array type, -0x22 + "\78" ;; Storage type: i8 or -0x08 + "\02" ;; Mutability, should be 0 or 1, but isn't + ) + "malformed mutability" +) diff --git a/tests/wasm-tools/gc/br_on_cast.wast b/tests/wasm-tools/gc/br_on_cast.wast new file mode 100644 index 0000000..3c895c0 --- /dev/null +++ b/tests/wasm-tools/gc/br_on_cast.wast @@ -0,0 +1,285 @@ +;; Abstract Types + +(module + (type $ft (func (result i32))) + (type $st (struct (field i16))) + (type $at (array i8)) + + (table 10 anyref) + + (elem declare func $f) + (func $f (result i32) (i32.const 9)) + + (func (export "init") (param $x externref) + (table.set (i32.const 0) (ref.null any)) + (table.set (i32.const 1) (ref.i31 (i32.const 7))) + (table.set (i32.const 2) (struct.new $st (i32.const 6))) + (table.set (i32.const 3) (array.new $at (i32.const 5) (i32.const 3))) + (table.set (i32.const 4) (any.convert_extern (local.get $x))) + ) + + (func (export "br_on_null") (param $i i32) (result i32) + (block $l + (br_on_null $l (table.get (local.get $i))) + (return (i32.const -1)) + ) + (i32.const 0) + ) + (func (export "br_on_i31") (param $i i32) (result i32) + (block $l (result (ref i31)) + (br_on_cast $l anyref (ref i31) (table.get (local.get $i))) + (return (i32.const -1)) + ) + (i31.get_u) + ) + (func (export "br_on_struct") (param $i i32) (result i32) + (block $l (result (ref struct)) + (br_on_cast $l anyref (ref struct) (table.get (local.get $i))) + (return (i32.const -1)) + ) + (block $l2 (param structref) (result (ref $st)) + (block $l3 (param structref) (result (ref $at)) + (br_on_cast $l2 structref (ref $st)) + (br_on_cast $l3 anyref (ref $at)) + (return (i32.const -2)) + ) + (return (array.get_u $at (i32.const 0))) + ) + (struct.get_s $st 0) + ) + (func (export "br_on_array") (param $i i32) (result i32) + (block $l (result (ref array)) + (br_on_cast $l anyref (ref array) (table.get (local.get $i))) + (return (i32.const -1)) + ) + (array.len) + ) + + (func (export "null-diff") (param $i i32) (result i32) + (block $l (result (ref null struct)) + (block (result (ref any)) + (br_on_cast $l (ref null any) (ref null struct) (table.get (local.get $i))) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) +) + +(invoke "init" (ref.extern 0)) + +(assert_return (invoke "br_on_null" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "br_on_null" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "br_on_null" (i32.const 2)) (i32.const -1)) +(assert_return (invoke "br_on_null" (i32.const 3)) (i32.const -1)) +(assert_return (invoke "br_on_null" (i32.const 4)) (i32.const -1)) + +(assert_return (invoke "br_on_i31" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "br_on_i31" (i32.const 1)) (i32.const 7)) +(assert_return (invoke "br_on_i31" (i32.const 2)) (i32.const -1)) +(assert_return (invoke "br_on_i31" (i32.const 3)) (i32.const -1)) +(assert_return (invoke "br_on_i31" (i32.const 4)) (i32.const -1)) + +(assert_return (invoke "br_on_struct" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "br_on_struct" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "br_on_struct" (i32.const 2)) (i32.const 6)) +(assert_return (invoke "br_on_struct" (i32.const 3)) (i32.const -1)) +(assert_return (invoke "br_on_struct" (i32.const 4)) (i32.const -1)) + +(assert_return (invoke "br_on_array" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "br_on_array" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "br_on_array" (i32.const 2)) (i32.const -1)) +(assert_return (invoke "br_on_array" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "br_on_array" (i32.const 4)) (i32.const -1)) + +(assert_return (invoke "null-diff" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "null-diff" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "null-diff" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "null-diff" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "null-diff" (i32.const 4)) (i32.const 0)) + + +;; Concrete Types + +(module + (type $t0 (sub (struct))) + (type $t1 (sub $t0 (struct (field i32)))) + (type $t1' (sub $t0 (struct (field i32)))) + (type $t2 (sub $t1 (struct (field i32 i32)))) + (type $t2' (sub $t1' (struct (field i32 i32)))) + (type $t3 (sub $t0 (struct (field i32 i32)))) + (type $t0' (sub $t0 (struct))) + (type $t4 (sub $t0' (struct (field i32 i32)))) + + (table 20 structref) + + (func $init + (table.set (i32.const 0) (struct.new_default $t0)) + (table.set (i32.const 10) (struct.new_default $t0')) + (table.set (i32.const 1) (struct.new_default $t1)) + (table.set (i32.const 11) (struct.new_default $t1')) + (table.set (i32.const 2) (struct.new_default $t2)) + (table.set (i32.const 12) (struct.new_default $t2')) + (table.set (i32.const 3) (struct.new_default $t3)) + (table.set (i32.const 4) (struct.new_default $t4)) + ) + + (func (export "test-sub") + (call $init) + (block $l (result structref) + ;; must succeed + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (ref.null struct)))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 3))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 4))))) + + (drop (block (result structref) (br_on_cast 0 structref (ref $t1) (ref.null struct)))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t1) (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t1) (table.get (i32.const 2))))) + + (drop (block (result structref) (br_on_cast 0 structref (ref $t2) (ref.null struct)))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t2) (table.get (i32.const 2))))) + + (drop (block (result structref) (br_on_cast 0 structref (ref $t3) (ref.null struct)))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t3) (table.get (i32.const 3))))) + + (drop (block (result structref) (br_on_cast 0 structref (ref $t4) (ref.null struct)))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t4) (table.get (i32.const 4))))) + + ;; must not succeed + (br_on_cast $l anyref (ref $t1) (table.get (i32.const 0))) + (br_on_cast $l anyref (ref $t1) (table.get (i32.const 3))) + (br_on_cast $l anyref (ref $t1) (table.get (i32.const 4))) + + (br_on_cast $l anyref (ref $t2) (table.get (i32.const 0))) + (br_on_cast $l anyref (ref $t2) (table.get (i32.const 1))) + (br_on_cast $l anyref (ref $t2) (table.get (i32.const 3))) + (br_on_cast $l anyref (ref $t2) (table.get (i32.const 4))) + + (br_on_cast $l anyref (ref $t3) (table.get (i32.const 0))) + (br_on_cast $l anyref (ref $t3) (table.get (i32.const 1))) + (br_on_cast $l anyref (ref $t3) (table.get (i32.const 2))) + (br_on_cast $l anyref (ref $t3) (table.get (i32.const 4))) + + (br_on_cast $l anyref (ref $t4) (table.get (i32.const 0))) + (br_on_cast $l anyref (ref $t4) (table.get (i32.const 1))) + (br_on_cast $l anyref (ref $t4) (table.get (i32.const 2))) + (br_on_cast $l anyref (ref $t4) (table.get (i32.const 3))) + + (return) + ) + (unreachable) + ) + + (func (export "test-canon") + (call $init) + (block $l + (drop (block (result structref) (br_on_cast 0 structref (ref $t0') (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0') (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0') (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0') (table.get (i32.const 3))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0') (table.get (i32.const 4))))) + + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 10))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 11))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t0) (table.get (i32.const 12))))) + + (drop (block (result structref) (br_on_cast 0 structref (ref $t1') (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t1') (table.get (i32.const 2))))) + + (drop (block (result structref) (br_on_cast 0 structref (ref $t1) (table.get (i32.const 11))))) + (drop (block (result structref) (br_on_cast 0 structref (ref $t1) (table.get (i32.const 12))))) + + (drop (block (result structref) (br_on_cast 0 structref (ref $t2') (table.get (i32.const 2))))) + + (drop (block (result structref) (br_on_cast 0 structref (ref $t2) (table.get (i32.const 12))))) + + (return) + ) + (unreachable) + ) +) + +(invoke "test-sub") +(invoke "test-canon") + + +;; Cases of nullability + +(module + (type $t (struct)) + + (func (param (ref any)) (result (ref $t)) + (block (result (ref any)) (br_on_cast 1 (ref any) (ref $t) (local.get 0))) (unreachable) + ) + (func (param (ref null any)) (result (ref $t)) + (block (result (ref null any)) (br_on_cast 1 (ref null any) (ref $t) (local.get 0))) (unreachable) + ) + (func (param (ref null any)) (result (ref null $t)) + (block (result (ref null any)) (br_on_cast 1 (ref null any) (ref null $t) (local.get 0))) (unreachable) + ) +) + +(assert_invalid + (module + (type $t (struct)) + (func (param (ref any)) (result (ref $t)) + (block (result (ref any)) (br_on_cast 1 (ref null any) (ref null $t) (local.get 0))) (unreachable) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type $t (struct)) + (func (param (ref any)) (result (ref null $t)) + (block (result (ref any)) (br_on_cast 1 (ref any) (ref null $t) (local.get 0))) (unreachable) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type $t (struct)) + (func (param (ref null any)) (result (ref $t)) + (block (result (ref any)) (br_on_cast 1 (ref null any) (ref $t) (local.get 0))) (unreachable) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (result anyref) + (br_on_cast 0 eqref anyref (unreachable)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (result anyref) + (br_on_cast 0 structref arrayref (unreachable)) + ) + ) + "type mismatch" +) + + +;; https://github.com/WebAssembly/gc/issues/516 +(assert_invalid + (module + (type $t (func)) + (func $f (param (ref null $t)) (result funcref) (local.get 0)) + (func (param funcref) (result funcref funcref) + (ref.null $t) + (local.get 0) + (br_on_cast 0 funcref (ref $t)) ;; only leaves two funcref's on the stack + (drop) + (call $f) + (local.get 0) + ) + ) + "type mismatch" +) diff --git a/tests/wasm-tools/gc/br_on_cast_fail.wast b/tests/wasm-tools/gc/br_on_cast_fail.wast new file mode 100644 index 0000000..db6db11 --- /dev/null +++ b/tests/wasm-tools/gc/br_on_cast_fail.wast @@ -0,0 +1,300 @@ +;; Abstract Types + +(module + (type $ft (func (result i32))) + (type $st (struct (field i16))) + (type $at (array i8)) + + (table 10 anyref) + + (elem declare func $f) + (func $f (result i32) (i32.const 9)) + + (func (export "init") (param $x externref) + (table.set (i32.const 0) (ref.null any)) + (table.set (i32.const 1) (ref.i31 (i32.const 7))) + (table.set (i32.const 2) (struct.new $st (i32.const 6))) + (table.set (i32.const 3) (array.new $at (i32.const 5) (i32.const 3))) + (table.set (i32.const 4) (any.convert_extern (local.get $x))) + ) + + (func (export "br_on_non_null") (param $i i32) (result i32) + (block $l (result (ref any)) + (br_on_non_null $l (table.get (local.get $i))) + (return (i32.const 0)) + ) + (return (i32.const -1)) + ) + (func (export "br_on_non_i31") (param $i i32) (result i32) + (block $l (result anyref) + (br_on_cast_fail $l anyref (ref i31) (table.get (local.get $i))) + (return (i31.get_u)) + ) + (return (i32.const -1)) + ) + (func (export "br_on_non_struct") (param $i i32) (result i32) + (block $l (result anyref) + (br_on_cast_fail $l anyref (ref struct) (table.get (local.get $i))) + (block $l2 (param structref) (result (ref $st)) + (block $l3 (param structref) (result (ref $at)) + (br_on_cast $l2 structref (ref $st)) + (br_on_cast $l3 anyref (ref $at)) + (return (i32.const -2)) + ) + (return (array.get_u $at (i32.const 0))) + ) + (return (struct.get_s $st 0)) + ) + (return (i32.const -1)) + ) + (func (export "br_on_non_array") (param $i i32) (result i32) + (block $l (result anyref) + (br_on_cast_fail $l anyref (ref array) (table.get (local.get $i))) + (return (array.len)) + ) + (return (i32.const -1)) + ) + + (func (export "null-diff") (param $i i32) (result i32) + (block $l (result (ref any)) + (block (result (ref null struct)) + (br_on_cast_fail $l (ref null any) (ref null struct) (table.get (local.get $i))) + ) + (return (i32.const 1)) + ) + (return (i32.const 0)) + ) +) + +(invoke "init" (ref.extern 0)) + +(assert_return (invoke "br_on_non_null" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "br_on_non_null" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "br_on_non_null" (i32.const 2)) (i32.const -1)) +(assert_return (invoke "br_on_non_null" (i32.const 3)) (i32.const -1)) +(assert_return (invoke "br_on_non_null" (i32.const 4)) (i32.const -1)) + +(assert_return (invoke "br_on_non_i31" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "br_on_non_i31" (i32.const 1)) (i32.const 7)) +(assert_return (invoke "br_on_non_i31" (i32.const 2)) (i32.const -1)) +(assert_return (invoke "br_on_non_i31" (i32.const 3)) (i32.const -1)) +(assert_return (invoke "br_on_non_i31" (i32.const 4)) (i32.const -1)) + +(assert_return (invoke "br_on_non_struct" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "br_on_non_struct" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "br_on_non_struct" (i32.const 2)) (i32.const 6)) +(assert_return (invoke "br_on_non_struct" (i32.const 3)) (i32.const -1)) +(assert_return (invoke "br_on_non_struct" (i32.const 4)) (i32.const -1)) + +(assert_return (invoke "br_on_non_array" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "br_on_non_array" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "br_on_non_array" (i32.const 2)) (i32.const -1)) +(assert_return (invoke "br_on_non_array" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "br_on_non_array" (i32.const 4)) (i32.const -1)) + +(assert_return (invoke "null-diff" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "null-diff" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "null-diff" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "null-diff" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "null-diff" (i32.const 4)) (i32.const 0)) + + +;; Concrete Types + +(module + (type $t0 (sub (struct))) + (type $t1 (sub $t0 (struct (field i32)))) + (type $t1' (sub $t0 (struct (field i32)))) + (type $t2 (sub $t1 (struct (field i32 i32)))) + (type $t2' (sub $t1' (struct (field i32 i32)))) + (type $t3 (sub $t0 (struct (field i32 i32)))) + (type $t0' (sub $t0 (struct))) + (type $t4 (sub $t0' (struct (field i32 i32)))) + + (table 20 structref) + + (func $init + (table.set (i32.const 0) (struct.new_default $t0)) + (table.set (i32.const 10) (struct.new_default $t0)) + (table.set (i32.const 1) (struct.new_default $t1)) + (table.set (i32.const 11) (struct.new_default $t1')) + (table.set (i32.const 2) (struct.new_default $t2)) + (table.set (i32.const 12) (struct.new_default $t2')) + (table.set (i32.const 3) (struct.new_default $t3 )) + (table.set (i32.const 4) (struct.new_default $t4)) + ) + + (func (export "test-sub") + (call $init) + (block $l (result structref) + ;; must not succeed + (br_on_cast_fail $l structref (ref null $t0) (ref.null struct)) + (br_on_cast_fail $l structref (ref null $t0) (table.get (i32.const 0))) + (br_on_cast_fail $l structref (ref null $t0) (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref null $t0) (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref null $t0) (table.get (i32.const 3))) + (br_on_cast_fail $l structref (ref null $t0) (table.get (i32.const 4))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 0))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 3))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 4))) + + (br_on_cast_fail $l structref (ref null $t1) (ref.null struct)) + (br_on_cast_fail $l structref (ref null $t1) (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref null $t1) (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref $t1) (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref $t1) (table.get (i32.const 2))) + + (br_on_cast_fail $l structref (ref null $t2) (ref.null struct)) + (br_on_cast_fail $l structref (ref null $t2) (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref $t2) (table.get (i32.const 2))) + + (br_on_cast_fail $l structref (ref null $t3) (ref.null struct)) + (br_on_cast_fail $l structref (ref null $t3) (table.get (i32.const 3))) + (br_on_cast_fail $l structref (ref $t3) (table.get (i32.const 3))) + + (br_on_cast_fail $l structref (ref null $t4) (ref.null struct)) + (br_on_cast_fail $l structref (ref null $t4) (table.get (i32.const 4))) + (br_on_cast_fail $l structref (ref $t4) (table.get (i32.const 4))) + + ;; must succeed + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t0) (ref.null struct)))) + + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t1) (ref.null struct)))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t1) (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t1) (table.get (i32.const 3))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t1) (table.get (i32.const 4))))) + + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t2) (ref.null struct)))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t2) (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t2) (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t2) (table.get (i32.const 3))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t2) (table.get (i32.const 4))))) + + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t3) (ref.null struct)))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t3) (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t3) (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t3) (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t3) (table.get (i32.const 4))))) + + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t4) (ref.null struct)))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t4) (table.get (i32.const 0))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t4) (table.get (i32.const 1))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t4) (table.get (i32.const 2))))) + (drop (block (result structref) (br_on_cast_fail 0 structref (ref $t4) (table.get (i32.const 3))))) + + (return) + ) + (unreachable) + ) + + (func (export "test-canon") + (call $init) + (block $l (result structref) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 0))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 2))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 3))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 4))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 10))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 11))) + (br_on_cast_fail $l structref (ref $t0) (table.get (i32.const 12))) + + (br_on_cast_fail $l structref (ref $t1') (table.get (i32.const 1))) + (br_on_cast_fail $l structref (ref $t1') (table.get (i32.const 2))) + + (br_on_cast_fail $l structref (ref $t1) (table.get (i32.const 11))) + (br_on_cast_fail $l structref (ref $t1) (table.get (i32.const 12))) + + (br_on_cast_fail $l structref (ref $t2') (table.get (i32.const 2))) + + (br_on_cast_fail $l structref (ref $t2) (table.get (i32.const 12))) + + (return) + ) + (unreachable) + ) +) + +(invoke "test-sub") +(invoke "test-canon") + + +;; Cases of nullability + +(module + (type $t (struct)) + + (func (param (ref any)) (result (ref any)) + (block (result (ref $t)) (br_on_cast_fail 1 (ref any) (ref $t) (local.get 0))) + ) + (func (param (ref null any)) (result (ref null any)) + (block (result (ref $t)) (br_on_cast_fail 1 (ref null any) (ref $t) (local.get 0))) + ) + (func (param (ref null any)) (result (ref null any)) + (block (result (ref null $t)) (br_on_cast_fail 1 (ref null any) (ref null $t) (local.get 0))) + ) +) + +(assert_invalid + (module + (type $t (struct)) + (func (param (ref any)) (result (ref any)) + (block (result (ref $t)) (br_on_cast_fail 1 (ref null any) (ref null $t) (local.get 0))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type $t (struct)) + (func (param (ref any)) (result (ref any)) + (block (result (ref null $t)) (br_on_cast_fail 1 (ref any) (ref null $t) (local.get 0))) (ref.as_non_null) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type $t (struct)) + (func (param (ref null any)) (result (ref any)) + (block (result (ref $t)) (br_on_cast_fail 1 (ref null any) (ref $t) (local.get 0))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (result anyref) + (br_on_cast_fail 0 eqref anyref (unreachable)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (result anyref) + (br_on_cast_fail 0 structref arrayref (unreachable)) + ) + ) + "type mismatch" +) + + +;; https://github.com/WebAssembly/gc/issues/516 +(assert_invalid + (module + (type $t (func)) + (func $f (param (ref null $t)) (result funcref) (local.get 0)) + (func (param funcref) (result funcref funcref) + (ref.null $t) + (local.get 0) + (br_on_cast_fail 0 funcref (ref $t)) ;; only leaves two funcref's on the stack + (drop) + (call $f) + (local.get 0) + ) + ) + "type mismatch" +) diff --git a/tests/wasm-tools/gc/extern.wast b/tests/wasm-tools/gc/extern.wast new file mode 100644 index 0000000..abf3166 --- /dev/null +++ b/tests/wasm-tools/gc/extern.wast @@ -0,0 +1,54 @@ +(module + (type $ft (func)) + (type $st (struct)) + (type $at (array i8)) + + (table 10 anyref) + + (elem declare func $f) + (func $f) + + (func (export "init") (param $x externref) + (table.set (i32.const 0) (ref.null any)) + (table.set (i32.const 1) (ref.i31 (i32.const 7))) + (table.set (i32.const 2) (struct.new_default $st)) + (table.set (i32.const 3) (array.new_default $at (i32.const 0))) + (table.set (i32.const 4) (any.convert_extern (local.get $x))) + ) + + (func (export "internalize") (param externref) (result anyref) + (any.convert_extern (local.get 0)) + ) + (func (export "externalize") (param anyref) (result externref) + (extern.convert_any (local.get 0)) + ) + + (func (export "externalize-i") (param i32) (result externref) + (extern.convert_any (table.get (local.get 0))) + ) + (func (export "externalize-ii") (param i32) (result anyref) + (any.convert_extern (extern.convert_any (table.get (local.get 0)))) + ) +) + +(invoke "init" (ref.extern 0)) + +(assert_return (invoke "internalize" (ref.extern 1)) (ref.host 1)) +(assert_return (invoke "internalize" (ref.null extern)) (ref.null any)) + +(assert_return (invoke "externalize" (ref.host 2)) (ref.extern 2)) +(assert_return (invoke "externalize" (ref.null any)) (ref.null extern)) + +(assert_return (invoke "externalize-i" (i32.const 0)) (ref.null extern)) +(assert_return (invoke "externalize-i" (i32.const 1)) (ref.extern)) +(assert_return (invoke "externalize-i" (i32.const 2)) (ref.extern)) +(assert_return (invoke "externalize-i" (i32.const 3)) (ref.extern)) +(assert_return (invoke "externalize-i" (i32.const 4)) (ref.extern)) +(assert_return (invoke "externalize-i" (i32.const 5)) (ref.null extern)) + +(assert_return (invoke "externalize-ii" (i32.const 0)) (ref.null any)) +(assert_return (invoke "externalize-ii" (i32.const 1)) (ref.i31)) +(assert_return (invoke "externalize-ii" (i32.const 2)) (ref.struct)) +(assert_return (invoke "externalize-ii" (i32.const 3)) (ref.array)) +(assert_return (invoke "externalize-ii" (i32.const 4)) (ref.host 0)) +(assert_return (invoke "externalize-ii" (i32.const 5)) (ref.null any)) diff --git a/tests/wasm-tools/gc/i31.wast b/tests/wasm-tools/gc/i31.wast new file mode 100644 index 0000000..6309e72 --- /dev/null +++ b/tests/wasm-tools/gc/i31.wast @@ -0,0 +1,228 @@ +(module + (func (export "new") (param $i i32) (result (ref i31)) + (ref.i31 (local.get $i)) + ) + + (func (export "get_u") (param $i i32) (result i32) + (i31.get_u (ref.i31 (local.get $i))) + ) + (func (export "get_s") (param $i i32) (result i32) + (i31.get_s (ref.i31 (local.get $i))) + ) + + (func (export "get_u-null") (result i32) + (i31.get_u (ref.null i31)) + ) + (func (export "get_s-null") (result i32) + (i31.get_s (ref.null i31)) + ) + + (global $i (ref i31) (ref.i31 (i32.const 2))) + (global $m (mut (ref i31)) (ref.i31 (i32.const 3))) + + (func (export "get_globals") (result i32 i32) + (i31.get_u (global.get $i)) + (i31.get_u (global.get $m)) + ) + + (func (export "set_global") (param i32) + (global.set $m (ref.i31 (local.get 0))) + ) +) + +(assert_return (invoke "new" (i32.const 1)) (ref.i31)) + +(assert_return (invoke "get_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "get_u" (i32.const 100)) (i32.const 100)) +(assert_return (invoke "get_u" (i32.const -1)) (i32.const 0x7fff_ffff)) +(assert_return (invoke "get_u" (i32.const 0x3fff_ffff)) (i32.const 0x3fff_ffff)) +(assert_return (invoke "get_u" (i32.const 0x4000_0000)) (i32.const 0x4000_0000)) +(assert_return (invoke "get_u" (i32.const 0x7fff_ffff)) (i32.const 0x7fff_ffff)) +(assert_return (invoke "get_u" (i32.const 0xaaaa_aaaa)) (i32.const 0x2aaa_aaaa)) +(assert_return (invoke "get_u" (i32.const 0xcaaa_aaaa)) (i32.const 0x4aaa_aaaa)) + +(assert_return (invoke "get_s" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "get_s" (i32.const 100)) (i32.const 100)) +(assert_return (invoke "get_s" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "get_s" (i32.const 0x3fff_ffff)) (i32.const 0x3fff_ffff)) +(assert_return (invoke "get_s" (i32.const 0x4000_0000)) (i32.const -0x4000_0000)) +(assert_return (invoke "get_s" (i32.const 0x7fff_ffff)) (i32.const -1)) +(assert_return (invoke "get_s" (i32.const 0xaaaa_aaaa)) (i32.const 0x2aaa_aaaa)) +(assert_return (invoke "get_s" (i32.const 0xcaaa_aaaa)) (i32.const 0xcaaa_aaaa)) + +(assert_trap (invoke "get_u-null") "null i31 reference") +(assert_trap (invoke "get_s-null") "null i31 reference") + +(assert_return (invoke "get_globals") (i32.const 2) (i32.const 3)) + +(invoke "set_global" (i32.const 1234)) +(assert_return (invoke "get_globals") (i32.const 2) (i32.const 1234)) + +(module $tables_of_i31ref + (table $table 3 10 i31ref) + (elem (table $table) (i32.const 0) i31ref (item (ref.i31 (i32.const 999))) + (item (ref.i31 (i32.const 888))) + (item (ref.i31 (i32.const 777)))) + + (func (export "size") (result i32) + table.size $table + ) + + (func (export "get") (param i32) (result i32) + (i31.get_u (table.get $table (local.get 0))) + ) + + (func (export "grow") (param i32 i32) (result i32) + (table.grow $table (ref.i31 (local.get 1)) (local.get 0)) + ) + + (func (export "fill") (param i32 i32 i32) + (table.fill $table (local.get 0) (ref.i31 (local.get 1)) (local.get 2)) + ) + + (func (export "copy") (param i32 i32 i32) + (table.copy $table $table (local.get 0) (local.get 1) (local.get 2)) + ) + + (elem $elem i31ref (item (ref.i31 (i32.const 123))) + (item (ref.i31 (i32.const 456))) + (item (ref.i31 (i32.const 789)))) + (func (export "init") (param i32 i32 i32) + (table.init $table $elem (local.get 0) (local.get 1) (local.get 2)) + ) +) + +;; Initial state. +(assert_return (invoke "size") (i32.const 3)) +(assert_return (invoke "get" (i32.const 0)) (i32.const 999)) +(assert_return (invoke "get" (i32.const 1)) (i32.const 888)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 777)) + +;; Grow from size 3 to size 5. +(assert_return (invoke "grow" (i32.const 2) (i32.const 333)) (i32.const 3)) +(assert_return (invoke "size") (i32.const 5)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 333)) +(assert_return (invoke "get" (i32.const 4)) (i32.const 333)) + +;; Fill table[2..4] = 111. +(invoke "fill" (i32.const 2) (i32.const 111) (i32.const 2)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 111)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 111)) + +;; Copy from table[0..2] to table[3..5]. +(invoke "copy" (i32.const 3) (i32.const 0) (i32.const 2)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 999)) +(assert_return (invoke "get" (i32.const 4)) (i32.const 888)) + +;; Initialize the passive element at table[1..4]. +(invoke "init" (i32.const 1) (i32.const 0) (i32.const 3)) +(assert_return (invoke "get" (i32.const 1)) (i32.const 123)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 456)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 789)) + +(module $env + (global (export "g") i32 (i32.const 42)) +) +(register "env") + +(module $i31ref_of_global_table_initializer + (global $g (import "env" "g") i32) + (table $t 3 3 (ref i31) (ref.i31 (global.get $g))) + (func (export "get") (param i32) (result i32) + (i31.get_u (local.get 0) (table.get $t)) + ) +) + +(assert_return (invoke "get" (i32.const 0)) (i32.const 42)) +(assert_return (invoke "get" (i32.const 1)) (i32.const 42)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 42)) + +(module $i31ref_of_global_global_initializer + (global $g0 (import "env" "g") i32) + (global $g1 i31ref (ref.i31 (global.get $g0))) + (func (export "get") (result i32) + (i31.get_u (global.get $g1)) + ) +) + +(assert_return (invoke "get") (i32.const 42)) + +(module $anyref_global_of_i31ref + (global $c anyref (ref.i31 (i32.const 1234))) + (global $m (mut anyref) (ref.i31 (i32.const 5678))) + + (func (export "get_globals") (result i32 i32) + (i31.get_u (ref.cast i31ref (global.get $c))) + (i31.get_u (ref.cast i31ref (global.get $m))) + ) + + (func (export "set_global") (param i32) + (global.set $m (ref.i31 (local.get 0))) + ) +) + +(assert_return (invoke "get_globals") (i32.const 1234) (i32.const 5678)) +(invoke "set_global" (i32.const 0)) +(assert_return (invoke "get_globals") (i32.const 1234) (i32.const 0)) + +(module $anyref_table_of_i31ref + (table $table 3 10 anyref) + (elem (table $table) (i32.const 0) i31ref (item (ref.i31 (i32.const 999))) + (item (ref.i31 (i32.const 888))) + (item (ref.i31 (i32.const 777)))) + + (func (export "size") (result i32) + table.size $table + ) + + (func (export "get") (param i32) (result i32) + (i31.get_u (ref.cast i31ref (table.get $table (local.get 0)))) + ) + + (func (export "grow") (param i32 i32) (result i32) + (table.grow $table (ref.i31 (local.get 1)) (local.get 0)) + ) + + (func (export "fill") (param i32 i32 i32) + (table.fill $table (local.get 0) (ref.i31 (local.get 1)) (local.get 2)) + ) + + (func (export "copy") (param i32 i32 i32) + (table.copy $table $table (local.get 0) (local.get 1) (local.get 2)) + ) + + (elem $elem i31ref (item (ref.i31 (i32.const 123))) + (item (ref.i31 (i32.const 456))) + (item (ref.i31 (i32.const 789)))) + (func (export "init") (param i32 i32 i32) + (table.init $table $elem (local.get 0) (local.get 1) (local.get 2)) + ) +) + +;; Initial state. +(assert_return (invoke "size") (i32.const 3)) +(assert_return (invoke "get" (i32.const 0)) (i32.const 999)) +(assert_return (invoke "get" (i32.const 1)) (i32.const 888)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 777)) + +;; Grow from size 3 to size 5. +(assert_return (invoke "grow" (i32.const 2) (i32.const 333)) (i32.const 3)) +(assert_return (invoke "size") (i32.const 5)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 333)) +(assert_return (invoke "get" (i32.const 4)) (i32.const 333)) + +;; Fill table[2..4] = 111. +(invoke "fill" (i32.const 2) (i32.const 111) (i32.const 2)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 111)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 111)) + +;; Copy from table[0..2] to table[3..5]. +(invoke "copy" (i32.const 3) (i32.const 0) (i32.const 2)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 999)) +(assert_return (invoke "get" (i32.const 4)) (i32.const 888)) + +;; Initialize the passive element at table[1..4]. +(invoke "init" (i32.const 1) (i32.const 0) (i32.const 3)) +(assert_return (invoke "get" (i32.const 1)) (i32.const 123)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 456)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 789)) diff --git a/tests/wasm-tools/gc/ref_cast.wast b/tests/wasm-tools/gc/ref_cast.wast new file mode 100644 index 0000000..8e35431 --- /dev/null +++ b/tests/wasm-tools/gc/ref_cast.wast @@ -0,0 +1,186 @@ +;; Abstract Types + +(module + (type $ft (func)) + (type $st (struct)) + (type $at (array i8)) + + (table 10 anyref) + + (elem declare func $f) + (func $f) + + (func (export "init") (param $x externref) + (table.set (i32.const 0) (ref.null any)) + (table.set (i32.const 1) (ref.i31 (i32.const 7))) + (table.set (i32.const 2) (struct.new_default $st)) + (table.set (i32.const 3) (array.new_default $at (i32.const 0))) + (table.set (i32.const 4) (any.convert_extern (local.get $x))) + (table.set (i32.const 5) (ref.null i31)) + (table.set (i32.const 6) (ref.null struct)) + (table.set (i32.const 7) (ref.null none)) + ) + + (func (export "ref_cast_non_null") (param $i i32) + (drop (ref.as_non_null (table.get (local.get $i)))) + (drop (ref.cast (ref null any) (table.get (local.get $i)))) + ) + (func (export "ref_cast_null") (param $i i32) + (drop (ref.cast anyref (table.get (local.get $i)))) + (drop (ref.cast structref (table.get (local.get $i)))) + (drop (ref.cast arrayref (table.get (local.get $i)))) + (drop (ref.cast i31ref (table.get (local.get $i)))) + (drop (ref.cast nullref (table.get (local.get $i)))) + ) + (func (export "ref_cast_i31") (param $i i32) + (drop (ref.cast (ref i31) (table.get (local.get $i)))) + (drop (ref.cast i31ref (table.get (local.get $i)))) + ) + (func (export "ref_cast_struct") (param $i i32) + (drop (ref.cast (ref struct) (table.get (local.get $i)))) + (drop (ref.cast structref (table.get (local.get $i)))) + ) + (func (export "ref_cast_array") (param $i i32) + (drop (ref.cast (ref array) (table.get (local.get $i)))) + (drop (ref.cast arrayref (table.get (local.get $i)))) + ) +) + +(invoke "init" (ref.extern 0)) + +(assert_trap (invoke "ref_cast_non_null" (i32.const 0)) "null reference") +(assert_return (invoke "ref_cast_non_null" (i32.const 1))) +(assert_return (invoke "ref_cast_non_null" (i32.const 2))) +(assert_return (invoke "ref_cast_non_null" (i32.const 3))) +(assert_return (invoke "ref_cast_non_null" (i32.const 4))) +(assert_trap (invoke "ref_cast_non_null" (i32.const 5)) "null reference") +(assert_trap (invoke "ref_cast_non_null" (i32.const 6)) "null reference") +(assert_trap (invoke "ref_cast_non_null" (i32.const 7)) "null reference") + +(assert_return (invoke "ref_cast_null" (i32.const 0))) +(assert_trap (invoke "ref_cast_null" (i32.const 1)) "cast failure") +(assert_trap (invoke "ref_cast_null" (i32.const 2)) "cast failure") +(assert_trap (invoke "ref_cast_null" (i32.const 3)) "cast failure") +(assert_trap (invoke "ref_cast_null" (i32.const 4)) "cast failure") +(assert_return (invoke "ref_cast_null" (i32.const 5))) +(assert_return (invoke "ref_cast_null" (i32.const 6))) +(assert_return (invoke "ref_cast_null" (i32.const 7))) + +(assert_trap (invoke "ref_cast_i31" (i32.const 0)) "cast failure") +(assert_return (invoke "ref_cast_i31" (i32.const 1))) +(assert_trap (invoke "ref_cast_i31" (i32.const 2)) "cast failure") +(assert_trap (invoke "ref_cast_i31" (i32.const 3)) "cast failure") +(assert_trap (invoke "ref_cast_i31" (i32.const 4)) "cast failure") +(assert_trap (invoke "ref_cast_i31" (i32.const 5)) "cast failure") +(assert_trap (invoke "ref_cast_i31" (i32.const 6)) "cast failure") +(assert_trap (invoke "ref_cast_i31" (i32.const 7)) "cast failure") + +(assert_trap (invoke "ref_cast_struct" (i32.const 0)) "cast failure") +(assert_trap (invoke "ref_cast_struct" (i32.const 1)) "cast failure") +(assert_return (invoke "ref_cast_struct" (i32.const 2))) +(assert_trap (invoke "ref_cast_struct" (i32.const 3)) "cast failure") +(assert_trap (invoke "ref_cast_struct" (i32.const 4)) "cast failure") +(assert_trap (invoke "ref_cast_struct" (i32.const 5)) "cast failure") +(assert_trap (invoke "ref_cast_struct" (i32.const 6)) "cast failure") +(assert_trap (invoke "ref_cast_struct" (i32.const 7)) "cast failure") + +(assert_trap (invoke "ref_cast_array" (i32.const 0)) "cast failure") +(assert_trap (invoke "ref_cast_array" (i32.const 1)) "cast failure") +(assert_trap (invoke "ref_cast_array" (i32.const 2)) "cast failure") +(assert_return (invoke "ref_cast_array" (i32.const 3))) +(assert_trap (invoke "ref_cast_array" (i32.const 4)) "cast failure") +(assert_trap (invoke "ref_cast_array" (i32.const 5)) "cast failure") +(assert_trap (invoke "ref_cast_array" (i32.const 6)) "cast failure") +(assert_trap (invoke "ref_cast_array" (i32.const 7)) "cast failure") + + +;; Concrete Types + +(module + (type $t0 (sub (struct))) + (type $t1 (sub $t0 (struct (field i32)))) + (type $t1' (sub $t0 (struct (field i32)))) + (type $t2 (sub $t1 (struct (field i32 i32)))) + (type $t2' (sub $t1' (struct (field i32 i32)))) + (type $t3 (sub $t0 (struct (field i32 i32)))) + (type $t0' (sub $t0 (struct))) + (type $t4 (sub $t0' (struct (field i32 i32)))) + + (table 20 (ref null struct)) + + (func $init + (table.set (i32.const 0) (struct.new_default $t0)) + (table.set (i32.const 10) (struct.new_default $t0)) + (table.set (i32.const 1) (struct.new_default $t1)) + (table.set (i32.const 11) (struct.new_default $t1')) + (table.set (i32.const 2) (struct.new_default $t2)) + (table.set (i32.const 12) (struct.new_default $t2')) + (table.set (i32.const 3) (struct.new_default $t3)) + (table.set (i32.const 4) (struct.new_default $t4)) + ) + + (func (export "test-sub") + (call $init) + + (drop (ref.cast (ref null $t0) (ref.null struct))) + (drop (ref.cast (ref null $t0) (table.get (i32.const 0)))) + (drop (ref.cast (ref null $t0) (table.get (i32.const 1)))) + (drop (ref.cast (ref null $t0) (table.get (i32.const 2)))) + (drop (ref.cast (ref null $t0) (table.get (i32.const 3)))) + (drop (ref.cast (ref null $t0) (table.get (i32.const 4)))) + + (drop (ref.cast (ref null $t0) (ref.null struct))) + (drop (ref.cast (ref null $t1) (table.get (i32.const 1)))) + (drop (ref.cast (ref null $t1) (table.get (i32.const 2)))) + + (drop (ref.cast (ref null $t0) (ref.null struct))) + (drop (ref.cast (ref null $t2) (table.get (i32.const 2)))) + + (drop (ref.cast (ref null $t0) (ref.null struct))) + (drop (ref.cast (ref null $t3) (table.get (i32.const 3)))) + + (drop (ref.cast (ref null $t4) (table.get (i32.const 4)))) + + (drop (ref.cast (ref $t0) (table.get (i32.const 0)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 1)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 2)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 3)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 4)))) + + (drop (ref.cast (ref $t1) (table.get (i32.const 1)))) + (drop (ref.cast (ref $t1) (table.get (i32.const 2)))) + + (drop (ref.cast (ref $t2) (table.get (i32.const 2)))) + + (drop (ref.cast (ref $t3) (table.get (i32.const 3)))) + + (drop (ref.cast (ref $t4) (table.get (i32.const 4)))) + ) + + (func (export "test-canon") + (call $init) + + (drop (ref.cast (ref $t0) (table.get (i32.const 0)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 1)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 2)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 3)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 4)))) + + (drop (ref.cast (ref $t0) (table.get (i32.const 10)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 11)))) + (drop (ref.cast (ref $t0) (table.get (i32.const 12)))) + + (drop (ref.cast (ref $t1') (table.get (i32.const 1)))) + (drop (ref.cast (ref $t1') (table.get (i32.const 2)))) + + (drop (ref.cast (ref $t1) (table.get (i32.const 11)))) + (drop (ref.cast (ref $t1) (table.get (i32.const 12)))) + + (drop (ref.cast (ref $t2') (table.get (i32.const 2)))) + + (drop (ref.cast (ref $t2) (table.get (i32.const 12)))) + ) +) + +(invoke "test-sub") +(invoke "test-canon") diff --git a/tests/wasm-tools/gc/ref_eq.wast b/tests/wasm-tools/gc/ref_eq.wast new file mode 100644 index 0000000..001efd6 --- /dev/null +++ b/tests/wasm-tools/gc/ref_eq.wast @@ -0,0 +1,168 @@ +(module + (type $st (sub (struct))) + (type $st' (sub (struct (field i32)))) + (type $at (array i8)) + (type $st-sub1 (sub $st (struct))) + (type $st-sub2 (sub $st (struct))) + (type $st'-sub1 (sub $st' (struct (field i32)))) + (type $st'-sub2 (sub $st' (struct (field i32)))) + + (table 20 (ref null eq)) + + (func (export "init") + (table.set (i32.const 0) (ref.null eq)) + (table.set (i32.const 1) (ref.null i31)) + (table.set (i32.const 2) (ref.i31 (i32.const 7))) + (table.set (i32.const 3) (ref.i31 (i32.const 7))) + (table.set (i32.const 4) (ref.i31 (i32.const 8))) + (table.set (i32.const 5) (struct.new_default $st)) + (table.set (i32.const 6) (struct.new_default $st)) + (table.set (i32.const 7) (array.new_default $at (i32.const 0))) + (table.set (i32.const 8) (array.new_default $at (i32.const 0))) + ) + + (func (export "eq") (param $i i32) (param $j i32) (result i32) + (ref.eq (table.get (local.get $i)) (table.get (local.get $j))) + ) +) + +(invoke "init") + +(assert_return (invoke "eq" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 2) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 3)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 3) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 3)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 4) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 4)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 5) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 5)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 6) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 6)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 7) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 7)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 8) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 8)) (i32.const 1)) + +(assert_invalid + (module + (func (export "eq") (param $r (ref any)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref null any)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref func)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref null func)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref extern)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref null extern)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) diff --git a/tests/wasm-tools/gc/ref_test.wast b/tests/wasm-tools/gc/ref_test.wast new file mode 100644 index 0000000..590b81b --- /dev/null +++ b/tests/wasm-tools/gc/ref_test.wast @@ -0,0 +1,330 @@ +;; Abstract Types + +(module + (type $ft (func)) + (type $st (struct)) + (type $at (array i8)) + + (table $ta 10 anyref) + (table $tf 10 funcref) + (table $te 10 externref) + + (elem declare func $f) + (func $f) + + (func (export "init") (param $x externref) + (table.set $ta (i32.const 0) (ref.null any)) + (table.set $ta (i32.const 1) (ref.null struct)) + (table.set $ta (i32.const 2) (ref.null none)) + (table.set $ta (i32.const 3) (ref.i31 (i32.const 7))) + (table.set $ta (i32.const 4) (struct.new_default $st)) + (table.set $ta (i32.const 5) (array.new_default $at (i32.const 0))) + (table.set $ta (i32.const 6) (any.convert_extern (local.get $x))) + (table.set $ta (i32.const 7) (any.convert_extern (ref.null extern))) + + (table.set $tf (i32.const 0) (ref.null nofunc)) + (table.set $tf (i32.const 1) (ref.null func)) + (table.set $tf (i32.const 2) (ref.func $f)) + + (table.set $te (i32.const 0) (ref.null noextern)) + (table.set $te (i32.const 1) (ref.null extern)) + (table.set $te (i32.const 2) (local.get $x)) + (table.set $te (i32.const 3) (extern.convert_any (ref.i31 (i32.const 8)))) + (table.set $te (i32.const 4) (extern.convert_any (struct.new_default $st))) + (table.set $te (i32.const 5) (extern.convert_any (ref.null any))) + ) + + (func (export "ref_test_null_data") (param $i i32) (result i32) + (i32.add + (ref.is_null (table.get $ta (local.get $i))) + (ref.test nullref (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_any") (param $i i32) (result i32) + (i32.add + (ref.test (ref any) (table.get $ta (local.get $i))) + (ref.test anyref (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_eq") (param $i i32) (result i32) + (i32.add + (ref.test (ref eq) (table.get $ta (local.get $i))) + (ref.test eqref (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_i31") (param $i i32) (result i32) + (i32.add + (ref.test (ref i31) (table.get $ta (local.get $i))) + (ref.test i31ref (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_struct") (param $i i32) (result i32) + (i32.add + (ref.test (ref struct) (table.get $ta (local.get $i))) + (ref.test structref (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_array") (param $i i32) (result i32) + (i32.add + (ref.test (ref array) (table.get $ta (local.get $i))) + (ref.test arrayref (table.get $ta (local.get $i))) + ) + ) + + (func (export "ref_test_null_func") (param $i i32) (result i32) + (i32.add + (ref.is_null (table.get $tf (local.get $i))) + (ref.test (ref null nofunc) (table.get $tf (local.get $i))) + ) + ) + (func (export "ref_test_func") (param $i i32) (result i32) + (i32.add + (ref.test (ref func) (table.get $tf (local.get $i))) + (ref.test funcref (table.get $tf (local.get $i))) + ) + ) + + (func (export "ref_test_null_extern") (param $i i32) (result i32) + (i32.add + (ref.is_null (table.get $te (local.get $i))) + (ref.test (ref null noextern) (table.get $te (local.get $i))) + ) + ) + (func (export "ref_test_extern") (param $i i32) (result i32) + (i32.add + (ref.test (ref extern) (table.get $te (local.get $i))) + (ref.test externref (table.get $te (local.get $i))) + ) + ) +) + +(invoke "init" (ref.extern 0)) + +(assert_return (invoke "ref_test_null_data" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "ref_test_null_data" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "ref_test_null_data" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "ref_test_null_data" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "ref_test_null_data" (i32.const 4)) (i32.const 0)) +(assert_return (invoke "ref_test_null_data" (i32.const 5)) (i32.const 0)) +(assert_return (invoke "ref_test_null_data" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "ref_test_null_data" (i32.const 7)) (i32.const 2)) + +(assert_return (invoke "ref_test_any" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_any" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_any" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "ref_test_any" (i32.const 3)) (i32.const 2)) +(assert_return (invoke "ref_test_any" (i32.const 4)) (i32.const 2)) +(assert_return (invoke "ref_test_any" (i32.const 5)) (i32.const 2)) +(assert_return (invoke "ref_test_any" (i32.const 6)) (i32.const 2)) +(assert_return (invoke "ref_test_any" (i32.const 7)) (i32.const 1)) + +(assert_return (invoke "ref_test_eq" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_eq" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_eq" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "ref_test_eq" (i32.const 3)) (i32.const 2)) +(assert_return (invoke "ref_test_eq" (i32.const 4)) (i32.const 2)) +(assert_return (invoke "ref_test_eq" (i32.const 5)) (i32.const 2)) +(assert_return (invoke "ref_test_eq" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "ref_test_eq" (i32.const 7)) (i32.const 1)) + +(assert_return (invoke "ref_test_i31" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_i31" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_i31" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "ref_test_i31" (i32.const 3)) (i32.const 2)) +(assert_return (invoke "ref_test_i31" (i32.const 4)) (i32.const 0)) +(assert_return (invoke "ref_test_i31" (i32.const 5)) (i32.const 0)) +(assert_return (invoke "ref_test_i31" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "ref_test_i31" (i32.const 7)) (i32.const 1)) + +(assert_return (invoke "ref_test_struct" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_struct" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_struct" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "ref_test_struct" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "ref_test_struct" (i32.const 4)) (i32.const 2)) +(assert_return (invoke "ref_test_struct" (i32.const 5)) (i32.const 0)) +(assert_return (invoke "ref_test_struct" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "ref_test_struct" (i32.const 7)) (i32.const 1)) + +(assert_return (invoke "ref_test_array" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_array" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_array" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "ref_test_array" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "ref_test_array" (i32.const 4)) (i32.const 0)) +(assert_return (invoke "ref_test_array" (i32.const 5)) (i32.const 2)) +(assert_return (invoke "ref_test_array" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "ref_test_array" (i32.const 7)) (i32.const 1)) + +(assert_return (invoke "ref_test_null_func" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "ref_test_null_func" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "ref_test_null_func" (i32.const 2)) (i32.const 0)) + +(assert_return (invoke "ref_test_func" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_func" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_func" (i32.const 2)) (i32.const 2)) + +(assert_return (invoke "ref_test_null_extern" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "ref_test_null_extern" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "ref_test_null_extern" (i32.const 2)) (i32.const 0)) +(assert_return (invoke "ref_test_null_extern" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "ref_test_null_extern" (i32.const 4)) (i32.const 0)) +(assert_return (invoke "ref_test_null_extern" (i32.const 5)) (i32.const 2)) + +(assert_return (invoke "ref_test_extern" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_extern" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_extern" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "ref_test_extern" (i32.const 3)) (i32.const 2)) +(assert_return (invoke "ref_test_extern" (i32.const 4)) (i32.const 2)) +(assert_return (invoke "ref_test_extern" (i32.const 5)) (i32.const 1)) + + +;; Concrete Types + +(module + (type $t0 (sub (struct))) + (type $t1 (sub $t0 (struct (field i32)))) + (type $t1' (sub $t0 (struct (field i32)))) + (type $t2 (sub $t1 (struct (field i32 i32)))) + (type $t2' (sub $t1' (struct (field i32 i32)))) + (type $t3 (sub $t0 (struct (field i32 i32)))) + (type $t0' (sub $t0 (struct))) + (type $t4 (sub $t0' (struct (field i32 i32)))) + + (table 20 (ref null struct)) + + (func $init + (table.set (i32.const 0) (struct.new_default $t0)) + (table.set (i32.const 10) (struct.new_default $t0)) + (table.set (i32.const 1) (struct.new_default $t1)) + (table.set (i32.const 11) (struct.new_default $t1')) + (table.set (i32.const 2) (struct.new_default $t2)) + (table.set (i32.const 12) (struct.new_default $t2')) + (table.set (i32.const 3) (struct.new_default $t3)) + (table.set (i32.const 4) (struct.new_default $t4)) + ) + + (func (export "test-sub") + (call $init) + (block $l + ;; must hold + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 0))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 2))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 3))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (table.get (i32.const 3))))) + + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (table.get (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 0))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 2))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 3))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref $t3) (table.get (i32.const 3))))) + + (br_if $l (i32.eqz (ref.test (ref $t4) (table.get (i32.const 4))))) + + ;; must not hold + (br_if $l (ref.test (ref $t0) (ref.null struct))) + (br_if $l (ref.test (ref $t1) (ref.null struct))) + (br_if $l (ref.test (ref $t2) (ref.null struct))) + (br_if $l (ref.test (ref $t3) (ref.null struct))) + (br_if $l (ref.test (ref $t4) (ref.null struct))) + + (br_if $l (ref.test (ref $t1) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t1) (table.get (i32.const 3)))) + (br_if $l (ref.test (ref $t1) (table.get (i32.const 4)))) + + (br_if $l (ref.test (ref $t2) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t2) (table.get (i32.const 1)))) + (br_if $l (ref.test (ref $t2) (table.get (i32.const 3)))) + (br_if $l (ref.test (ref $t2) (table.get (i32.const 4)))) + + (br_if $l (ref.test (ref $t3) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t3) (table.get (i32.const 1)))) + (br_if $l (ref.test (ref $t3) (table.get (i32.const 2)))) + (br_if $l (ref.test (ref $t3) (table.get (i32.const 4)))) + + (br_if $l (ref.test (ref $t4) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t4) (table.get (i32.const 1)))) + (br_if $l (ref.test (ref $t4) (table.get (i32.const 2)))) + (br_if $l (ref.test (ref $t4) (table.get (i32.const 3)))) + + (return) + ) + (unreachable) + ) + + (func (export "test-canon") + (call $init) + (block $l + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 0))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 2))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 3))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 10))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 11))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 12))))) + + (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 11))))) + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 12))))) + + (br_if $l (i32.eqz (ref.test (ref $t2') (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 12))))) + + (return) + ) + (unreachable) + ) +) + +(assert_return (invoke "test-sub")) +(assert_return (invoke "test-canon")) diff --git a/tests/wasm-tools/gc/struct.wast b/tests/wasm-tools/gc/struct.wast new file mode 100644 index 0000000..6151fe1 --- /dev/null +++ b/tests/wasm-tools/gc/struct.wast @@ -0,0 +1,229 @@ +;; Type syntax + +(module + (type (struct)) + (type (struct (field))) + (type (struct (field i8))) + (type (struct (field i8 i8 i8 i8))) + (type (struct (field $x1 i32) (field $y1 i32))) + (type (struct (field i8 i16 i32 i64 f32 f64 anyref funcref (ref 0) (ref null 1)))) + (type (struct (field i32 i64 i8) (field) (field) (field (ref null i31) anyref))) + (type (struct (field $x2 i32) (field f32 f64) (field $y2 i32))) +) + + +(assert_malformed + (module quote + "(type (struct (field $x i32) (field $x i32)))" + ) + "duplicate field" +) + + +;; Binding structure + +(module + (rec + (type $s0 (struct (field (ref 0) (ref 1) (ref $s0) (ref $s1)))) + (type $s1 (struct (field (ref 0) (ref 1) (ref $s0) (ref $s1)))) + ) + + (func (param (ref $forward))) + + (type $forward (struct)) +) + +(assert_invalid + (module (type (struct (field (ref 1))))) + "unknown type" +) +(assert_invalid + (module (type (struct (field (mut (ref 1)))))) + "unknown type" +) + + +;; Field names + +(module + (type (struct (field $x i32))) + (type $t1 (struct (field i32) (field $x f32))) + (type $t2 (struct (field i32 i32) (field $x i64))) + + (func (param (ref 0)) (result i32) (struct.get 0 $x (local.get 0))) + (func (param (ref $t1)) (result f32) (struct.get 1 $x (local.get 0))) + (func (param (ref $t2)) (result i64) (struct.get $t2 $x (local.get 0))) +) + +(assert_invalid + (module + (type (struct (field $x i64))) + (type $t (struct (field $x i32))) + (func (param (ref 0)) (result i32) (struct.get 0 $x (local.get 0))) + ) + "type mismatch" +) + + +;; Basic instructions + +(module + (type $vec (struct (field f32) (field $y (mut f32)) (field $z f32))) + + (global (ref $vec) (struct.new $vec (f32.const 1) (f32.const 2) (f32.const 3))) + (global (ref $vec) (struct.new_default $vec)) + + (func (export "new") (result anyref) + (struct.new_default $vec) + ) + + (func $get_0_0 (param $v (ref $vec)) (result f32) + (struct.get 0 0 (local.get $v)) + ) + (func (export "get_0_0") (result f32) + (call $get_0_0 (struct.new_default $vec)) + ) + (func $get_vec_0 (param $v (ref $vec)) (result f32) + (struct.get $vec 0 (local.get $v)) + ) + (func (export "get_vec_0") (result f32) + (call $get_vec_0 (struct.new_default $vec)) + ) + (func $get_0_y (param $v (ref $vec)) (result f32) + (struct.get 0 $y (local.get $v)) + ) + (func (export "get_0_y") (result f32) + (call $get_0_y (struct.new_default $vec)) + ) + (func $get_vec_y (param $v (ref $vec)) (result f32) + (struct.get $vec $y (local.get $v)) + ) + (func (export "get_vec_y") (result f32) + (call $get_vec_y (struct.new_default $vec)) + ) + + (func $set_get_y (param $v (ref $vec)) (param $y f32) (result f32) + (struct.set $vec $y (local.get $v) (local.get $y)) + (struct.get $vec $y (local.get $v)) + ) + (func (export "set_get_y") (param $y f32) (result f32) + (call $set_get_y (struct.new_default $vec) (local.get $y)) + ) + + (func $set_get_1 (param $v (ref $vec)) (param $y f32) (result f32) + (struct.set $vec 1 (local.get $v) (local.get $y)) + (struct.get $vec $y (local.get $v)) + ) + (func (export "set_get_1") (param $y f32) (result f32) + (call $set_get_1 (struct.new_default $vec) (local.get $y)) + ) +) + +(assert_return (invoke "new") (ref.struct)) + +(assert_return (invoke "get_0_0") (f32.const 0)) +(assert_return (invoke "get_vec_0") (f32.const 0)) +(assert_return (invoke "get_0_y") (f32.const 0)) +(assert_return (invoke "get_vec_y") (f32.const 0)) + +(assert_return (invoke "set_get_y" (f32.const 7)) (f32.const 7)) +(assert_return (invoke "set_get_1" (f32.const 7)) (f32.const 7)) + +(assert_invalid + (module + (type $s (struct (field i64))) + (func (export "struct.set-immutable") (param $s (ref $s)) + (struct.set $s 0 (local.get $s) (i64.const 1)) + ) + ) + "field is immutable" +) + + +;; Null dereference + +(module + (type $t (struct (field i32 (mut i32)))) + (func (export "struct.get-null") + (local (ref null $t)) (drop (struct.get $t 1 (local.get 0))) + ) + (func (export "struct.set-null") + (local (ref null $t)) (struct.set $t 1 (local.get 0) (i32.const 0)) + ) +) + +(assert_trap (invoke "struct.get-null") "null structure reference") +(assert_trap (invoke "struct.set-null") "null structure reference") + +;; Packed field instructions + +(module + (type $s (struct (field i8) (field (mut i8)) (field i16) (field (mut i16)))) + + (global (export "g0") (ref $s) (struct.new $s (i32.const 0) (i32.const 1) (i32.const 2) (i32.const 3))) + (global (export "g1") (ref $s) (struct.new $s (i32.const 254) (i32.const 255) (i32.const 65534) (i32.const 65535))) + + (func (export "get_packed_g0_0") (result i32 i32) + (struct.get_s 0 0 (global.get 0)) + (struct.get_u 0 0 (global.get 0)) + ) + + (func (export "get_packed_g1_0") (result i32 i32) + (struct.get_s 0 0 (global.get 1)) + (struct.get_u 0 0 (global.get 1)) + ) + + (func (export "get_packed_g0_1") (result i32 i32) + (struct.get_s 0 1 (global.get 0)) + (struct.get_u 0 1 (global.get 0)) + ) + + (func (export "get_packed_g1_1") (result i32 i32) + (struct.get_s 0 1 (global.get 1)) + (struct.get_u 0 1 (global.get 1)) + ) + + (func (export "get_packed_g0_2") (result i32 i32) + (struct.get_s 0 2 (global.get 0)) + (struct.get_u 0 2 (global.get 0)) + ) + + (func (export "get_packed_g1_2") (result i32 i32) + (struct.get_s 0 2 (global.get 1)) + (struct.get_u 0 2 (global.get 1)) + ) + + (func (export "get_packed_g0_3") (result i32 i32) + (struct.get_s 0 3 (global.get 0)) + (struct.get_u 0 3 (global.get 0)) + ) + + (func (export "get_packed_g1_3") (result i32 i32) + (struct.get_s 0 3 (global.get 1)) + (struct.get_u 0 3 (global.get 1)) + ) + + (func (export "set_get_packed_g0_1") (param i32) (result i32 i32) + (struct.set 0 1 (global.get 0) (local.get 0)) + (struct.get_s 0 1 (global.get 0)) + (struct.get_u 0 1 (global.get 0)) + ) + + (func (export "set_get_packed_g0_3") (param i32) (result i32 i32) + (struct.set 0 3 (global.get 0) (local.get 0)) + (struct.get_s 0 3 (global.get 0)) + (struct.get_u 0 3 (global.get 0)) + ) +) + +(assert_return (invoke "get_packed_g0_0") (i32.const 0) (i32.const 0)) +(assert_return (invoke "get_packed_g1_0") (i32.const -2) (i32.const 254)) +(assert_return (invoke "get_packed_g0_1") (i32.const 1) (i32.const 1)) +(assert_return (invoke "get_packed_g1_1") (i32.const -1) (i32.const 255)) +(assert_return (invoke "get_packed_g0_2") (i32.const 2) (i32.const 2)) +(assert_return (invoke "get_packed_g1_2") (i32.const -2) (i32.const 65534)) +(assert_return (invoke "get_packed_g0_3") (i32.const 3) (i32.const 3)) +(assert_return (invoke "get_packed_g1_3") (i32.const -1) (i32.const 65535)) + +(assert_return (invoke "set_get_packed_g0_1" (i32.const 257)) (i32.const 1) (i32.const 1)) +(assert_return (invoke "set_get_packed_g0_3" (i32.const 257)) (i32.const 257) (i32.const 257)) diff --git a/tests/wasm-tools/gc/type-subtyping.wast b/tests/wasm-tools/gc/type-subtyping.wast new file mode 100644 index 0000000..f2b33d7 --- /dev/null +++ b/tests/wasm-tools/gc/type-subtyping.wast @@ -0,0 +1,833 @@ +;; Definitions + +(module + (type $e0 (sub (array i32))) + (type $e1 (sub $e0 (array i32))) + + (type $e2 (sub (array anyref))) + (type $e3 (sub (array (ref null $e0)))) + (type $e4 (sub (array (ref $e1)))) + + (type $m1 (sub (array (mut i32)))) + (type $m2 (sub $m1 (array (mut i32)))) +) + +(module + (type $e0 (sub (struct))) + (type $e1 (sub $e0 (struct))) + (type $e2 (sub $e1 (struct (field i32)))) + (type $e3 (sub $e2 (struct (field i32 (ref null $e0))))) + (type $e4 (sub $e3 (struct (field i32 (ref $e0) (mut i64))))) + (type $e5 (sub $e4 (struct (field i32 (ref $e1) (mut i64))))) +) + +(module + (type $s (sub (struct))) + (type $s' (sub $s (struct))) + + (type $f1 (sub (func (param (ref $s')) (result anyref)))) + (type $f2 (sub $f1 (func (param (ref $s)) (result (ref any))))) + (type $f3 (sub $f2 (func (param (ref null $s)) (result (ref $s))))) + (type $f4 (sub $f3 (func (param (ref null struct)) (result (ref $s'))))) +) + + +;; Recursive definitions + +(module + (type $t (sub (struct (field anyref)))) + (rec (type $r (sub $t (struct (field (ref $r)))))) + (type $t' (sub $r (struct (field (ref $r) i32)))) +) + +(module + (rec + (type $r1 (sub (struct (field i32 (ref $r1))))) + ) + (rec + (type $r2 (sub $r1 (struct (field i32 (ref $r3))))) + (type $r3 (sub $r1 (struct (field i32 (ref $r2))))) + ) +) + +(module + (rec + (type $a1 (sub (struct (field i32 (ref $a2))))) + (type $a2 (sub (struct (field i64 (ref $a1))))) + ) + (rec + (type $b1 (sub $a2 (struct (field i64 (ref $a1) i32)))) + (type $b2 (sub $a1 (struct (field i32 (ref $a2) i32)))) + (type $b3 (sub $a2 (struct (field i64 (ref $b2) i32)))) + ) +) + + +;; Subsumption + +(module + (rec + (type $t1 (sub (func (param i32 (ref $t3))))) + (type $t2 (sub $t1 (func (param i32 (ref $t2))))) + (type $t3 (sub $t2 (func (param i32 (ref $t1))))) + ) + + (func $f1 (param $r (ref $t1)) + (call $f1 (local.get $r)) + ) + (func $f2 (param $r (ref $t2)) + (call $f1 (local.get $r)) + (call $f2 (local.get $r)) + ) + (func $f3 (param $r (ref $t3)) + (call $f1 (local.get $r)) + (call $f2 (local.get $r)) + (call $f3 (local.get $r)) + ) +) + +(module + (rec + (type $t1 (sub (func (result i32 (ref $u1))))) + (type $u1 (sub (func (result f32 (ref $t1))))) + ) + + (rec + (type $t2 (sub $t1 (func (result i32 (ref $u3))))) + (type $u2 (sub $u1 (func (result f32 (ref $t3))))) + (type $t3 (sub $t1 (func (result i32 (ref $u2))))) + (type $u3 (sub $u1 (func (result f32 (ref $t2))))) + ) + + (func $f1 (param $r (ref $t1)) + (call $f1 (local.get $r)) + ) + (func $f2 (param $r (ref $t2)) + (call $f1 (local.get $r)) + (call $f2 (local.get $r)) + ) + (func $f3 (param $r (ref $t3)) + (call $f1 (local.get $r)) + (call $f3 (local.get $r)) + ) +) + +(module + (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $f2 (sub (func))) (type (struct (field (ref $f2))))) + (rec (type $g1 (sub $f1 (func))) (type (struct))) + (rec (type $g2 (sub $f2 (func))) (type (struct))) + (func $g (type $g2)) + (global (ref $g1) (ref.func $g)) +) + +(module + (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) + (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) + (rec + (type $g1 (sub $f1 (func))) + (type (sub $s1 (struct (field (ref $f1) (ref $f1) (ref $f2) (ref $f2) (ref $g1))))) + ) + (rec + (type $g2 (sub $f2 (func))) + (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2))))) + ) + (func $g (type $g2)) + (global (ref $g1) (ref.func $g)) +) + +(assert_invalid + (module + (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $f2 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $g1 (sub $f1 (func))) (type (struct))) + (rec (type $g2 (sub $f2 (func))) (type (struct))) + (func $g (type $g2)) + (global (ref $g1) (ref.func $g)) + ) + "type mismatch" +) + +(module + (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $f2 (sub (func))) (type (struct (field (ref $f2))))) + (rec (type $g (sub $f1 (func))) (type (struct))) + (func $g (type $g)) + (global (ref $f1) (ref.func $g)) +) + +(module + (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) + (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) + (rec + (type $g1 (sub $f1 (func))) + (type (sub $s1 (struct (field (ref $f1) (ref $f1) (ref $f2) (ref $f2) (ref $g1))))) + ) + (rec + (type $g2 (sub $f2 (func))) + (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2))))) + ) + (rec (type $h (sub $g2 (func))) (type (struct))) + (func $h (type $h)) + (global (ref $f1) (ref.func $h)) + (global (ref $g1) (ref.func $h)) +) + + +(module + (rec (type $f11 (sub (func (result (ref func))))) (type $f12 (sub $f11 (func (result (ref $f11)))))) + (rec (type $f21 (sub (func (result (ref func))))) (type $f22 (sub $f21 (func (result (ref $f21)))))) + (func $f11 (type $f11) (unreachable)) + (func $f12 (type $f12) (unreachable)) + (global (ref $f11) (ref.func $f11)) + (global (ref $f21) (ref.func $f11)) + (global (ref $f12) (ref.func $f12)) + (global (ref $f22) (ref.func $f12)) +) + +(module + (rec (type $f11 (sub (func (result (ref func))))) (type $f12 (sub $f11 (func (result (ref $f11)))))) + (rec (type $f21 (sub (func (result (ref func))))) (type $f22 (sub $f21 (func (result (ref $f21)))))) + (rec (type $g11 (sub $f11 (func (result (ref func))))) (type $g12 (sub $g11 (func (result (ref $g11)))))) + (rec (type $g21 (sub $f21 (func (result (ref func))))) (type $g22 (sub $g21 (func (result (ref $g21)))))) + (func $g11 (type $g11) (unreachable)) + (func $g12 (type $g12) (unreachable)) + (global (ref $f11) (ref.func $g11)) + (global (ref $f21) (ref.func $g11)) + (global (ref $f11) (ref.func $g12)) + (global (ref $f21) (ref.func $g12)) + (global (ref $g11) (ref.func $g11)) + (global (ref $g21) (ref.func $g11)) + (global (ref $g12) (ref.func $g12)) + (global (ref $g22) (ref.func $g12)) +) + +(assert_invalid + (module + (rec (type $f11 (sub (func))) (type $f12 (sub $f11 (func)))) + (rec (type $f21 (sub (func))) (type $f22 (sub $f11 (func)))) + (func $f (type $f21)) + (global (ref $f11) (ref.func $f)) + ) + "type mismatch" +) + +(assert_invalid + (module + (rec (type $f01 (sub (func))) (type $f02 (sub $f01 (func)))) + (rec (type $f11 (sub (func))) (type $f12 (sub $f01 (func)))) + (rec (type $f21 (sub (func))) (type $f22 (sub $f11 (func)))) + (func $f (type $f21)) + (global (ref $f11) (ref.func $f)) + ) + "type mismatch" +) + + +;; Runtime types + +(module + (type $t0 (sub (func (result (ref null func))))) + (rec (type $t1 (sub $t0 (func (result (ref null $t1)))))) + (rec (type $t2 (sub $t1 (func (result (ref null $t2)))))) + + (func $f0 (type $t0) (ref.null func)) + (func $f1 (type $t1) (ref.null $t1)) + (func $f2 (type $t2) (ref.null $t2)) + (table funcref (elem $f0 $f1 $f2)) + + (func (export "run") + (block (result (ref null func)) (call_indirect (type $t0) (i32.const 0))) + (block (result (ref null func)) (call_indirect (type $t0) (i32.const 1))) + (block (result (ref null func)) (call_indirect (type $t0) (i32.const 2))) + (block (result (ref null $t1)) (call_indirect (type $t1) (i32.const 1))) + (block (result (ref null $t1)) (call_indirect (type $t1) (i32.const 2))) + (block (result (ref null $t2)) (call_indirect (type $t2) (i32.const 2))) + + (block (result (ref null $t0)) (ref.cast (ref $t0) (table.get (i32.const 0)))) + (block (result (ref null $t0)) (ref.cast (ref $t0) (table.get (i32.const 1)))) + (block (result (ref null $t0)) (ref.cast (ref $t0) (table.get (i32.const 2)))) + (block (result (ref null $t1)) (ref.cast (ref $t1) (table.get (i32.const 1)))) + (block (result (ref null $t1)) (ref.cast (ref $t1) (table.get (i32.const 2)))) + (block (result (ref null $t2)) (ref.cast (ref $t2) (table.get (i32.const 2)))) + (br 0) + ) + + (func (export "fail1") + (block (result (ref null $t1)) (call_indirect (type $t1) (i32.const 0))) + (br 0) + ) + (func (export "fail2") + (block (result (ref null $t1)) (call_indirect (type $t2) (i32.const 0))) + (br 0) + ) + (func (export "fail3") + (block (result (ref null $t1)) (call_indirect (type $t2) (i32.const 1))) + (br 0) + ) + + (func (export "fail4") + (ref.cast (ref $t1) (table.get (i32.const 0))) + (br 0) + ) + (func (export "fail5") + (ref.cast (ref $t2) (table.get (i32.const 0))) + (br 0) + ) + (func (export "fail6") + (ref.cast (ref $t2) (table.get (i32.const 1))) + (br 0) + ) +) +(assert_return (invoke "run")) +(assert_trap (invoke "fail1") "indirect call") +(assert_trap (invoke "fail2") "indirect call") +(assert_trap (invoke "fail3") "indirect call") +(assert_trap (invoke "fail4") "cast") +(assert_trap (invoke "fail5") "cast") +(assert_trap (invoke "fail6") "cast") + +(module + (type $t1 (sub (func))) + (type $t2 (sub final (func))) + + (func $f1 (type $t1)) + (func $f2 (type $t2)) + (table funcref (elem $f1 $f2)) + + (func (export "fail1") + (block (call_indirect (type $t1) (i32.const 1))) + ) + (func (export "fail2") + (block (call_indirect (type $t2) (i32.const 0))) + ) + + (func (export "fail3") + (ref.cast (ref $t1) (table.get (i32.const 1))) + (drop) + ) + (func (export "fail4") + (ref.cast (ref $t2) (table.get (i32.const 0))) + (drop) + ) +) +(assert_trap (invoke "fail1") "indirect call") +(assert_trap (invoke "fail2") "indirect call") +(assert_trap (invoke "fail3") "cast") +(assert_trap (invoke "fail4") "cast") + +(module + (type $t1 (sub (func))) + (type $t2 (sub $t1 (func))) + (type $t3 (sub $t2 (func))) + (type $t4 (sub final (func))) + + (func $f2 (type $t2)) + (func $f3 (type $t3)) + (table (ref null $t2) (elem $f2 $f3)) + + (func (export "run") + (call_indirect (type $t1) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1)) + (call_indirect (type $t2) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1)) + (call_indirect (type $t3) (i32.const 1)) + ) + + (func (export "fail1") + (call_indirect (type $t3) (i32.const 0)) + ) + (func (export "fail2") + (call_indirect (type $t4) (i32.const 0)) + ) +) +(assert_return (invoke "run")) +(assert_trap (invoke "fail1") "indirect call") +(assert_trap (invoke "fail2") "indirect call") + +(module + (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $f2 (sub (func))) (type (struct (field (ref $f2))))) + (rec (type $g1 (sub $f1 (func))) (type (struct))) + (rec (type $g2 (sub $f2 (func))) (type (struct))) + (func $g (type $g2)) (elem declare func $g) + (func (export "run") (result i32) + (ref.test (ref $g1) (ref.func $g)) + ) +) +(assert_return (invoke "run") (i32.const 1)) + +(module + (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) + (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) + (rec + (type $g1 (sub $f1 (func))) + (type (sub $s1 (struct (field (ref $f1) (ref $f1) (ref $f2) (ref $f2) (ref $g1))))) + ) + (rec + (type $g2 (sub $f2 (func))) + (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2))))) + ) + (func $g (type $g2)) (elem declare func $g) + (func (export "run") (result i32) + (ref.test (ref $g1) (ref.func $g)) + ) +) +(assert_return (invoke "run") (i32.const 1)) + +(module + (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $f2 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $g1 (sub $f1 (func))) (type (struct))) + (rec (type $g2 (sub $f2 (func))) (type (struct))) + (func $g (type $g2)) (elem declare func $g) + (func (export "run") (result i32) + (ref.test (ref $g1) (ref.func $g)) + ) +) +(assert_return (invoke "run") (i32.const 0)) + +(module + (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $f2 (sub (func))) (type (struct (field (ref $f2))))) + (rec (type $g (sub $f1 (func))) (type (struct))) + (func $g (type $g)) (elem declare func $g) + (func (export "run") (result i32) + (ref.test (ref $f1) (ref.func $g)) + ) +) +(assert_return (invoke "run") (i32.const 1)) + +(module + (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) + (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) + (rec + (type $g1 (sub $f1 (func))) + (type (sub $s1 (struct (field (ref $f1) (ref $f1) (ref $f2) (ref $f2) (ref $g1))))) + ) + (rec + (type $g2 (sub $f2 (func))) + (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2))))) + ) + (rec (type $h (sub $g2 (func))) (type (struct))) + (func $h (type $h)) (elem declare func $h) + (func (export "run") (result i32 i32) + (ref.test (ref $f1) (ref.func $h)) + (ref.test (ref $g1) (ref.func $h)) + ) +) +(assert_return (invoke "run") (i32.const 1) (i32.const 1)) + + +(module + (rec (type $f11 (sub (func (result (ref func))))) (type $f12 (sub $f11 (func (result (ref $f11)))))) + (rec (type $f21 (sub (func (result (ref func))))) (type $f22 (sub $f21 (func (result (ref $f21)))))) + (func $f11 (type $f11) (unreachable)) (elem declare func $f11) + (func $f12 (type $f12) (unreachable)) (elem declare func $f12) + (func (export "run") (result i32 i32 i32 i32) + (ref.test (ref $f11) (ref.func $f11)) + (ref.test (ref $f21) (ref.func $f11)) + (ref.test (ref $f12) (ref.func $f12)) + (ref.test (ref $f22) (ref.func $f12)) + ) +) +(assert_return (invoke "run") + (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1) +) + +(module + (rec (type $f11 (sub (func (result (ref func))))) (type $f12 (sub $f11 (func (result (ref $f11)))))) + (rec (type $f21 (sub (func (result (ref func))))) (type $f22 (sub $f21 (func (result (ref $f21)))))) + (rec (type $g11 (sub $f11 (func (result (ref func))))) (type $g12 (sub $g11 (func (result (ref $g11)))))) + (rec (type $g21 (sub $f21 (func (result (ref func))))) (type $g22 (sub $g21 (func (result (ref $g21)))))) + (func $g11 (type $g11) (unreachable)) (elem declare func $g11) + (func $g12 (type $g12) (unreachable)) (elem declare func $g12) + (func (export "run") (result i32 i32 i32 i32 i32 i32 i32 i32) + (ref.test (ref $f11) (ref.func $g11)) + (ref.test (ref $f21) (ref.func $g11)) + (ref.test (ref $f11) (ref.func $g12)) + (ref.test (ref $f21) (ref.func $g12)) + (ref.test (ref $g11) (ref.func $g11)) + (ref.test (ref $g21) (ref.func $g11)) + (ref.test (ref $g12) (ref.func $g12)) + (ref.test (ref $g22) (ref.func $g12)) + ) +) +(assert_return (invoke "run") + (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1) + (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1) +) + +(module + (rec (type $f11 (sub (func))) (type $f12 (sub $f11 (func)))) + (rec (type $f21 (sub (func))) (type $f22 (sub $f11 (func)))) + (func $f (type $f21)) (elem declare func $f) + (func (export "run") (result i32) + (ref.test (ref $f11) (ref.func $f)) + ) +) +(assert_return (invoke "run") (i32.const 0)) + +(module + (rec (type $f01 (sub (func))) (type $f02 (sub $f01 (func)))) + (rec (type $f11 (sub (func))) (type $f12 (sub $f01 (func)))) + (rec (type $f21 (sub (func))) (type $f22 (sub $f11 (func)))) + (func $f (type $f21)) (elem declare func $f) + (func (export "run") (result i32) + (ref.test (ref $f11) (ref.func $f)) + ) +) +(assert_return (invoke "run") (i32.const 0)) + + + +;; Linking + +(module + (type $t0 (sub (func (result (ref null func))))) + (rec (type $t1 (sub $t0 (func (result (ref null $t1)))))) + (rec (type $t2 (sub $t1 (func (result (ref null $t2)))))) + + (func (export "f0") (type $t0) (ref.null func)) + (func (export "f1") (type $t1) (ref.null $t1)) + (func (export "f2") (type $t2) (ref.null $t2)) +) +(register "M") + +(module + (type $t0 (sub (func (result (ref null func))))) + (rec (type $t1 (sub $t0 (func (result (ref null $t1)))))) + (rec (type $t2 (sub $t1 (func (result (ref null $t2)))))) + + (func (import "M" "f0") (type $t0)) + (func (import "M" "f1") (type $t0)) + (func (import "M" "f1") (type $t1)) + (func (import "M" "f2") (type $t0)) + (func (import "M" "f2") (type $t1)) + (func (import "M" "f2") (type $t2)) +) + +(assert_unlinkable + (module + (type $t0 (sub (func (result (ref null func))))) + (rec (type $t1 (sub $t0 (func (result (ref null $t1)))))) + (rec (type $t2 (sub $t1 (func (result (ref null $t2)))))) + (func (import "M" "f0") (type $t1)) + ) + "incompatible import type" +) + +(assert_unlinkable + (module + (type $t0 (sub (func (result (ref null func))))) + (rec (type $t1 (sub $t0 (func (result (ref null $t1)))))) + (rec (type $t2 (sub $t1 (func (result (ref null $t2)))))) + (func (import "M" "f0") (type $t2)) + ) + "incompatible import type" +) + +(assert_unlinkable + (module + (type $t0 (sub (func (result (ref null func))))) + (rec (type $t1 (sub $t0 (func (result (ref null $t1)))))) + (rec (type $t2 (sub $t1 (func (result (ref null $t2)))))) + (func (import "M" "f1") (type $t2)) + ) + "incompatible import type" +) + +(module + (type $t1 (sub (func))) + (type $t2 (sub final (func))) + (func (export "f1") (type $t1)) + (func (export "f2") (type $t2)) +) +(register "M2") + +(assert_unlinkable + (module + (type $t1 (sub (func))) + (type $t2 (sub final (func))) + (func (import "M2" "f1") (type $t2)) + ) + "incompatible import type" +) +(assert_unlinkable + (module + (type $t1 (sub (func))) + (type $t2 (sub final (func))) + (func (import "M2" "f2") (type $t1)) + ) + "incompatible import type" +) + + +(module + (rec (type $f2 (sub (func))) (type (struct (field (ref $f2))))) + (rec (type $g2 (sub $f2 (func))) (type (struct))) + (func (export "g") (type $g2)) +) +(register "M3") +(module + (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $g1 (sub $f1 (func))) (type (struct))) + (func (import "M3" "g") (type $g1)) +) + +(module + (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) + (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) + (rec + (type $g2 (sub $f2 (func))) + (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2))))) + ) + (func (export "g") (type $g2)) +) +(register "M4") +(module + (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) + (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) + (rec + (type $g1 (sub $f1 (func))) + (type (sub $s1 (struct (field (ref $f1) (ref $f1) (ref $f2) (ref $f2) (ref $g1))))) + ) + (func (import "M4" "g") (type $g1)) +) + +(module + (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $f2 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $g2 (sub $f2 (func))) (type (struct))) + (func (export "g") (type $g2)) +) +(register "M5") +(assert_unlinkable + (module + (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $g1 (sub $f1 (func))) (type (struct))) + (func (import "M5" "g") (type $g1)) + ) + "incompatible import" +) + +(module + (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $f2 (sub (func))) (type (struct (field (ref $f2))))) + (rec (type $g (sub $f1 (func))) (type (struct))) + (func (export "g") (type $g)) +) +(register "M6") +(module + (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) + (rec (type $f2 (sub (func))) (type (struct (field (ref $f2))))) + (rec (type $g (sub $f1 (func))) (type (struct))) + (func (import "M6" "g") (type $f1)) +) + +(module + (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) + (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) + (rec + (type $g2 (sub $f2 (func))) + (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2))))) + ) + (rec (type $h (sub $g2 (func))) (type (struct))) + (func (export "h") (type $h)) +) +(register "M7") +(module + (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1)))))) + (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2)))))) + (rec + (type $g1 (sub $f1 (func))) + (type (sub $s1 (struct (field (ref $f1) (ref $f1) (ref $f2) (ref $f2) (ref $g1))))) + ) + (rec (type $h (sub $g1 (func))) (type (struct))) + (func (import "M7" "h") (type $f1)) + (func (import "M7" "h") (type $g1)) +) + + +(module + (rec (type $f11 (sub (func (result (ref func))))) (type $f12 (sub $f11 (func (result (ref $f11)))))) + (rec (type $f21 (sub (func (result (ref func))))) (type $f22 (sub $f21 (func (result (ref $f21)))))) + (func (export "f11") (type $f11) (unreachable)) + (func (export "f12") (type $f12) (unreachable)) +) +(register "M8") +(module + (rec (type $f11 (sub (func (result (ref func))))) (type $f12 (sub $f11 (func (result (ref $f11)))))) + (rec (type $f21 (sub (func (result (ref func))))) (type $f22 (sub $f21 (func (result (ref $f21)))))) + (func (import "M8" "f11") (type $f11)) + (func (import "M8" "f11") (type $f21)) + (func (import "M8" "f12") (type $f12)) + (func (import "M8" "f12") (type $f22)) +) + +(module + (rec (type $f11 (sub (func (result (ref func))))) (type $f12 (sub $f11 (func (result (ref $f11)))))) + (rec (type $f21 (sub (func (result (ref func))))) (type $f22 (sub $f21 (func (result (ref $f21)))))) + (rec (type $g11 (sub $f11 (func (result (ref func))))) (type $g12 (sub $g11 (func (result (ref $g11)))))) + (rec (type $g21 (sub $f21 (func (result (ref func))))) (type $g22 (sub $g21 (func (result (ref $g21)))))) + (func (export "g11") (type $g11) (unreachable)) + (func (export "g12") (type $g12) (unreachable)) +) +(register "M9") +(module + (rec (type $f11 (sub (func (result (ref func))))) (type $f12 (sub $f11 (func (result (ref $f11)))))) + (rec (type $f21 (sub (func (result (ref func))))) (type $f22 (sub $f21 (func (result (ref $f21)))))) + (rec (type $g11 (sub $f11 (func (result (ref func))))) (type $g12 (sub $g11 (func (result (ref $g11)))))) + (rec (type $g21 (sub $f21 (func (result (ref func))))) (type $g22 (sub $g21 (func (result (ref $g21)))))) + (func (import "M9" "g11") (type $f11)) + (func (import "M9" "g11") (type $f21)) + (func (import "M9" "g12") (type $f11)) + (func (import "M9" "g12") (type $f21)) + (func (import "M9" "g11") (type $g11)) + (func (import "M9" "g11") (type $g21)) + (func (import "M9" "g12") (type $g12)) + (func (import "M9" "g12") (type $g22)) +) + +(module + (rec (type $f11 (sub (func))) (type $f12 (sub $f11 (func)))) + (rec (type $f21 (sub (func))) (type $f22 (sub $f11 (func)))) + (func (export "f") (type $f21)) +) +(register "M10") +(assert_unlinkable + (module + (rec (type $f11 (sub (func))) (type $f12 (sub $f11 (func)))) + (func (import "M10" "f") (type $f11)) + ) + "incompatible import" +) + +(module + (rec (type $f01 (sub (func))) (type $f02 (sub $f01 (func)))) + (rec (type $f11 (sub (func))) (type $f12 (sub $f01 (func)))) + (rec (type $f21 (sub (func))) (type $f22 (sub $f11 (func)))) + (func (export "f") (type $f21)) +) +(register "M11") +(assert_unlinkable + (module + (rec (type $f01 (sub (func))) (type $f02 (sub $f01 (func)))) + (rec (type $f11 (sub (func))) (type $f12 (sub $f01 (func)))) + (func (import "M11" "f") (type $f11)) + ) + "incompatible import" +) + + + +;; Finality violation + +(assert_invalid + (module + (type $t (func)) + (type $s (sub $t (func))) + ) + "sub type" +) + +(assert_invalid + (module + (type $t (struct)) + (type $s (sub $t (struct))) + ) + "sub type" +) + +(assert_invalid + (module + (type $t (sub final (func))) + (type $s (sub $t (func))) + ) + "sub type" +) + +(assert_invalid + (module + (type $t (sub (func))) + (type $s (sub final $t (func))) + (type $u (sub $s (func))) + ) + "sub type" +) + + + +;; Invalid subtyping definitions + +(assert_invalid + (module + (type $a0 (sub (array i32))) + (type $s0 (sub $a0 (struct))) + ) + "sub type" +) + +(assert_invalid + (module + (type $f0 (sub (func (param i32) (result i32)))) + (type $s0 (sub $f0 (struct))) + ) + "sub type" +) + +(assert_invalid + (module + (type $s0 (sub (struct))) + (type $a0 (sub $s0 (array i32))) + ) + "sub type" +) + +(assert_invalid + (module + (type $f0 (sub (func (param i32) (result i32)))) + (type $a0 (sub $f0 (array i32))) + ) + "sub type" +) + +(assert_invalid + (module + (type $s0 (sub (struct))) + (type $f0 (sub $s0 (func (param i32) (result i32)))) + ) + "sub type" +) + +(assert_invalid + (module + (type $a0 (sub (array i32))) + (type $f0 (sub $a0 (func (param i32) (result i32)))) + ) + "sub type" +) + +(assert_invalid + (module + (type $a0 (sub (array i32))) + (type $a1 (sub $a0 (array i64))) + ) + "sub type" +) + +(assert_invalid + (module + (type $s0 (sub (struct (field i32)))) + (type $s1 (sub $s0 (struct (field i64)))) + ) + "sub type" +) + +(assert_invalid + (module + (type $f0 (sub (func))) + (type $f1 (sub $f0 (func (param i32)))) + ) + "sub type" +)