From 4750f31eef08ab620e656f79ed9df03c17bd38c9 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Wed, 28 Jun 2017 15:17:45 -0700 Subject: [PATCH 01/10] parse atomic rmw wast --- src/wasm-s-parser.h | 1 + src/wasm.h | 32 +++++++-- src/wasm/wasm-binary.cpp | 1 + src/wasm/wasm-s-parser.cpp | 130 ++++++++++++++++++++----------------- src/wasm/wasm.cpp | 9 +++ 5 files changed, 105 insertions(+), 68 deletions(-) diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 90e797615b1..161256c2f1a 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -177,6 +177,7 @@ class SExpressionWasmBuilder { Expression* makeConst(Element& s, WasmType type); Expression* makeLoad(Element& s, WasmType type, bool isAtomic); Expression* makeStore(Element& s, WasmType type, bool isAtomic); + Expression* makeAtomicRMW(Element& s, WasmType type); Expression* makeIf(Element& s); Expression* makeMaybeBlock(Element& s, size_t i, WasmType type); Expression* makeLoop(Element& s); diff --git a/src/wasm.h b/src/wasm.h index 286604848cd..a5bdfc8dd1f 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -130,6 +130,10 @@ enum HostOp { PageSize, CurrentMemory, GrowMemory, HasFeature }; +enum AtomicRMWOp { + Add, Sub, And, Or, Xor, Xchg, +}; + // // Expressions // @@ -177,6 +181,7 @@ class Expression { HostId, NopId, UnreachableId, + AtomicCmpxchgId, AtomicRMWId, NumExpressionIds }; @@ -423,6 +428,26 @@ class Store : public SpecificExpression { void finalize(); }; +class AtomicRMW : public SpecificExpression { + public: + AtomicRMW() = default; + AtomicRMW(MixedArena& allocator) : AtomicRMW() {} + + AtomicRMWOp op; + uint8_t bytes; + Address offset; + Expression* ptr; + Expression* value; + WasmType valueType; + + void finalize(); +}; + +class AtomicCmpxchg : public SpecificExpression { + public: + AtomicCmpxchg() = default; +}; + class Const : public SpecificExpression { public: Const() {} @@ -514,13 +539,6 @@ class Unreachable : public SpecificExpression { Unreachable(MixedArena& allocator) : Unreachable() {} }; -class AtomicRMW : public SpecificExpression { - public: - AtomicRMW() {} - AtomicRMW(MixedArena& allocator) : AtomicRMW() {} - bool finalize(); -}; - // Globals class Function { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 4844dd5b4b1..2665a4c45ea 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1934,6 +1934,7 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { code = getInt8(); if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) break; if (maybeVisitStore(curr, code, /*isAtomic=*/true)) break; + //if (maybeVisitRMW(curr, code)) break; throw ParseException("invalid code after atomic prefix: " + std::to_string(code)); } default: { diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 38954af1f0f..d3009aa2ced 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -666,6 +666,7 @@ Expression* SExpressionWasmBuilder::makeExpression(Element& s) { if (op[1] == 't' && !strncmp(op, "atomic.", strlen("atomic."))) { if (op[7] == 'l') return makeLoad(s, type, /*isAtomic=*/true); if (op[7] == 's') return makeStore(s, type, /*isAtomic=*/true); + if (op[7] == 'r') return makeAtomicRMW(s, type); } abort_on(op); } @@ -1125,93 +1126,100 @@ Expression* SExpressionWasmBuilder::makeConst(Element& s, WasmType type) { return ret; } - -Expression* SExpressionWasmBuilder::makeLoad(Element& s, WasmType type, bool isAtomic) { - const char *extra = strchr(s[0]->c_str(), '.') + 5; // after "type.load" - if (isAtomic) extra += 7; // after "type.atomic.load" - auto* ret = allocator.alloc(); - ret->isAtomic = isAtomic; - ret->type = type; - ret->bytes = getWasmTypeSize(type); - if (extra[0] == '8') { - ret->bytes = 1; - extra++; - } else if (extra[0] == '1') { - if (extra[1] != '6') throw ParseException("expected load16"); - ret->bytes = 2; - extra += 2; - } else if (extra[0] == '3') { - if (extra[1] != '2') throw ParseException("expected load32"); - ret->bytes = 4; - extra += 2; +static uint8_t parseMemBytes(const char** in, uint8_t fallback) { + uint8_t ret; + const char* s = *in; + if (s[0] == '8') { + ret = 1; + (*in)++; + } else if (s[0] == '1') { + if (s[1] != '6') throw ParseException("expected 16 for memop size"); + ret = 2; + *in += 2; + } else if (s[0] == '3') { + if (s[1] != '2') throw ParseException("expected 32 for memop size");; + ret = 4; + *in += 2; + } else { + ret = fallback; } - ret->signed_ = extra[0] && extra[1] == 's'; + return ret; +} + +static size_t parseMemAttributes(Element& s, Address* offset, Address* align, Address fallback) { size_t i = 1; - ret->offset = 0; - ret->align = ret->bytes; + *offset = 0; + *align = fallback; while (!s[i]->isList()) { const char *str = s[i]->c_str(); const char *eq = strchr(str, '='); - if (!eq) throw ParseException("no = in load attribute"); + if (!eq) throw ParseException("missing = in memory attribute"); eq++; if (str[0] == 'a') { - uint64_t align = atoll(eq); - if (align > std::numeric_limits::max()) throw ParseException("bad align"); - ret->align = align; + uint64_t a = atoll(eq); + if (a > std::numeric_limits::max()) throw ParseException("bad align"); + *align = a; } else if (str[0] == 'o') { - uint64_t offset = atoll(eq); - if (offset > std::numeric_limits::max()) throw ParseException("bad offset"); - ret->offset = (uint32_t)offset; - } else throw ParseException("bad load attribute"); + uint64_t o = atoll(eq); + if (o > std::numeric_limits::max()) throw ParseException("bad offset"); + *offset = o; + } else throw ParseException("bad memory attribute"); i++; } + return i; +} + + +Expression* SExpressionWasmBuilder::makeLoad(Element& s, WasmType type, bool isAtomic) { + const char *extra = strchr(s[0]->c_str(), '.') + 5; // after "type.load" + if (isAtomic) extra += 7; // after "type.atomic.load" + auto* ret = allocator.alloc(); + ret->isAtomic = isAtomic; + ret->type = type; + ret->bytes = parseMemBytes(&extra, getWasmTypeSize(type)); + ret->signed_ = extra[0] && extra[1] == 's'; + size_t i = parseMemAttributes(s, &ret->offset, &ret->align, ret->bytes); ret->ptr = parseExpression(s[i]); ret->finalize(); return ret; } - + Expression* SExpressionWasmBuilder::makeStore(Element& s, WasmType type, bool isAtomic) { const char *extra = strchr(s[0]->c_str(), '.') + 6; // after "type.store" if (isAtomic) extra += 7; // after "type.atomic.store" auto ret = allocator.alloc(); ret->isAtomic = isAtomic; ret->valueType = type; - ret->bytes = getWasmTypeSize(type); - if (extra[0] == '8') { - ret->bytes = 1; - extra++; - } else if (extra[0] == '1') { - if (extra[1] != '6') throw ParseException("expected store16"); - ret->bytes = 2; - extra += 2; - } else if (extra[0] == '3') { - if (extra[1] != '2') throw ParseException("expected store32");; - ret->bytes = 4; - extra += 2; - } - size_t i = 1; - ret->offset = 0; - ret->align = ret->bytes; - while (!s[i]->isList()) { - const char *str = s[i]->c_str(); - const char *eq = strchr(str, '='); - if (!eq) throw ParseException("missing = in store attribute");; - eq++; - if (str[0] == 'a') { - uint64_t align = atoll(eq); - if (align > std::numeric_limits::max()) throw ParseException("bad align"); - ret->align = align; - } else if (str[0] == 'o') { - ret->offset = atoi(eq); - } else throw ParseException("bad store attribute"); - i++; - } + ret->bytes = parseMemBytes(&extra, getWasmTypeSize(type)); + size_t i = parseMemAttributes(s, &ret->offset, &ret->align, ret->bytes); + ret->ptr = parseExpression(s[i]); ret->value = parseExpression(s[i+1]); ret->finalize(); return ret; } +Expression* SExpressionWasmBuilder::makeAtomicRMW(Element& s, WasmType type) { + const char* extra = strchr(s[0]->c_str(), '.') + 10; // afer "type.atomic.rmw" + auto ret = allocator.alloc(); + ret->valueType = type; + ret->bytes = parseMemBytes(&extra, getWasmTypeSize(type)); + extra = strchr(extra, '.'); // after the optional '_u' and before the opcode + if (!extra) throw ParseException("malformed atomic rmw instruction"); + extra++; // after the '.' + if (!strncmp(extra, "add", 3)) ret->op = Add; + else if (!strncmp(extra, "and", 3)) ret->op = And; + else if (!strncmp(extra, "or", 2)) ret->op = Or; + else if (!strncmp(extra, "sub", 3)) ret->op = Sub; + else if (!strncmp(extra, "xor", 3)) ret->op = Xor; + else if (!strncmp(extra, "xchg", 4)) ret->op = Xchg; + else throw ParseException("bad atomic rmw operator"); + Address align; + parseMemAttributes(s, &ret->offset, &align, ret->bytes); + if (align != ret->bytes) throw ParseException("Align of Atomic RMW must match size"); + return nullptr; +} + Expression* SExpressionWasmBuilder::makeIf(Element& s) { auto ret = allocator.alloc(); Index i = 1; diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 201c8d18368..1ef321712e2 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -353,6 +353,15 @@ void Store::finalize() { } } +void AtomicRMW::finalize() { + assert(valueType != none); + if (ptr->type == unreachable || value->type == unreachable) { + type = unreachable; + } else { + type = valueType; + } +} + Const* Const::set(Literal value_) { value = value_; type = value.type; From f79fed24404b47985850d24db55165e0565d4928 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Thu, 29 Jun 2017 14:33:19 -0700 Subject: [PATCH 02/10] start --- src/wasm/wasm-s-parser.cpp | 11 +++++++---- test/atomics.wast | 22 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index d3009aa2ced..94bcf0611d9 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1169,7 +1169,6 @@ static size_t parseMemAttributes(Element& s, Address* offset, Address* align, Ad return i; } - Expression* SExpressionWasmBuilder::makeLoad(Element& s, WasmType type, bool isAtomic) { const char *extra = strchr(s[0]->c_str(), '.') + 5; // after "type.load" if (isAtomic) extra += 7; // after "type.atomic.load" @@ -1183,7 +1182,7 @@ Expression* SExpressionWasmBuilder::makeLoad(Element& s, WasmType type, bool isA ret->finalize(); return ret; } - + Expression* SExpressionWasmBuilder::makeStore(Element& s, WasmType type, bool isAtomic) { const char *extra = strchr(s[0]->c_str(), '.') + 6; // after "type.store" if (isAtomic) extra += 7; // after "type.atomic.store" @@ -1204,6 +1203,7 @@ Expression* SExpressionWasmBuilder::makeAtomicRMW(Element& s, WasmType type) { auto ret = allocator.alloc(); ret->valueType = type; ret->bytes = parseMemBytes(&extra, getWasmTypeSize(type)); + std::cout << "bytes "<<(int)ret->bytes; extra = strchr(extra, '.'); // after the optional '_u' and before the opcode if (!extra) throw ParseException("malformed atomic rmw instruction"); extra++; // after the '.' @@ -1215,9 +1215,12 @@ Expression* SExpressionWasmBuilder::makeAtomicRMW(Element& s, WasmType type) { else if (!strncmp(extra, "xchg", 4)) ret->op = Xchg; else throw ParseException("bad atomic rmw operator"); Address align; - parseMemAttributes(s, &ret->offset, &align, ret->bytes); + size_t i = parseMemAttributes(s, &ret->offset, &align, ret->bytes); if (align != ret->bytes) throw ParseException("Align of Atomic RMW must match size"); - return nullptr; + ret->ptr = parseExpression(s[i]); + ret->value = parseExpression(s[i+1]); + ret->finalize(); + return ret; } Expression* SExpressionWasmBuilder::makeIf(Element& s) { diff --git a/test/atomics.wast b/test/atomics.wast index af78b98d57d..9070a23c96e 100644 --- a/test/atomics.wast +++ b/test/atomics.wast @@ -1,7 +1,7 @@ (module (type $0 (func)) (memory $0 23 256 shared) - (func $atomics (type $0) + (func $atomic-loadstoare (type $0) (local $0 i32) (local $1 i64) (drop @@ -39,11 +39,11 @@ (get_local $0) ) ) - (i32.atomic.store offset=4 + (i32.atomic.store offset=4 align=4 (get_local $0) (get_local $0) ) - (i32.atomic.store8 offset=4 + (i32.atomic.store8 offset=4 align=1 (get_local $0) (get_local $0) ) @@ -68,4 +68,20 @@ (get_local $1) ) ) + (func $atomic-rmw (type $0) + (local $0 i32) + (local $1 i64) + (drop + (i32.atomic.rmw.add offset=4 + (get_local $0) + (get_local $0) + ) + ) + (drop + (i32.atomic.rmw8_u.add offset=4 + (get_local $0) + (get_local $0) + ) + ) + ) ) From f19ccb27e4829bf16105417be0082eb873f3ddaa Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Fri, 30 Jun 2017 16:44:11 -0700 Subject: [PATCH 03/10] add printing, fix pass traversal --- src/passes/Print.cpp | 33 +++++++++++++++++++++++++++++++++ src/wasm-traversal.h | 10 ++++++++++ src/wasm.h | 1 - src/wasm/wasm-s-parser.cpp | 5 ++--- src/wasm/wasm.cpp | 6 +----- 5 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 7ec3e98a3ec..d0c9603f512 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -350,6 +350,39 @@ struct PrintSExpression : public Visitor { printFullLine(curr->value); decIndent(); } + void visitAtomicRMW(AtomicRMW* curr) { + o << '('; + prepareColor(o) << printWasmType(curr->type) << ".atomic.rmw"; + if (curr->bytes != getWasmTypeSize(curr->type)) { + if (curr->bytes == 1) { + o << '8'; + } else if (curr->bytes == 2) { + o << "16"; + } else if (curr->bytes == 4) { + o << "32"; + } else { + WASM_UNREACHABLE(); + } + o << "_u"; + } + o << '.'; + switch (curr->op) { + case Add: o << "add"; break; + case Sub: o << "sub"; break; + case And: o << "and"; break; + case Or: o << "or"; break; + case Xor: o << "xor"; break; + case Xchg: o << "xchg"; break; + } + restoreNormalColor(o); + if (curr->offset) { + o << " offset=" << curr->offset; + } + incIndent(); + printFullLine(curr->ptr); + printFullLine(curr->value); + decIndent(); + } void visitConst(Const *curr) { o << curr->value; } diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index 3b1de2e32ec..11332b5c317 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -49,6 +49,7 @@ struct Visitor { ReturnType visitSetGlobal(SetGlobal* curr) {} ReturnType visitLoad(Load* curr) {} ReturnType visitStore(Store* curr) {} + ReturnType visitAtomicRMW(AtomicRMW* curr) {return ReturnType();} //Stub impl so not every pass has to implement this yet. ReturnType visitConst(Const* curr) {} ReturnType visitUnary(Unary* curr) {} ReturnType visitBinary(Binary* curr) {} @@ -90,6 +91,7 @@ struct Visitor { case Expression::Id::SetGlobalId: DELEGATE(SetGlobal); case Expression::Id::LoadId: DELEGATE(Load); case Expression::Id::StoreId: DELEGATE(Store); + case Expression::Id::AtomicRMWId: DELEGATE(AtomicRMW); case Expression::Id::ConstId: DELEGATE(Const); case Expression::Id::UnaryId: DELEGATE(Unary); case Expression::Id::BinaryId: DELEGATE(Binary); @@ -130,6 +132,7 @@ struct UnifiedExpressionVisitor : public Visitor { ReturnType visitSetGlobal(SetGlobal* curr) { return static_cast(this)->visitExpression(curr); } ReturnType visitLoad(Load* curr) { return static_cast(this)->visitExpression(curr); } ReturnType visitStore(Store* curr) { return static_cast(this)->visitExpression(curr); } + ReturnType visitAtomicRMW(AtomicRMW* curr) { return static_cast(this)->visitExpression(curr); } ReturnType visitConst(Const* curr) { return static_cast(this)->visitExpression(curr); } ReturnType visitUnary(Unary* curr) { return static_cast(this)->visitExpression(curr); } ReturnType visitBinary(Binary* curr) { return static_cast(this)->visitExpression(curr); } @@ -306,6 +309,7 @@ struct Walker : public VisitorType { static void doVisitSetGlobal(SubType* self, Expression** currp) { self->visitSetGlobal((*currp)->cast()); } static void doVisitLoad(SubType* self, Expression** currp) { self->visitLoad((*currp)->cast()); } static void doVisitStore(SubType* self, Expression** currp) { self->visitStore((*currp)->cast()); } + static void doVisitAtomicRMW(SubType* self, Expression** currp) { self->visitAtomicRMW((*currp)->cast()); } static void doVisitConst(SubType* self, Expression** currp) { self->visitConst((*currp)->cast()); } static void doVisitUnary(SubType* self, Expression** currp) { self->visitUnary((*currp)->cast()); } static void doVisitBinary(SubType* self, Expression** currp) { self->visitBinary((*currp)->cast()); } @@ -428,6 +432,12 @@ struct PostWalker : public Walker { self->pushTask(SubType::scan, &curr->cast()->ptr); break; } + case Expression::Id::AtomicRMWId: { + self->pushTask(SubType::doVisitAtomicRMW, currp); + self->pushTask(SubType::scan, &curr->cast()->value); + self->pushTask(SubType::scan, &curr->cast()->ptr); + break; + } case Expression::Id::ConstId: { self->pushTask(SubType::doVisitConst, currp); break; diff --git a/src/wasm.h b/src/wasm.h index a5bdfc8dd1f..e98c4db631f 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -438,7 +438,6 @@ class AtomicRMW : public SpecificExpression { Address offset; Expression* ptr; Expression* value; - WasmType valueType; void finalize(); }; diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 94bcf0611d9..390dd90d83c 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1199,11 +1199,10 @@ Expression* SExpressionWasmBuilder::makeStore(Element& s, WasmType type, bool is } Expression* SExpressionWasmBuilder::makeAtomicRMW(Element& s, WasmType type) { - const char* extra = strchr(s[0]->c_str(), '.') + 10; // afer "type.atomic.rmw" + const char* extra = strchr(s[0]->c_str(), '.') + 11; // afer "type.atomic.rmw" auto ret = allocator.alloc(); - ret->valueType = type; + ret->type = type; ret->bytes = parseMemBytes(&extra, getWasmTypeSize(type)); - std::cout << "bytes "<<(int)ret->bytes; extra = strchr(extra, '.'); // after the optional '_u' and before the opcode if (!extra) throw ParseException("malformed atomic rmw instruction"); extra++; // after the '.' diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 1ef321712e2..d5bf8172c74 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -354,12 +354,8 @@ void Store::finalize() { } void AtomicRMW::finalize() { - assert(valueType != none); - if (ptr->type == unreachable || value->type == unreachable) { + if (ptr->type == unreachable || value->type == unreachable) type = unreachable; - } else { - type = valueType; - } } Const* Const::set(Literal value_) { From 1b58b17fb885ee900655427602c98f22c76acede Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Wed, 5 Jul 2017 16:14:17 -0700 Subject: [PATCH 04/10] add binary --- src/wasm-binary.h | 47 ++++++++++++++++++++- src/wasm/wasm-binary.cpp | 90 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 135 insertions(+), 2 deletions(-) diff --git a/src/wasm-binary.h b/src/wasm-binary.h index f1396cbb29f..9194a9f43c6 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -525,7 +525,50 @@ enum AtomicOpcodes { I32AtomicStore16 = 0x1a, I64AtomicStore8 = 0x1b, I64AtomicStore16 = 0x1c, - I64AtomicStore32 = 0x1d + I64AtomicStore32 = 0x1d, + + I32AtomicRMWAdd = 0x1e, + I64AtomicRMWAdd = 0x1f, + I32AtomicRMWAdd8U = 0x20, + I32AtomicRMWAdd16U = 0x21, + I64AtomicRMWAdd8U = 0x22, + I64AtomicRMWAdd16U = 0x23, + I64AtomicRMWAdd32U = 0x24, + I32AtomicRMWSub = 0x25, + I64AtomicRMWSub = 0x26, + I32AtomicRMWSub8U = 0x27, + I32AtomicRMWSub16U = 0x28, + I64AtomicRMWSub8U = 0x29, + I64AtomicRMWSub16U = 0x2a, + I64AtomicRMWSub32U = 0x2b, + I32AtomicRMWAnd = 0x2c, + I64AtomicRMWAnd = 0x2d, + I32AtomicRMWAnd8U = 0x2e, + I32AtomicRMWAnd16U = 0x2f, + I64AtomicRMWAnd8U = 0x30, + I64AtomicRMWAnd16U = 0x31, + I64AtomicRMWAnd32U = 0x32, + I32AtomicRMWOr = 0x33, + I64AtomicRMWOr = 0x34, + I32AtomicRMWOr8U = 0x35, + I32AtomicRMWOr16U = 0x36, + I64AtomicRMWOr8U = 0x37, + I64AtomicRMWOr16U = 0x38, + I64AtomicRMWOr32U = 0x39, + I32AtomicRMWXor = 0x3a, + I64AtomicRMWXor = 0x3b, + I32AtomicRMWXor8U = 0x3c, + I32AtomicRMWXor16U = 0x3d, + I64AtomicRMWXor8U = 0x3e, + I64AtomicRMWXor16U = 0x3f, + I64AtomicRMWXor32U = 0x40, + I32AtomicRMWXchg = 0x41, + I64AtomicRMWXchg = 0x42, + I32AtomicRMWXchg8U = 0x43, + I32AtomicRMWXchg16U = 0x44, + I64AtomicRMWXchg8U = 0x45, + I64AtomicRMWXchg16U = 0x46, + I64AtomicRMWXchg32U = 0x47 }; enum MemoryAccess { @@ -676,6 +719,7 @@ class WasmBinaryWriter : public Visitor { void emitMemoryAccess(size_t alignment, size_t bytes, uint32_t offset); void visitLoad(Load *curr); void visitStore(Store *curr); + void visitAtomicRMW(AtomicRMW *curr); void visitConst(Const *curr); void visitUnary(Unary *curr); void visitBinary(Binary *curr); @@ -833,6 +877,7 @@ class WasmBinaryBuilder { void readMemoryAccess(Address& alignment, size_t bytes, Address& offset); bool maybeVisitLoad(Expression*& out, uint8_t code, bool isAtomic); bool maybeVisitStore(Expression*& out, uint8_t code, bool isAtomic); + bool maybeVisitAtomicRMW(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/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 2665a4c45ea..2acda4febfa 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -832,6 +832,50 @@ void WasmBinaryWriter::visitStore(Store *curr) { emitMemoryAccess(curr->align, curr->bytes, curr->offset); } +void WasmBinaryWriter::visitAtomicRMW(AtomicRMW *curr) { + if (debug) std::cerr << "zz node: AtomicRMW" << std::endl; + recurse(curr->ptr); + recurse(curr->value); + + o << int8_t(BinaryConsts::AtomicPrefix); + +#define CASE_FOR_OP(Op) \ + case Op: \ + switch (curr->type) { \ + case i32: \ + switch (curr->bytes) { \ + case 1: o << int8_t(BinaryConsts::I32AtomicRMW##Op##8U); break; \ + case 2: o << int8_t(BinaryConsts::I32AtomicRMW##Op##16U); break; \ + case 4: o << int8_t(BinaryConsts::I32AtomicRMW##Op); break; \ + default: WASM_UNREACHABLE(); \ + } \ + break; \ + case i64: \ + switch (curr->bytes) { \ + case 1: o << int8_t(BinaryConsts::I64AtomicRMW##Op##8U); break; \ + case 2: o << int8_t(BinaryConsts::I64AtomicRMW##Op##16U); break; \ + case 4: o << int8_t(BinaryConsts::I64AtomicRMW##Op##32U); break; \ + case 8: o << int8_t(BinaryConsts::I64AtomicRMW##Op); break; \ + default: WASM_UNREACHABLE(); \ + } \ + break; \ + default: WASM_UNREACHABLE(); \ + } \ + break + + switch(curr->op) { + CASE_FOR_OP(Add); + CASE_FOR_OP(Sub); + CASE_FOR_OP(And); + CASE_FOR_OP(Or); + CASE_FOR_OP(Xor); + CASE_FOR_OP(Xchg); + default: WASM_UNREACHABLE(); + } + + emitMemoryAccess(curr->bytes, curr->bytes, curr->offset); +} + void WasmBinaryWriter::visitConst(Const *curr) { if (debug) std::cerr << "zz node: Const" << curr << " : " << curr->type << std::endl; switch (curr->type) { @@ -1934,7 +1978,7 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { code = getInt8(); if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) break; if (maybeVisitStore(curr, code, /*isAtomic=*/true)) break; - //if (maybeVisitRMW(curr, code)) break; + if (maybeVisitAtomicRMW(curr, code)) break; throw ParseException("invalid code after atomic prefix: " + std::to_string(code)); } default: { @@ -2283,6 +2327,50 @@ bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code, bool isA return true; } + +bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) { + if (code < BinaryConsts::I32AtomicRMWAdd || code > BinaryConsts::I64AtomicRMWXchg32U) return false; + auto* curr = allocator.alloc(); + + // Set curr to the given opcode, type and size. +#define SET(opcode, optype, size) \ + curr->op = opcode; \ + curr->type = optype; \ + curr->bytes = size + + // Handle the cases for all the valid types for a particular opcode +#define SET_FOR_OP(Op) \ + case BinaryConsts::I32AtomicRMW##Op: SET(Op, i32, 4); break; \ + case BinaryConsts::I32AtomicRMW##Op##8U: SET(Op, i32, 1); break; \ + case BinaryConsts::I32AtomicRMW##Op##16U: SET(Op, i32, 2); break; \ + case BinaryConsts::I64AtomicRMW##Op: SET(Op, i64, 8); break; \ + case BinaryConsts::I64AtomicRMW##Op##8U: SET(Op, i64, 1); break; \ + case BinaryConsts::I64AtomicRMW##Op##16U: SET(Op, i64, 2); break; \ + case BinaryConsts::I64AtomicRMW##Op##32U: SET(Op, i64, 4); break; + + switch(code) { + SET_FOR_OP(Add); + SET_FOR_OP(Sub); + SET_FOR_OP(And); + SET_FOR_OP(Or); + SET_FOR_OP(Xor); + SET_FOR_OP(Xchg); + default: WASM_UNREACHABLE(); + } +#undef SET_FOR_OP +#undef SET + + if (debug) std::cerr << "zz node: AtomicRMW" << std::endl; + Address readAlign; + readMemoryAccess(readAlign, curr->bytes, curr->offset); + if (readAlign != curr->bytes) throw ParseException("Align of AtomicRMW must match size"); + curr->value = 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; From 5141e4692dbb0aa2159b35b14564045d8f70d16d Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Wed, 5 Jul 2017 16:46:43 -0700 Subject: [PATCH 05/10] update tests --- test/atomics.wast | 18 +++++++++++ test/atomics.wast.fromBinary | 38 +++++++++++++++++++++++- test/atomics.wast.fromBinary.noDebugInfo | 36 ++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/test/atomics.wast b/test/atomics.wast index 9070a23c96e..83189ca3419 100644 --- a/test/atomics.wast +++ b/test/atomics.wast @@ -83,5 +83,23 @@ (get_local $0) ) ) + (drop + (i32.atomic.rmw16_u.and align=2 + (get_local $0) + (get_local $0) + ) + ) + (drop + (i64.atomic.rmw32_u.or + (get_local $0) + (get_local $1) + ) + ) + (drop + (i32.atomic.rmw8_u.xchg align=1 + (get_local $0) + (get_local $0) + ) + ) ) ) diff --git a/test/atomics.wast.fromBinary b/test/atomics.wast.fromBinary index 95c5473e25e..98866ac7ec8 100644 --- a/test/atomics.wast.fromBinary +++ b/test/atomics.wast.fromBinary @@ -1,7 +1,7 @@ (module (type $0 (func)) (memory $0 23 256 shared) - (func $atomics (type $0) + (func $atomic-loadstoare (type $0) (local $var$0 i32) (local $var$1 i64) (block $label$0 @@ -70,5 +70,41 @@ ) ) ) + (func $atomic-rmw (type $0) + (local $var$0 i32) + (local $var$1 i64) + (block $label$0 + (drop + (i32.atomic.rmw.add offset=4 + (get_local $var$0) + (get_local $var$0) + ) + ) + (drop + (i32.atomic.rmw8_u.add offset=4 + (get_local $var$0) + (get_local $var$0) + ) + ) + (drop + (i32.atomic.rmw16_u.and + (get_local $var$0) + (get_local $var$0) + ) + ) + (drop + (i64.atomic.rmw32_u.or + (get_local $var$0) + (get_local $var$1) + ) + ) + (drop + (i32.atomic.rmw8_u.xchg + (get_local $var$0) + (get_local $var$0) + ) + ) + ) + ) ) diff --git a/test/atomics.wast.fromBinary.noDebugInfo b/test/atomics.wast.fromBinary.noDebugInfo index 279ef79a6ec..3777417bfa5 100644 --- a/test/atomics.wast.fromBinary.noDebugInfo +++ b/test/atomics.wast.fromBinary.noDebugInfo @@ -70,5 +70,41 @@ ) ) ) + (func $1 (type $0) + (local $var$0 i32) + (local $var$1 i64) + (block $label$0 + (drop + (i32.atomic.rmw.add offset=4 + (get_local $var$0) + (get_local $var$0) + ) + ) + (drop + (i32.atomic.rmw8_u.add offset=4 + (get_local $var$0) + (get_local $var$0) + ) + ) + (drop + (i32.atomic.rmw16_u.and + (get_local $var$0) + (get_local $var$0) + ) + ) + (drop + (i64.atomic.rmw32_u.or + (get_local $var$0) + (get_local $var$1) + ) + ) + (drop + (i32.atomic.rmw8_u.xchg + (get_local $var$0) + (get_local $var$0) + ) + ) + ) + ) ) From 58b87276b3af0e36e28129814939811c88f93f32 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Wed, 5 Jul 2017 16:57:52 -0700 Subject: [PATCH 06/10] update test, add undef --- src/wasm/wasm-binary.cpp | 1 + test/atomics.wast.from-wast | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 2acda4febfa..8fa0f7b51de 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -872,6 +872,7 @@ void WasmBinaryWriter::visitAtomicRMW(AtomicRMW *curr) { CASE_FOR_OP(Xchg); default: WASM_UNREACHABLE(); } +#undef CASE_FOR_OP emitMemoryAccess(curr->bytes, curr->bytes, curr->offset); } diff --git a/test/atomics.wast.from-wast b/test/atomics.wast.from-wast index af78b98d57d..555a228af3f 100644 --- a/test/atomics.wast.from-wast +++ b/test/atomics.wast.from-wast @@ -1,7 +1,7 @@ (module (type $0 (func)) (memory $0 23 256 shared) - (func $atomics (type $0) + (func $atomic-loadstoare (type $0) (local $0 i32) (local $1 i64) (drop @@ -68,4 +68,38 @@ (get_local $1) ) ) + (func $atomic-rmw (type $0) + (local $0 i32) + (local $1 i64) + (drop + (i32.atomic.rmw.add offset=4 + (get_local $0) + (get_local $0) + ) + ) + (drop + (i32.atomic.rmw8_u.add offset=4 + (get_local $0) + (get_local $0) + ) + ) + (drop + (i32.atomic.rmw16_u.and + (get_local $0) + (get_local $0) + ) + ) + (drop + (i64.atomic.rmw32_u.or + (get_local $0) + (get_local $1) + ) + ) + (drop + (i32.atomic.rmw8_u.xchg + (get_local $0) + (get_local $0) + ) + ) + ) ) From 654fafc4c9bce2b26d6ce3981769f1f87221a07c Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Thu, 6 Jul 2017 09:28:09 -0700 Subject: [PATCH 07/10] reviews --- src/wasm/wasm.cpp | 3 ++- test/atomics.wast | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index d5bf8172c74..cbfcbccad09 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -354,8 +354,9 @@ void Store::finalize() { } void AtomicRMW::finalize() { - if (ptr->type == unreachable || value->type == unreachable) + if (ptr->type == unreachable || value->type == unreachable) { type = unreachable; + } } Const* Const::set(Literal value_) { diff --git a/test/atomics.wast b/test/atomics.wast index 83189ca3419..26aebdb0d0c 100644 --- a/test/atomics.wast +++ b/test/atomics.wast @@ -1,7 +1,7 @@ (module (type $0 (func)) (memory $0 23 256 shared) - (func $atomic-loadstoare (type $0) + (func $atomic-loadstore (type $0) (local $0 i32) (local $1 i64) (drop From 8520d8095d6474d919b96db4b2a5dea79e29ee6f Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Thu, 6 Jul 2017 09:33:22 -0700 Subject: [PATCH 08/10] update tests --- test/atomics.wast.from-wast | 2 +- test/atomics.wast.fromBinary | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/atomics.wast.from-wast b/test/atomics.wast.from-wast index 555a228af3f..ef15de7e2d0 100644 --- a/test/atomics.wast.from-wast +++ b/test/atomics.wast.from-wast @@ -1,7 +1,7 @@ (module (type $0 (func)) (memory $0 23 256 shared) - (func $atomic-loadstoare (type $0) + (func $atomic-loadstore (type $0) (local $0 i32) (local $1 i64) (drop diff --git a/test/atomics.wast.fromBinary b/test/atomics.wast.fromBinary index 98866ac7ec8..b3bce034aa1 100644 --- a/test/atomics.wast.fromBinary +++ b/test/atomics.wast.fromBinary @@ -1,7 +1,7 @@ (module (type $0 (func)) (memory $0 23 256 shared) - (func $atomic-loadstoare (type $0) + (func $atomic-loadstore (type $0) (local $var$0 i32) (local $var$1 i64) (block $label$0 From f15707dcb58c77ffd7f0395e06c9457026fef762 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Thu, 6 Jul 2017 10:06:43 -0700 Subject: [PATCH 09/10] jgravelle comments --- src/wasm-binary.h | 5 ++++- src/wasm/wasm-binary.cpp | 2 +- src/wasm/wasm-s-parser.cpp | 11 +++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 9194a9f43c6..fd7d31d06fa 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -527,6 +527,7 @@ enum AtomicOpcodes { I64AtomicStore16 = 0x1c, I64AtomicStore32 = 0x1d, + AtomicRMWOps_Begin = 0x1e, I32AtomicRMWAdd = 0x1e, I64AtomicRMWAdd = 0x1f, I32AtomicRMWAdd8U = 0x20, @@ -568,9 +569,11 @@ enum AtomicOpcodes { I32AtomicRMWXchg16U = 0x44, I64AtomicRMWXchg8U = 0x45, I64AtomicRMWXchg16U = 0x46, - I64AtomicRMWXchg32U = 0x47 + I64AtomicRMWXchg32U = 0x47, + AtomicRMWOps_End = 0x48, }; + enum MemoryAccess { Offset = 0x10, // bit 4 Alignment = 0x80, // bit 7 diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 8fa0f7b51de..c4addd61fa8 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2330,7 +2330,7 @@ bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code, bool isA bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) { - if (code < BinaryConsts::I32AtomicRMWAdd || code > BinaryConsts::I64AtomicRMWXchg32U) return false; + if (code < BinaryConsts::AtomicRMWOps_Begin || code >= BinaryConsts::AtomicRMWOps_End) return false; auto* curr = allocator.alloc(); // Set curr to the given opcode, type and size. diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 390dd90d83c..c6331afc02d 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1155,14 +1155,13 @@ static size_t parseMemAttributes(Element& s, Address* offset, Address* align, Ad const char *eq = strchr(str, '='); if (!eq) throw ParseException("missing = in memory attribute"); eq++; + uint64_t value = atoll(eq); if (str[0] == 'a') { - uint64_t a = atoll(eq); - if (a > std::numeric_limits::max()) throw ParseException("bad align"); - *align = a; + if (value > std::numeric_limits::max()) throw ParseException("bad align"); + *align = value; } else if (str[0] == 'o') { - uint64_t o = atoll(eq); - if (o > std::numeric_limits::max()) throw ParseException("bad offset"); - *offset = o; + if (value > std::numeric_limits::max()) throw ParseException("bad offset"); + *offset = value; } else throw ParseException("bad memory attribute"); i++; } From c004c55131a890848fbc9318f2b8a41bb050fe37 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Thu, 6 Jul 2017 22:34:40 -0700 Subject: [PATCH 10/10] use inclusive range --- src/wasm-binary.h | 2 +- src/wasm/wasm-binary.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wasm-binary.h b/src/wasm-binary.h index fd7d31d06fa..332b4a7b66a 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -570,7 +570,7 @@ enum AtomicOpcodes { I64AtomicRMWXchg8U = 0x45, I64AtomicRMWXchg16U = 0x46, I64AtomicRMWXchg32U = 0x47, - AtomicRMWOps_End = 0x48, + AtomicRMWOps_End = 0x47, }; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index c4addd61fa8..69d3ecdbb0f 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2330,7 +2330,7 @@ bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code, bool isA bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) { - if (code < BinaryConsts::AtomicRMWOps_Begin || code >= BinaryConsts::AtomicRMWOps_End) return false; + if (code < BinaryConsts::AtomicRMWOps_Begin || code > BinaryConsts::AtomicRMWOps_End) return false; auto* curr = allocator.alloc(); // Set curr to the given opcode, type and size.