| ;; 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) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Function subtyping in a return_call_indirect. |
| (module |
| (rec |
| ;; CHECK: (type $0 (func (result i32))) |
| |
| ;; 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: (table $table 44 funcref) |
| (table $table 44 funcref) |
| |
| ;; CHECK: (elem $table (i32.const 0) $sub) |
| (elem $table (i32.const 0) $sub) |
| |
| ;; CHECK: (export "call" (func $call)) |
| |
| ;; CHECK: (export "call_ref" (func $call_ref)) |
| |
| ;; CHECK: (func $sub (type $sub) (result i32) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| (func $sub (type $sub) (result i32) |
| (i32.const 42) |
| ) |
| |
| ;; CHECK: (func $do_call (type $0) (result i32) |
| ;; CHECK-NEXT: (return_call_indirect $table (type $super) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $do_call (result i32) |
| ;; Call $sub using type $super. The result, 42, is returned from this |
| ;; function, even though we don't infer an explicit value here (the return- |
| ;; call is unreachable anyhow, and we don't replace an unreachable with a |
| ;; concrete type). |
| (return_call_indirect $table (type $super) |
| (i32.const 0) |
| ) |
| ) |
| |
| ;; CHECK: (func $call (type $0) (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (call $do_call) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| (func $call (export "call") (result i32) |
| ;; We can infer 42 here. |
| (call $do_call) |
| ) |
| |
| ;; CHECK: (func $do_call_ref (type $0) (result i32) |
| ;; CHECK-NEXT: (return_call_ref $sub |
| ;; CHECK-NEXT: (ref.func $sub) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $do_call_ref (result i32) |
| ;; As above, with a call_ref. |
| (return_call_ref $super |
| (ref.func $sub) |
| ) |
| ) |
| |
| ;; CHECK: (func $call_ref (type $0) (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (call $do_call_ref) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| (func $call_ref (export "call_ref") (result i32) |
| ;; We can infer 42 here. |
| (call $do_call_ref) |
| ) |
| ) |
| |