summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/Vacuum.cpp80
1 files changed, 79 insertions, 1 deletions
diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp
index 8d89553b2..41a8e02da 100644
--- a/src/passes/Vacuum.cpp
+++ b/src/passes/Vacuum.cpp
@@ -130,9 +130,55 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
}
void visitBlock(Block* curr) {
+ auto& list = curr->list;
+
+ // If traps are assumed to never happen, we can remove code on paths that
+ // must reach a trap:
+ //
+ // (block
+ // (i32.store ..)
+ // (br_if ..) ;; execution branches here, so the first store remains
+ // (i32.store ..) ;; this store can be removed
+ // (unreachable);
+ // )
+ //
+ // For this to be useful we need to have at least 2 elements: something to
+ // remove, and an unreachable.
+ if (getPassOptions().trapsNeverHappen && list.size() >= 2) {
+ // Go backwards. When we find a trap, mark the things before it as heading
+ // to a trap.
+ auto headingToTrap = false;
+ for (int i = list.size() - 1; i >= 0; i--) {
+ if (list[i]->is<Unreachable>()) {
+ headingToTrap = true;
+ continue;
+ }
+
+ if (!headingToTrap) {
+ continue;
+ }
+
+ // Check if we may no longer be heading to a trap. Two situations count
+ // here: Control flow might branch, or we might call (since a call might
+ // reach an import; see notes on that in pass.h:trapsNeverHappen).
+ //
+ // We also cannot remove a pop as it is necessary for structural
+ // reasons.
+ EffectAnalyzer effects(getPassOptions(), *getModule(), list[i]);
+ if (effects.transfersControlFlow() || effects.calls ||
+ effects.danglingPop) {
+ headingToTrap = false;
+ continue;
+ }
+
+ // This code can be removed! Turn it into a nop, and leave it for the
+ // code lower down to finish cleaning up.
+ ExpressionManipulator::nop(list[i]);
+ }
+ }
+
// compress out nops and other dead code
int skip = 0;
- auto& list = curr->list;
size_t size = list.size();
for (size_t z = 0; z < size; z++) {
auto* child = list[z];
@@ -210,6 +256,38 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
return;
}
// from here on, we can assume the condition executed
+
+ // In trapsNeverHappen mode, a definitely-trapping arm can be assumed to not
+ // happen. Such conditional code can be assumed to never be reached in this
+ // mode.
+ //
+ // Ignore the case of an unreachable if, such as having both arms be
+ // unreachable. In that case we'd need to fix up the IR to avoid changing
+ // the type; leave that for DCE to simplify first. After checking that
+ // curr->type != unreachable, we can assume that only one of the arms is
+ // unreachable (at most).
+ if (getPassOptions().trapsNeverHappen && curr->type != Type::unreachable) {
+ auto optimizeArm = [&](Expression* arm, Expression* otherArm) {
+ if (!arm->is<Unreachable>()) {
+ return false;
+ }
+ Builder builder(*getModule());
+ Expression* rep = builder.makeDrop(curr->condition);
+ if (otherArm) {
+ rep = builder.makeSequence(rep, otherArm);
+ }
+ replaceCurrent(rep);
+ return true;
+ };
+
+ // As mentioned above, do not try to optimize both arms; leave that case
+ // for DCE.
+ if (optimizeArm(curr->ifTrue, curr->ifFalse) ||
+ (curr->ifFalse && optimizeArm(curr->ifFalse, curr->ifTrue))) {
+ return;
+ }
+ }
+
if (curr->ifFalse) {
if (curr->ifFalse->is<Nop>()) {
curr->ifFalse = nullptr;