Skip to content
Draft
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 @@ -283,6 +283,7 @@ struct ExpressionInterpreter : OverriddenVisitor<ExpressionInterpreter, Flow> {
Flow visitResume(Resume* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitResumeThrow(ResumeThrow* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStackSwitch(StackSwitch* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStructWait(StructWait* curr) { WASM_UNREACHABLE("TODO"); }
};

} // anonymous namespace
Expand Down
1 change: 1 addition & 0 deletions src/ir/ReFinalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ void ReFinalize::visitResumeThrow(ResumeThrow* curr) {
}
}
void ReFinalize::visitStackSwitch(StackSwitch* curr) { curr->finalize(); }
void ReFinalize::visitStructWait(StructWait* curr) { curr->finalize(); }

void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); }
void ReFinalize::visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); }
Expand Down
10 changes: 10 additions & 0 deletions src/ir/child-typer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,16 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
}
note(&curr->cont, Type(*ct, Nullable));
}

void visitStructWait(StructWait* curr,
std::optional<HeapType> ht = std::nullopt) {
if (!ht) {
ht = curr->structType;
}
note(&curr->ref, Type(*ht, Nullable));
note(&curr->expected, Type(Type::BasicType::i32));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing this to AtomicWait, I see note(&curr->expected, curr->expectedType); there. Why is it i32 here? Is the spec just very different perhaps?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AtomicWait on memory comes in i32 and i64 variants. So far we only have an i32 variant for waitqueues.

note(&curr->timeout, Type(Type::BasicType::i64));
}
};

} // namespace wasm
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
16 changes: 16 additions & 0 deletions src/ir/effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,22 @@ class EffectAnalyzer {
parent.throws_ = true;
}
}

void visitStructWait(StructWait* curr) {
parent.isAtomic = true;
parent.mayNotReturn = true;
parent.implicitTrap = true;

// field must exist and be a packed waitqueue if this is valid Wasm.
if (curr->type.isStruct() &&
curr->index < curr->type.getHeapType().getStruct().fields.size() &&
curr->type.getHeapType()
.getStruct()
.fields.at(curr->index)
.mutable_ == Mutable) {
parent.readsMutableStruct = true;
}
}
};

public:
Expand Down
6 changes: 6 additions & 0 deletions src/ir/module-utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,12 @@ struct CodeScanner : PostWalker<CodeScanner> {
info.note(curr->cont->type);
info.note(curr->type);
}
void visitStructWait(StructWait* curr) {
info.note(curr->structType);
if (curr->ref && curr->ref->type != Type::unreachable) {
info.note(curr->ref->type);
}
}
void visitBlock(Block* curr) {
info.noteControlFlow(Signature(Type::none, curr->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 @@ -1401,6 +1401,7 @@ struct InfoCollector
// TODO: optimize when possible
addRoot(curr);
}
void visitStructWait(StructWait* curr) { addRoot(curr); }

void visitFunction(Function* func) {
// Functions with a result can flow a value out from their body.
Expand Down
3 changes: 3 additions & 0 deletions src/ir/subtype-exprs.h
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,9 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
.type.getSignature();
self()->noteSubtype(currResult, retSig.results);
}
void visitStructWait(StructWait* curr) {
// todo?
}
};

} // namespace wasm
Expand Down
13 changes: 13 additions & 0 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,12 @@ struct NullInstrParserCtx {
makeStackSwitch(Index, const std::vector<Annotation>&, HeapTypeT, TagIdxT) {
return Ok{};
}

template<typename HeapTypeT>
Result<>
makeStructWait(Index, const std::vector<Annotation>&, HeapTypeT, FieldIdxT) {
return Ok{};
}
};

struct NullCtx : NullTypeParserCtx, NullInstrParserCtx {
Expand Down Expand Up @@ -2947,6 +2953,13 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx>, AnnotationParserCtx {
Name tag) {
return withLoc(pos, irBuilder.makeStackSwitch(type, tag));
}

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

} // namespace wasm::WATParser
Expand Down
11 changes: 11 additions & 0 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -2840,6 +2840,17 @@ Result<> makeStackSwitch(Ctx& ctx,
return ctx.makeStackSwitch(pos, annotations, *type, *tag);
}

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);
}

// =======
// Modules
// =======
Expand Down
13 changes: 13 additions & 0 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,11 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
visitExpression(curr);
}
}
void visitStructWait(StructWait* curr) {
if (!maybePrintUnreachableReplacement(curr, curr->type)) {
visitExpression(curr);
}
}

// Module-level visitors
void handleSignature(Function* curr, bool printImplicitNames = false);
Expand Down Expand Up @@ -2658,6 +2663,14 @@ struct PrintExpressionContents
o << ' ';
curr->tag.print(o);
}

void visitStructWait(StructWait* curr) {
printMedium(o, "struct.wait");
o << ' ';
printHeapTypeName(curr->structType);
o << ' ';
o << curr->index;
}
};

void PrintSExpression::setModule(Module* module) {
Expand Down
1 change: 1 addition & 0 deletions src/passes/TypeGeneralizing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,7 @@ struct TransferFn : OverriddenVisitor<TransferFn> {
void visitResume(Resume* curr) { WASM_UNREACHABLE("TODO"); }
void visitResumeThrow(ResumeThrow* curr) { WASM_UNREACHABLE("TODO"); }
void visitStackSwitch(StackSwitch* curr) { WASM_UNREACHABLE("TODO"); }
void visitStructWait(StructWait* curr) { WASM_UNREACHABLE("TODO"); }
};

struct TypeGeneralizing : WalkerPass<PostWalker<TypeGeneralizing>> {
Expand Down
8 changes: 5 additions & 3 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 Expand Up @@ -1252,9 +1253,10 @@ enum ASTNodes {
Resume = 0xe3,
ResumeThrow = 0xe4,
ResumeThrowRef = 0xe5,
Switch = 0xe6, // NOTE(dhil): the internal class is known as
// StackSwitch to avoid conflict with the existing
// 'switch table'.
Switch = 0xe6, // NOTE(dhil): the internal class is known as
// StackSwitch to avoid conflict with the existing
// 'switch table'.

OnLabel = 0x00, // (on $tag $label)
OnSwitch = 0x01 // (on $tag switch)
};
Expand Down
15 changes: 15 additions & 0 deletions src/wasm-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1363,6 +1363,21 @@ class Builder {
return ret;
}

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

// Additional helpers

Drop* makeDrop(Expression* value) {
Expand Down
8 changes: 8 additions & 0 deletions src/wasm-delegations-fields.def
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,14 @@ 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_HEAPTYPE(StructWait, structType)
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
5 changes: 5 additions & 0 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2932,6 +2932,7 @@ class ConstantExpressionRunner : public ExpressionRunner<SubType> {
Flow visitResume(Resume* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitResumeThrow(ResumeThrow* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitStackSwitch(StackSwitch* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitStructWait(StructWait* curr) { return Flow(NONCONSTANT_FLOW); }

void trap(std::string_view why) override { throw NonconstantException(); }

Expand Down Expand Up @@ -4918,6 +4919,10 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
Flow visitResume(Resume* curr) { return doResume(curr); }
Flow visitResumeThrow(ResumeThrow* curr) { return doResume(curr); }
Flow visitStackSwitch(StackSwitch* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitStructWait(StructWait* curr) {
WASM_UNREACHABLE("struct.wait not implemented");
return Flow();
}

void trap(std::string_view why) override {
// Traps break all current continuations - they will never be resumable.
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 @@ -285,6 +285,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
return makeResumeThrow(ct, Name(), tags, labels);
}
Result<> makeStackSwitch(HeapType ct, Name tag);
Result<> makeStructWait(HeapType type, Index index);

// Private functions that must be public for technical reasons.
Result<> visitExpression(Expression*);
Expand Down
15 changes: 15 additions & 0 deletions src/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,7 @@ class Expression {
ResumeThrowId,
// Id for the stack switching `switch`
StackSwitchId,
StructWaitId,
NumExpressionIds
};
Id _id;
Expand Down Expand Up @@ -2155,6 +2156,20 @@ class StackSwitch : public SpecificExpression<Expression::StackSwitchId> {
void finalize();
};

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

Expression* ref = nullptr;
Expression* expected = nullptr;
Expression* timeout = nullptr;
HeapType structType;
Index index;

void finalize();
};

// Globals

struct Named {
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
16 changes: 16 additions & 0 deletions src/wasm/wasm-ir-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,12 @@ struct IRBuilder::ChildPopper
ConstraintCollector{builder, children}.visitStackSwitch(curr, ct);
return popConstrainedChildren(children);
}

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

Result<> IRBuilder::visit(Expression* curr) {
Expand Down Expand Up @@ -2655,6 +2661,16 @@ Result<> IRBuilder::makeStackSwitch(HeapType ct, Name tag) {
return Ok{};
}

Result<> IRBuilder::makeStructWait(HeapType type, Index index) {
StructWait curr(wasm.allocator);
curr.structType = type;
curr.index = index;
CHECK_ERR(ChildPopper{*this}.visitStructWait(&curr));
push(builder.makeStructWait(
curr.structType, curr.index, curr.ref, curr.expected, curr.timeout));
return Ok{};
}

void IRBuilder::applyAnnotations(Expression* expr,
const CodeAnnotation& annotation) {
if (annotation.branchLikely) {
Expand Down
7 changes: 7 additions & 0 deletions src/wasm/wasm-stack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2911,6 +2911,13 @@ void BinaryInstWriter::visitStackSwitch(StackSwitch* curr) {
o << U32LEB(parent.getTagIndex(curr->tag));
}

void BinaryInstWriter::visitStructWait(StructWait* curr) {
o << static_cast<int8_t>(BinaryConsts::AtomicPrefix)
<< U32LEB(BinaryConsts::StructWait);
parent.writeIndexedHeapType(curr->structType);
o << U32LEB(curr->index);
}

void BinaryInstWriter::emitScopeEnd(Expression* curr) {
assert(!breakStack.empty());
breakStack.pop_back();
Expand Down
Loading
Loading