summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJérôme Vouillon <jerome.vouillon@gmail.com>2024-04-18 13:05:53 -0400
committerGitHub <noreply@github.com>2024-04-18 10:05:53 -0700
commit3fd5c4f3dfc0fc2ccd1dc5ed39e4f0ffb46c10b8 (patch)
tree40d2ddfd2f5f18a6476d5f007e09c4a4b826eff2
parent1eedf1e3e76b4650015cec007a9d7f8c4be3898b (diff)
downloadbinaryen-3fd5c4f3dfc0fc2ccd1dc5ed39e4f0ffb46c10b8.tar.gz
binaryen-3fd5c4f3dfc0fc2ccd1dc5ed39e4f0ffb46c10b8.tar.bz2
binaryen-3fd5c4f3dfc0fc2ccd1dc5ed39e4f0ffb46c10b8.zip
OptimizeCasts: Also handle local.tee (#6507)
Converts the following: (some.operation (ref.cast .. (local.tee $ref ..)) (local.get $ref) ) into: (some.operation (local.tee $temp (ref.cast .. (local.tee $ref ..)) ) (local.get $temp) )
-rw-r--r--src/passes/OptimizeCasts.cpp53
-rw-r--r--test/lit/passes/optimize-casts.wast37
2 files changed, 63 insertions, 27 deletions
diff --git a/src/passes/OptimizeCasts.cpp b/src/passes/OptimizeCasts.cpp
index 7877bea9f..5edde4d2f 100644
--- a/src/passes/OptimizeCasts.cpp
+++ b/src/passes/OptimizeCasts.cpp
@@ -93,21 +93,10 @@
// RefAs with ExternInternalize and ExternExternalize are not considered casts
// when obtaining fallthroughs, and so are ignored.
//
-// TODO: 1. Look past individual basic blocks? This may be worth considering
-// given the pattern of a cast appearing in an if condition that is
-// then used in an if arm, for example, where simple dominance shows
-// the cast can be reused.
-// TODO: 2. Look at LocalSet as well and not just Get. That would add some
-// overlap with the other passes mentioned above (SimplifyLocals and
-// RedundantSetElimination also track sets and can switch a get to use
-// a better set's index when that refines the type). But once we do the
-// first two TODOs above then we'd be adding some novel things here,
-// as we could optimize "backwards" as well (TODO 1) and past basic
-// blocks (TODO 2, though RedundantSetElimination does that as well).
-// However, we should consider whether improving those other passes
-// might make more sense (as it would help more than casts, if we could
-// make them operate "backwards" and/or past basic blocks).
-//
+// TODO: Look past individual basic blocks? This may be worth considering
+// given the pattern of a cast appearing in an if condition that is
+// then used in an if arm, for example, where simple dominance shows
+// the cast can be reused.
#include "ir/effects.h"
#include "ir/linear-execution.h"
@@ -453,20 +442,30 @@ struct BestCastFinder : public LinearExecutionWalker<BestCastFinder> {
void visitRefCast(RefCast* curr) { handleRefinement(curr); }
void handleRefinement(Expression* curr) {
- auto* fallthrough = Properties::getFallthrough(curr, options, *getModule());
+ auto* teeFallthrough = Properties::getFallthrough(
+ curr, options, *getModule(), Properties::FallthroughBehavior::NoTeeBrIf);
+ if (auto* tee = teeFallthrough->dynCast<LocalSet>()) {
+ updateBestCast(curr, tee->index);
+ }
+ auto* fallthrough =
+ Properties::getFallthrough(teeFallthrough, options, *getModule());
if (auto* get = fallthrough->dynCast<LocalGet>()) {
- auto*& bestCast = mostCastedGets[get->index];
- if (!bestCast) {
- // This is the first.
- bestCast = curr;
- return;
- }
+ updateBestCast(curr, get->index);
+ }
+ }
- // See if we are better than the current best.
- if (curr->type != bestCast->type &&
- Type::isSubType(curr->type, bestCast->type)) {
- bestCast = curr;
- }
+ void updateBestCast(Expression* curr, Index index) {
+ auto*& bestCast = mostCastedGets[index];
+ if (!bestCast) {
+ // This is the first.
+ bestCast = curr;
+ return;
+ }
+
+ // See if we are better than the current best.
+ if (curr->type != bestCast->type &&
+ Type::isSubType(curr->type, bestCast->type)) {
+ bestCast = curr;
}
}
};
diff --git a/test/lit/passes/optimize-casts.wast b/test/lit/passes/optimize-casts.wast
index 512a04e9d..a10540f5d 100644
--- a/test/lit/passes/optimize-casts.wast
+++ b/test/lit/passes/optimize-casts.wast
@@ -1352,6 +1352,43 @@
)
)
+ ;; CHECK: (func $local-tee (type $2) (param $x (ref struct))
+ ;; CHECK-NEXT: (local $y (ref struct))
+ ;; CHECK-NEXT: (local $2 (ref $A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $2
+ ;; CHECK-NEXT: (ref.cast (ref $A)
+ ;; CHECK-NEXT: (local.tee $y
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $local-tee (param $x (ref struct))
+ (local $y (ref struct))
+ ;; We should use the cast value after it has been computed, in both gets.
+ (drop
+ (ref.cast (ref $A)
+ (local.tee $y
+ (local.get $x)
+ )
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (local.get $y)
+ )
+ )
+
;; CHECK: (func $get (type $11) (result (ref struct))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )