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/gen-s-parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@
("i64.extend32_s", "makeUnary(UnaryOp::ExtendS32Int64)"),
# atomic instructions
("memory.atomic.notify", "makeAtomicNotify()"),
("struct.wait", "makeStructWait()"),
("memory.atomic.wait32", "makeAtomicWait(Type::i32)"),
("memory.atomic.wait64", "makeAtomicWait(Type::i64)"),
("atomic.fence", "makeAtomicFence()"),
Expand Down
6 changes: 6 additions & 0 deletions src/gen-s-parser.inc
Original file line number Diff line number Diff line change
Expand Up @@ -5400,6 +5400,12 @@ switch (buf[0]) {
return Ok{};
}
goto parse_error;
case 'w':
if (op == "struct.wait"sv) {
CHECK_ERR(makeStructWait(ctx, pos, annotations));
return Ok{};
}
goto parse_error;
default: goto parse_error;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/interpreter/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ struct ExpressionInterpreter : OverriddenVisitor<ExpressionInterpreter, Flow> {
Flow visitStructSet(StructSet* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStructRMW(StructRMW* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStructCmpxchg(StructCmpxchg* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStructWait(StructWait* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayNew(ArrayNew* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayNewData(ArrayNewData* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayNewElem(ArrayNewElem* curr) { WASM_UNREACHABLE("TODO"); }
Expand Down
1 change: 1 addition & 0 deletions src/ir/ReFinalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ void ReFinalize::visitStructGet(StructGet* curr) { curr->finalize(); }
void ReFinalize::visitStructSet(StructSet* curr) { curr->finalize(); }
void ReFinalize::visitStructRMW(StructRMW* curr) { curr->finalize(); }
void ReFinalize::visitStructCmpxchg(StructCmpxchg* curr) { curr->finalize(); }
void ReFinalize::visitStructWait(StructWait* curr) { curr->finalize(); }
void ReFinalize::visitArrayNew(ArrayNew* curr) { curr->finalize(); }
void ReFinalize::visitArrayNewData(ArrayNewData* curr) { curr->finalize(); }
void ReFinalize::visitArrayNewElem(ArrayNewElem* curr) { curr->finalize(); }
Expand Down
15 changes: 15 additions & 0 deletions src/ir/child-typer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,21 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
note(&curr->replacement, type);
}

void visitStructWait(StructWait* curr,
std::optional<HeapType> ht = std::nullopt) {
if (!ht) {
if (!curr->ref->type.isStruct()) {
self().noteUnknown();
return;
}
ht = curr->ref->type.getHeapType();
}

note(&curr->ref, Type(*ht, Nullable));
note(&curr->expected, Type(Type::BasicType::i32));
note(&curr->timeout, Type(Type::BasicType::i64));
}

void visitArrayNew(ArrayNew* curr) {
if (!curr->isWithDefault()) {
if (!curr->type.isRef()) {
Expand Down
4 changes: 4 additions & 0 deletions src/ir/cost.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
return AtomicCost + visit(curr->ptr) + visit(curr->expected) +
visit(curr->timeout);
}
CostType visitStructWait(StructWait* curr) {
return AtomicCost + nullCheckCost(curr->ref) + visit(curr->ref) +
visit(curr->expected) + visit(curr->timeout);
}
CostType visitAtomicNotify(AtomicNotify* curr) {
return AtomicCost + visit(curr->ptr) + visit(curr->notifyCount);
}
Expand Down
33 changes: 33 additions & 0 deletions src/ir/effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,39 @@ class EffectAnalyzer {
assert(curr->order != MemoryOrder::Unordered);
parent.isAtomic = true;
}
void visitStructWait(StructWait* curr) {
parent.isAtomic = true;

// If the ref is null.
parent.implicitTrap = true;

// If the timeout is negative and no-one wakes us.
parent.mayNotReturn = true;

// struct.wait mutates an opaque waiter queue which isn't visible in user
// code. Model this as a struct write which prevents reorderings (since
// isAtomic == true).
parent.writesStruct = true;

if (curr->ref->type == Type::unreachable) {
return;
}

// If the ref isn't `unreachable`, then the field must exist and be a
// packed waitqueue due to validation.
assert(curr->ref->type.isStruct());
assert(curr->index <
curr->ref->type.getHeapType().getStruct().fields.size());
assert(curr->ref->type.getHeapType()
.getStruct()
.fields.at(curr->index)
.packedType == Field::PackedType::WaitQueue);

parent.readsMutableStruct |= curr->ref->type.getHeapType()
.getStruct()
.fields.at(curr->index)
.mutable_ == Mutable;
}
void visitArrayNew(ArrayNew* curr) {}
void visitArrayNewData(ArrayNewData* curr) {
// Traps on out of bounds access to segments or access to dropped
Expand Down
1 change: 1 addition & 0 deletions src/ir/module-utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ struct CodeScanner : PostWalker<CodeScanner> {
}
void visitStructGet(StructGet* curr) { info.note(curr->ref->type); }
void visitStructSet(StructSet* curr) { info.note(curr->ref->type); }
void visitStructWait(StructWait* curr) { info.note(curr->ref->type); }
void visitArrayGet(ArrayGet* curr) { info.note(curr->ref->type); }
void visitArraySet(ArraySet* curr) { info.note(curr->ref->type); }
void visitContBind(ContBind* curr) {
Expand Down
2 changes: 1 addition & 1 deletion src/ir/possible-constant.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ struct PossibleConstantValues {
}
break;
case Field::WaitQueue:
WASM_UNREACHABLE("waitqueue not implemented");
value = val;
break;
case Field::NotPacked:
WASM_UNREACHABLE("unexpected packed type");
Expand Down
1 change: 1 addition & 0 deletions src/ir/possible-contents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,7 @@ struct InfoCollector
// the write.
addRoot(curr);
}
void visitStructWait(StructWait* curr) { addRoot(curr); }
// Array operations access the array's location, parallel to how structs work.
void visitArrayGet(ArrayGet* curr) {
if (!isRelevant(curr->ref)) {
Expand Down
1 change: 1 addition & 0 deletions src/ir/subtype-exprs.h
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
type.isRef() ? Type(HeapType::eq, Nullable) : type);
self()->noteSubtype(curr->replacement, type);
}
void visitStructWait(StructWait* curr) {}
void visitArrayNew(ArrayNew* curr) {
if (!curr->type.isArray() || curr->isWithDefault()) {
return;
Expand Down
12 changes: 12 additions & 0 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,11 @@ struct NullInstrParserCtx {
return Ok{};
}
template<typename HeapTypeT>
Result<>
makeStructWait(Index, const std::vector<Annotation>&, HeapTypeT, FieldIdxT) {
return Ok{};
}
template<typename HeapTypeT>
Result<> makeArrayNew(Index, const std::vector<Annotation>&, HeapTypeT) {
return Ok{};
}
Expand Down Expand Up @@ -2712,6 +2717,13 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx>, AnnotationParserCtx {
return withLoc(pos, irBuilder.makeStructCmpxchg(type, field, order));
}

Result<> makeStructWait(Index pos,
const std::vector<Annotation>& annotations,
HeapType type,
Index field) {
return withLoc(pos, irBuilder.makeStructWait(type, field));
}

Result<> makeArrayNew(Index pos,
const std::vector<Annotation>& annotations,
HeapType type) {
Expand Down
11 changes: 11 additions & 0 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -2482,6 +2482,17 @@ Result<> makeStructCmpxchg(Ctx& ctx,
return ctx.makeStructCmpxchg(pos, annotations, *type, *field, *order1);
}

template<typename Ctx>
Result<> makeStructWait(Ctx& ctx,
Index pos,
const std::vector<Annotation>& annotations) {
auto type = typeidx(ctx);
CHECK_ERR(type);
auto field = fieldidx(ctx, *type);
CHECK_ERR(field);
return ctx.makeStructWait(pos, annotations, *type, *field);
}

template<typename Ctx>
Result<> makeArrayNew(Ctx& ctx,
Index pos,
Expand Down
7 changes: 7 additions & 0 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2404,6 +2404,13 @@ struct PrintExpressionContents
o << ' ';
printFieldName(heapType, curr->index);
}
void visitStructWait(StructWait* curr) {
printMedium(o, "struct.wait");
o << ' ';
printHeapTypeName(curr->ref->type.getHeapType());
o << ' ';
o << curr->index;
}
void visitArrayNew(ArrayNew* curr) {
printMedium(o, "array.new");
if (curr->isWithDefault()) {
Expand Down
2 changes: 2 additions & 0 deletions src/passes/TypeGeneralizing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,8 @@ struct TransferFn : OverriddenVisitor<TransferFn> {

void visitStructCmpxchg(StructCmpxchg* curr) { WASM_UNREACHABLE("TODO"); }

void visitStructWait(StructWait* curr) { WASM_UNREACHABLE("TODO"); }

void visitArrayNew(ArrayNew* curr) {
// We cannot yet generalize allocations. Push a requirement for the
// reference type needed to initialize the array, if any.
Expand Down
1 change: 1 addition & 0 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@ enum ASTNodes {
I64AtomicWait = 0x02,
AtomicFence = 0x03,
Pause = 0x04,
StructWait = 0x05,

I32AtomicLoad = 0x10,
I64AtomicLoad = 0x11,
Expand Down
13 changes: 13 additions & 0 deletions src/wasm-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1363,6 +1363,19 @@ class Builder {
return ret;
}

StructWait* makeStructWait(Index index,
Expression* ref,
Expression* expected,
Expression* timeout) {
auto* ret = wasm.allocator.alloc<StructWait>();
ret->index = index;
ret->ref = ref;
ret->expected = expected;
ret->timeout = timeout;
ret->finalize();
return ret;
}

// Additional helpers

Drop* makeDrop(Expression* value) {
Expand Down
7 changes: 7 additions & 0 deletions src/wasm-delegations-fields.def
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,13 @@ DELEGATE_FIELD_CHILD_VECTOR(StackSwitch, operands)
DELEGATE_FIELD_NAME_KIND(StackSwitch, tag, ModuleItemKind::Tag)
DELEGATE_FIELD_CASE_END(StackSwitch)

DELEGATE_FIELD_CASE_START(StructWait)
DELEGATE_FIELD_CHILD(StructWait, timeout)
DELEGATE_FIELD_CHILD(StructWait, expected)
DELEGATE_FIELD_IMMEDIATE_TYPED_CHILD(StructWait, ref)
DELEGATE_FIELD_INT(StructWait, index)
DELEGATE_FIELD_CASE_END(StructWait)


DELEGATE_FIELD_MAIN_END

Expand Down
1 change: 1 addition & 0 deletions src/wasm-delegations.def
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,6 @@ DELEGATE(Suspend);
DELEGATE(Resume);
DELEGATE(ResumeThrow);
DELEGATE(StackSwitch);
DELEGATE(StructWait);

#undef DELEGATE
6 changes: 6 additions & 0 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2243,6 +2243,11 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
return oldVal;
}

Flow visitStructWait(StructWait* curr) {
WASM_UNREACHABLE("struct.wait not implemented");
return Flow();
}

// Arbitrary deterministic limit on size. If we need to allocate a Literals
// vector that takes around 1-2GB of memory then we are likely to hit memory
// limits on 32-bit machines, and in particular on wasm32 VMs that do not
Expand Down Expand Up @@ -2899,6 +2904,7 @@ class ConstantExpressionRunner : public ExpressionRunner<SubType> {
}
Flow visitAtomicWait(AtomicWait* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitAtomicNotify(AtomicNotify* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitStructWait(StructWait* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitSIMDLoad(SIMDLoad* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitSIMDLoadSplat(SIMDLoad* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitSIMDLoadExtend(SIMDLoad* curr) { return Flow(NONCONSTANT_FLOW); }
Expand Down
1 change: 1 addition & 0 deletions src/wasm-ir-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
Result<>
makeStructRMW(AtomicRMWOp op, HeapType type, Index field, MemoryOrder order);
Result<> makeStructCmpxchg(HeapType type, Index field, MemoryOrder order);
Result<> makeStructWait(HeapType type, Index index);
Result<> makeArrayNew(HeapType type);
Result<> makeArrayNewDefault(HeapType type);
Result<> makeArrayNewData(HeapType type, Name data);
Expand Down
15 changes: 14 additions & 1 deletion src/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -758,8 +758,8 @@ class Expression {
SuspendId,
ResumeId,
ResumeThrowId,
// Id for the stack switching `switch`
StackSwitchId,
StructWaitId,
NumExpressionIds
};
Id _id;
Expand Down Expand Up @@ -1758,6 +1758,19 @@ class StructCmpxchg : public SpecificExpression<Expression::StructCmpxchgId> {
void finalize();
};

class StructWait : public SpecificExpression<Expression::StructWaitId> {
public:
StructWait() = default;
StructWait(MixedArena& allocator) : StructWait() {}

Expression* ref;
Expression* expected;
Expression* timeout;
Index index;

void finalize();
};

class ArrayNew : public SpecificExpression<Expression::ArrayNewId> {
public:
ArrayNew() = default;
Expand Down
5 changes: 5 additions & 0 deletions src/wasm/wasm-binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3913,6 +3913,11 @@ Result<> WasmBinaryReader::readInst() {
auto type = getIndexedHeapType();
return builder.makeArrayCmpxchg(type, order);
}
case BinaryConsts::StructWait: {
auto structType = getIndexedHeapType();
auto index = getU32LEB();
return builder.makeStructWait(structType, index);
}
}
return Err{"unknown atomic operation " + std::to_string(op)};
}
Expand Down
20 changes: 20 additions & 0 deletions src/wasm/wasm-ir-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,13 @@ struct IRBuilder::ChildPopper
return popConstrainedChildren(children);
}

Result<> visitStructWait(StructWait* curr,
std::optional<HeapType> structType = std::nullopt) {
std::vector<Child> children;
ConstraintCollector{builder, children}.visitStructWait(curr, structType);
return popConstrainedChildren(children);
}

Result<> visitArrayGet(ArrayGet* curr,
std::optional<HeapType> ht = std::nullopt) {
std::vector<Child> children;
Expand Down Expand Up @@ -2237,6 +2244,19 @@ IRBuilder::makeStructCmpxchg(HeapType type, Index field, MemoryOrder order) {
return Ok{};
}

Result<> IRBuilder::makeStructWait(HeapType type, Index index) {
if (!type.isStruct()) {
return Err{"expected struct type annotation on struct.wait"};
}
StructWait curr(wasm.allocator);
curr.index = index;
CHECK_ERR(ChildPopper{*this}.visitStructWait(&curr, type));
CHECK_ERR(validateTypeAnnotation(type, curr.ref));
push(
builder.makeStructWait(curr.index, curr.ref, curr.expected, curr.timeout));
return Ok{};
}

Result<> IRBuilder::makeArrayNew(HeapType type) {
if (!type.isArray()) {
return Err{"expected array type annotation on array.new"};
Expand Down
11 changes: 11 additions & 0 deletions src/wasm/wasm-stack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2516,6 +2516,17 @@ void BinaryInstWriter::visitStructCmpxchg(StructCmpxchg* curr) {
o << U32LEB(curr->index);
}

void BinaryInstWriter::visitStructWait(StructWait* curr) {
if (curr->ref->type.isNull()) {
emitUnreachable();
return;
}
o << static_cast<int8_t>(BinaryConsts::AtomicPrefix)
<< U32LEB(BinaryConsts::StructWait);
parent.writeIndexedHeapType(curr->ref->type.getHeapType());
o << U32LEB(curr->index);
}

void BinaryInstWriter::visitArrayNew(ArrayNew* curr) {
o << int8_t(BinaryConsts::GCPrefix);
if (curr->isWithDefault()) {
Expand Down
3 changes: 1 addition & 2 deletions src/wasm/wasm-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1488,8 +1488,7 @@ unsigned Field::getByteSize() const {
case Field::PackedType::NotPacked:
return 4;
case Field::PackedType::WaitQueue:
WASM_UNREACHABLE("waitqueue not implemented");
break;
return 4;
}
WASM_UNREACHABLE("impossible packed type");
}
Expand Down
Loading
Loading