[llvm] Remove the optional bitcast between a musttail call and its ret (PR #201280)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 3 07:30:07 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/201280

>From cc59a9d640fbfdea4944718f9869daf7de2807c2 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 3 Jun 2026 09:20:13 +0200
Subject: [PATCH 1/4] Remove the optional bitcast between a musttail call and
 its ret

Under opaque pointers the only bitcast the verifier could accept in this position is a no-op ptr->ptr cast

Drop it and reduce isTypeCongruent to a plain type equality check
---
 llvm/lib/IR/BasicBlock.cpp                    |  8 ----
 llvm/lib/IR/Verifier.cpp                      | 43 ++++---------------
 llvm/lib/Transforms/Utils/InlineFunction.cpp  | 23 +---------
 .../AddressSanitizer/musttail.ll              | 14 ------
 .../ThreadSanitizer/tsan_musttail.ll          | 13 ------
 .../Transforms/CallSiteSplitting/musttail.ll  | 24 -----------
 .../test/Transforms/SafeStack/X86/musttail.ll | 19 --------
 llvm/test/Verifier/musttail-invalid.ll        |  2 +-
 8 files changed, 12 insertions(+), 134 deletions(-)

diff --git a/llvm/lib/IR/BasicBlock.cpp b/llvm/lib/IR/BasicBlock.cpp
index d611e9c2d0a96..03e38b3b89061 100644
--- a/llvm/lib/IR/BasicBlock.cpp
+++ b/llvm/lib/IR/BasicBlock.cpp
@@ -239,14 +239,6 @@ const CallInst *BasicBlock::getTerminatingMustTailCall() const {
   if (Value *RV = RI->getReturnValue()) {
     if (RV != Prev)
       return nullptr;
-
-    // Look through the optional bitcast.
-    if (auto *BI = dyn_cast<BitCastInst>(Prev)) {
-      RV = BI->getOperand(0);
-      Prev = BI->getPrevNode();
-      if (!Prev || RV != Prev)
-        return nullptr;
-    }
   }
 
   if (auto *CI = dyn_cast<CallInst>(Prev)) {
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index c9639d1420bfc..344314e39c38a 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -4246,18 +4246,6 @@ void Verifier::verifyTailCCMustTailAttrs(const AttrBuilder &Attrs,
         Twine("byref attribute not allowed in ") + Context);
 }
 
-/// Two types are "congruent" if they are identical, or if they are both pointer
-/// types with different pointee types and the same address space.
-static bool isTypeCongruent(Type *L, Type *R) {
-  if (L == R)
-    return true;
-  PointerType *PL = dyn_cast<PointerType>(L);
-  PointerType *PR = dyn_cast<PointerType>(R);
-  if (!PL || !PR)
-    return false;
-  return PL->getAddressSpace() == PR->getAddressSpace();
-}
-
 static AttrBuilder getParameterABIAttributes(LLVMContext& C, unsigned I, AttributeList Attrs) {
   static const Attribute::AttrKind ABIAttrs[] = {
       Attribute::StructRet,  Attribute::ByVal,          Attribute::InAlloca,
@@ -4287,32 +4275,21 @@ void Verifier::verifyMustTailCall(CallInst &CI) {
   FunctionType *CalleeTy = CI.getFunctionType();
   Check(CallerTy->isVarArg() == CalleeTy->isVarArg(),
         "cannot guarantee tail call due to mismatched varargs", &CI);
-  Check(isTypeCongruent(CallerTy->getReturnType(), CalleeTy->getReturnType()),
+  Check(CallerTy->getReturnType() == CalleeTy->getReturnType(),
         "cannot guarantee tail call due to mismatched return types", &CI);
 
   // - The calling conventions of the caller and callee must match.
   Check(F->getCallingConv() == CI.getCallingConv(),
         "cannot guarantee tail call due to mismatched calling conv", &CI);
 
-  // - The call must immediately precede a :ref:`ret <i_ret>` instruction,
-  //   or a pointer bitcast followed by a ret instruction.
-  // - The ret instruction must return the (possibly bitcasted) value
-  //   produced by the call or void.
-  Value *RetVal = &CI;
+  // - The call must immediately precede a :ref:`ret <i_ret>` instruction.
+  // - The ret instruction must return the value produced by the call or void.
   Instruction *Next = CI.getNextNode();
 
-  // Handle the optional bitcast.
-  if (BitCastInst *BI = dyn_cast_or_null<BitCastInst>(Next)) {
-    Check(BI->getOperand(0) == RetVal,
-          "bitcast following musttail call must use the call", BI);
-    RetVal = BI;
-    Next = BI->getNextNode();
-  }
-
   // Check the return.
   ReturnInst *Ret = dyn_cast_or_null<ReturnInst>(Next);
-  Check(Ret, "musttail call must precede a ret with an optional bitcast", &CI);
-  Check(!Ret->getReturnValue() || Ret->getReturnValue() == RetVal ||
+  Check(Ret, "musttail call must precede a ret", &CI);
+  Check(!Ret->getReturnValue() || Ret->getReturnValue() == &CI ||
             isa<UndefValue>(Ret->getReturnValue()),
         "musttail call result must be returned", Ret);
 
@@ -4341,16 +4318,14 @@ void Verifier::verifyMustTailCall(CallInst &CI) {
     return;
   }
 
-  // - The caller and callee prototypes must match.  Pointer types of
-  //   parameters or return types may differ in pointee type, but not
-  //   address space.
+  // - The caller and callee prototypes must match.
   if (!CI.getIntrinsicID()) {
     Check(CallerTy->getNumParams() == CalleeTy->getNumParams(),
           "cannot guarantee tail call due to mismatched parameter counts", &CI);
     for (unsigned I = 0, E = CallerTy->getNumParams(); I != E; ++I) {
-      Check(
-          isTypeCongruent(CallerTy->getParamType(I), CalleeTy->getParamType(I)),
-          "cannot guarantee tail call due to mismatched parameter types", &CI);
+      Check(CallerTy->getParamType(I) == CalleeTy->getParamType(I),
+            "cannot guarantee tail call due to mismatched parameter types",
+            &CI);
     }
   }
 
diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index 1d3f66509b1c5..be186ffbf7e42 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -3300,32 +3300,13 @@ void llvm::InlineFunctionImpl(CallBase &CB, InlineFunctionInfo &IFI,
   // musttail.  Therefore it's safe to return without merging control into the
   // phi below.
   if (InlinedMustTailCalls) {
-    // Check if we need to bitcast the result of any musttail calls.
-    Type *NewRetTy = Caller->getReturnType();
-    bool NeedBitCast = !CB.use_empty() && CB.getType() != NewRetTy;
-
     // Handle the returns preceded by musttail calls separately.
     SmallVector<ReturnInst *, 8> NormalReturns;
     for (ReturnInst *RI : Returns) {
       CallInst *ReturnedMustTail =
           RI->getParent()->getTerminatingMustTailCall();
-      if (!ReturnedMustTail) {
+      if (!ReturnedMustTail)
         NormalReturns.push_back(RI);
-        continue;
-      }
-      if (!NeedBitCast)
-        continue;
-
-      // Delete the old return and any preceding bitcast.
-      BasicBlock *CurBB = RI->getParent();
-      auto *OldCast = dyn_cast_or_null<BitCastInst>(RI->getReturnValue());
-      RI->eraseFromParent();
-      if (OldCast)
-        OldCast->eraseFromParent();
-
-      // Insert a new bitcast and return with the right type.
-      IRBuilder<> Builder(CurBB);
-      Builder.CreateRet(Builder.CreateBitCast(ReturnedMustTail, NewRetTy));
     }
 
     // Leave behind the normal returns so we can merge control flow.
@@ -3547,7 +3528,7 @@ void llvm::InlineFunctionImpl(CallBase &CB, InlineFunctionInfo &IFI,
   CB.eraseFromParent();
 
   // If we inlined any musttail calls and the original return is now
-  // unreachable, delete it.  It can only contain a bitcast and ret.
+  // unreachable, delete it.  It can only contain a ret.
   if (InlinedMustTailCalls && pred_empty(AfterCallBB))
     AfterCallBB->eraseFromParent();
 
diff --git a/llvm/test/Instrumentation/AddressSanitizer/musttail.ll b/llvm/test/Instrumentation/AddressSanitizer/musttail.ll
index fed4521c195a4..8ec87693d1859 100644
--- a/llvm/test/Instrumentation/AddressSanitizer/musttail.ll
+++ b/llvm/test/Instrumentation/AddressSanitizer/musttail.ll
@@ -18,17 +18,3 @@ define i32 @call_foo(ptr %a) sanitize_address {
 ; CHECK-LABEL:  define i32 @call_foo(ptr %a) 
 ; CHECK:          %r = musttail call i32 @foo(ptr %a)
 ; CHECK-NEXT:     ret i32 %r
-
-
-define i32 @call_foo_cast(ptr %a) sanitize_address {
-  %x = alloca [10 x i8], align 1
-  call void @alloca_test_use(ptr %x)
-  %r = musttail call i32 @foo(ptr %a)
-  %t = bitcast i32 %r to i32
-  ret i32 %t
-}
-
-; CHECK-LABEL:  define i32 @call_foo_cast(ptr %a)
-; CHECK:          %r = musttail call i32 @foo(ptr %a)
-; CHECK-NEXT:     %t = bitcast i32 %r to i32
-; CHECK-NEXT:     ret i32 %t
diff --git a/llvm/test/Instrumentation/ThreadSanitizer/tsan_musttail.ll b/llvm/test/Instrumentation/ThreadSanitizer/tsan_musttail.ll
index 5e56aa2d11068..2d16a82f666d1 100644
--- a/llvm/test/Instrumentation/ThreadSanitizer/tsan_musttail.ll
+++ b/llvm/test/Instrumentation/ThreadSanitizer/tsan_musttail.ll
@@ -15,16 +15,3 @@ define i32 @call_preallocated_musttail(ptr preallocated(i32) %a) sanitize_thread
 ; CHECK:          call void @__tsan_func_exit()
 ; CHECK-NEXT:     %r = musttail call i32 @preallocated_musttail(ptr preallocated(i32) %a)
 ; CHECK-NEXT:     ret i32 %r
-
-
-define i32 @call_preallocated_musttail_cast(ptr preallocated(i32) %a) sanitize_thread {
-  %r = musttail call i32 @preallocated_musttail(ptr preallocated(i32) %a)
-  %t = bitcast i32 %r to i32
-  ret i32 %t
-}
-
-; CHECK-LABEL:  define i32 @call_preallocated_musttail_cast(ptr preallocated(i32) %a)
-; CHECK:          call void @__tsan_func_exit()
-; CHECK-NEXT:     %r = musttail call i32 @preallocated_musttail(ptr preallocated(i32) %a)
-; CHECK-NEXT:     %t = bitcast i32 %r to i32
-; CHECK-NEXT:     ret i32 %t
diff --git a/llvm/test/Transforms/CallSiteSplitting/musttail.ll b/llvm/test/Transforms/CallSiteSplitting/musttail.ll
index 0f989a2ae4ad1..1993e1e97e403 100644
--- a/llvm/test/Transforms/CallSiteSplitting/musttail.ll
+++ b/llvm/test/Transforms/CallSiteSplitting/musttail.ll
@@ -1,29 +1,5 @@
 ; RUN: opt < %s -passes=callsite-splitting -verify-dom-info -S | FileCheck %s
 
-;CHECK-LABEL: @caller
-;CHECK-LABEL: Top.split:
-;CHECK: %ca1 = musttail call ptr @callee(ptr null, ptr %b)
-;CHECK: %cb2 = bitcast ptr %ca1 to ptr
-;CHECK: ret ptr %cb2
-;CHECK-LABEL: TBB.split
-;CHECK: %ca3 = musttail call ptr @callee(ptr nonnull %a, ptr null)
-;CHECK: %cb4 = bitcast ptr %ca3 to ptr
-;CHECK: ret ptr %cb4
-define ptr @caller(ptr %a, ptr %b) {
-Top:
-  %c = icmp eq ptr %a, null
-  br i1 %c, label %Tail, label %TBB
-TBB:
-  %c2 = icmp eq ptr %b, null
-  br i1 %c2, label %Tail, label %End
-Tail:
-  %ca = musttail call ptr @callee(ptr %a, ptr %b)
-  %cb = bitcast ptr %ca to ptr
-  ret ptr %cb
-End:
-  ret ptr null
-}
-
 define ptr @callee(ptr %a, ptr %b) noinline {
   ret ptr %a
 }
diff --git a/llvm/test/Transforms/SafeStack/X86/musttail.ll b/llvm/test/Transforms/SafeStack/X86/musttail.ll
index 0289729f53b50..317bc3920fb1b 100644
--- a/llvm/test/Transforms/SafeStack/X86/musttail.ll
+++ b/llvm/test/Transforms/SafeStack/X86/musttail.ll
@@ -24,22 +24,3 @@ define i32 @call_foo(ptr %a) safestack {
   %r = musttail call i32 @foo(ptr %a)
   ret i32 %r
 }
-
-define i32 @call_foo_cast(ptr %a) safestack {
-; CHECK-LABEL: @call_foo_cast(
-; CHECK-NEXT:    [[UNSAFE_STACK_PTR:%.*]] = load ptr, ptr @__safestack_unsafe_stack_ptr, align 8
-; CHECK-NEXT:    [[UNSAFE_STACK_STATIC_TOP:%.*]] = getelementptr i8, ptr [[UNSAFE_STACK_PTR]], i32 -16
-; CHECK-NEXT:    store ptr [[UNSAFE_STACK_STATIC_TOP]], ptr @__safestack_unsafe_stack_ptr, align 8
-; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[UNSAFE_STACK_PTR]], i32 -10
-; CHECK-NEXT:    call void @alloca_test_use(ptr [[TMP1]])
-; CHECK-NEXT:    store ptr [[UNSAFE_STACK_PTR]], ptr @__safestack_unsafe_stack_ptr, align 8
-; CHECK-NEXT:    [[R:%.*]] = musttail call i32 @foo(ptr [[A:%.*]])
-; CHECK-NEXT:    [[T:%.*]] = bitcast i32 [[R]] to i32
-; CHECK-NEXT:    ret i32 [[T]]
-;
-  %x = alloca [10 x i8], align 1
-  call void @alloca_test_use(ptr %x)
-  %r = musttail call i32 @foo(ptr %a)
-  %t = bitcast i32 %r to i32
-  ret i32 %t
-}
diff --git a/llvm/test/Verifier/musttail-invalid.ll b/llvm/test/Verifier/musttail-invalid.ll
index b9109a09bb543..849967ee9d487 100644
--- a/llvm/test/Verifier/musttail-invalid.ll
+++ b/llvm/test/Verifier/musttail-invalid.ll
@@ -69,7 +69,7 @@ define void @mismatched_alignment(ptr byval(i32) align 4 %a) {
 
 declare i32 @not_tail_pos_callee()
 define i32 @not_tail_pos() {
-; CHECK: musttail call must precede a ret with an optional bitcast
+; CHECK: musttail call must precede a ret
   %v = musttail call i32 @not_tail_pos_callee()
   %w = add i32 %v, 1
   ret i32 %w

>From 9e955df688cf8492db12c8c2c94cccaa8eb8d895 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 3 Jun 2026 14:50:10 +0200
Subject: [PATCH 2/4] Fix autoupgrade and add bitcode test

---
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     |  14 ++++++++++++
 llvm/test/Bitcode/musttail-bitcast-upgrade.ll |  20 ++++++++++++++++++
 .../Bitcode/musttail-bitcast-upgrade.ll.bc    | Bin 0 -> 1852 bytes
 3 files changed, 34 insertions(+)
 create mode 100644 llvm/test/Bitcode/musttail-bitcast-upgrade.ll
 create mode 100644 llvm/test/Bitcode/musttail-bitcast-upgrade.ll.bc

diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 3e863f4786e1a..b1825e721e81c 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -7132,6 +7132,20 @@ Error BitcodeReader::materialize(GlobalValue *GV) {
       }
     }
 
+    // Old bitcode allowed an optional bitcast between a musttail call and the
+    // return. Under opaque pointers that cast is always a no-op, and the
+    // verifier no longer accepts it, so drop it.
+    if (auto *BC = dyn_cast<BitCastInst>(&I);
+        BC && BC->getSrcTy() == BC->getDestTy() &&
+        isa_and_nonnull<ReturnInst>(BC->getNextNode())) {
+      if (auto *CI = dyn_cast<CallInst>(BC->getOperand(0));
+          CI && CI->isMustTailCall() && CI->getNextNode() == BC) {
+        BC->replaceAllUsesWith(CI);
+        BC->eraseFromParent();
+        continue;
+      }
+    }
+
     if (auto *CI = dyn_cast<CallBase>(&I)) {
       // Remove incompatible attributes on function calls.
       CI->removeRetAttrs(AttributeFuncs::typeIncompatible(
diff --git a/llvm/test/Bitcode/musttail-bitcast-upgrade.ll b/llvm/test/Bitcode/musttail-bitcast-upgrade.ll
new file mode 100644
index 0000000000000..133efe2f7afa4
--- /dev/null
+++ b/llvm/test/Bitcode/musttail-bitcast-upgrade.ll
@@ -0,0 +1,20 @@
+; RUN: llvm-dis %s.bc -o - | FileCheck %s
+; RUN: verify-uselistorder %s.bc
+
+; musttail-bitcast-upgrade.ll.bc was produced from the IR below using an
+; llvm-as that still emitted the optional no-op bitcast between a musttail call
+; and its return. The reader must drop that bitcast so the module verifies.
+
+; CHECK-LABEL: define ptr @caller(ptr %a)
+; CHECK-NEXT:    %c = musttail call ptr @callee(ptr %a)
+; CHECK-NEXT:    ret ptr %c
+
+define ptr @callee(ptr %a) {
+  ret ptr %a
+}
+
+define ptr @caller(ptr %a) {
+  %c = musttail call ptr @callee(ptr %a)
+  %b = bitcast ptr %c to ptr
+  ret ptr %b
+}
diff --git a/llvm/test/Bitcode/musttail-bitcast-upgrade.ll.bc b/llvm/test/Bitcode/musttail-bitcast-upgrade.ll.bc
new file mode 100644
index 0000000000000000000000000000000000000000..731c1ae079fa14e44a616db92abd31e0b166df02
GIT binary patch
literal 1852
zcmX|CeM}qY8Gr4=K8HB(T_RTO=I-VTG_tmQ+zbsqY)jaeAy>;bH_abR>J%RshjcVO
zjAK5!RW|lGvZ2bYtjK?45?;5e{f|s&qY|nxN103AjAdWhijZ9lNj4H#LZ?WJracE+
zPrB#5_r3R?_j!K5-}78RnqPaq0U;AYsM{(ufBSnMzWbwp&wTf2srjtIWkS3ap<fyh
zI%wpP5%x5}O{SXO@|WsfFw-vaZ9~TToXKM6>VDdtw0_eV_(@ZyxiM36M4SpOTyKa(
z=(oZj2ga?354`D)WP+b(4<8GjPg?E#bz`JtIovFEbdJ-X7+kK5#dRTc!ST*)M`tW$
zwY$ct>tA<8Z1xwhE3()X%koW~+2?4sJzko4Gs~YHpR9AWB6Q7x&;f|)ShSA*RbcF6
z`aGEIz(Ockf8e;ZWD2;BTP$BQ3bbHE&+_N+XUR{0{hN!sk8b?u$h-H~+dg>u%3tor
zN8O)Zx%P+Mf#L7G_NVHlk^IYvn}3x~%v-?kmzNdUy6v4`PT?Vpl8ffl&))}hZrY4s
zTP#UtvC2dn4894;ozW%(NpfVE9Lx_`Hxc!K#=A+}VhwN$MfO&Ki3+B at ZRw+B!dH at f
zlZ3B4JQ-KW*Dd5oP*oIiR3Q?>s^U=X<->R at i>m^4MWEJq at J<M~(2MLE&)nhJ1(}Nq
z*F1A+&x$0 at 5Z<}8H$!;JI}M32`FbDO)~Ck6CRHV?Dq%9=Boj|e;C3jfUbLtKHZ_)4
zNAjr?4|njx3jSgqZwW+qsle0<OnJ|)%FHs)ET-6H+5G3MB$gyGzh`GPcTRFo5T2ZN
z8V-Fr=J_Mxnbxeoy{sx-YD`JT`^e$28n>zODb;@KCF&uf9))l%i`P4-ssnF977i!7
zTI3e|+-;s&O>wIgrdDPDZs4M}7v0lR`;D}x9BTewB4KrCm>kZ(cy2dK=?HMIaLY1t
zk7w74>>CD75140nWb=g|r`=`EGcEZ5kf#gYn&g>)sW$mhJ!Gm5K|B_6)Jewjsh8Jj
zyfu$ELin-JRJ`ryYSqR&Rdx*m2--c<Y2QrxXhw5SN at 7{_&KAXz2041m!I=RI2~iCI
zfL*Hnvn9M0rXEzxI}U0?pf()Tze4y5jn at SX)5jI_{R*yy at Driw^DUWKNU?XSjaxjs
z$+L?*3k=@lzZ!M?%rhtXW(hHu7Rz08z&<293ULP2Aw{)+au;t4RJDTdcTo4I;1tb3
z48BJ~E*JA46&>OD3Q(EK_{E9_(D{I=a>wEnyIf!^er~PEM4NjAT-Eudz{!Rn@#(8J
zaxkb~oKk<7gl})=4Bko2o73DYQai}042W5S{8s`p2ch07vgcC`(Q}ssyzQX2E9UJG
zwX3_r8x`}WgQ~7TvhQ at H6Mf`}MYW%Qc2 at I%4u2uJ%YY#vX4Bp>;RP^rnzi%L5=a*m
zRbbvoagPkZ<I+ttC=mPr7cEE5*i<D+j=o2ZIDxX_jNd&?c;+<sQ=@NAYtKP!eX9=k
zZw3y~TLJ)!hjjMk at ke`35Vbz97t3QIol5U9rRNCxA9RwV`F}3 at +h-xLw65DJsU4~h
z)LYXgzlE1mY`NOFSzwk at -0dp6T5Z&!gYf(u$ZQ!3tzl5XUu1DjSTCd#PBo!`e|4Zk
zjXg2NgXGW at sW*uCZ3wl&c(6X^ggPTfyOca|tJ5N`Xk(kqQsK}&zupD_*M0py)4CqU
zcSBGXR821q>TU?{PA5}30D9F9tB{AT*)HXV=AJP?_`aNG`~froX0^(!I+&WQ+j}W)
zL1tD8Ole<ea?wET0y)4{5+9t{C*1TqH_&?ridX}=c|hIJdrE}ogcG!R8@$QIvv>pa
z3t9_PRj|+-R?LsH)B^|Jp2vEQzo!v0!$_9BfaDEm0)%fW?S+Pr)u0t3*}4BuX|jwx
zQ%4$+zIGhG(J%d7J&f><H=F1&g|?>Zr%zgN9YO at GchYrdXv_b1BQ)UHUw2{pSO#4u
zY%3z{Lg at SOZ{lD)LgfCT&wJ$l{@&g_8*{as5>5%1F2@}`J<p4>7&+4_M7yJrUb$6l
zZEgF8J1V-*h!Hv37U^}lg|;47e}CfD)A8QH`02pqc+YE*-qWvM9gN52%l#+2VL={@
SpS-GEipi1QQ~mw=ap=F$2c)k6

literal 0
HcmV?d00001


>From 200e5c9caca4f737bb44518c77b14f305de52e6d Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 3 Jun 2026 15:14:24 +0200
Subject: [PATCH 3/4] Address comments

---
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     |  29 +++++++++---------
 llvm/test/Bitcode/musttail-bitcast-upgrade.ll |  12 ++++++++
 .../Bitcode/musttail-bitcast-upgrade.ll.bc    | Bin 1852 -> 1972 bytes
 3 files changed, 27 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index b1825e721e81c..a87c6ae2f29e0 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -7132,20 +7132,6 @@ Error BitcodeReader::materialize(GlobalValue *GV) {
       }
     }
 
-    // Old bitcode allowed an optional bitcast between a musttail call and the
-    // return. Under opaque pointers that cast is always a no-op, and the
-    // verifier no longer accepts it, so drop it.
-    if (auto *BC = dyn_cast<BitCastInst>(&I);
-        BC && BC->getSrcTy() == BC->getDestTy() &&
-        isa_and_nonnull<ReturnInst>(BC->getNextNode())) {
-      if (auto *CI = dyn_cast<CallInst>(BC->getOperand(0));
-          CI && CI->isMustTailCall() && CI->getNextNode() == BC) {
-        BC->replaceAllUsesWith(CI);
-        BC->eraseFromParent();
-        continue;
-      }
-    }
-
     if (auto *CI = dyn_cast<CallBase>(&I)) {
       // Remove incompatible attributes on function calls.
       CI->removeRetAttrs(AttributeFuncs::typeIncompatible(
@@ -7165,6 +7151,21 @@ Error BitcodeReader::materialize(GlobalValue *GV) {
     }
   }
 
+  // Old bitcode allowed an optional bitcast between a musttail call and its
+  // return. Under opaque pointers that cast is always a no-op, and the verifier
+  // no longer accepts it, so drop it.
+  for (Instruction &I : instructions(F)) {
+    auto *CI = dyn_cast<CallInst>(&I);
+    if (!CI || !CI->isMustTailCall())
+      continue;
+    if (auto *BC = dyn_cast_or_null<BitCastInst>(CI->getNextNode());
+        BC && BC->getOperand(0) == CI && BC->getSrcTy() == BC->getDestTy() &&
+        isa_and_nonnull<ReturnInst>(BC->getNextNode())) {
+      BC->replaceAllUsesWith(CI);
+      BC->eraseFromParent();
+    }
+  }
+
   // Look for functions that rely on old function attribute behavior.
   UpgradeFunctionAttributes(*F);
 
diff --git a/llvm/test/Bitcode/musttail-bitcast-upgrade.ll b/llvm/test/Bitcode/musttail-bitcast-upgrade.ll
index 133efe2f7afa4..63c24778a067c 100644
--- a/llvm/test/Bitcode/musttail-bitcast-upgrade.ll
+++ b/llvm/test/Bitcode/musttail-bitcast-upgrade.ll
@@ -18,3 +18,15 @@ define ptr @caller(ptr %a) {
   %b = bitcast ptr %c to ptr
   ret ptr %b
 }
+
+; CHECK-LABEL: define i32 @caller_i32(ptr %a)
+; CHECK-NEXT:    %c = musttail call i32 @callee_i32(ptr %a)
+; CHECK-NEXT:    ret i32 %c
+
+declare i32 @callee_i32(ptr %a)
+
+define i32 @caller_i32(ptr %a) {
+  %c = musttail call i32 @callee_i32(ptr %a)
+  %b = bitcast i32 %c to i32
+  ret i32 %b
+}
diff --git a/llvm/test/Bitcode/musttail-bitcast-upgrade.ll.bc b/llvm/test/Bitcode/musttail-bitcast-upgrade.ll.bc
index 731c1ae079fa14e44a616db92abd31e0b166df02..57c6bade7ebfe6dbef26baa950c85482fae4d365 100644
GIT binary patch
delta 290
zcmdnPw}pR#3gf<ss`DASCmxmO=h0#^aA#n16<}j>V_;yI_&Uh=fIxR3n*xvn1I!F<
z5z~xdJP<9xps>+#K}G=sgM$tmkiXf3QH7P!ZE`M~q=ZN#*9Kh<J{ATBW}r-(085wv
zhnoNc!{j_R at p=QEMy at T`WI?7fFcc~<FaR|&81V at xwJ<DVmY87Gz`PY?4g*6h4+DcT
zkQQQ|@*l)cPRz+kO$C!h5O#c~u at Qt1Vi_6h8R!|LXO<{frdpbsrlgr!SjZ)(S(sQF
hr6d^|C0isLC0m*#CnqN+TBIgfq*z#*PUd1a0sw_eJCpzb

delta 188
zcmdnOzlU#v3ggm=s`D8+Cmxk&=g?v at 5HMhx_&JEX;RsvfqLqvc3>zgjS1_uuGOA6U
z%O+XR)5x_!mxGUmfq|KUfx%6H#Y})hO#sLQgF*#}xDlU#QVYX0W{C at yVDVTU1_n7G
zONe>OYmh*4VopwKDwr%XGS)NDGf2-YQAkcUwM?{3F*7kpOG-;gO*FAIF)=r^NVBvs
XvrI`$Gfzo1Vzn?ZPd1v|#BKxt%7-gs


>From e2753e8d4d3556468a54963bd14b95405bf877ae Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 3 Jun 2026 15:42:28 +0200
Subject: [PATCH 4/4] remove the loop

---
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index a87c6ae2f29e0..4df54f259ad10 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -7149,20 +7149,18 @@ Error BitcodeReader::materialize(GlobalValue *GV) {
           UpgradeIntrinsicCall(CI, It->second);
       }
     }
-  }
 
-  // Old bitcode allowed an optional bitcast between a musttail call and its
-  // return. Under opaque pointers that cast is always a no-op, and the verifier
-  // no longer accepts it, so drop it.
-  for (Instruction &I : instructions(F)) {
-    auto *CI = dyn_cast<CallInst>(&I);
-    if (!CI || !CI->isMustTailCall())
-      continue;
-    if (auto *BC = dyn_cast_or_null<BitCastInst>(CI->getNextNode());
-        BC && BC->getOperand(0) == CI && BC->getSrcTy() == BC->getDestTy() &&
+    // Old bitcode allowed an optional bitcast between a musttail call and its
+    // return. Under opaque pointers that cast is always a no-op, and the
+    // verifier no longer accepts it, so drop it.
+    if (auto *BC = dyn_cast<BitCastInst>(&I);
+        BC && BC->getSrcTy() == BC->getDestTy() &&
         isa_and_nonnull<ReturnInst>(BC->getNextNode())) {
-      BC->replaceAllUsesWith(CI);
-      BC->eraseFromParent();
+      if (auto *CI = dyn_cast<CallInst>(BC->getOperand(0));
+          CI && CI->isMustTailCall() && CI->getNextNode() == BC) {
+        BC->replaceAllUsesWith(CI);
+        BC->eraseFromParent();
+      }
     }
   }
 



More information about the llvm-commits mailing list