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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits