diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1680,7 +1680,7 @@ for (InputFile *file : lto->compile()) { auto *obj = cast>(file); - obj->parse(/*ignoreComdats=*/true); + obj->parse(/*isLTOOutput=*/true); for (Symbol *sym : obj->getGlobalSymbols()) sym->parseSymbolVersion(); objectFiles.push_back(file); diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -201,7 +201,7 @@ this->archiveName = std::string(archiveName); } - void parse(bool ignoreComdats = false); + void parse(bool isLTOOutput = false); StringRef getShtGroupSignature(ArrayRef sections, const Elf_Shdr &sec); @@ -252,7 +252,7 @@ DWARFCache *getDwarf(); private: - void initializeSections(bool ignoreComdats); + void initializeSections(bool isLTOOutput); void initializeSymbols(); void initializeJustSymbols(); diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -390,12 +390,12 @@ return makeArrayRef(this->symbols).slice(this->firstGlobal); } -template void ObjFile::parse(bool ignoreComdats) { +template void ObjFile::parse(bool isLTOOutput) { // Read a section table. justSymbols is usually false. if (this->justSymbols) initializeJustSymbols(); else - initializeSections(ignoreComdats); + initializeSections(isLTOOutput); // Read a symbol table. initializeSymbols(); @@ -545,8 +545,7 @@ prev->nextInSectionGroup = head; } -template -void ObjFile::initializeSections(bool ignoreComdats) { +template void ObjFile::initializeSections(bool isLTOOutput) { const ELFFile &obj = this->getObj(); ArrayRef objSections = CHECK(obj.sections(), this); @@ -607,10 +606,15 @@ if (entries[0] != GRP_COMDAT) fatal(toString(this) + ": unsupported SHT_GROUP format"); - bool isNew = - ignoreComdats || - symtab->comdatGroups.try_emplace(CachedHashStringRef(signature), this) - .second; + // If this is an lto output file, ignore already processed comdat groups. + // Else, check all comdat groups. + bool isNew = isLTOOutput + ? symtab->ltoOutputComdatGroups + .try_emplace(CachedHashStringRef(signature), this) + .second + : symtab->comdatGroups + .try_emplace(CachedHashStringRef(signature), this) + .second; if (isNew) { if (config->relocatable) this->sections[i] = createInputSection(sec); diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -694,8 +694,12 @@ // If the discarded section is a COMDAT. StringRef signature = file->getShtGroupSignature(objSections, elfSec); - if (const InputFile *prevailing = - symtab->comdatGroups.lookup(CachedHashStringRef(signature))) + const InputFile *prevailing = + symtab->comdatGroups.lookup(CachedHashStringRef(signature)); + if (!prevailing) + prevailing = + symtab->ltoOutputComdatGroups.lookup(CachedHashStringRef(signature)); + if (prevailing) msg += "\n>>> section group signature: " + signature.str() + "\n>>> prevailing definition is in " + toString(prevailing); return msg; diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -63,6 +63,15 @@ // is used to uniquify them. llvm::DenseMap comdatGroups; + // We link twice in thinlto. A comdat group might be processed while it was + // in bitcode file where symbols are added but not defined, so when it is + // processed again as object file the symbol will be skipped and we end up + // with undefined symbols. Define a separate map of comdat groups for + // bitcode files to make sure the symbols are distinct from the ones found + // in object files + llvm::DenseMap + ltoOutputComdatGroups; + private: std::vector findByVersion(SymbolVersion ver); std::vector findAllByVersion(SymbolVersion ver); diff --git a/lld/test/ELF/lto/Inputs/debug-types-deduplication.ll b/lld/test/ELF/lto/Inputs/debug-types-deduplication.ll new file mode 100644 --- /dev/null +++ b/lld/test/ELF/lto/Inputs/debug-types-deduplication.ll @@ -0,0 +1,32 @@ +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.foo = type { i32 } + +define dso_local i32 @_Z3bar3foo(i32 %0) noinline optnone !dbg !4 { + %2 = alloca %struct.foo, align 4 + call void @llvm.dbg.declare(metadata %struct.foo* %2, metadata !10, metadata !DIExpression()), !dbg !11 + ret i32 0, !dbg !12 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, emissionKind: FullDebug) +!1 = !DIFile(filename: "foo.cc", directory: "") +!2 = !{i32 7, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = distinct !DISubprogram(name: "bar", linkageName: "_Z3bar3foo", type: !5, unit: !0) +!5 = !DISubroutineType(types: !{!6, !7}) +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !8, line: 4, size: 32, flags: DIFlagTypePassByValue, elements: !{!9}, identifier: "_ZTS3foo") +!8 = !DIFile(filename: "./foo.h", directory: "") +!9 = !DIDerivedType(tag: DW_TAG_member, name: "f", scope: !7, file: !8, line: 4, baseType: !6, size: 32) +!10 = !DILocalVariable(name: "f1", arg: 1, scope: !4, file: !1, line: 3, type: !7) +!11 = !DILocation(line: 1, column: 1, scope: !4) +!12 = !DILocation(line: 2, column: 1, scope: !4) + +^0 = module: (path: "foo.o", hash: (505897185, 3065645384, 2109903963, 2773340897, 2672438121)) +^1 = gv: (name: "_Z3bar3foo", summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 6, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0)))) ; guid = 11911810691311191905 diff --git a/lld/test/ELF/lto/debug-types-deduplication.ll b/lld/test/ELF/lto/debug-types-deduplication.ll new file mode 100644 --- /dev/null +++ b/lld/test/ELF/lto/debug-types-deduplication.ll @@ -0,0 +1,45 @@ +; REQUIRES: x86 +; RUN: llvm-as %s -o %t.o +; RUN: llvm-as %p/Inputs/debug-types-deduplication.ll -o %t2.o +; RUN: ld.lld %t.o %t2.o -mllvm --generate-type-units --entry=main -o %t3.o +; RUN: llvm-dwarfdump --debug-types %t3.o | FileCheck %s + +; CHECK-COUNT-1: Type Unit: +; CHECK-NOT: Type Unit: + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.foo = type { i32 } + +define dso_local i32 @main() noinline optnone !dbg !4 { + %1 = alloca %struct.foo, align 4 + call void @llvm.dbg.declare(metadata %struct.foo* %1, metadata !7, metadata !DIExpression()), !dbg !11 + ret i32 0, !dbg !12 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +declare dso_local i32 @_Z3bar3foo(i32) + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) + + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, emissionKind: FullDebug) +!1 = !DIFile(filename: "hello.cc", directory: "") +!2 = !{i32 7, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = distinct !DISubprogram(name: "main", type: !5, unit: !0) +!5 = !DISubroutineType(types: !{!6}) +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !DILocalVariable(name: "f1", scope: !4, file: !1,type: !8) +!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !9, line: 4, size: 32, flags: DIFlagTypePassByValue, elements: !{!10}, identifier: "_ZTS3foo") +!9 = !DIFile(filename: "./foo.h", directory: "") +!10 = !DIDerivedType(tag: DW_TAG_member, name: "f", scope: !8, file: !9, line: 4, baseType: !6, size: 32) +!11 = !DILocation(line: 1, column: 1, scope: !4) +!12 = !DILocation(line: 2, column: 2, scope: !4) + +^0 = module: (path: "hello.o", hash: (339834615, 71091658, 394989773, 1362321694, 1785275294))