Index: lib/Target/NDS32/NDS32ISelLowering.h =================================================================== --- lib/Target/NDS32/NDS32ISelLowering.h +++ lib/Target/NDS32/NDS32ISelLowering.h @@ -73,6 +73,8 @@ SDValue LowerDIVREM(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const; + /// getTargetNodeName - This method returns the name of a target specific /// DAG node. const char *getTargetNodeName(unsigned Opcode) const override; @@ -136,6 +138,13 @@ SDValue getTargetNode(BlockAddressSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const; + /// RestoreVarArgRegs - Restore variable function arguments passed in + /// registers to the stack. Also create a stack frame object for the + /// first variable argument. + void RestoreVarArgRegs(std::vector &OutChains, SDValue Chain, + const SDLoc &DL, SelectionDAG &DAG, + CCState &State) const; + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, const SmallVectorImpl &Ins, Index: lib/Target/NDS32/NDS32ISelLowering.cpp =================================================================== --- lib/Target/NDS32/NDS32ISelLowering.cpp +++ lib/Target/NDS32/NDS32ISelLowering.cpp @@ -148,6 +148,7 @@ case ISD::ExternalSymbol: return LowerExternalSymbol(Op, DAG); case ISD::UDIVREM: return LowerDIVREM(Op, DAG); case ISD::SDIVREM: return LowerDIVREM(Op, DAG); + case ISD::VASTART: return LowerVASTART(Op, DAG); default: llvm_unreachable("unimplemented operand"); } @@ -208,6 +209,22 @@ return DAG.getTargetBlockAddress(N->getBlockAddress(), Ty, 0, Flag); } +SDValue NDS32TargetLowering::LowerVASTART(SDValue Op, + SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + NDS32MachineFunctionInfo *FuncInfo = MF.getInfo(); + auto PtrVT = getPointerTy(DAG.getDataLayout()); + + // Frame index of first vararg argument + SDValue FrameIndex = + DAG.getFrameIndex(FuncInfo->getVarArgsFrameIndex(), PtrVT); + const Value *SV = cast(Op.getOperand(2))->getValue(); + + // Create a store of the frame index to the location operand + return DAG.getStore(Op.getOperand(0), SDLoc(Op), FrameIndex, Op.getOperand(1), + MachinePointerInfo(SV)); +} + //===----------------------------------------------------------------------===// // NDS32 Addressing Mode Support @@ -335,6 +352,67 @@ NDS32::R3, NDS32::R4, NDS32::R5 }; +// RestoreVarArgRegs - Store VarArg register to the stack +void NDS32TargetLowering::RestoreVarArgRegs(std::vector &OutChains, + SDValue Chain, const SDLoc &DL, + SelectionDAG &DAG, + CCState &State) const { + ArrayRef ArgRegs = makeArrayRef(NDS32ArgRegs); + unsigned Idx = State.getFirstUnallocated(ArgRegs); + unsigned RegSizeInBytes = 4; + MVT RegTy = MVT::getIntegerVT(RegSizeInBytes * 8); + const TargetRegisterClass *RC = getRegClassFor(RegTy); + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + NDS32MachineFunctionInfo *NDS32FI = MF.getInfo(); + // VaArgOffset is the va_start offset from stack pointer + int VaArgOffset = 0; + + // All ArgRegs use to pass arguments + if (ArgRegs.size() == Idx) { + VaArgOffset = State.getNextStackOffset(); + // + // ---------------- <-- va_start + // | outgoing args| + // ---------------- <-- sp + // | R0 ~ R5 | + // ---------------- + } + else { + VaArgOffset = -(int)(RegSizeInBytes * (ArgRegs.size() - Idx)); + // E.g Idx = R3 which means f (a, b, c, ...) + // + // ---------------- <-- sp + // | R3 ~ R5 | + // ---------------- <-- va_start + // | R0 ~ R2 | + // ---------------- + } + + // Record the frame index of the first variable argument + // which is a value necessary to VASTART. + int VA_FI = MFI.CreateFixedObject(RegSizeInBytes, VaArgOffset, true); + NDS32FI->setVarArgsFrameIndex(VA_FI); + + // Copy the integer registers that have not been used for argument passing + // to the argument register save area. The save area is allocated in the + // callee's stack frame. + // For above case Idx = R3, generate store to push R3~R5 to callee stack + // frame. + for (unsigned I = Idx; I < ArgRegs.size(); + ++I, VaArgOffset += RegSizeInBytes) { + unsigned Reg = addLiveIn(MF, ArgRegs[I], RC); + SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegTy); + int FI = MFI.CreateFixedObject(RegSizeInBytes, VaArgOffset, true); + SDValue PtrOff = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + SDValue Store = + DAG.getStore(Chain, DL, ArgValue, PtrOff, MachinePointerInfo()); + cast(Store.getNode())->getMemOperand()->setValue( + (Value *)nullptr); + OutChains.push_back(Store); + } +} + // LowerFormalArguments - Specify how callee get arguments SDValue NDS32TargetLowering::LowerFormalArguments( SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, @@ -403,6 +481,10 @@ } } + // Push VarArg Registers to the stack + if (IsVarArg) + RestoreVarArgRegs(OutChains, Chain, DL, DAG, CCInfo); + // 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()) { Index: test/CodeGen/NDS32/va-arg.ll =================================================================== --- /dev/null +++ test/CodeGen/NDS32/va-arg.ll @@ -0,0 +1,85 @@ +; RUN: llc < %s | FileCheck %s +target datalayout = "e-m:e-p:32:32-i64:64-a:0:32-n32-S64" +target triple = "nds32le---elf" + +; Function Attrs: nounwind +define i32 @f(i32 %p0, i32 %p1, i32 %p2, i32 %p3, i32 %p4, i32 %p5, i32 %p6, i32 %p7, i32 %p8, ...) local_unnamed_addr #0 { +entry: + %select = alloca i8*, align 4 + %0 = bitcast i8** %select to i8* + call void @llvm.lifetime.start(i64 4, i8* nonnull %0) #2 + call void @llvm.va_start(i8* nonnull %0) +; CHECK: addi $r0, $sp, 20 +; CHECK: swi $r0, [$sp + (0)] + %argp.cur = load i8*, i8** %select, align 4 + %argp.next = getelementptr inbounds i8, i8* %argp.cur, i32 4 + store i8* %argp.next, i8** %select, align 4 + %1 = bitcast i8* %argp.cur to i32* + %2 = load i32, i32* %1, align 4 + %cmp = icmp eq i32 %2, 10 + br i1 %cmp, label %if.end, label %if.then + +if.then: ; preds = %entry + call void @abort() #5 + unreachable + +if.end: ; preds = %entry + %argp.next3 = getelementptr inbounds i8, i8* %argp.cur, i32 8 + store i8* %argp.next3, i8** %select, align 4 + %3 = bitcast i8* %argp.next to i32* + %4 = load i32, i32* %3, align 4 + %cmp4 = icmp eq i32 %4, 11 + br i1 %cmp4, label %if.end6, label %if.then5 + +if.then5: ; preds = %if.end + call void @abort() #5 + unreachable + +if.end6: ; preds = %if.end + %argp.next8 = getelementptr inbounds i8, i8* %argp.cur, i32 12 + store i8* %argp.next8, i8** %select, align 4 + %5 = bitcast i8* %argp.next3 to i32* + %6 = load i32, i32* %5, align 4 + %cmp9 = icmp eq i32 %6, 0 + br i1 %cmp9, label %if.end11, label %if.then10 + +if.then10: ; preds = %if.end6 + call void @abort() #5 + unreachable + +if.end11: ; preds = %if.end6 + call void @llvm.va_end(i8* nonnull %0) + call void @llvm.lifetime.end(i64 4, i8* nonnull %0) #2 + ret i32 undef +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.start(i64, i8* nocapture) #1 + +; Function Attrs: nounwind +declare void @llvm.va_start(i8*) #2 + +; Function Attrs: noreturn +declare void @abort() local_unnamed_addr #3 + +; Function Attrs: nounwind +declare void @llvm.va_end(i8*) #2 + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.end(i64, i8* nocapture) #1 + +; Function Attrs: noreturn nounwind +define i32 @main() local_unnamed_addr #4 { +entry: +; CHECK: mov55 $r0, $sp +; CHECK: movi55 $r1, 11 +; CHECK: swi333 $r1, [$r0 + (16)] +; CHECK: movi55 $r1, 10 +; CHECK: swi333 $r1, [$r0 + (12)] + %call = tail call i32 (i32, i32, i32, i32, i32, i32, i32, i32, i32, ...) @f(i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 10, i32 11, i32 0) + tail call void @exit(i32 0) #5 + unreachable +} + +; Function Attrs: noreturn +declare void @exit(i32) local_unnamed_addr #3