blob: 9f0010c4986cfd587ca00dfc01d0d80e8666a4ae [file] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --cfp --closed-world -all -S -o - | filecheck %s
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (descriptor $B) (struct))
(type $A (descriptor $B) (struct))
;; CHECK: (type $B (describes $A) (struct))
(type $B (describes $A) (struct))
)
(type $struct (struct i32))
;; CHECK: (type $2 (func))
;; CHECK: (func $impossible-get (type $2)
;; CHECK-NEXT: (local $A (ref null $A))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $impossible-get (local $A (ref null $A))
(drop
;; This type is never created, so a get is impossible, and we can trap.
(ref.get_desc $A
(local.get $A)
)
)
)
)
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (descriptor $B) (struct))
(type $A (descriptor $B) (struct))
;; CHECK: (type $B (describes $A) (struct))
(type $B (describes $A) (struct))
)
;; CHECK: (type $2 (func (param (ref null $A))))
;; CHECK: (global $B (ref (exact $B)) (struct.new_default $B))
(global $B (ref (exact $B)) (struct.new $B))
;; CHECK: (func $test (type $2) (param $A (ref null $A))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default_desc $A
;; CHECK-NEXT: (global.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $B)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $A (ref null $A))
;; Only one creation of $A, so we can infer the descriptor that is read
;; below.
(drop
(struct.new_desc $A
(global.get $B)
)
)
(drop
(ref.get_desc $A
(local.get $A)
)
)
)
)
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (descriptor $B) (struct))
(type $A (descriptor $B) (struct))
;; CHECK: (type $B (describes $A) (struct))
(type $B (describes $A) (struct))
)
;; CHECK: (type $2 (func (param (ref null $A))))
;; CHECK: (global $B (ref (exact $B)) (struct.new_default $B))
(global $B (ref (exact $B)) (struct.new $B))
;; CHECK: (func $test (type $2) (param $A (ref null $A))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default_desc $A
;; CHECK-NEXT: (global.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default_desc $A
;; CHECK-NEXT: (struct.new_default $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $A
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $A (ref null $A))
;; As above, but with another struct.new here, with another value, so we
;; cannot infer.
(drop
(struct.new_desc $A
(global.get $B)
)
)
(drop
(struct.new_desc $A
(struct.new $B)
)
)
(drop
(ref.get_desc $A
(local.get $A)
)
)
)
)
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (descriptor $B) (struct))
(type $A (descriptor $B) (struct))
;; CHECK: (type $B (describes $A) (struct))
(type $B (describes $A) (struct))
)
;; CHECK: (type $2 (func (param (ref null $A))))
;; CHECK: (global $B (ref (exact $B)) (struct.new_default $B))
(global $B (ref (exact $B)) (struct.new $B))
;; CHECK: (func $test (type $2) (param $A (ref null $A))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default_desc $A
;; CHECK-NEXT: (global.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default_desc $A
;; CHECK-NEXT: (global.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $B)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $A (ref null $A))
;; As above, but both struct.news agree, so we can infer.
(drop
(struct.new_desc $A
(global.get $B)
)
)
(drop
(struct.new_desc $A
(global.get $B)
)
)
(drop
(ref.get_desc $A
(local.get $A)
)
)
)
)
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (descriptor $A.desc) (struct)))
(type $A (sub (descriptor $A.desc) (struct)))
;; CHECK: (type $A.desc (sub (describes $A) (struct)))
(type $A.desc (sub (describes $A) (struct)))
;; CHECK: (type $B (sub (descriptor $B.desc) (struct)))
(type $B (sub (descriptor $B.desc) (struct)))
;; CHECK: (type $B.desc (sub (describes $B) (struct)))
(type $B.desc (sub (describes $B) (struct)))
)
;; CHECK: (type $4 (func (param (ref null $A) (ref null $B))))
;; CHECK: (global $A.desc (ref (exact $A.desc)) (struct.new_default $A.desc))
(global $A.desc (ref (exact $A.desc)) (struct.new $A.desc))
;; CHECK: (func $test (type $4) (param $A (ref null $A)) (param $B (ref null $B))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default_desc $A
;; CHECK-NEXT: (global.get $A.desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default_desc $B
;; CHECK-NEXT: (struct.new_default $B.desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $A.desc)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $A.desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $B
;; CHECK-NEXT: (local.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $A (ref null $A)) (param $B (ref null $B))
;; We can optimize $A's read below, but not $B's.
(drop
(struct.new_desc $A
(global.get $A.desc)
)
)
(drop
(struct.new_desc $B
(struct.new $B.desc)
)
)
(drop
(ref.get_desc $A
(local.get $A)
)
)
(drop
(ref.get_desc $B
(local.get $B)
)
)
)
)
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (descriptor $A.desc) (struct)))
(type $A (sub (descriptor $A.desc) (struct)))
;; CHECK: (type $A.desc (sub (describes $A) (struct)))
(type $A.desc (sub (describes $A) (struct)))
;; CHECK: (type $B (sub $A (descriptor $B.desc) (struct)))
(type $B (sub $A (descriptor $B.desc) (struct)))
;; CHECK: (type $B.desc (sub $A.desc (describes $B) (struct)))
(type $B.desc (sub $A.desc (describes $B) (struct)))
)
;; CHECK: (type $4 (func (param (ref null $A) (ref null $B))))
;; CHECK: (global $A.desc (ref (exact $A.desc)) (struct.new_default $A.desc))
(global $A.desc (ref (exact $A.desc)) (struct.new $A.desc))
;; CHECK: (func $test (type $4) (param $A (ref null $A)) (param $B (ref null $B))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default_desc $A
;; CHECK-NEXT: (global.get $A.desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default_desc $B
;; CHECK-NEXT: (struct.new_default $B.desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $A
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $B
;; CHECK-NEXT: (local.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $A (ref null $A)) (param $B (ref null $B))
;; As above, but now $B is a subtype of $A, preventing $A's optimization.
(drop
(struct.new_desc $A
(global.get $A.desc)
)
)
(drop
(struct.new_desc $B
(struct.new $B.desc)
)
)
(drop
(ref.get_desc $A
(local.get $A)
)
)
(drop
(ref.get_desc $B
(local.get $B)
)
)
)
)
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (descriptor $A.desc) (struct)))
(type $A (sub (descriptor $A.desc) (struct)))
;; CHECK: (type $A.desc (sub (describes $A) (struct)))
(type $A.desc (sub (describes $A) (struct)))
;; CHECK: (type $B (sub $A (descriptor $B.desc) (struct)))
(type $B (sub $A (descriptor $B.desc) (struct)))
;; CHECK: (type $B.desc (sub $A.desc (describes $B) (struct)))
(type $B.desc (sub $A.desc (describes $B) (struct)))
)
;; CHECK: (type $4 (func (param (ref null $A) (ref null $B))))
;; CHECK: (global $B.desc (ref (exact $B.desc)) (struct.new_default $B.desc))
(global $B.desc (ref (exact $B.desc)) (struct.new $B.desc))
;; CHECK: (func $test (type $4) (param $A (ref null $A)) (param $B (ref null $B))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default_desc $A
;; CHECK-NEXT: (struct.new_default $A.desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default_desc $B
;; CHECK-NEXT: (global.get $B.desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $A
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $B.desc)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $B.desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $A (ref null $A)) (param $B (ref null $B))
;; As above, but the roles of $A and $B are reversed: now the subtype $B
;; can be optimized but not the supertype. The problem with $A does not stop
;; $B from being optimized.
(drop
(struct.new_desc $A
(struct.new $A.desc)
)
)
(drop
(struct.new_desc $B
(global.get $B.desc)
)
)
(drop
(ref.get_desc $A
(local.get $A)
)
)
(drop
(ref.get_desc $B
(local.get $B)
)
)
)
)
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (descriptor $B) (struct))
(type $A (descriptor $B) (struct))
;; CHECK: (type $B (describes $A) (struct))
(type $B (describes $A) (struct))
)
;; CHECK: (type $2 (func (result (ref (exact $B)))))
;; CHECK: (func $test (type $2) (result (ref (exact $B)))
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (struct.new_default_desc $A
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (result (ref (exact $B)))
;; We need to add a ref.as_non_null on the descriptor that is read, as the
;; function result is non-nullable.
(ref.get_desc $A
(struct.new_default_desc $A
(ref.null none)
)
)
)
)