diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp +++ b/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(); diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/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,6 +1961,47 @@ 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::i64) { + // Pass in STG registers: Base, Sp, Hp, R1, R2, R3, R4, R5, R6, R7, SpLim + 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 + 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 + 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; + } + } + + return true; // CC didn't match. +} + // Transform physical registers into virtual registers. SDValue RISCVTargetLowering::LowerFormalArguments( SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, @@ -1968,6 +2013,7 @@ report_fatal_error("Unsupported calling convention"); case CallingConv::C: case CallingConv::Fast: + case CallingConv::GHC: break; } @@ -1999,6 +2045,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 +2247,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 +2536,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); diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp +++ b/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"); diff --git a/llvm/test/CodeGen/RISCV/ghc-cc.ll b/llvm/test/CodeGen/RISCV/ghc-cc.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/ghc-cc.ll @@ -0,0 +1,113 @@ +; RUN: llc -mtriple=riscv64-unknown-linux-gnu -mattr=+f,+d \ +; RUN: --target-abi lp64d < %s \ +; RUN: | FileCheck %s + +; Check the GHC call convention works (riscv64) + +@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 { +entry: + ; CHECK: lui {{a[0-9]}}, %hi(d6) + ; CHECK-NEXT: fld fs11, %lo(d6)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(d5) + ; CHECK-NEXT: fld fs10, %lo(d5)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(d4) + ; CHECK-NEXT: fld fs9, %lo(d4)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(d3) + ; CHECK-NEXT: fld fs8, %lo(d3)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(d2) + ; CHECK-NEXT: fld fs7, %lo(d2)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(d1) + ; CHECK-NEXT: fld fs6, %lo(d1)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(f6) + ; CHECK-NEXT: flw fs5, %lo(f6)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(f5) + ; CHECK-NEXT: flw fs4, %lo(f5)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(f4) + ; CHECK-NEXT: flw fs3, %lo(f4)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(f3) + ; CHECK-NEXT: flw fs2, %lo(f3)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(f2) + ; CHECK-NEXT: flw fs1, %lo(f2)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(f1) + ; CHECK-NEXT: flw fs0, %lo(f1)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(splim) + ; CHECK-NEXT: ld s11, %lo(splim)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(r7) + ; CHECK-NEXT: ld s10, %lo(r7)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(r6) + ; CHECK-NEXT: ld s9, %lo(r6)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(r5) + ; CHECK-NEXT: ld s8, %lo(r5)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(r4) + ; CHECK-NEXT: ld s7, %lo(r4)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(r3) + ; CHECK-NEXT: ld s6, %lo(r3)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(r2) + ; CHECK-NEXT: ld s5, %lo(r2)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(r1) + ; CHECK-NEXT: ld s4, %lo(r1)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(hp) + ; CHECK-NEXT: ld s3, %lo(hp)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(sp) + ; CHECK-NEXT: ld s2, %lo(sp)({{a[0-9]}}) + ; CHECK-NEXT: lui {{a[0-9]}}, %hi(base) + ; CHECK-NEXT: ld s1, %lo(base)({{a[0-9]}}) + %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 + ; CHECK: tail bar + 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)