Skip to content

Commit 6b72393

Browse files
authored
[NFC] Refactor heap type fuzzer generator (#7681)
Refactor the top-level driver in the heap type generator to plan recursion groups before planning the types they contain. This will make it easier to add support for generating described and descriptor types in a follow-on PR because these types can only be added if there is sufficient room left in the current recursion group.
1 parent 47076ca commit 6b72393

File tree

2 files changed

+96
-82
lines changed

2 files changed

+96
-82
lines changed

src/tools/fuzzing/heap-types.cpp

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -66,55 +66,69 @@ struct HeapTypeGeneratorImpl {
6666
// appropriately use types we haven't constructed yet.
6767
typeKinds.reserve(builder.size());
6868
supertypeIndices.reserve(builder.size());
69-
Index numRoots = 1 + rand.upTo(builder.size());
70-
for (Index i = 0; i < builder.size(); ++i) {
71-
typeIndices.insert({builder[i], i});
72-
// Everything is a subtype of itself.
73-
subtypeIndices[i].push_back(i);
74-
if (i < numRoots || rand.oneIn(2)) {
75-
// This is a root type with no supertype. Choose a kind for this type.
76-
typeKinds.emplace_back(generateHeapTypeKind());
77-
builder[i].setShared(
78-
!features.hasSharedEverything() || rand.oneIn(2) ? Unshared : Shared);
79-
} else {
80-
// This is a subtype. Choose one of the previous types to be the
81-
// supertype.
82-
Index super = rand.upTo(i);
83-
builder[i].subTypeOf(builder[super]);
84-
builder[i].setShared(HeapType(builder[super]).getShared());
85-
supertypeIndices[i] = super;
86-
subtypeIndices[super].push_back(i);
87-
typeKinds.push_back(typeKinds[super]);
69+
recGroupEnds.reserve(builder.size());
70+
71+
// The number of root types to generate before we start adding subtypes.
72+
size_t numRoots = 1 + rand.upTo(builder.size());
73+
74+
// The mean expected size of the recursion groups.
75+
size_t expectedGroupSize = 1 + rand.upTo(builder.size());
76+
77+
size_t i = 0;
78+
while (i < builder.size()) {
79+
i += planGroup(i, numRoots, expectedGroupSize);
80+
}
81+
assert(recGroupEnds.size() == builder.size());
82+
83+
populateTypes();
84+
}
85+
86+
size_t planGroup(size_t start, size_t numRoots, size_t expectedGroupSize) {
87+
size_t maxSize = builder.size() - start;
88+
size_t size = 1;
89+
// Generate the group size according to a geometric distribution.
90+
for (; size < maxSize; ++size) {
91+
if (rand.oneIn(expectedGroupSize)) {
92+
break;
8893
}
8994
}
95+
assert(start + size <= builder.size());
96+
builder.createRecGroup(start, size);
9097

91-
// Types without nontrivial subtypes may be marked final.
92-
for (Index i = 0; i < builder.size(); ++i) {
93-
builder[i].setOpen(subtypeIndices[i].size() > 1 || rand.oneIn(2));
98+
size_t end = start + size;
99+
for (size_t i = start; i < end; ++i) {
100+
recGroupEnds.push_back(end);
101+
planType(i, numRoots);
94102
}
103+
return size;
104+
}
95105

96-
// Initialize the recursion groups.
97-
recGroupEnds.reserve(builder.size());
98-
// Create isorecursive recursion groups. Choose an expected group size
99-
// uniformly at random, then create groups with random sizes on a geometric
100-
// distribution based on that expected size.
101-
size_t expectedSize = 1 + rand.upTo(builder.size());
102-
Index groupStart = 0;
103-
for (Index i = 0; i < builder.size(); ++i) {
104-
if (i == builder.size() - 1 || rand.oneIn(expectedSize)) {
105-
// End the old group and create a new group.
106-
Index newGroupStart = i + 1;
107-
builder.createRecGroup(groupStart, newGroupStart - groupStart);
108-
for (Index j = groupStart; j < newGroupStart; ++j) {
109-
recGroupEnds.push_back(newGroupStart);
110-
}
111-
groupStart = newGroupStart;
112-
}
106+
void planType(size_t i, size_t numRoots) {
107+
typeIndices.insert({builder[i], i});
108+
// Everything is a subtype of itself.
109+
subtypeIndices[i].push_back(i);
110+
if (i < numRoots || rand.oneIn(2)) {
111+
// This is a root type with no supertype. Choose a kind for this type.
112+
typeKinds.emplace_back(generateHeapTypeKind());
113+
builder[i].setShared(
114+
!features.hasSharedEverything() || rand.oneIn(2) ? Unshared : Shared);
115+
} else {
116+
// This is a subtype. Choose one of the previous types to be the
117+
// supertype.
118+
Index super = rand.upTo(i);
119+
builder[i].subTypeOf(builder[super]);
120+
builder[i].setShared(HeapType(builder[super]).getShared());
121+
supertypeIndices[i] = super;
122+
subtypeIndices[super].push_back(i);
123+
typeKinds.push_back(typeKinds[super]);
113124
}
114-
assert(recGroupEnds.size() == builder.size());
125+
}
115126

127+
void populateTypes() {
116128
// Create the heap types.
117129
for (; index < builder.size(); ++index) {
130+
// Types without nontrivial subtypes may be marked final.
131+
builder[index].setOpen(subtypeIndices[index].size() > 1 || rand.oneIn(2));
118132
auto kind = typeKinds[index];
119133
auto share = HeapType(builder[index]).getShared();
120134
if (!supertypeIndices[index]) {

test/lit/fuzz-types.test

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,60 @@
1-
;; RUN: wasm-fuzz-types -v --seed=1 | filecheck %s
1+
;; RUN: wasm-fuzz-types -v --seed=0 | filecheck %s
22

3-
;; CHECK: Running with seed 1
3+
;; CHECK: Running with seed 0
44
;; CHECK-NEXT: Built 20 types:
55
;; CHECK-NEXT: (rec
6-
;; CHECK-NEXT: (type $0 (sub (struct (field (mut i16)) (field (mut (ref $2))) (field (mut (ref null $2))))))
7-
;; CHECK-NEXT: (type $1 (sub (func (param (ref $1)) (result f64 (ref $0) f32 structref))))
8-
;; CHECK-NEXT: (type $2 (sub (shared (struct (field (mut (ref null (shared extern)))) (field (mut (ref null $2)))))))
9-
;; CHECK-NEXT: (type $3 (sub (shared (struct))))
6+
;; CHECK-NEXT: (type $0 (sub (func (param (ref (shared func)) f32 i64) (result (ref $7)))))
7+
;; CHECK-NEXT: (type $1 (sub (array (mut i16))))
8+
;; CHECK-NEXT: (type $2 (sub (shared (func (param f64 (ref null $5) (ref $9)) (result (ref null $6) (ref $3) (ref null $8))))))
9+
;; CHECK-NEXT: (type $3 (sub final $2 (shared (func (param f64 (ref null $1) (ref $0)) (result (ref $6) (ref $3) (ref $8))))))
10+
;; CHECK-NEXT: (type $4 (sub $1 (array (mut i16))))
11+
;; CHECK-NEXT: (type $5 (sub final $4 (array (mut i16))))
12+
;; CHECK-NEXT: (type $6 (sub $2 (shared (func (param f64 arrayref funcref) (result (ref $6) (ref $3) (ref $8))))))
13+
;; CHECK-NEXT: (type $7 (sub $0 (func (param (ref (shared func)) f32 i64) (result (ref $9)))))
14+
;; CHECK-NEXT: (type $8 (sub final $0 (func (param (ref (shared func)) f32 i64) (result (ref $9)))))
15+
;; CHECK-NEXT: (type $9 (sub $7 (func (param (ref (shared func)) f32 i64) (result (ref $9)))))
16+
;; CHECK-NEXT: (type $10 (sub $2 (shared (func (param f64 (ref null $5) (ref func)) (result (ref $6) (ref $3) (ref null $8))))))
17+
;; CHECK-NEXT: (type $11 (sub final $2 (shared (func (param f64 arrayref (ref $0)) (result (ref $6) (ref $3) (ref $8))))))
18+
;; CHECK-NEXT: (type $12 (sub $10 (shared (func (param f64 (ref null $1) funcref) (result (ref $6) (ref (shared nofunc)) (ref $8))))))
1019
;; CHECK-NEXT: )
1120
;; CHECK-NEXT: (rec
12-
;; CHECK-NEXT: (type $4 (sub (array i32)))
13-
;; CHECK-NEXT: (type $5 (sub $4 (array i32)))
14-
;; CHECK-NEXT: (type $6 (shared (func (param (ref null $3)) (result i32))))
15-
;; CHECK-NEXT: (type $7 (sub $2 (shared (struct (field (mut (ref null (shared extern)))) (field (mut (ref null $2))) (field (mut (ref null $3))) (field (mut i16)) (field (mut (ref null $7))) (field (mut (ref null $7)))))))
16-
;; CHECK-NEXT: (type $8 (sub $0 (struct (field (mut i16)) (field (mut (ref $2))) (field (mut (ref null $2))))))
21+
;; CHECK-NEXT: (type $13 (sub (func (param f64 f32))))
22+
;; CHECK-NEXT: (type $14 (func (param i64) (result v128 (ref null $2) (ref $12) f32 v128)))
23+
;; CHECK-NEXT: (type $15 (sub final $12 (shared (func (param f64 anyref funcref) (result (ref $6) (ref (shared nofunc)) (ref $8))))))
1724
;; CHECK-NEXT: )
1825
;; CHECK-NEXT: (rec
19-
;; CHECK-NEXT: (type $9 (shared (array i32)))
20-
;; CHECK-NEXT: (type $10 (sub $5 (array i32)))
21-
;; CHECK-NEXT: (type $11 (func (result i32)))
22-
;; CHECK-NEXT: (type $12 (sub (shared (array (ref $3)))))
23-
;; CHECK-NEXT: (type $13 (sub (shared (func (param (ref null $19) v128) (result (ref null $12))))))
24-
;; CHECK-NEXT: (type $14 (sub final $12 (shared (array (ref $3)))))
25-
;; CHECK-NEXT: (type $15 (sub (shared (func (param i31ref (ref $5)) (result i32)))))
26-
;; CHECK-NEXT: (type $16 (sub $5 (array i32)))
27-
;; CHECK-NEXT: (type $17 (sub (func (result (ref $7)))))
28-
;; CHECK-NEXT: (type $18 (sub (array (mut i8))))
29-
;; CHECK-NEXT: (type $19 (shared (array v128)))
26+
;; CHECK-NEXT: (type $16 (sub final $7 (func (param (ref (shared func)) f32 i64) (result (ref nofunc)))))
27+
;; CHECK-NEXT: (type $17 (sub (shared (func (param (ref $11) i64 (ref (shared extern)))))))
28+
;; CHECK-NEXT: (type $18 (sub (struct)))
29+
;; CHECK-NEXT: (type $19 (sub $7 (func (param (ref null (shared func)) f32 i64) (result (ref $9)))))
3030
;; CHECK-NEXT: )
3131
;; CHECK-NEXT:
3232
;; CHECK-NEXT: Inhabitable types:
3333
;; CHECK-NEXT:
3434
;; CHECK-NEXT: Built 20 types:
3535
;; CHECK-NEXT: (rec
36-
;; CHECK-NEXT: (type $0 (sub (struct (field (mut i16)) (field (mut (ref $2))) (field (mut (ref null $2))))))
37-
;; CHECK-NEXT: (type $1 (sub (func (param (ref $1)) (result f64 (ref $0) f32 structref))))
38-
;; CHECK-NEXT: (type $2 (sub (shared (struct (field (mut (ref null (shared extern)))) (field (mut (ref null $2)))))))
39-
;; CHECK-NEXT: (type $3 (sub (shared (struct))))
36+
;; CHECK-NEXT: (type $0 (sub (func (param (ref (shared func)) f32 i64) (result (ref $7)))))
37+
;; CHECK-NEXT: (type $1 (sub (array (mut i16))))
38+
;; CHECK-NEXT: (type $2 (sub (shared (func (param f64 (ref null $5) (ref $9)) (result (ref null $6) (ref $3) (ref null $8))))))
39+
;; CHECK-NEXT: (type $3 (sub final $2 (shared (func (param f64 (ref null $1) (ref $0)) (result (ref $6) (ref $3) (ref $8))))))
40+
;; CHECK-NEXT: (type $4 (sub $1 (array (mut i16))))
41+
;; CHECK-NEXT: (type $5 (sub final $4 (array (mut i16))))
42+
;; CHECK-NEXT: (type $6 (sub $2 (shared (func (param f64 arrayref funcref) (result (ref $6) (ref $3) (ref $8))))))
43+
;; CHECK-NEXT: (type $7 (sub $0 (func (param (ref (shared func)) f32 i64) (result (ref $9)))))
44+
;; CHECK-NEXT: (type $8 (sub final $0 (func (param (ref (shared func)) f32 i64) (result (ref $9)))))
45+
;; CHECK-NEXT: (type $9 (sub $7 (func (param (ref (shared func)) f32 i64) (result (ref $9)))))
46+
;; CHECK-NEXT: (type $10 (sub $2 (shared (func (param f64 (ref null $5) (ref func)) (result (ref $6) (ref $3) (ref null $8))))))
47+
;; CHECK-NEXT: (type $11 (sub final $2 (shared (func (param f64 arrayref (ref $0)) (result (ref $6) (ref $3) (ref $8))))))
48+
;; CHECK-NEXT: (type $12 (sub $10 (shared (func (param f64 (ref null $1) funcref) (result (ref $6) (ref (shared nofunc)) (ref $8))))))
4049
;; CHECK-NEXT: )
4150
;; CHECK-NEXT: (rec
42-
;; CHECK-NEXT: (type $4 (sub (array i32)))
43-
;; CHECK-NEXT: (type $5 (sub $4 (array i32)))
44-
;; CHECK-NEXT: (type $6 (shared (func (param (ref null $3)) (result i32))))
45-
;; CHECK-NEXT: (type $7 (sub $2 (shared (struct (field (mut (ref null (shared extern)))) (field (mut (ref null $2))) (field (mut (ref null $3))) (field (mut i16)) (field (mut (ref null $7))) (field (mut (ref null $7)))))))
46-
;; CHECK-NEXT: (type $8 (sub $0 (struct (field (mut i16)) (field (mut (ref $2))) (field (mut (ref null $2))))))
51+
;; CHECK-NEXT: (type $13 (sub (func (param f64 f32))))
52+
;; CHECK-NEXT: (type $14 (func (param i64) (result v128 (ref null $2) (ref $12) f32 v128)))
53+
;; CHECK-NEXT: (type $15 (sub final $12 (shared (func (param f64 anyref funcref) (result (ref $6) (ref (shared nofunc)) (ref $8))))))
4754
;; CHECK-NEXT: )
4855
;; CHECK-NEXT: (rec
49-
;; CHECK-NEXT: (type $9 (shared (array i32)))
50-
;; CHECK-NEXT: (type $10 (sub $5 (array i32)))
51-
;; CHECK-NEXT: (type $11 (func (result i32)))
52-
;; CHECK-NEXT: (type $12 (sub (shared (array (ref $3)))))
53-
;; CHECK-NEXT: (type $13 (sub (shared (func (param (ref null $19) v128) (result (ref null $12))))))
54-
;; CHECK-NEXT: (type $14 (sub final $12 (shared (array (ref $3)))))
55-
;; CHECK-NEXT: (type $15 (sub (shared (func (param i31ref (ref $5)) (result i32)))))
56-
;; CHECK-NEXT: (type $16 (sub $5 (array i32)))
57-
;; CHECK-NEXT: (type $17 (sub (func (result (ref $7)))))
58-
;; CHECK-NEXT: (type $18 (sub (array (mut i8))))
59-
;; CHECK-NEXT: (type $19 (shared (array v128)))
56+
;; CHECK-NEXT: (type $16 (sub final $7 (func (param (ref (shared func)) f32 i64) (result (ref nofunc)))))
57+
;; CHECK-NEXT: (type $17 (sub (shared (func (param (ref $11) i64 (ref (shared extern)))))))
58+
;; CHECK-NEXT: (type $18 (sub (struct)))
59+
;; CHECK-NEXT: (type $19 (sub $7 (func (param (ref null (shared func)) f32 i64) (result (ref $9)))))
6060
;; CHECK-NEXT: )

0 commit comments

Comments
 (0)