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