|
21 | 21 | #include <ir/branch-utils.h> |
22 | 22 | #include <ir/cost.h> |
23 | 23 | #include <ir/effects.h> |
| 24 | +#include <ir/literal-utils.h> |
24 | 25 | #include <ir/utils.h> |
25 | 26 | #include <parsing.h> |
26 | 27 | #include <pass.h> |
@@ -49,6 +50,23 @@ static bool canTurnIfIntoBrIf(Expression* ifCondition, |
49 | 50 | return !EffectAnalyzer(options, ifCondition).invalidates(value); |
50 | 51 | } |
51 | 52 |
|
| 53 | +// Check if it is not worth it to run code unconditionally. This |
| 54 | +// assumes we are trying to run two expressions where previously |
| 55 | +// only one of the two might have executed. We assume here that |
| 56 | +// executing both is good for code size. |
| 57 | +static bool tooCostlyToRunUnconditionally(const PassOptions& passOptions, |
| 58 | + Expression* one, |
| 59 | + Expression* two) { |
| 60 | + // If we care mostly about code size, just do it for that reason. |
| 61 | + if (passOptions.shrinkLevel) { |
| 62 | + return false; |
| 63 | + } |
| 64 | + // Consider the cost of executing all the code unconditionally. |
| 65 | + const auto TOO_MUCH = 7; |
| 66 | + auto total = CostAnalyzer(one).cost + CostAnalyzer(two).cost; |
| 67 | + return total >= TOO_MUCH; |
| 68 | +} |
| 69 | + |
52 | 70 | struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { |
53 | 71 | bool isFunctionParallel() override { return true; } |
54 | 72 |
|
@@ -283,12 +301,40 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { |
283 | 301 |
|
284 | 302 | void visitIf(If* curr) { |
285 | 303 | if (!curr->ifFalse) { |
286 | | - // if without an else. try to reduce if (condition) br => br_if |
287 | | - // (condition) |
288 | | - Break* br = curr->ifTrue->dynCast<Break>(); |
289 | | - if (br && !br->condition) { // TODO: if there is a condition, join them |
| 304 | + // if without an else. try to reduce |
| 305 | + // if (condition) br => br_if (condition) |
| 306 | + if (Break* br = curr->ifTrue->dynCast<Break>()) { |
290 | 307 | if (canTurnIfIntoBrIf(curr->condition, br->value, getPassOptions())) { |
291 | | - br->condition = curr->condition; |
| 308 | + if (!br->condition) { |
| 309 | + br->condition = curr->condition; |
| 310 | + } else { |
| 311 | + // In this case we can replace |
| 312 | + // if (condition1) br_if (condition2) |
| 313 | + // => |
| 314 | + // br_if select (condition1) (condition2) (i32.const 0) |
| 315 | + // In other words, we replace an if (3 bytes) with a select and a |
| 316 | + // zero (also 3 bytes). The size is unchanged, but the select may |
| 317 | + // be further optimizable, and if select does not branch we also |
| 318 | + // avoid one branch. |
| 319 | + // If running the br's condition unconditionally is too expensive, |
| 320 | + // give up. |
| 321 | + auto* zero = LiteralUtils::makeZero(i32, *getModule()); |
| 322 | + if (tooCostlyToRunUnconditionally( |
| 323 | + getPassOptions(), br->condition, zero)) { |
| 324 | + return; |
| 325 | + } |
| 326 | + // Of course we can't do this if the br's condition has side |
| 327 | + // effects, as we would then execute those unconditionally. |
| 328 | + if (EffectAnalyzer(getPassOptions(), br->condition) |
| 329 | + .hasSideEffects()) { |
| 330 | + return; |
| 331 | + } |
| 332 | + Builder builder(*getModule()); |
| 333 | + // Note that we use the br's condition as the select condition. |
| 334 | + // That keeps the order of the two conditions as it was originally. |
| 335 | + br->condition = |
| 336 | + builder.makeSelect(br->condition, curr->condition, zero); |
| 337 | + } |
292 | 338 | br->finalize(); |
293 | 339 | replaceCurrent(Builder(*getModule()).dropIfConcretelyTyped(br)); |
294 | 340 | anotherCycle = true; |
@@ -871,14 +917,9 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { |
871 | 917 | // This is always helpful for code size, but can be a tradeoff with |
872 | 918 | // performance as we run both code paths. So when shrinking we always |
873 | 919 | // try to do this, but otherwise must consider more carefully. |
874 | | - if (!passOptions.shrinkLevel) { |
875 | | - // Consider the cost of executing all the code unconditionally |
876 | | - const auto MAX_COST = 7; |
877 | | - auto total = |
878 | | - CostAnalyzer(iff->ifTrue).cost + CostAnalyzer(iff->ifFalse).cost; |
879 | | - if (total >= MAX_COST) { |
880 | | - return nullptr; |
881 | | - } |
| 920 | + if (tooCostlyToRunUnconditionally( |
| 921 | + passOptions, iff->ifTrue, iff->ifFalse)) { |
| 922 | + return nullptr; |
882 | 923 | } |
883 | 924 | // Check if side effects allow this. |
884 | 925 | EffectAnalyzer condition(passOptions, iff->condition); |
|
0 commit comments