Index: lld/MachO/InputFiles.cpp =================================================================== --- lld/MachO/InputFiles.cpp +++ lld/MachO/InputFiles.cpp @@ -2213,9 +2213,11 @@ } void macho::extract(InputFile &file, StringRef reason) { - printArchiveMemberLoad(reason, &file); - assert(file.lazy); + if (!file.lazy) + return; file.lazy = false; + + printArchiveMemberLoad(reason, &file); if (auto *bitcode = dyn_cast(&file)) { bitcode->parse(); } else { Index: lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++abi.tbd =================================================================== --- lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++abi.tbd +++ lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++abi.tbd @@ -6,5 +6,6 @@ current-version: 1281 exports: - archs: [ i386, x86_64, arm64 ] - symbols: [ ___cxa_allocate_exception, ___cxa_begin_catch, ___cxa_end_catch, ___cxa_throw, ___gxx_personality_v0, __ZTIi ] + symbols: [ ___cxa_allocate_exception, ___cxa_begin_catch, ___cxa_end_catch, ___cxa_throw, + ___gxx_personality_v0, __ZTIi, __ZTVN10__cxxabiv117__class_type_infoE ] ... Index: lld/test/MachO/thinlto-split-unit-start-lib.ll =================================================================== --- /dev/null +++ lld/test/MachO/thinlto-split-unit-start-lib.ll @@ -0,0 +1,125 @@ +; REQUIRES: x86 +; RUN: rm -rf %t; split-file %s %t + +;; Split-LTO bitcode files can have the same symbol (typeinfo) twice in the +;; symbol table: First undefined in the main file, then defined in the index +;; file. When used in --start-lib / --end-lib, ld64.lld creates lazy symbols +;; for all non-undefined symbols in the bitcode file (and since the typeinfo, +;; _ZTI1S below) is present once as undefined and once as defined, it gets +;; added. When something then references a different symbol in the bitcode +;; file, causing it to be loaded, would go through all the symbols in the +;; bitcode and add them to the symbol file. The undefined _ZTI1S shows up +;; first in the bitcode file, which then found the lazy symbol for it, +;; pointing to the same bitcode file again, causing it to be loaded a +;; second time, causing an assert. This test makes sure we don't load it twice +;; on the second time round. + +;; See PR59162 for details. + + +; RUN: opt --thinlto-bc --thinlto-split-lto-unit -o %t/vtable.o %t/vtable.ll +; RUN: opt --thinlto-bc --thinlto-split-lto-unit -o %t/vtable_use.o %t/vtable_use.ll + +; RUN: %lld -lc++ --start-lib %t/vtable.o --end-lib %t/vtable_use.o -o /dev/null + +;; Bitcode files created by: +; % cat vtable.cc +; struct S { +; S(); +; virtual void f(); +; }; +; S::S() {} +; void S::f() {} + +; % cat vtable_use.cc +; struct S { +; S(); +; virtual void f(); +; }; +; int main() { S s; } + +; % clang -c vtable_use.cc vtable.cc -emit-llvm -S -fno-exceptions -arch x86_64 -mmacos-version-min=11 -O1 + +; ...and then manually ading `, !type !8, type !9` based on `clang -S -emit-llvm -flto=thin` output, +; because splitAndWriteThinLTOBitcode() in ThinLTOBitcodeWriter.cpp only splits bitcode +; if type annotations are present. +; (NB: The first comment creates vtable.ll while the latter generates vtable.s! vtable.s +; contains a few things opt complains about, so we can't use the output of that directly.) + +;--- vtable.ll +; ModuleID = 'vtable.cc' +source_filename = "vtable.cc" +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx11.0.0" + +@_ZTV1S = unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1S, ptr @_ZN1S1fEv] }, align 8 +@_ZTVN10__cxxabiv117__class_type_infoE = external global ptr +@_ZTS1S = constant [3 x i8] c"1S\00", align 1 +@_ZTI1S = constant { ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), ptr @_ZTS1S }, align 8, !type !8, !type !9 + +; Function Attrs: mustprogress nofree norecurse nosync nounwind ssp willreturn memory(argmem: write) uwtable +define void @_ZN1SC2Ev(ptr nocapture noundef nonnull writeonly align 8 dereferenceable(8) %this) unnamed_addr #0 align 2 { +entry: + store ptr getelementptr inbounds ({ [3 x ptr] }, ptr @_ZTV1S, i64 0, inrange i32 0, i64 2), ptr %this, align 8, !tbaa !5 + ret void +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind ssp willreturn memory(argmem: write) uwtable +define void @_ZN1SC1Ev(ptr nocapture noundef nonnull writeonly align 8 dereferenceable(8) %this) unnamed_addr #0 align 2 { +entry: + store ptr getelementptr inbounds ({ [3 x ptr] }, ptr @_ZTV1S, i64 0, inrange i32 0, i64 2), ptr %this, align 8, !tbaa !5 + ret void +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind ssp willreturn memory(none) uwtable +define void @_ZN1S1fEv(ptr nocapture nonnull align 8 %this) unnamed_addr #1 align 2 { +entry: + ret void +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind ssp willreturn memory(argmem: write) uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" } +attributes #1 = { mustprogress nofree norecurse nosync nounwind ssp willreturn memory(none) uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!llvm.ident = !{!4} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 8, !"PIC Level", i32 2} +!2 = !{i32 7, !"uwtable", i32 2} +!3 = !{i32 7, !"frame-pointer", i32 2} +!4 = !{!"clang version 16.0.0"} +!5 = !{!6, !6, i64 0} +!6 = !{!"vtable pointer", !7, i64 0} +!7 = !{!"Simple C++ TBAA"} +!8 = !{i64 16, !"_ZTS1S"} +!9 = !{i64 16, !"_ZTSM1SFvvE.virtual"} + +;--- vtable_use.ll +; ModuleID = 'vtable_use.cc' +source_filename = "vtable_use.cc" +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx11.0.0" + +%struct.S = type { ptr } + +; Function Attrs: mustprogress noinline norecurse nounwind optnone ssp uwtable +define noundef i32 @main() #0 { +entry: + %s = alloca %struct.S, align 8 + call void @_ZN1SC1Ev(ptr noundef nonnull align 8 dereferenceable(8) %s) + ret i32 0 +} + +declare void @_ZN1SC1Ev(ptr noundef nonnull align 8 dereferenceable(8)) unnamed_addr #1 + +attributes #0 = { mustprogress noinline norecurse nounwind optnone ssp uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" } +attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!llvm.ident = !{!4} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 8, !"PIC Level", i32 2} +!2 = !{i32 7, !"uwtable", i32 2} +!3 = !{i32 7, !"frame-pointer", i32 2} +!4 = !{!"clang version 16.0.0"}