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 1/9] [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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 6/9] 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 7/9] 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 8/9] 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 9/9] 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; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
