[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 10:04:32 PDT 2026


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

>From 332185885430954843c156f2404f76d8e8923311 Mon Sep 17 00:00:00 2001
From: Larry Meadows <Lawrence.Meadows at amd.com>
Date: Tue, 18 Aug 2026 12:03:17 -0500
Subject: [PATCH 1/2] [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 | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/llvm/test/Transforms/Attributor/callgraph.ll b/llvm/test/Transforms/Attributor/callgraph.ll
index 84e2c54bd832d..0524f8e741021 100644
--- a/llvm/test/Transforms/Attributor/callgraph.ll
+++ b/llvm/test/Transforms/Attributor/callgraph.ll
@@ -578,21 +578,21 @@ define void @func7(ptr %unknown) {
 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:    call void poison(ptr poison, i32 poison, ptr poison), !callees [[META2:![0-9]+]]
 ; UNLIM-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 poison(ptr poison, i32 poison, ptr poison), !callees [[META4:![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
 ;
 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
 }
 
@@ -658,7 +658,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
 
@@ -699,13 +699,13 @@ define void @as_cast(ptr %arg) {
 ;.
 ; OUNLM: [[META0:![0-9]+]] = !{[[META1:![0-9]+]]}
 ; OUNLM: [[META1]] = !{i64 0, i1 false}
-; OUNLM: [[META2]] = distinct !{ptr undef, ptr null}
+; OUNLM: [[META2]] = distinct !{ptr poison, 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: [[META4]] = distinct !{ptr poison, ptr null}
 ;.
 ; LIMI0: [[META0]] = !{ptr @func4, ptr @internal_good}
 ; LIMI0: [[META1]] = !{ptr @func3, ptr @func4}
@@ -713,11 +713,11 @@ 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:![0-9]+]] = !{[[META1:![0-9]+]]}
 ; CWRLD: [[META1]] = !{i64 0, i1 false}
-; CWRLD: [[META2]] = distinct !{ptr undef, ptr null}
+; CWRLD: [[META2]] = distinct !{ptr poison, ptr null}
 ;.
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; DOT: {{.*}}

>From 7d68162b21f8bc2585531bf2d7063b5144ba8dab 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 2/2] [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 0524f8e741021..ace71ed23e7d8 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 poison(ptr poison, i32 poison, ptr poison), !callees [[META2:![0-9]+]]
-; UNLIM-NEXT:    ret void
+; OUNLM-LABEL: @undef_in_callees(
+; OUNLM-NEXT:  cond.end.i:
+; 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 poison(ptr poison, i32 poison, ptr poison), !callees [[META4:![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(
@@ -591,6 +358,11 @@ define void @undef_in_callees() {
 ; 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 poison(ptr poison, i32 poison, ptr poison), !callees [[META6:![0-9]+]]
+; CWRLD-NEXT:    ret void
+;
 cond.end.i:
   call void poison(ptr poison, i32 poison, ptr poison), !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 poison, 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 poison, 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 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 poison, 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 poison, ptr null}
 ;.
-; CWRLD: [[META0:![0-9]+]] = !{[[META1:![0-9]+]]}
-; CWRLD: [[META1]] = !{i64 0, i1 false}
-; CWRLD: [[META2]] = distinct !{ptr poison, 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 poison, 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