[llvm-branch-commits] [llvm] 5be0462 - Revert "Remove the optional bitcast between a musttail call and its ret (#201…"
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Jun 3 16:46:52 PDT 2026
Author: Florian Mayer
Date: 2026-06-03T16:46:48-07:00
New Revision: 5be046268f78dc1eef49a080228aadaf506b44ff
URL: https://github.com/llvm/llvm-project/commit/5be046268f78dc1eef49a080228aadaf506b44ff
DIFF: https://github.com/llvm/llvm-project/commit/5be046268f78dc1eef49a080228aadaf506b44ff.diff
LOG: Revert "Remove the optional bitcast between a musttail call and its ret (#201…"
This reverts commit 91b00526a599fa21e43da82438f1e9c8a8e1b7b3.
Added:
Modified:
llvm/lib/Bitcode/Reader/BitcodeReader.cpp
llvm/lib/IR/BasicBlock.cpp
llvm/lib/IR/Verifier.cpp
llvm/lib/Transforms/Utils/InlineFunction.cpp
llvm/test/Instrumentation/AddressSanitizer/musttail.ll
llvm/test/Instrumentation/ThreadSanitizer/tsan_musttail.ll
llvm/test/Transforms/CallSiteSplitting/musttail.ll
llvm/test/Transforms/SafeStack/X86/musttail.ll
llvm/test/Verifier/musttail-invalid.ll
Removed:
llvm/test/Bitcode/musttail-bitcast-upgrade.ll
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 4df54f259ad10..3e863f4786e1a 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -7149,19 +7149,6 @@ 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.
- 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();
- }
- }
}
// Look for functions that rely on old function attribute behavior.
diff --git a/llvm/lib/IR/BasicBlock.cpp b/llvm/lib/IR/BasicBlock.cpp
index 03e38b3b89061..d611e9c2d0a96 100644
--- a/llvm/lib/IR/BasicBlock.cpp
+++ b/llvm/lib/IR/BasicBlock.cpp
@@ -239,6 +239,14 @@ 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 344314e39c38a..c9639d1420bfc 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -4246,6 +4246,18 @@ 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
diff erent 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,
@@ -4275,21 +4287,32 @@ void Verifier::verifyMustTailCall(CallInst &CI) {
FunctionType *CalleeTy = CI.getFunctionType();
Check(CallerTy->isVarArg() == CalleeTy->isVarArg(),
"cannot guarantee tail call due to mismatched varargs", &CI);
- Check(CallerTy->getReturnType() == CalleeTy->getReturnType(),
+ Check(isTypeCongruent(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.
- // - The ret instruction must return the value produced by the call or void.
+ // - 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;
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", &CI);
- Check(!Ret->getReturnValue() || Ret->getReturnValue() == &CI ||
+ Check(Ret, "musttail call must precede a ret with an optional bitcast", &CI);
+ Check(!Ret->getReturnValue() || Ret->getReturnValue() == RetVal ||
isa<UndefValue>(Ret->getReturnValue()),
"musttail call result must be returned", Ret);
@@ -4318,14 +4341,16 @@ void Verifier::verifyMustTailCall(CallInst &CI) {
return;
}
- // - The caller and callee prototypes must match.
+ // - The caller and callee prototypes must match. Pointer types of
+ // parameters or return types may
diff er in pointee type, but not
+ // address space.
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(CallerTy->getParamType(I) == CalleeTy->getParamType(I),
- "cannot guarantee tail call due to mismatched parameter types",
- &CI);
+ Check(
+ isTypeCongruent(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 be186ffbf7e42..1d3f66509b1c5 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -3300,13 +3300,32 @@ 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.
@@ -3528,7 +3547,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 ret.
+ // unreachable, delete it. It can only contain a bitcast and ret.
if (InlinedMustTailCalls && pred_empty(AfterCallBB))
AfterCallBB->eraseFromParent();
diff --git a/llvm/test/Bitcode/musttail-bitcast-upgrade.ll b/llvm/test/Bitcode/musttail-bitcast-upgrade.ll
deleted file mode 100644
index 63c24778a067c..0000000000000
--- a/llvm/test/Bitcode/musttail-bitcast-upgrade.ll
+++ /dev/null
@@ -1,32 +0,0 @@
-; 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
-}
-
-; 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
deleted file mode 100644
index 57c6bade7ebfe..0000000000000
Binary files a/llvm/test/Bitcode/musttail-bitcast-upgrade.ll.bc and /dev/null
diff er
diff --git a/llvm/test/Instrumentation/AddressSanitizer/musttail.ll b/llvm/test/Instrumentation/AddressSanitizer/musttail.ll
index 8ec87693d1859..fed4521c195a4 100644
--- a/llvm/test/Instrumentation/AddressSanitizer/musttail.ll
+++ b/llvm/test/Instrumentation/AddressSanitizer/musttail.ll
@@ -18,3 +18,17 @@ 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 2d16a82f666d1..5e56aa2d11068 100644
--- a/llvm/test/Instrumentation/ThreadSanitizer/tsan_musttail.ll
+++ b/llvm/test/Instrumentation/ThreadSanitizer/tsan_musttail.ll
@@ -15,3 +15,16 @@ 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 1993e1e97e403..0f989a2ae4ad1 100644
--- a/llvm/test/Transforms/CallSiteSplitting/musttail.ll
+++ b/llvm/test/Transforms/CallSiteSplitting/musttail.ll
@@ -1,5 +1,29 @@
; 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 317bc3920fb1b..0289729f53b50 100644
--- a/llvm/test/Transforms/SafeStack/X86/musttail.ll
+++ b/llvm/test/Transforms/SafeStack/X86/musttail.ll
@@ -24,3 +24,22 @@ 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 849967ee9d487..b9109a09bb543 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
+; CHECK: musttail call must precede a ret with an optional bitcast
%v = musttail call i32 @not_tail_pos_callee()
%w = add i32 %v, 1
ret i32 %w
More information about the llvm-branch-commits
mailing list