hoy updated this revision to Diff 313221.
hoy added a comment.
Herald added subscribers: cfe-commits, dexonsmith, dang, steven_wu, MaskRay, 
arichardson, emaste.
Herald added a reviewer: espindola.
Herald added a reviewer: MaskRay.
Herald added a project: clang.

Adding LTO, linker and clang supports.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D92806/new/

https://reviews.llvm.org/D92806

Files:
  clang/include/clang/Basic/CodeGenOptions.h
  clang/include/clang/Driver/Options.td
  clang/lib/CodeGen/BackendUtil.cpp
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/test/CodeGen/single-func.c
  lld/ELF/Config.h
  lld/ELF/Driver.cpp
  lld/ELF/LTO.cpp
  lld/ELF/Options.td
  lld/test/ELF/lto/lto-single-func.ll
  llvm/include/llvm/LTO/Config.h
  llvm/include/llvm/Passes/PassBuilder.h
  llvm/include/llvm/Transforms/IPO/FuncExtractor.h
  llvm/lib/LTO/LTO.cpp
  llvm/lib/LTO/LTOBackend.cpp
  llvm/lib/Passes/PassBuilder.cpp
  llvm/lib/Transforms/IPO/ExtractGV.cpp
  llvm/test/Other/Inputs/single-func.ll
  llvm/test/Other/single-func.ll
  llvm/tools/llvm-lto2/llvm-lto2.cpp
  llvm/tools/opt/NewPMDriver.cpp

Index: llvm/tools/opt/NewPMDriver.cpp
===================================================================
--- llvm/tools/opt/NewPMDriver.cpp
+++ llvm/tools/opt/NewPMDriver.cpp
@@ -117,6 +117,11 @@
              "the OptimizerLast extension point into default pipelines"),
     cl::Hidden);
 
+static cl::list<std::string> SingleFuncList(
+    "single-func", cl::value_desc("function names"),
+    cl::desc("Only compile functions whose names are in the list"),
+    cl::CommaSeparated, cl::Hidden);
+
 // Individual pipeline tuning options.
 extern cl::opt<bool> DisableLoopUnrolling;
 
@@ -384,6 +389,7 @@
   PB.registerFunctionAnalyses(FAM);
   PB.registerLoopAnalyses(LAM);
   PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+  PB.registerFunctionsToCompile(SingleFuncList);
 
   ModulePassManager MPM(DebugPM);
   if (VK > VK_NoVerifier)
Index: llvm/tools/llvm-lto2/llvm-lto2.cpp
===================================================================
--- llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -158,6 +158,11 @@
     PassPlugins("load-pass-plugin",
                 cl::desc("Load passes from plugin library"));
 
+static cl::list<std::string>
+    SingleFuncList("single-func", cl::value_desc("function names"),
+                   cl::desc("Only compile functions whose name match this"),
+                   cl::CommaSeparated, cl::Hidden);
+
 static void check(Error E, std::string Msg) {
   if (!E)
     return;
@@ -297,6 +302,7 @@
   Conf.StatsFile = StatsFile;
   Conf.PTO.LoopVectorization = Conf.OptLevel > 1;
   Conf.PTO.SLPVectorization = Conf.OptLevel > 1;
+  Conf.LTOFunctionsToCompile = SingleFuncList;
 
   ThinBackend Backend;
   if (ThinLTODistributedIndexes)
Index: llvm/test/Other/single-func.ll
===================================================================
--- /dev/null
+++ llvm/test/Other/single-func.ll
@@ -0,0 +1,75 @@
+; REQUIRES: x86_64-linux
+; RUN: opt < %s -passes='default<O0>' -single-func=foo -S -o %t0 | FileCheck %s --check-prefix=FOO
+; RUN: opt < %s -passes='default<O1>' -single-func=foo -S -o %t1 | FileCheck %s --check-prefix=RECUR-FOO
+; RUN: opt < %s -passes='default<O2>' -single-func=foo -single-func=bar -S -o %t2 | FileCheck %s --check-prefix=FOO-BAR
+; RUN: opt < %s -passes='default<O2>' -single-func=foo -single-func=bar -S -o %t2 | FileCheck %s --check-prefix=FOO-BAR
+
+; RUN: opt -thinlto-bc -o %t3.bc %s
+; RUN: opt -thinlto-bc -o %t4.bc %S/Inputs/single-func.ll
+; RUN: llvm-lto2 run -use-new-pm -single-func=foo %t3.bc %t4.bc -o %t5 -r %t3.bc,main,px -r %t3.bc,foo,px  -r %t3.bc,bar,px -r %t3.bc,go,px -r %t4.bc,other,px  | FileCheck %s --check-prefix=RECUR-FOO
+
+; RUN: opt -o %t3.bc %s
+; RUN: opt -o %t4.bc %S/Inputs/single-func.ll
+; RUN: llvm-lto2 run -use-new-pm -single-func=foo %t3.bc %t4.bc -o %t5 -r %t3.bc,main,px -r %t3.bc,foo,px  -r %t3.bc,bar,px -r %t3.bc,go,px -r %t4.bc,other,px  | FileCheck %s --check-prefix=RECUR-FOO
+
+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-scei-ps4"
+
+define dso_local i32 @main(i32 %x, i32 (i32)* %f) {
+  call i32 @foo(i32 %x, i32 (i32)* %f)
+  ret i32 1
+}
+
+define dso_local i32 @foo(i32 %x, i32 (i32)* %f) {
+entry:
+  %retval = alloca i32, align 4
+  %x.addr = alloca i32, align 4
+  store i32 %x, i32* %x.addr, align 4
+  %0 = load i32, i32* %x.addr, align 4
+  %cmp = icmp eq i32 %0, 0
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:
+  call i32 @bar()
+  store i32 1, i32* %retval, align 4
+  br label %return
+
+if.else:
+  call i32 %f(i32 1), !prof !0
+  store i32 2, i32* %retval, align 4
+  br label %return
+
+return:
+  %3 = load i32, i32* %retval, align 4
+  ret i32 %3
+}
+
+define dso_local i32 @bar() {
+entry:
+  ret i32 8
+}
+
+define dso_local i32 @go() {
+entry:
+  ret i32 6
+}
+
+!0 = !{!"VP", i32 0, i64 7, i64 -5182264717993193164, i64 5, i64 -1069303473483922844, i64 2}
+
+;; Check only function foo is compiled.
+; FOO: Found Function foo to compile.
+; FOO-NOT: main
+; FOO-NOT: bar
+; FOO-NOT: go
+
+;; Check only function foo, bar and go are compiled.
+; RECUR-FOO: Found Function foo to compile.
+; RECUR-FOO: Found Function bar to compile.
+; RECUR-FOO: Found Function go to compile.
+; RECUR-FOO-NOT: main
+
+;; Check only function foo and bar are compiled.
+; FOO-BAR: Found Function foo to compile.
+; FOO-BAR: Found Function bar to compile.
+; FOO-NOT: main
+; FOO-NOT: go
Index: llvm/test/Other/Inputs/single-func.ll
===================================================================
--- /dev/null
+++ llvm/test/Other/Inputs/single-func.ll
@@ -0,0 +1,7 @@
+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-scei-ps4"
+
+define dso_local i32 @other() {
+entry:
+  ret i32 16
+}
Index: llvm/lib/Transforms/IPO/ExtractGV.cpp
===================================================================
--- llvm/lib/Transforms/IPO/ExtractGV.cpp
+++ llvm/lib/Transforms/IPO/ExtractGV.cpp
@@ -11,10 +11,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ADT/SetVector.h"
+#include "llvm/IR/Instructions.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Pass.h"
+#include "llvm/ProfileData/InstrProf.h"
 #include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/FuncExtractor.h"
 #include <algorithm>
 using namespace llvm;
 
@@ -50,115 +53,218 @@
 }
 
 namespace {
-  /// A pass to extract specific global values and their dependencies.
-  class GVExtractorPass : public ModulePass {
-    SetVector<GlobalValue *> Named;
-    bool deleteStuff;
-    bool keepConstInit;
-  public:
-    static char ID; // Pass identification, replacement for typeid
-
-    /// If deleteS is true, this pass deletes the specified global values.
-    /// Otherwise, it deletes as much of the module as possible, except for the
-    /// global values specified.
-    explicit GVExtractorPass(std::vector<GlobalValue*> &GVs,
-                             bool deleteS = true, bool keepConstInit = false)
-      : ModulePass(ID), Named(GVs.begin(), GVs.end()), deleteStuff(deleteS),
-        keepConstInit(keepConstInit) {}
-
-    bool runOnModule(Module &M) override {
-      if (skipModule(M))
-        return false;
-
-      // Visit the global inline asm.
-      if (!deleteStuff)
-        M.setModuleInlineAsm("");
-
-      // For simplicity, just give all GlobalValues ExternalLinkage. A trickier
-      // implementation could figure out which GlobalValues are actually
-      // referenced by the Named set, and which GlobalValues in the rest of
-      // the module are referenced by the NamedSet, and get away with leaving
-      // more internal and private things internal and private. But for now,
-      // be conservative and simple.
-
-      // Visit the GlobalVariables.
-      for (Module::global_iterator I = M.global_begin(), E = M.global_end();
-           I != E; ++I) {
-        bool Delete =
-            deleteStuff == (bool)Named.count(&*I) && !I->isDeclaration() &&
-            (!I->isConstant() || !keepConstInit);
-        if (!Delete) {
-          if (I->hasAvailableExternallyLinkage())
-            continue;
-          if (I->getName() == "llvm.global_ctors")
-            continue;
-        }
+class GVExtractor {
+  SetVector<GlobalValue *> &Named;
+  bool DeleteStuff;
+  bool KeepConstInit;
 
-        makeVisible(*I, Delete);
+public:
+  /// If deleteS is true, this pass deletes the specified global values.
+  /// Otherwise, it deletes as much of the module as possible, except for the
+  /// global values specified.
+  GVExtractor(SetVector<GlobalValue *> &GVs, bool deleteS, bool KeepConstInit)
+      : Named(GVs), DeleteStuff(deleteS), KeepConstInit(KeepConstInit) {}
 
-        if (Delete) {
-          // Make this a declaration and drop it's comdat.
-          I->setInitializer(nullptr);
-          I->setComdat(nullptr);
-        }
+  bool run(Module &M) {
+    // Visit the global inline asm.
+    if (!DeleteStuff)
+      M.setModuleInlineAsm("");
+
+    // For simplicity, just give all GlobalValues ExternalLinkage. A trickier
+    // implementation could figure out which GlobalValues are actually
+    // referenced by the Named set, and which GlobalValues in the rest of
+    // the module are referenced by the NamedSet, and get away with leaving
+    // more internal and private things internal and private. But for now,
+    // be conservative and simple.
+
+    // Visit the GlobalVariables.
+    for (Module::global_iterator I = M.global_begin(), E = M.global_end();
+         I != E; ++I) {
+      bool Delete = DeleteStuff == (bool)Named.count(&*I) &&
+                    !I->isDeclaration() && (!I->isConstant() || !KeepConstInit);
+      if (!Delete) {
+        if (I->hasAvailableExternallyLinkage())
+          continue;
+        if (I->getName() == "llvm.global_ctors")
+          continue;
       }
 
-      // Visit the Functions.
-      for (Function &F : M) {
-        bool Delete =
-            deleteStuff == (bool)Named.count(&F) && !F.isDeclaration();
-        if (!Delete) {
-          if (F.hasAvailableExternallyLinkage())
-            continue;
-        }
+      makeVisible(*I, Delete);
 
-        makeVisible(F, Delete);
+      if (Delete) {
+        // Make this a declaration and drop it's comdat.
+        I->setInitializer(nullptr);
+        I->setComdat(nullptr);
+      }
+    }
 
-        if (Delete) {
-          // Make this a declaration and drop it's comdat.
-          F.deleteBody();
-          F.setComdat(nullptr);
-        }
+    // Visit the Functions.
+    for (Function &F : M) {
+      bool Delete = DeleteStuff == (bool)Named.count(&F) && !F.isDeclaration();
+      if (!Delete) {
+        if (F.hasAvailableExternallyLinkage())
+          continue;
       }
 
-      // Visit the Aliases.
-      for (Module::alias_iterator I = M.alias_begin(), E = M.alias_end();
-           I != E;) {
-        Module::alias_iterator CurI = I;
-        ++I;
+      makeVisible(F, Delete);
 
-        bool Delete = deleteStuff == (bool)Named.count(&*CurI);
-        makeVisible(*CurI, Delete);
+      if (Delete) {
+        // Make this a declaration and drop it's comdat.
+        F.deleteBody();
+        F.setComdat(nullptr);
+      }
+    }
 
-        if (Delete) {
-          Type *Ty =  CurI->getValueType();
+    // Visit the Aliases.
+    for (Module::alias_iterator I = M.alias_begin(), E = M.alias_end();
+         I != E;) {
+      Module::alias_iterator CurI = I;
+      ++I;
 
-          CurI->removeFromParent();
-          llvm::Value *Declaration;
-          if (FunctionType *FTy = dyn_cast<FunctionType>(Ty)) {
-            Declaration = Function::Create(FTy, GlobalValue::ExternalLinkage,
-                                           CurI->getAddressSpace(),
-                                           CurI->getName(), &M);
+      bool Delete = DeleteStuff == (bool)Named.count(&*CurI);
+      makeVisible(*CurI, Delete);
 
-          } else {
-            Declaration =
+      if (Delete) {
+        Type *Ty = CurI->getValueType();
+
+        CurI->removeFromParent();
+        llvm::Value *Declaration;
+        if (FunctionType *FTy = dyn_cast<FunctionType>(Ty)) {
+          Declaration =
+              Function::Create(FTy, GlobalValue::ExternalLinkage,
+                               CurI->getAddressSpace(), CurI->getName(), &M);
+
+        } else {
+          Declaration =
               new GlobalVariable(M, Ty, false, GlobalValue::ExternalLinkage,
                                  nullptr, CurI->getName());
+        }
+        CurI->replaceAllUsesWith(Declaration);
+        delete &*CurI;
+      }
+    }
+
+    return true;
+  }
+};
+
+/// A pass to extract specific global values and their dependencies.
+class GVExtractorPass : public ModulePass {
+  SetVector<GlobalValue *> Named;
+  bool DeleteStuff;
+  bool KeepConstInit;
+
+public:
+  static char ID; // Pass identification, replacement for typeid
+
+  /// If deleteS is true, this pass deletes the specified global values.
+  /// Otherwise, it deletes as much of the module as possible, except for the
+  /// global values specified.
+  explicit GVExtractorPass(std::vector<GlobalValue *> &GVs, bool deleteS = true,
+                           bool KeepConstInit = false)
+      : ModulePass(ID), Named(GVs.begin(), GVs.end()), DeleteStuff(deleteS),
+        KeepConstInit(KeepConstInit) {}
+
+  bool runOnModule(Module &M) override {
+    if (skipModule(M))
+      return false;
 
+    GVExtractor Extractor(Named, DeleteStuff, KeepConstInit);
+    Extractor.run(M);
+    return true;
+  }
+};
+
+char GVExtractorPass::ID = 0;
+} // namespace
+
+ModulePass *llvm::createGVExtractionPass(std::vector<GlobalValue *> &GVs,
+                                         bool deleteFn, bool KeepConstInit) {
+  return new GVExtractorPass(GVs, deleteFn, KeepConstInit);
+}
+
+PreservedAnalyses FuncExtractorPass::run(Module &M, ModuleAnalysisManager &AM) {
+  if (FunctionsToCompile.empty())
+    return PreservedAnalyses::all();
+
+  // Figure out seed functions to compile.
+  SetVector<GlobalValue *> Seeds;
+  for (const auto &Name : FunctionsToCompile) {
+    if (Function *F = M.getFunction(Name))
+      if (!F->isDeclaration())
+        Seeds.insert(F);
+  }
+
+  SetVector<GlobalValue *> Functions(Seeds);
+
+  // Collect possible callee functions of the seed functions.
+  if (Recursive) {
+    InstrProfSymtab Symtab;
+    (void)(bool) Symtab.create(M, InLTO);
+
+    std::vector<llvm::Function *> Workqueue;
+    for (GlobalValue *GV : Seeds) {
+      if (auto *F = dyn_cast<Function>(GV)) {
+        Workqueue.push_back(F);
+      }
+    }
+
+    auto AddFunc = [&](Function *CF) {
+      if (CF && !CF->isDeclaration() && !Functions.count(CF)) {
+        Functions.insert(CF);
+        Workqueue.push_back(CF);
+      }
+    };
+
+    while (!Workqueue.empty()) {
+      Function *F = &*Workqueue.back();
+      Workqueue.pop_back();
+      for (auto &BB : *F) {
+        for (auto &I : BB) {
+          CallBase *CB = dyn_cast<CallBase>(&I);
+          if (!CB)
+            continue;
+          if (Function *CF = CB->getCalledFunction()) {
+            AddFunc(CF);
+          } else {
+            // Collect potential indirect call targets from the profile.
+            InstrProfValueData ValueData[8];
+            uint32_t ActualNumValueData;
+            uint64_t TotalC;
+            if (getValueProfDataFromInst(*CB, IPVK_IndirectCallTarget, 8,
+                                         ValueData, ActualNumValueData,
+                                         TotalC)) {
+              for (const auto &VD : ArrayRef<InstrProfValueData>(
+                       ValueData, ActualNumValueData)) {
+                AddFunc(Symtab.getFunction(VD.Value));
+              }
+            }
           }
-          CurI->replaceAllUsesWith(Declaration);
-          delete &*CurI;
         }
       }
+    }
+  }
 
-      return true;
+  for (GlobalValue *GV : Functions) {
+    if (auto *F = dyn_cast<Function>(GV)) {
+      outs() << "Found Function " << F->getName() << " to compile.\n";
     }
-  };
+  }
 
-  char GVExtractorPass::ID = 0;
-}
+  // Assign a linker declaration linkage to functions that are not
+  // user-specified to avoid emitting object code for them.
+  for (GlobalValue *GV : Functions) {
+    if (!Seeds.contains(GV)) {
+      GV->setLinkage(GlobalValue::AvailableExternallyLinkage);
+      auto *F = dyn_cast<Function>(GV);
+      assert(F);
+      // Reset comdat for linker declaration functions to favor the IR
+      // verifier.
+      F->setComdat(nullptr);
+    }
+  }
 
-ModulePass *llvm::createGVExtractionPass(std::vector<GlobalValue *> &GVs,
-                                         bool deleteFn, bool keepConstInit) {
-  return new GVExtractorPass(GVs, deleteFn, keepConstInit);
+  GVExtractor Extractor(Functions, false, true);
+  Extractor.run(M);
+  return PreservedAnalyses::none();
 }
Index: llvm/lib/Passes/PassBuilder.cpp
===================================================================
--- llvm/lib/Passes/PassBuilder.cpp
+++ llvm/lib/Passes/PassBuilder.cpp
@@ -97,6 +97,7 @@
 #include "llvm/Transforms/IPO/DeadArgumentElimination.h"
 #include "llvm/Transforms/IPO/ElimAvailExtern.h"
 #include "llvm/Transforms/IPO/ForceFunctionAttrs.h"
+#include "llvm/Transforms/IPO/FuncExtractor.h"
 #include "llvm/Transforms/IPO/FunctionAttrs.h"
 #include "llvm/Transforms/IPO/FunctionImport.h"
 #include "llvm/Transforms/IPO/GlobalDCE.h"
@@ -435,7 +436,8 @@
 PassBuilder::PassBuilder(bool DebugLogging, TargetMachine *TM,
                          PipelineTuningOptions PTO, Optional<PGOOptions> PGOOpt,
                          PassInstrumentationCallbacks *PIC)
-    : DebugLogging(DebugLogging), TM(TM), PTO(PTO), PGOOpt(PGOOpt), PIC(PIC) {
+    : DebugLogging(DebugLogging), TM(TM), PTO(PTO), PGOOpt(PGOOpt), PIC(PIC),
+      FunctionsToCompile() {
   if (TM)
     TM->registerPassBuilderCallbacks(*this, DebugLogging);
   if (PIC && shouldPopulateClassToPassNames()) {
@@ -509,6 +511,12 @@
     C(LAM);
 }
 
+void PassBuilder::registerFunctionsToCompile(
+    const std::vector<std::string> &Names) {
+  FunctionsToCompile.insert(FunctionsToCompile.end(), Names.begin(),
+                            Names.end());
+}
+
 // Helper to add AnnotationRemarksPass.
 static void addAnnotationRemarksPass(ModulePassManager &MPM) {
   FunctionPassManager FPM;
@@ -1079,6 +1087,10 @@
                                            true /* SamplePGO */));
   }
 
+  if (!FunctionsToCompile.empty())
+    MPM.addPass(
+        FuncExtractorPass(FunctionsToCompile, Phase == ThinLTOPhase::PostLink));
+
   if (AttributorRun & AttributorRunOption::MODULE)
     MPM.addPass(AttributorPass());
 
@@ -1538,6 +1550,9 @@
     MPM.addPass(RequireAnalysisPass<ProfileSummaryAnalysis, Module>());
   }
 
+  if (!FunctionsToCompile.empty())
+    MPM.addPass(FuncExtractorPass(FunctionsToCompile, true));
+
   // Remove unused virtual tables to improve the quality of code generated by
   // whole-program devirtualization and bitset lowering.
   MPM.addPass(GlobalDCEPass());
@@ -1764,6 +1779,9 @@
 
   ModulePassManager MPM(DebugLogging);
 
+  if (!FunctionsToCompile.empty())
+    MPM.addPass(FuncExtractorPass(FunctionsToCompile, false, false));
+
   if (PGOOpt && (PGOOpt->Action == PGOOptions::IRInstr ||
                  PGOOpt->Action == PGOOptions::IRUse))
     addPGOInstrPassesForO0(
Index: llvm/lib/LTO/LTOBackend.cpp
===================================================================
--- llvm/lib/LTO/LTOBackend.cpp
+++ llvm/lib/LTO/LTOBackend.cpp
@@ -247,6 +247,7 @@
   PB.registerFunctionAnalyses(FAM);
   PB.registerLoopAnalyses(LAM);
   PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+  PB.registerFunctionsToCompile(Conf.LTOFunctionsToCompile);
 
   ModulePassManager MPM(Conf.DebugPassManager);
 
@@ -312,6 +313,7 @@
   PB.registerFunctionAnalyses(FAM);
   PB.registerLoopAnalyses(LAM);
   PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+  PB.registerFunctionsToCompile(Conf.LTOFunctionsToCompile);
 
   ModulePassManager MPM;
 
Index: llvm/lib/LTO/LTO.cpp
===================================================================
--- llvm/lib/LTO/LTO.cpp
+++ llvm/lib/LTO/LTO.cpp
@@ -835,6 +835,7 @@
                          ThinLTO.ModuleMap.size()))
     return Err;
 
+  const SymbolResolution *OrigResI = ResI;
   for (const InputFile::Symbol &Sym : Syms) {
     assert(ResI != ResE);
     SymbolResolution Res = *ResI++;
@@ -885,6 +886,22 @@
     }
   }
 
+  if (!Conf.LTOFunctionsToCompile.empty()) {
+    assert(Conf.ThinLTOModulesToCompile.empty() &&
+           "Single function mode should not be combined with single "
+           "module mode.");
+    if (!ThinLTO.ModulesToCompile)
+      ThinLTO.ModulesToCompile = ModuleMapType();
+    std::set<StringRef> FuncNames(Conf.LTOFunctionsToCompile.begin(),
+                                  Conf.LTOFunctionsToCompile.end());
+    for (const InputFile::Symbol &Sym : Syms) {
+      SymbolResolution Res = *OrigResI++;
+      // Only compile a name-matched function that is prevailing.
+      if (Res.Prevailing && FuncNames.count(Sym.getIRName()))
+        ThinLTO.ModulesToCompile->insert({BM.getModuleIdentifier(), BM});
+    }
+  }
+
   return Error::success();
 }
 
Index: llvm/include/llvm/Transforms/IPO/FuncExtractor.h
===================================================================
--- /dev/null
+++ llvm/include/llvm/Transforms/IPO/FuncExtractor.h
@@ -0,0 +1,41 @@
+//===- FuncExtractor.h - Function extraction pass ----------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// This file extracts necessary functions to favor -single-func complication.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_IPO_FUNCEXTRACTOR_H
+#define LLVM_TRANSFORMS_IPO_FUNCEXTRACTOR_H
+
+#include "llvm/IR/PassManager.h"
+#include <string>
+
+namespace llvm {
+
+class Module;
+
+class FuncExtractorPass : public PassInfoMixin<FuncExtractorPass> {
+  std::vector<std::string> FunctionsToCompile;
+  bool InLTO;
+  // Recursively extract possible callees.
+  bool Recursive;
+
+public:
+  FuncExtractorPass(const std::vector<std::string> &FunctionsToCompile,
+                    bool InLTO, bool Recursive = true)
+      : FunctionsToCompile(FunctionsToCompile), InLTO(InLTO),
+        Recursive(Recursive) {}
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_IPO_FUNCEXTRACTOR_H
Index: llvm/include/llvm/Passes/PassBuilder.h
===================================================================
--- llvm/include/llvm/Passes/PassBuilder.h
+++ llvm/include/llvm/Passes/PassBuilder.h
@@ -141,6 +141,8 @@
   PipelineTuningOptions PTO;
   Optional<PGOOptions> PGOOpt;
   PassInstrumentationCallbacks *PIC;
+  /// Specific functions to compile.
+  std::vector<std::string> FunctionsToCompile;
 
 public:
   /// A struct to capture parsed pass pipeline names.
@@ -323,6 +325,9 @@
   /// additional analyses.
   void registerLoopAnalyses(LoopAnalysisManager &LAM);
 
+  /// Registers specific functions to compile.
+  void registerFunctionsToCompile(const std::vector<std::string> &Names);
+
   /// Construct the core LLVM function canonicalization and simplification
   /// pipeline.
   ///
Index: llvm/include/llvm/LTO/Config.h
===================================================================
--- llvm/include/llvm/LTO/Config.h
+++ llvm/include/llvm/LTO/Config.h
@@ -149,6 +149,9 @@
   /// Specific thinLTO modules to compile.
   std::vector<std::string> ThinLTOModulesToCompile;
 
+  /// Specific functions to compile.
+  std::vector<std::string> LTOFunctionsToCompile;
+
   /// Time trace enabled.
   bool TimeTraceEnabled = false;
 
Index: lld/test/ELF/lto/lto-single-func.ll
===================================================================
--- /dev/null
+++ lld/test/ELF/lto/lto-single-func.ll
@@ -0,0 +1,90 @@
+; REQUIRES: x86
+; RUN: rm -fr %t && mkdir %t && cd %t
+
+; RUN: opt -thinlto-bc -o thin1.o %s
+; RUN: opt -thinlto-bc -o thin2.o %S/Inputs/thin2.ll
+; RUN: ld.lld thin1.o thin2.o --lto-single-func=foo --lto-obj-path=thin.o -plugin-opt=new-pass-manager  | FileCheck %s --check-prefix=RECUR-FOO
+; RUN: llvm-readelf -S -s thin.o | FileCheck %s --check-prefix=THIN-DEFAULT
+; RUN: llvm-readelf -S -s thin.o1 | FileCheck %s --check-prefix=THIN-FOO
+; RUN: not ls thin.o2
+
+; RUN: ld.lld thin1.o thin2.o --lto-single-func=foo  --lto-single-func=blah --lto-obj-path=thin.o -plugin-opt=new-pass-manager  | FileCheck %s --check-prefix=RECUR-FOO-BLAH
+; RUN: llvm-readelf -S -s thin.o | FileCheck %s --check-prefix=THIN-DEFAULT
+; RUN: llvm-readelf -S -s thin.o1 | FileCheck %s --check-prefix=THIN-FOO
+; RUN: llvm-readelf -S -s thin.o2 | FileCheck %s --check-prefix=THIN-BLAH
+
+; RUN: opt -o full1.o %s
+; RUN: opt -o full2.o %S/Inputs/thin2.ll
+; RUN: ld.lld full1.o full2.o --lto-single-func=foo --lto-obj-path=full.o -plugin-opt=new-pass-manager  | FileCheck %s --check-prefix=RECUR-FOO
+; RUN: llvm-readelf -S -s full.o | FileCheck %s --check-prefix=LTO-FOO
+
+;; Check only function foo, bar and go are compiled.
+; RECUR-FOO: Found Function foo to compile.
+; RECUR-FOO: Found Function bar to compile.
+; RECUR-FOO: Found Function go to compile.
+; RECUR-FOO-NOT: main
+
+;; Check only function foo and bar are compiled.
+; RECUR-FOO-BLAH: Found Function blah to compile.
+; RECUR-FOO-BLAH: Found Function foo to compile.
+; RECUR-FOO-BLAH: Found Function bar to compile.
+; RECUR-FOO-BLAH: Found Function go to compile.
+; RECUR-FOO-BLAH-NOT: main
+
+; THIN-DEFAULT:     Value        Size Type Bind   Vis     Ndx Name
+; THIN-DEFAULT: 0000000000000000    0 FILE LOCAL  DEFAULT ABS ld-temp.o
+; THIN-FOO:         Value        Size Type Bind   Vis     Ndx Name
+; THIN-FOO:     0000000000000000    0 FILE LOCAL  DEFAULT ABS lto-single-func.ll
+; THIN-FOO:     0000000000000000   28 FUNC GLOBAL HIDDEN   3  foo
+; THIN-FOO-NOT:  {{.*}} bar
+
+; THIN-BLAH     0000000000000000    0 FILE LOCAL  DEFAULT ABS thin2.ll
+; THIN-BLAH:    0000000000000000   4  FUNC GLOBAL HIDDEN   3  blah
+
+; LTO-FOO:     0000000000000000    0 FILE LOCAL  DEFAULT ABS ld-temp.o
+; LTO-FOO:     0000000000000000   28 FUNC GLOBAL HIDDEN   3  foo
+; LTO-FOO-NOT:  {{.*}} bar
+
+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-scei-ps4"
+
+define dso_local i32 @main(i32 %x, i32 (i32)* %f) {
+  call i32 @foo(i32 %x, i32 (i32)* %f)
+  ret i32 1
+}
+
+define dso_local i32 @foo(i32 %x, i32 (i32)* %f) {
+entry:
+  %retval = alloca i32, align 4
+  %x.addr = alloca i32, align 4
+  store i32 %x, i32* %x.addr, align 4
+  %0 = load i32, i32* %x.addr, align 4
+  %cmp = icmp eq i32 %0, 0
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:
+  call i32 @bar()
+  store i32 1, i32* %retval, align 4
+  br label %return
+
+if.else:
+  call i32 %f(i32 1), !prof !0
+  store i32 2, i32* %retval, align 4
+  br label %return
+
+return:
+  %3 = load i32, i32* %retval, align 4
+  ret i32 %3
+}
+
+define dso_local i32 @bar() {
+entry:
+  ret i32 8
+}
+
+define dso_local i32 @go() {
+entry:
+  ret i32 6
+}
+
+!0 = !{!"VP", i32 0, i64 7, i64 -5182264717993193164, i64 5, i64 -1069303473483922844, i64 2}
Index: lld/ELF/Options.td
===================================================================
--- lld/ELF/Options.td
+++ lld/ELF/Options.td
@@ -582,7 +582,9 @@
 def thinlto_object_suffix_replace_eq: JJ<"thinlto-object-suffix-replace=">;
 def thinlto_prefix_replace_eq: JJ<"thinlto-prefix-replace=">;
 def thinlto_single_module_eq: JJ<"thinlto-single-module=">,
-  HelpText<"Specific a single module to compile in ThinLTO mode, for debugging only">;
+  HelpText<"Specify a single module to compile in ThinLTO mode, for debugging only">;
+def lto_single_func_eq: JJ<"lto-single-func=">,
+  HelpText<"Specify a single function to compile in LTO mode, for debugging only">;
 
 def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for --lto-O">;
 def: F<"plugin-opt=debug-pass-manager">,
Index: lld/ELF/LTO.cpp
===================================================================
--- lld/ELF/LTO.cpp
+++ lld/ELF/LTO.cpp
@@ -157,6 +157,9 @@
   for (const llvm::StringRef &name : config->thinLTOModulesToCompile)
     c.ThinLTOModulesToCompile.emplace_back(name);
 
+  for (const llvm::StringRef &name : config->LTOFunctionsToCompile)
+    c.LTOFunctionsToCompile.emplace_back(name);
+
   c.TimeTraceEnabled = config->timeTraceEnabled;
   c.TimeTraceGranularity = config->timeTraceGranularity;
 
Index: lld/ELF/Driver.cpp
===================================================================
--- lld/ELF/Driver.cpp
+++ lld/ELF/Driver.cpp
@@ -1077,6 +1077,8 @@
       getOldNewOptions(args, OPT_thinlto_prefix_replace_eq);
   config->thinLTOModulesToCompile =
       args::getStrings(args, OPT_thinlto_single_module_eq);
+  config->LTOFunctionsToCompile =
+      args::getStrings(args, OPT_lto_single_func_eq);
   config->timeTraceEnabled = args.hasArg(OPT_time_trace);
   config->timeTraceGranularity =
       args::getInteger(args, OPT_time_trace_granularity, 500);
@@ -2189,10 +2191,12 @@
   // Likewise, --plugin-opt=emit-llvm and --plugin-opt=emit-asm are the
   // options to create output files in bitcode or assembly code
   // repsectively. No object files are generated.
-  // Also bail out here when only certain thinLTO modules are specified for
-  // compilation. The intermediate object file are the expected output.
+  // Also bail out here when only certain functions or thinLTO modules are
+  // specified for compilation. The intermediate object file are the expected
+  // output.
   if (config->thinLTOIndexOnly || config->emitLLVM || config->ltoEmitAsm ||
-      !config->thinLTOModulesToCompile.empty())
+      !config->thinLTOModulesToCompile.empty() ||
+      !config->LTOFunctionsToCompile.empty())
     return;
 
   // Apply symbol renames for -wrap and combine foo@v1 and foo@@v1.
Index: lld/ELF/Config.h
===================================================================
--- lld/ELF/Config.h
+++ lld/ELF/Config.h
@@ -130,6 +130,7 @@
   std::vector<llvm::StringRef> filterList;
   std::vector<llvm::StringRef> searchPaths;
   std::vector<llvm::StringRef> symbolOrderingFile;
+  std::vector<llvm::StringRef> LTOFunctionsToCompile;
   std::vector<llvm::StringRef> thinLTOModulesToCompile;
   std::vector<llvm::StringRef> undefined;
   std::vector<SymbolVersion> dynamicList;
Index: clang/test/CodeGen/single-func.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/single-func.c
@@ -0,0 +1,59 @@
+// RUN: %clang %s -O0 -fexperimental-new-pass-manager -fsingle-func=foo -S -o %t0 | FileCheck %s --check-prefix=FOO
+// RUN: FileCheck %s < %t0 --check-prefix=ASM-FOO
+// RUN: %clang %s -O1 -fexperimental-new-pass-manager -fsingle-func=foo -S -o %t1 | FileCheck %s --check-prefix=RECUR-FOO
+// RUN: FileCheck %s < %t1 --check-prefix=ASM-FOO
+// RUN: %clang %s -O2 -fexperimental-new-pass-manager -fsingle-func=bar -fsingle-func=go  -S -o %t2 | FileCheck %s --check-prefix=BAR-GO
+// RUN: FileCheck %s < %t2 --check-prefix=ASM-BAR-GO
+
+int g;
+
+void bar() {
+  g = 0;
+}
+
+void go() {
+  g = 1;
+}
+
+void foo(int x) {
+  if (x == 0)
+    bar();
+  else
+    go();
+}
+
+int main()
+{
+  foo(g);
+  return 0;
+}
+
+// Check only function foo is compiled.
+// FOO: Found Function foo to compile.
+// FOO-NOT: main
+// FOO-NOT: bar
+// FOO-NOT: go
+
+// Check only function foo has code generated.
+// ASM-FOO: foo:
+// ASM-FOO-NOT: main:
+// ASM-FOO-NOT: bar:
+// ASM-FOO-NOT: go:
+
+// Check only function foo, bar and go are compiled.
+// RECUR-FOO: Found Function foo to compile.
+// RECUR-FOO: Found Function bar to compile.
+// RECUR-FOO: Found Function go to compile.
+// RECUR-FOO-NOT: main
+
+// Check only function bar and go are compiled.
+// BAR-GO: Found Function bar to compile.
+// BAR-GO: Found Function go to compile.
+// FOO-NOT: main
+// FOO-NOT: foo
+
+// Check only function foo has code generated.
+// ASM-BAR-GO: bar:
+// ASM-BAR-GO: go:
+// ASM-BAR-GO-NOT: foo:
+// ASM-BAR-GO-NOT: main:
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -1340,6 +1340,7 @@
     }
   }
 
+  Opts.FunctionsToCompile = Args.getAllArgValues(OPT_fsingle_func);
   Opts.DependentLibraries = Args.getAllArgValues(OPT_dependent_lib);
   Opts.LinkerOptions = Args.getAllArgValues(OPT_linker_option);
   bool NeedLocTracking = false;
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -5911,6 +5911,13 @@
                    options::OPT_fno_sized_deallocation, false))
     CmdArgs.push_back("-fsized-deallocation");
 
+  if (Args.hasArg(options::OPT_fsingle_func)) {
+    std::vector<std::string> Names =
+        Args.getAllArgValues(options::OPT_fsingle_func);
+    for (const auto &Name : Names)
+      CmdArgs.push_back(Args.MakeArgString("-fsingle-func=" + Name));
+  }
+
   // -faligned-allocation is on by default in C++17 onwards and otherwise off
   // by default.
   if (Arg *A = Args.getLastArg(options::OPT_faligned_allocation,
Index: clang/lib/CodeGen/BackendUtil.cpp
===================================================================
--- clang/lib/CodeGen/BackendUtil.cpp
+++ clang/lib/CodeGen/BackendUtil.cpp
@@ -1185,6 +1185,7 @@
   PB.registerFunctionAnalyses(FAM);
   PB.registerLoopAnalyses(LAM);
   PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+  PB.registerFunctionsToCompile(CodeGenOpts.FunctionsToCompile);
 
   ModulePassManager MPM(CodeGenOpts.DebugPassManager);
 
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -1163,6 +1163,9 @@
 def forder_file_instrumentation : Flag<["-"], "forder-file-instrumentation">,
     Group<f_Group>, Flags<[CC1Option, CoreOption]>,
     HelpText<"Generate instrumented code to collect order file into default.profraw file (overridden by '=' form of option or LLVM_PROFILE_FILE env var)">;
+def fsingle_func : Joined<["-"], "fsingle-func=">,
+    Group<f_Group>, Flags<[CC1Option, CoreOption]>,
+    HelpText<"Specify a single function to compile in non-LTO mode, for debugging only">;
 
 defm addrsig : BoolFOption<"addrsig",
   "CodeGenOpts.Addrsig", DefaultsToFalse,
Index: clang/include/clang/Basic/CodeGenOptions.h
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.h
+++ clang/include/clang/Basic/CodeGenOptions.h
@@ -215,6 +215,9 @@
   /// function instead of to trap instructions.
   std::string TrapFuncName;
 
+  /// Specific functions to compile.
+  std::vector<std::string> FunctionsToCompile;
+
   /// A list of dependent libraries.
   std::vector<std::string> DependentLibraries;
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D92806: Single func... Hongtao Yu via Phabricator via cfe-commits

Reply via email to