[llvm] [Attributor] Don't specialize an indirect call for a callee it cannot call (PR #217077)

Larry Meadows via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 18 09:46:23 PDT 2026


https://github.com/lfmeadow created https://github.com/llvm/llvm-project/pull/217077

`AAIndirectCallInfoCallSite::manifest()` replaces an indirect call with an if-cascade of direct calls, one arm per assumed callee. Where a callee cannot be promoted to, it asks `isLegalToPromote()`, is told no, and then builds the call anyway by another route:

```c++
NewCall = CallInst::Create(FunctionCallee(CSFT, NewCallee), CSArgs,
                           CB->getName(), ThenTI->getIterator());
```

a direct call naming the callee but carrying the call site's function type and operands.

Given a call site that passes one argument and two candidates, one taking one parameter and one taking two:

```llvm
define void @caller(i1 %c) {
  %fp = select i1 %c, ptr @two_params, ptr @one_param
  call void %fp(i32 1)
  ret void
}
```

`opt -passes=attributor` produces an arm that calls a two-parameter function with one argument:

```llvm
4:
  call void @two_params(i32 1)
```

`@two_params` reads a second parameter that nothing supplies. The signature at the call site is not the callee's, so this is not a call the indirect call could have performed — it is reachable through it only in ways that are undefined — and naming the callee directly hands the rest of the pipeline a call site to reason about as if the signature matched.

With this change the promotable candidate is still specialized and the other is left to the fallback, which the `!callees` metadata already names, so nothing is lost:

```llvm
2:
  call void @one_param(i32 1)
  br label %4
3:
  call void %fp(i32 1), !callees !0
```

Since the arms that remain are promotable by construction, the fallback path in the loop and in the single-callee case both go away.

Two in-tree tests change, and both are this shape: in `nounwind.ll` the candidates return `i32` while the call site is `call void %fp()`, and `callgraph.ll` has several of the same. The punned direct calls are replaced by the indirect call surviving with its metadata.

`check-llvm` is clean: 46668 passed, 77 expectedly failed, no unexpected failures.

cc @jdoerfert


>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] [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}
 ;.



More information about the llvm-commits mailing list