diff options
37 files changed, 1612 insertions, 504 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index dd053ecee..5bf5ec942 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -496,8 +496,11 @@ instructions = [ ("tuple.make", "makeTupleMake(s)"), ("tuple.extract", "makeTupleExtract(s)"), ("pop", "makePop(s)"), - # GC instructions - ("ref.eq", "makeRefEq(s)") + # GC + ("ref.eq", "makeRefEq(s)"), + ("i31.new", "makeI31New(s)"), + ("i31.get_s", "makeI31Get(s, true)"), + ("i31.get_u", "makeI31Get(s, false)") ] diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 8524b7622..0fcb158d9 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -287,6 +287,8 @@ BinaryenExpressionId BinaryenTupleExtractId(void) { return Expression::Id::TupleExtractId; } BinaryenExpressionId BinaryenPopId(void) { return Expression::Id::PopId; } +BinaryenExpressionId BinaryenI31NewId(void) { return Expression::Id::I31NewId; } +BinaryenExpressionId BinaryenI31GetId(void) { return Expression::Id::I31GetId; } // External kinds @@ -1338,6 +1340,19 @@ BinaryenExpressionRef BinaryenBrOnExn(BinaryenModuleRef module, Builder(*wasm).makeBrOnExn(name, event, (Expression*)exnref)); } +BinaryenExpressionRef BinaryenI31New(BinaryenModuleRef module, + BinaryenExpressionRef value) { + return static_cast<Expression*>( + Builder(*(Module*)module).makeI31New((Expression*)value)); +} + +BinaryenExpressionRef BinaryenI31Get(BinaryenModuleRef module, + BinaryenExpressionRef i31, + int signed_) { + return static_cast<Expression*>( + Builder(*(Module*)module).makeI31Get((Expression*)i31, signed_ != 0)); +} + // Expression utility BinaryenExpressionId BinaryenExpressionGetId(BinaryenExpressionRef expr) { @@ -2998,7 +3013,6 @@ BinaryenTupleMakeRemoveOperandAt(BinaryenExpressionRef expr, assert(expression->is<TupleMake>()); return static_cast<TupleMake*>(expression)->operands.removeAt(index); } - // TupleExtract BinaryenExpressionRef BinaryenTupleExtractGetTuple(BinaryenExpressionRef expr) { auto* expression = (Expression*)expr; @@ -3023,6 +3037,42 @@ void BinaryenTupleExtractSetIndex(BinaryenExpressionRef expr, assert(expression->is<TupleExtract>()); static_cast<TupleExtract*>(expression)->index = index; } +// I31New +BinaryenExpressionRef BinaryenI31NewGetValue(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<I31New>()); + return static_cast<I31New*>(expression)->value; +} +void BinaryenI31NewSetValue(BinaryenExpressionRef expr, + BinaryenExpressionRef valueExpr) { + auto* expression = (Expression*)expr; + assert(expression->is<I31New>()); + assert(valueExpr); + static_cast<I31New*>(expression)->value = (Expression*)valueExpr; +} +// I31Get +BinaryenExpressionRef BinaryenI31GetGetI31(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<I31Get>()); + return static_cast<I31Get*>(expression)->i31; +} +void BinaryenI31GetSetI31(BinaryenExpressionRef expr, + BinaryenExpressionRef i31Expr) { + auto* expression = (Expression*)expr; + assert(expression->is<I31Get>()); + assert(i31Expr); + static_cast<I31Get*>(expression)->i31 = (Expression*)i31Expr; +} +int BinaryenI31GetIsSigned(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<I31Get>()); + return static_cast<I31Get*>(expression)->signed_; +} +void BinaryenI31GetSetSigned(BinaryenExpressionRef expr, int signed_) { + auto* expression = (Expression*)expr; + assert(expression->is<I31Get>()); + static_cast<I31Get*>(expression)->signed_ = signed_ != 0; +} // Functions diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 5ca07316f..f695fa6f0 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -174,6 +174,8 @@ BINARYEN_API BinaryenExpressionId BinaryenBrOnExnId(void); BINARYEN_API BinaryenExpressionId BinaryenTupleMakeId(void); BINARYEN_API BinaryenExpressionId BinaryenTupleExtractId(void); BINARYEN_API BinaryenExpressionId BinaryenPopId(void); +BINARYEN_API BinaryenExpressionId BinaryenI31NewId(void); +BINARYEN_API BinaryenExpressionId BinaryenI31GetId(void); // External kinds (call to get the value of each; you can cache them) @@ -860,6 +862,11 @@ BINARYEN_API BinaryenExpressionRef BinaryenTupleExtract( BinaryenModuleRef module, BinaryenExpressionRef tuple, BinaryenIndex index); BINARYEN_API BinaryenExpressionRef BinaryenPop(BinaryenModuleRef module, BinaryenType type); +BINARYEN_API BinaryenExpressionRef BinaryenI31New(BinaryenModuleRef module, + BinaryenExpressionRef value); +BINARYEN_API BinaryenExpressionRef BinaryenI31Get(BinaryenModuleRef module, + BinaryenExpressionRef i31, + int signed_); // Expression @@ -1852,6 +1859,29 @@ BinaryenTupleExtractGetIndex(BinaryenExpressionRef expr); BINARYEN_API void BinaryenTupleExtractSetIndex(BinaryenExpressionRef expr, BinaryenIndex index); +// I31New + +// Gets the value expression of an `i31.new` expression. +BINARYEN_API BinaryenExpressionRef +BinaryenI31NewGetValue(BinaryenExpressionRef expr); +// Sets the value expression of an `i31.new` expression. +BINARYEN_API void BinaryenI31NewSetValue(BinaryenExpressionRef expr, + BinaryenExpressionRef valueExpr); + +// I31Get + +// Gets the i31 expression of an `i31.get` expression. +BINARYEN_API BinaryenExpressionRef +BinaryenI31GetGetI31(BinaryenExpressionRef expr); +// Sets the i31 expression of an `i31.get` expression. +BINARYEN_API void BinaryenI31GetSetI31(BinaryenExpressionRef expr, + BinaryenExpressionRef i31Expr); +// Gets whether an `i31.get` expression returns a signed value (`_s`). +BINARYEN_API int BinaryenI31GetIsSigned(BinaryenExpressionRef expr); +// Sets whether an `i31.get` expression returns a signed value (`_s`). +BINARYEN_API void BinaryenI31GetSetSigned(BinaryenExpressionRef expr, + int signed_); + // Functions BINARYEN_REF(Function); diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 76616c320..041a6d519 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -960,284 +960,262 @@ switch (op[0]) { } } case '3': { - switch (op[3]) { - case '.': { + switch (op[2]) { + case '1': { switch (op[4]) { - case 'a': { - switch (op[5]) { - case 'd': - if (strcmp(op, "i32.add") == 0) { return makeBinary(s, BinaryOp::AddInt32); } + case 'g': { + switch (op[8]) { + case 's': + if (strcmp(op, "i31.get_s") == 0) { return makeI31Get(s, true); } goto parse_error; - case 'n': - if (strcmp(op, "i32.and") == 0) { return makeBinary(s, BinaryOp::AndInt32); } + case 'u': + if (strcmp(op, "i31.get_u") == 0) { return makeI31Get(s, false); } goto parse_error; - case 't': { - switch (op[11]) { - case 'l': { - switch (op[15]) { - case '\0': - if (strcmp(op, "i32.atomic.load") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/true); } - goto parse_error; - case '1': - if (strcmp(op, "i32.atomic.load16_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/true); } - goto parse_error; - case '8': - if (strcmp(op, "i32.atomic.load8_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/true); } - goto parse_error; - default: goto parse_error; - } - } - case 'r': { - switch (op[14]) { - case '.': { + default: goto parse_error; + } + } + case 'n': + if (strcmp(op, "i31.new") == 0) { return makeI31New(s); } + goto parse_error; + default: goto parse_error; + } + } + case '2': { + switch (op[3]) { + case '.': { + switch (op[4]) { + case 'a': { + switch (op[5]) { + case 'd': + if (strcmp(op, "i32.add") == 0) { return makeBinary(s, BinaryOp::AddInt32); } + goto parse_error; + case 'n': + if (strcmp(op, "i32.and") == 0) { return makeBinary(s, BinaryOp::AndInt32); } + goto parse_error; + case 't': { + switch (op[11]) { + case 'l': { switch (op[15]) { - case 'a': { - switch (op[16]) { - case 'd': - if (strcmp(op, "i32.atomic.rmw.add") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } - goto parse_error; - case 'n': - if (strcmp(op, "i32.atomic.rmw.and") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } - goto parse_error; - default: goto parse_error; - } - } - case 'c': - if (strcmp(op, "i32.atomic.rmw.cmpxchg") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + case '\0': + if (strcmp(op, "i32.atomic.load") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/true); } goto parse_error; - case 'o': - if (strcmp(op, "i32.atomic.rmw.or") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + case '1': + if (strcmp(op, "i32.atomic.load16_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/true); } goto parse_error; - case 's': - if (strcmp(op, "i32.atomic.rmw.sub") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + case '8': + if (strcmp(op, "i32.atomic.load8_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/true); } goto parse_error; - case 'x': { - switch (op[16]) { + default: goto parse_error; + } + } + case 'r': { + switch (op[14]) { + case '.': { + switch (op[15]) { + case 'a': { + switch (op[16]) { + case 'd': + if (strcmp(op, "i32.atomic.rmw.add") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + case 'n': + if (strcmp(op, "i32.atomic.rmw.and") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + default: goto parse_error; + } + } case 'c': - if (strcmp(op, "i32.atomic.rmw.xchg") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + if (strcmp(op, "i32.atomic.rmw.cmpxchg") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'o': - if (strcmp(op, "i32.atomic.rmw.xor") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + if (strcmp(op, "i32.atomic.rmw.or") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + case 's': + if (strcmp(op, "i32.atomic.rmw.sub") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; + case 'x': { + switch (op[16]) { + case 'c': + if (strcmp(op, "i32.atomic.rmw.xchg") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + case 'o': + if (strcmp(op, "i32.atomic.rmw.xor") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } - default: goto parse_error; - } - } - case '1': { - switch (op[17]) { - case 'a': { - switch (op[18]) { - case 'd': - if (strcmp(op, "i32.atomic.rmw16.add_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + case '1': { + switch (op[17]) { + case 'a': { + switch (op[18]) { + case 'd': + if (strcmp(op, "i32.atomic.rmw16.add_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + case 'n': + if (strcmp(op, "i32.atomic.rmw16.and_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + default: goto parse_error; + } + } + case 'c': + if (strcmp(op, "i32.atomic.rmw16.cmpxchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; - case 'n': - if (strcmp(op, "i32.atomic.rmw16.and_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + case 'o': + if (strcmp(op, "i32.atomic.rmw16.or_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; + case 's': + if (strcmp(op, "i32.atomic.rmw16.sub_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + case 'x': { + switch (op[18]) { + case 'c': + if (strcmp(op, "i32.atomic.rmw16.xchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + case 'o': + if (strcmp(op, "i32.atomic.rmw16.xor_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } - case 'c': - if (strcmp(op, "i32.atomic.rmw16.cmpxchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } - goto parse_error; - case 'o': - if (strcmp(op, "i32.atomic.rmw16.or_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } - goto parse_error; - case 's': - if (strcmp(op, "i32.atomic.rmw16.sub_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } - goto parse_error; - case 'x': { - switch (op[18]) { + case '8': { + switch (op[16]) { + case 'a': { + switch (op[17]) { + case 'd': + if (strcmp(op, "i32.atomic.rmw8.add_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + case 'n': + if (strcmp(op, "i32.atomic.rmw8.and_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + default: goto parse_error; + } + } case 'c': - if (strcmp(op, "i32.atomic.rmw16.xchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + if (strcmp(op, "i32.atomic.rmw8.cmpxchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'o': - if (strcmp(op, "i32.atomic.rmw16.xor_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + if (strcmp(op, "i32.atomic.rmw8.or_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; + case 's': + if (strcmp(op, "i32.atomic.rmw8.sub_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + case 'x': { + switch (op[17]) { + case 'c': + if (strcmp(op, "i32.atomic.rmw8.xchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + case 'o': + if (strcmp(op, "i32.atomic.rmw8.xor_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } default: goto parse_error; } } - case '8': { + case 's': { switch (op[16]) { - case 'a': { - switch (op[17]) { - case 'd': - if (strcmp(op, "i32.atomic.rmw8.add_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } - goto parse_error; - case 'n': - if (strcmp(op, "i32.atomic.rmw8.and_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } - goto parse_error; - default: goto parse_error; - } - } - case 'c': - if (strcmp(op, "i32.atomic.rmw8.cmpxchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + case '\0': + if (strcmp(op, "i32.atomic.store") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/true); } goto parse_error; - case 'o': - if (strcmp(op, "i32.atomic.rmw8.or_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + case '1': + if (strcmp(op, "i32.atomic.store16") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/true); } goto parse_error; - case 's': - if (strcmp(op, "i32.atomic.rmw8.sub_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } + case '8': + if (strcmp(op, "i32.atomic.store8") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/true); } goto parse_error; - case 'x': { - switch (op[17]) { - case 'c': - if (strcmp(op, "i32.atomic.rmw8.xchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } - goto parse_error; - case 'o': - if (strcmp(op, "i32.atomic.rmw8.xor_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } - goto parse_error; - default: goto parse_error; - } - } default: goto parse_error; } } - default: goto parse_error; - } - } - case 's': { - switch (op[16]) { - case '\0': - if (strcmp(op, "i32.atomic.store") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/true); } - goto parse_error; - case '1': - if (strcmp(op, "i32.atomic.store16") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/true); } - goto parse_error; - case '8': - if (strcmp(op, "i32.atomic.store8") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/true); } + case 'w': + if (strcmp(op, "i32.atomic.wait") == 0) { return makeAtomicWait(s, Type::i32); } goto parse_error; default: goto parse_error; } } - case 'w': - if (strcmp(op, "i32.atomic.wait") == 0) { return makeAtomicWait(s, Type::i32); } - goto parse_error; default: goto parse_error; } } - default: goto parse_error; - } - } - case 'c': { - switch (op[5]) { - case 'l': - if (strcmp(op, "i32.clz") == 0) { return makeUnary(s, UnaryOp::ClzInt32); } - goto parse_error; - case 'o': - if (strcmp(op, "i32.const") == 0) { return makeConst(s, Type::i32); } - goto parse_error; - case 't': - if (strcmp(op, "i32.ctz") == 0) { return makeUnary(s, UnaryOp::CtzInt32); } - goto parse_error; - default: goto parse_error; - } - } - case 'd': { - switch (op[8]) { - case 's': - if (strcmp(op, "i32.div_s") == 0) { return makeBinary(s, BinaryOp::DivSInt32); } - goto parse_error; - case 'u': - if (strcmp(op, "i32.div_u") == 0) { return makeBinary(s, BinaryOp::DivUInt32); } - goto parse_error; - default: goto parse_error; - } - } - case 'e': { - switch (op[5]) { - case 'q': { - switch (op[6]) { - case '\0': - if (strcmp(op, "i32.eq") == 0) { return makeBinary(s, BinaryOp::EqInt32); } - goto parse_error; - case 'z': - if (strcmp(op, "i32.eqz") == 0) { return makeUnary(s, UnaryOp::EqZInt32); } - goto parse_error; - default: goto parse_error; - } - } - case 'x': { - switch (op[10]) { - case '1': - if (strcmp(op, "i32.extend16_s") == 0) { return makeUnary(s, UnaryOp::ExtendS16Int32); } + case 'c': { + switch (op[5]) { + case 'l': + if (strcmp(op, "i32.clz") == 0) { return makeUnary(s, UnaryOp::ClzInt32); } goto parse_error; - case '8': - if (strcmp(op, "i32.extend8_s") == 0) { return makeUnary(s, UnaryOp::ExtendS8Int32); } + case 'o': + if (strcmp(op, "i32.const") == 0) { return makeConst(s, Type::i32); } goto parse_error; - default: goto parse_error; - } - } - default: goto parse_error; - } - } - case 'g': { - switch (op[5]) { - case 'e': { - switch (op[7]) { - case 's': - if (strcmp(op, "i32.ge_s") == 0) { return makeBinary(s, BinaryOp::GeSInt32); } - goto parse_error; - case 'u': - if (strcmp(op, "i32.ge_u") == 0) { return makeBinary(s, BinaryOp::GeUInt32); } + case 't': + if (strcmp(op, "i32.ctz") == 0) { return makeUnary(s, UnaryOp::CtzInt32); } goto parse_error; default: goto parse_error; } } - case 't': { - switch (op[7]) { + case 'd': { + switch (op[8]) { case 's': - if (strcmp(op, "i32.gt_s") == 0) { return makeBinary(s, BinaryOp::GtSInt32); } + if (strcmp(op, "i32.div_s") == 0) { return makeBinary(s, BinaryOp::DivSInt32); } goto parse_error; case 'u': - if (strcmp(op, "i32.gt_u") == 0) { return makeBinary(s, BinaryOp::GtUInt32); } + if (strcmp(op, "i32.div_u") == 0) { return makeBinary(s, BinaryOp::DivUInt32); } goto parse_error; default: goto parse_error; } } - default: goto parse_error; - } - } - case 'l': { - switch (op[5]) { case 'e': { - switch (op[7]) { - case 's': - if (strcmp(op, "i32.le_s") == 0) { return makeBinary(s, BinaryOp::LeSInt32); } - goto parse_error; - case 'u': - if (strcmp(op, "i32.le_u") == 0) { return makeBinary(s, BinaryOp::LeUInt32); } - goto parse_error; + switch (op[5]) { + case 'q': { + switch (op[6]) { + case '\0': + if (strcmp(op, "i32.eq") == 0) { return makeBinary(s, BinaryOp::EqInt32); } + goto parse_error; + case 'z': + if (strcmp(op, "i32.eqz") == 0) { return makeUnary(s, UnaryOp::EqZInt32); } + goto parse_error; + default: goto parse_error; + } + } + case 'x': { + switch (op[10]) { + case '1': + if (strcmp(op, "i32.extend16_s") == 0) { return makeUnary(s, UnaryOp::ExtendS16Int32); } + goto parse_error; + case '8': + if (strcmp(op, "i32.extend8_s") == 0) { return makeUnary(s, UnaryOp::ExtendS8Int32); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } - case 'o': { - switch (op[8]) { - case '\0': - if (strcmp(op, "i32.load") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } - goto parse_error; - case '1': { - switch (op[11]) { + case 'g': { + switch (op[5]) { + case 'e': { + switch (op[7]) { case 's': - if (strcmp(op, "i32.load16_s") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } + if (strcmp(op, "i32.ge_s") == 0) { return makeBinary(s, BinaryOp::GeSInt32); } goto parse_error; case 'u': - if (strcmp(op, "i32.load16_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } + if (strcmp(op, "i32.ge_u") == 0) { return makeBinary(s, BinaryOp::GeUInt32); } goto parse_error; default: goto parse_error; } } - case '8': { - switch (op[10]) { + case 't': { + switch (op[7]) { case 's': - if (strcmp(op, "i32.load8_s") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } + if (strcmp(op, "i32.gt_s") == 0) { return makeBinary(s, BinaryOp::GtSInt32); } goto parse_error; case 'u': - if (strcmp(op, "i32.load8_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } + if (strcmp(op, "i32.gt_u") == 0) { return makeBinary(s, BinaryOp::GtUInt32); } goto parse_error; default: goto parse_error; } @@ -1245,81 +1223,56 @@ switch (op[0]) { default: goto parse_error; } } - case 't': { - switch (op[7]) { - case 's': - if (strcmp(op, "i32.lt_s") == 0) { return makeBinary(s, BinaryOp::LtSInt32); } - goto parse_error; - case 'u': - if (strcmp(op, "i32.lt_u") == 0) { return makeBinary(s, BinaryOp::LtUInt32); } - goto parse_error; - default: goto parse_error; - } - } - default: goto parse_error; - } - } - case 'm': - if (strcmp(op, "i32.mul") == 0) { return makeBinary(s, BinaryOp::MulInt32); } - goto parse_error; - case 'n': - if (strcmp(op, "i32.ne") == 0) { return makeBinary(s, BinaryOp::NeInt32); } - goto parse_error; - case 'o': - if (strcmp(op, "i32.or") == 0) { return makeBinary(s, BinaryOp::OrInt32); } - goto parse_error; - case 'p': - if (strcmp(op, "i32.popcnt") == 0) { return makeUnary(s, UnaryOp::PopcntInt32); } - goto parse_error; - case 'r': { - switch (op[5]) { - case 'e': { - switch (op[6]) { - case 'i': - if (strcmp(op, "i32.reinterpret_f32") == 0) { return makeUnary(s, UnaryOp::ReinterpretFloat32); } - goto parse_error; - case 'm': { - switch (op[8]) { + case 'l': { + switch (op[5]) { + case 'e': { + switch (op[7]) { case 's': - if (strcmp(op, "i32.rem_s") == 0) { return makeBinary(s, BinaryOp::RemSInt32); } + if (strcmp(op, "i32.le_s") == 0) { return makeBinary(s, BinaryOp::LeSInt32); } goto parse_error; case 'u': - if (strcmp(op, "i32.rem_u") == 0) { return makeBinary(s, BinaryOp::RemUInt32); } + if (strcmp(op, "i32.le_u") == 0) { return makeBinary(s, BinaryOp::LeUInt32); } goto parse_error; default: goto parse_error; } } - default: goto parse_error; - } - } - case 'o': { - switch (op[7]) { - case 'l': - if (strcmp(op, "i32.rotl") == 0) { return makeBinary(s, BinaryOp::RotLInt32); } - goto parse_error; - case 'r': - if (strcmp(op, "i32.rotr") == 0) { return makeBinary(s, BinaryOp::RotRInt32); } - goto parse_error; - default: goto parse_error; - } - } - default: goto parse_error; - } - } - case 's': { - switch (op[5]) { - case 'h': { - switch (op[6]) { - case 'l': - if (strcmp(op, "i32.shl") == 0) { return makeBinary(s, BinaryOp::ShlInt32); } - goto parse_error; - case 'r': { + case 'o': { switch (op[8]) { + case '\0': + if (strcmp(op, "i32.load") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } + goto parse_error; + case '1': { + switch (op[11]) { + case 's': + if (strcmp(op, "i32.load16_s") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } + goto parse_error; + case 'u': + if (strcmp(op, "i32.load16_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } + goto parse_error; + default: goto parse_error; + } + } + case '8': { + switch (op[10]) { + case 's': + if (strcmp(op, "i32.load8_s") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } + goto parse_error; + case 'u': + if (strcmp(op, "i32.load8_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } + goto parse_error; + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 't': { + switch (op[7]) { case 's': - if (strcmp(op, "i32.shr_s") == 0) { return makeBinary(s, BinaryOp::ShrSInt32); } + if (strcmp(op, "i32.lt_s") == 0) { return makeBinary(s, BinaryOp::LtSInt32); } goto parse_error; case 'u': - if (strcmp(op, "i32.shr_u") == 0) { return makeBinary(s, BinaryOp::ShrUInt32); } + if (strcmp(op, "i32.lt_u") == 0) { return makeBinary(s, BinaryOp::LtUInt32); } goto parse_error; default: goto parse_error; } @@ -1327,48 +1280,46 @@ switch (op[0]) { default: goto parse_error; } } - case 't': { - switch (op[9]) { - case '\0': - if (strcmp(op, "i32.store") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/false); } - goto parse_error; - case '1': - if (strcmp(op, "i32.store16") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/false); } - goto parse_error; - case '8': - if (strcmp(op, "i32.store8") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/false); } - goto parse_error; - default: goto parse_error; - } - } - case 'u': - if (strcmp(op, "i32.sub") == 0) { return makeBinary(s, BinaryOp::SubInt32); } + case 'm': + if (strcmp(op, "i32.mul") == 0) { return makeBinary(s, BinaryOp::MulInt32); } goto parse_error; - default: goto parse_error; - } - } - case 't': { - switch (op[10]) { - case 'f': { - switch (op[11]) { - case '3': { - switch (op[14]) { - case 's': - if (strcmp(op, "i32.trunc_f32_s") == 0) { return makeUnary(s, UnaryOp::TruncSFloat32ToInt32); } - goto parse_error; - case 'u': - if (strcmp(op, "i32.trunc_f32_u") == 0) { return makeUnary(s, UnaryOp::TruncUFloat32ToInt32); } + case 'n': + if (strcmp(op, "i32.ne") == 0) { return makeBinary(s, BinaryOp::NeInt32); } + goto parse_error; + case 'o': + if (strcmp(op, "i32.or") == 0) { return makeBinary(s, BinaryOp::OrInt32); } + goto parse_error; + case 'p': + if (strcmp(op, "i32.popcnt") == 0) { return makeUnary(s, UnaryOp::PopcntInt32); } + goto parse_error; + case 'r': { + switch (op[5]) { + case 'e': { + switch (op[6]) { + case 'i': + if (strcmp(op, "i32.reinterpret_f32") == 0) { return makeUnary(s, UnaryOp::ReinterpretFloat32); } goto parse_error; + case 'm': { + switch (op[8]) { + case 's': + if (strcmp(op, "i32.rem_s") == 0) { return makeBinary(s, BinaryOp::RemSInt32); } + goto parse_error; + case 'u': + if (strcmp(op, "i32.rem_u") == 0) { return makeBinary(s, BinaryOp::RemUInt32); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } - case '6': { - switch (op[14]) { - case 's': - if (strcmp(op, "i32.trunc_f64_s") == 0) { return makeUnary(s, UnaryOp::TruncSFloat64ToInt32); } + case 'o': { + switch (op[7]) { + case 'l': + if (strcmp(op, "i32.rotl") == 0) { return makeBinary(s, BinaryOp::RotLInt32); } goto parse_error; - case 'u': - if (strcmp(op, "i32.trunc_f64_u") == 0) { return makeUnary(s, UnaryOp::TruncUFloat64ToInt32); } + case 'r': + if (strcmp(op, "i32.rotr") == 0) { return makeBinary(s, BinaryOp::RotRInt32); } goto parse_error; default: goto parse_error; } @@ -1377,254 +1328,327 @@ switch (op[0]) { } } case 's': { - switch (op[15]) { - case '3': { - switch (op[18]) { - case 's': - if (strcmp(op, "i32.trunc_sat_f32_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSFloat32ToInt32); } - goto parse_error; - case 'u': - if (strcmp(op, "i32.trunc_sat_f32_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUFloat32ToInt32); } + switch (op[5]) { + case 'h': { + switch (op[6]) { + case 'l': + if (strcmp(op, "i32.shl") == 0) { return makeBinary(s, BinaryOp::ShlInt32); } goto parse_error; + case 'r': { + switch (op[8]) { + case 's': + if (strcmp(op, "i32.shr_s") == 0) { return makeBinary(s, BinaryOp::ShrSInt32); } + goto parse_error; + case 'u': + if (strcmp(op, "i32.shr_u") == 0) { return makeBinary(s, BinaryOp::ShrUInt32); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } - case '6': { - switch (op[18]) { - case 's': - if (strcmp(op, "i32.trunc_sat_f64_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSFloat64ToInt32); } + case 't': { + switch (op[9]) { + case '\0': + if (strcmp(op, "i32.store") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/false); } goto parse_error; - case 'u': - if (strcmp(op, "i32.trunc_sat_f64_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUFloat64ToInt32); } + case '1': + if (strcmp(op, "i32.store16") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/false); } + goto parse_error; + case '8': + if (strcmp(op, "i32.store8") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/false); } goto parse_error; default: goto parse_error; } } + case 'u': + if (strcmp(op, "i32.sub") == 0) { return makeBinary(s, BinaryOp::SubInt32); } + goto parse_error; default: goto parse_error; } } - default: goto parse_error; - } - } - case 'w': - if (strcmp(op, "i32.wrap_i64") == 0) { return makeUnary(s, UnaryOp::WrapInt64); } - goto parse_error; - case 'x': - if (strcmp(op, "i32.xor") == 0) { return makeBinary(s, BinaryOp::XorInt32); } - goto parse_error; - default: goto parse_error; - } - } - case 'x': { - switch (op[6]) { - case 'a': { - switch (op[7]) { - case 'b': - if (strcmp(op, "i32x4.abs") == 0) { return makeUnary(s, UnaryOp::AbsVecI32x4); } - goto parse_error; - case 'd': - if (strcmp(op, "i32x4.add") == 0) { return makeBinary(s, BinaryOp::AddVecI32x4); } - goto parse_error; - case 'l': - if (strcmp(op, "i32x4.all_true") == 0) { return makeUnary(s, UnaryOp::AllTrueVecI32x4); } - goto parse_error; - case 'n': - if (strcmp(op, "i32x4.any_true") == 0) { return makeUnary(s, UnaryOp::AnyTrueVecI32x4); } - goto parse_error; - default: goto parse_error; - } - } - case 'b': - if (strcmp(op, "i32x4.bitmask") == 0) { return makeUnary(s, UnaryOp::BitmaskVecI32x4); } - goto parse_error; - case 'd': - if (strcmp(op, "i32x4.dot_i16x8_s") == 0) { return makeBinary(s, BinaryOp::DotSVecI16x8ToVecI32x4); } - goto parse_error; - case 'e': { - switch (op[7]) { - case 'q': - if (strcmp(op, "i32x4.eq") == 0) { return makeBinary(s, BinaryOp::EqVecI32x4); } + case 't': { + switch (op[10]) { + case 'f': { + switch (op[11]) { + case '3': { + switch (op[14]) { + case 's': + if (strcmp(op, "i32.trunc_f32_s") == 0) { return makeUnary(s, UnaryOp::TruncSFloat32ToInt32); } + goto parse_error; + case 'u': + if (strcmp(op, "i32.trunc_f32_u") == 0) { return makeUnary(s, UnaryOp::TruncUFloat32ToInt32); } + goto parse_error; + default: goto parse_error; + } + } + case '6': { + switch (op[14]) { + case 's': + if (strcmp(op, "i32.trunc_f64_s") == 0) { return makeUnary(s, UnaryOp::TruncSFloat64ToInt32); } + goto parse_error; + case 'u': + if (strcmp(op, "i32.trunc_f64_u") == 0) { return makeUnary(s, UnaryOp::TruncUFloat64ToInt32); } + goto parse_error; + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 's': { + switch (op[15]) { + case '3': { + switch (op[18]) { + case 's': + if (strcmp(op, "i32.trunc_sat_f32_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSFloat32ToInt32); } + goto parse_error; + case 'u': + if (strcmp(op, "i32.trunc_sat_f32_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUFloat32ToInt32); } + goto parse_error; + default: goto parse_error; + } + } + case '6': { + switch (op[18]) { + case 's': + if (strcmp(op, "i32.trunc_sat_f64_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSFloat64ToInt32); } + goto parse_error; + case 'u': + if (strcmp(op, "i32.trunc_sat_f64_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUFloat64ToInt32); } + goto parse_error; + default: goto parse_error; + } + } + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'w': + if (strcmp(op, "i32.wrap_i64") == 0) { return makeUnary(s, UnaryOp::WrapInt64); } goto parse_error; case 'x': - if (strcmp(op, "i32x4.extract_lane") == 0) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecI32x4, 4); } + if (strcmp(op, "i32.xor") == 0) { return makeBinary(s, BinaryOp::XorInt32); } goto parse_error; default: goto parse_error; } } - case 'g': { - switch (op[7]) { - case 'e': { - switch (op[9]) { - case 's': - if (strcmp(op, "i32x4.ge_s") == 0) { return makeBinary(s, BinaryOp::GeSVecI32x4); } + case 'x': { + switch (op[6]) { + case 'a': { + switch (op[7]) { + case 'b': + if (strcmp(op, "i32x4.abs") == 0) { return makeUnary(s, UnaryOp::AbsVecI32x4); } goto parse_error; - case 'u': - if (strcmp(op, "i32x4.ge_u") == 0) { return makeBinary(s, BinaryOp::GeUVecI32x4); } + case 'd': + if (strcmp(op, "i32x4.add") == 0) { return makeBinary(s, BinaryOp::AddVecI32x4); } goto parse_error; - default: goto parse_error; - } - } - case 't': { - switch (op[9]) { - case 's': - if (strcmp(op, "i32x4.gt_s") == 0) { return makeBinary(s, BinaryOp::GtSVecI32x4); } + case 'l': + if (strcmp(op, "i32x4.all_true") == 0) { return makeUnary(s, UnaryOp::AllTrueVecI32x4); } goto parse_error; - case 'u': - if (strcmp(op, "i32x4.gt_u") == 0) { return makeBinary(s, BinaryOp::GtUVecI32x4); } + case 'n': + if (strcmp(op, "i32x4.any_true") == 0) { return makeUnary(s, UnaryOp::AnyTrueVecI32x4); } goto parse_error; default: goto parse_error; } } - default: goto parse_error; - } - } - case 'l': { - switch (op[7]) { + case 'b': + if (strcmp(op, "i32x4.bitmask") == 0) { return makeUnary(s, UnaryOp::BitmaskVecI32x4); } + goto parse_error; + case 'd': + if (strcmp(op, "i32x4.dot_i16x8_s") == 0) { return makeBinary(s, BinaryOp::DotSVecI16x8ToVecI32x4); } + goto parse_error; case 'e': { - switch (op[9]) { - case 's': - if (strcmp(op, "i32x4.le_s") == 0) { return makeBinary(s, BinaryOp::LeSVecI32x4); } + switch (op[7]) { + case 'q': + if (strcmp(op, "i32x4.eq") == 0) { return makeBinary(s, BinaryOp::EqVecI32x4); } goto parse_error; - case 'u': - if (strcmp(op, "i32x4.le_u") == 0) { return makeBinary(s, BinaryOp::LeUVecI32x4); } + case 'x': + if (strcmp(op, "i32x4.extract_lane") == 0) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecI32x4, 4); } goto parse_error; default: goto parse_error; } } - case 'o': { - switch (op[15]) { - case 's': - if (strcmp(op, "i32x4.load16x4_s") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadExtSVec16x4ToVecI32x4); } - goto parse_error; - case 'u': - if (strcmp(op, "i32x4.load16x4_u") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadExtUVec16x4ToVecI32x4); } - goto parse_error; + case 'g': { + switch (op[7]) { + case 'e': { + switch (op[9]) { + case 's': + if (strcmp(op, "i32x4.ge_s") == 0) { return makeBinary(s, BinaryOp::GeSVecI32x4); } + goto parse_error; + case 'u': + if (strcmp(op, "i32x4.ge_u") == 0) { return makeBinary(s, BinaryOp::GeUVecI32x4); } + goto parse_error; + default: goto parse_error; + } + } + case 't': { + switch (op[9]) { + case 's': + if (strcmp(op, "i32x4.gt_s") == 0) { return makeBinary(s, BinaryOp::GtSVecI32x4); } + goto parse_error; + case 'u': + if (strcmp(op, "i32x4.gt_u") == 0) { return makeBinary(s, BinaryOp::GtUVecI32x4); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } - case 't': { - switch (op[9]) { - case 's': - if (strcmp(op, "i32x4.lt_s") == 0) { return makeBinary(s, BinaryOp::LtSVecI32x4); } - goto parse_error; - case 'u': - if (strcmp(op, "i32x4.lt_u") == 0) { return makeBinary(s, BinaryOp::LtUVecI32x4); } - goto parse_error; + case 'l': { + switch (op[7]) { + case 'e': { + switch (op[9]) { + case 's': + if (strcmp(op, "i32x4.le_s") == 0) { return makeBinary(s, BinaryOp::LeSVecI32x4); } + goto parse_error; + case 'u': + if (strcmp(op, "i32x4.le_u") == 0) { return makeBinary(s, BinaryOp::LeUVecI32x4); } + goto parse_error; + default: goto parse_error; + } + } + case 'o': { + switch (op[15]) { + case 's': + if (strcmp(op, "i32x4.load16x4_s") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadExtSVec16x4ToVecI32x4); } + goto parse_error; + case 'u': + if (strcmp(op, "i32x4.load16x4_u") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadExtUVec16x4ToVecI32x4); } + goto parse_error; + default: goto parse_error; + } + } + case 't': { + switch (op[9]) { + case 's': + if (strcmp(op, "i32x4.lt_s") == 0) { return makeBinary(s, BinaryOp::LtSVecI32x4); } + goto parse_error; + case 'u': + if (strcmp(op, "i32x4.lt_u") == 0) { return makeBinary(s, BinaryOp::LtUVecI32x4); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } - default: goto parse_error; - } - } - case 'm': { - switch (op[7]) { - case 'a': { - switch (op[10]) { - case 's': - if (strcmp(op, "i32x4.max_s") == 0) { return makeBinary(s, BinaryOp::MaxSVecI32x4); } - goto parse_error; + case 'm': { + switch (op[7]) { + case 'a': { + switch (op[10]) { + case 's': + if (strcmp(op, "i32x4.max_s") == 0) { return makeBinary(s, BinaryOp::MaxSVecI32x4); } + goto parse_error; + case 'u': + if (strcmp(op, "i32x4.max_u") == 0) { return makeBinary(s, BinaryOp::MaxUVecI32x4); } + goto parse_error; + default: goto parse_error; + } + } + case 'i': { + switch (op[10]) { + case 's': + if (strcmp(op, "i32x4.min_s") == 0) { return makeBinary(s, BinaryOp::MinSVecI32x4); } + goto parse_error; + case 'u': + if (strcmp(op, "i32x4.min_u") == 0) { return makeBinary(s, BinaryOp::MinUVecI32x4); } + goto parse_error; + default: goto parse_error; + } + } case 'u': - if (strcmp(op, "i32x4.max_u") == 0) { return makeBinary(s, BinaryOp::MaxUVecI32x4); } + if (strcmp(op, "i32x4.mul") == 0) { return makeBinary(s, BinaryOp::MulVecI32x4); } goto parse_error; default: goto parse_error; } } - case 'i': { - switch (op[10]) { - case 's': - if (strcmp(op, "i32x4.min_s") == 0) { return makeBinary(s, BinaryOp::MinSVecI32x4); } + case 'n': { + switch (op[8]) { + case '\0': + if (strcmp(op, "i32x4.ne") == 0) { return makeBinary(s, BinaryOp::NeVecI32x4); } goto parse_error; - case 'u': - if (strcmp(op, "i32x4.min_u") == 0) { return makeBinary(s, BinaryOp::MinUVecI32x4); } + case 'g': + if (strcmp(op, "i32x4.neg") == 0) { return makeUnary(s, UnaryOp::NegVecI32x4); } goto parse_error; default: goto parse_error; } } - case 'u': - if (strcmp(op, "i32x4.mul") == 0) { return makeBinary(s, BinaryOp::MulVecI32x4); } + case 'r': + if (strcmp(op, "i32x4.replace_lane") == 0) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI32x4, 4); } goto parse_error; - default: goto parse_error; - } - } - case 'n': { - switch (op[8]) { - case '\0': - if (strcmp(op, "i32x4.ne") == 0) { return makeBinary(s, BinaryOp::NeVecI32x4); } - goto parse_error; - case 'g': - if (strcmp(op, "i32x4.neg") == 0) { return makeUnary(s, UnaryOp::NegVecI32x4); } - goto parse_error; - default: goto parse_error; - } - } - case 'r': - if (strcmp(op, "i32x4.replace_lane") == 0) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI32x4, 4); } - goto parse_error; - case 's': { - switch (op[7]) { - case 'h': { - switch (op[8]) { - case 'l': - if (strcmp(op, "i32x4.shl") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShlVecI32x4); } - goto parse_error; - case 'r': { - switch (op[10]) { - case 's': - if (strcmp(op, "i32x4.shr_s") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrSVecI32x4); } - goto parse_error; - case 'u': - if (strcmp(op, "i32x4.shr_u") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrUVecI32x4); } + case 's': { + switch (op[7]) { + case 'h': { + switch (op[8]) { + case 'l': + if (strcmp(op, "i32x4.shl") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShlVecI32x4); } goto parse_error; + case 'r': { + switch (op[10]) { + case 's': + if (strcmp(op, "i32x4.shr_s") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrSVecI32x4); } + goto parse_error; + case 'u': + if (strcmp(op, "i32x4.shr_u") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrUVecI32x4); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } - default: goto parse_error; - } - } - case 'p': - if (strcmp(op, "i32x4.splat") == 0) { return makeUnary(s, UnaryOp::SplatVecI32x4); } - goto parse_error; - case 'u': - if (strcmp(op, "i32x4.sub") == 0) { return makeBinary(s, BinaryOp::SubVecI32x4); } - goto parse_error; - default: goto parse_error; - } - } - case 't': { - switch (op[22]) { - case 's': - if (strcmp(op, "i32x4.trunc_sat_f32x4_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSVecF32x4ToVecI32x4); } - goto parse_error; - case 'u': - if (strcmp(op, "i32x4.trunc_sat_f32x4_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUVecF32x4ToVecI32x4); } - goto parse_error; - default: goto parse_error; - } - } - case 'w': { - switch (op[12]) { - case 'h': { - switch (op[23]) { - case 's': - if (strcmp(op, "i32x4.widen_high_i16x8_s") == 0) { return makeUnary(s, UnaryOp::WidenHighSVecI16x8ToVecI32x4); } + case 'p': + if (strcmp(op, "i32x4.splat") == 0) { return makeUnary(s, UnaryOp::SplatVecI32x4); } goto parse_error; case 'u': - if (strcmp(op, "i32x4.widen_high_i16x8_u") == 0) { return makeUnary(s, UnaryOp::WidenHighUVecI16x8ToVecI32x4); } + if (strcmp(op, "i32x4.sub") == 0) { return makeBinary(s, BinaryOp::SubVecI32x4); } goto parse_error; default: goto parse_error; } } - case 'l': { + case 't': { switch (op[22]) { case 's': - if (strcmp(op, "i32x4.widen_low_i16x8_s") == 0) { return makeUnary(s, UnaryOp::WidenLowSVecI16x8ToVecI32x4); } + if (strcmp(op, "i32x4.trunc_sat_f32x4_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSVecF32x4ToVecI32x4); } goto parse_error; case 'u': - if (strcmp(op, "i32x4.widen_low_i16x8_u") == 0) { return makeUnary(s, UnaryOp::WidenLowUVecI16x8ToVecI32x4); } + if (strcmp(op, "i32x4.trunc_sat_f32x4_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUVecF32x4ToVecI32x4); } goto parse_error; default: goto parse_error; } } + case 'w': { + switch (op[12]) { + case 'h': { + switch (op[23]) { + case 's': + if (strcmp(op, "i32x4.widen_high_i16x8_s") == 0) { return makeUnary(s, UnaryOp::WidenHighSVecI16x8ToVecI32x4); } + goto parse_error; + case 'u': + if (strcmp(op, "i32x4.widen_high_i16x8_u") == 0) { return makeUnary(s, UnaryOp::WidenHighUVecI16x8ToVecI32x4); } + goto parse_error; + default: goto parse_error; + } + } + case 'l': { + switch (op[22]) { + case 's': + if (strcmp(op, "i32x4.widen_low_i16x8_s") == 0) { return makeUnary(s, UnaryOp::WidenLowSVecI16x8ToVecI32x4); } + goto parse_error; + case 'u': + if (strcmp(op, "i32x4.widen_low_i16x8_u") == 0) { return makeUnary(s, UnaryOp::WidenLowUVecI16x8ToVecI32x4); } + goto parse_error; + default: goto parse_error; + } + } + default: goto parse_error; + } + } default: goto parse_error; } } diff --git a/src/ir/ExpressionAnalyzer.cpp b/src/ir/ExpressionAnalyzer.cpp index 766d4023e..562631420 100644 --- a/src/ir/ExpressionAnalyzer.cpp +++ b/src/ir/ExpressionAnalyzer.cpp @@ -235,6 +235,8 @@ template<typename T> void visitImmediates(Expression* curr, T& visitor) { void visitTupleExtract(TupleExtract* curr) { visitor.visitIndex(curr->index); } + void visitI31New(I31New* curr) {} + void visitI31Get(I31Get* curr) { visitor.visitInt(curr->signed_); } } singleton(curr, visitor); } diff --git a/src/ir/ExpressionManipulator.cpp b/src/ir/ExpressionManipulator.cpp index 95fee632f..b0616e34d 100644 --- a/src/ir/ExpressionManipulator.cpp +++ b/src/ir/ExpressionManipulator.cpp @@ -269,6 +269,12 @@ flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) { Expression* visitTupleExtract(TupleExtract* curr) { return builder.makeTupleExtract(copy(curr->tuple), curr->index); } + Expression* visitI31New(I31New* curr) { + return builder.makeI31New(copy(curr->value)); + } + Expression* visitI31Get(I31Get* curr) { + return builder.makeI31Get(copy(curr->i31), curr->signed_); + } }; Copier copier(wasm, custom); diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 99387ada1..c6b3ab855 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -141,6 +141,8 @@ void ReFinalize::visitUnreachable(Unreachable* curr) { curr->finalize(); } void ReFinalize::visitPop(Pop* curr) { curr->finalize(); } void ReFinalize::visitTupleMake(TupleMake* curr) { curr->finalize(); } void ReFinalize::visitTupleExtract(TupleExtract* curr) { curr->finalize(); } +void ReFinalize::visitI31New(I31New* curr) { curr->finalize(); } +void ReFinalize::visitI31Get(I31Get* curr) { curr->finalize(); } void ReFinalize::visitFunction(Function* curr) { // we may have changed the body from unreachable to none, which might be bad diff --git a/src/ir/effects.h b/src/ir/effects.h index a90e902b0..8199092ea 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -505,6 +505,8 @@ struct EffectAnalyzer } void visitTupleMake(TupleMake* curr) {} void visitTupleExtract(TupleExtract* curr) {} + void visitI31New(I31New* curr) {} + void visitI31Get(I31Get* curr) {} // Helpers diff --git a/src/ir/utils.h b/src/ir/utils.h index 428657e88..7d8b28759 100644 --- a/src/ir/utils.h +++ b/src/ir/utils.h @@ -160,6 +160,8 @@ struct ReFinalize void visitPop(Pop* curr); void visitTupleMake(TupleMake* curr); void visitTupleExtract(TupleExtract* curr); + void visitI31New(I31New* curr); + void visitI31Get(I31Get* curr); void visitFunction(Function* curr); @@ -230,6 +232,8 @@ struct ReFinalizeNode : public OverriddenVisitor<ReFinalizeNode> { void visitPop(Pop* curr) { curr->finalize(); } void visitTupleMake(TupleMake* curr) { curr->finalize(); } void visitTupleExtract(TupleExtract* curr) { curr->finalize(); } + void visitI31New(I31New* curr) { curr->finalize(); } + void visitI31Get(I31Get* curr) { curr->finalize(); } void visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); } void visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); } diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index baaa71429..d3cf7d449 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -96,7 +96,9 @@ function initializeConstants() { 'BrOnExn', 'TupleMake', 'TupleExtract', - 'Pop' + 'Pop', + 'I31New', + 'I31Get' ].forEach(name => { Module['ExpressionIds'][name] = Module[name + 'Id'] = Module['_Binaryen' + name + 'Id'](); }); @@ -2148,6 +2150,18 @@ function wrapModule(module, self = {}) { } }; + self['i31'] = { + 'new'(value) { + return Module['_BinaryenI31New'](module, value); + }, + 'get_s'(i31) { + return Module['_BinaryenI31Get'](module, i31, 1); + }, + 'get_u'(i31) { + return Module['_BinaryenI31Get'](module, i31, 0); + } + }; + // 'Module' operations self['addFunction'] = function(name, params, results, varTypes, body) { return preserveStack(() => @@ -2856,6 +2870,19 @@ Module['getExpressionInfo'] = function(expr) { 'tuple': Module['_BinaryenTupleExtractGetTuple'](expr), 'index': Module['_BinaryenTupleExtractGetIndex'](expr) }; + case Module['I31NewId']: + return { + 'id': id, + 'type': type, + 'value': Module['_BinaryenI31NewGetValue'](expr) + }; + case Module['I31GetId']: + return { + 'id': id, + 'type': type, + 'i31': Module['_BinaryenI31GetGetI31'](expr), + 'isSigned': Boolean(Module['_BinaryenI31GetIsSigned'](expr)) + }; default: throw Error('unexpected id: ' + id); @@ -4285,6 +4312,30 @@ Module['TupleExtract'] = makeExpressionWrapper({ } }); +Module['I31New'] = makeExpressionWrapper({ + 'getValue'(expr) { + return Module['_BinaryenI31NewGetValue'](expr); + }, + 'setValue'(expr, valueExpr) { + Module['_BinaryenI31NewSetValue'](expr, valueExpr); + } +}); + +Module['I31Get'] = makeExpressionWrapper({ + 'getI31'(expr) { + return Module['_BinaryenI31GetGetI31'](expr); + }, + 'setI31'(expr, i31Expr) { + Module['_BinaryenI31GetSetI31'](expr, i31Expr); + }, + 'isSigned'(expr) { + return Boolean(Module['_BinaryenI31GetIsSigned'](expr)); + }, + 'setSigned'(expr, isSigned) { + Module['_BinaryenI31GetSetSigned'](expr, isSigned); + } +}); + // Function wrapper Module['Function'] = (() => { diff --git a/src/literal.h b/src/literal.h index 698063cbe..4ae4f969a 100644 --- a/src/literal.h +++ b/src/literal.h @@ -143,6 +143,11 @@ public: static Literal makeExn(std::unique_ptr<ExceptionPackage>&& exn) { return Literal(std::move(exn)); } + static Literal makeI31(int32_t value) { + auto lit = Literal(Type::i31ref); + lit.i32 = value & 0x7fffffff; + return lit; + } Literal castToF32(); Literal castToF64(); @@ -153,6 +158,10 @@ public: assert(type == Type::i32); return i32; } + int32_t geti31(bool signed_) const { + assert(type == Type::i31ref); + return signed_ ? (i32 << 1) >> 1 : i32; + } int64_t geti64() const { assert(type == Type::i64); return i64; diff --git a/src/passes/DeadCodeElimination.cpp b/src/passes/DeadCodeElimination.cpp index bf1c0705a..7562645e0 100644 --- a/src/passes/DeadCodeElimination.cpp +++ b/src/passes/DeadCodeElimination.cpp @@ -375,6 +375,10 @@ struct DeadCodeElimination DELEGATE(TupleMake); case Expression::Id::TupleExtractId: DELEGATE(TupleExtract); + case Expression::Id::I31NewId: + DELEGATE(I31New); + case Expression::Id::I31GetId: + DELEGATE(I31Get); case Expression::Id::InvalidId: WASM_UNREACHABLE("unimp"); case Expression::Id::NumExpressionIds: diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 27c3e327c..ac115edc3 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -1474,6 +1474,10 @@ struct PrintExpressionContents printMedium(o, "tuple.extract "); o << curr->index; } + void visitI31New(I31New* curr) { printMedium(o, "i31.new"); } + void visitI31Get(I31Get* curr) { + printMedium(o, curr->signed_ ? "i31.get_s" : "i31.get_u"); + } }; // Prints an expression in s-expr format, including both the @@ -2063,6 +2067,20 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> { printFullLine(curr->tuple); decIndent(); } + void visitI31New(I31New* curr) { + o << '('; + PrintExpressionContents(currFunction, o).visit(curr); + incIndent(); + printFullLine(curr->value); + decIndent(); + } + void visitI31Get(I31Get* curr) { + o << '('; + PrintExpressionContents(currFunction, o).visit(curr); + incIndent(); + printFullLine(curr->i31); + decIndent(); + } // Module-level visitors void handleSignature(Signature curr, Name* funcName = nullptr) { o << "(func"; diff --git a/src/wasm-binary.h b/src/wasm-binary.h index df800d470..9ac5664d3 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -599,6 +599,7 @@ enum ASTNodes { // prefixes + GCPrefix = 0xfb, MiscPrefix = 0xfc, SIMDPrefix = 0xfd, AtomicPrefix = 0xfe, @@ -937,7 +938,13 @@ enum ASTNodes { Catch = 0x07, Throw = 0x08, Rethrow = 0x09, - BrOnExn = 0x0a + BrOnExn = 0x0a, + + // gc opcodes + + I31New = 0x20, + I31GetS = 0x21, + I31GetU = 0x22 }; enum MemoryAccess { @@ -1452,6 +1459,8 @@ public: bool maybeVisitDataDrop(Expression*& out, uint32_t code); bool maybeVisitMemoryCopy(Expression*& out, uint32_t code); bool maybeVisitMemoryFill(Expression*& out, uint32_t code); + bool maybeVisitI31New(Expression*& out, uint32_t code); + bool maybeVisitI31Get(Expression*& out, uint32_t code); void visitSelect(Select* curr, uint8_t code); void visitReturn(Return* curr); void visitMemorySize(MemorySize* curr); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index e0c00428f..d78640200 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -626,6 +626,19 @@ public: ret->finalize(); return ret; } + I31New* makeI31New(Expression* value) { + auto* ret = wasm.allocator.alloc<I31New>(); + ret->value = value; + ret->finalize(); + return ret; + } + I31Get* makeI31Get(Expression* i31, bool signed_) { + auto* ret = wasm.allocator.alloc<I31Get>(); + ret->i31 = i31; + ret->signed_ = signed_; + ret->finalize(); + return ret; + } // Additional helpers diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 57ec65edd..5e98e0fec 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1324,6 +1324,26 @@ public: flow.breakTo = curr->name; return flow; } + Flow visitI31New(I31New* curr) { + NOTE_ENTER("I31New"); + Flow flow = visit(curr->value); + if (flow.breaking()) { + return flow; + } + const auto& value = flow.getSingleValue(); + NOTE_EVAL1(value); + return Literal::makeI31(value.geti32()); + } + Flow visitI31Get(I31Get* curr) { + NOTE_ENTER("I31Get"); + Flow flow = visit(curr->i31); + if (flow.breaking()) { + return flow; + } + const auto& value = flow.getSingleValue(); + NOTE_EVAL1(value); + return Literal(value.geti31(curr->signed_)); + } virtual void trap(const char* why) { WASM_UNREACHABLE("unimp"); } diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 88fa4d02e..3b0d1a92d 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -243,6 +243,8 @@ private: Expression* makeBrOnExn(Element& s); Expression* makeTupleMake(Element& s); Expression* makeTupleExtract(Element& s); + Expression* makeI31New(Element& s); + Expression* makeI31Get(Element& s, bool signed_); // Helper functions Type parseOptionalResultType(Element& s, Index& i); diff --git a/src/wasm-stack.h b/src/wasm-stack.h index b7a064168..740473106 100644 --- a/src/wasm-stack.h +++ b/src/wasm-stack.h @@ -148,6 +148,8 @@ public: void visitPop(Pop* curr); void visitTupleMake(TupleMake* curr); void visitTupleExtract(TupleExtract* curr); + void visitI31New(I31New* curr); + void visitI31Get(I31Get* curr); void emitResultType(Type type); void emitIfElse(If* curr); diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index 8267f4767..cd83aa1b6 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -86,6 +86,8 @@ template<typename SubType, typename ReturnType = void> struct Visitor { ReturnType visitPop(Pop* curr) { return ReturnType(); } ReturnType visitTupleMake(TupleMake* curr) { return ReturnType(); } ReturnType visitTupleExtract(TupleExtract* curr) { return ReturnType(); } + ReturnType visitI31New(I31New* curr) { return ReturnType(); } + ReturnType visitI31Get(I31Get* curr) { return ReturnType(); } // Module-level visitors ReturnType visitExport(Export* curr) { return ReturnType(); } ReturnType visitGlobal(Global* curr) { return ReturnType(); } @@ -201,6 +203,10 @@ template<typename SubType, typename ReturnType = void> struct Visitor { DELEGATE(TupleMake); case Expression::Id::TupleExtractId: DELEGATE(TupleExtract); + case Expression::Id::I31NewId: + DELEGATE(I31New); + case Expression::Id::I31GetId: + DELEGATE(I31Get); case Expression::Id::InvalidId: default: WASM_UNREACHABLE("unexpected expression type"); @@ -273,6 +279,8 @@ struct OverriddenVisitor { UNIMPLEMENTED(Pop); UNIMPLEMENTED(TupleMake); UNIMPLEMENTED(TupleExtract); + UNIMPLEMENTED(I31New); + UNIMPLEMENTED(I31Get); UNIMPLEMENTED(Export); UNIMPLEMENTED(Global); UNIMPLEMENTED(Function); @@ -389,6 +397,10 @@ struct OverriddenVisitor { DELEGATE(TupleMake); case Expression::Id::TupleExtractId: DELEGATE(TupleExtract); + case Expression::Id::I31NewId: + DELEGATE(I31New); + case Expression::Id::I31GetId: + DELEGATE(I31Get); case Expression::Id::InvalidId: default: WASM_UNREACHABLE("unexpected expression type"); @@ -554,6 +566,12 @@ struct UnifiedExpressionVisitor : public Visitor<SubType, ReturnType> { ReturnType visitTupleExtract(TupleExtract* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } + ReturnType visitI31New(I31New* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitI31Get(I31Get* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } }; // @@ -874,6 +892,12 @@ struct Walker : public VisitorType { static void doVisitTupleExtract(SubType* self, Expression** currp) { self->visitTupleExtract((*currp)->cast<TupleExtract>()); } + static void doVisitI31New(SubType* self, Expression** currp) { + self->visitI31New((*currp)->cast<I31New>()); + } + static void doVisitI31Get(SubType* self, Expression** currp) { + self->visitI31Get((*currp)->cast<I31Get>()); + } void setModule(Module* module) { currModule = module; } @@ -1175,6 +1199,16 @@ struct PostWalker : public Walker<SubType, VisitorType> { self->pushTask(SubType::scan, &curr->cast<TupleExtract>()->tuple); break; } + case Expression::Id::I31NewId: { + self->pushTask(SubType::doVisitI31New, currp); + self->pushTask(SubType::scan, &curr->cast<I31New>()->value); + break; + } + case Expression::Id::I31GetId: { + self->pushTask(SubType::doVisitI31Get, currp); + self->pushTask(SubType::scan, &curr->cast<I31Get>()->i31); + break; + } case Expression::Id::NumExpressionIds: WASM_UNREACHABLE("unexpected expression type"); } diff --git a/src/wasm.h b/src/wasm.h index 3f12f435e..d2965b405 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -560,6 +560,8 @@ public: BrOnExnId, TupleMakeId, TupleExtractId, + I31NewId, + I31GetId, NumExpressionIds }; Id _id; @@ -1203,6 +1205,25 @@ public: void finalize(); }; +class I31New : public SpecificExpression<Expression::I31NewId> { +public: + I31New(MixedArena& allocator) {} + + Expression* value; + + void finalize(); +}; + +class I31Get : public SpecificExpression<Expression::I31GetId> { +public: + I31Get(MixedArena& allocator) {} + + Expression* i31; + bool signed_; + + void finalize(); +}; + // Globals struct Importable { diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index 4a89b5cd1..eb28268fb 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -30,11 +30,16 @@ namespace wasm { template<int N> using LaneArray = std::array<Literal, N>; Literal::Literal(Type type) : type(type) { - assert(type != Type::unreachable && (!type.isRef() || type.isNullable())); - if (type.isException()) { - new (&exn) std::unique_ptr<ExceptionPackage>(); + if (type == Type::i31ref) { + // i31ref is special in that it is non-nullable, so we construct with zero + i32 = 0; } else { - memset(&v128, 0, 16); + assert(type != Type::unreachable && (!type.isRef() || type.isNullable())); + if (type.isException()) { + new (&exn) std::unique_ptr<ExceptionPackage>(); + } else { + memset(&v128, 0, 16); + } } } @@ -57,6 +62,7 @@ Literal::Literal(const Literal& other) : type(other.type) { switch (type.getBasic()) { case Type::i32: case Type::f32: + case Type::i31ref: i32 = other.i32; break; case Type::i64: @@ -72,8 +78,6 @@ Literal::Literal(const Literal& other) : type(other.type) { case Type::anyref: case Type::eqref: break; // null - case Type::i31ref: - WASM_UNREACHABLE("TODO: i31ref"); case Type::funcref: case Type::exnref: case Type::unreachable: @@ -209,6 +213,7 @@ void Literal::getBits(uint8_t (&buf)[16]) const { switch (type.getBasic()) { case Type::i32: case Type::f32: + case Type::i31ref: memcpy(buf, &i32, sizeof(i32)); break; case Type::i64: @@ -229,8 +234,6 @@ void Literal::getBits(uint8_t (&buf)[16]) const { case Type::eqref: assert(isNull() && "unexpected non-null reference type literal"); break; - case Type::i31ref: - WASM_UNREACHABLE("TODO: i31ref"); case Type::none: case Type::unreachable: WASM_UNREACHABLE("invalid type"); @@ -403,7 +406,8 @@ std::ostream& operator<<(std::ostream& o, Literal literal) { o << "eqref(null)"; break; case Type::i31ref: - WASM_UNREACHABLE("TODO: i31ref"); + o << "i31ref(" << literal.geti31(false) << ")"; + break; case Type::unreachable: WASM_UNREACHABLE("invalid type"); } diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index c1f698bb0..85880a988 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2589,6 +2589,17 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { throwError("invalid code after SIMD prefix: " + std::to_string(opcode)); break; } + case BinaryConsts::GCPrefix: { + auto opcode = getU32LEB(); + if (maybeVisitI31New(curr, opcode)) { + break; + } + if (maybeVisitI31Get(curr, opcode)) { + break; + } + throwError("invalid code after GC prefix: " + std::to_string(opcode)); + break; + } default: { // otherwise, the code is a subcode TODO: optimize if (maybeVisitBinary(curr, code)) { @@ -5008,6 +5019,37 @@ void WasmBinaryBuilder::visitBrOnExn(BrOnExn* curr) { curr->finalize(); } +bool WasmBinaryBuilder::maybeVisitI31New(Expression*& out, uint32_t code) { + if (code != BinaryConsts::I31New) { + return false; + } + auto* curr = allocator.alloc<I31New>(); + curr->value = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitI31Get(Expression*& out, uint32_t code) { + I31Get* curr; + switch (code) { + case BinaryConsts::I31GetS: + curr = allocator.alloc<I31Get>(); + curr->signed_ = true; + break; + case BinaryConsts::I31GetU: + curr = allocator.alloc<I31Get>(); + curr->signed_ = false; + break; + default: + return false; + } + curr->i31 = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + void WasmBinaryBuilder::throwError(std::string text) { throw ParseException(text, 0, pos); } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 1bb83ec94..166fc88bb 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1979,6 +1979,21 @@ Expression* SExpressionWasmBuilder::makeTupleExtract(Element& s) { return ret; } +Expression* SExpressionWasmBuilder::makeI31New(Element& s) { + auto ret = allocator.alloc<I31New>(); + ret->value = parseExpression(s[1]); + ret->finalize(); + return ret; +} + +Expression* SExpressionWasmBuilder::makeI31Get(Element& s, bool signed_) { + auto ret = allocator.alloc<I31Get>(); + ret->i31 = parseExpression(s[1]); + ret->signed_ = signed_; + ret->finalize(); + return ret; +} + // converts an s-expression string representing binary data into an output // sequence of raw bytes this appends to data, which may already contain // content. diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 551275622..1c16dff16 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1775,6 +1775,15 @@ void BinaryInstWriter::visitTupleExtract(TupleExtract* curr) { o << int8_t(BinaryConsts::LocalGet) << U32LEB(scratch); } +void BinaryInstWriter::visitI31New(I31New* curr) { + o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::I31New); +} + +void BinaryInstWriter::visitI31Get(I31Get* curr) { + o << int8_t(BinaryConsts::GCPrefix) + << U32LEB(curr->signed_ ? BinaryConsts::I31GetS : BinaryConsts::I31GetU); +} + void BinaryInstWriter::emitScopeEnd(Expression* curr) { assert(!breakStack.empty()); breakStack.pop_back(); diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index c1df740df..d55f60009 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -337,6 +337,8 @@ public: void visitBrOnExn(BrOnExn* curr); void visitTupleMake(TupleMake* curr); void visitTupleExtract(TupleExtract* curr); + void visitI31New(I31New* curr); + void visitI31Get(I31Get* curr); void visitFunction(Function* curr); // helpers @@ -2109,6 +2111,26 @@ void FunctionValidator::visitTupleExtract(TupleExtract* curr) { } } +void FunctionValidator::visitI31New(I31New* curr) { + shouldBeTrue( + getModule()->features.hasGC(), curr, "i31.new requires gc to be enabled"); + shouldBeSubTypeOrFirstIsUnreachable(curr->value->type, + Type::i32, + curr->value, + "i31.new's argument should be i32"); +} + +void FunctionValidator::visitI31Get(I31Get* curr) { + shouldBeTrue(getModule()->features.hasGC(), + curr, + "i31.get_s/u requires gc to be enabled"); + shouldBeSubTypeOrFirstIsUnreachable( + curr->i31->type, + Type::i31ref, + curr->i31, + "i31.get_s/u's argument should be i31ref"); +} + void FunctionValidator::visitFunction(Function* curr) { if (curr->sig.results.isTuple()) { shouldBeTrue(getModule()->features.hasMultivalue(), diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 0b45f494d..c03a9b281 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -206,6 +206,10 @@ const char* getExpressionName(Expression* curr) { return "tuple.make"; case Expression::Id::TupleExtractId: return "tuple.extract"; + case Expression::Id::I31NewId: + return "i31.new"; + case Expression::Id::I31GetId: + return "i31.get"; case Expression::Id::NumExpressionIds: WASM_UNREACHABLE("invalid expr id"); } @@ -969,6 +973,22 @@ void TupleExtract::finalize() { } } +void I31New::finalize() { + if (value->type == Type::unreachable) { + type = Type::unreachable; + } else { + type = Type::i31ref; + } +} + +void I31Get::finalize() { + if (i31->type == Type::unreachable) { + type = Type::unreachable; + } else { + type = Type::i32; + } +} + size_t Function::getNumParams() { return sig.params.size(); } size_t Function::getNumVars() { return vars.size(); } diff --git a/src/wasm2js.h b/src/wasm2js.h index 79222d4ba..db3a250aa 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2114,6 +2114,14 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, unimplemented(curr); WASM_UNREACHABLE("unimp"); } + Ref visitI31New(I31New* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } + Ref visitI31Get(I31Get* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } private: Ref makePointer(Expression* ptr, Address offset) { diff --git a/test/binaryen.js/expressions.js b/test/binaryen.js/expressions.js index a97097afe..0d48e5551 100644 --- a/test/binaryen.js/expressions.js +++ b/test/binaryen.js/expressions.js @@ -1649,3 +1649,60 @@ console.log("# TupleExtract"); module.dispose(); })(); + +console.log("# I31New"); +(function testI31New() { + const module = new binaryen.Module(); + + var value = module.local.get(1, binaryen.i32); + const theI31New = binaryen.I31New(module.i31.new(value)); + assert(theI31New instanceof binaryen.I31New); + assert(theI31New instanceof binaryen.Expression); + assert(theI31New.value === value); + assert(theI31New.type === binaryen.i31ref); + + theI31New.value = value = module.local.get(2, binaryen.i32); + assert(theI31New.value === value); + theI31New.type = binaryen.f64; + theI31New.finalize(); + assert(theI31New.type === binaryen.i31ref); + + console.log(theI31New.toText()); + assert( + theI31New.toText() + == + "(i31.new\n (local.get $2)\n)\n" + ); + + module.dispose(); +})(); + +console.log("# I31Get"); +(function testI31Get() { + const module = new binaryen.Module(); + + var i31 = module.local.get(1, binaryen.i31ref); + const theI31Get = binaryen.I31Get(module.i31.get_s(i31)); + assert(theI31Get instanceof binaryen.I31Get); + assert(theI31Get instanceof binaryen.Expression); + assert(theI31Get.i31 === i31); + assert(theI31Get.signed === true); + assert(theI31Get.type === binaryen.i32); + + theI31Get.i31 = i31 = module.local.get(2, binaryen.i31ref); + assert(theI31Get.i31 === i31); + theI31Get.signed = false; + assert(theI31Get.signed === false); + theI31Get.type = binaryen.f64; + theI31Get.finalize(); + assert(theI31Get.type === binaryen.i32); + + console.log(theI31Get.toText()); + assert( + theI31Get.toText() + == + "(i31.get_u\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 22c4f2747..e2b34e501 100644 --- a/test/binaryen.js/expressions.js.txt +++ b/test/binaryen.js/expressions.js.txt @@ -256,3 +256,13 @@ ) ) +# I31New +(i31.new + (local.get $2) +) + +# I31Get +(i31.get_u + (local.get $2) +) + diff --git a/test/binaryen.js/kitchen-sink.js b/test/binaryen.js/kitchen-sink.js index f699e96e0..051f53c54 100644 --- a/test/binaryen.js/kitchen-sink.js +++ b/test/binaryen.js/kitchen-sink.js @@ -165,6 +165,10 @@ function test_ids() { console.log("ThrowId: " + binaryen.ThrowId); console.log("RethrowId: " + binaryen.RethrowId); console.log("BrOnExnId: " + binaryen.BrOnExnId); + console.log("TupleMakeId: " + binaryen.TupleMakeId); + console.log("TupleExtractId: " + binaryen.TupleExtractId); + console.log("I31NewId: " + binaryen.I31NewId); + console.log("I31GetId: " + binaryen.I31GetId); } function test_core() { @@ -595,6 +599,21 @@ function test_core() { module.memory.size(), module.memory.grow(makeInt32(0)), + // GC + module.i31.new( + module.i32.const(0) + ), + module.i31.get_s( + module.i31.new( + module.i32.const(1) + ) + ), + module.i31.get_u( + module.i31.new( + module.i32.const(2) + ) + ), + // Other module.nop(), module.unreachable(), diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index 1c11e787e..9fca3ae53 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -89,6 +89,10 @@ TryId: 44 ThrowId: 45 RethrowId: 46 BrOnExnId: 47 +TupleMakeId: 48 +TupleExtractId: 49 +I31NewId: 50 +I31GetId: 51 getExpressionInfo={"id":15,"type":4,"op":6} (f32.neg (f32.const -33.61199951171875) @@ -1952,6 +1956,25 @@ getExpressionInfo(tuple[3])={"id":14,"type":5,"value":3.7} (i32.const 0) ) ) + (drop + (i31.new + (i32.const 0) + ) + ) + (drop + (i31.get_s + (i31.new + (i32.const 1) + ) + ) + ) + (drop + (i31.get_u + (i31.new + (i32.const 2) + ) + ) + ) (nop) (unreachable) ) @@ -3815,6 +3838,25 @@ getExpressionInfo(tuple[3])={"id":14,"type":5,"value":3.7} (i32.const 0) ) ) + (drop + (i31.new + (i32.const 0) + ) + ) + (drop + (i31.get_s + (i31.new + (i32.const 1) + ) + ) + ) + (drop + (i31.get_u + (i31.new + (i32.const 2) + ) + ) + ) (nop) (unreachable) ) diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index 09a6897eb..118fcb3f3 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -312,6 +312,7 @@ void test_core() { BinaryenExpressionRef funcrefExpr = BinaryenRefNull(module, BinaryenTypeFuncref()); funcrefExpr = BinaryenRefFunc(module, "kitchen()sinker"); BinaryenExpressionRef exnrefExpr = BinaryenRefNull(module, BinaryenTypeExnref()); + BinaryenExpressionRef i31refExpr = BinaryenI31New(module, makeInt32(module, 1)); // Events BinaryenAddEvent( @@ -776,6 +777,10 @@ void test_core() { // Memory BinaryenMemorySize(module), BinaryenMemoryGrow(module, makeInt32(module, 0)), + // GC + BinaryenI31New(module, makeInt32(module, 0)), + BinaryenI31Get(module, i31refExpr, 1), + BinaryenI31Get(module, BinaryenI31New(module, makeInt32(module, 2)), 0), // Other BinaryenNop(module), BinaryenUnreachable(module), diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index 29ead874f..fe951cdcc 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -1874,6 +1874,25 @@ BinaryenFeatureAll: 4095 (i32.const 0) ) ) + (drop + (i31.new + (i32.const 0) + ) + ) + (drop + (i31.get_s + (i31.new + (i32.const 1) + ) + ) + ) + (drop + (i31.get_u + (i31.new + (i32.const 2) + ) + ) + ) (nop) (unreachable) ) diff --git a/test/gc.wast b/test/gc.wast new file mode 100644 index 000000000..c3c561413 --- /dev/null +++ b/test/gc.wast @@ -0,0 +1,81 @@ +;; A preliminary test for prototype GC types and instructions. +;; TODO: Move subtype tests from reference-types.wast here? + +(module + ;; TODO: There are no trivial global initializers for i31ref globals because + ;; i31ref is non-nullable, hence `(ref.null i31)` cannot be used. The test + ;; currently works around this limitation by importing a constant value. + (import "env" "trivial_i31ref" (global $trivial_i31ref i31ref)) + + ;; Test global initializer expressions + (global $global_anyref (mut anyref) (ref.null any)) + (global $global_eqref (mut eqref) (ref.null eq)) + (global $global_i31ref (mut i31ref) (global.get $trivial_i31ref)) ;; ^ + + ;; Test subtype relationship in global initializer expressions + (global $global_anyref2 (mut anyref) (ref.null eq)) + (global $global_anyref3 (mut anyref) (global.get $trivial_i31ref)) ;; ^ + (global $global_eqref2 (mut eqref) (global.get $trivial_i31ref)) ;; ^ + + (func $test + (local $local_i32 i32) + (local $local_anyref anyref) + (local $local_eqref eqref) + (local $local_i31ref i31ref) + + ;; Test types for local.get/set + (local.set $local_anyref (local.get $local_anyref)) + (local.set $local_anyref (global.get $global_anyref)) + (local.set $local_anyref (ref.null any)) + (local.set $local_eqref (local.get $local_eqref)) + (local.set $local_eqref (global.get $global_eqref)) + (local.set $local_eqref (ref.null eq)) + (local.set $local_i31ref (local.get $local_i31ref)) + (local.set $local_i31ref (global.get $global_i31ref)) + (local.set $local_i31ref (global.get $trivial_i31ref)) ;; ^ + + ;; Test subtype relationship for local.set + (local.set $local_anyref (local.get $local_eqref)) + (local.set $local_anyref (global.get $global_eqref)) + (local.set $local_anyref (ref.null eq)) + (local.set $local_anyref (local.get $local_i31ref)) + (local.set $local_anyref (global.get $global_i31ref)) + (local.set $local_anyref (global.get $trivial_i31ref)) ;; ^ + (local.set $local_eqref (local.get $local_i31ref)) + (local.set $local_eqref (global.get $global_i31ref)) + (local.set $local_eqref (global.get $trivial_i31ref)) ;; ^ + + ;; Test types for global.get/set + (global.set $global_anyref (local.get $local_anyref)) + (global.set $global_anyref (global.get $global_anyref)) + (global.set $global_anyref (ref.null any)) + (global.set $global_eqref (local.get $local_eqref)) + (global.set $global_eqref (global.get $global_eqref)) + (global.set $global_eqref (ref.null eq)) + (global.set $global_i31ref (local.get $local_i31ref)) + (global.set $global_i31ref (global.get $global_i31ref)) + (global.set $global_i31ref (global.get $trivial_i31ref)) ;; ^ + + ;; Test subtype relationship for global.set + (global.set $global_anyref (local.get $local_eqref)) + (global.set $global_anyref (global.get $global_eqref)) + (global.set $global_anyref (ref.null eq)) + (global.set $global_anyref (local.get $local_i31ref)) + (global.set $global_anyref (global.get $global_i31ref)) + (global.set $global_anyref (global.get $trivial_i31ref)) ;; ^ + (global.set $global_eqref (local.get $local_i31ref)) + (global.set $global_eqref (global.get $global_i31ref)) + (global.set $global_eqref (global.get $trivial_i31ref)) ;; ^ + + ;; Test i31.new + (local.set $local_i31ref (i31.new (i32.const 0))) + + ;; Test subtype relationship for i31.new + (local.set $local_anyref (i31.new (i32.const 0))) + (local.set $local_eqref (i31.new (i32.const 0))) + + ;; Test i31.get_s/u + (local.set $local_i32 (i31.get_s (local.get $local_i31ref))) + (local.set $local_i32 (i31.get_u (local.get $local_i31ref))) + ) +) diff --git a/test/gc.wast.from-wast b/test/gc.wast.from-wast new file mode 100644 index 000000000..7a80cffe6 --- /dev/null +++ b/test/gc.wast.from-wast @@ -0,0 +1,149 @@ +(module + (type $none_=>_none (func)) + (import "env" "trivial_i31ref" (global $trivial_i31ref i31ref)) + (global $global_anyref (mut anyref) (ref.null any)) + (global $global_eqref (mut eqref) (ref.null eq)) + (global $global_i31ref (mut i31ref) (global.get $trivial_i31ref)) + (global $global_anyref2 (mut anyref) (ref.null eq)) + (global $global_anyref3 (mut anyref) (global.get $trivial_i31ref)) + (global $global_eqref2 (mut eqref) (global.get $trivial_i31ref)) + (func $test + (local $local_i32 i32) + (local $local_anyref anyref) + (local $local_eqref eqref) + (local $local_i31ref i31ref) + (local.set $local_anyref + (local.get $local_anyref) + ) + (local.set $local_anyref + (global.get $global_anyref) + ) + (local.set $local_anyref + (ref.null any) + ) + (local.set $local_eqref + (local.get $local_eqref) + ) + (local.set $local_eqref + (global.get $global_eqref) + ) + (local.set $local_eqref + (ref.null eq) + ) + (local.set $local_i31ref + (local.get $local_i31ref) + ) + (local.set $local_i31ref + (global.get $global_i31ref) + ) + (local.set $local_i31ref + (global.get $trivial_i31ref) + ) + (local.set $local_anyref + (local.get $local_eqref) + ) + (local.set $local_anyref + (global.get $global_eqref) + ) + (local.set $local_anyref + (ref.null eq) + ) + (local.set $local_anyref + (local.get $local_i31ref) + ) + (local.set $local_anyref + (global.get $global_i31ref) + ) + (local.set $local_anyref + (global.get $trivial_i31ref) + ) + (local.set $local_eqref + (local.get $local_i31ref) + ) + (local.set $local_eqref + (global.get $global_i31ref) + ) + (local.set $local_eqref + (global.get $trivial_i31ref) + ) + (global.set $global_anyref + (local.get $local_anyref) + ) + (global.set $global_anyref + (global.get $global_anyref) + ) + (global.set $global_anyref + (ref.null any) + ) + (global.set $global_eqref + (local.get $local_eqref) + ) + (global.set $global_eqref + (global.get $global_eqref) + ) + (global.set $global_eqref + (ref.null eq) + ) + (global.set $global_i31ref + (local.get $local_i31ref) + ) + (global.set $global_i31ref + (global.get $global_i31ref) + ) + (global.set $global_i31ref + (global.get $trivial_i31ref) + ) + (global.set $global_anyref + (local.get $local_eqref) + ) + (global.set $global_anyref + (global.get $global_eqref) + ) + (global.set $global_anyref + (ref.null eq) + ) + (global.set $global_anyref + (local.get $local_i31ref) + ) + (global.set $global_anyref + (global.get $global_i31ref) + ) + (global.set $global_anyref + (global.get $trivial_i31ref) + ) + (global.set $global_eqref + (local.get $local_i31ref) + ) + (global.set $global_eqref + (global.get $global_i31ref) + ) + (global.set $global_eqref + (global.get $trivial_i31ref) + ) + (local.set $local_i31ref + (i31.new + (i32.const 0) + ) + ) + (local.set $local_anyref + (i31.new + (i32.const 0) + ) + ) + (local.set $local_eqref + (i31.new + (i32.const 0) + ) + ) + (local.set $local_i32 + (i31.get_s + (local.get $local_i31ref) + ) + ) + (local.set $local_i32 + (i31.get_u + (local.get $local_i31ref) + ) + ) + ) +) diff --git a/test/gc.wast.fromBinary b/test/gc.wast.fromBinary new file mode 100644 index 000000000..f1308b9b1 --- /dev/null +++ b/test/gc.wast.fromBinary @@ -0,0 +1,150 @@ +(module + (type $none_=>_none (func)) + (import "env" "trivial_i31ref" (global $gimport$0 i31ref)) + (global $global$0 (mut anyref) (ref.null any)) + (global $global$1 (mut eqref) (ref.null eq)) + (global $global$2 (mut i31ref) (global.get $gimport$0)) + (global $global$3 (mut anyref) (ref.null eq)) + (global $global$4 (mut anyref) (global.get $gimport$0)) + (global $global$5 (mut eqref) (global.get $gimport$0)) + (func $test + (local $local_i32 i32) + (local $local_anyref anyref) + (local $local_eqref eqref) + (local $local_i31ref i31ref) + (local.set $local_anyref + (local.get $local_anyref) + ) + (local.set $local_anyref + (global.get $global$0) + ) + (local.set $local_anyref + (ref.null any) + ) + (local.set $local_eqref + (local.get $local_eqref) + ) + (local.set $local_eqref + (global.get $global$1) + ) + (local.set $local_eqref + (ref.null eq) + ) + (local.set $local_i31ref + (local.get $local_i31ref) + ) + (local.set $local_i31ref + (global.get $global$2) + ) + (local.set $local_i31ref + (global.get $gimport$0) + ) + (local.set $local_anyref + (local.get $local_eqref) + ) + (local.set $local_anyref + (global.get $global$1) + ) + (local.set $local_anyref + (ref.null eq) + ) + (local.set $local_anyref + (local.get $local_i31ref) + ) + (local.set $local_anyref + (global.get $global$2) + ) + (local.set $local_anyref + (global.get $gimport$0) + ) + (local.set $local_eqref + (local.get $local_i31ref) + ) + (local.set $local_eqref + (global.get $global$2) + ) + (local.set $local_eqref + (global.get $gimport$0) + ) + (global.set $global$0 + (local.get $local_anyref) + ) + (global.set $global$0 + (global.get $global$0) + ) + (global.set $global$0 + (ref.null any) + ) + (global.set $global$1 + (local.get $local_eqref) + ) + (global.set $global$1 + (global.get $global$1) + ) + (global.set $global$1 + (ref.null eq) + ) + (global.set $global$2 + (local.get $local_i31ref) + ) + (global.set $global$2 + (global.get $global$2) + ) + (global.set $global$2 + (global.get $gimport$0) + ) + (global.set $global$0 + (local.get $local_eqref) + ) + (global.set $global$0 + (global.get $global$1) + ) + (global.set $global$0 + (ref.null eq) + ) + (global.set $global$0 + (local.get $local_i31ref) + ) + (global.set $global$0 + (global.get $global$2) + ) + (global.set $global$0 + (global.get $gimport$0) + ) + (global.set $global$1 + (local.get $local_i31ref) + ) + (global.set $global$1 + (global.get $global$2) + ) + (global.set $global$1 + (global.get $gimport$0) + ) + (local.set $local_i31ref + (i31.new + (i32.const 0) + ) + ) + (local.set $local_anyref + (i31.new + (i32.const 0) + ) + ) + (local.set $local_eqref + (i31.new + (i32.const 0) + ) + ) + (local.set $local_i32 + (i31.get_s + (local.get $local_i31ref) + ) + ) + (local.set $local_i32 + (i31.get_u + (local.get $local_i31ref) + ) + ) + ) +) + diff --git a/test/gc.wast.fromBinary.noDebugInfo b/test/gc.wast.fromBinary.noDebugInfo new file mode 100644 index 000000000..9989a5ad8 --- /dev/null +++ b/test/gc.wast.fromBinary.noDebugInfo @@ -0,0 +1,150 @@ +(module + (type $none_=>_none (func)) + (import "env" "trivial_i31ref" (global $gimport$0 i31ref)) + (global $global$0 (mut anyref) (ref.null any)) + (global $global$1 (mut eqref) (ref.null eq)) + (global $global$2 (mut i31ref) (global.get $gimport$0)) + (global $global$3 (mut anyref) (ref.null eq)) + (global $global$4 (mut anyref) (global.get $gimport$0)) + (global $global$5 (mut eqref) (global.get $gimport$0)) + (func $0 + (local $0 i32) + (local $1 anyref) + (local $2 eqref) + (local $3 i31ref) + (local.set $1 + (local.get $1) + ) + (local.set $1 + (global.get $global$0) + ) + (local.set $1 + (ref.null any) + ) + (local.set $2 + (local.get $2) + ) + (local.set $2 + (global.get $global$1) + ) + (local.set $2 + (ref.null eq) + ) + (local.set $3 + (local.get $3) + ) + (local.set $3 + (global.get $global$2) + ) + (local.set $3 + (global.get $gimport$0) + ) + (local.set $1 + (local.get $2) + ) + (local.set $1 + (global.get $global$1) + ) + (local.set $1 + (ref.null eq) + ) + (local.set $1 + (local.get $3) + ) + (local.set $1 + (global.get $global$2) + ) + (local.set $1 + (global.get $gimport$0) + ) + (local.set $2 + (local.get $3) + ) + (local.set $2 + (global.get $global$2) + ) + (local.set $2 + (global.get $gimport$0) + ) + (global.set $global$0 + (local.get $1) + ) + (global.set $global$0 + (global.get $global$0) + ) + (global.set $global$0 + (ref.null any) + ) + (global.set $global$1 + (local.get $2) + ) + (global.set $global$1 + (global.get $global$1) + ) + (global.set $global$1 + (ref.null eq) + ) + (global.set $global$2 + (local.get $3) + ) + (global.set $global$2 + (global.get $global$2) + ) + (global.set $global$2 + (global.get $gimport$0) + ) + (global.set $global$0 + (local.get $2) + ) + (global.set $global$0 + (global.get $global$1) + ) + (global.set $global$0 + (ref.null eq) + ) + (global.set $global$0 + (local.get $3) + ) + (global.set $global$0 + (global.get $global$2) + ) + (global.set $global$0 + (global.get $gimport$0) + ) + (global.set $global$1 + (local.get $3) + ) + (global.set $global$1 + (global.get $global$2) + ) + (global.set $global$1 + (global.get $gimport$0) + ) + (local.set $3 + (i31.new + (i32.const 0) + ) + ) + (local.set $1 + (i31.new + (i32.const 0) + ) + ) + (local.set $2 + (i31.new + (i32.const 0) + ) + ) + (local.set $0 + (i31.get_s + (local.get $3) + ) + ) + (local.set $0 + (i31.get_u + (local.get $3) + ) + ) + ) +) + |