diff options
author | Thomas Lively <tlively@google.com> | 2023-01-09 16:23:57 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-09 14:23:57 -0800 |
commit | 67abc2a1b9adcdf080387a29e0c92b6f5a31057a (patch) | |
tree | d8e8304e9ecdd700b56b949bb5132b26e09362f4 | |
parent | e3d9b82d9f8910063373e2952582de545659d448 (diff) | |
download | binaryen-67abc2a1b9adcdf080387a29e0c92b6f5a31057a.tar.gz binaryen-67abc2a1b9adcdf080387a29e0c92b6f5a31057a.tar.bz2 binaryen-67abc2a1b9adcdf080387a29e0c92b6f5a31057a.zip |
Replace `RefIs` with `RefIsNull` (#5401)
* Replace `RefIs` with `RefIsNull`
The other `ref.is*` instructions are deprecated and expressible in terms of
`ref.test`. Update binary and text parsing to parse those instructions as
`RefTest` expressions. Also update the printing and emitting of `RefTest`
expressions to emit the legacy instructions for now to minimize test changes and
make this a mostly non-functional change. Since `ref.is_null` is the only
`RefIs` instruction left, remove the `RefIsOp` field and rename the expression
class to `RefIsNull`.
The few test changes are due to the fact that `ref.is*` instructions are now
subject to `ref.test` validation, and in particular it is no longer valid to
perform a `ref.is_func` on a value outside of the `func` type hierarchy.
46 files changed, 209 insertions, 553 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 466e98e77..43407c611 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ Current Trunk - The isorecursive WasmGC type system (i.e. --hybrid) is now the default to match the spec and the old default equirecursive (i.e. --structural) system has been removed. +- `ref.is_func`, `ref.is_data`, and `ref.is_i31` have been removed from the C + and JS APIs and `RefIs` has been replaced with `RefIsNull`. v111 ---- diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index 46b62711a..232429fe0 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -539,7 +539,7 @@ instructions = [ # reference types instructions ("ref.null", "makeRefNull(s)"), - ("ref.is_null", "makeRefIs(s, RefIsNull)"), + ("ref.is_null", "makeRefIsNull(s)"), ("ref.func", "makeRefFunc(s)"), ("ref.eq", "makeRefEq(s)"), # table instructions @@ -602,9 +602,9 @@ instructions = [ ("array.set", "makeArraySet(s)"), ("array.len", "makeArrayLen(s)"), ("array.copy", "makeArrayCopy(s)"), - ("ref.is_func", "makeRefIs(s, RefIsFunc)"), - ("ref.is_data", "makeRefIs(s, RefIsData)"), - ("ref.is_i31", "makeRefIs(s, RefIsI31)"), + ("ref.is_func", "makeRefTest(s, Type(HeapType::func, NonNullable))"), + ("ref.is_data", "makeRefTest(s, Type(HeapType::data, NonNullable))"), + ("ref.is_i31", "makeRefTest(s, Type(HeapType::i31, NonNullable))"), ("ref.as_non_null", "makeRefAs(s, RefAsNonNull)"), ("ref.as_func", "makeRefAs(s, RefAsFunc)"), ("ref.as_data", "makeRefAs(s, RefAsData)"), diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 7ff827aac..416c8d45b 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -1008,10 +1008,6 @@ BinaryenOp BinaryenPromoteLowVecF32x4ToVecF64x2(void) { return PromoteLowVecF32x4ToVecF64x2; } BinaryenOp BinaryenSwizzleVecI8x16(void) { return SwizzleVecI8x16; } -BinaryenOp BinaryenRefIsNull(void) { return RefIsNull; } -BinaryenOp BinaryenRefIsFunc(void) { return RefIsFunc; } -BinaryenOp BinaryenRefIsData(void) { return RefIsData; } -BinaryenOp BinaryenRefIsI31(void) { return RefIsI31; } BinaryenOp BinaryenRefAsNonNull(void) { return RefAsNonNull; } BinaryenOp BinaryenRefAsFunc(void) { return RefAsFunc; } BinaryenOp BinaryenRefAsData(void) { return RefAsData; } @@ -1598,11 +1594,10 @@ BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module, Builder(*(Module*)module).makeRefNull(type_.getHeapType())); } -BinaryenExpressionRef BinaryenRefIs(BinaryenModuleRef module, - BinaryenOp op, - BinaryenExpressionRef value) { +BinaryenExpressionRef BinaryenRefIsNull(BinaryenModuleRef module, + BinaryenExpressionRef value) { return static_cast<Expression*>( - Builder(*(Module*)module).makeRefIs(RefIsOp(op), (Expression*)value)); + Builder(*(Module*)module).makeRefIsNull((Expression*)value)); } BinaryenExpressionRef BinaryenRefAs(BinaryenModuleRef module, @@ -3579,28 +3574,18 @@ void BinaryenMemoryFillSetSize(BinaryenExpressionRef expr, assert(sizeExpr); static_cast<MemoryFill*>(expression)->size = (Expression*)sizeExpr; } -// RefIs -BinaryenOp BinaryenRefIsGetOp(BinaryenExpressionRef expr) { - auto* expression = (Expression*)expr; - assert(expression->is<RefIs>()); - return static_cast<RefIs*>(expression)->op; -} -void BinaryenRefIsSetOp(BinaryenExpressionRef expr, BinaryenOp op) { +// RefIsNull +BinaryenExpressionRef BinaryenRefIsNullGetValue(BinaryenExpressionRef expr) { auto* expression = (Expression*)expr; - assert(expression->is<RefIs>()); - static_cast<RefIs*>(expression)->op = RefIsOp(op); + assert(expression->is<RefIsNull>()); + return static_cast<RefIsNull*>(expression)->value; } -BinaryenExpressionRef BinaryenRefIsGetValue(BinaryenExpressionRef expr) { - auto* expression = (Expression*)expr; - assert(expression->is<RefIs>()); - return static_cast<RefIs*>(expression)->value; -} -void BinaryenRefIsSetValue(BinaryenExpressionRef expr, - BinaryenExpressionRef valueExpr) { +void BinaryenRefIsNullSetValue(BinaryenExpressionRef expr, + BinaryenExpressionRef valueExpr) { auto* expression = (Expression*)expr; - assert(expression->is<RefIs>()); + assert(expression->is<RefIsNull>()); assert(valueExpr); - static_cast<RefIs*>(expression)->value = (Expression*)valueExpr; + static_cast<RefIsNull*>(expression)->value = (Expression*)valueExpr; } // RefAs BinaryenOp BinaryenRefAsGetOp(BinaryenExpressionRef expr) { diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 171d1248d..2c861a141 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -670,10 +670,6 @@ BINARYEN_API BinaryenOp BinaryenTruncSatZeroUVecF64x2ToVecI32x4(void); BINARYEN_API BinaryenOp BinaryenDemoteZeroVecF64x2ToVecF32x4(void); BINARYEN_API BinaryenOp BinaryenPromoteLowVecF32x4ToVecF64x2(void); BINARYEN_API BinaryenOp BinaryenSwizzleVecI8x16(void); -BINARYEN_API BinaryenOp BinaryenRefIsNull(void); -BINARYEN_API BinaryenOp BinaryenRefIsFunc(void); -BINARYEN_API BinaryenOp BinaryenRefIsData(void); -BINARYEN_API BinaryenOp BinaryenRefIsI31(void); BINARYEN_API BinaryenOp BinaryenRefAsNonNull(void); BINARYEN_API BinaryenOp BinaryenRefAsFunc(void); BINARYEN_API BinaryenOp BinaryenRefAsData(void); @@ -967,9 +963,8 @@ BinaryenMemoryFill(BinaryenModuleRef module, const char* memoryName); BINARYEN_API BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module, BinaryenType type); -BINARYEN_API BinaryenExpressionRef BinaryenRefIs(BinaryenModuleRef module, - BinaryenOp op, - BinaryenExpressionRef value); +BINARYEN_API BinaryenExpressionRef +BinaryenRefIsNull(BinaryenModuleRef module, BinaryenExpressionRef value); BINARYEN_API BinaryenExpressionRef BinaryenRefAs(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef value); @@ -2092,18 +2087,13 @@ BinaryenMemoryFillGetSize(BinaryenExpressionRef expr); BINARYEN_API void BinaryenMemoryFillSetSize(BinaryenExpressionRef expr, BinaryenExpressionRef sizeExpr); -// RefIs +// RefIsNull -// Gets the operation performed by a `ref.is_*` expression. -BINARYEN_API BinaryenOp BinaryenRefIsGetOp(BinaryenExpressionRef expr); -// Sets the operation performed by a `ref.is_*` expression. -BINARYEN_API void BinaryenRefIsSetOp(BinaryenExpressionRef expr, BinaryenOp op); -// Gets the value expression tested by a `ref.is_*` expression. BINARYEN_API BinaryenExpressionRef -BinaryenRefIsGetValue(BinaryenExpressionRef expr); -// Sets the value expression tested by a `ref.is_*` expression. -BINARYEN_API void BinaryenRefIsSetValue(BinaryenExpressionRef expr, - BinaryenExpressionRef valueExpr); +BinaryenRefIsNullGetValue(BinaryenExpressionRef expr); +// Sets the value expression tested by a `ref.is_null` expression. +BINARYEN_API void BinaryenRefIsNullSetValue(BinaryenExpressionRef expr, + BinaryenExpressionRef valueExpr); // RefAs diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index acae3af50..70644827c 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -3050,16 +3050,16 @@ switch (buf[0]) { case 'i': { switch (buf[7]) { case 'd': - if (op == "ref.is_data"sv) { return makeRefIs(s, RefIsData); } + if (op == "ref.is_data"sv) { return makeRefTest(s, Type(HeapType::data, NonNullable)); } goto parse_error; case 'f': - if (op == "ref.is_func"sv) { return makeRefIs(s, RefIsFunc); } + if (op == "ref.is_func"sv) { return makeRefTest(s, Type(HeapType::func, NonNullable)); } goto parse_error; case 'i': - if (op == "ref.is_i31"sv) { return makeRefIs(s, RefIsI31); } + if (op == "ref.is_i31"sv) { return makeRefTest(s, Type(HeapType::i31, NonNullable)); } goto parse_error; case 'n': - if (op == "ref.is_null"sv) { return makeRefIs(s, RefIsNull); } + if (op == "ref.is_null"sv) { return makeRefIsNull(s); } goto parse_error; default: goto parse_error; } @@ -8689,28 +8689,28 @@ switch (buf[0]) { switch (buf[7]) { case 'd': if (op == "ref.is_data"sv) { - auto ret = makeRefIs(ctx, pos, RefIsData); + auto ret = makeRefTest(ctx, pos, Type(HeapType::data, NonNullable)); CHECK_ERR(ret); return *ret; } goto parse_error; case 'f': if (op == "ref.is_func"sv) { - auto ret = makeRefIs(ctx, pos, RefIsFunc); + auto ret = makeRefTest(ctx, pos, Type(HeapType::func, NonNullable)); CHECK_ERR(ret); return *ret; } goto parse_error; case 'i': if (op == "ref.is_i31"sv) { - auto ret = makeRefIs(ctx, pos, RefIsI31); + auto ret = makeRefTest(ctx, pos, Type(HeapType::i31, NonNullable)); CHECK_ERR(ret); return *ret; } goto parse_error; case 'n': if (op == "ref.is_null"sv) { - auto ret = makeRefIs(ctx, pos, RefIsNull); + auto ret = makeRefIsNull(ctx, pos); CHECK_ERR(ret); return *ret; } diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 5ae6a67ce..a5651c6b6 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -128,7 +128,7 @@ void ReFinalize::visitReturn(Return* curr) { curr->finalize(); } void ReFinalize::visitMemorySize(MemorySize* curr) { curr->finalize(); } void ReFinalize::visitMemoryGrow(MemoryGrow* curr) { curr->finalize(); } void ReFinalize::visitRefNull(RefNull* curr) { curr->finalize(); } -void ReFinalize::visitRefIs(RefIs* curr) { curr->finalize(); } +void ReFinalize::visitRefIsNull(RefIsNull* curr) { curr->finalize(); } void ReFinalize::visitRefFunc(RefFunc* curr) { // TODO: should we look up the function and update the type from there? This // could handle a change to the function's type, but is also not really what diff --git a/src/ir/boolean.h b/src/ir/boolean.h index 58601c2ce..68dfb8b32 100644 --- a/src/ir/boolean.h +++ b/src/ir/boolean.h @@ -26,8 +26,8 @@ inline bool emitsBoolean(Expression* curr) { return unary->isRelational(); } else if (auto* binary = curr->dynCast<Binary>()) { return binary->isRelational(); - } else if (curr->is<RefIs>() || curr->is<RefEq>() || curr->is<RefTest>() || - curr->is<StringEq>()) { + } else if (curr->is<RefIsNull>() || curr->is<RefEq>() || + curr->is<RefTest>() || curr->is<StringEq>()) { return true; } return false; diff --git a/src/ir/cost.h b/src/ir/cost.h index 59b738b1b..11c179d40 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -561,7 +561,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { return 1 + visit(curr->left) + visit(curr->right); } CostType visitRefNull(RefNull* curr) { return 1; } - CostType visitRefIs(RefIs* curr) { return 1 + visit(curr->value); } + CostType visitRefIsNull(RefIsNull* curr) { return 1 + visit(curr->value); } CostType visitRefFunc(RefFunc* curr) { return 1; } CostType visitRefEq(RefEq* curr) { return 1 + visit(curr->left) + visit(curr->right); diff --git a/src/ir/effects.h b/src/ir/effects.h index 70e9192c0..4ea577f7a 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -648,7 +648,7 @@ private: parent.isAtomic = true; } void visitRefNull(RefNull* curr) {} - void visitRefIs(RefIs* curr) {} + void visitRefIsNull(RefIsNull* curr) {} void visitRefFunc(RefFunc* curr) {} void visitRefEq(RefEq* curr) {} void visitTableGet(TableGet* curr) { diff --git a/src/ir/gc-type-utils.h b/src/ir/gc-type-utils.h index 1646e47c3..5f1b4807c 100644 --- a/src/ir/gc-type-utils.h +++ b/src/ir/gc-type-utils.h @@ -75,24 +75,6 @@ inline EvaluationResult evaluateKindCheck(Expression* curr) { WASM_UNREACHABLE("unhandled BrOn"); } child = br->ref; - } else if (auto* is = curr->dynCast<RefIs>()) { - switch (is->op) { - // We don't check nullability here. - case RefIsNull: - return Unknown; - case RefIsFunc: - expected = Func; - break; - case RefIsData: - expected = Data; - break; - case RefIsI31: - expected = I31; - break; - default: - WASM_UNREACHABLE("unhandled RefIs"); - } - child = is->value; } else if (auto* as = curr->dynCast<RefAs>()) { switch (as->op) { // We don't check nullability here. diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 52b05057f..cf30cd917 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -587,7 +587,7 @@ struct InfoCollector curr, PossibleContents::literal(Literal::makeNull(curr->type.getHeapType()))); } - void visitRefIs(RefIs* curr) { + void visitRefIsNull(RefIsNull* curr) { // TODO: Optimize when possible. For example, if we can infer an exact type // here which allows us to know the result then we should do so. This // is unlike the case in visitUnary, above: the information that lets diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index d4395359e..2a08c5e5a 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -92,7 +92,7 @@ function initializeConstants() { 'MemoryCopy', 'MemoryFill', 'RefNull', - 'RefIs', + 'RefIsNull', 'RefFunc', 'RefEq', 'TableGet', @@ -549,10 +549,6 @@ function initializeConstants() { 'DemoteZeroVecF64x2ToVecF32x4', 'PromoteLowVecF32x4ToVecF64x2', 'SwizzleVecI8x16', - 'RefIsNull', - 'RefIsFunc', - 'RefIsData', - 'RefIsI31', 'RefAsNonNull', 'RefAsFunc', 'RefAsData', @@ -2344,16 +2340,7 @@ function wrapModule(module, self = {}) { return Module['_BinaryenRefNull'](module, type); }, 'is_null'(value) { - return Module['_BinaryenRefIs'](module, Module['RefIsNull'], value); - }, - 'is_func'(value) { - return Module['_BinaryenRefIs'](module, Module['RefIsFunc'], value); - }, - 'is_data'(value) { - return Module['_BinaryenRefIs'](module, Module['RefIsData'], value); - }, - 'is_i31'(value) { - return Module['_BinaryenRefIs'](module, Module['RefIsI31'], value); + return Module['_BinaryenRefIsNull'](module, value); }, 'as_non_null'(value) { return Module['_BinaryenRefAs'](module, Module['RefAsNonNull'], value); @@ -3203,12 +3190,11 @@ Module['getExpressionInfo'] = function(expr) { 'id': id, 'type': type }; - case Module['RefIsId']: + case Module['RefIsNullId']: return { 'id': id, 'type': type, - 'op': Module['_BinaryenRefIsGetOp'](expr), - 'value': Module['_BinaryenRefIsGetValue'](expr) + 'value': Module['_BinaryenRefIsNullGetValue'](expr) }; case Module['RefAsId']: return { @@ -4622,18 +4608,12 @@ Module['MemoryFill'] = makeExpressionWrapper({ } }); -Module['RefIs'] = makeExpressionWrapper({ - 'getOp'(expr) { - return Module['_BinaryenRefIsGetOp'](expr); - }, - 'setOp'(expr, op) { - Module['_BinaryenRefIsSetOp'](expr, op); - }, +Module['RefIsNull'] = makeExpressionWrapper({ 'getValue'(expr) { - return Module['_BinaryenRefIsGetValue'](expr); + return Module['_BinaryenRefIsNullGetValue'](expr); }, 'setValue'(expr, valueExpr) { - Module['_BinaryenRefIsSetValue'](expr, valueExpr); + Module['_BinaryenRefIsNullSetValue'](expr, valueExpr); } }); diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp index 41083567c..4475db7b0 100644 --- a/src/passes/Inlining.cpp +++ b/src/passes/Inlining.cpp @@ -834,7 +834,7 @@ private: if (auto* unary = curr->dynCast<Unary>()) { return isSimple(unary->value); } - if (auto* is = curr->dynCast<RefIs>()) { + if (auto* is = curr->dynCast<RefIsNull>()) { return isSimple(is->value); } return false; diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 7902eaebd..36f5fbc25 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1705,7 +1705,7 @@ struct OptimizeInstructions // RefEq of a value to Null can be replaced with RefIsNull. if (curr->right->is<RefNull>()) { - replaceCurrent(Builder(*getModule()).makeRefIs(RefIsNull, curr->left)); + replaceCurrent(Builder(*getModule()).makeRefIsNull(curr->left)); } } @@ -2154,78 +2154,27 @@ struct OptimizeInstructions } } - void visitRefIs(RefIs* curr) { + void visitRefIsNull(RefIsNull* curr) { if (curr->type == Type::unreachable) { return; } - // Optimizating RefIs is not that obvious, since even if we know the result - // evaluates to 0 or 1 then the replacement may not actually save code size, - // since RefIsNull is a single byte (the others are 2), while adding a Const - // of 0 would be two bytes. Other factors are that we can remove the input - // and the added drop on it if it has no side effects, and that replacing - // with a constant may allow further optimizations later. For now, replace - // with a constant, but this warrants more investigation. TODO + // Optimizing RefIsNull is not that obvious, since even if we know the + // result evaluates to 0 or 1 then the replacement may not actually save + // code size, since RefIsNull is a single byte while adding a Const of 0 + // would be two bytes. Other factors are that we can remove the input and + // the added drop on it if it has no side effects, and that replacing with a + // constant may allow further optimizations later. For now, replace with a + // constant, but this warrants more investigation. TODO Builder builder(*getModule()); - - auto nonNull = !curr->value->type.isNullable(); - - if (curr->op == RefIsNull) { - if (nonNull) { - replaceCurrent(builder.makeSequence( - builder.makeDrop(curr->value), - builder.makeConst(Literal::makeZero(Type::i32)))); - } else { - // See the comment on the other call to this lower down. Because of that - // other code path we run this optimization at the end (though in this - // code path it would be fine either way). - skipCast(curr->value); - } - return; - } - - // Check if the type is the kind we are checking for. - auto result = GCTypeUtils::evaluateKindCheck(curr); - - if (result != GCTypeUtils::Unknown) { - // We know the kind. Now we must also take into account nullability. - if (nonNull) { - // We know the entire result. - replaceCurrent( - builder.makeSequence(builder.makeDrop(curr->value), - builder.makeConst(Literal::makeFromInt32( - result == GCTypeUtils::Success, Type::i32)))); - } else { - // The value may be null. Leave only a check for that. - curr->op = RefIsNull; - if (result == GCTypeUtils::Success) { - // The input is of the right kind. If it is not null then the result - // is 1, and otherwise it is 0, so we need to flip the result of - // RefIsNull. - // Note that even after adding an eqz here we do not regress code size - // as RefIsNull is a single byte while the others are two. So we keep - // code size identical. However, in theory this may be more work, if - // a VM considers ref.is_X to be as fast as ref.is_null, and if eqz is - // not free, so this is worth more investigation. TODO - replaceCurrent(builder.makeUnary(EqZInt32, curr)); - } else { - // The input is of the wrong kind. In this case if it is null we - // return zero because of that, and if it is not then we return zero - // because of the kind, so the result is always the same. - assert(result == GCTypeUtils::Failure); - replaceCurrent(builder.makeSequence( - builder.makeDrop(curr->value), - builder.makeConst(Literal::makeZero(Type::i32)))); - } - } + if (curr->value->type.isNonNullable()) { + replaceCurrent( + builder.makeSequence(builder.makeDrop(curr->value), + builder.makeConst(Literal::makeZero(Type::i32)))); + } else { + skipCast(curr->value); } - - // What the reference points to does not depend on the type, so casts - // may be removable. Do this right before returning because removing a - // cast may remove info that we could have used to optimize, see - // "notes on removing casts". - skipCast(curr->value); } void visitRefAs(RefAs* curr) { diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 988b1d251..12c185e69 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2000,24 +2000,7 @@ struct PrintExpressionContents printMedium(o, "ref.null "); printHeapType(o, curr->type.getHeapType(), wasm); } - void visitRefIs(RefIs* curr) { - switch (curr->op) { - case RefIsNull: - printMedium(o, "ref.is_null"); - break; - case RefIsFunc: - printMedium(o, "ref.is_func"); - break; - case RefIsData: - printMedium(o, "ref.is_data"); - break; - case RefIsI31: - printMedium(o, "ref.is_i31"); - break; - default: - WASM_UNREACHABLE("unimplemented ref.is_*"); - } - } + void visitRefIsNull(RefIsNull* curr) { printMedium(o, "ref.is_null"); } void visitRefFunc(RefFunc* curr) { printMedium(o, "ref.func "); printName(curr->func, o); @@ -2109,6 +2092,23 @@ struct PrintExpressionContents printHeapType(o, curr->target->type.getHeapType(), wasm); } void visitRefTest(RefTest* curr) { + // TODO: These instructions are deprecated. Remove them. + if (auto type = curr->castType.getHeapType(); + curr->castType.isNonNullable() && type.isBasic()) { + switch (type.getBasic()) { + case HeapType::func: + printMedium(o, "ref.is_func"); + return; + case HeapType::data: + printMedium(o, "ref.is_data"); + return; + case HeapType::i31: + printMedium(o, "ref.is_i31"); + return; + default: + break; + } + } printMedium(o, "ref.test "); if (curr->castType.isNullable()) { printMedium(o, "null "); diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index cda14c989..18568271b 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -2954,11 +2954,10 @@ Expression* TranslateToFuzzReader::makeBulkMemory(Type type) { WASM_UNREACHABLE("invalid value"); } -// TODO: support other RefIs variants, and rename this Expression* TranslateToFuzzReader::makeRefIsNull(Type type) { assert(type == Type::i32); assert(wasm.features.hasReferenceTypes()); - return builder.makeRefIs(RefIsNull, make(getReferenceType())); + return builder.makeRefIsNull(make(getReferenceType())); } Expression* TranslateToFuzzReader::makeRefEq(Type type) { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 303c81128..1a3e5c82b 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1738,7 +1738,8 @@ public: void visitUnreachable(Unreachable* curr); void visitDrop(Drop* curr); void visitRefNull(RefNull* curr); - void visitRefIs(RefIs* curr, uint8_t code); + void visitRefIsNull(RefIsNull* curr); + void visitRefIs(RefTest* curr, uint8_t code); void visitRefFunc(RefFunc* curr); void visitRefEq(RefEq* curr); void visitTableGet(TableGet* curr); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 915c189ae..263ee80fd 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -716,9 +716,8 @@ public: ret->finalize(type); return ret; } - RefIs* makeRefIs(RefIsOp op, Expression* value) { - auto* ret = wasm.allocator.alloc<RefIs>(); - ret->op = op; + RefIsNull* makeRefIsNull(Expression* value) { + auto* ret = wasm.allocator.alloc<RefIsNull>(); ret->value = value; ret->finalize(); return ret; diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index f929def4a..019cdbc0d 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -491,11 +491,10 @@ switch (DELEGATE_ID) { DELEGATE_END(RefNull); break; } - case Expression::Id::RefIsId: { - DELEGATE_START(RefIs); - DELEGATE_FIELD_INT(RefIs, op); - DELEGATE_FIELD_CHILD(RefIs, value); - DELEGATE_END(RefIs); + case Expression::Id::RefIsNullId: { + DELEGATE_START(RefIsNull); + DELEGATE_FIELD_CHILD(RefIsNull, value); + DELEGATE_END(RefIsNull); break; } case Expression::Id::RefFuncId: { diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def index dbdef3a86..d2b5dfa2e 100644 --- a/src/wasm-delegations.def +++ b/src/wasm-delegations.def @@ -55,7 +55,7 @@ DELEGATE(MemoryGrow); DELEGATE(Unreachable); DELEGATE(Pop); DELEGATE(RefNull); -DELEGATE(RefIs); +DELEGATE(RefIsNull); DELEGATE(RefFunc); DELEGATE(RefEq); DELEGATE(TableGet); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 181114c92..104f06585 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1336,26 +1336,15 @@ public: NOTE_ENTER("RefNull"); return Literal::makeNull(curr->type.getHeapType()); } - Flow visitRefIs(RefIs* curr) { - NOTE_ENTER("RefIs"); + Flow visitRefIsNull(RefIsNull* curr) { + NOTE_ENTER("RefIsNull"); Flow flow = visit(curr->value); if (flow.breaking()) { return flow; } const auto& value = flow.getSingleValue(); NOTE_EVAL1(value); - switch (curr->op) { - case RefIsNull: - return Literal(value.isNull()); - case RefIsFunc: - return Literal(value.type.isFunction()); - case RefIsData: - return Literal(value.isData()); - case RefIsI31: - return Literal(value.type.getHeapType() == HeapType::i31); - default: - WASM_UNREACHABLE("unimplemented ref.is_*"); - } + return Literal(int32_t(value.isNull())); } Flow visitRefFunc(RefFunc* curr) { NOTE_ENTER("RefFunc"); diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 04c832869..6ae3e57be 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -267,7 +267,7 @@ private: Expression* makeBreakTable(Element& s); Expression* makeReturn(Element& s); Expression* makeRefNull(Element& s); - Expression* makeRefIs(Element& s, RefIsOp op); + Expression* makeRefIsNull(Element& s); Expression* makeRefFunc(Element& s); Expression* makeRefEq(Element& s); Expression* makeTableGet(Element& s); @@ -283,7 +283,8 @@ private: Expression* makeCallRef(Element& s, bool isReturn); Expression* makeI31New(Element& s); Expression* makeI31Get(Element& s, bool signed_); - Expression* makeRefTest(Element& s); + Expression* makeRefTest(Element& s, + std::optional<Type> castType = std::nullopt); Expression* makeRefCast(Element& s); Expression* makeRefCastNop(Element& s); Expression* makeBrOnNull(Element& s, bool onFail = false); diff --git a/src/wasm.h b/src/wasm.h index ae88bc4d9..7b75ec3b8 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -557,13 +557,6 @@ enum SIMDTernaryOp { DotI8x16I7x16AddSToVecI32x4, }; -enum RefIsOp { - RefIsNull, - RefIsFunc, - RefIsData, - RefIsI31, -}; - enum RefAsOp { RefAsNonNull, RefAsFunc, @@ -696,7 +689,7 @@ public: MemoryFillId, PopId, RefNullId, - RefIsId, + RefIsNullId, RefFuncId, RefEqId, TableGetId, @@ -1346,12 +1339,9 @@ public: void finalize(Type type); }; -class RefIs : public SpecificExpression<Expression::RefIsId> { +class RefIsNull : public SpecificExpression<Expression::RefIsNullId> { public: - RefIs(MixedArena& allocator) {} - - // RefIs can represent ref.is_null, ref.is_func, ref.is_data, and ref.is_i31. - RefIsOp op; + RefIsNull(MixedArena& allocator) {} Expression* value; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 2639599d5..b1f4b8907 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -3795,7 +3795,7 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { visitRefNull((curr = allocator.alloc<RefNull>())->cast<RefNull>()); break; case BinaryConsts::RefIsNull: - visitRefIs((curr = allocator.alloc<RefIs>())->cast<RefIs>(), code); + visitRefIsNull((curr = allocator.alloc<RefIsNull>())->cast<RefIsNull>()); break; case BinaryConsts::RefFunc: visitRefFunc((curr = allocator.alloc<RefFunc>())->cast<RefFunc>()); @@ -4026,7 +4026,8 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { if (opcode == BinaryConsts::RefIsFunc || opcode == BinaryConsts::RefIsData || opcode == BinaryConsts::RefIsI31) { - visitRefIs((curr = allocator.alloc<RefIs>())->cast<RefIs>(), opcode); + visitRefIs((curr = allocator.alloc<RefTest>())->cast<RefTest>(), + opcode); break; } if (opcode == BinaryConsts::RefAsFunc || @@ -6591,25 +6592,28 @@ void WasmBinaryBuilder::visitRefNull(RefNull* curr) { curr->finalize(getHeapType().getBottom()); } -void WasmBinaryBuilder::visitRefIs(RefIs* curr, uint8_t code) { +void WasmBinaryBuilder::visitRefIsNull(RefIsNull* curr) { + BYN_TRACE("zz node: RefIsNull\n"); + curr->value = popNonVoidExpression(); + curr->finalize(); +} + +void WasmBinaryBuilder::visitRefIs(RefTest* curr, uint8_t code) { BYN_TRACE("zz node: RefIs\n"); switch (code) { - case BinaryConsts::RefIsNull: - curr->op = RefIsNull; - break; case BinaryConsts::RefIsFunc: - curr->op = RefIsFunc; + curr->castType = Type(HeapType::func, NonNullable); break; case BinaryConsts::RefIsData: - curr->op = RefIsData; + curr->castType = Type(HeapType::data, NonNullable); break; case BinaryConsts::RefIsI31: - curr->op = RefIsI31; + curr->castType = Type(HeapType::i31, NonNullable); break; default: WASM_UNREACHABLE("invalid code for ref.is_*"); } - curr->value = popNonVoidExpression(); + curr->ref = popNonVoidExpression(); curr->finalize(); } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 638dfbebf..4f725d48b 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2536,9 +2536,8 @@ Expression* SExpressionWasmBuilder::makeRefNull(Element& s) { return ret; } -Expression* SExpressionWasmBuilder::makeRefIs(Element& s, RefIsOp op) { - auto ret = allocator.alloc<RefIs>(); - ret->op = op; +Expression* SExpressionWasmBuilder::makeRefIsNull(Element& s) { + auto ret = allocator.alloc<RefIsNull>(); ret->value = parseExpression(s[1]); ret->finalize(); return ret; @@ -2775,16 +2774,20 @@ Expression* SExpressionWasmBuilder::makeI31Get(Element& s, bool signed_) { return ret; } -Expression* SExpressionWasmBuilder::makeRefTest(Element& s) { +Expression* SExpressionWasmBuilder::makeRefTest(Element& s, + std::optional<Type> castType) { int i = 1; - auto nullability = NonNullable; - if (s[0]->str().str != "ref.test_static" && s[1]->str().str == "null") { - nullability = Nullable; - ++i; + if (!castType) { + auto nullability = NonNullable; + if (s[0]->str().str != "ref.test_static" && s[1]->str().str == "null") { + nullability = Nullable; + ++i; + } + auto type = parseHeapType(*s[i++]); + castType = Type(type, nullability); } - auto heapType = parseHeapType(*s[i++]); auto* ref = parseExpression(*s[i++]); - return Builder(wasm).makeRefTest(ref, Type(heapType, nullability)); + return Builder(wasm).makeRefTest(ref, *castType); } Expression* SExpressionWasmBuilder::makeRefCast(Element& s) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 24710f206..82c7c511f 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1875,23 +1875,8 @@ void BinaryInstWriter::visitRefNull(RefNull* curr) { parent.writeHeapType(curr->type.getHeapType()); } -void BinaryInstWriter::visitRefIs(RefIs* curr) { - switch (curr->op) { - case RefIsNull: - o << int8_t(BinaryConsts::RefIsNull); - break; - case RefIsFunc: - o << int8_t(BinaryConsts::GCPrefix) << int8_t(BinaryConsts::RefIsFunc); - break; - case RefIsData: - o << int8_t(BinaryConsts::GCPrefix) << int8_t(BinaryConsts::RefIsData); - break; - case RefIsI31: - o << int8_t(BinaryConsts::GCPrefix) << int8_t(BinaryConsts::RefIsI31); - break; - default: - WASM_UNREACHABLE("unimplemented ref.is_*"); - } +void BinaryInstWriter::visitRefIsNull(RefIsNull* curr) { + o << int8_t(BinaryConsts::RefIsNull); } void BinaryInstWriter::visitRefFunc(RefFunc* curr) { @@ -2025,6 +2010,23 @@ void BinaryInstWriter::visitCallRef(CallRef* curr) { void BinaryInstWriter::visitRefTest(RefTest* curr) { o << int8_t(BinaryConsts::GCPrefix); + // TODO: These instructions are deprecated. Remove them. + if (auto type = curr->castType.getHeapType(); + curr->castType.isNonNullable() && type.isBasic()) { + switch (type.getBasic()) { + case HeapType::func: + o << U32LEB(BinaryConsts::RefIsFunc); + return; + case HeapType::data: + o << U32LEB(BinaryConsts::RefIsData); + return; + case HeapType::i31: + o << U32LEB(BinaryConsts::RefIsI31); + return; + default: + break; + } + } if (curr->castType.isNullable()) { o << U32LEB(BinaryConsts::RefTestNull); } else { diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 486f13d24..28526cb33 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -427,7 +427,7 @@ public: void visitMemorySize(MemorySize* curr); void visitMemoryGrow(MemoryGrow* curr); void visitRefNull(RefNull* curr); - void visitRefIs(RefIs* curr); + void visitRefIsNull(RefIsNull* curr); void visitRefAs(RefAs* curr); void visitRefFunc(RefFunc* curr); void visitRefEq(RefEq* curr); @@ -2132,14 +2132,15 @@ void FunctionValidator::visitRefNull(RefNull* curr) { curr->type.isNull(), curr, "ref.null must have a bottom heap type"); } -void FunctionValidator::visitRefIs(RefIs* curr) { - shouldBeTrue(getModule()->features.hasReferenceTypes(), - curr, - "ref.is_* requires reference-types [--enable-reference-types]"); +void FunctionValidator::visitRefIsNull(RefIsNull* curr) { + shouldBeTrue( + getModule()->features.hasReferenceTypes(), + curr, + "ref.is_null requires reference-types [--enable-reference-types]"); shouldBeTrue(curr->value->type == Type::unreachable || curr->value->type.isRef(), curr->value, - "ref.is_*'s argument should be a reference type"); + "ref.is_null's argument should be a reference type"); } void FunctionValidator::visitRefAs(RefAs* curr) { diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index c6779a328..c121ba89a 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -805,7 +805,7 @@ void RefNull::finalize(Type type_) { type = type_; } void RefNull::finalize() {} -void RefIs::finalize() { +void RefIsNull::finalize() { if (value->type == Type::unreachable) { type = Type::unreachable; } else { diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp index 14d30db1c..08eec41c6 100644 --- a/src/wasm/wat-parser.cpp +++ b/src/wasm/wat-parser.cpp @@ -758,7 +758,7 @@ struct NullInstrParserCtx { template<typename HeapTypeT> InstrT makeRefNull(Index, HeapTypeT) { return Ok{}; } - InstrT makeRefIs(Index, RefIsOp) { return Ok{}; } + InstrT makeRefIsNull(Index) { return Ok{}; } InstrT makeRefEq(Index) { return Ok{}; } @@ -2053,10 +2053,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { return push(pos, builder.makeRefNull(type)); } - Result<> makeRefIs(Index pos, RefIsOp op) { + Result<> makeRefIsNull(Index pos) { auto ref = pop(pos); CHECK_ERR(ref); - return push(pos, builder.makeRefIs(op, *ref)); + return push(pos, builder.makeRefIsNull(*ref)); } Result<> makeRefEq(Index pos) { @@ -2322,8 +2322,7 @@ template<typename Ctx> Result<typename Ctx::InstrT> makeBreak(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeBreakTable(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeReturn(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeRefNull(Ctx&, Index); -template<typename Ctx> -Result<typename Ctx::InstrT> makeRefIs(Ctx&, Index, RefIsOp op); +template<typename Ctx> Result<typename Ctx::InstrT> makeRefIsNull(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeRefFunc(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeRefEq(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeTableGet(Ctx&, Index); @@ -2344,8 +2343,9 @@ Result<typename Ctx::InstrT> makeCallRef(Ctx&, Index, bool isReturn); template<typename Ctx> Result<typename Ctx::InstrT> makeI31New(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeI31Get(Ctx&, Index, bool signed_); -template<typename Ctx> Result<typename Ctx::InstrT> makeRefTest(Ctx&, Index); -template<typename Ctx> Result<typename Ctx::InstrT> makeRefTest(Ctx&, Index); +template<typename Ctx> +Result<typename Ctx::InstrT> +makeRefTest(Ctx&, Index, std::optional<Type> castType = std::nullopt); template<typename Ctx> Result<typename Ctx::InstrT> makeRefCast(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeRefCastNop(Ctx&, Index); template<typename Ctx> @@ -3357,8 +3357,8 @@ Result<typename Ctx::InstrT> makeRefNull(Ctx& ctx, Index pos) { } template<typename Ctx> -Result<typename Ctx::InstrT> makeRefIs(Ctx& ctx, Index pos, RefIsOp op) { - return ctx.makeRefIs(pos, op); +Result<typename Ctx::InstrT> makeRefIsNull(Ctx& ctx, Index pos) { + return ctx.makeRefIsNull(pos); } template<typename Ctx> @@ -3438,7 +3438,8 @@ Result<typename Ctx::InstrT> makeI31Get(Ctx& ctx, Index pos, bool signed_) { } template<typename Ctx> -Result<typename Ctx::InstrT> makeRefTest(Ctx& ctx, Index pos) { +Result<typename Ctx::InstrT> +makeRefTest(Ctx& ctx, Index pos, std::optional<Type> castType) { return ctx.in.err("unimplemented instruction"); } diff --git a/src/wasm2js.h b/src/wasm2js.h index 25931db85..eddb26866 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2206,7 +2206,7 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, unimplemented(curr); WASM_UNREACHABLE("unimp"); } - Ref visitRefIs(RefIs* curr) { + Ref visitRefIsNull(RefIsNull* curr) { unimplemented(curr); WASM_UNREACHABLE("unimp"); } diff --git a/test/binaryen.js/expressions.js b/test/binaryen.js/expressions.js index c0bc402a0..ee55ee90b 100644 --- a/test/binaryen.js/expressions.js +++ b/test/binaryen.js/expressions.js @@ -1417,51 +1417,30 @@ console.log("# MemoryFill"); module.dispose(); })(); -console.log("# RefIs"); -(function testRefIs() { +console.log("# RefIsNull"); +(function testRefIsNull() { const module = new binaryen.Module(); - var op = binaryen.Operations.RefIsNull; var value = module.local.get(1, binaryen.externref); - const theRefIs = binaryen.RefIs(module.ref.is_null(value)); - assert(theRefIs instanceof binaryen.RefIs); - assert(theRefIs instanceof binaryen.Expression); - assert(theRefIs.op === op); - assert(theRefIs.value === value); - assert(theRefIs.type === binaryen.i32); - - theRefIs.op = op = binaryen.Operations.RefIsFunc; - assert(theRefIs.op === op); - theRefIs.op = op = binaryen.Operations.RefIsNull; - theRefIs.value = value = module.local.get(2, binaryen.externref); - assert(theRefIs.value === value); - theRefIs.type = binaryen.f64; - theRefIs.finalize(); - assert(theRefIs.type === binaryen.i32); - - console.log(theRefIs.toText()); + const theRefIsNull = binaryen.RefIsNull(module.ref.is_null(value)); + assert(theRefIsNull instanceof binaryen.RefIsNull); + assert(theRefIsNull instanceof binaryen.Expression); + assert(theRefIsNull.value === value); + assert(theRefIsNull.type === binaryen.i32); + + theRefIsNull.value = value = module.local.get(2, binaryen.externref); + assert(theRefIsNull.value === value); + theRefIsNull.type = binaryen.f64; + theRefIsNull.finalize(); + assert(theRefIsNull.type === binaryen.i32); + + console.log(theRefIsNull.toText()); assert( - theRefIs.toText() + theRefIsNull.toText() == "(ref.is_null\n (local.get $2)\n)\n" ); - assert( - binaryen.RefIs(module.ref.is_func(value)).toText() - == - "(ref.is_func\n (local.get $2)\n)\n" - ); - assert( - binaryen.RefIs(module.ref.is_data(value)).toText() - == - "(ref.is_data\n (local.get $2)\n)\n" - ); - assert( - binaryen.RefIs(module.ref.is_i31(value)).toText() - == - "(ref.is_i31\n (local.get $2)\n)\n" - ); - module.dispose(); })(); diff --git a/test/binaryen.js/expressions.js.txt b/test/binaryen.js/expressions.js.txt index fbf69888a..75f3a8d54 100644 --- a/test/binaryen.js/expressions.js.txt +++ b/test/binaryen.js/expressions.js.txt @@ -214,7 +214,7 @@ (i32.const 6) ) -# RefIs +# RefIsNull (ref.is_null (local.get $2) ) diff --git a/test/binaryen.js/kitchen-sink.js b/test/binaryen.js/kitchen-sink.js index 52d8fd16f..0de23883a 100644 --- a/test/binaryen.js/kitchen-sink.js +++ b/test/binaryen.js/kitchen-sink.js @@ -145,7 +145,7 @@ function test_ids() { console.log("MemoryFillId: " + binaryen.MemoryFillId); console.log("PopId: " + binaryen.PopId); console.log("RefNullId: " + binaryen.RefNullId); - console.log("RefIsId: " + binaryen.RefIsId); + console.log("RefIsNullId: " + binaryen.RefIsNullId); console.log("RefFuncId: " + binaryen.RefFuncId); console.log("RefEqId: " + binaryen.RefEqId); console.log("TableGetId: " + binaryen.TableGetId); diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index c59c2dac1..4798c8592 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -75,7 +75,7 @@ MemoryCopyId: 38 MemoryFillId: 39 PopId: 40 RefNullId: 41 -RefIsId: 42 +RefIsNullId: 42 RefFuncId: 43 RefEqId: 44 TableGetId: 45 diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index de105d239..cc2c96538 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -1002,8 +1002,8 @@ void test_core() { iIfF, BinaryenTypeInt32()), // Reference types - BinaryenRefIs(module, BinaryenRefIsNull(), externrefExpr), - BinaryenRefIs(module, BinaryenRefIsNull(), funcrefExpr), + BinaryenRefIsNull(module, externrefExpr), + BinaryenRefIsNull(module, funcrefExpr), BinaryenSelect( module, temp10, @@ -1014,15 +1014,6 @@ void test_core() { BinaryenRefEq(module, BinaryenRefNull(module, BinaryenTypeNullref()), BinaryenRefNull(module, BinaryenTypeNullref())), - BinaryenRefIs(module, - BinaryenRefIsFunc(), - BinaryenRefNull(module, BinaryenTypeNullref())), - BinaryenRefIs(module, - BinaryenRefIsData(), - BinaryenRefNull(module, BinaryenTypeNullref())), - BinaryenRefIs(module, - BinaryenRefIsI31(), - BinaryenRefNull(module, BinaryenTypeNullref())), BinaryenRefAs(module, BinaryenRefAsNonNull(), BinaryenRefNull(module, BinaryenTypeNullref())), diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index 1ee529eb3..51298d1a2 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -2004,21 +2004,6 @@ BinaryenFeatureAll: 126975 ) ) (drop - (ref.is_func - (ref.null none) - ) - ) - (drop - (ref.is_data - (ref.null none) - ) - ) - (drop - (ref.is_i31 - (ref.null none) - ) - ) - (drop (ref.as_non_null (ref.null none) ) diff --git a/test/heap-types.wast b/test/heap-types.wast index f75622142..5171d5a66 100644 --- a/test/heap-types.wast +++ b/test/heap-types.wast @@ -182,7 +182,6 @@ (unreachable) ) (func $ref.is_X (param $x anyref) - (if (ref.is_func (local.get $x)) (unreachable)) (if (ref.is_data (local.get $x)) (unreachable)) (if (ref.is_i31 (local.get $x)) (unreachable)) ) diff --git a/test/heap-types.wast.from-wast b/test/heap-types.wast.from-wast index 9eafd633b..b551a9073 100644 --- a/test/heap-types.wast.from-wast +++ b/test/heap-types.wast.from-wast @@ -177,12 +177,6 @@ ) (func $ref.is_X (type $anyref_=>_none) (param $x anyref) (if - (ref.is_func - (local.get $x) - ) - (unreachable) - ) - (if (ref.is_data (local.get $x) ) diff --git a/test/heap-types.wast.fromBinary b/test/heap-types.wast.fromBinary index 04e9bbe05..485611fef 100644 --- a/test/heap-types.wast.fromBinary +++ b/test/heap-types.wast.fromBinary @@ -175,12 +175,6 @@ ) (func $ref.is_X (type $anyref_=>_none) (param $x anyref) (if - (ref.is_func - (local.get $x) - ) - (unreachable) - ) - (if (ref.is_data (local.get $x) ) diff --git a/test/heap-types.wast.fromBinary.noDebugInfo b/test/heap-types.wast.fromBinary.noDebugInfo index 472c3288f..03047def7 100644 --- a/test/heap-types.wast.fromBinary.noDebugInfo +++ b/test/heap-types.wast.fromBinary.noDebugInfo @@ -175,12 +175,6 @@ ) (func $2 (type $anyref_=>_none) (param $0 anyref) (if - (ref.is_func - (local.get $0) - ) - (unreachable) - ) - (if (ref.is_data (local.get $0) ) diff --git a/test/lit/cast-to-basic.wast b/test/lit/cast-to-basic.wast index 239fd820c..9433c3ba1 100644 --- a/test/lit/cast-to-basic.wast +++ b/test/lit/cast-to-basic.wast @@ -6,7 +6,7 @@ (module ;; CHECK: (func $test (type $none_=>_i32) (result i32) - ;; CHECK-NEXT: (ref.test data + ;; CHECK-NEXT: (ref.is_data ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/inlining_splitting.wast b/test/lit/passes/inlining_splitting.wast index efd6b3c7d..2f840a494 100644 --- a/test/lit/passes/inlining_splitting.wast +++ b/test/lit/passes/inlining_splitting.wast @@ -1163,7 +1163,7 @@ ) ;; A second if. We can outline both if bodies. (if - (ref.is_func + (ref.is_null (local.get $x) ) (loop $x @@ -1200,7 +1200,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (ref.is_func + ;; CHECK-NEXT: (ref.is_null ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call $byn-split-outlined-B$multi-if_0 @@ -1231,7 +1231,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (ref.is_func + ;; CHECK-NEXT: (ref.is_null ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call $byn-split-outlined-B$multi-if_0 diff --git a/test/lit/passes/optimize-instructions-gc-tnh.wast b/test/lit/passes/optimize-instructions-gc-tnh.wast index fec4c17f0..67f15de6c 100644 --- a/test/lit/passes/optimize-instructions-gc-tnh.wast +++ b/test/lit/passes/optimize-instructions-gc-tnh.wast @@ -140,7 +140,7 @@ ) ) - ;; TNH: (func $ref.is_func_a (type $anyref_=>_i32) (param $a anyref) (result i32) + ;; TNH: (func $ref.is_func (type $anyref_=>_i32) (param $a anyref) (result i32) ;; TNH-NEXT: (drop ;; TNH-NEXT: (ref.as_func ;; TNH-NEXT: (local.get $a) @@ -148,7 +148,7 @@ ;; TNH-NEXT: ) ;; TNH-NEXT: (i32.const 1) ;; TNH-NEXT: ) - ;; NO_TNH: (func $ref.is_func_a (type $anyref_=>_i32) (param $a anyref) (result i32) + ;; NO_TNH: (func $ref.is_func (type $anyref_=>_i32) (param $a anyref) (result i32) ;; NO_TNH-NEXT: (drop ;; NO_TNH-NEXT: (ref.as_func ;; NO_TNH-NEXT: (local.get $a) @@ -156,7 +156,7 @@ ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: (i32.const 1) ;; NO_TNH-NEXT: ) - (func $ref.is_func_a (param $a (ref null any)) (result i32) + (func $ref.is_func (param $a (ref null any)) (result i32) ;; The check must succeed. We can return 1 here, and drop the rest, with or ;; without TNH (in particular, TNH should not just remove the cast but not ;; return a 1). @@ -167,31 +167,6 @@ ) ) - ;; TNH: (func $ref.is_func_b (type $anyref_=>_i32) (param $a anyref) (result i32) - ;; TNH-NEXT: (drop - ;; TNH-NEXT: (ref.as_data - ;; TNH-NEXT: (local.get $a) - ;; TNH-NEXT: ) - ;; TNH-NEXT: ) - ;; TNH-NEXT: (i32.const 0) - ;; TNH-NEXT: ) - ;; NO_TNH: (func $ref.is_func_b (type $anyref_=>_i32) (param $a anyref) (result i32) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (ref.as_data - ;; NO_TNH-NEXT: (local.get $a) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (i32.const 0) - ;; NO_TNH-NEXT: ) - (func $ref.is_func_b (param $a (ref null any)) (result i32) - ;; A case where the type cannot match, and we return 0. - (ref.is_func - (ref.as_data - (local.get $a) - ) - ) - ) - ;; TNH: (func $if.arm.null (type $i32_ref|$struct|_=>_none) (param $x i32) (param $ref (ref $struct)) ;; TNH-NEXT: (struct.set $struct 0 ;; TNH-NEXT: (block (result (ref $struct)) diff --git a/test/lit/passes/optimize-instructions-gc.wast b/test/lit/passes/optimize-instructions-gc.wast index c0ea515d0..42b915c38 100644 --- a/test/lit/passes/optimize-instructions-gc.wast +++ b/test/lit/passes/optimize-instructions-gc.wast @@ -238,17 +238,13 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (ref.is_null - ;; CHECK-NEXT: (local.get $func) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.is_func + ;; CHECK-NEXT: (local.get $func) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (ref.is_null - ;; CHECK-NEXT: (local.get $i31) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.is_i31 + ;; CHECK-NEXT: (local.get $i31) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -259,17 +255,13 @@ ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (i32.eqz - ;; NOMNL-NEXT: (ref.is_null - ;; NOMNL-NEXT: (local.get $func) - ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (ref.is_func + ;; NOMNL-NEXT: (local.get $func) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (i32.eqz - ;; NOMNL-NEXT: (ref.is_null - ;; NOMNL-NEXT: (local.get $i31) - ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (ref.is_i31 + ;; NOMNL-NEXT: (local.get $i31) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) @@ -288,103 +280,6 @@ ) ) - ;; similar to $unneeded_is, but the values are of mixed kind (is_func of - ;; struct, etc.). regardless of nullability the result here is always 0. - ;; CHECK: (func $unneeded_is_bad_kinds (type $funcref_dataref_i31ref_=>_none) (param $func funcref) (param $struct dataref) (param $i31 i31ref) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $struct) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $func) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.as_non_null - ;; CHECK-NEXT: (local.get $struct) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.as_non_null - ;; CHECK-NEXT: (local.get $func) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; NOMNL: (func $unneeded_is_bad_kinds (type $funcref_dataref_i31ref_=>_none) (param $func funcref) (param $struct dataref) (param $i31 i31ref) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (block (result i32) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (local.get $struct) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (i32.const 0) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (block (result i32) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (local.get $func) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (i32.const 0) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (block (result i32) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (ref.as_non_null - ;; NOMNL-NEXT: (local.get $struct) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (i32.const 0) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (block (result i32) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (ref.as_non_null - ;; NOMNL-NEXT: (local.get $func) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (i32.const 0) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - (func $unneeded_is_bad_kinds - (param $func (ref null func)) - (param $struct (ref null struct)) - (param $i31 (ref null i31)) - (drop - (ref.is_func (local.get $struct)) - ) - (drop - (ref.is_i31 (local.get $func)) - ) - ;; also check non-nullable types as inputs - (drop - (ref.is_func (ref.as_non_null (local.get $struct))) - ) - (drop - (ref.is_i31 (ref.as_non_null (local.get $func))) - ) - ) - ;; ref.as_non_null is not needed on a non-nullable value, and if something is ;; a func we don't need that either etc., and can just return the value. ;; CHECK: (func $unneeded_as (type $ref|$struct|_ref|func|_ref|i31|_=>_none) (param $struct (ref $struct)) (param $func (ref func)) (param $i31 (ref i31)) @@ -2952,7 +2847,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.is_func + ;; CHECK-NEXT: (ref.is_null ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -2970,7 +2865,7 @@ ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (ref.is_func + ;; NOMNL-NEXT: (ref.is_null ;; NOMNL-NEXT: (local.get $x) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) @@ -2994,7 +2889,7 @@ ;; likewise ref.is and ref.test (drop (i32.and - (ref.is_func + (ref.is_null (local.get $x) ) (i32.const 1) diff --git a/test/lit/wat-kitchen-sink.wast b/test/lit/wat-kitchen-sink.wast index 889b82fc4..00801b0c5 100644 --- a/test/lit/wat-kitchen-sink.wast +++ b/test/lit/wat-kitchen-sink.wast @@ -59,7 +59,7 @@ ;; CHECK: (type $none_=>_i32_i64 (func (result i32 i64))) - ;; CHECK: (type $anyref_=>_none (func (param anyref))) + ;; CHECK: (type $anyref_=>_i32 (func (param anyref) (result i32))) ;; CHECK: (type $eqref_eqref_=>_i32 (func (param eqref eqref) (result i32))) @@ -1398,41 +1398,14 @@ return ) - ;; CHECK: (func $ref-is (type $anyref_=>_none) (param $0 anyref) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.is_null - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.is_func - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.is_data - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.is_i31 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) + ;; CHECK: (func $ref-is-null (type $anyref_=>_i32) (param $0 anyref) (result i32) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $ref-is (param anyref) + (func $ref-is-null (param anyref) (result i32) local.get 0 ref.is_null - drop - local.get 0 - ref.is_func - drop - local.get 0 - ref.is_data - drop - local.get 0 - ref.is_i31 - drop ) ;; CHECK: (func $ref-eq (type $eqref_eqref_=>_i32) (param $0 eqref) (param $1 eqref) (result i32) diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt index 8006cb2a6..fb11af060 100644 --- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt +++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt @@ -36,7 +36,7 @@ total RefAs : 4 RefEq : 2 RefFunc : 9 - RefIs : 1 + RefIsNull : 1 RefNull : 5 Return : 29 SIMDExtract : 3 |