[llvm] [IR] Define empty and malformed !callees semantics (PR #221550)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 01:07:03 PDT 2026


================
@@ -7778,12 +7778,24 @@ Example (assuming 64-bit pointers):
 
 #### '`callees`' Metadata
 
-`callees` metadata may be attached to indirect call sites. If `callees`
-metadata is attached to a call site, and any callee is not among the set of
-functions provided by the metadata, the behavior is undefined. The intent of
-this metadata is to facilitate optimizations such as indirect-call promotion.
-For example, in the code below, the call instruction may only target the
-`add` or `sub` functions:
+`callees` metadata may be attached to call sites.
+Its operands provide an exhaustive list of possible callees.
+The list may be conservative: a listed function need not be dynamically feasible, but on every defined execution of the call the callee must be one of the listed functions.
+The order and duplication of operands are semantically irrelevant.
+The intent of this metadata is to facilitate optimizations such as indirect-call promotion.
+
+The constraint applies whether the called operand is a constant or not.
+Executing a direct call whose target is not in the list has undefined behavior.
+If the direct target is in the list, the attachment is redundant and may be dropped.
+
+An empty node is an exhaustive empty set, so executing the call has undefined behavior.
+If the metadata is absent, no exhaustive callee information is provided.
+
+Each operand must refer to a `Function`.
+If any operand does not, the entire attachment provides no information and must be ignored.
----------------
mmiftahx wrote:

@nikic 

> Is `!callees` normally only used with full LTO...

Thanks for confirming the semantic point. `CalledValuePropagation` is in the [default optimized non-LTO pipeline](https://github.com/llvm/llvm-project/blob/7d1f3eadd5e0281fe5489b6cde5f9cb1719963d3/llvm/lib/Passes/PassBuilderPipelines.cpp#L1220-L1222) and the standard ThinLTO pipelines, and it can emit lists containing declarations. Its [`simple-select.ll` test](https://github.com/llvm/llvm-project/blob/7d1f3eadd5e0281fe5489b6cde5f9cb1719963d3/llvm/test/Transforms/CalledValuePropagation/simple-select.ll#L18-L37) produces `!callees` naming two declared functions.

In that example, the live `select` keeps both declarations referenced by ordinary IR operands, so it does not exercise the metadata-only retention problem. Declaration targets therefore do not by themselves imply that the information will be lost. I have not measured how often declaration removal loses useful `!callees` information in real workloads.

If a declaration is referenced only through metadata, `StripDeadPrototypes` or `GlobalDCE` can remove it and leave `null`, losing the information needed to interpret the list exhaustively. This patch addresses consumers’ **conservative** handling of that loss; preserving the information across declaration deletion is a separate issue.


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


More information about the llvm-commits mailing list