Fuzzing support for waitqueue instructions
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 7369208..c608551 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -220,6 +220,9 @@
   // All struct fields that are mutable.
   std::vector<StructField> mutableStructFields;
 
+  // All struct fields that can be waited on.
+  std::vector<StructField> structWaitFields;
+
   // All arrays that are mutable.
   std::vector<HeapType> mutableArrays;
 
@@ -560,6 +563,8 @@
   Expression* makeStructRMW(Type type);
   Expression* makeStructCmpxchg(Type type);
   Expression* makeStructSet(Type type);
+  Expression* makeStructWait(Type type);
+  Expression* makeWaitqueueNotify(Type type);
   Expression* makeArrayGet(Type type);
   Expression* makeArraySet(Type type);
   Expression* makeArrayRMW(Type type);
diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp
index c28026b..a4cc516 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -560,12 +560,20 @@
         interestingHeapSubTypes[struct_].push_back(type);
         interestingHeapSubTypes[eq].push_back(type);
         interestingHeapSubTypes[any].push_back(type);
-        // Note the mutable fields.
+        // Note the mutable fields and fields that can be waited on.
         auto& fields = type.getStruct().fields;
         for (Index i = 0; i < fields.size(); i++) {
           if (fields[i].mutable_) {
             mutableStructFields.push_back(StructField{type, i});
           }
+          if (!fields[i].isPacked()) {
+            auto fieldType = fields[i].type;
+            if (fieldType == Type::i32 || fieldType == Type::i64 ||
+                Type::isSubType(
+                  fieldType, Type(HeapTypes::eq.getBasic(Shared), Nullable))) {
+              structWaitFields.push_back(StructField{type, i});
+            }
+          }
         }
         break;
       }
@@ -1709,6 +1717,18 @@
     }
   }
 
+  if (!ATOMIC_WAITS) {
+    for (auto& func : wasm.functions) {
+      if (!func->imported()) {
+        for (auto* wait : FindAll<StructWait>(func->body).list) {
+          if (wait->timeout->type == Type::i64) {
+            wait->timeout = builder.makeConst(int64_t(0));
+          }
+        }
+      }
+    }
+  }
+
   // Also fix up closed world, if we need to. We must do this at the end, so
   // nothing can break the closed world assumptions after.
   if (worldMode == WorldMode::Closed) {
@@ -1858,6 +1878,13 @@
         AndInt32, arrayNew->size, builder.makeConst(int32_t(1024 - 1)));
     }
   }
+  if (!ATOMIC_WAITS) {
+    for (auto* wait : FindAll<StructWait>(func->body).list) {
+      if (wait->timeout->type == Type::i64) {
+        wait->timeout = builder.makeConst(int64_t(0));
+      }
+    }
+  }
 }
 
 void TranslateToFuzzReader::recombine(Function* func) {
@@ -2393,6 +2420,14 @@
   } fixer(wasm, *this);
   fixer.walk(func->body);
 
+  if (!ATOMIC_WAITS) {
+    for (auto* wait : FindAll<StructWait>(func->body).list) {
+      if (wait->timeout->type == Type::i64) {
+        wait->timeout = builder.makeConst(int64_t(0));
+      }
+    }
+  }
+
   // Refinalize at the end, after labels are all fixed up.
   ReFinalize().walkFunctionInModule(func, &wasm);
 }
@@ -2838,6 +2873,11 @@
                 &Self::makeStringEq,
                 &Self::makeStringMeasure,
                 &Self::makeStringGet);
+    options.add(FeatureSet::ReferenceTypes | FeatureSet::SharedEverything,
+                &Self::makeWaitqueueNotify);
+    options.add(FeatureSet::ReferenceTypes | FeatureSet::GC |
+                  FeatureSet::SharedEverything,
+                &Self::makeStructWait);
   }
   if (type == Type::i64) {
     options.add(FeatureSet::WideArithmetic | FeatureSet::Multivalue,
@@ -4364,7 +4404,8 @@
     case HeapType::noext:
     case HeapType::nofunc:
     case HeapType::nocont:
-    case HeapType::noexn: {
+    case HeapType::noexn:
+    case HeapType::nowaitqueue: {
       auto null = builder.makeRefNull(heapType.getBasic(share));
       if (!type.isNullable()) {
         return builder.makeRefAs(RefAsNonNull, null);
@@ -4372,9 +4413,11 @@
       return null;
     }
 
-    case HeapType::waitqueue:
-    case HeapType::nowaitqueue: {
-      WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
+    case HeapType::waitqueue: {
+      if (type.isNullable() && oneIn(2)) {
+        return builder.makeRefNull(HeapTypes::sharedWaitqueue.getBasic(share));
+      }
+      return builder.makeWaitqueueNew();
     }
   }
   WASM_UNREACHABLE("invalid basic ref type");
@@ -6017,8 +6060,11 @@
     return makeTrivial(type);
   }
   auto [structType, fieldIndex] = pick(mutableStructFields);
-  auto fieldType = structType.getStruct().fields[fieldIndex].type;
   auto* ref = makeTrappingRefUse(structType);
+  auto fieldType = structType.getStruct().fields[fieldIndex].type;
+  if (ref->type.isStruct()) {
+    fieldType = ref->type.getHeapType().getStruct().fields[fieldIndex].type;
+  }
   auto* value = make(fieldType);
   auto order = MemoryOrder::Unordered;
   if (wasm.features.hasAtomics() && wasm.features.hasSharedEverything() &&
@@ -6028,6 +6074,35 @@
   return builder.makeStructSet(fieldIndex, ref, value, order);
 }
 
+Expression* TranslateToFuzzReader::makeStructWait(Type type) {
+  assert(type == Type::i32);
+  if (structWaitFields.empty()) {
+    return makeTrivial(type);
+  }
+  auto [structType, fieldIndex] = pick(structWaitFields);
+  auto* ref = makeTrappingRefUse(structType);
+  auto* waitqueue = make(Type(HeapTypes::sharedWaitqueue, Nullable));
+  auto fieldType = structType.getStruct().fields[fieldIndex].type;
+  if (ref->type.isStruct()) {
+    fieldType = ref->type.getHeapType().getStruct().fields[fieldIndex].type;
+  }
+  auto* expected = make(fieldType);
+  Expression* timeout = nullptr;
+  if (ATOMIC_WAITS && oneIn(2)) {
+    timeout = make(Type::i64);
+  } else {
+    timeout = builder.makeConst(int64_t(0));
+  }
+  return builder.makeStructWait(fieldIndex, ref, waitqueue, expected, timeout);
+}
+
+Expression* TranslateToFuzzReader::makeWaitqueueNotify(Type type) {
+  assert(type == Type::i32);
+  auto* waitqueue = make(Type(HeapTypes::sharedWaitqueue, Nullable));
+  auto* count = make(Type::i32);
+  return builder.makeWaitqueueNotify(waitqueue, count);
+}
+
 // Make a bounds check for an array operation, given a ref + index. An optional
 // additional length parameter can be provided, which is added to the index if
 // so (that is useful for something like array.fill, which operations on not a
@@ -6662,11 +6737,11 @@
       case HeapType::nofunc:
       case HeapType::nocont:
       case HeapType::noexn:
+      case HeapType::nowaitqueue:
         break;
       case HeapType::waitqueue:
-      case HeapType::nowaitqueue: {
-        WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
-      }
+        return pick(HeapTypes::sharedWaitqueue, HeapTypes::sharedNowaitqueue)
+          .getBasic(share);
     }
   }
   // Look for an interesting subtype.
diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp
index a808bef..3475eef 100644
--- a/src/tools/fuzzing/heap-types.cpp
+++ b/src/tools/fuzzing/heap-types.cpp
@@ -338,6 +338,9 @@
       if (features.hasStackSwitching() && share == Unshared) {
         bottoms.push_back(HeapType::nocont);
       }
+      if (features.hasSharedEverything() && share == Shared) {
+        bottoms.push_back(HeapType::nowaitqueue);
+      }
       return rand.pick(bottoms).getBasic(share);
     }
 
@@ -360,6 +363,9 @@
     if (features.hasExceptionHandling() && share == Unshared) {
       options.push_back(HeapType::exn);
     }
+    if (features.hasSharedEverything() && share == Shared) {
+      options.push_back(HeapType::waitqueue);
+    }
     auto ht = rand.pick(options);
     return ht.getBasic(share);
   }
@@ -685,11 +691,13 @@
         case HeapType::nofunc:
         case HeapType::nocont:
         case HeapType::noexn:
+        case HeapType::nowaitqueue:
           return type;
         case HeapType::waitqueue:
-        case HeapType::nowaitqueue: {
-          WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
-        }
+          if (rand.oneIn(2)) {
+            return HeapTypes::sharedNowaitqueue.getBasic(share);
+          }
+          return type;
       }
       WASM_UNREACHABLE("unexpected type");
     }
@@ -737,6 +745,7 @@
       case HeapType::exn:
       case HeapType::cont:
       case HeapType::any:
+      case HeapType::waitqueue:
         break;
       case HeapType::eq:
         candidates.push_back(HeapTypes::any.getBasic(share));
@@ -762,10 +771,9 @@
       case HeapType::noexn:
         candidates.push_back(HeapTypes::exn.getBasic(share));
         break;
-      case HeapType::waitqueue:
-      case HeapType::nowaitqueue: {
-        WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
-      }
+      case HeapType::nowaitqueue:
+        candidates.push_back(HeapTypes::sharedWaitqueue.getBasic(share));
+        break;
     }
     assert(!candidates.empty());
     return rand.pick(candidates);
diff --git a/test/lit/fuzz-types.test b/test/lit/fuzz-types.test
index 9d39339..32a8241 100644
--- a/test/lit/fuzz-types.test
+++ b/test/lit/fuzz-types.test
@@ -1,6 +1,6 @@
 ;; RUN: wasm-fuzz-types -v --seed=3 | filecheck %s
 
-;; CHECK:      Running with seed 3
+;; CHECK:       Running with seed 3
 ;; CHECK-NEXT: Built 20 types:
 ;; CHECK-NEXT: (rec
 ;; CHECK-NEXT:  (type $0 (sub (shared (func (param i64 f64 exnref (ref null $0)) (result (ref cont))))))
diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
index 0cf4bd8..8a0e348 100644
--- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
+++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
@@ -1,93 +1,97 @@
 Metrics
 total
- [exports]      : 145     
- [funcs]        : 269     
- [globals]      : 6       
- [imports]      : 13      
+ [exports]      : 133     
+ [funcs]        : 251     
+ [globals]      : 23      
+ [imports]      : 15      
  [memories]     : 1       
  [memory-data]  : 31      
- [table-data]   : 80      
+ [table-data]   : 70      
  [tables]       : 2       
  [tags]         : 2       
- [total]        : 93143   
- [vars]         : 4351    
- ArrayCmpxchg   : 15      
+ [total]        : 90629   
+ [vars]         : 4816    
+ ArrayCmpxchg   : 9       
  ArrayCopy      : 34      
- ArrayFill      : 27      
- ArrayGet       : 343     
- ArrayLen       : 468     
- ArrayNew       : 1077    
- ArrayNewFixed  : 550     
- ArrayRMW       : 9       
- ArraySet       : 54      
- AtomicCmpxchg  : 50      
- AtomicFence    : 60      
- AtomicNotify   : 30      
- AtomicRMW      : 29      
- Binary         : 3798    
- Block          : 7102    
- BrOn           : 288     
- Break          : 1081    
- Call           : 1292    
- CallIndirect   : 215     
- CallRef        : 266     
- Const          : 14626   
- ContBind       : 1       
- ContNew        : 68      
- DataDrop       : 10      
- Drop           : 592     
- GlobalGet      : 4826    
- GlobalSet      : 2370    
- I31Get         : 72      
- If             : 2596    
- Load           : 344     
- LocalGet       : 8687    
- LocalSet       : 3915    
- Loop           : 911     
- MemoryCopy     : 13      
- MemoryFill     : 18      
- MemoryInit     : 23      
- Nop            : 795     
- RefAs          : 7167    
- RefCast        : 793     
- RefEq          : 280     
- RefFunc        : 1614    
- RefGetDesc     : 83      
- RefI31         : 707     
- RefIsNull      : 72      
- RefNull        : 8468    
- RefTest        : 80      
- Return         : 464     
- SIMDExtract    : 157     
- SIMDLoad       : 2       
- SIMDReplace    : 1       
- SIMDShift      : 2       
- SIMDShuffle    : 6       
- SIMDTernary    : 3       
- Select         : 348     
- Store          : 159     
- StringConst    : 523     
- StringEncode   : 62      
- StringEq       : 70      
- StringMeasure  : 82      
- StringNew      : 7       
- StringSliceWTF : 2       
- StringWTF16Get : 68      
- StructCmpxchg  : 67      
- StructGet      : 560     
- StructNew      : 9434    
- StructRMW      : 52      
- StructSet      : 78      
- Switch         : 6       
- TableGet       : 2       
- TableSet       : 77      
- Throw          : 58      
- ThrowRef       : 4       
- Try            : 430     
- TryTable       : 440     
- TupleExtract   : 294     
- TupleMake      : 259     
- Unary          : 2249    
- Unreachable    : 1209    
- WideIntAddSub  : 26      
- WideIntMul     : 23      
+ ArrayFill      : 38      
+ ArrayGet       : 530     
+ ArrayLen       : 672     
+ ArrayNew       : 1322    
+ ArrayNewFixed  : 361     
+ ArrayRMW       : 8       
+ ArraySet       : 86      
+ AtomicCmpxchg  : 58      
+ AtomicFence    : 76      
+ AtomicNotify   : 35      
+ AtomicRMW      : 45      
+ Binary         : 4348    
+ Block          : 7428    
+ BrOn           : 308     
+ Break          : 1138    
+ Call           : 1128    
+ CallIndirect   : 115     
+ CallRef        : 259     
+ Const          : 13257   
+ ContBind       : 2       
+ ContNew        : 115     
+ DataDrop       : 13      
+ Drop           : 581     
+ GlobalGet      : 5215    
+ GlobalSet      : 2377    
+ I31Get         : 74      
+ If             : 2796    
+ Load           : 371     
+ LocalGet       : 9381    
+ LocalSet       : 4568    
+ Loop           : 933     
+ MemoryCopy     : 20      
+ MemoryFill     : 16      
+ MemoryInit     : 13      
+ Nop            : 783     
+ Pop            : 348     
+ RefAs          : 5871    
+ RefCast        : 769     
+ RefEq          : 315     
+ RefFunc        : 1385    
+ RefGetDesc     : 87      
+ RefI31         : 796     
+ RefIsNull      : 82      
+ RefNull        : 6323    
+ RefTest        : 68      
+ Return         : 414     
+ SIMDExtract    : 171     
+ SIMDLoad       : 3       
+ SIMDReplace    : 3       
+ SIMDShift      : 3       
+ SIMDShuffle    : 3       
+ SIMDTernary    : 5       
+ Select         : 372     
+ Store          : 146     
+ StringConst    : 544     
+ StringEncode   : 75      
+ StringEq       : 73      
+ StringMeasure  : 79      
+ StringNew      : 3       
+ StringSliceWTF : 3       
+ StringWTF16Get : 78      
+ StructCmpxchg  : 58      
+ StructGet      : 569     
+ StructNew      : 7475    
+ StructRMW      : 58      
+ StructSet      : 64      
+ StructWait     : 69      
+ Switch         : 8       
+ TableGet       : 1       
+ TableSet       : 66      
+ Throw          : 100     
+ ThrowRef       : 10      
+ Try            : 460     
+ TryTable       : 522     
+ TupleExtract   : 337     
+ TupleMake      : 410     
+ Unary          : 2303    
+ Unreachable    : 1221    
+ WaitqueueNew   : 250     
+ WaitqueueNotify: 76      
+ WideIntAddSub  : 16      
+ WideIntMul     : 24