diff --git a/src/passes/TypeSSA.cpp b/src/passes/TypeSSA.cpp index dd8f694c659..095d6cec2bf 100644 --- a/src/passes/TypeSSA.cpp +++ b/src/passes/TypeSSA.cpp @@ -73,8 +73,10 @@ std::vector ensureTypesAreInNewRecGroup(std::vector&& types, UniqueRecGroups unique(wasm.features); for (auto group : existing) { std::vector types(group.begin(), group.end()); - [[maybe_unused]] auto uniqueTypes = unique.insert(std::move(types)); - assert(uniqueTypes.size() == group.size() && "unexpected collision"); + // N.B. we use `insertOrGet` rather than `insert` because some passes (DAE, + // BlockMerging) can create multiple types with the same shape, so we can't + // assume all the rec groups are already unique. + unique.insertOrGet(std::move(types)); } auto num = types.size(); diff --git a/test/lit/passes/dae-typessa-repeat-types.wast b/test/lit/passes/dae-typessa-repeat-types.wast new file mode 100644 index 00000000000..d03e7e1a1f1 --- /dev/null +++ b/test/lit/passes/dae-typessa-repeat-types.wast @@ -0,0 +1,81 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; RUN: wasm-opt %s -all --disable-custom-descriptors --dae --type-ssa -S -o - | filecheck %s + +(module + ;; CHECK: (type $struct (struct)) + (type $struct (struct)) + + ;; CHECK: (type $1 (func (result i32 (ref (exact $struct))))) + + ;; CHECK: (type $array (sub (array (mut i32)))) + (type $array (sub (array (mut i32)))) + + ;; Trigger TypeSSA + ;; CHECK: (type $3 (func)) + + ;; CHECK: (type $array_1 (sub $array (array (mut i32)))) + + ;; CHECK: (type $5 (func (result i32 (ref $struct)))) + + ;; CHECK: (global $array (ref $array) (array.new $array_1 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: )) + (global $array (ref $array) + (array.new $array + (i32.const 0) + (i32.const 0) + ) + ) + + ;; CHECK: (func $caller (type $3) + ;; CHECK-NEXT: (call $callee) + ;; CHECK-NEXT: ) + (func $caller + ;; Give DAE a constant null parameter to optimize. + (tuple.drop 2 + (call $callee + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $callee (type $3) + ;; CHECK-NEXT: (local $0 anyref) + ;; CHECK-NEXT: (tuple.drop 2 + ;; CHECK-NEXT: (block (type $1) (result i32 (ref (exact $struct))) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (type $1) (result i32 (ref (exact $struct))) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (struct.new_default $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $callee (param (ref null any)) (result i32 (ref $struct)) + ;; When applying the constant null, DAE will create a block with + ;; (result i32 (ref (exact $struct))) + (block (result i32 (ref $struct)) + (tuple.make 2 + (i32.const 0) + (struct.new_default $struct) + ) + ) + ) + + ;; CHECK: (func $other (type $5) (result i32 (ref $struct)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $other (result i32 (ref $struct)) + ;; This will keep the (result i32 (ref $struct)) signature, which will + ;; conflict with the (result i32 (ref (exact $struct))) of the block above + ;; after binary writing. This will not be observable, though, since DAE only + ;; optimizes non-referenced functions. TypeSSA should not crash or fail an + ;; assertion due to the repeated type shape. + (unreachable) + ) +)