summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/gen-s-parser.py7
-rw-r--r--src/binaryen-c.cpp52
-rw-r--r--src/binaryen-c.h30
-rw-r--r--src/gen-s-parser.inc1004
-rw-r--r--src/ir/ExpressionAnalyzer.cpp2
-rw-r--r--src/ir/ExpressionManipulator.cpp6
-rw-r--r--src/ir/ReFinalize.cpp2
-rw-r--r--src/ir/effects.h2
-rw-r--r--src/ir/utils.h4
-rw-r--r--src/js/binaryen.js-post.js53
-rw-r--r--src/literal.h9
-rw-r--r--src/passes/DeadCodeElimination.cpp4
-rw-r--r--src/passes/Print.cpp18
-rw-r--r--src/wasm-binary.h11
-rw-r--r--src/wasm-builder.h13
-rw-r--r--src/wasm-interpreter.h20
-rw-r--r--src/wasm-s-parser.h2
-rw-r--r--src/wasm-stack.h2
-rw-r--r--src/wasm-traversal.h34
-rw-r--r--src/wasm.h21
-rw-r--r--src/wasm/literal.cpp22
-rw-r--r--src/wasm/wasm-binary.cpp42
-rw-r--r--src/wasm/wasm-s-parser.cpp15
-rw-r--r--src/wasm/wasm-stack.cpp9
-rw-r--r--src/wasm/wasm-validator.cpp22
-rw-r--r--src/wasm/wasm.cpp20
-rw-r--r--src/wasm2js.h8
-rw-r--r--test/binaryen.js/expressions.js57
-rw-r--r--test/binaryen.js/expressions.js.txt10
-rw-r--r--test/binaryen.js/kitchen-sink.js19
-rw-r--r--test/binaryen.js/kitchen-sink.js.txt42
-rw-r--r--test/example/c-api-kitchen-sink.c5
-rw-r--r--test/example/c-api-kitchen-sink.txt19
-rw-r--r--test/gc.wast81
-rw-r--r--test/gc.wast.from-wast149
-rw-r--r--test/gc.wast.fromBinary150
-rw-r--r--test/gc.wast.fromBinary.noDebugInfo150
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)
+ )
+ )
+ )
+)
+