Index: clang/include/clang/Basic/CodeGenOptions.def =================================================================== --- clang/include/clang/Basic/CodeGenOptions.def +++ clang/include/clang/Basic/CodeGenOptions.def @@ -161,6 +161,7 @@ /// CFI and traditional whole program /// devirtualization that require whole /// program IR support. +CODEGENOPT(UnifiedLTO, 1, 0) ///< Use the unified LTO pipeline. CODEGENOPT(IncrementalLinkerCompatible, 1, 0) ///< Emit an object file which can ///< be used with an incremental ///< linker. Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -2086,6 +2086,11 @@ Alias, AliasArgs<["full"]>, HelpText<"Enable LTO in 'full' mode">; def flto_EQ_auto : Flag<["-"], "flto=auto">, Group, Alias, AliasArgs<["full"]>, HelpText<"Enable LTO in 'full' mode">; +defm unified_lto : BoolFOption<"unified-lto", + CodeGenOpts<"UnifiedLTO">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[CC1Option], "">>; def flto : Flag<["-"], "flto">, Flags<[CoreOption, CC1Option]>, Group, Alias, AliasArgs<["full"]>, HelpText<"Enable LTO in 'full' mode">; def fno_lto : Flag<["-"], "fno-lto">, Flags<[CoreOption, CC1Option]>, Group, Index: clang/lib/CodeGen/BackendUtil.cpp =================================================================== --- clang/lib/CodeGen/BackendUtil.cpp +++ clang/lib/CodeGen/BackendUtil.cpp @@ -766,6 +766,7 @@ // Only enable CGProfilePass when using integrated assembler, since // non-integrated assemblers don't recognize .cgprofile section. PTO.CallGraphProfile = !CodeGenOpts.DisableIntegratedAS; + PTO.UnifiedLTO = CodeGenOpts.UnifiedLTO; LoopAnalysisManager LAM; FunctionAnalysisManager FAM; @@ -897,7 +898,10 @@ if (CodeGenOpts.OptimizationLevel == 0) { MPM = PB.buildO0DefaultPipeline(Level, IsLTO || IsThinLTO); } else if (IsThinLTO) { - MPM = PB.buildThinLTOPreLinkDefaultPipeline(Level); + if (CodeGenOpts.UnifiedLTO) + MPM = PB.buildLTOPreLinkDefaultPipeline(Level); + else + MPM = PB.buildThinLTOPreLinkDefaultPipeline(Level); } else if (IsLTO) { MPM = PB.buildLTOPreLinkDefaultPipeline(Level); } else { @@ -927,18 +931,24 @@ if (!TheModule->getModuleFlag("EnableSplitLTOUnit")) TheModule->addModuleFlag(Module::Error, "EnableSplitLTOUnit", CodeGenOpts.EnableSplitLTOUnit); + if (CodeGenOpts.UnifiedLTO) + TheModule->addModuleFlag(Module::Error, "UnifiedLTO", uint32_t(1)); MPM.addPass(ThinLTOBitcodeWriterPass(*OS, ThinLinkOS ? &ThinLinkOS->os() - : nullptr)); + : nullptr, + CodeGenOpts.UnifiedLTO)); } else { // Emit a module summary by default for Regular LTO except for ld64 // targets bool EmitLTOSummary = shouldEmitRegularLTOSummary(); if (EmitLTOSummary) { - if (!TheModule->getModuleFlag("ThinLTO")) + if (!TheModule->getModuleFlag("ThinLTO") && !CodeGenOpts.UnifiedLTO) TheModule->addModuleFlag(Module::Error, "ThinLTO", uint32_t(0)); + if (CodeGenOpts.UnifiedLTO) + TheModule->addModuleFlag(Module::Error, "UnifiedLTO", uint32_t(1)); if (!TheModule->getModuleFlag("EnableSplitLTOUnit")) TheModule->addModuleFlag(Module::Error, "EnableSplitLTOUnit", - uint32_t(1)); + !CodeGenOpts.UnifiedLTO ? + uint32_t(1) : !CodeGenOpts.UnifiedLTO); } MPM.addPass( BitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists, EmitLTOSummary)); Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -4551,6 +4551,14 @@ // Select the appropriate action. RewriteKind rewriteKind = RK_None; + bool DefaultUnified = Triple.isPS4(); + bool UnifiedLTO = + Args.hasFlag(options::OPT_funified_lto, + options::OPT_fno_unified_lto, + DefaultUnified); + if (UnifiedLTO) + CmdArgs.push_back("-funified-lto"); + // If CollectArgsForIntegratedAssembler() isn't called below, claim the args // it claims when not running an assembler. Otherwise, clang would emit // "argument unused" warnings for assembler flags when e.g. adding "-E" to @@ -4669,18 +4677,34 @@ CmdArgs.push_back("-emit-llvm-uselists"); if (IsUsingLTO && !Args.hasArg(options::OPT_fopenmp_new_driver)) { - // Only AMDGPU supports device-side LTO. - if (IsDeviceOffloadAction && !Triple.isAMDGPU()) { + if (!IsDeviceOffloadAction) { + if (Args.hasArg(options::OPT_flto)) + CmdArgs.push_back("-flto"); + else if (UnifiedLTO) { + // Ensure that the ThinLTO bitcode writer is used for UnifiedLTO. + if (D.getLTOMode() != LTOK_None) + CmdArgs.push_back("-flto=thin"); + } else { + if (D.getLTOMode() == LTOK_Thin) + CmdArgs.push_back("-flto=thin"); + else + CmdArgs.push_back("-flto=full"); + } + if ((RawTriple.getOS() != llvm::Triple::PS4) || + (D.getLTOMode() == LTOK_Full) || !UnifiedLTO) + CmdArgs.push_back("-flto-unit"); + } else if (Triple.isAMDGPU()) { + // Only AMDGPU supports device-side LTO + assert(LTOMode == LTOK_Full || LTOMode == LTOK_Thin); + CmdArgs.push_back(Args.MakeArgString( + Twine("-flto=") + (LTOMode == LTOK_Thin ? "thin" : "full"))); + CmdArgs.push_back("-flto-unit"); + } else { D.Diag(diag::err_drv_unsupported_opt_for_target) << Args.getLastArg(options::OPT_foffload_lto, options::OPT_foffload_lto_EQ) ->getAsString(Args) << Triple.getTriple(); - } else { - assert(LTOMode == LTOK_Full || LTOMode == LTOK_Thin); - CmdArgs.push_back(Args.MakeArgString( - Twine("-flto=") + (LTOMode == LTOK_Thin ? "thin" : "full"))); - CmdArgs.push_back("-flto-unit"); } } } @@ -7001,22 +7025,27 @@ } if (WholeProgramVTables) { - // Propagate -fwhole-program-vtables if this is an LTO compile. - if (IsUsingLTO) - CmdArgs.push_back("-fwhole-program-vtables"); + bool IsPS4 = getToolChain().getTriple().isPS4(); + // Check if we passed LTO options but they were suppressed because this is a // device offloading action, or we passed device offload LTO options which // were suppressed because this is not the device offload action. // Otherwise, issue an error. - else if (!D.isUsingLTO(!IsDeviceOffloadAction)) + if ((!IsUsingLTO && !D.isUsingLTO(!IsDeviceOffloadAction)) || + (IsPS4 && !UnifiedLTO && (D.getLTOMode() != LTOK_Full))) D.Diag(diag::err_drv_argument_only_allowed_with) << "-fwhole-program-vtables" - << "-flto"; + << ((IsPS4 && !UnifiedLTO) ? "-flto=full" : "-flto"); + + // Propagate -fwhole-program-vtables if this is an LTO compile. + if (IsUsingLTO) + CmdArgs.push_back("-fwhole-program-vtables"); } bool DefaultsSplitLTOUnit = (WholeProgramVTables || SanitizeArgs.needsLTO()) && - (LTOMode == LTOK_Full || TC.canSplitThinLTOUnit()); + (LTOMode == LTOK_Full || TC.canSplitThinLTOUnit()) || + UnifiedLTO; bool SplitLTOUnit = Args.hasFlag(options::OPT_fsplit_lto_unit, options::OPT_fno_split_lto_unit, DefaultsSplitLTOUnit); Index: clang/lib/Driver/ToolChains/PS4CPU.cpp =================================================================== --- clang/lib/Driver/ToolChains/PS4CPU.cpp +++ clang/lib/Driver/ToolChains/PS4CPU.cpp @@ -139,6 +139,13 @@ if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) TC.addSanitizerArgs(Args, CmdArgs, "-l", ""); + if (D.isUsingLTO() && Args.hasArg(options::OPT_funified_lto)) { + if (D.getLTOMode() == LTOK_Thin) + CmdArgs.push_back("--lto=thin"); + else if (D.getLTOMode() == LTOK_Full) + CmdArgs.push_back("--lto=full"); + } + Args.AddAllArgs(CmdArgs, options::OPT_L); Args.AddAllArgs(CmdArgs, options::OPT_T_Group); Args.AddAllArgs(CmdArgs, options::OPT_e); Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -1739,6 +1739,12 @@ Opts.PrepareForLTO = false; Opts.PrepareForThinLTO = false; + + if (Opts.UnifiedLTO && Opts.PrepareForLTO) { + Opts.PrepareForThinLTO = true; + Opts.PrepareForLTO = false; + } + if (Arg *A = Args.getLastArg(OPT_flto_EQ)) { Opts.PrepareForLTO = true; StringRef S = A->getValue(); @@ -1747,6 +1753,14 @@ else if (S != "full") Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << S; } + + if (Opts.UnifiedLTO) { + if (T.getOS() == llvm::Triple::PS4) + Opts.EnableSplitLTOUnit = false; + else + Opts.EnableSplitLTOUnit = true; + } + if (Arg *A = Args.getLastArg(OPT_fthinlto_index_EQ)) { if (IK.getLanguage() != Language::LLVM_IR) Diags.Report(diag::err_drv_argument_only_allowed_with) Index: clang/test/CodeGen/asan-unified-lto.ll =================================================================== --- /dev/null +++ clang/test/CodeGen/asan-unified-lto.ll @@ -0,0 +1,18 @@ +; Verify that in the cases of explict distinct LTO piplines, +; explicit unified LTO pipelines, and the default LTO pipeline, +; there is no crash and the anonoymous global is named +; as expected. + +; RUN: %clang_cc1 -emit-llvm-bc -O1 -flto -fsanitize=address -o - -x ir < %s | llvm-dis -o - | FileCheck %s +; RUN: %clang_cc1 -emit-llvm-bc -O1 -flto -funified-lto -fsanitize=address -o - -x ir < %s | llvm-dis -o - | FileCheck %s +; CHECK: @anon.3ee0898e5200a57350fed5485ae5d237 + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@.str = private unnamed_addr constant [5 x i8] c"none\00", align 1 + +define i8* @f() { + %ptr = getelementptr inbounds [5 x i8], [5 x i8]* @.str, i32 0, i32 0 + ret i8* %ptr +} Index: clang/test/CodeGen/emit-summary-index.c =================================================================== --- clang/test/CodeGen/emit-summary-index.c +++ clang/test/CodeGen/emit-summary-index.c @@ -14,4 +14,9 @@ // RUN: %clang_cc1 -flto -triple x86_64-pc-linux-gnu -emit-llvm-bc -disable-llvm-passes < %s -o %t.bc // RUN: %clang_cc1 -flto -triple x86_64-pc-linux-gnu -emit-llvm-bc -x ir < %t.bc | llvm-bcanalyzer -dump | FileCheck --check-prefix=LTOINDEX %s -int main(void) {} +// RUN: %clang_cc1 -flto=thin -funified-lto -emit-llvm-bc < %s | llvm-bcanalyzer -dump | FileCheck --check-prefix=UNITHIN %s +// RUN: %clang_cc1 -flto -funified-lto -emit-llvm-bc < %s | llvm-bcanalyzer -dump | FileCheck --check-prefix=UNITHIN %s + +// UNITHIN: &1 | FileCheck --check-prefix=UNIT %s // RUN: %clang -target x86_64-apple-darwin13.3.0 -### %s -flto=full 2>&1 | FileCheck --check-prefix=UNIT %s // RUN: %clang -target x86_64-apple-darwin13.3.0 -### %s -flto=thin 2>&1 | FileCheck --check-prefix=UNIT %s -// RUN: %clang -target x86_64-scei-ps4 -### %s -flto=full 2>&1 | FileCheck --check-prefix=UNIT %s -// RUN: %clang -target x86_64-scei-ps4 -### %s -flto=thin 2>&1 | FileCheck --check-prefix=UNIT %s +// RUN: %clang -target x86_64-scei-ps4 -### %s -flto=full -funified-lto 2>&1 | FileCheck --check-prefix=UNIT %s +// RUN: %clang -target x86_64-scei-ps4 -### %s -flto=thin -funified-lto 2>&1 | FileCheck --check-prefix=NOUNIT %s +// RUN: %clang -target x86_64-unknown-linux -### %s -flto=full -funified-lto 2>&1 | FileCheck --check-prefix=UNIT %s +// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -funified-lto 2>&1 | FileCheck --check-prefix=UNIT %s // UNIT: "-flto-unit" +// NOUNIT-NOT: "-flto-unit" Index: clang/test/Driver/split-lto-unit.c =================================================================== --- clang/test/Driver/split-lto-unit.c +++ clang/test/Driver/split-lto-unit.c @@ -5,8 +5,10 @@ // RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -fno-split-lto-unit -fsanitize=cfi 2>&1 | FileCheck --check-prefix=ERROR2 %s // RUN: %clang -target x86_64-apple-darwin13.3.0 -### %s -fwhole-program-vtables -flto=full 2>&1 | FileCheck --check-prefix=UNIT %s // RUN: %clang -target x86_64-apple-darwin13.3.0 -### %s -fwhole-program-vtables -flto=thin 2>&1 | FileCheck --check-prefix=NOUNIT %s -// RUN: %clang -target x86_64-scei-ps4 -### %s -fwhole-program-vtables -flto=full 2>&1 | FileCheck --check-prefix=UNIT %s -// RUN: %clang -target x86_64-scei-ps4 -### %s -fwhole-program-vtables -flto=thin 2>&1 | FileCheck --check-prefix=NOUNIT %s +// RUN: %clang -target x86_64-scei-ps4 -### %s -fwhole-program-vtables -flto=full -funified-lto 2>&1 | FileCheck --check-prefix=UNIT %s +// RUN: %clang -target x86_64-scei-ps4 -### %s -fwhole-program-vtables -flto=thin -funified-lto 2>&1 | FileCheck --check-prefix=UNIT %s +// RUN: %clang -target x86_64-unknown-linux -### %s -fwhole-program-vtables -flto=full -funified-lto 2>&1 | FileCheck --check-prefix=UNIT %s +// RUN: %clang -target x86_64-unknown-linux -### %s -fwhole-program-vtables -flto=thin -funified-lto 2>&1 | FileCheck --check-prefix=UNIT %s // UNIT: "-fsplit-lto-unit" // NOUNIT-NOT: "-fsplit-lto-unit" Index: clang/test/Driver/unified-whole-program-vtables.c =================================================================== --- /dev/null +++ clang/test/Driver/unified-whole-program-vtables.c @@ -0,0 +1,3 @@ +// RUN: %clang -target x86_64-pc-linux-gnu -fwhole-program-vtables -funified-lto -### %s 2>&1 | FileCheck %s +// RUN: %clang -target x86_64-pc-linux-gnu -fwhole-program-vtables -fno-unified-lto -### %s 2>&1 | FileCheck %s +// CHECK: invalid argument '-fwhole-program-vtables' only allowed with '-flto'