summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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))