summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2024-07-12 16:48:06 -0400
committerGitHub <noreply@github.com>2024-07-12 13:48:06 -0700
commiteda6530a187add28f06d9ab84d78b21b5001a6df (patch)
treeb731f8dc5a32a1b8e969f47f82f497909a236c71
parent0e0e08db6280dec4f4fcce2dff3ba07445c45b8a (diff)
downloadbinaryen-eda6530a187add28f06d9ab84d78b21b5001a6df.tar.gz
binaryen-eda6530a187add28f06d9ab84d78b21b5001a6df.tar.bz2
binaryen-eda6530a187add28f06d9ab84d78b21b5001a6df.zip
[threads] ref.i31_shared (#6735)
Implement `ref.i31_shared` the new instruction for creating references to shared i31s. Implement binary and text parsing and emitting as well as interpretation. Copy the upstream spec test for i31 and modify it so that all the heap types are shared. Comment out some parts that we do not yet support.
-rwxr-xr-xscripts/fuzz_opt.py1
-rwxr-xr-xscripts/gen-s-parser.py5
-rw-r--r--scripts/test/binaryenjs.py2
-rw-r--r--src/gen-s-parser.inc23
-rw-r--r--src/ir/properties.h3
-rw-r--r--src/literal.h6
-rw-r--r--src/parser/contexts.h11
-rw-r--r--src/parser/parsers.h11
-rw-r--r--src/parser/wast-parser.cpp7
-rw-r--r--src/passes/Print.cpp6
-rw-r--r--src/wasm-binary.h1
-rw-r--r--src/wasm-builder.h3
-rw-r--r--src/wasm-interpreter.h3
-rw-r--r--src/wasm-ir-builder.h2
-rw-r--r--src/wasm/literal.cpp3
-rw-r--r--src/wasm/wasm-binary.cpp18
-rw-r--r--src/wasm/wasm-ir-builder.cpp4
-rw-r--r--src/wasm/wasm-stack.cpp4
-rw-r--r--src/wasm/wasm.cpp3
-rw-r--r--test/binaryen.js/expressions.js3
-rw-r--r--test/lit/basic/shared-i31.wast37
-rw-r--r--test/spec/shared-i31.wast230
22 files changed, 348 insertions, 38 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py
index 2707df4c0..ada930127 100755
--- a/scripts/fuzz_opt.py
+++ b/scripts/fuzz_opt.py
@@ -356,6 +356,7 @@ INITIAL_CONTENTS_IGNORE = [
'shared-polymorphism.wast',
'shared-struct.wast',
'shared-array.wast',
+ 'shared-i31.wast',
]
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py
index 43a85c636..711096a5b 100755
--- a/scripts/gen-s-parser.py
+++ b/scripts/gen-s-parser.py
@@ -572,8 +572,9 @@ instructions = [
("resume", "makeResume()"),
("suspend", "makeSuspend()"),
# GC
- ("i31.new", "makeRefI31()"), # deprecated
- ("ref.i31", "makeRefI31()"),
+ ("i31.new", "makeRefI31(Unshared)"), # deprecated
+ ("ref.i31", "makeRefI31(Unshared)"),
+ ("ref.i31_shared", "makeRefI31(Shared)"),
("i31.get_s", "makeI31Get(true)"),
("i31.get_u", "makeI31Get(false)"),
("ref.test", "makeRefTest()"),
diff --git a/scripts/test/binaryenjs.py b/scripts/test/binaryenjs.py
index d546ced13..93fbe532f 100644
--- a/scripts/test/binaryenjs.py
+++ b/scripts/test/binaryenjs.py
@@ -60,7 +60,7 @@ def do_test_binaryen_js_with(which):
test([shared.MOZJS, '-m', 'a.mjs'])
if shared.NODEJS:
if node_has_wasm or 'WebAssembly.' not in test_src:
- test([shared.NODEJS, '--experimental-wasm-eh', 'a.mjs'])
+ test([shared.NODEJS, 'a.mjs'])
else:
print('Skipping ' + test_path + ' because WebAssembly might not be supported')
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index 8a3dcba0b..bbed0f8be 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -1831,7 +1831,7 @@ switch (buf[0]) {
}
case 'n':
if (op == "i31.new"sv) {
- CHECK_ERR(makeRefI31(ctx, pos, annotations));
+ CHECK_ERR(makeRefI31(ctx, pos, annotations, Unshared));
return Ok{};
}
goto parse_error;
@@ -4505,12 +4505,23 @@ switch (buf[0]) {
goto parse_error;
case 'i': {
switch (buf[5]) {
- case '3':
- if (op == "ref.i31"sv) {
- CHECK_ERR(makeRefI31(ctx, pos, annotations));
- return Ok{};
+ case '3': {
+ switch (buf[7]) {
+ case '\0':
+ if (op == "ref.i31"sv) {
+ CHECK_ERR(makeRefI31(ctx, pos, annotations, Unshared));
+ return Ok{};
+ }
+ goto parse_error;
+ case '_':
+ if (op == "ref.i31_shared"sv) {
+ CHECK_ERR(makeRefI31(ctx, pos, annotations, Shared));
+ return Ok{};
+ }
+ goto parse_error;
+ default: goto parse_error;
}
- goto parse_error;
+ }
case 's':
if (op == "ref.is_null"sv) {
CHECK_ERR(makeRefIsNull(ctx, pos, annotations));
diff --git a/src/ir/properties.h b/src/ir/properties.h
index ccb2392b0..09486ee34 100644
--- a/src/ir/properties.h
+++ b/src/ir/properties.h
@@ -119,7 +119,8 @@ inline Literal getLiteral(const Expression* curr) {
return Literal(r->func, r->type.getHeapType());
} else if (auto* i = curr->dynCast<RefI31>()) {
if (auto* c = i->value->dynCast<Const>()) {
- return Literal::makeI31(c->value.geti32());
+ return Literal::makeI31(c->value.geti32(),
+ i->type.getHeapType().getShared());
}
} else if (auto* s = curr->dynCast<StringConst>()) {
return Literal(s->string.toString());
diff --git a/src/literal.h b/src/literal.h
index 1268448fb..45f76f247 100644
--- a/src/literal.h
+++ b/src/literal.h
@@ -243,8 +243,8 @@ public:
static Literal makeFunc(Name func, HeapType type) {
return Literal(func, type);
}
- static Literal makeI31(int32_t value) {
- auto lit = Literal(Type(HeapType::i31, NonNullable));
+ static Literal makeI31(int32_t value, Shareability share) {
+ auto lit = Literal(Type(HeapTypes::i31.getBasic(share), NonNullable));
lit.i32 = value | 0x80000000;
return lit;
}
@@ -281,7 +281,7 @@ public:
return i32;
}
int32_t geti31(bool signed_ = true) const {
- assert(type.getHeapType() == HeapType::i31);
+ assert(type.getHeapType().getBasic(Unshared) == HeapType::i31);
// Cast to unsigned for the left shift to avoid undefined behavior.
return signed_ ? int32_t((uint32_t(i32) << 1)) >> 1 : (i32 & 0x7fffffff);
}
diff --git a/src/parser/contexts.h b/src/parser/contexts.h
index f58275d71..96a70663d 100644
--- a/src/parser/contexts.h
+++ b/src/parser/contexts.h
@@ -695,7 +695,10 @@ struct NullInstrParserCtx {
Result<> makeCallRef(Index, const std::vector<Annotation>&, HeapTypeT, bool) {
return Ok{};
}
- Result<> makeRefI31(Index, const std::vector<Annotation>&) { return Ok{}; }
+ Result<>
+ makeRefI31(Index, const std::vector<Annotation>&, Shareability share) {
+ return Ok{};
+ }
Result<> makeI31Get(Index, const std::vector<Annotation>&, bool) {
return Ok{};
}
@@ -2363,8 +2366,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
return withLoc(pos, irBuilder.makeCallRef(type, isReturn));
}
- Result<> makeRefI31(Index pos, const std::vector<Annotation>& annotations) {
- return withLoc(pos, irBuilder.makeRefI31());
+ Result<> makeRefI31(Index pos,
+ const std::vector<Annotation>& annotations,
+ Shareability share) {
+ return withLoc(pos, irBuilder.makeRefI31(share));
}
Result<> makeI31Get(Index pos,
diff --git a/src/parser/parsers.h b/src/parser/parsers.h
index db450e3c6..2c3916fa3 100644
--- a/src/parser/parsers.h
+++ b/src/parser/parsers.h
@@ -221,7 +221,8 @@ template<typename Ctx>
Result<>
makeCallRef(Ctx&, Index, const std::vector<Annotation>&, bool isReturn);
template<typename Ctx>
-Result<> makeRefI31(Ctx&, Index, const std::vector<Annotation>&);
+Result<>
+makeRefI31(Ctx&, Index, const std::vector<Annotation>&, Shareability share);
template<typename Ctx>
Result<> makeI31Get(Ctx&, Index, const std::vector<Annotation>&, bool signed_);
template<typename Ctx>
@@ -2127,9 +2128,11 @@ Result<> makeCallRef(Ctx& ctx,
}
template<typename Ctx>
-Result<>
-makeRefI31(Ctx& ctx, Index pos, const std::vector<Annotation>& annotations) {
- return ctx.makeRefI31(pos, annotations);
+Result<> makeRefI31(Ctx& ctx,
+ Index pos,
+ const std::vector<Annotation>& annotations,
+ Shareability share) {
+ return ctx.makeRefI31(pos, annotations, share);
}
template<typename Ctx>
diff --git a/src/parser/wast-parser.cpp b/src/parser/wast-parser.cpp
index 137ef0df1..a3a6c14ce 100644
--- a/src/parser/wast-parser.cpp
+++ b/src/parser/wast-parser.cpp
@@ -207,6 +207,13 @@ Result<ExpectedResult> result(Lexer& in) {
return RefResult{HeapType::func};
}
+ if (in.takeSExprStart("ref.i31_shared")) {
+ if (!in.takeRParen()) {
+ return in.err("expected end of ref.i31_shared");
+ }
+ return RefResult{HeapTypes::i31.getBasic(Shared)};
+ }
+
return in.err("unrecognized result");
}
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 2e9800775..ede49ab38 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -2076,7 +2076,11 @@ struct PrintExpressionContents
o << std::max(curr->tuple->type.size(), size_t(2)) << " ";
o << curr->index;
}
- void visitRefI31(RefI31* curr) { printMedium(o, "ref.i31"); }
+ void visitRefI31(RefI31* curr) {
+ bool shared =
+ curr->type != Type::unreachable && curr->type.getHeapType().isShared();
+ printMedium(o, shared ? "ref.i31_shared" : "ref.i31");
+ }
void visitI31Get(I31Get* curr) {
printMedium(o, curr->signed_ ? "i31.get_s" : "i31.get_u");
}
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 0b9615c7e..3e45b4969 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1125,6 +1125,7 @@ enum ASTNodes {
RefI31 = 0x1c,
I31GetS = 0x1d,
I31GetU = 0x1e,
+ RefI31Shared = 0x1f,
// stringref opcodes
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index a20ba46a6..1e768b11c 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -873,9 +873,10 @@ public:
ret->finalize();
return ret;
}
- RefI31* makeRefI31(Expression* value) {
+ RefI31* makeRefI31(Expression* value, Shareability share = Unshared) {
auto* ret = wasm.allocator.alloc<RefI31>();
ret->value = value;
+ ret->type = Type(HeapTypes::i31.getBasic(share), NonNullable);
ret->finalize();
return ret;
}
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 93739d1c9..9bdf0e72c 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1426,7 +1426,8 @@ public:
}
const auto& value = flow.getSingleValue();
NOTE_EVAL1(value);
- return Literal::makeI31(value.geti32());
+ return Literal::makeI31(value.geti32(),
+ curr->type.getHeapType().getShared());
}
Flow visitI31Get(I31Get* curr) {
NOTE_ENTER("I31Get");
diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h
index 16c3b7570..eff8c85ad 100644
--- a/src/wasm-ir-builder.h
+++ b/src/wasm-ir-builder.h
@@ -181,7 +181,7 @@ public:
[[nodiscard]] Result<> makeTupleMake(uint32_t arity);
[[nodiscard]] Result<> makeTupleExtract(uint32_t arity, uint32_t index);
[[nodiscard]] Result<> makeTupleDrop(uint32_t arity);
- [[nodiscard]] Result<> makeRefI31();
+ [[nodiscard]] Result<> makeRefI31(Shareability share);
[[nodiscard]] Result<> makeI31Get(bool signed_);
[[nodiscard]] Result<> makeCallRef(HeapType type, bool isReturn);
[[nodiscard]] Result<> makeRefTest(Type type);
diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp
index f13ea504f..20b6b7234 100644
--- a/src/wasm/literal.cpp
+++ b/src/wasm/literal.cpp
@@ -57,7 +57,8 @@ Literal::Literal(Type type) : type(type) {
return;
}
- if (type.isRef() && type.getHeapType() == HeapType::i31) {
+ if (type.isRef() && type.getHeapType().isBasic() &&
+ type.getHeapType().getBasic(Unshared) == HeapType::i31) {
assert(type.isNonNullable());
i32 = 0;
return;
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 78470324c..74d74f473 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -7244,13 +7244,19 @@ void WasmBinaryReader::visitCallRef(CallRef* curr) {
}
bool WasmBinaryReader::maybeVisitRefI31(Expression*& out, uint32_t code) {
- if (code != BinaryConsts::RefI31) {
- return false;
+ Shareability share;
+ switch (code) {
+ case BinaryConsts::RefI31:
+ share = Unshared;
+ break;
+ case BinaryConsts::RefI31Shared:
+ share = Shared;
+ break;
+ default:
+ return false;
}
- auto* curr = allocator.alloc<RefI31>();
- curr->value = popNonVoidExpression();
- curr->finalize();
- out = curr;
+ auto* value = popNonVoidExpression();
+ out = Builder(wasm).makeRefI31(value, share);
return true;
}
diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp
index 63f5df4c0..3db6238c4 100644
--- a/src/wasm/wasm-ir-builder.cpp
+++ b/src/wasm/wasm-ir-builder.cpp
@@ -1605,10 +1605,10 @@ Result<> IRBuilder::makeTupleDrop(uint32_t arity) {
return Ok{};
}
-Result<> IRBuilder::makeRefI31() {
+Result<> IRBuilder::makeRefI31(Shareability share) {
RefI31 curr;
CHECK_ERR(visitRefI31(&curr));
- push(builder.makeRefI31(curr.value));
+ push(builder.makeRefI31(curr.value, share));
return Ok{};
}
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 40a50706a..cd0a9928e 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -2114,7 +2114,9 @@ void BinaryInstWriter::visitTupleExtract(TupleExtract* curr) {
}
void BinaryInstWriter::visitRefI31(RefI31* curr) {
- o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::RefI31);
+ o << int8_t(BinaryConsts::GCPrefix)
+ << U32LEB(curr->type.getHeapType().isShared() ? BinaryConsts::RefI31Shared
+ : BinaryConsts::RefI31);
}
void BinaryInstWriter::visitI31Get(I31Get* curr) {
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 8a2b4755c..c4f02128e 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -964,7 +964,8 @@ void RefI31::finalize() {
if (value->type == Type::unreachable) {
type = Type::unreachable;
} else {
- type = Type(HeapType::i31, NonNullable);
+ assert(type.isRef() && type.getHeapType().isBasic() &&
+ type.getHeapType().getBasic(Unshared) == HeapType::i31);
}
}
diff --git a/test/binaryen.js/expressions.js b/test/binaryen.js/expressions.js
index bd8ec6793..c05e820a7 100644
--- a/test/binaryen.js/expressions.js
+++ b/test/binaryen.js/expressions.js
@@ -1777,9 +1777,6 @@ console.log("# RefI31");
theRefI31.value = value = module.local.get(2, binaryen.i32);
assert(theRefI31.value === value);
- theRefI31.type = binaryen.f64;
- theRefI31.finalize();
- // assert(theRefI31.type === binaryen.?); // TODO: (ref i31)
console.log(theRefI31.toText());
assert(
diff --git a/test/lit/basic/shared-i31.wast b/test/lit/basic/shared-i31.wast
new file mode 100644
index 000000000..b448bcad3
--- /dev/null
+++ b/test/lit/basic/shared-i31.wast
@@ -0,0 +1,37 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; RUN: wasm-opt %s -all -S -o - | filecheck %s
+;; RUN: wasm-opt %s -all --roundtrip -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (type $0 (func (param (ref null (shared i31))) (result i32)))
+
+ ;; CHECK: (type $1 (func (param i32) (result (ref (shared i31)))))
+
+ ;; CHECK: (func $make (type $1) (param $0 i32) (result (ref (shared i31)))
+ ;; CHECK-NEXT: (ref.i31_shared
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $make (param i32) (result (ref (shared i31)))
+ (ref.i31_shared (local.get 0))
+ )
+
+ ;; CHECK: (func $get_s (type $0) (param $0 (ref null (shared i31))) (result i32)
+ ;; CHECK-NEXT: (i31.get_s
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get_s (param (ref null (shared i31))) (result i32)
+ (i31.get_s (local.get 0))
+ )
+
+ ;; CHECK: (func $get_u (type $0) (param $0 (ref null (shared i31))) (result i32)
+ ;; CHECK-NEXT: (i31.get_u
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get_u (param (ref null (shared i31))) (result i32)
+ (i31.get_u (local.get 0))
+ )
+)
diff --git a/test/spec/shared-i31.wast b/test/spec/shared-i31.wast
new file mode 100644
index 000000000..ef1e2a9f1
--- /dev/null
+++ b/test/spec/shared-i31.wast
@@ -0,0 +1,230 @@
+(module
+ (func (export "new") (param $i i32) (result (ref (shared i31)))
+ (ref.i31_shared (local.get $i))
+ )
+
+ (func (export "get_u") (param $i i32) (result i32)
+ (i31.get_u (ref.i31_shared (local.get $i)))
+ )
+ (func (export "get_s") (param $i i32) (result i32)
+ (i31.get_s (ref.i31_shared (local.get $i)))
+ )
+
+ (func (export "get_u-null") (result i32)
+ (i31.get_u (ref.null (shared i31)))
+ )
+ (func (export "get_s-null") (result i32)
+ (i31.get_u (ref.null (shared i31)))
+ )
+
+ (global $i (ref (shared i31)) (ref.i31_shared (i32.const 2)))
+ (global $m (mut (ref (shared i31))) (ref.i31_shared (i32.const 3)))
+
+ (func (export "get_globals") (result i32 i32)
+ (i31.get_u (global.get $i))
+ (i31.get_u (global.get $m))
+ )
+
+ (func (export "set_global") (param i32)
+ (global.set $m (ref.i31_shared (local.get 0)))
+ )
+)
+
+(assert_return (invoke "new" (i32.const 1)) (ref.i31_shared))
+
+(assert_return (invoke "get_u" (i32.const 0)) (i32.const 0))
+(assert_return (invoke "get_u" (i32.const 100)) (i32.const 100))
+(assert_return (invoke "get_u" (i32.const -1)) (i32.const 0x7fff_ffff))
+(assert_return (invoke "get_u" (i32.const 0x3fff_ffff)) (i32.const 0x3fff_ffff))
+(assert_return (invoke "get_u" (i32.const 0x4000_0000)) (i32.const 0x4000_0000))
+(assert_return (invoke "get_u" (i32.const 0x7fff_ffff)) (i32.const 0x7fff_ffff))
+(assert_return (invoke "get_u" (i32.const 0xaaaa_aaaa)) (i32.const 0x2aaa_aaaa))
+(assert_return (invoke "get_u" (i32.const 0xcaaa_aaaa)) (i32.const 0x4aaa_aaaa))
+
+(assert_return (invoke "get_s" (i32.const 0)) (i32.const 0))
+(assert_return (invoke "get_s" (i32.const 100)) (i32.const 100))
+(assert_return (invoke "get_s" (i32.const -1)) (i32.const -1))
+(assert_return (invoke "get_s" (i32.const 0x3fff_ffff)) (i32.const 0x3fff_ffff))
+(assert_return (invoke "get_s" (i32.const 0x4000_0000)) (i32.const -0x4000_0000))
+(assert_return (invoke "get_s" (i32.const 0x7fff_ffff)) (i32.const -1))
+(assert_return (invoke "get_s" (i32.const 0xaaaa_aaaa)) (i32.const 0x2aaa_aaaa))
+(assert_return (invoke "get_s" (i32.const 0xcaaa_aaaa)) (i32.const 0xcaaa_aaaa))
+
+(assert_trap (invoke "get_u-null") "null i31 reference")
+(assert_trap (invoke "get_s-null") "null i31 reference")
+
+(assert_return (invoke "get_globals") (i32.const 2) (i32.const 3))
+
+(invoke "set_global" (i32.const 1234))
+(assert_return (invoke "get_globals") (i32.const 2) (i32.const 1234))
+
+(module $tables_of_i31ref
+ (table $table 3 10 (ref null (shared i31)))
+ (elem (table $table) (i32.const 0) (ref null (shared i31))
+ (item (ref.i31_shared (i32.const 999)))
+ (item (ref.i31_shared (i32.const 888)))
+ (item (ref.i31_shared (i32.const 777))))
+
+ (func (export "size") (result i32)
+ table.size $table
+ )
+
+ (func (export "get") (param i32) (result i32)
+ (i31.get_u (table.get $table (local.get 0)))
+ )
+
+ (func (export "grow") (param i32 i32) (result i32)
+ (table.grow $table (ref.i31_shared (local.get 1)) (local.get 0))
+ )
+
+ (func (export "fill") (param i32 i32 i32)
+ (table.fill $table (local.get 0) (ref.i31_shared (local.get 1)) (local.get 2))
+ )
+
+ (func (export "copy") (param i32 i32 i32)
+ (table.copy $table $table (local.get 0) (local.get 1) (local.get 2))
+ )
+
+ (elem $elem (ref null (shared i31)) (item (ref.i31_shared (i32.const 123)))
+ (item (ref.i31_shared (i32.const 456)))
+ (item (ref.i31_shared (i32.const 789))))
+ ;; (func (export "init") (param i32 i32 i32)
+ ;; (table.init $table $elem (local.get 0) (local.get 1) (local.get 2))
+ ;; )
+)
+
+;; Initial state.
+(assert_return (invoke "size") (i32.const 3))
+(assert_return (invoke "get" (i32.const 0)) (i32.const 999))
+(assert_return (invoke "get" (i32.const 1)) (i32.const 888))
+(assert_return (invoke "get" (i32.const 2)) (i32.const 777))
+
+;; Grow from size 3 to size 5.
+(assert_return (invoke "grow" (i32.const 2) (i32.const 333)) (i32.const 3))
+(assert_return (invoke "size") (i32.const 5))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 333))
+(assert_return (invoke "get" (i32.const 4)) (i32.const 333))
+
+;; Fill table[2..4] = 111.
+(invoke "fill" (i32.const 2) (i32.const 111) (i32.const 2))
+(assert_return (invoke "get" (i32.const 2)) (i32.const 111))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 111))
+
+;; Copy from table[0..2] to table[3..5].
+(invoke "copy" (i32.const 3) (i32.const 0) (i32.const 2))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 999))
+(assert_return (invoke "get" (i32.const 4)) (i32.const 888))
+
+;; ;; Initialize the passive element at table[1..4].
+;; (invoke "init" (i32.const 1) (i32.const 0) (i32.const 3))
+;; (assert_return (invoke "get" (i32.const 1)) (i32.const 123))
+;; (assert_return (invoke "get" (i32.const 2)) (i32.const 456))
+;; (assert_return (invoke "get" (i32.const 3)) (i32.const 789))
+
+(module $env
+ (global (export "g") i32 (i32.const 42))
+)
+(register "env")
+
+;; (module $i31ref_of_global_table_initializer
+;; (global $g (import "env" "g") i32)
+;; (table $t 3 3 (ref (shared i31)) (ref.i31_shared (global.get $g)))
+;; (func (export "get") (param i32) (result i32)
+;; (i31.get_u (local.get 0) (table.get $t))
+;; )
+;; )
+
+;; (assert_return (invoke "get" (i32.const 0)) (i32.const 42))
+;; (assert_return (invoke "get" (i32.const 1)) (i32.const 42))
+;; (assert_return (invoke "get" (i32.const 2)) (i32.const 42))
+
+(module $i31ref_of_global_global_initializer
+ (global $g0 (import "env" "g") i32)
+ (global $g1 (ref null (shared i31)) (ref.i31_shared (global.get $g0)))
+ (func (export "get") (result i32)
+ (i31.get_u (global.get $g1))
+ )
+)
+
+(assert_return (invoke "get") (i32.const 42))
+
+(module $anyref_global_of_i31ref
+ (global $c (ref null (shared any)) (ref.i31_shared (i32.const 1234)))
+ (global $m (mut (ref null (shared any))) (ref.i31_shared (i32.const 5678)))
+
+ (func (export "get_globals") (result i32 i32)
+ (i31.get_u (ref.cast (ref null (shared i31)) (global.get $c)))
+ (i31.get_u (ref.cast (ref null (shared i31)) (global.get $m)))
+ )
+
+ (func (export "set_global") (param i32)
+ (global.set $m (ref.i31_shared (local.get 0)))
+ )
+)
+
+(assert_return (invoke "get_globals") (i32.const 1234) (i32.const 5678))
+(invoke "set_global" (i32.const 0))
+(assert_return (invoke "get_globals") (i32.const 1234) (i32.const 0))
+
+(module $anyref_table_of_i31ref
+ (table $table 3 10 (ref null (shared any)))
+ (elem (table $table) (i32.const 0) (ref null (shared i31))
+ (item (ref.i31_shared (i32.const 999)))
+ (item (ref.i31_shared (i32.const 888)))
+ (item (ref.i31_shared (i32.const 777))))
+
+ (func (export "size") (result i32)
+ table.size $table
+ )
+
+ (func (export "get") (param i32) (result i32)
+ (i31.get_u (ref.cast (ref null (shared i31)) (table.get $table (local.get 0))))
+ )
+
+ (func (export "grow") (param i32 i32) (result i32)
+ (table.grow $table (ref.i31_shared (local.get 1)) (local.get 0))
+ )
+
+ (func (export "fill") (param i32 i32 i32)
+ (table.fill $table (local.get 0) (ref.i31_shared (local.get 1)) (local.get 2))
+ )
+
+ (func (export "copy") (param i32 i32 i32)
+ (table.copy $table $table (local.get 0) (local.get 1) (local.get 2))
+ )
+
+ (elem $elem (ref null (shared i31)) (item (ref.i31_shared (i32.const 123)))
+ (item (ref.i31_shared (i32.const 456)))
+ (item (ref.i31_shared (i32.const 789))))
+ ;; (func (export "init") (param i32 i32 i32)
+ ;; (table.init $table $elem (local.get 0) (local.get 1) (local.get 2))
+ ;; )
+)
+
+;; Initial state.
+(assert_return (invoke "size") (i32.const 3))
+(assert_return (invoke "get" (i32.const 0)) (i32.const 999))
+(assert_return (invoke "get" (i32.const 1)) (i32.const 888))
+(assert_return (invoke "get" (i32.const 2)) (i32.const 777))
+
+;; Grow from size 3 to size 5.
+(assert_return (invoke "grow" (i32.const 2) (i32.const 333)) (i32.const 3))
+(assert_return (invoke "size") (i32.const 5))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 333))
+(assert_return (invoke "get" (i32.const 4)) (i32.const 333))
+
+;; Fill table[2..4] = 111.
+(invoke "fill" (i32.const 2) (i32.const 111) (i32.const 2))
+(assert_return (invoke "get" (i32.const 2)) (i32.const 111))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 111))
+
+;; Copy from table[0..2] to table[3..5].
+(invoke "copy" (i32.const 3) (i32.const 0) (i32.const 2))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 999))
+(assert_return (invoke "get" (i32.const 4)) (i32.const 888))
+
+;; ;; Initialize the passive element at table[1..4].
+;; (invoke "init" (i32.const 1) (i32.const 0) (i32.const 3))
+;; (assert_return (invoke "get" (i32.const 1)) (i32.const 123))
+;; (assert_return (invoke "get" (i32.const 2)) (i32.const 456))
+;; (assert_return (invoke "get" (i32.const 3)) (i32.const 789))