[llvm] [LLVM] Add flatten function attribute to LLVM IR and implement recursive inlining in AlwaysInliner (PR #174899)

Wei Wang via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 15 09:36:27 PST 2026


================
@@ -30,21 +31,206 @@ using namespace llvm;
 
 namespace {
 
+class InlinerHelper {
+  Module &M;
+  FunctionAnalysisManager *FAM;
+  function_ref<AssumptionCache &(Function &)> GetAssumptionCache;
+  function_ref<AAResults &(Function &)> GetAAR;
+  bool InsertLifetime;
+
+  SmallSetVector<Function *, 16> MaybeInlinedFunctions;
+  InlineFunctionInfo IFI;
+
+public:
+  InlinerHelper(Module &M, ProfileSummaryInfo &PSI,
+                FunctionAnalysisManager *FAM,
+                function_ref<AssumptionCache &(Function &)> GetAssumptionCache,
+                function_ref<AAResults &(Function &)> GetAAR,
+                bool InsertLifetime)
+      : M(M), FAM(FAM), GetAssumptionCache(GetAssumptionCache), GetAAR(GetAAR),
+        InsertLifetime(InsertLifetime), IFI(GetAssumptionCache, &PSI) {}
+
+  bool canInline(Function &F) {
+    return !F.isPresplitCoroutine() && !F.isDeclaration() &&
+           isInlineViable(F).isSuccess();
+  }
+
+  bool tryInline(CallBase &CB, StringRef InlignReason) {
+    IFI.reset();
+    Function &Callee = *CB.getCalledFunction();
+    Function *Caller = CB.getCaller();
+    OptimizationRemarkEmitter ORE(Caller);
+    DebugLoc DLoc = CB.getDebugLoc();
+    BasicBlock *Block = CB.getParent();
+
+    InlineResult Res = InlineFunction(CB, IFI, /*MergeAttributes=*/true,
+                                      &GetAAR(Callee), InsertLifetime);
+    if (!Res.isSuccess()) {
+      ORE.emit([&]() {
+        return OptimizationRemarkMissed(DEBUG_TYPE, "NotInlined", DLoc, Block)
+               << "'" << ore::NV("Callee", &Callee) << "' is not inlined into '"
+               << ore::NV("Caller", Caller)
+               << "': " << ore::NV("Reason", Res.getFailureReason());
+      });
+      return false;
+    }
+
+    emitInlinedIntoBasedOnCost(ORE, DLoc, Block, Callee, *Caller,
+                               InlineCost::getAlways(InlignReason.data()),
+                               /*ForProfileContext=*/false, DEBUG_TYPE);
+    if (FAM)
+      FAM->invalidate(*Caller, PreservedAnalyses::none());
+    return true;
+  }
+
+  void addNewCallsToWorklist(
+      SmallVectorImpl<std::pair<CallBase *, int>> &Worklist,
+      int InlineHistoryID,
+      SmallVectorImpl<std::pair<Function *, int>> &InlineHistory,
+      Function *InlinedCallee) {
+    if (IFI.InlinedCallSites.empty())
+      return;
+    int NewHistoryID = InlineHistory.size();
+    InlineHistory.push_back({InlinedCallee, InlineHistoryID});
+    for (CallBase *CB : IFI.InlinedCallSites)
+      Worklist.push_back({CB, NewHistoryID});
+  }
+
+  void addToMaybeInlinedFunctions(Function &F) {
+    MaybeInlinedFunctions.insert(&F);
+  }
+
+  bool postInlinerCleanup() {
+    SmallVector<Function *, 16> InlinedComdatFunctions;
+    bool Changed = false;
+    for (Function *F : MaybeInlinedFunctions) {
+      F->removeDeadConstantUsers();
+      if (F->hasFnAttribute(Attribute::AlwaysInline) &&
+          F->isDefTriviallyDead()) {
+        if (F->hasComdat()) {
+          InlinedComdatFunctions.push_back(F);
+        } else {
+          if (FAM)
+            FAM->clear(*F, F->getName());
+          M.getFunctionList().erase(F);
+          Changed = true;
+        }
+      }
+    }
+    if (!InlinedComdatFunctions.empty()) {
+      // Now we just have the comdat functions. Filter out the ones whose
+      // comdats are not actually dead.
+      filterDeadComdatFunctions(InlinedComdatFunctions);
+      // The remaining functions are actually dead.
+      for (Function *F : InlinedComdatFunctions) {
+        if (FAM)
+          FAM->clear(*F, F->getName());
+        M.getFunctionList().erase(F);
+        Changed = true;
+      }
+    }
+    return Changed;
+  }
+};
+
+static bool inlineHistoryIncludes(
+    Function *F, int InlineHistoryID,
+    const SmallVectorImpl<std::pair<Function *, int>> &InlineHistory) {
+  while (InlineHistoryID != -1) {
----------------
apolloww wrote:

I mean if the function is first seen, there can't be a cycle, so it's a fast path to return "false". 

Since it's used in inliner and doesn't cause issue, I think it's fine that we keep using it here. 

https://github.com/llvm/llvm-project/pull/174899


More information about the llvm-commits mailing list