Fold __builtin_str{n}{case}cmp functions
* builtins.c (fold_builtin_strcmp): Remove function. (fold_builtin_strncmp): Likewise. (fold_builtin_2): Remove call of the function. (fold_builtin_3): Likewise. * fold-const-call.c (fold_const_call): Add constant folding for CFN_BUILT_IN_STRCASECMP and CFN_BUILT_IN_STRNCASECMP. * fold-const-call.h (build_cmp_result): Declare the function. * gimple-fold.c (gimple_load_first_char): New function. (gimple_fold_builtin_string_compare): Likewise. (gimple_fold_builtin): Call the function. From-SVN: r241159
This commit is contained in:
parent
ace4c2f060
commit
a918bfbf3c
5 changed files with 239 additions and 147 deletions
|
@ -1,3 +1,16 @@
|
|||
2016-10-14 Martin Liska <mliska@suse.cz>
|
||||
|
||||
* builtins.c (fold_builtin_strcmp): Remove function.
|
||||
(fold_builtin_strncmp): Likewise.
|
||||
(fold_builtin_2): Remove call of the function.
|
||||
(fold_builtin_3): Likewise.
|
||||
* fold-const-call.c (fold_const_call): Add constant folding
|
||||
for CFN_BUILT_IN_STRCASECMP and CFN_BUILT_IN_STRNCASECMP.
|
||||
* fold-const-call.h (build_cmp_result): Declare the function.
|
||||
* gimple-fold.c (gimple_load_first_char): New function.
|
||||
(gimple_fold_builtin_string_compare): Likewise.
|
||||
(gimple_fold_builtin): Call the function.
|
||||
|
||||
2016-10-14 Nathan Sidwell <nathan@acm.org>
|
||||
|
||||
* gcov-io.c (gcov_open): Deconstify 'mode'.
|
||||
|
|
138
gcc/builtins.c
138
gcc/builtins.c
|
@ -150,8 +150,6 @@ static rtx expand_builtin_fabs (tree, rtx, rtx);
|
|||
static rtx expand_builtin_signbit (tree, rtx);
|
||||
static tree fold_builtin_memchr (location_t, tree, tree, tree, tree);
|
||||
static tree fold_builtin_memcmp (location_t, tree, tree, tree);
|
||||
static tree fold_builtin_strcmp (location_t, tree, tree);
|
||||
static tree fold_builtin_strncmp (location_t, tree, tree, tree);
|
||||
static tree fold_builtin_isascii (location_t, tree);
|
||||
static tree fold_builtin_toascii (location_t, tree);
|
||||
static tree fold_builtin_isdigit (location_t, tree);
|
||||
|
@ -7331,136 +7329,6 @@ fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
|
|||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Fold function call to builtin strcmp with arguments ARG1 and ARG2.
|
||||
Return NULL_TREE if no simplification can be made. */
|
||||
|
||||
static tree
|
||||
fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
|
||||
{
|
||||
if (!validate_arg (arg1, POINTER_TYPE)
|
||||
|| !validate_arg (arg2, POINTER_TYPE))
|
||||
return NULL_TREE;
|
||||
|
||||
/* If ARG1 and ARG2 are the same (and not volatile), return zero. */
|
||||
if (operand_equal_p (arg1, arg2, 0))
|
||||
return integer_zero_node;
|
||||
|
||||
/* If the second arg is "", return *(const unsigned char*)arg1. */
|
||||
const char *p2 = c_getstr (arg2);
|
||||
if (p2 && *p2 == '\0')
|
||||
{
|
||||
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
|
||||
tree cst_uchar_ptr_node
|
||||
= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
|
||||
|
||||
return fold_convert_loc (loc, integer_type_node,
|
||||
build1 (INDIRECT_REF, cst_uchar_node,
|
||||
fold_convert_loc (loc,
|
||||
cst_uchar_ptr_node,
|
||||
arg1)));
|
||||
}
|
||||
|
||||
/* If the first arg is "", return -*(const unsigned char*)arg2. */
|
||||
const char *p1 = c_getstr (arg1);
|
||||
if (p1 && *p1 == '\0')
|
||||
{
|
||||
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
|
||||
tree cst_uchar_ptr_node
|
||||
= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
|
||||
|
||||
tree temp
|
||||
= fold_convert_loc (loc, integer_type_node,
|
||||
build1 (INDIRECT_REF, cst_uchar_node,
|
||||
fold_convert_loc (loc,
|
||||
cst_uchar_ptr_node,
|
||||
arg2)));
|
||||
return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Fold function call to builtin strncmp with arguments ARG1, ARG2, and LEN.
|
||||
Return NULL_TREE if no simplification can be made. */
|
||||
|
||||
static tree
|
||||
fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
|
||||
{
|
||||
if (!validate_arg (arg1, POINTER_TYPE)
|
||||
|| !validate_arg (arg2, POINTER_TYPE)
|
||||
|| !validate_arg (len, INTEGER_TYPE))
|
||||
return NULL_TREE;
|
||||
|
||||
/* If the LEN parameter is zero, return zero. */
|
||||
if (integer_zerop (len))
|
||||
return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
|
||||
arg1, arg2);
|
||||
|
||||
/* If ARG1 and ARG2 are the same (and not volatile), return zero. */
|
||||
if (operand_equal_p (arg1, arg2, 0))
|
||||
return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
|
||||
|
||||
/* If the second arg is "", and the length is greater than zero,
|
||||
return *(const unsigned char*)arg1. */
|
||||
const char *p2 = c_getstr (arg2);
|
||||
if (p2 && *p2 == '\0'
|
||||
&& TREE_CODE (len) == INTEGER_CST
|
||||
&& tree_int_cst_sgn (len) == 1)
|
||||
{
|
||||
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
|
||||
tree cst_uchar_ptr_node
|
||||
= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
|
||||
|
||||
return fold_convert_loc (loc, integer_type_node,
|
||||
build1 (INDIRECT_REF, cst_uchar_node,
|
||||
fold_convert_loc (loc,
|
||||
cst_uchar_ptr_node,
|
||||
arg1)));
|
||||
}
|
||||
|
||||
/* If the first arg is "", and the length is greater than zero,
|
||||
return -*(const unsigned char*)arg2. */
|
||||
const char *p1 = c_getstr (arg1);
|
||||
if (p1 && *p1 == '\0'
|
||||
&& TREE_CODE (len) == INTEGER_CST
|
||||
&& tree_int_cst_sgn (len) == 1)
|
||||
{
|
||||
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
|
||||
tree cst_uchar_ptr_node
|
||||
= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
|
||||
|
||||
tree temp = fold_convert_loc (loc, integer_type_node,
|
||||
build1 (INDIRECT_REF, cst_uchar_node,
|
||||
fold_convert_loc (loc,
|
||||
cst_uchar_ptr_node,
|
||||
arg2)));
|
||||
return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
|
||||
}
|
||||
|
||||
/* If len parameter is one, return an expression corresponding to
|
||||
(*(const unsigned char*)arg1 - (const unsigned char*)arg2). */
|
||||
if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
|
||||
{
|
||||
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
|
||||
tree cst_uchar_ptr_node
|
||||
= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
|
||||
|
||||
tree ind1 = fold_convert_loc (loc, integer_type_node,
|
||||
build1 (INDIRECT_REF, cst_uchar_node,
|
||||
fold_convert_loc (loc,
|
||||
cst_uchar_ptr_node,
|
||||
arg1)));
|
||||
tree ind2 = fold_convert_loc (loc, integer_type_node,
|
||||
build1 (INDIRECT_REF, cst_uchar_node,
|
||||
fold_convert_loc (loc,
|
||||
cst_uchar_ptr_node,
|
||||
arg2)));
|
||||
return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Fold a call to builtin isascii with argument ARG. */
|
||||
|
||||
static tree
|
||||
|
@ -8389,9 +8257,6 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
|
|||
case BUILT_IN_STRCSPN:
|
||||
return fold_builtin_strcspn (loc, arg0, arg1);
|
||||
|
||||
case BUILT_IN_STRCMP:
|
||||
return fold_builtin_strcmp (loc, arg0, arg1);
|
||||
|
||||
case BUILT_IN_STRPBRK:
|
||||
return fold_builtin_strpbrk (loc, arg0, arg1, type);
|
||||
|
||||
|
@ -8473,9 +8338,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
|
|||
return do_mpfr_remquo (arg0, arg1, arg2);
|
||||
break;
|
||||
|
||||
case BUILT_IN_STRNCMP:
|
||||
return fold_builtin_strncmp (loc, arg0, arg1, arg2);
|
||||
|
||||
case BUILT_IN_MEMCHR:
|
||||
return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ host_size_t_cst_p (tree t, size_t *size_out)
|
|||
"equal" and > 0 means "more". Canonicalize it to -1, 0 or 1 and
|
||||
return it in type TYPE. */
|
||||
|
||||
static inline tree
|
||||
tree
|
||||
build_cmp_result (tree type, int res)
|
||||
{
|
||||
return build_int_cst (type, res < 0 ? -1 : res > 0 ? 1 : 0);
|
||||
|
@ -1397,6 +1397,15 @@ fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1)
|
|||
return build_cmp_result (type, strcmp (p0, p1));
|
||||
return NULL_TREE;
|
||||
|
||||
case CFN_BUILT_IN_STRCASECMP:
|
||||
if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
|
||||
{
|
||||
int r = strcmp (p0, p1);
|
||||
if (r == 0)
|
||||
return build_cmp_result (type, r);
|
||||
}
|
||||
return NULL_TREE;
|
||||
|
||||
default:
|
||||
return fold_const_call_1 (fn, type, arg0, arg1);
|
||||
}
|
||||
|
@ -1464,16 +1473,36 @@ tree
|
|||
fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2)
|
||||
{
|
||||
const char *p0, *p1;
|
||||
size_t s2;
|
||||
size_t s2 = 0;
|
||||
switch (fn)
|
||||
{
|
||||
case CFN_BUILT_IN_STRNCMP:
|
||||
if ((p0 = c_getstr (arg0))
|
||||
&& (p1 = c_getstr (arg1))
|
||||
&& host_size_t_cst_p (arg2, &s2))
|
||||
return build_int_cst (type, strncmp (p0, p1, s2));
|
||||
return NULL_TREE;
|
||||
|
||||
{
|
||||
bool const_size_p = host_size_t_cst_p (arg2, &s2);
|
||||
if (const_size_p && s2 == 0
|
||||
&& !TREE_SIDE_EFFECTS (arg0)
|
||||
&& !TREE_SIDE_EFFECTS (arg1))
|
||||
return build_int_cst (type, 0);
|
||||
else if (const_size_p
|
||||
&& (p0 = c_getstr (arg0))
|
||||
&& (p1 = c_getstr (arg1)))
|
||||
return build_int_cst (type, strncmp (p0, p1, s2));
|
||||
return NULL_TREE;
|
||||
}
|
||||
case CFN_BUILT_IN_STRNCASECMP:
|
||||
{
|
||||
bool const_size_p = host_size_t_cst_p (arg2, &s2);
|
||||
if (const_size_p && s2 == 0
|
||||
&& !TREE_SIDE_EFFECTS (arg0)
|
||||
&& !TREE_SIDE_EFFECTS (arg1))
|
||||
return build_int_cst (type, 0);
|
||||
else if (const_size_p
|
||||
&& (p0 = c_getstr (arg0))
|
||||
&& (p1 = c_getstr (arg1))
|
||||
&& strncmp (p0, p1, s2) == 0)
|
||||
return build_int_cst (type, 0);
|
||||
return NULL_TREE;
|
||||
}
|
||||
case CFN_BUILT_IN_BCMP:
|
||||
case CFN_BUILT_IN_MEMCMP:
|
||||
if ((p0 = c_getstr (arg0))
|
||||
|
|
|
@ -24,5 +24,6 @@ tree fold_const_call (combined_fn, tree, tree);
|
|||
tree fold_const_call (combined_fn, tree, tree, tree);
|
||||
tree fold_const_call (combined_fn, tree, tree, tree, tree);
|
||||
tree fold_fma (location_t, tree, tree, tree, tree);
|
||||
tree build_cmp_result (tree type, int res);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -55,7 +55,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "omp-low.h"
|
||||
#include "ipa-chkp.h"
|
||||
#include "tree-cfg.h"
|
||||
|
||||
#include "fold-const-call.h"
|
||||
|
||||
/* Return true if T is a constant and the value cast to a target char
|
||||
can be represented by a host char.
|
||||
|
@ -1786,6 +1786,188 @@ gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Build and append gimple statements to STMTS that would load a first
|
||||
character of a memory location identified by STR. LOC is location
|
||||
of the statement. */
|
||||
|
||||
static tree
|
||||
gimple_load_first_char (location_t loc, tree str, gimple_seq *stmts)
|
||||
{
|
||||
tree var;
|
||||
|
||||
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
|
||||
tree cst_uchar_ptr_node
|
||||
= build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
|
||||
tree off0 = build_int_cst (cst_uchar_ptr_node, 0);
|
||||
|
||||
tree temp = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str, off0);
|
||||
gassign *stmt = gimple_build_assign (NULL_TREE, temp);
|
||||
var = create_tmp_reg_or_ssa_name (cst_uchar_node, stmt);
|
||||
|
||||
gimple_assign_set_lhs (stmt, var);
|
||||
gimple_seq_add_stmt_without_update (stmts, stmt);
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
|
||||
FCODE is the name of the builtin. */
|
||||
|
||||
static bool
|
||||
gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
|
||||
{
|
||||
gimple *stmt = gsi_stmt (*gsi);
|
||||
tree callee = gimple_call_fndecl (stmt);
|
||||
enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
|
||||
|
||||
tree type = integer_type_node;
|
||||
tree str1 = gimple_call_arg (stmt, 0);
|
||||
tree str2 = gimple_call_arg (stmt, 1);
|
||||
tree lhs = gimple_call_lhs (stmt);
|
||||
HOST_WIDE_INT length = -1;
|
||||
|
||||
/* Handle strncmp and strncasecmp functions. */
|
||||
if (gimple_call_num_args (stmt) == 3)
|
||||
{
|
||||
tree len = gimple_call_arg (stmt, 2);
|
||||
if (tree_fits_uhwi_p (len))
|
||||
length = tree_to_uhwi (len);
|
||||
}
|
||||
|
||||
/* If the LEN parameter is zero, return zero. */
|
||||
if (length == 0)
|
||||
{
|
||||
replace_call_with_value (gsi, integer_zero_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If ARG1 and ARG2 are the same (and not volatile), return zero. */
|
||||
if (operand_equal_p (str1, str2, 0))
|
||||
{
|
||||
replace_call_with_value (gsi, integer_zero_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *p1 = c_getstr (str1);
|
||||
const char *p2 = c_getstr (str2);
|
||||
|
||||
/* For known strings, return an immediate value. */
|
||||
if (p1 && p2)
|
||||
{
|
||||
int r = 0;
|
||||
bool known_result = false;
|
||||
|
||||
switch (fcode)
|
||||
{
|
||||
case BUILT_IN_STRCMP:
|
||||
{
|
||||
r = strcmp (p1, p2);
|
||||
known_result = true;
|
||||
break;
|
||||
}
|
||||
case BUILT_IN_STRNCMP:
|
||||
{
|
||||
if (length == -1)
|
||||
break;
|
||||
r = strncmp (p1, p2, length);
|
||||
known_result = true;
|
||||
break;
|
||||
}
|
||||
/* Only handleable situation is where the string are equal (result 0),
|
||||
which is already handled by operand_equal_p case. */
|
||||
case BUILT_IN_STRCASECMP:
|
||||
break;
|
||||
case BUILT_IN_STRNCASECMP:
|
||||
{
|
||||
if (length == -1)
|
||||
break;
|
||||
r = strncmp (p1, p2, length);
|
||||
if (r == 0)
|
||||
known_result = true;
|
||||
break;;
|
||||
}
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
if (known_result)
|
||||
{
|
||||
replace_call_with_value (gsi, build_cmp_result (type, r));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool nonzero_length = length >= 1
|
||||
|| fcode == BUILT_IN_STRCMP
|
||||
|| fcode == BUILT_IN_STRCASECMP;
|
||||
|
||||
location_t loc = gimple_location (stmt);
|
||||
|
||||
/* If the second arg is "", return *(const unsigned char*)arg1. */
|
||||
if (p2 && *p2 == '\0' && nonzero_length)
|
||||
{
|
||||
gimple_seq stmts = NULL;
|
||||
tree var = gimple_load_first_char (loc, str1, &stmts);
|
||||
if (lhs)
|
||||
{
|
||||
stmt = gimple_build_assign (lhs, NOP_EXPR, var);
|
||||
gimple_seq_add_stmt_without_update (&stmts, stmt);
|
||||
}
|
||||
|
||||
gsi_replace_with_seq_vops (gsi, stmts);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the first arg is "", return -*(const unsigned char*)arg2. */
|
||||
if (p1 && *p1 == '\0' && nonzero_length)
|
||||
{
|
||||
gimple_seq stmts = NULL;
|
||||
tree var = gimple_load_first_char (loc, str2, &stmts);
|
||||
|
||||
if (lhs)
|
||||
{
|
||||
tree c = create_tmp_reg_or_ssa_name (integer_type_node);
|
||||
stmt = gimple_build_assign (c, NOP_EXPR, var);
|
||||
gimple_seq_add_stmt_without_update (&stmts, stmt);
|
||||
|
||||
stmt = gimple_build_assign (lhs, NEGATE_EXPR, c);
|
||||
gimple_seq_add_stmt_without_update (&stmts, stmt);
|
||||
}
|
||||
|
||||
gsi_replace_with_seq_vops (gsi, stmts);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If len parameter is one, return an expression corresponding to
|
||||
(*(const unsigned char*)arg2 - *(const unsigned char*)arg1). */
|
||||
if (fcode == BUILT_IN_STRNCMP && length == 1)
|
||||
{
|
||||
gimple_seq stmts = NULL;
|
||||
tree temp1 = gimple_load_first_char (loc, str1, &stmts);
|
||||
tree temp2 = gimple_load_first_char (loc, str2, &stmts);
|
||||
|
||||
if (lhs)
|
||||
{
|
||||
tree c1 = create_tmp_reg_or_ssa_name (integer_type_node);
|
||||
gassign *convert1 = gimple_build_assign (c1, NOP_EXPR, temp1);
|
||||
gimple_seq_add_stmt_without_update (&stmts, convert1);
|
||||
|
||||
tree c2 = create_tmp_reg_or_ssa_name (integer_type_node);
|
||||
gassign *convert2 = gimple_build_assign (c2, NOP_EXPR, temp2);
|
||||
gimple_seq_add_stmt_without_update (&stmts, convert2);
|
||||
|
||||
stmt = gimple_build_assign (lhs, MINUS_EXPR, c1, c2);
|
||||
gimple_seq_add_stmt_without_update (&stmts, stmt);
|
||||
}
|
||||
|
||||
gsi_replace_with_seq_vops (gsi, stmts);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Fold a call to the fputs builtin. ARG0 and ARG1 are the arguments
|
||||
to the call. IGNORE is true if the value returned
|
||||
by the builtin will be ignored. UNLOCKED is true is true if this
|
||||
|
@ -3007,6 +3189,11 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
|
|||
case BUILT_IN_RINDEX:
|
||||
case BUILT_IN_STRRCHR:
|
||||
return gimple_fold_builtin_strchr (gsi, true);
|
||||
case BUILT_IN_STRCMP:
|
||||
case BUILT_IN_STRCASECMP:
|
||||
case BUILT_IN_STRNCMP:
|
||||
case BUILT_IN_STRNCASECMP:
|
||||
return gimple_fold_builtin_string_compare (gsi);
|
||||
case BUILT_IN_FPUTS:
|
||||
return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
|
||||
gimple_call_arg (stmt, 1), false);
|
||||
|
|
Loading…
Add table
Reference in a new issue