14
14
* limitations under the License.
15
15
*/
16
16
17
+ #include < cassert>
17
18
#include < variant>
18
19
19
20
#include " ir/gc-type-utils.h"
@@ -31,6 +32,9 @@ struct HeapTypeGeneratorImpl {
31
32
TypeBuilder& builder;
32
33
std::vector<std::vector<Index>>& subtypeIndices;
33
34
std::vector<std::optional<Index>> supertypeIndices;
35
+ std::vector<std::optional<Index>>& descriptorIndices;
36
+ std::vector<std::optional<Index>> describedIndices;
37
+ std::vector<size_t > descriptorChainLengths;
34
38
Random& rand;
35
39
FeatureSet features;
36
40
@@ -57,9 +61,13 @@ struct HeapTypeGeneratorImpl {
57
61
FuzzParams params;
58
62
59
63
HeapTypeGeneratorImpl (Random& rand, FeatureSet features, size_t n)
60
- : result{TypeBuilder (n), std::vector<std::vector<Index>>(n)},
64
+ : result{TypeBuilder (n),
65
+ std::vector<std::vector<Index>>(n),
66
+ std::vector<std::optional<Index>>(n)},
61
67
builder (result.builder), subtypeIndices(result.subtypeIndices),
62
- supertypeIndices (n), rand(rand), features(features) {
68
+ supertypeIndices (n), descriptorIndices(result.descriptorIndices),
69
+ describedIndices (n), descriptorChainLengths(n), rand(rand),
70
+ features (features) {
63
71
// Set up the subtype relationships. Start with some number of root types,
64
72
// then after that start creating subtypes of existing types. Determine the
65
73
// top-level kind and shareability of each type in advance so that we can
@@ -95,32 +103,170 @@ struct HeapTypeGeneratorImpl {
95
103
assert (start + size <= builder.size ());
96
104
builder.createRecGroup (start, size);
97
105
106
+ // The indices of types that need descriptors and the total number of
107
+ // remaining descriptors we have committed to create in this group.
108
+ std::vector<Index> describees;
109
+ size_t numPlannedDescriptors = 0 ;
110
+
98
111
size_t end = start + size;
99
112
for (size_t i = start; i < end; ++i) {
100
113
recGroupEnds.push_back (end);
101
- planType (i, numRoots);
114
+ planType (i, numRoots, end - i, describees, numPlannedDescriptors );
102
115
}
103
116
return size;
104
117
}
105
118
106
- void planType (size_t i, size_t numRoots) {
119
+ void planType (size_t i,
120
+ size_t numRoots,
121
+ size_t remaining,
122
+ std::vector<Index>& describees,
123
+ size_t & numPlannedDescriptors) {
124
+ assert (remaining >= numPlannedDescriptors);
107
125
typeIndices.insert ({builder[i], i});
108
126
// Everything is a subtype of itself.
109
127
subtypeIndices[i].push_back (i);
110
- if (i < numRoots || rand.oneIn (2 )) {
128
+
129
+ // We may pick a supertype. If we have a described type that itself has a
130
+ // supertype, then we must choose that supertype's descriptor as our
131
+ // supertype.
132
+ std::optional<Index> super;
133
+
134
+ // Pick a type to describe, or choose not to describe a type by
135
+ // picking the one-past-the-end index. If all of the remaining types must be
136
+ // descriptors, then we must choose a describee.
137
+ Index describee =
138
+ rand.upTo (describees.size () + (remaining != numPlannedDescriptors));
139
+
140
+ bool isDescriptor = false ;
141
+ if (describee != describees.size ()) {
142
+ isDescriptor = true ;
143
+ --numPlannedDescriptors;
144
+
145
+ // If the intended described type has a supertype with a descriptor, then
146
+ // that descriptor must be the supertype of the type we intend to
147
+ // generate. However, we may not have generated that descriptor yet,
148
+ // meaning it is unavailable to be the supertype of the current type.
149
+ // Detect that situation and plan to generate the missing supertype
150
+ // instead.
151
+ Index described;
152
+ while (true ) {
153
+ assert (describee < describees.size ());
154
+ described = describees[describee];
155
+ auto describedSuper = supertypeIndices[described];
156
+ if (!describedSuper) {
157
+ // The described type has no supertype, so there is no problem.
158
+ break ;
159
+ }
160
+ if (descriptorChainLengths[*describedSuper] == 0 ) {
161
+ // The supertype of the described type will not have a descriptor,
162
+ // so there is no problem.
163
+ break ;
164
+ }
165
+ if ((super = descriptorIndices[*describedSuper])) {
166
+ // The descriptor of the described type's supertype, which must become
167
+ // the current type's supertype, has already been generated. There is
168
+ // no problem.
169
+ break ;
170
+ }
171
+ // The necessary supertype does not yet exist. Find its described type
172
+ // so we can try to generate the missing supertype instead.
173
+ for (describee = 0 ; describee < describees.size (); ++describee) {
174
+ if (describees[describee] == *describedSuper) {
175
+ break ;
176
+ }
177
+ }
178
+ // Go back and check whether the new intended type can be generated.
179
+ continue ;
180
+ }
181
+
182
+ // We have locked in the type we will describe.
183
+ std::swap (describees[describee], describees.back ());
184
+ describees.pop_back ();
185
+ descriptorIndices[described] = i;
186
+ describedIndices[i] = described;
187
+ builder[described].descriptor (builder[i]);
188
+ builder[i].describes (builder[described]);
189
+
190
+ // The length of the descriptor chain from this type is determined by the
191
+ // planned length of the chain from its described type.
192
+ descriptorChainLengths[i] = descriptorChainLengths[described] - 1 ;
193
+ }
194
+
195
+ --remaining;
196
+ assert (remaining >= numPlannedDescriptors);
197
+ size_t remainingUncommitted = remaining - numPlannedDescriptors;
198
+
199
+ if (!super && i >= numRoots && rand.oneIn (2 )) {
200
+ // Try to pick a supertype. The supertype must be a descriptor type if and
201
+ // only if we are currently generating a descriptor type. Furthermore, we
202
+ // must have space left in the current chain if it exists, or else in the
203
+ // rec group, to mirror the supertype's descriptor chain, if it has one.
204
+ // Finally, if this is a descriptor, the sharedness of the described type
205
+ // and supertype must match.
206
+ size_t maxChain =
207
+ isDescriptor ? descriptorChainLengths[i] : remainingUncommitted;
208
+ std::vector<Index> candidates;
209
+ candidates.reserve (i);
210
+ for (Index candidate = 0 ; candidate < i; ++candidate) {
211
+ bool descMatch = bool (describedIndices[candidate]) == isDescriptor;
212
+ bool chainMatch = descriptorChainLengths[candidate] <= maxChain;
213
+ bool shareMatch = !isDescriptor ||
214
+ HeapType (builder[candidate]).getShared () ==
215
+ HeapType (builder[*describedIndices[i]]).getShared ();
216
+ if (descMatch && chainMatch && shareMatch) {
217
+ candidates.push_back (candidate);
218
+ }
219
+ }
220
+ if (!candidates.empty ()) {
221
+ super = rand.pick (candidates);
222
+ }
223
+ }
224
+
225
+ // Set up the builder entry and type kind for this type.
226
+ if (super) {
227
+ typeKinds.push_back (typeKinds[*super]);
228
+ builder[i].subTypeOf (builder[*super]);
229
+ builder[i].setShared (HeapType (builder[*super]).getShared ());
230
+ supertypeIndices[i] = *super;
231
+ subtypeIndices[*super].push_back (i);
232
+ } else if (isDescriptor) {
233
+ // Descriptor types must be structs and their sharedness must match their
234
+ // described types.
235
+ typeKinds.push_back (StructKind{});
236
+ builder[i].setShared (HeapType (builder[*describedIndices[i]]).getShared ());
237
+ } else {
111
238
// This is a root type with no supertype. Choose a kind for this type.
112
239
typeKinds.emplace_back (generateHeapTypeKind ());
113
240
builder[i].setShared (
114
241
!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]);
242
+ }
243
+
244
+ // Plan this descriptor chain for this type if it is not already determined
245
+ // by a described type. Only structs may have descriptor chains.
246
+ if (!isDescriptor && std::get_if<StructKind>(&typeKinds.back ()) &&
247
+ remainingUncommitted && features.hasCustomDescriptors ()) {
248
+ if (super) {
249
+ // If we have a supertype, our descriptor chain must be at least as
250
+ // long as the supertype's descriptor chain.
251
+ size_t length = descriptorChainLengths[*super];
252
+ if (rand.oneIn (2 )) {
253
+ length += rand.upToSquared (remainingUncommitted - length);
254
+ }
255
+ descriptorChainLengths[i] = length;
256
+ numPlannedDescriptors += length;
257
+ } else {
258
+ // We can choose to start a brand new chain at this type.
259
+ if (rand.oneIn (2 )) {
260
+ size_t length = rand.upToSquared (remainingUncommitted);
261
+ descriptorChainLengths[i] = length;
262
+ numPlannedDescriptors += length;
263
+ }
264
+ }
265
+ }
266
+ // If this type has a descriptor chain, then we need to be able to
267
+ // choose to generate the next type in the chain in the future.
268
+ if (descriptorChainLengths[i]) {
269
+ describees.push_back (i);
124
270
}
125
271
}
126
272
@@ -557,9 +703,9 @@ struct HeapTypeGeneratorImpl {
557
703
// from JS). There are also no subtypes to consider, so just return.
558
704
return super;
559
705
}
560
- auto nullability = super.nullability == NonNullable
561
- ? NonNullable
562
- : rand. oneIn ( 2 ) ? Nullable : NonNullable;
706
+ auto nullability = super.nullability == NonNullable ? NonNullable
707
+ : rand. oneIn ( 2 ) ? Nullable
708
+ : NonNullable;
563
709
return {pickSubHeapType (super.type ), nullability};
564
710
}
565
711
@@ -736,9 +882,13 @@ void Inhabitator::markNullable(FieldPos field) {
736
882
switch (getVariance (field)) {
737
883
case Covariant:
738
884
// Mark the field null in all supertypes. If the supertype field is
739
- // already nullable or does not exist, that's ok and this will have no
740
- // effect.
885
+ // already nullable, that's ok and this will have no effect.
741
886
while (auto super = curr.getDeclaredSuperType ()) {
887
+ if (super->isStruct () && idx >= super->getStruct ().fields .size ()) {
888
+ // Do not mark fields that don't exist as nullable; this index may be
889
+ // used by a descriptor.
890
+ break ;
891
+ }
742
892
nullables.insert ({*super, idx});
743
893
curr = *super;
744
894
}
@@ -818,15 +968,17 @@ void Inhabitator::markExternRefsNullable() {
818
968
//
819
969
// [1]: https://en.wikipedia.org/wiki/Feedback_arc_set
820
970
void Inhabitator::breakNonNullableCycles () {
821
- // The types reachable from each heap type.
822
- // TODO: Include descriptors .
971
+ // The types reachable from each heap type. Descriptors are modeled as
972
+ // additional non-nullable reference types appended to the other children .
823
973
std::unordered_map<HeapType, std::vector<Type>> children;
824
974
825
975
auto getChildren = [&children](HeapType type) {
826
976
auto [it, inserted] = children.insert ({type, {}});
827
977
if (inserted) {
828
- // TODO: Add descriptors.
829
978
it->second = type.getTypeChildren ();
979
+ if (auto desc = type.getDescriptorType ()) {
980
+ it->second .push_back (Type (*desc, NonNullable, Exact));
981
+ }
830
982
}
831
983
return it->second ;
832
984
};
@@ -865,7 +1017,14 @@ void Inhabitator::breakNonNullableCycles() {
865
1017
visitType (root);
866
1018
867
1019
while (path.size ()) {
868
- auto [curr, index] = path.back ();
1020
+ auto & [curr, index] = path.back ();
1021
+ // We may have visited this type again after searching through a
1022
+ // descriptor backedge. If we've already finished visiting this type on
1023
+ // that later visit, we don't need to continue this earlier visit.
1024
+ if (visited.count (curr)) {
1025
+ finishType ();
1026
+ continue ;
1027
+ }
869
1028
const auto & children = getChildren (curr);
870
1029
871
1030
while (index < children.size ()) {
@@ -903,11 +1062,15 @@ void Inhabitator::breakNonNullableCycles() {
903
1062
continue ;
904
1063
}
905
1064
// If this ref forms a cycle, break the cycle by marking it nullable and
906
- // continue.
907
- if (auto it = visiting.find (heapType); it != visiting.end ()) {
908
- markNullable ({curr, index});
909
- ++index;
910
- continue ;
1065
+ // continue. We can't do this for descriptors, though. For those we will
1066
+ // continue searching as if for any other non-nullable reference and
1067
+ // eventually find a non-descriptor backedge.
1068
+ if (!curr.getDescriptorType () || index != children.size () - 1 ) {
1069
+ if (auto it = visiting.find (heapType); it != visiting.end ()) {
1070
+ markNullable ({curr, index});
1071
+ ++index;
1072
+ continue ;
1073
+ }
911
1074
}
912
1075
break ;
913
1076
}
@@ -1002,7 +1165,7 @@ std::vector<HeapType> Inhabitator::build() {
1002
1165
start += size;
1003
1166
}
1004
1167
1005
- // Establish supertypes and finality.
1168
+ // Establish supertypes, descriptors, and finality.
1006
1169
for (size_t i = 0 ; i < types.size (); ++i) {
1007
1170
if (auto super = types[i].getDeclaredSuperType ()) {
1008
1171
if (auto it = typeIndices.find (*super); it != typeIndices.end ()) {
@@ -1011,6 +1174,12 @@ std::vector<HeapType> Inhabitator::build() {
1011
1174
builder[i].subTypeOf (*super);
1012
1175
}
1013
1176
}
1177
+ if (auto desc = types[i].getDescriptorType ()) {
1178
+ auto it = typeIndices.find (*desc);
1179
+ assert (it != typeIndices.end ());
1180
+ builder[i].descriptor (builder[it->second ]);
1181
+ builder[it->second ].describes (builder[i]);
1182
+ }
1014
1183
builder[i].setOpen (types[i].isOpen ());
1015
1184
builder[i].setShared (types[i].getShared ());
1016
1185
}
@@ -1094,6 +1263,11 @@ bool isUninhabitable(HeapType type,
1094
1263
if (!inserted) {
1095
1264
return true ;
1096
1265
}
1266
+ if (auto desc = type.getDescriptorType ()) {
1267
+ if (isUninhabitable (Type (*desc, NonNullable, Exact), visited, visiting)) {
1268
+ return true ;
1269
+ }
1270
+ }
1097
1271
switch (type.getKind ()) {
1098
1272
case HeapTypeKind::Struct:
1099
1273
for (auto & field : type.getStruct ().fields ) {
0 commit comments