blob: 2a773a3882f93bbaee79b2ea7281ea9b7dd5fe39 [file] [log] [blame] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --remove-unused-names --heap-store-optimization -all -S -o - \
;; RUN: | filecheck %s
;;
;; --remove-unused-names allows the optimizer to see that the blocks have no
;; breaks to them, and so they have no nonlinear control flow.
(module
;; CHECK: (type $struct (struct (field (mut i32))))
;; CHECK: (type $struct2 (struct (field (mut i32)) (field (mut i32))))
;; CHECK: (type $struct3 (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))
;; CHECK: (tag $tag (type $1))
(tag $tag)
(type $struct (struct (field (mut i32))))
(type $struct2 (struct (field (mut i32)) (field (mut i32))))
(type $struct3 (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))
;; CHECK: (func $tee (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $tee
(local $ref (ref null $struct))
;; The set is not needed as we can apply the 20 in the new.
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 10)
)
)
(i32.const 20)
)
)
;; CHECK: (func $side-effects-in-old-value (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $helper-i32
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $side-effects-in-old-value
(local $ref (ref null $struct))
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
;; Side effects here force us to keep the old value around.
(call $helper-i32 (i32.const 0))
)
)
(i32.const 20)
)
)
;; CHECK: (func $side-effects-in-new-value (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (call $helper-i32
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $side-effects-in-new-value
(local $ref (ref null $struct))
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 10)
)
)
;; Side effects here are not a problem.
(call $helper-i32 (i32.const 0))
)
)
;; CHECK: (func $many-fields (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct2))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct2
;; CHECK-NEXT: (i32.const 30)
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct2
;; CHECK-NEXT: (i32.const 40)
;; CHECK-NEXT: (i32.const 60)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $many-fields
(local $ref (ref null $struct2))
;; Set to the first field.
(struct.set $struct2 0
(local.tee $ref
(struct.new $struct2
(i32.const 10)
(i32.const 20)
)
)
(i32.const 30)
)
;; Set to the second.
(struct.set $struct2 1
(local.tee $ref
(struct.new $struct2
(i32.const 40)
(i32.const 50)
)
)
(i32.const 60)
)
)
;; CHECK: (func $side-effect-conflict (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct2))
;; CHECK-NEXT: (struct.set $struct2 0
;; CHECK-NEXT: (local.tee $ref
;; CHECK-NEXT: (struct.new $struct2
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: (call $helper-i32
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $helper-i32
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $side-effect-conflict
(local $ref (ref null $struct2))
(struct.set $struct2 0
(local.tee $ref
(struct.new $struct2
(i32.const 10)
;; Side effects on the second field prevent us from moving the set's
;; value past it to replace the first field above it.
(call $helper-i32 (i32.const 0))
)
)
(call $helper-i32 (i32.const 1))
)
)
;; CHECK: (func $side-effect-ok (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct2))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct2
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $helper-i32
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $helper-i32
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $side-effect-ok
(local $ref (ref null $struct2))
(struct.set $struct2 0
(local.tee $ref
(struct.new $struct2
;; Side effects on the first field do not interfere.
(call $helper-i32 (i32.const 0))
(i32.const 10)
)
)
(call $helper-i32 (i32.const 1))
)
)
;; CHECK: (func $optimize-subsequent (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $optimize-subsequent
(local $ref (ref null $struct))
(local.set $ref
(struct.new $struct
(i32.const 10)
)
)
;; A set that comes right after can be optimized too.
(struct.set $struct 0
(local.get $ref)
(i32.const 20)
)
)
;; CHECK: (func $optimize-subsequent-bad-local (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local $other (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $other)
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $optimize-subsequent-bad-local
(local $ref (ref null $struct))
(local $other (ref null $struct))
(local.set $ref
(struct.new $struct
(i32.const 10)
)
)
;; As above, but the local.get uses a different local, so we have nothing
;; to optimize.
(struct.set $struct 0
(local.get $other)
(i32.const 20)
)
)
;; CHECK: (func $optimize-chain (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 30)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $optimize-chain
(local $ref (ref null $struct))
(local.set $ref
(struct.new $struct
(i32.const 10)
)
)
(struct.set $struct 0
(local.get $ref)
(i32.const 20)
)
;; The value in the last item in the chain should apply.
(struct.set $struct 0
(local.get $ref)
(i32.const 30)
)
)
;; CHECK: (func $pattern-breaker (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local $ref2 (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $ref2
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $pattern-breaker
(local $ref (ref null $struct))
(local $ref2 (ref null $struct))
(local.set $ref
(struct.new $struct
(i32.const 10)
)
)
;; Any instruction that can not be swapped and is not
;; the expected struct.set breaks the pattern.
(local.set $ref2 (local.get $ref))
(struct.set $struct 0
(local.get $ref)
(i32.const 20)
)
)
;; CHECK: (func $dont-swap-subsequent-struct-new (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local $ref2 (ref null $struct))
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $ref2
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $dont-swap-subsequent-struct-new
(local $ref (ref null $struct))
(local $ref2 (ref null $struct))
(local.set $ref
(struct.new $struct
(i32.const 10)
)
)
(nop)
;; We do not swap with another local.set of struct.new.
(local.set $ref2
(struct.new $struct
(i32.const 20)
)
)
;; last instruction in the block won't be swapped.
(struct.set $struct 0
(local.get $ref)
(i32.const 20)
)
)
;; CHECK: (func $ref-local-write (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $ref-local-write
(local $ref (ref null $struct))
(local.set $ref
(struct.new $struct
(i32.const 10)
)
)
(struct.set $struct 0
(local.get $ref)
(block (result i32)
;; A write to the ref local prevents us from optimizing.
(local.set $ref
(ref.null $struct)
)
(i32.const 20)
)
)
)
;; CHECK: (func $ref-local-write-tee (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.tee $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $ref-local-write-tee
(local $ref (ref null $struct))
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 10)
)
)
(block (result i32)
;; As above, but now in a tee.
(local.set $ref
(ref.null $struct)
)
(i32.const 20)
)
)
)
;; CHECK: (func $other-local-write (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local $other (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (local.set $other
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $other-local-write
(local $ref (ref null $struct))
(local $other (ref null $struct))
(local.set $ref
(struct.new $struct
(i32.const 10)
)
)
(struct.set $struct 0
(local.get $ref)
(block (result i32)
;; A write to another local is fine.
(local.set $other
(ref.null $struct)
)
(i32.const 20)
)
)
)
;; CHECK: (func $ref-local-read (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $ref-local-read
(local $ref (ref null $struct))
(local.set $ref
(struct.new $struct
(i32.const 10)
)
)
(struct.set $struct 0
(local.get $ref)
(block (result i32)
;; A read of the ref local prevents us from optimizing.
(drop
(local.get $ref)
)
(i32.const 20)
)
)
)
;; CHECK: (func $ref-local-read-tee (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.tee $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $ref-local-read-tee
(local $ref (ref null $struct))
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 10)
)
)
(block (result i32)
;; As above, but now in a tee.
(drop
(local.get $ref)
)
(i32.const 20)
)
)
)
;; CHECK: (func $ref-other-read (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local $other (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $other)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $ref-other-read
(local $ref (ref null $struct))
(local $other (ref null $struct))
(local.set $ref
(struct.new $struct
(i32.const 10)
)
)
(struct.set $struct 0
(local.get $ref)
(block (result i32)
;; A read of another local is fine.
(drop
(local.get $other)
)
(i32.const 20)
)
)
)
;; CHECK: (func $tee-and-subsequent (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct3))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct3
;; CHECK-NEXT: (i32.const 40)
;; CHECK-NEXT: (i32.const 50)
;; CHECK-NEXT: (i32.const 60)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $tee-and-subsequent
(local $ref (ref null $struct3))
;; Test the common pattern of several subsequent sets, one of which is
;; using a tee.
(struct.set $struct3 0
(local.tee $ref
(struct.new $struct3
(i32.const 10)
(i32.const 20)
(i32.const 30)
)
)
(i32.const 40)
)
(struct.set $struct3 1
(local.get $ref)
(i32.const 50)
)
(struct.set $struct3 2
(local.get $ref)
(i32.const 60)
)
)
;; CHECK: (func $side-effect-subsequent-ok (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct2))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct2
;; CHECK-NEXT: (call $helper-i32
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $helper-i32
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $side-effect-subsequent-ok
(local $ref (ref null $struct2))
(local.set $ref
(struct.new $struct2
;; The first field has side effects, but the second does not.
(call $helper-i32 (i32.const 0))
(i32.const 10)
)
)
;; Replace the second field with something with side effects.
(struct.set $struct2 1
(local.get $ref)
(call $helper-i32 (i32.const 1))
)
)
;; CHECK: (func $default (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local $ref3 (ref null $struct3))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $ref3
;; CHECK-NEXT: (struct.new $struct3
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (i32.const 33)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $default
(local $ref (ref null $struct))
(local $ref3 (ref null $struct3))
;; We optimize new_default as well, adding default values as needed.
(struct.set $struct 0
(local.tee $ref
(struct.new_default $struct)
)
(i32.const 10)
)
(struct.set $struct3 1
(local.tee $ref3
(struct.new_default $struct3)
)
(i32.const 33)
)
)
;; CHECK: (func $many-news (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct3))
;; CHECK-NEXT: (local $ref2 (ref null $struct3))
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct3
;; CHECK-NEXT: (i32.const 40)
;; CHECK-NEXT: (i32.const 50)
;; CHECK-NEXT: (i32.const 60)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct3
;; CHECK-NEXT: (i32.const 400)
;; CHECK-NEXT: (i32.const 200)
;; CHECK-NEXT: (i32.const 500)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct3
;; CHECK-NEXT: (i32.const 40)
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: (i32.const 30)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $ref2
;; CHECK-NEXT: (struct.new $struct3
;; CHECK-NEXT: (i32.const 400)
;; CHECK-NEXT: (i32.const 600)
;; CHECK-NEXT: (i32.const 500)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $many-news
(local $ref (ref null $struct3))
(local $ref2 (ref null $struct3))
;; Test that we optimize for multiple struct.news in the same function.
(struct.set $struct3 0
(local.tee $ref
(struct.new $struct3
(i32.const 10)
(i32.const 20)
(i32.const 30)
)
)
(i32.const 40)
)
(struct.set $struct3 1
(local.get $ref)
(i32.const 50)
)
(nop)
(struct.set $struct3 2
(local.get $ref)
(i32.const 60)
)
(nop)
(struct.set $struct3 0
(local.tee $ref
(struct.new $struct3
(i32.const 100)
(i32.const 200)
(i32.const 300)
)
)
(i32.const 400)
)
(struct.set $struct3 2
(local.get $ref)
(i32.const 500)
)
;; Test inside an inner block.
(block $inner
(struct.set $struct3 0
(local.tee $ref
(struct.new $struct3
(i32.const 10)
(i32.const 20)
(i32.const 30)
)
)
(i32.const 40)
)
;; Use a different ref local here.
(struct.set $struct3 0
(local.tee $ref2
(struct.new $struct3
(i32.const 100)
(i32.const 200)
(i32.const 300)
)
)
(i32.const 400)
)
(struct.set $struct3 2
(local.get $ref2)
(i32.const 500)
)
(struct.set $struct3 1
(local.get $ref2)
(i32.const 600)
)
)
)
;; CHECK: (func $unreachable (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local.tee $ref
;; CHECK-NEXT: (block ;; (replaces unreachable StructNew we can't emit)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $unreachable
(local $ref (ref null $struct))
;; Do not optimize unreachable code, either in the new (first pair) or the
;; set (second pair)
(local.set $ref
(struct.new $struct
(unreachable)
)
)
(struct.set $struct 0
(local.get $ref)
(i32.const 10)
)
(nop)
(local.set $ref
(struct.new $struct
(i32.const 20)
)
)
(struct.set $struct 0
(local.get $ref)
(unreachable)
)
)
;; CHECK: (func $helper-i32 (type $7) (param $x i32) (result i32)
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
(func $helper-i32 (param $x i32) (result i32)
(i32.const 42)
)
;; CHECK: (func $control-flow-in-set-value (type $4) (result i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (block $label
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.tee $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if (result i32)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $label)
;; CHECK-NEXT: )
;; CHECK-NEXT: (else
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $control-flow-in-set-value (result i32)
;; Test we handle control flow in the struct.set's value when we combine a
;; struct.set with a struct.new. We should not optimize here.
(local $ref (ref null $struct))
(block $label
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 1)
)
)
(if (result i32)
(i32.const 1)
(then
;; This conditional break happens *after* the local.tee of $ref. We
;; must not move code around that reorders it, since there is a use
;; of the local below that could notice changes.
(br $label)
)
(else
(i32.const 42)
)
)
)
)
;; We did not reach the struct.set, but we did reach the local.tee, so this
;; reads the initial value of 1 (and does not trap on a nullref).
(struct.get $struct 0
(local.get $ref)
)
)
;; CHECK: (func $control-flow-in-set-value-safe (type $4) (result i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (if (result i32)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: )
;; CHECK-NEXT: (else
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $control-flow-in-set-value-safe (result i32)
;; As above, but now the control flow in the value is safe: it does not
;; escape out. We should optimize here.
(local $ref (ref null $struct))
(block $label
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 1)
)
)
(if (result i32)
(i32.const 1)
(then
(i32.const 1337) ;; the break to $out was replaced
)
(else
(i32.const 42)
)
)
)
)
(struct.get $struct 0
(local.get $ref)
)
)
;; CHECK: (func $control-flow-in-set-value-safe-call (type $4) (result i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (call $helper-i32
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $control-flow-in-set-value-safe-call (result i32)
;; As above, but now the possible control flow is a call. It may throw, but
;; that would go outside of the function, which is fine.
(local $ref (ref null $struct))
(block $label
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 1)
)
)
(call $helper-i32 (i32.const 42)) ;; the if was replaced by this call
)
)
(struct.get $struct 0
(local.get $ref)
)
)
;; CHECK: (func $control-flow-in-set-value-safe-return (type $4) (result i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (if (result i32)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (return
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (else
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $control-flow-in-set-value-safe-return (result i32)
;; As above, but replace the call with a return in an if. We can still
;; optimize (if the return is taken, we go outside of the function anyhow).
(local $ref (ref null $struct))
(block $label
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 1)
)
)
(if (result i32)
(i32.const 1)
(then
(return (i32.const 42))
)
(else
(i32.const 42)
)
)
)
)
(struct.get $struct 0
(local.get $ref)
)
)
;; CHECK: (func $control-flow-in-set-value-unsafe-call (type $4) (result i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (block $label
;; CHECK-NEXT: (try_table (catch $tag $label)
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.tee $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $helper-i32
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $control-flow-in-set-value-unsafe-call (result i32)
;; As above, but now the call's possible throw could be caught *inside* the
;; function, which means it is dangerous, and we do not optimize.
(local $ref (ref null $struct))
(block $label
(try_table (catch $tag $label) ;; this try was added
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 1)
)
)
(call $helper-i32 (i32.const 42))
)
)
)
(struct.get $struct 0
(local.get $ref)
)
)
;; CHECK: (func $control-flow-later (type $5) (param $x i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $control-flow-later (param $x i32)
(local $ref (ref null $struct))
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 1)
)
)
(i32.const 42)
)
;; This later control flow should not prevent optimizing the struct.set
;; before it.
(block $out
(br_if $out
(local.get $x)
)
)
(if
(local.get $x)
(then
(nop)
)
)
)
;; CHECK: (func $loop (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.tee $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if (result i32)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: (else
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $loop
(local $ref (ref null $struct))
(loop $loop
;; There is a use of the reference at the top of the loop, and the br_if
;; may get here, so this is a basic block before the struct.set that we
;; need to be careful of reaching. We should not optimize here.
(drop
(struct.get $struct 0
(local.get $ref)
)
)
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 1)
)
)
(if (result i32)
(i32.const 1)
(then
(br $loop)
)
(else
(i32.const 42)
)
)
)
)
)
;; CHECK: (func $loop-more-flow (type $1)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.tee $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if (result i32)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: (else
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $loop-more-flow
(local $ref (ref null $struct))
(loop $loop
;; As above, but add this if which adds more control flow at the loop top.
;; We should still not optimize here.
(if
(i32.const 1)
(then
(br $loop)
)
)
(drop
(struct.get $struct 0
(local.get $ref)
)
)
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 1)
)
)
(if (result i32)
(i32.const 1)
(then
(br $loop)
)
(else
(i32.const 42)
)
)
)
)
)
;; CHECK: (func $loop-in-value (type $5) (param $x i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (loop $loop (result i32)
;; CHECK-NEXT: (br_if $loop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $loop-in-value (param $x i32)
(local $ref (ref null $struct))
;; The struct.set's value has a loop in it, but that is fine, as while there
;; are backedges there, they are still contained in the value: we can't skip
;; the struct.set. We can optimize here.
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 1)
)
)
(loop $loop (result i32)
(br_if $loop
(local.get $x)
)
(i32.const 42)
)
)
)
;; CHECK: (func $in-if-arm (type $6) (param $x i32) (param $y i32) (result i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (if
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.tee $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $in-if-arm (param $x i32) (param $y i32) (result i32)
(local $ref (ref null $struct))
(if
(local.get $x)
(then
(block $out
;; We cannot optimize here, as the struct.get outside of the if can
;; read different state if the br_if happens.
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 1)
)
)
(block (result i32)
(br_if $out
(local.get $x)
)
(i32.const 42)
)
)
)
)
)
(struct.get $struct 0
(local.get $ref)
)
)
;; CHECK: (func $in-if-arm-yes (type $6) (param $x i32) (param $y i32) (result i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (if
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: )
(func $in-if-arm-yes (param $x i32) (param $y i32) (result i32)
(local $ref (ref null $struct))
;; As before, but the struct.get at the end is removed, so we can optimize.
(if
(local.get $x)
(then
(block $out
(struct.set $struct 0
(local.tee $ref
(struct.new $struct
(i32.const 1)
)
)
(block (result i32)
(br_if $out
(local.get $x)
)
(i32.const 42)
)
)
)
)
)
(i32.const 1337) ;; this changed
)
;; CHECK: (func $control-flow-in-set-value-sequence (type $4) (result i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $out (result i32)
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $control-flow-in-set-value-sequence (result i32)
(local $ref (ref null $struct))
(drop
(block $out (result i32)
(local.set $ref
;; Also test struct.new_default here, with control flow.
(struct.new_default $struct)
)
;; The struct.get outside is a problem, so we do not optimize here,
;; nor the set after us.
(struct.set $struct 0
(local.get $ref)
(br_if $out
(i32.const 1)
(i32.const 2)
)
)
(struct.set $struct 0
(local.get $ref)
(i32.const 3)
)
;; This struct.set is not a problem: if we branch, we don't reach it
;; anyhow.
(drop
(struct.get $struct 0
(local.get $ref)
)
)
(i32.const 42)
)
)
(struct.get $struct 0
(local.get $ref)
)
)
;; CHECK: (func $control-flow-in-set-value-sequence-2 (type $4) (result i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $out (result i32)
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $control-flow-in-set-value-sequence-2 (result i32)
(local $ref (ref null $struct))
(drop
(block $out (result i32)
(local.set $ref
(struct.new_default $struct)
)
;; As above, but the order of struct.sets is flipped. We can at least
;; optimize the first here.
(struct.set $struct 0
(local.get $ref)
(i32.const 3)
)
(struct.set $struct 0
(local.get $ref)
(br_if $out
(i32.const 1)
(i32.const 2)
)
)
(drop
(struct.get $struct 0
(local.get $ref)
)
)
(i32.const 42)
)
)
(struct.get $struct 0
(local.get $ref)
)
)
;; CHECK: (func $control-flow-in-set-value-sequence-yes (type $4) (result i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $out (result i32)
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: )
(func $control-flow-in-set-value-sequence-yes (result i32)
(local $ref (ref null $struct))
;; As above, but the struct.get at the end is removed, allowing us to
;; optimize it all.
(drop
(block $out (result i32)
(local.set $ref
(struct.new_default $struct)
)
(struct.set $struct 0
(local.get $ref)
(i32.const 3)
)
(struct.set $struct 0
(local.get $ref)
(br_if $out
(i32.const 1)
(i32.const 2)
)
)
;; Note how this struct.get remains and does not stop us.
(drop
(struct.get $struct 0
(local.get $ref)
)
)
(i32.const 42)
)
)
(i32.const 1337)
)
;; CHECK: (func $multi-control-flow-in-set-value-sequence-yes (type $4) (result i32)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $out (result i32)
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: (i32.const 4)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (i32.const 5)
;; CHECK-NEXT: (i32.const 6)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: )
(func $multi-control-flow-in-set-value-sequence-yes (result i32)
(local $ref (ref null $struct))
;; As above, but now we have multiple br_ifs. We optimize one, but then
;; stop because of the control flow that is now in the struct.new. TODO we
;; could be more precise here.
(drop
(block $out (result i32)
(local.set $ref
(struct.new_default $struct)
)
(struct.set $struct 0
(local.get $ref)
(br_if $out
(i32.const 1)
(i32.const 2)
)
)
(struct.set $struct 0
(local.get $ref)
(br_if $out
(i32.const 3)
(i32.const 4)
)
)
(struct.set $struct 0
(local.get $ref)
(br_if $out
(i32.const 5)
(i32.const 6)
)
)
(i32.const 42)
)
)
(i32.const 1337)
)
;; CHECK: (func $multi-control-flow-in-set-value-sequence-no (type $8) (result anyref)
;; CHECK-NEXT: (local $ref (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $out (result i32)
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: (i32.const 4)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (br_if $out
;; CHECK-NEXT: (i32.const 5)
;; CHECK-NEXT: (i32.const 6)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
(func $multi-control-flow-in-set-value-sequence-no (result anyref)
(local $ref (ref null $struct))
;; As above, but now we have a dangerous local.get at the end, stopping us
;; from optimizing.
(drop
(block $out (result i32)
(local.set $ref
(struct.new_default $struct)
)
(struct.set $struct 0
(local.get $ref)
(br_if $out
(i32.const 1)
(i32.const 2)
)
)
(struct.set $struct 0
(local.get $ref)
(br_if $out
(i32.const 3)
(i32.const 4)
)
)
(struct.set $struct 0
(local.get $ref)
(br_if $out
(i32.const 5)
(i32.const 6)
)
)
(i32.const 42)
)
)
(local.get $ref)
)
)