]> granicus.if.org Git - clang/commitdiff
[ThinLTO] Option to invoke ThinLTO backend passes and importing
authorTeresa Johnson <tejohnson@google.com>
Mon, 7 Dec 2015 19:21:34 +0000 (19:21 +0000)
committerTeresa Johnson <tejohnson@google.com>
Mon, 7 Dec 2015 19:21:34 +0000 (19:21 +0000)
Summary:
Adds new option -fthinlto-index=<file> to invoke the LTO pipeline
along with function importing via clang using the supplied function
summary index file. This supports invoking the parallel ThinLTO
backend processes in a distributed build environment via clang.

Additionally, this causes the module linker to be invoked on the bitcode
file being compiled to perform any necessary promotion and renaming of
locals that are exported via the function summary index file.

Add a couple tests that confirm we get expected errors when we try to
use the new option on a file that isn't bitcode, or specify an invalid
index file. The tests also confirm that we trigger the expected function
import pass.

Depends on D15024

Reviewers: joker.eph, dexonsmith

Subscribers: joker.eph, davidxl, cfe-commits

Differential Revision: http://reviews.llvm.org/D15025

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@254927 91177308-0d34-0410-b5e6-96231b3b80d8

12 files changed:
include/clang/CodeGen/BackendUtil.h
include/clang/Driver/Options.td
include/clang/Driver/Types.h
include/clang/Frontend/CodeGenOptions.h
lib/CodeGen/BackendUtil.cpp
lib/CodeGen/CodeGenAction.cpp
lib/Driver/Tools.cpp
lib/Driver/Types.cpp
lib/Frontend/CompilerInvocation.cpp
test/CMakeLists.txt
test/CodeGen/thinlto_backend.c [new file with mode: 0644]
test/Driver/thinlto_backend.c [new file with mode: 0644]

index 8586e7788942c5639afae1ebc302bfc6e15964bd..ba5dc3939ced211c0cef85c35d6620b7458d544b 100644 (file)
@@ -11,6 +11,8 @@
 #define LLVM_CLANG_CODEGEN_BACKENDUTIL_H
 
 #include "clang/Basic/LLVM.h"
+#include "llvm/IR/FunctionInfo.h"
+#include <memory>
 
 namespace llvm {
   class Module;
@@ -31,10 +33,12 @@ namespace clang {
     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<llvm::FunctionInfoIndex> Index = nullptr);
 }
 
 #endif
index 7141d1a6c14a3f29a15439a514284792a6ecc139..9852ecf2a5231054ffa77e60ef0907e2f55e039d 100644 (file)
@@ -693,6 +693,9 @@ def flto : Flag<["-"], "flto">, Flags<[CC1Option]>, Group<f_Group>,
   HelpText<"Enable LTO in 'full' mode">;
 def fno_lto : Flag<["-"], "fno-lto">, Group<f_Group>,
   HelpText<"Disable LTO mode (default)">;
+def fthinlto_index_EQ : Joined<["-"], "fthinlto-index=">,
+  Flags<[CC1Option]>, Group<f_Group>,
+  HelpText<"Perform ThinLTO importing using provided function summary index">;
 def fmacro_backtrace_limit_EQ : Joined<["-"], "fmacro-backtrace-limit=">,
                                 Group<f_Group>, Flags<[DriverOption, CoreOption]>;
 def fmerge_all_constants : Flag<["-"], "fmerge-all-constants">, Group<f_Group>;
index dd95d659915681c0071edf0e547c7be34e1eed0a..22122c7a8eb993c681216cf04cdd046ba31979dc 100644 (file)
@@ -63,6 +63,9 @@ namespace types {
   /// 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 4923192169c7bf4d23a92c7a4b76b6eebcda7797..5bf8f8b92b480b52f361e7612d5fe5a8bfc9db37 100644 (file)
@@ -167,6 +167,10 @@ public:
   /// 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 37e62f8ddcdbbaf081acb92bb846d7b6fe7e729e..784e9c7ba5f7dc7d2e6513200f75f0d8516bb5f6 100644 (file)
@@ -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 @@ class EmitAssemblyHelper {
   const clang::TargetOptions &TargetOpts;
   const LangOptions &LangOpts;
   Module *TheModule;
+  std::unique_ptr<FunctionInfoIndex> FunctionIndex;
 
   Timer CodeGenerationTime;
 
@@ -113,15 +115,14 @@ private:
   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<FunctionInfoIndex> 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 @@ void EmitAssemblyHelper::CreatePasses() {
   }
 
   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 @@ void EmitAssemblyHelper::CreatePasses() {
   PMBuilder.PrepareForLTO = CodeGenOpts.PrepareForLTO;
   PMBuilder.RerollLoops = CodeGenOpts.RerollLoops;
 
+  legacy::PassManager *MPM = getPerModulePasses();
+
+  // If we are performing a ThinLTO importing 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 = FunctionIndex.get();
+    PMBuilder.populateLTOPassManager(*MPM);
+    return;
+  }
+
   PMBuilder.addExtension(PassManagerBuilder::EP_EarlyAsPossible,
                          addAddDiscriminatorsPass);
 
@@ -361,27 +396,6 @@ void EmitAssemblyHelper::CreatePasses() {
                            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 @@ void EmitAssemblyHelper::CreatePasses() {
   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 @@ void clang::EmitBackendOutput(DiagnosticsEngine &Diags,
                               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<FunctionInfoIndex> Index) {
+  EmitAssemblyHelper AsmHelper(Diags, CGOpts, TOpts, LOpts, M,
+                               std::move(Index));
 
   AsmHelper.EmitAssembly(Action, OS);
 
index da48dbdefbe9710f2fbc1ac3998d295d09ef8241..9c0ae9ed9914fa5ea903a3153eb59ab5a66acf18 100644 (file)
 #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 clang;
 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 @@ namespace clang {
         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 @@ namespace clang {
       ((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 @@ void BackendConsumer::OptimizationFailureHandler(
   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 @@ void CodeGenAction::ExecuteAction() {
       TheModule->setTargetTriple(TargetOpts.Triple);
     }
 
+    auto DiagHandler = [&](const DiagnosticInfo &DI) {
+      linkerDiagnosticHandler(DI, TheModule.get(),
+                              getCompilerInstance().getDiagnostics());
+    };
+
+    // If we are performing ThinLTO importing 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<FunctionInfoIndex> Index;
+    if (!CI.getCodeGenOpts().ThinLTOIndexFile.empty()) {
+      ErrorOr<std::unique_ptr<FunctionInfoIndex>> IndexOrErr =
+          llvm::getFunctionIndexForFile(CI.getCodeGenOpts().ThinLTOIndexFile,
+                                        DiagHandler);
+      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<llvm::Module> 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 2015e3b821d3239be38c2764575ab6f8c5e64ca6..7025314c691488beed6efd56b2c892df1e47e59a 100644 (file)
@@ -3419,6 +3419,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
       Args.AddLastArg(CmdArgs, options::OPT_flto, options::OPT_flto_EQ);
   }
 
+  if (const Arg *A = Args.getLastArg(options::OPT_fthinlto_index_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_index_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 2085b0124298d3a29c7855f3ee9fe85acf87315d..c29ce9462a0792398d192cd6f5bb2465391035a8 100644 (file)
@@ -128,6 +128,19 @@ bool types::isCXX(ID Id) {
   }
 }
 
+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 1bad8d8a1ac1d09e07c6739b493286e7681c25ae..4dd1d20c91fe082ef67b9486b2d27e45231a029e 100644 (file)
@@ -521,6 +521,12 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
   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_index_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_index_EQ);
+  }
 
   Opts.MSVolatile = Args.hasArg(OPT_fms_volatile);
 
index 9aeed455f669fc6ed1545aa96d97671dc8eb458a..8dd64d1614ac261c19e185cb5f9e309b58628dc9 100644 (file)
@@ -61,6 +61,7 @@ if( NOT CLANG_BUILT_STANDALONE )
     FileCheck count not
     llc
     llvm-bcanalyzer
+    llvm-lto
     llvm-objdump
     llvm-profdata
     llvm-readobj
diff --git a/test/CodeGen/thinlto_backend.c b/test/CodeGen/thinlto_backend.c
new file mode 100644 (file)
index 0000000..6a74dea
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: %clang -target x86_64-unknown-linux -O2 %s -flto=thin -c -o %t.o
+// RUN: llvm-lto -thinlto -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-index=%t.thinlto.bc 2>&1 | FileCheck %s -check-prefix=CHECK-WARNING
+// CHECK-WARNING: error: invalid argument '-fthinlto-index={{.*}}' 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-index=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-index=%t.thinlto.bc -mllvm -debug-pass=Structure 2>&1 | FileCheck %s -check-prefix=CHECK-PASS
+// CHECK-PASS: Function Importing
diff --git a/test/Driver/thinlto_backend.c b/test/Driver/thinlto_backend.c
new file mode 100644 (file)
index 0000000..9a28f77
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: %clang -target x86_64-unknown-linux -O2 %s -flto=thin -c -o %t.o
+// RUN: llvm-lto -thinlto -o %t %t.o
+
+// -fthinlto_index should be passed to cc1
+// RUN: %clang -target x86_64-unknown-linux -O2 -o %t1.o -x ir %t.o -c -fthinlto-index=%t.thinlto.bc -### 2>&1 | FileCheck %s -check-prefix=CHECK-THINLTOBE-ACTION
+// CHECK-THINLTOBE-ACTION: -fthinlto-index=
+
+// 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-index=%t.thinlto.bc 2>&1 | FileCheck %s -check-prefix=CHECK-WARNING
+// CHECK-WARNING: error: invalid argument '-fthinlto-index={{.*}}' only allowed with '-x ir'