From d14882595e5312548bbebfc50bd85a1392ca43eb Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Wed, 14 May 2025 16:22:34 +0000 Subject: [PATCH 01/12] [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 | 95 ++++++++-- .../norecurse_multiSCC_indirect_recursion.ll | 159 ++++++++++++++++ .../norecurse_multiSCC_indirect_recursion1.ll | 102 +++++++++++ .../norecurse_self_recursive_callee.ll | 173 ++++++++++++++++++ 4 files changed, 509 insertions(+), 20 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 8d9a0e7eaef63..35d30be88cb33 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -2067,8 +2067,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 Worklist; + DenseSet 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(&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, - SmallPtrSet &Changed) { + SmallPtrSet &Changed, + LazyCallGraph &CG) { // Try and identify functions that do not recurse. // If the SCC contains multiple nodes we know for sure there is recursion. @@ -2079,24 +2120,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(&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); @@ -2240,7 +2292,7 @@ static SCCNodesResult createSCCNodeSet(ArrayRef Functions) { template static SmallPtrSet deriveAttrsInPostOrder(ArrayRef Functions, AARGetterT &&AARGetter, - bool ArgAttrsOnly) { + bool ArgAttrsOnly, LazyCallGraph &CG) { SCCNodesResult Nodes = createSCCNodeSet(Functions); // Bail if the SCC only contains optnone functions. @@ -2264,10 +2316,13 @@ deriveAttrsInPostOrder(ArrayRef Functions, AARGetterT &&AARGetter, addColdAttrs(Nodes.SCCNodes, Changed); addWillReturn(Nodes.SCCNodes, Changed); addNoUndefAttrs(Nodes.SCCNodes, Changed); - addNoAliasAttrs(Nodes.SCCNodes, Changed); - addNonNullAttrs(Nodes.SCCNodes, Changed); - inferAttrsFromFunctionBodies(Nodes.SCCNodes, Changed); - addNoRecurseAttrs(Nodes.SCCNodes, Changed); + + // If we have no external nodes participating in the SCC, we can deduce some + // more precise attributes as well. + addNoAliasAttrs(Nodes.SCCNodes, Changed); + addNonNullAttrs(Nodes.SCCNodes, Changed); + inferAttrsFromFunctionBodies(Nodes.SCCNodes, Changed); + addNoRecurseAttrs(Nodes.SCCNodes, Changed, CG); // Finally, infer the maximal set of attributes from the ones we've inferred // above. This is handling the cases where one attribute on a signature @@ -2310,7 +2365,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. + +@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 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 41c6eaa621e67a9c67a9c7bb142f81ef22b206ca Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Tue, 20 May 2025 16:26:07 +0000 Subject: [PATCH 02/12] 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 35d30be88cb33..aedbe4ede3d91 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -2132,6 +2132,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 b51fa88d8810486673f9378b86c875e28458c262 Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Thu, 5 Jun 2025 17:13:38 +0000 Subject: [PATCH 03/12] 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 | 193 ++++++++++++------ .../FunctionAttrs/sendmsg-nocallback.ll | 9 +- llvm/test/Transforms/Inline/cgscc-update.ll | 20 +- llvm/test/Transforms/PhaseOrdering/pr95152.ll | 6 +- 10 files changed, 186 insertions(+), 159 deletions(-) diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index aedbe4ede3d91..8fbeab1e283d6 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -2067,49 +2067,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 Worklist; - DenseSet 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(&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, - SmallPtrSet &Changed, - LazyCallGraph &CG) { + SmallPtrSet &Changed) { // Try and identify functions that do not recurse. // If the SCC contains multiple nodes we know for sure there is recursion. @@ -2120,6 +2079,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 @@ -2141,10 +2102,6 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes, continue; return; } - - if (auto *CNode = CG.lookup(*Callee)) - if (callsUnknownExternal(CNode)) - return; } } } @@ -2295,7 +2252,7 @@ static SCCNodesResult createSCCNodeSet(ArrayRef Functions) { template static SmallPtrSet deriveAttrsInPostOrder(ArrayRef Functions, AARGetterT &&AARGetter, - bool ArgAttrsOnly, LazyCallGraph &CG) { + bool ArgAttrsOnly) { SCCNodesResult Nodes = createSCCNodeSet(Functions); // Bail if the SCC only contains optnone functions. @@ -2325,7 +2282,7 @@ deriveAttrsInPostOrder(ArrayRef 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 // above. This is handling the cases where one attribute on a signature @@ -2368,7 +2325,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 8df242fb023af..5c1b53fcaed30 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 076a7df2781ce..c14cfe568fb1e 100644 --- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll +++ b/llvm/test/Transforms/FunctionAttrs/nounwind.ll @@ -157,7 +157,9 @@ define i32 @catch_thing() personality ptr @__gxx_personality_v0 { } define i32 @catch_thing_user() { -; FNATTRS-LABEL: define {{[^@]+}}@catch_thing_user() { +; 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]] ; @@ -174,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 @@ -201,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 @@ -228,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 @@ -255,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 @@ -282,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 @@ -311,7 +378,7 @@ 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: cpad: @@ -348,7 +415,7 @@ 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: cs: diff --git a/llvm/test/Transforms/FunctionAttrs/sendmsg-nocallback.ll b/llvm/test/Transforms/FunctionAttrs/sendmsg-nocallback.ll index 04575e4cc940b..36a80ce182136 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]] ; @@ -74,8 +74,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 d4f1637ee46d222a54c9627390a031c678415d9f Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Wed, 11 Jun 2025 16:19:02 +0000 Subject: [PATCH 04/12] 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(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 { 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 98821bb1408a7..5dc7f6b823365 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -970,7 +970,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. @@ -992,7 +993,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))); @@ -2076,8 +2078,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 8fbeab1e283d6..b18ed6b47c9d9 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -2068,7 +2068,9 @@ static void inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes, } static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes, - SmallPtrSet &Changed) { + SmallPtrSet &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. @@ -2081,6 +2083,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 @@ -2096,11 +2102,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; } } } @@ -2252,7 +2268,8 @@ static SCCNodesResult createSCCNodeSet(ArrayRef Functions) { template static SmallPtrSet deriveAttrsInPostOrder(ArrayRef 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. @@ -2282,7 +2299,8 @@ deriveAttrsInPostOrder(ArrayRef 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 // above. This is handling the cases where one attribute on a signature @@ -2324,8 +2342,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 8d1a31cf2812f12d709543ad4ba1b26a567965d6 Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Fri, 13 Jun 2025 09:53:34 +0000 Subject: [PATCH 05/12] 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 b18ed6b47c9d9..cea96e53457d4 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -2069,8 +2069,7 @@ static void inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes, static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes, SmallPtrSet &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. @@ -2081,9 +2080,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); @@ -2094,6 +2090,9 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes, for (auto &BB : *F) { for (auto &I : BB.instructionsWithoutDebug()) { if (auto *CB = dyn_cast(&I)) { + if (F->hasAddressTaken()) + return; + Function *Callee = CB->getCalledFunction(); if (!Callee || Callee == F) @@ -2104,19 +2103,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; } } } @@ -2268,8 +2259,8 @@ static SCCNodesResult createSCCNodeSet(ArrayRef Functions) { template static SmallPtrSet deriveAttrsInPostOrder(ArrayRef 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. @@ -2299,8 +2290,7 @@ deriveAttrsInPostOrder(ArrayRef 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 // above. This is handling the cases where one attribute on a signature @@ -2342,12 +2332,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) { @@ -2356,15 +2347,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 0a422d96a08a67d850a6524222fe17f340b3ba2c Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Mon, 16 Jun 2025 11:24:07 +0000 Subject: [PATCH 06/12] 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 cea96e53457d4..2d4349b58c70a 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -2071,7 +2071,6 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes, SmallPtrSet &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; @@ -2080,9 +2079,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 @@ -2090,9 +2086,6 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes, for (auto &BB : *F) { for (auto &I : BB.instructionsWithoutDebug()) { if (auto *CB = dyn_cast(&I)) { - if (F->hasAddressTaken()) - return; - Function *Callee = CB->getCalledFunction(); if (!Callee || Callee == F) @@ -2101,21 +2094,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 5c1b53fcaed30..8df242fb023af 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 c14cfe568fb1e..076a7df2781ce 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 36a80ce182136..04575e4cc940b 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]] ; @@ -74,9 +74,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 53d477b38b873208ac81ae0f6b2246896d212bf4 Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Thu, 19 Jun 2025 10:36:09 +0000 Subject: [PATCH 07/12] 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 2d4349b58c70a..d18210a7937b4 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -2094,18 +2094,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; } } } @@ -2330,10 +2328,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 @@ -2344,6 +2343,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 @@ -2351,6 +2356,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" -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" -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" -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 6e0bbc2c5dd3416450e01f464c073ceaa15acd6a Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Thu, 19 Jun 2025 13:31:40 +0000 Subject: [PATCH 08/12] 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 d18210a7937b4..e372a52acb5c2 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -2071,6 +2071,7 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes, SmallPtrSet &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 076a7df2781ce..f774fb70c8f2a 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 74225b68e8e0817e3c071c6fd45496dba3fbe6c2 Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Fri, 20 Jun 2025 11:46:48 +0000 Subject: [PATCH 09/12] 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" -S | FileCheck %s + +@.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 } From 4e952ede28495d6c74a0033ba10eec24af1f7ca8 Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Mon, 23 Jun 2025 12:05:23 +0000 Subject: [PATCH 10/12] Added test option to avoid using full lto pipeline, updated tests accordingly --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 7 +- .../norecurse_libfunc_NoAddressTaken.ll | 43 +++++++ .../norecurse_libfunc_addressTaken.ll | 43 +++++++ .../Transforms/FunctionAttrs/norecurse_lto.ll | 36 ------ .../norecurse_multiSCC_indirect_recursion.ll | 107 ++++++++++-------- .../norecurse_multiSCC_indirect_recursion1.ll | 63 +++++------ .../norecurse_self_recursive_callee.ll | 46 ++++---- 7 files changed, 207 insertions(+), 138 deletions(-) create mode 100644 llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_NoAddressTaken.ll create mode 100644 llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_addressTaken.ll delete mode 100644 llvm/test/Transforms/FunctionAttrs/norecurse_lto.ll diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index e372a52acb5c2..26a179466e479 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -109,6 +109,11 @@ static cl::opt DisableThinLTOPropagation( "disable-thinlto-funcattrs", cl::init(true), cl::Hidden, cl::desc("Don't propagate function-attrs in thinLTO")); +static cl::opt ForceLTOFuncAttrs( + "force-lto-funcattrs", cl::init(false), cl::Hidden, + cl::desc("Force LTO behaviour for function-attrs pass. Intended for testing" + "purposes only")); + static void addCapturesStat(CaptureInfo CI) { if (capturesNothing(CI)) ++NumCapturesNone; @@ -2334,7 +2339,7 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C, // We use this information when inferring norecurse attribute: If there is // 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) { + if (IsLTOPostLink || ForceLTOFuncAttrs) { bool AnyFunctionsAddressIsTaken = false; // Get the parent Module of the Function Module &M = *C.begin()->getFunction().getParent(); diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_NoAddressTaken.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_NoAddressTaken.ll new file mode 100644 index 0000000000000..6111f9ae777f6 --- /dev/null +++ b/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_NoAddressTaken.ll @@ -0,0 +1,43 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5 +; RUN: opt < %s -passes=function-attrs -force-lto-funcattrs -S | FileCheck %s + +; This test includes a call to a library function which is not marked as +; NoCallback. All functions except main() are internal and main is marked +; norecurse, so as to not block norecurse to be added to bob(). + +@.str = private unnamed_addr constant [12 x i8] c"Hello World\00", align 1 + +; Function Attrs: nofree noinline nounwind uwtable +define internal void @bob() #0 { +; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable +; CHECK-LABEL: define internal void @bob( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str) +; CHECK-NEXT: ret void +; +entry: + %call = tail call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str) + ret void +} + +; Function Attrs: nofree nounwind +declare noundef i32 @printf(ptr noundef readonly captures(none), ...) local_unnamed_addr #1 + +; Function Attrs: nofree norecurse nounwind uwtable +define dso_local noundef i32 @main() #2 { +; CHECK: Function Attrs: nofree norecurse nounwind uwtable +; CHECK-LABEL: define dso_local noundef i32 @main( +; CHECK-SAME: ) #[[ATTR2:[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 nounwind uwtable } +attributes #1 = { nofree nounwind } +attributes #2 = { nofree norecurse nounwind uwtable } diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_addressTaken.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_addressTaken.ll new file mode 100644 index 0000000000000..bd4b838b6d2b2 --- /dev/null +++ b/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_addressTaken.ll @@ -0,0 +1,43 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5 +; RUN: opt < %s -passes=function-attrs -force-lto-funcattrs -S | FileCheck %s + +; This test includes a call to a library function which is not marked as +; NoCallback. Function bob() does not have internal linkage and hence prevents +; norecurse to be added. + +@.str = private unnamed_addr constant [12 x i8] c"Hello World\00", align 1 + +; Function Attrs: nofree noinline nounwind uwtable +define dso_local void @bob() #0 { +; CHECK: Function Attrs: nofree noinline nounwind uwtable +; CHECK-LABEL: define dso_local void @bob( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str) +; CHECK-NEXT: ret void +; +entry: + %call = tail call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str) + ret void +} + +; Function Attrs: nofree nounwind +declare noundef i32 @printf(ptr noundef readonly captures(none), ...) local_unnamed_addr #1 + +; Function Attrs: nofree norecurse nounwind uwtable +define dso_local noundef i32 @main() #2 { +; CHECK: Function Attrs: nofree norecurse nounwind uwtable +; CHECK-LABEL: define dso_local noundef i32 @main( +; CHECK-SAME: ) #[[ATTR2:[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 nounwind uwtable } +attributes #1 = { nofree nounwind } +attributes #2 = { nofree norecurse nounwind uwtable } diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_lto.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_lto.ll deleted file mode 100644 index d29dfe0feb94e..0000000000000 --- a/llvm/test/Transforms/FunctionAttrs/norecurse_lto.ll +++ /dev/null @@ -1,36 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5 -; RUN: opt < %s -passes="lto" -S | FileCheck %s - -@.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 } diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll index d5b7323413eee..ed95e8a590e44 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll @@ -1,54 +1,68 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5 -; RUN: opt < %s -passes="lto" -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -force-lto-funcattrs -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 ; SCC. ; There are three different SCCs in this test: -; SCC#1: main, foo, bar, foo1, bar1 +; SCC#1: f1, 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 internal void @bar1() local_unnamed_addr #0 { +define internal void @bar1() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @bar1( -; CHECK-SAME: ) unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-LABEL: define internal void @bar1( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @main() +; CHECK-NEXT: tail call void @f1() ; CHECK-NEXT: ret void ; entry: - %call = tail call i32 @main() + tail call void @f1() ret void } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define dso_local noundef i32 @main() local_unnamed_addr #0 { +define internal void @f1() #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-LABEL: define internal void @f1( +; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; 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 +; CHECK-NEXT: tail call void @foo() +; CHECK-NEXT: tail call void @bar2() +; CHECK-NEXT: tail call void @baz() +; CHECK-NEXT: ret void ; entry: tail call void @foo() tail call void @bar2() tail call void @baz() + ret void +} + +; Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable +define dso_local noundef i32 @main() #1 { +; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable +; CHECK-LABEL: define dso_local noundef i32 @main( +; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: tail call void @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 void @foo1() local_unnamed_addr #0 { +define internal void @foo1() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @foo1( -; CHECK-SAME: ) unnamed_addr #[[ATTR0]] { +; CHECK-LABEL: define internal void @foo1( +; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @bar1() +; CHECK-NEXT: tail call void @bar1() ; CHECK-NEXT: ret void ; entry: @@ -57,12 +71,12 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar() local_unnamed_addr #0 { +define internal void @bar() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @bar( -; CHECK-SAME: ) unnamed_addr #[[ATTR0]] { +; CHECK-LABEL: define internal void @bar( +; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @foo1() +; CHECK-NEXT: tail call void @foo1() ; CHECK-NEXT: ret void ; entry: @@ -71,12 +85,12 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @foo() local_unnamed_addr #0 { +define internal void @foo() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @foo( -; CHECK-SAME: ) unnamed_addr #[[ATTR0]] { +; CHECK-LABEL: define internal void @foo( +; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @bar() +; CHECK-NEXT: tail call void @bar() ; CHECK-NEXT: ret void ; entry: @@ -85,12 +99,12 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar4() local_unnamed_addr #0 { +define internal void @bar4() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @bar4( -; CHECK-SAME: ) unnamed_addr #[[ATTR0]] { +; CHECK-LABEL: define internal void @bar4( +; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @bar2() +; CHECK-NEXT: tail call void @bar2() ; CHECK-NEXT: ret void ; entry: @@ -99,12 +113,12 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar2() local_unnamed_addr #0 { +define internal void @bar2() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @bar2( -; CHECK-SAME: ) unnamed_addr #[[ATTR0]] { +; CHECK-LABEL: define internal void @bar2( +; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @bar3() +; CHECK-NEXT: tail call void @bar3() ; CHECK-NEXT: ret void ; entry: @@ -113,12 +127,12 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar3() local_unnamed_addr #0 { +define internal void @bar3() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @bar3( -; CHECK-SAME: ) unnamed_addr #[[ATTR0]] { +; CHECK-LABEL: define internal void @bar3( +; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @bar4() +; CHECK-NEXT: tail call void @bar4() ; CHECK-NEXT: ret void ; entry: @@ -127,12 +141,12 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @fun() local_unnamed_addr #0 { +define internal void @fun() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @fun( -; CHECK-SAME: ) unnamed_addr #[[ATTR0]] { +; CHECK-LABEL: define internal void @fun( +; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @baz() +; CHECK-NEXT: tail call void @baz() ; CHECK-NEXT: ret void ; entry: @@ -141,12 +155,12 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @baz() local_unnamed_addr #0 { +define internal void @baz() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @baz( -; CHECK-SAME: ) unnamed_addr #[[ATTR0]] { +; CHECK-LABEL: define internal void @baz( +; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @fun() +; CHECK-NEXT: tail call void @fun() ; CHECK-NEXT: ret void ; entry: @@ -155,3 +169,4 @@ entry: } 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_multiSCC_indirect_recursion1.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll index d4f37b69bfacf..656ab669c5fde 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5 -; RUN: opt < %s -passes="lto" -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -force-lto-funcattrs -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 @@ -12,12 +12,12 @@ ; norecurse. ; Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable -define dso_local noundef i32 @main() local_unnamed_addr #1 { +define dso_local noundef i32 @main() #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-SAME: ) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[TMP0:%.*]] = tail call fastcc i32 @f1() +; CHECK-NEXT: tail call void @f1() ; CHECK-NEXT: ret i32 0 ; entry: @@ -25,30 +25,29 @@ entry: ret i32 0 } - ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal i32 @f1() local_unnamed_addr #0 { +define internal void @f1() #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-LABEL: define internal void @f1( +; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @bar2() -; CHECK-NEXT: tail call fastcc void @baz() -; CHECK-NEXT: ret i32 0 +; CHECK-NEXT: tail call void @bar2() +; CHECK-NEXT: tail call void @baz() +; CHECK-NEXT: ret void ; entry: tail call void @bar2() tail call void @baz() - ret i32 0 + ret void } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar4() local_unnamed_addr #0 { +define internal void @bar4() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @bar4( -; CHECK-SAME: ) unnamed_addr #[[ATTR1:[0-9]+]] { +; CHECK-LABEL: define internal void @bar4( +; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @bar2() +; CHECK-NEXT: tail call void @bar2() ; CHECK-NEXT: ret void ; entry: @@ -57,12 +56,12 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar2() local_unnamed_addr #0 { +define internal void @bar2() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @bar2( -; CHECK-SAME: ) unnamed_addr #[[ATTR1]] { +; CHECK-LABEL: define internal void @bar2( +; CHECK-SAME: ) #[[ATTR1]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @bar3() +; CHECK-NEXT: tail call void @bar3() ; CHECK-NEXT: ret void ; entry: @@ -71,12 +70,12 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar3() local_unnamed_addr #0 { +define internal void @bar3() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @bar3( -; CHECK-SAME: ) unnamed_addr #[[ATTR1]] { +; CHECK-LABEL: define internal void @bar3( +; CHECK-SAME: ) #[[ATTR1]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @bar4() +; CHECK-NEXT: tail call void @bar4() ; CHECK-NEXT: ret void ; entry: @@ -85,12 +84,12 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @fun() local_unnamed_addr #0 { +define internal void @fun() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @fun( -; CHECK-SAME: ) unnamed_addr #[[ATTR1]] { +; CHECK-LABEL: define internal void @fun( +; CHECK-SAME: ) #[[ATTR1]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @baz() +; CHECK-NEXT: tail call void @baz() ; CHECK-NEXT: ret void ; entry: @@ -99,12 +98,12 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @baz() local_unnamed_addr #0 { +define internal void @baz() #0 { ; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -; CHECK-LABEL: define internal fastcc void @baz( -; CHECK-SAME: ) unnamed_addr #[[ATTR1]] { +; CHECK-LABEL: define internal void @baz( +; CHECK-SAME: ) #[[ATTR1]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @fun() +; CHECK-NEXT: tail call void @fun() ; CHECK-NEXT: ret void ; entry: diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll index c3e897d056441..07d2a53fe6b8b 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 5 -; RUN: opt < %s -passes="lto" -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -force-lto-funcattrs -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 @@ -14,8 +14,8 @@ ; 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 fastcc void @callee6( -; CHECK-SAME: ) unnamed_addr #[[ATTR0:[0-9]+]] { +; 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 ; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP0]], 1 @@ -32,16 +32,16 @@ entry: ; 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 fastcc void @callee5( -; CHECK-SAME: i32 noundef [[X:%.*]]) unnamed_addr #[[ATTR1:[0-9]+]] { +; 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 ; CHECK-NEXT: br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN]]: -; CHECK-NEXT: tail call fastcc void @callee5(i32 noundef [[X]]) +; CHECK-NEXT: tail call void @callee5(i32 noundef [[X]]) ; CHECK-NEXT: br label %[[IF_END]] ; CHECK: [[IF_END]]: -; CHECK-NEXT: tail call fastcc void @callee6() +; CHECK-NEXT: tail call void @callee6() ; CHECK-NEXT: ret void ; entry: @@ -60,11 +60,11 @@ 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 fastcc void @callee4( -; CHECK-SAME: ) unnamed_addr #[[ATTR2:[0-9]+]] { +; 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 -; CHECK-NEXT: tail call fastcc void @callee5(i32 noundef [[TMP0]]) +; CHECK-NEXT: tail call void @callee5(i32 noundef [[TMP0]]) ; CHECK-NEXT: ret void ; entry: @@ -76,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 fastcc void @callee3( -; CHECK-SAME: ) unnamed_addr #[[ATTR2]] { +; CHECK-LABEL: define internal void @callee3( +; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @callee4() +; CHECK-NEXT: tail call void @callee4() ; CHECK-NEXT: ret void ; entry: @@ -90,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 fastcc void @callee2( -; CHECK-SAME: ) unnamed_addr #[[ATTR2]] { +; CHECK-LABEL: define internal void @callee2( +; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @callee3() +; CHECK-NEXT: tail call void @callee3() ; CHECK-NEXT: ret void ; entry: @@ -104,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 fastcc void @callee1( -; CHECK-SAME: ) unnamed_addr #[[ATTR2]] { +; CHECK-LABEL: define internal void @callee1( +; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @callee2() +; CHECK-NEXT: tail call void @callee2() ; CHECK-NEXT: ret void ; entry: @@ -118,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 fastcc void @bob( -; CHECK-SAME: ) unnamed_addr #[[ATTR2]] { +; CHECK-LABEL: define internal void @bob( +; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @callee1() +; CHECK-NEXT: tail call void @callee1() ; CHECK-NEXT: ret void ; entry: @@ -135,7 +135,7 @@ define dso_local noundef i32 @main() local_unnamed_addr #3 { ; CHECK-LABEL: define dso_local noundef i32 @main( ; CHECK-SAME: ) local_unnamed_addr #[[ATTR3:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: tail call fastcc void @bob() +; CHECK-NEXT: tail call void @bob() ; CHECK-NEXT: ret i32 0 ; entry: From 338a47e48bc93d95526227d4368772ec3352e656 Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Mon, 23 Jun 2025 15:22:59 +0000 Subject: [PATCH 11/12] Rename testfiles as per comments --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 2 +- ...bfunc_addressTaken.ll => norecurse_libfunc_address_taken.ll} | 0 ..._NoAddressTaken.ll => norecurse_libfunc_no_address_taken.ll} | 0 ...t_recursion.ll => norecurse_multi_scc_indirect_recursion.ll} | 0 ...recursion1.ll => norecurse_multi_scc_indirect_recursion1.ll} | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename llvm/test/Transforms/FunctionAttrs/{norecurse_libfunc_addressTaken.ll => norecurse_libfunc_address_taken.ll} (100%) rename llvm/test/Transforms/FunctionAttrs/{norecurse_libfunc_NoAddressTaken.ll => norecurse_libfunc_no_address_taken.ll} (100%) rename llvm/test/Transforms/FunctionAttrs/{norecurse_multiSCC_indirect_recursion.ll => norecurse_multi_scc_indirect_recursion.ll} (100%) rename llvm/test/Transforms/FunctionAttrs/{norecurse_multiSCC_indirect_recursion1.ll => norecurse_multi_scc_indirect_recursion1.ll} (100%) diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index 26a179466e479..41538c37376ff 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -2116,7 +2116,7 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes, // Every call was either to an external function guaranteed to not make a // call to this function or a direct call to internal function. Also, SCC is - // one. Together, the above checks ensures, this function cannot norecurse. + // one. Together, the above checks ensures, this function cannot recurse. F->setDoesNotRecurse(); ++NumNoRecurse; Changed.insert(F); diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_addressTaken.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_address_taken.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_addressTaken.ll rename to llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_address_taken.ll diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_NoAddressTaken.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_no_address_taken.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_NoAddressTaken.ll rename to llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_no_address_taken.ll diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multi_scc_indirect_recursion.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion.ll rename to llvm/test/Transforms/FunctionAttrs/norecurse_multi_scc_indirect_recursion.ll diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multi_scc_indirect_recursion1.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/norecurse_multiSCC_indirect_recursion1.ll rename to llvm/test/Transforms/FunctionAttrs/norecurse_multi_scc_indirect_recursion1.ll From 1a626a6fcffe0804869075a3969d4ab44217f67b Mon Sep 17 00:00:00 2001 From: Usha Gupta Date: Wed, 23 Jul 2025 17:30:10 +0000 Subject: [PATCH 12/12] Rebased and addressed review comments --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 52 ++++++++-------- .../norecurse_libfunc_address_taken.ll | 21 ++----- .../norecurse_libfunc_no_address_taken.ll | 16 ++--- .../norecurse_multi_scc_indirect_recursion.ll | 50 +++++++-------- ...norecurse_multi_scc_indirect_recursion1.ll | 31 +++++----- .../norecurse_self_recursive_callee.ll | 61 ++++++++----------- 6 files changed, 97 insertions(+), 134 deletions(-) diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index 41538c37376ff..9184c65e12539 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -2285,13 +2285,10 @@ deriveAttrsInPostOrder(ArrayRef Functions, AARGetterT &&AARGetter, addColdAttrs(Nodes.SCCNodes, Changed); addWillReturn(Nodes.SCCNodes, Changed); addNoUndefAttrs(Nodes.SCCNodes, Changed); - - // If we have no external nodes participating in the SCC, we can deduce some - // more precise attributes as well. - addNoAliasAttrs(Nodes.SCCNodes, Changed); - addNonNullAttrs(Nodes.SCCNodes, Changed); - inferAttrsFromFunctionBodies(Nodes.SCCNodes, Changed); - addNoRecurseAttrs(Nodes.SCCNodes, Changed, NoFunctionAddressIsTaken); + addNoAliasAttrs(Nodes.SCCNodes, Changed); + addNonNullAttrs(Nodes.SCCNodes, Changed); + inferAttrsFromFunctionBodies(Nodes.SCCNodes, Changed); + addNoRecurseAttrs(Nodes.SCCNodes, Changed, NoFunctionAddressIsTaken); // Finally, infer the maximal set of attributes from the ones we've inferred // above. This is handling the cases where one attribute on a signature @@ -2333,38 +2330,37 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C, Functions.push_back(&N.getFunction()); } - bool NoFunctionsAddressIsTaken = false; // 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 and all functions have internal // linkage, there is no path for a callback to any user function. - if (IsLTOPostLink || ForceLTOFuncAttrs) { - bool AnyFunctionsAddressIsTaken = false; - // 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; + bool AnyFunctionsAddressIsTaken = false; + // 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 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 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 - } + if (!F.hasLocalLinkage() || F.hasAddressTaken()) { + AnyFunctionsAddressIsTaken = true; + break; // break if we found one } - NoFunctionsAddressIsTaken = !AnyFunctionsAddressIsTaken; } + if (ForceLTOFuncAttrs) + IsLTOPostLink = true; // For testing purposes only auto ChangedFunctions = deriveAttrsInPostOrder( - Functions, AARGetter, ArgAttrsOnly, NoFunctionsAddressIsTaken); + Functions, AARGetter, ArgAttrsOnly, + IsLTOPostLink ? !AnyFunctionsAddressIsTaken : false); if (ChangedFunctions.empty()) return PreservedAnalyses::all(); diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_address_taken.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_address_taken.ll index bd4b838b6d2b2..f1a13aa4cf9c4 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_address_taken.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_address_taken.ll @@ -7,11 +7,8 @@ @.str = private unnamed_addr constant [12 x i8] c"Hello World\00", align 1 -; Function Attrs: nofree noinline nounwind uwtable -define dso_local void @bob() #0 { -; CHECK: Function Attrs: nofree noinline nounwind uwtable -; CHECK-LABEL: define dso_local void @bob( -; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +define dso_local void @bob() { +; CHECK-LABEL: define dso_local void @bob() { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[CALL:%.*]] = tail call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str) ; CHECK-NEXT: ret void @@ -21,14 +18,12 @@ entry: ret void } -; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr noundef readonly captures(none), ...) local_unnamed_addr #1 +declare noundef i32 @printf(ptr noundef readonly captures(none), ...) -; Function Attrs: nofree norecurse nounwind uwtable -define dso_local noundef i32 @main() #2 { -; CHECK: Function Attrs: nofree norecurse nounwind uwtable +define dso_local noundef i32 @main() norecurse { +; CHECK: Function Attrs: norecurse ; CHECK-LABEL: define dso_local noundef i32 @main( -; CHECK-SAME: ) #[[ATTR2:[0-9]+]] { +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: tail call void @bob() ; CHECK-NEXT: ret i32 0 @@ -37,7 +32,3 @@ entry: tail call void @bob() ret i32 0 } - -attributes #0 = { nofree noinline nounwind uwtable } -attributes #1 = { nofree nounwind } -attributes #2 = { nofree norecurse nounwind uwtable } diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_no_address_taken.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_no_address_taken.ll index 6111f9ae777f6..cf32676527ed2 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_no_address_taken.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse_libfunc_no_address_taken.ll @@ -8,8 +8,8 @@ @.str = private unnamed_addr constant [12 x i8] c"Hello World\00", align 1 ; Function Attrs: nofree noinline nounwind uwtable -define internal void @bob() #0 { -; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable +define internal void @bob() { +; CHECK: Function Attrs: norecurse ; CHECK-LABEL: define internal void @bob( ; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -22,13 +22,13 @@ entry: } ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr noundef readonly captures(none), ...) local_unnamed_addr #1 +declare noundef i32 @printf(ptr noundef readonly captures(none), ...) ; Function Attrs: nofree norecurse nounwind uwtable -define dso_local noundef i32 @main() #2 { -; CHECK: Function Attrs: nofree norecurse nounwind uwtable +define dso_local noundef i32 @main() norecurse { +; CHECK: Function Attrs: norecurse ; CHECK-LABEL: define dso_local noundef i32 @main( -; CHECK-SAME: ) #[[ATTR2:[0-9]+]] { +; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: tail call void @bob() ; CHECK-NEXT: ret i32 0 @@ -37,7 +37,3 @@ entry: tail call void @bob() ret i32 0 } - -attributes #0 = { nofree noinline nounwind uwtable } -attributes #1 = { nofree nounwind } -attributes #2 = { nofree norecurse nounwind uwtable } diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse_multi_scc_indirect_recursion.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multi_scc_indirect_recursion.ll index ed95e8a590e44..ed01b88db56b6 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse_multi_scc_indirect_recursion.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse_multi_scc_indirect_recursion.ll @@ -1,5 +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 -force-lto-funcattrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -force-lto-funcattrs -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 @@ -11,8 +11,8 @@ ; None of these functions should be marked as norecurse ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar1() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @bar1() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @bar1( ; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -25,8 +25,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @f1() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @f1() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @f1( ; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -43,8 +43,8 @@ entry: } ; Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable -define dso_local noundef i32 @main() #1 { -; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable +define dso_local noundef i32 @main() norecurse { +; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(none) ; CHECK-LABEL: define dso_local noundef i32 @main( ; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -57,8 +57,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @foo1() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @foo1() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @foo1( ; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -71,8 +71,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @bar() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @bar( ; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -85,8 +85,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @foo() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @foo() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @foo( ; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -99,8 +99,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar4() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @bar4() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @bar4( ; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -113,8 +113,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar2() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @bar2() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @bar2( ; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -127,8 +127,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar3() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @bar3() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @bar3( ; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -141,8 +141,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @fun() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @fun() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @fun( ; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -154,9 +154,8 @@ entry: ret void } -; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @baz() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @baz() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @baz( ; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -167,6 +166,3 @@ entry: tail call void @fun() ret void } - -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_multi_scc_indirect_recursion1.ll b/llvm/test/Transforms/FunctionAttrs/norecurse_multi_scc_indirect_recursion1.ll index 656ab669c5fde..4004ed0f1d40b 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse_multi_scc_indirect_recursion1.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse_multi_scc_indirect_recursion1.ll @@ -12,8 +12,8 @@ ; norecurse. ; Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable -define dso_local noundef i32 @main() #1 { -; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable +define dso_local noundef i32 @main() norecurse { +; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(none) ; CHECK-LABEL: define dso_local noundef i32 @main( ; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -26,8 +26,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @f1() #0 { -; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind memory(none) uwtable +define internal void @f1() { +; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(none) ; CHECK-LABEL: define internal void @f1( ; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -42,8 +42,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar4() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @bar4() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @bar4( ; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -56,8 +56,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar2() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @bar2() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @bar2( ; CHECK-SAME: ) #[[ATTR1]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -70,8 +70,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @bar3() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @bar3() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @bar3( ; CHECK-SAME: ) #[[ATTR1]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -84,8 +84,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @fun() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @fun() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @fun( ; CHECK-SAME: ) #[[ATTR1]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -98,8 +98,8 @@ entry: } ; Function Attrs: nofree noinline nosync nounwind memory(none) uwtable -define internal void @baz() #0 { -; CHECK: Function Attrs: nofree noinline nosync nounwind memory(none) uwtable +define internal void @baz() { +; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define internal void @baz( ; CHECK-SAME: ) #[[ATTR1]] { ; CHECK-NEXT: [[ENTRY:.*:]] @@ -110,6 +110,3 @@ entry: tail call void @fun() ret void } - -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 07d2a53fe6b8b..f4a809335e1e9 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse_self_recursive_callee.ll @@ -11,11 +11,10 @@ @x = dso_local global i32 4, align 4 @y = dso_local global i32 2, align 4 -; 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 +define internal void @callee6() { +; CHECK: Function Attrs: nofree norecurse nounwind memory(readwrite, argmem: none) ; CHECK-LABEL: define internal void @callee6( -; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[TMP0:%.*]] = load volatile i32, ptr @y, align 4 ; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP0]], 1 @@ -29,11 +28,10 @@ entry: 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 +define internal void @callee5(i32 noundef %x) { +; CHECK: Function Attrs: nofree nounwind ; CHECK-LABEL: define internal void @callee5( -; CHECK-SAME: i32 noundef [[X:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +; CHECK-SAME: i32 noundef [[X:%.*]]) #[[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:.*]] @@ -57,11 +55,10 @@ if.end: ; preds = %if.then, %entry ret void } -; Function Attrs: nofree noinline nounwind uwtable -define internal void @callee4() local_unnamed_addr #1 { -; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable +define internal void @callee4() { +; CHECK: Function Attrs: nofree norecurse nounwind ; CHECK-LABEL: define internal void @callee4( -; CHECK-SAME: ) local_unnamed_addr #[[ATTR2:[0-9]+]] { +; CHECK-SAME: ) #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[TMP0:%.*]] = load volatile i32, ptr @x, align 4 ; CHECK-NEXT: tail call void @callee5(i32 noundef [[TMP0]]) @@ -73,11 +70,10 @@ entry: ret void } -; Function Attrs: nofree noinline nounwind uwtable -define internal void @callee3() local_unnamed_addr #1 { -; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable +define internal void @callee3() { +; CHECK: Function Attrs: nofree norecurse nounwind ; CHECK-LABEL: define internal void @callee3( -; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: ) #[[ATTR2]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: tail call void @callee4() ; CHECK-NEXT: ret void @@ -87,11 +83,10 @@ entry: ret void } -; Function Attrs: nofree noinline nounwind uwtable -define internal void @callee2() local_unnamed_addr #1 { -; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable +define internal void @callee2() { +; CHECK: Function Attrs: nofree norecurse nounwind ; CHECK-LABEL: define internal void @callee2( -; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: ) #[[ATTR2]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: tail call void @callee3() ; CHECK-NEXT: ret void @@ -101,11 +96,10 @@ entry: ret void } -; Function Attrs: nofree noinline nounwind uwtable -define internal void @callee1() local_unnamed_addr #1 { -; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable +define internal void @callee1() { +; CHECK: Function Attrs: nofree norecurse nounwind ; CHECK-LABEL: define internal void @callee1( -; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: ) #[[ATTR2]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: tail call void @callee2() ; CHECK-NEXT: ret void @@ -115,11 +109,10 @@ entry: ret void } -; Function Attrs: nofree noinline nounwind uwtable -define internal void @bob() local_unnamed_addr #1 { -; CHECK: Function Attrs: nofree noinline norecurse nounwind uwtable +define internal void @bob() { +; CHECK: Function Attrs: nofree norecurse nounwind ; CHECK-LABEL: define internal void @bob( -; CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: ) #[[ATTR2]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: tail call void @callee1() ; CHECK-NEXT: ret void @@ -129,11 +122,10 @@ entry: ret void } -; Function Attrs: nofree norecurse nounwind uwtable -define dso_local noundef i32 @main() local_unnamed_addr #3 { -; CHECK: Function Attrs: nofree norecurse nounwind uwtable +define dso_local noundef i32 @main() norecurse { +; CHECK: Function Attrs: nofree norecurse nounwind ; CHECK-LABEL: define dso_local noundef i32 @main( -; CHECK-SAME: ) local_unnamed_addr #[[ATTR3:[0-9]+]] { +; CHECK-SAME: ) #[[ATTR2]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: tail call void @bob() ; CHECK-NEXT: ret i32 0 @@ -142,8 +134,3 @@ entry: tail call void @bob() ret i32 0 } - -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 }