Merge remote-tracking branch 'origin/main' into small.tu
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index 6beb2b9..4fbd4cf 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -81,8 +81,8 @@
       case HeapType::i31:
         WASM_UNREACHABLE("TODO: i31");
       case HeapType::ext:
-      case HeapType::any:
         WASM_UNREACHABLE("TODO: extern literals");
+      case HeapType::any:
       case HeapType::eq:
       case HeapType::func:
       case HeapType::struct_:
diff --git a/src/literal.h b/src/literal.h
index 9d7ac22..b484fc3 100644
--- a/src/literal.h
+++ b/src/literal.h
@@ -45,17 +45,17 @@
     uint8_t v128[16];
     // funcref function name. `isNull()` indicates a `null` value.
     Name func;
-    // A reference to GC data, either a Struct or an Array. For both of those
-    // we store the referred data as a Literals object (which is natural for an
+    // A reference to GC data, either a Struct or an Array. For both of those we
+    // store the referred data as a Literals object (which is natural for an
     // Array, and for a Struct, is just the fields in order). The type is used
     // to indicate whether this is a Struct or an Array, and of what type. We
     // also use this to store String data, as it is similarly stored on the
-    // heap.
+    // heap. For externrefs, the gcData is the same as for the corresponding
+    // internal references and the values are only differentiated by the type.
+    // Externalized i31 references have a gcData containing the internal i31
+    // reference as its sole value even though internal i31 references do not
+    // have a gcData.
     std::shared_ptr<GCData> gcData;
-    // TODO: Literals of type `anyref` can only be `null` currently but we
-    // will need to represent external values eventually, to
-    // 1) run the spec tests and fuzzer with reference types enabled and
-    // 2) avoid bailing out when seeing a reference typed value in precompute
   };
 
 public:
@@ -665,6 +665,9 @@
   Literal relaxedFmaF64x2(const Literal& left, const Literal& right) const;
   Literal relaxedFmsF64x2(const Literal& left, const Literal& right) const;
 
+  Literal externalize() const;
+  Literal internalize() const;
+
 private:
   Literal addSatSI8(const Literal& other) const;
   Literal addSatUI8(const Literal& other) const;
diff --git a/src/mixed_arena.h b/src/mixed_arena.h
index a36f72a..4d8cda0 100644
--- a/src/mixed_arena.h
+++ b/src/mixed_arena.h
@@ -253,8 +253,9 @@
     if (allocatedElements < size) {
       static_cast<SubType*>(this)->allocate(size);
     }
-    for (size_t i = 0; i < size; i++) {
-      data[i] = list[i];
+    size_t i = 0;
+    for (auto elem : list) {
+      data[i++] = elem;
     }
     usedElements = size;
   }
diff --git a/src/passes/MergeSimilarFunctions.cpp b/src/passes/MergeSimilarFunctions.cpp
index 8e03956..525d7c3 100644
--- a/src/passes/MergeSimilarFunctions.cpp
+++ b/src/passes/MergeSimilarFunctions.cpp
@@ -565,7 +565,9 @@
             operands.push_back(
               ExpressionManipulator::flexibleCopy(operand, *module, copier));
           }
-          return builder.makeCallRef(paramExpr, operands, call->type);
+          auto returnType = module->getFunction(call->target)->getResults();
+          return builder.makeCallRef(
+            paramExpr, operands, returnType, call->isReturn);
         }
       }
     }
@@ -616,6 +618,7 @@
     callOperands.push_back(value);
   }
 
+  // TODO: make a return_call when possible?
   auto ret = builder.makeCall(shared->name, callOperands, target->getResults());
   target->vars.clear();
   target->body = ret;
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 4a4fe60..c9a7170 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -3283,7 +3283,7 @@
     } else {
       for (auto* entry : curr->data) {
         o << ' ';
-        printExpression(entry, o);
+        visit(entry);
       }
     }
     o << ')' << maybeNewLine;
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index b8be674..c67d6c9 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -317,6 +317,7 @@
   // TODO: support other RefIs variants, and rename this
   Expression* makeRefIsNull(Type type);
   Expression* makeRefEq(Type type);
+  Expression* makeRefTest(Type type);
   Expression* makeI31New(Type type);
   Expression* makeI31Get(Type type);
   Expression* makeMemoryInit();
diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp
index 9beedc7..73aff5a 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -1077,6 +1077,7 @@
     options.add(FeatureSet::ReferenceTypes, &Self::makeRefIsNull);
     options.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
                 &Self::makeRefEq,
+                &Self::makeRefTest,
                 &Self::makeI31Get);
   }
   if (type.isTuple()) {
@@ -3094,6 +3095,42 @@
   return builder.makeRefEq(left, right);
 }
 
+Expression* TranslateToFuzzReader::makeRefTest(Type type) {
+  assert(type == Type::i32);
+  assert(wasm.features.hasReferenceTypes() && wasm.features.hasGC());
+  // The case of the reference and the cast type having a connection is useful,
+  // so give a decent chance for one to be a subtype of the other.
+  Type refType, castType;
+  switch (upTo(3)) {
+    case 0:
+      // Totally random.
+      refType = getReferenceType();
+      castType = getReferenceType();
+      // They must share a bottom type in order to validate.
+      if (refType.getHeapType().getBottom() ==
+          castType.getHeapType().getBottom()) {
+        break;
+      }
+      // Otherwise, fall through and generate things in a way that is
+      // guaranteed to validate.
+      [[fallthrough]];
+    case 1:
+      // Cast is a subtype of ref.
+      refType = getReferenceType();
+      castType = getSubType(refType);
+      break;
+    case 2:
+      // Ref is a subtype of cast.
+      castType = getReferenceType();
+      refType = getSubType(castType);
+      break;
+    default:
+      // This unreachable avoids a warning on refType being possibly undefined.
+      WASM_UNREACHABLE("bad case");
+  }
+  return builder.makeRefTest(make(refType), castType);
+}
+
 Expression* TranslateToFuzzReader::makeI31New(Type type) {
   assert(type.isRef() && type.getHeapType() == HeapType::i31);
   assert(wasm.features.hasReferenceTypes() && wasm.features.hasGC());
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp
index 00bceac..195a535 100644
--- a/src/tools/wasm-ctor-eval.cpp
+++ b/src/tools/wasm-ctor-eval.cpp
@@ -572,13 +572,28 @@
   // If |possibleDefiningGlobal| is provided, it is the name of a global that we
   // are in the init expression of, and which can be reused as defining global,
   // if the other conditions are suitable.
-  Expression* getSerialization(const Literal& value,
+  Expression* getSerialization(Literal value,
                                Name possibleDefiningGlobal = Name()) {
     Builder builder(*wasm);
 
+    // If this is externalized then we want to inspect the inner data, handle
+    // that, and emit a ref.externalize around it as needed. To simplify the
+    // logic here, we save the original (possible externalized) value, and then
+    // look at the internals from here on out.
+    Literal original = value;
+    if (value.type.isRef() && value.type.getHeapType() == HeapType::ext) {
+      value = value.internalize();
+
+      // We cannot serialize truly external things, only data and i31s.
+      assert(value.isData() || value.type.getHeapType() == HeapType::i31);
+    }
+
+    // GC data (structs and arrays) must be handled with the special global-
+    // creating logic later down. But MVP types as well as i31s (even
+    // externalized i31s) can be handled by the general makeConstantExpression
+    // logic (which knows how to handle externalization, for i31s).
     if (!value.isData()) {
-      // This can be handled normally.
-      return builder.makeConstantExpression(value);
+      return builder.makeConstantExpression(original);
     }
 
     // This is GC data, which we must handle in a more careful way.
@@ -637,7 +652,13 @@
 
     // Refer to this GC allocation by reading from the global that is
     // designated to contain it.
-    return builder.makeGlobalGet(definingGlobal, value.type);
+    Expression* ret = builder.makeGlobalGet(definingGlobal, value.type);
+    if (original != value) {
+      // The original is externalized.
+      assert(original.type.getHeapType() == HeapType::ext);
+      ret = builder.makeRefAs(ExternExternalize, ret);
+    }
+    return ret;
   }
 
   Expression* getSerialization(const Literals& values,
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 3e9577f..5f597cb 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -168,7 +168,6 @@
   }
 
   // IR nodes
-
   Nop* makeNop() { return wasm.allocator.alloc<Nop>(); }
   Block* makeBlock(Expression* first = nullptr) {
     auto* ret = wasm.allocator.alloc<Block>();
@@ -184,59 +183,47 @@
     ret->finalize();
     return ret;
   }
-  Block* makeBlock(const std::vector<Expression*>& items) {
+
+  template<typename T>
+  using bool_if_not_expr_t =
+    std::enable_if_t<std::negation_v<std::is_convertible<T, Expression*>>,
+                     bool>;
+
+  template<typename T, bool_if_not_expr_t<T> = true>
+  Block* makeBlock(const T& items) {
     auto* ret = wasm.allocator.alloc<Block>();
     ret->list.set(items);
     ret->finalize();
     return ret;
   }
-  Block* makeBlock(const std::vector<Expression*>& items, Type type) {
+
+  template<typename T, bool_if_not_expr_t<T> = true>
+  Block* makeBlock(const T& items, Type type) {
     auto* ret = wasm.allocator.alloc<Block>();
     ret->list.set(items);
     ret->finalize(type);
     return ret;
   }
+
+  template<typename T, bool_if_not_expr_t<T> = true>
+  Block* makeBlock(Name name, const T& items, Type type) {
+    auto* ret = wasm.allocator.alloc<Block>();
+    ret->name = name;
+    ret->list.set(items);
+    ret->finalize(type);
+    return ret;
+  }
+  Block* makeBlock(std::initializer_list<Expression*>&& items) {
+    return makeBlock(items);
+  }
+  Block* makeBlock(std::initializer_list<Expression*>&& items, Type type) {
+    return makeBlock(items, type);
+  }
   Block*
-  makeBlock(Name name, const std::vector<Expression*>& items, Type type) {
-    auto* ret = wasm.allocator.alloc<Block>();
-    ret->name = name;
-    ret->list.set(items);
-    ret->finalize(type);
-    return ret;
+  makeBlock(Name name, std::initializer_list<Expression*>&& items, Type type) {
+    return makeBlock(name, items, type);
   }
-  Block* makeBlock(const ExpressionList& items) {
-    auto* ret = wasm.allocator.alloc<Block>();
-    ret->list.set(items);
-    ret->finalize();
-    return ret;
-  }
-  Block* makeBlock(const ExpressionList& items, Type type) {
-    auto* ret = wasm.allocator.alloc<Block>();
-    ret->list.set(items);
-    ret->finalize(type);
-    return ret;
-  }
-  Block* makeBlock(Name name, const ExpressionList& items) {
-    auto* ret = wasm.allocator.alloc<Block>();
-    ret->name = name;
-    ret->list.set(items);
-    ret->finalize();
-    return ret;
-  }
-  Block* makeBlock(Name name, const ExpressionList& items, Type type) {
-    auto* ret = wasm.allocator.alloc<Block>();
-    ret->name = name;
-    ret->list.set(items);
-    ret->finalize(type);
-    return ret;
-  }
-  template<size_t N>
-  Block* makeBlock(const SmallVector<Expression*, N>& items) {
-    auto* ret = wasm.allocator.alloc<Block>();
-    ret->list.set(items);
-    ret->finalize();
-    return ret;
-  }
+
   If* makeIf(Expression* condition,
              Expression* ifTrue,
              Expression* ifFalse = nullptr) {
@@ -1169,6 +1156,10 @@
       }
       return makeStringConst(string);
     }
+    if (type.isRef() && type.getHeapType() == HeapType::ext) {
+      return makeRefAs(ExternExternalize,
+                       makeConstantExpression(value.internalize()));
+    }
     TODO_SINGLE_COMPOUND(type);
     WASM_UNREACHABLE("unsupported constant expression");
   }
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index afc193f..75f2a83 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1785,16 +1785,16 @@
     }
     const auto& value = flow.getSingleValue();
     NOTE_EVAL1(value);
-    if (value.isNull()) {
-      trap("null ref");
-    }
     switch (curr->op) {
       case RefAsNonNull:
-        // We've already checked for a null.
+        if (value.isNull()) {
+          trap("null ref");
+        }
         return value;
       case ExternInternalize:
+        return value.internalize();
       case ExternExternalize:
-        WASM_UNREACHABLE("unimplemented extern conversion");
+        return value.externalize();
     }
     WASM_UNREACHABLE("unimplemented ref.as_*");
   }
diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp
index 2a7dd1b..230eeae 100644
--- a/src/wasm/literal.cpp
+++ b/src/wasm/literal.cpp
@@ -70,10 +70,11 @@
 }
 
 Literal::Literal(std::shared_ptr<GCData> gcData, HeapType type)
-  : gcData(gcData), type(type, NonNullable) {
+  : gcData(gcData), type(type, gcData ? NonNullable : Nullable) {
   // The type must be a proper type for GC data: either a struct, array, or
-  // string; or a null.
-  assert((isData() && gcData) || (type.isBottom() && !gcData));
+  // string; or an externalized version of the same; or a null.
+  assert((isData() && gcData) || (type == HeapType::ext && gcData) ||
+         (type.isBottom() && !gcData));
 }
 
 Literal::Literal(std::string string)
@@ -110,7 +111,7 @@
     new (&gcData) std::shared_ptr<GCData>();
     return;
   }
-  if (other.isData()) {
+  if (other.isData() || other.type.getHeapType() == HeapType::ext) {
     new (&gcData) std::shared_ptr<GCData>(other.gcData);
     return;
   }
@@ -126,14 +127,14 @@
         case HeapType::i31:
           i32 = other.i32;
           return;
+        case HeapType::ext:
+          gcData = other.gcData;
+          return;
         case HeapType::none:
         case HeapType::noext:
         case HeapType::nofunc:
-          // Null
-          return;
-        case HeapType::ext:
+          WASM_UNREACHABLE("null literals should already have been handled");
         case HeapType::any:
-          WASM_UNREACHABLE("TODO: extern literals");
         case HeapType::eq:
         case HeapType::func:
         case HeapType::struct_:
@@ -154,7 +155,7 @@
   if (type.isBasic()) {
     return;
   }
-  if (isNull() || isData()) {
+  if (isNull() || isData() || type.getHeapType() == HeapType::ext) {
     gcData.~shared_ptr();
   }
 }
@@ -584,8 +585,9 @@
           o << "nullfuncref";
           break;
         case HeapType::ext:
+          o << "externref";
+          break;
         case HeapType::any:
-          WASM_UNREACHABLE("TODO: extern literals");
         case HeapType::eq:
         case HeapType::func:
         case HeapType::struct_:
@@ -2590,4 +2592,42 @@
   return ternary<2, &Literal::getLanesF64x2, &Literal::fms>(*this, left, right);
 }
 
+Literal Literal::externalize() const {
+  assert(Type::isSubType(type, Type(HeapType::any, Nullable)) &&
+         "can only externalize internal references");
+  if (isNull()) {
+    return Literal(std::shared_ptr<GCData>{}, HeapType::noext);
+  }
+  auto heapType = type.getHeapType();
+  if (heapType.isBasic()) {
+    switch (heapType.getBasic()) {
+      case HeapType::i31: {
+        return Literal(std::make_shared<GCData>(HeapType::i31, Literals{*this}),
+                       HeapType::ext);
+      }
+      case HeapType::string:
+      case HeapType::stringview_wtf8:
+      case HeapType::stringview_wtf16:
+      case HeapType::stringview_iter:
+        WASM_UNREACHABLE("TODO: string literals");
+      default:
+        WASM_UNREACHABLE("unexpected type");
+    }
+  }
+  return Literal(gcData, HeapType::ext);
+}
+
+Literal Literal::internalize() const {
+  assert(Type::isSubType(type, Type(HeapType::ext, Nullable)) &&
+         "can only internalize external references");
+  if (isNull()) {
+    return Literal(std::shared_ptr<GCData>{}, HeapType::none);
+  }
+  if (gcData->type == HeapType::i31) {
+    assert(gcData->values[0].type.getHeapType() == HeapType::i31);
+    return gcData->values[0];
+  }
+  return Literal(gcData, gcData->type);
+}
+
 } // namespace wasm
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 518a809..9368325 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -3222,9 +3222,6 @@
     if (isPassive || hasTableIdx) {
       if (usesExpressions) {
         segment->type = getType();
-        if (!segment->type.isFunction()) {
-          throwError("Invalid type for a usesExpressions element segment");
-        }
       } else {
         auto elemKind = getU32LEB();
         if (elemKind != 0x0) {
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 522c3f3..77e265f 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -3561,7 +3561,7 @@
 void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) {
   std::unique_ptr<Global> global = make_unique<Global>();
   size_t i = 1;
-  if (s[i]->dollared() && !(s[i]->isStr() && isType(s[i]->str()))) {
+  if (s[i]->dollared()) {
     global->setExplicitName(s[i++]->str());
   } else if (preParseImport) {
     global->name = Name("gimport$" + std::to_string(globalCounter));
@@ -3572,7 +3572,6 @@
   globalNames.push_back(global->name);
   bool mutable_ = false;
   Type type = Type::none;
-  bool exported = false;
   Name importModule, importBase;
   while (i < s.size() && s[i]->isList()) {
     auto& inner = *s[i++];
@@ -3585,7 +3584,6 @@
         throw ParseException("duplicate export", s.line, s.col);
       }
       wasm.addExport(ex.release());
-      exported = true;
     } else if (elementStartsWith(inner, IMPORT)) {
       importModule = inner[1]->str();
       importBase = inner[2]->str();
@@ -3598,9 +3596,6 @@
       break;
     }
   }
-  if (exported && mutable_) {
-    throw ParseException("cannot export a mutable global", s.line, s.col);
-  }
   if (type == Type::none) {
     type = stringToType(s[i++]->str());
   }
@@ -3784,11 +3779,6 @@
       segment->type = elementToType(*s[i]);
       usesExpressions = true;
       i += 1;
-
-      if (!segment->type.isFunction()) {
-        throw ParseException(
-          "Invalid type for an element segment.", s.line, s.col);
-      }
     }
   }
 
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index e0391a7..123a15c 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -3405,13 +3405,9 @@
   }
 
   for (auto& segment : module.elementSegments) {
-    // Since element segment items need to be constant expressions, that leaves
-    // us with ref.null, ref.func and global.get. As a result, the only possible
-    // type for element segments will be function references.
-    // TODO: This is not true! Allow GC data here (#4846).
-    info.shouldBeTrue(segment->type.isFunction(),
+    info.shouldBeTrue(segment->type.isRef(),
                       "elem",
-                      "element segment type must be of function type.");
+                      "element segment type must be of reference type.");
     info.shouldBeTrue(
       segment->type.isNullable(),
       "elem",
@@ -3443,18 +3439,15 @@
                         "elem",
                         "non-table segment offset should have no offset");
     }
-    // Avoid double checking items
-    if (module.features.hasReferenceTypes()) {
-      for (auto* expr : segment->data) {
-        info.shouldBeTrue(Properties::isValidConstantExpression(module, expr),
-                          expr,
-                          "element must be a constant expression");
-        info.shouldBeSubType(expr->type,
-                             segment->type,
-                             expr,
-                             "element must be a subtype of the segment type");
-        validator.validate(expr);
-      }
+    for (auto* expr : segment->data) {
+      info.shouldBeTrue(Properties::isValidConstantExpression(module, expr),
+                        expr,
+                        "element must be a constant expression");
+      info.shouldBeSubType(expr->type,
+                           segment->type,
+                           expr,
+                           "element must be a subtype of the segment type");
+      validator.validate(expr);
     }
   }
 }
diff --git a/test/lit/ctor-eval/extern.wast b/test/lit/ctor-eval/extern.wast
new file mode 100644
index 0000000..1bd7383
--- /dev/null
+++ b/test/lit/ctor-eval/extern.wast
@@ -0,0 +1,59 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+;; RUN: wasm-ctor-eval %s --ctors=test1,test2 --kept-exports=test1,test2 --quiet -all -S -o - | filecheck %s
+
+(module
+ ;; CHECK:      (type $array (array (mut i8)))
+ (type $array (array (mut i8)))
+
+ ;; CHECK:      (type $none_=>_externref (func (result externref)))
+
+ ;; CHECK:      (global $ctor-eval$global (ref $array) (array.new_fixed $array
+ ;; CHECK-NEXT:  (i32.const 1)
+ ;; CHECK-NEXT:  (i32.const 2)
+ ;; CHECK-NEXT:  (i32.const 3)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK:      (export "test1" (func $test1_0))
+ (export "test1" (func $test1))
+ ;; CHECK:      (export "test2" (func $test2_0))
+ (export "test2" (func $test2))
+
+ (func $test1 (result externref)
+  ;; This will remain almost the same, even though we eval it, since the
+  ;; serialization of an externalized i31 is what is written here. But the add
+  ;; will be evalled out.
+  (extern.externalize
+   (i31.new
+    (i32.add
+     (i32.const 41)
+     (i32.const 1)
+    )
+   )
+  )
+ )
+
+ (func $test2 (result externref)
+  ;; This will be evalled into an externalization of a global.get.
+  (extern.externalize
+   (array.new_fixed $array
+    (i32.const 1)
+    (i32.const 2)
+    (i32.const 3)
+   )
+  )
+ )
+)
+
+;; CHECK:      (func $test1_0 (type $none_=>_externref) (result externref)
+;; CHECK-NEXT:  (extern.externalize
+;; CHECK-NEXT:   (i31.new
+;; CHECK-NEXT:    (i32.const 42)
+;; CHECK-NEXT:   )
+;; CHECK-NEXT:  )
+;; CHECK-NEXT: )
+
+;; CHECK:      (func $test2_0 (type $none_=>_externref) (result externref)
+;; CHECK-NEXT:  (extern.externalize
+;; CHECK-NEXT:   (global.get $ctor-eval$global)
+;; CHECK-NEXT:  )
+;; CHECK-NEXT: )
diff --git a/test/lit/passes/merge-similar-functions_all-features.wast b/test/lit/passes/merge-similar-functions_all-features.wast
index 873b645..1592989 100644
--- a/test/lit/passes/merge-similar-functions_all-features.wast
+++ b/test/lit/passes/merge-similar-functions_all-features.wast
@@ -1,10 +1,16 @@
-;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
 ;; RUN: foreach %s %t wasm-opt --all-features --merge-similar-functions -S -o - | filecheck %s
 
 (module
+  ;; CHECK:      (type $none_=>_none (func))
+
   ;; CHECK:      (type $[i8] (array i8))
   (type $[i8] (array i8))
 
+  ;; CHECK:      (type $arrayref_=>_none (func (param arrayref)))
+
+  ;; CHECK:      (type $ref|eq|_=>_none (func (param (ref eq))))
+
   ;; CHECK:      (func $take-ref-null-array (type $arrayref_=>_none) (param $0 arrayref)
   ;; CHECK-NEXT:  (unreachable)
   ;; CHECK-NEXT: )
@@ -86,5 +92,77 @@
       (array.new_fixed $[i8])
     )
   )
-
 )
+
+;; Test that we can merge properly when there is a return_call.
+(module
+  ;; CHECK:      (type $none_=>_i32 (func (result i32)))
+
+  ;; CHECK:      (type $ref|none_->_i32|_=>_i32 (func (param (ref $none_=>_i32)) (result i32)))
+
+  ;; CHECK:      (elem declare func $return_a $return_b)
+
+  ;; CHECK:      (func $return_call_a (type $none_=>_i32) (result i32)
+  ;; CHECK-NEXT:  (call $byn$mgfn-shared$return_call_a
+  ;; CHECK-NEXT:   (ref.func $return_a)
+  ;; CHECK-NEXT:  )
+  ;; CHECK-NEXT: )
+  (func $return_call_a (result i32)
+    (nop) (nop) (nop) (nop) (nop) (nop)
+    (nop) (nop) (nop) (nop) (nop) (nop)
+    (nop) (nop) (nop) (nop) (nop) (nop)
+    (return_call $return_a)
+  )
+
+  ;; CHECK:      (func $return_call_b (type $none_=>_i32) (result i32)
+  ;; CHECK-NEXT:  (call $byn$mgfn-shared$return_call_a
+  ;; CHECK-NEXT:   (ref.func $return_b)
+  ;; CHECK-NEXT:  )
+  ;; CHECK-NEXT: )
+  (func $return_call_b (result i32)
+    (nop) (nop) (nop) (nop) (nop) (nop)
+    (nop) (nop) (nop) (nop) (nop) (nop)
+    (nop) (nop) (nop) (nop) (nop) (nop)
+    ;; As above, but now use a return_call.
+    (return_call $return_b)
+  )
+
+  ;; CHECK:      (func $return_a (type $none_=>_i32) (result i32)
+  ;; CHECK-NEXT:  (i32.const 0)
+  ;; CHECK-NEXT: )
+  (func $return_a (result i32)
+    ;; Helper function.
+    (i32.const 0)
+  )
+
+  ;; CHECK:      (func $return_b (type $none_=>_i32) (result i32)
+  ;; CHECK-NEXT:  (i32.const 1)
+  ;; CHECK-NEXT: )
+  (func $return_b (result i32)
+    ;; Helper function.
+    (i32.const 1)
+  )
+)
+;; CHECK:      (func $byn$mgfn-shared$return_call_a (type $ref|none_->_i32|_=>_i32) (param $0 (ref $none_=>_i32)) (result i32)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (nop)
+;; CHECK-NEXT:  (return_call_ref $none_=>_i32
+;; CHECK-NEXT:   (local.get $0)
+;; CHECK-NEXT:  )
+;; CHECK-NEXT: )
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 491b273..4665575 100644
--- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
+++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
@@ -1,44 +1,41 @@
 total
- [exports]      : 12      
- [funcs]        : 22      
+ [exports]      : 9       
+ [funcs]        : 18      
  [globals]      : 5       
  [imports]      : 5       
  [memories]     : 1       
  [memory-data]  : 20      
- [table-data]   : 9       
+ [table-data]   : 4       
  [tables]       : 1       
  [tags]         : 0       
- [total]        : 781     
- [vars]         : 35      
- ArrayNew       : 4       
- ArrayNewFixed  : 1       
- AtomicNotify   : 1       
- Binary         : 90      
- Block          : 118     
- Break          : 8       
- Call           : 25      
- CallIndirect   : 1       
+ [total]        : 579     
+ [vars]         : 19      
+ ArrayNew       : 5       
+ ArrayNewFixed  : 8       
+ Binary         : 71      
+ Block          : 74      
+ Break          : 2       
+ Call           : 18      
  CallRef        : 2       
- Const          : 171     
- Drop           : 8       
- GlobalGet      : 56      
- GlobalSet      : 56      
- I31New         : 3       
- If             : 36      
- Load           : 21      
- LocalGet       : 36      
- LocalSet       : 17      
- Loop           : 8       
- Nop            : 11      
- RefAs          : 1       
+ Const          : 121     
+ DataDrop       : 1       
+ Drop           : 5       
+ GlobalGet      : 34      
+ GlobalSet      : 34      
+ I31Get         : 2       
+ I31New         : 4       
+ If             : 21      
+ Load           : 19      
+ LocalGet       : 46      
+ LocalSet       : 22      
+ Loop           : 2       
+ Nop            : 9       
+ RefEq          : 1       
  RefFunc        : 16      
  RefNull        : 4       
  Return         : 6       
- SIMDExtract    : 2       
- Select         : 1       
- Store          : 6       
- StructNew      : 4       
- Switch         : 1       
- TupleMake      : 3       
- Unary          : 33      
- Unreachable    : 31      
+ Store          : 1       
+ StructNew      : 5       
+ TupleMake      : 5       
+ Unary          : 20      
+ Unreachable    : 21      
diff --git a/test/spec/bulk-array.wast b/test/spec/bulk-array.wast
new file mode 100644
index 0000000..06c3410
--- /dev/null
+++ b/test/spec/bulk-array.wast
@@ -0,0 +1,225 @@
+(module
+  ;; Array types used in tests.
+  (type $i8 (array (mut i8)))
+  (type $i16 (array (mut i16)))
+  (type $i32 (array (mut i32)))
+  (type $anyref (array (mut anyref)))
+  (type $funcref (array (mut funcref)))
+  (type $externref (array (mut externref)))
+
+  ;; Array values used in tests. Reset in between tests with the "reset"
+  ;; function.
+  (global $i8 (mut (ref null $i8)) (ref.null none))
+  (global $i16 (mut (ref null $i16)) (ref.null none))
+  (global $i32 (mut (ref null $i32)) (ref.null none))
+  (global $anyref (mut (ref null $anyref)) (ref.null none))
+  (global $funcref (mut (ref null $funcref)) (ref.null none))
+  (global $externref (mut (ref null $externref)) (ref.null none))
+
+  ;; GC objects with distinct identities used in anyref tests.
+  (global $g1 (export "g1") (mut anyref) (array.new_fixed $i8))
+  (global $g2 (export "g2") (mut anyref) (array.new_fixed $i8))
+  (global $g3 (export "g3") (mut anyref) (array.new_fixed $i8))
+  (global $g4 (export "g4") (mut anyref) (array.new_fixed $i8))
+  (global $g5 (export "g5") (mut anyref) (array.new_fixed $i8))
+
+  ;; Functions with distinct return values used in funcref tests.
+  (func $f1 (result i32) (i32.const 0))
+  (func $f2 (result i32) (i32.const 1))
+  (func $f3 (result i32) (i32.const 2))
+  (func $f4 (result i32) (i32.const 3))
+  (func $f5 (result i32) (i32.const 4))
+
+  ;; Passive element segment used in array.init_elem tests.
+  (elem $elem anyref
+    (array.new_fixed $i8)
+    (array.new_fixed $i8)
+    (array.new_fixed $i8)
+    (array.new_fixed $i8)
+    (array.new_fixed $i8))
+
+  (table $tab anyref 5 5)
+
+  ;; Resets the array globals to known states.
+  (func (export "reset")
+    (global.set $i8
+      (array.new_fixed $i8
+        (i32.const 0)
+        (i32.const 1)
+        (i32.const 2)
+        (i32.const 3)
+        (i32.const 4)))
+    (global.set $i16
+      (array.new_fixed $i16
+        (i32.const 0)
+        (i32.const 1)
+        (i32.const 2)
+        (i32.const 3)
+        (i32.const 4)))
+    (global.set $i32
+      (array.new_fixed $i32
+        (i32.const 0)
+        (i32.const 1)
+        (i32.const 2)
+        (i32.const 3)
+        (i32.const 4)))
+    (global.set $anyref
+      (array.new_fixed $anyref
+        (global.get $g1)
+        (global.get $g2)
+        (global.get $g3)
+        (global.get $g4)
+        (global.get $g5)))
+    (global.set $funcref
+      (array.new_fixed $funcref
+        (ref.func $f1)
+        (ref.func $f2)
+        (ref.func $f3)
+        (ref.func $f4)
+        (ref.func $f5)))
+    (global.set $externref
+      (array.new_fixed $externref
+        (extern.externalize (global.get $g1))
+        (extern.externalize (global.get $g2))
+        (extern.externalize (global.get $g3))
+        (extern.externalize (global.get $g4))
+        (extern.externalize (global.get $g5)))))
+)
+
+;; array.fill
+
+;; basic i8
+;; basic i16
+;; basic i32
+;; basic anyref
+;; basic funcref
+;; basic externref
+;; basic ref subtype
+;; basic ref nullability subtype
+
+;; zero size in bounds
+;; zero size at bounds
+;; zero size out of bounds traps
+
+;; out of bounds index traps
+;; out of bounds size traps
+;; out of bounds index + size traps
+
+;; null destination traps
+
+;; immutable field invalid
+
+;; ref supertype invalid
+;; ref nullability supertype invalid
+
+;; array.copy
+
+;; basic i8
+;; basic i16
+;; basic i32
+;; basic anyref
+;; basic funcref
+;; basic externref
+;; basic ref subtype
+;; basic ref nullability subtype
+
+;; same i8 no overlap
+;; same i8 overlap src first
+;; same i8 overlap dest first
+;; same i8 overlap complete
+
+;; same i32 no overlap
+;; same i32 overlap src first
+;; same i32 overlap dest first
+;; same i32 overlap complete
+
+;; same anyref no overlap
+;; same anyref overloap
+;; same anyref src first
+;; same anyref dest first
+;; same anyref overlap complete
+
+;; zero size in bounds
+;; zero size at dest bounds
+;; zero size at src bounds
+;; zero size out of dest bounds traps
+;; zero size out of src bounds traps
+
+;; out of bounds dest index traps
+;; out of bounds src index traps
+;; out of bounds dest size traps
+;; out of bounds src index traps
+;; out of bounds dest index + size traps
+;; out of bounds src index + size traps
+
+;; null dest traps
+;; null src traps
+
+;; immutable dest field invalid
+;; immutable src field ok
+
+;; ref supertype invalid
+;; ref nullability supertype invalid
+
+;; array.init_data
+
+;; basic i8
+;; basic i16
+;; basic i32
+;; basic f32
+
+;; zero size in bounds
+;; zero size at dest bounds
+;; zero size at src bounds
+;; zero size out of dest bounds traps
+;; zero size out of src bounds traps
+
+;; out of bounds dest index traps
+;; out of bounds src index traps
+;; out of bounds dest size traps
+;; out of bounds src size traps
+;; out of bounds src multiplied size traps
+;; out of bounds dest index + size traps
+;; out of bounds src index + size traps
+;; out of bounds src index + multiplied size traps
+
+;; null dest traps
+;; segment dropped traps
+
+;; immutable dest field invalid
+
+;; ref supertype invalid
+;; ref nullability supertype invalid
+
+;; out of bounds segment index invalid
+
+;; array.init_elem
+
+;; basic anyref
+;; basic funcref
+;; basic externref
+;; basic ref subtype
+;; basic ref nullability subtype
+
+;; zero size in bounds
+;; zero size at dest bounds
+;; zero size at src bounds
+;; zero size out of dest bounds traps
+;; zero size out of src bounds traps
+
+;; out of bounds dest index traps
+;; out of bounds src index traps
+;; out of bounds dest size traps
+;; out of bounds src size traps
+;; out of bounds dest index + size traps
+;; out of bounds src index + size traps
+
+;; null dest traps
+;; segment dropped traps
+
+;; immutable dest field invalid
+
+;; ref supertype invalid
+;; ref nullability supertype invalid
+
+;; out of bounds segment index invalid
diff --git a/test/spec/ref_test.wast b/test/spec/ref_test.wast
index b0bb1df..b5291e1 100644
--- a/test/spec/ref_test.wast
+++ b/test/spec/ref_test.wast
@@ -19,8 +19,8 @@
     (table.set $ta (i32.const 3) (i31.new (i32.const 7)))
     (table.set $ta (i32.const 4) (struct.new_default $st))
     (table.set $ta (i32.const 5) (array.new_default $at (i32.const 0)))
-    ;; (table.set $ta (i32.const 6) (extern.internalize (extern.externalize (i31.new (i32.const 0)))))
-    ;; (table.set $ta (i32.const 7) (extern.internalize (ref.null extern)))
+    (table.set $ta (i32.const 6) (extern.internalize (extern.externalize (i31.new (i32.const 0)))))
+    (table.set $ta (i32.const 7) (extern.internalize (ref.null extern)))
 
     (table.set $tf (i32.const 0) (ref.null nofunc))
     (table.set $tf (i32.const 1) (ref.null func))
@@ -28,10 +28,10 @@
 
     (table.set $te (i32.const 0) (ref.null noextern))
     (table.set $te (i32.const 1) (ref.null extern))
-    ;; (table.set $te (i32.const 2) (extern.externalize (i31.new (i32.const 0))))
-    ;; (table.set $te (i32.const 3) (extern.externalize (i31.new (i32.const 8))))
-    ;; (table.set $te (i32.const 4) (extern.externalize (struct.new_default $st)))
-    ;; (table.set $te (i32.const 5) (extern.externalize (ref.null any)))
+    (table.set $te (i32.const 2) (extern.externalize (i31.new (i32.const 0))))
+    (table.set $te (i32.const 3) (extern.externalize (i31.new (i32.const 8))))
+    (table.set $te (i32.const 4) (extern.externalize (struct.new_default $st)))
+    (table.set $te (i32.const 5) (extern.externalize (ref.null any)))
   )
 
   (func (export "ref_test_null_data") (param $i i32) (result i32)
@@ -106,8 +106,8 @@
 (assert_return (invoke "ref_test_null_data" (i32.const 3)) (i32.const 0))
 (assert_return (invoke "ref_test_null_data" (i32.const 4)) (i32.const 0))
 (assert_return (invoke "ref_test_null_data" (i32.const 5)) (i32.const 0))
-;; (assert_return (invoke "ref_test_null_data" (i32.const 6)) (i32.const 0))
-;; (assert_return (invoke "ref_test_null_data" (i32.const 7)) (i32.const 2))
+(assert_return (invoke "ref_test_null_data" (i32.const 6)) (i32.const 0))
+(assert_return (invoke "ref_test_null_data" (i32.const 7)) (i32.const 2))
 
 (assert_return (invoke "ref_test_any" (i32.const 0)) (i32.const 1))
 (assert_return (invoke "ref_test_any" (i32.const 1)) (i32.const 1))
@@ -115,8 +115,8 @@
 (assert_return (invoke "ref_test_any" (i32.const 3)) (i32.const 2))
 (assert_return (invoke "ref_test_any" (i32.const 4)) (i32.const 2))
 (assert_return (invoke "ref_test_any" (i32.const 5)) (i32.const 2))
-;; (assert_return (invoke "ref_test_any" (i32.const 6)) (i32.const 2))
-;; (assert_return (invoke "ref_test_any" (i32.const 7)) (i32.const 1))
+(assert_return (invoke "ref_test_any" (i32.const 6)) (i32.const 2))
+(assert_return (invoke "ref_test_any" (i32.const 7)) (i32.const 1))
 
 (assert_return (invoke "ref_test_eq" (i32.const 0)) (i32.const 1))
 (assert_return (invoke "ref_test_eq" (i32.const 1)) (i32.const 1))
@@ -124,8 +124,8 @@
 (assert_return (invoke "ref_test_eq" (i32.const 3)) (i32.const 2))
 (assert_return (invoke "ref_test_eq" (i32.const 4)) (i32.const 2))
 (assert_return (invoke "ref_test_eq" (i32.const 5)) (i32.const 2))
-;; (assert_return (invoke "ref_test_eq" (i32.const 6)) (i32.const 0))
-;; (assert_return (invoke "ref_test_eq" (i32.const 7)) (i32.const 1))
+(assert_return (invoke "ref_test_eq" (i32.const 6)) (i32.const 2))
+(assert_return (invoke "ref_test_eq" (i32.const 7)) (i32.const 1))
 
 (assert_return (invoke "ref_test_i31" (i32.const 0)) (i32.const 1))
 (assert_return (invoke "ref_test_i31" (i32.const 1)) (i32.const 1))
@@ -133,8 +133,8 @@
 (assert_return (invoke "ref_test_i31" (i32.const 3)) (i32.const 2))
 (assert_return (invoke "ref_test_i31" (i32.const 4)) (i32.const 0))
 (assert_return (invoke "ref_test_i31" (i32.const 5)) (i32.const 0))
-;; (assert_return (invoke "ref_test_i31" (i32.const 6)) (i32.const 0))
-;; (assert_return (invoke "ref_test_i31" (i32.const 7)) (i32.const 1))
+(assert_return (invoke "ref_test_i31" (i32.const 6)) (i32.const 2))
+(assert_return (invoke "ref_test_i31" (i32.const 7)) (i32.const 1))
 
 (assert_return (invoke "ref_test_struct" (i32.const 0)) (i32.const 1))
 (assert_return (invoke "ref_test_struct" (i32.const 1)) (i32.const 1))
@@ -142,8 +142,8 @@
 (assert_return (invoke "ref_test_struct" (i32.const 3)) (i32.const 0))
 (assert_return (invoke "ref_test_struct" (i32.const 4)) (i32.const 2))
 (assert_return (invoke "ref_test_struct" (i32.const 5)) (i32.const 0))
-;; (assert_return (invoke "ref_test_struct" (i32.const 6)) (i32.const 0))
-;; (assert_return (invoke "ref_test_struct" (i32.const 7)) (i32.const 1))
+(assert_return (invoke "ref_test_struct" (i32.const 6)) (i32.const 0))
+(assert_return (invoke "ref_test_struct" (i32.const 7)) (i32.const 1))
 
 (assert_return (invoke "ref_test_array" (i32.const 0)) (i32.const 1))
 (assert_return (invoke "ref_test_array" (i32.const 1)) (i32.const 1))
@@ -151,8 +151,8 @@
 (assert_return (invoke "ref_test_array" (i32.const 3)) (i32.const 0))
 (assert_return (invoke "ref_test_array" (i32.const 4)) (i32.const 0))
 (assert_return (invoke "ref_test_array" (i32.const 5)) (i32.const 2))
-;; (assert_return (invoke "ref_test_array" (i32.const 6)) (i32.const 0))
-;; (assert_return (invoke "ref_test_array" (i32.const 7)) (i32.const 1))
+(assert_return (invoke "ref_test_array" (i32.const 6)) (i32.const 0))
+(assert_return (invoke "ref_test_array" (i32.const 7)) (i32.const 1))
 
 (assert_return (invoke "ref_test_null_func" (i32.const 0)) (i32.const 2))
 (assert_return (invoke "ref_test_null_func" (i32.const 1)) (i32.const 2))
@@ -164,17 +164,17 @@
 
 (assert_return (invoke "ref_test_null_extern" (i32.const 0)) (i32.const 2))
 (assert_return (invoke "ref_test_null_extern" (i32.const 1)) (i32.const 2))
-;; (assert_return (invoke "ref_test_null_extern" (i32.const 2)) (i32.const 0))
-;; (assert_return (invoke "ref_test_null_extern" (i32.const 3)) (i32.const 0))
-;; (assert_return (invoke "ref_test_null_extern" (i32.const 4)) (i32.const 0))
-;; (assert_return (invoke "ref_test_null_extern" (i32.const 5)) (i32.const 2))
+(assert_return (invoke "ref_test_null_extern" (i32.const 2)) (i32.const 0))
+(assert_return (invoke "ref_test_null_extern" (i32.const 3)) (i32.const 0))
+(assert_return (invoke "ref_test_null_extern" (i32.const 4)) (i32.const 0))
+(assert_return (invoke "ref_test_null_extern" (i32.const 5)) (i32.const 2))
 
 (assert_return (invoke "ref_test_extern" (i32.const 0)) (i32.const 1))
 (assert_return (invoke "ref_test_extern" (i32.const 1)) (i32.const 1))
-;; (assert_return (invoke "ref_test_extern" (i32.const 2)) (i32.const 2))
-;; (assert_return (invoke "ref_test_extern" (i32.const 3)) (i32.const 2))
-;; (assert_return (invoke "ref_test_extern" (i32.const 4)) (i32.const 2))
-;; (assert_return (invoke "ref_test_extern" (i32.const 5)) (i32.const 1))
+(assert_return (invoke "ref_test_extern" (i32.const 2)) (i32.const 2))
+(assert_return (invoke "ref_test_extern" (i32.const 3)) (i32.const 2))
+(assert_return (invoke "ref_test_extern" (i32.const 4)) (i32.const 2))
+(assert_return (invoke "ref_test_extern" (i32.const 5)) (i32.const 1))
 
 
 ;; Concrete Types