blob: 5090307879253f36cb8737ff4d2a8a652c6c5286 [file] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt -all --gufa --closed-world -S -o - | filecheck %s
;; $struct is our main test class. $desc is $struct's descriptor.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (descriptor $desc) (struct (field i32) (field i32) (field i32))))
(type $struct (sub (descriptor $desc) (struct (field i32) (field i32) (field i32))))
;; CHECK: (type $desc (sub (describes $struct) (struct (field funcref))))
(type $desc (sub (describes $struct) (struct (field funcref))))
)
;; CHECK: (type $2 (func (result i32)))
;; CHECK: (type $3 (func))
;; CHECK: (global $desc (ref (exact $desc)) (struct.new $desc
;; CHECK-NEXT: (ref.func $func)
;; CHECK-NEXT: ))
(global $desc (ref (exact $desc)) (struct.new $desc
(ref.func $func)
))
;; CHECK: (global $struct (ref $struct) (struct.new_desc $struct
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: (i32.const 200)
;; CHECK-NEXT: (i32.const 300)
;; CHECK-NEXT: (global.get $desc)
;; CHECK-NEXT: ))
(global $struct (ref $struct) (struct.new_desc $struct
(i32.const 100)
(i32.const 200)
(i32.const 300)
(global.get $desc)
))
;; CHECK: (elem declare func $func)
;; CHECK: (export "desc" (func $desc))
;; CHECK: (export "struct" (func $struct))
;; CHECK: (func $func (type $2) (result i32)
;; CHECK-NEXT: (i32.const -1)
;; CHECK-NEXT: )
(func $func (result i32)
(i32.const -1)
)
;; CHECK: (func $desc (type $3)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $desc)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $struct
;; CHECK-NEXT: (global.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $2)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $desc)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $struct
;; CHECK-NEXT: (global.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.func $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $desc (export "desc")
;; Show we can infer the descriptor.
(drop
(ref.get_desc $struct
(global.get $struct)
)
)
;; Show we can read from the descriptor.
(drop
(struct.get $desc 0
(ref.get_desc $struct
(global.get $struct)
)
)
)
)
;; CHECK: (func $struct (type $3)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 200)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 300)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $struct (export "struct")
;; Show we don't disrupt normal struct field inference. Field 1 is
;; particuarly interesting as we represent descriptors as field -1
;; internally.
(drop
(struct.get $struct 0
(global.get $struct)
)
)
(drop
(struct.get $struct 1
(global.get $struct)
)
)
(drop
(struct.get $struct 2
(global.get $struct)
)
)
)
)
;; As above, but now we have a parent and subtype of $struct. The parent has no
;; descriptor, while the subtype does.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $parent (sub (struct (field i32) (field i32) (field i32))))
(type $parent (sub (struct (field i32) (field i32) (field i32))))
;; CHECK: (type $struct (sub $parent (descriptor $desc) (struct (field i32) (field i32) (field i32))))
(type $struct (sub $parent (descriptor $desc) (struct (field i32) (field i32) (field i32))))
;; CHECK: (type $desc (sub (describes $struct) (struct (field funcref))))
(type $desc (sub (describes $struct) (struct (field funcref))))
;; CHECK: (type $sub (sub $struct (descriptor $subdesc) (struct (field i32) (field i32) (field i32))))
(type $sub (sub $struct (descriptor $subdesc) (struct (field i32) (field i32) (field i32))))
;; CHECK: (type $subdesc (sub $desc (describes $sub) (struct (field funcref))))
(type $subdesc (sub $desc (describes $sub) (struct (field funcref))))
)
;; CHECK: (type $5 (func))
;; CHECK: (type $6 (func (result i32)))
;; CHECK: (global $parent (ref $parent) (struct.new $parent
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: (i32.const 200)
;; CHECK-NEXT: (i32.const 300)
;; CHECK-NEXT: ))
(global $parent (ref $parent) (struct.new $parent
(i32.const 10) ;; disagrees with the child
(i32.const 200) ;; agrees with the child
(i32.const 300) ;; agrees with the child
))
;; CHECK: (global $desc (ref (exact $desc)) (struct.new $desc
;; CHECK-NEXT: (ref.func $func)
;; CHECK-NEXT: ))
(global $desc (ref (exact $desc)) (struct.new $desc
(ref.func $func)
))
;; CHECK: (global $struct (ref $struct) (struct.new_desc $struct
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: (i32.const 200)
;; CHECK-NEXT: (i32.const 300)
;; CHECK-NEXT: (global.get $desc)
;; CHECK-NEXT: ))
(global $struct (ref $struct) (struct.new_desc $struct
(i32.const 100)
(i32.const 200)
(i32.const 300)
(global.get $desc)
))
;; CHECK: (global $subdesc (ref (exact $subdesc)) (struct.new $subdesc
;; CHECK-NEXT: (ref.func $func)
;; CHECK-NEXT: ))
(global $subdesc (ref (exact $subdesc)) (struct.new $subdesc
(ref.func $func) ;; agrees with parent
))
;; CHECK: (elem declare func $func)
;; CHECK: (export "desc" (func $desc))
;; CHECK: (export "parent" (func $parent))
;; CHECK: (export "sub" (func $sub))
;; CHECK: (export "struct" (func $struct))
;; CHECK: (func $func (type $6) (result i32)
;; CHECK-NEXT: (i32.const -1)
;; CHECK-NEXT: )
(func $func (result i32)
(i32.const -1)
)
;; CHECK: (func $desc (type $5)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $struct
;; CHECK-NEXT: (global.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $6)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $struct
;; CHECK-NEXT: (global.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.func $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $desc (export "desc")
;; We cannot infer the descriptor: the subtype has a different one.
(drop
(ref.get_desc $struct
(global.get $struct)
)
)
;; While we can't infer the descriptor, we *can* infer the value in the
;; descriptor, as all descriptors have the same value.
(drop
(struct.get $desc 0
(ref.get_desc $struct
(global.get $struct)
)
)
)
)
;; CHECK: (func $parent (type $5)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $parent 0
;; CHECK-NEXT: (global.get $parent)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 200)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $parent 2
;; CHECK-NEXT: (global.get $parent)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $parent (export "parent")
;; Show we don't disrupt the parent's inference. We can infer field 1, which
;; is in agreement among all possible subtypes.
(drop
(struct.get $parent 0
(global.get $parent)
)
)
(drop
(struct.get $parent 1
(global.get $parent)
)
)
(drop
(struct.get $parent 2
(global.get $parent)
)
)
)
;; CHECK: (func $sub (type $5)
;; CHECK-NEXT: (local $temp (ref $sub))
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (struct.new_desc $sub
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: (i32.const 200)
;; CHECK-NEXT: (i32.const 333)
;; CHECK-NEXT: (global.get $subdesc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $subdesc)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $sub
;; CHECK-NEXT: (local.get $temp)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $subdesc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 200)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 333)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $sub (export "sub")
(local $temp (ref $sub))
;; Test $sub, and for variety, not using globals.
(local.set $temp
(struct.new_desc $sub
(i32.const 100) ;; agrees with parent
(i32.const 200) ;; agrees with parent
(i32.const 333) ;; disagrees with parent
(global.get $subdesc)
)
)
;; We can infer the descriptor.
(drop
(ref.get_desc $sub
(local.get $temp)
)
)
;; We can infer all these.
(drop
(struct.get $sub 0
(local.get $temp)
)
)
(drop
(struct.get $sub 1
(local.get $temp)
)
)
(drop
(struct.get $sub 2
(local.get $temp)
)
)
)
;; CHECK: (func $struct (type $5)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 200)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 2
;; CHECK-NEXT: (global.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $struct (export "struct")
;; We can infer the first two.
(drop
(struct.get $struct 0
(global.get $struct)
)
)
(drop
(struct.get $struct 1
(global.get $struct)
)
)
(drop
(struct.get $struct 2
(global.get $struct)
)
)
)
)
;; As above, but now the descriptor's funcref fields do not agree, so we cannot
;; infer the value there.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (descriptor $desc) (struct (field i32) (field i32) (field i32))))
(type $struct (sub (descriptor $desc) (struct (field i32) (field i32) (field i32))))
;; CHECK: (type $desc (sub (describes $struct) (struct (field funcref))))
(type $desc (sub (describes $struct) (struct (field funcref))))
;; CHECK: (type $sub (sub $struct (descriptor $subdesc) (struct (field i32) (field i32) (field i32))))
(type $sub (sub $struct (descriptor $subdesc) (struct (field i32) (field i32) (field i32))))
;; CHECK: (type $subdesc (sub $desc (describes $sub) (struct (field funcref))))
(type $subdesc (sub $desc (describes $sub) (struct (field funcref))))
)
;; CHECK: (type $4 (func (result i32)))
;; CHECK: (type $5 (func))
;; CHECK: (global $desc (ref (exact $desc)) (struct.new $desc
;; CHECK-NEXT: (ref.func $func)
;; CHECK-NEXT: ))
(global $desc (ref (exact $desc)) (struct.new $desc
(ref.func $func)
))
;; CHECK: (global $struct (ref $struct) (struct.new_desc $struct
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: (i32.const 200)
;; CHECK-NEXT: (i32.const 300)
;; CHECK-NEXT: (global.get $desc)
;; CHECK-NEXT: ))
(global $struct (ref $struct) (struct.new_desc $struct
(i32.const 100)
(i32.const 200)
(i32.const 300)
(global.get $desc)
))
;; CHECK: (global $subdesc (ref (exact $subdesc)) (struct.new $subdesc
;; CHECK-NEXT: (ref.func $test)
;; CHECK-NEXT: ))
(global $subdesc (ref (exact $subdesc)) (struct.new $subdesc
(ref.func $test) ;; disagrees with parent
))
;; CHECK: (elem declare func $func)
;; CHECK: (export "test" (func $test))
;; CHECK: (func $func (type $4) (result i32)
;; CHECK-NEXT: (i32.const -1)
;; CHECK-NEXT: )
(func $func (result i32)
(i32.const -1)
)
;; CHECK: (func $test (type $5)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_desc $sub
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: (i32.const 200)
;; CHECK-NEXT: (i32.const 333)
;; CHECK-NEXT: (global.get $subdesc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $struct
;; CHECK-NEXT: (global.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $desc)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $struct
;; CHECK-NEXT: (ref.cast (ref (exact $struct))
;; CHECK-NEXT: (global.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $desc 0
;; CHECK-NEXT: (ref.get_desc $struct
;; CHECK-NEXT: (global.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $4)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $desc)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $struct
;; CHECK-NEXT: (ref.cast (ref (exact $struct))
;; CHECK-NEXT: (global.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.func $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (export "test")
(drop
(struct.new_desc $sub
(i32.const 100) ;; agrees with parent
(i32.const 200) ;; agrees with parent
(i32.const 333) ;; disagrees with parent
(global.get $subdesc)
)
)
;; We cannot infer the descriptor due to the subtype.
(drop
(ref.get_desc $struct
(global.get $struct)
)
)
;; Using an exact $struct, we can.
(drop
(ref.get_desc $struct
(ref.cast (ref (exact $struct))
(global.get $struct)
)
)
)
;; We cannot infer from the descriptor due to the different descriptor
;; subtypes.
(drop
(struct.get $desc 0
(ref.get_desc $struct
(global.get $struct)
)
)
)
;; Using an exact $struct, we can.
(drop
(struct.get $desc 0
(ref.get_desc $struct
(ref.cast (ref (exact $struct))
(global.get $struct)
)
)
)
)
)
)
;; Descriptor chain.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (descriptor $B) (struct (field i32)))
(type $A (descriptor $B) (struct (field i32)))
;; CHECK: (type $B (describes $A) (descriptor $C) (struct (field i32)))
(type $B (describes $A) (descriptor $C) (struct (field i32)))
;; CHECK: (type $C (describes $B) (struct (field i32)))
(type $C (describes $B) (struct (field i32)))
)
;; CHECK: (type $3 (func))
;; CHECK: (global $C (ref (exact $C)) (struct.new $C
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: ))
(global $C (ref (exact $C)) (struct.new $C
(i32.const 10)
))
;; CHECK: (global $B (ref (exact $B)) (struct.new_desc $B
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: (global.get $C)
;; CHECK-NEXT: ))
(global $B (ref (exact $B)) (struct.new_desc $B
(i32.const 20)
(global.get $C)
))
;; CHECK: (global $A (ref (exact $A)) (struct.new_desc $A
;; CHECK-NEXT: (i32.const 30)
;; CHECK-NEXT: (global.get $B)
;; CHECK-NEXT: ))
(global $A (ref (exact $A)) (struct.new_desc $A
(i32.const 30)
(global.get $B)
))
;; CHECK: (export "test" (func $test))
;; CHECK: (func $test (type $3)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $B)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $A
;; CHECK-NEXT: (global.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $C)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $B
;; CHECK-NEXT: (global.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $C)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref (exact $C)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $B
;; CHECK-NEXT: (block (result (ref (exact $B)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.get_desc $A
;; CHECK-NEXT: (global.get $A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $B)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $C)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test (export "test")
;; We can infer these.
(drop
(ref.get_desc $A
(global.get $A)
)
)
(drop
(ref.get_desc $B
(global.get $B)
)
)
;; We can do so all at once too.
(drop
(struct.get $C 0
(ref.get_desc $B
(ref.get_desc $A
(global.get $A)
)
)
)
)
)
)