This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Constant fold <string.h> library functions
- From: Roger Sayle <roger at eyesopen dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: Eric Botcazou <ebotcazou at libertysurf dot fr>, Richard Henderson <rth at redhat dot com>
- Date: Wed, 15 Oct 2003 14:45:06 -0600 (MDT)
- Subject: [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