diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc
index ebc1ed7d7e4..8fec3bc71c9 100644
--- a/gcc/common/config/riscv/riscv-common.cc
+++ b/gcc/common/config/riscv/riscv-common.cc
@@ -217,6 +217,8 @@ static const struct riscv_ext_version riscv_ext_version_table[] =
   {"zfh",       ISA_SPEC_CLASS_NONE, 1, 0},
   {"zfhmin",    ISA_SPEC_CLASS_NONE, 1, 0},
 
+  {"zfa",     ISA_SPEC_CLASS_NONE, 0, 1},
+
   {"zmmul", ISA_SPEC_CLASS_NONE, 1, 0},
 
   {"svinval", ISA_SPEC_CLASS_NONE, 1, 0},
@@ -1243,6 +1245,8 @@ static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
   {"zfhmin",    &gcc_options::x_riscv_zf_subext, MASK_ZFHMIN},
   {"zfh",       &gcc_options::x_riscv_zf_subext, MASK_ZFH},
 
+  {"zfa",       &gcc_options::x_riscv_zf_subext, MASK_ZFA},
+
   {"zmmul", &gcc_options::x_riscv_zm_subext, MASK_ZMMUL},
 
   {"svinval", &gcc_options::x_riscv_sv_subext, MASK_SVINVAL},
diff --git a/gcc/config/riscv/constraints.md b/gcc/config/riscv/constraints.md
index fdfcf2380f8..2ba0160934a 100644
--- a/gcc/config/riscv/constraints.md
+++ b/gcc/config/riscv/constraints.md
@@ -118,6 +118,13 @@ (define_constraint "T"
   (and (match_operand 0 "move_operand")
        (match_test "CONSTANT_P (op)")))
 
+;; Zfa constraints.
+
+(define_constraint "Zf"
+  "A floating point number that can be loaded using instruction `fli` in zfa."
+  (and (match_code "const_double")
+       (match_test "(riscv_float_const_rtx_index_for_fli (op) != -1)")))
+
 ;; Vector constraints.
 
 (define_register_constraint "vr" "TARGET_VECTOR ? V_REGS : NO_REGS"
diff --git a/gcc/config/riscv/iterators.md b/gcc/config/riscv/iterators.md
index 5b70ab20758..ef9b1aa9ed3 100644
--- a/gcc/config/riscv/iterators.md
+++ b/gcc/config/riscv/iterators.md
@@ -284,3 +284,8 @@ (define_int_iterator QUIET_COMPARISON [UNSPEC_FLT_QUIET UNSPEC_FLE_QUIET])
 (define_int_attr quiet_pattern [(UNSPEC_FLT_QUIET "lt") (UNSPEC_FLE_QUIET "le")])
 (define_int_attr QUIET_PATTERN [(UNSPEC_FLT_QUIET "LT") (UNSPEC_FLE_QUIET "LE")])
 
+(define_int_iterator ROUND [UNSPEC_ROUND UNSPEC_FLOOR UNSPEC_CEIL UNSPEC_BTRUNC UNSPEC_ROUNDEVEN UNSPEC_NEARBYINT])
+(define_int_attr round_pattern [(UNSPEC_ROUND "round") (UNSPEC_FLOOR "floor") (UNSPEC_CEIL "ceil")
+				(UNSPEC_BTRUNC "btrunc") (UNSPEC_ROUNDEVEN "roundeven") (UNSPEC_NEARBYINT "nearbyint")])
+(define_int_attr round_rm [(UNSPEC_ROUND "rmm") (UNSPEC_FLOOR "rdn") (UNSPEC_CEIL "rup")
+			   (UNSPEC_BTRUNC "rtz") (UNSPEC_ROUNDEVEN "rne") (UNSPEC_NEARBYINT "dyn")])
\ No newline at end of file
diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md
index 0d9d7701c7e..b16b8d438e3 100644
--- a/gcc/config/riscv/predicates.md
+++ b/gcc/config/riscv/predicates.md
@@ -149,6 +149,10 @@ (define_predicate "move_operand"
     case CONST_POLY_INT:
       return known_eq (rtx_to_poly_int64 (op), BYTES_PER_RISCV_VECTOR);
 
+    case CONST_DOUBLE:
+      return const_0_operand (op, mode)
+	      || (riscv_float_const_rtx_index_for_fli (op) != -1);
+
     case CONST:
     case SYMBOL_REF:
     case LABEL_REF:
diff --git a/gcc/config/riscv/riscv-opts.h b/gcc/config/riscv/riscv-opts.h
index ff398c0a2ae..3ae729eec30 100644
--- a/gcc/config/riscv/riscv-opts.h
+++ b/gcc/config/riscv/riscv-opts.h
@@ -172,6 +172,9 @@ enum stack_protector_guard {
 #define TARGET_ZFHMIN ((riscv_zf_subext & MASK_ZFHMIN) != 0)
 #define TARGET_ZFH    ((riscv_zf_subext & MASK_ZFH) != 0)
 
+#define MASK_ZFA   (1 << 0)
+#define TARGET_ZFA    ((riscv_zf_subext & MASK_ZFA) != 0)
+
 #define MASK_ZMMUL      (1 << 0)
 #define TARGET_ZMMUL    ((riscv_zm_subext & MASK_ZMMUL) != 0)
 
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index f35aaf35b48..0f5dcba8294 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -38,6 +38,7 @@ enum riscv_symbol_type {
 /* Routines implemented in riscv.cc.  */
 extern enum riscv_symbol_type riscv_classify_symbolic_expression (rtx);
 extern bool riscv_symbolic_constant_p (rtx, enum riscv_symbol_type *);
+extern int riscv_float_const_rtx_index_for_fli (rtx);
 extern int riscv_regno_mode_ok_for_base_p (int, machine_mode, bool);
 extern int riscv_address_insns (rtx, machine_mode, bool);
 extern int riscv_const_insns (rtx);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index c91fa3101aa..c81a2bf44f5 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -799,6 +799,108 @@ static int riscv_symbol_insns (enum riscv_symbol_type type)
     }
 }
 
+/* Immediate values loaded by the FLI.S instruction in Chapter 25 of the latest RISC-V ISA
+   Manual draft. For details, please see:
+   https://github.com/riscv/riscv-isa-manual/releases/tag/draft-20221217-cb3b9d1 */
+
+unsigned HOST_WIDE_INT fli_value_hf[32] =
+{
+  0xbc00, 0x400, 0x100, 0x200, 0x1c00, 0x2000, 0x2c00, 0x3000,
+  0x3400, 0x3500, 0x3600, 0x3700, 0x3800, 0x3900, 0x3a00, 0x3b00,
+  0x3c00, 0x3d00, 0x3e00, 0x3f00, 0x4000, 0x4100, 0x4200, 0x4400,
+  0x4800, 0x4c00, 0x5800, 0x5c00, 0x7800,
+  /* Only used for filling, ensuring that 29 and 30 of HF are the same. */
+  0x7800,
+  0x7c00, 0x7e00,
+};
+
+unsigned HOST_WIDE_INT fli_value_sf[32] =
+{
+  0xbf800000, 0x00800000, 0x37800000, 0x38000000, 0x3b800000, 0x3c000000, 0x3d800000, 0x3e000000,
+  0x3e800000, 0x3ea00000, 0x3ec00000, 0x3ee00000, 0x3f000000, 0x3f200000, 0x3f400000, 0x3f600000,
+  0x3f800000, 0x3fa00000, 0x3fc00000, 0x3fe00000, 0x40000000, 0x40200000, 0x40400000, 0x40800000,
+  0x41000000, 0x41800000, 0x43000000, 0x43800000, 0x47000000, 0x47800000, 0x7f800000, 0x7fc00000
+};
+
+unsigned HOST_WIDE_INT fli_value_df[32] =
+{
+  0xbff0000000000000, 0x10000000000000, 0x3ef0000000000000, 0x3f00000000000000,
+  0x3f70000000000000, 0x3f80000000000000, 0x3fb0000000000000, 0x3fc0000000000000,
+  0x3fd0000000000000, 0x3fd4000000000000, 0x3fd8000000000000, 0x3fdc000000000000,
+  0x3fe0000000000000, 0x3fe4000000000000, 0x3fe8000000000000, 0x3fec000000000000,
+  0x3ff0000000000000, 0x3ff4000000000000, 0x3ff8000000000000, 0x3ffc000000000000,
+  0x4000000000000000, 0x4004000000000000, 0x4008000000000000, 0x4010000000000000,
+  0x4020000000000000, 0x4030000000000000, 0x4060000000000000, 0x4070000000000000,
+  0x40e0000000000000, 0x40f0000000000000, 0x7ff0000000000000, 0x7ff8000000000000,
+};
+
+/* Find the index of TARGET in ARRAY, and return -1 if not found. */
+
+static int
+find_index_in_array (unsigned HOST_WIDE_INT target, unsigned HOST_WIDE_INT *array, int len)
+{
+  if (array == NULL)
+    return -1;
+
+  for (int i = 0; i < len; i++)
+    {
+      if (target == array[i])
+	return i;
+    }
+  return -1;
+}
+
+/* Return index of the FLI instruction table if rtx X is an immediate constant that
+   can be moved using a single FLI instruction in zfa extension. -1 otherwise. */
+
+int
+riscv_float_const_rtx_index_for_fli (rtx x)
+{
+  machine_mode mode = GET_MODE (x);
+
+  if (!TARGET_ZFA || mode == VOIDmode
+      || !CONST_DOUBLE_P(x)
+      || (mode == HFmode && !TARGET_ZFH)
+      || (mode == SFmode && !TARGET_HARD_FLOAT)
+      || (mode == DFmode && !TARGET_DOUBLE_FLOAT))
+    return -1;
+
+  if (!SCALAR_FLOAT_MODE_P (mode)
+      || GET_MODE_BITSIZE (mode).to_constant () > HOST_BITS_PER_WIDE_INT
+      /* Only support up to DF mode.  */
+      || GET_MODE_BITSIZE (mode).to_constant () > GET_MODE_BITSIZE (DFmode))
+    return -1;
+
+  unsigned HOST_WIDE_INT ival = 0;
+
+  long res[2];
+  real_to_target (res,
+		  CONST_DOUBLE_REAL_VALUE (x),
+		  REAL_MODE_FORMAT (mode));
+
+  if (mode == DFmode)
+    {
+      int order = BYTES_BIG_ENDIAN ? 1 : 0;
+      ival = zext_hwi (res[order], 32);
+      ival |= (zext_hwi (res[1 - order], 32) << 32);
+    }
+  else
+      ival = zext_hwi (res[0], 32);
+
+  switch (mode)
+    {
+      case SFmode:
+	return find_index_in_array (ival, fli_value_sf, 32);
+      case DFmode:
+	return find_index_in_array (ival, fli_value_df, 32);
+      case HFmode:
+	return find_index_in_array (ival, fli_value_hf, 32);
+      default:
+	break;
+    }
+  return -1;
+}
+
 /* Implement TARGET_LEGITIMATE_CONSTANT_P.  */
 
 static bool
@@ -826,6 +928,9 @@ riscv_cannot_force_const_mem (machine_mode mode ATTRIBUTE_UNUSED, rtx x)
   if (GET_CODE (x) == HIGH)
     return true;
 
+  if (riscv_float_const_rtx_index_for_fli (x) != -1)
+   return true;
+
   split_const (x, &base, &offset);
   if (riscv_symbolic_constant_p (base, &type))
     {
@@ -1191,6 +1296,8 @@ riscv_const_insns (rtx x)
       }
 
     case CONST_DOUBLE:
+      if (riscv_float_const_rtx_index_for_fli (x) != -1)
+	return 4;
     case CONST_VECTOR:
       /* We can use x0 to load floating-point zero.  */
       return x == CONST0_RTX (GET_MODE (x)) ? 1 : 0;
@@ -1727,6 +1834,12 @@ riscv_legitimize_const_move (machine_mode mode, rtx dest, rtx src)
       return;
     }
 
+  if (riscv_float_const_rtx_index_for_fli (src) != -1)
+    {
+      riscv_emit_set (dest, src);
+      return;
+    }
+
   /* Split moves of symbolic constants into high/low pairs.  */
   if (riscv_split_symbol (dest, src, MAX_MACHINE_MODE, &src, FALSE))
     {
@@ -2739,12 +2852,19 @@ riscv_split_64bit_move_p (rtx dest, rtx src)
   if (TARGET_64BIT)
     return false;
 
+  /* There is no need to split if the FLI instruction in the `Zfa` extension can be used. */
+  if (TARGET_ZFA && (riscv_float_const_rtx_index_for_fli (src) != -1))
+    return false;
+
   /* Allow FPR <-> FPR and FPR <-> MEM moves, and permit the special case
      of zeroing an FPR with FCVT.D.W.  */
   if (TARGET_DOUBLE_FLOAT
       && ((FP_REG_RTX_P (src) && FP_REG_RTX_P (dest))
 	  || (FP_REG_RTX_P (dest) && MEM_P (src))
 	  || (FP_REG_RTX_P (src) && MEM_P (dest))
+	  || (TARGET_ZFA
+	      && ((FP_REG_RTX_P (dest) && GP_REG_RTX_P (src))
+	      || (FP_REG_RTX_P (src) && GP_REG_RTX_P (dest))))
 	  || (FP_REG_RTX_P (dest) && src == CONST0_RTX (GET_MODE (src)))))
     return false;
 
@@ -2808,6 +2928,8 @@ riscv_output_move (rtx dest, rtx src)
 	  case 4:
 	    return "fmv.x.s\t%0,%1";
 	  case 8:
+	    if (!TARGET_64BIT && TARGET_ZFA)
+	      return "fmv.x.w\t%0,%1\n\tfmvh.x.d\t%N0,%1";
 	    return "fmv.x.d\t%0,%1";
 	  }
 
@@ -2867,6 +2989,8 @@ riscv_output_move (rtx dest, rtx src)
 	      case 8:
 		if (TARGET_64BIT)
 		  return "fmv.d.x\t%0,%z1";
+		else if (TARGET_ZFA && src != CONST0_RTX (mode))
+		  return "fmvp.d.x\t%0,%1,%N1";
 		/* in RV32, we can emulate fmv.d.x %0, x0 using fcvt.d.w */
 		gcc_assert (src == CONST0_RTX (mode));
 		return "fcvt.d.w\t%0,x0";
@@ -2919,6 +3043,14 @@ riscv_output_move (rtx dest, rtx src)
 	  case 8:
 	    return "fld\t%0,%1";
 	  }
+
+      if (src_code == CONST_DOUBLE && (riscv_float_const_rtx_index_for_fli (src) != -1))
+	switch (width)
+	  {
+	    case 2: return "fli.h\t%0,%1";
+	    case 4: return "fli.s\t%0,%1";
+	    case 8: return "fli.d\t%0,%1";
+	  }
     }
   if (dest_code == REG && GP_REG_P (REGNO (dest)) && src_code == CONST_POLY_INT)
     {
@@ -4222,6 +4354,7 @@ riscv_memmodel_needs_release_fence (enum memmodel model)
    'S'	Print shift-index of single-bit mask OP.
    'T'	Print shift-index of inverted single-bit mask OP.
    '~'	Print w if TARGET_64BIT is true; otherwise not print anything.
+   'N'  Print next register.
 
    Note please keep this list and the list in riscv.md in sync.  */
 
@@ -4406,6 +4539,9 @@ riscv_print_operand (FILE *file, rtx op, int letter)
 	output_addr_const (file, newop);
 	break;
       }
+    case 'N':
+      fputs (reg_names[REGNO (op) + 1], file);
+      break;
     default:
       switch (code)
 	{
@@ -4422,6 +4558,35 @@ riscv_print_operand (FILE *file, rtx op, int letter)
 	    output_address (mode, XEXP (op, 0));
 	  break;
 
+	case CONST_DOUBLE:
+	  {
+	    if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
+	      {
+		fputs (reg_names[GP_REG_FIRST], file);
+		break;
+	      }
+
+	    int fli_index = riscv_float_const_rtx_index_for_fli (op);
+	    if (fli_index == -1 || fli_index > 31)
+	      {
+		output_operand_lossage ("invalid use of '%%%c'", letter);
+		break;
+	      }
+
+	    switch (fli_index)
+	      {
+		case 1:
+		  asm_fprintf (file, "%s", "min");break;
+		case 30:
+		  asm_fprintf (file, "%s", "inf");break;
+		case 31:
+		  asm_fprintf (file, "%s", "nan");break;
+		default:
+		  asm_fprintf (file, "%d", fli_index);break;
+	      }
+	    break;
+	  }
+
 	default:
 	  if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
 	    fputs (reg_names[GP_REG_FIRST], file);
@@ -5734,7 +5899,8 @@ riscv_secondary_memory_needed (machine_mode mode, reg_class_t class1,
 {
   return (!riscv_v_ext_vector_mode_p (mode)
 	  && GET_MODE_SIZE (mode).to_constant () > UNITS_PER_WORD
-	  && (class1 == FP_REGS) != (class2 == FP_REGS));
+	  && (class1 == FP_REGS) != (class2 == FP_REGS)
+	  && !TARGET_ZFA);
 }
 
 /* Implement TARGET_REGISTER_MOVE_COST.  */
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index 15b9317a8ce..f6ab9c75317 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -377,6 +377,7 @@ ASM_MISA_SPEC
 #define SIBCALL_REG_P(REGNO)	\
   TEST_HARD_REG_BIT (reg_class_contents[SIBCALL_REGS], REGNO)
 
+#define GP_REG_RTX_P(X) (REG_P (X) && GP_REG_P (REGNO (X)))
 #define FP_REG_RTX_P(X) (REG_P (X) && FP_REG_P (REGNO (X)))
 
 /* Use s0 as the frame pointer if it is so requested.  */
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 371d6838c0b..4fa2896b8e8 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -59,6 +59,15 @@ (define_c_enum "unspec" [
   UNSPEC_LROUND
   UNSPEC_FMIN
   UNSPEC_FMAX
+  UNSPEC_RINT
+  UNSPEC_ROUND
+  UNSPEC_FLOOR
+  UNSPEC_CEIL
+  UNSPEC_BTRUNC
+  UNSPEC_ROUNDEVEN
+  UNSPEC_NEARBYINT
+  UNSPEC_FMINM
+  UNSPEC_FMAXM
 
   ;; Stack tie
   UNSPEC_TIE
@@ -1227,6 +1236,26 @@ (define_insn "neg<mode>2"
 ;;
 ;;  ....................
 
+(define_insn "fminm<mode>3"
+  [(set (match_operand:ANYF                    0 "register_operand" "=f")
+	(unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" " f"))
+		      (use (match_operand:ANYF 2 "register_operand" " f"))]
+		     UNSPEC_FMINM))]
+  "TARGET_HARD_FLOAT && TARGET_ZFA"
+  "fminm.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fmove")
+   (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "fmaxm<mode>3"
+  [(set (match_operand:ANYF                    0 "register_operand" "=f")
+	(unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" " f"))
+		      (use (match_operand:ANYF 2 "register_operand" " f"))]
+		     UNSPEC_FMAXM))]
+  "TARGET_HARD_FLOAT && TARGET_ZFA"
+  "fmaxm.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fmove")
+   (set_attr "mode" "<UNITMODE>")])
+
 (define_insn "fmin<mode>3"
   [(set (match_operand:ANYF                    0 "register_operand" "=f")
 	(unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" " f"))
@@ -1503,13 +1532,13 @@ (define_expand "movhf"
 })
 
 (define_insn "*movhf_hardfloat"
-  [(set (match_operand:HF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*r,  *r,*r,*m")
-	(match_operand:HF 1 "move_operand"         " f,G,m,f,G,*r,*f,*G*r,*m,*r"))]
+  [(set (match_operand:HF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r,  *r,*r,*m")
+	(match_operand:HF 1 "move_operand"         " f,Zf,G,m,f,G,*r,*f,*G*r,*m,*r"))]
   "TARGET_ZFHMIN
    && (register_operand (operands[0], HFmode)
        || reg_or_0_operand (operands[1], HFmode))"
   { return riscv_output_move (operands[0], operands[1]); }
-  [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
+  [(set_attr "move_type" "fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
    (set_attr "mode" "HF")])
 
 (define_insn "*movhf_softfloat"
@@ -1575,6 +1604,26 @@ (define_insn "l<rint_pattern><ANYF:mode><GPR:mode>2"
   [(set_attr "type" "fcvt")
    (set_attr "mode" "<ANYF:MODE>")])
 
+(define_insn "<round_pattern><ANYF:mode>2"
+  [(set (match_operand:ANYF     0 "register_operand" "=f")
+	(unspec:ANYF
+	    [(match_operand:ANYF 1 "register_operand" " f")]
+	ROUND))]
+  "TARGET_HARD_FLOAT && TARGET_ZFA"
+  "fround.<ANYF:fmt>\t%0,%1,<round_rm>"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "<ANYF:MODE>")])
+
+(define_insn "rint<ANYF:mode>2"
+  [(set (match_operand:ANYF     0 "register_operand" "=f")
+	(unspec:ANYF
+	    [(match_operand:ANYF 1 "register_operand" " f")]
+	UNSPEC_RINT))]
+  "TARGET_HARD_FLOAT && TARGET_ZFA"
+  "froundnx.<ANYF:fmt>\t%0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "<ANYF:MODE>")])
+
 ;;
 ;;  ....................
 ;;
@@ -1834,13 +1883,13 @@ (define_expand "movsf"
 })
 
 (define_insn "*movsf_hardfloat"
-  [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*r,  *r,*r,*m")
-	(match_operand:SF 1 "move_operand"         " f,G,m,f,G,*r,*f,*G*r,*m,*r"))]
+  [(set (match_operand:SF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r,  *r,*r,*m")
+	(match_operand:SF 1 "move_operand"         " f,Zf,G,m,f,G,*r,*f,*G*r,*m,*r"))]
   "TARGET_HARD_FLOAT
    && (register_operand (operands[0], SFmode)
        || reg_or_0_operand (operands[1], SFmode))"
   { return riscv_output_move (operands[0], operands[1]); }
-  [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
+  [(set_attr "move_type" "fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
    (set_attr "mode" "SF")])
 
 (define_insn "*movsf_softfloat"
@@ -1867,23 +1916,23 @@ (define_expand "movdf"
 ;; In RV32, we lack fmv.x.d and fmv.d.x.  Go through memory instead.
 ;; (However, we can still use fcvt.d.w to zero a floating-point register.)
 (define_insn "*movdf_hardfloat_rv32"
-  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,m,m,  *r,*r,*m")
-	(match_operand:DF 1 "move_operand"         " f,G,m,f,G,*r*G,*m,*r"))]
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r,  *r,*r,*m")
+	(match_operand:DF 1 "move_operand"         " f,Zf,G,m,f,G,*r,*f,*r*G,*m,*r"))]
   "!TARGET_64BIT && TARGET_DOUBLE_FLOAT
    && (register_operand (operands[0], DFmode)
        || reg_or_0_operand (operands[1], DFmode))"
   { return riscv_output_move (operands[0], operands[1]); }
-  [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,move,load,store")
+  [(set_attr "move_type" "fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
    (set_attr "mode" "DF")])
 
 (define_insn "*movdf_hardfloat_rv64"
-  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*r,  *r,*r,*m")
-	(match_operand:DF 1 "move_operand"         " f,G,m,f,G,*r,*f,*r*G,*m,*r"))]
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r,  *r,*r,*m")
+	(match_operand:DF 1 "move_operand"         " f,Zf,G,m,f,G,*r,*f,*r*G,*m,*r"))]
   "TARGET_64BIT && TARGET_DOUBLE_FLOAT
    && (register_operand (operands[0], DFmode)
        || reg_or_0_operand (operands[1], DFmode))"
   { return riscv_output_move (operands[0], operands[1]); }
-  [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
+  [(set_attr "move_type" "fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
    (set_attr "mode" "DF")])
 
 (define_insn "*movdf_softfloat"
@@ -2490,16 +2539,23 @@ (define_expand "f<quiet_pattern>_quiet<ANYF:mode><X:mode>4"
   rtx op0 = operands[0];
   rtx op1 = operands[1];
   rtx op2 = operands[2];
-  rtx tmp = gen_reg_rtx (SImode);
-  rtx cmp = gen_rtx_<QUIET_PATTERN> (<X:MODE>mode, op1, op2);
-  rtx frflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, const0_rtx),
-					 UNSPECV_FRFLAGS);
-  rtx fsflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, tmp),
-					 UNSPECV_FSFLAGS);
-
-  emit_insn (gen_rtx_SET (tmp, frflags));
-  emit_insn (gen_rtx_SET (op0, cmp));
-  emit_insn (fsflags);
+
+  if (TARGET_ZFA)
+    emit_insn (gen_f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa(op0, op1, op2));
+  else
+    {
+      rtx tmp = gen_reg_rtx (SImode);
+      rtx cmp = gen_rtx_<QUIET_PATTERN> (<X:MODE>mode, op1, op2);
+      rtx frflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, const0_rtx),
+					     UNSPECV_FRFLAGS);
+      rtx fsflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, tmp),
+					     UNSPECV_FSFLAGS);
+
+      emit_insn (gen_rtx_SET (tmp, frflags));
+      emit_insn (gen_rtx_SET (op0, cmp));
+      emit_insn (fsflags);
+    }
+
   if (HONOR_SNANS (<ANYF:MODE>mode))
     emit_insn (gen_rtx_UNSPEC_VOLATILE (<ANYF:MODE>mode,
 					gen_rtvec (2, op1, op2),
@@ -2507,6 +2563,18 @@ (define_expand "f<quiet_pattern>_quiet<ANYF:mode><X:mode>4"
   DONE;
 })
 
+(define_insn "f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa"
+   [(set (match_operand:X      0 "register_operand" "=r")
+	 (unspec:X
+	  [(match_operand:ANYF 1 "register_operand" " f")
+	   (match_operand:ANYF 2 "register_operand" " f")]
+	  QUIET_COMPARISON))]
+  "TARGET_HARD_FLOAT && TARGET_ZFA"
+  "f<quiet_pattern>q.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fcmp")
+   (set_attr "mode" "<UNITMODE>")
+   (set (attr "length") (const_int 16))])
+
 (define_insn "*seq_zero_<X:mode><GPR:mode>"
   [(set (match_operand:GPR       0 "register_operand" "=r")
 	(eq:GPR (match_operand:X 1 "register_operand" " r")
diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c
new file mode 100644
index 00000000000..26895b76fa4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32imafdc_zfa -mabi=ilp32d -O2" } */
+
+extern void abort(void);
+extern float a, b;
+extern double c, d;
+
+void 
+foo()
+{
+  if ((__builtin_isless(a, b) ||  __builtin_islessequal(c, d))
+      && (__builtin_islessequal(a, b)|| __builtin_isless(c, d)))
+    abort();
+}
+
+/* { dg-final { scan-assembler-times "fleq.s" 1 } } */
+/* { dg-final { scan-assembler-times "fltq.s" 1 } } */
+/* { dg-final { scan-assembler-times "fleq.d" 1 } } */
+/* { dg-final { scan-assembler-times "fltq.d" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
new file mode 100644
index 00000000000..4ccd6a7dd78
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64imafdc_zfa -mabi=lp64d -O2" } */
+
+extern void abort(void);
+extern float a, b;
+extern double c, d;
+
+void 
+foo()
+{
+  if ((__builtin_isless(a, b) ||  __builtin_islessequal(c, d))
+      && (__builtin_islessequal(a, b)|| __builtin_isless(c, d)))
+    abort();
+}
+
+/* { dg-final { scan-assembler-times "fleq.s" 1 } } */
+/* { dg-final { scan-assembler-times "fltq.s" 1 } } */
+/* { dg-final { scan-assembler-times "fleq.d" 1 } } */
+/* { dg-final { scan-assembler-times "fltq.d" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c
new file mode 100644
index 00000000000..c4da04797aa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c
@@ -0,0 +1,79 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32imafdc_zfa -mabi=ilp32d -O0" } */
+
+void foo_float32 ()
+{
+  volatile float a;
+  a = -1.0;
+  a = 1.1754944e-38;
+  a = 1.0/(1 << 16);
+  a = 1.0/(1 << 15);
+  a = 1.0/(1 << 8);
+  a = 1.0/(1 << 7);
+  a = 1.0/(1 << 4);
+  a = 1.0/(1 << 3);
+  a = 1.0/(1 << 2);
+  a = 0.3125;
+  a = 0.375;
+  a = 0.4375;
+  a = 0.5;
+  a = 0.625;
+  a = 0.75;
+  a = 0.875;
+  a = 1.0;
+  a = 1.25;
+  a = 1.5;
+  a = 1.75;
+  a = 2.0;
+  a = 2.5;
+  a = 3.0;
+  a = 1.0*(1 << 2);
+  a = 1.0*(1 << 3);
+  a = 1.0*(1 << 4);
+  a = 1.0*(1 << 7);
+  a = 1.0*(1 << 8);
+  a = 1.0*(1 << 15);
+  a = 1.0*(1 << 16);
+  a = __builtin_inff ();
+  a = __builtin_nanf ("");
+}
+
+void foo_double64 ()
+{
+  volatile double a;
+  a = -1.0;
+  a = 2.2250738585072014E-308;
+  a = 1.0/(1 << 16);
+  a = 1.0/(1 << 15);
+  a = 1.0/(1 << 8);
+  a = 1.0/(1 << 7);
+  a = 1.0/(1 << 4);
+  a = 1.0/(1 << 3);
+  a = 1.0/(1 << 2);
+  a = 0.3125;
+  a = 0.375;
+  a = 0.4375;
+  a = 0.5;
+  a = 0.625;
+  a = 0.75;
+  a = 0.875;
+  a = 1.0;
+  a = 1.25;
+  a = 1.5;
+  a = 1.75;
+  a = 2.0;
+  a = 2.5;
+  a = 3.0;
+  a = 1.0*(1 << 2);
+  a = 1.0*(1 << 3);
+  a = 1.0*(1 << 4);
+  a = 1.0*(1 << 7);
+  a = 1.0*(1 << 8);
+  a = 1.0*(1 << 15);
+  a = 1.0*(1 << 16);
+  a = __builtin_inf ();
+  a = __builtin_nan ("");
+}
+
+/* { dg-final { scan-assembler-times "fli.s" 32 } } */
+/* { dg-final { scan-assembler-times "fli.d" 32 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.c
new file mode 100644
index 00000000000..bcffe9d2c82
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32imafdc_zfa_zfh -mabi=ilp32d -O0" } */
+
+void foo_float16 ()
+{
+  volatile _Float16 a;
+  a = -1.0;
+  a = 6.104E-5;
+  a = 1.0/(1 << 16);
+  a = 1.0/(1 << 15);
+  a = 1.0/(1 << 8);
+  a = 1.0/(1 << 7);
+  a = 1.0/(1 << 4);
+  a = 1.0/(1 << 3);
+  a = 1.0/(1 << 2);
+  a = 0.3125;
+  a = 0.375;
+  a = 0.4375;
+  a = 0.5;
+  a = 0.625;
+  a = 0.75;
+  a = 0.875;
+  a = 1.0;
+  a = 1.25;
+  a = 1.5;
+  a = 1.75;
+  a = 2.0;
+  a = 2.5;
+  a = 3.0;
+  a = 1.0*(1 << 2);
+  a = 1.0*(1 << 3);
+  a = 1.0*(1 << 4);
+  a = 1.0*(1 << 7);
+  a = 1.0*(1 << 8);
+  a = 1.0*(1 << 15);
+  a = 1.0*(1 << 16);
+  a = __builtin_inff16 ();
+  a = __builtin_nanf16 ("");
+}
+
+/* { dg-final { scan-assembler-times "fli.h" 32 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
new file mode 100644
index 00000000000..13aa7b5f846
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64imafdc_zfa_zfh -mabi=lp64d -O0" } */
+
+void foo_float16 ()
+{
+  volatile _Float16 a;
+  a = -1.0;
+  a = 6.104E-5;
+  a = 1.0/(1 << 16);
+  a = 1.0/(1 << 15);
+  a = 1.0/(1 << 8);
+  a = 1.0/(1 << 7);
+  a = 1.0/(1 << 4);
+  a = 1.0/(1 << 3);
+  a = 1.0/(1 << 2);
+  a = 0.3125;
+  a = 0.375;
+  a = 0.4375;
+  a = 0.5;
+  a = 0.625;
+  a = 0.75;
+  a = 0.875;
+  a = 1.0;
+  a = 1.25;
+  a = 1.5;
+  a = 1.75;
+  a = 2.0;
+  a = 2.5;
+  a = 3.0;
+  a = 1.0*(1 << 2);
+  a = 1.0*(1 << 3);
+  a = 1.0*(1 << 4);
+  a = 1.0*(1 << 7);
+  a = 1.0*(1 << 8);
+  a = 1.0*(1 << 15);
+  a = 1.0*(1 << 16);
+  a = __builtin_inff16 ();
+  a = __builtin_nanf16 ("");
+}
+
+/* { dg-final { scan-assembler-times "fli.h" 32 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli.c b/gcc/testsuite/gcc.target/riscv/zfa-fli.c
new file mode 100644
index 00000000000..b6d41cf460f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fli.c
@@ -0,0 +1,79 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64imafdc_zfa -mabi=lp64d -O0" } */
+
+void foo_float32 ()
+{
+  volatile float a;
+  a = -1.0;
+  a = 1.1754944e-38;
+  a = 1.0/(1 << 16);
+  a = 1.0/(1 << 15);
+  a = 1.0/(1 << 8);
+  a = 1.0/(1 << 7);
+  a = 1.0/(1 << 4);
+  a = 1.0/(1 << 3);
+  a = 1.0/(1 << 2);
+  a = 0.3125;
+  a = 0.375;
+  a = 0.4375;
+  a = 0.5;
+  a = 0.625;
+  a = 0.75;
+  a = 0.875;
+  a = 1.0;
+  a = 1.25;
+  a = 1.5;
+  a = 1.75;
+  a = 2.0;
+  a = 2.5;
+  a = 3.0;
+  a = 1.0*(1 << 2);
+  a = 1.0*(1 << 3);
+  a = 1.0*(1 << 4);
+  a = 1.0*(1 << 7);
+  a = 1.0*(1 << 8);
+  a = 1.0*(1 << 15);
+  a = 1.0*(1 << 16);
+  a = __builtin_inff ();
+  a = __builtin_nanf ("");
+}
+
+void foo_double64 ()
+{
+  volatile double a;
+  a = -1.0;
+  a = 2.2250738585072014E-308;
+  a = 1.0/(1 << 16);
+  a = 1.0/(1 << 15);
+  a = 1.0/(1 << 8);
+  a = 1.0/(1 << 7);
+  a = 1.0/(1 << 4);
+  a = 1.0/(1 << 3);
+  a = 1.0/(1 << 2);
+  a = 0.3125;
+  a = 0.375;
+  a = 0.4375;
+  a = 0.5;
+  a = 0.625;
+  a = 0.75;
+  a = 0.875;
+  a = 1.0;
+  a = 1.25;
+  a = 1.5;
+  a = 1.75;
+  a = 2.0;
+  a = 2.5;
+  a = 3.0;
+  a = 1.0*(1 << 2);
+  a = 1.0*(1 << 3);
+  a = 1.0*(1 << 4);
+  a = 1.0*(1 << 7);
+  a = 1.0*(1 << 8);
+  a = 1.0*(1 << 15);
+  a = 1.0*(1 << 16);
+  a = __builtin_inf ();
+  a = __builtin_nan ("");
+}
+
+/* { dg-final { scan-assembler-times "fli.s" 32 } } */
+/* { dg-final { scan-assembler-times "fli.d" 32 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp-rv32.c
new file mode 100644
index 00000000000..5a52adce36a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp-rv32.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32g_zfa -mabi=ilp32 -O0" } */
+
+double foo(long long a)
+{
+  return (double)(a + 3);
+}
+
+/* { dg-final { scan-assembler-times "fmvp.d.x" 1 } } */
+/* { dg-final { scan-assembler-times "fmvh.x.d" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.c
new file mode 100644
index 00000000000..b53601d6e1f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32imafdc_zfa -mabi=ilp32d -O2" } */
+
+extern float a;
+extern double b;
+
+void foo (float *x, double *y)
+{
+  {
+    *x = __builtin_roundf (a);
+    *y = __builtin_round (b);
+  }
+  {
+    *x = __builtin_floorf (a);
+    *y = __builtin_floor (b);
+  }
+  {
+    *x = __builtin_ceilf (a);
+    *y = __builtin_ceil (b);
+  }
+  {
+    *x = __builtin_truncf (a);
+    *y = __builtin_trunc (b);
+  }
+  {
+    *x = __builtin_roundevenf (a);
+    *y = __builtin_roundeven (b);
+  }
+  {
+    *x = __builtin_nearbyintf (a);
+    *y = __builtin_nearbyint (b);
+  }
+  {
+    *x = __builtin_rintf (a);
+    *y = __builtin_rint (b);
+  }
+}
+
+/* { dg-final { scan-assembler-times "fround.s" 6 } } */
+/* { dg-final { scan-assembler-times "fround.d" 6 } } */
+/* { dg-final { scan-assembler-times "froundnx.s" 1 } } */
+/* { dg-final { scan-assembler-times "froundnx.d" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fround.c b/gcc/testsuite/gcc.target/riscv/zfa-fround.c
new file mode 100644
index 00000000000..c10de82578e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fround.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64imafdc_zfa -mabi=lp64d -O2" } */
+
+extern float a;
+extern double b;
+
+void foo (float *x, double *y)
+{
+  {
+    *x = __builtin_roundf (a);
+    *y = __builtin_round (b);
+  }
+  {
+    *x = __builtin_floorf (a);
+    *y = __builtin_floor (b);
+  }
+  {
+    *x = __builtin_ceilf (a);
+    *y = __builtin_ceil (b);
+  }
+  {
+    *x = __builtin_truncf (a);
+    *y = __builtin_trunc (b);
+  }
+  {
+    *x = __builtin_roundevenf (a);
+    *y = __builtin_roundeven (b);
+  }
+  {
+    *x = __builtin_nearbyintf (a);
+    *y = __builtin_nearbyint (b);
+  }
+  {
+    *x = __builtin_rintf (a);
+    *y = __builtin_rint (b);
+  }
+}
+
+/* { dg-final { scan-assembler-times "fround.s" 6 } } */
+/* { dg-final { scan-assembler-times "fround.d" 6 } } */
+/* { dg-final { scan-assembler-times "froundnx.s" 1 } } */
+/* { dg-final { scan-assembler-times "froundnx.d" 1 } } */
