Skip to content

Commit

Permalink
[threads] ref.i31_shared
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tlively committed Jul 12, 2024
1 parent ae4800b commit 6fadc97
Show file tree
Hide file tree
Showing 21 changed files with 342 additions and 30 deletions.
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
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
2 changes: 1 addition & 1 deletion src/ir/properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ 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->share);
}
} 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
4 changes: 3 additions & 1 deletion src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2076,7 +2076,9 @@ 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) {
printMedium(o, curr->share == 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,8 +873,9 @@ class Builder {
ret->finalize();
return ret;
}
RefI31* makeRefI31(Expression* value) {
RefI31* makeRefI31(Expression* value, Shareability share = Unshared) {
auto* ret = wasm.allocator.alloc<RefI31>();
ret->share = share;
ret->value = value;
ret->finalize();
return ret;
Expand Down
1 change: 1 addition & 0 deletions src/wasm-delegations-fields.def
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ DELEGATE_FIELD_INT(TupleExtract, index)
DELEGATE_FIELD_CASE_END(TupleExtract)

DELEGATE_FIELD_CASE_START(RefI31)
DELEGATE_FIELD_INT(RefI31, share)
DELEGATE_FIELD_CHILD(RefI31, value)
DELEGATE_FIELD_CASE_END(RefI31)

Expand Down
2 changes: 1 addition & 1 deletion src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -1426,7 +1426,7 @@ 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->share);
}
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
1 change: 1 addition & 0 deletions src/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -1514,6 +1514,7 @@ class RefI31 : public SpecificExpression<Expression::RefI31Id> {
RefI31() = default;
RefI31(MixedArena& allocator) {}

Shareability share;
Expression* value;

void finalize();
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
13 changes: 11 additions & 2 deletions src/wasm/wasm-binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7244,10 +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->share = share;
curr->value = popNonVoidExpression();
curr->finalize();
out = curr;
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->share == Shared ? BinaryConsts::RefI31Shared
: BinaryConsts::RefI31);
}

void BinaryInstWriter::visitI31Get(I31Get* curr) {
Expand Down
2 changes: 1 addition & 1 deletion src/wasm/wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,7 @@ void RefI31::finalize() {
if (value->type == Type::unreachable) {
type = Type::unreachable;
} else {
type = Type(HeapType::i31, NonNullable);
type = Type(HeapTypes::i31.getBasic(share), NonNullable);
}
}

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

0 comments on commit 6fadc97

Please sign in to comment.