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 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 } } */ -- 2.17.1