2nd Patch for PR78009
Patch for PR83026 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78809 Inline strcmp with small constant strings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83026 missing strlen optimization for strcmp of unequal strings The design doc for PR78809 is at: https://www.mail-archive.com/gcc@gcc.gnu.org/msg83822.html this patch is for the second part of change of PR78809 and PR83026: B. for strncmp (s1, s2, n) (!)= 0 or strcmp (s1, s2) (!)= 0 B.1. (PR83026) When the lengths of both arguments are constant and it's a strcmp: * if the lengths are NOT equal, we can safely fold the call to a non-zero value. * otherwise, do nothing now. B.2. (PR78809) When the length of one argument is constant, try to replace the call with a __builtin_str(n)cmp_eq call where possible, i.e: strncmp (s, STR, C) (!)= 0 in which, s is a pointer to a string, STR is a string with constant length, C is a constant. if (C <= strlen(STR) && sizeof_array(s) > C) { replace this call with __builtin_strncmp_eq (s, STR, C) (!)= 0 } if (C > strlen(STR) { it can be safely treated as a call to strcmp (s, STR) (!)= 0 can handled by the following strcmp. } strcmp (s, STR) (!)= 0 in which, s is a pointer to a string, STR is a string with constant length. if (sizeof_array(s) > strlen(STR)) { replace this call with __builtin_strcmp_eq (s, STR, strlen(STR)+1) (!)= 0 } later when expanding the new __builtin_str(n)cmp_eq calls, first expand them as __builtin_memcmp_eq, if the expansion does not succeed, change them back to call to __builtin_str(n)cmp. adding test case strcmpopt_2.c and strcmpopt_4.c into gcc.dg for part B of PR78809 adding test case strcmpopt_3.c into gcc.dg for PR83026 From-SVN: r261039
This commit is contained in:
parent
28c84b6247
commit
8b0b334af3
@ -1,3 +1,25 @@
|
||||
2018-05-31 Qing Zhao <qing.zhao@oracle.com>
|
||||
|
||||
PR middle-end/78809
|
||||
PR middle-end/83026
|
||||
* builtins.c (expand_builtin): Add the handling of BUILT_IN_STRCMP_EQ
|
||||
and BUILT_IN_STRNCMP_EQ.
|
||||
* builtins.def: Add new builtins BUILT_IN_STRCMP_EQ and
|
||||
BUILT_IN_STRNCMP_EQ.
|
||||
* gimple-fold.c (gimple_fold_builtin_string_compare): Add the
|
||||
handling of BUILTIN_IN_STRCMP_EQ and BUILT_IN_STRNCMP_EQ.
|
||||
(gimple_fold_builtin): Likewise.
|
||||
* tree-ssa-strlen.c (compute_string_length): New function.
|
||||
(determine_min_obsize): New function.
|
||||
(handle_builtin_string_cmp): New function to handle calls to
|
||||
string compare functions.
|
||||
(strlen_optimize_stmt): Add handling to builtin string compare
|
||||
calls.
|
||||
* tree-ssa-structalias.c (find_func_aliases_for_builtin_call):
|
||||
Add the handling of BUILT_IN_STRCMP_EQ and BUILT_IN_STRNCMP_EQ.
|
||||
* tree.c (build_common_builtin_nodes): Add new defines of
|
||||
BUILT_IN_STRNCMP_EQ and BUILT_IN_STRCMP_EQ.
|
||||
|
||||
2018-05-31 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR target/85984
|
||||
|
@ -7139,12 +7139,45 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
|
||||
return target;
|
||||
break;
|
||||
|
||||
/* Expand it as BUILT_IN_MEMCMP_EQ first. If not successful, change it
|
||||
back to a BUILT_IN_STRCMP. Remember to delete the 3rd paramater
|
||||
when changing it to a strcmp call. */
|
||||
case BUILT_IN_STRCMP_EQ:
|
||||
target = expand_builtin_memcmp (exp, target, true);
|
||||
if (target)
|
||||
return target;
|
||||
|
||||
/* Change this call back to a BUILT_IN_STRCMP. */
|
||||
TREE_OPERAND (exp, 1)
|
||||
= build_fold_addr_expr (builtin_decl_explicit (BUILT_IN_STRCMP));
|
||||
|
||||
/* Delete the last parameter. */
|
||||
unsigned int i;
|
||||
vec<tree, va_gc> *arg_vec;
|
||||
vec_alloc (arg_vec, 2);
|
||||
for (i = 0; i < 2; i++)
|
||||
arg_vec->quick_push (CALL_EXPR_ARG (exp, i));
|
||||
exp = build_call_vec (TREE_TYPE (exp), CALL_EXPR_FN (exp), arg_vec);
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case BUILT_IN_STRCMP:
|
||||
target = expand_builtin_strcmp (exp, target);
|
||||
if (target)
|
||||
return target;
|
||||
break;
|
||||
|
||||
/* Expand it as BUILT_IN_MEMCMP_EQ first. If not successful, change it
|
||||
back to a BUILT_IN_STRNCMP. */
|
||||
case BUILT_IN_STRNCMP_EQ:
|
||||
target = expand_builtin_memcmp (exp, target, true);
|
||||
if (target)
|
||||
return target;
|
||||
|
||||
/* Change it back to a BUILT_IN_STRNCMP. */
|
||||
TREE_OPERAND (exp, 1)
|
||||
= build_fold_addr_expr (builtin_decl_explicit (BUILT_IN_STRNCMP));
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case BUILT_IN_STRNCMP:
|
||||
target = expand_builtin_strncmp (exp, target, mode);
|
||||
if (target)
|
||||
|
@ -971,6 +971,11 @@ DEF_BUILTIN_STUB (BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX, "__builtin_alloca_with_ali
|
||||
equality with zero. */
|
||||
DEF_BUILTIN_STUB (BUILT_IN_MEMCMP_EQ, "__builtin_memcmp_eq")
|
||||
|
||||
/* An internal version of strcmp/strncmp, used when the result is only
|
||||
tested for equality with zero. */
|
||||
DEF_BUILTIN_STUB (BUILT_IN_STRCMP_EQ, "__builtin_strcmp_eq")
|
||||
DEF_BUILTIN_STUB (BUILT_IN_STRNCMP_EQ, "__builtin_strncmp_eq")
|
||||
|
||||
/* Object size checking builtins. */
|
||||
DEF_GCC_BUILTIN (BUILT_IN_OBJECT_SIZE, "object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_PURE_NOTHROW_LEAF_LIST)
|
||||
DEF_EXT_LIB_BUILTIN_CHKP (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
|
||||
|
@ -2215,12 +2215,14 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
|
||||
switch (fcode)
|
||||
{
|
||||
case BUILT_IN_STRCMP:
|
||||
case BUILT_IN_STRCMP_EQ:
|
||||
{
|
||||
r = strcmp (p1, p2);
|
||||
known_result = true;
|
||||
break;
|
||||
}
|
||||
case BUILT_IN_STRNCMP:
|
||||
case BUILT_IN_STRNCMP_EQ:
|
||||
{
|
||||
if (length == -1)
|
||||
break;
|
||||
@ -2254,6 +2256,7 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
|
||||
|
||||
bool nonzero_length = length >= 1
|
||||
|| fcode == BUILT_IN_STRCMP
|
||||
|| fcode == BUILT_IN_STRCMP_EQ
|
||||
|| fcode == BUILT_IN_STRCASECMP;
|
||||
|
||||
location_t loc = gimple_location (stmt);
|
||||
@ -3687,8 +3690,10 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
|
||||
case BUILT_IN_STRSTR:
|
||||
return gimple_fold_builtin_strstr (gsi);
|
||||
case BUILT_IN_STRCMP:
|
||||
case BUILT_IN_STRCMP_EQ:
|
||||
case BUILT_IN_STRCASECMP:
|
||||
case BUILT_IN_STRNCMP:
|
||||
case BUILT_IN_STRNCMP_EQ:
|
||||
case BUILT_IN_STRNCASECMP:
|
||||
return gimple_fold_builtin_string_compare (gsi);
|
||||
case BUILT_IN_MEMCHR:
|
||||
|
@ -1,3 +1,12 @@
|
||||
2018-05-31 Qing Zhao <qing.zhao@oracle.com>
|
||||
|
||||
PR middle-end/78809
|
||||
* gcc.dg/strcmpopt_2.c: New test.
|
||||
* gcc.dg/strcmpopt_3.c: New test.
|
||||
|
||||
PR middle-end/83026
|
||||
* gcc.dg/strcmpopt_3.c: New test.
|
||||
|
||||
2018-05-31 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR target/85984
|
||||
|
67
gcc/testsuite/gcc.dg/strcmpopt_2.c
Normal file
67
gcc/testsuite/gcc.dg/strcmpopt_2.c
Normal file
@ -0,0 +1,67 @@
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-O2 -fdump-tree-strlen" } */
|
||||
|
||||
char s[100] = {'a','b','c','d'};
|
||||
typedef struct { char s[8]; int x; } S;
|
||||
|
||||
__attribute__ ((noinline)) int
|
||||
f1 (S *s)
|
||||
{
|
||||
return __builtin_strcmp (s->s, "abc") != 0;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline)) int
|
||||
f2 (void)
|
||||
{
|
||||
return __builtin_strcmp (s, "abc") != 0;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline)) int
|
||||
f3 (S *s)
|
||||
{
|
||||
return __builtin_strcmp ("abc", s->s) != 0;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline)) int
|
||||
f4 (void)
|
||||
{
|
||||
return __builtin_strcmp ("abc", s) != 0;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline)) int
|
||||
f5 (S *s)
|
||||
{
|
||||
return __builtin_strncmp (s->s, "abc", 3) != 0;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline)) int
|
||||
f6 (void)
|
||||
{
|
||||
return __builtin_strncmp (s, "abc", 2) != 0;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline)) int
|
||||
f7 (S *s)
|
||||
{
|
||||
return __builtin_strncmp ("abc", s->s, 3) != 0;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline)) int
|
||||
f8 (void)
|
||||
{
|
||||
return __builtin_strncmp ("abc", s, 2) != 0;
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
S ss = {{'a','b','c'}, 2};
|
||||
|
||||
if (f1 (&ss) != 0 || f2 () != 1 || f3 (&ss) != 0 ||
|
||||
f4 () != 1 || f5 (&ss) != 0 || f6 () != 0 ||
|
||||
f7 (&ss) != 0 || f8 () != 0)
|
||||
__builtin_abort ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 8 "strlen" } } */
|
31
gcc/testsuite/gcc.dg/strcmpopt_3.c
Normal file
31
gcc/testsuite/gcc.dg/strcmpopt_3.c
Normal file
@ -0,0 +1,31 @@
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-O2 -fdump-tree-strlen" } */
|
||||
|
||||
__attribute__ ((noinline)) int
|
||||
f1 (void)
|
||||
{
|
||||
char *s0= "abcd";
|
||||
char s[8];
|
||||
__builtin_strcpy (s, s0);
|
||||
return __builtin_strcmp(s, "abc") != 0;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline)) int
|
||||
f2 (void)
|
||||
{
|
||||
char *s0 = "ab";
|
||||
char s[8];
|
||||
__builtin_strcpy (s, s0);
|
||||
return __builtin_strcmp("abc", s) != 0;
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
if (f1 () != 1
|
||||
|| f2 () != 1)
|
||||
__builtin_abort ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "strcmp" 0 "strlen" } } */
|
16
gcc/testsuite/gcc.dg/strcmpopt_4.c
Normal file
16
gcc/testsuite/gcc.dg/strcmpopt_4.c
Normal file
@ -0,0 +1,16 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-tree-strlen" } */
|
||||
|
||||
typedef struct { char s[8]; int x; } S;
|
||||
extern int max_i;
|
||||
|
||||
int
|
||||
f1 (S * s)
|
||||
{
|
||||
int result, i;
|
||||
for (i = 0; i < max_i; i++)
|
||||
result += __builtin_strcmp (s->s, "abc") != 0 ? 2 : 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 1 "strlen" } } */
|
@ -48,6 +48,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "params.h"
|
||||
#include "ipa-chkp.h"
|
||||
#include "tree-hash-traits.h"
|
||||
#include "tree-object-size.h"
|
||||
#include "builtins.h"
|
||||
#include "target.h"
|
||||
#include "diagnostic-core.h"
|
||||
@ -2627,27 +2628,28 @@ handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
||||
|
||||
/* Handle a call to memset.
|
||||
After a call to calloc, memset(,0,) is unnecessary.
|
||||
memset(malloc(n),0,n) is calloc(n,1). */
|
||||
memset(malloc(n),0,n) is calloc(n,1).
|
||||
return true when the call is transfomred, false otherwise. */
|
||||
|
||||
static bool
|
||||
handle_builtin_memset (gimple_stmt_iterator *gsi)
|
||||
{
|
||||
gimple *stmt2 = gsi_stmt (*gsi);
|
||||
if (!integer_zerop (gimple_call_arg (stmt2, 1)))
|
||||
return true;
|
||||
return false;
|
||||
tree ptr = gimple_call_arg (stmt2, 0);
|
||||
int idx1 = get_stridx (ptr);
|
||||
if (idx1 <= 0)
|
||||
return true;
|
||||
return false;
|
||||
strinfo *si1 = get_strinfo (idx1);
|
||||
if (!si1)
|
||||
return true;
|
||||
return false;
|
||||
gimple *stmt1 = si1->stmt;
|
||||
if (!stmt1 || !is_gimple_call (stmt1))
|
||||
return true;
|
||||
return false;
|
||||
tree callee1 = gimple_call_fndecl (stmt1);
|
||||
if (!valid_builtin_call (stmt1))
|
||||
return true;
|
||||
return false;
|
||||
enum built_in_function code1 = DECL_FUNCTION_CODE (callee1);
|
||||
tree size = gimple_call_arg (stmt2, 2);
|
||||
if (code1 == BUILT_IN_CALLOC)
|
||||
@ -2663,7 +2665,7 @@ handle_builtin_memset (gimple_stmt_iterator *gsi)
|
||||
si1->stmt = gsi_stmt (gsi1);
|
||||
}
|
||||
else
|
||||
return true;
|
||||
return false;
|
||||
tree lhs = gimple_call_lhs (stmt2);
|
||||
unlink_stmt_vdef (stmt2);
|
||||
if (lhs)
|
||||
@ -2677,12 +2679,13 @@ handle_builtin_memset (gimple_stmt_iterator *gsi)
|
||||
release_defs (stmt2);
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Handle a call to memcmp. We try to handle small comparisons by
|
||||
converting them to load and compare, and replacing the call to memcmp
|
||||
with a __builtin_memcmp_eq call where possible. */
|
||||
with a __builtin_memcmp_eq call where possible.
|
||||
return true when call is transformed, return false otherwise. */
|
||||
|
||||
static bool
|
||||
handle_builtin_memcmp (gimple_stmt_iterator *gsi)
|
||||
@ -2697,7 +2700,7 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi)
|
||||
imm_use_iterator iter;
|
||||
|
||||
if (!res)
|
||||
return true;
|
||||
return false;
|
||||
|
||||
FOR_EACH_IMM_USE_FAST (use_p, iter, res)
|
||||
{
|
||||
@ -2711,17 +2714,17 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi)
|
||||
tree_code code = gimple_assign_rhs_code (asgn);
|
||||
if ((code != EQ_EXPR && code != NE_EXPR)
|
||||
|| !integer_zerop (gimple_assign_rhs2 (asgn)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
else if (gimple_code (ustmt) == GIMPLE_COND)
|
||||
{
|
||||
tree_code code = gimple_cond_code (ustmt);
|
||||
if ((code != EQ_EXPR && code != NE_EXPR)
|
||||
|| !integer_zerop (gimple_cond_rhs (ustmt)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tree_fits_uhwi_p (len)
|
||||
@ -2756,12 +2759,274 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi)
|
||||
boolean_type_node,
|
||||
arg1, arg2));
|
||||
gimplify_and_update_call_from_tree (gsi, res);
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
gimple_call_set_fndecl (stmt2, builtin_decl_explicit (BUILT_IN_MEMCMP_EQ));
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Given an index to the strinfo vector, compute the string length for the
|
||||
corresponding string. Return -1 when unknown. */
|
||||
|
||||
static HOST_WIDE_INT
|
||||
compute_string_length (int idx)
|
||||
{
|
||||
HOST_WIDE_INT string_leni = -1;
|
||||
gcc_assert (idx != 0);
|
||||
|
||||
if (idx < 0)
|
||||
return ~idx;
|
||||
|
||||
strinfo *si = get_strinfo (idx);
|
||||
if (si)
|
||||
{
|
||||
tree const_string_len = get_string_length (si);
|
||||
if (const_string_len && tree_fits_shwi_p (const_string_len))
|
||||
string_leni = tree_to_shwi (const_string_len);
|
||||
}
|
||||
|
||||
if (string_leni < 0)
|
||||
return -1;
|
||||
|
||||
return string_leni;
|
||||
}
|
||||
|
||||
/* Determine the minimum size of the object referenced by DEST expression which
|
||||
must have a pointer type.
|
||||
Return the minimum size of the object if successful or NULL when the size
|
||||
cannot be determined. */
|
||||
static tree
|
||||
determine_min_objsize (tree dest)
|
||||
{
|
||||
unsigned HOST_WIDE_INT size = 0;
|
||||
|
||||
if (compute_builtin_object_size (dest, 2, &size))
|
||||
return build_int_cst (sizetype, size);
|
||||
|
||||
/* Try to determine the size of the object through the RHS of the
|
||||
assign statement. */
|
||||
if (TREE_CODE (dest) == SSA_NAME)
|
||||
{
|
||||
gimple *stmt = SSA_NAME_DEF_STMT (dest);
|
||||
if (!is_gimple_assign (stmt))
|
||||
return NULL_TREE;
|
||||
|
||||
if (!gimple_assign_single_p (stmt)
|
||||
&& !gimple_assign_unary_nop_p (stmt))
|
||||
return NULL_TREE;
|
||||
|
||||
dest = gimple_assign_rhs1 (stmt);
|
||||
return determine_min_objsize (dest);
|
||||
}
|
||||
|
||||
/* Try to determine the size of the object from its type. */
|
||||
if (TREE_CODE (dest) != ADDR_EXPR)
|
||||
return NULL_TREE;
|
||||
|
||||
tree type = TREE_TYPE (dest);
|
||||
if (TREE_CODE (type) == POINTER_TYPE)
|
||||
type = TREE_TYPE (type);
|
||||
|
||||
type = TYPE_MAIN_VARIANT (type);
|
||||
|
||||
/* We cannot determine the size of the array if it's a flexible array,
|
||||
which is declared at the end of a structure. */
|
||||
if (TREE_CODE (type) == ARRAY_TYPE
|
||||
&& !array_at_struct_end_p (dest))
|
||||
{
|
||||
tree size_t = TYPE_SIZE_UNIT (type);
|
||||
if (size_t && TREE_CODE (size_t) == INTEGER_CST
|
||||
&& !integer_zerop (size_t))
|
||||
return size_t;
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Handle a call to strcmp or strncmp. When the result is ONLY used to do
|
||||
equality test against zero:
|
||||
|
||||
A. When the lengths of both arguments are constant and it's a strcmp:
|
||||
* if the lengths are NOT equal, we can safely fold the call
|
||||
to a non-zero value.
|
||||
* otherwise, do nothing now.
|
||||
|
||||
B. When the length of one argument is constant, try to replace the call with
|
||||
a __builtin_str(n)cmp_eq call where possible, i.e:
|
||||
|
||||
strncmp (s, STR, C) (!)= 0 in which, s is a pointer to a string, STR is a
|
||||
string with constant length , C is a constant.
|
||||
if (C <= strlen(STR) && sizeof_array(s) > C)
|
||||
{
|
||||
replace this call with
|
||||
strncmp_eq (s, STR, C) (!)= 0
|
||||
}
|
||||
if (C > strlen(STR)
|
||||
{
|
||||
it can be safely treated as a call to strcmp (s, STR) (!)= 0
|
||||
can handled by the following strcmp.
|
||||
}
|
||||
|
||||
strcmp (s, STR) (!)= 0 in which, s is a pointer to a string, STR is a
|
||||
string with constant length.
|
||||
if (sizeof_array(s) > strlen(STR))
|
||||
{
|
||||
replace this call with
|
||||
strcmp_eq (s, STR, strlen(STR)+1) (!)= 0
|
||||
}
|
||||
|
||||
Return true when the call is transformed, return false otherwise.
|
||||
*/
|
||||
|
||||
static bool
|
||||
handle_builtin_string_cmp (gimple_stmt_iterator *gsi)
|
||||
{
|
||||
gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
|
||||
tree res = gimple_call_lhs (stmt);
|
||||
use_operand_p use_p;
|
||||
imm_use_iterator iter;
|
||||
tree arg1 = gimple_call_arg (stmt, 0);
|
||||
tree arg2 = gimple_call_arg (stmt, 1);
|
||||
int idx1 = get_stridx (arg1);
|
||||
int idx2 = get_stridx (arg2);
|
||||
HOST_WIDE_INT length = -1;
|
||||
bool is_ncmp = false;
|
||||
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
/* When both arguments are unknown, do nothing. */
|
||||
if (idx1 == 0 && idx2 == 0)
|
||||
return false;
|
||||
|
||||
/* Handle strncmp function. */
|
||||
if (gimple_call_num_args (stmt) == 3)
|
||||
{
|
||||
tree len = gimple_call_arg (stmt, 2);
|
||||
if (tree_fits_shwi_p (len))
|
||||
length = tree_to_shwi (len);
|
||||
|
||||
is_ncmp = true;
|
||||
}
|
||||
|
||||
/* For strncmp, if the length argument is NOT known, do nothing. */
|
||||
if (is_ncmp && length < 0)
|
||||
return false;
|
||||
|
||||
/* When the result is ONLY used to do equality test against zero. */
|
||||
FOR_EACH_IMM_USE_FAST (use_p, iter, res)
|
||||
{
|
||||
gimple *use_stmt = USE_STMT (use_p);
|
||||
|
||||
if (is_gimple_debug (use_stmt))
|
||||
continue;
|
||||
if (gimple_code (use_stmt) == GIMPLE_ASSIGN)
|
||||
{
|
||||
tree_code code = gimple_assign_rhs_code (use_stmt);
|
||||
if (code == COND_EXPR)
|
||||
{
|
||||
tree cond_expr = gimple_assign_rhs1 (use_stmt);
|
||||
if ((TREE_CODE (cond_expr) != EQ_EXPR
|
||||
&& (TREE_CODE (cond_expr) != NE_EXPR))
|
||||
|| !integer_zerop (TREE_OPERAND (cond_expr, 1)))
|
||||
return false;
|
||||
}
|
||||
else if (code == EQ_EXPR || code == NE_EXPR)
|
||||
{
|
||||
if (!integer_zerop (gimple_assign_rhs2 (use_stmt)))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (gimple_code (use_stmt) == GIMPLE_COND)
|
||||
{
|
||||
tree_code code = gimple_cond_code (use_stmt);
|
||||
if ((code != EQ_EXPR && code != NE_EXPR)
|
||||
|| !integer_zerop (gimple_cond_rhs (use_stmt)))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* When the lengths of both arguments are known, and they are unequal, we can
|
||||
safely fold the call to a non-zero value for strcmp;
|
||||
othewise, do nothing now. */
|
||||
if (idx1 != 0 && idx2 != 0)
|
||||
{
|
||||
HOST_WIDE_INT const_string_leni1 = compute_string_length (idx1);
|
||||
HOST_WIDE_INT const_string_leni2 = compute_string_length (idx2);
|
||||
|
||||
if (!is_ncmp
|
||||
&& const_string_leni1 != -1
|
||||
&& const_string_leni2 != -1
|
||||
&& const_string_leni1 != const_string_leni2)
|
||||
{
|
||||
replace_call_with_value (gsi, integer_one_node);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* When the length of one argument is constant. */
|
||||
tree var_string = NULL_TREE;
|
||||
HOST_WIDE_INT const_string_leni = -1;
|
||||
|
||||
if (idx1)
|
||||
{
|
||||
const_string_leni = compute_string_length (idx1);
|
||||
var_string = arg2;
|
||||
}
|
||||
else
|
||||
{
|
||||
gcc_checking_assert (idx2);
|
||||
const_string_leni = compute_string_length (idx2);
|
||||
var_string = arg1;
|
||||
}
|
||||
|
||||
if (const_string_leni < 0)
|
||||
return false;
|
||||
|
||||
unsigned HOST_WIDE_INT var_sizei = 0;
|
||||
/* try to determine the minimum size of the object pointed by var_string. */
|
||||
tree size = determine_min_objsize (var_string);
|
||||
|
||||
if (!size)
|
||||
return false;
|
||||
|
||||
if (tree_fits_uhwi_p (size))
|
||||
var_sizei = tree_to_uhwi (size);
|
||||
|
||||
if (var_sizei == 0)
|
||||
return false;
|
||||
|
||||
/* For strncmp, if length > const_string_leni , this call can be safely
|
||||
transformed to a strcmp. */
|
||||
if (is_ncmp && length > const_string_leni)
|
||||
is_ncmp = false;
|
||||
|
||||
unsigned HOST_WIDE_INT final_length
|
||||
= is_ncmp ? length : const_string_leni + 1;
|
||||
|
||||
/* Replace strcmp or strncmp with the corresponding str(n)cmp_eq. */
|
||||
if (var_sizei > final_length)
|
||||
{
|
||||
tree fn
|
||||
= (is_ncmp
|
||||
? builtin_decl_implicit (BUILT_IN_STRNCMP_EQ)
|
||||
: builtin_decl_implicit (BUILT_IN_STRCMP_EQ));
|
||||
if (!fn)
|
||||
return false;
|
||||
tree const_string_len = build_int_cst (size_type_node, final_length);
|
||||
update_gimple_call (gsi, fn, 3, arg1, arg2, const_string_len);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Handle a POINTER_PLUS_EXPR statement.
|
||||
@ -3209,11 +3474,16 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh)
|
||||
handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi);
|
||||
break;
|
||||
case BUILT_IN_MEMSET:
|
||||
if (!handle_builtin_memset (gsi))
|
||||
if (handle_builtin_memset (gsi))
|
||||
return false;
|
||||
break;
|
||||
case BUILT_IN_MEMCMP:
|
||||
if (!handle_builtin_memcmp (gsi))
|
||||
if (handle_builtin_memcmp (gsi))
|
||||
return false;
|
||||
break;
|
||||
case BUILT_IN_STRCMP:
|
||||
case BUILT_IN_STRNCMP:
|
||||
if (handle_builtin_string_cmp (gsi))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
|
@ -4498,7 +4498,9 @@ find_func_aliases_for_builtin_call (struct function *fn, gcall *t)
|
||||
that use the memory pointed to by their arguments (but not
|
||||
transitively). */
|
||||
case BUILT_IN_STRCMP:
|
||||
case BUILT_IN_STRCMP_EQ:
|
||||
case BUILT_IN_STRNCMP:
|
||||
case BUILT_IN_STRNCMP_EQ:
|
||||
case BUILT_IN_STRCASECMP:
|
||||
case BUILT_IN_STRNCASECMP:
|
||||
case BUILT_IN_MEMCMP:
|
||||
|
@ -10277,6 +10277,14 @@ build_common_builtin_nodes (void)
|
||||
"__builtin_memcmp_eq",
|
||||
ECF_PURE | ECF_NOTHROW | ECF_LEAF);
|
||||
|
||||
local_define_builtin ("__builtin_strncmp_eq", ftype, BUILT_IN_STRNCMP_EQ,
|
||||
"__builtin_strncmp_eq",
|
||||
ECF_PURE | ECF_NOTHROW | ECF_LEAF);
|
||||
|
||||
local_define_builtin ("__builtin_strcmp_eq", ftype, BUILT_IN_STRCMP_EQ,
|
||||
"__builtin_strcmp_eq",
|
||||
ECF_PURE | ECF_NOTHROW | ECF_LEAF);
|
||||
|
||||
/* If there's a possibility that we might use the ARM EABI, build the
|
||||
alternate __cxa_end_cleanup node used to resume from C++. */
|
||||
if (targetm.arm_eabi_unwinder)
|
||||
|
Loading…
Reference in New Issue
Block a user