From b153e60e06a2a69d10e11a0e91e69fd3ed0e904f Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Tue, 27 Jun 2017 17:37:16 -0700 Subject: [PATCH 1/8] atomic loads --- src/compiler-support.h | 2 +- src/passes/Print.cpp | 4 +++- src/wasm-s-parser.h | 2 +- src/wasm.h | 10 ++++++++++ src/wasm/wasm-s-parser.cpp | 12 +++++++++--- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/compiler-support.h b/src/compiler-support.h index 3dd87338335..f2e58ff5e50 100644 --- a/src/compiler-support.h +++ b/src/compiler-support.h @@ -27,7 +27,7 @@ // If control flow reaches the point of the WASM_UNREACHABLE(), the program is // undefined. -#if __has_builtin(__builtin_unreachable) +#if __has_builtin(__builtin_unreachable) && defined(NDEBUG) # define WASM_UNREACHABLE() __builtin_unreachable() #elif defined(_MSC_VER) # define WASM_UNREACHABLE() __assume(false) diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 0494e7d78f3..c5359a7bae7 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -296,7 +296,9 @@ struct PrintSExpression : public Visitor { } void visitLoad(Load *curr) { o << '('; - prepareColor(o) << printWasmType(curr->type) << ".load"; + prepareColor(o) << printWasmType(curr->type); + if (curr->isAtomic) o << ".atomic"; + o << ".load"; if (curr->bytes < 4 || (curr->type == i64 && curr->bytes < 8)) { if (curr->bytes == 1) { o << '8'; diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index c79e0a458fe..ff634039582 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -175,7 +175,7 @@ class SExpressionWasmBuilder { Expression* makeBlock(Element& s); Expression* makeThenOrElse(Element& s); Expression* makeConst(Element& s, WasmType type); - Expression* makeLoad(Element& s, WasmType type); + Expression* makeLoad(Element& s, WasmType type, bool isAtomic); Expression* makeStore(Element& s, WasmType type); Expression* makeIf(Element& s); Expression* makeMaybeBlock(Element& s, size_t i, WasmType type); diff --git a/src/wasm.h b/src/wasm.h index 56432faf980..286604848cd 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -177,6 +177,7 @@ class Expression { HostId, NopId, UnreachableId, + AtomicRMWId, NumExpressionIds }; Id _id; @@ -398,6 +399,7 @@ class Load : public SpecificExpression { bool signed_; Address offset; Address align; + bool isAtomic; Expression* ptr; // type must be set during creation, cannot be inferred @@ -413,6 +415,7 @@ class Store : public SpecificExpression { uint8_t bytes; Address offset; Address align; + bool isAtomic; Expression* ptr; Expression* value; WasmType valueType; // the store never returns a value @@ -511,6 +514,13 @@ 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-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 6bf030302b5..3bb15796b97 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -663,6 +663,9 @@ Expression* SExpressionWasmBuilder::makeExpression(Element& s) { if (op[1] == 'b') return makeUnary(s, type == f32 ? UnaryOp::AbsFloat32 : UnaryOp::AbsFloat64, type); if (op[1] == 'd') return makeBinary(s, BINARY_INT_OR_FLOAT(Add), type); if (op[1] == 'n') return makeBinary(s, BINARY_INT(And), type); + if (op[1] == 't' && !strncmp(op, "atomic.", strlen("atomic."))) { + if (op[7] == 'l') return makeLoad(s, type, /*isAtomic=*/true); + } abort_on(op); } case 'c': { @@ -721,7 +724,7 @@ Expression* SExpressionWasmBuilder::makeExpression(Element& s) { if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(LeU) : BINARY_INT(LeS), type); if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Le), type); } - if (op[1] == 'o') return makeLoad(s, type); + if (op[1] == 'o') return makeLoad(s, type, /*isAtomic=*/false); abort_on(op); } case 'm': { @@ -1122,9 +1125,12 @@ Expression* SExpressionWasmBuilder::makeConst(Element& s, WasmType type) { } -Expression* SExpressionWasmBuilder::makeLoad(Element& s, WasmType type) { +Expression* SExpressionWasmBuilder::makeLoad(Element& s, WasmType type, bool isAtomic) { const char *extra = strchr(s[0]->c_str(), '.') + 5; // after "type.load" - auto ret = allocator.alloc(); + 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') { From cd238baa66660936ca6af4dc185586ab148987c8 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Tue, 27 Jun 2017 17:49:51 -0700 Subject: [PATCH 2/8] parse store --- src/passes/Print.cpp | 4 +++- src/wasm-s-parser.h | 2 +- src/wasm/wasm-s-parser.cpp | 8 +++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index c5359a7bae7..7ec3e98a3ec 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -324,7 +324,9 @@ struct PrintSExpression : public Visitor { } void visitStore(Store *curr) { o << '('; - prepareColor(o) << printWasmType(curr->valueType) << ".store"; + prepareColor(o) << printWasmType(curr->valueType); + if (curr->isAtomic) o << ".atomic"; + o << ".store"; if (curr->bytes < 4 || (curr->valueType == i64 && curr->bytes < 8)) { if (curr->bytes == 1) { o << '8'; diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index ff634039582..90e797615b1 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -176,7 +176,7 @@ class SExpressionWasmBuilder { Expression* makeThenOrElse(Element& s); Expression* makeConst(Element& s, WasmType type); Expression* makeLoad(Element& s, WasmType type, bool isAtomic); - Expression* makeStore(Element& s, WasmType type); + Expression* makeStore(Element& s, WasmType type, bool isAtomic); Expression* makeIf(Element& s); Expression* makeMaybeBlock(Element& s, size_t i, WasmType type); Expression* makeLoop(Element& s); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 3bb15796b97..38954af1f0f 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -665,6 +665,7 @@ Expression* SExpressionWasmBuilder::makeExpression(Element& s) { if (op[1] == 'n') return makeBinary(s, BINARY_INT(And), type); 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); } abort_on(op); } @@ -767,7 +768,7 @@ Expression* SExpressionWasmBuilder::makeExpression(Element& s) { } if (op[1] == 'u') return makeBinary(s, BINARY_INT_OR_FLOAT(Sub), type); if (op[1] == 'q') return makeUnary(s, type == f32 ? UnaryOp::SqrtFloat32 : UnaryOp::SqrtFloat64, type); - if (op[1] == 't') return makeStore(s, type); + if (op[1] == 't') return makeStore(s, type, /*isAtomic=*/false); abort_on(op); } case 't': { @@ -1129,7 +1130,6 @@ Expression* SExpressionWasmBuilder::makeLoad(Element& s, WasmType type, bool isA 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); @@ -1170,9 +1170,11 @@ Expression* SExpressionWasmBuilder::makeLoad(Element& s, WasmType type, bool isA return ret; } -Expression* SExpressionWasmBuilder::makeStore(Element& s, WasmType type) { +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') { From bc0a6b91ba4f4a460f278a91a717ba71f66e4bb1 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Tue, 27 Jun 2017 23:24:59 -0700 Subject: [PATCH 3/8] Binary format support --- src/binaryen-c.cpp | 4 +- src/s2wasm.h | 2 + src/wasm-binary.h | 25 ++++- src/wasm-builder.h | 2 + src/wasm/wasm-binary.cpp | 233 +++++++++++++++++++++++++++------------ test/atomics.wast | 71 ++++++++++++ 6 files changed, 261 insertions(+), 76 deletions(-) create mode 100644 test/atomics.wast diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 36b2abbe75a..83d0e155f31 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -549,7 +549,7 @@ BinaryenExpressionRef BinaryenLoad(BinaryenModuleRef module, uint32_t bytes, int auto id = noteExpression(ret); std::cout << " expressions[" << id << "] = BinaryenLoad(the_module, " << bytes << ", " << int(signed_) << ", " << offset << ", " << align << ", " << type << ", expressions[" << expressions[ptr] << "]);\n"; } - + ret->isAtomic = false; ret->bytes = bytes; ret->signed_ = !!signed_; ret->offset = offset; @@ -566,7 +566,7 @@ BinaryenExpressionRef BinaryenStore(BinaryenModuleRef module, uint32_t bytes, ui auto id = noteExpression(ret); std::cout << " expressions[" << id << "] = BinaryenStore(the_module, " << bytes << ", " << offset << ", " << align << ", expressions[" << expressions[ptr] << "], expressions[" << expressions[value] << "], " << type << ");\n"; } - + ret->isAtomic = false; ret->bytes = bytes; ret->offset = offset; ret->align = align ? align : bytes; diff --git a/src/s2wasm.h b/src/s2wasm.h index efa4ad6013b..0fc0201cd7c 100644 --- a/src/s2wasm.h +++ b/src/s2wasm.h @@ -920,6 +920,7 @@ class S2WasmBuilder { auto makeLoad = [&](WasmType type) { skipComma(); auto curr = allocator->alloc(); + curr->isAtomic = false; curr->type = type; int32_t bytes = getInt() / CHAR_BIT; curr->bytes = bytes > 0 ? bytes : getWasmTypeSize(type); @@ -939,6 +940,7 @@ class S2WasmBuilder { }; auto makeStore = [&](WasmType type) { auto curr = allocator->alloc(); + curr->isAtomic = false; curr->valueType = type; s += strlen("store"); if(!isspace(*s)) { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 9e75d6501a6..f1396cbb29f 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -506,7 +506,26 @@ enum ASTNodes { I32ReinterpretF32 = 0xbc, I64ReinterpretF64 = 0xbd, F32ReinterpretI32 = 0xbe, - F64ReinterpretI64 = 0xbf + F64ReinterpretI64 = 0xbf, + + AtomicPrefix = 0xfe +}; + +enum AtomicOpcodes { + I32AtomicLoad = 0x10, + I64AtomicLoad = 0x11, + I32AtomicLoad8U = 0x12, + I32AtomicLoad16U = 0x13, + I64AtomicLoad8U = 0x14, + I64AtomicLoad16U = 0x15, + I64AtomicLoad32U = 0x16, + I32AtomicStore = 0x17, + I64AtomicStore = 0x18, + I32AtomicStore8 = 0x19, + I32AtomicStore16 = 0x1a, + I64AtomicStore8 = 0x1b, + I64AtomicStore16 = 0x1c, + I64AtomicStore32 = 0x1d }; enum MemoryAccess { @@ -812,8 +831,8 @@ class WasmBinaryBuilder { void visitGetGlobal(GetGlobal *curr); void visitSetGlobal(SetGlobal *curr); void readMemoryAccess(Address& alignment, size_t bytes, Address& offset); - bool maybeVisitLoad(Expression*& out, uint8_t code); - bool maybeVisitStore(Expression*& out, uint8_t code); + bool maybeVisitLoad(Expression*& out, uint8_t code, bool isAtomic); + bool maybeVisitStore(Expression*& out, uint8_t code, bool isAtomic); 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 42d3dfe4791..41aaf676628 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -188,12 +188,14 @@ class Builder { } Load* makeLoad(unsigned bytes, bool signed_, uint32_t offset, unsigned align, Expression *ptr, WasmType type) { auto* ret = allocator.alloc(); + ret->isAtomic = false; ret->bytes = bytes; ret->signed_ = signed_; ret->offset = offset; ret->align = align; ret->ptr = ptr; ret->type = type; return ret; } Store* makeStore(unsigned bytes, uint32_t offset, unsigned align, Expression *ptr, Expression *value, WasmType type) { auto* ret = allocator.alloc(); + ret->isAtomic = false; ret->bytes = bytes; ret->offset = offset; ret->align = align; ret->ptr = ptr; ret->value = value; ret->valueType = type; ret->finalize(); assert(isConcreteWasmType(ret->value->type) ? ret->value->type == type : true); diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 359d9d8f8b9..ad28f4d9012 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -720,30 +720,57 @@ void WasmBinaryWriter::emitMemoryAccess(size_t alignment, size_t bytes, uint32_t void WasmBinaryWriter::visitLoad(Load *curr) { if (debug) std::cerr << "zz node: Load" << std::endl; recurse(curr->ptr); - switch (curr->type) { - case i32: { - switch (curr->bytes) { - case 1: o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem8S : BinaryConsts::I32LoadMem8U); break; - case 2: o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem16S : BinaryConsts::I32LoadMem16U); break; - case 4: o << int8_t(BinaryConsts::I32LoadMem); break; - default: abort(); + if (!curr->isAtomic) { + switch (curr->type) { + case i32: { + switch (curr->bytes) { + case 1: o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem8S : BinaryConsts::I32LoadMem8U); break; + case 2: o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem16S : BinaryConsts::I32LoadMem16U); break; + case 4: o << int8_t(BinaryConsts::I32LoadMem); break; + default: abort(); + } + break; } - break; + case i64: { + switch (curr->bytes) { + case 1: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem8S : BinaryConsts::I64LoadMem8U); break; + case 2: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem16S : BinaryConsts::I64LoadMem16U); break; + case 4: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem32S : BinaryConsts::I64LoadMem32U); break; + case 8: o << int8_t(BinaryConsts::I64LoadMem); break; + default: abort(); + } + break; + } + case f32: o << int8_t(BinaryConsts::F32LoadMem); break; + case f64: o << int8_t(BinaryConsts::F64LoadMem); break; + case unreachable: return; // the pointer is unreachable, so we are never reached; just don't emit a load + default: WASM_UNREACHABLE(); } - case i64: { - switch (curr->bytes) { - case 1: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem8S : BinaryConsts::I64LoadMem8U); break; - case 2: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem16S : BinaryConsts::I64LoadMem16U); break; - case 4: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem32S : BinaryConsts::I64LoadMem32U); break; - case 8: o << int8_t(BinaryConsts::I64LoadMem); break; - default: abort(); + } else { + o << int8_t(BinaryConsts::AtomicPrefix); + switch (curr->type) { + case i32: { + switch (curr->bytes) { + case 1: o << int8_t(BinaryConsts::I32AtomicLoad8U); break; + case 2: o << int8_t(BinaryConsts::I32AtomicLoad16U); break; + case 4: o << int8_t(BinaryConsts::I32AtomicLoad); break; + default: WASM_UNREACHABLE(); + } + break; } - break; + case i64: { + switch (curr->bytes) { + case 1: o << int8_t(BinaryConsts::I64AtomicLoad8U); break; + case 2: o << int8_t(BinaryConsts::I64AtomicLoad16U); break; + case 4: o << int8_t(BinaryConsts::I64AtomicLoad32U); break; + case 8: o << int8_t(BinaryConsts::I64AtomicLoad); break; + default: WASM_UNREACHABLE(); + } + break; + } + case unreachable: return; + default: WASM_UNREACHABLE(); } - case f32: o << int8_t(BinaryConsts::F32LoadMem); break; - case f64: o << int8_t(BinaryConsts::F64LoadMem); break; - case unreachable: return; // the pointer is unreachable, so we are never reached; just don't emit a load - default: WASM_UNREACHABLE(); } emitMemoryAccess(curr->align, curr->bytes, curr->offset); } @@ -752,29 +779,55 @@ void WasmBinaryWriter::visitStore(Store *curr) { if (debug) std::cerr << "zz node: Store" << std::endl; recurse(curr->ptr); recurse(curr->value); - switch (curr->valueType) { - case i32: { - switch (curr->bytes) { - case 1: o << int8_t(BinaryConsts::I32StoreMem8); break; - case 2: o << int8_t(BinaryConsts::I32StoreMem16); break; - case 4: o << int8_t(BinaryConsts::I32StoreMem); break; - default: abort(); + if (!curr->isAtomic) { + switch (curr->valueType) { + case i32: { + switch (curr->bytes) { + case 1: o << int8_t(BinaryConsts::I32StoreMem8); break; + case 2: o << int8_t(BinaryConsts::I32StoreMem16); break; + case 4: o << int8_t(BinaryConsts::I32StoreMem); break; + default: abort(); + } + break; } - break; + case i64: { + switch (curr->bytes) { + case 1: o << int8_t(BinaryConsts::I64StoreMem8); break; + case 2: o << int8_t(BinaryConsts::I64StoreMem16); break; + case 4: o << int8_t(BinaryConsts::I64StoreMem32); break; + case 8: o << int8_t(BinaryConsts::I64StoreMem); break; + default: abort(); + } + break; + } + case f32: o << int8_t(BinaryConsts::F32StoreMem); break; + case f64: o << int8_t(BinaryConsts::F64StoreMem); break; + default: abort(); } - case i64: { - switch (curr->bytes) { - case 1: o << int8_t(BinaryConsts::I64StoreMem8); break; - case 2: o << int8_t(BinaryConsts::I64StoreMem16); break; - case 4: o << int8_t(BinaryConsts::I64StoreMem32); break; - case 8: o << int8_t(BinaryConsts::I64StoreMem); break; - default: abort(); + } else { + o << int8_t(BinaryConsts::AtomicPrefix); + switch (curr->valueType) { + case i32: { + switch (curr->bytes) { + case 1: o << int8_t(BinaryConsts::I32AtomicStore8); break; + case 2: o << int8_t(BinaryConsts::I32AtomicStore16); break; + case 4: o << int8_t(BinaryConsts::I32AtomicStore); break; + default: WASM_UNREACHABLE(); + } + break; } - break; + case i64: { + switch (curr->bytes) { + case 1: o << int8_t(BinaryConsts::I64AtomicStore8); break; + case 2: o << int8_t(BinaryConsts::I64AtomicStore16); break; + case 4: o << int8_t(BinaryConsts::I64AtomicStore32); break; + case 8: o << int8_t(BinaryConsts::I64AtomicStore); break; + default: WASM_UNREACHABLE(); + } + break; + } + default: WASM_UNREACHABLE(); } - case f32: o << int8_t(BinaryConsts::F32StoreMem); break; - case f64: o << int8_t(BinaryConsts::F64StoreMem); break; - default: abort(); } emitMemoryAccess(curr->align, curr->bytes, curr->offset); } @@ -1873,13 +1926,19 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { case BinaryConsts::Drop: visitDrop((curr = allocator.alloc())->cast()); break; case BinaryConsts::End: case BinaryConsts::Else: curr = nullptr; break; + case BinaryConsts::AtomicPrefix: { + code = getInt8(); + if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) break; + if (maybeVisitStore(curr, code, /*isAtomic=*/true)) break; + throw ParseException("invalid code after atomic prefix: " + std::to_string(code)); + } default: { // otherwise, the code is a subcode TODO: optimize if (maybeVisitBinary(curr, code)) break; if (maybeVisitUnary(curr, code)) break; if (maybeVisitConst(curr, code)) break; - if (maybeVisitLoad(curr, code)) break; - if (maybeVisitStore(curr, code)) break; + if (maybeVisitLoad(curr, code, /*isAtomc=*/false)) break; + if (maybeVisitStore(curr, code, /*isAtomic=*/false)) break; if (maybeVisitHost(curr, code)) break; throw ParseException("bad node code " + std::to_string(code)); } @@ -2137,26 +2196,43 @@ void WasmBinaryBuilder::readMemoryAccess(Address& alignment, size_t bytes, Addre offset = getU32LEB(); } -bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, uint8_t code) { +bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, uint8_t code, bool isAtomic) { Load* curr; - switch (code) { - case BinaryConsts::I32LoadMem8S: curr = allocator.alloc(); curr->bytes = 1; curr->type = i32; curr->signed_ = true; break; - case BinaryConsts::I32LoadMem8U: curr = allocator.alloc(); curr->bytes = 1; curr->type = i32; curr->signed_ = false; break; - case BinaryConsts::I32LoadMem16S: curr = allocator.alloc(); curr->bytes = 2; curr->type = i32; curr->signed_ = true; break; - case BinaryConsts::I32LoadMem16U: curr = allocator.alloc(); curr->bytes = 2; curr->type = i32; curr->signed_ = false; break; - case BinaryConsts::I32LoadMem: curr = allocator.alloc(); curr->bytes = 4; curr->type = i32; break; - case BinaryConsts::I64LoadMem8S: curr = allocator.alloc(); curr->bytes = 1; curr->type = i64; curr->signed_ = true; break; - case BinaryConsts::I64LoadMem8U: curr = allocator.alloc(); curr->bytes = 1; curr->type = i64; curr->signed_ = false; break; - case BinaryConsts::I64LoadMem16S: curr = allocator.alloc(); curr->bytes = 2; curr->type = i64; curr->signed_ = true; break; - case BinaryConsts::I64LoadMem16U: curr = allocator.alloc(); curr->bytes = 2; curr->type = i64; curr->signed_ = false; break; - case BinaryConsts::I64LoadMem32S: curr = allocator.alloc(); curr->bytes = 4; curr->type = i64; curr->signed_ = true; break; - case BinaryConsts::I64LoadMem32U: curr = allocator.alloc(); curr->bytes = 4; curr->type = i64; curr->signed_ = false; break; - case BinaryConsts::I64LoadMem: curr = allocator.alloc(); curr->bytes = 8; curr->type = i64; break; - case BinaryConsts::F32LoadMem: curr = allocator.alloc(); curr->bytes = 4; curr->type = f32; break; - case BinaryConsts::F64LoadMem: curr = allocator.alloc(); curr->bytes = 8; curr->type = f64; break; - default: return false; + if (!isAtomic) { + switch (code) { + case BinaryConsts::I32LoadMem8S: curr = allocator.alloc(); curr->bytes = 1; curr->type = i32; curr->signed_ = true; break; + case BinaryConsts::I32LoadMem8U: curr = allocator.alloc(); curr->bytes = 1; curr->type = i32; curr->signed_ = false; break; + case BinaryConsts::I32LoadMem16S: curr = allocator.alloc(); curr->bytes = 2; curr->type = i32; curr->signed_ = true; break; + case BinaryConsts::I32LoadMem16U: curr = allocator.alloc(); curr->bytes = 2; curr->type = i32; curr->signed_ = false; break; + case BinaryConsts::I32LoadMem: curr = allocator.alloc(); curr->bytes = 4; curr->type = i32; break; + case BinaryConsts::I64LoadMem8S: curr = allocator.alloc(); curr->bytes = 1; curr->type = i64; curr->signed_ = true; break; + case BinaryConsts::I64LoadMem8U: curr = allocator.alloc(); curr->bytes = 1; curr->type = i64; curr->signed_ = false; break; + case BinaryConsts::I64LoadMem16S: curr = allocator.alloc(); curr->bytes = 2; curr->type = i64; curr->signed_ = true; break; + case BinaryConsts::I64LoadMem16U: curr = allocator.alloc(); curr->bytes = 2; curr->type = i64; curr->signed_ = false; break; + case BinaryConsts::I64LoadMem32S: curr = allocator.alloc(); curr->bytes = 4; curr->type = i64; curr->signed_ = true; break; + case BinaryConsts::I64LoadMem32U: curr = allocator.alloc(); curr->bytes = 4; curr->type = i64; curr->signed_ = false; break; + case BinaryConsts::I64LoadMem: curr = allocator.alloc(); curr->bytes = 8; curr->type = i64; break; + case BinaryConsts::F32LoadMem: curr = allocator.alloc(); curr->bytes = 4; curr->type = f32; break; + case BinaryConsts::F64LoadMem: curr = allocator.alloc(); curr->bytes = 8; curr->type = f64; break; + default: return false; + } + if (debug) std::cerr << "zz node: Load" << std::endl; + } else { + switch (code) { + case BinaryConsts::I32AtomicLoad8U: curr = allocator.alloc(); curr->bytes = 1; curr->type = i32; break; + case BinaryConsts::I32AtomicLoad16U: curr = allocator.alloc(); curr->bytes = 2; curr->type = i32; break; + case BinaryConsts::I32AtomicLoad: curr = allocator.alloc(); curr->bytes = 4; curr->type = i32; break; + case BinaryConsts::I64AtomicLoad8U: curr = allocator.alloc(); curr->bytes = 1; curr->type = i64; break; + case BinaryConsts::I64AtomicLoad16U: curr = allocator.alloc(); curr->bytes = 2; curr->type = i64; break; + case BinaryConsts::I64AtomicLoad32U: curr = allocator.alloc(); curr->bytes = 4; curr->type = i64; break; + case BinaryConsts::I64AtomicLoad: curr = allocator.alloc(); curr->bytes = 8; curr->type = i64; break; + default: return false; + } + curr->signed_ = false; + if (debug) std::cerr << "zz node: AtomicLoad" << std::endl; } - if (debug) std::cerr << "zz node: Load" << std::endl; + + curr->isAtomic = isAtomic; readMemoryAccess(curr->align, curr->bytes, curr->offset); curr->ptr = popNonVoidExpression(); curr->finalize(); @@ -2164,20 +2240,35 @@ bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, uint8_t code) { return true; } -bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code) { +bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code, bool isAtomic) { Store* curr; - switch (code) { - case BinaryConsts::I32StoreMem8: curr = allocator.alloc(); curr->bytes = 1; curr->valueType = i32; break; - case BinaryConsts::I32StoreMem16: curr = allocator.alloc(); curr->bytes = 2; curr->valueType = i32; break; - case BinaryConsts::I32StoreMem: curr = allocator.alloc(); curr->bytes = 4; curr->valueType = i32; break; - case BinaryConsts::I64StoreMem8: curr = allocator.alloc(); curr->bytes = 1; curr->valueType = i64; break; - case BinaryConsts::I64StoreMem16: curr = allocator.alloc(); curr->bytes = 2; curr->valueType = i64; break; - case BinaryConsts::I64StoreMem32: curr = allocator.alloc(); curr->bytes = 4; curr->valueType = i64; break; - case BinaryConsts::I64StoreMem: curr = allocator.alloc(); curr->bytes = 8; curr->valueType = i64; break; - case BinaryConsts::F32StoreMem: curr = allocator.alloc(); curr->bytes = 4; curr->valueType = f32; break; - case BinaryConsts::F64StoreMem: curr = allocator.alloc(); curr->bytes = 8; curr->valueType = f64; break; - default: return false; + if (!isAtomic) { + switch (code) { + case BinaryConsts::I32StoreMem8: curr = allocator.alloc(); curr->bytes = 1; curr->valueType = i32; break; + case BinaryConsts::I32StoreMem16: curr = allocator.alloc(); curr->bytes = 2; curr->valueType = i32; break; + case BinaryConsts::I32StoreMem: curr = allocator.alloc(); curr->bytes = 4; curr->valueType = i32; break; + case BinaryConsts::I64StoreMem8: curr = allocator.alloc(); curr->bytes = 1; curr->valueType = i64; break; + case BinaryConsts::I64StoreMem16: curr = allocator.alloc(); curr->bytes = 2; curr->valueType = i64; break; + case BinaryConsts::I64StoreMem32: curr = allocator.alloc(); curr->bytes = 4; curr->valueType = i64; break; + case BinaryConsts::I64StoreMem: curr = allocator.alloc(); curr->bytes = 8; curr->valueType = i64; break; + case BinaryConsts::F32StoreMem: curr = allocator.alloc(); curr->bytes = 4; curr->valueType = f32; break; + case BinaryConsts::F64StoreMem: curr = allocator.alloc(); curr->bytes = 8; curr->valueType = f64; break; + default: return false; + } + } else { + switch (code) { + case BinaryConsts::I32AtomicStore8: curr = allocator.alloc(); curr->bytes = 1; curr->valueType = i32; break; + case BinaryConsts::I32AtomicStore16: curr = allocator.alloc(); curr->bytes = 2; curr->valueType = i32; break; + case BinaryConsts::I32AtomicStore: curr = allocator.alloc(); curr->bytes = 4; curr->valueType = i32; break; + case BinaryConsts::I64AtomicStore8: curr = allocator.alloc(); curr->bytes = 1; curr->valueType = i64; break; + case BinaryConsts::I64AtomicStore16: curr = allocator.alloc(); curr->bytes = 2; curr->valueType = i64; break; + case BinaryConsts::I64AtomicStore32: curr = allocator.alloc(); curr->bytes = 4; curr->valueType = i64; break; + case BinaryConsts::I64AtomicStore: curr = allocator.alloc(); curr->bytes = 8; curr->valueType = i64; break; + default: return false; + } } + + curr->isAtomic = isAtomic; if (debug) std::cerr << "zz node: Store" << std::endl; readMemoryAccess(curr->align, curr->bytes, curr->offset); curr->value = popNonVoidExpression(); diff --git a/test/atomics.wast b/test/atomics.wast new file mode 100644 index 00000000000..af78b98d57d --- /dev/null +++ b/test/atomics.wast @@ -0,0 +1,71 @@ +(module + (type $0 (func)) + (memory $0 23 256 shared) + (func $atomics (type $0) + (local $0 i32) + (local $1 i64) + (drop + (i32.atomic.load8_u offset=4 + (get_local $0) + ) + ) + (drop + (i32.atomic.load16_u offset=4 + (get_local $0) + ) + ) + (drop + (i32.atomic.load offset=4 + (get_local $0) + ) + ) + (drop + (i64.atomic.load8_u + (get_local $0) + ) + ) + (drop + (i64.atomic.load16_u + (get_local $0) + ) + ) + (drop + (i64.atomic.load32_u + (get_local $0) + ) + ) + (drop + (i64.atomic.load + (get_local $0) + ) + ) + (i32.atomic.store offset=4 + (get_local $0) + (get_local $0) + ) + (i32.atomic.store8 offset=4 + (get_local $0) + (get_local $0) + ) + (i32.atomic.store16 offset=4 + (get_local $0) + (get_local $0) + ) + (i64.atomic.store offset=4 + (get_local $0) + (get_local $1) + ) + (i64.atomic.store8 offset=4 + (get_local $0) + (get_local $1) + ) + (i64.atomic.store16 offset=4 + (get_local $0) + (get_local $1) + ) + (i64.atomic.store32 offset=4 + (get_local $0) + (get_local $1) + ) + ) +) From f4baa7cfab28da47fc1b2d7d6a6e6f43111c1e80 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Wed, 28 Jun 2017 10:31:37 -0700 Subject: [PATCH 4/8] Fix asm2wasm load/store generation --- src/asm2wasm.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 90e258c549e..4395054f60b 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -1772,6 +1772,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { assert(views.find(heap) != views.end()); View& view = views[heap]; auto ret = allocator.alloc(); + ret->isAtomic = false; ret->bytes = view.bytes; ret->offset = 0; ret->align = view.bytes; @@ -1843,6 +1844,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { assert(views.find(heap) != views.end()); View& view = views[heap]; auto ret = allocator.alloc(); + ret->isAtomic = false; ret->bytes = view.bytes; ret->signed_ = view.signed_; ret->offset = 0; From 4dcd1d0df715ff12ff85760276dc1f482fd45e41 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Wed, 28 Jun 2017 10:32:28 -0700 Subject: [PATCH 5/8] Add atomic test files --- test/atomics.wast.from-wast | 71 +++++++++++++++++++++++ test/atomics.wast.fromBinary | 74 ++++++++++++++++++++++++ test/atomics.wast.fromBinary.noDebugInfo | 74 ++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 test/atomics.wast.from-wast create mode 100644 test/atomics.wast.fromBinary create mode 100644 test/atomics.wast.fromBinary.noDebugInfo diff --git a/test/atomics.wast.from-wast b/test/atomics.wast.from-wast new file mode 100644 index 00000000000..af78b98d57d --- /dev/null +++ b/test/atomics.wast.from-wast @@ -0,0 +1,71 @@ +(module + (type $0 (func)) + (memory $0 23 256 shared) + (func $atomics (type $0) + (local $0 i32) + (local $1 i64) + (drop + (i32.atomic.load8_u offset=4 + (get_local $0) + ) + ) + (drop + (i32.atomic.load16_u offset=4 + (get_local $0) + ) + ) + (drop + (i32.atomic.load offset=4 + (get_local $0) + ) + ) + (drop + (i64.atomic.load8_u + (get_local $0) + ) + ) + (drop + (i64.atomic.load16_u + (get_local $0) + ) + ) + (drop + (i64.atomic.load32_u + (get_local $0) + ) + ) + (drop + (i64.atomic.load + (get_local $0) + ) + ) + (i32.atomic.store offset=4 + (get_local $0) + (get_local $0) + ) + (i32.atomic.store8 offset=4 + (get_local $0) + (get_local $0) + ) + (i32.atomic.store16 offset=4 + (get_local $0) + (get_local $0) + ) + (i64.atomic.store offset=4 + (get_local $0) + (get_local $1) + ) + (i64.atomic.store8 offset=4 + (get_local $0) + (get_local $1) + ) + (i64.atomic.store16 offset=4 + (get_local $0) + (get_local $1) + ) + (i64.atomic.store32 offset=4 + (get_local $0) + (get_local $1) + ) + ) +) diff --git a/test/atomics.wast.fromBinary b/test/atomics.wast.fromBinary new file mode 100644 index 00000000000..95c5473e25e --- /dev/null +++ b/test/atomics.wast.fromBinary @@ -0,0 +1,74 @@ +(module + (type $0 (func)) + (memory $0 23 256 shared) + (func $atomics (type $0) + (local $var$0 i32) + (local $var$1 i64) + (block $label$0 + (drop + (i32.atomic.load8_u offset=4 + (get_local $var$0) + ) + ) + (drop + (i32.atomic.load16_u offset=4 + (get_local $var$0) + ) + ) + (drop + (i32.atomic.load offset=4 + (get_local $var$0) + ) + ) + (drop + (i64.atomic.load8_u + (get_local $var$0) + ) + ) + (drop + (i64.atomic.load16_u + (get_local $var$0) + ) + ) + (drop + (i64.atomic.load32_u + (get_local $var$0) + ) + ) + (drop + (i64.atomic.load + (get_local $var$0) + ) + ) + (i32.atomic.store offset=4 + (get_local $var$0) + (get_local $var$0) + ) + (i32.atomic.store8 offset=4 + (get_local $var$0) + (get_local $var$0) + ) + (i32.atomic.store16 offset=4 + (get_local $var$0) + (get_local $var$0) + ) + (i64.atomic.store offset=4 + (get_local $var$0) + (get_local $var$1) + ) + (i64.atomic.store8 offset=4 + (get_local $var$0) + (get_local $var$1) + ) + (i64.atomic.store16 offset=4 + (get_local $var$0) + (get_local $var$1) + ) + (i64.atomic.store32 offset=4 + (get_local $var$0) + (get_local $var$1) + ) + ) + ) +) + diff --git a/test/atomics.wast.fromBinary.noDebugInfo b/test/atomics.wast.fromBinary.noDebugInfo new file mode 100644 index 00000000000..279ef79a6ec --- /dev/null +++ b/test/atomics.wast.fromBinary.noDebugInfo @@ -0,0 +1,74 @@ +(module + (type $0 (func)) + (memory $0 23 256 shared) + (func $0 (type $0) + (local $var$0 i32) + (local $var$1 i64) + (block $label$0 + (drop + (i32.atomic.load8_u offset=4 + (get_local $var$0) + ) + ) + (drop + (i32.atomic.load16_u offset=4 + (get_local $var$0) + ) + ) + (drop + (i32.atomic.load offset=4 + (get_local $var$0) + ) + ) + (drop + (i64.atomic.load8_u + (get_local $var$0) + ) + ) + (drop + (i64.atomic.load16_u + (get_local $var$0) + ) + ) + (drop + (i64.atomic.load32_u + (get_local $var$0) + ) + ) + (drop + (i64.atomic.load + (get_local $var$0) + ) + ) + (i32.atomic.store offset=4 + (get_local $var$0) + (get_local $var$0) + ) + (i32.atomic.store8 offset=4 + (get_local $var$0) + (get_local $var$0) + ) + (i32.atomic.store16 offset=4 + (get_local $var$0) + (get_local $var$0) + ) + (i64.atomic.store offset=4 + (get_local $var$0) + (get_local $var$1) + ) + (i64.atomic.store8 offset=4 + (get_local $var$0) + (get_local $var$1) + ) + (i64.atomic.store16 offset=4 + (get_local $var$0) + (get_local $var$1) + ) + (i64.atomic.store32 offset=4 + (get_local $var$0) + (get_local $var$1) + ) + ) + ) +) + From 8bb2b844f2be9b68b311a9af669fa70c7e857ac8 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Wed, 28 Jun 2017 11:13:47 -0700 Subject: [PATCH 6/8] use builder in asm2wasm --- src/asm2wasm.h | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 4395054f60b..602147f2c9c 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -1771,15 +1771,10 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { IString heap = target[1]->getIString(); assert(views.find(heap) != views.end()); View& view = views[heap]; - auto ret = allocator.alloc(); - ret->isAtomic = false; - ret->bytes = view.bytes; - ret->offset = 0; - ret->align = view.bytes; - ret->ptr = processUnshifted(target[2], view.bytes); - ret->value = process(assign->value()); - ret->valueType = asmToWasmType(view.type); - ret->finalize(); + auto* ret = builder.makeStore(view.bytes, 0, view.bytes, + processUnshifted(target[2], view.bytes), + process(assign->value()), + asmToWasmType(view.type)); if (ret->valueType != ret->value->type) { // in asm.js we have some implicit coercions that we must do explicitly here if (ret->valueType == f32 && ret->value->type == f64) { @@ -1843,15 +1838,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { IString heap = target->getIString(); assert(views.find(heap) != views.end()); View& view = views[heap]; - auto ret = allocator.alloc(); - ret->isAtomic = false; - ret->bytes = view.bytes; - ret->signed_ = view.signed_; - ret->offset = 0; - ret->align = view.bytes; - ret->ptr = processUnshifted(ast[2], view.bytes); - ret->type = getWasmType(view.bytes, !view.integer); - return ret; + return builder.makeLoad(view.bytes, view.signed_, 0, view.bytes, + processUnshifted(ast[2], view.bytes), + getWasmType(view.bytes, !view.integer)); } else if (what == UNARY_PREFIX) { if (ast[1] == PLUS) { Literal literal = checkLiteral(ast); From 397efae8e596bdfcf1120af4d970a4f31b3526de Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Wed, 28 Jun 2017 11:35:57 -0700 Subject: [PATCH 7/8] Revert "use builder in asm2wasm" This reverts commit 8bb2b844f2be9b68b311a9af669fa70c7e857ac8. The assertion makeStore is failing and I'm not going to debug that right now. --- src/asm2wasm.h | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 602147f2c9c..4395054f60b 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -1771,10 +1771,15 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { IString heap = target[1]->getIString(); assert(views.find(heap) != views.end()); View& view = views[heap]; - auto* ret = builder.makeStore(view.bytes, 0, view.bytes, - processUnshifted(target[2], view.bytes), - process(assign->value()), - asmToWasmType(view.type)); + auto ret = allocator.alloc(); + ret->isAtomic = false; + ret->bytes = view.bytes; + ret->offset = 0; + ret->align = view.bytes; + ret->ptr = processUnshifted(target[2], view.bytes); + ret->value = process(assign->value()); + ret->valueType = asmToWasmType(view.type); + ret->finalize(); if (ret->valueType != ret->value->type) { // in asm.js we have some implicit coercions that we must do explicitly here if (ret->valueType == f32 && ret->value->type == f64) { @@ -1838,9 +1843,15 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { IString heap = target->getIString(); assert(views.find(heap) != views.end()); View& view = views[heap]; - return builder.makeLoad(view.bytes, view.signed_, 0, view.bytes, - processUnshifted(ast[2], view.bytes), - getWasmType(view.bytes, !view.integer)); + auto ret = allocator.alloc(); + ret->isAtomic = false; + ret->bytes = view.bytes; + ret->signed_ = view.signed_; + ret->offset = 0; + ret->align = view.bytes; + ret->ptr = processUnshifted(ast[2], view.bytes); + ret->type = getWasmType(view.bytes, !view.integer); + return ret; } else if (what == UNARY_PREFIX) { if (ast[1] == PLUS) { Literal literal = checkLiteral(ast); From ebd856891f8b0b544a7968ef499bc6daf72664ad Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Wed, 28 Jun 2017 12:04:46 -0700 Subject: [PATCH 8/8] fix typo --- src/wasm/wasm-binary.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index ad28f4d9012..e44f9fb83e6 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1937,7 +1937,7 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { if (maybeVisitBinary(curr, code)) break; if (maybeVisitUnary(curr, code)) break; if (maybeVisitConst(curr, code)) break; - if (maybeVisitLoad(curr, code, /*isAtomc=*/false)) break; + if (maybeVisitLoad(curr, code, /*isAtomic=*/false)) break; if (maybeVisitStore(curr, code, /*isAtomic=*/false)) break; if (maybeVisitHost(curr, code)) break; throw ParseException("bad node code " + std::to_string(code));