| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. |
| ;; RUN: foreach %s %t wasm-opt -all --gufa --closed-world -S -o - | filecheck %s |
| |
| ;; Function subtyping in results. |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $super (sub (func (result i32)))) |
| (type $super (sub (func (result i32)))) |
| ;; CHECK: (type $sub (sub $super (func (result i32)))) |
| (type $sub (sub $super (func (result i32)))) |
| ) |
| |
| ;; CHECK: (type $2 (func (param i32))) |
| |
| ;; CHECK: (table $table 3 funcref) |
| (table $table i32 3 funcref) |
| ;; CHECK: (elem $elem (i32.const 0) $super $sub) |
| (elem $elem (i32.const 0) $super $sub) |
| |
| ;; CHECK: (export "export" (func $export)) |
| |
| ;; CHECK: (func $super (type $super) (result i32) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| (func $super (type $super) (result i32) |
| (i32.const 42) |
| ) |
| |
| ;; CHECK: (func $sub (type $sub) (result i32) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| (func $sub (type $sub) (result i32) |
| (i32.const 1337) |
| ) |
| |
| ;; CHECK: (func $export (type $2) (param $x i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (call_indirect $table (type $super) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (call_indirect $table (type $sub) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $export (export "export") (param $x i32) |
| ;; Call using the supertype. We might be calling the subtype too, so we can't |
| ;; infer the result. |
| (drop |
| (call_indirect $table (type $super) |
| (local.get $x) |
| ) |
| ) |
| ;; Call using the subtype. Now we can infer the return value. (It might also |
| ;; trap, of course, if the table slot is null, but that does not prevent us |
| ;; from setting the result of 1 here, which is only used if it does not trap.) |
| (drop |
| (call_indirect $table (type $sub) |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; As above, but now the functions return the same. |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $super (sub (func (result i32)))) |
| (type $super (sub (func (result i32)))) |
| ;; CHECK: (type $sub (sub $super (func (result i32)))) |
| (type $sub (sub $super (func (result i32)))) |
| ) |
| |
| ;; CHECK: (type $2 (func (param i32))) |
| |
| ;; CHECK: (table $table 3 funcref) |
| (table $table i32 3 funcref) |
| ;; CHECK: (elem $elem (i32.const 0) $super $sub) |
| (elem $elem (i32.const 0) $super $sub) |
| |
| ;; CHECK: (export "export" (func $export)) |
| |
| ;; CHECK: (func $super (type $super) (result i32) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| (func $super (type $super) (result i32) |
| (i32.const 42) |
| ) |
| |
| ;; CHECK: (func $sub (type $sub) (result i32) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| (func $sub (type $sub) (result i32) |
| (i32.const 42) ;; this changed |
| ) |
| |
| ;; CHECK: (func $export (type $2) (param $x i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (call_indirect $table (type $super) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (call_indirect $table (type $sub) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $export (export "export") (param $x i32) |
| (drop |
| (call_indirect $table (type $super) |
| (local.get $x) |
| ) |
| ) |
| ;; They both return 42 now, so we can optimize this. |
| (drop |
| (call_indirect $table (type $sub) |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Function subtyping in params. |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $super (sub (func (param i32)))) |
| (type $super (sub (func (param i32)))) |
| ;; CHECK: (type $sub (sub $super (func (param i32)))) |
| (type $sub (sub $super (func (param i32)))) |
| ) |
| |
| ;; CHECK: (type $2 (func (param i32))) |
| |
| ;; CHECK: (table $table 3 funcref) |
| (table $table i32 3 funcref) |
| ;; CHECK: (elem $elem (i32.const 0) $super $sub) |
| (elem $elem (i32.const 0) $super $sub) |
| |
| ;; CHECK: (export "export" (func $export)) |
| |
| ;; CHECK: (func $super (type $super) (param $x i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $super (type $super) (param $x i32) |
| ;; This type is called with only one possible value, 42. |
| (drop |
| (local.get $x) |
| ) |
| ) |
| |
| ;; CHECK: (func $sub (type $sub) (param $x i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $sub (type $sub) (param $x i32) |
| ;; We might be called with our supertype too, and the values differ, so we do |
| ;; not infer anything here. |
| (drop |
| (local.get $x) |
| ) |
| ) |
| |
| ;; CHECK: (func $export (type $2) (param $x i32) |
| ;; CHECK-NEXT: (call_indirect $table (type $super) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call_indirect $table (type $sub) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $export (export "export") (param $x i32) |
| ;; Call using the supertype. |
| (call_indirect $table (type $super) |
| (i32.const 42) |
| (local.get $x) |
| ) |
| ;; Call using the subtype. |
| (call_indirect $table (type $sub) |
| (i32.const 1337) |
| (local.get $x) |
| ) |
| ) |
| ) |
| |
| ;; As above, but now send the same thing to both types. |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $super (sub (func (param i32)))) |
| (type $super (sub (func (param i32)))) |
| ;; CHECK: (type $sub (sub $super (func (param i32)))) |
| (type $sub (sub $super (func (param i32)))) |
| ) |
| |
| ;; CHECK: (type $2 (func (param i32))) |
| |
| ;; CHECK: (table $table 3 funcref) |
| (table $table i32 3 funcref) |
| ;; CHECK: (elem $elem (i32.const 0) $super $sub) |
| (elem $elem (i32.const 0) $super $sub) |
| |
| ;; CHECK: (export "export" (func $export)) |
| |
| ;; CHECK: (func $super (type $super) (param $x i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $super (type $super) (param $x i32) |
| (drop |
| (local.get $x) |
| ) |
| ) |
| |
| ;; CHECK: (func $sub (type $sub) (param $x i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $sub (type $sub) (param $x i32) |
| ;; We can now infer 42 here. |
| (drop |
| (local.get $x) |
| ) |
| ) |
| |
| ;; CHECK: (func $export (type $2) (param $x i32) |
| ;; CHECK-NEXT: (call_indirect $table (type $super) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call_indirect $table (type $sub) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $export (export "export") (param $x i32) |
| (call_indirect $table (type $super) |
| (i32.const 42) |
| (local.get $x) |
| ) |
| (call_indirect $table (type $sub) |
| (i32.const 42) ;; this changed |
| (local.get $x) |
| ) |
| ) |
| ) |
| |
| ;; CallRef, where exactness matters. |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $super (sub (func (result i32)))) |
| (type $super (sub (func (result i32)))) |
| ;; CHECK: (type $sub (sub $super (func (result i32)))) |
| (type $sub (sub $super (func (result i32)))) |
| ) |
| |
| ;; CHECK: (type $2 (func (param (ref $super) (ref (exact $super)) (ref $sub) (ref (exact $sub))))) |
| |
| ;; CHECK: (table $table 3 funcref) |
| (table $table i32 3 funcref) |
| ;; CHECK: (elem $elem (i32.const 0) $super $sub) |
| (elem $elem (i32.const 0) $super $sub) |
| |
| ;; CHECK: (export "export" (func $export)) |
| |
| ;; CHECK: (func $super (type $super) (result i32) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| (func $super (type $super) (result i32) |
| (i32.const 42) |
| ) |
| |
| ;; CHECK: (func $sub (type $sub) (result i32) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| (func $sub (type $sub) (result i32) |
| (i32.const 1337) |
| ) |
| |
| ;; CHECK: (func $export (type $2) (param $super (ref $super)) (param $super-exact (ref (exact $super))) (param $sub (ref $sub)) (param $sub-exact (ref (exact $sub))) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (call_ref $super |
| ;; CHECK-NEXT: (local.get $super) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (call_ref $super |
| ;; CHECK-NEXT: (local.get $super-exact) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (call_ref $sub |
| ;; CHECK-NEXT: (local.get $sub) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (call_ref $sub |
| ;; CHECK-NEXT: (local.get $sub-exact) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $export (export "export") (param $super (ref $super)) (param $super-exact (ref (exact $super))) |
| (param $sub (ref $sub)) (param $sub-exact (ref (exact $sub))) |
| ;; This could call anything, so we infer nothing. |
| (drop |
| (call_ref $super |
| (local.get $super) |
| ) |
| ) |
| ;; This calls exactly super, so it returns 42. |
| (drop |
| (call_ref $super |
| (local.get $super-exact) |
| ) |
| ) |
| ;; Called exactly or not, we can infer 1337 here. |
| (drop |
| (call_ref $sub |
| (local.get $sub) |
| ) |
| ) |
| (drop |
| (call_ref $sub |
| (local.get $sub-exact) |
| ) |
| ) |
| ) |
| ) |
| |