[llvm][LLD][COFF] Add fat-lto-object support for COFF targets (#165529)
This adds support for FatLTO to COFF targets in clang and lld. The changes are adapted from610fc5cbccand14e3bec8fcbut much smaller because it just needed the COFF-specific parts wired in, and I tried my best to adapt the pre-existing ELF tests for the COFF version. My main goal is to be able to use this for shipping pre-built https://github.com/XboxDev/nxdk container images someday, which uses the `i386-pc-win32` target.
This commit is contained in:
parent
cf5aeed7ba
commit
759fb0a224
@ -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;
|
||||
|
||||
|
||||
@ -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"
|
||||
@ -256,6 +257,24 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
|
||||
return mbref;
|
||||
}
|
||||
|
||||
static InputFile *tryCreateFatLTOFile(COFFLinkerContext &ctx,
|
||||
MemoryBufferRef mb, StringRef archiveName,
|
||||
uint64_t offsetInArchive, bool lazy) {
|
||||
if (ctx.config.fatLTOObjects) {
|
||||
Expected<MemoryBufferRef> fatLTOData =
|
||||
IRObjectFile::findBitcodeInMemBuffer(mb);
|
||||
|
||||
if (!errorToBool(fatLTOData.takeError())) {
|
||||
return BitcodeFile::create(ctx, *fatLTOData, archiveName, offsetInArchive,
|
||||
lazy);
|
||||
}
|
||||
}
|
||||
|
||||
InputFile *obj = ObjFile::create(ctx, mb, lazy);
|
||||
obj->parentName = archiveName;
|
||||
return obj;
|
||||
}
|
||||
|
||||
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
||||
bool wholeArchive, bool lazy) {
|
||||
StringRef filename = mb->getBufferIdentifier();
|
||||
@ -289,7 +308,10 @@ 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: {
|
||||
addFile(tryCreateFatLTOFile(ctx, mbref, "", 0, lazy));
|
||||
break;
|
||||
}
|
||||
case file_magic::coff_import_library:
|
||||
addFile(ObjFile::create(ctx, mbref, lazy));
|
||||
break;
|
||||
@ -374,7 +396,8 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName,
|
||||
|
||||
InputFile *obj;
|
||||
if (magic == file_magic::coff_object) {
|
||||
obj = ObjFile::create(ctx, mb);
|
||||
obj = tryCreateFatLTOFile(ctx, mb, parentName, offsetInArchive,
|
||||
/*lazy=*/false);
|
||||
} else if (magic == file_magic::bitcode) {
|
||||
obj = BitcodeFile::create(ctx, mb, parentName, offsetInArchive,
|
||||
/*lazy=*/false);
|
||||
@ -2129,6 +2152,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);
|
||||
|
||||
|
||||
@ -299,6 +299,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">;
|
||||
|
||||
@ -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
|
||||
------------------
|
||||
|
||||
|
||||
14
lld/test/COFF/fatlto/fatlto-invalid-obj.test
Normal file
14
lld/test/COFF/fatlto/fatlto-invalid-obj.test
Normal file
@ -0,0 +1,14 @@
|
||||
# RUN: yaml2obj %s -o %t.obj
|
||||
# RUN: env LLD_IN_TEST=1 not lld-link %t.obj /fat-lto-objects 2>&1 | FileCheck %s
|
||||
|
||||
# CHECK: getSectionName failed: #1:
|
||||
|
||||
--- !COFF
|
||||
header:
|
||||
Machine: IMAGE_FILE_MACHINE_AMD64
|
||||
Characteristics: []
|
||||
sections:
|
||||
- Name: '/1'
|
||||
Characteristics: []
|
||||
SectionData: 00
|
||||
symbols:
|
||||
8
lld/test/COFF/fatlto/fatlto-invalid.s
Normal file
8
lld/test/COFF/fatlto/fatlto-invalid.s
Normal file
@ -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
lld/test/COFF/fatlto/fatlto.test
Normal file
111
lld/test/COFF/fatlto/fatlto.test
Normal file
@ -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}
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user