diff options
-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 | ||||
-rw-r--r-- | test/passes/inlining-optimizing.txt | 12 | ||||
-rw-r--r-- | test/passes/inlining-optimizing.wast | 17 | ||||
-rw-r--r-- | test/passes/safe-heap.txt | 50 | ||||
-rw-r--r-- | test/passes/translate-to-fuzz.txt | 1533 |
18 files changed, 1152 insertions, 939 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; } diff --git a/test/passes/inlining-optimizing.txt b/test/passes/inlining-optimizing.txt index 19c580063..8079d818a 100644 --- a/test/passes/inlining-optimizing.txt +++ b/test/passes/inlining-optimizing.txt @@ -41,3 +41,15 @@ (unreachable) ) ) +(module + (type $0 (func (result i32))) + (type $1 (func (result i64))) + (memory $0 (shared 1 1)) + (func $1 (type $1) (result i64) + (i32.atomic.store16 + (i32.const 0) + (i32.const 0) + ) + (i64.const 0) + ) +) diff --git a/test/passes/inlining-optimizing.wast b/test/passes/inlining-optimizing.wast index 4f4f348a6..aac4706fe 100644 --- a/test/passes/inlining-optimizing.wast +++ b/test/passes/inlining-optimizing.wast @@ -85,4 +85,19 @@ (unreachable) ;; void function but having unreachable body, when inlined, type must be fixed ) ) - +(module + (memory $0 (shared 1 1)) + (func $0 (result i32) + (i32.atomic.store16 + (i32.const 0) + (i32.const 0) + ) + (i32.const 1) + ) + (func $1 (result i64) + (drop + (call $0) + ) + (i64.const 0) + ) +) diff --git a/test/passes/safe-heap.txt b/test/passes/safe-heap.txt index f34dd7455..144766f1f 100644 --- a/test/passes/safe-heap.txt +++ b/test/passes/safe-heap.txt @@ -189,8 +189,14 @@ ) (call $segfault) ) - (i32.atomic.load8_s - (get_local $2) + (i32.shr_s + (i32.shl + (i32.atomic.load8_u + (get_local $2) + ) + (i32.const 24) + ) + (i32.const 24) ) ) (func $SAFE_HEAP_LOAD_i32_1_1 (param $0 i32) (param $1 i32) (result i32) @@ -346,8 +352,14 @@ ) (call $alignfault) ) - (i32.atomic.load16_s - (get_local $2) + (i32.shr_s + (i32.shl + (i32.atomic.load16_u + (get_local $2) + ) + (i32.const 16) + ) + (i32.const 16) ) ) (func $SAFE_HEAP_LOAD_i32_2_2 (param $0 i32) (param $1 i32) (result i32) @@ -799,8 +811,14 @@ ) (call $segfault) ) - (i64.atomic.load8_s - (get_local $2) + (i64.shr_s + (i64.shl + (i64.atomic.load8_u + (get_local $2) + ) + (i64.const 56) + ) + (i64.const 56) ) ) (func $SAFE_HEAP_LOAD_i64_1_1 (param $0 i32) (param $1 i32) (result i64) @@ -956,8 +974,14 @@ ) (call $alignfault) ) - (i64.atomic.load16_s - (get_local $2) + (i64.shr_s + (i64.shl + (i64.atomic.load16_u + (get_local $2) + ) + (i64.const 48) + ) + (i64.const 48) ) ) (func $SAFE_HEAP_LOAD_i64_2_2 (param $0 i32) (param $1 i32) (result i64) @@ -1201,8 +1225,14 @@ ) (call $alignfault) ) - (i64.atomic.load32_s - (get_local $2) + (i64.shr_s + (i64.shl + (i64.atomic.load32_u + (get_local $2) + ) + (i64.const 32) + ) + (i64.const 32) ) ) (func $SAFE_HEAP_LOAD_i64_4_4 (param $0 i32) (param $1 i32) (result i64) diff --git a/test/passes/translate-to-fuzz.txt b/test/passes/translate-to-fuzz.txt index fca9307df..f088010f7 100644 --- a/test/passes/translate-to-fuzz.txt +++ b/test/passes/translate-to-fuzz.txt @@ -1,17 +1,17 @@ (module (type $FUNCSIG$ifi (func (param f32 i32) (result i32))) - (type $FUNCSIG$i (func (result i32))) - (type $FUNCSIG$j (func (result i64))) + (type $FUNCSIG$dj (func (param i64) (result f64))) + (type $FUNCSIG$f (func (result f32))) (global $global$0 (mut f64) (f64.const 138413376)) (global $global$1 (mut f64) (f64.const 1.1754943508222875e-38)) (global $hangLimit (mut i32) (i32.const 100)) - (table 1 anyfunc) - (elem (i32.const 0) $func_3) - (memory $0 1 1) + (table 2 anyfunc) + (elem (i32.const 0) $func_0 $func_5) + (memory $0 (shared 1 1)) (data (i32.const 0) "\00C\00[\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") (export "func_0" (func $func_0)) (export "func_3" (func $func_3)) - (export "func_4" (func $func_4)) + (export "func_5" (func $func_5)) (export "hangLimitInitializer" (func $hangLimitInitializer)) (func $func_0 (type $FUNCSIG$ifi) (param $0 f32) (param $1 i32) (result i32) (block @@ -20,7 +20,7 @@ (get_global $hangLimit) ) (return - (i32.const -2147483648) + (get_local $1) ) ) (set_global $hangLimit @@ -30,15 +30,141 @@ ) ) ) - (get_local $1) + (tee_local $1 + (i32.eqz + (if (result i32) + (i32.eqz + (f64.le + (f64.const -2147483648) + (f64.const 8) + ) + ) + (i32.or + (i32.div_s + (if (result i32) + (i32.eqz + (loop $label$0 (result i32) + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return + (i32.const 24332) + ) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) + ) + (block (result i32) + (loop $label$1 + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return + (get_local $1) + ) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) + ) + (nop) + ) + (br_if $label$0 + (i32.eqz + (get_local $1) + ) + ) + (loop $label$3 (result i32) + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return + (get_local $1) + ) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) + ) + (i32.trunc_s/f64 + (f64.const -9223372036854775808) + ) + ) + ) + ) + ) + (block $label$4 (result i32) + (i32.load8_u offset=3 + (i32.and + (br_if $label$4 + (i32.const -44) + (i32.const 437327895) + ) + (i32.const 31) + ) + ) + ) + (block $label$5 (result i32) + (nop) + (return + (i32.const 84) + ) + ) + ) + (block $label$6 (result i32) + (block $label$7 + (block $label$8 + (nop) + (nop) + ) + (br_if $label$7 + (i32.eqz + (get_local $1) + ) + ) + ) + (get_local $1) + ) + ) + (i32.const 1048576) + ) + (get_local $1) + ) + ) + ) ) - (func $func_1 + (func $func_1 (result f64) + (local $0 i64) + (local $1 f32) + (local $2 i32) + (local $3 i64) + (local $4 f32) + (local $5 i32) + (local $6 f32) (block (if (i32.eqz (get_global $hangLimit) ) - (return) + (return + (f64.const 61) + ) ) (set_global $hangLimit (i32.sub @@ -47,40 +173,15 @@ ) ) ) - (loop $label$0 - (block - (if - (i32.eqz - (get_global $hangLimit) - ) - (return) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) - ) - ) - ) - (block $label$1 - (nop) - (nop) - ) - ) + (get_global $global$1) ) - (func $func_2 (param $0 f32) (result f64) - (local $1 f32) - (local $2 i32) - (local $3 i32) - (local $4 i32) + (func $func_2 (block (if (i32.eqz (get_global $hangLimit) ) - (return - (f64.const 26471) - ) + (return) ) (set_global $hangLimit (i32.sub @@ -89,157 +190,93 @@ ) ) ) - (call $deNan64 - (f64.mul - (f64.const 1) - (if (result f64) - (select - (block $label$10 (result i32) - (if - (i32.eqz - (tee_local $2 - (i32.const -2147483648) + (block $label$0 + (set_global $global$0 + (loop $label$1 (result f64) + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) + ) + (block $label$2 (result f64) + (loop $label$3 (result f64) + (block + (if + (i32.eqz + (get_global $hangLimit) ) + (return) ) - (drop - (i32.const -131072) - ) - (block $label$11 - (br_if $label$11 - (tee_local $4 - (i32.const -128) - ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) ) - (call $func_1) ) ) - (return - (f64.const 4) - ) - ) - (tee_local $2 - (tee_local $3 - (get_local $3) - ) - ) - (i32.load16_u offset=4 - (i32.and - (i32.load offset=4 align=1 - (i32.and - (loop $label$0 (result i32) - (block - (if - (i32.eqz - (get_global $hangLimit) - ) - (return - (f64.const -nan:0xfffffffffffb5) - ) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) - ) - ) + (block $label$4 (result f64) + (loop $label$5 + (block + (if + (i32.eqz + (get_global $hangLimit) ) - (block $label$1 (result i32) - (block $label$2 - (nop) - (call $func_1) - ) - (select - (if (result i32) - (get_local $3) - (block $label$3 (result i32) - (block $label$4 - (set_local $2 - (get_local $3) - ) - (set_local $0 - (get_local $1) - ) + (return) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) + ) + (nop) + ) + (call $deNan64 + (f64.reinterpret/i64 + (i64.extend_u/i32 + (block $label$6 (result i32) + (loop $label$7 + (block + (if + (i32.eqz + (get_global $hangLimit) ) - (br $label$0) + (return) ) - (if (result i32) - (i32.eqz - (get_local $3) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) ) - (i32.const -123) - (get_local $4) ) ) - (tee_local $2 - (if (result i32) - (i32.eqz - (call $func_0 - (get_local $1) - (i32.const 0) - ) - ) - (block $label$5 (result i32) - (loop $label$6 - (block - (if - (i32.eqz - (get_global $hangLimit) - ) - (return - (f64.const -33554432) - ) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) - ) - ) - ) - (block - (i64.store offset=4 align=1 - (i32.and - (get_local $4) - (i32.const 31) - ) - (i64.const -1) - ) - (br_if $label$6 - (get_local $2) - ) - (f32.store offset=4 - (i32.and - (i32.const -256) - (i32.const 31) - ) - (f32.const 2147483648) - ) - ) - ) - (i32.load8_s offset=4 - (i32.and - (i32.load16_u offset=3 align=1 - (i32.and - (get_local $2) - (i32.const 31) - ) - ) - (i32.const 31) - ) + (block $label$8 + (nop) + (block $label$9 + (br_if $label$3 + (i32.eqz + (i32.const 185342814) ) ) - (block $label$7 (result i32) - (block $label$8 - (nop) - (loop $label$9 + (i32.store offset=3 align=2 + (i32.and + (loop $label$10 (result i32) (block (if (i32.eqz (get_global $hangLimit) ) - (return - (f64.const -8192) - ) + (return) ) (set_global $hangLimit (i32.sub @@ -248,66 +285,52 @@ ) ) ) - (set_local $2 - (br_if $label$7 - (tee_local $4 - (if (result i32) - (i32.eqz - (get_local $4) - ) - (get_local $2) - (i32.const 1514756685) - ) - ) - (i32.eqz - (i32.load offset=2 - (i32.and - (i32.const 514) - (i32.const 31) - ) + (block (result i32) + (nop) + (br_if $label$10 + (i32.const 1195902538) + ) + (call $func_0 + (call $deNan32 + (f32.convert_u/i64 + (i64.const 2251799813685248) ) ) + (i32.const 336464134) ) ) ) + (i32.const 31) ) - (br $label$0) + (i32.const 27) ) ) ) - (i32.const 2147483647) + ) + (i32.reinterpret/f32 + (f32.const 19533) ) ) ) - (i32.const 31) ) ) - (i32.const 31) ) ) ) - (get_global $global$1) - (block $label$12 (result f64) - (call $func_1) - (return - (f64.const -140737488355328) - ) - ) ) ) + (nop) ) ) - (func $func_3 (type $FUNCSIG$i) (result i32) - (local $0 i32) + (func $func_3 (type $FUNCSIG$dj) (param $0 i64) (result f64) (local $1 i64) - (local $2 f64) (block (if (i32.eqz (get_global $hangLimit) ) (return - (get_local $0) + (f64.const -nan:0xffffffffffff9) ) ) (set_global $hangLimit @@ -317,171 +340,186 @@ ) ) ) - (block $label$0 (result i32) + (block $label$0 (result f64) (nop) - (return - (i32.const -4194304) - ) - ) - ) - (func $func_4 (type $FUNCSIG$j) (result i64) - (local $0 i64) - (local $1 f32) - (local $2 i64) - (block - (if - (i32.eqz - (get_global $hangLimit) - ) - (return - (i64.const 68719476736) - ) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) - ) - ) - ) - (block $label$0 - (i64.store32 offset=4 align=2 - (i32.and - (if (result i32) - (i32.eqz - (block $label$1 (result i32) + (block $label$1 + (drop + (i64.shr_s + (loop $label$2 (result i64) + (block (if (i32.eqz - (loop $label$2 (result i32) - (block - (if - (i32.eqz - (get_global $hangLimit) - ) - (return - (i64.const 512) - ) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) + (get_global $hangLimit) + ) + (return + (f64.const 9150713448007298) + ) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) + ) + (block $label$3 (result i64) + (f64.store offset=2 align=4 + (i32.and + (i32.const 127) + (i32.const 31) + ) + (f64.load offset=3 + (i32.and + (i32.const -16777216) + (i32.const 31) + ) + ) + ) + (i64.popcnt + (block $label$4 (result i64) + (br_if $label$1 + (i32.load offset=1 align=1 + (i32.and + (f64.gt + (br_if $label$0 + (f64.const 7) + (i32.eqz + (call_indirect $FUNCSIG$ifi + (f32.const 2147483648) + (i32.const -28) + (i32.const 0) + ) + ) + ) + (call $func_1) ) - ) - ) - (block $label$3 (result i32) - (return - (get_local $2) + (i32.const 31) ) ) ) - ) - (block $label$4 - (block $label$5 - (set_local $2 - (loop $label$6 (result i64) - (block - (if - (i32.eqz - (get_global $hangLimit) + (br_if $label$4 + (if (result i64) + (i32.const 825692018) + (block $label$8 (result i64) + (loop $label$9 + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return + (f64.const -nan:0xffffffffffff9) + ) ) - (return - (get_local $0) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) ) ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) - ) + (block $label$10 + (nop) + (nop) ) ) - (block (result i64) - (block $label$7 - (drop - (get_local $1) + (i64.load8_u + (block $label$11 (result i32) + (nop) + (i32.const -2) + ) + ) + ) + (i64.shl + (if (result i64) + (f64.ge + (f64.const 4294967200) + (loop $label$12 (result f64) + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return + (f64.const 16471262202863238) + ) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) + ) + (f64.const 1812599913) ) - (br_if $label$5 - (i32.eqz - (i32.const 1393493003) + ) + (block $label$13 (result i64) + (i32.atomic.store8 offset=3 + (i32.and + (i32.const 1026760786) + (i32.const 31) ) + (i32.const -32769) ) + (br $label$1) ) - (br_if $label$6 - (i32.eqz - (br_if $label$1 - (i32.const 32766) - (i32.eqz - (i32.const -73) + (block $label$14 (result i64) + (if + (i32.const 185339652) + (set_local $0 + (select + (get_local $1) + (i64.const 9223372036854775807) + (if (result i32) + (i32.const 0) + (i32.const -21) + (i32.const 39) + ) ) ) + (nop) ) + (br $label$1) ) - (get_local $0) ) - ) - ) - (call $func_1) - ) - (block $label$8 - (drop - (loop $label$9 (result f64) - (block - (if - (i32.eqz - (get_global $hangLimit) - ) - (return - (get_local $2) - ) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) + (i64.div_u + (i64.ctz + (select + (get_local $0) + (i64.const 29041) + (select + (i32.const 256) + (i32.const 65535) + (call $func_0 + (f32.const 1.7485578309992735e-21) + (i32.const 134217728) + ) + ) ) ) + (i64.const -131072) ) - (f64.const 30079) ) ) - ) - ) - (nop) - ) - (call_indirect $FUNCSIG$i - (i32.const 0) - ) - ) - ) - (block $label$10 (result i32) - (i32.store8 offset=22 - (i32.and - (if (result i32) - (i32.eqz - (select - (i32.const 6) - (if (result i32) - (i32.const 4103) - (block $label$12 (result i32) - (drop - (i64.ctz - (i64.const 17) - ) + (i32.atomic.load16_u offset=22 + (i32.and + (if (result i32) + (i32.eqz + (i32.const 268435456) ) - (return - (i64.const 18505) + (block $label$5 (result i32) + (nop) + (br $label$1) ) - ) - (f32.ne - (loop $label$13 (result f32) + (loop $label$6 (result i32) (block (if (i32.eqz (get_global $hangLimit) ) (return - (i64.const 17728) + (f64.const 9030) ) ) (set_global $hangLimit @@ -491,110 +529,15 @@ ) ) ) - (block (result f32) - (block $label$14 - (block $label$15 - (nop) - (nop) - ) - (if - (i32.eqz - (br_if $label$10 - (if (result i32) - (if (result i32) - (i32.const -65536) - (i32.const -32768) - (call_indirect $FUNCSIG$i - (i32.const 0) - ) - ) - (i32.const 454301983) - (i32.const 18460) - ) - (i32.const -64) - ) - ) - (block $label$16 - (if - (i32.const 1014178876) - (if - (i32.eqz - (i32.const -116) - ) - (nop) - (i32.store16 offset=2 align=1 - (i32.and - (i32.const -16) - (i32.const 31) - ) - (i32.load16_s offset=3 - (i32.and - (block $label$17 (result i32) - (nop) - (i32.const 23528569) - ) - (i32.const 31) - ) - ) - ) - ) - (nop) - ) - (br_if $label$16 - (i32.eqz - (block $label$18 (result i32) - (nop) - (i32.const -93) - ) - ) - ) - ) - (br_if $label$13 - (i32.eqz - (i32.const 512) - ) - ) - ) - ) - (br_if $label$13 - (i32.eqz - (br_if $label$10 - (call $func_0 - (call $deNan32 - (f32.convert_u/i64 - (if (result i64) - (i32.eqz - (i32.const 8388608) - ) - (get_local $0) - (get_local $0) - ) - ) - ) - (if (result i32) - (i32.const 4) - (i32.const 1196969301) - (select - (i32.const 64) - (i32.trunc_s/f32 - (get_local $1) - ) - (i32.const -2) - ) - ) - ) - (i32.const -1048576) - ) - ) - ) - (loop $label$19 (result f32) + (block (result i32) + (loop $label$7 (block (if (i32.eqz (get_global $hangLimit) ) (return - (i64.const 357908754) + (f64.const 1797693134862315708145274e284) ) ) (set_global $hangLimit @@ -604,261 +547,232 @@ ) ) ) - (block $label$20 (result f32) - (nop) - (br $label$19) - ) - ) - ) - ) - (call $deNan32 - (f32.floor - (loop $label$21 (result f32) (block - (if - (i32.eqz - (get_global $hangLimit) - ) - (return - (get_local $0) - ) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) - ) + (nop) + (br_if $label$7 + (i32.const 0) ) + (nop) ) - (loop $label$22 (result f32) - (block - (if - (i32.eqz - (get_global $hangLimit) - ) - (return - (get_local $0) - ) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) - ) - ) - ) - (block (result f32) - (br_if $label$21 - (i32.eqz - (if (result i32) - (i32.eqz - (i32.const -4194304) - ) - (i32.const 9537) - (i32.const -262144) - ) - ) - ) - (br_if $label$22 - (call $func_3) - ) - (tee_local $1 - (get_local $1) - ) - ) + ) + (br_if $label$6 + (call $func_0 + (f32.const 82) + (i32.const 770) ) ) + (call_indirect $FUNCSIG$ifi + (f32.const 1.1754943508222875e-38) + (i32.const -1) + (i32.const 0) + ) ) ) ) + (i32.const 31) ) + ) + ) + ) + ) + ) + ) + (i64.const -1) + ) + ) + (f32.store offset=22 + (i32.and + (i32.const 2147483647) + (i32.const 31) + ) + (f32.const -1) + ) + ) + (return + (f64.const 18446744073709551615) + ) + ) + ) + (func $func_4 (result f32) + (local $0 f32) + (local $1 i32) + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return + (f32.const -9223372036854775808) + ) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) + ) + (block $label$0 (result f32) + (return + (f32.const -1048576) + ) + ) + ) + (func $func_5 (type $FUNCSIG$f) (result f32) + (local $0 f32) + (local $1 i32) + (local $2 i32) + (local $3 f64) + (local $4 i64) + (local $5 i32) + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return + (get_local $0) + ) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) + ) + (call $deNan32 + (f32.convert_s/i64 + (if (result i64) + (loop $label$0 (result i32) + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return + (f32.const 28726) + ) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) + ) + (i32.const -76) + ) + (block $label$1 (result i64) + (nop) + (block $label$2 (result i64) + (drop + (if (result f32) + (i32.const -64) + (get_local $0) + (block $label$5 (result f32) + (drop + (get_local $2) + ) + (return + (f32.const 1.9604099208437475e-30) + ) + ) + ) + ) + (return + (get_local $0) + ) + ) + ) + (block $label$6 (result i64) + (set_local $3 + (call $deNan64 + (f64.copysign + (get_global $global$1) + (block $label$7 (result f64) + (block $label$8 + (br_if $label$8 (i32.load offset=4 align=2 (i32.and - (select - (call $func_3) - (i32.const 1) - (call $func_0 - (call $deNan32 + (i32.load16_u offset=4 + (i32.and + (select + (get_local $1) (select - (tee_local $1 - (f32.const 9223372036854775808) - ) - (f32.const -1) - (if (result i32) - (i32.eqz - (i32.const 404363289) - ) - (i32.const 32767) - (block $label$11 (result i32) - (nop) - (i64.eqz - (i64.popcnt - (get_local $0) - ) - ) + (call $func_0 + (get_local $0) + (select + (i32.const 65535) + (get_local $1) + (i32.const 1999974947) ) ) + (i32.const 64) + (get_local $5) ) + (get_local $2) ) - (i32.const 4) + (i32.const 31) ) ) (i32.const 31) ) ) ) - ) - (if (result i32) - (call_indirect $FUNCSIG$i - (i32.const 0) - ) - (block $label$23 (result i32) - (return - (i64.const -2) - ) - ) - (block $label$24 (result i32) - (i32.store16 offset=22 align=1 - (i32.and - (i32.load16_s offset=4 align=1 - (i32.and - (i32.const 125568104) - (i32.const 31) - ) + (loop $label$9 + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return + (get_local $0) ) - (i32.const 31) ) - (call $func_3) - ) - (i32.load16_s offset=3 align=1 - (i32.and - (call $func_0 - (f32.const 0) - (i32.load16_u offset=4 - (i32.and - (if (result i32) - (call_indirect $FUNCSIG$i - (i32.const 0) - ) - (select - (select - (i32.const 1734962792) - (i32.const -4096) - (i32.const 218824716) - ) - (i32.const -1073741824) - (i32.const 40) - ) - (block $label$25 (result i32) - (block $label$26 - (nop) - (block $label$27 - (nop) - (f64.store offset=4 align=4 - (call $func_0 - (f32.const 9223372036854775808) - (f32.eq - (f32.const 18446744073709551615) - (f32.load offset=4 - (i32.and - (i32.const 524288) - (i32.const 31) - ) - ) - ) - ) - (f64.const -1099511627776) - ) - ) - ) - (i32.const 128) - ) - ) - (i32.const 31) - ) - ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) ) - (i32.const 31) ) ) - ) - ) - (i32.const 335937919) - ) - (i32.const 31) - ) - (if (result i32) - (i32.eqz - (call $func_3) - ) - (block $label$28 (result i32) - (block $label$29 - (if - (i32.eqz - (block $label$30 (result i32) - (nop) - (call_indirect $FUNCSIG$i + (block $label$10 + (drop + (call_indirect $FUNCSIG$ifi + (f32.const 2097640320) + (i32.eqz + (select + (i32.const -128) + (get_local $2) + (get_local $2) + ) + ) (i32.const 0) ) ) - ) - (block $label$31 - (nop) - (nop) - ) - (block $label$32 - (nop) - ) - ) - (drop - (i64.div_s - (block $label$33 (result i64) - (call $func_1) - (i64.const -94) - ) - (tee_local $2 - (loop $label$34 (result i64) - (block + (if + (tee_local $2 + (get_local $2) + ) + (block $label$11 + (nop) + (call $func_2) + ) + (block $label$12 + (block $label$13 + (nop) (if - (i32.eqz - (get_global $hangLimit) - ) - (return - (get_local $2) - ) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) - ) - ) - ) - (block (result i64) - (block $label$35 + (i32.const 1936882040) (nop) - (f64.store offset=4 align=2 - (i32.and - (i32.const 127) - (i32.const 31) - ) - (f64.const -2251799813685248) - ) - ) - (br_if $label$34 - (i32.const 471406943) - ) - (select - (get_local $0) - (get_local $0) - (loop $label$36 (result i32) + (loop $label$14 (block (if (i32.eqz (get_global $hangLimit) ) (return - (get_local $0) + (f32.const 4294967296) ) ) (set_global $hangLimit @@ -868,84 +782,53 @@ ) ) ) - (select - (i32.const 23130) - (i32.const 1111901516) - (block $label$37 (result i32) - (nop) - (i32.const -2147483648) + (block + (nop) + (br_if $label$14 + (i32.eqz + (i32.const -2147483648) + ) ) + (nop) ) ) ) ) + (nop) ) ) ) ) ) (return - (get_local $2) - ) - ) - (block $label$39 (result i32) - (drop - (i64.const 255) + (get_local $0) ) - (call $func_3) - ) - ) - ) - (drop - (block $label$40 (result f32) - (return - (i64.const -562949953421312) - ) - ) - ) - (f32.ne - (f32.const 217.36842346191406) - (f32.load offset=4 - (i32.and - (i32.const 822703163) - (i32.const 31) ) ) ) ) - (select - (i32.const 1931754023) - (i32.const -128) - (block $label$41 (result i32) - (call $func_1) - (i32.const 1701396008) + (i64.atomic.rmw8_u.xor offset=4 + (i32.and + (i32.const 32767) + (i32.const 31) ) + (i64.const 9223372036854775807) ) ) - (i32.const 31) ) - (get_local $2) - ) - (return - (get_local $2) ) ) ) - (func $func_5 (result i64) - (local $0 i32) - (local $1 i32) - (local $2 f32) + (func $func_6 (param $0 f32) (param $1 f32) (result f64) + (local $2 i64) (local $3 f32) - (local $4 f32) - (local $5 i64) - (local $6 i64) (block (if (i32.eqz (get_global $hangLimit) ) (return - (get_local $6) + (f64.const -nan:0xffffffffffffa) ) ) (set_global $hangLimit @@ -955,145 +838,175 @@ ) ) ) - (drop + (block $label$0 (result f64) (drop - (block $label$0 - (loop $label$1 - (block - (if - (i32.eqz - (get_global $hangLimit) - ) - (return - (i64.const 6364521229224202323) - ) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) + (i32.atomic.load8_u offset=4 + (i32.atomic.load offset=4 + (i32.and + (loop $label$1 (result i32) + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return + (f64.const 1.4089981056487379e-134) + ) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) ) - ) - ) - (nop) - ) - (f64.store offset=4 align=1 - (block $label$9 - (if - (i32.eqz - (if (result i32) - (if (result i32) - (if (result i32) - (f64.eq - (f64.const 7196) - (f64.const -562949953421312) - ) - (call $func_3) - (block $label$10 (result i32) - (set_global $global$0 - (f64.const 1.9804914402226382e-202) - ) - (return - (get_local $6) - ) - ) + (block (result i32) + (block $label$2 + (if + (i32.eqz + (i32.const 119342106) ) - (block $label$11 (result i32) - (loop $label$12 - (block - (if - (i32.eqz - (get_global $hangLimit) - ) - (return - (i64.const -281474976710656) - ) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) - ) - ) - ) - (set_global $global$0 - (f64.const 127) - ) - ) - (i32.load8_u offset=2 + (block $label$3 + (i64.atomic.store32 offset=3 (i32.and - (i32.trunc_u/f32 - (f32.const -33554432) - ) + (i32.const 127) (i32.const 31) ) + (i64.const 651625613631293707) ) ) - (block $label$13 (result i32) - (call_indirect $FUNCSIG$i - (i32.const 0) - ) - ) + (call $func_2) ) - (block $label$14 (result i32) - (nop) - (loop $label$18 (result i32) - (block - (if - (i32.eqz - (get_global $hangLimit) - ) - (return - (i64.const -4096) - ) - ) - (set_global $hangLimit - (i32.sub - (get_global $hangLimit) - (i32.const 1) + (f64.store offset=22 + (i32.and + (i32.atomic.rmw8_u.xchg offset=22 + (i32.and + (block $label$4 (result i32) + (nop) + (br $label$1) ) + (i32.const 31) ) - ) - (block (result i32) - (block $label$19 - (set_local $6 - (i64.const 36028797018963968) + (select + (i32.atomic.rmw16_u.cmpxchg offset=4 + (i32.and + (i32.const 37) + (i32.const 31) + ) + (select + (i32.atomic.rmw16_u.cmpxchg offset=22 + (i32.and + (f32.ge + (get_local $0) + (f32.const -2147483648) + ) + (i32.const 31) + ) + (loop $label$6 (result i32) + (block + (if + (i32.eqz + (get_global $hangLimit) + ) + (return + (f64.const 1791883197976824701597689e116) + ) + ) + (set_global $hangLimit + (i32.sub + (get_global $hangLimit) + (i32.const 1) + ) + ) + ) + (block (result i32) + (set_local $2 + (i64.const -55) + ) + (br_if $label$6 + (i32.eqz + (i32.const 131072) + ) + ) + (i32.const -134217728) + ) + ) + (i32.const 924871013) + ) + (call_indirect $FUNCSIG$ifi + (call $deNan32 + (f32.sub + (call $deNan32 + (f32.add + (get_local $0) + (f32.const 4.581032111437074e-16) + ) + ) + (tee_local $0 + (block $label$7 (result f32) + (nop) + (get_local $0) + ) + ) + ) + ) + (i32.const 65446) + (i32.const 0) + ) + (call_indirect $FUNCSIG$ifi + (call $deNan32 + (f32.sub + (get_local $3) + (f32.const -4096) + ) + ) + (i32.const -2147483648) + (i32.const 0) + ) + ) + (i32.const -42) ) - (set_local $6 - (tee_local $6 - (call $func_4) + (call_indirect $FUNCSIG$ifi + (call_indirect $FUNCSIG$f + (i32.const -134217728) ) + (i32.const -2147483648) + (i32.const 0) ) - ) - (br_if $label$18 - (tee_local $1 - (i32.const 442457430) + (block $label$5 (result i32) + (drop + (call $func_0 + (tee_local $1 + (get_local $3) + ) + (call_indirect $FUNCSIG$ifi + (get_local $3) + (i32.const 2) + (i32.const 0) + ) + ) + ) + (br $label$1) ) ) - (i32.const -262144) ) + (i32.const 31) ) + (f64.const 1.8443913781292733e-124) ) - (get_local $1) ) - ) - (set_local $1 - (i32.const -2147483648) - ) - (block $label$20 - (nop) + (br_if $label$1 + (i32.const 811742759) + ) + (i32.const 19806) ) ) - (return - (get_local $5) - ) - ) - (return - (i64.const 819399075040401932) + (i32.const 31) ) ) ) ) + (f64.const 3495358995652723316329699e63) ) ) (func $hangLimitInitializer |