blob: d386e7f34eefbdad12855f4c03b8d3c89fe89949 [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 --remove-unused-names --cfp -all -S -o - | filecheck %s
;; (remove-unused-names is added to test fallthrough values without a block
;; name getting in the way)
(module
;; CHECK: (type $0 (func))
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (func $impossible-get (type $0)
;; CHECK-NEXT: (local $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $impossible-get (local $struct (ref null $struct))
(drop
;; This type is never created, so a get is impossible, and we will trap
;; anyhow. So we can turn this into an unreachable (plus a drop of the
;; reference).
(struct.get $struct 0
(local.get $struct)
)
)
)
)
(module
;; CHECK: (type $struct (struct (field i64)))
(type $struct (struct i64))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i64.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
;; The only place this type is created is with a default value, and so we
;; can optimize the later get into a constant (plus a drop of the ref).
;;
;; (Note that the allocated reference is dropped here, so it is not actually
;; used anywhere, but this pass does not attempt to trace the paths of
;; references escaping and being stored etc. - it just thinks at the type
;; level.)
(drop
(struct.new_default $struct)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
(module
;; CHECK: (type $struct (struct (field f32)))
(type $struct (struct f32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (f32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
;; The only place this type is created is with a constant value, and so we
;; can optimize the later get into a constant (plus a drop of the ref).
(drop
(struct.new $struct
(f32.const 42)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
(module
;; CHECK: (type $struct (struct (field f32)))
(type $struct (struct f32))
;; CHECK: (type $1 (func (param f32 (ref null $struct))))
;; CHECK: (func $test (type $1) (param $f f32) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (local.get $f)
;; 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 $f f32) (param $struct (ref null $struct))
;; The value given is not a constant, and so we cannot optimize.
(drop
(struct.new $struct
(local.get $f)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Create in one function, get in another. The 10 should be forwarded to the
;; get.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func))
;; CHECK: (type $2 (func (param (ref null $struct))))
;; CHECK: (func $create (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct
(i32.const 10)
)
)
)
;; CHECK: (func $get (type $2) (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 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; As before, but with the order of functions reversed to check for any ordering
;; issues.
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (type $2 (func))
;; CHECK: (func $get (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 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
;; CHECK: (func $create (type $2)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct
(i32.const 10)
)
)
)
)
;; Different values assigned in the same function, in different struct.news,
;; so we cannot optimize the struct.get away.
(module
;; CHECK: (type $struct (struct (field f32)))
(type $struct (struct f32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (f32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (f32.const 1337)
;; 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
(f32.const 42)
)
)
(drop
(struct.new $struct
(f32.const 1337)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Different values assigned in different functions, and one is a struct.set.
(module
;; CHECK: (type $struct (struct (field (mut f32))))
(type $struct (struct (mut f32)))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (type $2 (func))
;; CHECK: (func $create (type $2)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (f32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct
(f32.const 42)
)
)
)
;; CHECK: (func $set (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: (f32.const 1337)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $set (param $struct (ref null $struct))
(struct.set $struct 0
(local.get $struct)
(f32.const 1337)
)
)
;; CHECK: (func $get (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 $get (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; As the last testcase, but the values happen to coincide, so we can optimize
;; the get into a constant.
(module
;; CHECK: (type $struct (struct (field (mut f32))))
(type $struct (struct (mut f32)))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (type $2 (func))
;; CHECK: (func $create (type $2)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (f32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct
(f32.const 42)
)
)
)
;; CHECK: (func $set (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: (f32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $set (param $struct (ref null $struct))
(struct.set $struct 0
(local.get $struct)
(f32.const 42) ;; The last testcase had 1337 here.
)
)
;; CHECK: (func $get (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Check that we look into the fallthrough value that is assigned.
(module
;; CHECK: (type $struct (struct (field (mut f32))))
(type $struct (struct (mut f32)))
;; CHECK: (type $1 (func))
;; CHECK: (type $2 (func (param i32 (ref null $struct))))
;; CHECK: (type $3 (func (param (ref null $struct))))
;; CHECK: (func $create (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (block (result f32)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (f32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct
;; Fall though a 42 via a block.
(block (result f32)
(nop)
(f32.const 42)
)
)
)
)
;; CHECK: (func $set (type $2) (param $x i32) (param $struct (ref null $struct))
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: (if (result f32)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (else
;; CHECK-NEXT: (f32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $set (param $x i32) (param $struct (ref null $struct))
(struct.set $struct 0
(local.get $struct)
;; Fall though a 42 via an if.
(if (result f32)
(local.get $x)
(then
(unreachable)
)
(else
(f32.const 42)
)
)
)
)
;; CHECK: (func $get (type $3) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Test a function reference instead of a number.
(module
;; CHECK: (type $struct (struct (field funcref)))
(type $struct (struct funcref))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (elem declare func $test)
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (ref.func $test)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref $1))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.func $test)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.new $struct
(ref.func $test)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Test for unreachable creations, sets, and gets.
(module
(type $struct (struct (mut i32)))
;; CHECK: (type $0 (func))
;; CHECK: (func $test (type $0)
;; CHECK-NEXT: (drop
;; 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: (drop
;; CHECK-NEXT: (block ;; (replaces unreachable StructGet we can't emit)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block ;; (replaces unreachable StructSet we can't emit)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test
(drop
(struct.new $struct
(unreachable)
)
)
(drop
(struct.get $struct 0
(unreachable)
)
)
(struct.set $struct 0
(unreachable)
(i32.const 20)
)
)
)
;; Subtyping: Create a supertype and get a subtype. As we never create a
;; subtype, the get must trap anyhow (the reference it receives can
;; only be null in this closed world).
(module
;; CHECK: (type $struct (sub (struct (field i32))))
(type $struct (sub (struct i32)))
;; CHECK: (type $substruct (sub $struct (struct (field i32))))
(type $substruct (sub $struct (struct i32)))
;; CHECK: (type $2 (func))
;; CHECK: (type $3 (func (param (ref null $substruct))))
;; CHECK: (func $create (type $2)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct
(i32.const 10)
)
)
)
;; CHECK: (func $get (type $3) (param $substruct (ref null $substruct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $substruct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $substruct (ref null $substruct))
(drop
(struct.get $substruct 0
(local.get $substruct)
)
)
)
)
;; As above, but in addition to a new of $struct also add a set. The set could
;; in principle be relevant for the get as far as this pass knows, and so we
;; will optimize the result to the only possible value. (In practice, though,
;; it will trap anyhow.)
(module
;; CHECK: (type $struct (sub (struct (field (mut i32)))))
(type $struct (sub (struct (mut i32))))
;; CHECK: (type $substruct (sub $struct (struct (field (mut i32)))))
(type $substruct (sub $struct (struct (mut i32))))
;; CHECK: (type $2 (func (param (ref null $struct))))
;; CHECK: (type $3 (func (param (ref null $substruct))))
;; CHECK: (func $create (type $2) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create (param $struct (ref null $struct))
(drop
(struct.new $struct
(i32.const 10)
)
)
(struct.set $struct 0
(local.get $struct)
(i32.const 10)
)
)
;; CHECK: (func $get (type $3) (param $substruct (ref null $substruct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $substruct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $substruct (ref null $substruct))
(drop
(struct.get $substruct 0
(local.get $substruct)
)
)
)
)
;; Subtyping: Create a subtype and get a supertype. The get must receive a
;; reference to the subtype (we never create a supertype) and so we
;; can optimize.
(module
;; CHECK: (type $struct (sub (struct (field i32))))
(type $struct (sub (struct i32)))
;; CHECK: (type $1 (func))
;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
(type $substruct (sub $struct (struct i32 f64)))
;; CHECK: (type $3 (func (param (ref null $struct))))
;; CHECK: (func $create (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $substruct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $substruct
(i32.const 10)
(f64.const 3.14159)
)
)
)
;; CHECK: (func $get (type $3) (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 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Subtyping: Create both a subtype and a supertype, with identical constants
;; for the shared field, and get the supertype.
(module
;; CHECK: (type $struct (sub (struct (field i32))))
(type $struct (sub (struct i32)))
;; CHECK: (type $1 (func))
;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
(type $substruct (sub $struct (struct i32 f64)))
;; CHECK: (type $3 (func (param (ref null $struct))))
;; CHECK: (func $create (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $substruct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct
(i32.const 10)
)
)
(drop
(struct.new $substruct
(i32.const 10)
(f64.const 3.14159)
)
)
)
;; CHECK: (func $get (type $3) (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 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Subtyping: Create both a subtype and a supertype, with different constants
;; for the shared field, preventing optimization, as a get of the
;; supertype may receive an instance of the subtype.
;;
;; Note that this may be optimized using a ref.test, in --cfp-reftest, but not
;; in --cfp. This gives us coverage that --cfp does not do the things that
;; --cfp-reftest does (how --cfp-reftest works is tested in cfp-reftest.wast).
(module
;; CHECK: (type $struct (sub (struct (field i32))))
(type $struct (sub (struct i32)))
;; CHECK: (type $1 (func))
;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
(type $substruct (sub $struct (struct i32 f64)))
;; CHECK: (type $3 (func (param (ref null $struct))))
;; CHECK: (func $create (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $substruct
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct
(i32.const 10)
)
)
(drop
(struct.new $substruct
(i32.const 20)
(f64.const 3.14159)
)
)
)
;; CHECK: (func $get (type $3) (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 $get (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
;; Subtyping: Create both a subtype and a supertype, with different constants
;; for the shared field, but get from the subtype. The field is
;; shared between the types, but we only create the substruct with
;; one value, so we can optimize.
(module
;; CHECK: (type $struct (sub (struct (field i32))))
(type $struct (sub (struct i32)))
;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
(type $substruct (sub $struct (struct i32 f64)))
;; CHECK: (type $2 (func))
;; CHECK: (type $3 (func (param (ref null $substruct))))
;; CHECK: (func $create (type $2)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $substruct
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct
(i32.const 10)
)
)
(drop
(struct.new $substruct
(i32.const 20)
(f64.const 3.14159)
)
)
)
;; CHECK: (func $get (type $3) (param $substruct (ref null $substruct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $substruct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $substruct (ref null $substruct))
(drop
(struct.get $substruct 0
(local.get $substruct)
)
)
)
)
;; As above, but add a set of $struct. The set prevents the optimization.
(module
;; CHECK: (type $struct (sub (struct (field (mut i32)))))
(type $struct (sub (struct (mut i32))))
;; CHECK: (type $substruct (sub $struct (struct (field (mut i32)) (field f64))))
(type $substruct (sub $struct (struct (mut i32) f64)))
;; CHECK: (type $2 (func (param (ref null $struct))))
;; CHECK: (type $3 (func (param (ref null $substruct))))
;; CHECK: (func $create (type $2) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $substruct
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create (param $struct (ref null $struct))
(drop
(struct.new $struct
(i32.const 10)
)
)
(struct.set $struct 0
(local.get $struct)
(i32.const 10)
)
(drop
(struct.new $substruct
(i32.const 20)
(f64.const 3.14159)
)
)
)
;; CHECK: (func $get (type $3) (param $substruct (ref null $substruct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $substruct 0
;; CHECK-NEXT: (local.get $substruct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $substruct (ref null $substruct))
(drop
(struct.get $substruct 0
(local.get $substruct)
)
)
)
)
;; Multi-level subtyping, check that we propagate not just to the immediate
;; supertype but all the way as needed.
(module
;; CHECK: (type $struct1 (sub (struct (field i32))))
(type $struct1 (sub (struct i32)))
;; CHECK: (type $struct2 (sub $struct1 (struct (field i32) (field f64))))
(type $struct2 (sub $struct1 (struct i32 f64)))
;; CHECK: (type $struct3 (sub $struct2 (struct (field i32) (field f64) (field anyref))))
(type $struct3 (sub $struct2 (struct i32 f64 anyref)))
;; CHECK: (type $3 (func))
;; CHECK: (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3))))
;; CHECK: (func $create (type $3)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct3
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct3
(i32.const 20)
(f64.const 3.14159)
(ref.null none)
)
)
)
;; CHECK: (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
;; Get field 0 from the $struct1. This can be optimized to a constant
;; since we only ever created an instance of struct3 with a constant there.
(drop
(struct.get $struct1 0
(local.get $struct1)
)
)
;; Get both fields of $struct2.
(drop
(struct.get $struct2 0
(local.get $struct2)
)
)
(drop
(struct.get $struct2 1
(local.get $struct2)
)
)
;; Get all 3 fields of $struct3
(drop
(struct.get $struct3 0
(local.get $struct3)
)
)
(drop
(struct.get $struct3 1
(local.get $struct3)
)
)
(drop
(struct.get $struct3 2
(local.get $struct3)
)
)
)
)
;; Multi-level subtyping with conflicts. The even-numbered fields will get
;; different values in the sub-most type. Create the top and bottom types, but
;; not the middle one.
(module
;; CHECK: (type $struct1 (sub (struct (field i32) (field i32))))
(type $struct1 (sub (struct i32 i32)))
;; CHECK: (type $struct2 (sub $struct1 (struct (field i32) (field i32) (field f64) (field f64))))
(type $struct2 (sub $struct1 (struct i32 i32 f64 f64)))
;; CHECK: (type $struct3 (sub $struct2 (struct (field i32) (field i32) (field f64) (field f64) (field anyref) (field anyref))))
(type $struct3 (sub $struct2 (struct i32 i32 f64 f64 anyref anyref)))
;; CHECK: (type $3 (func (param anyref)))
;; CHECK: (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3))))
;; CHECK: (func $create (type $3) (param $any anyref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct1
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct3
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: (i32.const 999)
;; CHECK-NEXT: (f64.const 2.71828)
;; CHECK-NEXT: (f64.const 9.9999999)
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: (local.get $any)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create (param $any anyref)
(drop
(struct.new $struct1
(i32.const 10)
(i32.const 20)
)
)
(drop
(struct.new $struct3
(i32.const 10)
(i32.const 999) ;; use a different value here
(f64.const 2.71828)
(f64.const 9.9999999)
(ref.null any)
(local.get $any) ;; use a non-constant value here, which can never be
;; optimized.
)
)
)
;; CHECK: (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct1 1
;; CHECK-NEXT: (local.get $struct1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 999)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f64.const 2.71828)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f64.const 9.9999999)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 999)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f64.const 2.71828)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f64.const 9.9999999)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct3 5
;; CHECK-NEXT: (local.get $struct3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
;; Get all the fields of all the structs.
(drop
(struct.get $struct1 0
(local.get $struct1)
)
)
(drop
(struct.get $struct1 1
(local.get $struct1)
)
)
(drop
(struct.get $struct2 0
(local.get $struct2)
)
)
(drop
(struct.get $struct2 1
(local.get $struct2)
)
)
(drop
(struct.get $struct2 2
(local.get $struct2)
)
)
(drop
(struct.get $struct2 3
(local.get $struct2)
)
)
(drop
(struct.get $struct3 0
(local.get $struct3)
)
)
(drop
(struct.get $struct3 1
(local.get $struct3)
)
)
(drop
(struct.get $struct3 2
(local.get $struct3)
)
)
(drop
(struct.get $struct3 3
(local.get $struct3)
)
)
(drop
(struct.get $struct3 4
(local.get $struct3)
)
)
(drop
(struct.get $struct3 5
(local.get $struct3)
)
)
)
)
;; Multi-level subtyping with a different value in the middle of the chain. We
;; can only optimize $struct3.
(module
;; CHECK: (type $struct1 (sub (struct (field (mut i32)))))
(type $struct1 (sub (struct (mut i32))))
;; CHECK: (type $struct2 (sub $struct1 (struct (field (mut i32)) (field f64))))
(type $struct2 (sub $struct1 (struct (mut i32) f64)))
;; CHECK: (type $struct3 (sub $struct2 (struct (field (mut i32)) (field f64) (field anyref))))
(type $struct3 (sub $struct2 (struct (mut i32) f64 anyref)))
;; CHECK: (type $3 (func))
;; CHECK: (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3))))
;; CHECK: (func $create (type $3)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct1
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct2
;; CHECK-NEXT: (i32.const 9999)
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct3
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct1
(i32.const 10)
)
)
(drop
(struct.new $struct2
(i32.const 9999) ;; use a different value here
(f64.const 0)
)
)
(drop
(struct.new $struct3
(i32.const 10)
(f64.const 0)
(ref.null any)
)
)
)
;; CHECK: (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct1 0
;; CHECK-NEXT: (local.get $struct1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct2 0
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
;; Get field 0 in all the types.
(drop
(struct.get $struct1 0
(local.get $struct1)
)
)
(drop
(struct.get $struct2 0
(local.get $struct2)
)
)
(drop
(struct.get $struct3 0
(local.get $struct3)
)
)
)
)
;; As above, but add not just a new of the middle class with a different value
;; but also a set. That prevents all optimizations.
(module
;; CHECK: (type $struct1 (sub (struct (field (mut i32)))))
(type $struct1 (sub (struct (mut i32))))
;; CHECK: (type $struct2 (sub $struct1 (struct (field (mut i32)) (field f64))))
(type $struct2 (sub $struct1 (struct (mut i32) f64)))
;; CHECK: (type $struct3 (sub $struct2 (struct (field (mut i32)) (field f64) (field anyref))))
(type $struct3 (sub $struct2 (struct (mut i32) f64 anyref)))
;; CHECK: (type $3 (func (param (ref null $struct2))))
;; CHECK: (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3))))
;; CHECK: (func $create (type $3) (param $struct2 (ref null $struct2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct1
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct2
;; CHECK-NEXT: (i32.const 9999)
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct2 0
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: (i32.const 9999)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct3
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create (param $struct2 (ref null $struct2))
(drop
(struct.new $struct1
(i32.const 10)
)
)
(drop
(struct.new $struct2
(i32.const 9999) ;; use a different value here
(f64.const 0)
)
)
(struct.set $struct2 0
(local.get $struct2)
(i32.const 9999) ;; use a different value here
)
(drop
(struct.new $struct3
(i32.const 10)
(f64.const 0)
(ref.null any)
)
)
)
;; CHECK: (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct1 0
;; CHECK-NEXT: (local.get $struct1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct2 0
;; CHECK-NEXT: (local.get $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct3 0
;; CHECK-NEXT: (local.get $struct3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
;; Get field 0 in all the types.
(drop
(struct.get $struct1 0
(local.get $struct1)
)
)
(drop
(struct.get $struct2 0
(local.get $struct2)
)
)
(drop
(struct.get $struct3 0
(local.get $struct3)
)
)
)
)
;; Test for a struct with multiple fields, some of which are constant and hence
;; optimizable, and some not. Also test that some have the same type.
(module
;; CHECK: (type $struct (struct (field i32) (field f64) (field i32) (field f64) (field i32)))
(type $struct (struct i32 f64 i32 f64 i32))
;; CHECK: (type $1 (func))
;; CHECK: (type $2 (func (param (ref null $struct))))
;; CHECK: (func $create (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (i32.eqz
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: (f64.abs
;; CHECK-NEXT: (f64.const 2.71828)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 30)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $struct
(i32.eqz (i32.const 10)) ;; not a constant (as far as this pass knows)
(f64.const 3.14159)
(i32.const 20)
(f64.abs (f64.const 2.71828)) ;; not a constant
(i32.const 30)
)
)
)
;; CHECK: (func $get (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: (drop
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; 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 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 3
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; 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 30)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; 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 30)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $struct (ref null $struct))
(drop
(struct.get $struct 0
(local.get $struct)
)
)
(drop
(struct.get $struct 1
(local.get $struct)
)
)
(drop
(struct.get $struct 2
(local.get $struct)
)
)
(drop
(struct.get $struct 3
(local.get $struct)
)
)
(drop
(struct.get $struct 4
(local.get $struct)
)
)
;; Also test for multiple gets of the same field.
(drop
(struct.get $struct 4
(local.get $struct)
)
)
)
)
;; Never create A, but have a set to its field. A subtype B has no creates nor
;; sets, and the final subtype C has a create and a get. The set to A should
;; apply to it, preventing optimization.
(module
;; CHECK: (type $A (sub (struct (field (mut i32)))))
(type $A (sub (struct (mut i32))))
;; CHECK: (type $B (sub $A (struct (field (mut i32)))))
(type $B (sub $A (struct (mut i32))))
;; CHECK: (type $C (sub $B (struct (field (mut i32)))))
(type $C (sub $B (struct (mut i32))))
;; CHECK: (type $3 (func))
;; CHECK: (type $4 (func (param (ref $A))))
;; CHECK: (type $5 (func (param (ref $C))))
;; CHECK: (func $create (type $3)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $C
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $create
(drop
(struct.new $C
(i32.const 10)
)
)
)
;; CHECK: (func $set (type $4) (param $a (ref $A))
;; CHECK-NEXT: (struct.set $A 0
;; CHECK-NEXT: (local.get $a)
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $set (param $a (ref $A))
(struct.set $A 0
(local.get $a)
(i32.const 20)
)
)
;; CHECK: (func $get (type $5) (param $c (ref $C))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $C 0
;; CHECK-NEXT: (local.get $c)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $get (param $c (ref $C))
(drop
(struct.get $C 0
(local.get $c)
)
)
)
)
;; Copies of a field to itself can be ignored. As a result, we can optimize both
;; of the gets here.
(module
;; CHECK: (type $struct (struct (field (mut i32))))
(type $struct (struct (mut i32)))
;; CHECK: (type $1 (func (param (ref null $struct) (ref null $struct))))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) (param $other (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $struct)
;; 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 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $other)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct)) (param $other (ref null $struct))
(drop
(struct.new_default $struct)
)
;; This copy does not actually introduce any new possible values, and so it
;; remains true that the only possible value is the default.
(struct.set $struct 0
(local.get $struct)
(struct.get $struct 0
(local.get $struct)
)
)
(drop
(struct.get $struct 0
(local.get $other)
)
)
)
)
;; Test of a near-copy, of a similar looking field (same index, and same field
;; type) but in a different struct.
(module
;; CHECK: (type $struct (struct (field (mut f32)) (field (mut i32))))
(type $struct (struct (mut f32) (mut i32)))
;; CHECK: (type $other (struct (field (mut f64)) (field (mut i32))))
(type $other (struct (mut f64) (mut i32)))
;; CHECK: (type $2 (func (param (ref null $struct) (ref null $other))))
;; CHECK: (func $test (type $2) (param $struct (ref null $struct)) (param $other (ref null $other))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 1
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $other)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 1
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct)) (param $other (ref null $other))
(drop
(struct.new_default $struct)
)
;; As this is not a copy, we cannot optimize struct.1's get lower down.
(struct.set $struct 1
(local.get $struct)
(struct.get $other 1
(local.get $other)
)
)
(drop
(struct.get $struct 1
(local.get $struct)
)
)
)
)
;; Test of a near-copy, of a different index.
(module
;; CHECK: (type $struct (struct (field (mut i32)) (field (mut i32))))
(type $struct (struct (mut i32) (mut i32)))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $struct)
;; 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 0)
;; 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_default $struct)
)
;; As this is not a copy, we cannot optimize struct.0's get lower down.
(struct.set $struct 0
(local.get $struct)
(struct.get $struct 1
(local.get $struct)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global i32 (i32.const 42))
(global $global i32 (i32.const 42))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (global.get $global)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; 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: (global.get $global)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
;; An immutable global is the only thing written to this field, so we can
;; propagate the value to the struct.get and replace it with a global.get.
(drop
(struct.new $struct
(global.get $global)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
(module
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global (mut i32) (i32.const 42))
(global $global (mut i32) (i32.const 42))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (global.get $global)
;; 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))
;; As above, but the global is *not* immutable, so we cannot optimize.
(drop
(struct.new $struct
(global.get $global)
)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
(module
;; CHECK: (type $struct (struct (field (mut i32))))
(type $struct (struct (mut i32)))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global i32 (i32.const 42))
(global $global i32 (i32.const 42))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (global.get $global)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: (global.get $global)
;; CHECK-NEXT: )
;; 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: (global.get $global)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $struct (ref null $struct))
(drop
(struct.new $struct
(global.get $global)
)
)
;; As above, but there is another set of the field. It is the same, though,
;; so that is fine. Also, the struct's field is now mutable as well to allow
;; that, and that also does not prevent optimization.
(struct.set $struct 0
(local.get $struct)
(global.get $global)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
(module
;; CHECK: (type $struct (struct (field (mut i32))))
(type $struct (struct (mut i32)))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global i32 (i32.const 42))
(global $global i32 (i32.const 42))
;; CHECK: (global $global-2 i32 (i32.const 1337))
(global $global-2 i32 (i32.const 1337))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (global.get $global)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: (global.get $global-2)
;; 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
(global.get $global)
)
)
;; As above, but set a different global, which prevents optimization.
(struct.set $struct 0
(local.get $struct)
(global.get $global-2)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
(module
;; CHECK: (type $struct (struct (field (mut i32))))
(type $struct (struct (mut i32)))
;; CHECK: (type $1 (func (param (ref null $struct))))
;; CHECK: (global $global i32 (i32.const 42))
(global $global i32 (i32.const 42))
;; CHECK: (global $global-2 i32 (i32.const 1337))
(global $global-2 i32 (i32.const 1337))
;; CHECK: (func $test (type $1) (param $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (global.get $global)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $struct 0
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: (i32.const 1337)
;; 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
(global.get $global)
)
)
;; As above, but set a constant, which means we are mixing constants with
;; globals, which prevents the optimization.
(struct.set $struct 0
(local.get $struct)
(i32.const 1337)
)
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
)
(module
;; Test a global type other than i32. Arrays of structs are a realistic case
;; as they are used to implement itables.
;; CHECK: (type $vtable (struct (field funcref)))
(type $vtable (struct funcref))
;; CHECK: (type $itable (array (ref $vtable)))
(type $itable (array (ref $vtable)))
;; CHECK: (type $object (struct (field $itable (ref $itable))))
(type $object (struct (field $itable (ref $itable))))
;; CHECK: (type $3 (func (param (ref null $object)) (result funcref)))
;; CHECK: (global $global (ref $itable) (array.new_fixed $itable 2
;; CHECK-NEXT: (struct.new $vtable
;; CHECK-NEXT: (ref.null nofunc)
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.new $vtable
;; CHECK-NEXT: (ref.func $test)
;; CHECK-NEXT: )
;; CHECK-NEXT: ))
(global $global (ref $itable) (array.new_fixed $itable 2
(struct.new $vtable
(ref.null func)
)
(struct.new $vtable
(ref.func $test)
)
))
;; CHECK: (func $test (type $3) (param $object (ref null $object)) (result funcref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $object
;; CHECK-NEXT: (global.get $global)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $vtable 0
;; CHECK-NEXT: (array.get $itable
;; CHECK-NEXT: (block (result (ref $itable))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $object)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $global)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $object (ref null $object)) (result funcref)
(drop
(struct.new $object
(global.get $global)
)
)
;; Realistic usage of an itable: read an item from it, then a func from
;; that, and return the value (all verifying that the types are correct
;; after optimization). Note how after optimization everything is lined up
;; so that precompute-propagate can infer from the global.get the specific
;; object the array.get is on, allowing us to emit a constant value for the
;; outer struct.get in principle.
(struct.get $vtable 0
(array.get $itable
(struct.get $object $itable
(local.get $object)
)
(i32.const 1)
)
)
)
)
;; Test we handle packed fields properly.
(module
(rec
;; CHECK: (type $0 (func))
;; CHECK: (rec
;; CHECK-NEXT: (type $A_8 (struct (field i8)))
(type $A_8 (struct (field i8)))
;; CHECK: (type $A_16 (struct (field i16)))
(type $A_16 (struct (field i16)))
;; CHECK: (type $B_16 (struct (field i16)))
(type $B_16 (struct (field i16)))
)
;; CHECK: (import "a" "b" (global $g i32))
(import "a" "b" (global $g i32))
;; CHECK: (func $test (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (struct.new $A_8
;; CHECK-NEXT: (i32.const 305419896)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (i32.const 305419896)
;; CHECK-NEXT: (i32.const 255)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (struct.new $A_16
;; CHECK-NEXT: (i32.const 305419896)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (i32.const 305419896)
;; CHECK-NEXT: (i32.const 65535)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (struct.new $B_16
;; CHECK-NEXT: (global.get $g)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (global.get $g)
;; CHECK-NEXT: (i32.const 65535)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test
;; We can infer values here, but must add proper masks, as the inputs get
;; truncated during packing.
(drop
(struct.get_u $A_8 0
(struct.new $A_8
(i32.const 0x12345678)
)
)
)
(drop
(struct.get_u $A_16 0
(struct.new $A_16
(i32.const 0x12345678)
)
)
)
;; Also test reading a value from an imported global, which is an unknown
;; value at compile time, but which we know must be masked as well.
(drop
(struct.get_u $B_16 0
(struct.new $B_16
(global.get $g)
)
)
)
)
;; CHECK: (func $test_signed (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (struct.new $A_8
;; CHECK-NEXT: (i32.const 305419896)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.shr_s
;; CHECK-NEXT: (i32.shl
;; CHECK-NEXT: (i32.const 305419896)
;; CHECK-NEXT: (i32.const 24)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 24)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (struct.new $A_16
;; CHECK-NEXT: (i32.const 305419896)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.shr_s
;; CHECK-NEXT: (i32.shl
;; CHECK-NEXT: (i32.const 305419896)
;; CHECK-NEXT: (i32.const 16)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 16)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (struct.new $B_16
;; CHECK-NEXT: (global.get $g)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.shr_s
;; CHECK-NEXT: (i32.shl
;; CHECK-NEXT: (global.get $g)
;; CHECK-NEXT: (i32.const 16)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 16)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test_signed
;; As above, but with signed gets.
(drop
(struct.get_s $A_8 0
(struct.new $A_8
(i32.const 0x12345678)
)
)
)
(drop
(struct.get_s $A_16 0
(struct.new $A_16
(i32.const 0x12345678)
)
)
)
(drop
(struct.get_s $B_16 0
(struct.new $B_16
(global.get $g)
)
)
)
)
)
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (struct (field (mut i32)))))
(type $A (sub (struct (field (mut i32)))))
;; CHECK: (type $B (sub $A (struct (field (mut i32)))))
(type $B (sub $A (struct (field (mut i32)))))
)
;; CHECK: (type $2 (func (param i32) (result i32)))
;; CHECK: (func $test (type $2) (param $0 i32) (result i32)
;; CHECK-NEXT: (local $A (ref $A))
;; CHECK-NEXT: (local $B (ref $B))
;; CHECK-NEXT: (struct.set $A 0
;; CHECK-NEXT: (select (result (ref null $A))
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: (local.tee $B
;; CHECK-NEXT: (struct.new $B
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $A 0
;; CHECK-NEXT: (struct.new $A
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $B 0
;; CHECK-NEXT: (local.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $0 i32) (result i32)
(local $A (ref $A))
(local $B (ref $B))
;; This set is part of a copy, as the value is a struct.get. The copy is on
;; $A, but the reference we operate on is an instance of $B, actually. So
;; the value read at the end is in fact 10. That is, this test verifies
;; that we track the copied value even though the copy is on $A but it
;; affects $B.
(struct.set $A 0
;; This select is used to keep the type that reaches the struct.set $A,
;; and not $B, so it looks like a perfect copy of $A->$A.
(select (result (ref null $A))
(ref.null $A)
(local.tee $B
(struct.new $B
(i32.const 20)
)
)
(i32.const 0)
)
(struct.get $A 0
(struct.new $A
(i32.const 10)
)
)
)
;; This should not turn into 20, since just based on values flowing to
;; fields we cannot infer that (the value will be 10, but CFP cannot infer
;; that either - that would require tracking references through locals
;; etc.).
(struct.get $B 0
(local.get $B)
)
)
)
;; A type with two subtypes. A copy on the parent can affect either child.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (struct (field (mut i32)))))
(type $A (sub (struct (field (mut i32)))))
;; CHECK: (type $B1 (sub $A (struct (field (mut i32)))))
(type $B1 (sub $A (struct (field (mut i32)))))
;; CHECK: (type $B2 (sub $A (struct (field (mut i32)))))
(type $B2 (sub $A (struct (field (mut i32)))))
)
;; CHECK: (type $3 (func (param (ref null $A) (ref null $B1) (ref null $B2))))
;; CHECK: (func $test (type $3) (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $A
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $B1
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $B2
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $A 0
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: (struct.get $A 0
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $A 0
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B1 0
;; CHECK-NEXT: (local.get $B1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B2 0
;; CHECK-NEXT: (local.get $B2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
;; A and B1 agree on their value in their construction.
(drop
(struct.new $A
(i32.const 10)
)
)
(drop
(struct.new $B1
(i32.const 10)
)
)
(drop
(struct.new $B2
(i32.const 20) ;; this value is different from the others
)
)
;; Copy on $A
(struct.set $A 0
(local.get $A)
(struct.get $A 0
(local.get $A)
)
)
;; $A might read either child, so we can't infer.
(drop
(struct.get $A 0
(local.get $A)
)
)
;; $B1 should be only able to read 10, but the copy opens the possibility
;; of 20, so we can't optimize.
(drop
(struct.get $B1 0
(local.get $B1)
)
)
;; As with $B1, the copy stops us.
(drop
(struct.get $B2 0
(local.get $B2)
)
)
)
)
;; As above, but now the copy is on B1.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (struct (field (mut i32)))))
(type $A (sub (struct (field (mut i32)))))
;; CHECK: (type $B1 (sub $A (struct (field (mut i32)))))
(type $B1 (sub $A (struct (field (mut i32)))))
;; CHECK: (type $B2 (sub $A (struct (field (mut i32)))))
(type $B2 (sub $A (struct (field (mut i32)))))
)
;; CHECK: (type $3 (func (param (ref null $A) (ref null $B1) (ref null $B2))))
;; CHECK: (func $test (type $3) (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $A
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $B1
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $B2
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $B1 0
;; CHECK-NEXT: (local.get $B1)
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $B1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $A 0
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $B1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B2 0
;; CHECK-NEXT: (local.get $B2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
(drop
(struct.new $A
(i32.const 10)
)
)
(drop
(struct.new $B1
(i32.const 10)
)
)
(drop
(struct.new $B2
(i32.const 20)
)
)
;; This changed from $A to $B1.
(struct.set $B1 0
(local.get $B1)
(struct.get $B1 0
(local.get $B1)
)
)
;; This might still read $B1 or $B2, with different values, so we can't
;; optimize.
(drop
(struct.get $A 0
(local.get $A)
)
)
;; The copy can only refer to $B1, so we can optimize here.
(drop
(struct.get $B1 0
(local.get $B1)
)
)
;; The copy can't refer to a $B2, so we can optimize here. TODO (but GUFA
;; can do this)
(drop
(struct.get $B2 0
(local.get $B2)
)
)
)
)
;; As above, but now the copy is on B2.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (struct (field (mut i32)))))
(type $A (sub (struct (field (mut i32)))))
;; CHECK: (type $B1 (sub $A (struct (field (mut i32)))))
(type $B1 (sub $A (struct (field (mut i32)))))
;; CHECK: (type $B2 (sub $A (struct (field (mut i32)))))
(type $B2 (sub $A (struct (field (mut i32)))))
)
;; CHECK: (type $3 (func (param (ref null $A) (ref null $B1) (ref null $B2))))
;; CHECK: (func $test (type $3) (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $A
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $B1
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $B2
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $B2 0
;; CHECK-NEXT: (local.get $B2)
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $B2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $A 0
;; CHECK-NEXT: (local.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B1 0
;; CHECK-NEXT: (local.get $B1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (local.get $B2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
(drop
(struct.new $A
(i32.const 10)
)
)
(drop
(struct.new $B1
(i32.const 10)
)
)
(drop
(struct.new $B2
(i32.const 20)
)
)
;; This changed from $A to $B2.
(struct.set $B2 0
(local.get $B2)
(struct.get $B2 0
(local.get $B2)
)
)
;; This might still read $B1 or $B2, with different values, so we can't
;; optimize.
(drop
(struct.get $A 0
(local.get $A)
)
)
;; The copy can't refer to a $B1, so we can optimize here. TODO (but GUFA
;; can do this)
(drop
(struct.get $B1 0
(local.get $B1)
)
)
;; $B2 is copied to itself, and nothing else, so we can optimize here.
(drop
(struct.get $B2 0
(local.get $B2)
)
)
)
)