[llvm] [Xtensa] Implement vararg support. (PR #117126)

Andrei Safronov via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 21 00:19:13 PST 2024


https://github.com/andreisfr created https://github.com/llvm/llvm-project/pull/117126

None

>From 174b461f6a7ba6cb44472158beab501ecb19f14d Mon Sep 17 00:00:00 2001
From: Andrei Safronov <safronov at espressif.com>
Date: Thu, 21 Nov 2024 11:16:01 +0300
Subject: [PATCH] [Xtensa] Implement vararg support.

---
 llvm/lib/Target/Xtensa/XtensaISelLowering.cpp | 556 +++++++++++++++++-
 llvm/lib/Target/Xtensa/XtensaISelLowering.h   |   9 +
 .../Target/Xtensa/XtensaMachineFunctionInfo.h |  16 +-
 3 files changed, 574 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
index 5450222a7b2e1d..b2bc7f2469f790 100644
--- a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
+++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
@@ -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"
@@ -133,6 +134,14 @@ XtensaTargetLowering::XtensaTargetLowering(const TargetMachine &TM,
   setOperationAction(ISD::STACKSAVE, MVT::Other, Custom);
   setOperationAction(ISD::STACKRESTORE, MVT::Other, Custom);
 
+  // VASTART and VACOPY need to deal with the Xtensa-specific varargs
+  // structure, but VAEND is a no-op.
+  setOperationAction(ISD::VASTART, MVT::Other, Custom);
+  // we use special va_list structure so we have to customize this
+  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());
 }
@@ -211,6 +220,11 @@ void XtensaTargetLowering::LowerAsmOperandForConstraint(
   TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG);
 }
 
+unsigned XtensaTargetLowering::getVaListSizeInBits(const DataLayout &DL) const {
+  // 2 * sizeof(int*) + sizeof(int)
+  return 3 * 4;
+}
+
 //===----------------------------------------------------------------------===//
 // Calling conventions
 //===----------------------------------------------------------------------===//
@@ -304,13 +318,14 @@ SDValue XtensaTargetLowering::LowerFormalArguments(
     SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const {
   MachineFunction &MF = DAG.getMachineFunction();
   MachineFrameInfo &MFI = MF.getFrameInfo();
+  XtensaMachineFunctionInfo *XtensaFI = MF.getInfo<XtensaMachineFunctionInfo>();
+  EVT PtrVT = getPointerTy(MF.getDataLayout());
+
+  XtensaFI->setVarArgsFrameIndex(0);
 
   // 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,
@@ -378,6 +393,68 @@ SDValue XtensaTargetLowering::LowerFormalArguments(
     }
   }
 
+  if (IsVarArg) {
+    static const MCPhysReg XtensaArgRegs[6] = {
+        Xtensa::A2, Xtensa::A3, Xtensa::A4, Xtensa::A5, Xtensa::A6, Xtensa::A7};
+    ArrayRef<MCPhysReg> ArgRegs = ArrayRef(XtensaArgRegs);
+    unsigned Idx = CCInfo.getFirstUnallocated(ArgRegs);
+    const TargetRegisterClass *RC = &Xtensa::ARRegClass;
+    MachineFrameInfo &MFI = MF.getFrameInfo();
+    MachineRegisterInfo &RegInfo = MF.getRegInfo();
+    unsigned RegSize = 4;
+    MVT RegTy = MVT::getIntegerVT(RegSize * 8);
+
+    XtensaFI->setVarArgsFirstGPR(Idx + 2); // 2 - number of a2 register
+
+    XtensaFI->setVarArgsStackOffset(MFI.CreateFixedObject(
+        PtrVT.getSizeInBits() / 8, 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 (ArgRegs.size() == Idx) {
+      VaArgOffset = CCInfo.getStackSize();
+      VarArgsSaveSize = 0;
+    } else {
+      VarArgsSaveSize = RegSize * (ArgRegs.size() - 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->setVarArgsFrameIndex(FI);
+
+    // Copy the integer registers that may have been used for passing varargs
+    // to the vararg save area.
+    for (unsigned I = Idx; I < ArgRegs.size(); ++I, VaArgOffset += RegSize) {
+      const unsigned Reg = RegInfo.createVirtualRegister(RC);
+      unsigned FrameReg = Subtarget.getRegisterInfo()->getFrameRegister(MF);
+
+      // Argument passed in FrameReg we save in A8 (in emitPrologue),
+      // so load argument from A8
+      if (ArgRegs[I] == FrameReg) {
+        RegInfo.addLiveIn(Xtensa::A8, Reg);
+      } else {
+        RegInfo.addLiveIn(ArgRegs[I], Reg);
+      }
+
+      SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegTy);
+      FI = MFI.CreateFixedObject(RegSize, VaArgOffset, true);
+      SDValue PtrOff = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout()));
+      SDValue Store = DAG.getStore(Chain, DL, ArgValue, PtrOff,
+                                   MachinePointerInfo::getFixedStack(MF, FI));
+      cast<StoreSDNode>(Store.getNode())
+          ->getMemOperand()
+          ->setValue((Value *)nullptr);
+      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()) {
@@ -579,9 +656,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.
@@ -859,6 +933,470 @@ 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>();
+  EVT PtrVT = getPointerTy(DAG.getDataLayout());
+  SDLoc DL(Op);
+
+  SDValue Chain = Op.getOperand(0);
+  SDValue Addr = Op.getOperand(1);
+
+  // typedef struct __va_list_tag {
+  //   int32_t *__va_stk; /* Initialized to point  to the position of the
+  //                       * first argument in memory offset to account for
+  //                       the
+  //                       * arguments passed in registers and to account for
+  //                       * the size of the argument registers not being
+  //                       16-byte
+  //                       * aligned.  E.G., there are 6 argument registers
+  //                       * of 4 bytes each, but we want the __va_ndx for the
+  //                       * first stack argument to have the maximal
+  //                       * alignment of 16 bytes, so we offset the __va_stk
+  //                       address by
+  //                       * 32 bytes so that __va_stk[32] references the
+  //                       first
+  //                       * argument on the stack.
+  //                       */
+  //   int32_t  *__va_reg; /* Points to a stack-allocated region holding the
+  //                        * contents
+  //                        * of the incoming argument registers
+  //                        */
+  //   int32_t __va_ndx;   /* Index initialized to the position of the first
+  //                        * unnamed (variable) argument.  This same index is
+  //                        also
+  //                        * used to address the arguments passed in memory.
+  //                       */
+  //  } __va_list_tag[1];
+
+  SDValue ArgAR;
+  SDValue OverflowPtrAdvance;
+  SDValue StackOffsetFI =
+      DAG.getFrameIndex(XtensaFI->getVarArgsStackOffset(), PtrVT);
+
+  if (XtensaFI->getVarArgsFirstGPR() < 8) {
+    ArgAR =
+        DAG.getConstant(XtensaFI->getVarArgsFirstGPR() * 4 - 8, DL, MVT::i32);
+    OverflowPtrAdvance = DAG.getConstant(32, DL, PtrVT);
+  } else {
+    OverflowPtrAdvance = DAG.getNode(ISD::AND, DL, PtrVT, StackOffsetFI,
+                                     DAG.getConstant(0xf, DL, PtrVT));
+    OverflowPtrAdvance = DAG.getNode(ISD::ADD, DL, PtrVT, OverflowPtrAdvance,
+                                     DAG.getConstant(32, DL, PtrVT));
+    ArgAR = OverflowPtrAdvance;
+  }
+
+  SDValue FR = DAG.getFrameIndex(XtensaFI->getVarArgsFrameIndex(), PtrVT);
+
+  uint64_t FrameOffset = PtrVT.getSizeInBits() / 8;
+  SDValue ConstFrameOffset1 = DAG.getConstant(FrameOffset, DL, PtrVT);
+  SDValue ConstFrameOffset2 = DAG.getConstant(FrameOffset * 2, DL, PtrVT);
+
+  const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue();
+
+  // Store first word : arguments given in stack  (__va_stk)
+  // Advance Argument Overflow pointer down, lest it will point to start
+  // after register argument va_arg finished
+  SDValue StackOffsetFICorr =
+      DAG.getNode(ISD::SUB, DL, PtrVT, StackOffsetFI, OverflowPtrAdvance);
+  SDValue firstStore =
+      DAG.getStore(Chain, DL, StackOffsetFICorr, Addr, MachinePointerInfo(SV));
+
+  uint64_t nextOffset = FrameOffset;
+  SDValue nextPtr = DAG.getNode(ISD::ADD, DL, PtrVT, Addr, ConstFrameOffset1);
+
+  // Store second word : arguments given on registers  (__va_reg)
+  SDValue FRAdvance =
+      DAG.getConstant(XtensaFI->getVarArgsFirstGPR() * 4 - 8, DL, PtrVT);
+  SDValue FRDecr = DAG.getNode(ISD::SUB, DL, PtrVT, FR, FRAdvance);
+  SDValue secondStore = DAG.getStore(firstStore, DL, FRDecr, nextPtr,
+                                     MachinePointerInfo(SV, nextOffset));
+  nextOffset += FrameOffset;
+  nextPtr = DAG.getNode(ISD::ADD, DL, PtrVT, Addr, ConstFrameOffset2);
+
+  // Store first word : number of int regs  (__va_ndx)
+  return DAG.getStore(secondStore, DL, ArgAR, nextPtr,
+                      MachinePointerInfo(SV, nextOffset));
+}
+
+SDValue XtensaTargetLowering::LowerVACOPY(SDValue Op, SelectionDAG &DAG) const {
+  unsigned VAListSize = getVaListSizeInBits(DAG.getDataLayout());
+  return DAG.getMemcpy(
+      Op.getOperand(0), Op, Op.getOperand(1), Op.getOperand(2),
+      DAG.getConstant(VAListSize, SDLoc(Op), MVT::i32), Align(4),
+      /*isVolatile=*/false, /*AlwaysInline=*/false,
+      /*CI=*/nullptr, std::nullopt, MachinePointerInfo(), MachinePointerInfo());
+}
+
+SDValue XtensaTargetLowering::LowerVAARG(SDValue Op, SelectionDAG &DAG) const {
+  SDNode *Node = Op.getNode();
+  EVT VT = Node->getValueType(0);
+  EVT PtrVT = getPointerTy(DAG.getDataLayout());
+  SDValue InChain = Node->getOperand(0);
+  SDValue VAListPtr = Node->getOperand(1);
+  const Value *SV = cast<SrcValueSDNode>(Node->getOperand(2))->getValue();
+  SDLoc DL(Node);
+#if 1
+#if 0
+  /// VAARG - VAARG has four operands: an input chain, a pointer, a SRCVALUE,
+  /// and the alignment. It returns a pair of values: the vaarg value and a
+  /// new chain.
+  VAARG,
+
+  /// VACOPY - VACOPY has 5 operands: an input chain, a destination pointer,
+  /// a source pointer, a SRCVALUE for the destination, and a SRCVALUE for the
+  /// source.
+  VACOPY,
+
+  /// VAEND, VASTART - VAEND and VASTART have three operands: an input chain,
+  /// pointer, and a SRCVALUE.
+  VAEND,
+  VASTART,
+
+#endif
+  auto &TD = DAG.getDataLayout();
+  Align ArgAlignment = TD.getPrefTypeAlign(VT.getTypeForEVT(*DAG.getContext()));
+  unsigned ArgAlignInBytes = ArgAlignment.value();
+  unsigned ArgSizeInBytes =
+      TD.getTypeAllocSize(VT.getTypeForEVT(*DAG.getContext()));
+  unsigned VASizeInBytes = (ArgSizeInBytes + 3) & 0x3;
+
+  // areas
+  // va_stk
+  SDValue OverflowArea =
+      DAG.getLoad(MVT::i32, DL, InChain, VAListPtr, MachinePointerInfo());
+  InChain = OverflowArea.getValue(1);
+
+  SDValue RegSaveAreaPtr = DAG.getNode(ISD::ADD, DL, PtrVT, VAListPtr,
+                                       DAG.getConstant(4, DL, MVT::i32));
+
+  // va_reg
+  SDValue RegSaveArea =
+      DAG.getLoad(MVT::i32, DL, InChain, RegSaveAreaPtr, MachinePointerInfo());
+  InChain = RegSaveArea.getValue(1);
+
+  // va_ndx
+  SDValue ARAreaPtr = DAG.getNode(ISD::ADD, DL, PtrVT, RegSaveAreaPtr,
+                                  DAG.getConstant(4, DL, MVT::i32));
+
+  SDValue ARIndex =
+      DAG.getLoad(MVT::i32, DL, InChain, ARAreaPtr, MachinePointerInfo());
+  InChain = ARIndex.getValue(1);
+
+  SDValue OrigIndex = ARIndex;
+
+  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.getConstant(-ArgAlignInBytes, DL, MVT::i32));
+  }
+
+  ARIndex = 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, ARIndex,
+                  DAG.getConstant(32 + VASizeInBytes, DL, MVT::i32));
+
+  CC = DAG.getSetCC(DL, MVT::i32, ARIndex, DAG.getConstant(6 * 4, DL, MVT::i32),
+                    ISD::SETLE);
+
+  SDValue Array =
+      DAG.getNode(ISD::SELECT, DL, MVT::i32, CC, RegSaveArea, OverflowArea);
+
+  ARIndex = DAG.getNode(ISD::SELECT, DL, MVT::i32, CC, ARIndex, StkIndex);
+
+  CC = DAG.getSetCC(DL, MVT::i32, ARIndex, DAG.getConstant(6 * 4, DL, MVT::i32),
+                    ISD::SETLE);
+
+  SDValue ARIndexStore =
+      DAG.getStore(InChain, DL, ARIndex, ARAreaPtr, MachinePointerInfo(SV));
+  InChain = ARIndexStore;
+
+  SDValue Addr = DAG.getNode(ISD::SUB, DL, PtrVT, ARIndex,
+                             DAG.getConstant(VASizeInBytes, DL, MVT::i32));
+
+  Addr = DAG.getNode(ISD::ADD, DL, PtrVT, Array, Addr);
+
+  return DAG.getLoad(VT, DL, InChain, Addr, MachinePointerInfo());
+
+#if 0
+  //////////////////////////////////////////////
+  // va_stk
+  SDValue GprIndex = DAG.getExtLoad(ISD::ZEXTLOAD, dl, MVT::i32, InChain,
+                                    VAListPtr, MachinePointerInfo(SV), MVT::i8);
+  InChain = GprIndex.getValue(1);
+
+  if (VT == MVT::i64) {
+    // Check if GprIndex is even
+    SDValue GprAnd = DAG.getNode(ISD::AND, dl, MVT::i32, GprIndex,
+                                 DAG.getConstant(1, dl, MVT::i32));
+    SDValue CC64 = DAG.getSetCC(dl, MVT::i32, GprAnd,
+                                DAG.getConstant(0, dl, MVT::i32), ISD::SETNE);
+    SDValue GprIndexPlusOne = DAG.getNode(ISD::ADD, dl, MVT::i32, GprIndex,
+                                          DAG.getConstant(1, dl, MVT::i32));
+    // Align GprIndex to be even if it isn't
+    GprIndex =
+        DAG.getNode(ISD::SELECT, dl, MVT::i32, CC64, GprIndexPlusOne, GprIndex);
+  }
+
+  // fpr index is 1 byte after gpr
+  SDValue FprPtr = DAG.getNode(ISD::ADD, dl, PtrVT, VAListPtr,
+                               DAG.getConstant(1, dl, MVT::i32));
+
+  // fpr
+  SDValue FprIndex = DAG.getExtLoad(ISD::ZEXTLOAD, dl, MVT::i32, InChain,
+                                    FprPtr, MachinePointerInfo(SV), MVT::i8);
+  InChain = FprIndex.getValue(1);
+
+  SDValue RegSaveAreaPtr = DAG.getNode(ISD::ADD, dl, PtrVT, VAListPtr,
+                                       DAG.getConstant(8, dl, MVT::i32));
+
+  SDValue OverflowAreaPtr = DAG.getNode(ISD::ADD, dl, PtrVT, VAListPtr,
+                                        DAG.getConstant(4, dl, MVT::i32));
+
+  // areas
+  SDValue OverflowArea =
+      DAG.getLoad(MVT::i32, dl, InChain, OverflowAreaPtr, MachinePointerInfo());
+  InChain = OverflowArea.getValue(1);
+
+  SDValue RegSaveArea =
+      DAG.getLoad(MVT::i32, dl, InChain, RegSaveAreaPtr, MachinePointerInfo());
+  InChain = RegSaveArea.getValue(1);
+
+  // select overflow_area if index > 8
+  SDValue CC = DAG.getSetCC(dl, MVT::i32, VT.isInteger() ? GprIndex : FprIndex,
+                            DAG.getConstant(8, dl, MVT::i32), ISD::SETLT);
+
+  // adjustment constant gpr_index * 4/8
+  SDValue RegConstant =
+      DAG.getNode(ISD::MUL, dl, MVT::i32, VT.isInteger() ? GprIndex : FprIndex,
+                  DAG.getConstant(VT.isInteger() ? 4 : 8, dl, MVT::i32));
+
+  // OurReg = RegSaveArea + RegConstant
+  SDValue OurReg = DAG.getNode(ISD::ADD, dl, PtrVT, RegSaveArea, RegConstant);
+
+  // Floating types are 32 bytes into RegSaveArea
+  if (VT.isFloatingPoint())
+    OurReg = DAG.getNode(ISD::ADD, dl, PtrVT, OurReg,
+                         DAG.getConstant(32, dl, MVT::i32));
+
+  // increase {f,g}pr_index by 1 (or 2 if VT is i64)
+  SDValue IndexPlus1 =
+      DAG.getNode(ISD::ADD, dl, MVT::i32, VT.isInteger() ? GprIndex : FprIndex,
+                  DAG.getConstant(VT == MVT::i64 ? 2 : 1, dl, MVT::i32));
+
+  InChain = DAG.getTruncStore(InChain, dl, IndexPlus1,
+                              VT.isInteger() ? VAListPtr : FprPtr,
+                              MachinePointerInfo(SV), MVT::i8);
+
+  // determine if we should load from reg_save_area or overflow_area
+  SDValue Result =
+      DAG.getNode(ISD::SELECT, dl, PtrVT, CC, OurReg, OverflowArea);
+
+  // increase overflow_area by 4/8 if gpr/fpr > 8
+  SDValue OverflowAreaPlusN =
+      DAG.getNode(ISD::ADD, dl, PtrVT, OverflowArea,
+                  DAG.getConstant(VT.isInteger() ? 4 : 8, dl, MVT::i32));
+
+  InChain = DAG.getTruncStore(InChain, dl, OverflowArea,
+                              Ove OverflowArea =
+                                  DAG.getNode(ISD::SELECT, dl, MVT::i32, CC,
+                                              OverflowArea, OverflowAreaPlusN);
+                              rflowAreaPtr, MachinePointerInfo(), MVT::i32);
+
+  return DAG.getLoad(VT, dl, InChain, Result, MachinePointerInfo());
+#endif
+#else
+  // gpr_index
+  SDValue GprIndex = DAG.getExtLoad(ISD::ZEXTLOAD, dl, MVT::i32, InChain,
+                                    VAListPtr, MachinePointerInfo(SV), MVT::i8);
+  InChain = GprIndex.getValue(1);
+
+  if (VT == MVT::i64) {
+    // Check if GprIndex is even
+    SDValue GprAnd = DAG.getNode(ISD::AND, dl, MVT::i32, GprIndex,
+                                 DAG.getConstant(1, dl, MVT::i32));
+    SDValue CC64 = DAG.getSetCC(dl, MVT::i32, GprAnd,
+                                DAG.getConstant(0, dl, MVT::i32), ISD::SETNE);
+    SDValue GprIndexPlusOne = DAG.getNode(ISD::ADD, dl, MVT::i32, GprIndex,
+                                          DAG.getConstant(1, dl, MVT::i32));
+    // Align GprIndex to be even if it isn't
+    GprIndex =
+        DAG.getNode(ISD::SELECT, dl, MVT::i32, CC64, GprIndexPlusOne, GprIndex);
+  }
+
+  // fpr index is 1 byte after gpr
+  SDValue FprPtr = DAG.getNode(ISD::ADD, dl, PtrVT, VAListPtr,
+                               DAG.getConstant(1, dl, MVT::i32));
+
+  // fpr
+  SDValue FprIndex = DAG.getExtLoad(ISD::ZEXTLOAD, dl, MVT::i32, InChain,
+                                    FprPtr, MachinePointerInfo(SV), MVT::i8);
+  InChain = FprIndex.getValue(1);
+
+  SDValue RegSaveAreaPtr = DAG.getNode(ISD::ADD, dl, PtrVT, VAListPtr,
+                                       DAG.getConstant(8, dl, MVT::i32));
+
+  SDValue OverflowAreaPtr = DAG.getNode(ISD::ADD, dl, PtrVT, VAListPtr,
+                                        DAG.getConstant(4, dl, MVT::i32));
+
+  // areas
+  SDValue OverflowArea =
+      DAG.getLoad(MVT::i32, dl, InChain, OverflowAreaPtr, MachinePointerInfo());
+  InChain = OverflowArea.getValue(1);
+
+  SDValue RegSaveArea =
+      DAG.getLoad(MVT::i32, dl, InChain, RegSaveAreaPtr, MachinePointerInfo());
+  InChain = RegSaveArea.getValue(1);
+
+  // select overflow_area if index > 8
+  SDValue CC = DAG.getSetCC(dl, MVT::i32, VT.isInteger() ? GprIndex : FprIndex,
+                            DAG.getConstant(8, dl, MVT::i32), ISD::SETLT);
+
+  // adjustment constant gpr_index * 4/8
+  SDValue RegConstant =
+      DAG.getNode(ISD::MUL, dl, MVT::i32, VT.isInteger() ? GprIndex : FprIndex,
+                  DAG.getConstant(VT.isInteger() ? 4 : 8, dl, MVT::i32));
+
+  // OurReg = RegSaveArea + RegConstant
+  SDValue OurReg = DAG.getNode(ISD::ADD, dl, PtrVT, RegSaveArea, RegConstant);
+
+  // Floating types are 32 bytes into RegSaveArea
+  if (VT.isFloatingPoint())
+    OurReg = DAG.getNode(ISD::ADD, dl, PtrVT, OurReg,
+                         DAG.getConstant(32, dl, MVT::i32));
+
+  // increase {f,g}pr_index by 1 (or 2 if VT is i64)
+  SDValue IndexPlus1 =
+      DAG.getNode(ISD::ADD, dl, MVT::i32, VT.isInteger() ? GprIndex : FprIndex,
+                  DAG.getConstant(VT == MVT::i64 ? 2 : 1, dl, MVT::i32));
+
+  InChain = DAG.getTruncStore(InChain, dl, IndexPlus1,
+                              VT.isInteger() ? VAListPtr : FprPtr,
+                              MachinePointerInfo(SV), MVT::i8);
+
+  // determine if we should load from reg_save_area or overflow_area
+  SDValue Result =
+      DAG.getNode(ISD::SELECT, dl, PtrVT, CC, OurReg, OverflowArea);
+
+  // increase overflow_area by 4/8 if gpr/fpr > 8
+  SDValue OverflowAreaPlusN =
+      DAG.getNode(ISD::ADD, dl, PtrVT, OverflowArea,
+                  DAG.getConstant(VT.isInteger() ? 4 : 8, dl, MVT::i32));
+
+  InChain = DAG.getTruncStore(InChain, dl, OverflowArea,
+                              Ove OverflowArea =
+                                  DAG.getNode(ISD::SELECT, dl, MVT::i32, CC,
+                                              OverflowArea, OverflowAreaPlusN);
+                              rflowAreaPtr, MachinePointerInfo(), MVT::i32);
+
+  return DAG.getLoad(VT, dl, InChain, Result, MachinePointerInfo());
+#endif
+}
+
+#if 0
+Address XtensaABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
+                                 QualType Ty) const {
+  // The va_list structure memory layout:
+  // struct __va_list_tag {
+  //   int32_t *va_stk;
+  //   int32_t *va_reg;
+  //   int32_t va_ndx;
+  // };
+  CGBuilderTy &Builder = CGF.Builder;
+
+  Address OverflowAreaPtr = Builder.CreateStructGEP(VAListAddr, 0, "__va_stk");
+  Address OverflowArea = Address(Builder.CreateLoad(OverflowAreaPtr, ""),
+                                 CGF.Int32Ty, CharUnits::fromQuantity(4));
+  Address RegSaveAreaPtr = Builder.CreateStructGEP(VAListAddr, 1, "__va_reg");
+  Address RegSaveArea = Address(Builder.CreateLoad(RegSaveAreaPtr, ""),
+                                CGF.Int32Ty, CharUnits::fromQuantity(4));
+  Address ARAreaPtr = Builder.CreateStructGEP(VAListAddr, 2, "__va_ndx");
+  llvm::Value *ARIndex = Builder.CreateLoad(ARAreaPtr, "");
+
+  ARIndex = Builder.CreateLShr(ARIndex, Builder.getInt32(2));
+
+  unsigned Align = getContext().getTypeAlign(Ty) / 32;
+  unsigned Size = (getContext().getTypeSize(Ty) + 31) / 32;
+
+  if (Align > 1) {
+    ARIndex = Builder.CreateAdd(ARIndex, Builder.getInt32(Align - 1));
+    ARIndex =
+        Builder.CreateAnd(ARIndex, Builder.getInt32((uint32_t) ~(Align - 1)));
+  }
+
+  llvm::Value *ARIndexNext = Builder.CreateAdd(ARIndex, Builder.getInt32(Size));
+  Builder.CreateStore(Builder.CreateShl(ARIndexNext, Builder.getInt32(2)),
+                      ARAreaPtr);
+
+  const unsigned OverflowLimit = 6;
+  llvm::Value *CC = Builder.CreateICmpULE(
+      ARIndexNext, Builder.getInt32(OverflowLimit), "cond");
+
+  llvm::BasicBlock *UsingRegSaveArea =
+      CGF.createBasicBlock("using_regsavearea");
+  llvm::BasicBlock *UsingOverflow = CGF.createBasicBlock("using_overflow");
+  llvm::BasicBlock *Cont = CGF.createBasicBlock("cont");
+
+  Builder.CreateCondBr(CC, UsingRegSaveArea, UsingOverflow);
+
+  llvm::Type *DirectTy = CGF.ConvertType(Ty);
+
+  // Case 1: consume registers.
+  Address RegAddr = Address::invalid();
+  {
+    CGF.EmitBlock(UsingRegSaveArea);
+
+    CharUnits RegSize = CharUnits::fromQuantity(4);
+    RegSaveArea =
+        Address(Builder.CreateInBoundsGEP(CGF.Int32Ty, RegSaveArea.getPointer(),
+                                          ARIndex),
+                CGF.Int32Ty, RegSaveArea.getAlignment().alignmentOfArrayElement(RegSize));
+    RegAddr = RegSaveArea.withElementType(DirectTy);
+    CGF.EmitBranch(Cont);
+  }
+
+  // Case 2: consume space in the overflow area.
+  Address MemAddr = Address::invalid();
+  {
+    CGF.EmitBlock(UsingOverflow);
+    llvm::Value *CC1 = Builder.CreateICmpULE(
+        ARIndex, Builder.getInt32(OverflowLimit), "cond_overflow");
+
+    llvm::Value *ARIndexOff = Builder.CreateSelect(
+        CC1, Builder.CreateSub(Builder.getInt32(8), ARIndex),
+        Builder.getInt32(0));
+
+    llvm::Value *ARIndexCorr = Builder.CreateAdd(ARIndex, ARIndexOff);
+    llvm::Value *ARIndexNextCorr = Builder.CreateAdd(ARIndexNext, ARIndexOff);
+    Builder.CreateStore(Builder.CreateShl(ARIndexNextCorr, Builder.getInt32(2)),
+                        ARAreaPtr);
+
+    CharUnits RegSize = CharUnits::fromQuantity(4);
+    OverflowArea =
+        Address(Builder.CreateInBoundsGEP(
+                    CGF.Int32Ty, OverflowArea.getPointer(), ARIndexCorr),
+                CGF.Int32Ty, OverflowArea.getAlignment().alignmentOfArrayElement(RegSize));
+    MemAddr = OverflowArea.withElementType(DirectTy);
+    CGF.EmitBranch(Cont);
+  }
+
+  CGF.EmitBlock(Cont);
+
+  // Merge the cases with a phi.
+  Address Result =
+      emitMergePHI(CGF, RegAddr, UsingRegSaveArea, MemAddr, UsingOverflow, "");
+
+  return Result;
+}
+
+#endif
 SDValue XtensaTargetLowering::LowerShiftLeftParts(SDValue Op,
                                                   SelectionDAG &DAG) const {
   SDLoc DL(Op);
@@ -1001,6 +1539,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:
diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.h b/llvm/lib/Target/Xtensa/XtensaISelLowering.h
index f1cd00c41437a4..973b51cd73e4b7 100644
--- a/llvm/lib/Target/Xtensa/XtensaISelLowering.h
+++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.h
@@ -74,6 +74,9 @@ class XtensaTargetLowering : public TargetLowering {
 
   bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override;
 
+  /// Returns the size of the platform's va_list object.
+  unsigned getVaListSizeInBits(const DataLayout &DL) const override;
+
   const char *getTargetNodeName(unsigned Opcode) const override;
 
   std::pair<unsigned, const TargetRegisterClass *>
@@ -148,6 +151,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;
diff --git a/llvm/lib/Target/Xtensa/XtensaMachineFunctionInfo.h b/llvm/lib/Target/Xtensa/XtensaMachineFunctionInfo.h
index c38c060b9387ff..36fbd018bb8c9f 100644
--- a/llvm/lib/Target/Xtensa/XtensaMachineFunctionInfo.h
+++ b/llvm/lib/Target/Xtensa/XtensaMachineFunctionInfo.h
@@ -24,10 +24,14 @@ namespace llvm {
 class XtensaMachineFunctionInfo : public MachineFunctionInfo {
   /// FrameIndex of the spill slot for the scratch register in BranchRelaxation.
   int BranchRelaxationScratchFrameIndex = -1;
+  unsigned VarArgsFirstGPR;
+  int VarArgsStackOffset;
+  unsigned VarArgsFrameIndex;
 
 public:
   explicit XtensaMachineFunctionInfo(const Function &F,
-                                     const TargetSubtargetInfo *STI) {}
+                                     const TargetSubtargetInfo *STI)
+      : VarArgsFirstGPR(0), VarArgsStackOffset(0), VarArgsFrameIndex(0) {}
 
   int getBranchRelaxationScratchFrameIndex() const {
     return BranchRelaxationScratchFrameIndex;
@@ -35,6 +39,16 @@ class XtensaMachineFunctionInfo : public MachineFunctionInfo {
   void setBranchRelaxationScratchFrameIndex(int Index) {
     BranchRelaxationScratchFrameIndex = Index;
   }
+
+  unsigned getVarArgsFirstGPR() const { return VarArgsFirstGPR; }
+  void setVarArgsFirstGPR(unsigned GPR) { VarArgsFirstGPR = GPR; }
+
+  int getVarArgsStackOffset() const { return VarArgsStackOffset; }
+  void setVarArgsStackOffset(int Offset) { VarArgsStackOffset = Offset; }
+
+  // Get and set the frame index of the first stack vararg.
+  unsigned getVarArgsFrameIndex() const { return VarArgsFrameIndex; }
+  void setVarArgsFrameIndex(unsigned FI) { VarArgsFrameIndex = FI; }
 };
 
 } // namespace llvm



More information about the llvm-commits mailing list