| //===-- RISCVCallingConv.cpp - RISC-V Custom CC Routines ------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file contains the custom routines for the RISC-V Calling Convention. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "RISCVCallingConv.h" | |
| #include "RISCVMachineFunctionInfo.h" | |
| #include "RISCVSubtarget.h" | |
| #include "llvm/IR/DataLayout.h" | |
| #include "llvm/MC/MCRegister.h" | |
| using namespace llvm; | |
| // This does not have the regular `CCAssignFn` signature, it has an extra | |
| // `bool IsRet` parameter. | |
| static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT, | |
| CCValAssign::LocInfo LocInfo, | |
| ISD::ArgFlagsTy ArgFlags, Type *OrigTy, | |
| CCState &State, bool IsRet); | |
| /// Used for assigning arguments with CallingConvention::GHC | |
| static CCAssignFn CC_RISCV_GHC; | |
| /// Used for assigning arguments with CallingConvention::Fast | |
| static CCAssignFn CC_RISCV_FastCC; | |
| bool llvm::CC_RISCV(unsigned ValNo, MVT ValVT, MVT LocVT, | |
| CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, | |
| Type *OrigTy, CCState &State) { | |
| if (State.getCallingConv() == CallingConv::GHC) | |
| return CC_RISCV_GHC(ValNo, ValVT, LocVT, LocInfo, ArgFlags, OrigTy, State); | |
| if (State.getCallingConv() == CallingConv::Fast) | |
| return CC_RISCV_FastCC(ValNo, ValVT, LocVT, LocInfo, ArgFlags, OrigTy, | |
| State); | |
| // For all other cases, use the standard calling convention | |
| return CC_RISCV_Impl(ValNo, ValVT, LocVT, LocInfo, ArgFlags, OrigTy, State, | |
| /*IsRet=*/false); | |
| } | |
| bool llvm::RetCC_RISCV(unsigned ValNo, MVT ValVT, MVT LocVT, | |
| CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, | |
| Type *OrigTy, CCState &State) { | |
| // Always use the standard calling convention. | |
| return CC_RISCV_Impl(ValNo, ValVT, LocVT, LocInfo, ArgFlags, OrigTy, State, | |
| /*IsRet=*/true); | |
| } | |
| // Calling Convention Implementation. | |
| // The expectations for frontend ABI lowering vary from target to target. | |
| // Ideally, an LLVM frontend would be able to avoid worrying about many ABI | |
| // details, but this is a longer term goal. For now, we simply try to keep the | |
| // role of the frontend as simple and well-defined as possible. The rules can | |
| // be summarised as: | |
| // * Never split up large scalar arguments. We handle them here. | |
| // * If a hardfloat calling convention is being used, and the struct may be | |
| // passed in a pair of registers (fp+fp, int+fp), and both registers are | |
| // available, then pass as two separate arguments. If either the GPRs or FPRs | |
| // are exhausted, then pass according to the rule below. | |
| // * If a struct could never be passed in registers or directly in a stack | |
| // slot (as it is larger than 2*XLEN and the floating point rules don't | |
| // apply), then pass it using a pointer with the byval attribute. | |
| // * If a struct is less than 2*XLEN, then coerce to either a two-element | |
| // word-sized array or a 2*XLEN scalar (depending on alignment). | |
| // * The frontend can determine whether a struct is returned by reference or | |
| // not based on its size and fields. If it will be returned by reference, the | |
| // frontend must modify the prototype so a pointer with the sret annotation is | |
| // passed as the first argument. This is not necessary for large scalar | |
| // returns. | |
| // * Struct return values and varargs should be coerced to structs containing | |
| // register-size fields in the same situations they would be for fixed | |
| // arguments. | |
| static const MCPhysReg ArgFPR16s[] = {RISCV::F10_H, RISCV::F11_H, RISCV::F12_H, | |
| RISCV::F13_H, RISCV::F14_H, RISCV::F15_H, | |
| RISCV::F16_H, RISCV::F17_H}; | |
| static const MCPhysReg ArgFPR32s[] = {RISCV::F10_F, RISCV::F11_F, RISCV::F12_F, | |
| RISCV::F13_F, RISCV::F14_F, RISCV::F15_F, | |
| RISCV::F16_F, RISCV::F17_F}; | |
| static const MCPhysReg ArgFPR64s[] = {RISCV::F10_D, RISCV::F11_D, RISCV::F12_D, | |
| RISCV::F13_D, RISCV::F14_D, RISCV::F15_D, | |
| RISCV::F16_D, RISCV::F17_D}; | |
| static const MCPhysReg ArgFPR128s[] = {RISCV::F10_Q, RISCV::F11_Q, RISCV::F12_Q, | |
| RISCV::F13_Q, RISCV::F14_Q, RISCV::F15_Q, | |
| RISCV::F16_Q, RISCV::F17_Q}; | |
| // This is an interim calling convention and it may be changed in the future. | |
| static const MCPhysReg ArgVRs[] = { | |
| RISCV::V8, RISCV::V9, RISCV::V10, RISCV::V11, RISCV::V12, RISCV::V13, | |
| RISCV::V14, RISCV::V15, RISCV::V16, RISCV::V17, RISCV::V18, RISCV::V19, | |
| RISCV::V20, RISCV::V21, RISCV::V22, RISCV::V23}; | |
| static const MCPhysReg ArgVRM2s[] = {RISCV::V8M2, RISCV::V10M2, RISCV::V12M2, | |
| RISCV::V14M2, RISCV::V16M2, RISCV::V18M2, | |
| RISCV::V20M2, RISCV::V22M2}; | |
| static const MCPhysReg ArgVRM4s[] = {RISCV::V8M4, RISCV::V12M4, RISCV::V16M4, | |
| RISCV::V20M4}; | |
| static const MCPhysReg ArgVRM8s[] = {RISCV::V8M8, RISCV::V16M8}; | |
| static const MCPhysReg ArgVRN2M1s[] = { | |
| RISCV::V8_V9, RISCV::V9_V10, RISCV::V10_V11, RISCV::V11_V12, | |
| RISCV::V12_V13, RISCV::V13_V14, RISCV::V14_V15, RISCV::V15_V16, | |
| RISCV::V16_V17, RISCV::V17_V18, RISCV::V18_V19, RISCV::V19_V20, | |
| RISCV::V20_V21, RISCV::V21_V22, RISCV::V22_V23}; | |
| static const MCPhysReg ArgVRN3M1s[] = { | |
| RISCV::V8_V9_V10, RISCV::V9_V10_V11, RISCV::V10_V11_V12, | |
| RISCV::V11_V12_V13, RISCV::V12_V13_V14, RISCV::V13_V14_V15, | |
| RISCV::V14_V15_V16, RISCV::V15_V16_V17, RISCV::V16_V17_V18, | |
| RISCV::V17_V18_V19, RISCV::V18_V19_V20, RISCV::V19_V20_V21, | |
| RISCV::V20_V21_V22, RISCV::V21_V22_V23}; | |
| static const MCPhysReg ArgVRN4M1s[] = { | |
| RISCV::V8_V9_V10_V11, RISCV::V9_V10_V11_V12, RISCV::V10_V11_V12_V13, | |
| RISCV::V11_V12_V13_V14, RISCV::V12_V13_V14_V15, RISCV::V13_V14_V15_V16, | |
| RISCV::V14_V15_V16_V17, RISCV::V15_V16_V17_V18, RISCV::V16_V17_V18_V19, | |
| RISCV::V17_V18_V19_V20, RISCV::V18_V19_V20_V21, RISCV::V19_V20_V21_V22, | |
| RISCV::V20_V21_V22_V23}; | |
| static const MCPhysReg ArgVRN5M1s[] = { | |
| RISCV::V8_V9_V10_V11_V12, RISCV::V9_V10_V11_V12_V13, | |
| RISCV::V10_V11_V12_V13_V14, RISCV::V11_V12_V13_V14_V15, | |
| RISCV::V12_V13_V14_V15_V16, RISCV::V13_V14_V15_V16_V17, | |
| RISCV::V14_V15_V16_V17_V18, RISCV::V15_V16_V17_V18_V19, | |
| RISCV::V16_V17_V18_V19_V20, RISCV::V17_V18_V19_V20_V21, | |
| RISCV::V18_V19_V20_V21_V22, RISCV::V19_V20_V21_V22_V23}; | |
| static const MCPhysReg ArgVRN6M1s[] = { | |
| RISCV::V8_V9_V10_V11_V12_V13, RISCV::V9_V10_V11_V12_V13_V14, | |
| RISCV::V10_V11_V12_V13_V14_V15, RISCV::V11_V12_V13_V14_V15_V16, | |
| RISCV::V12_V13_V14_V15_V16_V17, RISCV::V13_V14_V15_V16_V17_V18, | |
| RISCV::V14_V15_V16_V17_V18_V19, RISCV::V15_V16_V17_V18_V19_V20, | |
| RISCV::V16_V17_V18_V19_V20_V21, RISCV::V17_V18_V19_V20_V21_V22, | |
| RISCV::V18_V19_V20_V21_V22_V23}; | |
| static const MCPhysReg ArgVRN7M1s[] = { | |
| RISCV::V8_V9_V10_V11_V12_V13_V14, RISCV::V9_V10_V11_V12_V13_V14_V15, | |
| RISCV::V10_V11_V12_V13_V14_V15_V16, RISCV::V11_V12_V13_V14_V15_V16_V17, | |
| RISCV::V12_V13_V14_V15_V16_V17_V18, RISCV::V13_V14_V15_V16_V17_V18_V19, | |
| RISCV::V14_V15_V16_V17_V18_V19_V20, RISCV::V15_V16_V17_V18_V19_V20_V21, | |
| RISCV::V16_V17_V18_V19_V20_V21_V22, RISCV::V17_V18_V19_V20_V21_V22_V23}; | |
| static const MCPhysReg ArgVRN8M1s[] = {RISCV::V8_V9_V10_V11_V12_V13_V14_V15, | |
| RISCV::V9_V10_V11_V12_V13_V14_V15_V16, | |
| RISCV::V10_V11_V12_V13_V14_V15_V16_V17, | |
| RISCV::V11_V12_V13_V14_V15_V16_V17_V18, | |
| RISCV::V12_V13_V14_V15_V16_V17_V18_V19, | |
| RISCV::V13_V14_V15_V16_V17_V18_V19_V20, | |
| RISCV::V14_V15_V16_V17_V18_V19_V20_V21, | |
| RISCV::V15_V16_V17_V18_V19_V20_V21_V22, | |
| RISCV::V16_V17_V18_V19_V20_V21_V22_V23}; | |
| static const MCPhysReg ArgVRN2M2s[] = {RISCV::V8M2_V10M2, RISCV::V10M2_V12M2, | |
| RISCV::V12M2_V14M2, RISCV::V14M2_V16M2, | |
| RISCV::V16M2_V18M2, RISCV::V18M2_V20M2, | |
| RISCV::V20M2_V22M2}; | |
| static const MCPhysReg ArgVRN3M2s[] = { | |
| RISCV::V8M2_V10M2_V12M2, RISCV::V10M2_V12M2_V14M2, | |
| RISCV::V12M2_V14M2_V16M2, RISCV::V14M2_V16M2_V18M2, | |
| RISCV::V16M2_V18M2_V20M2, RISCV::V18M2_V20M2_V22M2}; | |
| static const MCPhysReg ArgVRN4M2s[] = { | |
| RISCV::V8M2_V10M2_V12M2_V14M2, RISCV::V10M2_V12M2_V14M2_V16M2, | |
| RISCV::V12M2_V14M2_V16M2_V18M2, RISCV::V14M2_V16M2_V18M2_V20M2, | |
| RISCV::V16M2_V18M2_V20M2_V22M2}; | |
| static const MCPhysReg ArgVRN2M4s[] = {RISCV::V8M4_V12M4, RISCV::V12M4_V16M4, | |
| RISCV::V16M4_V20M4}; | |
| ArrayRef<MCPhysReg> RISCV::getArgGPRs(const RISCVSubtarget &STI) { | |
| RISCVABI::ABI ABI = STI.getTargetABI(); | |
| // The GPRs used for passing arguments in the ILP32* and LP64* ABIs, except | |
| // the ILP32E ABI. | |
| static const MCPhysReg ArgIGPRs[] = {RISCV::X10, RISCV::X11, RISCV::X12, | |
| RISCV::X13, RISCV::X14, RISCV::X15, | |
| RISCV::X16, RISCV::X17}; | |
| // The GPRs used for passing arguments in the ILP32E/LP64E ABI. | |
| static const MCPhysReg ArgEGPRs[] = {RISCV::X10, RISCV::X11, RISCV::X12, | |
| RISCV::X13, RISCV::X14, RISCV::X15}; | |
| if (ABI == RISCVABI::ABI_ILP32E || ABI == RISCVABI::ABI_LP64E) | |
| return ArrayRef(ArgEGPRs); | |
| return ArrayRef(ArgIGPRs); | |
| } | |
| ArrayRef<MCPhysReg> RISCV::getArgFPRs(const RISCVSubtarget &STI) { | |
| static const RISCVABI::ABI SoftFPABIs[] = { | |
| RISCVABI::ABI_ILP32, | |
| RISCVABI::ABI_ILP32E, | |
| RISCVABI::ABI_LP64, | |
| RISCVABI::ABI_LP64E, | |
| }; | |
| RISCVABI::ABI ABI = STI.getTargetABI(); | |
| if (llvm::is_contained(SoftFPABIs, ABI) || !STI.hasStdExtF()) | |
| return {}; | |
| if (STI.hasStdExtQ()) | |
| return ArrayRef(ArgFPR128s); | |
| if (STI.hasStdExtD()) | |
| return ArrayRef(ArgFPR64s); | |
| return ArrayRef(ArgFPR32s); | |
| } | |
| ArrayRef<MCPhysReg> RISCV::getArgVRs(const RISCVSubtarget &STI) { | |
| if (STI.hasVInstructions()) | |
| return ArrayRef(ArgVRs); | |
| return {}; | |
| } | |
| static ArrayRef<MCPhysReg> getArgGPR16s(const RISCVABI::ABI ABI) { | |
| // The GPRs used for passing arguments in the ILP32* and LP64* ABIs, except | |
| // the ILP32E ABI. | |
| static const MCPhysReg ArgIGPRs[] = {RISCV::X10_H, RISCV::X11_H, RISCV::X12_H, | |
| RISCV::X13_H, RISCV::X14_H, RISCV::X15_H, | |
| RISCV::X16_H, RISCV::X17_H}; | |
| // The GPRs used for passing arguments in the ILP32E/LP64E ABI. | |
| static const MCPhysReg ArgEGPRs[] = {RISCV::X10_H, RISCV::X11_H, | |
| RISCV::X12_H, RISCV::X13_H, | |
| RISCV::X14_H, RISCV::X15_H}; | |
| if (ABI == RISCVABI::ABI_ILP32E || ABI == RISCVABI::ABI_LP64E) | |
| return ArrayRef(ArgEGPRs); | |
| return ArrayRef(ArgIGPRs); | |
| } | |
| static ArrayRef<MCPhysReg> getArgGPR32s(const RISCVABI::ABI ABI) { | |
| // The GPRs used for passing arguments in the ILP32* and LP64* ABIs, except | |
| // the ILP32E ABI. | |
| static const MCPhysReg ArgIGPRs[] = {RISCV::X10_W, RISCV::X11_W, RISCV::X12_W, | |
| RISCV::X13_W, RISCV::X14_W, RISCV::X15_W, | |
| RISCV::X16_W, RISCV::X17_W}; | |
| // The GPRs used for passing arguments in the ILP32E/LP64E ABI. | |
| static const MCPhysReg ArgEGPRs[] = {RISCV::X10_W, RISCV::X11_W, | |
| RISCV::X12_W, RISCV::X13_W, | |
| RISCV::X14_W, RISCV::X15_W}; | |
| if (ABI == RISCVABI::ABI_ILP32E || ABI == RISCVABI::ABI_LP64E) | |
| return ArrayRef(ArgEGPRs); | |
| return ArrayRef(ArgIGPRs); | |
| } | |
| static ArrayRef<MCPhysReg> getFastCCArgGPRs(const RISCVABI::ABI ABI) { | |
| // The GPRs used for passing arguments in the FastCC, X5 and X6 might be used | |
| // for save-restore libcall, so we don't use them. | |
| // Don't use X7 for fastcc, since Zicfilp uses X7 as the label register. | |
| static const MCPhysReg FastCCIGPRs[] = { | |
| RISCV::X10, RISCV::X11, RISCV::X12, RISCV::X13, RISCV::X14, RISCV::X15, | |
| RISCV::X16, RISCV::X17, RISCV::X28, RISCV::X29, RISCV::X30, RISCV::X31}; | |
| // The GPRs used for passing arguments in the FastCC when using ILP32E/LP64E. | |
| static const MCPhysReg FastCCEGPRs[] = {RISCV::X10, RISCV::X11, RISCV::X12, | |
| RISCV::X13, RISCV::X14, RISCV::X15}; | |
| if (ABI == RISCVABI::ABI_ILP32E || ABI == RISCVABI::ABI_LP64E) | |
| return ArrayRef(FastCCEGPRs); | |
| return ArrayRef(FastCCIGPRs); | |
| } | |
| static ArrayRef<MCPhysReg> getFastCCArgGPRF16s(const RISCVABI::ABI ABI) { | |
| // The GPRs used for passing arguments in the FastCC, X5 and X6 might be used | |
| // for save-restore libcall, so we don't use them. | |
| // Don't use X7 for fastcc, since Zicfilp uses X7 as the label register. | |
| static const MCPhysReg FastCCIGPRs[] = { | |
| RISCV::X10_H, RISCV::X11_H, RISCV::X12_H, RISCV::X13_H, | |
| RISCV::X14_H, RISCV::X15_H, RISCV::X16_H, RISCV::X17_H, | |
| RISCV::X28_H, RISCV::X29_H, RISCV::X30_H, RISCV::X31_H}; | |
| // The GPRs used for passing arguments in the FastCC when using ILP32E/LP64E. | |
| static const MCPhysReg FastCCEGPRs[] = {RISCV::X10_H, RISCV::X11_H, | |
| RISCV::X12_H, RISCV::X13_H, | |
| RISCV::X14_H, RISCV::X15_H}; | |
| if (ABI == RISCVABI::ABI_ILP32E || ABI == RISCVABI::ABI_LP64E) | |
| return ArrayRef(FastCCEGPRs); | |
| return ArrayRef(FastCCIGPRs); | |
| } | |
| static ArrayRef<MCPhysReg> getFastCCArgGPRF32s(const RISCVABI::ABI ABI) { | |
| // The GPRs used for passing arguments in the FastCC, X5 and X6 might be used | |
| // for save-restore libcall, so we don't use them. | |
| // Don't use X7 for fastcc, since Zicfilp uses X7 as the label register. | |
| static const MCPhysReg FastCCIGPRs[] = { | |
| RISCV::X10_W, RISCV::X11_W, RISCV::X12_W, RISCV::X13_W, | |
| RISCV::X14_W, RISCV::X15_W, RISCV::X16_W, RISCV::X17_W, | |
| RISCV::X28_W, RISCV::X29_W, RISCV::X30_W, RISCV::X31_W}; | |
| // The GPRs used for passing arguments in the FastCC when using ILP32E/LP64E. | |
| static const MCPhysReg FastCCEGPRs[] = {RISCV::X10_W, RISCV::X11_W, | |
| RISCV::X12_W, RISCV::X13_W, | |
| RISCV::X14_W, RISCV::X15_W}; | |
| if (ABI == RISCVABI::ABI_ILP32E || ABI == RISCVABI::ABI_LP64E) | |
| return ArrayRef(FastCCEGPRs); | |
| return ArrayRef(FastCCIGPRs); | |
| } | |
| // Pass a 2*XLEN argument that has been split into two XLEN values through | |
| // registers or the stack as necessary. | |
| static bool CC_RISCVAssign2XLen(CCState &State, CCValAssign VA1, | |
| ISD::ArgFlagsTy ArgFlags1, unsigned ValNo2, | |
| MVT ValVT2, MVT LocVT2, | |
| ISD::ArgFlagsTy ArgFlags2, | |
| const RISCVSubtarget &Subtarget) { | |
| unsigned XLen = Subtarget.getXLen(); | |
| unsigned XLenInBytes = XLen / 8; | |
| RISCVABI::ABI ABI = Subtarget.getTargetABI(); | |
| bool EABI = ABI == RISCVABI::ABI_ILP32E || ABI == RISCVABI::ABI_LP64E; | |
| ArrayRef<MCPhysReg> ArgGPRs = RISCV::getArgGPRs(Subtarget); | |
| if (MCRegister Reg = State.AllocateReg(ArgGPRs)) { | |
| // At least one half can be passed via register. | |
| State.addLoc(CCValAssign::getReg(VA1.getValNo(), VA1.getValVT(), Reg, | |
| VA1.getLocVT(), CCValAssign::Full)); | |
| } else { | |
| // Both halves must be passed on the stack, with proper alignment. | |
| // TODO: To be compatible with GCC's behaviors, we force them to have 4-byte | |
| // alignment. This behavior may be changed when RV32E/ILP32E is ratified. | |
| Align StackAlign(XLenInBytes); | |
| if (!EABI || XLen != 32) | |
| StackAlign = std::max(StackAlign, ArgFlags1.getNonZeroOrigAlign()); | |
| State.addLoc( | |
| CCValAssign::getMem(VA1.getValNo(), VA1.getValVT(), | |
| State.AllocateStack(XLenInBytes, StackAlign), | |
| VA1.getLocVT(), CCValAssign::Full)); | |
| State.addLoc(CCValAssign::getMem( | |
| ValNo2, ValVT2, State.AllocateStack(XLenInBytes, Align(XLenInBytes)), | |
| LocVT2, CCValAssign::Full)); | |
| return false; | |
| } | |
| if (MCRegister Reg = State.AllocateReg(ArgGPRs)) { | |
| // The second half can also be passed via register. | |
| State.addLoc( | |
| CCValAssign::getReg(ValNo2, ValVT2, Reg, LocVT2, CCValAssign::Full)); | |
| } else { | |
| // The second half is passed via the stack, without additional alignment. | |
| State.addLoc(CCValAssign::getMem( | |
| ValNo2, ValVT2, State.AllocateStack(XLenInBytes, Align(XLenInBytes)), | |
| LocVT2, CCValAssign::Full)); | |
| } | |
| return false; | |
| } | |
| static MCRegister allocateRVVReg(MVT LocVT, unsigned ValNo, CCState &State, | |
| const RISCVTargetLowering &TLI) { | |
| const TargetRegisterClass *RC = TLI.getRegClassFor(LocVT); | |
| if (RC == &RISCV::VRRegClass) { | |
| // Assign the first mask argument to V0. | |
| // This is an interim calling convention and it may be changed in the | |
| // future. | |
| if (LocVT.getVectorElementType() == MVT::i1) | |
| if (MCRegister Reg = State.AllocateReg(RISCV::V0)) | |
| return Reg; | |
| return State.AllocateReg(ArgVRs); | |
| } | |
| if (RC == &RISCV::VRM2RegClass) | |
| return State.AllocateReg(ArgVRM2s); | |
| if (RC == &RISCV::VRM4RegClass) | |
| return State.AllocateReg(ArgVRM4s); | |
| if (RC == &RISCV::VRM8RegClass) | |
| return State.AllocateReg(ArgVRM8s); | |
| if (RC == &RISCV::VRN2M1RegClass) | |
| return State.AllocateReg(ArgVRN2M1s); | |
| if (RC == &RISCV::VRN3M1RegClass) | |
| return State.AllocateReg(ArgVRN3M1s); | |
| if (RC == &RISCV::VRN4M1RegClass) | |
| return State.AllocateReg(ArgVRN4M1s); | |
| if (RC == &RISCV::VRN5M1RegClass) | |
| return State.AllocateReg(ArgVRN5M1s); | |
| if (RC == &RISCV::VRN6M1RegClass) | |
| return State.AllocateReg(ArgVRN6M1s); | |
| if (RC == &RISCV::VRN7M1RegClass) | |
| return State.AllocateReg(ArgVRN7M1s); | |
| if (RC == &RISCV::VRN8M1RegClass) | |
| return State.AllocateReg(ArgVRN8M1s); | |
| if (RC == &RISCV::VRN2M2RegClass) | |
| return State.AllocateReg(ArgVRN2M2s); | |
| if (RC == &RISCV::VRN3M2RegClass) | |
| return State.AllocateReg(ArgVRN3M2s); | |
| if (RC == &RISCV::VRN4M2RegClass) | |
| return State.AllocateReg(ArgVRN4M2s); | |
| if (RC == &RISCV::VRN2M4RegClass) | |
| return State.AllocateReg(ArgVRN2M4s); | |
| llvm_unreachable("Unhandled register class for ValueType"); | |
| } | |
| // Implements the RISC-V calling convention. Returns true upon failure. | |
| // | |
| // This has a slightly different signature to CCAssignFn - it adds `bool IsRet`. | |
| static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT, | |
| CCValAssign::LocInfo LocInfo, | |
| ISD::ArgFlagsTy ArgFlags, Type *OrigTy, | |
| CCState &State, bool IsRet) { | |
| assert(ValVT == LocVT && "Expected ValVT and LocVT to match"); | |
| const MachineFunction &MF = State.getMachineFunction(); | |
| const DataLayout &DL = MF.getDataLayout(); | |
| const RISCVSubtarget &Subtarget = MF.getSubtarget<RISCVSubtarget>(); | |
| const RISCVTargetLowering &TLI = *Subtarget.getTargetLowering(); | |
| unsigned XLen = Subtarget.getXLen(); | |
| MVT XLenVT = Subtarget.getXLenVT(); | |
| if (ArgFlags.isNest()) { | |
| // Static chain parameter must not be passed in normal argument registers, | |
| // so we assign t2/t3 for it as done in GCC's | |
| // __builtin_call_with_static_chain | |
| bool HasCFBranch = | |
| MF.getInfo<RISCVMachineFunctionInfo>()->hasCFProtectionBranch(); | |
| // Normal: t2, Branch control flow protection: t3 | |
| const auto StaticChainReg = HasCFBranch ? RISCV::X28 : RISCV::X7; | |
| RISCVABI::ABI ABI = Subtarget.getTargetABI(); | |
| if (HasCFBranch && | |
| (ABI == RISCVABI::ABI_ILP32E || ABI == RISCVABI::ABI_LP64E)) | |
| reportFatalUsageError( | |
| "Nested functions with control flow protection are not " | |
| "usable with ILP32E or LP64E ABI."); | |
| if (MCRegister Reg = State.AllocateReg(StaticChainReg)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| // Any return value split in to more than two values can't be returned | |
| // directly. Vectors are returned via the available vector registers. | |
| if ((!LocVT.isVector() || Subtarget.isPExtPackedType(LocVT)) && IsRet && | |
| ValNo > 1) | |
| return true; | |
| // Double wide packed types require 2 GPRs so we can only return 1 of them. | |
| if (Subtarget.isPExtPackedDoubleType(LocVT) && IsRet && ValNo > 0) | |
| return true; | |
| // AllowFPRForF16_F32 if targeting an FLEN>=32 ABI and the argument isn't | |
| // variadic. | |
| bool AllowFPRForF16_F32 = false; | |
| // UseFPRForF64 if targeting an FLEN>=64 ABI and the argument isn't variadic. | |
| bool AllowFPRForF64 = false; | |
| RISCVABI::ABI ABI = Subtarget.getTargetABI(); | |
| switch (ABI) { | |
| default: | |
| llvm_unreachable("Unexpected ABI"); | |
| case RISCVABI::ABI_ILP32: | |
| case RISCVABI::ABI_ILP32E: | |
| case RISCVABI::ABI_LP64: | |
| case RISCVABI::ABI_LP64E: | |
| break; | |
| case RISCVABI::ABI_ILP32D: | |
| case RISCVABI::ABI_LP64D: | |
| AllowFPRForF64 = !ArgFlags.isVarArg(); | |
| [[fallthrough]]; | |
| case RISCVABI::ABI_ILP32F: | |
| case RISCVABI::ABI_LP64F: | |
| AllowFPRForF16_F32 = !ArgFlags.isVarArg(); | |
| break; | |
| } | |
| if ((LocVT == MVT::f16 || LocVT == MVT::bf16) && AllowFPRForF16_F32) { | |
| if (MCRegister Reg = State.AllocateReg(ArgFPR16s)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| if (LocVT == MVT::f32 && AllowFPRForF16_F32) { | |
| if (MCRegister Reg = State.AllocateReg(ArgFPR32s)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| if (LocVT == MVT::f64 && AllowFPRForF64) { | |
| if (MCRegister Reg = State.AllocateReg(ArgFPR64s)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| if (LocVT == MVT::f16 && Subtarget.hasStdExtZhinxmin()) { | |
| if (MCRegister Reg = State.AllocateReg(getArgGPR16s(ABI))) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| if (LocVT == MVT::f32 && Subtarget.hasStdExtZfinx()) { | |
| if (MCRegister Reg = State.AllocateReg(getArgGPR32s(ABI))) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| ArrayRef<MCPhysReg> ArgGPRs = RISCV::getArgGPRs(Subtarget); | |
| // Zdinx use GPR without a bitcast when possible. | |
| if (LocVT == MVT::f64 && XLen == 64 && Subtarget.hasStdExtZdinx()) { | |
| if (MCRegister Reg = State.AllocateReg(ArgGPRs)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| // FP smaller than XLen, uses custom GPR. | |
| if (LocVT == MVT::f16 || LocVT == MVT::bf16 || | |
| (LocVT == MVT::f32 && XLen == 64)) { | |
| if (MCRegister Reg = State.AllocateReg(ArgGPRs)) { | |
| LocVT = XLenVT; | |
| State.addLoc( | |
| CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| // Bitcast FP to GPR if we can use a GPR register. | |
| if ((XLen == 32 && LocVT == MVT::f32) || (XLen == 64 && LocVT == MVT::f64)) { | |
| if (MCRegister Reg = State.AllocateReg(ArgGPRs)) { | |
| LocVT = XLenVT; | |
| LocInfo = CCValAssign::BCvt; | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| // 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. | |
| // TODO: To be compatible with GCC's behaviors, we don't align registers | |
| // currently if we are using ILP32E calling convention. This behavior may be | |
| // changed when RV32E/ILP32E is ratified. | |
| unsigned TwoXLenInBytes = (2 * XLen) / 8; | |
| if (ArgFlags.isVarArg() && ArgFlags.getNonZeroOrigAlign() == TwoXLenInBytes && | |
| DL.getTypeAllocSize(OrigTy) == TwoXLenInBytes && | |
| ABI != RISCVABI::ABI_ILP32E) { | |
| unsigned RegIdx = State.getFirstUnallocated(ArgGPRs); | |
| // Skip 'odd' register if necessary. | |
| if (RegIdx != std::size(ArgGPRs) && RegIdx % 2 == 1) | |
| State.AllocateReg(ArgGPRs); | |
| } | |
| SmallVectorImpl<CCValAssign> &PendingLocs = State.getPendingLocs(); | |
| SmallVectorImpl<ISD::ArgFlagsTy> &PendingArgFlags = | |
| State.getPendingArgFlags(); | |
| assert(PendingLocs.size() == PendingArgFlags.size() && | |
| "PendingLocs and PendingArgFlags out of sync"); | |
| // Handle passing f64 on RV32D with a soft float ABI or when floating point | |
| // registers are exhausted. Or 64-bit P extension vectors on RV32. | |
| if (XLen == 32 && | |
| (LocVT == MVT::f64 || (Subtarget.isPExtPackedDoubleType(LocVT) && | |
| !ArgFlags.isSplit() && PendingLocs.empty()))) { | |
| assert(PendingLocs.empty() && | |
| "Can't lower f64 or P extension vector if it is split"); | |
| // Depending on available argument GPRS, f64 may be passed in a pair of | |
| // GPRs, split between a GPR and the stack, or passed completely on the | |
| // stack. LowerCall/LowerFormalArguments/LowerReturn must recognise these | |
| // cases. | |
| MCRegister Reg = State.AllocateReg(ArgGPRs); | |
| if (!Reg) { | |
| int64_t StackOffset = State.AllocateStack(8, Align(8)); | |
| State.addLoc( | |
| CCValAssign::getMem(ValNo, ValVT, StackOffset, LocVT, LocInfo)); | |
| return false; | |
| } | |
| LocVT = MVT::i32; | |
| State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| MCRegister HiReg = State.AllocateReg(ArgGPRs); | |
| if (HiReg) { | |
| State.addLoc( | |
| CCValAssign::getCustomReg(ValNo, ValVT, HiReg, LocVT, LocInfo)); | |
| } else { | |
| int64_t StackOffset = State.AllocateStack(4, Align(4)); | |
| State.addLoc( | |
| CCValAssign::getCustomMem(ValNo, ValVT, StackOffset, LocVT, LocInfo)); | |
| } | |
| return false; | |
| } | |
| // If the split argument only had two elements, it should be passed directly | |
| // in registers or on the stack. | |
| if ((LocVT.isScalarInteger() || | |
| (Subtarget.isPExtPackedType(LocVT) && LocVT.getSizeInBits() == XLen)) && | |
| ArgFlags.isSplitEnd() && PendingLocs.size() <= 1) { | |
| assert(PendingLocs.size() == 1 && "Unexpected PendingLocs.size()"); | |
| // Apply the normal calling convention rules to the first half of the | |
| // split argument. | |
| CCValAssign VA = PendingLocs[0]; | |
| ISD::ArgFlagsTy AF = PendingArgFlags[0]; | |
| PendingLocs.clear(); | |
| PendingArgFlags.clear(); | |
| return CC_RISCVAssign2XLen(State, VA, AF, ValNo, ValVT, LocVT, ArgFlags, | |
| Subtarget); | |
| } | |
| // Split arguments might be passed indirectly, so keep track of the pending | |
| // values. Split vectors excluding P extension packed vectors(see | |
| // isPExtPackedType) are passed via a mix of registers and indirectly, so | |
| // treat them as we would any other argument. | |
| if ((LocVT.isScalarInteger() || Subtarget.isPExtPackedType(LocVT)) && | |
| (ArgFlags.isSplit() || !PendingLocs.empty())) { | |
| PendingLocs.push_back( | |
| CCValAssign::getPending(ValNo, ValVT, LocVT, LocInfo)); | |
| PendingArgFlags.push_back(ArgFlags); | |
| if (!ArgFlags.isSplitEnd()) { | |
| return false; | |
| } | |
| } | |
| // Allocate to a register if possible, or else a stack slot. | |
| MCRegister Reg; | |
| unsigned StoreSizeBytes = XLen / 8; | |
| Align StackAlign = Align(XLen / 8); | |
| // FIXME: If P extension and V extension are enabled at the same time, | |
| // who should go first? | |
| if (!Subtarget.isPExtPackedType(LocVT) && | |
| (LocVT.isVector() || LocVT.isRISCVVectorTuple())) { | |
| Reg = allocateRVVReg(LocVT, ValNo, State, TLI); | |
| if (Reg) { | |
| // Fixed-length vectors are located in the corresponding scalable-vector | |
| // container types. | |
| if (LocVT.isFixedLengthVector()) { | |
| LocVT = TLI.getContainerForFixedLengthVector(LocVT); | |
| State.addLoc( | |
| CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } else { | |
| // For return values, the vector must be passed fully via registers or | |
| // via the stack. | |
| if (IsRet) | |
| return true; | |
| // Try using a GPR to pass the address | |
| if ((Reg = State.AllocateReg(ArgGPRs))) { | |
| LocVT = XLenVT; | |
| LocInfo = CCValAssign::Indirect; | |
| } else if (LocVT.isScalableVector()) { | |
| LocVT = XLenVT; | |
| LocInfo = CCValAssign::Indirect; | |
| } else { | |
| StoreSizeBytes = LocVT.getStoreSize(); | |
| // Align vectors to their element sizes, being careful for vXi1 | |
| // vectors. | |
| StackAlign = MaybeAlign(LocVT.getScalarSizeInBits() / 8).valueOrOne(); | |
| } | |
| } | |
| } else { | |
| Reg = State.AllocateReg(ArgGPRs); | |
| } | |
| int64_t StackOffset = | |
| Reg ? 0 : State.AllocateStack(StoreSizeBytes, StackAlign); | |
| // If we reach this point and PendingLocs is non-empty, we must be at the | |
| // end of a split argument that must be passed indirectly. | |
| if (!PendingLocs.empty()) { | |
| assert(ArgFlags.isSplitEnd() && "Expected ArgFlags.isSplitEnd()"); | |
| assert(PendingLocs.size() > 1 && "Unexpected PendingLocs.size()"); | |
| for (auto &It : PendingLocs) { | |
| if (Reg) | |
| State.addLoc(CCValAssign::getReg(It.getValNo(), It.getValVT(), Reg, | |
| XLenVT, CCValAssign::Indirect)); | |
| else | |
| State.addLoc(CCValAssign::getMem(It.getValNo(), It.getValVT(), | |
| StackOffset, XLenVT, | |
| CCValAssign::Indirect)); | |
| } | |
| PendingLocs.clear(); | |
| PendingArgFlags.clear(); | |
| return false; | |
| } | |
| assert(((LocVT.isFloatingPoint() && !LocVT.isVector()) || LocVT == XLenVT || | |
| Subtarget.isPExtPackedType(LocVT) || | |
| (TLI.getSubtarget().hasVInstructions() && | |
| (LocVT.isVector() || LocVT.isRISCVVectorTuple()))) && | |
| "Expected an XLenVT or vector types at this stage"); | |
| if (Reg) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| State.addLoc(CCValAssign::getMem(ValNo, ValVT, StackOffset, LocVT, LocInfo)); | |
| return false; | |
| } | |
| // FastCC has less than 1% performance improvement for some particular | |
| // benchmark. But theoretically, it may have benefit for some cases. | |
| static bool CC_RISCV_FastCC(unsigned ValNo, MVT ValVT, MVT LocVT, | |
| CCValAssign::LocInfo LocInfo, | |
| ISD::ArgFlagsTy ArgFlags, Type *OrigTy, | |
| CCState &State) { | |
| const MachineFunction &MF = State.getMachineFunction(); | |
| const RISCVSubtarget &Subtarget = MF.getSubtarget<RISCVSubtarget>(); | |
| const RISCVTargetLowering &TLI = *Subtarget.getTargetLowering(); | |
| RISCVABI::ABI ABI = Subtarget.getTargetABI(); | |
| if ((LocVT == MVT::f16 && Subtarget.hasStdExtZfhmin()) || | |
| (LocVT == MVT::bf16 && Subtarget.hasStdExtZfbfmin())) { | |
| static const MCPhysReg FPR16List[] = { | |
| RISCV::F10_H, RISCV::F11_H, RISCV::F12_H, RISCV::F13_H, RISCV::F14_H, | |
| RISCV::F15_H, RISCV::F16_H, RISCV::F17_H, RISCV::F0_H, RISCV::F1_H, | |
| RISCV::F2_H, RISCV::F3_H, RISCV::F4_H, RISCV::F5_H, RISCV::F6_H, | |
| RISCV::F7_H, RISCV::F28_H, RISCV::F29_H, RISCV::F30_H, RISCV::F31_H}; | |
| if (MCRegister Reg = State.AllocateReg(FPR16List)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| if (LocVT == MVT::f32 && Subtarget.hasStdExtF()) { | |
| static const MCPhysReg FPR32List[] = { | |
| RISCV::F10_F, RISCV::F11_F, RISCV::F12_F, RISCV::F13_F, RISCV::F14_F, | |
| RISCV::F15_F, RISCV::F16_F, RISCV::F17_F, RISCV::F0_F, RISCV::F1_F, | |
| RISCV::F2_F, RISCV::F3_F, RISCV::F4_F, RISCV::F5_F, RISCV::F6_F, | |
| RISCV::F7_F, RISCV::F28_F, RISCV::F29_F, RISCV::F30_F, RISCV::F31_F}; | |
| if (MCRegister Reg = State.AllocateReg(FPR32List)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| if (LocVT == MVT::f64 && Subtarget.hasStdExtD()) { | |
| static const MCPhysReg FPR64List[] = { | |
| RISCV::F10_D, RISCV::F11_D, RISCV::F12_D, RISCV::F13_D, RISCV::F14_D, | |
| RISCV::F15_D, RISCV::F16_D, RISCV::F17_D, RISCV::F0_D, RISCV::F1_D, | |
| RISCV::F2_D, RISCV::F3_D, RISCV::F4_D, RISCV::F5_D, RISCV::F6_D, | |
| RISCV::F7_D, RISCV::F28_D, RISCV::F29_D, RISCV::F30_D, RISCV::F31_D}; | |
| if (MCRegister Reg = State.AllocateReg(FPR64List)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| MVT XLenVT = Subtarget.getXLenVT(); | |
| // Check if there is an available GPRF16 before hitting the stack. | |
| if ((LocVT == MVT::f16 && Subtarget.hasStdExtZhinxmin())) { | |
| if (MCRegister Reg = State.AllocateReg(getFastCCArgGPRF16s(ABI))) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| // Check if there is an available GPRF32 before hitting the stack. | |
| if (LocVT == MVT::f32 && Subtarget.hasStdExtZfinx()) { | |
| if (MCRegister Reg = State.AllocateReg(getFastCCArgGPRF32s(ABI))) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| // Check if there is an available GPR before hitting the stack. | |
| if (LocVT == MVT::f64 && Subtarget.is64Bit() && Subtarget.hasStdExtZdinx()) { | |
| if (MCRegister Reg = State.AllocateReg(getFastCCArgGPRs(ABI))) { | |
| if (LocVT.getSizeInBits() != Subtarget.getXLen()) { | |
| LocVT = XLenVT; | |
| State.addLoc( | |
| CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| ArrayRef<MCPhysReg> ArgGPRs = getFastCCArgGPRs(ABI); | |
| if (LocVT.isVector()) { | |
| if (MCRegister Reg = allocateRVVReg(ValVT, ValNo, State, TLI)) { | |
| // Fixed-length vectors are located in the corresponding scalable-vector | |
| // container types. | |
| if (LocVT.isFixedLengthVector()) { | |
| LocVT = TLI.getContainerForFixedLengthVector(LocVT); | |
| State.addLoc( | |
| CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| // Pass scalable vectors indirectly. Pass fixed vectors indirectly if we | |
| // have a free GPR. | |
| if (LocVT.isScalableVector() || | |
| State.getFirstUnallocated(ArgGPRs) != ArgGPRs.size()) { | |
| LocInfo = CCValAssign::Indirect; | |
| LocVT = XLenVT; | |
| } | |
| } | |
| if (LocVT == XLenVT) { | |
| if (MCRegister Reg = State.AllocateReg(getFastCCArgGPRs(ABI))) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| if (LocVT == XLenVT || LocVT == MVT::f16 || LocVT == MVT::bf16 || | |
| LocVT == MVT::f32 || LocVT == MVT::f64 || LocVT.isFixedLengthVector()) { | |
| Align StackAlign = MaybeAlign(ValVT.getScalarSizeInBits() / 8).valueOrOne(); | |
| int64_t Offset = State.AllocateStack(LocVT.getStoreSize(), StackAlign); | |
| State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo)); | |
| return false; | |
| } | |
| return true; // CC didn't match. | |
| } | |
| static bool CC_RISCV_GHC(unsigned ValNo, MVT ValVT, MVT LocVT, | |
| CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, | |
| Type *OrigTy, CCState &State) { | |
| if (ArgFlags.isNest()) { | |
| report_fatal_error( | |
| "Attribute 'nest' is not supported in GHC calling convention"); | |
| } | |
| static const MCPhysReg GPRList[] = { | |
| RISCV::X9, RISCV::X18, RISCV::X19, RISCV::X20, RISCV::X21, RISCV::X22, | |
| RISCV::X23, RISCV::X24, RISCV::X25, RISCV::X26, RISCV::X27}; | |
| if (LocVT == MVT::i32 || LocVT == MVT::i64) { | |
| // Pass in STG registers: Base, Sp, Hp, R1, R2, R3, R4, R5, R6, R7, SpLim | |
| // s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 | |
| if (MCRegister Reg = State.AllocateReg(GPRList)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| const RISCVSubtarget &Subtarget = | |
| State.getMachineFunction().getSubtarget<RISCVSubtarget>(); | |
| if (LocVT == MVT::f32 && Subtarget.hasStdExtF()) { | |
| // Pass in STG registers: F1, ..., F6 | |
| // fs0 ... fs5 | |
| static const MCPhysReg FPR32List[] = {RISCV::F8_F, RISCV::F9_F, | |
| RISCV::F18_F, RISCV::F19_F, | |
| RISCV::F20_F, RISCV::F21_F}; | |
| if (MCRegister Reg = State.AllocateReg(FPR32List)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| if (LocVT == MVT::f64 && Subtarget.hasStdExtD()) { | |
| // Pass in STG registers: D1, ..., D6 | |
| // fs6 ... fs11 | |
| static const MCPhysReg FPR64List[] = {RISCV::F22_D, RISCV::F23_D, | |
| RISCV::F24_D, RISCV::F25_D, | |
| RISCV::F26_D, RISCV::F27_D}; | |
| if (MCRegister Reg = State.AllocateReg(FPR64List)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| if (LocVT == MVT::f32 && Subtarget.hasStdExtZfinx()) { | |
| static const MCPhysReg GPR32List[] = { | |
| RISCV::X9_W, RISCV::X18_W, RISCV::X19_W, RISCV::X20_W, | |
| RISCV::X21_W, RISCV::X22_W, RISCV::X23_W, RISCV::X24_W, | |
| RISCV::X25_W, RISCV::X26_W, RISCV::X27_W}; | |
| if (MCRegister Reg = State.AllocateReg(GPR32List)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| if (LocVT == MVT::f64 && Subtarget.hasStdExtZdinx() && Subtarget.is64Bit()) { | |
| if (MCRegister Reg = State.AllocateReg(GPRList)) { | |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); | |
| return false; | |
| } | |
| } | |
| report_fatal_error("No registers left in GHC calling convention"); | |
| return true; | |
| } |