[llvm] 6d12cf1 - Reapply "[IR][NFC] Drop vtable from PassConcept/PassModel" (#208168) (#209414)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 14 06:33:29 PDT 2026
Author: Alexis Engelke
Date: 2026-07-14T15:33:25+02:00
New Revision: 6d12cf1389fa3ef57f880b8c0b25e66f0caa2f32
URL: https://github.com/llvm/llvm-project/commit/6d12cf1389fa3ef57f880b8c0b25e66f0caa2f32
DIFF: https://github.com/llvm/llvm-project/commit/6d12cf1389fa3ef57f880b8c0b25e66f0caa2f32.diff
LOG: Reapply "[IR][NFC] Drop vtable from PassConcept/PassModel" (#208168) (#209414)
The PassConcept/PassModel vtable has a size of 64 bytes (offset to top,
RTTI pointer, complete object destructor, deleting destructor, run,
printPipeline, name, isRequired), which add up to 53kiB (all
targets)/44 kiB (single-target). As more back-end passes get ported to
the new pass manager, this size will increase.
Remove the vtables by replacing the virtual dispatch with explicit
function pointers, initialized when adding the pass to the pass manager.
While this very slightly increases the cost of adding a pass, there's
also a very slight win from avoiding the vtable indirection when
running/destructing the pass.
Use a unique_ptr with a custom deleter to store PassConcept instances.
This avoids new/delete size mismatches.
This reverts commit 6ae5965f26e53d7338a779a73725a597a4d136e5.
Added:
Modified:
llvm/include/llvm/Analysis/CGSCCPassManager.h
llvm/include/llvm/CodeGen/MachinePassManager.h
llvm/include/llvm/IR/PassManager.h
llvm/include/llvm/IR/PassManagerInternal.h
llvm/include/llvm/Transforms/Scalar/LoopPassManager.h
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/CGSCCPassManager.h b/llvm/include/llvm/Analysis/CGSCCPassManager.h
index e85c1ef9a90d2..f2ce8158675ae 100644
--- a/llvm/include/llvm/Analysis/CGSCCPassManager.h
+++ b/llvm/include/llvm/Analysis/CGSCCPassManager.h
@@ -319,7 +319,7 @@ class ModuleToPostOrderCGSCCPassAdaptor
detail::PassConcept<LazyCallGraph::SCC, CGSCCAnalysisManager,
LazyCallGraph &, CGSCCUpdateResult &>;
- explicit ModuleToPostOrderCGSCCPassAdaptor(std::unique_ptr<PassConceptT> Pass)
+ explicit ModuleToPostOrderCGSCCPassAdaptor(PassConceptT::unique_ptr Pass)
: Pass(std::move(Pass)) {}
ModuleToPostOrderCGSCCPassAdaptor(ModuleToPostOrderCGSCCPassAdaptor &&Arg)
@@ -347,7 +347,7 @@ class ModuleToPostOrderCGSCCPassAdaptor
}
private:
- std::unique_ptr<PassConceptT> Pass;
+ PassConceptT::unique_ptr Pass;
};
/// A function to deduce a function pass type and wrap it in the
@@ -358,11 +358,7 @@ createModuleToPostOrderCGSCCPassAdaptor(CGSCCPassT &&Pass) {
using PassModelT =
detail::PassModel<LazyCallGraph::SCC, CGSCCPassT, CGSCCAnalysisManager,
LazyCallGraph &, CGSCCUpdateResult &>;
- // Do not use make_unique, it causes too many template instantiations,
- // causing terrible compile times.
- return ModuleToPostOrderCGSCCPassAdaptor(
- std::unique_ptr<ModuleToPostOrderCGSCCPassAdaptor::PassConceptT>(
- new PassModelT(std::forward<CGSCCPassT>(Pass))));
+ return ModuleToPostOrderCGSCCPassAdaptor(PassModelT::create(std::move(Pass)));
}
/// A proxy from a \c FunctionAnalysisManager to an \c SCC.
@@ -447,7 +443,7 @@ class CGSCCToFunctionPassAdaptor
public:
using PassConceptT = detail::PassConcept<Function, FunctionAnalysisManager>;
- explicit CGSCCToFunctionPassAdaptor(std::unique_ptr<PassConceptT> Pass,
+ explicit CGSCCToFunctionPassAdaptor(typename PassConceptT::unique_ptr Pass,
bool EagerlyInvalidate, bool NoRerun)
: Pass(std::move(Pass)), EagerlyInvalidate(EagerlyInvalidate),
NoRerun(NoRerun) {}
@@ -490,7 +486,7 @@ class CGSCCToFunctionPassAdaptor
}
private:
- std::unique_ptr<PassConceptT> Pass;
+ PassConceptT::unique_ptr Pass;
bool EagerlyInvalidate;
bool NoRerun;
};
@@ -504,12 +500,8 @@ createCGSCCToFunctionPassAdaptor(FunctionPassT &&Pass,
bool NoRerun = false) {
using PassModelT =
detail::PassModel<Function, FunctionPassT, FunctionAnalysisManager>;
- // Do not use make_unique, it causes too many template instantiations,
- // causing terrible compile times.
- return CGSCCToFunctionPassAdaptor(
- std::unique_ptr<CGSCCToFunctionPassAdaptor::PassConceptT>(
- new PassModelT(std::forward<FunctionPassT>(Pass))),
- EagerlyInvalidate, NoRerun);
+ return CGSCCToFunctionPassAdaptor(PassModelT::create(std::move(Pass)),
+ EagerlyInvalidate, NoRerun);
}
// A marker to determine if function passes should be run on a function within a
@@ -547,7 +539,7 @@ class DevirtSCCRepeatedPass
detail::PassConcept<LazyCallGraph::SCC, CGSCCAnalysisManager,
LazyCallGraph &, CGSCCUpdateResult &>;
- explicit DevirtSCCRepeatedPass(std::unique_ptr<PassConceptT> Pass,
+ explicit DevirtSCCRepeatedPass(PassConceptT::unique_ptr Pass,
int MaxIterations)
: Pass(std::move(Pass)), MaxIterations(MaxIterations) {}
@@ -565,7 +557,7 @@ class DevirtSCCRepeatedPass
}
private:
- std::unique_ptr<PassConceptT> Pass;
+ PassConceptT::unique_ptr Pass;
int MaxIterations;
};
@@ -577,12 +569,8 @@ DevirtSCCRepeatedPass createDevirtSCCRepeatedPass(CGSCCPassT &&Pass,
using PassModelT =
detail::PassModel<LazyCallGraph::SCC, CGSCCPassT, CGSCCAnalysisManager,
LazyCallGraph &, CGSCCUpdateResult &>;
- // Do not use make_unique, it causes too many template instantiations,
- // causing terrible compile times.
- return DevirtSCCRepeatedPass(
- std::unique_ptr<DevirtSCCRepeatedPass::PassConceptT>(
- new PassModelT(std::forward<CGSCCPassT>(Pass))),
- MaxIterations);
+ return DevirtSCCRepeatedPass(PassModelT::create(std::move(Pass)),
+ MaxIterations);
}
// Clear out the debug logging macro.
diff --git a/llvm/include/llvm/CodeGen/MachinePassManager.h b/llvm/include/llvm/CodeGen/MachinePassManager.h
index 936e80e88192c..f4d5ab4d55b2d 100644
--- a/llvm/include/llvm/CodeGen/MachinePassManager.h
+++ b/llvm/include/llvm/CodeGen/MachinePassManager.h
@@ -194,8 +194,7 @@ class FunctionToMachineFunctionPassAdaptor
using PassConceptT =
detail::PassConcept<MachineFunction, MachineFunctionAnalysisManager>;
- explicit FunctionToMachineFunctionPassAdaptor(
- std::unique_ptr<PassConceptT> Pass)
+ explicit FunctionToMachineFunctionPassAdaptor(PassConceptT::unique_ptr Pass)
: Pass(std::move(Pass)) {}
/// Runs the function pass across every function in the function.
@@ -205,7 +204,7 @@ class FunctionToMachineFunctionPassAdaptor
function_ref<StringRef(StringRef)> MapClassName2PassName);
private:
- std::unique_ptr<PassConceptT> Pass;
+ PassConceptT::unique_ptr Pass;
};
template <typename MachineFunctionPassT>
@@ -213,11 +212,8 @@ FunctionToMachineFunctionPassAdaptor
createFunctionToMachineFunctionPassAdaptor(MachineFunctionPassT &&Pass) {
using PassModelT = detail::PassModel<MachineFunction, MachineFunctionPassT,
MachineFunctionAnalysisManager>;
- // Do not use make_unique, it causes too many template instantiations,
- // causing terrible compile times.
return FunctionToMachineFunctionPassAdaptor(
- std::unique_ptr<FunctionToMachineFunctionPassAdaptor::PassConceptT>(
- new PassModelT(std::forward<MachineFunctionPassT>(Pass))));
+ PassModelT::create(std::move(Pass)));
}
template <>
diff --git a/llvm/include/llvm/IR/PassManager.h b/llvm/include/llvm/IR/PassManager.h
index 22e02853bc567..adb375f4f837a 100644
--- a/llvm/include/llvm/IR/PassManager.h
+++ b/llvm/include/llvm/IR/PassManager.h
@@ -220,10 +220,7 @@ class PassManager : public RequiredPassInfoMixin<
addPass(PassT &&Pass) {
using PassModelT =
detail::PassModel<IRUnitT, PassT, AnalysisManagerT, ExtraArgTs...>;
- // Do not use make_unique or emplace_back, they cause too many template
- // instantiations, causing terrible compile times.
- Passes.push_back(std::unique_ptr<PassConceptT>(
- new PassModelT(std::forward<PassT>(Pass))));
+ Passes.push_back(PassModelT::create(std::move(Pass)));
}
/// When adding a pass manager pass that has the same type as this pass
@@ -245,7 +242,7 @@ class PassManager : public RequiredPassInfoMixin<
using PassConceptT =
detail::PassConcept<IRUnitT, AnalysisManagerT, ExtraArgTs...>;
- std::vector<std::unique_ptr<PassConceptT>> Passes;
+ std::vector<typename PassConceptT::unique_ptr> Passes;
};
template <typename IRUnitT>
@@ -875,7 +872,7 @@ class ModuleToFunctionPassAdaptor
public:
using PassConceptT = detail::PassConcept<Function, FunctionAnalysisManager>;
- explicit ModuleToFunctionPassAdaptor(std::unique_ptr<PassConceptT> Pass,
+ explicit ModuleToFunctionPassAdaptor(PassConceptT::unique_ptr Pass,
bool EagerlyInvalidate)
: Pass(std::move(Pass)), EagerlyInvalidate(EagerlyInvalidate) {}
@@ -886,7 +883,7 @@ class ModuleToFunctionPassAdaptor
function_ref<StringRef(StringRef)> MapClassName2PassName);
private:
- std::unique_ptr<PassConceptT> Pass;
+ PassConceptT::unique_ptr Pass;
bool EagerlyInvalidate;
};
@@ -898,12 +895,8 @@ createModuleToFunctionPassAdaptor(FunctionPassT &&Pass,
bool EagerlyInvalidate = false) {
using PassModelT =
detail::PassModel<Function, FunctionPassT, FunctionAnalysisManager>;
- // Do not use make_unique, it causes too many template instantiations,
- // causing terrible compile times.
- return ModuleToFunctionPassAdaptor(
- std::unique_ptr<ModuleToFunctionPassAdaptor::PassConceptT>(
- new PassModelT(std::forward<FunctionPassT>(Pass))),
- EagerlyInvalidate);
+ return ModuleToFunctionPassAdaptor(PassModelT::create(std::move(Pass)),
+ EagerlyInvalidate);
}
/// A utility pass template to force an analysis result to be available.
diff --git a/llvm/include/llvm/IR/PassManagerInternal.h b/llvm/include/llvm/IR/PassManagerInternal.h
index f894f91cf1783..376dcd7d3af62 100644
--- a/llvm/include/llvm/IR/PassManagerInternal.h
+++ b/llvm/include/llvm/IR/PassManagerInternal.h
@@ -34,68 +34,115 @@ class PreservedAnalyses;
// Implementation details of the pass manager interfaces.
namespace detail {
-/// Template for the abstract base class used to dispatch
-/// polymorphically over pass objects.
+/// Template for the abstract base class used to dispatch over pass objects.
+/// This doesn't use virtual functions to avoid vtables, which cost a fair
+/// amount of storage that needs to be relocated in PIC builds and add an extra
+/// indirection on dispatch.
template <typename IRUnitT, typename AnalysisManagerT, typename... ExtraArgTs>
-struct PassConcept {
- PassConcept() = default;
+class PassConcept {
+private:
+ using DestroyTy = void (*)(PassConcept &);
+ using RunTy = PreservedAnalyses (*)(PassConcept &, IRUnitT &,
+ AnalysisManagerT &, ExtraArgTs...);
+ using PrintPipelineTy =
+ void (*)(PassConcept &, raw_ostream &,
+ function_ref<StringRef(StringRef)> MapClassName2PassName);
- // Boiler plate necessary for the container of derived classes.
- virtual ~PassConcept() = default;
+public:
+ struct Deleter {
+ void operator()(PassConcept *P) { P->Destroy(*P); }
+ };
+
+ using unique_ptr = std::unique_ptr<PassConcept, Deleter>;
+
+private:
+ StringRef Name;
+ bool IsRequired;
+
+ DestroyTy Destroy;
+ RunTy Run;
+ PrintPipelineTy PrintPipeline;
+
+protected:
+ PassConcept(StringRef Name, bool IsRequired, DestroyTy Destroy, RunTy Run,
+ PrintPipelineTy PrintPipeline)
+ : Name(Name), IsRequired(IsRequired), Destroy(Destroy), Run(Run),
+ PrintPipeline(PrintPipeline) {}
+
+ // Note: this is intentionally not public to catch uses of delete and
+ // unique_ptr<PassConcept>.
+ void operator delete(void *P) { ::operator delete(P); }
+public:
// Passes are immovable.
PassConcept(const PassConcept &) = delete;
PassConcept &operator=(const PassConcept &) = delete;
- /// The polymorphic API which runs the pass over a given IR entity.
- ///
- /// Note that actual pass object can omit the analysis manager argument if
- /// desired. Also that the analysis manager may be null if there is no
- /// analysis manager in the pass pipeline.
- virtual PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM,
- ExtraArgTs... ExtraArgs) = 0;
-
- virtual void
- printPipeline(raw_ostream &OS,
- function_ref<StringRef(StringRef)> MapClassName2PassName) = 0;
- /// Polymorphic method to access the name of a pass.
- virtual StringRef name() const = 0;
+ /// Run the pass.
+ PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM,
+ ExtraArgTs... ExtraArgs) {
+ return Run(*this, IR, AM, std::forward<ExtraArgTs>(ExtraArgs)...);
+ }
- /// Polymorphic method to let a pass optionally exempted from skipping by
+ void printPipeline(raw_ostream &OS,
+ function_ref<StringRef(StringRef)> MapClassName2PassName) {
+ PrintPipeline(*this, OS, MapClassName2PassName);
+ }
+
+ /// Get name of a pass.
+ StringRef name() const { return Name; }
+
+ /// Indicate whether a pass can optionally be exempted from skipping by
/// PassInstrumentation.
/// To opt-in, pass should implement `static bool isRequired()`, or inherit
/// from `RequiredPassInfoMixin` or `OptionalPassInfoMixin`.
/// It's no-op to have `isRequired` always return false since that is the
/// default.
- virtual bool isRequired() const = 0;
+ bool isRequired() const { return IsRequired; }
};
-/// A template wrapper used to implement the polymorphic API.
+/// A template wrapper used to implement PassConcept.
///
/// Can be instantiated for any object which provides a \c run method accepting
-/// an \c IRUnitT& and an \c AnalysisManager<IRUnit>&. It requires the pass to
-/// be a copyable object.
+/// an \c IRUnitT& and an \c AnalysisManager<IRUnit>&.
template <typename IRUnitT, typename PassT, typename AnalysisManagerT,
typename... ExtraArgTs>
-struct PassModel final : PassConcept<IRUnitT, AnalysisManagerT, ExtraArgTs...> {
- explicit PassModel(PassT Pass) : Pass(std::move(Pass)) {}
+class PassModel final
+ : public PassConcept<IRUnitT, AnalysisManagerT, ExtraArgTs...> {
+private:
+ using PassConceptT = PassConcept<IRUnitT, AnalysisManagerT, ExtraArgTs...>;
- PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM,
- ExtraArgTs... ExtraArgs) override {
- return Pass.run(IR, AM, ExtraArgs...);
+ PassT Pass;
+
+ static PassT &getPass(PassConceptT &Self) {
+ return static_cast<PassModel &>(Self).Pass;
}
- void printPipeline(
- raw_ostream &OS,
- function_ref<StringRef(StringRef)> MapClassName2PassName) override {
- Pass.printPipeline(OS, MapClassName2PassName);
+ static void destroyImpl(PassConceptT &Self) {
+ delete static_cast<PassModel *>(&Self);
}
- StringRef name() const override { return PassT::name(); }
+ static PreservedAnalyses runImpl(PassConceptT &Self, IRUnitT &IR,
+ AnalysisManagerT &AM,
+ ExtraArgTs... ExtraArgs) {
+ return getPass(Self).run(IR, AM, ExtraArgs...);
+ }
- bool isRequired() const override { return PassT::isRequired(); }
+ static void
+ printPipelineImpl(PassConceptT &Self, raw_ostream &OS,
+ function_ref<StringRef(StringRef)> MapClassName2PassName) {
+ getPass(Self).printPipeline(OS, MapClassName2PassName);
+ }
- PassT Pass;
+ explicit PassModel(PassT &&Pass)
+ : PassConceptT(PassT::name(), PassT::isRequired(), destroyImpl, runImpl,
+ printPipelineImpl),
+ Pass(std::move(Pass)) {}
+
+public:
+ static typename PassConceptT::unique_ptr create(PassT &&Pass) {
+ return typename PassConceptT::unique_ptr(new PassModel(std::move(Pass)));
+ }
};
/// Abstract concept of an analysis result.
diff --git a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h
index 973d8a3cb1008..bb882096142f7 100644
--- a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h
+++ b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h
@@ -111,19 +111,13 @@ class PassManager<Loop, LoopAnalysisManager, LoopStandardAnalysisResults &,
detail::PassModel<Loop, PassT, LoopAnalysisManager,
LoopStandardAnalysisResults &, LPMUpdater &>;
IsLoopNestPass.push_back(false);
- // Do not use make_unique or emplace_back, they cause too many template
- // instantiations, causing terrible compile times.
- LoopPasses.push_back(std::unique_ptr<LoopPassConceptT>(
- new LoopPassModelT(std::forward<PassT>(Pass))));
+ LoopPasses.push_back(LoopPassModelT::create(std::move(Pass)));
} else {
using LoopNestPassModelT =
detail::PassModel<LoopNest, PassT, LoopAnalysisManager,
LoopStandardAnalysisResults &, LPMUpdater &>;
IsLoopNestPass.push_back(true);
- // Do not use make_unique or emplace_back, they cause too many template
- // instantiations, causing terrible compile times.
- LoopNestPasses.push_back(std::unique_ptr<LoopNestPassConceptT>(
- new LoopNestPassModelT(std::forward<PassT>(Pass))));
+ LoopNestPasses.push_back(LoopNestPassModelT::create(std::move(Pass)));
}
}
@@ -143,8 +137,8 @@ class PassManager<Loop, LoopAnalysisManager, LoopStandardAnalysisResults &,
// BitVector that identifies whether the passes are loop passes or loop-nest
// passes (true for loop-nest passes).
BitVector IsLoopNestPass;
- std::vector<std::unique_ptr<LoopPassConceptT>> LoopPasses;
- std::vector<std::unique_ptr<LoopNestPassConceptT>> LoopNestPasses;
+ std::vector<LoopPassConceptT::unique_ptr> LoopPasses;
+ std::vector<LoopNestPassConceptT::unique_ptr> LoopNestPasses;
/// Run either a loop pass or a loop-nest pass. Returns `std::nullopt` if
/// PassInstrumentation's BeforePass returns false. Otherwise, returns the
@@ -400,7 +394,7 @@ class FunctionToLoopPassAdaptor
detail::PassConcept<Loop, LoopAnalysisManager,
LoopStandardAnalysisResults &, LPMUpdater &>;
- explicit FunctionToLoopPassAdaptor(std::unique_ptr<PassConceptT> Pass,
+ explicit FunctionToLoopPassAdaptor(PassConceptT::unique_ptr Pass,
bool UseMemorySSA = false,
bool LoopNestMode = false)
: Pass(std::move(Pass)), UseMemorySSA(UseMemorySSA),
@@ -418,7 +412,7 @@ class FunctionToLoopPassAdaptor
bool isLoopNestMode() const { return LoopNestMode; }
private:
- std::unique_ptr<PassConceptT> Pass;
+ PassConceptT::unique_ptr Pass;
FunctionPassManager LoopCanonicalizationFPM;
@@ -440,24 +434,16 @@ createFunctionToLoopPassAdaptor(LoopPassT &&Pass, bool UseMemorySSA = false) {
using PassModelT =
detail::PassModel<Loop, LoopPassT, LoopAnalysisManager,
LoopStandardAnalysisResults &, LPMUpdater &>;
- // Do not use make_unique, it causes too many template instantiations,
- // causing terrible compile times.
- return FunctionToLoopPassAdaptor(
- std::unique_ptr<FunctionToLoopPassAdaptor::PassConceptT>(
- new PassModelT(std::forward<LoopPassT>(Pass))),
- UseMemorySSA, false);
+ return FunctionToLoopPassAdaptor(PassModelT::create(std::move(Pass)),
+ UseMemorySSA, false);
} else {
LoopPassManager LPM;
- LPM.addPass(std::forward<LoopPassT>(Pass));
+ LPM.addPass(std::move(Pass));
using PassModelT =
detail::PassModel<Loop, LoopPassManager, LoopAnalysisManager,
LoopStandardAnalysisResults &, LPMUpdater &>;
- // Do not use make_unique, it causes too many template instantiations,
- // causing terrible compile times.
- return FunctionToLoopPassAdaptor(
- std::unique_ptr<FunctionToLoopPassAdaptor::PassConceptT>(
- new PassModelT(std::move(LPM))),
- UseMemorySSA, true);
+ return FunctionToLoopPassAdaptor(PassModelT::create(std::move(LPM)),
+ UseMemorySSA, true);
}
}
@@ -473,12 +459,8 @@ createFunctionToLoopPassAdaptor<LoopPassManager>(LoopPassManager &&LPM,
detail::PassModel<Loop, LoopPassManager, LoopAnalysisManager,
LoopStandardAnalysisResults &, LPMUpdater &>;
bool LoopNestMode = (LPM.getNumLoopPasses() == 0);
- // Do not use make_unique, it causes too many template instantiations,
- // causing terrible compile times.
- return FunctionToLoopPassAdaptor(
- std::unique_ptr<FunctionToLoopPassAdaptor::PassConceptT>(
- new PassModelT(std::move(LPM))),
- UseMemorySSA, LoopNestMode);
+ return FunctionToLoopPassAdaptor(PassModelT::create(std::move(LPM)),
+ UseMemorySSA, LoopNestMode);
}
/// Pass for printing a loop's contents as textual IR.
More information about the llvm-commits
mailing list