The following patch fixes a bug in the rs6000 DFP handling of some _Decimal128 patterns, namely the unary patterns neg, abs and nabs. These patterns use the legacy binary floating point instructions fneg, fabs and fnabs to twiddle the signbit. However, these instructions only operate on the first register in the even/odd register pair, so for destiniation and source operands that do not overlap, the destination's odd register will be left uninitialized. This patch fixes that.
I also noticed there is no current way for the ABS and NABS patterns to be generated, so I created some generic builtins so they can be. I have also created testcases to verify we generate correct code. This bootstrapped and regtested with no regressions. Is this ok for trunk? David, since this is a bad code generation bug, is it ok to backport just the *negtd2_fpr portion of the patch to the open release branches? Peter gcc/ * builtins.def (BUILT_IN_FABSD32): New DFP ABS builtin. (BUILT_IN_FABSD64): Likewise. (BUILT_IN_FABSD128): Likewise. * builtins.c (expand_builtin): Add support for new DFP ABS builtins. (fold_builtin_1): Likewise. * config/rs6000/dfp.md (*negtd2_fpr): Handle non-overlapping destination and source operands. (*abstd2_fpr): Likewise. (*nabstd2_fpr): Likewise. gcc/testsuite/ * gcc.target/powerpc/dfp-dd-2.c: New test. * gcc.target/powerpc/dfp-td-2.c: Likewise. Index: gcc/builtins.c =================================================================== --- gcc/builtins.c (revision 201779) +++ gcc/builtins.c (working copy) @@ -5865,6 +5865,9 @@ expand_builtin (tree exp, rtx target, rt switch (fcode) { CASE_FLT_FN (BUILT_IN_FABS): + case BUILT_IN_FABSD32: + case BUILT_IN_FABSD64: + case BUILT_IN_FABSD128: target = expand_builtin_fabs (exp, target, subtarget); if (target) return target; @@ -10314,6 +10317,9 @@ fold_builtin_1 (location_t loc, tree fnd return fold_builtin_strlen (loc, type, arg0); CASE_FLT_FN (BUILT_IN_FABS): + case BUILT_IN_FABSD32: + case BUILT_IN_FABSD64: + case BUILT_IN_FABSD128: return fold_builtin_fabs (loc, arg0, type); case BUILT_IN_ABS: Index: gcc/builtins.def =================================================================== --- gcc/builtins.def (revision 201779) +++ gcc/builtins.def (working copy) @@ -257,6 +257,9 @@ DEF_C99_BUILTIN (BUILT_IN_EXPM1L, DEF_LIB_BUILTIN (BUILT_IN_FABS, "fabs", BT_FN_DOUBLE_DOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_C99_C90RES_BUILTIN (BUILT_IN_FABSF, "fabsf", BT_FN_FLOAT_FLOAT, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_C99_C90RES_BUILTIN (BUILT_IN_FABSL, "fabsl", BT_FN_LONGDOUBLE_LONGDOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_FABSD32, "fabsd32", BT_FN_DFLOAT32_DFLOAT32, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_FABSD64, "fabsd64", BT_FN_DFLOAT64_DFLOAT64, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_FABSD128, "fabsd128", BT_FN_DFLOAT128_DFLOAT128, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_C99_BUILTIN (BUILT_IN_FDIM, "fdim", BT_FN_DOUBLE_DOUBLE_DOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO) DEF_C99_BUILTIN (BUILT_IN_FDIMF, "fdimf", BT_FN_FLOAT_FLOAT_FLOAT, ATTR_MATHFN_FPROUNDING_ERRNO) DEF_C99_BUILTIN (BUILT_IN_FDIML, "fdiml", BT_FN_LONGDOUBLE_LONGDOUBLE_LONGDOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO) Index: gcc/config/rs6000/dfp.md =================================================================== --- gcc/config/rs6000/dfp.md (revision 201779) +++ gcc/config/rs6000/dfp.md (working copy) @@ -135,8 +135,15 @@ (define_insn "*negtd2_fpr" [(set (match_operand:TD 0 "gpc_reg_operand" "=d") (neg:TD (match_operand:TD 1 "gpc_reg_operand" "d")))] "TARGET_HARD_FLOAT && TARGET_FPRS" - "fneg %0,%1" - [(set_attr "type" "fp")]) + "* +{ + if (REGNO (operands[0]) == REGNO (operands[1])) + return \"fneg %0,%1\"; + else + return \"fneg %0,%1\;fmr %L0,%L1\"; +}" + [(set_attr "type" "fp") + (set_attr "length" "8")]) (define_expand "abstd2" [(set (match_operand:TD 0 "gpc_reg_operand" "") @@ -148,15 +155,29 @@ (define_insn "*abstd2_fpr" [(set (match_operand:TD 0 "gpc_reg_operand" "=d") (abs:TD (match_operand:TD 1 "gpc_reg_operand" "d")))] "TARGET_HARD_FLOAT && TARGET_FPRS" - "fabs %0,%1" - [(set_attr "type" "fp")]) + "* +{ + if (REGNO (operands[0]) == REGNO (operands[1])) + return \"fabs %0,%1\"; + else + return \"fabs %0,%1\;fmr %L0,%L1\"; +}" + [(set_attr "type" "fp") + (set_attr "length" "8")]) (define_insn "*nabstd2_fpr" [(set (match_operand:TD 0 "gpc_reg_operand" "=d") (neg:TD (abs:TD (match_operand:TD 1 "gpc_reg_operand" "d"))))] "TARGET_HARD_FLOAT && TARGET_FPRS" - "fnabs %0,%1" - [(set_attr "type" "fp")]) + "* +{ + if (REGNO (operands[0]) == REGNO (operands[1])) + return \"fnabs %0,%1\"; + else + return \"fnabs %0,%1\;fmr %L0,%L1\"; +}" + [(set_attr "type" "fp") + (set_attr "length" "8")]) ;; Hardware support for decimal floating point operations. Index: gcc/testsuite/gcc.target/powerpc/dfp-dd-2.c =================================================================== --- gcc/testsuite/gcc.target/powerpc/dfp-dd-2.c (revision 0) +++ gcc/testsuite/gcc.target/powerpc/dfp-dd-2.c (revision 0) @@ -0,0 +1,26 @@ +/* Test generation of DFP instructions for POWER6. */ +/* { dg-do compile { target { powerpc*-*-linux* && powerpc_fprs } } } */ +/* { dg-options "-std=gnu99 -O1 -mcpu=power6" } */ + +/* { dg-final { scan-assembler-times "fneg" 1 } } */ +/* { dg-final { scan-assembler-times "fabs" 1 } } */ +/* { dg-final { scan-assembler-times "fnabs" 1 } } */ +/* { dg-final { scan-assembler-times "fmr" 0 } } */ + +_Decimal64 +func1 (_Decimal64 a, _Decimal64 b) +{ + return -b; +} + +_Decimal64 +func2 (_Decimal64 a, _Decimal64 b) +{ + return __builtin_fabsd64 (b); +} + +_Decimal64 +func3 (_Decimal64 a, _Decimal64 b) +{ + return - __builtin_fabsd64 (b); +} Index: gcc/testsuite/gcc.target/powerpc/dfp-td-2.c =================================================================== --- gcc/testsuite/gcc.target/powerpc/dfp-td-2.c (revision 0) +++ gcc/testsuite/gcc.target/powerpc/dfp-td-2.c (revision 0) @@ -0,0 +1,26 @@ +/* Test generation of DFP instructions for POWER6. */ +/* { dg-do compile { target { powerpc*-*-linux* && powerpc_fprs } } } */ +/* { dg-options "-std=gnu99 -O1 -mcpu=power6" } */ + +/* { dg-final { scan-assembler-times "fneg" 1 } } */ +/* { dg-final { scan-assembler-times "fabs" 1 } } */ +/* { dg-final { scan-assembler-times "fnabs" 1 } } */ +/* { dg-final { scan-assembler-times "fmr" 3 } } */ + +_Decimal128 +func1 (_Decimal128 a, _Decimal128 b) +{ + return -b; +} + +_Decimal128 +func2 (_Decimal128 a, _Decimal128 b) +{ + return __builtin_fabsd128 (b); +} + +_Decimal128 +func3 (_Decimal128 a, _Decimal128 b) +{ + return - __builtin_fabsd128 (b); +}