Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -693,6 +693,9 @@ HelpText<"Enable LTO in 'full' mode">; def fno_lto : Flag<["-"], "fno-lto">, Group, HelpText<"Disable LTO mode (default)">; +def fthinlto_backend_EQ : Joined<["-"], "fthinlto-backend=">, + Flags<[CC1Option]>, Group, + HelpText<"Perform ThinLTO backend using provided function summary index">; def fmacro_backtrace_limit_EQ : Joined<["-"], "fmacro-backtrace-limit=">, Group, Flags<[DriverOption, CoreOption]>; def fmerge_all_constants : Flag<["-"], "fmerge-all-constants">, Group; Index: include/clang/Driver/Types.h =================================================================== --- include/clang/Driver/Types.h +++ include/clang/Driver/Types.h @@ -63,6 +63,9 @@ /// isCXX - Is this a "C++" input (C++ and Obj-C++ sources and headers). bool isCXX(ID Id); + /// Is this LLVM IR. + bool isLLVMIR(ID Id); + /// isCuda - Is this a CUDA input. bool isCuda(ID Id); Index: include/clang/Frontend/CodeGenOptions.h =================================================================== --- include/clang/Frontend/CodeGenOptions.h +++ include/clang/Frontend/CodeGenOptions.h @@ -167,6 +167,10 @@ /// Name of the profile file to use as input for -fprofile-instr-use std::string InstrProfileInput; + /// Name of the function summary index file to use for ThinLTO function + /// importing. + std::string ThinLTOIndexFile; + /// The EABI version to use std::string EABIVersion; Index: lib/CodeGen/BackendUtil.cpp =================================================================== --- lib/CodeGen/BackendUtil.cpp +++ lib/CodeGen/BackendUtil.cpp @@ -284,6 +284,29 @@ Inlining = CodeGenOpts.NoInlining; } + // If we are performing a ThinLTO backend compile, invoke the LTO + // pipeline and initialize the FunctionImportIndexFile field. + if (!CodeGenOpts.ThinLTOIndexFile.empty()) { + legacy::PassManager *MPM = getPerModulePasses(); + PassManagerBuilder PMB; + Triple TargetTriple(TheModule->getTargetTriple()); + PMB.LibraryInfo = createTLII(TargetTriple, CodeGenOpts); + PMB.Inliner = createFunctionInliningPass(); + PMB.VerifyInput = true; + // Disable the verification pass in -asserts builds. +#ifdef NDEBUG + PMB.VerifyOutput = false; +#else + PMB.VerifyOutput = true; +#endif + PMB.LoopVectorize = true; + PMB.SLPVectorize = true; + PMB.FunctionImportIndexFile = CodeGenOpts.ThinLTOIndexFile; + PMB.OptLevel = OptLevel; + PMB.populateLTOPassManager(*MPM); + return; + } + PassManagerBuilderWrapper PMBuilder(CodeGenOpts, LangOpts); PMBuilder.OptLevel = OptLevel; PMBuilder.SizeLevel = CodeGenOpts.OptimizeSize; Index: lib/CodeGen/CodeGenAction.cpp =================================================================== --- lib/CodeGen/CodeGenAction.cpp +++ lib/CodeGen/CodeGenAction.cpp @@ -27,9 +27,11 @@ #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/FunctionInfo.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Linker/Linker.h" +#include "llvm/Object/FunctionIndexObjectFile.h" #include "llvm/Pass.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" @@ -39,6 +41,24 @@ using namespace llvm; namespace clang { +/// Diagnostic handler used by invocations of Linker::LinkModules +static void linkerDiagnosticHandler(const DiagnosticInfo &DI, + const llvm::Module *LinkModule, + DiagnosticsEngine &Diags) { + if (DI.getSeverity() != DS_Error) + return; + + std::string MsgStorage; + { + raw_string_ostream Stream(MsgStorage); + DiagnosticPrinterRawOStream DP(Stream); + DI.print(DP); + } + + Diags.Report(diag::err_fe_cannot_link_module) + << LinkModule->getModuleIdentifier() << MsgStorage; +} + class BackendConsumer : public ASTConsumer { virtual void anchor(); DiagnosticsEngine &Diags; @@ -167,7 +187,7 @@ llvm::Module *LinkModule = I.second.get(); if (Linker::LinkModules(M, LinkModule, [=](const DiagnosticInfo &DI) { - linkerDiagnosticHandler(DI, LinkModule); + linkerDiagnosticHandler(DI, LinkModule, Diags); }, LinkFlags)) return; @@ -233,9 +253,6 @@ ((BackendConsumer*)Context)->InlineAsmDiagHandler2(SM, Loc); } - void linkerDiagnosticHandler(const llvm::DiagnosticInfo &DI, - const llvm::Module *LinkModule); - static void DiagnosticHandler(const llvm::DiagnosticInfo &DI, void *Context) { ((BackendConsumer *)Context)->DiagnosticHandlerImpl(DI); @@ -545,22 +562,6 @@ EmitOptimizationMessage(D, diag::warn_fe_backend_optimization_failure); } -void BackendConsumer::linkerDiagnosticHandler(const DiagnosticInfo &DI, - const llvm::Module *LinkModule) { - if (DI.getSeverity() != DS_Error) - return; - - std::string MsgStorage; - { - raw_string_ostream Stream(MsgStorage); - DiagnosticPrinterRawOStream DP(Stream); - DI.print(DP); - } - - Diags.Report(diag::err_fe_cannot_link_module) - << LinkModule->getModuleIdentifier() << MsgStorage; -} - /// \brief This function is invoked when the backend needs /// to report something to the user. void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) { @@ -785,6 +786,45 @@ TheModule->setTargetTriple(TargetOpts.Triple); } + // If we are performing ThinLTO backend compilation (indicated by + // a non-empty index file option), then we need to invoke the module + // linker for the current module that we are importing into. This + // is because the module linker will need to promote to global scope + // and rename any local values that are potentially exported to other + // modules. Do this early so that the rest of the compilation sees the + // promoted symbols. + if (!CI.getCodeGenOpts().ThinLTOIndexFile.empty()) { + // TODO: Stash this on the module object so we can reuse in + // the FunctionImport pass? + ErrorOr> IndexOrErr = + llvm::getFunctionIndexForFile(CI.getCodeGenOpts().ThinLTOIndexFile, + [=, &CI](const DiagnosticInfo &DI) { + linkerDiagnosticHandler( + DI, TheModule.get(), + CI.getDiagnostics()); + }, + TheModule.get()); + if (std::error_code EC = IndexOrErr.getError()) { + std::string Error = EC.message(); + errs() << "Error loading index file '" + << CI.getCodeGenOpts().ThinLTOIndexFile << "': " << Error + << "\n"; + return; + } + std::unique_ptr Index = std::move(IndexOrErr.get()); + assert(Index); + std::unique_ptr Combined(new llvm::Module( + TheModule->getModuleIdentifier(), TheModule->getContext())); + if (Linker::LinkModules(Combined.get(), TheModule.get(), + [=, &CI](const DiagnosticInfo &DI) { + linkerDiagnosticHandler(DI, TheModule.get(), + CI.getDiagnostics()); + }, llvm::Linker::Flags::None, Index.get())) + return; + + TheModule = std::move(Combined); + } + LLVMContext &Ctx = TheModule->getContext(); Ctx.setInlineAsmDiagnosticHandler(BitcodeInlineAsmDiagHandler); EmitBackendOutput(CI.getDiagnostics(), CI.getCodeGenOpts(), TargetOpts, Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -3413,6 +3413,13 @@ Args.AddLastArg(CmdArgs, options::OPT_flto, options::OPT_flto_EQ); } + if (const Arg *A = Args.getLastArg(options::OPT_fthinlto_backend_EQ)) { + if (!types::isLLVMIR(Input.getType())) + D.Diag(diag::err_drv_argument_only_allowed_with) << A->getAsString(Args) + << "-x ir"; + Args.AddLastArg(CmdArgs, options::OPT_fthinlto_backend_EQ); + } + // We normally speed up the clang process a bit by skipping destructors at // exit, but when we're generating diagnostics we can rely on some of the // cleanup. Index: lib/Driver/Types.cpp =================================================================== --- lib/Driver/Types.cpp +++ lib/Driver/Types.cpp @@ -128,6 +128,19 @@ } } +bool types::isLLVMIR(ID Id) { + switch (Id) { + default: + return false; + + case TY_LLVM_IR: + case TY_LLVM_BC: + case TY_LTO_IR: + case TY_LTO_BC: + return true; + } +} + bool types::isCuda(ID Id) { switch (Id) { default: Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -521,6 +521,12 @@ Opts.PrepareForLTO = Args.hasArg(OPT_flto, OPT_flto_EQ); const Arg *A = Args.getLastArg(OPT_flto, OPT_flto_EQ); Opts.EmitFunctionSummary = A && A->containsValue("thin"); + if (Arg *A = Args.getLastArg(OPT_fthinlto_backend_EQ)) { + if (IK != IK_LLVM_IR) + Diags.Report(diag::err_drv_argument_only_allowed_with) + << A->getAsString(Args) << "-x ir"; + Opts.ThinLTOIndexFile = Args.getLastArgValue(OPT_fthinlto_backend_EQ); + } Opts.MSVolatile = Args.hasArg(OPT_fms_volatile); Index: test/CodeGen/thinlto_backend.c =================================================================== --- /dev/null +++ test/CodeGen/thinlto_backend.c @@ -0,0 +1,17 @@ +// RUN: %clang -target x86_64-unknown-linux -O2 %s -flto=thin -c -o %t.o +// RUN: %clang -target x86_64-unknown-linux -O2 -flto=thin -fuse-ld=gold -o %t %t.o + +// Ensure clang -cc1 give expected error for incorrect input type +// RUN: not %clang_cc1 -target x86_64-unknown-linux -O2 -o %t1.o %s -c -fthinlto-backend=%t.thinlto.bc 2> %t +// RUN: FileCheck -check-prefix=CHECK-WARNING < %t %s +// CHECK-WARNING: error: invalid argument '-fthinlto-backend={{.*}}' only allowed with '-x ir' + +// Ensure we get expected error for missing index file +// RUN: %clang -target x86_64-unknown-linux -O2 -o %t1.o -x ir %t.o -c -fthinlto-backend=bad.thinlto.bc 2> %t +// RUN: FileCheck -check-prefix=CHECK-ERROR < %t %s +// CHECK-ERROR: Error loading index file 'bad.thinlto.bc': No such file or directory + +// Ensure Function Importing pass added +// RUN: %clang -target x86_64-unknown-linux -O2 -o %t1.o -x ir %t.o -c -fthinlto-backend=%t.thinlto.bc -mllvm -debug-pass=Structure 2> %t +// RUN: FileCheck -check-prefix=CHECK-PASS < %t %s +// CHECK-PASS: Function Importing Index: test/Driver/thinlto_backend.c =================================================================== --- /dev/null +++ test/Driver/thinlto_backend.c @@ -0,0 +1,12 @@ +// RUN: %clang -target x86_64-unknown-linux -O2 %s -flto=thin -c -o %t.o +// RUN: %clang -target x86_64-unknown-linux -O2 -flto=thin -fuse-ld=gold -o %t %t.o + +// -fthinlto_backend should be passed to cc1 +// RUN: %clang -target x86_64-unknown-linux -O2 -o %t1.o -x ir %t.o -c -fthinlto-backend=%t.thinlto.bc -### 2> %t +// RUN: FileCheck -check-prefix=CHECK-THINLTOBE-ACTION < %t %s +// CHECK-THINLTOBE-ACTION: -fthinlto-backend= + +// Ensure clang driver gives the expected error for incorrect input type +// RUN: not %clang -target x86_64-unknown-linux -O2 -o %t1.o %s -c -fthinlto-backend=%t.thinlto.bc 2> %t +// RUN: FileCheck -check-prefix=CHECK-WARNING < %t %s +// CHECK-WARNING: error: invalid argument '-fthinlto-backend={{.*}}' only allowed with '-x ir'