From bfdc006c4a7cf44b754473bbeef8fbdc18b2f9e7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Mar 2026 10:44:07 -0700 Subject: [PATCH 01/24] start --- src/tools/fuzzing.h | 2 + src/tools/fuzzing/fuzzing.cpp | 149 ++++++++++++++++++++++++++++++++++ src/wasm-builder.h | 26 ++++++ 3 files changed, 177 insertions(+) diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 8989105af30..6d6a52b803d 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -529,6 +529,8 @@ class TranslateToFuzzReader { Expression* makeRefCast(Type type); Expression* makeRefGetDesc(Type type); Expression* makeBrOn(Type type); + Expression* makeContBind(Type type); + Expression* makeResume(Type type); // Decide to emit a signed Struct/ArrayGet sometimes, when the field is // packed. diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 8ee053eb371..c47d5df7380 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -2570,6 +2570,14 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) { options.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeRefGetDesc); } + if (heapType.isContinuation()) { + options.add(FeatureSet::ReferenceTypes | FeatureSet::StackSwitching, + &Self::makeContBind); + if (canMakeControlFlow) { + options.add(FeatureSet::ReferenceTypes | FeatureSet::StackSwitching, + &Self::makeResume); + } + } } if (wasm.features.hasGC()) { if (typeStructFields.find(type) != typeStructFields.end()) { @@ -4114,6 +4122,7 @@ Expression* TranslateToFuzzReader::makeCompoundRef(Type type) { case HeapTypeKind::Cont: { auto funcType = heapType.getContinuation().type; return builder.makeContNew(heapType, makeTrappingRefUse(funcType)); + // todo contbind, resume } case HeapTypeKind::Basic: break; @@ -5457,6 +5466,146 @@ Expression* TranslateToFuzzReader::makeBrOn(Type type) { builder.makeBrOn(op, targetName, make(refType), castType)); } +Expression* TranslateToFuzzReader::makeContBind(Type type) { + auto sig = type.getHeapType().getContinuation().type.getSignature(); + // Add a single param to be bound. TODO: Add multiple, and look in + // interestingHeapTypes. + std::vector newParams; + for (auto t : sig.params) { + newParams.push_back(t); + } + auto newParam = getConcreteType(); + newParams.push_back(newParam); + auto newSig = Signature(Type(newParams), sig.results); + auto newCont = Continuation(newSig); + auto newType = Type(newCont, NonNullable, Exact); + std::vector newArgs{make(newParam)}; + return builder.makeContBind(type.getHeapType(), newArgs, make(newType)); +} + +Expression* TranslateToFuzzReader::makeResume(Type type) { + // TODO + if (funcContext->breakableStack.empty()) { + return makeTrivial(type); + } + // We need to find a proper target to break to; try a few times. Finding the + // target is harder than flowing out the proper type, so focus on the target, + // and fix up the flowing type later. That is, once we find a target to break + // to, we can then either drop ourselves or wrap ourselves in a block + + // another value, so that we return the proper thing here (which is done below + // in fixFlowingType). + int tries = fuzzParams->TRIES; + Name targetName; + Type targetType; + while (--tries >= 0) { + auto* target = pick(funcContext->breakableStack); + targetName = getTargetName(target); + targetType = getTargetType(target); + // We can send any reference type, or no value at all, but nothing else. + if (targetType.isRef() || targetType == Type::none) { + break; + } + } + if (tries < 0) { + return makeTrivial(type); + } + + auto fixFlowingType = [&](Expression* brOn) -> Expression* { + if (Type::isSubType(brOn->type, type)) { + // Already of the proper type. + return brOn; + } + if (type == Type::none) { + // We just need to drop whatever it is. + return builder.makeDrop(brOn); + } + // We need to replace the type with something else. Drop the BrOn if we need + // to, and append a value with the proper type. + if (brOn->type != Type::none) { + brOn = builder.makeDrop(brOn); + } + return builder.makeSequence(brOn, make(type)); + }; + + // We found something to break to. Figure out which BrOn variants we can + // send. + if (targetType == Type::none) { + // BrOnNull is the only variant that sends no value. + return fixFlowingType( + builder.makeBrOn(BrOnNull, targetName, make(getReferenceType()))); + } + + // We are sending a reference type to the target. All other BrOn variants can + // do that. + assert(targetType.isRef()); + // BrOnNonNull can handle sending any reference. The casts are more limited. + auto op = BrOnNonNull; + if (targetType.isCastable()) { + op = pick(BrOnNonNull, BrOnCast, BrOnCastFail); + } + Type castType = Type::none; + Type refType; + switch (op) { + case BrOnNonNull: { + // The sent type is the non-nullable version of the reference, so any ref + // of that type is ok, nullable or not. + refType = targetType.with(getNullability()); + break; + } + case BrOnCast: { + // The sent type is the heap type we cast to, with the input type's + // nullability, so the combination of the two must be a subtype of + // targetType. + castType = getSubType(targetType); + if (castType.isExact() && !wasm.features.hasCustomDescriptors()) { + // This exact cast is only valid if its input has the same type (or the + // only possible strict subtype, bottom). + refType = castType; + } else { + // The ref's type must be castable to castType, or we'd not validate. + // But it can also be a subtype, which will trivially also succeed (so + // do that more rarely). Pick subtypes rarely, as they make the cast + // trivial. + refType = oneIn(5) ? getSubType(castType) : getSuperType(castType); + } + if (targetType.isNonNullable()) { + // And it must have the right nullability for the target, as mentioned + // above: if the target type is non-nullable then either the ref or the + // cast types must be. + if (!refType.isNonNullable() && !castType.isNonNullable()) { + // Pick one to make non-nullable. + if (oneIn(2)) { + refType = Type(refType.getHeapType(), NonNullable); + } else { + castType = Type(castType.getHeapType(), NonNullable); + } + } + } + break; + } + case BrOnCastFail: { + // The sent type is the ref's type, with adjusted nullability (if the cast + // allows nulls then no null can fail the cast, and what is sent is non- + // nullable). First, pick a ref type that we can send to the target. + refType = getSubType(targetType); + // See above on BrOnCast, but flipped. + castType = oneIn(5) ? getSuperType(refType) : getSubType(refType); + castType = castType.withInexactIfNoCustomDescs(wasm.features); + // There is no nullability to adjust: if targetType is non-nullable then + // both refType and castType are as well, as subtypes of it. But we can + // also allow castType to be nullable (it is not sent to the target). + if (castType.isNonNullable() && oneIn(2)) { + castType = Type(castType.getHeapType(), Nullable); + } + } break; + default: { + WASM_UNREACHABLE("bad br_on op"); + } + } + return fixFlowingType( + builder.makeBrOn(op, targetName, make(refType), castType)); +} + bool TranslateToFuzzReader::maybeSignedGet(const Field& field) { if (field.isPacked()) { return oneIn(2); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 1740e369d5d..0db119b0700 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1319,6 +1319,17 @@ class Builder { ret->finalize(); return ret; } + template + ContBind* makeContBind(HeapType targetType, + const T& operands, + Expression* cont) { + auto* ret = wasm.allocator.alloc(); + ret->type = Type(targetType, NonNullable, Exact); + ret->operands.set(operands); + ret->cont = cont; + ret->finalize(); + return ret; + } Suspend* makeSuspend(Name tag, const std::vector& args) { auto* ret = wasm.allocator.alloc(); ret->tag = tag; @@ -1340,6 +1351,21 @@ class Builder { ret->finalize(); return ret; } + template + Resume* makeResume(const std::vector& handlerTags, + const std::vector& handlerBlocks, + const std::vector& sentTypes, + ExpressionList& operands, + Expression* cont) { + auto* ret = wasm.allocator.alloc(); + ret->handlerTags.set(handlerTags); + ret->handlerBlocks.set(handlerBlocks); + ret->sentTypes.set(sentTypes); + ret->operands.set(operands); + ret->cont = cont; + ret->finalize(); + return ret; + } ResumeThrow* makeResumeThrow(Name tag, const std::vector& handlerTags, const std::vector& handlerBlocks, From a649f95280320b3f3e372cde82cf7aa4f2ec7d4a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Mar 2026 14:01:05 -0700 Subject: [PATCH 02/24] fornow --- scripts/fuzz_opt.py | 12 ---- src/tools/fuzzing.h | 2 +- src/tools/fuzzing/fuzzing.cpp | 127 ---------------------------------- 3 files changed, 1 insertion(+), 140 deletions(-) diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 95bf140b320..8a1da49ecc1 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -2276,18 +2276,6 @@ def handle(self, wasm): # The global list of all test case handlers testcase_handlers = [ FuzzExec(), - CompareVMs(), - CheckDeterminism(), - Wasm2JS(), - TrapsNeverHappen(), - CtorEval(), - Merge(), - Split(), - RoundtripText(), - ClusterFuzz(), - Two(), - PreserveImportsExports(), - BranchHintPreservation(), ] diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 6d6a52b803d..97bbea7a77d 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -530,7 +530,7 @@ class TranslateToFuzzReader { Expression* makeRefGetDesc(Type type); Expression* makeBrOn(Type type); Expression* makeContBind(Type type); - Expression* makeResume(Type type); + // TODO: Expression* makeResume(Type type); // Decide to emit a signed Struct/ArrayGet sometimes, when the field is // packed. diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index c47d5df7380..425a06b4b72 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -2573,10 +2573,6 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) { if (heapType.isContinuation()) { options.add(FeatureSet::ReferenceTypes | FeatureSet::StackSwitching, &Self::makeContBind); - if (canMakeControlFlow) { - options.add(FeatureSet::ReferenceTypes | FeatureSet::StackSwitching, - &Self::makeResume); - } } } if (wasm.features.hasGC()) { @@ -5483,129 +5479,6 @@ Expression* TranslateToFuzzReader::makeContBind(Type type) { return builder.makeContBind(type.getHeapType(), newArgs, make(newType)); } -Expression* TranslateToFuzzReader::makeResume(Type type) { - // TODO - if (funcContext->breakableStack.empty()) { - return makeTrivial(type); - } - // We need to find a proper target to break to; try a few times. Finding the - // target is harder than flowing out the proper type, so focus on the target, - // and fix up the flowing type later. That is, once we find a target to break - // to, we can then either drop ourselves or wrap ourselves in a block + - // another value, so that we return the proper thing here (which is done below - // in fixFlowingType). - int tries = fuzzParams->TRIES; - Name targetName; - Type targetType; - while (--tries >= 0) { - auto* target = pick(funcContext->breakableStack); - targetName = getTargetName(target); - targetType = getTargetType(target); - // We can send any reference type, or no value at all, but nothing else. - if (targetType.isRef() || targetType == Type::none) { - break; - } - } - if (tries < 0) { - return makeTrivial(type); - } - - auto fixFlowingType = [&](Expression* brOn) -> Expression* { - if (Type::isSubType(brOn->type, type)) { - // Already of the proper type. - return brOn; - } - if (type == Type::none) { - // We just need to drop whatever it is. - return builder.makeDrop(brOn); - } - // We need to replace the type with something else. Drop the BrOn if we need - // to, and append a value with the proper type. - if (brOn->type != Type::none) { - brOn = builder.makeDrop(brOn); - } - return builder.makeSequence(brOn, make(type)); - }; - - // We found something to break to. Figure out which BrOn variants we can - // send. - if (targetType == Type::none) { - // BrOnNull is the only variant that sends no value. - return fixFlowingType( - builder.makeBrOn(BrOnNull, targetName, make(getReferenceType()))); - } - - // We are sending a reference type to the target. All other BrOn variants can - // do that. - assert(targetType.isRef()); - // BrOnNonNull can handle sending any reference. The casts are more limited. - auto op = BrOnNonNull; - if (targetType.isCastable()) { - op = pick(BrOnNonNull, BrOnCast, BrOnCastFail); - } - Type castType = Type::none; - Type refType; - switch (op) { - case BrOnNonNull: { - // The sent type is the non-nullable version of the reference, so any ref - // of that type is ok, nullable or not. - refType = targetType.with(getNullability()); - break; - } - case BrOnCast: { - // The sent type is the heap type we cast to, with the input type's - // nullability, so the combination of the two must be a subtype of - // targetType. - castType = getSubType(targetType); - if (castType.isExact() && !wasm.features.hasCustomDescriptors()) { - // This exact cast is only valid if its input has the same type (or the - // only possible strict subtype, bottom). - refType = castType; - } else { - // The ref's type must be castable to castType, or we'd not validate. - // But it can also be a subtype, which will trivially also succeed (so - // do that more rarely). Pick subtypes rarely, as they make the cast - // trivial. - refType = oneIn(5) ? getSubType(castType) : getSuperType(castType); - } - if (targetType.isNonNullable()) { - // And it must have the right nullability for the target, as mentioned - // above: if the target type is non-nullable then either the ref or the - // cast types must be. - if (!refType.isNonNullable() && !castType.isNonNullable()) { - // Pick one to make non-nullable. - if (oneIn(2)) { - refType = Type(refType.getHeapType(), NonNullable); - } else { - castType = Type(castType.getHeapType(), NonNullable); - } - } - } - break; - } - case BrOnCastFail: { - // The sent type is the ref's type, with adjusted nullability (if the cast - // allows nulls then no null can fail the cast, and what is sent is non- - // nullable). First, pick a ref type that we can send to the target. - refType = getSubType(targetType); - // See above on BrOnCast, but flipped. - castType = oneIn(5) ? getSuperType(refType) : getSubType(refType); - castType = castType.withInexactIfNoCustomDescs(wasm.features); - // There is no nullability to adjust: if targetType is non-nullable then - // both refType and castType are as well, as subtypes of it. But we can - // also allow castType to be nullable (it is not sent to the target). - if (castType.isNonNullable() && oneIn(2)) { - castType = Type(castType.getHeapType(), Nullable); - } - } break; - default: { - WASM_UNREACHABLE("bad br_on op"); - } - } - return fixFlowingType( - builder.makeBrOn(op, targetName, make(refType), castType)); -} - bool TranslateToFuzzReader::maybeSignedGet(const Field& field) { if (field.isPacked()) { return oneIn(2); From bdc515b1d1f542fe2e3ca8ea7ba523244815387f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Mar 2026 14:20:51 -0700 Subject: [PATCH 03/24] go --- src/tools/fuzzing/fuzzing.cpp | 1 + src/tools/fuzzing/heap-types.cpp | 97 +++++++++++++++++++++++++------- 2 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 425a06b4b72..06baed9f973 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -505,6 +505,7 @@ void TranslateToFuzzReader::setupHeapTypes() { random, wasm.features, upTo(fuzzParams->MAX_NEW_GC_TYPES)); auto result = generator.builder.build(); if (auto* err = result.getError()) { + std::cout << generator.builder.dump() << '\n'; Fatal() << "Failed to build heap types: " << err->reason << " at index " << err->index; } diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index af8e235e442..e0303293346 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -46,7 +46,9 @@ struct HeapTypeGeneratorImpl { struct SignatureKind {}; struct StructKind {}; struct ArrayKind {}; - using HeapTypeKind = std::variant; + struct ContinuationKind {}; + using HeapTypeKind = + std::variant; std::vector typeKinds; // For each type, the index one past the end of its recursion group, used to @@ -283,6 +285,8 @@ struct HeapTypeGeneratorImpl { builder[index] = generateStruct(share, isDesc); } else if (std::get_if(&kind)) { builder[index] = generateArray(share); + } else if (std::get_if(&kind)) { + builder[index] = generateContinuation(share); } else { WASM_UNREACHABLE("unexpected kind"); } @@ -300,7 +304,9 @@ struct HeapTypeGeneratorImpl { builder[index] = generateSubArray(supertype.getArray()); break; case wasm::HeapTypeKind::Cont: - WASM_UNREACHABLE("TODO: cont"); + builder[index] = + generateSubContinuation(supertype.getContinuation()); + break; case wasm::HeapTypeKind::Basic: WASM_UNREACHABLE("unexpected kind"); } @@ -310,11 +316,14 @@ struct HeapTypeGeneratorImpl { HeapType::BasicHeapType generateBasicHeapType(Shareability share) { // Choose bottom types more rarely. - // TODO: string and cont types + // TODO: string types if (rand.oneIn(16)) { - HeapType ht = - rand.pick(HeapType::noext, HeapType::nofunc, HeapType::none); - return ht.getBasic(share); + std::vector bottoms{ + HeapType::noext, HeapType::nofunc, HeapType::none}; + if (features.hasStackSwitching()) { + bottoms.push_back(HeapType::nocont); + } + return rand.pick(bottoms).getBasic(share); } std::vector options{HeapType::func, @@ -324,6 +333,9 @@ struct HeapTypeGeneratorImpl { HeapType::i31, HeapType::struct_, HeapType::array}; + if (features.hasStackSwitching()) { + options.push_back(HeapType::cont); + } // Avoid shared exn, which we cannot generate. if (features.hasExceptionHandling() && share == Unshared) { options.push_back(HeapType::exn); @@ -443,6 +455,18 @@ struct HeapTypeGeneratorImpl { Array generateArray(Shareability share) { return {generateField(share)}; } + Continuation generateContinuation(Shareability share) { + if (auto type = pickKind(share)) { + return Continuation(*type); + } + // We failed to find a type. Use a trivial one. + return Continuation(Signature(Type::none, Type::none)); + } + + Continuation generateSubContinuation(Continuation super) { + return Continuation(pickSubHeapType(super.type)); + } + template std::vector getKindCandidates(Shareability share) { std::vector candidates; @@ -518,6 +542,23 @@ struct HeapTypeGeneratorImpl { } } + HeapType pickSubCont(Shareability share) { + auto choice = rand.upTo(8); + switch (choice) { + case 0: + return HeapTypes::cont.getBasic(share); + case 1: + return HeapTypes::nocont.getBasic(share); + default: { + if (auto type = pickKind(share)) { + return *type; + } + HeapType ht = (choice % 2) ? HeapType::cont : HeapType::nocont; + return ht.getBasic(share); + } + } + } + HeapType pickSubEq(Shareability share) { auto choice = rand.upTo(16); switch (choice) { @@ -585,6 +626,8 @@ struct HeapTypeGeneratorImpl { auto* kind = &typeKinds[it->second]; if (std::get_if(kind)) { return HeapTypes::nofunc.getBasic(share); + } else if (std::get_if(kind)) { + return HeapTypes::nocont.getBasic(share); } else { return HeapTypes::none.getBasic(share); } @@ -603,7 +646,7 @@ struct HeapTypeGeneratorImpl { case HeapType::func: return pickSubFunc(share); case HeapType::cont: - WASM_UNREACHABLE("not implemented"); + return pickSubCont(share); case HeapType::any: return pickSubAny(share); case HeapType::eq: @@ -654,6 +697,9 @@ struct HeapTypeGeneratorImpl { } else if (std::get_if(kind)) { candidates.push_back(HeapTypes::func.getBasic(share)); return rand.pick(candidates); + } else if (std::get_if(kind)) { + candidates.push_back(HeapTypes::cont.getBasic(share)); + return rand.pick(candidates); } else { WASM_UNREACHABLE("unexpected kind"); } @@ -685,7 +731,7 @@ struct HeapTypeGeneratorImpl { case HeapType::nofunc: return pickSubFunc(share); case HeapType::nocont: - WASM_UNREACHABLE("not implemented"); + return pickSubCont(share); case HeapType::noext: candidates.push_back(HeapTypes::ext.getBasic(share)); break; @@ -798,13 +844,16 @@ struct HeapTypeGeneratorImpl { } HeapTypeKind generateHeapTypeKind() { - switch (rand.upTo(3)) { + uint32_t numKinds = features.hasStackSwitching() ? 4 : 3; + switch (rand.upTo(numKinds)) { case 0: return SignatureKind{}; case 1: return StructKind{}; case 2: return ArrayKind{}; + case 3: + return ContinuationKind{}; } WASM_UNREACHABLE("unexpected index"); } @@ -871,7 +920,7 @@ struct Inhabitator { Inhabitator::Variance Inhabitator::getVariance(FieldPos fieldPos) { auto [type, idx] = fieldPos; - assert(!type.isBasic() && !type.isSignature()); + assert(!type.isBasic() && !type.isSignature() && !type.isContinuation()); auto field = GCTypeUtils::getField(type, idx); assert(field); if (field->mutable_ == Mutable) { @@ -929,9 +978,9 @@ void Inhabitator::markNullable(FieldPos field) { void Inhabitator::markBottomRefsNullable() { for (auto type : types) { - if (type.isSignature()) { - // Functions can always be instantiated, even if their types refer to - // uninhabitable types. + if (type.isSignature() || type.isContinuation()) { + // Functions/continuations can always be instantiated, even if their types + // refer to uninhabitable types. continue; } auto children = type.getTypeChildren(); @@ -951,9 +1000,9 @@ void Inhabitator::markExternRefsNullable() { // TODO: Remove this once the fuzzer imports externref globals or gets some // other way to instantiate externrefs. for (auto type : types) { - if (type.isSignature()) { - // Functions can always be instantiated, even if their types refer to - // uninhabitable types. + if (type.isSignature() || type.isContinuation()) { + // Functions/continuations can always be instantiated, even if their types + // refer to uninhabitable types. continue; } auto children = type.getTypeChildren(); @@ -1062,8 +1111,9 @@ void Inhabitator::breakNonNullableCycles() { // Skip references to function types. Functions types can always be // instantiated since functions can be created even with uninhabitable // params or results. Function references therefore break cycles that - // would otherwise produce uninhabitability. - if (heapType.isSignature()) { + // would otherwise produce uninhabitability. (Continuations are + // similar.) + if (heapType.isSignature() || heapType.isContinuation()) { ++index; continue; } @@ -1156,8 +1206,15 @@ std::vector Inhabitator::build() { builder[i] = copy; continue; } - case HeapTypeKind::Cont: - WASM_UNREACHABLE("TODO: cont"); + case HeapTypeKind::Cont: { + Continuation copy = type.getContinuation(); + auto heapType = copy.type; + if (auto it = typeIndices.find(heapType); it != typeIndices.end()) { + heapType = builder[it->second]; + } + builder[i] = Continuation(heapType); + continue; + } case HeapTypeKind::Basic: break; } From 0fd2c1e6db2c3545c752ad0d0ee056ba1d8931f5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Mar 2026 14:21:53 -0700 Subject: [PATCH 04/24] format --- src/tools/fuzzing/fuzzing.cpp | 2 +- src/wasm-builder.h | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 06baed9f973..baf002406e4 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -505,7 +505,7 @@ void TranslateToFuzzReader::setupHeapTypes() { random, wasm.features, upTo(fuzzParams->MAX_NEW_GC_TYPES)); auto result = generator.builder.build(); if (auto* err = result.getError()) { - std::cout << generator.builder.dump() << '\n'; + generator.builder.dump(); Fatal() << "Failed to build heap types: " << err->reason << " at index " << err->index; } diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 0db119b0700..b17ca328bc0 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1320,9 +1320,8 @@ class Builder { return ret; } template - ContBind* makeContBind(HeapType targetType, - const T& operands, - Expression* cont) { + ContBind* + makeContBind(HeapType targetType, const T& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); ret->type = Type(targetType, NonNullable, Exact); ret->operands.set(operands); From 75a0c90a449c295a6bb367f0cd0acb2e91b56eb2 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Mar 2026 15:41:26 -0700 Subject: [PATCH 05/24] more --- src/tools/fuzzing/fuzzing.cpp | 3 ++- src/tools/fuzzing/heap-types.cpp | 34 +++++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index baf002406e4..6d34a23bd9b 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -503,9 +503,10 @@ void TranslateToFuzzReader::setupHeapTypes() { if (wasm.features.hasGC()) { auto generator = HeapTypeGenerator::create( random, wasm.features, upTo(fuzzParams->MAX_NEW_GC_TYPES)); + //std::cout << "dump:\n"; + //generator.builder.dump(); auto result = generator.builder.build(); if (auto* err = result.getError()) { - generator.builder.dump(); Fatal() << "Failed to build heap types: " << err->reason << " at index " << err->index; } diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index e0303293346..d4ee89e9bd2 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -235,9 +235,15 @@ struct HeapTypeGeneratorImpl { builder[i].setShared(HeapType(builder[*describedIndices[i]]).getShared()); } else { // This is a root type with no supertype. Choose a kind for this type. - typeKinds.emplace_back(generateHeapTypeKind()); - builder[i].setShared( - !features.hasSharedEverything() || rand.oneIn(2) ? Unshared : Shared); + auto kind = generateHeapTypeKind(); + typeKinds.emplace_back(kind); + // Continuations cannot be shared. + auto shared = Unshared; + if (features.hasSharedEverything() && + !std::get_if(&kind) && rand.oneIn(2)) { + shared = Shared; + } + builder[i].setShared(shared); } // Plan this descriptor chain for this type if it is not already determined @@ -320,7 +326,8 @@ struct HeapTypeGeneratorImpl { if (rand.oneIn(16)) { std::vector bottoms{ HeapType::noext, HeapType::nofunc, HeapType::none}; - if (features.hasStackSwitching()) { + // Continuations cannot be shared. + if (features.hasStackSwitching() && share == Unshared) { bottoms.push_back(HeapType::nocont); } return rand.pick(bottoms).getBasic(share); @@ -333,7 +340,7 @@ struct HeapTypeGeneratorImpl { HeapType::i31, HeapType::struct_, HeapType::array}; - if (features.hasStackSwitching()) { + if (features.hasStackSwitching() && share == Unshared) { options.push_back(HeapType::cont); } // Avoid shared exn, which we cannot generate. @@ -1207,12 +1214,25 @@ std::vector Inhabitator::build() { continue; } case HeapTypeKind::Cont: { + /* +@@ -1210,9 +1216,9 @@ std::vector Inhabitator::build() { + Continuation copy = type.getContinuation(); + auto heapType = copy.type; + if (auto it = typeIndices.find(heapType); it != typeIndices.end()) { +- heapType = builder[it->second]; ++ copy.type = builder.getTempHeapType(it->second); + } +- builder[i] = Continuation(heapType); ++ builder[i] = copy; + continue; + } + */ Continuation copy = type.getContinuation(); auto heapType = copy.type; if (auto it = typeIndices.find(heapType); it != typeIndices.end()) { - heapType = builder[it->second]; + copy.type = builder.getTempHeapType(it->second); } - builder[i] = Continuation(heapType); + builder[i] = copy; continue; } case HeapTypeKind::Basic: From 015e26b47ff966e9da76b2136e22dc9cd1d75617 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Mar 2026 15:44:30 -0700 Subject: [PATCH 06/24] more --- src/tools/fuzzing/fuzzing.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 6d34a23bd9b..4451d8d36b1 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -4120,7 +4120,6 @@ Expression* TranslateToFuzzReader::makeCompoundRef(Type type) { case HeapTypeKind::Cont: { auto funcType = heapType.getContinuation().type; return builder.makeContNew(heapType, makeTrappingRefUse(funcType)); - // todo contbind, resume } case HeapTypeKind::Basic: break; @@ -5472,7 +5471,7 @@ Expression* TranslateToFuzzReader::makeContBind(Type type) { for (auto t : sig.params) { newParams.push_back(t); } - auto newParam = getConcreteType(); + auto newParam = getSingleConcreteType(); newParams.push_back(newParam); auto newSig = Signature(Type(newParams), sig.results); auto newCont = Continuation(newSig); From 4d6cd3c37adde0c7c70f026d0e068d010182f632 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Mar 2026 16:28:42 -0700 Subject: [PATCH 07/24] more --- src/tools/fuzzing/heap-types.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index d4ee89e9bd2..84f8427ef50 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -333,6 +333,12 @@ struct HeapTypeGeneratorImpl { return rand.pick(bottoms).getBasic(share); } + // Sometimes emit shared in place of unshared. + if (share == Unshared && features.hasSharedEverything() && + rand.oneIn(4)) { + share = Shared; + } + std::vector options{HeapType::func, HeapType::ext, HeapType::any, @@ -348,10 +354,6 @@ struct HeapTypeGeneratorImpl { options.push_back(HeapType::exn); } auto ht = rand.pick(options); - if (share == Unshared && features.hasSharedEverything() && - ht != HeapType::exn && rand.oneIn(2)) { - share = Shared; - } return ht.getBasic(share); } From 44163f85f199a0fb210ba17bf218cc32872d8200 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Mar 2026 16:38:49 -0700 Subject: [PATCH 08/24] more --- src/tools/fuzzing/heap-types.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 84f8427ef50..bd0f3ed3709 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -645,8 +645,13 @@ struct HeapTypeGeneratorImpl { // true, since oneIn(0) => true. assert(!candidates.empty()); return rand.pick(candidates); + } else if (!type.isBasic()) { + // This is not basic, but also not an existing type. This can happen only + // when a continuation can't find a signature, and creates a trivial one. + // Return that type itself (though other subtypes may exist). + return type; } else { - // This is not a constructed type, so it must be a basic type. + // A basic type. assert(type.isBasic()); if (rand.oneIn(8)) { return type.getBottom(); From 9e83f3c53a2fb8f14b89da5b72b49d2a00285fde Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Mar 2026 16:41:22 -0700 Subject: [PATCH 09/24] more --- src/passes/Unsubtyping.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/passes/Unsubtyping.cpp b/src/passes/Unsubtyping.cpp index e44b50fb7c0..2db80ba336c 100644 --- a/src/passes/Unsubtyping.cpp +++ b/src/passes/Unsubtyping.cpp @@ -1026,7 +1026,8 @@ struct Unsubtyping : Pass, Noter { break; } case HeapTypeKind::Cont: - WASM_UNREACHABLE("TODO: cont"); + noteSubtype(sub.getContinuation().type, super.getContinuation().type); + break; case HeapTypeKind::Basic: WASM_UNREACHABLE("unexpected kind"); } From 2c121802a812776fbcc2f06a12f1e2c73dd177f9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Mar 2026 16:47:07 -0700 Subject: [PATCH 10/24] more --- src/tools/fuzzing/fuzzing.cpp | 4 ++-- src/tools/fuzzing/heap-types.cpp | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 4451d8d36b1..50e11b939b5 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -503,8 +503,8 @@ void TranslateToFuzzReader::setupHeapTypes() { if (wasm.features.hasGC()) { auto generator = HeapTypeGenerator::create( random, wasm.features, upTo(fuzzParams->MAX_NEW_GC_TYPES)); - //std::cout << "dump:\n"; - //generator.builder.dump(); + std::cout << "dump:\n"; + generator.builder.dump(); auto result = generator.builder.build(); if (auto* err = result.getError()) { Fatal() << "Failed to build heap types: " << err->reason << " at index " diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index bd0f3ed3709..7424133830c 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -473,7 +473,12 @@ struct HeapTypeGeneratorImpl { } Continuation generateSubContinuation(Continuation super) { - return Continuation(pickSubHeapType(super.type)); + auto subType = pickSubHeapType(super.type); + if (subType.isBasic()) { + // We cannot use a bottom type here. + subType = super.type; + } + return Continuation(subType); } template From b1b17a87f1cf95ef28df4f662ded219244bcab29 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 12 Mar 2026 17:00:59 -0700 Subject: [PATCH 11/24] more --- scripts/fuzz_opt.py | 2 +- src/passes/Unsubtyping.cpp | 3 +-- src/passes/pass.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 8a1da49ecc1..dca6a9cc0a8 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -2469,7 +2469,7 @@ def write_commands(commands, filename): ("--type-merging",), ("--type-ssa",), ("--type-unfinalizing",), - ("--unsubtyping",), + #("--unsubtyping",), ("--vacuum",), ] diff --git a/src/passes/Unsubtyping.cpp b/src/passes/Unsubtyping.cpp index 2db80ba336c..e44b50fb7c0 100644 --- a/src/passes/Unsubtyping.cpp +++ b/src/passes/Unsubtyping.cpp @@ -1026,8 +1026,7 @@ struct Unsubtyping : Pass, Noter { break; } case HeapTypeKind::Cont: - noteSubtype(sub.getContinuation().type, super.getContinuation().type); - break; + WASM_UNREACHABLE("TODO: cont"); case HeapTypeKind::Basic: WASM_UNREACHABLE("unexpected kind"); } diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 0e6e28267c2..72acd7f75e0 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -781,7 +781,7 @@ void PassRunner::addDefaultGlobalOptimizationPrePasses() { addIfNoDWARFIssues("gsi"); if (options.closedWorld) { addIfNoDWARFIssues("abstract-type-refining"); - addIfNoDWARFIssues("unsubtyping"); + //addIfNoDWARFIssues("unsubtyping"); } } // TODO: generate-global-effects here, right before function passes, then From 14a8ef5a2d7d41c44b58c9534337b72c5c0a4514 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Mar 2026 10:02:11 -0700 Subject: [PATCH 12/24] fix --- src/tools/fuzzing/fuzzing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 50e11b939b5..ca754d0e4a0 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -5472,7 +5472,7 @@ Expression* TranslateToFuzzReader::makeContBind(Type type) { newParams.push_back(t); } auto newParam = getSingleConcreteType(); - newParams.push_back(newParam); + newParams.insert(newParams.begin(), newParam); auto newSig = Signature(Type(newParams), sig.results); auto newCont = Continuation(newSig); auto newType = Type(newCont, NonNullable, Exact); From 60dc5562ef837efb8867b761c8eba140c6ecbb61 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Mar 2026 10:16:19 -0700 Subject: [PATCH 13/24] debug --- src/tools/fuzzing/heap-types.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 7424133830c..f888a99921a 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -1279,9 +1279,14 @@ std::vector Inhabitator::build() { builder[i].setShared(types[i].getShared()); } - auto built = builder.build(); - assert(!built.getError() && "unexpected build error"); - return *built; +std::cout << "damp\n"; +builder.dump(); + auto result = builder.build(); + if (auto* err = result.getError()) { + Fatal() << "Failed to build heap types: " << err->reason << " at index " + << err->index; + } + return *result; } } // anonymous namespace From 94f1c9e7d26c5bd57bbc2710cd5d0b33c4446b3e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Mar 2026 10:54:48 -0700 Subject: [PATCH 14/24] fix --- src/tools/fuzzing/heap-types.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index f888a99921a..4f214797f9c 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -221,6 +221,8 @@ struct HeapTypeGeneratorImpl { } } + bool addedSignature = false; + // Set up the builder entry and type kind for this type. if (super) { typeKinds.push_back(typeKinds[*super]); @@ -236,8 +238,16 @@ struct HeapTypeGeneratorImpl { } else { // This is a root type with no supertype. Choose a kind for this type. auto kind = generateHeapTypeKind(); + // Continuations must be after at least one signature, so we have a + // signature to pick from which appears before them. + if (std::get_if(&kind) && !addedSignature) { + kind = SignatureKind{}; + } typeKinds.emplace_back(kind); - // Continuations cannot be shared. + if (std::get_if(&kind)) { + addedSignature = true; + } + // Continuations cannot be shared, but other things can. auto shared = Unshared; if (features.hasSharedEverything() && !std::get_if(&kind) && rand.oneIn(2)) { @@ -465,18 +475,17 @@ struct HeapTypeGeneratorImpl { Array generateArray(Shareability share) { return {generateField(share)}; } Continuation generateContinuation(Shareability share) { - if (auto type = pickKind(share)) { - return Continuation(*type); - } - // We failed to find a type. Use a trivial one. - return Continuation(Signature(Type::none, Type::none)); + auto type = pickKind(share); + // There must be signatures to pick from. + assert(type); + return Continuation(*type); } Continuation generateSubContinuation(Continuation super) { auto subType = pickSubHeapType(super.type); if (subType.isBasic()) { // We cannot use a bottom type here. - subType = super.type; + subType = super.type; } return Continuation(subType); } @@ -1254,8 +1263,10 @@ std::vector Inhabitator::build() { } // Establish rec groups. +std::cout << "types size " << types.size() << '\n'; for (size_t start = 0; start < types.size();) { size_t size = types[start].getRecGroup().size(); +std::cout << " rec group at " << start << " of size " << size << '\n'; builder.createRecGroup(start, size); start += size; } From 92072bbb322019cc8f733d3c2aa6720a4dfa27ba Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Mar 2026 15:46:57 -0700 Subject: [PATCH 15/24] redo --- scripts/fuzz_opt.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index dca6a9cc0a8..95bf140b320 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -2276,6 +2276,18 @@ def handle(self, wasm): # The global list of all test case handlers testcase_handlers = [ FuzzExec(), + CompareVMs(), + CheckDeterminism(), + Wasm2JS(), + TrapsNeverHappen(), + CtorEval(), + Merge(), + Split(), + RoundtripText(), + ClusterFuzz(), + Two(), + PreserveImportsExports(), + BranchHintPreservation(), ] @@ -2469,7 +2481,7 @@ def write_commands(commands, filename): ("--type-merging",), ("--type-ssa",), ("--type-unfinalizing",), - #("--unsubtyping",), + ("--unsubtyping",), ("--vacuum",), ] From e0ec76ce2be186e383ca5aa5b11efee86165e32b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Mar 2026 15:47:11 -0700 Subject: [PATCH 16/24] redo --- src/passes/pass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 72acd7f75e0..0e6e28267c2 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -781,7 +781,7 @@ void PassRunner::addDefaultGlobalOptimizationPrePasses() { addIfNoDWARFIssues("gsi"); if (options.closedWorld) { addIfNoDWARFIssues("abstract-type-refining"); - //addIfNoDWARFIssues("unsubtyping"); + addIfNoDWARFIssues("unsubtyping"); } } // TODO: generate-global-effects here, right before function passes, then From b3ad80d85ee614edc4ac9f3bf4a961211d632aae Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Mar 2026 15:48:30 -0700 Subject: [PATCH 17/24] undebug --- src/tools/fuzzing/fuzzing.cpp | 2 -- src/tools/fuzzing/heap-types.cpp | 24 +----------------------- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index ca754d0e4a0..a2fe1b1407b 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -503,8 +503,6 @@ void TranslateToFuzzReader::setupHeapTypes() { if (wasm.features.hasGC()) { auto generator = HeapTypeGenerator::create( random, wasm.features, upTo(fuzzParams->MAX_NEW_GC_TYPES)); - std::cout << "dump:\n"; - generator.builder.dump(); auto result = generator.builder.build(); if (auto* err = result.getError()) { Fatal() << "Failed to build heap types: " << err->reason << " at index " diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 4f214797f9c..87d87abadc4 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -659,13 +659,8 @@ struct HeapTypeGeneratorImpl { // true, since oneIn(0) => true. assert(!candidates.empty()); return rand.pick(candidates); - } else if (!type.isBasic()) { - // This is not basic, but also not an existing type. This can happen only - // when a continuation can't find a signature, and creates a trivial one. - // Return that type itself (though other subtypes may exist). - return type; } else { - // A basic type. + // This is not a constructed type, so it must be a basic type. assert(type.isBasic()); if (rand.oneIn(8)) { return type.getBottom(); @@ -1235,19 +1230,6 @@ std::vector Inhabitator::build() { continue; } case HeapTypeKind::Cont: { - /* -@@ -1210,9 +1216,9 @@ std::vector Inhabitator::build() { - Continuation copy = type.getContinuation(); - auto heapType = copy.type; - if (auto it = typeIndices.find(heapType); it != typeIndices.end()) { -- heapType = builder[it->second]; -+ copy.type = builder.getTempHeapType(it->second); - } -- builder[i] = Continuation(heapType); -+ builder[i] = copy; - continue; - } - */ Continuation copy = type.getContinuation(); auto heapType = copy.type; if (auto it = typeIndices.find(heapType); it != typeIndices.end()) { @@ -1263,10 +1245,8 @@ std::vector Inhabitator::build() { } // Establish rec groups. -std::cout << "types size " << types.size() << '\n'; for (size_t start = 0; start < types.size();) { size_t size = types[start].getRecGroup().size(); -std::cout << " rec group at " << start << " of size " << size << '\n'; builder.createRecGroup(start, size); start += size; } @@ -1290,8 +1270,6 @@ std::cout << " rec group at " << start << " of size " << size << '\n'; builder[i].setShared(types[i].getShared()); } -std::cout << "damp\n"; -builder.dump(); auto result = builder.build(); if (auto* err = result.getError()) { Fatal() << "Failed to build heap types: " << err->reason << " at index " From f0415d0b4d1d8f2d5db28f998a1bde0ff77a587f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Mar 2026 15:49:35 -0700 Subject: [PATCH 18/24] form --- src/tools/fuzzing/heap-types.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 87d87abadc4..7880002888d 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -344,8 +344,7 @@ struct HeapTypeGeneratorImpl { } // Sometimes emit shared in place of unshared. - if (share == Unshared && features.hasSharedEverything() && - rand.oneIn(4)) { + if (share == Unshared && features.hasSharedEverything() && rand.oneIn(4)) { share = Shared; } From 3256a1e20379c5cae4d663853395f77aaafde16f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Mar 2026 16:23:07 -0700 Subject: [PATCH 19/24] update test --- test/lit/fuzz-types.test | 84 +++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/test/lit/fuzz-types.test b/test/lit/fuzz-types.test index 44d475f6e3d..3cba2616512 100644 --- a/test/lit/fuzz-types.test +++ b/test/lit/fuzz-types.test @@ -2,55 +2,51 @@ ;; CHECK: Running with seed 2 ;; CHECK-NEXT: Built 20 types: -;; CHECK-NEXT: (type $0 (shared (struct))) +;; CHECK-NEXT: (type $0 (sub (func (param i32 (ref $0) (ref null $0) (ref null $0)) (result (ref $0))))) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $1 (array (ref $2))) -;; CHECK-NEXT: (type $2 (sub (shared (array (mut i16))))) -;; CHECK-NEXT: (type $3 (sub (shared (array i32)))) -;; CHECK-NEXT: (type $4 (sub (descriptor $5) (struct (field (mut (ref $0)))))) -;; CHECK-NEXT: (type $5 (sub (describes $4) (struct (field f64) (field (mut i64))))) -;; CHECK-NEXT: (type $6 (sub (array v128))) -;; CHECK-NEXT: (type $7 (shared (struct (field f32) (field (mut (ref $0)))))) -;; CHECK-NEXT: (type $8 (sub (shared (struct (field f64) (field (mut (ref (shared struct)))) (field (mut f64)) (field i16) (field i32) (field i64))))) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $9 (descriptor $12) (struct (field i64) (field i16))) -;; CHECK-NEXT: (type $10 (array (mut (ref null $5)))) -;; CHECK-NEXT: (type $11 (sub (shared (func (param (ref $7) f64 (ref $9)) (result (ref null $10)))))) -;; CHECK-NEXT: (type $12 (sub (describes $9) (descriptor $13) (struct (field (ref (shared any))) (field (mut (ref extern))) (field v128) (field (ref null $17))))) -;; CHECK-NEXT: (type $13 (sub (describes $12) (descriptor $17) (struct (field externref) (field (mut i8)) (field (mut i32)) (field (mut f32)) (field i16) (field (mut (ref null $6)))))) -;; CHECK-NEXT: (type $14 (sub (func (result i64)))) -;; CHECK-NEXT: (type $15 (sub (shared (func)))) -;; CHECK-NEXT: (type $16 (shared (func (result (ref null $0))))) -;; CHECK-NEXT: (type $17 (sub (describes $13) (struct (field (ref extern))))) -;; CHECK-NEXT: (type $18 (sub (func (param v128 (ref null $10))))) -;; CHECK-NEXT: (type $19 (sub final $11 (shared (func (param (ref null (shared any)) f64 (ref any)) (result (ref $10)))))) +;; CHECK-NEXT: (type $1 (shared (struct))) +;; CHECK-NEXT: (type $2 (sub (func (result (ref $13))))) +;; CHECK-NEXT: (type $3 (sub (shared (struct (field v128) (field (ref (shared i31))) (field (mut (ref null (shared array)))))))) +;; CHECK-NEXT: (type $4 (sub (shared (func (param i64 i64) (result i64))))) +;; CHECK-NEXT: (type $5 (shared (func (param f64 v128 (ref $19) (ref null $3) (ref $2) (ref null $13) i64) (result (ref null $11))))) +;; CHECK-NEXT: (type $6 (sub (func (result i64)))) +;; CHECK-NEXT: (type $7 (shared (func (result f32)))) +;; CHECK-NEXT: (type $8 (shared (struct))) +;; CHECK-NEXT: (type $9 (shared (func))) +;; CHECK-NEXT: (type $10 (sub (array i64))) +;; CHECK-NEXT: (type $11 (shared (func))) +;; CHECK-NEXT: (type $12 (sub (struct (field (mut i32)) (field i32) (field f64) (field (ref $4))))) +;; CHECK-NEXT: (type $13 (sub (array (mut i64)))) +;; CHECK-NEXT: (type $14 (sub (shared (struct (field (mut i64)) (field v128) (field f64) (field (mut (ref (shared array)))) (field (ref $1)) (field i16))))) +;; CHECK-NEXT: (type $15 (sub (shared (array (mut (ref $16)))))) +;; CHECK-NEXT: (type $16 (sub (shared (array (ref null (shared i31)))))) +;; CHECK-NEXT: (type $17 (sub final $4 (shared (func (param i64 i64) (result i64))))) +;; CHECK-NEXT: (type $18 (sub final $2 (func (result (ref $13))))) +;; CHECK-NEXT: (type $19 (sub $13 (array (mut i64)))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ;; CHECK-NEXT: Inhabitable types: ;; CHECK-NEXT: ;; CHECK-NEXT: Built 20 types: -;; CHECK-NEXT: (type $0 (shared (struct))) -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $1 (array (ref $2))) -;; CHECK-NEXT: (type $2 (sub (shared (array (mut i16))))) -;; CHECK-NEXT: (type $3 (sub (shared (array i32)))) -;; CHECK-NEXT: (type $4 (sub (descriptor $5) (struct (field (mut (ref $0)))))) -;; CHECK-NEXT: (type $5 (sub (describes $4) (struct (field f64) (field (mut i64))))) -;; CHECK-NEXT: (type $6 (sub (array v128))) -;; CHECK-NEXT: (type $7 (shared (struct (field f32) (field (mut (ref $0)))))) -;; CHECK-NEXT: (type $8 (sub (shared (struct (field f64) (field (mut (ref (shared struct)))) (field (mut f64)) (field i16) (field i32) (field i64))))) -;; CHECK-NEXT: ) +;; CHECK-NEXT: (type $0 (sub (func (param i32 (ref $0) (ref null $0) (ref null $0)) (result (ref $0))))) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $9 (descriptor $12) (struct (field i64) (field i16))) -;; CHECK-NEXT: (type $10 (array (mut (ref null $5)))) -;; CHECK-NEXT: (type $11 (sub (shared (func (param (ref $7) f64 (ref $9)) (result (ref null $10)))))) -;; CHECK-NEXT: (type $12 (sub (describes $9) (descriptor $13) (struct (field (ref (shared any))) (field (mut externref)) (field v128) (field (ref null $17))))) -;; CHECK-NEXT: (type $13 (sub (describes $12) (descriptor $17) (struct (field externref) (field (mut i8)) (field (mut i32)) (field (mut f32)) (field i16) (field (mut (ref null $6)))))) -;; CHECK-NEXT: (type $14 (sub (func (result i64)))) -;; CHECK-NEXT: (type $15 (sub (shared (func)))) -;; CHECK-NEXT: (type $16 (shared (func (result (ref null $0))))) -;; CHECK-NEXT: (type $17 (sub (describes $13) (struct (field externref)))) -;; CHECK-NEXT: (type $18 (sub (func (param v128 (ref null $10))))) -;; CHECK-NEXT: (type $19 (sub final $11 (shared (func (param (ref null (shared any)) f64 (ref any)) (result (ref $10)))))) +;; CHECK-NEXT: (type $1 (shared (struct))) +;; CHECK-NEXT: (type $2 (sub (func (result (ref $13))))) +;; CHECK-NEXT: (type $3 (sub (shared (struct (field v128) (field (ref (shared i31))) (field (mut (ref null (shared array)))))))) +;; CHECK-NEXT: (type $4 (sub (shared (func (param i64 i64) (result i64))))) +;; CHECK-NEXT: (type $5 (shared (func (param f64 v128 (ref $19) (ref null $3) (ref $2) (ref null $13) i64) (result (ref null $11))))) +;; CHECK-NEXT: (type $6 (sub (func (result i64)))) +;; CHECK-NEXT: (type $7 (shared (func (result f32)))) +;; CHECK-NEXT: (type $8 (shared (struct))) +;; CHECK-NEXT: (type $9 (shared (func))) +;; CHECK-NEXT: (type $10 (sub (array i64))) +;; CHECK-NEXT: (type $11 (shared (func))) +;; CHECK-NEXT: (type $12 (sub (struct (field (mut i32)) (field i32) (field f64) (field (ref $4))))) +;; CHECK-NEXT: (type $13 (sub (array (mut i64)))) +;; CHECK-NEXT: (type $14 (sub (shared (struct (field (mut i64)) (field v128) (field f64) (field (mut (ref (shared array)))) (field (ref $1)) (field i16))))) +;; CHECK-NEXT: (type $15 (sub (shared (array (mut (ref $16)))))) +;; CHECK-NEXT: (type $16 (sub (shared (array (ref null (shared i31)))))) +;; CHECK-NEXT: (type $17 (sub final $4 (shared (func (param i64 i64) (result i64))))) +;; CHECK-NEXT: (type $18 (sub final $2 (func (result (ref $13))))) +;; CHECK-NEXT: (type $19 (sub $13 (array (mut i64)))) ;; CHECK-NEXT: ) From efeab27173ff7517f684df09984cd5452a1b7f4d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Mar 2026 16:43:31 -0700 Subject: [PATCH 20/24] fix --- src/tools/fuzzing/heap-types.cpp | 23 +++++++++++++++-------- src/tools/wasm-fuzz-types.cpp | 8 +++++++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 7880002888d..19f02680862 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -117,6 +117,10 @@ struct HeapTypeGeneratorImpl { return size; } + // Whether we ever added a signature. We can only emit continuations after + // that point, as they must refer to a signature. + bool addedSignature = false; + void planType(size_t i, size_t numRoots, size_t remaining, @@ -221,8 +225,6 @@ struct HeapTypeGeneratorImpl { } } - bool addedSignature = false; - // Set up the builder entry and type kind for this type. if (super) { typeKinds.push_back(typeKinds[*super]); @@ -238,9 +240,9 @@ struct HeapTypeGeneratorImpl { } else { // This is a root type with no supertype. Choose a kind for this type. auto kind = generateHeapTypeKind(); - // Continuations must be after at least one signature, so we have a - // signature to pick from which appears before them. if (std::get_if(&kind) && !addedSignature) { + // No signature for a continuation. Emit a signature so we can emit one + // later. kind = SignatureKind{}; } typeKinds.emplace_back(kind); @@ -866,15 +868,20 @@ struct HeapTypeGeneratorImpl { } HeapTypeKind generateHeapTypeKind() { - uint32_t numKinds = features.hasStackSwitching() ? 4 : 3; + // Emit continuations less frequently, as we need fewer of them to get + // interesting results. + uint32_t numKinds = features.hasStackSwitching() ? 7 : 6; switch (rand.upTo(numKinds)) { case 0: - return SignatureKind{}; case 1: - return StructKind{}; + return SignatureKind{}; case 2: - return ArrayKind{}; case 3: + return StructKind{}; + case 4: + case 5: + return ArrayKind{}; + case 6: return ContinuationKind{}; } WASM_UNREACHABLE("unexpected index"); diff --git a/src/tools/wasm-fuzz-types.cpp b/src/tools/wasm-fuzz-types.cpp index dc04ae96733..5340e734b1c 100644 --- a/src/tools/wasm-fuzz-types.cpp +++ b/src/tools/wasm-fuzz-types.cpp @@ -321,7 +321,8 @@ void Fuzzer::checkCanonicalization() { builder[index] = getArray(type.getArray()); continue; case HeapTypeKind::Cont: - WASM_UNREACHABLE("TODO: cont"); + builder[index] = getContinuation(type.getContinuation()); + continue; case HeapTypeKind::Basic: break; } @@ -465,6 +466,11 @@ void Fuzzer::checkCanonicalization() { old.element = getField(old.element); return old; } + + Continuation getContinuation(Continuation old) { + // No fields. + return old; + } }; Copier{*this, builder}; From c82876a4a2177c396a155854e6943f8e9f96ec67 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Mar 2026 16:54:27 -0700 Subject: [PATCH 21/24] fix --- src/tools/fuzzing/heap-types.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 19f02680862..64ccde0e96e 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -117,9 +117,9 @@ struct HeapTypeGeneratorImpl { return size; } - // Whether we ever added a signature. We can only emit continuations after - // that point, as they must refer to a signature. - bool addedSignature = false; + // We can only emit continuations after emitting a valid signature for them, + // as the signature must appear first. + bool canEmitContinuation = false; void planType(size_t i, size_t numRoots, @@ -240,15 +240,12 @@ struct HeapTypeGeneratorImpl { } else { // This is a root type with no supertype. Choose a kind for this type. auto kind = generateHeapTypeKind(); - if (std::get_if(&kind) && !addedSignature) { + if (std::get_if(&kind) && !canEmitContinuation) { // No signature for a continuation. Emit a signature so we can emit one // later. kind = SignatureKind{}; } typeKinds.emplace_back(kind); - if (std::get_if(&kind)) { - addedSignature = true; - } // Continuations cannot be shared, but other things can. auto shared = Unshared; if (features.hasSharedEverything() && @@ -256,6 +253,10 @@ struct HeapTypeGeneratorImpl { shared = Shared; } builder[i].setShared(shared); + // Once we emit a non-shared signature, continuations are possible. + if (std::get_if(&kind) && shared == Unshared) { + canEmitContinuation = true; + } } // Plan this descriptor chain for this type if it is not already determined From 433f3f6670ba54b06c3ac5a56aaea96ea4875d60 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 17 Mar 2026 17:15:25 -0700 Subject: [PATCH 22/24] fix --- src/tools/wasm-fuzz-types.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/wasm-fuzz-types.cpp b/src/tools/wasm-fuzz-types.cpp index 5340e734b1c..31809658844 100644 --- a/src/tools/wasm-fuzz-types.cpp +++ b/src/tools/wasm-fuzz-types.cpp @@ -468,7 +468,7 @@ void Fuzzer::checkCanonicalization() { } Continuation getContinuation(Continuation old) { - // No fields. + old.type = getChildHeapType(old.type).get(); return old; } }; From 40de6c99d0415707711847be22620a5c67925b37 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 18 Mar 2026 11:13:44 -0700 Subject: [PATCH 23/24] update.test --- test/lit/fuzz-types.test | 100 ++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 44 deletions(-) diff --git a/test/lit/fuzz-types.test b/test/lit/fuzz-types.test index 3cba2616512..b3683141c96 100644 --- a/test/lit/fuzz-types.test +++ b/test/lit/fuzz-types.test @@ -1,52 +1,64 @@ -;; RUN: wasm-fuzz-types -v --seed=2 | filecheck %s +;; RUN: wasm-fuzz-types -v --seed=3 | filecheck %s -;; CHECK: Running with seed 2 +;; CHECK: Running with seed 3 ;; CHECK-NEXT: Built 20 types: -;; CHECK-NEXT: (type $0 (sub (func (param i32 (ref $0) (ref null $0) (ref null $0)) (result (ref $0))))) -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $1 (shared (struct))) -;; CHECK-NEXT: (type $2 (sub (func (result (ref $13))))) -;; CHECK-NEXT: (type $3 (sub (shared (struct (field v128) (field (ref (shared i31))) (field (mut (ref null (shared array)))))))) -;; CHECK-NEXT: (type $4 (sub (shared (func (param i64 i64) (result i64))))) -;; CHECK-NEXT: (type $5 (shared (func (param f64 v128 (ref $19) (ref null $3) (ref $2) (ref null $13) i64) (result (ref null $11))))) -;; CHECK-NEXT: (type $6 (sub (func (result i64)))) -;; CHECK-NEXT: (type $7 (shared (func (result f32)))) -;; CHECK-NEXT: (type $8 (shared (struct))) -;; CHECK-NEXT: (type $9 (shared (func))) -;; CHECK-NEXT: (type $10 (sub (array i64))) -;; CHECK-NEXT: (type $11 (shared (func))) -;; CHECK-NEXT: (type $12 (sub (struct (field (mut i32)) (field i32) (field f64) (field (ref $4))))) -;; CHECK-NEXT: (type $13 (sub (array (mut i64)))) -;; CHECK-NEXT: (type $14 (sub (shared (struct (field (mut i64)) (field v128) (field f64) (field (mut (ref (shared array)))) (field (ref $1)) (field i16))))) -;; CHECK-NEXT: (type $15 (sub (shared (array (mut (ref $16)))))) -;; CHECK-NEXT: (type $16 (sub (shared (array (ref null (shared i31)))))) -;; CHECK-NEXT: (type $17 (sub final $4 (shared (func (param i64 i64) (result i64))))) -;; CHECK-NEXT: (type $18 (sub final $2 (func (result (ref $13))))) -;; CHECK-NEXT: (type $19 (sub $13 (array (mut i64)))) +;; CHECK-NEXT: (rec +;; CHECK-NEXT: (type $0 (sub (shared (func (result (ref $3) i64))))) +;; CHECK-NEXT: (type $1 (sub (shared (struct (field (mut (ref null (shared extern)))) (field (ref null $3)) (field (mut v128)) (field v128) (field (mut (ref (shared any)))) (field (ref (shared i31))))))) +;; CHECK-NEXT: (type $2 (sub (func (param (ref $1))))) +;; CHECK-NEXT: (type $3 (shared (struct (field f32) (field (mut (ref null (shared eq)))) (field (ref (shared extern))) (field (mut (ref null (shared struct)))) (field (mut f32)) (field (mut (ref null $0)))))) +;; CHECK-NEXT: (type $4 (sub (func (result i64)))) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (rec +;; CHECK-NEXT: (type $5 (struct (field (mut (ref $0))) (field i8) (field i31ref) (field (ref func)) (field f32))) +;; CHECK-NEXT: (type $6 (sub (func (param f64 (ref $7) (ref null $5) f32) (result i32)))) +;; CHECK-NEXT: (type $7 (sub (shared (func (param (ref null $4) (ref null $6)))))) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (rec +;; CHECK-NEXT: (type $8 (struct (field (mut v128)) (field (mut (ref $5))) (field (mut v128)) (field (mut f64)) (field (mut i8)))) +;; CHECK-NEXT: (type $9 (shared (struct (field i32) (field (mut i32)) (field (mut i16))))) +;; CHECK-NEXT: (type $10 (sub (func (param f32 (ref null $4) f64) (result (ref (shared struct)))))) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (rec +;; CHECK-NEXT: (type $11 (cont $16)) +;; CHECK-NEXT: (type $12 (sub final $10 (func (param f32 funcref f64) (result (ref $9))))) +;; CHECK-NEXT: (type $13 (cont $19)) +;; CHECK-NEXT: (type $14 (sub $2 (func (param (ref null (shared struct)))))) +;; CHECK-NEXT: (type $15 (sub (struct (field (mut (ref $7))) (field (mut i8)) (field (mut v128)) (field f64) (field f64) (field i64)))) +;; CHECK-NEXT: (type $16 (sub final $14 (func (param (ref null (shared eq)))))) +;; CHECK-NEXT: (type $17 (sub (func (param f32 v128 (ref $13)) (result (ref $5))))) +;; CHECK-NEXT: (type $18 (struct (field (ref null $6)) (field (ref $17)) (field (mut f64)) (field (mut i64)))) +;; CHECK-NEXT: (type $19 (sub $4 (func (result i64)))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ;; CHECK-NEXT: Inhabitable types: ;; CHECK-NEXT: ;; CHECK-NEXT: Built 20 types: -;; CHECK-NEXT: (type $0 (sub (func (param i32 (ref $0) (ref null $0) (ref null $0)) (result (ref $0))))) -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $1 (shared (struct))) -;; CHECK-NEXT: (type $2 (sub (func (result (ref $13))))) -;; CHECK-NEXT: (type $3 (sub (shared (struct (field v128) (field (ref (shared i31))) (field (mut (ref null (shared array)))))))) -;; CHECK-NEXT: (type $4 (sub (shared (func (param i64 i64) (result i64))))) -;; CHECK-NEXT: (type $5 (shared (func (param f64 v128 (ref $19) (ref null $3) (ref $2) (ref null $13) i64) (result (ref null $11))))) -;; CHECK-NEXT: (type $6 (sub (func (result i64)))) -;; CHECK-NEXT: (type $7 (shared (func (result f32)))) -;; CHECK-NEXT: (type $8 (shared (struct))) -;; CHECK-NEXT: (type $9 (shared (func))) -;; CHECK-NEXT: (type $10 (sub (array i64))) -;; CHECK-NEXT: (type $11 (shared (func))) -;; CHECK-NEXT: (type $12 (sub (struct (field (mut i32)) (field i32) (field f64) (field (ref $4))))) -;; CHECK-NEXT: (type $13 (sub (array (mut i64)))) -;; CHECK-NEXT: (type $14 (sub (shared (struct (field (mut i64)) (field v128) (field f64) (field (mut (ref (shared array)))) (field (ref $1)) (field i16))))) -;; CHECK-NEXT: (type $15 (sub (shared (array (mut (ref $16)))))) -;; CHECK-NEXT: (type $16 (sub (shared (array (ref null (shared i31)))))) -;; CHECK-NEXT: (type $17 (sub final $4 (shared (func (param i64 i64) (result i64))))) -;; CHECK-NEXT: (type $18 (sub final $2 (func (result (ref $13))))) -;; CHECK-NEXT: (type $19 (sub $13 (array (mut i64)))) +;; CHECK-NEXT: (rec +;; CHECK-NEXT: (type $0 (sub (shared (func (result (ref $3) i64))))) +;; CHECK-NEXT: (type $1 (sub (shared (struct (field (mut (ref null (shared extern)))) (field (ref null $3)) (field (mut v128)) (field v128) (field (mut (ref (shared any)))) (field (ref (shared i31))))))) +;; CHECK-NEXT: (type $2 (sub (func (param (ref $1))))) +;; CHECK-NEXT: (type $3 (shared (struct (field f32) (field (mut (ref null (shared eq)))) (field (ref null (shared extern))) (field (mut (ref null (shared struct)))) (field (mut f32)) (field (mut (ref null $0)))))) +;; CHECK-NEXT: (type $4 (sub (func (result i64)))) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (rec +;; CHECK-NEXT: (type $5 (struct (field (mut (ref $0))) (field i8) (field i31ref) (field (ref func)) (field f32))) +;; CHECK-NEXT: (type $6 (sub (func (param f64 (ref $7) (ref null $5) f32) (result i32)))) +;; CHECK-NEXT: (type $7 (sub (shared (func (param (ref null $4) (ref null $6)))))) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (rec +;; CHECK-NEXT: (type $8 (struct (field (mut v128)) (field (mut (ref $5))) (field (mut v128)) (field (mut f64)) (field (mut i8)))) +;; CHECK-NEXT: (type $9 (shared (struct (field i32) (field (mut i32)) (field (mut i16))))) +;; CHECK-NEXT: (type $10 (sub (func (param f32 (ref null $4) f64) (result (ref (shared struct)))))) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (rec +;; CHECK-NEXT: (type $11 (cont $16)) +;; CHECK-NEXT: (type $12 (sub final $10 (func (param f32 funcref f64) (result (ref $9))))) +;; CHECK-NEXT: (type $13 (cont $19)) +;; CHECK-NEXT: (type $14 (sub $2 (func (param (ref null (shared struct)))))) +;; CHECK-NEXT: (type $15 (sub (struct (field (mut (ref $7))) (field (mut i8)) (field (mut v128)) (field f64) (field f64) (field i64)))) +;; CHECK-NEXT: (type $16 (sub final $14 (func (param (ref null (shared eq)))))) +;; CHECK-NEXT: (type $17 (sub (func (param f32 v128 (ref $13)) (result (ref $5))))) +;; CHECK-NEXT: (type $18 (struct (field (ref null $6)) (field (ref $17)) (field (mut f64)) (field (mut i64)))) +;; CHECK-NEXT: (type $19 (sub $4 (func (result i64)))) ;; CHECK-NEXT: ) From 725cfafc26ecc535df0222e03aaebf06c74fa184 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 18 Mar 2026 14:45:42 -0700 Subject: [PATCH 24/24] Update src/tools/fuzzing/heap-types.cpp Co-authored-by: Thomas Lively --- src/tools/fuzzing/heap-types.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 64ccde0e96e..4fef0da80c8 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -346,7 +346,7 @@ struct HeapTypeGeneratorImpl { return rand.pick(bottoms).getBasic(share); } - // Sometimes emit shared in place of unshared. + // Sometimes emit shared in unshared contexts. if (share == Unshared && features.hasSharedEverything() && rand.oneIn(4)) { share = Shared; }