Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scripts/fuzz_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ def is_git_repo():
'shared-polymorphism.wast',
'shared-struct.wast',
'shared-array.wast',
'shared-i31.wast',
]


Expand Down
5 changes: 3 additions & 2 deletions scripts/gen-s-parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,8 +572,9 @@
("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()"),
Expand Down
2 changes: 1 addition & 1 deletion scripts/test/binaryenjs.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def test(cmd):
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')

Expand Down
23 changes: 17 additions & 6 deletions src/gen-s-parser.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down
3 changes: 2 additions & 1 deletion src/ir/properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
6 changes: 3 additions & 3 deletions src/literal.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,8 @@ class Literal {
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;
}
Expand Down Expand Up @@ -281,7 +281,7 @@ class Literal {
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);
}
Expand Down
11 changes: 8 additions & 3 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -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{};
}
Expand Down Expand Up @@ -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,
Expand Down
11 changes: 7 additions & 4 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
7 changes: 7 additions & 0 deletions src/parser/wast-parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

Expand Down
6 changes: 5 additions & 1 deletion src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
1 change: 1 addition & 0 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,7 @@ enum ASTNodes {
RefI31 = 0x1c,
I31GetS = 0x1d,
I31GetU = 0x1e,
RefI31Shared = 0x1f,

// stringref opcodes

Expand Down
3 changes: 2 additions & 1 deletion src/wasm-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -873,9 +873,10 @@ class Builder {
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;
}
Expand Down
3 changes: 2 additions & 1 deletion src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -1426,7 +1426,8 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
}
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");
Expand Down
2 changes: 1 addition & 1 deletion src/wasm-ir-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
[[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);
Expand Down
3 changes: 2 additions & 1 deletion src/wasm/literal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
18 changes: 12 additions & 6 deletions src/wasm/wasm-binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 2 additions & 2 deletions src/wasm/wasm-ir-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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{};
}

Expand Down
4 changes: 3 additions & 1 deletion src/wasm/wasm-stack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion src/wasm/wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
3 changes: 0 additions & 3 deletions test/binaryen.js/expressions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
37 changes: 37 additions & 0 deletions test/lit/basic/shared-i31.wast
Original file line number Diff line number Diff line change
@@ -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))
)
)
Loading