summaryrefslogtreecommitdiff
path: root/test/lit/passes/optimize-casts.wast
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2022-11-17 15:00:28 -0800
committerGitHub <noreply@github.com>2022-11-17 23:00:28 +0000
commit977d653f9801b3eedc7dd667e4068573e73d2bb5 (patch)
tree8876e96e5f31d32b6a656a627b02e635db1314af /test/lit/passes/optimize-casts.wast
parent5f5c70255cfa917efee9855ce1f8340b017e0adb (diff)
downloadbinaryen-977d653f9801b3eedc7dd667e4068573e73d2bb5.tar.gz
binaryen-977d653f9801b3eedc7dd667e4068573e73d2bb5.tar.bz2
binaryen-977d653f9801b3eedc7dd667e4068573e73d2bb5.zip
[Wasm GC] Start an OptimizeCasts pass and reuse cast values there (#5263)
(some.operation (ref.cast .. (local.get $ref)) (local.get $ref) ) => (some.operation (local.tee $temp (ref.cast .. (local.get $ref)) ) (local.get $temp) ) This can help cases where we cast for some reason but happen to not use the cast value in all places. This occurs in j2wasm in itable calls sometimes: The this pointer is is refined, but the itable may be done with an unrefined pointer, which is less optimizable. So far this is just inside basic blocks, but that is enough for the cast of itable calls and other common patterns I see.
Diffstat (limited to 'test/lit/passes/optimize-casts.wast')
-rw-r--r--test/lit/passes/optimize-casts.wast398
1 files changed, 398 insertions, 0 deletions
diff --git a/test/lit/passes/optimize-casts.wast b/test/lit/passes/optimize-casts.wast
new file mode 100644
index 000000000..449416e42
--- /dev/null
+++ b/test/lit/passes/optimize-casts.wast
@@ -0,0 +1,398 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+;; RUN: wasm-opt %s --optimize-casts -all --nominal -S -o - \
+;; RUN: | filecheck %s
+
+(module
+ ;; CHECK: (type $A (struct_subtype data))
+ (type $A (struct_subtype data))
+
+ ;; CHECK: (type $B (struct_subtype $A))
+ (type $B (struct_subtype $A))
+
+ ;; CHECK: (func $ref.as (type $ref?|$A|_=>_none) (param $x (ref null $A))
+ ;; CHECK-NEXT: (local $1 (ref $A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $1
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $ref.as (param $x (ref null $A))
+ ;; After the first ref.as, we can use the cast value in later gets, which is
+ ;; more refined.
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (ref.as_non_null
+ (local.get $x)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ ;; In this case we don't really need the last ref.as here, but we leave that
+ ;; for later opts.
+ (drop
+ (ref.as_non_null
+ (local.get $x)
+ )
+ )
+ )
+
+ ;; CHECK: (func $ref.as-no (type $ref|$A|_=>_none) (param $x (ref $A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $ref.as-no (param $x (ref $A))
+ ;; As above, but the param is now non-nullable anyhow, so we should do
+ ;; nothing.
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (ref.as_non_null
+ (local.get $x)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (ref.as_non_null
+ (local.get $x)
+ )
+ )
+ )
+
+ ;; CHECK: (func $ref.cast (type $ref|data|_=>_none) (param $x (ref data))
+ ;; CHECK-NEXT: (local $1 (ref $A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $1
+ ;; CHECK-NEXT: (ref.cast_static $A
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $ref.cast (param $x (ref data))
+ ;; As $ref.as but with ref.casts: we should use the cast value after it has
+ ;; been computed, in both gets.
+ (drop
+ (ref.cast_static $A
+ (local.get $x)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+
+ ;; CHECK: (func $not-past-set (type $ref|data|_=>_none) (param $x (ref data))
+ ;; CHECK-NEXT: (local $1 (ref $A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $1
+ ;; CHECK-NEXT: (ref.cast_static $A
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (call $get)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $not-past-set (param $x (ref data))
+ (drop
+ (ref.cast_static $A
+ (local.get $x)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ ;; The local.set in the middle stops us from helping the last get.
+ (local.set $x
+ (call $get)
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+
+ ;; CHECK: (func $best (type $ref|data|_=>_none) (param $x (ref data))
+ ;; CHECK-NEXT: (local $1 (ref $A))
+ ;; CHECK-NEXT: (local $2 (ref $B))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $1
+ ;; CHECK-NEXT: (ref.cast_static $A
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $2
+ ;; CHECK-NEXT: (ref.cast_static $B
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $best (param $x (ref data))
+ (drop
+ (ref.cast_static $A
+ (local.get $x)
+ )
+ )
+ ;; Here we should use $A.
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (ref.cast_static $B
+ (local.get $x)
+ )
+ )
+ ;; Here we should use $B, which is even better.
+ (drop
+ (local.get $x)
+ )
+ )
+
+ ;; CHECK: (func $best-2 (type $ref|data|_=>_none) (param $x (ref data))
+ ;; CHECK-NEXT: (local $1 (ref $B))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $1
+ ;; CHECK-NEXT: (ref.cast_static $B
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast_static $A
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $best-2 (param $x (ref data))
+ ;; As above, but with the casts reversed. Now we should use $B in both
+ ;; gets.
+ (drop
+ (ref.cast_static $B
+ (local.get $x)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (ref.cast_static $A
+ (local.get $x)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+
+ ;; CHECK: (func $fallthrough (type $ref|data|_=>_none) (param $x (ref data))
+ ;; CHECK-NEXT: (local $1 (ref $A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $1
+ ;; CHECK-NEXT: (ref.cast_static $A
+ ;; CHECK-NEXT: (block (result (ref data))
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $fallthrough (param $x (ref data))
+ (drop
+ (ref.cast_static $A
+ ;; We look through the block, and optimize.
+ (block (result (ref data))
+ (local.get $x)
+ )
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+
+ ;; CHECK: (func $past-basic-block (type $ref|data|_=>_none) (param $x (ref data))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast_static $A
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (return)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $past-basic-block (param $x (ref data))
+ (drop
+ (ref.cast_static $A
+ (local.get $x)
+ )
+ )
+ ;; The if means the later get is in another basic block. We do not handle
+ ;; this atm.
+ (if
+ (i32.const 0)
+ (return)
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+
+ ;; CHECK: (func $multiple (type $ref|data|_ref|data|_=>_none) (param $x (ref data)) (param $y (ref data))
+ ;; CHECK-NEXT: (local $a (ref data))
+ ;; CHECK-NEXT: (local $b (ref data))
+ ;; CHECK-NEXT: (local $4 (ref $A))
+ ;; CHECK-NEXT: (local $5 (ref $A))
+ ;; CHECK-NEXT: (local.set $a
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $b
+ ;; CHECK-NEXT: (local.get $y)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $4
+ ;; CHECK-NEXT: (ref.cast_static $A
+ ;; CHECK-NEXT: (local.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $5
+ ;; CHECK-NEXT: (ref.cast_static $A
+ ;; CHECK-NEXT: (local.get $b)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $4)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $5)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $b
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $4)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $b)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $multiple (param $x (ref data)) (param $y (ref data))
+ (local $a (ref data))
+ (local $b (ref data))
+ ;; Two different locals, with overlapping lives.
+ (local.set $a
+ (local.get $x)
+ )
+ (local.set $b
+ (local.get $y)
+ )
+ (drop
+ (ref.cast_static $A
+ (local.get $a)
+ )
+ )
+ (drop
+ (ref.cast_static $A
+ (local.get $b)
+ )
+ )
+ ;; These two can be optimized.
+ (drop
+ (local.get $a)
+ )
+ (drop
+ (local.get $b)
+ )
+ (local.set $b
+ (local.get $x)
+ )
+ ;; Now only the first can be, since $b changed.
+ (drop
+ (local.get $a)
+ )
+ (drop
+ (local.get $b)
+ )
+ )
+
+ ;; CHECK: (func $get (type $none_=>_ref|data|) (result (ref data))
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $get (result (ref data))
+ ;; Helper for the above.
+ (unreachable)
+ )
+)