diff mbox series

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

Message ID 77a18666-f71d-48e2-a502-a879b3eb6ccf.jinma@linux.alibaba.com
State New
Headers show
Series [v2,RISCV] Add 'Zfa' extension according to riscv-isa-manual | expand

Commit Message

Jin Ma Jan. 12, 2023, 1:56 a.m. UTC
From e4ce8e825c145d74e6b9827f972629548e39f118 Mon Sep 17 00:00:00 2001
From: Jin Ma <jinma@linux.alibaba.com>
Date: Wed, 11 Jan 2023 19:13:27 +0800
Subject: [PATCH] [RISCV] Add 'Zfa' extension according to riscv-isa-manual

From e4ce8e825c145d74e6b9827f972629548e39f118 Mon Sep 17 00:00:00 2001
From: Jin Ma <jinma@linux.alibaba.com>
Date: Wed, 11 Jan 2023 19:13:27 +0800
Subject: [PATCH] [RISCV] Add 'Zfa' extension according to riscv-isa-manual

This patch adds the 'Zfa' extension for riscv, which is an implementation for
unratified and unfrozen RISC-V extension.

Although the binutils-gdb for 'Zfa' extension is not yet upstream, we can try
to discuss it. And we can test new instructions for your (possibly virtual)
environment and early review for fast adoption after ratification.

This 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                     | 105 +++++++++++++++-
 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, 448 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

--
2.17.1

Comments

jinma Jan. 12, 2023, 4:19 a.m. UTC | #1
From e4ce8e825c145d74e6b9827f972629548e39f118 Mon Sep 17 00:00:00 2001
From: Jin Ma <jinma@linux.alibaba.com>
Date: Wed, 11 Jan 2023 19:13:27 +0800
Subject: [PATCH] [RISCV] Add 'Zfa' extension according to riscv-isa-manual
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 | 105 +++++++++++++++-
 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, 448 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 0a89fdaffe2..cccec12975c 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 51cffb2bcb6..2fd407b1d9c 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 5a5a49bf7c0..0e8cf3b3708 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 24ae22c99cd..a99b1e50d96 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 bf2d30782d9..dd3e6f39ff0 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 25fd85b09b1..3ab6d5766c4 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 64ee56b8a7c..d99ec3e9002 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 9a53999a39d..1b269b76dd6 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -799,6 +799,53 @@ static int riscv_symbol_insns (enum riscv_symbol_type type)
 }
 }
+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 +873,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 +1229,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 +1767,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))
 {
@@ -2732,6 +2790,9 @@ riscv_split_64bit_move_p (rtx dest, rtx src)
 && ((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 +2856,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 +2917,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 +2971,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 +4282,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. */
@@ -4350,6 +4424,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)
 {
@@ -4366,6 +4443,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);
@@ -5676,7 +5778,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 a10cee2a0dc..d90ea8d35e7 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 7c3ca48d1cc..14427624392 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
jinma Jan. 12, 2023, 5:17 a.m. UTC | #2
From 4ee11f99d23d39d55bdadd86699ac35a60c79705 Mon Sep 17 00:00:00 2001
From: Jin Ma <jinma@linux.alibaba.com>
Date: Thu, 12 Jan 2023 12:51:37 +0800
Subject: [PATCH v2] [RISCV] Add 'Zfa' extension according to riscv-isa-manual
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 | 109 ++++++++++++++++-
 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, 452 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 0a89fdaffe2..cccec12975c 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 51cffb2bcb6..2fd407b1d9c 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 5a5a49bf7c0..0e8cf3b3708 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 24ae22c99cd..a99b1e50d96 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 bf2d30782d9..dd3e6f39ff0 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 25fd85b09b1..3ab6d5766c4 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 64ee56b8a7c..d99ec3e9002 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 9a53999a39d..77777e8a77f 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))
 {
@@ -2732,6 +2794,9 @@ riscv_split_64bit_move_p (rtx dest, rtx src)
 && ((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 +2860,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 +2921,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 +2975,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 +4286,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. */
@@ -4350,6 +4428,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)
 {
@@ -4366,6 +4447,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);
@@ -5676,7 +5782,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 a10cee2a0dc..d90ea8d35e7 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 7c3ca48d1cc..14427624392 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
jinma Jan. 12, 2023, 5:25 a.m. UTC | #3
I am very sorry. There seems to be some unknown problems in my email, which
caused a problem with the format of the patch. I will deal with it as soon as 
possible. 
I am very sorry for the trouble.
diff mbox series

Patch

diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc
index 0a89fdaffe2..cccec12975c 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 51cffb2bcb6..2fd407b1d9c 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 5a5a49bf7c0..0e8cf3b3708 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 24ae22c99cd..a99b1e50d96 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 bf2d30782d9..dd3e6f39ff0 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 25fd85b09b1..3ab6d5766c4 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 64ee56b8a7c..d99ec3e9002 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 9a53999a39d..1b269b76dd6 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -799,6 +799,53 @@  static int riscv_symbol_insns (enum riscv_symbol_type type)
     }
 }

+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 +873,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 +1229,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 +1767,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))
     {
@@ -2732,6 +2790,9 @@  riscv_split_64bit_move_p (rtx dest, rtx src)
       && ((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 +2856,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 +2917,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 +2971,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 +4282,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.  */

@@ -4350,6 +4424,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)
        {
@@ -4366,6 +4443,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);
@@ -5676,7 +5778,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 a10cee2a0dc..d90ea8d35e7 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 7c3ca48d1cc..14427624392 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 } } */