summaryrefslogtreecommitdiff
path: root/src/ir/effects.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/ir/effects.h')
-rw-r--r--src/ir/effects.h93
1 files changed, 76 insertions, 17 deletions
diff --git a/src/ir/effects.h b/src/ir/effects.h
index 0d1e2c2b1..9c1ed18f2 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -27,9 +27,11 @@ namespace wasm {
struct EffectAnalyzer
: public PostWalker<EffectAnalyzer, OverriddenVisitor<EffectAnalyzer>> {
- EffectAnalyzer(const PassOptions& passOptions, Expression* ast = nullptr) {
- ignoreImplicitTraps = passOptions.ignoreImplicitTraps;
- debugInfo = passOptions.debugInfo;
+ EffectAnalyzer(const PassOptions& passOptions,
+ FeatureSet features,
+ Expression* ast = nullptr)
+ : ignoreImplicitTraps(passOptions.ignoreImplicitTraps),
+ debugInfo(passOptions.debugInfo), features(features) {
if (ast) {
analyze(ast);
}
@@ -37,6 +39,7 @@ struct EffectAnalyzer
bool ignoreImplicitTraps;
bool debugInfo;
+ FeatureSet features;
void analyze(Expression* ast) {
breakNames.clear();
@@ -45,6 +48,7 @@ struct EffectAnalyzer
if (breakNames.size() > 0) {
branches = true;
}
+ assert(tryDepth == 0);
}
// Core effect tracking
@@ -66,6 +70,36 @@ struct EffectAnalyzer
// An atomic load/store/RMW/Cmpxchg or an operator that has a defined ordering
// wrt atomics (e.g. memory.grow)
bool isAtomic = false;
+ bool throws = false;
+ // The nested depth of try. If an instruction that may throw is inside an
+ // inner try, we don't mark it as 'throws', because it will be caught by an
+ // inner catch.
+ size_t tryDepth = 0;
+
+ static void scan(EffectAnalyzer* self, Expression** currp) {
+ Expression* curr = *currp;
+ // We need to decrement try depth before catch starts, so handle it
+ // separately
+ if (curr->is<Try>()) {
+ self->pushTask(doVisitTry, currp);
+ self->pushTask(scan, &curr->cast<Try>()->catchBody);
+ self->pushTask(doStartCatch, currp);
+ self->pushTask(scan, &curr->cast<Try>()->body);
+ self->pushTask(doStartTry, currp);
+ return;
+ }
+ PostWalker<EffectAnalyzer, OverriddenVisitor<EffectAnalyzer>>::scan(self,
+ currp);
+ }
+
+ static void doStartTry(EffectAnalyzer* self, Expression** currp) {
+ self->tryDepth++;
+ }
+
+ static void doStartCatch(EffectAnalyzer* self, Expression** currp) {
+ assert(self->tryDepth > 0 && "try depth cannot be negative");
+ self->tryDepth--;
+ }
// Helper functions to check for various effect types
@@ -76,17 +110,20 @@ struct EffectAnalyzer
return globalsRead.size() + globalsWritten.size() > 0;
}
bool accessesMemory() const { return calls || readsMemory || writesMemory; }
+ bool transfersControlFlow() const { return branches || throws; }
bool hasGlobalSideEffects() const {
- return calls || globalsWritten.size() > 0 || writesMemory || isAtomic;
+ return calls || globalsWritten.size() > 0 || writesMemory || isAtomic ||
+ throws;
}
bool hasSideEffects() const {
return hasGlobalSideEffects() || localsWritten.size() > 0 || branches ||
implicitTrap;
}
bool hasAnything() const {
- return branches || calls || accessesLocal() || readsMemory ||
- writesMemory || accessesGlobal() || implicitTrap || isAtomic;
+ return calls || accessesLocal() || readsMemory || writesMemory ||
+ accessesGlobal() || implicitTrap || isAtomic ||
+ transfersControlFlow();
}
bool noticesGlobalSideEffects() {
@@ -99,8 +136,8 @@ struct EffectAnalyzer
// 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(const EffectAnalyzer& other) {
- if ((branches && other.hasSideEffects()) ||
- (other.branches && hasSideEffects()) ||
+ if ((transfersControlFlow() && other.hasSideEffects()) ||
+ (other.transfersControlFlow() && hasSideEffects()) ||
((writesMemory || calls) && other.accessesMemory()) ||
(accessesMemory() && (other.writesMemory || other.calls))) {
return true;
@@ -137,7 +174,8 @@ struct EffectAnalyzer
}
}
// we are ok to reorder implicit traps, but not conditionalize them
- if ((implicitTrap && other.branches) || (other.implicitTrap && branches)) {
+ if ((implicitTrap && other.transfersControlFlow()) ||
+ (other.implicitTrap && transfersControlFlow())) {
return true;
}
// we can't reorder an implicit trap in a way that alters global state
@@ -155,6 +193,7 @@ struct EffectAnalyzer
writesMemory = writesMemory || other.writesMemory;
implicitTrap = implicitTrap || other.implicitTrap;
isAtomic = isAtomic || other.isAtomic;
+ throws = throws || other.throws;
for (auto i : other.localsRead) {
localsRead.insert(i);
}
@@ -223,6 +262,10 @@ struct EffectAnalyzer
void visitCall(Call* curr) {
calls = true;
+ // When EH is enabled, any call can throw.
+ if (features.hasExceptionHandling() && tryDepth == 0) {
+ throws = true;
+ }
if (curr->isReturn) {
branches = true;
}
@@ -235,6 +278,9 @@ struct EffectAnalyzer
}
void visitCallIndirect(CallIndirect* curr) {
calls = true;
+ if (features.hasExceptionHandling() && tryDepth == 0) {
+ throws = true;
+ }
if (curr->isReturn) {
branches = true;
}
@@ -391,9 +437,16 @@ struct EffectAnalyzer
void visitRefIsNull(RefIsNull* curr) {}
void visitRefFunc(RefFunc* curr) {}
void visitTry(Try* curr) {}
- // We safely model throws as branches
- void visitThrow(Throw* curr) { branches = true; }
- void visitRethrow(Rethrow* curr) { branches = true; }
+ void visitThrow(Throw* curr) {
+ if (tryDepth == 0) {
+ throws = true;
+ }
+ }
+ void visitRethrow(Rethrow* curr) {
+ if (tryDepth == 0) {
+ throws = true;
+ }
+ }
void visitBrOnExn(BrOnExn* curr) { breakNames.insert(curr->name); }
void visitNop(Nop* curr) {}
void visitUnreachable(Unreachable* curr) { branches = true; }
@@ -402,10 +455,12 @@ struct EffectAnalyzer
// Helpers
- static bool
- canReorder(const PassOptions& passOptions, Expression* a, Expression* b) {
- EffectAnalyzer aEffects(passOptions, a);
- EffectAnalyzer bEffects(passOptions, b);
+ static bool canReorder(const PassOptions& passOptions,
+ FeatureSet features,
+ Expression* a,
+ Expression* b) {
+ EffectAnalyzer aEffects(passOptions, features, a);
+ EffectAnalyzer bEffects(passOptions, features, b);
return !aEffects.invalidates(bEffects);
}
@@ -423,7 +478,8 @@ struct EffectAnalyzer
WritesMemory = 1 << 7,
ImplicitTrap = 1 << 8,
IsAtomic = 1 << 9,
- Any = (1 << 10) - 1
+ Throws = 1 << 10,
+ Any = (1 << 11) - 1
};
uint32_t getSideEffects() const {
uint32_t effects = 0;
@@ -457,6 +513,9 @@ struct EffectAnalyzer
if (isAtomic) {
effects |= SideEffects::IsAtomic;
}
+ if (throws) {
+ effects |= SideEffects::Throws;
+ }
return effects;
}
};