[RISCV] Add codegen support for ilp32f, ilp32d, lp64f, and lp64d ("hard 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
This commit is contained in:
Alex Bradbury 2019-03-30 17:59:30 +00:00
parent 10c9032c02
commit 0b2803ee65
14 changed files with 869 additions and 39 deletions

View File

@ -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)>;

View File

@ -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();
@ -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.
@ -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;
@ -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;
}
@ -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
@ -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
@ -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));
@ -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');
@ -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");
@ -1224,8 +1284,24 @@ static SDValue unpackFromRegLoc(SelectionDAG &DAG, SDValue Chain,
MachineRegisterInfo &RegInfo = MF.getRegInfo();
EVT LocVT = VA.getLocVT();
SDValue Val;
const TargetRegisterClass *RC;
unsigned VReg = RegInfo.createVirtualRegister(&RISCV::GPRRegClass);
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(RC);
RegInfo.addLiveIn(VA.getLocReg(), VReg);
Val = DAG.getCopyFromReg(Chain, DL, VReg, LocVT);
@ -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;

View File

@ -40,7 +40,20 @@ RISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
return CSR_XLEN_F32_Interrupt_SaveList;
return CSR_Interrupt_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 {
@ -127,5 +140,18 @@ RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & MF,
return CSR_XLEN_F32_Interrupt_RegMask;
return CSR_Interrupt_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;
}
}

View File

@ -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.
@ -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
@ -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

View File

@ -1,12 +1,16 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv32 -mattr=+d -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=ILP32-LP64
; RUN: llc -mtriple=riscv64 -mattr=+d -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=ILP32-LP64
; 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 double] 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.
@ -81,6 +85,24 @@ define void @callee() {
; ILP32-LP64-NEXT: fsd ft1, 8(a1)
; ILP32-LP64-NEXT: fsd ft0, %lo(var)(a0)
; ILP32-LP64-NEXT: ret
;
; 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 double], [32 x double]* @var
store volatile [32 x double] %val, [32 x double]* @var
ret void
@ -90,17 +112,47 @@ 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-LP64D-LABEL: caller:
; ILP32D-LP64D: fld fs8, 160(s1)
; ILP32D-LP64D-NEXT: fld fs9, 168(s1)
; ILP32D-LP64D-NEXT: fld fs10, 176(s1)
; ILP32D-LP64D-NEXT: fld fs11, 184(s1)
; ILP32D-LP64D-NEXT: fld fs0, 192(s1)
; ILP32D-LP64D-NEXT: fld fs1, 200(s1)
; ILP32D-LP64D-NEXT: fld fs2, 208(s1)
; ILP32D-LP64D-NEXT: fld fs3, 216(s1)
; ILP32D-LP64D-NEXT: fld fs4, 224(s1)
; ILP32D-LP64D-NEXT: fld fs5, 232(s1)
; ILP32D-LP64D-NEXT: fld fs6, 240(s1)
; ILP32D-LP64D-NEXT: fld fs7, 248(s1)
; ILP32D-LP64D-NEXT: call callee
; ILP32D-LP64D-NEXT: fsd fs7, 248(s1)
; ILP32D-LP64D-NEXT: fsd fs6, 240(s1)
; ILP32D-LP64D-NEXT: fsd fs5, 232(s1)
; ILP32D-LP64D-NEXT: fsd fs4, 224(s1)
; ILP32D-LP64D-NEXT: fsd fs3, 216(s1)
; ILP32D-LP64D-NEXT: fsd fs2, 208(s1)
; ILP32D-LP64D-NEXT: fsd fs1, 200(s1)
; ILP32D-LP64D-NEXT: fsd fs0, 192(s1)
; ILP32D-LP64D-NEXT: fsd fs11, 184(s1)
; ILP32D-LP64D-NEXT: fsd fs10, 176(s1)
; ILP32D-LP64D-NEXT: fsd fs9, 168(s1)
; ILP32D-LP64D-NEXT: fsd fs8, 160(s1)
; ILP32D-LP64D-NEXT: fld ft0, {{[0-9]+}}(sp)
%val = load [32 x double], [32 x double]* @var
call void @callee()
store volatile [32 x double] %val, [32 x double]* @var

View File

@ -1,9 +1,21 @@
; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=RV32I
; RUN: llc -mtriple=riscv32 -mattr=+f -target-abi ilp32f -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=RV32I
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi ilp32f -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=RV32I
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi ilp32d -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=RV32I
; RUN: llc -mtriple=riscv32 -verify-machineinstrs -frame-pointer=all < %s \
; RUN: | FileCheck %s -check-prefix=RV32I-WITH-FP
; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=RV64I
; RUN: llc -mtriple=riscv64 -mattr=+f -target-abi ilp32f -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=RV64I
; RUN: llc -mtriple=riscv64 -mattr=+d -target-abi ilp32f -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=RV64I
; RUN: llc -mtriple=riscv64 -mattr=+d -target-abi ilp32d -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=RV64I
; RUN: llc -mtriple=riscv64 -verify-machineinstrs -frame-pointer=all < %s \
; RUN: | FileCheck %s -check-prefix=RV64I-WITH-FP

View File

@ -1,8 +1,14 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV32I-FPELIM %s
; RUN: llc -mtriple=riscv32 -mattr=+f -target-abi ilp32f \
; RUN: -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV32I-FPELIM %s
; RUN: llc -mtriple=riscv32 -verify-machineinstrs -frame-pointer=all < %s \
; RUN: | FileCheck -check-prefix=RV32I-WITHFP %s
; RUN: llc -mtriple=riscv32 -verify-machineinstrs -frame-pointer=all \
; RUN: -mattr=+f -target-abi ilp32f < %s \
; RUN: | FileCheck -check-prefix=RV32I-WITHFP %s
; This file contains tests that should have identical output for the ilp32,
; and ilp32f. As well as calling convention details, we check that

View File

@ -1,8 +1,20 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV32I-FPELIM %s
; RUN: llc -mtriple=riscv32 -mattr=+f -target-abi ilp32f \
; RUN: -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV32I-FPELIM %s
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi ilp32d \
; RUN: -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV32I-FPELIM %s
; RUN: llc -mtriple=riscv32 -verify-machineinstrs -frame-pointer=all < %s \
; RUN: | FileCheck -check-prefix=RV32I-WITHFP %s
; RUN: llc -mtriple=riscv32 -verify-machineinstrs -frame-pointer=all \
; RUN: -mattr=+f -target-abi ilp32f < %s \
; RUN: | FileCheck -check-prefix=RV32I-WITHFP %s
; RUN: llc -mtriple=riscv32 -verify-machineinstrs -frame-pointer=all \
; RUN: -mattr=+d -target-abi ilp32d < %s \
; RUN: | FileCheck -check-prefix=RV32I-WITHFP %s
; This file contains tests that should have identical output for the ilp32,
; ilp32f, and ilp32d ABIs. i.e. where no arguments are passed according to

View File

@ -0,0 +1,294 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv32 -verify-machineinstrs -mattr=+d \
; RUN: -target-abi ilp32d < %s \
; RUN: | FileCheck -check-prefix=RV32-ILP32D %s
; This file contains tests that will have differing output for the ilp32 and
; ilp32f ABIs.
define i32 @callee_double_in_fpr(i32 %a, double %b) nounwind {
; RV32-ILP32D-LABEL: callee_double_in_fpr:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: fcvt.w.d a1, fa0, rtz
; RV32-ILP32D-NEXT: add a0, a0, a1
; RV32-ILP32D-NEXT: ret
%b_fptosi = fptosi double %b to i32
%1 = add i32 %a, %b_fptosi
ret i32 %1
}
define i32 @caller_double_in_fpr() nounwind {
; RV32-ILP32D-LABEL: caller_double_in_fpr:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: addi sp, sp, -16
; RV32-ILP32D-NEXT: sw ra, 12(sp)
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI1_0)
; RV32-ILP32D-NEXT: addi a0, a0, %lo(.LCPI1_0)
; RV32-ILP32D-NEXT: fld fa0, 0(a0)
; RV32-ILP32D-NEXT: addi a0, zero, 1
; RV32-ILP32D-NEXT: call callee_double_in_fpr
; RV32-ILP32D-NEXT: lw ra, 12(sp)
; RV32-ILP32D-NEXT: addi sp, sp, 16
; RV32-ILP32D-NEXT: ret
%1 = call i32 @callee_double_in_fpr(i32 1, double 2.0)
ret i32 %1
}
; Must keep define on a single line due to an update_llc_test_checks.py limitation
define i32 @callee_double_in_fpr_exhausted_gprs(i64 %a, i64 %b, i64 %c, i64 %d, i32 %e, double %f) nounwind {
; RV32-ILP32D-LABEL: callee_double_in_fpr_exhausted_gprs:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: fcvt.w.d a0, fa0, rtz
; RV32-ILP32D-NEXT: lw a1, 0(sp)
; RV32-ILP32D-NEXT: add a0, a1, a0
; RV32-ILP32D-NEXT: ret
%f_fptosi = fptosi double %f to i32
%1 = add i32 %e, %f_fptosi
ret i32 %1
}
define i32 @caller_double_in_fpr_exhausted_gprs() nounwind {
; RV32-ILP32D-LABEL: caller_double_in_fpr_exhausted_gprs:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: addi sp, sp, -16
; RV32-ILP32D-NEXT: sw ra, 12(sp)
; RV32-ILP32D-NEXT: addi a0, zero, 5
; RV32-ILP32D-NEXT: sw a0, 0(sp)
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI3_0)
; RV32-ILP32D-NEXT: addi a0, a0, %lo(.LCPI3_0)
; RV32-ILP32D-NEXT: fld fa0, 0(a0)
; RV32-ILP32D-NEXT: addi a0, zero, 1
; RV32-ILP32D-NEXT: mv a1, zero
; RV32-ILP32D-NEXT: addi a2, zero, 2
; RV32-ILP32D-NEXT: mv a3, zero
; RV32-ILP32D-NEXT: addi a4, zero, 3
; RV32-ILP32D-NEXT: mv a5, zero
; RV32-ILP32D-NEXT: addi a6, zero, 4
; RV32-ILP32D-NEXT: mv a7, zero
; RV32-ILP32D-NEXT: call callee_double_in_fpr_exhausted_gprs
; RV32-ILP32D-NEXT: lw ra, 12(sp)
; RV32-ILP32D-NEXT: addi sp, sp, 16
; RV32-ILP32D-NEXT: ret
%1 = call i32 @callee_double_in_fpr_exhausted_gprs(
i64 1, i64 2, i64 3, i64 4, i32 5, double 6.0)
ret i32 %1
}
; Must keep define on a single line due to an update_llc_test_checks.py limitation
define i32 @callee_double_in_gpr_exhausted_fprs(double %a, double %b, double %c, double %d, double %e, double %f, double %g, double %h, double %i) nounwind {
; RV32-ILP32D-LABEL: callee_double_in_gpr_exhausted_fprs:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: addi sp, sp, -16
; RV32-ILP32D-NEXT: sw a0, 8(sp)
; RV32-ILP32D-NEXT: sw a1, 12(sp)
; RV32-ILP32D-NEXT: fld ft0, 8(sp)
; RV32-ILP32D-NEXT: fcvt.w.d a0, ft0, rtz
; RV32-ILP32D-NEXT: fcvt.w.d a1, fa7, rtz
; RV32-ILP32D-NEXT: add a0, a1, a0
; RV32-ILP32D-NEXT: addi sp, sp, 16
; RV32-ILP32D-NEXT: ret
%h_fptosi = fptosi double %h to i32
%i_fptosi = fptosi double %i to i32
%1 = add i32 %h_fptosi, %i_fptosi
ret i32 %1
}
define i32 @caller_double_in_gpr_exhausted_fprs() nounwind {
; RV32-ILP32D-LABEL: caller_double_in_gpr_exhausted_fprs:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: addi sp, sp, -16
; RV32-ILP32D-NEXT: sw ra, 12(sp)
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI5_0)
; RV32-ILP32D-NEXT: addi a0, a0, %lo(.LCPI5_0)
; RV32-ILP32D-NEXT: lui a1, %hi(.LCPI5_1)
; RV32-ILP32D-NEXT: addi a1, a1, %lo(.LCPI5_1)
; RV32-ILP32D-NEXT: lui a2, %hi(.LCPI5_2)
; RV32-ILP32D-NEXT: addi a2, a2, %lo(.LCPI5_2)
; RV32-ILP32D-NEXT: lui a3, %hi(.LCPI5_3)
; RV32-ILP32D-NEXT: addi a3, a3, %lo(.LCPI5_3)
; RV32-ILP32D-NEXT: lui a4, %hi(.LCPI5_4)
; RV32-ILP32D-NEXT: addi a4, a4, %lo(.LCPI5_4)
; RV32-ILP32D-NEXT: lui a5, %hi(.LCPI5_5)
; RV32-ILP32D-NEXT: addi a5, a5, %lo(.LCPI5_5)
; RV32-ILP32D-NEXT: fld fa0, 0(a5)
; RV32-ILP32D-NEXT: fld fa1, 0(a4)
; RV32-ILP32D-NEXT: fld fa2, 0(a3)
; RV32-ILP32D-NEXT: fld fa3, 0(a2)
; RV32-ILP32D-NEXT: fld fa4, 0(a1)
; RV32-ILP32D-NEXT: fld fa5, 0(a0)
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI5_6)
; RV32-ILP32D-NEXT: addi a0, a0, %lo(.LCPI5_6)
; RV32-ILP32D-NEXT: fld fa6, 0(a0)
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI5_7)
; RV32-ILP32D-NEXT: addi a0, a0, %lo(.LCPI5_7)
; RV32-ILP32D-NEXT: fld fa7, 0(a0)
; RV32-ILP32D-NEXT: mv a0, zero
; RV32-ILP32D-NEXT: lui a1, 262688
; RV32-ILP32D-NEXT: call callee_double_in_gpr_exhausted_fprs
; RV32-ILP32D-NEXT: lw ra, 12(sp)
; RV32-ILP32D-NEXT: addi sp, sp, 16
; RV32-ILP32D-NEXT: ret
%1 = call i32 @callee_double_in_gpr_exhausted_fprs(
double 1.0, double 2.0, double 3.0, double 4.0, double 5.0, double 6.0,
double 7.0, double 8.0, double 9.0)
ret i32 %1
}
; Must keep define on a single line due to an update_llc_test_checks.py limitation
define i32 @callee_double_in_gpr_and_stack_almost_exhausted_gprs_fprs(i64 %a, double %b, i64 %c, double %d, i64 %e, double %f, i32 %g, double %h, double %i, double %j, double %k, double %l, double %m) nounwind {
; RV32-ILP32D-LABEL: callee_double_in_gpr_and_stack_almost_exhausted_gprs_fprs:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: addi sp, sp, -16
; RV32-ILP32D-NEXT: lw a0, 16(sp)
; RV32-ILP32D-NEXT: sw a7, 8(sp)
; RV32-ILP32D-NEXT: sw a0, 12(sp)
; RV32-ILP32D-NEXT: fld ft0, 8(sp)
; RV32-ILP32D-NEXT: fcvt.w.d a0, ft0, rtz
; RV32-ILP32D-NEXT: add a0, a6, a0
; RV32-ILP32D-NEXT: addi sp, sp, 16
; RV32-ILP32D-NEXT: ret
%m_fptosi = fptosi double %m to i32
%1 = add i32 %g, %m_fptosi
ret i32 %1
}
define i32 @caller_double_in_gpr_and_stack_almost_exhausted_gprs_fprs() nounwind {
; RV32-ILP32D-LABEL: caller_double_in_gpr_and_stack_almost_exhausted_gprs_fprs:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: addi sp, sp, -16
; RV32-ILP32D-NEXT: sw ra, 12(sp)
; RV32-ILP32D-NEXT: lui a0, 262816
; RV32-ILP32D-NEXT: sw a0, 0(sp)
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI7_0)
; RV32-ILP32D-NEXT: addi a6, a0, %lo(.LCPI7_0)
; RV32-ILP32D-NEXT: lui a1, %hi(.LCPI7_1)
; RV32-ILP32D-NEXT: addi a1, a1, %lo(.LCPI7_1)
; RV32-ILP32D-NEXT: lui a2, %hi(.LCPI7_2)
; RV32-ILP32D-NEXT: addi a2, a2, %lo(.LCPI7_2)
; RV32-ILP32D-NEXT: lui a3, %hi(.LCPI7_3)
; RV32-ILP32D-NEXT: addi a3, a3, %lo(.LCPI7_3)
; RV32-ILP32D-NEXT: lui a4, %hi(.LCPI7_4)
; RV32-ILP32D-NEXT: addi a4, a4, %lo(.LCPI7_4)
; RV32-ILP32D-NEXT: lui a5, %hi(.LCPI7_5)
; RV32-ILP32D-NEXT: addi a5, a5, %lo(.LCPI7_5)
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI7_6)
; RV32-ILP32D-NEXT: addi a0, a0, %lo(.LCPI7_6)
; RV32-ILP32D-NEXT: fld fa0, 0(a0)
; RV32-ILP32D-NEXT: fld fa1, 0(a5)
; RV32-ILP32D-NEXT: fld fa2, 0(a4)
; RV32-ILP32D-NEXT: fld fa3, 0(a3)
; RV32-ILP32D-NEXT: fld fa4, 0(a2)
; RV32-ILP32D-NEXT: fld fa5, 0(a1)
; RV32-ILP32D-NEXT: fld fa6, 0(a6)
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI7_7)
; RV32-ILP32D-NEXT: addi a0, a0, %lo(.LCPI7_7)
; RV32-ILP32D-NEXT: fld fa7, 0(a0)
; RV32-ILP32D-NEXT: addi a0, zero, 1
; RV32-ILP32D-NEXT: mv a1, zero
; RV32-ILP32D-NEXT: addi a2, zero, 3
; RV32-ILP32D-NEXT: mv a3, zero
; RV32-ILP32D-NEXT: addi a4, zero, 5
; RV32-ILP32D-NEXT: mv a5, zero
; RV32-ILP32D-NEXT: addi a6, zero, 7
; RV32-ILP32D-NEXT: mv a7, zero
; RV32-ILP32D-NEXT: call callee_double_in_gpr_and_stack_almost_exhausted_gprs_fprs
; RV32-ILP32D-NEXT: lw ra, 12(sp)
; RV32-ILP32D-NEXT: addi sp, sp, 16
; RV32-ILP32D-NEXT: ret
%1 = call i32 @callee_double_in_gpr_and_stack_almost_exhausted_gprs_fprs(
i64 1, double 2.0, i64 3, double 4.0, i64 5, double 6.0, i32 7, double 8.0,
double 9.0, double 10.0, double 11.0, double 12.0, double 13.0)
ret i32 %1
}
; Must keep define on a single line due to an update_llc_test_checks.py limitation
define i32 @callee_double_on_stack_exhausted_gprs_fprs(i64 %a, double %b, i64 %c, double %d, i64 %e, double %f, i64 %g, double %h, double %i, double %j, double %k, double %l, double %m) nounwind {
; RV32-ILP32D-LABEL: callee_double_on_stack_exhausted_gprs_fprs:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: fld ft0, 0(sp)
; RV32-ILP32D-NEXT: fcvt.w.d a0, ft0, rtz
; RV32-ILP32D-NEXT: add a0, a6, a0
; RV32-ILP32D-NEXT: ret
%g_trunc = trunc i64 %g to i32
%m_fptosi = fptosi double %m to i32
%1 = add i32 %g_trunc, %m_fptosi
ret i32 %1
}
define i32 @caller_double_on_stack_exhausted_gprs_fprs() nounwind {
; RV32-ILP32D-LABEL: caller_double_on_stack_exhausted_gprs_fprs:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: addi sp, sp, -16
; RV32-ILP32D-NEXT: sw ra, 12(sp)
; RV32-ILP32D-NEXT: lui a0, 262816
; RV32-ILP32D-NEXT: sw a0, 4(sp)
; RV32-ILP32D-NEXT: sw zero, 0(sp)
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI9_0)
; RV32-ILP32D-NEXT: addi a6, a0, %lo(.LCPI9_0)
; RV32-ILP32D-NEXT: lui a1, %hi(.LCPI9_1)
; RV32-ILP32D-NEXT: addi a1, a1, %lo(.LCPI9_1)
; RV32-ILP32D-NEXT: lui a2, %hi(.LCPI9_2)
; RV32-ILP32D-NEXT: addi a2, a2, %lo(.LCPI9_2)
; RV32-ILP32D-NEXT: lui a3, %hi(.LCPI9_3)
; RV32-ILP32D-NEXT: addi a3, a3, %lo(.LCPI9_3)
; RV32-ILP32D-NEXT: lui a4, %hi(.LCPI9_4)
; RV32-ILP32D-NEXT: addi a4, a4, %lo(.LCPI9_4)
; RV32-ILP32D-NEXT: lui a5, %hi(.LCPI9_5)
; RV32-ILP32D-NEXT: addi a5, a5, %lo(.LCPI9_5)
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI9_6)
; RV32-ILP32D-NEXT: addi a0, a0, %lo(.LCPI9_6)
; RV32-ILP32D-NEXT: fld fa0, 0(a0)
; RV32-ILP32D-NEXT: fld fa1, 0(a5)
; RV32-ILP32D-NEXT: fld fa2, 0(a4)
; RV32-ILP32D-NEXT: fld fa3, 0(a3)
; RV32-ILP32D-NEXT: fld fa4, 0(a2)
; RV32-ILP32D-NEXT: fld fa5, 0(a1)
; RV32-ILP32D-NEXT: fld fa6, 0(a6)
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI9_7)
; RV32-ILP32D-NEXT: addi a0, a0, %lo(.LCPI9_7)
; RV32-ILP32D-NEXT: fld fa7, 0(a0)
; RV32-ILP32D-NEXT: addi a0, zero, 1
; RV32-ILP32D-NEXT: mv a1, zero
; RV32-ILP32D-NEXT: addi a2, zero, 3
; RV32-ILP32D-NEXT: mv a3, zero
; RV32-ILP32D-NEXT: addi a4, zero, 5
; RV32-ILP32D-NEXT: mv a5, zero
; RV32-ILP32D-NEXT: addi a6, zero, 7
; RV32-ILP32D-NEXT: mv a7, zero
; RV32-ILP32D-NEXT: call callee_double_on_stack_exhausted_gprs_fprs
; RV32-ILP32D-NEXT: lw ra, 12(sp)
; RV32-ILP32D-NEXT: addi sp, sp, 16
; RV32-ILP32D-NEXT: ret
%1 = call i32 @callee_double_on_stack_exhausted_gprs_fprs(
i64 1, double 2.0, i64 3, double 4.0, i64 5, double 6.0, i64 7, double 8.0,
double 9.0, double 10.0, double 11.0, double 12.0, double 13.0)
ret i32 %1
}
define double @callee_double_ret() nounwind {
; RV32-ILP32D-LABEL: callee_double_ret:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: lui a0, %hi(.LCPI10_0)
; RV32-ILP32D-NEXT: addi a0, a0, %lo(.LCPI10_0)
; RV32-ILP32D-NEXT: fld fa0, 0(a0)
; RV32-ILP32D-NEXT: ret
ret double 1.0
}
define i32 @caller_double_ret() nounwind {
; RV32-ILP32D-LABEL: caller_double_ret:
; RV32-ILP32D: # %bb.0:
; RV32-ILP32D-NEXT: addi sp, sp, -16
; RV32-ILP32D-NEXT: sw ra, 12(sp)
; RV32-ILP32D-NEXT: call callee_double_ret
; RV32-ILP32D-NEXT: fsd fa0, 0(sp)
; RV32-ILP32D-NEXT: lw a0, 0(sp)
; RV32-ILP32D-NEXT: lw ra, 12(sp)
; RV32-ILP32D-NEXT: addi sp, sp, 16
; RV32-ILP32D-NEXT: ret
%1 = call double @callee_double_ret()
%2 = bitcast double %1 to i64
%3 = trunc i64 %2 to i32
ret i32 %3
}

View File

@ -0,0 +1,221 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv32 -verify-machineinstrs -mattr=+f \
; RUN: -target-abi ilp32f < %s \
; RUN: | FileCheck -check-prefix=RV32-ILP32FD %s
; RUN: llc -mtriple=riscv32 -verify-machineinstrs -mattr=+d \
; RUN: -target-abi ilp32d < %s \
; RUN: | FileCheck -check-prefix=RV32-ILP32FD %s
; This file contains tests that should have identical output for the ilp32f
; and ilp32d ABIs.
define i32 @callee_float_in_fpr(i32 %a, float %b) nounwind {
; RV32-ILP32FD-LABEL: callee_float_in_fpr:
; RV32-ILP32FD: # %bb.0:
; RV32-ILP32FD-NEXT: fcvt.w.s a1, fa0, rtz
; RV32-ILP32FD-NEXT: add a0, a0, a1
; RV32-ILP32FD-NEXT: ret
%b_fptosi = fptosi float %b to i32
%1 = add i32 %a, %b_fptosi
ret i32 %1
}
define i32 @caller_float_in_fpr() nounwind {
; RV32-ILP32FD-LABEL: caller_float_in_fpr:
; RV32-ILP32FD: # %bb.0:
; RV32-ILP32FD-NEXT: addi sp, sp, -16
; RV32-ILP32FD-NEXT: sw ra, 12(sp)
; RV32-ILP32FD-NEXT: lui a0, %hi(.LCPI1_0)
; RV32-ILP32FD-NEXT: addi a0, a0, %lo(.LCPI1_0)
; RV32-ILP32FD-NEXT: flw fa0, 0(a0)
; RV32-ILP32FD-NEXT: addi a0, zero, 1
; RV32-ILP32FD-NEXT: call callee_float_in_fpr
; RV32-ILP32FD-NEXT: lw ra, 12(sp)
; RV32-ILP32FD-NEXT: addi sp, sp, 16
; RV32-ILP32FD-NEXT: ret
%1 = call i32 @callee_float_in_fpr(i32 1, float 2.0)
ret i32 %1
}
; Must keep define on a single line due to an update_llc_test_checks.py limitation
define i32 @callee_float_in_fpr_exhausted_gprs(i64 %a, i64 %b, i64 %c, i64 %d, i32 %e, float %f) nounwind {
; RV32-ILP32FD-LABEL: callee_float_in_fpr_exhausted_gprs:
; RV32-ILP32FD: # %bb.0:
; RV32-ILP32FD-NEXT: fcvt.w.s a0, fa0, rtz
; RV32-ILP32FD-NEXT: lw a1, 0(sp)
; RV32-ILP32FD-NEXT: add a0, a1, a0
; RV32-ILP32FD-NEXT: ret
%f_fptosi = fptosi float %f to i32
%1 = add i32 %e, %f_fptosi
ret i32 %1
}
define i32 @caller_float_in_fpr_exhausted_gprs() nounwind {
; RV32-ILP32FD-LABEL: caller_float_in_fpr_exhausted_gprs:
; RV32-ILP32FD: # %bb.0:
; RV32-ILP32FD-NEXT: addi sp, sp, -16
; RV32-ILP32FD-NEXT: sw ra, 12(sp)
; RV32-ILP32FD-NEXT: addi a0, zero, 5
; RV32-ILP32FD-NEXT: sw a0, 0(sp)
; RV32-ILP32FD-NEXT: lui a0, %hi(.LCPI3_0)
; RV32-ILP32FD-NEXT: addi a0, a0, %lo(.LCPI3_0)
; RV32-ILP32FD-NEXT: flw fa0, 0(a0)
; RV32-ILP32FD-NEXT: addi a0, zero, 1
; RV32-ILP32FD-NEXT: mv a1, zero
; RV32-ILP32FD-NEXT: addi a2, zero, 2
; RV32-ILP32FD-NEXT: mv a3, zero
; RV32-ILP32FD-NEXT: addi a4, zero, 3
; RV32-ILP32FD-NEXT: mv a5, zero
; RV32-ILP32FD-NEXT: addi a6, zero, 4
; RV32-ILP32FD-NEXT: mv a7, zero
; RV32-ILP32FD-NEXT: call callee_float_in_fpr_exhausted_gprs
; RV32-ILP32FD-NEXT: lw ra, 12(sp)
; RV32-ILP32FD-NEXT: addi sp, sp, 16
; RV32-ILP32FD-NEXT: ret
%1 = call i32 @callee_float_in_fpr_exhausted_gprs(
i64 1, i64 2, i64 3, i64 4, i32 5, float 6.0)
ret i32 %1
}
; Must keep define on a single line due to an update_llc_test_checks.py limitation
define i32 @callee_float_in_gpr_exhausted_fprs(float %a, float %b, float %c, float %d, float %e, float %f, float %g, float %h, float %i) nounwind {
; RV32-ILP32FD-LABEL: callee_float_in_gpr_exhausted_fprs:
; RV32-ILP32FD: # %bb.0:
; RV32-ILP32FD-NEXT: fcvt.w.s a1, fa7, rtz
; RV32-ILP32FD-NEXT: fmv.w.x ft0, a0
; RV32-ILP32FD-NEXT: fcvt.w.s a0, ft0, rtz
; RV32-ILP32FD-NEXT: add a0, a1, a0
; RV32-ILP32FD-NEXT: ret
%h_fptosi = fptosi float %h to i32
%i_fptosi = fptosi float %i to i32
%1 = add i32 %h_fptosi, %i_fptosi
ret i32 %1
}
define i32 @caller_float_in_gpr_exhausted_fprs() nounwind {
; RV32-ILP32FD-LABEL: caller_float_in_gpr_exhausted_fprs:
; RV32-ILP32FD: # %bb.0:
; RV32-ILP32FD-NEXT: addi sp, sp, -16
; RV32-ILP32FD-NEXT: sw ra, 12(sp)
; RV32-ILP32FD-NEXT: lui a0, %hi(.LCPI5_0)
; RV32-ILP32FD-NEXT: addi a0, a0, %lo(.LCPI5_0)
; RV32-ILP32FD-NEXT: lui a1, %hi(.LCPI5_1)
; RV32-ILP32FD-NEXT: addi a1, a1, %lo(.LCPI5_1)
; RV32-ILP32FD-NEXT: lui a2, %hi(.LCPI5_2)
; RV32-ILP32FD-NEXT: addi a2, a2, %lo(.LCPI5_2)
; RV32-ILP32FD-NEXT: lui a3, %hi(.LCPI5_3)
; RV32-ILP32FD-NEXT: addi a3, a3, %lo(.LCPI5_3)
; RV32-ILP32FD-NEXT: lui a4, %hi(.LCPI5_4)
; RV32-ILP32FD-NEXT: addi a4, a4, %lo(.LCPI5_4)
; RV32-ILP32FD-NEXT: lui a5, %hi(.LCPI5_5)
; RV32-ILP32FD-NEXT: addi a5, a5, %lo(.LCPI5_5)
; RV32-ILP32FD-NEXT: flw fa0, 0(a5)
; RV32-ILP32FD-NEXT: flw fa1, 0(a4)
; RV32-ILP32FD-NEXT: flw fa2, 0(a3)
; RV32-ILP32FD-NEXT: flw fa3, 0(a2)
; RV32-ILP32FD-NEXT: flw fa4, 0(a1)
; RV32-ILP32FD-NEXT: flw fa5, 0(a0)
; RV32-ILP32FD-NEXT: lui a0, %hi(.LCPI5_6)
; RV32-ILP32FD-NEXT: addi a0, a0, %lo(.LCPI5_6)
; RV32-ILP32FD-NEXT: flw fa6, 0(a0)
; RV32-ILP32FD-NEXT: lui a0, %hi(.LCPI5_7)
; RV32-ILP32FD-NEXT: addi a0, a0, %lo(.LCPI5_7)
; RV32-ILP32FD-NEXT: flw fa7, 0(a0)
; RV32-ILP32FD-NEXT: lui a0, 266496
; RV32-ILP32FD-NEXT: call callee_float_in_gpr_exhausted_fprs
; RV32-ILP32FD-NEXT: lw ra, 12(sp)
; RV32-ILP32FD-NEXT: addi sp, sp, 16
; RV32-ILP32FD-NEXT: ret
%1 = call i32 @callee_float_in_gpr_exhausted_fprs(
float 1.0, float 2.0, float 3.0, float 4.0, float 5.0, float 6.0,
float 7.0, float 8.0, float 9.0)
ret i32 %1
}
; Must keep define on a single line due to an update_llc_test_checks.py limitation
define i32 @callee_float_on_stack_exhausted_gprs_fprs(i64 %a, float %b, i64 %c, float %d, i64 %e, float %f, i64 %g, float %h, float %i, float %j, float %k, float %l, float %m) nounwind {
; RV32-ILP32FD-LABEL: callee_float_on_stack_exhausted_gprs_fprs:
; RV32-ILP32FD: # %bb.0:
; RV32-ILP32FD-NEXT: flw ft0, 0(sp)
; RV32-ILP32FD-NEXT: fcvt.w.s a0, ft0, rtz
; RV32-ILP32FD-NEXT: add a0, a6, a0
; RV32-ILP32FD-NEXT: ret
%g_trunc = trunc i64 %g to i32
%m_fptosi = fptosi float %m to i32
%1 = add i32 %g_trunc, %m_fptosi
ret i32 %1
}
define i32 @caller_float_on_stack_exhausted_gprs_fprs() nounwind {
; RV32-ILP32FD-LABEL: caller_float_on_stack_exhausted_gprs_fprs:
; RV32-ILP32FD: # %bb.0:
; RV32-ILP32FD-NEXT: addi sp, sp, -16
; RV32-ILP32FD-NEXT: sw ra, 12(sp)
; RV32-ILP32FD-NEXT: lui a0, 267520
; RV32-ILP32FD-NEXT: sw a0, 0(sp)
; RV32-ILP32FD-NEXT: lui a0, %hi(.LCPI7_0)
; RV32-ILP32FD-NEXT: addi a6, a0, %lo(.LCPI7_0)
; RV32-ILP32FD-NEXT: lui a1, %hi(.LCPI7_1)
; RV32-ILP32FD-NEXT: addi a1, a1, %lo(.LCPI7_1)
; RV32-ILP32FD-NEXT: lui a2, %hi(.LCPI7_2)
; RV32-ILP32FD-NEXT: addi a2, a2, %lo(.LCPI7_2)
; RV32-ILP32FD-NEXT: lui a3, %hi(.LCPI7_3)
; RV32-ILP32FD-NEXT: addi a3, a3, %lo(.LCPI7_3)
; RV32-ILP32FD-NEXT: lui a4, %hi(.LCPI7_4)
; RV32-ILP32FD-NEXT: addi a4, a4, %lo(.LCPI7_4)
; RV32-ILP32FD-NEXT: lui a5, %hi(.LCPI7_5)
; RV32-ILP32FD-NEXT: addi a5, a5, %lo(.LCPI7_5)
; RV32-ILP32FD-NEXT: lui a0, %hi(.LCPI7_6)
; RV32-ILP32FD-NEXT: addi a0, a0, %lo(.LCPI7_6)
; RV32-ILP32FD-NEXT: flw fa0, 0(a0)
; RV32-ILP32FD-NEXT: flw fa1, 0(a5)
; RV32-ILP32FD-NEXT: flw fa2, 0(a4)
; RV32-ILP32FD-NEXT: flw fa3, 0(a3)
; RV32-ILP32FD-NEXT: flw fa4, 0(a2)
; RV32-ILP32FD-NEXT: flw fa5, 0(a1)
; RV32-ILP32FD-NEXT: flw fa6, 0(a6)
; RV32-ILP32FD-NEXT: lui a0, %hi(.LCPI7_7)
; RV32-ILP32FD-NEXT: addi a0, a0, %lo(.LCPI7_7)
; RV32-ILP32FD-NEXT: flw fa7, 0(a0)
; RV32-ILP32FD-NEXT: addi a0, zero, 1
; RV32-ILP32FD-NEXT: mv a1, zero
; RV32-ILP32FD-NEXT: addi a2, zero, 3
; RV32-ILP32FD-NEXT: mv a3, zero
; RV32-ILP32FD-NEXT: addi a4, zero, 5
; RV32-ILP32FD-NEXT: mv a5, zero
; RV32-ILP32FD-NEXT: addi a6, zero, 7
; RV32-ILP32FD-NEXT: mv a7, zero
; RV32-ILP32FD-NEXT: call callee_float_on_stack_exhausted_gprs_fprs
; RV32-ILP32FD-NEXT: lw ra, 12(sp)
; RV32-ILP32FD-NEXT: addi sp, sp, 16
; RV32-ILP32FD-NEXT: ret
%1 = call i32 @callee_float_on_stack_exhausted_gprs_fprs(
i64 1, float 2.0, i64 3, float 4.0, i64 5, float 6.0, i64 7, float 8.0,
float 9.0, float 10.0, float 11.0, float 12.0, float 13.0)
ret i32 %1
}
define float @callee_float_ret() nounwind {
; RV32-ILP32FD-LABEL: callee_float_ret:
; RV32-ILP32FD: # %bb.0:
; RV32-ILP32FD-NEXT: lui a0, %hi(.LCPI8_0)
; RV32-ILP32FD-NEXT: addi a0, a0, %lo(.LCPI8_0)
; RV32-ILP32FD-NEXT: flw fa0, 0(a0)
; RV32-ILP32FD-NEXT: ret
ret float 1.0
}
define i32 @caller_float_ret() nounwind {
; RV32-ILP32FD-LABEL: caller_float_ret:
; RV32-ILP32FD: # %bb.0:
; RV32-ILP32FD-NEXT: addi sp, sp, -16
; RV32-ILP32FD-NEXT: sw ra, 12(sp)
; RV32-ILP32FD-NEXT: call callee_float_ret
; RV32-ILP32FD-NEXT: fmv.x.w a0, fa0
; RV32-ILP32FD-NEXT: lw ra, 12(sp)
; RV32-ILP32FD-NEXT: addi sp, sp, 16
; RV32-ILP32FD-NEXT: ret
%1 = call float @callee_float_ret()
%2 = bitcast float %1 to i32
ret i32 %2
}

View File

@ -1,6 +1,9 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV64I %s
; RUN: llc -mtriple=riscv64 -mattr=+f -target-abi lp64f \
; RUN: -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV64I %s
; This file contains tests that should have identical output for the lp64 and
; lp64f ABIs. It doesn't check codegen when frame pointer elimination is

View File

@ -1,6 +1,12 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV64I %s
; RUN: llc -mtriple=riscv64 -mattr=+f -target-abi lp64f \
; RUN: -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV64I %s
; RUN: llc -mtriple=riscv64 -mattr=+d -target-abi lp64d \
; RUN: -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV64I %s
; This file contains tests that should have identical output for the lp64,
; lp64f, and lp64d ABIs. i.e. where no arguments are passed according to

View File

@ -14,6 +14,18 @@
; RUN: | FileCheck -check-prefix=CHECK-IMP %s
; RUN: llc -mtriple=riscv64 -mattr=+d -target-abi lp64 < %s \
; RUN: | FileCheck -check-prefix=CHECK-IMP %s
; RUN: llc -mtriple=riscv32 -mattr=+f -target-abi ilp32f < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-IMP %s
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi ilp32f < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-IMP %s
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi ilp32d < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-IMP %s
; RUN: llc -mtriple=riscv64 -mattr=+f -target-abi lp64f < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-IMP %s
; RUN: llc -mtriple=riscv64 -mattr=+d -target-abi lp64f < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-IMP %s
; RUN: llc -mtriple=riscv64 -mattr=+d -target-abi lp64d < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-IMP %s
define void @nothing() nounwind {
; CHECK-IMP-LABEL: nothing:
@ -22,19 +34,7 @@ define void @nothing() nounwind {
ret void
}
; RUN: not llc -mtriple=riscv32 -mattr=+f -target-abi ilp32f < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-UNIMP %s
; RUN: not llc -mtriple=riscv32 -mattr=+d -target-abi ilp32f < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-UNIMP %s
; RUN: not llc -mtriple=riscv32 -mattr=+d -target-abi ilp32d < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-UNIMP %s
; RUN: not llc -mtriple=riscv32 -target-abi ilp32e < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-UNIMP %s
; RUN: not llc -mtriple=riscv64 -mattr=+f -target-abi lp64f < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-UNIMP %s
; RUN: not llc -mtriple=riscv64 -mattr=+d -target-abi lp64f < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-UNIMP %s
; RUN: not llc -mtriple=riscv64 -mattr=+d -target-abi lp64d < %s 2>&1 \
; RUN: | FileCheck -check-prefix=CHECK-UNIMP %s
; CHECK-UNIMP: LLVM ERROR: Don't know how to lower this ABI

View File

@ -5,16 +5,26 @@
; RUN: | FileCheck -check-prefix=ILP32-ILP32F-WITHFP %s
; RUN: llc -mtriple=riscv32 -mattr=+d -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV32D-ILP32-ILP32F-ILP32D-FPELIM %s
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi ilp32f \
; RUN: -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV32D-ILP32-ILP32F-ILP32D-FPELIM %s
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi ilp32d \
; RUN: -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV32D-ILP32-ILP32F-ILP32D-FPELIM %s
; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=LP64-LP64F-LP64D-FPELIM %s
; RUN: llc -mtriple=riscv64 -mattr=+d -target-abi lp64f \
; RUN: -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=LP64-LP64F-LP64D-FPELIM %s
; RUN: llc -mtriple=riscv64 -mattr=+d -target-abi lp64d \
; RUN: -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=LP64-LP64F-LP64D-FPELIM %s
; RUN: llc -mtriple=riscv64 -verify-machineinstrs -frame-pointer=all < %s \
; RUN: | FileCheck -check-prefix=LP64-LP64F-LP64D-WITHFP %s
; TODO: RUN lines for ilp32f/ilp32d/lp64f/lp64d must be added when hard float
; ABI Support lands. The same vararg calling convention is used for
; ilp32/ilp32f/ilp32d and for lp64/lp64f/lp64d. Different CHECK lines are
; required for RV32D due to slight codegen differences due to the way the
; f64 load operations are lowered.
; The same vararg calling convention is used for ilp32/ilp32f/ilp32d and for
; lp64/lp64f/lp64d. Different CHECK lines are required for RV32D due to slight
; codegen differences due to the way the f64 load operations are lowered.
declare void @llvm.va_start(i8*)
declare void @llvm.va_end(i8*)