blob: 262561d0cbb8b94f47251f9713a558adea65bb2a [file] [log] [blame]
;; 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)
)
)
)
)
)