Skip to content

Commit f8b9edb

Browse files
committed
review: invisibly propagate sharedness
As @alexcrichton suggested, this holds a `must_share` flag on `Module` and carefully sets it and unsets it using `propagate_shared`.
1 parent c39f508 commit f8b9edb

File tree

2 files changed

+73
-64
lines changed

2 files changed

+73
-64
lines changed

crates/wasm-smith/src/core.rs

+69-51
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ pub struct Module {
6666
/// Whether we should encode a types section, even if `self.types` is empty.
6767
should_encode_types: bool,
6868

69+
/// Whether we should propagate sharedness to types generated inside
70+
/// `propagate_shared`.
71+
must_share: bool,
72+
6973
/// All of this module's imports. These don't have their own index space,
7074
/// but instead introduce entries to each imported entity's associated index
7175
/// space.
@@ -246,6 +250,7 @@ impl Module {
246250
max_type_limit: MaxTypeLimit::ModuleTypes,
247251
interesting_values32: Vec::new(),
248252
interesting_values64: Vec::new(),
253+
must_share: false,
249254
}
250255
}
251256
}
@@ -680,8 +685,9 @@ impl Module {
680685
fn arbitrary_sub_type(&mut self, u: &mut Unstructured) -> Result<SubType> {
681686
if !self.config.gc_enabled {
682687
let shared = self.arbitrary_shared(u)?;
688+
let func_type = self.propagate_shared(shared, |m| m.arbitrary_func_type(u))?;
683689
let composite_type = CompositeType {
684-
inner: CompositeInnerType::Func(self.arbitrary_func_type(u, shared)?),
690+
inner: CompositeInnerType::Func(func_type),
685691
shared,
686692
};
687693
return Ok(SubType {
@@ -715,7 +721,9 @@ impl Module {
715721
*f = self.arbitrary_matching_func_type(u, f)?;
716722
}
717723
CompositeInnerType::Struct(s) => {
718-
*s = self.arbitrary_matching_struct_type(u, s, composite_type.shared)?;
724+
*s = self.propagate_shared(composite_type.shared, |m| {
725+
m.arbitrary_matching_struct_type(u, s)
726+
})?;
719727
}
720728
}
721729
Ok(SubType {
@@ -729,15 +737,14 @@ impl Module {
729737
&mut self,
730738
u: &mut Unstructured,
731739
ty: &StructType,
732-
must_share: bool,
733740
) -> Result<StructType> {
734741
let len_extra_fields = u.int_in_range(0..=5)?;
735742
let mut fields = Vec::with_capacity(ty.fields.len() + len_extra_fields);
736743
for field in ty.fields.iter() {
737744
fields.push(self.arbitrary_matching_field_type(u, *field)?);
738745
}
739746
for _ in 0..len_extra_fields {
740-
fields.push(self.arbitrary_field_type(u, must_share)?);
747+
fields.push(self.arbitrary_field_type(u)?);
741748
}
742749
Ok(StructType {
743750
fields: fields.into_boxed_slice(),
@@ -1024,85 +1031,75 @@ impl Module {
10241031
if !self.config.gc_enabled {
10251032
return Ok(CompositeType {
10261033
shared,
1027-
inner: CT::Func(self.arbitrary_func_type(u, shared)?),
1034+
inner: CT::Func(self.propagate_shared(shared, |m| m.arbitrary_func_type(u))?),
10281035
});
10291036
}
10301037

10311038
match u.int_in_range(0..=2)? {
10321039
0 => Ok(CompositeType {
10331040
shared,
1034-
inner: CT::Array(ArrayType(self.arbitrary_field_type(u, shared)?)),
1041+
inner: CT::Array(ArrayType(
1042+
self.propagate_shared(shared, |m| m.arbitrary_field_type(u))?,
1043+
)),
10351044
}),
10361045
1 => Ok(CompositeType {
10371046
shared,
1038-
inner: CT::Func(self.arbitrary_func_type(u, shared)?),
1047+
inner: CT::Func(self.propagate_shared(shared, |m| m.arbitrary_func_type(u))?),
10391048
}),
10401049
2 => Ok(CompositeType {
10411050
shared,
1042-
inner: CT::Struct(self.arbitrary_struct_type(u, shared)?),
1051+
inner: CT::Struct(self.propagate_shared(shared, |m| m.arbitrary_struct_type(u))?),
10431052
}),
10441053
_ => unreachable!(),
10451054
}
10461055
}
10471056

1048-
fn arbitrary_struct_type(
1049-
&mut self,
1050-
u: &mut Unstructured,
1051-
must_share: bool,
1052-
) -> Result<StructType> {
1057+
fn arbitrary_struct_type(&mut self, u: &mut Unstructured) -> Result<StructType> {
10531058
let len = u.int_in_range(0..=20)?;
10541059
let mut fields = Vec::with_capacity(len);
10551060
for _ in 0..len {
1056-
fields.push(self.arbitrary_field_type(u, must_share)?);
1061+
fields.push(self.arbitrary_field_type(u)?);
10571062
}
10581063
Ok(StructType {
10591064
fields: fields.into_boxed_slice(),
10601065
})
10611066
}
10621067

1063-
fn arbitrary_field_type(
1064-
&mut self,
1065-
u: &mut Unstructured,
1066-
must_share: bool,
1067-
) -> Result<FieldType> {
1068+
fn arbitrary_field_type(&mut self, u: &mut Unstructured) -> Result<FieldType> {
10681069
Ok(FieldType {
1069-
element_type: self.arbitrary_storage_type(u, must_share)?,
1070+
element_type: self.arbitrary_storage_type(u)?,
10701071
mutable: u.arbitrary()?,
10711072
})
10721073
}
10731074

1074-
fn arbitrary_storage_type(
1075-
&mut self,
1076-
u: &mut Unstructured,
1077-
must_share: bool,
1078-
) -> Result<StorageType> {
1075+
fn arbitrary_storage_type(&mut self, u: &mut Unstructured) -> Result<StorageType> {
10791076
match u.int_in_range(0..=2)? {
10801077
0 => Ok(StorageType::I8),
10811078
1 => Ok(StorageType::I16),
1082-
2 => Ok(StorageType::Val(self.arbitrary_valtype(u, must_share)?)),
1079+
2 => Ok(StorageType::Val(self.arbitrary_valtype(u)?)),
10831080
_ => unreachable!(),
10841081
}
10851082
}
10861083

1087-
fn arbitrary_ref_type(&self, u: &mut Unstructured, must_share: bool) -> Result<RefType> {
1084+
fn arbitrary_ref_type(&self, u: &mut Unstructured) -> Result<RefType> {
10881085
if !self.config.reference_types_enabled {
10891086
// Create a `RefType::FUNCREF` but with variable sharedness.
10901087
Ok(RefType {
10911088
nullable: true,
10921089
heap_type: HeapType::Abstract {
1093-
shared: must_share,
1090+
shared: self.arbitrary_shared(u)?,
10941091
ty: AbstractHeapType::Func,
10951092
},
10961093
})
10971094
} else {
10981095
Ok(RefType {
10991096
nullable: true,
1100-
heap_type: self.arbitrary_heap_type(u, must_share)?,
1097+
heap_type: self.arbitrary_heap_type(u)?,
11011098
})
11021099
}
11031100
}
11041101

1105-
fn arbitrary_heap_type(&self, u: &mut Unstructured, must_share: bool) -> Result<HeapType> {
1102+
fn arbitrary_heap_type(&self, u: &mut Unstructured) -> Result<HeapType> {
11061103
assert!(self.config.reference_types_enabled);
11071104

11081105
let concrete_type_limit = match self.max_type_limit {
@@ -1118,7 +1115,7 @@ impl Module {
11181115
// shared type, though, we can use either a shared or unshared
11191116
// concrete type.
11201117
if let Some(ty) = self.types.get(idx as usize) {
1121-
if !(must_share && !ty.composite_type.shared) {
1118+
if !(self.must_share && !ty.composite_type.shared) {
11221119
return Ok(HeapType::Concrete(idx));
11231120
}
11241121
}
@@ -1138,21 +1135,17 @@ impl Module {
11381135
}
11391136

11401137
Ok(HeapType::Abstract {
1141-
shared: must_share || self.arbitrary_shared(u)?,
1138+
shared: self.arbitrary_shared(u)?,
11421139
ty: *u.choose(&choices)?,
11431140
})
11441141
}
11451142

1146-
fn arbitrary_func_type(
1147-
&mut self,
1148-
u: &mut Unstructured,
1149-
must_share: bool,
1150-
) -> Result<Rc<FuncType>> {
1143+
fn arbitrary_func_type(&mut self, u: &mut Unstructured) -> Result<Rc<FuncType>> {
11511144
let mut params = vec![];
11521145
let mut results = vec![];
11531146
let max_params = 20;
11541147
arbitrary_loop(u, 0, max_params, |u| {
1155-
params.push(self.arbitrary_valtype(u, must_share)?);
1148+
params.push(self.arbitrary_valtype(u)?);
11561149
Ok(true)
11571150
})?;
11581151
let max_results = if self.config.multi_value_enabled {
@@ -1161,7 +1154,7 @@ impl Module {
11611154
1
11621155
};
11631156
arbitrary_loop(u, 0, max_results, |u| {
1164-
results.push(self.arbitrary_valtype(u, must_share)?);
1157+
results.push(self.arbitrary_valtype(u)?);
11651158
Ok(true)
11661159
})?;
11671160
Ok(Rc::new(FuncType { params, results }))
@@ -1547,7 +1540,7 @@ impl Module {
15471540
.filter(move |i| self.func_type(*i).results.is_empty())
15481541
}
15491542

1550-
fn arbitrary_valtype(&self, u: &mut Unstructured, must_share: bool) -> Result<ValType> {
1543+
fn arbitrary_valtype(&self, u: &mut Unstructured) -> Result<ValType> {
15511544
#[derive(PartialEq, Eq, PartialOrd, Ord)]
15521545
enum ValTypeClass {
15531546
I32,
@@ -1579,14 +1572,19 @@ impl Module {
15791572
ValTypeClass::F32 => Ok(ValType::F32),
15801573
ValTypeClass::F64 => Ok(ValType::F64),
15811574
ValTypeClass::V128 => Ok(ValType::V128),
1582-
ValTypeClass::Ref => Ok(ValType::Ref(self.arbitrary_ref_type(u, must_share)?)),
1575+
ValTypeClass::Ref => Ok(ValType::Ref(self.arbitrary_ref_type(u)?)),
15831576
}
15841577
}
15851578

15861579
fn arbitrary_global_type(&self, u: &mut Unstructured) -> Result<GlobalType> {
1587-
let shared = self.arbitrary_shared(u)?;
1580+
let val_type = self.arbitrary_valtype(u)?;
1581+
// Propagate the inner type's sharedness to the global type.
1582+
let shared = match val_type {
1583+
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => self.arbitrary_shared(u)?,
1584+
ValType::Ref(r) => self.is_shared_ref_type(r),
1585+
};
15881586
Ok(GlobalType {
1589-
val_type: self.arbitrary_valtype(u, shared)?,
1587+
val_type,
15901588
mutable: u.arbitrary()?,
15911589
shared,
15921590
})
@@ -2210,9 +2208,7 @@ impl Module {
22102208
// segment. Passive/declared segments can be declared with any
22112209
// reference type, but active segments must match their table.
22122210
let ty = match kind {
2213-
ElementKind::Passive | ElementKind::Declared => {
2214-
self.arbitrary_ref_type(u, false)? // TODO: handle shared (no shared element types yet)
2215-
}
2211+
ElementKind::Passive | ElementKind::Declared => self.arbitrary_ref_type(u)?,
22162212
ElementKind::Active { table, .. } => {
22172213
let idx = table.unwrap_or(0);
22182214
self.arbitrary_matching_ref_type(u, self.tables[idx as usize].element_type)?
@@ -2318,8 +2314,7 @@ impl Module {
23182314
fn arbitrary_locals(&self, u: &mut Unstructured) -> Result<Vec<ValType>> {
23192315
let mut ret = Vec::new();
23202316
arbitrary_loop(u, 0, 100, |u| {
2321-
let shared = self.arbitrary_shared(u)?;
2322-
ret.push(self.arbitrary_valtype(u, shared)?);
2317+
ret.push(self.arbitrary_valtype(u)?);
23232318
Ok(true)
23242319
})?;
23252320
Ok(ret)
@@ -2636,8 +2631,26 @@ impl Module {
26362631
}
26372632
}
26382633

2634+
fn propagate_shared<T>(&mut self, must_share: bool, mut f: impl FnMut(&mut Self) -> T) -> T {
2635+
let tmp = mem::replace(&mut self.must_share, must_share);
2636+
let result = f(self);
2637+
self.must_share = tmp;
2638+
result
2639+
}
2640+
26392641
fn arbitrary_shared(&self, u: &mut Unstructured) -> Result<bool> {
2640-
Ok(self.config.shared_everything_threads_enabled && u.ratio(1, 4)?)
2642+
if self.must_share {
2643+
Ok(true)
2644+
} else {
2645+
Ok(self.config.shared_everything_threads_enabled && u.ratio(1, 4)?)
2646+
}
2647+
}
2648+
2649+
fn is_shared_ref_type(&self, ty: RefType) -> bool {
2650+
match ty.heap_type {
2651+
HeapType::Abstract { shared, .. } => shared,
2652+
HeapType::Concrete(i) => self.types[i as usize].composite_type.shared,
2653+
}
26412654
}
26422655

26432656
fn is_shared_type(&self, index: u32) -> bool {
@@ -2744,12 +2757,17 @@ pub(crate) fn arbitrary_table_type(
27442757
if config.disallow_traps {
27452758
assert!(minimum > 0);
27462759
}
2747-
let shared = config.shared_everything_threads_enabled && u.arbitrary()?;
27482760
let element_type = match module {
2749-
Some(module) => module.arbitrary_ref_type(u, shared)?,
2761+
Some(module) => module.arbitrary_ref_type(u)?,
27502762
None => RefType::FUNCREF,
27512763
};
27522764

2765+
// Propagate the element type's sharedness to the table type.
2766+
let shared = match module {
2767+
Some(module) => module.is_shared_ref_type(element_type),
2768+
None => false,
2769+
};
2770+
27532771
Ok(TableType {
27542772
element_type,
27552773
minimum,

crates/wasm-smith/src/core/code_builder.rs

+4-13
Original file line numberDiff line numberDiff line change
@@ -1228,7 +1228,7 @@ impl CodeBuilder<'_> {
12281228
fn arbitrary_block_type(&self, u: &mut Unstructured, module: &Module) -> Result<BlockType> {
12291229
let mut options: Vec<Box<dyn Fn(&mut Unstructured) -> Result<BlockType>>> = vec![
12301230
Box::new(|_| Ok(BlockType::Empty)),
1231-
Box::new(|u| Ok(BlockType::Result(module.arbitrary_valtype(u, false)?))), // TODO: handle shared
1231+
Box::new(|u| Ok(BlockType::Result(module.arbitrary_valtype(u)?))),
12321232
];
12331233
if module.config.multi_value_enabled {
12341234
for (i, ty) in module.func_types() {
@@ -5467,10 +5467,7 @@ fn ref_as_non_null(
54675467
) -> Result<()> {
54685468
let ref_ty = match builder.pop_ref_type() {
54695469
Some(r) => r,
5470-
None => {
5471-
let shared = module.arbitrary_shared(u)?;
5472-
module.arbitrary_ref_type(u, shared)?
5473-
}
5470+
None => module.arbitrary_ref_type(u)?,
54745471
};
54755472
builder.push_operand(Some(ValType::Ref(RefType {
54765473
nullable: false,
@@ -5512,10 +5509,7 @@ fn ref_test(
55125509
) -> Result<()> {
55135510
let ref_ty = match builder.pop_ref_type() {
55145511
Some(r) => r,
5515-
None => {
5516-
let shared = module.arbitrary_shared(u)?;
5517-
module.arbitrary_ref_type(u, shared)?
5518-
}
5512+
None => module.arbitrary_ref_type(u)?,
55195513
};
55205514
builder.push_operand(Some(ValType::I32));
55215515

@@ -5543,10 +5537,7 @@ fn ref_cast(
55435537
) -> Result<()> {
55445538
let ref_ty = match builder.pop_ref_type() {
55455539
Some(r) => r,
5546-
None => {
5547-
let shared = module.arbitrary_shared(u)?;
5548-
module.arbitrary_ref_type(u, shared)?
5549-
}
5540+
None => module.arbitrary_ref_type(u)?,
55505541
};
55515542
let sub_ty = RefType {
55525543
nullable: if !ref_ty.nullable {

0 commit comments

Comments
 (0)