#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"
/* 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)
si1->stmt = gsi_stmt (gsi1);
}
else
- return true;
+ return false;
tree lhs = gimple_call_lhs (stmt2);
unlink_stmt_vdef (stmt2);
if (lhs)
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)
imm_use_iterator iter;
if (!res)
- return true;
+ return false;
FOR_EACH_IMM_USE_FAST (use_p, iter, res)
{
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)
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.
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: