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
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticCommonKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;

Expand Down
28 changes: 27 additions & 1 deletion clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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")) {
Expand All @@ -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 =
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditions used here differ from the conditions used in MipsISelLowering. This causes it to be inconsistent and results in the error I provided in conversations.

Locally, I used this which still isn't correct but seems to work more consistently:

bool HasLocalLinkage =
  llvm::GlobalValue::isLocalLinkage(Linkage) || llvm::GlobalValue::isPrivateLinkage(Linkage);
if (!HasLocalLinkage || llvm::GlobalValue::isWeakForLinker(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);
}
}
Expand Down
34 changes: 25 additions & 9 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in CGCall.cpp, the conditions used here differ from the conditions used in MipsISelLowering.

getFunctionLinkage(GlobalDecl(Definition));
if (!llvm::GlobalValue::isLocalLinkage(Linkage) &&
Definition->isExternallyVisible())
getDiags().Report(I.second, diag::err_mips_impossible_musttail) << 1;
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions clang/test/CodeGen/Mips/musttail.c
Original file line number Diff line number Diff line change
@@ -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);
}
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
41 changes: 26 additions & 15 deletions llvm/lib/Target/Mips/MipsISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3404,23 +3404,33 @@ 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();
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>());
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());
}
bool Eligible = isEligibleForTailCallOptimization(
CCInfo, StackSize, *MF.getInfo<MipsFunctionInfo>(), IsMustTail);
if (!Eligible || !CalleeIsLocal) {
IsTailCall = false;
if (IsMustTail)
report_fatal_error("failed to perform tail call elimination on a call "
"site marked musttail");
}
}
if (!IsTailCall && CLI.CB && CLI.CB->isMustTailCall())
report_fatal_error("failed to perform tail call elimination on a call "
"site marked musttail");

if (IsTailCall)
++NumTailCalls;
Expand Down Expand Up @@ -3595,6 +3605,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
120 changes: 120 additions & 0 deletions llvm/test/CodeGen/Mips/musttail.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
; 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
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:
; 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 }

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