summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ir/ReFinalize.cpp6
-rw-r--r--src/ir/branch-utils.h2
-rw-r--r--src/ir/cost.h4
-rw-r--r--src/ir/effects.h2
-rw-r--r--src/passes/Precompute.cpp43
-rw-r--r--src/passes/Print.cpp15
-rw-r--r--src/wasm-builder.h8
-rw-r--r--src/wasm-delegations-fields.h5
-rw-r--r--src/wasm-interpreter.h11
-rw-r--r--src/wasm.h15
-rw-r--r--src/wasm/wasm-binary.cpp12
-rw-r--r--src/wasm/wasm-s-parser.cpp10
-rw-r--r--src/wasm/wasm-stack.cpp10
-rw-r--r--src/wasm/wasm-validator.cpp24
-rw-r--r--src/wasm/wasm.cpp29
15 files changed, 154 insertions, 42 deletions
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp
index 54dde65bc..b3639ba00 100644
--- a/src/ir/ReFinalize.cpp
+++ b/src/ir/ReFinalize.cpp
@@ -155,7 +155,11 @@ void ReFinalize::visitRefTest(RefTest* curr) { curr->finalize(); }
void ReFinalize::visitRefCast(RefCast* curr) { curr->finalize(); }
void ReFinalize::visitBrOnCast(BrOnCast* curr) {
curr->finalize();
- WASM_UNREACHABLE("TODO (gc): br_on_cast");
+ if (curr->type == Type::unreachable) {
+ replaceUntaken(curr->ref, nullptr);
+ } else {
+ updateBreakValueType(curr->name, curr->getCastType());
+ }
}
void ReFinalize::visitRttCanon(RttCanon* curr) { curr->finalize(); }
void ReFinalize::visitRttSub(RttSub* curr) { curr->finalize(); }
diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h
index 6dfe216a2..d7a6edcae 100644
--- a/src/ir/branch-utils.h
+++ b/src/ir/branch-utils.h
@@ -81,6 +81,8 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) {
func(name, sw->value ? sw->value->type : Type::none);
} else if (auto* br = expr->dynCast<BrOnExn>()) {
func(name, br->sent);
+ } else if (auto* br = expr->dynCast<BrOnCast>()) {
+ func(name, br->getCastType());
} else {
WASM_UNREACHABLE("bad br type");
}
diff --git a/src/ir/cost.h b/src/ir/cost.h
index c5b77ea01..c218cbaab 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -576,7 +576,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> {
Index visitRefCast(RefCast* curr) {
return 2 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->rtt);
}
- Index visitBrOnCast(BrOnCast* curr) { WASM_UNREACHABLE("TODO: GC"); }
+ Index visitBrOnCast(BrOnCast* curr) {
+ return 3 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->rtt);
+ }
Index visitRttCanon(RttCanon* curr) {
// TODO: investigate actual RTT costs in VMs
return 1;
diff --git a/src/ir/effects.h b/src/ir/effects.h
index 3cbddc491..5483242b5 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -551,7 +551,7 @@ private:
parent.implicitTrap = true;
}
void visitBrOnCast(BrOnCast* curr) {
- WASM_UNREACHABLE("TODO (gc): br_on_cast");
+ parent.breakTargets.insert(curr->name);
}
void visitRttCanon(RttCanon* curr) {}
void visitRttSub(RttSub* curr) {}
diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp
index dceb45fb2..ae488a01f 100644
--- a/src/passes/Precompute.cpp
+++ b/src/passes/Precompute.cpp
@@ -177,6 +177,9 @@ struct Precompute
if (flow.breakTo == NONCONSTANT_FLOW) {
return;
}
+ if (!canEmitConstantFor(flow.values)) {
+ return;
+ }
if (flow.breakTo == RETURN_FLOW) {
// this expression causes a return. if it's already a return, reuse the
// node
@@ -223,14 +226,7 @@ private:
// Precompute an expression, returning a flow, which may be a constant
// (that we can replace the expression with if replaceExpression is set).
Flow precomputeExpression(Expression* curr, bool replaceExpression = true) {
- // Don't try to precompute a reference. We can't replace it with a constant
- // expression, as that would make a copy of it by value.
- // TODO: do so when safe
- if (curr->type.isRef()) {
- return Flow(NONCONSTANT_FLOW);
- }
- // Don't try to precompute an Rtt. TODO figure out when that would be safe
- if (curr->type.isRtt()) {
+ if (!canEmitConstantFor(curr->type)) {
return Flow(NONCONSTANT_FLOW);
}
try {
@@ -348,6 +344,37 @@ private:
}
}
}
+
+ bool canEmitConstantFor(const Literals& values) {
+ for (auto& value : values) {
+ if (!canEmitConstantFor(value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool canEmitConstantFor(const Literal& value) {
+ // A null is fine to emit a constant for - we'll emit a RefNull. Otherwise,
+ // see below about references to GC data.
+ if (value.isNull()) {
+ return true;
+ }
+ // A function is fine to emit a constant for - we'll emit a RefFunc, which
+ // is compact and immutable, so there can't be a problem.
+ if (value.type.isFunction()) {
+ return true;
+ }
+ return canEmitConstantFor(value.type);
+ }
+
+ bool canEmitConstantFor(Type type) {
+ // Don't try to precompute a reference. We can't replace it with a constant
+ // expression, as that would make a copy of it by value.
+ // For now, don't try to precompute an Rtt. TODO figure out when that would
+ // be safe and useful.
+ return !type.isRef() && !type.isRtt();
+ }
};
Pass* createPrecomputePass() { return new Precompute(false); }
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index c0e940d04..b921fb074 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -1733,15 +1733,17 @@ struct PrintExpressionContents
}
void visitRefTest(RefTest* curr) {
printMedium(o, "ref.test ");
- printHeapTypeName(o, curr->rtt->type.getHeapType());
+ printHeapTypeName(o, curr->getCastType().getHeapType());
}
void visitRefCast(RefCast* curr) {
printMedium(o, "ref.cast ");
- printHeapTypeName(o, curr->rtt->type.getHeapType());
+ printHeapTypeName(o, curr->getCastType().getHeapType());
}
void visitBrOnCast(BrOnCast* curr) {
- printMedium(o, "br_on_cast");
- WASM_UNREACHABLE("TODO (gc): br_on_cast");
+ printMedium(o, "br_on_cast ");
+ printName(curr->name, o);
+ o << " ";
+ printHeapTypeName(o, curr->getCastType().getHeapType());
}
void visitRttCanon(RttCanon* curr) {
printMedium(o, "rtt.canon ");
@@ -2450,7 +2452,10 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
void visitBrOnCast(BrOnCast* curr) {
o << '(';
PrintExpressionContents(currFunction, o).visit(curr);
- WASM_UNREACHABLE("TODO (gc): br_on_cast");
+ incIndent();
+ printFullLine(curr->ref);
+ printFullLine(curr->rtt);
+ decIndent();
}
void visitRttCanon(RttCanon* curr) {
o << '(';
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 59c8d4c1b..1ae018e13 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -705,9 +705,13 @@ public:
ret->finalize();
return ret;
}
- BrOnCast* makeBrOnCast() {
+ BrOnCast*
+ makeBrOnCast(Name name, HeapType heapType, Expression* ref, Expression* rtt) {
auto* ret = wasm.allocator.alloc<BrOnCast>();
- WASM_UNREACHABLE("TODO (gc): br_on_cast");
+ ret->name = name;
+ ret->castType = Type(heapType, Nullable);
+ 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 4d32b5f76..ca8b0bbe6 100644
--- a/src/wasm-delegations-fields.h
+++ b/src/wasm-delegations-fields.h
@@ -573,7 +573,10 @@ switch (DELEGATE_ID) {
}
case Expression::Id::BrOnCastId: {
DELEGATE_START(BrOnCast);
- WASM_UNREACHABLE("TODO (gc): br_on_cast");
+ DELEGATE_FIELD_SCOPE_NAME_USE(BrOnCast, name);
+ DELEGATE_FIELD_TYPE(BrOnCast, castType);
+ DELEGATE_FIELD_CHILD(BrOnCast, ref);
+ DELEGATE_FIELD_CHILD(BrOnCast, rtt);
DELEGATE_END(BrOnCast);
break;
}
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 223d02d87..019a56db1 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -60,6 +60,7 @@ public:
Flow(Literals& values) : values(values) {}
Flow(Literals&& values) : values(std::move(values)) {}
Flow(Name breakTo) : values(), breakTo(breakTo) {}
+ Flow(Name breakTo, Literal value) : values{value}, breakTo(breakTo) {}
Literals values;
Name breakTo; // if non-null, a break is going on
@@ -1462,7 +1463,15 @@ public:
}
Flow visitBrOnCast(BrOnCast* curr) {
NOTE_ENTER("BrOnCast");
- WASM_UNREACHABLE("TODO (gc): br_on_cast");
+ auto cast = doCast(curr);
+ if (cast.outcome == cast.Break) {
+ return cast.breaking;
+ }
+ if (cast.outcome == cast.Null || cast.outcome == cast.Failure) {
+ return cast.originalRef;
+ }
+ assert(cast.outcome == cast.Success);
+ return Flow(curr->name, cast.castRef);
}
Flow visitRttCanon(RttCanon* curr) { return Literal(curr->type); }
Flow visitRttSub(RttSub* curr) {
diff --git a/src/wasm.h b/src/wasm.h
index 8cada039f..255f18a16 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1333,6 +1333,8 @@ public:
Expression* rtt;
void finalize();
+
+ Type getCastType();
};
class RefCast : public SpecificExpression<Expression::RefCastId> {
@@ -1343,13 +1345,24 @@ public:
Expression* rtt;
void finalize();
+
+ Type getCastType();
};
class BrOnCast : public SpecificExpression<Expression::BrOnCastId> {
public:
BrOnCast(MixedArena& allocator) {}
- void finalize() { WASM_UNREACHABLE("TODO (gc): br_on_cast"); }
+ Name name;
+ // The cast type cannot be inferred from rtt if rtt is unreachable, so we must
+ // store it explicitly.
+ Type castType;
+ Expression* ref;
+ Expression* rtt;
+
+ void finalize();
+
+ Type getCastType();
};
class RttCanon : public SpecificExpression<Expression::RttCanonId> {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index a4d652608..70aaa6a61 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -5678,10 +5678,14 @@ bool WasmBinaryBuilder::maybeVisitBrOnCast(Expression*& out, uint32_t code) {
if (code != BinaryConsts::BrOnCast) {
return false;
}
- auto* curr = allocator.alloc<BrOnCast>();
- WASM_UNREACHABLE("TODO (gc): br_on_cast");
- curr->finalize();
- out = curr;
+ auto name = getBreakTarget(getU32LEB()).name;
+ auto heapType1 = getHeapType();
+ auto heapType2 = getHeapType();
+ auto* ref = popNonVoidExpression();
+ validateHeapTypeUsingChild(ref, heapType1);
+ auto* rtt = popNonVoidExpression();
+ validateHeapTypeUsingChild(rtt, heapType2);
+ out = Builder(wasm).makeBrOnCast(name, heapType2, ref, rtt);
return true;
}
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index c1c227d21..32b62d0e1 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2108,10 +2108,12 @@ Expression* SExpressionWasmBuilder::makeRefCast(Element& s) {
}
Expression* SExpressionWasmBuilder::makeBrOnCast(Element& s) {
- auto ret = allocator.alloc<BrOnCast>();
- WASM_UNREACHABLE("TODO (gc): br_on_cast");
- ret->finalize();
- return ret;
+ auto name = getLabel(*s[1]);
+ auto heapType = parseHeapType(*s[2]);
+ auto* ref = parseExpression(*s[3]);
+ auto* rtt = parseExpression(*s[4]);
+ validateHeapTypeUsingChild(rtt, heapType, s);
+ return Builder(wasm).makeBrOnCast(name, heapType, ref, rtt);
}
Expression* SExpressionWasmBuilder::makeRttCanon(Element& s) {
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 3a24d0cc0..6af7279bb 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -1918,18 +1918,20 @@ void BinaryInstWriter::visitCallRef(CallRef* curr) {
void BinaryInstWriter::visitRefTest(RefTest* curr) {
o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::RefTest);
parent.writeHeapType(curr->ref->type.getHeapType());
- parent.writeHeapType(curr->rtt->type.getHeapType());
+ parent.writeHeapType(curr->getCastType().getHeapType());
}
void BinaryInstWriter::visitRefCast(RefCast* curr) {
o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::RefCast);
parent.writeHeapType(curr->ref->type.getHeapType());
- parent.writeHeapType(curr->rtt->type.getHeapType());
+ parent.writeHeapType(curr->getCastType().getHeapType());
}
void BinaryInstWriter::visitBrOnCast(BrOnCast* curr) {
- o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::BrOnCast);
- WASM_UNREACHABLE("TODO (gc): br_on_cast");
+ o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::BrOnCast)
+ << U32LEB(getBreakIndex(curr->name));
+ parent.writeHeapType(curr->ref->type.getHeapType());
+ parent.writeHeapType(curr->getCastType().getHeapType());
}
void BinaryInstWriter::visitRttCanon(RttCanon* curr) {
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index f110faf56..edcf3676d 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -2214,11 +2214,11 @@ void FunctionValidator::visitRefCast(RefCast* curr) {
getModule()->features.hasGC(), curr, "ref.cast requires gc to be enabled");
if (curr->ref->type != Type::unreachable) {
shouldBeTrue(
- curr->ref->type.isRef(), curr, "ref.test ref must have ref type");
+ curr->ref->type.isRef(), curr, "ref.cast ref must have ref type");
}
if (curr->rtt->type != Type::unreachable) {
shouldBeTrue(
- curr->rtt->type.isRtt(), curr, "ref.test rtt must have rtt type");
+ curr->rtt->type.isRtt(), curr, "ref.cast rtt must have rtt type");
}
}
@@ -2226,7 +2226,19 @@ void FunctionValidator::visitBrOnCast(BrOnCast* curr) {
shouldBeTrue(getModule()->features.hasGC(),
curr,
"br_on_cast requires gc to be enabled");
- WASM_UNREACHABLE("TODO (gc): br_on_cast");
+ if (curr->ref->type != Type::unreachable) {
+ shouldBeTrue(
+ curr->ref->type.isRef(), curr, "br_on_cast ref must have ref type");
+ }
+ if (curr->rtt->type != Type::unreachable) {
+ shouldBeTrue(
+ curr->rtt->type.isRtt(), curr, "br_on_cast rtt must have rtt type");
+ shouldBeEqual(curr->rtt->type.getHeapType(),
+ curr->castType.getHeapType(),
+ curr,
+ "br_on_cast rtt must have the proper heap type");
+ noteBreak(curr->name, Type(curr->rtt->type.getHeapType(), Nullable), curr);
+ }
}
void FunctionValidator::visitRttCanon(RttCanon* curr) {
@@ -2368,15 +2380,15 @@ void FunctionValidator::visitArrayGet(ArrayGet* curr) {
getModule()->features.hasGC(), curr, "array.get requires gc to be enabled");
shouldBeEqualOrFirstIsUnreachable(
curr->index->type, Type(Type::i32), curr, "array.get index must be an i32");
+ if (curr->type == Type::unreachable) {
+ return;
+ }
const auto& element = curr->ref->type.getHeapType().getArray().element;
// If the type is not packed, it must be marked internally as unsigned, by
// convention.
if (element.type != Type::i32 || element.packedType == Field::not_packed) {
shouldBeFalse(curr->signed_, curr, "non-packed get cannot be signed");
}
- if (curr->type == Type::unreachable) {
- return;
- }
shouldBeEqual(
curr->type, element.type, curr, "array.get must have the proper type");
}
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 90120233d..436eedb2b 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -1023,16 +1023,39 @@ void RefTest::finalize() {
}
}
+// Helper to get the cast type for a cast instruction. They all look at the rtt
+// operand's type.
+template<typename T> static Type doGetCastType(T* curr) {
+ if (curr->rtt->type == Type::unreachable) {
+ // We don't have the RTT type, so just return unreachable. The type in this
+ // case should not matter in practice, but it may be seen while debugging.
+ return Type::unreachable;
+ }
+ // TODO: make non-nullable when we support that
+ return Type(curr->rtt->type.getHeapType(), Nullable);
+}
+
+Type RefTest::getCastType() { return doGetCastType(this); }
+
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);
+ type = getCastType();
+ }
+}
+
+Type RefCast::getCastType() { return doGetCastType(this); }
+
+void BrOnCast::finalize() {
+ if (ref->type == Type::unreachable || rtt->type == Type::unreachable) {
+ type = Type::unreachable;
+ } else {
+ type = ref->type;
}
}
-// TODO (gc): br_on_cast
+Type BrOnCast::getCastType() { return castType; }
void RttCanon::finalize() {
// Nothing to do - the type must have been set already during construction.