[llvm] [FuncAttrs] Relax norecurse attribute inference (PR #139943)

Usha Gupta via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 2 09:15:57 PDT 2025


================
@@ -2322,8 +2343,39 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
     Functions.push_back(&N.getFunction());
   }
 
-  auto ChangedFunctions =
-      deriveAttrsInPostOrder(Functions, AARGetter, ArgAttrsOnly);
+  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;
+
+      // 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;
----------------
usha1830 wrote:

@nikic I was running some additional tests based on the example you suggested.
I might be mistaken, but it seems there’s an issue when compiling the test below (with and without this PR) using **-flto** and C++ as the source language.

The function `norecurse_fn()` gets internalized, and as a result, _ReversePostOrderFunctionAttrsPass_ marks it as `norecurse`. This, in turn, causes function` a` to also be marked as `norecurse`.

I think the reason for this is `main()` being marked as `norecurse` and `norecurse_fn` getting internalized during **-flto**. If one of these conditions is removed, `a` and `norecurse_fn` does get `norecurse`. (Used same program in C mode to avoid main getting norecurse) 

```
extern void calls_norecurse();

__attribute__((noinline))
static void a(int c) {
    if (c) {
        calls_norecurse();
    }
}

__attribute__((noinline))
void norecurse_fn() {
    a(0);
}

__attribute__((noinline))
int foo1() {
    norecurse_fn();
    a(1);
    return 1;
}

int main() {
  int ret = foo1();
  return ret;
}


```

https://github.com/llvm/llvm-project/pull/139943


More information about the llvm-commits mailing list