summaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-strlen.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-ssa-strlen.c')
-rw-r--r--gcc/tree-ssa-strlen.c638
1 files changed, 408 insertions, 230 deletions
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index e4f18dba1e7..b0563fe7c32 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -57,11 +57,22 @@ static int max_stridx;
/* String information record. */
struct strinfo
{
- /* String length of this string. */
- tree length;
+ /* Number of leading characters that are known to be nonzero. This is
+ also the length of the string if FULL_STRING_P.
+
+ The values in a list of related string pointers must be consistent;
+ that is, if strinfo B comes X bytes after strinfo A, it must be
+ the case that A->nonzero_chars == X + B->nonzero_chars. */
+ tree nonzero_chars;
/* Any of the corresponding pointers for querying alias oracle. */
tree ptr;
- /* Statement for delayed length computation. */
+ /* This is used for two things:
+
+ - To record the statement that should be used for delayed length
+ computations. We maintain the invariant that all related strinfos
+ have delayed lengths or none do.
+
+ - To record the malloc or calloc call that produced this result. */
gimple *stmt;
/* Pointer to '\0' if known, if NULL, it can be computed as
ptr + length. */
@@ -99,6 +110,10 @@ struct strinfo
/* A flag for the next maybe_invalidate that this strinfo shouldn't
be invalidated. Always cleared by maybe_invalidate. */
bool dont_invalidate;
+ /* True if the string is known to be nul-terminated after NONZERO_CHARS
+ characters. False is useful when detecting strings that are built
+ up via successive memcpys. */
+ bool full_string_p;
};
/* Pool for allocating strinfo_struct entries. */
@@ -144,7 +159,34 @@ struct laststmt_struct
int stridx;
} laststmt;
-static int get_stridx_plus_constant (strinfo *, HOST_WIDE_INT, tree);
+static int get_stridx_plus_constant (strinfo *, unsigned HOST_WIDE_INT, tree);
+
+/* Return:
+
+ - 1 if SI is known to start with more than OFF nonzero characters.
+
+ - 0 if SI is known to start with OFF nonzero characters,
+ but is not known to start with more.
+
+ - -1 if SI might not start with OFF nonzero characters. */
+
+static inline int
+compare_nonzero_chars (strinfo *si, unsigned HOST_WIDE_INT off)
+{
+ if (si->nonzero_chars
+ && TREE_CODE (si->nonzero_chars) == INTEGER_CST)
+ return compare_tree_int (si->nonzero_chars, off);
+ else
+ return -1;
+}
+
+/* Return true if SI is known to be a zero-length string. */
+
+static inline bool
+zero_length_string_p (strinfo *si)
+{
+ return si->full_string_p && integer_zerop (si->nonzero_chars);
+}
/* Return strinfo vector entry IDX. */
@@ -169,10 +211,13 @@ get_next_strinfo (strinfo *si)
return nextsi;
}
-/* Helper function for get_stridx. */
+/* Helper function for get_stridx. Return the strinfo index of the address
+ of EXP, which is available in PTR if nonnull. If OFFSET_OUT, it is
+ OK to return the index for some X <= &EXP and store &EXP - X in
+ *OFFSET_OUT. */
static int
-get_addr_stridx (tree exp, tree ptr)
+get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out)
{
HOST_WIDE_INT off;
struct stridxlist *list, *last = NULL;
@@ -192,7 +237,11 @@ get_addr_stridx (tree exp, tree ptr)
do
{
if (list->offset == off)
- return list->idx;
+ {
+ if (offset_out)
+ *offset_out = 0;
+ return list->idx;
+ }
if (list->offset > off)
return 0;
last = list;
@@ -200,14 +249,21 @@ get_addr_stridx (tree exp, tree ptr)
}
while (list);
- if (ptr && last && last->idx > 0)
+ if ((offset_out || ptr) && last && last->idx > 0)
{
+ unsigned HOST_WIDE_INT rel_off
+ = (unsigned HOST_WIDE_INT) off - last->offset;
strinfo *si = get_strinfo (last->idx);
- if (si
- && si->length
- && TREE_CODE (si->length) == INTEGER_CST
- && compare_tree_int (si->length, off - last->offset) != -1)
- return get_stridx_plus_constant (si, off - last->offset, ptr);
+ if (si && compare_nonzero_chars (si, rel_off) >= 0)
+ {
+ if (offset_out)
+ {
+ *offset_out = rel_off;
+ return last->idx;
+ }
+ else
+ return get_stridx_plus_constant (si, rel_off, ptr);
+ }
}
return 0;
}
@@ -247,10 +303,7 @@ get_stridx (tree exp)
{
strinfo *si
= get_strinfo (ssa_ver_to_stridx[SSA_NAME_VERSION (rhs1)]);
- if (si
- && si->length
- && TREE_CODE (si->length) == INTEGER_CST
- && compare_tree_int (si->length, off) != -1)
+ if (si && compare_nonzero_chars (si, off) >= 0)
return get_stridx_plus_constant (si, off, exp);
}
e = rhs1;
@@ -260,7 +313,7 @@ get_stridx (tree exp)
if (TREE_CODE (exp) == ADDR_EXPR)
{
- int idx = get_addr_stridx (TREE_OPERAND (exp, 0), exp);
+ int idx = get_addr_stridx (TREE_OPERAND (exp, 0), exp, NULL);
if (idx != 0)
return idx;
}
@@ -413,10 +466,10 @@ new_addr_stridx (tree exp)
/* Create a new strinfo. */
static strinfo *
-new_strinfo (tree ptr, int idx, tree length)
+new_strinfo (tree ptr, int idx, tree nonzero_chars, bool full_string_p)
{
strinfo *si = strinfo_pool.allocate ();
- si->length = length;
+ si->nonzero_chars = nonzero_chars;
si->ptr = ptr;
si->stmt = NULL;
si->endptr = NULL_TREE;
@@ -427,6 +480,7 @@ new_strinfo (tree ptr, int idx, tree length)
si->next = 0;
si->writable = false;
si->dont_invalidate = false;
+ si->full_string_p = full_string_p;
return si;
}
@@ -451,13 +505,53 @@ set_strinfo (int idx, strinfo *si)
(*stridx_to_strinfo)[idx] = si;
}
+/* Return the first strinfo in the related strinfo chain
+ if all strinfos in between belong to the chain, otherwise NULL. */
+
+static strinfo *
+verify_related_strinfos (strinfo *origsi)
+{
+ strinfo *si = origsi, *psi;
+
+ if (origsi->first == 0)
+ return NULL;
+ for (; si->prev; si = psi)
+ {
+ if (si->first != origsi->first)
+ return NULL;
+ psi = get_strinfo (si->prev);
+ if (psi == NULL)
+ return NULL;
+ if (psi->next != si->idx)
+ return NULL;
+ }
+ if (si->idx != si->first)
+ return NULL;
+ return si;
+}
+
+/* Set SI's endptr to ENDPTR and compute its length based on SI->ptr.
+ Use LOC for folding. */
+
+static void
+set_endptr_and_length (location_t loc, strinfo *si, tree endptr)
+{
+ si->endptr = endptr;
+ si->stmt = NULL;
+ tree start_as_size = fold_convert_loc (loc, size_type_node, si->ptr);
+ tree end_as_size = fold_convert_loc (loc, size_type_node, endptr);
+ si->nonzero_chars = fold_build2_loc (loc, MINUS_EXPR, size_type_node,
+ end_as_size, start_as_size);
+ si->full_string_p = true;
+}
+
/* Return string length, or NULL if it can't be computed. */
static tree
get_string_length (strinfo *si)
{
- if (si->length)
- return si->length;
+ if (si->nonzero_chars)
+ return si->full_string_p ? si->nonzero_chars : NULL;
if (si->stmt)
{
@@ -546,23 +640,23 @@ get_string_length (strinfo *si)
case BUILT_IN_STPCPY_CHK_CHKP:
gcc_assert (lhs != NULL_TREE);
loc = gimple_location (stmt);
- si->endptr = lhs;
- si->stmt = NULL;
- lhs = fold_convert_loc (loc, size_type_node, lhs);
- si->length = fold_convert_loc (loc, size_type_node, si->ptr);
- si->length = fold_build2_loc (loc, MINUS_EXPR, size_type_node,
- lhs, si->length);
+ set_endptr_and_length (loc, si, lhs);
+ for (strinfo *chainsi = verify_related_strinfos (si);
+ chainsi != NULL;
+ chainsi = get_next_strinfo (chainsi))
+ if (chainsi->nonzero_chars == NULL)
+ set_endptr_and_length (loc, chainsi, lhs);
break;
case BUILT_IN_MALLOC:
break;
- /* BUILT_IN_CALLOC always has si->length set. */
+ /* BUILT_IN_CALLOC always has si->nonzero_chars set. */
default:
gcc_unreachable ();
break;
}
}
- return si->length;
+ return si->nonzero_chars;
}
/* Invalidate string length information for strings whose length
@@ -581,7 +675,7 @@ maybe_invalidate (gimple *stmt)
if (!si->dont_invalidate)
{
ao_ref r;
- /* Do not use si->length. */
+ /* Do not use si->nonzero_chars. */
ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE);
if (stmt_may_clobber_ref_p_1 (stmt, &r))
{
@@ -608,7 +702,7 @@ unshare_strinfo (strinfo *si)
if (si->refcount == 1 && !strinfo_shared ())
return si;
- nsi = new_strinfo (si->ptr, si->idx, si->length);
+ nsi = new_strinfo (si->ptr, si->idx, si->nonzero_chars, si->full_string_p);
nsi->stmt = si->stmt;
nsi->endptr = si->endptr;
nsi->first = si->first;
@@ -620,70 +714,44 @@ unshare_strinfo (strinfo *si)
return nsi;
}
-/* Return first strinfo in the related strinfo chain
- if all strinfos in between belong to the chain, otherwise
- NULL. */
-
-static strinfo *
-verify_related_strinfos (strinfo *origsi)
-{
- strinfo *si = origsi, *psi;
-
- if (origsi->first == 0)
- return NULL;
- for (; si->prev; si = psi)
- {
- if (si->first != origsi->first)
- return NULL;
- psi = get_strinfo (si->prev);
- if (psi == NULL)
- return NULL;
- if (psi->next != si->idx)
- return NULL;
- }
- if (si->idx != si->first)
- return NULL;
- return si;
-}
-
/* Attempt to create a new strinfo for BASESI + OFF, or find existing
strinfo if there is any. Return it's idx, or 0 if no strinfo has
been created. */
static int
-get_stridx_plus_constant (strinfo *basesi, HOST_WIDE_INT off, tree ptr)
+get_stridx_plus_constant (strinfo *basesi, unsigned HOST_WIDE_INT off,
+ tree ptr)
{
if (TREE_CODE (ptr) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr))
return 0;
- if (basesi->length == NULL_TREE
- || TREE_CODE (basesi->length) != INTEGER_CST
- || compare_tree_int (basesi->length, off) == -1
- || !tree_fits_shwi_p (basesi->length))
+ if (compare_nonzero_chars (basesi, off) < 0
+ || !tree_fits_uhwi_p (basesi->nonzero_chars))
return 0;
- HOST_WIDE_INT len = tree_to_shwi (basesi->length) - off;
+ unsigned HOST_WIDE_INT nonzero_chars
+ = tree_to_uhwi (basesi->nonzero_chars) - off;
strinfo *si = basesi, *chainsi;
if (si->first || si->prev || si->next)
si = verify_related_strinfos (basesi);
if (si == NULL
- || si->length == NULL_TREE
- || TREE_CODE (si->length) != INTEGER_CST)
+ || si->nonzero_chars == NULL_TREE
+ || TREE_CODE (si->nonzero_chars) != INTEGER_CST)
return 0;
if (TREE_CODE (ptr) == SSA_NAME
&& ssa_ver_to_stridx.length () <= SSA_NAME_VERSION (ptr))
ssa_ver_to_stridx.safe_grow_cleared (num_ssa_names);
- gcc_checking_assert (compare_tree_int (si->length, off) != -1);
+ gcc_checking_assert (compare_tree_int (si->nonzero_chars, off) != -1);
for (chainsi = si; chainsi->next; chainsi = si)
{
si = get_next_strinfo (chainsi);
if (si == NULL
- || si->length == NULL_TREE
- || TREE_CODE (si->length) != INTEGER_CST)
+ || si->nonzero_chars == NULL_TREE
+ || TREE_CODE (si->nonzero_chars) != INTEGER_CST)
break;
- int r = compare_tree_int (si->length, len);
+ int r = compare_tree_int (si->nonzero_chars, nonzero_chars);
if (r != 1)
{
if (r == 0)
@@ -705,7 +773,8 @@ get_stridx_plus_constant (strinfo *basesi, HOST_WIDE_INT off, tree ptr)
int idx = new_stridx (ptr);
if (idx == 0)
return 0;
- si = new_strinfo (ptr, idx, build_int_cst (size_type_node, len));
+ si = new_strinfo (ptr, idx, build_int_cst (size_type_node, nonzero_chars),
+ basesi->full_string_p);
set_strinfo (idx, si);
if (chainsi->next)
{
@@ -717,7 +786,7 @@ get_stridx_plus_constant (strinfo *basesi, HOST_WIDE_INT off, tree ptr)
if (chainsi->first == 0)
chainsi->first = chainsi->idx;
chainsi->next = idx;
- if (chainsi->endptr == NULL_TREE && len == 0)
+ if (chainsi->endptr == NULL_TREE && zero_length_string_p (si))
chainsi->endptr = ptr;
si->endptr = chainsi->endptr;
si->prev = chainsi->idx;
@@ -749,7 +818,8 @@ zero_length_string (tree ptr, strinfo *chainsi)
{
do
{
- gcc_assert (si->length || si->stmt);
+ /* We shouldn't mix delayed and non-delayed lengths. */
+ gcc_assert (si->full_string_p);
if (si->endptr == NULL_TREE)
{
si = unshare_strinfo (si);
@@ -759,7 +829,7 @@ zero_length_string (tree ptr, strinfo *chainsi)
si = get_next_strinfo (si);
}
while (si != NULL);
- if (chainsi->length && integer_zerop (chainsi->length))
+ if (zero_length_string_p (chainsi))
{
if (chainsi->next)
{
@@ -770,18 +840,23 @@ zero_length_string (tree ptr, strinfo *chainsi)
return chainsi;
}
}
- else if (chainsi->first || chainsi->prev || chainsi->next)
+ else
{
- chainsi = unshare_strinfo (chainsi);
- chainsi->first = 0;
- chainsi->prev = 0;
- chainsi->next = 0;
+ /* We shouldn't mix delayed and non-delayed lengths. */
+ gcc_assert (chainsi->full_string_p);
+ if (chainsi->first || chainsi->prev || chainsi->next)
+ {
+ chainsi = unshare_strinfo (chainsi);
+ chainsi->first = 0;
+ chainsi->prev = 0;
+ chainsi->next = 0;
+ }
}
}
idx = new_stridx (ptr);
if (idx == 0)
return NULL;
- si = new_strinfo (ptr, idx, build_int_cst (size_type_node, 0));
+ si = new_strinfo (ptr, idx, build_int_cst (size_type_node, 0), true);
set_strinfo (idx, si);
si->endptr = ptr;
if (chainsi != NULL)
@@ -799,9 +874,11 @@ zero_length_string (tree ptr, strinfo *chainsi)
return si;
}
-/* For strinfo ORIGSI whose length has been just updated
- update also related strinfo lengths (add ADJ to each,
- but don't adjust ORIGSI). */
+/* For strinfo ORIGSI whose length has been just updated, adjust other
+ related strinfos so that they match the new ORIGSI. This involves:
+
+ - adding ADJ to the nonzero_chars fields
+ - copying full_string_p from the new ORIGSI. */
static void
adjust_related_strinfos (location_t loc, strinfo *origsi, tree adj)
@@ -820,18 +897,15 @@ adjust_related_strinfos (location_t loc, strinfo *origsi, tree adj)
tree tem;
si = unshare_strinfo (si);
- if (si->length)
- {
- tem = fold_convert_loc (loc, TREE_TYPE (si->length), adj);
- si->length = fold_build2_loc (loc, PLUS_EXPR,
- TREE_TYPE (si->length), si->length,
- tem);
- }
- else if (si->stmt != NULL)
- /* Delayed length computation is unaffected. */
- ;
- else
- gcc_unreachable ();
+ /* We shouldn't see delayed lengths here; the caller must have
+ calculated the old length in order to calculate the
+ adjustment. */
+ gcc_assert (si->nonzero_chars);
+ tem = fold_convert_loc (loc, TREE_TYPE (si->nonzero_chars), adj);
+ si->nonzero_chars = fold_build2_loc (loc, PLUS_EXPR,
+ TREE_TYPE (si->nonzero_chars),
+ si->nonzero_chars, tem);
+ si->full_string_p = origsi->full_string_p;
si->endptr = NULL_TREE;
si->dont_invalidate = true;
@@ -1000,11 +1074,8 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
}
}
- if (!is_strcat)
- {
- if (si->length == NULL_TREE || !integer_zerop (si->length))
- return;
- }
+ if (!is_strcat && !zero_length_string_p (si))
+ return;
if (is_gimple_assign (last.stmt))
{
@@ -1118,12 +1189,13 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
}
if (si != NULL
- && TREE_CODE (si->length) != SSA_NAME
- && TREE_CODE (si->length) != INTEGER_CST
+ && TREE_CODE (si->nonzero_chars) != SSA_NAME
+ && TREE_CODE (si->nonzero_chars) != INTEGER_CST
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
{
si = unshare_strinfo (si);
- si->length = lhs;
+ si->nonzero_chars = lhs;
+ gcc_assert (si->full_string_p);
}
return;
}
@@ -1132,11 +1204,40 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
return;
if (idx == 0)
idx = new_stridx (src);
- else if (get_strinfo (idx) != NULL)
- return;
+ else
+ {
+ strinfo *si = get_strinfo (idx);
+ if (si != NULL)
+ {
+ if (!si->full_string_p && !si->stmt)
+ {
+ /* Until now we only had a lower bound on the string length.
+ Install LHS as the actual length. */
+ si = unshare_strinfo (si);
+ tree old = si->nonzero_chars;
+ si->nonzero_chars = lhs;
+ si->full_string_p = true;
+ if (TREE_CODE (old) == INTEGER_CST)
+ {
+ location_t loc = gimple_location (stmt);
+ old = fold_convert_loc (loc, TREE_TYPE (lhs), old);
+ tree adj = fold_build2_loc (loc, MINUS_EXPR,
+ TREE_TYPE (lhs), lhs, old);
+ adjust_related_strinfos (loc, si, adj);
+ }
+ else
+ {
+ si->first = 0;
+ si->prev = 0;
+ si->next = 0;
+ }
+ }
+ return;
+ }
+ }
if (idx)
{
- strinfo *si = new_strinfo (src, idx, lhs);
+ strinfo *si = new_strinfo (src, idx, lhs, true);
set_strinfo (idx, si);
find_equal_ptrs (src, idx);
}
@@ -1240,7 +1341,7 @@ handle_builtin_strchr (gimple_stmt_iterator *gsi)
tree srcu = fold_convert_loc (loc, size_type_node, src);
tree length = fold_build2_loc (loc, MINUS_EXPR,
size_type_node, lhsu, srcu);
- strinfo *si = new_strinfo (src, idx, length);
+ strinfo *si = new_strinfo (src, idx, length, true);
si->endptr = lhs;
set_strinfo (idx, si);
find_equal_ptrs (src, idx);
@@ -1329,9 +1430,10 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
}
if (olddsi != NULL)
{
- oldlen = olddsi->length;
+ oldlen = olddsi->nonzero_chars;
dsi = unshare_strinfo (olddsi);
- dsi->length = srclen;
+ dsi->nonzero_chars = srclen;
+ dsi->full_string_p = (srclen != NULL_TREE);
/* Break the chain, so adjust_related_strinfo on later pointers in
the chain won't adjust this one anymore. */
dsi->next = 0;
@@ -1340,14 +1442,14 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
}
else
{
- dsi = new_strinfo (dst, didx, srclen);
+ dsi = new_strinfo (dst, didx, srclen, srclen != NULL_TREE);
set_strinfo (didx, dsi);
find_equal_ptrs (dst, didx);
}
dsi->writable = true;
dsi->dont_invalidate = true;
- if (dsi->length == NULL_TREE)
+ if (dsi->nonzero_chars == NULL_TREE)
{
strinfo *chainsi;
@@ -1368,7 +1470,8 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
invalidated. */
chainsi = unshare_strinfo (chainsi);
chainsi->stmt = stmt;
- chainsi->length = NULL_TREE;
+ chainsi->nonzero_chars = NULL_TREE;
+ chainsi->full_string_p = false;
chainsi->endptr = NULL_TREE;
chainsi->dont_invalidate = true;
}
@@ -1536,31 +1639,61 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
&& !integer_zerop (len))
adjust_last_stmt (olddsi, stmt, false);
+ bool full_string_p;
if (idx > 0)
{
gimple *def_stmt;
- /* Handle memcpy (x, y, l) where l is strlen (y) + 1. */
+ /* Handle memcpy (x, y, l) where l's relationship with strlen (y)
+ is known. */
si = get_strinfo (idx);
- if (si == NULL || si->length == NULL_TREE)
- return;
- if (TREE_CODE (len) != SSA_NAME)
- return;
- def_stmt = SSA_NAME_DEF_STMT (len);
- if (!is_gimple_assign (def_stmt)
- || gimple_assign_rhs_code (def_stmt) != PLUS_EXPR
- || gimple_assign_rhs1 (def_stmt) != si->length
- || !integer_onep (gimple_assign_rhs2 (def_stmt)))
+ if (si == NULL || si->nonzero_chars == NULL_TREE)
return;
+ if (TREE_CODE (len) == INTEGER_CST
+ && TREE_CODE (si->nonzero_chars) == INTEGER_CST)
+ {
+ if (tree_int_cst_le (len, si->nonzero_chars))
+ {
+ /* Copying LEN nonzero characters, where LEN is constant. */
+ newlen = len;
+ full_string_p = false;
+ }
+ else
+ {
+ /* Copying the whole of the analyzed part of SI. */
+ newlen = si->nonzero_chars;
+ full_string_p = si->full_string_p;
+ }
+ }
+ else
+ {
+ if (!si->full_string_p)
+ return;
+ if (TREE_CODE (len) != SSA_NAME)
+ return;
+ def_stmt = SSA_NAME_DEF_STMT (len);
+ if (!is_gimple_assign (def_stmt)
+ || gimple_assign_rhs_code (def_stmt) != PLUS_EXPR
+ || gimple_assign_rhs1 (def_stmt) != si->nonzero_chars
+ || !integer_onep (gimple_assign_rhs2 (def_stmt)))
+ return;
+ /* Copying variable-length string SI (and no more). */
+ newlen = si->nonzero_chars;
+ full_string_p = true;
+ }
}
else
{
si = NULL;
/* Handle memcpy (x, "abcd", 5) or
memcpy (x, "abc\0uvw", 7). */
- if (!tree_fits_uhwi_p (len)
- || tree_to_uhwi (len) <= (unsigned HOST_WIDE_INT) ~idx)
+ if (!tree_fits_uhwi_p (len))
return;
+
+ unsigned HOST_WIDE_INT clen = tree_to_uhwi (len);
+ unsigned HOST_WIDE_INT nonzero_chars = ~idx;
+ newlen = build_int_cst (size_type_node, MIN (nonzero_chars, clen));
+ full_string_p = clen > nonzero_chars;
}
if (olddsi != NULL && TREE_CODE (len) == SSA_NAME)
@@ -1572,16 +1705,13 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
if (didx == 0)
return;
}
- if (si != NULL)
- newlen = si->length;
- else
- newlen = build_int_cst (size_type_node, ~idx);
oldlen = NULL_TREE;
if (olddsi != NULL)
{
dsi = unshare_strinfo (olddsi);
- oldlen = olddsi->length;
- dsi->length = newlen;
+ oldlen = olddsi->nonzero_chars;
+ dsi->nonzero_chars = newlen;
+ dsi->full_string_p = full_string_p;
/* Break the chain, so adjust_related_strinfo on later pointers in
the chain won't adjust this one anymore. */
dsi->next = 0;
@@ -1590,7 +1720,7 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
}
else
{
- dsi = new_strinfo (dst, didx, newlen);
+ dsi = new_strinfo (dst, didx, newlen, full_string_p);
set_strinfo (didx, dsi);
find_equal_ptrs (dst, didx);
}
@@ -1603,12 +1733,11 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
if (oldlen == NULL_TREE)
;
else if (integer_zerop (oldlen))
- adj = dsi->length;
+ adj = newlen;
else if (TREE_CODE (oldlen) == INTEGER_CST
- || TREE_CODE (dsi->length) == INTEGER_CST)
- adj = fold_build2_loc (loc, MINUS_EXPR,
- TREE_TYPE (dsi->length), dsi->length,
- fold_convert_loc (loc, TREE_TYPE (dsi->length),
+ || TREE_CODE (newlen) == INTEGER_CST)
+ adj = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (newlen), newlen,
+ fold_convert_loc (loc, TREE_TYPE (newlen),
oldlen));
if (adj != NULL_TREE)
adjust_related_strinfos (loc, dsi, adj);
@@ -1620,27 +1749,30 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
if (si != NULL)
si->dont_invalidate = true;
- lhs = gimple_call_lhs (stmt);
- switch (bcode)
+ if (full_string_p)
{
- case BUILT_IN_MEMCPY:
- case BUILT_IN_MEMCPY_CHK:
- case BUILT_IN_MEMCPY_CHKP:
- case BUILT_IN_MEMCPY_CHK_CHKP:
- /* Allow adjust_last_stmt to decrease this memcpy's size. */
- laststmt.stmt = stmt;
- laststmt.len = dsi->length;
- laststmt.stridx = dsi->idx;
- if (lhs)
- ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx;
- break;
- case BUILT_IN_MEMPCPY:
- case BUILT_IN_MEMPCPY_CHK:
- case BUILT_IN_MEMPCPY_CHKP:
- case BUILT_IN_MEMPCPY_CHK_CHKP:
- break;
- default:
- gcc_unreachable ();
+ lhs = gimple_call_lhs (stmt);
+ switch (bcode)
+ {
+ case BUILT_IN_MEMCPY:
+ case BUILT_IN_MEMCPY_CHK:
+ case BUILT_IN_MEMCPY_CHKP:
+ case BUILT_IN_MEMCPY_CHK_CHKP:
+ /* Allow adjust_last_stmt to decrease this memcpy's size. */
+ laststmt.stmt = stmt;
+ laststmt.len = dsi->nonzero_chars;
+ laststmt.stridx = dsi->idx;
+ if (lhs)
+ ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx;
+ break;
+ case BUILT_IN_MEMPCPY:
+ case BUILT_IN_MEMPCPY_CHK:
+ case BUILT_IN_MEMPCPY_CHKP:
+ case BUILT_IN_MEMPCPY_CHK_CHKP:
+ break;
+ default:
+ gcc_unreachable ();
+ }
}
}
@@ -1689,14 +1821,15 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
}
if (dsi == NULL)
{
- dsi = new_strinfo (dst, didx, NULL_TREE);
+ dsi = new_strinfo (dst, didx, NULL_TREE, false);
set_strinfo (didx, dsi);
find_equal_ptrs (dst, didx);
}
else
{
dsi = unshare_strinfo (dsi);
- dsi->length = NULL_TREE;
+ dsi->nonzero_chars = NULL_TREE;
+ dsi->full_string_p = false;
dsi->next = 0;
dsi->endptr = NULL_TREE;
}
@@ -1720,7 +1853,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
}
loc = gimple_location (stmt);
- dstlen = dsi->length;
+ dstlen = dsi->nonzero_chars;
endptr = dsi->endptr;
dsi = unshare_strinfo (dsi);
@@ -1730,14 +1863,17 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
if (srclen != NULL_TREE)
{
- dsi->length = fold_build2_loc (loc, PLUS_EXPR, TREE_TYPE (dsi->length),
- dsi->length, srclen);
+ dsi->nonzero_chars = fold_build2_loc (loc, PLUS_EXPR,
+ TREE_TYPE (dsi->nonzero_chars),
+ dsi->nonzero_chars, srclen);
+ gcc_assert (dsi->full_string_p);
adjust_related_strinfos (loc, dsi, srclen);
dsi->dont_invalidate = true;
}
else
{
- dsi->length = NULL;
+ dsi->nonzero_chars = NULL;
+ dsi->full_string_p = false;
if (lhs == NULL_TREE && builtin_decl_implicit_p (BUILT_IN_STPCPY))
dsi->dont_invalidate = true;
}
@@ -1870,7 +2006,7 @@ handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi)
tree length = NULL_TREE;
if (bcode == BUILT_IN_CALLOC)
length = build_int_cst (size_type_node, 0);
- strinfo *si = new_strinfo (lhs, idx, length);
+ strinfo *si = new_strinfo (lhs, idx, length, length != NULL_TREE);
if (bcode == BUILT_IN_CALLOC)
si->endptr = lhs;
set_strinfo (idx, si);
@@ -1912,7 +2048,8 @@ handle_builtin_memset (gimple_stmt_iterator *gsi)
gimple_stmt_iterator gsi1 = gsi_for_stmt (stmt1);
update_gimple_call (&gsi1, builtin_decl_implicit (BUILT_IN_CALLOC), 2,
size, build_one_cst (size_type_node));
- si1->length = build_int_cst (size_type_node, 0);
+ si1->nonzero_chars = build_int_cst (size_type_node, 0);
+ si1->full_string_p = true;
si1->stmt = gsi_stmt (gsi1);
}
else
@@ -2044,18 +2181,20 @@ handle_pointer_plus (gimple_stmt_iterator *gsi)
}
si = get_strinfo (idx);
- if (si == NULL || si->length == NULL_TREE)
+ if (si == NULL || si->nonzero_chars == NULL_TREE)
return;
off = gimple_assign_rhs2 (stmt);
zsi = NULL;
- if (operand_equal_p (si->length, off, 0))
+ if (si->full_string_p && operand_equal_p (si->nonzero_chars, off, 0))
zsi = zero_length_string (lhs, si);
else if (TREE_CODE (off) == SSA_NAME)
{
gimple *def_stmt = SSA_NAME_DEF_STMT (off);
if (gimple_assign_single_p (def_stmt)
- && operand_equal_p (si->length, gimple_assign_rhs1 (def_stmt), 0))
+ && si->full_string_p
+ && operand_equal_p (si->nonzero_chars,
+ gimple_assign_rhs1 (def_stmt), 0))
zsi = zero_length_string (lhs, si);
}
if (zsi != NULL
@@ -2081,63 +2220,63 @@ handle_char_store (gimple_stmt_iterator *gsi)
strinfo *si = NULL;
gimple *stmt = gsi_stmt (*gsi);
tree ssaname = NULL_TREE, lhs = gimple_assign_lhs (stmt);
+ tree rhs = gimple_assign_rhs1 (stmt);
+ unsigned HOST_WIDE_INT offset = 0;
if (TREE_CODE (lhs) == MEM_REF
&& TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME)
{
- if (integer_zerop (TREE_OPERAND (lhs, 1)))
+ tree mem_offset = TREE_OPERAND (lhs, 1);
+ if (tree_fits_uhwi_p (mem_offset))
{
- ssaname = TREE_OPERAND (lhs, 0);
- idx = get_stridx (ssaname);
+ /* Get the strinfo for the base, and use it if it starts with at
+ least OFFSET nonzero characters. This is trivially true if
+ OFFSET is zero. */
+ offset = tree_to_uhwi (mem_offset);
+ idx = get_stridx (TREE_OPERAND (lhs, 0));
+ if (idx > 0)
+ si = get_strinfo (idx);
+ if (offset == 0)
+ ssaname = TREE_OPERAND (lhs, 0);
+ else if (si == NULL || compare_nonzero_chars (si, offset) < 0)
+ return true;
}
}
else
- idx = get_addr_stridx (lhs, NULL_TREE);
+ {
+ idx = get_addr_stridx (lhs, NULL_TREE, &offset);
+ if (idx > 0)
+ si = get_strinfo (idx);
+ }
- if (idx > 0)
+ bool storing_zero_p = initializer_zerop (rhs);
+ bool storing_nonzero_p = (!storing_zero_p
+ && TREE_CODE (rhs) == INTEGER_CST
+ && integer_nonzerop (rhs));
+
+ if (si != NULL)
{
- si = get_strinfo (idx);
- if (si != NULL && si->length != NULL_TREE && integer_zerop (si->length))
+ int cmp = compare_nonzero_chars (si, offset);
+ gcc_assert (offset == 0 || cmp >= 0);
+ if (storing_zero_p && cmp == 0 && si->full_string_p)
{
- if (initializer_zerop (gimple_assign_rhs1 (stmt)))
+ /* When overwriting a '\0' with a '\0', the store can be removed
+ if we know it has been stored in the current function. */
+ if (!stmt_could_throw_p (stmt) && si->writable)
{
- /* When storing '\0', the store can be removed
- if we know it has been stored in the current function. */
- if (!stmt_could_throw_p (stmt) && si->writable)
- {
- unlink_stmt_vdef (stmt);
- release_defs (stmt);
- gsi_remove (gsi, true);
- return false;
- }
- else
- {
- si->writable = true;
- gsi_next (gsi);
- return false;
- }
+ unlink_stmt_vdef (stmt);
+ release_defs (stmt);
+ gsi_remove (gsi, true);
+ return false;
}
else
- /* Otherwise this statement overwrites the '\0' with
- something, if the previous stmt was a memcpy,
- its length may be decreased. */
- adjust_last_stmt (si, stmt, false);
- }
- else if (si != NULL && integer_zerop (gimple_assign_rhs1 (stmt)))
- {
- si = unshare_strinfo (si);
- si->length = build_int_cst (size_type_node, 0);
- si->endptr = NULL;
- si->prev = 0;
- si->next = 0;
- si->stmt = NULL;
- si->first = 0;
- si->writable = true;
- if (ssaname && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
- si->endptr = ssaname;
- si->dont_invalidate = true;
+ {
+ si->writable = true;
+ gsi_next (gsi);
+ return false;
+ }
}
- /* If si->length is non-zero constant, we aren't overwriting '\0',
+ /* If si->nonzero_chars > OFFSET, we aren't overwriting '\0',
and if we aren't storing '\0', we know that the length of the
string and any other zero terminated string in memory remains
the same. In that case we move to the next gimple statement and
@@ -2157,35 +2296,74 @@ handle_char_store (gimple_stmt_iterator *gsi)
bar (len, len2, len3, len4);
}
*/
- else if (si != NULL && si->length != NULL_TREE
- && TREE_CODE (si->length) == INTEGER_CST
- && integer_nonzerop (gimple_assign_rhs1 (stmt)))
+ else if (storing_nonzero_p && cmp > 0)
{
gsi_next (gsi);
return false;
}
+ else if (storing_zero_p || storing_nonzero_p || (offset != 0 && cmp > 0))
+ {
+ /* When storing_nonzero_p, we know that the string now starts
+ with OFFSET + 1 nonzero characters, but don't know whether
+ there's a following nul terminator.
+
+ When storing_zero_p, we know that the string is now OFFSET
+ characters long.
+
+ Otherwise, we're storing an unknown value at offset OFFSET,
+ so need to clip the nonzero_chars to OFFSET. */
+ location_t loc = gimple_location (stmt);
+ tree oldlen = si->nonzero_chars;
+ if (cmp == 0 && si->full_string_p)
+ /* We're overwriting the nul terminator with a nonzero or
+ unknown character. If the previous stmt was a memcpy,
+ its length may be decreased. */
+ adjust_last_stmt (si, stmt, false);
+ si = unshare_strinfo (si);
+ if (storing_nonzero_p)
+ si->nonzero_chars = build_int_cst (size_type_node, offset + 1);
+ else
+ si->nonzero_chars = build_int_cst (size_type_node, offset);
+ si->full_string_p = storing_zero_p;
+ if (storing_zero_p
+ && ssaname
+ && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
+ si->endptr = ssaname;
+ else
+ si->endptr = NULL;
+ si->next = 0;
+ si->stmt = NULL;
+ si->writable = true;
+ si->dont_invalidate = true;
+ if (oldlen)
+ {
+ tree adj = fold_build2_loc (loc, MINUS_EXPR, size_type_node,
+ si->nonzero_chars, oldlen);
+ adjust_related_strinfos (loc, si, adj);
+ }
+ else
+ si->prev = 0;
+ }
}
- else if (idx == 0 && initializer_zerop (gimple_assign_rhs1 (stmt)))
+ else if (idx == 0 && (storing_zero_p || storing_nonzero_p))
{
if (ssaname)
- {
- si = zero_length_string (ssaname, NULL);
- if (si != NULL)
- si->dont_invalidate = true;
- }
+ idx = new_stridx (ssaname);
else
+ idx = new_addr_stridx (lhs);
+ if (idx != 0)
{
- int idx = new_addr_stridx (lhs);
- if (idx != 0)
- {
- si = new_strinfo (build_fold_addr_expr (lhs), idx,
- build_int_cst (size_type_node, 0));
- set_strinfo (idx, si);
- si->dont_invalidate = true;
- }
+ tree ptr = (ssaname ? ssaname : build_fold_addr_expr (lhs));
+ tree len = storing_nonzero_p ? size_one_node : size_zero_node;
+ si = new_strinfo (ptr, idx, len, storing_zero_p);
+ set_strinfo (idx, si);
+ if (storing_zero_p
+ && ssaname
+ && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
+ si->endptr = ssaname;
+ si->dont_invalidate = true;
+ si->writable = true;
}
- if (si != NULL)
- si->writable = true;
}
else if (idx == 0
&& TREE_CODE (gimple_assign_rhs1 (stmt)) == STRING_CST
@@ -2200,14 +2378,14 @@ handle_char_store (gimple_stmt_iterator *gsi)
if (idx != 0)
{
si = new_strinfo (build_fold_addr_expr (lhs), idx,
- build_int_cst (size_type_node, l));
+ build_int_cst (size_type_node, l), true);
set_strinfo (idx, si);
si->dont_invalidate = true;
}
}
}
- if (si != NULL && initializer_zerop (gimple_assign_rhs1 (stmt)))
+ if (si != NULL && offset == 0 && storing_zero_p)
{
/* Allow adjust_last_stmt to remove it if the stored '\0'
is immediately overwritten. */