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] Constant fold <string.h> library functions


The following patch implements Richard Henderson's suggestion that
many of tree-level optimizations of strcmp, memcpy, et al. that currently
are performed during RTL expansion could/should be performed during
constant folding.  This allows these transformations to be visible to
the tree-ssa optimizers, and may enable further optimization
opportunities.  For example, tree-ssa should now be able to determine
that strcmp("foo","bar") > 0 is a compile-time constant.

Additionally, this patch has the added benefit of fixing the failure
of gcc.c-torture/execute/string-opt-18.c on sparc*-sun-solaris* at -O0.
The problem is that the RTL expansion optimizations that remove the
reference to mempcpy are only performed when expanding string functions
as intrinsics, i.e. at -O1 and higher.  Hence at -O0, the call to
mempcy survives, causing a link error on systems without it.  Tree
folding will eliminate the problematic call even at -O0.


Whilst writing this patch, I noticed two other things that I fixed.
Firstly, it appears "patch" incorrectly applied an earlier patch of
mine, placing a hunk in an identical "context" in the same source
file.  Whilst this was "harmless" I've now moved that hunk back where
it was originally intended.  Secondly, there were a number of places
in the expand_builtin_foo functions where we should have been using
the function integer_zerop but instead were performing the equivalent
tests by hand.  These are now cleaned up in the patch below.


For those worried about the temporary code duplication, I intend to submit
a follow up patch such that the expand_builtin_foo functions internally
call the corresponding fold_builtin_foo routines.  However, this change
will require significant reindentation but (hopefully) no functional
change, so I thought it better to submit these two changes as two
independent patches.


The following patch has been tested on i686-pc-linux-gnu with a complete
"make bootstrap", all languages except treelang, and regression tested
with a top-level "make -k check" with no new failures.  Tests for all
of these optimizations are already in the testsuite.


Ok for mainline?  Even with my recently granted middle-end maintainer
privileges, I thought it would be best/safe to make sure everyone was
happy with factoring these function up to "fold", and doing so in two
steps.



2003-10-15  Roger Sayle  <roger@eyesopen.com>

	* builtins.c (fold_builtin_memcpy, fold_builtin_mempcpy,
	fold_builtin_memmove, fold_builtin_strcpy, fold_builtin_strncpy,
	fold_builtin_memcmp, fold_builtin_strcmp, fold_builtin_strncmp):
	New functions.
	(expand_builtin_memcpy): Use integer_zerop instead of testing
	host_integerp and tree_low_cst directly.  Move misapplied hunk
	for optimization when SRC and DEST point to the same location.
	(expand_builtin_mempcpy): From here.
	(expand_builtin_memmove): Use integer_zerop instead of testing
	host_integerp and tree_low_cst_directly.
	(expand_builtin_memset): Likewise.
	(expand_builtin_memcmp): Likewise (and for integer_onep).
	(expand_builtin_strncmp): Likewise.
	(fold_builtin): Call the appropriate fold_builtin_foo functions
	to optimize memcpy, mempcpy, memmove, strcpy, strncpy, memcmp,
	strcmp and strncmp.


Index: builtins.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/builtins.c,v
retrieving revision 1.259
diff -c -3 -p -r1.259 builtins.c
*** builtins.c	11 Oct 2003 21:11:21 -0000	1.259
--- builtins.c	15 Oct 2003 18:03:03 -0000
*************** static tree fold_builtin_trunc (tree);
*** 157,162 ****
--- 157,170 ----
  static tree fold_builtin_floor (tree);
  static tree fold_builtin_ceil (tree);
  static tree fold_builtin_bitop (tree);
+ static tree fold_builtin_memcpy (tree);
+ static tree fold_builtin_mempcpy (tree);
+ static tree fold_builtin_memmove (tree);
+ static tree fold_builtin_strcpy (tree);
+ static tree fold_builtin_strncpy (tree);
+ static tree fold_builtin_memcmp (tree);
+ static tree fold_builtin_strcmp (tree);
+ static tree fold_builtin_strncmp (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_memcpy (tree arglist, rtx
*** 2474,2486 ****
  	return 0;

        /* If the LEN parameter is zero, return DEST.  */
!       if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
  	{
  	  /* Evaluate and ignore SRC in case it has side-effects.  */
  	  expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
  	  return expand_expr (dest, target, mode, EXPAND_NORMAL);
  	}

        /* If either SRC is not a pointer type, don't do this
           operation in-line.  */
        if (src_align == 0)
--- 2482,2502 ----
  	return 0;

        /* If the LEN parameter is zero, return DEST.  */
!       if (integer_zerop (len))
  	{
  	  /* Evaluate and ignore SRC in case it has side-effects.  */
  	  expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
  	  return expand_expr (dest, target, mode, EXPAND_NORMAL);
  	}

+       /* If SRC and DEST are the same (and not volatile), return DEST.  */
+       if (operand_equal_p (src, dest, 0))
+ 	{
+ 	  /* Evaluate and ignore LEN in case it has side-effects.  */
+ 	  expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ 	  return expand_expr (dest, target, mode, EXPAND_NORMAL);
+ 	}
+
        /* If either SRC is not a pointer type, don't do this
           operation in-line.  */
        if (src_align == 0)
*************** expand_builtin_mempcpy (tree arglist, rt
*** 2597,2610 ****
  	  return expand_expr (dest, target, mode, EXPAND_NORMAL);
  	}

-       /* If SRC and DEST are the same (and not volatile), return DEST.  */
-       if (operand_equal_p (src, dest, 0))
- 	{
- 	  /* Evaluate and ignore LEN in case it has side-effects.  */
- 	  expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
- 	  return expand_expr (dest, target, mode, EXPAND_NORMAL);
- 	}
-
        /* If either SRC is not a pointer type, don't do this
           operation in-line.  */
        if (src_align == 0)
--- 2613,2618 ----
*************** expand_builtin_memmove (tree arglist, rt
*** 2675,2681 ****
  	return 0;

        /* If the LEN parameter is zero, return DEST.  */
!       if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
  	{
  	  /* Evaluate and ignore SRC in case it has side-effects.  */
  	  expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
--- 2683,2689 ----
  	return 0;

        /* If the LEN parameter is zero, return DEST.  */
!       if (integer_zerop (len))
  	{
  	  /* Evaluate and ignore SRC in case it has side-effects.  */
  	  expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
*************** expand_builtin_memset (tree arglist, rtx
*** 2975,2981 ****
  	return 0;

        /* If the LEN parameter is zero, return DEST.  */
!       if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
  	{
  	  /* Evaluate and ignore VAL in case it has side-effects.  */
  	  expand_expr (val, const0_rtx, VOIDmode, EXPAND_NORMAL);
--- 2983,2989 ----
  	return 0;

        /* If the LEN parameter is zero, return DEST.  */
!       if (integer_zerop (len))
  	{
  	  /* Evaluate and ignore VAL in case it has side-effects.  */
  	  expand_expr (val, const0_rtx, VOIDmode, EXPAND_NORMAL);
*************** expand_builtin_memcmp (tree exp ATTRIBUT
*** 3098,3104 ****
    len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));

    /* If the len parameter is zero, return zero.  */
!   if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
      {
        /* Evaluate and ignore arg1 and arg2 in case they have
           side-effects.  */
--- 3106,3112 ----
    len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));

    /* If the len parameter is zero, return zero.  */
!   if (integer_zerop (len))
      {
        /* Evaluate and ignore arg1 and arg2 in case they have
           side-effects.  */
*************** expand_builtin_memcmp (tree exp ATTRIBUT
*** 3131,3137 ****

    /* If len parameter is one, return an expression corresponding to
       (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
!   if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1)
      {
        tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
        tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
--- 3139,3145 ----

    /* If len parameter is one, return an expression corresponding to
       (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
!   if (integer_onep (len))
      {
        tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
        tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
*************** expand_builtin_strncmp (tree exp, rtx ta
*** 3392,3398 ****
    arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));

    /* If the len parameter is zero, return zero.  */
!   if (host_integerp (arg3, 1) && tree_low_cst (arg3, 1) == 0)
      {
        /* Evaluate and ignore arg1 and arg2 in case they have
  	 side-effects.  */
--- 3400,3406 ----
    arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));

    /* If the len parameter is zero, return zero.  */
!   if (integer_zerop (arg3))
      {
        /* Evaluate and ignore arg1 and arg2 in case they have
  	 side-effects.  */
*************** fold_builtin_exponent (tree exp, const R
*** 6190,6195 ****
--- 6198,6457 ----
    return 0;
  }

+ /* Fold function call to builtin memcpy.  Return
+    NULL_TREE if no simplification can be made.  */
+
+ static tree
+ fold_builtin_memcpy (tree exp)
+ {
+   tree arglist = TREE_OPERAND (exp, 1);
+   tree dest, src, len;
+
+   if (!validate_arglist (arglist,
+ 			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+     return 0;
+
+   dest = TREE_VALUE (arglist);
+   src = TREE_VALUE (TREE_CHAIN (arglist));
+   len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+   /* If the LEN parameter is zero, return DEST.  */
+   if (integer_zerop (len))
+     return omit_one_operand (TREE_TYPE (exp), dest, src);
+
+   /* If SRC and DEST are the same (and not volatile), return DEST.  */
+   if (operand_equal_p (src, dest, 0))
+     return omit_one_operand (TREE_TYPE (exp), dest, len);
+
+   return 0;
+ }
+
+ /* Fold function call to builtin mempcpy.  Return
+    NULL_TREE if no simplification can be made.  */
+
+ static tree
+ fold_builtin_mempcpy (tree exp)
+ {
+   tree arglist = TREE_OPERAND (exp, 1);
+   tree dest, src, len;
+
+   if (!validate_arglist (arglist,
+ 			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+     return 0;
+
+   dest = TREE_VALUE (arglist);
+   src = TREE_VALUE (TREE_CHAIN (arglist));
+   len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+   /* If the LEN parameter is zero, return DEST.  */
+   if (integer_zerop (len))
+     return omit_one_operand (TREE_TYPE (exp), dest, src);
+
+   /* If SRC and DEST are the same (and not volatile), return DEST+LEN.  */
+   if (operand_equal_p (src, dest, 0))
+     {
+       tree temp = convert (TREE_TYPE (dest), len);
+       temp = fold (build (PLUS_EXPR, TREE_TYPE (dest), dest, len));
+       return convert (TREE_TYPE (exp), temp);
+     }
+
+   return 0;
+ }
+
+ /* Fold function call to builtin memmove.  Return
+    NULL_TREE if no simplification can be made.  */
+
+ static tree
+ fold_builtin_memmove (tree exp)
+ {
+   tree arglist = TREE_OPERAND (exp, 1);
+   tree dest, src, len;
+
+   if (!validate_arglist (arglist,
+ 			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+     return 0;
+
+   dest = TREE_VALUE (arglist);
+   src = TREE_VALUE (TREE_CHAIN (arglist));
+   len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+   /* If the LEN parameter is zero, return DEST.  */
+   if (integer_zerop (len))
+     return omit_one_operand (TREE_TYPE (exp), dest, src);
+
+   /* If SRC and DEST are the same (and not volatile), return DEST.  */
+   if (operand_equal_p (src, dest, 0))
+     return omit_one_operand (TREE_TYPE (exp), dest, len);
+
+   return 0;
+ }
+
+ /* Fold function call to builtin strcpy.  Return
+    NULL_TREE if no simplification can be made.  */
+
+ static tree
+ fold_builtin_strcpy (tree exp)
+ {
+   tree arglist = TREE_OPERAND (exp, 1);
+   tree dest, src;
+
+   if (!validate_arglist (arglist,
+ 			 POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+     return 0;
+
+   dest = TREE_VALUE (arglist);
+   src = TREE_VALUE (TREE_CHAIN (arglist));
+
+   /* If SRC and DEST are the same (and not volatile), return DEST.  */
+   if (operand_equal_p (src, dest, 0))
+     return convert (TREE_TYPE (exp), dest);
+
+   return 0;
+ }
+
+ /* Fold function call to builtin strncpy.  Return
+    NULL_TREE if no simplification can be made.  */
+
+ static tree
+ fold_builtin_strncpy (tree exp)
+ {
+   tree arglist = TREE_OPERAND (exp, 1);
+   tree dest, src, len;
+
+   if (!validate_arglist (arglist,
+ 			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+     return 0;
+
+   dest = TREE_VALUE (arglist);
+   src = TREE_VALUE (TREE_CHAIN (arglist));
+   len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+   /* If the LEN parameter is zero, return DEST.  */
+   if (integer_zerop (len))
+     return omit_one_operand (TREE_TYPE (exp), dest, src);
+
+   return 0;
+ }
+
+ /* Fold function call to builtin memcmp.  Return
+    NULL_TREE if no simplification can be made.  */
+
+ static tree
+ fold_builtin_memcmp (tree exp)
+ {
+   tree arglist = TREE_OPERAND (exp, 1);
+   tree arg1, arg2, len;
+
+   if (!validate_arglist (arglist,
+ 			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+     return 0;
+
+   arg1 = TREE_VALUE (arglist);
+   arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+   len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+   /* If the LEN parameter is zero, return zero.  */
+   if (integer_zerop (len))
+     {
+       tree temp = omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg2);
+       return omit_one_operand (TREE_TYPE (exp), temp, arg1);
+     }
+
+   /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+   if (operand_equal_p (arg1, arg2, 0))
+     return omit_one_operand (TREE_TYPE (exp), integer_zero_node, len);
+
+   return 0;
+ }
+
+ /* Fold function call to builtin strcmp.  Return
+    NULL_TREE if no simplification can be made.  */
+
+ static tree
+ fold_builtin_strcmp (tree exp)
+ {
+   tree arglist = TREE_OPERAND (exp, 1);
+   tree arg1, arg2;
+   const char *p1, *p2;
+
+   if (!validate_arglist (arglist,
+ 			 POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+     return 0;
+
+   arg1 = TREE_VALUE (arglist);
+   arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+
+   /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+   if (operand_equal_p (arg1, arg2, 0))
+     return convert (TREE_TYPE (exp), integer_zero_node);
+
+   p1 = c_getstr (arg1);
+   p2 = c_getstr (arg2);
+
+   if (p1 && p2)
+     {
+       tree temp;
+       const int i = strcmp (p1, p2);
+       if (i < 0)
+ 	temp = integer_minus_one_node;
+       else if (i > 0)
+ 	temp = integer_one_node;
+       else
+ 	temp = integer_zero_node;
+       return convert (TREE_TYPE (exp), temp);
+     }
+
+   return 0;
+ }
+
+ /* Fold function call to builtin strncmp.  Return
+    NULL_TREE if no simplification can be made.  */
+
+ static tree
+ fold_builtin_strncmp (tree exp)
+ {
+   tree arglist = TREE_OPERAND (exp, 1);
+   tree arg1, arg2, len;
+   const char *p1, *p2;
+
+   if (!validate_arglist (arglist,
+ 			 POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+     return 0;
+
+   arg1 = TREE_VALUE (arglist);
+   arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+   len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+   /* If the LEN parameter is zero, return zero.  */
+   if (integer_zerop (len))
+     {
+       tree temp = omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg2);
+       return omit_one_operand (TREE_TYPE (exp), temp, arg1);
+     }
+
+   /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+   if (operand_equal_p (arg1, arg2, 0))
+     return omit_one_operand (TREE_TYPE (exp), integer_zero_node, len);
+
+   p1 = c_getstr (arg1);
+   p2 = c_getstr (arg2);
+
+   if (host_integerp (len, 1) && p1 && p2)
+     {
+       tree temp;
+       const int i = strncmp (p1, p2, tree_low_cst (len, 1));
+       if (i < 0)
+ 	temp = integer_minus_one_node;
+       else if (i > 0)
+ 	temp = integer_one_node;
+       else
+ 	temp = integer_zero_node;
+       return convert (TREE_TYPE (exp), temp);
+     }
+
+   return 0;
+ }
+
  /* 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)
*** 6584,6589 ****
--- 6846,6875 ----
      case BUILT_IN_PARITYL:
      case BUILT_IN_PARITYLL:
        return fold_builtin_bitop (exp);
+
+     case BUILT_IN_MEMCPY:
+       return fold_builtin_memcpy (exp);
+
+     case BUILT_IN_MEMPCPY:
+       return fold_builtin_mempcpy (exp);
+
+     case BUILT_IN_MEMMOVE:
+       return fold_builtin_memmove (exp);
+
+     case BUILT_IN_STRCPY:
+       return fold_builtin_strcpy (exp);
+
+     case BUILT_IN_STRNCPY:
+       return fold_builtin_strncpy (exp);
+
+     case BUILT_IN_MEMCMP:
+       return fold_builtin_memcmp (exp);
+
+     case BUILT_IN_STRCMP:
+       return fold_builtin_strcmp (exp);
+
+     case BUILT_IN_STRNCMP:
+       return fold_builtin_strncmp (exp);

      default:
        break;


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


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