rs6000 floatdisf

Alan Modra amodra@bigpond.net.au
Wed Sep 18 05:27:00 GMT 2002


This is a followup to http://gcc.gnu.org/ml/gcc/2002-09/msg00681.html

I've done some testing using the attached test program, so I'd like
to apply the following to rs6000.md.  rth's solution may be better
for libgcc2.c, but I've only tested the rs6000.md change. ;)

	* config/rs6000/rs6000.md (floatdisf2): Rename to floatdisf2_int1.
	(floatdisf2): New define_expand.
	(floatdisf2_int2): Likewise.

Index: gcc/config/rs6000/rs6000.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/rs6000/rs6000.md,v
retrieving revision 1.206
diff -u -p -r1.206 rs6000.md
--- gcc/config/rs6000/rs6000.md	13 Sep 2002 01:40:44 -0000	1.206
+++ gcc/config/rs6000/rs6000.md	18 Sep 2002 07:26:06 -0000
@@ -5890,13 +5890,30 @@
   "fctidz %0,%1"
   [(set_attr "type" "fp")])
 
-;; This only is safe if rounding mode set appropriately.
-(define_insn_and_split "floatdisf2"
+(define_expand "floatdisf2"
+  [(set (match_operand:SF 0 "gpc_reg_operand" "")
+        (float:SF (match_operand:DI 1 "gpc_reg_operand" "")))]
+  "TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS"
+  "
+{
+  if (!flag_unsafe_math_optimizations)
+    {
+      rtx label = gen_label_rtx ();
+      emit_insn (gen_floatdisf2_int2 (operands[1], label));
+      emit_label (label);
+    }
+  emit_insn (gen_floatdisf2_int1 (operands[0], operands[1]));
+  DONE;
+}")
+
+;; This is not IEEE compliant if rounding mode is "round to nearest".
+;; If the DI->DF conversion is inexact, then it's possible to suffer
+;; from double rounding.
+(define_insn_and_split "floatdisf2_int1"
   [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
         (float:SF (match_operand:DI 1 "gpc_reg_operand" "*f")))
    (clobber (match_scratch:DF 2 "=f"))]
-  "TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS
-   && flag_unsafe_math_optimizations"
+  "TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS"
   "#"
   "&& reload_completed"
   [(set (match_dup 2)
@@ -5904,6 +5921,33 @@
    (set (match_dup 0)
         (float_truncate:SF (match_dup 2)))]
   "")
+
+;; Twiddles bits to avoid double rounding.
+;; Bits that might be trucated when converting to DFmode are replaced
+;; by a bit that won't be lost at that stage, but is below the SFmode
+;; rounding position.
+(define_expand "floatdisf2_int2"
+  [(set (match_dup 2) (and:DI (match_operand:DI 0 "" "") (const_int 2047)))
+   (set (match_dup 4) (compare:CC (match_dup 2) (const_int 0)))
+   (set (match_dup 3) (ashiftrt:DI (match_dup 0) (const_int 53)))
+   (set (match_dup 3) (plus:DI (match_dup 3) (const_int 1)))
+   (set (pc) (if_then_else (eq (match_dup 4) (const_int 0))
+			   (label_ref (match_operand:DI 1 "" ""))
+			   (pc)))
+   (set (match_dup 5) (compare:CCUNS (match_dup 3) (const_int 2)))
+   (set (pc) (if_then_else (ltu (match_dup 5) (const_int 0))
+			   (label_ref (match_dup 1))
+			   (pc)))
+   (set (match_dup 0) (xor:DI (match_dup 0) (match_dup 2)))
+   (set (match_dup 0) (ior:DI (match_dup 0) (const_int 2048)))]
+  "TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS"
+  "
+{
+  operands[2] = gen_reg_rtx (DImode);
+  operands[3] = gen_reg_rtx (DImode);
+  operands[4] = gen_reg_rtx (CCmode);
+  operands[5] = gen_reg_rtx (CCUNSmode);
+}")
 
 ;; Define the DImode operations that can be done in a small number
 ;; of instructions.  The & constraints are to prevent the register

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre
-------------- next part --------------
/* Tests validity of 64 bit integer -> IEEE single conversion.  */

#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <endian.h>

/* This is the IEEE 754 single-precision format.  */
union ieee754_float
{
  float f;

  struct
  {
#if __BYTE_ORDER == __BIG_ENDIAN
    unsigned int negative:1;
    unsigned int exponent:8;
    unsigned int mantissa:23;
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int mantissa:23;
    unsigned int exponent:8;
    unsigned int negative:1;
#endif
  } ieee;

  int i;
} x;

#define IEEE754_FLOAT_BIAS      0x7f


typedef int DIval __attribute ((mode (DI)));

/* Generate a pseudo-random floating point value within the range
   -1 << 63 to 1 << 63 - 1, with an exponent >= 0.  */
static unsigned int
gen_value (void)
{
  union ieee754_float x;
  unsigned int tmp = random ();
  unsigned int exp;

  x.ieee.negative = tmp & 1;
  exp = ((tmp >> 1) & 63);
  if (exp == 63)
    /* Biases our sample set, but who cares?  */
    --exp;
  x.ieee.exponent = exp + IEEE754_FLOAT_BIAS;
  x.ieee.mantissa = tmp >> 7;
  return x.i;
}


static int
test_one (int samp)
{
  float f1, f2;
  DIval z1, z2, mid;

  x.i = gen_value ();
  f1 = x.f;
  x.ieee.mantissa ^= 1;
  f2 = x.f;
  z1 = (DIval) f1;
  z2 = (DIval) f2;
  mid = z1 + (z2 - z1) / 2;
  if (z1 != mid && z2 != mid)
    {
      /* The interesting tests.  */
      DIval val, start;
      float f, midf;

      /* Value in the middle of the range goes to the endpoint with a
	 zero LSB.  */
      if ((x.ieee.mantissa & 1) != 0)
	midf = f1;
      else
	midf = f2;

      if (f1 > f2)
	{
	  f = f1, f1 = f2, f2 = f;
	  val = z1, z1 = z2, z2 = val;
	}

      start = mid - 1;
      do
	{
	  if (samp)
	    {
	      val = z1 + (z2 - z1) * ((double) random () / RAND_MAX);
	      --samp;
	    }
	  else
	    {
	      val = start;
	      ++start;
	    }
	  f = (float) val;
	  if (val < mid && f == f1)
	    ;
	  else if (val > mid && f == f2)
	    ;
	  else if (val == mid && f == midf)
	    ;
	  else
	    {
	      printf ("%f %f %f", f, f1, f2);
	      return 0;
	    }
	}
      while (start != mid + 2);

      return 1;
    }
  else
    return (DIval) (float) z1 == z1;
}


int
main (int argc, char **argv)
{
  int lim = 0;
  int samp = 0;
  struct timeval tv;

  if (--argc > 0)
    {
      /* How many times to run.  0 => no limit.  */
      lim = atoi (*++argv);
      if (--argc > 0)
	/* How many samples to try between integers that map to
	   adjacent floats, in addition to mid-1, mid, mid+1.  */
	samp = atoi (*++argv);
    }

  gettimeofday (&tv, NULL);
  srandom (tv.tv_sec ^ tv.tv_usec);

  do
    {
      if (!test_one (samp))
	{
	  printf (" failed %x\n", x.i);
	  return 1;
	}
    }
  while (lim == 0 || --lim != 0);

  return 0;
}


More information about the Gcc-patches mailing list