Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1733,6 +1733,11 @@ HelpText<"Enables whole-program vtable optimization. Requires -flto">; def fno_whole_program_vtables : Flag<["-"], "fno-whole-program-vtables">, Group, Flags<[CoreOption]>; +def fsplit_lto_unit : Flag<["-"], "fsplit-lto-unit">, Group, + Flags<[CoreOption, CC1Option]>, + HelpText<"Enables splitting of the LTO unit.">; +def fno_split_lto_unit : Flag<["-"], "fno-split-lto-unit">, Group, + Flags<[CoreOption]>; def fforce_emit_vtables : Flag<["-"], "fforce-emit-vtables">, Group, Flags<[CC1Option]>, HelpText<"Emits more virtual tables to improve devirtualization">; Index: include/clang/Driver/SanitizerArgs.h =================================================================== --- include/clang/Driver/SanitizerArgs.h +++ include/clang/Driver/SanitizerArgs.h @@ -79,6 +79,7 @@ bool requiresPIE() const; bool needsUnwindTables() const; + bool needsLTO() const; bool linkCXXRuntimes() const { return LinkCXXRuntimes; } bool hasCrossDsoCfi() const { return CfiCrossDso; } void addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, Index: include/clang/Frontend/CodeGenOptions.def =================================================================== --- include/clang/Frontend/CodeGenOptions.def +++ include/clang/Frontend/CodeGenOptions.def @@ -116,6 +116,10 @@ ///< compile step. CODEGENOPT(LTOUnit, 1, 0) ///< Emit IR to support LTO unit features (CFI, whole ///< program vtable opt). +CODEGENOPT(EnableSplitLTOUnit, 1, 0) ///< Enable LTO unit splitting to support + /// CFI and tranditional whole program + /// devirtulization that require whole + /// program IR support. CODEGENOPT(IncrementalLinkerCompatible, 1, 0) ///< Emit an object file which can ///< be used with an incremental ///< linker. Index: lib/CodeGen/BackendUtil.cpp =================================================================== --- lib/CodeGen/BackendUtil.cpp +++ lib/CodeGen/BackendUtil.cpp @@ -808,7 +808,8 @@ return; } PerModulePasses.add(createWriteThinLTOBitcodePass( - *OS, ThinLinkOS ? &ThinLinkOS->os() : nullptr)); + *OS, ThinLinkOS ? &ThinLinkOS->os() : nullptr, + CodeGenOpts.EnableSplitLTOUnit)); } else { // Emit a module summary by default for Regular LTO except for ld64 // targets @@ -820,9 +821,9 @@ if (EmitLTOSummary && !TheModule->getModuleFlag("ThinLTO")) TheModule->addModuleFlag(Module::Error, "ThinLTO", uint32_t(0)); - PerModulePasses.add( - createBitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists, - EmitLTOSummary)); + PerModulePasses.add(createBitcodeWriterPass( + *OS, CodeGenOpts.EmitLLVMUseLists, EmitLTOSummary, + /*EmitModuleHash=*/false, CodeGenOpts.EnableSplitLTOUnit)); } break; @@ -1043,8 +1044,9 @@ if (!ThinLinkOS) return; } - MPM.addPass(ThinLTOBitcodeWriterPass(*OS, ThinLinkOS ? &ThinLinkOS->os() - : nullptr)); + MPM.addPass(ThinLTOBitcodeWriterPass( + *OS, ThinLinkOS ? &ThinLinkOS->os() : nullptr, + CodeGenOpts.EnableSplitLTOUnit)); } else { // Emit a module summary by default for Regular LTO except for ld64 // targets @@ -1057,7 +1059,8 @@ TheModule->addModuleFlag(Module::Error, "ThinLTO", uint32_t(0)); MPM.addPass(BitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists, - EmitLTOSummary)); + EmitLTOSummary, /*EmitModuleHash=*/false, + CodeGenOpts.EnableSplitLTOUnit)); } break; Index: lib/Driver/SanitizerArgs.cpp =================================================================== --- lib/Driver/SanitizerArgs.cpp +++ lib/Driver/SanitizerArgs.cpp @@ -207,6 +207,8 @@ return Sanitizers.Mask & NeedsUnwindTables; } +bool SanitizerArgs::needsLTO() const { return Sanitizers.Mask & NeedsLTO; } + SanitizerArgs::SanitizerArgs(const ToolChain &TC, const llvm::opt::ArgList &Args) { SanitizerMask AllRemove = 0; // During the loop below, the accumulated set of Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -5108,6 +5108,12 @@ CmdArgs.push_back("-fwhole-program-vtables"); } + bool EnableSplitLTOUnit = Args.hasFlag( + options::OPT_fsplit_lto_unit, options::OPT_fno_split_lto_unit, false); + if (EnableSplitLTOUnit || WholeProgramVTables || Sanitize.needsLTO()) { + CmdArgs.push_back("-fsplit-lto-unit"); + } + if (Arg *A = Args.getLastArg(options::OPT_fexperimental_isel, options::OPT_fno_experimental_isel)) { CmdArgs.push_back("-mllvm"); Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -793,6 +793,7 @@ Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << S; } Opts.LTOUnit = Args.hasFlag(OPT_flto_unit, OPT_fno_lto_unit, false); + Opts.EnableSplitLTOUnit = Args.hasArg(OPT_fsplit_lto_unit); if (Arg *A = Args.getLastArg(OPT_fthinlto_index_EQ)) { if (IK.getLanguage() != InputKind::LLVM_IR) Diags.Report(diag::err_drv_argument_only_allowed_with) Index: test/CodeGenCXX/no-lto-unit.cpp =================================================================== --- test/CodeGenCXX/no-lto-unit.cpp +++ test/CodeGenCXX/no-lto-unit.cpp @@ -2,6 +2,8 @@ // RUN: llvm-dis -o - %t | FileCheck %s // RUN: %clang_cc1 -flto=thin -flto-unit -fno-lto-unit -triple x86_64-unknown-linux -fvisibility hidden -emit-llvm-bc -o %t %s // RUN: llvm-dis -o - %t | FileCheck %s +// RUN: llvm-bcanalyzer -dump %t | FileCheck %s --check-prefix=NOLTOUNIT +// NOLTOUNIT: // CHECK-NOT: !type class A { Index: test/CodeGenCXX/type-metadata-thinlto.cpp =================================================================== --- test/CodeGenCXX/type-metadata-thinlto.cpp +++ test/CodeGenCXX/type-metadata-thinlto.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -flto=thin -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -emit-llvm-bc -o %t %s // RUN: llvm-modextract -o - -n 1 %t | llvm-dis | FileCheck %s +// RUN: llvm-modextract -b -o - -n 1 %t | llvm-bcanalyzer -dump | FileCheck %s --check-prefix=LTOUNIT +// LTOUNIT: // CHECK: @_ZTV1A = linkonce_odr class A { Index: test/Driver/split-lto-unit.c =================================================================== --- /dev/null +++ test/Driver/split-lto-unit.c @@ -0,0 +1,8 @@ +// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin 2>&1 | FileCheck --check-prefix=NOUNIT %s +// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -fsplit-lto-unit 2>&1 | FileCheck --check-prefix=UNIT %s +// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -fno-split-lto-unit 2>&1 | FileCheck --check-prefix=NOUNIT %s +// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -fno-split-lto-unit -fwhole-program-vtables 2>&1 | FileCheck --check-prefix=UNIT %s +// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -fno-split-lto-unit -fsanitize=cfi 2>&1 | FileCheck --check-prefix=UNIT %s + +// UNIT: "-fsplit-lto-unit" +// NOUNIT-NOT: "-fsplit-lto-unit"