Index: llvm/lib/Target/RISCV/RISCVFrameLowering.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVFrameLowering.cpp +++ llvm/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -325,6 +325,11 @@ // to determine the end of the prologue. DebugLoc DL; + // All calls are tail calls in GHC calling conv, and functions have no + // prologue/epilogue. + if (MF.getFunction().getCallingConv() == CallingConv::GHC) + return; + // Emit prologue for shadow call stack. emitSCSPrologue(MF, MBB, MBBI, DL); @@ -500,6 +505,11 @@ Register FPReg = getFPReg(STI); Register SPReg = getSPReg(STI); + // All calls are tail calls in GHC calling conv, and functions have no + // prologue/epilogue. + if (MF.getFunction().getCallingConv() == CallingConv::GHC) + return; + // Get the insert location for the epilogue. If there were no terminators in // the block, get the last instruction. MachineBasicBlock::iterator MBBI = MBB.end(); Index: llvm/lib/Target/RISCV/RISCVISelLowering.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -641,6 +641,10 @@ TLSModel::Model Model = getTargetMachine().getTLSModel(N->getGlobal()); + if (DAG.getMachineFunction().getFunction().getCallingConv() == + CallingConv::GHC) + report_fatal_error("In GHC calling convention TLS is not supported"); + SDValue Addr; switch (Model) { case TLSModel::LocalExec: @@ -1957,22 +1961,71 @@ return true; // CC didn't match. } +static bool CC_RISCV_GHC(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, + ISD::ArgFlagsTy ArgFlags, CCState &State) { + + 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 + 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 (unsigned Reg = State.AllocateReg(GPRList)) { + State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + return false; + } + } + + if (LocVT == MVT::f32) { + // 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 (unsigned Reg = State.AllocateReg(FPR32List)) { + State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + return false; + } + } + + if (LocVT == MVT::f64) { + // 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 (unsigned Reg = State.AllocateReg(FPR64List)) { + State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + return false; + } + } + + report_fatal_error("No registers left in GHC calling convention"); + return true; +} + // Transform physical registers into virtual registers. SDValue RISCVTargetLowering::LowerFormalArguments( SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, const SmallVectorImpl &Ins, const SDLoc &DL, SelectionDAG &DAG, SmallVectorImpl &InVals) const { + MachineFunction &MF = DAG.getMachineFunction(); + switch (CallConv) { default: report_fatal_error("Unsupported calling convention"); case CallingConv::C: case CallingConv::Fast: break; + case CallingConv::GHC: + if (!MF.getSubtarget().getFeatureBits()[RISCV::FeatureStdExtF] || + !MF.getSubtarget().getFeatureBits()[RISCV::FeatureStdExtD]) + report_fatal_error( + "GHC calling convention requires the F and D instruction set extensions"); } - MachineFunction &MF = DAG.getMachineFunction(); - const Function &Func = MF.getFunction(); if (Func.hasFnAttribute("interrupt")) { if (!Func.arg_empty()) @@ -1999,6 +2052,8 @@ if (CallConv == CallingConv::Fast) CCInfo.AnalyzeFormalArguments(Ins, CC_RISCV_FastCC); + else if (CallConv == CallingConv::GHC) + CCInfo.AnalyzeFormalArguments(Ins, CC_RISCV_GHC); else analyzeInputArgs(MF, CCInfo, Ins, /*IsRet=*/false); @@ -2199,6 +2254,8 @@ if (CallConv == CallingConv::Fast) ArgCCInfo.AnalyzeCallOperands(Outs, CC_RISCV_FastCC); + else if (CallConv == CallingConv::GHC) + ArgCCInfo.AnalyzeCallOperands(Outs, CC_RISCV_GHC); else analyzeOutputArgs(MF, ArgCCInfo, Outs, /*IsRet=*/false, &CLI); @@ -2486,6 +2543,9 @@ analyzeOutputArgs(DAG.getMachineFunction(), CCInfo, Outs, /*IsRet=*/true, nullptr); + if (CallConv == CallingConv::GHC && !RVLocs.empty()) + report_fatal_error("GHC functions return void only"); + SDValue Glue; SmallVector RetOps(1, Chain); Index: llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp +++ llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp @@ -45,6 +45,8 @@ const MCPhysReg * RISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { auto &Subtarget = MF->getSubtarget(); + if (MF->getFunction().getCallingConv() == CallingConv::GHC) + return CSR_NoRegs_SaveList; if (MF->getFunction().hasFnAttribute("interrupt")) { if (Subtarget.hasStdExtD()) return CSR_XLEN_F64_Interrupt_SaveList; @@ -190,9 +192,11 @@ const uint32_t * RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & MF, - CallingConv::ID /*CC*/) const { + CallingConv::ID CC) const { auto &Subtarget = MF.getSubtarget(); + if (CC == CallingConv::GHC) + return CSR_NoRegs_RegMask; switch (Subtarget.getTargetABI()) { default: llvm_unreachable("Unrecognized ABI"); Index: llvm/test/CodeGen/RISCV/ghccc-rv32.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/RISCV/ghccc-rv32.ll @@ -0,0 +1,114 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=riscv32 -mattr=+f,+d < %s | FileCheck %s + +; Check the GHC call convention works (rv32) + +@base = external global i32 ; assigned to register: s1 +@sp = external global i32 ; assigned to register: s2 +@hp = external global i32 ; assigned to register: s3 +@r1 = external global i32 ; assigned to register: s4 +@r2 = external global i32 ; assigned to register: s5 +@r3 = external global i32 ; assigned to register: s6 +@r4 = external global i32 ; assigned to register: s7 +@r5 = external global i32 ; assigned to register: s8 +@r6 = external global i32 ; assigned to register: s9 +@r7 = external global i32 ; assigned to register: s10 +@splim = external global i32 ; assigned to register: s11 + +@f1 = external global float ; assigned to register: fs0 +@f2 = external global float ; assigned to register: fs1 +@f3 = external global float ; assigned to register: fs2 +@f4 = external global float ; assigned to register: fs3 +@f5 = external global float ; assigned to register: fs4 +@f6 = external global float ; assigned to register: fs5 + +@d1 = external global double ; assigned to register: fs6 +@d2 = external global double ; assigned to register: fs7 +@d3 = external global double ; assigned to register: fs8 +@d4 = external global double ; assigned to register: fs9 +@d5 = external global double ; assigned to register: fs10 +@d6 = external global double ; assigned to register: fs11 + +define ghccc void @foo() nounwind { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: lui a0, %hi(d6) +; CHECK-NEXT: fld fs11, %lo(d6)(a0) +; CHECK-NEXT: lui a0, %hi(d5) +; CHECK-NEXT: fld fs10, %lo(d5)(a0) +; CHECK-NEXT: lui a0, %hi(d4) +; CHECK-NEXT: fld fs9, %lo(d4)(a0) +; CHECK-NEXT: lui a0, %hi(d3) +; CHECK-NEXT: fld fs8, %lo(d3)(a0) +; CHECK-NEXT: lui a0, %hi(d2) +; CHECK-NEXT: fld fs7, %lo(d2)(a0) +; CHECK-NEXT: lui a0, %hi(d1) +; CHECK-NEXT: fld fs6, %lo(d1)(a0) +; CHECK-NEXT: lui a0, %hi(f6) +; CHECK-NEXT: flw fs5, %lo(f6)(a0) +; CHECK-NEXT: lui a0, %hi(f5) +; CHECK-NEXT: flw fs4, %lo(f5)(a0) +; CHECK-NEXT: lui a0, %hi(f4) +; CHECK-NEXT: flw fs3, %lo(f4)(a0) +; CHECK-NEXT: lui a0, %hi(f3) +; CHECK-NEXT: flw fs2, %lo(f3)(a0) +; CHECK-NEXT: lui a0, %hi(f2) +; CHECK-NEXT: flw fs1, %lo(f2)(a0) +; CHECK-NEXT: lui a0, %hi(f1) +; CHECK-NEXT: flw fs0, %lo(f1)(a0) +; CHECK-NEXT: lui a0, %hi(splim) +; CHECK-NEXT: lw s11, %lo(splim)(a0) +; CHECK-NEXT: lui a0, %hi(r7) +; CHECK-NEXT: lw s10, %lo(r7)(a0) +; CHECK-NEXT: lui a0, %hi(r6) +; CHECK-NEXT: lw s9, %lo(r6)(a0) +; CHECK-NEXT: lui a0, %hi(r5) +; CHECK-NEXT: lw s8, %lo(r5)(a0) +; CHECK-NEXT: lui a0, %hi(r4) +; CHECK-NEXT: lw s7, %lo(r4)(a0) +; CHECK-NEXT: lui a0, %hi(r3) +; CHECK-NEXT: lw s6, %lo(r3)(a0) +; CHECK-NEXT: lui a0, %hi(r2) +; CHECK-NEXT: lw s5, %lo(r2)(a0) +; CHECK-NEXT: lui a0, %hi(r1) +; CHECK-NEXT: lw s4, %lo(r1)(a0) +; CHECK-NEXT: lui a0, %hi(hp) +; CHECK-NEXT: lw s3, %lo(hp)(a0) +; CHECK-NEXT: lui a0, %hi(sp) +; CHECK-NEXT: lw s2, %lo(sp)(a0) +; CHECK-NEXT: lui a0, %hi(base) +; CHECK-NEXT: lw s1, %lo(base)(a0) +; CHECK-NEXT: tail bar +entry: + %0 = load double, double* @d6 + %1 = load double, double* @d5 + %2 = load double, double* @d4 + %3 = load double, double* @d3 + %4 = load double, double* @d2 + %5 = load double, double* @d1 + %6 = load float, float* @f6 + %7 = load float, float* @f5 + %8 = load float, float* @f4 + %9 = load float, float* @f3 + %10 = load float, float* @f2 + %11 = load float, float* @f1 + %12 = load i32, i32* @splim + %13 = load i32, i32* @r7 + %14 = load i32, i32* @r6 + %15 = load i32, i32* @r5 + %16 = load i32, i32* @r4 + %17 = load i32, i32* @r3 + %18 = load i32, i32* @r2 + %19 = load i32, i32* @r1 + %20 = load i32, i32* @hp + %21 = load i32, i32* @sp + %22 = load i32, i32* @base + tail call ghccc void @bar(i32 %22, i32 %21, i32 %20, i32 %19, i32 %18, i32 %17, i32 %16, i32 %15, i32 %14, i32 %13, i32 %12, + float %11, float %10, float %9, float %8, float %7, float %6, + double %5, double %4, double %3, double %2, double %1, double %0) nounwind + ret void +} + +declare ghccc void @bar(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, + float, float, float, float, float, float, + double, double, double, double, double, double) Index: llvm/test/CodeGen/RISCV/ghccc-rv64.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/RISCV/ghccc-rv64.ll @@ -0,0 +1,114 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=riscv64 -mattr=+f,+d < %s | FileCheck %s + +; Check the GHC call convention works (rv64) + +@base = external global i64 ; assigned to register: s1 +@sp = external global i64 ; assigned to register: s2 +@hp = external global i64 ; assigned to register: s3 +@r1 = external global i64 ; assigned to register: s4 +@r2 = external global i64 ; assigned to register: s5 +@r3 = external global i64 ; assigned to register: s6 +@r4 = external global i64 ; assigned to register: s7 +@r5 = external global i64 ; assigned to register: s8 +@r6 = external global i64 ; assigned to register: s9 +@r7 = external global i64 ; assigned to register: s10 +@splim = external global i64 ; assigned to register: s11 + +@f1 = external global float ; assigned to register: fs0 +@f2 = external global float ; assigned to register: fs1 +@f3 = external global float ; assigned to register: fs2 +@f4 = external global float ; assigned to register: fs3 +@f5 = external global float ; assigned to register: fs4 +@f6 = external global float ; assigned to register: fs5 + +@d1 = external global double ; assigned to register: fs6 +@d2 = external global double ; assigned to register: fs7 +@d3 = external global double ; assigned to register: fs8 +@d4 = external global double ; assigned to register: fs9 +@d5 = external global double ; assigned to register: fs10 +@d6 = external global double ; assigned to register: fs11 + +define ghccc void @foo() nounwind { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: lui a0, %hi(d6) +; CHECK-NEXT: fld fs11, %lo(d6)(a0) +; CHECK-NEXT: lui a0, %hi(d5) +; CHECK-NEXT: fld fs10, %lo(d5)(a0) +; CHECK-NEXT: lui a0, %hi(d4) +; CHECK-NEXT: fld fs9, %lo(d4)(a0) +; CHECK-NEXT: lui a0, %hi(d3) +; CHECK-NEXT: fld fs8, %lo(d3)(a0) +; CHECK-NEXT: lui a0, %hi(d2) +; CHECK-NEXT: fld fs7, %lo(d2)(a0) +; CHECK-NEXT: lui a0, %hi(d1) +; CHECK-NEXT: fld fs6, %lo(d1)(a0) +; CHECK-NEXT: lui a0, %hi(f6) +; CHECK-NEXT: flw fs5, %lo(f6)(a0) +; CHECK-NEXT: lui a0, %hi(f5) +; CHECK-NEXT: flw fs4, %lo(f5)(a0) +; CHECK-NEXT: lui a0, %hi(f4) +; CHECK-NEXT: flw fs3, %lo(f4)(a0) +; CHECK-NEXT: lui a0, %hi(f3) +; CHECK-NEXT: flw fs2, %lo(f3)(a0) +; CHECK-NEXT: lui a0, %hi(f2) +; CHECK-NEXT: flw fs1, %lo(f2)(a0) +; CHECK-NEXT: lui a0, %hi(f1) +; CHECK-NEXT: flw fs0, %lo(f1)(a0) +; CHECK-NEXT: lui a0, %hi(splim) +; CHECK-NEXT: ld s11, %lo(splim)(a0) +; CHECK-NEXT: lui a0, %hi(r7) +; CHECK-NEXT: ld s10, %lo(r7)(a0) +; CHECK-NEXT: lui a0, %hi(r6) +; CHECK-NEXT: ld s9, %lo(r6)(a0) +; CHECK-NEXT: lui a0, %hi(r5) +; CHECK-NEXT: ld s8, %lo(r5)(a0) +; CHECK-NEXT: lui a0, %hi(r4) +; CHECK-NEXT: ld s7, %lo(r4)(a0) +; CHECK-NEXT: lui a0, %hi(r3) +; CHECK-NEXT: ld s6, %lo(r3)(a0) +; CHECK-NEXT: lui a0, %hi(r2) +; CHECK-NEXT: ld s5, %lo(r2)(a0) +; CHECK-NEXT: lui a0, %hi(r1) +; CHECK-NEXT: ld s4, %lo(r1)(a0) +; CHECK-NEXT: lui a0, %hi(hp) +; CHECK-NEXT: ld s3, %lo(hp)(a0) +; CHECK-NEXT: lui a0, %hi(sp) +; CHECK-NEXT: ld s2, %lo(sp)(a0) +; CHECK-NEXT: lui a0, %hi(base) +; CHECK-NEXT: ld s1, %lo(base)(a0) +; CHECK-NEXT: tail bar +entry: + %0 = load double, double* @d6 + %1 = load double, double* @d5 + %2 = load double, double* @d4 + %3 = load double, double* @d3 + %4 = load double, double* @d2 + %5 = load double, double* @d1 + %6 = load float, float* @f6 + %7 = load float, float* @f5 + %8 = load float, float* @f4 + %9 = load float, float* @f3 + %10 = load float, float* @f2 + %11 = load float, float* @f1 + %12 = load i64, i64* @splim + %13 = load i64, i64* @r7 + %14 = load i64, i64* @r6 + %15 = load i64, i64* @r5 + %16 = load i64, i64* @r4 + %17 = load i64, i64* @r3 + %18 = load i64, i64* @r2 + %19 = load i64, i64* @r1 + %20 = load i64, i64* @hp + %21 = load i64, i64* @sp + %22 = load i64, i64* @base + tail call ghccc void @bar(i64 %22, i64 %21, i64 %20, i64 %19, i64 %18, i64 %17, i64 %16, i64 %15, i64 %14, i64 %13, i64 %12, + float %11, float %10, float %9, float %8, float %7, float %6, + double %5, double %4, double %3, double %2, double %1, double %0) nounwind + ret void +} + +declare ghccc void @bar(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, + float, float, float, float, float, float, + double, double, double, double, double, double)