[llvm] [TLI] Add support for inferring attr `cold` on `exit`/`abort` (PR #101003)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 29 05:46:08 PDT 2024


https://github.com/goldsteinn updated https://github.com/llvm/llvm-project/pull/101003

>From f5d2444e9fdfb1543d3977724e049f9894a46df1 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Mon, 29 Jul 2024 20:02:54 +0800
Subject: [PATCH 1/3] [InstCombine][InferFunctionAttrs] Add tests for inferring
 `cold` on exit/abort; NFC

---
 .../Transforms/InferFunctionAttrs/annotate.ll |  2 +
 .../Transforms/InstCombine/lib-call-exit.ll   | 42 +++++++++++++++++++
 2 files changed, 44 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/lib-call-exit.ll

diff --git a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
index 79962b45d253a..9a5b902210009 100644
--- a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
+++ b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
@@ -1088,6 +1088,8 @@ declare i32 @vsscanf(ptr, ptr, ptr)
 ; CHECK: declare noundef i64 @write(i32 noundef, ptr nocapture noundef readonly, i64 noundef) [[NOFREE]]
 declare i64 @write(i32, ptr, i64)
 
+; CHECK: declare void @abort()
+declare void @abort()
 
 ; memset_pattern{4,8,16} aren't available everywhere.
 ; CHECK-DARWIN: declare void @memset_pattern4(ptr nocapture writeonly, ptr nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
diff --git a/llvm/test/Transforms/InstCombine/lib-call-exit.ll b/llvm/test/Transforms/InstCombine/lib-call-exit.ll
new file mode 100644
index 0000000000000..7f1f7f4208899
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/lib-call-exit.ll
@@ -0,0 +1,42 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @exit(i32)
+declare void @_Exit(i32)
+
+define void @call_exit_0() {
+; CHECK-LABEL: define void @call_exit_0() {
+; CHECK-NEXT:    call void @exit(i32 0)
+; CHECK-NEXT:    ret void
+;
+  call void @exit(i32 0)
+  ret void
+}
+
+define void @call_exit_1() {
+; CHECK-LABEL: define void @call_exit_1() {
+; CHECK-NEXT:    call void @exit(i32 1)
+; CHECK-NEXT:    ret void
+;
+  call void @exit(i32 1)
+  ret void
+}
+
+define void @call__Exit_m1() {
+; CHECK-LABEL: define void @call__Exit_m1() {
+; CHECK-NEXT:    call void @_Exit(i32 -1)
+; CHECK-NEXT:    ret void
+;
+  call void @_Exit(i32 -1)
+  ret void
+}
+
+define void @call__Exit_N(i32 %N) {
+; CHECK-LABEL: define void @call__Exit_N(
+; CHECK-SAME: i32 [[N:%.*]]) {
+; CHECK-NEXT:    call void @_Exit(i32 [[N]])
+; CHECK-NEXT:    ret void
+;
+  call void @_Exit(i32 %N)
+  ret void
+}

>From 79b06fb228136ec917d2b5bab40deeee030ff0db Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Mon, 29 Jul 2024 20:02:28 +0800
Subject: [PATCH 2/3] [TLI] Add support for inferring attr `cold` on
 `exit`/`abort`

`abort` can be assumed always cold and assume non-zero `exit` status
as a `cold` path as well.
---
 .../llvm/Analysis/TargetLibraryInfo.def       | 15 ++++++++++++++
 .../llvm/Transforms/Utils/SimplifyLibCalls.h  |  3 +++
 llvm/lib/Transforms/Utils/BuildLibCalls.cpp   | 12 +++++++++++
 .../lib/Transforms/Utils/SimplifyLibCalls.cpp | 14 +++++++++++++
 .../Transforms/InferFunctionAttrs/annotate.ll |  3 ++-
 .../Transforms/InstCombine/lib-call-exit.ll   |  4 ++--
 .../tools/llvm-tli-checker/ps4-tli-check.yaml | 20 +++++++++++++++----
 .../Analysis/TargetLibraryInfoTest.cpp        |  6 +++++-
 8 files changed, 69 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.def b/llvm/include/llvm/Analysis/TargetLibraryInfo.def
index 623cdb4b6e0b7..e9f3b7fcd99eb 100644
--- a/llvm/include/llvm/Analysis/TargetLibraryInfo.def
+++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.def
@@ -476,6 +476,21 @@ TLI_DEFINE_ENUM_INTERNAL(atexit)
 TLI_DEFINE_STRING_INTERNAL("atexit")
 TLI_DEFINE_SIG_INTERNAL(Int, Ptr)
 
+/// void abort(void)
+TLI_DEFINE_ENUM_INTERNAL(abort)
+TLI_DEFINE_STRING_INTERNAL("abort")
+TLI_DEFINE_SIG_INTERNAL(Void)
+
+/// void exit(int)
+TLI_DEFINE_ENUM_INTERNAL(exit)
+TLI_DEFINE_STRING_INTERNAL("exit")
+TLI_DEFINE_SIG_INTERNAL(Void, Int)
+
+/// void _Exit(int)
+TLI_DEFINE_ENUM_INTERNAL(Exit)
+TLI_DEFINE_STRING_INTERNAL("_Exit")
+TLI_DEFINE_SIG_INTERNAL(Void, Int)
+
 /// void __cxa_guard_abort(guard_t *guard);
 /// guard_t is int64_t in Itanium ABI or int32_t on ARM eabi.
 TLI_DEFINE_ENUM_INTERNAL(cxa_guard_abort)
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
index 770da12e01233..43b5c9250a890 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -245,6 +245,9 @@ class LibCallSimplifier {
   Value *optimizeSnPrintFString(CallInst *CI, IRBuilderBase &B);
   Value *optimizeFPrintFString(CallInst *CI, IRBuilderBase &B);
 
+  /// Exit functions
+  Value *optimizeExit(CallInst *CI);
+
   /// hasFloatVersion - Checks if there is a float version of the specified
   /// function by checking for an existing function with name FuncName + f
   bool hasFloatVersion(const Module *M, StringRef FuncName);
diff --git a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
index 0c45bd886af9d..898e5b0f41812 100644
--- a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
@@ -48,6 +48,7 @@ STATISTIC(NumNoAlias, "Number of function returns inferred as noalias");
 STATISTIC(NumNoUndef, "Number of function returns inferred as noundef returns");
 STATISTIC(NumReturnedArg, "Number of arguments inferred as returned");
 STATISTIC(NumWillReturn, "Number of functions inferred as willreturn");
+STATISTIC(NumCold, "Number of functions inferred as cold");
 
 static bool setDoesNotAccessMemory(Function &F) {
   if (F.doesNotAccessMemory())
@@ -57,6 +58,14 @@ static bool setDoesNotAccessMemory(Function &F) {
   return true;
 }
 
+static bool setIsCold(Function &F) {
+  if (F.hasFnAttribute(Attribute::Cold))
+    return false;
+  F.addFnAttr(Attribute::Cold);
+  ++NumCold;
+  return true;
+}
+
 static bool setOnlyAccessesInaccessibleMemory(Function &F) {
   if (F.onlyAccessesInaccessibleMemory())
     return false;
@@ -1087,6 +1096,9 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyWritesMemory(F, 0);
     Changed |= setDoesNotThrow(F);
     break;
+  case LibFunc_abort:
+    Changed |= setIsCold(F);
+    break;
   // int __nvvm_reflect(const char *)
   case LibFunc_nvvm_reflect:
     Changed |= setRetAndArgsNoUndef(F);
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 92c44265ce8b6..4100471eaaa1d 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -3729,6 +3729,17 @@ Value *LibCallSimplifier::optimizePuts(CallInst *CI, IRBuilderBase &B) {
   return nullptr;
 }
 
+Value *LibCallSimplifier::optimizeExit(CallInst *CI) {
+
+  // Mark 'exit' as cold if its not exit(0) (success).
+  const APInt *C;
+  if (!CI->hasFnAttr(Attribute::Cold) &&
+      match(CI->getArgOperand(0), m_APInt(C)) && !C->isZero()) {
+    CI->addFnAttr(Attribute::Cold);
+  }
+  return nullptr;
+}
+
 Value *LibCallSimplifier::optimizeBCopy(CallInst *CI, IRBuilderBase &B) {
   // bcopy(src, dst, n) -> llvm.memmove(dst, src, n)
   return copyFlags(*CI, B.CreateMemMove(CI->getArgOperand(1), Align(1),
@@ -4084,6 +4095,9 @@ Value *LibCallSimplifier::optimizeCall(CallInst *CI, IRBuilderBase &Builder) {
     case LibFunc_vfprintf:
     case LibFunc_fiprintf:
       return optimizeErrorReporting(CI, Builder, 0);
+    case LibFunc_exit:
+    case LibFunc_Exit:
+      return optimizeExit(CI);
     default:
       return nullptr;
     }
diff --git a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
index 9a5b902210009..d54290fd3869c 100644
--- a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
+++ b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
@@ -1088,7 +1088,7 @@ declare i32 @vsscanf(ptr, ptr, ptr)
 ; CHECK: declare noundef i64 @write(i32 noundef, ptr nocapture noundef readonly, i64 noundef) [[NOFREE]]
 declare i64 @write(i32, ptr, i64)
 
-; CHECK: declare void @abort()
+; CHECK: declare void @abort() [[NOFREE_COLD:#[0-9]+]]
 declare void @abort()
 
 ; memset_pattern{4,8,16} aren't available everywhere.
@@ -1116,6 +1116,7 @@ declare void @memset_pattern16(ptr, ptr, i64)
 ; CHECK-DAG: attributes [[ARGMEMONLY_NOFREE_NOUNWIND]] = { nofree nounwind memory(argmem: readwrite) }
 ; CHECK-DAG: attributes [[INACCESSIBLEMEMORARGMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE1_FAMILY_MALLOC]] = { mustprogress nounwind willreturn allockind("realloc") allocsize(1) memory(argmem: readwrite, inaccessiblemem: readwrite) "alloc-family"="malloc" }
 ; CHECK-DAG: attributes [[INACCESSIBLEMEMORARGONLY_NOFREE_NOUNWIND_WILLRETURN_FAMILY_MALLOC]] = { mustprogress nofree nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) "alloc-family"="malloc" }
+; CHECK-DAG: attributes [[NOFREE_COLD]] = { cold nofree }
 
 ; CHECK-NVPTX-DAG: attributes [[NOFREE_NOUNWIND_READNONE]] = { nofree nosync nounwind memory(none) }
 
diff --git a/llvm/test/Transforms/InstCombine/lib-call-exit.ll b/llvm/test/Transforms/InstCombine/lib-call-exit.ll
index 7f1f7f4208899..d6c5ec8979df4 100644
--- a/llvm/test/Transforms/InstCombine/lib-call-exit.ll
+++ b/llvm/test/Transforms/InstCombine/lib-call-exit.ll
@@ -15,7 +15,7 @@ define void @call_exit_0() {
 
 define void @call_exit_1() {
 ; CHECK-LABEL: define void @call_exit_1() {
-; CHECK-NEXT:    call void @exit(i32 1)
+; CHECK-NEXT:    call void @exit(i32 1) #[[ATTR0:[0-9]+]]
 ; CHECK-NEXT:    ret void
 ;
   call void @exit(i32 1)
@@ -24,7 +24,7 @@ define void @call_exit_1() {
 
 define void @call__Exit_m1() {
 ; CHECK-LABEL: define void @call__Exit_m1() {
-; CHECK-NEXT:    call void @_Exit(i32 -1)
+; CHECK-NEXT:    call void @_Exit(i32 -1) #[[ATTR0]]
 ; CHECK-NEXT:    ret void
 ;
   call void @_Exit(i32 -1)
diff --git a/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml b/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml
index 5840e024e9786..19e18e09b76d0 100644
--- a/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml
+++ b/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml
@@ -34,21 +34,21 @@
 #
 # CHECK: << Total TLI yes SDK no:  8
 # CHECK: >> Total TLI no  SDK yes: 0
-# CHECK: == Total TLI yes SDK yes: 242
+# CHECK: == Total TLI yes SDK yes: 245
 #
 # WRONG_DETAIL: << TLI yes SDK no : '_ZdaPv' aka operator delete[](void*)
 # WRONG_DETAIL: >> TLI no  SDK yes: '_ZdaPvj' aka operator delete[](void*, unsigned int)
 # WRONG_DETAIL-COUNT-8: << TLI yes SDK no : {{.*}}__hot_cold_t
 # WRONG_SUMMARY: << Total TLI yes SDK no:  9{{$}}
 # WRONG_SUMMARY: >> Total TLI no  SDK yes: 1{{$}}
-# WRONG_SUMMARY: == Total TLI yes SDK yes: 241
+# WRONG_SUMMARY: == Total TLI yes SDK yes: 244
 #
 ## The -COUNT suffix doesn't care if there are too many matches, so check
 ## the exact count first; the two directives should add up to that.
 ## Yes, this means additions to TLI will fail this test, but the argument
 ## to -COUNT can't be an expression.
-# AVAIL: TLI knows 483 symbols, 250 available
-# AVAIL-COUNT-250: {{^}} available
+# AVAIL: TLI knows 486 symbols, 253 available
+# AVAIL-COUNT-253: {{^}} available
 # AVAIL-NOT:       {{^}} available
 # UNAVAIL-COUNT-233: not available
 # UNAVAIL-NOT:       not available
@@ -267,6 +267,18 @@ DynamicSymbols:
     Type:            STT_FUNC
     Section:         .text
     Binding:         STB_GLOBAL
+  - Name:            abort
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+  - Name:            exit
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+  - Name:            _Exit
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
   - Name:            atof
     Type:            STT_FUNC
     Section:         .text
diff --git a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
index 1447291844dd2..b8125b099343b 100644
--- a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
+++ b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
@@ -500,6 +500,10 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
 
       "declare i32 @atexit(void ()*)\n"
 
+      "declare void @abort()\n"
+      "declare void @exit(i32)\n"
+      "declare void @_Exit(i32)\n"
+
       "declare i32 @__nvvm_reflect(i8*)\n"
 
       "declare i8* @__memcpy_chk(i8*, i8*, i64, i64)\n"
@@ -663,4 +667,4 @@ class TLITestAarch64 : public ::testing::Test {
 TEST_F(TLITestAarch64, TestFrem) {
   EXPECT_EQ(getScalarName(Instruction::FRem, Type::getDoubleTy(Ctx)), "fmod");
   EXPECT_EQ(getScalarName(Instruction::FRem, Type::getFloatTy(Ctx)), "fmodf");
-}
\ No newline at end of file
+}

>From 0158f7eadef431fc3c53ac6f8c6f642f4635df93 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Mon, 29 Jul 2024 20:45:42 +0800
Subject: [PATCH 3/3] fixup tests

---
 llvm/test/Transforms/InstCombine/lib-call-exit.ll | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/lib-call-exit.ll b/llvm/test/Transforms/InstCombine/lib-call-exit.ll
index d6c5ec8979df4..786eab09c092a 100644
--- a/llvm/test/Transforms/InstCombine/lib-call-exit.ll
+++ b/llvm/test/Transforms/InstCombine/lib-call-exit.ll
@@ -40,3 +40,5 @@ define void @call__Exit_N(i32 %N) {
   call void @_Exit(i32 %N)
   ret void
 }
+
+; CHECK: attributes #[[ATTR0:[0-9]+]] = { cold }



More information about the llvm-commits mailing list