summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/fuzz_opt.py1
-rwxr-xr-xscripts/gen-s-parser.py5
-rw-r--r--src/gen-s-parser.inc70
-rw-r--r--src/ir/ExpressionAnalyzer.cpp3
-rw-r--r--src/ir/ExpressionManipulator.cpp1
-rw-r--r--src/ir/branch-utils.h2
-rw-r--r--src/ir/cost.h7
-rw-r--r--src/ir/iteration.h1
-rw-r--r--src/ir/module-utils.h28
-rw-r--r--src/ir/properties.h1
-rw-r--r--src/literal.h10
-rw-r--r--src/passes/Heap2Local.cpp4
-rw-r--r--src/passes/Print.cpp38
-rw-r--r--src/wasm-binary.h5
-rw-r--r--src/wasm-builder.h24
-rw-r--r--src/wasm-delegations-fields.def7
-rw-r--r--src/wasm-interpreter.h95
-rw-r--r--src/wasm-s-parser.h3
-rw-r--r--src/wasm.h15
-rw-r--r--src/wasm/wasm-binary.cpp111
-rw-r--r--src/wasm/wasm-s-parser.cpp37
-rw-r--r--src/wasm/wasm-stack.cpp41
-rw-r--r--src/wasm/wasm-validator.cpp48
-rw-r--r--src/wasm/wasm.cpp25
-rw-r--r--test/heap-types.wast31
-rw-r--r--test/heap-types.wast.from-wast35
-rw-r--r--test/heap-types.wast.fromBinary33
-rw-r--r--test/heap-types.wast.fromBinary.noDebugInfo33
-rw-r--r--test/lit/binary/heap-types.wast150
-rw-r--r--test/lit/passes/heap2local.wast25
-rw-r--r--test/lit/passes/precompute-gc.wast20
-rw-r--r--test/passes/Oz_fuzz-exec_all-features.txt23
-rw-r--r--test/passes/Oz_fuzz-exec_all-features.wast19
33 files changed, 791 insertions, 160 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py
index 1fa9d2ace..b72a81c13 100755
--- a/scripts/fuzz_opt.py
+++ b/scripts/fuzz_opt.py
@@ -181,6 +181,7 @@ def get_important_initial_contents():
os.path.join('lit', 'passes', 'optimize-instructions-gc-iit.wast'),
os.path.join('lit', 'passes', 'optimize-instructions-call_ref.wast'),
os.path.join('lit', 'passes', 'inlining_splitting.wast'),
+ os.path.join('heap-types.wast'),
]
RECENT_DAYS = 30
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py
index 556f537e7..cc9ffc7c3 100755
--- a/scripts/gen-s-parser.py
+++ b/scripts/gen-s-parser.py
@@ -557,13 +557,18 @@ instructions = [
("rtt.fresh_sub", "makeRttFreshSub(s)"),
("struct.new_with_rtt", "makeStructNew(s, false)"),
("struct.new_default_with_rtt", "makeStructNew(s, true)"),
+ ("struct.new", "makeStructNewStatic(s, false)"),
+ ("struct.new_default", "makeStructNewStatic(s, true)"),
("struct.get", "makeStructGet(s)"),
("struct.get_s", "makeStructGet(s, true)"),
("struct.get_u", "makeStructGet(s, false)"),
("struct.set", "makeStructSet(s)"),
("array.new_with_rtt", "makeArrayNew(s, false)"),
("array.new_default_with_rtt", "makeArrayNew(s, true)"),
+ ("array.new", "makeArrayNewStatic(s, false)"),
+ ("array.new_default", "makeArrayNewStatic(s, true)"),
("array.init", "makeArrayInit(s)"),
+ ("array.init_static", "makeArrayInitStatic(s)"),
("array.get", "makeArrayGet(s)"),
("array.get_s", "makeArrayGet(s, true)"),
("array.get_u", "makeArrayGet(s, false)"),
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index c7c521a21..69ce0dbbc 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -33,20 +33,44 @@ switch (op[0]) {
default: goto parse_error;
}
}
- case 'i':
- if (strcmp(op, "array.init") == 0) { return makeArrayInit(s); }
- goto parse_error;
+ case 'i': {
+ switch (op[10]) {
+ case '\0':
+ if (strcmp(op, "array.init") == 0) { return makeArrayInit(s); }
+ goto parse_error;
+ case '_':
+ if (strcmp(op, "array.init_static") == 0) { return makeArrayInitStatic(s); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
case 'l':
if (strcmp(op, "array.len") == 0) { return makeArrayLen(s); }
goto parse_error;
case 'n': {
- switch (op[10]) {
- case 'd':
- if (strcmp(op, "array.new_default_with_rtt") == 0) { return makeArrayNew(s, true); }
- goto parse_error;
- case 'w':
- if (strcmp(op, "array.new_with_rtt") == 0) { return makeArrayNew(s, false); }
+ switch (op[9]) {
+ case '\0':
+ if (strcmp(op, "array.new") == 0) { return makeArrayNewStatic(s, false); }
goto parse_error;
+ case '_': {
+ switch (op[10]) {
+ case 'd': {
+ switch (op[17]) {
+ case '\0':
+ if (strcmp(op, "array.new_default") == 0) { return makeArrayNewStatic(s, true); }
+ goto parse_error;
+ case '_':
+ if (strcmp(op, "array.new_default_with_rtt") == 0) { return makeArrayNew(s, true); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
+ case 'w':
+ if (strcmp(op, "array.new_with_rtt") == 0) { return makeArrayNew(s, false); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
default: goto parse_error;
}
}
@@ -2980,13 +3004,29 @@ switch (op[0]) {
}
}
case 'n': {
- switch (op[11]) {
- case 'd':
- if (strcmp(op, "struct.new_default_with_rtt") == 0) { return makeStructNew(s, true); }
- goto parse_error;
- case 'w':
- if (strcmp(op, "struct.new_with_rtt") == 0) { return makeStructNew(s, false); }
+ switch (op[10]) {
+ case '\0':
+ if (strcmp(op, "struct.new") == 0) { return makeStructNewStatic(s, false); }
goto parse_error;
+ case '_': {
+ switch (op[11]) {
+ case 'd': {
+ switch (op[18]) {
+ case '\0':
+ if (strcmp(op, "struct.new_default") == 0) { return makeStructNewStatic(s, true); }
+ goto parse_error;
+ case '_':
+ if (strcmp(op, "struct.new_default_with_rtt") == 0) { return makeStructNew(s, true); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
+ case 'w':
+ if (strcmp(op, "struct.new_with_rtt") == 0) { return makeStructNew(s, false); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
default: goto parse_error;
}
}
diff --git a/src/ir/ExpressionAnalyzer.cpp b/src/ir/ExpressionAnalyzer.cpp
index e98b14777..db972e707 100644
--- a/src/ir/ExpressionAnalyzer.cpp
+++ b/src/ir/ExpressionAnalyzer.cpp
@@ -194,6 +194,7 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left,
#define DELEGATE_FIELD_NAME(id, name) COMPARE_FIELD(name)
#define DELEGATE_FIELD_SIGNATURE(id, name) COMPARE_FIELD(name)
#define DELEGATE_FIELD_TYPE(id, name) COMPARE_FIELD(name)
+#define DELEGATE_FIELD_HEAPTYPE(id, name) COMPARE_FIELD(name)
#define DELEGATE_FIELD_ADDRESS(id, name) COMPARE_FIELD(name)
#define COMPARE_LIST(name) \
@@ -317,6 +318,7 @@ struct Hasher {
#define DELEGATE_FIELD_NAME(id, name) visitNonScopeName(cast->name)
#define DELEGATE_FIELD_TYPE(id, name) visitType(cast->name);
+#define DELEGATE_FIELD_HEAPTYPE(id, name) visitHeapType(cast->name);
#define DELEGATE_FIELD_ADDRESS(id, name) visitAddress(cast->name);
// Note that we only note the scope name, but do not also visit it. That means
@@ -359,6 +361,7 @@ struct Hasher {
}
void visitNonScopeName(Name curr) { rehash(digest, uint64_t(curr.str)); }
void visitType(Type curr) { rehash(digest, curr.getID()); }
+ void visitHeapType(HeapType curr) { rehash(digest, curr.getID()); }
void visitAddress(Address curr) { rehash(digest, curr.addr); }
};
diff --git a/src/ir/ExpressionManipulator.cpp b/src/ir/ExpressionManipulator.cpp
index 0bb94a66d..8ae3ddc1c 100644
--- a/src/ir/ExpressionManipulator.cpp
+++ b/src/ir/ExpressionManipulator.cpp
@@ -81,6 +81,7 @@ flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) {
#define DELEGATE_FIELD_SCOPE_NAME_USE(id, name) COPY_FIELD(name)
#define DELEGATE_FIELD_SIGNATURE(id, name) COPY_FIELD(name)
#define DELEGATE_FIELD_TYPE(id, name) COPY_FIELD(name)
+#define DELEGATE_FIELD_HEAPTYPE(id, name) COPY_FIELD(name)
#define DELEGATE_FIELD_ADDRESS(id, name) COPY_FIELD(name)
#define COPY_FIELD_LIST(name) \
diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h
index 54a3f1c16..f2fa43f18 100644
--- a/src/ir/branch-utils.h
+++ b/src/ir/branch-utils.h
@@ -62,6 +62,7 @@ template<typename T> void operateOnScopeNameUses(Expression* expr, T func) {
#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, name)
#define DELEGATE_FIELD_SIGNATURE(id, name)
#define DELEGATE_FIELD_TYPE(id, name)
+#define DELEGATE_FIELD_HEAPTYPE(id, name)
#define DELEGATE_FIELD_ADDRESS(id, name)
#define DELEGATE_FIELD_CHILD_VECTOR(id, name)
#define DELEGATE_FIELD_INT_ARRAY(id, name)
@@ -125,6 +126,7 @@ template<typename T> void operateOnScopeNameDefs(Expression* expr, T func) {
#define DELEGATE_FIELD_NAME_VECTOR(id, name)
#define DELEGATE_FIELD_SIGNATURE(id, name)
#define DELEGATE_FIELD_TYPE(id, name)
+#define DELEGATE_FIELD_HEAPTYPE(id, name)
#define DELEGATE_FIELD_ADDRESS(id, name)
#define DELEGATE_FIELD_CHILD_VECTOR(id, name)
#define DELEGATE_FIELD_INT_ARRAY(id, name)
diff --git a/src/ir/cost.h b/src/ir/cost.h
index 086b7b9af..29fcb8dd4 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -593,7 +593,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
// at least some baseline cost, plus writing the fields. (If we use default
// values for the fields, then it is possible they are all 0 and if so, we
// can get that almost for free as well, so don't add anything there.)
- CostType ret = 4 + visit(curr->rtt) + curr->operands.size();
+ CostType ret = 4 + maybeVisit(curr->rtt) + curr->operands.size();
for (auto* child : curr->operands) {
ret += visit(child);
}
@@ -606,10 +606,11 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
return 2 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->value);
}
CostType visitArrayNew(ArrayNew* curr) {
- return 4 + visit(curr->rtt) + visit(curr->size) + maybeVisit(curr->init);
+ return 4 + maybeVisit(curr->rtt) + visit(curr->size) +
+ maybeVisit(curr->init);
}
CostType visitArrayInit(ArrayInit* curr) {
- CostType ret = 4 + visit(curr->rtt);
+ CostType ret = 4 + maybeVisit(curr->rtt);
for (auto* child : curr->values) {
ret += visit(child);
}
diff --git a/src/ir/iteration.h b/src/ir/iteration.h
index 3de6d1435..19bc44af1 100644
--- a/src/ir/iteration.h
+++ b/src/ir/iteration.h
@@ -98,6 +98,7 @@ public:
#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, name)
#define DELEGATE_FIELD_SIGNATURE(id, name)
#define DELEGATE_FIELD_TYPE(id, name)
+#define DELEGATE_FIELD_HEAPTYPE(id, name)
#define DELEGATE_FIELD_ADDRESS(id, name)
#include "wasm-delegations-fields.def"
diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h
index d191a35f1..5d23278fa 100644
--- a/src/ir/module-utils.h
+++ b/src/ir/module-utils.h
@@ -497,6 +497,34 @@ inline void collectHeapTypes(Module& wasm,
counts.note(curr->type);
} else if (curr->is<RttCanon>() || curr->is<RttSub>()) {
counts.note(curr->type.getRtt().heapType);
+ } else if (auto* make = curr->dynCast<StructNew>()) {
+ // Some operations emit a HeapType in the binary format, if they are
+ // static and not dynamic (if dynamic, the RTT provides the heap type).
+ if (!make->rtt && make->type != Type::unreachable) {
+ counts.note(make->type.getHeapType());
+ }
+ } else if (auto* make = curr->dynCast<ArrayNew>()) {
+ if (!make->rtt && make->type != Type::unreachable) {
+ counts.note(make->type.getHeapType());
+ }
+ } else if (auto* make = curr->dynCast<ArrayInit>()) {
+ if (!make->rtt && make->type != Type::unreachable) {
+ counts.note(make->type.getHeapType());
+ }
+ } else if (auto* cast = curr->dynCast<RefCast>()) {
+ if (!cast->rtt && cast->type != Type::unreachable) {
+ counts.note(cast->getIntendedType());
+ }
+ } else if (auto* cast = curr->dynCast<RefTest>()) {
+ if (!cast->rtt && cast->type != Type::unreachable) {
+ counts.note(cast->getIntendedType());
+ }
+ } else if (auto* cast = curr->dynCast<BrOn>()) {
+ if (cast->op == BrOnCast || cast->op == BrOnCastFail) {
+ if (!cast->rtt && cast->type != Type::unreachable) {
+ counts.note(cast->getIntendedType());
+ }
+ }
} else if (auto* get = curr->dynCast<StructGet>()) {
counts.note(get->ref->type);
} else if (auto* set = curr->dynCast<StructSet>()) {
diff --git a/src/ir/properties.h b/src/ir/properties.h
index f3cab1fdc..841d154f7 100644
--- a/src/ir/properties.h
+++ b/src/ir/properties.h
@@ -340,6 +340,7 @@ inline Index getNumChildren(Expression* curr) {
#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, name)
#define DELEGATE_FIELD_SIGNATURE(id, name)
#define DELEGATE_FIELD_TYPE(id, name)
+#define DELEGATE_FIELD_HEAPTYPE(id, name)
#define DELEGATE_FIELD_ADDRESS(id, name)
#include "wasm-delegations-fields.def"
diff --git a/src/literal.h b/src/literal.h
index f7ce78f7a..6f2267752 100644
--- a/src/literal.h
+++ b/src/literal.h
@@ -61,6 +61,8 @@ class Literal {
// as the Literal class itself.
// To support the experimental RttFreshSub instruction, we not only store
// the type, but also a reference to an allocation.
+ // The above describes dynamic data, that is with an actual RTT. The static
+ // case just has a static type in its GCData.
// See struct RttSuper below for more details.
std::unique_ptr<RttSupers> rttSupers;
// TODO: Literals of type `externref` can only be `null` currently but we
@@ -694,10 +696,16 @@ std::ostream& operator<<(std::ostream& o, wasm::Literal literal);
std::ostream& operator<<(std::ostream& o, wasm::Literals literals);
// A GC Struct or Array is a set of values with a run-time type saying what it
-// is.
+// is. In the case of static (rtt-free) typing, the rtt is not present and
+// instead we have a static type.
struct GCData {
+ // Either the RTT or the type must be present, but not both.
Literal rtt;
+ HeapType type;
+
Literals values;
+
+ GCData(HeapType type, Literals values) : type(type), values(values) {}
GCData(Literal rtt, Literals values) : rtt(rtt), values(values) {}
};
diff --git a/src/passes/Heap2Local.cpp b/src/passes/Heap2Local.cpp
index f2846671c..796e0fa05 100644
--- a/src/passes/Heap2Local.cpp
+++ b/src/passes/Heap2Local.cpp
@@ -402,7 +402,9 @@ struct Heap2LocalOptimizer {
}
// Drop the RTT (as it may have side effects; leave it to other passes).
- contents.push_back(builder.makeDrop(allocation->rtt));
+ if (allocation->rtt) {
+ contents.push_back(builder.makeDrop(allocation->rtt));
+ }
// Replace the allocation with a null reference. This changes the type
// from non-nullable to nullable, but as we optimize away the code that
// the allocation reaches, we will handle that.
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 6198218d8..6a41961a2 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -1985,15 +1985,18 @@ struct PrintExpressionContents
}
void visitStructNew(StructNew* curr) {
- if (printUnreachableReplacement(curr->rtt)) {
+ if (printUnreachableReplacement(curr)) {
return;
}
- printMedium(o, "struct.new_");
+ printMedium(o, "struct.new");
if (curr->isWithDefault()) {
- o << "default_";
+ printMedium(o, "_default");
}
- o << "with_rtt ";
- TypeNamePrinter(o, wasm).print(curr->rtt->type.getHeapType());
+ if (curr->rtt) {
+ printMedium(o, "_with_rtt");
+ }
+ o << ' ';
+ TypeNamePrinter(o, wasm).print(curr->type.getHeapType());
}
void printFieldName(HeapType type, Index index) {
@@ -2035,16 +2038,29 @@ struct PrintExpressionContents
printFieldName(heapType, curr->index);
}
void visitArrayNew(ArrayNew* curr) {
- printMedium(o, "array.new_");
+ if (printUnreachableReplacement(curr)) {
+ return;
+ }
+ printMedium(o, "array.new");
if (curr->isWithDefault()) {
- o << "default_";
+ printMedium(o, "_default");
+ }
+ if (curr->rtt) {
+ printMedium(o, "_with_rtt");
}
- o << "with_rtt ";
- TypeNamePrinter(o, wasm).print(curr->rtt->type.getHeapType());
+ o << ' ';
+ TypeNamePrinter(o, wasm).print(curr->type.getHeapType());
}
void visitArrayInit(ArrayInit* curr) {
- printMedium(o, "array.init ");
- TypeNamePrinter(o, wasm).print(curr->rtt->type.getHeapType());
+ if (printUnreachableReplacement(curr)) {
+ return;
+ }
+ printMedium(o, "array.init");
+ if (!curr->rtt) {
+ printMedium(o, "_static");
+ }
+ o << ' ';
+ TypeNamePrinter(o, wasm).print(curr->type.getHeapType());
}
void visitArrayGet(ArrayGet* curr) {
if (printUnreachableReplacement(curr->ref)) {
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 0e4b4b873..057a2a981 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1052,6 +1052,8 @@ enum ASTNodes {
StructGetS = 0x04,
StructGetU = 0x05,
StructSet = 0x06,
+ StructNew = 0x07,
+ StructNewDefault = 0x08,
ArrayNewWithRtt = 0x11,
ArrayNewDefaultWithRtt = 0x12,
ArrayGet = 0x13,
@@ -1061,6 +1063,9 @@ enum ASTNodes {
ArrayLen = 0x17,
ArrayCopy = 0x18,
ArrayInit = 0x19,
+ ArrayInitStatic = 0x1a,
+ ArrayNew = 0x1b,
+ ArrayNewDefault = 0x1c,
I31New = 0x20,
I31GetS = 0x21,
I31GetU = 0x22,
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 346f2f5ba..f992b1e03 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -836,6 +836,13 @@ public:
ret->finalize();
return ret;
}
+ template<typename T> StructNew* makeStructNew(HeapType type, const T& args) {
+ auto* ret = wasm.allocator.alloc<StructNew>();
+ ret->operands.set(args);
+ ret->type = Type(type, NonNullable);
+ ret->finalize();
+ return ret;
+ }
StructGet*
makeStructGet(Index index, Expression* ref, Type type, bool signed_ = false) {
auto* ret = wasm.allocator.alloc<StructGet>();
@@ -863,6 +870,15 @@ public:
ret->finalize();
return ret;
}
+ ArrayNew*
+ makeArrayNew(HeapType type, Expression* size, Expression* init = nullptr) {
+ auto* ret = wasm.allocator.alloc<ArrayNew>();
+ ret->size = size;
+ ret->init = init;
+ ret->type = Type(type, NonNullable);
+ ret->finalize();
+ return ret;
+ }
ArrayInit* makeArrayInit(Expression* rtt,
const std::vector<Expression*>& values) {
auto* ret = wasm.allocator.alloc<ArrayInit>();
@@ -871,6 +887,14 @@ public:
ret->finalize();
return ret;
}
+ ArrayInit* makeArrayInit(HeapType type,
+ const std::vector<Expression*>& values) {
+ auto* ret = wasm.allocator.alloc<ArrayInit>();
+ ret->values.set(values);
+ ret->type = Type(type, NonNullable);
+ ret->finalize();
+ return ret;
+ }
ArrayGet*
makeArrayGet(Expression* ref, Expression* index, bool signed_ = false) {
auto* ret = wasm.allocator.alloc<ArrayGet>();
diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def
index 459f80afb..f5524d181 100644
--- a/src/wasm-delegations-fields.def
+++ b/src/wasm-delegations-fields.def
@@ -614,7 +614,7 @@ switch (DELEGATE_ID) {
}
case Expression::Id::StructNewId: {
DELEGATE_START(StructNew);
- DELEGATE_FIELD_CHILD(StructNew, rtt);
+ DELEGATE_FIELD_OPTIONAL_CHILD(StructNew, rtt);
DELEGATE_FIELD_CHILD_VECTOR(StructNew, operands);
DELEGATE_END(StructNew);
break;
@@ -637,7 +637,7 @@ switch (DELEGATE_ID) {
}
case Expression::Id::ArrayNewId: {
DELEGATE_START(ArrayNew);
- DELEGATE_FIELD_CHILD(ArrayNew, rtt);
+ DELEGATE_FIELD_OPTIONAL_CHILD(ArrayNew, rtt);
DELEGATE_FIELD_CHILD(ArrayNew, size);
DELEGATE_FIELD_OPTIONAL_CHILD(ArrayNew, init);
DELEGATE_END(ArrayNew);
@@ -645,7 +645,7 @@ switch (DELEGATE_ID) {
}
case Expression::Id::ArrayInitId: {
DELEGATE_START(ArrayInit);
- DELEGATE_FIELD_CHILD(ArrayInit, rtt);
+ DELEGATE_FIELD_OPTIONAL_CHILD(ArrayInit, rtt);
DELEGATE_FIELD_CHILD_VECTOR(ArrayInit, values);
DELEGATE_END(ArrayInit);
break;
@@ -707,5 +707,6 @@ switch (DELEGATE_ID) {
#undef DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR
#undef DELEGATE_FIELD_SIGNATURE
#undef DELEGATE_FIELD_TYPE
+#undef DELEGATE_FIELD_HEAPTYPE
#undef DELEGATE_FIELD_ADDRESS
#undef DELEGATE_GET_FIELD
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 53d3048b7..4e2d40e08 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1483,8 +1483,8 @@ public:
// GC data store an RTT in each instance.
assert(cast.originalRef.isData());
auto gcData = cast.originalRef.getGCData();
- Literal seenRtt = gcData->rtt;
if (curr->rtt) {
+ Literal seenRtt = gcData->rtt;
if (!seenRtt.isSubRtt(intendedRtt)) {
cast.outcome = cast.Failure;
return cast;
@@ -1492,7 +1492,7 @@ public:
cast.castRef =
Literal(gcData, Type(intendedRtt.type.getHeapType(), NonNullable));
} else {
- auto seenType = seenRtt.type.getHeapType();
+ auto seenType = gcData->type;
if (!HeapType::isSubType(seenType, curr->intendedType)) {
cast.outcome = cast.Failure;
return cast;
@@ -1628,13 +1628,42 @@ public:
}
return Literal(std::move(newSupers), curr->type);
}
+
+ // Generates GC data for either dynamic (with an RTT) or static (with a type)
+ // typing. Dynamic typing will provide an rtt expression and an rtt flow with
+ // the value, while static typing only provides a heap type directly.
+ template<typename T>
+ std::shared_ptr<GCData>
+ makeGCData(Expression* rttExpr, Flow& rttFlow, HeapType type, T& data) {
+ if (rttExpr) {
+ return std::make_shared<GCData>(rttFlow.getSingleValue(), data);
+ } else {
+ return std::make_shared<GCData>(type, data);
+ }
+ }
+
Flow visitStructNew(StructNew* curr) {
NOTE_ENTER("StructNew");
- auto rtt = this->visit(curr->rtt);
- if (rtt.breaking()) {
- return rtt;
+ Flow rtt;
+ if (curr->rtt) {
+ rtt = this->visit(curr->rtt);
+ if (rtt.breaking()) {
+ return rtt;
+ }
+ }
+ if (curr->type == Type::unreachable) {
+ // We cannot proceed to compute the heap type, as there isn't one. Just
+ // find why we are unreachable, and stop there.
+ for (auto* operand : curr->operands) {
+ auto value = this->visit(operand);
+ if (value.breaking()) {
+ return value;
+ }
+ }
+ WASM_UNREACHABLE("unreachable but no unreachable child");
}
- const auto& fields = curr->rtt->type.getHeapType().getStruct().fields;
+ auto heapType = curr->type.getHeapType();
+ const auto& fields = heapType.getStruct().fields;
Literals data(fields.size());
for (Index i = 0; i < fields.size(); i++) {
if (curr->isWithDefault()) {
@@ -1647,8 +1676,8 @@ public:
data[i] = value.getSingleValue();
}
}
- return Flow(Literal(std::make_shared<GCData>(rtt.getSingleValue(), data),
- curr->type));
+ return Flow(
+ Literal(makeGCData(curr->rtt, rtt, heapType, data), curr->type));
}
Flow visitStructGet(StructGet* curr) {
NOTE_ENTER("StructGet");
@@ -1691,15 +1720,26 @@ public:
Flow visitArrayNew(ArrayNew* curr) {
NOTE_ENTER("ArrayNew");
- auto rtt = this->visit(curr->rtt);
- if (rtt.breaking()) {
- return rtt;
+ Flow rtt;
+ if (curr->rtt) {
+ rtt = this->visit(curr->rtt);
+ if (rtt.breaking()) {
+ return rtt;
+ }
}
auto size = this->visit(curr->size);
if (size.breaking()) {
return size;
}
- const auto& element = curr->rtt->type.getHeapType().getArray().element;
+ if (curr->type == Type::unreachable) {
+ // We cannot proceed to compute the heap type, as there isn't one. Just
+ // visit the unreachable child, and stop there.
+ auto init = this->visit(curr->init);
+ assert(init.breaking());
+ return init;
+ }
+ auto heapType = curr->type.getHeapType();
+ const auto& element = heapType.getArray().element;
Index num = size.getSingleValue().geti32();
if (num >= ArrayLimit) {
hostLimit("allocation failure");
@@ -1720,20 +1760,35 @@ public:
data[i] = value;
}
}
- return Flow(Literal(std::make_shared<GCData>(rtt.getSingleValue(), data),
- curr->type));
+ return Flow(
+ Literal(makeGCData(curr->rtt, rtt, heapType, data), curr->type));
}
Flow visitArrayInit(ArrayInit* curr) {
NOTE_ENTER("ArrayInit");
- auto rtt = this->visit(curr->rtt);
- if (rtt.breaking()) {
- return rtt;
+ Flow rtt;
+ if (curr->rtt) {
+ rtt = this->visit(curr->rtt);
+ if (rtt.breaking()) {
+ return rtt;
+ }
}
Index num = curr->values.size();
if (num >= ArrayLimit) {
hostLimit("allocation failure");
}
- auto field = curr->type.getHeapType().getArray().element;
+ if (curr->type == Type::unreachable) {
+ // We cannot proceed to compute the heap type, as there isn't one. Just
+ // find why we are unreachable, and stop there.
+ for (auto* value : curr->values) {
+ auto result = this->visit(value);
+ if (result.breaking()) {
+ return result;
+ }
+ }
+ WASM_UNREACHABLE("unreachable but no unreachable child");
+ }
+ auto heapType = curr->type.getHeapType();
+ auto field = heapType.getArray().element;
Literals data(num);
for (Index i = 0; i < num; i++) {
auto value = this->visit(curr->values[i]);
@@ -1742,8 +1797,8 @@ public:
}
data[i] = truncateForPacking(value.getSingleValue(), field);
}
- return Flow(Literal(std::make_shared<GCData>(rtt.getSingleValue(), data),
- curr->type));
+ return Flow(
+ Literal(makeGCData(curr->rtt, rtt, heapType, data), curr->type));
}
Flow visitArrayGet(ArrayGet* curr) {
NOTE_ENTER("ArrayGet");
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index f195314b1..ef677cbf7 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -284,11 +284,14 @@ private:
Expression* makeRttSub(Element& s);
Expression* makeRttFreshSub(Element& s);
Expression* makeStructNew(Element& s, bool default_);
+ Expression* makeStructNewStatic(Element& s, bool default_);
Index getStructIndex(Element& type, Element& field);
Expression* makeStructGet(Element& s, bool signed_ = false);
Expression* makeStructSet(Element& s);
Expression* makeArrayNew(Element& s, bool default_);
+ Expression* makeArrayNewStatic(Element& s, bool default_);
Expression* makeArrayInit(Element& s);
+ Expression* makeArrayInitStatic(Element& s);
Expression* makeArrayGet(Element& s, bool signed_ = false);
Expression* makeArraySet(Element& s);
Expression* makeArrayLen(Element& s);
diff --git a/src/wasm.h b/src/wasm.h
index ffc97f71e..9c6062e48 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1438,7 +1438,10 @@ class StructNew : public SpecificExpression<Expression::StructNewId> {
public:
StructNew(MixedArena& allocator) : operands(allocator) {}
- Expression* rtt;
+ // A dynamic StructNew has an rtt, while a static one declares the type using
+ // the type field.
+ Expression* rtt = nullptr;
+
// A struct.new_with_default has empty operands. This does leave the case of a
// struct with no fields ambiguous, but it doesn't make a difference in that
// case, and binaryen doesn't guarantee roundtripping binaries anyhow.
@@ -1481,7 +1484,10 @@ public:
// used.
Expression* init = nullptr;
Expression* size;
- Expression* rtt;
+
+ // A dynamic ArrayNew has an rtt, while a static one declares the type using
+ // the type field.
+ Expression* rtt = nullptr;
bool isWithDefault() { return !init; }
@@ -1493,7 +1499,10 @@ public:
ArrayInit(MixedArena& allocator) : values(allocator) {}
ExpressionList values;
- Expression* rtt;
+
+ // A dynamic ArrayInit has an rtt, while a static one declares the type using
+ // the type field.
+ Expression* rtt = nullptr;
void finalize();
};
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index e1ff43988..74895c868 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -6527,23 +6527,36 @@ bool WasmBinaryBuilder::maybeVisitRttSub(Expression*& out, uint32_t code) {
}
bool WasmBinaryBuilder::maybeVisitStructNew(Expression*& out, uint32_t code) {
- if (code != BinaryConsts::StructNewWithRtt &&
- code != BinaryConsts::StructNewDefaultWithRtt) {
- return false;
- }
- auto heapType = getIndexedHeapType();
- auto* rtt = popNonVoidExpression();
- validateHeapTypeUsingChild(rtt, heapType);
- std::vector<Expression*> operands;
- if (code == BinaryConsts::StructNewWithRtt) {
- auto numOperands = heapType.getStruct().fields.size();
- operands.resize(numOperands);
- for (Index i = 0; i < numOperands; i++) {
- operands[numOperands - i - 1] = popNonVoidExpression();
+ if (code == BinaryConsts::StructNew ||
+ code == BinaryConsts::StructNewDefault) {
+ auto heapType = getIndexedHeapType();
+ std::vector<Expression*> operands;
+ if (code == BinaryConsts::StructNew) {
+ auto numOperands = heapType.getStruct().fields.size();
+ operands.resize(numOperands);
+ for (Index i = 0; i < numOperands; i++) {
+ operands[numOperands - i - 1] = popNonVoidExpression();
+ }
+ }
+ out = Builder(wasm).makeStructNew(heapType, operands);
+ return true;
+ } else if (code == BinaryConsts::StructNewWithRtt ||
+ code == BinaryConsts::StructNewDefaultWithRtt) {
+ auto heapType = getIndexedHeapType();
+ auto* rtt = popNonVoidExpression();
+ validateHeapTypeUsingChild(rtt, heapType);
+ std::vector<Expression*> operands;
+ if (code == BinaryConsts::StructNewWithRtt) {
+ auto numOperands = heapType.getStruct().fields.size();
+ operands.resize(numOperands);
+ for (Index i = 0; i < numOperands; i++) {
+ operands[numOperands - i - 1] = popNonVoidExpression();
+ }
}
+ out = Builder(wasm).makeStructNew(rtt, operands);
+ return true;
}
- out = Builder(wasm).makeStructNew(rtt, operands);
- return true;
+ return false;
}
bool WasmBinaryBuilder::maybeVisitStructGet(Expression*& out, uint32_t code) {
@@ -6588,36 +6601,54 @@ bool WasmBinaryBuilder::maybeVisitStructSet(Expression*& out, uint32_t code) {
}
bool WasmBinaryBuilder::maybeVisitArrayNew(Expression*& out, uint32_t code) {
- if (code != BinaryConsts::ArrayNewWithRtt &&
- code != BinaryConsts::ArrayNewDefaultWithRtt) {
- return false;
+ if (code == BinaryConsts::ArrayNew || code == BinaryConsts::ArrayNewDefault) {
+ auto heapType = getIndexedHeapType();
+ auto* size = popNonVoidExpression();
+ Expression* init = nullptr;
+ if (code == BinaryConsts::ArrayNew) {
+ init = popNonVoidExpression();
+ }
+ out = Builder(wasm).makeArrayNew(heapType, size, init);
+ return true;
+ } else if (code == BinaryConsts::ArrayNewWithRtt ||
+ code == BinaryConsts::ArrayNewDefaultWithRtt) {
+ auto heapType = getIndexedHeapType();
+ auto* rtt = popNonVoidExpression();
+ validateHeapTypeUsingChild(rtt, heapType);
+ auto* size = popNonVoidExpression();
+ Expression* init = nullptr;
+ if (code == BinaryConsts::ArrayNewWithRtt) {
+ init = popNonVoidExpression();
+ }
+ out = Builder(wasm).makeArrayNew(rtt, size, init);
+ return true;
}
- auto heapType = getIndexedHeapType();
- auto* rtt = popNonVoidExpression();
- validateHeapTypeUsingChild(rtt, heapType);
- auto* size = popNonVoidExpression();
- Expression* init = nullptr;
- if (code == BinaryConsts::ArrayNewWithRtt) {
- init = popNonVoidExpression();
- }
- out = Builder(wasm).makeArrayNew(rtt, size, init);
- return true;
+ return false;
}
bool WasmBinaryBuilder::maybeVisitArrayInit(Expression*& out, uint32_t code) {
- if (code != BinaryConsts::ArrayInit) {
- return false;
- }
- auto heapType = getIndexedHeapType();
- auto size = getU32LEB();
- auto* rtt = popNonVoidExpression();
- validateHeapTypeUsingChild(rtt, heapType);
- std::vector<Expression*> values(size);
- for (size_t i = 0; i < size; i++) {
- values[size - i - 1] = popNonVoidExpression();
+ if (code == BinaryConsts::ArrayInitStatic) {
+ auto heapType = getIndexedHeapType();
+ auto size = getU32LEB();
+ std::vector<Expression*> values(size);
+ for (size_t i = 0; i < size; i++) {
+ values[size - i - 1] = popNonVoidExpression();
+ }
+ out = Builder(wasm).makeArrayInit(heapType, values);
+ return true;
+ } else if (code == BinaryConsts::ArrayInit) {
+ auto heapType = getIndexedHeapType();
+ auto size = getU32LEB();
+ auto* rtt = popNonVoidExpression();
+ validateHeapTypeUsingChild(rtt, heapType);
+ std::vector<Expression*> values(size);
+ for (size_t i = 0; i < size; i++) {
+ values[size - i - 1] = popNonVoidExpression();
+ }
+ out = Builder(wasm).makeArrayInit(rtt, values);
+ return true;
}
- out = Builder(wasm).makeArrayInit(rtt, values);
- return true;
+ return false;
}
bool WasmBinaryBuilder::maybeVisitArrayGet(Expression*& out, uint32_t code) {
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 697e4e58d..e9ea6aaa5 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2652,6 +2652,21 @@ Expression* SExpressionWasmBuilder::makeStructNew(Element& s, bool default_) {
return Builder(wasm).makeStructNew(rtt, operands);
}
+Expression* SExpressionWasmBuilder::makeStructNewStatic(Element& s,
+ bool default_) {
+ auto heapType = parseHeapType(*s[1]);
+ auto numOperands = s.size() - 2;
+ if (default_ && numOperands > 0) {
+ throw ParseException("arguments provided for struct.new", s.line, s.col);
+ }
+ std::vector<Expression*> operands;
+ operands.resize(numOperands);
+ for (Index i = 0; i < numOperands; i++) {
+ operands[i] = parseExpression(*s[i + 2]);
+ }
+ return Builder(wasm).makeStructNew(heapType, operands);
+}
+
Index SExpressionWasmBuilder::getStructIndex(Element& type, Element& field) {
if (field.dollared()) {
auto name = field.str();
@@ -2708,6 +2723,18 @@ Expression* SExpressionWasmBuilder::makeArrayNew(Element& s, bool default_) {
return Builder(wasm).makeArrayNew(rtt, size, init);
}
+Expression* SExpressionWasmBuilder::makeArrayNewStatic(Element& s,
+ bool default_) {
+ auto heapType = parseHeapType(*s[1]);
+ Expression* init = nullptr;
+ size_t i = 2;
+ if (!default_) {
+ init = parseExpression(*s[i++]);
+ }
+ auto* size = parseExpression(*s[i++]);
+ return Builder(wasm).makeArrayNew(heapType, size, init);
+}
+
Expression* SExpressionWasmBuilder::makeArrayInit(Element& s) {
auto heapType = parseHeapType(*s[1]);
size_t i = 2;
@@ -2720,6 +2747,16 @@ Expression* SExpressionWasmBuilder::makeArrayInit(Element& s) {
return Builder(wasm).makeArrayInit(rtt, values);
}
+Expression* SExpressionWasmBuilder::makeArrayInitStatic(Element& s) {
+ auto heapType = parseHeapType(*s[1]);
+ size_t i = 2;
+ std::vector<Expression*> values;
+ while (i < s.size()) {
+ values.push_back(parseExpression(*s[i++]));
+ }
+ return Builder(wasm).makeArrayInit(heapType, values);
+}
+
Expression* SExpressionWasmBuilder::makeArrayGet(Element& s, bool signed_) {
auto heapType = parseHeapType(*s[1]);
auto ref = parseExpression(*s[2]);
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index b6424cdde..70b52cfbb 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -2025,12 +2025,20 @@ void BinaryInstWriter::visitRttSub(RttSub* curr) {
void BinaryInstWriter::visitStructNew(StructNew* curr) {
o << int8_t(BinaryConsts::GCPrefix);
- if (curr->isWithDefault()) {
- o << U32LEB(BinaryConsts::StructNewDefaultWithRtt);
+ if (curr->rtt) {
+ if (curr->isWithDefault()) {
+ o << U32LEB(BinaryConsts::StructNewDefaultWithRtt);
+ } else {
+ o << U32LEB(BinaryConsts::StructNewWithRtt);
+ }
} else {
- o << U32LEB(BinaryConsts::StructNewWithRtt);
+ if (curr->isWithDefault()) {
+ o << U32LEB(BinaryConsts::StructNewDefault);
+ } else {
+ o << U32LEB(BinaryConsts::StructNew);
+ }
}
- parent.writeIndexedHeapType(curr->rtt->type.getHeapType());
+ parent.writeIndexedHeapType(curr->type.getHeapType());
}
void BinaryInstWriter::visitStructGet(StructGet* curr) {
@@ -2057,17 +2065,30 @@ void BinaryInstWriter::visitStructSet(StructSet* curr) {
void BinaryInstWriter::visitArrayNew(ArrayNew* curr) {
o << int8_t(BinaryConsts::GCPrefix);
- if (curr->isWithDefault()) {
- o << U32LEB(BinaryConsts::ArrayNewDefaultWithRtt);
+ if (curr->rtt) {
+ if (curr->isWithDefault()) {
+ o << U32LEB(BinaryConsts::ArrayNewDefaultWithRtt);
+ } else {
+ o << U32LEB(BinaryConsts::ArrayNewWithRtt);
+ }
} else {
- o << U32LEB(BinaryConsts::ArrayNewWithRtt);
+ if (curr->isWithDefault()) {
+ o << U32LEB(BinaryConsts::ArrayNewDefault);
+ } else {
+ o << U32LEB(BinaryConsts::ArrayNew);
+ }
}
- parent.writeIndexedHeapType(curr->rtt->type.getHeapType());
+ parent.writeIndexedHeapType(curr->type.getHeapType());
}
void BinaryInstWriter::visitArrayInit(ArrayInit* curr) {
- o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::ArrayInit);
- parent.writeIndexedHeapType(curr->rtt->type.getHeapType());
+ o << int8_t(BinaryConsts::GCPrefix);
+ if (curr->rtt) {
+ o << U32LEB(BinaryConsts::ArrayInit);
+ } else {
+ o << U32LEB(BinaryConsts::ArrayInitStatic);
+ }
+ parent.writeIndexedHeapType(curr->type.getHeapType());
o << U32LEB(curr->values.size());
}
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 1f6421609..d03fc2446 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -2332,11 +2332,19 @@ void FunctionValidator::visitStructNew(StructNew* curr) {
if (curr->type == Type::unreachable) {
return;
}
- if (!shouldBeTrue(
- curr->rtt->type.isRtt(), curr, "struct.new rtt must be rtt")) {
- return;
+ if (curr->rtt) {
+ if (!shouldBeTrue(
+ curr->rtt->type.isRtt(), curr, "struct.new rtt must be rtt")) {
+ return;
+ }
+ }
+ auto heapType = curr->type.getHeapType();
+ if (curr->rtt) {
+ shouldBeEqual(curr->rtt->type.getHeapType(),
+ heapType,
+ curr,
+ "struct.new heap type must match rtt");
}
- auto heapType = curr->rtt->type.getHeapType();
if (!shouldBeTrue(
heapType.isStruct(), curr, "struct.new heap type must be struct")) {
return;
@@ -2428,11 +2436,19 @@ void FunctionValidator::visitArrayNew(ArrayNew* curr) {
if (curr->type == Type::unreachable) {
return;
}
- if (!shouldBeTrue(
- curr->rtt->type.isRtt(), curr, "array.new rtt must be rtt")) {
- return;
+ if (curr->rtt) {
+ if (!shouldBeTrue(
+ curr->rtt->type.isRtt(), curr, "array.new rtt must be rtt")) {
+ return;
+ }
+ }
+ auto heapType = curr->type.getHeapType();
+ if (curr->rtt) {
+ shouldBeEqual(curr->rtt->type.getHeapType(),
+ heapType,
+ curr,
+ "array.new heap type must match rtt");
}
- auto heapType = curr->rtt->type.getHeapType();
if (!shouldBeTrue(
heapType.isArray(), curr, "array.new heap type must be array")) {
return;
@@ -2462,11 +2478,19 @@ void FunctionValidator::visitArrayInit(ArrayInit* curr) {
if (curr->type == Type::unreachable) {
return;
}
- if (!shouldBeTrue(
- curr->rtt->type.isRtt(), curr, "array.init rtt must be rtt")) {
- return;
+ if (curr->rtt) {
+ if (!shouldBeTrue(
+ curr->rtt->type.isRtt(), curr, "array.init rtt must be rtt")) {
+ return;
+ }
+ }
+ auto heapType = curr->type.getHeapType();
+ if (curr->rtt) {
+ shouldBeEqual(curr->rtt->type.getHeapType(),
+ heapType,
+ curr,
+ "array.init heap type must match rtt");
}
- auto heapType = curr->rtt->type.getHeapType();
if (!shouldBeTrue(
heapType.isArray(), curr, "array.init heap type must be array")) {
return;
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 0f1017e00..792e43cbe 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -1018,14 +1018,18 @@ void RttSub::finalize() {
}
void StructNew::finalize() {
- if (rtt->type == Type::unreachable) {
+ if (rtt && rtt->type == Type::unreachable) {
type = Type::unreachable;
return;
}
if (handleUnreachableOperands(this)) {
return;
}
- type = Type(rtt->type.getHeapType(), NonNullable);
+ // A dynamic StructNew infers the type from the rtt. A static one has the type
+ // already in the type field.
+ if (rtt) {
+ type = Type(rtt->type.getHeapType(), NonNullable);
+ }
}
void StructGet::finalize() {
@@ -1045,16 +1049,21 @@ void StructSet::finalize() {
}
void ArrayNew::finalize() {
- if (rtt->type == Type::unreachable || size->type == Type::unreachable ||
+ if ((rtt && rtt->type == Type::unreachable) ||
+ size->type == Type::unreachable ||
(init && init->type == Type::unreachable)) {
type = Type::unreachable;
return;
}
- type = Type(rtt->type.getHeapType(), NonNullable);
+ // A dynamic ArrayNew infers the type from the rtt. A static one has the type
+ // already in the type field.
+ if (rtt) {
+ type = Type(rtt->type.getHeapType(), NonNullable);
+ }
}
void ArrayInit::finalize() {
- if (rtt->type == Type::unreachable) {
+ if (rtt && rtt->type == Type::unreachable) {
type = Type::unreachable;
return;
}
@@ -1064,7 +1073,11 @@ void ArrayInit::finalize() {
return;
}
}
- type = Type(rtt->type.getHeapType(), NonNullable);
+ // A dynamic ArrayInit infers the type from the rtt. A static one has the type
+ // already in the type field.
+ if (rtt) {
+ type = Type(rtt->type.getHeapType(), NonNullable);
+ }
}
void ArrayGet::finalize() {
diff --git a/test/heap-types.wast b/test/heap-types.wast
index be6da5330..3c0a5a0ef 100644
--- a/test/heap-types.wast
+++ b/test/heap-types.wast
@@ -422,4 +422,35 @@
)
)
)
+ (func $static-constructions
+ (drop
+ (struct.new_default $struct.A)
+ )
+ (drop
+ (struct.new $struct.A
+ (i32.const 1)
+ (f32.const 2.345)
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (array.new $vector
+ (f64.const 3.14159)
+ (i32.const 3)
+ )
+ )
+ (drop
+ (array.new_default $matrix
+ (i32.const 10)
+ )
+ )
+ (drop
+ (array.init_static $vector
+ (f64.const 1)
+ (f64.const 2)
+ (f64.const 4)
+ (f64.const 8)
+ )
+ )
+ )
)
diff --git a/test/heap-types.wast.from-wast b/test/heap-types.wast.from-wast
index 9ca3b580b..f4e7245bb 100644
--- a/test/heap-types.wast.from-wast
+++ b/test/heap-types.wast.from-wast
@@ -1,11 +1,11 @@
(module
(type $struct.A (struct (field i32) (field f32) (field $named f64)))
(type $struct.B (struct (field i8) (field (mut i16)) (field (ref $struct.A)) (field (mut (ref $struct.A)))))
- (type $none_=>_none (func))
(type $vector (array (mut f64)))
+ (type $none_=>_none (func))
(type $grandchild (struct (field i32) (field i64)))
- (type $struct.C (struct (field $named-mut (mut f32))))
(type $matrix (array (mut (ref null $vector))))
+ (type $struct.C (struct (field $named-mut (mut f32))))
(type $parent (struct ))
(type $child (struct (field i32)))
(type $bytes (array (mut i8)))
@@ -521,4 +521,35 @@
)
)
)
+ (func $static-constructions
+ (drop
+ (struct.new_default $struct.A)
+ )
+ (drop
+ (struct.new $struct.A
+ (i32.const 1)
+ (f32.const 2.3450000286102295)
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (array.new $vector
+ (f64.const 3.14159)
+ (i32.const 3)
+ )
+ )
+ (drop
+ (array.new_default $matrix
+ (i32.const 10)
+ )
+ )
+ (drop
+ (array.init_static $vector
+ (f64.const 1)
+ (f64.const 2)
+ (f64.const 4)
+ (f64.const 8)
+ )
+ )
+ )
)
diff --git a/test/heap-types.wast.fromBinary b/test/heap-types.wast.fromBinary
index 7ac581848..f6b364b14 100644
--- a/test/heap-types.wast.fromBinary
+++ b/test/heap-types.wast.fromBinary
@@ -1,8 +1,8 @@
(module
(type $struct.A (struct (field i32) (field f32) (field $named f64)))
(type $struct.B (struct (field i8) (field (mut i16)) (field (ref $struct.A)) (field (mut (ref $struct.A)))))
- (type $none_=>_none (func))
(type $vector (array (mut f64)))
+ (type $none_=>_none (func))
(type $grandchild (struct (field i32) (field i64)))
(type $matrix (array (mut (ref null $vector))))
(type $struct.C (struct (field $named-mut (mut f32))))
@@ -476,5 +476,36 @@
)
)
)
+ (func $static-constructions
+ (drop
+ (struct.new_default $struct.A)
+ )
+ (drop
+ (struct.new $struct.A
+ (i32.const 1)
+ (f32.const 2.3450000286102295)
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (array.new $vector
+ (f64.const 3.14159)
+ (i32.const 3)
+ )
+ )
+ (drop
+ (array.new_default $matrix
+ (i32.const 10)
+ )
+ )
+ (drop
+ (array.init_static $vector
+ (f64.const 1)
+ (f64.const 2)
+ (f64.const 4)
+ (f64.const 8)
+ )
+ )
+ )
)
diff --git a/test/heap-types.wast.fromBinary.noDebugInfo b/test/heap-types.wast.fromBinary.noDebugInfo
index 6a8991150..c33ba0cc8 100644
--- a/test/heap-types.wast.fromBinary.noDebugInfo
+++ b/test/heap-types.wast.fromBinary.noDebugInfo
@@ -1,8 +1,8 @@
(module
(type ${i32_f32_f64} (struct (field i32) (field f32) (field f64)))
(type ${i8_mut:i16_ref|{i32_f32_f64}|_mut:ref|{i32_f32_f64}|} (struct (field i8) (field (mut i16)) (field (ref ${i32_f32_f64})) (field (mut (ref ${i32_f32_f64})))))
- (type $none_=>_none (func))
(type $[mut:f64] (array (mut f64)))
+ (type $none_=>_none (func))
(type ${i32_i64} (struct (field i32) (field i64)))
(type $[mut:ref?|[mut:f64]|] (array (mut (ref null $[mut:f64]))))
(type ${mut:f32} (struct (field (mut f32))))
@@ -476,5 +476,36 @@
)
)
)
+ (func $23
+ (drop
+ (struct.new_default ${i32_f32_f64})
+ )
+ (drop
+ (struct.new ${i32_f32_f64}
+ (i32.const 1)
+ (f32.const 2.3450000286102295)
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (array.new $[mut:f64]
+ (f64.const 3.14159)
+ (i32.const 3)
+ )
+ )
+ (drop
+ (array.new_default $[mut:ref?|[mut:f64]|]
+ (i32.const 10)
+ )
+ )
+ (drop
+ (array.init_static $[mut:f64]
+ (f64.const 1)
+ (f64.const 2)
+ (f64.const 4)
+ (f64.const 8)
+ )
+ )
+ )
)
diff --git a/test/lit/binary/heap-types.wast b/test/lit/binary/heap-types.wast
new file mode 100644
index 000000000..f455d7376
--- /dev/null
+++ b/test/lit/binary/heap-types.wast
@@ -0,0 +1,150 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+
+;; Check that heap types are emitted properly in the binary format. This file
+;; contains small modules that each use a single instruction with a heap type.
+;; If we forgot to update collectHeapTypes then we would not write out their
+;; type, and hit an error during --roundtrip.
+
+;; RUN: foreach %s %t wasm-opt -all --roundtrip -S -o - | filecheck %s
+;; RUN: foreach %s %t wasm-opt -all --roundtrip -S --nominal -o - | filecheck %s --check-prefix NOMNL
+
+(module
+ ;; CHECK: (type $struct.A (struct (field i32)))
+ ;; NOMNL: (type $struct.A (struct (field i32)))
+ (type $struct.A (struct i32))
+ ;; NOMNL: (type $struct.B (struct (field i32)))
+ (type $struct.B (struct i32))
+ ;; CHECK: (func $test
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.test_static $struct.A
+ ;; CHECK-NEXT: (ref.null $struct.A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $test
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (ref.test_static $struct.B
+ ;; NOMNL-NEXT: (ref.null $struct.A)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $test
+ (drop
+ (ref.test_static $struct.B (ref.null $struct.A))
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $struct.A (struct (field i32)))
+ ;; NOMNL: (type $struct.A (struct (field i32)))
+ (type $struct.A (struct i32))
+ ;; NOMNL: (type $struct.B (struct (field i32)))
+ (type $struct.B (struct i32))
+ ;; CHECK: (func $test
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast_static $struct.A
+ ;; CHECK-NEXT: (ref.null $struct.A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $test
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (ref.cast_static $struct.B
+ ;; NOMNL-NEXT: (ref.null $struct.A)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $test
+ (drop
+ (ref.cast_static $struct.B (ref.null $struct.A))
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $struct.A (struct (field i32)))
+ ;; NOMNL: (type $struct.A (struct (field i32)))
+ (type $struct.A (struct i32))
+ ;; CHECK: (func $test
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $struct.A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $test
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (struct.new_default $struct.A)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $test
+ (drop
+ (struct.new_default $struct.A)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $vector (array (mut f64)))
+ ;; NOMNL: (type $vector (array (mut f64)))
+ (type $vector (array (mut f64)))
+ ;; CHECK: (func $test
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (array.new $vector
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (i32.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $test
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (array.new $vector
+ ;; NOMNL-NEXT: (f64.const 3.14159)
+ ;; NOMNL-NEXT: (i32.const 3)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $test
+ (drop
+ (array.new $vector
+ (f64.const 3.14159)
+ (i32.const 3)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $vector (array (mut f64)))
+ ;; NOMNL: (type $vector (array (mut f64)))
+ (type $vector (array (mut f64)))
+ ;; CHECK: (func $test
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (array.init_static $vector
+ ;; CHECK-NEXT: (f64.const 1)
+ ;; CHECK-NEXT: (f64.const 2)
+ ;; CHECK-NEXT: (f64.const 4)
+ ;; CHECK-NEXT: (f64.const 8)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $test
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (array.init_static $vector
+ ;; NOMNL-NEXT: (f64.const 1)
+ ;; NOMNL-NEXT: (f64.const 2)
+ ;; NOMNL-NEXT: (f64.const 4)
+ ;; NOMNL-NEXT: (f64.const 8)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $test
+ (drop
+ (array.init_static $vector
+ (f64.const 1)
+ (f64.const 2)
+ (f64.const 4)
+ (f64.const 8)
+ )
+ )
+ )
+)
diff --git a/test/lit/passes/heap2local.wast b/test/lit/passes/heap2local.wast
index d49836b99..90ac8a418 100644
--- a/test/lit/passes/heap2local.wast
+++ b/test/lit/passes/heap2local.wast
@@ -254,7 +254,7 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (struct.new_with_rtt $struct.A
+ ;; CHECK-NEXT: (block
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: (rtt.canon $struct.A)
@@ -1937,4 +1937,27 @@
)
)
)
+
+ ;; CHECK: (func $simple-no-rtt
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (local $1 f64)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
+ ;; CHECK-NEXT: (local.set $0
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $1
+ ;; CHECK-NEXT: (f64.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $simple-no-rtt
+ (drop
+ ;; This allocation has no rtt, so we have nothing to drop from it when
+ ;; we optimize.
+ (struct.new_default $struct.A)
+ )
+ )
)
diff --git a/test/lit/passes/precompute-gc.wast b/test/lit/passes/precompute-gc.wast
index 0a08c02c0..44eff1c5f 100644
--- a/test/lit/passes/precompute-gc.wast
+++ b/test/lit/passes/precompute-gc.wast
@@ -510,4 +510,24 @@
)
)
)
+
+ ;; CHECK: (func $new_block_unreachable (result anyref)
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (rtt.canon $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $new_block_unreachable (result anyref)
+ (struct.new_with_rtt $struct
+ ;; The value is a block with an unreachable. precompute will get rid of the
+ ;; block, after which fuzz-exec should not crash - this is a regression test
+ ;; for us being careful in how we execute an unreachable struct.new
+ (block $label$1 (result i32)
+ (unreachable)
+ )
+ (rtt.canon $struct)
+ )
+ )
)
diff --git a/test/passes/Oz_fuzz-exec_all-features.txt b/test/passes/Oz_fuzz-exec_all-features.txt
index 4cb8f4142..179a75082 100644
--- a/test/passes/Oz_fuzz-exec_all-features.txt
+++ b/test/passes/Oz_fuzz-exec_all-features.txt
@@ -534,32 +534,25 @@
)
(call $log
(ref.test_static $struct
- (array.new_with_rtt $bytes
+ (array.new $bytes
(i32.const 20)
(i32.const 10)
- (rtt.canon $bytes)
)
)
)
(call $log
(ref.test_static $struct
- (struct.new_default_with_rtt $struct
- (rtt.canon $struct)
- )
+ (struct.new_default $struct)
)
)
(call $log
(ref.test_static $extendedstruct
- (struct.new_default_with_rtt $struct
- (rtt.canon $struct)
- )
+ (struct.new_default $struct)
)
)
(call $log
(ref.test_static $struct
- (struct.new_default_with_rtt $extendedstruct
- (rtt.canon $extendedstruct)
- )
+ (struct.new_default $extendedstruct)
)
)
)
@@ -571,9 +564,7 @@
(drop
(br_on_cast_static $block $struct
(br_on_cast_static $extendedblock $extendedstruct
- (struct.new_default_with_rtt $struct
- (rtt.canon $struct)
- )
+ (struct.new_default $struct)
)
)
)
@@ -598,9 +589,7 @@
(block $failblock (result (ref $struct))
(drop
(br_on_cast_static_fail $failblock $extendedstruct
- (struct.new_default_with_rtt $struct
- (rtt.canon $struct)
- )
+ (struct.new_default $struct)
)
)
(call $log
diff --git a/test/passes/Oz_fuzz-exec_all-features.wast b/test/passes/Oz_fuzz-exec_all-features.wast
index 54bff9e5d..ea29f259a 100644
--- a/test/passes/Oz_fuzz-exec_all-features.wast
+++ b/test/passes/Oz_fuzz-exec_all-features.wast
@@ -556,35 +556,28 @@
;; Testing something completely wrong (struct vs array) returns 0.
(call $log
(ref.test_static $struct
- (array.new_with_rtt $bytes
+ (array.new $bytes
(i32.const 20)
(i32.const 10)
- (rtt.canon $bytes)
)
)
)
;; Testing a thing with the same type returns 1.
(call $log
(ref.test_static $struct
- (struct.new_default_with_rtt $struct
- (rtt.canon $struct)
- )
+ (struct.new_default $struct)
)
)
;; A bad downcast returns 0: we create a struct, which is not a extendedstruct.
(call $log
(ref.test_static $extendedstruct
- (struct.new_default_with_rtt $struct
- (rtt.canon $struct)
- )
+ (struct.new_default $struct)
)
)
;; Casting to a supertype works.
(call $log
(ref.test_static $struct
- (struct.new_default_with_rtt $extendedstruct
- (rtt.canon $extendedstruct)
- )
+ (struct.new_default $extendedstruct)
)
)
)
@@ -592,7 +585,7 @@
(local $any anyref)
;; create a simple $struct, store it in an anyref
(local.set $any
- (struct.new_default_with_rtt $struct (rtt.canon $struct))
+ (struct.new_default $struct)
)
(drop
(block $block (result ($ref $struct))
@@ -621,7 +614,7 @@
(local $any anyref)
;; create a simple $struct, store it in an anyref
(local.set $any
- (struct.new_default_with_rtt $struct (rtt.canon $struct))
+ (struct.new_default $struct)
)
(drop
(block $failblock (result anyref)