[PATCH] Support signbit, signbitf and signbitl as GCC builtins (take 2)

Roger Sayle roger@eyesopen.com
Sat Jan 31 23:42:00 GMT 2004


The following revision of my patch to support signbit, signbitf
and signbitl should addresses RTH's comments.

On Thu, 29 Jan 2004, Richard Henderson wrote:
> > +   if (!fmt
>
> I don't believe fmt should ever be null.

Check removed.


> >       || !fmt->has_signed_zero
>
> Why check for it?  That simply means that -0.0 isn't a valid
> representation, and so it should never be created.

No longer tested in the fmt->signbit >= 0 case.


> >       || fmt->signbit < 0)
>
> Here "signbit = (f < 0.0)" will work.

Indeed.  I've added support for implementing signbit using "f < 0.0"
both during constant folding, so that its useful to tree-ssa et al.,
and also in the sanity checking in expand_builtin_signbit.  This code
still checks for signed-zeros, which would invalidate "f < 0.0" even
with floating point formats without a sign bit.


> > +   /* Check we can represent the signbit as an immediate constant.  */
> > +   if (bitpos >= 2 * HOST_BITS_PER_WIDE_INT)
> > +     return 0;
>
> This is in the wrong place.  You want this after we've reduced to
> rmode.  And after that, the test should always be false.

Indeed.  This test has also been completely removed.


> > +   temp = expand_binop (rmode, and_optab, temp,
> > + 		       immed_double_const (lo, hi, rmode),
> > + 		       target, 1, OPTAB_LIB_WIDEN);
>
> I think we should canonicalize on 0/1 or 0/MIN_INT.  I know it's
> not required, but I don't like the idea of expand_signbit and
> fold_signbit producing different results.
>
> Hum.  Perhaps that's even more pointless, since either may or
> may not correspond with the libm routine.
>
> Oh, it would be nice to generate (lt temp 0) when the bit is
> in the correct position and it's cheaper.  E.g. Alpha.  ;-)

The descriptions of signbit that I can find, only describe its results
as either being equal to or not equal to zero.  Hence its unclear that
we'll produce the same values as the system library, and tests such as
signbit (x) == signbit (y) are fundamentally unsafe.

The inline implementations used in my patch take advantage of this
definition to make the generated code very light weight, one or two
instructions.  Indeed, this allows GCC to generate (lt temp 0) when
the bit is in the correct position (as you suggest).

void bar (void);
void foo (double x)
{
  if (signbit(x))
    bar();
}

generates the following code on x86 with -O2 -fomit-frame-pointer

foo:
        movl    8(%esp), %eax
        testl   %eax, %eax
        js      .L4
        ret
.L4:
        jmp     bar


I've also tested on alpha, but unfortunately the RTL optimizers currently
have difficulty turning "(x & 0x80000000) != 0" into "x < 0".  Rather than
duplicate and special case this in the signbit builtin expander, I promise
to look into fixing it at the RTL level.  I'm planning some improvements
to simplify_relational_operation anyway.

This should also improve/fix the following on alpha

void foo(double x)
{
  int c = 0x80000000;
  if (x & c)
    bar();
}



The following patch has been tested on i686-pc-linux-gnu with a full
"make bootstrap", all languages except treelang, and regression tested
with a top-level "make -k check" with no new failures.

Ok for mainline?


2004-01-31  Roger Sayle  <roger@eyesopen.com>

	* builtins.def (BUILT_IN_SIGNBIT, BUILT_IN_SIGNBITF,
	BUILT_IN_SIGNBITL): New GCC builtins.
	* builtins.c (expand_builtin_signbit): New function to RTL expand
	calls to signbit, signbitf and signbitl as inline intrinsics.
	(expand_builtin): Call expand_builtin_signbit for BUILT_IN_SIGNBIT*.
	(fold_builtin_signbit): New function to perform constant folding
	of signbit, signbitf and signbitl.
	(fold_builtin): Call fold_builtin_signbit fir BUILT_IN_SIGNBIT*.

	* doc/extend.texi: Document new signbit{,f,l} builtins.

	* gcc.dg/builtins-1.c: Also test for __builtin_signbit{,f,l}.
	* gcc.dg/builtins-31.c: New testcase.
	* gcc.dg/builtins-32.c: New testcase.


Index: builtins.def
===================================================================
RCS file: /cvs/gcc/gcc/gcc/builtins.def,v
retrieving revision 1.73
diff -c -3 -p -r1.73 builtins.def
*** builtins.def	9 Sep 2003 03:29:10 -0000	1.73
--- builtins.def	31 Jan 2004 18:52:44 -0000
*************** DEF_C99_BUILTIN        (BUILT_IN_SCALBLN
*** 320,325 ****
--- 320,328 ----
  DEF_C99_BUILTIN        (BUILT_IN_SCALBN, "scalbn", BT_FN_DOUBLE_DOUBLE_INT, ATTR_MATHFN_FPROUNDING_ERRNO)
  DEF_C99_BUILTIN        (BUILT_IN_SCALBNF, "scalbnf", BT_FN_FLOAT_FLOAT_INT, ATTR_MATHFN_FPROUNDING_ERRNO)
  DEF_C99_BUILTIN        (BUILT_IN_SCALBNL, "scalbnl", BT_FN_LONGDOUBLE_LONGDOUBLE_INT, ATTR_MATHFN_FPROUNDING_ERRNO)
+ DEF_EXT_LIB_BUILTIN    (BUILT_IN_SIGNBIT, "signbit", BT_FN_INT_DOUBLE, ATTR_CONST_NOTHROW_LIST)
+ DEF_EXT_LIB_BUILTIN    (BUILT_IN_SIGNBITF, "signbitf", BT_FN_INT_FLOAT, ATTR_CONST_NOTHROW_LIST)
+ DEF_EXT_LIB_BUILTIN    (BUILT_IN_SIGNBITL, "signbitl", BT_FN_INT_LONGDOUBLE, ATTR_CONST_NOTHROW_LIST)
  DEF_EXT_LIB_BUILTIN    (BUILT_IN_SIGNIFICAND, "significand", BT_FN_DOUBLE_DOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO)
  DEF_EXT_LIB_BUILTIN    (BUILT_IN_SIGNIFICANDF, "significandf", BT_FN_FLOAT_FLOAT, ATTR_MATHFN_FPROUNDING_ERRNO)
  DEF_EXT_LIB_BUILTIN    (BUILT_IN_SIGNIFICANDL, "significandl", BT_FN_LONGDOUBLE_LONGDOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO)
Index: builtins.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/builtins.c,v
retrieving revision 1.278
diff -c -3 -p -r1.278 builtins.c
*** builtins.c	23 Jan 2004 16:16:33 -0000	1.278
--- builtins.c	31 Jan 2004 18:52:46 -0000
*************** static tree fold_trunc_transparent_mathf
*** 152,157 ****
--- 152,158 ----
  static bool readonly_data_expr (tree);
  static rtx expand_builtin_fabs (tree, rtx, rtx);
  static rtx expand_builtin_cabs (tree, rtx);
+ static rtx expand_builtin_signbit (tree, rtx);
  static tree fold_builtin_cabs (tree, tree, tree);
  static tree fold_builtin_trunc (tree);
  static tree fold_builtin_floor (tree);
*************** static tree fold_builtin_strncpy (tree);
*** 166,171 ****
--- 167,173 ----
  static tree fold_builtin_memcmp (tree);
  static tree fold_builtin_strcmp (tree);
  static tree fold_builtin_strncmp (tree);
+ static tree fold_builtin_signbit (tree);

  /* Return the alignment in bits of EXP, a pointer valued expression.
     But don't return more than MAX_ALIGN no matter what.
*************** expand_builtin_sprintf (tree arglist, rt
*** 4920,4925 ****
--- 4922,5018 ----

    return 0;
  }
+
+ /* Expand a call to the built-in signbit, signbitf or signbitl function.
+    Return NULL_RTX if a normal call should be emitted rather than expanding
+    the function in-line.  EXP is the expression that is a call to the builtin
+    function; if convenient, the result should be placed in TARGET.  */
+
+ static rtx
+ expand_builtin_signbit (tree exp, rtx target)
+ {
+   const struct real_format *fmt;
+   enum machine_mode fmode, imode, rmode;
+   HOST_WIDE_INT hi, lo;
+   tree arg, arglist;
+   int bitpos;
+   rtx temp;
+
+   arglist = TREE_OPERAND (exp, 1);
+   if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+     return 0;
+
+   arg = TREE_VALUE (arglist);
+   fmode = TYPE_MODE (TREE_TYPE (arg));
+   rmode = TYPE_MODE (TREE_TYPE (exp));
+   fmt = REAL_MODE_FORMAT (fmode);
+
+   /* For floating point formats without a sign bit, implement signbit
+      as "ARG < 0.0".  */
+   if (fmt->signbit < 0)
+   {
+     /* But we can't do this if the format supports signed zero.  */
+     if (fmt->has_signed_zero && HONOR_SIGNED_ZEROS (fmode))
+       return 0;
+
+     arg = fold (build (LT_EXPR, TREE_TYPE (exp), arg,
+ 		build_real (TREE_TYPE (arg), dconst0)));
+     return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
+   }
+
+   imode = int_mode_for_mode (fmode);
+   if (imode == BLKmode)
+     return 0;
+
+   bitpos = fmt->signbit;
+   /* Handle targets with different FP word orders.  */
+   if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+     {
+       int nwords = GET_MODE_BITSIZE (fmode) / BITS_PER_WORD;
+       int word = nwords - (bitpos / BITS_PER_WORD) - 1;
+       bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD;
+     }
+
+   /* If the sign bit is not in the lowpart and the floating point format
+      is wider than an integer, check that is twice the size of an integer
+      so that we can use gen_highpart below.  */
+   if (bitpos >= GET_MODE_BITSIZE (rmode)
+       && GET_MODE_BITSIZE (imode) != 2 * GET_MODE_BITSIZE (rmode))
+     return 0;
+
+   temp = expand_expr (arg, NULL_RTX, VOIDmode, 0);
+   temp = gen_lowpart (imode, temp);
+
+   if (GET_MODE_BITSIZE (imode) < GET_MODE_BITSIZE (rmode))
+     temp = gen_lowpart (rmode, temp);
+   else if (GET_MODE_BITSIZE (imode) > GET_MODE_BITSIZE (rmode))
+     {
+       if (bitpos > GET_MODE_BITSIZE (rmode))
+ 	{
+ 	  temp = gen_highpart (rmode, temp);
+ 	  bitpos %= GET_MODE_BITSIZE (rmode);
+ 	}
+       else
+ 	temp = gen_lowpart (rmode, temp);
+     }
+
+   if (bitpos < HOST_BITS_PER_WIDE_INT)
+     {
+       hi = 0;
+       lo = (HOST_WIDE_INT) 1 << bitpos;
+     }
+   else
+     {
+       hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+       lo = 0;
+     }
+
+   temp = force_reg (rmode, temp);
+   temp = expand_binop (rmode, and_optab, temp,
+ 		       immed_double_const (lo, hi, rmode),
+ 		       target, 1, OPTAB_LIB_WIDEN);
+   return temp;
+ }

  /* Expand an expression EXP that calls a built-in function,
     with result going to TARGET if that's convenient
*************** expand_builtin (tree exp, rtx target, rt
*** 5411,5416 ****
--- 5504,5517 ----
  	return target;
        break;

+     case BUILT_IN_SIGNBIT:
+     case BUILT_IN_SIGNBITF:
+     case BUILT_IN_SIGNBITL:
+       target = expand_builtin_signbit (exp, target);
+       if (target)
+ 	return target;
+       break;
+
        /* Various hooks for the DWARF 2 __throw routine.  */
      case BUILT_IN_UNWIND_INIT:
        expand_builtin_unwind_init ();
*************** fold_builtin_strncmp (tree exp)
*** 6528,6533 ****
--- 6629,6672 ----
    return 0;
  }

+ /* Fold function call to builtin signbit, signbitf or signbitl.  Return
+    NULL_TREE if no simplification can be made.  */
+
+ static tree
+ fold_builtin_signbit (tree exp)
+ {
+   tree arglist = TREE_OPERAND (exp, 1);
+   tree arg, temp;
+
+   if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+     return NULL_TREE;
+
+   arg = TREE_VALUE (arglist);
+
+   /* If ARG is a compile-time constant, determine the result.  */
+   if (TREE_CODE (arg) == REAL_CST
+       && !TREE_CONSTANT_OVERFLOW (arg))
+     {
+       REAL_VALUE_TYPE c;
+
+       c = TREE_REAL_CST (arg);
+       temp = REAL_VALUE_NEGATIVE (c) ? integer_one_node : integer_zero_node;
+       return convert (TREE_TYPE (exp), temp);
+     }
+
+   /* If ARG is non-negative, the result is always zero.  */
+   if (tree_expr_nonnegative_p (arg))
+     return omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg);
+
+   /* If ARG's format doesn't have signed zeros, return "arg < 0.0".  */
+   if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg))))
+     return fold (build (LT_EXPR, TREE_TYPE (exp), arg,
+ 			build_real (TREE_TYPE (arg), dconst0)));
+
+   return NULL_TREE;
+ }
+
+
  /* Used by constant folding to eliminate some builtin calls early.  EXP is
     the CALL_EXPR of a call to a builtin function.  */

*************** fold_builtin (tree exp)
*** 6948,6953 ****
--- 7087,7097 ----

      case BUILT_IN_STRNCMP:
        return fold_builtin_strncmp (exp);
+
+     case BUILT_IN_SIGNBIT:
+     case BUILT_IN_SIGNBITF:
+     case BUILT_IN_SIGNBITL:
+       return fold_builtin_signbit (exp);

      default:
        break;
Index: doc/extend.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/extend.texi,v
retrieving revision 1.182
diff -c -3 -p -r1.182 extend.texi
*** doc/extend.texi	21 Jan 2004 17:26:08 -0000	1.182
--- doc/extend.texi	31 Jan 2004 18:52:52 -0000
*************** v4si f (v4si a, v4si b, v4si c)
*** 4988,4993 ****
--- 4988,4996 ----
  @findex scalbn
  @findex scalbnf
  @findex scanfnl
+ @findex signbit
+ @findex signbitf
+ @findex signbitl
  @findex significand
  @findex significandf
  @findex significandl
*************** Outside strict ISO C mode (@option{-ansi
*** 5082,5087 ****
--- 5085,5091 ----
  @code{j1}, @code{jnf}, @code{jnl}, @code{jn}, @code{mempcpy},
  @code{pow10f}, @code{pow10l}, @code{pow10}, @code{printf_unlocked},
  @code{rindex}, @code{scalbf}, @code{scalbl}, @code{scalb},
+ @code{signbit}, @code{signbitf}, @code{signbitl},
  @code{significandf}, @code{significandl}, @code{significand},
  @code{sincosf}, @code{sincosl}, @code{sincos}, @code{stpcpy},
  @code{strdup}, @code{strfmon}, @code{y0f}, @code{y0l}, @code{y0},


Index: builtins-1.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/gcc.dg/builtins-1.c,v
retrieving revision 1.8
diff -c -3 -p -r1.8 builtins-1.c
*** builtins-1.c	9 Sep 2003 03:29:18 -0000	1.8
--- builtins-1.c	29 Jan 2004 18:52:24 -0000
*************** FPTEST1     (round)
*** 165,170 ****
--- 165,171 ----
  FPTEST2     (scalb)
  FPTEST2ARG2 (scalbln, int)
  FPTEST2ARG2 (scalbn, int)
+ FPTEST1RET  (signbit, int)
  FPTEST1     (significand)
  FPTEST1     (sin)
  FPTEST3FPP23VOID (sincos)


/* Copyright (C) 2004 Free Software Foundation.

   Check that constant folding of signbit, signbitf and signbitl math
   functions doesn't break anything and produces the expected results.

   Written by Roger Sayle, 28th January 2004.  */

/* { dg-do link } */
/* { dg-options "-O2" } */

extern void link_error(void);

extern int signbit(double);
extern int signbitf(float);
extern int signbitl(long double);

int main()
{
  if (signbit (1.0) != 0)
    link_error ();
  if (signbit (-2.0) == 0)
    link_error ();

  if (signbitf (1.0f) != 0)
    link_error ();
  if (signbitf (-2.0f) == 0)
    link_error ();

  if (signbitl (1.0l) != 0)
    link_error ();
  if (signbitl (-2.0f) == 0)
    link_error ();

  return 0;
}


/* Copyright (C) 2004 Free Software Foundation.

   Check that constant folding of signbit, signbitf and signbitl math
   functions doesn't break anything and produces the expected results.

   Written by Roger Sayle, 28th January 2004.  */

/* { dg-do run } */
/* { dg-options "-O2" } */

extern void abort(void);

extern int signbit(double);
extern int signbitf(float);

int test (double x)
{
  return signbit(x);
}

int testf (float x)
{
  return signbitf(x);
}

int main()
{
  if (test (1.0) != 0)
    abort ();
  if (test (-2.0) == 0)
    abort ();

  if (testf (1.0f) != 0)
    abort ();
  if (testf (-2.0f) == 0)
    abort ();

  return 0;
}


Roger
--
Roger Sayle,                         E-mail: roger@eyesopen.com
OpenEye Scientific Software,         WWW: http://www.eyesopen.com/
Suite 1107, 3600 Cerrillos Road,     Tel: (+1) 505-473-7385
Santa Fe, New Mexico, 87507.         Fax: (+1) 505-473-0833



More information about the Gcc-patches mailing list