Skip to content

Conversation

@arsenm
Copy link
Contributor

@arsenm arsenm commented Dec 2, 2025

GlobalISel: Use LibcallLoweringInfo analysis in legalizer

This is mostly boilerplate to move various freestanding utility
functions into LegalizerHelper. LibcallLoweringInfo is currently
optional, mostly because threading it through assorted other
uses of LegalizerHelper is more difficult.

I had a lot of trouble getting this to work in the legacy pass
manager with setRequiresCodeGenSCCOrder, and am not happy with the
result. A sub-pass manager is introduced and this is invalidated,
so we're re-computing this unnecessarily.

Old pass manager hacking

The libcall lowering decisions should be program dependent,
depending on the current module's RuntimeLibcallInfo. We need
another related analysis derived from that plus the current
function's subtarget to provide concrete lowering decisions.

This takes on a somewhat unusual form. It's a Module analysis,
with a lookup keyed on the subtarget. This is a separate module
analysis from RuntimeLibraryAnalysis to avoid that depending on
codegen. It's not a function pass to avoid depending on any
particular function, to avoid repeated subtarget map lookups in
most of the use passes, and to avoid any recomputation in the
common case of one subtarget (and keeps it reusable across
repeated compilations).

This also switches ExpandFp and PreISelIntrinsicLowering as
a sample function and module pass. Note this is not yet wired
up to SelectionDAG, which is still using the LibcallLoweringInfo
constructed inside of TargetLowering.
@llvmbot
Copy link
Member

llvmbot commented Dec 2, 2025

@llvm/pr-subscribers-llvm-analysis
@llvm/pr-subscribers-backend-arm
@llvm/pr-subscribers-llvm-globalisel
@llvm/pr-subscribers-backend-spir-v
@llvm/pr-subscribers-backend-amdgpu
@llvm/pr-subscribers-backend-risc-v
@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-backend-aarch64

Author: Matt Arsenault (arsenm)

Changes

GlobalISel: Use LibcallLoweringInfo analysis in legalizer

This is mostly boilerplate to move various freestanding utility
functions into LegalizerHelper. LibcallLoweringInfo is currently
optional, mostly because threading it through assorted other
uses of LegalizerHelper is more difficult.

I had a lot of trouble getting this to work in the legacy pass
manager with setRequiresCodeGenSCCOrder, and am not happy with the
result. A sub-pass manager is introduced and this is invalidated,
so we're re-computing this unnecessarily.

Old pass manager hacking


Patch is 49.98 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/170328.diff

15 Files Affected:

  • (modified) llvm/include/llvm/Analysis/RuntimeLibcallInfo.h (+2)
  • (modified) llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h (+6-5)
  • (modified) llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h (+43-30)
  • (modified) llvm/include/llvm/CodeGen/LibcallLoweringInfo.h (+11-5)
  • (modified) llvm/include/llvm/CodeGen/Passes.h (+2)
  • (modified) llvm/lib/CodeGen/ExpandFp.cpp (+1-1)
  • (modified) llvm/lib/CodeGen/GlobalISel/Legalizer.cpp (+16-9)
  • (modified) llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp (+71-78)
  • (modified) llvm/lib/CodeGen/LibcallLoweringInfo.cpp (+6-3)
  • (modified) llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp (+1-1)
  • (modified) llvm/lib/CodeGen/TargetPassConfig.cpp (+2)
  • (modified) llvm/lib/Target/ARM/ARMLegalizerInfo.cpp (+9-9)
  • (modified) llvm/unittests/CodeGen/GlobalISel/GISelMITest.h (+4)
  • (modified) llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp (+32-31)
  • (modified) llvm/unittests/CodeGen/GlobalISel/LegalizerTest.cpp (+3-3)
diff --git a/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h b/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h
index 2d31c8aa6301b..b0f398d6e2b28 100644
--- a/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h
+++ b/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h
@@ -32,6 +32,8 @@ class LLVM_ABI RuntimeLibraryAnalysis
   LLVM_ABI RTLIB::RuntimeLibcallsInfo run(const Module &M,
                                           ModuleAnalysisManager &);
 
+  operator bool() const { return LibcallsInfo.has_value(); }
+
 private:
   friend AnalysisInfoMixin<RuntimeLibraryAnalysis>;
   LLVM_ABI static AnalysisKey Key;
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h b/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h
index a94daa202d856..f8e629bc50a90 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h
@@ -33,6 +33,7 @@ class LegalizerInfo;
 class MachineIRBuilder;
 class MachineInstr;
 class GISelChangeObserver;
+class LibcallLoweringInfo;
 class LostDebugLocObserver;
 
 class LLVM_ABI Legalizer : public MachineFunctionPass {
@@ -70,11 +71,11 @@ class LLVM_ABI Legalizer : public MachineFunctionPass {
 
   bool runOnMachineFunction(MachineFunction &MF) override;
 
-  static MFResult
-  legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
-                          ArrayRef<GISelChangeObserver *> AuxObservers,
-                          LostDebugLocObserver &LocObserver,
-                          MachineIRBuilder &MIRBuilder, GISelValueTracking *VT);
+  static MFResult legalizeMachineFunction(
+      MachineFunction &MF, const LegalizerInfo &LI,
+      ArrayRef<GISelChangeObserver *> AuxObservers,
+      LostDebugLocObserver &LocObserver, MachineIRBuilder &MIRBuilder,
+      const LibcallLoweringInfo *Libcalls, GISelValueTracking *VT);
 };
 } // End namespace llvm.
 
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
index a458cbd94ccb1..11f734ec64eb1 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
@@ -59,7 +59,10 @@ class LegalizerHelper {
   MachineRegisterInfo &MRI;
   const LegalizerInfo &LI;
   const TargetLowering &TLI;
-  GISelValueTracking *VT;
+
+  // FIXME: Should probably make Libcalls mandatory
+  const LibcallLoweringInfo *Libcalls = nullptr;
+  GISelValueTracking *VT = nullptr;
 
 public:
   enum LegalizeResult {
@@ -78,12 +81,15 @@ class LegalizerHelper {
   /// Expose LegalizerInfo so the clients can re-use.
   const LegalizerInfo &getLegalizerInfo() const { return LI; }
   const TargetLowering &getTargetLowering() const { return TLI; }
+  const LibcallLoweringInfo *getLibcallLoweringInfo() { return Libcalls; }
   GISelValueTracking *getValueTracking() const { return VT; }
 
   LLVM_ABI LegalizerHelper(MachineFunction &MF, GISelChangeObserver &Observer,
-                           MachineIRBuilder &B);
+                           MachineIRBuilder &B,
+                           const LibcallLoweringInfo *Libcalls = nullptr);
   LLVM_ABI LegalizerHelper(MachineFunction &MF, const LegalizerInfo &LI,
                            GISelChangeObserver &Observer, MachineIRBuilder &B,
+                           const LibcallLoweringInfo *Libcalls = nullptr,
                            GISelValueTracking *VT = nullptr);
 
   /// Replace \p MI by a sequence of legal instructions that can implement the
@@ -178,6 +184,37 @@ class LegalizerHelper {
   /// def by inserting a G_BITCAST from \p CastTy
   LLVM_ABI void bitcastDst(MachineInstr &MI, LLT CastTy, unsigned OpIdx);
 
+  // Useful for libcalls where all operands have the same type.
+  LLVM_ABI LegalizeResult
+  simpleLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder, unsigned Size,
+                Type *OpType, LostDebugLocObserver &LocObserver) const;
+
+  LLVM_ABI LegalizeResult conversionLibcall(MachineInstr &MI, Type *ToType,
+                                            Type *FromType,
+                                            LostDebugLocObserver &LocObserver,
+                                            bool IsSigned = false) const;
+
+  /// Helper function that creates a libcall to the given \p Name using the
+  /// given calling convention \p CC.
+  LLVM_ABI LegalizeResult createLibcall(const char *Name,
+                                        const CallLowering::ArgInfo &Result,
+                                        ArrayRef<CallLowering::ArgInfo> Args,
+                                        CallingConv::ID CC,
+                                        LostDebugLocObserver &LocObserver,
+                                        MachineInstr *MI = nullptr) const;
+
+  /// Helper function that creates the given libcall.
+  LLVM_ABI LegalizeResult createLibcall(RTLIB::Libcall Libcall,
+                                        const CallLowering::ArgInfo &Result,
+                                        ArrayRef<CallLowering::ArgInfo> Args,
+                                        LostDebugLocObserver &LocObserver,
+                                        MachineInstr *MI = nullptr) const;
+
+  /// Create a libcall to memcpy et al.
+  LLVM_ABI LegalizeResult
+  createMemLibcall(MachineRegisterInfo &MRI, MachineInstr &MI,
+                   LostDebugLocObserver &LocObserver) const;
+
 private:
   LegalizeResult
   widenScalarMergeValues(MachineInstr &MI, unsigned TypeIdx, LLT WideTy);
@@ -278,17 +315,13 @@ class LegalizerHelper {
                               bool IsVolatile);
 
   // Implements floating-point environment read/write via library function call.
-  LegalizeResult createGetStateLibcall(MachineIRBuilder &MIRBuilder,
-                                       MachineInstr &MI,
+  LegalizeResult createGetStateLibcall(MachineInstr &MI,
                                        LostDebugLocObserver &LocObserver);
-  LegalizeResult createSetStateLibcall(MachineIRBuilder &MIRBuilder,
-                                       MachineInstr &MI,
+  LegalizeResult createSetStateLibcall(MachineInstr &MI,
                                        LostDebugLocObserver &LocObserver);
-  LegalizeResult createResetStateLibcall(MachineIRBuilder &MIRBuilder,
-                                         MachineInstr &MI,
+  LegalizeResult createResetStateLibcall(MachineInstr &MI,
                                          LostDebugLocObserver &LocObserver);
-  LegalizeResult createFCMPLibcall(MachineIRBuilder &MIRBuilder,
-                                   MachineInstr &MI,
+  LegalizeResult createFCMPLibcall(MachineInstr &MI,
                                    LostDebugLocObserver &LocObserver);
 
   MachineInstrBuilder
@@ -539,26 +572,6 @@ class LegalizerHelper {
   LLVM_ABI LegalizeResult lowerVAArg(MachineInstr &MI);
 };
 
-/// Helper function that creates a libcall to the given \p Name using the given
-/// calling convention \p CC.
-LLVM_ABI LegalizerHelper::LegalizeResult
-createLibcall(MachineIRBuilder &MIRBuilder, const char *Name,
-              const CallLowering::ArgInfo &Result,
-              ArrayRef<CallLowering::ArgInfo> Args, CallingConv::ID CC,
-              LostDebugLocObserver &LocObserver, MachineInstr *MI = nullptr);
-
-/// Helper function that creates the given libcall.
-LLVM_ABI LegalizerHelper::LegalizeResult
-createLibcall(MachineIRBuilder &MIRBuilder, RTLIB::Libcall Libcall,
-              const CallLowering::ArgInfo &Result,
-              ArrayRef<CallLowering::ArgInfo> Args,
-              LostDebugLocObserver &LocObserver, MachineInstr *MI = nullptr);
-
-/// Create a libcall to memcpy et al.
-LLVM_ABI LegalizerHelper::LegalizeResult
-createMemLibcall(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI,
-                 MachineInstr &MI, LostDebugLocObserver &LocObserver);
-
 } // End namespace llvm.
 
 #endif
diff --git a/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h b/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h
index 3e0137710e8eb..47c0dd315d3b5 100644
--- a/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h
+++ b/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h
@@ -10,11 +10,12 @@
 #define LLVM_CODEGEN_LIBCALLLOWERINGINFO_H
 
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/Analysis/RuntimeLibcallInfo.h"
 #include "llvm/IR/RuntimeLibcalls.h"
 #include "llvm/Pass.h"
 
 namespace llvm {
-
+class RuntimeLibraryInfoWrapper;
 class TargetSubtargetInfo;
 class TargetMachine;
 
@@ -97,6 +98,8 @@ class LibcallLoweringModuleAnalysisResult {
     LoweringMap.clear();
   }
 
+  operator bool() const { return RTLCI != nullptr; }
+
   LLVM_ABI bool invalidate(Module &, const PreservedAnalyses &,
                            ModuleAnalysisManager::Invalidator &);
 
@@ -122,21 +125,24 @@ class LibcallLoweringModuleAnalysis
 
 class LLVM_ABI LibcallLoweringInfoWrapper : public ImmutablePass {
   LibcallLoweringModuleAnalysisResult Result;
+  RuntimeLibraryInfoWrapper *RuntimeLibcallsWrapper = nullptr;
 
 public:
   static char ID;
   LibcallLoweringInfoWrapper();
 
   const LibcallLoweringInfo &
-  getLibcallLowering(const TargetSubtargetInfo &Subtarget) const {
-    return Result.getLibcallLowering(Subtarget);
+  getLibcallLowering(const Module &M, const TargetSubtargetInfo &Subtarget) {
+    return getResult(M).getLibcallLowering(Subtarget);
   }
 
-  const LibcallLoweringModuleAnalysisResult &getResult() const {
+  const LibcallLoweringModuleAnalysisResult &getResult(const Module &M) {
+    if (!Result)
+      Result.init(&RuntimeLibcallsWrapper->getRTLCI(M));
     return Result;
   }
 
-  bool doInitialization(Module &M) override;
+  void initializePass() override;
   void getAnalysisUsage(AnalysisUsage &AU) const override;
   void releaseMemory() override;
 };
diff --git a/llvm/include/llvm/CodeGen/Passes.h b/llvm/include/llvm/CodeGen/Passes.h
index a8525554b142e..168be429e13a6 100644
--- a/llvm/include/llvm/CodeGen/Passes.h
+++ b/llvm/include/llvm/CodeGen/Passes.h
@@ -487,6 +487,8 @@ LLVM_ABI FunctionPass *createInterleavedLoadCombinePass();
 ///
 LLVM_ABI ModulePass *createLowerEmuTLSPass();
 
+LLVM_ABI ModulePass *createLibcallLoweringInfoWrapper();
+
 /// This pass lowers the \@llvm.load.relative and \@llvm.objc.* intrinsics to
 /// instructions.  This is unsafe to do earlier because a pass may combine the
 /// constant initializer into the load, which may result in an overflowing
diff --git a/llvm/lib/CodeGen/ExpandFp.cpp b/llvm/lib/CodeGen/ExpandFp.cpp
index 13ed4846d2bf7..7d333be7ed6fc 100644
--- a/llvm/lib/CodeGen/ExpandFp.cpp
+++ b/llvm/lib/CodeGen/ExpandFp.cpp
@@ -1097,7 +1097,7 @@ class ExpandFpLegacyPass : public FunctionPass {
 
     const LibcallLoweringInfo &Libcalls =
         getAnalysis<LibcallLoweringInfoWrapper>().getLibcallLowering(
-            *Subtarget);
+            *F.getParent(), *Subtarget);
 
     if (OptLevel != CodeGenOptLevel::None && !F.hasOptNone())
       AC = &getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
diff --git a/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp b/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp
index 0f0656aaa4f45..711f7a0b5c35b 100644
--- a/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp
@@ -73,6 +73,7 @@ char Legalizer::ID = 0;
 INITIALIZE_PASS_BEGIN(Legalizer, DEBUG_TYPE,
                       "Legalize the Machine IR a function's Machine IR", false,
                       false)
+INITIALIZE_PASS_DEPENDENCY(LibcallLoweringInfoWrapper)
 INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
 INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(GISelValueTrackingAnalysisLegacy)
@@ -83,6 +84,7 @@ INITIALIZE_PASS_END(Legalizer, DEBUG_TYPE,
 Legalizer::Legalizer() : MachineFunctionPass(ID) { }
 
 void Legalizer::getAnalysisUsage(AnalysisUsage &AU) const {
+  AU.addRequired<LibcallLoweringInfoWrapper>();
   AU.addRequired<TargetPassConfig>();
   AU.addRequired<GISelCSEAnalysisWrapperPass>();
   AU.addPreserved<GISelCSEAnalysisWrapperPass>();
@@ -172,12 +174,11 @@ class LegalizerWorkListManager : public GISelChangeObserver {
 };
 } // namespace
 
-Legalizer::MFResult
-Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
-                                   ArrayRef<GISelChangeObserver *> AuxObservers,
-                                   LostDebugLocObserver &LocObserver,
-                                   MachineIRBuilder &MIRBuilder,
-                                   GISelValueTracking *VT) {
+Legalizer::MFResult Legalizer::legalizeMachineFunction(
+    MachineFunction &MF, const LegalizerInfo &LI,
+    ArrayRef<GISelChangeObserver *> AuxObservers,
+    LostDebugLocObserver &LocObserver, MachineIRBuilder &MIRBuilder,
+    const LibcallLoweringInfo *Libcalls, GISelValueTracking *VT) {
   MIRBuilder.setMF(MF);
   MachineRegisterInfo &MRI = MF.getRegInfo();
 
@@ -216,7 +217,7 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
   // Now install the observer as the delegate to MF.
   // This will keep all the observers notified about new insertions/deletions.
   RAIIMFObsDelInstaller Installer(MF, WrapperObserver);
-  LegalizerHelper Helper(MF, LI, WrapperObserver, MIRBuilder, VT);
+  LegalizerHelper Helper(MF, LI, WrapperObserver, MIRBuilder, Libcalls, VT);
   LegalizationArtifactCombiner ArtCombiner(MIRBuilder, MRI, LI, VT);
   bool Changed = false;
   SmallVector<MachineInstr *, 128> RetryList;
@@ -339,13 +340,19 @@ bool Legalizer::runOnMachineFunction(MachineFunction &MF) {
   if (VerifyDebugLocs > DebugLocVerifyLevel::None)
     AuxObservers.push_back(&LocObserver);
 
+  const TargetSubtargetInfo &Subtarget = MF.getSubtarget();
+
+  const LibcallLoweringInfo &Libcalls =
+      getAnalysis<LibcallLoweringInfoWrapper>().getLibcallLowering(
+          *MF.getFunction().getParent(), Subtarget);
+
   // This allows Known Bits Analysis in the legalizer.
   GISelValueTracking *VT =
       &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
 
-  const LegalizerInfo &LI = *MF.getSubtarget().getLegalizerInfo();
+  const LegalizerInfo &LI = *Subtarget.getLegalizerInfo();
   MFResult Result = legalizeMachineFunction(MF, LI, AuxObservers, LocObserver,
-                                            *MIRBuilder, VT);
+                                            *MIRBuilder, &Libcalls, VT);
 
   if (Result.FailedOn) {
     reportGISelFailure(MF, MORE, "gisel-legalize",
diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index 1aa1d465d8da6..32cda979a0cda 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -104,16 +104,19 @@ static Type *getFloatTypeForLLT(LLVMContext &Ctx, LLT Ty) {
 
 LegalizerHelper::LegalizerHelper(MachineFunction &MF,
                                  GISelChangeObserver &Observer,
-                                 MachineIRBuilder &Builder)
+                                 MachineIRBuilder &Builder,
+                                 const LibcallLoweringInfo *Libcalls)
     : MIRBuilder(Builder), Observer(Observer), MRI(MF.getRegInfo()),
       LI(*MF.getSubtarget().getLegalizerInfo()),
-      TLI(*MF.getSubtarget().getTargetLowering()), VT(nullptr) {}
+      TLI(*MF.getSubtarget().getTargetLowering()), Libcalls(Libcalls) {}
 
 LegalizerHelper::LegalizerHelper(MachineFunction &MF, const LegalizerInfo &LI,
                                  GISelChangeObserver &Observer,
-                                 MachineIRBuilder &B, GISelValueTracking *VT)
+                                 MachineIRBuilder &B,
+                                 const LibcallLoweringInfo *Libcalls,
+                                 GISelValueTracking *VT)
     : MIRBuilder(B), Observer(Observer), MRI(MF.getRegInfo()), LI(LI),
-      TLI(*MF.getSubtarget().getTargetLowering()), VT(VT) {}
+      TLI(*MF.getSubtarget().getTargetLowering()), Libcalls(Libcalls), VT(VT) {}
 
 LegalizerHelper::LegalizeResult
 LegalizerHelper::legalizeInstrStep(MachineInstr &MI,
@@ -581,12 +584,10 @@ static bool isLibCallInTailPosition(const CallLowering::ArgInfo &Result,
   return true;
 }
 
-LegalizerHelper::LegalizeResult
-llvm::createLibcall(MachineIRBuilder &MIRBuilder, const char *Name,
-                    const CallLowering::ArgInfo &Result,
-                    ArrayRef<CallLowering::ArgInfo> Args,
-                    const CallingConv::ID CC, LostDebugLocObserver &LocObserver,
-                    MachineInstr *MI) {
+LegalizerHelper::LegalizeResult LegalizerHelper::createLibcall(
+    const char *Name, const CallLowering::ArgInfo &Result,
+    ArrayRef<CallLowering::ArgInfo> Args, const CallingConv::ID CC,
+    LostDebugLocObserver &LocObserver, MachineInstr *MI) const {
   auto &CLI = *MIRBuilder.getMF().getSubtarget().getCallLowering();
 
   CallLowering::CallLoweringInfo Info;
@@ -628,31 +629,36 @@ llvm::createLibcall(MachineIRBuilder &MIRBuilder, const char *Name,
   return LegalizerHelper::Legalized;
 }
 
-LegalizerHelper::LegalizeResult
-llvm::createLibcall(MachineIRBuilder &MIRBuilder, RTLIB::Libcall Libcall,
-                    const CallLowering::ArgInfo &Result,
-                    ArrayRef<CallLowering::ArgInfo> Args,
-                    LostDebugLocObserver &LocObserver, MachineInstr *MI) {
-  auto &TLI = *MIRBuilder.getMF().getSubtarget().getTargetLowering();
-  const char *Name = TLI.getLibcallName(Libcall);
-  if (!Name)
+LegalizerHelper::LegalizeResult LegalizerHelper::createLibcall(
+    RTLIB::Libcall Libcall, const CallLowering::ArgInfo &Result,
+    ArrayRef<CallLowering::ArgInfo> Args, LostDebugLocObserver &LocObserver,
+    MachineInstr *MI) const {
+  if (!Libcalls)
     return LegalizerHelper::UnableToLegalize;
-  const CallingConv::ID CC = TLI.getLibcallCallingConv(Libcall);
-  return createLibcall(MIRBuilder, Name, Result, Args, CC, LocObserver, MI);
+
+  RTLIB::LibcallImpl LibcallImpl = Libcalls->getLibcallImpl(Libcall);
+  if (LibcallImpl == RTLIB::Unsupported)
+    return LegalizerHelper::UnableToLegalize;
+
+  auto &TLI = *MIRBuilder.getMF().getSubtarget().getTargetLowering();
+
+  StringRef Name = RTLIB::RuntimeLibcallsInfo::getLibcallImplName(LibcallImpl);
+  const CallingConv::ID CC = TLI.getLibcallImplCallingConv(LibcallImpl);
+  return createLibcall(Name.data(), Result, Args, CC, LocObserver, MI);
 }
 
 // Useful for libcalls where all operands have the same type.
-static LegalizerHelper::LegalizeResult
-simpleLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder, unsigned Size,
-              Type *OpType, LostDebugLocObserver &LocObserver) {
+LegalizerHelper::LegalizeResult
+LegalizerHelper::simpleLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder,
+                               unsigned Size, Type *OpType,
+                               LostDebugLocObserver &LocObserver) const {
   auto Libcall = getRTLibDesc(MI.getOpcode(), Size);
 
   // FIXME: What does the original arg index mean here?
   SmallVector<CallLowering::ArgInfo, 3> Args;
   for (const MachineOperand &MO : llvm::drop_begin(MI.operands()))
     Args.push_back({MO.getReg(), OpType, 0});
-  return createLibcall(MIRBuilder, Libcall,
-                       {MI.getOperand(0).getReg(), OpType, 0}, Args,
+  return createLibcall(Libcall, {MI.getOperand(0).getReg(), OpType, 0}, Args,
                        LocObserver, &MI);
 }
 
@@ -681,13 +687,12 @@ LegalizerHelper::LegalizeResult LegalizerHelper::emitSincosLibcall(
           .getReg(0);
 
   auto &Ctx = MF.getFunction().getContext();
-  auto LibcallResult =
-      createLibcall(MIRBuilder, getRTLibDesc(MI.getOpcode(), Size),
-                    {{0}, Type::getVoidTy(Ctx), 0},
-                    {{Src, OpType, 0},
-                     {StackPtrSin, PointerType::get(Ctx, AddrSpace), 1},
-                     {StackPtrCos, PointerType::get(Ctx, AddrSpace), 2}},
-                    LocObserver, &MI);
+  auto LibcallResult = createLibcall(
+      getRTLibDesc(MI.getOpcode(), Size), {{0}, Type::getVoidTy(Ctx), 0},
+      {{Src, OpType, 0},
+       {StackPtrSin, PointerType::get(Ctx, AddrSpace), 1},
+       {StackPtrCos, PointerType::get(Ctx, AddrSpace), 2}},
+      LocObserver, &MI);
 
   if (LibcallResult != LegalizeResult::Legalized)
     return LegalizerHelper::UnableToLegalize;
@@ -728,7 +733,7 @@ LegalizerHelper::emitModfLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder,
 
   auto &Ctx = MF.getFunction().getContext();
   auto Libc...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Dec 2, 2025

@llvm/pr-subscribers-backend-x86

Author: Matt Arsenault (arsenm)

Changes

GlobalISel: Use LibcallLoweringInfo analysis in legalizer

This is mostly boilerplate to move various freestanding utility
functions into LegalizerHelper. LibcallLoweringInfo is currently
optional, mostly because threading it through assorted other
uses of LegalizerHelper is more difficult.

I had a lot of trouble getting this to work in the legacy pass
manager with setRequiresCodeGenSCCOrder, and am not happy with the
result. A sub-pass manager is introduced and this is invalidated,
so we're re-computing this unnecessarily.

Old pass manager hacking


Patch is 49.98 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/170328.diff

15 Files Affected:

  • (modified) llvm/include/llvm/Analysis/RuntimeLibcallInfo.h (+2)
  • (modified) llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h (+6-5)
  • (modified) llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h (+43-30)
  • (modified) llvm/include/llvm/CodeGen/LibcallLoweringInfo.h (+11-5)
  • (modified) llvm/include/llvm/CodeGen/Passes.h (+2)
  • (modified) llvm/lib/CodeGen/ExpandFp.cpp (+1-1)
  • (modified) llvm/lib/CodeGen/GlobalISel/Legalizer.cpp (+16-9)
  • (modified) llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp (+71-78)
  • (modified) llvm/lib/CodeGen/LibcallLoweringInfo.cpp (+6-3)
  • (modified) llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp (+1-1)
  • (modified) llvm/lib/CodeGen/TargetPassConfig.cpp (+2)
  • (modified) llvm/lib/Target/ARM/ARMLegalizerInfo.cpp (+9-9)
  • (modified) llvm/unittests/CodeGen/GlobalISel/GISelMITest.h (+4)
  • (modified) llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp (+32-31)
  • (modified) llvm/unittests/CodeGen/GlobalISel/LegalizerTest.cpp (+3-3)
diff --git a/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h b/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h
index 2d31c8aa6301b..b0f398d6e2b28 100644
--- a/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h
+++ b/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h
@@ -32,6 +32,8 @@ class LLVM_ABI RuntimeLibraryAnalysis
   LLVM_ABI RTLIB::RuntimeLibcallsInfo run(const Module &M,
                                           ModuleAnalysisManager &);
 
+  operator bool() const { return LibcallsInfo.has_value(); }
+
 private:
   friend AnalysisInfoMixin<RuntimeLibraryAnalysis>;
   LLVM_ABI static AnalysisKey Key;
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h b/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h
index a94daa202d856..f8e629bc50a90 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h
@@ -33,6 +33,7 @@ class LegalizerInfo;
 class MachineIRBuilder;
 class MachineInstr;
 class GISelChangeObserver;
+class LibcallLoweringInfo;
 class LostDebugLocObserver;
 
 class LLVM_ABI Legalizer : public MachineFunctionPass {
@@ -70,11 +71,11 @@ class LLVM_ABI Legalizer : public MachineFunctionPass {
 
   bool runOnMachineFunction(MachineFunction &MF) override;
 
-  static MFResult
-  legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
-                          ArrayRef<GISelChangeObserver *> AuxObservers,
-                          LostDebugLocObserver &LocObserver,
-                          MachineIRBuilder &MIRBuilder, GISelValueTracking *VT);
+  static MFResult legalizeMachineFunction(
+      MachineFunction &MF, const LegalizerInfo &LI,
+      ArrayRef<GISelChangeObserver *> AuxObservers,
+      LostDebugLocObserver &LocObserver, MachineIRBuilder &MIRBuilder,
+      const LibcallLoweringInfo *Libcalls, GISelValueTracking *VT);
 };
 } // End namespace llvm.
 
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
index a458cbd94ccb1..11f734ec64eb1 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
@@ -59,7 +59,10 @@ class LegalizerHelper {
   MachineRegisterInfo &MRI;
   const LegalizerInfo &LI;
   const TargetLowering &TLI;
-  GISelValueTracking *VT;
+
+  // FIXME: Should probably make Libcalls mandatory
+  const LibcallLoweringInfo *Libcalls = nullptr;
+  GISelValueTracking *VT = nullptr;
 
 public:
   enum LegalizeResult {
@@ -78,12 +81,15 @@ class LegalizerHelper {
   /// Expose LegalizerInfo so the clients can re-use.
   const LegalizerInfo &getLegalizerInfo() const { return LI; }
   const TargetLowering &getTargetLowering() const { return TLI; }
+  const LibcallLoweringInfo *getLibcallLoweringInfo() { return Libcalls; }
   GISelValueTracking *getValueTracking() const { return VT; }
 
   LLVM_ABI LegalizerHelper(MachineFunction &MF, GISelChangeObserver &Observer,
-                           MachineIRBuilder &B);
+                           MachineIRBuilder &B,
+                           const LibcallLoweringInfo *Libcalls = nullptr);
   LLVM_ABI LegalizerHelper(MachineFunction &MF, const LegalizerInfo &LI,
                            GISelChangeObserver &Observer, MachineIRBuilder &B,
+                           const LibcallLoweringInfo *Libcalls = nullptr,
                            GISelValueTracking *VT = nullptr);
 
   /// Replace \p MI by a sequence of legal instructions that can implement the
@@ -178,6 +184,37 @@ class LegalizerHelper {
   /// def by inserting a G_BITCAST from \p CastTy
   LLVM_ABI void bitcastDst(MachineInstr &MI, LLT CastTy, unsigned OpIdx);
 
+  // Useful for libcalls where all operands have the same type.
+  LLVM_ABI LegalizeResult
+  simpleLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder, unsigned Size,
+                Type *OpType, LostDebugLocObserver &LocObserver) const;
+
+  LLVM_ABI LegalizeResult conversionLibcall(MachineInstr &MI, Type *ToType,
+                                            Type *FromType,
+                                            LostDebugLocObserver &LocObserver,
+                                            bool IsSigned = false) const;
+
+  /// Helper function that creates a libcall to the given \p Name using the
+  /// given calling convention \p CC.
+  LLVM_ABI LegalizeResult createLibcall(const char *Name,
+                                        const CallLowering::ArgInfo &Result,
+                                        ArrayRef<CallLowering::ArgInfo> Args,
+                                        CallingConv::ID CC,
+                                        LostDebugLocObserver &LocObserver,
+                                        MachineInstr *MI = nullptr) const;
+
+  /// Helper function that creates the given libcall.
+  LLVM_ABI LegalizeResult createLibcall(RTLIB::Libcall Libcall,
+                                        const CallLowering::ArgInfo &Result,
+                                        ArrayRef<CallLowering::ArgInfo> Args,
+                                        LostDebugLocObserver &LocObserver,
+                                        MachineInstr *MI = nullptr) const;
+
+  /// Create a libcall to memcpy et al.
+  LLVM_ABI LegalizeResult
+  createMemLibcall(MachineRegisterInfo &MRI, MachineInstr &MI,
+                   LostDebugLocObserver &LocObserver) const;
+
 private:
   LegalizeResult
   widenScalarMergeValues(MachineInstr &MI, unsigned TypeIdx, LLT WideTy);
@@ -278,17 +315,13 @@ class LegalizerHelper {
                               bool IsVolatile);
 
   // Implements floating-point environment read/write via library function call.
-  LegalizeResult createGetStateLibcall(MachineIRBuilder &MIRBuilder,
-                                       MachineInstr &MI,
+  LegalizeResult createGetStateLibcall(MachineInstr &MI,
                                        LostDebugLocObserver &LocObserver);
-  LegalizeResult createSetStateLibcall(MachineIRBuilder &MIRBuilder,
-                                       MachineInstr &MI,
+  LegalizeResult createSetStateLibcall(MachineInstr &MI,
                                        LostDebugLocObserver &LocObserver);
-  LegalizeResult createResetStateLibcall(MachineIRBuilder &MIRBuilder,
-                                         MachineInstr &MI,
+  LegalizeResult createResetStateLibcall(MachineInstr &MI,
                                          LostDebugLocObserver &LocObserver);
-  LegalizeResult createFCMPLibcall(MachineIRBuilder &MIRBuilder,
-                                   MachineInstr &MI,
+  LegalizeResult createFCMPLibcall(MachineInstr &MI,
                                    LostDebugLocObserver &LocObserver);
 
   MachineInstrBuilder
@@ -539,26 +572,6 @@ class LegalizerHelper {
   LLVM_ABI LegalizeResult lowerVAArg(MachineInstr &MI);
 };
 
-/// Helper function that creates a libcall to the given \p Name using the given
-/// calling convention \p CC.
-LLVM_ABI LegalizerHelper::LegalizeResult
-createLibcall(MachineIRBuilder &MIRBuilder, const char *Name,
-              const CallLowering::ArgInfo &Result,
-              ArrayRef<CallLowering::ArgInfo> Args, CallingConv::ID CC,
-              LostDebugLocObserver &LocObserver, MachineInstr *MI = nullptr);
-
-/// Helper function that creates the given libcall.
-LLVM_ABI LegalizerHelper::LegalizeResult
-createLibcall(MachineIRBuilder &MIRBuilder, RTLIB::Libcall Libcall,
-              const CallLowering::ArgInfo &Result,
-              ArrayRef<CallLowering::ArgInfo> Args,
-              LostDebugLocObserver &LocObserver, MachineInstr *MI = nullptr);
-
-/// Create a libcall to memcpy et al.
-LLVM_ABI LegalizerHelper::LegalizeResult
-createMemLibcall(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI,
-                 MachineInstr &MI, LostDebugLocObserver &LocObserver);
-
 } // End namespace llvm.
 
 #endif
diff --git a/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h b/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h
index 3e0137710e8eb..47c0dd315d3b5 100644
--- a/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h
+++ b/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h
@@ -10,11 +10,12 @@
 #define LLVM_CODEGEN_LIBCALLLOWERINGINFO_H
 
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/Analysis/RuntimeLibcallInfo.h"
 #include "llvm/IR/RuntimeLibcalls.h"
 #include "llvm/Pass.h"
 
 namespace llvm {
-
+class RuntimeLibraryInfoWrapper;
 class TargetSubtargetInfo;
 class TargetMachine;
 
@@ -97,6 +98,8 @@ class LibcallLoweringModuleAnalysisResult {
     LoweringMap.clear();
   }
 
+  operator bool() const { return RTLCI != nullptr; }
+
   LLVM_ABI bool invalidate(Module &, const PreservedAnalyses &,
                            ModuleAnalysisManager::Invalidator &);
 
@@ -122,21 +125,24 @@ class LibcallLoweringModuleAnalysis
 
 class LLVM_ABI LibcallLoweringInfoWrapper : public ImmutablePass {
   LibcallLoweringModuleAnalysisResult Result;
+  RuntimeLibraryInfoWrapper *RuntimeLibcallsWrapper = nullptr;
 
 public:
   static char ID;
   LibcallLoweringInfoWrapper();
 
   const LibcallLoweringInfo &
-  getLibcallLowering(const TargetSubtargetInfo &Subtarget) const {
-    return Result.getLibcallLowering(Subtarget);
+  getLibcallLowering(const Module &M, const TargetSubtargetInfo &Subtarget) {
+    return getResult(M).getLibcallLowering(Subtarget);
   }
 
-  const LibcallLoweringModuleAnalysisResult &getResult() const {
+  const LibcallLoweringModuleAnalysisResult &getResult(const Module &M) {
+    if (!Result)
+      Result.init(&RuntimeLibcallsWrapper->getRTLCI(M));
     return Result;
   }
 
-  bool doInitialization(Module &M) override;
+  void initializePass() override;
   void getAnalysisUsage(AnalysisUsage &AU) const override;
   void releaseMemory() override;
 };
diff --git a/llvm/include/llvm/CodeGen/Passes.h b/llvm/include/llvm/CodeGen/Passes.h
index a8525554b142e..168be429e13a6 100644
--- a/llvm/include/llvm/CodeGen/Passes.h
+++ b/llvm/include/llvm/CodeGen/Passes.h
@@ -487,6 +487,8 @@ LLVM_ABI FunctionPass *createInterleavedLoadCombinePass();
 ///
 LLVM_ABI ModulePass *createLowerEmuTLSPass();
 
+LLVM_ABI ModulePass *createLibcallLoweringInfoWrapper();
+
 /// This pass lowers the \@llvm.load.relative and \@llvm.objc.* intrinsics to
 /// instructions.  This is unsafe to do earlier because a pass may combine the
 /// constant initializer into the load, which may result in an overflowing
diff --git a/llvm/lib/CodeGen/ExpandFp.cpp b/llvm/lib/CodeGen/ExpandFp.cpp
index 13ed4846d2bf7..7d333be7ed6fc 100644
--- a/llvm/lib/CodeGen/ExpandFp.cpp
+++ b/llvm/lib/CodeGen/ExpandFp.cpp
@@ -1097,7 +1097,7 @@ class ExpandFpLegacyPass : public FunctionPass {
 
     const LibcallLoweringInfo &Libcalls =
         getAnalysis<LibcallLoweringInfoWrapper>().getLibcallLowering(
-            *Subtarget);
+            *F.getParent(), *Subtarget);
 
     if (OptLevel != CodeGenOptLevel::None && !F.hasOptNone())
       AC = &getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
diff --git a/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp b/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp
index 0f0656aaa4f45..711f7a0b5c35b 100644
--- a/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp
@@ -73,6 +73,7 @@ char Legalizer::ID = 0;
 INITIALIZE_PASS_BEGIN(Legalizer, DEBUG_TYPE,
                       "Legalize the Machine IR a function's Machine IR", false,
                       false)
+INITIALIZE_PASS_DEPENDENCY(LibcallLoweringInfoWrapper)
 INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
 INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(GISelValueTrackingAnalysisLegacy)
@@ -83,6 +84,7 @@ INITIALIZE_PASS_END(Legalizer, DEBUG_TYPE,
 Legalizer::Legalizer() : MachineFunctionPass(ID) { }
 
 void Legalizer::getAnalysisUsage(AnalysisUsage &AU) const {
+  AU.addRequired<LibcallLoweringInfoWrapper>();
   AU.addRequired<TargetPassConfig>();
   AU.addRequired<GISelCSEAnalysisWrapperPass>();
   AU.addPreserved<GISelCSEAnalysisWrapperPass>();
@@ -172,12 +174,11 @@ class LegalizerWorkListManager : public GISelChangeObserver {
 };
 } // namespace
 
-Legalizer::MFResult
-Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
-                                   ArrayRef<GISelChangeObserver *> AuxObservers,
-                                   LostDebugLocObserver &LocObserver,
-                                   MachineIRBuilder &MIRBuilder,
-                                   GISelValueTracking *VT) {
+Legalizer::MFResult Legalizer::legalizeMachineFunction(
+    MachineFunction &MF, const LegalizerInfo &LI,
+    ArrayRef<GISelChangeObserver *> AuxObservers,
+    LostDebugLocObserver &LocObserver, MachineIRBuilder &MIRBuilder,
+    const LibcallLoweringInfo *Libcalls, GISelValueTracking *VT) {
   MIRBuilder.setMF(MF);
   MachineRegisterInfo &MRI = MF.getRegInfo();
 
@@ -216,7 +217,7 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
   // Now install the observer as the delegate to MF.
   // This will keep all the observers notified about new insertions/deletions.
   RAIIMFObsDelInstaller Installer(MF, WrapperObserver);
-  LegalizerHelper Helper(MF, LI, WrapperObserver, MIRBuilder, VT);
+  LegalizerHelper Helper(MF, LI, WrapperObserver, MIRBuilder, Libcalls, VT);
   LegalizationArtifactCombiner ArtCombiner(MIRBuilder, MRI, LI, VT);
   bool Changed = false;
   SmallVector<MachineInstr *, 128> RetryList;
@@ -339,13 +340,19 @@ bool Legalizer::runOnMachineFunction(MachineFunction &MF) {
   if (VerifyDebugLocs > DebugLocVerifyLevel::None)
     AuxObservers.push_back(&LocObserver);
 
+  const TargetSubtargetInfo &Subtarget = MF.getSubtarget();
+
+  const LibcallLoweringInfo &Libcalls =
+      getAnalysis<LibcallLoweringInfoWrapper>().getLibcallLowering(
+          *MF.getFunction().getParent(), Subtarget);
+
   // This allows Known Bits Analysis in the legalizer.
   GISelValueTracking *VT =
       &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
 
-  const LegalizerInfo &LI = *MF.getSubtarget().getLegalizerInfo();
+  const LegalizerInfo &LI = *Subtarget.getLegalizerInfo();
   MFResult Result = legalizeMachineFunction(MF, LI, AuxObservers, LocObserver,
-                                            *MIRBuilder, VT);
+                                            *MIRBuilder, &Libcalls, VT);
 
   if (Result.FailedOn) {
     reportGISelFailure(MF, MORE, "gisel-legalize",
diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index 1aa1d465d8da6..32cda979a0cda 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -104,16 +104,19 @@ static Type *getFloatTypeForLLT(LLVMContext &Ctx, LLT Ty) {
 
 LegalizerHelper::LegalizerHelper(MachineFunction &MF,
                                  GISelChangeObserver &Observer,
-                                 MachineIRBuilder &Builder)
+                                 MachineIRBuilder &Builder,
+                                 const LibcallLoweringInfo *Libcalls)
     : MIRBuilder(Builder), Observer(Observer), MRI(MF.getRegInfo()),
       LI(*MF.getSubtarget().getLegalizerInfo()),
-      TLI(*MF.getSubtarget().getTargetLowering()), VT(nullptr) {}
+      TLI(*MF.getSubtarget().getTargetLowering()), Libcalls(Libcalls) {}
 
 LegalizerHelper::LegalizerHelper(MachineFunction &MF, const LegalizerInfo &LI,
                                  GISelChangeObserver &Observer,
-                                 MachineIRBuilder &B, GISelValueTracking *VT)
+                                 MachineIRBuilder &B,
+                                 const LibcallLoweringInfo *Libcalls,
+                                 GISelValueTracking *VT)
     : MIRBuilder(B), Observer(Observer), MRI(MF.getRegInfo()), LI(LI),
-      TLI(*MF.getSubtarget().getTargetLowering()), VT(VT) {}
+      TLI(*MF.getSubtarget().getTargetLowering()), Libcalls(Libcalls), VT(VT) {}
 
 LegalizerHelper::LegalizeResult
 LegalizerHelper::legalizeInstrStep(MachineInstr &MI,
@@ -581,12 +584,10 @@ static bool isLibCallInTailPosition(const CallLowering::ArgInfo &Result,
   return true;
 }
 
-LegalizerHelper::LegalizeResult
-llvm::createLibcall(MachineIRBuilder &MIRBuilder, const char *Name,
-                    const CallLowering::ArgInfo &Result,
-                    ArrayRef<CallLowering::ArgInfo> Args,
-                    const CallingConv::ID CC, LostDebugLocObserver &LocObserver,
-                    MachineInstr *MI) {
+LegalizerHelper::LegalizeResult LegalizerHelper::createLibcall(
+    const char *Name, const CallLowering::ArgInfo &Result,
+    ArrayRef<CallLowering::ArgInfo> Args, const CallingConv::ID CC,
+    LostDebugLocObserver &LocObserver, MachineInstr *MI) const {
   auto &CLI = *MIRBuilder.getMF().getSubtarget().getCallLowering();
 
   CallLowering::CallLoweringInfo Info;
@@ -628,31 +629,36 @@ llvm::createLibcall(MachineIRBuilder &MIRBuilder, const char *Name,
   return LegalizerHelper::Legalized;
 }
 
-LegalizerHelper::LegalizeResult
-llvm::createLibcall(MachineIRBuilder &MIRBuilder, RTLIB::Libcall Libcall,
-                    const CallLowering::ArgInfo &Result,
-                    ArrayRef<CallLowering::ArgInfo> Args,
-                    LostDebugLocObserver &LocObserver, MachineInstr *MI) {
-  auto &TLI = *MIRBuilder.getMF().getSubtarget().getTargetLowering();
-  const char *Name = TLI.getLibcallName(Libcall);
-  if (!Name)
+LegalizerHelper::LegalizeResult LegalizerHelper::createLibcall(
+    RTLIB::Libcall Libcall, const CallLowering::ArgInfo &Result,
+    ArrayRef<CallLowering::ArgInfo> Args, LostDebugLocObserver &LocObserver,
+    MachineInstr *MI) const {
+  if (!Libcalls)
     return LegalizerHelper::UnableToLegalize;
-  const CallingConv::ID CC = TLI.getLibcallCallingConv(Libcall);
-  return createLibcall(MIRBuilder, Name, Result, Args, CC, LocObserver, MI);
+
+  RTLIB::LibcallImpl LibcallImpl = Libcalls->getLibcallImpl(Libcall);
+  if (LibcallImpl == RTLIB::Unsupported)
+    return LegalizerHelper::UnableToLegalize;
+
+  auto &TLI = *MIRBuilder.getMF().getSubtarget().getTargetLowering();
+
+  StringRef Name = RTLIB::RuntimeLibcallsInfo::getLibcallImplName(LibcallImpl);
+  const CallingConv::ID CC = TLI.getLibcallImplCallingConv(LibcallImpl);
+  return createLibcall(Name.data(), Result, Args, CC, LocObserver, MI);
 }
 
 // Useful for libcalls where all operands have the same type.
-static LegalizerHelper::LegalizeResult
-simpleLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder, unsigned Size,
-              Type *OpType, LostDebugLocObserver &LocObserver) {
+LegalizerHelper::LegalizeResult
+LegalizerHelper::simpleLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder,
+                               unsigned Size, Type *OpType,
+                               LostDebugLocObserver &LocObserver) const {
   auto Libcall = getRTLibDesc(MI.getOpcode(), Size);
 
   // FIXME: What does the original arg index mean here?
   SmallVector<CallLowering::ArgInfo, 3> Args;
   for (const MachineOperand &MO : llvm::drop_begin(MI.operands()))
     Args.push_back({MO.getReg(), OpType, 0});
-  return createLibcall(MIRBuilder, Libcall,
-                       {MI.getOperand(0).getReg(), OpType, 0}, Args,
+  return createLibcall(Libcall, {MI.getOperand(0).getReg(), OpType, 0}, Args,
                        LocObserver, &MI);
 }
 
@@ -681,13 +687,12 @@ LegalizerHelper::LegalizeResult LegalizerHelper::emitSincosLibcall(
           .getReg(0);
 
   auto &Ctx = MF.getFunction().getContext();
-  auto LibcallResult =
-      createLibcall(MIRBuilder, getRTLibDesc(MI.getOpcode(), Size),
-                    {{0}, Type::getVoidTy(Ctx), 0},
-                    {{Src, OpType, 0},
-                     {StackPtrSin, PointerType::get(Ctx, AddrSpace), 1},
-                     {StackPtrCos, PointerType::get(Ctx, AddrSpace), 2}},
-                    LocObserver, &MI);
+  auto LibcallResult = createLibcall(
+      getRTLibDesc(MI.getOpcode(), Size), {{0}, Type::getVoidTy(Ctx), 0},
+      {{Src, OpType, 0},
+       {StackPtrSin, PointerType::get(Ctx, AddrSpace), 1},
+       {StackPtrCos, PointerType::get(Ctx, AddrSpace), 2}},
+      LocObserver, &MI);
 
   if (LibcallResult != LegalizeResult::Legalized)
     return LegalizerHelper::UnableToLegalize;
@@ -728,7 +733,7 @@ LegalizerHelper::emitModfLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder,
 
   auto &Ctx = MF.getFunction().getContext();
   auto Libc...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Dec 2, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Matt Arsenault (arsenm)

Changes

GlobalISel: Use LibcallLoweringInfo analysis in legalizer

This is mostly boilerplate to move various freestanding utility
functions into LegalizerHelper. LibcallLoweringInfo is currently
optional, mostly because threading it through assorted other
uses of LegalizerHelper is more difficult.

I had a lot of trouble getting this to work in the legacy pass
manager with setRequiresCodeGenSCCOrder, and am not happy with the
result. A sub-pass manager is introduced and this is invalidated,
so we're re-computing this unnecessarily.

Old pass manager hacking


Patch is 49.98 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/170328.diff

15 Files Affected:

  • (modified) llvm/include/llvm/Analysis/RuntimeLibcallInfo.h (+2)
  • (modified) llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h (+6-5)
  • (modified) llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h (+43-30)
  • (modified) llvm/include/llvm/CodeGen/LibcallLoweringInfo.h (+11-5)
  • (modified) llvm/include/llvm/CodeGen/Passes.h (+2)
  • (modified) llvm/lib/CodeGen/ExpandFp.cpp (+1-1)
  • (modified) llvm/lib/CodeGen/GlobalISel/Legalizer.cpp (+16-9)
  • (modified) llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp (+71-78)
  • (modified) llvm/lib/CodeGen/LibcallLoweringInfo.cpp (+6-3)
  • (modified) llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp (+1-1)
  • (modified) llvm/lib/CodeGen/TargetPassConfig.cpp (+2)
  • (modified) llvm/lib/Target/ARM/ARMLegalizerInfo.cpp (+9-9)
  • (modified) llvm/unittests/CodeGen/GlobalISel/GISelMITest.h (+4)
  • (modified) llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp (+32-31)
  • (modified) llvm/unittests/CodeGen/GlobalISel/LegalizerTest.cpp (+3-3)
diff --git a/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h b/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h
index 2d31c8aa6301b..b0f398d6e2b28 100644
--- a/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h
+++ b/llvm/include/llvm/Analysis/RuntimeLibcallInfo.h
@@ -32,6 +32,8 @@ class LLVM_ABI RuntimeLibraryAnalysis
   LLVM_ABI RTLIB::RuntimeLibcallsInfo run(const Module &M,
                                           ModuleAnalysisManager &);
 
+  operator bool() const { return LibcallsInfo.has_value(); }
+
 private:
   friend AnalysisInfoMixin<RuntimeLibraryAnalysis>;
   LLVM_ABI static AnalysisKey Key;
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h b/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h
index a94daa202d856..f8e629bc50a90 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h
@@ -33,6 +33,7 @@ class LegalizerInfo;
 class MachineIRBuilder;
 class MachineInstr;
 class GISelChangeObserver;
+class LibcallLoweringInfo;
 class LostDebugLocObserver;
 
 class LLVM_ABI Legalizer : public MachineFunctionPass {
@@ -70,11 +71,11 @@ class LLVM_ABI Legalizer : public MachineFunctionPass {
 
   bool runOnMachineFunction(MachineFunction &MF) override;
 
-  static MFResult
-  legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
-                          ArrayRef<GISelChangeObserver *> AuxObservers,
-                          LostDebugLocObserver &LocObserver,
-                          MachineIRBuilder &MIRBuilder, GISelValueTracking *VT);
+  static MFResult legalizeMachineFunction(
+      MachineFunction &MF, const LegalizerInfo &LI,
+      ArrayRef<GISelChangeObserver *> AuxObservers,
+      LostDebugLocObserver &LocObserver, MachineIRBuilder &MIRBuilder,
+      const LibcallLoweringInfo *Libcalls, GISelValueTracking *VT);
 };
 } // End namespace llvm.
 
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
index a458cbd94ccb1..11f734ec64eb1 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
@@ -59,7 +59,10 @@ class LegalizerHelper {
   MachineRegisterInfo &MRI;
   const LegalizerInfo &LI;
   const TargetLowering &TLI;
-  GISelValueTracking *VT;
+
+  // FIXME: Should probably make Libcalls mandatory
+  const LibcallLoweringInfo *Libcalls = nullptr;
+  GISelValueTracking *VT = nullptr;
 
 public:
   enum LegalizeResult {
@@ -78,12 +81,15 @@ class LegalizerHelper {
   /// Expose LegalizerInfo so the clients can re-use.
   const LegalizerInfo &getLegalizerInfo() const { return LI; }
   const TargetLowering &getTargetLowering() const { return TLI; }
+  const LibcallLoweringInfo *getLibcallLoweringInfo() { return Libcalls; }
   GISelValueTracking *getValueTracking() const { return VT; }
 
   LLVM_ABI LegalizerHelper(MachineFunction &MF, GISelChangeObserver &Observer,
-                           MachineIRBuilder &B);
+                           MachineIRBuilder &B,
+                           const LibcallLoweringInfo *Libcalls = nullptr);
   LLVM_ABI LegalizerHelper(MachineFunction &MF, const LegalizerInfo &LI,
                            GISelChangeObserver &Observer, MachineIRBuilder &B,
+                           const LibcallLoweringInfo *Libcalls = nullptr,
                            GISelValueTracking *VT = nullptr);
 
   /// Replace \p MI by a sequence of legal instructions that can implement the
@@ -178,6 +184,37 @@ class LegalizerHelper {
   /// def by inserting a G_BITCAST from \p CastTy
   LLVM_ABI void bitcastDst(MachineInstr &MI, LLT CastTy, unsigned OpIdx);
 
+  // Useful for libcalls where all operands have the same type.
+  LLVM_ABI LegalizeResult
+  simpleLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder, unsigned Size,
+                Type *OpType, LostDebugLocObserver &LocObserver) const;
+
+  LLVM_ABI LegalizeResult conversionLibcall(MachineInstr &MI, Type *ToType,
+                                            Type *FromType,
+                                            LostDebugLocObserver &LocObserver,
+                                            bool IsSigned = false) const;
+
+  /// Helper function that creates a libcall to the given \p Name using the
+  /// given calling convention \p CC.
+  LLVM_ABI LegalizeResult createLibcall(const char *Name,
+                                        const CallLowering::ArgInfo &Result,
+                                        ArrayRef<CallLowering::ArgInfo> Args,
+                                        CallingConv::ID CC,
+                                        LostDebugLocObserver &LocObserver,
+                                        MachineInstr *MI = nullptr) const;
+
+  /// Helper function that creates the given libcall.
+  LLVM_ABI LegalizeResult createLibcall(RTLIB::Libcall Libcall,
+                                        const CallLowering::ArgInfo &Result,
+                                        ArrayRef<CallLowering::ArgInfo> Args,
+                                        LostDebugLocObserver &LocObserver,
+                                        MachineInstr *MI = nullptr) const;
+
+  /// Create a libcall to memcpy et al.
+  LLVM_ABI LegalizeResult
+  createMemLibcall(MachineRegisterInfo &MRI, MachineInstr &MI,
+                   LostDebugLocObserver &LocObserver) const;
+
 private:
   LegalizeResult
   widenScalarMergeValues(MachineInstr &MI, unsigned TypeIdx, LLT WideTy);
@@ -278,17 +315,13 @@ class LegalizerHelper {
                               bool IsVolatile);
 
   // Implements floating-point environment read/write via library function call.
-  LegalizeResult createGetStateLibcall(MachineIRBuilder &MIRBuilder,
-                                       MachineInstr &MI,
+  LegalizeResult createGetStateLibcall(MachineInstr &MI,
                                        LostDebugLocObserver &LocObserver);
-  LegalizeResult createSetStateLibcall(MachineIRBuilder &MIRBuilder,
-                                       MachineInstr &MI,
+  LegalizeResult createSetStateLibcall(MachineInstr &MI,
                                        LostDebugLocObserver &LocObserver);
-  LegalizeResult createResetStateLibcall(MachineIRBuilder &MIRBuilder,
-                                         MachineInstr &MI,
+  LegalizeResult createResetStateLibcall(MachineInstr &MI,
                                          LostDebugLocObserver &LocObserver);
-  LegalizeResult createFCMPLibcall(MachineIRBuilder &MIRBuilder,
-                                   MachineInstr &MI,
+  LegalizeResult createFCMPLibcall(MachineInstr &MI,
                                    LostDebugLocObserver &LocObserver);
 
   MachineInstrBuilder
@@ -539,26 +572,6 @@ class LegalizerHelper {
   LLVM_ABI LegalizeResult lowerVAArg(MachineInstr &MI);
 };
 
-/// Helper function that creates a libcall to the given \p Name using the given
-/// calling convention \p CC.
-LLVM_ABI LegalizerHelper::LegalizeResult
-createLibcall(MachineIRBuilder &MIRBuilder, const char *Name,
-              const CallLowering::ArgInfo &Result,
-              ArrayRef<CallLowering::ArgInfo> Args, CallingConv::ID CC,
-              LostDebugLocObserver &LocObserver, MachineInstr *MI = nullptr);
-
-/// Helper function that creates the given libcall.
-LLVM_ABI LegalizerHelper::LegalizeResult
-createLibcall(MachineIRBuilder &MIRBuilder, RTLIB::Libcall Libcall,
-              const CallLowering::ArgInfo &Result,
-              ArrayRef<CallLowering::ArgInfo> Args,
-              LostDebugLocObserver &LocObserver, MachineInstr *MI = nullptr);
-
-/// Create a libcall to memcpy et al.
-LLVM_ABI LegalizerHelper::LegalizeResult
-createMemLibcall(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI,
-                 MachineInstr &MI, LostDebugLocObserver &LocObserver);
-
 } // End namespace llvm.
 
 #endif
diff --git a/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h b/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h
index 3e0137710e8eb..47c0dd315d3b5 100644
--- a/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h
+++ b/llvm/include/llvm/CodeGen/LibcallLoweringInfo.h
@@ -10,11 +10,12 @@
 #define LLVM_CODEGEN_LIBCALLLOWERINGINFO_H
 
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/Analysis/RuntimeLibcallInfo.h"
 #include "llvm/IR/RuntimeLibcalls.h"
 #include "llvm/Pass.h"
 
 namespace llvm {
-
+class RuntimeLibraryInfoWrapper;
 class TargetSubtargetInfo;
 class TargetMachine;
 
@@ -97,6 +98,8 @@ class LibcallLoweringModuleAnalysisResult {
     LoweringMap.clear();
   }
 
+  operator bool() const { return RTLCI != nullptr; }
+
   LLVM_ABI bool invalidate(Module &, const PreservedAnalyses &,
                            ModuleAnalysisManager::Invalidator &);
 
@@ -122,21 +125,24 @@ class LibcallLoweringModuleAnalysis
 
 class LLVM_ABI LibcallLoweringInfoWrapper : public ImmutablePass {
   LibcallLoweringModuleAnalysisResult Result;
+  RuntimeLibraryInfoWrapper *RuntimeLibcallsWrapper = nullptr;
 
 public:
   static char ID;
   LibcallLoweringInfoWrapper();
 
   const LibcallLoweringInfo &
-  getLibcallLowering(const TargetSubtargetInfo &Subtarget) const {
-    return Result.getLibcallLowering(Subtarget);
+  getLibcallLowering(const Module &M, const TargetSubtargetInfo &Subtarget) {
+    return getResult(M).getLibcallLowering(Subtarget);
   }
 
-  const LibcallLoweringModuleAnalysisResult &getResult() const {
+  const LibcallLoweringModuleAnalysisResult &getResult(const Module &M) {
+    if (!Result)
+      Result.init(&RuntimeLibcallsWrapper->getRTLCI(M));
     return Result;
   }
 
-  bool doInitialization(Module &M) override;
+  void initializePass() override;
   void getAnalysisUsage(AnalysisUsage &AU) const override;
   void releaseMemory() override;
 };
diff --git a/llvm/include/llvm/CodeGen/Passes.h b/llvm/include/llvm/CodeGen/Passes.h
index a8525554b142e..168be429e13a6 100644
--- a/llvm/include/llvm/CodeGen/Passes.h
+++ b/llvm/include/llvm/CodeGen/Passes.h
@@ -487,6 +487,8 @@ LLVM_ABI FunctionPass *createInterleavedLoadCombinePass();
 ///
 LLVM_ABI ModulePass *createLowerEmuTLSPass();
 
+LLVM_ABI ModulePass *createLibcallLoweringInfoWrapper();
+
 /// This pass lowers the \@llvm.load.relative and \@llvm.objc.* intrinsics to
 /// instructions.  This is unsafe to do earlier because a pass may combine the
 /// constant initializer into the load, which may result in an overflowing
diff --git a/llvm/lib/CodeGen/ExpandFp.cpp b/llvm/lib/CodeGen/ExpandFp.cpp
index 13ed4846d2bf7..7d333be7ed6fc 100644
--- a/llvm/lib/CodeGen/ExpandFp.cpp
+++ b/llvm/lib/CodeGen/ExpandFp.cpp
@@ -1097,7 +1097,7 @@ class ExpandFpLegacyPass : public FunctionPass {
 
     const LibcallLoweringInfo &Libcalls =
         getAnalysis<LibcallLoweringInfoWrapper>().getLibcallLowering(
-            *Subtarget);
+            *F.getParent(), *Subtarget);
 
     if (OptLevel != CodeGenOptLevel::None && !F.hasOptNone())
       AC = &getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
diff --git a/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp b/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp
index 0f0656aaa4f45..711f7a0b5c35b 100644
--- a/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp
@@ -73,6 +73,7 @@ char Legalizer::ID = 0;
 INITIALIZE_PASS_BEGIN(Legalizer, DEBUG_TYPE,
                       "Legalize the Machine IR a function's Machine IR", false,
                       false)
+INITIALIZE_PASS_DEPENDENCY(LibcallLoweringInfoWrapper)
 INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
 INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(GISelValueTrackingAnalysisLegacy)
@@ -83,6 +84,7 @@ INITIALIZE_PASS_END(Legalizer, DEBUG_TYPE,
 Legalizer::Legalizer() : MachineFunctionPass(ID) { }
 
 void Legalizer::getAnalysisUsage(AnalysisUsage &AU) const {
+  AU.addRequired<LibcallLoweringInfoWrapper>();
   AU.addRequired<TargetPassConfig>();
   AU.addRequired<GISelCSEAnalysisWrapperPass>();
   AU.addPreserved<GISelCSEAnalysisWrapperPass>();
@@ -172,12 +174,11 @@ class LegalizerWorkListManager : public GISelChangeObserver {
 };
 } // namespace
 
-Legalizer::MFResult
-Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
-                                   ArrayRef<GISelChangeObserver *> AuxObservers,
-                                   LostDebugLocObserver &LocObserver,
-                                   MachineIRBuilder &MIRBuilder,
-                                   GISelValueTracking *VT) {
+Legalizer::MFResult Legalizer::legalizeMachineFunction(
+    MachineFunction &MF, const LegalizerInfo &LI,
+    ArrayRef<GISelChangeObserver *> AuxObservers,
+    LostDebugLocObserver &LocObserver, MachineIRBuilder &MIRBuilder,
+    const LibcallLoweringInfo *Libcalls, GISelValueTracking *VT) {
   MIRBuilder.setMF(MF);
   MachineRegisterInfo &MRI = MF.getRegInfo();
 
@@ -216,7 +217,7 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
   // Now install the observer as the delegate to MF.
   // This will keep all the observers notified about new insertions/deletions.
   RAIIMFObsDelInstaller Installer(MF, WrapperObserver);
-  LegalizerHelper Helper(MF, LI, WrapperObserver, MIRBuilder, VT);
+  LegalizerHelper Helper(MF, LI, WrapperObserver, MIRBuilder, Libcalls, VT);
   LegalizationArtifactCombiner ArtCombiner(MIRBuilder, MRI, LI, VT);
   bool Changed = false;
   SmallVector<MachineInstr *, 128> RetryList;
@@ -339,13 +340,19 @@ bool Legalizer::runOnMachineFunction(MachineFunction &MF) {
   if (VerifyDebugLocs > DebugLocVerifyLevel::None)
     AuxObservers.push_back(&LocObserver);
 
+  const TargetSubtargetInfo &Subtarget = MF.getSubtarget();
+
+  const LibcallLoweringInfo &Libcalls =
+      getAnalysis<LibcallLoweringInfoWrapper>().getLibcallLowering(
+          *MF.getFunction().getParent(), Subtarget);
+
   // This allows Known Bits Analysis in the legalizer.
   GISelValueTracking *VT =
       &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
 
-  const LegalizerInfo &LI = *MF.getSubtarget().getLegalizerInfo();
+  const LegalizerInfo &LI = *Subtarget.getLegalizerInfo();
   MFResult Result = legalizeMachineFunction(MF, LI, AuxObservers, LocObserver,
-                                            *MIRBuilder, VT);
+                                            *MIRBuilder, &Libcalls, VT);
 
   if (Result.FailedOn) {
     reportGISelFailure(MF, MORE, "gisel-legalize",
diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index 1aa1d465d8da6..32cda979a0cda 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -104,16 +104,19 @@ static Type *getFloatTypeForLLT(LLVMContext &Ctx, LLT Ty) {
 
 LegalizerHelper::LegalizerHelper(MachineFunction &MF,
                                  GISelChangeObserver &Observer,
-                                 MachineIRBuilder &Builder)
+                                 MachineIRBuilder &Builder,
+                                 const LibcallLoweringInfo *Libcalls)
     : MIRBuilder(Builder), Observer(Observer), MRI(MF.getRegInfo()),
       LI(*MF.getSubtarget().getLegalizerInfo()),
-      TLI(*MF.getSubtarget().getTargetLowering()), VT(nullptr) {}
+      TLI(*MF.getSubtarget().getTargetLowering()), Libcalls(Libcalls) {}
 
 LegalizerHelper::LegalizerHelper(MachineFunction &MF, const LegalizerInfo &LI,
                                  GISelChangeObserver &Observer,
-                                 MachineIRBuilder &B, GISelValueTracking *VT)
+                                 MachineIRBuilder &B,
+                                 const LibcallLoweringInfo *Libcalls,
+                                 GISelValueTracking *VT)
     : MIRBuilder(B), Observer(Observer), MRI(MF.getRegInfo()), LI(LI),
-      TLI(*MF.getSubtarget().getTargetLowering()), VT(VT) {}
+      TLI(*MF.getSubtarget().getTargetLowering()), Libcalls(Libcalls), VT(VT) {}
 
 LegalizerHelper::LegalizeResult
 LegalizerHelper::legalizeInstrStep(MachineInstr &MI,
@@ -581,12 +584,10 @@ static bool isLibCallInTailPosition(const CallLowering::ArgInfo &Result,
   return true;
 }
 
-LegalizerHelper::LegalizeResult
-llvm::createLibcall(MachineIRBuilder &MIRBuilder, const char *Name,
-                    const CallLowering::ArgInfo &Result,
-                    ArrayRef<CallLowering::ArgInfo> Args,
-                    const CallingConv::ID CC, LostDebugLocObserver &LocObserver,
-                    MachineInstr *MI) {
+LegalizerHelper::LegalizeResult LegalizerHelper::createLibcall(
+    const char *Name, const CallLowering::ArgInfo &Result,
+    ArrayRef<CallLowering::ArgInfo> Args, const CallingConv::ID CC,
+    LostDebugLocObserver &LocObserver, MachineInstr *MI) const {
   auto &CLI = *MIRBuilder.getMF().getSubtarget().getCallLowering();
 
   CallLowering::CallLoweringInfo Info;
@@ -628,31 +629,36 @@ llvm::createLibcall(MachineIRBuilder &MIRBuilder, const char *Name,
   return LegalizerHelper::Legalized;
 }
 
-LegalizerHelper::LegalizeResult
-llvm::createLibcall(MachineIRBuilder &MIRBuilder, RTLIB::Libcall Libcall,
-                    const CallLowering::ArgInfo &Result,
-                    ArrayRef<CallLowering::ArgInfo> Args,
-                    LostDebugLocObserver &LocObserver, MachineInstr *MI) {
-  auto &TLI = *MIRBuilder.getMF().getSubtarget().getTargetLowering();
-  const char *Name = TLI.getLibcallName(Libcall);
-  if (!Name)
+LegalizerHelper::LegalizeResult LegalizerHelper::createLibcall(
+    RTLIB::Libcall Libcall, const CallLowering::ArgInfo &Result,
+    ArrayRef<CallLowering::ArgInfo> Args, LostDebugLocObserver &LocObserver,
+    MachineInstr *MI) const {
+  if (!Libcalls)
     return LegalizerHelper::UnableToLegalize;
-  const CallingConv::ID CC = TLI.getLibcallCallingConv(Libcall);
-  return createLibcall(MIRBuilder, Name, Result, Args, CC, LocObserver, MI);
+
+  RTLIB::LibcallImpl LibcallImpl = Libcalls->getLibcallImpl(Libcall);
+  if (LibcallImpl == RTLIB::Unsupported)
+    return LegalizerHelper::UnableToLegalize;
+
+  auto &TLI = *MIRBuilder.getMF().getSubtarget().getTargetLowering();
+
+  StringRef Name = RTLIB::RuntimeLibcallsInfo::getLibcallImplName(LibcallImpl);
+  const CallingConv::ID CC = TLI.getLibcallImplCallingConv(LibcallImpl);
+  return createLibcall(Name.data(), Result, Args, CC, LocObserver, MI);
 }
 
 // Useful for libcalls where all operands have the same type.
-static LegalizerHelper::LegalizeResult
-simpleLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder, unsigned Size,
-              Type *OpType, LostDebugLocObserver &LocObserver) {
+LegalizerHelper::LegalizeResult
+LegalizerHelper::simpleLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder,
+                               unsigned Size, Type *OpType,
+                               LostDebugLocObserver &LocObserver) const {
   auto Libcall = getRTLibDesc(MI.getOpcode(), Size);
 
   // FIXME: What does the original arg index mean here?
   SmallVector<CallLowering::ArgInfo, 3> Args;
   for (const MachineOperand &MO : llvm::drop_begin(MI.operands()))
     Args.push_back({MO.getReg(), OpType, 0});
-  return createLibcall(MIRBuilder, Libcall,
-                       {MI.getOperand(0).getReg(), OpType, 0}, Args,
+  return createLibcall(Libcall, {MI.getOperand(0).getReg(), OpType, 0}, Args,
                        LocObserver, &MI);
 }
 
@@ -681,13 +687,12 @@ LegalizerHelper::LegalizeResult LegalizerHelper::emitSincosLibcall(
           .getReg(0);
 
   auto &Ctx = MF.getFunction().getContext();
-  auto LibcallResult =
-      createLibcall(MIRBuilder, getRTLibDesc(MI.getOpcode(), Size),
-                    {{0}, Type::getVoidTy(Ctx), 0},
-                    {{Src, OpType, 0},
-                     {StackPtrSin, PointerType::get(Ctx, AddrSpace), 1},
-                     {StackPtrCos, PointerType::get(Ctx, AddrSpace), 2}},
-                    LocObserver, &MI);
+  auto LibcallResult = createLibcall(
+      getRTLibDesc(MI.getOpcode(), Size), {{0}, Type::getVoidTy(Ctx), 0},
+      {{Src, OpType, 0},
+       {StackPtrSin, PointerType::get(Ctx, AddrSpace), 1},
+       {StackPtrCos, PointerType::get(Ctx, AddrSpace), 2}},
+      LocObserver, &MI);
 
   if (LibcallResult != LegalizeResult::Legalized)
     return LegalizerHelper::UnableToLegalize;
@@ -728,7 +733,7 @@ LegalizerHelper::emitModfLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder,
 
   auto &Ctx = MF.getFunction().getContext();
   auto Libc...
[truncated]

@arsenm arsenm removed backend:PowerPC backend:loongarch backend:AMDGPU backend:AArch64 clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. llvm:transforms llvm:analysis Includes value tracking, cost tables and constant folding labels Dec 2, 2025
@arsenm arsenm marked this pull request as ready for review December 2, 2025 17:14
@llvmbot llvmbot added backend:ARM llvm:analysis Includes value tracking, cost tables and constant folding labels Dec 2, 2025
This is mostly boilerplate to move various freestanding utility
functions into LegalizerHelper. LibcallLoweringInfo is currently
optional, mostly because threading it through assorted other
uses of LegalizerHelper is more difficult.

I had a lot of trouble getting this to work in the legacy pass
manager with setRequiresCodeGenSCCOrder, and am not happy with the
result. A sub-pass manager is introduced and this is invalidated,
so we're re-computing this unnecessarily.
@arsenm arsenm force-pushed the users/arsenm/global-isel/use-libcall-lowering-info-legalizer-helper branch from 17769e5 to 0c95e55 Compare December 2, 2025 17:38
Base automatically changed from users/arsenm/codegen/add-libcall-lowering-info-analysis to main December 3, 2025 21:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:ARM llvm:analysis Includes value tracking, cost tables and constant folding llvm:codegen llvm:globalisel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants