Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions llvm/lib/Target/Mips/Mips16ISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ Mips16TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
}

bool Mips16TargetLowering::isEligibleForTailCallOptimization(
const CCState &CCInfo, unsigned NextStackOffset,
const MipsFunctionInfo &FI) const {
const CCState &CCInfo, unsigned NextStackOffset, const MipsFunctionInfo &FI,
bool IsMustTail) const {
// No tail call optimization for mips16.
return false;
}
Expand Down
7 changes: 4 additions & 3 deletions llvm/lib/Target/Mips/Mips16ISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ namespace llvm {
MachineBasicBlock *MBB) const override;

private:
bool isEligibleForTailCallOptimization(
const CCState &CCInfo, unsigned NextStackOffset,
const MipsFunctionInfo &FI) const override;
bool isEligibleForTailCallOptimization(const CCState &CCInfo,
unsigned NextStackOffset,
const MipsFunctionInfo &FI,
bool IsMustTail) const override;

void setMips16HardFloatLibCalls();

Expand Down
28 changes: 16 additions & 12 deletions llvm/lib/Target/Mips/MipsISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3404,21 +3404,24 @@ 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.
bool InternalLinkage = false;
// 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 IsMustTail = CLI.CB && CLI.CB->isMustTailCall();
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);
// 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 && CLI.CB && CLI.CB->isMustTailCall())
if (!IsTailCall && IsMustTail)
report_fatal_error("failed to perform tail call elimination on a call "
"site marked musttail");

Expand Down Expand Up @@ -3595,6 +3598,7 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
}
}

bool InternalLinkage = false;
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) {
if (Subtarget.isTargetCOFF() &&
G->getGlobal()->hasDLLImportStorageClass()) {
Expand Down
8 changes: 4 additions & 4 deletions llvm/lib/Target/Mips/MipsISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) 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
Expand Down
6 changes: 3 additions & 3 deletions llvm/lib/Target/Mips/MipsSEISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1179,9 +1179,9 @@ MipsSETargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
}

bool MipsSETargetLowering::isEligibleForTailCallOptimization(
const CCState &CCInfo, unsigned NextStackOffset,
const MipsFunctionInfo &FI) const {
if (!UseMipsTailCalls)
const CCState &CCInfo, unsigned NextStackOffset, const MipsFunctionInfo &FI,
bool IsMustTail) const {
if (!UseMipsTailCalls && !IsMustTail)
return false;

// Exception has to be cleared with eret.
Expand Down
7 changes: 4 additions & 3 deletions llvm/lib/Target/Mips/MipsSEISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ class TargetRegisterClass;
const TargetRegisterClass *getRepRegClassFor(MVT VT) const override;

private:
bool isEligibleForTailCallOptimization(
const CCState &CCInfo, unsigned NextStackOffset,
const MipsFunctionInfo &FI) const override;
bool isEligibleForTailCallOptimization(const CCState &CCInfo,
unsigned NextStackOffset,
const MipsFunctionInfo &FI,
bool IsMustTail) const override;

void
getOpndList(SmallVectorImpl<SDValue> &Ops,
Expand Down
114 changes: 114 additions & 0 deletions llvm/test/CodeGen/Mips/musttail.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
; 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

define dso_local i32 @callee_args(i32 %a, i32 %b, i32 %c) {
ret i32 %a;
}

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
}

; 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
}

; 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
}