summaryrefslogtreecommitdiff
path: root/src/passes/RemoveUnusedBrs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/passes/RemoveUnusedBrs.cpp')
-rw-r--r--src/passes/RemoveUnusedBrs.cpp67
1 files changed, 54 insertions, 13 deletions
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp
index 1e67ecec3..7f3a9965d 100644
--- a/src/passes/RemoveUnusedBrs.cpp
+++ b/src/passes/RemoveUnusedBrs.cpp
@@ -21,6 +21,7 @@
#include <ir/branch-utils.h>
#include <ir/cost.h>
#include <ir/effects.h>
+#include <ir/literal-utils.h>
#include <ir/utils.h>
#include <parsing.h>
#include <pass.h>
@@ -49,6 +50,23 @@ static bool canTurnIfIntoBrIf(Expression* ifCondition,
return !EffectAnalyzer(options, ifCondition).invalidates(value);
}
+// Check if it is not worth it to run code unconditionally. This
+// assumes we are trying to run two expressions where previously
+// only one of the two might have executed. We assume here that
+// executing both is good for code size.
+static bool tooCostlyToRunUnconditionally(const PassOptions& passOptions,
+ Expression* one,
+ Expression* two) {
+ // If we care mostly about code size, just do it for that reason.
+ if (passOptions.shrinkLevel) {
+ return false;
+ }
+ // Consider the cost of executing all the code unconditionally.
+ const auto TOO_MUCH = 7;
+ auto total = CostAnalyzer(one).cost + CostAnalyzer(two).cost;
+ return total >= TOO_MUCH;
+}
+
struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
bool isFunctionParallel() override { return true; }
@@ -283,12 +301,40 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
void visitIf(If* curr) {
if (!curr->ifFalse) {
- // if without an else. try to reduce if (condition) br => br_if
- // (condition)
- Break* br = curr->ifTrue->dynCast<Break>();
- if (br && !br->condition) { // TODO: if there is a condition, join them
+ // if without an else. try to reduce
+ // if (condition) br => br_if (condition)
+ if (Break* br = curr->ifTrue->dynCast<Break>()) {
if (canTurnIfIntoBrIf(curr->condition, br->value, getPassOptions())) {
- br->condition = curr->condition;
+ if (!br->condition) {
+ br->condition = curr->condition;
+ } else {
+ // In this case we can replace
+ // if (condition1) br_if (condition2)
+ // =>
+ // br_if select (condition1) (condition2) (i32.const 0)
+ // In other words, we replace an if (3 bytes) with a select and a
+ // zero (also 3 bytes). The size is unchanged, but the select may
+ // be further optimizable, and if select does not branch we also
+ // avoid one branch.
+ // If running the br's condition unconditionally is too expensive,
+ // give up.
+ auto* zero = LiteralUtils::makeZero(i32, *getModule());
+ if (tooCostlyToRunUnconditionally(
+ getPassOptions(), br->condition, zero)) {
+ return;
+ }
+ // Of course we can't do this if the br's condition has side
+ // effects, as we would then execute those unconditionally.
+ if (EffectAnalyzer(getPassOptions(), br->condition)
+ .hasSideEffects()) {
+ return;
+ }
+ Builder builder(*getModule());
+ // Note that we use the br's condition as the select condition.
+ // That keeps the order of the two conditions as it was originally.
+ br->condition =
+ builder.makeSelect(br->condition, curr->condition, zero);
+ }
br->finalize();
replaceCurrent(Builder(*getModule()).dropIfConcretelyTyped(br));
anotherCycle = true;
@@ -871,14 +917,9 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
// This is always helpful for code size, but can be a tradeoff with
// performance as we run both code paths. So when shrinking we always
// try to do this, but otherwise must consider more carefully.
- if (!passOptions.shrinkLevel) {
- // Consider the cost of executing all the code unconditionally
- const auto MAX_COST = 7;
- auto total =
- CostAnalyzer(iff->ifTrue).cost + CostAnalyzer(iff->ifFalse).cost;
- if (total >= MAX_COST) {
- return nullptr;
- }
+ if (tooCostlyToRunUnconditionally(
+ passOptions, iff->ifTrue, iff->ifFalse)) {
+ return nullptr;
}
// Check if side effects allow this.
EffectAnalyzer condition(passOptions, iff->condition);