summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2023-01-06 15:47:19 -0600
committerGitHub <noreply@github.com>2023-01-06 13:47:19 -0800
commitf719c3527512377657f72f16ee4ac5581843614f (patch)
treeb082f8d5e80bb53a9d69e7c8197f6b7ada2939b6 /src
parent73a1cfcacd028ed7aefe304c2e140cda4068dcb0 (diff)
downloadbinaryen-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')
-rw-r--r--src/binaryen-c.cpp6
-rw-r--r--src/binaryen-c.h6
-rw-r--r--src/gen-s-parser.inc48
-rw-r--r--src/ir/gc-type-utils.h18
-rw-r--r--src/js/binaryen.js-post.js6
-rw-r--r--src/passes/Print.cpp68
-rw-r--r--src/wasm-builder.h11
-rw-r--r--src/wasm-interpreter.h50
-rw-r--r--src/wasm-s-parser.h4
-rw-r--r--src/wasm.h7
-rw-r--r--src/wasm/wasm-binary.cpp35
-rw-r--r--src/wasm/wasm-s-parser.cpp18
-rw-r--r--src/wasm/wasm-stack.cpp79
-rw-r--r--src/wasm/wasm.cpp25
-rw-r--r--src/wasm/wat-parser.cpp13
15 files changed, 172 insertions, 222 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index 34a45540d..7ff827aac 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -1022,12 +1022,6 @@ BinaryenOp BinaryenBrOnNull(void) { return BrOnNull; }
BinaryenOp BinaryenBrOnNonNull(void) { return BrOnNonNull; }
BinaryenOp BinaryenBrOnCast(void) { return BrOnCast; }
BinaryenOp BinaryenBrOnCastFail(void) { return BrOnCastFail; };
-BinaryenOp BinaryenBrOnFunc(void) { return BrOnFunc; }
-BinaryenOp BinaryenBrOnNonFunc(void) { return BrOnNonFunc; }
-BinaryenOp BinaryenBrOnData(void) { return BrOnData; }
-BinaryenOp BinaryenBrOnNonData(void) { return BrOnNonData; }
-BinaryenOp BinaryenBrOnI31(void) { return BrOnI31; }
-BinaryenOp BinaryenBrOnNonI31(void) { return BrOnNonI31; }
BinaryenOp BinaryenStringNewUTF8(void) { return StringNewUTF8; }
BinaryenOp BinaryenStringNewWTF8(void) { return StringNewWTF8; }
BinaryenOp BinaryenStringNewReplace(void) { return StringNewReplace; }
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index 357e1f18d..171d1248d 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -684,12 +684,6 @@ BINARYEN_API BinaryenOp BinaryenBrOnNull(void);
BINARYEN_API BinaryenOp BinaryenBrOnNonNull(void);
BINARYEN_API BinaryenOp BinaryenBrOnCast(void);
BINARYEN_API BinaryenOp BinaryenBrOnCastFail(void);
-BINARYEN_API BinaryenOp BinaryenBrOnFunc(void);
-BINARYEN_API BinaryenOp BinaryenBrOnNonFunc(void);
-BINARYEN_API BinaryenOp BinaryenBrOnData(void);
-BINARYEN_API BinaryenOp BinaryenBrOnNonData(void);
-BINARYEN_API BinaryenOp BinaryenBrOnI31(void);
-BINARYEN_API BinaryenOp BinaryenBrOnNonI31(void);
BINARYEN_API BinaryenOp BinaryenStringNewUTF8(void);
BINARYEN_API BinaryenOp BinaryenStringNewWTF8(void);
BINARYEN_API BinaryenOp BinaryenStringNewReplace(void);
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index 83a7a3121..acae3af50 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -100,20 +100,20 @@ switch (buf[0]) {
case 'c': {
switch (buf[10]) {
case '\0':
- if (op == "br_on_cast"sv) { return makeBrOn(s, BrOnCast); }
+ if (op == "br_on_cast"sv) { return makeBrOnCast(s, std::nullopt); }
goto parse_error;
case '_': {
switch (buf[11]) {
case 'f':
- if (op == "br_on_cast_fail"sv) { return makeBrOn(s, BrOnCastFail); }
+ if (op == "br_on_cast_fail"sv) { return makeBrOnCast(s, std::nullopt, true); }
goto parse_error;
case 's': {
switch (buf[17]) {
case '\0':
- if (op == "br_on_cast_static"sv) { return makeBrOn(s, BrOnCast); }
+ if (op == "br_on_cast_static"sv) { return makeBrOnCast(s, std::nullopt); }
goto parse_error;
case '_':
- if (op == "br_on_cast_static_fail"sv) { return makeBrOn(s, BrOnCastFail); }
+ if (op == "br_on_cast_static_fail"sv) { return makeBrOnCast(s, std::nullopt, true); }
goto parse_error;
default: goto parse_error;
}
@@ -125,35 +125,35 @@ switch (buf[0]) {
}
}
case 'd':
- if (op == "br_on_data"sv) { return makeBrOn(s, BrOnData); }
+ if (op == "br_on_data"sv) { return makeBrOnCast(s, Type(HeapType::data, NonNullable)); }
goto parse_error;
case 'f':
- if (op == "br_on_func"sv) { return makeBrOn(s, BrOnFunc); }
+ if (op == "br_on_func"sv) { return makeBrOnCast(s, Type(HeapType::func, NonNullable)); }
goto parse_error;
case 'i':
- if (op == "br_on_i31"sv) { return makeBrOn(s, BrOnI31); }
+ if (op == "br_on_i31"sv) { return makeBrOnCast(s, Type(HeapType::i31, NonNullable)); }
goto parse_error;
case 'n': {
switch (buf[7]) {
case 'o': {
switch (buf[10]) {
case 'd':
- if (op == "br_on_non_data"sv) { return makeBrOn(s, BrOnNonData); }
+ if (op == "br_on_non_data"sv) { return makeBrOnCast(s, Type(HeapType::data, NonNullable), true); }
goto parse_error;
case 'f':
- if (op == "br_on_non_func"sv) { return makeBrOn(s, BrOnNonFunc); }
+ if (op == "br_on_non_func"sv) { return makeBrOnCast(s, Type(HeapType::func, NonNullable), true); }
goto parse_error;
case 'i':
- if (op == "br_on_non_i31"sv) { return makeBrOn(s, BrOnNonI31); }
+ if (op == "br_on_non_i31"sv) { return makeBrOnCast(s, Type(HeapType::i31, NonNullable), true); }
goto parse_error;
case 'n':
- if (op == "br_on_non_null"sv) { return makeBrOn(s, BrOnNonNull); }
+ if (op == "br_on_non_null"sv) { return makeBrOnNull(s, true); }
goto parse_error;
default: goto parse_error;
}
}
case 'u':
- if (op == "br_on_null"sv) { return makeBrOn(s, BrOnNull); }
+ if (op == "br_on_null"sv) { return makeBrOnNull(s); }
goto parse_error;
default: goto parse_error;
}
@@ -3727,7 +3727,7 @@ switch (buf[0]) {
switch (buf[10]) {
case '\0':
if (op == "br_on_cast"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnCast);
+ auto ret = makeBrOnCast(ctx, pos, std::nullopt);
CHECK_ERR(ret);
return *ret;
}
@@ -3736,7 +3736,7 @@ switch (buf[0]) {
switch (buf[11]) {
case 'f':
if (op == "br_on_cast_fail"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnCastFail);
+ auto ret = makeBrOnCast(ctx, pos, std::nullopt, true);
CHECK_ERR(ret);
return *ret;
}
@@ -3745,14 +3745,14 @@ switch (buf[0]) {
switch (buf[17]) {
case '\0':
if (op == "br_on_cast_static"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnCast);
+ auto ret = makeBrOnCast(ctx, pos, std::nullopt);
CHECK_ERR(ret);
return *ret;
}
goto parse_error;
case '_':
if (op == "br_on_cast_static_fail"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnCastFail);
+ auto ret = makeBrOnCast(ctx, pos, std::nullopt, true);
CHECK_ERR(ret);
return *ret;
}
@@ -3768,21 +3768,21 @@ switch (buf[0]) {
}
case 'd':
if (op == "br_on_data"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnData);
+ auto ret = makeBrOnCast(ctx, pos, Type(HeapType::data, NonNullable));
CHECK_ERR(ret);
return *ret;
}
goto parse_error;
case 'f':
if (op == "br_on_func"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnFunc);
+ auto ret = makeBrOnCast(ctx, pos, Type(HeapType::func, NonNullable));
CHECK_ERR(ret);
return *ret;
}
goto parse_error;
case 'i':
if (op == "br_on_i31"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnI31);
+ auto ret = makeBrOnCast(ctx, pos, Type(HeapType::i31, NonNullable));
CHECK_ERR(ret);
return *ret;
}
@@ -3793,28 +3793,28 @@ switch (buf[0]) {
switch (buf[10]) {
case 'd':
if (op == "br_on_non_data"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnNonData);
+ auto ret = makeBrOnCast(ctx, pos, Type(HeapType::data, NonNullable), true);
CHECK_ERR(ret);
return *ret;
}
goto parse_error;
case 'f':
if (op == "br_on_non_func"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnNonFunc);
+ auto ret = makeBrOnCast(ctx, pos, Type(HeapType::func, NonNullable), true);
CHECK_ERR(ret);
return *ret;
}
goto parse_error;
case 'i':
if (op == "br_on_non_i31"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnNonI31);
+ auto ret = makeBrOnCast(ctx, pos, Type(HeapType::i31, NonNullable), true);
CHECK_ERR(ret);
return *ret;
}
goto parse_error;
case 'n':
if (op == "br_on_non_null"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnNonNull);
+ auto ret = makeBrOnNull(ctx, pos, true);
CHECK_ERR(ret);
return *ret;
}
@@ -3824,7 +3824,7 @@ switch (buf[0]) {
}
case 'u':
if (op == "br_on_null"sv) {
- auto ret = makeBrOn(ctx, pos, BrOnNull);
+ auto ret = makeBrOnNull(ctx, pos);
CHECK_ERR(ret);
return *ret;
}
diff --git a/src/ir/gc-type-utils.h b/src/ir/gc-type-utils.h
index 5e3de37e5..6ac604ad2 100644
--- a/src/ir/gc-type-utils.h
+++ b/src/ir/gc-type-utils.h
@@ -67,24 +67,6 @@ inline EvaluationResult evaluateKindCheck(Expression* curr) {
return flip ? Success : Failure;
}
return Unknown;
- case BrOnNonFunc:
- flip = true;
- [[fallthrough]];
- case BrOnFunc:
- expected = Func;
- break;
- case BrOnNonData:
- flip = true;
- [[fallthrough]];
- case BrOnData:
- expected = Data;
- break;
- case BrOnNonI31:
- flip = true;
- [[fallthrough]];
- case BrOnI31:
- expected = I31;
- break;
default:
WASM_UNREACHABLE("unhandled BrOn");
}
diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js
index f252dae8e..d4395359e 100644
--- a/src/js/binaryen.js-post.js
+++ b/src/js/binaryen.js-post.js
@@ -563,12 +563,6 @@ function initializeConstants() {
'BrOnNonNull',
'BrOnCast',
'BrOnCastFail',
- 'BrOnFunc',
- 'BrOnNonFunc',
- 'BrOnData',
- 'BrOnNonData',
- 'BrOnI31',
- 'BrOnNonI31',
'StringNewUTF8',
'StringNewWTF8',
'StringNewReplace',
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index c1064b7db..988b1d251 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -2135,11 +2135,33 @@ struct PrintExpressionContents
switch (curr->op) {
case BrOnNull:
printMedium(o, "br_on_null ");
- break;
+ printName(curr->name, o);
+ return;
case BrOnNonNull:
printMedium(o, "br_on_non_null ");
- break;
+ printName(curr->name, o);
+ return;
case BrOnCast:
+ // 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:
+ printMedium(o, "br_on_func ");
+ printName(curr->name, o);
+ return;
+ case HeapType::data:
+ printMedium(o, "br_on_data ");
+ printName(curr->name, o);
+ return;
+ case HeapType::i31:
+ printMedium(o, "br_on_i31 ");
+ printName(curr->name, o);
+ return;
+ default:
+ break;
+ }
+ }
printMedium(o, "br_on_cast ");
printName(curr->name, o);
o << ' ';
@@ -2149,6 +2171,26 @@ struct PrintExpressionContents
printHeapType(o, curr->castType.getHeapType(), wasm);
return;
case BrOnCastFail:
+ // 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:
+ printMedium(o, "br_on_non_func ");
+ printName(curr->name, o);
+ return;
+ case HeapType::data:
+ printMedium(o, "br_on_non_data ");
+ printName(curr->name, o);
+ return;
+ case HeapType::i31:
+ printMedium(o, "br_on_non_i31 ");
+ printName(curr->name, o);
+ return;
+ default:
+ break;
+ }
+ }
printMedium(o, "br_on_cast_fail ");
printName(curr->name, o);
o << ' ';
@@ -2157,28 +2199,8 @@ struct PrintExpressionContents
}
printHeapType(o, curr->castType.getHeapType(), wasm);
return;
- case BrOnFunc:
- printMedium(o, "br_on_func ");
- break;
- case BrOnNonFunc:
- printMedium(o, "br_on_non_func ");
- break;
- case BrOnData:
- printMedium(o, "br_on_data ");
- break;
- case BrOnNonData:
- printMedium(o, "br_on_non_data ");
- break;
- case BrOnI31:
- printMedium(o, "br_on_i31 ");
- break;
- case BrOnNonI31:
- printMedium(o, "br_on_non_i31 ");
- break;
- default:
- WASM_UNREACHABLE("invalid ref.is_*");
}
- printName(curr->name, o);
+ WASM_UNREACHABLE("Unexpected br_on* op");
}
void visitStructNew(StructNew* curr) {
if (printUnreachableReplacement(curr)) {
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 49c9bb0ba..915c189ae 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -891,15 +891,8 @@ public:
ret->finalize();
return ret;
}
- BrOn* makeBrOn(BrOnOp op, Name name, Expression* ref) {
- auto* ret = wasm.allocator.alloc<BrOn>();
- ret->op = op;
- ret->name = name;
- ret->ref = ref;
- ret->finalize();
- return ret;
- }
- BrOn* makeBrOn(BrOnOp op, Name name, Expression* ref, Type castType) {
+ BrOn*
+ makeBrOn(BrOnOp op, Name name, Expression* ref, Type castType = Type::none) {
auto* ret = wasm.allocator.alloc<BrOn>();
ret->op = op;
ret->name = name;
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 17c8a6456..181114c92 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1512,7 +1512,7 @@ public:
}
}
}
- // The others do a simpler check for the type.
+ // Otherwise we are just checking for null.
Flow flow = visit(curr->ref);
if (flow.breaking()) {
return flow;
@@ -1520,62 +1520,20 @@ public:
const auto& value = flow.getSingleValue();
NOTE_EVAL1(value);
if (curr->op == BrOnNull) {
- // Unlike the others, BrOnNull does not propagate the value if it takes
- // the branch.
+ // BrOnNull does not propagate the value if it takes the branch.
if (value.isNull()) {
return Flow(curr->name);
}
// If the branch is not taken, we return the non-null value.
return {value};
- }
- if (curr->op == BrOnNonNull) {
- // Unlike the others, BrOnNonNull does not return a value if it does not
- // take the branch.
+ } else {
+ // BrOnNonNull does not return a value if it does not take the branch.
if (value.isNull()) {
return Flow();
}
// If the branch is taken, we send the non-null value.
return Flow(curr->name, value);
}
- // See if the input is the right kind (ignoring the flipping behavior of
- // BrOn*).
- bool isRightKind;
- if (value.isNull()) {
- // A null is never the right kind.
- isRightKind = false;
- } else {
- switch (curr->op) {
- case BrOnNonFunc:
- case BrOnFunc:
- isRightKind = value.type.isFunction();
- break;
- case BrOnNonData:
- case BrOnData:
- isRightKind = value.isData();
- break;
- case BrOnNonI31:
- case BrOnI31:
- isRightKind = value.type.getHeapType() == HeapType::i31;
- break;
- default:
- WASM_UNREACHABLE("invalid br_on_*");
- }
- }
- // The Non* operations require us to flip the normal behavior.
- switch (curr->op) {
- case BrOnNonFunc:
- case BrOnNonData:
- case BrOnNonI31:
- isRightKind = !isRightKind;
- break;
- default: {
- }
- }
- if (isRightKind) {
- // Take the branch.
- return Flow(curr->name, value);
- }
- return {value};
}
Flow visitStructNew(StructNew* curr) {
NOTE_ENTER("StructNew");
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 23eed7923..04c832869 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -286,7 +286,9 @@ private:
Expression* makeRefTest(Element& s);
Expression* makeRefCast(Element& s);
Expression* makeRefCastNop(Element& s);
- Expression* makeBrOn(Element& s, BrOnOp op);
+ Expression* makeBrOnNull(Element& s, bool onFail = false);
+ Expression*
+ makeBrOnCast(Element& s, std::optional<Type> castType, bool onFail = false);
Expression* makeStructNew(Element& s, bool default_);
Index getStructIndex(Element& type, Element& field);
Expression* makeStructGet(Element& s, bool signed_ = false);
diff --git a/src/wasm.h b/src/wasm.h
index db10fe260..ae88bc4d9 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -583,12 +583,6 @@ enum BrOnOp {
BrOnNonNull,
BrOnCast,
BrOnCastFail,
- BrOnFunc,
- BrOnNonFunc,
- BrOnData,
- BrOnNonData,
- BrOnI31,
- BrOnNonI31,
};
enum StringNewOp {
@@ -1552,7 +1546,6 @@ public:
BrOnOp op;
Name name;
Expression* ref;
-
Type castType;
void finalize();
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");
}