[llvm] [ThinLTO] Don't mark calloc function dead (PR #72673)

via llvm-commits llvm-commits at lists.llvm.org
Fri Nov 17 08:11:55 PST 2023


https://github.com/eleviant created https://github.com/llvm/llvm-project/pull/72673

Dead store elimination pass may fold malloc + memset calls into a single call to calloc. If calloc is not preserved and is not being called directly it can be marked dead during thin link and result in link error.

>From eb9ba8ad9b3d7e3d31d2d13580edd182cc5e2ff2 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Fri, 17 Nov 2023 15:32:24 +0300
Subject: [PATCH] [ThinLTO] Don't mark calloc function dead

Dead store elimination pass may fold malloc + memset calls into a
single call to calloc. If calloc is not preserved and is not being
called directly it can be marked dead during thin link and result
in link error.
---
 llvm/lib/LTO/LTO.cpp                   |  8 +++-
 llvm/test/ThinLTO/X86/Inputs/calloc.ll | 48 +++++++++++++++++++++
 llvm/test/ThinLTO/X86/call-calloc.ll   | 60 ++++++++++++++++++++++++++
 3 files changed, 115 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/ThinLTO/X86/Inputs/calloc.ll
 create mode 100644 llvm/test/ThinLTO/X86/call-calloc.ll

diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index e111e09681178e2..8614f1c9bbcd680 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -606,6 +606,8 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
   auto *ResE = Res.end();
   (void)ResE;
   const Triple TT(RegularLTO.CombinedModule->getTargetTriple());
+  TargetLibraryInfoImpl TLII(TT);
+  StringRef CallocName = TargetLibraryInfo(TLII).getName(LibFunc_calloc);
   for (const InputFile::Symbol &Sym : Syms) {
     assert(ResI != ResE);
     SymbolResolution Res = *ResI++;
@@ -644,7 +646,11 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
     // FIXME: instead of this check, it would be desirable to compute GUIDs
     // based on mangled name, but this requires an access to the Target Triple
     // and would be relatively invasive on the codebase.
-    if (GlobalRes.IRName != Sym.getIRName()) {
+    if ((GlobalRes.IRName != Sym.getIRName()) ||
+        // The dead store elimination pass can fold malloc + memset calls into
+        // a single call to calloc. Prevent thin LTO from marking calloc a dead
+        // function otherwise we may face link errors.
+        (!CallocName.empty() && GlobalRes.IRName == CallocName)) {
       GlobalRes.Partition = GlobalResolution::External;
       GlobalRes.VisibleOutsideSummary = true;
     }
diff --git a/llvm/test/ThinLTO/X86/Inputs/calloc.ll b/llvm/test/ThinLTO/X86/Inputs/calloc.ll
new file mode 100644
index 000000000000000..a8a522a29cbd8a4
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/Inputs/calloc.ll
@@ -0,0 +1,48 @@
+; ModuleID = 'calloc.o'
+source_filename = "calloc.cpp"
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-unknown-linux-gnu"
+
+ at _ZZ6mallocE3buf = internal global [2000 x i8] zeroinitializer, align 1
+
+define hidden noalias ptr @malloc(i64 %a) local_unnamed_addr !type !7 !type !8 {
+entry:
+  tail call void asm sideeffect "dsb sy", "~{memory}"(), !srcloc !9
+  ret ptr @_ZZ6mallocE3buf
+}
+
+define hidden void @free(ptr nocapture noundef readnone %p) local_unnamed_addr !type !10 !type !11 {
+entry:
+  tail call void asm sideeffect "dsb nsh", "~{memory}"(), !srcloc !12
+  ret void
+}
+
+define hidden noalias nonnull ptr @calloc(i64 noundef %num, i64 noundef %size) local_unnamed_addr !type !13 !type !14 {
+entry:
+  %mul = mul i64 %size, %num
+  %call = tail call noalias ptr @malloc(i64 poison)
+  tail call void @llvm.memset.p0.i64(ptr nonnull align 1 @_ZZ6mallocE3buf, i8 0, i64 %mul, i1 false)
+  ret ptr @_ZZ6mallocE3buf
+}
+
+; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
+declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg)
+
+!llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 4, !"CFI Canonical Jump Tables", i32 1}
+!2 = !{i32 8, !"PIC Level", i32 2}
+!3 = !{i32 7, !"uwtable", i32 2}
+!4 = !{i32 7, !"frame-pointer", i32 1}
+!5 = !{i32 1, !"EnableSplitLTOUnit", i32 1}
+!6 = !{!"clang version 18.0.0 (https://github.com/llvm/llvm-project.git a902ca66428164b4f11dedf1c551393add511271)"}
+!7 = !{i64 0, !"_ZTSFPvmE"}
+!8 = !{i64 0, !"_ZTSFPvmE.generalized"}
+!9 = !{i64 194}
+!10 = !{i64 0, !"_ZTSFvPvE"}
+!11 = !{i64 0, !"_ZTSFvPvE.generalized"}
+!12 = !{i64 288}
+!13 = !{i64 0, !"_ZTSFPvmmE"}
+!14 = !{i64 0, !"_ZTSFPvmmE.generalized"}
diff --git a/llvm/test/ThinLTO/X86/call-calloc.ll b/llvm/test/ThinLTO/X86/call-calloc.ll
new file mode 100644
index 000000000000000..41e002cecbcc2c7
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/call-calloc.ll
@@ -0,0 +1,60 @@
+; RUN: opt -module-summary %s -o %t1.bc
+; RUN: opt -module-summary %p/Inputs/calloc.ll -o %t2.bc
+; RUN: llvm-lto2 run -save-temps -o %t.out \
+; RUN:   %t1.bc -r=%t1.bc,_Z7DoStuffv,px -r=%t1.bc,_ZnwmRKSt9nothrow_t,px \
+; RUN:          -r=%t1.bc,__gxx_personality_v0, -r=%t1.bc,_ZSt7nothrow, -r=%t1.bc,malloc,l \
+; RUN:   %t2.bc -r=%t2.bc,malloc,pl -r=%t2.bc,free,pl -r=%t2.bc,calloc,pl
+; RUN: llvm-dis %t.out.2.5.precodegen.bc -o - | FileCheck %s
+
+; CHECK: define {{.*}} @calloc
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-unknown-linux-gnu"
+
+%"struct.std::nothrow_t" = type { i8 }
+
+ at _ZSt7nothrow = external global %"struct.std::nothrow_t", align 1
+
+declare !type !12 !type !13 noalias noundef ptr @malloc(i64 noundef) local_unnamed_addr
+
+define noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef %size, ptr noundef nonnull align 1 dereferenceable(1) %0) local_unnamed_addr !type !10 !type !11 {
+entry:
+  %call = tail call noalias ptr @malloc(i64 noundef %size)
+  ret ptr %call
+}
+
+define void @_Z7DoStuffv() local_unnamed_addr personality ptr @__gxx_personality_v0 !type !7 !type !8 {
+entry:
+  %call = tail call noalias noundef dereferenceable_or_null(400) ptr @_ZnwmRKSt9nothrow_t(i64 noundef 400, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow)
+  %new.isnull = icmp ne ptr %call, null
+  tail call void @llvm.assume(i1 %new.isnull)
+  tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(400) %call, i8 0, i64 400, i1 false)
+  tail call void asm sideeffect "str xzr, [$0]", "r,~{memory}"(ptr nonnull %call), !srcloc !9
+  ret void
+}
+
+declare i32 @__gxx_personality_v0(...)
+
+; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
+declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg)
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
+declare void @llvm.assume(i1 noundef)
+
+!llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 4, !"CFI Canonical Jump Tables", i32 1}
+!2 = !{i32 8, !"PIC Level", i32 2}
+!3 = !{i32 7, !"uwtable", i32 2}
+!4 = !{i32 7, !"frame-pointer", i32 1}
+!5 = !{i32 1, !"EnableSplitLTOUnit", i32 1}
+!6 = !{!"clang version 18.0.0 (https://github.com/llvm/llvm-project.git a902ca66428164b4f11dedf1c551393add511271)"}
+!7 = !{i64 0, !"_ZTSFvvE"}
+!8 = !{i64 0, !"_ZTSFvvE.generalized"}
+!9 = !{i64 125}
+!10 = !{i64 0, !"_ZTSFPvmRKSt9nothrow_tE"}
+!11 = !{i64 0, !"_ZTSFPvmRKSt9nothrow_tE.generalized"}
+!12 = !{i64 0, !"_ZTSFPvmE"}
+!13 = !{i64 0, !"_ZTSFPvmE.generalized"}



More information about the llvm-commits mailing list