[RFC,2/2] RISC-V: Add 'Zfbfmin' extension.

Message ID 20230919084625.2183-1-jinma@linux.alibaba.com
State Unresolved
Headers
Series [RFC,1/2] RISC-V: Add support for _Bfloat16. |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Jin Ma Sept. 19, 2023, 8:46 a.m. UTC
  This patch adds the 'Zfbfmin' extension for riscv, which is based on spec of bfloat16:
https://github.com/riscv/riscv-bfloat16/commit/5578e34e15a44e9ad13246072a29f51274b4d999

The 'Zfbfmin' extension of binutils-gdb (REVIEW ONLY):
https://sourceware.org/pipermail/binutils/2023-August/128773.html

The 'Zfbfmin' extension of qemu:
https://github.com/qemu/qemu/commit/5d1270caac2ef7b8c887d4cb5a2444ba6d237516

Because the binutils does not yet support the 'Zfbfmin' extension, test case
zfbfmin_convert_run.c is invalidated with '#if 0' and '#endif'.

gcc/ChangeLog:

	* common/config/riscv/riscv-common.cc: Add 'Zfbfmin' extension.
	* config/riscv/riscv-opts.h (MASK_ZFBFMIN): New.
	(TARGET_ZFBFMIN): New.
	* config/riscv/riscv.cc (riscv_output_move): Enable FMV.X.H, and FMV.H.X
	for 'Zfbfmin' extension.
	(riscv_excess_precision): Likewise.
	* config/riscv/riscv.md (truncsfbf2): New.
	(extendbfsf2):  New.
	(*mov<mode>_hardfloat): Support for BFmode.
	(*mov<mode>_softfloat): Disable for BFmode  when 'Zfbfmin' extension is
	enabled.

gcc/testsuite/ChangeLog:

	* gcc.target/riscv/zfbfmin_arithmetic.c: New test.
	* gcc.target/riscv/zfbfmin_call.c: New test.
	* gcc.target/riscv/zfbfmin_comparisons.c: New test.
	* gcc.target/riscv/zfbfmin_convert.c: New test.
	* gcc.target/riscv/zfbfmin_convert_run.c: New test.
	* gcc.target/riscv/zfbfmin_fsh_and_flh.c: New test.
---
 gcc/common/config/riscv/riscv-common.cc       |   3 +
 gcc/config/riscv/riscv-opts.h                 |   2 +
 gcc/config/riscv/riscv.cc                     |   4 +-
 gcc/config/riscv/riscv.md                     |  40 ++--
 .../gcc.target/riscv/zfbfmin_arithmetic.c     |  31 ++++
 gcc/testsuite/gcc.target/riscv/zfbfmin_call.c |  17 ++
 .../gcc.target/riscv/zfbfmin_comparisons.c    |  22 +++
 .../gcc.target/riscv/zfbfmin_convert.c        |  38 ++++
 .../gcc.target/riscv/zfbfmin_convert_run.c    | 173 ++++++++++++++++++
 .../gcc.target/riscv/zfbfmin_fsh_and_flh.c    |  12 ++
 10 files changed, 329 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfbfmin_arithmetic.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfbfmin_call.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfbfmin_comparisons.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfbfmin_convert.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfbfmin_convert_run.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zfbfmin_fsh_and_flh.c
  

Comments

Jeff Law Sept. 29, 2023, 5:52 p.m. UTC | #1
On 9/19/23 02:46, Jin Ma wrote:
> This patch adds the 'Zfbfmin' extension for riscv, which is based on spec of bfloat16:
> https://github.com/riscv/riscv-bfloat16/commit/5578e34e15a44e9ad13246072a29f51274b4d999
> 
> The 'Zfbfmin' extension of binutils-gdb (REVIEW ONLY):
> https://sourceware.org/pipermail/binutils/2023-August/128773.html
> 
> The 'Zfbfmin' extension of qemu:
> https://github.com/qemu/qemu/commit/5d1270caac2ef7b8c887d4cb5a2444ba6d237516
> 
> Because the binutils does not yet support the 'Zfbfmin' extension, test case
> zfbfmin_convert_run.c is invalidated with '#if 0' and '#endif'.
> 
> gcc/ChangeLog:
> 
> 	* common/config/riscv/riscv-common.cc: Add 'Zfbfmin' extension.
> 	* config/riscv/riscv-opts.h (MASK_ZFBFMIN): New.
> 	(TARGET_ZFBFMIN): New.
> 	* config/riscv/riscv.cc (riscv_output_move): Enable FMV.X.H, and FMV.H.X
> 	for 'Zfbfmin' extension.
> 	(riscv_excess_precision): Likewise.
> 	* config/riscv/riscv.md (truncsfbf2): New.
> 	(extendbfsf2):  New.
> 	(*mov<mode>_hardfloat): Support for BFmode.
> 	(*mov<mode>_softfloat): Disable for BFmode  when 'Zfbfmin' extension is
> 	enabled.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.target/riscv/zfbfmin_arithmetic.c: New test.
> 	* gcc.target/riscv/zfbfmin_call.c: New test.
> 	* gcc.target/riscv/zfbfmin_comparisons.c: New test.
> 	* gcc.target/riscv/zfbfmin_convert.c: New test.
> 	* gcc.target/riscv/zfbfmin_convert_run.c: New test.
> 	* gcc.target/riscv/zfbfmin_fsh_and_flh.c: New test.
So as with 1/2 in this series, it can't go into the trunk until the 
relevant spec reaches a frozen state.

> 
> +/* { dg-final { scan-assembler-times "fcvt.s.bf16" 14 } } */
> +/* { dg-final { scan-assembler-times "fcvt.bf16.s" 10 } } */
So I think these have the potential to run afoul of unexpected matching 
of LTO bits.  Joern has an approach to tackle this problem that was 
recently pushed into the tree:

> https://gcc.gnu.org/pipermail/gcc-patches/2023-September/631485.html

The gist is wrap the assembly instruction inside a {\m \M} construct. 
So concretely

 > +/* { dg-final { scan-assembler-times {\mfcvt.s.bf16\M} 14 } } */
 > +/* { dg-final { scan-assembler-times {\mfcvt.bf16.s\M} 10 } } */

Similarly for the other new tests where you actually match an instruction.


Overall it looks pretty good.

Jeff
  

Patch

diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc
index 9a0a68fe5db..1fcbb862aa4 100644
--- a/gcc/common/config/riscv/riscv-common.cc
+++ b/gcc/common/config/riscv/riscv-common.cc
@@ -123,6 +123,7 @@  static const riscv_implied_info_t riscv_implied_info[] =
 
   {"zfh", "zfhmin"},
   {"zfhmin", "f"},
+  {"zfbfmin", "f"},
 
   {"zfa", "f"},
 
@@ -284,6 +285,7 @@  static const struct riscv_ext_version riscv_ext_version_table[] =
   {"zfhmin",    ISA_SPEC_CLASS_NONE, 1, 0},
   {"zvfhmin",   ISA_SPEC_CLASS_NONE, 1, 0},
   {"zvfh",      ISA_SPEC_CLASS_NONE, 1, 0},
+  {"zfbfmin",     ISA_SPEC_CLASS_NONE, 0, 8},
 
   {"zfa",     ISA_SPEC_CLASS_NONE, 0, 1},
 
@@ -1461,6 +1463,7 @@  static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
   {"zfh",       &gcc_options::x_riscv_zf_subext, MASK_ZFH},
   {"zvfhmin",   &gcc_options::x_riscv_zf_subext, MASK_ZVFHMIN},
   {"zvfh",      &gcc_options::x_riscv_zf_subext, MASK_ZVFH},
+  {"zfbfmin",      &gcc_options::x_riscv_zf_subext, MASK_ZFBFMIN},
 
   {"zfa",       &gcc_options::x_riscv_zfa_subext, MASK_ZFA},
 
diff --git a/gcc/config/riscv/riscv-opts.h b/gcc/config/riscv/riscv-opts.h
index a525f679683..900a46fcae0 100644
--- a/gcc/config/riscv/riscv-opts.h
+++ b/gcc/config/riscv/riscv-opts.h
@@ -256,11 +256,13 @@  enum riscv_entity
 #define MASK_ZFH      (1 << 1)
 #define MASK_ZVFHMIN  (1 << 2)
 #define MASK_ZVFH     (1 << 3)
+#define MASK_ZFBFMIN  (1 << 4)
 
 #define TARGET_ZFHMIN  ((riscv_zf_subext & MASK_ZFHMIN) != 0)
 #define TARGET_ZFH     ((riscv_zf_subext & MASK_ZFH) != 0)
 #define TARGET_ZVFHMIN ((riscv_zf_subext & MASK_ZVFHMIN) != 0)
 #define TARGET_ZVFH    ((riscv_zf_subext & MASK_ZVFH) != 0)
+#define TARGET_ZFBFMIN    ((riscv_zf_subext & MASK_ZFBFMIN) != 0)
 
 #define MASK_ZMMUL      (1 << 0)
 #define TARGET_ZMMUL    ((riscv_zm_subext & MASK_ZMMUL) != 0)
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 910523ee2b9..6362c3f83c8 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -3372,7 +3372,7 @@  riscv_output_move (rtx dest, rtx src)
 	switch (width)
 	  {
 	  case 2:
-	    if (TARGET_ZFHMIN)
+	    if (TARGET_ZFHMIN || TARGET_ZFBFMIN)
 	      return "fmv.x.h\t%0,%1";
 	    /* Using fmv.x.s + sign-extend to emulate fmv.x.h.  */
 	    return "fmv.x.s\t%0,%1;slli\t%0,%0,16;srai\t%0,%0,16";
@@ -3428,7 +3428,7 @@  riscv_output_move (rtx dest, rtx src)
 	    switch (width)
 	      {
 	      case 2:
-		if (TARGET_ZFHMIN)
+		if (TARGET_ZFHMIN || TARGET_ZFBFMIN)
 		  return "fmv.h.x\t%0,%z1";
 		/* High 16 bits should be all-1, otherwise HW will treated
 		   as a n-bit canonical NaN, but isn't matter for softfloat.  */
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 5048628c784..ef0c38cb633 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -1631,14 +1631,23 @@  (define_insn "truncdfhf2"
   [(set_attr "type" "fcvt")
    (set_attr "mode" "HF")])
 
+(define_insn "truncsfbf2"
+  [(set (match_operand:BF     0 "register_operand" "=f")
+       (float_truncate:BF
+           (match_operand:SF 1 "register_operand" " f")))]
+  "TARGET_ZFBFMIN"
+  "fcvt.bf16.s\t%0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "BF")])
+
 ;; The conversion of DF to BF needs to be done with SF if there is a
-;; chance to generate at least one instruction, otherwise just using
-;; libfunc __truncdfbf2.
+;; chance to generate at least one instruction, whether it is 'fcvt.s.d'
+;; or 'fcvt.bf16.s'. Otherwise just using libfunc __truncdfbf2.
 (define_expand "truncdfbf2"
   [(set (match_operand:BF     0 "register_operand" "=f")
        (float_truncate:BF
            (match_operand:DF 1 "register_operand" " f")))]
-  "TARGET_DOUBLE_FLOAT || TARGET_ZDINX"
+  "TARGET_DOUBLE_FLOAT || TARGET_ZDINX || TARGET_ZFBFMIN"
   {
     convert_move (operands[0],
 		  convert_modes (SFmode, DFmode, operands[1], 0), 0);
@@ -1797,6 +1806,15 @@  (define_insn "extendhfdf2"
   [(set_attr "type" "fcvt")
    (set_attr "mode" "DF")])
 
+(define_insn "extendbfsf2"
+  [(set (match_operand:SF     0 "register_operand" "=f")
+       (float_extend:SF
+           (match_operand:BF 1 "register_operand" " f")))]
+  "TARGET_ZFBFMIN"
+  "fcvt.s.bf16\t%0,%1"
+  [(set_attr "type" "fcvt")
+   (set_attr "mode" "SF")])
+
 ;; 16-bit floating point moves
 (define_expand "mov<mode>"
   [(set (match_operand:HFBF 0 "")
@@ -1807,21 +1825,21 @@  (define_expand "mov<mode>"
     DONE;
 })
 
-(define_insn "*movhf_hardfloat"
-  [(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,zfli,G,m,f,G,*r,*f,*G*r,*m,*r"))]
-  "TARGET_ZFHMIN
-   && (register_operand (operands[0], HFmode)
-       || reg_or_0_operand (operands[1], HFmode))"
+(define_insn "*mov<mode>_hardfloat"
+  [(set (match_operand:HFBF 0 "nonimmediate_operand" "=f,   f,f,f,m,m,*f,*r,  *r,*r,*m")
+	(match_operand:HFBF 1 "move_operand"         " f,zfli,G,m,f,G,*r,*f,*G*r,*m,*r"))]
+  "((TARGET_ZFHMIN && <MODE>mode == HFmode) || (TARGET_ZFBFMIN && <MODE>mode == BFmode))
+   && (register_operand (operands[0], <MODE>mode)
+       || reg_or_0_operand (operands[1], <MODE>mode))"
   { return riscv_output_move (operands[0], operands[1]); }
   [(set_attr "move_type" "fmove,fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
    (set_attr "type" "fmove")
-   (set_attr "mode" "HF")])
+   (set_attr "mode" "<MODE>")])
 
 (define_insn "*mov<mode>_softfloat"
   [(set (match_operand:HFBF 0 "nonimmediate_operand" "=f, r,r,m,*f,*r")
 	(match_operand:HFBF 1 "move_operand"         " f,Gr,m,r,*r,*f"))]
-  "(!(TARGET_ZFHMIN && <MODE>mode == HFmode) || (<MODE>mode == BFmode))
+  "!((TARGET_ZFHMIN && <MODE>mode == HFmode) || (TARGET_ZFBFMIN && <MODE>mode == BFmode))
    && (register_operand (operands[0], <MODE>mode)
        || reg_or_0_operand (operands[1], <MODE>mode))"
   { return riscv_output_move (operands[0], operands[1]); }
diff --git a/gcc/testsuite/gcc.target/riscv/zfbfmin_arithmetic.c b/gcc/testsuite/gcc.target/riscv/zfbfmin_arithmetic.c
new file mode 100644
index 00000000000..529e9b40daa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfbfmin_arithmetic.c
@@ -0,0 +1,31 @@ 
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gc_zfbfmin -mabi=ilp32d -O" { target { rv32 } } } */
+/* { dg-options "-march=rv64gc_zfbfmin -mabi=lp64d -O" { target { rv64 } } } */
+
+extern _Bfloat16 bf;
+extern _Bfloat16 bf1;
+extern _Bfloat16 bf2;
+
+/* Arithmetic.  */
+void bf_add_bf () { bf = bf1 + bf2; }
+
+void bf_sub_bf () { bf = bf1 - bf2; }
+
+void bf_mul_bf () { bf = bf1 * bf2; }
+
+void bf_div_bf () { bf = bf1 / bf2; }
+
+void bf_add_const () { bf = bf1 + 3.14; }
+
+void const_sub_bf () { bf = 3.14 - bf2; }
+
+void bf_mul_const () { bf = bf1 *3.14; }
+
+void const_div_bf () { bf = 3.14 / bf2; }
+
+void bf_inc () { ++bf; }
+
+void bf_dec () { --bf; }
+
+/* { dg-final { scan-assembler-times "fcvt.s.bf16" 14 } } */
+/* { dg-final { scan-assembler-times "fcvt.bf16.s" 10 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfbfmin_call.c b/gcc/testsuite/gcc.target/riscv/zfbfmin_call.c
new file mode 100644
index 00000000000..6cda430020e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfbfmin_call.c
@@ -0,0 +1,17 @@ 
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gc_zfbfmin -mabi=ilp32d -O" { target { rv32 } } } */
+/* { dg-options "-march=rv64gc_zfbfmin -mabi=lp64d -O" { target { rv64 } } } */
+
+_Bfloat16 add (_Bfloat16 a, _Bfloat16 b) __attribute__ ((noinline));
+_Bfloat16 add (_Bfloat16 a, _Bfloat16 b)
+{
+  return a + b;
+}
+
+_Bfloat16 foo(_Bfloat16 a, _Bfloat16 b)
+{
+  return add (a, b);
+}
+
+/* { dg-final { scan-assembler-times "fcvt.s.bf16" 2 } } */
+/* { dg-final { scan-assembler-times "fcvt.bf16.s" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfbfmin_comparisons.c b/gcc/testsuite/gcc.target/riscv/zfbfmin_comparisons.c
new file mode 100644
index 00000000000..3dd2874a8bd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfbfmin_comparisons.c
@@ -0,0 +1,22 @@ 
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gc_zfbfmin -mabi=ilp32d -O" { target { rv32 } } } */
+/* { dg-options "-march=rv64gc_zfbfmin -mabi=lp64d -O" { target { rv64 } } } */
+
+extern _Bfloat16 bf;
+extern _Bfloat16 bf1;
+extern _Bfloat16 bf2;
+
+/* Comparisons.  */
+void bf_lt_bf () { bf = (bf1 < bf2) ? bf1 : bf2; }
+
+void bf_gt_bf () { bf = (bf1 > bf2) ? bf1 : bf2; }
+
+void bf_eq_bf () { bf = (bf1 == bf2) ? bf1 : bf2; }
+
+void bf_lt_const () { bf = (bf1 < 3.14) ? bf1 : bf2; }
+
+void const_gt_bf () { bf = (3.14 > bf2) ? bf1 : bf2; }
+
+void bf_eq_const () { bf = (bf1 == 3.14) ? bf1 : bf2; }
+
+/* { dg-final { scan-assembler-times "fcvt.s.bf16" 9 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfbfmin_convert.c b/gcc/testsuite/gcc.target/riscv/zfbfmin_convert.c
new file mode 100644
index 00000000000..b9b2a1ca6b8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfbfmin_convert.c
@@ -0,0 +1,38 @@ 
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gc_zfbfmin -mabi=ilp32d -O" { target { rv32 } } } */
+/* { dg-options "-march=rv64gc_zfbfmin -mabi=lp64d -O" { target { rv64 } } } */
+
+extern _Bfloat16 bf;
+extern _Bfloat16 bf1;
+extern _Bfloat16 bf2;
+extern _Float16 hf;
+extern float sf;
+extern double df;
+
+extern int si;
+extern long long  di;
+
+extern unsigned int usi;
+extern unsigned long long udi;
+
+/* Fp or gp Converts to bf.  */
+void hf_to_bf () { bf = hf; } /* { dg-final { scan-assembler-times "call\t__trunchfbf2" 1 } } */
+void sf_to_bf () { bf = sf; }
+void df_to_bf () { bf = df; }
+void si_to_bf () { bf = si; }
+void di_to_bf () { bf = di; } /* { dg-final { scan-assembler-times "call\t__floatdibf" 1 { target { rv32 } } } } */ 
+void usi_to_bf () { bf = usi; }
+void udi_to_bf () { bf = udi; } /* { dg-final { scan-assembler-times "call\t__floatundibf" 1 { target { rv32 } } } } */ 
+void const_to_bf () { __volatile__ const float temp = 3.14; bf = temp; }
+/* { dg-final { scan-assembler-times "fcvt.bf16.s" 5 { target { rv32 } } } } */
+/* { dg-final { scan-assembler-times "fcvt.bf16.s" 7 { target { rv64 } } } } */
+
+/* Bf converts to fp or gp.  */
+void bf_to_hf () { hf = bf; } /* { dg-final { scan-assembler-times "call\t__truncsfhf2" 1 } } */
+void bf_to_sf () { sf = bf; }
+void bf_to_df () { df = bf; }
+void bf_to_si () { si = bf; }
+void bf_to_di () { di = bf; }
+void bf_to_usi () { usi = bf; }
+void bf_to_udi () { udi = bf; }
+/* { dg-final { scan-assembler-times "fcvt.s.bf16" 7 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfbfmin_convert_run.c b/gcc/testsuite/gcc.target/riscv/zfbfmin_convert_run.c
new file mode 100644
index 00000000000..e97c71b8595
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfbfmin_convert_run.c
@@ -0,0 +1,173 @@ 
+/* { dg-do run } */
+/* { dg-options "-march=rv32gc_zfbfmin -mabi=ilp32d -O" { target { rv32 } } } */
+/* { dg-options "-march=rv64gc_zfbfmin -mabi=lp64d -O" { target { rv64 } } } */
+
+/* Need to wait for binutils and qemu or other emulators or hardware to support
+   zfbfmin extensions.  */
+#if 0
+#include <stdio.h>
+
+#define NO_INLINE __attribute__((noinline))
+
+int NO_INLINE
+bf16_to_int ()
+{
+  int ret[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  _Bfloat16 a_bf16 = 1.2;
+  _Bfloat16 b_bf16 = 7.1;
+  signed char a_char = 1;
+  short int a_short_int = 2;
+  int a_int = 3;
+  long a_long = 4;
+  long long a_long_long = 5;
+
+  a_bf16 = (_Bfloat16)a_char;
+  if (a_bf16 != (_Bfloat16)1)
+    ret[0] = 1;
+
+  a_bf16 = (_Bfloat16)a_short_int;
+  if (a_bf16 != (_Bfloat16)2)
+    ret[1] = 1;
+
+  a_bf16 = (_Bfloat16)a_int;
+  if (a_bf16 != (_Bfloat16)3)
+    ret[2] = 1;
+
+  a_bf16 = (_Bfloat16)a_long;
+  if (a_bf16 != (_Bfloat16)4)
+    ret[3] = 1;
+
+  a_bf16 = (_Bfloat16)a_long_long;
+  if (a_bf16 != (_Bfloat16)5)
+    ret[4] = 1;
+
+  a_char = (signed char)b_bf16;
+  if (a_char != (signed char)7.1)
+    ret[5] = 1;
+
+  a_short_int = (short int)b_bf16;
+  if (a_short_int != (short int)7.1)
+    ret[6] = 1;
+
+  a_int = (int)b_bf16;
+  if (a_int != (int)7.1)
+    ret[7] = 1;
+
+  a_long = (long)b_bf16;
+  if (a_long != (long)7.1)
+    ret[8] = 1;
+
+  a_long_long = (long long)b_bf16;
+  if (a_long_long != (long long)7.1)
+    ret[9] = 1;
+
+  if ((ret[0] == 1) || (ret[1] == 1) || (ret[2] == 1) || (ret[3] == 1) || (ret[4] == 1) ||
+      (ret[5] == 1) || (ret[6] == 1) || (ret[7] == 1) || (ret[8] == 1) || (ret[9] == 1))
+    return 1;
+  else
+    return 0;
+}
+
+int NO_INLINE
+bf16_to_uint ()
+{
+  int ret[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  _Bfloat16 a_bf16 = 1.2;
+  _Bfloat16 b_bf16 = 7.1;
+  unsigned char a_uchar = 1;
+  unsigned short int a_short_uint = 2;
+  unsigned int a_uint = 3;
+  unsigned long a_ulong = 4;
+  unsigned long long a_ulong_ulong = 5;
+
+  a_bf16 = (_Bfloat16)a_uchar;
+  if (a_bf16 != (_Bfloat16)1)
+    ret[0] = 1;
+
+  a_bf16 = (_Bfloat16)a_short_uint;
+  if (a_bf16 != (_Bfloat16)2)
+    ret[1] = 1;
+
+  a_bf16 = (_Bfloat16)a_uint;
+  if (a_bf16 != (_Bfloat16)3)
+    ret[2] = 1;
+
+  a_bf16 = (_Bfloat16)a_ulong;
+  if (a_bf16 != (_Bfloat16)4)
+    ret[3] = 1;
+
+  a_bf16 = (_Bfloat16)a_ulong_ulong;
+  if (a_bf16 != (_Bfloat16)5)
+    ret[4] = 1;
+
+  a_uchar = (unsigned char)b_bf16;
+  if (a_uchar != (unsigned char)7.1)
+    ret[5] = 1;
+
+  a_short_uint = (unsigned short int)b_bf16;
+  if (a_short_uint != (unsigned short int)7.1)
+    ret[6] = 1;
+
+  a_uint = (unsigned int)b_bf16;
+  if (a_uint != (unsigned int)7.1)
+    ret[7] = 1;
+
+  a_ulong = (unsigned long)b_bf16;
+  if (a_ulong != (unsigned long)7.1)
+    ret[8] = 1;
+
+  a_ulong_ulong = (unsigned long long)b_bf16;
+  if (a_ulong_ulong != (unsigned long long)7.1)
+    ret[9] = 1;
+
+  if ((ret[0] == 1) || (ret[1] == 1) || (ret[2] == 1) || (ret[3] == 1) || (ret[4] == 1) ||
+      (ret[5] == 1) || (ret[6] == 1) || (ret[7] == 1) || (ret[8] == 1) || (ret[9] == 1))
+    return 1;
+  else
+    return 0;
+}
+
+int NO_INLINE
+bf16_to_float ()
+{
+  int ret[4] = {0, 0, 0, 0};
+  _Bfloat16 a_bf16 = 1.2;
+  _Bfloat16 b_bf16 = 7.5;
+  float a_float = 3.7;
+  double a_double = 5.8;
+  a_bf16 = (_Bfloat16)a_float;
+  if (a_bf16 != ((_Bfloat16)3.7))
+    ret[0] = 1;
+
+  a_bf16 = (_Bfloat16)a_double;
+  if (a_bf16 != ((_Bfloat16)5.8))
+    ret[1] = 1;
+
+  a_float = (float)b_bf16;
+  if (a_float != (float)7.5)
+    ret[2] = 1;
+
+  a_double = (double)b_bf16;
+  if (a_double != (double)7.5)
+    ret[3] = 1;
+
+  if ((ret[0] == 1) || (ret[1] == 1) || (ret[2] == 1) || (ret[3] == 1))
+    return 1;
+  else
+    return 0;
+}
+
+int main()
+{
+  if (bf16_to_int () || bf16_to_uint () || bf16_to_float ())
+    return 1;
+  else
+    return 0;
+}
+#else
+int main()
+{
+  return 0;
+}
+#endif
+
diff --git a/gcc/testsuite/gcc.target/riscv/zfbfmin_fsh_and_flh.c b/gcc/testsuite/gcc.target/riscv/zfbfmin_fsh_and_flh.c
new file mode 100644
index 00000000000..0255f27f3ba
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfbfmin_fsh_and_flh.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gc_zfbfmin -mabi=ilp32d -O2" { target { rv32 } } } */
+/* { dg-options "-march=rv64gc_zfbfmin -mabi=lp64d -O2" { target { rv64 } } } */
+
+void
+foo (int a, _Bfloat16 *b)
+{
+  *b += a;
+}
+
+/* { dg-final { scan-assembler-times "fsh\t" 1 } } */
+/* { dg-final { scan-assembler-times "flh\t" 1 } } */