summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ast/effects.h18
-rw-r--r--src/passes/Print.cpp23
-rw-r--r--src/wasm-binary.h8
-rw-r--r--src/wasm-builder.h18
-rw-r--r--src/wasm-s-parser.h2
-rw-r--r--src/wasm-traversal.h21
-rw-r--r--src/wasm-validator.h2
-rw-r--r--src/wasm.h26
-rw-r--r--src/wasm/wasm-binary.cpp55
-rw-r--r--src/wasm/wasm-s-parser.cpp24
-rw-r--r--src/wasm/wasm-validator.cpp14
-rw-r--r--src/wasm/wasm.cpp12
12 files changed, 221 insertions, 2 deletions
diff --git a/src/ast/effects.h b/src/ast/effects.h
index 2db671b5e..1ae27d2a7 100644
--- a/src/ast/effects.h
+++ b/src/ast/effects.h
@@ -208,6 +208,24 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
isAtomic = true;
if (!ignoreImplicitTraps) implicitTrap = true;
}
+ void visitAtomicWait(AtomicWait* curr) {
+ readsMemory = true;
+ // AtomicWait doesn't strictly write memory, but it does modify the waiters
+ // list associated with the specified address, which we can think of as a
+ // write.
+ writesMemory = true;
+ isAtomic = true;
+ if (!ignoreImplicitTraps) implicitTrap = true;
+ }
+ void visitAtomicWake(AtomicWake* curr) {
+ // AtomicWake doesn't strictly write memory, but it does modify the waiters
+ // list associated with the specified address, which we can think of as a
+ // write.
+ readsMemory = true;
+ writesMemory = true;
+ isAtomic = true;
+ if (!ignoreImplicitTraps) implicitTrap = true;
+ };
void visitUnary(Unary *curr) {
if (!ignoreImplicitTraps) {
switch (curr->op) {
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 33e06d7a5..3c538a40e 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -368,6 +368,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
}
void visitAtomicRMW(AtomicRMW* curr) {
o << '(';
+ prepareColor(o);
printRMWSize(o, curr->type, curr->bytes);
switch (curr->op) {
case Add: o << "add"; break;
@@ -388,6 +389,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
}
void visitAtomicCmpxchg(AtomicCmpxchg* curr) {
o << '(';
+ prepareColor(o);
printRMWSize(o, curr->type, curr->bytes);
o << "cmpxchg";
restoreNormalColor(o);
@@ -400,6 +402,24 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
printFullLine(curr->replacement);
decIndent();
}
+ void visitAtomicWait(AtomicWait* curr) {
+ o << '(' ;
+ prepareColor(o);
+ o << printWasmType(curr->expectedType) << ".wait";
+ restoreNormalColor(o);
+ incIndent();
+ printFullLine(curr->ptr);
+ printFullLine(curr->expected);
+ printFullLine(curr->timeout);
+ decIndent();
+ }
+ void visitAtomicWake(AtomicWake* curr) {
+ printOpening(o, "wake");
+ incIndent();
+ printFullLine(curr->ptr);
+ printFullLine(curr->wakeCount);
+ decIndent();
+ }
void visitConst(Const *curr) {
o << curr->value;
}
@@ -562,8 +582,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
decIndent();
}
void visitDrop(Drop *curr) {
- o << '(';
- prepareColor(o) << "drop";
+ printOpening(o, "drop");
incIndent();
printFullLine(curr->value);
decIndent();
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 9724bc93d..6df49b162 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -535,6 +535,10 @@ enum ASTNodes {
};
enum AtomicOpcodes {
+ AtomicWake = 0x00,
+ I32AtomicWait = 0x01,
+ I64AtomicWait = 0x02,
+
I32AtomicLoad = 0x10,
I64AtomicLoad = 0x11,
I32AtomicLoad8U = 0x12,
@@ -758,6 +762,8 @@ public:
void visitStore(Store *curr);
void visitAtomicRMW(AtomicRMW *curr);
void visitAtomicCmpxchg(AtomicCmpxchg *curr);
+ void visitAtomicWait(AtomicWait *curr);
+ void visitAtomicWake(AtomicWake *curr);
void visitConst(Const *curr);
void visitUnary(Unary *curr);
void visitBinary(Binary *curr);
@@ -924,6 +930,8 @@ public:
bool maybeVisitStore(Expression*& out, uint8_t code, bool isAtomic);
bool maybeVisitAtomicRMW(Expression*& out, uint8_t code);
bool maybeVisitAtomicCmpxchg(Expression*& out, uint8_t code);
+ bool maybeVisitAtomicWait(Expression*& out, uint8_t code);
+ bool maybeVisitAtomicWake(Expression*& out, uint8_t code);
bool maybeVisitConst(Expression*& out, uint8_t code);
bool maybeVisitUnary(Expression*& out, uint8_t code);
bool maybeVisitBinary(Expression*& out, uint8_t code);
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index df9f1fcaa..4ef4b1e0b 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -198,6 +198,24 @@ public:
load->isAtomic = true;
return load;
}
+ AtomicWait* makeAtomicWait(Expression* ptr, Expression* expected, Expression* timeout, WasmType type) {
+ auto* wait = allocator.alloc<AtomicWait>();
+ wait->ptr = ptr;
+ wait->expected = expected;
+ wait->timeout = timeout;
+ wait->expectedType = type;
+ wait->type = i32;
+ wait->finalize();
+ return wait;
+ }
+ AtomicWake* makeAtomicWake(Expression* ptr, Expression* wakeCount) {
+ auto* wake = allocator.alloc<AtomicWake>();
+ wake->ptr = ptr;
+ wake->wakeCount = wakeCount;
+ wake->type = i32;
+ wake->finalize();
+ return wake;
+ }
Store* makeStore(unsigned bytes, uint32_t offset, unsigned align, Expression *ptr, Expression *value, WasmType type) {
auto* ret = allocator.alloc<Store>();
ret->isAtomic = false;
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 6be17f561..31abb8fb9 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -184,6 +184,8 @@ private:
Expression* makeAtomicRMWOrCmpxchg(Element& s, WasmType type);
Expression* makeAtomicRMW(Element& s, WasmType type, uint8_t bytes, const char* extra);
Expression* makeAtomicCmpxchg(Element& s, WasmType type, uint8_t bytes, const char* extra);
+ Expression* makeAtomicWait(Element& s, WasmType type);
+ Expression* makeAtomicWake(Element& s);
Expression* makeIf(Element& s);
Expression* makeMaybeBlock(Element& s, size_t i, WasmType type);
Expression* makeLoop(Element& s);
diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h
index 49c7bfe06..6e46b8cb9 100644
--- a/src/wasm-traversal.h
+++ b/src/wasm-traversal.h
@@ -51,6 +51,8 @@ struct Visitor {
ReturnType visitStore(Store* curr) { return ReturnType(); }
ReturnType visitAtomicRMW(AtomicRMW* curr) { return ReturnType(); }
ReturnType visitAtomicCmpxchg(AtomicCmpxchg* curr) { return ReturnType(); }
+ ReturnType visitAtomicWait(AtomicWait* curr) { return ReturnType(); }
+ ReturnType visitAtomicWake(AtomicWake* curr) { return ReturnType(); }
ReturnType visitConst(Const* curr) { return ReturnType(); }
ReturnType visitUnary(Unary* curr) { return ReturnType(); }
ReturnType visitBinary(Binary* curr) { return ReturnType(); }
@@ -94,6 +96,8 @@ struct Visitor {
case Expression::Id::StoreId: DELEGATE(Store);
case Expression::Id::AtomicRMWId: DELEGATE(AtomicRMW);
case Expression::Id::AtomicCmpxchgId: DELEGATE(AtomicCmpxchg);
+ case Expression::Id::AtomicWaitId: DELEGATE(AtomicWait);
+ case Expression::Id::AtomicWakeId: DELEGATE(AtomicWake);
case Expression::Id::ConstId: DELEGATE(Const);
case Expression::Id::UnaryId: DELEGATE(Unary);
case Expression::Id::BinaryId: DELEGATE(Binary);
@@ -136,6 +140,8 @@ struct UnifiedExpressionVisitor : public Visitor<SubType> {
ReturnType visitStore(Store* curr) { return static_cast<SubType*>(this)->visitExpression(curr); }
ReturnType visitAtomicRMW(AtomicRMW* curr) { return static_cast<SubType*>(this)->visitExpression(curr); }
ReturnType visitAtomicCmpxchg(AtomicCmpxchg* curr) { return static_cast<SubType*>(this)->visitExpression(curr); }
+ ReturnType visitAtomicWait(AtomicWait* curr) { return static_cast<SubType*>(this)->visitExpression(curr); }
+ ReturnType visitAtomicWake(AtomicWake* curr) { return static_cast<SubType*>(this)->visitExpression(curr); }
ReturnType visitConst(Const* curr) { return static_cast<SubType*>(this)->visitExpression(curr); }
ReturnType visitUnary(Unary* curr) { return static_cast<SubType*>(this)->visitExpression(curr); }
ReturnType visitBinary(Binary* curr) { return static_cast<SubType*>(this)->visitExpression(curr); }
@@ -314,6 +320,8 @@ struct Walker : public VisitorType {
static void doVisitStore(SubType* self, Expression** currp) { self->visitStore((*currp)->cast<Store>()); }
static void doVisitAtomicRMW(SubType* self, Expression** currp) { self->visitAtomicRMW((*currp)->cast<AtomicRMW>()); }
static void doVisitAtomicCmpxchg(SubType* self, Expression** currp){ self->visitAtomicCmpxchg((*currp)->cast<AtomicCmpxchg>()); }
+ static void doVisitAtomicWait(SubType* self, Expression** currp) { self->visitAtomicWait((*currp)->cast<AtomicWait>()); }
+ static void doVisitAtomicWake(SubType* self, Expression** currp) { self->visitAtomicWake((*currp)->cast<AtomicWake>()); }
static void doVisitConst(SubType* self, Expression** currp) { self->visitConst((*currp)->cast<Const>()); }
static void doVisitUnary(SubType* self, Expression** currp) { self->visitUnary((*currp)->cast<Unary>()); }
static void doVisitBinary(SubType* self, Expression** currp) { self->visitBinary((*currp)->cast<Binary>()); }
@@ -449,6 +457,19 @@ struct PostWalker : public Walker<SubType, VisitorType> {
self->pushTask(SubType::scan, &curr->cast<AtomicCmpxchg>()->ptr);
break;
}
+ case Expression::Id::AtomicWaitId: {
+ self->pushTask(SubType::doVisitAtomicWait, currp);
+ self->pushTask(SubType::scan, &curr->cast<AtomicWait>()->timeout);
+ self->pushTask(SubType::scan, &curr->cast<AtomicWait>()->expected);
+ self->pushTask(SubType::scan, &curr->cast<AtomicWait>()->ptr);
+ break;
+ }
+ case Expression::Id::AtomicWakeId: {
+ self->pushTask(SubType::doVisitAtomicWake, currp);
+ self->pushTask(SubType::scan, &curr->cast<AtomicWake>()->wakeCount);
+ self->pushTask(SubType::scan, &curr->cast<AtomicWake>()->ptr);
+ break;
+ }
case Expression::Id::ConstId: {
self->pushTask(SubType::doVisitConst, currp);
break;
diff --git a/src/wasm-validator.h b/src/wasm-validator.h
index a359102ea..d78b54a47 100644
--- a/src/wasm-validator.h
+++ b/src/wasm-validator.h
@@ -142,6 +142,8 @@ public:
void visitStore(Store *curr);
void visitAtomicRMW(AtomicRMW *curr);
void visitAtomicCmpxchg(AtomicCmpxchg *curr);
+ void visitAtomicWait(AtomicWait *curr);
+ void visitAtomicWake(AtomicWake *curr);
void visitBinary(Binary *curr);
void visitUnary(Unary *curr);
void visitSelect(Select* curr);
diff --git a/src/wasm.h b/src/wasm.h
index 974bf1730..e9782ab6e 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -183,6 +183,8 @@ public:
UnreachableId,
AtomicCmpxchgId,
AtomicRMWId,
+ AtomicWaitId,
+ AtomicWakeId,
NumExpressionIds
};
Id _id;
@@ -456,6 +458,30 @@ class AtomicCmpxchg : public SpecificExpression<Expression::AtomicCmpxchgId> {
void finalize();
};
+class AtomicWait : public SpecificExpression<Expression::AtomicWaitId> {
+ public:
+ AtomicWait() = default;
+ AtomicWait(MixedArena& allocator) : AtomicWait() {}
+
+ Expression* ptr;
+ Expression* expected;
+ Expression* timeout;
+ WasmType expectedType;
+
+ void finalize();
+};
+
+class AtomicWake : public SpecificExpression<Expression::AtomicWakeId> {
+ public:
+ AtomicWake() = default;
+ AtomicWake(MixedArena& allocator) : AtomicWake() {}
+
+ Expression* ptr;
+ Expression* wakeCount;
+
+ void finalize();
+};
+
class Const : public SpecificExpression<Expression::ConstId> {
public:
Const() {}
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index b114b81fb..f0787600e 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -952,6 +952,27 @@ void WasmBinaryWriter::visitAtomicCmpxchg(AtomicCmpxchg *curr) {
emitMemoryAccess(curr->bytes, curr->bytes, curr->offset);
}
+void WasmBinaryWriter::visitAtomicWait(AtomicWait *curr) {
+ if (debug) std::cerr << "zz node: AtomicWait" << std::endl;
+ recurse(curr->ptr);
+ recurse(curr->expected);
+ recurse(curr->timeout);
+
+ o << int8_t(BinaryConsts::AtomicPrefix);
+ switch (curr->expectedType) {
+ case i32: o << int8_t(BinaryConsts::I32AtomicWait); break;
+ case i64: o << int8_t(BinaryConsts::I64AtomicWait); break;
+ default: WASM_UNREACHABLE();
+ }
+}
+
+void WasmBinaryWriter::visitAtomicWake(AtomicWake *curr) {
+ if (debug) std::cerr << "zz node: AtomicWake" << std::endl;
+ recurse(curr->ptr);
+ recurse(curr->wakeCount);
+
+ o << int8_t(BinaryConsts::AtomicPrefix) << int8_t(BinaryConsts::AtomicWake);
+}
void WasmBinaryWriter::visitConst(Const *curr) {
if (debug) std::cerr << "zz node: Const" << curr << " : " << curr->type << std::endl;
@@ -2123,6 +2144,8 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
if (maybeVisitStore(curr, code, /*isAtomic=*/true)) break;
if (maybeVisitAtomicRMW(curr, code)) break;
if (maybeVisitAtomicCmpxchg(curr, code)) break;
+ if (maybeVisitAtomicWait(curr, code)) break;
+ if (maybeVisitAtomicWake(curr, code)) break;
throw ParseException("invalid code after atomic prefix: " + std::to_string(code));
}
default: {
@@ -2553,6 +2576,38 @@ bool WasmBinaryBuilder::maybeVisitAtomicCmpxchg(Expression*& out, uint8_t code)
return true;
}
+bool WasmBinaryBuilder::maybeVisitAtomicWait(Expression*& out, uint8_t code) {
+ if (code < BinaryConsts::I32AtomicWait || code > BinaryConsts::I64AtomicWait) return false;
+ auto* curr = allocator.alloc<AtomicWait>();
+
+ switch (code) {
+ case BinaryConsts::I32AtomicWait: curr->expectedType = i32; break;
+ case BinaryConsts::I64AtomicWait: curr->expectedType = i64; break;
+ default: WASM_UNREACHABLE();
+ }
+ curr->type = i32;
+ if (debug) std::cerr << "zz node: AtomicWait" << std::endl;
+ curr->timeout = popNonVoidExpression();
+ curr->expected = popNonVoidExpression();
+ curr->ptr = popNonVoidExpression();
+ curr->finalize();
+ out = curr;
+ return true;
+}
+
+bool WasmBinaryBuilder::maybeVisitAtomicWake(Expression*& out, uint8_t code) {
+ if (code != BinaryConsts::AtomicWake) return false;
+ auto* curr = allocator.alloc<AtomicWake>();
+ if (debug) std::cerr << "zz node: AtomicWake" << std::endl;
+
+ curr->type = i32;
+ curr->wakeCount = popNonVoidExpression();
+ curr->ptr = popNonVoidExpression();
+ curr->finalize();
+ out = curr;
+ return true;
+}
+
bool WasmBinaryBuilder::maybeVisitConst(Expression*& out, uint8_t code) {
Const* curr;
if (debug) std::cerr << "zz node: Const, code " << code << std::endl;
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 044507e86..fd7c7e7ae 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -781,6 +781,7 @@ Expression* SExpressionWasmBuilder::makeExpression(Element& s) {
abort_on(op);
}
case 'w': {
+ if (!strncmp(op, "wait", strlen("wait"))) return makeAtomicWait(s, type);
if (op[1] == 'r') return makeUnary(s, UnaryOp::WrapInt64, type);
abort_on(op);
}
@@ -866,6 +867,9 @@ Expression* SExpressionWasmBuilder::makeExpression(Element& s) {
if (str[1] == 'n') return allocator.alloc<Unreachable>();
abort_on(str);
}
+ case 'w': {
+ if (!strncmp(str, "wake", strlen("wake"))) return makeAtomicWake(s);
+ }
default: abort_on(str);
}
}
@@ -1241,6 +1245,26 @@ Expression* SExpressionWasmBuilder::makeAtomicCmpxchg(Element& s, WasmType type,
return ret;
}
+Expression* SExpressionWasmBuilder::makeAtomicWait(Element& s, WasmType type) {
+ auto ret = allocator.alloc<AtomicWait>();
+ ret->type = i32;
+ ret->expectedType = type;
+ ret->ptr = parseExpression(s[1]);
+ ret->expected = parseExpression(s[2]);
+ ret->timeout = parseExpression(s[3]);
+ ret->finalize();
+ return ret;
+}
+
+Expression* SExpressionWasmBuilder::makeAtomicWake(Element& s) {
+ auto ret = allocator.alloc<AtomicWake>();
+ ret->type = i32;
+ ret->ptr = parseExpression(s[1]);
+ ret->wakeCount = parseExpression(s[2]);
+ ret->finalize();
+ return ret;
+}
+
Expression* SExpressionWasmBuilder::makeIf(Element& s) {
auto ret = allocator.alloc<If>();
Index i = 1;
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index aaf5a7fd5..819602608 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -261,6 +261,20 @@ void WasmValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
shouldBeEqualOrFirstIsUnreachable(curr->replacement->type, curr->type, curr, "Cmpxchg result type must match replacement");
shouldBeIntOrUnreachable(curr->expected->type, curr, "Atomic operations are only valid on int types");
}
+void WasmValidator::visitAtomicWait(AtomicWait* curr) {
+ if (!getModule()->memory.shared) fail("Atomic operation with non-shared memory", curr);
+ shouldBeEqualOrFirstIsUnreachable(curr->type, i32, curr, "AtomicWait must have type i32");
+ shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicWait pointer type must be i32");
+ shouldBeIntOrUnreachable(curr->expected->type, curr, "AtomicWait expected type must be int");
+ shouldBeEqualOrFirstIsUnreachable(curr->expected->type, curr->expectedType, curr, "AtomicWait expected type must match operand");
+ shouldBeEqualOrFirstIsUnreachable(curr->timeout->type, i64, curr, "AtomicWait timeout type must be i64");
+}
+void WasmValidator::visitAtomicWake(AtomicWake* curr) {
+ if (!getModule()->memory.shared) fail("Atomic operation with non-shared memory", curr);
+ shouldBeEqualOrFirstIsUnreachable(curr->type, i32, curr, "AtomicWake must have type i32");
+ shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicWake pointer type must be i32");
+ shouldBeEqualOrFirstIsUnreachable(curr->wakeCount->type, i32, curr, "AtomicWake wakeCount type must be i32");
+}
void WasmValidator::validateMemBytes(uint8_t bytes, WasmType type, Expression* curr) {
switch (bytes) {
case 1:
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 19ba5be21..f82a06e68 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -368,6 +368,18 @@ void AtomicCmpxchg::finalize() {
}
}
+void AtomicWait::finalize() {
+ if (ptr->type == unreachable || expected->type == unreachable || timeout->type == unreachable) {
+ type = unreachable;
+ }
+}
+
+void AtomicWake::finalize() {
+ if (ptr->type == unreachable || wakeCount->type == unreachable) {
+ type = unreachable;
+ }
+}
+
Const* Const::set(Literal value_) {
value = value_;
type = value.type;