We see that unused functions metadata is still a part of the overall binary post applying full swift lto.
The same also applies to unused protocol witness records, which we would expect not to exist since those functions are totally unused and can be stripped.
I believe this would need some deeper virtual method elimination and witness method elimination, and I would appreciate it if someone could guide how to go about removing these entries, any compiler flags that can help us here? If there are flags that can help achieve this deeper stripping, how stable is using those flags for production, any known issues/edge-cases?
Repro 1: Dynamic dispatch - unused virtual function entries
Foo.swift
public class A {
public func unusedFunc() {}
public func usedFunc() {}
public init() {}
}
public class B: A {
public override func usedFunc() {}
}
public class C: B {
public override func usedFunc() {}
}
main.swift
import Foo
let a: A = C()
a.usedFunc()
Execute swiftc
swiftc Foo.swift -emit-library -static -emit-module -Xfrontend -internalize-at-link -lto=llvm-full
swiftc main.swift -I . -L . -lFoo -Xfrontend -internalize-at-link -lto=llvm-full
nm main | swift demangle
Output:
0000000100003cfc t **Foo.A.unusedFunc() -> ()**
0000000100003d00 t Foo.A.usedFunc() -> ()
0000000100003d04 t Foo.A.__allocating_init() -> Foo.A
0000000100003f74 s reflection metadata field descriptor Foo.A
0000000100003ce0 t type metadata accessor for Foo.A
00000001000081d8 d full type metadata for Foo.A
00000001000081b0 d metaclass for Foo.A
0000000100003e50 s nominal type descriptor for Foo.A
00000001000081e8 d type metadata for Foo.A
0000000100003d14 t Foo.A.__deallocating_deinit
0000000100003d40 t Foo.B.usedFunc() -> ()
0000000100003d44 t Foo.B.__allocating_init() -> Foo.B
0000000100003f84 s reflection metadata field descriptor Foo.B
0000000100003d24 t type metadata accessor for Foo.B
0000000100008278 d full type metadata for Foo.B
0000000100008250 d metaclass for Foo.B
0000000100003ea0 s nominal type descriptor for Foo.B
0000000100008288 d type metadata for Foo.B
0000000100003d7c t Foo.B.__deallocating_deinit
0000000100003da8 t Foo.C.usedFunc() -> ()
0000000100003dac t Foo.C.__allocating_init() -> Foo.C
0000000100003f94 s reflection metadata field descriptor Foo.C
0000000100003d8c t type metadata accessor for Foo.C
0000000100008318 d full type metadata for Foo.C
00000001000082f0 d metaclass for Foo.C
0000000100003eec s nominal type descriptor for Foo.C
0000000100008328 d type metadata for Foo.C
0000000100003df0 t Foo.C.__deallocating_deinit
0000000100003e40 s module descriptor Foo
0000000100008390 b main.a : Foo.A
U value witness table for Builtin.NativeObject
U _OBJC_CLASS_$_Swift._SwiftObject
U _OBJC_METACLASS_$_Swift._SwiftObject
0000000100008048 s __DATA_Foo.A
00000001000080d8 s __DATA_Foo.B
0000000100008168 s __DATA_Foo.C
0000000100008000 s __METACLASS_DATA_Foo.A
0000000100008090 s __METACLASS_DATA_Foo.B
0000000100008120 s __METACLASS_DATA_Foo.C
0000000100003f34 s ___swift_reflection_version
0000000100000000 T __mh_execute_header
U __objc_empty_cache
0000000100003c84 T _main
U _objc_opt_self
U _swift_allocObject
U _swift_deallocClassInstance
U _swift_release
U _swift_retain
0000000100003f62 s _symbolic _____ 3Foo1AC
0000000100003f68 s _symbolic _____ 3Foo1BC
0000000100003f6e s _symbolic _____ 3Foo1CC
Notice: Foo.A.unusedFunc() -> ()
is present, which can be stripped.
Repro 2: unused protocol witness conformance records entries
Foo.swift
public protocol TheProtocol {
func unusedFunc()
func usedFunc()
}
public struct A: TheProtocol {
public func unusedFunc() {}
public func usedFunc() {}
public init() {}
}
main.swift
import Foo
let a: TheProtocol = A()
a.usedFunc()
swiftc Foo.swift -emit-library -static -emit-module -Xfrontend -internalize-at-link -lto=llvm-full
swiftc main.swift -I . -L . -lFoo -Xfrontend -internalize-at-link -lto=llvm-full
nm main | swift demangle
Output
0000000100003f1c s protocol descriptor for Foo.TheProtocol
0000000100003f8c s reflection metadata field descriptor Foo.TheProtocol
0000000100003ed8 t protocol witness for Foo.TheProtocol.unusedFunc() -> () in conformance Foo.A : Foo.TheProtocol in Foo
0000000100003edc t protocol witness for Foo.TheProtocol.usedFunc() -> () in conformance Foo.A : Foo.TheProtocol in Foo
0000000100003ef0 s protocol conformance descriptor for Foo.A : Foo.TheProtocol in Foo
0000000100004000 s protocol witness table for Foo.A : Foo.TheProtocol in Foo
0000000100003f9c s reflection metadata field descriptor Foo.A
0000000100003ee0 t type metadata accessor for Foo.A
0000000100004018 s full type metadata for Foo.A
0000000100003f48 s nominal type descriptor for Foo.A
0000000100004020 s type metadata for Foo.A
0000000100003f04 s module descriptor Foo
0000000100008000 b main.a : Foo.TheProtocol
U value witness table for ()
0000000100003f64 s ___swift_reflection_version
0000000100000000 T __mh_execute_header
0000000100003eb4 T _main
0000000100003f70 s _symbolic Foo.TheProtocol
0000000100003f86 s _symbolic _____ 3Foo1AV
Notice the presence of:
protocol witness for Foo.TheProtocol.unusedFunc() -> () in conformance Foo.A : Foo.TheProtocol in Foo