| ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. |
| |
| ;; (remove-unused-names allows the pass to see that blocks flow values) |
| ;; RUN: wasm-opt %s -all --remove-unused-names --heap2local -S -o - | filecheck %s |
| ;; RUN: wasm-opt %s -all --remove-unused-names --heap2local --nominal -S -o - | filecheck %s --check-prefix=NOMNL |
| |
| (module |
| ;; CHECK: (type $struct.A (struct (field (mut i32) (mut f64)))) |
| ;; NOMNL: (type $struct.A (struct_subtype (field (mut i32) (mut f64)) data)) |
| (type $struct.A (struct (field (mut i32)) (field (mut f64)))) |
| |
| ;; CHECK: (type $struct.recursive (struct (field (mut (ref null $struct.recursive))))) |
| |
| ;; CHECK: (type $struct.nonnullable (struct (field (ref $struct.A)))) |
| |
| ;; CHECK: (type $struct.packed (struct (field (mut i8)))) |
| ;; NOMNL: (type $struct.recursive (struct_subtype (field (mut (ref null $struct.recursive))) data)) |
| |
| ;; NOMNL: (type $struct.nonnullable (struct_subtype (field (ref $struct.A)) data)) |
| |
| ;; NOMNL: (type $struct.packed (struct_subtype (field (mut i8)) data)) |
| (type $struct.packed (struct (field (mut i8)))) |
| |
| ;; CHECK: (type $struct.nondefaultable (struct (field (rtt $struct.A)))) |
| ;; NOMNL: (type $struct.nondefaultable (struct_subtype (field (rtt $struct.A)) data)) |
| (type $struct.nondefaultable (struct (field (rtt $struct.A)))) |
| |
| (type $struct.recursive (struct (field (mut (ref null $struct.recursive))))) |
| |
| (type $struct.nonnullable (struct (field (ref $struct.A)))) |
| |
| ;; CHECK: (func $simple |
| ;; CHECK-NEXT: (local $0 i32) |
| ;; CHECK-NEXT: (local $1 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $simple (type $func.0) |
| ;; NOMNL-NEXT: (local $0 i32) |
| ;; NOMNL-NEXT: (local $1 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $simple |
| ;; Other passes can remove such a trivial case of an unused allocation, but |
| ;; we still optimize it. |
| (drop |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $to-local |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $to-local (type $func.0) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $to-local |
| (local $ref (ref null $struct.A)) |
| ;; While set to a local, this allocation has no get/set operations. Other |
| ;; optimizations can remove it, but so can we, turning the set into a |
| ;; drop (and adding some unnecessary code to allocate the values, which we |
| ;; depend on other passes to remove). |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $one-get |
| ;; CHECK-NEXT: (local $0 i32) |
| ;; CHECK-NEXT: (local $1 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $one-get (type $func.0) |
| ;; NOMNL-NEXT: (local $0 i32) |
| ;; NOMNL-NEXT: (local $1 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $one-get |
| ;; An allocation followed by an immediate get of a field. This is a non- |
| ;; escaping allocation, with a use, so we can optimize it out. The |
| ;; allocation is dropped (letting later opts remove it), and the |
| ;; allocation's data is moved to locals: we write the initial value to the |
| ;; locals, and we read from the locals instead of the struct.get. |
| (drop |
| (struct.get $struct.A 0 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $one-get-b |
| ;; CHECK-NEXT: (local $0 i32) |
| ;; CHECK-NEXT: (local $1 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $one-get-b (type $func.0) |
| ;; NOMNL-NEXT: (local $0 i32) |
| ;; NOMNL-NEXT: (local $1 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $one-get-b |
| ;; Similar to the above, but using a different field index. |
| (drop |
| (struct.get $struct.A 1 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $one-set |
| ;; CHECK-NEXT: (local $0 i32) |
| ;; CHECK-NEXT: (local $1 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $one-set (type $func.0) |
| ;; NOMNL-NEXT: (local $0 i32) |
| ;; NOMNL-NEXT: (local $1 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (i32.const 1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $one-set |
| ;; A simple optimizable allocation only used in one set. |
| (struct.set $struct.A 0 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| (i32.const 1) |
| ) |
| ) |
| |
| ;; CHECK: (func $packed |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get_u $struct.packed 0 |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.packed |
| ;; CHECK-NEXT: (rtt.canon $struct.packed) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $packed (type $func.0) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (struct.get_u $struct.packed 0 |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.packed |
| ;; NOMNL-NEXT: (rtt.canon $struct.packed) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $packed |
| ;; We do not optimize packed structs yet. |
| (drop |
| (struct.get $struct.packed 0 |
| (struct.new_default_with_rtt $struct.packed |
| (rtt.canon $struct.packed) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $with-init-values |
| ;; CHECK-NEXT: (local $0 i32) |
| ;; CHECK-NEXT: (local $1 f64) |
| ;; CHECK-NEXT: (local $2 i32) |
| ;; CHECK-NEXT: (local $3 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (i32.const 2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (f64.const 3.14159) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $with-init-values (type $func.0) |
| ;; NOMNL-NEXT: (local $0 i32) |
| ;; NOMNL-NEXT: (local $1 f64) |
| ;; NOMNL-NEXT: (local $2 i32) |
| ;; NOMNL-NEXT: (local $3 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (i32.const 2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $3 |
| ;; NOMNL-NEXT: (f64.const 3.14159) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (local.get $3) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $with-init-values |
| ;; When we get values to initialize the struct with, assign them to the |
| ;; proper locals. |
| (drop |
| (struct.get $struct.A 0 |
| (struct.new_with_rtt $struct.A |
| (i32.const 2) |
| (f64.const 3.14159) |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $ignore-unreachable |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $ignore-unreachable (type $func.0) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block ;; (replaces something unreachable we can't emit) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block ;; (replaces something unreachable we can't emit) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (i32.const 2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (unreachable) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $ignore-unreachable |
| ;; An unreachable allocation is not worth trying to process; DCE should |
| ;; remove it. |
| (drop |
| (struct.get $struct.A 0 |
| (struct.new_with_rtt $struct.A |
| (i32.const 2) |
| (unreachable) |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $nondefaultable |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct.nondefaultable 0 |
| ;; CHECK-NEXT: (struct.new_with_rtt $struct.nondefaultable |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: (rtt.canon $struct.nondefaultable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $nondefaultable (type $func.0) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (struct.get $struct.nondefaultable 0 |
| ;; NOMNL-NEXT: (struct.new_with_rtt $struct.nondefaultable |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: (rtt.canon $struct.nondefaultable) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $nondefaultable |
| ;; We do not optimize structs with nondefaultable types that we cannot |
| ;; handle, like rtts. |
| (drop |
| (struct.get $struct.nondefaultable 0 |
| (struct.new_with_rtt $struct.nondefaultable |
| (rtt.canon $struct.A) |
| (rtt.canon $struct.nondefaultable) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $simple-one-local-set |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $simple-one-local-set (type $func.0) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $simple-one-local-set |
| (local $ref (ref null $struct.A)) |
| ;; A simple optimizable allocation only used in one set, and also stored |
| ;; to a local. The local.set should not prevent our optimization, and the |
| ;; local.set can be turned into a drop. |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (struct.set $struct.A 0 |
| (local.get $ref) |
| (i32.const 1) |
| ) |
| ) |
| |
| ;; CHECK: (func $simple-one-local-get (result f64) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $simple-one-local-get (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $simple-one-local-get (result f64) |
| (local $ref (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ;; A simple optimizable allocation only used in one get, via a local. |
| (struct.get $struct.A 1 |
| (local.get $ref) |
| ) |
| ) |
| |
| ;; CHECK: (func $send-ref (param $0 (ref null $struct.A)) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $send-ref (type $func.5) (param $0 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (nop) |
| ;; NOMNL-NEXT: ) |
| (func $send-ref (param (ref null $struct.A)) |
| ) |
| |
| ;; CHECK: (func $safe-to-drop (result f64) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $safe-to-drop (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $safe-to-drop (result f64) |
| (local $ref (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ;; An extra drop does not let the allocation escape. |
| (drop |
| (local.get $ref) |
| ) |
| (struct.get $struct.A 1 |
| (local.get $ref) |
| ) |
| ) |
| |
| ;; CHECK: (func $escape-via-call (result f64) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $ref |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $send-ref |
| ;; CHECK-NEXT: (local.get $ref) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $struct.A 1 |
| ;; CHECK-NEXT: (local.get $ref) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $escape-via-call (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $ref |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (call $send-ref |
| ;; NOMNL-NEXT: (local.get $ref) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (struct.get $struct.A 1 |
| ;; NOMNL-NEXT: (local.get $ref) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $escape-via-call (result f64) |
| (local $ref (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ;; The allocation escapes into a call. |
| (call $send-ref |
| (local.get $ref) |
| ) |
| (struct.get $struct.A 1 |
| (local.get $ref) |
| ) |
| ) |
| |
| ;; CHECK: (func $safe-to-drop-multiflow (result f64) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $safe-to-drop-multiflow (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $safe-to-drop-multiflow (result f64) |
| (local $ref (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ;; An extra drop + multiple flows through things do not stop us. |
| (drop |
| (block (result (ref null $struct.A)) |
| (block (result (ref null $struct.A)) |
| (loop (result (ref null $struct.A)) |
| (local.get $ref) |
| ) |
| ) |
| ) |
| ) |
| (struct.get $struct.A 1 |
| (local.get $ref) |
| ) |
| ) |
| |
| ;; CHECK: (func $escape-after-multiflow (result f64) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $ref |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $send-ref |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.get $ref) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $struct.A 1 |
| ;; CHECK-NEXT: (local.get $ref) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $escape-after-multiflow (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $ref |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (call $send-ref |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.get $ref) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (struct.get $struct.A 1 |
| ;; NOMNL-NEXT: (local.get $ref) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $escape-after-multiflow (result f64) |
| (local $ref (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ;; An escape after multiple flows. |
| (call $send-ref |
| (block (result (ref null $struct.A)) |
| (block (result (ref null $struct.A)) |
| (loop (result (ref null $struct.A)) |
| (local.get $ref) |
| ) |
| ) |
| ) |
| ) |
| (struct.get $struct.A 1 |
| (local.get $ref) |
| ) |
| ) |
| |
| ;; CHECK: (func $non-exclusive-set (result f64) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $ref |
| ;; CHECK-NEXT: (select (result (ref $struct.A)) |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $struct.A 1 |
| ;; CHECK-NEXT: (local.get $ref) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $non-exclusive-set (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $ref |
| ;; NOMNL-NEXT: (select (result (ref $struct.A)) |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (i32.const 1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (struct.get $struct.A 1 |
| ;; NOMNL-NEXT: (local.get $ref) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $non-exclusive-set (result f64) |
| (local $ref (ref null $struct.A)) |
| ;; A set that receives two different allocations, and so we should not try |
| ;; to optimize it. |
| (local.set $ref |
| (select |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| (i32.const 1) |
| ) |
| ) |
| (struct.get $struct.A 1 |
| (local.get $ref) |
| ) |
| ) |
| |
| ;; CHECK: (func $local-copies (result f64) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $local-copies (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $local-copies (result f64) |
| (local $ref (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ;; Copying our allocation through locals does not bother us. |
| (local.set $ref |
| (local.get $ref) |
| ) |
| (struct.get $struct.A 1 |
| (local.get $ref) |
| ) |
| ) |
| |
| ;; CHECK: (func $local-copies-2 |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $ref-2 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $2 i32) |
| ;; CHECK-NEXT: (local $3 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $local-copies-2 (type $func.0) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $ref-2 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $2 i32) |
| ;; NOMNL-NEXT: (local $3 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $3 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $3) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $local-copies-2 |
| (local $ref (ref null $struct.A)) |
| (local $ref-2 (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ;; Copying our allocation through locals does not bother us, even if it's |
| ;; another local. |
| (local.set $ref-2 |
| (local.get $ref) |
| ) |
| (drop |
| (struct.get $struct.A 0 |
| (local.get $ref) |
| ) |
| ) |
| (drop |
| (struct.get $struct.A 1 |
| (local.get $ref-2) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $local-copies-conditional (param $x i32) (result f64) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $2 i32) |
| ;; CHECK-NEXT: (local $3 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $local-copies-conditional (type $func.2) (param $x i32) (result f64) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $2 i32) |
| ;; NOMNL-NEXT: (local $3 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $3 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (if |
| ;; NOMNL-NEXT: (local.get $x) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $3) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $local-copies-conditional (param $x i32) (result f64) |
| (local $ref (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ;; Possibly copying our allocation through locals does not bother us. Note |
| ;; that as a result of this the final local.get has two sets that send it |
| ;; values, but we know they are both the same allocation. |
| (if (local.get $x) |
| (local.set $ref |
| (local.get $ref) |
| ) |
| ) |
| (struct.get $struct.A 1 |
| (local.get $ref) |
| ) |
| ) |
| |
| ;; CHECK: (func $block-value (result f64) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (call $send-ref |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $block-value (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (call $send-ref |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $block-value (result f64) |
| (local $ref (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ;; Returning our allocation from a block does not bother us. |
| (struct.get $struct.A 1 |
| (block (result (ref null $struct.A)) |
| ;; This call in the block should not bother us either. |
| (call $send-ref |
| (ref.null $struct.A) |
| ) |
| (local.get $ref) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $non-exclusive-get (param $x i32) (result f64) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $ref |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.set $ref |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $struct.A 1 |
| ;; CHECK-NEXT: (local.get $ref) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $non-exclusive-get (type $func.2) (param $x i32) (result f64) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $ref |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (if |
| ;; NOMNL-NEXT: (local.get $x) |
| ;; NOMNL-NEXT: (local.set $ref |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (struct.get $struct.A 1 |
| ;; NOMNL-NEXT: (local.get $ref) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $non-exclusive-get (param $x i32) (result f64) |
| (local $ref (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (if (local.get $x) |
| (local.set $ref |
| (ref.null $struct.A) |
| ) |
| ) |
| ;; A get that receives two different allocations, and so we should not try |
| ;; to optimize it. |
| (struct.get $struct.A 1 |
| (local.get $ref) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee (result i32) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $tee (type $func.3) (result i32) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $1) |
| ;; NOMNL-NEXT: ) |
| (func $tee (result i32) |
| (local $ref (ref null $struct.A)) |
| (struct.get $struct.A 0 |
| ;; A tee flows out the value, and we can optimize this allocation. |
| (local.tee $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-set |
| ;; CHECK-NEXT: (local $ref (ref null $struct.recursive)) |
| ;; CHECK-NEXT: (local $1 (ref null $struct.recursive)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.recursive)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (ref.null $struct.recursive) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.recursive) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.recursive) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (ref.null $struct.recursive) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $tee-set (type $func.0) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.recursive)) |
| ;; NOMNL-NEXT: (local $1 (ref null $struct.recursive)) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.recursive)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (ref.null $struct.recursive) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.recursive) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.recursive) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (ref.null $struct.recursive) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $tee-set |
| (local $ref (ref null $struct.recursive)) |
| ;; As above, but with a set, and also a recursive type. |
| (struct.set $struct.recursive 0 |
| (local.tee $ref |
| (struct.new_default_with_rtt $struct.recursive |
| (rtt.canon $struct.recursive) |
| ) |
| ) |
| (ref.null $struct.recursive) |
| ) |
| ) |
| |
| ;; CHECK: (func $set-value |
| ;; CHECK-NEXT: (local $ref (ref null $struct.recursive)) |
| ;; CHECK-NEXT: (struct.set $struct.recursive 0 |
| ;; CHECK-NEXT: (ref.null $struct.recursive) |
| ;; CHECK-NEXT: (local.tee $ref |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.recursive |
| ;; CHECK-NEXT: (rtt.canon $struct.recursive) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $set-value (type $func.0) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.recursive)) |
| ;; NOMNL-NEXT: (struct.set $struct.recursive 0 |
| ;; NOMNL-NEXT: (ref.null $struct.recursive) |
| ;; NOMNL-NEXT: (local.tee $ref |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.recursive |
| ;; NOMNL-NEXT: (rtt.canon $struct.recursive) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $set-value |
| (local $ref (ref null $struct.recursive)) |
| (struct.set $struct.recursive 0 |
| (ref.null $struct.recursive) |
| ;; As above, but operands reversed: the allocation is now the value, not |
| ;; the reference, and so it escapes. |
| (local.tee $ref |
| (struct.new_default_with_rtt $struct.recursive |
| (rtt.canon $struct.recursive) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $initialize-with-reference |
| ;; CHECK-NEXT: (local $0 (ref null $struct.recursive)) |
| ;; CHECK-NEXT: (local $1 (ref null $struct.recursive)) |
| ;; CHECK-NEXT: (local $2 (ref null $struct.recursive)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.recursive)) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.recursive |
| ;; CHECK-NEXT: (rtt.canon $struct.recursive) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.recursive) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.recursive) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.recursive)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.recursive) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $initialize-with-reference (type $func.0) |
| ;; NOMNL-NEXT: (local $0 (ref null $struct.recursive)) |
| ;; NOMNL-NEXT: (local $1 (ref null $struct.recursive)) |
| ;; NOMNL-NEXT: (local $2 (ref null $struct.recursive)) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.recursive)) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.recursive |
| ;; NOMNL-NEXT: (rtt.canon $struct.recursive) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.recursive) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.recursive) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.recursive)) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.recursive) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $initialize-with-reference |
| (local $0 (ref null $struct.recursive)) |
| (local.set $0 |
| ;; The outer allocation can be optimized, as it does not escape. |
| (struct.new_with_rtt $struct.recursive |
| ;; The inner allocation should not prevent the outer one from being |
| ;; optimized through some form of confusion. |
| ;; After the outer one is optimized, the inner one can be optimized in |
| ;; principle, as it can be seen to no longer escape. However, we depend |
| ;; on other optimizations to actually remove the outer allocation (like |
| ;; vacuum), and so it cannot be optimized. If we ran vaccum, and then |
| ;; additional iterations, this might be handled. |
| (struct.new_default_with_rtt $struct.recursive |
| (rtt.canon $struct.recursive) |
| ) |
| (rtt.canon $struct.recursive) |
| ) |
| ) |
| (drop |
| (struct.get $struct.recursive 0 |
| (local.get $0) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $escape-flow-out (result anyref) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (struct.set $struct.A 0 |
| ;; CHECK-NEXT: (local.tee $ref |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $ref) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $escape-flow-out (type $func.4) (result anyref) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (struct.set $struct.A 0 |
| ;; NOMNL-NEXT: (local.tee $ref |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (i32.const 1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $ref) |
| ;; NOMNL-NEXT: ) |
| (func $escape-flow-out (result anyref) |
| (local $ref (ref null $struct.A)) |
| (struct.set $struct.A 0 |
| (local.tee $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (i32.const 1) |
| ) |
| ;; The allocation escapes out to the caller by flowing out. |
| (local.get $ref) |
| ) |
| |
| ;; CHECK: (func $escape-return (result anyref) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (struct.set $struct.A 0 |
| ;; CHECK-NEXT: (local.tee $ref |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (local.get $ref) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $escape-return (type $func.4) (result anyref) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (struct.set $struct.A 0 |
| ;; NOMNL-NEXT: (local.tee $ref |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (i32.const 1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (return |
| ;; NOMNL-NEXT: (local.get $ref) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $escape-return (result anyref) |
| (local $ref (ref null $struct.A)) |
| (struct.set $struct.A 0 |
| (local.tee $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (i32.const 1) |
| ) |
| ;; The allocation escapes out to the caller by a return. |
| (return |
| (local.get $ref) |
| ) |
| ) |
| |
| ;; CHECK: (func $non-nullable (param $a (ref $struct.A)) |
| ;; CHECK-NEXT: (local $1 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $2 (ref null $struct.A)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref $struct.A)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.nonnullable)) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.nonnullable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.nonnullable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $non-nullable (type $func.6) (param $a (ref $struct.A)) |
| ;; NOMNL-NEXT: (local $1 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $2 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref $struct.A)) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.nonnullable)) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (local.get $a) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (ref.as_non_null |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.nonnullable) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.nonnullable) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.as_non_null |
| ;; NOMNL-NEXT: (local.get $1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $non-nullable (param $a (ref $struct.A)) |
| (drop |
| ;; An optimizable case where the type is non-nullable, which requires |
| ;; special handling in locals. |
| (struct.get $struct.nonnullable 0 |
| (struct.new_with_rtt $struct.nonnullable |
| (local.get $a) |
| (rtt.canon $struct.nonnullable) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $before-loop-use-multi (param $x i32) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $2 i32) |
| ;; CHECK-NEXT: (local $3 f64) |
| ;; CHECK-NEXT: (local $4 i32) |
| ;; CHECK-NEXT: (local $5 f64) |
| ;; CHECK-NEXT: (loop $outer |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $4 |
| ;; CHECK-NEXT: (i32.const 2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $5 |
| ;; CHECK-NEXT: (f64.const 2.1828) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (local.get $4) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (local.get $5) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (f64.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (loop $inner |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (i32.add |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (br_if $inner |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (br $outer) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $before-loop-use-multi (type $func.7) (param $x i32) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $2 i32) |
| ;; NOMNL-NEXT: (local $3 f64) |
| ;; NOMNL-NEXT: (local $4 i32) |
| ;; NOMNL-NEXT: (local $5 f64) |
| ;; NOMNL-NEXT: (loop $outer |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $4 |
| ;; NOMNL-NEXT: (i32.const 2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $5 |
| ;; NOMNL-NEXT: (f64.const 2.1828) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (local.get $4) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $3 |
| ;; NOMNL-NEXT: (local.get $5) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (if |
| ;; NOMNL-NEXT: (local.get $x) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $3) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $3 |
| ;; NOMNL-NEXT: (f64.const 42) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (loop $inner |
| ;; NOMNL-NEXT: (block |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (i32.add |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (i32.const 1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (br_if $inner |
| ;; NOMNL-NEXT: (local.get $x) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (if |
| ;; NOMNL-NEXT: (local.get $x) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $3) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (br $outer) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $before-loop-use-multi (param $x i32) |
| (local $ref (ref null $struct.A)) |
| ;; Allocate in a loop, and use that allocation multiple times in that loop |
| ;; in various ways inside. |
| (loop $outer |
| (local.set $ref |
| (struct.new_with_rtt $struct.A |
| (i32.const 2) |
| (f64.const 2.1828) |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (drop |
| (struct.get $struct.A 0 |
| (local.get $ref) |
| ) |
| ) |
| (if (local.get $x) |
| (drop |
| (struct.get $struct.A 1 |
| (local.get $ref) |
| ) |
| ) |
| (struct.set $struct.A 1 |
| (local.get $ref) |
| (f64.const 42) |
| ) |
| ) |
| (loop $inner |
| (struct.set $struct.A 0 |
| (local.get $ref) |
| (i32.add |
| (struct.get $struct.A 0 |
| (local.get $ref) |
| ) |
| (i32.const 1) |
| ) |
| ) |
| (br_if $inner |
| (local.get $x) |
| ) |
| ) |
| (if (local.get $x) |
| (drop |
| (struct.get $struct.A 0 |
| (local.get $ref) |
| ) |
| ) |
| (drop |
| (struct.get $struct.A 1 |
| (local.get $ref) |
| ) |
| ) |
| ) |
| (br $outer) |
| ) |
| ) |
| |
| ;; CHECK: (func $multi-separate |
| ;; CHECK-NEXT: (local $0 i32) |
| ;; CHECK-NEXT: (local $1 f64) |
| ;; CHECK-NEXT: (local $2 i32) |
| ;; CHECK-NEXT: (local $3 f64) |
| ;; CHECK-NEXT: (local $4 i32) |
| ;; CHECK-NEXT: (local $5 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $4 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $5 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $5) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $multi-separate (type $func.0) |
| ;; NOMNL-NEXT: (local $0 i32) |
| ;; NOMNL-NEXT: (local $1 f64) |
| ;; NOMNL-NEXT: (local $2 i32) |
| ;; NOMNL-NEXT: (local $3 f64) |
| ;; NOMNL-NEXT: (local $4 i32) |
| ;; NOMNL-NEXT: (local $5 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $3 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $4 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $5 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $5) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $multi-separate |
| ;; Multiple independent things we can optimize. |
| (drop |
| (struct.get $struct.A 0 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ) |
| (drop |
| (struct.get $struct.A 0 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ) |
| (drop |
| (struct.get $struct.A 1 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $multi-separate-same-local-index |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (local $3 i32) |
| ;; CHECK-NEXT: (local $4 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $4 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $multi-separate-same-local-index (type $func.0) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (local $3 i32) |
| ;; NOMNL-NEXT: (local $4 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $3 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $4 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $3) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $multi-separate-same-local-index |
| (local $ref (ref null $struct.A)) |
| ;; Multiple independent things we can optimize that use the same local |
| ;; index, but they do not conflict in their live ranges. |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (drop |
| (struct.get $struct.A 0 |
| (local.get $ref) |
| ) |
| ) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (drop |
| (struct.get $struct.A 0 |
| (local.get $ref) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $multi-separate-different-local-index-overlapping-lifetimes |
| ;; CHECK-NEXT: (local $ref1 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $ref2 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $2 i32) |
| ;; CHECK-NEXT: (local $3 f64) |
| ;; CHECK-NEXT: (local $4 i32) |
| ;; CHECK-NEXT: (local $5 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $4 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $5 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $4) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $multi-separate-different-local-index-overlapping-lifetimes (type $func.0) |
| ;; NOMNL-NEXT: (local $ref1 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $ref2 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $2 i32) |
| ;; NOMNL-NEXT: (local $3 f64) |
| ;; NOMNL-NEXT: (local $4 i32) |
| ;; NOMNL-NEXT: (local $5 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $3 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $4 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $5 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $4) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $multi-separate-different-local-index-overlapping-lifetimes |
| (local $ref1 (ref null $struct.A)) |
| (local $ref2 (ref null $struct.A)) |
| ;; Multiple independent things we can optimize that use different local |
| ;; indexes, but whose lifetimes overlap. We should not be confused by that. |
| (local.set $ref1 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (local.set $ref2 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (drop |
| (struct.get $struct.A 0 |
| (local.get $ref1) |
| ) |
| ) |
| (drop |
| (struct.get $struct.A 0 |
| (local.get $ref2) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $get-through-block (result f64) |
| ;; CHECK-NEXT: (local $0 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $struct.A 1 |
| ;; CHECK-NEXT: (block $block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $get-through-block (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $0 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (struct.get $struct.A 1 |
| ;; NOMNL-NEXT: (block $block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (br_if $block |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $get-through-block (result f64) |
| (local $0 (ref null $struct.A)) |
| (local.set $0 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (struct.get $struct.A 1 |
| (block $block (result (ref null $struct.A)) |
| (drop |
| ;; A branch to the block. This ensures its name is not removable. And |
| ;; it indicates that the block does not have a single value that |
| ;; flows out, which means we do not have exclusive use of the |
| ;; allocation on this path, and must give up. |
| (br_if $block |
| (ref.null $struct.A) |
| (i32.const 0) |
| ) |
| ) |
| (local.get $0) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $branch-to-block (result f64) |
| ;; CHECK-NEXT: (local $0 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $struct.A 1 |
| ;; CHECK-NEXT: (block $block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $branch-to-block (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $0 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (struct.get $struct.A 1 |
| ;; NOMNL-NEXT: (block $block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (br_if $block |
| ;; NOMNL-NEXT: (local.get $0) |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $branch-to-block (result f64) |
| (local $0 (ref null $struct.A)) |
| (local.set $0 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (struct.get $struct.A 1 |
| (block $block (result (ref null $struct.A)) |
| (drop |
| ;; A branch to the block of our allocation. However, there is also |
| ;; a fallthrough value as well, so we must give up. |
| (br_if $block |
| (local.get $0) |
| (i32.const 0) |
| ) |
| ) |
| (ref.null $struct.A) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $branch-to-block-no-fallthrough (result f64) |
| ;; CHECK-NEXT: (local $0 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block $block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (f64.const 2.1828) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $branch-to-block-no-fallthrough (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $0 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block (result f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block $block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (br_if $block |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (return |
| ;; NOMNL-NEXT: (f64.const 2.1828) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $branch-to-block-no-fallthrough (result f64) |
| (local $0 (ref null $struct.A)) |
| (local.set $0 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (struct.get $struct.A 1 |
| (block $block (result (ref null $struct.A)) |
| (drop |
| ;; A branch to the block of our allocation. In this case there is no |
| ;; other value reaching the block, and so our branch is the sole value |
| ;; which means there is no mixing, and we can optimize this. |
| (br_if $block |
| (local.get $0) |
| (i32.const 0) |
| ) |
| ) |
| (return (f64.const 2.1828)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $two-branches (result f64) |
| ;; CHECK-NEXT: (local $0 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $struct.A 1 |
| ;; CHECK-NEXT: (block $block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (f64.const 2.1828) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $two-branches (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $0 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (struct.get $struct.A 1 |
| ;; NOMNL-NEXT: (block $block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (br_if $block |
| ;; NOMNL-NEXT: (local.get $0) |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (br_if $block |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (return |
| ;; NOMNL-NEXT: (f64.const 2.1828) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $two-branches (result f64) |
| (local $0 (ref null $struct.A)) |
| (local.set $0 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (struct.get $struct.A 1 |
| (block $block (result (ref null $struct.A)) |
| (drop |
| ;; A branch to the block of our allocation. |
| (br_if $block |
| (local.get $0) |
| (i32.const 0) |
| ) |
| ) |
| (drop |
| ;; Another branch, causing mixing that prevents optimizations. |
| (br_if $block |
| (ref.null $struct.A) |
| (i32.const 0) |
| ) |
| ) |
| (return (f64.const 2.1828)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $two-branches-b (result f64) |
| ;; CHECK-NEXT: (local $0 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $struct.A 1 |
| ;; CHECK-NEXT: (block $block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (f64.const 2.1828) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $two-branches-b (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $0 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (struct.get $struct.A 1 |
| ;; NOMNL-NEXT: (block $block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (br_if $block |
| ;; NOMNL-NEXT: (local.get $0) |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (br_if $block |
| ;; NOMNL-NEXT: (local.get $0) |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (return |
| ;; NOMNL-NEXT: (f64.const 2.1828) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $two-branches-b (result f64) |
| (local $0 (ref null $struct.A)) |
| (local.set $0 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (struct.get $struct.A 1 |
| (block $block (result (ref null $struct.A)) |
| (drop |
| (br_if $block |
| (local.get $0) |
| (i32.const 0) |
| ) |
| ) |
| (drop |
| ;; As in $two-branches, but the value here is our allocation, the same |
| ;; as in the first branch above us. We do not yet optimize such merges |
| ;; of our allocation, but we could in the future. |
| (br_if $block |
| (local.get $0) |
| (i32.const 0) |
| ) |
| ) |
| (return (f64.const 2.1828)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $br_if_flow (result f64) |
| ;; CHECK-NEXT: (local $0 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $struct.A 1 |
| ;; CHECK-NEXT: (block $block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (call $send-ref |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (f64.const 2.1828) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $br_if_flow (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $0 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (struct.new_default_with_rtt $struct.A |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (struct.get $struct.A 1 |
| ;; NOMNL-NEXT: (block $block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (call $send-ref |
| ;; NOMNL-NEXT: (br_if $block |
| ;; NOMNL-NEXT: (local.get $0) |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (return |
| ;; NOMNL-NEXT: (f64.const 2.1828) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $br_if_flow (result f64) |
| (local $0 (ref null $struct.A)) |
| (local.set $0 |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (struct.get $struct.A 1 |
| (block $block (result (ref null $struct.A)) |
| ;; If it were not for the call here then we would be able to optimize |
| ;; the allocation in this function. (The branch with the allocation is |
| ;; ok, but the br_if also flows the value into a call, that escapes it.) |
| (call $send-ref |
| (br_if $block |
| (local.get $0) |
| (i32.const 0) |
| ) |
| ) |
| (return (f64.const 2.1828)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $ref-as-non-null |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (ref.null any) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $ref-as-non-null (type $func.0) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.as_non_null |
| ;; NOMNL-NEXT: (ref.null any) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $ref-as-non-null |
| (local $ref (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| (struct.set $struct.A 0 |
| ;; We can see that the input to this RefAsNonNull is always non-null, as |
| ;; it is our allocation, and so it does not prevent us from optimizing |
| ;; here. |
| (ref.as_non_null |
| (local.get $ref) |
| ) |
| (i32.const 1) |
| ) |
| ;; Another RefAsNonNull, to check we do not modify irrelevant ones. |
| (drop |
| (ref.as_non_null |
| (ref.null any) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $ref-as-non-null-through-local (result i32) |
| ;; CHECK-NEXT: (local $ref (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $ref-as-non-null-through-local (type $func.3) (result i32) |
| ;; NOMNL-NEXT: (local $ref (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block (result i32) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $1) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $ref-as-non-null-through-local (result i32) |
| (local $ref (ref null $struct.A)) |
| (local.set $ref |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ;; Copy the allocation through a ref.as_non_null. This must not trap: it may |
| ;; trap if we leave the ref.as_non_null there and also we do not assign |
| ;; anything to the local (if we skip assignments to the local when we |
| ;; optimize). To avoid that, we should remove the ref.as_non_null, which is |
| ;; safe since we know our allocation is passed into it, which is not null, |
| ;; and will not trap. |
| (local.set $ref |
| (ref.as_non_null |
| (local.get $ref) |
| ) |
| ) |
| (struct.get $struct.A 0 |
| (local.get $ref) |
| ) |
| ) |
| |
| ;; CHECK: (func $br_if-allocation (result f64) |
| ;; CHECK-NEXT: (local $0 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (local $3 i32) |
| ;; CHECK-NEXT: (local $4 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block $block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $3 |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $4 |
| ;; CHECK-NEXT: (f64.const 13.37) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (local.get $4) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (f64.const 2.1828) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $br_if-allocation (type $func.1) (result f64) |
| ;; NOMNL-NEXT: (local $0 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (local $3 i32) |
| ;; NOMNL-NEXT: (local $4 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block $block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (br_if $block |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $3 |
| ;; NOMNL-NEXT: (i32.const 42) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $4 |
| ;; NOMNL-NEXT: (f64.const 13.37) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (local.get $3) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (local.get $4) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (return |
| ;; NOMNL-NEXT: (f64.const 2.1828) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.get $2) |
| ;; NOMNL-NEXT: ) |
| (func $br_if-allocation (result f64) |
| (local $0 (ref null $struct.A)) |
| (struct.get $struct.A 1 |
| (block $block (result (ref null $struct.A)) |
| (drop |
| ;; Our allocation flows into a br_if, which therefore has non-nullable |
| ;; type, which we must update after optimizing. |
| (br_if $block |
| (struct.new_with_rtt $struct.A |
| (i32.const 42) |
| (f64.const 13.37) |
| (rtt.canon $struct.A) |
| ) |
| (i32.const 0) |
| ) |
| ) |
| (return (f64.const 2.1828)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $simple-no-rtt |
| ;; CHECK-NEXT: (local $0 i32) |
| ;; CHECK-NEXT: (local $1 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $simple-no-rtt (type $func.0) |
| ;; NOMNL-NEXT: (local $0 i32) |
| ;; NOMNL-NEXT: (local $1 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $0 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $simple-no-rtt |
| (drop |
| ;; This allocation has no rtt, so we have nothing to drop from it when |
| ;; we optimize. |
| (struct.new_default $struct.A) |
| ) |
| ) |
| |
| ;; CHECK: (func $pass-through-loop |
| ;; CHECK-NEXT: (local $0 (ref null $struct.A)) |
| ;; CHECK-NEXT: (local $1 i32) |
| ;; CHECK-NEXT: (local $2 f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (loop $loop (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (br_if $loop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result (ref null $struct.A)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $2 |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (rtt.canon $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null $struct.A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NOMNL: (func $pass-through-loop (type $func.0) |
| ;; NOMNL-NEXT: (local $0 (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local $1 i32) |
| ;; NOMNL-NEXT: (local $2 f64) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (loop $loop (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (br_if $loop |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (block (result (ref null $struct.A)) |
| ;; NOMNL-NEXT: (local.set $1 |
| ;; NOMNL-NEXT: (i32.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (local.set $2 |
| ;; NOMNL-NEXT: (f64.const 0) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (drop |
| ;; NOMNL-NEXT: (rtt.canon $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: (ref.null $struct.A) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| ;; NOMNL-NEXT: ) |
| (func $pass-through-loop |
| (local $0 (ref null $struct.A)) |
| ;; The allocation pass through a loop, which should change type to be |
| ;; nullable. |
| (drop |
| (loop $loop (result (ref $struct.A)) |
| ;; Include a branch to the loop, so that the testcase does not become |
| ;; trivial (remove-unused-names will turn a loop with no name into a |
| ;; block). |
| (br_if $loop (i32.const 0)) |
| ;; The allocation that will be turned into locals. |
| (struct.new_default_with_rtt $struct.A |
| (rtt.canon $struct.A) |
| ) |
| ) |
| ) |
| ) |
| ) |