Bug type: crash
Component: GlobalOpt
Bug location: llvm/lib/Transforms/IPO/GlobalOpt.cpp: ChangeCalleesToFastCall
StripAttr
changeCallSitesToColdCC
isValidCandidateForColdCC
Issue title: crashes with assertion failure when running opt -passes=globalopt
Labels: ipo
crash
Reported by: k-arrows

Run opt with -passes=globalopt

Reproducer:
https://godbolt.org/z/6obEYeKEh

Backtrace:
```console
opt: /root/llvm-project/llvm/include/llvm/Support/Casting.h:578: decltype(auto) llvm::cast(From*) [with To = llvm::CallBase; From = llvm::User]: Assertion `isa<To>(Val) && "cast<Ty>() argument of incompatible type!"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: /opt/compiler-explorer/clang-assertions-trunk/bin/opt -o /app/output.s -S -passes=globalopt <source>
1.	Running pass "globalopt" on module "<source>"
 #0 0x000000000578f178 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x578f178)
 #1 0x000000000578c024 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #2 0x000071ca6f842520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x000071ca6f8969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x000071ca6f842476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x000071ca6f8287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x000071ca6f82871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x000071ca6f839e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x00000000030ac2ea optimizeGlobalsInModule(llvm::Module&, llvm::DataLayout const&, llvm::function_ref<llvm::TargetLibraryInfo& (llvm::Function&)>, llvm::function_ref<llvm::TargetTransformInfo& (llvm::Function&)>, llvm::function_ref<llvm::BlockFrequencyInfo& (llvm::Function&)>, llvm::function_ref<llvm::DominatorTree& (llvm::Function&)>, llvm::function_ref<void (llvm::Function&)>, llvm::function_ref<void (llvm::Function&)>) (.constprop.0) GlobalOpt.cpp:0:0
 #9 0x00000000030ad19f llvm::GlobalOptPass::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x30ad19f)
#10 0x0000000002e23dae llvm::detail::PassModel<llvm::Module, llvm::GlobalOptPass, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x2e23dae)
#11 0x0000000005578c41 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x5578c41)
#12 0x0000000000976da8 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x976da8)
#13 0x000000000096ae17 optMain (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x96ae17)
#14 0x000071ca6f829d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#15 0x000071ca6f829e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#16 0x0000000000961f35 _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x961f35)
Program terminated with signal: SIGSEGV
Compiler returned: 139
```


Fix patch:
commit f80e7e139e3e677638233037499f8cba50c66b9e
Author: Hongyu Chen <xxs_chy@outlook.com>
Date:   Wed Oct 1 16:47:41 2025 +0800

    [GlobalOpt] Check if users are CallBase when changing CC (#161399)
    
    Fixes https://github.com/llvm/llvm-project/issues/156656
    `hasChangeableCCImpl` guarantees the address of the function is not
    taken, but it ignores assume-like calls.
    This patch ignores assume-like calls when changing CC.

diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index f88d51f443bc..99c4982c58b4 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -1680,7 +1680,9 @@ processGlobal(GlobalValue &GV,
 /// FastCC.
 static void ChangeCalleesToFastCall(Function *F) {
   for (User *U : F->users())
-    cast<CallBase>(U)->setCallingConv(CallingConv::Fast);
+    if (auto *Call = dyn_cast<CallBase>(U))
+      if (Call->getCalledOperand() == F)
+        Call->setCallingConv(CallingConv::Fast);
 }
 
 static AttributeList StripAttr(LLVMContext &C, AttributeList Attrs,
@@ -1766,10 +1768,12 @@ isValidCandidateForColdCC(Function &F,
     return false;
 
   for (User *U : F.users()) {
-    CallBase &CB = cast<CallBase>(*U);
-    Function *CallerFunc = CB.getParent()->getParent();
+    CallBase *CB = dyn_cast<CallBase>(U);
+    if (!CB || CB->getCalledOperand() != &F)
+      continue;
+    Function *CallerFunc = CB->getParent()->getParent();
     BlockFrequencyInfo &CallerBFI = GetBFI(*CallerFunc);
-    if (!isColdCallSite(CB, CallerBFI))
+    if (!isColdCallSite(*CB, CallerBFI))
       return false;
     if (!llvm::is_contained(AllCallsCold, CallerFunc))
       return false;
@@ -1779,7 +1783,9 @@ isValidCandidateForColdCC(Function &F,
 
 static void changeCallSitesToColdCC(Function *F) {
   for (User *U : F->users())
-    cast<CallBase>(U)->setCallingConv(CallingConv::Cold);
+    if (auto *Call = dyn_cast<CallBase>(U))
+      if (Call->getCalledOperand() == F)
+        Call->setCallingConv(CallingConv::Cold);
 }
 
 // This function iterates over all the call instructions in the input Function


Regression tests:
file: llvm/test/Transforms/GlobalOpt/fastcc.ll
commands: opt < %s -passes=globalopt -S
tests: test_name: <module>
test_body: 
declare token @llvm.call.preallocated.setup(i32)
declare ptr @llvm.call.preallocated.arg(token, i32)

define internal i32 @f(ptr %m) {
;
  %v = load i32, ptr %m
  ret i32 %v
}

define internal x86_thiscallcc i32 @g(ptr %m) {
;
  %v = load i32, ptr %m
  ret i32 %v
}

; Leave this one alone, because the user went out of their way to request this
; convention.
define internal coldcc i32 @h(ptr %m) {
;
  %v = load i32, ptr %m
  ret i32 %v
}

define internal i32 @j(ptr %m) {
;
  %v = load i32, ptr %m
  ret i32 %v
}

define internal i32 @inalloca(ptr inalloca(i32) %p) {
;
  %rv = load i32, ptr %p
  ret i32 %rv
}

define i32 @inalloca2_caller(ptr inalloca(i32) %p) {
;
  %rv = musttail call i32 @inalloca2(ptr inalloca(i32) %p)
  ret i32 %rv
}
define internal i32 @inalloca2(ptr inalloca(i32) %p) {
; Because of the musttail caller, this inalloca cannot be dropped.
;
  %rv = load i32, ptr %p
  ret i32 %rv
}

define internal i32 @preallocated(ptr preallocated(i32) %p) {
;
  %rv = load i32, ptr %p
  ret i32 %rv
}

define void @call_things() {
;
  %m = alloca i32
  call i32 @f(ptr %m)
  call x86_thiscallcc i32 @g(ptr %m)
  call coldcc i32 @h(ptr %m)
  call i32 @j(ptr %m)
  %args = alloca inalloca i32
  call i32 @inalloca(ptr inalloca(i32) %args)
  %c = call token @llvm.call.preallocated.setup(i32 1)
  %N = call ptr @llvm.call.preallocated.arg(token %c, i32 0) preallocated(i32)
  call i32 @preallocated(ptr preallocated(i32) %N) ["preallocated"(token %c)]
  ret void
}

@llvm.used = appending global [1 x ptr] [
  ptr @j
], section "llvm.metadata"

define internal i32 @assume_fastcc() {
;
  %objsize = call i32 @llvm.objectsize.i32.p0(ptr @assume_fastcc, i1 false, i1 false, i1 false)
  ret i32 %objsize
}

define internal i32 @constexpr_self_user() addrspace(1) {
;
  %objsize = call i32 @llvm.objectsize.i32.p0(ptr addrspacecast (ptr addrspace(1) @constexpr_self_user to ptr), i1 false, i1 false, i1 false)
  ret i32 %objsize
}