Index: include/clang/CodeGen/BackendUtil.h =================================================================== --- include/clang/CodeGen/BackendUtil.h +++ include/clang/CodeGen/BackendUtil.h @@ -11,6 +11,8 @@ #define LLVM_CLANG_CODEGEN_BACKENDUTIL_H #include "clang/Basic/LLVM.h" +#include "llvm/IR/FunctionInfo.h" +#include namespace llvm { class Module; @@ -31,10 +33,12 @@ Backend_EmitObj ///< Emit native object files }; - void EmitBackendOutput(DiagnosticsEngine &Diags, const CodeGenOptions &CGOpts, - const TargetOptions &TOpts, const LangOptions &LOpts, - StringRef TDesc, llvm::Module *M, BackendAction Action, - raw_pwrite_stream *OS); + void + EmitBackendOutput(DiagnosticsEngine &Diags, const CodeGenOptions &CGOpts, + const TargetOptions &TOpts, const LangOptions &LOpts, + StringRef TDesc, llvm::Module *M, BackendAction Action, + raw_pwrite_stream *OS, + std::unique_ptr Index = nullptr); } #endif 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 @@ -22,6 +22,7 @@ #include "llvm/CodeGen/RegAllocRegistry.h" #include "llvm/CodeGen/SchedulerRegistry.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/FunctionInfo.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" @@ -53,6 +54,7 @@ const clang::TargetOptions &TargetOpts; const LangOptions &LangOpts; Module *TheModule; + std::unique_ptr FunctionIndex; Timer CodeGenerationTime; @@ -113,15 +115,14 @@ bool AddEmitPasses(BackendAction Action, raw_pwrite_stream &OS); public: - EmitAssemblyHelper(DiagnosticsEngine &_Diags, - const CodeGenOptions &CGOpts, + EmitAssemblyHelper(DiagnosticsEngine &_Diags, const CodeGenOptions &CGOpts, const clang::TargetOptions &TOpts, - const LangOptions &LOpts, - Module *M) - : Diags(_Diags), CodeGenOpts(CGOpts), TargetOpts(TOpts), LangOpts(LOpts), - TheModule(M), CodeGenerationTime("Code Generation Time"), - CodeGenPasses(nullptr), PerModulePasses(nullptr), - PerFunctionPasses(nullptr) {} + const LangOptions &LOpts, Module *M, + std::unique_ptr Index) + : Diags(_Diags), CodeGenOpts(CGOpts), TargetOpts(TOpts), LangOpts(LOpts), + TheModule(M), FunctionIndex(std::move(Index)), + CodeGenerationTime("Code Generation Time"), CodeGenPasses(nullptr), + PerModulePasses(nullptr), PerFunctionPasses(nullptr) {} ~EmitAssemblyHelper() { delete CodeGenPasses; @@ -285,6 +286,29 @@ } PassManagerBuilderWrapper PMBuilder(CodeGenOpts, LangOpts); + + // Figure out TargetLibraryInfo. + Triple TargetTriple(TheModule->getTargetTriple()); + PMBuilder.LibraryInfo = createTLII(TargetTriple, CodeGenOpts); + + switch (Inlining) { + case CodeGenOptions::NoInlining: + break; + case CodeGenOptions::NormalInlining: { + PMBuilder.Inliner = + createFunctionInliningPass(OptLevel, CodeGenOpts.OptimizeSize); + break; + } + case CodeGenOptions::OnlyAlwaysInlining: + // Respect always_inline. + if (OptLevel == 0) + // Do not insert lifetime intrinsics at -O0. + PMBuilder.Inliner = createAlwaysInlinerPass(false); + else + PMBuilder.Inliner = createAlwaysInlinerPass(); + break; + } + PMBuilder.OptLevel = OptLevel; PMBuilder.SizeLevel = CodeGenOpts.OptimizeSize; PMBuilder.BBVectorize = CodeGenOpts.VectorizeBB; @@ -297,6 +321,17 @@ PMBuilder.PrepareForLTO = CodeGenOpts.PrepareForLTO; PMBuilder.RerollLoops = CodeGenOpts.RerollLoops; + legacy::PassManager *MPM = getPerModulePasses(); + + // If we are performing a ThinLTO backend compile, invoke the LTO + // pipeline and pass down the in-memory function index. + if (!CodeGenOpts.ThinLTOIndexFile.empty()) { + assert(FunctionIndex && "Expected non-empty function index"); + PMBuilder.FunctionIndex = std::move(FunctionIndex); + PMBuilder.populateLTOPassManager(*MPM); + return; + } + PMBuilder.addExtension(PassManagerBuilder::EP_EarlyAsPossible, addAddDiscriminatorsPass); @@ -361,27 +396,6 @@ addDataFlowSanitizerPass); } - // Figure out TargetLibraryInfo. - Triple TargetTriple(TheModule->getTargetTriple()); - PMBuilder.LibraryInfo = createTLII(TargetTriple, CodeGenOpts); - - switch (Inlining) { - case CodeGenOptions::NoInlining: break; - case CodeGenOptions::NormalInlining: { - PMBuilder.Inliner = - createFunctionInliningPass(OptLevel, CodeGenOpts.OptimizeSize); - break; - } - case CodeGenOptions::OnlyAlwaysInlining: - // Respect always_inline. - if (OptLevel == 0) - // Do not insert lifetime intrinsics at -O0. - PMBuilder.Inliner = createAlwaysInlinerPass(false); - else - PMBuilder.Inliner = createAlwaysInlinerPass(); - break; - } - // Set up the per-function pass manager. legacy::FunctionPassManager *FPM = getPerFunctionPasses(); if (CodeGenOpts.VerifyModule) @@ -389,7 +403,6 @@ PMBuilder.populateFunctionPassManager(*FPM); // Set up the per-module pass manager. - legacy::PassManager *MPM = getPerModulePasses(); if (!CodeGenOpts.RewriteMapFiles.empty()) addSymbolRewriterPass(CodeGenOpts, MPM); @@ -660,8 +673,10 @@ const clang::TargetOptions &TOpts, const LangOptions &LOpts, StringRef TDesc, Module *M, BackendAction Action, - raw_pwrite_stream *OS) { - EmitAssemblyHelper AsmHelper(Diags, CGOpts, TOpts, LOpts, M); + raw_pwrite_stream *OS, + std::unique_ptr Index) { + EmitAssemblyHelper AsmHelper(Diags, CGOpts, TOpts, LOpts, M, + std::move(Index)); AsmHelper.EmitAssembly(Action, OS); Index: lib/CodeGen/CodeGenAction.cpp =================================================================== --- lib/CodeGen/CodeGenAction.cpp +++ lib/CodeGen/CodeGenAction.cpp @@ -26,10 +26,12 @@ #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/FunctionInfo.h" #include "llvm/IR/LLVMContext.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,11 +786,44 @@ TheModule->setTargetTriple(TargetOpts.Triple); } + auto DiagHandler = [&](const DiagnosticInfo &DI) { + linkerDiagnosticHandler(DI, TheModule.get(), + getCompilerInstance().getDiagnostics()); + }; + + // If we are performing ThinLTO backend compilation (indicated by + // a non-empty index file option), then we need 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. + std::unique_ptr Index; + if (!CI.getCodeGenOpts().ThinLTOIndexFile.empty()) { + ErrorOr> IndexOrErr = + llvm::getFunctionIndexForFile(CI.getCodeGenOpts().ThinLTOIndexFile, + DiagHandler, 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; + } + Index = std::move(IndexOrErr.get()); + assert(Index); + // Currently this requires creating a new Module object. + std::unique_ptr RenamedModule = + renameModuleForThinLTO(TheModule, Index.get(), DiagHandler); + if (!RenamedModule) + return; + + TheModule = std::move(RenamedModule); + } + LLVMContext &Ctx = TheModule->getContext(); Ctx.setInlineAsmDiagnosticHandler(BitcodeInlineAsmDiagHandler); EmitBackendOutput(CI.getDiagnostics(), CI.getCodeGenOpts(), TargetOpts, CI.getLangOpts(), CI.getTarget().getDataLayoutString(), - TheModule.get(), BA, OS); + TheModule.get(), BA, OS, std::move(Index)); return; } 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,14 @@ +// 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>&1 | FileCheck %s -check-prefix=CHECK-WARNING +// 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>&1 | FileCheck %s -check-prefix=CHECK-ERROR +// 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>&1 | FileCheck %s -check-prefix=CHECK-PASS +// CHECK-PASS: Function Importing Index: test/Driver/thinlto_backend.c =================================================================== --- /dev/null +++ test/Driver/thinlto_backend.c @@ -0,0 +1,10 @@ +// 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>&1 | FileCheck %s -check-prefix=CHECK-THINLTOBE-ACTION +// 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>&1 | FileCheck %s -check-prefix=CHECK-WARNING +// CHECK-WARNING: error: invalid argument '-fthinlto-backend={{.*}}' only allowed with '-x ir'