[llvm] [Attributor] Drop norecurse when specializing an indirect call closes a cycle (PR #218637)
Larry Meadows via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 25 12:59:39 PDT 2026
================
@@ -2670,6 +2671,42 @@ ChangeStatus Attributor::cleanupIR() {
Configuration.CGUpdater.removeFunction(*Fn);
}
+ // Specializing an indirect call turns an edge that only existed through a
+ // function pointer into a direct one, which can close a call graph cycle and
+ // make `norecurse` false for every function in it.
+ if (!SpecializedIndirectCallers.empty()) {
+ CallGraph CG(*SpecializedIndirectCallers.front()->getParent());
+ SmallPtrSet<Function *, 8> Handled;
+ for (Function *Caller : SpecializedIndirectCallers) {
+ if (!Handled.insert(Caller).second)
+ continue;
+ CallGraphNode *Node = CG[Caller];
+ for (scc_iterator<CallGraphNode *> It = scc_begin(Node); !It.isAtEnd();
+ ++It) {
+ if (!is_contained(*It, Node))
+ continue;
+ if (!It.hasCycle())
+ break;
+ for (CallGraphNode *N : *It) {
+ Function *Fn = N->getFunction();
+ if (!Fn || ToBeDeletedFunctions.count(Fn) || !Functions.count(Fn))
+ continue;
+ Handled.insert(Fn);
+ if (!Fn->hasFnAttribute(Attribute::NoRecurse))
+ continue;
+ Fn->removeFnAttr(Attribute::NoRecurse);
+ for (User *U : Fn->users())
+ if (auto *CB = dyn_cast<CallBase>(U))
+ if (CB->getCalledFunction() == Fn &&
+ Functions.count(CB->getFunction()))
+ CB->removeFnAttr(Attribute::NoRecurse);
----------------
lfmeadow wrote:
The `bitcast` example does not arise: with opaque pointers a `ptr`-to-`ptr` bitcast
of a function is not representable, so `call void bitcast (ptr @callee to ptr)()`
parses as `call void @callee()`, and calls built through `FunctionCallee` have the
function itself as the callee operand.
The mechanism is real for `addrspacecast`, though, which does survive as a
constant expression, and there the call site is a user of the expression rather
than of the function. Fixed by matching `getCalledOperand()->stripPointerCasts()`
over the call sites in the functions the Attributor tracks, rather than walking
`Fn->users()`, which also covers any other constant expression form.
`norecurse_call_site_constant_expr.ll` is the regression test; it keeps a stale
`norecurse` on the call site without the change.
https://github.com/llvm/llvm-project/pull/218637
More information about the llvm-commits
mailing list