summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/cost.h10
-rw-r--r--src/ir/effects.h9
-rw-r--r--src/ir/module-utils.h9
-rw-r--r--src/literal.h15
-rw-r--r--src/passes/Print.cpp41
-rw-r--r--src/tools/fuzzing.h40
-rw-r--r--src/wasm-binary.h8
-rw-r--r--src/wasm-builder.h29
-rw-r--r--src/wasm-delegations-fields.h4
-rw-r--r--src/wasm-interpreter.h10
-rw-r--r--src/wasm-s-parser.h1
-rw-r--r--src/wasm-type.h2
-rw-r--r--src/wasm.h7
-rw-r--r--src/wasm/literal.cpp15
-rw-r--r--src/wasm/wasm-binary.cpp60
-rw-r--r--src/wasm/wasm-s-parser.cpp68
-rw-r--r--src/wasm/wasm-stack.cpp14
-rw-r--r--src/wasm/wasm-type.cpp4
-rw-r--r--src/wasm/wasm-validator.cpp9
-rw-r--r--src/wasm/wasm.cpp10
-rw-r--r--test/heap-types.wast61
-rw-r--r--test/heap-types.wast.from-wast76
-rw-r--r--test/heap-types.wast.fromBinary76
-rw-r--r--test/heap-types.wast.fromBinary.noDebugInfo68
-rw-r--r--test/passes/O_all-features.txt12
-rw-r--r--test/passes/O_all-features.wast16
-rw-r--r--test/passes/O_all-features_ignore-implicit-traps.txt8
-rw-r--r--test/passes/O_all-features_ignore-implicit-traps.wast17
-rw-r--r--test/passes/inlining_all-features.txt2
-rw-r--r--test/passes/inlining_all-features.wast2
-rw-r--r--test/typed-function-references.wast4
-rw-r--r--test/typed-function-references.wast.from-wast4
-rw-r--r--test/typed-function-references.wast.fromBinary6
-rw-r--r--test/typed-function-references.wast.fromBinary.noDebugInfo6
34 files changed, 581 insertions, 142 deletions
diff --git a/src/ir/cost.h b/src/ir/cost.h
index 333f599ee..1e383afca 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -566,12 +566,20 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> {
Index visitRttCanon(RttCanon* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitRttSub(RttSub* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitStructNew(StructNew* curr) { WASM_UNREACHABLE("TODO: GC"); }
- Index visitStructGet(StructGet* curr) { WASM_UNREACHABLE("TODO: GC"); }
+ Index visitStructGet(StructGet* curr) {
+ return 1 + nullCheckCost(curr->value) + visit(curr->value);
+ }
Index visitStructSet(StructSet* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitArrayNew(ArrayNew* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitArrayGet(ArrayGet* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitArraySet(ArraySet* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitArrayLen(ArrayLen* curr) { WASM_UNREACHABLE("TODO: GC"); }
+
+private:
+ Index nullCheckCost(Expression* ref) {
+ // A nullable type requires a bounds check in most VMs.
+ return ref->type.isNullable();
+ }
};
} // namespace wasm
diff --git a/src/ir/effects.h b/src/ir/effects.h
index c0210c221..80aa74cb6 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -515,12 +515,12 @@ private:
if (parent.tryDepth == 0) {
parent.throws = true;
}
- // rethrow traps when the arg is null
+ // traps when the arg is null
parent.implicitTrap = true;
}
void visitBrOnExn(BrOnExn* curr) {
parent.breakTargets.insert(curr->name);
- // br_on_exn traps when the arg is null
+ // traps when the arg is null
parent.implicitTrap = true;
}
void visitNop(Nop* curr) {}
@@ -562,7 +562,10 @@ private:
WASM_UNREACHABLE("TODO (gc): struct.new");
}
void visitStructGet(StructGet* curr) {
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ // traps when the arg is null
+ if (curr->value->type.isNullable()) {
+ parent.implicitTrap = true;
+ }
}
void visitStructSet(StructSet* curr) {
WASM_UNREACHABLE("TODO (gc): struct.set");
diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h
index de37e0875..64f04da31 100644
--- a/src/ir/module-utils.h
+++ b/src/ir/module-utils.h
@@ -425,10 +425,12 @@ inline void collectHeapTypes(Module& wasm,
TypeCounter(Counts& counts) : counts(counts) {}
void visitExpression(Expression* curr) {
- if (curr->is<RefNull>()) {
- counts.maybeNote(curr->type);
- } else if (auto* call = curr->dynCast<CallIndirect>()) {
+ if (auto* call = curr->dynCast<CallIndirect>()) {
counts.note(call->sig);
+ } else if (curr->is<RefNull>()) {
+ counts.maybeNote(curr->type);
+ } else if (auto* get = curr->dynCast<StructGet>()) {
+ counts.maybeNote(get->value->type);
} else if (Properties::isControlFlowStructure(curr)) {
counts.maybeNote(curr->type);
if (curr->type.isTuple()) {
@@ -436,7 +438,6 @@ inline void collectHeapTypes(Module& wasm,
counts.note(Signature(Type::none, curr->type));
}
}
- // TODO struct.get and others contain a type index
}
};
TypeCounter(counts).walk(func->body);
diff --git a/src/literal.h b/src/literal.h
index 3d867a119..12d3c6d07 100644
--- a/src/literal.h
+++ b/src/literal.h
@@ -43,6 +43,11 @@ class Literal {
Name func;
// exnref package. `nullptr` indicates a `null` value.
std::unique_ptr<ExceptionPackage> exn;
+ // 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.
+ std::shared_ptr<Literals> gcData;
// TODO: Literals of type `externref` can only be `null` currently but we
// will need to represent extern values eventually, to
// 1) run the spec tests and fuzzer with reference types enabled and
@@ -74,12 +79,17 @@ public:
explicit Literal(Name func, Type type) : func(func), type(type) {}
explicit Literal(std::unique_ptr<ExceptionPackage>&& exn)
: exn(std::move(exn)), type(Type::exnref) {}
+ explicit Literal(std::shared_ptr<Literals> gcData, Type type)
+ : gcData(gcData), type(type) {}
Literal(const Literal& other);
Literal& operator=(const Literal& other);
~Literal() {
if (type.isException()) {
exn.~unique_ptr();
}
+ if (type.isStruct() || type.isArray()) {
+ gcData.~shared_ptr();
+ }
}
bool isConcrete() const { return type != Type::none; }
@@ -92,6 +102,9 @@ public:
if (type.isException()) {
return !exn;
}
+ if (isGCData()) {
+ return !gcData;
+ }
return true;
}
return false;
@@ -156,6 +169,7 @@ public:
WASM_UNREACHABLE("unexpected type");
}
}
+ bool isGCData() const { return type.isStruct() || type.isArray(); }
static Literals makeZeros(Type type);
static Literals makeOnes(Type type);
@@ -276,6 +290,7 @@ public:
return func;
}
ExceptionPackage getExceptionPackage() const;
+ std::shared_ptr<Literals> getGCData() const;
// careful!
int32_t* geti32Ptr() {
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 711c1b604..ee7aa9ac7 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -222,13 +222,13 @@ struct PrintExpressionContents
printName(curr->name, o);
}
if (curr->type.isConcrete()) {
- o << ' ' << ResultType(curr->type);
+ o << ' ' << ResultTypeName(curr->type);
}
}
void visitIf(If* curr) {
printMedium(o, "if");
if (curr->type.isConcrete()) {
- o << ' ' << ResultType(curr->type);
+ o << ' ' << ResultTypeName(curr->type);
}
}
void visitLoop(Loop* curr) {
@@ -238,7 +238,7 @@ struct PrintExpressionContents
printName(curr->name, o);
}
if (curr->type.isConcrete()) {
- o << ' ' << ResultType(curr->type);
+ o << ' ' << ResultTypeName(curr->type);
}
}
void visitBreak(Break* curr) {
@@ -1600,7 +1600,7 @@ struct PrintExpressionContents
void visitSelect(Select* curr) {
prepareColor(o) << "select";
if (curr->type.isRef()) {
- o << " (result " << curr->type << ')';
+ o << ' ' << ResultTypeName(curr->type);
}
}
void visitDrop(Drop* curr) { printMedium(o, "drop"); }
@@ -1609,7 +1609,7 @@ struct PrintExpressionContents
void visitMemoryGrow(MemoryGrow* curr) { printMedium(o, "memory.grow"); }
void visitRefNull(RefNull* curr) {
printMedium(o, "ref.null ");
- o << curr->type.getHeapType();
+ printHeapTypeName(o, curr->type.getHeapType());
}
void visitRefIsNull(RefIsNull* curr) { printMedium(o, "ref.is_null"); }
void visitRefFunc(RefFunc* curr) {
@@ -1684,7 +1684,20 @@ struct PrintExpressionContents
WASM_UNREACHABLE("TODO (gc): struct.new");
}
void visitStructGet(StructGet* curr) {
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ const auto& field =
+ curr->value->type.getHeapType().getStruct().fields[curr->index];
+ if (field.type == Type::i32 && field.packedType != Field::not_packed) {
+ if (curr->signed_) {
+ printMedium(o, "struct.get_s ");
+ } else {
+ printMedium(o, "struct.get_u ");
+ }
+ } else {
+ printMedium(o, "struct.get ");
+ }
+ printHeapTypeName(o, curr->value->type.getHeapType());
+ o << ' ';
+ o << curr->index;
}
void visitStructSet(StructSet* curr) {
printMedium(o, "struct.set");
@@ -2358,7 +2371,9 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
void visitStructGet(StructGet* curr) {
o << '(';
PrintExpressionContents(currFunction, o).visit(curr);
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ incIndent();
+ printFullLine(curr->value);
+ decIndent();
}
void visitStructSet(StructSet* curr) {
o << '(';
@@ -2417,7 +2432,17 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
if (field.mutable_) {
o << "(mut ";
}
- o << TypeName(field.type);
+ if (field.type == Type::i32 && field.packedType != Field::not_packed) {
+ if (field.packedType == Field::i8) {
+ o << "i8";
+ } else if (field.packedType == Field::i16) {
+ o << "i16";
+ } else {
+ WASM_UNREACHABLE("invalid packed type");
+ }
+ } else {
+ o << TypeName(field.type);
+ }
if (field.mutable_) {
o << ')';
}
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 63f10cac4..621555ab1 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -327,28 +327,31 @@ private:
}
SmallVector<Type, 2> options;
options.push_back(type); // includes itself
- TODO_SINGLE_COMPOUND(type);
- switch (type.getBasic()) {
- case Type::anyref:
- if (wasm.features.hasReferenceTypes()) {
- options.push_back(Type::funcref);
- options.push_back(Type::externref);
- if (wasm.features.hasExceptionHandling()) {
- options.push_back(Type::exnref);
+ // TODO: interesting uses of typed function types
+ // TODO: interesting subtypes of compound types
+ if (type.isBasic()) {
+ switch (type.getBasic()) {
+ case Type::anyref:
+ if (wasm.features.hasReferenceTypes()) {
+ options.push_back(Type::funcref);
+ options.push_back(Type::externref);
+ if (wasm.features.hasExceptionHandling()) {
+ options.push_back(Type::exnref);
+ }
+ if (wasm.features.hasGC()) {
+ options.push_back(Type::eqref);
+ options.push_back(Type::i31ref);
+ }
}
+ break;
+ case Type::eqref:
if (wasm.features.hasGC()) {
- options.push_back(Type::eqref);
options.push_back(Type::i31ref);
}
- }
- break;
- case Type::eqref:
- if (wasm.features.hasGC()) {
- options.push_back(Type::i31ref);
- }
- break;
- default:
- break;
+ break;
+ default:
+ break;
+ }
}
return pick(options);
}
@@ -1120,6 +1123,7 @@ private:
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
&Self::makeI31New);
}
+ // TODO: struct.get and other GC things
return (this->*pick(options))(type);
}
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index d22205a0a..4c4789b69 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -338,6 +338,8 @@ enum EncodedType {
f32 = -0x3, // 0x7d
f64 = -0x4, // 0x7c
v128 = -0x5, // 0x7b
+ i8 = -0x6, // 0x7a
+ i16 = -0x7, // 0x79
// function reference type
funcref = -0x10, // 0x70
// opaque host reference type
@@ -1252,7 +1254,12 @@ public:
int32_t getS32LEB();
int64_t getS64LEB();
uint64_t getUPtrLEB();
+
+ // Read a value and get a type for it.
Type getType();
+ // Get a type given the initial S32LEB has already been read, and is provided.
+ Type getType(int initial);
+
HeapType getHeapType();
Field getField();
Type getConcreteType();
@@ -1261,7 +1268,6 @@ public:
void verifyInt16(int16_t x);
void verifyInt32(int32_t x);
void verifyInt64(int64_t x);
- void ungetInt8();
void readHeader();
void readStart();
void readMemory();
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 50a6e97fb..e3b12d23f 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -727,9 +727,15 @@ public:
ret->finalize();
return ret;
}
- StructGet* makeStructGet() {
+ StructGet* makeStructGet(Index index,
+ Expression* value,
+ Type type,
+ bool signed_ = false) {
auto* ret = wasm.allocator.alloc<StructGet>();
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ ret->index = index;
+ ret->value = value;
+ ret->type = type;
+ ret->signed_ = signed_;
ret->finalize();
return ret;
}
@@ -780,12 +786,12 @@ public:
if (type.isNumber()) {
return makeConst(value);
}
- if (type.isFunction()) {
- if (!value.isNull()) {
- return makeRefFunc(value.getFunc(), type);
- }
+ if (value.isNull()) {
return makeRefNull(type);
}
+ if (type.isFunction()) {
+ return makeRefFunc(value.getFunc(), type);
+ }
TODO_SINGLE_COMPOUND(type);
switch (type.getBasic()) {
case Type::externref:
@@ -962,13 +968,12 @@ public:
if (curr->type.isTuple()) {
return makeConstantExpression(Literal::makeZeros(curr->type));
}
+ if (curr->type.isNullable()) {
+ return ExpressionManipulator::refNull(curr, curr->type);
+ }
if (curr->type.isFunction()) {
- if (curr->type.isNullable()) {
- return ExpressionManipulator::refNull(curr, curr->type);
- } else {
- // We can't do any better, keep the original.
- return curr;
- }
+ // We can't do any better, keep the original.
+ return curr;
}
Literal value;
// TODO: reuse node conditionally when possible for literals
diff --git a/src/wasm-delegations-fields.h b/src/wasm-delegations-fields.h
index ca0a8f7cb..f3c5b9546 100644
--- a/src/wasm-delegations-fields.h
+++ b/src/wasm-delegations-fields.h
@@ -595,7 +595,9 @@ switch (DELEGATE_ID) {
}
case Expression::Id::StructGetId: {
DELEGATE_START(StructGet);
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ DELEGATE_FIELD_INT(StructGet, index);
+ DELEGATE_FIELD_CHILD(StructGet, value);
+ DELEGATE_FIELD_INT(Load, signed_);
DELEGATE_END(StructGet);
break;
}
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 93c409797..b6e526544 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1402,7 +1402,15 @@ public:
}
Flow visitStructGet(StructGet* curr) {
NOTE_ENTER("StructGet");
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ Flow flow = this->visit(curr->value);
+ if (flow.breaking()) {
+ return flow;
+ }
+ auto data = flow.getSingleValue().getGCData();
+ if (!data) {
+ trap("null ref");
+ }
+ return (*data)[curr->index];
}
Flow visitStructSet(StructSet* curr) {
NOTE_ENTER("StructSet");
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 7e20ff4c9..2c70445cf 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -262,6 +262,7 @@ private:
Expression* makeRttCanon(Element& s);
Expression* makeRttSub(Element& s);
Expression* makeStructNew(Element& s, bool default_);
+ Index getStructIndex(const HeapType& type, Element& s);
Expression* makeStructGet(Element& s, bool signed_ = false);
Expression* makeStructSet(Element& s);
Expression* makeArrayNew(Element& s, bool default_);
diff --git a/src/wasm-type.h b/src/wasm-type.h
index 98c6c70ed..4d842cdca 100644
--- a/src/wasm-type.h
+++ b/src/wasm-type.h
@@ -116,6 +116,8 @@ public:
bool isException() const;
bool isNullable() const;
bool isRtt() const;
+ bool isStruct() const;
+ bool isArray() const;
private:
template<bool (Type::*pred)() const> bool hasPredicate() {
diff --git a/src/wasm.h b/src/wasm.h
index 6367fa345..cc390573d 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1352,7 +1352,12 @@ class StructGet : public SpecificExpression<Expression::StructGetId> {
public:
StructGet(MixedArena& allocator) {}
- void finalize() { WASM_UNREACHABLE("TODO (gc): struct.get"); }
+ Index index;
+ Expression* value;
+ // Packed fields have a sign.
+ bool signed_ = false;
+
+ void finalize();
};
class StructSet : public SpecificExpression<Expression::StructSetId> {
diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp
index 4edaf8039..b7cf5084e 100644
--- a/src/wasm/literal.cpp
+++ b/src/wasm/literal.cpp
@@ -55,6 +55,9 @@ Literal::Literal(const Literal& other) : type(other.type) {
} else {
new (&exn) std::unique_ptr<ExceptionPackage>();
}
+ } else if (other.isGCData()) {
+ // Avoid calling the destructor on an uninitialized value
+ new (&gcData) std::shared_ptr<Literals>(other.gcData);
} else if (type.isFunction()) {
func = other.func;
} else {
@@ -189,6 +192,11 @@ ExceptionPackage Literal::getExceptionPackage() const {
return *exn;
}
+std::shared_ptr<Literals> Literal::getGCData() const {
+ assert(isGCData());
+ return gcData;
+}
+
Literal Literal::castToF32() {
assert(type == Type::i32);
Literal ret(Type::f32);
@@ -428,6 +436,13 @@ std::ostream& operator<<(std::ostream& o, Literal literal) {
} else {
o << "funcref(" << literal.getFunc() << ")";
}
+ } else if (literal.isGCData()) {
+ auto data = literal.getGCData();
+ if (data) {
+ o << "[ref " << *data << ']';
+ } else {
+ o << "[ref null " << literal.type << ']';
+ }
} else {
TODO_SINGLE_COMPOUND(literal.type);
switch (literal.type.getBasic()) {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 3aace6721..361faf631 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -1030,7 +1030,7 @@ void WasmBinaryWriter::writeType(Type type) {
void WasmBinaryWriter::writeHeapType(HeapType type) {
if (type.isSignature() || type.isStruct() || type.isArray()) {
- o << S32LEB(getTypeIndex(type));
+ o << S64LEB(getTypeIndex(type)); // TODO: Actually s33
return;
}
int ret = 0;
@@ -1058,11 +1058,21 @@ void WasmBinaryWriter::writeHeapType(HeapType type) {
case HeapType::ArrayKind:
WASM_UNREACHABLE("TODO: compound GC types");
}
- o << S32LEB(ret); // TODO: Actually encoded as s33
+ o << S64LEB(ret); // TODO: Actually s33
}
void WasmBinaryWriter::writeField(const Field& field) {
- writeType(field.type);
+ if (field.type == Type::i32 && field.packedType != Field::not_packed) {
+ if (field.packedType == Field::i8) {
+ o << S32LEB(BinaryConsts::EncodedType::i8);
+ } else if (field.packedType == Field::i16) {
+ o << S32LEB(BinaryConsts::EncodedType::i16);
+ } else {
+ WASM_UNREACHABLE("invalid packed type");
+ }
+ } else {
+ writeType(field.type);
+ }
o << U32LEB(field.mutable_);
}
@@ -1335,14 +1345,13 @@ uint64_t WasmBinaryBuilder::getUPtrLEB() {
return wasm.memory.is64() ? getU64LEB() : getU32LEB();
}
-Type WasmBinaryBuilder::getType() {
- int type = getS32LEB();
+Type WasmBinaryBuilder::getType(int initial) {
// Single value types are negative; signature indices are non-negative
- if (type >= 0) {
+ if (initial >= 0) {
// TODO: Handle block input types properly.
- return getSignatureByTypeIndex(type).results;
+ return getSignatureByTypeIndex(initial).results;
}
- switch (type) {
+ switch (initial) {
// None only used for block signatures. TODO: Separate out?
case BinaryConsts::EncodedType::Empty:
return Type::none;
@@ -1374,13 +1383,15 @@ Type WasmBinaryBuilder::getType() {
case BinaryConsts::EncodedType::i31ref:
return Type::i31ref;
default:
- throwError("invalid wasm type: " + std::to_string(type));
+ throwError("invalid wasm type: " + std::to_string(initial));
}
WASM_UNREACHABLE("unexpected type");
}
+Type WasmBinaryBuilder::getType() { return getType(getS32LEB()); }
+
HeapType WasmBinaryBuilder::getHeapType() {
- int type = getS32LEB(); // TODO: Actually encoded as s33
+ auto type = getS64LEB(); // TODO: Actually s33
// Single heap types are negative; heap type indices are non-negative
if (type >= 0) {
if (size_t(type) >= types.size()) {
@@ -1408,7 +1419,19 @@ HeapType WasmBinaryBuilder::getHeapType() {
}
Field WasmBinaryBuilder::getField() {
- auto type = getConcreteType();
+ // The value may be a general wasm type, or one of the types only possible in
+ // a field.
+ auto initial = getS32LEB();
+ if (initial == BinaryConsts::EncodedType::i8) {
+ auto mutable_ = getU32LEB();
+ return Field(Field::i8, mutable_);
+ }
+ if (initial == BinaryConsts::EncodedType::i16) {
+ auto mutable_ = getU32LEB();
+ return Field(Field::i16, mutable_);
+ }
+ // It's a regular wasm value.
+ auto type = getType(initial);
auto mutable_ = getU32LEB();
return Field(type, mutable_);
}
@@ -1466,12 +1489,6 @@ void WasmBinaryBuilder::verifyInt64(int64_t x) {
}
}
-void WasmBinaryBuilder::ungetInt8() {
- assert(pos > 0);
- BYN_TRACE("ungetInt8 (at " << pos << ")\n");
- pos--;
-}
-
void WasmBinaryBuilder::readHeader() {
BYN_TRACE("== readHeader\n");
verifyInt32(BinaryConsts::Magic);
@@ -5601,20 +5618,21 @@ bool WasmBinaryBuilder::maybeVisitStructGet(Expression*& out, uint32_t code) {
switch (code) {
case BinaryConsts::StructGet:
curr = allocator.alloc<StructGet>();
- // ...
break;
case BinaryConsts::StructGetS:
curr = allocator.alloc<StructGet>();
- // ...
+ curr->signed_ = true;
break;
case BinaryConsts::StructGetU:
curr = allocator.alloc<StructGet>();
- // ...
+ curr->signed_ = false;
break;
default:
return false;
}
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ auto type = getHeapType();
+ curr->index = getU32LEB();
+ curr->value = popNonVoidExpression();
curr->finalize();
out = curr;
return true;
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index c5296aec3..3e605429d 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -49,7 +49,8 @@ int unhex(char c) {
namespace wasm {
-static Name STRUCT("struct"), FIELD("field"), ARRAY("array");
+static Name STRUCT("struct"), FIELD("field"), ARRAY("array"), I8("i8"),
+ I16("i16");
static Address getAddress(const Element* s) { return atoll(s->c_str()); }
@@ -1888,28 +1889,13 @@ Expression* SExpressionWasmBuilder::makeRefNull(Element& s) {
throw ParseException("invalid heap type reference", s.line, s.col);
}
auto ret = allocator.alloc<RefNull>();
- if (s[1]->isStr()) {
- // For example, this parses
- // (ref.null func)
- ret->finalize(stringToHeapType(s[1]->str()));
+ // The heap type may be just "func", that is, the whole thing is just
+ // (ref.null func), or it may be the name of a defined type, such as
+ // (ref.null $struct.FOO)
+ if (s[1]->dollared()) {
+ ret->finalize(parseHeapType(*s[1]));
} else {
- // To parse a heap type, create an element around it, and call that method.
- // That is, given (func) we wrap to (ref (func)).
- // For example, this parses
- // (ref.null (func (param i32)))
- // TODO add a helper method, but this is the only user atm, and we are
- // waiting on https://github.com/WebAssembly/function-references/issues/42
- Element wrapper(wasm.allocator);
- auto& list = wrapper.list();
- list.resize(3);
- Element ref(wasm.allocator);
- ref.setString(REF, false, false);
- Element null(wasm.allocator);
- null.setString(NULL_, false, false);
- list[0] = &ref;
- list[1] = &null;
- list[2] = s[1];
- ret->finalize(elementToType(wrapper));
+ ret->finalize(stringToHeapType(s[1]->str()));
}
return ret;
}
@@ -2136,11 +2122,28 @@ Expression* SExpressionWasmBuilder::makeStructNew(Element& s, bool default_) {
return ret;
}
+Index SExpressionWasmBuilder::getStructIndex(const HeapType& type, Element& s) {
+ if (s.dollared()) {
+ auto name = s.str();
+ auto struct_ = type.getStruct();
+ auto& fields = struct_.fields;
+ for (Index i = 0; i < fields.size(); i++) {
+ if (fields[i].name == name) {
+ return i;
+ }
+ }
+ throw ParseException("bad struct name", s.line, s.col);
+ }
+ // this is a numeric index
+ return atoi(s.c_str());
+}
+
Expression* SExpressionWasmBuilder::makeStructGet(Element& s, bool signed_) {
- auto ret = allocator.alloc<StructGet>();
- WASM_UNREACHABLE("TODO (gc): struct.get_s/u");
- ret->finalize();
- return ret;
+ auto structType = parseHeapType(*s[1]);
+ auto index = getStructIndex(structType, *s[2]);
+ auto type = structType.getStruct().fields[index].type;
+ auto value = parseExpression(*s[3]);
+ return Builder(wasm).makeStructGet(index, value, type, signed_);
}
Expression* SExpressionWasmBuilder::makeStructSet(Element& s) {
@@ -2793,10 +2796,6 @@ HeapType SExpressionWasmBuilder::parseHeapType(Element& s) {
// It's a struct or an array.
auto parseField = [&](Element* t) {
bool mutable_ = false;
- if (t->isStr()) {
- // t is a simple string name like "i32"
- return Field(elementToType(*t), mutable_);
- }
// t is a list, containing either
// TYPE
// or
@@ -2815,6 +2814,15 @@ HeapType SExpressionWasmBuilder::parseHeapType(Element& s) {
mutable_ = true;
t = (*t)[1];
}
+ if (t->isStr()) {
+ // t is a simple string name like "i32". It can be a normal wasm type, or
+ // one of the special types only available in fields.
+ if (*t == I8) {
+ return Field(Field::i8, mutable_, name);
+ } else if (*t == I16) {
+ return Field(Field::i16, mutable_, name);
+ }
+ }
// Otherwise it's an arbitrary type.
return Field(elementToType(*t), mutable_, name);
};
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index a6a4ce171..4791c4a23 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -1910,7 +1910,19 @@ void BinaryInstWriter::visitStructNew(StructNew* curr) {
}
void BinaryInstWriter::visitStructGet(StructGet* curr) {
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ const auto& heapType = curr->value->type.getHeapType();
+ const auto& field = heapType.getStruct().fields[curr->index];
+ int8_t op;
+ if (field.type != Type::i32 || field.packedType == Field::not_packed) {
+ op = BinaryConsts::StructGet;
+ } else if (curr->signed_) {
+ op = BinaryConsts::StructGetS;
+ } else {
+ op = BinaryConsts::StructGetU;
+ }
+ o << int8_t(BinaryConsts::GCPrefix) << int8_t(op);
+ parent.writeHeapType(heapType);
+ o << U32LEB(curr->index);
}
void BinaryInstWriter::visitStructSet(StructSet* curr) {
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index 286542303..22a7c9888 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -392,6 +392,10 @@ bool Type::isRtt() const {
}
}
+bool Type::isStruct() const { return isRef() && getHeapType().isStruct(); }
+
+bool Type::isArray() const { return isRef() && getHeapType().isArray(); }
+
bool Type::operator<(const Type& other) const {
auto comp = [](const Type& a, const Type& b) {
if (a.isBasic() && b.isBasic()) {
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index fb55417d5..9a8644b28 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -2231,7 +2231,14 @@ void FunctionValidator::visitStructGet(StructGet* curr) {
shouldBeTrue(getModule()->features.hasGC(),
curr,
"struct.get requires gc to be enabled");
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ if (curr->value->type != Type::unreachable) {
+ const auto& fields = curr->value->type.getHeapType().getStruct().fields;
+ shouldBeTrue(curr->index < fields.size(), curr, "bad struct.get field");
+ shouldBeEqual(curr->type,
+ fields[curr->index].type,
+ curr,
+ "struct.get must have the proper type");
+ }
}
void FunctionValidator::visitStructSet(StructSet* curr) {
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index cbf573950..20f3ec843 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -1083,7 +1083,15 @@ void CallRef::finalize(Type type_) {
// TODO (gc): rtt.canon
// TODO (gc): rtt.sub
// TODO (gc): struct.new
-// TODO (gc): struct.get
+
+void StructGet::finalize() {
+ if (value->type == Type::unreachable) {
+ type = Type::unreachable;
+ } else {
+ type = value->type.getHeapType().getStruct().fields[index].type;
+ }
+}
+
// TODO (gc): struct.set
// TODO (gc): array.new
// TODO (gc): array.get
diff --git a/test/heap-types.wast b/test/heap-types.wast
index aaeae3115..57751503f 100644
--- a/test/heap-types.wast
+++ b/test/heap-types.wast
@@ -6,7 +6,8 @@
(field $named f64)
))
(type $struct.B (struct
- (field (mut i64))
+ (field i8)
+ (field (mut i16))
(field (ref $struct.A))
(field (mut (ref $struct.A)))
))
@@ -18,11 +19,59 @@
(type $matrix (array (ref $vector)))
(func "foo" (param $x (ref $struct.A)) (result (ref $struct.B))
- (local (ref null $struct.A))
- (local (ref null $struct.B))
- (local (ref null $struct.C))
- (local (ref null $vector))
- (local (ref null $matrix))
+ (local $tA (ref null $struct.A))
+ (local $tB (ref null $struct.B))
+ (local $tc (ref null $struct.C))
+ (local $tv (ref null $vector))
+ (local $tm (ref null $matrix))
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (struct.get $struct.A 0 (local.get $x))
+ )
+ (drop
+ (struct.get $struct.A 1 (local.get $x))
+ )
+ (drop
+ (struct.get $struct.A 2 (local.get $x))
+ )
+ (drop
+ (struct.get $struct.A $named (local.get $x))
+ )
+ (drop
+ (struct.get_u $struct.B 0 (local.get $tB))
+ )
+ (drop
+ (struct.get_s $struct.B 0 (local.get $tB))
+ )
+ (drop
+ (ref.null $struct.A)
+ )
+ (drop
+ (block (result (ref null $struct.A))
+ (local.get $x)
+ )
+ )
+ (drop
+ (if (result (ref null $struct.A))
+ (i32.const 1)
+ (local.get $x)
+ (local.get $x)
+ )
+ )
+ (drop
+ (loop (result (ref null $struct.A))
+ (local.get $x)
+ )
+ )
+ (drop
+ (select (result (ref null $struct.A))
+ (local.get $x)
+ (local.get $x)
+ (i32.const 1)
+ )
+ )
(unreachable)
)
)
diff --git a/test/heap-types.wast.from-wast b/test/heap-types.wast.from-wast
index 0da7e5e90..c575d2dc6 100644
--- a/test/heap-types.wast.from-wast
+++ b/test/heap-types.wast.from-wast
@@ -2,16 +2,76 @@
(type ${i32_f32_f64} (struct (field i32) (field f32) (field f64)))
(type $[mut:f64] (array (mut f64)))
(type ${mut:f32} (struct (field (mut f32))))
- (type ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} (struct (field (mut i64)) (field (ref null ${i32_f32_f64})) (field (mut (ref null ${i32_f32_f64})))))
+ (type ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} (struct (field i8) (field (mut i16)) (field (ref null ${i32_f32_f64})) (field (mut (ref null ${i32_f32_f64})))))
(type $[ref?|[mut:f64]|] (array (ref null $[mut:f64])))
- (type $ref?|{i32_f32_f64}|_=>_ref?|{mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}| (func (param (ref null ${i32_f32_f64})) (result (ref null ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))))
+ (type $ref?|{i32_f32_f64}|_=>_ref?|{i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}| (func (param (ref null ${i32_f32_f64})) (result (ref null ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))))
(export "foo" (func $0))
- (func $0 (param $x (ref null ${i32_f32_f64})) (result (ref null ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
- (local $1 (ref null ${i32_f32_f64}))
- (local $2 (ref null ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
- (local $3 (ref null ${mut:f32}))
- (local $4 (ref null $[mut:f64]))
- (local $5 (ref null $[ref?|[mut:f64]|]))
+ (func $0 (param $x (ref null ${i32_f32_f64})) (result (ref null ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
+ (local $tA (ref null ${i32_f32_f64}))
+ (local $tB (ref null ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
+ (local $tc (ref null ${mut:f32}))
+ (local $tv (ref null $[mut:f64]))
+ (local $tm (ref null $[ref?|[mut:f64]|]))
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 0
+ (local.get $x)
+ )
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 1
+ (local.get $x)
+ )
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 2
+ (local.get $x)
+ )
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 2
+ (local.get $x)
+ )
+ )
+ (drop
+ (struct.get_u ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} 0
+ (local.get $tB)
+ )
+ )
+ (drop
+ (struct.get_s ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} 0
+ (local.get $tB)
+ )
+ )
+ (drop
+ (ref.null ${i32_f32_f64})
+ )
+ (drop
+ (block $block (result (ref null ${i32_f32_f64}))
+ (local.get $x)
+ )
+ )
+ (drop
+ (if (result (ref null ${i32_f32_f64}))
+ (i32.const 1)
+ (local.get $x)
+ (local.get $x)
+ )
+ )
+ (drop
+ (loop $loop-in (result (ref null ${i32_f32_f64}))
+ (local.get $x)
+ )
+ )
+ (drop
+ (select (result (ref null ${i32_f32_f64}))
+ (local.get $x)
+ (local.get $x)
+ (i32.const 1)
+ )
+ )
(unreachable)
)
)
diff --git a/test/heap-types.wast.fromBinary b/test/heap-types.wast.fromBinary
index e83d6bbb4..b6f6eb880 100644
--- a/test/heap-types.wast.fromBinary
+++ b/test/heap-types.wast.fromBinary
@@ -2,16 +2,76 @@
(type ${i32_f32_f64} (struct (field i32) (field f32) (field f64)))
(type $[mut:f64] (array (mut f64)))
(type ${mut:f32} (struct (field (mut f32))))
- (type ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} (struct (field (mut i64)) (field (ref null ${i32_f32_f64})) (field (mut (ref null ${i32_f32_f64})))))
+ (type ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} (struct (field i8) (field (mut i16)) (field (ref null ${i32_f32_f64})) (field (mut (ref null ${i32_f32_f64})))))
(type $[ref?|[mut:f64]|] (array (ref null $[mut:f64])))
- (type $ref?|{i32_f32_f64}|_=>_ref?|{mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}| (func (param (ref null ${i32_f32_f64})) (result (ref null ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))))
+ (type $ref?|{i32_f32_f64}|_=>_ref?|{i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}| (func (param (ref null ${i32_f32_f64})) (result (ref null ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))))
(export "foo" (func $0))
- (func $0 (param $x (ref null ${i32_f32_f64})) (result (ref null ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
- (local $1 (ref null ${i32_f32_f64}))
- (local $2 (ref null ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
- (local $3 (ref null ${mut:f32}))
- (local $4 (ref null $[ref?|[mut:f64]|]))
- (local $5 (ref null $[mut:f64]))
+ (func $0 (param $x (ref null ${i32_f32_f64})) (result (ref null ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
+ (local $tA (ref null ${i32_f32_f64}))
+ (local $tB (ref null ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
+ (local $tc (ref null ${mut:f32}))
+ (local $tv (ref null $[ref?|[mut:f64]|]))
+ (local $tm (ref null $[mut:f64]))
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 0
+ (local.get $x)
+ )
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 1
+ (local.get $x)
+ )
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 2
+ (local.get $x)
+ )
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 2
+ (local.get $x)
+ )
+ )
+ (drop
+ (struct.get_u ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} 0
+ (local.get $tB)
+ )
+ )
+ (drop
+ (struct.get_s ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} 0
+ (local.get $tB)
+ )
+ )
+ (drop
+ (ref.null ${i32_f32_f64})
+ )
+ (drop
+ (block $label$1 (result (ref null ${i32_f32_f64}))
+ (local.get $x)
+ )
+ )
+ (drop
+ (if (result (ref null ${i32_f32_f64}))
+ (i32.const 1)
+ (local.get $x)
+ (local.get $x)
+ )
+ )
+ (drop
+ (loop $label$4 (result (ref null ${i32_f32_f64}))
+ (local.get $x)
+ )
+ )
+ (drop
+ (select (result (ref null ${i32_f32_f64}))
+ (local.get $x)
+ (local.get $x)
+ (i32.const 1)
+ )
+ )
(unreachable)
)
)
diff --git a/test/heap-types.wast.fromBinary.noDebugInfo b/test/heap-types.wast.fromBinary.noDebugInfo
index b19152b8e..9e545d737 100644
--- a/test/heap-types.wast.fromBinary.noDebugInfo
+++ b/test/heap-types.wast.fromBinary.noDebugInfo
@@ -2,16 +2,76 @@
(type ${i32_f32_f64} (struct (field i32) (field f32) (field f64)))
(type $[mut:f64] (array (mut f64)))
(type ${mut:f32} (struct (field (mut f32))))
- (type ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} (struct (field (mut i64)) (field (ref null ${i32_f32_f64})) (field (mut (ref null ${i32_f32_f64})))))
+ (type ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} (struct (field i8) (field (mut i16)) (field (ref null ${i32_f32_f64})) (field (mut (ref null ${i32_f32_f64})))))
(type $[ref?|[mut:f64]|] (array (ref null $[mut:f64])))
- (type $ref?|{i32_f32_f64}|_=>_ref?|{mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}| (func (param (ref null ${i32_f32_f64})) (result (ref null ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))))
+ (type $ref?|{i32_f32_f64}|_=>_ref?|{i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}| (func (param (ref null ${i32_f32_f64})) (result (ref null ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))))
(export "foo" (func $0))
- (func $0 (param $0 (ref null ${i32_f32_f64})) (result (ref null ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
+ (func $0 (param $0 (ref null ${i32_f32_f64})) (result (ref null ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
(local $1 (ref null ${i32_f32_f64}))
- (local $2 (ref null ${mut:i64_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
+ (local $2 (ref null ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}))
(local $3 (ref null ${mut:f32}))
(local $4 (ref null $[ref?|[mut:f64]|]))
(local $5 (ref null $[mut:f64]))
+ (drop
+ (local.get $0)
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 0
+ (local.get $0)
+ )
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 1
+ (local.get $0)
+ )
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 2
+ (local.get $0)
+ )
+ )
+ (drop
+ (struct.get ${i32_f32_f64} 2
+ (local.get $0)
+ )
+ )
+ (drop
+ (struct.get_u ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} 0
+ (local.get $2)
+ )
+ )
+ (drop
+ (struct.get_s ${i32_mut:i32_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|} 0
+ (local.get $2)
+ )
+ )
+ (drop
+ (ref.null ${i32_f32_f64})
+ )
+ (drop
+ (block $label$1 (result (ref null ${i32_f32_f64}))
+ (local.get $0)
+ )
+ )
+ (drop
+ (if (result (ref null ${i32_f32_f64}))
+ (i32.const 1)
+ (local.get $0)
+ (local.get $0)
+ )
+ )
+ (drop
+ (loop $label$4 (result (ref null ${i32_f32_f64}))
+ (local.get $0)
+ )
+ )
+ (drop
+ (select (result (ref null ${i32_f32_f64}))
+ (local.get $0)
+ (local.get $0)
+ (i32.const 1)
+ )
+ )
(unreachable)
)
)
diff --git a/test/passes/O_all-features.txt b/test/passes/O_all-features.txt
new file mode 100644
index 000000000..761c4aeed
--- /dev/null
+++ b/test/passes/O_all-features.txt
@@ -0,0 +1,12 @@
+(module
+ (type ${i32} (struct (field i32)))
+ (type $ref?|{i32}|_=>_none (func (param (ref null ${i32}))))
+ (export "foo" (func $0))
+ (func $0 (; has Stack IR ;) (param $0 (ref null ${i32}))
+ (drop
+ (struct.get ${i32} 0
+ (local.get $0)
+ )
+ )
+ )
+)
diff --git a/test/passes/O_all-features.wast b/test/passes/O_all-features.wast
new file mode 100644
index 000000000..a8723b523
--- /dev/null
+++ b/test/passes/O_all-features.wast
@@ -0,0 +1,16 @@
+;; Test that we can run GC types through the optimizer
+(module
+ (type $struct.A (struct i32))
+
+ (func "foo" (param $x (ref null $struct.A))
+ ;; get a struct reference
+ (drop
+ (local.get $x)
+ )
+ ;; get a struct field value
+ ;; (note that since this is a nullable reference, it may trap)
+ (drop
+ (struct.get $struct.A 0 (local.get $x))
+ )
+ )
+)
diff --git a/test/passes/O_all-features_ignore-implicit-traps.txt b/test/passes/O_all-features_ignore-implicit-traps.txt
new file mode 100644
index 000000000..8e57a2e4b
--- /dev/null
+++ b/test/passes/O_all-features_ignore-implicit-traps.txt
@@ -0,0 +1,8 @@
+(module
+ (type ${i32} (struct (field i32)))
+ (type $ref?|{i32}|_=>_none (func (param (ref null ${i32}))))
+ (export "foo" (func $0))
+ (func $0 (; has Stack IR ;) (param $0 (ref null ${i32}))
+ (nop)
+ )
+)
diff --git a/test/passes/O_all-features_ignore-implicit-traps.wast b/test/passes/O_all-features_ignore-implicit-traps.wast
new file mode 100644
index 000000000..028a1d691
--- /dev/null
+++ b/test/passes/O_all-features_ignore-implicit-traps.wast
@@ -0,0 +1,17 @@
+;; Test that we can run GC types through the optimizer
+(module
+ (type $struct.A (struct i32))
+
+ (func "foo" (param $x (ref null $struct.A))
+ ;; get a struct reference
+ (drop
+ (local.get $x)
+ )
+ ;; get a struct field value
+ ;; (note that since this is a nullable reference, it may trap, but we
+ ;; are ignoring implicit traps, so it has no side effects)
+ (drop
+ (struct.get $struct.A 0 (local.get $x))
+ )
+ )
+)
diff --git a/test/passes/inlining_all-features.txt b/test/passes/inlining_all-features.txt
index 85b5a03b8..d16d60e7c 100644
--- a/test/passes/inlining_all-features.txt
+++ b/test/passes/inlining_all-features.txt
@@ -78,7 +78,7 @@
(block $__inlined_func$0
(block
(call_ref
- (ref.null (func))
+ (ref.null $none_=>_none)
)
(br $__inlined_func$0)
)
diff --git a/test/passes/inlining_all-features.wast b/test/passes/inlining_all-features.wast
index 187673b5c..ae9168988 100644
--- a/test/passes/inlining_all-features.wast
+++ b/test/passes/inlining_all-features.wast
@@ -64,7 +64,7 @@
(export "func_36_invoker" (func $1))
(func $0
(return_call_ref
- (ref.null (func))
+ (ref.null $none_=>_none)
)
)
(func $1
diff --git a/test/typed-function-references.wast b/test/typed-function-references.wast
index 47fd789d0..45120192d 100644
--- a/test/typed-function-references.wast
+++ b/test/typed-function-references.wast
@@ -1,6 +1,7 @@
(module
;; inline ref type in result
(type $f64_=>_ref_null<_->_eqref> (func (param f64) (result (ref null (func (result eqref))))))
+ (type $=>eqref (func (result eqref)))
(type $i32-i32 (func (param i32) (result i32)))
@@ -25,8 +26,7 @@
(call_ref (i32.const 42) (local.get $f))
)
(func $ref-in-sig (param $0 f64) (result (ref null (func (result eqref))))
- ;; ref.null of an inline type
- (ref.null (func (result eqref)))
+ (ref.null $=>eqref)
)
(func $type-only-in-tuple-local
(local $x (i32 (ref null (func (result anyref))) f64))
diff --git a/test/typed-function-references.wast.from-wast b/test/typed-function-references.wast.from-wast
index e7467b6cd..f287bdb7c 100644
--- a/test/typed-function-references.wast.from-wast
+++ b/test/typed-function-references.wast.from-wast
@@ -50,7 +50,7 @@
)
)
(func $ref-in-sig (param $0 f64) (result (ref null $none_=>_eqref))
- (ref.null (func (result eqref)))
+ (ref.null $none_=>_eqref)
)
(func $type-only-in-tuple-local
(local $x (i32 (ref null (func (result anyref))) f64))
@@ -58,7 +58,7 @@
)
(func $type-only-in-tuple-block
(drop
- (block $block (result i32 (ref null (func (result anyref f32 anyref f32))) f64)
+ (block $block (result i32 (ref null $none_=>_anyref_f32_anyref_f32) f64)
(unreachable)
)
)
diff --git a/test/typed-function-references.wast.fromBinary b/test/typed-function-references.wast.fromBinary
index e05e97757..1e3b8b64c 100644
--- a/test/typed-function-references.wast.fromBinary
+++ b/test/typed-function-references.wast.fromBinary
@@ -50,7 +50,7 @@
)
)
(func $ref-in-sig (param $0 f64) (result (ref null $none_=>_eqref))
- (ref.null (func (result eqref)))
+ (ref.null $none_=>_eqref)
)
(func $type-only-in-tuple-local
(local $x i32)
@@ -63,7 +63,7 @@
(local $1 (ref null $none_=>_anyref_f32_anyref_f32))
(local $2 i32)
(local.set $0
- (block $label$1 (result i32 (ref null (func (result anyref f32 anyref f32))) f64)
+ (block $label$1 (result i32 (ref null $none_=>_anyref_f32_anyref_f32) f64)
(unreachable)
)
)
@@ -75,7 +75,7 @@
)
)
(drop
- (block (result (ref null (func (result anyref f32 anyref f32))))
+ (block (result (ref null $none_=>_anyref_f32_anyref_f32))
(local.set $1
(tuple.extract 1
(local.get $0)
diff --git a/test/typed-function-references.wast.fromBinary.noDebugInfo b/test/typed-function-references.wast.fromBinary.noDebugInfo
index 0e2b6ca3d..a9d95b8d5 100644
--- a/test/typed-function-references.wast.fromBinary.noDebugInfo
+++ b/test/typed-function-references.wast.fromBinary.noDebugInfo
@@ -50,7 +50,7 @@
)
)
(func $6 (param $0 f64) (result (ref null $none_=>_eqref))
- (ref.null (func (result eqref)))
+ (ref.null $none_=>_eqref)
)
(func $7
(local $0 i32)
@@ -63,7 +63,7 @@
(local $1 (ref null $none_=>_anyref_f32_anyref_f32))
(local $2 i32)
(local.set $0
- (block $label$1 (result i32 (ref null (func (result anyref f32 anyref f32))) f64)
+ (block $label$1 (result i32 (ref null $none_=>_anyref_f32_anyref_f32) f64)
(unreachable)
)
)
@@ -75,7 +75,7 @@
)
)
(drop
- (block (result (ref null (func (result anyref f32 anyref f32))))
+ (block (result (ref null $none_=>_anyref_f32_anyref_f32))
(local.set $1
(tuple.extract 1
(local.get $0)