diff --git a/crates/wasm-encoder/src/core/types.rs b/crates/wasm-encoder/src/core/types.rs index 5cc3bdf502..5dda818f37 100644 --- a/crates/wasm-encoder/src/core/types.rs +++ b/crates/wasm-encoder/src/core/types.rs @@ -293,6 +293,14 @@ impl RefType { }, }; + /// Create a new abstract reference type. + pub fn new_abstract(ty: AbstractHeapType, nullable: bool, shared: bool) -> Self { + Self { + nullable, + heap_type: HeapType::Abstract { shared, ty }, + } + } + /// Set the nullability of this reference type. pub fn nullable(mut self, nullable: bool) -> Self { self.nullable = nullable; diff --git a/crates/wasm-smith/src/config.rs b/crates/wasm-smith/src/config.rs index 7702a170e8..42cd8102e1 100644 --- a/crates/wasm-smith/src/config.rs +++ b/crates/wasm-smith/src/config.rs @@ -551,17 +551,30 @@ define_config! { /// Defaults to `true`. pub relaxed_simd_enabled: bool = true, - /// Determines whether the nontrapping-float-to-int-conversions propsal - /// is enabled. + /// Determines whether the non-trapping float-to-int conversions + /// proposal is enabled. /// /// Defaults to `true`. pub saturating_float_to_int_enabled: bool = true, - /// Determines whether the sign-extension-ops propsal is enabled. + /// Determines whether the sign-extension-ops proposal is enabled. /// /// Defaults to `true`. pub sign_extension_ops_enabled: bool = true, + /// Determines whether the shared-everything-threads proposal is + /// enabled. + /// + /// The [shared-everything-threads] proposal, among other things, + /// extends `shared` attributes to all WebAssembly objects; it builds on + /// the [threads] proposal. + /// + /// [shared-everything-threads]: https://github.com/WebAssembly/shared-everything-threads + /// [threads]: https://github.com/WebAssembly/threads + /// + /// Defaults to `false`. + pub shared_everything_threads_enabled: bool = false, + /// Determines whether the SIMD proposal is enabled for generating /// instructions. /// @@ -754,6 +767,7 @@ impl<'a> Arbitrary<'a> for Config { memory64_enabled: false, custom_page_sizes_enabled: false, wide_arithmetic_enabled: false, + shared_everything_threads_enabled: false, }; config.sanitize(); Ok(config) @@ -779,6 +793,7 @@ impl Config { if !self.reference_types_enabled { self.max_tables = self.max_tables.min(1); self.gc_enabled = false; + self.shared_everything_threads_enabled = false; } // If simd is disabled then disable all relaxed simd instructions as @@ -786,6 +801,12 @@ impl Config { if !self.simd_enabled { self.relaxed_simd_enabled = false; } + + // It is impossible to use the shared-everything-threads proposal + // without threads, which it is built on. + if !self.threads_enabled { + self.shared_everything_threads_enabled = false; + } } /// Returns the set of features that are necessary for validating against @@ -821,6 +842,10 @@ impl Config { features.set(WasmFeatures::FUNCTION_REFERENCES, self.gc_enabled); features.set(WasmFeatures::GC, self.gc_enabled); features.set(WasmFeatures::THREADS, self.threads_enabled); + features.set( + WasmFeatures::SHARED_EVERYTHING_THREADS, + self.shared_everything_threads_enabled, + ); features.set( WasmFeatures::CUSTOM_PAGE_SIZES, self.custom_page_sizes_enabled, diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs index b5430b09a2..d9d406600d 100644 --- a/crates/wasm-smith/src/core.rs +++ b/crates/wasm-smith/src/core.rs @@ -1,4 +1,4 @@ -//! Generating arbitary core Wasm modules. +//! Generating arbitrary core Wasm modules. mod code_builder; pub(crate) mod encode; @@ -66,6 +66,10 @@ pub struct Module { /// Whether we should encode a types section, even if `self.types` is empty. should_encode_types: bool, + /// Whether we should propagate sharedness to types generated inside + /// `propagate_shared`. + must_share: bool, + /// All of this module's imports. These don't have their own index space, /// but instead introduce entries to each imported entity's associated index /// space. @@ -246,6 +250,7 @@ impl Module { max_type_limit: MaxTypeLimit::ModuleTypes, interesting_values32: Vec::new(), interesting_values64: Vec::new(), + must_share: false, } } } @@ -516,7 +521,7 @@ impl Module { (HT::Concrete(a), HT::Abstract { shared, ty }) => { let a_ty = &self.ty(a).composite_type; - if a_ty.shared == shared { + if a_ty.shared != shared { return false; } match ty { @@ -530,7 +535,7 @@ impl Module { (HT::Abstract { shared, ty }, HT::Concrete(b)) => { let b_ty = &self.ty(b).composite_type; - if shared == b_ty.shared { + if shared != b_ty.shared { return false; } match ty { @@ -572,6 +577,7 @@ impl Module { let index = u32::try_from(self.types.len()).unwrap(); if let Some(supertype) = ty.supertype { + assert_eq!(self.is_shared_type(supertype), ty.composite_type.shared); self.super_to_sub_types .entry(supertype) .or_default() @@ -658,12 +664,12 @@ impl Module { fn clone_rec_group(&mut self, u: &mut Unstructured, kind: AllowEmptyRecGroup) -> Result<()> { // NB: this does *not* guarantee that the cloned rec group will - // canonicalize the same as the original rec group and be - // deduplicated. That would reqiure a second pass over the cloned types - // to rewrite references within the original rec group to be references - // into the new rec group. That might make sense to do one day, but for - // now we don't do it. That also means that we can't mark the new types - // as "subtypes" of the old types and vice versa. + // canonicalize the same as the original rec group and be deduplicated. + // That would require a second pass over the cloned types to rewrite + // references within the original rec group to be references into the + // new rec group. That might make sense to do one day, but for now we + // don't do it. That also means that we can't mark the new types as + // "subtypes" of the old types and vice versa. let candidates: Vec<_> = self.clonable_rec_groups(kind).collect(); let group = u.choose(&candidates)?.clone(); let new_rec_group_start = self.types.len(); @@ -678,9 +684,11 @@ impl Module { fn arbitrary_sub_type(&mut self, u: &mut Unstructured) -> Result { if !self.config.gc_enabled { + let shared = self.arbitrary_shared(u)?; + let func_type = self.propagate_shared(shared, |m| m.arbitrary_func_type(u))?; let composite_type = CompositeType { - inner: CompositeInnerType::Func(self.arbitrary_func_type(u)?), - shared: false, + inner: CompositeInnerType::Func(func_type), + shared, }; return Ok(SubType { is_final: true, @@ -713,7 +721,9 @@ impl Module { *f = self.arbitrary_matching_func_type(u, f)?; } CompositeInnerType::Struct(s) => { - *s = self.arbitrary_matching_struct_type(u, s)?; + *s = self.propagate_shared(composite_type.shared, |m| { + m.arbitrary_matching_struct_type(u, s) + })?; } } Ok(SubType { @@ -787,44 +797,55 @@ impl Module { } fn arbitrary_matching_heap_type(&self, u: &mut Unstructured, ty: HeapType) -> Result { + use {AbstractHeapType as AHT, CompositeInnerType as CT, HeapType as HT}; + if !self.config.gc_enabled { return Ok(ty); } - use CompositeInnerType as CT; - use HeapType as HT; + let mut choices = vec![ty]; match ty { HT::Abstract { shared, ty } => { use AbstractHeapType::*; - let ht = |ty| HT::Abstract { shared, ty }; + let add_abstract = |choices: &mut Vec, tys: &[AHT]| { + choices.extend(tys.iter().map(|&ty| HT::Abstract { shared, ty })); + }; + let add_concrete = |choices: &mut Vec, tys: &[u32]| { + choices.extend( + tys.iter() + .filter(|&&idx| shared == self.is_shared_type(idx)) + .copied() + .map(HT::Concrete), + ); + }; match ty { Any => { - choices.extend([ht(Eq), ht(Struct), ht(Array), ht(I31), ht(None)]); - choices.extend(self.array_types.iter().copied().map(HT::Concrete)); - choices.extend(self.struct_types.iter().copied().map(HT::Concrete)); + add_abstract(&mut choices, &[Eq, Struct, Array, I31, None]); + add_concrete(&mut choices, &self.array_types); + add_concrete(&mut choices, &self.struct_types); } Eq => { - choices.extend([ht(Struct), ht(Array), ht(I31), ht(None)]); - choices.extend(self.array_types.iter().copied().map(HT::Concrete)); - choices.extend(self.struct_types.iter().copied().map(HT::Concrete)); + add_abstract(&mut choices, &[Struct, Array, I31, None]); + add_concrete(&mut choices, &self.array_types); + add_concrete(&mut choices, &self.struct_types); } Struct => { - choices.extend([ht(Struct), ht(None)]); - choices.extend(self.struct_types.iter().copied().map(HT::Concrete)); + add_abstract(&mut choices, &[Struct, None]); + add_concrete(&mut choices, &self.struct_types); } Array => { - choices.extend([ht(Array), ht(None)]); - choices.extend(self.array_types.iter().copied().map(HT::Concrete)); + add_abstract(&mut choices, &[Array, None]); + add_concrete(&mut choices, &self.array_types); } I31 => { - choices.push(ht(None)); + add_abstract(&mut choices, &[None]); } Func => { - choices.extend(self.func_types.iter().copied().map(HT::Concrete)); - choices.push(ht(NoFunc)); + add_abstract(&mut choices, &[NoFunc]); + add_concrete(&mut choices, &self.func_types); } Extern => { - choices.push(ht(NoExtern)); + add_abstract(&mut choices, &[NoExtern]); } Exn | NoExn | None | NoExtern | NoFunc | Cont | NoCont => {} } @@ -915,67 +936,71 @@ impl Module { u: &mut Unstructured, ty: HeapType, ) -> Result { + use {AbstractHeapType as AHT, CompositeInnerType as CT, HeapType as HT}; + if !self.config.gc_enabled { return Ok(ty); } - use CompositeInnerType as CT; - use HeapType as HT; + let mut choices = vec![ty]; match ty { HT::Abstract { shared, ty } => { use AbstractHeapType::*; - let ht = |ty| HT::Abstract { shared, ty }; + let add_abstract = |choices: &mut Vec, tys: &[AHT]| { + choices.extend(tys.iter().map(|&ty| HT::Abstract { shared, ty })); + }; + let add_concrete = |choices: &mut Vec, tys: &[u32]| { + choices.extend( + tys.iter() + .filter(|&&idx| shared == self.is_shared_type(idx)) + .copied() + .map(HT::Concrete), + ); + }; match ty { None => { - choices.extend([ht(Any), ht(Eq), ht(Struct), ht(Array), ht(I31)]); - choices.extend(self.array_types.iter().copied().map(HT::Concrete)); - choices.extend(self.struct_types.iter().copied().map(HT::Concrete)); + add_abstract(&mut choices, &[Any, Eq, Struct, Array, I31]); + add_concrete(&mut choices, &self.array_types); + add_concrete(&mut choices, &self.struct_types); } NoExtern => { - choices.push(ht(Extern)); + add_abstract(&mut choices, &[Extern]); } NoFunc => { - choices.extend(self.func_types.iter().copied().map(HT::Concrete)); - choices.push(ht(Func)); + add_abstract(&mut choices, &[Func]); + add_concrete(&mut choices, &self.func_types); } NoExn => { - choices.push(ht(Exn)); + add_abstract(&mut choices, &[Exn]); } Struct | Array | I31 => { - choices.extend([ht(Any), ht(Eq)]); + add_abstract(&mut choices, &[Any, Eq]); } Eq => { - choices.push(ht(Any)); + add_abstract(&mut choices, &[Any]); } NoCont => { - choices.push(ht(Cont)); + add_abstract(&mut choices, &[Cont]); } Exn | Any | Func | Extern | Cont => {} } } HT::Concrete(mut idx) => { if let Some(sub_ty) = &self.types.get(usize::try_from(idx).unwrap()) { + use AbstractHeapType::*; let ht = |ty| HT::Abstract { shared: sub_ty.composite_type.shared, ty, }; match &sub_ty.composite_type.inner { CT::Array(_) => { - choices.extend([ - ht(AbstractHeapType::Any), - ht(AbstractHeapType::Eq), - ht(AbstractHeapType::Array), - ]); + choices.extend([ht(Any), ht(Eq), ht(Array)]); } CT::Func(_) => { - choices.push(ht(AbstractHeapType::Func)); + choices.push(ht(Func)); } CT::Struct(_) => { - choices.extend([ - ht(AbstractHeapType::Any), - ht(AbstractHeapType::Eq), - ht(AbstractHeapType::Struct), - ]); + choices.extend([ht(Any), ht(Eq), ht(Struct)]); } } } else { @@ -1001,26 +1026,29 @@ impl Module { fn arbitrary_composite_type(&mut self, u: &mut Unstructured) -> Result { use CompositeInnerType as CT; - let shared = false; // TODO: handle shared + let shared = self.arbitrary_shared(u)?; + if !self.config.gc_enabled { return Ok(CompositeType { shared, - inner: CT::Func(self.arbitrary_func_type(u)?), + inner: CT::Func(self.propagate_shared(shared, |m| m.arbitrary_func_type(u))?), }); } match u.int_in_range(0..=2)? { 0 => Ok(CompositeType { shared, - inner: CT::Array(ArrayType(self.arbitrary_field_type(u)?)), + inner: CT::Array(ArrayType( + self.propagate_shared(shared, |m| m.arbitrary_field_type(u))?, + )), }), 1 => Ok(CompositeType { shared, - inner: CT::Func(self.arbitrary_func_type(u)?), + inner: CT::Func(self.propagate_shared(shared, |m| m.arbitrary_func_type(u))?), }), 2 => Ok(CompositeType { shared, - inner: CT::Struct(self.arbitrary_struct_type(u)?), + inner: CT::Struct(self.propagate_shared(shared, |m| m.arbitrary_struct_type(u))?), }), _ => unreachable!(), } @@ -1073,7 +1101,18 @@ impl Module { if self.config.gc_enabled && concrete_type_limit > 0 && u.arbitrary()? { let idx = u.int_in_range(0..=concrete_type_limit - 1)?; - return Ok(HeapType::Concrete(idx)); + // If the caller is demanding a shared heap type but the concrete + // type we found is not in fact shared, we skip down below to use an + // abstract heap type instead. If the caller is not demanding a + // shared type, though, we can use either a shared or unshared + // concrete type. + if let Some(ty) = self.types.get(idx as usize) { + // TODO: in the future, once we can easily query a list of + // existing shared types, remove this extra check. + if !(self.must_share && !ty.composite_type.shared) { + return Ok(HeapType::Concrete(idx)); + } + } } use AbstractHeapType::*; @@ -1090,7 +1129,7 @@ impl Module { } Ok(HeapType::Abstract { - shared: false, // TODO: turn on shared attribute with shared-everything-threads. + shared: self.arbitrary_shared(u)?, ty: *u.choose(&choices)?, }) } @@ -1305,7 +1344,7 @@ impl Module { // Returns the index to the translated type in the to-be type section, and the reference to // the type itself. - let mut make_func_type = |parsed_sig_idx: u32| { + let mut make_func_type = |module: &Self, parsed_sig_idx: u32| { let serialized_sig_idx = match available_types.get_mut(parsed_sig_idx as usize) { None => panic!("signature index refers to a type out of bounds"), Some((_, Some(idx))) => *idx as usize, @@ -1328,10 +1367,11 @@ impl Module { .collect(), }); index_store.replace(new_index as u32); + let shared = module.arbitrary_shared(u).ok()?; new_types.push(SubType { is_final: true, supertype: None, - composite_type: CompositeType::new_func(Rc::clone(&func_type), false), // TODO: handle shared + composite_type: CompositeType::new_func(Rc::clone(&func_type), shared), }); new_index } @@ -1351,7 +1391,7 @@ impl Module { wasmparser::TypeRef::Func(sig_idx) => { if self.funcs.len() >= self.config.max_funcs { continue; - } else if let Some((sig_idx, func_type)) = make_func_type(*sig_idx) { + } else if let Some((sig_idx, func_type)) = make_func_type(&self, *sig_idx) { let entity = EntityType::Func(sig_idx as u32, Rc::clone(&func_type)); if type_size_budget < entity.size() { continue; @@ -1367,7 +1407,8 @@ impl Module { let can_add_tag = self.tags.len() < self.config.max_tags; if !self.config.exceptions_enabled || !can_add_tag { continue; - } else if let Some((sig_idx, func_type)) = make_func_type(*func_type_idx) { + } else if let Some((sig_idx, func_type)) = make_func_type(&self, *func_type_idx) + { let tag_type = TagType { func_type_idx: sig_idx, func_type, @@ -1530,10 +1571,18 @@ impl Module { } fn arbitrary_global_type(&self, u: &mut Unstructured) -> Result { + let val_type = self.arbitrary_valtype(u)?; + // Propagate the inner type's sharedness to the global type. + let shared = match val_type { + ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => { + self.arbitrary_shared(u)? + } + ValType::Ref(r) => self.is_shared_ref_type(r), + }; Ok(GlobalType { - val_type: self.arbitrary_valtype(u)?, + val_type, mutable: u.arbitrary()?, - shared: false, + shared, }) } @@ -1564,12 +1613,26 @@ impl Module { return Ok(()); } + // For now, only define non-shared functions. Until we can update + // instruction generation to understand the additional sharedness + // validation, we don't want to generate instructions that touch + // unshared objects from a shared context (TODO: handle shared). + let unshared_func_types: Vec<_> = self + .func_types + .iter() + .copied() + .filter(|&i| !self.is_shared_type(i)) + .collect(); + if unshared_func_types.is_empty() { + return Ok(()); + } + arbitrary_loop(u, self.config.min_funcs, self.config.max_funcs, |u| { if !self.can_add_local_or_import_func() { return Ok(false); } - let max = self.func_types.len() - 1; - let ty = self.func_types[u.int_in_range(0..=max)?]; + let max = unshared_func_types.len() - 1; + let ty = unshared_func_types[u.int_in_range(0..=max)?]; self.funcs.push((ty, self.func_type(ty).clone())); self.num_defined_funcs += 1; Ok(true) @@ -1650,7 +1713,6 @@ impl Module { fn arbitrary_const_expr(&mut self, ty: ValType, u: &mut Unstructured) -> Result { let mut choices = mem::take(&mut self.const_expr_choices); choices.clear(); - let num_funcs = self.funcs.len() as u32; // MVP wasm can `global.get` any immutable imported global in a // constant expression, and the GC proposal enables this for all @@ -1690,12 +1752,25 @@ impl Module { match ty.heap_type { HeapType::Abstract { ty: AbstractHeapType::Func, - .. - } if num_funcs > 0 => { - choices.push(Box::new(move |u, _| { - let func = u.int_in_range(0..=num_funcs - 1)?; - Ok(ConstExpr::ref_func(func)) - })); + shared, + } => { + let num_funcs = self + .funcs + .iter() + .filter(|(t, _)| shared == self.is_shared_type(*t)) + .count(); + if num_funcs > 0 { + let pick = u.int_in_range(0..=num_funcs - 1)?; + let (i, _) = self + .funcs + .iter() + .map(|(t, _)| *t) + .enumerate() + .filter(|(_, t)| shared == self.is_shared_type(*t)) + .nth(pick) + .unwrap(); + choices.push(Box::new(move |_, _| Ok(ConstExpr::ref_func(i as u32)))); + } } HeapType::Concrete(ty) => { @@ -1883,8 +1958,8 @@ impl Module { supertype: None, composite_type: CompositeType::new_func( Rc::clone(&new_type), - false, - ), // TODO: handle shared + subtype.composite_type.shared, + ), }); let func_index = self.funcs.len() as u32; self.funcs.push((type_index, new_type)); @@ -2202,8 +2277,9 @@ impl Module { self.code.reserve(self.num_defined_funcs); let mut allocs = CodeBuilderAllocations::new(self, self.config.exports.is_some()); - for (_, ty) in self.funcs[self.funcs.len() - self.num_defined_funcs..].iter() { - let body = self.arbitrary_func_body(u, ty, &mut allocs)?; + for (idx, ty) in self.funcs[self.funcs.len() - self.num_defined_funcs..].iter() { + let shared = self.is_shared_type(*idx); + let body = self.arbitrary_func_body(u, ty, &mut allocs, shared)?; self.code.push(body); } allocs.finish(u, self)?; @@ -2215,9 +2291,10 @@ impl Module { u: &mut Unstructured, ty: &FuncType, allocs: &mut CodeBuilderAllocations, + shared: bool, ) -> Result { let mut locals = self.arbitrary_locals(u)?; - let builder = allocs.builder(ty, &mut locals); + let builder = allocs.builder(ty, &mut locals, shared); let instructions = if self.config.allow_invalid_funcs && u.arbitrary().unwrap_or(false) { Instructions::Arbitrary(arbitrary_vec_u8(u)?) } else { @@ -2549,6 +2626,34 @@ impl Module { } } } + + fn propagate_shared(&mut self, must_share: bool, mut f: impl FnMut(&mut Self) -> T) -> T { + let tmp = mem::replace(&mut self.must_share, must_share); + let result = f(self); + self.must_share = tmp; + result + } + + fn arbitrary_shared(&self, u: &mut Unstructured) -> Result { + if self.must_share { + Ok(true) + } else { + Ok(self.config.shared_everything_threads_enabled && u.ratio(1, 4)?) + } + } + + fn is_shared_ref_type(&self, ty: RefType) -> bool { + match ty.heap_type { + HeapType::Abstract { shared, .. } => shared, + HeapType::Concrete(i) => self.types[i as usize].composite_type.shared, + } + } + + fn is_shared_type(&self, index: u32) -> bool { + let index = usize::try_from(index).unwrap(); + let ty = self.types.get(index).unwrap(); + ty.composite_type.shared + } } pub(crate) fn arbitrary_limits64( @@ -2604,14 +2709,20 @@ pub(crate) fn configured_valtypes(config: &Config) -> Vec { true, ] { use AbstractHeapType::*; - for ty in [ + let abs_ref_types = [ Any, Eq, I31, Array, Struct, None, Func, NoFunc, Extern, NoExtern, - ] { - valtypes.push(ValType::Ref(RefType { - nullable, - // TODO: handle shared - heap_type: HeapType::Abstract { shared: false, ty }, - })); + ]; + valtypes.extend( + abs_ref_types + .iter() + .map(|&ty| ValType::Ref(RefType::new_abstract(ty, nullable, false))), + ); + if config.shared_everything_threads_enabled { + valtypes.extend( + abs_ref_types + .iter() + .map(|&ty| ValType::Ref(RefType::new_abstract(ty, nullable, true))), + ); } } } else if config.reference_types_enabled { @@ -2646,12 +2757,19 @@ pub(crate) fn arbitrary_table_type( Some(module) => module.arbitrary_ref_type(u)?, None => RefType::FUNCREF, }; + + // Propagate the element type's sharedness to the table type. + let shared = match module { + Some(module) => module.is_shared_ref_type(element_type), + None => false, + }; + Ok(TableType { element_type, minimum, maximum, table64, - shared: false, // TODO: handle shared + shared, }) } diff --git a/crates/wasm-smith/src/core/code_builder.rs b/crates/wasm-smith/src/core/code_builder.rs index 310800bf8a..c8c2671474 100644 --- a/crates/wasm-smith/src/core/code_builder.rs +++ b/crates/wasm-smith/src/core/code_builder.rs @@ -668,6 +668,8 @@ pub(crate) struct CodeBuilderAllocations { } pub(crate) struct CodeBuilder<'a> { + #[allow(dead_code)] + shared: bool, func_ty: &'a FuncType, locals: &'a mut Vec, allocs: &'a mut CodeBuilderAllocations, @@ -903,6 +905,7 @@ impl CodeBuilderAllocations { &'a mut self, func_ty: &'a FuncType, locals: &'a mut Vec, + shared: bool, ) -> CodeBuilder<'a> { self.controls.clear(); self.controls.push(Control { @@ -916,6 +919,7 @@ impl CodeBuilderAllocations { self.options.clear(); CodeBuilder { + shared, func_ty, locals, allocs: self, @@ -2323,8 +2327,8 @@ fn br_on_null( let ty = *u.choose(&[ Func, Extern, Any, None, NoExtern, NoFunc, Eq, Struct, Array, I31, ])?; - // TODO: handle shared - HeapType::Abstract { shared: false, ty } + let shared = module.arbitrary_shared(u)?; + HeapType::Abstract { shared, ty } } } }; @@ -5397,25 +5401,26 @@ fn ref_null( } if module.config.gc_enabled { use AbstractHeapType::*; - let r = |heap_type| RefType { - nullable: true, - heap_type, - }; - let a = |abstract_heap_type| HeapType::Abstract { - shared: false, // TODO: handle shared - ty: abstract_heap_type, - }; - choices.push(r(a(Any))); - choices.push(r(a(Eq))); - choices.push(r(a(Array))); - choices.push(r(a(Struct))); - choices.push(r(a(I31))); - choices.push(r(a(None))); - choices.push(r(a(NoFunc))); - choices.push(r(a(NoExtern))); + let abs_ref_types = [Any, Eq, Array, Struct, I31, None, NoFunc, NoExtern]; + choices.extend( + abs_ref_types + .iter() + .map(|&ty| RefType::new_abstract(ty, true, false)), + ); + if module.config().shared_everything_threads_enabled { + choices.extend( + abs_ref_types + .iter() + .map(|&ty| RefType::new_abstract(ty, true, true)), + ); + } + for i in 0..module.types.len() { let i = u32::try_from(i).unwrap(); - choices.push(r(HeapType::Concrete(i))); + choices.push(RefType { + nullable: true, + heap_type: HeapType::Concrete(i), + }); } } let ty = *u.choose(&choices)?; diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs index f0d70ed9ed..66942dec6b 100644 --- a/fuzz/src/lib.rs +++ b/fuzz/src/lib.rs @@ -27,6 +27,7 @@ pub fn generate_valid_module( config.canonicalize_nans = u.arbitrary()?; config.custom_page_sizes_enabled = u.arbitrary()?; config.wide_arithmetic_enabled = u.arbitrary()?; + config.shared_everything_threads_enabled = u.arbitrary()?; configure(&mut config, u)?; diff --git a/fuzz/src/mutate.rs b/fuzz/src/mutate.rs index d1492f1d2e..f0923d2e7d 100644 --- a/fuzz/src/mutate.rs +++ b/fuzz/src/mutate.rs @@ -11,12 +11,16 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> { let mut seed = 0; let mut preserve_semantics = false; - let (wasm, _config) = crate::generate_valid_module(u, |_config, u| { + let (wasm, _config) = crate::generate_valid_module(u, |config, u| { // NB: wasm-mutate is a general-purpose tool so unsupported proposals by // wasm-mutate are not disabled here. Those must be rejected with a // first-class error in wasm-mutate instead of panicking. seed = u.arbitrary()?; preserve_semantics = u.arbitrary()?; + + // NB: the mutator will change shared to unshared in ways that create + // invalid modules so we disable the proposal (TODO: handle shared). + config.shared_everything_threads_enabled = false; Ok(()) })?; log::debug!("seed = {}", seed);