blob: 3483d1c9de3ef6968ff3e654ea50846fd43e0a5b [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 -all --minimize-rec-groups -S -o - | filecheck %s
;; A module with no heap types at all should be ok.
(module
;; CHECK: (global $g i32 (i32.const 0))
(global $g i32 (i32.const 0))
)
;; A module with a single heap type should be ok.
(module
;; CHECK: (type $t (struct))
(type $t (struct))
;; CHECK: (global $g (ref null $t) (ref.null none))
(global $g (ref null $t) (ref.null none))
)
;; Split a rec group containing independent types
(module
(rec
;; CHECK: (type $a (struct (field i32)))
(type $a (struct (field i32)))
;; CHECK: (type $b (struct (field i64)))
(type $b (struct (field i64)))
;; CHECK: (type $c (struct (field f32)))
(type $c (struct (field f32)))
)
;; CHECK: (global $a (ref null $a) (ref.null none))
(global $a (ref null $a) (ref.null none))
;; CHECK: (global $b (ref null $b) (ref.null none))
(global $b (ref null $b) (ref.null none))
;; CHECK: (global $c (ref null $c) (ref.null none))
(global $c (ref null $c) (ref.null none))
)
;; Split a rec group containing types that depend on each other but belong to
;; different SCCs.
(module
(rec
;; CHECK: (type $a (struct))
(type $a (struct))
;; CHECK: (type $b (struct (field (ref $a))))
(type $b (struct (field (ref $a))))
;; CHECK: (type $c (struct (field (ref $b))))
(type $c (struct (field (ref $b))))
)
;; CHECK: (global $a (ref null $a) (ref.null none))
(global $a (ref null $a) (ref.null none))
;; CHECK: (global $b (ref null $b) (ref.null none))
(global $b (ref null $b) (ref.null none))
;; CHECK: (global $c (ref null $c) (ref.null none))
(global $c (ref null $c) (ref.null none))
)
;; Reverse the order of the previous case. The output should still be in a valid
;; order.
(module
(rec
;; CHECK: (type $a (struct))
;; CHECK: (type $b (struct (field (ref $a))))
;; CHECK: (type $c (struct (field (ref $b))))
(type $c (struct (field (ref $b))))
(type $b (struct (field (ref $a))))
(type $a (struct))
)
;; CHECK: (global $c (ref null $c) (ref.null none))
(global $c (ref null $c) (ref.null none))
;; CHECK: (global $b (ref null $b) (ref.null none))
(global $b (ref null $b) (ref.null none))
;; CHECK: (global $a (ref null $a) (ref.null none))
(global $a (ref null $a) (ref.null none))
)
;; Now all the types are in the same SCC.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $c (struct (field (ref $a))))
;; CHECK: (type $b (struct (field (ref $c))))
;; CHECK: (type $a (struct (field (ref $b))))
(type $a (struct (field (ref $b))))
(type $b (struct (field (ref $c))))
(type $c (struct (field (ref $a))))
)
;; CHECK: (global $a (ref null $a) (ref.null none))
(global $a (ref null $a) (ref.null none))
)
;; Only two of the types are in the same SCC.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $b (struct (field (ref $a))))
;; CHECK: (type $a (struct (field (ref $b))))
(type $a (struct (field (ref $b))))
(type $b (struct (field (ref $a))))
;; CHECK: (type $c (struct (field (ref $a))))
(type $c (struct (field (ref $a))))
)
;; CHECK: (global $c (ref null $c) (ref.null none))
(global $c (ref null $c) (ref.null none))
)
;; Same, but change which two are in the SCC. The output order should still be
;; valid.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $c (struct (field (ref $b))))
;; CHECK: (type $b (struct (field (ref $c))))
;; CHECK: (type $a (struct (field (ref $b))))
(type $a (struct (field (ref $b))))
(type $b (struct (field (ref $c))))
(type $c (struct (field (ref $b))))
)
;; CHECK: (global $a (ref null $a) (ref.null none))
(global $a (ref null $a) (ref.null none))
)
;; Two types that are in conflicting SCCs should be disambiguated. In this case
;; there are no different permutations, so we use a brand.
(module
(rec
;; CHECK: (type $a (func))
(type $a (func))
;; CHECK: (rec
;; CHECK-NEXT: (type $1 (struct))
;; CHECK: (type $b (func))
(type $b (func))
)
;; CHECK: (global $a (ref null $a) (ref.null nofunc))
(global $a (ref null $a) (ref.null nofunc))
;; CHECK: (global $b (ref null $b) (ref.null nofunc))
(global $b (ref null $b) (ref.null nofunc))
)
;; Same as above, but now the types match the initial brand, so we have to skip
;; to the next one.
(module
(rec
;; CHECK: (type $a (struct))
(type $a (struct))
;; CHECK: (rec
;; CHECK-NEXT: (type $1 (array (mut i8)))
;; CHECK: (type $b (struct))
(type $b (struct))
)
;; CHECK: (global $a (ref null $a) (ref.null none))
(global $a (ref null $a) (ref.null none))
;; CHECK: (global $b (ref null $b) (ref.null none))
(global $b (ref null $b) (ref.null none))
)
;; Now we have groups that match both the initial brand and the next one, so
;; adding the brand will cause a conflict. We will have to go to the next brand.
(module
(rec
;; CHECK: (type $a1 (struct))
(type $a1 (struct))
;; CHECK: (type $b1 (array (mut i8)))
;; CHECK: (rec
;; CHECK-NEXT: (type $2 (array (mut i8)))
;; CHECK: (type $a2 (struct))
(type $a2 (struct))
;; CHECK: (rec
;; CHECK-NEXT: (type $a3 (struct))
(type $a3 (struct))
(type $b1 (array (mut i8)))
;; CHECK: (type $5 (array (mut i8)))
;; CHECK: (rec
;; CHECK-NEXT: (type $6 (array i8))
;; CHECK: (type $b2 (array (mut i8)))
(type $b2 (array (mut i8)))
)
;; CHECK: (global $a1 (ref null $a1) (ref.null none))
(global $a1 (ref null $a1) (ref.null none))
;; CHECK: (global $a2 (ref null $a2) (ref.null none))
(global $a2 (ref null $a2) (ref.null none))
;; CHECK: (global $a3 (ref null $a3) (ref.null none))
(global $a3 (ref null $a3) (ref.null none))
;; CHECK: (global $b1 (ref null $b1) (ref.null none))
(global $b1 (ref null $b1) (ref.null none))
;; CHECK: (global $b2 (ref null $b2) (ref.null none))
(global $b2 (ref null $b2) (ref.null none))
)
;; Now the types have more fields, including one referring to a previous SCC.
(module
(rec
;; CHECK: (type $other (struct (field i32)))
(type $other (struct (field i32)))
;; CHECK: (type $a (struct (field anyref) (field i32) (field (ref $other))))
(type $a (struct (field anyref i32 (ref $other))))
;; CHECK: (rec
;; CHECK-NEXT: (type $2 (struct))
;; CHECK: (type $b (struct (field anyref) (field i32) (field (ref $other))))
(type $b (struct (field anyref i32 (ref $other))))
)
;; CHECK: (global $a (ref null $a) (ref.null none))
(global $a (ref null $a) (ref.null none))
;; CHECK: (global $b (ref null $b) (ref.null none))
(global $b (ref null $b) (ref.null none))
)
;; Now there is a third type and we can disambiguate it by using a different
;; permutation with the same brand.
(module
(rec
;; CHECK: (type $a (struct))
(type $a (struct))
;; CHECK: (rec
;; CHECK-NEXT: (type $1 (array (mut i8)))
;; CHECK: (type $b (struct))
(type $b (struct))
;; CHECK: (rec
;; CHECK-NEXT: (type $c (struct))
(type $c (struct))
)
;; CHECK: (type $4 (array (mut i8)))
;; CHECK: (global $a (ref null $a) (ref.null none))
(global $a (ref null $a) (ref.null none))
;; CHECK: (global $b (ref null $b) (ref.null none))
(global $b (ref null $b) (ref.null none))
;; CHECK: (global $c (ref null $c) (ref.null none))
(global $c (ref null $c) (ref.null none))
)
;; Adding a fourth type requires using yet another brand.
(module
(rec
;; CHECK: (type $a (struct))
(type $a (struct))
;; CHECK: (rec
;; CHECK-NEXT: (type $1 (array (mut i8)))
;; CHECK: (type $b (struct))
(type $b (struct))
;; CHECK: (rec
;; CHECK-NEXT: (type $c (struct))
(type $c (struct))
;; CHECK: (type $4 (array (mut i8)))
;; CHECK: (rec
;; CHECK-NEXT: (type $5 (array i8))
;; CHECK: (type $d (struct))
(type $d (struct))
)
;; CHECK: (global $a (ref null $a) (ref.null none))
(global $a (ref null $a) (ref.null none))
;; CHECK: (global $b (ref null $b) (ref.null none))
(global $b (ref null $b) (ref.null none))
;; CHECK: (global $c (ref null $c) (ref.null none))
(global $c (ref null $c) (ref.null none))
;; CHECK: (global $d (ref null $d) (ref.null none))
(global $d (ref null $d) (ref.null none))
)
;; After $a1 and $a2 are dismabiguated with a brand, $b1 and $b2 require no
;; further disambiguation.
(module
(rec
;; CHECK: (type $a1 (struct))
(type $a1 (struct))
;; CHECK: (type $b1 (struct (field (ref $a1))))
;; CHECK: (rec
;; CHECK-NEXT: (type $2 (array (mut i8)))
;; CHECK: (type $a2 (struct))
(type $a2 (struct))
(type $b1 (struct (field (ref $a1))))
;; CHECK: (type $b2 (struct (field (ref $a2))))
(type $b2 (struct (field (ref $a2))))
)
;; CHECK: (global $b1 (ref null $b1) (ref.null none))
(global $b1 (ref null $b1) (ref.null none))
;; CHECK: (global $b2 (ref null $b2) (ref.null none))
(global $b2 (ref null $b2) (ref.null none))
)
;; Now we can disambiguate by permuting without a brand.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $b1 (struct (field (ref $a1))))
;; CHECK: (type $a1 (struct (field (ref $b1)) (field i32)))
(type $a1 (struct (field (ref $b1) i32)))
(type $b1 (struct (field (ref $a1))))
;; CHECK: (rec
;; CHECK-NEXT: (type $a2 (struct (field (ref $b2)) (field i32)))
(type $a2 (struct (field (ref $b2) i32)))
;; CHECK: (type $b2 (struct (field (ref $a2))))
(type $b2 (struct (field (ref $a2))))
)
;; CHECK: (global $a1 (ref null $a1) (ref.null none))
(global $a1 (ref null $a1) (ref.null none))
;; CHECK: (global $a2 (ref null $a2) (ref.null none))
(global $a2 (ref null $a2) (ref.null none))
)
;; But when we run out of permutations, we need a brand again.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $b1 (struct (field (ref $a1))))
;; CHECK: (type $a1 (struct (field (ref $b1)) (field i32)))
(type $a1 (struct (field (ref $b1) i32)))
(type $b1 (struct (field (ref $a1))))
;; CHECK: (rec
;; CHECK-NEXT: (type $a2 (struct (field (ref $b2)) (field i32)))
(type $a2 (struct (field (ref $b2) i32)))
;; CHECK: (type $b2 (struct (field (ref $a2))))
(type $b2 (struct (field (ref $a2))))
;; CHECK: (rec
;; CHECK-NEXT: (type $4 (struct))
;; CHECK: (type $b3 (struct (field (ref $a3))))
;; CHECK: (type $a3 (struct (field (ref $b3)) (field i32)))
(type $a3 (struct (field (ref $b3) i32)))
(type $b3 (struct (field (ref $a3))))
)
;; CHECK: (global $a1 (ref null $a1) (ref.null none))
(global $a1 (ref null $a1) (ref.null none))
;; CHECK: (global $a2 (ref null $a2) (ref.null none))
(global $a2 (ref null $a2) (ref.null none))
;; CHECK: (global $a3 (ref null $a3) (ref.null none))
(global $a3 (ref null $a3) (ref.null none))
)
;; Same as above, except the middle global now refers to $b2 instead of $a2,
;; changing the initial order of types in the middle SCC. We arrive at the same
;; result by a different path.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $b1 (struct (field (ref $a1))))
;; CHECK: (type $a1 (struct (field (ref $b1)) (field i32)))
(type $a1 (struct (field (ref $b1) i32)))
(type $b1 (struct (field (ref $a1))))
;; CHECK: (rec
;; CHECK-NEXT: (type $a2 (struct (field (ref $b2)) (field i32)))
(type $a2 (struct (field (ref $b2) i32)))
;; CHECK: (type $b2 (struct (field (ref $a2))))
(type $b2 (struct (field (ref $a2))))
;; CHECK: (rec
;; CHECK-NEXT: (type $4 (struct))
;; CHECK: (type $b3 (struct (field (ref $a3))))
;; CHECK: (type $a3 (struct (field (ref $b3)) (field i32)))
(type $a3 (struct (field (ref $b3) i32)))
(type $b3 (struct (field (ref $a3))))
)
;; CHECK: (global $a1 (ref null $a1) (ref.null none))
(global $a1 (ref null $a1) (ref.null none))
;; CHECK: (global $b2 (ref null $b2) (ref.null none))
(global $b2 (ref null $b2) (ref.null none))
;; CHECK: (global $a3 (ref null $a3) (ref.null none))
(global $a3 (ref null $a3) (ref.null none))
)
;; Now we can't differentiate by permutation because of automorphisms.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $b1 (struct (field (ref $a1))))
;; CHECK: (type $a1 (struct (field (ref $b1))))
(type $a1 (struct (field (ref $b1))))
(type $b1 (struct (field (ref $a1))))
;; CHECK: (rec
;; CHECK-NEXT: (type $2 (struct))
;; CHECK: (type $b2 (struct (field (ref $a2))))
;; CHECK: (type $a2 (struct (field (ref $b2))))
(type $a2 (struct (field (ref $b2))))
(type $b2 (struct (field (ref $a2))))
)
;; CHECK: (global $a1 (ref null $a1) (ref.null none))
(global $a1 (ref null $a1) (ref.null none))
;; CHECK: (global $a2 (ref null $a2) (ref.null none))
(global $a2 (ref null $a2) (ref.null none))
)
;; Now we can't differentiate by permutation because the subtyping constraint
;; admits only one ordering.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $a1 (sub (struct (field (ref $b1)))))
(type $a1 (sub (struct (field (ref $b1)))))
;; CHECK: (type $b1 (sub $a1 (struct (field (ref $b1)))))
(type $b1 (sub $a1 (struct (field (ref $b1)))))
;; CHECK: (rec
;; CHECK-NEXT: (type $2 (struct))
;; CHECK: (type $a2 (sub (struct (field (ref $b2)))))
(type $a2 (sub (struct (field (ref $b2)))))
;; CHECK: (type $b2 (sub $a2 (struct (field (ref $b2)))))
(type $b2 (sub $a2 (struct (field (ref $b2)))))
)
;; CHECK: (global $a1 (ref null $a1) (ref.null none))
(global $a1 (ref null $a1) (ref.null none))
;; CHECK: (global $a2 (ref null $a2) (ref.null none))
(global $a2 (ref null $a2) (ref.null none))
)
;; Now there are only two possible orderings admitted by the subtyping
;; constraint.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $a1 (sub (struct (field (ref $b1)))))
(type $a1 (sub (struct (field (ref $b1)))))
;; CHECK: (type $c1 (sub $a1 (struct (field (ref $b1)))))
;; CHECK: (type $b1 (sub $a1 (struct (field (ref $b1)) (field (ref $c1)))))
(type $b1 (sub $a1 (struct (field (ref $b1)) (ref $c1))))
(type $c1 (sub $a1 (struct (field (ref $b1)))))
;; CHECK: (rec
;; CHECK-NEXT: (type $a2 (sub (struct (field (ref $b2)))))
(type $a2 (sub (struct (field (ref $b2)))))
;; CHECK: (type $b2 (sub $a2 (struct (field (ref $b2)) (field (ref $c2)))))
(type $b2 (sub $a2 (struct (field (ref $b2)) (ref $c2))))
;; CHECK: (type $c2 (sub $a2 (struct (field (ref $b2)))))
(type $c2 (sub $a2 (struct (field (ref $b2)))))
;; CHECK: (rec
;; CHECK-NEXT: (type $6 (struct))
;; CHECK: (type $a3 (sub (struct (field (ref $b3)))))
(type $a3 (sub (struct (field (ref $b3)))))
;; CHECK: (type $c3 (sub $a3 (struct (field (ref $b3)))))
;; CHECK: (type $b3 (sub $a3 (struct (field (ref $b3)) (field (ref $c3)))))
(type $b3 (sub $a3 (struct (field (ref $b3)) (ref $c3))))
(type $c3 (sub $a3 (struct (field (ref $b3)))))
)
;; CHECK: (global $a1 (ref null $a1) (ref.null none))
(global $a1 (ref null $a1) (ref.null none))
;; CHECK: (global $a2 (ref null $a2) (ref.null none))
(global $a2 (ref null $a2) (ref.null none))
;; CHECK: (global $a3 (ref null $a3) (ref.null none))
(global $a3 (ref null $a3) (ref.null none))
)
;; We must avoid conflicts with public types.
(module
;; CHECK: (type $public (struct))
(type $public (struct))
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $1 (array (mut i8)))
;; CHECK: (type $private (struct))
(type $private (struct))
(type $other (struct (field (ref null $private))))
)
;; CHECK: (global $public (ref null $public) (ref.null none))
(global $public (export "g") (ref null $public) (ref.null none))
;; CHECK: (global $private (ref null $private) (ref.null none))
(global $private (ref null $private) (ref.null none))
)
;; Same as above, but now the public types are more complicated.
;; CHECK: (export "g" (global $public))
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $publicA (struct (field (ref null $publicB))))
(type $publicA (struct (field (ref null $publicB))))
;; CHECK: (type $publicB (struct (field (ref null $publicA))))
(type $publicB (struct (field (ref null $publicA))))
)
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $2 (struct))
;; CHECK: (type $privateB (struct (field (ref null $privateA))))
;; CHECK: (type $privateA (struct (field (ref null $privateB))))
(type $privateA (struct (field (ref null $privateB))))
(type $privateB (struct (field (ref null $privateA))))
(type $other (struct (field i32)))
)
;; CHECK: (global $public (ref null $publicA) (ref.null none))
(global $public (export "g") (ref null $publicA) (ref.null none))
;; CHECK: (global $private (ref null $privateA) (ref.null none))
(global $private (ref null $privateA) (ref.null none))
)
;; Now the conflict with the public type does not arise until we try to resolve
;; a conflict between the private types.
;; CHECK: (export "g" (global $public))
(module
(rec
;; CHECK: (type $privateA (struct (field i32) (field i64)))
;; CHECK: (rec
;; CHECK-NEXT: (type $publicBrand (struct))
(type $publicBrand (struct))
;; CHECK: (type $public (struct (field i32) (field i64)))
(type $public (struct (field i32 i64)))
)
(rec
(type $privateA (struct (field i32 i64)))
;; CHECK: (rec
;; CHECK-NEXT: (type $privateB (struct (field i32) (field i64)))
(type $privateB (struct (field i32 i64)))
)
;; CHECK: (type $4 (struct))
;; CHECK: (global $public (ref null $public) (ref.null none))
(global $public (export "g") (ref null $public) (ref.null none))
;; CHECK: (global $privateA (ref null $privateA) (ref.null none))
(global $privateA (ref null $privateA) (ref.null none))
;; CHECK: (global $privateB (ref null $privateB) (ref.null none))
(global $privateB (ref null $privateB) (ref.null none))
)
;; CHECK: (export "g" (global $public))