From ee41e3273418737831332e6326a8c39152522bcb Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 28 Jul 2025 15:24:43 +0200 Subject: [PATCH 1/2] [FunctionAttrs] Don't bail out on unknown calls When inferring attributes, we should not bail out early on unknown calls (such as virtual calls), as we may still have call-site attributes that can be used for inference. Fixes https://github.com/llvm/llvm-project/issues/150817. --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 42 +++++-------------- llvm/test/Transforms/FunctionAttrs/noalias.ll | 2 +- llvm/test/Transforms/FunctionAttrs/nonnull.ll | 2 +- .../Transforms/FunctionAttrs/norecurse.ll | 5 ++- .../test/Transforms/FunctionAttrs/nounwind.ll | 5 ++- 5 files changed, 19 insertions(+), 37 deletions(-) diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index f43202eea6306..7fde4605e5686 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -1863,7 +1863,6 @@ void AttributeInferer::run(const SCCNodeSet &SCCNodes, struct SCCNodesResult { SCCNodeSet SCCNodes; - bool HasUnknownCall; }; } // end anonymous namespace @@ -2087,11 +2086,13 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes, for (auto &BB : *F) for (auto &I : BB.instructionsWithoutDebug()) if (auto *CB = dyn_cast(&I)) { + if (CB->hasFnAttr(Attribute::NoRecurse)) + continue; + Function *Callee = CB->getCalledFunction(); if (!Callee || Callee == F || - (!Callee->doesNotRecurse() && - !(Callee->isDeclaration() && - Callee->hasFnAttribute(Attribute::NoCallback)))) + !(Callee->isDeclaration() && + Callee->hasFnAttribute(Attribute::NoCallback))) // Function calls a potentially recursive function. return; } @@ -2227,29 +2228,13 @@ static void addWillReturn(const SCCNodeSet &SCCNodes, static SCCNodesResult createSCCNodeSet(ArrayRef Functions) { SCCNodesResult Res; - Res.HasUnknownCall = false; for (Function *F : Functions) { if (!F || F->hasOptNone() || F->hasFnAttribute(Attribute::Naked) || F->isPresplitCoroutine()) { - // Treat any function we're trying not to optimize as if it were an - // indirect call and omit it from the node set used below. - Res.HasUnknownCall = true; + // Omit any functions we're trying not to optimize from the set. continue; } - // Track whether any functions in this SCC have an unknown call edge. - // Note: if this is ever a performance hit, we can common it with - // subsequent routines which also do scans over the instructions of the - // function. - if (!Res.HasUnknownCall) { - for (Instruction &I : instructions(*F)) { - if (auto *CB = dyn_cast(&I)) { - if (!CB->getCalledFunction()) { - Res.HasUnknownCall = true; - break; - } - } - } - } + Res.SCCNodes.insert(F); } return Res; @@ -2282,15 +2267,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. - if (!Nodes.HasUnknownCall) { - addNoAliasAttrs(Nodes.SCCNodes, Changed); - addNonNullAttrs(Nodes.SCCNodes, Changed); - inferAttrsFromFunctionBodies(Nodes.SCCNodes, Changed); - addNoRecurseAttrs(Nodes.SCCNodes, Changed); - } + addNoAliasAttrs(Nodes.SCCNodes, Changed); + addNonNullAttrs(Nodes.SCCNodes, Changed); + inferAttrsFromFunctionBodies(Nodes.SCCNodes, Changed); + 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 diff --git a/llvm/test/Transforms/FunctionAttrs/noalias.ll b/llvm/test/Transforms/FunctionAttrs/noalias.ll index 8beb6fe78f852..de8bd9eade621 100644 --- a/llvm/test/Transforms/FunctionAttrs/noalias.ll +++ b/llvm/test/Transforms/FunctionAttrs/noalias.ll @@ -235,7 +235,7 @@ define ptr @return_unknown_call(ptr %fn) { } define ptr @return_unknown_noalias_call(ptr %fn) { -; CHECK-LABEL: define ptr @return_unknown_noalias_call( +; CHECK-LABEL: define noalias ptr @return_unknown_noalias_call( ; CHECK-SAME: ptr readonly captures(none) [[FN:%.*]]) { ; CHECK-NEXT: [[A:%.*]] = call noalias ptr [[FN]]() ; CHECK-NEXT: ret ptr [[A]] diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll index 9b17ded3bdb26..8df242fb023af 100644 --- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll @@ -1412,7 +1412,7 @@ define ptr @unknown_func(ptr %fn) { } define ptr @unknown_nonnull_func(ptr %fn) { -; FNATTRS-LABEL: define ptr @unknown_nonnull_func( +; FNATTRS-LABEL: define nonnull ptr @unknown_nonnull_func( ; FNATTRS-SAME: ptr readonly captures(none) [[FN:%.*]]) { ; FNATTRS-NEXT: [[RES:%.*]] = call nonnull ptr [[FN]]() ; FNATTRS-NEXT: ret ptr [[RES]] diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll index 5cb8ac05847aa..a9437848150da 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse.ll @@ -258,9 +258,10 @@ define void @unknown_call(ptr %fn) { } define void @unknown_norecurse_call(ptr %fn) { +; FNATTRS: Function Attrs: norecurse ; FNATTRS-LABEL: define {{[^@]+}}@unknown_norecurse_call -; FNATTRS-SAME: (ptr readonly captures(none) [[FN:%.*]]) { -; FNATTRS-NEXT: call void [[FN]]() #[[ATTR7:[0-9]+]] +; FNATTRS-SAME: (ptr readonly captures(none) [[FN:%.*]]) #[[ATTR7:[0-9]+]] { +; FNATTRS-NEXT: call void [[FN]]() #[[ATTR7]] ; FNATTRS-NEXT: ret void ; ; ATTRIBUTOR-LABEL: define {{[^@]+}}@unknown_norecurse_call diff --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll index a64d9a6504256..076a7df2781ce 100644 --- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll +++ b/llvm/test/Transforms/FunctionAttrs/nounwind.ll @@ -418,9 +418,10 @@ define void @unknown_call(ptr %fn) { } define void @unknown_nounwind_call(ptr %fn) { +; FNATTRS: Function Attrs: nounwind ; FNATTRS-LABEL: define {{[^@]+}}@unknown_nounwind_call -; FNATTRS-SAME: (ptr readonly captures(none) [[FN:%.*]]) { -; FNATTRS-NEXT: call void [[FN]]() #[[ATTR2:[0-9]+]] +; FNATTRS-SAME: (ptr readonly captures(none) [[FN:%.*]]) #[[ATTR2:[0-9]+]] { +; FNATTRS-NEXT: call void [[FN]]() #[[ATTR2]] ; FNATTRS-NEXT: ret void ; ; ATTRIBUTOR: Function Attrs: nounwind From 62e8ffe3d2a4181d9d3898c25b0019e7a6495079 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 29 Jul 2025 09:39:19 +0200 Subject: [PATCH 2/2] drop norecurse change for now --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 8 +++----- llvm/test/Transforms/FunctionAttrs/norecurse.ll | 5 ++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index 7fde4605e5686..8262c8c3a90f2 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -2086,13 +2086,11 @@ static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes, for (auto &BB : *F) for (auto &I : BB.instructionsWithoutDebug()) if (auto *CB = dyn_cast(&I)) { - if (CB->hasFnAttr(Attribute::NoRecurse)) - continue; - Function *Callee = CB->getCalledFunction(); if (!Callee || Callee == F || - !(Callee->isDeclaration() && - Callee->hasFnAttribute(Attribute::NoCallback))) + (!Callee->doesNotRecurse() && + !(Callee->isDeclaration() && + Callee->hasFnAttribute(Attribute::NoCallback)))) // Function calls a potentially recursive function. return; } diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll index a9437848150da..5cb8ac05847aa 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse.ll @@ -258,10 +258,9 @@ define void @unknown_call(ptr %fn) { } define void @unknown_norecurse_call(ptr %fn) { -; FNATTRS: Function Attrs: norecurse ; FNATTRS-LABEL: define {{[^@]+}}@unknown_norecurse_call -; FNATTRS-SAME: (ptr readonly captures(none) [[FN:%.*]]) #[[ATTR7:[0-9]+]] { -; FNATTRS-NEXT: call void [[FN]]() #[[ATTR7]] +; FNATTRS-SAME: (ptr readonly captures(none) [[FN:%.*]]) { +; FNATTRS-NEXT: call void [[FN]]() #[[ATTR7:[0-9]+]] ; FNATTRS-NEXT: ret void ; ; ATTRIBUTOR-LABEL: define {{[^@]+}}@unknown_norecurse_call