Skip to content

[Xtensa] Implement vararg support. #117126

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 7 commits into from
Dec 12, 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
236 changes: 221 additions & 15 deletions llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "XtensaISelLowering.h"
#include "XtensaConstantPoolValue.h"
#include "XtensaInstrInfo.h"
#include "XtensaMachineFunctionInfo.h"
#include "XtensaSubtarget.h"
#include "XtensaTargetMachine.h"
#include "llvm/CodeGen/CallingConvLower.h"
Expand Down Expand Up @@ -133,6 +134,13 @@ XtensaTargetLowering::XtensaTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::STACKSAVE, MVT::Other, Custom);
setOperationAction(ISD::STACKRESTORE, MVT::Other, Custom);

// VASTART, VAARG and VACOPY need to deal with the Xtensa-specific varargs
// structure, but VAEND is a no-op.
setOperationAction(ISD::VASTART, MVT::Other, Custom);
setOperationAction(ISD::VAARG, MVT::Other, Custom);
setOperationAction(ISD::VACOPY, MVT::Other, Custom);
setOperationAction(ISD::VAEND, MVT::Other, Expand);

// Compute derived properties from the register classes
computeRegisterProperties(STI.getRegisterInfo());
}
Expand Down Expand Up @@ -217,12 +225,12 @@ void XtensaTargetLowering::LowerAsmOperandForConstraint(

#include "XtensaGenCallingConv.inc"

static const MCPhysReg IntRegs[] = {Xtensa::A2, Xtensa::A3, Xtensa::A4,
Xtensa::A5, Xtensa::A6, Xtensa::A7};

static bool CC_Xtensa_Custom(unsigned ValNo, MVT ValVT, MVT LocVT,
CCValAssign::LocInfo LocInfo,
ISD::ArgFlagsTy ArgFlags, CCState &State) {
static const MCPhysReg IntRegs[] = {Xtensa::A2, Xtensa::A3, Xtensa::A4,
Xtensa::A5, Xtensa::A6, Xtensa::A7};

if (ArgFlags.isByVal()) {
Align ByValAlign = ArgFlags.getNonZeroByValAlign();
unsigned ByValSize = ArgFlags.getByValSize();
Expand Down Expand Up @@ -304,13 +312,11 @@ SDValue XtensaTargetLowering::LowerFormalArguments(
SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const {
MachineFunction &MF = DAG.getMachineFunction();
MachineFrameInfo &MFI = MF.getFrameInfo();
XtensaMachineFunctionInfo *XtensaFI = MF.getInfo<XtensaMachineFunctionInfo>();

// Used with vargs to acumulate store chains.
std::vector<SDValue> OutChains;

if (IsVarArg)
report_fatal_error("Var arg not supported by FormalArguments Lowering");

// Assign locations to all of the incoming arguments.
SmallVector<CCValAssign, 16> ArgLocs;
CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs,
Expand All @@ -323,17 +329,14 @@ SDValue XtensaTargetLowering::LowerFormalArguments(
// Arguments stored on registers
if (VA.isRegLoc()) {
EVT RegVT = VA.getLocVT();
const TargetRegisterClass *RC;

if (RegVT == MVT::i32)
RC = &Xtensa::ARRegClass;
else
if (RegVT != MVT::i32)
report_fatal_error("RegVT not supported by FormalArguments Lowering");

// Transform the arguments stored on
// physical registers into virtual ones
unsigned Register = MF.addLiveIn(VA.getLocReg(), RC);
SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Register, RegVT);
Register Reg = MF.addLiveIn(VA.getLocReg(), &Xtensa::ARRegClass);
SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegVT);

// If this is an 8 or 16-bit value, it has been passed promoted
// to 32 bits. Insert an assert[sz]ext to capture this, then
Expand Down Expand Up @@ -378,6 +381,56 @@ SDValue XtensaTargetLowering::LowerFormalArguments(
}
}

if (IsVarArg) {
unsigned Idx = CCInfo.getFirstUnallocated(IntRegs);
unsigned ArgRegsNum = std::size(IntRegs);
const TargetRegisterClass *RC = &Xtensa::ARRegClass;
MachineFrameInfo &MFI = MF.getFrameInfo();
MachineRegisterInfo &RegInfo = MF.getRegInfo();
unsigned RegSize = 4;
MVT RegTy = MVT::i32;
MVT FITy = getFrameIndexTy(DAG.getDataLayout());

XtensaFI->setVarArgsFirstGPR(Idx + 2); // 2 - number of a2 register

XtensaFI->setVarArgsOnStackFrameIndex(
MFI.CreateFixedObject(4, CCInfo.getStackSize(), true));

// Offset of the first variable argument from stack pointer, and size of
// the vararg save area. For now, the varargs save area is either zero or
// large enough to hold a0-a7.
int VaArgOffset, VarArgsSaveSize;

// If all registers are allocated, then all varargs must be passed on the
// stack and we don't need to save any argregs.
if (ArgRegsNum == Idx) {
VaArgOffset = CCInfo.getStackSize();
VarArgsSaveSize = 0;
} else {
VarArgsSaveSize = RegSize * (ArgRegsNum - Idx);
VaArgOffset = -VarArgsSaveSize;

// Record the frame index of the first variable argument
// which is a value necessary to VASTART.
int FI = MFI.CreateFixedObject(RegSize, VaArgOffset, true);
XtensaFI->setVarArgsInRegsFrameIndex(FI);

// Copy the integer registers that may have been used for passing varargs
// to the vararg save area.
for (unsigned I = Idx; I < ArgRegsNum; ++I, VaArgOffset += RegSize) {
const Register Reg = RegInfo.createVirtualRegister(RC);
RegInfo.addLiveIn(IntRegs[I], Reg);

SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegTy);
FI = MFI.CreateFixedObject(RegSize, VaArgOffset, true);
SDValue PtrOff = DAG.getFrameIndex(FI, FITy);
SDValue Store = DAG.getStore(Chain, DL, ArgValue, PtrOff,
MachinePointerInfo::getFixedStack(MF, FI));
OutChains.push_back(Store);
}
}
}

// All stores are grouped in one node to allow the matching between
// the size of Ins and InVals. This only happens when on varg functions
if (!OutChains.empty()) {
Expand Down Expand Up @@ -579,9 +632,6 @@ XtensaTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals,
const SDLoc &DL, SelectionDAG &DAG) const {
if (IsVarArg)
report_fatal_error("VarArg not supported");

MachineFunction &MF = DAG.getMachineFunction();

// Assign locations to each returned value.
Expand Down Expand Up @@ -859,6 +909,156 @@ SDValue XtensaTargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op,
return DAG.getMergeValues(Ops, DL);
}

SDValue XtensaTargetLowering::LowerVASTART(SDValue Op,
SelectionDAG &DAG) const {
MachineFunction &MF = DAG.getMachineFunction();
XtensaMachineFunctionInfo *XtensaFI = MF.getInfo<XtensaMachineFunctionInfo>();
SDValue Chain = Op.getOperand(0);
SDValue Addr = Op.getOperand(1);
EVT PtrVT = Addr.getValueType();
SDLoc DL(Op);

// Struct va_list_tag
// int32 *va_stk - points to the arguments passed in memory
// int32 *va_reg - points to the registers with arguments saved in memory
// int32 va_ndx - offset from va_stk or va_reg pointers which points to the
// next variable argument

SDValue VAIndex;
SDValue StackOffsetFI =
DAG.getFrameIndex(XtensaFI->getVarArgsOnStackFrameIndex(), PtrVT);
unsigned ArgWords = XtensaFI->getVarArgsFirstGPR() - 2;

// If first variable argument passed in registers (maximum words in registers
// is 6) then set va_ndx to the position of this argument in registers area
// stored in memory (va_reg pointer). Otherwise va_ndx should point to the
// position of the first variable argument on stack (va_stk pointer).
if (ArgWords < 6) {
VAIndex = DAG.getConstant(ArgWords * 4, DL, MVT::i32);
} else {
VAIndex = DAG.getConstant(32, DL, MVT::i32);
}

SDValue FrameIndex =
DAG.getFrameIndex(XtensaFI->getVarArgsInRegsFrameIndex(), PtrVT);
uint64_t FrameOffset = PtrVT.getStoreSize();
const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue();

// Store pointer to arguments given on stack (va_stk)
SDValue StackPtr = DAG.getNode(ISD::SUB, DL, PtrVT, StackOffsetFI,
DAG.getConstant(32, DL, PtrVT));

SDValue StoreStackPtr =
DAG.getStore(Chain, DL, StackPtr, Addr, MachinePointerInfo(SV));

uint64_t NextOffset = FrameOffset;
SDValue NextPtr =
DAG.getObjectPtrOffset(DL, Addr, TypeSize::getFixed(NextOffset));

// Store pointer to arguments given on registers (va_reg)
SDValue StoreRegPtr = DAG.getStore(StoreStackPtr, DL, FrameIndex, NextPtr,
MachinePointerInfo(SV, NextOffset));
NextOffset += FrameOffset;
NextPtr = DAG.getObjectPtrOffset(DL, Addr, TypeSize::getFixed(NextOffset));

// Store third word : position in bytes of the first VA argument (va_ndx)
return DAG.getStore(StoreRegPtr, DL, VAIndex, NextPtr,
MachinePointerInfo(SV, NextOffset));
}

SDValue XtensaTargetLowering::LowerVACOPY(SDValue Op, SelectionDAG &DAG) const {
// Size of the va_list_tag structure
constexpr unsigned VAListSize = 3 * 4;
SDValue Chain = Op.getOperand(0);
SDValue DstPtr = Op.getOperand(1);
SDValue SrcPtr = Op.getOperand(2);
const Value *DstSV = cast<SrcValueSDNode>(Op.getOperand(3))->getValue();
const Value *SrcSV = cast<SrcValueSDNode>(Op.getOperand(4))->getValue();
SDLoc DL(Op);

return DAG.getMemcpy(Chain, DL, DstPtr, SrcPtr,
DAG.getConstant(VAListSize, SDLoc(Op), MVT::i32),
Align(4), /*isVolatile*/ false, /*AlwaysInline*/ true,
/*CI=*/nullptr, std::nullopt, MachinePointerInfo(DstSV),
MachinePointerInfo(SrcSV));
}

SDValue XtensaTargetLowering::LowerVAARG(SDValue Op, SelectionDAG &DAG) const {
SDNode *Node = Op.getNode();
EVT VT = Node->getValueType(0);
Type *Ty = VT.getTypeForEVT(*DAG.getContext());
EVT PtrVT = Op.getValueType();
SDValue InChain = Node->getOperand(0);
SDValue VAListPtr = Node->getOperand(1);
const Value *SV = cast<SrcValueSDNode>(Node->getOperand(2))->getValue();
SDLoc DL(Node);
auto &TD = DAG.getDataLayout();
Align ArgAlignment = TD.getABITypeAlign(Ty);
unsigned ArgAlignInBytes = ArgAlignment.value();
unsigned ArgSizeInBytes = TD.getTypeAllocSize(Ty);
unsigned VASizeInBytes = llvm::alignTo(ArgSizeInBytes, 4);

// va_stk
SDValue VAStack =
DAG.getLoad(MVT::i32, DL, InChain, VAListPtr, MachinePointerInfo());
InChain = VAStack.getValue(1);

// va_reg
SDValue VARegPtr =
DAG.getObjectPtrOffset(DL, VAListPtr, TypeSize::getFixed(4));
SDValue VAReg =
DAG.getLoad(MVT::i32, DL, InChain, VARegPtr, MachinePointerInfo());
InChain = VAReg.getValue(1);

// va_ndx
SDValue VarArgIndexPtr =
DAG.getObjectPtrOffset(DL, VARegPtr, TypeSize::getFixed(4));
SDValue VAIndex =
DAG.getLoad(MVT::i32, DL, InChain, VarArgIndexPtr, MachinePointerInfo());
InChain = VAIndex.getValue(1);

SDValue OrigIndex = VAIndex;

if (ArgAlignInBytes > 4) {
OrigIndex = DAG.getNode(ISD::ADD, DL, PtrVT, OrigIndex,
DAG.getConstant(ArgAlignInBytes - 1, DL, MVT::i32));
OrigIndex =
DAG.getNode(ISD::AND, DL, PtrVT, OrigIndex,
DAG.getSignedConstant(-ArgAlignInBytes, DL, MVT::i32));
}

VAIndex = DAG.getNode(ISD::ADD, DL, PtrVT, OrigIndex,
DAG.getConstant(VASizeInBytes, DL, MVT::i32));

SDValue CC = DAG.getSetCC(DL, MVT::i32, OrigIndex,
DAG.getConstant(6 * 4, DL, MVT::i32), ISD::SETLE);

SDValue StkIndex =
DAG.getNode(ISD::ADD, DL, PtrVT, VAIndex,
DAG.getConstant(32 + VASizeInBytes, DL, MVT::i32));
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe should use getObjectPtrOffset in a follow up for all of these adds

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added use of getObjectPtrOffset to access vararg struct fields.


CC = DAG.getSetCC(DL, MVT::i32, VAIndex, DAG.getConstant(6 * 4, DL, MVT::i32),
ISD::SETLE);

SDValue Array = DAG.getNode(ISD::SELECT, DL, MVT::i32, CC, VAReg, VAStack);

VAIndex = DAG.getNode(ISD::SELECT, DL, MVT::i32, CC, VAIndex, StkIndex);

CC = DAG.getSetCC(DL, MVT::i32, VAIndex, DAG.getConstant(6 * 4, DL, MVT::i32),
ISD::SETLE);

SDValue VAIndexStore = DAG.getStore(InChain, DL, VAIndex, VarArgIndexPtr,
MachinePointerInfo(SV));
InChain = VAIndexStore;

SDValue Addr = DAG.getNode(ISD::SUB, DL, PtrVT, VAIndex,
DAG.getConstant(VASizeInBytes, DL, MVT::i32));

Addr = DAG.getNode(ISD::ADD, DL, PtrVT, Array, Addr);

return DAG.getLoad(VT, DL, InChain, Addr, MachinePointerInfo());
}

SDValue XtensaTargetLowering::LowerShiftLeftParts(SDValue Op,
SelectionDAG &DAG) const {
SDLoc DL(Op);
Expand Down Expand Up @@ -1001,6 +1201,12 @@ SDValue XtensaTargetLowering::LowerOperation(SDValue Op,
return LowerFRAMEADDR(Op, DAG);
case ISD::DYNAMIC_STACKALLOC:
return LowerDYNAMIC_STACKALLOC(Op, DAG);
case ISD::VASTART:
return LowerVASTART(Op, DAG);
case ISD::VAARG:
return LowerVAARG(Op, DAG);
case ISD::VACOPY:
return LowerVACOPY(Op, DAG);
case ISD::SHL_PARTS:
return LowerShiftLeftParts(Op, DAG);
case ISD::SRA_PARTS:
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Target/Xtensa/XtensaISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ class XtensaTargetLowering : public TargetLowering {

SDValue LowerSTACKRESTORE(SDValue Op, SelectionDAG &DAG) const;

SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const;

SDValue LowerVAARG(SDValue Op, SelectionDAG &DAG) const;

SDValue LowerVACOPY(SDValue Op, SelectionDAG &DAG) const;

SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const;

SDValue LowerShiftLeftParts(SDValue Op, SelectionDAG &DAG) const;
Expand Down
17 changes: 16 additions & 1 deletion llvm/lib/Target/Xtensa/XtensaMachineFunctionInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,32 @@ namespace llvm {
class XtensaMachineFunctionInfo : public MachineFunctionInfo {
/// FrameIndex of the spill slot for the scratch register in BranchRelaxation.
int BranchRelaxationScratchFrameIndex = -1;
unsigned VarArgsFirstGPR;
int VarArgsOnStackFrameIndex;
int VarArgsInRegsFrameIndex;

public:
explicit XtensaMachineFunctionInfo(const Function &F,
const TargetSubtargetInfo *STI) {}
const TargetSubtargetInfo *STI)
: VarArgsFirstGPR(0), VarArgsOnStackFrameIndex(0),
VarArgsInRegsFrameIndex(0) {}

int getBranchRelaxationScratchFrameIndex() const {
return BranchRelaxationScratchFrameIndex;
}
void setBranchRelaxationScratchFrameIndex(int Index) {
BranchRelaxationScratchFrameIndex = Index;
}

unsigned getVarArgsFirstGPR() const { return VarArgsFirstGPR; }
void setVarArgsFirstGPR(unsigned GPR) { VarArgsFirstGPR = GPR; }

int getVarArgsOnStackFrameIndex() const { return VarArgsOnStackFrameIndex; }
void setVarArgsOnStackFrameIndex(int FI) { VarArgsOnStackFrameIndex = FI; }

// Get and set the frame index of the first stack vararg.
int getVarArgsInRegsFrameIndex() const { return VarArgsInRegsFrameIndex; }
void setVarArgsInRegsFrameIndex(int FI) { VarArgsInRegsFrameIndex = FI; }
};

} // namespace llvm
Expand Down
Loading
Loading