[v5,RISCV] Add 'Zfa' extension according to riscv-isa-manual

Message ID 20230202054550.2034-1-jinma@linux.alibaba.com
State New
Headers
Series [v5,RISCV] Add 'Zfa' extension according to riscv-isa-manual |

Commit Message

Jin Ma Feb. 2, 2023, 5:45 a.m. UTC
  This patch adds the 'Zfa' extension for riscv, which is based on:
( https://github.com/riscv/riscv-isa-manual/commit/d74d99e22d5f68832f70982d867614e2149a3bd7 )
latest 'Zfa' change on the master branch of the RISC-V ISA Manual as
of this writing.

The Wiki Page (details):
( https://github.com/a4lg/binutils-gdb/wiki/riscv_zfa )

The binutils-gdb for 'Zfa' extension:
( https://sourceware.org/pipermail/binutils/2022-September/122938.html )

gcc/ChangeLog:

	* common/config/riscv/riscv-common.cc:
	* config/riscv/constraints.md (Zf):
	* config/riscv/predicates.md:
	* config/riscv/riscv-builtins.cc (RISCV_FTYPE_NAME2):
	(AVAIL):
	(RISCV_ATYPE_SF):
	(RISCV_ATYPE_DF):
	(RISCV_FTYPE_ATYPES2):
	* config/riscv/riscv-ftypes.def (2):
	* config/riscv/riscv-opts.h (MASK_ZFA):
	(TARGET_ZFA):
	* config/riscv/riscv-protos.h (riscv_float_const_rtx_index_for_fli):
	* config/riscv/riscv.cc (riscv_float_const_rtx_index_for_fli):
	(riscv_cannot_force_const_mem):
	(riscv_const_insns):
	(riscv_legitimize_const_move):
	(riscv_split_64bit_move_p):
	(riscv_output_move):
	(riscv_memmodel_needs_release_fence):
	(riscv_print_operand):
	(riscv_secondary_memory_needed):
	* config/riscv/riscv.h (GP_REG_RTX_P):
	* config/riscv/riscv.md (riscv_fminm<mode>3):
	(riscv_fmaxm<mode>3):
	(fix_truncdfsi2_zfa):
	(round<ANYF:mode>2):
	(rint<ANYF:mode>2):
	(f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa):
	* config/riscv/riscv.opt:

gcc/testsuite/ChangeLog:

	* gcc.target/riscv/zfa-fcvtmod.c: New test.
	* gcc.target/riscv/zfa-fleq-fltq.c: New test.
	* gcc.target/riscv/zfa-fli-zfh.c: New test.
	* gcc.target/riscv/zfa-fli.c: New test.
	* gcc.target/riscv/zfa-fminm-fmaxm.c: New test.
	* gcc.target/riscv/zfa-fmovh-fmovp.c: New test.
	* gcc.target/riscv/zfa-fround.c: New test.
---
 gcc/common/config/riscv/riscv-common.cc       |   4 +
 gcc/config/riscv/constraints.md               |   7 ++
 gcc/config/riscv/predicates.md                |   4 +
 gcc/config/riscv/riscv-builtins.cc            |  11 ++
 gcc/config/riscv/riscv-ftypes.def             |   2 +
 gcc/config/riscv/riscv-opts.h                 |   3 +
 gcc/config/riscv/riscv-protos.h               |   1 +
 gcc/config/riscv/riscv.cc                     | 113 ++++++++++++++++-
 gcc/config/riscv/riscv.h                      |   1 +
 gcc/config/riscv/riscv.md                     | 114 ++++++++++++++----
 gcc/config/riscv/riscv.opt                    |   4 +
 gcc/testsuite/gcc.target/riscv/zfa-fcvtmod.c  |  12 ++
 .../gcc.target/riscv/zfa-fleq-fltq.c          |  20 +++
 gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c  |  42 +++++++
 gcc/testsuite/gcc.target/riscv/zfa-fli.c      |  80 ++++++++++++
 .../gcc.target/riscv/zfa-fminm-fmaxm.c        |  25 ++++
 .../gcc.target/riscv/zfa-fmovh-fmovp.c        |  11 ++
 gcc/testsuite/gcc.target/riscv/zfa-fround.c   |  25 ++++
 18 files changed, 456 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fcvtmod.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fminm-fmaxm.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fround.c
  

Comments

Kito Cheng Feb. 2, 2023, 1:33 p.m. UTC | #1
ack, just let you know reviewing this patch is on my todo list :)

My first impression is...you need to write something in your
changelog, it seems like generated by contrib/git-commit-mklog.py
without any modification.

On Thu, Feb 2, 2023 at 1:46 PM Jin Ma <jinma@linux.alibaba.com> wrote:
>
> This patch adds the 'Zfa' extension for riscv, which is based on:
> ( https://github.com/riscv/riscv-isa-manual/commit/d74d99e22d5f68832f70982d867614e2149a3bd7 )
> latest 'Zfa' change on the master branch of the RISC-V ISA Manual as
> of this writing.
>
> The Wiki Page (details):
> ( https://github.com/a4lg/binutils-gdb/wiki/riscv_zfa )
>
> The binutils-gdb for 'Zfa' extension:
> ( https://sourceware.org/pipermail/binutils/2022-September/122938.html )
>
> gcc/ChangeLog:
>
>         * common/config/riscv/riscv-common.cc:
>         * config/riscv/constraints.md (Zf):
>         * config/riscv/predicates.md:
>         * config/riscv/riscv-builtins.cc (RISCV_FTYPE_NAME2):
>         (AVAIL):
>         (RISCV_ATYPE_SF):
>         (RISCV_ATYPE_DF):
>         (RISCV_FTYPE_ATYPES2):
>         * config/riscv/riscv-ftypes.def (2):
>         * config/riscv/riscv-opts.h (MASK_ZFA):
>         (TARGET_ZFA):
>         * config/riscv/riscv-protos.h (riscv_float_const_rtx_index_for_fli):
>         * config/riscv/riscv.cc (riscv_float_const_rtx_index_for_fli):
>         (riscv_cannot_force_const_mem):
>         (riscv_const_insns):
>         (riscv_legitimize_const_move):
>         (riscv_split_64bit_move_p):
>         (riscv_output_move):
>         (riscv_memmodel_needs_release_fence):
>         (riscv_print_operand):
>         (riscv_secondary_memory_needed):
>         * config/riscv/riscv.h (GP_REG_RTX_P):
>         * config/riscv/riscv.md (riscv_fminm<mode>3):
>         (riscv_fmaxm<mode>3):
>         (fix_truncdfsi2_zfa):
>         (round<ANYF:mode>2):
>         (rint<ANYF:mode>2):
>         (f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa):
>         * config/riscv/riscv.opt:
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.target/riscv/zfa-fcvtmod.c: New test.
>         * gcc.target/riscv/zfa-fleq-fltq.c: New test.
>         * gcc.target/riscv/zfa-fli-zfh.c: New test.
>         * gcc.target/riscv/zfa-fli.c: New test.
>         * gcc.target/riscv/zfa-fminm-fmaxm.c: New test.
>         * gcc.target/riscv/zfa-fmovh-fmovp.c: New test.
>         * gcc.target/riscv/zfa-fround.c: New test.
> ---
>  gcc/common/config/riscv/riscv-common.cc       |   4 +
>  gcc/config/riscv/constraints.md               |   7 ++
>  gcc/config/riscv/predicates.md                |   4 +
>  gcc/config/riscv/riscv-builtins.cc            |  11 ++
>  gcc/config/riscv/riscv-ftypes.def             |   2 +
>  gcc/config/riscv/riscv-opts.h                 |   3 +
>  gcc/config/riscv/riscv-protos.h               |   1 +
>  gcc/config/riscv/riscv.cc                     | 113 ++++++++++++++++-
>  gcc/config/riscv/riscv.h                      |   1 +
>  gcc/config/riscv/riscv.md                     | 114 ++++++++++++++----
>  gcc/config/riscv/riscv.opt                    |   4 +
>  gcc/testsuite/gcc.target/riscv/zfa-fcvtmod.c  |  12 ++
>  .../gcc.target/riscv/zfa-fleq-fltq.c          |  20 +++
>  gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c  |  42 +++++++
>  gcc/testsuite/gcc.target/riscv/zfa-fli.c      |  80 ++++++++++++
>  .../gcc.target/riscv/zfa-fminm-fmaxm.c        |  25 ++++
>  .../gcc.target/riscv/zfa-fmovh-fmovp.c        |  11 ++
>  gcc/testsuite/gcc.target/riscv/zfa-fround.c   |  25 ++++
>  18 files changed, 456 insertions(+), 23 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fcvtmod.c
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli.c
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fminm-fmaxm.c
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp.c
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fround.c
>
> diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc
> index 616e2f897b9..977c8a9acf7 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, 1, 0},
> +
>    {"zmmul", ISA_SPEC_CLASS_NONE, 1, 0},
>
>    {"svinval", ISA_SPEC_CLASS_NONE, 1, 0},
> @@ -1242,6 +1244,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 3637380ee47..ace53b355f0 100644
> --- a/gcc/config/riscv/constraints.md
> +++ b/gcc/config/riscv/constraints.md
> @@ -110,6 +110,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/predicates.md b/gcc/config/riscv/predicates.md
> index 57f7ddfbd7d..4d5827e774b 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-builtins.cc b/gcc/config/riscv/riscv-builtins.cc
> index 25ca407f9a9..98821fc938c 100644
> --- a/gcc/config/riscv/riscv-builtins.cc
> +++ b/gcc/config/riscv/riscv-builtins.cc
> @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.  If not see
>  /* Macros to create an enumeration identifier for a function prototype.  */
>  #define RISCV_FTYPE_NAME0(A) RISCV_##A##_FTYPE
>  #define RISCV_FTYPE_NAME1(A, B) RISCV_##A##_FTYPE_##B
> +#define RISCV_FTYPE_NAME2(A, B, C) RISCV_##A##_FTYPE_##B##_##C
>
>  /* Classifies the prototype of a built-in function.  */
>  enum riscv_function_type {
> @@ -98,6 +99,7 @@ AVAIL (zero32,  TARGET_ZICBOZ && !TARGET_64BIT)
>  AVAIL (zero64,  TARGET_ZICBOZ && TARGET_64BIT)
>  AVAIL (prefetchi32, TARGET_ZICBOP && !TARGET_64BIT)
>  AVAIL (prefetchi64, TARGET_ZICBOP && TARGET_64BIT)
> +AVAIL (zfa, TARGET_ZFA)
>  AVAIL (always,     (!0))
>
>  /* Construct a riscv_builtin_description from the given arguments.
> @@ -135,6 +137,8 @@ AVAIL (always,     (!0))
>  #define RISCV_ATYPE_SI intSI_type_node
>  #define RISCV_ATYPE_DI intDI_type_node
>  #define RISCV_ATYPE_VOID_PTR ptr_type_node
> +#define RISCV_ATYPE_SF float_type_node
> +#define RISCV_ATYPE_DF double_type_node
>
>  /* RISCV_FTYPE_ATYPESN takes N RISCV_FTYPES-like type codes and lists
>     their associated RISCV_ATYPEs.  */
> @@ -142,6 +146,8 @@ AVAIL (always,     (!0))
>    RISCV_ATYPE_##A
>  #define RISCV_FTYPE_ATYPES1(A, B) \
>    RISCV_ATYPE_##A, RISCV_ATYPE_##B
> +#define RISCV_FTYPE_ATYPES2(A, B, C) \
> +  RISCV_ATYPE_##A, RISCV_ATYPE_##B, RISCV_ATYPE_##C
>
>  static const struct riscv_builtin_description riscv_builtins[] = {
>    #include "riscv-cmo.def"
> @@ -149,6 +155,11 @@ static const struct riscv_builtin_description riscv_builtins[] = {
>    DIRECT_BUILTIN (frflags, RISCV_USI_FTYPE, hard_float),
>    DIRECT_NO_TARGET_BUILTIN (fsflags, RISCV_VOID_FTYPE_USI, hard_float),
>    DIRECT_NO_TARGET_BUILTIN (pause, RISCV_VOID_FTYPE, always),
> +
> +  RISCV_BUILTIN (fminmsf3, "fminmf", RISCV_BUILTIN_DIRECT, RISCV_SF_FTYPE_SF_SF, zfa),
> +  RISCV_BUILTIN (fminmdf3, "fminm", RISCV_BUILTIN_DIRECT, RISCV_DF_FTYPE_DF_DF, zfa),
> +  RISCV_BUILTIN (fmaxmsf3, "fmaxmf", RISCV_BUILTIN_DIRECT, RISCV_SF_FTYPE_SF_SF, zfa),
> +  RISCV_BUILTIN (fmaxmdf3, "fmaxm", RISCV_BUILTIN_DIRECT, RISCV_DF_FTYPE_DF_DF, zfa),
>  };
>
>  /* Index I is the function declaration for riscv_builtins[I], or null if the
> diff --git a/gcc/config/riscv/riscv-ftypes.def b/gcc/config/riscv/riscv-ftypes.def
> index 3a40c33e7c2..617654f4d7b 100644
> --- a/gcc/config/riscv/riscv-ftypes.def
> +++ b/gcc/config/riscv/riscv-ftypes.def
> @@ -32,3 +32,5 @@ DEF_RISCV_FTYPE (1, (VOID, USI))
>  DEF_RISCV_FTYPE (1, (VOID, VOID_PTR))
>  DEF_RISCV_FTYPE (1, (SI, SI))
>  DEF_RISCV_FTYPE (1, (DI, DI))
> +DEF_RISCV_FTYPE (2, (SF, SF, SF))
> +DEF_RISCV_FTYPE (2, (DF, DF, DF))
> 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 ceae4007fd1..2cb837520ba 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 209d9a53e7b..4db266418f6 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -799,6 +799,57 @@ 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 */
> +
> +static unsigned HOST_WIDE_INT fli_value[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
> +};
> +
> +/* 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_ZFHMIN)
> +      || (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))
> +    {
> +      unsigned HOST_WIDE_INT ival = 0;
> +      REAL_VALUE_TYPE d;
> +
> +      d = real_value_truncate (float_mode_for_size (32).require (), *CONST_DOUBLE_REAL_VALUE (x));
> +      REAL_VALUE_TO_TARGET_SINGLE (d, ival);
> +
> +      for (int i = 0; i < 32; i++)
> +       {
> +         if (ival == fli_value[i])
> +           return i;
> +       }
> +
> +      /* The minimum positive normal value (6.104E-5) for half-precision. */
> +      if (mode == HFmode && ival == 0x38800000)
> +       return 1;
> +    }
> +  return -1;
> +}
> +
>  /* Implement TARGET_LEGITIMATE_CONSTANT_P.  */
>
>  static bool
> @@ -826,6 +877,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))
>      {
> @@ -1179,6 +1233,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;
> @@ -1715,6 +1771,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))
>      {
> @@ -2726,12 +2788,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;
>
> @@ -2795,6 +2864,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";
>           }
>
> @@ -2854,6 +2925,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";
> @@ -2906,6 +2979,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)
>      {
> @@ -4209,6 +4290,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.  */
>
> @@ -4360,6 +4442,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)
>         {
> @@ -4376,6 +4461,31 @@ 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;
> +             }
> +
> +           if (riscv_float_const_rtx_index_for_fli (op) == -1)
> +             output_operand_lossage ("invalid use of '%%%c'", letter);
> +
> +           if (riscv_fli_value_by_int)
> +             {
> +               asm_fprintf (file, "%d", riscv_float_const_rtx_index_for_fli (op));
> +               break;
> +             }
> +
> +           const unsigned int buf_size = 32;
> +           char float_buf[buf_size] = {'\0'};
> +           real_to_decimal_for_mode (float_buf, CONST_DOUBLE_REAL_VALUE (op),
> +                                     buf_size, buf_size, 1, GET_MODE(op));
> +           asm_fprintf (file, "%s", float_buf);
> +           break;
> +         }
> +
>         default:
>           if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
>             fputs (reg_names[GP_REG_FIRST], file);
> @@ -5686,7 +5796,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 0ab739bd6eb..8918f350ec0 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 c8adc5af5d2..95b4c9e98d3 100644
> --- a/gcc/config/riscv/riscv.md
> +++ b/gcc/config/riscv/riscv.md
> @@ -59,6 +59,8 @@ (define_c_enum "unspec" [
>    UNSPEC_LROUND
>    UNSPEC_FMIN
>    UNSPEC_FMAX
> +  UNSPEC_FMINM
> +  UNSPEC_FMAXM
>
>    ;; Stack tie
>    UNSPEC_TIE
> @@ -1220,6 +1222,26 @@ (define_insn "neg<mode>2"
>  ;;
>  ;;  ....................
>
> +(define_insn "riscv_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 && !HONOR_SNANS (<MODE>mode)"
> +  "fminm.<fmt>\t%0,%1,%2"
> +  [(set_attr "type" "fmove")
> +   (set_attr "mode" "<UNITMODE>")])
> +
> +(define_insn "riscv_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 && !HONOR_SNANS (<MODE>mode)"
> +  "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"))
> @@ -1494,13 +1516,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"
> @@ -1520,6 +1542,15 @@ (define_insn "*movhf_softfloat"
>  ;;
>  ;;  ....................
>
> +(define_insn "fix_truncdfsi2_zfa"
> +  [(set (match_operand:SI      0 "register_operand" "=r")
> +       (fix:SI
> +           (match_operand:DF 1 "register_operand" " f")))]
> +  "TARGET_ZFA && (TARGET_HARD_FLOAT || TARGET_ZFINX)"
> +  "fcvtmod.w.d\t%0,%1,rtz"
> +  [(set_attr "type" "fcvt")
> +   (set_attr "mode" "DF")])
> +
>  (define_insn "fix_trunc<ANYF:mode><GPR:mode>2"
>    [(set (match_operand:GPR      0 "register_operand" "=r")
>         (fix:GPR
> @@ -1566,6 +1597,26 @@ (define_insn "l<rint_pattern><ANYF:mode><GPR:mode>2"
>    [(set_attr "type" "fcvt")
>     (set_attr "mode" "<ANYF:MODE>")])
>
> +(define_insn "round<ANYF:mode>2"
> +  [(set (match_operand:ANYF     0 "register_operand" "=f")
> +       (unspec:ANYF
> +           [(match_operand:ANYF 1 "register_operand" " f")]
> +       UNSPEC_LROUND))]
> +  "TARGET_HARD_FLOAT && TARGET_ZFA"
> +  "fround.<ANYF:fmt>\t%0,%1"
> +  [(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_LRINT))]
> +  "TARGET_HARD_FLOAT && TARGET_ZFA"
> +  "froundnx.<ANYF:fmt>\t%0,%1"
> +  [(set_attr "type" "fcvt")
> +   (set_attr "mode" "<ANYF:MODE>")])
> +
>  ;;
>  ;;  ....................
>  ;;
> @@ -1823,13 +1874,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"
> @@ -1856,23 +1907,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"
> @@ -2479,16 +2530,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),
> @@ -2496,6 +2554,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/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt
> index e78c99382cd..a555b347dd7 100644
> --- a/gcc/config/riscv/riscv.opt
> +++ b/gcc/config/riscv/riscv.opt
> @@ -162,6 +162,10 @@ malign-data=
>  Target RejectNegative Joined Var(riscv_align_data_type) Enum(riscv_align_data) Init(riscv_align_data_type_xlen)
>  Use the given data alignment.
>
> +mfli_value_by_int
> +Target Var(riscv_fli_value_by_int) Init(1) Undocumented
> +The fli instruction in the zfa extension uses integers to represent floating-point values.
> +
>  Enum
>  Name(riscv_align_data) Type(enum riscv_align_data)
>  Known data alignment choices (for use with the -malign-data= option):
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fcvtmod.c b/gcc/testsuite/gcc.target/riscv/zfa-fcvtmod.c
> new file mode 100644
> index 00000000000..65455516a77
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fcvtmod.c
> @@ -0,0 +1,12 @@
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
> +/* { dg-options "-march=rv64g_zfa -mabi=lp64d" } */
> +
> +extern double a;
> +
> +void foo(int *x, double a)
> +{
> +  *x = a;
> +}
> +
> +/* { dg-final { scan-assembler-times "fcvtmod.w.d" 1 } } */
> \ No newline at end of file
> 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..b57b0810e96
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
> +/* { dg-options "-march=rv64g_zfa -mabi=lp64d" } */
> +
> +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-zfh.c b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
> new file mode 100644
> index 00000000000..26b43f61774
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
> @@ -0,0 +1,42 @@
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
> +/* { dg-options "-march=rv64g_zfa_zfh -mabi=lp64d" } */
> +
> +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_inff ();
> +  a = __builtin_nanf ("");
> +}
> +
> +/* { dg-final { scan-assembler-times "fli.h" 31 } } */
> 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..e3390c697ce
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fli.c
> @@ -0,0 +1,80 @@
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
> +/* { dg-options "-march=rv64g_zfa -mabi=lp64d" } */
> +
> +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 = 1.1754943508222875E-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_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-fminm-fmaxm.c b/gcc/testsuite/gcc.target/riscv/zfa-fminm-fmaxm.c
> new file mode 100644
> index 00000000000..386725a9030
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fminm-fmaxm.c
> @@ -0,0 +1,25 @@
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
> +/* { dg-options "-march=rv64g_zfa -mabi=lp64d" } */
> +
> +extern float a, b;
> +extern double c, d;
> +
> +void
> +foo(float *x, double *y)
> +{
> +  {
> +    *x = __builtin_riscv_fminmf(a, b);
> +    *y = __builtin_riscv_fminm(c, d);
> +  }
> +
> +  {
> +    *x = __builtin_riscv_fmaxmf(a, b);
> +    *y = __builtin_riscv_fmaxm(c, d);
> +  }
> +}
> +
> +/* { dg-final { scan-assembler-times "fminm.s" 1 } } */
> +/* { dg-final { scan-assembler-times "fminm.d" 1 } } */
> +/* { dg-final { scan-assembler-times "fmaxm.s" 1 } } */
> +/* { dg-final { scan-assembler-times "fmaxm.d" 1 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp.c b/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp.c
> new file mode 100644
> index 00000000000..9005e75d868
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp.c
> @@ -0,0 +1,11 @@
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv32* -O0" } } */
> +/* { dg-options "-march=rv32g_zfa -mabi=ilp32" } */
> +
> +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.c b/gcc/testsuite/gcc.target/riscv/zfa-fround.c
> new file mode 100644
> index 00000000000..e84a0bd1998
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fround.c
> @@ -0,0 +1,25 @@
> +/* { dg-do compile } */
> +/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
> +/* { dg-options "-march=rv64g_zfa -mabi=lp64d" } */
> +
> +#include <math.h>
> +
> +extern float a;
> +extern double b;
> +
> +void foo (float *x, double *y)
> +{
> +  {
> +    *x = roundf (a);
> +    *y = round (b);
> +  }
> +  {
> +    *x = rintf (a);
> +    *y = rint (b);
> +  }
> +}
> +
> +/* { dg-final { scan-assembler-times "fround.s" 1 } } */
> +/* { dg-final { scan-assembler-times "fround.d" 1 } } */
> +/* { dg-final { scan-assembler-times "froundnx.s" 1 } } */
> +/* { dg-final { scan-assembler-times "froundnx.d" 1 } } */
> --
> 2.17.1
>
  

Patch

diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc
index 616e2f897b9..977c8a9acf7 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, 1, 0},
+
   {"zmmul", ISA_SPEC_CLASS_NONE, 1, 0},
 
   {"svinval", ISA_SPEC_CLASS_NONE, 1, 0},
@@ -1242,6 +1244,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 3637380ee47..ace53b355f0 100644
--- a/gcc/config/riscv/constraints.md
+++ b/gcc/config/riscv/constraints.md
@@ -110,6 +110,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/predicates.md b/gcc/config/riscv/predicates.md
index 57f7ddfbd7d..4d5827e774b 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-builtins.cc b/gcc/config/riscv/riscv-builtins.cc
index 25ca407f9a9..98821fc938c 100644
--- a/gcc/config/riscv/riscv-builtins.cc
+++ b/gcc/config/riscv/riscv-builtins.cc
@@ -42,6 +42,7 @@  along with GCC; see the file COPYING3.  If not see
 /* Macros to create an enumeration identifier for a function prototype.  */
 #define RISCV_FTYPE_NAME0(A) RISCV_##A##_FTYPE
 #define RISCV_FTYPE_NAME1(A, B) RISCV_##A##_FTYPE_##B
+#define RISCV_FTYPE_NAME2(A, B, C) RISCV_##A##_FTYPE_##B##_##C
 
 /* Classifies the prototype of a built-in function.  */
 enum riscv_function_type {
@@ -98,6 +99,7 @@  AVAIL (zero32,  TARGET_ZICBOZ && !TARGET_64BIT)
 AVAIL (zero64,  TARGET_ZICBOZ && TARGET_64BIT)
 AVAIL (prefetchi32, TARGET_ZICBOP && !TARGET_64BIT)
 AVAIL (prefetchi64, TARGET_ZICBOP && TARGET_64BIT)
+AVAIL (zfa, TARGET_ZFA)
 AVAIL (always,     (!0))
 
 /* Construct a riscv_builtin_description from the given arguments.
@@ -135,6 +137,8 @@  AVAIL (always,     (!0))
 #define RISCV_ATYPE_SI intSI_type_node
 #define RISCV_ATYPE_DI intDI_type_node
 #define RISCV_ATYPE_VOID_PTR ptr_type_node
+#define RISCV_ATYPE_SF float_type_node
+#define RISCV_ATYPE_DF double_type_node
 
 /* RISCV_FTYPE_ATYPESN takes N RISCV_FTYPES-like type codes and lists
    their associated RISCV_ATYPEs.  */
@@ -142,6 +146,8 @@  AVAIL (always,     (!0))
   RISCV_ATYPE_##A
 #define RISCV_FTYPE_ATYPES1(A, B) \
   RISCV_ATYPE_##A, RISCV_ATYPE_##B
+#define RISCV_FTYPE_ATYPES2(A, B, C) \
+  RISCV_ATYPE_##A, RISCV_ATYPE_##B, RISCV_ATYPE_##C
 
 static const struct riscv_builtin_description riscv_builtins[] = {
   #include "riscv-cmo.def"
@@ -149,6 +155,11 @@  static const struct riscv_builtin_description riscv_builtins[] = {
   DIRECT_BUILTIN (frflags, RISCV_USI_FTYPE, hard_float),
   DIRECT_NO_TARGET_BUILTIN (fsflags, RISCV_VOID_FTYPE_USI, hard_float),
   DIRECT_NO_TARGET_BUILTIN (pause, RISCV_VOID_FTYPE, always),
+
+  RISCV_BUILTIN (fminmsf3, "fminmf", RISCV_BUILTIN_DIRECT, RISCV_SF_FTYPE_SF_SF, zfa),
+  RISCV_BUILTIN (fminmdf3, "fminm", RISCV_BUILTIN_DIRECT, RISCV_DF_FTYPE_DF_DF, zfa),
+  RISCV_BUILTIN (fmaxmsf3, "fmaxmf", RISCV_BUILTIN_DIRECT, RISCV_SF_FTYPE_SF_SF, zfa),
+  RISCV_BUILTIN (fmaxmdf3, "fmaxm", RISCV_BUILTIN_DIRECT, RISCV_DF_FTYPE_DF_DF, zfa),
 };
 
 /* Index I is the function declaration for riscv_builtins[I], or null if the
diff --git a/gcc/config/riscv/riscv-ftypes.def b/gcc/config/riscv/riscv-ftypes.def
index 3a40c33e7c2..617654f4d7b 100644
--- a/gcc/config/riscv/riscv-ftypes.def
+++ b/gcc/config/riscv/riscv-ftypes.def
@@ -32,3 +32,5 @@  DEF_RISCV_FTYPE (1, (VOID, USI))
 DEF_RISCV_FTYPE (1, (VOID, VOID_PTR))
 DEF_RISCV_FTYPE (1, (SI, SI))
 DEF_RISCV_FTYPE (1, (DI, DI))
+DEF_RISCV_FTYPE (2, (SF, SF, SF))
+DEF_RISCV_FTYPE (2, (DF, DF, DF))
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 ceae4007fd1..2cb837520ba 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 209d9a53e7b..4db266418f6 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -799,6 +799,57 @@  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 */
+
+static unsigned HOST_WIDE_INT fli_value[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
+};
+
+/* 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_ZFHMIN)
+      || (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))
+    {
+      unsigned HOST_WIDE_INT ival = 0;
+      REAL_VALUE_TYPE d;
+
+      d = real_value_truncate (float_mode_for_size (32).require (), *CONST_DOUBLE_REAL_VALUE (x));
+      REAL_VALUE_TO_TARGET_SINGLE (d, ival);
+
+      for (int i = 0; i < 32; i++)
+	{
+	  if (ival == fli_value[i])
+	    return i;
+	}
+
+      /* The minimum positive normal value (6.104E-5) for half-precision. */
+      if (mode == HFmode && ival == 0x38800000)
+	return 1;
+    }
+  return -1;
+}
+
 /* Implement TARGET_LEGITIMATE_CONSTANT_P.  */
 
 static bool
@@ -826,6 +877,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))
     {
@@ -1179,6 +1233,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;
@@ -1715,6 +1771,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))
     {
@@ -2726,12 +2788,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;
 
@@ -2795,6 +2864,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";
 	  }
 
@@ -2854,6 +2925,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";
@@ -2906,6 +2979,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)
     {
@@ -4209,6 +4290,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.  */
 
@@ -4360,6 +4442,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)
 	{
@@ -4376,6 +4461,31 @@  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;
+	      }
+
+	    if (riscv_float_const_rtx_index_for_fli (op) == -1)
+	      output_operand_lossage ("invalid use of '%%%c'", letter);
+
+	    if (riscv_fli_value_by_int)
+	      {
+		asm_fprintf (file, "%d", riscv_float_const_rtx_index_for_fli (op));
+		break;
+	      }
+
+	    const unsigned int buf_size = 32;
+	    char float_buf[buf_size] = {'\0'};
+	    real_to_decimal_for_mode (float_buf, CONST_DOUBLE_REAL_VALUE (op),
+				      buf_size, buf_size, 1, GET_MODE(op));
+	    asm_fprintf (file, "%s", float_buf);
+	    break;
+	  }
+
 	default:
 	  if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
 	    fputs (reg_names[GP_REG_FIRST], file);
@@ -5686,7 +5796,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 0ab739bd6eb..8918f350ec0 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 c8adc5af5d2..95b4c9e98d3 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -59,6 +59,8 @@  (define_c_enum "unspec" [
   UNSPEC_LROUND
   UNSPEC_FMIN
   UNSPEC_FMAX
+  UNSPEC_FMINM
+  UNSPEC_FMAXM
 
   ;; Stack tie
   UNSPEC_TIE
@@ -1220,6 +1222,26 @@  (define_insn "neg<mode>2"
 ;;
 ;;  ....................
 
+(define_insn "riscv_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 && !HONOR_SNANS (<MODE>mode)"
+  "fminm.<fmt>\t%0,%1,%2"
+  [(set_attr "type" "fmove")
+   (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "riscv_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 && !HONOR_SNANS (<MODE>mode)"
+  "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"))
@@ -1494,13 +1516,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"
@@ -1520,6 +1542,15 @@  (define_insn "*movhf_softfloat"
 ;;
 ;;  ....................
 
+(define_insn "fix_truncdfsi2_zfa"
+  [(set (match_operand:SI      0 "register_operand" "=r")
+	(fix:SI
+	    (match_operand:DF 1 "register_operand" " f")))]
+  "TARGET_ZFA && (TARGET_HARD_FLOAT || TARGET_ZFINX)"
+  "fcvtmod.w.d\t%0,%1,rtz"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "DF")])
+
 (define_insn "fix_trunc<ANYF:mode><GPR:mode>2"
   [(set (match_operand:GPR      0 "register_operand" "=r")
 	(fix:GPR
@@ -1566,6 +1597,26 @@  (define_insn "l<rint_pattern><ANYF:mode><GPR:mode>2"
   [(set_attr "type" "fcvt")
    (set_attr "mode" "<ANYF:MODE>")])
 
+(define_insn "round<ANYF:mode>2"
+  [(set (match_operand:ANYF     0 "register_operand" "=f")
+	(unspec:ANYF
+	    [(match_operand:ANYF 1 "register_operand" " f")]
+	UNSPEC_LROUND))]
+  "TARGET_HARD_FLOAT && TARGET_ZFA"
+  "fround.<ANYF:fmt>\t%0,%1"
+  [(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_LRINT))]
+  "TARGET_HARD_FLOAT && TARGET_ZFA"
+  "froundnx.<ANYF:fmt>\t%0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "<ANYF:MODE>")])
+
 ;;
 ;;  ....................
 ;;
@@ -1823,13 +1874,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"
@@ -1856,23 +1907,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"
@@ -2479,16 +2530,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),
@@ -2496,6 +2554,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/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt
index e78c99382cd..a555b347dd7 100644
--- a/gcc/config/riscv/riscv.opt
+++ b/gcc/config/riscv/riscv.opt
@@ -162,6 +162,10 @@  malign-data=
 Target RejectNegative Joined Var(riscv_align_data_type) Enum(riscv_align_data) Init(riscv_align_data_type_xlen)
 Use the given data alignment.
 
+mfli_value_by_int
+Target Var(riscv_fli_value_by_int) Init(1) Undocumented
+The fli instruction in the zfa extension uses integers to represent floating-point values.
+
 Enum
 Name(riscv_align_data) Type(enum riscv_align_data)
 Known data alignment choices (for use with the -malign-data= option):
diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fcvtmod.c b/gcc/testsuite/gcc.target/riscv/zfa-fcvtmod.c
new file mode 100644
index 00000000000..65455516a77
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fcvtmod.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
+/* { dg-options "-march=rv64g_zfa -mabi=lp64d" } */
+
+extern double a;
+
+void foo(int *x, double a)
+{
+  *x = a;
+}
+
+/* { dg-final { scan-assembler-times "fcvtmod.w.d" 1 } } */
\ No newline at end of file
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..b57b0810e96
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
+/* { dg-options "-march=rv64g_zfa -mabi=lp64d" } */
+
+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-zfh.c b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
new file mode 100644
index 00000000000..26b43f61774
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
@@ -0,0 +1,42 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
+/* { dg-options "-march=rv64g_zfa_zfh -mabi=lp64d" } */
+
+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_inff ();
+  a = __builtin_nanf ("");
+}
+
+/* { dg-final { scan-assembler-times "fli.h" 31 } } */
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..e3390c697ce
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fli.c
@@ -0,0 +1,80 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
+/* { dg-options "-march=rv64g_zfa -mabi=lp64d" } */
+
+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 = 1.1754943508222875E-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_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-fminm-fmaxm.c b/gcc/testsuite/gcc.target/riscv/zfa-fminm-fmaxm.c
new file mode 100644
index 00000000000..386725a9030
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fminm-fmaxm.c
@@ -0,0 +1,25 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
+/* { dg-options "-march=rv64g_zfa -mabi=lp64d" } */
+
+extern float a, b;
+extern double c, d;
+
+void
+foo(float *x, double *y)
+{
+  {
+    *x = __builtin_riscv_fminmf(a, b);
+    *y = __builtin_riscv_fminm(c, d);
+  }
+
+  {
+    *x = __builtin_riscv_fmaxmf(a, b);
+    *y = __builtin_riscv_fmaxm(c, d);
+  }
+}
+
+/* { dg-final { scan-assembler-times "fminm.s" 1 } } */
+/* { dg-final { scan-assembler-times "fminm.d" 1 } } */
+/* { dg-final { scan-assembler-times "fmaxm.s" 1 } } */
+/* { dg-final { scan-assembler-times "fmaxm.d" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp.c b/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp.c
new file mode 100644
index 00000000000..9005e75d868
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv32* -O0" } } */
+/* { dg-options "-march=rv32g_zfa -mabi=ilp32" } */
+
+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.c b/gcc/testsuite/gcc.target/riscv/zfa-fround.c
new file mode 100644
index 00000000000..e84a0bd1998
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfa-fround.c
@@ -0,0 +1,25 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-march=rv64* -O2" } } */
+/* { dg-options "-march=rv64g_zfa -mabi=lp64d" } */
+
+#include <math.h>
+
+extern float a;
+extern double b;
+
+void foo (float *x, double *y)
+{
+  {
+    *x = roundf (a);
+    *y = round (b);
+  }
+  {
+    *x = rintf (a);
+    *y = rint (b);
+  }
+}
+
+/* { dg-final { scan-assembler-times "fround.s" 1 } } */
+/* { dg-final { scan-assembler-times "fround.d" 1 } } */
+/* { dg-final { scan-assembler-times "froundnx.s" 1 } } */
+/* { dg-final { scan-assembler-times "froundnx.d" 1 } } */