[llvm] [Inline][CloneAndPruneFunctionInto] Consider the nonnull argument for null check (PR #177235)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 21 12:52:19 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Shimin Cui (scui-ibm)

<details>
<summary>Changes</summary>

Currently, for null check of a callee parameter, inline cost analysis checks if the corresponding argument at call site is marked nonnull and, if yes, the resulting dead blocks after the null check are excluded from the cost computation. But those dead blocks from the callee are still inlined into the caller. This is to teach inliner not to inline those dead blocks into the caller. This can reduce the code size of the caller after inlining.

---
Full diff: https://github.com/llvm/llvm-project/pull/177235.diff


4 Files Affected:

- (modified) llvm/include/llvm/Transforms/Utils/Cloning.h (+11-8) 
- (modified) llvm/lib/Transforms/Utils/CloneFunction.cpp (+36-19) 
- (modified) llvm/lib/Transforms/Utils/InlineFunction.cpp (+1-1) 
- (modified) llvm/test/Transforms/Inline/nonnull.ll (-16) 


``````````diff
diff --git a/llvm/include/llvm/Transforms/Utils/Cloning.h b/llvm/include/llvm/Transforms/Utils/Cloning.h
index cfa06a5be79fd..831163a8c6d51 100644
--- a/llvm/include/llvm/Transforms/Utils/Cloning.h
+++ b/llvm/include/llvm/Transforms/Utils/Cloning.h
@@ -225,10 +225,10 @@ LLVM_ABI void CloneFunctionBodyInto(
     const MetadataPredicate *IdentityMD = nullptr);
 
 LLVM_ABI void CloneAndPruneIntoFromInst(
-    Function *NewFunc, const Function *OldFunc, const Instruction *StartingInst,
-    ValueToValueMapTy &VMap, bool ModuleLevelChanges,
-    SmallVectorImpl<ReturnInst *> &Returns, const char *NameSuffix = "",
-    ClonedCodeInfo *CodeInfo = nullptr);
+    CallBase &CB, Function *NewFunc, const Function *OldFunc,
+    const Instruction *StartingInst, ValueToValueMapTy &VMap,
+    bool ModuleLevelChanges, SmallVectorImpl<ReturnInst *> &Returns,
+    const char *NameSuffix = "", ClonedCodeInfo *CodeInfo = nullptr);
 
 /// This works exactly like CloneFunctionInto,
 /// except that it does some simple constant prop and DCE on the fly.  The
@@ -241,10 +241,13 @@ LLVM_ABI void CloneAndPruneIntoFromInst(
 /// If ModuleLevelChanges is false, VMap contains no non-identity GlobalValue
 /// mappings.
 ///
-LLVM_ABI void CloneAndPruneFunctionInto(
-    Function *NewFunc, const Function *OldFunc, ValueToValueMapTy &VMap,
-    bool ModuleLevelChanges, SmallVectorImpl<ReturnInst *> &Returns,
-    const char *NameSuffix = "", ClonedCodeInfo *CodeInfo = nullptr);
+LLVM_ABI void CloneAndPruneFunctionInto(CallBase &CB, Function *NewFunc,
+                                        const Function *OldFunc,
+                                        ValueToValueMapTy &VMap,
+                                        bool ModuleLevelChanges,
+                                        SmallVectorImpl<ReturnInst *> &Returns,
+                                        const char *NameSuffix = "",
+                                        ClonedCodeInfo *CodeInfo = nullptr);
 
 /// This class captures the data input to the InlineFunction call, and records
 /// the auxiliary results produced by it.
diff --git a/llvm/lib/Transforms/Utils/CloneFunction.cpp b/llvm/lib/Transforms/Utils/CloneFunction.cpp
index 260dc3a12df43..46ae257fef5d0 100644
--- a/llvm/lib/Transforms/Utils/CloneFunction.cpp
+++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp
@@ -429,7 +429,7 @@ struct PruningFunctionCloner {
   /// The specified block is found to be reachable, clone it and
   /// anything that it can reach.
   void CloneBlock(const BasicBlock *BB, BasicBlock::const_iterator StartingInst,
-                  std::vector<const BasicBlock *> &ToClone);
+                  std::vector<const BasicBlock *> &ToClone, CallBase &CB);
 };
 } // namespace
 
@@ -506,9 +506,10 @@ PruningFunctionCloner::cloneInstruction(BasicBlock::const_iterator II) {
 
 /// The specified block is found to be reachable, clone it and
 /// anything that it can reach.
-void PruningFunctionCloner::CloneBlock(
-    const BasicBlock *BB, BasicBlock::const_iterator StartingInst,
-    std::vector<const BasicBlock *> &ToClone) {
+void PruningFunctionCloner::CloneBlock(const BasicBlock *BB,
+                                       BasicBlock::const_iterator StartingInst,
+                                       std::vector<const BasicBlock *> &ToClone,
+                                       CallBase &CB) {
   WeakTrackingVH &BBEntry = VMap[BB];
 
   // Have we already cloned this block?
@@ -639,6 +640,20 @@ void PruningFunctionCloner::CloneBlock(
         VMap[OldTI] = NewBI;
         ToClone.push_back(Dest);
         TerminatorDone = true;
+      } else {
+        if (auto *CI = dyn_cast<CmpInst>(BI->getCondition()))
+          if (auto *A = dyn_cast<Argument>(CI->getOperand(0)))
+            if (CI->isEquality() &&
+                isa<ConstantPointerNull>(CI->getOperand(1)) &&
+                CB.paramHasAttr(A->getArgNo(), Attribute::NonNull)) {
+              bool IsEqual = CI->getPredicate() == CmpInst::ICMP_EQ;
+              BasicBlock *Dest = BI->getSuccessor(IsEqual);
+              auto *NewBI = BranchInst::Create(Dest, NewBB);
+              NewBI->setDebugLoc(BI->getDebugLoc());
+              VMap[OldTI] = NewBI;
+              ToClone.push_back(Dest);
+              TerminatorDone = true;
+            }
       }
     }
   } else if (const SwitchInst *SI = dyn_cast<SwitchInst>(OldTI)) {
@@ -699,13 +714,11 @@ void PruningFunctionCloner::CloneBlock(
 /// This works like CloneAndPruneFunctionInto, except that it does not clone the
 /// entire function. Instead it starts at an instruction provided by the caller
 /// and copies (and prunes) only the code reachable from that instruction.
-void llvm::CloneAndPruneIntoFromInst(Function *NewFunc, const Function *OldFunc,
-                                     const Instruction *StartingInst,
-                                     ValueToValueMapTy &VMap,
-                                     bool ModuleLevelChanges,
-                                     SmallVectorImpl<ReturnInst *> &Returns,
-                                     const char *NameSuffix,
-                                     ClonedCodeInfo *CodeInfo) {
+void llvm::CloneAndPruneIntoFromInst(
+    CallBase &CB, Function *NewFunc, const Function *OldFunc,
+    const Instruction *StartingInst, ValueToValueMapTy &VMap,
+    bool ModuleLevelChanges, SmallVectorImpl<ReturnInst *> &Returns,
+    const char *NameSuffix, ClonedCodeInfo *CodeInfo) {
   assert(NameSuffix && "NameSuffix cannot be null!");
 
   ValueMapTypeRemapper *TypeMapper = nullptr;
@@ -731,11 +744,11 @@ void llvm::CloneAndPruneIntoFromInst(Function *NewFunc, const Function *OldFunc,
 
   // Clone the entry block, and anything recursively reachable from it.
   std::vector<const BasicBlock *> CloneWorklist;
-  PFC.CloneBlock(StartingBB, StartingInst->getIterator(), CloneWorklist);
+  PFC.CloneBlock(StartingBB, StartingInst->getIterator(), CloneWorklist, CB);
   while (!CloneWorklist.empty()) {
     const BasicBlock *BB = CloneWorklist.back();
     CloneWorklist.pop_back();
-    PFC.CloneBlock(BB, BB->begin(), CloneWorklist);
+    PFC.CloneBlock(BB, BB->begin(), CloneWorklist, CB);
   }
 
   // Loop over all of the basic blocks in the old function.  If the block was
@@ -985,12 +998,16 @@ void llvm::CloneAndPruneIntoFromInst(Function *NewFunc, const Function *OldFunc,
 /// constant arguments cause a significant amount of code in the callee to be
 /// dead.  Since this doesn't produce an exact copy of the input, it can't be
 /// used for things like CloneFunction or CloneModule.
-void llvm::CloneAndPruneFunctionInto(
-    Function *NewFunc, const Function *OldFunc, ValueToValueMapTy &VMap,
-    bool ModuleLevelChanges, SmallVectorImpl<ReturnInst *> &Returns,
-    const char *NameSuffix, ClonedCodeInfo *CodeInfo) {
-  CloneAndPruneIntoFromInst(NewFunc, OldFunc, &OldFunc->front().front(), VMap,
-                            ModuleLevelChanges, Returns, NameSuffix, CodeInfo);
+void llvm::CloneAndPruneFunctionInto(CallBase &CB, Function *NewFunc,
+                                     const Function *OldFunc,
+                                     ValueToValueMapTy &VMap,
+                                     bool ModuleLevelChanges,
+                                     SmallVectorImpl<ReturnInst *> &Returns,
+                                     const char *NameSuffix,
+                                     ClonedCodeInfo *CodeInfo) {
+  CloneAndPruneIntoFromInst(CB, NewFunc, OldFunc, &OldFunc->front().front(),
+                            VMap, ModuleLevelChanges, Returns, NameSuffix,
+                            CodeInfo);
 }
 
 /// Remaps instructions in \p Blocks using the mapping in \p VMap.
diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index aa902f687d8aa..3cc3e3e2124a6 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -2714,7 +2714,7 @@ void llvm::InlineFunctionImpl(CallBase &CB, InlineFunctionInfo &IFI,
     // have no dead or constant instructions leftover after inlining occurs
     // (which can happen, e.g., because an argument was constant), but we'll be
     // happy with whatever the cloner can do.
-    CloneAndPruneFunctionInto(Caller, CalledFunc, VMap,
+    CloneAndPruneFunctionInto(CB, Caller, CalledFunc, VMap,
                               /*ModuleLevelChanges=*/false, Returns, ".i",
                               &InlinedFunctionInfo);
     // Remember the first block that is newly cloned over.
diff --git a/llvm/test/Transforms/Inline/nonnull.ll b/llvm/test/Transforms/Inline/nonnull.ll
index 9c0a225c57646..a6e79f5075111 100644
--- a/llvm/test/Transforms/Inline/nonnull.ll
+++ b/llvm/test/Transforms/Inline/nonnull.ll
@@ -76,23 +76,7 @@ define void @caller3(ptr %arg) {
 ; CHECK-LABEL: define void @caller3
 ; CHECK-SAME: (ptr [[ARG:%.*]]) {
 ; CHECK-NEXT:    [[CMP_I:%.*]] = icmp eq ptr [[ARG]], null
-; CHECK-NEXT:    br i1 [[CMP_I]], label [[EXPENSIVE_I:%.*]], label [[DONE_I:%.*]]
-; CHECK:       expensive.i:
-; CHECK-NEXT:    call void @foo()
-; CHECK-NEXT:    call void @foo()
-; CHECK-NEXT:    call void @foo()
-; CHECK-NEXT:    call void @foo()
-; CHECK-NEXT:    call void @foo()
-; CHECK-NEXT:    call void @foo()
-; CHECK-NEXT:    call void @foo()
-; CHECK-NEXT:    call void @foo()
-; CHECK-NEXT:    call void @foo()
-; CHECK-NEXT:    call void @foo()
-; CHECK-NEXT:    br label [[CALLEE_EXIT:%.*]]
-; CHECK:       done.i:
 ; CHECK-NEXT:    call void @bar()
-; CHECK-NEXT:    br label [[CALLEE_EXIT]]
-; CHECK:       callee.exit:
 ; CHECK-NEXT:    ret void
 ;
   call void @callee(ptr nonnull %arg)

``````````

</details>


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


More information about the llvm-commits mailing list