blob: e0d68848e8a1c0505c1a744ac7064e6567f9b5cc [file] [log] [blame] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --tuple-optimization -all -S -o - | filecheck %s
(module
;; CHECK: (func $just-set (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $just-set
(local $tuple (tuple i32 i32))
;; This tuple local can be optimized into separate locals per element. The
;; tuple local itself then has no uses and other passes will remove it.
(local.set $tuple
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
)
;; CHECK: (func $just-get (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $just-get
(local $tuple (tuple i32 i32))
;; The default value of the tuple elements is used here in the new locals we
;; add.
(drop
(tuple.extract 2 0
(local.get $tuple)
)
)
(drop
(tuple.extract 2 1
(local.get $tuple)
)
)
)
;; CHECK: (func $just-get-bad (type $1) (result i32 i32)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (tuple.extract 2 0
;; CHECK-NEXT: (local.get $tuple)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (tuple.extract 2 1
;; CHECK-NEXT: (local.get $tuple)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $tuple)
;; CHECK-NEXT: )
(func $just-get-bad (result i32 i32)
(local $tuple (tuple i32 i32))
(drop
(tuple.extract 2 0
(local.get $tuple)
)
)
(drop
(tuple.extract 2 1
(local.get $tuple)
)
)
;; This get is not used by something we can handle (it escapes from the
;; function), so we should not try to optimize this tuple.
(local.get $tuple)
)
;; CHECK: (func $set-and-gets (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $set-and-gets
(local $tuple (tuple i32 i32))
(local.set $tuple
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
(drop
(tuple.extract 2 0
(local.get $tuple)
)
)
(drop
(tuple.extract 2 1
(local.get $tuple)
)
)
;; Add another get for more coverage
(drop
(tuple.extract 2 0
(local.get $tuple)
)
)
)
;; CHECK: (func $tee (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32))
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (local $4 i32)
;; CHECK-NEXT: (local $5 i32)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $5
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; 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: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $4)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $5)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $tee
(local $tuple (tuple i32 i32))
(local $tuple2 (tuple i32 i32))
(local.set $tuple
(local.tee $tuple2
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
)
;; Read the first tuple.
(drop
(tuple.extract 2 0
(local.get $tuple)
)
)
(drop
(tuple.extract 2 1
(local.get $tuple)
)
)
;; Read the second tuple.
(drop
(tuple.extract 2 0
(local.get $tuple2)
)
)
(drop
(tuple.extract 2 1
(local.get $tuple2)
)
)
)
;; CHECK: (func $just-tee (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $just-tee
(local $tuple (tuple i32 i32))
(drop
(tuple.extract 2 0
(local.tee $tuple
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
)
)
)
;; CHECK: (func $just-tee-bad (type $1) (result i32 i32)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local.tee $tuple
;; CHECK-NEXT: (tuple.make 2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $just-tee-bad (result i32 i32)
(local $tuple (tuple i32 i32))
;; This tee goes somewhere we cannot handle, so we do not optimize here.
(local.tee $tuple
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
)
;; CHECK: (func $no-uses (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32))
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (local $4 i32)
;; CHECK-NEXT: (local $5 i32)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $5
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; 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: )
(func $no-uses
(local $tuple (tuple i32 i32))
(local $tuple2 (tuple i32 i32))
;; The set has no uses, and the tee only has an immediate use. We can
;; still optimize both.
(local.set $tuple
(local.tee $tuple2
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
)
)
;; CHECK: (func $corruption-tee (type $1) (result i32 i32)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32))
;; CHECK-NEXT: (local.set $tuple
;; CHECK-NEXT: (local.tee $tuple2
;; CHECK-NEXT: (tuple.make 2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $tuple2)
;; CHECK-NEXT: )
(func $corruption-tee (result i32 i32)
(local $tuple (tuple i32 i32))
(local $tuple2 (tuple i32 i32))
;; As above, but the tee's tuple is bad and it prevents the other from
;; being optimized too, due to the copy between them.
(local.set $tuple
(local.tee $tuple2
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
)
(local.get $tuple2)
)
;; CHECK: (func $corruption-set (type $1) (result i32 i32)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32))
;; CHECK-NEXT: (local.set $tuple
;; CHECK-NEXT: (local.tee $tuple2
;; CHECK-NEXT: (tuple.make 2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $tuple)
;; CHECK-NEXT: )
(func $corruption-set (result i32 i32)
(local $tuple (tuple i32 i32))
(local $tuple2 (tuple i32 i32))
;; As above, but now the set is bad.
(local.set $tuple
(local.tee $tuple2
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
)
(local.get $tuple) ;; this changed from $tuple2; same outcome.
)
;; CHECK: (func $set-after-set (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32))
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (local $4 i32)
;; CHECK-NEXT: (local $5 i32)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $5
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $set-after-set
(local $tuple (tuple i32 i32))
(local $tuple2 (tuple i32 i32))
;; We can optimize both these tuples.
(local.set $tuple
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
(local.set $tuple2
(local.get $tuple)
)
;; Add a copy of a tuple to itself for extra coverage.
(local.set $tuple
(local.get $tuple)
)
)
;; CHECK: (func $corruption-first-set (type $1) (result i32 i32)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32))
;; CHECK-NEXT: (local.set $tuple
;; CHECK-NEXT: (tuple.make 2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $tuple2
;; CHECK-NEXT: (local.get $tuple)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $tuple)
;; CHECK-NEXT: )
(func $corruption-first-set (result i32 i32)
(local $tuple (tuple i32 i32))
(local $tuple2 (tuple i32 i32))
(local.set $tuple
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
(local.set $tuple2
(local.get $tuple)
)
;; This local.get prevents both locals from being optimized.
(local.get $tuple)
)
;; CHECK: (func $corruption-second-set (type $1) (result i32 i32)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32))
;; CHECK-NEXT: (local.set $tuple
;; CHECK-NEXT: (tuple.make 2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $tuple2
;; CHECK-NEXT: (local.get $tuple)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $tuple2)
;; CHECK-NEXT: )
(func $corruption-second-set (result i32 i32)
(local $tuple (tuple i32 i32))
(local $tuple2 (tuple i32 i32))
(local.set $tuple
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
(local.set $tuple2
(local.get $tuple)
)
(local.get $tuple2) ;; this changed from $tuple; same outcome.
)
;; CHECK: (func $other (type $0)
;; CHECK-NEXT: (local $other f64)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local.set $other
;; CHECK-NEXT: (local.tee $other
;; CHECK-NEXT: (local.get $other)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $other
;; A non-tuple local and all operations on it should be ignored.
(local $other f64)
;; A tuple local with no uses at all should be ignored.
(local $tuple (tuple i32 i32))
(local.set $other
(local.tee $other
(local.get $other)
)
)
)
;; CHECK: (func $make-extract-no-local (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (tuple.extract 2 0
;; CHECK-NEXT: (tuple.make 2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $make-extract-no-local
;; Tuple operations without locals. We do nothing here; other passes can
;; help on this kind of thing.
(drop
(tuple.extract 2 0
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
)
)
;; CHECK: (func $make-extract-no-local-but-other (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (tuple.extract 2 0
;; CHECK-NEXT: (tuple.make 2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $make-extract-no-local-but-other
(local $tuple (tuple i32 i32))
(local.set $tuple
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
;; The code below is as in the previous testcase, but now before us there
;; is an unrelated local that can be optimized. We should remain as before.
(drop
(tuple.extract 2 0
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
)
)
;; CHECK: (func $set-of-block (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local.set $tuple
;; CHECK-NEXT: (block (type $1) (result i32 i32)
;; CHECK-NEXT: (tuple.make 2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $set-of-block
(local $tuple (tuple i32 i32))
;; We do not handle blocks yet, so this is not optimized.
(local.set $tuple
(block (result i32 i32)
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
)
)
;; CHECK: (func $unreachability (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $nontuple f64)
;; CHECK-NEXT: (local.set $tuple
;; CHECK-NEXT: (tuple.make 2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.tee $tuple
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (tuple.extract 2 0
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (tuple.extract 2 1
;; CHECK-NEXT: (local.tee $tuple
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.tee $tuple
;; CHECK-NEXT: (local.tee $nontuple
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $unreachability
(local $tuple (tuple i32 i32))
(local $nontuple f64)
(local.set $tuple
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
;; We should not error here, and do nothing.
(local.set $tuple
(unreachable)
)
(drop
(tuple.extract 2 0
(unreachable)
)
)
(drop
(tuple.extract 2 1
(local.tee $tuple
(unreachable)
)
)
)
;; Teeing a nontuple into a tuple is allowed in unreachable code (the input
;; to the outer tee is simply unreachable, so there is no checking that we
;; are copying a local into another local of a compatible type). We should
;; not error here.
(drop
(local.tee $tuple
(local.tee $nontuple
(unreachable)
)
)
)
)
;; CHECK: (func $tee-chain (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32))
;; CHECK-NEXT: (local $tuple3 (tuple i32 i32))
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (local $4 i32)
;; CHECK-NEXT: (local $5 i32)
;; CHECK-NEXT: (local $6 i32)
;; CHECK-NEXT: (local $7 i32)
;; CHECK-NEXT: (local $8 i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (block
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $7
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $8
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $5
;; CHECK-NEXT: (local.get $7)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $6
;; CHECK-NEXT: (local.get $8)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (local.get $5)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (local.get $6)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $tee-chain
(local $tuple (tuple i32 i32))
(local $tuple2 (tuple i32 i32))
(local $tuple3 (tuple i32 i32))
(drop
(tuple.extract 2 0
(local.tee $tuple
(local.tee $tuple2
(local.tee $tuple3
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
)
)
)
)
)
;; CHECK: (func $chain-3 (type $0)
;; CHECK-NEXT: (local $tuple (tuple i32 i32 i32))
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32 i32))
;; CHECK-NEXT: (local $tuple3 (tuple i32 i32 i32))
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (local $4 i32)
;; CHECK-NEXT: (local $5 i32)
;; CHECK-NEXT: (local $6 i32)
;; CHECK-NEXT: (local $7 i32)
;; CHECK-NEXT: (local $8 i32)
;; CHECK-NEXT: (local $9 i32)
;; CHECK-NEXT: (local $10 i32)
;; CHECK-NEXT: (local $11 i32)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $5
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $6
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $7
;; CHECK-NEXT: (local.get $4)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $8
;; CHECK-NEXT: (local.get $5)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $9
;; CHECK-NEXT: (local.get $6)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $10
;; CHECK-NEXT: (local.get $7)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $11
;; CHECK-NEXT: (local.get $8)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $7)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $11)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $chain-3
(local $tuple (tuple i32 i32 i32))
(local $tuple2 (tuple i32 i32 i32))
(local $tuple3 (tuple i32 i32 i32))
;; A chain of 3 copied tuples.
(local.set $tuple
(tuple.make 3
(i32.const 1)
(i32.const 2)
(i32.const 3)
)
)
(local.set $tuple2
(local.get $tuple)
)
(local.set $tuple3
(local.get $tuple2)
)
;; Read from each.
(drop
(tuple.extract 3 0
(local.get $tuple)
)
)
(drop
(tuple.extract 3 1
(local.get $tuple2)
)
)
(drop
(tuple.extract 3 2
(local.get $tuple3)
)
)
)
;; CHECK: (func $chain-3-corruption (type $2) (result i32 i32 i32)
;; CHECK-NEXT: (local $tuple (tuple i32 i32 i32))
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32 i32))
;; CHECK-NEXT: (local $tuple3 (tuple i32 i32 i32))
;; CHECK-NEXT: (local.set $tuple
;; CHECK-NEXT: (tuple.make 3
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $tuple2
;; CHECK-NEXT: (local.get $tuple)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $tuple3
;; CHECK-NEXT: (local.get $tuple2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (tuple.extract 3 0
;; CHECK-NEXT: (local.get $tuple)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (tuple.extract 3 1
;; CHECK-NEXT: (local.get $tuple2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (tuple.extract 3 2
;; CHECK-NEXT: (local.get $tuple3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $tuple)
;; CHECK-NEXT: )
(func $chain-3-corruption (result i32 i32 i32)
(local $tuple (tuple i32 i32 i32))
(local $tuple2 (tuple i32 i32 i32))
(local $tuple3 (tuple i32 i32 i32))
;; As above, but a get at the very end prevents the entire chain from being
;; optimized.
(local.set $tuple
(tuple.make 3
(i32.const 1)
(i32.const 2)
(i32.const 3)
)
)
(local.set $tuple2
(local.get $tuple)
)
(local.set $tuple3
(local.get $tuple2)
)
;; Read from each.
(drop
(tuple.extract 3 0
(local.get $tuple)
)
)
(drop
(tuple.extract 3 1
(local.get $tuple2)
)
)
(drop
(tuple.extract 3 2
(local.get $tuple3)
)
)
(local.get $tuple) ;; this was added
)
;; CHECK: (func $set-call (type $1) (result i32 i32)
;; CHECK-NEXT: (local $tuple (tuple i32 i32))
;; CHECK-NEXT: (local.set $tuple
;; CHECK-NEXT: (call $set-call)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (tuple.extract 2 0
;; CHECK-NEXT: (local.get $tuple)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (tuple.make 2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $set-call (result i32 i32)
(local $tuple (tuple i32 i32))
;; Setting from a call prevents optimization.
(local.set $tuple
(call $set-call)
)
(drop
(tuple.extract 2 0
(local.get $tuple)
)
)
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
;; CHECK: (func $two-2-three (type $0)
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32))
;; CHECK-NEXT: (local $tuple3 (tuple i32 i32 i32))
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (local $4 i32)
;; CHECK-NEXT: (local $5 i32)
;; CHECK-NEXT: (local $6 i32)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $5
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $6
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $6)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $two-2-three
(local $tuple2 (tuple i32 i32))
(local $tuple3 (tuple i32 i32 i32))
(local.set $tuple2
(tuple.make 2
(i32.const 1)
(i32.const 2)
)
)
(local.set $tuple3
(tuple.make 3
(tuple.extract 2 0
(local.get $tuple2)
)
(tuple.extract 2 1
(local.get $tuple2)
)
(i32.const 3)
)
)
;; Read from each.
(drop
(tuple.extract 2 1
(local.get $tuple2)
)
)
(drop
(tuple.extract 3 2
(local.get $tuple3)
)
)
)
;; CHECK: (func $three-2-two (type $0)
;; CHECK-NEXT: (local $tuple2 (tuple i32 i32))
;; CHECK-NEXT: (local $tuple3 (tuple i32 i32 i32))
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (local $4 i32)
;; CHECK-NEXT: (local $5 i32)
;; CHECK-NEXT: (local $6 i32)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $5
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $6
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; 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: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $6)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $three-2-two
(local $tuple2 (tuple i32 i32))
(local $tuple3 (tuple i32 i32 i32))
(local.set $tuple3
(tuple.make 3
(i32.const 1)
(i32.const 2)
(i32.const 3)
)
)
(local.set $tuple2
(tuple.make 2
(tuple.extract 3 0
(local.get $tuple3)
)
(tuple.extract 3 1
(local.get $tuple3)
)
)
)
;; Read from each.
(drop
(tuple.extract 2 1
(local.get $tuple2)
)
)
(drop
(tuple.extract 3 2
(local.get $tuple3)
)
)
)
;; CHECK: (func $tuple.element.subtyping (type $0)
;; CHECK-NEXT: (local $tuple_null (tuple i32 nullref))
;; CHECK-NEXT: (local $tuple_eq (tuple i32 eqref))
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 nullref)
;; CHECK-NEXT: (local $4 i32)
;; CHECK-NEXT: (local $5 eqref)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $5
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $tuple.element.subtyping
(local $tuple_null (tuple i32 nullref))
(local $tuple_eq (tuple i32 eqref))
;; The tee emits a nullref in the second element, which is written to an
;; element of eqref. That is, the source and the target do not have
;; identical type, which we need to properly handle and not error.
(local.set $tuple_eq
(local.tee $tuple_null
(tuple.make 2
(i32.const 0)
(ref.null none)
)
)
)
)
)