;; 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))