diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ir/ReFinalize.cpp | 6 | ||||
-rw-r--r-- | src/ir/branch-utils.h | 2 | ||||
-rw-r--r-- | src/ir/cost.h | 4 | ||||
-rw-r--r-- | src/ir/effects.h | 2 | ||||
-rw-r--r-- | src/passes/Precompute.cpp | 43 | ||||
-rw-r--r-- | src/passes/Print.cpp | 15 | ||||
-rw-r--r-- | src/wasm-builder.h | 8 | ||||
-rw-r--r-- | src/wasm-delegations-fields.h | 5 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 11 | ||||
-rw-r--r-- | src/wasm.h | 15 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 12 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 10 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 10 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 24 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 29 |
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. |