From a918bfbf3c5b686362f3f97314c2b4bf142d2f82 Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Fri, 14 Oct 2016 14:08:27 +0200 Subject: [PATCH] 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 --- gcc/ChangeLog | 13 +++ gcc/builtins.c | 138 ------------------------------ gcc/fold-const-call.c | 45 ++++++++-- gcc/fold-const-call.h | 1 + gcc/gimple-fold.c | 189 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 239 insertions(+), 147 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index de8a650f260..ed51c32fc3a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2016-10-14 Martin Liska + + * 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 * gcov-io.c (gcov_open): Deconstify 'mode'. diff --git a/gcc/builtins.c b/gcc/builtins.c index 43a9db02f2e..ed5a6359b02 100644 --- a/gcc/builtins.c +++ b/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); diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c index 2bbc8872865..f67b245876c 100644 --- a/gcc/fold-const-call.c +++ b/gcc/fold-const-call.c @@ -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)) diff --git a/gcc/fold-const-call.h b/gcc/fold-const-call.h index 324ecbf934b..7f61f2e08cb 100644 --- a/gcc/fold-const-call.h +++ b/gcc/fold-const-call.h @@ -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 diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 1836927e289..f349472a18a 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -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); -- 2.30.2