diff options
author | Thomas Lively <tlively@google.com> | 2023-01-06 15:47:19 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-06 13:47:19 -0800 |
commit | f719c3527512377657f72f16ee4ac5581843614f (patch) | |
tree | b082f8d5e80bb53a9d69e7c8197f6b7ada2939b6 /src/wasm | |
parent | 73a1cfcacd028ed7aefe304c2e140cda4068dcb0 (diff) | |
download | binaryen-f719c3527512377657f72f16ee4ac5581843614f.tar.gz binaryen-f719c3527512377657f72f16ee4ac5581843614f.tar.bz2 binaryen-f719c3527512377657f72f16ee4ac5581843614f.zip |
Consolidate br_on* operations (#5399)
The `br_on{_non}_{data,i31,func}` operations are deprecated and directly
representable in terms of the new `br_on_cast` and `br_on_cast_fail`
instructions, so remove their dedicated IR opcodes in favor of representing them
as casts. `br_on_null` and `br_on_non_null` cannot be consolidated the same way
because their behavior is not directly representable in terms of `br_on_cast`
and `br_on_cast_fail`; when the cast to null bottom type succeeds, the null
check instructions implicitly drop the null value whereas the cast instructions
would propagate it.
Add special logic to the binary writer and printer to continue emitting the
deprecated instructions for now. This will allow us to update the test suite in
a separate future PR with no additional functional changes.
Some tests are updated because the validator no longer allows passing non-func
data to `br_on_func`. Doing so has not made sense since we separated the three
reference type hierarchies.
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 35 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 18 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 79 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 25 | ||||
-rw-r--r-- | src/wasm/wat-parser.cpp | 13 |
5 files changed, 94 insertions, 76 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 61fd65eab..2639599d5 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -6930,8 +6930,8 @@ bool WasmBinaryBuilder::maybeVisitRefCast(Expression*& out, uint32_t code) { } bool WasmBinaryBuilder::maybeVisitBrOn(Expression*& out, uint32_t code) { + Type castType = Type::none; BrOnOp op; - auto nullability = NonNullable; switch (code) { case BinaryConsts::BrOnNull: op = BrOnNull; @@ -6941,44 +6941,47 @@ bool WasmBinaryBuilder::maybeVisitBrOn(Expression*& out, uint32_t code) { break; case BinaryConsts::BrOnCastStatic: case BinaryConsts::BrOnCast: + case BinaryConsts::BrOnCastNull: op = BrOnCast; break; case BinaryConsts::BrOnCastStaticFail: 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; + op = BrOnCast; + castType = Type(HeapType::func, NonNullable); break; case BinaryConsts::BrOnNonFunc: - op = BrOnNonFunc; + op = BrOnCastFail; + castType = Type(HeapType::func, NonNullable); break; case BinaryConsts::BrOnData: - op = BrOnData; + op = BrOnCast; + castType = Type(HeapType::data, NonNullable); break; case BinaryConsts::BrOnNonData: - op = BrOnNonData; + op = BrOnCastFail; + castType = Type(HeapType::data, NonNullable); break; case BinaryConsts::BrOnI31: - op = BrOnI31; + op = BrOnCast; + castType = Type(HeapType::i31, NonNullable); break; case BinaryConsts::BrOnNonI31: - op = BrOnNonI31; + op = BrOnCastFail; + castType = Type(HeapType::i31, NonNullable); break; default: return false; } auto name = getBreakTarget(getU32LEB()).name; - Type castType = Type::none; - if (op == BrOnCast || op == BrOnCastFail) { + if (castType == Type::none && (op == BrOnCast || op == BrOnCastFail)) { + auto nullability = (code == BinaryConsts::BrOnCastNull || + code == BinaryConsts::BrOnCastFailNull) + ? Nullable + : NonNullable; bool legacy = code == BinaryConsts::BrOnCastStatic || code == BinaryConsts::BrOnCastStaticFail; auto type = legacy ? getIndexedHeapType() : getHeapType(); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index fd3cbcc23..638dfbebf 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2818,11 +2818,20 @@ Expression* SExpressionWasmBuilder::makeRefCastNop(Element& s) { return Builder(wasm).makeRefCast(ref, type, RefCast::Unsafe); } -Expression* SExpressionWasmBuilder::makeBrOn(Element& s, BrOnOp op) { +Expression* SExpressionWasmBuilder::makeBrOnNull(Element& s, bool onFail) { int i = 1; auto name = getLabel(*s[i++]); - Type castType = Type::none; - if (op == BrOnCast || op == BrOnCastFail) { + auto* ref = parseExpression(*s[i]); + auto op = onFail ? BrOnNonNull : BrOnNull; + return Builder(wasm).makeBrOn(op, name, ref); +} + +Expression* SExpressionWasmBuilder::makeBrOnCast(Element& s, + std::optional<Type> castType, + bool onFail) { + int i = 1; + auto name = getLabel(*s[i++]); + if (!castType) { auto nullability = NonNullable; if (s[i]->str().str == "null") { nullability = Nullable; @@ -2832,7 +2841,8 @@ Expression* SExpressionWasmBuilder::makeBrOn(Element& s, BrOnOp op) { castType = Type(type, nullability); } auto* ref = parseExpression(*s[i]); - return Builder(wasm).makeBrOn(op, name, ref, castType); + auto op = onFail ? BrOnCastFail : BrOnCast; + 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 fbf73b7c2..24710f206 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2052,51 +2052,74 @@ void BinaryInstWriter::visitBrOn(BrOn* curr) { switch (curr->op) { case BrOnNull: o << int8_t(BinaryConsts::BrOnNull); - break; + o << U32LEB(getBreakIndex(curr->name)); + return; case BrOnNonNull: o << int8_t(BinaryConsts::BrOnNonNull); - break; + o << U32LEB(getBreakIndex(curr->name)); + return; case BrOnCast: o << int8_t(BinaryConsts::GCPrefix); + // TODO: These instructions are deprecated, so stop emitting them. + if (auto type = curr->castType.getHeapType(); + type.isBasic() && curr->castType.isNonNullable()) { + switch (type.getBasic()) { + case HeapType::func: + o << U32LEB(BinaryConsts::BrOnFunc); + o << U32LEB(getBreakIndex(curr->name)); + return; + case HeapType::data: + o << U32LEB(BinaryConsts::BrOnData); + o << U32LEB(getBreakIndex(curr->name)); + return; + case HeapType::i31: + o << U32LEB(BinaryConsts::BrOnI31); + o << U32LEB(getBreakIndex(curr->name)); + return; + default: + break; + } + } if (curr->castType.isNullable()) { o << U32LEB(BinaryConsts::BrOnCastNull); } else { o << U32LEB(BinaryConsts::BrOnCast); } - break; + o << U32LEB(getBreakIndex(curr->name)); + parent.writeHeapType(curr->castType.getHeapType()); + return; case BrOnCastFail: o << int8_t(BinaryConsts::GCPrefix); + // TODO: These instructions are deprecated, so stop emitting them. + if (auto type = curr->castType.getHeapType(); + type.isBasic() && curr->castType.isNonNullable()) { + switch (type.getBasic()) { + case HeapType::func: + o << U32LEB(BinaryConsts::BrOnNonFunc); + o << U32LEB(getBreakIndex(curr->name)); + return; + case HeapType::data: + o << U32LEB(BinaryConsts::BrOnNonData); + o << U32LEB(getBreakIndex(curr->name)); + return; + case HeapType::i31: + o << U32LEB(BinaryConsts::BrOnNonI31); + o << U32LEB(getBreakIndex(curr->name)); + return; + default: + break; + } + } if (curr->castType.isNullable()) { o << U32LEB(BinaryConsts::BrOnCastFailNull); } else { o << U32LEB(BinaryConsts::BrOnCastFail); } - break; - case BrOnFunc: - o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::BrOnFunc); - break; - case BrOnNonFunc: - o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::BrOnNonFunc); - break; - case BrOnData: - o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::BrOnData); - break; - case BrOnNonData: - o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::BrOnNonData); - break; - case BrOnI31: - o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::BrOnI31); - break; - case BrOnNonI31: - o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::BrOnNonI31); - break; - default: - WASM_UNREACHABLE("invalid br_on_*"); - } - o << U32LEB(getBreakIndex(curr->name)); - if (curr->op == BrOnCast || curr->op == BrOnCastFail) { - parent.writeHeapType(curr->castType.getHeapType()); + o << U32LEB(getBreakIndex(curr->name)); + parent.writeHeapType(curr->castType.getHeapType()); + return; } + WASM_UNREACHABLE("invalid br_on_*"); } void BinaryInstWriter::visitStructNew(StructNew* curr) { diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index de18966ff..c6779a328 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -968,21 +968,6 @@ void BrOn::finalize() { // us flow out the null, but it does not). type = Type::none; break; - case BrOnFunc: - case BrOnData: - case BrOnI31: - // If we do not branch, we return the input in this case. - type = ref->type; - break; - case BrOnNonFunc: - type = Type(HeapType::func, NonNullable); - break; - case BrOnNonData: - type = Type(HeapType::data, NonNullable); - break; - case BrOnNonI31: - type = Type(HeapType::i31, NonNullable); - break; case BrOnCast: if (castType.isNullable()) { // Nulls take the branch, so the result is non-nullable. @@ -1021,16 +1006,6 @@ Type BrOn::getSentType() { } // BrOnNonNull sends the non-nullable type on the branch. return Type(ref->type.getHeapType(), NonNullable); - case BrOnFunc: - return Type(HeapType::func, NonNullable); - case BrOnData: - return Type(HeapType::data, NonNullable); - case BrOnI31: - return Type(HeapType::i31, NonNullable); - 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()) { diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp index 409d51052..14d30db1c 100644 --- a/src/wasm/wat-parser.cpp +++ b/src/wasm/wat-parser.cpp @@ -2349,9 +2349,10 @@ template<typename Ctx> Result<typename Ctx::InstrT> makeRefTest(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeRefCast(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeRefCastNop(Ctx&, Index); template<typename Ctx> -Result<typename Ctx::InstrT> makeBrOn(Ctx&, Index, BrOnOp op); +Result<typename Ctx::InstrT> makeBrOnNull(Ctx&, Index, bool onFail = false); template<typename Ctx> -Result<typename Ctx::InstrT> makeBrOn(Ctx&, Index, BrOnOp op); +Result<typename Ctx::InstrT> +makeBrOnCast(Ctx&, Index, std::optional<Type>, bool onFail = false); template<typename Ctx> Result<typename Ctx::InstrT> makeStructNew(Ctx&, Index, bool default_); template<typename Ctx> @@ -3452,7 +3453,13 @@ Result<typename Ctx::InstrT> makeRefCastNop(Ctx& ctx, Index pos) { } template<typename Ctx> -Result<typename Ctx::InstrT> makeBrOn(Ctx& ctx, Index pos, BrOnOp op) { +Result<typename Ctx::InstrT> makeBrOnNull(Ctx& ctx, Index pos, bool onFail) { + return ctx.in.err("unimplemented instruction"); +} + +template<typename Ctx> +Result<typename Ctx::InstrT> +makeBrOnCast(Ctx& ctx, Index pos, std::optional<Type> castType, bool onFail) { return ctx.in.err("unimplemented instruction"); } |