| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. |
| ;; RUN: foreach %s %t wasm-opt --nominal --signature-refining -all -S -o - | filecheck %s |
| |
| (module |
| ;; $func is defined with an anyref parameter but always called with a $struct, |
| ;; and we can specialize the heap type to that. That will both update the |
| ;; heap type's definition as well as the types of the parameters as printed |
| ;; on the function (which are derived from the heap type). |
| |
| ;; CHECK: (type $struct (struct_subtype data)) |
| (type $struct (struct_subtype data)) |
| |
| ;; CHECK: (type $sig (func_subtype (param (ref $struct)) func)) |
| (type $sig (func_subtype (param anyref) func)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (func $func (type $sig) (param $x (ref $struct)) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (call $func |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (call $func |
| (struct.new $struct) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; As above, but the call is via call_ref. |
| |
| ;; CHECK: (type $sig (func_subtype (param (ref $struct)) func)) |
| |
| ;; CHECK: (type $struct (struct_subtype data)) |
| (type $struct (struct_subtype data)) |
| |
| (type $sig (func_subtype (param anyref) func)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (elem declare func $func) |
| |
| ;; CHECK: (func $func (type $sig) (param $x (ref $struct)) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (call_ref $sig |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: (ref.func $func) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (call_ref |
| (struct.new $struct) |
| (ref.func $func) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; A combination of call types, and the LUB is affected by all of them: one |
| ;; call uses a nullable $struct, the other a non-nullable dataref, so the LUB |
| ;; is a nullable dataref. |
| |
| ;; CHECK: (type $sig (func_subtype (param dataref) func)) |
| |
| ;; CHECK: (type $struct (struct_subtype data)) |
| (type $struct (struct_subtype data)) |
| |
| (type $sig (func_subtype (param anyref) func)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (elem declare func $func) |
| |
| ;; CHECK: (func $func (type $sig) (param $x dataref) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (local $struct (ref null $struct)) |
| ;; CHECK-NEXT: (call $func |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call_ref $sig |
| ;; CHECK-NEXT: (ref.as_data |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.func $func) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (local $struct (ref null $struct)) |
| (call $func |
| ;; Use a local to avoid a ref.null being updated. |
| (local.get $struct) |
| ) |
| (call_ref |
| (ref.as_data |
| (struct.new $struct) |
| ) |
| (ref.func $func) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; Multiple functions with the same heap type. Again, the LUB is in the |
| ;; middle, this time the parent $struct and not a subtype. |
| |
| ;; CHECK: (type $sig (func_subtype (param (ref $struct)) func)) |
| (type $sig (func_subtype (param anyref) func)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (type $struct (struct_subtype data)) |
| |
| ;; CHECK: (type $struct-sub1 (struct_subtype $struct)) |
| (type $struct-sub1 (struct_subtype $struct)) |
| |
| ;; CHECK: (type $struct-sub2 (struct_subtype $struct)) |
| (type $struct-sub2 (struct_subtype $struct)) |
| |
| (type $struct (struct_subtype data)) |
| |
| ;; CHECK: (func $func-1 (type $sig) (param $x (ref $struct)) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func-1 (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $func-2 (type $sig) (param $x (ref $struct)) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func-2 (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (call $func-1 |
| ;; CHECK-NEXT: (struct.new_default $struct-sub1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $func-2 |
| ;; CHECK-NEXT: (struct.new_default $struct-sub2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (call $func-1 |
| (struct.new $struct-sub1) |
| ) |
| (call $func-2 |
| (struct.new $struct-sub2) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; As above, but now only one of the functions is called. The other is still |
| ;; updated, though, as they share a heap type. |
| |
| ;; CHECK: (type $sig (func_subtype (param (ref $struct)) func)) |
| (type $sig (func_subtype (param anyref) func)) |
| |
| ;; CHECK: (type $struct (struct_subtype data)) |
| (type $struct (struct_subtype data)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (func $func-1 (type $sig) (param $x (ref $struct)) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func-1 (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $func-2 (type $sig) (param $x (ref $struct)) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func-2 (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (call $func-1 |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (call $func-1 |
| (struct.new $struct) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; Define a field in the struct of the signature type that will be updated, |
| ;; to check for proper validation after the update. |
| |
| ;; CHECK: (type $sig (func_subtype (param (ref $struct) (ref $sig)) func)) |
| (type $sig (func_subtype (param anyref funcref) func)) |
| |
| ;; CHECK: (type $struct (struct_subtype (field (ref $sig)) data)) |
| (type $struct (struct_subtype (field (ref $sig)) data)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (elem declare func $func) |
| |
| ;; CHECK: (func $func (type $sig) (param $x (ref $struct)) (param $f (ref $sig)) |
| ;; CHECK-NEXT: (local $temp (ref null $sig)) |
| ;; CHECK-NEXT: (local $3 funcref) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (local.get $f) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (local.get $temp) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $func (type $sig) (param $x anyref) (param $f funcref) |
| ;; Define a local of the signature type that is updated. |
| (local $temp (ref null $sig)) |
| ;; Do a local.get of the param, to verify its type is valid. |
| (drop |
| (local.get $x) |
| ) |
| ;; Copy from a funcref local to the formerly funcref param to verify their |
| ;; types are still compatible after the update. Note that we will need to |
| ;; add a fixup local here, as $f's new type becomes too specific to be |
| ;; assigned the value here. |
| (local.set $f |
| (local.get $temp) |
| ) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (call $func |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (ref.func $func) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.func $func) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (call $func |
| (struct.new $struct |
| (ref.func $func) |
| ) |
| (ref.func $func) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; An unreachable value does not prevent optimization: we will update the |
| ;; param to be $struct. |
| |
| ;; CHECK: (type $sig (func_subtype (param (ref $struct)) func)) |
| |
| ;; CHECK: (type $struct (struct_subtype data)) |
| (type $struct (struct_subtype data)) |
| |
| (type $sig (func_subtype (param anyref) func)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (elem declare func $func) |
| |
| ;; CHECK: (func $func (type $sig) (param $x (ref $struct)) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (call $func |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call_ref $sig |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (ref.func $func) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (call $func |
| (struct.new $struct) |
| ) |
| (call_ref |
| (unreachable) |
| (ref.func $func) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; When we have only unreachable values, there is nothing to optimize, and we |
| ;; should not crash. |
| |
| (type $struct (struct_subtype data)) |
| |
| ;; CHECK: (type $sig (func_subtype (param anyref) func)) |
| (type $sig (func_subtype (param anyref) func)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (elem declare func $func) |
| |
| ;; CHECK: (func $func (type $sig) (param $x anyref) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (call_ref $sig |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (ref.func $func) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (call_ref |
| (unreachable) |
| (ref.func $func) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; When we have no calls, there is nothing to optimize, and we should not |
| ;; crash. |
| |
| (type $struct (struct_subtype data)) |
| |
| ;; CHECK: (type $sig (func_subtype (param anyref) func)) |
| (type $sig (func_subtype (param anyref) func)) |
| |
| ;; CHECK: (func $func (type $sig) (param $x anyref) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func (type $sig) (param $x anyref) |
| ) |
| ) |
| |
| (module |
| ;; Test multiple fields in multiple types. |
| ;; CHECK: (type $struct (struct_subtype data)) |
| (type $struct (struct_subtype data)) |
| |
| ;; CHECK: (type $sig-2 (func_subtype (param eqref (ref $struct)) func)) |
| |
| ;; CHECK: (type $sig-1 (func_subtype (param dataref anyref) func)) |
| (type $sig-1 (func_subtype (param anyref) (param anyref) func)) |
| (type $sig-2 (func_subtype (param anyref) (param anyref) func)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (elem declare func $func-2) |
| |
| ;; CHECK: (func $func-1 (type $sig-1) (param $x dataref) (param $y anyref) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func-1 (type $sig-1) (param $x anyref) (param $y anyref) |
| ) |
| |
| ;; CHECK: (func $func-2 (type $sig-2) (param $x eqref) (param $y (ref $struct)) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func-2 (type $sig-2) (param $x anyref) (param $y anyref) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (local $any anyref) |
| ;; CHECK-NEXT: (local $data dataref) |
| ;; CHECK-NEXT: (local $i31 i31ref) |
| ;; CHECK-NEXT: (call $func-1 |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: (local.get $data) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $func-1 |
| ;; CHECK-NEXT: (local.get $data) |
| ;; CHECK-NEXT: (local.get $any) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $func-2 |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call_ref $sig-2 |
| ;; CHECK-NEXT: (local.get $i31) |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: (ref.func $func-2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (local $any (ref null any)) |
| (local $data (ref null data)) |
| (local $i31 (ref null i31)) |
| |
| (call $func-1 |
| (struct.new $struct) |
| (local.get $data) |
| ) |
| (call $func-1 |
| (local.get $data) |
| (local.get $any) |
| ) |
| (call $func-2 |
| (struct.new $struct) |
| (struct.new $struct) |
| ) |
| (call_ref |
| (local.get $i31) |
| (struct.new $struct) |
| (ref.func $func-2) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; The presence of a table prevents us from doing any optimizations. |
| |
| ;; CHECK: (type $sig (func_subtype (param anyref) func)) |
| (type $sig (func_subtype (param anyref) func)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (type $struct (struct_subtype data)) |
| (type $struct (struct_subtype data)) |
| |
| (table 1 1 anyref) |
| |
| ;; CHECK: (table $0 1 1 anyref) |
| |
| ;; CHECK: (func $func (type $sig) (param $x anyref) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (call $func |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (call $func |
| (struct.new $struct) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; Pass a null in one call to the function. The null can be updated which |
| ;; allows us to refine (but the new type must be nullable). |
| |
| ;; CHECK: (type $struct (struct_subtype data)) |
| |
| ;; CHECK: (type $sig (func_subtype (param (ref null $struct)) func)) |
| (type $sig (func_subtype (param anyref) func)) |
| |
| (type $struct (struct_subtype data)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (func $func (type $sig) (param $x (ref null $struct)) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (call $func |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $func |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (call $func |
| (struct.new $struct) |
| ) |
| (call $func |
| (ref.null data) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $struct (struct_subtype data)) |
| (type $struct (struct_subtype data)) |
| |
| ;; This signature has a single function using it, which returns a more |
| ;; refined type, and we can refine to that. |
| ;; CHECK: (type $sig-can-refine (func_subtype (result (ref $struct)) func)) |
| (type $sig-can-refine (func_subtype (result anyref) func)) |
| |
| ;; Also a single function, but no refinement is possible. |
| ;; CHECK: (type $sig-cannot-refine (func_subtype (result anyref) func)) |
| (type $sig-cannot-refine (func_subtype (result anyref) func)) |
| |
| ;; The single function never returns, so no refinement is possible. |
| ;; CHECK: (type $sig-unreachable (func_subtype (result anyref) func)) |
| (type $sig-unreachable (func_subtype (result anyref) func)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (elem declare func $func-can-refine) |
| |
| ;; CHECK: (func $func-can-refine (type $sig-can-refine) (result (ref $struct)) |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| (func $func-can-refine (type $sig-can-refine) (result anyref) |
| (struct.new $struct) |
| ) |
| |
| ;; CHECK: (func $func-cannot-refine (type $sig-cannot-refine) (result anyref) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| (func $func-cannot-refine (type $sig-cannot-refine) (result anyref) |
| (ref.null any) |
| ) |
| |
| ;; CHECK: (func $func-unreachable (type $sig-unreachable) (result anyref) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| (func $func-unreachable (type $sig-unreachable) (result anyref) |
| (unreachable) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (if (result (ref $struct)) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: (call $func-can-refine) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (if (result (ref $struct)) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: (call_ref $sig-can-refine |
| ;; CHECK-NEXT: (ref.func $func-can-refine) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| ;; Add a call to see that we update call types properly. |
| ;; Put the call in an if so the refinalize will update the if type and get |
| ;; printed out conveniently. |
| (drop |
| (if (result anyref) |
| (i32.const 1) |
| (call $func-can-refine) |
| (unreachable) |
| ) |
| ) |
| ;; The same with a call_ref. |
| (drop |
| (if (result anyref) |
| (i32.const 1) |
| (call_ref |
| (ref.func $func-can-refine) |
| ) |
| (unreachable) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $sig (func_subtype (result (ref null $struct)) func)) |
| |
| ;; CHECK: (type $struct (struct_subtype data)) |
| (type $struct (struct_subtype data)) |
| |
| ;; This signature has multiple functions using it, and some of them have nulls |
| ;; which should be updated when we refine. |
| (type $sig (func_subtype (result anyref) func)) |
| |
| ;; CHECK: (func $func-1 (type $sig) (result (ref null $struct)) |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| (func $func-1 (type $sig) (result anyref) |
| (struct.new $struct) |
| ) |
| |
| ;; CHECK: (func $func-2 (type $sig) (result (ref null $struct)) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| (func $func-2 (type $sig) (result anyref) |
| (ref.null any) |
| ) |
| |
| ;; CHECK: (func $func-3 (type $sig) (result (ref null $struct)) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| (func $func-3 (type $sig) (result anyref) |
| (ref.null eq) |
| ) |
| |
| ;; CHECK: (func $func-4 (type $sig) (result (ref null $struct)) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| (func $func-4 (type $sig) (result anyref) |
| (if |
| (i32.const 1) |
| (return |
| (ref.null any) |
| ) |
| ) |
| (unreachable) |
| ) |
| ) |
| |
| ;; Exports prevent optimization, so $func's type will not change here. |
| (module |
| ;; CHECK: (type $sig (func_subtype (param anyref) func)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (type $struct (struct_subtype data)) |
| (type $struct (struct_subtype data)) |
| |
| (type $sig (func_subtype (param anyref) func)) |
| |
| ;; CHECK: (export "prevent-opts" (func $func)) |
| |
| ;; CHECK: (func $func (type $sig) (param $x anyref) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $func (export "prevent-opts") (type $sig) (param $x anyref) |
| ) |
| |
| ;; CHECK: (func $caller (type $none_=>_none) |
| ;; CHECK-NEXT: (call $func |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $caller |
| (call $func |
| (struct.new $struct) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $A (func_subtype (param i32) func)) |
| (type $A (func_subtype (param i32) func)) |
| ;; CHECK: (type $B (func_subtype (param i32) $A)) |
| (type $B (func_subtype (param i32) $A)) |
| |
| ;; CHECK: (func $bar (type $B) (param $x i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $bar (type $B) (param $x i32) |
| ;; The parameter to this function can be pruned. But while doing so we must |
| ;; properly preserve the subtyping of $B from $A, which means we cannot just |
| ;; remove it - we'd need to remove it from $A as well, which we don't |
| ;; attempt to do in the pass atm. So we do not optimize here. |
| (nop) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $ref|${}|_i32_=>_none (func_subtype (param (ref ${}) i32) func)) |
| |
| ;; CHECK: (type ${} (struct_subtype data)) |
| (type ${} (struct_subtype data)) |
| |
| ;; CHECK: (func $foo (type $ref|${}|_i32_=>_none) (param $ref (ref ${})) (param $i32 i32) |
| ;; CHECK-NEXT: (local $2 eqref) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (local.get $ref) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (call $foo |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $foo (param $ref eqref) (param $i32 i32) |
| (call $foo |
| ;; The only reference to the ${} type is in this block signature. Even |
| ;; this will go away in the internal ReFinalize (which makes the block |
| ;; type unreachable). |
| (block (result (ref ${})) |
| (unreachable) |
| ) |
| (i32.const 0) |
| ) |
| ;; Write something of type eqref into $ref. When we refine the type of the |
| ;; parameter from eqref to ${} we must do something here, as we can no |
| ;; longer just write this (ref.null eq) into a parameter of the more |
| ;; refined type. While doing so, we must not be confused by the fact that |
| ;; the only mention of ${} in the original module gets removed during our |
| ;; processing, as mentioned in the earlier comment. This is a regression |
| ;; test for a crash because of that. |
| (local.set $ref |
| (ref.null eq) |
| ) |
| ) |
| ) |
| |
| ;; Do not modify the types used on imported functions (until the spec and VM |
| ;; support becomes stable). |
| (module |
| ;; CHECK: (type $dataref_=>_none (func_subtype (param dataref) func)) |
| |
| ;; CHECK: (type $none_=>_none (func_subtype func)) |
| |
| ;; CHECK: (type $struct (struct_subtype data)) |
| (type $struct (struct_subtype data)) |
| |
| ;; CHECK: (import "a" "b" (func $import (param dataref))) |
| (import "a" "b" (func $import (param (ref null data)))) |
| |
| ;; CHECK: (func $test (type $none_=>_none) |
| ;; CHECK-NEXT: (call $import |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test |
| (call $import |
| (struct.new $struct) |
| ) |
| ) |
| ) |