[llvm] r322215 - [RISCV] Support for varargs

Alex Bradbury via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 10 11:41:03 PST 2018


Author: asb
Date: Wed Jan 10 11:41:03 2018
New Revision: 322215

URL: http://llvm.org/viewvc/llvm-project?rev=322215&view=rev
Log:
[RISCV] Support for varargs

Includes support for expanding va_copy. Also adds support for using 'aligned'
registers when necessary for vararg calls, and ensure the frame pointer always
points to the bottom of the vararg spill region. This is necessary to ensure
that the saved return address and stack pointer are always available at fixed
known offsets of the frame pointer.

Differential Revision: https://reviews.llvm.org/D40805


Added:
    llvm/trunk/lib/Target/RISCV/RISCVMachineFunctionInfo.h
    llvm/trunk/test/CodeGen/RISCV/vararg.ll
Modified:
    llvm/trunk/lib/Target/RISCV/RISCVFrameLowering.cpp
    llvm/trunk/lib/Target/RISCV/RISCVISelLowering.cpp
    llvm/trunk/lib/Target/RISCV/RISCVISelLowering.h

Modified: llvm/trunk/lib/Target/RISCV/RISCVFrameLowering.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/RISCV/RISCVFrameLowering.cpp?rev=322215&r1=322214&r2=322215&view=diff
==============================================================================
--- llvm/trunk/lib/Target/RISCV/RISCVFrameLowering.cpp (original)
+++ llvm/trunk/lib/Target/RISCV/RISCVFrameLowering.cpp Wed Jan 10 11:41:03 2018
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "RISCVFrameLowering.h"
+#include "RISCVMachineFunctionInfo.h"
 #include "RISCVSubtarget.h"
 #include "llvm/CodeGen/MachineFrameInfo.h"
 #include "llvm/CodeGen/MachineFunction.h"
@@ -91,6 +92,7 @@ void RISCVFrameLowering::emitPrologue(Ma
   }
 
   MachineFrameInfo &MFI = MF.getFrameInfo();
+  auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
   MachineBasicBlock::iterator MBBI = MBB.begin();
 
   unsigned FPReg = getFPReg(STI);
@@ -124,7 +126,8 @@ void RISCVFrameLowering::emitPrologue(Ma
   std::advance(MBBI, CSI.size());
 
   // Generate new FP.
-  adjustReg(MBB, MBBI, DL, FPReg, SPReg, StackSize, MachineInstr::FrameSetup);
+  adjustReg(MBB, MBBI, DL, FPReg, SPReg, StackSize - RVFI->getVarArgsSaveSize(),
+            MachineInstr::FrameSetup);
 }
 
 void RISCVFrameLowering::emitEpilogue(MachineFunction &MF,
@@ -137,6 +140,7 @@ void RISCVFrameLowering::emitEpilogue(Ma
   MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr();
   const RISCVRegisterInfo *RI = STI.getRegisterInfo();
   MachineFrameInfo &MFI = MF.getFrameInfo();
+  auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
   DebugLoc DL = MBBI->getDebugLoc();
   unsigned FPReg = getFPReg(STI);
   unsigned SPReg = getSPReg(STI);
@@ -153,7 +157,8 @@ void RISCVFrameLowering::emitEpilogue(Ma
   // necessary if the stack pointer was modified, meaning the stack size is
   // unknown.
   if (RI->needsStackRealignment(MF) || MFI.hasVarSizedObjects()) {
-    adjustReg(MBB, LastFrameDestroy, DL, SPReg, FPReg, -StackSize,
+    adjustReg(MBB, LastFrameDestroy, DL, SPReg, FPReg,
+              -StackSize + RVFI->getVarArgsSaveSize(),
               MachineInstr::FrameDestroy);
   }
 
@@ -166,6 +171,7 @@ int RISCVFrameLowering::getFrameIndexRef
                                                unsigned &FrameReg) const {
   const MachineFrameInfo &MFI = MF.getFrameInfo();
   const TargetRegisterInfo *RI = MF.getSubtarget().getRegisterInfo();
+  const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
 
   // Callee-saved registers should be referenced relative to the stack
   // pointer (positive offset), otherwise use the frame pointer (negative
@@ -182,10 +188,13 @@ int RISCVFrameLowering::getFrameIndexRef
     MaxCSFI = CSI[CSI.size() - 1].getFrameIdx();
   }
 
-  FrameReg = RI->getFrameRegister(MF);
   if (FI >= MinCSFI && FI <= MaxCSFI) {
     FrameReg = RISCV::X2;
     Offset += MF.getFrameInfo().getStackSize();
+  } else {
+    FrameReg = RI->getFrameRegister(MF);
+    assert(hasFP(MF) && "Offset calculation incorrect if !hasFP");
+    Offset += RVFI->getVarArgsSaveSize();
   }
   return Offset;
 }

Modified: llvm/trunk/lib/Target/RISCV/RISCVISelLowering.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/RISCV/RISCVISelLowering.cpp?rev=322215&r1=322214&r2=322215&view=diff
==============================================================================
--- llvm/trunk/lib/Target/RISCV/RISCVISelLowering.cpp (original)
+++ llvm/trunk/lib/Target/RISCV/RISCVISelLowering.cpp Wed Jan 10 11:41:03 2018
@@ -14,6 +14,7 @@
 
 #include "RISCVISelLowering.h"
 #include "RISCV.h"
+#include "RISCVMachineFunctionInfo.h"
 #include "RISCVRegisterInfo.h"
 #include "RISCVSubtarget.h"
 #include "RISCVTargetMachine.h"
@@ -63,6 +64,11 @@ RISCVTargetLowering::RISCVTargetLowering
   setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
   setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
 
+  setOperationAction(ISD::VASTART, MVT::Other, Custom);
+  setOperationAction(ISD::VAARG, MVT::Other, Expand);
+  setOperationAction(ISD::VACOPY, MVT::Other, Expand);
+  setOperationAction(ISD::VAEND, MVT::Other, Expand);
+
   for (auto VT : {MVT::i1, MVT::i8, MVT::i16})
     setOperationAction(ISD::SIGN_EXTEND_INREG, VT, Expand);
 
@@ -158,6 +164,8 @@ SDValue RISCVTargetLowering::LowerOperat
     return lowerBlockAddress(Op, DAG);
   case ISD::SELECT:
     return lowerSELECT(Op, DAG);
+  case ISD::VASTART:
+    return lowerVASTART(Op, DAG);
   }
 }
 
@@ -261,6 +269,21 @@ SDValue RISCVTargetLowering::lowerSELECT
   return DAG.getNode(RISCVISD::SELECT_CC, DL, VTs, Ops);
 }
 
+SDValue RISCVTargetLowering::lowerVASTART(SDValue Op, SelectionDAG &DAG) const {
+  MachineFunction &MF = DAG.getMachineFunction();
+  RISCVMachineFunctionInfo *FuncInfo = MF.getInfo<RISCVMachineFunctionInfo>();
+
+  SDLoc DL(Op);
+  SDValue FI = DAG.getFrameIndex(FuncInfo->getVarArgsFrameIndex(),
+                                 getPointerTy(MF.getDataLayout()));
+
+  // vastart just stores the address of the VarArgsFrameIndex slot into the
+  // memory location argument.
+  const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue();
+  return DAG.getStore(Op.getOperand(0), DL, FI, Op.getOperand(1),
+                      MachinePointerInfo(SV));
+}
+
 MachineBasicBlock *
 RISCVTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
                                                  MachineBasicBlock *BB) const {
@@ -398,19 +421,34 @@ static bool CC_RISCVAssign2XLen(unsigned
 // Implements the RISC-V calling convention. Returns true upon failure.
 static bool CC_RISCV(const DataLayout &DL, unsigned ValNo, MVT ValVT, MVT LocVT,
                      CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags,
-                     CCState &State, bool IsFixed, bool IsRet) {
+                     CCState &State, bool IsFixed, bool IsRet, Type *OrigTy) {
   unsigned XLen = DL.getLargestLegalIntTypeSizeInBits();
   assert(XLen == 32 || XLen == 64);
   MVT XLenVT = XLen == 32 ? MVT::i32 : MVT::i64;
   assert(ValVT == XLenVT && "Unexpected ValVT");
   assert(LocVT == XLenVT && "Unexpected LocVT");
-  assert(IsFixed && "Vararg support not yet implemented");
 
   // Any return value split in to more than two values can't be returned
   // directly.
   if (IsRet && ValNo > 1)
     return true;
 
+  // If this is a variadic argument, the RISC-V calling convention requires
+  // that it is assigned an 'even' or 'aligned' register if it has 8-byte
+  // alignment (RV32) or 16-byte alignment (RV64). An aligned register should
+  // be used regardless of whether the original argument was split during
+  // legalisation or not. The argument will not be passed by registers if the
+  // original type is larger than 2*XLEN, so the register alignment rule does
+  // not apply.
+  unsigned TwoXLenInBytes = (2 * XLen) / 8;
+  if (!IsFixed && ArgFlags.getOrigAlign() == TwoXLenInBytes &&
+      DL.getTypeAllocSize(OrigTy) == TwoXLenInBytes) {
+    unsigned RegIdx = State.getFirstUnallocated(ArgGPRs);
+    // Skip 'odd' register if necessary.
+    if (RegIdx != array_lengthof(ArgGPRs) && RegIdx % 2 == 1)
+      State.AllocateReg(ArgGPRs);
+  }
+
   SmallVectorImpl<CCValAssign> &PendingLocs = State.getPendingLocs();
   SmallVectorImpl<ISD::ArgFlagsTy> &PendingArgFlags =
       State.getPendingArgFlags();
@@ -482,13 +520,20 @@ void RISCVTargetLowering::analyzeInputAr
     MachineFunction &MF, CCState &CCInfo,
     const SmallVectorImpl<ISD::InputArg> &Ins, bool IsRet) const {
   unsigned NumArgs = Ins.size();
+  FunctionType *FType = MF.getFunction().getFunctionType();
 
   for (unsigned i = 0; i != NumArgs; ++i) {
     MVT ArgVT = Ins[i].VT;
     ISD::ArgFlagsTy ArgFlags = Ins[i].Flags;
 
+    Type *ArgTy = nullptr;
+    if (IsRet)
+      ArgTy = FType->getReturnType();
+    else if (Ins[i].isOrigArg())
+      ArgTy = FType->getParamType(Ins[i].getOrigArgIndex());
+
     if (CC_RISCV(MF.getDataLayout(), i, ArgVT, ArgVT, CCValAssign::Full,
-                 ArgFlags, CCInfo, /*IsRet=*/true, IsRet)) {
+                 ArgFlags, CCInfo, /*IsRet=*/true, IsRet, ArgTy)) {
       DEBUG(dbgs() << "InputArg #" << i << " has unhandled type "
                    << EVT(ArgVT).getEVTString() << '\n');
       llvm_unreachable(nullptr);
@@ -498,15 +543,17 @@ void RISCVTargetLowering::analyzeInputAr
 
 void RISCVTargetLowering::analyzeOutputArgs(
     MachineFunction &MF, CCState &CCInfo,
-    const SmallVectorImpl<ISD::OutputArg> &Outs, bool IsRet) const {
+    const SmallVectorImpl<ISD::OutputArg> &Outs, bool IsRet,
+    CallLoweringInfo *CLI) const {
   unsigned NumArgs = Outs.size();
 
   for (unsigned i = 0; i != NumArgs; i++) {
     MVT ArgVT = Outs[i].VT;
     ISD::ArgFlagsTy ArgFlags = Outs[i].Flags;
+    Type *OrigTy = CLI ? CLI->getArgs()[Outs[i].OrigArgIndex].Ty : nullptr;
 
     if (CC_RISCV(MF.getDataLayout(), i, ArgVT, ArgVT, CCValAssign::Full,
-                 ArgFlags, CCInfo, Outs[i].IsFixed, IsRet)) {
+                 ArgFlags, CCInfo, Outs[i].IsFixed, IsRet, OrigTy)) {
       DEBUG(dbgs() << "OutputArg #" << i << " has unhandled type "
                    << EVT(ArgVT).getEVTString() << "\n");
       llvm_unreachable(nullptr);
@@ -581,9 +628,10 @@ SDValue RISCVTargetLowering::LowerFormal
 
   MachineFunction &MF = DAG.getMachineFunction();
   EVT PtrVT = getPointerTy(DAG.getDataLayout());
-
-  if (IsVarArg)
-    report_fatal_error("VarArg not supported");
+  MVT XLenVT = Subtarget.getXLenVT();
+  unsigned XLenInBytes = Subtarget.getXLen() / 8;
+  // Used with vargs to acumulate store chains.
+  std::vector<SDValue> OutChains;
 
   // Assign locations to all of the incoming arguments.
   SmallVector<CCValAssign, 16> ArgLocs;
@@ -592,7 +640,7 @@ SDValue RISCVTargetLowering::LowerFormal
 
   for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) {
     CCValAssign &VA = ArgLocs[i];
-    assert(VA.getLocVT() == Subtarget.getXLenVT() && "Unhandled argument type");
+    assert(VA.getLocVT() == XLenVT && "Unhandled argument type");
     SDValue ArgValue;
     if (VA.isRegLoc())
       ArgValue = unpackFromRegLoc(DAG, Chain, VA, DL);
@@ -620,6 +668,70 @@ SDValue RISCVTargetLowering::LowerFormal
     }
     InVals.push_back(ArgValue);
   }
+
+  if (IsVarArg) {
+    ArrayRef<MCPhysReg> ArgRegs = makeArrayRef(ArgGPRs);
+    unsigned Idx = CCInfo.getFirstUnallocated(ArgRegs);
+    const TargetRegisterClass *RC = &RISCV::GPRRegClass;
+    MachineFrameInfo &MFI = MF.getFrameInfo();
+    MachineRegisterInfo &RegInfo = MF.getRegInfo();
+    RISCVMachineFunctionInfo *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+
+    // 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.getNextStackOffset();
+      VarArgsSaveSize = 0;
+    } else {
+      VarArgsSaveSize = XLenInBytes * (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(XLenInBytes, VaArgOffset, true);
+    RVFI->setVarArgsFrameIndex(FI);
+
+    // If saving an odd number of registers then create an extra stack slot to
+    // ensure that the frame pointer is 2*XLEN-aligned, which in turn ensures
+    // offsets to even-numbered registered remain 2*XLEN-aligned.
+    if (Idx % 2) {
+      FI = MFI.CreateFixedObject(XLenInBytes, VaArgOffset - (int)XLenInBytes,
+                                 true);
+      VarArgsSaveSize += XLenInBytes;
+    }
+
+    // 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 += XLenInBytes) {
+      const unsigned Reg = RegInfo.createVirtualRegister(RC);
+      RegInfo.addLiveIn(ArgRegs[I], Reg);
+      SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, XLenVT);
+      FI = MFI.CreateFixedObject(XLenInBytes, 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);
+    }
+    RVFI->setVarArgsSaveSize(VarArgsSaveSize);
+  }
+
+  // All stores are grouped in one node to allow the matching between
+  // the size of Ins and InVals. This only happens for vararg functions.
+  if (!OutChains.empty()) {
+    OutChains.push_back(Chain);
+    Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, OutChains);
+  }
+
   return Chain;
 }
 
@@ -640,16 +752,12 @@ SDValue RISCVTargetLowering::LowerCall(C
   EVT PtrVT = getPointerTy(DAG.getDataLayout());
   MVT XLenVT = Subtarget.getXLenVT();
 
-  if (IsVarArg) {
-    report_fatal_error("LowerCall with varargs not implemented");
-  }
-
   MachineFunction &MF = DAG.getMachineFunction();
 
   // Analyze the operands of the call, assigning locations to each operand.
   SmallVector<CCValAssign, 16> ArgLocs;
   CCState ArgCCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext());
-  analyzeOutputArgs(MF, ArgCCInfo, Outs, /*IsRet=*/false);
+  analyzeOutputArgs(MF, ArgCCInfo, Outs, /*IsRet=*/false, &CLI);
 
   // Get a count of how many bytes are to be pushed on the stack.
   unsigned NumBytes = ArgCCInfo.getNextStackOffset();
@@ -823,7 +931,7 @@ bool RISCVTargetLowering::CanLowerReturn
     MVT VT = Outs[i].VT;
     ISD::ArgFlagsTy ArgFlags = Outs[i].Flags;
     if (CC_RISCV(MF.getDataLayout(), i, VT, VT, CCValAssign::Full, ArgFlags,
-                 CCInfo, /*IsFixed=*/true, /*IsRet=*/true))
+                 CCInfo, /*IsFixed=*/true, /*IsRet=*/true, nullptr))
       return false;
   }
   return true;
@@ -835,10 +943,6 @@ RISCVTargetLowering::LowerReturn(SDValue
                                  const SmallVectorImpl<ISD::OutputArg> &Outs,
                                  const SmallVectorImpl<SDValue> &OutVals,
                                  const SDLoc &DL, SelectionDAG &DAG) const {
-  if (IsVarArg) {
-    report_fatal_error("VarArg not supported");
-  }
-
   // Stores the assignment of the return value to a location.
   SmallVector<CCValAssign, 16> RVLocs;
 
@@ -846,7 +950,8 @@ RISCVTargetLowering::LowerReturn(SDValue
   CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs,
                  *DAG.getContext());
 
-  analyzeOutputArgs(DAG.getMachineFunction(), CCInfo, Outs, /*IsRet=*/true);
+  analyzeOutputArgs(DAG.getMachineFunction(), CCInfo, Outs, /*IsRet=*/true,
+                    nullptr);
 
   SDValue Flag;
   SmallVector<SDValue, 4> RetOps(1, Chain);

Modified: llvm/trunk/lib/Target/RISCV/RISCVISelLowering.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/RISCV/RISCVISelLowering.h?rev=322215&r1=322214&r2=322215&view=diff
==============================================================================
--- llvm/trunk/lib/Target/RISCV/RISCVISelLowering.h (original)
+++ llvm/trunk/lib/Target/RISCV/RISCVISelLowering.h Wed Jan 10 11:41:03 2018
@@ -53,7 +53,7 @@ private:
                         bool IsRet) const;
   void analyzeOutputArgs(MachineFunction &MF, CCState &CCInfo,
                          const SmallVectorImpl<ISD::OutputArg> &Outs,
-                         bool IsRet) const;
+                         bool IsRet, CallLoweringInfo *CLI) const;
   // Lower incoming arguments, copy physregs into vregs
   SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv,
                                bool IsVarArg,
@@ -78,6 +78,7 @@ private:
   SDValue lowerBlockAddress(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerSELECT(SDValue Op, SelectionDAG &DAG) const;
+  SDValue lowerVASTART(SDValue Op, SelectionDAG &DAG) const;
 };
 }
 

Added: llvm/trunk/lib/Target/RISCV/RISCVMachineFunctionInfo.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/RISCV/RISCVMachineFunctionInfo.h?rev=322215&view=auto
==============================================================================
--- llvm/trunk/lib/Target/RISCV/RISCVMachineFunctionInfo.h (added)
+++ llvm/trunk/lib/Target/RISCV/RISCVMachineFunctionInfo.h Wed Jan 10 11:41:03 2018
@@ -0,0 +1,44 @@
+//=- RISCVMachineFunctionInfo.h - RISCV machine function info -----*- C++ -*-=//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares RISCV-specific per-machine-function information.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H
+#define LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H
+
+#include "llvm/CodeGen/MachineFunction.h"
+
+namespace llvm {
+
+/// RISCVMachineFunctionInfo - This class is derived from MachineFunctionInfo
+/// and contains private RISCV-specific information for each MachineFunction.
+class RISCVMachineFunctionInfo : public MachineFunctionInfo {
+
+  /// FrameIndex for start of varargs area
+  int VarArgsFrameIndex = 0;
+  /// Size of the save area used for varargs
+  int VarArgsSaveSize = 0;
+
+public:
+  RISCVMachineFunctionInfo() = default;
+
+  explicit RISCVMachineFunctionInfo(MachineFunction &MF) {}
+
+  int getVarArgsFrameIndex() const { return VarArgsFrameIndex; }
+  void setVarArgsFrameIndex(int Index) { VarArgsFrameIndex = Index; }
+
+  unsigned getVarArgsSaveSize() const { return VarArgsSaveSize; }
+  void setVarArgsSaveSize(int Size) { VarArgsSaveSize = Size; }
+};
+
+} // end namespace llvm
+
+#endif // LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H

Added: llvm/trunk/test/CodeGen/RISCV/vararg.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/RISCV/vararg.ll?rev=322215&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/RISCV/vararg.ll (added)
+++ llvm/trunk/test/CodeGen/RISCV/vararg.ll Wed Jan 10 11:41:03 2018
@@ -0,0 +1,535 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefix=RV32I %s
+
+declare void @llvm.va_start(i8*)
+declare void @llvm.va_end(i8*)
+
+declare void @notdead(i8*)
+
+; Although frontends are recommended to not generate va_arg due to the lack of
+; support for aggregate types, we test simple cases here to ensure they are
+; lowered correctly
+
+define i32 @va1(i8* %fmt, ...) nounwind {
+; RV32I-LABEL: va1:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -48
+; RV32I-NEXT:    sw ra, 12(sp)
+; RV32I-NEXT:    sw s0, 8(sp)
+; RV32I-NEXT:    addi s0, sp, 16
+; RV32I-NEXT:    sw a1, 4(s0)
+; RV32I-NEXT:    sw a7, 28(s0)
+; RV32I-NEXT:    sw a6, 24(s0)
+; RV32I-NEXT:    sw a5, 20(s0)
+; RV32I-NEXT:    sw a4, 16(s0)
+; RV32I-NEXT:    sw a3, 12(s0)
+; RV32I-NEXT:    sw a2, 8(s0)
+; RV32I-NEXT:    addi a0, s0, 8
+; RV32I-NEXT:    sw a0, -12(s0)
+; RV32I-NEXT:    lw a0, 4(s0)
+; RV32I-NEXT:    lw s0, 8(sp)
+; RV32I-NEXT:    lw ra, 12(sp)
+; RV32I-NEXT:    addi sp, sp, 48
+; RV32I-NEXT:    ret
+  %va = alloca i8*, align 4
+  %1 = bitcast i8** %va to i8*
+  call void @llvm.va_start(i8* %1)
+  %argp.cur = load i8*, i8** %va, align 4
+  %argp.next = getelementptr inbounds i8, i8* %argp.cur, i32 4
+  store i8* %argp.next, i8** %va, align 4
+  %2 = bitcast i8* %argp.cur to i32*
+  %3 = load i32, i32* %2, align 4
+  call void @llvm.va_end(i8* %1)
+  ret i32 %3
+}
+
+define i32 @va1_va_arg(i8* %fmt, ...) nounwind {
+; RV32I-LABEL: va1_va_arg:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -48
+; RV32I-NEXT:    sw ra, 12(sp)
+; RV32I-NEXT:    sw s0, 8(sp)
+; RV32I-NEXT:    addi s0, sp, 16
+; RV32I-NEXT:    sw a1, 4(s0)
+; RV32I-NEXT:    sw a7, 28(s0)
+; RV32I-NEXT:    sw a6, 24(s0)
+; RV32I-NEXT:    sw a5, 20(s0)
+; RV32I-NEXT:    sw a4, 16(s0)
+; RV32I-NEXT:    sw a3, 12(s0)
+; RV32I-NEXT:    sw a2, 8(s0)
+; RV32I-NEXT:    addi a0, s0, 8
+; RV32I-NEXT:    sw a0, -12(s0)
+; RV32I-NEXT:    lw a0, 4(s0)
+; RV32I-NEXT:    lw s0, 8(sp)
+; RV32I-NEXT:    lw ra, 12(sp)
+; RV32I-NEXT:    addi sp, sp, 48
+; RV32I-NEXT:    ret
+  %va = alloca i8*, align 4
+  %1 = bitcast i8** %va to i8*
+  call void @llvm.va_start(i8* %1)
+  %2 = va_arg i8** %va, i32
+  call void @llvm.va_end(i8* %1)
+  ret i32 %2
+}
+
+; Ensure the adjustment when restoring the stack pointer using the frame
+; pointer is correct
+define i32 @va1_va_arg_alloca(i8* %fmt, ...) nounwind {
+; RV32I-LABEL: va1_va_arg_alloca:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -48
+; RV32I-NEXT:    sw ra, 12(sp)
+; RV32I-NEXT:    sw s0, 8(sp)
+; RV32I-NEXT:    sw s1, 4(sp)
+; RV32I-NEXT:    addi s0, sp, 16
+; RV32I-NEXT:    sw a1, 4(s0)
+; RV32I-NEXT:    sw a7, 28(s0)
+; RV32I-NEXT:    sw a6, 24(s0)
+; RV32I-NEXT:    sw a5, 20(s0)
+; RV32I-NEXT:    sw a4, 16(s0)
+; RV32I-NEXT:    sw a3, 12(s0)
+; RV32I-NEXT:    sw a2, 8(s0)
+; RV32I-NEXT:    addi a0, s0, 8
+; RV32I-NEXT:    sw a0, -16(s0)
+; RV32I-NEXT:    lw s1, 4(s0)
+; RV32I-NEXT:    addi a0, s1, 15
+; RV32I-NEXT:    andi a0, a0, -16
+; RV32I-NEXT:    sub a0, sp, a0
+; RV32I-NEXT:    mv sp, a0
+; RV32I-NEXT:    lui a1, %hi(notdead)
+; RV32I-NEXT:    addi a1, a1, %lo(notdead)
+; RV32I-NEXT:    jalr a1
+; RV32I-NEXT:    mv a0, s1
+; RV32I-NEXT:    addi sp, s0, -16
+; RV32I-NEXT:    lw s1, 4(sp)
+; RV32I-NEXT:    lw s0, 8(sp)
+; RV32I-NEXT:    lw ra, 12(sp)
+; RV32I-NEXT:    addi sp, sp, 48
+; RV32I-NEXT:    ret
+  %va = alloca i8*, align 4
+  %1 = bitcast i8** %va to i8*
+  call void @llvm.va_start(i8* %1)
+  %2 = va_arg i8** %va, i32
+  %3 = alloca i8, i32 %2
+  call void @notdead(i8* %3)
+  call void @llvm.va_end(i8* %1)
+  ret i32 %2
+}
+
+define void @va1_caller() nounwind {
+; RV32I-LABEL: va1_caller:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp)
+; RV32I-NEXT:    sw s0, 8(sp)
+; RV32I-NEXT:    addi s0, sp, 16
+; RV32I-NEXT:    lui a0, 261888
+; RV32I-NEXT:    mv a3, a0
+; RV32I-NEXT:    lui a0, %hi(va1)
+; RV32I-NEXT:    addi a0, a0, %lo(va1)
+; RV32I-NEXT:    addi a4, zero, 2
+; RV32I-NEXT:    mv a2, zero
+; RV32I-NEXT:    jalr a0
+; RV32I-NEXT:    lw s0, 8(sp)
+; RV32I-NEXT:    lw ra, 12(sp)
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+; Pass a double, as a float would be promoted by a C/C++ frontend
+  %1 = call i32 (i8*, ...) @va1(i8* undef, double 1.0, i32 2)
+  ret void
+}
+
+; Ensure that 2x xlen size+alignment varargs are accessed via an "aligned"
+; register pair (where the first register is even-numbered).
+
+define double @va2(i8 *%fmt, ...) nounwind {
+; RV32I-LABEL: va2:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -48
+; RV32I-NEXT:    sw ra, 12(sp)
+; RV32I-NEXT:    sw s0, 8(sp)
+; RV32I-NEXT:    addi s0, sp, 16
+; RV32I-NEXT:    sw a7, 28(s0)
+; RV32I-NEXT:    sw a6, 24(s0)
+; RV32I-NEXT:    sw a5, 20(s0)
+; RV32I-NEXT:    sw a4, 16(s0)
+; RV32I-NEXT:    sw a3, 12(s0)
+; RV32I-NEXT:    sw a2, 8(s0)
+; RV32I-NEXT:    sw a1, 4(s0)
+; RV32I-NEXT:    addi a0, s0, 19
+; RV32I-NEXT:    sw a0, -12(s0)
+; RV32I-NEXT:    addi a0, s0, 11
+; RV32I-NEXT:    andi a1, a0, -8
+; RV32I-NEXT:    lw a0, 0(a1)
+; RV32I-NEXT:    ori a1, a1, 4
+; RV32I-NEXT:    lw a1, 0(a1)
+; RV32I-NEXT:    lw s0, 8(sp)
+; RV32I-NEXT:    lw ra, 12(sp)
+; RV32I-NEXT:    addi sp, sp, 48
+; RV32I-NEXT:    ret
+  %va = alloca i8*, align 4
+  %1 = bitcast i8** %va to i8*
+  call void @llvm.va_start(i8* %1)
+  %2 = bitcast i8** %va to i32*
+  %argp.cur = load i32, i32* %2, align 4
+  %3 = add i32 %argp.cur, 7
+  %4 = and i32 %3, -8
+  %argp.cur.aligned = inttoptr i32 %3 to i8*
+  %argp.next = getelementptr inbounds i8, i8* %argp.cur.aligned, i32 8
+  store i8* %argp.next, i8** %va, align 4
+  %5 = inttoptr i32 %4 to double*
+  %6 = load double, double* %5, align 8
+  call void @llvm.va_end(i8* %1)
+  ret double %6
+}
+
+define double @va2_va_arg(i8 *%fmt, ...) nounwind {
+; RV32I-LABEL: va2_va_arg:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -48
+; RV32I-NEXT:    sw ra, 12(sp)
+; RV32I-NEXT:    sw s0, 8(sp)
+; RV32I-NEXT:    addi s0, sp, 16
+; RV32I-NEXT:    sw a7, 28(s0)
+; RV32I-NEXT:    sw a6, 24(s0)
+; RV32I-NEXT:    sw a5, 20(s0)
+; RV32I-NEXT:    sw a4, 16(s0)
+; RV32I-NEXT:    sw a3, 12(s0)
+; RV32I-NEXT:    sw a2, 8(s0)
+; RV32I-NEXT:    sw a1, 4(s0)
+; RV32I-NEXT:    addi a0, s0, 11
+; RV32I-NEXT:    andi a0, a0, -8
+; RV32I-NEXT:    ori a1, a0, 4
+; RV32I-NEXT:    sw a1, -12(s0)
+; RV32I-NEXT:    lw a0, 0(a0)
+; RV32I-NEXT:    addi a2, a1, 4
+; RV32I-NEXT:    sw a2, -12(s0)
+; RV32I-NEXT:    lw a1, 0(a1)
+; RV32I-NEXT:    lw s0, 8(sp)
+; RV32I-NEXT:    lw ra, 12(sp)
+; RV32I-NEXT:    addi sp, sp, 48
+; RV32I-NEXT:    ret
+  %va = alloca i8*, align 4
+  %1 = bitcast i8** %va to i8*
+  call void @llvm.va_start(i8* %1)
+  %2 = va_arg i8** %va, double
+  call void @llvm.va_end(i8* %1)
+  ret double %2
+}
+
+define void @va2_caller() nounwind {
+; RV32I-LABEL: va2_caller:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp)
+; RV32I-NEXT:    sw s0, 8(sp)
+; RV32I-NEXT:    addi s0, sp, 16
+; RV32I-NEXT:    lui a0, 261888
+; RV32I-NEXT:    mv a3, a0
+; RV32I-NEXT:    lui a0, %hi(va2)
+; RV32I-NEXT:    addi a0, a0, %lo(va2)
+; RV32I-NEXT:    mv a2, zero
+; RV32I-NEXT:    jalr a0
+; RV32I-NEXT:    lw s0, 8(sp)
+; RV32I-NEXT:    lw ra, 12(sp)
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+ %1 = call double (i8*, ...) @va2(i8* undef, double 1.000000e+00)
+ ret void
+}
+
+; Ensure a named double argument is passed in a1 and a2, while the vararg
+; double is passed in a4 and a5 (rather than a3 and a4)
+
+define double @va3(i32 %a, double %b, ...) nounwind {
+; RV32I-LABEL: va3:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -48
+; RV32I-NEXT:    sw ra, 20(sp)
+; RV32I-NEXT:    sw s0, 16(sp)
+; RV32I-NEXT:    addi s0, sp, 24
+; RV32I-NEXT:    sw a7, 20(s0)
+; RV32I-NEXT:    sw a6, 16(s0)
+; RV32I-NEXT:    sw a5, 12(s0)
+; RV32I-NEXT:    sw a4, 8(s0)
+; RV32I-NEXT:    sw a3, 4(s0)
+; RV32I-NEXT:    addi a0, s0, 19
+; RV32I-NEXT:    sw a0, -12(s0)
+; RV32I-NEXT:    lui a0, %hi(__adddf3)
+; RV32I-NEXT:    addi a5, a0, %lo(__adddf3)
+; RV32I-NEXT:    addi a0, s0, 11
+; RV32I-NEXT:    andi a0, a0, -8
+; RV32I-NEXT:    lw a4, 0(a0)
+; RV32I-NEXT:    ori a0, a0, 4
+; RV32I-NEXT:    lw a3, 0(a0)
+; RV32I-NEXT:    mv a0, a1
+; RV32I-NEXT:    mv a1, a2
+; RV32I-NEXT:    mv a2, a4
+; RV32I-NEXT:    jalr a5
+; RV32I-NEXT:    lw s0, 16(sp)
+; RV32I-NEXT:    lw ra, 20(sp)
+; RV32I-NEXT:    addi sp, sp, 48
+; RV32I-NEXT:    ret
+  %va = alloca i8*, align 4
+  %1 = bitcast i8** %va to i8*
+  call void @llvm.va_start(i8* %1)
+  %2 = bitcast i8** %va to i32*
+  %argp.cur = load i32, i32* %2, align 4
+  %3 = add i32 %argp.cur, 7
+  %4 = and i32 %3, -8
+  %argp.cur.aligned = inttoptr i32 %3 to i8*
+  %argp.next = getelementptr inbounds i8, i8* %argp.cur.aligned, i32 8
+  store i8* %argp.next, i8** %va, align 4
+  %5 = inttoptr i32 %4 to double*
+  %6 = load double, double* %5, align 8
+  call void @llvm.va_end(i8* %1)
+  %7 = fadd double %b, %6
+  ret double %7
+}
+
+define double @va3_va_arg(i32 %a, double %b, ...) nounwind {
+; RV32I-LABEL: va3_va_arg:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -48
+; RV32I-NEXT:    sw ra, 20(sp)
+; RV32I-NEXT:    sw s0, 16(sp)
+; RV32I-NEXT:    addi s0, sp, 24
+; RV32I-NEXT:    sw a7, 20(s0)
+; RV32I-NEXT:    sw a6, 16(s0)
+; RV32I-NEXT:    sw a5, 12(s0)
+; RV32I-NEXT:    sw a4, 8(s0)
+; RV32I-NEXT:    sw a3, 4(s0)
+; RV32I-NEXT:    addi a0, s0, 11
+; RV32I-NEXT:    andi a0, a0, -8
+; RV32I-NEXT:    ori a3, a0, 4
+; RV32I-NEXT:    sw a3, -12(s0)
+; RV32I-NEXT:    lw a4, 0(a0)
+; RV32I-NEXT:    addi a0, a3, 4
+; RV32I-NEXT:    sw a0, -12(s0)
+; RV32I-NEXT:    lui a0, %hi(__adddf3)
+; RV32I-NEXT:    addi a5, a0, %lo(__adddf3)
+; RV32I-NEXT:    lw a3, 0(a3)
+; RV32I-NEXT:    mv a0, a1
+; RV32I-NEXT:    mv a1, a2
+; RV32I-NEXT:    mv a2, a4
+; RV32I-NEXT:    jalr a5
+; RV32I-NEXT:    lw s0, 16(sp)
+; RV32I-NEXT:    lw ra, 20(sp)
+; RV32I-NEXT:    addi sp, sp, 48
+; RV32I-NEXT:    ret
+  %va = alloca i8*, align 4
+  %1 = bitcast i8** %va to i8*
+  call void @llvm.va_start(i8* %1)
+  %2 = va_arg i8** %va, double
+  call void @llvm.va_end(i8* %1)
+  %3 = fadd double %b, %2
+  ret double %3
+}
+
+define void @va3_caller() nounwind {
+; RV32I-LABEL: va3_caller:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp)
+; RV32I-NEXT:    sw s0, 8(sp)
+; RV32I-NEXT:    addi s0, sp, 16
+; RV32I-NEXT:    lui a0, 261888
+; RV32I-NEXT:    mv a2, a0
+; RV32I-NEXT:    lui a0, 262144
+; RV32I-NEXT:    mv a5, a0
+; RV32I-NEXT:    lui a0, %hi(va3)
+; RV32I-NEXT:    addi a3, a0, %lo(va3)
+; RV32I-NEXT:    addi a0, zero, 2
+; RV32I-NEXT:    mv a1, zero
+; RV32I-NEXT:    mv a4, zero
+; RV32I-NEXT:    jalr a3
+; RV32I-NEXT:    lw s0, 8(sp)
+; RV32I-NEXT:    lw ra, 12(sp)
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+ %1 = call double (i32, double, ...) @va3(i32 2, double 1.000000e+00, double 2.000000e+00)
+ ret void
+}
+
+declare void @llvm.va_copy(i8*, i8*)
+
+define i32 @va4_va_copy(i32 %argno, ...) nounwind {
+; RV32I-LABEL: va4_va_copy:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -64
+; RV32I-NEXT:    sw ra, 28(sp)
+; RV32I-NEXT:    sw s0, 24(sp)
+; RV32I-NEXT:    sw s1, 20(sp)
+; RV32I-NEXT:    addi s0, sp, 32
+; RV32I-NEXT:    sw a1, 4(s0)
+; RV32I-NEXT:    sw a7, 28(s0)
+; RV32I-NEXT:    sw a6, 24(s0)
+; RV32I-NEXT:    sw a5, 20(s0)
+; RV32I-NEXT:    sw a4, 16(s0)
+; RV32I-NEXT:    sw a3, 12(s0)
+; RV32I-NEXT:    sw a2, 8(s0)
+; RV32I-NEXT:    addi a0, s0, 8
+; RV32I-NEXT:    sw a0, -16(s0)
+; RV32I-NEXT:    sw a0, -20(s0)
+; RV32I-NEXT:    lw s1, 4(s0)
+; RV32I-NEXT:    lui a1, %hi(notdead)
+; RV32I-NEXT:    addi a1, a1, %lo(notdead)
+; RV32I-NEXT:    jalr a1
+; RV32I-NEXT:    lw a0, -16(s0)
+; RV32I-NEXT:    addi a0, a0, 3
+; RV32I-NEXT:    andi a0, a0, -4
+; RV32I-NEXT:    addi a1, a0, 4
+; RV32I-NEXT:    sw a1, -16(s0)
+; RV32I-NEXT:    lw a1, 0(a0)
+; RV32I-NEXT:    addi a0, a0, 7
+; RV32I-NEXT:    andi a0, a0, -4
+; RV32I-NEXT:    addi a2, a0, 4
+; RV32I-NEXT:    sw a2, -16(s0)
+; RV32I-NEXT:    lw a2, 0(a0)
+; RV32I-NEXT:    addi a0, a0, 7
+; RV32I-NEXT:    andi a0, a0, -4
+; RV32I-NEXT:    addi a3, a0, 4
+; RV32I-NEXT:    sw a3, -16(s0)
+; RV32I-NEXT:    add a1, a1, s1
+; RV32I-NEXT:    add a1, a1, a2
+; RV32I-NEXT:    lw a0, 0(a0)
+; RV32I-NEXT:    add a0, a1, a0
+; RV32I-NEXT:    lw s1, 20(sp)
+; RV32I-NEXT:    lw s0, 24(sp)
+; RV32I-NEXT:    lw ra, 28(sp)
+; RV32I-NEXT:    addi sp, sp, 64
+; RV32I-NEXT:    ret
+  %vargs = alloca i8*, align 4
+  %wargs = alloca i8*, align 4
+  %1 = bitcast i8** %vargs to i8*
+  %2 = bitcast i8** %wargs to i8*
+  call void @llvm.va_start(i8* %1)
+  %3 = va_arg i8** %vargs, i32
+  call void @llvm.va_copy(i8* %2, i8* %1)
+  %4 = load i8*, i8** %wargs, align 4
+  call void @notdead(i8* %4)
+  %5 = va_arg i8** %vargs, i32
+  %6 = va_arg i8** %vargs, i32
+  %7 = va_arg i8** %vargs, i32
+  call void @llvm.va_end(i8* %1)
+  call void @llvm.va_end(i8* %2)
+  %add1 = add i32 %5, %3
+  %add2 = add i32 %add1, %6
+  %add3 = add i32 %add2, %7
+  ret i32 %add3
+}
+
+; Check 2x*xlen values are aligned appropriately when passed on the stack in a vararg call
+
+define i32 @va5_aligned_stack_callee(i32 %a, ...) nounwind {
+; RV32I-LABEL: va5_aligned_stack_callee:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -48
+; RV32I-NEXT:    sw ra, 12(sp)
+; RV32I-NEXT:    sw s0, 8(sp)
+; RV32I-NEXT:    addi s0, sp, 16
+; RV32I-NEXT:    sw a7, 28(s0)
+; RV32I-NEXT:    sw a6, 24(s0)
+; RV32I-NEXT:    sw a5, 20(s0)
+; RV32I-NEXT:    sw a4, 16(s0)
+; RV32I-NEXT:    sw a3, 12(s0)
+; RV32I-NEXT:    sw a2, 8(s0)
+; RV32I-NEXT:    sw a1, 4(s0)
+; RV32I-NEXT:    addi a0, zero, 1
+; RV32I-NEXT:    lw s0, 8(sp)
+; RV32I-NEXT:    lw ra, 12(sp)
+; RV32I-NEXT:    addi sp, sp, 48
+; RV32I-NEXT:    ret
+  ret i32 1
+}
+
+define void @va5_aligned_stack_caller() nounwind {
+; The double should be 8-byte aligned on the stack, but the two-element array
+; should only be 4-byte aligned
+; RV32I-LABEL: va5_aligned_stack_caller:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -64
+; RV32I-NEXT:    sw ra, 60(sp)
+; RV32I-NEXT:    sw s0, 56(sp)
+; RV32I-NEXT:    addi s0, sp, 64
+; RV32I-NEXT:    addi a0, zero, 17
+; RV32I-NEXT:    sw a0, 24(sp)
+; RV32I-NEXT:    addi a0, zero, 16
+; RV32I-NEXT:    sw a0, 20(sp)
+; RV32I-NEXT:    addi a0, zero, 15
+; RV32I-NEXT:    sw a0, 16(sp)
+; RV32I-NEXT:    lui a0, 262236
+; RV32I-NEXT:    addi a0, a0, 655
+; RV32I-NEXT:    sw a0, 12(sp)
+; RV32I-NEXT:    lui a0, 377487
+; RV32I-NEXT:    addi a0, a0, 1475
+; RV32I-NEXT:    sw a0, 8(sp)
+; RV32I-NEXT:    addi a0, zero, 14
+; RV32I-NEXT:    sw a0, 0(sp)
+; RV32I-NEXT:    lui a0, 262153
+; RV32I-NEXT:    addi a0, a0, 491
+; RV32I-NEXT:    sw a0, -20(s0)
+; RV32I-NEXT:    lui a0, 545260
+; RV32I-NEXT:    addi a0, a0, -1967
+; RV32I-NEXT:    sw a0, -24(s0)
+; RV32I-NEXT:    lui a0, 964690
+; RV32I-NEXT:    addi a0, a0, -328
+; RV32I-NEXT:    sw a0, -28(s0)
+; RV32I-NEXT:    lui a0, 335544
+; RV32I-NEXT:    addi a0, a0, 1311
+; RV32I-NEXT:    sw a0, -32(s0)
+; RV32I-NEXT:    lui a0, 688509
+; RV32I-NEXT:    addi a6, a0, -2048
+; RV32I-NEXT:    lui a0, %hi(va5_aligned_stack_callee)
+; RV32I-NEXT:    addi a5, a0, %lo(va5_aligned_stack_callee)
+; RV32I-NEXT:    addi a0, zero, 1
+; RV32I-NEXT:    addi a1, zero, 11
+; RV32I-NEXT:    addi a2, s0, -32
+; RV32I-NEXT:    addi a3, zero, 12
+; RV32I-NEXT:    addi a4, zero, 13
+; RV32I-NEXT:    addi a7, zero, 4
+; RV32I-NEXT:    jalr a5
+; RV32I-NEXT:    lw s0, 56(sp)
+; RV32I-NEXT:    lw ra, 60(sp)
+; RV32I-NEXT:    addi sp, sp, 64
+; RV32I-NEXT:    ret
+  %1 = call i32 (i32, ...) @va5_aligned_stack_callee(i32 1, i32 11,
+    fp128 0xLEB851EB851EB851F400091EB851EB851, i32 12, i32 13, i64 20000000000,
+    i32 14, double 2.720000e+00, i32 15, [2 x i32] [i32 16, i32 17])
+  ret void
+}
+
+; A function with no fixed arguments is not valid C, but can be
+; specified in LLVM IR. We must ensure the vararg save area is
+; still set up correctly.
+
+define i32 @va6_no_fixed_args(...) nounwind {
+; RV32I-LABEL: va6_no_fixed_args:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -48
+; RV32I-NEXT:    sw ra, 12(sp)
+; RV32I-NEXT:    sw s0, 8(sp)
+; RV32I-NEXT:    addi s0, sp, 16
+; RV32I-NEXT:    sw a0, 0(s0)
+; RV32I-NEXT:    sw a7, 28(s0)
+; RV32I-NEXT:    sw a6, 24(s0)
+; RV32I-NEXT:    sw a5, 20(s0)
+; RV32I-NEXT:    sw a4, 16(s0)
+; RV32I-NEXT:    sw a3, 12(s0)
+; RV32I-NEXT:    sw a2, 8(s0)
+; RV32I-NEXT:    sw a1, 4(s0)
+; RV32I-NEXT:    addi a0, s0, 4
+; RV32I-NEXT:    sw a0, -12(s0)
+; RV32I-NEXT:    lw a0, 0(s0)
+; RV32I-NEXT:    lw s0, 8(sp)
+; RV32I-NEXT:    lw ra, 12(sp)
+; RV32I-NEXT:    addi sp, sp, 48
+; RV32I-NEXT:    ret
+  %va = alloca i8*, align 4
+  %1 = bitcast i8** %va to i8*
+  call void @llvm.va_start(i8* %1)
+  %2 = va_arg i8** %va, i32
+  call void @llvm.va_end(i8* %1)
+  ret i32 %2
+}




More information about the llvm-commits mailing list