Skip to content

[AArch64] Support varargs for preserve_nonecc #99434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 21, 2024
Merged
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
73 changes: 43 additions & 30 deletions llvm/lib/Target/AArch64/AArch64CallingConvention.td
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ class CCIfBigEndian<CCAction A> :
class CCIfILP32<CCAction A> :
CCIf<"State.getMachineFunction().getDataLayout().getPointerSize() == 4", A>;

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

//===----------------------------------------------------------------------===//
// ARM AAPCS64 Calling Convention
Expand Down Expand Up @@ -496,36 +501,44 @@ def CC_AArch64_GHC : CallingConv<[

let Entry = 1 in
def CC_AArch64_Preserve_None : CallingConv<[
// We can pass arguments in all general registers, except:
// - X8, used for sret
// - X16/X17, used by the linker as IP0/IP1
// - X18, the platform register
// - X19, the base pointer
// - X29, the frame pointer
// - X30, the link register
// General registers are not preserved with the exception of
// FP, LR, and X18
// Non-volatile registers are used first, so functions may call
// normal functions without saving and reloading arguments.
// X9 is assigned last as it is used in FrameLowering as the first
// choice for a scratch register.
CCIfType<[i32], CCAssignToReg<[W20, W21, W22, W23,
W24, W25, W26, W27, W28,
W0, W1, W2, W3, W4, W5,
W6, W7, W10, W11,
W12, W13, W14, W9]>>,
CCIfType<[i64], CCAssignToReg<[X20, X21, X22, X23,
X24, X25, X26, X27, X28,
X0, X1, X2, X3, X4, X5,
X6, X7, X10, X11,
X12, X13, X14, X9]>>,

// Windows uses X15 for stack allocation
CCIf<"!State.getMachineFunction().getSubtarget<AArch64Subtarget>().isTargetWindows()",
CCIfType<[i32], CCAssignToReg<[W15]>>>,
CCIf<"!State.getMachineFunction().getSubtarget<AArch64Subtarget>().isTargetWindows()",
CCIfType<[i64], CCAssignToReg<[X15]>>>,
CCDelegateTo<CC_AArch64_AAPCS>
// VarArgs are only supported using the C calling convention.
// This handles the non-variadic parameter case. Variadic parameters
// are handled in CCAssignFnForCall.
CCIfVarArg<CCIfSubtarget<"isTargetDarwin()", CCDelegateTo<CC_AArch64_DarwinPCS>>>,
CCIfVarArg<CCIfSubtarget<"isTargetWindows()", CCDelegateTo<CC_AArch64_Win64PCS>>>,
CCIfVarArg<CCDelegateTo<CC_AArch64_AAPCS>>,

// We can pass arguments in all general registers, except:
// - X8, used for sret
// - X16/X17, used by the linker as IP0/IP1
// - X18, the platform register
// - X19, the base pointer
// - X29, the frame pointer
// - X30, the link register
// General registers are not preserved with the exception of
// FP, LR, and X18
// Non-volatile registers are used first, so functions may call
// normal functions without saving and reloading arguments.
// X9 is assigned last as it is used in FrameLowering as the first
// choice for a scratch register.
CCIfType<[i32], CCAssignToReg<[W20, W21, W22, W23,
W24, W25, W26, W27, W28,
W0, W1, W2, W3, W4, W5,
W6, W7, W10, W11,
W12, W13, W14, W9]>>,
CCIfType<[i64], CCAssignToReg<[X20, X21, X22, X23,
X24, X25, X26, X27, X28,
X0, X1, X2, X3, X4, X5,
X6, X7, X10, X11,
X12, X13, X14, X9]>>,

// Windows uses X15 for stack allocation
CCIf<"!State.getMachineFunction().getSubtarget<AArch64Subtarget>().isTargetWindows()",
CCIfType<[i32], CCAssignToReg<[W15]>>>,
CCIf<"!State.getMachineFunction().getSubtarget<AArch64Subtarget>().isTargetWindows()",
CCIfType<[i64], CCAssignToReg<[X15]>>>,

CCDelegateTo<CC_AArch64_AAPCS>
]>;

// The order of the callee-saves in this file is important, because the
Expand Down
17 changes: 8 additions & 9 deletions llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1901,8 +1901,7 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
return;
}

bool IsWin64 =
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv());
bool IsWin64 = Subtarget.isCallingConvWin64(F.getCallingConv(), F.isVarArg());
unsigned FixedObject = getFixedObjectSize(MF, AFI, IsWin64, IsFunclet);

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

int64_t AfterCSRPopSize = ArgumentStackToRestore;
Expand Down Expand Up @@ -2615,8 +2614,8 @@ static StackOffset getFPOffset(const MachineFunction &MF,
int64_t ObjectOffset) {
const auto *AFI = MF.getInfo<AArch64FunctionInfo>();
const auto &Subtarget = MF.getSubtarget<AArch64Subtarget>();
bool IsWin64 =
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv());
const Function &F = MF.getFunction();
bool IsWin64 = Subtarget.isCallingConvWin64(F.getCallingConv(), F.isVarArg());
unsigned FixedObject =
getFixedObjectSize(MF, AFI, IsWin64, /*IsFunclet=*/false);
int64_t CalleeSaveSize = AFI->getCalleeSavedStackSize(MF.getFrameInfo());
Expand Down Expand Up @@ -2722,9 +2721,9 @@ StackOffset AArch64FrameLowering::resolveFrameOffsetReference(
// via the frame pointer, so we have to use the FP in the parent
// function.
(void) Subtarget;
assert(
Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv()) &&
"Funclets should only be present on Win64");
assert(Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv(),
MF.getFunction().isVarArg()) &&
"Funclets should only be present on Win64");
UseFP = true;
} else {
// We have the choice between FP and (SP or BP).
Expand Down
39 changes: 32 additions & 7 deletions llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7109,7 +7109,13 @@ CCAssignFn *AArch64TargetLowering::CCAssignFnForCall(CallingConv::ID CC,
case CallingConv::GHC:
return CC_AArch64_GHC;
case CallingConv::PreserveNone:
return CC_AArch64_Preserve_None;
// The VarArg implementation makes assumptions about register
// argument passing that do not hold for preserve_none, so we
// instead fall back to C argument passing.
// The non-vararg case is handled in the CC function itself.
if (!IsVarArg)
return CC_AArch64_Preserve_None;
[[fallthrough]];
case CallingConv::C:
case CallingConv::Fast:
case CallingConv::PreserveMost:
Expand Down Expand Up @@ -7182,7 +7188,8 @@ SDValue AArch64TargetLowering::LowerFormalArguments(
MachineFunction &MF = DAG.getMachineFunction();
const Function &F = MF.getFunction();
MachineFrameInfo &MFI = MF.getFrameInfo();
bool IsWin64 = Subtarget->isCallingConvWin64(F.getCallingConv());
bool IsWin64 =
Subtarget->isCallingConvWin64(F.getCallingConv(), F.isVarArg());
bool StackViaX4 = CallConv == CallingConv::ARM64EC_Thunk_X64 ||
(isVarArg && Subtarget->isWindowsArm64EC());
AArch64FunctionInfo *FuncInfo = MF.getInfo<AArch64FunctionInfo>();
Expand Down Expand Up @@ -7634,7 +7641,9 @@ void AArch64TargetLowering::saveVarArgRegisters(CCState &CCInfo,
MachineFrameInfo &MFI = MF.getFrameInfo();
AArch64FunctionInfo *FuncInfo = MF.getInfo<AArch64FunctionInfo>();
auto PtrVT = getPointerTy(DAG.getDataLayout());
bool IsWin64 = Subtarget->isCallingConvWin64(MF.getFunction().getCallingConv());
Function &F = MF.getFunction();
bool IsWin64 =
Subtarget->isCallingConvWin64(F.getCallingConv(), F.isVarArg());

SmallVector<SDValue, 8> MemOps;

Expand Down Expand Up @@ -7805,6 +7814,21 @@ static bool mayTailCallThisCC(CallingConv::ID CC) {
}
}

/// Return true if the call convention supports varargs
/// Currently only those that pass varargs like the C
/// calling convention does are eligible
/// Calling conventions listed in this function must also
/// be properly handled in AArch64Subtarget::isCallingConvWin64
static bool callConvSupportsVarArgs(CallingConv::ID CC) {
switch (CC) {
case CallingConv::C:
case CallingConv::PreserveNone:
return true;
default:
return false;
}
}

static void analyzeCallOperands(const AArch64TargetLowering &TLI,
const AArch64Subtarget *Subtarget,
const TargetLowering::CallLoweringInfo &CLI,
Expand All @@ -7813,7 +7837,7 @@ static void analyzeCallOperands(const AArch64TargetLowering &TLI,
CallingConv::ID CalleeCC = CLI.CallConv;
bool IsVarArg = CLI.IsVarArg;
const SmallVector<ISD::OutputArg, 32> &Outs = CLI.Outs;
bool IsCalleeWin64 = Subtarget->isCallingConvWin64(CalleeCC);
bool IsCalleeWin64 = Subtarget->isCallingConvWin64(CalleeCC, IsVarArg);

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

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

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

if (Subtarget->isCallingConvWin64(MF.getFunction().getCallingConv()))
if (Subtarget->isCallingConvWin64(F.getCallingConv(), F.isVarArg()))
return LowerWin64_VASTART(Op, DAG);
else if (Subtarget->isTargetDarwin())
return LowerDarwin_VASTART(Op, DAG);
Expand Down
7 changes: 5 additions & 2 deletions llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,8 @@ bool AArch64RegisterInfo::isArgumentRegister(const MachineFunction &MF,
MCRegister Reg) const {
CallingConv::ID CC = MF.getFunction().getCallingConv();
const AArch64Subtarget &STI = MF.getSubtarget<AArch64Subtarget>();
bool IsVarArg = STI.isCallingConvWin64(MF.getFunction().getCallingConv());
bool IsVarArg = STI.isCallingConvWin64(MF.getFunction().getCallingConv(),
MF.getFunction().isVarArg());

auto HasReg = [](ArrayRef<MCRegister> RegList, MCRegister Reg) {
return llvm::is_contained(RegList, Reg);
Expand All @@ -623,7 +624,9 @@ bool AArch64RegisterInfo::isArgumentRegister(const MachineFunction &MF,
case CallingConv::GHC:
return HasReg(CC_AArch64_GHC_ArgRegs, Reg);
case CallingConv::PreserveNone:
return HasReg(CC_AArch64_Preserve_None_ArgRegs, Reg);
if (!MF.getFunction().isVarArg())
return HasReg(CC_AArch64_Preserve_None_ArgRegs, Reg);
[[fallthrough]];
case CallingConv::C:
case CallingConv::Fast:
case CallingConv::PreserveMost:
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/Target/AArch64/AArch64Subtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,13 +322,15 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo {

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

bool isCallingConvWin64(CallingConv::ID CC) const {
bool isCallingConvWin64(CallingConv::ID CC, bool IsVarArg) const {
switch (CC) {
case CallingConv::C:
case CallingConv::Fast:
case CallingConv::Swift:
case CallingConv::SwiftTail:
return isTargetWindows();
case CallingConv::PreserveNone:
return IsVarArg && isTargetWindows();
case CallingConv::Win64:
return true;
default:
Expand Down
12 changes: 8 additions & 4 deletions llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ struct AArch64OutgoingValueAssigner
CCValAssign::LocInfo LocInfo,
const CallLowering::ArgInfo &Info, ISD::ArgFlagsTy Flags,
CCState &State) override {
bool IsCalleeWin = Subtarget.isCallingConvWin64(State.getCallingConv());
const Function &F = State.getMachineFunction().getFunction();
bool IsCalleeWin =
Subtarget.isCallingConvWin64(State.getCallingConv(), F.isVarArg());
bool UseVarArgsCCForFixed = IsCalleeWin && State.isVarArg();

bool Res;
Expand Down Expand Up @@ -557,8 +559,8 @@ void AArch64CallLowering::saveVarArgRegisters(
MachineFrameInfo &MFI = MF.getFrameInfo();
AArch64FunctionInfo *FuncInfo = MF.getInfo<AArch64FunctionInfo>();
auto &Subtarget = MF.getSubtarget<AArch64Subtarget>();
bool IsWin64CC =
Subtarget.isCallingConvWin64(CCInfo.getCallingConv());
bool IsWin64CC = Subtarget.isCallingConvWin64(CCInfo.getCallingConv(),
MF.getFunction().isVarArg());
const LLT p0 = LLT::pointer(0, 64);
const LLT s64 = LLT::scalar(64);

Expand Down Expand Up @@ -653,7 +655,9 @@ bool AArch64CallLowering::lowerFormalArguments(
F.getCallingConv() == CallingConv::ARM64EC_Thunk_X64)
return false;

bool IsWin64 = Subtarget.isCallingConvWin64(F.getCallingConv()) && !Subtarget.isWindowsArm64EC();
bool IsWin64 =
Subtarget.isCallingConvWin64(F.getCallingConv(), F.isVarArg()) &&
!Subtarget.isWindowsArm64EC();

SmallVector<ArgInfo, 8> SplitArgs;
SmallVector<std::pair<Register, Register>> BoolArgs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2006,7 +2006,7 @@ bool AArch64InstructionSelector::selectVaStartDarwin(

int FrameIdx = FuncInfo->getVarArgsStackIndex();
if (MF.getSubtarget<AArch64Subtarget>().isCallingConvWin64(
MF.getFunction().getCallingConv())) {
MF.getFunction().getCallingConv(), MF.getFunction().isVarArg())) {
FrameIdx = FuncInfo->getVarArgsGPRSize() > 0
? FuncInfo->getVarArgsGPRIndex()
: FuncInfo->getVarArgsStackIndex();
Expand Down
Loading
Loading