Index: include/clang/Basic/CodeGenOptions.def =================================================================== --- include/clang/Basic/CodeGenOptions.def +++ include/clang/Basic/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: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1768,6 +1768,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 @@ -80,6 +80,7 @@ bool requiresPIE() const; bool needsUnwindTables() const; + bool needsLTO() const; bool linkCXXRuntimes() const { return LinkCXXRuntimes; } bool hasCrossDsoCfi() const { return CfiCrossDso; } bool hasAnySanitizer() const { return !Sanitizers.empty(); } Index: lib/CodeGen/BackendUtil.cpp =================================================================== --- lib/CodeGen/BackendUtil.cpp +++ lib/CodeGen/BackendUtil.cpp @@ -812,6 +812,8 @@ if (!ThinLinkOS) return; } + TheModule->addModuleFlag(Module::Error, "EnableSplitLTOUnit", + CodeGenOpts.EnableSplitLTOUnit); PerModulePasses.add(createWriteThinLTOBitcodePass( *OS, ThinLinkOS ? &ThinLinkOS->os() : nullptr)); } else { @@ -822,12 +824,15 @@ !CodeGenOpts.DisableLLVMPasses && llvm::Triple(TheModule->getTargetTriple()).getVendor() != llvm::Triple::Apple); - if (EmitLTOSummary && !TheModule->getModuleFlag("ThinLTO")) - TheModule->addModuleFlag(Module::Error, "ThinLTO", uint32_t(0)); + if (EmitLTOSummary) { + if (!TheModule->getModuleFlag("ThinLTO")) + TheModule->addModuleFlag(Module::Error, "ThinLTO", uint32_t(0)); + TheModule->addModuleFlag(Module::Error, "EnableSplitLTOUnit", + CodeGenOpts.EnableSplitLTOUnit); + } - PerModulePasses.add( - createBitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists, - EmitLTOSummary)); + PerModulePasses.add(createBitcodeWriterPass( + *OS, CodeGenOpts.EmitLLVMUseLists, EmitLTOSummary)); } break; @@ -1048,6 +1053,8 @@ if (!ThinLinkOS) return; } + TheModule->addModuleFlag(Module::Error, "EnableSplitLTOUnit", + CodeGenOpts.EnableSplitLTOUnit); MPM.addPass(ThinLTOBitcodeWriterPass(*OS, ThinLinkOS ? &ThinLinkOS->os() : nullptr)); } else { @@ -1058,11 +1065,14 @@ !CodeGenOpts.DisableLLVMPasses && llvm::Triple(TheModule->getTargetTriple()).getVendor() != llvm::Triple::Apple); - if (EmitLTOSummary && !TheModule->getModuleFlag("ThinLTO")) - TheModule->addModuleFlag(Module::Error, "ThinLTO", uint32_t(0)); - - MPM.addPass(BitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists, - EmitLTOSummary)); + if (EmitLTOSummary) { + if (!TheModule->getModuleFlag("ThinLTO")) + TheModule->addModuleFlag(Module::Error, "ThinLTO", uint32_t(0)); + TheModule->addModuleFlag(Module::Error, "EnableSplitLTOUnit", + CodeGenOpts.EnableSplitLTOUnit); + } + MPM.addPass( + BitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists, EmitLTOSummary)); } 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 @@ -5206,6 +5206,21 @@ CmdArgs.push_back("-fwhole-program-vtables"); } + Arg *SplitLTOUnitArg = Args.getLastArg(options::OPT_fsplit_lto_unit, + options::OPT_fno_split_lto_unit); + if (SplitLTOUnitArg && + SplitLTOUnitArg->getOption().matches(options::OPT_fsplit_lto_unit)) + CmdArgs.push_back("-fsplit-lto-unit"); + else if (WholeProgramVTables || Sanitize.needsLTO()) { + if (SplitLTOUnitArg && + SplitLTOUnitArg->getOption().matches(options::OPT_fno_split_lto_unit)) + D.Diag(diag::err_drv_argument_not_allowed_with) + << "-fno-split-lto-unit" + << (WholeProgramVTables ? "-fwhole-program-vtables" + : "-fsanitize=cfi"); + 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 @@ -906,6 +906,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/CodeGen/thinlto-distributed-cfi-devirt.ll =================================================================== --- test/CodeGen/thinlto-distributed-cfi-devirt.ll +++ test/CodeGen/thinlto-distributed-cfi-devirt.ll @@ -4,7 +4,7 @@ ; It additionally enables -fwhole-program-vtables to get more information in ; TYPE_IDs of GLOBALVAL_SUMMARY_BLOCK. -; RUN: opt -thinlto-bc -o %t.o %s +; RUN: opt -thinlto-bc -thinlto-split-lto-unit -o %t.o %s ; FIXME: Fix machine verifier issues and remove -verify-machineinstrs=0. PR39436. ; RUN: llvm-lto2 run -thinlto-distributed-indexes %t.o \ Index: test/CodeGen/thinlto-distributed-cfi.ll =================================================================== --- test/CodeGen/thinlto-distributed-cfi.ll +++ test/CodeGen/thinlto-distributed-cfi.ll @@ -2,7 +2,7 @@ ; Backend test for distribute ThinLTO with CFI. -; RUN: opt -thinlto-bc -o %t.o %s +; RUN: opt -thinlto-bc -thinlto-split-lto-unit -o %t.o %s ; RUN: llvm-lto2 run -thinlto-distributed-indexes %t.o \ ; RUN: -o %t2.index \ 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: %clang_cc1 -flto=thin -flto-unit -fsplit-lto-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,10 @@ +// 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=ERROR1 %s +// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -fno-split-lto-unit -fsanitize=cfi 2>&1 | FileCheck --check-prefix=ERROR2 %s + +// UNIT: "-fsplit-lto-unit" +// NOUNIT-NOT: "-fsplit-lto-unit" +// ERROR1: error: invalid argument '-fno-split-lto-unit' not allowed with '-fwhole-program-vtables' +// ERROR2: error: invalid argument '-fno-split-lto-unit' not allowed with '-fsanitize=cfi'