summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/gen-s-parser.py24
-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
-rw-r--r--test/heap-types.wast16
-rw-r--r--test/heap-types.wast.from-wast20
-rw-r--r--test/heap-types.wast.fromBinary32
-rw-r--r--test/heap-types.wast.fromBinary.noDebugInfo32
-rw-r--r--test/lit/cast-to-basic.wast2
-rw-r--r--test/lit/passes/gufa-refs.wast48
-rw-r--r--test/passes/remove-unused-brs_all-features.txt43
-rw-r--r--test/passes/remove-unused-brs_all-features.wast53
24 files changed, 200 insertions, 464 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py
index 689c30ae1..46b62711a 100755
--- a/scripts/gen-s-parser.py
+++ b/scripts/gen-s-parser.py
@@ -573,18 +573,18 @@ instructions = [
("ref.cast_static", "makeRefCast(s)"),
("ref.cast_nop", "makeRefCastNop(s)"),
("ref.cast_nop_static", "makeRefCastNop(s)"),
- ("br_on_null", "makeBrOn(s, BrOnNull)"),
- ("br_on_non_null", "makeBrOn(s, BrOnNonNull)"),
- ("br_on_cast", "makeBrOn(s, BrOnCast)"),
- ("br_on_cast_static", "makeBrOn(s, BrOnCast)"),
- ("br_on_cast_fail", "makeBrOn(s, BrOnCastFail)"),
- ("br_on_cast_static_fail", "makeBrOn(s, BrOnCastFail)"),
- ("br_on_func", "makeBrOn(s, BrOnFunc)"),
- ("br_on_non_func", "makeBrOn(s, BrOnNonFunc)"),
- ("br_on_data", "makeBrOn(s, BrOnData)"),
- ("br_on_non_data", "makeBrOn(s, BrOnNonData)"),
- ("br_on_i31", "makeBrOn(s, BrOnI31)"),
- ("br_on_non_i31", "makeBrOn(s, BrOnNonI31)"),
+ ("br_on_null", "makeBrOnNull(s)"),
+ ("br_on_non_null", "makeBrOnNull(s, true)"),
+ ("br_on_cast", "makeBrOnCast(s, std::nullopt)"),
+ ("br_on_cast_static", "makeBrOnCast(s, std::nullopt)"),
+ ("br_on_cast_fail", "makeBrOnCast(s, std::nullopt, true)"),
+ ("br_on_cast_static_fail", "makeBrOnCast(s, std::nullopt, true)"),
+ ("br_on_func", "makeBrOnCast(s, Type(HeapType::func, NonNullable))"),
+ ("br_on_non_func", "makeBrOnCast(s, Type(HeapType::func, NonNullable), true)"),
+ ("br_on_data", "makeBrOnCast(s, Type(HeapType::data, NonNullable))"),
+ ("br_on_non_data", "makeBrOnCast(s, Type(HeapType::data, NonNullable), true)"),
+ ("br_on_i31", "makeBrOnCast(s, Type(HeapType::i31, NonNullable))"),
+ ("br_on_non_i31", "makeBrOnCast(s, Type(HeapType::i31, NonNullable), true)"),
("struct.new", "makeStructNew(s, false)"),
("struct.new_default", "makeStructNew(s, true)"),
("struct.get", "makeStructGet(s)"),
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");
}
diff --git a/test/heap-types.wast b/test/heap-types.wast
index 9366dca01..f75622142 100644
--- a/test/heap-types.wast
+++ b/test/heap-types.wast
@@ -203,14 +203,6 @@
)
)
(drop
- (block $func (result funcref)
- (local.set $y
- (br_on_func $func (local.get $x))
- )
- (ref.null func)
- )
- )
- (drop
(block $i31 (result (ref null i31))
(local.set $y
(br_on_i31 $i31 (local.get $x))
@@ -225,14 +217,6 @@
)
)
(drop
- (block $non-func (result anyref)
- (local.set $temp-func
- (br_on_non_func $non-func (local.get $x))
- )
- (ref.null any)
- )
- )
- (drop
(block $non-i31 (result anyref)
(local.set $temp-i31
(br_on_non_i31 $non-i31 (local.get $x))
diff --git a/test/heap-types.wast.from-wast b/test/heap-types.wast.from-wast
index 9f1a28d13..9eafd633b 100644
--- a/test/heap-types.wast.from-wast
+++ b/test/heap-types.wast.from-wast
@@ -230,16 +230,6 @@
)
)
(drop
- (block $func (result funcref)
- (local.set $y
- (br_on_func $func
- (local.get $x)
- )
- )
- (ref.null nofunc)
- )
- )
- (drop
(block $i31 (result i31ref)
(local.set $y
(br_on_i31 $i31
@@ -258,16 +248,6 @@
)
)
(drop
- (block $non-func (result anyref)
- (local.set $temp-func
- (br_on_non_func $non-func
- (local.get $x)
- )
- )
- (ref.null none)
- )
- )
- (drop
(block $non-i31 (result anyref)
(local.set $temp-i31
(br_on_non_i31 $non-i31
diff --git a/test/heap-types.wast.fromBinary b/test/heap-types.wast.fromBinary
index b217ebc2e..04e9bbe05 100644
--- a/test/heap-types.wast.fromBinary
+++ b/test/heap-types.wast.fromBinary
@@ -228,19 +228,9 @@
)
)
(drop
- (block $label$2 (result funcref)
+ (block $label$2 (result i31ref)
(local.set $y
- (br_on_func $label$2
- (local.get $x)
- )
- )
- (ref.null nofunc)
- )
- )
- (drop
- (block $label$3 (result i31ref)
- (local.set $y
- (br_on_i31 $label$3
+ (br_on_i31 $label$2
(local.get $x)
)
)
@@ -248,27 +238,17 @@
)
)
(drop
- (block $label$4 (result (ref any))
- (br_on_non_null $label$4
+ (block $label$3 (result (ref any))
+ (br_on_non_null $label$3
(local.get $x)
)
(unreachable)
)
)
(drop
- (block $label$5 (result anyref)
- (local.set $temp-func
- (br_on_non_func $label$5
- (local.get $x)
- )
- )
- (ref.null none)
- )
- )
- (drop
- (block $label$6 (result anyref)
+ (block $label$4 (result anyref)
(local.set $temp-i31
- (br_on_non_i31 $label$6
+ (br_on_non_i31 $label$4
(local.get $x)
)
)
diff --git a/test/heap-types.wast.fromBinary.noDebugInfo b/test/heap-types.wast.fromBinary.noDebugInfo
index 89f0c676e..472c3288f 100644
--- a/test/heap-types.wast.fromBinary.noDebugInfo
+++ b/test/heap-types.wast.fromBinary.noDebugInfo
@@ -228,19 +228,9 @@
)
)
(drop
- (block $label$2 (result funcref)
+ (block $label$2 (result i31ref)
(local.set $1
- (br_on_func $label$2
- (local.get $0)
- )
- )
- (ref.null nofunc)
- )
- )
- (drop
- (block $label$3 (result i31ref)
- (local.set $1
- (br_on_i31 $label$3
+ (br_on_i31 $label$2
(local.get $0)
)
)
@@ -248,27 +238,17 @@
)
)
(drop
- (block $label$4 (result (ref any))
- (br_on_non_null $label$4
+ (block $label$3 (result (ref any))
+ (br_on_non_null $label$3
(local.get $0)
)
(unreachable)
)
)
(drop
- (block $label$5 (result anyref)
- (local.set $3
- (br_on_non_func $label$5
- (local.get $0)
- )
- )
- (ref.null none)
- )
- )
- (drop
- (block $label$6 (result anyref)
+ (block $label$4 (result anyref)
(local.set $4
- (br_on_non_i31 $label$6
+ (br_on_non_i31 $label$4
(local.get $0)
)
)
diff --git a/test/lit/cast-to-basic.wast b/test/lit/cast-to-basic.wast
index 1aa329bf5..239fd820c 100644
--- a/test/lit/cast-to-basic.wast
+++ b/test/lit/cast-to-basic.wast
@@ -35,7 +35,7 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $label$1 (result dataref)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (br_on_cast $label$1 data
+ ;; CHECK-NEXT: (br_on_data $label$1
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
diff --git a/test/lit/passes/gufa-refs.wast b/test/lit/passes/gufa-refs.wast
index 9f45ab62a..f1ea265d7 100644
--- a/test/lit/passes/gufa-refs.wast
+++ b/test/lit/passes/gufa-refs.wast
@@ -14,8 +14,6 @@
;; CHECK: (type $none_=>_ref|any| (func (result (ref any))))
- ;; CHECK: (type $none_=>_funcref (func (result funcref)))
-
;; CHECK: (import "a" "b" (func $import (result i32)))
(import "a" "b" (func $import (result i32)))
@@ -58,7 +56,7 @@
(nop)
(ref.as_func
(ref.as_non_null
- (ref.null any)
+ (ref.null func)
)
)
)
@@ -360,50 +358,6 @@
(local.get $z)
)
)
-
- ;; CHECK: (func $nondeterminism (type $none_=>_funcref) (result funcref)
- ;; CHECK-NEXT: (block $label$1 (result funcref)
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (br_on_func $label$1
- ;; CHECK-NEXT: (i31.new
- ;; CHECK-NEXT: (i32.const 1337)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null nofunc)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $nondeterminism (result funcref)
- ;; This block is sent an i31 and a null. The null is compatible with the
- ;; type and the i31 is not. The order in which we process this matters:
- ;;
- ;; * If the i31 arrives first, we'll filter it out as incompatible with the
- ;; type of the block. Then the null arrives and the final result is that
- ;; null, which we can then optimize the block to return.
- ;; * Or, if the null arrives first, then when the i31 arrives the
- ;; combination of nullfunc + i31 is Many (since the types are
- ;; incompatible). We then filter that to the block's type, ending up with
- ;; a cone of funcref. We cannot optimize in that case, unlike before.
- ;;
- ;; Ideally we'd optimize here, but atm we do not since the order in
- ;; practice is a less ideal one. At minimum we should be deterministic in
- ;; how we handle this, which this test enforces at least.
- ;;
- ;; TODO: Find a way to actually optimize such cases, perhaps by filtering
- ;; when sending as well and not just when receiving (the br_on_func
- ;; here should not send anything, as what it sends should be first
- ;; intersected with funcref).
- (block $label$1 (result funcref)
- (drop
- (br_on_func $label$1
- (i31.new
- (i32.const 1337)
- )
- )
- )
- (ref.null nofunc)
- )
- )
)
(module
diff --git a/test/passes/remove-unused-brs_all-features.txt b/test/passes/remove-unused-brs_all-features.txt
index d833e469e..253092e1c 100644
--- a/test/passes/remove-unused-brs_all-features.txt
+++ b/test/passes/remove-unused-brs_all-features.txt
@@ -7,9 +7,8 @@
(type $none_=>_f64 (func (result f64)))
(type $none_=>_i32 (func (result i32)))
(type $i32_=>_funcref (func (param i32) (result funcref)))
- (type $none_=>_none (func))
(import "out" "log" (func $log (param i32)))
- (elem declare func $br_on-to-br $br_on-to-flow $i32_=>_none $none_=>_i32)
+ (elem declare func $br_on-to-br $i32_=>_none $none_=>_i32)
(func $foo (type $none_=>_ref?|$struct|) (result (ref null $struct))
(if (result (ref null $struct))
(i32.const 1)
@@ -114,44 +113,4 @@
)
)
)
- (func $br_on-to-flow (type $none_=>_none)
- (drop
- (block $func (result nullfuncref)
- (drop
- (array.new_default $vector
- (i32.const 2)
- )
- )
- (ref.null nofunc)
- )
- )
- (drop
- (block $funcb (result nullfuncref)
- (drop
- (i31.new
- (i32.const 1337)
- )
- )
- (ref.null nofunc)
- )
- )
- (drop
- (block $i31 (result nullref)
- (drop
- (array.new_default $vector
- (i32.const 2)
- )
- )
- (ref.null none)
- )
- )
- (drop
- (block $i31b (result nullref)
- (drop
- (ref.func $br_on-to-flow)
- )
- (ref.null none)
- )
- )
- )
)
diff --git a/test/passes/remove-unused-brs_all-features.wast b/test/passes/remove-unused-brs_all-features.wast
index 356616b7b..5960e1029 100644
--- a/test/passes/remove-unused-brs_all-features.wast
+++ b/test/passes/remove-unused-brs_all-features.wast
@@ -96,55 +96,4 @@
)
)
)
-
- ;; a br_on of the obviously incorrect kind can just flow out the value as the
- ;; break is never taken
- (func $br_on-to-flow
- ;; brs to func
- (drop
- (block $func (result (ref null func))
- (drop
- (br_on_func $func
- (array.new_default $vector
- (i32.const 2)
- )
- )
- )
- (ref.null func)
- )
- )
- (drop
- (block $funcb (result (ref null func))
- (drop
- (br_on_func $funcb
- (i31.new (i32.const 1337))
- )
- )
- (ref.null func)
- )
- )
- ;; brs to i31
- (drop
- (block $i31 (result (ref null i31))
- (drop
- (br_on_i31 $i31
- (array.new_default $vector
- (i32.const 2)
- )
- )
- )
- (ref.null i31)
- )
- )
- (drop
- (block $i31b (result (ref null i31))
- (drop
- (br_on_i31 $i31b
- (ref.func $br_on-to-flow)
- )
- )
- (ref.null i31)
- )
- )
- )
-)
+) \ No newline at end of file