summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2024-05-14 10:11:40 -0700
committerGitHub <noreply@github.com>2024-05-14 10:11:40 -0700
commit76a2c64402ebcc3ae6bf32f5f1c90f07d42cfa4b (patch)
treed89d74c2c4c199d1deb6bfb2c2ed50bb579bdb89
parent4ca05f765ca6ec99b7582d357520ca217265677d (diff)
downloadbinaryen-76a2c64402ebcc3ae6bf32f5f1c90f07d42cfa4b.tar.gz
binaryen-76a2c64402ebcc3ae6bf32f5f1c90f07d42cfa4b.tar.bz2
binaryen-76a2c64402ebcc3ae6bf32f5f1c90f07d42cfa4b.zip
LocalCSE: Ignore traps of code in between (#6588)
Given: (ORIGINAL) (in between) (COPY) We want to change that to (local.tee $temp (ORIGINAL)) (in between) (local.get $temp) It is fine if "in between" traps: then we never reach the new local.get. This is a safer situation than most optimizations because we are not reordering anything, only replacing known-equivalent code.
-rw-r--r--src/passes/LocalCSE.cpp12
-rw-r--r--test/lit/passes/local-cse_all-features.wast52
2 files changed, 63 insertions, 1 deletions
diff --git a/src/passes/LocalCSE.cpp b/src/passes/LocalCSE.cpp
index bb017e938..63704382e 100644
--- a/src/passes/LocalCSE.cpp
+++ b/src/passes/LocalCSE.cpp
@@ -421,6 +421,16 @@ struct Checker
// hashed expressions, if there are any.
if (!activeOriginals.empty()) {
EffectAnalyzer effects(options, *getModule());
+ // We can ignore traps here:
+ //
+ // (ORIGINAL)
+ // (curr)
+ // (COPY)
+ //
+ // We are some code in between an original and a copy of it, and we are
+ // trying to turn COPY into a local.get of a value that we stash at the
+ // original. If |curr| traps then we simply don't reach the copy anyhow.
+ effects.trap = false;
// We only need to visit this node itself, as we have already visited its
// children by the time we get here.
effects.visit(curr);
@@ -460,7 +470,7 @@ struct Checker
if (info.requests > 0) {
// This is an original. Compute its side effects, as we cannot optimize
- // away repeated apperances if it has any.
+ // away repeated appearances if it has any.
EffectAnalyzer effects(options, *getModule(), curr);
// We can ignore traps here, as we replace a repeating expression with a
diff --git a/test/lit/passes/local-cse_all-features.wast b/test/lit/passes/local-cse_all-features.wast
index cbd5c68c2..d943b940f 100644
--- a/test/lit/passes/local-cse_all-features.wast
+++ b/test/lit/passes/local-cse_all-features.wast
@@ -350,3 +350,55 @@
)
)
)
+
+(module
+ ;; CHECK: (type $struct (struct (field i32)))
+ (type $struct (struct (field i32)))
+
+ ;; CHECK: (type $1 (func (param anyref)))
+
+ ;; CHECK: (type $2 (func (param i32)))
+
+ ;; CHECK: (func $caller (type $1) (param $x anyref)
+ ;; CHECK-NEXT: (local $1 i32)
+ ;; CHECK-NEXT: (call $callee
+ ;; CHECK-NEXT: (local.tee $1
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (ref.cast (ref $struct)
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $callee
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $caller (param $x anyref)
+ (call $callee
+ (struct.get $struct 0
+ (ref.cast (ref $struct)
+ (local.get $x)
+ )
+ )
+ )
+ ;; The call in between the struct.get has effects, but they do not
+ ;; interfere: the struct.get reads locals and immutable data only, and we
+ ;; can ignore possible traps in both the call and the struct.get (as if
+ ;; anything traps we just don't reach the local.get that the optimization
+ ;; emits).
+ (call $callee
+ (struct.get $struct 0
+ (ref.cast (ref $struct)
+ (local.get $x)
+ )
+ )
+ )
+ )
+
+ ;; CHECK: (func $callee (type $2) (param $x i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $callee (param $x i32)
+ )
+)