summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/cost.h8
-rw-r--r--src/ir/effects.h7
-rw-r--r--src/passes/Print.cpp22
-rw-r--r--src/wasm-builder.h10
-rw-r--r--src/wasm-delegations-fields.h6
-rw-r--r--src/wasm-type.h2
-rw-r--r--src/wasm.h10
-rw-r--r--src/wasm/wasm-binary.cpp22
-rw-r--r--src/wasm/wasm-s-parser.cpp18
-rw-r--r--src/wasm/wasm-stack.cpp6
-rw-r--r--src/wasm/wasm-validator.cpp18
-rw-r--r--src/wasm/wasm.cpp19
-rw-r--r--test/heap-types.wast8
-rw-r--r--test/heap-types.wast.from-wast15
-rw-r--r--test/heap-types.wast.fromBinary15
-rw-r--r--test/heap-types.wast.fromBinary.noDebugInfo15
16 files changed, 156 insertions, 45 deletions
diff --git a/src/ir/cost.h b/src/ir/cost.h
index b71e1ea0e..66a316bf6 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -560,8 +560,12 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> {
Index visitDataDrop(DataDrop* curr) { return 5; }
Index visitI31New(I31New* curr) { return 3 + visit(curr->value); }
Index visitI31Get(I31Get* curr) { return 2 + visit(curr->i31); }
- Index visitRefTest(RefTest* curr) { WASM_UNREACHABLE("TODO: GC"); }
- Index visitRefCast(RefCast* curr) { WASM_UNREACHABLE("TODO: GC"); }
+ Index visitRefTest(RefTest* curr) {
+ return 2 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->rtt);
+ }
+ Index visitRefCast(RefCast* curr) {
+ return 2 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->rtt);
+ }
Index visitBrOnCast(BrOnCast* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitRttCanon(RttCanon* curr) {
// TODO: investigate actual RTT costs in VMs
diff --git a/src/ir/effects.h b/src/ir/effects.h
index 94387c2cf..3cbddc491 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -545,11 +545,10 @@ private:
// traps when the arg is null
parent.implicitTrap = true;
}
- void visitRefTest(RefTest* curr) {
- WASM_UNREACHABLE("TODO (gc): ref.test");
- }
+ void visitRefTest(RefTest* curr) {}
void visitRefCast(RefCast* curr) {
- WASM_UNREACHABLE("TODO (gc): ref.cast");
+ // Traps if the ref is not null and it has an invalid rtt.
+ parent.implicitTrap = true;
}
void visitBrOnCast(BrOnCast* curr) {
WASM_UNREACHABLE("TODO (gc): br_on_cast");
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 694b1b118..4007b6daa 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -1692,12 +1692,12 @@ struct PrintExpressionContents
}
}
void visitRefTest(RefTest* curr) {
- printMedium(o, "ref.test");
- WASM_UNREACHABLE("TODO (gc): ref.test");
+ printMedium(o, "ref.test ");
+ printHeapTypeName(o, curr->rtt->type.getHeapType());
}
void visitRefCast(RefCast* curr) {
- printMedium(o, "ref.cast");
- WASM_UNREACHABLE("TODO (gc): ref.cast");
+ printMedium(o, "ref.cast ");
+ printHeapTypeName(o, curr->rtt->type.getHeapType());
}
void visitBrOnCast(BrOnCast* curr) {
printMedium(o, "br_on_cast");
@@ -1717,7 +1717,7 @@ struct PrintExpressionContents
o << "default_";
}
o << "with_rtt ";
- printHeapTypeName(o, curr->rtt->type.getRtt().heapType);
+ printHeapTypeName(o, curr->rtt->type.getHeapType());
}
void visitStructGet(StructGet* curr) {
const auto& field =
@@ -1747,7 +1747,7 @@ struct PrintExpressionContents
o << "default_";
}
o << "with_rtt ";
- printHeapTypeName(o, curr->rtt->type.getRtt().heapType);
+ printHeapTypeName(o, curr->rtt->type.getHeapType());
}
void visitArrayGet(ArrayGet* curr) {
const auto& element = curr->ref->type.getHeapType().getArray().element;
@@ -2394,12 +2394,18 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
void visitRefTest(RefTest* curr) {
o << '(';
PrintExpressionContents(currFunction, o).visit(curr);
- WASM_UNREACHABLE("TODO (gc): ref.test");
+ incIndent();
+ printFullLine(curr->ref);
+ printFullLine(curr->rtt);
+ decIndent();
}
void visitRefCast(RefCast* curr) {
o << '(';
PrintExpressionContents(currFunction, o).visit(curr);
- WASM_UNREACHABLE("TODO (gc): ref.cast");
+ incIndent();
+ printFullLine(curr->ref);
+ printFullLine(curr->rtt);
+ decIndent();
}
void visitBrOnCast(BrOnCast* curr) {
o << '(';
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 73c8f9823..59c8d4c1b 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -691,15 +691,17 @@ public:
ret->finalize();
return ret;
}
- RefTest* makeRefTest() {
+ RefTest* makeRefTest(Expression* ref, Expression* rtt) {
auto* ret = wasm.allocator.alloc<RefTest>();
- WASM_UNREACHABLE("TODO (gc): ref.test");
+ ret->ref = ref;
+ ret->rtt = rtt;
ret->finalize();
return ret;
}
- RefCast* makeRefCast() {
+ RefCast* makeRefCast(Expression* ref, Expression* rtt) {
auto* ret = wasm.allocator.alloc<RefCast>();
- WASM_UNREACHABLE("TODO (gc): ref.cast");
+ ret->ref = ref;
+ ret->rtt = rtt;
ret->finalize();
return ret;
}
diff --git a/src/wasm-delegations-fields.h b/src/wasm-delegations-fields.h
index b90bf720b..d53af695f 100644
--- a/src/wasm-delegations-fields.h
+++ b/src/wasm-delegations-fields.h
@@ -559,13 +559,15 @@ switch (DELEGATE_ID) {
}
case Expression::Id::RefTestId: {
DELEGATE_START(RefTest);
- WASM_UNREACHABLE("TODO (gc): ref.test");
+ DELEGATE_FIELD_CHILD(RefTest, ref);
+ DELEGATE_FIELD_CHILD(RefTest, rtt);
DELEGATE_END(RefTest);
break;
}
case Expression::Id::RefCastId: {
DELEGATE_START(RefCast);
- WASM_UNREACHABLE("TODO (gc): ref.cast");
+ DELEGATE_FIELD_CHILD(RefCast, ref);
+ DELEGATE_FIELD_CHILD(RefCast, rtt);
DELEGATE_END(RefCast);
break;
}
diff --git a/src/wasm-type.h b/src/wasm-type.h
index a76e30b42..0df515f79 100644
--- a/src/wasm-type.h
+++ b/src/wasm-type.h
@@ -352,7 +352,7 @@ struct Tuple {
std::string toString() const;
// Prevent accidental copies
- Struct& operator=(const Struct&) = delete;
+ Tuple& operator=(const Tuple&) = delete;
private:
void validate() {
diff --git a/src/wasm.h b/src/wasm.h
index 0cc2f430c..0c6566407 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1310,14 +1310,20 @@ class RefTest : public SpecificExpression<Expression::RefTestId> {
public:
RefTest(MixedArena& allocator) {}
- void finalize() { WASM_UNREACHABLE("TODO (gc): ref.test"); }
+ Expression* ref;
+ Expression* rtt;
+
+ void finalize();
};
class RefCast : public SpecificExpression<Expression::RefCastId> {
public:
RefCast(MixedArena& allocator) {}
- void finalize() { WASM_UNREACHABLE("TODO (gc): ref.cast"); }
+ Expression* ref;
+ Expression* rtt;
+
+ void finalize();
};
class BrOnCast : public SpecificExpression<Expression::BrOnCastId> {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 0c68264a3..1b3d50a41 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -5599,10 +5599,13 @@ bool WasmBinaryBuilder::maybeVisitRefTest(Expression*& out, uint32_t code) {
if (code != BinaryConsts::RefTest) {
return false;
}
- auto* curr = allocator.alloc<RefTest>();
- WASM_UNREACHABLE("TODO (gc): ref.test");
- curr->finalize();
- out = curr;
+ auto heapType1 = getHeapType();
+ auto heapType2 = getHeapType();
+ auto* ref = popNonVoidExpression();
+ validateHeapTypeUsingChild(ref, heapType1);
+ auto* rtt = popNonVoidExpression();
+ validateHeapTypeUsingChild(rtt, heapType2);
+ out = Builder(wasm).makeRefTest(ref, rtt);
return true;
}
@@ -5610,10 +5613,13 @@ bool WasmBinaryBuilder::maybeVisitRefCast(Expression*& out, uint32_t code) {
if (code != BinaryConsts::RefCast) {
return false;
}
- auto* curr = allocator.alloc<RefCast>();
- WASM_UNREACHABLE("TODO (gc): ref.cast");
- curr->finalize();
- out = curr;
+ auto heapType1 = getHeapType();
+ auto heapType2 = getHeapType();
+ auto* ref = popNonVoidExpression();
+ validateHeapTypeUsingChild(ref, heapType1);
+ auto* rtt = popNonVoidExpression();
+ validateHeapTypeUsingChild(rtt, heapType2);
+ out = Builder(wasm).makeRefCast(ref, rtt);
return true;
}
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index b9f321b14..85bc2aba9 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2093,17 +2093,19 @@ Expression* SExpressionWasmBuilder::makeI31Get(Element& s, bool signed_) {
}
Expression* SExpressionWasmBuilder::makeRefTest(Element& s) {
- auto ret = allocator.alloc<RefTest>();
- WASM_UNREACHABLE("TODO (gc): ref.test");
- ret->finalize();
- return ret;
+ auto heapType = parseHeapType(*s[1]);
+ auto* ref = parseExpression(*s[2]);
+ auto* rtt = parseExpression(*s[3]);
+ validateHeapTypeUsingChild(rtt, heapType, s);
+ return Builder(wasm).makeRefTest(ref, rtt);
}
Expression* SExpressionWasmBuilder::makeRefCast(Element& s) {
- auto ret = allocator.alloc<RefCast>();
- WASM_UNREACHABLE("TODO (gc): ref.cast");
- ret->finalize();
- return ret;
+ auto heapType = parseHeapType(*s[1]);
+ auto* ref = parseExpression(*s[2]);
+ auto* rtt = parseExpression(*s[3]);
+ validateHeapTypeUsingChild(rtt, heapType, s);
+ return Builder(wasm).makeRefCast(ref, rtt);
}
Expression* SExpressionWasmBuilder::makeBrOnCast(Element& s) {
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 60c352c5d..741aef5ce 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -1882,12 +1882,14 @@ void BinaryInstWriter::visitCallRef(CallRef* curr) {
void BinaryInstWriter::visitRefTest(RefTest* curr) {
o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::RefTest);
- WASM_UNREACHABLE("TODO (gc): ref.test");
+ parent.writeHeapType(curr->ref->type.getHeapType());
+ parent.writeHeapType(curr->rtt->type.getHeapType());
}
void BinaryInstWriter::visitRefCast(RefCast* curr) {
o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::RefCast);
- WASM_UNREACHABLE("TODO (gc): ref.cast");
+ parent.writeHeapType(curr->ref->type.getHeapType());
+ parent.writeHeapType(curr->rtt->type.getHeapType());
}
void BinaryInstWriter::visitBrOnCast(BrOnCast* curr) {
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index cf92cf4d3..56cf9c114 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -2192,13 +2192,27 @@ void FunctionValidator::visitI31Get(I31Get* curr) {
void FunctionValidator::visitRefTest(RefTest* curr) {
shouldBeTrue(
getModule()->features.hasGC(), curr, "ref.test requires gc to be enabled");
- WASM_UNREACHABLE("TODO (gc): ref.test");
+ if (curr->ref->type != Type::unreachable) {
+ shouldBeTrue(
+ curr->ref->type.isRef(), curr, "ref.test ref must have ref type");
+ }
+ if (curr->rtt->type != Type::unreachable) {
+ shouldBeTrue(
+ curr->rtt->type.isRtt(), curr, "ref.test rtt must have rtt type");
+ }
}
void FunctionValidator::visitRefCast(RefCast* curr) {
shouldBeTrue(
getModule()->features.hasGC(), curr, "ref.cast requires gc to be enabled");
- WASM_UNREACHABLE("TODO (gc): ref.cast");
+ if (curr->ref->type != Type::unreachable) {
+ shouldBeTrue(
+ curr->ref->type.isRef(), curr, "ref.test ref must have ref type");
+ }
+ if (curr->rtt->type != Type::unreachable) {
+ shouldBeTrue(
+ curr->rtt->type.isRtt(), curr, "ref.test rtt must have rtt type");
+ }
}
void FunctionValidator::visitBrOnCast(BrOnCast* curr) {
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 2eed06981..93a14aecc 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -1080,8 +1080,23 @@ void CallRef::finalize(Type type_) {
finalize();
}
-// TODO (gc): ref.test
-// TODO (gc): ref.cast
+void RefTest::finalize() {
+ if (ref->type == Type::unreachable || rtt->type == Type::unreachable) {
+ type = Type::unreachable;
+ } else {
+ type = Type::i32;
+ }
+}
+
+void RefCast::finalize() {
+ if (ref->type == Type::unreachable || rtt->type == Type::unreachable) {
+ type = Type::unreachable;
+ } else {
+ // TODO: make non-nullable when we support that
+ type = Type(rtt->type.getHeapType(), /* nullable = */ true);
+ }
+}
+
// TODO (gc): br_on_cast
void RttCanon::finalize() {
diff --git a/test/heap-types.wast b/test/heap-types.wast
index 1162bee16..981b3b257 100644
--- a/test/heap-types.wast
+++ b/test/heap-types.wast
@@ -160,4 +160,12 @@
;; RTT types as parameters
(func $rtt-param-with-depth (param $rtt (rtt 1 $parent)))
(func $rtt-param-without-depth (param $rtt (rtt $parent)))
+ (func $rtt-operations
+ (drop
+ (ref.test $struct.B (ref.null $struct.A) (rtt.canon $struct.B))
+ )
+ (drop
+ (ref.cast $struct.B (ref.null $struct.A) (rtt.canon $struct.B))
+ )
+ )
)
diff --git a/test/heap-types.wast.from-wast b/test/heap-types.wast.from-wast
index f3ba97d99..824e1924b 100644
--- a/test/heap-types.wast.from-wast
+++ b/test/heap-types.wast.from-wast
@@ -2,6 +2,7 @@
(type ${i32_f32_f64} (struct (field i32) (field f32) (field f64)))
(type $[mut:f64] (array (mut f64)))
(type ${} (struct ))
+ (type $none_=>_none (func))
(type ${i32} (struct (field i32)))
(type ${i32_i64} (struct (field i32) (field i64)))
(type ${mut:f32} (struct (field (mut f32))))
@@ -165,4 +166,18 @@
(func $rtt-param-without-depth (param $rtt (rtt ${}))
(nop)
)
+ (func $rtt-operations
+ (drop
+ (ref.test ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}
+ (ref.null ${i32_f32_f64})
+ (rtt.canon ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|})
+ )
+ )
+ (drop
+ (ref.cast ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}
+ (ref.null ${i32_f32_f64})
+ (rtt.canon ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|})
+ )
+ )
+ )
)
diff --git a/test/heap-types.wast.fromBinary b/test/heap-types.wast.fromBinary
index 7917d95e0..3e010d25c 100644
--- a/test/heap-types.wast.fromBinary
+++ b/test/heap-types.wast.fromBinary
@@ -2,6 +2,7 @@
(type ${i32_f32_f64} (struct (field i32) (field f32) (field f64)))
(type $[mut:f64] (array (mut f64)))
(type ${} (struct ))
+ (type $none_=>_none (func))
(type ${i32} (struct (field i32)))
(type ${i32_i64} (struct (field i32) (field i64)))
(type ${mut:f32} (struct (field (mut f32))))
@@ -165,5 +166,19 @@
(func $rtt-param-without-depth (param $rtt (rtt ${}))
(nop)
)
+ (func $rtt-operations
+ (drop
+ (ref.test ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}
+ (ref.null ${i32_f32_f64})
+ (rtt.canon ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|})
+ )
+ )
+ (drop
+ (ref.cast ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}
+ (ref.null ${i32_f32_f64})
+ (rtt.canon ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|})
+ )
+ )
+ )
)
diff --git a/test/heap-types.wast.fromBinary.noDebugInfo b/test/heap-types.wast.fromBinary.noDebugInfo
index 5fa0352c6..9d448f8fb 100644
--- a/test/heap-types.wast.fromBinary.noDebugInfo
+++ b/test/heap-types.wast.fromBinary.noDebugInfo
@@ -2,6 +2,7 @@
(type ${i32_f32_f64} (struct (field i32) (field f32) (field f64)))
(type $[mut:f64] (array (mut f64)))
(type ${} (struct ))
+ (type $none_=>_none (func))
(type ${i32} (struct (field i32)))
(type ${i32_i64} (struct (field i32) (field i64)))
(type ${mut:f32} (struct (field (mut f32))))
@@ -165,5 +166,19 @@
(func $3 (param $0 (rtt ${}))
(nop)
)
+ (func $4
+ (drop
+ (ref.test ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}
+ (ref.null ${i32_f32_f64})
+ (rtt.canon ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|})
+ )
+ )
+ (drop
+ (ref.cast ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|}
+ (ref.null ${i32_f32_f64})
+ (rtt.canon ${i8_mut:i16_ref?|{i32_f32_f64}|_mut:ref?|{i32_f32_f64}|})
+ )
+ )
+ )
)