[wasm][IRGen] Stop attaching COMDATs to dead stub methods

When generating dead stub methods for vtable entries, we should not
attach COMDATs to their definitions. This is because such stubs may be
referenced only through aliases, and the presence of a COMDAT on the
stub definition would cause the aliased symbol, we want to keep, to be
discarded from the symbol table when other object files also have a
dead stub method.

Given the following two object files generated:

```mermaid
graph TD
    subgraph O0[A.swift.o]
        subgraph C0[COMDAT Group swift_dead_method_stub]
            D0_0["[Def] swift_dead_method_stub"]
        end
        S0_0["[Symbol] swift_dead_method_stub"] --> D0_0
        S1["[Symbol] C1.dead"] --alias--> D0_0
    end

    subgraph O1[B.swift.o]
        subgraph C1[COMDAT Group swift_dead_method_stub]
            D0_1["[Def] swift_dead_method_stub"]
        end
        S0_1["[Symbol] swift_dead_method_stub"] --> D0_1
        S2["[Symbol] C2.beef"] --alias--> D0_1
    end
```

When linking these two object files, the linker will pick one of the
COMDAT groups of `swift_dead_method_stub`. The other COMDAT group will be
discarded, along with all symbols aliased to the discarded definition,
even though those aliased symbols (`C1.dead` and `C2.beef`) are the
ones we want to keep to construct vtables.

This change stops attaching COMDATs to dead stub method definitions.
This effectively only affects WebAssembly targets because MachO's
`supportsCOMDAT` returns false, and COFF targets don't use
`linkonce_odr` for dead stub methods.

The COMDAT was added in 7e8f782457
This commit is contained in:
Yuta Saito
2025-10-24 06:49:07 +00:00
parent 25543a56fc
commit 50298bd8e5
3 changed files with 54 additions and 5 deletions

View File

@@ -0,0 +1,42 @@
// RUN: %empty-directory(%t)
// RUN: split-file --leading-lines %s %t
// Note: IRGen uses internal linkage instead of linkonce_odr only for COFF for dead stub methods.
// UNSUPPORTED: OS=windows-msvc
// Ensure that swift_dead_method_stub is emitted without comdat linkage
// RUN: %target-swift-frontend -target %target-swift-5.1-abi-triple -parse-as-library -O -module-name A -c -primary-file %t/A.swift %t/B.swift -emit-ir -o - | %FileCheck %s -check-prefix CHECK
// RUN: %target-swift-frontend -target %target-swift-5.1-abi-triple -parse-as-library -O -module-name A -c %t/A.swift -primary-file %t/B.swift -emit-ir -o - | %FileCheck %s -check-prefix CHECK
// CHECK-LABEL: define {{(linkonce_odr )?}}hidden void @_swift_dead_method_stub(
// CHECK-NOT: comdat
// CHECK: {
// Ensure that link-time deduplication for swift_dead_method_stub works correctly
// RUN: %target-swift-frontend -target %target-swift-5.1-abi-triple -parse-as-library -O -module-name A -c -primary-file %t/A.swift %t/B.swift -o %t/A.swift.o
// RUN: %target-swift-frontend -target %target-swift-5.1-abi-triple -parse-as-library -O -module-name A -c %t/A.swift -primary-file %t/B.swift -o %t/B.swift.o
// RUN: %target-build-swift -target %target-swift-5.1-abi-triple %t/B.swift.o %t/A.swift.o -o %t/a.out
//--- A.swift
// Define an open class with a dead vtable entry
class C1 {
private func dead() {}
}
//--- B.swift
class C2: C1 {
// Define another dead vtable entry to ensure that this object file
// also should have dead vtable stub definition
private func beef() {}
}
@main
struct Entry {
static func main() {
_ = C2()
}
}