This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] generate power6 DFP instructions


This patch adds support for generating POWER6 decimal floating point
instructions for existing options -mcpu=power6 or -mdfp.  It doesn't
support using DFP instructions for SDmode (decimal32), which will be
added when GCC can follow the PowerPC ELF ABIs to put SDmode in floating
point registers; Peter Bergner is working on that.  It doesn't provide a
conversion from TDmode to DDmode, which I will submit separately because
I have questions about how it ought to be done.

Conversions between decimal float types and unsigned integer types are
done a bit differently from binary float conversions to preserve
accuracy for very large integers that are powers of ten.

Tested on powerpc64-linux with -m32/-m64, with some testing on POWER6
hardware for DFP tests; OK for trunk?

2007-08-17  Janis Johnson  <janis187@us.ibm.com>

gcc/
	* optabs.c (expand_float): Convert unsigned integer as signed only
	if it provides sufficient accuracy; add mode argument to real_2expN.
	(expand_fix): Fix comment typos; extend binary float into mode
	wider than destination for converion to unsigned integer; add mode
	argument to real_2expN.
	* real.c (real_2expN): Add mode argument to special-case decimal
	float values.
	* real.h (real_2expN): Ditto.
	* fixed-value.c (check_real_for_fixed_mode): Add mode argument to
	real_2expN.
	(fixed_from_string): Ditto.
	(fixed_to_decimal): Ditto.
	(fixed_convert_from_real): Ditto.
	(real_convert_from_fixed): Ditto.
	* config/rs6000/rs6000.md (FP): Include DD and TD modes.
	* config/rs6000/dfp.md (extendddtd2, adddd3, addtd3, subdd3, subtd3,
	muldd3, multd3, divdd3, divtd3, cmpdd_internal1, cmptd_internal1,
	floatditd2, ftruncdd2, fixdddi2, ftrunctd2, fixddi2): New.

gcc/testsuite/
	* gcc.target/powerpc/dfp-dd.c: New test.
	* gcc.target/powerpc/dfp-td.c: New test.

Index: gcc/optabs.c
===================================================================
--- gcc/optabs.c	(revision 127562)
+++ gcc/optabs.c	(working copy)
@@ -4839,10 +4839,11 @@
 	  }
       }
 
-  /* Unsigned integer, and no way to convert directly.  For binary
-     floating point modes, convert as signed, then conditionally adjust
-     the result.  */
-  if (unsignedp && can_do_signed && !DECIMAL_FLOAT_MODE_P (GET_MODE (to)))
+  /* Unsigned integer, and no way to convert directly.  Convert as signed,
+     then unconditionally adjust the result.  For decimal float values we
+     do this only if we have already determined that a signed conversion
+     provides sufficient accuracy.  */
+  if (unsignedp && (can_do_signed || !DECIMAL_FLOAT_MODE_P (GET_MODE (to))))
     {
       rtx label = gen_label_rtx ();
       rtx temp;
@@ -4933,7 +4934,7 @@
 			       0, label);
 
 
-      real_2expN (&offset, GET_MODE_BITSIZE (GET_MODE (from)));
+      real_2expN (&offset, GET_MODE_BITSIZE (GET_MODE (from)), fmode);
       temp = expand_binop (fmode, add_optab, target,
 			   CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode),
 			   target, 0, OPTAB_LIB_WIDEN);
@@ -5045,14 +5046,16 @@
      anything with a wider integer mode.
 
      This code used to extend FP value into mode wider than the destination.
-     This is not needed.  Consider, for instance conversion from SFmode
+     This is needed for decimal float modes which cannot accurately
+     represent one plus the highest signed number of the same size, but
+     not for binary modes.  Consider, for instance conversion from SFmode
      into DImode.
 
      The hot path through the code is dealing with inputs smaller than 2^63
      and doing just the conversion, so there is no bits to lose.
 
      In the other path we know the value is positive in the range 2^63..2^64-1
-     inclusive.  (as for other imput overflow happens and result is undefined)
+     inclusive.  (as for other input overflow happens and result is undefined)
      So we know that the most important bit set in mantissa corresponds to
      2^63.  The subtraction of 2^63 should not generate any rounding as it
      simply clears out that bit.  The rest is trivial.  */
@@ -5060,15 +5063,16 @@
   if (unsignedp && GET_MODE_BITSIZE (GET_MODE (to)) <= HOST_BITS_PER_WIDE_INT)
     for (fmode = GET_MODE (from); fmode != VOIDmode;
 	 fmode = GET_MODE_WIDER_MODE (fmode))
-      if (CODE_FOR_nothing != can_fix_p (GET_MODE (to), fmode, 0,
-					 &must_trunc))
+      if (CODE_FOR_nothing != can_fix_p (GET_MODE (to), fmode, 0, &must_trunc)
+	  && (!DECIMAL_FLOAT_MODE_P (fmode)
+	      || GET_MODE_BITSIZE (fmode) > GET_MODE_BITSIZE (GET_MODE (to))))
 	{
 	  int bitsize;
 	  REAL_VALUE_TYPE offset;
 	  rtx limit, lab1, lab2, insn;
 
 	  bitsize = GET_MODE_BITSIZE (GET_MODE (to));
-	  real_2expN (&offset, bitsize - 1);
+	  real_2expN (&offset, bitsize - 1, fmode);
 	  limit = CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode);
 	  lab1 = gen_label_rtx ();
 	  lab2 = gen_label_rtx ();
Index: gcc/real.c
===================================================================
--- gcc/real.c	(revision 127562)
+++ gcc/real.c	(working copy)
@@ -2304,7 +2304,7 @@
 /* Fills R with 2**N.  */
 
 void
-real_2expN (REAL_VALUE_TYPE *r, int n)
+real_2expN (REAL_VALUE_TYPE *r, int n, enum machine_mode fmode)
 {
   memset (r, 0, sizeof (*r));
 
@@ -2319,6 +2319,9 @@
       SET_REAL_EXP (r, n);
       r->sig[SIGSZ-1] = SIG_MSB;
     }
+
+  if (DECIMAL_FLOAT_MODE_P (fmode))
+    decimal_real_convert (r, fmode, r);
 }
 
 
Index: gcc/real.h
===================================================================
--- gcc/real.h	(revision 127562)
+++ gcc/real.h	(working copy)
@@ -248,7 +248,7 @@
 
 extern void real_maxval (REAL_VALUE_TYPE *, int, enum machine_mode);
 
-extern void real_2expN (REAL_VALUE_TYPE *, int);
+extern void real_2expN (REAL_VALUE_TYPE *, int, enum machine_mode);
 
 extern unsigned int real_hash (const REAL_VALUE_TYPE *);
 
Index: gcc/fixed-value.c
===================================================================
--- gcc/fixed-value.c	(revision 127562)
+++ gcc/fixed-value.c	(working copy)
@@ -64,8 +64,8 @@
 {
   REAL_VALUE_TYPE max_value, min_value, epsilon_value;
 
-  real_2expN (&max_value, GET_MODE_IBIT (mode));
-  real_2expN (&epsilon_value, -GET_MODE_FBIT (mode));
+  real_2expN (&max_value, GET_MODE_IBIT (mode), mode);
+  real_2expN (&epsilon_value, -GET_MODE_FBIT (mode), mode);
 
   if (SIGNED_FIXED_POINT_MODE_P (mode))
     min_value = REAL_VALUE_NEGATE (max_value);
@@ -102,7 +102,7 @@
       || (temp == FIXED_MAX_EPS && ALL_ACCUM_MODE_P (f->mode)))
     warning (OPT_Woverflow,
 	     "large fixed-point constant implicitly truncated to fixed-point type");
-  real_2expN (&base_value, fbit);
+  real_2expN (&base_value, fbit, mode);
   real_arithmetic (&fixed_value, MULT_EXPR, &real_value, &base_value);
   real_to_integer2 ((HOST_WIDE_INT *)&f->data.low, &f->data.high,
 		    &fixed_value);
@@ -132,7 +132,7 @@
 {
   REAL_VALUE_TYPE real_value, base_value, fixed_value;
 
-  real_2expN (&base_value, GET_MODE_FBIT (f_orig->mode));
+  real_2expN (&base_value, GET_MODE_FBIT (f_orig->mode), f_orig->mode);
   real_from_integer (&real_value, VOIDmode, f_orig->data.low, f_orig->data.high,
 		     UNSIGNED_FIXED_POINT_MODE_P (f_orig->mode));
   real_arithmetic (&fixed_value, RDIV_EXPR, &real_value, &base_value);
@@ -1067,7 +1067,7 @@
 
   real_value = *a;
   f->mode = mode;
-  real_2expN (&base_value, fbit);
+  real_2expN (&base_value, fbit, mode);
   real_arithmetic (&fixed_value, MULT_EXPR, &real_value, &base_value);
   real_to_integer2 ((HOST_WIDE_INT *)&f->data.low, &f->data.high, &fixed_value);
   temp = check_real_for_fixed_mode (&real_value, mode);
@@ -1116,7 +1116,7 @@
 {
   REAL_VALUE_TYPE base_value, fixed_value, real_value;
 
-  real_2expN (&base_value, GET_MODE_FBIT (f->mode));
+  real_2expN (&base_value, GET_MODE_FBIT (f->mode), f->mode);
   real_from_integer (&fixed_value, VOIDmode, f->data.low, f->data.high,
 		     UNSIGNED_FIXED_POINT_MODE_P (f->mode));
   real_arithmetic (&real_value, RDIV_EXPR, &fixed_value, &base_value);
Index: gcc/config/rs6000/dfp.md
===================================================================
--- gcc/config/rs6000/dfp.md	(revision 127562)
+++ gcc/config/rs6000/dfp.md	(working copy)
@@ -405,3 +405,151 @@
 { rs6000_split_multireg_move (operands[0], operands[1]); DONE; }
   [(set_attr "length" "8,8,8,20,20,16")])
 
+;; Hardware support for decimal floating point operations.
+
+(define_insn "extendddtd2"
+  [(set (match_operand:TD 0 "gpc_reg_operand" "=f")
+	(float_extend:TD (match_operand:DD 1 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "dctqpq %0,%1"
+  [(set_attr "type" "fp")])
+
+;; The result of drdpq is an even/odd register pair with the converted
+;; value in the even register and zero in the odd register.
+;; FIXME: Avoid the register move by using a reload constraint to ensure
+;; that the result is the first of the pair receiving the result of drdpq.
+
+(define_insn "trunctddd2"
+  [(set (match_operand:DD 0 "gpc_reg_operand" "=f")
+	(float_truncate:DD (match_operand:TD 1 "gpc_reg_operand" "f")))
+   (clobber (match_scratch:TD 2 "=f"))]
+  "TARGET_DFP"
+  "drdpq %2,%1\;fmr %0,%2"
+  [(set_attr "type" "fp")])
+
+(define_insn "adddd3"
+  [(set (match_operand:DD 0 "gpc_reg_operand" "=f")
+	(plus:DD (match_operand:DD 1 "gpc_reg_operand" "%f")
+		 (match_operand:DD 2 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "dadd %0,%1,%2"
+  [(set_attr "type" "fp")])
+
+(define_insn "addtd3"
+  [(set (match_operand:TD 0 "gpc_reg_operand" "=f")
+	(plus:TD (match_operand:TD 1 "gpc_reg_operand" "%f")
+		 (match_operand:TD 2 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "daddq %0,%1,%2"
+  [(set_attr "type" "fp")])
+
+(define_insn "subdd3"
+  [(set (match_operand:DD 0 "gpc_reg_operand" "=f")
+	(minus:DD (match_operand:DD 1 "gpc_reg_operand" "f")
+		  (match_operand:DD 2 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "dsub %0,%1,%2"
+  [(set_attr "type" "fp")])
+
+(define_insn "subtd3"
+  [(set (match_operand:TD 0 "gpc_reg_operand" "=f")
+	(minus:TD (match_operand:TD 1 "gpc_reg_operand" "f")
+		  (match_operand:TD 2 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "dsubq %0,%1,%2"
+  [(set_attr "type" "fp")])
+
+(define_insn "muldd3"
+  [(set (match_operand:DD 0 "gpc_reg_operand" "=f")
+	(mult:DD (match_operand:DD 1 "gpc_reg_operand" "%f")
+		 (match_operand:DD 2 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "dmul %0,%1,%2"
+  [(set_attr "type" "fp")])
+
+(define_insn "multd3"
+  [(set (match_operand:TD 0 "gpc_reg_operand" "=f")
+	(mult:TD (match_operand:TD 1 "gpc_reg_operand" "%f")
+		 (match_operand:TD 2 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "dmulq %0,%1,%2"
+  [(set_attr "type" "fp")])
+
+(define_insn "divdd3"
+  [(set (match_operand:DD 0 "gpc_reg_operand" "=f")
+	(div:DD (match_operand:DD 1 "gpc_reg_operand" "f")
+		(match_operand:DD 2 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "ddiv %0,%1,%2"
+  [(set_attr "type" "fp")])
+
+(define_insn "divtd3"
+  [(set (match_operand:TD 0 "gpc_reg_operand" "=f")
+	(div:TD (match_operand:TD 1 "gpc_reg_operand" "f")
+		(match_operand:TD 2 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "ddivq %0,%1,%2"
+  [(set_attr "type" "fp")])
+
+(define_insn "*cmpdd_internal1"
+  [(set (match_operand:CCFP 0 "cc_reg_operand" "=y")
+	(compare:CCFP (match_operand:DD 1 "gpc_reg_operand" "f")
+		      (match_operand:DD 2 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "dcmpu %0,%1,%2"
+  [(set_attr "type" "fpcompare")])
+
+(define_insn "*cmptd_internal1"
+  [(set (match_operand:CCFP 0 "cc_reg_operand" "=y")
+	(compare:CCFP (match_operand:TD 1 "gpc_reg_operand" "f")
+		      (match_operand:TD 2 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "dcmpuq %0,%1,%2"
+  [(set_attr "type" "fpcompare")])
+
+(define_insn "floatditd2"
+  [(set (match_operand:TD 0 "gpc_reg_operand" "=f")
+	(float:TD (match_operand:DI 1 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "dcffixq %0,%1"
+  [(set_attr "type" "fp")])
+
+;; Convert a decimal64 to a decimal64 whose value is an integer.
+;; This is the first stage of converting it to an integer type.
+
+(define_insn "ftruncdd2"
+  [(set (match_operand:DD 0 "gpc_reg_operand" "=f")
+	(fix:DD (match_operand:DD 1 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "drintn. 0,%0,%1,1"
+  [(set_attr "type" "fp")])
+
+;; Convert a decimal64 whose value is an integer to an actual integer.
+;; This is the second stage of converting decimal float to integer type.
+
+(define_insn "fixdddi2"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=f")
+	(fix:DI (match_operand:DD 1 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "dctfix %0,%1"
+  [(set_attr "type" "fp")])
+
+;; Convert a decimal128 to a decimal128 whose value is an integer.
+;; This is the first stage of converting it to an integer type.
+
+(define_insn "ftrunctd2"
+  [(set (match_operand:TD 0 "gpc_reg_operand" "=f")
+	(fix:TD (match_operand:TD 1 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "drintnq. 0,%0,%1,1"
+  [(set_attr "type" "fp")])
+
+;; Convert a decimal128 whose value is an integer to an actual integer.
+;; This is the second stage of converting decimal float to integer type.
+
+(define_insn "fixtddi2"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=f")
+	(fix:DI (match_operand:TD 1 "gpc_reg_operand" "f")))]
+  "TARGET_DFP"
+  "dctfixq %0,%1"
+  [(set_attr "type" "fp")])
Index: gcc/config/rs6000/rs6000.md
===================================================================
--- gcc/config/rs6000/rs6000.md	(revision 127562)
+++ gcc/config/rs6000/rs6000.md	(working copy)
@@ -204,7 +204,9 @@
   (TF "!TARGET_IEEEQUAD
    && TARGET_HARD_FLOAT
    && (TARGET_FPRS || TARGET_E500_DOUBLE)
-   && TARGET_LONG_DOUBLE_128")])
+   && TARGET_LONG_DOUBLE_128")
+  (DD "TARGET_DFP")
+  (TD "TARGET_DFP")])
 
 ; Various instructions that come in SI and DI forms.
 ; A generic w/d attribute, for things like cmpw/cmpd.
Index: gcc/testsuite/gcc.target/powerpc/dfp-dd.c
===================================================================
--- gcc/testsuite/gcc.target/powerpc/dfp-dd.c	(revision 0)
+++ gcc/testsuite/gcc.target/powerpc/dfp-dd.c	(revision 0)
@@ -0,0 +1,33 @@
+/* Test generation of DFP instructions for POWER6.  */
+/* Origin: Janis Johnson <janis187@us.ibm.com> */
+/* { dg-do compile { target powerpc*-*-linux* } } */
+/* { dg-options "-std=gnu99 -mcpu=power6" } */
+
+/* { dg-final { scan-assembler "dadd" } } */
+/* { dg-final { scan-assembler "ddiv" } } */
+/* { dg-final { scan-assembler "dmul" } } */
+/* { dg-final { scan-assembler "dsub" } } */
+/* { dg-final { scan-assembler-times "dcmpu" 6 } } */
+/* { dg-final { scan-assembler-times "dctfix" 2 } } */
+/* { dg-final { scan-assembler-times "drintn" 2 } } */
+/* { dg-final { scan-assembler-times "dcffixq" 2 } } */
+
+extern _Decimal64 a, b, c;
+extern int result;
+extern int si;
+extern long long di;
+
+void add (void) { a = b + c; }
+void div (void) { a = b / c; }
+void mul (void) { a = b * c; }
+void sub (void) { a = b - c; }
+void eq (void) { result = a == b; }
+void ne (void) { result = a != b; }
+void lt (void) { result = a < b; }
+void le (void) { result = a <= b; }
+void gt (void) { result = a > b; }
+void ge (void) { result = a >= b; }
+void ddsi (void) { si = a; }
+void dddi (void) { di = a; }
+void sidd (void) { a = si; }
+void didd (void) { a = di; }
Index: gcc/testsuite/gcc.target/powerpc/dfp-td.c
===================================================================
--- gcc/testsuite/gcc.target/powerpc/dfp-td.c	(revision 0)
+++ gcc/testsuite/gcc.target/powerpc/dfp-td.c	(revision 0)
@@ -0,0 +1,33 @@
+/* Test generation of DFP instructions for POWER6.  */
+/* Origin: Janis Johnson <janis187@us.ibm.com> */
+/* { dg-do compile { target powerpc*-*-linux* } } */
+/* { dg-options "-std=gnu99 -mcpu=power6" } */
+
+/* { dg-final { scan-assembler "daddq" } } */
+/* { dg-final { scan-assembler "ddivq" } } */
+/* { dg-final { scan-assembler "dmulq" } } */
+/* { dg-final { scan-assembler "dsubq" } } */
+/* { dg-final { scan-assembler-times "dcmpuq" 6 } } */
+/* { dg-final { scan-assembler-times "dctfixq" 2 } } */
+/* { dg-final { scan-assembler-times "drintnq" 2 } } */
+/* { dg-final { scan-assembler-times "dcffixq" 2 } } */
+
+extern _Decimal128 a, b, c;
+extern int result;
+extern int si;
+extern long long di;
+
+void add (void) { a = b + c; }
+void div (void) { a = b / c; }
+void mul (void) { a = b * c; }
+void sub (void) { a = b - c; }
+void eq (void) { result = a == b; }
+void ne (void) { result = a != b; }
+void lt (void) { result = a < b; }
+void le (void) { result = a <= b; }
+void gt (void) { result = a > b; }
+void ge (void) { result = a >= b; }
+void tdsi (void) { si = a; }
+void tddi (void) { di = a; }
+void sitd (void) { a = si; }
+void ditd (void) { a = di; }



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]