blob: 299b19e00f2e3989b70a3e0abbb8b605d5d4fc57 [file] [log] [blame] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --gsi -all --closed-world -S -o - | filecheck %s
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; A non-reference global does not confuse us.
;; CHECK: (global $global-other i32 (i32.const 123456))
(global $global-other i32 (i32.const 123456))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
;; We can infer that this get can reference either $global1 or $global2,
;; and nothing else (aside from a null), and can emit a select between
;; those values.
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; As above, but now the field is mutable, so we cannot optimize.
(module
;; CHECK: (type $struct (struct (field (mut i32))))
(type $struct (struct (mut i32)))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Just one global.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $struct1 (struct (field i32)))
(type $struct1 (struct i32))
;; CHECK: (type $struct2 (struct (field i32)))
(type $struct2 (struct i32))
)
;; CHECK: (type $2 (func (param (ref null $struct1) (ref null $struct2))))
;; CHECK: (import "a" "b" (global $imported i32))
(import "a" "b" (global $imported i32))
;; CHECK: (global $global1 (ref $struct1) (struct.new $struct1
;; CHECK-NEXT: (global.get $imported)
;; CHECK-NEXT: ))
(global $global1 (ref $struct1) (struct.new $struct1
(global.get $imported)
))
;; CHECK: (global $global2 (ref $struct2) (struct.new $struct2
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global2 (ref $struct2) (struct.new $struct2
(i32.const 42)
))
;; CHECK: (func $test1 (type $2) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct1 0
;; CHECK-NEXT: (block (result (ref $struct1))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct2 0
;; CHECK-NEXT: (block (result (ref $struct2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test1 (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
;; We can infer that this get must reference $global1 and make the reference
;; point to that. Note that we do not infer the value of 42 here, but leave
;; it for other passes to do.
(drop
(struct.get $struct1 0
(local.get $struct1)
)
)
;; Even though the value here is not known at compile time - it reads an
;; imported global - we can still infer that we are reading from $global2.
(drop
(struct.get $struct2 0
(local.get $struct2)
)
)
)
)
;; Three globals. For now, we do not optimize here.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (global $global3 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 99999)
;; CHECK-NEXT: ))
(global $global3 (ref $struct) (struct.new $struct
(i32.const 99999)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Three globals, as above, but now two agree on their values. We can optimize
;; by comparing to the one that has a single value.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (global $global3 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global3 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; As above, but move the different value of the three to the middle.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global3 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global3 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; As above, but move the different value of the three to the end.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (global $global3 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global3 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Four values, two pairs of equal ones. We do not optimize this.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global3 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global3 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (global $global4 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global4 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Four values, three equal and one unique. We can optimize this with a single
;; comparison on the unique one.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global3 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global3 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (global $global4 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global4 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; A struct.new inside a function stops us from optimizing.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.new $struct
(i32.const 1)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; A struct.new in a non-toplevel position in a global stops us from
;; optimizing.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $tuple (struct (field anyref) (field anyref)))
(type $tuple (struct anyref anyref))
;; CHECK: (type $2 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (global $global-tuple (ref $tuple) (struct.new $tuple
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 999999)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: ))
(global $global-tuple (ref $tuple) (struct.new $tuple
(struct.new $struct
(i32.const 999999)
)
(ref.null any)
))
;; CHECK: (func $test (type $2) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; As above, but remove the struct.new in a nested position, while keeping all
;; the other stuff in the above test. Now we should optimize.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $tuple (struct (field anyref) (field anyref)))
(type $tuple (struct anyref anyref))
;; CHECK: (type $2 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (global $global-tuple (ref $tuple) (struct.new $tuple
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: ))
(global $global-tuple (ref $tuple) (struct.new $tuple
(ref.null any)
(ref.null any)
))
;; CHECK: (func $test (type $2) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; When one of the globals is mutable, we cannot optimize.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (mut (ref $struct)) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (mut (ref $struct)) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; A subtype is not optimizable, which prevents $struct from being optimized.
(module
;; CHECK: (type $struct (sub (struct (field i32))))
(type $struct (sub (struct i32)))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (type $sub-struct (sub $struct (struct (field i32))))
(type $sub-struct (sub $struct (struct i32)))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $sub-struct
;; CHECK-NEXT: (i32.const 999999)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.new $sub-struct
(i32.const 999999)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; A *super*-type is not optimizable, but that does not block us, and we can
;; optimize.
(module
;; CHECK: (type $super-struct (sub (struct (field i32))))
(type $super-struct (sub (struct i32)))
;; CHECK: (type $struct (sub $super-struct (struct (field i32))))
(type $struct (sub $super-struct (struct i32)))
;; CHECK: (type $2 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (func $test (type $2) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $super-struct
;; CHECK-NEXT: (i32.const 999999)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.new $super-struct
(i32.const 999999)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; One global for each of the type and the subtype. The optimization will pick
;; between their 2 values.
(module
;; CHECK: (type $super-struct (sub (struct (field i32))))
(type $super-struct (sub (struct i32)))
;; CHECK: (type $struct (sub $super-struct (struct (field i32))))
(type $struct (sub $super-struct (struct i32)))
;; CHECK: (type $2 (func (param (ref null $struct) (ref null $super-struct))))
;; CHECK: (global $global1 (ref $super-struct) (struct.new $super-struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $super-struct) (struct.new $super-struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (func $test (type $2) (param $struct (ref null $struct)) (param $super-struct (ref null $super-struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (block (result (ref $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $super-struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct)) (param $super-struct (ref null $super-struct))
;; The first has just one global, which we switch the reference to, while
;; the second will consider the struct and sub-struct, find 2 possible
;; values, and optimize.
(drop
(struct.get $struct 0
(local.get $struct)
)
)
(drop
(struct.get $super-struct 0
(local.get $super-struct)
)
)
)
)
;; One global has a non-constant field. We can still optimize, if we move that
;; field out into another global, that is, if we un-nest it. The select will
;; then pick either the constant or a global.get of the new un-nested global.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1.unnested.0 i32 (i32.add
;; CHECK-NEXT: (i32.const 41)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: ))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (global.get $global1.unnested.0)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.add
(i32.const 41)
(i32.const 1)
)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (global.get $global1.unnested.0)
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; As above, but with the globals flipped. Now the second global has a non-
;; constant field.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global2.unnested.0 i32 (i32.add
;; CHECK-NEXT: (i32.const 41)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: ))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 1337)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (global.get $global2.unnested.0)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.add
(i32.const 41)
(i32.const 1)
)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (global.get $global2.unnested.0)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; As above, but now both globals have non-constant fields. We un-nest both.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1.unnested.0 i32 (i32.add
;; CHECK-NEXT: (i32.const 13)
;; CHECK-NEXT: (i32.const 37)
;; CHECK-NEXT: ))
;; CHECK: (global $global2.unnested.0 i32 (i32.add
;; CHECK-NEXT: (i32.const 41)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: ))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (global.get $global1.unnested.0)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.add
(i32.const 13)
(i32.const 37)
)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (global.get $global2.unnested.0)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.add
(i32.const 41)
(i32.const 1)
)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (global.get $global1.unnested.0)
;; CHECK-NEXT: (global.get $global2.unnested.0)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Multiple and overlapping un-nesting situations.
(module
;; CHECK: (type $struct (struct (field i32) (field i32)))
(type $struct (struct i32 i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1.unnested.0 i32 (i32.add
;; CHECK-NEXT: (i32.const 13)
;; CHECK-NEXT: (i32.const 37)
;; CHECK-NEXT: ))
;; CHECK: (global $global2.unnested.0 i32 (i32.add
;; CHECK-NEXT: (i32.const 41)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: ))
;; CHECK: (global $global1.unnested.1 i32 (i32.add
;; CHECK-NEXT: (i32.const 99)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: ))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (global.get $global1.unnested.0)
;; CHECK-NEXT: (global.get $global1.unnested.1)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.add
(i32.const 13)
(i32.const 37)
)
(i32.add
(i32.const 99)
(i32.const 1)
)
))
;; CHECK: (global $global2.unnested.1 i32 (i32.add
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: ))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (global.get $global2.unnested.0)
;; CHECK-NEXT: (global.get $global2.unnested.1)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.add
(i32.const 41)
(i32.const 1)
)
(i32.add
(i32.const 100)
(i32.const 2)
)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (global.get $global1.unnested.0)
;; CHECK-NEXT: (global.get $global2.unnested.0)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (global.get $global1.unnested.0)
;; CHECK-NEXT: (global.get $global2.unnested.0)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
;; We only need to un-nest once for these two.
(drop
(struct.get $struct 0
(local.get $struct)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
;; CHECK: (func $test2 (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (global.get $global1.unnested.0)
;; CHECK-NEXT: (global.get $global2.unnested.0)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (global.get $global1.unnested.1)
;; CHECK-NEXT: (global.get $global2.unnested.1)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (global.get $global1.unnested.1)
;; CHECK-NEXT: (global.get $global2.unnested.1)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test2 (param $struct (ref null $struct))
;; Add another get of 0 in another function.
(drop
(struct.get $struct 0
(local.get $struct)
)
)
;; Add gets of the second field in the struct.
(drop
(struct.get $struct 1
(local.get $struct)
)
)
(drop
(struct.get $struct 1
(local.get $struct)
)
)
)
)
;; Three globals with non-constant fields. We do not optimize as we cannot pick
;; between three values with a single comparison.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.add
(i32.const 42)
(i32.const 0)
)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.add
(i32.const 1337)
(i32.const 0)
)
))
;; CHECK: (global $global3 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (i32.const 99999)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: ))
(global $global3 (ref $struct) (struct.new $struct
(i32.add
(i32.const 99999)
(i32.const 0)
)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; As above, but now two of the three's non-constant fields are identical. That
;; does not help us: they are still non-constant, and we do nothing. (But, other
;; passes might simplify things by un-nesting the identical code.)
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.add
(i32.const 42)
(i32.const 0)
)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.add
(i32.const 42)
(i32.const 0)
)
))
;; CHECK: (global $global3 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (i32.const 99999)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: ))
(global $global3 (ref $struct) (struct.new $struct
(i32.add
(i32.const 99999)
(i32.const 0)
)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; One global each for two subtypes of a common supertype, and one for the
;; supertype.
(module
;; CHECK: (type $super-struct (sub (struct (field i32))))
(type $super-struct (sub (struct i32)))
;; CHECK: (type $struct1 (sub $super-struct (struct (field i32) (field f32))))
(type $struct1 (sub $super-struct (struct i32 f32)))
;; CHECK: (type $struct2 (sub $super-struct (struct (field i32) (field f64))))
(type $struct2 (sub $super-struct (struct i32 f64)))
;; CHECK: (type $3 (func (param (ref null $super-struct) (ref null $struct1) (ref null $struct2))))
;; CHECK: (global $global0 (ref $super-struct) (struct.new $super-struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global0 (ref $super-struct) (struct.new $super-struct
(i32.const 42)
))
;; CHECK: (global $global1 (ref $struct1) (struct.new $struct1
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (f32.const 3.141590118408203)
;; CHECK-NEXT: ))
(global $global1 (ref $struct1) (struct.new $struct1
(i32.const 1337)
(f32.const 3.14159)
))
;; CHECK: (global $global2 (ref $struct2) (struct.new $struct2
;; CHECK-NEXT: (i32.const 99999)
;; CHECK-NEXT: (f64.const 2.71828)
;; CHECK-NEXT: ))
(global $global2 (ref $struct2) (struct.new $struct2
(i32.const 99999)
(f64.const 2.71828)
))
;; CHECK: (func $test (type $3) (param $super-struct (ref null $super-struct)) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $super-struct 0
;; CHECK-NEXT: (local.get $super-struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct1 0
;; CHECK-NEXT: (block (result (ref $struct1))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct2 0
;; CHECK-NEXT: (block (result (ref $struct2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $super-struct (ref null $super-struct)) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
;; This has three possible values due to the two children, so we do not
;; optimize.
(drop
(struct.get $super-struct 0
(local.get $super-struct)
)
)
;; These each have one possible value, which we can switch the references
;; to.
(drop
(struct.get $struct1 0
(local.get $struct1)
)
)
(drop
(struct.get $struct2 0
(local.get $struct2)
)
)
)
)
;; As above, but now the subtypes each have 2 values, and we can optimize.
(module
;; CHECK: (type $super-struct (sub (struct (field i32))))
(type $super-struct (sub (struct i32)))
;; CHECK: (type $struct1 (sub $super-struct (struct (field i32) (field f32))))
(type $struct1 (sub $super-struct (struct i32 f32)))
;; CHECK: (type $struct2 (sub $super-struct (struct (field i32) (field f64))))
(type $struct2 (sub $super-struct (struct i32 f64)))
;; CHECK: (type $3 (func (param (ref null $super-struct) (ref null $struct1) (ref null $struct2))))
;; CHECK: (global $global0 (ref $super-struct) (struct.new $super-struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global0 (ref $super-struct) (struct.new $super-struct
(i32.const 42)
))
;; CHECK: (global $global1 (ref $struct1) (struct.new $struct1
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (f32.const 3.141590118408203)
;; CHECK-NEXT: ))
(global $global1 (ref $struct1) (struct.new $struct1
(i32.const 1337)
(f32.const 3.14159)
))
;; CHECK: (global $global1b (ref $struct1) (struct.new $struct1
;; CHECK-NEXT: (i32.const 1338)
;; CHECK-NEXT: (f32.const 3.141590118408203)
;; CHECK-NEXT: ))
(global $global1b (ref $struct1) (struct.new $struct1
(i32.const 1338)
(f32.const 3.14159)
))
;; CHECK: (global $global2 (ref $struct2) (struct.new $struct2
;; CHECK-NEXT: (i32.const 99999)
;; CHECK-NEXT: (f64.const 2.71828)
;; CHECK-NEXT: ))
(global $global2 (ref $struct2) (struct.new $struct2
(i32.const 99999)
(f64.const 2.71828)
))
;; CHECK: (global $global2b (ref $struct2) (struct.new $struct2
;; CHECK-NEXT: (i32.const 99998)
;; CHECK-NEXT: (f64.const 2.71828)
;; CHECK-NEXT: ))
(global $global2b (ref $struct2) (struct.new $struct2
(i32.const 99998)
(f64.const 2.71828)
))
;; CHECK: (func $test (type $3) (param $super-struct (ref null $super-struct)) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $super-struct 0
;; CHECK-NEXT: (local.get $super-struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (i32.const 1338)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 99999)
;; CHECK-NEXT: (i32.const 99998)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $super-struct (ref null $super-struct)) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
;; This still cannot be optimized.
(drop
(struct.get $super-struct 0
(local.get $super-struct)
)
)
;; These can be optimized, and will be different from one another.
(drop
(struct.get $struct1 0
(local.get $struct1)
)
)
(drop
(struct.get $struct2 0
(local.get $struct2)
)
)
)
)
;; Multiple globals, but all the same value, so we do not even need a select and
;; can just apply the value.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(i32.const 42)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; One global is declared as heap type |any|, which we cannot do a ref.eq on, so
;; we do not optimize.
(module
;; CHECK: (type $A (struct (field i32)))
(type $A (struct (field i32)))
;; CHECK: (type $1 (func (param (ref null $A)) (result i32)))
;; CHECK: (global $A0 (ref any) (struct.new $A
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $A0 (ref any) (struct.new $A
(i32.const 1337)
))
;; CHECK: (global $A1 (ref $A) (struct.new $A
;; CHECK-NEXT: (i32.const 9999)
;; CHECK-NEXT: ))
(global $A1 (ref $A) (struct.new $A
(i32.const 9999)
))
;; CHECK: (func $func (type $1) (param $ref (ref null $A)) (result i32)
;; CHECK-NEXT: (struct.get $A 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $ref (ref null $A)) (result i32)
(struct.get $A 0
(local.get $ref)
)
)
)
;; As above, but now there is just a single global. Again, we should not
;; optimize because the global is not declared as a struct type (which means we
;; cannot do a struct.get on a global.get of that global - we'd need a cast; it
;; is simpler to not optimize here and let other passes first refine the global
;; type).
(module
;; CHECK: (type $A (struct (field i32)))
(type $A (struct (field i32)))
;; CHECK: (type $1 (func (param (ref null $A)) (result i32)))
;; CHECK: (global $A0 (ref any) (struct.new $A
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $A0 (ref any) (struct.new $A
(i32.const 1337)
))
;; CHECK: (func $func (type $1) (param $ref (ref null $A)) (result i32)
;; CHECK-NEXT: (struct.get $A 0
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $ref (ref null $A)) (result i32)
(struct.get $A 0
(local.get $ref)
)
)
)
(module
;; CHECK: (type $A (struct (field i32)))
(type $A (struct (field i32)))
;; CHECK: (type $1 (func (param (ref null $A)) (result i32)))
;; CHECK: (global $A0 (ref $A) (struct.new $A
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $A0 (ref $A) (struct.new $A
(i32.const 1337)
))
;; CHECK: (func $func (type $1) (param $ref (ref null $A)) (result i32)
;; CHECK-NEXT: (block ;; (replaces unreachable StructGet we can't emit)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $ref (ref null $A)) (result i32)
;; Test that we do not error when we see a struct.get of a bottom type.
(struct.get $A 0
(ref.null none)
)
)
)
;; Two subtypes, each with a global. A get of the parent can be optimized into
;; a select, as it must read one of the children.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (struct (field i32))))
(type $struct (sub (struct i32)))
;; CHECK: (type $sub-struct1 (sub $struct (struct (field i32))))
(type $sub-struct1 (sub $struct (struct i32)))
;; CHECK: (type $sub-struct2 (sub $struct (struct (field i32))))
(type $sub-struct2 (sub $struct (struct i32)))
)
;; CHECK: (type $3 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $sub-struct1) (struct.new $sub-struct1
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $sub-struct1) (struct.new $sub-struct1
(i32.const 42)
))
;; CHECK: (global $global2 (ref $sub-struct2) (struct.new $sub-struct2
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $sub-struct2) (struct.new $sub-struct2
(i32.const 1337)
))
;; CHECK: (func $test (type $3) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; As above, but now the parent is unoptimizable due to a struct.new in a
;; function. We must not optimize here to a select.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (struct (field i32))))
(type $struct (sub (struct i32)))
;; CHECK: (type $sub-struct1 (sub $struct (struct (field i32))))
(type $sub-struct1 (sub $struct (struct i32)))
;; CHECK: (type $sub-struct2 (sub $struct (struct (field i32))))
(type $sub-struct2 (sub $struct (struct i32)))
)
;; CHECK: (type $3 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $sub-struct1) (struct.new $sub-struct1
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $sub-struct1) (struct.new $sub-struct1
(i32.const 42)
))
;; CHECK: (global $global2 (ref $sub-struct2) (struct.new $sub-struct2
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $sub-struct2) (struct.new $sub-struct2
(i32.const 1337)
))
;; CHECK: (func $test (type $3) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 999999)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.new $struct
(i32.const 999999)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; As above, the struct.new in a function is of a subtype. Again, we cannot
;; optimize, as unoptimizability spreads to supertypes.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (struct (field i32))))
(type $struct (sub (struct i32)))
;; CHECK: (type $sub-struct1 (sub $struct (struct (field i32))))
(type $sub-struct1 (sub $struct (struct i32)))
;; CHECK: (type $sub-struct2 (sub $struct (struct (field i32))))
(type $sub-struct2 (sub $struct (struct i32)))
)
;; CHECK: (type $3 (func (param (ref null $struct))))
;; CHECK: (global $global1 (ref $sub-struct1) (struct.new $sub-struct1
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: ))
(global $global1 (ref $sub-struct1) (struct.new $sub-struct1
(i32.const 42)
))
;; CHECK: (global $global2 (ref $sub-struct2) (struct.new $sub-struct2
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: ))
(global $global2 (ref $sub-struct2) (struct.new $sub-struct2
(i32.const 1337)
))
;; CHECK: (func $test (type $3) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $sub-struct1
;; CHECK-NEXT: (i32.const 999999)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.new $sub-struct1
(i32.const 999999)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (struct (field funcref))))
(type $A (sub (struct (field funcref))))
;; CHECK: (type $B (sub $A (struct (field (ref func)))))
(type $B (sub $A (struct (field (ref func)))))
)
;; CHECK: (type $2 (func (param (ref null $A) (ref null $B)) (result funcref)))
;; CHECK: (global $global (ref $B) (struct.new $B
;; CHECK-NEXT: (ref.func $func)
;; CHECK-NEXT: ))
(global $global (ref $B) (struct.new $B
(ref.func $func)
))
;; CHECK: (func $func (type $2) (param $a (ref null $A)) (param $b (ref null $B)) (result funcref)
;; CHECK-NEXT: (struct.get $B 0
;; CHECK-NEXT: (block (result (ref $B))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.tee $a
;; CHECK-NEXT: (local.get $b)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $a (ref null $A)) (param $b (ref null $B)) (result funcref)
(struct.get $A 0
;; We can infer that we read from $global here, since it is the only place
;; a $B is created (the tee in the middle to $A does not confuse us).
;; After that, the struct.get will be reading a global.get of $global,
;; which is of type $B, and compared to $A from before we will read a more
;; refined type from the field, ref func vs funcref, which must be updated
;; in the IR.
(local.tee $a
(local.get $b)
)
)
)
)
;; Test that we can optimize global.get operations on immutable globals.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $one i32 (i32.const 1))
(global $one i32 (i32.const 1))
;; CHECK: (global $two i32 (i32.const 2))
(global $two i32 (i32.const 2))
;; CHECK: (global $global1 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (global.get $one)
;; CHECK-NEXT: ))
(global $global1 (ref $struct) (struct.new $struct
(global.get $one)
))
;; CHECK: (global $global2 (ref $struct) (struct.new $struct
;; CHECK-NEXT: (global.get $two)
;; CHECK-NEXT: ))
(global $global2 (ref $struct) (struct.new $struct
(global.get $two)
))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select
;; CHECK-NEXT: (global.get $one)
;; CHECK-NEXT: (global.get $two)
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
;; The get here will read one of the two globals, so we can use a select.
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Test packed fields.
(module
;; CHECK: (type $struct (struct (field i8)))
(type $struct (struct (field i8)))
;; CHECK: (type $1 (func (result i32)))
;; CHECK: (global $A (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 257)
;; CHECK-NEXT: ))
(global $A (ref $struct) (struct.new $struct
(i32.const 257)
))
;; CHECK: (global $B (ref $struct) (struct.new $struct
;; CHECK-NEXT: (i32.const 258)
;; CHECK-NEXT: ))
(global $B (ref $struct) (struct.new $struct
(i32.const 258)
))
;; CHECK: (func $test (type $1) (result i32)
;; CHECK-NEXT: (select
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (i32.const 257)
;; CHECK-NEXT: (i32.const 255)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (i32.const 258)
;; CHECK-NEXT: (i32.const 255)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (global.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (result i32)
;; We can infer this value is one of two things since only two objects exist
;; of this type. We must emit the proper truncated value for them, as the
;; values are truncated into i8.
(struct.get_u $struct 0
(global.get $A)
)
)
)