Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GC][EH] Fuzz exnref in GC structs #7288

Merged
merged 13 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions src/tools/fuzzing/fuzzing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1857,8 +1857,10 @@ Expression* TranslateToFuzzReader::make(Type type) {
assert(type == Type::unreachable);
ret = _makeunreachable();
}
// We should create the right type of thing.
assert(Type::isSubType(ret->type, type));
if (!Type::isSubType(ret->type, type)) {
Fatal() << "Did not generate the right type of " << type
kripken marked this conversation as resolved.
Show resolved Hide resolved
<< ", instead we have " << ret->type << " : " << *ret << '\n';
}
nesting--;
return ret;
}
Expand Down Expand Up @@ -3273,13 +3275,13 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
return builder.makeArrayNewFixed(ht, {});
}
case HeapType::exn: {
// If nullable, we can emit a null. If not, generate an exnref using a
// throw in a try_table.
if (type.isNullable() && oneIn(2)) {
// If nullable, we can emit a null. If there is no function context, then
// we must do so, as the other option is a throw in a block, which are not
// possible outside of functions.
if ((type.isNullable() && oneIn(2)) || !funcContext) {
return builder.makeRefNull(HeapTypes::exn.getBasic(share));
}

// Make a catch_all_ref to a block.
auto* throww = makeThrow(Type::unreachable);
auto label = makeLabel();
auto* tryy = builder.makeTryTable(throww, {Name()}, {label}, {true});
Expand Down Expand Up @@ -5101,6 +5103,7 @@ HeapType TranslateToFuzzReader::getSubType(HeapType type) {
case HeapType::array:
return pick(HeapTypes::array, HeapTypes::none).getBasic(share);
case HeapType::exn:
assert(share == Unshared);
return HeapTypes::exn.getBasic(share);
case HeapType::string:
assert(share == Unshared);
Expand Down Expand Up @@ -5133,7 +5136,13 @@ Type TranslateToFuzzReader::getSubType(Type type) {
}
return Type(types);
} else if (type.isRef()) {
auto heapType = getSubType(type.getHeapType());
auto heapType = type.getHeapType();
// Do not generate non-nullable exnrefs in global positions (they cannot be
// created in wasm, nor imported from JS).
if (!funcContext && heapType.isMaybeShared(HeapType::exn)) {
return type;
}
heapType = getSubType(heapType);
auto nullability = getSubType(type.getNullability());
auto subType = Type(heapType, nullability);
// We don't want to emit lots of uninhabitable types like (ref none), so
Expand Down
41 changes: 31 additions & 10 deletions src/tools/fuzzing/heap-types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,27 @@ struct HeapTypeGeneratorImpl {

HeapType::BasicHeapType generateBasicHeapType(Shareability share) {
// Choose bottom types more rarely.
// TODO: string, exn, and cont types
// TODO: string and cont types
if (rand.oneIn(16)) {
HeapType ht =
rand.pick(HeapType::noext, HeapType::nofunc, HeapType::none);
return ht.getBasic(share);
}
HeapType ht = rand.pick(HeapType::func,
HeapType::ext,
HeapType::any,
HeapType::eq,
HeapType::i31,
HeapType::struct_,
HeapType::array);
if (share == Unshared && features.hasSharedEverything() && rand.oneIn(2)) {

std::vector<HeapType> options{HeapType::func,
HeapType::ext,
HeapType::any,
HeapType::eq,
HeapType::i31,
HeapType::struct_,
HeapType::array};
Comment on lines +164 to +170
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we check for features.hasExceptionHandling() for exn, don't we also need to check for features.hasGC() or something for these?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this entire file is for fuzzing of heap types, so GC is assumed everywhere. This is a separate file from the main fuzzer.

// Avoid shared exn, which we cannot generate.
if (features.hasExceptionHandling() && share == Unshared) {
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);
Expand Down Expand Up @@ -203,7 +210,15 @@ struct HeapTypeGeneratorImpl {

Type generateRefType(Shareability share) {
auto heapType = generateHeapType(share);
auto nullability = rand.oneIn(2) ? Nullable : NonNullable;
Nullability nullability;
if (heapType.isMaybeShared(HeapType::exn)) {
// Do not generate non-nullable exnrefs for now, as we cannot generate
// them in global positions (they cannot be created in wasm, nor imported
// from JS).
nullability = Nullable;
} else {
nullability = rand.oneIn(2) ? Nullable : NonNullable;
}
return builder.getTempRefType(heapType, nullability);
}

Expand Down Expand Up @@ -521,6 +536,12 @@ struct HeapTypeGeneratorImpl {
};

Ref generateSubRef(Ref super) {
if (super.type.isMaybeShared(HeapType::exn)) {
// Do not generate non-nullable exnrefs for now, as we cannot generate
// them in global positions (they cannot be created in wasm, nor imported
// from JS). There are also no subtypes to consider, so just return.
return super;
}
auto nullability = super.nullability == NonNullable
? NonNullable
: rand.oneIn(2) ? Nullable : NonNullable;
Expand Down
26 changes: 13 additions & 13 deletions test/lit/fuzz-types.test
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
;; CHECK-NEXT: Built 20 types:
;; CHECK-NEXT: (rec
;; CHECK-NEXT: (type $0 (sub (struct (field (mut i16)) (field (mut (ref $2))) (field (mut (ref null $2))))))
;; CHECK-NEXT: (type $1 (sub (func (param (ref $1)) (result f64 (ref $0) f32 (ref null (shared eq))))))
;; CHECK-NEXT: (type $1 (sub (func (param (ref $1)) (result f64 (ref $0) f32 structref))))
;; CHECK-NEXT: (type $2 (sub (shared (struct (field (mut (ref null (shared extern)))) (field (mut (ref null $2)))))))
;; CHECK-NEXT: (type $3 (sub (shared (struct))))
;; CHECK-NEXT: )
Expand All @@ -22,21 +22,21 @@
;; CHECK-NEXT: (type $12 (sub (shared (array (ref $3)))))
;; CHECK-NEXT: (type $13 (sub (shared (func (param (ref null $19) v128) (result (ref null $12))))))
;; CHECK-NEXT: (type $14 (sub final $12 (shared (array (ref $3)))))
;; CHECK-NEXT: (type $15 (sub (shared (func (param (ref null (shared struct)) i31ref) (result nullfuncref)))))
;; CHECK-NEXT: (type $15 (sub (shared (func (param i31ref (ref $5)) (result i32)))))
;; CHECK-NEXT: (type $16 (sub $5 (array i32)))
;; CHECK-NEXT: (type $17 (sub (func (param v128) (result f64))))
;; CHECK-NEXT: (type $18 (sub (array (ref $11))))
;; CHECK-NEXT: (type $19 (shared (array i8)))
;; CHECK-NEXT: (type $17 (sub (func (result (ref $7)))))
;; CHECK-NEXT: (type $18 (sub (array (mut i8))))
;; CHECK-NEXT: (type $19 (shared (array v128)))
;; CHECK-NEXT: )
;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: Inhabitable types:
;; CHECK-NEXT:
;; CHECK-NEXT:
;; CHECK-NEXT: Built 20 types:
;; CHECK-NEXT: (rec
;; CHECK-NEXT: (type $0 (sub (struct (field (mut i16)) (field (mut (ref $2))) (field (mut (ref null $2))))))
;; CHECK-NEXT: (type $1 (sub (func (param (ref $1)) (result f64 (ref $0) f32 (ref null (shared eq))))))
;; CHECK-NEXT: (type $1 (sub (func (param (ref $1)) (result f64 (ref $0) f32 structref))))
;; CHECK-NEXT: (type $2 (sub (shared (struct (field (mut (ref null (shared extern)))) (field (mut (ref null $2)))))))
;; CHECK-NEXT: (type $3 (sub (shared (struct)))
;; CHECK-NEXT: (type $3 (sub (shared (struct))))
;; CHECK-NEXT: )
;; CHECK-NEXT: (rec
;; CHECK-NEXT: (type $4 (sub (array i32)))
Expand All @@ -52,9 +52,9 @@
;; CHECK-NEXT: (type $12 (sub (shared (array (ref $3)))))
;; CHECK-NEXT: (type $13 (sub (shared (func (param (ref null $19) v128) (result (ref null $12))))))
;; CHECK-NEXT: (type $14 (sub final $12 (shared (array (ref $3)))))
;; CHECK-NEXT: (type $15 (sub (shared (func (param (ref null (shared struct)) i31ref) (result nullfuncref)))))
;; CHECK-NEXT: (type $15 (sub (shared (func (param i31ref (ref $5)) (result i32)))))
;; CHECK-NEXT: (type $16 (sub $5 (array i32)))
;; CHECK-NEXT: (type $17 (sub (func (param v128) (result f64))))
;; CHECK-NEXT: (type $18 (sub (array (ref $11))))
;; CHECK-NEXT: (type $19 (shared (array i8)))
;; CHECK-NEXT: (type $17 (sub (func (result (ref $7)))))
;; CHECK-NEXT: (type $18 (sub (array (mut i8))))
;; CHECK-NEXT: (type $19 (shared (array v128)))
;; CHECK-NEXT: )
Loading