diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/asm2wasm.h | 8 | ||||
-rw-r--r-- | src/ast/ExpressionManipulator.cpp | 14 | ||||
-rw-r--r-- | src/ast/bits.h | 39 | ||||
-rw-r--r-- | src/ast/global-utils.h | 2 | ||||
-rw-r--r-- | src/passes/DeadCodeElimination.cpp | 4 | ||||
-rw-r--r-- | src/passes/Precompute.cpp | 6 | ||||
-rw-r--r-- | src/passes/Print.cpp | 4 | ||||
-rw-r--r-- | src/passes/SafeHeap.cpp | 9 | ||||
-rw-r--r-- | src/tools/translate-to-fuzz.h | 186 | ||||
-rw-r--r-- | src/wasm-builder.h | 12 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 164 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 22 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 7 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 2 |
14 files changed, 361 insertions, 118 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 8f3c771be..9a4305f6a 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -32,6 +32,7 @@ #include "pass.h" #include "parsing.h" #include "ast_utils.h" +#include "ast/bits.h" #include "ast/branch-utils.h" #include "ast/literal-utils.h" #include "ast/trapping.h" @@ -1884,7 +1885,12 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { View& view = views[heap]; wasm.memory.shared = true; if (name == Atomics_load) { - return builder.makeAtomicLoad(view.bytes, view.signed_, 0, processUnshifted(ast[2][1], view.bytes), asmToWasmType(view.type)); + Expression* ret = builder.makeAtomicLoad(view.bytes, 0, processUnshifted(ast[2][1], view.bytes), asmToWasmType(view.type)); + if (view.signed_) { + // atomic loads are unsigned; add a signing + ret = Bits::makeSignExt(ret, view.bytes, wasm); + } + return ret; } else if (name == Atomics_store) { // asm.js stores return the value, wasm does not auto type = asmToWasmType(view.type); diff --git a/src/ast/ExpressionManipulator.cpp b/src/ast/ExpressionManipulator.cpp index f5b303488..6cb0219c5 100644 --- a/src/ast/ExpressionManipulator.cpp +++ b/src/ast/ExpressionManipulator.cpp @@ -97,28 +97,28 @@ Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom } Expression* visitLoad(Load *curr) { if (curr->isAtomic) { - return builder.makeAtomicLoad(curr->bytes, curr->signed_, curr->offset, - copy(curr->ptr), curr->type); + return builder.makeAtomicLoad(curr->bytes, curr->offset, + copy(curr->ptr), curr->type); } return builder.makeLoad(curr->bytes, curr->signed_, curr->offset, curr->align, copy(curr->ptr), curr->type); } Expression* visitStore(Store *curr) { if (curr->isAtomic) { - return builder.makeAtomicStore(curr->bytes, curr->offset, copy(curr->ptr), copy(curr->value), curr->valueType); + return builder.makeAtomicStore(curr->bytes, curr->offset, copy(curr->ptr), copy(curr->value), curr->valueType); } return builder.makeStore(curr->bytes, curr->offset, curr->align, copy(curr->ptr), copy(curr->value), curr->valueType); } Expression* visitAtomicRMW(AtomicRMW* curr) { return builder.makeAtomicRMW(curr->op, curr->bytes, curr->offset, - copy(curr->ptr), copy(curr->value), curr->type); + copy(curr->ptr), copy(curr->value), curr->type); } Expression* visitAtomicCmpxchg(AtomicCmpxchg* curr) { return builder.makeAtomicCmpxchg(curr->bytes, curr->offset, - copy(curr->ptr), copy(curr->expected), copy(curr->replacement), - curr->type); + copy(curr->ptr), copy(curr->expected), copy(curr->replacement), + curr->type); } Expression* visitAtomicWait(AtomicWait* curr) { - return builder.makeAtomicWait(copy(curr->ptr), copy(curr->expected), copy(curr->timeout), curr->type); + return builder.makeAtomicWait(copy(curr->ptr), copy(curr->expected), copy(curr->timeout), curr->expectedType); } Expression* visitAtomicWake(AtomicWake* curr) { return builder.makeAtomicWake(copy(curr->ptr), copy(curr->wakeCount)); diff --git a/src/ast/bits.h b/src/ast/bits.h index 0aee50ffe..7a86e70f4 100644 --- a/src/ast/bits.h +++ b/src/ast/bits.h @@ -18,6 +18,8 @@ #define wasm_ast_bits_h #include "support/bits.h" +#include "wasm-builder.h" +#include "ast/literal-utils.h" namespace wasm { @@ -60,6 +62,43 @@ struct Bits { } WASM_UNREACHABLE(); } + + static Expression* makeSignExt(Expression* value, Index bytes, Module& wasm) { + if (value->type == i32) { + if (bytes == 1 || bytes == 2) { + auto shifts = bytes == 1 ? 24 : 16; + Builder builder(wasm); + return builder.makeBinary( + ShrSInt32, + builder.makeBinary( + ShlInt32, + value, + LiteralUtils::makeFromInt32(shifts, i32, wasm) + ), + LiteralUtils::makeFromInt32(shifts, i32, wasm) + ); + } + assert(bytes == 4); + return value; // nothing to do + } else { + assert(value->type == i64); + if (bytes == 1 || bytes == 2 || bytes == 4) { + auto shifts = bytes == 1 ? 56 : (bytes == 2 ? 48 : 32); + Builder builder(wasm); + return builder.makeBinary( + ShrSInt64, + builder.makeBinary( + ShlInt64, + value, + LiteralUtils::makeFromInt32(shifts, i64, wasm) + ), + LiteralUtils::makeFromInt32(shifts, i64, wasm) + ); + } + assert(bytes == 8); + return value; // nothing to do + } + } }; } // namespace wasm diff --git a/src/ast/global-utils.h b/src/ast/global-utils.h index f5bebe8aa..96fec282b 100644 --- a/src/ast/global-utils.h +++ b/src/ast/global-utils.h @@ -27,7 +27,7 @@ namespace wasm { namespace GlobalUtils { // find a global initialized to the value of an import, or null if no such global - inline Global* getGlobalInitializedToImport(Module&wasm, Name module, Name base) { + inline Global* getGlobalInitializedToImport(Module& wasm, Name module, Name base) { // find the import Name imported; for (auto& import : wasm.imports) { diff --git a/src/passes/DeadCodeElimination.cpp b/src/passes/DeadCodeElimination.cpp index 48fc4a91c..5c0cfc291 100644 --- a/src/passes/DeadCodeElimination.cpp +++ b/src/passes/DeadCodeElimination.cpp @@ -254,6 +254,10 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> case Expression::Id::HostId: DELEGATE(Host); case Expression::Id::NopId: DELEGATE(Nop); case Expression::Id::UnreachableId: break; + case Expression::Id::AtomicCmpxchgId: DELEGATE(AtomicCmpxchg); + case Expression::Id::AtomicRMWId: DELEGATE(AtomicRMW); + case Expression::Id::AtomicWaitId: DELEGATE(AtomicWait); + case Expression::Id::AtomicWakeId: DELEGATE(AtomicWake); case Expression::Id::InvalidId: default: WASM_UNREACHABLE(); } diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp index ab114fa14..d2a7d0b9a 100644 --- a/src/passes/Precompute.cpp +++ b/src/passes/Precompute.cpp @@ -89,6 +89,12 @@ public: Flow visitAtomicCmpxchg(AtomicCmpxchg *curr) { return Flow(NONSTANDALONE_FLOW); } + Flow visitAtomicWait(AtomicWait *curr) { + return Flow(NONSTANDALONE_FLOW); + } + Flow visitAtomicWake(AtomicWake *curr) { + return Flow(NONSTANDALONE_FLOW); + } Flow visitHost(Host *curr) { return Flow(NONSTANDALONE_FLOW); } diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 8bb5175ab..6d40034fc 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -352,7 +352,9 @@ struct PrintSExpression : public Visitor<PrintSExpression> { } static void printRMWSize(std::ostream& o, WasmType type, uint8_t bytes) { prepareColor(o) << printWasmType(type) << ".atomic.rmw"; - if (bytes != getWasmTypeSize(type)) { + if (type == unreachable) { + o << '?'; + } else if (bytes != getWasmTypeSize(type)) { if (bytes == 1) { o << '8'; } else if (bytes == 2) { diff --git a/src/passes/SafeHeap.cpp b/src/passes/SafeHeap.cpp index 784bc761d..9b4779ee0 100644 --- a/src/passes/SafeHeap.cpp +++ b/src/passes/SafeHeap.cpp @@ -25,6 +25,7 @@ #include "asm_v_wasm.h" #include "asmjs/shared-constants.h" #include "wasm-builder.h" +#include "ast/bits.h" #include "ast/import-utils.h" namespace wasm { @@ -229,7 +230,13 @@ struct SafeHeap : public Pass { auto* load = module->allocator.alloc<Load>(); *load = style; // basically the same as the template we are given! load->ptr = builder.makeGetLocal(2, i32); - block->list.push_back(load); + Expression* last = load; + if (load->isAtomic && load->signed_) { + // atomic loads cannot be signed, manually sign it + last = Bits::makeSignExt(load, load->bytes, *module); + load->signed_ = false; + } + block->list.push_back(last); block->finalize(style.type); func->body = block; module->addFunction(func); diff --git a/src/tools/translate-to-fuzz.h b/src/tools/translate-to-fuzz.h index a316e6528..39b183d6e 100644 --- a/src/tools/translate-to-fuzz.h +++ b/src/tools/translate-to-fuzz.h @@ -101,6 +101,9 @@ private: // cross-VM comparisons harder) static const bool DE_NAN = true; + // Whether to emit atomics + static const bool ATOMICS = true; + // after we finish the input, we start going through it again, but xoring // so it's not identical int xorFactor = 0; @@ -377,94 +380,36 @@ private: nesting++; Expression* ret; switch (type) { - case i32: ret = _makei32(); break; - case i64: ret = _makei64(); break; - case f32: ret = _makef32(); break; - case f64: ret = _makef64(); break; + case i32: + case i64: + case f32: + case f64: ret = _makeConcrete(type); break; case none: ret = _makenone(); break; case unreachable: ret = _makeunreachable(); break; default: WASM_UNREACHABLE(); } + assert(ret->type == type); // we should create the right type of thing nesting--; return ret; } - Expression* _makei32() { - switch (upTo(14)) { - case 0: return makeBlock(i32); - case 1: return makeIf(i32); - case 2: return makeLoop(i32); - case 3: return makeBreak(i32); - case 4: return makeCall(i32); - case 5: return makeCallIndirect(i32); - case 6: return makeGetLocal(i32); - case 7: return makeSetLocal(i32); - case 8: return makeLoad(i32); - case 9: return makeConst(i32); - case 10: return makeUnary(i32); - case 11: return makeBinary(i32); - case 12: return makeSelect(i32); - case 13: return makeGetGlobal(i32); - } - WASM_UNREACHABLE(); - } - - Expression* _makei64() { - switch (upTo(14)) { - case 0: return makeBlock(i64); - case 1: return makeIf(i64); - case 2: return makeLoop(i64); - case 3: return makeBreak(i64); - case 4: return makeCall(i64); - case 5: return makeCallIndirect(i64); - case 6: return makeGetLocal(i64); - case 7: return makeSetLocal(i64); - case 8: return makeLoad(i64); - case 9: return makeConst(i64); - case 10: return makeUnary(i64); - case 11: return makeBinary(i64); - case 12: return makeSelect(i64); - case 13: return makeGetGlobal(i64); - } - WASM_UNREACHABLE(); - } - - Expression* _makef32() { - switch (upTo(14)) { - case 0: return makeBlock(f32); - case 1: return makeIf(f32); - case 2: return makeLoop(f32); - case 3: return makeBreak(f32); - case 4: return makeCall(f32); - case 5: return makeCallIndirect(f32); - case 6: return makeGetLocal(f32); - case 7: return makeSetLocal(f32); - case 8: return makeLoad(f32); - case 9: return makeConst(f32); - case 10: return makeUnary(f32); - case 11: return makeBinary(f32); - case 12: return makeSelect(f32); - case 13: return makeGetGlobal(f32); - } - WASM_UNREACHABLE(); - } - - Expression* _makef64() { - switch (upTo(14)) { - case 0: return makeBlock(f64); - case 1: return makeIf(f64); - case 2: return makeLoop(f64); - case 3: return makeBreak(f64); - case 4: return makeCall(f64); - case 5: return makeCallIndirect(f64); - case 6: return makeGetLocal(f64); - case 7: return makeSetLocal(f64); - case 8: return makeLoad(f64); - case 9: return makeConst(f64); - case 10: return makeUnary(f64); - case 11: return makeBinary(f64); - case 12: return makeSelect(f64); - case 13: return makeGetGlobal(f64); + Expression* _makeConcrete(WasmType type) { + switch (upTo(15)) { + case 0: return makeBlock(type); + case 1: return makeIf(type); + case 2: return makeLoop(type); + case 3: return makeBreak(type); + case 4: return makeCall(type); + case 5: return makeCallIndirect(type); + case 6: return makeGetLocal(type); + case 7: return makeSetLocal(type); + case 8: return makeLoad(type); + case 9: return makeConst(type); + case 10: return makeUnary(type); + case 11: return makeBinary(type); + case 12: return makeSelect(type); + case 13: return makeGetGlobal(type); + case 14: return makeAtomic(type); } WASM_UNREACHABLE(); } @@ -821,7 +766,7 @@ private: return ret; } - Expression* makeLoad(WasmType type) { + Load* makeNonAtomicLoad(WasmType type) { auto offset = logify(get()); auto ptr = makePointer(); switch (type) { @@ -854,10 +799,22 @@ private: } } - Store* makeStore(WasmType type) { + Expression* makeLoad(WasmType type) { + auto* ret = makeNonAtomicLoad(type); + if (type != i32 && type != i64) return ret; + if (!ATOMICS || oneIn(2)) return ret; + // make it atomic + wasm.memory.shared = true; + ret->isAtomic = true; + ret->signed_ = false; + ret->align = ret->bytes; + return ret; + } + + Store* makeNonAtomicStore(WasmType type) { if (type == unreachable) { // make a normal store, then make it unreachable - auto* ret = makeStore(getConcreteType()); + auto* ret = makeNonAtomicStore(getConcreteType()); switch (upTo(3)) { case 0: ret->ptr = make(unreachable); break; case 1: ret->value = make(unreachable); break; @@ -902,6 +859,17 @@ private: } } + Store* makeStore(WasmType type) { + auto* ret = makeNonAtomicStore(type); + if (ret->value->type != i32 && ret->value->type != i64) return ret; + if (!ATOMICS || oneIn(2)) return ret; + // make it atomic + wasm.memory.shared = true; + ret->isAtomic = true; + ret->align = ret->bytes; + return ret; + } + Expression* makeConst(WasmType type) { Literal value; switch (upTo(4)) { @@ -1144,6 +1112,58 @@ private: return builder.makeUnreachable(); } + Expression* makeAtomic(WasmType type) { + if (!ATOMICS || (type != i32 && type != i64)) return makeTrivial(type); + wasm.memory.shared = true; + if (type == i32 && oneIn(2)) { + if (oneIn(2)) { + auto* ptr = makePointer(); + auto expectedType = pick(i32, i64); + auto* expected = make(expectedType); + auto* timeout = make(i64); + return builder.makeAtomicWait(ptr, expected, timeout, expectedType); + } else { + auto* ptr = makePointer(); + auto* count = make(i32); + return builder.makeAtomicWake(ptr, count); + } + } + Index bytes; + switch (type) { + case i32: { + switch (upTo(3)) { + case 0: bytes = 1; break; + case 1: bytes = pick(1, 2); break; + case 2: bytes = pick(1, 2, 4); break; + default: WASM_UNREACHABLE(); + } + break; + } + case i64: { + switch (upTo(4)) { + case 0: bytes = 1; break; + case 1: bytes = pick(1, 2); break; + case 2: bytes = pick(1, 2, 4); break; + case 3: bytes = pick(1, 2, 4, 8); break; + default: WASM_UNREACHABLE(); + } + break; + } + default: WASM_UNREACHABLE(); + } + auto offset = logify(get()); + auto* ptr = makePointer(); + if (oneIn(2)) { + auto* value = make(type); + return builder.makeAtomicRMW(pick(AtomicRMWOp::Add, AtomicRMWOp::Sub, AtomicRMWOp::And, AtomicRMWOp::Or, AtomicRMWOp::Xor, AtomicRMWOp::Xchg), + bytes, offset, ptr, value, type); + } else { + auto* expected = make(type); + auto* replacement = make(type); + return builder.makeAtomicCmpxchg(bytes, offset, ptr, expected, replacement, type); + } + } + // special getters WasmType getType() { diff --git a/src/wasm-builder.h b/src/wasm-builder.h index bd756297e..e5ca79845 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -202,18 +202,17 @@ public: ret->type = type; return ret; } - Load* makeAtomicLoad(unsigned bytes, bool signed_, uint32_t offset, Expression* ptr, WasmType type) { - Load* load = makeLoad(bytes, signed_, offset, bytes, ptr, type); + Load* makeAtomicLoad(unsigned bytes, uint32_t offset, Expression* ptr, WasmType type) { + Load* load = makeLoad(bytes, false, offset, bytes, ptr, type); load->isAtomic = true; return load; } - AtomicWait* makeAtomicWait(Expression* ptr, Expression* expected, Expression* timeout, WasmType type) { + AtomicWait* makeAtomicWait(Expression* ptr, Expression* expected, Expression* timeout, WasmType expectedType) { auto* wait = allocator.alloc<AtomicWait>(); wait->ptr = ptr; wait->expected = expected; wait->timeout = timeout; - wait->expectedType = type; - wait->type = i32; + wait->expectedType = expectedType; wait->finalize(); return wait; } @@ -221,7 +220,6 @@ public: auto* wake = allocator.alloc<AtomicWake>(); wake->ptr = ptr; wake->wakeCount = wakeCount; - wake->type = i32; wake->finalize(); return wake; } @@ -234,7 +232,7 @@ public: return ret; } Store* makeAtomicStore(unsigned bytes, uint32_t offset, Expression* ptr, Expression* value, WasmType type) { - Store* store = makeStore(bytes, offset, getWasmTypeSize(type), ptr, value, type); + Store* store = makeStore(bytes, offset, bytes, ptr, value, type); store->isAtomic = true; return store; } diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 0b01fb1d3..b3fa3f265 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -32,6 +32,7 @@ #include "wasm.h" #include "wasm-traversal.h" + #ifdef WASM_INTERPRETER_DEBUG #include "wasm-printing.h" #endif @@ -118,7 +119,16 @@ template<typename SubType> class ExpressionRunner : public Visitor<SubType, Flow> { public: Flow visit(Expression *curr) { - return Visitor<SubType, Flow>::visit(curr); + auto ret = Visitor<SubType, Flow>::visit(curr); + if (!ret.breaking() && (isConcreteWasmType(curr->type) || isConcreteWasmType(ret.value.type))) { +#if 1 // def WASM_INTERPRETER_DEBUG + if (ret.value.type != curr->type) { + std::cerr << "expected " << printWasmType(curr->type) << ", seeing " << printWasmType(ret.value.type) << " from\n" << curr << '\n'; + } +#endif + assert(ret.value.type == curr->type); + } + return ret; } Flow visitBlock(Block *curr) { @@ -461,7 +471,7 @@ public: Flow visitUnreachable(Unreachable *curr) { NOTE_ENTER("Unreachable"); trap("unreachable"); - return Flow(); + WASM_UNREACHABLE(); } Literal truncSFloat(Unary* curr, Literal value) { @@ -522,7 +532,9 @@ public: Flow visitCallIndirect(CallIndirect* curr) { WASM_UNREACHABLE(); } Flow visitGetLocal(GetLocal *curr) { WASM_UNREACHABLE(); } Flow visitSetLocal(SetLocal *curr) { WASM_UNREACHABLE(); } - Flow visitGetGlobal(GetGlobal *curr) { return Flow(globals[curr->name]); } + Flow visitGetGlobal(GetGlobal *curr) { + return Flow(globals[curr->name]); + } Flow visitSetGlobal(SetGlobal *curr) { WASM_UNREACHABLE(); } Flow visitLoad(Load *curr) { WASM_UNREACHABLE(); } Flow visitStore(Store *curr) { WASM_UNREACHABLE(); } @@ -563,7 +575,7 @@ public: switch (load->bytes) { case 1: return load->signed_ ? Literal((int32_t)load8s(addr)) : Literal((int32_t)load8u(addr)); case 2: return load->signed_ ? Literal((int32_t)load16s(addr)) : Literal((int32_t)load16u(addr)); - case 4: return load->signed_ ? Literal((int32_t)load32s(addr)) : Literal((int32_t)load32u(addr)); + case 4: return Literal((int32_t)load32s(addr)); default: WASM_UNREACHABLE(); } break; @@ -573,7 +585,7 @@ public: case 1: return load->signed_ ? Literal((int64_t)load8s(addr)) : Literal((int64_t)load8u(addr)); case 2: return load->signed_ ? Literal((int64_t)load16s(addr)) : Literal((int64_t)load16u(addr)); case 4: return load->signed_ ? Literal((int64_t)load32s(addr)) : Literal((int64_t)load32u(addr)); - case 8: return load->signed_ ? Literal((int64_t)load64s(addr)) : Literal((int64_t)load64u(addr)); + case 8: return Literal((int64_t)load64s(addr)); default: WASM_UNREACHABLE(); } break; @@ -810,8 +822,8 @@ public: NOTE_ENTER("GetGlobal"); auto name = curr->name; NOTE_EVAL1(name); - NOTE_EVAL1(instance.globals[name]); assert(instance.globals.find(name) != instance.globals.end()); + NOTE_EVAL1(instance.globals[name]); return instance.globals[name]; } Flow visitSetGlobal(SetGlobal *curr) { @@ -849,6 +861,85 @@ public: return Flow(); } + Flow visitAtomicRMW(AtomicRMW *curr) { + NOTE_ENTER("AtomicRMW"); + Flow ptr = this->visit(curr->ptr); + if (ptr.breaking()) return ptr; + auto value = this->visit(curr->value); + if (value.breaking()) return value; + NOTE_EVAL1(ptr); + auto addr = instance.getFinalAddress(curr, ptr.value); + NOTE_EVAL1(addr); + NOTE_EVAL1(value); + auto loaded = instance.doAtomicLoad(addr, curr->bytes, curr->type); + NOTE_EVAL1(loaded); + auto computed = value.value; + switch (curr->op) { + case Add: computed = computed.add(value.value); break; + case Sub: computed = computed.sub(value.value); break; + case And: computed = computed.and_(value.value); break; + case Or: computed = computed.or_(value.value); break; + case Xor: computed = computed.xor_(value.value); break; + case Xchg: computed = value.value; break; + default: WASM_UNREACHABLE(); + } + instance.doAtomicStore(addr, curr->bytes, computed); + return loaded; + } + Flow visitAtomicCmpxchg(AtomicCmpxchg *curr) { + NOTE_ENTER("AtomicCmpxchg"); + Flow ptr = this->visit(curr->ptr); + if (ptr.breaking()) return ptr; + NOTE_EVAL1(ptr); + auto expected = this->visit(curr->expected); + if (expected.breaking()) return expected; + auto replacement = this->visit(curr->replacement); + if (replacement.breaking()) return replacement; + auto addr = instance.getFinalAddress(curr, ptr.value); + NOTE_EVAL1(addr); + NOTE_EVAL1(expected); + NOTE_EVAL1(replacement); + auto loaded = instance.doAtomicLoad(addr, curr->bytes, curr->type); + NOTE_EVAL1(loaded); + if (loaded == expected.value) { + instance.doAtomicStore(addr, curr->bytes, replacement.value); + } + return loaded; + } + Flow visitAtomicWait(AtomicWait *curr) { + NOTE_ENTER("AtomicWait"); + Flow ptr = this->visit(curr->ptr); + if (ptr.breaking()) return ptr; + NOTE_EVAL1(ptr); + auto expected = this->visit(curr->expected); + NOTE_EVAL1(expected); + if (expected.breaking()) return expected; + auto timeout = this->visit(curr->timeout); + NOTE_EVAL1(timeout); + if (timeout.breaking()) return timeout; + auto bytes = getWasmTypeSize(curr->expectedType); + auto addr = instance.getFinalAddress(ptr.value, bytes); + auto loaded = instance.doAtomicLoad(addr, bytes, curr->expectedType); + NOTE_EVAL1(loaded); + if (loaded != expected.value) { + return Literal(int32_t(1)); // not equal + } + // TODO: add threads support! + // for now, just assume we are woken up + return Literal(int32_t(0)); // woken up + } + Flow visitAtomicWake(AtomicWake *curr) { + NOTE_ENTER("AtomicWake"); + Flow ptr = this->visit(curr->ptr); + if (ptr.breaking()) return ptr; + NOTE_EVAL1(ptr); + auto count = this->visit(curr->wakeCount); + NOTE_EVAL1(count); + if (count.breaking()) return count; + // TODO: add threads support! + return Literal(int32_t(0)); // none woken up + } + Flow visitHost(Host *curr) { NOTE_ENTER("Host"); switch (curr->op) { @@ -922,25 +1013,70 @@ protected: Address memorySize; // in pages + void trapIfGt(uint64_t lhs, uint64_t rhs, const char* msg) { + if (lhs > rhs) { + std::stringstream ss; + ss << msg << ": " << lhs << " > " << rhs; + externalInterface->trap(ss.str().c_str()); + } + } + template <class LS> Address getFinalAddress(LS* curr, Literal ptr) { - auto trapIfGt = [this](uint64_t lhs, uint64_t rhs, const char* msg) { - if (lhs > rhs) { - std::stringstream ss; - ss << msg << ": " << lhs << " > " << rhs; - externalInterface->trap(ss.str().c_str()); - } - }; Address memorySizeBytes = memorySize * Memory::kPageSize; uint64_t addr = ptr.type == i32 ? ptr.geti32() : ptr.geti64(); trapIfGt(curr->offset, memorySizeBytes, "offset > memory"); trapIfGt(addr, memorySizeBytes - curr->offset, "final > memory"); addr += curr->offset; trapIfGt(curr->bytes, memorySizeBytes, "bytes > memory"); - trapIfGt(addr, memorySizeBytes - curr->bytes, "highest > memory"); + checkLoadAddress(addr, curr->bytes); return addr; } + Address getFinalAddress(Literal ptr, Index bytes) { + Address memorySizeBytes = memorySize * Memory::kPageSize; + uint64_t addr = ptr.type == i32 ? ptr.geti32() : ptr.geti64(); + trapIfGt(addr, memorySizeBytes - bytes, "highest > memory"); + return addr; + } + + void checkLoadAddress(Address addr, Index bytes) { + Address memorySizeBytes = memorySize * Memory::kPageSize; + trapIfGt(addr, memorySizeBytes - bytes, "highest > memory"); + } + + Literal doAtomicLoad(Address addr, Index bytes, WasmType type) { + checkLoadAddress(addr, bytes); + Const ptr; + ptr.value = Literal(int32_t(addr)); + ptr.type = i32; + Load load; + load.bytes = bytes; + load.signed_ = true; + load.align = bytes; + load.isAtomic = true; // understatement + load.ptr = &ptr; + load.type = type; + return externalInterface->load(&load, addr); + } + + void doAtomicStore(Address addr, Index bytes, Literal toStore) { + Const ptr; + ptr.value = Literal(int32_t(addr)); + ptr.type = i32; + Const value; + value.value = toStore; + value.type = toStore.type; + Store store; + store.bytes = bytes; + store.align = bytes; + store.isAtomic = true; // understatement + store.ptr = &ptr; + store.value = &value; + store.valueType = value.type; + return externalInterface->store(&store, addr, toStore); + } + ExternalInterface* externalInterface; }; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index ceb230f73..f98eff670 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -791,6 +791,11 @@ void WasmBinaryWriter::visitLoad(Load *curr) { default: WASM_UNREACHABLE(); } } else { + if (curr->type == unreachable) { + // don't even emit it; we don't know the right type + o << int8_t(BinaryConsts::Unreachable); + return; + } o << int8_t(BinaryConsts::AtomicPrefix); switch (curr->type) { case i32: { @@ -849,6 +854,11 @@ void WasmBinaryWriter::visitStore(Store *curr) { default: abort(); } } else { + if (curr->type == unreachable) { + // don't even emit it; we don't know the right type + o << int8_t(BinaryConsts::Unreachable); + return; + } o << int8_t(BinaryConsts::AtomicPrefix); switch (curr->valueType) { case i32: { @@ -881,6 +891,12 @@ void WasmBinaryWriter::visitAtomicRMW(AtomicRMW *curr) { recurse(curr->ptr); recurse(curr->value); + if (curr->type == unreachable) { + // don't even emit it; we don't know the right type + o << int8_t(BinaryConsts::Unreachable); + return; + } + o << int8_t(BinaryConsts::AtomicPrefix); #define CASE_FOR_OP(Op) \ @@ -927,6 +943,12 @@ void WasmBinaryWriter::visitAtomicCmpxchg(AtomicCmpxchg *curr) { recurse(curr->expected); recurse(curr->replacement); + if (curr->type == unreachable) { + // don't even emit it; we don't know the right type + o << int8_t(BinaryConsts::Unreachable); + return; + } + o << int8_t(BinaryConsts::AtomicPrefix); switch (curr->type) { case i32: diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 6d75296a4..5120eea7a 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -487,6 +487,7 @@ void FunctionValidator::visitLoad(Load *curr) { validateMemBytes(curr->bytes, curr->type, curr); validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr); shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "load pointer type must be i32"); + if (curr->isAtomic) shouldBeFalse(curr->signed_, curr, "atomic loads must be unsigned"); } void FunctionValidator::visitStore(Store *curr) { @@ -502,7 +503,7 @@ void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) { shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory"); validateMemBytes(curr->bytes, curr->type, curr); shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicRMW pointer type must be i32"); - shouldBeEqualOrFirstIsUnreachable(curr->value->type, curr->type, curr, "AtomicRMW result type must match operand"); + shouldBeEqualOrFirstIsUnreachable(curr->type, curr->value->type, curr, "AtomicRMW result type must match operand"); shouldBeIntOrUnreachable(curr->type, curr, "Atomic operations are only valid on int types"); } @@ -513,8 +514,8 @@ void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) { if (curr->expected->type != unreachable && curr->replacement->type != unreachable) { shouldBeEqual(curr->expected->type, curr->replacement->type, curr, "cmpxchg operand types must match"); } - shouldBeEqualOrFirstIsUnreachable(curr->expected->type, curr->type, curr, "Cmpxchg result type must match expected"); - shouldBeEqualOrFirstIsUnreachable(curr->replacement->type, curr->type, curr, "Cmpxchg result type must match replacement"); + shouldBeEqualOrFirstIsUnreachable(curr->type, curr->expected->type, curr, "Cmpxchg result type must match expected"); + shouldBeEqualOrFirstIsUnreachable(curr->type, curr->replacement->type, curr, "Cmpxchg result type must match replacement"); shouldBeIntOrUnreachable(curr->expected->type, curr, "Atomic operations are only valid on int types"); } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index f4562fe64..6441991c9 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -390,12 +390,14 @@ void AtomicCmpxchg::finalize() { } void AtomicWait::finalize() { + type = i32; if (ptr->type == unreachable || expected->type == unreachable || timeout->type == unreachable) { type = unreachable; } } void AtomicWake::finalize() { + type = i32; if (ptr->type == unreachable || wakeCount->type == unreachable) { type = unreachable; } |