@@ -46,7 +46,9 @@ struct HeapTypeGeneratorImpl {
4646 struct SignatureKind {};
4747 struct StructKind {};
4848 struct ArrayKind {};
49- using HeapTypeKind = std::variant<SignatureKind, StructKind, ArrayKind>;
49+ struct ContinuationKind {};
50+ using HeapTypeKind =
51+ std::variant<SignatureKind, StructKind, ArrayKind, ContinuationKind>;
5052 std::vector<HeapTypeKind> typeKinds;
5153
5254 // For each type, the index one past the end of its recursion group, used to
@@ -115,6 +117,10 @@ struct HeapTypeGeneratorImpl {
115117 return size;
116118 }
117119
120+ // We can only emit continuations after emitting a valid signature for them,
121+ // as the signature must appear first.
122+ bool canEmitContinuation = false ;
123+
118124 void planType (size_t i,
119125 size_t numRoots,
120126 size_t remaining,
@@ -233,9 +239,24 @@ struct HeapTypeGeneratorImpl {
233239 builder[i].setShared (HeapType (builder[*describedIndices[i]]).getShared ());
234240 } else {
235241 // This is a root type with no supertype. Choose a kind for this type.
236- typeKinds.emplace_back (generateHeapTypeKind ());
237- builder[i].setShared (
238- !features.hasSharedEverything () || rand.oneIn (2 ) ? Unshared : Shared);
242+ auto kind = generateHeapTypeKind ();
243+ if (std::get_if<ContinuationKind>(&kind) && !canEmitContinuation) {
244+ // No signature for a continuation. Emit a signature so we can emit one
245+ // later.
246+ kind = SignatureKind{};
247+ }
248+ typeKinds.emplace_back (kind);
249+ // Continuations cannot be shared, but other things can.
250+ auto shared = Unshared;
251+ if (features.hasSharedEverything () &&
252+ !std::get_if<ContinuationKind>(&kind) && rand.oneIn (2 )) {
253+ shared = Shared;
254+ }
255+ builder[i].setShared (shared);
256+ // Once we emit a non-shared signature, continuations are possible.
257+ if (std::get_if<SignatureKind>(&kind) && shared == Unshared) {
258+ canEmitContinuation = true ;
259+ }
239260 }
240261
241262 // Plan this descriptor chain for this type if it is not already determined
@@ -283,6 +304,8 @@ struct HeapTypeGeneratorImpl {
283304 builder[index] = generateStruct (share, isDesc);
284305 } else if (std::get_if<ArrayKind>(&kind)) {
285306 builder[index] = generateArray (share);
307+ } else if (std::get_if<ContinuationKind>(&kind)) {
308+ builder[index] = generateContinuation (share);
286309 } else {
287310 WASM_UNREACHABLE (" unexpected kind" );
288311 }
@@ -300,7 +323,9 @@ struct HeapTypeGeneratorImpl {
300323 builder[index] = generateSubArray (supertype.getArray ());
301324 break ;
302325 case wasm::HeapTypeKind::Cont:
303- WASM_UNREACHABLE (" TODO: cont" );
326+ builder[index] =
327+ generateSubContinuation (supertype.getContinuation ());
328+ break ;
304329 case wasm::HeapTypeKind::Basic:
305330 WASM_UNREACHABLE (" unexpected kind" );
306331 }
@@ -310,11 +335,20 @@ struct HeapTypeGeneratorImpl {
310335
311336 HeapType::BasicHeapType generateBasicHeapType (Shareability share) {
312337 // Choose bottom types more rarely.
313- // TODO: string and cont types
338+ // TODO: string types
314339 if (rand.oneIn (16 )) {
315- HeapType ht =
316- rand.pick (HeapType::noext, HeapType::nofunc, HeapType::none);
317- return ht.getBasic (share);
340+ std::vector<HeapType> bottoms{
341+ HeapType::noext, HeapType::nofunc, HeapType::none};
342+ // Continuations cannot be shared.
343+ if (features.hasStackSwitching () && share == Unshared) {
344+ bottoms.push_back (HeapType::nocont);
345+ }
346+ return rand.pick (bottoms).getBasic (share);
347+ }
348+
349+ // Sometimes emit shared in unshared contexts.
350+ if (share == Unshared && features.hasSharedEverything () && rand.oneIn (4 )) {
351+ share = Shared;
318352 }
319353
320354 std::vector<HeapType> options{HeapType::func,
@@ -324,15 +358,14 @@ struct HeapTypeGeneratorImpl {
324358 HeapType::i31,
325359 HeapType::struct_,
326360 HeapType::array};
361+ if (features.hasStackSwitching () && share == Unshared) {
362+ options.push_back (HeapType::cont);
363+ }
327364 // Avoid shared exn, which we cannot generate.
328365 if (features.hasExceptionHandling () && share == Unshared) {
329366 options.push_back (HeapType::exn);
330367 }
331368 auto ht = rand.pick (options);
332- if (share == Unshared && features.hasSharedEverything () &&
333- ht != HeapType::exn && rand.oneIn (2 )) {
334- share = Shared;
335- }
336369 return ht.getBasic (share);
337370 }
338371
@@ -443,6 +476,22 @@ struct HeapTypeGeneratorImpl {
443476
444477 Array generateArray (Shareability share) { return {generateField (share)}; }
445478
479+ Continuation generateContinuation (Shareability share) {
480+ auto type = pickKind<SignatureKind>(share);
481+ // There must be signatures to pick from.
482+ assert (type);
483+ return Continuation (*type);
484+ }
485+
486+ Continuation generateSubContinuation (Continuation super) {
487+ auto subType = pickSubHeapType (super.type );
488+ if (subType.isBasic ()) {
489+ // We cannot use a bottom type here.
490+ subType = super.type ;
491+ }
492+ return Continuation (subType);
493+ }
494+
446495 template <typename Kind>
447496 std::vector<HeapType> getKindCandidates (Shareability share) {
448497 std::vector<HeapType> candidates;
@@ -518,6 +567,23 @@ struct HeapTypeGeneratorImpl {
518567 }
519568 }
520569
570+ HeapType pickSubCont (Shareability share) {
571+ auto choice = rand.upTo (8 );
572+ switch (choice) {
573+ case 0 :
574+ return HeapTypes::cont.getBasic (share);
575+ case 1 :
576+ return HeapTypes::nocont.getBasic (share);
577+ default : {
578+ if (auto type = pickKind<ContinuationKind>(share)) {
579+ return *type;
580+ }
581+ HeapType ht = (choice % 2 ) ? HeapType::cont : HeapType::nocont;
582+ return ht.getBasic (share);
583+ }
584+ }
585+ }
586+
521587 HeapType pickSubEq (Shareability share) {
522588 auto choice = rand.upTo (16 );
523589 switch (choice) {
@@ -585,6 +651,8 @@ struct HeapTypeGeneratorImpl {
585651 auto * kind = &typeKinds[it->second ];
586652 if (std::get_if<SignatureKind>(kind)) {
587653 return HeapTypes::nofunc.getBasic (share);
654+ } else if (std::get_if<ContinuationKind>(kind)) {
655+ return HeapTypes::nocont.getBasic (share);
588656 } else {
589657 return HeapTypes::none.getBasic (share);
590658 }
@@ -603,7 +671,7 @@ struct HeapTypeGeneratorImpl {
603671 case HeapType::func:
604672 return pickSubFunc (share);
605673 case HeapType::cont:
606- WASM_UNREACHABLE ( " not implemented " );
674+ return pickSubCont (share );
607675 case HeapType::any:
608676 return pickSubAny (share);
609677 case HeapType::eq:
@@ -654,6 +722,9 @@ struct HeapTypeGeneratorImpl {
654722 } else if (std::get_if<SignatureKind>(kind)) {
655723 candidates.push_back (HeapTypes::func.getBasic (share));
656724 return rand.pick (candidates);
725+ } else if (std::get_if<ContinuationKind>(kind)) {
726+ candidates.push_back (HeapTypes::cont.getBasic (share));
727+ return rand.pick (candidates);
657728 } else {
658729 WASM_UNREACHABLE (" unexpected kind" );
659730 }
@@ -685,7 +756,7 @@ struct HeapTypeGeneratorImpl {
685756 case HeapType::nofunc:
686757 return pickSubFunc (share);
687758 case HeapType::nocont:
688- WASM_UNREACHABLE ( " not implemented " );
759+ return pickSubCont (share );
689760 case HeapType::noext:
690761 candidates.push_back (HeapTypes::ext.getBasic (share));
691762 break ;
@@ -798,13 +869,21 @@ struct HeapTypeGeneratorImpl {
798869 }
799870
800871 HeapTypeKind generateHeapTypeKind () {
801- switch (rand.upTo (3 )) {
872+ // Emit continuations less frequently, as we need fewer of them to get
873+ // interesting results.
874+ uint32_t numKinds = features.hasStackSwitching () ? 7 : 6 ;
875+ switch (rand.upTo (numKinds)) {
802876 case 0 :
803- return SignatureKind{};
804877 case 1 :
805- return StructKind {};
878+ return SignatureKind {};
806879 case 2 :
880+ case 3 :
881+ return StructKind{};
882+ case 4 :
883+ case 5 :
807884 return ArrayKind{};
885+ case 6 :
886+ return ContinuationKind{};
808887 }
809888 WASM_UNREACHABLE (" unexpected index" );
810889 }
@@ -871,7 +950,7 @@ struct Inhabitator {
871950
872951Inhabitator::Variance Inhabitator::getVariance (FieldPos fieldPos) {
873952 auto [type, idx] = fieldPos;
874- assert (!type.isBasic () && !type.isSignature ());
953+ assert (!type.isBasic () && !type.isSignature () && !type. isContinuation () );
875954 auto field = GCTypeUtils::getField (type, idx);
876955 assert (field);
877956 if (field->mutable_ == Mutable) {
@@ -929,9 +1008,9 @@ void Inhabitator::markNullable(FieldPos field) {
9291008
9301009void Inhabitator::markBottomRefsNullable () {
9311010 for (auto type : types) {
932- if (type.isSignature ()) {
933- // Functions can always be instantiated, even if their types refer to
934- // uninhabitable types.
1011+ if (type.isSignature () || type. isContinuation () ) {
1012+ // Functions/continuations can always be instantiated, even if their types
1013+ // refer to uninhabitable types.
9351014 continue ;
9361015 }
9371016 auto children = type.getTypeChildren ();
@@ -951,9 +1030,9 @@ void Inhabitator::markExternRefsNullable() {
9511030 // TODO: Remove this once the fuzzer imports externref globals or gets some
9521031 // other way to instantiate externrefs.
9531032 for (auto type : types) {
954- if (type.isSignature ()) {
955- // Functions can always be instantiated, even if their types refer to
956- // uninhabitable types.
1033+ if (type.isSignature () || type. isContinuation () ) {
1034+ // Functions/continuations can always be instantiated, even if their types
1035+ // refer to uninhabitable types.
9571036 continue ;
9581037 }
9591038 auto children = type.getTypeChildren ();
@@ -1062,8 +1141,9 @@ void Inhabitator::breakNonNullableCycles() {
10621141 // Skip references to function types. Functions types can always be
10631142 // instantiated since functions can be created even with uninhabitable
10641143 // params or results. Function references therefore break cycles that
1065- // would otherwise produce uninhabitability.
1066- if (heapType.isSignature ()) {
1144+ // would otherwise produce uninhabitability. (Continuations are
1145+ // similar.)
1146+ if (heapType.isSignature () || heapType.isContinuation ()) {
10671147 ++index;
10681148 continue ;
10691149 }
@@ -1156,8 +1236,15 @@ std::vector<HeapType> Inhabitator::build() {
11561236 builder[i] = copy;
11571237 continue ;
11581238 }
1159- case HeapTypeKind::Cont:
1160- WASM_UNREACHABLE (" TODO: cont" );
1239+ case HeapTypeKind::Cont: {
1240+ Continuation copy = type.getContinuation ();
1241+ auto heapType = copy.type ;
1242+ if (auto it = typeIndices.find (heapType); it != typeIndices.end ()) {
1243+ copy.type = builder.getTempHeapType (it->second );
1244+ }
1245+ builder[i] = copy;
1246+ continue ;
1247+ }
11611248 case HeapTypeKind::Basic:
11621249 break ;
11631250 }
@@ -1190,9 +1277,12 @@ std::vector<HeapType> Inhabitator::build() {
11901277 builder[i].setShared (types[i].getShared ());
11911278 }
11921279
1193- auto built = builder.build ();
1194- assert (!built.getError () && " unexpected build error" );
1195- return *built;
1280+ auto result = builder.build ();
1281+ if (auto * err = result.getError ()) {
1282+ Fatal () << " Failed to build heap types: " << err->reason << " at index "
1283+ << err->index ;
1284+ }
1285+ return *result;
11961286}
11971287
11981288} // anonymous namespace
0 commit comments