Skip to content

Commit 2af5b19

Browse files
author
Jessica Paquette
committedSep 10, 2019
[AArch64][GlobalISel] Support sibling calls with mismatched calling conventions
Add support for sibcalling calls whose calling convention differs from the caller's. - Port over `CCState::resultsCombatible` from CallingConvLower.cpp into CallLowering. This is used to verify that the way the caller and callee CC handle incoming arguments matches up. - Add `CallLowering::analyzeCallResult`. This is basically a port of `CCState::AnalyzeCallResult`, but using `ArgInfo` rather than `ISD::InputArg`. - Add `AArch64CallLowering::doCallerAndCalleePassArgsTheSameWay`. This checks that the calling conventions are compatible, and that the caller and callee preserve the same registers. For testing: - Update call-translator-tail-call.ll to show that we can now handle this. - Add a GISel line to tailcall-ccmismatch.ll to show that we will not tail call when the regmasks don't line up. Differential Revision: https://reviews.llvm.org/D67361 llvm-svn: 371570
1 parent a6e0bef commit 2af5b19

File tree

6 files changed

+144
-23
lines changed

6 files changed

+144
-23
lines changed
 

‎llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h

+18
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,24 @@ class CallLowering {
211211
SmallVectorImpl<ArgInfo> &Args,
212212
ValueHandler &Handler) const;
213213

214+
/// Analyze the return values of a call, incorporating info about the passed
215+
/// values into \p CCState.
216+
bool analyzeCallResult(CCState &CCState, SmallVectorImpl<ArgInfo> &Args,
217+
CCAssignFn &Fn) const;
218+
219+
/// \returns True if the calling convention for a callee and its caller pass
220+
/// results in the same way. Typically used for tail call eligibility checks.
221+
///
222+
/// \p Info is the CallLoweringInfo for the call.
223+
/// \p MF is the MachineFunction for the caller.
224+
/// \p InArgs contains the results of the call.
225+
/// \p CalleeAssignFn is the CCAssignFn to be used for the callee.
226+
/// \p CallerAssignFn is the CCAssignFn to be used for the caller.
227+
bool resultsCompatible(CallLoweringInfo &Info, MachineFunction &MF,
228+
SmallVectorImpl<ArgInfo> &InArgs,
229+
CCAssignFn &CalleeAssignFn,
230+
CCAssignFn &CallerAssignFn) const;
231+
214232
public:
215233
CallLowering(const TargetLowering *TLI) : TLI(TLI) {}
216234
virtual ~CallLowering() = default;

‎llvm/lib/CodeGen/GlobalISel/CallLowering.cpp

+69
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,75 @@ bool CallLowering::handleAssignments(CCState &CCInfo,
370370
return true;
371371
}
372372

373+
bool CallLowering::analyzeCallResult(CCState &CCState,
374+
SmallVectorImpl<ArgInfo> &Args,
375+
CCAssignFn &Fn) const {
376+
for (unsigned i = 0, e = Args.size(); i < e; ++i) {
377+
MVT VT = MVT::getVT(Args[i].Ty);
378+
if (Fn(i, VT, VT, CCValAssign::Full, Args[i].Flags[0], CCState)) {
379+
// Bail out on anything we can't handle.
380+
LLVM_DEBUG(dbgs() << "Cannot analyze " << EVT(VT).getEVTString()
381+
<< " (arg number = " << i << "\n");
382+
return false;
383+
}
384+
}
385+
return true;
386+
}
387+
388+
bool CallLowering::resultsCompatible(CallLoweringInfo &Info,
389+
MachineFunction &MF,
390+
SmallVectorImpl<ArgInfo> &InArgs,
391+
CCAssignFn &CalleeAssignFn,
392+
CCAssignFn &CallerAssignFn) const {
393+
const Function &F = MF.getFunction();
394+
CallingConv::ID CalleeCC = Info.CallConv;
395+
CallingConv::ID CallerCC = F.getCallingConv();
396+
397+
if (CallerCC == CalleeCC)
398+
return true;
399+
400+
SmallVector<CCValAssign, 16> ArgLocs1;
401+
CCState CCInfo1(CalleeCC, false, MF, ArgLocs1, F.getContext());
402+
if (!analyzeCallResult(CCInfo1, InArgs, CalleeAssignFn))
403+
return false;
404+
405+
SmallVector<CCValAssign, 16> ArgLocs2;
406+
CCState CCInfo2(CallerCC, false, MF, ArgLocs2, F.getContext());
407+
if (!analyzeCallResult(CCInfo2, InArgs, CallerAssignFn))
408+
return false;
409+
410+
// We need the argument locations to match up exactly. If there's more in
411+
// one than the other, then we are done.
412+
if (ArgLocs1.size() != ArgLocs2.size())
413+
return false;
414+
415+
// Make sure that each location is passed in exactly the same way.
416+
for (unsigned i = 0, e = ArgLocs1.size(); i < e; ++i) {
417+
const CCValAssign &Loc1 = ArgLocs1[i];
418+
const CCValAssign &Loc2 = ArgLocs2[i];
419+
420+
// We need both of them to be the same. So if one is a register and one
421+
// isn't, we're done.
422+
if (Loc1.isRegLoc() != Loc2.isRegLoc())
423+
return false;
424+
425+
if (Loc1.isRegLoc()) {
426+
// If they don't have the same register location, we're done.
427+
if (Loc1.getLocReg() != Loc2.getLocReg())
428+
return false;
429+
430+
// They matched, so we can move to the next ArgLoc.
431+
continue;
432+
}
433+
434+
// Loc1 wasn't a RegLoc, so they both must be MemLocs. Check if they match.
435+
if (Loc1.getLocMemOffset() != Loc2.getLocMemOffset())
436+
return false;
437+
}
438+
439+
return true;
440+
}
441+
373442
Register CallLowering::ValueHandler::extendRegister(Register ValReg,
374443
CCValAssign &VA) {
375444
LLT LocTy{VA.getLocVT()};

‎llvm/lib/Target/AArch64/AArch64CallLowering.cpp

+46-15
Original file line numberDiff line numberDiff line change
@@ -431,13 +431,44 @@ static bool mayTailCallThisCC(CallingConv::ID CC) {
431431
}
432432
}
433433

434+
bool AArch64CallLowering::doCallerAndCalleePassArgsTheSameWay(
435+
CallLoweringInfo &Info, MachineFunction &MF,
436+
SmallVectorImpl<ArgInfo> &InArgs) const {
437+
const Function &CallerF = MF.getFunction();
438+
CallingConv::ID CalleeCC = Info.CallConv;
439+
CallingConv::ID CallerCC = CallerF.getCallingConv();
440+
441+
// If the calling conventions match, then everything must be the same.
442+
if (CalleeCC == CallerCC)
443+
return true;
444+
445+
// Check if the caller and callee will handle arguments in the same way.
446+
const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>();
447+
CCAssignFn *CalleeAssignFn = TLI.CCAssignFnForCall(CalleeCC, Info.IsVarArg);
448+
CCAssignFn *CallerAssignFn =
449+
TLI.CCAssignFnForCall(CallerCC, CallerF.isVarArg());
450+
451+
if (!resultsCompatible(Info, MF, InArgs, *CalleeAssignFn, *CallerAssignFn))
452+
return false;
453+
454+
// Make sure that the caller and callee preserve all of the same registers.
455+
auto TRI = MF.getSubtarget<AArch64Subtarget>().getRegisterInfo();
456+
const uint32_t *CallerPreserved = TRI->getCallPreservedMask(MF, CallerCC);
457+
const uint32_t *CalleePreserved = TRI->getCallPreservedMask(MF, CalleeCC);
458+
if (MF.getSubtarget<AArch64Subtarget>().hasCustomCallingConv()) {
459+
TRI->UpdateCustomCallPreservedMask(MF, &CallerPreserved);
460+
TRI->UpdateCustomCallPreservedMask(MF, &CalleePreserved);
461+
}
462+
463+
return TRI->regmaskSubsetEqual(CallerPreserved, CalleePreserved);
464+
}
465+
434466
bool AArch64CallLowering::isEligibleForTailCallOptimization(
435-
MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info) const {
467+
MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info,
468+
SmallVectorImpl<ArgInfo> &InArgs) const {
436469
CallingConv::ID CalleeCC = Info.CallConv;
437470
MachineFunction &MF = MIRBuilder.getMF();
438471
const Function &CallerF = MF.getFunction();
439-
CallingConv::ID CallerCC = CallerF.getCallingConv();
440-
bool CCMatch = CallerCC == CalleeCC;
441472

442473
LLVM_DEBUG(dbgs() << "Attempting to lower call as tail call\n");
443474

@@ -509,11 +540,11 @@ bool AArch64CallLowering::isEligibleForTailCallOptimization(
509540
assert((!Info.IsVarArg || CalleeCC == CallingConv::C) &&
510541
"Unexpected variadic calling convention");
511542

512-
// For now, only support the case where the calling conventions match.
513-
if (!CCMatch) {
543+
// Look at the incoming values.
544+
if (!doCallerAndCalleePassArgsTheSameWay(Info, MF, InArgs)) {
514545
LLVM_DEBUG(
515546
dbgs()
516-
<< "... Cannot tail call with mismatched calling conventions yet.\n");
547+
<< "... Caller and callee have incompatible calling conventions.\n");
517548
return false;
518549
}
519550

@@ -552,6 +583,7 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
552583
const Function &F = MF.getFunction();
553584
MachineRegisterInfo &MRI = MF.getRegInfo();
554585
auto &DL = F.getParent()->getDataLayout();
586+
const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>();
555587

556588
if (Info.IsMustTailCall) {
557589
// TODO: Until we lower all tail calls, we should fall back on this.
@@ -573,13 +605,16 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
573605
SplitArgs.back().Flags[0].setZExt();
574606
}
575607

576-
bool IsSibCall =
577-
Info.IsTailCall && isEligibleForTailCallOptimization(MIRBuilder, Info);
608+
SmallVector<ArgInfo, 8> InArgs;
609+
if (!Info.OrigRet.Ty->isVoidTy())
610+
splitToValueTypes(Info.OrigRet, InArgs, DL, MRI, F.getCallingConv());
611+
612+
bool IsSibCall = Info.IsTailCall &&
613+
isEligibleForTailCallOptimization(MIRBuilder, Info, InArgs);
578614
if (IsSibCall)
579615
MF.getFrameInfo().setHasTailCall();
580616

581617
// Find out which ABI gets to decide where things go.
582-
const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>();
583618
CCAssignFn *AssignFnFixed =
584619
TLI.CCAssignFnForCall(Info.CallConv, /*IsVarArg=*/false);
585620
CCAssignFn *AssignFnVarArg =
@@ -649,14 +684,10 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
649684
// Finally we can copy the returned value back into its virtual-register. In
650685
// symmetry with the arugments, the physical register must be an
651686
// implicit-define of the call instruction.
652-
CCAssignFn *RetAssignFn = TLI.CCAssignFnForReturn(F.getCallingConv());
653687
if (!Info.OrigRet.Ty->isVoidTy()) {
654-
SplitArgs.clear();
655-
656-
splitToValueTypes(Info.OrigRet, SplitArgs, DL, MRI, F.getCallingConv());
657-
688+
CCAssignFn *RetAssignFn = TLI.CCAssignFnForReturn(F.getCallingConv());
658689
CallReturnHandler Handler(MIRBuilder, MRI, MIB, RetAssignFn);
659-
if (!handleAssignments(MIRBuilder, SplitArgs, Handler))
690+
if (!handleAssignments(MIRBuilder, InArgs, Handler))
660691
return false;
661692
}
662693

‎llvm/lib/Target/AArch64/AArch64CallLowering.h

+9-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ class AArch64CallLowering: public CallLowering {
4444
CallLoweringInfo &Info) const override;
4545

4646
/// Returns true if the call can be lowered as a tail call.
47-
bool isEligibleForTailCallOptimization(MachineIRBuilder &MIRBuilder,
48-
CallLoweringInfo &Info) const;
47+
bool
48+
isEligibleForTailCallOptimization(MachineIRBuilder &MIRBuilder,
49+
CallLoweringInfo &Info,
50+
SmallVectorImpl<ArgInfo> &InArgs) const;
4951

5052
bool supportSwiftError() const override { return true; }
5153

@@ -60,6 +62,11 @@ class AArch64CallLowering: public CallLowering {
6062
SmallVectorImpl<ArgInfo> &SplitArgs,
6163
const DataLayout &DL, MachineRegisterInfo &MRI,
6264
CallingConv::ID CallConv) const;
65+
66+
bool
67+
doCallerAndCalleePassArgsTheSameWay(CallLoweringInfo &Info,
68+
MachineFunction &MF,
69+
SmallVectorImpl<ArgInfo> &InArgs) const;
6370
};
6471

6572
} // end namespace llvm

‎llvm/test/CodeGen/AArch64/GlobalISel/call-translator-tail-call.ll

+1-6
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,11 @@ define void @test_extern_weak() {
175175
ret void
176176
}
177177

178-
; Right now, mismatched calling conventions should not be tail called.
179-
; TODO: Support this.
180178
declare fastcc void @fast_fn()
181179
define void @test_mismatched_caller() {
182180
; COMMON-LABEL: name: test_mismatched_caller
183181
; COMMON: bb.1 (%ir-block.0):
184-
; COMMON: ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp
185-
; COMMON: BL @fast_fn, csr_aarch64_aapcs, implicit-def $lr, implicit $sp
186-
; COMMON: ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp
187-
; COMMON: RET_ReallyLR
182+
; COMMON: TCRETURNdi @fast_fn, 0, csr_aarch64_aapcs, implicit $sp
188183
tail call fastcc void @fast_fn()
189184
ret void
190185
}

‎llvm/test/CodeGen/AArch64/tailcall-ccmismatch.ll

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
; RUN: llc -o - %s | FileCheck %s
2+
; RUN: llc -global-isel -verify-machineinstrs -o - %s | FileCheck %s
23
target triple="aarch64--"
34

45
declare void @somefunc()

0 commit comments

Comments
 (0)
Please sign in to comment.