https://github.com/djtodoro updated https://github.com/llvm/llvm-project/pull/161860
>From ac9497ed3fffb45a8044c043f703487baeedb4e5 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic <[email protected]> Date: Fri, 3 Oct 2025 17:03:52 +0200 Subject: [PATCH 01/11] [MIPS][ISel] Fix musttail Properly handle clang::musttail attribute on MIPS backend. --- llvm/lib/Target/Mips/MipsISelLowering.cpp | 7 +++-- llvm/test/CodeGen/Mips/musttail.ll | 38 +++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 llvm/test/CodeGen/Mips/musttail.ll diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp index 881ba8e2f9eff..d79db127f01a2 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp @@ -3407,7 +3407,8 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // Check if it's really possible to do a tail call. Restrict it to functions // that are part of this compilation unit. bool InternalLinkage = false; - if (IsTailCall) { + bool IsMustTail = CLI.CB && CLI.CB->isMustTailCall(); + if (IsTailCall && !IsMustTail) { IsTailCall = isEligibleForTailCallOptimization( CCInfo, StackSize, *MF.getInfo<MipsFunctionInfo>()); if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { @@ -3416,9 +3417,9 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, G->getGlobal()->hasPrivateLinkage() || G->getGlobal()->hasHiddenVisibility() || G->getGlobal()->hasProtectedVisibility()); - } + } } - if (!IsTailCall && CLI.CB && CLI.CB->isMustTailCall()) + if (!IsTailCall && IsMustTail) report_fatal_error("failed to perform tail call elimination on a call " "site marked musttail"); diff --git a/llvm/test/CodeGen/Mips/musttail.ll b/llvm/test/CodeGen/Mips/musttail.ll new file mode 100644 index 0000000000000..d5f457f6eb665 --- /dev/null +++ b/llvm/test/CodeGen/Mips/musttail.ll @@ -0,0 +1,38 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=mips-unknown-linux-gnu < %s | FileCheck %s --check-prefix=MIPS32 +; RUN: llc -mtriple=mips64-unknown-linux-gnu < %s | FileCheck %s --check-prefix=MIPS64 + +; Test musttail support for MIPS + +declare void @external_func() + +; Test basic musttail with external function +define void @test_musttail_external() { +; MIPS32-LABEL: test_musttail_external: +; MIPS32: # %bb.0: +; MIPS32-NEXT: j external_func +; MIPS32-NEXT: nop +; +; MIPS64-LABEL: test_musttail_external: +; MIPS64: # %bb.0: +; MIPS64-NEXT: j external_func +; MIPS64-NEXT: nop + musttail call void @external_func() + ret void +} + +declare i32 @callee_args(i32 %a, i32 %b, i32 %c) + +define i32 @test_musttail_args(i32 %x, i32 %y, i32 %z) { +; MIPS32-LABEL: test_musttail_args: +; MIPS32: # %bb.0: +; MIPS32-NEXT: j callee_args +; MIPS32-NEXT: nop +; +; MIPS64-LABEL: test_musttail_args: +; MIPS64: # %bb.0: +; MIPS64-NEXT: j callee_args +; MIPS64-NEXT: nop + %ret = musttail call i32 @callee_args(i32 %x, i32 %y, i32 %z) + ret i32 %ret +} >From 9a77adf5a2d1217ac419413c06480f92998dafe8 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic <[email protected]> Date: Tue, 7 Oct 2025 10:04:21 +0200 Subject: [PATCH 02/11] Address comments --- llvm/lib/Target/Mips/Mips16ISelLowering.cpp | 2 +- llvm/lib/Target/Mips/Mips16ISelLowering.h | 3 ++- llvm/lib/Target/Mips/MipsISelLowering.cpp | 19 +++++++++++-------- llvm/lib/Target/Mips/MipsISelLowering.h | 3 ++- llvm/lib/Target/Mips/MipsSEISelLowering.cpp | 4 ++-- llvm/lib/Target/Mips/MipsSEISelLowering.h | 3 ++- llvm/test/CodeGen/Mips/musttail.ll | 19 ++----------------- 7 files changed, 22 insertions(+), 31 deletions(-) diff --git a/llvm/lib/Target/Mips/Mips16ISelLowering.cpp b/llvm/lib/Target/Mips/Mips16ISelLowering.cpp index 330cb4e0e206f..e7144d1fecfb9 100644 --- a/llvm/lib/Target/Mips/Mips16ISelLowering.cpp +++ b/llvm/lib/Target/Mips/Mips16ISelLowering.cpp @@ -248,7 +248,7 @@ Mips16TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, bool Mips16TargetLowering::isEligibleForTailCallOptimization( const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI) const { + const MipsFunctionInfo &FI, bool IsMustTail) const { // No tail call optimization for mips16. return false; } diff --git a/llvm/lib/Target/Mips/Mips16ISelLowering.h b/llvm/lib/Target/Mips/Mips16ISelLowering.h index f120cbbd24f91..8ac40644dce0a 100644 --- a/llvm/lib/Target/Mips/Mips16ISelLowering.h +++ b/llvm/lib/Target/Mips/Mips16ISelLowering.h @@ -33,7 +33,8 @@ namespace llvm { private: bool isEligibleForTailCallOptimization( const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI) const override; + const MipsFunctionInfo &FI, + bool IsMustTail = false) const override; void setMips16HardFloatLibCalls(); diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp index d79db127f01a2..52a6e65abfd29 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp @@ -3408,15 +3408,18 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // that are part of this compilation unit. bool InternalLinkage = false; bool IsMustTail = CLI.CB && CLI.CB->isMustTailCall(); - if (IsTailCall && !IsMustTail) { + if (IsTailCall) { IsTailCall = isEligibleForTailCallOptimization( - CCInfo, StackSize, *MF.getInfo<MipsFunctionInfo>()); - if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { - InternalLinkage = G->getGlobal()->hasInternalLinkage(); - IsTailCall &= (InternalLinkage || G->getGlobal()->hasLocalLinkage() || - G->getGlobal()->hasPrivateLinkage() || - G->getGlobal()->hasHiddenVisibility() || - G->getGlobal()->hasProtectedVisibility()); + CCInfo, StackSize, *MF.getInfo<MipsFunctionInfo>(), IsMustTail); + if (IsTailCall) { + if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { + InternalLinkage = G->getGlobal()->hasInternalLinkage(); + IsTailCall &= (InternalLinkage || G->getGlobal()->hasLocalLinkage() || + G->getGlobal()->isDSOLocal() || + G->getGlobal()->hasPrivateLinkage() || + G->getGlobal()->hasHiddenVisibility() || + G->getGlobal()->hasProtectedVisibility()); + } } } if (!IsTailCall && IsMustTail) diff --git a/llvm/lib/Target/Mips/MipsISelLowering.h b/llvm/lib/Target/Mips/MipsISelLowering.h index c65c76ccffc75..3189f94046b35 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.h +++ b/llvm/lib/Target/Mips/MipsISelLowering.h @@ -598,7 +598,8 @@ class TargetRegisterClass; virtual bool isEligibleForTailCallOptimization(const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI) const = 0; + const MipsFunctionInfo &FI, + bool IsMustTail = false) const = 0; /// copyByValArg - Copy argument registers which were used to pass a byval /// argument to the stack. Create a stack frame object for the byval diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp index 71a70d9c2dd46..33e31bb06e50f 100644 --- a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp @@ -1180,8 +1180,8 @@ MipsSETargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, bool MipsSETargetLowering::isEligibleForTailCallOptimization( const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI) const { - if (!UseMipsTailCalls) + const MipsFunctionInfo &FI, bool IsMustTail) const { + if (!UseMipsTailCalls && !IsMustTail) return false; // Exception has to be cleared with eret. diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.h b/llvm/lib/Target/Mips/MipsSEISelLowering.h index 675131aefb6dd..7230e9cff6efa 100644 --- a/llvm/lib/Target/Mips/MipsSEISelLowering.h +++ b/llvm/lib/Target/Mips/MipsSEISelLowering.h @@ -65,7 +65,8 @@ class TargetRegisterClass; private: bool isEligibleForTailCallOptimization( const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI) const override; + const MipsFunctionInfo &FI, + bool IsMustTail = false) const override; void getOpndList(SmallVectorImpl<SDValue> &Ops, diff --git a/llvm/test/CodeGen/Mips/musttail.ll b/llvm/test/CodeGen/Mips/musttail.ll index d5f457f6eb665..48d3d7178b203 100644 --- a/llvm/test/CodeGen/Mips/musttail.ll +++ b/llvm/test/CodeGen/Mips/musttail.ll @@ -4,25 +4,10 @@ ; Test musttail support for MIPS -declare void @external_func() - -; Test basic musttail with external function -define void @test_musttail_external() { -; MIPS32-LABEL: test_musttail_external: -; MIPS32: # %bb.0: -; MIPS32-NEXT: j external_func -; MIPS32-NEXT: nop -; -; MIPS64-LABEL: test_musttail_external: -; MIPS64: # %bb.0: -; MIPS64-NEXT: j external_func -; MIPS64-NEXT: nop - musttail call void @external_func() - ret void +define dso_local i32 @callee_args(i32 %a, i32 %b, i32 %c) { + ret i32 %a; } -declare i32 @callee_args(i32 %a, i32 %b, i32 %c) - define i32 @test_musttail_args(i32 %x, i32 %y, i32 %z) { ; MIPS32-LABEL: test_musttail_args: ; MIPS32: # %bb.0: >From 1e26120441a1278257561a7f10cd18056368ffca Mon Sep 17 00:00:00 2001 From: Djordje Todorovic <[email protected]> Date: Tue, 7 Oct 2025 10:13:39 +0200 Subject: [PATCH 03/11] Apply clang-format --- llvm/lib/Target/Mips/Mips16ISelLowering.cpp | 4 ++-- llvm/lib/Target/Mips/Mips16ISelLowering.h | 3 +-- llvm/lib/Target/Mips/MipsISelLowering.h | 8 +++----- llvm/lib/Target/Mips/MipsSEISelLowering.cpp | 4 ++-- llvm/lib/Target/Mips/MipsSEISelLowering.h | 3 +-- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/llvm/lib/Target/Mips/Mips16ISelLowering.cpp b/llvm/lib/Target/Mips/Mips16ISelLowering.cpp index e7144d1fecfb9..72019008bbd1c 100644 --- a/llvm/lib/Target/Mips/Mips16ISelLowering.cpp +++ b/llvm/lib/Target/Mips/Mips16ISelLowering.cpp @@ -247,8 +247,8 @@ Mips16TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, } bool Mips16TargetLowering::isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI, bool IsMustTail) const { + const CCState &CCInfo, unsigned NextStackOffset, const MipsFunctionInfo &FI, + bool IsMustTail) const { // No tail call optimization for mips16. return false; } diff --git a/llvm/lib/Target/Mips/Mips16ISelLowering.h b/llvm/lib/Target/Mips/Mips16ISelLowering.h index 8ac40644dce0a..6d0b909b748de 100644 --- a/llvm/lib/Target/Mips/Mips16ISelLowering.h +++ b/llvm/lib/Target/Mips/Mips16ISelLowering.h @@ -33,8 +33,7 @@ namespace llvm { private: bool isEligibleForTailCallOptimization( const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI, - bool IsMustTail = false) const override; + const MipsFunctionInfo &FI, bool IsMustTail = false) const override; void setMips16HardFloatLibCalls(); diff --git a/llvm/lib/Target/Mips/MipsISelLowering.h b/llvm/lib/Target/Mips/MipsISelLowering.h index 3189f94046b35..c8e0de89639a8 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.h +++ b/llvm/lib/Target/Mips/MipsISelLowering.h @@ -595,11 +595,9 @@ class TargetRegisterClass; /// isEligibleForTailCallOptimization - Check whether the call is eligible /// for tail call optimization. - virtual bool - isEligibleForTailCallOptimization(const CCState &CCInfo, - unsigned NextStackOffset, - const MipsFunctionInfo &FI, - bool IsMustTail = false) const = 0; + virtual bool isEligibleForTailCallOptimization( + const CCState &CCInfo, unsigned NextStackOffset, + const MipsFunctionInfo &FI, bool IsMustTail = false) const = 0; /// copyByValArg - Copy argument registers which were used to pass a byval /// argument to the stack. Create a stack frame object for the byval diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp index 33e31bb06e50f..cb6bd9c385519 100644 --- a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp @@ -1179,8 +1179,8 @@ MipsSETargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, } bool MipsSETargetLowering::isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI, bool IsMustTail) const { + const CCState &CCInfo, unsigned NextStackOffset, const MipsFunctionInfo &FI, + bool IsMustTail) const { if (!UseMipsTailCalls && !IsMustTail) return false; diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.h b/llvm/lib/Target/Mips/MipsSEISelLowering.h index 7230e9cff6efa..9a44957027dee 100644 --- a/llvm/lib/Target/Mips/MipsSEISelLowering.h +++ b/llvm/lib/Target/Mips/MipsSEISelLowering.h @@ -65,8 +65,7 @@ class TargetRegisterClass; private: bool isEligibleForTailCallOptimization( const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI, - bool IsMustTail = false) const override; + const MipsFunctionInfo &FI, bool IsMustTail = false) const override; void getOpndList(SmallVectorImpl<SDValue> &Ops, >From 20d634f42afeb918f1a12422e9fb4d55550ecd70 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic <[email protected]> Date: Fri, 17 Oct 2025 18:31:27 +0200 Subject: [PATCH 04/11] address comments --- llvm/lib/Target/Mips/Mips16ISelLowering.h | 2 +- llvm/lib/Target/Mips/MipsISelLowering.cpp | 13 ++-- llvm/lib/Target/Mips/MipsISelLowering.h | 2 +- llvm/lib/Target/Mips/MipsSEISelLowering.h | 2 +- llvm/test/CodeGen/Mips/musttail.ll | 74 +++++++++++++++++++++++ 5 files changed, 84 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Target/Mips/Mips16ISelLowering.h b/llvm/lib/Target/Mips/Mips16ISelLowering.h index 6d0b909b748de..ea6f049393327 100644 --- a/llvm/lib/Target/Mips/Mips16ISelLowering.h +++ b/llvm/lib/Target/Mips/Mips16ISelLowering.h @@ -33,7 +33,7 @@ namespace llvm { private: bool isEligibleForTailCallOptimization( const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI, bool IsMustTail = false) const override; + const MipsFunctionInfo &FI, bool IsMustTail) const override; void setMips16HardFloatLibCalls(); diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp index 52a6e65abfd29..92404effe3896 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp @@ -3404,19 +3404,20 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall()) CSInfo = MachineFunction::CallSiteInfo(*CB); - // Check if it's really possible to do a tail call. Restrict it to functions - // that are part of this compilation unit. + // Check if it's really possible to do a tail call. + // For non-musttail calls, restrict to functions that won't require $gp + // restoration. In PIC mode, calling external functions via tail call can + // cause issues with $gp register handling (see D24763). bool InternalLinkage = false; bool IsMustTail = CLI.CB && CLI.CB->isMustTailCall(); if (IsTailCall) { IsTailCall = isEligibleForTailCallOptimization( CCInfo, StackSize, *MF.getInfo<MipsFunctionInfo>(), IsMustTail); - if (IsTailCall) { + // For non-musttail calls, restrict to local or non-interposable functions + if (IsTailCall && !IsMustTail) { if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { InternalLinkage = G->getGlobal()->hasInternalLinkage(); - IsTailCall &= (InternalLinkage || G->getGlobal()->hasLocalLinkage() || - G->getGlobal()->isDSOLocal() || - G->getGlobal()->hasPrivateLinkage() || + IsTailCall &= (G->getGlobal()->hasLocalLinkage() || G->getGlobal()->hasHiddenVisibility() || G->getGlobal()->hasProtectedVisibility()); } diff --git a/llvm/lib/Target/Mips/MipsISelLowering.h b/llvm/lib/Target/Mips/MipsISelLowering.h index c8e0de89639a8..866b2d36fb893 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.h +++ b/llvm/lib/Target/Mips/MipsISelLowering.h @@ -597,7 +597,7 @@ class TargetRegisterClass; /// for tail call optimization. virtual bool isEligibleForTailCallOptimization( const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI, bool IsMustTail = false) const = 0; + const MipsFunctionInfo &FI, bool IsMustTail) const = 0; /// copyByValArg - Copy argument registers which were used to pass a byval /// argument to the stack. Create a stack frame object for the byval diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.h b/llvm/lib/Target/Mips/MipsSEISelLowering.h index 9a44957027dee..978abd4490c39 100644 --- a/llvm/lib/Target/Mips/MipsSEISelLowering.h +++ b/llvm/lib/Target/Mips/MipsSEISelLowering.h @@ -65,7 +65,7 @@ class TargetRegisterClass; private: bool isEligibleForTailCallOptimization( const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI, bool IsMustTail = false) const override; + const MipsFunctionInfo &FI, bool IsMustTail) const override; void getOpndList(SmallVectorImpl<SDValue> &Ops, diff --git a/llvm/test/CodeGen/Mips/musttail.ll b/llvm/test/CodeGen/Mips/musttail.ll index 48d3d7178b203..0bcce8882d6b0 100644 --- a/llvm/test/CodeGen/Mips/musttail.ll +++ b/llvm/test/CodeGen/Mips/musttail.ll @@ -21,3 +21,77 @@ define i32 @test_musttail_args(i32 %x, i32 %y, i32 %z) { %ret = musttail call i32 @callee_args(i32 %x, i32 %y, i32 %z) ret i32 %ret } + +; Test musttail with many arguments that spill to stack (involves memory) +; MIPS O32 ABI: first 4 args in $a0-$a3, rest on stack +declare i32 @many_args_callee(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) + +define i32 @test_musttail_many_args(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) { +; MIPS32-LABEL: test_musttail_many_args: +; MIPS32: # %bb.0: +; MIPS32-NEXT: lw $1, 24($sp) +; MIPS32-NEXT: lw $2, 20($sp) +; MIPS32-NEXT: lw $3, 16($sp) +; MIPS32-NEXT: sw $3, 16($sp) +; MIPS32-NEXT: sw $2, 20($sp) +; MIPS32-NEXT: sw $1, 24($sp) +; MIPS32-NEXT: lw $1, 28($sp) +; MIPS32-NEXT: j many_args_callee +; MIPS32-NEXT: sw $1, 28($sp) +; +; MIPS64-LABEL: test_musttail_many_args: +; MIPS64: # %bb.0: +; MIPS64-NEXT: j many_args_callee +; MIPS64-NEXT: nop + %ret = musttail call i32 @many_args_callee(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) + ret i32 %ret +} + +; Test musttail with large struct passed by value (involves memory) +%struct.large = type { i32, i32, i32, i32, i32, i32, i32, i32 } + +declare i32 @callee_with_struct(%struct.large %s, i32 %x) + +define i32 @test_musttail_struct(%struct.large %s, i32 %x) { +; MIPS32-LABEL: test_musttail_struct: +; MIPS32: # %bb.0: +; MIPS32-NEXT: lw $1, 28($sp) +; MIPS32-NEXT: lw $2, 24($sp) +; MIPS32-NEXT: lw $3, 20($sp) +; MIPS32-NEXT: lw $8, 16($sp) +; MIPS32-NEXT: sw $8, 16($sp) +; MIPS32-NEXT: sw $3, 20($sp) +; MIPS32-NEXT: sw $2, 24($sp) +; MIPS32-NEXT: sw $1, 28($sp) +; MIPS32-NEXT: lw $1, 32($sp) +; MIPS32-NEXT: j callee_with_struct +; MIPS32-NEXT: sw $1, 32($sp) +; +; MIPS64-LABEL: test_musttail_struct: +; MIPS64: # %bb.0: +; MIPS64-NEXT: ld $1, 0($sp) +; MIPS64-NEXT: j callee_with_struct +; MIPS64-NEXT: sd $1, 0($sp) + %ret = musttail call i32 @callee_with_struct(%struct.large %s, i32 %x) + ret i32 %ret +} + +; Test musttail with mixed int and float arguments that use stack +declare float @mixed_args_callee(i32 %a, float %b, i32 %c, float %d, i32 %e, float %f) + +define float @test_musttail_mixed_args(i32 %a, float %b, i32 %c, float %d, i32 %e, float %f) { +; MIPS32-LABEL: test_musttail_mixed_args: +; MIPS32: # %bb.0: +; MIPS32-NEXT: lw $1, 16($sp) +; MIPS32-NEXT: sw $1, 16($sp) +; MIPS32-NEXT: lwc1 $f0, 20($sp) +; MIPS32-NEXT: j mixed_args_callee +; MIPS32-NEXT: swc1 $f0, 20($sp) +; +; MIPS64-LABEL: test_musttail_mixed_args: +; MIPS64: # %bb.0: +; MIPS64-NEXT: j mixed_args_callee +; MIPS64-NEXT: nop + %ret = musttail call float @mixed_args_callee(i32 %a, float %b, i32 %c, float %d, i32 %e, float %f) + ret float %ret +} >From eaa5678dfed669abb8d733b2243dc1ced89d73f0 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic <[email protected]> Date: Sat, 18 Oct 2025 20:40:58 +0200 Subject: [PATCH 05/11] Clean up and improve tests --- llvm/lib/Target/Mips/MipsISelLowering.cpp | 3 +-- llvm/lib/Target/Mips/MipsSEISelLowering.h | 7 ++++--- llvm/test/CodeGen/Mips/musttail.ll | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp index 92404effe3896..262c2212b1a4f 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp @@ -3408,7 +3408,6 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // For non-musttail calls, restrict to functions that won't require $gp // restoration. In PIC mode, calling external functions via tail call can // cause issues with $gp register handling (see D24763). - bool InternalLinkage = false; bool IsMustTail = CLI.CB && CLI.CB->isMustTailCall(); if (IsTailCall) { IsTailCall = isEligibleForTailCallOptimization( @@ -3416,7 +3415,6 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // For non-musttail calls, restrict to local or non-interposable functions if (IsTailCall && !IsMustTail) { if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { - InternalLinkage = G->getGlobal()->hasInternalLinkage(); IsTailCall &= (G->getGlobal()->hasLocalLinkage() || G->getGlobal()->hasHiddenVisibility() || G->getGlobal()->hasProtectedVisibility()); @@ -3600,6 +3598,7 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, } } + bool InternalLinkage = false; if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { if (Subtarget.isTargetCOFF() && G->getGlobal()->hasDLLImportStorageClass()) { diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.h b/llvm/lib/Target/Mips/MipsSEISelLowering.h index 978abd4490c39..392e8dc3dbb11 100644 --- a/llvm/lib/Target/Mips/MipsSEISelLowering.h +++ b/llvm/lib/Target/Mips/MipsSEISelLowering.h @@ -63,9 +63,10 @@ class TargetRegisterClass; const TargetRegisterClass *getRepRegClassFor(MVT VT) const override; private: - bool isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI, bool IsMustTail) const override; + bool isEligibleForTailCallOptimization(const CCState &CCInfo, + unsigned NextStackOffset, + const MipsFunctionInfo &FI, + bool IsMustTail) const override; void getOpndList(SmallVectorImpl<SDValue> &Ops, diff --git a/llvm/test/CodeGen/Mips/musttail.ll b/llvm/test/CodeGen/Mips/musttail.ll index 0bcce8882d6b0..c486ea5c59eea 100644 --- a/llvm/test/CodeGen/Mips/musttail.ll +++ b/llvm/test/CodeGen/Mips/musttail.ll @@ -95,3 +95,20 @@ define float @test_musttail_mixed_args(i32 %a, float %b, i32 %c, float %d, i32 % %ret = musttail call float @mixed_args_callee(i32 %a, float %b, i32 %c, float %d, i32 %e, float %f) ret float %ret } + +; Test musttail with indirect call +define i32 @test_musttail_fptr(ptr %fptr, i32 %x) { +; MIPS32-LABEL: test_musttail_fptr: +; MIPS32: # %bb.0: +; MIPS32-NEXT: move $25, $4 +; MIPS32-NEXT: jr $25 +; MIPS32-NEXT: nop +; +; MIPS64-LABEL: test_musttail_fptr: +; MIPS64: # %bb.0: +; MIPS64-NEXT: move $25, $4 +; MIPS64-NEXT: jr $25 +; MIPS64-NEXT: nop + %ret = musttail call i32 %fptr(ptr %fptr, i32 %x) + ret i32 %ret +} >From 54412fe1481c39b8537ae847a9b056ed726b2c88 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic <[email protected]> Date: Sun, 19 Oct 2025 08:50:16 +0200 Subject: [PATCH 06/11] apply clang-format --- llvm/lib/Target/Mips/MipsISelLowering.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/Mips/MipsISelLowering.h b/llvm/lib/Target/Mips/MipsISelLowering.h index 866b2d36fb893..3aa0a0d3ad7c4 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.h +++ b/llvm/lib/Target/Mips/MipsISelLowering.h @@ -595,9 +595,10 @@ class TargetRegisterClass; /// isEligibleForTailCallOptimization - Check whether the call is eligible /// for tail call optimization. - virtual bool isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI, bool IsMustTail) const = 0; + virtual bool isEligibleForTailCallOptimization(const CCState &CCInfo, + unsigned NextStackOffset, + const MipsFunctionInfo &FI, + bool IsMustTail) const = 0; /// copyByValArg - Copy argument registers which were used to pass a byval /// argument to the stack. Create a stack frame object for the byval >From b9e145d26aedadbfcc38a9ce8f97a5c7df425107 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic <[email protected]> Date: Sun, 19 Oct 2025 08:59:28 +0200 Subject: [PATCH 07/11] apply clang-format on Mips16ISelLowering.h --- llvm/lib/Target/Mips/Mips16ISelLowering.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/Mips/Mips16ISelLowering.h b/llvm/lib/Target/Mips/Mips16ISelLowering.h index ea6f049393327..5281a62791590 100644 --- a/llvm/lib/Target/Mips/Mips16ISelLowering.h +++ b/llvm/lib/Target/Mips/Mips16ISelLowering.h @@ -31,9 +31,10 @@ namespace llvm { MachineBasicBlock *MBB) const override; private: - bool isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI, bool IsMustTail) const override; + bool isEligibleForTailCallOptimization(const CCState &CCInfo, + unsigned NextStackOffset, + const MipsFunctionInfo &FI, + bool IsMustTail) const override; void setMips16HardFloatLibCalls(); >From e4a399181f57004006b7db75df7da0e5e5c01a92 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic <[email protected]> Date: Tue, 28 Oct 2025 15:38:13 +0100 Subject: [PATCH 08/11] Align with PPC solution --- .../clang/Basic/DiagnosticCommonKinds.td | 5 +++ clang/lib/CodeGen/CGCall.cpp | 28 ++++++++++++++- clang/lib/CodeGen/CodeGenModule.cpp | 34 ++++++++++++++----- clang/test/CodeGen/Mips/musttail.c | 19 +++++++++++ llvm/lib/Target/Mips/MipsISelLowering.cpp | 25 ++++++++++---- llvm/test/CodeGen/Mips/musttail.ll | 12 +++++-- 6 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 clang/test/CodeGen/Mips/musttail.c diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index 0bd8a423c393e..c744378ae8ec0 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -372,6 +372,11 @@ def err_ppc_impossible_musttail: Error< "indirect calls cannot be tail called on PPC|" "external calls cannot be tail called on PPC}0" >; +def err_mips_impossible_musttail: Error< + "'musttail' attribute for this call is impossible because %select{" + "the MIPS16 ABI does not support tail calls|" + "calls outside the current linkage unit cannot be tail called on MIPS}0" + >; def err_aix_musttail_unsupported: Error< "'musttail' attribute is not supported on AIX">; diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index d9bd443455e0f..5a94db834342d 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -31,6 +31,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/CodeGen/SwiftCallingConv.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/Assumptions.h" @@ -5954,11 +5955,20 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, AddObjCARCExceptionMetadata(CI); // Set tail call kind if necessary. + bool IsPPC = getTarget().getTriple().isPPC(); + bool IsMIPS = getTarget().getTriple().isMIPS(); + bool HasMips16 = false; + if (IsMIPS) { + const TargetOptions &TargetOpts = getTarget().getTargetOpts(); + HasMips16 = TargetOpts.FeatureMap.lookup("mips16"); + if (!HasMips16) + HasMips16 = llvm::is_contained(TargetOpts.Features, "+mips16"); + } if (llvm::CallInst *Call = dyn_cast<llvm::CallInst>(CI)) { if (TargetDecl && TargetDecl->hasAttr<NotTailCalledAttr>()) Call->setTailCallKind(llvm::CallInst::TCK_NoTail); else if (IsMustTail) { - if (getTarget().getTriple().isPPC()) { + if (IsPPC) { if (getTarget().getTriple().isOSAIX()) CGM.getDiags().Report(Loc, diag::err_aix_musttail_unsupported); else if (!getTarget().hasFeature("pcrelative-memops")) { @@ -5984,6 +5994,22 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } } } + if (IsMIPS) { + if (HasMips16) + CGM.getDiags().Report(Loc, diag::err_mips_impossible_musttail) << 0; + else if (const auto *FD = dyn_cast_or_null<FunctionDecl>(TargetDecl)) { + if (!FD->isDefined()) { + CGM.addUndefinedGlobalForTailCall({FD, Loc}); + } else { + llvm::GlobalValue::LinkageTypes Linkage = + CGM.getFunctionLinkage(GlobalDecl(FD)); + if (!llvm::GlobalValue::isLocalLinkage(Linkage) && + FD->isExternallyVisible()) + CGM.getDiags().Report(Loc, diag::err_mips_impossible_musttail) + << 1; + } + } + } Call->setTailCallKind(llvm::CallInst::TCK_MustTail); } } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 834b1c067d84c..11a71bf7f769b 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1561,16 +1561,32 @@ void CodeGenModule::Release() { setVisibilityFromDLLStorageClass(LangOpts, getModule()); // Check the tail call symbols are truly undefined. - if (getTriple().isPPC() && !MustTailCallUndefinedGlobals.empty()) { - for (auto &I : MustTailCallUndefinedGlobals) { - if (!I.first->isDefined()) - getDiags().Report(I.second, diag::err_ppc_impossible_musttail) << 2; - else { - StringRef MangledName = getMangledName(GlobalDecl(I.first)); - llvm::GlobalValue *Entry = GetGlobalValue(MangledName); - if (!Entry || Entry->isWeakForLinker() || - Entry->isDeclarationForLinker()) + if (!MustTailCallUndefinedGlobals.empty()) { + if (getTriple().isPPC()) { + for (auto &I : MustTailCallUndefinedGlobals) { + if (!I.first->isDefined()) getDiags().Report(I.second, diag::err_ppc_impossible_musttail) << 2; + else { + StringRef MangledName = getMangledName(GlobalDecl(I.first)); + llvm::GlobalValue *Entry = GetGlobalValue(MangledName); + if (!Entry || Entry->isWeakForLinker() || + Entry->isDeclarationForLinker()) + getDiags().Report(I.second, diag::err_ppc_impossible_musttail) << 2; + } + } + } else if (getTriple().isMIPS()) { + for (auto &I : MustTailCallUndefinedGlobals) { + const FunctionDecl *FD = I.first; + const FunctionDecl *Definition = FD->getDefinition(); + if (!Definition) { + getDiags().Report(I.second, diag::err_mips_impossible_musttail) << 1; + continue; + } + llvm::GlobalValue::LinkageTypes Linkage = + getFunctionLinkage(GlobalDecl(Definition)); + if (!llvm::GlobalValue::isLocalLinkage(Linkage) && + Definition->isExternallyVisible()) + getDiags().Report(I.second, diag::err_mips_impossible_musttail) << 1; } } } diff --git a/clang/test/CodeGen/Mips/musttail.c b/clang/test/CodeGen/Mips/musttail.c new file mode 100644 index 0000000000000..c0c0132e7f82f --- /dev/null +++ b/clang/test/CodeGen/Mips/musttail.c @@ -0,0 +1,19 @@ +// RUN: not %clang_cc1 %s -triple mipsel-unknown-linux-gnu -emit-llvm 2>&1 \ +// RUN: | FileCheck %s +// RUN: not %clang_cc1 %s -triple mipsel-unknown-linux-gnu -target-feature +mips16 \ +// RUN: -emit-llvm 2>&1 | FileCheck %s --check-prefix=CHECK-MIPS16 + +// CHECK: error: 'musttail' attribute for this call is impossible because calls outside the current linkage unit cannot be tail called on MIPS +// CHECK-MIPS16: error: 'musttail' attribute for this call is impossible because the MIPS16 ABI does not support tail calls + +static int local(int x) { return x; } + +int call_local(int x) { + [[clang::musttail]] return local(x); +} + +extern int external(int x); + +int call_external(int x) { + [[clang::musttail]] return external(x); +} diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp index 262c2212b1a4f..7dbbaf1b9371f 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp @@ -3409,15 +3409,28 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // restoration. In PIC mode, calling external functions via tail call can // cause issues with $gp register handling (see D24763). bool IsMustTail = CLI.CB && CLI.CB->isMustTailCall(); + bool CalleeIsLocal = true; + if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { + const GlobalValue *GV = G->getGlobal(); + bool HasLocalLinkage = GV->hasLocalLinkage() || GV->hasPrivateLinkage(); + bool HasHiddenVisibility = GV->hasHiddenVisibility() || + GV->hasProtectedVisibility(); + if (GV->isDeclarationForLinker()) + CalleeIsLocal = HasLocalLinkage || HasHiddenVisibility; + else + CalleeIsLocal = GV->isDSOLocal(); + } + if (IsTailCall) { IsTailCall = isEligibleForTailCallOptimization( CCInfo, StackSize, *MF.getInfo<MipsFunctionInfo>(), IsMustTail); - // For non-musttail calls, restrict to local or non-interposable functions - if (IsTailCall && !IsMustTail) { - if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { - IsTailCall &= (G->getGlobal()->hasLocalLinkage() || - G->getGlobal()->hasHiddenVisibility() || - G->getGlobal()->hasProtectedVisibility()); + if (IsTailCall) { + if (IsMustTail) { + if (!CalleeIsLocal) + report_fatal_error("failed to perform tail call elimination on a call " + "site marked musttail"); + } else { + IsTailCall &= CalleeIsLocal; } } } diff --git a/llvm/test/CodeGen/Mips/musttail.ll b/llvm/test/CodeGen/Mips/musttail.ll index c486ea5c59eea..17a55c11bf669 100644 --- a/llvm/test/CodeGen/Mips/musttail.ll +++ b/llvm/test/CodeGen/Mips/musttail.ll @@ -24,7 +24,9 @@ define i32 @test_musttail_args(i32 %x, i32 %y, i32 %z) { ; Test musttail with many arguments that spill to stack (involves memory) ; MIPS O32 ABI: first 4 args in $a0-$a3, rest on stack -declare i32 @many_args_callee(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) +define hidden i32 @many_args_callee(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) { + ret i32 %a +} define i32 @test_musttail_many_args(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) { ; MIPS32-LABEL: test_musttail_many_args: @@ -50,7 +52,9 @@ define i32 @test_musttail_many_args(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 ; Test musttail with large struct passed by value (involves memory) %struct.large = type { i32, i32, i32, i32, i32, i32, i32, i32 } -declare i32 @callee_with_struct(%struct.large %s, i32 %x) +define hidden i32 @callee_with_struct(%struct.large %s, i32 %x) { + ret i32 %x +} define i32 @test_musttail_struct(%struct.large %s, i32 %x) { ; MIPS32-LABEL: test_musttail_struct: @@ -77,7 +81,9 @@ define i32 @test_musttail_struct(%struct.large %s, i32 %x) { } ; Test musttail with mixed int and float arguments that use stack -declare float @mixed_args_callee(i32 %a, float %b, i32 %c, float %d, i32 %e, float %f) +define hidden float @mixed_args_callee(i32 %a, float %b, i32 %c, float %d, i32 %e, float %f) { + ret float %b +} define float @test_musttail_mixed_args(i32 %a, float %b, i32 %c, float %d, i32 %e, float %f) { ; MIPS32-LABEL: test_musttail_mixed_args: >From a4b960c33bb814f46086b6ee5b43546c570ee20b Mon Sep 17 00:00:00 2001 From: Djordje Todorovic <[email protected]> Date: Wed, 29 Oct 2025 20:07:10 +0100 Subject: [PATCH 09/11] Apply clang-format and simplify code for err reporting --- llvm/lib/Target/Mips/MipsISelLowering.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp index 7dbbaf1b9371f..2b2073099e04a 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp @@ -3413,8 +3413,8 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { const GlobalValue *GV = G->getGlobal(); bool HasLocalLinkage = GV->hasLocalLinkage() || GV->hasPrivateLinkage(); - bool HasHiddenVisibility = GV->hasHiddenVisibility() || - GV->hasProtectedVisibility(); + bool HasHiddenVisibility = + GV->hasHiddenVisibility() || GV->hasProtectedVisibility(); if (GV->isDeclarationForLinker()) CalleeIsLocal = HasLocalLinkage || HasHiddenVisibility; else @@ -3422,21 +3422,15 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, } if (IsTailCall) { - IsTailCall = isEligibleForTailCallOptimization( + bool Eligible = isEligibleForTailCallOptimization( CCInfo, StackSize, *MF.getInfo<MipsFunctionInfo>(), IsMustTail); - if (IsTailCall) { - if (IsMustTail) { - if (!CalleeIsLocal) - report_fatal_error("failed to perform tail call elimination on a call " - "site marked musttail"); - } else { - IsTailCall &= CalleeIsLocal; - } + if (!Eligible || !CalleeIsLocal) { + IsTailCall = false; + if (IsMustTail) + report_fatal_error("failed to perform tail call elimination on a call " + "site marked musttail"); } } - if (!IsTailCall && IsMustTail) - report_fatal_error("failed to perform tail call elimination on a call " - "site marked musttail"); if (IsTailCall) ++NumTailCalls; >From e1e2201ee31e7f22736eece784a558ad96a2d691 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic <[email protected]> Date: Mon, 15 Dec 2025 12:40:26 +0100 Subject: [PATCH 10/11] Align frontend and backend for the musttail checks --- clang/lib/CodeGen/CGCall.cpp | 14 ++-------- clang/lib/CodeGen/CodeGenModule.cpp | 26 ++++++++++++++----- .../Mips/musttail-forward-declaration.c | 15 +++++++++++ clang/test/CodeGen/Mips/musttail-pic.c | 21 +++++++++++++++ clang/test/CodeGen/Mips/musttail-visibility.c | 17 ++++++++++++ clang/test/CodeGen/Mips/musttail-weak.c | 13 ++++++++++ llvm/lib/Target/Mips/Mips16ISelLowering.cpp | 4 +-- llvm/lib/Target/Mips/Mips16ISelLowering.h | 7 +++-- llvm/lib/Target/Mips/MipsISelLowering.cpp | 21 ++++++++++----- llvm/lib/Target/Mips/MipsISelLowering.h | 8 +++--- llvm/lib/Target/Mips/MipsSEISelLowering.cpp | 14 +++------- llvm/lib/Target/Mips/MipsSEISelLowering.h | 7 +++-- 12 files changed, 117 insertions(+), 50 deletions(-) create mode 100644 clang/test/CodeGen/Mips/musttail-forward-declaration.c create mode 100644 clang/test/CodeGen/Mips/musttail-pic.c create mode 100644 clang/test/CodeGen/Mips/musttail-visibility.c create mode 100644 clang/test/CodeGen/Mips/musttail-weak.c diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 5a94db834342d..4163a9b26c828 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5997,18 +5997,8 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (IsMIPS) { if (HasMips16) CGM.getDiags().Report(Loc, diag::err_mips_impossible_musttail) << 0; - else if (const auto *FD = dyn_cast_or_null<FunctionDecl>(TargetDecl)) { - if (!FD->isDefined()) { - CGM.addUndefinedGlobalForTailCall({FD, Loc}); - } else { - llvm::GlobalValue::LinkageTypes Linkage = - CGM.getFunctionLinkage(GlobalDecl(FD)); - if (!llvm::GlobalValue::isLocalLinkage(Linkage) && - FD->isExternallyVisible()) - CGM.getDiags().Report(Loc, diag::err_mips_impossible_musttail) - << 1; - } - } + else if (const auto *FD = dyn_cast_or_null<FunctionDecl>(TargetDecl)) + CGM.addUndefinedGlobalForTailCall({FD, Loc}); } Call->setTailCallKind(llvm::CallInst::TCK_MustTail); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 11a71bf7f769b..f28e7c1a532ca 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1577,15 +1577,27 @@ void CodeGenModule::Release() { } else if (getTriple().isMIPS()) { for (auto &I : MustTailCallUndefinedGlobals) { const FunctionDecl *FD = I.first; - const FunctionDecl *Definition = FD->getDefinition(); - if (!Definition) { - getDiags().Report(I.second, diag::err_mips_impossible_musttail) << 1; + StringRef MangledName = getMangledName(GlobalDecl(FD)); + llvm::GlobalValue *Entry = GetGlobalValue(MangledName); + + if (!Entry) { + Visibility Vis = FD->getVisibility(); + if (Vis == DefaultVisibility) + getDiags().Report(I.second, diag::err_mips_impossible_musttail) << 1; continue; } - llvm::GlobalValue::LinkageTypes Linkage = - getFunctionLinkage(GlobalDecl(Definition)); - if (!llvm::GlobalValue::isLocalLinkage(Linkage) && - Definition->isExternallyVisible()) + + bool CalleeIsLocal; + if (Entry->isDeclarationForLinker()) { + bool HasLocalLinkage = Entry->hasLocalLinkage(); + bool HasHiddenVisibility = + Entry->hasHiddenVisibility() || Entry->hasProtectedVisibility(); + CalleeIsLocal = HasLocalLinkage || HasHiddenVisibility; + } else { + CalleeIsLocal = Entry->isDSOLocal(); + } + + if (!CalleeIsLocal) getDiags().Report(I.second, diag::err_mips_impossible_musttail) << 1; } } diff --git a/clang/test/CodeGen/Mips/musttail-forward-declaration.c b/clang/test/CodeGen/Mips/musttail-forward-declaration.c new file mode 100644 index 0000000000000..b25baa76f451f --- /dev/null +++ b/clang/test/CodeGen/Mips/musttail-forward-declaration.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 %s -triple mipsel-unknown-linux-gnu -o /dev/null -emit-llvm -verify + +// Test that a forward declaration that is later defined in the same TU +// is allowed for musttail calls. + +int func(int i); + +int caller(int i) { + // expected-no-diagnostics + [[clang::musttail]] return func(i); +} + +int func(int i) { + return i + 1; +} diff --git a/clang/test/CodeGen/Mips/musttail-pic.c b/clang/test/CodeGen/Mips/musttail-pic.c new file mode 100644 index 0000000000000..bcad84d63f193 --- /dev/null +++ b/clang/test/CodeGen/Mips/musttail-pic.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 %s -triple mipsel-unknown-linux-gnu -pic-level 2 \ +// RUN: -fhalf-no-semantic-interposition -o /dev/null -emit-llvm -verify + +// Test musttail behavior in PIC mode with semantic interposition. + +int external_defined(int i) { return i; } +static int static_func(int i) { return i; } +__attribute__((visibility("hidden"))) int hidden_defined(int i) { return i; } + +int call_external(int i) { + // expected-error@+1 {{'musttail' attribute for this call is impossible because calls outside the current linkage unit cannot be tail called on MIPS}} + [[clang::musttail]] return external_defined(i); +} + +int call_static(int i) { + [[clang::musttail]] return static_func(i); +} + +int call_hidden(int i) { + [[clang::musttail]] return hidden_defined(i); +} diff --git a/clang/test/CodeGen/Mips/musttail-visibility.c b/clang/test/CodeGen/Mips/musttail-visibility.c new file mode 100644 index 0000000000000..351563e991129 --- /dev/null +++ b/clang/test/CodeGen/Mips/musttail-visibility.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 %s -triple mipsel-unknown-linux-gnu -o /dev/null -emit-llvm -verify + +// Test that hidden and protected visibility functions can be tail called +// because they are guaranteed to be DSO-local. + +extern int hidden_func(int i) __attribute__((visibility("hidden"))); +extern int protected_func(int i) __attribute__((visibility("protected"))); +extern int default_func(int i); + +int call_hidden(int i) { + // expected-no-diagnostics + [[clang::musttail]] return hidden_func(i); +} + +int call_protected(int i) { + [[clang::musttail]] return protected_func(i); +} diff --git a/clang/test/CodeGen/Mips/musttail-weak.c b/clang/test/CodeGen/Mips/musttail-weak.c new file mode 100644 index 0000000000000..06c0726d3d1e1 --- /dev/null +++ b/clang/test/CodeGen/Mips/musttail-weak.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 %s -triple mipsel-unknown-linux-gnu -o /dev/null -emit-llvm -verify + +// Test musttail with weak functions. +// Weak functions can be interposed, so they are not considered DSO-local. + +__attribute__((weak)) int weak_func(int i) { + return i; +} + +int caller(int i) { + // expected-error@+1 {{'musttail' attribute for this call is impossible because calls outside the current linkage unit cannot be tail called on MIPS}} + [[clang::musttail]] return weak_func(i); +} diff --git a/llvm/lib/Target/Mips/Mips16ISelLowering.cpp b/llvm/lib/Target/Mips/Mips16ISelLowering.cpp index 72019008bbd1c..330cb4e0e206f 100644 --- a/llvm/lib/Target/Mips/Mips16ISelLowering.cpp +++ b/llvm/lib/Target/Mips/Mips16ISelLowering.cpp @@ -247,8 +247,8 @@ Mips16TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, } bool Mips16TargetLowering::isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, const MipsFunctionInfo &FI, - bool IsMustTail) const { + const CCState &CCInfo, unsigned NextStackOffset, + const MipsFunctionInfo &FI) const { // No tail call optimization for mips16. return false; } diff --git a/llvm/lib/Target/Mips/Mips16ISelLowering.h b/llvm/lib/Target/Mips/Mips16ISelLowering.h index 5281a62791590..f120cbbd24f91 100644 --- a/llvm/lib/Target/Mips/Mips16ISelLowering.h +++ b/llvm/lib/Target/Mips/Mips16ISelLowering.h @@ -31,10 +31,9 @@ namespace llvm { MachineBasicBlock *MBB) const override; private: - bool isEligibleForTailCallOptimization(const CCState &CCInfo, - unsigned NextStackOffset, - const MipsFunctionInfo &FI, - bool IsMustTail) const override; + bool isEligibleForTailCallOptimization( + const CCState &CCInfo, unsigned NextStackOffset, + const MipsFunctionInfo &FI) const override; void setMips16HardFloatLibCalls(); diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp index 2b2073099e04a..2d4e87b2506d9 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp @@ -88,6 +88,10 @@ NoZeroDivCheck("mno-check-zero-division", cl::Hidden, extern cl::opt<bool> EmitJalrReloc; +static cl::opt<bool> UseMipsTailCalls("mips-tail-calls", cl::Hidden, + cl::desc("MIPS: permit tail calls."), + cl::init(false)); + static const MCPhysReg Mips64DPRegs[8] = { Mips::D12_64, Mips::D13_64, Mips::D14_64, Mips::D15_64, Mips::D16_64, Mips::D17_64, Mips::D18_64, Mips::D19_64 @@ -3422,13 +3426,18 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, } if (IsTailCall) { - bool Eligible = isEligibleForTailCallOptimization( - CCInfo, StackSize, *MF.getInfo<MipsFunctionInfo>(), IsMustTail); - if (!Eligible || !CalleeIsLocal) { + if (!UseMipsTailCalls) { IsTailCall = false; - if (IsMustTail) - report_fatal_error("failed to perform tail call elimination on a call " - "site marked musttail"); + } else { + bool Eligible = isEligibleForTailCallOptimization( + CCInfo, StackSize, *MF.getInfo<MipsFunctionInfo>()); + if (!Eligible || !CalleeIsLocal) { + IsTailCall = false; + if (IsMustTail) + report_fatal_error( + "failed to perform tail call elimination on a call " + "site marked musttail"); + } } } diff --git a/llvm/lib/Target/Mips/MipsISelLowering.h b/llvm/lib/Target/Mips/MipsISelLowering.h index 3aa0a0d3ad7c4..c65c76ccffc75 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.h +++ b/llvm/lib/Target/Mips/MipsISelLowering.h @@ -595,10 +595,10 @@ class TargetRegisterClass; /// isEligibleForTailCallOptimization - Check whether the call is eligible /// for tail call optimization. - virtual bool isEligibleForTailCallOptimization(const CCState &CCInfo, - unsigned NextStackOffset, - const MipsFunctionInfo &FI, - bool IsMustTail) const = 0; + virtual bool + isEligibleForTailCallOptimization(const CCState &CCInfo, + unsigned NextStackOffset, + const MipsFunctionInfo &FI) const = 0; /// copyByValArg - Copy argument registers which were used to pass a byval /// argument to the stack. Create a stack frame object for the byval diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp index cb6bd9c385519..ed57f4f3c7332 100644 --- a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp @@ -51,10 +51,6 @@ using namespace llvm; #define DEBUG_TYPE "mips-isel" -static cl::opt<bool> -UseMipsTailCalls("mips-tail-calls", cl::Hidden, - cl::desc("MIPS: permit tail calls."), cl::init(false)); - static cl::opt<bool> NoDPLoadStore("mno-ldc1-sdc1", cl::init(false), cl::desc("Expand double precision loads and " "stores to their single precision " @@ -1179,11 +1175,8 @@ MipsSETargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, } bool MipsSETargetLowering::isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, const MipsFunctionInfo &FI, - bool IsMustTail) const { - if (!UseMipsTailCalls && !IsMustTail) - return false; - + const CCState &CCInfo, unsigned NextStackOffset, + const MipsFunctionInfo &FI) const { // Exception has to be cleared with eret. if (FI.isISR()) return false; @@ -1192,8 +1185,7 @@ bool MipsSETargetLowering::isEligibleForTailCallOptimization( if (CCInfo.getInRegsParamsCount() > 0 || FI.hasByvalArg()) return false; - // Return true if the callee's argument area is no larger than the - // caller's. + // Return true if the callee's argument area is no larger than the caller's. return NextStackOffset <= FI.getIncomingArgSize(); } diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.h b/llvm/lib/Target/Mips/MipsSEISelLowering.h index 392e8dc3dbb11..6f71225b51111 100644 --- a/llvm/lib/Target/Mips/MipsSEISelLowering.h +++ b/llvm/lib/Target/Mips/MipsSEISelLowering.h @@ -63,10 +63,9 @@ class TargetRegisterClass; const TargetRegisterClass *getRepRegClassFor(MVT VT) const override; private: - bool isEligibleForTailCallOptimization(const CCState &CCInfo, - unsigned NextStackOffset, - const MipsFunctionInfo &FI, - bool IsMustTail) const override; + bool isEligibleForTailCallOptimization( + const CCState &CCInfo, unsigned NextStackOffset, + const MipsFunctionInfo &FI) const override; void getOpndList(SmallVectorImpl<SDValue> &Ops, >From 23ea7a441eede4ee5b4271e002ccebf9734daa64 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic <[email protected]> Date: Tue, 30 Dec 2025 15:24:18 +0100 Subject: [PATCH 11/11] Apply clang-format --- clang/lib/CodeGen/CodeGenModule.cpp | 3 ++- llvm/lib/Target/Mips/MipsSEISelLowering.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index f28e7c1a532ca..0fc40a45b9c98 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1583,7 +1583,8 @@ void CodeGenModule::Release() { if (!Entry) { Visibility Vis = FD->getVisibility(); if (Vis == DefaultVisibility) - getDiags().Report(I.second, diag::err_mips_impossible_musttail) << 1; + getDiags().Report(I.second, diag::err_mips_impossible_musttail) + << 1; continue; } diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.h b/llvm/lib/Target/Mips/MipsSEISelLowering.h index 6f71225b51111..675131aefb6dd 100644 --- a/llvm/lib/Target/Mips/MipsSEISelLowering.h +++ b/llvm/lib/Target/Mips/MipsSEISelLowering.h @@ -64,8 +64,8 @@ class TargetRegisterClass; private: bool isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI) const override; + const CCState &CCInfo, unsigned NextStackOffset, + const MipsFunctionInfo &FI) const override; void getOpndList(SmallVectorImpl<SDValue> &Ops, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
