[v6] RISC-V: Add support for experimental zfa extension.

Message ID 20230310124053.164-1-jinma@linux.alibaba.com
State Superseded
Headers
Series [v6] RISC-V: Add support for experimental zfa extension. |

Commit Message

Jin Ma March 10, 2023, 12:40 p.m. UTC
  This patch adds the 'Zfa' extension for riscv, which is based on:
 https://github.com/riscv/riscv-isa-manual/commit/d74d99e22d5f68832f70982d867614e2149a3bd7
latest 'Zfa' change on the master branch of the RISC-V ISA Manual as
of this writing.

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

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

Implementation of zfa extension on LLVM:
  https://reviews.llvm.org/rGc0947dc44109252fcc0f68a542fc6ef250d4d3a9

There are three points that need to be discussed here.
1. According to riscv-spec, "The FCVTMO D.W.D instruction was added principally to
  accelerate the processing of JavaScript Numbers.", so it seems that no implementation
  is required in the compiler.
2. The FROUND and FROUNDN instructions in this patch use related functions in the math
  library, such as round, floor, ceil, etc. Since there is no interface for half-precision in
  the math library, the instructions FROUN D.H and FROUNDN X.H have not been implemented for
  the time being. Is it necessary to add a built-in interface belonging to riscv such as
 __builtin_roundhf or __builtin_roundf16 to generate half floating point instructions?
3. As far as I know, FMINM and FMAXM instructions correspond to C23 library function fminimum
  and fmaximum. Therefore, I have not dealt with such instructions for the time being, but have
  simply implemented the pattern of fminm<hf\sf\df>3 and fmaxm<hf\sf\df>3. Is it necessary to
  add a built-in interface belonging to riscv such as__builtin_fminm to generate half
  floating-point instructions?

gcc/ChangeLog:

	* common/config/riscv/riscv-common.cc: Add zfa extension.
	* config/riscv/constraints.md (Zf): Constrain the floating point number that the FLI instruction can load.
	* config/riscv/iterators.md (round_pattern): New.
	* config/riscv/predicates.md: Predicate the floating point number that the FLI instruction can load.
	* config/riscv/riscv-opts.h (MASK_ZFA): New.
	(TARGET_ZFA): New.
	* config/riscv/riscv-protos.h (riscv_float_const_rtx_index_for_fli): Get the index of the
          floating-point number that the FLI instruction can load.
	* config/riscv/riscv.cc (find_index_in_array): New.
	(riscv_float_const_rtx_index_for_fli): New.
	(riscv_cannot_force_const_mem): Likewise.
	(riscv_const_insns): Likewise.
	(riscv_legitimize_const_move): Likewise.
	(riscv_split_64bit_move_p): Exclude floating point numbers that can be loaded by FLI instructions.
	(riscv_output_move): Likewise.
	(riscv_memmodel_needs_release_fence): Likewise.
	(riscv_print_operand): Likewise.
	(riscv_secondary_memory_needed): Likewise.
	* config/riscv/riscv.h (GP_REG_RTX_P): New.
	* config/riscv/riscv.md (fminm<mode>3): New.
	(fmaxm<mode>3): New.
	(<round_pattern><ANYF:mode>2): New.
	(rint<ANYF:mode>2): New.
	(f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa): New.

gcc/testsuite/ChangeLog:

	* gcc.target/riscv/zfa-fleq-fltq-rv32.c: New test.
	* gcc.target/riscv/zfa-fleq-fltq.c: New test.
	* gcc.target/riscv/zfa-fli-rv32.c: New test.
	* gcc.target/riscv/zfa-fli-zfh-rv32.c: New test.
	* gcc.target/riscv/zfa-fli-zfh.c: New test.
	* gcc.target/riscv/zfa-fli.c: New test.
	* gcc.target/riscv/zfa-fmovh-fmovp-rv32.c: New test.
	* gcc.target/riscv/zfa-fround-rv32.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/iterators.md                 |   5 +
 gcc/config/riscv/predicates.md                |   4 +
 gcc/config/riscv/riscv-opts.h                 |   3 +
 gcc/config/riscv/riscv-protos.h               |   1 +
 gcc/config/riscv/riscv.cc                     | 168 +++++++++++++++++-
 gcc/config/riscv/riscv.h                      |   1 +
 gcc/config/riscv/riscv.md                     | 112 +++++++++---
 .../gcc.target/riscv/zfa-fleq-fltq-rv32.c     |  19 ++
 .../gcc.target/riscv/zfa-fleq-fltq.c          |  19 ++
 gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c |  79 ++++++++
 .../gcc.target/riscv/zfa-fli-zfh-rv32.c       |  41 +++++
 gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c  |  41 +++++
 gcc/testsuite/gcc.target/riscv/zfa-fli.c      |  79 ++++++++
 .../gcc.target/riscv/zfa-fmovh-fmovp-rv32.c   |  10 ++
 .../gcc.target/riscv/zfa-fround-rv32.c        |  42 +++++
 gcc/testsuite/gcc.target/riscv/zfa-fround.c   |  42 +++++
 18 files changed, 654 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.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-fmovh-fmovp-rv32.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fround.c
  

Comments

Jeff Law March 14, 2023, 6 p.m. UTC | #1
On 3/10/23 05:40, Jin Ma via Gcc-patches wrote:
> This patch adds the 'Zfa' extension for riscv, which is based on:
>   https://github.com/riscv/riscv-isa-manual/commit/d74d99e22d5f68832f70982d867614e2149a3bd7
> latest 'Zfa' change on the master branch of the RISC-V ISA Manual as
> of this writing.
> 
> The Wiki Page (details):
>   https://github.com/a4lg/binutils-gdb/wiki/riscv_zfa
> 
> The binutils-gdb for 'Zfa' extension:
>   https://sourceware.org/pipermail/binutils/2022-September/122938.html
> 
> Implementation of zfa extension on LLVM:
>    https://reviews.llvm.org/rGc0947dc44109252fcc0f68a542fc6ef250d4d3a9
> 
> There are three points that need to be discussed here.
> 1. According to riscv-spec, "The FCVTMO D.W.D instruction was added principally to
>    accelerate the processing of JavaScript Numbers.", so it seems that no implementation
>    is required in the compiler.
> 2. The FROUND and FROUNDN instructions in this patch use related functions in the math
>    library, such as round, floor, ceil, etc. Since there is no interface for half-precision in
>    the math library, the instructions FROUN D.H and FROUNDN X.H have not been implemented for
>    the time being. Is it necessary to add a built-in interface belonging to riscv such as
>   __builtin_roundhf or __builtin_roundf16 to generate half floating point instructions?
> 3. As far as I know, FMINM and FMAXM instructions correspond to C23 library function fminimum
>    and fmaximum. Therefore, I have not dealt with such instructions for the time being, but have
>    simply implemented the pattern of fminm<hf\sf\df>3 and fmaxm<hf\sf\df>3. Is it necessary to
>    add a built-in interface belonging to riscv such as__builtin_fminm to generate half
>    floating-point instructions?
> 
> gcc/ChangeLog:
> 
> 	* common/config/riscv/riscv-common.cc: Add zfa extension.
> 	* config/riscv/constraints.md (Zf): Constrain the floating point number that the FLI instruction can load.
> 	* config/riscv/iterators.md (round_pattern): New.
> 	* config/riscv/predicates.md: Predicate the floating point number that the FLI instruction can load.
> 	* config/riscv/riscv-opts.h (MASK_ZFA): New.
> 	(TARGET_ZFA): New.
> 	* config/riscv/riscv-protos.h (riscv_float_const_rtx_index_for_fli): Get the index of the
>            floating-point number that the FLI instruction can load.
> 	* config/riscv/riscv.cc (find_index_in_array): New.
> 	(riscv_float_const_rtx_index_for_fli): New.
> 	(riscv_cannot_force_const_mem): Likewise.
> 	(riscv_const_insns): Likewise.
> 	(riscv_legitimize_const_move): Likewise.
> 	(riscv_split_64bit_move_p): Exclude floating point numbers that can be loaded by FLI instructions.
> 	(riscv_output_move): Likewise.
> 	(riscv_memmodel_needs_release_fence): Likewise.
> 	(riscv_print_operand): Likewise.
> 	(riscv_secondary_memory_needed): Likewise.
> 	* config/riscv/riscv.h (GP_REG_RTX_P): New.
> 	* config/riscv/riscv.md (fminm<mode>3): New.
> 	(fmaxm<mode>3): New.
> 	(<round_pattern><ANYF:mode>2): New.
> 	(rint<ANYF:mode>2): New.
> 	(f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa): New.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.target/riscv/zfa-fleq-fltq-rv32.c: New test.
> 	* gcc.target/riscv/zfa-fleq-fltq.c: New test.
> 	* gcc.target/riscv/zfa-fli-rv32.c: New test.
> 	* gcc.target/riscv/zfa-fli-zfh-rv32.c: New test.
> 	* gcc.target/riscv/zfa-fli-zfh.c: New test.
> 	* gcc.target/riscv/zfa-fli.c: New test.
> 	* gcc.target/riscv/zfa-fmovh-fmovp-rv32.c: New test.
> 	* gcc.target/riscv/zfa-fround-rv32.c: New test.
> 	* gcc.target/riscv/zfa-fround.c: New test.
This needs to wait for gcc-14 IMHO.

jeff
  
Christoph Müllner April 13, 2023, 9:22 a.m. UTC | #2
On Fri, Mar 10, 2023 at 1:41 PM Jin Ma via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> This patch adds the 'Zfa' extension for riscv, which is based on:
>  https://github.com/riscv/riscv-isa-manual/commit/d74d99e22d5f68832f70982d867614e2149a3bd7
> latest 'Zfa' change on the master branch of the RISC-V ISA Manual as
> of this writing.
>
> The Wiki Page (details):
>  https://github.com/a4lg/binutils-gdb/wiki/riscv_zfa
>
> The binutils-gdb for 'Zfa' extension:
>  https://sourceware.org/pipermail/binutils/2022-September/122938.html
>
> Implementation of zfa extension on LLVM:
>   https://reviews.llvm.org/rGc0947dc44109252fcc0f68a542fc6ef250d4d3a9
>
> There are three points that need to be discussed here.
> 1. According to riscv-spec, "The FCVTMO D.W.D instruction was added principally to
>   accelerate the processing of JavaScript Numbers.", so it seems that no implementation
>   is required in the compiler.
> 2. The FROUND and FROUNDN instructions in this patch use related functions in the math
>   library, such as round, floor, ceil, etc. Since there is no interface for half-precision in
>   the math library, the instructions FROUN D.H and FROUNDN X.H have not been implemented for
>   the time being. Is it necessary to add a built-in interface belonging to riscv such as
>  __builtin_roundhf or __builtin_roundf16 to generate half floating point instructions?
> 3. As far as I know, FMINM and FMAXM instructions correspond to C23 library function fminimum
>   and fmaximum. Therefore, I have not dealt with such instructions for the time being, but have
>   simply implemented the pattern of fminm<hf\sf\df>3 and fmaxm<hf\sf\df>3. Is it necessary to
>   add a built-in interface belonging to riscv such as__builtin_fminm to generate half
>   floating-point instructions?


I have rebased and tested this patch.
Here are my observations (with fixes below at the actual code):
* There is a compiler warning because of a missing "fallthrough" comment
* There are merge conflicts with a current master
* The constant operand of the fli instruction uses the constant index
in the rs1-field, but not the constant in hex FP literal form

A patch that addresses these issues can also be found here:
  https://github.com/cmuellner/gcc/tree/riscv-zfa

Additionally I observe the following failing test cases with this patch applied:

                === gcc: Unexpected fails for rv64gc lp64d medlow ===
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O0  (internal compiler
error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O0  (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O1  (internal compiler
error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O1  (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O2  (internal compiler
error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O2  (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O2 -flto
-fno-use-linker-plugin -flto-partition=none  (internal compiler error:
Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O2 -flto
-fno-use-linker-plugin -flto-partition=none  (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O2 -flto
-fuse-linker-plugin -fno-fat-lto-objects  (internal compiler error:
Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O2 -flto
-fuse-linker-plugin -fno-fat-lto-objects  (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O3 -g  (internal
compiler error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -O3 -g  (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -Os  (internal compiler
error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c   -Os  (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c  -Og -g  (internal
compiler error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c  -Og -g  (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c  -Oz  (internal compiler
error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c  -Oz  (test for excess errors)

I have not analysed these ICEs so far.


>
> gcc/ChangeLog:
>
>         * common/config/riscv/riscv-common.cc: Add zfa extension.
>         * config/riscv/constraints.md (Zf): Constrain the floating point number that the FLI instruction can load.
>         * config/riscv/iterators.md (round_pattern): New.
>         * config/riscv/predicates.md: Predicate the floating point number that the FLI instruction can load.
>         * config/riscv/riscv-opts.h (MASK_ZFA): New.
>         (TARGET_ZFA): New.
>         * config/riscv/riscv-protos.h (riscv_float_const_rtx_index_for_fli): Get the index of the
>           floating-point number that the FLI instruction can load.
>         * config/riscv/riscv.cc (find_index_in_array): New.
>         (riscv_float_const_rtx_index_for_fli): New.
>         (riscv_cannot_force_const_mem): Likewise.
>         (riscv_const_insns): Likewise.
>         (riscv_legitimize_const_move): Likewise.
>         (riscv_split_64bit_move_p): Exclude floating point numbers that can be loaded by FLI instructions.
>         (riscv_output_move): Likewise.
>         (riscv_memmodel_needs_release_fence): Likewise.
>         (riscv_print_operand): Likewise.
>         (riscv_secondary_memory_needed): Likewise.
>         * config/riscv/riscv.h (GP_REG_RTX_P): New.
>         * config/riscv/riscv.md (fminm<mode>3): New.
>         (fmaxm<mode>3): New.
>         (<round_pattern><ANYF:mode>2): New.
>         (rint<ANYF:mode>2): New.
>         (f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa): New.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.target/riscv/zfa-fleq-fltq-rv32.c: New test.
>         * gcc.target/riscv/zfa-fleq-fltq.c: New test.
>         * gcc.target/riscv/zfa-fli-rv32.c: New test.
>         * gcc.target/riscv/zfa-fli-zfh-rv32.c: New test.
>         * gcc.target/riscv/zfa-fli-zfh.c: New test.
>         * gcc.target/riscv/zfa-fli.c: New test.
>         * gcc.target/riscv/zfa-fmovh-fmovp-rv32.c: New test.
>         * gcc.target/riscv/zfa-fround-rv32.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/iterators.md                 |   5 +
>  gcc/config/riscv/predicates.md                |   4 +
>  gcc/config/riscv/riscv-opts.h                 |   3 +
>  gcc/config/riscv/riscv-protos.h               |   1 +
>  gcc/config/riscv/riscv.cc                     | 168 +++++++++++++++++-
>  gcc/config/riscv/riscv.h                      |   1 +
>  gcc/config/riscv/riscv.md                     | 112 +++++++++---
>  .../gcc.target/riscv/zfa-fleq-fltq-rv32.c     |  19 ++
>  .../gcc.target/riscv/zfa-fleq-fltq.c          |  19 ++
>  gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c |  79 ++++++++
>  .../gcc.target/riscv/zfa-fli-zfh-rv32.c       |  41 +++++
>  gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c  |  41 +++++
>  gcc/testsuite/gcc.target/riscv/zfa-fli.c      |  79 ++++++++
>  .../gcc.target/riscv/zfa-fmovh-fmovp-rv32.c   |  10 ++
>  .../gcc.target/riscv/zfa-fround-rv32.c        |  42 +++++
>  gcc/testsuite/gcc.target/riscv/zfa-fround.c   |  42 +++++
>  18 files changed, 654 insertions(+), 23 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.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-fmovh-fmovp-rv32.c
>  create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.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 ebc1ed7d7e4..8fec3bc71c9 100644
> --- a/gcc/common/config/riscv/riscv-common.cc
> +++ b/gcc/common/config/riscv/riscv-common.cc
> @@ -217,6 +217,8 @@ static const struct riscv_ext_version riscv_ext_version_table[] =
>    {"zfh",       ISA_SPEC_CLASS_NONE, 1, 0},
>    {"zfhmin",    ISA_SPEC_CLASS_NONE, 1, 0},
>
> +  {"zfa",     ISA_SPEC_CLASS_NONE, 0, 1},
> +
>    {"zmmul", ISA_SPEC_CLASS_NONE, 1, 0},
>
>    {"svinval", ISA_SPEC_CLASS_NONE, 1, 0},
> @@ -1243,6 +1245,8 @@ static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
>    {"zfhmin",    &gcc_options::x_riscv_zf_subext, MASK_ZFHMIN},
>    {"zfh",       &gcc_options::x_riscv_zf_subext, MASK_ZFH},
>
> +  {"zfa",       &gcc_options::x_riscv_zf_subext, MASK_ZFA},
> +
>    {"zmmul", &gcc_options::x_riscv_zm_subext, MASK_ZMMUL},
>
>    {"svinval", &gcc_options::x_riscv_sv_subext, MASK_SVINVAL},
> diff --git a/gcc/config/riscv/constraints.md b/gcc/config/riscv/constraints.md
> index fdfcf2380f8..2ba0160934a 100644
> --- a/gcc/config/riscv/constraints.md
> +++ b/gcc/config/riscv/constraints.md
> @@ -118,6 +118,13 @@ (define_constraint "T"
>    (and (match_operand 0 "move_operand")
>         (match_test "CONSTANT_P (op)")))
>
> +;; Zfa constraints.
> +
> +(define_constraint "Zf"
> +  "A floating point number that can be loaded using instruction `fli` in zfa."
> +  (and (match_code "const_double")
> +       (match_test "(riscv_float_const_rtx_index_for_fli (op) != -1)")))
> +
>  ;; Vector constraints.
>
>  (define_register_constraint "vr" "TARGET_VECTOR ? V_REGS : NO_REGS"
> diff --git a/gcc/config/riscv/iterators.md b/gcc/config/riscv/iterators.md
> index 5b70ab20758..ef9b1aa9ed3 100644
> --- a/gcc/config/riscv/iterators.md
> +++ b/gcc/config/riscv/iterators.md
> @@ -284,3 +284,8 @@ (define_int_iterator QUIET_COMPARISON [UNSPEC_FLT_QUIET UNSPEC_FLE_QUIET])
>  (define_int_attr quiet_pattern [(UNSPEC_FLT_QUIET "lt") (UNSPEC_FLE_QUIET "le")])
>  (define_int_attr QUIET_PATTERN [(UNSPEC_FLT_QUIET "LT") (UNSPEC_FLE_QUIET "LE")])
>
> +(define_int_iterator ROUND [UNSPEC_ROUND UNSPEC_FLOOR UNSPEC_CEIL UNSPEC_BTRUNC UNSPEC_ROUNDEVEN UNSPEC_NEARBYINT])
> +(define_int_attr round_pattern [(UNSPEC_ROUND "round") (UNSPEC_FLOOR "floor") (UNSPEC_CEIL "ceil")
> +                               (UNSPEC_BTRUNC "btrunc") (UNSPEC_ROUNDEVEN "roundeven") (UNSPEC_NEARBYINT "nearbyint")])
> +(define_int_attr round_rm [(UNSPEC_ROUND "rmm") (UNSPEC_FLOOR "rdn") (UNSPEC_CEIL "rup")
> +                          (UNSPEC_BTRUNC "rtz") (UNSPEC_ROUNDEVEN "rne") (UNSPEC_NEARBYINT "dyn")])
> \ No newline at end of file
> diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md
> index 0d9d7701c7e..b16b8d438e3 100644
> --- a/gcc/config/riscv/predicates.md
> +++ b/gcc/config/riscv/predicates.md
> @@ -149,6 +149,10 @@ (define_predicate "move_operand"
>      case CONST_POLY_INT:
>        return known_eq (rtx_to_poly_int64 (op), BYTES_PER_RISCV_VECTOR);
>
> +    case CONST_DOUBLE:
> +      return const_0_operand (op, mode)
> +             || (riscv_float_const_rtx_index_for_fli (op) != -1);
> +
>      case CONST:
>      case SYMBOL_REF:
>      case LABEL_REF:
> diff --git a/gcc/config/riscv/riscv-opts.h b/gcc/config/riscv/riscv-opts.h
> index ff398c0a2ae..3ae729eec30 100644
> --- a/gcc/config/riscv/riscv-opts.h
> +++ b/gcc/config/riscv/riscv-opts.h
> @@ -172,6 +172,9 @@ enum stack_protector_guard {
>  #define TARGET_ZFHMIN ((riscv_zf_subext & MASK_ZFHMIN) != 0)
>  #define TARGET_ZFH    ((riscv_zf_subext & MASK_ZFH) != 0)
>
> +#define MASK_ZFA   (1 << 0)
> +#define TARGET_ZFA    ((riscv_zf_subext & MASK_ZFA) != 0)
> +
>  #define MASK_ZMMUL      (1 << 0)
>  #define TARGET_ZMMUL    ((riscv_zm_subext & MASK_ZMMUL) != 0)
>
> diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
> index f35aaf35b48..0f5dcba8294 100644
> --- a/gcc/config/riscv/riscv-protos.h
> +++ b/gcc/config/riscv/riscv-protos.h
> @@ -38,6 +38,7 @@ enum riscv_symbol_type {
>  /* Routines implemented in riscv.cc.  */
>  extern enum riscv_symbol_type riscv_classify_symbolic_expression (rtx);
>  extern bool riscv_symbolic_constant_p (rtx, enum riscv_symbol_type *);
> +extern int riscv_float_const_rtx_index_for_fli (rtx);
>  extern int riscv_regno_mode_ok_for_base_p (int, machine_mode, bool);
>  extern int riscv_address_insns (rtx, machine_mode, bool);
>  extern int riscv_const_insns (rtx);
> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
> index c91fa3101aa..c81a2bf44f5 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -799,6 +799,108 @@ static int riscv_symbol_insns (enum riscv_symbol_type type)
>      }
>  }
>
> +/* Immediate values loaded by the FLI.S instruction in Chapter 25 of the latest RISC-V ISA
> +   Manual draft. For details, please see:
> +   https://github.com/riscv/riscv-isa-manual/releases/tag/draft-20221217-cb3b9d1 */
> +
> +unsigned HOST_WIDE_INT fli_value_hf[32] =
> +{
> +  0xbc00, 0x400, 0x100, 0x200, 0x1c00, 0x2000, 0x2c00, 0x3000,
> +  0x3400, 0x3500, 0x3600, 0x3700, 0x3800, 0x3900, 0x3a00, 0x3b00,
> +  0x3c00, 0x3d00, 0x3e00, 0x3f00, 0x4000, 0x4100, 0x4200, 0x4400,
> +  0x4800, 0x4c00, 0x5800, 0x5c00, 0x7800,
> +  /* Only used for filling, ensuring that 29 and 30 of HF are the same. */
> +  0x7800,
> +  0x7c00, 0x7e00,
> +};
> +
> +unsigned HOST_WIDE_INT fli_value_sf[32] =
> +{
> +  0xbf800000, 0x00800000, 0x37800000, 0x38000000, 0x3b800000, 0x3c000000, 0x3d800000, 0x3e000000,
> +  0x3e800000, 0x3ea00000, 0x3ec00000, 0x3ee00000, 0x3f000000, 0x3f200000, 0x3f400000, 0x3f600000,
> +  0x3f800000, 0x3fa00000, 0x3fc00000, 0x3fe00000, 0x40000000, 0x40200000, 0x40400000, 0x40800000,
> +  0x41000000, 0x41800000, 0x43000000, 0x43800000, 0x47000000, 0x47800000, 0x7f800000, 0x7fc00000
> +};
> +
> +unsigned HOST_WIDE_INT fli_value_df[32] =
> +{
> +  0xbff0000000000000, 0x10000000000000, 0x3ef0000000000000, 0x3f00000000000000,
> +  0x3f70000000000000, 0x3f80000000000000, 0x3fb0000000000000, 0x3fc0000000000000,
> +  0x3fd0000000000000, 0x3fd4000000000000, 0x3fd8000000000000, 0x3fdc000000000000,
> +  0x3fe0000000000000, 0x3fe4000000000000, 0x3fe8000000000000, 0x3fec000000000000,
> +  0x3ff0000000000000, 0x3ff4000000000000, 0x3ff8000000000000, 0x3ffc000000000000,
> +  0x4000000000000000, 0x4004000000000000, 0x4008000000000000, 0x4010000000000000,
> +  0x4020000000000000, 0x4030000000000000, 0x4060000000000000, 0x4070000000000000,
> +  0x40e0000000000000, 0x40f0000000000000, 0x7ff0000000000000, 0x7ff8000000000000,
> +};
> +
> +/* Find the index of TARGET in ARRAY, and return -1 if not found. */
> +
> +static int
> +find_index_in_array (unsigned HOST_WIDE_INT target, unsigned HOST_WIDE_INT *array, int len)
> +{
> +  if (array == NULL)
> +    return -1;
> +
> +  for (int i = 0; i < len; i++)
> +    {
> +      if (target == array[i])
> +       return i;
> +    }
> +  return -1;
> +}
> +
> +/* Return index of the FLI instruction table if rtx X is an immediate constant that
> +   can be moved using a single FLI instruction in zfa extension. -1 otherwise. */
> +
> +int
> +riscv_float_const_rtx_index_for_fli (rtx x)
> +{
> +  machine_mode mode = GET_MODE (x);
> +
> +  if (!TARGET_ZFA || mode == VOIDmode
> +      || !CONST_DOUBLE_P(x)
> +      || (mode == HFmode && !TARGET_ZFH)
> +      || (mode == SFmode && !TARGET_HARD_FLOAT)
> +      || (mode == DFmode && !TARGET_DOUBLE_FLOAT))
> +    return -1;
> +
> +  if (!SCALAR_FLOAT_MODE_P (mode)
> +      || GET_MODE_BITSIZE (mode).to_constant () > HOST_BITS_PER_WIDE_INT
> +      /* Only support up to DF mode.  */
> +      || GET_MODE_BITSIZE (mode).to_constant () > GET_MODE_BITSIZE (DFmode))
> +    return -1;
> +
> +  unsigned HOST_WIDE_INT ival = 0;
> +
> +  long res[2];
> +  real_to_target (res,
> +                 CONST_DOUBLE_REAL_VALUE (x),
> +                 REAL_MODE_FORMAT (mode));
> +
> +  if (mode == DFmode)
> +    {
> +      int order = BYTES_BIG_ENDIAN ? 1 : 0;
> +      ival = zext_hwi (res[order], 32);
> +      ival |= (zext_hwi (res[1 - order], 32) << 32);
> +    }
> +  else
> +      ival = zext_hwi (res[0], 32);
> +
> +  switch (mode)
> +    {
> +      case SFmode:
> +       return find_index_in_array (ival, fli_value_sf, 32);
> +      case DFmode:
> +       return find_index_in_array (ival, fli_value_df, 32);
> +      case HFmode:
> +       return find_index_in_array (ival, fli_value_hf, 32);
> +      default:
> +       break;
> +    }
> +  return -1;
> +}
> +
>  /* Implement TARGET_LEGITIMATE_CONSTANT_P.  */
>
>  static bool
> @@ -826,6 +928,9 @@ riscv_cannot_force_const_mem (machine_mode mode ATTRIBUTE_UNUSED, rtx x)
>    if (GET_CODE (x) == HIGH)
>      return true;
>
> +  if (riscv_float_const_rtx_index_for_fli (x) != -1)
> +   return true;
> +
>    split_const (x, &base, &offset);
>    if (riscv_symbolic_constant_p (base, &type))
>      {
> @@ -1191,6 +1296,8 @@ riscv_const_insns (rtx x)
>        }
>
>      case CONST_DOUBLE:
> +      if (riscv_float_const_rtx_index_for_fli (x) != -1)
> +       return 4;

Here a "/* Fall through.  */" is missing to avoid a compiler warning.

>      case CONST_VECTOR:
>        /* We can use x0 to load floating-point zero.  */
>        return x == CONST0_RTX (GET_MODE (x)) ? 1 : 0;
> @@ -1727,6 +1834,12 @@ riscv_legitimize_const_move (machine_mode mode, rtx dest, rtx src)
>        return;
>      }
>
> +  if (riscv_float_const_rtx_index_for_fli (src) != -1)
> +    {
> +      riscv_emit_set (dest, src);
> +      return;
> +    }
> +
>    /* Split moves of symbolic constants into high/low pairs.  */
>    if (riscv_split_symbol (dest, src, MAX_MACHINE_MODE, &src, FALSE))
>      {
> @@ -2739,12 +2852,19 @@ riscv_split_64bit_move_p (rtx dest, rtx src)
>    if (TARGET_64BIT)
>      return false;
>
> +  /* There is no need to split if the FLI instruction in the `Zfa` extension can be used. */
> +  if (TARGET_ZFA && (riscv_float_const_rtx_index_for_fli (src) != -1))
> +    return false;
> +
>    /* Allow FPR <-> FPR and FPR <-> MEM moves, and permit the special case
>       of zeroing an FPR with FCVT.D.W.  */
>    if (TARGET_DOUBLE_FLOAT
>        && ((FP_REG_RTX_P (src) && FP_REG_RTX_P (dest))
>           || (FP_REG_RTX_P (dest) && MEM_P (src))
>           || (FP_REG_RTX_P (src) && MEM_P (dest))
> +         || (TARGET_ZFA
> +             && ((FP_REG_RTX_P (dest) && GP_REG_RTX_P (src))
> +             || (FP_REG_RTX_P (src) && GP_REG_RTX_P (dest))))
>           || (FP_REG_RTX_P (dest) && src == CONST0_RTX (GET_MODE (src)))))
>      return false;
>
> @@ -2808,6 +2928,8 @@ riscv_output_move (rtx dest, rtx src)
>           case 4:
>             return "fmv.x.s\t%0,%1";
>           case 8:
> +           if (!TARGET_64BIT && TARGET_ZFA)
> +             return "fmv.x.w\t%0,%1\n\tfmvh.x.d\t%N0,%1";
>             return "fmv.x.d\t%0,%1";
>           }
>
> @@ -2867,6 +2989,8 @@ riscv_output_move (rtx dest, rtx src)
>               case 8:
>                 if (TARGET_64BIT)
>                   return "fmv.d.x\t%0,%z1";
> +               else if (TARGET_ZFA && src != CONST0_RTX (mode))
> +                 return "fmvp.d.x\t%0,%1,%N1";
>                 /* in RV32, we can emulate fmv.d.x %0, x0 using fcvt.d.w */
>                 gcc_assert (src == CONST0_RTX (mode));
>                 return "fcvt.d.w\t%0,x0";
> @@ -2919,6 +3043,14 @@ riscv_output_move (rtx dest, rtx src)
>           case 8:
>             return "fld\t%0,%1";
>           }
> +
> +      if (src_code == CONST_DOUBLE && (riscv_float_const_rtx_index_for_fli (src) != -1))
> +       switch (width)
> +         {
> +           case 2: return "fli.h\t%0,%1";
> +           case 4: return "fli.s\t%0,%1";
> +           case 8: return "fli.d\t%0,%1";


> +         }
>      }
>    if (dest_code == REG && GP_REG_P (REGNO (dest)) && src_code == CONST_POLY_INT)
>      {
> @@ -4222,6 +4354,7 @@ riscv_memmodel_needs_release_fence (enum memmodel model)
>     'S' Print shift-index of single-bit mask OP.
>     'T' Print shift-index of inverted single-bit mask OP.
>     '~' Print w if TARGET_64BIT is true; otherwise not print anything.
> +   'N'  Print next register.
>
>     Note please keep this list and the list in riscv.md in sync.  */
>
> @@ -4406,6 +4539,9 @@ riscv_print_operand (FILE *file, rtx op, int letter)
>         output_addr_const (file, newop);
>         break;
>        }
> +    case 'N':
> +      fputs (reg_names[REGNO (op) + 1], file);
> +      break;
>      default:
>        switch (code)
>         {
> @@ -4422,6 +4558,35 @@ riscv_print_operand (FILE *file, rtx op, int letter)
>             output_address (mode, XEXP (op, 0));
>           break;
>
> +       case CONST_DOUBLE:
> +         {
> +           if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
> +             {
> +               fputs (reg_names[GP_REG_FIRST], file);
> +               break;
> +             }
> +
> +           int fli_index = riscv_float_const_rtx_index_for_fli (op);
> +           if (fli_index == -1 || fli_index > 31)
> +             {
> +               output_operand_lossage ("invalid use of '%%%c'", letter);
> +               break;
> +             }
> +
> +           switch (fli_index)
> +             {
> +               case 1:
> +                 asm_fprintf (file, "%s", "min");break;
> +               case 30:
> +                 asm_fprintf (file, "%s", "inf");break;
> +               case 31:
> +                 asm_fprintf (file, "%s", "nan");break;
> +               default:
> +                 asm_fprintf (file, "%d", fli_index);break;

This prints the constant-index, but not the constant itself.
We discussed this on the Binutils patch and agreed that we will use
hex floating-point literals.

Something like this should work:
                default:
                  {
                    char s[60];
                    real_to_hexadecimal (s, CONST_DOUBLE_REAL_VALUE (op),
                                         sizeof (s), 0, 1);
                    asm_fprintf (file, "%s", s);
                    break;
                  }

So, since we know the value is a valid constant, we can just print it.


> +             }
> +           break;
> +         }
> +
>         default:
>           if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
>             fputs (reg_names[GP_REG_FIRST], file);
> @@ -5734,7 +5899,8 @@ riscv_secondary_memory_needed (machine_mode mode, reg_class_t class1,
>  {
>    return (!riscv_v_ext_vector_mode_p (mode)
>           && GET_MODE_SIZE (mode).to_constant () > UNITS_PER_WORD
> -         && (class1 == FP_REGS) != (class2 == FP_REGS));
> +         && (class1 == FP_REGS) != (class2 == FP_REGS)
> +         && !TARGET_ZFA);

Does not apply on master anymore.
New code:
  && !TARGET_XTHEADFMV && !TARGET_ZFA);

>  }
>
>  /* Implement TARGET_REGISTER_MOVE_COST.  */
> diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
> index 15b9317a8ce..f6ab9c75317 100644
> --- a/gcc/config/riscv/riscv.h
> +++ b/gcc/config/riscv/riscv.h
> @@ -377,6 +377,7 @@ ASM_MISA_SPEC
>  #define SIBCALL_REG_P(REGNO)   \
>    TEST_HARD_REG_BIT (reg_class_contents[SIBCALL_REGS], REGNO)
>
> +#define GP_REG_RTX_P(X) (REG_P (X) && GP_REG_P (REGNO (X)))
>  #define FP_REG_RTX_P(X) (REG_P (X) && FP_REG_P (REGNO (X)))
>
>  /* Use s0 as the frame pointer if it is so requested.  */
> diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
> index 371d6838c0b..4fa2896b8e8 100644
> --- a/gcc/config/riscv/riscv.md
> +++ b/gcc/config/riscv/riscv.md
> @@ -59,6 +59,15 @@ (define_c_enum "unspec" [
>    UNSPEC_LROUND
>    UNSPEC_FMIN
>    UNSPEC_FMAX
> +  UNSPEC_RINT
> +  UNSPEC_ROUND
> +  UNSPEC_FLOOR
> +  UNSPEC_CEIL
> +  UNSPEC_BTRUNC
> +  UNSPEC_ROUNDEVEN
> +  UNSPEC_NEARBYINT
> +  UNSPEC_FMINM
> +  UNSPEC_FMAXM
>
>    ;; Stack tie
>    UNSPEC_TIE
> @@ -1227,6 +1236,26 @@ (define_insn "neg<mode>2"
>  ;;
>  ;;  ....................
>
> +(define_insn "fminm<mode>3"
> +  [(set (match_operand:ANYF                    0 "register_operand" "=f")
> +       (unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" " f"))
> +                     (use (match_operand:ANYF 2 "register_operand" " f"))]
> +                    UNSPEC_FMINM))]
> +  "TARGET_HARD_FLOAT && TARGET_ZFA"
> +  "fminm.<fmt>\t%0,%1,%2"
> +  [(set_attr "type" "fmove")
> +   (set_attr "mode" "<UNITMODE>")])
> +
> +(define_insn "fmaxm<mode>3"
> +  [(set (match_operand:ANYF                    0 "register_operand" "=f")
> +       (unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" " f"))
> +                     (use (match_operand:ANYF 2 "register_operand" " f"))]
> +                    UNSPEC_FMAXM))]
> +  "TARGET_HARD_FLOAT && TARGET_ZFA"
> +  "fmaxm.<fmt>\t%0,%1,%2"
> +  [(set_attr "type" "fmove")
> +   (set_attr "mode" "<UNITMODE>")])
> +
>  (define_insn "fmin<mode>3"
>    [(set (match_operand:ANYF                    0 "register_operand" "=f")
>         (unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" " f"))
> @@ -1503,13 +1532,13 @@ (define_expand "movhf"
>  })
>
>  (define_insn "*movhf_hardfloat"
> -  [(set (match_operand:HF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*r,  *r,*r,*m")
> -       (match_operand:HF 1 "move_operand"         " f,G,m,f,G,*r,*f,*G*r,*m,*r"))]
> +  [(set (match_operand:HF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r,  *r,*r,*m")
> +       (match_operand:HF 1 "move_operand"         " f,Zf,G,m,f,G,*r,*f,*G*r,*m,*r"))]
>    "TARGET_ZFHMIN
>     && (register_operand (operands[0], HFmode)
>         || reg_or_0_operand (operands[1], HFmode))"
>    { return riscv_output_move (operands[0], operands[1]); }
> -  [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
> +  [(set_attr "move_type" "fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
>     (set_attr "mode" "HF")])
>
>  (define_insn "*movhf_softfloat"
> @@ -1575,6 +1604,26 @@ (define_insn "l<rint_pattern><ANYF:mode><GPR:mode>2"
>    [(set_attr "type" "fcvt")
>     (set_attr "mode" "<ANYF:MODE>")])
>
> +(define_insn "<round_pattern><ANYF:mode>2"
> +  [(set (match_operand:ANYF     0 "register_operand" "=f")
> +       (unspec:ANYF
> +           [(match_operand:ANYF 1 "register_operand" " f")]
> +       ROUND))]
> +  "TARGET_HARD_FLOAT && TARGET_ZFA"
> +  "fround.<ANYF:fmt>\t%0,%1,<round_rm>"
> +  [(set_attr "type" "fcvt")
> +   (set_attr "mode" "<ANYF:MODE>")])
> +
> +(define_insn "rint<ANYF:mode>2"
> +  [(set (match_operand:ANYF     0 "register_operand" "=f")
> +       (unspec:ANYF
> +           [(match_operand:ANYF 1 "register_operand" " f")]
> +       UNSPEC_RINT))]
> +  "TARGET_HARD_FLOAT && TARGET_ZFA"
> +  "froundnx.<ANYF:fmt>\t%0,%1"
> +  [(set_attr "type" "fcvt")
> +   (set_attr "mode" "<ANYF:MODE>")])
> +
>  ;;
>  ;;  ....................
>  ;;
> @@ -1834,13 +1883,13 @@ (define_expand "movsf"
>  })
>
>  (define_insn "*movsf_hardfloat"
> -  [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*r,  *r,*r,*m")
> -       (match_operand:SF 1 "move_operand"         " f,G,m,f,G,*r,*f,*G*r,*m,*r"))]
> +  [(set (match_operand:SF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r,  *r,*r,*m")
> +       (match_operand:SF 1 "move_operand"         " f,Zf,G,m,f,G,*r,*f,*G*r,*m,*r"))]
>    "TARGET_HARD_FLOAT
>     && (register_operand (operands[0], SFmode)
>         || reg_or_0_operand (operands[1], SFmode))"
>    { return riscv_output_move (operands[0], operands[1]); }
> -  [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
> +  [(set_attr "move_type" "fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
>     (set_attr "mode" "SF")])
>
>  (define_insn "*movsf_softfloat"
> @@ -1867,23 +1916,23 @@ (define_expand "movdf"
>  ;; In RV32, we lack fmv.x.d and fmv.d.x.  Go through memory instead.
>  ;; (However, we can still use fcvt.d.w to zero a floating-point register.)
>  (define_insn "*movdf_hardfloat_rv32"
> -  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,m,m,  *r,*r,*m")
> -       (match_operand:DF 1 "move_operand"         " f,G,m,f,G,*r*G,*m,*r"))]
> +  [(set (match_operand:DF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r,  *r,*r,*m")
> +       (match_operand:DF 1 "move_operand"         " f,Zf,G,m,f,G,*r,*f,*r*G,*m,*r"))]

Does not apply on master anymore, needs:
  [(set (match_operand:DF 0 "nonimmediate_operand"
"=f,f,f,f,m,m,*f,*r,*th_f_fmv,*th_r_fmv,  *r,*r,*m")
       (match_operand:DF 1 "move_operand"         "
f,Zf,G,m,f,G,*r,*f,*th_r_fmv,*th_f_fmv,*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")

Does not apply on master anymore, needs:
  [(set_attr "move_type"
"fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,mtc,mfc,move,load,store")

>     (set_attr "mode" "DF")])
>
>  (define_insn "*movdf_hardfloat_rv64"
> -  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*r,  *r,*r,*m")
> -       (match_operand:DF 1 "move_operand"         " f,G,m,f,G,*r,*f,*r*G,*m,*r"))]
> +  [(set (match_operand:DF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r,  *r,*r,*m")
> +       (match_operand:DF 1 "move_operand"         " f,Zf,G,m,f,G,*r,*f,*r*G,*m,*r"))]
>    "TARGET_64BIT && TARGET_DOUBLE_FLOAT
>     && (register_operand (operands[0], DFmode)
>         || reg_or_0_operand (operands[1], DFmode))"
>    { return riscv_output_move (operands[0], operands[1]); }
> -  [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
> +  [(set_attr "move_type" "fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
>     (set_attr "mode" "DF")])
>
>  (define_insn "*movdf_softfloat"
> @@ -2490,16 +2539,23 @@ (define_expand "f<quiet_pattern>_quiet<ANYF:mode><X:mode>4"
>    rtx op0 = operands[0];
>    rtx op1 = operands[1];
>    rtx op2 = operands[2];
> -  rtx tmp = gen_reg_rtx (SImode);
> -  rtx cmp = gen_rtx_<QUIET_PATTERN> (<X:MODE>mode, op1, op2);
> -  rtx frflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, const0_rtx),
> -                                        UNSPECV_FRFLAGS);
> -  rtx fsflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, tmp),
> -                                        UNSPECV_FSFLAGS);
> -
> -  emit_insn (gen_rtx_SET (tmp, frflags));
> -  emit_insn (gen_rtx_SET (op0, cmp));
> -  emit_insn (fsflags);
> +
> +  if (TARGET_ZFA)
> +    emit_insn (gen_f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa(op0, op1, op2));
> +  else
> +    {
> +      rtx tmp = gen_reg_rtx (SImode);
> +      rtx cmp = gen_rtx_<QUIET_PATTERN> (<X:MODE>mode, op1, op2);
> +      rtx frflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, const0_rtx),
> +                                            UNSPECV_FRFLAGS);
> +      rtx fsflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, tmp),
> +                                            UNSPECV_FSFLAGS);
> +
> +      emit_insn (gen_rtx_SET (tmp, frflags));
> +      emit_insn (gen_rtx_SET (op0, cmp));
> +      emit_insn (fsflags);
> +    }
> +
>    if (HONOR_SNANS (<ANYF:MODE>mode))
>      emit_insn (gen_rtx_UNSPEC_VOLATILE (<ANYF:MODE>mode,
>                                         gen_rtvec (2, op1, op2),
> @@ -2507,6 +2563,18 @@ (define_expand "f<quiet_pattern>_quiet<ANYF:mode><X:mode>4"
>    DONE;
>  })
>
> +(define_insn "f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa"
> +   [(set (match_operand:X      0 "register_operand" "=r")
> +        (unspec:X
> +         [(match_operand:ANYF 1 "register_operand" " f")
> +          (match_operand:ANYF 2 "register_operand" " f")]
> +         QUIET_COMPARISON))]
> +  "TARGET_HARD_FLOAT && TARGET_ZFA"
> +  "f<quiet_pattern>q.<fmt>\t%0,%1,%2"
> +  [(set_attr "type" "fcmp")
> +   (set_attr "mode" "<UNITMODE>")
> +   (set (attr "length") (const_int 16))])
> +
>  (define_insn "*seq_zero_<X:mode><GPR:mode>"
>    [(set (match_operand:GPR       0 "register_operand" "=r")
>         (eq:GPR (match_operand:X 1 "register_operand" " r")
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c
> new file mode 100644
> index 00000000000..26895b76fa4
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c
> @@ -0,0 +1,19 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv32imafdc_zfa -mabi=ilp32d -O2" } */
> +
> +extern void abort(void);
> +extern float a, b;
> +extern double c, d;
> +
> +void
> +foo()
> +{
> +  if ((__builtin_isless(a, b) ||  __builtin_islessequal(c, d))
> +      && (__builtin_islessequal(a, b)|| __builtin_isless(c, d)))
> +    abort();
> +}
> +
> +/* { dg-final { scan-assembler-times "fleq.s" 1 } } */
> +/* { dg-final { scan-assembler-times "fltq.s" 1 } } */
> +/* { dg-final { scan-assembler-times "fleq.d" 1 } } */
> +/* { dg-final { scan-assembler-times "fltq.d" 1 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
> new file mode 100644
> index 00000000000..4ccd6a7dd78
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
> @@ -0,0 +1,19 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv64imafdc_zfa -mabi=lp64d -O2" } */
> +
> +extern void abort(void);
> +extern float a, b;
> +extern double c, d;
> +
> +void
> +foo()
> +{
> +  if ((__builtin_isless(a, b) ||  __builtin_islessequal(c, d))
> +      && (__builtin_islessequal(a, b)|| __builtin_isless(c, d)))
> +    abort();
> +}
> +
> +/* { dg-final { scan-assembler-times "fleq.s" 1 } } */
> +/* { dg-final { scan-assembler-times "fltq.s" 1 } } */
> +/* { dg-final { scan-assembler-times "fleq.d" 1 } } */
> +/* { dg-final { scan-assembler-times "fltq.d" 1 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c
> new file mode 100644
> index 00000000000..c4da04797aa
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c
> @@ -0,0 +1,79 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv32imafdc_zfa -mabi=ilp32d -O0" } */
> +
> +void foo_float32 ()
> +{
> +  volatile float a;
> +  a = -1.0;
> +  a = 1.1754944e-38;
> +  a = 1.0/(1 << 16);
> +  a = 1.0/(1 << 15);
> +  a = 1.0/(1 << 8);
> +  a = 1.0/(1 << 7);
> +  a = 1.0/(1 << 4);
> +  a = 1.0/(1 << 3);
> +  a = 1.0/(1 << 2);
> +  a = 0.3125;
> +  a = 0.375;
> +  a = 0.4375;
> +  a = 0.5;
> +  a = 0.625;
> +  a = 0.75;
> +  a = 0.875;
> +  a = 1.0;
> +  a = 1.25;
> +  a = 1.5;
> +  a = 1.75;
> +  a = 2.0;
> +  a = 2.5;
> +  a = 3.0;
> +  a = 1.0*(1 << 2);
> +  a = 1.0*(1 << 3);
> +  a = 1.0*(1 << 4);
> +  a = 1.0*(1 << 7);
> +  a = 1.0*(1 << 8);
> +  a = 1.0*(1 << 15);
> +  a = 1.0*(1 << 16);
> +  a = __builtin_inff ();
> +  a = __builtin_nanf ("");
> +}
> +
> +void foo_double64 ()
> +{
> +  volatile double a;
> +  a = -1.0;
> +  a = 2.2250738585072014E-308;
> +  a = 1.0/(1 << 16);
> +  a = 1.0/(1 << 15);
> +  a = 1.0/(1 << 8);
> +  a = 1.0/(1 << 7);
> +  a = 1.0/(1 << 4);
> +  a = 1.0/(1 << 3);
> +  a = 1.0/(1 << 2);
> +  a = 0.3125;
> +  a = 0.375;
> +  a = 0.4375;
> +  a = 0.5;
> +  a = 0.625;
> +  a = 0.75;
> +  a = 0.875;
> +  a = 1.0;
> +  a = 1.25;
> +  a = 1.5;
> +  a = 1.75;
> +  a = 2.0;
> +  a = 2.5;
> +  a = 3.0;
> +  a = 1.0*(1 << 2);
> +  a = 1.0*(1 << 3);
> +  a = 1.0*(1 << 4);
> +  a = 1.0*(1 << 7);
> +  a = 1.0*(1 << 8);
> +  a = 1.0*(1 << 15);
> +  a = 1.0*(1 << 16);
> +  a = __builtin_inf ();
> +  a = __builtin_nan ("");
> +}
> +
> +/* { dg-final { scan-assembler-times "fli.s" 32 } } */
> +/* { dg-final { scan-assembler-times "fli.d" 32 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.c
> new file mode 100644
> index 00000000000..bcffe9d2c82
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.c
> @@ -0,0 +1,41 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv32imafdc_zfa_zfh -mabi=ilp32d -O0" } */
> +
> +void foo_float16 ()
> +{
> +  volatile _Float16 a;
> +  a = -1.0;
> +  a = 6.104E-5;
> +  a = 1.0/(1 << 16);
> +  a = 1.0/(1 << 15);
> +  a = 1.0/(1 << 8);
> +  a = 1.0/(1 << 7);
> +  a = 1.0/(1 << 4);
> +  a = 1.0/(1 << 3);
> +  a = 1.0/(1 << 2);
> +  a = 0.3125;
> +  a = 0.375;
> +  a = 0.4375;
> +  a = 0.5;
> +  a = 0.625;
> +  a = 0.75;
> +  a = 0.875;
> +  a = 1.0;
> +  a = 1.25;
> +  a = 1.5;
> +  a = 1.75;
> +  a = 2.0;
> +  a = 2.5;
> +  a = 3.0;
> +  a = 1.0*(1 << 2);
> +  a = 1.0*(1 << 3);
> +  a = 1.0*(1 << 4);
> +  a = 1.0*(1 << 7);
> +  a = 1.0*(1 << 8);
> +  a = 1.0*(1 << 15);
> +  a = 1.0*(1 << 16);
> +  a = __builtin_inff16 ();
> +  a = __builtin_nanf16 ("");
> +}
> +
> +/* { dg-final { scan-assembler-times "fli.h" 32 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
> new file mode 100644
> index 00000000000..13aa7b5f846
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
> @@ -0,0 +1,41 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv64imafdc_zfa_zfh -mabi=lp64d -O0" } */
> +
> +void foo_float16 ()
> +{
> +  volatile _Float16 a;
> +  a = -1.0;
> +  a = 6.104E-5;
> +  a = 1.0/(1 << 16);
> +  a = 1.0/(1 << 15);
> +  a = 1.0/(1 << 8);
> +  a = 1.0/(1 << 7);
> +  a = 1.0/(1 << 4);
> +  a = 1.0/(1 << 3);
> +  a = 1.0/(1 << 2);
> +  a = 0.3125;
> +  a = 0.375;
> +  a = 0.4375;
> +  a = 0.5;
> +  a = 0.625;
> +  a = 0.75;
> +  a = 0.875;
> +  a = 1.0;
> +  a = 1.25;
> +  a = 1.5;
> +  a = 1.75;
> +  a = 2.0;
> +  a = 2.5;
> +  a = 3.0;
> +  a = 1.0*(1 << 2);
> +  a = 1.0*(1 << 3);
> +  a = 1.0*(1 << 4);
> +  a = 1.0*(1 << 7);
> +  a = 1.0*(1 << 8);
> +  a = 1.0*(1 << 15);
> +  a = 1.0*(1 << 16);
> +  a = __builtin_inff16 ();
> +  a = __builtin_nanf16 ("");
> +}
> +
> +/* { dg-final { scan-assembler-times "fli.h" 32 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli.c b/gcc/testsuite/gcc.target/riscv/zfa-fli.c
> new file mode 100644
> index 00000000000..b6d41cf460f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fli.c
> @@ -0,0 +1,79 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv64imafdc_zfa -mabi=lp64d -O0" } */
> +
> +void foo_float32 ()
> +{
> +  volatile float a;
> +  a = -1.0;
> +  a = 1.1754944e-38;
> +  a = 1.0/(1 << 16);
> +  a = 1.0/(1 << 15);
> +  a = 1.0/(1 << 8);
> +  a = 1.0/(1 << 7);
> +  a = 1.0/(1 << 4);
> +  a = 1.0/(1 << 3);
> +  a = 1.0/(1 << 2);
> +  a = 0.3125;
> +  a = 0.375;
> +  a = 0.4375;
> +  a = 0.5;
> +  a = 0.625;
> +  a = 0.75;
> +  a = 0.875;
> +  a = 1.0;
> +  a = 1.25;
> +  a = 1.5;
> +  a = 1.75;
> +  a = 2.0;
> +  a = 2.5;
> +  a = 3.0;
> +  a = 1.0*(1 << 2);
> +  a = 1.0*(1 << 3);
> +  a = 1.0*(1 << 4);
> +  a = 1.0*(1 << 7);
> +  a = 1.0*(1 << 8);
> +  a = 1.0*(1 << 15);
> +  a = 1.0*(1 << 16);
> +  a = __builtin_inff ();
> +  a = __builtin_nanf ("");
> +}
> +
> +void foo_double64 ()
> +{
> +  volatile double a;
> +  a = -1.0;
> +  a = 2.2250738585072014E-308;
> +  a = 1.0/(1 << 16);
> +  a = 1.0/(1 << 15);
> +  a = 1.0/(1 << 8);
> +  a = 1.0/(1 << 7);
> +  a = 1.0/(1 << 4);
> +  a = 1.0/(1 << 3);
> +  a = 1.0/(1 << 2);
> +  a = 0.3125;
> +  a = 0.375;
> +  a = 0.4375;
> +  a = 0.5;
> +  a = 0.625;
> +  a = 0.75;
> +  a = 0.875;
> +  a = 1.0;
> +  a = 1.25;
> +  a = 1.5;
> +  a = 1.75;
> +  a = 2.0;
> +  a = 2.5;
> +  a = 3.0;
> +  a = 1.0*(1 << 2);
> +  a = 1.0*(1 << 3);
> +  a = 1.0*(1 << 4);
> +  a = 1.0*(1 << 7);
> +  a = 1.0*(1 << 8);
> +  a = 1.0*(1 << 15);
> +  a = 1.0*(1 << 16);
> +  a = __builtin_inf ();
> +  a = __builtin_nan ("");
> +}
> +
> +/* { dg-final { scan-assembler-times "fli.s" 32 } } */
> +/* { dg-final { scan-assembler-times "fli.d" 32 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp-rv32.c
> new file mode 100644
> index 00000000000..5a52adce36a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp-rv32.c
> @@ -0,0 +1,10 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv32g_zfa -mabi=ilp32 -O0" } */
> +
> +double foo(long long a)
> +{
> +  return (double)(a + 3);
> +}
> +
> +/* { dg-final { scan-assembler-times "fmvp.d.x" 1 } } */
> +/* { dg-final { scan-assembler-times "fmvh.x.d" 1 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.c
> new file mode 100644
> index 00000000000..b53601d6e1f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.c
> @@ -0,0 +1,42 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv32imafdc_zfa -mabi=ilp32d -O2" } */
> +
> +extern float a;
> +extern double b;
> +
> +void foo (float *x, double *y)
> +{
> +  {
> +    *x = __builtin_roundf (a);
> +    *y = __builtin_round (b);
> +  }
> +  {
> +    *x = __builtin_floorf (a);
> +    *y = __builtin_floor (b);
> +  }
> +  {
> +    *x = __builtin_ceilf (a);
> +    *y = __builtin_ceil (b);
> +  }
> +  {
> +    *x = __builtin_truncf (a);
> +    *y = __builtin_trunc (b);
> +  }
> +  {
> +    *x = __builtin_roundevenf (a);
> +    *y = __builtin_roundeven (b);
> +  }
> +  {
> +    *x = __builtin_nearbyintf (a);
> +    *y = __builtin_nearbyint (b);
> +  }
> +  {
> +    *x = __builtin_rintf (a);
> +    *y = __builtin_rint (b);
> +  }
> +}
> +
> +/* { dg-final { scan-assembler-times "fround.s" 6 } } */
> +/* { dg-final { scan-assembler-times "fround.d" 6 } } */
> +/* { dg-final { scan-assembler-times "froundnx.s" 1 } } */
> +/* { dg-final { scan-assembler-times "froundnx.d" 1 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fround.c b/gcc/testsuite/gcc.target/riscv/zfa-fround.c
> new file mode 100644
> index 00000000000..c10de82578e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fround.c
> @@ -0,0 +1,42 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv64imafdc_zfa -mabi=lp64d -O2" } */
> +
> +extern float a;
> +extern double b;
> +
> +void foo (float *x, double *y)
> +{
> +  {
> +    *x = __builtin_roundf (a);
> +    *y = __builtin_round (b);
> +  }
> +  {
> +    *x = __builtin_floorf (a);
> +    *y = __builtin_floor (b);
> +  }
> +  {
> +    *x = __builtin_ceilf (a);
> +    *y = __builtin_ceil (b);
> +  }
> +  {
> +    *x = __builtin_truncf (a);
> +    *y = __builtin_trunc (b);
> +  }
> +  {
> +    *x = __builtin_roundevenf (a);
> +    *y = __builtin_roundeven (b);
> +  }
> +  {
> +    *x = __builtin_nearbyintf (a);
> +    *y = __builtin_nearbyint (b);
> +  }
> +  {
> +    *x = __builtin_rintf (a);
> +    *y = __builtin_rint (b);
> +  }
> +}
> +
> +/* { dg-final { scan-assembler-times "fround.s" 6 } } */
> +/* { dg-final { scan-assembler-times "fround.d" 6 } } */
> +/* { dg-final { scan-assembler-times "froundnx.s" 1 } } */
> +/* { dg-final { scan-assembler-times "froundnx.d" 1 } } */
> --
> 2.17.1
>
  
jinma April 13, 2023, 11:15 a.m. UTC | #3
Thank you very much for your comments. Since a long time has passed and this is an initial version, I will update this patch.
------------------------------------------------------------------
From:Christoph Müllner <christoph.muellner@vrull.eu>
Sent At:2023 Apr. 13 (Thu.) 17:22
To:Jin Ma <jinma@linux.alibaba.com>
Cc:gcc-patches <gcc-patches@gcc.gnu.org>; kito.cheng <kito.cheng@sifive.com>; kito.cheng <kito.cheng@gmail.com>; palmer <palmer@dabbelt.com>
Subject:Re: [PATCH v6] RISC-V: Add support for experimental zfa extension.
On Fri, Mar 10, 2023 at 1:41 PM Jin Ma via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> This patch adds the 'Zfa' extension for riscv, which is based on:
> https://github.com/riscv/riscv-isa-manual/commit/d74d99e22d5f68832f70982d867614e2149a3bd7
> latest 'Zfa' change on the master branch of the RISC-V ISA Manual as
> of this writing.
>
> The Wiki Page (details):
> https://github.com/a4lg/binutils-gdb/wiki/riscv_zfa
>
> The binutils-gdb for 'Zfa' extension:
> https://sourceware.org/pipermail/binutils/2022-September/122938.html
>
> Implementation of zfa extension on LLVM:
> https://reviews.llvm.org/rGc0947dc44109252fcc0f68a542fc6ef250d4d3a9
>
> There are three points that need to be discussed here.
> 1. According to riscv-spec, "The FCVTMO D.W.D instruction was added principally to
> accelerate the processing of JavaScript Numbers.", so it seems that no implementation
> is required in the compiler.
> 2. The FROUND and FROUNDN instructions in this patch use related functions in the math
> library, such as round, floor, ceil, etc. Since there is no interface for half-precision in
> the math library, the instructions FROUN D.H and FROUNDN X.H have not been implemented for
> the time being. Is it necessary to add a built-in interface belonging to riscv such as
> __builtin_roundhf or __builtin_roundf16 to generate half floating point instructions?
> 3. As far as I know, FMINM and FMAXM instructions correspond to C23 library function fminimum
> and fmaximum. Therefore, I have not dealt with such instructions for the time being, but have
> simply implemented the pattern of fminm<hf\sf\df>3 and fmaxm<hf\sf\df>3. Is it necessary to
> add a built-in interface belonging to riscv such as__builtin_fminm to generate half
> floating-point instructions?
I have rebased and tested this patch.
Here are my observations (with fixes below at the actual code):
* There is a compiler warning because of a missing "fallthrough" comment
* There are merge conflicts with a current master
* The constant operand of the fli instruction uses the constant index
in the rs1-field, but not the constant in hex FP literal form
A patch that addresses these issues can also be found here:
 https://github.com/cmuellner/gcc/tree/riscv-zfa
Additionally I observe the following failing test cases with this patch applied:
 === gcc: Unexpected fails for rv64gc lp64d medlow ===
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O0 (internal compiler
error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O0 (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O1 (internal compiler
error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O1 (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O2 (internal compiler
error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O2 (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O2 -flto
-fno-use-linker-plugin -flto-partition=none (internal compiler error:
Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O2 -flto
-fno-use-linker-plugin -flto-partition=none (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O2 -flto
-fuse-linker-plugin -fno-fat-lto-objects (internal compiler error:
Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O2 -flto
-fuse-linker-plugin -fno-fat-lto-objects (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O3 -g (internal
compiler error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -O3 -g (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -Os (internal compiler
error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -Os (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -Og -g (internal
compiler error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -Og -g (test for excess errors)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -Oz (internal compiler
error: Segmentation fault)
FAIL: gcc.target/riscv/zero-scratch-regs-3.c -Oz (test for excess errors)
I have not analysed these ICEs so far.
>
> gcc/ChangeLog:
>
> * common/config/riscv/riscv-common.cc: Add zfa extension.
> * config/riscv/constraints.md (Zf): Constrain the floating point number that the FLI instruction can load.
> * config/riscv/iterators.md (round_pattern): New.
> * config/riscv/predicates.md: Predicate the floating point number that the FLI instruction can load.
> * config/riscv/riscv-opts.h (MASK_ZFA): New.
> (TARGET_ZFA): New.
> * config/riscv/riscv-protos.h (riscv_float_const_rtx_index_for_fli): Get the index of the
> floating-point number that the FLI instruction can load.
> * config/riscv/riscv.cc (find_index_in_array): New.
> (riscv_float_const_rtx_index_for_fli): New.
> (riscv_cannot_force_const_mem): Likewise.
> (riscv_const_insns): Likewise.
> (riscv_legitimize_const_move): Likewise.
> (riscv_split_64bit_move_p): Exclude floating point numbers that can be loaded by FLI instructions.
> (riscv_output_move): Likewise.
> (riscv_memmodel_needs_release_fence): Likewise.
> (riscv_print_operand): Likewise.
> (riscv_secondary_memory_needed): Likewise.
> * config/riscv/riscv.h (GP_REG_RTX_P): New.
> * config/riscv/riscv.md (fminm<mode>3): New.
> (fmaxm<mode>3): New.
> (<round_pattern><ANYF:mode>2): New.
> (rint<ANYF:mode>2): New.
> (f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa): New.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.target/riscv/zfa-fleq-fltq-rv32.c: New test.
> * gcc.target/riscv/zfa-fleq-fltq.c: New test.
> * gcc.target/riscv/zfa-fli-rv32.c: New test.
> * gcc.target/riscv/zfa-fli-zfh-rv32.c: New test.
> * gcc.target/riscv/zfa-fli-zfh.c: New test.
> * gcc.target/riscv/zfa-fli.c: New test.
> * gcc.target/riscv/zfa-fmovh-fmovp-rv32.c: New test.
> * gcc.target/riscv/zfa-fround-rv32.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/iterators.md | 5 +
> gcc/config/riscv/predicates.md | 4 +
> gcc/config/riscv/riscv-opts.h | 3 +
> gcc/config/riscv/riscv-protos.h | 1 +
> gcc/config/riscv/riscv.cc | 168 +++++++++++++++++-
> gcc/config/riscv/riscv.h | 1 +
> gcc/config/riscv/riscv.md | 112 +++++++++---
> .../gcc.target/riscv/zfa-fleq-fltq-rv32.c | 19 ++
> .../gcc.target/riscv/zfa-fleq-fltq.c | 19 ++
> gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c | 79 ++++++++
> .../gcc.target/riscv/zfa-fli-zfh-rv32.c | 41 +++++
> gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c | 41 +++++
> gcc/testsuite/gcc.target/riscv/zfa-fli.c | 79 ++++++++
> .../gcc.target/riscv/zfa-fmovh-fmovp-rv32.c | 10 ++
> .../gcc.target/riscv/zfa-fround-rv32.c | 42 +++++
> gcc/testsuite/gcc.target/riscv/zfa-fround.c | 42 +++++
> 18 files changed, 654 insertions(+), 23 deletions(-)
> create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c
> create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
> create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c
> create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.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-fmovh-fmovp-rv32.c
> create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.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 ebc1ed7d7e4..8fec3bc71c9 100644
> --- a/gcc/common/config/riscv/riscv-common.cc
> +++ b/gcc/common/config/riscv/riscv-common.cc
> @@ -217,6 +217,8 @@ static const struct riscv_ext_version riscv_ext_version_table[] =
> {"zfh", ISA_SPEC_CLASS_NONE, 1, 0},
> {"zfhmin", ISA_SPEC_CLASS_NONE, 1, 0},
>
> + {"zfa", ISA_SPEC_CLASS_NONE, 0, 1},
> +
> {"zmmul", ISA_SPEC_CLASS_NONE, 1, 0},
>
> {"svinval", ISA_SPEC_CLASS_NONE, 1, 0},
> @@ -1243,6 +1245,8 @@ static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
> {"zfhmin", &gcc_options::x_riscv_zf_subext, MASK_ZFHMIN},
> {"zfh", &gcc_options::x_riscv_zf_subext, MASK_ZFH},
>
> + {"zfa", &gcc_options::x_riscv_zf_subext, MASK_ZFA},
> +
> {"zmmul", &gcc_options::x_riscv_zm_subext, MASK_ZMMUL},
>
> {"svinval", &gcc_options::x_riscv_sv_subext, MASK_SVINVAL},
> diff --git a/gcc/config/riscv/constraints.md b/gcc/config/riscv/constraints.md
> index fdfcf2380f8..2ba0160934a 100644
> --- a/gcc/config/riscv/constraints.md
> +++ b/gcc/config/riscv/constraints.md
> @@ -118,6 +118,13 @@ (define_constraint "T"
> (and (match_operand 0 "move_operand")
> (match_test "CONSTANT_P (op)")))
>
> +;; Zfa constraints.
> +
> +(define_constraint "Zf"
> + "A floating point number that can be loaded using instruction `fli` in zfa."
> + (and (match_code "const_double")
> + (match_test "(riscv_float_const_rtx_index_for_fli (op) != -1)")))
> +
> ;; Vector constraints.
>
> (define_register_constraint "vr" "TARGET_VECTOR ? V_REGS : NO_REGS"
> diff --git a/gcc/config/riscv/iterators.md b/gcc/config/riscv/iterators.md
> index 5b70ab20758..ef9b1aa9ed3 100644
> --- a/gcc/config/riscv/iterators.md
> +++ b/gcc/config/riscv/iterators.md
> @@ -284,3 +284,8 @@ (define_int_iterator QUIET_COMPARISON [UNSPEC_FLT_QUIET UNSPEC_FLE_QUIET])
> (define_int_attr quiet_pattern [(UNSPEC_FLT_QUIET "lt") (UNSPEC_FLE_QUIET "le")])
> (define_int_attr QUIET_PATTERN [(UNSPEC_FLT_QUIET "LT") (UNSPEC_FLE_QUIET "LE")])
>
> +(define_int_iterator ROUND [UNSPEC_ROUND UNSPEC_FLOOR UNSPEC_CEIL UNSPEC_BTRUNC UNSPEC_ROUNDEVEN UNSPEC_NEARBYINT])
> +(define_int_attr round_pattern [(UNSPEC_ROUND "round") (UNSPEC_FLOOR "floor") (UNSPEC_CEIL "ceil")
> + (UNSPEC_BTRUNC "btrunc") (UNSPEC_ROUNDEVEN "roundeven") (UNSPEC_NEARBYINT "nearbyint")])
> +(define_int_attr round_rm [(UNSPEC_ROUND "rmm") (UNSPEC_FLOOR "rdn") (UNSPEC_CEIL "rup")
> + (UNSPEC_BTRUNC "rtz") (UNSPEC_ROUNDEVEN "rne") (UNSPEC_NEARBYINT "dyn")])
> \ No newline at end of file
> diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md
> index 0d9d7701c7e..b16b8d438e3 100644
> --- a/gcc/config/riscv/predicates.md
> +++ b/gcc/config/riscv/predicates.md
> @@ -149,6 +149,10 @@ (define_predicate "move_operand"
> case CONST_POLY_INT:
> return known_eq (rtx_to_poly_int64 (op), BYTES_PER_RISCV_VECTOR);
>
> + case CONST_DOUBLE:
> + return const_0_operand (op, mode)
> + || (riscv_float_const_rtx_index_for_fli (op) != -1);
> +
> case CONST:
> case SYMBOL_REF:
> case LABEL_REF:
> diff --git a/gcc/config/riscv/riscv-opts.h b/gcc/config/riscv/riscv-opts.h
> index ff398c0a2ae..3ae729eec30 100644
> --- a/gcc/config/riscv/riscv-opts.h
> +++ b/gcc/config/riscv/riscv-opts.h
> @@ -172,6 +172,9 @@ enum stack_protector_guard {
> #define TARGET_ZFHMIN ((riscv_zf_subext & MASK_ZFHMIN) != 0)
> #define TARGET_ZFH ((riscv_zf_subext & MASK_ZFH) != 0)
>
> +#define MASK_ZFA (1 << 0)
> +#define TARGET_ZFA ((riscv_zf_subext & MASK_ZFA) != 0)
> +
> #define MASK_ZMMUL (1 << 0)
> #define TARGET_ZMMUL ((riscv_zm_subext & MASK_ZMMUL) != 0)
>
> diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
> index f35aaf35b48..0f5dcba8294 100644
> --- a/gcc/config/riscv/riscv-protos.h
> +++ b/gcc/config/riscv/riscv-protos.h
> @@ -38,6 +38,7 @@ enum riscv_symbol_type {
> /* Routines implemented in riscv.cc. */
> extern enum riscv_symbol_type riscv_classify_symbolic_expression (rtx);
> extern bool riscv_symbolic_constant_p (rtx, enum riscv_symbol_type *);
> +extern int riscv_float_const_rtx_index_for_fli (rtx);
> extern int riscv_regno_mode_ok_for_base_p (int, machine_mode, bool);
> extern int riscv_address_insns (rtx, machine_mode, bool);
> extern int riscv_const_insns (rtx);
> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
> index c91fa3101aa..c81a2bf44f5 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -799,6 +799,108 @@ static int riscv_symbol_insns (enum riscv_symbol_type type)
> }
> }
>
> +/* Immediate values loaded by the FLI.S instruction in Chapter 25 of the latest RISC-V ISA
> + Manual draft. For details, please see:
> + https://github.com/riscv/riscv-isa-manual/releases/tag/draft-20221217-cb3b9d1 */
> +
> +unsigned HOST_WIDE_INT fli_value_hf[32] =
> +{
> + 0xbc00, 0x400, 0x100, 0x200, 0x1c00, 0x2000, 0x2c00, 0x3000,
> + 0x3400, 0x3500, 0x3600, 0x3700, 0x3800, 0x3900, 0x3a00, 0x3b00,
> + 0x3c00, 0x3d00, 0x3e00, 0x3f00, 0x4000, 0x4100, 0x4200, 0x4400,
> + 0x4800, 0x4c00, 0x5800, 0x5c00, 0x7800,
> + /* Only used for filling, ensuring that 29 and 30 of HF are the same. */
> + 0x7800,
> + 0x7c00, 0x7e00,
> +};
> +
> +unsigned HOST_WIDE_INT fli_value_sf[32] =
> +{
> + 0xbf800000, 0x00800000, 0x37800000, 0x38000000, 0x3b800000, 0x3c000000, 0x3d800000, 0x3e000000,
> + 0x3e800000, 0x3ea00000, 0x3ec00000, 0x3ee00000, 0x3f000000, 0x3f200000, 0x3f400000, 0x3f600000,
> + 0x3f800000, 0x3fa00000, 0x3fc00000, 0x3fe00000, 0x40000000, 0x40200000, 0x40400000, 0x40800000,
> + 0x41000000, 0x41800000, 0x43000000, 0x43800000, 0x47000000, 0x47800000, 0x7f800000, 0x7fc00000
> +};
> +
> +unsigned HOST_WIDE_INT fli_value_df[32] =
> +{
> + 0xbff0000000000000, 0x10000000000000, 0x3ef0000000000000, 0x3f00000000000000,
> + 0x3f70000000000000, 0x3f80000000000000, 0x3fb0000000000000, 0x3fc0000000000000,
> + 0x3fd0000000000000, 0x3fd4000000000000, 0x3fd8000000000000, 0x3fdc000000000000,
> + 0x3fe0000000000000, 0x3fe4000000000000, 0x3fe8000000000000, 0x3fec000000000000,
> + 0x3ff0000000000000, 0x3ff4000000000000, 0x3ff8000000000000, 0x3ffc000000000000,
> + 0x4000000000000000, 0x4004000000000000, 0x4008000000000000, 0x4010000000000000,
> + 0x4020000000000000, 0x4030000000000000, 0x4060000000000000, 0x4070000000000000,
> + 0x40e0000000000000, 0x40f0000000000000, 0x7ff0000000000000, 0x7ff8000000000000,
> +};
> +
> +/* Find the index of TARGET in ARRAY, and return -1 if not found. */
> +
> +static int
> +find_index_in_array (unsigned HOST_WIDE_INT target, unsigned HOST_WIDE_INT *array, int len)
> +{
> + if (array == NULL)
> + return -1;
> +
> + for (int i = 0; i < len; i++)
> + {
> + if (target == array[i])
> + return i;
> + }
> + return -1;
> +}
> +
> +/* Return index of the FLI instruction table if rtx X is an immediate constant that
> + can be moved using a single FLI instruction in zfa extension. -1 otherwise. */
> +
> +int
> +riscv_float_const_rtx_index_for_fli (rtx x)
> +{
> + machine_mode mode = GET_MODE (x);
> +
> + if (!TARGET_ZFA || mode == VOIDmode
> + || !CONST_DOUBLE_P(x)
> + || (mode == HFmode && !TARGET_ZFH)
> + || (mode == SFmode && !TARGET_HARD_FLOAT)
> + || (mode == DFmode && !TARGET_DOUBLE_FLOAT))
> + return -1;
> +
> + if (!SCALAR_FLOAT_MODE_P (mode)
> + || GET_MODE_BITSIZE (mode).to_constant () > HOST_BITS_PER_WIDE_INT
> + /* Only support up to DF mode. */
> + || GET_MODE_BITSIZE (mode).to_constant () > GET_MODE_BITSIZE (DFmode))
> + return -1;
> +
> + unsigned HOST_WIDE_INT ival = 0;
> +
> + long res[2];
> + real_to_target (res,
> + CONST_DOUBLE_REAL_VALUE (x),
> + REAL_MODE_FORMAT (mode));
> +
> + if (mode == DFmode)
> + {
> + int order = BYTES_BIG_ENDIAN ? 1 : 0;
> + ival = zext_hwi (res[order], 32);
> + ival |= (zext_hwi (res[1 - order], 32) << 32);
> + }
> + else
> + ival = zext_hwi (res[0], 32);
> +
> + switch (mode)
> + {
> + case SFmode:
> + return find_index_in_array (ival, fli_value_sf, 32);
> + case DFmode:
> + return find_index_in_array (ival, fli_value_df, 32);
> + case HFmode:
> + return find_index_in_array (ival, fli_value_hf, 32);
> + default:
> + break;
> + }
> + return -1;
> +}
> +
> /* Implement TARGET_LEGITIMATE_CONSTANT_P. */
>
> static bool
> @@ -826,6 +928,9 @@ riscv_cannot_force_const_mem (machine_mode mode ATTRIBUTE_UNUSED, rtx x)
> if (GET_CODE (x) == HIGH)
> return true;
>
> + if (riscv_float_const_rtx_index_for_fli (x) != -1)
> + return true;
> +
> split_const (x, &base, &offset);
> if (riscv_symbolic_constant_p (base, &type))
> {
> @@ -1191,6 +1296,8 @@ riscv_const_insns (rtx x)
> }
>
> case CONST_DOUBLE:
> + if (riscv_float_const_rtx_index_for_fli (x) != -1)
> + return 4;
Here a "/* Fall through. */" is missing to avoid a compiler warning.
> case CONST_VECTOR:
> /* We can use x0 to load floating-point zero. */
> return x == CONST0_RTX (GET_MODE (x)) ? 1 : 0;
> @@ -1727,6 +1834,12 @@ riscv_legitimize_const_move (machine_mode mode, rtx dest, rtx src)
> return;
> }
>
> + if (riscv_float_const_rtx_index_for_fli (src) != -1)
> + {
> + riscv_emit_set (dest, src);
> + return;
> + }
> +
> /* Split moves of symbolic constants into high/low pairs. */
> if (riscv_split_symbol (dest, src, MAX_MACHINE_MODE, &src, FALSE))
> {
> @@ -2739,12 +2852,19 @@ riscv_split_64bit_move_p (rtx dest, rtx src)
> if (TARGET_64BIT)
> return false;
>
> + /* There is no need to split if the FLI instruction in the `Zfa` extension can be used. */
> + if (TARGET_ZFA && (riscv_float_const_rtx_index_for_fli (src) != -1))
> + return false;
> +
> /* Allow FPR <-> FPR and FPR <-> MEM moves, and permit the special case
> of zeroing an FPR with FCVT.D.W. */
> if (TARGET_DOUBLE_FLOAT
> && ((FP_REG_RTX_P (src) && FP_REG_RTX_P (dest))
> || (FP_REG_RTX_P (dest) && MEM_P (src))
> || (FP_REG_RTX_P (src) && MEM_P (dest))
> + || (TARGET_ZFA
> + && ((FP_REG_RTX_P (dest) && GP_REG_RTX_P (src))
> + || (FP_REG_RTX_P (src) && GP_REG_RTX_P (dest))))
> || (FP_REG_RTX_P (dest) && src == CONST0_RTX (GET_MODE (src)))))
> return false;
>
> @@ -2808,6 +2928,8 @@ riscv_output_move (rtx dest, rtx src)
> case 4:
> return "fmv.x.s\t%0,%1";
> case 8:
> + if (!TARGET_64BIT && TARGET_ZFA)
> + return "fmv.x.w\t%0,%1\n\tfmvh.x.d\t%N0,%1";
> return "fmv.x.d\t%0,%1";
> }
>
> @@ -2867,6 +2989,8 @@ riscv_output_move (rtx dest, rtx src)
> case 8:
> if (TARGET_64BIT)
> return "fmv.d.x\t%0,%z1";
> + else if (TARGET_ZFA && src != CONST0_RTX (mode))
> + return "fmvp.d.x\t%0,%1,%N1";
> /* in RV32, we can emulate fmv.d.x %0, x0 using fcvt.d.w */
> gcc_assert (src == CONST0_RTX (mode));
> return "fcvt.d.w\t%0,x0";
> @@ -2919,6 +3043,14 @@ riscv_output_move (rtx dest, rtx src)
> case 8:
> return "fld\t%0,%1";
> }
> +
> + if (src_code == CONST_DOUBLE && (riscv_float_const_rtx_index_for_fli (src) != -1))
> + switch (width)
> + {
> + case 2: return "fli.h\t%0,%1";
> + case 4: return "fli.s\t%0,%1";
> + case 8: return "fli.d\t%0,%1";
> + }
> }
> if (dest_code == REG && GP_REG_P (REGNO (dest)) && src_code == CONST_POLY_INT)
> {
> @@ -4222,6 +4354,7 @@ riscv_memmodel_needs_release_fence (enum memmodel model)
> 'S' Print shift-index of single-bit mask OP.
> 'T' Print shift-index of inverted single-bit mask OP.
> '~' Print w if TARGET_64BIT is true; otherwise not print anything.
> + 'N' Print next register.
>
> Note please keep this list and the list in riscv.md in sync. */
>
> @@ -4406,6 +4539,9 @@ riscv_print_operand (FILE *file, rtx op, int letter)
> output_addr_const (file, newop);
> break;
> }
> + case 'N':
> + fputs (reg_names[REGNO (op) + 1], file);
> + break;
> default:
> switch (code)
> {
> @@ -4422,6 +4558,35 @@ riscv_print_operand (FILE *file, rtx op, int letter)
> output_address (mode, XEXP (op, 0));
> break;
>
> + case CONST_DOUBLE:
> + {
> + if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
> + {
> + fputs (reg_names[GP_REG_FIRST], file);
> + break;
> + }
> +
> + int fli_index = riscv_float_const_rtx_index_for_fli (op);
> + if (fli_index == -1 || fli_index > 31)
> + {
> + output_operand_lossage ("invalid use of '%%%c'", letter);
> + break;
> + }
> +
> + switch (fli_index)
> + {
> + case 1:
> + asm_fprintf (file, "%s", "min");break;
> + case 30:
> + asm_fprintf (file, "%s", "inf");break;
> + case 31:
> + asm_fprintf (file, "%s", "nan");break;
> + default:
> + asm_fprintf (file, "%d", fli_index);break;
This prints the constant-index, but not the constant itself.
We discussed this on the Binutils patch and agreed that we will use
hex floating-point literals.
Something like this should work:
 default:
 {
 char s[60];
 real_to_hexadecimal (s, CONST_DOUBLE_REAL_VALUE (op),
 sizeof (s), 0, 1);
 asm_fprintf (file, "%s", s);
 break;
 }
So, since we know the value is a valid constant, we can just print it.
> + }
> + break;
> + }
> +
> default:
> if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
> fputs (reg_names[GP_REG_FIRST], file);
> @@ -5734,7 +5899,8 @@ riscv_secondary_memory_needed (machine_mode mode, reg_class_t class1,
> {
> return (!riscv_v_ext_vector_mode_p (mode)
> && GET_MODE_SIZE (mode).to_constant () > UNITS_PER_WORD
> - && (class1 == FP_REGS) != (class2 == FP_REGS));
> + && (class1 == FP_REGS) != (class2 == FP_REGS)
> + && !TARGET_ZFA);
Does not apply on master anymore.
New code:
 && !TARGET_XTHEADFMV && !TARGET_ZFA);
> }
>
> /* Implement TARGET_REGISTER_MOVE_COST. */
> diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
> index 15b9317a8ce..f6ab9c75317 100644
> --- a/gcc/config/riscv/riscv.h
> +++ b/gcc/config/riscv/riscv.h
> @@ -377,6 +377,7 @@ ASM_MISA_SPEC
> #define SIBCALL_REG_P(REGNO) \
> TEST_HARD_REG_BIT (reg_class_contents[SIBCALL_REGS], REGNO)
>
> +#define GP_REG_RTX_P(X) (REG_P (X) && GP_REG_P (REGNO (X)))
> #define FP_REG_RTX_P(X) (REG_P (X) && FP_REG_P (REGNO (X)))
>
> /* Use s0 as the frame pointer if it is so requested. */
> diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
> index 371d6838c0b..4fa2896b8e8 100644
> --- a/gcc/config/riscv/riscv.md
> +++ b/gcc/config/riscv/riscv.md
> @@ -59,6 +59,15 @@ (define_c_enum "unspec" [
> UNSPEC_LROUND
> UNSPEC_FMIN
> UNSPEC_FMAX
> + UNSPEC_RINT
> + UNSPEC_ROUND
> + UNSPEC_FLOOR
> + UNSPEC_CEIL
> + UNSPEC_BTRUNC
> + UNSPEC_ROUNDEVEN
> + UNSPEC_NEARBYINT
> + UNSPEC_FMINM
> + UNSPEC_FMAXM
>
> ;; Stack tie
> UNSPEC_TIE
> @@ -1227,6 +1236,26 @@ (define_insn "neg<mode>2"
> ;;
> ;; ....................
>
> +(define_insn "fminm<mode>3"
> + [(set (match_operand:ANYF 0 "register_operand" "=f")
> + (unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" " f"))
> + (use (match_operand:ANYF 2 "register_operand" " f"))]
> + UNSPEC_FMINM))]
> + "TARGET_HARD_FLOAT && TARGET_ZFA"
> + "fminm.<fmt>\t%0,%1,%2"
> + [(set_attr "type" "fmove")
> + (set_attr "mode" "<UNITMODE>")])
> +
> +(define_insn "fmaxm<mode>3"
> + [(set (match_operand:ANYF 0 "register_operand" "=f")
> + (unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" " f"))
> + (use (match_operand:ANYF 2 "register_operand" " f"))]
> + UNSPEC_FMAXM))]
> + "TARGET_HARD_FLOAT && TARGET_ZFA"
> + "fmaxm.<fmt>\t%0,%1,%2"
> + [(set_attr "type" "fmove")
> + (set_attr "mode" "<UNITMODE>")])
> +
> (define_insn "fmin<mode>3"
> [(set (match_operand:ANYF 0 "register_operand" "=f")
> (unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" " f"))
> @@ -1503,13 +1532,13 @@ (define_expand "movhf"
> })
>
> (define_insn "*movhf_hardfloat"
> - [(set (match_operand:HF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*r, *r,*r,*m")
> - (match_operand:HF 1 "move_operand" " f,G,m,f,G,*r,*f,*G*r,*m,*r"))]
> + [(set (match_operand:HF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r, *r,*r,*m")
> + (match_operand:HF 1 "move_operand" " f,Zf,G,m,f,G,*r,*f,*G*r,*m,*r"))]
> "TARGET_ZFHMIN
> && (register_operand (operands[0], HFmode)
> || reg_or_0_operand (operands[1], HFmode))"
> { return riscv_output_move (operands[0], operands[1]); }
> - [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
> + [(set_attr "move_type" "fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
> (set_attr "mode" "HF")])
>
> (define_insn "*movhf_softfloat"
> @@ -1575,6 +1604,26 @@ (define_insn "l<rint_pattern><ANYF:mode><GPR:mode>2"
> [(set_attr "type" "fcvt")
> (set_attr "mode" "<ANYF:MODE>")])
>
> +(define_insn "<round_pattern><ANYF:mode>2"
> + [(set (match_operand:ANYF 0 "register_operand" "=f")
> + (unspec:ANYF
> + [(match_operand:ANYF 1 "register_operand" " f")]
> + ROUND))]
> + "TARGET_HARD_FLOAT && TARGET_ZFA"
> + "fround.<ANYF:fmt>\t%0,%1,<round_rm>"
> + [(set_attr "type" "fcvt")
> + (set_attr "mode" "<ANYF:MODE>")])
> +
> +(define_insn "rint<ANYF:mode>2"
> + [(set (match_operand:ANYF 0 "register_operand" "=f")
> + (unspec:ANYF
> + [(match_operand:ANYF 1 "register_operand" " f")]
> + UNSPEC_RINT))]
> + "TARGET_HARD_FLOAT && TARGET_ZFA"
> + "froundnx.<ANYF:fmt>\t%0,%1"
> + [(set_attr "type" "fcvt")
> + (set_attr "mode" "<ANYF:MODE>")])
> +
> ;;
> ;; ....................
> ;;
> @@ -1834,13 +1883,13 @@ (define_expand "movsf"
> })
>
> (define_insn "*movsf_hardfloat"
> - [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*r, *r,*r,*m")
> - (match_operand:SF 1 "move_operand" " f,G,m,f,G,*r,*f,*G*r,*m,*r"))]
> + [(set (match_operand:SF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r, *r,*r,*m")
> + (match_operand:SF 1 "move_operand" " f,Zf,G,m,f,G,*r,*f,*G*r,*m,*r"))]
> "TARGET_HARD_FLOAT
> && (register_operand (operands[0], SFmode)
> || reg_or_0_operand (operands[1], SFmode))"
> { return riscv_output_move (operands[0], operands[1]); }
> - [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
> + [(set_attr "move_type" "fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
> (set_attr "mode" "SF")])
>
> (define_insn "*movsf_softfloat"
> @@ -1867,23 +1916,23 @@ (define_expand "movdf"
> ;; In RV32, we lack fmv.x.d and fmv.d.x. Go through memory instead.
> ;; (However, we can still use fcvt.d.w to zero a floating-point register.)
> (define_insn "*movdf_hardfloat_rv32"
> - [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,m,m, *r,*r,*m")
> - (match_operand:DF 1 "move_operand" " f,G,m,f,G,*r*G,*m,*r"))]
> + [(set (match_operand:DF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r, *r,*r,*m")
> + (match_operand:DF 1 "move_operand" " f,Zf,G,m,f,G,*r,*f,*r*G,*m,*r"))]
Does not apply on master anymore, needs:
 [(set (match_operand:DF 0 "nonimmediate_operand"
"=f,f,f,f,m,m,*f,*r,*th_f_fmv,*th_r_fmv, *r,*r,*m")
 (match_operand:DF 1 "move_operand" "
f,Zf,G,m,f,G,*r,*f,*th_r_fmv,*th_f_fmv,*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")
Does not apply on master anymore, needs:
 [(set_attr "move_type"
"fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,mtc,mfc,move,load,store")
> (set_attr "mode" "DF")])
>
> (define_insn "*movdf_hardfloat_rv64"
> - [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*r, *r,*r,*m")
> - (match_operand:DF 1 "move_operand" " f,G,m,f,G,*r,*f,*r*G,*m,*r"))]
> + [(set (match_operand:DF 0 "nonimmediate_operand" "=f, f,f,f,m,m,*f,*r, *r,*r,*m")
> + (match_operand:DF 1 "move_operand" " f,Zf,G,m,f,G,*r,*f,*r*G,*m,*r"))]
> "TARGET_64BIT && TARGET_DOUBLE_FLOAT
> && (register_operand (operands[0], DFmode)
> || reg_or_0_operand (operands[1], DFmode))"
> { return riscv_output_move (operands[0], operands[1]); }
> - [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
> + [(set_attr "move_type" "fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
> (set_attr "mode" "DF")])
>
> (define_insn "*movdf_softfloat"
> @@ -2490,16 +2539,23 @@ (define_expand "f<quiet_pattern>_quiet<ANYF:mode><X:mode>4"
> rtx op0 = operands[0];
> rtx op1 = operands[1];
> rtx op2 = operands[2];
> - rtx tmp = gen_reg_rtx (SImode);
> - rtx cmp = gen_rtx_<QUIET_PATTERN> (<X:MODE>mode, op1, op2);
> - rtx frflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, const0_rtx),
> - UNSPECV_FRFLAGS);
> - rtx fsflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, tmp),
> - UNSPECV_FSFLAGS);
> -
> - emit_insn (gen_rtx_SET (tmp, frflags));
> - emit_insn (gen_rtx_SET (op0, cmp));
> - emit_insn (fsflags);
> +
> + if (TARGET_ZFA)
> + emit_insn (gen_f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa(op0, op1, op2));
> + else
> + {
> + rtx tmp = gen_reg_rtx (SImode);
> + rtx cmp = gen_rtx_<QUIET_PATTERN> (<X:MODE>mode, op1, op2);
> + rtx frflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, const0_rtx),
> + UNSPECV_FRFLAGS);
> + rtx fsflags = gen_rtx_UNSPEC_VOLATILE (SImode, gen_rtvec (1, tmp),
> + UNSPECV_FSFLAGS);
> +
> + emit_insn (gen_rtx_SET (tmp, frflags));
> + emit_insn (gen_rtx_SET (op0, cmp));
> + emit_insn (fsflags);
> + }
> +
> if (HONOR_SNANS (<ANYF:MODE>mode))
> emit_insn (gen_rtx_UNSPEC_VOLATILE (<ANYF:MODE>mode,
> gen_rtvec (2, op1, op2),
> @@ -2507,6 +2563,18 @@ (define_expand "f<quiet_pattern>_quiet<ANYF:mode><X:mode>4"
> DONE;
> })
>
> +(define_insn "f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa"
> + [(set (match_operand:X 0 "register_operand" "=r")
> + (unspec:X
> + [(match_operand:ANYF 1 "register_operand" " f")
> + (match_operand:ANYF 2 "register_operand" " f")]
> + QUIET_COMPARISON))]
> + "TARGET_HARD_FLOAT && TARGET_ZFA"
> + "f<quiet_pattern>q.<fmt>\t%0,%1,%2"
> + [(set_attr "type" "fcmp")
> + (set_attr "mode" "<UNITMODE>")
> + (set (attr "length") (const_int 16))])
> +
> (define_insn "*seq_zero_<X:mode><GPR:mode>"
> [(set (match_operand:GPR 0 "register_operand" "=r")
> (eq:GPR (match_operand:X 1 "register_operand" " r")
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c
> new file mode 100644
> index 00000000000..26895b76fa4
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c
> @@ -0,0 +1,19 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv32imafdc_zfa -mabi=ilp32d -O2" } */
> +
> +extern void abort(void);
> +extern float a, b;
> +extern double c, d;
> +
> +void
> +foo()
> +{
> + if ((__builtin_isless(a, b) || __builtin_islessequal(c, d))
> + && (__builtin_islessequal(a, b)|| __builtin_isless(c, d)))
> + abort();
> +}
> +
> +/* { dg-final { scan-assembler-times "fleq.s" 1 } } */
> +/* { dg-final { scan-assembler-times "fltq.s" 1 } } */
> +/* { dg-final { scan-assembler-times "fleq.d" 1 } } */
> +/* { dg-final { scan-assembler-times "fltq.d" 1 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
> new file mode 100644
> index 00000000000..4ccd6a7dd78
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
> @@ -0,0 +1,19 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv64imafdc_zfa -mabi=lp64d -O2" } */
> +
> +extern void abort(void);
> +extern float a, b;
> +extern double c, d;
> +
> +void
> +foo()
> +{
> + if ((__builtin_isless(a, b) || __builtin_islessequal(c, d))
> + && (__builtin_islessequal(a, b)|| __builtin_isless(c, d)))
> + abort();
> +}
> +
> +/* { dg-final { scan-assembler-times "fleq.s" 1 } } */
> +/* { dg-final { scan-assembler-times "fltq.s" 1 } } */
> +/* { dg-final { scan-assembler-times "fleq.d" 1 } } */
> +/* { dg-final { scan-assembler-times "fltq.d" 1 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c
> new file mode 100644
> index 00000000000..c4da04797aa
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c
> @@ -0,0 +1,79 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv32imafdc_zfa -mabi=ilp32d -O0" } */
> +
> +void foo_float32 ()
> +{
> + volatile float a;
> + a = -1.0;
> + a = 1.1754944e-38;
> + a = 1.0/(1 << 16);
> + a = 1.0/(1 << 15);
> + a = 1.0/(1 << 8);
> + a = 1.0/(1 << 7);
> + a = 1.0/(1 << 4);
> + a = 1.0/(1 << 3);
> + a = 1.0/(1 << 2);
> + a = 0.3125;
> + a = 0.375;
> + a = 0.4375;
> + a = 0.5;
> + a = 0.625;
> + a = 0.75;
> + a = 0.875;
> + a = 1.0;
> + a = 1.25;
> + a = 1.5;
> + a = 1.75;
> + a = 2.0;
> + a = 2.5;
> + a = 3.0;
> + a = 1.0*(1 << 2);
> + a = 1.0*(1 << 3);
> + a = 1.0*(1 << 4);
> + a = 1.0*(1 << 7);
> + a = 1.0*(1 << 8);
> + a = 1.0*(1 << 15);
> + a = 1.0*(1 << 16);
> + a = __builtin_inff ();
> + a = __builtin_nanf ("");
> +}
> +
> +void foo_double64 ()
> +{
> + volatile double a;
> + a = -1.0;
> + a = 2.2250738585072014E-308;
> + a = 1.0/(1 << 16);
> + a = 1.0/(1 << 15);
> + a = 1.0/(1 << 8);
> + a = 1.0/(1 << 7);
> + a = 1.0/(1 << 4);
> + a = 1.0/(1 << 3);
> + a = 1.0/(1 << 2);
> + a = 0.3125;
> + a = 0.375;
> + a = 0.4375;
> + a = 0.5;
> + a = 0.625;
> + a = 0.75;
> + a = 0.875;
> + a = 1.0;
> + a = 1.25;
> + a = 1.5;
> + a = 1.75;
> + a = 2.0;
> + a = 2.5;
> + a = 3.0;
> + a = 1.0*(1 << 2);
> + a = 1.0*(1 << 3);
> + a = 1.0*(1 << 4);
> + a = 1.0*(1 << 7);
> + a = 1.0*(1 << 8);
> + a = 1.0*(1 << 15);
> + a = 1.0*(1 << 16);
> + a = __builtin_inf ();
> + a = __builtin_nan ("");
> +}
> +
> +/* { dg-final { scan-assembler-times "fli.s" 32 } } */
> +/* { dg-final { scan-assembler-times "fli.d" 32 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.c
> new file mode 100644
> index 00000000000..bcffe9d2c82
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.c
> @@ -0,0 +1,41 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv32imafdc_zfa_zfh -mabi=ilp32d -O0" } */
> +
> +void foo_float16 ()
> +{
> + volatile _Float16 a;
> + a = -1.0;
> + a = 6.104E-5;
> + a = 1.0/(1 << 16);
> + a = 1.0/(1 << 15);
> + a = 1.0/(1 << 8);
> + a = 1.0/(1 << 7);
> + a = 1.0/(1 << 4);
> + a = 1.0/(1 << 3);
> + a = 1.0/(1 << 2);
> + a = 0.3125;
> + a = 0.375;
> + a = 0.4375;
> + a = 0.5;
> + a = 0.625;
> + a = 0.75;
> + a = 0.875;
> + a = 1.0;
> + a = 1.25;
> + a = 1.5;
> + a = 1.75;
> + a = 2.0;
> + a = 2.5;
> + a = 3.0;
> + a = 1.0*(1 << 2);
> + a = 1.0*(1 << 3);
> + a = 1.0*(1 << 4);
> + a = 1.0*(1 << 7);
> + a = 1.0*(1 << 8);
> + a = 1.0*(1 << 15);
> + a = 1.0*(1 << 16);
> + a = __builtin_inff16 ();
> + a = __builtin_nanf16 ("");
> +}
> +
> +/* { dg-final { scan-assembler-times "fli.h" 32 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
> new file mode 100644
> index 00000000000..13aa7b5f846
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c
> @@ -0,0 +1,41 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv64imafdc_zfa_zfh -mabi=lp64d -O0" } */
> +
> +void foo_float16 ()
> +{
> + volatile _Float16 a;
> + a = -1.0;
> + a = 6.104E-5;
> + a = 1.0/(1 << 16);
> + a = 1.0/(1 << 15);
> + a = 1.0/(1 << 8);
> + a = 1.0/(1 << 7);
> + a = 1.0/(1 << 4);
> + a = 1.0/(1 << 3);
> + a = 1.0/(1 << 2);
> + a = 0.3125;
> + a = 0.375;
> + a = 0.4375;
> + a = 0.5;
> + a = 0.625;
> + a = 0.75;
> + a = 0.875;
> + a = 1.0;
> + a = 1.25;
> + a = 1.5;
> + a = 1.75;
> + a = 2.0;
> + a = 2.5;
> + a = 3.0;
> + a = 1.0*(1 << 2);
> + a = 1.0*(1 << 3);
> + a = 1.0*(1 << 4);
> + a = 1.0*(1 << 7);
> + a = 1.0*(1 << 8);
> + a = 1.0*(1 << 15);
> + a = 1.0*(1 << 16);
> + a = __builtin_inff16 ();
> + a = __builtin_nanf16 ("");
> +}
> +
> +/* { dg-final { scan-assembler-times "fli.h" 32 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fli.c b/gcc/testsuite/gcc.target/riscv/zfa-fli.c
> new file mode 100644
> index 00000000000..b6d41cf460f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fli.c
> @@ -0,0 +1,79 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv64imafdc_zfa -mabi=lp64d -O0" } */
> +
> +void foo_float32 ()
> +{
> + volatile float a;
> + a = -1.0;
> + a = 1.1754944e-38;
> + a = 1.0/(1 << 16);
> + a = 1.0/(1 << 15);
> + a = 1.0/(1 << 8);
> + a = 1.0/(1 << 7);
> + a = 1.0/(1 << 4);
> + a = 1.0/(1 << 3);
> + a = 1.0/(1 << 2);
> + a = 0.3125;
> + a = 0.375;
> + a = 0.4375;
> + a = 0.5;
> + a = 0.625;
> + a = 0.75;
> + a = 0.875;
> + a = 1.0;
> + a = 1.25;
> + a = 1.5;
> + a = 1.75;
> + a = 2.0;
> + a = 2.5;
> + a = 3.0;
> + a = 1.0*(1 << 2);
> + a = 1.0*(1 << 3);
> + a = 1.0*(1 << 4);
> + a = 1.0*(1 << 7);
> + a = 1.0*(1 << 8);
> + a = 1.0*(1 << 15);
> + a = 1.0*(1 << 16);
> + a = __builtin_inff ();
> + a = __builtin_nanf ("");
> +}
> +
> +void foo_double64 ()
> +{
> + volatile double a;
> + a = -1.0;
> + a = 2.2250738585072014E-308;
> + a = 1.0/(1 << 16);
> + a = 1.0/(1 << 15);
> + a = 1.0/(1 << 8);
> + a = 1.0/(1 << 7);
> + a = 1.0/(1 << 4);
> + a = 1.0/(1 << 3);
> + a = 1.0/(1 << 2);
> + a = 0.3125;
> + a = 0.375;
> + a = 0.4375;
> + a = 0.5;
> + a = 0.625;
> + a = 0.75;
> + a = 0.875;
> + a = 1.0;
> + a = 1.25;
> + a = 1.5;
> + a = 1.75;
> + a = 2.0;
> + a = 2.5;
> + a = 3.0;
> + a = 1.0*(1 << 2);
> + a = 1.0*(1 << 3);
> + a = 1.0*(1 << 4);
> + a = 1.0*(1 << 7);
> + a = 1.0*(1 << 8);
> + a = 1.0*(1 << 15);
> + a = 1.0*(1 << 16);
> + a = __builtin_inf ();
> + a = __builtin_nan ("");
> +}
> +
> +/* { dg-final { scan-assembler-times "fli.s" 32 } } */
> +/* { dg-final { scan-assembler-times "fli.d" 32 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp-rv32.c
> new file mode 100644
> index 00000000000..5a52adce36a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fmovh-fmovp-rv32.c
> @@ -0,0 +1,10 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv32g_zfa -mabi=ilp32 -O0" } */
> +
> +double foo(long long a)
> +{
> + return (double)(a + 3);
> +}
> +
> +/* { dg-final { scan-assembler-times "fmvp.d.x" 1 } } */
> +/* { dg-final { scan-assembler-times "fmvh.x.d" 1 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.c b/gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.c
> new file mode 100644
> index 00000000000..b53601d6e1f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.c
> @@ -0,0 +1,42 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv32imafdc_zfa -mabi=ilp32d -O2" } */
> +
> +extern float a;
> +extern double b;
> +
> +void foo (float *x, double *y)
> +{
> + {
> + *x = __builtin_roundf (a);
> + *y = __builtin_round (b);
> + }
> + {
> + *x = __builtin_floorf (a);
> + *y = __builtin_floor (b);
> + }
> + {
> + *x = __builtin_ceilf (a);
> + *y = __builtin_ceil (b);
> + }
> + {
> + *x = __builtin_truncf (a);
> + *y = __builtin_trunc (b);
> + }
> + {
> + *x = __builtin_roundevenf (a);
> + *y = __builtin_roundeven (b);
> + }
> + {
> + *x = __builtin_nearbyintf (a);
> + *y = __builtin_nearbyint (b);
> + }
> + {
> + *x = __builtin_rintf (a);
> + *y = __builtin_rint (b);
> + }
> +}
> +
> +/* { dg-final { scan-assembler-times "fround.s" 6 } } */
> +/* { dg-final { scan-assembler-times "fround.d" 6 } } */
> +/* { dg-final { scan-assembler-times "froundnx.s" 1 } } */
> +/* { dg-final { scan-assembler-times "froundnx.d" 1 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/zfa-fround.c b/gcc/testsuite/gcc.target/riscv/zfa-fround.c
> new file mode 100644
> index 00000000000..c10de82578e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zfa-fround.c
> @@ -0,0 +1,42 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv64imafdc_zfa -mabi=lp64d -O2" } */
> +
> +extern float a;
> +extern double b;
> +
> +void foo (float *x, double *y)
> +{
> + {
> + *x = __builtin_roundf (a);
> + *y = __builtin_round (b);
> + }
> + {
> + *x = __builtin_floorf (a);
> + *y = __builtin_floor (b);
> + }
> + {
> + *x = __builtin_ceilf (a);
> + *y = __builtin_ceil (b);
> + }
> + {
> + *x = __builtin_truncf (a);
> + *y = __builtin_trunc (b);
> + }
> + {
> + *x = __builtin_roundevenf (a);
> + *y = __builtin_roundeven (b);
> + }
> + {
> + *x = __builtin_nearbyintf (a);
> + *y = __builtin_nearbyint (b);
> + }
> + {
> + *x = __builtin_rintf (a);
> + *y = __builtin_rint (b);
> + }
> +}
> +
> +/* { dg-final { scan-assembler-times "fround.s" 6 } } */
> +/* { dg-final { scan-assembler-times "fround.d" 6 } } */
> +/* { dg-final { scan-assembler-times "froundnx.s" 1 } } */
> +/* { dg-final { scan-assembler-times "froundnx.d" 1 } } */
> --
> 2.17.1
>
  
Jeff Law April 20, 2023, 5:40 p.m. UTC | #4
On 3/10/23 05:40, Jin Ma via Gcc-patches wrote:
> This patch adds the 'Zfa' extension for riscv, which is based on:
>   https://github.com/riscv/riscv-isa-manual/commit/d74d99e22d5f68832f70982d867614e2149a3bd7
> latest 'Zfa' change on the master branch of the RISC-V ISA Manual as
> of this writing.
> 
> The Wiki Page (details):
>   https://github.com/a4lg/binutils-gdb/wiki/riscv_zfa
> 
> The binutils-gdb for 'Zfa' extension:
>   https://sourceware.org/pipermail/binutils/2022-September/122938.html
> 
> Implementation of zfa extension on LLVM:
>    https://reviews.llvm.org/rGc0947dc44109252fcc0f68a542fc6ef250d4d3a9
> 
> There are three points that need to be discussed here.
> 1. According to riscv-spec, "The FCVTMO D.W.D instruction was added principally to
>    accelerate the processing of JavaScript Numbers.", so it seems that no implementation
>    is required in the compiler.
> 2. The FROUND and FROUNDN instructions in this patch use related functions in the math
>    library, such as round, floor, ceil, etc. Since there is no interface for half-precision in
>    the math library, the instructions FROUN D.H and FROUNDN X.H have not been implemented for
>    the time being. Is it necessary to add a built-in interface belonging to riscv such as
>   __builtin_roundhf or __builtin_roundf16 to generate half floating point instructions?
> 3. As far as I know, FMINM and FMAXM instructions correspond to C23 library function fminimum
>    and fmaximum. Therefore, I have not dealt with such instructions for the time being, but have
>    simply implemented the pattern of fminm<hf\sf\df>3 and fmaxm<hf\sf\df>3. Is it necessary to
>    add a built-in interface belonging to riscv such as__builtin_fminm to generate half
>    floating-point instructions?
> 
> gcc/ChangeLog:
> 
> 	* common/config/riscv/riscv-common.cc: Add zfa extension.
> 	* config/riscv/constraints.md (Zf): Constrain the floating point number that the FLI instruction can load.
> 	* config/riscv/iterators.md (round_pattern): New.
> 	* config/riscv/predicates.md: Predicate the floating point number that the FLI instruction can load.
> 	* config/riscv/riscv-opts.h (MASK_ZFA): New.
> 	(TARGET_ZFA): New.
> 	* config/riscv/riscv-protos.h (riscv_float_const_rtx_index_for_fli): Get the index of the
>            floating-point number that the FLI instruction can load.
> 	* config/riscv/riscv.cc (find_index_in_array): New.
> 	(riscv_float_const_rtx_index_for_fli): New.
> 	(riscv_cannot_force_const_mem): Likewise.
> 	(riscv_const_insns): Likewise.
> 	(riscv_legitimize_const_move): Likewise.
> 	(riscv_split_64bit_move_p): Exclude floating point numbers that can be loaded by FLI instructions.
> 	(riscv_output_move): Likewise.
> 	(riscv_memmodel_needs_release_fence): Likewise.
> 	(riscv_print_operand): Likewise.
> 	(riscv_secondary_memory_needed): Likewise.
> 	* config/riscv/riscv.h (GP_REG_RTX_P): New.
> 	* config/riscv/riscv.md (fminm<mode>3): New.
> 	(fmaxm<mode>3): New.
> 	(<round_pattern><ANYF:mode>2): New.
> 	(rint<ANYF:mode>2): New.
> 	(f<quiet_pattern>_quiet<ANYF:mode><X:mode>4_zfa): New.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.target/riscv/zfa-fleq-fltq-rv32.c: New test.
> 	* gcc.target/riscv/zfa-fleq-fltq.c: New test.
> 	* gcc.target/riscv/zfa-fli-rv32.c: New test.
> 	* gcc.target/riscv/zfa-fli-zfh-rv32.c: New test.
> 	* gcc.target/riscv/zfa-fli-zfh.c: New test.
> 	* gcc.target/riscv/zfa-fli.c: New test.
> 	* gcc.target/riscv/zfa-fmovh-fmovp-rv32.c: New test.
> 	* gcc.target/riscv/zfa-fround-rv32.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/iterators.md                 |   5 +
>   gcc/config/riscv/predicates.md                |   4 +
>   gcc/config/riscv/riscv-opts.h                 |   3 +
>   gcc/config/riscv/riscv-protos.h               |   1 +
>   gcc/config/riscv/riscv.cc                     | 168 +++++++++++++++++-
>   gcc/config/riscv/riscv.h                      |   1 +
>   gcc/config/riscv/riscv.md                     | 112 +++++++++---
>   .../gcc.target/riscv/zfa-fleq-fltq-rv32.c     |  19 ++
>   .../gcc.target/riscv/zfa-fleq-fltq.c          |  19 ++
>   gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c |  79 ++++++++
>   .../gcc.target/riscv/zfa-fli-zfh-rv32.c       |  41 +++++
>   gcc/testsuite/gcc.target/riscv/zfa-fli-zfh.c  |  41 +++++
>   gcc/testsuite/gcc.target/riscv/zfa-fli.c      |  79 ++++++++
>   .../gcc.target/riscv/zfa-fmovh-fmovp-rv32.c   |  10 ++
>   .../gcc.target/riscv/zfa-fround-rv32.c        |  42 +++++
>   gcc/testsuite/gcc.target/riscv/zfa-fround.c   |  42 +++++
>   18 files changed, 654 insertions(+), 23 deletions(-)
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq-rv32.c
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fleq-fltq.c
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli-rv32.c
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fli-zfh-rv32.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-fmovh-fmovp-rv32.c
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fround-rv32.c
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zfa-fround.c
> 

> diff --git a/gcc/config/riscv/iterators.md b/gcc/config/riscv/iterators.md
> index 5b70ab20758..ef9b1aa9ed3 100644
> --- a/gcc/config/riscv/iterators.md
> +++ b/gcc/config/riscv/iterators.md
> @@ -284,3 +284,8 @@ (define_int_iterator QUIET_COMPARISON [UNSPEC_FLT_QUIET UNSPEC_FLE_QUIET])
>   (define_int_attr quiet_pattern [(UNSPEC_FLT_QUIET "lt") (UNSPEC_FLE_QUIET "le")])
>   (define_int_attr QUIET_PATTERN [(UNSPEC_FLT_QUIET "LT") (UNSPEC_FLE_QUIET "LE")])
>   
> +(define_int_iterator ROUND [UNSPEC_ROUND UNSPEC_FLOOR UNSPEC_CEIL UNSPEC_BTRUNC UNSPEC_ROUNDEVEN UNSPEC_NEARBYINT])
> +(define_int_attr round_pattern [(UNSPEC_ROUND "round") (UNSPEC_FLOOR "floor") (UNSPEC_CEIL "ceil")
> +				(UNSPEC_BTRUNC "btrunc") (UNSPEC_ROUNDEVEN "roundeven") (UNSPEC_NEARBYINT "nearbyint")])
> +(define_int_attr round_rm [(UNSPEC_ROUND "rmm") (UNSPEC_FLOOR "rdn") (UNSPEC_CEIL "rup")
> +			   (UNSPEC_BTRUNC "rtz") (UNSPEC_ROUNDEVEN "rne") (UNSPEC_NEARBYINT "dyn")])
> \ No newline at end of file
> diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md
> index 0d9d7701c7e..b16b8d438e3 100644
> --- a/gcc/config/riscv/predicates.md
> +++ b/gcc/config/riscv/predicates.md
> @@ -149,6 +149,10 @@ (define_predicate "move_operand"
>       case CONST_POLY_INT:
>         return known_eq (rtx_to_poly_int64 (op), BYTES_PER_RISCV_VECTOR);
>   
> +    case CONST_DOUBLE:
> +      return const_0_operand (op, mode)
> +	      || (riscv_float_const_rtx_index_for_fli (op) != -1);
Formatting nit.  When you have a line break like this, go ahead and wrap 
the whole expression with parens and put the operator one space past the 
open paren.  Like this:

> 
>     case CONST_DOUBLE
>       return (const_0_operand (op, mode)
>               || riscv_float_const_rtx_index_for_fli (op) != -1);




I used spaces so that mailers don't muck up the formating.  Please make 
sure to use tabs insted of 8 spaces if the indent is >= 8.


> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
> index c91fa3101aa..c81a2bf44f5 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -799,6 +799,108 @@ static int riscv_symbol_insns (enum riscv_symbol_type type)
>       }
>   }
>   
> +/* Immediate values loaded by the FLI.S instruction in Chapter 25 of the latest RISC-V ISA
> +   Manual draft. For details, please see:
> +   https://github.com/riscv/riscv-isa-manual/releases/tag/draft-20221217-cb3b9d1 */
> +
> +unsigned HOST_WIDE_INT fli_value_hf[32] =
> +{
> +  0xbc00, 0x400, 0x100, 0x200, 0x1c00, 0x2000, 0x2c00, 0x3000,
> +  0x3400, 0x3500, 0x3600, 0x3700, 0x3800, 0x3900, 0x3a00, 0x3b00,
> +  0x3c00, 0x3d00, 0x3e00, 0x3f00, 0x4000, 0x4100, 0x4200, 0x4400,
> +  0x4800, 0x4c00, 0x5800, 0x5c00, 0x7800,
> +  /* Only used for filling, ensuring that 29 and 30 of HF are the same. */
> +  0x7800,
> +  0x7c00, 0x7e00,
> +};
> +
> +unsigned HOST_WIDE_INT fli_value_sf[32] =
> +{
> +  0xbf800000, 0x00800000, 0x37800000, 0x38000000, 0x3b800000, 0x3c000000, 0x3d800000, 0x3e000000,
> +  0x3e800000, 0x3ea00000, 0x3ec00000, 0x3ee00000, 0x3f000000, 0x3f200000, 0x3f400000, 0x3f600000,
> +  0x3f800000, 0x3fa00000, 0x3fc00000, 0x3fe00000, 0x40000000, 0x40200000, 0x40400000, 0x40800000,
> +  0x41000000, 0x41800000, 0x43000000, 0x43800000, 0x47000000, 0x47800000, 0x7f800000, 0x7fc00000
> +};
> +
> +unsigned HOST_WIDE_INT fli_value_df[32] =
> +{
> +  0xbff0000000000000, 0x10000000000000, 0x3ef0000000000000, 0x3f00000000000000,
> +  0x3f70000000000000, 0x3f80000000000000, 0x3fb0000000000000, 0x3fc0000000000000,
> +  0x3fd0000000000000, 0x3fd4000000000000, 0x3fd8000000000000, 0x3fdc000000000000,
> +  0x3fe0000000000000, 0x3fe4000000000000, 0x3fe8000000000000, 0x3fec000000000000,
> +  0x3ff0000000000000, 0x3ff4000000000000, 0x3ff8000000000000, 0x3ffc000000000000,
> +  0x4000000000000000, 0x4004000000000000, 0x4008000000000000, 0x4010000000000000,
> +  0x4020000000000000, 0x4030000000000000, 0x4060000000000000, 0x4070000000000000,
> +  0x40e0000000000000, 0x40f0000000000000, 0x7ff0000000000000, 0x7ff8000000000000,
> +};
Would it make more sense to use the newer floating point literal 
formats?  I like them marginally more than hext constants because many 
tools can consume that floating point literal syntax trivially.


> +
> +/* Find the index of TARGET in ARRAY, and return -1 if not found. */
"Return the index of TARGET in ARRAY, return -1 if not found.  */


> +
> +/* Return index of the FLI instruction table if rtx X is an immediate constant that
> +   can be moved using a single FLI instruction in zfa extension. -1 otherwise. */
> +
> +int
> +riscv_float_const_rtx_index_for_fli (rtx x)
> +{
> +  machine_mode mode = GET_MODE (x);
> +
> +  if (!TARGET_ZFA || mode == VOIDmode
> +      || !CONST_DOUBLE_P(x)
> +      || (mode == HFmode && !TARGET_ZFH)
> +      || (mode == SFmode && !TARGET_HARD_FLOAT)
> +      || (mode == DFmode && !TARGET_DOUBLE_FLOAT))
> +    return -1;
Bring the "|| mode == VOIDmode" down on its own line.




> @@ -1191,6 +1296,8 @@ riscv_const_insns (rtx x)
>         }
>   
>       case CONST_DOUBLE:
> +      if (riscv_float_const_rtx_index_for_fli (x) != -1)
> +	return 4;
Should this be returning 1?  That's the case when we find the constant 
in the table and have the FLI instruction.





>     /* Allow FPR <-> FPR and FPR <-> MEM moves, and permit the special case
>        of zeroing an FPR with FCVT.D.W.  */
>     if (TARGET_DOUBLE_FLOAT
>         && ((FP_REG_RTX_P (src) && FP_REG_RTX_P (dest))
>   	  || (FP_REG_RTX_P (dest) && MEM_P (src))
>   	  || (FP_REG_RTX_P (src) && MEM_P (dest))
> +	  || (TARGET_ZFA
> +	      && ((FP_REG_RTX_P (dest) && GP_REG_RTX_P (src))
> +	      || (FP_REG_RTX_P (src) && GP_REG_RTX_P (dest))))
>   	  || (FP_REG_RTX_P (dest) && src == CONST0_RTX (GET_MODE (src)))))
>       return false;

The formatting here seems off.  In particular the last || line you added 
seems like it might need to be indented further.  It's also possible the 
mailer has mucked up.  So double check and fix is it needs adjustment.


In the .md file changes, several of the patterns use UNSPECs.  If 
possible we should try to avoid those when there are reasonable mappings 
to GCC's RTL codes.  Can you please double-check to see if any of the 
cases you're adding can be properly handled by the existing RTL codes?

Overall it looks pretty close.

Jeff
  
Kito Cheng May 3, 2023, 10:30 a.m. UTC | #5
FYI:

This PR described the syntax of fli instruction, which is implemented
in LLVM, and the latest binutils patch (IIRC :P):

https://github.com/riscv-non-isa/riscv-asm-manual/pull/85
  

Patch

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