Skip to content

Commit 6c9086d

Browse files
authored
[AArch64] Support varargs for preserve_nonecc (#99434)
Adds varargs support for preserve_none by falling back to C argument passing for the target platform for varargs functions. Fixes #95093
1 parent b8220b9 commit 6c9086d

10 files changed

+359
-54
lines changed

llvm/lib/Target/AArch64/AArch64CallingConvention.td

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ class CCIfBigEndian<CCAction A> :
1717
class CCIfILP32<CCAction A> :
1818
CCIf<"State.getMachineFunction().getDataLayout().getPointerSize() == 4", A>;
1919

20+
/// CCIfSubtarget - Match if the current subtarget has a feature F.
21+
class CCIfSubtarget<string F, CCAction A>
22+
: CCIf<!strconcat("State.getMachineFunction()"
23+
".getSubtarget<AArch64Subtarget>().", F),
24+
A>;
2025

2126
//===----------------------------------------------------------------------===//
2227
// ARM AAPCS64 Calling Convention
@@ -496,36 +501,44 @@ def CC_AArch64_GHC : CallingConv<[
496501

497502
let Entry = 1 in
498503
def CC_AArch64_Preserve_None : CallingConv<[
499-
// We can pass arguments in all general registers, except:
500-
// - X8, used for sret
501-
// - X16/X17, used by the linker as IP0/IP1
502-
// - X18, the platform register
503-
// - X19, the base pointer
504-
// - X29, the frame pointer
505-
// - X30, the link register
506-
// General registers are not preserved with the exception of
507-
// FP, LR, and X18
508-
// Non-volatile registers are used first, so functions may call
509-
// normal functions without saving and reloading arguments.
510-
// X9 is assigned last as it is used in FrameLowering as the first
511-
// choice for a scratch register.
512-
CCIfType<[i32], CCAssignToReg<[W20, W21, W22, W23,
513-
W24, W25, W26, W27, W28,
514-
W0, W1, W2, W3, W4, W5,
515-
W6, W7, W10, W11,
516-
W12, W13, W14, W9]>>,
517-
CCIfType<[i64], CCAssignToReg<[X20, X21, X22, X23,
518-
X24, X25, X26, X27, X28,
519-
X0, X1, X2, X3, X4, X5,
520-
X6, X7, X10, X11,
521-
X12, X13, X14, X9]>>,
522-
523-
// Windows uses X15 for stack allocation
524-
CCIf<"!State.getMachineFunction().getSubtarget<AArch64Subtarget>().isTargetWindows()",
525-
CCIfType<[i32], CCAssignToReg<[W15]>>>,
526-
CCIf<"!State.getMachineFunction().getSubtarget<AArch64Subtarget>().isTargetWindows()",
527-
CCIfType<[i64], CCAssignToReg<[X15]>>>,
528-
CCDelegateTo<CC_AArch64_AAPCS>
504+
// VarArgs are only supported using the C calling convention.
505+
// This handles the non-variadic parameter case. Variadic parameters
506+
// are handled in CCAssignFnForCall.
507+
CCIfVarArg<CCIfSubtarget<"isTargetDarwin()", CCDelegateTo<CC_AArch64_DarwinPCS>>>,
508+
CCIfVarArg<CCIfSubtarget<"isTargetWindows()", CCDelegateTo<CC_AArch64_Win64PCS>>>,
509+
CCIfVarArg<CCDelegateTo<CC_AArch64_AAPCS>>,
510+
511+
// We can pass arguments in all general registers, except:
512+
// - X8, used for sret
513+
// - X16/X17, used by the linker as IP0/IP1
514+
// - X18, the platform register
515+
// - X19, the base pointer
516+
// - X29, the frame pointer
517+
// - X30, the link register
518+
// General registers are not preserved with the exception of
519+
// FP, LR, and X18
520+
// Non-volatile registers are used first, so functions may call
521+
// normal functions without saving and reloading arguments.
522+
// X9 is assigned last as it is used in FrameLowering as the first
523+
// choice for a scratch register.
524+
CCIfType<[i32], CCAssignToReg<[W20, W21, W22, W23,
525+
W24, W25, W26, W27, W28,
526+
W0, W1, W2, W3, W4, W5,
527+
W6, W7, W10, W11,
528+
W12, W13, W14, W9]>>,
529+
CCIfType<[i64], CCAssignToReg<[X20, X21, X22, X23,
530+
X24, X25, X26, X27, X28,
531+
X0, X1, X2, X3, X4, X5,
532+
X6, X7, X10, X11,
533+
X12, X13, X14, X9]>>,
534+
535+
// Windows uses X15 for stack allocation
536+
CCIf<"!State.getMachineFunction().getSubtarget<AArch64Subtarget>().isTargetWindows()",
537+
CCIfType<[i32], CCAssignToReg<[W15]>>>,
538+
CCIf<"!State.getMachineFunction().getSubtarget<AArch64Subtarget>().isTargetWindows()",
539+
CCIfType<[i64], CCAssignToReg<[X15]>>>,
540+
541+
CCDelegateTo<CC_AArch64_AAPCS>
529542
]>;
530543

531544
// The order of the callee-saves in this file is important, because the

llvm/lib/Target/AArch64/AArch64FrameLowering.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,8 +1900,7 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
19001900
return;
19011901
}
19021902

1903-
bool IsWin64 =
1904-
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv());
1903+
bool IsWin64 = Subtarget.isCallingConvWin64(F.getCallingConv(), F.isVarArg());
19051904
unsigned FixedObject = getFixedObjectSize(MF, AFI, IsWin64, IsFunclet);
19061905

19071906
auto PrologueSaveSize = AFI->getCalleeSavedStackSize() + FixedObject;
@@ -2307,8 +2306,8 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF,
23072306
// How much of the stack used by incoming arguments this function is expected
23082307
// to restore in this particular epilogue.
23092308
int64_t ArgumentStackToRestore = getArgumentStackToRestore(MF, MBB);
2310-
bool IsWin64 =
2311-
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv());
2309+
bool IsWin64 = Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv(),
2310+
MF.getFunction().isVarArg());
23122311
unsigned FixedObject = getFixedObjectSize(MF, AFI, IsWin64, IsFunclet);
23132312

23142313
int64_t AfterCSRPopSize = ArgumentStackToRestore;
@@ -2614,8 +2613,8 @@ static StackOffset getFPOffset(const MachineFunction &MF,
26142613
int64_t ObjectOffset) {
26152614
const auto *AFI = MF.getInfo<AArch64FunctionInfo>();
26162615
const auto &Subtarget = MF.getSubtarget<AArch64Subtarget>();
2617-
bool IsWin64 =
2618-
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv());
2616+
const Function &F = MF.getFunction();
2617+
bool IsWin64 = Subtarget.isCallingConvWin64(F.getCallingConv(), F.isVarArg());
26192618
unsigned FixedObject =
26202619
getFixedObjectSize(MF, AFI, IsWin64, /*IsFunclet=*/false);
26212620
int64_t CalleeSaveSize = AFI->getCalleeSavedStackSize(MF.getFrameInfo());
@@ -2721,9 +2720,9 @@ StackOffset AArch64FrameLowering::resolveFrameOffsetReference(
27212720
// via the frame pointer, so we have to use the FP in the parent
27222721
// function.
27232722
(void) Subtarget;
2724-
assert(
2725-
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv()) &&
2726-
"Funclets should only be present on Win64");
2723+
assert(Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv(),
2724+
MF.getFunction().isVarArg()) &&
2725+
"Funclets should only be present on Win64");
27272726
UseFP = true;
27282727
} else {
27292728
// We have the choice between FP and (SP or BP).

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7109,7 +7109,13 @@ CCAssignFn *AArch64TargetLowering::CCAssignFnForCall(CallingConv::ID CC,
71097109
case CallingConv::GHC:
71107110
return CC_AArch64_GHC;
71117111
case CallingConv::PreserveNone:
7112-
return CC_AArch64_Preserve_None;
7112+
// The VarArg implementation makes assumptions about register
7113+
// argument passing that do not hold for preserve_none, so we
7114+
// instead fall back to C argument passing.
7115+
// The non-vararg case is handled in the CC function itself.
7116+
if (!IsVarArg)
7117+
return CC_AArch64_Preserve_None;
7118+
[[fallthrough]];
71137119
case CallingConv::C:
71147120
case CallingConv::Fast:
71157121
case CallingConv::PreserveMost:
@@ -7182,7 +7188,8 @@ SDValue AArch64TargetLowering::LowerFormalArguments(
71827188
MachineFunction &MF = DAG.getMachineFunction();
71837189
const Function &F = MF.getFunction();
71847190
MachineFrameInfo &MFI = MF.getFrameInfo();
7185-
bool IsWin64 = Subtarget->isCallingConvWin64(F.getCallingConv());
7191+
bool IsWin64 =
7192+
Subtarget->isCallingConvWin64(F.getCallingConv(), F.isVarArg());
71867193
bool StackViaX4 = CallConv == CallingConv::ARM64EC_Thunk_X64 ||
71877194
(isVarArg && Subtarget->isWindowsArm64EC());
71887195
AArch64FunctionInfo *FuncInfo = MF.getInfo<AArch64FunctionInfo>();
@@ -7634,7 +7641,9 @@ void AArch64TargetLowering::saveVarArgRegisters(CCState &CCInfo,
76347641
MachineFrameInfo &MFI = MF.getFrameInfo();
76357642
AArch64FunctionInfo *FuncInfo = MF.getInfo<AArch64FunctionInfo>();
76367643
auto PtrVT = getPointerTy(DAG.getDataLayout());
7637-
bool IsWin64 = Subtarget->isCallingConvWin64(MF.getFunction().getCallingConv());
7644+
Function &F = MF.getFunction();
7645+
bool IsWin64 =
7646+
Subtarget->isCallingConvWin64(F.getCallingConv(), F.isVarArg());
76387647

76397648
SmallVector<SDValue, 8> MemOps;
76407649

@@ -7805,6 +7814,21 @@ static bool mayTailCallThisCC(CallingConv::ID CC) {
78057814
}
78067815
}
78077816

7817+
/// Return true if the call convention supports varargs
7818+
/// Currently only those that pass varargs like the C
7819+
/// calling convention does are eligible
7820+
/// Calling conventions listed in this function must also
7821+
/// be properly handled in AArch64Subtarget::isCallingConvWin64
7822+
static bool callConvSupportsVarArgs(CallingConv::ID CC) {
7823+
switch (CC) {
7824+
case CallingConv::C:
7825+
case CallingConv::PreserveNone:
7826+
return true;
7827+
default:
7828+
return false;
7829+
}
7830+
}
7831+
78087832
static void analyzeCallOperands(const AArch64TargetLowering &TLI,
78097833
const AArch64Subtarget *Subtarget,
78107834
const TargetLowering::CallLoweringInfo &CLI,
@@ -7813,7 +7837,7 @@ static void analyzeCallOperands(const AArch64TargetLowering &TLI,
78137837
CallingConv::ID CalleeCC = CLI.CallConv;
78147838
bool IsVarArg = CLI.IsVarArg;
78157839
const SmallVector<ISD::OutputArg, 32> &Outs = CLI.Outs;
7816-
bool IsCalleeWin64 = Subtarget->isCallingConvWin64(CalleeCC);
7840+
bool IsCalleeWin64 = Subtarget->isCallingConvWin64(CalleeCC, IsVarArg);
78177841

78187842
// For Arm64EC thunks, allocate 32 extra bytes at the bottom of the stack
78197843
// for the shadow store.
@@ -7941,8 +7965,8 @@ bool AArch64TargetLowering::isEligibleForTailCallOptimization(
79417965

79427966
// I want anyone implementing a new calling convention to think long and hard
79437967
// about this assert.
7944-
assert((!IsVarArg || CalleeCC == CallingConv::C) &&
7945-
"Unexpected variadic calling convention");
7968+
if (IsVarArg && !callConvSupportsVarArgs(CalleeCC))
7969+
report_fatal_error("Unsupported variadic calling convention");
79467970

79477971
LLVMContext &C = *DAG.getContext();
79487972
// Check that the call results are passed in the same way.
@@ -10872,8 +10896,9 @@ SDValue AArch64TargetLowering::LowerAAPCS_VASTART(SDValue Op,
1087210896
SDValue AArch64TargetLowering::LowerVASTART(SDValue Op,
1087310897
SelectionDAG &DAG) const {
1087410898
MachineFunction &MF = DAG.getMachineFunction();
10899+
Function &F = MF.getFunction();
1087510900

10876-
if (Subtarget->isCallingConvWin64(MF.getFunction().getCallingConv()))
10901+
if (Subtarget->isCallingConvWin64(F.getCallingConv(), F.isVarArg()))
1087710902
return LowerWin64_VASTART(Op, DAG);
1087810903
else if (Subtarget->isTargetDarwin())
1087910904
return LowerDarwin_VASTART(Op, DAG);

llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,8 @@ bool AArch64RegisterInfo::isArgumentRegister(const MachineFunction &MF,
611611
MCRegister Reg) const {
612612
CallingConv::ID CC = MF.getFunction().getCallingConv();
613613
const AArch64Subtarget &STI = MF.getSubtarget<AArch64Subtarget>();
614-
bool IsVarArg = STI.isCallingConvWin64(MF.getFunction().getCallingConv());
614+
bool IsVarArg = STI.isCallingConvWin64(MF.getFunction().getCallingConv(),
615+
MF.getFunction().isVarArg());
615616

616617
auto HasReg = [](ArrayRef<MCRegister> RegList, MCRegister Reg) {
617618
return llvm::is_contained(RegList, Reg);
@@ -623,7 +624,9 @@ bool AArch64RegisterInfo::isArgumentRegister(const MachineFunction &MF,
623624
case CallingConv::GHC:
624625
return HasReg(CC_AArch64_GHC_ArgRegs, Reg);
625626
case CallingConv::PreserveNone:
626-
return HasReg(CC_AArch64_Preserve_None_ArgRegs, Reg);
627+
if (!MF.getFunction().isVarArg())
628+
return HasReg(CC_AArch64_Preserve_None_ArgRegs, Reg);
629+
[[fallthrough]];
627630
case CallingConv::C:
628631
case CallingConv::Fast:
629632
case CallingConv::PreserveMost:

llvm/lib/Target/AArch64/AArch64Subtarget.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,15 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo {
322322

323323
std::unique_ptr<PBQPRAConstraint> getCustomPBQPConstraints() const override;
324324

325-
bool isCallingConvWin64(CallingConv::ID CC) const {
325+
bool isCallingConvWin64(CallingConv::ID CC, bool IsVarArg) const {
326326
switch (CC) {
327327
case CallingConv::C:
328328
case CallingConv::Fast:
329329
case CallingConv::Swift:
330330
case CallingConv::SwiftTail:
331331
return isTargetWindows();
332+
case CallingConv::PreserveNone:
333+
return IsVarArg && isTargetWindows();
332334
case CallingConv::Win64:
333335
return true;
334336
default:

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ struct AArch64OutgoingValueAssigner
117117
CCValAssign::LocInfo LocInfo,
118118
const CallLowering::ArgInfo &Info, ISD::ArgFlagsTy Flags,
119119
CCState &State) override {
120-
bool IsCalleeWin = Subtarget.isCallingConvWin64(State.getCallingConv());
120+
const Function &F = State.getMachineFunction().getFunction();
121+
bool IsCalleeWin =
122+
Subtarget.isCallingConvWin64(State.getCallingConv(), F.isVarArg());
121123
bool UseVarArgsCCForFixed = IsCalleeWin && State.isVarArg();
122124

123125
bool Res;
@@ -557,8 +559,8 @@ void AArch64CallLowering::saveVarArgRegisters(
557559
MachineFrameInfo &MFI = MF.getFrameInfo();
558560
AArch64FunctionInfo *FuncInfo = MF.getInfo<AArch64FunctionInfo>();
559561
auto &Subtarget = MF.getSubtarget<AArch64Subtarget>();
560-
bool IsWin64CC =
561-
Subtarget.isCallingConvWin64(CCInfo.getCallingConv());
562+
bool IsWin64CC = Subtarget.isCallingConvWin64(CCInfo.getCallingConv(),
563+
MF.getFunction().isVarArg());
562564
const LLT p0 = LLT::pointer(0, 64);
563565
const LLT s64 = LLT::scalar(64);
564566

@@ -653,7 +655,9 @@ bool AArch64CallLowering::lowerFormalArguments(
653655
F.getCallingConv() == CallingConv::ARM64EC_Thunk_X64)
654656
return false;
655657

656-
bool IsWin64 = Subtarget.isCallingConvWin64(F.getCallingConv()) && !Subtarget.isWindowsArm64EC();
658+
bool IsWin64 =
659+
Subtarget.isCallingConvWin64(F.getCallingConv(), F.isVarArg()) &&
660+
!Subtarget.isWindowsArm64EC();
657661

658662
SmallVector<ArgInfo, 8> SplitArgs;
659663
SmallVector<std::pair<Register, Register>> BoolArgs;

llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2006,7 +2006,7 @@ bool AArch64InstructionSelector::selectVaStartDarwin(
20062006

20072007
int FrameIdx = FuncInfo->getVarArgsStackIndex();
20082008
if (MF.getSubtarget<AArch64Subtarget>().isCallingConvWin64(
2009-
MF.getFunction().getCallingConv())) {
2009+
MF.getFunction().getCallingConv(), MF.getFunction().isVarArg())) {
20102010
FrameIdx = FuncInfo->getVarArgsGPRSize() > 0
20112011
? FuncInfo->getVarArgsGPRIndex()
20122012
: FuncInfo->getVarArgsStackIndex();

0 commit comments

Comments
 (0)