[llvm] [FuncAttrs] Relax norecurse attribute inference (PR #139943)
Usha Gupta via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 20 04:52:48 PDT 2025
https://github.com/usha1830 updated https://github.com/llvm/llvm-project/pull/139943
>From 0e5eda5835db7adee4ab5636f77a8cf078c6f96b Mon Sep 17 00:00:00 2001
From: Usha Gupta <usha.gupta at arm.com>
Date: Wed, 14 May 2025 16:22:34 +0000
Subject: [PATCH 1/9] [FuncAttrs] Relax norecurse attribute when callee is
self-recursive or is part of a recursive call chain which does not include
the caller.
---
llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 86 +++++++--
.../norecurse_multiSCC_indirect_recursion.ll | 159 ++++++++++++++++
.../norecurse_multiSCC_indirect_recursion1.ll | 102 +++++++++++
.../norecurse_self_recursive_callee.ll | 173 ++++++++++++++++++
4 files changed, 503 insertions(+), 17 deletions(-)
create mode 100644 llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll
create mode 100644 llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll
create mode 100644 llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index f918d7e059b63..293699a1639c6 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -2059,8 +2059,49 @@ static void inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes,
AI.run(SCCNodes, Changed);
}
+/// Returns true if N or any function it (transitively) calls makes a call
+/// to an unknown external function: either an indirect call or a declaration
+/// without the NoCallback attribute.
+static bool callsUnknownExternal(LazyCallGraph::Node *N) {
+ std::deque<LazyCallGraph::Node *> Worklist;
+ DenseSet<LazyCallGraph::Node *> Visited;
+ Worklist.push_back(N);
+ Visited.insert(N);
+
+ while (!Worklist.empty()) {
+ auto *Cur = Worklist.front();
+ Worklist.pop_front();
+
+ Function &F = Cur->getFunction();
+ for (auto &BB : F) {
+ for (auto &I : BB.instructionsWithoutDebug()) {
+ if (auto *CB = dyn_cast<CallBase>(&I)) {
+ const Function *Callee = CB->getCalledFunction();
+ // Indirect call, treat as unknown external function.
+ if (!Callee)
+ return true;
+ // Extern declaration without NoCallback attribute
+ if (Callee->isDeclaration() &&
+ !Callee->hasFnAttribute(Attribute::NoCallback))
+ return true;
+ }
+ }
+ }
+
+ // Enqueue all direct call-edge successors for further scanning
+ for (auto &Edge : Cur->populate().calls()) {
+ LazyCallGraph::Node *Succ = &Edge.getNode();
+ if (Visited.insert(Succ).second)
+ Worklist.push_back(Succ);
+ }
+ }
+
+ return false;
+}
+
static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
- SmallSet<Function *, 8> &Changed) {
+ SmallSet<Function *, 8> &Changed,
+ LazyCallGraph &CG) {
// Try and identify functions that do not recurse.
// If the SCC contains multiple nodes we know for sure there is recursion.
@@ -2071,24 +2112,35 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
if (!F || !F->hasExactDefinition() || F->doesNotRecurse())
return;
- // If all of the calls in F are identifiable and are to norecurse functions, F
- // is norecurse. This check also detects self-recursion as F is not currently
- // marked norecurse, so any called from F to F will not be marked norecurse.
- for (auto &BB : *F)
- for (auto &I : BB.instructionsWithoutDebug())
+ // If all of the calls in F are identifiable and can be proven to not
+ // callback F, F is norecurse. This check also detects self-recursion
+ // as F is not currently marked norecurse, so any call from F to F
+ // will not be marked norecurse.
+ for (auto &BB : *F) {
+ for (auto &I : BB.instructionsWithoutDebug()) {
if (auto *CB = dyn_cast<CallBase>(&I)) {
Function *Callee = CB->getCalledFunction();
- if (!Callee || Callee == F ||
- (!Callee->doesNotRecurse() &&
- !(Callee->isDeclaration() &&
- Callee->hasFnAttribute(Attribute::NoCallback))))
- // Function calls a potentially recursive function.
+
+ if (!Callee || Callee == F)
+ return;
+
+ // External call with NoCallback attribute.
+ if (Callee->isDeclaration()) {
+ if (Callee->hasFnAttribute(Attribute::NoCallback))
+ continue;
return;
+ }
+
+ if (auto *CNode = CG.lookup(*Callee))
+ if (callsUnknownExternal(CNode))
+ return;
}
+ }
+ }
- // Every call was to a non-recursive function other than this function, and
- // we have no indirect recursion as the SCC size is one. This function cannot
- // recurse.
+ // Every call was either to an external function guaranteed to not make a
+ // call to this function or a direct call to internal function and we have no
+ // indirect recursion as the SCC size is one. This function cannot recurse.
F->setDoesNotRecurse();
++NumNoRecurse;
Changed.insert(F);
@@ -2248,7 +2300,7 @@ static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) {
template <typename AARGetterT>
static SmallSet<Function *, 8>
deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
- bool ArgAttrsOnly) {
+ bool ArgAttrsOnly, LazyCallGraph &CG) {
SCCNodesResult Nodes = createSCCNodeSet(Functions);
// Bail if the SCC only contains optnone functions.
@@ -2279,7 +2331,7 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
addNoAliasAttrs(Nodes.SCCNodes, Changed);
addNonNullAttrs(Nodes.SCCNodes, Changed);
inferAttrsFromFunctionBodies(Nodes.SCCNodes, Changed);
- addNoRecurseAttrs(Nodes.SCCNodes, Changed);
+ addNoRecurseAttrs(Nodes.SCCNodes, Changed, CG);
}
// Finally, infer the maximal set of attributes from the ones we've inferred
@@ -2323,7 +2375,7 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
}
auto ChangedFunctions =
- deriveAttrsInPostOrder(Functions, AARGetter, ArgAttrsOnly);
+ deriveAttrsInPostOrder(Functions, AARGetter, ArgAttrsOnly, CG);
if (ChangedFunctions.empty())
return PreservedAnalyses::all();
diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll
new file mode 100644
index 0000000000000..c0af16b069bde
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll
@@ -0,0 +1,159 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "aarch64-unknown-linux-gnu"
+
+; This test includes a call graph with multiple SCCs. The purpose of this is
+; to check that norecurse is not added when a function is part of non-singular
+; SCC.
+; There are three different SCCs in this test:
+; SCC#1: main, foo, bar, foo1, bar1
+; SCC#2: bar2, bar3, bar4
+; SCC#3: baz, fun
+; None of these functions should be marked as norecurse
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @bar1() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @bar1(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @main()
+; CHECK-NEXT: ret void
+;
+entry:
+ %call = tail call i32 @main()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local noundef i32 @main() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local noundef i32 @main(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @foo()
+; CHECK-NEXT: tail call void @bar2()
+; CHECK-NEXT: tail call void @baz()
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ tail call void @foo()
+ tail call void @bar2()
+ tail call void @baz()
+ ret i32 0
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @foo1() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @foo1(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @bar1()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @bar1()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @bar() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @bar(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @foo1()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @foo1()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @foo() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @foo(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @bar()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @bar()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @bar4() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @bar4(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @bar2()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @bar2()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @bar2() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @bar2(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @bar3()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @bar3()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @bar3() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @bar3(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @bar4()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @bar4()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @fun() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @fun(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @baz()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @baz()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @baz() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @baz(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @fun()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @fun()
+ ret void
+}
+
+attributes #0 = { nofree noinline nosync nounwind memory(none) uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+outline-atomics,+v8a,-fmv" }
diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll
new file mode 100644
index 0000000000000..60bbaab2a7d66
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll
@@ -0,0 +1,102 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "aarch64-unknown-linux-gnu"
+
+; This test includes a call graph with multiple SCCs. The purpose of this is
+; to check that norecurse is added to a function which calls a function which
+; is indirectly recursive but is not part of the recursive chain.
+; There are two SCCs in this test:
+; SCC#1: bar2, bar3, bar4
+; SCC#2: baz, fun
+; main() calls bar2 and baz, both of which are part of some indirect recursive
+; chain. but does not call back main() and hence main() can be marked as
+; norecurse.
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local noundef i32 @main() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local noundef i32 @main(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @bar2()
+; CHECK-NEXT: tail call void @baz()
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ tail call void @bar2()
+ tail call void @baz()
+ ret i32 0
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @bar4() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @bar4(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @bar2()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @bar2()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @bar2() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @bar2(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR1]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @bar3()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @bar3()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @bar3() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @bar3(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR1]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @bar4()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @bar4()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @fun() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @fun(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR1]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @baz()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @baz()
+ ret void
+}
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define dso_local void @baz() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define dso_local void @baz(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR1]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @fun()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @fun()
+ ret void
+}
+
+attributes #0 = { nofree noinline nosync nounwind memory(none) uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+outline-atomics,+v8a,-fmv" }
diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll
new file mode 100644
index 0000000000000..9b87c4e4ad76c
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll
@@ -0,0 +1,173 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "aarch64-unknown-linux-gnu"
+
+; This test includes a call graph with a self recursive function.
+; The purpose of this is to check that norecurse is added to functions
+; which have a self-recursive function in the call-chain.
+; The call-chain in this test is as follows
+; main -> bob -> callee1 -> callee2 -> callee3 -> callee4 -> callee5
+; where callee5 is self recursive.
+
+ at x = dso_local global i32 4, align 4
+ at y = dso_local global i32 2, align 4
+
+; Function Attrs: nofree noinline norecurse nounwind memory(readwrite, argmem: none) uwtable
+define dso_local void @callee6() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline norecurse nounwind memory(readwrite, argmem: none) uwtable
+; CHECK-LABEL: define dso_local void @callee6(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[TMP0:%.*]] = load volatile i32, ptr @y, align 4, !tbaa [[TBAA8:![0-9]+]]
+; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP0]], 1
+; CHECK-NEXT: store volatile i32 [[INC]], ptr @y, align 4, !tbaa [[TBAA8]]
+; CHECK-NEXT: ret void
+;
+entry:
+ %0 = load volatile i32, ptr @y, align 4, !tbaa !8
+ %inc = add nsw i32 %0, 1
+ store volatile i32 %inc, ptr @y, align 4, !tbaa !8
+ ret void
+}
+
+; Function Attrs: nofree noinline nounwind uwtable
+define dso_local void @callee5(i32 noundef %x) local_unnamed_addr #1 {
+; CHECK: Function Attrs: nofree noinline nounwind uwtable
+; CHECK-LABEL: define dso_local void @callee5(
+; CHECK-SAME: i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT: br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
+; CHECK: [[IF_THEN]]:
+; CHECK-NEXT: tail call void @callee5(i32 noundef [[X]])
+; CHECK-NEXT: br label %[[IF_END]]
+; CHECK: [[IF_END]]:
+; CHECK-NEXT: tail call void @callee6()
+; CHECK-NEXT: ret void
+;
+entry:
+ %cmp = icmp sgt i32 %x, 0
+ br i1 %cmp, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ tail call void @callee5(i32 noundef %x)
+ br label %if.end
+
+if.end: ; preds = %if.then, %entry
+ tail call void @callee6()
+ ret void
+}
+
+; Function Attrs: nofree noinline nounwind uwtable
+define dso_local void @callee4() local_unnamed_addr #1 {
+; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
+; CHECK-LABEL: define dso_local void @callee4(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[TMP0:%.*]] = load volatile i32, ptr @x, align 4, !tbaa [[TBAA8]]
+; CHECK-NEXT: tail call void @callee5(i32 noundef [[TMP0]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %0 = load volatile i32, ptr @x, align 4, !tbaa !8
+ tail call void @callee5(i32 noundef %0)
+ ret void
+}
+
+; Function Attrs: nofree noinline nounwind uwtable
+define dso_local void @callee3() local_unnamed_addr #1 {
+; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
+; CHECK-LABEL: define dso_local void @callee3(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @callee4()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @callee4()
+ ret void
+}
+
+; Function Attrs: nofree noinline nounwind uwtable
+define dso_local void @callee2() local_unnamed_addr #1 {
+; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
+; CHECK-LABEL: define dso_local void @callee2(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @callee3()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @callee3()
+ ret void
+}
+
+; Function Attrs: nofree noinline nounwind uwtable
+define dso_local void @callee1() local_unnamed_addr #1 {
+; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
+; CHECK-LABEL: define dso_local void @callee1(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @callee2()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @callee2()
+ ret void
+}
+
+; Function Attrs: nofree noinline nounwind uwtable
+define dso_local void @bob() local_unnamed_addr #1 {
+; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
+; CHECK-LABEL: define dso_local void @bob(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @callee1()
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @callee1()
+ ret void
+}
+
+; Function Attrs: nofree nounwind uwtable
+define dso_local noundef i32 @main() local_unnamed_addr #2 {
+; CHECK: Function Attrs: nofree norecurse nounwind uwtable
+; CHECK-LABEL: define dso_local noundef i32 @main(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call void @bob()
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ tail call void @bob()
+ ret i32 0
+}
+
+attributes #0 = { nofree noinline norecurse nounwind memory(readwrite, argmem: none) uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+outline-atomics,+v8a,-fmv" }
+attributes #1 = { nofree noinline nounwind uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+outline-atomics,+v8a,-fmv" }
+attributes #2 = { nofree nounwind uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+outline-atomics,+v8a,-fmv" }
+
+!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6}
+!llvm.ident = !{!7}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 8, !"PIC Level", i32 2}
+!2 = !{i32 7, !"PIE Level", i32 2}
+!3 = !{i32 7, !"uwtable", i32 2}
+!4 = !{i32 7, !"frame-pointer", i32 1}
+!5 = !{i32 1, !"ThinLTO", i32 0}
+!6 = !{i32 1, !"EnableSplitLTOUnit", i32 1}
+!7 = !{!"clang version 21.0.0git (https://github.com/llvm/llvm-project db42345dc660329e34fd119fc8edab74521f7c06)"}
+!8 = !{!9, !9, i64 0}
+!9 = !{!"int", !10, i64 0}
+!10 = !{!"omnipotent char", !11, i64 0}
+!11 = !{!"Simple C/C++ TBAA"}
+
+;.
+; CHECK: [[TBAA8]] = !{[[META9:![0-9]+]], [[META9]], i64 0}
+; CHECK: [[META9]] = !{!"int", [[META10:![0-9]+]], i64 0}
+; CHECK: [[META10]] = !{!"omnipotent char", [[META11:![0-9]+]], i64 0}
+; CHECK: [[META11]] = !{!"Simple C/C++ TBAA"}
+;.
>From 828e7773be9f48db91d4cd4e16d3e65e7a078b94 Mon Sep 17 00:00:00 2001
From: Usha Gupta <usha.gupta at arm.com>
Date: Tue, 20 May 2025 16:26:07 +0000
Subject: [PATCH 2/9] Skip checks on callee which already have norecurse
attribute
---
llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 293699a1639c6..f51e22d7c6264 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -2124,6 +2124,9 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
if (!Callee || Callee == F)
return;
+ if (Callee->doesNotRecurse())
+ continue;
+
// External call with NoCallback attribute.
if (Callee->isDeclaration()) {
if (Callee->hasFnAttribute(Attribute::NoCallback))
>From b850dc1f9cbd085dfd1e453487f2621c140261ac Mon Sep 17 00:00:00 2001
From: Usha Gupta <usha.gupta at arm.com>
Date: Thu, 5 Jun 2025 17:13:38 +0000
Subject: [PATCH 3/9] Bail without adding norecurse if address of function is
taken as it could be called back in
---
llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 55 +---
.../AMDGPU/amdgpu-simplify-libcall-sincos.ll | 8 +-
.../Other/cgscc-iterate-function-mutation.ll | 40 +--
.../FunctionAttrs/nofree-attributor.ll | 4 +-
llvm/test/Transforms/FunctionAttrs/nonnull.ll | 2 +-
.../test/Transforms/FunctionAttrs/noreturn.ll | 8 +-
.../test/Transforms/FunctionAttrs/nounwind.ll | 284 ++++++++++++------
.../FunctionAttrs/sendmsg-nocallback.ll | 9 +-
llvm/test/Transforms/Inline/cgscc-update.ll | 20 +-
llvm/test/Transforms/PhaseOrdering/pr95152.ll | 6 +-
10 files changed, 247 insertions(+), 189 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index f51e22d7c6264..d08a2df9d9e66 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -2059,49 +2059,8 @@ static void inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes,
AI.run(SCCNodes, Changed);
}
-/// Returns true if N or any function it (transitively) calls makes a call
-/// to an unknown external function: either an indirect call or a declaration
-/// without the NoCallback attribute.
-static bool callsUnknownExternal(LazyCallGraph::Node *N) {
- std::deque<LazyCallGraph::Node *> Worklist;
- DenseSet<LazyCallGraph::Node *> Visited;
- Worklist.push_back(N);
- Visited.insert(N);
-
- while (!Worklist.empty()) {
- auto *Cur = Worklist.front();
- Worklist.pop_front();
-
- Function &F = Cur->getFunction();
- for (auto &BB : F) {
- for (auto &I : BB.instructionsWithoutDebug()) {
- if (auto *CB = dyn_cast<CallBase>(&I)) {
- const Function *Callee = CB->getCalledFunction();
- // Indirect call, treat as unknown external function.
- if (!Callee)
- return true;
- // Extern declaration without NoCallback attribute
- if (Callee->isDeclaration() &&
- !Callee->hasFnAttribute(Attribute::NoCallback))
- return true;
- }
- }
- }
-
- // Enqueue all direct call-edge successors for further scanning
- for (auto &Edge : Cur->populate().calls()) {
- LazyCallGraph::Node *Succ = &Edge.getNode();
- if (Visited.insert(Succ).second)
- Worklist.push_back(Succ);
- }
- }
-
- return false;
-}
-
static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
- SmallSet<Function *, 8> &Changed,
- LazyCallGraph &CG) {
+ SmallSet<Function *, 8> &Changed) {
// Try and identify functions that do not recurse.
// If the SCC contains multiple nodes we know for sure there is recursion.
@@ -2112,6 +2071,8 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
if (!F || !F->hasExactDefinition() || F->doesNotRecurse())
return;
+ if (F->hasAddressTaken())
+ return;
// If all of the calls in F are identifiable and can be proven to not
// callback F, F is norecurse. This check also detects self-recursion
// as F is not currently marked norecurse, so any call from F to F
@@ -2133,10 +2094,6 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
continue;
return;
}
-
- if (auto *CNode = CG.lookup(*Callee))
- if (callsUnknownExternal(CNode))
- return;
}
}
}
@@ -2303,7 +2260,7 @@ static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) {
template <typename AARGetterT>
static SmallSet<Function *, 8>
deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
- bool ArgAttrsOnly, LazyCallGraph &CG) {
+ bool ArgAttrsOnly) {
SCCNodesResult Nodes = createSCCNodeSet(Functions);
// Bail if the SCC only contains optnone functions.
@@ -2334,7 +2291,7 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
addNoAliasAttrs(Nodes.SCCNodes, Changed);
addNonNullAttrs(Nodes.SCCNodes, Changed);
inferAttrsFromFunctionBodies(Nodes.SCCNodes, Changed);
- addNoRecurseAttrs(Nodes.SCCNodes, Changed, CG);
+ addNoRecurseAttrs(Nodes.SCCNodes, Changed);
}
// Finally, infer the maximal set of attributes from the ones we've inferred
@@ -2378,7 +2335,7 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
}
auto ChangedFunctions =
- deriveAttrsInPostOrder(Functions, AARGetter, ArgAttrsOnly, CG);
+ deriveAttrsInPostOrder(Functions, AARGetter, ArgAttrsOnly);
if (ChangedFunctions.empty())
return PreservedAnalyses::all();
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll
index 34777eff0e856..6e2b825a45f56 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll
@@ -725,7 +725,7 @@ entry:
define void @sincos_f32_value_is_different_constexpr(ptr addrspace(1) nocapture writeonly %sin_out, ptr addrspace(1) nocapture writeonly %cos_out) {
; CHECK-LABEL: define void @sincos_f32_value_is_different_constexpr
-; CHECK-SAME: (ptr addrspace(1) writeonly captures(none) initializes((0, 4)) [[SIN_OUT:%.*]], ptr addrspace(1) writeonly captures(none) initializes((0, 4)) [[COS_OUT:%.*]]) #[[ATTR2]] {
+; CHECK-SAME: (ptr addrspace(1) writeonly captures(none) initializes((0, 4)) [[SIN_OUT:%.*]], ptr addrspace(1) writeonly captures(none) initializes((0, 4)) [[COS_OUT:%.*]]) #[[ATTR6:[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CALL:%.*]] = tail call contract float @_Z3sinf(float bitcast (i32 ptrtoint (ptr @func to i32) to float))
; CHECK-NEXT: store float [[CALL]], ptr addrspace(1) [[SIN_OUT]], align 4
@@ -881,7 +881,7 @@ entry:
define float @sincos_f32_unused_result_cos(float %x) {
; CHECK-LABEL: define float @sincos_f32_unused_result_cos
-; CHECK-SAME: (float [[X:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] {
+; CHECK-SAME: (float [[X:%.*]]) local_unnamed_addr #[[ATTR7:[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SIN:%.*]] = tail call contract float @_Z3sinf(float [[X]])
; CHECK-NEXT: ret float [[SIN]]
@@ -896,7 +896,7 @@ entry:
define float @sincos_f32_unused_result_sin(float %x) {
; CHECK-LABEL: define float @sincos_f32_unused_result_sin
-; CHECK-SAME: (float [[X:%.*]]) local_unnamed_addr #[[ATTR6]] {
+; CHECK-SAME: (float [[X:%.*]]) local_unnamed_addr #[[ATTR7]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[COS:%.*]] = tail call contract float @_Z3cosf(float [[X]])
; CHECK-NEXT: ret float [[COS]]
@@ -911,7 +911,7 @@ entry:
define void @sincos_f32_repeated_uses(float %x, ptr addrspace(1) %sin_out, ptr addrspace(1) %cos_out) {
; CHECK-LABEL: define void @sincos_f32_repeated_uses
-; CHECK-SAME: (float [[X:%.*]], ptr addrspace(1) [[SIN_OUT:%.*]], ptr addrspace(1) [[COS_OUT:%.*]]) local_unnamed_addr #[[ATTR7:[0-9]+]] {
+; CHECK-SAME: (float [[X:%.*]], ptr addrspace(1) [[SIN_OUT:%.*]], ptr addrspace(1) [[COS_OUT:%.*]]) local_unnamed_addr #[[ATTR8:[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[__SINCOS_:%.*]] = alloca float, align 4, addrspace(5)
; CHECK-NEXT: [[TMP0:%.*]] = call contract float @_Z6sincosfPU3AS5f(float [[X]], ptr addrspace(5) [[__SINCOS_]])
diff --git a/llvm/test/Other/cgscc-iterate-function-mutation.ll b/llvm/test/Other/cgscc-iterate-function-mutation.ll
index aafd38d1e8825..631674362bb09 100644
--- a/llvm/test/Other/cgscc-iterate-function-mutation.ll
+++ b/llvm/test/Other/cgscc-iterate-function-mutation.ll
@@ -9,7 +9,7 @@ declare void @reference_function_pointer(ptr) nofree nosync readnone
; and the RefSCCs that those functions are in, we re-run the CGSCC passes to
; observe the refined call graph structure.
-; CHECK: define void @test1_a() {
+; CHECK: define void @test1_a() #1 {
define void @test1_a() {
call void @test1_b1()
call void @test1_b2()
@@ -128,7 +128,7 @@ exit:
; multiple layers that have to be traversed in the correct order instead of
; a single node.
-; CHECK: define void @test3_a() {
+; CHECK: define void @test3_a() #1 {
define void @test3_a() {
call void @test3_b11()
call void @test3_b21()
@@ -137,13 +137,13 @@ define void @test3_a() {
ret void
}
-; CHECK: define void @test3_b11() #0 {
+; CHECK: define void @test3_b11() #2 {
define void @test3_b11() {
call void @test3_b12()
ret void
}
-; CHECK: define void @test3_b12() #0 {
+; CHECK: define void @test3_b12() #2 {
define void @test3_b12() {
call void @test3_b13()
ret void
@@ -155,13 +155,13 @@ define void @test3_b13() {
ret void
}
-; CHECK: define void @test3_b21() #0 {
+; CHECK: define void @test3_b21() #2 {
define void @test3_b21() {
call void @test3_b22()
ret void
}
-; CHECK: define void @test3_b22() #0 {
+; CHECK: define void @test3_b22() #2 {
define void @test3_b22() {
call void @test3_b23()
ret void
@@ -180,13 +180,13 @@ exit:
ret void
}
-; CHECK: define void @test3_b31() {
+; CHECK: define void @test3_b31() #1 {
define void @test3_b31() {
call void @test3_b32()
ret void
}
-; CHECK: define void @test3_b32() {
+; CHECK: define void @test3_b32() #1 {
define void @test3_b32() {
call void @test3_b33()
ret void
@@ -205,13 +205,13 @@ exit:
ret void
}
-; CHECK: define void @test3_b41() #0 {
+; CHECK: define void @test3_b41() #2 {
define void @test3_b41() {
call void @test3_b42()
ret void
}
-; CHECK: define void @test3_b42() #0 {
+; CHECK: define void @test3_b42() #2 {
define void @test3_b42() {
call void @test3_b43()
ret void
@@ -244,13 +244,13 @@ define void @test4_a() {
ret void
}
-; CHECK: define void @test4_b11() #0 {
+; CHECK: define void @test4_b11() #2 {
define void @test4_b11() {
call void @test4_b12()
ret void
}
-; CHECK: define void @test4_b12() #0 {
+; CHECK: define void @test4_b12() #2 {
define void @test4_b12() {
call void @test4_b13()
ret void
@@ -262,20 +262,20 @@ define void @test4_b13() {
ret void
}
-; CHECK: define void @test4_b21() #0 {
+; CHECK: define void @test4_b21() #2 {
define void @test4_b21() {
call void @test4_b22()
ret void
}
-; CHECK: define void @test4_b22() #0 {
+; CHECK: define void @test4_b22() #2 {
define void @test4_b22() {
call void @test4_b23()
ret void
}
; CHECK: define void @test4_b23() #0 {
-define void @test4_b23() {
+define void @test4_b23() #0 {
call void @reference_function_pointer(ptr @test4_a)
br i1 false, label %dead, label %exit
@@ -287,13 +287,13 @@ exit:
ret void
}
-; CHECK: define void @test4_b31() {
+; CHECK: define void @test4_b31() #1 {
define void @test4_b31() {
call void @test4_b32()
ret void
}
-; CHECK: define void @test4_b32() {
+; CHECK: define void @test4_b32() #1 {
define void @test4_b32() {
call void @test4_b33()
ret void
@@ -313,13 +313,13 @@ exit:
ret void
}
-; CHECK: define void @test4_b41() #0 {
+; CHECK: define void @test4_b41() #2 {
define void @test4_b41() {
call void @test4_b42()
ret void
}
-; CHECK: define void @test4_b42() #0 {
+; CHECK: define void @test4_b42() #2 {
define void @test4_b42() {
call void @test4_b43()
ret void
@@ -339,3 +339,5 @@ exit:
}
; CHECK: attributes #0 = { nofree nosync memory(none) }
+; CHECK: attributes #1 = { norecurse }
+; CHECK: attributes #2 = { nofree norecurse nosync memory(none) }
diff --git a/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll b/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll
index 5a0b6ef1e3043..1457e96e1af68 100644
--- a/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll
@@ -250,9 +250,9 @@ define void @f1() #0 {
}
define void @f2() #0 {
-; FNATTR: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; FNATTR: Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable
; FNATTR-LABEL: define {{[^@]+}}@f2
-; FNATTR-SAME: () #[[ATTR4]] {
+; FNATTR-SAME: () #[[ATTR7:[0-9]+]] {
; FNATTR-NEXT: tail call void @f1()
; FNATTR-NEXT: ret void
;
diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll
index 483b560ece6c8..b484210d75957 100644
--- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll
@@ -1105,7 +1105,7 @@ define internal void @optnone(ptr dereferenceable(4) %a) optnone noinline {
}
define void @make_live(ptr nonnull dereferenceable(8) %a) {
; FNATTRS-LABEL: define void @make_live(
-; FNATTRS-SAME: ptr nonnull dereferenceable(8) [[A:%.*]]) {
+; FNATTRS-SAME: ptr nonnull dereferenceable(8) [[A:%.*]]) #[[ATTR13:[0-9]+]] {
; FNATTRS-NEXT: call void @naked(ptr nonnull align 16 dereferenceable(8) [[A]])
; FNATTRS-NEXT: call void @control(ptr nonnull align 16 dereferenceable(8) [[A]])
; FNATTRS-NEXT: call void @optnone(ptr nonnull align 16 dereferenceable(8) [[A]])
diff --git a/llvm/test/Transforms/FunctionAttrs/noreturn.ll b/llvm/test/Transforms/FunctionAttrs/noreturn.ll
index fa80f6c2eced4..965467ba6015f 100644
--- a/llvm/test/Transforms/FunctionAttrs/noreturn.ll
+++ b/llvm/test/Transforms/FunctionAttrs/noreturn.ll
@@ -2,25 +2,25 @@
declare i32 @f()
-; CHECK: Function Attrs: noreturn
+; CHECK: Function Attrs: {{.*}}noreturn
; CHECK-NEXT: @noreturn()
declare i32 @noreturn() noreturn
-; CHECK: Function Attrs: noreturn
+; CHECK: Function Attrs: {{.*}}noreturn
; CHECK-NEXT: @caller()
define i32 @caller() {
%c = call i32 @noreturn()
ret i32 %c
}
-; CHECK: Function Attrs: noreturn
+; CHECK: Function Attrs: {{.*}}noreturn
; CHECK-NEXT: @caller2()
define i32 @caller2() {
%c = call i32 @caller()
ret i32 %c
}
-; CHECK: Function Attrs: noreturn
+; CHECK: Function Attrs: {{.*}}noreturn
; CHECK-NEXT: @caller3()
define i32 @caller3() {
entry:
diff --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll
index afa9ae31b5fba..b613c47435a03 100644
--- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nounwind.ll
@@ -4,10 +4,15 @@
; TEST 1
define i32 @foo1() {
-; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; COMMON-LABEL: define {{[^@]+}}@foo1
-; COMMON-SAME: () #[[ATTR0:[0-9]+]] {
-; COMMON-NEXT: ret i32 1
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@foo1
+; FNATTRS-SAME: () #[[ATTR0:[0-9]+]] {
+; FNATTRS-NEXT: ret i32 1
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@foo1
+; ATTRIBUTOR-SAME: () #[[ATTR0:[0-9]+]] {
+; ATTRIBUTOR-NEXT: ret i32 1
;
ret i32 1
}
@@ -70,14 +75,23 @@ define void @call_non_nounwind(){
; }
define i32 @maybe_throw(i1 zeroext %0) {
-; COMMON-LABEL: define {{[^@]+}}@maybe_throw
-; COMMON-SAME: (i1 zeroext [[TMP0:%.*]]) {
-; COMMON-NEXT: br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; COMMON: 2:
-; COMMON-NEXT: tail call void @__cxa_rethrow()
-; COMMON-NEXT: unreachable
-; COMMON: 3:
-; COMMON-NEXT: ret i32 -1
+; FNATTRS-LABEL: define {{[^@]+}}@maybe_throw
+; FNATTRS-SAME: (i1 zeroext [[TMP0:%.*]]) {
+; FNATTRS-NEXT: br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; FNATTRS: 2:
+; FNATTRS-NEXT: tail call void @__cxa_rethrow()
+; FNATTRS-NEXT: unreachable
+; FNATTRS: 3:
+; FNATTRS-NEXT: ret i32 -1
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@maybe_throw
+; ATTRIBUTOR-SAME: (i1 zeroext [[TMP0:%.*]]) {
+; ATTRIBUTOR-NEXT: br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; ATTRIBUTOR: 2:
+; ATTRIBUTOR-NEXT: tail call void @__cxa_rethrow()
+; ATTRIBUTOR-NEXT: unreachable
+; ATTRIBUTOR: 3:
+; ATTRIBUTOR-NEXT: ret i32 -1
;
br i1 %0, label %2, label %3
@@ -101,18 +115,31 @@ declare void @__cxa_rethrow()
; }
define i32 @catch_thing() personality ptr @__gxx_personality_v0 {
-; COMMON-LABEL: define {{[^@]+}}@catch_thing() personality ptr @__gxx_personality_v0 {
-; COMMON-NEXT: invoke void @__cxa_rethrow()
-; COMMON-NEXT: to label [[TMP1:%.*]] unwind label [[TMP2:%.*]]
-; COMMON: 1:
-; COMMON-NEXT: unreachable
-; COMMON: 2:
-; COMMON-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
-; COMMON-NEXT: catch ptr null
-; COMMON-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
-; COMMON-NEXT: [[TMP5:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP4]])
-; COMMON-NEXT: tail call void @__cxa_end_catch()
-; COMMON-NEXT: ret i32 -1
+; FNATTRS-LABEL: define {{[^@]+}}@catch_thing() personality ptr @__gxx_personality_v0 {
+; FNATTRS-NEXT: invoke void @__cxa_rethrow()
+; FNATTRS-NEXT: to label [[TMP1:%.*]] unwind label [[TMP2:%.*]]
+; FNATTRS: 1:
+; FNATTRS-NEXT: unreachable
+; FNATTRS: 2:
+; FNATTRS-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
+; FNATTRS-NEXT: catch ptr null
+; FNATTRS-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+; FNATTRS-NEXT: [[TMP5:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP4]])
+; FNATTRS-NEXT: tail call void @__cxa_end_catch()
+; FNATTRS-NEXT: ret i32 -1
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@catch_thing() personality ptr @__gxx_personality_v0 {
+; ATTRIBUTOR-NEXT: invoke void @__cxa_rethrow()
+; ATTRIBUTOR-NEXT: to label [[TMP1:%.*]] unwind label [[TMP2:%.*]]
+; ATTRIBUTOR: 1:
+; ATTRIBUTOR-NEXT: unreachable
+; ATTRIBUTOR: 2:
+; ATTRIBUTOR-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
+; ATTRIBUTOR-NEXT: catch ptr null
+; ATTRIBUTOR-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+; ATTRIBUTOR-NEXT: [[TMP5:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP4]])
+; ATTRIBUTOR-NEXT: tail call void @__cxa_end_catch()
+; ATTRIBUTOR-NEXT: ret i32 -1
;
invoke void @__cxa_rethrow() #1
to label %1 unwind label %2
@@ -130,9 +157,15 @@ define i32 @catch_thing() personality ptr @__gxx_personality_v0 {
}
define i32 @catch_thing_user() {
-; COMMON-LABEL: define {{[^@]+}}@catch_thing_user() {
-; COMMON-NEXT: [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing()
-; COMMON-NEXT: ret i32 [[CATCH_THING_CALL]]
+; FNATTRS: Function Attrs: norecurse
+; FNATTRS-LABEL: define {{[^@]+}}@catch_thing_user
+; FNATTRS-SAME: () #[[ATTR2:[0-9]+]] {
+; FNATTRS-NEXT: [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing()
+; FNATTRS-NEXT: ret i32 [[CATCH_THING_CALL]]
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@catch_thing_user() {
+; ATTRIBUTOR-NEXT: [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing()
+; ATTRIBUTOR-NEXT: ret i32 [[CATCH_THING_CALL]]
;
%catch_thing_call = call i32 @catch_thing()
ret i32 %catch_thing_call
@@ -143,18 +176,31 @@ declare void @abort() nounwind
@catch_ty = external global ptr
define void @catch_specific_landingpad() personality ptr @__gxx_personality_v0 {
-; COMMON: Function Attrs: noreturn
-; COMMON-LABEL: define {{[^@]+}}@catch_specific_landingpad
-; COMMON-SAME: () #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 {
-; COMMON-NEXT: invoke void @do_throw()
-; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; COMMON: lpad:
-; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; COMMON-NEXT: catch ptr @catch_ty
-; COMMON-NEXT: call void @abort()
-; COMMON-NEXT: unreachable
-; COMMON: unreachable:
-; COMMON-NEXT: unreachable
+; FNATTRS: Function Attrs: noreturn
+; FNATTRS-LABEL: define {{[^@]+}}@catch_specific_landingpad
+; FNATTRS-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-NEXT: invoke void @do_throw()
+; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; FNATTRS: lpad:
+; FNATTRS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; FNATTRS-NEXT: catch ptr @catch_ty
+; FNATTRS-NEXT: call void @abort()
+; FNATTRS-NEXT: unreachable
+; FNATTRS: unreachable:
+; FNATTRS-NEXT: unreachable
+;
+; ATTRIBUTOR: Function Attrs: noreturn
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@catch_specific_landingpad
+; ATTRIBUTOR-SAME: () #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 {
+; ATTRIBUTOR-NEXT: invoke void @do_throw()
+; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; ATTRIBUTOR: lpad:
+; ATTRIBUTOR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; ATTRIBUTOR-NEXT: catch ptr @catch_ty
+; ATTRIBUTOR-NEXT: call void @abort()
+; ATTRIBUTOR-NEXT: unreachable
+; ATTRIBUTOR: unreachable:
+; ATTRIBUTOR-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
@@ -170,18 +216,31 @@ unreachable:
}
define void @catch_all_landingpad() personality ptr @__gxx_personality_v0 {
-; COMMON: Function Attrs: noreturn nounwind
-; COMMON-LABEL: define {{[^@]+}}@catch_all_landingpad
-; COMMON-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
-; COMMON-NEXT: invoke void @do_throw()
-; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; COMMON: lpad:
-; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; COMMON-NEXT: catch ptr null
-; COMMON-NEXT: call void @abort()
-; COMMON-NEXT: unreachable
-; COMMON: unreachable:
-; COMMON-NEXT: unreachable
+; FNATTRS: Function Attrs: noreturn nounwind
+; FNATTRS-LABEL: define {{[^@]+}}@catch_all_landingpad
+; FNATTRS-SAME: () #[[ATTR5:[0-9]+]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-NEXT: invoke void @do_throw()
+; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; FNATTRS: lpad:
+; FNATTRS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; FNATTRS-NEXT: catch ptr null
+; FNATTRS-NEXT: call void @abort()
+; FNATTRS-NEXT: unreachable
+; FNATTRS: unreachable:
+; FNATTRS-NEXT: unreachable
+;
+; ATTRIBUTOR: Function Attrs: noreturn nounwind
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@catch_all_landingpad
+; ATTRIBUTOR-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
+; ATTRIBUTOR-NEXT: invoke void @do_throw()
+; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; ATTRIBUTOR: lpad:
+; ATTRIBUTOR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; ATTRIBUTOR-NEXT: catch ptr null
+; ATTRIBUTOR-NEXT: call void @abort()
+; ATTRIBUTOR-NEXT: unreachable
+; ATTRIBUTOR: unreachable:
+; ATTRIBUTOR-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
@@ -197,18 +256,31 @@ unreachable:
}
define void @filter_specific_landingpad() personality ptr @__gxx_personality_v0 {
-; COMMON: Function Attrs: noreturn
-; COMMON-LABEL: define {{[^@]+}}@filter_specific_landingpad
-; COMMON-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
-; COMMON-NEXT: invoke void @do_throw()
-; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; COMMON: lpad:
-; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; COMMON-NEXT: filter [1 x ptr] [ptr @catch_ty]
-; COMMON-NEXT: call void @abort()
-; COMMON-NEXT: unreachable
-; COMMON: unreachable:
-; COMMON-NEXT: unreachable
+; FNATTRS: Function Attrs: noreturn
+; FNATTRS-LABEL: define {{[^@]+}}@filter_specific_landingpad
+; FNATTRS-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-NEXT: invoke void @do_throw()
+; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; FNATTRS: lpad:
+; FNATTRS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; FNATTRS-NEXT: filter [1 x ptr] [ptr @catch_ty]
+; FNATTRS-NEXT: call void @abort()
+; FNATTRS-NEXT: unreachable
+; FNATTRS: unreachable:
+; FNATTRS-NEXT: unreachable
+;
+; ATTRIBUTOR: Function Attrs: noreturn
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@filter_specific_landingpad
+; ATTRIBUTOR-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
+; ATTRIBUTOR-NEXT: invoke void @do_throw()
+; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; ATTRIBUTOR: lpad:
+; ATTRIBUTOR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; ATTRIBUTOR-NEXT: filter [1 x ptr] [ptr @catch_ty]
+; ATTRIBUTOR-NEXT: call void @abort()
+; ATTRIBUTOR-NEXT: unreachable
+; ATTRIBUTOR: unreachable:
+; ATTRIBUTOR-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
@@ -224,18 +296,31 @@ unreachable:
}
define void @filter_none_landingpad() personality ptr @__gxx_personality_v0 {
-; COMMON: Function Attrs: noreturn nounwind
-; COMMON-LABEL: define {{[^@]+}}@filter_none_landingpad
-; COMMON-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
-; COMMON-NEXT: invoke void @do_throw()
-; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; COMMON: lpad:
-; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; COMMON-NEXT: filter [0 x ptr] zeroinitializer
-; COMMON-NEXT: call void @abort()
-; COMMON-NEXT: unreachable
-; COMMON: unreachable:
-; COMMON-NEXT: unreachable
+; FNATTRS: Function Attrs: noreturn nounwind
+; FNATTRS-LABEL: define {{[^@]+}}@filter_none_landingpad
+; FNATTRS-SAME: () #[[ATTR5]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-NEXT: invoke void @do_throw()
+; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; FNATTRS: lpad:
+; FNATTRS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; FNATTRS-NEXT: filter [0 x ptr] zeroinitializer
+; FNATTRS-NEXT: call void @abort()
+; FNATTRS-NEXT: unreachable
+; FNATTRS: unreachable:
+; FNATTRS-NEXT: unreachable
+;
+; ATTRIBUTOR: Function Attrs: noreturn nounwind
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@filter_none_landingpad
+; ATTRIBUTOR-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
+; ATTRIBUTOR-NEXT: invoke void @do_throw()
+; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; ATTRIBUTOR: lpad:
+; ATTRIBUTOR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; ATTRIBUTOR-NEXT: filter [0 x ptr] zeroinitializer
+; ATTRIBUTOR-NEXT: call void @abort()
+; ATTRIBUTOR-NEXT: unreachable
+; ATTRIBUTOR: unreachable:
+; ATTRIBUTOR-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
@@ -251,18 +336,31 @@ unreachable:
}
define void @cleanup_landingpad() personality ptr @__gxx_personality_v0 {
-; COMMON: Function Attrs: noreturn
-; COMMON-LABEL: define {{[^@]+}}@cleanup_landingpad
-; COMMON-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
-; COMMON-NEXT: invoke void @do_throw()
-; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; COMMON: lpad:
-; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; COMMON-NEXT: cleanup
-; COMMON-NEXT: call void @abort()
-; COMMON-NEXT: unreachable
-; COMMON: unreachable:
-; COMMON-NEXT: unreachable
+; FNATTRS: Function Attrs: noreturn
+; FNATTRS-LABEL: define {{[^@]+}}@cleanup_landingpad
+; FNATTRS-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-NEXT: invoke void @do_throw()
+; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; FNATTRS: lpad:
+; FNATTRS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; FNATTRS-NEXT: cleanup
+; FNATTRS-NEXT: call void @abort()
+; FNATTRS-NEXT: unreachable
+; FNATTRS: unreachable:
+; FNATTRS-NEXT: unreachable
+;
+; ATTRIBUTOR: Function Attrs: noreturn
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@cleanup_landingpad
+; ATTRIBUTOR-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
+; ATTRIBUTOR-NEXT: invoke void @do_throw()
+; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; ATTRIBUTOR: lpad:
+; ATTRIBUTOR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; ATTRIBUTOR-NEXT: cleanup
+; ATTRIBUTOR-NEXT: call void @abort()
+; ATTRIBUTOR-NEXT: unreachable
+; ATTRIBUTOR: unreachable:
+; ATTRIBUTOR-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
@@ -280,9 +378,9 @@ unreachable:
define void @cleanuppad() personality ptr @__gxx_personality_v0 {
; FNATTRS: Function Attrs: noreturn
; FNATTRS-LABEL: define {{[^@]+}}@cleanuppad
-; FNATTRS-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
; FNATTRS-NEXT: invoke void @do_throw()
-; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
+; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
; FNATTRS: cpad:
; FNATTRS-NEXT: [[CP:%.*]] = cleanuppad within none []
; FNATTRS-NEXT: call void @abort()
@@ -294,7 +392,7 @@ define void @cleanuppad() personality ptr @__gxx_personality_v0 {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@cleanuppad
; ATTRIBUTOR-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
; ATTRIBUTOR-NEXT: invoke void @do_throw()
-; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
+; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
; ATTRIBUTOR: cpad:
; ATTRIBUTOR-NEXT: [[CP:%.*]] = cleanuppad within none []
; ATTRIBUTOR-NEXT: call void @abort()
@@ -317,9 +415,9 @@ unreachable:
define void @catchswitch_cleanuppad() personality ptr @__gxx_personality_v0 {
; FNATTRS: Function Attrs: noreturn
; FNATTRS-LABEL: define {{[^@]+}}@catchswitch_cleanuppad
-; FNATTRS-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
; FNATTRS-NEXT: invoke void @do_throw()
-; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
+; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
; FNATTRS: cs:
; FNATTRS-NEXT: [[TOK:%.*]] = catchswitch within none [label %catch] unwind label [[CPAD:%.*]]
; FNATTRS: catch:
@@ -337,7 +435,7 @@ define void @catchswitch_cleanuppad() personality ptr @__gxx_personality_v0 {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@catchswitch_cleanuppad
; ATTRIBUTOR-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
; ATTRIBUTOR-NEXT: invoke void @do_throw()
-; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
+; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
; ATTRIBUTOR: cs:
; ATTRIBUTOR-NEXT: [[TOK:%.*]] = catchswitch within none [label %catch] unwind label [[CPAD:%.*]]
; ATTRIBUTOR: catch:
diff --git a/llvm/test/Transforms/FunctionAttrs/sendmsg-nocallback.ll b/llvm/test/Transforms/FunctionAttrs/sendmsg-nocallback.ll
index 4d5db3263f527..f5fde8822e30b 100644
--- a/llvm/test/Transforms/FunctionAttrs/sendmsg-nocallback.ll
+++ b/llvm/test/Transforms/FunctionAttrs/sendmsg-nocallback.ll
@@ -33,9 +33,9 @@ define internal void @sendmsghalt_is_norecurse() {
}
define internal i32 @sendmsg_rtn_is_norecurse() {
-; FNATTRS: Function Attrs: mustprogress norecurse nounwind willreturn
+; FNATTRS: Function Attrs: mustprogress nounwind willreturn
; FNATTRS-LABEL: define internal i32 @sendmsg_rtn_is_norecurse(
-; FNATTRS-SAME: ) #[[ATTR0]] {
+; FNATTRS-SAME: ) #[[ATTR2:[0-9]+]] {
; FNATTRS-NEXT: [[RES:%.*]] = call i32 @llvm.amdgcn.s.sendmsg.rtn.i32(i32 1)
; FNATTRS-NEXT: ret i32 [[RES]]
;
@@ -72,8 +72,9 @@ define void @user() {
;.
; FNATTRS: attributes #[[ATTR0]] = { mustprogress norecurse nounwind willreturn }
; FNATTRS: attributes #[[ATTR1]] = { norecurse nounwind }
-; FNATTRS: attributes #[[ATTR2:[0-9]+]] = { nocallback nounwind willreturn }
-; FNATTRS: attributes #[[ATTR3:[0-9]+]] = { nocallback nounwind }
+; FNATTRS: attributes #[[ATTR2]] = { mustprogress nounwind willreturn }
+; FNATTRS: attributes #[[ATTR3:[0-9]+]] = { nocallback nounwind willreturn }
+; FNATTRS: attributes #[[ATTR4:[0-9]+]] = { nocallback nounwind }
;.
; ATTRIBUTOR: attributes #[[ATTR0]] = { mustprogress norecurse nounwind willreturn }
; ATTRIBUTOR: attributes #[[ATTR1]] = { norecurse nounwind }
diff --git a/llvm/test/Transforms/Inline/cgscc-update.ll b/llvm/test/Transforms/Inline/cgscc-update.ll
index f1121fa88a4b1..f1d6325b7ee5a 100644
--- a/llvm/test/Transforms/Inline/cgscc-update.ll
+++ b/llvm/test/Transforms/Inline/cgscc-update.ll
@@ -11,7 +11,7 @@ declare void @unknown()
; Basic correctness check: this should get annotated as memory(none).
; CHECK: Function Attrs: nounwind memory(none)
-; CHECK-NEXT: declare void @readnone()
+; CHECK-NEXT: declare void @readnone() #0
declare void @readnone() readnone nounwind
; The 'test1_' prefixed functions are designed to trigger forming a new direct
@@ -27,8 +27,8 @@ entry:
}
; This function should have had 'memory(none)' deduced for its SCC.
-; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
-; CHECK-NEXT: define void @test1_g()
+; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none)
+; CHECK-NEXT: define void @test1_g() #1
define void @test1_g() noinline {
entry:
call void @test1_f(ptr @test1_h)
@@ -37,7 +37,7 @@ entry:
; This function should have had 'memory(none)' deduced for its SCC.
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
-; CHECK-NEXT: define void @test1_h()
+; CHECK-NEXT: define void @test1_h() #2
define void @test1_h() noinline {
entry:
call void @test1_g()
@@ -60,7 +60,7 @@ entry:
; This function should have had 'memory(none)' deduced for its SCC.
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
-; CHECK-NEXT: define void @test2_g()
+; CHECK-NEXT: define void @test2_g() #2
define void @test2_g() noinline {
entry:
%p = call ptr @test2_f()
@@ -70,7 +70,7 @@ entry:
; This function should have had 'memory(none)' deduced for its SCC.
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
-; CHECK-NEXT: define void @test2_h()
+; CHECK-NEXT: define void @test2_h() #2
define void @test2_h() noinline {
entry:
call void @test2_g()
@@ -129,7 +129,7 @@ exit:
; doesn't simplify the caller's trivially dead CFG and so we end with a dead
; block calling @unknown.
; CHECK-NOT: Function Attrs: readnone
-; CHECK: define void @test3_h()
+; CHECK: define void @test3_h() #3
define void @test3_h() {
entry:
%g = call i1 @test3_g(i1 false)
@@ -153,7 +153,7 @@ exit:
; This function should have had 'memory(none)' deduced for its SCC.
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
-; CHECK-NEXT: define void @test4_f1()
+; CHECK-NEXT: define void @test4_f1() #2
define void @test4_f1() noinline {
entry:
call void @test4_h()
@@ -175,8 +175,8 @@ entry:
}
; This function should have had 'memory(none)' deduced for its SCC.
-; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
-; CHECK-NEXT: define void @test4_h()
+; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none)
+; CHECK-NEXT: define void @test4_h() #1
define void @test4_h() noinline {
entry:
call void @test4_g(ptr @test4_f2)
diff --git a/llvm/test/Transforms/PhaseOrdering/pr95152.ll b/llvm/test/Transforms/PhaseOrdering/pr95152.ll
index 1e28856f32bd4..66e3ff901c23a 100644
--- a/llvm/test/Transforms/PhaseOrdering/pr95152.ll
+++ b/llvm/test/Transforms/PhaseOrdering/pr95152.ll
@@ -21,7 +21,7 @@ define void @j(ptr %p) optnone noinline {
define void @h(ptr %p) {
; CHECK-LABEL: define void @h(
-; CHECK-SAME: ptr initializes((0, 8)) [[P:%.*]]) local_unnamed_addr {
+; CHECK-SAME: ptr initializes((0, 8)) [[P:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: store i64 3, ptr [[P]], align 4
; CHECK-NEXT: tail call void @j(ptr nonnull [[P]])
; CHECK-NEXT: ret void
@@ -33,7 +33,7 @@ define void @h(ptr %p) {
define void @g(ptr dead_on_unwind noalias writable dereferenceable(8) align 8 %p) minsize {
; CHECK-LABEL: define void @g(
-; CHECK-SAME: ptr dead_on_unwind noalias writable writeonly align 8 captures(none) dereferenceable(8) initializes((0, 8)) [[P:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+; CHECK-SAME: ptr dead_on_unwind noalias writable writeonly align 8 captures(none) dereferenceable(8) initializes((0, 8)) [[P:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
; CHECK-NEXT: tail call void @h(ptr nonnull [[P]])
; CHECK-NEXT: ret void
;
@@ -45,7 +45,7 @@ define void @g(ptr dead_on_unwind noalias writable dereferenceable(8) align 8 %p
define void @f(ptr dead_on_unwind noalias %p) {
; CHECK-LABEL: define void @f(
-; CHECK-SAME: ptr dead_on_unwind noalias initializes((0, 8)) [[P:%.*]]) local_unnamed_addr {
+; CHECK-SAME: ptr dead_on_unwind noalias initializes((0, 8)) [[P:%.*]]) local_unnamed_addr #[[ATTR1]] {
; CHECK-NEXT: store i64 3, ptr [[P]], align 4
; CHECK-NEXT: tail call void @j(ptr nonnull align 8 dereferenceable(8) [[P]])
; CHECK-NEXT: store i64 43, ptr [[P]], align 4
>From fa864546deb7a089fe3325c6fca71990e82e3e6a Mon Sep 17 00:00:00 2001
From: Usha Gupta <usha.gupta at arm.com>
Date: Wed, 11 Jun 2025 16:19:02 +0000
Subject: [PATCH 4/9] Infer norecurse for functions with external function
call/library function callls if none of the user functions have had there
address taken
---
.../llvm/Transforms/IPO/FunctionAttrs.h | 6 ++-
llvm/lib/Passes/PassBuilderPipelines.cpp | 12 +++--
llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 51 ++++++++++++++++---
llvm/test/Other/new-pm-lto-defaults.ll | 5 +-
4 files changed, 60 insertions(+), 14 deletions(-)
diff --git a/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h b/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h
index 754714dceb7a6..bf2095bf7286a 100644
--- a/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h
+++ b/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h
@@ -49,8 +49,9 @@ LLVM_ABI bool thinLTOPropagateFunctionAttrs(
/// attribute. It also discovers function arguments that are not captured by
/// the function and marks them with the nocapture attribute.
struct PostOrderFunctionAttrsPass : PassInfoMixin<PostOrderFunctionAttrsPass> {
- PostOrderFunctionAttrsPass(bool SkipNonRecursive = false)
- : SkipNonRecursive(SkipNonRecursive) {}
+ PostOrderFunctionAttrsPass(bool SkipNonRecursive = false,
+ bool IsLTOPostLink = false)
+ : SkipNonRecursive(SkipNonRecursive), IsLTOPostLink(IsLTOPostLink) {}
LLVM_ABI PreservedAnalyses run(LazyCallGraph::SCC &C,
CGSCCAnalysisManager &AM, LazyCallGraph &CG,
CGSCCUpdateResult &UR);
@@ -61,6 +62,7 @@ struct PostOrderFunctionAttrsPass : PassInfoMixin<PostOrderFunctionAttrsPass> {
private:
bool SkipNonRecursive;
+ bool IsLTOPostLink;
};
/// A pass to do RPO deduction and propagation of function attributes.
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index a99146d5eaa34..1b4fad84d9771 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -975,7 +975,8 @@ PassBuilder::buildInlinerPipeline(OptimizationLevel Level,
// simplification pipeline, so this only needs to run when it could affect the
// function simplification pipeline, which is only the case with recursive
// functions.
- MainCGPipeline.addPass(PostOrderFunctionAttrsPass(/*SkipNonRecursive*/ true));
+ MainCGPipeline.addPass(PostOrderFunctionAttrsPass(
+ /*SkipNonRecursive*/ true, Phase == ThinOrFullLTOPhase::FullLTOPostLink));
// When at O3 add argument promotion to the pass pipeline.
// FIXME: It isn't at all clear why this should be limited to O3.
@@ -997,7 +998,8 @@ PassBuilder::buildInlinerPipeline(OptimizationLevel Level,
// Finally, deduce any function attributes based on the fully simplified
// function.
- MainCGPipeline.addPass(PostOrderFunctionAttrsPass());
+ MainCGPipeline.addPass(PostOrderFunctionAttrsPass(
+ false, Phase == ThinOrFullLTOPhase::FullLTOPostLink));
// Mark that the function is fully simplified and that it shouldn't be
// simplified again if we somehow revisit it due to CGSCC mutations unless
@@ -1914,7 +1916,7 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
// Promoting by-reference arguments to by-value exposes more constants to
// IPSCCP.
CGSCCPassManager CGPM;
- CGPM.addPass(PostOrderFunctionAttrsPass());
+ CGPM.addPass(PostOrderFunctionAttrsPass(false, true));
CGPM.addPass(ArgumentPromotionPass());
CGPM.addPass(
createCGSCCToFunctionPassAdaptor(SROAPass(SROAOptions::ModifyCFG)));
@@ -2075,8 +2077,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM),
PTO.EagerlyInvalidateAnalyses));
- MPM.addPass(
- createModuleToPostOrderCGSCCPassAdaptor(PostOrderFunctionAttrsPass()));
+ MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(
+ PostOrderFunctionAttrsPass(false, true)));
// Require the GlobalsAA analysis for the module so we can query it within
// MainFPM.
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index d08a2df9d9e66..9eb103a1dbccd 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -2060,7 +2060,9 @@ static void inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes,
}
static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
- SmallSet<Function *, 8> &Changed) {
+ SmallSet<Function *, 8> &Changed,
+ bool AnyFunctionsAddressIsTaken,
+ bool IsLTOPostLink) {
// Try and identify functions that do not recurse.
// If the SCC contains multiple nodes we know for sure there is recursion.
@@ -2073,6 +2075,10 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
if (F->hasAddressTaken())
return;
+
+ Module *M = F->getParent();
+ llvm::TargetLibraryInfoImpl TLII(llvm::Triple(M->getTargetTriple()));
+ llvm::TargetLibraryInfo TLI(TLII);
// If all of the calls in F are identifiable and can be proven to not
// callback F, F is norecurse. This check also detects self-recursion
// as F is not currently marked norecurse, so any call from F to F
@@ -2088,11 +2094,21 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
if (Callee->doesNotRecurse())
continue;
- // External call with NoCallback attribute.
+ LibFunc LF;
if (Callee->isDeclaration()) {
+ // External call with NoCallback attribute.
if (Callee->hasFnAttribute(Attribute::NoCallback))
continue;
- return;
+ // We rely on this only in post link stage when all functions in the
+ // program are known.
+ if (IsLTOPostLink && !AnyFunctionsAddressIsTaken &&
+ TLI.getLibFunc(Callee->getName(), LF))
+ continue;
+ // Do not consider external functions safe unless we are in LTO
+ // post-link stage and have information about all functions in the
+ // program.
+ if (!IsLTOPostLink)
+ return;
}
}
}
@@ -2260,7 +2276,8 @@ static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) {
template <typename AARGetterT>
static SmallSet<Function *, 8>
deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
- bool ArgAttrsOnly) {
+ bool ArgAttrsOnly, bool AnyFunctionAddressTaken = false,
+ bool IsLTOPostLink = false) {
SCCNodesResult Nodes = createSCCNodeSet(Functions);
// Bail if the SCC only contains optnone functions.
@@ -2291,7 +2308,8 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
addNoAliasAttrs(Nodes.SCCNodes, Changed);
addNonNullAttrs(Nodes.SCCNodes, Changed);
inferAttrsFromFunctionBodies(Nodes.SCCNodes, Changed);
- addNoRecurseAttrs(Nodes.SCCNodes, Changed);
+ addNoRecurseAttrs(Nodes.SCCNodes, Changed, AnyFunctionAddressTaken,
+ IsLTOPostLink);
}
// Finally, infer the maximal set of attributes from the ones we've inferred
@@ -2334,8 +2352,29 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
Functions.push_back(&N.getFunction());
}
+ bool AnyFunctionsAddressIsTaken = false;
+ // Check if any function in the whole program has its address taken.
+ // We use this information when inferring norecurse attribute: If there is
+ // no function whose address is taken, we conclude that any external function
+ // cannot callback into any user function.
+ if (IsLTOPostLink) {
+ // Get the parent Module of the Function
+ Module &M = *C.begin()->getFunction().getParent();
+ for (Function &F : M) {
+ // We only care about functions defined in user program whose addresses
+ // escape, making them potential callback targets.
+ if (F.isDeclaration())
+ continue;
+
+ if (F.hasAddressTaken()) {
+ AnyFunctionsAddressIsTaken = true;
+ break; // break if we found one
+ }
+ }
+ }
auto ChangedFunctions =
- deriveAttrsInPostOrder(Functions, AARGetter, ArgAttrsOnly);
+ deriveAttrsInPostOrder(Functions, AARGetter, ArgAttrsOnly,
+ AnyFunctionsAddressIsTaken, IsLTOPostLink);
if (ChangedFunctions.empty())
return PreservedAnalyses::all();
diff --git a/llvm/test/Other/new-pm-lto-defaults.ll b/llvm/test/Other/new-pm-lto-defaults.ll
index 3aea0f2061f3e..91285e2ec85c6 100644
--- a/llvm/test/Other/new-pm-lto-defaults.ll
+++ b/llvm/test/Other/new-pm-lto-defaults.ll
@@ -166,7 +166,9 @@
; CHECK-O-NEXT: Running pass: PrintModulePass
; Make sure we get the IR back out without changes when we print the module.
-; CHECK-O-LABEL: define void @foo(i32 %n) local_unnamed_addr {
+; CHECK-O23SZ: ; Function Attrs: norecurse
+; CHECK-O1-LABEL: define void @foo(i32 %n) local_unnamed_addr {
+; CHECK-O23SZ-LABEL: define void @foo(i32 %n) local_unnamed_addr #0 {
; CHECK-O-NEXT: entry:
; CHECK-O-NEXT: br label %loop
; CHECK-O: loop:
@@ -178,6 +180,7 @@
; CHECK-O: exit:
; CHECK-O-NEXT: ret void
; CHECK-O-NEXT: }
+; CHECK-O23SZ: attributes #0 = { norecurse }
;
declare void @bar() local_unnamed_addr
>From 5f1dd6f6b77b3183df40670fa0599d0feb2eadd3 Mon Sep 17 00:00:00 2001
From: Usha Gupta <usha.gupta at arm.com>
Date: Fri, 13 Jun 2025 09:53:34 +0000
Subject: [PATCH 5/9] Allow norecurse on functions calling library functions
only when there are no external references
---
llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 44 ++++++++++-------------
llvm/test/Other/new-pm-lto-defaults.ll | 5 +--
2 files changed, 19 insertions(+), 30 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 9eb103a1dbccd..fa64ff7aaea59 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -2061,8 +2061,7 @@ static void inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes,
static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
SmallSet<Function *, 8> &Changed,
- bool AnyFunctionsAddressIsTaken,
- bool IsLTOPostLink) {
+ bool NoFunctionsAddressIsTaken) {
// Try and identify functions that do not recurse.
// If the SCC contains multiple nodes we know for sure there is recursion.
@@ -2073,9 +2072,6 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
if (!F || !F->hasExactDefinition() || F->doesNotRecurse())
return;
- if (F->hasAddressTaken())
- return;
-
Module *M = F->getParent();
llvm::TargetLibraryInfoImpl TLII(llvm::Triple(M->getTargetTriple()));
llvm::TargetLibraryInfo TLI(TLII);
@@ -2086,6 +2082,9 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
for (auto &BB : *F) {
for (auto &I : BB.instructionsWithoutDebug()) {
if (auto *CB = dyn_cast<CallBase>(&I)) {
+ if (F->hasAddressTaken())
+ return;
+
Function *Callee = CB->getCalledFunction();
if (!Callee || Callee == F)
@@ -2096,19 +2095,11 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
LibFunc LF;
if (Callee->isDeclaration()) {
- // External call with NoCallback attribute.
- if (Callee->hasFnAttribute(Attribute::NoCallback))
+ if (Callee->hasFnAttribute(Attribute::NoCallback) ||
+ (NoFunctionsAddressIsTaken &&
+ TLI.getLibFunc(Callee->getName(), LF)))
continue;
- // We rely on this only in post link stage when all functions in the
- // program are known.
- if (IsLTOPostLink && !AnyFunctionsAddressIsTaken &&
- TLI.getLibFunc(Callee->getName(), LF))
- continue;
- // Do not consider external functions safe unless we are in LTO
- // post-link stage and have information about all functions in the
- // program.
- if (!IsLTOPostLink)
- return;
+ return;
}
}
}
@@ -2276,8 +2267,8 @@ static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) {
template <typename AARGetterT>
static SmallSet<Function *, 8>
deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
- bool ArgAttrsOnly, bool AnyFunctionAddressTaken = false,
- bool IsLTOPostLink = false) {
+ bool ArgAttrsOnly,
+ bool NoFunctionAddressIsTaken = false) {
SCCNodesResult Nodes = createSCCNodeSet(Functions);
// Bail if the SCC only contains optnone functions.
@@ -2308,8 +2299,7 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
addNoAliasAttrs(Nodes.SCCNodes, Changed);
addNonNullAttrs(Nodes.SCCNodes, Changed);
inferAttrsFromFunctionBodies(Nodes.SCCNodes, Changed);
- addNoRecurseAttrs(Nodes.SCCNodes, Changed, AnyFunctionAddressTaken,
- IsLTOPostLink);
+ addNoRecurseAttrs(Nodes.SCCNodes, Changed, NoFunctionAddressIsTaken);
}
// Finally, infer the maximal set of attributes from the ones we've inferred
@@ -2352,12 +2342,13 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
Functions.push_back(&N.getFunction());
}
- bool AnyFunctionsAddressIsTaken = false;
+ bool NoFunctionsAddressIsTaken = false;
// Check if any function in the whole program has its address taken.
// We use this information when inferring norecurse attribute: If there is
// no function whose address is taken, we conclude that any external function
// cannot callback into any user function.
if (IsLTOPostLink) {
+ bool AnyFunctionsAddressIsTaken = false;
// Get the parent Module of the Function
Module &M = *C.begin()->getFunction().getParent();
for (Function &F : M) {
@@ -2366,15 +2357,16 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
if (F.isDeclaration())
continue;
- if (F.hasAddressTaken()) {
+ if (!F.hasLocalLinkage() || F.hasAddressTaken()) {
AnyFunctionsAddressIsTaken = true;
break; // break if we found one
}
}
+ NoFunctionsAddressIsTaken = !AnyFunctionsAddressIsTaken;
}
- auto ChangedFunctions =
- deriveAttrsInPostOrder(Functions, AARGetter, ArgAttrsOnly,
- AnyFunctionsAddressIsTaken, IsLTOPostLink);
+ auto ChangedFunctions = deriveAttrsInPostOrder(
+ Functions, AARGetter, ArgAttrsOnly, NoFunctionsAddressIsTaken);
+
if (ChangedFunctions.empty())
return PreservedAnalyses::all();
diff --git a/llvm/test/Other/new-pm-lto-defaults.ll b/llvm/test/Other/new-pm-lto-defaults.ll
index 91285e2ec85c6..3aea0f2061f3e 100644
--- a/llvm/test/Other/new-pm-lto-defaults.ll
+++ b/llvm/test/Other/new-pm-lto-defaults.ll
@@ -166,9 +166,7 @@
; CHECK-O-NEXT: Running pass: PrintModulePass
; Make sure we get the IR back out without changes when we print the module.
-; CHECK-O23SZ: ; Function Attrs: norecurse
-; CHECK-O1-LABEL: define void @foo(i32 %n) local_unnamed_addr {
-; CHECK-O23SZ-LABEL: define void @foo(i32 %n) local_unnamed_addr #0 {
+; CHECK-O-LABEL: define void @foo(i32 %n) local_unnamed_addr {
; CHECK-O-NEXT: entry:
; CHECK-O-NEXT: br label %loop
; CHECK-O: loop:
@@ -180,7 +178,6 @@
; CHECK-O: exit:
; CHECK-O-NEXT: ret void
; CHECK-O-NEXT: }
-; CHECK-O23SZ: attributes #0 = { norecurse }
;
declare void @bar() local_unnamed_addr
>From 8df2493b435016c5c0e735185c03d306385f3d03 Mon Sep 17 00:00:00 2001
From: Usha Gupta <usha.gupta at arm.com>
Date: Mon, 16 Jun 2025 11:24:07 +0000
Subject: [PATCH 6/9] Check for functions's LocalLinkage when applying
norecurse attribute
---
llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 21 +-
.../Other/cgscc-iterate-function-mutation.ll | 40 ++--
.../FunctionAttrs/nofree-attributor.ll | 4 +-
llvm/test/Transforms/FunctionAttrs/nonnull.ll | 2 +-
.../norecurse_multiSCC_indirect_recursion.ll | 40 ++--
.../norecurse_multiSCC_indirect_recursion1.ll | 43 ++--
.../norecurse_self_recursive_callee.ll | 36 ++--
.../test/Transforms/FunctionAttrs/nounwind.ll | 193 ++++++------------
.../FunctionAttrs/sendmsg-nocallback.ll | 9 +-
llvm/test/Transforms/Inline/cgscc-update.ll | 20 +-
llvm/test/Transforms/PhaseOrdering/pr95152.ll | 6 +-
11 files changed, 171 insertions(+), 243 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index fa64ff7aaea59..de671c48f572e 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -2063,7 +2063,6 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
SmallSet<Function *, 8> &Changed,
bool NoFunctionsAddressIsTaken) {
// Try and identify functions that do not recurse.
-
// If the SCC contains multiple nodes we know for sure there is recursion.
if (SCCNodes.size() != 1)
return;
@@ -2072,9 +2071,6 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
if (!F || !F->hasExactDefinition() || F->doesNotRecurse())
return;
- Module *M = F->getParent();
- llvm::TargetLibraryInfoImpl TLII(llvm::Triple(M->getTargetTriple()));
- llvm::TargetLibraryInfo TLI(TLII);
// If all of the calls in F are identifiable and can be proven to not
// callback F, F is norecurse. This check also detects self-recursion
// as F is not currently marked norecurse, so any call from F to F
@@ -2082,9 +2078,6 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
for (auto &BB : *F) {
for (auto &I : BB.instructionsWithoutDebug()) {
if (auto *CB = dyn_cast<CallBase>(&I)) {
- if (F->hasAddressTaken())
- return;
-
Function *Callee = CB->getCalledFunction();
if (!Callee || Callee == F)
@@ -2093,21 +2086,25 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
if (Callee->doesNotRecurse())
continue;
- LibFunc LF;
if (Callee->isDeclaration()) {
if (Callee->hasFnAttribute(Attribute::NoCallback) ||
- (NoFunctionsAddressIsTaken &&
- TLI.getLibFunc(Callee->getName(), LF)))
+ NoFunctionsAddressIsTaken)
continue;
return;
+ } else if (F->hasAddressTaken() || !F->hasLocalLinkage()) {
+ // Control reaches here only for callees which are defined in this
+ // module and do not satisfy conditions for norecurse attribute.
+ // In such a case, if function F has external linkage or address
+ // taken, conversatively avoid adding norecurse.
+ return;
}
}
}
}
// Every call was either to an external function guaranteed to not make a
- // call to this function or a direct call to internal function and we have no
- // indirect recursion as the SCC size is one. This function cannot recurse.
+ // call to this function or a direct call to internal function. Also, SCC is
+ // one. Together, the above checks ensures, this function cannot norecurse.
F->setDoesNotRecurse();
++NumNoRecurse;
Changed.insert(F);
diff --git a/llvm/test/Other/cgscc-iterate-function-mutation.ll b/llvm/test/Other/cgscc-iterate-function-mutation.ll
index 631674362bb09..aafd38d1e8825 100644
--- a/llvm/test/Other/cgscc-iterate-function-mutation.ll
+++ b/llvm/test/Other/cgscc-iterate-function-mutation.ll
@@ -9,7 +9,7 @@ declare void @reference_function_pointer(ptr) nofree nosync readnone
; and the RefSCCs that those functions are in, we re-run the CGSCC passes to
; observe the refined call graph structure.
-; CHECK: define void @test1_a() #1 {
+; CHECK: define void @test1_a() {
define void @test1_a() {
call void @test1_b1()
call void @test1_b2()
@@ -128,7 +128,7 @@ exit:
; multiple layers that have to be traversed in the correct order instead of
; a single node.
-; CHECK: define void @test3_a() #1 {
+; CHECK: define void @test3_a() {
define void @test3_a() {
call void @test3_b11()
call void @test3_b21()
@@ -137,13 +137,13 @@ define void @test3_a() {
ret void
}
-; CHECK: define void @test3_b11() #2 {
+; CHECK: define void @test3_b11() #0 {
define void @test3_b11() {
call void @test3_b12()
ret void
}
-; CHECK: define void @test3_b12() #2 {
+; CHECK: define void @test3_b12() #0 {
define void @test3_b12() {
call void @test3_b13()
ret void
@@ -155,13 +155,13 @@ define void @test3_b13() {
ret void
}
-; CHECK: define void @test3_b21() #2 {
+; CHECK: define void @test3_b21() #0 {
define void @test3_b21() {
call void @test3_b22()
ret void
}
-; CHECK: define void @test3_b22() #2 {
+; CHECK: define void @test3_b22() #0 {
define void @test3_b22() {
call void @test3_b23()
ret void
@@ -180,13 +180,13 @@ exit:
ret void
}
-; CHECK: define void @test3_b31() #1 {
+; CHECK: define void @test3_b31() {
define void @test3_b31() {
call void @test3_b32()
ret void
}
-; CHECK: define void @test3_b32() #1 {
+; CHECK: define void @test3_b32() {
define void @test3_b32() {
call void @test3_b33()
ret void
@@ -205,13 +205,13 @@ exit:
ret void
}
-; CHECK: define void @test3_b41() #2 {
+; CHECK: define void @test3_b41() #0 {
define void @test3_b41() {
call void @test3_b42()
ret void
}
-; CHECK: define void @test3_b42() #2 {
+; CHECK: define void @test3_b42() #0 {
define void @test3_b42() {
call void @test3_b43()
ret void
@@ -244,13 +244,13 @@ define void @test4_a() {
ret void
}
-; CHECK: define void @test4_b11() #2 {
+; CHECK: define void @test4_b11() #0 {
define void @test4_b11() {
call void @test4_b12()
ret void
}
-; CHECK: define void @test4_b12() #2 {
+; CHECK: define void @test4_b12() #0 {
define void @test4_b12() {
call void @test4_b13()
ret void
@@ -262,20 +262,20 @@ define void @test4_b13() {
ret void
}
-; CHECK: define void @test4_b21() #2 {
+; CHECK: define void @test4_b21() #0 {
define void @test4_b21() {
call void @test4_b22()
ret void
}
-; CHECK: define void @test4_b22() #2 {
+; CHECK: define void @test4_b22() #0 {
define void @test4_b22() {
call void @test4_b23()
ret void
}
; CHECK: define void @test4_b23() #0 {
-define void @test4_b23() #0 {
+define void @test4_b23() {
call void @reference_function_pointer(ptr @test4_a)
br i1 false, label %dead, label %exit
@@ -287,13 +287,13 @@ exit:
ret void
}
-; CHECK: define void @test4_b31() #1 {
+; CHECK: define void @test4_b31() {
define void @test4_b31() {
call void @test4_b32()
ret void
}
-; CHECK: define void @test4_b32() #1 {
+; CHECK: define void @test4_b32() {
define void @test4_b32() {
call void @test4_b33()
ret void
@@ -313,13 +313,13 @@ exit:
ret void
}
-; CHECK: define void @test4_b41() #2 {
+; CHECK: define void @test4_b41() #0 {
define void @test4_b41() {
call void @test4_b42()
ret void
}
-; CHECK: define void @test4_b42() #2 {
+; CHECK: define void @test4_b42() #0 {
define void @test4_b42() {
call void @test4_b43()
ret void
@@ -339,5 +339,3 @@ exit:
}
; CHECK: attributes #0 = { nofree nosync memory(none) }
-; CHECK: attributes #1 = { norecurse }
-; CHECK: attributes #2 = { nofree norecurse nosync memory(none) }
diff --git a/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll b/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll
index 1457e96e1af68..5a0b6ef1e3043 100644
--- a/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll
@@ -250,9 +250,9 @@ define void @f1() #0 {
}
define void @f2() #0 {
-; FNATTR: Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable
+; FNATTR: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
; FNATTR-LABEL: define {{[^@]+}}@f2
-; FNATTR-SAME: () #[[ATTR7:[0-9]+]] {
+; FNATTR-SAME: () #[[ATTR4]] {
; FNATTR-NEXT: tail call void @f1()
; FNATTR-NEXT: ret void
;
diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll
index b484210d75957..483b560ece6c8 100644
--- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll
@@ -1105,7 +1105,7 @@ define internal void @optnone(ptr dereferenceable(4) %a) optnone noinline {
}
define void @make_live(ptr nonnull dereferenceable(8) %a) {
; FNATTRS-LABEL: define void @make_live(
-; FNATTRS-SAME: ptr nonnull dereferenceable(8) [[A:%.*]]) #[[ATTR13:[0-9]+]] {
+; FNATTRS-SAME: ptr nonnull dereferenceable(8) [[A:%.*]]) {
; FNATTRS-NEXT: call void @naked(ptr nonnull align 16 dereferenceable(8) [[A]])
; FNATTRS-NEXT: call void @control(ptr nonnull align 16 dereferenceable(8) [[A]])
; FNATTRS-NEXT: call void @optnone(ptr nonnull align 16 dereferenceable(8) [[A]])
diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll
index c0af16b069bde..3f616b1bf76f5 100644
--- a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll
@@ -3,8 +3,8 @@
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "aarch64-unknown-linux-gnu"
-; This test includes a call graph with multiple SCCs. The purpose of this is
-; to check that norecurse is not added when a function is part of non-singular
+; This test includes a call graph with multiple SCCs. The purpose of this is
+; to check that norecurse is not added when a function is part of non-singular
; SCC.
; There are three different SCCs in this test:
; SCC#1: main, foo, bar, foo1, bar1
@@ -13,9 +13,9 @@ target triple = "aarch64-unknown-linux-gnu"
; None of these functions should be marked as norecurse
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @bar1() local_unnamed_addr #0 {
+define internal void @bar1() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @bar1(
+; CHECK-LABEL: define internal void @bar1(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @main()
@@ -45,9 +45,9 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @foo1() local_unnamed_addr #0 {
+define internal void @foo1() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @foo1(
+; CHECK-LABEL: define internal void @foo1(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @bar1()
@@ -59,9 +59,9 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @bar() local_unnamed_addr #0 {
+define internal void @bar() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @bar(
+; CHECK-LABEL: define internal void @bar(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @foo1()
@@ -73,9 +73,9 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @foo() local_unnamed_addr #0 {
+define internal void @foo() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @foo(
+; CHECK-LABEL: define internal void @foo(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @bar()
@@ -87,9 +87,9 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @bar4() local_unnamed_addr #0 {
+define internal void @bar4() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @bar4(
+; CHECK-LABEL: define internal void @bar4(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @bar2()
@@ -101,9 +101,9 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @bar2() local_unnamed_addr #0 {
+define internal void @bar2() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @bar2(
+; CHECK-LABEL: define internal void @bar2(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @bar3()
@@ -115,9 +115,9 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @bar3() local_unnamed_addr #0 {
+define internal void @bar3() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @bar3(
+; CHECK-LABEL: define internal void @bar3(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @bar4()
@@ -129,9 +129,9 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @fun() local_unnamed_addr #0 {
+define internal void @fun() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @fun(
+; CHECK-LABEL: define internal void @fun(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @baz()
@@ -143,9 +143,9 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @baz() local_unnamed_addr #0 {
+define internal void @baz() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @baz(
+; CHECK-LABEL: define internal void @baz(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @fun()
diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll
index 60bbaab2a7d66..e35ad4101fecb 100644
--- a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll
@@ -3,19 +3,20 @@
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "aarch64-unknown-linux-gnu"
-; This test includes a call graph with multiple SCCs. The purpose of this is
-; to check that norecurse is added to a function which calls a function which
-; is indirectly recursive but is not part of the recursive chain.
+; This test includes a call graph with multiple SCCs. The purpose of this is
+; to check that norecurse is added to a function which calls a function which
+; is indirectly recursive but is not part of the recursive chain.
; There are two SCCs in this test:
; SCC#1: bar2, bar3, bar4
; SCC#2: baz, fun
; main() calls bar2 and baz, both of which are part of some indirect recursive
-; chain. but does not call back main() and hence main() can be marked as
-; norecurse.
+; chain. but does not call back main() and hence main() can be marked as
+; norecurse. But main() does not have internal linkage, hence we avoid adding
+; norecurse for main() as well.
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define dso_local noundef i32 @main() local_unnamed_addr #0 {
-; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
; CHECK-LABEL: define dso_local noundef i32 @main(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
@@ -30,10 +31,10 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @bar4() local_unnamed_addr #0 {
+define internal void @bar4() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @bar4(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+; CHECK-LABEL: define internal void @bar4(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @bar2()
; CHECK-NEXT: ret void
@@ -44,10 +45,10 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @bar2() local_unnamed_addr #0 {
+define internal void @bar2() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @bar2(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR1]] {
+; CHECK-LABEL: define internal void @bar2(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @bar3()
; CHECK-NEXT: ret void
@@ -58,10 +59,10 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @bar3() local_unnamed_addr #0 {
+define internal void @bar3() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @bar3(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR1]] {
+; CHECK-LABEL: define internal void @bar3(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @bar4()
; CHECK-NEXT: ret void
@@ -72,10 +73,10 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @fun() local_unnamed_addr #0 {
+define internal void @fun() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @fun(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR1]] {
+; CHECK-LABEL: define internal void @fun(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @baz()
; CHECK-NEXT: ret void
@@ -86,10 +87,10 @@ entry:
}
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local void @baz() local_unnamed_addr #0 {
+define internal void @baz() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define dso_local void @baz(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR1]] {
+; CHECK-LABEL: define internal void @baz(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @fun()
; CHECK-NEXT: ret void
diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll
index 9b87c4e4ad76c..e151e7178026a 100644
--- a/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll
@@ -3,10 +3,10 @@
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "aarch64-unknown-linux-gnu"
-; This test includes a call graph with a self recursive function.
-; The purpose of this is to check that norecurse is added to functions
-; which have a self-recursive function in the call-chain.
-; The call-chain in this test is as follows
+; This test includes a call graph with a self recursive function.
+; The purpose of this is to check that norecurse is added to functions
+; which have a self-recursive function in the call-chain.
+; The call-chain in this test is as follows
; main -> bob -> callee1 -> callee2 -> callee3 -> callee4 -> callee5
; where callee5 is self recursive.
@@ -14,9 +14,9 @@ target triple = "aarch64-unknown-linux-gnu"
@y = dso_local global i32 2, align 4
; Function Attrs: nofree noinline norecurse nounwind memory(readwrite, argmem: none) uwtable
-define dso_local void @callee6() local_unnamed_addr #0 {
+define internal void @callee6() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind memory(readwrite, argmem: none) uwtable
-; CHECK-LABEL: define dso_local void @callee6(
+; CHECK-LABEL: define internal void @callee6(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[TMP0:%.*]] = load volatile i32, ptr @y, align 4, !tbaa [[TBAA8:![0-9]+]]
@@ -32,9 +32,9 @@ entry:
}
; Function Attrs: nofree noinline nounwind uwtable
-define dso_local void @callee5(i32 noundef %x) local_unnamed_addr #1 {
+define internal void @callee5(i32 noundef %x) local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline nounwind uwtable
-; CHECK-LABEL: define dso_local void @callee5(
+; CHECK-LABEL: define internal void @callee5(
; CHECK-SAME: i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], 0
@@ -60,9 +60,9 @@ if.end: ; preds = %if.then, %entry
}
; Function Attrs: nofree noinline nounwind uwtable
-define dso_local void @callee4() local_unnamed_addr #1 {
+define internal void @callee4() local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
-; CHECK-LABEL: define dso_local void @callee4(
+; CHECK-LABEL: define internal void @callee4(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR2:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[TMP0:%.*]] = load volatile i32, ptr @x, align 4, !tbaa [[TBAA8]]
@@ -76,9 +76,9 @@ entry:
}
; Function Attrs: nofree noinline nounwind uwtable
-define dso_local void @callee3() local_unnamed_addr #1 {
+define internal void @callee3() local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
-; CHECK-LABEL: define dso_local void @callee3(
+; CHECK-LABEL: define internal void @callee3(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @callee4()
@@ -90,9 +90,9 @@ entry:
}
; Function Attrs: nofree noinline nounwind uwtable
-define dso_local void @callee2() local_unnamed_addr #1 {
+define internal void @callee2() local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
-; CHECK-LABEL: define dso_local void @callee2(
+; CHECK-LABEL: define internal void @callee2(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @callee3()
@@ -104,9 +104,9 @@ entry:
}
; Function Attrs: nofree noinline nounwind uwtable
-define dso_local void @callee1() local_unnamed_addr #1 {
+define internal void @callee1() local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
-; CHECK-LABEL: define dso_local void @callee1(
+; CHECK-LABEL: define internal void @callee1(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @callee2()
@@ -118,9 +118,9 @@ entry:
}
; Function Attrs: nofree noinline nounwind uwtable
-define dso_local void @bob() local_unnamed_addr #1 {
+define internal void @bob() local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
-; CHECK-LABEL: define dso_local void @bob(
+; CHECK-LABEL: define internal void @bob(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @callee1()
diff --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll
index b613c47435a03..ecba7a0318cd2 100644
--- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nounwind.ll
@@ -157,9 +157,7 @@ define i32 @catch_thing() personality ptr @__gxx_personality_v0 {
}
define i32 @catch_thing_user() {
-; FNATTRS: Function Attrs: norecurse
-; FNATTRS-LABEL: define {{[^@]+}}@catch_thing_user
-; FNATTRS-SAME: () #[[ATTR2:[0-9]+]] {
+; FNATTRS-LABEL: define {{[^@]+}}@catch_thing_user() {
; FNATTRS-NEXT: [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing()
; FNATTRS-NEXT: ret i32 [[CATCH_THING_CALL]]
;
@@ -176,31 +174,18 @@ declare void @abort() nounwind
@catch_ty = external global ptr
define void @catch_specific_landingpad() personality ptr @__gxx_personality_v0 {
-; FNATTRS: Function Attrs: noreturn
-; FNATTRS-LABEL: define {{[^@]+}}@catch_specific_landingpad
-; FNATTRS-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
-; FNATTRS-NEXT: invoke void @do_throw()
-; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; FNATTRS: lpad:
-; FNATTRS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; FNATTRS-NEXT: catch ptr @catch_ty
-; FNATTRS-NEXT: call void @abort()
-; FNATTRS-NEXT: unreachable
-; FNATTRS: unreachable:
-; FNATTRS-NEXT: unreachable
-;
-; ATTRIBUTOR: Function Attrs: noreturn
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@catch_specific_landingpad
-; ATTRIBUTOR-SAME: () #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 {
-; ATTRIBUTOR-NEXT: invoke void @do_throw()
-; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; ATTRIBUTOR: lpad:
-; ATTRIBUTOR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; ATTRIBUTOR-NEXT: catch ptr @catch_ty
-; ATTRIBUTOR-NEXT: call void @abort()
-; ATTRIBUTOR-NEXT: unreachable
-; ATTRIBUTOR: unreachable:
-; ATTRIBUTOR-NEXT: unreachable
+; COMMON: Function Attrs: noreturn
+; COMMON-LABEL: define {{[^@]+}}@catch_specific_landingpad
+; COMMON-SAME: () #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT: invoke void @do_throw()
+; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON: lpad:
+; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT: catch ptr @catch_ty
+; COMMON-NEXT: call void @abort()
+; COMMON-NEXT: unreachable
+; COMMON: unreachable:
+; COMMON-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
@@ -216,31 +201,18 @@ unreachable:
}
define void @catch_all_landingpad() personality ptr @__gxx_personality_v0 {
-; FNATTRS: Function Attrs: noreturn nounwind
-; FNATTRS-LABEL: define {{[^@]+}}@catch_all_landingpad
-; FNATTRS-SAME: () #[[ATTR5:[0-9]+]] personality ptr @__gxx_personality_v0 {
-; FNATTRS-NEXT: invoke void @do_throw()
-; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; FNATTRS: lpad:
-; FNATTRS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; FNATTRS-NEXT: catch ptr null
-; FNATTRS-NEXT: call void @abort()
-; FNATTRS-NEXT: unreachable
-; FNATTRS: unreachable:
-; FNATTRS-NEXT: unreachable
-;
-; ATTRIBUTOR: Function Attrs: noreturn nounwind
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@catch_all_landingpad
-; ATTRIBUTOR-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
-; ATTRIBUTOR-NEXT: invoke void @do_throw()
-; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; ATTRIBUTOR: lpad:
-; ATTRIBUTOR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; ATTRIBUTOR-NEXT: catch ptr null
-; ATTRIBUTOR-NEXT: call void @abort()
-; ATTRIBUTOR-NEXT: unreachable
-; ATTRIBUTOR: unreachable:
-; ATTRIBUTOR-NEXT: unreachable
+; COMMON: Function Attrs: noreturn nounwind
+; COMMON-LABEL: define {{[^@]+}}@catch_all_landingpad
+; COMMON-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT: invoke void @do_throw()
+; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON: lpad:
+; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT: catch ptr null
+; COMMON-NEXT: call void @abort()
+; COMMON-NEXT: unreachable
+; COMMON: unreachable:
+; COMMON-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
@@ -256,31 +228,18 @@ unreachable:
}
define void @filter_specific_landingpad() personality ptr @__gxx_personality_v0 {
-; FNATTRS: Function Attrs: noreturn
-; FNATTRS-LABEL: define {{[^@]+}}@filter_specific_landingpad
-; FNATTRS-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
-; FNATTRS-NEXT: invoke void @do_throw()
-; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; FNATTRS: lpad:
-; FNATTRS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; FNATTRS-NEXT: filter [1 x ptr] [ptr @catch_ty]
-; FNATTRS-NEXT: call void @abort()
-; FNATTRS-NEXT: unreachable
-; FNATTRS: unreachable:
-; FNATTRS-NEXT: unreachable
-;
-; ATTRIBUTOR: Function Attrs: noreturn
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@filter_specific_landingpad
-; ATTRIBUTOR-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
-; ATTRIBUTOR-NEXT: invoke void @do_throw()
-; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; ATTRIBUTOR: lpad:
-; ATTRIBUTOR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; ATTRIBUTOR-NEXT: filter [1 x ptr] [ptr @catch_ty]
-; ATTRIBUTOR-NEXT: call void @abort()
-; ATTRIBUTOR-NEXT: unreachable
-; ATTRIBUTOR: unreachable:
-; ATTRIBUTOR-NEXT: unreachable
+; COMMON: Function Attrs: noreturn
+; COMMON-LABEL: define {{[^@]+}}@filter_specific_landingpad
+; COMMON-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT: invoke void @do_throw()
+; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON: lpad:
+; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT: filter [1 x ptr] [ptr @catch_ty]
+; COMMON-NEXT: call void @abort()
+; COMMON-NEXT: unreachable
+; COMMON: unreachable:
+; COMMON-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
@@ -296,31 +255,18 @@ unreachable:
}
define void @filter_none_landingpad() personality ptr @__gxx_personality_v0 {
-; FNATTRS: Function Attrs: noreturn nounwind
-; FNATTRS-LABEL: define {{[^@]+}}@filter_none_landingpad
-; FNATTRS-SAME: () #[[ATTR5]] personality ptr @__gxx_personality_v0 {
-; FNATTRS-NEXT: invoke void @do_throw()
-; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; FNATTRS: lpad:
-; FNATTRS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; FNATTRS-NEXT: filter [0 x ptr] zeroinitializer
-; FNATTRS-NEXT: call void @abort()
-; FNATTRS-NEXT: unreachable
-; FNATTRS: unreachable:
-; FNATTRS-NEXT: unreachable
-;
-; ATTRIBUTOR: Function Attrs: noreturn nounwind
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@filter_none_landingpad
-; ATTRIBUTOR-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
-; ATTRIBUTOR-NEXT: invoke void @do_throw()
-; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; ATTRIBUTOR: lpad:
-; ATTRIBUTOR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; ATTRIBUTOR-NEXT: filter [0 x ptr] zeroinitializer
-; ATTRIBUTOR-NEXT: call void @abort()
-; ATTRIBUTOR-NEXT: unreachable
-; ATTRIBUTOR: unreachable:
-; ATTRIBUTOR-NEXT: unreachable
+; COMMON: Function Attrs: noreturn nounwind
+; COMMON-LABEL: define {{[^@]+}}@filter_none_landingpad
+; COMMON-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT: invoke void @do_throw()
+; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON: lpad:
+; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT: filter [0 x ptr] zeroinitializer
+; COMMON-NEXT: call void @abort()
+; COMMON-NEXT: unreachable
+; COMMON: unreachable:
+; COMMON-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
@@ -336,31 +282,18 @@ unreachable:
}
define void @cleanup_landingpad() personality ptr @__gxx_personality_v0 {
-; FNATTRS: Function Attrs: noreturn
-; FNATTRS-LABEL: define {{[^@]+}}@cleanup_landingpad
-; FNATTRS-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
-; FNATTRS-NEXT: invoke void @do_throw()
-; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; FNATTRS: lpad:
-; FNATTRS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; FNATTRS-NEXT: cleanup
-; FNATTRS-NEXT: call void @abort()
-; FNATTRS-NEXT: unreachable
-; FNATTRS: unreachable:
-; FNATTRS-NEXT: unreachable
-;
-; ATTRIBUTOR: Function Attrs: noreturn
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@cleanup_landingpad
-; ATTRIBUTOR-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
-; ATTRIBUTOR-NEXT: invoke void @do_throw()
-; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; ATTRIBUTOR: lpad:
-; ATTRIBUTOR-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; ATTRIBUTOR-NEXT: cleanup
-; ATTRIBUTOR-NEXT: call void @abort()
-; ATTRIBUTOR-NEXT: unreachable
-; ATTRIBUTOR: unreachable:
-; ATTRIBUTOR-NEXT: unreachable
+; COMMON: Function Attrs: noreturn
+; COMMON-LABEL: define {{[^@]+}}@cleanup_landingpad
+; COMMON-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT: invoke void @do_throw()
+; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON: lpad:
+; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT: cleanup
+; COMMON-NEXT: call void @abort()
+; COMMON-NEXT: unreachable
+; COMMON: unreachable:
+; COMMON-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
@@ -378,7 +311,7 @@ unreachable:
define void @cleanuppad() personality ptr @__gxx_personality_v0 {
; FNATTRS: Function Attrs: noreturn
; FNATTRS-LABEL: define {{[^@]+}}@cleanuppad
-; FNATTRS-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
; FNATTRS-NEXT: invoke void @do_throw()
; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
; FNATTRS: cpad:
@@ -415,7 +348,7 @@ unreachable:
define void @catchswitch_cleanuppad() personality ptr @__gxx_personality_v0 {
; FNATTRS: Function Attrs: noreturn
; FNATTRS-LABEL: define {{[^@]+}}@catchswitch_cleanuppad
-; FNATTRS-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
; FNATTRS-NEXT: invoke void @do_throw()
; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
; FNATTRS: cs:
diff --git a/llvm/test/Transforms/FunctionAttrs/sendmsg-nocallback.ll b/llvm/test/Transforms/FunctionAttrs/sendmsg-nocallback.ll
index f5fde8822e30b..4d5db3263f527 100644
--- a/llvm/test/Transforms/FunctionAttrs/sendmsg-nocallback.ll
+++ b/llvm/test/Transforms/FunctionAttrs/sendmsg-nocallback.ll
@@ -33,9 +33,9 @@ define internal void @sendmsghalt_is_norecurse() {
}
define internal i32 @sendmsg_rtn_is_norecurse() {
-; FNATTRS: Function Attrs: mustprogress nounwind willreturn
+; FNATTRS: Function Attrs: mustprogress norecurse nounwind willreturn
; FNATTRS-LABEL: define internal i32 @sendmsg_rtn_is_norecurse(
-; FNATTRS-SAME: ) #[[ATTR2:[0-9]+]] {
+; FNATTRS-SAME: ) #[[ATTR0]] {
; FNATTRS-NEXT: [[RES:%.*]] = call i32 @llvm.amdgcn.s.sendmsg.rtn.i32(i32 1)
; FNATTRS-NEXT: ret i32 [[RES]]
;
@@ -72,9 +72,8 @@ define void @user() {
;.
; FNATTRS: attributes #[[ATTR0]] = { mustprogress norecurse nounwind willreturn }
; FNATTRS: attributes #[[ATTR1]] = { norecurse nounwind }
-; FNATTRS: attributes #[[ATTR2]] = { mustprogress nounwind willreturn }
-; FNATTRS: attributes #[[ATTR3:[0-9]+]] = { nocallback nounwind willreturn }
-; FNATTRS: attributes #[[ATTR4:[0-9]+]] = { nocallback nounwind }
+; FNATTRS: attributes #[[ATTR2:[0-9]+]] = { nocallback nounwind willreturn }
+; FNATTRS: attributes #[[ATTR3:[0-9]+]] = { nocallback nounwind }
;.
; ATTRIBUTOR: attributes #[[ATTR0]] = { mustprogress norecurse nounwind willreturn }
; ATTRIBUTOR: attributes #[[ATTR1]] = { norecurse nounwind }
diff --git a/llvm/test/Transforms/Inline/cgscc-update.ll b/llvm/test/Transforms/Inline/cgscc-update.ll
index f1d6325b7ee5a..f1121fa88a4b1 100644
--- a/llvm/test/Transforms/Inline/cgscc-update.ll
+++ b/llvm/test/Transforms/Inline/cgscc-update.ll
@@ -11,7 +11,7 @@ declare void @unknown()
; Basic correctness check: this should get annotated as memory(none).
; CHECK: Function Attrs: nounwind memory(none)
-; CHECK-NEXT: declare void @readnone() #0
+; CHECK-NEXT: declare void @readnone()
declare void @readnone() readnone nounwind
; The 'test1_' prefixed functions are designed to trigger forming a new direct
@@ -27,8 +27,8 @@ entry:
}
; This function should have had 'memory(none)' deduced for its SCC.
-; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none)
-; CHECK-NEXT: define void @test1_g() #1
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
+; CHECK-NEXT: define void @test1_g()
define void @test1_g() noinline {
entry:
call void @test1_f(ptr @test1_h)
@@ -37,7 +37,7 @@ entry:
; This function should have had 'memory(none)' deduced for its SCC.
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
-; CHECK-NEXT: define void @test1_h() #2
+; CHECK-NEXT: define void @test1_h()
define void @test1_h() noinline {
entry:
call void @test1_g()
@@ -60,7 +60,7 @@ entry:
; This function should have had 'memory(none)' deduced for its SCC.
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
-; CHECK-NEXT: define void @test2_g() #2
+; CHECK-NEXT: define void @test2_g()
define void @test2_g() noinline {
entry:
%p = call ptr @test2_f()
@@ -70,7 +70,7 @@ entry:
; This function should have had 'memory(none)' deduced for its SCC.
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
-; CHECK-NEXT: define void @test2_h() #2
+; CHECK-NEXT: define void @test2_h()
define void @test2_h() noinline {
entry:
call void @test2_g()
@@ -129,7 +129,7 @@ exit:
; doesn't simplify the caller's trivially dead CFG and so we end with a dead
; block calling @unknown.
; CHECK-NOT: Function Attrs: readnone
-; CHECK: define void @test3_h() #3
+; CHECK: define void @test3_h()
define void @test3_h() {
entry:
%g = call i1 @test3_g(i1 false)
@@ -153,7 +153,7 @@ exit:
; This function should have had 'memory(none)' deduced for its SCC.
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
-; CHECK-NEXT: define void @test4_f1() #2
+; CHECK-NEXT: define void @test4_f1()
define void @test4_f1() noinline {
entry:
call void @test4_h()
@@ -175,8 +175,8 @@ entry:
}
; This function should have had 'memory(none)' deduced for its SCC.
-; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none)
-; CHECK-NEXT: define void @test4_h() #1
+; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none)
+; CHECK-NEXT: define void @test4_h()
define void @test4_h() noinline {
entry:
call void @test4_g(ptr @test4_f2)
diff --git a/llvm/test/Transforms/PhaseOrdering/pr95152.ll b/llvm/test/Transforms/PhaseOrdering/pr95152.ll
index 66e3ff901c23a..1e28856f32bd4 100644
--- a/llvm/test/Transforms/PhaseOrdering/pr95152.ll
+++ b/llvm/test/Transforms/PhaseOrdering/pr95152.ll
@@ -21,7 +21,7 @@ define void @j(ptr %p) optnone noinline {
define void @h(ptr %p) {
; CHECK-LABEL: define void @h(
-; CHECK-SAME: ptr initializes((0, 8)) [[P:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+; CHECK-SAME: ptr initializes((0, 8)) [[P:%.*]]) local_unnamed_addr {
; CHECK-NEXT: store i64 3, ptr [[P]], align 4
; CHECK-NEXT: tail call void @j(ptr nonnull [[P]])
; CHECK-NEXT: ret void
@@ -33,7 +33,7 @@ define void @h(ptr %p) {
define void @g(ptr dead_on_unwind noalias writable dereferenceable(8) align 8 %p) minsize {
; CHECK-LABEL: define void @g(
-; CHECK-SAME: ptr dead_on_unwind noalias writable writeonly align 8 captures(none) dereferenceable(8) initializes((0, 8)) [[P:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+; CHECK-SAME: ptr dead_on_unwind noalias writable writeonly align 8 captures(none) dereferenceable(8) initializes((0, 8)) [[P:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: tail call void @h(ptr nonnull [[P]])
; CHECK-NEXT: ret void
;
@@ -45,7 +45,7 @@ define void @g(ptr dead_on_unwind noalias writable dereferenceable(8) align 8 %p
define void @f(ptr dead_on_unwind noalias %p) {
; CHECK-LABEL: define void @f(
-; CHECK-SAME: ptr dead_on_unwind noalias initializes((0, 8)) [[P:%.*]]) local_unnamed_addr #[[ATTR1]] {
+; CHECK-SAME: ptr dead_on_unwind noalias initializes((0, 8)) [[P:%.*]]) local_unnamed_addr {
; CHECK-NEXT: store i64 3, ptr [[P]], align 4
; CHECK-NEXT: tail call void @j(ptr nonnull align 8 dereferenceable(8) [[P]])
; CHECK-NEXT: store i64 43, ptr [[P]], align 4
>From d4fa041e561d158c2e7a23e62b854352dc46cd36 Mon Sep 17 00:00:00 2001
From: Usha Gupta <usha.gupta at arm.com>
Date: Thu, 19 Jun 2025 10:36:09 +0000
Subject: [PATCH 7/9] Apply optimizations based on linkage and function address
only during LTO to avoid incorrect inference pre-LTO
---
llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 36 ++++---
.../norecurse_multiSCC_indirect_recursion.ll | 64 ++++++------
.../norecurse_multiSCC_indirect_recursion1.ll | 69 +++++++------
.../norecurse_self_recursive_callee.ll | 98 +++++++------------
4 files changed, 130 insertions(+), 137 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index de671c48f572e..3d6daafb0e11c 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -2086,18 +2086,16 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
if (Callee->doesNotRecurse())
continue;
- if (Callee->isDeclaration()) {
- if (Callee->hasFnAttribute(Attribute::NoCallback) ||
- NoFunctionsAddressIsTaken)
- continue;
- return;
- } else if (F->hasAddressTaken() || !F->hasLocalLinkage()) {
- // Control reaches here only for callees which are defined in this
- // module and do not satisfy conditions for norecurse attribute.
- // In such a case, if function F has external linkage or address
- // taken, conversatively avoid adding norecurse.
- return;
- }
+ // If there are no functions with external linkage and none of the
+ // functions' address is taken, it ensures that this Callee does not
+ // have any path leading back to the Caller F.
+ // The 'NoFunctionsAddressIsTaken' flag is only set during post-link
+ // LTO phase after examining all available function definitions.
+ if (NoFunctionsAddressIsTaken ||
+ (Callee->isDeclaration() &&
+ Callee->hasFnAttribute(Attribute::NoCallback)))
+ continue;
+ return;
}
}
}
@@ -2340,10 +2338,11 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
}
bool NoFunctionsAddressIsTaken = false;
- // Check if any function in the whole program has its address taken.
+ // Check if any function in the whole program has its address taken or has
+ // potentially external linkage.
// We use this information when inferring norecurse attribute: If there is
- // no function whose address is taken, we conclude that any external function
- // cannot callback into any user function.
+ // no function whose address is taken and all functions have internal
+ // linkage, there is no path for a callback to any user function.
if (IsLTOPostLink) {
bool AnyFunctionsAddressIsTaken = false;
// Get the parent Module of the Function
@@ -2354,6 +2353,12 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
if (F.isDeclaration())
continue;
+ // If the function is already marked as norecurse, this should not block
+ // norecurse inference even though it may have external linkage.
+ // For ex: main() in C++.
+ if (F.doesNotRecurse())
+ continue;
+
if (!F.hasLocalLinkage() || F.hasAddressTaken()) {
AnyFunctionsAddressIsTaken = true;
break; // break if we found one
@@ -2361,6 +2366,7 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
}
NoFunctionsAddressIsTaken = !AnyFunctionsAddressIsTaken;
}
+
auto ChangedFunctions = deriveAttrsInPostOrder(
Functions, AARGetter, ArgAttrsOnly, NoFunctionsAddressIsTaken);
diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll
index 3f616b1bf76f5..d5b7323413eee 100644
--- a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll
@@ -1,7 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5
-; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
-target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
-target triple = "aarch64-unknown-linux-gnu"
+; RUN: opt < %s -passes="lto<O2>" -S | FileCheck %s
; This test includes a call graph with multiple SCCs. The purpose of this is
; to check that norecurse is not added when a function is part of non-singular
@@ -15,8 +13,8 @@ target triple = "aarch64-unknown-linux-gnu"
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @bar1() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @bar1(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+; CHECK-LABEL: define internal fastcc void @bar1(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @main()
; CHECK-NEXT: ret void
@@ -32,9 +30,9 @@ define dso_local noundef i32 @main() local_unnamed_addr #0 {
; CHECK-LABEL: define dso_local noundef i32 @main(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @foo()
-; CHECK-NEXT: tail call void @bar2()
-; CHECK-NEXT: tail call void @baz()
+; CHECK-NEXT: tail call fastcc void @foo()
+; CHECK-NEXT: tail call fastcc void @bar2()
+; CHECK-NEXT: tail call fastcc void @baz()
; CHECK-NEXT: ret i32 0
;
entry:
@@ -47,10 +45,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @foo1() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @foo1(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @foo1(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @bar1()
+; CHECK-NEXT: tail call fastcc void @bar1()
; CHECK-NEXT: ret void
;
entry:
@@ -61,10 +59,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @bar() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @bar(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @bar(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @foo1()
+; CHECK-NEXT: tail call fastcc void @foo1()
; CHECK-NEXT: ret void
;
entry:
@@ -75,10 +73,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @foo() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @foo(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @foo(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @bar()
+; CHECK-NEXT: tail call fastcc void @bar()
; CHECK-NEXT: ret void
;
entry:
@@ -89,10 +87,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @bar4() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @bar4(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @bar4(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @bar2()
+; CHECK-NEXT: tail call fastcc void @bar2()
; CHECK-NEXT: ret void
;
entry:
@@ -103,10 +101,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @bar2() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @bar2(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @bar2(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @bar3()
+; CHECK-NEXT: tail call fastcc void @bar3()
; CHECK-NEXT: ret void
;
entry:
@@ -117,10 +115,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @bar3() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @bar3(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @bar3(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @bar4()
+; CHECK-NEXT: tail call fastcc void @bar4()
; CHECK-NEXT: ret void
;
entry:
@@ -131,10 +129,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @fun() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @fun(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @fun(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @baz()
+; CHECK-NEXT: tail call fastcc void @baz()
; CHECK-NEXT: ret void
;
entry:
@@ -145,10 +143,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @baz() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @baz(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @baz(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @fun()
+; CHECK-NEXT: tail call fastcc void @fun()
; CHECK-NEXT: ret void
;
entry:
@@ -156,4 +154,4 @@ entry:
ret void
}
-attributes #0 = { nofree noinline nosync nounwind memory(none) uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+outline-atomics,+v8a,-fmv" }
+attributes #0 = { nofree noinline nosync nounwind memory(none) uwtable }
diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll
index e35ad4101fecb..d4f37b69bfacf 100644
--- a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll
@@ -1,7 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5
-; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
-target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
-target triple = "aarch64-unknown-linux-gnu"
+; RUN: opt < %s -passes="lto<O2>" -S | FileCheck %s
; This test includes a call graph with multiple SCCs. The purpose of this is
; to check that norecurse is added to a function which calls a function which
@@ -9,19 +7,33 @@ target triple = "aarch64-unknown-linux-gnu"
; There are two SCCs in this test:
; SCC#1: bar2, bar3, bar4
; SCC#2: baz, fun
-; main() calls bar2 and baz, both of which are part of some indirect recursive
-; chain. but does not call back main() and hence main() can be marked as
-; norecurse. But main() does not have internal linkage, hence we avoid adding
-; norecurse for main() as well.
+; f1() calls bar2 and baz, both of which are part of some indirect recursive
+; chain. but does not call back f1() and hence f1() can be marked as
+; norecurse.
-; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-define dso_local noundef i32 @main() local_unnamed_addr #0 {
-; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+; Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable
+define dso_local noundef i32 @main() local_unnamed_addr #1 {
+; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable
; CHECK-LABEL: define dso_local noundef i32 @main(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @bar2()
-; CHECK-NEXT: tail call void @baz()
+; CHECK-NEXT: [[TMP0:%.*]] = tail call fastcc i32 @f1()
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ tail call void @f1()
+ ret i32 0
+}
+
+
+; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
+define internal i32 @f1() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable
+; CHECK-LABEL: define internal fastcc noundef i32 @f1(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: tail call fastcc void @bar2()
+; CHECK-NEXT: tail call fastcc void @baz()
; CHECK-NEXT: ret i32 0
;
entry:
@@ -33,10 +45,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @bar4() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @bar4(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @bar4(
+; CHECK-SAME: ) unnamed_addr #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @bar2()
+; CHECK-NEXT: tail call fastcc void @bar2()
; CHECK-NEXT: ret void
;
entry:
@@ -47,10 +59,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @bar2() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @bar2(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @bar2(
+; CHECK-SAME: ) unnamed_addr #[[ATTR1]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @bar3()
+; CHECK-NEXT: tail call fastcc void @bar3()
; CHECK-NEXT: ret void
;
entry:
@@ -61,10 +73,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @bar3() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @bar3(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @bar3(
+; CHECK-SAME: ) unnamed_addr #[[ATTR1]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @bar4()
+; CHECK-NEXT: tail call fastcc void @bar4()
; CHECK-NEXT: ret void
;
entry:
@@ -75,10 +87,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @fun() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @fun(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @fun(
+; CHECK-SAME: ) unnamed_addr #[[ATTR1]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @baz()
+; CHECK-NEXT: tail call fastcc void @baz()
; CHECK-NEXT: ret void
;
entry:
@@ -89,10 +101,10 @@ entry:
; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
define internal void @baz() local_unnamed_addr #0 {
; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable
-; CHECK-LABEL: define internal void @baz(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] {
+; CHECK-LABEL: define internal fastcc void @baz(
+; CHECK-SAME: ) unnamed_addr #[[ATTR1]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @fun()
+; CHECK-NEXT: tail call fastcc void @fun()
; CHECK-NEXT: ret void
;
entry:
@@ -100,4 +112,5 @@ entry:
ret void
}
-attributes #0 = { nofree noinline nosync nounwind memory(none) uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+outline-atomics,+v8a,-fmv" }
+attributes #0 = { nofree noinline nosync nounwind memory(none) uwtable }
+attributes #1 = { nofree noinline norecurse nosync nounwind memory(none) uwtable }
diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll
index e151e7178026a..c3e897d056441 100644
--- a/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll
@@ -1,7 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5
-; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
-target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
-target triple = "aarch64-unknown-linux-gnu"
+; RUN: opt < %s -passes="lto<O2>" -S | FileCheck %s
; This test includes a call graph with a self recursive function.
; The purpose of this is to check that norecurse is added to functions
@@ -13,37 +11,37 @@ target triple = "aarch64-unknown-linux-gnu"
@x = dso_local global i32 4, align 4
@y = dso_local global i32 2, align 4
-; Function Attrs: nofree noinline norecurse nounwind memory(readwrite, argmem: none) uwtable
-define internal void @callee6() local_unnamed_addr #0 {
+; Function Attrs: nofree noinline nounwind uwtable
+define internal void @callee6() local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind memory(readwrite, argmem: none) uwtable
-; CHECK-LABEL: define internal void @callee6(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+; CHECK-LABEL: define internal fastcc void @callee6(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[TMP0:%.*]] = load volatile i32, ptr @y, align 4, !tbaa [[TBAA8:![0-9]+]]
+; CHECK-NEXT: [[TMP0:%.*]] = load volatile i32, ptr @y, align 4
; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP0]], 1
-; CHECK-NEXT: store volatile i32 [[INC]], ptr @y, align 4, !tbaa [[TBAA8]]
+; CHECK-NEXT: store volatile i32 [[INC]], ptr @y, align 4
; CHECK-NEXT: ret void
;
entry:
- %0 = load volatile i32, ptr @y, align 4, !tbaa !8
+ %0 = load volatile i32, ptr @y, align 4
%inc = add nsw i32 %0, 1
- store volatile i32 %inc, ptr @y, align 4, !tbaa !8
+ store volatile i32 %inc, ptr @y, align 4
ret void
}
; Function Attrs: nofree noinline nounwind uwtable
define internal void @callee5(i32 noundef %x) local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline nounwind uwtable
-; CHECK-LABEL: define internal void @callee5(
-; CHECK-SAME: i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+; CHECK-LABEL: define internal fastcc void @callee5(
+; CHECK-SAME: i32 noundef [[X:%.*]]) unnamed_addr #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], 0
; CHECK-NEXT: br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
; CHECK: [[IF_THEN]]:
-; CHECK-NEXT: tail call void @callee5(i32 noundef [[X]])
+; CHECK-NEXT: tail call fastcc void @callee5(i32 noundef [[X]])
; CHECK-NEXT: br label %[[IF_END]]
; CHECK: [[IF_END]]:
-; CHECK-NEXT: tail call void @callee6()
+; CHECK-NEXT: tail call fastcc void @callee6()
; CHECK-NEXT: ret void
;
entry:
@@ -62,15 +60,15 @@ if.end: ; preds = %if.then, %entry
; Function Attrs: nofree noinline nounwind uwtable
define internal void @callee4() local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
-; CHECK-LABEL: define internal void @callee4(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+; CHECK-LABEL: define internal fastcc void @callee4(
+; CHECK-SAME: ) unnamed_addr #[[ATTR2:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[TMP0:%.*]] = load volatile i32, ptr @x, align 4, !tbaa [[TBAA8]]
-; CHECK-NEXT: tail call void @callee5(i32 noundef [[TMP0]])
+; CHECK-NEXT: [[TMP0:%.*]] = load volatile i32, ptr @x, align 4
+; CHECK-NEXT: tail call fastcc void @callee5(i32 noundef [[TMP0]])
; CHECK-NEXT: ret void
;
entry:
- %0 = load volatile i32, ptr @x, align 4, !tbaa !8
+ %0 = load volatile i32, ptr @x, align 4
tail call void @callee5(i32 noundef %0)
ret void
}
@@ -78,10 +76,10 @@ entry:
; Function Attrs: nofree noinline nounwind uwtable
define internal void @callee3() local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
-; CHECK-LABEL: define internal void @callee3(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
+; CHECK-LABEL: define internal fastcc void @callee3(
+; CHECK-SAME: ) unnamed_addr #[[ATTR2]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @callee4()
+; CHECK-NEXT: tail call fastcc void @callee4()
; CHECK-NEXT: ret void
;
entry:
@@ -92,10 +90,10 @@ entry:
; Function Attrs: nofree noinline nounwind uwtable
define internal void @callee2() local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
-; CHECK-LABEL: define internal void @callee2(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
+; CHECK-LABEL: define internal fastcc void @callee2(
+; CHECK-SAME: ) unnamed_addr #[[ATTR2]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @callee3()
+; CHECK-NEXT: tail call fastcc void @callee3()
; CHECK-NEXT: ret void
;
entry:
@@ -106,10 +104,10 @@ entry:
; Function Attrs: nofree noinline nounwind uwtable
define internal void @callee1() local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
-; CHECK-LABEL: define internal void @callee1(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
+; CHECK-LABEL: define internal fastcc void @callee1(
+; CHECK-SAME: ) unnamed_addr #[[ATTR2]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @callee2()
+; CHECK-NEXT: tail call fastcc void @callee2()
; CHECK-NEXT: ret void
;
entry:
@@ -120,10 +118,10 @@ entry:
; Function Attrs: nofree noinline nounwind uwtable
define internal void @bob() local_unnamed_addr #1 {
; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
-; CHECK-LABEL: define internal void @bob(
-; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] {
+; CHECK-LABEL: define internal fastcc void @bob(
+; CHECK-SAME: ) unnamed_addr #[[ATTR2]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @callee1()
+; CHECK-NEXT: tail call fastcc void @callee1()
; CHECK-NEXT: ret void
;
entry:
@@ -131,13 +129,13 @@ entry:
ret void
}
-; Function Attrs: nofree nounwind uwtable
-define dso_local noundef i32 @main() local_unnamed_addr #2 {
+; Function Attrs: nofree norecurse nounwind uwtable
+define dso_local noundef i32 @main() local_unnamed_addr #3 {
; CHECK: Function Attrs: nofree norecurse nounwind uwtable
; CHECK-LABEL: define dso_local noundef i32 @main(
; CHECK-SAME: ) local_unnamed_addr #[[ATTR3:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: tail call void @bob()
+; CHECK-NEXT: tail call fastcc void @bob()
; CHECK-NEXT: ret i32 0
;
entry:
@@ -145,29 +143,7 @@ entry:
ret i32 0
}
-attributes #0 = { nofree noinline norecurse nounwind memory(readwrite, argmem: none) uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+outline-atomics,+v8a,-fmv" }
-attributes #1 = { nofree noinline nounwind uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+outline-atomics,+v8a,-fmv" }
-attributes #2 = { nofree nounwind uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+outline-atomics,+v8a,-fmv" }
-
-!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6}
-!llvm.ident = !{!7}
-
-!0 = !{i32 1, !"wchar_size", i32 4}
-!1 = !{i32 8, !"PIC Level", i32 2}
-!2 = !{i32 7, !"PIE Level", i32 2}
-!3 = !{i32 7, !"uwtable", i32 2}
-!4 = !{i32 7, !"frame-pointer", i32 1}
-!5 = !{i32 1, !"ThinLTO", i32 0}
-!6 = !{i32 1, !"EnableSplitLTOUnit", i32 1}
-!7 = !{!"clang version 21.0.0git (https://github.com/llvm/llvm-project db42345dc660329e34fd119fc8edab74521f7c06)"}
-!8 = !{!9, !9, i64 0}
-!9 = !{!"int", !10, i64 0}
-!10 = !{!"omnipotent char", !11, i64 0}
-!11 = !{!"Simple C/C++ TBAA"}
-
-;.
-; CHECK: [[TBAA8]] = !{[[META9:![0-9]+]], [[META9]], i64 0}
-; CHECK: [[META9]] = !{!"int", [[META10:![0-9]+]], i64 0}
-; CHECK: [[META10]] = !{!"omnipotent char", [[META11:![0-9]+]], i64 0}
-; CHECK: [[META11]] = !{!"Simple C/C++ TBAA"}
-;.
+attributes #0 = { nofree noinline norecurse nounwind memory(readwrite, argmem: none) uwtable }
+attributes #1 = { nofree noinline nounwind uwtable }
+attributes #2 = { nofree nounwind uwtable }
+attributes #3 = { nofree norecurse nounwind uwtable }
>From cfe4a3916eaf92fd04756ffeb25c581e5bd0c14a Mon Sep 17 00:00:00 2001
From: Usha Gupta <usha.gupta at arm.com>
Date: Thu, 19 Jun 2025 13:31:40 +0000
Subject: [PATCH 8/9] Revert changes in unaffected tests
---
llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 1 +
.../AMDGPU/amdgpu-simplify-libcall-sincos.ll | 8 +-
.../test/Transforms/FunctionAttrs/noreturn.ll | 8 +-
.../test/Transforms/FunctionAttrs/nounwind.ll | 113 +++++++-----------
4 files changed, 50 insertions(+), 80 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 3d6daafb0e11c..871470743255e 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -2063,6 +2063,7 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes,
SmallSet<Function *, 8> &Changed,
bool NoFunctionsAddressIsTaken) {
// Try and identify functions that do not recurse.
+
// If the SCC contains multiple nodes we know for sure there is recursion.
if (SCCNodes.size() != 1)
return;
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll
index 6e2b825a45f56..34777eff0e856 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll
@@ -725,7 +725,7 @@ entry:
define void @sincos_f32_value_is_different_constexpr(ptr addrspace(1) nocapture writeonly %sin_out, ptr addrspace(1) nocapture writeonly %cos_out) {
; CHECK-LABEL: define void @sincos_f32_value_is_different_constexpr
-; CHECK-SAME: (ptr addrspace(1) writeonly captures(none) initializes((0, 4)) [[SIN_OUT:%.*]], ptr addrspace(1) writeonly captures(none) initializes((0, 4)) [[COS_OUT:%.*]]) #[[ATTR6:[0-9]+]] {
+; CHECK-SAME: (ptr addrspace(1) writeonly captures(none) initializes((0, 4)) [[SIN_OUT:%.*]], ptr addrspace(1) writeonly captures(none) initializes((0, 4)) [[COS_OUT:%.*]]) #[[ATTR2]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CALL:%.*]] = tail call contract float @_Z3sinf(float bitcast (i32 ptrtoint (ptr @func to i32) to float))
; CHECK-NEXT: store float [[CALL]], ptr addrspace(1) [[SIN_OUT]], align 4
@@ -881,7 +881,7 @@ entry:
define float @sincos_f32_unused_result_cos(float %x) {
; CHECK-LABEL: define float @sincos_f32_unused_result_cos
-; CHECK-SAME: (float [[X:%.*]]) local_unnamed_addr #[[ATTR7:[0-9]+]] {
+; CHECK-SAME: (float [[X:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SIN:%.*]] = tail call contract float @_Z3sinf(float [[X]])
; CHECK-NEXT: ret float [[SIN]]
@@ -896,7 +896,7 @@ entry:
define float @sincos_f32_unused_result_sin(float %x) {
; CHECK-LABEL: define float @sincos_f32_unused_result_sin
-; CHECK-SAME: (float [[X:%.*]]) local_unnamed_addr #[[ATTR7]] {
+; CHECK-SAME: (float [[X:%.*]]) local_unnamed_addr #[[ATTR6]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[COS:%.*]] = tail call contract float @_Z3cosf(float [[X]])
; CHECK-NEXT: ret float [[COS]]
@@ -911,7 +911,7 @@ entry:
define void @sincos_f32_repeated_uses(float %x, ptr addrspace(1) %sin_out, ptr addrspace(1) %cos_out) {
; CHECK-LABEL: define void @sincos_f32_repeated_uses
-; CHECK-SAME: (float [[X:%.*]], ptr addrspace(1) [[SIN_OUT:%.*]], ptr addrspace(1) [[COS_OUT:%.*]]) local_unnamed_addr #[[ATTR8:[0-9]+]] {
+; CHECK-SAME: (float [[X:%.*]], ptr addrspace(1) [[SIN_OUT:%.*]], ptr addrspace(1) [[COS_OUT:%.*]]) local_unnamed_addr #[[ATTR7:[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[__SINCOS_:%.*]] = alloca float, align 4, addrspace(5)
; CHECK-NEXT: [[TMP0:%.*]] = call contract float @_Z6sincosfPU3AS5f(float [[X]], ptr addrspace(5) [[__SINCOS_]])
diff --git a/llvm/test/Transforms/FunctionAttrs/noreturn.ll b/llvm/test/Transforms/FunctionAttrs/noreturn.ll
index 965467ba6015f..fa80f6c2eced4 100644
--- a/llvm/test/Transforms/FunctionAttrs/noreturn.ll
+++ b/llvm/test/Transforms/FunctionAttrs/noreturn.ll
@@ -2,25 +2,25 @@
declare i32 @f()
-; CHECK: Function Attrs: {{.*}}noreturn
+; CHECK: Function Attrs: noreturn
; CHECK-NEXT: @noreturn()
declare i32 @noreturn() noreturn
-; CHECK: Function Attrs: {{.*}}noreturn
+; CHECK: Function Attrs: noreturn
; CHECK-NEXT: @caller()
define i32 @caller() {
%c = call i32 @noreturn()
ret i32 %c
}
-; CHECK: Function Attrs: {{.*}}noreturn
+; CHECK: Function Attrs: noreturn
; CHECK-NEXT: @caller2()
define i32 @caller2() {
%c = call i32 @caller()
ret i32 %c
}
-; CHECK: Function Attrs: {{.*}}noreturn
+; CHECK: Function Attrs: noreturn
; CHECK-NEXT: @caller3()
define i32 @caller3() {
entry:
diff --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll
index ecba7a0318cd2..afa9ae31b5fba 100644
--- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nounwind.ll
@@ -4,15 +4,10 @@
; TEST 1
define i32 @foo1() {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; FNATTRS-LABEL: define {{[^@]+}}@foo1
-; FNATTRS-SAME: () #[[ATTR0:[0-9]+]] {
-; FNATTRS-NEXT: ret i32 1
-;
-; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@foo1
-; ATTRIBUTOR-SAME: () #[[ATTR0:[0-9]+]] {
-; ATTRIBUTOR-NEXT: ret i32 1
+; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; COMMON-LABEL: define {{[^@]+}}@foo1
+; COMMON-SAME: () #[[ATTR0:[0-9]+]] {
+; COMMON-NEXT: ret i32 1
;
ret i32 1
}
@@ -75,23 +70,14 @@ define void @call_non_nounwind(){
; }
define i32 @maybe_throw(i1 zeroext %0) {
-; FNATTRS-LABEL: define {{[^@]+}}@maybe_throw
-; FNATTRS-SAME: (i1 zeroext [[TMP0:%.*]]) {
-; FNATTRS-NEXT: br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; FNATTRS: 2:
-; FNATTRS-NEXT: tail call void @__cxa_rethrow()
-; FNATTRS-NEXT: unreachable
-; FNATTRS: 3:
-; FNATTRS-NEXT: ret i32 -1
-;
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@maybe_throw
-; ATTRIBUTOR-SAME: (i1 zeroext [[TMP0:%.*]]) {
-; ATTRIBUTOR-NEXT: br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; ATTRIBUTOR: 2:
-; ATTRIBUTOR-NEXT: tail call void @__cxa_rethrow()
-; ATTRIBUTOR-NEXT: unreachable
-; ATTRIBUTOR: 3:
-; ATTRIBUTOR-NEXT: ret i32 -1
+; COMMON-LABEL: define {{[^@]+}}@maybe_throw
+; COMMON-SAME: (i1 zeroext [[TMP0:%.*]]) {
+; COMMON-NEXT: br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; COMMON: 2:
+; COMMON-NEXT: tail call void @__cxa_rethrow()
+; COMMON-NEXT: unreachable
+; COMMON: 3:
+; COMMON-NEXT: ret i32 -1
;
br i1 %0, label %2, label %3
@@ -115,31 +101,18 @@ declare void @__cxa_rethrow()
; }
define i32 @catch_thing() personality ptr @__gxx_personality_v0 {
-; FNATTRS-LABEL: define {{[^@]+}}@catch_thing() personality ptr @__gxx_personality_v0 {
-; FNATTRS-NEXT: invoke void @__cxa_rethrow()
-; FNATTRS-NEXT: to label [[TMP1:%.*]] unwind label [[TMP2:%.*]]
-; FNATTRS: 1:
-; FNATTRS-NEXT: unreachable
-; FNATTRS: 2:
-; FNATTRS-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
-; FNATTRS-NEXT: catch ptr null
-; FNATTRS-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
-; FNATTRS-NEXT: [[TMP5:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP4]])
-; FNATTRS-NEXT: tail call void @__cxa_end_catch()
-; FNATTRS-NEXT: ret i32 -1
-;
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@catch_thing() personality ptr @__gxx_personality_v0 {
-; ATTRIBUTOR-NEXT: invoke void @__cxa_rethrow()
-; ATTRIBUTOR-NEXT: to label [[TMP1:%.*]] unwind label [[TMP2:%.*]]
-; ATTRIBUTOR: 1:
-; ATTRIBUTOR-NEXT: unreachable
-; ATTRIBUTOR: 2:
-; ATTRIBUTOR-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
-; ATTRIBUTOR-NEXT: catch ptr null
-; ATTRIBUTOR-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
-; ATTRIBUTOR-NEXT: [[TMP5:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP4]])
-; ATTRIBUTOR-NEXT: tail call void @__cxa_end_catch()
-; ATTRIBUTOR-NEXT: ret i32 -1
+; COMMON-LABEL: define {{[^@]+}}@catch_thing() personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT: invoke void @__cxa_rethrow()
+; COMMON-NEXT: to label [[TMP1:%.*]] unwind label [[TMP2:%.*]]
+; COMMON: 1:
+; COMMON-NEXT: unreachable
+; COMMON: 2:
+; COMMON-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT: catch ptr null
+; COMMON-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+; COMMON-NEXT: [[TMP5:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP4]])
+; COMMON-NEXT: tail call void @__cxa_end_catch()
+; COMMON-NEXT: ret i32 -1
;
invoke void @__cxa_rethrow() #1
to label %1 unwind label %2
@@ -157,13 +130,9 @@ define i32 @catch_thing() personality ptr @__gxx_personality_v0 {
}
define i32 @catch_thing_user() {
-; FNATTRS-LABEL: define {{[^@]+}}@catch_thing_user() {
-; FNATTRS-NEXT: [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing()
-; FNATTRS-NEXT: ret i32 [[CATCH_THING_CALL]]
-;
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@catch_thing_user() {
-; ATTRIBUTOR-NEXT: [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing()
-; ATTRIBUTOR-NEXT: ret i32 [[CATCH_THING_CALL]]
+; COMMON-LABEL: define {{[^@]+}}@catch_thing_user() {
+; COMMON-NEXT: [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing()
+; COMMON-NEXT: ret i32 [[CATCH_THING_CALL]]
;
%catch_thing_call = call i32 @catch_thing()
ret i32 %catch_thing_call
@@ -178,10 +147,10 @@ define void @catch_specific_landingpad() personality ptr @__gxx_personality_v0 {
; COMMON-LABEL: define {{[^@]+}}@catch_specific_landingpad
; COMMON-SAME: () #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 {
; COMMON-NEXT: invoke void @do_throw()
-; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
; COMMON: lpad:
; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; COMMON-NEXT: catch ptr @catch_ty
+; COMMON-NEXT: catch ptr @catch_ty
; COMMON-NEXT: call void @abort()
; COMMON-NEXT: unreachable
; COMMON: unreachable:
@@ -205,10 +174,10 @@ define void @catch_all_landingpad() personality ptr @__gxx_personality_v0 {
; COMMON-LABEL: define {{[^@]+}}@catch_all_landingpad
; COMMON-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
; COMMON-NEXT: invoke void @do_throw()
-; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
; COMMON: lpad:
; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; COMMON-NEXT: catch ptr null
+; COMMON-NEXT: catch ptr null
; COMMON-NEXT: call void @abort()
; COMMON-NEXT: unreachable
; COMMON: unreachable:
@@ -232,10 +201,10 @@ define void @filter_specific_landingpad() personality ptr @__gxx_personality_v0
; COMMON-LABEL: define {{[^@]+}}@filter_specific_landingpad
; COMMON-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
; COMMON-NEXT: invoke void @do_throw()
-; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
; COMMON: lpad:
; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; COMMON-NEXT: filter [1 x ptr] [ptr @catch_ty]
+; COMMON-NEXT: filter [1 x ptr] [ptr @catch_ty]
; COMMON-NEXT: call void @abort()
; COMMON-NEXT: unreachable
; COMMON: unreachable:
@@ -259,10 +228,10 @@ define void @filter_none_landingpad() personality ptr @__gxx_personality_v0 {
; COMMON-LABEL: define {{[^@]+}}@filter_none_landingpad
; COMMON-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
; COMMON-NEXT: invoke void @do_throw()
-; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
; COMMON: lpad:
; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; COMMON-NEXT: filter [0 x ptr] zeroinitializer
+; COMMON-NEXT: filter [0 x ptr] zeroinitializer
; COMMON-NEXT: call void @abort()
; COMMON-NEXT: unreachable
; COMMON: unreachable:
@@ -286,10 +255,10 @@ define void @cleanup_landingpad() personality ptr @__gxx_personality_v0 {
; COMMON-LABEL: define {{[^@]+}}@cleanup_landingpad
; COMMON-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
; COMMON-NEXT: invoke void @do_throw()
-; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
; COMMON: lpad:
; COMMON-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
-; COMMON-NEXT: cleanup
+; COMMON-NEXT: cleanup
; COMMON-NEXT: call void @abort()
; COMMON-NEXT: unreachable
; COMMON: unreachable:
@@ -313,7 +282,7 @@ define void @cleanuppad() personality ptr @__gxx_personality_v0 {
; FNATTRS-LABEL: define {{[^@]+}}@cleanuppad
; FNATTRS-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
; FNATTRS-NEXT: invoke void @do_throw()
-; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
+; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
; FNATTRS: cpad:
; FNATTRS-NEXT: [[CP:%.*]] = cleanuppad within none []
; FNATTRS-NEXT: call void @abort()
@@ -325,7 +294,7 @@ define void @cleanuppad() personality ptr @__gxx_personality_v0 {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@cleanuppad
; ATTRIBUTOR-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
; ATTRIBUTOR-NEXT: invoke void @do_throw()
-; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
+; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
; ATTRIBUTOR: cpad:
; ATTRIBUTOR-NEXT: [[CP:%.*]] = cleanuppad within none []
; ATTRIBUTOR-NEXT: call void @abort()
@@ -350,7 +319,7 @@ define void @catchswitch_cleanuppad() personality ptr @__gxx_personality_v0 {
; FNATTRS-LABEL: define {{[^@]+}}@catchswitch_cleanuppad
; FNATTRS-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
; FNATTRS-NEXT: invoke void @do_throw()
-; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
+; FNATTRS-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
; FNATTRS: cs:
; FNATTRS-NEXT: [[TOK:%.*]] = catchswitch within none [label %catch] unwind label [[CPAD:%.*]]
; FNATTRS: catch:
@@ -368,7 +337,7 @@ define void @catchswitch_cleanuppad() personality ptr @__gxx_personality_v0 {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@catchswitch_cleanuppad
; ATTRIBUTOR-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
; ATTRIBUTOR-NEXT: invoke void @do_throw()
-; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
+; ATTRIBUTOR-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
; ATTRIBUTOR: cs:
; ATTRIBUTOR-NEXT: [[TOK:%.*]] = catchswitch within none [label %catch] unwind label [[CPAD:%.*]]
; ATTRIBUTOR: catch:
>From e89c42f844a47c1c05789fff1ab221cace680585 Mon Sep 17 00:00:00 2001
From: Usha Gupta <usha.gupta at arm.com>
Date: Fri, 20 Jun 2025 11:46:48 +0000
Subject: [PATCH 9/9] Additional test with a call to library function without
NoCallback
---
.../Transforms/FunctionAttrs/norecurse_lto.ll | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 llvm/test/Transforms/FunctionAttrs/norecurse_lto.ll
diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_lto.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_lto.ll
new file mode 100644
index 0000000000000..d29dfe0feb94e
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse_lto.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5
+; RUN: opt < %s -passes="lto<O2>" -S | FileCheck %s
+
+ at .str = private unnamed_addr constant [7 x i8] c"Hello \00", align 1
+
+; Function Attrs: nofree noinline nounwind uwtable vscale_range(1,16)
+define internal void @bob() local_unnamed_addr #0 {
+; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable
+; CHECK-LABEL: define internal fastcc void @bob(
+; CHECK-SAME: ) unnamed_addr #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[TMP1:%.*]] = tail call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str)
+; CHECK-NEXT: ret void
+;
+ %1 = tail call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str)
+ ret void
+}
+
+; Function Attrs: nofree nounwind
+declare noundef i32 @printf(ptr nocapture noundef readonly, ...) local_unnamed_addr #1
+
+; Function Attrs: nofree norecurse nounwind uwtable vscale_range(1,16)
+define dso_local noundef i32 @main() local_unnamed_addr #2 {
+; CHECK: Function Attrs: nofree norecurse nounwind uwtable
+; CHECK-LABEL: define dso_local noundef i32 @main(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: tail call fastcc void @bob()
+; CHECK-NEXT: ret i32 0
+;
+ tail call void @bob()
+ ret i32 0
+}
+
+
+attributes #0 = { nofree noinline nounwind uwtable }
+attributes #1 = { nofree nounwind }
+attributes #2 = { nofree norecurse nounwind uwtable }
More information about the llvm-commits
mailing list