This is a clean-up patch for IEEE 128-bit floating point support on the PowerPC.
The main change is to change the way comparisons are done when IEEE 128-bit floating is emulated. Previously, I had added special __cmpkf2 and __cmpukf2 functions that were modeled on the CR bits that the XSCMPUQP instruction sets. I have changed this to use the standard EQ, GE, LE, and UNORD functions provided by glibc/libgcc's software floating point emulation functions. I've done some debugging on issues with _Complex __float128, and I think at the moment it is just not feasible to support _Complex __float128 in GCC 6.x. The main problem is the way the IFmode/KFmode types are added as fractional floating point types which prevents them from being default types for conversion. I think in order to support this, the machine independent type system is going to have to be tinkered with. However, since we are now coming to the end of stage3 it is not the time to be making these changes. I added documentation to state that right now -mfloat128 is only enabled on 64-bit PowerPC Linux systems. There is some desire for this to be eventually supported on other systems like FreeBSD, but I wanted to set expectations for users interested in the this. I also documented that complex __float128 currently does not work. I added an undocumented debug switch (-mfloat128-convert) that enables default conversions between IBM extended double and IEEE 128-bit floating point, to enable further debugging of the complex __float128 support in the future. Finally, I noticed if you use -mabi=ieeelongdouble, that it generated calls to convert between long double and explicit __float128, even though they are the same representation, and fixed that. Going forward, patch #11 will enable the software emulation library. At the moment, there is no support for converting between decimal types and __float128, nor for the complex __float128 support. These are being worked on, and should be done in the GCC 7.x time frame. However, it is important to add the software emulation support in GCC 6.x so that most users that want to use IEEE 128-bit floating point can use it, and that we can work on the glibc issues to fully support __float128 in the GCC 7.x time frame. 2015-12-29 Michael Meissner <meiss...@linux.vnet.ibm.com> * config/rs6000/rs6000.c (init_float128_ieee): Remove IEEE 128-bit comparison functions in cmp_optab and ucmp_optab. (rs6000_generate_compare): Rewrite IEEE 128-bit floating point software emulation comparisons to only use __eqkf2, __gekf2, __lekf2, and __unordkf2 functions. (rs6000_invalid_binary_op): Add support for -mfloat128-convert. * config/rs6000/rs6000-c.c (rs6000_cpu_cpp_builtins): Define __FLOAT128_HARDWARE__ if hardware IEEE 128-bit support is available. * config/rs6000/rs6000.opt (-mfloat128-convert): Add debug switch to allow IBM extended double and IEEE 128-bit floating point to be converted with default conversions. * config/rs6000/rs6000.md (extendkftf2): Add converters between KFmode and TFmode if -mabi=ieeelongdouble. (trunctfkf2): Likewise. (ieee128_mfvsrd): Split 64-bit integer conversions into 32-bit and 64-bit insns. (ieee128_mfvsrd_64bit): Likewise. (ieee128_mfvsrd_32bit): Likewise. (ieee128_mtvsrd): Likewise. (ieee128_mtvsrd_64bit): Likewise. (ieee128_mtvsrd_32bit): Likewise. * doc/extend.texi (Floating Types): Document that complex __float128 does not work currently. * doc/invoke.texi (RS/6000 and PowerPC Options): Document that -mfloat128 is only supported on PowerPC 64-bit Linux systems. -- Michael Meissner, IBM IBM, M/S 2506R, 550 King Street, Littleton, MA 01460-6245, USA email: meiss...@linux.vnet.ibm.com, phone: +1 (978) 899-4797
Index: gcc/config/rs6000/rs6000.c =================================================================== --- gcc/config/rs6000/rs6000.c (.../svn+ssh://meiss...@gcc.gnu.org/svn/gcc/trunk/gcc/config/rs6000) (revision 231978) +++ gcc/config/rs6000/rs6000.c (.../gcc/config/rs6000) (working copy) @@ -16501,8 +16501,6 @@ init_float128_ieee (machine_mode mode) set_optab_libfunc (lt_optab, mode, "__ltkf2"); set_optab_libfunc (le_optab, mode, "__lekf2"); set_optab_libfunc (unord_optab, mode, "__unordkf2"); - set_optab_libfunc (cmp_optab, mode, "__cmpokf2"); /* fcmpo */ - set_optab_libfunc (ucmp_optab, mode, "__cmpukf2"); /* fcmpu */ set_conv_libfunc (sext_optab, mode, SFmode, "__extendsfkf2"); set_conv_libfunc (sext_optab, mode, DFmode, "__extenddfkf2"); @@ -20297,7 +20295,9 @@ rs6000_generate_compare (rtx cmp, machin rtx op0 = XEXP (cmp, 0); rtx op1 = XEXP (cmp, 1); - if (FLOAT_MODE_P (mode)) + if (!TARGET_FLOAT128_HW && FLOAT128_VECTOR_P (mode)) + comp_mode = CCmode; + else if (FLOAT_MODE_P (mode)) comp_mode = CCFPmode; else if (code == GTU || code == LTU || code == GEU || code == LEU) @@ -20503,106 +20503,77 @@ rs6000_generate_compare (rtx cmp, machin emit_insn (cmp); } - /* IEEE 128-bit support in VSX registers. If we do not have IEEE 128-bit - hardware, the comparison functions (__cmpokf2 and __cmpukf2) returns 0..15 - that is laid out the same way as the PowerPC CR register would for a - normal floating point comparison from the fcmpo and fcmpu - instructions. */ - else if (!TARGET_FLOAT128_HW && FLOAT128_IEEE_P (mode)) + /* IEEE 128-bit support in VSX registers when we do not have hardware + support. */ + else if (!TARGET_FLOAT128_HW && FLOAT128_VECTOR_P (mode)) { - rtx and_reg = gen_reg_rtx (SImode); + rtx libfunc = NULL_RTX; + bool uneq_or_ltgt = false; rtx dest = gen_reg_rtx (SImode); - rtx libfunc = optab_libfunc (ucmp_optab, mode); - HOST_WIDE_INT mask_value = 0; - - /* Values that __cmpokf2/__cmpukf2 returns. */ -#define PPC_CMP_UNORDERED 0x1 /* isnan (a) || isnan (b). */ -#define PPC_CMP_EQUAL 0x2 /* a == b. */ -#define PPC_CMP_GREATER_THEN 0x4 /* a > b. */ -#define PPC_CMP_LESS_THEN 0x8 /* a < b. */ switch (code) { case EQ: - mask_value = PPC_CMP_EQUAL; - code = NE; - break; - case NE: - mask_value = PPC_CMP_EQUAL; - code = EQ; + libfunc = optab_libfunc (eq_optab, mode); break; case GT: - mask_value = PPC_CMP_GREATER_THEN; - code = NE; - break; - case GE: - mask_value = PPC_CMP_GREATER_THEN | PPC_CMP_EQUAL; - code = NE; + libfunc = optab_libfunc (ge_optab, mode); break; case LT: - mask_value = PPC_CMP_LESS_THEN; - code = NE; - break; - case LE: - mask_value = PPC_CMP_LESS_THEN | PPC_CMP_EQUAL; - code = NE; - break; - - case UNLE: - mask_value = PPC_CMP_GREATER_THEN; - code = EQ; + libfunc = optab_libfunc (le_optab, mode); break; - case UNLT: - mask_value = PPC_CMP_GREATER_THEN | PPC_CMP_EQUAL; - code = EQ; + case UNORDERED: + case ORDERED: + libfunc = optab_libfunc (unord_optab, mode); + code = (code == UNORDERED) ? NE : EQ; break; case UNGE: - mask_value = PPC_CMP_LESS_THEN; - code = EQ; + case UNGT: + libfunc = optab_libfunc (le_optab, mode); + code = (code == UNGE) ? GE : GT; break; - case UNGT: - mask_value = PPC_CMP_LESS_THEN | PPC_CMP_EQUAL; - code = EQ; + case UNLE: + case UNLT: + libfunc = optab_libfunc (ge_optab, mode); + code = (code == UNLE) ? LE : LT; break; case UNEQ: - mask_value = PPC_CMP_EQUAL | PPC_CMP_UNORDERED; - code = NE; - case LTGT: - mask_value = PPC_CMP_EQUAL | PPC_CMP_UNORDERED; - code = EQ; - break; - - case UNORDERED: - mask_value = PPC_CMP_UNORDERED; - code = NE; - break; - - case ORDERED: - mask_value = PPC_CMP_UNORDERED; - code = EQ; + libfunc = optab_libfunc (le_optab, mode); + uneq_or_ltgt = true; + code = (code = UNEQ) ? NE : EQ; break; default: gcc_unreachable (); } - gcc_assert (mask_value != 0); - and_reg = emit_library_call_value (libfunc, and_reg, LCT_CONST, SImode, 2, - op0, mode, op1, mode); - - emit_insn (gen_andsi3 (dest, and_reg, GEN_INT (mask_value))); - compare_result = gen_reg_rtx (CCmode); - comp_mode = CCmode; + gcc_assert (libfunc); + dest = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST, + SImode, 2, op0, mode, op1, mode); + + /* If this is UNEQ or LTGT, we call __lekf2, which returns -1 for less + than, 0 for equal, +1 for greater, and +2 for nan. We add 1, to give + a value of 0..3, and then do and AND immediate of 1 to isolate whether + it is 0/Nan (i.e. bottom bit is 0), or less than/greater than + (i.e. bottom bit is 1). */ + if (uneq_or_ltgt) + { + rtx add_result = gen_reg_rtx (SImode); + rtx and_result = gen_reg_rtx (SImode); + emit_insn (gen_addsi3 (add_result, dest, GEN_INT (1))); + emit_insn (gen_andsi3 (and_result, add_result, GEN_INT (1))); + dest = and_result; + } emit_insn (gen_rtx_SET (compare_result, gen_rtx_COMPARE (comp_mode, dest, const0_rtx))); @@ -20706,24 +20677,29 @@ rs6000_invalid_binary_op (int op ATTRIBU mode2 = GET_MODE_INNER (mode2); /* Don't allow IEEE 754R 128-bit binary floating point and IBM extended - double to intermix. */ + double to intermix unless -mfloat128-convert. */ if (mode1 == mode2) return NULL; - if ((mode1 == KFmode && mode2 == IFmode) - || (mode1 == IFmode && mode2 == KFmode)) - return N_("__float128 and __ibm128 cannot be used in the same expression"); - - if (TARGET_IEEEQUAD - && ((mode1 == IFmode && mode2 == TFmode) - || (mode1 == TFmode && mode2 == IFmode))) - return N_("__ibm128 and long double cannot be used in the same expression"); - - if (!TARGET_IEEEQUAD - && ((mode1 == KFmode && mode2 == TFmode) - || (mode1 == TFmode && mode2 == KFmode))) - return N_("__float128 and long double cannot be used in the same " - "expression"); + if (!TARGET_FLOAT128_CVT) + { + if ((mode1 == KFmode && mode2 == IFmode) + || (mode1 == IFmode && mode2 == KFmode)) + return N_("__float128 and __ibm128 cannot be used in the same " + "expression"); + + if (TARGET_IEEEQUAD + && ((mode1 == IFmode && mode2 == TFmode) + || (mode1 == TFmode && mode2 == IFmode))) + return N_("__ibm128 and long double cannot be used in the same " + "expression"); + + if (!TARGET_IEEEQUAD + && ((mode1 == KFmode && mode2 == TFmode) + || (mode1 == TFmode && mode2 == KFmode))) + return N_("__float128 and long double cannot be used in the same " + "expression"); + } return NULL; } Index: gcc/config/rs6000/rs6000-c.c =================================================================== --- gcc/config/rs6000/rs6000-c.c (.../svn+ssh://meiss...@gcc.gnu.org/svn/gcc/trunk/gcc/config/rs6000) (revision 231978) +++ gcc/config/rs6000/rs6000-c.c (.../gcc/config/rs6000) (working copy) @@ -412,6 +412,8 @@ rs6000_cpu_cpp_builtins (cpp_reader *pfi builtin_define ("__RSQRTEF__"); if (TARGET_FLOAT128) builtin_define ("__FLOAT128__"); + if (TARGET_FLOAT128_HW) + builtin_define ("__FLOAT128_HARDWARE__"); if (TARGET_EXTRA_BUILTINS && cpp_get_options (pfile)->lang != CLK_ASM) { Index: gcc/config/rs6000/rs6000.opt =================================================================== --- gcc/config/rs6000/rs6000.opt (.../svn+ssh://meiss...@gcc.gnu.org/svn/gcc/trunk/gcc/config/rs6000) (revision 231978) +++ gcc/config/rs6000/rs6000.opt (.../gcc/config/rs6000) (working copy) @@ -632,3 +632,7 @@ Enable/disable IEEE 128-bit floating poi mfloat128-hardware Target Report Mask(FLOAT128_HW) Var(rs6000_isa_flags) Enable/disable using IEEE 128-bit floating point instructions. + +mfloat128-convert +Target Undocumented Mask(FLOAT128_CVT) Var(rs6000_isa_flags) +Enable/disable default conversions between __float128 & long double. Index: gcc/config/rs6000/rs6000.md =================================================================== --- gcc/config/rs6000/rs6000.md (.../svn+ssh://meiss...@gcc.gnu.org/svn/gcc/trunk/gcc/config/rs6000) (revision 231978) +++ gcc/config/rs6000/rs6000.md (.../gcc/config/rs6000) (working copy) @@ -13352,6 +13352,40 @@ (define_insn "extend<SFDF:mode><IEEE128: "xscvdpqp %0,%1" [(set_attr "type" "vecfloat")]) +;; Conversion between KFmode and TFmode if TFmode is ieee 128-bit floating +;; point is a simple copy. +(define_insn_and_split "extendkftf2" + [(set (match_operand:TF 0 "vsx_register_operand" "=wa,?wa") + (float_extend:TF (match_operand:KF 1 "vsx_register_operand" "0,wa")))] + "TARGET_FLOAT128 && TARGET_IEEEQUAD" + "@ + # + xxlor %x0,%x1,%x1" + "&& reload_completed && REGNO (operands[0]) == REGNO (operands[1])" + [(const_int 0)] +{ + emit_note (NOTE_INSN_DELETED); + DONE; +} + [(set_attr "type" "*,vecsimple") + (set_attr "length" "0,4")]) + +(define_insn_and_split "trunctfkf2" + [(set (match_operand:KF 0 "vsx_register_operand" "=wa,?wa") + (float_extend:KF (match_operand:TF 1 "vsx_register_operand" "0,wa")))] + "TARGET_FLOAT128 && TARGET_IEEEQUAD" + "@ + # + xxlor %x0,%x1,%x1" + "&& reload_completed && REGNO (operands[0]) == REGNO (operands[1])" + [(const_int 0)] +{ + emit_note (NOTE_INSN_DELETED); + DONE; +} + [(set_attr "type" "*,vecsimple") + (set_attr "length" "0,4")]) + (define_insn "trunc<mode>df2_hw" [(set (match_operand:DF 0 "altivec_register_operand" "=v") (float_truncate:DF @@ -13476,7 +13510,7 @@ (define_insn "*xscv<su>dqp_<mode>" "xscv<su>dqp %0,%1" [(set_attr "type" "vecfloat")]) -(define_insn "*ieee128_mfvsrd" +(define_insn "*ieee128_mfvsrd_64bit" [(set (match_operand:DI 0 "reg_or_indexed_operand" "=wr,Z,wi") (unspec:DI [(match_operand:V2DI 1 "altivec_register_operand" "v,v,v")] UNSPEC_IEEE128_MOVE))] @@ -13487,6 +13521,17 @@ (define_insn "*ieee128_mfvsrd" xxlor %x0,%x1,%x1" [(set_attr "type" "mftgpr,vecsimple,fpstore")]) + +(define_insn "*ieee128_mfvsrd_32bit" + [(set (match_operand:DI 0 "reg_or_indexed_operand" "=Z,wi") + (unspec:DI [(match_operand:V2DI 1 "altivec_register_operand" "v,v")] + UNSPEC_IEEE128_MOVE))] + "TARGET_FLOAT128_HW && !TARGET_POWERPC64" + "@ + stxsdx %x1,%y0 + xxlor %x0,%x1,%x1" + [(set_attr "type" "vecsimple,fpstore")]) + (define_insn "*ieee128_mfvsrwz" [(set (match_operand:SI 0 "reg_or_indexed_operand" "=r,Z") (unspec:SI [(match_operand:V2DI 1 "altivec_register_operand" "v,v")] @@ -13512,17 +13557,27 @@ (define_insn "*ieee128_mtvsrw" [(set_attr "type" "mffgpr,fpload,mffgpr,fpload")]) -(define_insn "*ieee128_mtvsrd" +(define_insn "*ieee128_mtvsrd_64bit" [(set (match_operand:V2DI 0 "altivec_register_operand" "=v,v,v") (unspec:V2DI [(match_operand:DI 1 "nonimmediate_operand" "wr,Z,wi")] UNSPEC_IEEE128_MOVE))] - "TARGET_FLOAT128_HW" + "TARGET_FLOAT128_HW && TARGET_POWERPC64" "@ mtvsrd %x0,%1 lxsdx %x0,%y1 xxlor %x0,%x1,%x1" [(set_attr "type" "mffgpr,fpload,vecsimple")]) +(define_insn "*ieee128_mtvsrd_32bit" + [(set (match_operand:V2DI 0 "altivec_register_operand" "=v,v") + (unspec:V2DI [(match_operand:DI 1 "nonimmediate_operand" "Z,wi")] + UNSPEC_IEEE128_MOVE))] + "TARGET_FLOAT128_HW && !TARGET_POWERPC64" + "@ + lxsdx %x0,%y1 + xxlor %x0,%x1,%x1" + [(set_attr "type" "fpload,vecsimple")]) + ;; IEEE 128-bit instructions with round to odd semantics (define_insn "*trunc<mode>df2_odd" [(set (match_operand:DF 0 "vsx_register_operand" "=v") Index: gcc/doc/extend.texi =================================================================== --- gcc/doc/extend.texi (.../svn+ssh://meiss...@gcc.gnu.org/svn/gcc/trunk/gcc/doc) (revision 231978) +++ gcc/doc/extend.texi (.../gcc/doc) (working copy) @@ -954,22 +954,20 @@ typedef _Complex float __attribute__((mo typedef _Complex float __attribute__((mode(XC))) _Complex80; @end smallexample -On PowerPC Linux, Freebsd and Darwin systems, the default for -@code{long double} is to use the IBM extended floating point format -that uses a pair of @code{double} values to extend the precision. -This means that the mode @code{TCmode} was already used by the -traditional IBM long double format, and you would need to use the mode -@code{KCmode}: +On PowerPC 64-bit Linux systems there are currently problems in using +the complex @code{__float128} type. When these problems are fixed, +you would use: @smallexample typedef _Complex float __attribute__((mode(KC))) _Complex128; @end smallexample -Not all targets support additional floating-point types. @code{__float80} -and @code{__float128} types are supported on x86 and IA-64 targets. -The @code{__float128} type is supported on hppa HP-UX. -The @code{__float128} type is supported on PowerPC systems by default -if the vector scalar instruction set (VSX) is enabled. +Not all targets support additional floating-point types. +@code{__float80} and @code{__float128} types are supported on x86 and +IA-64 targets. The @code{__float128} type is supported on hppa HP-UX. +The @code{__float128} type is supported on PowerPC 64-bit Linux +systems by default if the vector scalar instruction set (VSX) is +enabled. On the PowerPC, @code{__ibm128} provides access to the IBM extended double format, and it is intended to be used by the library functions Index: gcc/doc/invoke.texi =================================================================== --- gcc/doc/invoke.texi (.../svn+ssh://meiss...@gcc.gnu.org/svn/gcc/trunk/gcc/doc) (revision 231978) +++ gcc/doc/invoke.texi (.../gcc/doc) (working copy) @@ -19690,7 +19690,8 @@ hardware instructions. The VSX instruction set (@option{-mvsx}, @option{-mcpu=power7}, or @option{-mcpu=power8}) must be enabled to use the @option{-mfloat128} -option. +option. The @code{-mfloat128} option only works on PowerPC 64-bit +Linux systems. @item -mfloat128-hardware @itemx -mno-float128-hardware