[llvm] [Attributor][NFC] Use poison for the placeholder callee in callgraph.ll (PR #217082)
Larry Meadows via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 18 10:01:12 PDT 2026
https://github.com/lfmeadow created https://github.com/llvm/llvm-project/pull/217082
Stacked on #217077 and #217079, which regenerate this file; only the last commit belongs to this PR.
`@undef_in_callees` checks that a `!callees` list naming something that is not a function does not crash the pass. The callee, its arguments and the entry in the metadata are all placeholders standing for "not a function", which `poison` says as well as `undef` does.
`PoisonValue` is a `UndefValue`, so the pass cannot tell the difference: regenerating after the swap gives a file identical token for token, which is also why this is worth doing — it moves a test off `undef` at no cost.
```llvm
- call void undef(ptr undef, i32 undef, ptr undef), !callees !3
+ call void poison(ptr poison, i32 poison, ptr poison), !callees !3
-!3 = distinct !{ptr undef, ptr null}
+!3 = distinct !{ptr poison, ptr null}
```
cc @jdoerfert @nikic
Made with [Cursor](https://cursor.com)
>From 28b6cdd1bf8e27e82d2e8080f268236779b44857 Mon Sep 17 00:00:00 2001
From: Larry Meadows <Lawrence.Meadows at amd.com>
Date: Tue, 18 Aug 2026 11:43:42 -0500
Subject: [PATCH 1/3] [Attributor] Don't specialize an indirect call for a
callee it cannot call
AAIndirectCallInfoCallSite::manifest() replaces an indirect call with an
if-cascade of direct calls, one arm per assumed callee. Where the callee cannot
be promoted to, it asks isLegalToPromote(), is told no, and then builds the call
anyway by another route:
CallInst::Create(FunctionCallee(CSFT, NewCallee), CSArgs, ...)
that is, a direct call naming the callee but carrying the call site's function
type and operands. For a call site passing one argument and a callee taking two,
the result is
call void @two_params(i32 1)
where @two_params reads a second parameter that no caller supplies. The
signature the call site has is not the callee's, so this is not a call the
indirect call could have performed; it is only reachable through it in ways that
are undefined. Naming the callee directly also hands the rest of the pipeline a
call site to reason about as if the signature matched.
Skip those callees instead and leave them to the indirect fallback, which the
!callees metadata already names, so no information is lost. Since the remaining
arms are promotable by construction, the fallback path in the loop and the
single-callee case both go away.
Two in-tree tests change. In nounwind.ll, @foo1 and @scc1_foo return i32 while
the call site is `call void %fp()`, and in callgraph.ll several cases are the
same shape; the punned direct calls are replaced by the indirect call that
survives with its metadata.
Co-authored-by: Cursor <cursoragent at cursor.com>
---
.../Transforms/IPO/AttributorAttributes.cpp | 42 +-
llvm/test/Transforms/Attributor/callgraph.ll | 407 ++++--------------
.../indirect_call_signature_mismatch.ll | 57 +++
llvm/test/Transforms/Attributor/nounwind.ll | 97 ++---
4 files changed, 203 insertions(+), 400 deletions(-)
create mode 100644 llvm/test/Transforms/Attributor/indirect_call_signature_mismatch.ll
diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 050bc32355253..484d163d3c01b 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -12524,8 +12524,6 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
bool CBIsVoid = CB->getType()->isVoidTy();
BasicBlock::iterator IP = CB->getIterator();
- FunctionType *CSFT = CB->getFunctionType();
- SmallVector<Value *> CSArgs(CB->args());
// If we know all callees and there are none, the call site is (effectively)
// dead (or UB).
@@ -12537,19 +12535,10 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
}
// Special handling for the single callee case.
- if (AllCalleesKnown && AssumedCallees.size() == 1) {
- auto *NewCallee = AssumedCallees.front();
- if (isLegalToPromote(*CB, NewCallee)) {
- promoteCall(*CB, NewCallee, nullptr);
- NumIndirectCallsPromoted++;
- return ChangeStatus::CHANGED;
- }
- Instruction *NewCall =
- CallInst::Create(FunctionCallee(CSFT, NewCallee), CSArgs,
- CB->getName(), CB->getIterator());
- if (!CBIsVoid)
- A.changeAfterManifest(IRPosition::callsite_returned(*CB), *NewCall);
- A.deleteAfterManifest(*CB);
+ if (AllCalleesKnown && AssumedCallees.size() == 1 &&
+ isLegalToPromote(*CB, AssumedCallees.front())) {
+ promoteCall(*CB, AssumedCallees.front(), nullptr);
+ NumIndirectCallsPromoted++;
return ChangeStatus::CHANGED;
}
@@ -12566,7 +12555,13 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
SmallVector<Function *, 8> SkippedAssumedCallees;
SmallVector<std::pair<CallInst *, Instruction *>> NewCalls;
for (Function *NewCallee : AssumedCallees) {
- if (!A.shouldSpecializeCallSiteForCallee(*this, *CB, *NewCallee,
+ // A callee the call site cannot be promoted to is one it can only reach
+ // in ways that are undefined, so a direct call built from the call site's
+ // own operands would not be the call the indirect call performs. Leave it
+ // to the indirect fallback, which the !callees metadata below still
+ // names.
+ if (!isLegalToPromote(*CB, NewCallee) ||
+ !A.shouldSpecializeCallSiteForCallee(*this, *CB, *NewCallee,
AssumedCallees.size())) {
SkippedAssumedCallees.push_back(NewCallee);
SpecializedForAllCallees = false;
@@ -12593,16 +12588,11 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
ThenTI->replaceUsesOfWith(ElseBB, CBBB);
}
CastInst *RetBC = nullptr;
- CallInst *NewCall = nullptr;
- if (isLegalToPromote(*CB, NewCallee)) {
- auto *CBClone = cast<CallBase>(CB->clone());
- CBClone->insertBefore(ThenTI->getIterator());
- NewCall = &cast<CallInst>(promoteCall(*CBClone, NewCallee, &RetBC));
- NumIndirectCallsPromoted++;
- } else {
- NewCall = CallInst::Create(FunctionCallee(CSFT, NewCallee), CSArgs,
- CB->getName(), ThenTI->getIterator());
- }
+ auto *CBClone = cast<CallBase>(CB->clone());
+ CBClone->insertBefore(ThenTI->getIterator());
+ CallInst *NewCall =
+ &cast<CallInst>(promoteCall(*CBClone, NewCallee, &RetBC));
+ NumIndirectCallsPromoted++;
NewCalls.push_back({NewCall, RetBC});
}
diff --git a/llvm/test/Transforms/Attributor/callgraph.ll b/llvm/test/Transforms/Attributor/callgraph.ll
index 84e2c54bd832d..0dd6ac64158d6 100644
--- a/llvm/test/Transforms/Attributor/callgraph.ll
+++ b/llvm/test/Transforms/Attributor/callgraph.ll
@@ -128,61 +128,23 @@ declare float @retFloatTakeFloatFloatNoundef(float, float noundef)
declare void @void()
define i32 @non_matching_fp1(i1 %c1, i1 %c2, i1 %c) {
-; UNLIM-LABEL: @non_matching_fp1(
-; UNLIM-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
-; UNLIM-NEXT: [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr @void
-; UNLIM-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
-; UNLIM-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
-; UNLIM-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; UNLIM: 2:
-; UNLIM-NEXT: [[CALL1:%.*]] = call i32 @takeI32(i32 42)
-; UNLIM-NEXT: br label [[TMP15:%.*]]
-; UNLIM: 3:
-; UNLIM-NEXT: [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
-; UNLIM-NEXT: br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
-; UNLIM: 5:
-; UNLIM-NEXT: [[CALL2:%.*]] = call i32 @retI32(i32 42)
-; UNLIM-NEXT: br label [[TMP15]]
-; UNLIM: 6:
-; UNLIM-NEXT: [[TMP7:%.*]] = icmp eq ptr [[FP]], @void
-; UNLIM-NEXT: br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP9:%.*]]
-; UNLIM: 8:
-; UNLIM-NEXT: [[CALL3:%.*]] = call i32 @void(i32 42)
-; UNLIM-NEXT: br label [[TMP15]]
-; UNLIM: 9:
-; UNLIM-NEXT: br i1 true, label [[TMP10:%.*]], label [[TMP14:%.*]]
-; UNLIM: 10:
-; UNLIM-NEXT: [[TMP11:%.*]] = bitcast i32 42 to float
-; UNLIM-NEXT: [[TMP12:%.*]] = call float @retFloatTakeFloat(float [[TMP11]])
-; UNLIM-NEXT: [[TMP13:%.*]] = bitcast float [[TMP12]] to i32
-; UNLIM-NEXT: br label [[TMP15]]
-; UNLIM: 14:
-; UNLIM-NEXT: unreachable
-; UNLIM: 15:
-; UNLIM-NEXT: [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP5]] ], [ [[CALL3]], [[TMP8]] ], [ [[TMP13]], [[TMP10]] ]
-; UNLIM-NEXT: ret i32 [[CALL_PHI]]
-;
-; LIMI2-LABEL: @non_matching_fp1(
-; LIMI2-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
-; LIMI2-NEXT: [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr @void
-; LIMI2-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
-; LIMI2-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
-; LIMI2-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; LIMI2: 2:
-; LIMI2-NEXT: [[CALL1:%.*]] = call i32 @takeI32(i32 42)
-; LIMI2-NEXT: br label [[TMP7:%.*]]
-; LIMI2: 3:
-; LIMI2-NEXT: [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
-; LIMI2-NEXT: br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
-; LIMI2: 5:
-; LIMI2-NEXT: [[CALL2:%.*]] = call i32 @retI32(i32 42)
-; LIMI2-NEXT: br label [[TMP7]]
-; LIMI2: 6:
-; LIMI2-NEXT: [[CALL3:%.*]] = call i32 [[FP]](i32 42), !callees [[META0:![0-9]+]]
-; LIMI2-NEXT: br label [[TMP7]]
-; LIMI2: 7:
-; LIMI2-NEXT: [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP5]] ], [ [[CALL3]], [[TMP6]] ]
-; LIMI2-NEXT: ret i32 [[CALL_PHI]]
+; UPTO2-LABEL: @non_matching_fp1(
+; UPTO2-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; UPTO2-NEXT: [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr @void
+; UPTO2-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; UPTO2-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @retFloatTakeFloat
+; UPTO2-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP6:%.*]]
+; UPTO2: 2:
+; UPTO2-NEXT: [[TMP3:%.*]] = bitcast i32 42 to float
+; UPTO2-NEXT: [[TMP4:%.*]] = call float @retFloatTakeFloat(float [[TMP3]])
+; UPTO2-NEXT: [[TMP5:%.*]] = bitcast float [[TMP4]] to i32
+; UPTO2-NEXT: br label [[TMP7:%.*]]
+; UPTO2: 6:
+; UPTO2-NEXT: [[CALL1:%.*]] = call i32 [[FP]](i32 42), !callees [[META0:![0-9]+]]
+; UPTO2-NEXT: br label [[TMP7]]
+; UPTO2: 7:
+; UPTO2-NEXT: [[CALL_PHI:%.*]] = phi i32 [ [[TMP5]], [[TMP2]] ], [ [[CALL1]], [[TMP6]] ]
+; UPTO2-NEXT: ret i32 [[CALL_PHI]]
;
; LIMI0-LABEL: @non_matching_fp1(
; LIMI0-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
@@ -199,53 +161,12 @@ define i32 @non_matching_fp1(i1 %c1, i1 %c2, i1 %c) {
}
define i32 @non_matching_fp1_noundef(i1 %c1, i1 %c2, i1 %c) {
-; UNLIM-LABEL: @non_matching_fp1_noundef(
-; UNLIM-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
-; UNLIM-NEXT: [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloatFloatNoundef, ptr @void
-; UNLIM-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
-; UNLIM-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
-; UNLIM-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; UNLIM: 2:
-; UNLIM-NEXT: [[CALL1:%.*]] = call i32 @takeI32(i32 42)
-; UNLIM-NEXT: br label [[TMP9:%.*]]
-; UNLIM: 3:
-; UNLIM-NEXT: [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
-; UNLIM-NEXT: br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
-; UNLIM: 5:
-; UNLIM-NEXT: [[CALL2:%.*]] = call i32 @retI32(i32 42)
-; UNLIM-NEXT: br label [[TMP9]]
-; UNLIM: 6:
-; UNLIM-NEXT: br i1 true, label [[TMP7:%.*]], label [[TMP8:%.*]]
-; UNLIM: 7:
-; UNLIM-NEXT: [[CALL3:%.*]] = call i32 @void(i32 42)
-; UNLIM-NEXT: br label [[TMP9]]
-; UNLIM: 8:
-; UNLIM-NEXT: unreachable
-; UNLIM: 9:
-; UNLIM-NEXT: [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP5]] ], [ [[CALL3]], [[TMP7]] ]
-; UNLIM-NEXT: ret i32 [[CALL_PHI]]
-;
-; LIMI2-LABEL: @non_matching_fp1_noundef(
-; LIMI2-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
-; LIMI2-NEXT: [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloatFloatNoundef, ptr @void
-; LIMI2-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
-; LIMI2-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
-; LIMI2-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; LIMI2: 2:
-; LIMI2-NEXT: [[CALL1:%.*]] = call i32 @takeI32(i32 42)
-; LIMI2-NEXT: br label [[TMP7:%.*]]
-; LIMI2: 3:
-; LIMI2-NEXT: [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
-; LIMI2-NEXT: br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
-; LIMI2: 5:
-; LIMI2-NEXT: [[CALL2:%.*]] = call i32 @retI32(i32 42)
-; LIMI2-NEXT: br label [[TMP7]]
-; LIMI2: 6:
-; LIMI2-NEXT: [[CALL3:%.*]] = call i32 [[FP]](i32 42), !callees [[META1:![0-9]+]]
-; LIMI2-NEXT: br label [[TMP7]]
-; LIMI2: 7:
-; LIMI2-NEXT: [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP5]] ], [ [[CALL3]], [[TMP6]] ]
-; LIMI2-NEXT: ret i32 [[CALL_PHI]]
+; UPTO2-LABEL: @non_matching_fp1_noundef(
+; UPTO2-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; UPTO2-NEXT: [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloatFloatNoundef, ptr @void
+; UPTO2-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; UPTO2-NEXT: [[CALL:%.*]] = call i32 [[FP]](i32 42), !callees [[META0]]
+; UPTO2-NEXT: ret i32 [[CALL]]
;
; LIMI0-LABEL: @non_matching_fp1_noundef(
; LIMI0-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
@@ -262,108 +183,38 @@ define i32 @non_matching_fp1_noundef(i1 %c1, i1 %c2, i1 %c) {
}
define void @non_matching_fp2(i1 %c1, i1 %c2, i1 %c, ptr %unknown) {
-; OUNLM-LABEL: @non_matching_fp2(
-; OUNLM-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
-; OUNLM-NEXT: [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr [[UNKNOWN:%.*]]
-; OUNLM-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
-; OUNLM-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
-; OUNLM-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; OUNLM: 2:
-; OUNLM-NEXT: call void @takeI32()
-; OUNLM-NEXT: br label [[TMP10:%.*]]
-; OUNLM: 3:
-; OUNLM-NEXT: [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
-; OUNLM-NEXT: br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
-; OUNLM: 5:
-; OUNLM-NEXT: call void @retI32()
-; OUNLM-NEXT: br label [[TMP10]]
-; OUNLM: 6:
-; OUNLM-NEXT: [[TMP7:%.*]] = icmp eq ptr [[FP]], @retFloatTakeFloat
-; OUNLM-NEXT: br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP9:%.*]]
-; OUNLM: 8:
-; OUNLM-NEXT: call void @retFloatTakeFloat()
-; OUNLM-NEXT: br label [[TMP10]]
-; OUNLM: 9:
-; OUNLM-NEXT: call void [[FP]]()
-; OUNLM-NEXT: br label [[TMP10]]
-; OUNLM: 10:
-; OUNLM-NEXT: ret void
-;
-; LIMI2-LABEL: @non_matching_fp2(
-; LIMI2-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
-; LIMI2-NEXT: [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr [[UNKNOWN:%.*]]
-; LIMI2-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
-; LIMI2-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
-; LIMI2-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; LIMI2: 2:
-; LIMI2-NEXT: call void @takeI32()
-; LIMI2-NEXT: br label [[TMP7:%.*]]
-; LIMI2: 3:
-; LIMI2-NEXT: [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
-; LIMI2-NEXT: br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
-; LIMI2: 5:
-; LIMI2-NEXT: call void @retI32()
-; LIMI2-NEXT: br label [[TMP7]]
-; LIMI2: 6:
-; LIMI2-NEXT: call void [[FP]]()
-; LIMI2-NEXT: br label [[TMP7]]
-; LIMI2: 7:
-; LIMI2-NEXT: ret void
-;
-; LIMI0-LABEL: @non_matching_fp2(
-; LIMI0-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
-; LIMI0-NEXT: [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr [[UNKNOWN:%.*]]
-; LIMI0-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
-; LIMI0-NEXT: call void [[FP]]()
-; LIMI0-NEXT: ret void
+; OWRDL-LABEL: @non_matching_fp2(
+; OWRDL-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; OWRDL-NEXT: [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr [[UNKNOWN:%.*]]
+; OWRDL-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; OWRDL-NEXT: call void [[FP]]()
+; OWRDL-NEXT: ret void
;
; CWRLD-LABEL: @non_matching_fp2(
; CWRLD-NEXT: [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
; CWRLD-NEXT: [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr [[UNKNOWN:%.*]]
; CWRLD-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
-; CWRLD-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
+; CWRLD-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @func3
; CWRLD-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
; CWRLD: 2:
-; CWRLD-NEXT: call void @takeI32()
+; CWRLD-NEXT: call void @func3()
; CWRLD-NEXT: br label [[TMP21:%.*]]
; CWRLD: 3:
-; CWRLD-NEXT: [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
+; CWRLD-NEXT: [[TMP4:%.*]] = icmp eq ptr [[FP]], @func4
; CWRLD-NEXT: br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
; CWRLD: 5:
-; CWRLD-NEXT: call void @retI32()
+; CWRLD-NEXT: call void @func4()
; CWRLD-NEXT: br label [[TMP21]]
; CWRLD: 6:
-; CWRLD-NEXT: [[TMP7:%.*]] = icmp eq ptr [[FP]], @func3
+; CWRLD-NEXT: [[TMP7:%.*]] = icmp eq ptr [[FP]], @void
; CWRLD-NEXT: br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP9:%.*]]
; CWRLD: 8:
-; CWRLD-NEXT: call void @func3()
+; CWRLD-NEXT: call void @void()
; CWRLD-NEXT: br label [[TMP21]]
; CWRLD: 9:
-; CWRLD-NEXT: [[TMP10:%.*]] = icmp eq ptr [[FP]], @func4
-; CWRLD-NEXT: br i1 [[TMP10]], label [[TMP11:%.*]], label [[TMP12:%.*]]
-; CWRLD: 11:
-; CWRLD-NEXT: call void @func4()
+; CWRLD-NEXT: call void [[FP]](), !callees [[META1:![0-9]+]]
; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 12:
-; CWRLD-NEXT: [[TMP13:%.*]] = icmp eq ptr [[FP]], @retFloatTakeFloat
-; CWRLD-NEXT: br i1 [[TMP13]], label [[TMP14:%.*]], label [[TMP15:%.*]]
-; CWRLD: 14:
-; CWRLD-NEXT: call void @retFloatTakeFloat()
-; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 15:
-; CWRLD-NEXT: [[TMP16:%.*]] = icmp eq ptr [[FP]], @retFloatTakeFloatFloatNoundef
-; CWRLD-NEXT: br i1 [[TMP16]], label [[TMP17:%.*]], label [[TMP18:%.*]]
-; CWRLD: 17:
-; CWRLD-NEXT: call void @retFloatTakeFloatFloatNoundef()
-; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 18:
-; CWRLD-NEXT: br i1 true, label [[TMP19:%.*]], label [[TMP20:%.*]]
-; CWRLD: 19:
-; CWRLD-NEXT: call void @void()
-; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 20:
-; CWRLD-NEXT: unreachable
-; CWRLD: 21:
+; CWRLD: 10:
; CWRLD-NEXT: ret void
;
%fp1 = select i1 %c1, ptr @retI32, ptr @takeI32
@@ -374,87 +225,25 @@ define void @non_matching_fp2(i1 %c1, i1 %c2, i1 %c, ptr %unknown) {
}
define i32 @non_matching_unknown(i1 %c, ptr %fn) {
-; OUNLM-LABEL: @non_matching_unknown(
-; OUNLM-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr @retI32, ptr [[FN:%.*]]
-; OUNLM-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @retI32
-; OUNLM-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; OUNLM: 2:
-; OUNLM-NEXT: [[CALL1:%.*]] = call i32 @retI32(i32 42)
-; OUNLM-NEXT: br label [[TMP4:%.*]]
-; OUNLM: 3:
-; OUNLM-NEXT: [[CALL2:%.*]] = call i32 [[FP]](i32 42)
-; OUNLM-NEXT: br label [[TMP4]]
-; OUNLM: 4:
-; OUNLM-NEXT: [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP3]] ]
-; OUNLM-NEXT: ret i32 [[CALL_PHI]]
-;
-; LIMI2-LABEL: @non_matching_unknown(
-; LIMI2-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr @retI32, ptr [[FN:%.*]]
-; LIMI2-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @retI32
-; LIMI2-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; LIMI2: 2:
-; LIMI2-NEXT: [[CALL1:%.*]] = call i32 @retI32(i32 42)
-; LIMI2-NEXT: br label [[TMP4:%.*]]
-; LIMI2: 3:
-; LIMI2-NEXT: [[CALL2:%.*]] = call i32 [[FP]](i32 42)
-; LIMI2-NEXT: br label [[TMP4]]
-; LIMI2: 4:
-; LIMI2-NEXT: [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP3]] ]
-; LIMI2-NEXT: ret i32 [[CALL_PHI]]
-;
-; LIMI0-LABEL: @non_matching_unknown(
-; LIMI0-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr @retI32, ptr [[FN:%.*]]
-; LIMI0-NEXT: [[CALL:%.*]] = call i32 [[FP]](i32 42)
-; LIMI0-NEXT: ret i32 [[CALL]]
+; OWRDL-LABEL: @non_matching_unknown(
+; OWRDL-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr @retI32, ptr [[FN:%.*]]
+; OWRDL-NEXT: [[CALL:%.*]] = call i32 [[FP]](i32 42)
+; OWRDL-NEXT: ret i32 [[CALL]]
;
; CWRLD-LABEL: @non_matching_unknown(
; CWRLD-NEXT: [[FP:%.*]] = select i1 [[C:%.*]], ptr @retI32, ptr [[FN:%.*]]
-; CWRLD-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @func3
-; CWRLD-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; CWRLD: 2:
-; CWRLD-NEXT: [[CALL1:%.*]] = call i32 @func3(i32 42)
-; CWRLD-NEXT: br label [[TMP24:%.*]]
-; CWRLD: 3:
-; CWRLD-NEXT: [[TMP4:%.*]] = icmp eq ptr [[FP]], @func4
-; CWRLD-NEXT: br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
-; CWRLD: 5:
-; CWRLD-NEXT: [[CALL2:%.*]] = call i32 @func4(i32 42)
-; CWRLD-NEXT: br label [[TMP24]]
-; CWRLD: 6:
-; CWRLD-NEXT: [[TMP7:%.*]] = icmp eq ptr [[FP]], @retI32
-; CWRLD-NEXT: br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP9:%.*]]
-; CWRLD: 8:
-; CWRLD-NEXT: [[CALL3:%.*]] = call i32 @retI32(i32 42)
-; CWRLD-NEXT: br label [[TMP24]]
-; CWRLD: 9:
-; CWRLD-NEXT: [[TMP10:%.*]] = icmp eq ptr [[FP]], @takeI32
-; CWRLD-NEXT: br i1 [[TMP10]], label [[TMP11:%.*]], label [[TMP12:%.*]]
-; CWRLD: 11:
-; CWRLD-NEXT: [[CALL4:%.*]] = call i32 @takeI32(i32 42)
-; CWRLD-NEXT: br label [[TMP24]]
-; CWRLD: 12:
; CWRLD-NEXT: [[TMP13:%.*]] = icmp eq ptr [[FP]], @retFloatTakeFloat
; CWRLD-NEXT: br i1 [[TMP13]], label [[TMP14:%.*]], label [[TMP18:%.*]]
-; CWRLD: 14:
+; CWRLD: 2:
; CWRLD-NEXT: [[TMP15:%.*]] = bitcast i32 42 to float
; CWRLD-NEXT: [[TMP16:%.*]] = call float @retFloatTakeFloat(float [[TMP15]])
; CWRLD-NEXT: [[TMP17:%.*]] = bitcast float [[TMP16]] to i32
+; CWRLD-NEXT: br label [[TMP24:%.*]]
+; CWRLD: 6:
+; CWRLD-NEXT: [[CALL1:%.*]] = call i32 [[FP]](i32 42), !callees [[META2:![0-9]+]]
; CWRLD-NEXT: br label [[TMP24]]
-; CWRLD: 18:
-; CWRLD-NEXT: [[TMP19:%.*]] = icmp eq ptr [[FP]], @retFloatTakeFloatFloatNoundef
-; CWRLD-NEXT: br i1 [[TMP19]], label [[TMP20:%.*]], label [[TMP21:%.*]]
-; CWRLD: 20:
-; CWRLD-NEXT: [[CALL5:%.*]] = call i32 @retFloatTakeFloatFloatNoundef(i32 42)
-; CWRLD-NEXT: br label [[TMP24]]
-; CWRLD: 21:
-; CWRLD-NEXT: br i1 true, label [[TMP22:%.*]], label [[TMP23:%.*]]
-; CWRLD: 22:
-; CWRLD-NEXT: [[CALL6:%.*]] = call i32 @void(i32 42)
-; CWRLD-NEXT: br label [[TMP24]]
-; CWRLD: 23:
-; CWRLD-NEXT: unreachable
-; CWRLD: 24:
-; CWRLD-NEXT: [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP5]] ], [ [[CALL3]], [[TMP8]] ], [ [[CALL4]], [[TMP11]] ], [ [[TMP17]], [[TMP14]] ], [ [[CALL5]], [[TMP20]] ], [ [[CALL6]], [[TMP22]] ]
+; CWRLD: 7:
+; CWRLD-NEXT: [[CALL_PHI:%.*]] = phi i32 [ [[TMP17]], [[TMP14]] ], [ [[CALL1]], [[TMP18]] ]
; CWRLD-NEXT: ret i32 [[CALL_PHI]]
;
%fp = select i1 %c, ptr @retI32, ptr %fn
@@ -501,37 +290,15 @@ define void @broker(ptr %unknown) !callback !0 {
; CWRLD-NEXT: call void @func4()
; CWRLD-NEXT: br label [[TMP21]]
; CWRLD: 6:
-; CWRLD-NEXT: [[TMP7:%.*]] = icmp eq ptr [[UNKNOWN]], @retI32
+; CWRLD-NEXT: [[TMP7:%.*]] = icmp eq ptr [[UNKNOWN]], @void
; CWRLD-NEXT: br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP9:%.*]]
; CWRLD: 8:
-; CWRLD-NEXT: call void @retI32()
+; CWRLD-NEXT: call void @void()
; CWRLD-NEXT: br label [[TMP21]]
; CWRLD: 9:
-; CWRLD-NEXT: [[TMP10:%.*]] = icmp eq ptr [[UNKNOWN]], @takeI32
-; CWRLD-NEXT: br i1 [[TMP10]], label [[TMP11:%.*]], label [[TMP12:%.*]]
-; CWRLD: 11:
-; CWRLD-NEXT: call void @takeI32()
-; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 12:
-; CWRLD-NEXT: [[TMP13:%.*]] = icmp eq ptr [[UNKNOWN]], @retFloatTakeFloat
-; CWRLD-NEXT: br i1 [[TMP13]], label [[TMP14:%.*]], label [[TMP15:%.*]]
-; CWRLD: 14:
-; CWRLD-NEXT: call void @retFloatTakeFloat()
+; CWRLD-NEXT: call void [[UNKNOWN]](), !callees [[META5:![0-9]+]]
; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 15:
-; CWRLD-NEXT: [[TMP16:%.*]] = icmp eq ptr [[UNKNOWN]], @retFloatTakeFloatFloatNoundef
-; CWRLD-NEXT: br i1 [[TMP16]], label [[TMP17:%.*]], label [[TMP18:%.*]]
-; CWRLD: 17:
-; CWRLD-NEXT: call void @retFloatTakeFloatFloatNoundef()
-; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 18:
-; CWRLD-NEXT: br i1 true, label [[TMP19:%.*]], label [[TMP20:%.*]]
-; CWRLD: 19:
-; CWRLD-NEXT: call void @void()
-; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 20:
-; CWRLD-NEXT: unreachable
-; CWRLD: 21:
+; CWRLD: 10:
; CWRLD-NEXT: ret void
;
call void %unknown()
@@ -576,14 +343,14 @@ define void @func7(ptr %unknown) {
; Check there's no crash if something that isn't a function appears in !callees
define void @undef_in_callees() {
-; UNLIM-LABEL: @undef_in_callees(
-; UNLIM-NEXT: cond.end.i:
-; UNLIM-NEXT: call void undef(ptr undef, i32 undef, ptr undef), !callees [[META2:![0-9]+]]
-; UNLIM-NEXT: ret void
+; OUNLM-LABEL: @undef_in_callees(
+; OUNLM-NEXT: cond.end.i:
+; OUNLM-NEXT: call void undef(ptr undef, i32 undef, ptr undef), !callees [[META3:![0-9]+]]
+; OUNLM-NEXT: ret void
;
; LIMI2-LABEL: @undef_in_callees(
; LIMI2-NEXT: cond.end.i:
-; LIMI2-NEXT: call void undef(ptr undef, i32 undef, ptr undef), !callees [[META4:![0-9]+]]
+; LIMI2-NEXT: call void undef(ptr undef, i32 undef, ptr undef), !callees [[META3:![0-9]+]]
; LIMI2-NEXT: ret void
;
; LIMI0-LABEL: @undef_in_callees(
@@ -591,6 +358,11 @@ define void @undef_in_callees() {
; LIMI0-NEXT: call void undef(ptr undef, i32 undef, ptr undef), !callees [[META6:![0-9]+]]
; LIMI0-NEXT: ret void
;
+; CWRLD-LABEL: @undef_in_callees(
+; CWRLD-NEXT: cond.end.i:
+; CWRLD-NEXT: call void undef(ptr undef, i32 undef, ptr undef), !callees [[META6:![0-9]+]]
+; CWRLD-NEXT: ret void
+;
cond.end.i:
call void undef(ptr undef, i32 undef, ptr undef), !callees !3
ret void
@@ -617,37 +389,15 @@ define void @as_cast(ptr %arg) {
; CWRLD-NEXT: tail call void @func4()
; CWRLD-NEXT: br label [[TMP21]]
; CWRLD: 6:
-; CWRLD-NEXT: [[TMP7:%.*]] = icmp eq ptr [[FP_AS0]], @retI32
+; CWRLD-NEXT: [[TMP7:%.*]] = icmp eq ptr [[FP_AS0]], @void
; CWRLD-NEXT: br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP9:%.*]]
; CWRLD: 8:
-; CWRLD-NEXT: call void @retI32()
+; CWRLD-NEXT: tail call void @void()
; CWRLD-NEXT: br label [[TMP21]]
; CWRLD: 9:
-; CWRLD-NEXT: [[TMP10:%.*]] = icmp eq ptr [[FP_AS0]], @takeI32
-; CWRLD-NEXT: br i1 [[TMP10]], label [[TMP11:%.*]], label [[TMP12:%.*]]
-; CWRLD: 11:
-; CWRLD-NEXT: call void @takeI32()
-; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 12:
-; CWRLD-NEXT: [[TMP13:%.*]] = icmp eq ptr [[FP_AS0]], @retFloatTakeFloat
-; CWRLD-NEXT: br i1 [[TMP13]], label [[TMP14:%.*]], label [[TMP15:%.*]]
-; CWRLD: 14:
-; CWRLD-NEXT: call void @retFloatTakeFloat()
-; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 15:
-; CWRLD-NEXT: [[TMP16:%.*]] = icmp eq ptr [[FP_AS0]], @retFloatTakeFloatFloatNoundef
-; CWRLD-NEXT: br i1 [[TMP16]], label [[TMP17:%.*]], label [[TMP18:%.*]]
-; CWRLD: 17:
-; CWRLD-NEXT: call void @retFloatTakeFloatFloatNoundef()
-; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 18:
-; CWRLD-NEXT: br i1 true, label [[TMP19:%.*]], label [[TMP20:%.*]]
-; CWRLD: 19:
-; CWRLD-NEXT: tail call void @void()
+; CWRLD-NEXT: tail call addrspace(1) void [[FP]](), !callees [[META5]]
; CWRLD-NEXT: br label [[TMP21]]
-; CWRLD: 20:
-; CWRLD-NEXT: unreachable
-; CWRLD: 21:
+; CWRLD: 10:
; CWRLD-NEXT: ret void
;
%fp = load ptr addrspace(1), ptr %arg, align 8
@@ -697,15 +447,15 @@ define void @as_cast(ptr %arg) {
;.
; CWRLD: attributes #[[ATTR0:[0-9]+]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
;.
-; OUNLM: [[META0:![0-9]+]] = !{[[META1:![0-9]+]]}
-; OUNLM: [[META1]] = !{i64 0, i1 false}
-; OUNLM: [[META2]] = distinct !{ptr undef, ptr null}
+; OUNLM: [[META0]] = !{ptr @takeI32, ptr @retI32, ptr @void}
+; OUNLM: [[META1:![0-9]+]] = !{[[META2:![0-9]+]]}
+; OUNLM: [[META2]] = !{i64 0, i1 false}
+; OUNLM: [[META3]] = distinct !{ptr undef, ptr null}
;.
-; LIMI2: [[META0]] = !{ptr @void, ptr @retFloatTakeFloat}
-; LIMI2: [[META1]] = !{ptr @void}
-; LIMI2: [[META2:![0-9]+]] = !{[[META3:![0-9]+]]}
-; LIMI2: [[META3]] = !{i64 0, i1 false}
-; LIMI2: [[META4]] = distinct !{ptr undef, ptr null}
+; LIMI2: [[META0]] = !{ptr @takeI32, ptr @retI32, ptr @void}
+; LIMI2: [[META1:![0-9]+]] = !{[[META2:![0-9]+]]}
+; LIMI2: [[META2]] = !{i64 0, i1 false}
+; LIMI2: [[META3]] = distinct !{ptr undef, ptr null}
;.
; LIMI0: [[META0]] = !{ptr @func4, ptr @internal_good}
; LIMI0: [[META1]] = !{ptr @func3, ptr @func4}
@@ -715,9 +465,14 @@ define void @as_cast(ptr %arg) {
; LIMI0: [[META5]] = !{i64 0, i1 false}
; LIMI0: [[META6]] = distinct !{ptr undef, ptr null}
;.
-; CWRLD: [[META0:![0-9]+]] = !{[[META1:![0-9]+]]}
-; CWRLD: [[META1]] = !{i64 0, i1 false}
-; CWRLD: [[META2]] = distinct !{ptr undef, ptr null}
+; CWRLD: [[META0]] = !{ptr @takeI32, ptr @retI32, ptr @void}
+; CWRLD: [[META1]] = !{ptr @takeI32, ptr @retI32, ptr @retFloatTakeFloat, ptr @retFloatTakeFloatFloatNoundef}
+; CWRLD: [[META2]] = !{ptr @func3, ptr @func4, ptr @retI32, ptr @takeI32, ptr @retFloatTakeFloatFloatNoundef, ptr @void}
+; CWRLD: [[META3:![0-9]+]] = !{[[META4:![0-9]+]]}
+; CWRLD: [[META4]] = !{i64 0, i1 false}
+; CWRLD: [[META5]] = !{ptr @retI32, ptr @takeI32, ptr @retFloatTakeFloat, ptr @retFloatTakeFloatFloatNoundef}
+; CWRLD: [[META6]] = distinct !{ptr undef, ptr null}
;.
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; DOT: {{.*}}
+; UNLIM: {{.*}}
diff --git a/llvm/test/Transforms/Attributor/indirect_call_signature_mismatch.ll b/llvm/test/Transforms/Attributor/indirect_call_signature_mismatch.ll
new file mode 100644
index 0000000000000..b25978bdafb95
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/indirect_call_signature_mismatch.ll
@@ -0,0 +1,57 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals all --version 6
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
+
+; The call site passes one argument. @one_param can be promoted to and is
+; specialized for; @two_params cannot, and is left to the indirect call rather
+; than called directly with an argument list that is not its own.
+
+define internal void @one_param(i32 %a) {
+; CHECK-LABEL: define internal void @one_param(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT: call void @use(i32 [[A]])
+; CHECK-NEXT: ret void
+;
+ call void @use(i32 %a)
+ ret void
+}
+
+define internal void @two_params(i32 %a, i32 %b) {
+; CHECK-LABEL: define internal void @two_params(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: [[SUM:%.*]] = add i32 [[A]], [[B]]
+; CHECK-NEXT: call void @use(i32 [[SUM]])
+; CHECK-NEXT: ret void
+;
+ %sum = add i32 %a, %b
+ call void @use(i32 %sum)
+ ret void
+}
+
+declare void @use(i32)
+
+define void @caller(i1 %c) {
+; CHECK-LABEL: define void @caller(
+; CHECK-SAME: i1 [[C:%.*]]) {
+; CHECK-NEXT: [[FP:%.*]] = select i1 [[C]], ptr @two_params, ptr @one_param
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @one_param
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB3:.*]]
+; CHECK: [[BB2]]:
+; CHECK-NEXT: call void @one_param(i32 1)
+; CHECK-NEXT: br label %[[BB4:.*]]
+; CHECK: [[BB3]]:
+; CHECK-NEXT: call void [[FP]](i32 1), !callees [[META0:![0-9]+]]
+; CHECK-NEXT: br label %[[BB4]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: ret void
+;
+ %fp = select i1 %c, ptr @two_params, ptr @one_param
+ call void %fp(i32 1)
+ ret void
+}
+;.
+; CHECK: [[META0]] = !{ptr @two_params}
+;.
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CGSCC: {{.*}}
+; TUNIT: {{.*}}
diff --git a/llvm/test/Transforms/Attributor/nounwind.ll b/llvm/test/Transforms/Attributor/nounwind.ll
index 8ea812ce42a83..8c096855fa16c 100644
--- a/llvm/test/Transforms/Attributor/nounwind.ll
+++ b/llvm/test/Transforms/Attributor/nounwind.ll
@@ -160,19 +160,7 @@ define void @two_potential_callees_pos1(i1 %c) {
; CGSCC-LABEL: define {{[^@]+}}@two_potential_callees_pos1
; CGSCC-SAME: (i1 [[C:%.*]]) #[[ATTR2:[0-9]+]] {
; CGSCC-NEXT: [[FP:%.*]] = select i1 [[C]], ptr @foo1, ptr @scc1_foo
-; CGSCC-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @scc1_foo
-; CGSCC-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; CGSCC: 2:
-; CGSCC-NEXT: call void @scc1_foo()
-; CGSCC-NEXT: br label [[TMP6:%.*]]
-; CGSCC: 3:
-; CGSCC-NEXT: br i1 true, label [[TMP4:%.*]], label [[TMP5:%.*]]
-; CGSCC: 4:
-; CGSCC-NEXT: call void @foo1()
-; CGSCC-NEXT: br label [[TMP6]]
-; CGSCC: 5:
-; CGSCC-NEXT: unreachable
-; CGSCC: 6:
+; CGSCC-NEXT: call void [[FP]]() #[[ATTR3:[0-9]+]], !callees [[META0:![0-9]+]]
; CGSCC-NEXT: ret void
;
%fp = select i1 %c, ptr @foo1, ptr @scc1_foo
@@ -180,47 +168,52 @@ define void @two_potential_callees_pos1(i1 %c) {
ret void
}
define void @two_potential_callees_pos2(i1 %c) {
-; CHECK: Function Attrs: nounwind
-; CHECK-LABEL: define {{[^@]+}}@two_potential_callees_pos2
-; CHECK-SAME: (i1 [[C:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT: [[FP:%.*]] = select i1 [[C]], ptr @foo2, ptr @scc1_foo
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @scc1_foo
-; CHECK-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; CHECK: 2:
-; CHECK-NEXT: call void @scc1_foo()
-; CHECK-NEXT: br label [[TMP6:%.*]]
-; CHECK: 3:
-; CHECK-NEXT: br i1 true, label [[TMP4:%.*]], label [[TMP5:%.*]]
-; CHECK: 4:
-; CHECK-NEXT: call void @foo2()
-; CHECK-NEXT: br label [[TMP6]]
-; CHECK: 5:
-; CHECK-NEXT: unreachable
-; CHECK: 6:
-; CHECK-NEXT: ret void
+; TUNIT: Function Attrs: nounwind
+; TUNIT-LABEL: define {{[^@]+}}@two_potential_callees_pos2
+; TUNIT-SAME: (i1 [[C:%.*]]) #[[ATTR1]] {
+; TUNIT-NEXT: [[FP:%.*]] = select i1 [[C]], ptr @foo2, ptr @scc1_foo
+; TUNIT-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @foo2
+; TUNIT-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; TUNIT: 2:
+; TUNIT-NEXT: call void @foo2()
+; TUNIT-NEXT: br label [[TMP4:%.*]]
+; TUNIT: 3:
+; TUNIT-NEXT: call void [[FP]](), !callees [[META0:![0-9]+]]
+; TUNIT-NEXT: br label [[TMP4]]
+; TUNIT: 4:
+; TUNIT-NEXT: ret void
+;
+; CGSCC: Function Attrs: nounwind
+; CGSCC-LABEL: define {{[^@]+}}@two_potential_callees_pos2
+; CGSCC-SAME: (i1 [[C:%.*]]) #[[ATTR1]] {
+; CGSCC-NEXT: [[FP:%.*]] = select i1 [[C]], ptr @foo2, ptr @scc1_foo
+; CGSCC-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @foo2
+; CGSCC-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; CGSCC: 2:
+; CGSCC-NEXT: call void @foo2()
+; CGSCC-NEXT: br label [[TMP4:%.*]]
+; CGSCC: 3:
+; CGSCC-NEXT: call void [[FP]](), !callees [[META1:![0-9]+]]
+; CGSCC-NEXT: br label [[TMP4]]
+; CGSCC: 4:
+; CGSCC-NEXT: ret void
;
%fp = select i1 %c, ptr @foo2, ptr @scc1_foo
call void %fp()
ret void
}
define void @two_potential_callees_neg(i1 %c) {
-; CHECK-LABEL: define {{[^@]+}}@two_potential_callees_neg
-; CHECK-SAME: (i1 [[C:%.*]]) {
-; CHECK-NEXT: [[FP:%.*]] = select i1 [[C]], ptr @foo1, ptr @non_nounwind
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FP]], @non_nounwind
-; CHECK-NEXT: br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; CHECK: 2:
-; CHECK-NEXT: call void @non_nounwind()
-; CHECK-NEXT: br label [[TMP6:%.*]]
-; CHECK: 3:
-; CHECK-NEXT: br i1 true, label [[TMP4:%.*]], label [[TMP5:%.*]]
-; CHECK: 4:
-; CHECK-NEXT: call void @foo1()
-; CHECK-NEXT: br label [[TMP6]]
-; CHECK: 5:
-; CHECK-NEXT: unreachable
-; CHECK: 6:
-; CHECK-NEXT: ret void
+; TUNIT-LABEL: define {{[^@]+}}@two_potential_callees_neg
+; TUNIT-SAME: (i1 [[C:%.*]]) {
+; TUNIT-NEXT: [[FP:%.*]] = select i1 [[C]], ptr @foo1, ptr @non_nounwind
+; TUNIT-NEXT: call void [[FP]](), !callees [[META1:![0-9]+]]
+; TUNIT-NEXT: ret void
+;
+; CGSCC-LABEL: define {{[^@]+}}@two_potential_callees_neg
+; CGSCC-SAME: (i1 [[C:%.*]]) {
+; CGSCC-NEXT: [[FP:%.*]] = select i1 [[C]], ptr @foo1, ptr @non_nounwind
+; CGSCC-NEXT: call void [[FP]](), !callees [[META2:![0-9]+]]
+; CGSCC-NEXT: ret void
;
%fp = select i1 %c, ptr @foo1, ptr @non_nounwind
call void %fp()
@@ -240,4 +233,12 @@ declare void @__cxa_end_catch()
; CGSCC: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
; CGSCC: attributes #[[ATTR1]] = { nounwind }
; CGSCC: attributes #[[ATTR2]] = { mustprogress nofree nosync nounwind willreturn }
+; CGSCC: attributes #[[ATTR3]] = { nofree nosync nounwind willreturn }
+;.
+; TUNIT: [[META0]] = !{ptr @scc1_foo}
+; TUNIT: [[META1]] = !{ptr @non_nounwind, ptr @foo1}
+;.
+; CGSCC: [[META0]] = !{ptr @scc1_foo, ptr @foo1}
+; CGSCC: [[META1]] = !{ptr @scc1_foo}
+; CGSCC: [[META2]] = !{ptr @non_nounwind, ptr @foo1}
;.
>From 028e7833e5e229c9148b90253f2f867bdd54e8f0 Mon Sep 17 00:00:00 2001
From: Larry Meadows <Lawrence.Meadows at amd.com>
Date: Tue, 18 Aug 2026 11:50:26 -0500
Subject: [PATCH 2/3] [Attributor] Don't specialize an indirect call for a
callee that reaches it
Specializing an indirect call for a callee that can reach the function holding
the call site turns an edge that existed only through the function pointer into
a direct one, and nothing downstream undoes it.
That is enough to defeat alwaysinline. A dispatcher marked alwaysinline is
written that way so every caller gets its own copy and folds the dispatch away;
once it sits in a call graph cycle it cannot be inlined and survives to code
generation as a recursive function. On a target without a dynamically sized
stack there is then no stack size to state for it: the AMDGPU kernel descriptor
carries one fixed per-lane scratch size, so the recursion runs off the end of
its scratch slice. In the OpenMP device runtime, where __kmpc_parallel_60
dispatches to outlined regions and is alwaysinline for exactly this reason,
nested parallel regions fault with
HSA_STATUS_ERROR_MEMORY_APERTURE_VIOLATION: The agent attempted to access
memory beyond the largest legal address.
So ask, before specializing for a callee, whether that callee can reach the
function holding the call site, and leave the ones that can to the indirect
call, which the !callees metadata already names.
Ask it in updateImpl and record the answer in the AA's state: queries made
during manifestation are forced to pessimistic fixpoints and cannot register a
new dependency.
The walk is over AACallEdges' optimistic edges, which include callback edges and
edges the solver resolved that no user list in the IR spells out.
AAInterFnReachability answers a broader question -- whether control can get from
one function to another at all, which is conservatively true for everything
downstream of an unknown callee -- and using it here suppressed specialization
far past the cycles this is about: @foo2 in nounwind.ll calls a declaration, so
it "reaches" every function in the module. Only an edge the call graph agrees is
direct can produce the recursion the inliner and the backend have to live with.
Nothing in tree covered this, so indirect_call_cycle.ll is new. Its second half
is a cycle whose only path back to the dispatcher is a callback edge, which is
the case an IR walk over direct callers misses and the solver's edges do not.
Co-authored-by: Cursor <cursoragent at cursor.com>
---
.../Transforms/IPO/AttributorAttributes.cpp | 59 +++++++-
.../Attributor/indirect_call_cycle.ll | 134 ++++++++++++++++++
2 files changed, 190 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/Transforms/Attributor/indirect_call_cycle.ll
diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 484d163d3c01b..1d43a479599a1 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -12495,11 +12495,51 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
AllCalleesKnownNow = false;
}
+ // A direct call to a callee that can reach the function holding this call
+ // site closes a call graph cycle that previously existed only through the
+ // function pointer, and nothing downstream undoes that: an alwaysinline
+ // dispatcher stops being inlinable once it is in a cycle, and on a target
+ // without a dynamically sized stack the recursion it leaves behind has no
+ // statically known stack size. Record which callees those are so manifest()
+ // can leave them to the indirect call. The question has to be asked here,
+ // because the solver no longer answers once manifestation starts.
+ //
+ // Only edges the call graph agrees are direct can close such a cycle, so
+ // walk the solver's known edges rather than asking AAInterFnReachability,
+ // which conservatively reaches everything downstream of an unknown callee
+ // and would suppress far more than this.
+ SmallPtrSet<Function *, 4> CalleesReachingCallerNow;
+ if (Function *Caller = CB->getFunction()) {
+ auto KnownEdgesReach = [&](Function *From) {
+ SmallPtrSet<Function *, 16> Visited;
+ SmallVector<Function *, 16> Worklist = {From};
+ while (!Worklist.empty()) {
+ Function *Fn = Worklist.pop_back_val();
+ if (Fn == Caller)
+ return true;
+ if (!Visited.insert(Fn).second)
+ continue;
+ const auto *EdgesAA = A.getAAFor<AACallEdges>(
+ *this, IRPosition::function(*Fn), DepClassTy::REQUIRED);
+ if (!EdgesAA)
+ continue;
+ for (Function *Next : EdgesAA->getOptimisticEdges())
+ Worklist.push_back(Next);
+ }
+ return false;
+ };
+ for (Function *Callee : AssumedCalleesNow)
+ if (Callee == Caller || KnownEdgesReach(Callee))
+ CalleesReachingCallerNow.insert(Callee);
+ }
+
if (AssumedCalleesNow == AssumedCallees &&
- AllCalleesKnown == AllCalleesKnownNow)
+ AllCalleesKnown == AllCalleesKnownNow &&
+ CalleesReachingCallerNow == CalleesReachingCaller)
return ChangeStatus::UNCHANGED;
std::swap(AssumedCallees, AssumedCalleesNow);
+ std::swap(CalleesReachingCaller, CalleesReachingCallerNow);
AllCalleesKnown = AllCalleesKnownNow;
return ChangeStatus::CHANGED;
}
@@ -12534,9 +12574,16 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
return ChangeStatus::CHANGED;
}
+ // Callees that can reach the function holding this call site were recorded
+ // during the update; see there for why they are left to the indirect call.
+ auto ReachesCaller = [&](Function *Callee) {
+ return CalleesReachingCaller.contains(Callee);
+ };
+
// Special handling for the single callee case.
if (AllCalleesKnown && AssumedCallees.size() == 1 &&
- isLegalToPromote(*CB, AssumedCallees.front())) {
+ isLegalToPromote(*CB, AssumedCallees.front()) &&
+ !ReachesCaller(AssumedCallees.front())) {
promoteCall(*CB, AssumedCallees.front(), nullptr);
NumIndirectCallsPromoted++;
return ChangeStatus::CHANGED;
@@ -12560,7 +12607,7 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
// own operands would not be the call the indirect call performs. Leave it
// to the indirect fallback, which the !callees metadata below still
// names.
- if (!isLegalToPromote(*CB, NewCallee) ||
+ if (!isLegalToPromote(*CB, NewCallee) || ReachesCaller(NewCallee) ||
!A.shouldSpecializeCallSiteForCallee(*this, *CB, *NewCallee,
AssumedCallees.size())) {
SkippedAssumedCallees.push_back(NewCallee);
@@ -12681,6 +12728,12 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
/// time.
SmallSetVector<Function *, 4> AssumedCallees;
+ /// The subset of the assumed callees that can reach the function holding this
+ /// call site, so that specializing for them would make a call graph cycle
+ /// explicit. Determined during the update, where reachability can still be
+ /// queried, and only read when manifesting.
+ SmallPtrSet<Function *, 4> CalleesReachingCaller;
+
/// Flag to indicate if all possible callees are in the AssumedCallees set or
/// if there could be others.
bool AllCalleesKnown = true;
diff --git a/llvm/test/Transforms/Attributor/indirect_call_cycle.ll b/llvm/test/Transforms/Attributor/indirect_call_cycle.ll
new file mode 100644
index 0000000000000..174b1159b941f
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/indirect_call_cycle.ll
@@ -0,0 +1,134 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=attributor --attributor-assume-closed-world -S < %s | FileCheck %s
+
+; Specializing an indirect call for a callee that can reach the function holding
+; the call site turns an edge that only existed through a function pointer into a
+; direct call, closing a call graph cycle. @dispatch would become recursive, and
+; a recursive alwaysinline runtime dispatcher cannot be inlined and has no
+; statically known stack size on targets without a dynamic stack. @inner is
+; specialized, @outer stays on the indirect call.
+
+ at g = external global i32
+
+define internal void @dispatch(ptr %fn) {
+; CHECK-LABEL: define internal void @dispatch(
+; CHECK-SAME: ptr nofree noundef nonnull captures(none) [[FN:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: store volatile i32 1, ptr @g, align 4
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FN]], @inner
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB3:.*]]
+; CHECK: [[BB2]]:
+; CHECK-NEXT: call void @inner()
+; CHECK-NEXT: br label %[[BB4:.*]]
+; CHECK: [[BB3]]:
+; CHECK-NEXT: call void [[FN]](), !callees [[META0:![0-9]+]]
+; CHECK-NEXT: br label %[[BB4]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: ret void
+;
+ store volatile i32 1, ptr @g
+ call void %fn()
+ ret void
+}
+
+define internal void @outer() {
+; CHECK-LABEL: define internal void @outer(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT: store volatile i32 2, ptr @g, align 4
+; CHECK-NEXT: call void @dispatch(ptr nofree noundef nonnull captures(none) @inner) #[[ATTR0]]
+; CHECK-NEXT: ret void
+;
+ store volatile i32 2, ptr @g
+ call void @dispatch(ptr @inner)
+ ret void
+}
+
+define internal void @inner() {
+; CHECK-LABEL: define internal void @inner(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: store volatile i32 3, ptr @g, align 4
+; CHECK-NEXT: ret void
+;
+ store volatile i32 3, ptr @g
+ ret void
+}
+
+define void @kernel() {
+; CHECK-LABEL: define void @kernel(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT: call void @dispatch(ptr nofree noundef nonnull captures(none) @outer) #[[ATTR0]]
+; CHECK-NEXT: ret void
+;
+ call void @dispatch(ptr @outer)
+ ret void
+}
+
+; The way back to @dispatch2 runs through a callback edge: @via_callback hands
+; @calls_dispatch2 to a broker that calls it. Asking the solver for the call
+; edges sees that edge, so @via_callback stays on the indirect call while @leaf,
+; which reaches nothing, is specialized.
+
+declare !callback !0 void @broker(ptr)
+
+define internal void @dispatch2(ptr %fn) {
+; CHECK-LABEL: define internal void @dispatch2(
+; CHECK-SAME: ptr nofree noundef nonnull captures(none) [[FN:%.*]]) {
+; CHECK-NEXT: store volatile i32 4, ptr @g, align 4
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr [[FN]], @leaf
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB3:.*]]
+; CHECK: [[BB2]]:
+; CHECK-NEXT: call void @leaf()
+; CHECK-NEXT: br label %[[BB4:.*]]
+; CHECK: [[BB3]]:
+; CHECK-NEXT: call void [[FN]](), !callees [[META3:![0-9]+]]
+; CHECK-NEXT: br label %[[BB4]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: ret void
+;
+ store volatile i32 4, ptr @g
+ call void %fn()
+ ret void
+}
+
+define internal void @via_callback() {
+; CHECK-LABEL: define internal void @via_callback() {
+; CHECK-NEXT: call void @broker(ptr noundef nonnull @calls_dispatch2)
+; CHECK-NEXT: ret void
+;
+ call void @broker(ptr @calls_dispatch2)
+ ret void
+}
+
+define internal void @calls_dispatch2() {
+; CHECK-LABEL: define internal void @calls_dispatch2() {
+; CHECK-NEXT: call void @dispatch2(ptr nofree noundef nonnull captures(none) @leaf)
+; CHECK-NEXT: ret void
+;
+ call void @dispatch2(ptr @leaf)
+ ret void
+}
+
+define internal void @leaf() {
+; CHECK-LABEL: define internal void @leaf(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT: store volatile i32 5, ptr @g, align 4
+; CHECK-NEXT: ret void
+;
+ store volatile i32 5, ptr @g
+ ret void
+}
+
+define void @kernel2() {
+; CHECK-LABEL: define void @kernel2() {
+; CHECK-NEXT: call void @dispatch2(ptr nofree noundef nonnull captures(none) @via_callback)
+; CHECK-NEXT: ret void
+;
+ call void @dispatch2(ptr @via_callback)
+ ret void
+}
+
+!0 = !{!1}
+!1 = !{i64 0, i1 false}
+;.
+; CHECK: [[META0]] = !{ptr @outer}
+; CHECK: [[META3]] = !{ptr @via_callback}
+;.
>From 058693c6e8aa15769f01ca18c453d5b20026d19b Mon Sep 17 00:00:00 2001
From: Larry Meadows <Lawrence.Meadows at amd.com>
Date: Tue, 18 Aug 2026 12:00:50 -0500
Subject: [PATCH 3/3] [Attributor][NFC] Use poison for the placeholder callee
in callgraph.ll
@undef_in_callees checks that a !callees list naming something that is not a
function does not crash the pass. The callee, its arguments and the entry in
the metadata are all placeholders standing for "not a function", which poison
says as well as undef does, and PoisonValue is a UndefValue, so the pass cannot
tell the difference: the regenerated output is identical token for token.
Co-authored-by: Cursor <cursoragent at cursor.com>
---
llvm/test/Transforms/Attributor/callgraph.ll | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/llvm/test/Transforms/Attributor/callgraph.ll b/llvm/test/Transforms/Attributor/callgraph.ll
index 0dd6ac64158d6..ace71ed23e7d8 100644
--- a/llvm/test/Transforms/Attributor/callgraph.ll
+++ b/llvm/test/Transforms/Attributor/callgraph.ll
@@ -345,26 +345,26 @@ define void @func7(ptr %unknown) {
define void @undef_in_callees() {
; OUNLM-LABEL: @undef_in_callees(
; OUNLM-NEXT: cond.end.i:
-; OUNLM-NEXT: call void undef(ptr undef, i32 undef, ptr undef), !callees [[META3:![0-9]+]]
+; OUNLM-NEXT: call void poison(ptr poison, i32 poison, ptr poison), !callees [[META3:![0-9]+]]
; OUNLM-NEXT: ret void
;
; LIMI2-LABEL: @undef_in_callees(
; LIMI2-NEXT: cond.end.i:
-; LIMI2-NEXT: call void undef(ptr undef, i32 undef, ptr undef), !callees [[META3:![0-9]+]]
+; LIMI2-NEXT: call void poison(ptr poison, i32 poison, ptr poison), !callees [[META3:![0-9]+]]
; LIMI2-NEXT: ret void
;
; LIMI0-LABEL: @undef_in_callees(
; LIMI0-NEXT: cond.end.i:
-; LIMI0-NEXT: call void undef(ptr undef, i32 undef, ptr undef), !callees [[META6:![0-9]+]]
+; LIMI0-NEXT: call void poison(ptr poison, i32 poison, ptr poison), !callees [[META6:![0-9]+]]
; LIMI0-NEXT: ret void
;
; CWRLD-LABEL: @undef_in_callees(
; CWRLD-NEXT: cond.end.i:
-; CWRLD-NEXT: call void undef(ptr undef, i32 undef, ptr undef), !callees [[META6:![0-9]+]]
+; CWRLD-NEXT: call void poison(ptr poison, i32 poison, ptr poison), !callees [[META6:![0-9]+]]
; CWRLD-NEXT: ret void
;
cond.end.i:
- call void undef(ptr undef, i32 undef, ptr undef), !callees !3
+ call void poison(ptr poison, i32 poison, ptr poison), !callees !3
ret void
}
@@ -408,7 +408,7 @@ define void @as_cast(ptr %arg) {
!0 = !{!1}
!1 = !{i64 0, i1 false}
!2 = !{ptr @func3, ptr @func4}
-!3 = distinct !{ptr undef, ptr null}
+!3 = distinct !{ptr poison, ptr null}
; UTC_ARGS: --disable
@@ -450,12 +450,12 @@ define void @as_cast(ptr %arg) {
; OUNLM: [[META0]] = !{ptr @takeI32, ptr @retI32, ptr @void}
; OUNLM: [[META1:![0-9]+]] = !{[[META2:![0-9]+]]}
; OUNLM: [[META2]] = !{i64 0, i1 false}
-; OUNLM: [[META3]] = distinct !{ptr undef, ptr null}
+; OUNLM: [[META3]] = distinct !{ptr poison, ptr null}
;.
; LIMI2: [[META0]] = !{ptr @takeI32, ptr @retI32, ptr @void}
; LIMI2: [[META1:![0-9]+]] = !{[[META2:![0-9]+]]}
; LIMI2: [[META2]] = !{i64 0, i1 false}
-; LIMI2: [[META3]] = distinct !{ptr undef, ptr null}
+; LIMI2: [[META3]] = distinct !{ptr poison, ptr null}
;.
; LIMI0: [[META0]] = !{ptr @func4, ptr @internal_good}
; LIMI0: [[META1]] = !{ptr @func3, ptr @func4}
@@ -463,7 +463,7 @@ define void @as_cast(ptr %arg) {
; LIMI0: [[META3]] = !{ptr @takeI32, ptr @retI32, ptr @void}
; LIMI0: [[META4:![0-9]+]] = !{[[META5:![0-9]+]]}
; LIMI0: [[META5]] = !{i64 0, i1 false}
-; LIMI0: [[META6]] = distinct !{ptr undef, ptr null}
+; LIMI0: [[META6]] = distinct !{ptr poison, ptr null}
;.
; CWRLD: [[META0]] = !{ptr @takeI32, ptr @retI32, ptr @void}
; CWRLD: [[META1]] = !{ptr @takeI32, ptr @retI32, ptr @retFloatTakeFloat, ptr @retFloatTakeFloatFloatNoundef}
@@ -471,7 +471,7 @@ define void @as_cast(ptr %arg) {
; CWRLD: [[META3:![0-9]+]] = !{[[META4:![0-9]+]]}
; CWRLD: [[META4]] = !{i64 0, i1 false}
; CWRLD: [[META5]] = !{ptr @retI32, ptr @takeI32, ptr @retFloatTakeFloat, ptr @retFloatTakeFloatFloatNoundef}
-; CWRLD: [[META6]] = distinct !{ptr undef, ptr null}
+; CWRLD: [[META6]] = distinct !{ptr poison, ptr null}
;.
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; DOT: {{.*}}
More information about the llvm-commits
mailing list