Skip to content

Commit

Permalink
[RISCV] Add codegen support for ilp32f, ilp32d, lp64f, and lp64d ("ha…
Browse files Browse the repository at this point in the history
…rd float") ABIs

This patch adds support for the RISC-V hard float ABIs, building on top of
rL355771, which added basic target-abi parsing and MC layer support. It also
builds on some re-organisations and expansion of the upstream ABI and calling
convention tests which were recently committed directly upstream.

A number of aspects of the RISC-V float hard float ABIs require frontend
support (e.g. flattening of structs and passing int+fp for fp+fp structs in a
pair of registers), and will be addressed in a Clang patch.

As can be seen from the tests, it would be worthwhile extending
RISCVMergeBaseOffsets to handle constant pool as well as global accesses.

Differential Revision: https://reviews.llvm.org/D59357

llvm-svn: 357352
  • Loading branch information
asb committed Mar 30, 2019
1 parent 10c9032 commit 0b2803e
Show file tree
Hide file tree
Showing 14 changed files with 869 additions and 39 deletions.
8 changes: 8 additions & 0 deletions llvm/lib/Target/RISCV/RISCVCallingConv.td
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
def CSR_ILP32_LP64
: CalleeSavedRegs<(add X1, X3, X4, X8, X9, (sequence "X%u", 18, 27))>;

def CSR_ILP32F_LP64F
: CalleeSavedRegs<(add CSR_ILP32_LP64,
F8_32, F9_32, (sequence "F%u_32", 18, 27))>;

def CSR_ILP32D_LP64D
: CalleeSavedRegs<(add CSR_ILP32_LP64,
F8_64, F9_64, (sequence "F%u_64", 18, 27))>;

// Needed for implementation of RISCVRegisterInfo::getNoPreservedMask()
def CSR_NoRegs : CalleeSavedRegs<(add)>;

Expand Down
107 changes: 92 additions & 15 deletions llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,17 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
RISCVABI::ABI ABI = Subtarget.getTargetABI();
assert(ABI != RISCVABI::ABI_Unknown && "Improperly initialised target ABI");

if (ABI != RISCVABI::ABI_ILP32 && ABI != RISCVABI::ABI_LP64)
switch (ABI) {
default:
report_fatal_error("Don't know how to lower this ABI");
case RISCVABI::ABI_ILP32:
case RISCVABI::ABI_ILP32F:
case RISCVABI::ABI_ILP32D:
case RISCVABI::ABI_LP64:
case RISCVABI::ABI_LP64F:
case RISCVABI::ABI_LP64D:
break;
}

MVT XLenVT = Subtarget.getXLenVT();

Expand Down Expand Up @@ -981,6 +990,14 @@ static const MCPhysReg ArgGPRs[] = {
RISCV::X10, RISCV::X11, RISCV::X12, RISCV::X13,
RISCV::X14, RISCV::X15, RISCV::X16, RISCV::X17
};
static const MCPhysReg ArgFPR32s[] = {
RISCV::F10_32, RISCV::F11_32, RISCV::F12_32, RISCV::F13_32,
RISCV::F14_32, RISCV::F15_32, RISCV::F16_32, RISCV::F17_32
};
static const MCPhysReg ArgFPR64s[] = {
RISCV::F10_64, RISCV::F11_64, RISCV::F12_64, RISCV::F13_64,
RISCV::F14_64, RISCV::F15_64, RISCV::F16_64, RISCV::F17_64
};

// Pass a 2*XLEN argument that has been split into two XLEN values through
// registers or the stack as necessary.
Expand Down Expand Up @@ -1021,9 +1038,10 @@ static bool CC_RISCVAssign2XLen(unsigned XLen, CCState &State, CCValAssign VA1,
}

// Implements the RISC-V calling convention. Returns true upon failure.
static bool CC_RISCV(const DataLayout &DL, unsigned ValNo, MVT ValVT, MVT LocVT,
CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags,
CCState &State, bool IsFixed, bool IsRet, Type *OrigTy) {
static bool CC_RISCV(const DataLayout &DL, RISCVABI::ABI ABI, unsigned ValNo,
MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo,
ISD::ArgFlagsTy ArgFlags, CCState &State, bool IsFixed,
bool IsRet, Type *OrigTy) {
unsigned XLen = DL.getLargestLegalIntTypeSizeInBits();
assert(XLen == 32 || XLen == 64);
MVT XLenVT = XLen == 32 ? MVT::i32 : MVT::i64;
Expand All @@ -1033,10 +1051,42 @@ static bool CC_RISCV(const DataLayout &DL, unsigned ValNo, MVT ValVT, MVT LocVT,
if (IsRet && ValNo > 1)
return true;

if (ValVT == MVT::f32) {
// UseGPRForF32 if targeting one of the soft-float ABIs, if passing a
// variadic argument, or if no F32 argument registers are available.
bool UseGPRForF32 = true;
// UseGPRForF64 if targeting soft-float ABIs or an FLEN=32 ABI, if passing a
// variadic argument, or if no F64 argument registers are available.
bool UseGPRForF64 = true;

switch (ABI) {
default:
llvm_unreachable("Unexpected ABI");
case RISCVABI::ABI_ILP32:
case RISCVABI::ABI_LP64:
break;
case RISCVABI::ABI_ILP32F:
case RISCVABI::ABI_LP64F:
UseGPRForF32 = !IsFixed;
break;
case RISCVABI::ABI_ILP32D:
case RISCVABI::ABI_LP64D:
UseGPRForF32 = !IsFixed;
UseGPRForF64 = !IsFixed;
break;
}

if (State.getFirstUnallocated(ArgFPR32s) == array_lengthof(ArgFPR32s))
UseGPRForF32 = true;
if (State.getFirstUnallocated(ArgFPR64s) == array_lengthof(ArgFPR64s))
UseGPRForF64 = true;

// From this point on, rely on UseGPRForF32, UseGPRForF64 and similar local
// variables rather than directly checking against the target ABI.

if (UseGPRForF32 && ValVT == MVT::f32) {
LocVT = XLenVT;
LocInfo = CCValAssign::BCvt;
} else if (XLen == 64 && ValVT == MVT::f64) {
} else if (UseGPRForF64 && XLen == 64 && ValVT == MVT::f64) {
LocVT = MVT::i64;
LocInfo = CCValAssign::BCvt;
}
Expand Down Expand Up @@ -1064,8 +1114,9 @@ static bool CC_RISCV(const DataLayout &DL, unsigned ValNo, MVT ValVT, MVT LocVT,
assert(PendingLocs.size() == PendingArgFlags.size() &&
"PendingLocs and PendingArgFlags out of sync");

// Handle passing f64 on RV32D with a soft float ABI.
if (XLen == 32 && ValVT == MVT::f64) {
// Handle passing f64 on RV32D with a soft float ABI or when floating point
// registers are exhausted.
if (UseGPRForF64 && XLen == 32 && ValVT == MVT::f64) {
assert(!ArgFlags.isSplit() && PendingLocs.empty() &&
"Can't lower f64 if it is split");
// Depending on available argument GPRS, f64 may be passed in a pair of
Expand Down Expand Up @@ -1114,7 +1165,13 @@ static bool CC_RISCV(const DataLayout &DL, unsigned ValNo, MVT ValVT, MVT LocVT,
}

// Allocate to a register if possible, or else a stack slot.
unsigned Reg = State.AllocateReg(ArgGPRs);
unsigned Reg;
if (ValVT == MVT::f32 && !UseGPRForF32)
Reg = State.AllocateReg(ArgFPR32s, ArgFPR64s);
else if (ValVT == MVT::f64 && !UseGPRForF64)
Reg = State.AllocateReg(ArgFPR64s, ArgFPR32s);
else
Reg = Reg = State.AllocateReg(ArgGPRs);
unsigned StackOffset = Reg ? 0 : State.AllocateStack(XLen / 8, XLen / 8);

// If we reach this point and PendingLocs is non-empty, we must be at the
Expand All @@ -1135,7 +1192,8 @@ static bool CC_RISCV(const DataLayout &DL, unsigned ValNo, MVT ValVT, MVT LocVT,
return false;
}

assert(LocVT == XLenVT && "Expected an XLenVT at this stage");
assert((!UseGPRForF32 || !UseGPRForF64 || LocVT == XLenVT) &&
"Expected an XLenVT at this stage");

if (Reg) {
State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
Expand Down Expand Up @@ -1167,7 +1225,8 @@ void RISCVTargetLowering::analyzeInputArgs(
else if (Ins[i].isOrigArg())
ArgTy = FType->getParamType(Ins[i].getOrigArgIndex());

if (CC_RISCV(MF.getDataLayout(), i, ArgVT, ArgVT, CCValAssign::Full,
RISCVABI::ABI ABI = MF.getSubtarget<RISCVSubtarget>().getTargetABI();
if (CC_RISCV(MF.getDataLayout(), ABI, i, ArgVT, ArgVT, CCValAssign::Full,
ArgFlags, CCInfo, /*IsRet=*/true, IsRet, ArgTy)) {
LLVM_DEBUG(dbgs() << "InputArg #" << i << " has unhandled type "
<< EVT(ArgVT).getEVTString() << '\n');
Expand All @@ -1187,7 +1246,8 @@ void RISCVTargetLowering::analyzeOutputArgs(
ISD::ArgFlagsTy ArgFlags = Outs[i].Flags;
Type *OrigTy = CLI ? CLI->getArgs()[Outs[i].OrigArgIndex].Ty : nullptr;

if (CC_RISCV(MF.getDataLayout(), i, ArgVT, ArgVT, CCValAssign::Full,
RISCVABI::ABI ABI = MF.getSubtarget<RISCVSubtarget>().getTargetABI();
if (CC_RISCV(MF.getDataLayout(), ABI, i, ArgVT, ArgVT, CCValAssign::Full,
ArgFlags, CCInfo, Outs[i].IsFixed, IsRet, OrigTy)) {
LLVM_DEBUG(dbgs() << "OutputArg #" << i << " has unhandled type "
<< EVT(ArgVT).getEVTString() << "\n");
Expand Down Expand Up @@ -1224,8 +1284,24 @@ static SDValue unpackFromRegLoc(SelectionDAG &DAG, SDValue Chain,
MachineRegisterInfo &RegInfo = MF.getRegInfo();
EVT LocVT = VA.getLocVT();
SDValue Val;
const TargetRegisterClass *RC;

switch (LocVT.getSimpleVT().SimpleTy) {
default:
llvm_unreachable("Unexpected register type");
case MVT::i32:
case MVT::i64:
RC = &RISCV::GPRRegClass;
break;
case MVT::f32:
RC = &RISCV::FPR32RegClass;
break;
case MVT::f64:
RC = &RISCV::FPR64RegClass;
break;
}

unsigned VReg = RegInfo.createVirtualRegister(&RISCV::GPRRegClass);
unsigned VReg = RegInfo.createVirtualRegister(RC);
RegInfo.addLiveIn(VA.getLocReg(), VReg);
Val = DAG.getCopyFromReg(Chain, DL, VReg, LocVT);

Expand Down Expand Up @@ -1802,8 +1878,9 @@ bool RISCVTargetLowering::CanLowerReturn(
for (unsigned i = 0, e = Outs.size(); i != e; ++i) {
MVT VT = Outs[i].VT;
ISD::ArgFlagsTy ArgFlags = Outs[i].Flags;
if (CC_RISCV(MF.getDataLayout(), i, VT, VT, CCValAssign::Full, ArgFlags,
CCInfo, /*IsFixed=*/true, /*IsRet=*/true, nullptr))
RISCVABI::ABI ABI = MF.getSubtarget<RISCVSubtarget>().getTargetABI();
if (CC_RISCV(MF.getDataLayout(), ABI, i, VT, VT, CCValAssign::Full,
ArgFlags, CCInfo, /*IsFixed=*/true, /*IsRet=*/true, nullptr))
return false;
}
return true;
Expand Down
30 changes: 28 additions & 2 deletions llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,20 @@ RISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
return CSR_XLEN_F32_Interrupt_SaveList;
return CSR_Interrupt_SaveList;
}
return CSR_ILP32_LP64_SaveList;

switch (Subtarget.getTargetABI()) {
default:
llvm_unreachable("Unrecognized ABI");
case RISCVABI::ABI_ILP32:
case RISCVABI::ABI_LP64:
return CSR_ILP32_LP64_SaveList;
case RISCVABI::ABI_ILP32F:
case RISCVABI::ABI_LP64F:
return CSR_ILP32F_LP64F_SaveList;
case RISCVABI::ABI_ILP32D:
case RISCVABI::ABI_LP64D:
return CSR_ILP32D_LP64D_SaveList;
}
}

BitVector RISCVRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
Expand Down Expand Up @@ -127,5 +140,18 @@ RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & MF,
return CSR_XLEN_F32_Interrupt_RegMask;
return CSR_Interrupt_RegMask;
}
return CSR_ILP32_LP64_RegMask;

switch (Subtarget.getTargetABI()) {
default:
llvm_unreachable("Unrecognized ABI");
case RISCVABI::ABI_ILP32:
case RISCVABI::ABI_LP64:
return CSR_ILP32_LP64_RegMask;
case RISCVABI::ABI_ILP32F:
case RISCVABI::ABI_LP64F:
return CSR_ILP32F_LP64F_RegMask;
case RISCVABI::ABI_ILP32D:
case RISCVABI::ABI_LP64D:
return CSR_ILP32D_LP64D_RegMask;
}
}
107 changes: 105 additions & 2 deletions llvm/test/CodeGen/RISCV/callee-saved-fpr32s.ll
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@
; RUN: | FileCheck %s -check-prefix=ILP32-LP64
; RUN: llc -mtriple=riscv64 -mattr=+f -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=ILP32-LP64
; RUN: llc -mtriple=riscv32 -mattr=+f -target-abi ilp32f -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=ILP32F-LP64F
; RUN: llc -mtriple=riscv64 -mattr=+f -target-abi lp64f -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=ILP32F-LP64F
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi ilp32d -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=ILP32D-LP64D
; RUN: llc -mtriple=riscv64 -mattr=+d -target-abi lp64d -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=ILP32D-LP64D

@var = global [32 x float] zeroinitializer

; All floating point registers are temporaries for the ilp32 and lp64 ABIs.
; fs0-fs11 are callee-saved for the ilp32f, ilp32d, lp64f, and lp64d ABIs.

; This function tests that RISCVRegisterInfo::getCalleeSavedRegs returns
; something appropriate.
Expand Down Expand Up @@ -80,6 +89,42 @@ define void @callee() {
; ILP32-LP64-NEXT: fsw ft1, 4(a1)
; ILP32-LP64-NEXT: fsw ft0, %lo(var)(a0)
; ILP32-LP64-NEXT: ret
;
; ILP32F-LP64F-LABEL: callee:
; ILP32F-LP64F: # %bb.0:
; ILP32F-LP64F-NEXT: addi sp, sp, -48
; ILP32F-LP64F-NEXT: fsw fs0, 44(sp)
; ILP32F-LP64F-NEXT: fsw fs1, 40(sp)
; ILP32F-LP64F-NEXT: fsw fs2, 36(sp)
; ILP32F-LP64F-NEXT: fsw fs3, 32(sp)
; ILP32F-LP64F-NEXT: fsw fs4, 28(sp)
; ILP32F-LP64F-NEXT: fsw fs5, 24(sp)
; ILP32F-LP64F-NEXT: fsw fs6, 20(sp)
; ILP32F-LP64F-NEXT: fsw fs7, 16(sp)
; ILP32F-LP64F-NEXT: fsw fs8, 12(sp)
; ILP32F-LP64F-NEXT: fsw fs9, 8(sp)
; ILP32F-LP64F-NEXT: fsw fs10, 4(sp)
; ILP32F-LP64F-NEXT: fsw fs11, 0(sp)
; ILP32F-LP64F-NEXT: lui a0, %hi(var)
; ILP32F-LP64F-NEXT: addi a1, a0, %lo(var)
;
; ILP32D-LP64D-LABEL: callee:
; ILP32D-LP64D: # %bb.0:
; ILP32D-LP64D-NEXT: addi sp, sp, -96
; ILP32D-LP64D-NEXT: fsd fs0, 88(sp)
; ILP32D-LP64D-NEXT: fsd fs1, 80(sp)
; ILP32D-LP64D-NEXT: fsd fs2, 72(sp)
; ILP32D-LP64D-NEXT: fsd fs3, 64(sp)
; ILP32D-LP64D-NEXT: fsd fs4, 56(sp)
; ILP32D-LP64D-NEXT: fsd fs5, 48(sp)
; ILP32D-LP64D-NEXT: fsd fs6, 40(sp)
; ILP32D-LP64D-NEXT: fsd fs7, 32(sp)
; ILP32D-LP64D-NEXT: fsd fs8, 24(sp)
; ILP32D-LP64D-NEXT: fsd fs9, 16(sp)
; ILP32D-LP64D-NEXT: fsd fs10, 8(sp)
; ILP32D-LP64D-NEXT: fsd fs11, 0(sp)
; ILP32D-LP64D-NEXT: lui a0, %hi(var)
; ILP32D-LP64D-NEXT: addi a1, a0, %lo(var)
%val = load [32 x float], [32 x float]* @var
store volatile [32 x float] %val, [32 x float]* @var
ret void
Expand All @@ -89,17 +134,75 @@ define void @callee() {
; something appropriate.
;
; For the soft float ABIs, no floating point registers are preserved, and
; codegen will use only ft0 in the body of caller.
; codegen will use only ft0 in the body of caller. For the 'f' and 'd ABIs,
; fs0-fs11 are preserved across calls.

define void @caller() {
; ILP32-LP64-LABEL: caller:
; ILP32-LP64-NOT: ft{{[1-9][0-9]*}}
; ILP32-LP64-NOT: fs{{[0-9]+}}
; ILP32-LP64-NOT: fa{{[0-9]+}}
; ILP32-LP64: ret
; ILP32-LP64: call callee
; ILP32-LP64-NOT: ft{{[1-9][0-9]*}}
; ILP32-LP64-NOT: fs{{[0-9]+}}
; ILP32-LP64-NOT: fa{{[0-9]+}}
; ILP32-LP64: ret
;
; ILP32F-LP64F-LABEL: caller:
; ILP32F-LP64F: flw fs8, 80(s1)
; ILP32F-LP64F-NEXT: flw fs9, 84(s1)
; ILP32F-LP64F-NEXT: flw fs10, 88(s1)
; ILP32F-LP64F-NEXT: flw fs11, 92(s1)
; ILP32F-LP64F-NEXT: flw fs0, 96(s1)
; ILP32F-LP64F-NEXT: flw fs1, 100(s1)
; ILP32F-LP64F-NEXT: flw fs2, 104(s1)
; ILP32F-LP64F-NEXT: flw fs3, 108(s1)
; ILP32F-LP64F-NEXT: flw fs4, 112(s1)
; ILP32F-LP64F-NEXT: flw fs5, 116(s1)
; ILP32F-LP64F-NEXT: flw fs6, 120(s1)
; ILP32F-LP64F-NEXT: flw fs7, 124(s1)
; ILP32F-LP64F-NEXT: call callee
; ILP32F-LP64F-NEXT: fsw fs7, 124(s1)
; ILP32F-LP64F-NEXT: fsw fs6, 120(s1)
; ILP32F-LP64F-NEXT: fsw fs5, 116(s1)
; ILP32F-LP64F-NEXT: fsw fs4, 112(s1)
; ILP32F-LP64F-NEXT: fsw fs3, 108(s1)
; ILP32F-LP64F-NEXT: fsw fs2, 104(s1)
; ILP32F-LP64F-NEXT: fsw fs1, 100(s1)
; ILP32F-LP64F-NEXT: fsw fs0, 96(s1)
; ILP32F-LP64F-NEXT: fsw fs11, 92(s1)
; ILP32F-LP64F-NEXT: fsw fs10, 88(s1)
; ILP32F-LP64F-NEXT: fsw fs9, 84(s1)
; ILP32F-LP64F-NEXT: fsw fs8, 80(s1)
; ILP32F-LP64F-NEXT: lw ft0, {{[0-9]+}}(sp)
;
; ILP32D-LP64D-LABEL: caller:
; ILP32D-LP64D: flw fs8, 80(s1)
; ILP32D-LP64D-NEXT: flw fs9, 84(s1)
; ILP32D-LP64D-NEXT: flw fs10, 88(s1)
; ILP32D-LP64D-NEXT: flw fs11, 92(s1)
; ILP32D-LP64D-NEXT: flw fs0, 96(s1)
; ILP32D-LP64D-NEXT: flw fs1, 100(s1)
; ILP32D-LP64D-NEXT: flw fs2, 104(s1)
; ILP32D-LP64D-NEXT: flw fs3, 108(s1)
; ILP32D-LP64D-NEXT: flw fs4, 112(s1)
; ILP32D-LP64D-NEXT: flw fs5, 116(s1)
; ILP32D-LP64D-NEXT: flw fs6, 120(s1)
; ILP32D-LP64D-NEXT: flw fs7, 124(s1)
; ILP32D-LP64D-NEXT: call callee
; ILP32D-LP64D-NEXT: fsw fs7, 124(s1)
; ILP32D-LP64D-NEXT: fsw fs6, 120(s1)
; ILP32D-LP64D-NEXT: fsw fs5, 116(s1)
; ILP32D-LP64D-NEXT: fsw fs4, 112(s1)
; ILP32D-LP64D-NEXT: fsw fs3, 108(s1)
; ILP32D-LP64D-NEXT: fsw fs2, 104(s1)
; ILP32D-LP64D-NEXT: fsw fs1, 100(s1)
; ILP32D-LP64D-NEXT: fsw fs0, 96(s1)
; ILP32D-LP64D-NEXT: fsw fs11, 92(s1)
; ILP32D-LP64D-NEXT: fsw fs10, 88(s1)
; ILP32D-LP64D-NEXT: fsw fs9, 84(s1)
; ILP32D-LP64D-NEXT: fsw fs8, 80(s1)
; ILP32D-LP64D-NEXT: flw ft0, {{[0-9]+}}(sp)
%val = load [32 x float], [32 x float]* @var
call void @callee()
store volatile [32 x float] %val, [32 x float]* @var
Expand Down
Loading

0 comments on commit 0b2803e

Please sign in to comment.