length and 2 for maximum value ARG can have. */
static bool
-get_maxval_strlen (tree arg, tree *length, bitmap visited, int type)
+get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
{
tree var, val;
gimple def_stmt;
return false;
/* If we were already here, break the infinite cycle. */
- if (!bitmap_set_bit (visited, SSA_NAME_VERSION (arg)))
+ if (!*visited)
+ *visited = BITMAP_ALLOC (NULL);
+ if (!bitmap_set_bit (*visited, SSA_NAME_VERSION (arg)))
return true;
var = arg;
}
}
+tree
+get_maxval_strlen (tree arg, int type)
+{
+ bitmap visited = NULL;
+ tree len = NULL_TREE;
+ if (!get_maxval_strlen (arg, &len, &visited, type))
+ len = NULL_TREE;
+ if (visited)
+ BITMAP_FREE (visited);
+
+ return len;
+}
+
/* Fold function call to builtin strcpy with arguments DEST and SRC.
If LEN is not NULL, it represents the length of the string to be
static bool
gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
- location_t loc, tree dest, tree src, tree len)
+ tree dest, tree src)
{
+ location_t loc = gimple_location (gsi_stmt (*gsi));
tree fn;
/* If SRC and DEST are the same (and not volatile), return DEST. */
if (!fn)
return false;
+ tree len = get_maxval_strlen (src, 1);
if (!len)
- {
- len = c_strlen (src, 1);
- if (! len || TREE_SIDE_EFFECTS (len))
- return NULL_TREE;
- }
+ return false;
len = fold_convert_loc (loc, size_type_node, len);
len = size_binop_loc (loc, PLUS_EXPR, len, build_int_cst (size_type_node, 1));
Return NULL_TREE if no simplification can be made. */
static bool
-gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi, location_t loc,
- tree dest, tree src, tree len, tree slen)
+gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
+ tree dest, tree src, tree len)
{
+ location_t loc = gimple_location (gsi_stmt (*gsi));
tree fn;
/* If the LEN parameter is zero, return DEST. */
/* We can't compare slen with len as constants below if len is not a
constant. */
- if (len == 0 || TREE_CODE (len) != INTEGER_CST)
+ if (TREE_CODE (len) != INTEGER_CST)
return false;
- if (!slen)
- slen = c_strlen (src, 1);
-
/* Now, we must be passed a constant src ptr parameter. */
- if (slen == 0 || TREE_CODE (slen) != INTEGER_CST)
+ tree slen = get_maxval_strlen (src, 1);
+ if (!slen || TREE_CODE (slen) != INTEGER_CST)
return false;
slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
form of the builtin function call. */
static bool
-gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi,
- location_t loc ATTRIBUTE_UNUSED, tree dst, tree src,
- tree len)
+gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi, tree dst, tree src)
{
gimple stmt = gsi_stmt (*gsi);
+ location_t loc = gimple_location (stmt);
const char *p = c_getstr (src);
/* If the length of the source string isn't computable don't
split strcat into strlen and memcpy. */
+ tree len = get_maxval_strlen (src, 0);
if (! len)
- len = c_strlen (src, 1);
- if (! len || TREE_SIDE_EFFECTS (len))
return false;
/* Create strlen (dst). */
static bool
gimple_fold_builtin_fputs (gimple_stmt_iterator *gsi,
- location_t loc ATTRIBUTE_UNUSED,
tree arg0, tree arg1,
- bool ignore, bool unlocked, tree len)
+ bool unlocked)
{
+ gimple stmt = gsi_stmt (*gsi);
+
/* If we're using an unlocked function, assume the other unlocked
functions exist explicitly. */
tree const fn_fputc = (unlocked
: builtin_decl_implicit (BUILT_IN_FWRITE));
/* If the return value is used, don't do the transformation. */
- if (!ignore)
+ if (gimple_call_lhs (stmt))
return false;
- if (! len)
- len = c_strlen (arg0, 0);
-
/* Get the length of the string passed to fputs. If the length
can't be determined, punt. */
+ tree len = get_maxval_strlen (arg0, 0);
if (!len
|| TREE_CODE (len) != INTEGER_CST)
return false;
static bool
gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
- location_t loc,
tree dest, tree src, tree len, tree size,
- tree maxlen, bool ignore,
enum built_in_function fcode)
{
+ gimple stmt = gsi_stmt (*gsi);
+ location_t loc = gimple_location (stmt);
+ bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
tree fn;
/* If SRC and DEST are the same (and not volatile), return DEST
if (! tree_fits_uhwi_p (size))
return false;
+ tree maxlen = get_maxval_strlen (len, 2);
if (! integer_all_onesp (size))
{
if (! tree_fits_uhwi_p (len))
static bool
gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
- location_t loc, tree dest,
+ tree dest,
tree src, tree size,
- tree maxlen, bool ignore,
enum built_in_function fcode)
{
+ gimple stmt = gsi_stmt (*gsi);
+ location_t loc = gimple_location (stmt);
+ bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
tree len, fn;
/* If SRC and DEST are the same (and not volatile), return DEST. */
if (! tree_fits_uhwi_p (size))
return false;
+ tree maxlen = get_maxval_strlen (src, 1);
if (! integer_all_onesp (size))
{
len = c_strlen (src, 1);
static bool
gimple_fold_builtin_stxncpy_chk (gimple_stmt_iterator *gsi,
tree dest, tree src,
- tree len, tree size, tree maxlen, bool ignore,
+ tree len, tree size,
enum built_in_function fcode)
{
+ gimple stmt = gsi_stmt (*gsi);
+ bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
tree fn;
if (fcode == BUILT_IN_STPNCPY_CHK && ignore)
if (! tree_fits_uhwi_p (size))
return false;
+ tree maxlen = get_maxval_strlen (len, 2);
if (! integer_all_onesp (size))
{
if (! tree_fits_uhwi_p (len))
static bool
gimple_fold_builtin_snprintf_chk (gimple_stmt_iterator *gsi,
- tree maxlen, enum built_in_function fcode)
+ enum built_in_function fcode)
{
gimple stmt = gsi_stmt (*gsi);
tree dest, size, len, fn, fmt, flag;
if (! integer_all_onesp (size))
{
+ tree maxlen = get_maxval_strlen (len, 2);
if (! tree_fits_uhwi_p (len))
{
/* If LEN is not constant, try MAXLEN too.
the caller does not use the returned value of the function. */
static bool
-gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi, tree orig_len)
+gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
{
gimple stmt = gsi_stmt (*gsi);
tree dest = gimple_call_arg (stmt, 0);
if (!orig)
return false;
- if (gimple_call_lhs (stmt)
- && !orig_len)
+ tree orig_len = NULL_TREE;
+ if (gimple_call_lhs (stmt))
{
- orig_len = c_strlen (orig, 1);
+ orig_len = get_maxval_strlen (orig, 0);
if (!orig_len)
return false;
}
the caller does not use the returned value of the function. */
static bool
-gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi, tree orig_len)
+gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
{
gimple stmt = gsi_stmt (*gsi);
tree dest = gimple_call_arg (stmt, 0);
if (!orig)
return false;
+ tree orig_len = get_maxval_strlen (orig, 0);
if (!orig_len)
- {
- orig_len = c_strlen (orig, 1);
- if (!orig_len)
- return false;
- }
+ return false;
/* We could expand this as
memcpy (str1, str2, cst - 1); str1[cst - 1] = '\0';
/* Fold a call to __builtin_strlen with known length LEN. */
static bool
-gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi, tree len)
+gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
{
- if (!len)
- {
- gimple stmt = gsi_stmt (*gsi);
- len = c_strlen (gimple_call_arg (stmt, 0), 0);
- }
+ gimple stmt = gsi_stmt (*gsi);
+ tree len = get_maxval_strlen (gimple_call_arg (stmt, 0), 0);
if (!len)
return false;
replace_call_with_value (gsi, len);
}
-/* Fold builtins at *GSI with knowledge about a length argument. */
+/* Fold the non-target builtin at *GSI and return whether any simplification
+ was made. */
static bool
-gimple_fold_builtin_with_strlen (gimple_stmt_iterator *gsi)
+gimple_fold_builtin (gimple_stmt_iterator *gsi)
{
gimple stmt = gsi_stmt (*gsi);
- tree val[4];
- tree a;
- int arg_idx, type;
- bitmap visited;
- bool ignore;
- location_t loc = gimple_location (stmt);
-
- ignore = (gimple_call_lhs (stmt) == NULL);
-
- /* Limit the work only for builtins we know how to simplify. */
tree callee = gimple_call_fndecl (stmt);
- switch (DECL_FUNCTION_CODE (callee))
- {
- case BUILT_IN_STRLEN:
- case BUILT_IN_FPUTS:
- case BUILT_IN_FPUTS_UNLOCKED:
- arg_idx = 0;
- type = 0;
- break;
- case BUILT_IN_STRCPY:
- case BUILT_IN_STRNCPY:
- case BUILT_IN_STRCAT:
- arg_idx = 1;
- type = 0;
- break;
- case BUILT_IN_MEMCPY_CHK:
- case BUILT_IN_MEMPCPY_CHK:
- case BUILT_IN_MEMMOVE_CHK:
- case BUILT_IN_MEMSET_CHK:
- case BUILT_IN_STRNCPY_CHK:
- case BUILT_IN_STPNCPY_CHK:
- arg_idx = 2;
- type = 2;
- break;
- case BUILT_IN_STRCPY_CHK:
- case BUILT_IN_STPCPY_CHK:
- arg_idx = 1;
- type = 1;
- break;
- case BUILT_IN_SNPRINTF_CHK:
- case BUILT_IN_VSNPRINTF_CHK:
- arg_idx = 1;
- type = 2;
- break;
- case BUILT_IN_SPRINTF:
- arg_idx = 2;
- type = 0;
- break;
- case BUILT_IN_SNPRINTF:
- arg_idx = 3;
- type = 0;
- break;
- default:
- return false;
- }
-
- int nargs = gimple_call_num_args (stmt);
-
- /* Try to use the dataflow information gathered by the CCP process. */
- visited = BITMAP_ALLOC (NULL);
- bitmap_clear (visited);
-
- memset (val, 0, sizeof (val));
- if (arg_idx < nargs)
- {
- a = gimple_call_arg (stmt, arg_idx);
- if (!get_maxval_strlen (a, &val[arg_idx], visited, type)
- || !is_gimple_val (val[arg_idx]))
- val[arg_idx] = NULL_TREE;
- }
- BITMAP_FREE (visited);
+ /* Give up for always_inline inline builtins until they are
+ inlined. */
+ if (avoid_folding_inline_builtin (callee))
+ return false;
switch (DECL_FUNCTION_CODE (callee))
{
+ case BUILT_IN_BZERO:
+ return gimple_fold_builtin_memset (gsi, integer_zero_node,
+ gimple_call_arg (stmt, 1));
+ case BUILT_IN_MEMSET:
+ return gimple_fold_builtin_memset (gsi,
+ gimple_call_arg (stmt, 1),
+ gimple_call_arg (stmt, 2));
+ case BUILT_IN_BCOPY:
+ return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 1),
+ gimple_call_arg (stmt, 0), 3);
+ case BUILT_IN_MEMCPY:
+ return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1), 0);
+ case BUILT_IN_MEMPCPY:
+ return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1), 1);
+ case BUILT_IN_MEMMOVE:
+ return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1), 3);
+ case BUILT_IN_SPRINTF_CHK:
+ case BUILT_IN_VSPRINTF_CHK:
+ return gimple_fold_builtin_sprintf_chk (gsi, DECL_FUNCTION_CODE (callee));
+ case BUILT_IN_STRCAT_CHK:
+ return gimple_fold_builtin_strcat_chk (gsi);
case BUILT_IN_STRLEN:
- return gimple_fold_builtin_strlen (gsi, val[0]);
-
+ return gimple_fold_builtin_strlen (gsi);
case BUILT_IN_STRCPY:
- return gimple_fold_builtin_strcpy (gsi, loc,
+ return gimple_fold_builtin_strcpy (gsi,
gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- val[1]);
-
+ gimple_call_arg (stmt, 1));
case BUILT_IN_STRNCPY:
- return gimple_fold_builtin_strncpy (gsi, loc,
+ return gimple_fold_builtin_strncpy (gsi,
gimple_call_arg (stmt, 0),
gimple_call_arg (stmt, 1),
- gimple_call_arg (stmt, 2),
- val[1]);
-
+ gimple_call_arg (stmt, 2));
case BUILT_IN_STRCAT:
- return gimple_fold_builtin_strcat (gsi, loc, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- val[1]);
-
+ return gimple_fold_builtin_strcat (gsi, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1));
case BUILT_IN_FPUTS:
- return gimple_fold_builtin_fputs (gsi, loc, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- ignore, false, val[0]);
-
+ return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1), false);
case BUILT_IN_FPUTS_UNLOCKED:
- return gimple_fold_builtin_fputs (gsi, loc, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- ignore, true, val[0]);
-
+ return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1), true);
case BUILT_IN_MEMCPY_CHK:
case BUILT_IN_MEMPCPY_CHK:
case BUILT_IN_MEMMOVE_CHK:
case BUILT_IN_MEMSET_CHK:
- return gimple_fold_builtin_memory_chk (gsi, loc,
+ return gimple_fold_builtin_memory_chk (gsi,
gimple_call_arg (stmt, 0),
gimple_call_arg (stmt, 1),
gimple_call_arg (stmt, 2),
gimple_call_arg (stmt, 3),
- val[2], ignore,
DECL_FUNCTION_CODE (callee));
-
case BUILT_IN_STRCPY_CHK:
case BUILT_IN_STPCPY_CHK:
- return gimple_fold_builtin_stxcpy_chk (gsi, loc,
+ return gimple_fold_builtin_stxcpy_chk (gsi,
gimple_call_arg (stmt, 0),
gimple_call_arg (stmt, 1),
gimple_call_arg (stmt, 2),
- val[1], ignore,
DECL_FUNCTION_CODE (callee));
-
case BUILT_IN_STRNCPY_CHK:
case BUILT_IN_STPNCPY_CHK:
return gimple_fold_builtin_stxncpy_chk (gsi,
gimple_call_arg (stmt, 1),
gimple_call_arg (stmt, 2),
gimple_call_arg (stmt, 3),
- val[2], ignore,
DECL_FUNCTION_CODE (callee));
-
case BUILT_IN_SNPRINTF_CHK:
case BUILT_IN_VSNPRINTF_CHK:
- return gimple_fold_builtin_snprintf_chk (gsi, val[1],
+ return gimple_fold_builtin_snprintf_chk (gsi,
DECL_FUNCTION_CODE (callee));
case BUILT_IN_SNPRINTF:
- return gimple_fold_builtin_snprintf (gsi, val[3]);
+ return gimple_fold_builtin_snprintf (gsi);
case BUILT_IN_SPRINTF:
- return gimple_fold_builtin_sprintf (gsi, val[2]);
- default:
- gcc_unreachable ();
- }
-
- return false;
-}
-
-
-/* Fold the non-target builtin at *GSI and return whether any simplification
- was made. */
-
-static bool
-gimple_fold_builtin (gimple_stmt_iterator *gsi)
-{
- gimple stmt = gsi_stmt (*gsi);
- tree callee = gimple_call_fndecl (stmt);
-
- /* Give up for always_inline inline builtins until they are
- inlined. */
- if (avoid_folding_inline_builtin (callee))
- return false;
-
- if (gimple_fold_builtin_with_strlen (gsi))
- return true;
-
- switch (DECL_FUNCTION_CODE (callee))
- {
- case BUILT_IN_BZERO:
- return gimple_fold_builtin_memset (gsi, integer_zero_node,
- gimple_call_arg (stmt, 1));
- case BUILT_IN_MEMSET:
- return gimple_fold_builtin_memset (gsi,
- gimple_call_arg (stmt, 1),
- gimple_call_arg (stmt, 2));
- case BUILT_IN_BCOPY:
- return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 1),
- gimple_call_arg (stmt, 0), 3);
- case BUILT_IN_MEMCPY:
- return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1), 0);
- case BUILT_IN_MEMPCPY:
- return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1), 1);
- case BUILT_IN_MEMMOVE:
- return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1), 3);
- case BUILT_IN_SPRINTF_CHK:
- case BUILT_IN_VSPRINTF_CHK:
- return gimple_fold_builtin_sprintf_chk (gsi, DECL_FUNCTION_CODE (callee));
- case BUILT_IN_STRCAT_CHK:
- return gimple_fold_builtin_strcat_chk (gsi);
+ return gimple_fold_builtin_sprintf (gsi);
default:;
}