diff --git a/lld/MachO/ConcatOutputSection.cpp b/lld/MachO/ConcatOutputSection.cpp --- a/lld/MachO/ConcatOutputSection.cpp +++ b/lld/MachO/ConcatOutputSection.cpp @@ -297,11 +297,12 @@ thunkInfo.isec->parent = this; StringRef thunkName = saver.save(funcSym->getName() + ".thunk." + std::to_string(thunkInfo.sequence++)); - r.referent = thunkInfo.sym = symtab->addDefined( + // calls to addDefined() with isWeakDef=false always return a Defined + r.referent = thunkInfo.sym = cast(symtab->addDefined( thunkName, /*file=*/nullptr, thunkInfo.isec, /*value=*/0, /*size=*/thunkSize, /*isWeakDef=*/false, /*isPrivateExtern=*/true, /*isThumb=*/false, /*isReferencedDynamically=*/false, - /*noDeadStrip=*/false); + /*noDeadStrip=*/false)); target->populateThunk(thunkInfo.isec, funcSym); finalizeOne(thunkInfo.isec); thunks.push_back(thunkInfo.isec); diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -1273,8 +1273,6 @@ if (objSym.isUndefined()) return symtab->addUndefined(name, &file, /*isWeakRef=*/false); - assert(!objSym.isCommon() && "TODO: support common symbols in LTO"); - // TODO: Write a test demonstrating why computing isPrivateExtern before // LTO compilation is important. bool isPrivateExtern = false; @@ -1289,6 +1287,9 @@ break; } + if (objSym.isCommon()) + return symtab->addBitcodeCommon(name, &file, isPrivateExtern); + return symtab->addDefined(name, &file, /*isec=*/nullptr, /*value=*/0, /*size=*/0, objSym.isWeak(), isPrivateExtern, /*isThumb=*/false, diff --git a/lld/MachO/SymbolTable.h b/lld/MachO/SymbolTable.h --- a/lld/MachO/SymbolTable.h +++ b/lld/MachO/SymbolTable.h @@ -37,16 +37,18 @@ */ class SymbolTable { public: - Defined *addDefined(StringRef name, InputFile *, InputSection *, - uint64_t value, uint64_t size, bool isWeakDef, - bool isPrivateExtern, bool isThumb, - bool isReferencedDynamically, bool noDeadStrip); + Symbol *addDefined(StringRef name, InputFile *, InputSection *, + uint64_t value, uint64_t size, bool isWeakDef, + bool isPrivateExtern, bool isThumb, + bool isReferencedDynamically, bool noDeadStrip); Symbol *addUndefined(StringRef name, InputFile *, bool isWeakRef); Symbol *addCommon(StringRef name, InputFile *, uint64_t size, uint32_t align, bool isPrivateExtern); + Symbol *addBitcodeCommon(StringRef name, InputFile *, bool isPrivateExtern); + Symbol *addDylib(StringRef name, DylibFile *file, bool isWeakDef, bool isTlv); Symbol *addDynamicLookup(StringRef name); diff --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp --- a/lld/MachO/SymbolTable.cpp +++ b/lld/MachO/SymbolTable.cpp @@ -42,12 +42,12 @@ return {sym, p.second}; } -Defined *SymbolTable::addDefined(StringRef name, InputFile *file, - InputSection *isec, uint64_t value, - uint64_t size, bool isWeakDef, - bool isPrivateExtern, bool isThumb, - bool isReferencedDynamically, - bool noDeadStrip) { +Symbol *SymbolTable::addDefined(StringRef name, InputFile *file, + InputSection *isec, uint64_t value, + uint64_t size, bool isWeakDef, + bool isPrivateExtern, bool isThumb, + bool isReferencedDynamically, + bool noDeadStrip) { Symbol *s; bool wasInserted; bool overridesWeakDef = false; @@ -84,6 +84,8 @@ } else if (auto *dysym = dyn_cast(s)) { overridesWeakDef = !isWeakDef && dysym->isWeakDef(); dysym->unreference(); + } else if (isa(s) && isWeakDef) { + return s; } // Defined symbols take priority over other types of symbols, so in case // of a name conflict, we fall through to the replaceSymbol() call below. @@ -136,6 +138,22 @@ return s; } +Symbol *SymbolTable::addBitcodeCommon(StringRef name, InputFile *file, + bool isPrivateExtern) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, file); + + if (!wasInserted) { + if (auto *lazy = dyn_cast(s)) + lazy->fetchArchiveMember(); + return s; + } + + replaceSymbol(s, name, file, isPrivateExtern); + return s; +} + Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file, bool isWeakDef, bool isTlv) { Symbol *s; @@ -155,7 +173,7 @@ } bool isDynamicLookup = file == nullptr; - if (wasInserted || isa(s) || + if (wasInserted || isa(s) || isa(s) || (isa(s) && ((!isWeakDef && s->isWeakDef()) || (!isDynamicLookup && cast(s)->isDynamicLookup())))) { @@ -179,7 +197,8 @@ if (wasInserted) replaceSymbol(s, file, sym); - else if (isa(s) || (isa(s) && s->isWeakDef())) + else if (isa(s) || isa(s) || + (isa(s) && s->isWeakDef())) file->fetch(sym); return s; } @@ -188,12 +207,14 @@ uint64_t value, bool isPrivateExtern, bool includeInSymtab, bool referencedDynamically) { - Defined *s = addDefined(name, nullptr, isec, value, /*size=*/0, - /*isWeakDef=*/false, isPrivateExtern, - /*isThumb=*/false, referencedDynamically, - /*noDeadStrip=*/false); - s->includeInSymtab = includeInSymtab; - return s; + Symbol *s = addDefined(name, nullptr, isec, value, /*size=*/0, + /*isWeakDef=*/false, isPrivateExtern, + /*isThumb=*/false, referencedDynamically, + /*noDeadStrip=*/false); + // calls to addDefined() with isWeakDef=false always return a Defined + Defined *d = cast(s); + d->includeInSymtab = includeInSymtab; + return d; } void lld::macho::treatUndefinedSymbol(const Undefined &sym, StringRef source) { diff --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h --- a/lld/MachO/Symbols.h +++ b/lld/MachO/Symbols.h @@ -37,6 +37,7 @@ DefinedKind, UndefinedKind, CommonKind, + BitcodeCommonKind, DylibKind, LazyKind, }; @@ -224,6 +225,20 @@ const bool privateExtern; }; +// For some reason, ld64 applies different precedence rules to common bitcode +// symbols. Hence we create a separate type for them. +class BitcodeCommonSymbol : public Symbol { +public: + BitcodeCommonSymbol(StringRefZ name, InputFile *file, bool isPrivateExtern) + : Symbol(BitcodeCommonKind, name, file), privateExtern(isPrivateExtern) {} + + static bool classof(const Symbol *s) { + return s->kind() == BitcodeCommonKind; + } + + const bool privateExtern; +}; + class DylibSymbol : public Symbol { public: DylibSymbol(DylibFile *file, StringRefZ name, bool isWeakDef, diff --git a/lld/test/MachO/lto-common-symbol-coalescing.ll b/lld/test/MachO/lto-common-symbol-coalescing.ll new file mode 100644 --- /dev/null +++ b/lld/test/MachO/lto-common-symbol-coalescing.ll @@ -0,0 +1,75 @@ +; REQUIRES: x86 +; RUN: rm -rf %t; split-file %s %t + +; RUN: opt -module-summary %t/test.ll -o %t/test.o +; RUN: opt -module-summary %t/same-size.ll -o %t/same-size.o +; RUN: opt -module-summary %t/smaller-size.ll -o %t/smaller-size.o +; RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/same-size.s -o %t/same-size-asm.o +; RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/smaller-size.s -o %t/smaller-size-asm.o + +;; Common bitcode symbols all have equal precedence, regardless of size or +;; alignment. +; RUN: %lld -dylib %t/test.o %t/smaller-size.o -order_file %t/order -o %t/test +; RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s -D#SIZE=2 -D#ALIGN=8 +; RUN: %lld -dylib %t/smaller-size.o %t/test.o -order_file %t/order -o %t/test +; RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s -D#SIZE=1 -D#ALIGN=16 + +; RUN: %lld -dylib %t/test.o %t/same-size.o -order_file %t/order -o %t/test +; RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s -D#SIZE=2 -D#ALIGN=8 +; RUN: %lld -dylib %t/same-size.o %t/test.o -order_file %t/order -o %t/test +; RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s -D#SIZE=2 -D#ALIGN=16 + +;; Non-bitcode common symbols take precedence. +; RUN: %lld -dylib %t/test.o %t/smaller-size-asm.o -order_file %t/order -o %t/test +; RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s -D#SIZE=1 -D#ALIGN=16 +; RUN: %lld -dylib %t/smaller-size-asm.o %t/test.o -order_file %t/order -o %t/test +; RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s -D#SIZE=1 -D#ALIGN=16 + +; RUN: %lld -dylib %t/test.o %t/same-size-asm.o -order_file %t/order -o %t/test +; RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s -D#SIZE=2 -D#ALIGN=16 +; RUN: %lld -dylib %t/same-size-asm.o %t/test.o -order_file %t/order -o %t/test +; RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s -D#SIZE=2 -D#ALIGN=16 + +; CHECK-LABEL: Sections: +; CHECK: __common {{[0-9a-f]+}} [[#%x, COMMON_START:]] BSS +; +; CHECK-LABEL: SYMBOL TABLE: +; CHECK-DAG: [[#%.16x, COMMON_START]] g O __DATA,__common _check_size +; CHECK-DAG: [[#%.16x, COMMON_START + SIZE]] g O __DATA,__common _end_marker +; CHECK-DAG: [[#%.16x, COMMON_START + ALIGN]] g O __DATA,__common _check_alignment + +;--- order +;; Order is important as we determine the size of a given symbol via the +;; address of the next symbol. +_check_size +_end_marker +_check_alignment + +;--- smaller-size.ll +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-macosx10.15.0" + +@check_size = common global i8 0, align 1 +@check_alignment = common global i8 0, align 16 + +;--- same-size.ll +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-macosx10.15.0" + +@check_size = common global i16 0, align 1 +@check_alignment = common global i16 0, align 16 + +;--- smaller-size.s +.comm _check_size, 1, 1 +.comm _check_alignment, 1, 4 + +;--- same-size.s +.comm _check_size, 2, 1 +.comm _check_alignment, 2, 4 + +;--- test.ll +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-macosx10.15.0" +@check_size = common global i16 0, align 1 +@end_marker = common global i8 0 +@check_alignment = common global i16 0, align 8 diff --git a/lld/test/MachO/lto-common-symbol-resolution.ll b/lld/test/MachO/lto-common-symbol-resolution.ll new file mode 100644 --- /dev/null +++ b/lld/test/MachO/lto-common-symbol-resolution.ll @@ -0,0 +1,109 @@ +; REQUIRES: x86 + +; RUN: rm -rf %t; split-file %s %t + +; RUN: opt -module-summary %t/common.ll -o %t/common.o +; RUN: opt -module-summary %t/defined.ll -o %t/defined.o +; RUN: opt -module-summary %t/weak-defined.ll -o %t/weak-defined.o +; RUN: opt -module-summary %t/libfoo.ll -o %t/libfoo.o +; RUN: opt -module-summary %t/refs-foo.ll -o %t/refs-foo.o +; RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/weak-defined.s -o %t/weak-defined-asm.o + +; RUN: %lld -dylib -dylib %t/libfoo.o -o %t/libfoo.dylib + +; RUN: llvm-ar rcs %t/defined.a %t/defined.o +; RUN: llvm-ar rcs %t/defined-and-common.a %t/defined.o %t/common.o +; RUN: llvm-ar rcs %t/common-and-defined.a %t/common.o %t/defined.o +; RUN: llvm-ar rcs %t/weak-defined-and-common.a %t/weak-defined.o %t/common.o +; RUN: llvm-ar rcs %t/common-and-weak-defined.a %t/common.o %t/weak-defined.o + +;; Defined symbols take precedence over common bitcode symbols. +; RUN: %lld -dylib %t/defined.o %t/common.o -o %t/test +; RUN: llvm-objdump --syms %t/test | FileCheck %s --check-prefix=DEFINED +; RUN: %lld -dylib %t/common.o %t/defined.o -o %t/test +; RUN: llvm-objdump --syms %t/test | FileCheck %s --check-prefix=DEFINED + +;; NOTE: we are deviating from ld64's behavior here. +;; ld64: Defined symbols have the same precedence as common bitcode symbols +;; within an archive. +;; lld: Defined symbols take precedence over common bitcode symbols within an +;; archive. +; RUN: %lld -dylib %t/defined-and-common.a %t/refs-foo.o -o %t/refs-foo +; RUN: llvm-objdump --syms %t/refs-foo | FileCheck %s --check-prefix=DEFINED +; RUN: %lld -dylib %t/common-and-defined.a %t/refs-foo.o -o %t/refs-foo +; RUN: llvm-objdump --syms %t/refs-foo | FileCheck %s --check-prefix=DEFINED +; COM (ld64): llvm-objdump --syms %t/refs-foo | FileCheck %s --check-prefix=COMMON + +;; Weak bitcode symbols have the same precedence as common bitcode symbols. +; RUN: %lld -dylib %t/weak-defined.o %t/common.o -o %t/test +; RUN: llvm-objdump --syms %t/test | FileCheck %s --check-prefix=WEAK-DEFINED +; RUN: %lld -dylib %t/common.o %t/weak-defined.o -o %t/test +; RUN: llvm-objdump --syms %t/test | FileCheck %s --check-prefix=COMMON + +;; NOTE: we are deviating from ld64's behavior here. +;; ld64: Weak non-bitcode symbols take precedence over common bitcode symbols. +;; lld: Weak non-bitcode symbols have the same precedence as common bitcode +;; symbols. +; RUN: %lld -dylib %t/weak-defined-asm.o %t/common.o -o %t/test +; RUN: llvm-objdump --syms %t/test | FileCheck %s --check-prefix=WEAK-DEFINED +; RUN: %lld -dylib %t/common.o %t/weak-defined-asm.o -o %t/test +; RUN: llvm-objdump --syms %t/test | FileCheck %s --check-prefix=COMMON +; COM (ld64): llvm-objdump --syms %t/test | FileCheck %s --check-prefix=WEAK-DEFINED + +;; Archive symbols take precedence over common bitcode symbols. +; RUN: %lld -dylib %t/defined.a %t/common.o -o %t/test +; RUN: llvm-objdump --syms %t/test | FileCheck %s --check-prefix=DEFINED +; RUN: %lld -dylib %t/common.o %t/defined.a -o %t/test +; RUN: llvm-objdump --syms %t/test | FileCheck %s --check-prefix=DEFINED + +;; Dylib symbols take precedence over common bitcode symbols. +; RUN: %lld -dylib %t/libfoo.dylib %t/common.o %t/refs-foo.o -o %t/test +; RUN: llvm-objdump --syms %t/test | FileCheck %s --check-prefix=DYLIB +; RUN: %lld -dylib %t/common.o %t/libfoo.dylib %t/refs-foo.o -o %t/test +; RUN: llvm-objdump --syms %t/test | FileCheck %s --check-prefix=DYLIB + +; COMMON: g O __DATA,__common _foo +; DEFINED: g O __DATA,__data _foo +; WEAK-DEFINED: w O __DATA,__data _foo +; DYLIB: *UND* _foo + +;--- common.ll +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-macosx10.15.0" + +@foo = common global i8 0 + +;--- defined.ll +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-macosx10.15.0" + +@foo = global i8 12 + +;--- weak-defined.ll +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-macosx10.15.0" + +@foo = weak global i8 12 + +;--- weak-defined.s +.globl _foo +.weak_definition _foo +.data +_foo: + +;--- libfoo.ll +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-macosx10.15.0" + +@foo = common global i8 0 + +;--- refs-foo.ll +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-macosx10.15.0" + +@foo = external global i8 + +define void @f() { + %1 = load i8, i8* @foo + ret void +}