Skip to content

Conversation

usha1830
Copy link
Contributor

@usha1830 usha1830 commented Sep 5, 2025

Update the definition of the norecurse attribute to forbid marking a function as norecurse if any call path from its body may reach it (possibly through an external function without a visible definition). This makes it clear that norecurse excludes both direct and mutual recursion, even when recursion could arise through callees in separate modules.

As per my understanding, this scenario only arises when norecurse is forced through a llvm user option
-mllvm -force-attribute=<fname>:norecurse.

There are a few examples in #157081 which shows that the function attribute inference incorrectly infers norecurse when the behavior (as per new definition) is not enforced.

@llvmbot llvmbot added the llvm:ir label Sep 5, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 5, 2025

@llvm/pr-subscribers-llvm-ir

Author: Usha Gupta (usha1830)

Changes

Update the definition of the norecurse attribute to forbid marking a function as norecurse if any call path from its body may reach an external function without a visible definition. This makes it clear that norecurse excludes both direct and mutual recursion, even when recursion could arise through callees in separate modules.

As per my understanding, this scenario only arises when norecurse is forced through a llvm user option
-mllvm -force-attribute=&lt;fname&gt;:norecurse.

There are a few examples in #157081 which shows that the function attribute inference incorrectly infers norecurse when the behavior (as per new definition) is not enforced.


Full diff: https://github.com/llvm/llvm-project/pull/157087.diff

1 Files Affected:

  • (modified) llvm/docs/LangRef.rst (+6-3)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index e64b9343b7622..787592a85971e 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2256,9 +2256,12 @@ For example:
     behavior at runtime if the function ever does dynamically return. Annotated
     functions may still raise an exception, i.a., ``nounwind`` is not implied.
 ``norecurse``
-    This function attribute indicates that the function does not call itself
-    either directly or indirectly down any possible call path. This produces
-    undefined behavior at runtime if the function ever does recurse.
+    This function attribute indicates that the function does not participate in
+    recursion, either directly or through mutual recursion. At runtime it is
+    undefined behavior if any dynamic call stack contains this function more
+    than once at the same time. A function must not be marked ``norecurse`` if,
+    along any call path starting from its body, control may reach an external
+    function whose definition is not available.
 
 .. _langref_willreturn:
 

@usha1830 usha1830 changed the title [LangRef] Clarify to exclude any call paths through external functions [LangRef] Clarify to exclude norecurse attribute when any call paths through external functions is present Sep 5, 2025
@nikic nikic requested a review from efriedma-quic September 5, 2025 12:33
@usha1830 usha1830 changed the title [LangRef] Clarify to exclude norecurse attribute when any call paths through external functions is present [LangRef] Clarify to exclude norecurse attribute when a function could occur in a cycle in dynamic call-graph Sep 15, 2025
@usha1830
Copy link
Contributor Author

Just a friendly ping.
Please let me know if you have any questions or if there's anything I can do to help move it along.
Thanks!

@nikic nikic requested a review from fhahn September 15, 2025 18:35
Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, but I'd like to have a second opinion on this one.

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

.. code-block:: llvm

fn -> other_fn -> fn ; fn is not norecurse
other_fn -> fn -> other_fn ; fn is not norecurse
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old definition of norecurse arguably allows other_fn -> fn -> other_fn. For some uses of norecurse, the uniqueness of the stack frame itself is sufficient. But I can see how it would cause difficulties for inference. If we can't actually infer norecurse, it's pretty useless; frontends will rarely add norecurse markings.

@usha1830
Copy link
Contributor Author

@nikic @efriedma-quic Thank you for taking the time to review this!

@usha1830 usha1830 merged commit d8e1c20 into llvm:main Sep 17, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants