From 5e0f10a0f590b31bd7d78bd194c1c9808008f378 Mon Sep 17 00:00:00 2001 From: James Greenhalgh Date: Wed, 23 Nov 2016 17:33:39 +0000 Subject: [PATCH] [Patch 16/17 libgcc ARM] Half to double precision conversions gcc/ * config/arm/arm.c (arm_convert_to_type): Delete. (TARGET_CONVERT_TO_TYPE): Delete. (arm_init_libfuncs): Enable trunc_optab from DFmode to HFmode. (arm_libcall_uses_aapcs_base): Add trunc_optab from DF- to HFmode. * config/arm/arm.h (TARGET_FP16_TO_DOUBLE): New. * config/arm/arm.md (truncdfhf2): Only convert through SFmode if we are in fast math mode, and have no single step hardware instruction. (extendhfdf2): Only expand through SFmode if we don't have a single-step hardware instruction. * config/arm/vfp.md (*truncdfhf2): New. (extendhfdf2): Likewise. gcc/testsuite/ * gcc.target/arm/fp16-rounding-alt-1.c (ROUNDED): Change expected result. * gcc.target/arm/fp16-rounding-ieee-1.c (ROUNDED): Change expected result. From-SVN: r242783 --- gcc/ChangeLog | 14 ++++ gcc/config/arm/arm.c | 28 ++------ gcc/config/arm/arm.h | 5 ++ gcc/config/arm/arm.md | 69 ++++++++++++------- gcc/config/arm/vfp.md | 20 ++++++ gcc/testsuite/ChangeLog | 7 ++ .../gcc.target/arm/fp16-rounding-alt-1.c | 10 +-- .../gcc.target/arm/fp16-rounding-ieee-1.c | 10 +-- 8 files changed, 109 insertions(+), 54 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 5a19b605205..4ab87a9d009 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,17 @@ +2016-11-23 James Greenhalgh + + * config/arm/arm.c (arm_convert_to_type): Delete. + (TARGET_CONVERT_TO_TYPE): Delete. + (arm_init_libfuncs): Enable trunc_optab from DFmode to HFmode. + (arm_libcall_uses_aapcs_base): Add trunc_optab from DF- to HFmode. + * config/arm/arm.h (TARGET_FP16_TO_DOUBLE): New. + * config/arm/arm.md (truncdfhf2): Only convert through SFmode if we + are in fast math mode, and have no single step hardware instruction. + (extendhfdf2): Only expand through SFmode if we don't have a + single-step hardware instruction. + * config/arm/vfp.md (*truncdfhf2): New. + (extendhfdf2): Likewise. + 2016-11-23 James Greenhalgh * targhooks.c (default_floatn_mode): Enable _Float16 if a target diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index abd3276f131..118b93b9430 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -245,7 +245,6 @@ static bool arm_output_addr_const_extra (FILE *, rtx); static bool arm_allocate_stack_slots_for_args (void); static bool arm_warn_func_return (tree); static tree arm_promoted_type (const_tree t); -static tree arm_convert_to_type (tree type, tree expr); static bool arm_scalar_mode_supported_p (machine_mode); static bool arm_frame_pointer_required (void); static bool arm_can_eliminate (const int, const int); @@ -654,9 +653,6 @@ static const struct attribute_spec arm_attribute_table[] = #undef TARGET_PROMOTED_TYPE #define TARGET_PROMOTED_TYPE arm_promoted_type -#undef TARGET_CONVERT_TO_TYPE -#define TARGET_CONVERT_TO_TYPE arm_convert_to_type - #undef TARGET_SCALAR_MODE_SUPPORTED_P #define TARGET_SCALAR_MODE_SUPPORTED_P arm_scalar_mode_supported_p @@ -2535,6 +2531,11 @@ arm_init_libfuncs (void) ? "__gnu_h2f_ieee" : "__gnu_h2f_alternative")); + set_conv_libfunc (trunc_optab, HFmode, DFmode, + (arm_fp16_format == ARM_FP16_FORMAT_IEEE + ? "__gnu_d2h_ieee" + : "__gnu_d2h_alternative")); + /* Arithmetic. */ set_optab_libfunc (add_optab, HFmode, NULL); set_optab_libfunc (sdiv_optab, HFmode, NULL); @@ -5259,6 +5260,8 @@ arm_libcall_uses_aapcs_base (const_rtx libcall) SFmode)); add_libcall (libcall_htab, convert_optab_libfunc (trunc_optab, SFmode, DFmode)); + add_libcall (libcall_htab, + convert_optab_libfunc (trunc_optab, HFmode, DFmode)); } return libcall && libcall_htab->find (libcall) != NULL; @@ -22514,23 +22517,6 @@ arm_promoted_type (const_tree t) return NULL_TREE; } -/* Implement TARGET_CONVERT_TO_TYPE. - Specifically, this hook implements the peculiarity of the ARM - half-precision floating-point C semantics that requires conversions between - __fp16 to or from double to do an intermediate conversion to float. */ - -static tree -arm_convert_to_type (tree type, tree expr) -{ - tree fromtype = TREE_TYPE (expr); - if (!SCALAR_FLOAT_TYPE_P (fromtype) || !SCALAR_FLOAT_TYPE_P (type)) - return NULL_TREE; - if ((TYPE_PRECISION (fromtype) == 16 && TYPE_PRECISION (type) > 32) - || (TYPE_PRECISION (type) == 16 && TYPE_PRECISION (fromtype) > 32)) - return convert (type, convert (float_type_node, expr)); - return NULL_TREE; -} - /* Implement TARGET_SCALAR_MODE_SUPPORTED_P. This simply adds HFmode as a supported mode; even though we don't implement arithmetic on this type directly, it's supported by diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h index 7ad0fbf0d80..15930f0ca8f 100644 --- a/gcc/config/arm/arm.h +++ b/gcc/config/arm/arm.h @@ -179,6 +179,11 @@ extern void (*arm_lang_output_object_attributes_hook)(void); #define TARGET_FP16 \ (ARM_FPU_FSET_HAS (TARGET_FPU_FEATURES, FPU_FL_FP16)) +/* FPU supports converting between HFmode and DFmode in a single hardware + step. */ +#define TARGET_FP16_TO_DOUBLE \ + (TARGET_HARD_FLOAT && (TARGET_FP16 && TARGET_VFP5)) + /* FPU supports fused-multiply-add operations. */ #define TARGET_FMA (TARGET_FPU_REV >= 4) diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index ccae728bd16..5523baf5099 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -5182,20 +5182,34 @@ "" ) -;; DFmode to HFmode conversions have to go through SFmode. +;; DFmode to HFmode conversions on targets without a single-step hardware +;; instruction for it would have to go through SFmode. This is dangerous +;; as it introduces double rounding. +;; +;; Disable this pattern unless we are in an unsafe math mode, or we have +;; a single-step instruction. + (define_expand "truncdfhf2" - [(set (match_operand:HF 0 "general_operand" "") + [(set (match_operand:HF 0 "s_register_operand" "") (float_truncate:HF - (match_operand:DF 1 "general_operand" "")))] - "TARGET_EITHER" - " - { - rtx op1; - op1 = convert_to_mode (SFmode, operands[1], 0); - op1 = convert_to_mode (HFmode, op1, 0); - emit_move_insn (operands[0], op1); - DONE; - }" + (match_operand:DF 1 "s_register_operand" "")))] + "(TARGET_EITHER && flag_unsafe_math_optimizations) + || (TARGET_32BIT && TARGET_FP16_TO_DOUBLE)" +{ + /* We don't have a direct instruction for this, so we must be in + an unsafe math mode, and going via SFmode. */ + + if (!(TARGET_32BIT && TARGET_FP16_TO_DOUBLE)) + { + rtx op1; + op1 = convert_to_mode (SFmode, operands[1], 0); + op1 = convert_to_mode (HFmode, op1, 0); + emit_move_insn (operands[0], op1); + DONE; + } + /* Otherwise, we will pick this up as a single instruction with + no intermediary rounding. */ +} ) ;; Zero and sign extension instructions. @@ -5689,19 +5703,28 @@ "" ) -;; HFmode -> DFmode conversions have to go through SFmode. +;; HFmode -> DFmode conversions where we don't have an instruction for it +;; must go through SFmode. +;; +;; This is always safe for an extend. + (define_expand "extendhfdf2" - [(set (match_operand:DF 0 "general_operand" "") - (float_extend:DF (match_operand:HF 1 "general_operand" "")))] + [(set (match_operand:DF 0 "s_register_operand" "") + (float_extend:DF (match_operand:HF 1 "s_register_operand" "")))] "TARGET_EITHER" - " - { - rtx op1; - op1 = convert_to_mode (SFmode, operands[1], 0); - op1 = convert_to_mode (DFmode, op1, 0); - emit_insn (gen_movdf (operands[0], op1)); - DONE; - }" +{ + /* We don't have a direct instruction for this, so go via SFmode. */ + if (!(TARGET_32BIT && TARGET_FP16_TO_DOUBLE)) + { + rtx op1; + op1 = convert_to_mode (SFmode, operands[1], 0); + op1 = convert_to_mode (DFmode, op1, 0); + emit_insn (gen_movdf (operands[0], op1)); + DONE; + } + /* Otherwise, we're done producing RTL and will pick up the correct + pattern to do this with one rounding-step in a single instruction. */ +} ) ;; Move insns (including loads and stores) diff --git a/gcc/config/arm/vfp.md b/gcc/config/arm/vfp.md index ce56e160c04..f83dc9b130e 100644 --- a/gcc/config/arm/vfp.md +++ b/gcc/config/arm/vfp.md @@ -1507,6 +1507,26 @@ (set_attr "type" "f_cvt")] ) +(define_insn "*truncdfhf2" + [(set (match_operand:HF 0 "s_register_operand" "=t") + (float_truncate:HF (match_operand:DF 1 "s_register_operand" "w")))] + "TARGET_32BIT && TARGET_FP16_TO_DOUBLE" + "vcvtb%?.f16.f64\\t%0, %P1" + [(set_attr "predicable" "yes") + (set_attr "predicable_short_it" "no") + (set_attr "type" "f_cvt")] +) + +(define_insn "*extendhfdf2" + [(set (match_operand:DF 0 "s_register_operand" "=w") + (float_extend:DF (match_operand:HF 1 "s_register_operand" "t")))] + "TARGET_32BIT && TARGET_FP16_TO_DOUBLE" + "vcvtb%?.f64.f16\\t%P0, %1" + [(set_attr "predicable" "yes") + (set_attr "predicable_short_it" "no") + (set_attr "type" "f_cvt")] +) + (define_insn "truncsfhf2" [(set (match_operand:HF 0 "s_register_operand" "=t") (float_truncate:HF (match_operand:SF 1 "s_register_operand" "t")))] diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 44c1f262e35..0d5909052d4 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2016-11-23 James Greenhalgh + + * gcc.target/arm/fp16-rounding-alt-1.c (ROUNDED): Change expected + result. + * gcc.target/arm/fp16-rounding-ieee-1.c (ROUNDED): Change expected + result. + 2016-11-23 James Greenhalgh * lib/target-supports.exp (check_effective_target_float16): Add diff --git a/gcc/testsuite/gcc.target/arm/fp16-rounding-alt-1.c b/gcc/testsuite/gcc.target/arm/fp16-rounding-alt-1.c index 1c15b613239..27bb40dcfee 100644 --- a/gcc/testsuite/gcc.target/arm/fp16-rounding-alt-1.c +++ b/gcc/testsuite/gcc.target/arm/fp16-rounding-alt-1.c @@ -1,6 +1,6 @@ -/* Test intermediate rounding of double to float and then to __fp16, using - an example of a number that would round differently if it went directly - from double to __fp16. */ +/* Test that rounding double to __fp16 happens directly, using an example + of a number that would round differently if it went from double to + __fp16 via float. */ /* { dg-do run } */ /* { dg-require-effective-target arm_fp16_alternative_ok } */ @@ -11,8 +11,8 @@ /* The original double value. */ #define ORIG 0x1.0020008p0 -/* The expected (double)((__fp16)((float)ORIG)) value. */ -#define ROUNDED 0x1.0000000p0 +/* The expected (double)((__fp16)ORIG) value. */ +#define ROUNDED 0x1.0040000p0 typedef union u { __fp16 f; diff --git a/gcc/testsuite/gcc.target/arm/fp16-rounding-ieee-1.c b/gcc/testsuite/gcc.target/arm/fp16-rounding-ieee-1.c index 866d4d82403..194dc9dc3bd 100644 --- a/gcc/testsuite/gcc.target/arm/fp16-rounding-ieee-1.c +++ b/gcc/testsuite/gcc.target/arm/fp16-rounding-ieee-1.c @@ -1,6 +1,6 @@ -/* Test intermediate rounding of double to float and then to __fp16, using - an example of a number that would round differently if it went directly - from double to __fp16. */ +/* Test that rounding double to __fp16 happens directly, using an example + of a number that would round differently if it went from double to + __fp16 via float. */ /* { dg-do run } */ /* { dg-options "-mfp16-format=ieee" } */ @@ -10,8 +10,8 @@ /* The original double value. */ #define ORIG 0x1.0020008p0 -/* The expected (double)((__fp16)((float)ORIG)) value. */ -#define ROUNDED 0x1.0000000p0 +/* The expected (double)((__fp16)ORIG) value. */ +#define ROUNDED 0x1.0040000p0 typedef union u { __fp16 f; -- 2.30.2