Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7798,7 +7798,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
options::OPT_fno_fat_lto_objects)) {
if (IsUsingLTO && A->getOption().matches(options::OPT_ffat_lto_objects)) {
assert(LTOMode == LTOK_Full || LTOMode == LTOK_Thin);
if (!Triple.isOSBinFormatELF()) {
if (!Triple.isOSBinFormatELF() && !Triple.isOSBinFormatCOFF()) {
D.Diag(diag::err_drv_unsupported_opt_for_target)
<< A->getAsString(Args) << TC.getTripleString();
}
Expand Down
14 changes: 14 additions & 0 deletions clang/test/CodeGen/fat-lto-objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
// RUN: %clang -cc1 -triple x86_64-unknown-linux-gnu -flto=full -ffat-lto-objects -fsplit-lto-unit -S < %s -o - \
// RUN: | FileCheck %s --check-prefixes=ASM

// RUN: %clang -cc1 -triple i386-pc-win32 -flto=full -ffat-lto-objects -fsplit-lto-unit -S < %s -o - \
// RUN: | FileCheck %s --check-prefixes=ASM-COFF

/// Make sure that FatLTO generates .llvm.lto sections that are the same as the output from normal LTO compilations
// RUN: %clang -O2 --target=x86_64-unknown-linux-gnu -fPIE -flto=full -ffat-lto-objects -c %s -o %t.fatlto.full.o
// RUN: llvm-objcopy --dump-section=.llvm.lto=%t.fatlto.full.bc %t.fatlto.full.o
Expand All @@ -43,6 +46,13 @@
// RUN: llvm-dis < %t.nofat.full.bc -o %t.nofat.full.ll
// RUN: diff %t.fatlto.full.ll %t.nofat.full.ll

// RUN: %clang -O2 --target=i386-pc-win32 -flto=full -ffat-lto-objects -c %s -o %t.fatlto.full.coff.o
// RUN: llvm-objcopy --dump-section=.llvm.lto=%t.fatlto.full.coff.bc %t.fatlto.full.coff.o
// RUN: llvm-dis < %t.fatlto.full.coff.bc -o %t.fatlto.full.coff.ll
// RUN: %clang -O2 --target=i386-pc-win32 -flto=full -c %s -o %t.nofat.full.coff.bc
// RUN: llvm-dis < %t.nofat.full.coff.bc -o %t.nofat.full.coff.ll
// RUN: diff %t.fatlto.full.coff.ll %t.nofat.full.coff.ll

// RUN: %clang -O2 --target=x86_64-unknown-linux-gnu -fPIE -flto=thin -ffat-lto-objects -c %s -o %t.fatlto.thin.o
// RUN: llvm-objcopy --dump-section=.llvm.lto=%t.fatlto.thin.bc %t.fatlto.thin.o
// RUN: llvm-dis < %t.fatlto.thin.bc -o %t.fatlto.thin.ll
Expand All @@ -67,6 +77,10 @@
// ASM-NEXT: .asciz "BC
// ASM-NEXT: .size .Lllvm.embedded.object

// ASM-COFF: .section .llvm.lto,"ynD"
// ASM-COFF-NEXT: L_llvm.embedded.object:
// ASM-COFF-NEXT: .asciz "BC

const char* foo = "foo";

int test(void) {
Expand Down
8 changes: 8 additions & 0 deletions clang/test/Driver/fat-lto-objects.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC
// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC
// CHECK-CC: -cc1
// CHECK-CC-SAME: -emit-obj
// CHECK-CC-SAME: -ffat-lto-objects

/// Without -flto -S will just emit normal ASM, so we don't expect -emit-{llvm,obj} or -ffat-lto-objects to be passed to cc1.
// RUN: %clang --target=x86_64-unknown-linux-gnu -ffat-lto-objects -### %s -S 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S
// RUN: %clang --target=i386-pc-win32 -ffat-lto-objects -### %s -S 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S
// CHECK-CC-S: -cc1
// CHECK-CC-S: -S
// CHECK-CC-S-NOT: -emit-obj
Expand All @@ -13,35 +15,41 @@

/// When fat LTO is enabled with -S, we expect asm output and -ffat-lto-objects to be passed to cc1.
// RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -### %s -S 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S-LTO
// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -### %s -S 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S-LTO
// CHECK-CC-S-LTO: -cc1
// CHECK-CC-S-NOT: -emit-llvm
// CHECK-CC-S-LTO-SAME: -ffat-lto-objects

/// When fat LTO is enabled with -S and -emit-llvm, we expect IR output and -ffat-lto-objects to be passed to cc1.
// RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -### %s -S -emit-llvm 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S-EL-LTO
// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -### %s -S -emit-llvm 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S-EL-LTO
// CHECK-CC-S-EL-LTO: -cc1
// CHECK-CC-S-EL-LTO-SAME: -emit-llvm
// CHECK-CC-S-EL-LTO-SAME: -ffat-lto-objects

/// When fat LTO is enabled without -S we expect native object output and -ffat-lto-object to be passed to cc1.
// RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-C-LTO
// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-C-LTO
// CHECK-CC-C-LTO: -cc1
// CHECK-CC-C-LTO-SAME: -emit-obj
// CHECK-CC-C-LTO-SAME: -ffat-lto-objects

/// When fat LTO is enabled with -c and -emit-llvm we expect bitcode output and -ffat-lto-object to be passed to cc1.
// RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -### %s -c -emit-llvm 2>&1 | FileCheck %s -check-prefix=CHECK-CC-C-EL-LTO
// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -### %s -c -emit-llvm 2>&1 | FileCheck %s -check-prefix=CHECK-CC-C-EL-LTO
// CHECK-CC-C-EL-LTO: -cc1
// CHECK-CC-C-EL-LTO-SAME: -emit-llvm-bc
// CHECK-CC-C-EL-LTO-SAME: -ffat-lto-objects

/// Make sure we don't have a warning for -ffat-lto-objects being unused
// RUN: %clang --target=x86_64-unknown-linux-gnu -ffat-lto-objects -fdriver-only -Werror -v %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-NOLTO
// RUN: %clang --target=i386-pc-win32 -ffat-lto-objects -fdriver-only -Werror -v %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-NOLTO
// CHECK-CC-NOLTO: -cc1
// CHECK-CC-NOLTO-SAME: -emit-obj
// CHECK-CC-NOLTO-NOT: -ffat-lto-objects

// RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -fno-fat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-NOLTO
// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -fno-fat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-NOLTO

/// We need to pass an additional flag (--fat-lto-objects) to lld when linking w/ -flto -ffat-lto-objects
/// But it should not be there when LTO is disabled w/ -fno-lto
Expand Down
3 changes: 3 additions & 0 deletions lld/COFF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ struct Configuration {
// Used for /thinlto-remote-compiler-arg:<arg>
llvm::SmallVector<llvm::StringRef, 0> dtltoCompilerArgs;

// Used for /fat-lto-objects
bool fatLTOObjects = false;

// Used for /opt:[no]ltodebugpassmanager
bool ltoDebugPassManager = false;

Expand Down
35 changes: 33 additions & 2 deletions lld/COFF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "llvm/Config/llvm-config.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
Expand Down Expand Up @@ -256,6 +257,23 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
return mbref;
}

InputFile *LinkerDriver::tryCreateFatLTOFile(MemoryBufferRef mb,
StringRef archiveName,
uint64_t offsetInArchive,
bool lazy) {
if (!ctx.config.fatLTOObjects)
return nullptr;

Expected<MemoryBufferRef> fatLTOData =
IRObjectFile::findBitcodeInMemBuffer(mb);

if (errorToBool(fatLTOData.takeError()))
return nullptr;

return BitcodeFile::create(ctx, *fatLTOData, archiveName, offsetInArchive,
lazy);
}

void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
bool wholeArchive, bool lazy) {
StringRef filename = mb->getBufferIdentifier();
Expand Down Expand Up @@ -289,7 +307,14 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
case file_magic::bitcode:
addFile(BitcodeFile::create(ctx, mbref, "", 0, lazy));
break;
case file_magic::coff_object:
case file_magic::coff_object: {
InputFile *obj = tryCreateFatLTOFile(mbref, "", 0, lazy);
if (obj)
addFile(obj);
else
addFile(ObjFile::create(ctx, mbref, lazy));
break;
}
case file_magic::coff_import_library:
addFile(ObjFile::create(ctx, mbref, lazy));
break;
Expand Down Expand Up @@ -374,7 +399,9 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName,

InputFile *obj;
if (magic == file_magic::coff_object) {
obj = ObjFile::create(ctx, mb);
obj = tryCreateFatLTOFile(mb, parentName, offsetInArchive, /*lazy=*/false);
if (!obj)
obj = ObjFile::create(ctx, mb);
} else if (magic == file_magic::bitcode) {
obj = BitcodeFile::create(ctx, mb, parentName, offsetInArchive,
/*lazy=*/false);
Expand Down Expand Up @@ -2121,6 +2148,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
config->dtltoCompilerArgs =
args::getStrings(args, OPT_thinlto_remote_compiler_arg);

// Handle /fat-lto-objects
config->fatLTOObjects =
args.hasFlag(OPT_fat_lto_objects, OPT_fat_lto_objects_no, false);

// Handle /dwodir
config->dwoDir = args.getLastArgValue(OPT_dwodir);

Expand Down
2 changes: 2 additions & 0 deletions lld/COFF/Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ class LinkerDriver {

std::set<std::string> visitedLibs;

InputFile *tryCreateFatLTOFile(MemoryBufferRef mb, StringRef archiveName,
uint64_t offsetInArchive, bool lazy);
void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive,
bool lazy);
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
Expand Down
3 changes: 3 additions & 0 deletions lld/COFF/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ def thinlto_remote_compiler_prepend_arg : P<"thinlto-remote-compiler-prepend-arg
def thinlto_remote_compiler_arg : P<"thinlto-remote-compiler-arg",
"Compiler arguments for the ThinLTO distributor to pass for ThinLTO backend "
"compilations">;
defm fat_lto_objects: B<"fat-lto-objects",
"Use the .llvm.lto section, which contains LLVM bitcode, in fat LTO object files to perform LTO.",
"Ignore the .llvm.lto section in relocatable object files (default).">;
def lto_obj_path : P<
"lto-obj-path",
"output native object for merged LTO unit to this path">;
Expand Down
3 changes: 3 additions & 0 deletions lld/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ Breaking changes
COFF Improvements
-----------------

* ``/fat-lto-objects`` addded to support FatLTO. Without ``/fat-lto-objects`` or with ``/fat-lto-objects:no``, LLD will link LLVM FatLTO objects using the relocatable object file.
(`#165529 <https://github.com/llvm/llvm-project/pull/165529>`_)

MinGW Improvements
------------------

Expand Down
8 changes: 8 additions & 0 deletions lld/test/COFF/fatlto/fatlto-invalid.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# RUN: llvm-mc -filetype=obj -triple=i386-pc-win32 %s -o %t
# RUN: env LLD_IN_TEST=1 not lld-link %t /out:/dev/null /fat-lto-objects 2>&1 | FileCheck %s

# CHECK: error:{{.*}} Invalid bitcode signature

.section .llvm.lto,"ynD"
L_llvm.embedded.object:
.asciz "BC\300\3365\000"
111 changes: 111 additions & 0 deletions lld/test/COFF/fatlto/fatlto.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
;; Basic FatLTO tests.
; REQUIRES: x86

; RUN: rm -rf %t && split-file %s %t && cd %t

;; Ensure that input files contain .llvm.lto section.
; RUN: llc a-LTO.ll --filetype=obj -o a-fatLTO.o --relocation-model=pic
; RUN: opt < a-LTO.ll --module-summary -o a-fatLTO.bc
; RUN: llvm-objcopy --add-section=.llvm.lto=a-fatLTO.bc --set-section-flags=.llvm.lto=exclude a-fatLTO.o

; RUN: llc main-LTO.ll --filetype=obj -o main-fatLTO.o --relocation-model=pic
; RUN: opt < main-LTO.ll --module-summary -o main-fatLTO.bc
; RUN: llvm-objcopy --add-section=.llvm.lto=main-fatLTO.bc --set-section-flags=.llvm.lto=exclude main-fatLTO.o

; RUN: llvm-readobj --sections a-fatLTO.o | FileCheck --check-prefix=HAS_LLVM_LTO %s
; RUN: llvm-readobj --sections main-fatLTO.o | FileCheck --check-prefix=HAS_LLVM_LTO %s

;; Make sure that the section flags are set correctly
; HAS_LLVM_LTO: Name: .llvm.lto (2F 34 00 00 00 00 00 00)
; HAS_LLVM_LTO: Characteristics [
; HAS_LLVM_LTO-SAME: (0xC0000800)
; HAS_LLVM_LTO-NEXT: IMAGE_SCN_LNK_REMOVE (0x800)
; HAS_LLVM_LTO-NEXT: IMAGE_SCN_MEM_READ (0x40000000)
; HAS_LLVM_LTO-NEXT: IMAGE_SCN_MEM_WRITE (0x80000000)
; HAS_LLVM_LTO-NEXT: ]

;; Final executable should not have .llvm.lto section no matter what the target is.
; RUN: lld-link /timestamp:0 /out:foo-fatLTO /entry:main a-fatLTO.o main-fatLTO.o -fat-lto-objects
; RUN: llvm-readobj --sections foo-fatLTO | FileCheck --check-prefix=CHECK-LTO-TARGET %s

;; Check that fat objects work w/ --start-lib.
; RUN: lld-link /out:foo-fatLTO.start_lib /entry:main -start-lib a-fatLTO.o main-fatLTO.o -fat-lto-objects
; RUN: llvm-readobj --sections foo-fatLTO.start_lib | FileCheck --check-prefix=CHECK-LTO-TARGET %s

;; Check if .llvm.lto section gets aggregated in LTO target.
; CHECK-LTO-TARGET-NOT: Name: .llvm.lto

;; Final executable should not have .llvm.lto section no matter what the target is.
; RUN: lld-link /timestamp:0 /out:foo-fatNoLTO /entry:main a-fatLTO.o %/t/main-fatLTO.o
; RUN: llvm-readobj --sections foo-fatNoLTO | FileCheck --check-prefix=CHECK-NON-LTO-TARGET %s

;; Check if .llvm.lto section gets aggregated in non-LTO target.
; CHECK-NON-LTO-TARGET-NOT: Name: .llvm.lto

;; Check if the LTO target executable produced from FatLTO object file is
;; identical to the one produced from LTO modules.
; RUN: opt < a-LTO.ll --module-summary -o a-LTO.bc
; RUN: opt < main-LTO.ll --module-summary -o main-LTO.bc
; RUN: lld-link /timestamp:0 /out:foo-LTO /entry:main a-LTO.bc main-LTO.bc
; RUN: cmp foo-fatLTO foo-LTO

;; Check if the no-LTO target executable produced from FatLTO object file is
;; identical to the one produced from regular object files.
; RUN: llc a-LTO.ll --filetype=obj -o a.o
; RUN: llc main-LTO.ll --filetype=obj -o main.o
; RUN: lld-link /timestamp:0 /out:foo-noLTO /entry:main a.o main.o
; RUN: cmp foo-fatNoLTO foo-noLTO

;; Check archive support.
; RUN: llvm-ar rcs a.a a-fatLTO.o
; RUN: lld-link /timestamp:0 /out:foo-fatLTO.archive /entry:main /wholearchive a.a main-LTO.bc -fat-lto-objects
; RUN: cmp foo-fatLTO.archive foo-LTO

;--- a-LTO.ll
target datalayout = "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32"
target triple = "i386-pc-windows-msvc19.33.0"

; Function Attrs: noinline nounwind uwtable
define dso_local i32 @_start() #0 {
entry:
ret i32 0
}

attributes #0 = { noinline nounwind uwtable }

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6}

!0 = !{i32 1, !"NumRegisterParameters", i32 0}
!1 = !{i32 2, !"Debug Info Version", i32 3}
!2 = !{i32 1, !"wchar_size", i32 2}
!3 = !{i32 7, !"frame-pointer", i32 2}
!4 = !{i32 1, !"MaxTLSAlign", i32 65536}
!5 = !{i32 1, !"ThinLTO", i32 0}
!6 = !{i32 1, !"EnableSplitLTOUnit", i32 1}

;--- main-LTO.ll
target datalayout = "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32"
target triple = "i386-pc-windows-msvc19.33.0"

; Function Attrs: noinline nounwind uwtable
define dso_local i32 @main() #0 {
entry:
%retval = alloca i32, align 4
store i32 0, ptr %retval, align 4
%call = call i32 (...) @_start()
ret i32 %call
}

declare i32 @_start(...)

attributes #0 = { noinline nounwind uwtable }

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6}

!0 = !{i32 1, !"NumRegisterParameters", i32 0}
!1 = !{i32 2, !"Debug Info Version", i32 3}
!2 = !{i32 1, !"wchar_size", i32 2}
!3 = !{i32 7, !"frame-pointer", i32 2}
!4 = !{i32 1, !"MaxTLSAlign", i32 65536}
!5 = !{i32 1, !"ThinLTO", i32 0}
!6 = !{i32 1, !"EnableSplitLTOUnit", i32 1}
6 changes: 3 additions & 3 deletions llvm/lib/Transforms/IPO/EmbedBitcodePass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ PreservedAnalyses EmbedBitcodePass::run(Module &M, ModuleAnalysisManager &AM) {
reportFatalUsageError("Can only embed the module once");

Triple T(M.getTargetTriple());
if (T.getObjectFormat() != Triple::ELF)
reportFatalUsageError(
"EmbedBitcode pass currently only supports ELF object format");
if (T.getObjectFormat() != Triple::ELF && T.getObjectFormat() != Triple::COFF)
reportFatalUsageError("EmbedBitcode pass currently only supports COFF and "
"ELF object formats");

std::string Data;
raw_string_ostream OS(Data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

@a = global i32 1

; CHECK: LLVM ERROR: EmbedBitcode pass currently only supports ELF object format
; CHECK: LLVM ERROR: EmbedBitcode pass currently only supports COFF and ELF object formats