| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. |
| |
| ;; RUN: foreach %s %t wasm-opt --cfp-reftest --closed-world -all -S -o - | filecheck %s |
| |
| ;; When a ref.get_desc can only read from two types, and those types have a |
| ;; constant field, we can select between those two values using a ref.test. |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $super (sub (descriptor $super.desc) (struct))) |
| (type $super (sub (descriptor $super.desc) (struct))) |
| ;; CHECK: (type $super.desc (sub (describes $super) (struct))) |
| (type $super.desc (sub (describes $super) (struct))) |
| |
| ;; CHECK: (type $A (sub $super (descriptor $A.desc) (struct))) |
| (type $A (sub $super (descriptor $A.desc) (struct))) |
| ;; CHECK: (type $A.desc (sub $super.desc (describes $A) (struct))) |
| (type $A.desc (sub $super.desc (describes $A) (struct))) |
| |
| ;; CHECK: (type $B (sub $super (descriptor $B.desc) (struct))) |
| (type $B (sub $super (descriptor $B.desc) (struct))) |
| ;; CHECK: (type $B.desc (sub $super.desc (describes $B) (struct))) |
| (type $B.desc (sub $super.desc (describes $B) (struct))) |
| ) |
| |
| |
| ;; CHECK: (type $6 (func (param (ref null $super) (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: (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 $6) (param $super (ref null $super)) (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: (global.get $B.desc) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (select (result (ref $super.desc)) |
| ;; CHECK-NEXT: (global.get $A.desc) |
| ;; CHECK-NEXT: (global.get $B.desc) |
| ;; CHECK-NEXT: (ref.test (ref $A) |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $super) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; 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: (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 $super (ref null $super)) (param $A (ref null $A)) (param $B (ref null $B)) |
| (drop |
| (struct.new_desc $A |
| (global.get $A.desc) |
| ) |
| ) |
| (drop |
| (struct.new_desc $B |
| (global.get $B.desc) |
| ) |
| ) |
| ;; We can optimize the read from the super here, using a ref.test. |
| (drop |
| (ref.get_desc $super |
| (local.get $super) |
| ) |
| ) |
| ;; These are optimizable even without ref.test. |
| (drop |
| (ref.get_desc $A |
| (local.get $A) |
| ) |
| ) |
| (drop |
| (ref.get_desc $B |
| (local.get $B) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $super (sub (descriptor $super.desc) (struct))) |
| (type $super (sub (descriptor $super.desc) (struct))) |
| ;; CHECK: (type $super.desc (sub (describes $super) (struct))) |
| (type $super.desc (sub (describes $super) (struct))) |
| |
| ;; CHECK: (type $func (func (param i32) (result i32))) |
| (type $func (func (param i32) (result i32))) |
| |
| ;; CHECK: (type $sub (sub $super (descriptor $sub.desc) (struct))) |
| (type $sub (sub $super (descriptor $sub.desc) (struct))) |
| ;; CHECK: (type $sub.desc (sub $super.desc (describes $sub) (struct))) |
| (type $sub.desc (sub $super.desc (describes $sub) (struct))) |
| ) |
| |
| ;; CHECK: (type $5 (func (result (ref (exact $super.desc))))) |
| |
| ;; CHECK: (global $A (ref (exact $super.desc)) (struct.new_default $super.desc)) |
| (global $A (ref (exact $super.desc)) (struct.new $super.desc)) |
| |
| ;; CHECK: (global $B (ref (exact $sub.desc)) (struct.new_default $sub.desc)) |
| (global $B (ref (exact $sub.desc)) (struct.new $sub.desc)) |
| |
| ;; CHECK: (func $test (type $5) (result (ref (exact $super.desc))) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new_default_desc $super |
| ;; CHECK-NEXT: (global.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new_default_desc $sub |
| ;; CHECK-NEXT: (global.get $B) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result (ref (exact $super.desc))) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (block (result nullref) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (global.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (result (ref (exact $super.desc))) |
| (drop |
| (struct.new_default_desc $super |
| (global.get $A) |
| ) |
| ) |
| (drop |
| (struct.new_default_desc $sub |
| (global.get $B) |
| ) |
| ) |
| ;; We read from an exact $super here, so the type of the ref.get_desc is |
| ;; exact as well. If we ignore that in the optimization, we might think that |
| ;; the two struct.news before us are two possible values, one from $super and |
| ;; one from $sub, and if we emitted a ref.test between those values, we'd get |
| ;; a non-exact value that does not validate. |
| ;; |
| ;; Instead, we should look only at $super itself, and optimize to $A. |
| (ref.get_desc $super |
| (block (result (ref null (exact $super))) |
| (ref.null $super) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $func (type $func) (param $0 i32) (result i32) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| (func $func (type $func) (param $0 i32) (result i32) |
| (i32.const 42) |
| ) |
| ) |