diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/binaryen-c.cpp | 18 | ||||
-rw-r--r-- | src/binaryen-c.h | 9 | ||||
-rw-r--r-- | src/ir/gc-type-utils.h | 12 | ||||
-rw-r--r-- | src/ir/module-utils.cpp | 2 | ||||
-rw-r--r-- | src/passes/Print.cpp | 10 | ||||
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 5 | ||||
-rw-r--r-- | src/wasm-binary.h | 4 | ||||
-rw-r--r-- | src/wasm-builder.h | 4 | ||||
-rw-r--r-- | src/wasm-delegations-fields.def | 2 | ||||
-rw-r--r-- | src/wasm.h | 5 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 16 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 12 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 14 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 11 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 46 |
15 files changed, 114 insertions, 56 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index b26ec3389..34a45540d 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -1757,12 +1757,10 @@ BinaryenExpressionRef BinaryenBrOn(BinaryenModuleRef module, BinaryenOp op, const char* name, BinaryenExpressionRef ref, - BinaryenHeapType intendedType) { - Builder builder(*(Module*)module); + BinaryenType castType) { return static_cast<Expression*>( - intendedType ? builder.makeBrOn( - BrOnOp(op), name, (Expression*)ref, HeapType(intendedType)) - : builder.makeBrOn(BrOnOp(op), name, (Expression*)ref)); + Builder(*(Module*)module) + .makeBrOn(BrOnOp(op), name, (Expression*)ref, Type(castType))); } BinaryenExpressionRef BinaryenStructNew(BinaryenModuleRef module, BinaryenExpressionRef* operands, @@ -4119,16 +4117,16 @@ void BinaryenBrOnSetRef(BinaryenExpressionRef expr, assert(refExpr); static_cast<BrOn*>(expression)->ref = (Expression*)refExpr; } -BinaryenHeapType BinaryenBrOnGetIntendedType(BinaryenExpressionRef expr) { +BinaryenType BinaryenBrOnGetCastType(BinaryenExpressionRef expr) { auto* expression = (Expression*)expr; assert(expression->is<BrOn>()); - return static_cast<BrOn*>(expression)->intendedType.getID(); + return static_cast<BrOn*>(expression)->castType.getID(); } -void BinaryenBrOnSetIntendedType(BinaryenExpressionRef expr, - BinaryenHeapType intendedType) { +void BinaryenBrOnSetCastType(BinaryenExpressionRef expr, + BinaryenType castType) { auto* expression = (Expression*)expr; assert(expression->is<BrOn>()); - static_cast<BrOn*>(expression)->intendedType = HeapType(intendedType); + static_cast<BrOn*>(expression)->castType = Type(castType); } // StructNew BinaryenIndex BinaryenStructNewGetNumOperands(BinaryenExpressionRef expr) { diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 7894c1c24..357e1f18d 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -1048,7 +1048,7 @@ BINARYEN_API BinaryenExpressionRef BinaryenBrOn(BinaryenModuleRef module, BinaryenOp op, const char* name, BinaryenExpressionRef ref, - BinaryenHeapType intendedType); + BinaryenType castType); BINARYEN_API BinaryenExpressionRef BinaryenStructNew(BinaryenModuleRef module, BinaryenExpressionRef* operands, @@ -2384,10 +2384,9 @@ BINARYEN_API BinaryenExpressionRef BinaryenBrOnGetRef(BinaryenExpressionRef expr); BINARYEN_API void BinaryenBrOnSetRef(BinaryenExpressionRef expr, BinaryenExpressionRef refExpr); -BINARYEN_API BinaryenHeapType -BinaryenBrOnGetIntendedType(BinaryenExpressionRef expr); -BINARYEN_API void BinaryenBrOnSetIntendedType(BinaryenExpressionRef expr, - BinaryenHeapType intendedType); +BINARYEN_API BinaryenType BinaryenBrOnGetCastType(BinaryenExpressionRef expr); +BINARYEN_API void BinaryenBrOnSetCastType(BinaryenExpressionRef expr, + BinaryenType castType); // StructNew diff --git a/src/ir/gc-type-utils.h b/src/ir/gc-type-utils.h index abe5f2dfc..5e3de37e5 100644 --- a/src/ir/gc-type-utils.h +++ b/src/ir/gc-type-utils.h @@ -52,16 +52,20 @@ inline EvaluationResult evaluateKindCheck(Expression* curr) { // We don't check nullability here. case BrOnNull: case BrOnNonNull: + return Unknown; case BrOnCastFail: flip = true; [[fallthrough]]; case BrOnCast: - // Note that the type must be non-nullable for us to succeed since a - // null would make us fail. - if (Type::isSubType(br->ref->type, - Type(br->intendedType, NonNullable))) { + // If we already have a subtype of the cast type, the cast will succeed. + if (Type::isSubType(br->ref->type, br->castType)) { return flip ? Failure : Success; } + // If the cast type is unrelated to the type we have, the cast will + // certainly fail. + if (!Type::isSubType(br->castType, br->ref->type)) { + return flip ? Success : Failure; + } return Unknown; case BrOnNonFunc: flip = true; diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index 0cdff0955..9dfc5f123 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -77,7 +77,7 @@ struct CodeScanner counts.note(cast->castType); } else if (auto* cast = curr->dynCast<BrOn>()) { if (cast->op == BrOnCast || cast->op == BrOnCastFail) { - counts.note(cast->intendedType); + counts.note(cast->castType); } } else if (auto* get = curr->dynCast<StructGet>()) { counts.note(get->ref->type); diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 81e3ae989..c1064b7db 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2143,13 +2143,19 @@ struct PrintExpressionContents printMedium(o, "br_on_cast "); printName(curr->name, o); o << ' '; - printHeapType(o, curr->intendedType, wasm); + if (curr->castType.isNullable()) { + printMedium(o, "null "); + } + printHeapType(o, curr->castType.getHeapType(), wasm); return; case BrOnCastFail: printMedium(o, "br_on_cast_fail "); printName(curr->name, o); o << ' '; - printHeapType(o, curr->intendedType, wasm); + if (curr->castType.isNullable()) { + printMedium(o, "null "); + } + printHeapType(o, curr->castType.getHeapType(), wasm); return; case BrOnFunc: printMedium(o, "br_on_func "); diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index f465d46c1..4edfb8b5e 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -713,12 +713,13 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } // First, check for a possible null which would prevent all other - // optimizations. + // optimizations (except for br_on_cast variants). // TODO: Look into using BrOnNonNull here, to replace a br_on_func whose // input is (ref null func) with br_on_non_null (as only the null check // would be needed). auto refType = curr->ref->type; - if (refType.isNullable()) { + if (refType.isNullable() && curr->op != BrOnCast && + curr->op != BrOnCastFail) { return; } diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 996d9ab4c..303c81128 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1127,8 +1127,8 @@ enum ASTNodes { BrOnCastStaticFail = 0x47, RefTestNull = 0x48, RefCastNull = 0x49, - // TODO: BrOnCastNull - // TODO: BrOnCastFailNull + BrOnCastNull = 0x4a, + BrOnCastFailNull = 0x4b, RefCastNop = 0x4c, RefIsFunc = 0x50, RefIsData = 0x51, diff --git a/src/wasm-builder.h b/src/wasm-builder.h index a83a0c0c2..49c9bb0ba 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -899,12 +899,12 @@ public: ret->finalize(); return ret; } - BrOn* makeBrOn(BrOnOp op, Name name, Expression* ref, HeapType intendedType) { + BrOn* makeBrOn(BrOnOp op, Name name, Expression* ref, Type castType) { auto* ret = wasm.allocator.alloc<BrOn>(); ret->op = op; ret->name = name; ret->ref = ref; - ret->intendedType = intendedType; + ret->castType = castType; ret->finalize(); return ret; } diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index 9c966faec..f929def4a 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -629,7 +629,7 @@ switch (DELEGATE_ID) { DELEGATE_START(BrOn); DELEGATE_FIELD_INT(BrOn, op); DELEGATE_FIELD_SCOPE_NAME_USE(BrOn, name); - DELEGATE_FIELD_HEAPTYPE(BrOn, intendedType); + DELEGATE_FIELD_TYPE(BrOn, castType); DELEGATE_FIELD_CHILD(BrOn, ref); DELEGATE_END(BrOn); break; diff --git a/src/wasm.h b/src/wasm.h index f1dcd7b46..db10fe260 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1553,12 +1553,11 @@ public: Name name; Expression* ref; - HeapType intendedType; + Type castType; void finalize(); - // TODO: Support br_on_cast* null as well. - Type getCastType() { return Type(intendedType, NonNullable); } + Type getCastType() { return castType; } // Returns the type sent on the branch, if it is taken. Type getSentType(); diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index c6fb361bd..61fd65eab 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -6931,6 +6931,7 @@ bool WasmBinaryBuilder::maybeVisitRefCast(Expression*& out, uint32_t code) { bool WasmBinaryBuilder::maybeVisitBrOn(Expression*& out, uint32_t code) { BrOnOp op; + auto nullability = NonNullable; switch (code) { case BinaryConsts::BrOnNull: op = BrOnNull; @@ -6946,6 +6947,14 @@ bool WasmBinaryBuilder::maybeVisitBrOn(Expression*& out, uint32_t code) { case BinaryConsts::BrOnCastFail: op = BrOnCastFail; break; + case BinaryConsts::BrOnCastNull: + op = BrOnCast; + nullability = Nullable; + break; + case BinaryConsts::BrOnCastFailNull: + op = BrOnCastFail; + nullability = Nullable; + break; case BinaryConsts::BrOnFunc: op = BrOnFunc; break; @@ -6968,14 +6977,15 @@ bool WasmBinaryBuilder::maybeVisitBrOn(Expression*& out, uint32_t code) { return false; } auto name = getBreakTarget(getU32LEB()).name; - HeapType intendedType; + Type castType = Type::none; if (op == BrOnCast || op == BrOnCastFail) { bool legacy = code == BinaryConsts::BrOnCastStatic || code == BinaryConsts::BrOnCastStaticFail; - intendedType = legacy ? getIndexedHeapType() : getHeapType(); + auto type = legacy ? getIndexedHeapType() : getHeapType(); + castType = Type(type, nullability); } auto* ref = popNonVoidExpression(); - out = Builder(wasm).makeBrOn(op, name, ref, intendedType); + out = Builder(wasm).makeBrOn(op, name, ref, castType); return true; } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 2f791609a..fd3cbcc23 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2821,12 +2821,18 @@ Expression* SExpressionWasmBuilder::makeRefCastNop(Element& s) { Expression* SExpressionWasmBuilder::makeBrOn(Element& s, BrOnOp op) { int i = 1; auto name = getLabel(*s[i++]); - HeapType heapType; + Type castType = Type::none; if (op == BrOnCast || op == BrOnCastFail) { - heapType = parseHeapType(*s[i++]); + auto nullability = NonNullable; + if (s[i]->str().str == "null") { + nullability = Nullable; + ++i; + } + auto type = parseHeapType(*s[i++]); + castType = Type(type, nullability); } auto* ref = parseExpression(*s[i]); - return Builder(wasm).makeBrOn(op, name, ref, heapType); + return Builder(wasm).makeBrOn(op, name, ref, castType); } Expression* SExpressionWasmBuilder::makeStructNew(Element& s, bool default_) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index e24060f2a..fbf73b7c2 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2058,11 +2058,19 @@ void BinaryInstWriter::visitBrOn(BrOn* curr) { break; case BrOnCast: o << int8_t(BinaryConsts::GCPrefix); - o << U32LEB(BinaryConsts::BrOnCast); + if (curr->castType.isNullable()) { + o << U32LEB(BinaryConsts::BrOnCastNull); + } else { + o << U32LEB(BinaryConsts::BrOnCast); + } break; case BrOnCastFail: o << int8_t(BinaryConsts::GCPrefix); - o << U32LEB(BinaryConsts::BrOnCastFail); + if (curr->castType.isNullable()) { + o << U32LEB(BinaryConsts::BrOnCastFailNull); + } else { + o << U32LEB(BinaryConsts::BrOnCastFail); + } break; case BrOnFunc: o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::BrOnFunc); @@ -2087,7 +2095,7 @@ void BinaryInstWriter::visitBrOn(BrOn* curr) { } o << U32LEB(getBreakIndex(curr->name)); if (curr->op == BrOnCast || curr->op == BrOnCastFail) { - parent.writeHeapType(curr->intendedType); + parent.writeHeapType(curr->castType.getHeapType()); } } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index bec9b0ad0..486f13d24 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2554,14 +2554,19 @@ void FunctionValidator::visitBrOn(BrOn* curr) { return; } if (curr->op == BrOnCast || curr->op == BrOnCastFail) { + if (!shouldBeTrue(curr->castType.isRef(), + curr, + "br_on_cast must have reference cast type")) { + return; + } shouldBeEqual( - curr->intendedType.getBottom(), + curr->castType.getHeapType().getBottom(), curr->ref->type.getHeapType().getBottom(), curr, "br_on_cast* target type and ref type must have a common supertype"); } else { - shouldBeEqual(curr->intendedType, - HeapType(), + shouldBeEqual(curr->castType, + Type(Type::none), curr, "non-cast br_on* must not set intendedType field"); } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 682936461..de18966ff 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -968,18 +968,12 @@ void BrOn::finalize() { // us flow out the null, but it does not). type = Type::none; break; - case BrOnCast: case BrOnFunc: case BrOnData: case BrOnI31: // If we do not branch, we return the input in this case. type = ref->type; break; - case BrOnCastFail: - // If we do not branch, the cast worked, and we have something of the cast - // type. - type = Type(intendedType, NonNullable); - break; case BrOnNonFunc: type = Type(HeapType::func, NonNullable); break; @@ -989,6 +983,26 @@ void BrOn::finalize() { case BrOnNonI31: type = Type(HeapType::i31, NonNullable); break; + case BrOnCast: + if (castType.isNullable()) { + // Nulls take the branch, so the result is non-nullable. + type = Type(ref->type.getHeapType(), NonNullable); + } else { + // Nulls do not take the branch, so the result is non-nullable only if + // the input is. + type = ref->type; + } + break; + case BrOnCastFail: + if (castType.isNullable()) { + // Nulls do not take the branch, so the result is non-nullable only if + // the input is. + type = Type(castType.getHeapType(), ref->type.getNullability()); + } else { + // Nulls take the branch, so the result is non-nullable. + type = castType; + } + break; default: WASM_UNREACHABLE("invalid br_on_*"); } @@ -1007,22 +1021,30 @@ Type BrOn::getSentType() { } // BrOnNonNull sends the non-nullable type on the branch. return Type(ref->type.getHeapType(), NonNullable); - case BrOnCast: - if (ref->type == Type::unreachable) { - return Type::unreachable; - } - return Type(intendedType, NonNullable); case BrOnFunc: return Type(HeapType::func, NonNullable); case BrOnData: return Type(HeapType::data, NonNullable); case BrOnI31: return Type(HeapType::i31, NonNullable); - case BrOnCastFail: case BrOnNonFunc: case BrOnNonData: case BrOnNonI31: return ref->type; + case BrOnCast: + // The same as the result type of br_on_cast_fail. + if (castType.isNullable()) { + return Type(castType.getHeapType(), ref->type.getNullability()); + } else { + return castType; + } + case BrOnCastFail: + // The same as the result type of br_on_cast. + if (castType.isNullable()) { + return Type(ref->type.getHeapType(), NonNullable); + } else { + return ref->type; + } default: WASM_UNREACHABLE("invalid br_on_*"); } |