[llvm-branch-commits] [llvm] f7bc7c2 - [RISCV] Support Zfh half-precision floating-point extension.
Hsiangkai Wang via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Dec 2 17:22:04 PST 2020
Author: Hsiangkai Wang
Date: 2020-12-03T09:16:33+08:00
New Revision: f7bc7c2981d48525acbf34a26dfb450c6098582c
URL: https://github.com/llvm/llvm-project/commit/f7bc7c2981d48525acbf34a26dfb450c6098582c
DIFF: https://github.com/llvm/llvm-project/commit/f7bc7c2981d48525acbf34a26dfb450c6098582c.diff
LOG: [RISCV] Support Zfh half-precision floating-point extension.
Support "Zfh" extension according to
https://github.com/riscv/riscv-isa-manual/blob/zfh/src/zfh.tex
Differential Revision: https://reviews.llvm.org/D90738
Added:
llvm/lib/Target/RISCV/RISCVInstrInfoZfh.td
llvm/test/CodeGen/RISCV/half-arith.ll
llvm/test/CodeGen/RISCV/half-bitmanip-dagcombines.ll
llvm/test/CodeGen/RISCV/half-br-fcmp.ll
llvm/test/CodeGen/RISCV/half-convert.ll
llvm/test/CodeGen/RISCV/half-fcmp.ll
llvm/test/CodeGen/RISCV/half-imm.ll
llvm/test/CodeGen/RISCV/half-intrinsics.ll
llvm/test/CodeGen/RISCV/half-isnan.ll
llvm/test/CodeGen/RISCV/half-mem.ll
llvm/test/CodeGen/RISCV/half-select-fcmp.ll
llvm/test/CodeGen/RISCV/rv32i-rv64i-half.ll
llvm/test/CodeGen/RISCV/rv64f-half-convert.ll
llvm/test/CodeGen/RISCV/zfh-imm.ll
llvm/test/MC/RISCV/rv32zfh-invalid.s
llvm/test/MC/RISCV/rv32zfh-valid.s
llvm/test/MC/RISCV/rv64zfh-invalid.s
llvm/test/MC/RISCV/rv64zfh-valid.s
llvm/test/MC/RISCV/rvzfh-aliases-valid.s
llvm/test/MC/RISCV/rvzfh-pseudos.s
Modified:
llvm/include/llvm/IR/RuntimeLibcalls.def
llvm/lib/CodeGen/TargetLoweringBase.cpp
llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
llvm/lib/Target/RISCV/RISCV.td
llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
llvm/lib/Target/RISCV/RISCVISelLowering.cpp
llvm/lib/Target/RISCV/RISCVISelLowering.h
llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
llvm/lib/Target/RISCV/RISCVInstrInfo.td
llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
llvm/lib/Target/RISCV/RISCVRegisterInfo.td
llvm/lib/Target/RISCV/RISCVSubtarget.h
llvm/test/CodeGen/RISCV/copysign-casts.ll
llvm/test/MC/RISCV/rv32i-invalid.s
Removed:
################################################################################
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.def b/llvm/include/llvm/IR/RuntimeLibcalls.def
index 3329fa85ef3d..c73172612b1e 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.def
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.def
@@ -288,6 +288,7 @@ HANDLE_LIBCALL(FPEXT_F64_F128, "__extenddftf2")
HANDLE_LIBCALL(FPEXT_F32_F128, "__extendsftf2")
HANDLE_LIBCALL(FPEXT_F16_F128, "__extendhftf2")
HANDLE_LIBCALL(FPEXT_F32_F64, "__extendsfdf2")
+HANDLE_LIBCALL(FPEXT_F16_F64, "__extendhfdf2")
HANDLE_LIBCALL(FPEXT_F16_F32, "__gnu_h2f_ieee")
HANDLE_LIBCALL(FPROUND_F32_F16, "__gnu_f2h_ieee")
HANDLE_LIBCALL(FPROUND_F64_F16, "__truncdfhf2")
@@ -302,6 +303,9 @@ HANDLE_LIBCALL(FPROUND_F80_F64, "__truncxfdf2")
HANDLE_LIBCALL(FPROUND_F128_F64, "__trunctfdf2")
HANDLE_LIBCALL(FPROUND_PPCF128_F64, "__gcc_qtod")
HANDLE_LIBCALL(FPROUND_F128_F80, "__trunctfxf2")
+HANDLE_LIBCALL(FPTOSINT_F16_I32, "__fixhfsi")
+HANDLE_LIBCALL(FPTOSINT_F16_I64, "__fixhfdi")
+HANDLE_LIBCALL(FPTOSINT_F16_I128, "__fixhfti")
HANDLE_LIBCALL(FPTOSINT_F32_I32, "__fixsfsi")
HANDLE_LIBCALL(FPTOSINT_F32_I64, "__fixsfdi")
HANDLE_LIBCALL(FPTOSINT_F32_I128, "__fixsfti")
@@ -317,6 +321,9 @@ HANDLE_LIBCALL(FPTOSINT_F128_I128, "__fixtfti")
HANDLE_LIBCALL(FPTOSINT_PPCF128_I32, "__gcc_qtou")
HANDLE_LIBCALL(FPTOSINT_PPCF128_I64, "__fixtfdi")
HANDLE_LIBCALL(FPTOSINT_PPCF128_I128, "__fixtfti")
+HANDLE_LIBCALL(FPTOUINT_F16_I32, "__fixunshfsi")
+HANDLE_LIBCALL(FPTOUINT_F16_I64, "__fixunshfdi")
+HANDLE_LIBCALL(FPTOUINT_F16_I128, "__fixunshfti")
HANDLE_LIBCALL(FPTOUINT_F32_I32, "__fixunssfsi")
HANDLE_LIBCALL(FPTOUINT_F32_I64, "__fixunssfdi")
HANDLE_LIBCALL(FPTOUINT_F32_I128, "__fixunssfti")
@@ -332,31 +339,37 @@ HANDLE_LIBCALL(FPTOUINT_F128_I128, "__fixunstfti")
HANDLE_LIBCALL(FPTOUINT_PPCF128_I32, "__fixunstfsi")
HANDLE_LIBCALL(FPTOUINT_PPCF128_I64, "__fixunstfdi")
HANDLE_LIBCALL(FPTOUINT_PPCF128_I128, "__fixunstfti")
+HANDLE_LIBCALL(SINTTOFP_I32_F16, "__floatsihf")
HANDLE_LIBCALL(SINTTOFP_I32_F32, "__floatsisf")
HANDLE_LIBCALL(SINTTOFP_I32_F64, "__floatsidf")
HANDLE_LIBCALL(SINTTOFP_I32_F80, "__floatsixf")
HANDLE_LIBCALL(SINTTOFP_I32_F128, "__floatsitf")
HANDLE_LIBCALL(SINTTOFP_I32_PPCF128, "__gcc_itoq")
+HANDLE_LIBCALL(SINTTOFP_I64_F16, "__floatdihf")
HANDLE_LIBCALL(SINTTOFP_I64_F32, "__floatdisf")
HANDLE_LIBCALL(SINTTOFP_I64_F64, "__floatdidf")
HANDLE_LIBCALL(SINTTOFP_I64_F80, "__floatdixf")
HANDLE_LIBCALL(SINTTOFP_I64_F128, "__floatditf")
HANDLE_LIBCALL(SINTTOFP_I64_PPCF128, "__floatditf")
+HANDLE_LIBCALL(SINTTOFP_I128_F16, "__floattihf")
HANDLE_LIBCALL(SINTTOFP_I128_F32, "__floattisf")
HANDLE_LIBCALL(SINTTOFP_I128_F64, "__floattidf")
HANDLE_LIBCALL(SINTTOFP_I128_F80, "__floattixf")
HANDLE_LIBCALL(SINTTOFP_I128_F128, "__floattitf")
HANDLE_LIBCALL(SINTTOFP_I128_PPCF128, "__floattitf")
+HANDLE_LIBCALL(UINTTOFP_I32_F16, "__floatunsihf")
HANDLE_LIBCALL(UINTTOFP_I32_F32, "__floatunsisf")
HANDLE_LIBCALL(UINTTOFP_I32_F64, "__floatunsidf")
HANDLE_LIBCALL(UINTTOFP_I32_F80, "__floatunsixf")
HANDLE_LIBCALL(UINTTOFP_I32_F128, "__floatunsitf")
HANDLE_LIBCALL(UINTTOFP_I32_PPCF128, "__gcc_utoq")
+HANDLE_LIBCALL(UINTTOFP_I64_F16, "__floatundihf")
HANDLE_LIBCALL(UINTTOFP_I64_F32, "__floatundisf")
HANDLE_LIBCALL(UINTTOFP_I64_F64, "__floatundidf")
HANDLE_LIBCALL(UINTTOFP_I64_F80, "__floatundixf")
HANDLE_LIBCALL(UINTTOFP_I64_F128, "__floatunditf")
HANDLE_LIBCALL(UINTTOFP_I64_PPCF128, "__floatunditf")
+HANDLE_LIBCALL(UINTTOFP_I128_F16, "__floatuntihf")
HANDLE_LIBCALL(UINTTOFP_I128_F32, "__floatuntisf")
HANDLE_LIBCALL(UINTTOFP_I128_F64, "__floatuntidf")
HANDLE_LIBCALL(UINTTOFP_I128_F80, "__floatuntixf")
diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp
index e4d9b46824ac..23b62456e2c3 100644
--- a/llvm/lib/CodeGen/TargetLoweringBase.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp
@@ -224,6 +224,8 @@ RTLIB::Libcall RTLIB::getFPEXT(EVT OpVT, EVT RetVT) {
if (OpVT == MVT::f16) {
if (RetVT == MVT::f32)
return FPEXT_F16_F32;
+ if (RetVT == MVT::f64)
+ return FPEXT_F16_F64;
if (RetVT == MVT::f128)
return FPEXT_F16_F128;
} else if (OpVT == MVT::f32) {
@@ -287,7 +289,14 @@ RTLIB::Libcall RTLIB::getFPROUND(EVT OpVT, EVT RetVT) {
/// getFPTOSINT - Return the FPTOSINT_*_* value for the given types, or
/// UNKNOWN_LIBCALL if there is none.
RTLIB::Libcall RTLIB::getFPTOSINT(EVT OpVT, EVT RetVT) {
- if (OpVT == MVT::f32) {
+ if (OpVT == MVT::f16) {
+ if (RetVT == MVT::i32)
+ return FPTOSINT_F16_I32;
+ if (RetVT == MVT::i64)
+ return FPTOSINT_F16_I64;
+ if (RetVT == MVT::i128)
+ return FPTOSINT_F16_I128;
+ } else if (OpVT == MVT::f32) {
if (RetVT == MVT::i32)
return FPTOSINT_F32_I32;
if (RetVT == MVT::i64)
@@ -329,7 +338,14 @@ RTLIB::Libcall RTLIB::getFPTOSINT(EVT OpVT, EVT RetVT) {
/// getFPTOUINT - Return the FPTOUINT_*_* value for the given types, or
/// UNKNOWN_LIBCALL if there is none.
RTLIB::Libcall RTLIB::getFPTOUINT(EVT OpVT, EVT RetVT) {
- if (OpVT == MVT::f32) {
+ if (OpVT == MVT::f16) {
+ if (RetVT == MVT::i32)
+ return FPTOUINT_F16_I32;
+ if (RetVT == MVT::i64)
+ return FPTOUINT_F16_I64;
+ if (RetVT == MVT::i128)
+ return FPTOUINT_F16_I128;
+ } else if (OpVT == MVT::f32) {
if (RetVT == MVT::i32)
return FPTOUINT_F32_I32;
if (RetVT == MVT::i64)
@@ -372,6 +388,8 @@ RTLIB::Libcall RTLIB::getFPTOUINT(EVT OpVT, EVT RetVT) {
/// UNKNOWN_LIBCALL if there is none.
RTLIB::Libcall RTLIB::getSINTTOFP(EVT OpVT, EVT RetVT) {
if (OpVT == MVT::i32) {
+ if (RetVT == MVT::f16)
+ return SINTTOFP_I32_F16;
if (RetVT == MVT::f32)
return SINTTOFP_I32_F32;
if (RetVT == MVT::f64)
@@ -383,6 +401,8 @@ RTLIB::Libcall RTLIB::getSINTTOFP(EVT OpVT, EVT RetVT) {
if (RetVT == MVT::ppcf128)
return SINTTOFP_I32_PPCF128;
} else if (OpVT == MVT::i64) {
+ if (RetVT == MVT::f16)
+ return SINTTOFP_I64_F16;
if (RetVT == MVT::f32)
return SINTTOFP_I64_F32;
if (RetVT == MVT::f64)
@@ -394,6 +414,8 @@ RTLIB::Libcall RTLIB::getSINTTOFP(EVT OpVT, EVT RetVT) {
if (RetVT == MVT::ppcf128)
return SINTTOFP_I64_PPCF128;
} else if (OpVT == MVT::i128) {
+ if (RetVT == MVT::f16)
+ return SINTTOFP_I128_F16;
if (RetVT == MVT::f32)
return SINTTOFP_I128_F32;
if (RetVT == MVT::f64)
@@ -412,6 +434,8 @@ RTLIB::Libcall RTLIB::getSINTTOFP(EVT OpVT, EVT RetVT) {
/// UNKNOWN_LIBCALL if there is none.
RTLIB::Libcall RTLIB::getUINTTOFP(EVT OpVT, EVT RetVT) {
if (OpVT == MVT::i32) {
+ if (RetVT == MVT::f16)
+ return UINTTOFP_I32_F16;
if (RetVT == MVT::f32)
return UINTTOFP_I32_F32;
if (RetVT == MVT::f64)
@@ -423,6 +447,8 @@ RTLIB::Libcall RTLIB::getUINTTOFP(EVT OpVT, EVT RetVT) {
if (RetVT == MVT::ppcf128)
return UINTTOFP_I32_PPCF128;
} else if (OpVT == MVT::i64) {
+ if (RetVT == MVT::f16)
+ return UINTTOFP_I64_F16;
if (RetVT == MVT::f32)
return UINTTOFP_I64_F32;
if (RetVT == MVT::f64)
@@ -434,6 +460,8 @@ RTLIB::Libcall RTLIB::getUINTTOFP(EVT OpVT, EVT RetVT) {
if (RetVT == MVT::ppcf128)
return UINTTOFP_I64_PPCF128;
} else if (OpVT == MVT::i128) {
+ if (RetVT == MVT::f16)
+ return UINTTOFP_I128_F16;
if (RetVT == MVT::f32)
return UINTTOFP_I128_F32;
if (RetVT == MVT::f64)
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 7874d46386d3..d5692af9e681 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -982,6 +982,11 @@ struct RISCVOperand : public MCParsedAsmOperand {
#define GET_MNEMONIC_SPELL_CHECKER
#include "RISCVGenAsmMatcher.inc"
+static MCRegister convertFPR64ToFPR16(MCRegister Reg) {
+ assert(Reg >= RISCV::F0_D && Reg <= RISCV::F31_D && "Invalid register");
+ return Reg - RISCV::F0_D + RISCV::F0_H;
+}
+
static MCRegister convertFPR64ToFPR32(MCRegister Reg) {
assert(Reg >= RISCV::F0_D && Reg <= RISCV::F31_D && "Invalid register");
return Reg - RISCV::F0_D + RISCV::F0_F;
@@ -1006,6 +1011,12 @@ unsigned RISCVAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp,
Op.Reg.RegNum = convertFPR64ToFPR32(Reg);
return Match_Success;
}
+ // As the parser couldn't
diff erentiate an FPR16 from an FPR64, coerce the
+ // register from FPR64 to FPR16 if necessary.
+ if (IsRegFPR64 && Kind == MCK_FPR16) {
+ Op.Reg.RegNum = convertFPR64ToFPR16(Reg);
+ return Match_Success;
+ }
return Match_InvalidOperand;
}
@@ -1237,10 +1248,12 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
static bool matchRegisterNameHelper(bool IsRV32E, MCRegister &RegNo,
StringRef Name) {
RegNo = MatchRegisterName(Name);
- // The 32- and 64-bit FPRs have the same asm name. Check that the initial
- // match always matches the 64-bit variant, and not the 32-bit one.
+ // The 16-/32- and 64-bit FPRs have the same asm name. Check that the initial
+ // match always matches the 64-bit variant, and not the 16/32-bit one.
+ assert(!(RegNo >= RISCV::F0_H && RegNo <= RISCV::F31_H));
assert(!(RegNo >= RISCV::F0_F && RegNo <= RISCV::F31_F));
// The default FPR register class is based on the tablegen enum ordering.
+ static_assert(RISCV::F0_D < RISCV::F0_H, "FPR matching must be updated");
static_assert(RISCV::F0_D < RISCV::F0_F, "FPR matching must be updated");
if (RegNo == RISCV::NoRegister)
RegNo = MatchRegisterAltName(Name);
@@ -2414,6 +2427,9 @@ bool RISCVAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
case RISCV::PseudoLD:
emitLoadStoreSymbol(Inst, RISCV::LD, IDLoc, Out, /*HasTmpReg=*/false);
return false;
+ case RISCV::PseudoFLH:
+ emitLoadStoreSymbol(Inst, RISCV::FLH, IDLoc, Out, /*HasTmpReg=*/true);
+ return false;
case RISCV::PseudoFLW:
emitLoadStoreSymbol(Inst, RISCV::FLW, IDLoc, Out, /*HasTmpReg=*/true);
return false;
@@ -2432,6 +2448,9 @@ bool RISCVAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
case RISCV::PseudoSD:
emitLoadStoreSymbol(Inst, RISCV::SD, IDLoc, Out, /*HasTmpReg=*/true);
return false;
+ case RISCV::PseudoFSH:
+ emitLoadStoreSymbol(Inst, RISCV::FSH, IDLoc, Out, /*HasTmpReg=*/true);
+ return false;
case RISCV::PseudoFSW:
emitLoadStoreSymbol(Inst, RISCV::FSW, IDLoc, Out, /*HasTmpReg=*/true);
return false;
diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
index d26add046be1..50c45e34e36d 100644
--- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
+++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
@@ -75,6 +75,17 @@ static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, uint64_t RegNo,
return MCDisassembler::Success;
}
+static DecodeStatus DecodeFPR16RegisterClass(MCInst &Inst, uint64_t RegNo,
+ uint64_t Address,
+ const void *Decoder) {
+ if (RegNo >= 32)
+ return MCDisassembler::Fail;
+
+ MCRegister Reg = RISCV::F0_H + RegNo;
+ Inst.addOperand(MCOperand::createReg(Reg));
+ return MCDisassembler::Success;
+}
+
static DecodeStatus DecodeFPR32RegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address,
const void *Decoder) {
diff --git a/llvm/lib/Target/RISCV/RISCV.td b/llvm/lib/Target/RISCV/RISCV.td
index f8093ff6a440..9a24e28ad6e2 100644
--- a/llvm/lib/Target/RISCV/RISCV.td
+++ b/llvm/lib/Target/RISCV/RISCV.td
@@ -41,6 +41,14 @@ def HasStdExtD : Predicate<"Subtarget->hasStdExtD()">,
AssemblerPredicate<(all_of FeatureStdExtD),
"'D' (Double-Precision Floating-Point)">;
+def FeatureExtZfh
+ : SubtargetFeature<"experimental-zfh", "HasStdExtZfh", "true",
+ "'Zfh' (Half-Precision Floating-Point)",
+ [FeatureStdExtF]>;
+def HasStdExtZfh : Predicate<"Subtarget->hasStdExtZfh()">,
+ AssemblerPredicate<(all_of FeatureExtZfh),
+ "'Zfh' (Half-Precision Floating-Point)">;
+
def FeatureStdExtC
: SubtargetFeature<"c", "HasStdExtC", "true",
"'C' (Compressed Instructions)">;
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 9222146c5909..3dd68fa36d45 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -662,14 +662,14 @@ void RISCVFrameLowering::determineCalleeSaves(MachineFunction &MF,
for (unsigned i = 0; CSRegs[i]; ++i)
SavedRegs.set(CSRegs[i]);
- if (MF.getSubtarget<RISCVSubtarget>().hasStdExtD() ||
- MF.getSubtarget<RISCVSubtarget>().hasStdExtF()) {
+ if (MF.getSubtarget<RISCVSubtarget>().hasStdExtF()) {
// If interrupt is enabled, this list contains all FP registers.
const MCPhysReg * Regs = MF.getRegInfo().getCalleeSavedRegs();
for (unsigned i = 0; Regs[i]; ++i)
- if (RISCV::FPR32RegClass.contains(Regs[i]) ||
+ if (RISCV::FPR16RegClass.contains(Regs[i]) ||
+ RISCV::FPR32RegClass.contains(Regs[i]) ||
RISCV::FPR64RegClass.contains(Regs[i]))
SavedRegs.set(Regs[i]);
}
diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index 0f455a58f35c..65395706a40a 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -404,6 +404,7 @@ void RISCVDAGToDAGISel::doPeepholeLoadStoreADDI() {
case RISCV::LHU:
case RISCV::LWU:
case RISCV::LD:
+ case RISCV::FLH:
case RISCV::FLW:
case RISCV::FLD:
BaseOpIdx = 0;
@@ -413,6 +414,7 @@ void RISCVDAGToDAGISel::doPeepholeLoadStoreADDI() {
case RISCV::SH:
case RISCV::SW:
case RISCV::SD:
+ case RISCV::FSH:
case RISCV::FSW:
case RISCV::FSD:
BaseOpIdx = 1;
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 49c81284eba7..8797e3484439 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -82,6 +82,8 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
// Set up the register classes.
addRegisterClass(XLenVT, &RISCV::GPRRegClass);
+ if (Subtarget.hasStdExtZfh())
+ addRegisterClass(MVT::f16, &RISCV::FPR16RegClass);
if (Subtarget.hasStdExtF())
addRegisterClass(MVT::f32, &RISCV::FPR32RegClass);
if (Subtarget.hasStdExtD())
@@ -203,6 +205,21 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOW, ISD::FREM, ISD::FP16_TO_FP,
ISD::FP_TO_FP16};
+ if (Subtarget.hasStdExtZfh())
+ setOperationAction(ISD::BITCAST, MVT::i16, Custom);
+
+ if (Subtarget.hasStdExtZfh()) {
+ setOperationAction(ISD::FMINNUM, MVT::f16, Legal);
+ setOperationAction(ISD::FMAXNUM, MVT::f16, Legal);
+ for (auto CC : FPCCToExtend)
+ setCondCodeAction(CC, MVT::f16, Expand);
+ setOperationAction(ISD::SELECT_CC, MVT::f16, Expand);
+ setOperationAction(ISD::SELECT, MVT::f16, Custom);
+ setOperationAction(ISD::BR_CC, MVT::f16, Expand);
+ for (auto Op : FPOpToExtend)
+ setOperationAction(Op, MVT::f16, Expand);
+ }
+
if (Subtarget.hasStdExtF()) {
setOperationAction(ISD::FMINNUM, MVT::f32, Legal);
setOperationAction(ISD::FMAXNUM, MVT::f32, Legal);
@@ -403,6 +420,8 @@ bool RISCVTargetLowering::isCheapToSpeculateCtlz() const {
bool RISCVTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT,
bool ForCodeSize) const {
+ if (VT == MVT::f16 && !Subtarget.hasStdExtZfh())
+ return false;
if (VT == MVT::f32 && !Subtarget.hasStdExtF())
return false;
if (VT == MVT::f64 && !Subtarget.hasStdExtD())
@@ -413,7 +432,8 @@ bool RISCVTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT,
}
bool RISCVTargetLowering::hasBitPreservingFPLogic(EVT VT) const {
- return (VT == MVT::f32 && Subtarget.hasStdExtF()) ||
+ return (VT == MVT::f16 && Subtarget.hasStdExtZfh()) ||
+ (VT == MVT::f32 && Subtarget.hasStdExtF()) ||
(VT == MVT::f64 && Subtarget.hasStdExtD());
}
@@ -484,15 +504,33 @@ SDValue RISCVTargetLowering::LowerOperation(SDValue Op,
case ISD::SRL_PARTS:
return lowerShiftRightParts(Op, DAG, false);
case ISD::BITCAST: {
- assert(Subtarget.is64Bit() && Subtarget.hasStdExtF() &&
+ assert(((Subtarget.is64Bit() && Subtarget.hasStdExtF()) ||
+ Subtarget.hasStdExtZfh()) &&
"Unexpected custom legalisation");
SDLoc DL(Op);
SDValue Op0 = Op.getOperand(0);
- if (Op.getValueType() != MVT::f32 || Op0.getValueType() != MVT::i32)
- return SDValue();
- SDValue NewOp0 = DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op0);
- SDValue FPConv = DAG.getNode(RISCVISD::FMV_W_X_RV64, DL, MVT::f32, NewOp0);
- return FPConv;
+ if (Op.getValueType() == MVT::f16 && Subtarget.hasStdExtZfh()) {
+ if (Op0.getValueType() != MVT::i16)
+ return SDValue();
+ unsigned Opcode = RISCVISD::FMV_H_X_RV32;
+ EVT ExtType = MVT::i32;
+ if (Subtarget.is64Bit()) {
+ Opcode = RISCVISD::FMV_H_X_RV64;
+ ExtType = MVT::i64;
+ }
+ SDValue NewOp0 = DAG.getNode(ISD::ANY_EXTEND, DL, ExtType, Op0);
+ SDValue FPConv = DAG.getNode(Opcode, DL, MVT::f16, NewOp0);
+ return FPConv;
+ } else if (Op.getValueType() == MVT::f32 && Subtarget.is64Bit() &&
+ Subtarget.hasStdExtF()) {
+ if (Op0.getValueType() != MVT::i32)
+ return SDValue();
+ SDValue NewOp0 = DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op0);
+ SDValue FPConv =
+ DAG.getNode(RISCVISD::FMV_W_X_RV64, DL, MVT::f32, NewOp0);
+ return FPConv;
+ }
+ return SDValue();
}
case ISD::INTRINSIC_WO_CHAIN:
return LowerINTRINSIC_WO_CHAIN(Op, DAG);
@@ -1073,14 +1111,30 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
Results.push_back(customLegalizeToWOp(N, DAG));
break;
case ISD::BITCAST: {
- assert(N->getValueType(0) == MVT::i32 && Subtarget.is64Bit() &&
- Subtarget.hasStdExtF() && "Unexpected custom legalisation");
+ assert(((N->getValueType(0) == MVT::i32 && Subtarget.is64Bit() &&
+ Subtarget.hasStdExtF()) ||
+ (N->getValueType(0) == MVT::i16 && Subtarget.hasStdExtZfh())) &&
+ "Unexpected custom legalisation");
SDValue Op0 = N->getOperand(0);
- if (Op0.getValueType() != MVT::f32)
- return;
- SDValue FPConv =
- DAG.getNode(RISCVISD::FMV_X_ANYEXTW_RV64, DL, MVT::i64, Op0);
- Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, FPConv));
+ if (N->getValueType(0) == MVT::i16 && Subtarget.hasStdExtZfh()) {
+ if (Op0.getValueType() != MVT::f16)
+ return;
+ unsigned Opcode = RISCVISD::FMV_X_ANYEXTH_RV32;
+ EVT ExtType = MVT::i32;
+ if (Subtarget.is64Bit()) {
+ Opcode = RISCVISD::FMV_X_ANYEXTH_RV64;
+ ExtType = MVT::i64;
+ }
+ SDValue FPConv = DAG.getNode(Opcode, DL, ExtType, Op0);
+ Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i16, FPConv));
+ } else if (N->getValueType(0) == MVT::i32 && Subtarget.is64Bit() &&
+ Subtarget.hasStdExtF()) {
+ if (Op0.getValueType() != MVT::f32)
+ return;
+ SDValue FPConv =
+ DAG.getNode(RISCVISD::FMV_X_ANYEXTW_RV64, DL, MVT::i64, Op0);
+ Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, FPConv));
+ }
break;
}
case RISCVISD::GREVI:
@@ -1682,6 +1736,7 @@ static bool isSelectPseudo(MachineInstr &MI) {
default:
return false;
case RISCV::Select_GPR_Using_CC_GPR:
+ case RISCV::Select_FPR16_Using_CC_GPR:
case RISCV::Select_FPR32_Using_CC_GPR:
case RISCV::Select_FPR64_Using_CC_GPR:
return true;
@@ -1822,6 +1877,7 @@ RISCVTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
"ReadCycleWrite is only to be used on riscv32");
return emitReadCycleWidePseudo(MI, BB);
case RISCV::Select_GPR_Using_CC_GPR:
+ case RISCV::Select_FPR16_Using_CC_GPR:
case RISCV::Select_FPR32_Using_CC_GPR:
case RISCV::Select_FPR64_Using_CC_GPR:
return emitSelectPseudo(MI, BB);
@@ -1861,6 +1917,10 @@ static const MCPhysReg ArgGPRs[] = {
RISCV::X10, RISCV::X11, RISCV::X12, RISCV::X13,
RISCV::X14, RISCV::X15, RISCV::X16, RISCV::X17
};
+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
@@ -1923,9 +1983,9 @@ static bool CC_RISCV(const DataLayout &DL, RISCVABI::ABI ABI, unsigned ValNo,
if (IsRet && ValNo > 1)
return true;
- // 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;
+ // UseGPRForF16_F32 if targeting one of the soft-float ABIs, if passing a
+ // variadic argument, or if no F16/F32 argument registers are available.
+ bool UseGPRForF16_F32 = 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;
@@ -1938,24 +1998,26 @@ static bool CC_RISCV(const DataLayout &DL, RISCVABI::ABI ABI, unsigned ValNo,
break;
case RISCVABI::ABI_ILP32F:
case RISCVABI::ABI_LP64F:
- UseGPRForF32 = !IsFixed;
+ UseGPRForF16_F32 = !IsFixed;
break;
case RISCVABI::ABI_ILP32D:
case RISCVABI::ABI_LP64D:
- UseGPRForF32 = !IsFixed;
+ UseGPRForF16_F32 = !IsFixed;
UseGPRForF64 = !IsFixed;
break;
}
- if (State.getFirstUnallocated(ArgFPR32s) == array_lengthof(ArgFPR32s))
- UseGPRForF32 = true;
- if (State.getFirstUnallocated(ArgFPR64s) == array_lengthof(ArgFPR64s))
+ // FPR16, FPR32, and FPR64 alias each other.
+ if (State.getFirstUnallocated(ArgFPR32s) == array_lengthof(ArgFPR32s)) {
+ UseGPRForF16_F32 = true;
UseGPRForF64 = true;
+ }
- // From this point on, rely on UseGPRForF32, UseGPRForF64 and similar local
- // variables rather than directly checking against the target ABI.
+ // From this point on, rely on UseGPRForF16_F32, UseGPRForF64 and
+ // similar local variables rather than directly checking against the target
+ // ABI.
- if (UseGPRForF32 && ValVT == MVT::f32) {
+ if (UseGPRForF16_F32 && (ValVT == MVT::f16 || ValVT == MVT::f32)) {
LocVT = XLenVT;
LocInfo = CCValAssign::BCvt;
} else if (UseGPRForF64 && XLen == 64 && ValVT == MVT::f64) {
@@ -2038,7 +2100,9 @@ static bool CC_RISCV(const DataLayout &DL, RISCVABI::ABI ABI, unsigned ValNo,
// Allocate to a register if possible, or else a stack slot.
Register Reg;
- if (ValVT == MVT::f32 && !UseGPRForF32)
+ if (ValVT == MVT::f16 && !UseGPRForF16_F32)
+ Reg = State.AllocateReg(ArgFPR16s);
+ else if (ValVT == MVT::f32 && !UseGPRForF16_F32)
Reg = State.AllocateReg(ArgFPR32s);
else if (ValVT == MVT::f64 && !UseGPRForF64)
Reg = State.AllocateReg(ArgFPR64s);
@@ -2065,7 +2129,7 @@ static bool CC_RISCV(const DataLayout &DL, RISCVABI::ABI ABI, unsigned ValNo,
return false;
}
- assert((!UseGPRForF32 || !UseGPRForF64 || LocVT == XLenVT) &&
+ assert((!UseGPRForF16_F32 || !UseGPRForF64 || LocVT == XLenVT) &&
"Expected an XLenVT at this stage");
if (Reg) {
@@ -2073,8 +2137,9 @@ static bool CC_RISCV(const DataLayout &DL, RISCVABI::ABI ABI, unsigned ValNo,
return false;
}
- // When an f32 or f64 is passed on the stack, no bit-conversion is needed.
- if (ValVT == MVT::f32 || ValVT == MVT::f64) {
+ // When a floating-point value is passed on the stack, no bit-conversion is
+ // needed.
+ if (ValVT.isFloatingPoint()) {
LocVT = ValVT;
LocInfo = CCValAssign::Full;
}
@@ -2139,11 +2204,14 @@ static SDValue convertLocVTToValVT(SelectionDAG &DAG, SDValue Val,
case CCValAssign::Full:
break;
case CCValAssign::BCvt:
- if (VA.getLocVT() == MVT::i64 && VA.getValVT() == MVT::f32) {
+ if (VA.getLocVT() == MVT::i32 && VA.getValVT() == MVT::f16)
+ Val = DAG.getNode(RISCVISD::FMV_H_X_RV32, DL, MVT::f16, Val);
+ else if (VA.getLocVT() == MVT::i64 && VA.getValVT() == MVT::f16)
+ Val = DAG.getNode(RISCVISD::FMV_H_X_RV64, DL, MVT::f16, Val);
+ else if (VA.getLocVT() == MVT::i64 && VA.getValVT() == MVT::f32)
Val = DAG.getNode(RISCVISD::FMV_W_X_RV64, DL, MVT::f32, Val);
- break;
- }
- Val = DAG.getNode(ISD::BITCAST, DL, VA.getValVT(), Val);
+ else
+ Val = DAG.getNode(ISD::BITCAST, DL, VA.getValVT(), Val);
break;
}
return Val;
@@ -2166,6 +2234,9 @@ static SDValue unpackFromRegLoc(SelectionDAG &DAG, SDValue Chain,
case MVT::i64:
RC = &RISCV::GPRRegClass;
break;
+ case MVT::f16:
+ RC = &RISCV::FPR16RegClass;
+ break;
case MVT::f32:
RC = &RISCV::FPR32RegClass;
break;
@@ -2194,11 +2265,14 @@ static SDValue convertValVTToLocVT(SelectionDAG &DAG, SDValue Val,
case CCValAssign::Full:
break;
case CCValAssign::BCvt:
- if (VA.getLocVT() == MVT::i64 && VA.getValVT() == MVT::f32) {
+ if (VA.getLocVT() == MVT::i32 && VA.getValVT() == MVT::f16)
+ Val = DAG.getNode(RISCVISD::FMV_X_ANYEXTH_RV32, DL, MVT::i32, Val);
+ else if (VA.getLocVT() == MVT::i64 && VA.getValVT() == MVT::f16)
+ Val = DAG.getNode(RISCVISD::FMV_X_ANYEXTH_RV64, DL, MVT::i64, Val);
+ else if (VA.getLocVT() == MVT::i64 && VA.getValVT() == MVT::f32)
Val = DAG.getNode(RISCVISD::FMV_X_ANYEXTW_RV64, DL, MVT::i64, Val);
- break;
- }
- Val = DAG.getNode(ISD::BITCAST, DL, LocVT, Val);
+ else
+ Val = DAG.getNode(ISD::BITCAST, DL, LocVT, Val);
break;
}
return Val;
@@ -2289,6 +2363,18 @@ static bool CC_RISCV_FastCC(unsigned ValNo, MVT ValVT, MVT LocVT,
}
}
+ if (LocVT == MVT::f16) {
+ 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 (unsigned Reg = State.AllocateReg(FPR16List)) {
+ State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
+ return false;
+ }
+ }
+
if (LocVT == MVT::f32) {
static const MCPhysReg FPR32List[] = {
RISCV::F10_F, RISCV::F11_F, RISCV::F12_F, RISCV::F13_F, RISCV::F14_F,
@@ -3037,6 +3123,10 @@ const char *RISCVTargetLowering::getTargetNodeName(unsigned Opcode) const {
NODE_NAME_CASE(RORW)
NODE_NAME_CASE(FSLW)
NODE_NAME_CASE(FSRW)
+ NODE_NAME_CASE(FMV_H_X_RV32)
+ NODE_NAME_CASE(FMV_H_X_RV64)
+ NODE_NAME_CASE(FMV_X_ANYEXTH_RV32)
+ NODE_NAME_CASE(FMV_X_ANYEXTH_RV64)
NODE_NAME_CASE(FMV_W_X_RV64)
NODE_NAME_CASE(FMV_X_ANYEXTW_RV64)
NODE_NAME_CASE(READ_CYCLE_WIDE)
@@ -3082,6 +3172,8 @@ RISCVTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
case 'r':
return std::make_pair(0U, &RISCV::GPRRegClass);
case 'f':
+ if (Subtarget.hasStdExtZfh() && VT == MVT::f16)
+ return std::make_pair(0U, &RISCV::FPR16RegClass);
if (Subtarget.hasStdExtF() && VT == MVT::f32)
return std::make_pair(0U, &RISCV::FPR32RegClass);
if (Subtarget.hasStdExtD() && VT == MVT::f64)
@@ -3140,7 +3232,7 @@ RISCVTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
//
// The second case is the ABI name of the register, so that frontends can also
// use the ABI names in register constraint lists.
- if (Subtarget.hasStdExtF() || Subtarget.hasStdExtD()) {
+ if (Subtarget.hasStdExtF()) {
unsigned FReg = StringSwitch<unsigned>(Constraint.lower())
.Cases("{f0}", "{ft0}", RISCV::F0_F)
.Cases("{f1}", "{ft1}", RISCV::F1_F)
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index 065b35874e84..b0365ffcd6cb 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -50,11 +50,21 @@ enum NodeType : unsigned {
// but the same operand order as fshl/fshr intrinsics.
FSRW,
FSLW,
- // FPR32<->GPR transfer operations for RV64. Needed as an i32<->f32 bitcast
- // is not legal on RV64. FMV_W_X_RV64 matches the semantics of the FMV.W.X.
+ // FPR<->GPR transfer operations when the FPR is smaller than XLEN, needed as
+ // XLEN is the only legal integer width.
+ //
+ // FMV_H_X_RV32/RV64 match the semantics of the FMV.H.X.
+ // FMV_X_ANYEXTH_RV32/RV64 are similar to FMV.X.H but has an any-extended
+ // result.
+ // FMV_W_X_RV64 matches the semantics of the FMV.W.X.
// FMV_X_ANYEXTW_RV64 is similar to FMV.X.W but has an any-extended result.
+ //
// This is a more convenient semantic for producing dagcombines that remove
// unnecessary GPR->FPR->GPR moves.
+ FMV_H_X_RV32,
+ FMV_H_X_RV64,
+ FMV_X_ANYEXTH_RV32,
+ FMV_X_ANYEXTH_RV64,
FMV_W_X_RV64,
FMV_X_ANYEXTW_RV64,
// READ_CYCLE_WIDE - A read of the 64-bit cycle CSR on a 32-bit target
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index 084298685672..442c8be841b3 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -45,6 +45,7 @@ unsigned RISCVInstrInfo::isLoadFromStackSlot(const MachineInstr &MI,
case RISCV::LBU:
case RISCV::LH:
case RISCV::LHU:
+ case RISCV::FLH:
case RISCV::LW:
case RISCV::FLW:
case RISCV::LWU:
@@ -70,6 +71,7 @@ unsigned RISCVInstrInfo::isStoreToStackSlot(const MachineInstr &MI,
case RISCV::SB:
case RISCV::SH:
case RISCV::SW:
+ case RISCV::FSH:
case RISCV::FSW:
case RISCV::SD:
case RISCV::FSD:
@@ -98,7 +100,9 @@ void RISCVInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
// FPR->FPR copies
unsigned Opc;
- if (RISCV::FPR32RegClass.contains(DstReg, SrcReg))
+ if (RISCV::FPR16RegClass.contains(DstReg, SrcReg))
+ Opc = RISCV::FSGNJ_H;
+ else if (RISCV::FPR32RegClass.contains(DstReg, SrcReg))
Opc = RISCV::FSGNJ_S;
else if (RISCV::FPR64RegClass.contains(DstReg, SrcReg))
Opc = RISCV::FSGNJ_D;
@@ -129,6 +133,8 @@ void RISCVInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
if (RISCV::GPRRegClass.hasSubClassEq(RC))
Opcode = TRI->getRegSizeInBits(RISCV::GPRRegClass) == 32 ?
RISCV::SW : RISCV::SD;
+ else if (RISCV::FPR16RegClass.hasSubClassEq(RC))
+ Opcode = RISCV::FSH;
else if (RISCV::FPR32RegClass.hasSubClassEq(RC))
Opcode = RISCV::FSW;
else if (RISCV::FPR64RegClass.hasSubClassEq(RC))
@@ -162,6 +168,8 @@ void RISCVInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
if (RISCV::GPRRegClass.hasSubClassEq(RC))
Opcode = TRI->getRegSizeInBits(RISCV::GPRRegClass) == 32 ?
RISCV::LW : RISCV::LD;
+ else if (RISCV::FPR16RegClass.hasSubClassEq(RC))
+ Opcode = RISCV::FLH;
else if (RISCV::FPR32RegClass.hasSubClassEq(RC))
Opcode = RISCV::FLW;
else if (RISCV::FPR64RegClass.hasSubClassEq(RC))
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index 8c3409487806..5733e5e21f67 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -1195,3 +1195,4 @@ include "RISCVInstrInfoD.td"
include "RISCVInstrInfoC.td"
include "RISCVInstrInfoB.td"
include "RISCVInstrInfoV.td"
+include "RISCVInstrInfoZfh.td"
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZfh.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZfh.td
new file mode 100644
index 000000000000..319623b46bee
--- /dev/null
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZfh.td
@@ -0,0 +1,398 @@
+//===-- RISCVInstrInfoFH.td - RISC-V 'FH' instructions -----*- tablegen -*-===//
+//
+// 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 describes the RISC-V instructions from the standard 'Zfh'
+// half-precision floating-point extension, version 0.1.
+// This version is still experimental as the 'Zfh' extension hasn't been
+// ratified yet.
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// RISC-V specific DAG Nodes.
+//===----------------------------------------------------------------------===//
+
+def SDT_RISCVFMV_H_X_RV32
+ : SDTypeProfile<1, 1, [SDTCisVT<0, f16>, SDTCisVT<1, i32>]>;
+def SDT_RISCVFMV_H_X_RV64
+ : SDTypeProfile<1, 1, [SDTCisVT<0, f16>, SDTCisVT<1, i64>]>;
+def SDT_RISCVFMV_X_ANYEXTH_RV64
+ : SDTypeProfile<1, 1, [SDTCisVT<0, i64>, SDTCisVT<1, f16>]>;
+def SDT_RISCVFMV_X_ANYEXTH_RV32
+ : SDTypeProfile<1, 1, [SDTCisVT<0, i32>, SDTCisVT<1, f16>]>;
+
+def riscv_fmv_h_x_rv32
+ : SDNode<"RISCVISD::FMV_H_X_RV32", SDT_RISCVFMV_H_X_RV32>;
+def riscv_fmv_h_x_rv64
+ : SDNode<"RISCVISD::FMV_H_X_RV64", SDT_RISCVFMV_H_X_RV64>;
+def riscv_fmv_x_anyexth_rv64
+ : SDNode<"RISCVISD::FMV_X_ANYEXTH_RV64", SDT_RISCVFMV_X_ANYEXTH_RV64>;
+def riscv_fmv_x_anyexth_rv32
+ : SDNode<"RISCVISD::FMV_X_ANYEXTH_RV32", SDT_RISCVFMV_X_ANYEXTH_RV32>;
+
+//===----------------------------------------------------------------------===//
+// Instruction class templates
+//===----------------------------------------------------------------------===//
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
+class FPFMAH_rrr_frm<RISCVOpcode opcode, string opcodestr>
+ : RVInstR4<0b10, opcode, (outs FPR16:$rd),
+ (ins FPR16:$rs1, FPR16:$rs2, FPR16:$rs3, frmarg:$funct3),
+ opcodestr, "$rd, $rs1, $rs2, $rs3, $funct3">;
+
+class FPFMAHDynFrmAlias<FPFMAH_rrr_frm Inst, string OpcodeStr>
+ : InstAlias<OpcodeStr#" $rd, $rs1, $rs2, $rs3",
+ (Inst FPR16:$rd, FPR16:$rs1, FPR16:$rs2, FPR16:$rs3, 0b111)>;
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
+class FPALUH_rr<bits<7> funct7, bits<3> funct3, string opcodestr>
+ : RVInstR<funct7, funct3, OPC_OP_FP, (outs FPR16:$rd),
+ (ins FPR16:$rs1, FPR16:$rs2), opcodestr, "$rd, $rs1, $rs2">;
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
+class FPALUH_rr_frm<bits<7> funct7, string opcodestr>
+ : RVInstRFrm<funct7, OPC_OP_FP, (outs FPR16:$rd),
+ (ins FPR16:$rs1, FPR16:$rs2, frmarg:$funct3), opcodestr,
+ "$rd, $rs1, $rs2, $funct3">;
+
+class FPALUHDynFrmAlias<FPALUH_rr_frm Inst, string OpcodeStr>
+ : InstAlias<OpcodeStr#" $rd, $rs1, $rs2",
+ (Inst FPR16:$rd, FPR16:$rs1, FPR16:$rs2, 0b111)>;
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
+class FPCmpH_rr<bits<3> funct3, string opcodestr>
+ : RVInstR<0b1010010, funct3, OPC_OP_FP, (outs GPR:$rd),
+ (ins FPR16:$rs1, FPR16:$rs2), opcodestr, "$rd, $rs1, $rs2">,
+ Sched<[]>;
+
+//===----------------------------------------------------------------------===//
+// Instructions
+//===----------------------------------------------------------------------===//
+
+let Predicates = [HasStdExtZfh] in {
+let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in
+def FLH : RVInstI<0b001, OPC_LOAD_FP, (outs FPR16:$rd),
+ (ins GPR:$rs1, simm12:$imm12),
+ "flh", "$rd, ${imm12}(${rs1})">,
+ Sched<[]>;
+
+// Operands for stores are in the order srcreg, base, offset rather than
+// reflecting the order these fields are specified in the instruction
+// encoding.
+let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in
+def FSH : RVInstS<0b001, OPC_STORE_FP, (outs),
+ (ins FPR16:$rs2, GPR:$rs1, simm12:$imm12),
+ "fsh", "$rs2, ${imm12}(${rs1})">,
+ Sched<[]>;
+
+def FMADD_H : FPFMAH_rrr_frm<OPC_MADD, "fmadd.h">,
+ Sched<[]>;
+def : FPFMAHDynFrmAlias<FMADD_H, "fmadd.h">;
+def FMSUB_H : FPFMAH_rrr_frm<OPC_MSUB, "fmsub.h">,
+ Sched<[]>;
+def : FPFMAHDynFrmAlias<FMSUB_H, "fmsub.h">;
+def FNMSUB_H : FPFMAH_rrr_frm<OPC_NMSUB, "fnmsub.h">,
+ Sched<[]>;
+def : FPFMAHDynFrmAlias<FNMSUB_H, "fnmsub.h">;
+def FNMADD_H : FPFMAH_rrr_frm<OPC_NMADD, "fnmadd.h">,
+ Sched<[]>;
+def : FPFMAHDynFrmAlias<FNMADD_H, "fnmadd.h">;
+
+def FADD_H : FPALUH_rr_frm<0b0000010, "fadd.h">,
+ Sched<[]>;
+def : FPALUHDynFrmAlias<FADD_H, "fadd.h">;
+def FSUB_H : FPALUH_rr_frm<0b0000110, "fsub.h">,
+ Sched<[]>;
+def : FPALUHDynFrmAlias<FSUB_H, "fsub.h">;
+def FMUL_H : FPALUH_rr_frm<0b0001010, "fmul.h">,
+ Sched<[]>;
+def : FPALUHDynFrmAlias<FMUL_H, "fmul.h">;
+def FDIV_H : FPALUH_rr_frm<0b0001110, "fdiv.h">,
+ Sched<[]>;
+def : FPALUHDynFrmAlias<FDIV_H, "fdiv.h">;
+
+def FSQRT_H : FPUnaryOp_r_frm<0b0101110, FPR16, FPR16, "fsqrt.h">,
+ Sched<[]> {
+ let rs2 = 0b00000;
+}
+def : FPUnaryOpDynFrmAlias<FSQRT_H, "fsqrt.h", FPR16, FPR16>;
+
+def FSGNJ_H : FPALUH_rr<0b0010010, 0b000, "fsgnj.h">,
+ Sched<[]>;
+def FSGNJN_H : FPALUH_rr<0b0010010, 0b001, "fsgnjn.h">,
+ Sched<[]>;
+def FSGNJX_H : FPALUH_rr<0b0010010, 0b010, "fsgnjx.h">,
+ Sched<[]>;
+
+def FMIN_H : FPALUH_rr<0b0010110, 0b000, "fmin.h">,
+ Sched<[]>;
+def FMAX_H : FPALUH_rr<0b0010110, 0b001, "fmax.h">,
+ Sched<[]>;
+
+def FCVT_W_H : FPUnaryOp_r_frm<0b1100010, GPR, FPR16, "fcvt.w.h">,
+ Sched<[]> {
+ let rs2 = 0b00000;
+}
+def : FPUnaryOpDynFrmAlias<FCVT_W_H, "fcvt.w.h", GPR, FPR16>;
+
+def FCVT_WU_H : FPUnaryOp_r_frm<0b1100010, GPR, FPR16, "fcvt.wu.h">,
+ Sched<[]> {
+ let rs2 = 0b00001;
+}
+def : FPUnaryOpDynFrmAlias<FCVT_WU_H, "fcvt.wu.h", GPR, FPR16>;
+
+def FCVT_H_W : FPUnaryOp_r_frm<0b1101010, FPR16, GPR, "fcvt.h.w">,
+ Sched<[]> {
+ let rs2 = 0b00000;
+}
+def : FPUnaryOpDynFrmAlias<FCVT_H_W, "fcvt.h.w", FPR16, GPR>;
+
+def FCVT_H_WU : FPUnaryOp_r_frm<0b1101010, FPR16, GPR, "fcvt.h.wu">,
+ Sched<[]> {
+ let rs2 = 0b00001;
+}
+def : FPUnaryOpDynFrmAlias<FCVT_H_WU, "fcvt.h.wu", FPR16, GPR>;
+
+def FCVT_H_S : FPUnaryOp_r_frm<0b0100010, FPR16, FPR32, "fcvt.h.s">,
+ Sched<[]> {
+ let rs2 = 0b00000;
+}
+def : FPUnaryOpDynFrmAlias<FCVT_H_S, "fcvt.h.s", FPR16, FPR32>;
+
+def FCVT_S_H : FPUnaryOp_r<0b0100000, 0b000, FPR32, FPR16, "fcvt.s.h">,
+ Sched<[]> {
+ let rs2 = 0b00010;
+}
+
+def FMV_X_H : FPUnaryOp_r<0b1110010, 0b000, GPR, FPR16, "fmv.x.h">,
+ Sched<[]> {
+ let rs2 = 0b00000;
+}
+
+def FMV_H_X : FPUnaryOp_r<0b1111010, 0b000, FPR16, GPR, "fmv.h.x">,
+ Sched<[]> {
+ let rs2 = 0b00000;
+}
+
+def FEQ_H : FPCmpH_rr<0b010, "feq.h">;
+def FLT_H : FPCmpH_rr<0b001, "flt.h">;
+def FLE_H : FPCmpH_rr<0b000, "fle.h">;
+
+def FCLASS_H : FPUnaryOp_r<0b1110010, 0b001, GPR, FPR16, "fclass.h">,
+ Sched<[]> {
+ let rs2 = 0b00000;
+}
+} // Predicates = [HasStdExtZfh]
+
+let Predicates = [HasStdExtZfh, IsRV64] in {
+def FCVT_L_H : FPUnaryOp_r_frm<0b1100010, GPR, FPR16, "fcvt.l.h">,
+ Sched<[]> {
+ let rs2 = 0b00010;
+}
+def : FPUnaryOpDynFrmAlias<FCVT_L_H, "fcvt.l.h", GPR, FPR16>;
+
+def FCVT_LU_H : FPUnaryOp_r_frm<0b1100010, GPR, FPR16, "fcvt.lu.h">,
+ Sched<[]> {
+ let rs2 = 0b00011;
+}
+def : FPUnaryOpDynFrmAlias<FCVT_LU_H, "fcvt.lu.h", GPR, FPR16>;
+
+def FCVT_H_L : FPUnaryOp_r_frm<0b1101010, FPR16, GPR, "fcvt.h.l">,
+ Sched<[]> {
+ let rs2 = 0b00010;
+}
+def : FPUnaryOpDynFrmAlias<FCVT_H_L, "fcvt.h.l", FPR16, GPR>;
+
+def FCVT_H_LU : FPUnaryOp_r_frm<0b1101010, FPR16, GPR, "fcvt.h.lu">,
+ Sched<[]> {
+ let rs2 = 0b00011;
+}
+def : FPUnaryOpDynFrmAlias<FCVT_H_LU, "fcvt.h.lu", FPR16, GPR>;
+} // Predicates = [HasStdExtZfh, IsRV64]
+
+let Predicates = [HasStdExtZfh, HasStdExtD] in {
+def FCVT_H_D : FPUnaryOp_r_frm<0b0100010, FPR16, FPR64, "fcvt.h.d">,
+ Sched<[]> {
+ let rs2 = 0b00001;
+}
+def : FPUnaryOpDynFrmAlias<FCVT_H_D, "fcvt.h.d", FPR16, FPR64>;
+
+def FCVT_D_H : FPUnaryOp_r<0b0100001, 0b000, FPR64, FPR16, "fcvt.d.h">,
+ Sched<[]> {
+ let rs2 = 0b00010;
+}
+} // Predicates = [HasStdExtZfh, HasStdExtD]
+
+//===----------------------------------------------------------------------===//
+// Assembler Pseudo Instructions (User-Level ISA, Version 2.2, Chapter 20)
+//===----------------------------------------------------------------------===//
+
+let Predicates = [HasStdExtZfh] in {
+def : InstAlias<"flh $rd, (${rs1})", (FLH FPR16:$rd, GPR:$rs1, 0), 0>;
+def : InstAlias<"fsh $rs2, (${rs1})", (FSH FPR16:$rs2, GPR:$rs1, 0), 0>;
+
+def : InstAlias<"fmv.h $rd, $rs", (FSGNJ_H FPR16:$rd, FPR16:$rs, FPR16:$rs)>;
+def : InstAlias<"fabs.h $rd, $rs", (FSGNJX_H FPR16:$rd, FPR16:$rs, FPR16:$rs)>;
+def : InstAlias<"fneg.h $rd, $rs", (FSGNJN_H FPR16:$rd, FPR16:$rs, FPR16:$rs)>;
+
+// fgt.h/fge.h are recognised by the GNU assembler but the canonical
+// flt.h/fle.h forms will always be printed. Therefore, set a zero weight.
+def : InstAlias<"fgt.h $rd, $rs, $rt",
+ (FLT_H GPR:$rd, FPR16:$rt, FPR16:$rs), 0>;
+def : InstAlias<"fge.h $rd, $rs, $rt",
+ (FLE_H GPR:$rd, FPR16:$rt, FPR16:$rs), 0>;
+
+def PseudoFLH : PseudoFloatLoad<"flh", FPR16>;
+def PseudoFSH : PseudoStore<"fsh", FPR16>;
+} // Predicates = [HasStdExtZfh]
+
+//===----------------------------------------------------------------------===//
+// Pseudo-instructions and codegen patterns
+//===----------------------------------------------------------------------===//
+
+/// Generic pattern classes
+class PatFpr16Fpr16<SDPatternOperator OpNode, RVInstR Inst>
+ : Pat<(OpNode FPR16:$rs1, FPR16:$rs2), (Inst $rs1, $rs2)>;
+
+class PatFpr16Fpr16DynFrm<SDPatternOperator OpNode, RVInstRFrm Inst>
+ : Pat<(OpNode FPR16:$rs1, FPR16:$rs2), (Inst $rs1, $rs2, 0b111)>;
+
+let Predicates = [HasStdExtZfh] in {
+
+/// Float constants
+def : Pat<(f16 (fpimm0)), (FMV_H_X X0)>;
+
+/// Float conversion operations
+
+// [u]int32<->float conversion patterns must be gated on IsRV32 or IsRV64, so
+// are defined later.
+
+/// Float arithmetic operations
+
+def : PatFpr16Fpr16DynFrm<fadd, FADD_H>;
+def : PatFpr16Fpr16DynFrm<fsub, FSUB_H>;
+def : PatFpr16Fpr16DynFrm<fmul, FMUL_H>;
+def : PatFpr16Fpr16DynFrm<fdiv, FDIV_H>;
+
+def : Pat<(fsqrt FPR16:$rs1), (FSQRT_H FPR16:$rs1, 0b111)>;
+
+def : Pat<(fneg FPR16:$rs1), (FSGNJN_H $rs1, $rs1)>;
+def : Pat<(fabs FPR16:$rs1), (FSGNJX_H $rs1, $rs1)>;
+
+def : PatFpr16Fpr16<fcopysign, FSGNJ_H>;
+def : Pat<(fcopysign FPR16:$rs1, (fneg FPR16:$rs2)), (FSGNJN_H $rs1, $rs2)>;
+def : Pat<(fcopysign FPR16:$rs1, FPR32:$rs2),
+ (FSGNJ_H $rs1, (FCVT_H_S $rs2, 0b111))>;
+def : Pat<(fcopysign FPR16:$rs1, FPR64:$rs2),
+ (FSGNJ_H $rs1, (FCVT_H_D $rs2, 0b111))>;
+def : Pat<(fcopysign FPR32:$rs1, FPR16:$rs2), (FSGNJ_S $rs1, (FCVT_S_H $rs2))>;
+def : Pat<(fcopysign FPR64:$rs1, FPR16:$rs2), (FSGNJ_D $rs1, (FCVT_D_H $rs2))>;
+
+// fmadd: rs1 * rs2 + rs3
+def : Pat<(fma FPR16:$rs1, FPR16:$rs2, FPR16:$rs3),
+ (FMADD_H $rs1, $rs2, $rs3, 0b111)>;
+
+// fmsub: rs1 * rs2 - rs3
+def : Pat<(fma FPR16:$rs1, FPR16:$rs2, (fneg FPR16:$rs3)),
+ (FMSUB_H FPR16:$rs1, FPR16:$rs2, FPR16:$rs3, 0b111)>;
+
+// fnmsub: -rs1 * rs2 + rs3
+def : Pat<(fma (fneg FPR16:$rs1), FPR16:$rs2, FPR16:$rs3),
+ (FNMSUB_H FPR16:$rs1, FPR16:$rs2, FPR16:$rs3, 0b111)>;
+
+// fnmadd: -rs1 * rs2 - rs3
+def : Pat<(fma (fneg FPR16:$rs1), FPR16:$rs2, (fneg FPR16:$rs3)),
+ (FNMADD_H FPR16:$rs1, FPR16:$rs2, FPR16:$rs3, 0b111)>;
+
+def : PatFpr16Fpr16<fminnum, FMIN_H>;
+def : PatFpr16Fpr16<fmaxnum, FMAX_H>;
+
+/// Setcc
+
+def : PatFpr16Fpr16<seteq, FEQ_H>;
+def : PatFpr16Fpr16<setoeq, FEQ_H>;
+def : PatFpr16Fpr16<setlt, FLT_H>;
+def : PatFpr16Fpr16<setolt, FLT_H>;
+def : PatFpr16Fpr16<setle, FLE_H>;
+def : PatFpr16Fpr16<setole, FLE_H>;
+
+// Define pattern expansions for setcc operations which aren't directly
+// handled by a RISC-V instruction and aren't expanded in the SelectionDAG
+// Legalizer.
+
+def : Pat<(seto FPR16:$rs1, FPR16:$rs2),
+ (AND (FEQ_H FPR16:$rs1, FPR16:$rs1),
+ (FEQ_H FPR16:$rs2, FPR16:$rs2))>;
+def : Pat<(seto FPR16:$rs1, FPR16:$rs1),
+ (FEQ_H $rs1, $rs1)>;
+
+def : Pat<(setuo FPR16:$rs1, FPR16:$rs2),
+ (SLTIU (AND (FEQ_H FPR16:$rs1, FPR16:$rs1),
+ (FEQ_H FPR16:$rs2, FPR16:$rs2)),
+ 1)>;
+def : Pat<(setuo FPR16:$rs1, FPR16:$rs1),
+ (SLTIU (FEQ_H $rs1, $rs1), 1)>;
+
+def Select_FPR16_Using_CC_GPR : SelectCC_rrirr<FPR16, GPR>;
+
+/// Loads
+
+defm : LdPat<load, FLH>;
+
+/// Stores
+
+defm : StPat<store, FSH, FPR16>;
+
+/// Float conversion operations
+// f32 -> f16, f16 -> f32
+def : Pat<(fpround FPR32:$rs1), (FCVT_H_S FPR32:$rs1, 0b111)>;
+def : Pat<(fpextend FPR16:$rs1), (FCVT_S_H FPR16:$rs1)>;
+
+} // Predicates = [HasStdExtZfh]
+
+let Predicates = [HasStdExtZfh, IsRV32] in {
+def : Pat<(riscv_fmv_h_x_rv32 GPR:$src), (FMV_H_X GPR:$src)>;
+def : Pat<(riscv_fmv_x_anyexth_rv32 FPR16:$src), (FMV_X_H FPR16:$src)>;
+
+// float->[u]int. Round-to-zero must be used.
+def : Pat<(fp_to_sint FPR16:$rs1), (FCVT_W_H $rs1, 0b001)>;
+def : Pat<(fp_to_uint FPR16:$rs1), (FCVT_WU_H $rs1, 0b001)>;
+
+// [u]int->float. Match GCC and default to using dynamic rounding mode.
+def : Pat<(sint_to_fp GPR:$rs1), (FCVT_H_W $rs1, 0b111)>;
+def : Pat<(uint_to_fp GPR:$rs1), (FCVT_H_WU $rs1, 0b111)>;
+} // Predicates = [HasStdExtZfh, IsRV32]
+
+let Predicates = [HasStdExtZfh, IsRV64] in {
+def : Pat<(riscv_fmv_h_x_rv64 GPR:$src), (FMV_H_X GPR:$src)>;
+def : Pat<(riscv_fmv_x_anyexth_rv64 FPR16:$src), (FMV_X_H FPR16:$src)>;
+
+// FP->[u]int32 is mostly handled by the FP->[u]int64 patterns. This is safe
+// because fpto[u|s]i produces poison if the value can't fit into the target.
+// We match the single case below because fcvt.wu.s sign-extends its result so
+// is cheaper than fcvt.lu.h+sext.w.
+def : Pat<(sext_inreg (assertzexti32 (fp_to_uint FPR16:$rs1)), i32),
+ (FCVT_WU_H $rs1, 0b001)>;
+
+// FP->[u]int64
+def : Pat<(fp_to_sint FPR16:$rs1), (FCVT_L_H $rs1, 0b001)>;
+def : Pat<(fp_to_uint FPR16:$rs1), (FCVT_LU_H $rs1, 0b001)>;
+
+// [u]int->fp. Match GCC and default to using dynamic rounding mode.
+def : Pat<(sint_to_fp (sext_inreg GPR:$rs1, i32)), (FCVT_H_W $rs1, 0b111)>;
+def : Pat<(uint_to_fp (zexti32 GPR:$rs1)), (FCVT_H_WU $rs1, 0b111)>;
+def : Pat<(sint_to_fp GPR:$rs1), (FCVT_H_L $rs1, 0b111)>;
+def : Pat<(uint_to_fp GPR:$rs1), (FCVT_H_LU $rs1, 0b111)>;
+} // Predicates = [HasStdExtZfh, IsRV64]
+
+let Predicates = [HasStdExtZfh, HasStdExtD] in {
+/// Float conversion operations
+// f64 -> f16, f16 -> f64
+def : Pat<(fpround FPR64:$rs1), (FCVT_H_D FPR64:$rs1, 0b111)>;
+def : Pat<(fpextend FPR16:$rs1), (FCVT_D_H FPR16:$rs1)>;
+}
diff --git a/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp b/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
index 6c78c47e8551..87586023caa4 100644
--- a/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
+++ b/llvm/lib/Target/RISCV/RISCVMergeBaseOffset.cpp
@@ -216,12 +216,14 @@ bool RISCVMergeBaseOffsetOpt::detectAndFoldOffset(MachineInstr &HiLUI,
case RISCV::LHU:
case RISCV::LWU:
case RISCV::LD:
+ case RISCV::FLH:
case RISCV::FLW:
case RISCV::FLD:
case RISCV::SB:
case RISCV::SH:
case RISCV::SW:
case RISCV::SD:
+ case RISCV::FSH:
case RISCV::FSW:
case RISCV::FSD: {
// Transforms the sequence: Into:
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
index 05a2bd3f1feb..9590fd46581a 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
@@ -29,6 +29,9 @@ using namespace llvm;
static_assert(RISCV::X1 == RISCV::X0 + 1, "Register list not consecutive");
static_assert(RISCV::X31 == RISCV::X0 + 31, "Register list not consecutive");
+static_assert(RISCV::F1_H == RISCV::F0_H + 1, "Register list not consecutive");
+static_assert(RISCV::F31_H == RISCV::F0_H + 31,
+ "Register list not consecutive");
static_assert(RISCV::F1_F == RISCV::F0_F + 1, "Register list not consecutive");
static_assert(RISCV::F31_F == RISCV::F0_F + 31,
"Register list not consecutive");
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td
index 6d6babce98ca..27d6fcb74657 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td
@@ -16,14 +16,23 @@ class RISCVReg<bits<5> Enc, string n, list<string> alt = []> : Register<n> {
let AltNames = alt;
}
-class RISCVReg32<bits<5> Enc, string n, list<string> alt = []> : Register<n> {
+class RISCVReg16<bits<5> Enc, string n, list<string> alt = []> : Register<n> {
let HWEncoding{4-0} = Enc;
let AltNames = alt;
}
+def sub_16 : SubRegIndex<16>;
+class RISCVReg32<RISCVReg16 subreg> : Register<""> {
+ let HWEncoding{4-0} = subreg.HWEncoding{4-0};
+ let SubRegs = [subreg];
+ let SubRegIndices = [sub_16];
+ let AsmName = subreg.AsmName;
+ let AltNames = subreg.AltNames;
+}
+
// Because RISCVReg64 register have AsmName and AltNames that alias with their
-// 32-bit sub-register, RISCVAsmParser will need to coerce a register number
-// from a RISCVReg32 to the equivalent RISCVReg64 when appropriate.
+// 16/32-bit sub-register, RISCVAsmParser will need to coerce a register number
+// from a RISCVReg16/RISCVReg32 to the equivalent RISCVReg64 when appropriate.
def sub_32 : SubRegIndex<32>;
class RISCVReg64<RISCVReg32 subreg> : Register<""> {
let HWEncoding{4-0} = subreg.HWEncoding{4-0};
@@ -179,38 +188,43 @@ def SP : RegisterClass<"RISCV", [XLenVT], 32, (add X2)> {
// Floating point registers
let RegAltNameIndices = [ABIRegAltName] in {
- def F0_F : RISCVReg32<0, "f0", ["ft0"]>, DwarfRegNum<[32]>;
- def F1_F : RISCVReg32<1, "f1", ["ft1"]>, DwarfRegNum<[33]>;
- def F2_F : RISCVReg32<2, "f2", ["ft2"]>, DwarfRegNum<[34]>;
- def F3_F : RISCVReg32<3, "f3", ["ft3"]>, DwarfRegNum<[35]>;
- def F4_F : RISCVReg32<4, "f4", ["ft4"]>, DwarfRegNum<[36]>;
- def F5_F : RISCVReg32<5, "f5", ["ft5"]>, DwarfRegNum<[37]>;
- def F6_F : RISCVReg32<6, "f6", ["ft6"]>, DwarfRegNum<[38]>;
- def F7_F : RISCVReg32<7, "f7", ["ft7"]>, DwarfRegNum<[39]>;
- def F8_F : RISCVReg32<8, "f8", ["fs0"]>, DwarfRegNum<[40]>;
- def F9_F : RISCVReg32<9, "f9", ["fs1"]>, DwarfRegNum<[41]>;
- def F10_F : RISCVReg32<10,"f10", ["fa0"]>, DwarfRegNum<[42]>;
- def F11_F : RISCVReg32<11,"f11", ["fa1"]>, DwarfRegNum<[43]>;
- def F12_F : RISCVReg32<12,"f12", ["fa2"]>, DwarfRegNum<[44]>;
- def F13_F : RISCVReg32<13,"f13", ["fa3"]>, DwarfRegNum<[45]>;
- def F14_F : RISCVReg32<14,"f14", ["fa4"]>, DwarfRegNum<[46]>;
- def F15_F : RISCVReg32<15,"f15", ["fa5"]>, DwarfRegNum<[47]>;
- def F16_F : RISCVReg32<16,"f16", ["fa6"]>, DwarfRegNum<[48]>;
- def F17_F : RISCVReg32<17,"f17", ["fa7"]>, DwarfRegNum<[49]>;
- def F18_F : RISCVReg32<18,"f18", ["fs2"]>, DwarfRegNum<[50]>;
- def F19_F : RISCVReg32<19,"f19", ["fs3"]>, DwarfRegNum<[51]>;
- def F20_F : RISCVReg32<20,"f20", ["fs4"]>, DwarfRegNum<[52]>;
- def F21_F : RISCVReg32<21,"f21", ["fs5"]>, DwarfRegNum<[53]>;
- def F22_F : RISCVReg32<22,"f22", ["fs6"]>, DwarfRegNum<[54]>;
- def F23_F : RISCVReg32<23,"f23", ["fs7"]>, DwarfRegNum<[55]>;
- def F24_F : RISCVReg32<24,"f24", ["fs8"]>, DwarfRegNum<[56]>;
- def F25_F : RISCVReg32<25,"f25", ["fs9"]>, DwarfRegNum<[57]>;
- def F26_F : RISCVReg32<26,"f26", ["fs10"]>, DwarfRegNum<[58]>;
- def F27_F : RISCVReg32<27,"f27", ["fs11"]>, DwarfRegNum<[59]>;
- def F28_F : RISCVReg32<28,"f28", ["ft8"]>, DwarfRegNum<[60]>;
- def F29_F : RISCVReg32<29,"f29", ["ft9"]>, DwarfRegNum<[61]>;
- def F30_F : RISCVReg32<30,"f30", ["ft10"]>, DwarfRegNum<[62]>;
- def F31_F : RISCVReg32<31,"f31", ["ft11"]>, DwarfRegNum<[63]>;
+ def F0_H : RISCVReg16<0, "f0", ["ft0"]>, DwarfRegNum<[32]>;
+ def F1_H : RISCVReg16<1, "f1", ["ft1"]>, DwarfRegNum<[33]>;
+ def F2_H : RISCVReg16<2, "f2", ["ft2"]>, DwarfRegNum<[34]>;
+ def F3_H : RISCVReg16<3, "f3", ["ft3"]>, DwarfRegNum<[35]>;
+ def F4_H : RISCVReg16<4, "f4", ["ft4"]>, DwarfRegNum<[36]>;
+ def F5_H : RISCVReg16<5, "f5", ["ft5"]>, DwarfRegNum<[37]>;
+ def F6_H : RISCVReg16<6, "f6", ["ft6"]>, DwarfRegNum<[38]>;
+ def F7_H : RISCVReg16<7, "f7", ["ft7"]>, DwarfRegNum<[39]>;
+ def F8_H : RISCVReg16<8, "f8", ["fs0"]>, DwarfRegNum<[40]>;
+ def F9_H : RISCVReg16<9, "f9", ["fs1"]>, DwarfRegNum<[41]>;
+ def F10_H : RISCVReg16<10,"f10", ["fa0"]>, DwarfRegNum<[42]>;
+ def F11_H : RISCVReg16<11,"f11", ["fa1"]>, DwarfRegNum<[43]>;
+ def F12_H : RISCVReg16<12,"f12", ["fa2"]>, DwarfRegNum<[44]>;
+ def F13_H : RISCVReg16<13,"f13", ["fa3"]>, DwarfRegNum<[45]>;
+ def F14_H : RISCVReg16<14,"f14", ["fa4"]>, DwarfRegNum<[46]>;
+ def F15_H : RISCVReg16<15,"f15", ["fa5"]>, DwarfRegNum<[47]>;
+ def F16_H : RISCVReg16<16,"f16", ["fa6"]>, DwarfRegNum<[48]>;
+ def F17_H : RISCVReg16<17,"f17", ["fa7"]>, DwarfRegNum<[49]>;
+ def F18_H : RISCVReg16<18,"f18", ["fs2"]>, DwarfRegNum<[50]>;
+ def F19_H : RISCVReg16<19,"f19", ["fs3"]>, DwarfRegNum<[51]>;
+ def F20_H : RISCVReg16<20,"f20", ["fs4"]>, DwarfRegNum<[52]>;
+ def F21_H : RISCVReg16<21,"f21", ["fs5"]>, DwarfRegNum<[53]>;
+ def F22_H : RISCVReg16<22,"f22", ["fs6"]>, DwarfRegNum<[54]>;
+ def F23_H : RISCVReg16<23,"f23", ["fs7"]>, DwarfRegNum<[55]>;
+ def F24_H : RISCVReg16<24,"f24", ["fs8"]>, DwarfRegNum<[56]>;
+ def F25_H : RISCVReg16<25,"f25", ["fs9"]>, DwarfRegNum<[57]>;
+ def F26_H : RISCVReg16<26,"f26", ["fs10"]>, DwarfRegNum<[58]>;
+ def F27_H : RISCVReg16<27,"f27", ["fs11"]>, DwarfRegNum<[59]>;
+ def F28_H : RISCVReg16<28,"f28", ["ft8"]>, DwarfRegNum<[60]>;
+ def F29_H : RISCVReg16<29,"f29", ["ft9"]>, DwarfRegNum<[61]>;
+ def F30_H : RISCVReg16<30,"f30", ["ft10"]>, DwarfRegNum<[62]>;
+ def F31_H : RISCVReg16<31,"f31", ["ft11"]>, DwarfRegNum<[63]>;
+
+ foreach Index = 0-31 in {
+ def F#Index#_F : RISCVReg32<!cast<RISCVReg16>("F"#Index#"_H")>,
+ DwarfRegNum<[!add(Index, 32)]>;
+ }
foreach Index = 0-31 in {
def F#Index#_D : RISCVReg64<!cast<RISCVReg32>("F"#Index#"_F")>,
@@ -220,6 +234,14 @@ let RegAltNameIndices = [ABIRegAltName] in {
// The order of registers represents the preferred allocation sequence,
// meaning caller-save regs are listed before callee-save.
+def FPR16 : RegisterClass<"RISCV", [f16], 16, (add
+ (sequence "F%u_H", 0, 7),
+ (sequence "F%u_H", 10, 17),
+ (sequence "F%u_H", 28, 31),
+ (sequence "F%u_H", 8, 9),
+ (sequence "F%u_H", 18, 27)
+)>;
+
def FPR32 : RegisterClass<"RISCV", [f32], 32, (add
(sequence "F%u_F", 0, 7),
(sequence "F%u_F", 10, 17),
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.h b/llvm/lib/Target/RISCV/RISCVSubtarget.h
index c2526d214b74..222a664f8b96 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.h
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.h
@@ -53,6 +53,7 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
bool HasStdExtV = false;
bool HasStdExtZvlsseg = false;
bool HasStdExtZvamo = false;
+ bool HasStdExtZfh = false;
bool HasRV64 = false;
bool IsRV32E = false;
bool EnableLinkerRelax = false;
@@ -118,6 +119,7 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
bool hasStdExtV() const { return HasStdExtV; }
bool hasStdExtZvlsseg() const { return HasStdExtZvlsseg; }
bool hasStdExtZvamo() const { return HasStdExtZvamo; }
+ bool hasStdExtZfh() const { return HasStdExtZfh; }
bool is64Bit() const { return HasRV64; }
bool isRV32E() const { return IsRV32E; }
bool enableLinkerRelax() const { return EnableLinkerRelax; }
diff --git a/llvm/test/CodeGen/RISCV/copysign-casts.ll b/llvm/test/CodeGen/RISCV/copysign-casts.ll
index acd64c203657..340fe5c6719c 100644
--- a/llvm/test/CodeGen/RISCV/copysign-casts.ll
+++ b/llvm/test/CodeGen/RISCV/copysign-casts.ll
@@ -9,15 +9,25 @@
; RUN: -target-abi ilp32d < %s | FileCheck %s -check-prefix=RV32IFD
; RUN: llc -mtriple=riscv64 -verify-machineinstrs -mattr=+f -mattr=+d \
; RUN: -target-abi lp64d < %s | FileCheck %s -check-prefix=RV64IFD
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs -mattr=+f \
+; RUN: -mattr=+experimental-zfh -target-abi ilp32f < %s \
+; RUN: | FileCheck %s -check-prefix=RV32IFZFH
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs -mattr=+f -mattr=+d \
+; RUN: -mattr=+experimental-zfh -target-abi ilp32d < %s \
+; RUN: | FileCheck %s -check-prefix=RV32IFDZFH
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs -mattr=+f -mattr=+d \
+; RUN: -mattr=+experimental-zfh -target-abi lp64d < %s \
+; RUN: | FileCheck %s -check-prefix=RV64IFDZFH
; Test fcopysign scenarios where the sign argument is casted to the type of the
; magnitude argument. Those casts can be folded away by the DAGCombiner.
declare double @llvm.copysign.f64(double, double)
declare float @llvm.copysign.f32(float, float)
+declare half @llvm.copysign.f16(half, half)
-define double @fold_promote(double %a, float %b) nounwind {
-; RV32I-LABEL: fold_promote:
+define double @fold_promote_d_s(double %a, float %b) nounwind {
+; RV32I-LABEL: fold_promote_d_s:
; RV32I: # %bb.0:
; RV32I-NEXT: lui a3, 524288
; RV32I-NEXT: and a2, a2, a3
@@ -26,7 +36,7 @@ define double @fold_promote(double %a, float %b) nounwind {
; RV32I-NEXT: or a1, a1, a2
; RV32I-NEXT: ret
;
-; RV64I-LABEL: fold_promote:
+; RV64I-LABEL: fold_promote_d_s:
; RV64I: # %bb.0:
; RV64I-NEXT: addi a2, zero, -1
; RV64I-NEXT: slli a2, a2, 63
@@ -39,7 +49,7 @@ define double @fold_promote(double %a, float %b) nounwind {
; RV64I-NEXT: or a0, a0, a1
; RV64I-NEXT: ret
;
-; RV32IF-LABEL: fold_promote:
+; RV32IF-LABEL: fold_promote_d_s:
; RV32IF: # %bb.0:
; RV32IF-NEXT: fmv.x.w a2, fa0
; RV32IF-NEXT: lui a3, 524288
@@ -49,24 +59,183 @@ define double @fold_promote(double %a, float %b) nounwind {
; RV32IF-NEXT: or a1, a1, a2
; RV32IF-NEXT: ret
;
-; RV32IFD-LABEL: fold_promote:
+; RV32IFD-LABEL: fold_promote_d_s:
; RV32IFD: # %bb.0:
; RV32IFD-NEXT: fcvt.d.s ft0, fa1
; RV32IFD-NEXT: fsgnj.d fa0, fa0, ft0
; RV32IFD-NEXT: ret
;
-; RV64IFD-LABEL: fold_promote:
+; RV64IFD-LABEL: fold_promote_d_s:
; RV64IFD: # %bb.0:
; RV64IFD-NEXT: fcvt.d.s ft0, fa1
; RV64IFD-NEXT: fsgnj.d fa0, fa0, ft0
; RV64IFD-NEXT: ret
+;
+; RV32IFZFH-LABEL: fold_promote_d_s:
+; RV32IFZFH: # %bb.0:
+; RV32IFZFH-NEXT: fmv.x.w a2, fa0
+; RV32IFZFH-NEXT: lui a3, 524288
+; RV32IFZFH-NEXT: and a2, a2, a3
+; RV32IFZFH-NEXT: addi a3, a3, -1
+; RV32IFZFH-NEXT: and a1, a1, a3
+; RV32IFZFH-NEXT: or a1, a1, a2
+; RV32IFZFH-NEXT: ret
+;
+; RV32IFDZFH-LABEL: fold_promote_d_s:
+; RV32IFDZFH: # %bb.0:
+; RV32IFDZFH-NEXT: fcvt.d.s ft0, fa1
+; RV32IFDZFH-NEXT: fsgnj.d fa0, fa0, ft0
+; RV32IFDZFH-NEXT: ret
+;
+; RV64IFDZFH-LABEL: fold_promote_d_s:
+; RV64IFDZFH: # %bb.0:
+; RV64IFDZFH-NEXT: fcvt.d.s ft0, fa1
+; RV64IFDZFH-NEXT: fsgnj.d fa0, fa0, ft0
+; RV64IFDZFH-NEXT: ret
%c = fpext float %b to double
%t = call double @llvm.copysign.f64(double %a, double %c)
ret double %t
}
-define float @fold_demote(float %a, double %b) nounwind {
-; RV32I-LABEL: fold_demote:
+define double @fold_promote_d_h(double %a, half %b) nounwind {
+; RV32I-LABEL: fold_promote_d_h:
+; RV32I: # %bb.0:
+; RV32I-NEXT: lui a3, 524288
+; RV32I-NEXT: addi a3, a3, -1
+; RV32I-NEXT: and a1, a1, a3
+; RV32I-NEXT: lui a3, 8
+; RV32I-NEXT: and a2, a2, a3
+; RV32I-NEXT: slli a2, a2, 16
+; RV32I-NEXT: or a1, a1, a2
+; RV32I-NEXT: ret
+;
+; RV64I-LABEL: fold_promote_d_h:
+; RV64I: # %bb.0:
+; RV64I-NEXT: addi a2, zero, -1
+; RV64I-NEXT: slli a2, a2, 63
+; RV64I-NEXT: addi a2, a2, -1
+; RV64I-NEXT: and a0, a0, a2
+; RV64I-NEXT: lui a2, 8
+; RV64I-NEXT: and a1, a1, a2
+; RV64I-NEXT: slli a1, a1, 48
+; RV64I-NEXT: or a0, a0, a1
+; RV64I-NEXT: ret
+;
+; RV32IF-LABEL: fold_promote_d_h:
+; RV32IF: # %bb.0:
+; RV32IF-NEXT: fmv.x.w a2, fa0
+; RV32IF-NEXT: lui a3, 524288
+; RV32IF-NEXT: and a2, a2, a3
+; RV32IF-NEXT: addi a3, a3, -1
+; RV32IF-NEXT: and a1, a1, a3
+; RV32IF-NEXT: or a1, a1, a2
+; RV32IF-NEXT: ret
+;
+; RV32IFD-LABEL: fold_promote_d_h:
+; RV32IFD: # %bb.0:
+; RV32IFD-NEXT: fcvt.d.s ft0, fa1
+; RV32IFD-NEXT: fsgnj.d fa0, fa0, ft0
+; RV32IFD-NEXT: ret
+;
+; RV64IFD-LABEL: fold_promote_d_h:
+; RV64IFD: # %bb.0:
+; RV64IFD-NEXT: fcvt.d.s ft0, fa1
+; RV64IFD-NEXT: fsgnj.d fa0, fa0, ft0
+; RV64IFD-NEXT: ret
+;
+; RV32IFZFH-LABEL: fold_promote_d_h:
+; RV32IFZFH: # %bb.0:
+; RV32IFZFH-NEXT: fmv.x.h a2, fa0
+; RV32IFZFH-NEXT: lui a3, 524288
+; RV32IFZFH-NEXT: addi a3, a3, -1
+; RV32IFZFH-NEXT: and a1, a1, a3
+; RV32IFZFH-NEXT: lui a3, 8
+; RV32IFZFH-NEXT: and a2, a2, a3
+; RV32IFZFH-NEXT: slli a2, a2, 16
+; RV32IFZFH-NEXT: or a1, a1, a2
+; RV32IFZFH-NEXT: ret
+;
+; RV32IFDZFH-LABEL: fold_promote_d_h:
+; RV32IFDZFH: # %bb.0:
+; RV32IFDZFH-NEXT: fcvt.d.h ft0, fa1
+; RV32IFDZFH-NEXT: fsgnj.d fa0, fa0, ft0
+; RV32IFDZFH-NEXT: ret
+;
+; RV64IFDZFH-LABEL: fold_promote_d_h:
+; RV64IFDZFH: # %bb.0:
+; RV64IFDZFH-NEXT: fcvt.d.h ft0, fa1
+; RV64IFDZFH-NEXT: fsgnj.d fa0, fa0, ft0
+; RV64IFDZFH-NEXT: ret
+ %c = fpext half %b to double
+ %t = call double @llvm.copysign.f64(double %a, double %c)
+ ret double %t
+}
+
+define float @fold_promote_f_h(float %a, half %b) nounwind {
+; RV32I-LABEL: fold_promote_f_h:
+; RV32I: # %bb.0:
+; RV32I-NEXT: lui a2, 524288
+; RV32I-NEXT: addi a2, a2, -1
+; RV32I-NEXT: and a0, a0, a2
+; RV32I-NEXT: lui a2, 8
+; RV32I-NEXT: and a1, a1, a2
+; RV32I-NEXT: slli a1, a1, 16
+; RV32I-NEXT: or a0, a0, a1
+; RV32I-NEXT: ret
+;
+; RV64I-LABEL: fold_promote_f_h:
+; RV64I: # %bb.0:
+; RV64I-NEXT: lui a2, 524288
+; RV64I-NEXT: addiw a2, a2, -1
+; RV64I-NEXT: and a0, a0, a2
+; RV64I-NEXT: addi a2, zero, 1
+; RV64I-NEXT: slli a2, a2, 33
+; RV64I-NEXT: addi a2, a2, -1
+; RV64I-NEXT: slli a2, a2, 15
+; RV64I-NEXT: and a1, a1, a2
+; RV64I-NEXT: slli a1, a1, 16
+; RV64I-NEXT: or a0, a0, a1
+; RV64I-NEXT: ret
+;
+; RV32IF-LABEL: fold_promote_f_h:
+; RV32IF: # %bb.0:
+; RV32IF-NEXT: fsgnj.s fa0, fa0, fa1
+; RV32IF-NEXT: ret
+;
+; RV32IFD-LABEL: fold_promote_f_h:
+; RV32IFD: # %bb.0:
+; RV32IFD-NEXT: fsgnj.s fa0, fa0, fa1
+; RV32IFD-NEXT: ret
+;
+; RV64IFD-LABEL: fold_promote_f_h:
+; RV64IFD: # %bb.0:
+; RV64IFD-NEXT: fsgnj.s fa0, fa0, fa1
+; RV64IFD-NEXT: ret
+;
+; RV32IFZFH-LABEL: fold_promote_f_h:
+; RV32IFZFH: # %bb.0:
+; RV32IFZFH-NEXT: fcvt.s.h ft0, fa1
+; RV32IFZFH-NEXT: fsgnj.s fa0, fa0, ft0
+; RV32IFZFH-NEXT: ret
+;
+; RV32IFDZFH-LABEL: fold_promote_f_h:
+; RV32IFDZFH: # %bb.0:
+; RV32IFDZFH-NEXT: fcvt.s.h ft0, fa1
+; RV32IFDZFH-NEXT: fsgnj.s fa0, fa0, ft0
+; RV32IFDZFH-NEXT: ret
+;
+; RV64IFDZFH-LABEL: fold_promote_f_h:
+; RV64IFDZFH: # %bb.0:
+; RV64IFDZFH-NEXT: fcvt.s.h ft0, fa1
+; RV64IFDZFH-NEXT: fsgnj.s fa0, fa0, ft0
+; RV64IFDZFH-NEXT: ret
+ %c = fpext half %b to float
+ %t = call float @llvm.copysign.f32(float %a, float %c)
+ ret float %t
+}
+
+define float @fold_demote_s_d(float %a, double %b) nounwind {
+; RV32I-LABEL: fold_demote_s_d:
; RV32I: # %bb.0:
; RV32I-NEXT: lui a1, 524288
; RV32I-NEXT: and a2, a2, a1
@@ -75,7 +244,7 @@ define float @fold_demote(float %a, double %b) nounwind {
; RV32I-NEXT: or a0, a0, a2
; RV32I-NEXT: ret
;
-; RV64I-LABEL: fold_demote:
+; RV64I-LABEL: fold_demote_s_d:
; RV64I: # %bb.0:
; RV64I-NEXT: lui a2, 524288
; RV64I-NEXT: addiw a2, a2, -1
@@ -87,24 +256,263 @@ define float @fold_demote(float %a, double %b) nounwind {
; RV64I-NEXT: or a0, a0, a1
; RV64I-NEXT: ret
;
-; RV32IF-LABEL: fold_demote:
+; RV32IF-LABEL: fold_demote_s_d:
; RV32IF: # %bb.0:
; RV32IF-NEXT: fmv.w.x ft0, a1
; RV32IF-NEXT: fsgnj.s fa0, fa0, ft0
; RV32IF-NEXT: ret
;
-; RV32IFD-LABEL: fold_demote:
+; RV32IFD-LABEL: fold_demote_s_d:
; RV32IFD: # %bb.0:
; RV32IFD-NEXT: fcvt.s.d ft0, fa1
; RV32IFD-NEXT: fsgnj.s fa0, fa0, ft0
; RV32IFD-NEXT: ret
;
-; RV64IFD-LABEL: fold_demote:
+; RV64IFD-LABEL: fold_demote_s_d:
; RV64IFD: # %bb.0:
; RV64IFD-NEXT: fcvt.s.d ft0, fa1
; RV64IFD-NEXT: fsgnj.s fa0, fa0, ft0
; RV64IFD-NEXT: ret
+;
+; RV32IFZFH-LABEL: fold_demote_s_d:
+; RV32IFZFH: # %bb.0:
+; RV32IFZFH-NEXT: fmv.w.x ft0, a1
+; RV32IFZFH-NEXT: fsgnj.s fa0, fa0, ft0
+; RV32IFZFH-NEXT: ret
+;
+; RV32IFDZFH-LABEL: fold_demote_s_d:
+; RV32IFDZFH: # %bb.0:
+; RV32IFDZFH-NEXT: fcvt.s.d ft0, fa1
+; RV32IFDZFH-NEXT: fsgnj.s fa0, fa0, ft0
+; RV32IFDZFH-NEXT: ret
+;
+; RV64IFDZFH-LABEL: fold_demote_s_d:
+; RV64IFDZFH: # %bb.0:
+; RV64IFDZFH-NEXT: fcvt.s.d ft0, fa1
+; RV64IFDZFH-NEXT: fsgnj.s fa0, fa0, ft0
+; RV64IFDZFH-NEXT: ret
%c = fptrunc double %b to float
%t = call float @llvm.copysign.f32(float %a, float %c)
ret float %t
}
+
+define half @fold_demote_h_s(half %a, float %b) nounwind {
+; RV32I-LABEL: fold_demote_h_s:
+; RV32I: # %bb.0:
+; RV32I-NEXT: addi sp, sp, -16
+; RV32I-NEXT: sw ra, 12(sp)
+; RV32I-NEXT: sw s0, 8(sp)
+; RV32I-NEXT: mv s0, a1
+; RV32I-NEXT: lui a1, 16
+; RV32I-NEXT: addi a1, a1, -1
+; RV32I-NEXT: and a0, a0, a1
+; RV32I-NEXT: call __gnu_h2f_ieee
+; RV32I-NEXT: lui a1, 524288
+; RV32I-NEXT: and a2, s0, a1
+; RV32I-NEXT: addi a1, a1, -1
+; RV32I-NEXT: and a0, a0, a1
+; RV32I-NEXT: or a0, a0, a2
+; RV32I-NEXT: call __gnu_f2h_ieee
+; RV32I-NEXT: lw s0, 8(sp)
+; RV32I-NEXT: lw ra, 12(sp)
+; RV32I-NEXT: addi sp, sp, 16
+; RV32I-NEXT: ret
+;
+; RV64I-LABEL: fold_demote_h_s:
+; RV64I: # %bb.0:
+; RV64I-NEXT: addi sp, sp, -16
+; RV64I-NEXT: sd ra, 8(sp)
+; RV64I-NEXT: sd s0, 0(sp)
+; RV64I-NEXT: mv s0, a1
+; RV64I-NEXT: lui a1, 16
+; RV64I-NEXT: addiw a1, a1, -1
+; RV64I-NEXT: and a0, a0, a1
+; RV64I-NEXT: call __gnu_h2f_ieee
+; RV64I-NEXT: lui a1, 524288
+; RV64I-NEXT: and a2, s0, a1
+; RV64I-NEXT: addiw a1, a1, -1
+; RV64I-NEXT: and a0, a0, a1
+; RV64I-NEXT: or a0, a0, a2
+; RV64I-NEXT: call __gnu_f2h_ieee
+; RV64I-NEXT: ld s0, 0(sp)
+; RV64I-NEXT: ld ra, 8(sp)
+; RV64I-NEXT: addi sp, sp, 16
+; RV64I-NEXT: ret
+;
+; RV32IF-LABEL: fold_demote_h_s:
+; RV32IF: # %bb.0:
+; RV32IF-NEXT: addi sp, sp, -16
+; RV32IF-NEXT: sw ra, 12(sp)
+; RV32IF-NEXT: fsw fs0, 8(sp)
+; RV32IF-NEXT: fmv.s fs0, fa1
+; RV32IF-NEXT: call __gnu_f2h_ieee
+; RV32IF-NEXT: call __gnu_h2f_ieee
+; RV32IF-NEXT: fsgnj.s fa0, fa0, fs0
+; RV32IF-NEXT: flw fs0, 8(sp)
+; RV32IF-NEXT: lw ra, 12(sp)
+; RV32IF-NEXT: addi sp, sp, 16
+; RV32IF-NEXT: ret
+;
+; RV32IFD-LABEL: fold_demote_h_s:
+; RV32IFD: # %bb.0:
+; RV32IFD-NEXT: addi sp, sp, -16
+; RV32IFD-NEXT: sw ra, 12(sp)
+; RV32IFD-NEXT: fsd fs0, 0(sp)
+; RV32IFD-NEXT: fmv.s fs0, fa1
+; RV32IFD-NEXT: call __gnu_f2h_ieee
+; RV32IFD-NEXT: call __gnu_h2f_ieee
+; RV32IFD-NEXT: fsgnj.s fa0, fa0, fs0
+; RV32IFD-NEXT: fld fs0, 0(sp)
+; RV32IFD-NEXT: lw ra, 12(sp)
+; RV32IFD-NEXT: addi sp, sp, 16
+; RV32IFD-NEXT: ret
+;
+; RV64IFD-LABEL: fold_demote_h_s:
+; RV64IFD: # %bb.0:
+; RV64IFD-NEXT: addi sp, sp, -16
+; RV64IFD-NEXT: sd ra, 8(sp)
+; RV64IFD-NEXT: fsd fs0, 0(sp)
+; RV64IFD-NEXT: fmv.s fs0, fa1
+; RV64IFD-NEXT: call __gnu_f2h_ieee
+; RV64IFD-NEXT: call __gnu_h2f_ieee
+; RV64IFD-NEXT: fsgnj.s fa0, fa0, fs0
+; RV64IFD-NEXT: fld fs0, 0(sp)
+; RV64IFD-NEXT: ld ra, 8(sp)
+; RV64IFD-NEXT: addi sp, sp, 16
+; RV64IFD-NEXT: ret
+;
+; RV32IFZFH-LABEL: fold_demote_h_s:
+; RV32IFZFH: # %bb.0:
+; RV32IFZFH-NEXT: fcvt.h.s ft0, fa1
+; RV32IFZFH-NEXT: fsgnj.h fa0, fa0, ft0
+; RV32IFZFH-NEXT: ret
+;
+; RV32IFDZFH-LABEL: fold_demote_h_s:
+; RV32IFDZFH: # %bb.0:
+; RV32IFDZFH-NEXT: fcvt.h.s ft0, fa1
+; RV32IFDZFH-NEXT: fsgnj.h fa0, fa0, ft0
+; RV32IFDZFH-NEXT: ret
+;
+; RV64IFDZFH-LABEL: fold_demote_h_s:
+; RV64IFDZFH: # %bb.0:
+; RV64IFDZFH-NEXT: fcvt.h.s ft0, fa1
+; RV64IFDZFH-NEXT: fsgnj.h fa0, fa0, ft0
+; RV64IFDZFH-NEXT: ret
+ %c = fptrunc float %b to half
+ %t = call half @llvm.copysign.f16(half %a, half %c)
+ ret half %t
+}
+
+define half @fold_demote_h_d(half %a, double %b) nounwind {
+; RV32I-LABEL: fold_demote_h_d:
+; RV32I: # %bb.0:
+; RV32I-NEXT: addi sp, sp, -16
+; RV32I-NEXT: sw ra, 12(sp)
+; RV32I-NEXT: sw s0, 8(sp)
+; RV32I-NEXT: mv s0, a2
+; RV32I-NEXT: lui a1, 16
+; RV32I-NEXT: addi a1, a1, -1
+; RV32I-NEXT: and a0, a0, a1
+; RV32I-NEXT: call __gnu_h2f_ieee
+; RV32I-NEXT: lui a1, 524288
+; RV32I-NEXT: and a2, s0, a1
+; RV32I-NEXT: addi a1, a1, -1
+; RV32I-NEXT: and a0, a0, a1
+; RV32I-NEXT: or a0, a0, a2
+; RV32I-NEXT: call __gnu_f2h_ieee
+; RV32I-NEXT: lw s0, 8(sp)
+; RV32I-NEXT: lw ra, 12(sp)
+; RV32I-NEXT: addi sp, sp, 16
+; RV32I-NEXT: ret
+;
+; RV64I-LABEL: fold_demote_h_d:
+; RV64I: # %bb.0:
+; RV64I-NEXT: addi sp, sp, -16
+; RV64I-NEXT: sd ra, 8(sp)
+; RV64I-NEXT: sd s0, 0(sp)
+; RV64I-NEXT: mv s0, a1
+; RV64I-NEXT: lui a1, 16
+; RV64I-NEXT: addiw a1, a1, -1
+; RV64I-NEXT: and a0, a0, a1
+; RV64I-NEXT: call __gnu_h2f_ieee
+; RV64I-NEXT: lui a1, 524288
+; RV64I-NEXT: addiw a1, a1, -1
+; RV64I-NEXT: and a0, a0, a1
+; RV64I-NEXT: addi a1, zero, -1
+; RV64I-NEXT: slli a1, a1, 63
+; RV64I-NEXT: and a1, s0, a1
+; RV64I-NEXT: srli a1, a1, 32
+; RV64I-NEXT: or a0, a0, a1
+; RV64I-NEXT: call __gnu_f2h_ieee
+; RV64I-NEXT: ld s0, 0(sp)
+; RV64I-NEXT: ld ra, 8(sp)
+; RV64I-NEXT: addi sp, sp, 16
+; RV64I-NEXT: ret
+;
+; RV32IF-LABEL: fold_demote_h_d:
+; RV32IF: # %bb.0:
+; RV32IF-NEXT: addi sp, sp, -16
+; RV32IF-NEXT: sw ra, 12(sp)
+; RV32IF-NEXT: sw s0, 8(sp)
+; RV32IF-NEXT: mv s0, a1
+; RV32IF-NEXT: call __gnu_f2h_ieee
+; RV32IF-NEXT: call __gnu_h2f_ieee
+; RV32IF-NEXT: fmv.w.x ft0, s0
+; RV32IF-NEXT: fsgnj.s fa0, fa0, ft0
+; RV32IF-NEXT: lw s0, 8(sp)
+; RV32IF-NEXT: lw ra, 12(sp)
+; RV32IF-NEXT: addi sp, sp, 16
+; RV32IF-NEXT: ret
+;
+; RV32IFD-LABEL: fold_demote_h_d:
+; RV32IFD: # %bb.0:
+; RV32IFD-NEXT: addi sp, sp, -16
+; RV32IFD-NEXT: sw ra, 12(sp)
+; RV32IFD-NEXT: fsd fs0, 0(sp)
+; RV32IFD-NEXT: fmv.d fs0, fa1
+; RV32IFD-NEXT: call __gnu_f2h_ieee
+; RV32IFD-NEXT: call __gnu_h2f_ieee
+; RV32IFD-NEXT: fcvt.s.d ft0, fs0
+; RV32IFD-NEXT: fsgnj.s fa0, fa0, ft0
+; RV32IFD-NEXT: fld fs0, 0(sp)
+; RV32IFD-NEXT: lw ra, 12(sp)
+; RV32IFD-NEXT: addi sp, sp, 16
+; RV32IFD-NEXT: ret
+;
+; RV64IFD-LABEL: fold_demote_h_d:
+; RV64IFD: # %bb.0:
+; RV64IFD-NEXT: addi sp, sp, -16
+; RV64IFD-NEXT: sd ra, 8(sp)
+; RV64IFD-NEXT: fsd fs0, 0(sp)
+; RV64IFD-NEXT: fmv.d fs0, fa1
+; RV64IFD-NEXT: call __gnu_f2h_ieee
+; RV64IFD-NEXT: call __gnu_h2f_ieee
+; RV64IFD-NEXT: fcvt.s.d ft0, fs0
+; RV64IFD-NEXT: fsgnj.s fa0, fa0, ft0
+; RV64IFD-NEXT: fld fs0, 0(sp)
+; RV64IFD-NEXT: ld ra, 8(sp)
+; RV64IFD-NEXT: addi sp, sp, 16
+; RV64IFD-NEXT: ret
+;
+; RV32IFZFH-LABEL: fold_demote_h_d:
+; RV32IFZFH: # %bb.0:
+; RV32IFZFH-NEXT: srli a0, a1, 16
+; RV32IFZFH-NEXT: fmv.h.x ft0, a0
+; RV32IFZFH-NEXT: fsgnj.h fa0, fa0, ft0
+; RV32IFZFH-NEXT: ret
+;
+; RV32IFDZFH-LABEL: fold_demote_h_d:
+; RV32IFDZFH: # %bb.0:
+; RV32IFDZFH-NEXT: fcvt.h.d ft0, fa1
+; RV32IFDZFH-NEXT: fsgnj.h fa0, fa0, ft0
+; RV32IFDZFH-NEXT: ret
+;
+; RV64IFDZFH-LABEL: fold_demote_h_d:
+; RV64IFDZFH: # %bb.0:
+; RV64IFDZFH-NEXT: fcvt.h.d ft0, fa1
+; RV64IFDZFH-NEXT: fsgnj.h fa0, fa0, ft0
+; RV64IFDZFH-NEXT: ret
+ %c = fptrunc double %b to half
+ %t = call half @llvm.copysign.f16(half %a, half %c)
+ ret half %t
+}
diff --git a/llvm/test/CodeGen/RISCV/half-arith.ll b/llvm/test/CodeGen/RISCV/half-arith.ll
new file mode 100644
index 000000000000..6b6b1ddd907e
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/half-arith.ll
@@ -0,0 +1,323 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32f < %s \
+; RUN: | FileCheck -check-prefix=RV32IZFH %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64f < %s \
+; RUN: | FileCheck -check-prefix=RV64IZFH %s
+
+; These tests are each targeted at a particular RISC-V FPU instruction. Most
+; other files in this folder exercise LLVM IR instructions that don't directly
+; match a RISC-V instruction.
+
+define half @fadd_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fadd_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fadd.h fa0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fadd_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fadd.h fa0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fadd half %a, %b
+ ret half %1
+}
+
+define half @fsub_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fsub_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fsub.h fa0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fsub_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fsub.h fa0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fsub half %a, %b
+ ret half %1
+}
+
+define half @fmul_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fmul_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmul.h fa0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fmul_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmul.h fa0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fmul half %a, %b
+ ret half %1
+}
+
+define half @fdiv_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fdiv_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fdiv.h fa0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fdiv_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fdiv.h fa0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fdiv half %a, %b
+ ret half %1
+}
+
+declare half @llvm.sqrt.f16(half)
+
+define half @fsqrt_s(half %a) nounwind {
+; RV32IZFH-LABEL: fsqrt_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fsqrt.h fa0, fa0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fsqrt_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fsqrt.h fa0, fa0
+; RV64IZFH-NEXT: ret
+ %1 = call half @llvm.sqrt.f16(half %a)
+ ret half %1
+}
+
+declare half @llvm.copysign.f16(half, half)
+
+define half @fsgnj_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fsgnj_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fsgnj.h fa0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fsgnj_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fsgnj.h fa0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = call half @llvm.copysign.f16(half %a, half %b)
+ ret half %1
+}
+
+; This function performs extra work to ensure that
+; DAGCombiner::visitBITCAST doesn't replace the fneg with an xor.
+define i32 @fneg_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fneg_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fadd.h ft0, fa0, fa0
+; RV32IZFH-NEXT: fneg.h ft1, ft0
+; RV32IZFH-NEXT: feq.h a0, ft0, ft1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fneg_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fadd.h ft0, fa0, fa0
+; RV64IZFH-NEXT: fneg.h ft1, ft0
+; RV64IZFH-NEXT: feq.h a0, ft0, ft1
+; RV64IZFH-NEXT: ret
+ %1 = fadd half %a, %a
+ %2 = fneg half %1
+ %3 = fcmp oeq half %1, %2
+ %4 = zext i1 %3 to i32
+ ret i32 %4
+}
+
+; This function performs extra work to ensure that
+; DAGCombiner::visitBITCAST doesn't replace the fneg with an xor.
+define half @fsgnjn_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fsgnjn_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fadd.h ft0, fa0, fa1
+; RV32IZFH-NEXT: fsgnjn.h fa0, fa0, ft0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fsgnjn_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fadd.h ft0, fa0, fa1
+; RV64IZFH-NEXT: fsgnjn.h fa0, fa0, ft0
+; RV64IZFH-NEXT: ret
+ %1 = fadd half %a, %b
+ %2 = fneg half %1
+ %3 = call half @llvm.copysign.f16(half %a, half %2)
+ ret half %3
+}
+
+declare half @llvm.fabs.f16(half)
+
+; This function performs extra work to ensure that
+; DAGCombiner::visitBITCAST doesn't replace the fabs with an and.
+define half @fabs_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fabs_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fadd.h ft0, fa0, fa1
+; RV32IZFH-NEXT: fabs.h ft1, ft0
+; RV32IZFH-NEXT: fadd.h fa0, ft1, ft0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fabs_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fadd.h ft0, fa0, fa1
+; RV64IZFH-NEXT: fabs.h ft1, ft0
+; RV64IZFH-NEXT: fadd.h fa0, ft1, ft0
+; RV64IZFH-NEXT: ret
+ %1 = fadd half %a, %b
+ %2 = call half @llvm.fabs.f16(half %1)
+ %3 = fadd half %2, %1
+ ret half %3
+}
+
+declare half @llvm.minnum.f16(half, half)
+
+define half @fmin_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fmin_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmin.h fa0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fmin_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmin.h fa0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = call half @llvm.minnum.f16(half %a, half %b)
+ ret half %1
+}
+
+declare half @llvm.maxnum.f16(half, half)
+
+define half @fmax_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fmax_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmax.h fa0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fmax_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmax.h fa0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = call half @llvm.maxnum.f16(half %a, half %b)
+ ret half %1
+}
+
+define i32 @feq_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: feq_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: feq_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp oeq half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @flt_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: flt_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: flt.h a0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: flt_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: flt.h a0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp olt half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fle_s(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fle_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fle.h a0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fle_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fle.h a0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ole half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+declare half @llvm.fma.f16(half, half, half)
+
+define half @fmadd_s(half %a, half %b, half %c) nounwind {
+; RV32IZFH-LABEL: fmadd_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmadd.h fa0, fa0, fa1, fa2
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fmadd_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmadd.h fa0, fa0, fa1, fa2
+; RV64IZFH-NEXT: ret
+ %1 = call half @llvm.fma.f16(half %a, half %b, half %c)
+ ret half %1
+}
+
+define half @fmsub_s(half %a, half %b, half %c) nounwind {
+; RV32IZFH-LABEL: fmsub_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmv.h.x ft0, zero
+; RV32IZFH-NEXT: fadd.h ft0, fa2, ft0
+; RV32IZFH-NEXT: fmsub.h fa0, fa0, fa1, ft0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fmsub_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmv.h.x ft0, zero
+; RV64IZFH-NEXT: fadd.h ft0, fa2, ft0
+; RV64IZFH-NEXT: fmsub.h fa0, fa0, fa1, ft0
+; RV64IZFH-NEXT: ret
+ %c_ = fadd half 0.0, %c ; avoid negation using xor
+ %negc = fsub half -0.0, %c_
+ %1 = call half @llvm.fma.f16(half %a, half %b, half %negc)
+ ret half %1
+}
+
+define half @fnmadd_s(half %a, half %b, half %c) nounwind {
+; RV32IZFH-LABEL: fnmadd_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmv.h.x ft0, zero
+; RV32IZFH-NEXT: fadd.h ft1, fa0, ft0
+; RV32IZFH-NEXT: fadd.h ft0, fa2, ft0
+; RV32IZFH-NEXT: fnmadd.h fa0, ft1, fa1, ft0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fnmadd_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmv.h.x ft0, zero
+; RV64IZFH-NEXT: fadd.h ft1, fa0, ft0
+; RV64IZFH-NEXT: fadd.h ft0, fa2, ft0
+; RV64IZFH-NEXT: fnmadd.h fa0, ft1, fa1, ft0
+; RV64IZFH-NEXT: ret
+ %a_ = fadd half 0.0, %a
+ %c_ = fadd half 0.0, %c
+ %nega = fsub half -0.0, %a_
+ %negc = fsub half -0.0, %c_
+ %1 = call half @llvm.fma.f16(half %nega, half %b, half %negc)
+ ret half %1
+}
+
+define half @fnmsub_s(half %a, half %b, half %c) nounwind {
+; RV32IZFH-LABEL: fnmsub_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmv.h.x ft0, zero
+; RV32IZFH-NEXT: fadd.h ft0, fa0, ft0
+; RV32IZFH-NEXT: fnmsub.h fa0, ft0, fa1, fa2
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fnmsub_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmv.h.x ft0, zero
+; RV64IZFH-NEXT: fadd.h ft0, fa0, ft0
+; RV64IZFH-NEXT: fnmsub.h fa0, ft0, fa1, fa2
+; RV64IZFH-NEXT: ret
+ %a_ = fadd half 0.0, %a
+ %nega = fsub half -0.0, %a_
+ %1 = call half @llvm.fma.f16(half %nega, half %b, half %c)
+ ret half %1
+}
diff --git a/llvm/test/CodeGen/RISCV/half-bitmanip-dagcombines.ll b/llvm/test/CodeGen/RISCV/half-bitmanip-dagcombines.ll
new file mode 100644
index 000000000000..0f192a2eabce
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/half-bitmanip-dagcombines.ll
@@ -0,0 +1,145 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN: | FileCheck -check-prefix=RV32I %s
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32f < %s \
+; RUN: | FileCheck -check-prefix=RV32IZFH %s
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
+; RUN: | FileCheck -check-prefix=RV64I %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64f < %s \
+; RUN: | FileCheck -check-prefix=RV64IZFH %s
+
+; This file tests cases where simple floating point operations can be
+; profitably handled though bit manipulation if a soft-float ABI is being used
+; (e.g. fneg implemented by XORing the sign bit). This is typically handled in
+; DAGCombiner::visitBITCAST, but this target-independent code may not trigger
+; in cases where we perform custom legalisation (e.g. RV64F).
+
+define half @fneg(half %a) nounwind {
+; RV32I-LABEL: fneg:
+; RV32I: # %bb.0:
+; RV32I-NEXT: lui a1, 1048568
+; RV32I-NEXT: xor a0, a0, a1
+; RV32I-NEXT: ret
+;
+; RV32IZFH-LABEL: fneg:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fneg.h fa0, fa0
+; RV32IZFH-NEXT: ret
+;
+; RV64I-LABEL: fneg:
+; RV64I: # %bb.0:
+; RV64I-NEXT: lui a1, 1048568
+; RV64I-NEXT: xor a0, a0, a1
+; RV64I-NEXT: ret
+;
+; RV64IZFH-LABEL: fneg:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fneg.h fa0, fa0
+; RV64IZFH-NEXT: ret
+ %1 = fneg half %a
+ ret half %1
+}
+
+declare half @llvm.fabs.f16(half)
+
+define half @fabs(half %a) nounwind {
+; RV32I-LABEL: fabs:
+; RV32I: # %bb.0:
+; RV32I-NEXT: lui a1, 8
+; RV32I-NEXT: addi a1, a1, -1
+; RV32I-NEXT: and a0, a0, a1
+; RV32I-NEXT: ret
+;
+; RV32IZFH-LABEL: fabs:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fabs.h fa0, fa0
+; RV32IZFH-NEXT: ret
+;
+; RV64I-LABEL: fabs:
+; RV64I: # %bb.0:
+; RV64I-NEXT: lui a1, 8
+; RV64I-NEXT: addiw a1, a1, -1
+; RV64I-NEXT: and a0, a0, a1
+; RV64I-NEXT: ret
+;
+; RV64IZFH-LABEL: fabs:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fabs.h fa0, fa0
+; RV64IZFH-NEXT: ret
+ %1 = call half @llvm.fabs.f16(half %a)
+ ret half %1
+}
+
+declare half @llvm.copysign.f16(half, half)
+
+; DAGTypeLegalizer::SoftenFloatRes_FCOPYSIGN will convert to bitwise
+; operations if half precision floating point isn't supported. A combine could
+; be written to do the same even when f16 is legal.
+
+define half @fcopysign_fneg(half %a, half %b) nounwind {
+; RV32I-LABEL: fcopysign_fneg:
+; RV32I: # %bb.0:
+; RV32I-NEXT: addi sp, sp, -16
+; RV32I-NEXT: sw ra, 12(sp)
+; RV32I-NEXT: sw s0, 8(sp)
+; RV32I-NEXT: mv s0, a1
+; RV32I-NEXT: lui a1, 16
+; RV32I-NEXT: addi a1, a1, -1
+; RV32I-NEXT: and a0, a0, a1
+; RV32I-NEXT: call __gnu_h2f_ieee
+; RV32I-NEXT: not a1, s0
+; RV32I-NEXT: lui a2, 524288
+; RV32I-NEXT: addi a2, a2, -1
+; RV32I-NEXT: and a0, a0, a2
+; RV32I-NEXT: lui a2, 8
+; RV32I-NEXT: and a1, a1, a2
+; RV32I-NEXT: slli a1, a1, 16
+; RV32I-NEXT: or a0, a0, a1
+; RV32I-NEXT: call __gnu_f2h_ieee
+; RV32I-NEXT: lw s0, 8(sp)
+; RV32I-NEXT: lw ra, 12(sp)
+; RV32I-NEXT: addi sp, sp, 16
+; RV32I-NEXT: ret
+;
+; RV32IZFH-LABEL: fcopysign_fneg:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fsgnjn.h fa0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64I-LABEL: fcopysign_fneg:
+; RV64I: # %bb.0:
+; RV64I-NEXT: addi sp, sp, -16
+; RV64I-NEXT: sd ra, 8(sp)
+; RV64I-NEXT: sd s0, 0(sp)
+; RV64I-NEXT: mv s0, a1
+; RV64I-NEXT: lui a1, 16
+; RV64I-NEXT: addiw a1, a1, -1
+; RV64I-NEXT: and a0, a0, a1
+; RV64I-NEXT: call __gnu_h2f_ieee
+; RV64I-NEXT: not a1, s0
+; RV64I-NEXT: lui a2, 524288
+; RV64I-NEXT: addiw a2, a2, -1
+; RV64I-NEXT: and a0, a0, a2
+; RV64I-NEXT: addi a2, zero, 1
+; RV64I-NEXT: slli a2, a2, 33
+; RV64I-NEXT: addi a2, a2, -1
+; RV64I-NEXT: slli a2, a2, 15
+; RV64I-NEXT: and a1, a1, a2
+; RV64I-NEXT: slli a1, a1, 16
+; RV64I-NEXT: or a0, a0, a1
+; RV64I-NEXT: call __gnu_f2h_ieee
+; RV64I-NEXT: ld s0, 0(sp)
+; RV64I-NEXT: ld ra, 8(sp)
+; RV64I-NEXT: addi sp, sp, 16
+; RV64I-NEXT: ret
+;
+; RV64IZFH-LABEL: fcopysign_fneg:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fsgnjn.h fa0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fneg half %b
+ %2 = call half @llvm.copysign.f16(half %a, half %1)
+ ret half %2
+}
diff --git a/llvm/test/CodeGen/RISCV/half-br-fcmp.ll b/llvm/test/CodeGen/RISCV/half-br-fcmp.ll
new file mode 100644
index 000000000000..0566928796f0
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/half-br-fcmp.ll
@@ -0,0 +1,651 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32f < %s | FileCheck -check-prefix=RV32IZFH %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64f < %s | FileCheck -check-prefix=RV64IZFH %s
+
+declare void @abort()
+declare void @exit(i32)
+declare half @dummy(half)
+
+define void @br_fcmp_false(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_false:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: addi a0, zero, 1
+; RV32IZFH-NEXT: bnez a0, .LBB0_2
+; RV32IZFH-NEXT: # %bb.1: # %if.then
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB0_2: # %if.else
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_false:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: addi a0, zero, 1
+; RV64IZFH-NEXT: bnez a0, .LBB0_2
+; RV64IZFH-NEXT: # %bb.1: # %if.then
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB0_2: # %if.else
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp false half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.then:
+ ret void
+if.else:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_oeq(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_oeq:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: feq.h a0, fa0, fa1
+; RV32IZFH-NEXT: bnez a0, .LBB1_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB1_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_oeq:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: feq.h a0, fa0, fa1
+; RV64IZFH-NEXT: bnez a0, .LBB1_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB1_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp oeq half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+; TODO: generated code quality for this is very poor due to
+; DAGCombiner::visitXOR converting the legal setoeq to setune, which requires
+; expansion.
+define void @br_fcmp_oeq_alt(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_oeq_alt:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: feq.h a0, fa0, fa1
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: beqz a0, .LBB2_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB2_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_oeq_alt:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: feq.h a0, fa0, fa1
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: beqz a0, .LBB2_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB2_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp oeq half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.then:
+ tail call void @abort()
+ unreachable
+if.else:
+ ret void
+}
+
+define void @br_fcmp_ogt(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_ogt:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: flt.h a0, fa1, fa0
+; RV32IZFH-NEXT: bnez a0, .LBB3_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB3_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_ogt:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: flt.h a0, fa1, fa0
+; RV64IZFH-NEXT: bnez a0, .LBB3_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB3_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp ogt half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_oge(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_oge:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: fle.h a0, fa1, fa0
+; RV32IZFH-NEXT: bnez a0, .LBB4_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB4_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_oge:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: fle.h a0, fa1, fa0
+; RV64IZFH-NEXT: bnez a0, .LBB4_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB4_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp oge half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_olt(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_olt:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: flt.h a0, fa0, fa1
+; RV32IZFH-NEXT: bnez a0, .LBB5_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB5_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_olt:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: flt.h a0, fa0, fa1
+; RV64IZFH-NEXT: bnez a0, .LBB5_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB5_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp olt half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_ole(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_ole:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: fle.h a0, fa0, fa1
+; RV32IZFH-NEXT: bnez a0, .LBB6_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB6_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_ole:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: fle.h a0, fa0, fa1
+; RV64IZFH-NEXT: bnez a0, .LBB6_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB6_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp ole half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+; TODO: feq.h+sltiu+bne -> feq.h+beq
+define void @br_fcmp_one(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_one:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: feq.h a0, fa1, fa1
+; RV32IZFH-NEXT: feq.h a1, fa0, fa0
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: feq.h a1, fa0, fa1
+; RV32IZFH-NEXT: not a1, a1
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: bnez a0, .LBB7_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB7_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_one:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: feq.h a0, fa1, fa1
+; RV64IZFH-NEXT: feq.h a1, fa0, fa0
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: feq.h a1, fa0, fa1
+; RV64IZFH-NEXT: not a1, a1
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: bnez a0, .LBB7_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB7_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp one half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_ord(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_ord:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: feq.h a0, fa1, fa1
+; RV32IZFH-NEXT: feq.h a1, fa0, fa0
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: bnez a0, .LBB8_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB8_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_ord:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: feq.h a0, fa1, fa1
+; RV64IZFH-NEXT: feq.h a1, fa0, fa0
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: bnez a0, .LBB8_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB8_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp ord half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_ueq(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_ueq:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: feq.h a0, fa0, fa1
+; RV32IZFH-NEXT: feq.h a1, fa1, fa1
+; RV32IZFH-NEXT: feq.h a2, fa0, fa0
+; RV32IZFH-NEXT: and a1, a2, a1
+; RV32IZFH-NEXT: seqz a1, a1
+; RV32IZFH-NEXT: or a0, a0, a1
+; RV32IZFH-NEXT: bnez a0, .LBB9_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB9_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_ueq:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: feq.h a0, fa0, fa1
+; RV64IZFH-NEXT: feq.h a1, fa1, fa1
+; RV64IZFH-NEXT: feq.h a2, fa0, fa0
+; RV64IZFH-NEXT: and a1, a2, a1
+; RV64IZFH-NEXT: seqz a1, a1
+; RV64IZFH-NEXT: or a0, a0, a1
+; RV64IZFH-NEXT: bnez a0, .LBB9_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB9_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp ueq half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_ugt(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_ugt:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: fle.h a0, fa0, fa1
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: bnez a0, .LBB10_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB10_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_ugt:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: fle.h a0, fa0, fa1
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: bnez a0, .LBB10_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB10_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp ugt half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_uge(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_uge:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: flt.h a0, fa0, fa1
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: bnez a0, .LBB11_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB11_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_uge:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: flt.h a0, fa0, fa1
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: bnez a0, .LBB11_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB11_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp uge half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_ult(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_ult:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: fle.h a0, fa1, fa0
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: bnez a0, .LBB12_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB12_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_ult:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: fle.h a0, fa1, fa0
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: bnez a0, .LBB12_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB12_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp ult half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_ule(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_ule:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: flt.h a0, fa1, fa0
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: bnez a0, .LBB13_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB13_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_ule:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: flt.h a0, fa1, fa0
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: bnez a0, .LBB13_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB13_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp ule half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_une(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_une:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: feq.h a0, fa0, fa1
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: bnez a0, .LBB14_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB14_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_une:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: feq.h a0, fa0, fa1
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: bnez a0, .LBB14_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB14_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp une half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_uno(half %a, half %b) nounwind {
+; TODO: sltiu+bne -> beq
+; RV32IZFH-LABEL: br_fcmp_uno:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: feq.h a0, fa1, fa1
+; RV32IZFH-NEXT: feq.h a1, fa0, fa0
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: seqz a0, a0
+; RV32IZFH-NEXT: bnez a0, .LBB15_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB15_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_uno:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: feq.h a0, fa1, fa1
+; RV64IZFH-NEXT: feq.h a1, fa0, fa0
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: seqz a0, a0
+; RV64IZFH-NEXT: bnez a0, .LBB15_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB15_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp uno half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
+
+define void @br_fcmp_true(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: br_fcmp_true:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: addi a0, zero, 1
+; RV32IZFH-NEXT: bnez a0, .LBB16_2
+; RV32IZFH-NEXT: # %bb.1: # %if.else
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+; RV32IZFH-NEXT: .LBB16_2: # %if.then
+; RV32IZFH-NEXT: call abort
+;
+; RV64IZFH-LABEL: br_fcmp_true:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: addi a0, zero, 1
+; RV64IZFH-NEXT: bnez a0, .LBB16_2
+; RV64IZFH-NEXT: # %bb.1: # %if.else
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+; RV64IZFH-NEXT: .LBB16_2: # %if.then
+; RV64IZFH-NEXT: call abort
+ %1 = fcmp true half %a, %b
+ br i1 %1, label %if.then, label %if.else
+if.else:
+ ret void
+if.then:
+ tail call void @abort()
+ unreachable
+}
diff --git a/llvm/test/CodeGen/RISCV/half-convert.ll b/llvm/test/CodeGen/RISCV/half-convert.ll
new file mode 100644
index 000000000000..a9299f2d7ea6
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/half-convert.ll
@@ -0,0 +1,511 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32f < %s | FileCheck -check-prefix=RV32IZFH %s
+; RUN: llc -mtriple=riscv32 -mattr=+d,+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32d < %s | FileCheck -check-prefix=RV32IDZFH %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64f < %s | FileCheck -check-prefix=RV64IZFH %s
+; RUN: llc -mtriple=riscv64 -mattr=+d,+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64d < %s | FileCheck -check-prefix=RV64IDZFH %s
+
+define i16 @fcvt_si_h(half %a) nounwind {
+; RV32IZFH-LABEL: fcvt_si_h:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fcvt.w.h a0, fa0, rtz
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_si_h:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fcvt.w.h a0, fa0, rtz
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_si_h:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.l.h a0, fa0, rtz
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_si_h:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.l.h a0, fa0, rtz
+; RV64IDZFH-NEXT: ret
+ %1 = fptosi half %a to i16
+ ret i16 %1
+}
+
+define i16 @fcvt_ui_h(half %a) nounwind {
+; RV32IZFH-LABEL: fcvt_ui_h:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fcvt.wu.h a0, fa0, rtz
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_ui_h:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fcvt.wu.h a0, fa0, rtz
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_ui_h:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.lu.h a0, fa0, rtz
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_ui_h:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.lu.h a0, fa0, rtz
+; RV64IDZFH-NEXT: ret
+ %1 = fptoui half %a to i16
+ ret i16 %1
+}
+
+define i32 @fcvt_w_h(half %a) nounwind {
+; RV32IZFH-LABEL: fcvt_w_h:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fcvt.w.h a0, fa0, rtz
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_w_h:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fcvt.w.h a0, fa0, rtz
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_w_h:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.l.h a0, fa0, rtz
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_w_h:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.l.h a0, fa0, rtz
+; RV64IDZFH-NEXT: ret
+ %1 = fptosi half %a to i32
+ ret i32 %1
+}
+
+define i32 @fcvt_wu_h(half %a) nounwind {
+; RV32IZFH-LABEL: fcvt_wu_h:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fcvt.wu.h a0, fa0, rtz
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_wu_h:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fcvt.wu.h a0, fa0, rtz
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_wu_h:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.lu.h a0, fa0, rtz
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_wu_h:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.lu.h a0, fa0, rtz
+; RV64IDZFH-NEXT: ret
+ %1 = fptoui half %a to i32
+ ret i32 %1
+}
+
+define i64 @fcvt_l_h(half %a) nounwind {
+; RV32IZFH-LABEL: fcvt_l_h:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: call __fixhfdi
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_l_h:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: addi sp, sp, -16
+; RV32IDZFH-NEXT: sw ra, 12(sp)
+; RV32IDZFH-NEXT: call __fixhfdi
+; RV32IDZFH-NEXT: lw ra, 12(sp)
+; RV32IDZFH-NEXT: addi sp, sp, 16
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_l_h:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.l.h a0, fa0, rtz
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_l_h:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.l.h a0, fa0, rtz
+; RV64IDZFH-NEXT: ret
+ %1 = fptosi half %a to i64
+ ret i64 %1
+}
+
+define i64 @fcvt_lu_h(half %a) nounwind {
+; RV32IZFH-LABEL: fcvt_lu_h:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: call __fixunshfdi
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_lu_h:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: addi sp, sp, -16
+; RV32IDZFH-NEXT: sw ra, 12(sp)
+; RV32IDZFH-NEXT: call __fixunshfdi
+; RV32IDZFH-NEXT: lw ra, 12(sp)
+; RV32IDZFH-NEXT: addi sp, sp, 16
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_lu_h:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.lu.h a0, fa0, rtz
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_lu_h:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.lu.h a0, fa0, rtz
+; RV64IDZFH-NEXT: ret
+ %1 = fptoui half %a to i64
+ ret i64 %1
+}
+
+define half @fcvt_h_si(i16 %a) nounwind {
+; RV32IZFH-LABEL: fcvt_h_si:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: slli a0, a0, 16
+; RV32IZFH-NEXT: srai a0, a0, 16
+; RV32IZFH-NEXT: fcvt.h.w fa0, a0
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_h_si:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: slli a0, a0, 16
+; RV32IDZFH-NEXT: srai a0, a0, 16
+; RV32IDZFH-NEXT: fcvt.h.w fa0, a0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_h_si:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: slli a0, a0, 48
+; RV64IZFH-NEXT: srai a0, a0, 48
+; RV64IZFH-NEXT: fcvt.h.l fa0, a0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_h_si:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: slli a0, a0, 48
+; RV64IDZFH-NEXT: srai a0, a0, 48
+; RV64IDZFH-NEXT: fcvt.h.l fa0, a0
+; RV64IDZFH-NEXT: ret
+ %1 = sitofp i16 %a to half
+ ret half %1
+}
+
+define half @fcvt_h_ui(i16 %a) nounwind {
+; RV32IZFH-LABEL: fcvt_h_ui:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: lui a1, 16
+; RV32IZFH-NEXT: addi a1, a1, -1
+; RV32IZFH-NEXT: and a0, a0, a1
+; RV32IZFH-NEXT: fcvt.h.wu fa0, a0
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_h_ui:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: lui a1, 16
+; RV32IDZFH-NEXT: addi a1, a1, -1
+; RV32IDZFH-NEXT: and a0, a0, a1
+; RV32IDZFH-NEXT: fcvt.h.wu fa0, a0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_h_ui:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: lui a1, 16
+; RV64IZFH-NEXT: addiw a1, a1, -1
+; RV64IZFH-NEXT: and a0, a0, a1
+; RV64IZFH-NEXT: fcvt.h.lu fa0, a0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_h_ui:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: lui a1, 16
+; RV64IDZFH-NEXT: addiw a1, a1, -1
+; RV64IDZFH-NEXT: and a0, a0, a1
+; RV64IDZFH-NEXT: fcvt.h.lu fa0, a0
+; RV64IDZFH-NEXT: ret
+ %1 = uitofp i16 %a to half
+ ret half %1
+}
+
+define half @fcvt_h_w(i32 %a) nounwind {
+; RV32IZFH-LABEL: fcvt_h_w:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fcvt.h.w fa0, a0
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_h_w:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fcvt.h.w fa0, a0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_h_w:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.h.w fa0, a0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_h_w:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.h.w fa0, a0
+; RV64IDZFH-NEXT: ret
+ %1 = sitofp i32 %a to half
+ ret half %1
+}
+
+define half @fcvt_h_wu(i32 %a) nounwind {
+; RV32IZFH-LABEL: fcvt_h_wu:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fcvt.h.wu fa0, a0
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_h_wu:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fcvt.h.wu fa0, a0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_h_wu:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.h.wu fa0, a0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_h_wu:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.h.wu fa0, a0
+; RV64IDZFH-NEXT: ret
+ %1 = uitofp i32 %a to half
+ ret half %1
+}
+
+define half @fcvt_h_l(i64 %a) nounwind {
+; RV32IZFH-LABEL: fcvt_h_l:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: call __floatdihf
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_h_l:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: addi sp, sp, -16
+; RV32IDZFH-NEXT: sw ra, 12(sp)
+; RV32IDZFH-NEXT: call __floatdihf
+; RV32IDZFH-NEXT: lw ra, 12(sp)
+; RV32IDZFH-NEXT: addi sp, sp, 16
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_h_l:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.h.l fa0, a0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_h_l:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.h.l fa0, a0
+; RV64IDZFH-NEXT: ret
+ %1 = sitofp i64 %a to half
+ ret half %1
+}
+
+define half @fcvt_h_lu(i64 %a) nounwind {
+; RV32IZFH-LABEL: fcvt_h_lu:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: call __floatundihf
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_h_lu:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: addi sp, sp, -16
+; RV32IDZFH-NEXT: sw ra, 12(sp)
+; RV32IDZFH-NEXT: call __floatundihf
+; RV32IDZFH-NEXT: lw ra, 12(sp)
+; RV32IDZFH-NEXT: addi sp, sp, 16
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_h_lu:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.h.lu fa0, a0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_h_lu:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.h.lu fa0, a0
+; RV64IDZFH-NEXT: ret
+ %1 = uitofp i64 %a to half
+ ret half %1
+}
+
+define half @fcvt_h_s(float %a) nounwind {
+; RV32IZFH-LABEL: fcvt_h_s:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fcvt.h.s fa0, fa0
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_h_s:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fcvt.h.s fa0, fa0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_h_s:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.h.s fa0, fa0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_h_s:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.h.s fa0, fa0
+; RV64IDZFH-NEXT: ret
+ %1 = fptrunc float %a to half
+ ret half %1
+}
+
+define float @fcvt_s_h(half %a) nounwind {
+; RV32IZFH-LABEL: fcvt_s_h:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fcvt.s.h fa0, fa0
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_s_h:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fcvt.s.h fa0, fa0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_s_h:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.s.h fa0, fa0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_s_h:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.s.h fa0, fa0
+; RV64IDZFH-NEXT: ret
+ %1 = fpext half %a to float
+ ret float %1
+}
+
+define half @fcvt_h_d(double %a) nounwind {
+; RV32IZFH-LABEL: fcvt_h_d:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: call __truncdfhf2
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_h_d:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fcvt.h.d fa0, fa0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_h_d:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: call __truncdfhf2
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_h_d:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.h.d fa0, fa0
+; RV64IDZFH-NEXT: ret
+ %1 = fptrunc double %a to half
+ ret half %1
+}
+
+define double @fcvt_d_h(half %a) nounwind {
+; RV32IZFH-LABEL: fcvt_d_h:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: fcvt.s.h fa0, fa0
+; RV32IZFH-NEXT: call __extendsfdf2
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fcvt_d_h:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fcvt.d.h fa0, fa0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcvt_d_h:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: fcvt.s.h fa0, fa0
+; RV64IZFH-NEXT: call __extendsfdf2
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fcvt_d_h:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fcvt.d.h fa0, fa0
+; RV64IDZFH-NEXT: ret
+ %1 = fpext half %a to double
+ ret double %1
+}
+
+define half @bitcast_h_i16(i16 %a) nounwind {
+; RV32IZFH-LABEL: bitcast_h_i16:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmv.h.x fa0, a0
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: bitcast_h_i16:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fmv.h.x fa0, a0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: bitcast_h_i16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmv.h.x fa0, a0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: bitcast_h_i16:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fmv.h.x fa0, a0
+; RV64IDZFH-NEXT: ret
+ %1 = bitcast i16 %a to half
+ ret half %1
+}
+
+define i16 @bitcast_i16_h(half %a) nounwind {
+; RV32IZFH-LABEL: bitcast_i16_h:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmv.x.h a0, fa0
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: bitcast_i16_h:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fmv.x.h a0, fa0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: bitcast_i16_h:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmv.x.h a0, fa0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: bitcast_i16_h:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fmv.x.h a0, fa0
+; RV64IDZFH-NEXT: ret
+ %1 = bitcast half %a to i16
+ ret i16 %1
+}
diff --git a/llvm/test/CodeGen/RISCV/half-fcmp.ll b/llvm/test/CodeGen/RISCV/half-fcmp.ll
new file mode 100644
index 000000000000..e2dcbd3c8610
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/half-fcmp.ll
@@ -0,0 +1,285 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32f < %s | FileCheck -check-prefix=RV32IZFH %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64f < %s | FileCheck -check-prefix=RV64IZFH %s
+
+define i32 @fcmp_false(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_false:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: mv a0, zero
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_false:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: mv a0, zero
+; RV64IZFH-NEXT: ret
+ %1 = fcmp false half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_oeq(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_oeq:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_oeq:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp oeq half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_ogt(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_ogt:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: flt.h a0, fa1, fa0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_ogt:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: flt.h a0, fa1, fa0
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ogt half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_oge(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_oge:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fle.h a0, fa1, fa0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_oge:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fle.h a0, fa1, fa0
+; RV64IZFH-NEXT: ret
+ %1 = fcmp oge half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_olt(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_olt:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: flt.h a0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_olt:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: flt.h a0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp olt half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_ole(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_ole:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fle.h a0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_ole:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fle.h a0, fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ole half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_one(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_one:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa1, fa1
+; RV32IZFH-NEXT: feq.h a1, fa0, fa0
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: feq.h a1, fa0, fa1
+; RV32IZFH-NEXT: not a1, a1
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_one:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa1, fa1
+; RV64IZFH-NEXT: feq.h a1, fa0, fa0
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: feq.h a1, fa0, fa1
+; RV64IZFH-NEXT: not a1, a1
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: ret
+ %1 = fcmp one half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_ord(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_ord:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa1, fa1
+; RV32IZFH-NEXT: feq.h a1, fa0, fa0
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_ord:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa1, fa1
+; RV64IZFH-NEXT: feq.h a1, fa0, fa0
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ord half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_ueq(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_ueq:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa0, fa1
+; RV32IZFH-NEXT: feq.h a1, fa1, fa1
+; RV32IZFH-NEXT: feq.h a2, fa0, fa0
+; RV32IZFH-NEXT: and a1, a2, a1
+; RV32IZFH-NEXT: seqz a1, a1
+; RV32IZFH-NEXT: or a0, a0, a1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_ueq:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa0, fa1
+; RV64IZFH-NEXT: feq.h a1, fa1, fa1
+; RV64IZFH-NEXT: feq.h a2, fa0, fa0
+; RV64IZFH-NEXT: and a1, a2, a1
+; RV64IZFH-NEXT: seqz a1, a1
+; RV64IZFH-NEXT: or a0, a0, a1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ueq half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_ugt(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_ugt:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fle.h a0, fa0, fa1
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_ugt:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fle.h a0, fa0, fa1
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ugt half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_uge(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_uge:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: flt.h a0, fa0, fa1
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_uge:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: flt.h a0, fa0, fa1
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp uge half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_ult(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_ult:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fle.h a0, fa1, fa0
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_ult:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fle.h a0, fa1, fa0
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ult half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_ule(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_ule:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: flt.h a0, fa1, fa0
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_ule:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: flt.h a0, fa1, fa0
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ule half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_une(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_une:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa0, fa1
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_une:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa0, fa1
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp une half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_uno(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_uno:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa1, fa1
+; RV32IZFH-NEXT: feq.h a1, fa0, fa0
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: seqz a0, a0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_uno:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa1, fa1
+; RV64IZFH-NEXT: feq.h a1, fa0, fa0
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: seqz a0, a0
+; RV64IZFH-NEXT: ret
+ %1 = fcmp uno half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @fcmp_true(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fcmp_true:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi a0, zero, 1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fcmp_true:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi a0, zero, 1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp true half %a, %b
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
diff --git a/llvm/test/CodeGen/RISCV/half-imm.ll b/llvm/test/CodeGen/RISCV/half-imm.ll
new file mode 100644
index 000000000000..b707dccb6525
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/half-imm.ll
@@ -0,0 +1,39 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32f < %s | FileCheck -check-prefix=RV32IZFH %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64f < %s | FileCheck -check-prefix=RV64IZFH %s
+
+; TODO: constant pool shouldn't be necessary for RV32IZfh and RV64IZfh
+define half @half_imm() nounwind {
+; RV32IZFH-LABEL: half_imm:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: lui a0, %hi(.LCPI0_0)
+; RV32IZFH-NEXT: flh fa0, %lo(.LCPI0_0)(a0)
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: half_imm:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: lui a0, %hi(.LCPI0_0)
+; RV64IZFH-NEXT: flh fa0, %lo(.LCPI0_0)(a0)
+; RV64IZFH-NEXT: ret
+ ret half 3.0
+}
+
+define half @half_imm_op(half %a) nounwind {
+; RV32IZFH-LABEL: half_imm_op:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: lui a0, %hi(.LCPI1_0)
+; RV32IZFH-NEXT: flh ft0, %lo(.LCPI1_0)(a0)
+; RV32IZFH-NEXT: fadd.h fa0, fa0, ft0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: half_imm_op:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: lui a0, %hi(.LCPI1_0)
+; RV64IZFH-NEXT: flh ft0, %lo(.LCPI1_0)(a0)
+; RV64IZFH-NEXT: fadd.h fa0, fa0, ft0
+; RV64IZFH-NEXT: ret
+ %1 = fadd half %a, 1.0
+ ret half %1
+}
diff --git a/llvm/test/CodeGen/RISCV/half-intrinsics.ll b/llvm/test/CodeGen/RISCV/half-intrinsics.ll
new file mode 100644
index 000000000000..a23ac6798db3
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/half-intrinsics.ll
@@ -0,0 +1,195 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32f < %s | FileCheck -check-prefix=RV32IZFH %s
+; RUN: llc -mtriple=riscv32 -mattr=+d -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32d < %s | FileCheck -check-prefix=RV32IDZFH %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64f < %s | FileCheck -check-prefix=RV64IZFH %s
+; RUN: llc -mtriple=riscv64 -mattr=+d -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64d < %s | FileCheck -check-prefix=RV64IDZFH %s
+
+declare half @llvm.sqrt.f16(half)
+
+define half @sqrt_f16(half %a) nounwind {
+; RV32IZFH-LABEL: sqrt_f16:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fsqrt.h fa0, fa0
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: sqrt_f16:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fsqrt.h fa0, fa0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: sqrt_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fsqrt.h fa0, fa0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: sqrt_f16:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fsqrt.h fa0, fa0
+; RV64IDZFH-NEXT: ret
+ %1 = call half @llvm.sqrt.f16(half %a)
+ ret half %1
+}
+
+declare half @llvm.fma.f16(half, half, half)
+
+define half @fma_f16(half %a, half %b, half %c) nounwind {
+; RV32IZFH-LABEL: fma_f16:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmadd.h fa0, fa0, fa1, fa2
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fma_f16:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fmadd.h fa0, fa0, fa1, fa2
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fma_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmadd.h fa0, fa0, fa1, fa2
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fma_f16:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fmadd.h fa0, fa0, fa1, fa2
+; RV64IDZFH-NEXT: ret
+ %1 = call half @llvm.fma.f16(half %a, half %b, half %c)
+ ret half %1
+}
+
+declare half @llvm.fmuladd.f16(half, half, half)
+
+define half @fmuladd_f16(half %a, half %b, half %c) nounwind {
+; RV32IZFH-LABEL: fmuladd_f16:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmul.h ft0, fa0, fa1
+; RV32IZFH-NEXT: fadd.h fa0, ft0, fa2
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fmuladd_f16:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fmul.h ft0, fa0, fa1
+; RV32IDZFH-NEXT: fadd.h fa0, ft0, fa2
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fmuladd_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmul.h ft0, fa0, fa1
+; RV64IZFH-NEXT: fadd.h fa0, ft0, fa2
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fmuladd_f16:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fmul.h ft0, fa0, fa1
+; RV64IDZFH-NEXT: fadd.h fa0, ft0, fa2
+; RV64IDZFH-NEXT: ret
+ %1 = call half @llvm.fmuladd.f16(half %a, half %b, half %c)
+ ret half %1
+}
+
+declare half @llvm.fabs.f16(half)
+
+define half @fabs_f16(half %a) nounwind {
+; RV32IZFH-LABEL: fabs_f16:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fabs.h fa0, fa0
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: fabs_f16:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fabs.h fa0, fa0
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fabs_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fabs.h fa0, fa0
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: fabs_f16:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fabs.h fa0, fa0
+; RV64IDZFH-NEXT: ret
+ %1 = call half @llvm.fabs.f16(half %a)
+ ret half %1
+}
+
+declare half @llvm.minnum.f16(half, half)
+
+define half @minnum_f16(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: minnum_f16:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmin.h fa0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: minnum_f16:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fmin.h fa0, fa0, fa1
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: minnum_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmin.h fa0, fa0, fa1
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: minnum_f16:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fmin.h fa0, fa0, fa1
+; RV64IDZFH-NEXT: ret
+ %1 = call half @llvm.minnum.f16(half %a, half %b)
+ ret half %1
+}
+
+declare half @llvm.maxnum.f16(half, half)
+
+define half @maxnum_f16(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: maxnum_f16:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmax.h fa0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: maxnum_f16:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fmax.h fa0, fa0, fa1
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: maxnum_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmax.h fa0, fa0, fa1
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: maxnum_f16:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fmax.h fa0, fa0, fa1
+; RV64IDZFH-NEXT: ret
+ %1 = call half @llvm.maxnum.f16(half %a, half %b)
+ ret half %1
+}
+
+declare half @llvm.copysign.f16(half, half)
+
+define half @copysign_f16(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: copysign_f16:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fsgnj.h fa0, fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: copysign_f16:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fsgnj.h fa0, fa0, fa1
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: copysign_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fsgnj.h fa0, fa0, fa1
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: copysign_f16:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fsgnj.h fa0, fa0, fa1
+; RV64IDZFH-NEXT: ret
+ %1 = call half @llvm.copysign.f16(half %a, half %b)
+ ret half %1
+}
diff --git a/llvm/test/CodeGen/RISCV/half-isnan.ll b/llvm/test/CodeGen/RISCV/half-isnan.ll
new file mode 100644
index 000000000000..46c7e543208a
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/half-isnan.ll
@@ -0,0 +1,35 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32f < %s | FileCheck -check-prefix=RV32IZFH %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64f < %s | FileCheck -check-prefix=RV64IZFH %s
+
+define zeroext i1 @half_is_nan(half %a) nounwind {
+; RV32IZFH-LABEL: half_is_nan:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa0, fa0
+; RV32IZFH-NEXT: seqz a0, a0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: half_is_nan:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa0, fa0
+; RV64IZFH-NEXT: seqz a0, a0
+; RV64IZFH-NEXT: ret
+ %1 = fcmp uno half %a, 0.000000e+00
+ ret i1 %1
+}
+
+define zeroext i1 @half_not_nan(half %a) nounwind {
+; RV32IZFH-LABEL: half_not_nan:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa0, fa0
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: half_not_nan:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa0, fa0
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ord half %a, 0.000000e+00
+ ret i1 %1
+}
diff --git a/llvm/test/CodeGen/RISCV/half-mem.ll b/llvm/test/CodeGen/RISCV/half-mem.ll
new file mode 100644
index 000000000000..ded8b1803af0
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/half-mem.ll
@@ -0,0 +1,185 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32f < %s | FileCheck -check-prefix=RV32IZFH %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64f < %s | FileCheck -check-prefix=RV64IZFH %s
+
+define half @flh(half *%a) nounwind {
+; RV32IZFH-LABEL: flh:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: flh ft0, 0(a0)
+; RV32IZFH-NEXT: flh ft1, 6(a0)
+; RV32IZFH-NEXT: fadd.h fa0, ft0, ft1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: flh:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: flh ft0, 0(a0)
+; RV64IZFH-NEXT: flh ft1, 6(a0)
+; RV64IZFH-NEXT: fadd.h fa0, ft0, ft1
+; RV64IZFH-NEXT: ret
+ %1 = load half, half* %a
+ %2 = getelementptr half, half* %a, i32 3
+ %3 = load half, half* %2
+; Use both loaded values in an FP op to ensure an flh is used, even for the
+; soft half ABI
+ %4 = fadd half %1, %3
+ ret half %4
+}
+
+define void @fsh(half *%a, half %b, half %c) nounwind {
+; Use %b and %c in an FP op to ensure half precision floating point registers
+; are used, even for the soft half ABI
+; RV32IZFH-LABEL: fsh:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fadd.h ft0, fa0, fa1
+; RV32IZFH-NEXT: fsh ft0, 0(a0)
+; RV32IZFH-NEXT: fsh ft0, 16(a0)
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fsh:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fadd.h ft0, fa0, fa1
+; RV64IZFH-NEXT: fsh ft0, 0(a0)
+; RV64IZFH-NEXT: fsh ft0, 16(a0)
+; RV64IZFH-NEXT: ret
+ %1 = fadd half %b, %c
+ store half %1, half* %a
+ %2 = getelementptr half, half* %a, i32 8
+ store half %1, half* %2
+ ret void
+}
+
+; Check load and store to a global
+ at G = global half 0.0
+
+define half @flh_fsh_global(half %a, half %b) nounwind {
+; Use %a and %b in an FP op to ensure half precision floating point registers
+; are used, even for the soft half ABI
+; RV32IZFH-LABEL: flh_fsh_global:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fadd.h fa0, fa0, fa1
+; RV32IZFH-NEXT: lui a0, %hi(G)
+; RV32IZFH-NEXT: flh ft0, %lo(G)(a0)
+; RV32IZFH-NEXT: fsh fa0, %lo(G)(a0)
+; RV32IZFH-NEXT: addi a0, a0, %lo(G)
+; RV32IZFH-NEXT: flh ft0, 18(a0)
+; RV32IZFH-NEXT: fsh fa0, 18(a0)
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: flh_fsh_global:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fadd.h fa0, fa0, fa1
+; RV64IZFH-NEXT: lui a0, %hi(G)
+; RV64IZFH-NEXT: flh ft0, %lo(G)(a0)
+; RV64IZFH-NEXT: fsh fa0, %lo(G)(a0)
+; RV64IZFH-NEXT: addi a0, a0, %lo(G)
+; RV64IZFH-NEXT: flh ft0, 18(a0)
+; RV64IZFH-NEXT: fsh fa0, 18(a0)
+; RV64IZFH-NEXT: ret
+ %1 = fadd half %a, %b
+ %2 = load volatile half, half* @G
+ store half %1, half* @G
+ %3 = getelementptr half, half* @G, i32 9
+ %4 = load volatile half, half* %3
+ store half %1, half* %3
+ ret half %1
+}
+
+; Ensure that 1 is added to the high 20 bits if bit 11 of the low part is 1
+define half @flh_fsh_constant(half %a) nounwind {
+; RV32IZFH-LABEL: flh_fsh_constant:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: lui a0, 912092
+; RV32IZFH-NEXT: flh ft0, -273(a0)
+; RV32IZFH-NEXT: fadd.h fa0, fa0, ft0
+; RV32IZFH-NEXT: fsh fa0, -273(a0)
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: flh_fsh_constant:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: lui a0, 56
+; RV64IZFH-NEXT: addiw a0, a0, -1353
+; RV64IZFH-NEXT: slli a0, a0, 14
+; RV64IZFH-NEXT: flh ft0, -273(a0)
+; RV64IZFH-NEXT: fadd.h fa0, fa0, ft0
+; RV64IZFH-NEXT: fsh fa0, -273(a0)
+; RV64IZFH-NEXT: ret
+ %1 = inttoptr i32 3735928559 to half*
+ %2 = load volatile half, half* %1
+ %3 = fadd half %a, %2
+ store half %3, half* %1
+ ret half %3
+}
+
+declare void @notdead(i8*)
+
+define half @flh_stack(half %a) nounwind {
+; RV32IZFH-LABEL: flh_stack:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: fsw fs0, 8(sp)
+; RV32IZFH-NEXT: fmv.h fs0, fa0
+; RV32IZFH-NEXT: addi a0, sp, 4
+; RV32IZFH-NEXT: call notdead
+; RV32IZFH-NEXT: flh ft0, 4(sp)
+; RV32IZFH-NEXT: fadd.h fa0, ft0, fs0
+; RV32IZFH-NEXT: flw fs0, 8(sp)
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: flh_stack:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: fsw fs0, 4(sp)
+; RV64IZFH-NEXT: fmv.h fs0, fa0
+; RV64IZFH-NEXT: mv a0, sp
+; RV64IZFH-NEXT: call notdead
+; RV64IZFH-NEXT: flh ft0, 0(sp)
+; RV64IZFH-NEXT: fadd.h fa0, ft0, fs0
+; RV64IZFH-NEXT: flw fs0, 4(sp)
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+ %1 = alloca half, align 4
+ %2 = bitcast half* %1 to i8*
+ call void @notdead(i8* %2)
+ %3 = load half, half* %1
+ %4 = fadd half %3, %a ; force load in to FPR16
+ ret half %4
+}
+
+define void @fsh_stack(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: fsh_stack:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: addi sp, sp, -16
+; RV32IZFH-NEXT: sw ra, 12(sp)
+; RV32IZFH-NEXT: fadd.h ft0, fa0, fa1
+; RV32IZFH-NEXT: fsh ft0, 8(sp)
+; RV32IZFH-NEXT: addi a0, sp, 8
+; RV32IZFH-NEXT: call notdead
+; RV32IZFH-NEXT: lw ra, 12(sp)
+; RV32IZFH-NEXT: addi sp, sp, 16
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: fsh_stack:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: addi sp, sp, -16
+; RV64IZFH-NEXT: sd ra, 8(sp)
+; RV64IZFH-NEXT: fadd.h ft0, fa0, fa1
+; RV64IZFH-NEXT: fsh ft0, 4(sp)
+; RV64IZFH-NEXT: addi a0, sp, 4
+; RV64IZFH-NEXT: call notdead
+; RV64IZFH-NEXT: ld ra, 8(sp)
+; RV64IZFH-NEXT: addi sp, sp, 16
+; RV64IZFH-NEXT: ret
+ %1 = fadd half %a, %b ; force store from FPR16
+ %2 = alloca half, align 4
+ store half %1, half* %2
+ %3 = bitcast half* %2 to i8*
+ call void @notdead(i8* %3)
+ ret void
+}
diff --git a/llvm/test/CodeGen/RISCV/half-select-fcmp.ll b/llvm/test/CodeGen/RISCV/half-select-fcmp.ll
new file mode 100644
index 000000000000..a1b2ef1b61c6
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/half-select-fcmp.ll
@@ -0,0 +1,421 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi ilp32f < %s | FileCheck -check-prefix=RV32IZFH %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64f < %s | FileCheck -check-prefix=RV64IZFH %s
+
+define half @select_fcmp_false(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_false:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_false:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: ret
+ %1 = fcmp false half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_oeq(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_oeq:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa0, fa1
+; RV32IZFH-NEXT: bnez a0, .LBB1_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB1_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_oeq:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa0, fa1
+; RV64IZFH-NEXT: bnez a0, .LBB1_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB1_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp oeq half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_ogt(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_ogt:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: flt.h a0, fa1, fa0
+; RV32IZFH-NEXT: bnez a0, .LBB2_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB2_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_ogt:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: flt.h a0, fa1, fa0
+; RV64IZFH-NEXT: bnez a0, .LBB2_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB2_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ogt half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_oge(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_oge:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fle.h a0, fa1, fa0
+; RV32IZFH-NEXT: bnez a0, .LBB3_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB3_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_oge:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fle.h a0, fa1, fa0
+; RV64IZFH-NEXT: bnez a0, .LBB3_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB3_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp oge half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_olt(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_olt:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: flt.h a0, fa0, fa1
+; RV32IZFH-NEXT: bnez a0, .LBB4_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB4_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_olt:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: flt.h a0, fa0, fa1
+; RV64IZFH-NEXT: bnez a0, .LBB4_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB4_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp olt half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_ole(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_ole:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fle.h a0, fa0, fa1
+; RV32IZFH-NEXT: bnez a0, .LBB5_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB5_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_ole:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fle.h a0, fa0, fa1
+; RV64IZFH-NEXT: bnez a0, .LBB5_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB5_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ole half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_one(half %a, half %b) nounwind {
+; TODO: feq.h+sltiu+bne sequence could be optimised
+; RV32IZFH-LABEL: select_fcmp_one:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa1, fa1
+; RV32IZFH-NEXT: feq.h a1, fa0, fa0
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: feq.h a1, fa0, fa1
+; RV32IZFH-NEXT: not a1, a1
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: bnez a0, .LBB6_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB6_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_one:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa1, fa1
+; RV64IZFH-NEXT: feq.h a1, fa0, fa0
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: feq.h a1, fa0, fa1
+; RV64IZFH-NEXT: not a1, a1
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: bnez a0, .LBB6_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB6_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp one half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_ord(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_ord:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa1, fa1
+; RV32IZFH-NEXT: feq.h a1, fa0, fa0
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: bnez a0, .LBB7_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB7_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_ord:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa1, fa1
+; RV64IZFH-NEXT: feq.h a1, fa0, fa0
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: bnez a0, .LBB7_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB7_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ord half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_ueq(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_ueq:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa0, fa1
+; RV32IZFH-NEXT: feq.h a1, fa1, fa1
+; RV32IZFH-NEXT: feq.h a2, fa0, fa0
+; RV32IZFH-NEXT: and a1, a2, a1
+; RV32IZFH-NEXT: seqz a1, a1
+; RV32IZFH-NEXT: or a0, a0, a1
+; RV32IZFH-NEXT: bnez a0, .LBB8_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB8_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_ueq:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa0, fa1
+; RV64IZFH-NEXT: feq.h a1, fa1, fa1
+; RV64IZFH-NEXT: feq.h a2, fa0, fa0
+; RV64IZFH-NEXT: and a1, a2, a1
+; RV64IZFH-NEXT: seqz a1, a1
+; RV64IZFH-NEXT: or a0, a0, a1
+; RV64IZFH-NEXT: bnez a0, .LBB8_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB8_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ueq half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_ugt(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_ugt:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fle.h a0, fa0, fa1
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: bnez a0, .LBB9_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB9_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_ugt:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fle.h a0, fa0, fa1
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: bnez a0, .LBB9_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB9_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ugt half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_uge(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_uge:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: flt.h a0, fa0, fa1
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: bnez a0, .LBB10_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB10_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_uge:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: flt.h a0, fa0, fa1
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: bnez a0, .LBB10_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB10_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp uge half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_ult(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_ult:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fle.h a0, fa1, fa0
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: bnez a0, .LBB11_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB11_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_ult:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fle.h a0, fa1, fa0
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: bnez a0, .LBB11_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB11_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ult half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_ule(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_ule:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: flt.h a0, fa1, fa0
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: bnez a0, .LBB12_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB12_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_ule:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: flt.h a0, fa1, fa0
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: bnez a0, .LBB12_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB12_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp ule half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_une(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_une:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa0, fa1
+; RV32IZFH-NEXT: xori a0, a0, 1
+; RV32IZFH-NEXT: bnez a0, .LBB13_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB13_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_une:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa0, fa1
+; RV64IZFH-NEXT: xori a0, a0, 1
+; RV64IZFH-NEXT: bnez a0, .LBB13_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB13_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp une half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_uno(half %a, half %b) nounwind {
+; TODO: sltiu+bne could be optimized
+; RV32IZFH-LABEL: select_fcmp_uno:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a0, fa1, fa1
+; RV32IZFH-NEXT: feq.h a1, fa0, fa0
+; RV32IZFH-NEXT: and a0, a1, a0
+; RV32IZFH-NEXT: seqz a0, a0
+; RV32IZFH-NEXT: bnez a0, .LBB14_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: fmv.h fa0, fa1
+; RV32IZFH-NEXT: .LBB14_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_uno:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a0, fa1, fa1
+; RV64IZFH-NEXT: feq.h a1, fa0, fa0
+; RV64IZFH-NEXT: and a0, a1, a0
+; RV64IZFH-NEXT: seqz a0, a0
+; RV64IZFH-NEXT: bnez a0, .LBB14_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: fmv.h fa0, fa1
+; RV64IZFH-NEXT: .LBB14_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp uno half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+define half @select_fcmp_true(half %a, half %b) nounwind {
+; RV32IZFH-LABEL: select_fcmp_true:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: select_fcmp_true:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp true half %a, %b
+ %2 = select i1 %1, half %a, half %b
+ ret half %2
+}
+
+; Ensure that ISel succeeds for a select+fcmp that has an i32 result type.
+define i32 @i32_select_fcmp_oeq(half %a, half %b, i32 %c, i32 %d) nounwind {
+; RV32IZFH-LABEL: i32_select_fcmp_oeq:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: feq.h a2, fa0, fa1
+; RV32IZFH-NEXT: bnez a2, .LBB16_2
+; RV32IZFH-NEXT: # %bb.1:
+; RV32IZFH-NEXT: mv a0, a1
+; RV32IZFH-NEXT: .LBB16_2:
+; RV32IZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: i32_select_fcmp_oeq:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: feq.h a2, fa0, fa1
+; RV64IZFH-NEXT: bnez a2, .LBB16_2
+; RV64IZFH-NEXT: # %bb.1:
+; RV64IZFH-NEXT: mv a0, a1
+; RV64IZFH-NEXT: .LBB16_2:
+; RV64IZFH-NEXT: ret
+ %1 = fcmp oeq half %a, %b
+ %2 = select i1 %1, i32 %c, i32 %d
+ ret i32 %2
+}
diff --git a/llvm/test/CodeGen/RISCV/rv32i-rv64i-half.ll b/llvm/test/CodeGen/RISCV/rv32i-rv64i-half.ll
new file mode 100644
index 000000000000..ede169dce784
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rv32i-rv64i-half.ll
@@ -0,0 +1,73 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN: | FileCheck -check-prefix=RV32I %s
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
+; RUN: | FileCheck -check-prefix=RV64I %s
+
+; This file provides a simple sanity check of half operations for
+; RV32I and RV64I. This is primarily intended to ensure that custom
+; legalisation or DAG combines aren't incorrectly triggered when the Zfh
+; extension isn't enabled.
+
+define half @half_test(half %a, half %b) nounwind {
+; RV32I-LABEL: half_test:
+; RV32I: # %bb.0:
+; RV32I-NEXT: addi sp, sp, -16
+; RV32I-NEXT: sw ra, 12(sp)
+; RV32I-NEXT: sw s0, 8(sp)
+; RV32I-NEXT: sw s1, 4(sp)
+; RV32I-NEXT: sw s2, 0(sp)
+; RV32I-NEXT: mv s2, a1
+; RV32I-NEXT: lui a1, 16
+; RV32I-NEXT: addi s0, a1, -1
+; RV32I-NEXT: and a0, a0, s0
+; RV32I-NEXT: call __gnu_h2f_ieee
+; RV32I-NEXT: mv s1, a0
+; RV32I-NEXT: and a0, s2, s0
+; RV32I-NEXT: call __gnu_h2f_ieee
+; RV32I-NEXT: mv s0, a0
+; RV32I-NEXT: mv a0, s1
+; RV32I-NEXT: mv a1, s0
+; RV32I-NEXT: call __addsf3
+; RV32I-NEXT: mv a1, s0
+; RV32I-NEXT: call __divsf3
+; RV32I-NEXT: call __gnu_f2h_ieee
+; RV32I-NEXT: lw s2, 0(sp)
+; RV32I-NEXT: lw s1, 4(sp)
+; RV32I-NEXT: lw s0, 8(sp)
+; RV32I-NEXT: lw ra, 12(sp)
+; RV32I-NEXT: addi sp, sp, 16
+; RV32I-NEXT: ret
+;
+; RV64I-LABEL: half_test:
+; RV64I: # %bb.0:
+; RV64I-NEXT: addi sp, sp, -32
+; RV64I-NEXT: sd ra, 24(sp)
+; RV64I-NEXT: sd s0, 16(sp)
+; RV64I-NEXT: sd s1, 8(sp)
+; RV64I-NEXT: sd s2, 0(sp)
+; RV64I-NEXT: mv s2, a1
+; RV64I-NEXT: lui a1, 16
+; RV64I-NEXT: addiw s0, a1, -1
+; RV64I-NEXT: and a0, a0, s0
+; RV64I-NEXT: call __gnu_h2f_ieee
+; RV64I-NEXT: mv s1, a0
+; RV64I-NEXT: and a0, s2, s0
+; RV64I-NEXT: call __gnu_h2f_ieee
+; RV64I-NEXT: mv s0, a0
+; RV64I-NEXT: mv a0, s1
+; RV64I-NEXT: mv a1, s0
+; RV64I-NEXT: call __addsf3
+; RV64I-NEXT: mv a1, s0
+; RV64I-NEXT: call __divsf3
+; RV64I-NEXT: call __gnu_f2h_ieee
+; RV64I-NEXT: ld s2, 0(sp)
+; RV64I-NEXT: ld s1, 8(sp)
+; RV64I-NEXT: ld s0, 16(sp)
+; RV64I-NEXT: ld ra, 24(sp)
+; RV64I-NEXT: addi sp, sp, 32
+; RV64I-NEXT: ret
+ %1 = fadd half %a, %b
+ %2 = fdiv half %1, %b
+ ret half %2
+}
diff --git a/llvm/test/CodeGen/RISCV/rv64f-half-convert.ll b/llvm/test/CodeGen/RISCV/rv64f-half-convert.ll
new file mode 100644
index 000000000000..28b8ad5b902b
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rv64f-half-convert.ll
@@ -0,0 +1,171 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-zfh -verify-machineinstrs \
+; RUN: -target-abi lp64f < %s | FileCheck %s -check-prefix=RV64IZFH
+
+; This file exhaustively checks half<->i32 conversions. In general,
+; fcvt.l[u].h can be selected instead of fcvt.w[u].h because poison is
+; generated for an fpto[s|u]i conversion if the result doesn't fit in the
+; target type.
+
+define i32 @aext_fptosi(half %a) nounwind {
+; RV64IZFH-LABEL: aext_fptosi:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.l.h a0, fa0, rtz
+; RV64IZFH-NEXT: ret
+ %1 = fptosi half %a to i32
+ ret i32 %1
+}
+
+define signext i32 @sext_fptosi(half %a) nounwind {
+; RV64IZFH-LABEL: sext_fptosi:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.l.h a0, fa0, rtz
+; RV64IZFH-NEXT: ret
+ %1 = fptosi half %a to i32
+ ret i32 %1
+}
+
+define zeroext i32 @zext_fptosi(half %a) nounwind {
+; RV64IZFH-LABEL: zext_fptosi:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.l.h a0, fa0, rtz
+; RV64IZFH-NEXT: slli a0, a0, 32
+; RV64IZFH-NEXT: srli a0, a0, 32
+; RV64IZFH-NEXT: ret
+ %1 = fptosi half %a to i32
+ ret i32 %1
+}
+
+define i32 @aext_fptoui(half %a) nounwind {
+; RV64IZFH-LABEL: aext_fptoui:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.lu.h a0, fa0, rtz
+; RV64IZFH-NEXT: ret
+ %1 = fptoui half %a to i32
+ ret i32 %1
+}
+
+define signext i32 @sext_fptoui(half %a) nounwind {
+; RV64IZFH-LABEL: sext_fptoui:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.wu.h a0, fa0, rtz
+; RV64IZFH-NEXT: ret
+ %1 = fptoui half %a to i32
+ ret i32 %1
+}
+
+define zeroext i32 @zext_fptoui(half %a) nounwind {
+; RV64IZFH-LABEL: zext_fptoui:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.lu.h a0, fa0, rtz
+; RV64IZFH-NEXT: ret
+ %1 = fptoui half %a to i32
+ ret i32 %1
+}
+
+define i16 @bcvt_f16_to_aext_i16(half %a, half %b) nounwind {
+; RV64IZFH-LABEL: bcvt_f16_to_aext_i16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fadd.h ft0, fa0, fa1
+; RV64IZFH-NEXT: fmv.x.h a0, ft0
+; RV64IZFH-NEXT: ret
+ %1 = fadd half %a, %b
+ %2 = bitcast half %1 to i16
+ ret i16 %2
+}
+
+define signext i16 @bcvt_f16_to_sext_i16(half %a, half %b) nounwind {
+; RV64IZFH-LABEL: bcvt_f16_to_sext_i16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fadd.h ft0, fa0, fa1
+; RV64IZFH-NEXT: fmv.x.h a0, ft0
+; RV64IZFH-NEXT: slli a0, a0, 48
+; RV64IZFH-NEXT: srai a0, a0, 48
+; RV64IZFH-NEXT: ret
+ %1 = fadd half %a, %b
+ %2 = bitcast half %1 to i16
+ ret i16 %2
+}
+
+define zeroext i16 @bcvt_f16_to_zext_i16(half %a, half %b) nounwind {
+; RV64IZFH-LABEL: bcvt_f16_to_zext_i16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fadd.h ft0, fa0, fa1
+; RV64IZFH-NEXT: fmv.x.h a0, ft0
+; RV64IZFH-NEXT: lui a1, 16
+; RV64IZFH-NEXT: addiw a1, a1, -1
+; RV64IZFH-NEXT: and a0, a0, a1
+; RV64IZFH-NEXT: ret
+ %1 = fadd half %a, %b
+ %2 = bitcast half %1 to i16
+ ret i16 %2
+}
+
+define half @bcvt_i64_to_f16_via_i16(i64 %a, i64 %b) nounwind {
+; RV64IZFH-LABEL: bcvt_i64_to_f16_via_i16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmv.h.x ft0, a0
+; RV64IZFH-NEXT: fmv.h.x ft1, a1
+; RV64IZFH-NEXT: fadd.h fa0, ft0, ft1
+; RV64IZFH-NEXT: ret
+ %1 = trunc i64 %a to i16
+ %2 = trunc i64 %b to i16
+ %3 = bitcast i16 %1 to half
+ %4 = bitcast i16 %2 to half
+ %5 = fadd half %3, %4
+ ret half %5
+}
+
+define half @uitofp_aext_i32_to_f16(i32 %a) nounwind {
+; RV64IZFH-LABEL: uitofp_aext_i32_to_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.h.wu fa0, a0
+; RV64IZFH-NEXT: ret
+ %1 = uitofp i32 %a to half
+ ret half %1
+}
+
+define half @uitofp_sext_i32_to_f16(i32 signext %a) nounwind {
+; RV64IZFH-LABEL: uitofp_sext_i32_to_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.h.wu fa0, a0
+; RV64IZFH-NEXT: ret
+ %1 = uitofp i32 %a to half
+ ret half %1
+}
+
+define half @uitofp_zext_i32_to_f16(i32 zeroext %a) nounwind {
+; RV64IZFH-LABEL: uitofp_zext_i32_to_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.h.wu fa0, a0
+; RV64IZFH-NEXT: ret
+ %1 = uitofp i32 %a to half
+ ret half %1
+}
+
+define half @sitofp_aext_i32_to_f16(i32 %a) nounwind {
+; RV64IZFH-LABEL: sitofp_aext_i32_to_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.h.w fa0, a0
+; RV64IZFH-NEXT: ret
+ %1 = sitofp i32 %a to half
+ ret half %1
+}
+
+define half @sitofp_sext_i32_to_f16(i32 signext %a) nounwind {
+; RV64IZFH-LABEL: sitofp_sext_i32_to_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.h.l fa0, a0
+; RV64IZFH-NEXT: ret
+ %1 = sitofp i32 %a to half
+ ret half %1
+}
+
+define half @sitofp_zext_i32_to_f16(i32 zeroext %a) nounwind {
+; RV64IZFH-LABEL: sitofp_zext_i32_to_f16:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fcvt.h.w fa0, a0
+; RV64IZFH-NEXT: ret
+ %1 = sitofp i32 %a to half
+ ret half %1
+}
diff --git a/llvm/test/CodeGen/RISCV/zfh-imm.ll b/llvm/test/CodeGen/RISCV/zfh-imm.ll
new file mode 100644
index 000000000000..c2559033ed71
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/zfh-imm.ll
@@ -0,0 +1,59 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -target-abi ilp32f -mattr=+experimental-zfh < %s \
+; RUN: | FileCheck --check-prefix=RV32IZFH %s
+; RUN: llc -mtriple=riscv32 -target-abi ilp32d -mattr=+experimental-zfh,+d < %s \
+; RUN: | FileCheck --check-prefix=RV32IDZFH %s
+; RUN: llc -mtriple=riscv64 -target-abi lp64f -mattr=+experimental-zfh < %s \
+; RUN: | FileCheck --check-prefix=RV64IZFH %s
+; RUN: llc -mtriple=riscv64 -target-abi lp64d -mattr=+experimental-zfh,+d < %s \
+; RUN: | FileCheck --check-prefix=RV64IDZFH %s
+
+define half @f16_positive_zero(half *%pf) nounwind {
+; RV32IZFH-LABEL: f16_positive_zero:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: fmv.h.x fa0, zero
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: f16_positive_zero:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: fmv.h.x fa0, zero
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: f16_positive_zero:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: fmv.h.x fa0, zero
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: f16_positive_zero:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: fmv.h.x fa0, zero
+; RV64IDZFH-NEXT: ret
+ ret half 0.0
+}
+
+define half @f16_negative_zero(half *%pf) nounwind {
+; RV32IZFH-LABEL: f16_negative_zero:
+; RV32IZFH: # %bb.0:
+; RV32IZFH-NEXT: lui a0, %hi(.LCPI1_0)
+; RV32IZFH-NEXT: flh fa0, %lo(.LCPI1_0)(a0)
+; RV32IZFH-NEXT: ret
+;
+; RV32IDZFH-LABEL: f16_negative_zero:
+; RV32IDZFH: # %bb.0:
+; RV32IDZFH-NEXT: lui a0, %hi(.LCPI1_0)
+; RV32IDZFH-NEXT: flh fa0, %lo(.LCPI1_0)(a0)
+; RV32IDZFH-NEXT: ret
+;
+; RV64IZFH-LABEL: f16_negative_zero:
+; RV64IZFH: # %bb.0:
+; RV64IZFH-NEXT: lui a0, %hi(.LCPI1_0)
+; RV64IZFH-NEXT: flh fa0, %lo(.LCPI1_0)(a0)
+; RV64IZFH-NEXT: ret
+;
+; RV64IDZFH-LABEL: f16_negative_zero:
+; RV64IDZFH: # %bb.0:
+; RV64IDZFH-NEXT: lui a0, %hi(.LCPI1_0)
+; RV64IDZFH-NEXT: flh fa0, %lo(.LCPI1_0)(a0)
+; RV64IDZFH-NEXT: ret
+ ret half -0.0
+}
diff --git a/llvm/test/MC/RISCV/rv32i-invalid.s b/llvm/test/MC/RISCV/rv32i-invalid.s
index 7e03a0c49c5e..80ac401c67a5 100644
--- a/llvm/test/MC/RISCV/rv32i-invalid.s
+++ b/llvm/test/MC/RISCV/rv32i-invalid.s
@@ -172,6 +172,7 @@ xor s2, s2 # CHECK: :[[@LINE]]:1: error: too few operands for instruction
mul a4, ra, s0 # CHECK: :[[@LINE]]:1: error: instruction requires the following: 'M' (Integer Multiplication and Division)
amomaxu.w s5, s4, (s3) # CHECK: :[[@LINE]]:1: error: instruction requires the following: 'A' (Atomic Instructions)
fadd.s ft0, ft1, ft2 # CHECK: :[[@LINE]]:1: error: instruction requires the following: 'F' (Single-Precision Floating-Point)
+fadd.h ft0, ft1, ft2 # CHECK: :[[@LINE]]:1: error: instruction requires the following: 'Zfh' (Half-Precision Floating-Point)
# Using floating point registers when integer registers are expected
addi a2, ft0, 24 # CHECK: :[[@LINE]]:10: error: invalid operand for instruction
diff --git a/llvm/test/MC/RISCV/rv32zfh-invalid.s b/llvm/test/MC/RISCV/rv32zfh-invalid.s
new file mode 100644
index 000000000000..3bf3b46ee8e4
--- /dev/null
+++ b/llvm/test/MC/RISCV/rv32zfh-invalid.s
@@ -0,0 +1,36 @@
+# RUN: not llvm-mc -triple riscv32 -mattr=+experimental-zfh < %s 2>&1 | \
+# RUN: FileCheck %s
+
+# Out of range immediates
+## simm12
+flh ft1, -2049(a0) # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047]
+fsh ft2, 2048(a1) # CHECK: :[[@LINE]]:10: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047]
+
+# Memory operand not formatted correctly
+flh ft1, a0, -200 # CHECK: :[[@LINE]]:14: error: invalid operand for instruction
+fsw ft2, a1, 100 # CHECK: :[[@LINE]]:14: error: invalid operand for instruction
+
+# Invalid register names
+flh ft15, 100(a0) # CHECK: :[[@LINE]]:5: error: invalid operand for instruction
+flh ft1, 100(a10) # CHECK: :[[@LINE]]:14: error: expected register
+fsgnjn.h fa100, fa2, fa3 # CHECK: :[[@LINE]]:10: error: invalid operand for instruction
+
+# Integer registers where FP regs are expected
+fmv.x.h fs7, a2 # CHECK: :[[@LINE]]:9: error: invalid operand for instruction
+
+# FP registers where integer regs are expected
+fmv.h.x a8, ft2 # CHECK: :[[@LINE]]:9: error: invalid operand for instruction
+
+# Rounding mode when a register is expected
+fmadd.h f10, f11, f12, ree # CHECK: :[[@LINE]]:24: error: invalid operand for instruction
+
+# Invalid rounding modes
+fmadd.h f10, f11, f12, f13, ree # CHECK: :[[@LINE]]:29: error: operand must be a valid floating point rounding mode mnemonic
+fmsub.h f14, f15, f16, f17, 0 # CHECK: :[[@LINE]]:29: error: operand must be a valid floating point rounding mode mnemonic
+fnmsub.h f18, f19, f20, f21, 0b111 # CHECK: :[[@LINE]]:30: error: operand must be a valid floating point rounding mode mnemonic
+
+# Integer registers where FP regs are expected
+fadd.h a2, a1, a0 # CHECK: :[[@LINE]]:8: error: invalid operand for instruction
+
+# FP registers where integer regs are expected
+fcvt.wu.h ft2, a1 # CHECK: :[[@LINE]]:11: error: invalid operand for instruction
diff --git a/llvm/test/MC/RISCV/rv32zfh-valid.s b/llvm/test/MC/RISCV/rv32zfh-valid.s
new file mode 100644
index 000000000000..a6853fb95ea0
--- /dev/null
+++ b/llvm/test/MC/RISCV/rv32zfh-valid.s
@@ -0,0 +1,166 @@
+# RUN: llvm-mc %s -triple=riscv32 -mattr=+experimental-zfh -riscv-no-aliases -show-encoding \
+# RUN: | FileCheck -check-prefixes=CHECK-ASM,CHECK-ASM-AND-OBJ %s
+# RUN: llvm-mc %s -triple=riscv64 -mattr=+experimental-zfh -riscv-no-aliases -show-encoding \
+# RUN: | FileCheck -check-prefixes=CHECK-ASM,CHECK-ASM-AND-OBJ %s
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+experimental-zfh < %s \
+# RUN: | llvm-objdump --mattr=+experimental-zfh -M no-aliases -d -r - \
+# RUN: | FileCheck -check-prefixes=CHECK-OBJ,CHECK-ASM-AND-OBJ %s
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+experimental-zfh < %s \
+# RUN: | llvm-objdump --mattr=+experimental-zfh -M no-aliases -d -r - \
+# RUN: | FileCheck -check-prefixes=CHECK-OBJ,CHECK-ASM-AND-OBJ %s
+
+# CHECK-ASM-AND-OBJ: flh ft0, 12(a0)
+# CHECK-ASM: encoding: [0x07,0x10,0xc5,0x00]
+flh f0, 12(a0)
+# CHECK-ASM-AND-OBJ: flh ft1, 4(ra)
+# CHECK-ASM: encoding: [0x87,0x90,0x40,0x00]
+flh f1, +4(ra)
+# CHECK-ASM-AND-OBJ: flh ft2, -2048(a3)
+# CHECK-ASM: encoding: [0x07,0x91,0x06,0x80]
+flh f2, -2048(x13)
+# CHECK-ASM-AND-OBJ: flh ft3, -2048(s1)
+# CHECK-ASM: encoding: [0x87,0x91,0x04,0x80]
+flh f3, %lo(2048)(s1)
+# CHECK-ASM-AND-OBJ: flh ft4, 2047(s2)
+# CHECK-ASM: encoding: [0x07,0x12,0xf9,0x7f]
+flh f4, 2047(s2)
+# CHECK-ASM-AND-OBJ: flh ft5, 0(s3)
+# CHECK-ASM: encoding: [0x87,0x92,0x09,0x00]
+flh f5, 0(s3)
+
+# CHECK-ASM-AND-OBJ: fsh ft6, 2047(s4)
+# CHECK-ASM: encoding: [0xa7,0x1f,0x6a,0x7e]
+fsh f6, 2047(s4)
+# CHECK-ASM-AND-OBJ: fsh ft7, -2048(s5)
+# CHECK-ASM: encoding: [0x27,0x90,0x7a,0x80]
+fsh f7, -2048(s5)
+# CHECK-ASM-AND-OBJ: fsh fs0, -2048(s6)
+# CHECK-ASM: encoding: [0x27,0x10,0x8b,0x80]
+fsh f8, %lo(2048)(s6)
+# CHECK-ASM-AND-OBJ: fsh fs1, 999(s7)
+# CHECK-ASM: encoding: [0xa7,0x93,0x9b,0x3e]
+fsh f9, 999(s7)
+
+# CHECK-ASM-AND-OBJ: fmadd.h fa0, fa1, fa2, fa3, dyn
+# CHECK-ASM: encoding: [0x43,0xf5,0xc5,0x6c]
+fmadd.h f10, f11, f12, f13, dyn
+# CHECK-ASM-AND-OBJ: fmsub.h fa4, fa5, fa6, fa7, dyn
+# CHECK-ASM: encoding: [0x47,0xf7,0x07,0x8d]
+fmsub.h f14, f15, f16, f17, dyn
+# CHECK-ASM-AND-OBJ: fnmsub.h fs2, fs3, fs4, fs5, dyn
+# CHECK-ASM: encoding: [0x4b,0xf9,0x49,0xad]
+fnmsub.h f18, f19, f20, f21, dyn
+# CHECK-ASM-AND-OBJ: fnmadd.h fs6, fs7, fs8, fs9, dyn
+# CHECK-ASM: encoding: [0x4f,0xfb,0x8b,0xcd]
+fnmadd.h f22, f23, f24, f25, dyn
+
+# CHECK-ASM-AND-OBJ: fadd.h fs10, fs11, ft8, dyn
+# CHECK-ASM: encoding: [0x53,0xfd,0xcd,0x05]
+fadd.h f26, f27, f28, dyn
+# CHECK-ASM-AND-OBJ: fsub.h ft9, ft10, ft11, dyn
+# CHECK-ASM: encoding: [0xd3,0x7e,0xff,0x0d]
+fsub.h f29, f30, f31, dyn
+# CHECK-ASM-AND-OBJ: fmul.h ft0, ft1, ft2, dyn
+# CHECK-ASM: encoding: [0x53,0xf0,0x20,0x14]
+fmul.h ft0, ft1, ft2, dyn
+# CHECK-ASM-AND-OBJ: fdiv.h ft3, ft4, ft5, dyn
+# CHECK-ASM: encoding: [0xd3,0x71,0x52,0x1c]
+fdiv.h ft3, ft4, ft5, dyn
+# CHECK-ASM-AND-OBJ: fsqrt.h ft6, ft7, dyn
+# CHECK-ASM: encoding: [0x53,0xf3,0x03,0x5c]
+fsqrt.h ft6, ft7, dyn
+# CHECK-ASM-AND-OBJ: fsgnj.h fs1, fa0, fa1
+# CHECK-ASM: encoding: [0xd3,0x04,0xb5,0x24]
+fsgnj.h fs1, fa0, fa1
+# CHECK-ASM-AND-OBJ: fsgnjn.h fa1, fa3, fa4
+# CHECK-ASM: encoding: [0xd3,0x95,0xe6,0x24]
+fsgnjn.h fa1, fa3, fa4
+# CHECK-ASM-AND-OBJ: fsgnjx.h fa4, fa3, fa2
+# CHECK-ASM: encoding: [0x53,0xa7,0xc6,0x24]
+fsgnjx.h fa4, fa3, fa2
+# CHECK-ASM-AND-OBJ: fmin.h fa5, fa6, fa7
+# CHECK-ASM: encoding: [0xd3,0x07,0x18,0x2d]
+fmin.h fa5, fa6, fa7
+# CHECK-ASM-AND-OBJ: fmax.h fs2, fs3, fs4
+# CHECK-ASM: encoding: [0x53,0x99,0x49,0x2d]
+fmax.h fs2, fs3, fs4
+# CHECK-ASM-AND-OBJ: fcvt.w.h a0, fs5, dyn
+# CHECK-ASM: encoding: [0x53,0xf5,0x0a,0xc4]
+fcvt.w.h a0, fs5, dyn
+# CHECK-ASM-AND-OBJ: fcvt.wu.h a1, fs6, dyn
+# CHECK-ASM: encoding: [0xd3,0x75,0x1b,0xc4]
+fcvt.wu.h a1, fs6, dyn
+# CHECK-ASM-AND-OBJ: fmv.x.h a2, fs7
+# CHECK-ASM: encoding: [0x53,0x86,0x0b,0xe4]
+fmv.x.h a2, fs7
+# CHECK-ASM-AND-OBJ: feq.h a1, fs8, fs9
+# CHECK-ASM: encoding: [0xd3,0x25,0x9c,0xa5]
+feq.h a1, fs8, fs9
+# CHECK-ASM-AND-OBJ: flt.h a2, fs10, fs11
+# CHECK-ASM: encoding: [0x53,0x16,0xbd,0xa5]
+flt.h a2, fs10, fs11
+# CHECK-ASM-AND-OBJ: fle.h a3, ft8, ft9
+# CHECK-ASM: encoding: [0xd3,0x06,0xde,0xa5]
+fle.h a3, ft8, ft9
+# CHECK-ASM-AND-OBJ: fclass.h a3, ft10
+# CHECK-ASM: encoding: [0xd3,0x16,0x0f,0xe4]
+fclass.h a3, ft10
+# CHECK-ASM-AND-OBJ: fcvt.h.w ft11, a4, dyn
+# CHECK-ASM: encoding: [0xd3,0x7f,0x07,0xd4]
+fcvt.h.w ft11, a4, dyn
+# CHECK-ASM-AND-OBJ: fcvt.h.wu ft0, a5, dyn
+# CHECK-ASM: encoding: [0x53,0xf0,0x17,0xd4]
+fcvt.h.wu ft0, a5, dyn
+# CHECK-ASM-AND-OBJ: fmv.h.x ft1, a6
+# CHECK-ASM: encoding: [0xd3,0x00,0x08,0xf4]
+fmv.h.x ft1, a6
+
+# Rounding modes
+
+# CHECK-ASM-AND-OBJ: fmadd.h fa0, fa1, fa2, fa3, rne
+# CHECK-ASM: encoding: [0x43,0x85,0xc5,0x6c]
+fmadd.h f10, f11, f12, f13, rne
+# CHECK-ASM-AND-OBJ: fmsub.h fa4, fa5, fa6, fa7, rtz
+# CHECK-ASM: encoding: [0x47,0x97,0x07,0x8d]
+fmsub.h f14, f15, f16, f17, rtz
+# CHECK-ASM-AND-OBJ: fnmsub.h fs2, fs3, fs4, fs5, rdn
+# CHECK-ASM: encoding: [0x4b,0xa9,0x49,0xad]
+fnmsub.h f18, f19, f20, f21, rdn
+# CHECK-ASM-AND-OBJ: fnmadd.h fs6, fs7, fs8, fs9, rup
+# CHECK-ASM: encoding: [0x4f,0xbb,0x8b,0xcd]
+fnmadd.h f22, f23, f24, f25, rup
+# CHECK-ASM-AND-OBJ: fmadd.h fa0, fa1, fa2, fa3, rmm
+# CHECK-ASM: encoding: [0x43,0xc5,0xc5,0x6c]
+fmadd.h f10, f11, f12, f13, rmm
+# CHECK-ASM-AND-OBJ: fmsub.h fa4, fa5, fa6, fa7
+# CHECK-ASM: encoding: [0x47,0xf7,0x07,0x8d]
+fmsub.h f14, f15, f16, f17, dyn
+
+# CHECK-ASM-AND-OBJ: fadd.h fs10, fs11, ft8, rne
+# CHECK-ASM: encoding: [0x53,0x8d,0xcd,0x05]
+fadd.h f26, f27, f28, rne
+# CHECK-ASM-AND-OBJ: fsub.h ft9, ft10, ft11, rtz
+# CHECK-ASM: encoding: [0xd3,0x1e,0xff,0x0d]
+fsub.h f29, f30, f31, rtz
+# CHECK-ASM-AND-OBJ: fmul.h ft0, ft1, ft2, rdn
+# CHECK-ASM: encoding: [0x53,0xa0,0x20,0x14]
+fmul.h ft0, ft1, ft2, rdn
+# CHECK-ASM-AND-OBJ: fdiv.h ft3, ft4, ft5, rup
+# CHECK-ASM: encoding: [0xd3,0x31,0x52,0x1c]
+fdiv.h ft3, ft4, ft5, rup
+
+# CHECK-ASM-AND-OBJ: fsqrt.h ft6, ft7, rmm
+# CHECK-ASM: encoding: [0x53,0xc3,0x03,0x5c]
+fsqrt.h ft6, ft7, rmm
+# CHECK-ASM-AND-OBJ: fcvt.w.h a0, fs5, rup
+# CHECK-ASM: encoding: [0x53,0xb5,0x0a,0xc4]
+fcvt.w.h a0, fs5, rup
+# CHECK-ASM-AND-OBJ: fcvt.wu.h a1, fs6, rdn
+# CHECK-ASM: encoding: [0xd3,0x25,0x1b,0xc4]
+fcvt.wu.h a1, fs6, rdn
+# CHECK-ASM-AND-OBJ: fcvt.h.w ft11, a4, rtz
+# CHECK-ASM: encoding: [0xd3,0x1f,0x07,0xd4]
+fcvt.h.w ft11, a4, rtz
+# CHECK-ASM-AND-OBJ: fcvt.h.wu ft0, a5, rne
+# CHECK-ASM: encoding: [0x53,0x80,0x17,0xd4]
+fcvt.h.wu ft0, a5, rne
diff --git a/llvm/test/MC/RISCV/rv64zfh-invalid.s b/llvm/test/MC/RISCV/rv64zfh-invalid.s
new file mode 100644
index 000000000000..881957099577
--- /dev/null
+++ b/llvm/test/MC/RISCV/rv64zfh-invalid.s
@@ -0,0 +1,10 @@
+# RUN: not llvm-mc -triple riscv64 -mattr=+experimental-zfh < %s 2>&1 | \
+# RUN: FileCheck %s
+
+# Integer registers where FP regs are expected
+fcvt.l.h ft0, a0 # CHECK: :[[@LINE]]:10: error: invalid operand for instruction
+fcvt.lu.h ft1, a1 # CHECK: :[[@LINE]]:11: error: invalid operand for instruction
+
+# FP registers where integer regs are expected
+fcvt.h.l a2, ft2 # CHECK: :[[@LINE]]:10: error: invalid operand for instruction
+fcvt.h.lu a3, ft3 # CHECK: :[[@LINE]]:11: error: invalid operand for instruction
diff --git a/llvm/test/MC/RISCV/rv64zfh-valid.s b/llvm/test/MC/RISCV/rv64zfh-valid.s
new file mode 100644
index 000000000000..0a667f668d4f
--- /dev/null
+++ b/llvm/test/MC/RISCV/rv64zfh-valid.s
@@ -0,0 +1,39 @@
+# RUN: llvm-mc %s -triple=riscv64 -mattr=+experimental-zfh -riscv-no-aliases -show-encoding \
+# RUN: | FileCheck -check-prefixes=CHECK-ASM,CHECK-ASM-AND-OBJ %s
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+experimental-zfh < %s \
+# RUN: | llvm-objdump --mattr=+experimental-zfh -M no-aliases -d -r - \
+# RUN: | FileCheck -check-prefixes=CHECK-OBJ,CHECK-ASM-AND-OBJ %s
+#
+# RUN: not llvm-mc -triple riscv32 -mattr=+experimental-zfh < %s 2>&1 \
+# RUN: | FileCheck -check-prefix=CHECK-RV32 %s
+
+# CHECK-ASM-AND-OBJ: fcvt.l.h a0, ft0, dyn
+# CHECK-ASM: encoding: [0x53,0x75,0x20,0xc4]
+# CHECK-RV32: :[[@LINE+1]]:1: error: instruction requires the following: RV64I Base Instruction Set
+fcvt.l.h a0, ft0, dyn
+# CHECK-ASM-AND-OBJ: fcvt.lu.h a1, ft1, dyn
+# CHECK-ASM: encoding: [0xd3,0xf5,0x30,0xc4]
+# CHECK-RV32: :[[@LINE+1]]:1: error: instruction requires the following: RV64I Base Instruction Set
+fcvt.lu.h a1, ft1, dyn
+# CHECK-ASM-AND-OBJ: fcvt.h.l ft2, a2, dyn
+# CHECK-ASM: encoding: [0x53,0x71,0x26,0xd4]
+# CHECK-RV32: :[[@LINE+1]]:1: error: instruction requires the following: RV64I Base Instruction Set
+fcvt.h.l ft2, a2, dyn
+# CHECK-ASM-AND-OBJ: fcvt.h.lu ft3, a3, dyn
+# CHECK-ASM: encoding: [0xd3,0xf1,0x36,0xd4]
+# CHECK-RV32: :[[@LINE+1]]:1: error: instruction requires the following: RV64I Base Instruction Set
+fcvt.h.lu ft3, a3, dyn
+
+# Rounding modes
+# CHECK-ASM-AND-OBJ: fcvt.l.h a4, ft4, rne
+# CHECK-RV32: :[[@LINE+1]]:1: error: instruction requires the following: RV64I Base Instruction Set
+fcvt.l.h a4, ft4, rne
+# CHECK-ASM-AND-OBJ: fcvt.lu.h a5, ft5, rtz
+# CHECK-RV32: :[[@LINE+1]]:1: error: instruction requires the following: RV64I Base Instruction Set
+fcvt.lu.h a5, ft5, rtz
+# CHECK-ASM-AND-OBJ: fcvt.h.l ft6, a6, rdn
+# CHECK-RV32: :[[@LINE+1]]:1: error: instruction requires the following: RV64I Base Instruction Set
+fcvt.h.l ft6, a6, rdn
+# CHECK-ASM-AND-OBJ: fcvt.h.lu ft7, a7, rup
+# CHECK-RV32: :[[@LINE+1]]:1: error: instruction requires the following: RV64I Base Instruction Set
+fcvt.h.lu ft7, a7, rup
diff --git a/llvm/test/MC/RISCV/rvzfh-aliases-valid.s b/llvm/test/MC/RISCV/rvzfh-aliases-valid.s
new file mode 100644
index 000000000000..32ebda68fc2a
--- /dev/null
+++ b/llvm/test/MC/RISCV/rvzfh-aliases-valid.s
@@ -0,0 +1,99 @@
+# RUN: llvm-mc %s -triple=riscv32 -mattr=+experimental-zfh -riscv-no-aliases \
+# RUN: | FileCheck -check-prefix=CHECK-INST %s
+# RUN: llvm-mc %s -triple=riscv32 -mattr=+experimental-zfh \
+# RUN: | FileCheck -check-prefix=CHECK-ALIAS %s
+# RUN: llvm-mc %s -triple=riscv64 -mattr=+experimental-zfh -riscv-no-aliases \
+# RUN: | FileCheck -check-prefix=CHECK-INST %s
+# RUN: llvm-mc %s -triple=riscv64 -mattr=+experimental-zfh \
+# RUN: | FileCheck -check-prefix=CHECK-ALIAS %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-zfh < %s \
+# RUN: | llvm-objdump -d --mattr=+experimental-zfh -M no-aliases - \
+# RUN: | FileCheck -check-prefix=CHECK-INST %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-zfh < %s \
+# RUN: | llvm-objdump -d --mattr=+experimental-zfh - \
+# RUN: | FileCheck -check-prefix=CHECK-ALIAS %s
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+experimental-zfh < %s \
+# RUN: | llvm-objdump -d --mattr=+experimental-zfh -M no-aliases - \
+# RUN: | FileCheck -check-prefix=CHECK-INST %s
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+experimental-zfh < %s \
+# RUN: | llvm-objdump -d --mattr=+experimental-zfh - \
+# RUN: | FileCheck -check-prefix=CHECK-ALIAS %s
+
+##===----------------------------------------------------------------------===##
+## Assembler Pseudo Instructions (User-Level ISA, Version 2.2, Chapter 20)
+##===----------------------------------------------------------------------===##
+
+# CHECK-INST: fsgnj.h ft0, ft1, ft1
+# CHECK-ALIAS: fmv.h ft0, ft1
+fmv.h f0, f1
+# CHECK-INST: fsgnjx.h ft1, ft2, ft2
+# CHECK-ALIAS: fabs.h ft1, ft2
+fabs.h f1, f2
+# CHECK-INST: fsgnjn.h ft2, ft3, ft3
+# CHECK-ALIAS: fneg.h ft2, ft3
+fneg.h f2, f3
+
+# CHECK-INST: flt.h tp, ft6, ft5
+# CHECK-ALIAS: flt.h tp, ft6, ft5
+fgt.h x4, f5, f6
+# CHECK-INST: fle.h t2, fs1, fs0
+# CHECK-ALIAS: fle.h t2, fs1, fs0
+fge.h x7, f8, f9
+
+# CHECK-INST: fmv.x.h a2, fs7
+# CHECK-ALIAS: fmv.x.h a2, fs7
+fmv.x.h a2, fs7
+# CHECK-INST: fmv.h.x ft1, a6
+# CHECK-ALIAS: fmv.h.x ft1, a6
+fmv.h.x ft1, a6
+
+# CHECK-INST: flh ft0, 0(a0)
+# CHECK-ALIAS: flh ft0, 0(a0)
+flh f0, (x10)
+# CHECK-INST: fsh ft0, 0(a0)
+# CHECK-ALIAS: fsh ft0, 0(a0)
+fsh f0, (x10)
+
+##===----------------------------------------------------------------------===##
+## Aliases which omit the rounding mode.
+##===----------------------------------------------------------------------===##
+
+# CHECK-INST: fmadd.h fa0, fa1, fa2, fa3, dyn
+# CHECK-ALIAS: fmadd.h fa0, fa1, fa2, fa3{{[[:space:]]}}
+fmadd.h f10, f11, f12, f13
+# CHECK-INST: fmsub.h fa4, fa5, fa6, fa7, dyn
+# CHECK-ALIAS: fmsub.h fa4, fa5, fa6, fa7{{[[:space:]]}}
+fmsub.h f14, f15, f16, f17
+# CHECK-INST: fnmsub.h fs2, fs3, fs4, fs5, dyn
+# CHECK-ALIAS: fnmsub.h fs2, fs3, fs4, fs5{{[[:space:]]}}
+fnmsub.h f18, f19, f20, f21
+# CHECK-INST: fnmadd.h fs6, fs7, fs8, fs9, dyn
+# CHECK-ALIAS: fnmadd.h fs6, fs7, fs8, fs9{{[[:space:]]}}
+fnmadd.h f22, f23, f24, f25
+# CHECK-INST: fadd.h fs10, fs11, ft8, dyn
+# CHECK-ALIAS: fadd.h fs10, fs11, ft8{{[[:space:]]}}
+fadd.h f26, f27, f28
+# CHECK-INST: fsub.h ft9, ft10, ft11, dyn
+# CHECK-ALIAS: fsub.h ft9, ft10, ft11{{[[:space:]]}}
+fsub.h f29, f30, f31
+# CHECK-INST: fmul.h ft0, ft1, ft2, dyn
+# CHECK-ALIAS: fmul.h ft0, ft1, ft2{{[[:space:]]}}
+fmul.h ft0, ft1, ft2
+# CHECK-INST: fdiv.h ft3, ft4, ft5, dyn
+# CHECK-ALIAS: fdiv.h ft3, ft4, ft5{{[[:space:]]}}
+fdiv.h ft3, ft4, ft5
+# CHECK-INST: fsqrt.h ft6, ft7, dyn
+# CHECK-ALIAS: fsqrt.h ft6, ft7{{[[:space:]]}}
+fsqrt.h ft6, ft7
+# CHECK-INST: fcvt.w.h a0, fs5, dyn
+# CHECK-ALIAS: fcvt.w.h a0, fs5{{[[:space:]]}}
+fcvt.w.h a0, fs5
+# CHECK-INST: fcvt.wu.h a1, fs6, dyn
+# CHECK-ALIAS: fcvt.wu.h a1, fs6{{[[:space:]]}}
+fcvt.wu.h a1, fs6
+# CHECK-INST: fcvt.h.w ft11, a4, dyn
+# CHECK-ALIAS: fcvt.h.w ft11, a4{{[[:space:]]}}
+fcvt.h.w ft11, a4
+# CHECK-INST: fcvt.h.wu ft0, a5, dyn
+# CHECK-ALIAS: fcvt.h.wu ft0, a5{{[[:space:]]}}
+fcvt.h.wu ft0, a5
diff --git a/llvm/test/MC/RISCV/rvzfh-pseudos.s b/llvm/test/MC/RISCV/rvzfh-pseudos.s
new file mode 100644
index 000000000000..0506c9e468b5
--- /dev/null
+++ b/llvm/test/MC/RISCV/rvzfh-pseudos.s
@@ -0,0 +1,12 @@
+# RUN: llvm-mc %s -triple=riscv32 -mattr=+experimental-zfh | FileCheck %s
+# RUN: llvm-mc %s -triple=riscv64 -mattr=+experimental-zfh | FileCheck %s
+
+# CHECK: .Lpcrel_hi0:
+# CHECK: auipc a2, %pcrel_hi(a_symbol)
+# CHECK: flh fa2, %pcrel_lo(.Lpcrel_hi0)(a2)
+flh fa2, a_symbol, a2
+
+# CHECK: .Lpcrel_hi1:
+# CHECK: auipc a3, %pcrel_hi(a_symbol)
+# CHECK: fsh fa2, %pcrel_lo(.Lpcrel_hi1)(a3)
+fsh fa2, a_symbol, a3
More information about the llvm-branch-commits
mailing list