[llvm] [InstCombine] Merge mallocs followed directly by reallocs (PR #207513)

Nikolas Klauser via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 4 06:42:52 PDT 2026


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207513

None

>From 1a2a08c9b057c14d3550bc75b4c69273dd9b22fc Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 4 Jul 2026 15:42:18 +0200
Subject: [PATCH] [InstCombine] Merge mallocs followed directly by reallocs

---
 llvm/include/llvm/Analysis/MemoryBuiltins.h   |  5 +-
 llvm/lib/Analysis/MemoryBuiltins.cpp          | 11 ++-
 .../InstCombine/InstCombineCalls.cpp          | 36 ++++++++
 .../InstCombine/InstCombineInternal.h         |  1 +
 llvm/test/Transforms/InstCombine/realloc.ll   | 90 ++++++++++++++++++-
 5 files changed, 139 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/Analysis/MemoryBuiltins.h b/llvm/include/llvm/Analysis/MemoryBuiltins.h
index a42d662146563..2e9e7120524a9 100644
--- a/llvm/include/llvm/Analysis/MemoryBuiltins.h
+++ b/llvm/include/llvm/Analysis/MemoryBuiltins.h
@@ -69,7 +69,7 @@ LLVM_ABI bool isAllocLikeFn(const Value *V, const TargetLibraryInfo *TLI);
 
 /// Tests if a function is a call or invoke to a library function that
 /// reallocates memory (e.g., realloc).
-LLVM_ABI bool isReallocLikeFn(const Function *F);
+LLVM_ABI bool isReallocLikeFn(const Value *F);
 
 /// If this is a call to a realloc function, return the reallocated operand.
 LLVM_ABI Value *getReallocatedOperand(const CallBase *CB);
@@ -118,6 +118,9 @@ LLVM_ABI std::optional<APInt> getAllocSize(
       return V;
     });
 
+/// Return the Use of the allocation size argument if there exists one.
+LLVM_ABI Use *getAllocSizeArg(CallBase *CB, const TargetLibraryInfo *TLI);
+
 /// If this is a call to an allocation function that initializes memory to a
 /// fixed value, return said value in the requested type.  Otherwise, return
 /// nullptr.
diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp
index 0364a8bff9e53..a78a8d8cc4632 100644
--- a/llvm/lib/Analysis/MemoryBuiltins.cpp
+++ b/llvm/lib/Analysis/MemoryBuiltins.cpp
@@ -308,7 +308,7 @@ bool llvm::isAllocLikeFn(const Value *V, const TargetLibraryInfo *TLI) {
 
 /// Tests if a functions is a call or invoke to a library function that
 /// reallocates memory (e.g., realloc).
-bool llvm::isReallocLikeFn(const Function *F) {
+bool llvm::isReallocLikeFn(const Value *F) {
   return checkFnAllocKind(F, AllocFnKind::Realloc);
 }
 
@@ -354,6 +354,15 @@ static bool checkedZextOrTrunc(APInt &I, unsigned IntTyBits) {
   return true;
 }
 
+Use *llvm::getAllocSizeArg(CallBase *CB, const TargetLibraryInfo *TLI) {
+  std::optional<AllocFnsTy> FnData = getAllocationSize(CB, TLI);
+  if (!FnData)
+    return nullptr;
+  if (FnData->FstParam == -1 || FnData->SndParam != -1)
+    return nullptr;
+  return &CB->getArgOperandUse(FnData->FstParam);
+}
+
 std::optional<APInt>
 llvm::getAllocSize(const CallBase *CB, const TargetLibraryInfo *TLI,
                    function_ref<const Value *(const Value *)> Mapper) {
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index dcda2f57648d1..6c9780a1bf9ff 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -4882,6 +4882,39 @@ bool InstCombinerImpl::annotateAnyAllocSite(CallBase &Call,
   return Changed;
 }
 
+Instruction *InstCombinerImpl::visitReallocSite(CallBase &Call) {
+  assert(isReallocLikeFn(&Call));
+
+  CallBase *Realloced =
+      dyn_cast_or_null<CallBase>(getReallocatedOperand(&Call));
+
+  if (!Realloced || Realloced->getParent() != Call.getParent() ||
+      !isAllocLikeFn(Realloced, &TLI))
+    return nullptr;
+
+  if (getAllocationFamily(&Call, &TLI) != getAllocationFamily(Realloced, &TLI))
+    return nullptr;
+
+  if (getAllocAlignment(&Call, &TLI) != getAllocAlignment(Realloced, &TLI))
+    return nullptr;
+
+  for (Instruction &Inst :
+       make_range(++Realloced->getIterator(), Call.getIterator())) {
+    if (Inst.mayReadOrWriteMemory())
+      return nullptr;
+  }
+
+  Realloced->moveBefore(Call.getIterator());
+  Use *AllocSizeArg = getAllocSizeArg(Realloced, &TLI);
+  Use *ReallocSizeArg = getAllocSizeArg(&Call, &TLI);
+  if (!AllocSizeArg || !ReallocSizeArg)
+    return nullptr;
+
+  replaceUse(*AllocSizeArg, *ReallocSizeArg);
+  replaceInstUsesWith(Call, Realloced);
+  return eraseInstFromFunction(Call);
+}
+
 /// Improvements for call, callbr and invoke instructions.
 Instruction *InstCombinerImpl::visitCallBase(CallBase &Call) {
   bool Changed = annotateAnyAllocSite(Call, &TLI);
@@ -5066,6 +5099,9 @@ Instruction *InstCombinerImpl::visitCallBase(CallBase &Call) {
   if (isRemovableAlloc(&Call, &TLI))
     return visitAllocSite(Call);
 
+  if (isReallocLikeFn(&Call))
+    return visitReallocSite(Call);
+
   // Handle intrinsics which can be used in both call and invoke context.
   switch (Call.getIntrinsicID()) {
   case Intrinsic::experimental_gc_statepoint: {
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 8b759e701da60..81f0b73160cf9 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -171,6 +171,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   Instruction *visitGEPOfGEP(GetElementPtrInst &GEP, GEPOperator *Src);
   Instruction *visitAllocaInst(AllocaInst &AI);
   Instruction *visitAllocSite(Instruction &FI);
+  Instruction *visitReallocSite(CallBase &);
   Instruction *visitFree(CallInst &FI, Value *FreedOp);
   Instruction *visitLoadInst(LoadInst &LI);
   Instruction *visitStoreInst(StoreInst &SI);
diff --git a/llvm/test/Transforms/InstCombine/realloc.ll b/llvm/test/Transforms/InstCombine/realloc.ll
index 1542707d79c1a..ab751533af4fc 100644
--- a/llvm/test/Transforms/InstCombine/realloc.ll
+++ b/llvm/test/Transforms/InstCombine/realloc.ll
@@ -1,9 +1,14 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
 
-declare ptr @realloc(ptr allocptr, i64) allockind("realloc") allocsize(1)
-declare noalias ptr @malloc(i64) allockind("alloc,uninitialized")
+declare ptr @realloc(ptr allocptr, i64) allockind("realloc") allocsize(1) "alloc-family"="malloc"
+declare noalias ptr @malloc(i64) allockind("alloc,uninitialized") "alloc-family"="malloc"
+declare noalias ptr @aligned_alloc(i64, i64 allocalign) allockind("alloc,uninitialized,aligned") "alloc-family"="malloc"
 
+declare ptr @realloc2(ptr allocptr, i64) allockind("realloc") allocsize(1) "alloc-family"="malloc2"
+declare noalias ptr @malloc2(i64) allockind("alloc,uninitialized") "alloc-family"="malloc2"
+
+declare void @use_i64(i64)
 
 define ptr @realloc_null_ptr() #0 {
 ; CHECK-LABEL: @realloc_null_ptr(
@@ -22,3 +27,84 @@ define ptr @realloc_unknown_ptr(ptr %ptr) #0 {
   %call = call ptr @realloc(ptr %ptr, i64 100) #2
   ret ptr %call
 }
+
+define ptr @realloc_directly_after_alloc_shrinking() {
+; CHECK-LABEL: @realloc_directly_after_alloc_shrinking(
+; CHECK-NEXT:    [[ALLOC:%.*]] = call dereferenceable_or_null(1) ptr @malloc(i64 1)
+; CHECK-NEXT:    ret ptr [[ALLOC]]
+;
+  %alloc = call ptr @malloc(i64 3)
+  %realloc = call ptr @realloc(ptr %alloc, i64 1)
+  ret ptr %realloc
+}
+
+define ptr @realloc_directly_after_alloc_growing() {
+; CHECK-LABEL: @realloc_directly_after_alloc_growing(
+; CHECK-NEXT:    [[ALLOC:%.*]] = call dereferenceable_or_null(3) ptr @malloc(i64 3)
+; CHECK-NEXT:    ret ptr [[ALLOC]]
+;
+  %alloc = call ptr @malloc(i64 1)
+  %realloc = call ptr @realloc(ptr %alloc, i64 3)
+  ret ptr %realloc
+}
+
+define ptr @realloc_directly_after_alloc_identity() {
+; CHECK-LABEL: @realloc_directly_after_alloc_identity(
+; CHECK-NEXT:    [[ALLOC:%.*]] = call dereferenceable_or_null(1) ptr @malloc(i64 1)
+; CHECK-NEXT:    ret ptr [[ALLOC]]
+;
+  %alloc = call ptr @malloc(i64 1)
+  %realloc = call ptr @realloc(ptr %alloc, i64 1)
+  ret ptr %realloc
+}
+
+define ptr @realloc_directly_after_alloc_family_mismatch() {
+; CHECK-LABEL: @realloc_directly_after_alloc_family_mismatch(
+; CHECK-NEXT:    [[ALLOC:%.*]] = call dereferenceable_or_null(1) ptr @malloc(i64 1)
+; CHECK-NEXT:    [[REALLOC:%.*]] = call dereferenceable_or_null(1) ptr @realloc2(ptr [[ALLOC]], i64 1)
+; CHECK-NEXT:    ret ptr [[REALLOC]]
+;
+  %alloc = call ptr @malloc(i64 1)
+  %realloc = call ptr @realloc2(ptr %alloc, i64 1)
+  ret ptr %realloc
+}
+
+define ptr @realloc_directly_after_alloc_alignment_mismatch() {
+; CHECK-LABEL: @realloc_directly_after_alloc_alignment_mismatch(
+; CHECK-NEXT:    [[ALLOC:%.*]] = call ptr @aligned_alloc(i64 1, i64 1)
+; CHECK-NEXT:    [[REALLOC:%.*]] = call dereferenceable_or_null(1) ptr @realloc(ptr [[ALLOC]], i64 1)
+; CHECK-NEXT:    ret ptr [[REALLOC]]
+;
+  %alloc = call ptr @aligned_alloc(i64 1, i64 1)
+  %realloc = call ptr @realloc(ptr %alloc, i64 1)
+  ret ptr %realloc
+}
+
+define ptr @realloc_with_non_memory_instructions(ptr %ptr) {
+; CHECK-LABEL: @realloc_with_non_memory_instructions(
+; CHECK-NEXT:    [[INT:%.*]] = ptrtoint ptr [[PTR:%.*]] to i64
+; CHECK-NEXT:    [[REALLOC:%.*]] = call dereferenceable_or_null(1) ptr @malloc(i64 1)
+; CHECK-NEXT:    call void @use_i64(i64 [[INT]])
+; CHECK-NEXT:    ret ptr [[REALLOC]]
+;
+  %alloc = call ptr @malloc(i64 1)
+  %int = ptrtoint ptr %ptr to i64
+  %realloc = call ptr @realloc(ptr %alloc, i64 1)
+  call void @use_i64(i64 %int)
+  ret ptr %realloc
+}
+
+define ptr @realloc_with_memory_instructions(ptr %ptr) {
+; CHECK-LABEL: @realloc_with_memory_instructions(
+; CHECK-NEXT:    [[ALLOC:%.*]] = call dereferenceable_or_null(1) ptr @malloc(i64 1)
+; CHECK-NEXT:    [[INT:%.*]] = ptrtoint ptr [[PTR:%.*]] to i64
+; CHECK-NEXT:    call void @use_i64(i64 [[INT]])
+; CHECK-NEXT:    [[REALLOC:%.*]] = call dereferenceable_or_null(1) ptr @realloc(ptr [[ALLOC]], i64 1)
+; CHECK-NEXT:    ret ptr [[REALLOC]]
+;
+  %alloc = call ptr @malloc(i64 1)
+  %int = ptrtoint ptr %ptr to i64
+  call void @use_i64(i64 %int)
+  %realloc = call ptr @realloc(ptr %alloc, i64 1)
+  ret ptr %realloc
+}



More information about the llvm-commits mailing list