llvm.org GIT mirror llvm / 181a9c9
[RISCV] Add custom CC_RISCV calling convention and improved call support The TableGen-based calling convention definitions are inflexible, while writing a function to implement the calling convention is very straight-forward, and allows difficult cases to be handled more easily. With this patch adds support for: * Passing large scalars according to the RV32I calling convention * Byval arguments * Passing values on the stack when the argument registers are exhausted The custom CC_RISCV calling convention is also used for returns. This patch also documents the ABI lowering that a language frontend is expected to perform. I would like to work to simplify these requirements over time, but this will require further discussion within the LLVM community. We add PendingArgFlags CCState, as a companion to PendingLocs. The PendingLocs vector is used by a number of backends to handle arguments that are split during legalisation. However CCValAssign doesn't keep track of the original argument alignment. Therefore, add a PendingArgFlags vector which can be used to keep track of the ISD::ArgFlagsTy for every value added to PendingLocs. Differential Revision: https://reviews.llvm.org/D39898 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@320359 91177308-0d34-0410-b5e6-96231b3b80d8 Alex Bradbury 2 years ago
10 changed file(s) with 1872 addition(s) and 50 deletion(s). Raw diff Collapse all Expand all
200200 unsigned MaxStackArgAlign;
201201 SmallVector UsedRegs;
202202 SmallVector PendingLocs;
203 SmallVector PendingArgFlags;
203204
204205 // ByValInfo and SmallVector ByValRegs:
205206 //
507508 return PendingLocs;
508509 }
509510
511 // Get a list of argflags for pending assignments.
512 SmallVectorImpl &getPendingArgFlags() {
513 return PendingArgFlags;
514 }
515
510516 /// Compute the remaining unused register parameters that would be used for
511517 /// the given value type. This is useful when varargs are passed in the
512518 /// registers that normal prototyped parameters would be passed in, or for
55 tablegen(LLVM RISCVGenMCPseudoLowering.inc -gen-pseudo-lowering)
66 tablegen(LLVM RISCVGenAsmMatcher.inc -gen-asm-matcher)
77 tablegen(LLVM RISCVGenAsmWriter.inc -gen-asm-writer)
8 tablegen(LLVM RISCVGenCallingConv.inc -gen-callingconv)
98 tablegen(LLVM RISCVGenDAGISel.inc -gen-dag-isel)
109 tablegen(LLVM RISCVGenSubtargetInfo.inc -gen-subtarget)
1110 tablegen(LLVM RISCVGenDisassemblerTables.inc -gen-disassembler)
1010 //
1111 //===----------------------------------------------------------------------===//
1212
13 // RISCV 32-bit C return-value convention.
14 def RetCC_RISCV32 : CallingConv<[CCIfType<[i32], CCAssignToReg<[X10, X11]>>]>;
15
16 // RISCV 32-bit C Calling convention.
17 def CC_RISCV32 : CallingConv<[
18 // Promote i8/i16 args to i32
19 CCIfType<[ i8, i16 ], CCPromoteToType>,
20
21 // All arguments get passed in integer registers if there is space.
22 CCIfType<[i32], CCAssignToReg<[ X10, X11, X12, X13, X14, X15, X16, X17]>>,
23
24 // Could be assigned to the stack in 8-byte aligned units, but unsupported
25 CCAssignToStack<8, 8>
26 ]>;
13 // The RISC-V calling convention is handled with custom code in
14 // RISCVISelLowering.cpp (CC_RISCV).
2715
2816 def CSR : CalleeSavedRegs<(add X1, X3, X4, X8, X9, (sequence "X%u", 18, 27))>;
2917
327327 }
328328
329329 // Calling Convention Implementation.
330 #include "RISCVGenCallingConv.inc"
330 // The expectations for frontend ABI lowering vary from target to target.
331 // Ideally, an LLVM frontend would be able to avoid worrying about many ABI
332 // details, but this is a longer term goal. For now, we simply try to keep the
333 // role of the frontend as simple and well-defined as possible. The rules can
334 // be summarised as:
335 // * Never split up large scalar arguments. We handle them here.
336 // * If a hardfloat calling convention is being used, and the struct may be
337 // passed in a pair of registers (fp+fp, int+fp), and both registers are
338 // available, then pass as two separate arguments. If either the GPRs or FPRs
339 // are exhausted, then pass according to the rule below.
340 // * If a struct could never be passed in registers or directly in a stack
341 // slot (as it is larger than 2*XLEN and the floating point rules don't
342 // apply), then pass it using a pointer with the byval attribute.
343 // * If a struct is less than 2*XLEN, then coerce to either a two-element
344 // word-sized array or a 2*XLEN scalar (depending on alignment).
345 // * The frontend can determine whether a struct is returned by reference or
346 // not based on its size and fields. If it will be returned by reference, the
347 // frontend must modify the prototype so a pointer with the sret annotation is
348 // passed as the first argument. This is not necessary for large scalar
349 // returns.
350 // * Struct return values and varargs should be coerced to structs containing
351 // register-size fields in the same situations they would be for fixed
352 // arguments.
353
354 static const MCPhysReg ArgGPRs[] = {
355 RISCV::X10, RISCV::X11, RISCV::X12, RISCV::X13,
356 RISCV::X14, RISCV::X15, RISCV::X16, RISCV::X17
357 };
358
359 // Pass a 2*XLEN argument that has been split into two XLEN values through
360 // registers or the stack as necessary.
361 static bool CC_RISCVAssign2XLen(unsigned XLen, CCState &State, CCValAssign VA1,
362 ISD::ArgFlagsTy ArgFlags1, unsigned ValNo2,
363 MVT ValVT2, MVT LocVT2,
364 ISD::ArgFlagsTy ArgFlags2) {
365 unsigned XLenInBytes = XLen / 8;
366 if (unsigned Reg = State.AllocateReg(ArgGPRs)) {
367 // At least one half can be passed via register.
368 State.addLoc(CCValAssign::getReg(VA1.getValNo(), VA1.getValVT(), Reg,
369 VA1.getLocVT(), CCValAssign::Full));
370 } else {
371 // Both halves must be passed on the stack, with proper alignment.
372 unsigned StackAlign = std::max(XLenInBytes, ArgFlags1.getOrigAlign());
373 State.addLoc(
374 CCValAssign::getMem(VA1.getValNo(), VA1.getValVT(),
375 State.AllocateStack(XLenInBytes, StackAlign),
376 VA1.getLocVT(), CCValAssign::Full));
377 State.addLoc(CCValAssign::getMem(
378 ValNo2, ValVT2, State.AllocateStack(XLenInBytes, XLenInBytes), LocVT2,
379 CCValAssign::Full));
380 return false;
381 }
382
383 if (unsigned Reg = State.AllocateReg(ArgGPRs)) {
384 // The second half can also be passed via register.
385 State.addLoc(
386 CCValAssign::getReg(ValNo2, ValVT2, Reg, LocVT2, CCValAssign::Full));
387 } else {
388 // The second half is passed via the stack, without additional alignment.
389 State.addLoc(CCValAssign::getMem(
390 ValNo2, ValVT2, State.AllocateStack(XLenInBytes, XLenInBytes), LocVT2,
391 CCValAssign::Full));
392 }
393
394 return false;
395 }
396
397 // Implements the RISC-V calling convention. Returns true upon failure.
398 static bool CC_RISCV(const DataLayout &DL, unsigned ValNo, MVT ValVT, MVT LocVT,
399 CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags,
400 CCState &State, bool IsFixed, bool IsRet) {
401 unsigned XLen = DL.getLargestLegalIntTypeSizeInBits();
402 assert(XLen == 32 || XLen == 64);
403 MVT XLenVT = XLen == 32 ? MVT::i32 : MVT::i64;
404 assert(ValVT == XLenVT && "Unexpected ValVT");
405 assert(LocVT == XLenVT && "Unexpected LocVT");
406 assert(IsFixed && "Vararg support not yet implemented");
407
408 // Any return value split in to more than two values can't be returned
409 // directly.
410 if (IsRet && ValNo > 1)
411 return true;
412
413 SmallVectorImpl &PendingLocs = State.getPendingLocs();
414 SmallVectorImpl &PendingArgFlags =
415 State.getPendingArgFlags();
416
417 assert(PendingLocs.size() == PendingArgFlags.size() &&
418 "PendingLocs and PendingArgFlags out of sync");
419
420 // Split arguments might be passed indirectly, so keep track of the pending
421 // values.
422 if (ArgFlags.isSplit() || !PendingLocs.empty()) {
423 LocVT = XLenVT;
424 LocInfo = CCValAssign::Indirect;
425 PendingLocs.push_back(
426 CCValAssign::getPending(ValNo, ValVT, LocVT, LocInfo));
427 PendingArgFlags.push_back(ArgFlags);
428 if (!ArgFlags.isSplitEnd()) {
429 return false;
430 }
431 }
432
433 // If the split argument only had two elements, it should be passed directly
434 // in registers or on the stack.
435 if (ArgFlags.isSplitEnd() && PendingLocs.size() <= 2) {
436 assert(PendingLocs.size() == 2 && "Unexpected PendingLocs.size()");
437 // Apply the normal calling convention rules to the first half of the
438 // split argument.
439 CCValAssign VA = PendingLocs[0];
440 ISD::ArgFlagsTy AF = PendingArgFlags[0];
441 PendingLocs.clear();
442 PendingArgFlags.clear();
443 return CC_RISCVAssign2XLen(XLen, State, VA, AF, ValNo, ValVT, LocVT,
444 ArgFlags);
445 }
446
447 // Allocate to a register if possible, or else a stack slot.
448 unsigned Reg = State.AllocateReg(ArgGPRs);
449 unsigned StackOffset = Reg ? 0 : State.AllocateStack(XLen / 8, XLen / 8);
450
451 // If we reach this point and PendingLocs is non-empty, we must be at the
452 // end of a split argument that must be passed indirectly.
453 if (!PendingLocs.empty()) {
454 assert(ArgFlags.isSplitEnd() && "Expected ArgFlags.isSplitEnd()");
455 assert(PendingLocs.size() > 2 && "Unexpected PendingLocs.size()");
456
457 for (auto &It : PendingLocs) {
458 if (Reg)
459 It.convertToReg(Reg);
460 else
461 It.convertToMem(StackOffset);
462 State.addLoc(It);
463 }
464 PendingLocs.clear();
465 PendingArgFlags.clear();
466 return false;
467 }
468
469 assert(LocVT == XLenVT && "Expected an XLenVT at this stage");
470
471 if (Reg) {
472 State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
473 } else {
474 State.addLoc(
475 CCValAssign::getMem(ValNo, ValVT, StackOffset, LocVT, LocInfo));
476 }
477 return false;
478 }
479
480 void RISCVTargetLowering::analyzeInputArgs(
481 MachineFunction &MF, CCState &CCInfo,
482 const SmallVectorImpl &Ins, bool IsRet) const {
483 unsigned NumArgs = Ins.size();
484
485 for (unsigned i = 0; i != NumArgs; ++i) {
486 MVT ArgVT = Ins[i].VT;
487 ISD::ArgFlagsTy ArgFlags = Ins[i].Flags;
488
489 if (CC_RISCV(MF.getDataLayout(), i, ArgVT, ArgVT, CCValAssign::Full,
490 ArgFlags, CCInfo, /*IsRet=*/true, IsRet)) {
491 DEBUG(dbgs() << "InputArg #" << i << " has unhandled type "
492 << EVT(ArgVT).getEVTString() << '\n');
493 llvm_unreachable(nullptr);
494 }
495 }
496 }
497
498 void RISCVTargetLowering::analyzeOutputArgs(
499 MachineFunction &MF, CCState &CCInfo,
500 const SmallVectorImpl &Outs, bool IsRet) const {
501 unsigned NumArgs = Outs.size();
502
503 for (unsigned i = 0; i != NumArgs; i++) {
504 MVT ArgVT = Outs[i].VT;
505 ISD::ArgFlagsTy ArgFlags = Outs[i].Flags;
506
507 if (CC_RISCV(MF.getDataLayout(), i, ArgVT, ArgVT, CCValAssign::Full,
508 ArgFlags, CCInfo, Outs[i].IsFixed, IsRet)) {
509 DEBUG(dbgs() << "OutputArg #" << i << " has unhandled type "
510 << EVT(ArgVT).getEVTString() << "\n");
511 llvm_unreachable(nullptr);
512 }
513 }
514 }
515
516 // The caller is responsible for loading the full value if the argument is
517 // passed with CCValAssign::Indirect.
518 static SDValue unpackFromRegLoc(SelectionDAG &DAG, SDValue Chain,
519 const CCValAssign &VA, const SDLoc &DL) {
520 MachineFunction &MF = DAG.getMachineFunction();
521 MachineRegisterInfo &RegInfo = MF.getRegInfo();
522 EVT LocVT = VA.getLocVT();
523 SDValue Val;
524
525 unsigned VReg = RegInfo.createVirtualRegister(&RISCV::GPRRegClass);
526 RegInfo.addLiveIn(VA.getLocReg(), VReg);
527 Val = DAG.getCopyFromReg(Chain, DL, VReg, LocVT);
528
529 switch (VA.getLocInfo()) {
530 default:
531 llvm_unreachable("Unexpected CCValAssign::LocInfo");
532 case CCValAssign::Full:
533 case CCValAssign::Indirect:
534 return Val;
535 }
536 }
537
538 // The caller is responsible for loading the full value if the argument is
539 // passed with CCValAssign::Indirect.
540 static SDValue unpackFromMemLoc(SelectionDAG &DAG, SDValue Chain,
541 const CCValAssign &VA, const SDLoc &DL) {
542 MachineFunction &MF = DAG.getMachineFunction();
543 MachineFrameInfo &MFI = MF.getFrameInfo();
544 EVT LocVT = VA.getLocVT();
545 EVT ValVT = VA.getValVT();
546 EVT PtrVT = MVT::getIntegerVT(DAG.getDataLayout().getPointerSizeInBits(0));
547 int FI = MFI.CreateFixedObject(ValVT.getSizeInBits() / 8,
548 VA.getLocMemOffset(), /*Immutable=*/true);
549 SDValue FIN = DAG.getFrameIndex(FI, PtrVT);
550 SDValue Val;
551
552 ISD::LoadExtType ExtType;
553 switch (VA.getLocInfo()) {
554 default:
555 llvm_unreachable("Unexpected CCValAssign::LocInfo");
556 case CCValAssign::Full:
557 case CCValAssign::Indirect:
558 ExtType = ISD::NON_EXTLOAD;
559 break;
560 }
561 Val = DAG.getExtLoad(
562 ExtType, DL, LocVT, Chain, FIN,
563 MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI), ValVT);
564 return Val;
565 }
331566
332567 // Transform physical registers into virtual registers.
333568 SDValue RISCVTargetLowering::LowerFormalArguments(
344579 }
345580
346581 MachineFunction &MF = DAG.getMachineFunction();
347 MachineRegisterInfo &RegInfo = MF.getRegInfo();
348582 MVT XLenVT = Subtarget.getXLenVT();
583 EVT PtrVT = getPointerTy(DAG.getDataLayout());
349584
350585 if (IsVarArg)
351586 report_fatal_error("VarArg not supported");
353588 // Assign locations to all of the incoming arguments.
354589 SmallVector ArgLocs;
355590 CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext());
356 CCInfo.AnalyzeFormalArguments(Ins, CC_RISCV32);
357
358 for (auto &VA : ArgLocs) {
359 if (!VA.isRegLoc())
360 report_fatal_error("Defined with too many args");
361
362 // Arguments passed in registers.
363 EVT RegVT = VA.getLocVT();
364 if (RegVT != XLenVT) {
365 DEBUG(dbgs() << "LowerFormalArguments Unhandled argument type: "
366 << RegVT.getEVTString() << "\n");
367 report_fatal_error("unhandled argument type");
591 analyzeInputArgs(MF, CCInfo, Ins, /*IsRet=*/false);
592
593 for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) {
594 CCValAssign &VA = ArgLocs[i];
595 assert(VA.getLocVT() == XLenVT && "Unhandled argument type");
596 SDValue ArgValue;
597 if (VA.isRegLoc())
598 ArgValue = unpackFromRegLoc(DAG, Chain, VA, DL);
599 else
600 ArgValue = unpackFromMemLoc(DAG, Chain, VA, DL);
601
602 if (VA.getLocInfo() == CCValAssign::Indirect) {
603 // If the original argument was split and passed by reference (e.g. i128
604 // on RV32), we need to load all parts of it here (using the same
605 // address).
606 InVals.push_back(DAG.getLoad(VA.getValVT(), DL, Chain, ArgValue,
607 MachinePointerInfo()));
608 unsigned ArgIndex = Ins[i].OrigArgIndex;
609 assert(Ins[i].PartOffset == 0);
610 while (i + 1 != e && Ins[i + 1].OrigArgIndex == ArgIndex) {
611 CCValAssign &PartVA = ArgLocs[i + 1];
612 unsigned PartOffset = Ins[i + 1].PartOffset;
613 SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, ArgValue,
614 DAG.getIntPtrConstant(PartOffset, DL));
615 InVals.push_back(DAG.getLoad(PartVA.getValVT(), DL, Chain, Address,
616 MachinePointerInfo()));
617 ++i;
618 }
619 continue;
368620 }
369 const unsigned VReg =
370 RegInfo.createVirtualRegister(&RISCV::GPRRegClass);
371 RegInfo.addLiveIn(VA.getLocReg(), VReg);
372 SDValue ArgIn = DAG.getCopyFromReg(Chain, DL, VReg, RegVT);
373
374 InVals.push_back(ArgIn);
621 InVals.push_back(ArgValue);
375622 }
376623 return Chain;
377624 }
391638 CallingConv::ID CallConv = CLI.CallConv;
392639 bool IsVarArg = CLI.IsVarArg;
393640 EVT PtrVT = getPointerTy(DAG.getDataLayout());
641 MVT XLenVT = Subtarget.getXLenVT();
394642
395643 if (IsVarArg) {
396644 report_fatal_error("LowerCall with varargs not implemented");
401649 // Analyze the operands of the call, assigning locations to each operand.
402650 SmallVector ArgLocs;
403651 CCState ArgCCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext());
404 ArgCCInfo.AnalyzeCallOperands(Outs, CC_RISCV32);
652 analyzeOutputArgs(MF, ArgCCInfo, Outs, /*IsRet=*/false);
405653
406654 // Get a count of how many bytes are to be pushed on the stack.
407655 unsigned NumBytes = ArgCCInfo.getNextStackOffset();
408656
409 for (auto &Arg : Outs) {
410 if (!Arg.Flags.isByVal())
657 // Create local copies for byval args
658 SmallVector ByValArgs;
659 for (unsigned i = 0, e = Outs.size(); i != e; ++i) {
660 ISD::ArgFlagsTy Flags = Outs[i].Flags;
661 if (!Flags.isByVal())
411662 continue;
412 report_fatal_error("Passing arguments byval not yet implemented");
663
664 SDValue Arg = OutVals[i];
665 unsigned Size = Flags.getByValSize();
666 unsigned Align = Flags.getByValAlign();
667
668 int FI = MF.getFrameInfo().CreateStackObject(Size, Align, /*isSS=*/false);
669 SDValue FIPtr = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout()));
670 SDValue SizeNode = DAG.getConstant(Size, DL, XLenVT);
671
672 Chain = DAG.getMemcpy(Chain, DL, FIPtr, Arg, SizeNode, Align,
673 /*IsVolatile=*/false,
674 /*AlwaysInline=*/false,
675 /*isTailCall=*/false, MachinePointerInfo(),
676 MachinePointerInfo());
677 ByValArgs.push_back(FIPtr);
413678 }
414679
415680 Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, CLI.DL);
416681
417682 // Copy argument values to their designated locations.
418683 SmallVector, 8> RegsToPass;
684 SmallVector MemOpChains;
419685 SDValue StackPtr;
420 for (unsigned I = 0, E = ArgLocs.size(); I != E; ++I) {
421 CCValAssign &VA = ArgLocs[I];
422 SDValue ArgValue = OutVals[I];
686 for (unsigned i = 0, j = 0, e = ArgLocs.size(); i != e; ++i) {
687 CCValAssign &VA = ArgLocs[i];
688 SDValue ArgValue = OutVals[i];
689 ISD::ArgFlagsTy Flags = Outs[i].Flags;
423690
424691 // Promote the value if needed.
425 // For now, only handle fully promoted arguments.
692 // For now, only handle fully promoted and indirect arguments.
426693 switch (VA.getLocInfo()) {
427694 case CCValAssign::Full:
428695 break;
696 case CCValAssign::Indirect: {
697 // Store the argument in a stack slot and pass its address.
698 SDValue SpillSlot = DAG.CreateStackTemporary(Outs[i].ArgVT);
699 int FI = cast(SpillSlot)->getIndex();
700 MemOpChains.push_back(
701 DAG.getStore(Chain, DL, ArgValue, SpillSlot,
702 MachinePointerInfo::getFixedStack(MF, FI)));
703 // If the original argument was split (e.g. i128), we need
704 // to store all parts of it here (and pass just one address).
705 unsigned ArgIndex = Outs[i].OrigArgIndex;
706 assert(Outs[i].PartOffset == 0);
707 while (i + 1 != e && Outs[i + 1].OrigArgIndex == ArgIndex) {
708 SDValue PartValue = OutVals[i + 1];
709 unsigned PartOffset = Outs[i + 1].PartOffset;
710 SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, SpillSlot,
711 DAG.getIntPtrConstant(PartOffset, DL));
712 MemOpChains.push_back(
713 DAG.getStore(Chain, DL, PartValue, Address,
714 MachinePointerInfo::getFixedStack(MF, FI)));
715 ++i;
716 }
717 ArgValue = SpillSlot;
718 break;
719 }
429720 default:
430721 llvm_unreachable("Unknown loc info!");
431722 }
723
724 // Use local copy if it is a byval arg.
725 if (Flags.isByVal())
726 ArgValue = ByValArgs[j++];
432727
433728 if (VA.isRegLoc()) {
434729 // Queue up the argument copies and emit them at the end.
435730 RegsToPass.push_back(std::make_pair(VA.getLocReg(), ArgValue));
436731 } else {
437732 assert(VA.isMemLoc() && "Argument not register or memory");
438 report_fatal_error("Passing arguments via the stack not yet implemented");
733
734 // Work out the address of the stack slot.
735 if (!StackPtr.getNode())
736 StackPtr = DAG.getCopyFromReg(Chain, DL, RISCV::X2, PtrVT);
737 SDValue Address =
738 DAG.getNode(ISD::ADD, DL, PtrVT, StackPtr,
739 DAG.getIntPtrConstant(VA.getLocMemOffset(), DL));
740
741 // Emit the store.
742 MemOpChains.push_back(
743 DAG.getStore(Chain, DL, ArgValue, Address, MachinePointerInfo()));
439744 }
440745 }
746
747 // Join the stores, which are independent of one another.
748 if (!MemOpChains.empty())
749 Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains);
441750
442751 SDValue Glue;
443752
488797 // Assign locations to each value returned by this call.
489798 SmallVector RVLocs;
490799 CCState RetCCInfo(CallConv, IsVarArg, MF, RVLocs, *DAG.getContext());
491 RetCCInfo.AnalyzeCallResult(Ins, RetCC_RISCV32);
800 analyzeInputArgs(MF, RetCCInfo, Ins, /*IsRet=*/true);
492801
493802 // Copy all of the result registers out of their specified physreg.
494803 for (auto &VA : RVLocs) {
498807 Chain = RetValue.getValue(1);
499808 Glue = RetValue.getValue(2);
500809
501 InVals.push_back(Chain.getValue(0));
810 assert(VA.getLocInfo() == CCValAssign::Full && "Unknown loc info!");
811 InVals.push_back(RetValue);
502812 }
503813
504814 return Chain;
815 }
816
817 bool RISCVTargetLowering::CanLowerReturn(
818 CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg,
819 const SmallVectorImpl &Outs, LLVMContext &Context) const {
820 SmallVector RVLocs;
821 CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, Context);
822 for (unsigned i = 0, e = Outs.size(); i != e; ++i) {
823 MVT VT = Outs[i].VT;
824 ISD::ArgFlagsTy ArgFlags = Outs[i].Flags;
825 if (CC_RISCV(MF.getDataLayout(), i, VT, VT, CCValAssign::Full, ArgFlags,
826 CCInfo, /*IsFixed=*/true, /*IsRet=*/true))
827 return false;
828 }
829 return true;
505830 }
506831
507832 SDValue
521846 CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs,
522847 *DAG.getContext());
523848
524 CCInfo.AnalyzeReturn(Outs, RetCC_RISCV32);
849 analyzeOutputArgs(DAG.getMachineFunction(), CCInfo, Outs, /*IsRet=*/true);
525850
526851 SDValue Flag;
527852 SmallVector RetOps(1, Chain);
528853
529854 // Copy the result values into the output registers.
530855 for (unsigned i = 0, e = RVLocs.size(); i < e; ++i) {
856 SDValue Val = OutVals[i];
531857 CCValAssign &VA = RVLocs[i];
532858 assert(VA.isRegLoc() && "Can only return in registers!");
533
534 Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), OutVals[i], Flag);
859 assert(VA.getLocInfo() == CCValAssign::Full &&
860 "Unexpected CCValAssign::LocInfo");
861
862 Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), Val, Flag);
535863
536864 // Guarantee that all emitted copies are stuck together.
537865 Flag = Chain.getValue(1);
4747 MachineBasicBlock *BB) const override;
4848
4949 private:
50 void analyzeInputArgs(MachineFunction &MF, CCState &CCInfo,
51 const SmallVectorImpl &Ins,
52 bool IsRet) const;
53 void analyzeOutputArgs(MachineFunction &MF, CCState &CCInfo,
54 const SmallVectorImpl &Outs,
55 bool IsRet) const;
5056 // Lower incoming arguments, copy physregs into vregs
5157 SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv,
5258 bool IsVarArg,
5359 const SmallVectorImpl &Ins,
5460 const SDLoc &DL, SelectionDAG &DAG,
5561 SmallVectorImpl &InVals) const override;
62 bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF,
63 bool IsVarArg,
64 const SmallVectorImpl &Outs,
65 LLVMContext &Context) const override;
5666 SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
5767 const SmallVectorImpl &Outs,
5868 const SmallVectorImpl &OutVals, const SDLoc &DL,
0 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
1 ; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
2 ; RUN: | FileCheck -check-prefix=RV32I %s
3
4 %struct.Foo = type { i32, i32, i32, i16, i8 }
5 @foo = global %struct.Foo { i32 1, i32 2, i32 3, i16 4, i8 5 }, align 4
6
7 define i32 @callee(%struct.Foo* byval %f) nounwind {
8 ; RV32I-LABEL: callee:
9 ; RV32I: # %bb.0: # %entry
10 ; RV32I-NEXT: addi sp, sp, -16
11 ; RV32I-NEXT: sw ra, 12(sp)
12 ; RV32I-NEXT: sw s0, 8(sp)
13 ; RV32I-NEXT: addi s0, sp, 16
14 ; RV32I-NEXT: lw a0, 0(a0)
15 ; RV32I-NEXT: lw s0, 8(sp)
16 ; RV32I-NEXT: lw ra, 12(sp)
17 ; RV32I-NEXT: addi sp, sp, 16
18 ; RV32I-NEXT: jalr zero, ra, 0
19 entry:
20 %0 = getelementptr inbounds %struct.Foo, %struct.Foo* %f, i32 0, i32 0
21 %1 = load i32, i32* %0, align 4
22 ret i32 %1
23 }
24
25
26 define void @caller() nounwind {
27 ; RV32I-LABEL: caller:
28 ; RV32I: # %bb.0: # %entry
29 ; RV32I-NEXT: addi sp, sp, -32
30 ; RV32I-NEXT: sw ra, 28(sp)
31 ; RV32I-NEXT: sw s0, 24(sp)
32 ; RV32I-NEXT: addi s0, sp, 32
33 ; RV32I-NEXT: lui a0, %hi(foo+12)
34 ; RV32I-NEXT: addi a0, a0, %lo(foo+12)
35 ; RV32I-NEXT: lw a0, 0(a0)
36 ; RV32I-NEXT: sw a0, -12(s0)
37 ; RV32I-NEXT: lui a0, %hi(foo+8)
38 ; RV32I-NEXT: addi a0, a0, %lo(foo+8)
39 ; RV32I-NEXT: lw a0, 0(a0)
40 ; RV32I-NEXT: sw a0, -16(s0)
41 ; RV32I-NEXT: lui a0, %hi(foo+4)
42 ; RV32I-NEXT: addi a0, a0, %lo(foo+4)
43 ; RV32I-NEXT: lw a0, 0(a0)
44 ; RV32I-NEXT: sw a0, -20(s0)
45 ; RV32I-NEXT: lui a0, %hi(foo)
46 ; RV32I-NEXT: addi a0, a0, %lo(foo)
47 ; RV32I-NEXT: lw a0, 0(a0)
48 ; RV32I-NEXT: sw a0, -24(s0)
49 ; RV32I-NEXT: lui a0, %hi(callee)
50 ; RV32I-NEXT: addi a1, a0, %lo(callee)
51 ; RV32I-NEXT: addi a0, s0, -24
52 ; RV32I-NEXT: jalr ra, a1, 0
53 ; RV32I-NEXT: lw s0, 24(sp)
54 ; RV32I-NEXT: lw ra, 28(sp)
55 ; RV32I-NEXT: addi sp, sp, 32
56 ; RV32I-NEXT: jalr zero, ra, 0
57 entry:
58 %call = call i32 @callee(%struct.Foo* byval @foo)
59 ret void
60 }
0 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
1 ; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
2 ; RUN: | FileCheck -check-prefix=RV32I %s
3
4 define zeroext i8 @uint8_arg_to_uint8_ret(i8 zeroext %a) nounwind {
5 ; RV32I-LABEL: uint8_arg_to_uint8_ret:
6 ; RV32I: # %bb.0:
7 ; RV32I-NEXT: addi sp, sp, -16
8 ; RV32I-NEXT: sw ra, 12(sp)
9 ; RV32I-NEXT: sw s0, 8(sp)
10 ; RV32I-NEXT: addi s0, sp, 16
11 ; RV32I-NEXT: lw s0, 8(sp)
12 ; RV32I-NEXT: lw ra, 12(sp)
13 ; RV32I-NEXT: addi sp, sp, 16
14 ; RV32I-NEXT: jalr zero, ra, 0
15 ret i8 %a
16 }
17
18 declare void @receive_uint8(i8 zeroext)
19
20 define void @pass_uint8_as_uint8(i8 zeroext %a) nounwind {
21 ; RV32I-LABEL: pass_uint8_as_uint8:
22 ; RV32I: # %bb.0:
23 ; RV32I-NEXT: addi sp, sp, -16
24 ; RV32I-NEXT: sw ra, 12(sp)
25 ; RV32I-NEXT: sw s0, 8(sp)
26 ; RV32I-NEXT: addi s0, sp, 16
27 ; RV32I-NEXT: lui a1, %hi(receive_uint8)
28 ; RV32I-NEXT: addi a1, a1, %lo(receive_uint8)
29 ; RV32I-NEXT: jalr ra, a1, 0
30 ; RV32I-NEXT: lw s0, 8(sp)
31 ; RV32I-NEXT: lw ra, 12(sp)
32 ; RV32I-NEXT: addi sp, sp, 16
33 ; RV32I-NEXT: jalr zero, ra, 0
34 call void @receive_uint8(i8 zeroext %a)
35 ret void
36 }
37
38 declare zeroext i8 @return_uint8()
39
40 define zeroext i8 @ret_callresult_uint8_as_uint8() nounwind {
41 ; RV32I-LABEL: ret_callresult_uint8_as_uint8:
42 ; RV32I: # %bb.0:
43 ; RV32I-NEXT: addi sp, sp, -16
44 ; RV32I-NEXT: sw ra, 12(sp)
45 ; RV32I-NEXT: sw s0, 8(sp)
46 ; RV32I-NEXT: addi s0, sp, 16
47 ; RV32I-NEXT: lui a0, %hi(return_uint8)
48 ; RV32I-NEXT: addi a0, a0, %lo(return_uint8)
49 ; RV32I-NEXT: jalr ra, a0, 0
50 ; RV32I-NEXT: lw s0, 8(sp)
51 ; RV32I-NEXT: lw ra, 12(sp)
52 ; RV32I-NEXT: addi sp, sp, 16
53 ; RV32I-NEXT: jalr zero, ra, 0
54 %1 = call zeroext i8 @return_uint8()
55 ret i8 %1
56 }
57
58 define signext i8 @uint8_arg_to_sint8_ret(i8 zeroext %a) nounwind {
59 ; RV32I-LABEL: uint8_arg_to_sint8_ret:
60 ; RV32I: # %bb.0:
61 ; RV32I-NEXT: addi sp, sp, -16
62 ; RV32I-NEXT: sw ra, 12(sp)
63 ; RV32I-NEXT: sw s0, 8(sp)
64 ; RV32I-NEXT: addi s0, sp, 16
65 ; RV32I-NEXT: slli a0, a0, 24
66 ; RV32I-NEXT: srai a0, a0, 24
67 ; RV32I-NEXT: lw s0, 8(sp)
68 ; RV32I-NEXT: lw ra, 12(sp)
69 ; RV32I-NEXT: addi sp, sp, 16
70 ; RV32I-NEXT: jalr zero, ra, 0
71 ret i8 %a
72 }
73
74 declare void @receive_sint8(i8 signext)
75
76 define void @pass_uint8_as_sint8(i8 zeroext %a) nounwind {
77 ; RV32I-LABEL: pass_uint8_as_sint8:
78 ; RV32I: # %bb.0:
79 ; RV32I-NEXT: addi sp, sp, -16
80 ; RV32I-NEXT: sw ra, 12(sp)
81 ; RV32I-NEXT: sw s0, 8(sp)
82 ; RV32I-NEXT: addi s0, sp, 16
83 ; RV32I-NEXT: lui a1, %hi(receive_sint8)
84 ; RV32I-NEXT: addi a1, a1, %lo(receive_sint8)
85 ; RV32I-NEXT: slli a0, a0, 24
86 ; RV32I-NEXT: srai a0, a0, 24
87 ; RV32I-NEXT: jalr ra, a1, 0
88 ; RV32I-NEXT: lw s0, 8(sp)
89 ; RV32I-NEXT: lw ra, 12(sp)
90 ; RV32I-NEXT: addi sp, sp, 16
91 ; RV32I-NEXT: jalr zero, ra, 0
92
93 call void @receive_sint8(i8 signext %a)
94 ret void
95 }
96
97 define signext i8 @ret_callresult_uint8_as_sint8() nounwind {
98 ; RV32I-LABEL: ret_callresult_uint8_as_sint8:
99 ; RV32I: # %bb.0:
100 ; RV32I-NEXT: addi sp, sp, -16
101 ; RV32I-NEXT: sw ra, 12(sp)
102 ; RV32I-NEXT: sw s0, 8(sp)
103 ; RV32I-NEXT: addi s0, sp, 16
104 ; RV32I-NEXT: lui a0, %hi(return_uint8)
105 ; RV32I-NEXT: addi a0, a0, %lo(return_uint8)
106 ; RV32I-NEXT: jalr ra, a0, 0
107 ; RV32I-NEXT: slli a0, a0, 24
108 ; RV32I-NEXT: srai a0, a0, 24
109 ; RV32I-NEXT: lw s0, 8(sp)
110 ; RV32I-NEXT: lw ra, 12(sp)
111 ; RV32I-NEXT: addi sp, sp, 16
112 ; RV32I-NEXT: jalr zero, ra, 0
113 %1 = call zeroext i8 @return_uint8()
114 ret i8 %1
115 }
116
117 define signext i32 @uint8_arg_to_anyint32_ret(i8 zeroext %a) nounwind {
118 ; RV32I-LABEL: uint8_arg_to_anyint32_ret:
119 ; RV32I: # %bb.0:
120 ; RV32I-NEXT: addi sp, sp, -16
121 ; RV32I-NEXT: sw ra, 12(sp)
122 ; RV32I-NEXT: sw s0, 8(sp)
123 ; RV32I-NEXT: addi s0, sp, 16
124 ; RV32I-NEXT: lw s0, 8(sp)
125 ; RV32I-NEXT: lw ra, 12(sp)
126 ; RV32I-NEXT: addi sp, sp, 16
127 ; RV32I-NEXT: jalr zero, ra, 0
128 %1 = zext i8 %a to i32
129 ret i32 %1
130 }
131
132 declare void @receive_anyint32(i32 signext)
133
134 define void @pass_uint8_as_anyint32(i8 zeroext %a) nounwind {
135 ; RV32I-LABEL: pass_uint8_as_anyint32:
136 ; RV32I: # %bb.0:
137 ; RV32I-NEXT: addi sp, sp, -16
138 ; RV32I-NEXT: sw ra, 12(sp)
139 ; RV32I-NEXT: sw s0, 8(sp)
140 ; RV32I-NEXT: addi s0, sp, 16
141 ; RV32I-NEXT: lui a1, %hi(receive_anyint32)
142 ; RV32I-NEXT: addi a1, a1, %lo(receive_anyint32)
143 ; RV32I-NEXT: jalr ra, a1, 0
144 ; RV32I-NEXT: lw s0, 8(sp)
145 ; RV32I-NEXT: lw ra, 12(sp)
146 ; RV32I-NEXT: addi sp, sp, 16
147 ; RV32I-NEXT: jalr zero, ra, 0
148 %1 = zext i8 %a to i32
149 call void @receive_anyint32(i32 signext %1)
150 ret void
151 }
152
153 define signext i32 @ret_callresult_uint8_as_anyint32() nounwind {
154 ; RV32I-LABEL: ret_callresult_uint8_as_anyint32:
155 ; RV32I: # %bb.0:
156 ; RV32I-NEXT: addi sp, sp, -16
157 ; RV32I-NEXT: sw ra, 12(sp)
158 ; RV32I-NEXT: sw s0, 8(sp)
159 ; RV32I-NEXT: addi s0, sp, 16
160 ; RV32I-NEXT: lui a0, %hi(return_uint8)
161 ; RV32I-NEXT: addi a0, a0, %lo(return_uint8)
162 ; RV32I-NEXT: jalr ra, a0, 0
163 ; RV32I-NEXT: lw s0, 8(sp)
164 ; RV32I-NEXT: lw ra, 12(sp)
165 ; RV32I-NEXT: addi sp, sp, 16
166 ; RV32I-NEXT: jalr zero, ra, 0
167 %1 = call zeroext i8 @return_uint8()
168 %2 = zext i8 %1 to i32
169 ret i32 %2
170 }
171
172 define zeroext i8 @sint8_arg_to_uint8_ret(i8 signext %a) nounwind {
173 ; RV32I-LABEL: sint8_arg_to_uint8_ret:
174 ; RV32I: # %bb.0:
175 ; RV32I-NEXT: addi sp, sp, -16
176 ; RV32I-NEXT: sw ra, 12(sp)
177 ; RV32I-NEXT: sw s0, 8(sp)
178 ; RV32I-NEXT: addi s0, sp, 16
179 ; RV32I-NEXT: andi a0, a0, 255
180 ; RV32I-NEXT: lw s0, 8(sp)
181 ; RV32I-NEXT: lw ra, 12(sp)
182 ; RV32I-NEXT: addi sp, sp, 16
183 ; RV32I-NEXT: jalr zero, ra, 0
184 ret i8 %a
185 }
186
187 define void @pass_sint8_as_uint8(i8 signext %a) nounwind {
188 ; RV32I-LABEL: pass_sint8_as_uint8:
189 ; RV32I: # %bb.0:
190 ; RV32I-NEXT: addi sp, sp, -16
191 ; RV32I-NEXT: sw ra, 12(sp)
192 ; RV32I-NEXT: sw s0, 8(sp)
193 ; RV32I-NEXT: addi s0, sp, 16
194 ; RV32I-NEXT: andi a0, a0, 255
195 ; RV32I-NEXT: lui a1, %hi(receive_uint8)
196 ; RV32I-NEXT: addi a1, a1, %lo(receive_uint8)
197 ; RV32I-NEXT: jalr ra, a1, 0
198 ; RV32I-NEXT: lw s0, 8(sp)
199 ; RV32I-NEXT: lw ra, 12(sp)
200 ; RV32I-NEXT: addi sp, sp, 16
201 ; RV32I-NEXT: jalr zero, ra, 0
202 call void @receive_uint8(i8 zeroext %a)
203 ret void
204 }
205
206 declare signext i8 @return_sint8()
207
208 define zeroext i8 @ret_callresult_sint8_as_uint8() nounwind {
209 ; RV32I-LABEL: ret_callresult_sint8_as_uint8:
210 ; RV32I: # %bb.0:
211 ; RV32I-NEXT: addi sp, sp, -16
212 ; RV32I-NEXT: sw ra, 12(sp)
213 ; RV32I-NEXT: sw s0, 8(sp)
214 ; RV32I-NEXT: addi s0, sp, 16
215 ; RV32I-NEXT: lui a0, %hi(return_sint8)
216 ; RV32I-NEXT: addi a0, a0, %lo(return_sint8)
217 ; RV32I-NEXT: jalr ra, a0, 0
218 ; RV32I-NEXT: andi a0, a0, 255
219 ; RV32I-NEXT: lw s0, 8(sp)
220 ; RV32I-NEXT: lw ra, 12(sp)
221 ; RV32I-NEXT: addi sp, sp, 16
222 ; RV32I-NEXT: jalr zero, ra, 0
223 %1 = call signext i8 @return_sint8()
224 ret i8 %1
225 }
226
227 define signext i8 @sint8_arg_to_sint8_ret(i8 signext %a) nounwind {
228 ; RV32I-LABEL: sint8_arg_to_sint8_ret:
229 ; RV32I: # %bb.0:
230 ; RV32I-NEXT: addi sp, sp, -16
231 ; RV32I-NEXT: sw ra, 12(sp)
232 ; RV32I-NEXT: sw s0, 8(sp)
233 ; RV32I-NEXT: addi s0, sp, 16
234 ; RV32I-NEXT: lw s0, 8(sp)
235 ; RV32I-NEXT: lw ra, 12(sp)
236 ; RV32I-NEXT: addi sp, sp, 16
237 ; RV32I-NEXT: jalr zero, ra, 0
238 ret i8 %a
239 }
240
241 define void @pass_sint8_as_sint8(i8 signext %a) nounwind {
242 ; RV32I-LABEL: pass_sint8_as_sint8:
243 ; RV32I: # %bb.0:
244 ; RV32I-NEXT: addi sp, sp, -16
245 ; RV32I-NEXT: sw ra, 12(sp)
246 ; RV32I-NEXT: sw s0, 8(sp)
247 ; RV32I-NEXT: addi s0, sp, 16
248 ; RV32I-NEXT: lui a1, %hi(receive_sint8)
249 ; RV32I-NEXT: addi a1, a1, %lo(receive_sint8)
250 ; RV32I-NEXT: jalr ra, a1, 0
251 ; RV32I-NEXT: lw s0, 8(sp)
252 ; RV32I-NEXT: lw ra, 12(sp)
253 ; RV32I-NEXT: addi sp, sp, 16
254 ; RV32I-NEXT: jalr zero, ra, 0
255 call void @receive_sint8(i8 signext %a)
256 ret void
257 }
258
259 define signext i8 @ret_callresult_sint8_as_sint8() nounwind {
260 ; RV32I-LABEL: ret_callresult_sint8_as_sint8:
261 ; RV32I: # %bb.0:
262 ; RV32I-NEXT: addi sp, sp, -16
263 ; RV32I-NEXT: sw ra, 12(sp)
264 ; RV32I-NEXT: sw s0, 8(sp)
265 ; RV32I-NEXT: addi s0, sp, 16
266 ; RV32I-NEXT: lui a0, %hi(return_sint8)
267 ; RV32I-NEXT: addi a0, a0, %lo(return_sint8)
268 ; RV32I-NEXT: jalr ra, a0, 0
269 ; RV32I-NEXT: lw s0, 8(sp)
270 ; RV32I-NEXT: lw ra, 12(sp)
271 ; RV32I-NEXT: addi sp, sp, 16
272 ; RV32I-NEXT: jalr zero, ra, 0
273 %1 = call signext i8 @return_sint8()
274 ret i8 %1
275 }
276
277 define signext i32 @sint8_arg_to_anyint32_ret(i8 signext %a) nounwind {
278 ; RV32I-LABEL: sint8_arg_to_anyint32_ret:
279 ; RV32I: # %bb.0:
280 ; RV32I-NEXT: addi sp, sp, -16
281 ; RV32I-NEXT: sw ra, 12(sp)
282 ; RV32I-NEXT: sw s0, 8(sp)
283 ; RV32I-NEXT: addi s0, sp, 16
284 ; RV32I-NEXT: lw s0, 8(sp)
285 ; RV32I-NEXT: lw ra, 12(sp)
286 ; RV32I-NEXT: addi sp, sp, 16
287 ; RV32I-NEXT: jalr zero, ra, 0
288 %1 = sext i8 %a to i32
289 ret i32 %1
290 }
291
292 define void @pass_sint8_as_anyint32(i8 signext %a) nounwind {
293 ; RV32I-LABEL: pass_sint8_as_anyint32:
294 ; RV32I: # %bb.0:
295 ; RV32I-NEXT: addi sp, sp, -16
296 ; RV32I-NEXT: sw ra, 12(sp)
297 ; RV32I-NEXT: sw s0, 8(sp)
298 ; RV32I-NEXT: addi s0, sp, 16
299 ; RV32I-NEXT: lui a1, %hi(receive_anyint32)
300 ; RV32I-NEXT: addi a1, a1, %lo(receive_anyint32)
301 ; RV32I-NEXT: jalr ra, a1, 0
302 ; RV32I-NEXT: lw s0, 8(sp)
303 ; RV32I-NEXT: lw ra, 12(sp)
304 ; RV32I-NEXT: addi sp, sp, 16
305 ; RV32I-NEXT: jalr zero, ra, 0
306 %1 = sext i8 %a to i32
307 call void @receive_anyint32(i32 signext %1)
308 ret void
309 }
310
311 define signext i32 @ret_callresult_sint8_as_anyint32() nounwind {
312 ; RV32I-LABEL: ret_callresult_sint8_as_anyint32:
313 ; RV32I: # %bb.0:
314 ; RV32I-NEXT: addi sp, sp, -16
315 ; RV32I-NEXT: sw ra, 12(sp)
316 ; RV32I-NEXT: sw s0, 8(sp)
317 ; RV32I-NEXT: addi s0, sp, 16
318 ; RV32I-NEXT: lui a0, %hi(return_sint8)
319 ; RV32I-NEXT: addi a0, a0, %lo(return_sint8)
320 ; RV32I-NEXT: jalr ra, a0, 0
321 ; RV32I-NEXT: lw s0, 8(sp)
322 ; RV32I-NEXT: lw ra, 12(sp)
323 ; RV32I-NEXT: addi sp, sp, 16
324 ; RV32I-NEXT: jalr zero, ra, 0
325 %1 = call signext i8 @return_sint8()
326 %2 = sext i8 %1 to i32
327 ret i32 %2
328 }
329
330 define zeroext i8 @anyint32_arg_to_uint8_ret(i32 signext %a) nounwind {
331 ; RV32I-LABEL: anyint32_arg_to_uint8_ret:
332 ; RV32I: # %bb.0:
333 ; RV32I-NEXT: addi sp, sp, -16
334 ; RV32I-NEXT: sw ra, 12(sp)
335 ; RV32I-NEXT: sw s0, 8(sp)
336 ; RV32I-NEXT: addi s0, sp, 16
337 ; RV32I-NEXT: andi a0, a0, 255
338 ; RV32I-NEXT: lw s0, 8(sp)
339 ; RV32I-NEXT: lw ra, 12(sp)
340 ; RV32I-NEXT: addi sp, sp, 16
341 ; RV32I-NEXT: jalr zero, ra, 0
342 %1 = trunc i32 %a to i8
343 ret i8 %1
344 }
345
346 define void @pass_anyint32_as_uint8(i32 signext %a) nounwind {
347 ; RV32I-LABEL: pass_anyint32_as_uint8:
348 ; RV32I: # %bb.0:
349 ; RV32I-NEXT: addi sp, sp, -16
350 ; RV32I-NEXT: sw ra, 12(sp)
351 ; RV32I-NEXT: sw s0, 8(sp)
352 ; RV32I-NEXT: addi s0, sp, 16
353 ; RV32I-NEXT: andi a0, a0, 255
354 ; RV32I-NEXT: lui a1, %hi(receive_uint8)
355 ; RV32I-NEXT: addi a1, a1, %lo(receive_uint8)
356 ; RV32I-NEXT: jalr ra, a1, 0
357 ; RV32I-NEXT: lw s0, 8(sp)
358 ; RV32I-NEXT: lw ra, 12(sp)
359 ; RV32I-NEXT: addi sp, sp, 16
360 ; RV32I-NEXT: jalr zero, ra, 0
361 %1 = trunc i32 %a to i8
362 call void @receive_uint8(i8 zeroext %1)
363 ret void
364 }
365
366 declare signext i32 @return_anyint32()
367
368 define zeroext i8 @ret_callresult_anyint32_as_uint8() nounwind {
369 ; RV32I-LABEL: ret_callresult_anyint32_as_uint8:
370 ; RV32I: # %bb.0:
371 ; RV32I-NEXT: addi sp, sp, -16
372 ; RV32I-NEXT: sw ra, 12(sp)
373 ; RV32I-NEXT: sw s0, 8(sp)
374 ; RV32I-NEXT: addi s0, sp, 16
375 ; RV32I-NEXT: lui a0, %hi(return_anyint32)
376 ; RV32I-NEXT: addi a0, a0, %lo(return_anyint32)
377 ; RV32I-NEXT: jalr ra, a0, 0
378 ; RV32I-NEXT: andi a0, a0, 255
379 ; RV32I-NEXT: lw s0, 8(sp)
380 ; RV32I-NEXT: lw ra, 12(sp)
381 ; RV32I-NEXT: addi sp, sp, 16
382 ; RV32I-NEXT: jalr zero, ra, 0
383 %1 = call signext i32 @return_anyint32()
384 %2 = trunc i32 %1 to i8
385 ret i8 %2
386 }
387
388 define signext i8 @anyint32_arg_to_sint8_ret(i32 signext %a) nounwind {
389 ; RV32I-LABEL: anyint32_arg_to_sint8_ret:
390 ; RV32I: # %bb.0:
391 ; RV32I-NEXT: addi sp, sp, -16
392 ; RV32I-NEXT: sw ra, 12(sp)
393 ; RV32I-NEXT: sw s0, 8(sp)
394 ; RV32I-NEXT: addi s0, sp, 16
395 ; RV32I-NEXT: slli a0, a0, 24
396 ; RV32I-NEXT: srai a0, a0, 24
397 ; RV32I-NEXT: lw s0, 8(sp)
398 ; RV32I-NEXT: lw ra, 12(sp)
399 ; RV32I-NEXT: addi sp, sp, 16
400 ; RV32I-NEXT: jalr zero, ra, 0
401 %1 = trunc i32 %a to i8
402 ret i8 %1
403 }
404
405 define void @pass_anyint32_as_sint8(i32 signext %a) nounwind {
406 ; RV32I-LABEL: pass_anyint32_as_sint8:
407 ; RV32I: # %bb.0:
408 ; RV32I-NEXT: addi sp, sp, -16
409 ; RV32I-NEXT: sw ra, 12(sp)
410 ; RV32I-NEXT: sw s0, 8(sp)
411 ; RV32I-NEXT: addi s0, sp, 16
412 ; RV32I-NEXT: lui a1, %hi(receive_sint8)
413 ; RV32I-NEXT: addi a1, a1, %lo(receive_sint8)
414 ; RV32I-NEXT: slli a0, a0, 24
415 ; RV32I-NEXT: srai a0, a0, 24
416 ; RV32I-NEXT: jalr ra, a1, 0
417 ; RV32I-NEXT: lw s0, 8(sp)
418 ; RV32I-NEXT: lw ra, 12(sp)
419 ; RV32I-NEXT: addi sp, sp, 16
420 ; RV32I-NEXT: jalr zero, ra, 0
421 %1 = trunc i32 %a to i8
422 call void @receive_sint8(i8 signext %1)
423 ret void
424 }
425
426 define signext i8 @ret_callresult_anyint32_as_sint8() nounwind {
427 ; RV32I-LABEL: ret_callresult_anyint32_as_sint8:
428 ; RV32I: # %bb.0:
429 ; RV32I-NEXT: addi sp, sp, -16
430 ; RV32I-NEXT: sw ra, 12(sp)
431 ; RV32I-NEXT: sw s0, 8(sp)
432 ; RV32I-NEXT: addi s0, sp, 16
433 ; RV32I-NEXT: lui a0, %hi(return_anyint32)
434 ; RV32I-NEXT: addi a0, a0, %lo(return_anyint32)
435 ; RV32I-NEXT: jalr ra, a0, 0
436 ; RV32I-NEXT: slli a0, a0, 24
437 ; RV32I-NEXT: srai a0, a0, 24
438 ; RV32I-NEXT: lw s0, 8(sp)
439 ; RV32I-NEXT: lw ra, 12(sp)
440 ; RV32I-NEXT: addi sp, sp, 16
441 ; RV32I-NEXT: jalr zero, ra, 0
442 %1 = call signext i32 @return_anyint32()
443 %2 = trunc i32 %1 to i8
444 ret i8 %2
445 }
446
447 define signext i32 @anyint32_arg_to_anyint32_ret(i32 signext %a) nounwind {
448 ; RV32I-LABEL: anyint32_arg_to_anyint32_ret:
449 ; RV32I: # %bb.0:
450 ; RV32I-NEXT: addi sp, sp, -16
451 ; RV32I-NEXT: sw ra, 12(sp)
452 ; RV32I-NEXT: sw s0, 8(sp)
453 ; RV32I-NEXT: addi s0, sp, 16
454 ; RV32I-NEXT: lw s0, 8(sp)
455 ; RV32I-NEXT: lw ra, 12(sp)
456 ; RV32I-NEXT: addi sp, sp, 16
457 ; RV32I-NEXT: jalr zero, ra, 0
458 ret i32 %a
459 }
460
461 define void @pass_anyint32_as_anyint32(i32 signext %a) nounwind {
462 ; RV32I-LABEL: pass_anyint32_as_anyint32:
463 ; RV32I: # %bb.0:
464 ; RV32I-NEXT: addi sp, sp, -16
465 ; RV32I-NEXT: sw ra, 12(sp)
466 ; RV32I-NEXT: sw s0, 8(sp)
467 ; RV32I-NEXT: addi s0, sp, 16
468 ; RV32I-NEXT: lui a1, %hi(receive_anyint32)
469 ; RV32I-NEXT: addi a1, a1, %lo(receive_anyint32)
470 ; RV32I-NEXT: jalr ra, a1, 0
471 ; RV32I-NEXT: lw s0, 8(sp)
472 ; RV32I-NEXT: lw ra, 12(sp)
473 ; RV32I-NEXT: addi sp, sp, 16
474 ; RV32I-NEXT: jalr zero, ra, 0
475 call void @receive_anyint32(i32 signext %a)
476 ret void
477 }
478
479 define signext i32 @ret_callresult_anyint32_as_anyint32() nounwind {
480 ; RV32I-LABEL: ret_callresult_anyint32_as_anyint32:
481 ; RV32I: # %bb.0:
482 ; RV32I-NEXT: addi sp, sp, -16
483 ; RV32I-NEXT: sw ra, 12(sp)
484 ; RV32I-NEXT: sw s0, 8(sp)
485 ; RV32I-NEXT: addi s0, sp, 16
486 ; RV32I-NEXT: lui a0, %hi(return_anyint32)
487 ; RV32I-NEXT: addi a0, a0, %lo(return_anyint32)
488 ; RV32I-NEXT: jalr ra, a0, 0
489 ; RV32I-NEXT: lw s0, 8(sp)
490 ; RV32I-NEXT: lw ra, 12(sp)
491 ; RV32I-NEXT: addi sp, sp, 16
492 ; RV32I-NEXT: jalr zero, ra, 0
493 %1 = call signext i32 @return_anyint32()
494 ret i32 %1
495 }
496
0 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
1 ; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
2 ; RUN: | FileCheck -check-prefix=RV32I %s
3
4 ; As well as calling convention details, we check that ra and fp are
5 ; consistently stored to fp-4 and fp-8.
6
7 ; Check that on RV32, i64 and double are passed in a pair of registers. Unlike
8 ; the convention for varargs, this need not be an aligned pair.
9
10 define i32 @callee_scalars(i32 %a, i64 %b, i32 %c, i32 %d, double %e) nounwind {
11 ; RV32I-LABEL: callee_scalars:
12 ; RV32I: # %bb.0:
13 ; RV32I-NEXT: addi sp, sp, -32
14 ; RV32I-NEXT: sw ra, 28(sp)
15 ; RV32I-NEXT: sw s0, 24(sp)
16 ; RV32I-NEXT: sw s1, 20(sp)
17 ; RV32I-NEXT: sw s2, 16(sp)
18 ; RV32I-NEXT: sw s3, 12(sp)
19 ; RV32I-NEXT: sw s4, 8(sp)
20 ; RV32I-NEXT: addi s0, sp, 32
21 ; RV32I-NEXT: addi s1, a4, 0
22 ; RV32I-NEXT: addi s2, a3, 0
23 ; RV32I-NEXT: addi s3, a1, 0
24 ; RV32I-NEXT: addi s4, a0, 0
25 ; RV32I-NEXT: lui a0, %hi(__fixdfsi)
26 ; RV32I-NEXT: addi a2, a0, %lo(__fixdfsi)
27 ; RV32I-NEXT: addi a0, a5, 0
28 ; RV32I-NEXT: addi a1, a6, 0
29 ; RV32I-NEXT: jalr ra, a2, 0
30 ; RV32I-NEXT: add a1, s4, s3
31 ; RV32I-NEXT: add a1, a1, s2
32 ; RV32I-NEXT: add a1, a1, s1
33 ; RV32I-NEXT: add a0, a1, a0
34 ; RV32I-NEXT: lw s4, 8(sp)
35 ; RV32I-NEXT: lw s3, 12(sp)
36 ; RV32I-NEXT: lw s2, 16(sp)
37 ; RV32I-NEXT: lw s1, 20(sp)
38 ; RV32I-NEXT: lw s0, 24(sp)
39 ; RV32I-NEXT: lw ra, 28(sp)
40 ; RV32I-NEXT: addi sp, sp, 32
41 ; RV32I-NEXT: jalr zero, ra, 0
42 %b_trunc = trunc i64 %b to i32
43 %e_fptosi = fptosi double %e to i32
44 %1 = add i32 %a, %b_trunc
45 %2 = add i32 %1, %c
46 %3 = add i32 %2, %d
47 %4 = add i32 %3, %e_fptosi
48 ret i32 %4
49 }
50
51 define i32 @caller_scalars() nounwind {
52 ; RV32I-LABEL: caller_scalars:
53 ; RV32I: # %bb.0:
54 ; RV32I-NEXT: addi sp, sp, -16
55 ; RV32I-NEXT: sw ra, 12(sp)
56 ; RV32I-NEXT: sw s0, 8(sp)
57 ; RV32I-NEXT: addi s0, sp, 16
58 ; RV32I-NEXT: lui a0, 262464
59 ; RV32I-NEXT: addi a6, a0, 0
60 ; RV32I-NEXT: lui a0, %hi(callee_scalars)
61 ; RV32I-NEXT: addi a7, a0, %lo(callee_scalars)
62 ; RV32I-NEXT: addi a0, zero, 1
63 ; RV32I-NEXT: addi a1, zero, 2
64 ; RV32I-NEXT: addi a3, zero, 3
65 ; RV32I-NEXT: addi a4, zero, 4
66 ; RV32I-NEXT: addi a2, zero, 0
67 ; RV32I-NEXT: addi a5, zero, 0
68 ; RV32I-NEXT: jalr ra, a7, 0
69 ; RV32I-NEXT: lw s0, 8(sp)
70 ; RV32I-NEXT: lw ra, 12(sp)
71 ; RV32I-NEXT: addi sp, sp, 16
72 ; RV32I-NEXT: jalr zero, ra, 0
73 %1 = call i32 @callee_scalars(i32 1, i64 2, i32 3, i32 4, double 5.000000e+00)
74 ret i32 %1
75 }
76
77 ; Check that i128 and fp128 are passed indirectly
78
79 define i32 @callee_large_scalars(i128 %a, fp128 %b) nounwind {
80 ; RV32I-LABEL: callee_large_scalars:
81 ; RV32I: # %bb.0:
82 ; RV32I-NEXT: addi sp, sp, -16
83 ; RV32I-NEXT: sw ra, 12(sp)
84 ; RV32I-NEXT: sw s0, 8(sp)
85 ; RV32I-NEXT: addi s0, sp, 16
86 ; RV32I-NEXT: lw a2, 12(a1)
87 ; RV32I-NEXT: lw a3, 12(a0)
88 ; RV32I-NEXT: xor a2, a3, a2
89 ; RV32I-NEXT: lw a3, 4(a1)
90 ; RV32I-NEXT: lw a4, 4(a0)
91 ; RV32I-NEXT: xor a3, a4, a3
92 ; RV32I-NEXT: or a2, a3, a2
93 ; RV32I-NEXT: lw a3, 8(a1)
94 ; RV32I-NEXT: lw a4, 8(a0)
95 ; RV32I-NEXT: xor a3, a4, a3
96 ; RV32I-NEXT: lw a1, 0(a1)
97 ; RV32I-NEXT: lw a0, 0(a0)
98 ; RV32I-NEXT: xor a0, a0, a1
99 ; RV32I-NEXT: or a0, a0, a3
100 ; RV32I-NEXT: or a0, a0, a2
101 ; RV32I-NEXT: xor a0, a0, zero
102 ; RV32I-NEXT: sltiu a0, a0, 1
103 ; RV32I-NEXT: lw s0, 8(sp)
104 ; RV32I-NEXT: lw ra, 12(sp)
105 ; RV32I-NEXT: addi sp, sp, 16
106 ; RV32I-NEXT: jalr zero, ra, 0
107 %b_bitcast = bitcast fp128 %b to i128
108 %1 = icmp eq i128 %a, %b_bitcast
109 %2 = zext i1 %1 to i32
110 ret i32 %2
111 }
112
113 define i32 @caller_large_scalars() nounwind {
114 ; RV32I-LABEL: caller_large_scalars:
115 ; RV32I: # %bb.0:
116 ; RV32I-NEXT: addi sp, sp, -48
117 ; RV32I-NEXT: sw ra, 44(sp)
118 ; RV32I-NEXT: sw s0, 40(sp)
119 ; RV32I-NEXT: addi s0, sp, 48
120 ; RV32I-NEXT: sw zero, -40(s0)
121 ; RV32I-NEXT: sw zero, -44(s0)
122 ; RV32I-NEXT: sw zero, -48(s0)
123 ; RV32I-NEXT: sw zero, -12(s0)
124 ; RV32I-NEXT: sw zero, -16(s0)
125 ; RV32I-NEXT: sw zero, -20(s0)
126 ; RV32I-NEXT: addi a0, zero, 1
127 ; RV32I-NEXT: sw a0, -24(s0)
128 ; RV32I-NEXT: lui a0, 524272
129 ; RV32I-NEXT: addi a0, a0, 0
130 ; RV32I-NEXT: sw a0, -36(s0)
131 ; RV32I-NEXT: lui a0, %hi(callee_large_scalars)
132 ; RV32I-NEXT: addi a2, a0, %lo(callee_large_scalars)
133 ; RV32I-NEXT: addi a0, s0, -24
134 ; RV32I-NEXT: addi a1, s0, -48
135 ; RV32I-NEXT: jalr ra, a2, 0
136 ; RV32I-NEXT: lw s0, 40(sp)
137 ; RV32I-NEXT: lw ra, 44(sp)
138 ; RV32I-NEXT: addi sp, sp, 48
139 ; RV32I-NEXT: jalr zero, ra, 0
140 %1 = call i32 @callee_large_scalars(i128 1, fp128 0xL00000000000000007FFF000000000000)
141 ret i32 %1
142 }
143
144 ; Must keep define on a single line due to an update_llc_test_checks.py limitation
145 define i32 @callee_large_scalars_exhausted_regs(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i128 %h, i32 %i, fp128 %j) nounwind {
146 ; Check that arguments larger than 2*xlen are handled correctly when their
147 ; address is passed on the stack rather than in memory
148 ; RV32I-LABEL: callee_large_scalars_exhausted_regs:
149 ; RV32I: # %bb.0:
150 ; RV32I-NEXT: addi sp, sp, -16
151 ; RV32I-NEXT: sw ra, 12(sp)
152 ; RV32I-NEXT: sw s0, 8(sp)
153 ; RV32I-NEXT: addi s0, sp, 16
154 ; RV32I-NEXT: lw a0, 4(s0)
155 ; RV32I-NEXT: lw a1, 12(a0)
156 ; RV32I-NEXT: lw a2, 12(a7)
157 ; RV32I-NEXT: xor a1, a2, a1
158 ; RV32I-NEXT: lw a2, 4(a0)
159 ; RV32I-NEXT: lw a3, 4(a7)
160 ; RV32I-NEXT: xor a2, a3, a2
161 ; RV32I-NEXT: or a1, a2, a1
162 ; RV32I-NEXT: lw a2, 8(a0)
163 ; RV32I-NEXT: lw a3, 8(a7)
164 ; RV32I-NEXT: xor a2, a3, a2
165 ; RV32I-NEXT: lw a0, 0(a0)
166 ; RV32I-NEXT: lw a3, 0(a7)
167 ; RV32I-NEXT: xor a0, a3, a0
168 ; RV32I-NEXT: or a0, a0, a2
169 ; RV32I-NEXT: or a0, a0, a1
170 ; RV32I-NEXT: xor a0, a0, zero
171 ; RV32I-NEXT: sltiu a0, a0, 1
172 ; RV32I-NEXT: lw s0, 8(sp)
173 ; RV32I-NEXT: lw ra, 12(sp)
174 ; RV32I-NEXT: addi sp, sp, 16
175 ; RV32I-NEXT: jalr zero, ra, 0
176 %j_bitcast = bitcast fp128 %j to i128
177 %1 = icmp eq i128 %h, %j_bitcast
178 %2 = zext i1 %1 to i32
179 ret i32 %2
180 }
181
182 define i32 @caller_large_scalars_exhausted_regs() nounwind {
183 ; RV32I-LABEL: caller_large_scalars_exhausted_regs:
184 ; RV32I: # %bb.0:
185 ; RV32I-NEXT: addi sp, sp, -64
186 ; RV32I-NEXT: sw ra, 60(sp)
187 ; RV32I-NEXT: sw s0, 56(sp)
188 ; RV32I-NEXT: addi s0, sp, 64
189 ; RV32I-NEXT: addi a0, s0, -48
190 ; RV32I-NEXT: sw a0, 4(sp)
191 ; RV32I-NEXT: addi a0, zero, 9
192 ; RV32I-NEXT: sw a0, 0(sp)
193 ; RV32I-NEXT: sw zero, -40(s0)
194 ; RV32I-NEXT: sw zero, -44(s0)
195 ; RV32I-NEXT: sw zero, -48(s0)
196 ; RV32I-NEXT: sw zero, -12(s0)
197 ; RV32I-NEXT: sw zero, -16(s0)
198 ; RV32I-NEXT: sw zero, -20(s0)
199 ; RV32I-NEXT: addi a0, zero, 8
200 ; RV32I-NEXT: sw a0, -24(s0)
201 ; RV32I-NEXT: lui a0, 524272
202 ; RV32I-NEXT: addi a0, a0, 0
203 ; RV32I-NEXT: sw a0, -36(s0)
204 ; RV32I-NEXT: lui a0, %hi(callee_large_scalars_exhausted_regs)
205 ; RV32I-NEXT: addi t0, a0, %lo(callee_large_scalars_exhausted_regs)
206 ; RV32I-NEXT: addi a0, zero, 1
207 ; RV32I-NEXT: addi a1, zero, 2
208 ; RV32I-NEXT: addi a2, zero, 3
209 ; RV32I-NEXT: addi a3, zero, 4
210 ; RV32I-NEXT: addi a4, zero, 5
211 ; RV32I-NEXT: addi a5, zero, 6
212 ; RV32I-NEXT: addi a6, zero, 7
213 ; RV32I-NEXT: addi a7, s0, -24
214 ; RV32I-NEXT: jalr ra, t0, 0
215 ; RV32I-NEXT: lw s0, 56(sp)
216 ; RV32I-NEXT: lw ra, 60(sp)
217 ; RV32I-NEXT: addi sp, sp, 64
218 ; RV32I-NEXT: jalr zero, ra, 0
219 %1 = call i32 @callee_large_scalars_exhausted_regs(
220 i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i128 8, i32 9,
221 fp128 0xL00000000000000007FFF000000000000)
222 ret i32 %1
223 }
224
225 ; Ensure that libcalls generated in the middle-end obey the calling convention
226
227 define i32 @caller_mixed_scalar_libcalls(i64 %a) nounwind {
228 ; RV32I-LABEL: caller_mixed_scalar_libcalls:
229 ; RV32I: # %bb.0:
230 ; RV32I-NEXT: addi sp, sp, -32
231 ; RV32I-NEXT: sw ra, 28(sp)
232 ; RV32I-NEXT: sw s0, 24(sp)
233 ; RV32I-NEXT: addi s0, sp, 32
234 ; RV32I-NEXT: addi a2, a1, 0
235 ; RV32I-NEXT: addi a1, a0, 0
236 ; RV32I-NEXT: lui a0, %hi(__floatditf)
237 ; RV32I-NEXT: addi a3, a0, %lo(__floatditf)
238 ; RV32I-NEXT: addi a0, s0, -24
239 ; RV32I-NEXT: jalr ra, a3, 0
240 ; RV32I-NEXT: lw a0, -24(s0)
241 ; RV32I-NEXT: lw s0, 24(sp)
242 ; RV32I-NEXT: lw ra, 28(sp)
243 ; RV32I-NEXT: addi sp, sp, 32
244 ; RV32I-NEXT: jalr zero, ra, 0
245 %1 = sitofp i64 %a to fp128
246 %2 = bitcast fp128 %1 to i128
247 %3 = trunc i128 %2 to i32
248 ret i32 %3
249 }
250
251 ; Check that the stack is used once the GPRs are exhausted
252
253 define i32 @callee_many_scalars(i8 %a, i16 %b, i32 %c, i64 %d, i32 %e, i32 %f, i64 %g, i32 %h) nounwind {
254 ; RV32I-LABEL: callee_many_scalars:
255 ; RV32I: # %bb.0:
256 ; RV32I-NEXT: addi sp, sp, -16
257 ; RV32I-NEXT: sw ra, 12(sp)
258 ; RV32I-NEXT: sw s0, 8(sp)
259 ; RV32I-NEXT: addi s0, sp, 16
260 ; RV32I-NEXT: lw t0, 0(s0)
261 ; RV32I-NEXT: xor a4, a4, t0
262 ; RV32I-NEXT: xor a3, a3, a7
263 ; RV32I-NEXT: or a3, a3, a4
264 ; RV32I-NEXT: xor a3, a3, zero
265 ; RV32I-NEXT: lui a4, 16
266 ; RV32I-NEXT: addi a4, a4, -1
267 ; RV32I-NEXT: and a1, a1, a4
268 ; RV32I-NEXT: andi a0, a0, 255
269 ; RV32I-NEXT: add a0, a0, a1
270 ; RV32I-NEXT: add a0, a0, a2
271 ; RV32I-NEXT: sltiu a1, a3, 1
272 ; RV32I-NEXT: add a0, a1, a0
273 ; RV32I-NEXT: add a0, a0, a5
274 ; RV32I-NEXT: add a0, a0, a6
275 ; RV32I-NEXT: lw a1, 4(s0)
276 ; RV32I-NEXT: add a0, a0, a1
277 ; RV32I-NEXT: lw s0, 8(sp)
278 ; RV32I-NEXT: lw ra, 12(sp)
279 ; RV32I-NEXT: addi sp, sp, 16
280 ; RV32I-NEXT: jalr zero, ra, 0
281 %a_ext = zext i8 %a to i32
282 %b_ext = zext i16 %b to i32
283 %1 = add i32 %a_ext, %b_ext
284 %2 = add i32 %1, %c
285 %3 = icmp eq i64 %d, %g
286 %4 = zext i1 %3 to i32
287 %5 = add i32 %4, %2
288 %6 = add i32 %5, %e
289 %7 = add i32 %6, %f
290 %8 = add i32 %7, %h
291 ret i32 %8
292 }
293
294 define i32 @caller_many_scalars() nounwind {
295 ; RV32I-LABEL: caller_many_scalars:
296 ; RV32I: # %bb.0:
297 ; RV32I-NEXT: addi sp, sp, -32
298 ; RV32I-NEXT: sw ra, 28(sp)
299 ; RV32I-NEXT: sw s0, 24(sp)
300 ; RV32I-NEXT: addi s0, sp, 32
301 ; RV32I-NEXT: addi a0, zero, 8
302 ; RV32I-NEXT: sw a0, 4(sp)
303 ; RV32I-NEXT: sw zero, 0(sp)
304 ; RV32I-NEXT: lui a0, %hi(callee_many_scalars)
305 ; RV32I-NEXT: addi t0, a0, %lo(callee_many_scalars)
306 ; RV32I-NEXT: addi a0, zero, 1
307 ; RV32I-NEXT: addi a1, zero, 2
308 ; RV32I-NEXT: addi a2, zero, 3
309 ; RV32I-NEXT: addi a3, zero, 4
310 ; RV32I-NEXT: addi a5, zero, 5
311 ; RV32I-NEXT: addi a6, zero, 6
312 ; RV32I-NEXT: addi a7, zero, 7
313 ; RV32I-NEXT: addi a4, zero, 0
314 ; RV32I-NEXT: jalr ra, t0, 0
315 ; RV32I-NEXT: lw s0, 24(sp)
316 ; RV32I-NEXT: lw ra, 28(sp)
317 ; RV32I-NEXT: addi sp, sp, 32
318 ; RV32I-NEXT: jalr zero, ra, 0
319 %1 = call i32 @callee_many_scalars(i8 1, i16 2, i32 3, i64 4, i32 5, i32 6, i64 7, i32 8)
320 ret i32 %1
321 }
322
323 ; Check passing of coerced integer arrays
324
325 %struct.small = type { i32, i32* }
326
327 define i32 @callee_small_coerced_struct([2 x i32] %a.coerce) nounwind {
328 ; RV32I-LABEL: callee_small_coerced_struct:
329 ; RV32I: # %bb.0:
330 ; RV32I-NEXT: addi sp, sp, -16
331 ; RV32I-NEXT: sw ra, 12(sp)
332 ; RV32I-NEXT: sw s0, 8(sp)
333 ; RV32I-NEXT: addi s0, sp, 16
334 ; RV32I-NEXT: xor a0, a0, a1
335 ; RV32I-NEXT: sltiu a0, a0, 1
336 ; RV32I-NEXT: lw s0, 8(sp)
337 ; RV32I-NEXT: lw ra, 12(sp)
338 ; RV32I-NEXT: addi sp, sp, 16
339 ; RV32I-NEXT: jalr zero, ra, 0
340 %1 = extractvalue [2 x i32] %a.coerce, 0
341 %2 = extractvalue [2 x i32] %a.coerce, 1
342 %3 = icmp eq i32 %1, %2
343 %4 = zext i1 %3 to i32
344 ret i32 %4
345 }
346
347 define i32 @caller_small_coerced_struct() nounwind {
348 ; RV32I-LABEL: caller_small_coerced_struct:
349 ; RV32I: # %bb.0:
350 ; RV32I-NEXT: addi sp, sp, -16
351 ; RV32I-NEXT: sw ra, 12(sp)
352 ; RV32I-NEXT: sw s0, 8(sp)
353 ; RV32I-NEXT: addi s0, sp, 16
354 ; RV32I-NEXT: lui a0, %hi(callee_small_coerced_struct)
355 ; RV32I-NEXT: addi a2, a0, %lo(callee_small_coerced_struct)
356 ; RV32I-NEXT: addi a0, zero, 1
357 ; RV32I-NEXT: addi a1, zero, 2
358 ; RV32I-NEXT: jalr ra, a2, 0
359 ; RV32I-NEXT: lw s0, 8(sp)
360 ; RV32I-NEXT: lw ra, 12(sp)
361 ; RV32I-NEXT: addi sp, sp, 16
362 ; RV32I-NEXT: jalr zero, ra, 0
363 %1 = call i32 @callee_small_coerced_struct([2 x i32] [i32 1, i32 2])
364 ret i32 %1
365 }
366
367 ; Check large struct arguments, which are passed byval
368
369 %struct.large = type { i32, i32, i32, i32 }
370
371 define i32 @callee_large_struct(%struct.large* byval align 4 %a) nounwind {
372 ; RV32I-LABEL: callee_large_struct:
373 ; RV32I: # %bb.0:
374 ; RV32I-NEXT: addi sp, sp, -16
375 ; RV32I-NEXT: sw ra, 12(sp)
376 ; RV32I-NEXT: sw s0, 8(sp)
377 ; RV32I-NEXT: addi s0, sp, 16
378 ; RV32I-NEXT: lw a1, 12(a0)
379 ; RV32I-NEXT: lw a0, 0(a0)
380 ; RV32I-NEXT: add a0, a0, a1
381 ; RV32I-NEXT: lw s0, 8(sp)
382 ; RV32I-NEXT: lw ra, 12(sp)
383 ; RV32I-NEXT: addi sp, sp, 16
384 ; RV32I-NEXT: jalr zero, ra, 0
385 %1 = getelementptr inbounds %struct.large, %struct.large* %a, i32 0, i32 0
386 %2 = getelementptr inbounds %struct.large, %struct.large* %a, i32 0, i32 3
387 %3 = load i32, i32* %1
388 %4 = load i32, i32* %2
389 %5 = add i32 %3, %4
390 ret i32 %5
391 }
392
393 define i32 @caller_large_struct() nounwind {
394 ; RV32I-LABEL: caller_large_struct:
395 ; RV32I: # %bb.0:
396 ; RV32I-NEXT: addi sp, sp, -48
397 ; RV32I-NEXT: sw ra, 44(sp)
398 ; RV32I-NEXT: sw s0, 40(sp)
399 ; RV32I-NEXT: addi s0, sp, 48
400 ; RV32I-NEXT: addi a0, zero, 1
401 ; RV32I-NEXT: sw a0, -24(s0)
402 ; RV32I-NEXT: sw a0, -40(s0)
403 ; RV32I-NEXT: addi a0, zero, 2
404 ; RV32I-NEXT: sw a0, -20(s0)
405 ; RV32I-NEXT: sw a0, -36(s0)
406 ; RV32I-NEXT: addi a0, zero, 3
407 ; RV32I-NEXT: sw a0, -16(s0)
408 ; RV32I-NEXT: sw a0, -32(s0)
409 ; RV32I-NEXT: addi a0, zero, 4
410 ; RV32I-NEXT: sw a0, -12(s0)
411 ; RV32I-NEXT: sw a0, -28(s0)
412 ; RV32I-NEXT: lui a0, %hi(callee_large_struct)
413 ; RV32I-NEXT: addi a1, a0, %lo(callee_large_struct)
414 ; RV32I-NEXT: addi a0, s0, -40
415 ; RV32I-NEXT: jalr ra, a1, 0
416 ; RV32I-NEXT: lw s0, 40(sp)
417 ; RV32I-NEXT: lw ra, 44(sp)
418 ; RV32I-NEXT: addi sp, sp, 48
419 ; RV32I-NEXT: jalr zero, ra, 0
420 %ls = alloca %struct.large, align 4
421 %1 = bitcast %struct.large* %ls to i8*
422 %a = getelementptr inbounds %struct.large, %struct.large* %ls, i32 0, i32 0
423 store i32 1, i32* %a
424 %b = getelementptr inbounds %struct.large, %struct.large* %ls, i32 0, i32 1
425 store i32 2, i32* %b
426 %c = getelementptr inbounds %struct.large, %struct.large* %ls, i32 0, i32 2
427 store i32 3, i32* %c
428 %d = getelementptr inbounds %struct.large, %struct.large* %ls, i32 0, i32 3
429 store i32 4, i32* %d
430 %2 = call i32 @callee_large_struct(%struct.large* byval align 4 %ls)
431 ret i32 %2
432 }
433
434 ; Check 2x*xlen values are aligned appropriately when passed on the stack
435 ; Must keep define on a single line due to an update_llc_test_checks.py limitation
436 define i32 @callee_aligned_stack(i32 %a, i32 %b, fp128 %c, i32 %d, i32 %e, i64 %f, i32 %g, i32 %h, double %i, i32 %j, [2 x i32] %k) nounwind {
437 ; The double should be 8-byte aligned on the stack, but the two-element array
438 ; should only be 4-byte aligned
439 ; RV32I-LABEL: callee_aligned_stack:
440 ; RV32I: # %bb.0:
441 ; RV32I-NEXT: addi sp, sp, -16
442 ; RV32I-NEXT: sw ra, 12(sp)
443 ; RV32I-NEXT: sw s0, 8(sp)
444 ; RV32I-NEXT: addi s0, sp, 16
445 ; RV32I-NEXT: lw a0, 0(a2)
446 ; RV32I-NEXT: add a0, a0, a7
447 ; RV32I-NEXT: lw a1, 0(s0)
448 ; RV32I-NEXT: add a0, a0, a1
449 ; RV32I-NEXT: lw a1, 8(s0)
450 ; RV32I-NEXT: add a0, a0, a1
451 ; RV32I-NEXT: lw a1, 16(s0)
452 ; RV32I-NEXT: add a0, a0, a1
453 ; RV32I-NEXT: lw a1, 20(s0)
454 ; RV32I-NEXT: add a0, a0, a1
455 ; RV32I-NEXT: lw s0, 8(sp)
456 ; RV32I-NEXT: lw ra, 12(sp)
457 ; RV32I-NEXT: addi sp, sp, 16
458 ; RV32I-NEXT: jalr zero, ra, 0
459 %1 = bitcast fp128 %c to i128
460 %2 = trunc i128 %1 to i32
461 %3 = add i32 %2, %g
462 %4 = add i32 %3, %h
463 %5 = bitcast double %i to i64
464 %6 = trunc i64 %5 to i32
465 %7 = add i32 %4, %6
466 %8 = add i32 %7, %j
467 %9 = extractvalue [2 x i32] %k, 0
468 %10 = add i32 %8, %9
469 ret i32 %10
470 }
471
472 define void @caller_aligned_stack() nounwind {
473 ; The double should be 8-byte aligned on the stack, but the two-element array
474 ; should only be 4-byte aligned
475 ; RV32I-LABEL: caller_aligned_stack:
476 ; RV32I: # %bb.0:
477 ; RV32I-NEXT: addi sp, sp, -64
478 ; RV32I-NEXT: sw ra, 60(sp)
479 ; RV32I-NEXT: sw s0, 56(sp)
480 ; RV32I-NEXT: addi s0, sp, 64
481 ; RV32I-NEXT: addi a0, zero, 18
482 ; RV32I-NEXT: sw a0, 24(sp)
483 ; RV32I-NEXT: addi a0, zero, 17
484 ; RV32I-NEXT: sw a0, 20(sp)
485 ; RV32I-NEXT: addi a0, zero, 16
486 ; RV32I-NEXT: sw a0, 16(sp)
487 ; RV32I-NEXT: lui a0, 262236
488 ; RV32I-NEXT: addi a0, a0, 655
489 ; RV32I-NEXT: sw a0, 12(sp)
490 ; RV32I-NEXT: lui a0, 377487
491 ; RV32I-NEXT: addi a0, a0, 1475
492 ; RV32I-NEXT: sw a0, 8(sp)
493 ; RV32I-NEXT: addi a0, zero, 15
494 ; RV32I-NEXT: sw a0, 0(sp)
495 ; RV32I-NEXT: lui a0, 262153
496 ; RV32I-NEXT: addi a0, a0, 491
497 ; RV32I-NEXT: sw a0, -20(s0)
498 ; RV32I-NEXT: lui a0, 545260
499 ; RV32I-NEXT: addi a0, a0, -1967
500 ; RV32I-NEXT: sw a0, -24(s0)
501 ; RV32I-NEXT: lui a0, 964690
502 ; RV32I-NEXT: addi a0, a0, -328
503 ; RV32I-NEXT: sw a0, -28(s0)
504 ; RV32I-NEXT: lui a0, 335544
505 ; RV32I-NEXT: addi a0, a0, 1311
506 ; RV32I-NEXT: sw a0, -32(s0)
507 ; RV32I-NEXT: lui a0, 688509
508 ; RV32I-NEXT: addi a5, a0, -2048
509 ; RV32I-NEXT: lui a0, %hi(callee_aligned_stack)
510 ; RV32I-NEXT: addi t0, a0, %lo(callee_aligned_stack)
511 ; RV32I-NEXT: addi a0, zero, 1
512 ; RV32I-NEXT: addi a1, zero, 11
513 ; RV32I-NEXT: addi a2, s0, -32
514 ; RV32I-NEXT: addi a3, zero, 12
515 ; RV32I-NEXT: addi a4, zero, 13
516 ; RV32I-NEXT: addi a6, zero, 4
517 ; RV32I-NEXT: addi a7, zero, 14
518 ; RV32I-NEXT: jalr ra, t0, 0
519 ; RV32I-NEXT: lw s0, 56(sp)
520 ; RV32I-NEXT: lw ra, 60(sp)
521 ; RV32I-NEXT: addi sp, sp, 64
522 ; RV32I-NEXT: jalr zero, ra, 0
523 %1 = call i32 @callee_aligned_stack(i32 1, i32 11,
524 fp128 0xLEB851EB851EB851F400091EB851EB851, i32 12, i32 13,
525 i64 20000000000, i32 14, i32 15, double 2.720000e+00, i32 16,
526 [2 x i32] [i32 17, i32 18])
527 ret void
528 }
529
530 ; Check return of 2x xlen scalars
531
532 define i64 @callee_small_scalar_ret() nounwind {
533 ; RV32I-LABEL: callee_small_scalar_ret:
534 ; RV32I: # %bb.0:
535 ; RV32I-NEXT: addi sp, sp, -16
536 ; RV32I-NEXT: sw ra, 12(sp)
537 ; RV32I-NEXT: sw s0, 8(sp)
538 ; RV32I-NEXT: addi s0, sp, 16
539 ; RV32I-NEXT: lui a0, 466866
540 ; RV32I-NEXT: addi a0, a0, 1677
541 ; RV32I-NEXT: addi a1, zero, 287
542 ; RV32I-NEXT: lw s0, 8(sp)
543 ; RV32I-NEXT: lw ra, 12(sp)
544 ; RV32I-NEXT: addi sp, sp, 16
545 ; RV32I-NEXT: jalr zero, ra, 0
546 ret i64 1234567898765
547 }
548
549 define i32 @caller_small_scalar_ret() nounwind {
550 ; RV32I-LABEL: caller_small_scalar_ret:
551 ; RV32I: # %bb.0:
552 ; RV32I-NEXT: addi sp, sp, -16
553 ; RV32I-NEXT: sw ra, 12(sp)
554 ; RV32I-NEXT: sw s0, 8(sp)
555 ; RV32I-NEXT: addi s0, sp, 16
556 ; RV32I-NEXT: lui a0, %hi(callee_small_scalar_ret)
557 ; RV32I-NEXT: addi a0, a0, %lo(callee_small_scalar_ret)
558 ; RV32I-NEXT: jalr ra, a0, 0
559 ; RV32I-NEXT: lui a2, 56
560 ; RV32I-NEXT: addi a2, a2, 580
561 ; RV32I-NEXT: xor a1, a1, a2
562 ; RV32I-NEXT: lui a2, 200614
563 ; RV32I-NEXT: addi a2, a2, 647
564 ; RV32I-NEXT: xor a0, a0, a2
565 ; RV32I-NEXT: or a0, a0, a1
566 ; RV32I-NEXT: xor a0, a0, zero
567 ; RV32I-NEXT: sltiu a0, a0, 1
568 ; RV32I-NEXT: lw s0, 8(sp)
569 ; RV32I-NEXT: lw ra, 12(sp)
570 ; RV32I-NEXT: addi sp, sp, 16
571 ; RV32I-NEXT: jalr zero, ra, 0
572 %1 = call i64 @callee_small_scalar_ret()
573 %2 = icmp eq i64 987654321234567, %1
574 %3 = zext i1 %2 to i32
575 ret i32 %3
576 }
577
578 ; Check return of 2x xlen structs
579
580 define %struct.small @callee_small_struct_ret() nounwind {
581 ; RV32I-LABEL: callee_small_struct_ret:
582 ; RV32I: # %bb.0:
583 ; RV32I-NEXT: addi sp, sp, -16
584 ; RV32I-NEXT: sw ra, 12(sp)
585 ; RV32I-NEXT: sw s0, 8(sp)
586 ; RV32I-NEXT: addi s0, sp, 16
587 ; RV32I-NEXT: addi a0, zero, 1
588 ; RV32I-NEXT: addi a1, zero, 0
589 ; RV32I-NEXT: lw s0, 8(sp)
590 ; RV32I-NEXT: lw ra, 12(sp)
591 ; RV32I-NEXT: addi sp, sp, 16
592 ; RV32I-NEXT: jalr zero, ra, 0
593 ret %struct.small { i32 1, i32* null }
594 }
595
596 define i32 @caller_small_struct_ret() nounwind {
597 ; RV32I-LABEL: caller_small_struct_ret:
598 ; RV32I: # %bb.0:
599 ; RV32I-NEXT: addi sp, sp, -16
600 ; RV32I-NEXT: sw ra, 12(sp)
601 ; RV32I-NEXT: sw s0, 8(sp)
602 ; RV32I-NEXT: addi s0, sp, 16
603 ; RV32I-NEXT: lui a0, %hi(callee_small_struct_ret)
604 ; RV32I-NEXT: addi a0, a0, %lo(callee_small_struct_ret)
605 ; RV32I-NEXT: jalr ra, a0, 0
606 ; RV32I-NEXT: add a0, a0, a1
607 ; RV32I-NEXT: lw s0, 8(sp)
608 ; RV32I-NEXT: lw ra, 12(sp)
609 ; RV32I-NEXT: addi sp, sp, 16
610 ; RV32I-NEXT: jalr zero, ra, 0
611 %1 = call %struct.small @callee_small_struct_ret()
612 %2 = extractvalue %struct.small %1, 0
613 %3 = extractvalue %struct.small %1, 1
614 %4 = ptrtoint i32* %3 to i32
615 %5 = add i32 %2, %4
616 ret i32 %5
617 }
618
619 ; Check return of >2x xlen scalars
620
621 define fp128 @callee_large_scalar_ret() nounwind {
622 ; RV32I-LABEL: callee_large_scalar_ret:
623 ; RV32I: # %bb.0:
624 ; RV32I-NEXT: addi sp, sp, -16
625 ; RV32I-NEXT: sw ra, 12(sp)
626 ; RV32I-NEXT: sw s0, 8(sp)
627 ; RV32I-NEXT: addi s0, sp, 16
628 ; RV32I-NEXT: lui a1, 524272
629 ; RV32I-NEXT: addi a1, a1, 0
630 ; RV32I-NEXT: sw a1, 12(a0)
631 ; RV32I-NEXT: sw zero, 8(a0)
632 ; RV32I-NEXT: sw zero, 4(a0)
633 ; RV32I-NEXT: sw zero, 0(a0)
634 ; RV32I-NEXT: lw s0, 8(sp)
635 ; RV32I-NEXT: lw ra, 12(sp)
636 ; RV32I-NEXT: addi sp, sp, 16
637 ; RV32I-NEXT: jalr zero, ra, 0
638 ret fp128 0xL00000000000000007FFF000000000000
639 }
640
641 define void @caller_large_scalar_ret() nounwind {
642 ; RV32I-LABEL: caller_large_scalar_ret:
643 ; RV32I: # %bb.0:
644 ; RV32I-NEXT: addi sp, sp, -32
645 ; RV32I-NEXT: sw ra, 28(sp)
646 ; RV32I-NEXT: sw s0, 24(sp)
647 ; RV32I-NEXT: addi s0, sp, 32
648 ; RV32I-NEXT: lui a0, %hi(callee_large_scalar_ret)
649 ; RV32I-NEXT: addi a1, a0, %lo(callee_large_scalar_ret)
650 ; RV32I-NEXT: addi a0, s0, -32
651 ; RV32I-NEXT: jalr ra, a1, 0
652 ; RV32I-NEXT: lw s0, 24(sp)
653 ; RV32I-NEXT: lw ra, 28(sp)
654 ; RV32I-NEXT: addi sp, sp, 32
655 ; RV32I-NEXT: jalr zero, ra, 0
656 %1 = call fp128 @callee_large_scalar_ret()
657 ret void
658 }
659
660 ; Check return of >2x xlen structs
661
662 define void @callee_large_struct_ret(%struct.large* noalias sret %agg.result) nounwind {
663 ; RV32I-LABEL: callee_large_struct_ret:
664 ; RV32I: # %bb.0:
665 ; RV32I-NEXT: addi sp, sp, -16
666 ; RV32I-NEXT: sw ra, 12(sp)
667 ; RV32I-NEXT: sw s0, 8(sp)
668 ; RV32I-NEXT: addi s0, sp, 16
669 ; RV32I-NEXT: addi a1, zero, 2
670 ; RV32I-NEXT: sw a1, 4(a0)
671 ; RV32I-NEXT: addi a1, zero, 1
672 ; RV32I-NEXT: sw a1, 0(a0)
673 ; RV32I-NEXT: addi a1, zero, 3
674 ; RV32I-NEXT: sw a1, 8(a0)
675 ; RV32I-NEXT: addi a1, zero, 4
676 ; RV32I-NEXT: sw a1, 12(a0)
677 ; RV32I-NEXT: lw s0, 8(sp)
678 ; RV32I-NEXT: lw ra, 12(sp)
679 ; RV32I-NEXT: addi sp, sp, 16
680 ; RV32I-NEXT: jalr zero, ra, 0
681 %a = getelementptr inbounds %struct.large, %struct.large* %agg.result, i32 0, i32 0
682 store i32 1, i32* %a, align 4
683 %b = getelementptr inbounds %struct.large, %struct.large* %agg.result, i32 0, i32 1
684 store i32 2, i32* %b, align 4
685 %c = getelementptr inbounds %struct.large, %struct.large* %agg.result, i32 0, i32 2
686 store i32 3, i32* %c, align 4
687 %d = getelementptr inbounds %struct.large, %struct.large* %agg.result, i32 0, i32 3
688 store i32 4, i32* %d, align 4
689 ret void
690 }
691
692 define i32 @caller_large_struct_ret() nounwind {
693 ; RV32I-LABEL: caller_large_struct_ret:
694 ; RV32I: # %bb.0:
695 ; RV32I-NEXT: addi sp, sp, -32
696 ; RV32I-NEXT: sw ra, 28(sp)
697 ; RV32I-NEXT: sw s0, 24(sp)
698 ; RV32I-NEXT: addi s0, sp, 32
699 ; RV32I-NEXT: lui a0, %hi(callee_large_struct_ret)
700 ; RV32I-NEXT: addi a1, a0, %lo(callee_large_struct_ret)
701 ; RV32I-NEXT: addi a0, s0, -24
702 ; RV32I-NEXT: jalr ra, a1, 0
703 ; RV32I-NEXT: lw a0, -12(s0)
704 ; RV32I-NEXT: lw a1, -24(s0)
705 ; RV32I-NEXT: add a0, a1, a0
706 ; RV32I-NEXT: lw s0, 24(sp)
707 ; RV32I-NEXT: lw ra, 28(sp)
708 ; RV32I-NEXT: addi sp, sp, 32
709 ; RV32I-NEXT: jalr zero, ra, 0
710 %1 = alloca %struct.large
711 call void @callee_large_struct_ret(%struct.large* sret %1)
712 %2 = getelementptr inbounds %struct.large, %struct.large* %1, i32 0, i32 0
713 %3 = load i32, i32* %2
714 %4 = getelementptr inbounds %struct.large, %struct.large* %1, i32 0, i32 3
715 %5 = load i32, i32* %4
716 %6 = add i32 %3, %5
717 ret i32 %6
718 }
5151 ; RV32I-NEXT: lw ra, 12(sp)
5252 ; RV32I-NEXT: addi sp, sp, 16
5353 ; RV32I-NEXT: jalr zero, ra, 0
54 %1 = call i32 @defined_function(i32 %a) nounwind
54 %1 = call i32 @defined_function(i32 %a)
5555 ret i32 %1
5656 }
5757
114114 %1 = call fastcc i32 @fastcc_function(i32 %a, i32 %b)
115115 ret i32 %a
116116 }
117
118 declare i32 @external_many_args(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) nounwind
119
120 define i32 @test_call_external_many_args(i32 %a) nounwind {
121 ; RV32I-LABEL: test_call_external_many_args:
122 ; RV32I: # %bb.0:
123 ; RV32I-NEXT: addi sp, sp, -32
124 ; RV32I-NEXT: sw ra, 28(sp)
125 ; RV32I-NEXT: sw s0, 24(sp)
126 ; RV32I-NEXT: sw s1, 20(sp)
127 ; RV32I-NEXT: addi s0, sp, 32
128 ; RV32I-NEXT: addi s1, a0, 0
129 ; RV32I-NEXT: sw s1, 4(sp)
130 ; RV32I-NEXT: sw s1, 0(sp)
131 ; RV32I-NEXT: lui a0, %hi(external_many_args)
132 ; RV32I-NEXT: addi t0, a0, %lo(external_many_args)
133 ; RV32I-NEXT: addi a0, s1, 0
134 ; RV32I-NEXT: addi a1, s1, 0
135 ; RV32I-NEXT: addi a2, s1, 0
136 ; RV32I-NEXT: addi a3, s1, 0
137 ; RV32I-NEXT: addi a4, s1, 0
138 ; RV32I-NEXT: addi a5, s1, 0
139 ; RV32I-NEXT: addi a6, s1, 0
140 ; RV32I-NEXT: addi a7, s1, 0
141 ; RV32I-NEXT: jalr ra, t0, 0
142 ; RV32I-NEXT: addi a0, s1, 0
143 ; RV32I-NEXT: lw s1, 20(sp)
144 ; RV32I-NEXT: lw s0, 24(sp)
145 ; RV32I-NEXT: lw ra, 28(sp)
146 ; RV32I-NEXT: addi sp, sp, 32
147 ; RV32I-NEXT: jalr zero, ra, 0
148 %1 = call i32 @external_many_args(i32 %a, i32 %a, i32 %a, i32 %a, i32 %a,
149 i32 %a, i32 %a, i32 %a, i32 %a, i32 %a)
150 ret i32 %a
151 }
152
153 define i32 @defined_many_args(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 %j) nounwind {
154 ; RV32I-LABEL: defined_many_args:
155 ; RV32I: # %bb.0:
156 ; RV32I-NEXT: addi sp, sp, -16
157 ; RV32I-NEXT: sw ra, 12(sp)
158 ; RV32I-NEXT: sw s0, 8(sp)
159 ; RV32I-NEXT: addi s0, sp, 16
160 ; RV32I-NEXT: lw a0, 4(s0)
161 ; RV32I-NEXT: addi a0, a0, 1
162 ; RV32I-NEXT: lw s0, 8(sp)
163 ; RV32I-NEXT: lw ra, 12(sp)
164 ; RV32I-NEXT: addi sp, sp, 16
165 ; RV32I-NEXT: jalr zero, ra, 0
166 %added = add i32 %j, 1
167 ret i32 %added
168 }
169
170 define i32 @test_call_defined_many_args(i32 %a) nounwind {
171 ; RV32I-LABEL: test_call_defined_many_args:
172 ; RV32I: # %bb.0:
173 ; RV32I-NEXT: addi sp, sp, -32
174 ; RV32I-NEXT: sw ra, 28(sp)
175 ; RV32I-NEXT: sw s0, 24(sp)
176 ; RV32I-NEXT: addi s0, sp, 32
177 ; RV32I-NEXT: sw a0, 4(sp)
178 ; RV32I-NEXT: sw a0, 0(sp)
179 ; RV32I-NEXT: lui a1, %hi(defined_many_args)
180 ; RV32I-NEXT: addi t0, a1, %lo(defined_many_args)
181 ; RV32I-NEXT: addi a1, a0, 0
182 ; RV32I-NEXT: addi a2, a0, 0
183 ; RV32I-NEXT: addi a3, a0, 0
184 ; RV32I-NEXT: addi a4, a0, 0
185 ; RV32I-NEXT: addi a5, a0, 0
186 ; RV32I-NEXT: addi a6, a0, 0
187 ; RV32I-NEXT: addi a7, a0, 0
188 ; RV32I-NEXT: jalr ra, t0, 0
189 ; RV32I-NEXT: lw s0, 24(sp)
190 ; RV32I-NEXT: lw ra, 28(sp)
191 ; RV32I-NEXT: addi sp, sp, 32
192 ; RV32I-NEXT: jalr zero, ra, 0
193 %1 = call i32 @defined_many_args(i32 %a, i32 %a, i32 %a, i32 %a, i32 %a,
194 i32 %a, i32 %a, i32 %a, i32 %a, i32 %a)
195 ret i32 %1
196 }
0 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
1 ; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
2 ; RUN: | FileCheck -check-prefix=RV32I %s
3
4 @x = local_unnamed_addr global fp128 0xL00000000000000007FFF000000000000, align 16
5 @y = local_unnamed_addr global fp128 0xL00000000000000007FFF000000000000, align 16
6
7 ; Besides anything else, these tests help verify that libcall ABI lowering
8 ; works correctly
9
10 define i32 @test_load_and_cmp() nounwind {
11 ; RV32I-LABEL: test_load_and_cmp:
12 ; RV32I: # %bb.0:
13 ; RV32I-NEXT: addi sp, sp, -48
14 ; RV32I-NEXT: sw ra, 44(sp)
15 ; RV32I-NEXT: sw s0, 40(sp)
16 ; RV32I-NEXT: addi s0, sp, 48
17 ; RV32I-NEXT: lui a0, %hi(y+12)
18 ; RV32I-NEXT: addi a0, a0, %lo(y+12)
19 ; RV32I-NEXT: lw a0, 0(a0)
20 ; RV32I-NEXT: sw a0, -28(s0)
21 ; RV32I-NEXT: lui a0, %hi(y+8)
22 ; RV32I-NEXT: addi a0, a0, %lo(y+8)
23 ; RV32I-NEXT: lw a0, 0(a0)
24 ; RV32I-NEXT: sw a0, -32(s0)
25 ; RV32I-NEXT: lui a0, %hi(y+4)
26 ; RV32I-NEXT: addi a0, a0, %lo(y+4)
27 ; RV32I-NEXT: lw a0, 0(a0)
28 ; RV32I-NEXT: sw a0, -36(s0)
29 ; RV32I-NEXT: lui a0, %hi(y)
30 ; RV32I-NEXT: addi a0, a0, %lo(y)
31 ; RV32I-NEXT: lw a0, 0(a0)
32 ; RV32I-NEXT: sw a0, -40(s0)
33 ; RV32I-NEXT: lui a0, %hi(x+12)
34 ; RV32I-NEXT: addi a0, a0, %lo(x+12)
35 ; RV32I-NEXT: lw a0, 0(a0)
36 ; RV32I-NEXT: sw a0, -12(s0)
37 ; RV32I-NEXT: lui a0, %hi(x+8)
38 ; RV32I-NEXT: addi a0, a0, %lo(x+8)
39 ; RV32I-NEXT: lw a0, 0(a0)
40 ; RV32I-NEXT: sw a0, -16(s0)
41 ; RV32I-NEXT: lui a0, %hi(x+4)
42 ; RV32I-NEXT: addi a0, a0, %lo(x+4)
43 ; RV32I-NEXT: lw a0, 0(a0)
44 ; RV32I-NEXT: sw a0, -20(s0)
45 ; RV32I-NEXT: lui a0, %hi(x)
46 ; RV32I-NEXT: addi a0, a0, %lo(x)
47 ; RV32I-NEXT: lw a0, 0(a0)
48 ; RV32I-NEXT: sw a0, -24(s0)
49 ; RV32I-NEXT: lui a0, %hi(__netf2)
50 ; RV32I-NEXT: addi a2, a0, %lo(__netf2)
51 ; RV32I-NEXT: addi a0, s0, -24
52 ; RV32I-NEXT: addi a1, s0, -40
53 ; RV32I-NEXT: jalr ra, a2, 0
54 ; RV32I-NEXT: xor a0, a0, zero
55 ; RV32I-NEXT: sltu a0, zero, a0
56 ; RV32I-NEXT: lw s0, 40(sp)
57 ; RV32I-NEXT: lw ra, 44(sp)
58 ; RV32I-NEXT: addi sp, sp, 48
59 ; RV32I-NEXT: jalr zero, ra, 0
60 %1 = load fp128, fp128* @x, align 16
61 %2 = load fp128, fp128* @y, align 16
62 %cmp = fcmp une fp128 %1, %2
63 %3 = zext i1 %cmp to i32
64 ret i32 %3
65 }
66
67 define i32 @test_add_and_fptosi() nounwind {
68 ; RV32I-LABEL: test_add_and_fptosi:
69 ; RV32I: # %bb.0:
70 ; RV32I-NEXT: addi sp, sp, -80
71 ; RV32I-NEXT: sw ra, 76(sp)
72 ; RV32I-NEXT: sw s0, 72(sp)
73 ; RV32I-NEXT: addi s0, sp, 80
74 ; RV32I-NEXT: lui a0, %hi(y+12)
75 ; RV32I-NEXT: addi a0, a0, %lo(y+12)
76 ; RV32I-NEXT: lw a0, 0(a0)
77 ; RV32I-NEXT: sw a0, -44(s0)
78 ; RV32I-NEXT: lui a0, %hi(y+8)
79 ; RV32I-NEXT: addi a0, a0, %lo(y+8)
80 ; RV32I-NEXT: lw a0, 0(a0)
81 ; RV32I-NEXT: sw a0, -48(s0)
82 ; RV32I-NEXT: lui a0, %hi(y+4)
83 ; RV32I-NEXT: addi a0, a0, %lo(y+4)
84 ; RV32I-NEXT: lw a0, 0(a0)
85 ; RV32I-NEXT: sw a0, -52(s0)
86 ; RV32I-NEXT: lui a0, %hi(y)
87 ; RV32I-NEXT: addi a0, a0, %lo(y)
88 ; RV32I-NEXT: lw a0, 0(a0)
89 ; RV32I-NEXT: sw a0, -56(s0)
90 ; RV32I-NEXT: lui a0, %hi(x+12)
91 ; RV32I-NEXT: addi a0, a0, %lo(x+12)
92 ; RV32I-NEXT: lw a0, 0(a0)
93 ; RV32I-NEXT: sw a0, -28(s0)
94 ; RV32I-NEXT: lui a0, %hi(x+8)
95 ; RV32I-NEXT: addi a0, a0, %lo(x+8)
96 ; RV32I-NEXT: lw a0, 0(a0)
97 ; RV32I-NEXT: sw a0, -32(s0)
98 ; RV32I-NEXT: lui a0, %hi(x+4)
99 ; RV32I-NEXT: addi a0, a0, %lo(x+4)
100 ; RV32I-NEXT: lw a0, 0(a0)
101 ; RV32I-NEXT: sw a0, -36(s0)
102 ; RV32I-NEXT: lui a0, %hi(x)
103 ; RV32I-NEXT: addi a0, a0, %lo(x)
104 ; RV32I-NEXT: lw a0, 0(a0)
105 ; RV32I-NEXT: sw a0, -40(s0)
106 ; RV32I-NEXT: lui a0, %hi(__addtf3)
107 ; RV32I-NEXT: addi a3, a0, %lo(__addtf3)
108 ; RV32I-NEXT: addi a0, s0, -24
109 ; RV32I-NEXT: addi a1, s0, -40
110 ; RV32I-NEXT: addi a2, s0, -56
111 ; RV32I-NEXT: jalr ra, a3, 0
112 ; RV32I-NEXT: lw a0, -12(s0)
113 ; RV32I-NEXT: sw a0, -60(s0)
114 ; RV32I-NEXT: lw a0, -16(s0)
115 ; RV32I-NEXT: sw a0, -64(s0)
116 ; RV32I-NEXT: lw a0, -20(s0)
117 ; RV32I-NEXT: sw a0, -68(s0)
118 ; RV32I-NEXT: lw a0, -24(s0)
119 ; RV32I-NEXT: sw a0, -72(s0)
120 ; RV32I-NEXT: lui a0, %hi(__fixtfsi)
121 ; RV32I-NEXT: addi a1, a0, %lo(__fixtfsi)
122 ; RV32I-NEXT: addi a0, s0, -72
123 ; RV32I-NEXT: jalr ra, a1, 0
124 ; RV32I-NEXT: lw s0, 72(sp)
125 ; RV32I-NEXT: lw ra, 76(sp)
126 ; RV32I-NEXT: addi sp, sp, 80
127 ; RV32I-NEXT: jalr zero, ra, 0
128 %1 = load fp128, fp128* @x, align 16
129 %2 = load fp128, fp128* @y, align 16
130 %3 = fadd fp128 %1, %2
131 %4 = fptosi fp128 %3 to i32
132 ret i32 %4
133 }