diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -267,6 +267,10 @@ directory (``/tmp`` on \*NIX systems, if none of the environment variables TMPDIR, TMP, and TEMP are specified). +- ``-ffat-lto-objects`` can now be used to emit object files with both object + code and LLVM bitcode. Previously this flag was ignored for GCC compatibility. + (`See patch `_). + Removed Compiler Flags ------------------------- - The deprecated flag `-fmodules-ts` is removed. Please use ``-std=c++20`` diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -164,6 +164,7 @@ ///< compile step. CODEGENOPT(LTOUnit, 1, 0) ///< Emit IR to support LTO unit features (CFI, whole ///< program vtable opt). +CODEGENOPT(FatLTO, 1, 0) ///< Set when -ffat-lto-objects is enabled. CODEGENOPT(EnableSplitLTOUnit, 1, 0) ///< Enable LTO unit splitting to support /// CFI and traditional whole program /// devirtualization that require whole diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2366,6 +2366,11 @@ Flags<[CoreOption, CC1Option]>, Group, HelpText<"Write minimized bitcode to for the ThinLTO thin link only">, MarshallingInfoString>; +defm fat_lto_objects : BoolFOption<"fat-lto-objects", + CodeGenOpts<"FatLTO">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[CC1Option], " fat LTO object support">>; def fmacro_backtrace_limit_EQ : Joined<["-"], "fmacro-backtrace-limit=">, Group, Flags<[NoXarchOption, CC1Option, CoreOption]>, HelpText<"Set the maximum number of entries to print in a macro expansion backtrace (0 = no limit)">, @@ -5136,7 +5141,6 @@ defm reorder_blocks : BooleanFFlag<"reorder-blocks">, Group; defm branch_count_reg : BooleanFFlag<"branch-count-reg">, Group; defm default_inline : BooleanFFlag<"default-inline">, Group; -defm fat_lto_objects : BooleanFFlag<"fat-lto-objects">, Group; defm float_store : BooleanFFlag<"float-store">, Group; defm friend_injection : BooleanFFlag<"friend-injection">, Group; defm function_attribute_list : BooleanFFlag<"function-attribute-list">, Group; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -55,6 +55,7 @@ #include "llvm/Target/TargetOptions.h" #include "llvm/TargetParser/SubtargetFeature.h" #include "llvm/TargetParser/Triple.h" +#include "llvm/Transforms/IPO/EmbedBitcodePass.h" #include "llvm/Transforms/IPO/LowerTypeTests.h" #include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h" #include "llvm/Transforms/InstCombine/InstCombine.h" @@ -1015,7 +1016,12 @@ }); } - if (IsThinLTO || (IsLTO && CodeGenOpts.UnifiedLTO)) { + bool IsThinOrUnifiedLTO = IsThinLTO || (IsLTO && CodeGenOpts.UnifiedLTO); + if (CodeGenOpts.FatLTO) { + MPM = PB.buildFatLTODefaultPipeline(Level, IsThinOrUnifiedLTO, + IsThinOrUnifiedLTO || + shouldEmitRegularLTOSummary()); + } else if (IsThinOrUnifiedLTO) { MPM = PB.buildThinLTOPreLinkDefaultPipeline(Level); } else if (IsLTO) { MPM = PB.buildLTOPreLinkDefaultPipeline(Level); @@ -1071,6 +1077,21 @@ EmitLTOSummary)); } } + if (CodeGenOpts.FatLTO) { + // Set module flags, like EnableSplitLTOUnit and UnifiedLTO, since FatLTO + // uses a different action than Backend_EmitBC or Backend_EmitLL. + bool IsThinOrUnifiedLTO = + CodeGenOpts.PrepareForThinLTO || + (CodeGenOpts.PrepareForLTO && CodeGenOpts.UnifiedLTO); + if (!TheModule->getModuleFlag("ThinLTO")) + TheModule->addModuleFlag(Module::Error, "ThinLTO", + uint32_t(IsThinOrUnifiedLTO)); + if (!TheModule->getModuleFlag("EnableSplitLTOUnit")) + TheModule->addModuleFlag(Module::Error, "EnableSplitLTOUnit", + uint32_t(CodeGenOpts.EnableSplitLTOUnit)); + if (CodeGenOpts.UnifiedLTO && !TheModule->getModuleFlag("UnifiedLTO")) + TheModule->addModuleFlag(Module::Error, "UnifiedLTO", uint32_t(1)); + } // Now that we have all of the passes ready, run them. { diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -4729,8 +4729,13 @@ } case phases::Backend: { if (isUsingLTO() && TargetDeviceOffloadKind == Action::OFK_None) { - types::ID Output = - Args.hasArg(options::OPT_S) ? types::TY_LTO_IR : types::TY_LTO_BC; + types::ID Output; + if (Args.hasArg(options::OPT_S)) + Output = types::TY_LTO_IR; + else if (Args.hasArg(options::OPT_ffat_lto_objects)) + Output = types::TY_PP_Asm; + else + Output = types::TY_LTO_BC; return C.MakeAction(Input, Output); } if (isUsingLTO(/* IsOffload */ true) && diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7354,6 +7354,22 @@ if (SplitLTOUnit) CmdArgs.push_back("-fsplit-lto-unit"); + if (Arg *A = Args.getLastArg(options::OPT_ffat_lto_objects, + 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()) { + D.Diag(diag::err_drv_unsupported_opt_for_target) + << A->getAsString(Args) << TC.getTripleString(); + } + CmdArgs.push_back(Args.MakeArgString( + Twine("-flto=") + (LTOMode == LTOK_Thin ? "thin" : "full"))); + CmdArgs.push_back("-flto-unit"); + CmdArgs.push_back("-ffat-lto-objects"); + A->render(Args, CmdArgs); + } + } + if (Arg *A = Args.getLastArg(options::OPT_fglobal_isel, options::OPT_fno_global_isel)) { CmdArgs.push_back("-mllvm"); diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -617,6 +617,11 @@ PluginName + Suffix, Plugin); CmdArgs.push_back(Args.MakeArgString(Twine(PluginPrefix) + Plugin)); + } else { + // Tell LLD to find and use .llvm.lto section in regular relocatable object + // files + if (Args.hasArg(options::OPT_ffat_lto_objects)) + CmdArgs.push_back("--fat-lto-objects"); } const char *PluginOptPrefix = IsOSAIX ? "-bplugin_opt:" : "-plugin-opt="; diff --git a/clang/test/CodeGen/fat-lto-objects.c b/clang/test/CodeGen/fat-lto-objects.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/fat-lto-objects.c @@ -0,0 +1,57 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang -cc1 -triple x86_64-unknown-linux-gnu -flto=full -ffat-lto-objects -fsplit-lto-unit -emit-llvm < %s | FileCheck %s --check-prefixes=FULL,SPLIT +// RUN: %clang -cc1 -triple x86_64-unknown-linux-gnu -flto=full -ffat-lto-objects -emit-llvm < %s | FileCheck %s --check-prefixes=FULL,SPLIT + +// RUN: %clang -cc1 -triple x86_64-unknown-linux-gnu -flto=thin -fsplit-lto-unit -ffat-lto-objects -emit-llvm < %s | FileCheck %s --check-prefixes=THIN,SPLIT +// RUN: %clang -cc1 -triple x86_64-unknown-linux-gnu -flto=thin -ffat-lto-objects -emit-llvm < %s | FileCheck %s --check-prefixes=THIN,NOSPLIT + +// RUN: %clang -cc1 -triple x86_64-unknown-linux-gnu -flto=full -ffat-lto-objects -fsplit-lto-unit -emit-obj < %s -o %t.full.split.o +// RUN: llvm-readelf -S %t.full.split.o | FileCheck %s --check-prefixes=ELF +// RUN: llvm-objcopy --dump-section=.llvm.lto=%t.full.split.bc %t.full.split.o +// RUN: llvm-dis %t.full.split.bc -o - | FileCheck %s --check-prefixes=FULL,SPLIT,NOUNIFIED + +// RUN: %clang -cc1 -triple x86_64-unknown-linux-gnu -flto=full -ffat-lto-objects -emit-obj < %s -o %t.full.nosplit.o +// RUN: llvm-readelf -S %t.full.nosplit.o | FileCheck %s --check-prefixes=ELF +// RUN: llvm-objcopy --dump-section=.llvm.lto=%t.full.nosplit.bc %t.full.nosplit.o +// RUN: llvm-dis %t.full.nosplit.bc -o - | FileCheck %s --check-prefixes=FULL,NOSPLIT,NOUNIFIED + +// RUN: %clang -cc1 -triple x86_64-unknown-linux-gnu -flto=thin -fsplit-lto-unit -ffat-lto-objects -emit-obj < %s -o %t.thin.split.o +// RUN: llvm-readelf -S %t.thin.split.o | FileCheck %s --check-prefixes=ELF +// RUN: llvm-objcopy --dump-section=.llvm.lto=%t.thin.split.bc %t.thin.split.o +// RUN: llvm-dis %t.thin.split.bc -o - | FileCheck %s --check-prefixes=THIN,SPLIT,NOUNIFIED + +// RUN: %clang -cc1 -triple x86_64-unknown-linux-gnu -flto=thin -ffat-lto-objects -emit-obj < %s -o %t.thin.nosplit.o +// RUN: llvm-readelf -S %t.thin.nosplit.o | FileCheck %s --check-prefixes=ELF +// RUN: llvm-objcopy --dump-section=.llvm.lto=%t.thin.nosplit.bc %t.thin.nosplit.o +// RUN: llvm-dis %t.thin.nosplit.bc -o - | FileCheck %s --check-prefixes=THIN,NOSPLIT,NOUNIFIED + +// RUN: %clang -cc1 -triple x86_64-unknown-linux-gnu -flto=thin -funified-lto -ffat-lto-objects -emit-obj < %s -o %t.unified.o +// RUN: llvm-readelf -S %t.unified.o | FileCheck %s --check-prefixes=ELF +// RUN: llvm-objcopy --dump-section=.llvm.lto=%t.unified.bc %t.unified.o +// RUN: llvm-dis %t.unified.bc -o - | FileCheck %s --check-prefixes=THIN,NOSPLIT,UNIFIED + +// 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 + +/// Check that the ThinLTO metadata is only set false for full LTO. +// FULL: ![[#]] = !{i32 1, !"ThinLTO", i32 0} +// THIN-NOT: ![[#]] = !{i32 1, !"ThinLTO", i32 0} + +/// Be sure we enable split LTO units correctly under -ffat-lto-objects. +// SPLIT: ![[#]] = !{i32 1, !"EnableSplitLTOUnit", i32 1} +// NOSPLIT: ![[#]] = !{i32 1, !"EnableSplitLTOUnit", i32 0} + +// UNIFIED: ![[#]] = !{i32 1, !"UnifiedLTO", i32 1} +// NOUNIFIED-NOT: ![[#]] = !{i32 1, !"UnifiedLTO", i32 1} + +// ELF: .llvm.lto + +// ASM: .section .llvm.lto,"e",@progbits +// ASM-NEXT: .Lllvm.embedded.object: +// ASM-NEXT: .asciz "BC +// ASM-NEXT: .size .Lllvm.embedded.object + +int test(void) { + return 0xabcd; +} diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c --- a/clang/test/Driver/clang_f_opts.c +++ b/clang/test/Driver/clang_f_opts.c @@ -424,7 +424,6 @@ // CHECK-WARNING-DAG: optimization flag '-fwhole-program' is not supported // CHECK-WARNING-DAG: optimization flag '-fcaller-saves' is not supported // CHECK-WARNING-DAG: optimization flag '-freorder-blocks' is not supported -// CHECK-WARNING-DAG: optimization flag '-ffat-lto-objects' is not supported // CHECK-WARNING-DAG: optimization flag '-fmerge-constants' is not supported // CHECK-WARNING-DAG: optimization flag '-finline-small-functions' is not supported // CHECK-WARNING-DAG: optimization flag '-ftree-dce' is not supported diff --git a/clang/test/Driver/fat-lto-objects.c b/clang/test/Driver/fat-lto-objects.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/fat-lto-objects.c @@ -0,0 +1,34 @@ +// RUN: %clang --target=x86_64-unknown-linux-gnu -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 +// CHECK-CC-S: -cc1 +// CHECK-CC-S: -S +// CHECK-CC-S-NOT: -emit-obj +// CHECK-CC-S-NOT: -emit-llvm +// CHECK-CC-S-NOT: -ffat-lto-objects + +/// When LTO is enabled, we expect LLVM 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 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S-LTO +// 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-LTO +// CHECK-CC-S-LTO: -cc1 +// CHECK-CC-S-LTO-SAME: -emit-llvm +// CHECK-CC-S-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 +// CHECK-CC-NOLTO: -cc1 +// CHECK-CC-NOLTO-SAME: -emit-obj +// CHECK-CC-NOLTO-NOT: -ffat-lto-objects + +/// 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 +// RUN: %clang --target=x86_64-unknown-linux-gnu --sysroot=%S/Inputs/basic_cross_linux_tree %s \ +// RUN: -fuse-ld=lld -flto -ffat-lto-objects -### 2>&1 | FileCheck --check-prefix=LTO %s +// RUN: %clang --target=x86_64-unknown-linux-gnu --sysroot=%S/Inputs/basic_cross_linux_tree %s \ +// RUN: -fuse-ld=lld -fno-lto -ffat-lto-objects -### 2>&1 | FileCheck --check-prefix=NOLTO %s +// LTO: "--fat-lto-objects" +// NOLTO-NOT: "--fat-lto-objects"