summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm2wasm.h8
-rw-r--r--src/ast/ExpressionManipulator.cpp14
-rw-r--r--src/ast/bits.h39
-rw-r--r--src/ast/global-utils.h2
-rw-r--r--src/passes/DeadCodeElimination.cpp4
-rw-r--r--src/passes/Precompute.cpp6
-rw-r--r--src/passes/Print.cpp4
-rw-r--r--src/passes/SafeHeap.cpp9
-rw-r--r--src/tools/translate-to-fuzz.h186
-rw-r--r--src/wasm-builder.h12
-rw-r--r--src/wasm-interpreter.h164
-rw-r--r--src/wasm/wasm-binary.cpp22
-rw-r--r--src/wasm/wasm-validator.cpp7
-rw-r--r--src/wasm/wasm.cpp2
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;
}