Index: lld/trunk/COFF/InputFiles.cpp =================================================================== --- lld/trunk/COFF/InputFiles.cpp +++ lld/trunk/COFF/InputFiles.cpp @@ -266,7 +266,9 @@ if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) { SymbolBodies.push_back(new (Alloc) Undefined(SymName)); } else { - SymbolBodies.push_back(new (Alloc) DefinedBitcode(SymName)); + bool Replaceable = (SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE || + (Attrs & LTO_SYMBOL_COMDAT)); + SymbolBodies.push_back(new (Alloc) DefinedBitcode(SymName, Replaceable)); } } Index: lld/trunk/COFF/SymbolTable.cpp =================================================================== --- lld/trunk/COFF/SymbolTable.cpp +++ lld/trunk/COFF/SymbolTable.cpp @@ -268,6 +268,14 @@ return make_error_code(LLDError::BrokenFile); } Sym->Body = Body; + } else { + int comp = Sym->Body->compare(Body); + if (comp < 0) + Sym->Body = Body; + if (comp == 0) { + llvm::errs() << "LTO: unexpected duplicate symbol: " << Name << "\n"; + return make_error_code(LLDError::BrokenFile); + } } // We may see new references to runtime library symbols such as __chkstk @@ -300,6 +308,13 @@ if (auto *S = dyn_cast(Body->getReplacement())) CG->addMustPreserveSymbol(S->getName()); + // Likewise for bitcode symbols which we initially resolved to non-bitcode. + for (std::unique_ptr &File : BitcodeFiles) + for (SymbolBody *Body : File->getSymbols()) + if (isa(Body) && + !isa(Body->getReplacement())) + CG->addMustPreserveSymbol(Body->getName()); + // Likewise for other symbols that must be preserved. for (StringRef Name : Config->GCRoots) if (isa(Symtab[Name]->Body)) Index: lld/trunk/COFF/Symbols.h =================================================================== --- lld/trunk/COFF/Symbols.h +++ lld/trunk/COFF/Symbols.h @@ -267,7 +267,8 @@ class DefinedBitcode : public Defined { public: - DefinedBitcode(StringRef N) : Defined(DefinedBitcodeKind), Name(N) {} + DefinedBitcode(StringRef N, bool R) + : Defined(DefinedBitcodeKind), Name(N), Replaceable(R) {} static bool classof(const SymbolBody *S) { return S->kind() == DefinedBitcodeKind; @@ -276,9 +277,12 @@ StringRef getName() override { return Name; } uint64_t getRVA() override { llvm_unreachable("bitcode reached writer"); } uint64_t getFileOff() override { llvm_unreachable("bitcode reached writer"); } + int compare(SymbolBody *Other) override; + bool isReplaceable() const { return Replaceable; } private: StringRef Name; + bool Replaceable; }; } // namespace coff Index: lld/trunk/COFF/Symbols.cpp =================================================================== --- lld/trunk/COFF/Symbols.cpp +++ lld/trunk/COFF/Symbols.cpp @@ -21,14 +21,31 @@ namespace lld { namespace coff { +// As an approximation, regular symbols win over bitcode symbols, but we +// definitely have a conflict if the regular symbol is not replaceable and +// neither is the bitcode symbol. We do not replicate the rest of the symbol +// resolution logic here; symbol resolution will be done accurately after +// lowering bitcode symbols to regular symbols in addCombinedLTOObject(). +static int compareRegularBitcode(DefinedRegular *R, DefinedBitcode *B) { + if (!R->isCommon() && !R->isCOMDAT() && !B->isReplaceable()) + return 0; + return 1; +} + // Returns 1, 0 or -1 if this symbol should take precedence over the // Other in the symbol table, tie or lose, respectively. int Defined::compare(SymbolBody *Other) { if (!isa(Other)) return 1; auto *X = dyn_cast(this); + if (!X) + return 0; + + if (auto *B = dyn_cast(Other)) + return compareRegularBitcode(X, B); + auto *Y = dyn_cast(Other); - if (!X || !Y) + if (!Y) return 0; // Common symbols are weaker than other types of defined symbols. @@ -46,6 +63,22 @@ return 0; } +int DefinedBitcode::compare(SymbolBody *Other) { + if (!isa(Other)) + return 1; + + if (auto *R = dyn_cast(Other)) + return -compareRegularBitcode(R, this); + + if (auto *B = dyn_cast(Other)) { + if (!isReplaceable() && !B->isReplaceable()) + return 0; + // Non-replaceable symbols win. + return isReplaceable() ? -1 : 1; + } + return 0; +} + int Lazy::compare(SymbolBody *Other) { if (isa(Other)) return -1; Index: lld/trunk/test/COFF/Inputs/lto-comdat1.ll =================================================================== --- lld/trunk/test/COFF/Inputs/lto-comdat1.ll +++ lld/trunk/test/COFF/Inputs/lto-comdat1.ll @@ -0,0 +1,13 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +$comdat = comdat any + +define void @f1() { + call void @comdat() + ret void +} + +define linkonce_odr void @comdat() comdat { + ret void +} Index: lld/trunk/test/COFF/Inputs/lto-comdat2.ll =================================================================== --- lld/trunk/test/COFF/Inputs/lto-comdat2.ll +++ lld/trunk/test/COFF/Inputs/lto-comdat2.ll @@ -0,0 +1,13 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +$comdat = comdat any + +define void @f2() { + call void @comdat() + ret void +} + +define linkonce_odr void @comdat() comdat { + ret void +} Index: lld/trunk/test/COFF/lto-comdat.ll =================================================================== --- lld/trunk/test/COFF/lto-comdat.ll +++ lld/trunk/test/COFF/lto-comdat.ll @@ -0,0 +1,131 @@ +; RUN: llvm-as -o %T/comdat-main.lto.obj %s +; RUN: llvm-as -o %T/comdat1.lto.obj %S/Inputs/lto-comdat1.ll +; RUN: llvm-as -o %T/comdat2.lto.obj %S/Inputs/lto-comdat2.ll +; RUN: rm -f %T/comdat.lto.lib +; RUN: llvm-ar cru %T/comdat.lto.lib %T/comdat1.lto.obj %T/comdat2.lto.obj + +; RUN: llc -filetype=obj -o %T/comdat-main.obj %s +; RUN: llc -filetype=obj -o %T/comdat1.obj %S/Inputs/lto-comdat1.ll +; RUN: llc -filetype=obj -o %T/comdat2.obj %S/Inputs/lto-comdat2.ll +; RUN: rm -f %T/comdat.lib +; RUN: llvm-ar cru %T/comdat.lib %T/comdat1.obj %T/comdat2.obj + +; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat1.lto.obj %T/comdat2.lto.obj +; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-11 %s +; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-11 %s +; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat.lto.lib +; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-11 %s +; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-11 %s + +; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.obj %T/comdat1.lto.obj %T/comdat2.lto.obj +; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-01 %s +; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-01 %s +; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.obj %T/comdat.lto.lib +; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-01 %s +; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-01 %s + +; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat1.obj %T/comdat2.obj +; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-10 %s +; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-10 %s +; RUN: lld -flavor link2 /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat.lib +; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-10 %s +; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-10 %s + +; HEADERS-11: AddressOfEntryPoint: 0x1000 +; TEXT-11: Disassembly of section .text: +; TEXT-11-NEXT: .text: +; TEXT-11-NEXT: xorl %eax, %eax +; TEXT-11-NEXT: retq + +; HEADERS-01: AddressOfEntryPoint: 0x2000 +; TEXT-01: Disassembly of section .text: +; TEXT-01-NEXT: .text: +; TEXT-01-NEXT: subq $40, %rsp +; TEXT-01-NEXT: callq 39 +; TEXT-01-NEXT: callq 50 +; TEXT-01-NEXT: callq 13 +; TEXT-01-NEXT: xorl %eax, %eax +; TEXT-01-NEXT: addq $40, %rsp +; TEXT-01-NEXT: retq +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: retq +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: int3 +; TEXT-01-NEXT: retq +; TEXT-01-NEXT: nopw %cs:(%rax,%rax) +; TEXT-01-NEXT: retq + +; HEADERS-10: AddressOfEntryPoint: 0x2030 +; TEXT-10: Disassembly of section .text: +; TEXT-10-NEXT: .text: +; TEXT-10-NEXT: subq $40, %rsp +; TEXT-10-NEXT: callq 7 +; TEXT-10-NEXT: nop +; TEXT-10-NEXT: addq $40, %rsp +; TEXT-10-NEXT: retq +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: retq +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: subq $40, %rsp +; TEXT-10-NEXT: callq -25 +; TEXT-10-NEXT: nop +; TEXT-10-NEXT: addq $40, %rsp +; TEXT-10-NEXT: retq +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: subq $40, %rsp +; TEXT-10-NEXT: callq -57 +; TEXT-10-NEXT: callq -30 +; TEXT-10-NEXT: xorl %eax, %eax +; TEXT-10-NEXT: addq $40, %rsp +; TEXT-10-NEXT: retq + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +$comdat = comdat any + +define i32 @main() { + call void @f1() + call void @f2() + call void @comdat() + ret i32 0 +} + +define linkonce_odr void @comdat() comdat { + ret void +} + +declare void @f1() +declare void @f2()