summaryrefslogtreecommitdiff
path: root/src/ast
diff options
context:
space:
mode:
Diffstat (limited to 'src/ast')
-rw-r--r--src/ast/ExpressionManipulator.cpp16
-rw-r--r--src/ast/cost.h10
-rw-r--r--src/ast/effects.h35
3 files changed, 55 insertions, 6 deletions
diff --git a/src/ast/ExpressionManipulator.cpp b/src/ast/ExpressionManipulator.cpp
index 3868ca316..cca799e10 100644
--- a/src/ast/ExpressionManipulator.cpp
+++ b/src/ast/ExpressionManipulator.cpp
@@ -96,11 +96,27 @@ Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom
return builder.makeSetGlobal(curr->name, copy(curr->value));
}
Expression* visitLoad(Load *curr) {
+ if (curr->isAtomic) {
+ return builder.makeAtomicLoad(curr->bytes, curr->signed_, 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.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);
+ }
+ Expression* visitAtomicCmpxchg(AtomicCmpxchg* curr) {
+ return builder.makeAtomicCmpxchg(curr->bytes, curr->offset,
+ copy(curr->ptr), copy(curr->expected), copy(curr->replacement),
+ curr->type);
+ }
Expression* visitConst(Const *curr) {
return builder.makeConst(curr->value);
}
diff --git a/src/ast/cost.h b/src/ast/cost.h
index 151468650..56050b189 100644
--- a/src/ast/cost.h
+++ b/src/ast/cost.h
@@ -78,10 +78,16 @@ struct CostAnalyzer : public Visitor<CostAnalyzer, Index> {
return 2;
}
Index visitLoad(Load *curr) {
- return 1 + visit(curr->ptr);
+ return 1 + visit(curr->ptr) + 10 * curr->isAtomic;
}
Index visitStore(Store *curr) {
- return 2 + visit(curr->ptr) + visit(curr->value);
+ return 2 + visit(curr->ptr) + visit(curr->value) + 10 * curr->isAtomic;
+ }
+ Index visitAtomicRMW(AtomicRMW *curr) {
+ return 100;
+ }
+ Index visitAtomicCmpxchg(AtomicCmpxchg* curr) {
+ return 100;
}
Index visitConst(Const *curr) {
return 1;
diff --git a/src/ast/effects.h b/src/ast/effects.h
index 6e4bb617e..5392c0e50 100644
--- a/src/ast/effects.h
+++ b/src/ast/effects.h
@@ -53,12 +53,14 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
// (so a trap may occur later or earlier, if it is
// going to occur anyhow), but we can't remove them,
// they count as side effects
+ bool isAtomic = false; // An atomic load/store/RMW/Cmpxchg or an operator that
+ // has a defined ordering wrt atomics (e.g. grow_memory)
bool accessesLocal() { return localsRead.size() + localsWritten.size() > 0; }
bool accessesGlobal() { return globalsRead.size() + globalsWritten.size() > 0; }
bool accessesMemory() { return calls || readsMemory || writesMemory; }
- bool hasSideEffects() { return calls || localsWritten.size() > 0 || writesMemory || branches || globalsWritten.size() > 0 || implicitTrap; }
- bool hasAnything() { return branches || calls || accessesLocal() || readsMemory || writesMemory || accessesGlobal() || implicitTrap; }
+ bool hasSideEffects() { return calls || localsWritten.size() > 0 || writesMemory || branches || globalsWritten.size() > 0 || implicitTrap || isAtomic; }
+ bool hasAnything() { return branches || calls || accessesLocal() || readsMemory || writesMemory || accessesGlobal() || implicitTrap || isAtomic; }
// checks if these effects would invalidate another set (e.g., if we write, we invalidate someone that reads, they can't be moved past us)
bool invalidates(EffectAnalyzer& other) {
@@ -67,6 +69,12 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
|| (accessesMemory() && (other.writesMemory || other.calls))) {
return true;
}
+ // All atomics are sequentially consistent for now, and ordered wrt other
+ // memory references.
+ if ((isAtomic && other.accessesMemory()) ||
+ (other.isAtomic && accessesMemory())) {
+ return true;
+ }
for (auto local : localsWritten) {
if (other.localsWritten.count(local) || other.localsRead.count(local)) {
return true;
@@ -176,10 +184,24 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
}
void visitLoad(Load *curr) {
readsMemory = true;
+ isAtomic |= curr->isAtomic;
if (!ignoreImplicitTraps) implicitTrap = true;
}
void visitStore(Store *curr) {
writesMemory = true;
+ isAtomic |= curr->isAtomic;
+ if (!ignoreImplicitTraps) implicitTrap = true;
+ }
+ void visitAtomicRMW(AtomicRMW* curr) {
+ readsMemory = true;
+ writesMemory = true;
+ isAtomic = true;
+ if (!ignoreImplicitTraps) implicitTrap = true;
+ }
+ void visitAtomicCmpxchg(AtomicCmpxchg* curr) {
+ readsMemory = true;
+ writesMemory = true;
+ isAtomic = true;
if (!ignoreImplicitTraps) implicitTrap = true;
}
void visitUnary(Unary *curr) {
@@ -219,11 +241,16 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
}
}
void visitReturn(Return *curr) { branches = true; }
- void visitHost(Host *curr) { calls = true; }
+ void visitHost(Host *curr) {
+ calls = true;
+ // grow_memory modifies the set of valid addresses, and thus can be modeled as modifying memory
+ writesMemory = true;
+ // Atomics are also sequentially consistent with grow_memory.
+ isAtomic = true;
+ }
void visitUnreachable(Unreachable *curr) { branches = true; }
};
} // namespace wasm
#endif // wasm_ast_effects_h
-