#include "memmodel.h"
#include "gimple.h"
#include "predict.h"
+#include "params.h"
#include "tm_p.h"
#include "stringpool.h"
#include "tree-vrp.h"
static rtx expand_builtin_va_start (tree);
static rtx expand_builtin_va_end (tree);
static rtx expand_builtin_va_copy (tree);
+static rtx inline_expand_builtin_string_cmp (tree, rtx, bool);
static rtx expand_builtin_strcmp (tree, rtx);
static rtx expand_builtin_strncmp (tree, rtx, machine_mode);
static rtx builtin_memcpy_read_str (void *, HOST_WIDE_INT, scalar_int_mode);
tree arg1 = CALL_EXPR_ARG (exp, 0);
tree arg2 = CALL_EXPR_ARG (exp, 1);
tree len = CALL_EXPR_ARG (exp, 2);
+ enum built_in_function fcode = DECL_FUNCTION_CODE (get_callee_fndecl (exp));
+ bool no_overflow = true;
/* Diagnose calls where the specified length exceeds the size of either
object. */
- if (warn_stringop_overflow)
+ tree size = compute_objsize (arg1, 0);
+ no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
+ len, /*maxread=*/NULL_TREE, size,
+ /*objsize=*/NULL_TREE);
+ if (no_overflow)
+ {
+ size = compute_objsize (arg2, 0);
+ no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
+ len, /*maxread=*/NULL_TREE, size,
+ /*objsize=*/NULL_TREE);
+ }
+
+ /* Due to the performance benefit, always inline the calls first
+ when result_eq is false. */
+ rtx result = NULL_RTX;
+
+ if (!result_eq && fcode != BUILT_IN_BCMP && no_overflow)
{
- tree size = compute_objsize (arg1, 0);
- if (check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
- /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
- {
- size = compute_objsize (arg2, 0);
- check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
- /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
- }
+ result = inline_expand_builtin_string_cmp (exp, target, true);
+ if (result)
+ return result;
}
machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
&& (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1)
constfn = builtin_memcpy_read_str;
- rtx result = emit_block_cmp_hints (arg1_rtx, arg2_rtx, len_rtx,
- TREE_TYPE (len), target,
- result_eq, constfn,
- CONST_CAST (char *, src_str));
+ result = emit_block_cmp_hints (arg1_rtx, arg2_rtx, len_rtx,
+ TREE_TYPE (len), target,
+ result_eq, constfn,
+ CONST_CAST (char *, src_str));
if (result)
{
if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return NULL_RTX;
+ /* Due to the performance benefit, always inline the calls first. */
+ rtx result = NULL_RTX;
+ result = inline_expand_builtin_string_cmp (exp, target, false);
+ if (result)
+ return result;
+
insn_code cmpstr_icode = direct_optab_handler (cmpstr_optab, SImode);
insn_code cmpstrn_icode = direct_optab_handler (cmpstrn_optab, SImode);
if (cmpstr_icode == CODE_FOR_nothing && cmpstrn_icode == CODE_FOR_nothing)
rtx arg1_rtx = get_memory_rtx (arg1, NULL);
rtx arg2_rtx = get_memory_rtx (arg2, NULL);
- rtx result = NULL_RTX;
/* Try to call cmpstrsi. */
if (cmpstr_icode != CODE_FOR_nothing)
result = expand_cmpstr (cmpstr_icode, target, arg1_rtx, arg2_rtx,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return NULL_RTX;
+ /* Due to the performance benefit, always inline the calls first. */
+ rtx result = NULL_RTX;
+ result = inline_expand_builtin_string_cmp (exp, target, false);
+ if (result)
+ return result;
+
/* If c_strlen can determine an expression for one of the string
lengths, and it doesn't have side effects, then emit cmpstrnsi
using length MIN(strlen(string)+1, arg3). */
rtx arg1_rtx = get_memory_rtx (arg1, len);
rtx arg2_rtx = get_memory_rtx (arg2, len);
rtx arg3_rtx = expand_normal (len);
- rtx result = expand_cmpstrn_or_cmpmem (cmpstrn_icode, target, arg1_rtx,
- arg2_rtx, TREE_TYPE (len), arg3_rtx,
- MIN (arg1_align, arg2_align));
+ result = expand_cmpstrn_or_cmpmem (cmpstrn_icode, target, arg1_rtx,
+ arg2_rtx, TREE_TYPE (len), arg3_rtx,
+ MIN (arg1_align, arg2_align));
tree fndecl = get_callee_fndecl (exp);
if (result)
return target;
}
+/* Expand a string compare operation using a sequence of char comparison
+ to get rid of the calling overhead, with result going to TARGET if
+ that's convenient.
+
+ VAR_STR is the variable string source;
+ CONST_STR is the constant string source;
+ LENGTH is the number of chars to compare;
+ CONST_STR_N indicates which source string is the constant string;
+ IS_MEMCMP indicates whether it's a memcmp or strcmp.
+
+ to: (assume const_str_n is 2, i.e., arg2 is a constant string)
+
+ target = var_str[0] - const_str[0];
+ if (target != 0)
+ goto ne_label;
+ ...
+ target = var_str[length - 2] - const_str[length - 2];
+ if (target != 0)
+ goto ne_label;
+ target = var_str[length - 1] - const_str[length - 1];
+ ne_label:
+ */
+
+static rtx
+inline_string_cmp (rtx target, tree var_str, const char* const_str,
+ unsigned HOST_WIDE_INT length,
+ int const_str_n, machine_mode mode,
+ bool is_memcmp)
+{
+ HOST_WIDE_INT offset = 0;
+ rtx var_rtx_array
+ = get_memory_rtx (var_str, build_int_cst (unsigned_type_node,length));
+ rtx var_rtx = NULL_RTX;
+ rtx const_rtx = NULL_RTX;
+ rtx result = target ? target : gen_reg_rtx (mode);
+ rtx_code_label *ne_label = gen_label_rtx ();
+ tree unit_type_node = is_memcmp ? unsigned_char_type_node : char_type_node;
+
+ start_sequence ();
+
+ for (unsigned HOST_WIDE_INT i = 0; i < length; i++)
+ {
+ var_rtx
+ = adjust_address (var_rtx_array, TYPE_MODE (unit_type_node), offset);
+ const_rtx
+ = builtin_memcpy_read_str (CONST_CAST (char *, const_str),
+ offset,
+ as_a <scalar_int_mode>
+ TYPE_MODE (unit_type_node));
+ rtx op0 = (const_str_n == 1) ? const_rtx : var_rtx;
+ rtx op1 = (const_str_n == 1) ? var_rtx : const_rtx;
+
+ result = expand_simple_binop (mode, MINUS, op0, op1,
+ result, is_memcmp ? 1 : 0, OPTAB_WIDEN);
+ if (i < length - 1)
+ emit_cmp_and_jump_insns (result, CONST0_RTX (mode), NE, NULL_RTX,
+ mode, true, ne_label);
+ offset
+ += GET_MODE_SIZE (as_a <scalar_int_mode> TYPE_MODE (unit_type_node));
+ }
+
+ emit_label (ne_label);
+ rtx_insn *insns = get_insns ();
+ end_sequence ();
+ emit_insn (insns);
+
+ return result;
+}
+
+/* Inline expansion a call to str(n)cmp, with result going to
+ TARGET if that's convenient.
+ If the call is not been inlined, return NULL_RTX. */
+static rtx
+inline_expand_builtin_string_cmp (tree exp, rtx target, bool is_memcmp)
+{
+ tree fndecl = get_callee_fndecl (exp);
+ enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+ unsigned HOST_WIDE_INT length = 0;
+ bool is_ncmp = (fcode == BUILT_IN_STRNCMP || fcode == BUILT_IN_MEMCMP);
+
+ gcc_checking_assert (fcode == BUILT_IN_STRCMP
+ || fcode == BUILT_IN_STRNCMP
+ || fcode == BUILT_IN_MEMCMP);
+
+ tree arg1 = CALL_EXPR_ARG (exp, 0);
+ tree arg2 = CALL_EXPR_ARG (exp, 1);
+ tree len3_tree = is_ncmp ? CALL_EXPR_ARG (exp, 2) : NULL_TREE;
+
+ unsigned HOST_WIDE_INT len1 = 0;
+ unsigned HOST_WIDE_INT len2 = 0;
+ unsigned HOST_WIDE_INT len3 = 0;
+
+ const char *src_str1 = c_getstr (arg1, &len1);
+ const char *src_str2 = c_getstr (arg2, &len2);
+
+ /* If neither strings is constant string, the call is not qualify. */
+ if (!src_str1 && !src_str2)
+ return NULL_RTX;
+
+ /* For strncmp, if the length is not a const, not qualify. */
+ if (is_ncmp && !tree_fits_uhwi_p (len3_tree))
+ return NULL_RTX;
+
+ int const_str_n = 0;
+ if (!len1)
+ const_str_n = 2;
+ else if (!len2)
+ const_str_n = 1;
+ else if (len2 > len1)
+ const_str_n = 1;
+ else
+ const_str_n = 2;
+
+ gcc_checking_assert (const_str_n > 0);
+ length = (const_str_n == 1) ? len1 : len2;
+
+ if (is_ncmp && (len3 = tree_to_uhwi (len3_tree)) < length)
+ length = len3;
+
+ /* If the length of the comparision is larger than the threshold,
+ do nothing. */
+ if (length > (unsigned HOST_WIDE_INT)
+ PARAM_VALUE (BUILTIN_STRING_CMP_INLINE_LENGTH))
+ return NULL_RTX;
+
+ machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
+
+ /* Now, start inline expansion the call. */
+ return inline_string_cmp (target, (const_str_n == 1) ? arg2 : arg1,
+ (const_str_n == 1) ? src_str1 : src_str2, length,
+ const_str_n, mode, is_memcmp);
+}
+
/* Expand an expression EXP that calls a built-in function,
with result going to TARGET if that's convenient
(and in mode MODE if that's convenient).