aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Biener <rguenther@suse.de>2014-08-08 09:18:40 +0000
committerRichard Biener <rguenth@gcc.gnu.org>2014-08-08 09:18:40 +0000
commitfef5a0d95e0e593ace4be86a8aa27ebdaa92c078 (patch)
tree621bf63e4a68ad2ac887a0c87d72c5746a320a34
parent322d490e02ff3008949d14370e651c89290465a6 (diff)
downloadgcc-fef5a0d95e0e593ace4be86a8aa27ebdaa92c078.zip
gcc-fef5a0d95e0e593ace4be86a8aa27ebdaa92c078.tar.gz
gcc-fef5a0d95e0e593ace4be86a8aa27ebdaa92c078.tar.bz2
builtins.c (c_getstr, [...]): Export.
2014-08-08 Richard Biener <rguenther@suse.de> * builtins.c (c_getstr, readonly_data_expr, init_target_chars, target_percent, target_percent_s): Export. (var_decl_component_p, fold_builtin_memory_op, fold_builtin_memset, fold_builtin_bzero, fold_builtin_strcpy, fold_builtin_strncpy, fold_builtin_strcat, fold_builtin_fputs, fold_builtin_memory_chk, fold_builtin_stxcpy_chk, fold_builtin_stxncpy_chk, fold_builtin_sprintf_chk_1, fold_builtin_snprintf_chk_1): Move to gimple-fold.c. (fold_builtin_2): Remove handling of bzero, fputs, fputs_unlocked, strcat and strcpy. (fold_builtin_3): Remove handling of memset, bcopy, memcpy, mempcpy, memmove, strncpy, strcpy_chk and stpcpy_chk. (fold_builtin_4): Remove handling of memcpy_chk, mempcpy_chk, memmove_chk, memset_chk, strncpy_chk and stpncpy_chk. (rewrite_call_expr_array): Remove. (fold_builtin_sprintf_chk): Likewise. (fold_builtin_snprintf_chk): Likewise. (fold_builtin_varargs): Remove handling of sprintf_chk, vsprintf_chk, snprintf_chk and vsnprintf_chk. (gimple_fold_builtin_sprintf_chk): Remove. (gimple_fold_builtin_snprintf_chk): Likewise. (gimple_fold_builtin_varargs): Likewise. (fold_call_stmt): Do not call gimple_fold_builtin_varargs. * predict.c (optimize_bb_for_size_p): Handle NULL bb. * gimple.c (gimple_seq_add_seq_without_update): New function. * gimple.h (gimple_seq_add_seq_without_update): Declare. * gimple-fold.c: Include output.h. (gsi_replace_with_seq_vops): New function, split out from ... (gimplify_and_update_call_from_tree): ... here. (replace_call_with_value): New function. (replace_call_with_call_and_fold): Likewise. (var_decl_component_p): Moved from builtins.c. (gimple_fold_builtin_memory_op): Moved from builtins.c fold_builtin_memory_op and rewritten to GIMPLE. (gimple_fold_builtin_memset): Likewise. (gimple_fold_builtin_strcpy): Likewise. (gimple_fold_builtin_strncpy): Likewise. (gimple_fold_builtin_strcat): Likewise. (gimple_fold_builtin_fputs): Likewise. (gimple_fold_builtin_memory_chk): Likewise. (gimple_fold_builtin_stxcpy_chk): Likewise. (gimple_fold_builtin_stxncpy_chk): Likewise. (gimple_fold_builtin_snprintf_chk): Likewise. (gimple_fold_builtin_sprintf_chk): Likewise. (gimple_fold_builtin_strlen): New function. (gimple_fold_builtin_with_strlen): New function split out from gimple_fold_builtin. (gimple_fold_builtin): Change signature and handle bzero, memset, bcopy, memcpy, mempcpy and memmove folding here. Call gimple_fold_builtin_with_strlen. (gimple_fold_call): Adjust. * gcc.dg/strlenopt-8.c: Remove XFAIL. * gcc.dg/tree-prof/stringop-2.c: Adjust. * gfortran.dg/array_memcpy_4.f90: Likewise. * gfortran.dg/trim_optimize_1.f90: Likewise. * gfortran.dg/trim_optimize_2.f90: Likewise. From-SVN: r213753
-rw-r--r--gcc/ChangeLog54
-rw-r--r--gcc/builtins.c1295
-rw-r--r--gcc/builtins.h18
-rw-r--r--gcc/gimple-fold.c1627
-rw-r--r--gcc/gimple.c15
-rw-r--r--gcc/gimple.h1
-rw-r--r--gcc/predict.c3
-rw-r--r--gcc/testsuite/ChangeLog8
-rw-r--r--gcc/testsuite/gcc.dg/strlenopt-8.c4
-rw-r--r--gcc/testsuite/gcc.dg/tree-prof/stringop-2.c2
-rw-r--r--gcc/testsuite/gfortran.dg/array_memcpy_4.f902
-rw-r--r--gcc/testsuite/gfortran.dg/trim_optimize_1.f902
-rw-r--r--gcc/testsuite/gfortran.dg/trim_optimize_2.f902
13 files changed, 1556 insertions, 1477 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 7980f98..3c5cc47 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,57 @@
+2014-08-08 Richard Biener <rguenther@suse.de>
+
+ * builtins.c (c_getstr, readonly_data_expr, init_target_chars,
+ target_percent, target_percent_s): Export.
+ (var_decl_component_p, fold_builtin_memory_op, fold_builtin_memset,
+ fold_builtin_bzero, fold_builtin_strcpy, fold_builtin_strncpy,
+ fold_builtin_strcat, fold_builtin_fputs, fold_builtin_memory_chk,
+ fold_builtin_stxcpy_chk, fold_builtin_stxncpy_chk,
+ fold_builtin_sprintf_chk_1, fold_builtin_snprintf_chk_1):
+ Move to gimple-fold.c.
+ (fold_builtin_2): Remove handling of bzero, fputs, fputs_unlocked,
+ strcat and strcpy.
+ (fold_builtin_3): Remove handling of memset, bcopy, memcpy,
+ mempcpy, memmove, strncpy, strcpy_chk and stpcpy_chk.
+ (fold_builtin_4): Remove handling of memcpy_chk, mempcpy_chk,
+ memmove_chk, memset_chk, strncpy_chk and stpncpy_chk.
+ (rewrite_call_expr_array): Remove.
+ (fold_builtin_sprintf_chk): Likewise.
+ (fold_builtin_snprintf_chk): Likewise.
+ (fold_builtin_varargs): Remove handling of sprintf_chk,
+ vsprintf_chk, snprintf_chk and vsnprintf_chk.
+ (gimple_fold_builtin_sprintf_chk): Remove.
+ (gimple_fold_builtin_snprintf_chk): Likewise.
+ (gimple_fold_builtin_varargs): Likewise.
+ (fold_call_stmt): Do not call gimple_fold_builtin_varargs.
+ * predict.c (optimize_bb_for_size_p): Handle NULL bb.
+ * gimple.c (gimple_seq_add_seq_without_update): New function.
+ * gimple.h (gimple_seq_add_seq_without_update): Declare.
+ * gimple-fold.c: Include output.h.
+ (gsi_replace_with_seq_vops): New function, split out from ...
+ (gimplify_and_update_call_from_tree): ... here.
+ (replace_call_with_value): New function.
+ (replace_call_with_call_and_fold): Likewise.
+ (var_decl_component_p): Moved from builtins.c.
+ (gimple_fold_builtin_memory_op): Moved from builtins.c
+ fold_builtin_memory_op and rewritten to GIMPLE.
+ (gimple_fold_builtin_memset): Likewise.
+ (gimple_fold_builtin_strcpy): Likewise.
+ (gimple_fold_builtin_strncpy): Likewise.
+ (gimple_fold_builtin_strcat): Likewise.
+ (gimple_fold_builtin_fputs): Likewise.
+ (gimple_fold_builtin_memory_chk): Likewise.
+ (gimple_fold_builtin_stxcpy_chk): Likewise.
+ (gimple_fold_builtin_stxncpy_chk): Likewise.
+ (gimple_fold_builtin_snprintf_chk): Likewise.
+ (gimple_fold_builtin_sprintf_chk): Likewise.
+ (gimple_fold_builtin_strlen): New function.
+ (gimple_fold_builtin_with_strlen): New function split out from
+ gimple_fold_builtin.
+ (gimple_fold_builtin): Change signature and handle
+ bzero, memset, bcopy, memcpy, mempcpy and memmove folding
+ here. Call gimple_fold_builtin_with_strlen.
+ (gimple_fold_call): Adjust.
+
2014-08-08 Kugan Vivekanandarajah <kuganv@linaro.org>
* calls.c (precompute_arguments): Check
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 03d1dca..c1bd263 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -86,7 +86,6 @@ builtin_info_type builtin_info;
/* Non-zero if __builtin_constant_p should be folded right away. */
bool force_folding_builtin_constant_p;
-static const char *c_getstr (tree);
static rtx c_readstr (const char *, enum machine_mode);
static int target_char_cast (tree, char *);
static rtx get_memory_rtx (tree, tree);
@@ -148,7 +147,6 @@ static tree rewrite_call_expr (location_t, tree, int, tree, int, ...);
static bool validate_arg (const_tree, enum tree_code code);
static bool integer_valued_real_p (tree);
static tree fold_trunc_transparent_mathfn (location_t, tree, tree);
-static bool readonly_data_expr (tree);
static rtx expand_builtin_fabs (tree, rtx, rtx);
static rtx expand_builtin_signbit (tree, rtx);
static tree fold_builtin_sqrt (location_t, tree, tree);
@@ -164,7 +162,6 @@ static tree fold_builtin_ceil (location_t, tree, tree);
static tree fold_builtin_round (location_t, tree, tree);
static tree fold_builtin_int_roundingfn (location_t, tree, tree);
static tree fold_builtin_bitop (tree, tree);
-static tree fold_builtin_memory_op (location_t, tree, tree, tree, tree, bool, int);
static tree fold_builtin_strchr (location_t, tree, tree, tree);
static tree fold_builtin_memchr (location_t, tree, tree, tree, tree);
static tree fold_builtin_memcmp (location_t, tree, tree, tree);
@@ -205,18 +202,16 @@ static void maybe_emit_free_warning (tree);
static tree fold_builtin_object_size (tree, tree);
static tree fold_builtin_strcat_chk (location_t, tree, tree, tree, tree);
static tree fold_builtin_strncat_chk (location_t, tree, tree, tree, tree, tree);
-static tree fold_builtin_sprintf_chk (location_t, tree, enum built_in_function);
static tree fold_builtin_printf (location_t, tree, tree, tree, bool, enum built_in_function);
static tree fold_builtin_fprintf (location_t, tree, tree, tree, tree, bool,
enum built_in_function);
-static bool init_target_chars (void);
static unsigned HOST_WIDE_INT target_newline;
-static unsigned HOST_WIDE_INT target_percent;
+unsigned HOST_WIDE_INT target_percent;
static unsigned HOST_WIDE_INT target_c;
static unsigned HOST_WIDE_INT target_s;
static char target_percent_c[3];
-static char target_percent_s[3];
+char target_percent_s[3];
static char target_percent_s_newline[4];
static tree do_mpfr_arg1 (tree, tree, int (*)(mpfr_ptr, mpfr_srcptr, mp_rnd_t),
const REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *, bool);
@@ -634,7 +629,7 @@ c_strlen (tree src, int only_value)
/* Return a char pointer for a C string if it is a string constant
or sum of string constant and integer constant. */
-static const char *
+const char *
c_getstr (tree src)
{
tree offset_node;
@@ -8496,503 +8491,6 @@ fold_builtin_exponent (location_t loc, tree fndecl, tree arg,
return NULL_TREE;
}
-/* Return true if VAR is a VAR_DECL or a component thereof. */
-
-static bool
-var_decl_component_p (tree var)
-{
- tree inner = var;
- while (handled_component_p (inner))
- inner = TREE_OPERAND (inner, 0);
- return SSA_VAR_P (inner);
-}
-
-/* Fold function call to builtin memset. Return
- NULL_TREE if no simplification can be made. */
-
-static tree
-fold_builtin_memset (location_t loc, tree dest, tree c, tree len,
- tree type, bool ignore)
-{
- tree var, ret, etype;
- unsigned HOST_WIDE_INT length, cval;
-
- if (! validate_arg (dest, POINTER_TYPE)
- || ! validate_arg (c, INTEGER_TYPE)
- || ! validate_arg (len, INTEGER_TYPE))
- return NULL_TREE;
-
- if (! tree_fits_uhwi_p (len))
- return NULL_TREE;
-
- /* If the LEN parameter is zero, return DEST. */
- if (integer_zerop (len))
- return omit_one_operand_loc (loc, type, dest, c);
-
- if (TREE_CODE (c) != INTEGER_CST || TREE_SIDE_EFFECTS (dest))
- return NULL_TREE;
-
- var = dest;
- STRIP_NOPS (var);
- if (TREE_CODE (var) != ADDR_EXPR)
- return NULL_TREE;
-
- var = TREE_OPERAND (var, 0);
- if (TREE_THIS_VOLATILE (var))
- return NULL_TREE;
-
- etype = TREE_TYPE (var);
- if (TREE_CODE (etype) == ARRAY_TYPE)
- etype = TREE_TYPE (etype);
-
- if (!INTEGRAL_TYPE_P (etype)
- && !POINTER_TYPE_P (etype))
- return NULL_TREE;
-
- if (! var_decl_component_p (var))
- return NULL_TREE;
-
- length = tree_to_uhwi (len);
- if (GET_MODE_SIZE (TYPE_MODE (etype)) != length
- || get_pointer_alignment (dest) / BITS_PER_UNIT < length)
- return NULL_TREE;
-
- if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
- return NULL_TREE;
-
- if (integer_zerop (c))
- cval = 0;
- else
- {
- if (CHAR_BIT != 8 || BITS_PER_UNIT != 8 || HOST_BITS_PER_WIDE_INT > 64)
- return NULL_TREE;
-
- cval = TREE_INT_CST_LOW (c);
- cval &= 0xff;
- cval |= cval << 8;
- cval |= cval << 16;
- cval |= (cval << 31) << 1;
- }
-
- ret = build_int_cst_type (etype, cval);
- var = build_fold_indirect_ref_loc (loc,
- fold_convert_loc (loc,
- build_pointer_type (etype),
- dest));
- ret = build2 (MODIFY_EXPR, etype, var, ret);
- if (ignore)
- return ret;
-
- return omit_one_operand_loc (loc, type, dest, ret);
-}
-
-/* Fold function call to builtin memset. Return
- NULL_TREE if no simplification can be made. */
-
-static tree
-fold_builtin_bzero (location_t loc, tree dest, tree size, bool ignore)
-{
- if (! validate_arg (dest, POINTER_TYPE)
- || ! validate_arg (size, INTEGER_TYPE))
- return NULL_TREE;
-
- if (!ignore)
- return NULL_TREE;
-
- /* New argument list transforming bzero(ptr x, int y) to
- memset(ptr x, int 0, size_t y). This is done this way
- so that if it isn't expanded inline, we fallback to
- calling bzero instead of memset. */
-
- return fold_builtin_memset (loc, dest, integer_zero_node,
- fold_convert_loc (loc, size_type_node, size),
- void_type_node, ignore);
-}
-
-/* Fold function call to builtin mem{{,p}cpy,move}. Return
- NULL_TREE if no simplification can be made.
- If ENDP is 0, return DEST (like memcpy).
- If ENDP is 1, return DEST+LEN (like mempcpy).
- If ENDP is 2, return DEST+LEN-1 (like stpcpy).
- If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
- (memmove). */
-
-static tree
-fold_builtin_memory_op (location_t loc, tree dest, tree src,
- tree len, tree type, bool ignore, int endp)
-{
- tree destvar, srcvar, expr;
-
- if (! validate_arg (dest, POINTER_TYPE)
- || ! validate_arg (src, POINTER_TYPE)
- || ! validate_arg (len, INTEGER_TYPE))
- return NULL_TREE;
-
- /* If the LEN parameter is zero, return DEST. */
- if (integer_zerop (len))
- return omit_one_operand_loc (loc, type, dest, src);
-
- /* If SRC and DEST are the same (and not volatile), return
- DEST{,+LEN,+LEN-1}. */
- if (operand_equal_p (src, dest, 0))
- expr = len;
- else
- {
- tree srctype, desttype;
- unsigned int src_align, dest_align;
- tree off0;
-
- /* Build accesses at offset zero with a ref-all character type. */
- off0 = build_int_cst (build_pointer_type_for_mode (char_type_node,
- ptr_mode, true), 0);
-
- /* If we can perform the copy efficiently with first doing all loads
- and then all stores inline it that way. Currently efficiently
- means that we can load all the memory into a single integer
- register which is what MOVE_MAX gives us. */
- src_align = get_pointer_alignment (src);
- dest_align = get_pointer_alignment (dest);
- if (tree_fits_uhwi_p (len)
- && compare_tree_int (len, MOVE_MAX) <= 0
- /* ??? Don't transform copies from strings with known length this
- confuses the tree-ssa-strlen.c. This doesn't handle
- the case in gcc.dg/strlenopt-8.c which is XFAILed for that
- reason. */
- && !c_strlen (src, 2))
- {
- unsigned ilen = tree_to_uhwi (len);
- if (exact_log2 (ilen) != -1)
- {
- tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
- if (type
- && TYPE_MODE (type) != BLKmode
- && (GET_MODE_SIZE (TYPE_MODE (type)) * BITS_PER_UNIT
- == ilen * 8)
- /* If the pointers are not aligned we must be able to
- emit an unaligned load. */
- && ((src_align >= GET_MODE_ALIGNMENT (TYPE_MODE (type))
- && dest_align >= GET_MODE_ALIGNMENT (TYPE_MODE (type)))
- || !SLOW_UNALIGNED_ACCESS (TYPE_MODE (type),
- MIN (src_align, dest_align))))
- {
- tree srctype = type;
- tree desttype = type;
- if (src_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
- srctype = build_aligned_type (type, src_align);
- if (dest_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
- desttype = build_aligned_type (type, dest_align);
- if (!ignore)
- dest = builtin_save_expr (dest);
- expr = build2 (MODIFY_EXPR, type,
- fold_build2 (MEM_REF, desttype, dest, off0),
- fold_build2 (MEM_REF, srctype, src, off0));
- goto done;
- }
- }
- }
-
- if (endp == 3)
- {
- /* Both DEST and SRC must be pointer types.
- ??? This is what old code did. Is the testing for pointer types
- really mandatory?
-
- If either SRC is readonly or length is 1, we can use memcpy. */
- if (!dest_align || !src_align)
- return NULL_TREE;
- if (readonly_data_expr (src)
- || (tree_fits_uhwi_p (len)
- && (MIN (src_align, dest_align) / BITS_PER_UNIT
- >= tree_to_uhwi (len))))
- {
- tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
- if (!fn)
- return NULL_TREE;
- return build_call_expr_loc (loc, fn, 3, dest, src, len);
- }
-
- /* If *src and *dest can't overlap, optimize into memcpy as well. */
- if (TREE_CODE (src) == ADDR_EXPR
- && TREE_CODE (dest) == ADDR_EXPR)
- {
- tree src_base, dest_base, fn;
- HOST_WIDE_INT src_offset = 0, dest_offset = 0;
- HOST_WIDE_INT size = -1;
- HOST_WIDE_INT maxsize = -1;
-
- srcvar = TREE_OPERAND (src, 0);
- src_base = get_ref_base_and_extent (srcvar, &src_offset,
- &size, &maxsize);
- destvar = TREE_OPERAND (dest, 0);
- dest_base = get_ref_base_and_extent (destvar, &dest_offset,
- &size, &maxsize);
- if (tree_fits_uhwi_p (len))
- maxsize = tree_to_uhwi (len);
- else
- maxsize = -1;
- src_offset /= BITS_PER_UNIT;
- dest_offset /= BITS_PER_UNIT;
- if (SSA_VAR_P (src_base)
- && SSA_VAR_P (dest_base))
- {
- if (operand_equal_p (src_base, dest_base, 0)
- && ranges_overlap_p (src_offset, maxsize,
- dest_offset, maxsize))
- return NULL_TREE;
- }
- else if (TREE_CODE (src_base) == MEM_REF
- && TREE_CODE (dest_base) == MEM_REF)
- {
- if (! operand_equal_p (TREE_OPERAND (src_base, 0),
- TREE_OPERAND (dest_base, 0), 0))
- return NULL_TREE;
- offset_int off = mem_ref_offset (src_base) + src_offset;
- if (!wi::fits_shwi_p (off))
- return NULL_TREE;
- src_offset = off.to_shwi ();
-
- off = mem_ref_offset (dest_base) + dest_offset;
- if (!wi::fits_shwi_p (off))
- return NULL_TREE;
- dest_offset = off.to_shwi ();
- if (ranges_overlap_p (src_offset, maxsize,
- dest_offset, maxsize))
- return NULL_TREE;
- }
- else
- return NULL_TREE;
-
- fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
- if (!fn)
- return NULL_TREE;
- return build_call_expr_loc (loc, fn, 3, dest, src, len);
- }
-
- /* If the destination and source do not alias optimize into
- memcpy as well. */
- if ((is_gimple_min_invariant (dest)
- || TREE_CODE (dest) == SSA_NAME)
- && (is_gimple_min_invariant (src)
- || TREE_CODE (src) == SSA_NAME))
- {
- ao_ref destr, srcr;
- ao_ref_init_from_ptr_and_size (&destr, dest, len);
- ao_ref_init_from_ptr_and_size (&srcr, src, len);
- if (!refs_may_alias_p_1 (&destr, &srcr, false))
- {
- tree fn;
- fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
- if (!fn)
- return NULL_TREE;
- return build_call_expr_loc (loc, fn, 3, dest, src, len);
- }
- }
-
- return NULL_TREE;
- }
-
- if (!tree_fits_shwi_p (len))
- return NULL_TREE;
- /* FIXME:
- This logic lose for arguments like (type *)malloc (sizeof (type)),
- since we strip the casts of up to VOID return value from malloc.
- Perhaps we ought to inherit type from non-VOID argument here? */
- STRIP_NOPS (src);
- STRIP_NOPS (dest);
- if (!POINTER_TYPE_P (TREE_TYPE (src))
- || !POINTER_TYPE_P (TREE_TYPE (dest)))
- return NULL_TREE;
- /* In the following try to find a type that is most natural to be
- used for the memcpy source and destination and that allows
- the most optimization when memcpy is turned into a plain assignment
- using that type. In theory we could always use a char[len] type
- but that only gains us that the destination and source possibly
- no longer will have their address taken. */
- /* As we fold (void *)(p + CST) to (void *)p + CST undo this here. */
- if (TREE_CODE (src) == POINTER_PLUS_EXPR)
- {
- tree tem = TREE_OPERAND (src, 0);
- STRIP_NOPS (tem);
- if (tem != TREE_OPERAND (src, 0))
- src = build1 (NOP_EXPR, TREE_TYPE (tem), src);
- }
- if (TREE_CODE (dest) == POINTER_PLUS_EXPR)
- {
- tree tem = TREE_OPERAND (dest, 0);
- STRIP_NOPS (tem);
- if (tem != TREE_OPERAND (dest, 0))
- dest = build1 (NOP_EXPR, TREE_TYPE (tem), dest);
- }
- srctype = TREE_TYPE (TREE_TYPE (src));
- if (TREE_CODE (srctype) == ARRAY_TYPE
- && !tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
- {
- srctype = TREE_TYPE (srctype);
- STRIP_NOPS (src);
- src = build1 (NOP_EXPR, build_pointer_type (srctype), src);
- }
- desttype = TREE_TYPE (TREE_TYPE (dest));
- if (TREE_CODE (desttype) == ARRAY_TYPE
- && !tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
- {
- desttype = TREE_TYPE (desttype);
- STRIP_NOPS (dest);
- dest = build1 (NOP_EXPR, build_pointer_type (desttype), dest);
- }
- if (TREE_ADDRESSABLE (srctype)
- || TREE_ADDRESSABLE (desttype))
- return NULL_TREE;
-
- /* Make sure we are not copying using a floating-point mode or
- a type whose size possibly does not match its precision. */
- if (FLOAT_MODE_P (TYPE_MODE (desttype))
- || TREE_CODE (desttype) == BOOLEAN_TYPE
- || TREE_CODE (desttype) == ENUMERAL_TYPE)
- desttype = bitwise_type_for_mode (TYPE_MODE (desttype));
- if (FLOAT_MODE_P (TYPE_MODE (srctype))
- || TREE_CODE (srctype) == BOOLEAN_TYPE
- || TREE_CODE (srctype) == ENUMERAL_TYPE)
- srctype = bitwise_type_for_mode (TYPE_MODE (srctype));
- if (!srctype)
- srctype = desttype;
- if (!desttype)
- desttype = srctype;
- if (!srctype)
- return NULL_TREE;
-
- src_align = get_pointer_alignment (src);
- dest_align = get_pointer_alignment (dest);
- if (dest_align < TYPE_ALIGN (desttype)
- || src_align < TYPE_ALIGN (srctype))
- return NULL_TREE;
-
- if (!ignore)
- dest = builtin_save_expr (dest);
-
- destvar = dest;
- STRIP_NOPS (destvar);
- if (TREE_CODE (destvar) == ADDR_EXPR
- && var_decl_component_p (TREE_OPERAND (destvar, 0))
- && tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
- destvar = fold_build2 (MEM_REF, desttype, destvar, off0);
- else
- destvar = NULL_TREE;
-
- srcvar = src;
- STRIP_NOPS (srcvar);
- if (TREE_CODE (srcvar) == ADDR_EXPR
- && var_decl_component_p (TREE_OPERAND (srcvar, 0))
- && tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
- {
- if (!destvar
- || src_align >= TYPE_ALIGN (desttype))
- srcvar = fold_build2 (MEM_REF, destvar ? desttype : srctype,
- srcvar, off0);
- else if (!STRICT_ALIGNMENT)
- {
- srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype),
- src_align);
- srcvar = fold_build2 (MEM_REF, srctype, srcvar, off0);
- }
- else
- srcvar = NULL_TREE;
- }
- else
- srcvar = NULL_TREE;
-
- if (srcvar == NULL_TREE && destvar == NULL_TREE)
- return NULL_TREE;
-
- if (srcvar == NULL_TREE)
- {
- STRIP_NOPS (src);
- if (src_align >= TYPE_ALIGN (desttype))
- srcvar = fold_build2 (MEM_REF, desttype, src, off0);
- else
- {
- if (STRICT_ALIGNMENT)
- return NULL_TREE;
- srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype),
- src_align);
- srcvar = fold_build2 (MEM_REF, srctype, src, off0);
- }
- }
- else if (destvar == NULL_TREE)
- {
- STRIP_NOPS (dest);
- if (dest_align >= TYPE_ALIGN (srctype))
- destvar = fold_build2 (MEM_REF, srctype, dest, off0);
- else
- {
- if (STRICT_ALIGNMENT)
- return NULL_TREE;
- desttype = build_aligned_type (TYPE_MAIN_VARIANT (srctype),
- dest_align);
- destvar = fold_build2 (MEM_REF, desttype, dest, off0);
- }
- }
-
- expr = build2 (MODIFY_EXPR, TREE_TYPE (destvar), destvar, srcvar);
- }
-
-done:
- if (ignore)
- return expr;
-
- if (endp == 0 || endp == 3)
- return omit_one_operand_loc (loc, type, dest, expr);
-
- if (expr == len)
- expr = NULL_TREE;
-
- if (endp == 2)
- len = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (len), len,
- ssize_int (1));
-
- dest = fold_build_pointer_plus_loc (loc, dest, len);
- dest = fold_convert_loc (loc, type, dest);
- if (expr)
- dest = omit_one_operand_loc (loc, type, dest, expr);
- return dest;
-}
-
-/* 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
- copied. Return NULL_TREE if no simplification can be made. */
-
-tree
-fold_builtin_strcpy (location_t loc, tree fndecl, tree dest, tree src, tree len)
-{
- tree fn;
-
- if (!validate_arg (dest, POINTER_TYPE)
- || !validate_arg (src, POINTER_TYPE))
- return NULL_TREE;
-
- /* If SRC and DEST are the same (and not volatile), return DEST. */
- if (operand_equal_p (src, dest, 0))
- return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), dest);
-
- if (optimize_function_for_size_p (cfun))
- return NULL_TREE;
-
- fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
- if (!fn)
- return NULL_TREE;
-
- if (!len)
- {
- len = c_strlen (src, 1);
- if (! len || TREE_SIDE_EFFECTS (len))
- return NULL_TREE;
- }
-
- 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 fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)),
- build_call_expr_loc (loc, fn, 3, dest, src, len));
-}
-
/* Fold function call to builtin stpcpy with arguments DEST and SRC.
Return NULL_TREE if no simplification can be made. */
@@ -9034,55 +8532,6 @@ fold_builtin_stpcpy (location_t loc, tree fndecl, tree dest, tree src)
return dest;
}
-/* Fold function call to builtin strncpy with arguments DEST, SRC, and LEN.
- If SLEN is not NULL, it represents the length of the source string.
- Return NULL_TREE if no simplification can be made. */
-
-tree
-fold_builtin_strncpy (location_t loc, tree fndecl, tree dest,
- tree src, tree len, tree slen)
-{
- tree fn;
-
- if (!validate_arg (dest, POINTER_TYPE)
- || !validate_arg (src, POINTER_TYPE)
- || !validate_arg (len, INTEGER_TYPE))
- return NULL_TREE;
-
- /* If the LEN parameter is zero, return DEST. */
- if (integer_zerop (len))
- return omit_one_operand_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), dest, src);
-
- /* We can't compare slen with len as constants below if len is not a
- constant. */
- if (len == 0 || TREE_CODE (len) != INTEGER_CST)
- return NULL_TREE;
-
- 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)
- return NULL_TREE;
-
- slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
-
- /* We do not support simplification of this case, though we do
- support it when expanding trees into RTL. */
- /* FIXME: generate a call to __builtin_memset. */
- if (tree_int_cst_lt (slen, len))
- return NULL_TREE;
-
- /* OK transform into builtin memcpy. */
- fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
- if (!fn)
- return NULL_TREE;
-
- len = fold_convert_loc (loc, size_type_node, len);
- return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)),
- build_call_expr_loc (loc, fn, 3, dest, src, len));
-}
-
/* Fold function call to builtin memchr. ARG1, ARG2 and LEN are the
arguments to the call, and TYPE is its return type.
Return NULL_TREE if no simplification can be made. */
@@ -10707,21 +10156,9 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1, bool ignore)
CASE_FLT_FN (BUILT_IN_MODF):
return fold_builtin_modf (loc, arg0, arg1, type);
- case BUILT_IN_BZERO:
- return fold_builtin_bzero (loc, arg0, arg1, ignore);
-
- case BUILT_IN_FPUTS:
- return fold_builtin_fputs (loc, arg0, arg1, ignore, false, NULL_TREE);
-
- case BUILT_IN_FPUTS_UNLOCKED:
- return fold_builtin_fputs (loc, arg0, arg1, ignore, true, NULL_TREE);
-
case BUILT_IN_STRSTR:
return fold_builtin_strstr (loc, arg0, arg1, type);
- case BUILT_IN_STRCAT:
- return fold_builtin_strcat (loc, arg0, arg1, NULL_TREE);
-
case BUILT_IN_STRSPN:
return fold_builtin_strspn (loc, arg0, arg1);
@@ -10736,9 +10173,6 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1, bool ignore)
case BUILT_IN_RINDEX:
return fold_builtin_strrchr (loc, arg0, arg1, type);
- case BUILT_IN_STRCPY:
- return fold_builtin_strcpy (loc, fndecl, arg0, arg1, NULL_TREE);
-
case BUILT_IN_STPCPY:
if (ignore)
{
@@ -10866,31 +10300,9 @@ fold_builtin_3 (location_t loc, tree fndecl,
return do_mpfr_remquo (arg0, arg1, arg2);
break;
- case BUILT_IN_MEMSET:
- return fold_builtin_memset (loc, arg0, arg1, arg2, type, ignore);
-
- case BUILT_IN_BCOPY:
- return fold_builtin_memory_op (loc, arg1, arg0, arg2,
- void_type_node, true, /*endp=*/3);
-
- case BUILT_IN_MEMCPY:
- return fold_builtin_memory_op (loc, arg0, arg1, arg2,
- type, ignore, /*endp=*/0);
-
- case BUILT_IN_MEMPCPY:
- return fold_builtin_memory_op (loc, arg0, arg1, arg2,
- type, ignore, /*endp=*/1);
-
- case BUILT_IN_MEMMOVE:
- return fold_builtin_memory_op (loc, arg0, arg1, arg2,
- type, ignore, /*endp=*/3);
-
case BUILT_IN_STRNCAT:
return fold_builtin_strncat (loc, arg0, arg1, arg2);
- case BUILT_IN_STRNCPY:
- return fold_builtin_strncpy (loc, fndecl, arg0, arg1, arg2, NULL_TREE);
-
case BUILT_IN_STRNCMP:
return fold_builtin_strncmp (loc, arg0, arg1, arg2);
@@ -10907,11 +10319,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
case BUILT_IN_SNPRINTF:
return fold_builtin_snprintf (loc, arg0, arg1, arg2, NULL_TREE, ignore);
- case BUILT_IN_STRCPY_CHK:
- case BUILT_IN_STPCPY_CHK:
- return fold_builtin_stxcpy_chk (loc, fndecl, arg0, arg1, arg2, NULL_TREE,
- ignore, fcode);
-
case BUILT_IN_STRCAT_CHK:
return fold_builtin_strcat_chk (loc, fndecl, arg0, arg1, arg2);
@@ -10961,19 +10368,6 @@ fold_builtin_4 (location_t loc, tree fndecl,
switch (fcode)
{
- case BUILT_IN_MEMCPY_CHK:
- case BUILT_IN_MEMPCPY_CHK:
- case BUILT_IN_MEMMOVE_CHK:
- case BUILT_IN_MEMSET_CHK:
- return fold_builtin_memory_chk (loc, fndecl, arg0, arg1, arg2, arg3,
- NULL_TREE, ignore,
- DECL_FUNCTION_CODE (fndecl));
-
- case BUILT_IN_STRNCPY_CHK:
- case BUILT_IN_STPNCPY_CHK:
- return fold_builtin_stxncpy_chk (loc, arg0, arg1, arg2, arg3, NULL_TREE,
- ignore, fcode);
-
case BUILT_IN_STRNCAT_CHK:
return fold_builtin_strncat_chk (loc, fndecl, arg0, arg1, arg2, arg3);
@@ -11070,25 +10464,6 @@ rewrite_call_expr_valist (location_t loc, int oldnargs, tree *args,
return build_call_expr_loc_array (loc, fndecl, nargs, buffer);
}
-/* Construct a new CALL_EXPR to FNDECL using the tail of the argument
- list ARGS along with N new arguments specified as the "..."
- parameters. SKIP is the number of arguments in ARGS to be omitted.
- OLDNARGS is the number of elements in ARGS. */
-
-static tree
-rewrite_call_expr_array (location_t loc, int oldnargs, tree *args,
- int skip, tree fndecl, int n, ...)
-{
- va_list ap;
- tree t;
-
- va_start (ap, n);
- t = rewrite_call_expr_valist (loc, oldnargs, args, skip, fndecl, n, ap);
- va_end (ap);
-
- return t;
-}
-
/* Return true if FNDECL shouldn't be folded right now.
If a built-in function has an inline attribute always_inline
wrapper, defer folding it after always_inline functions have
@@ -11321,7 +10696,7 @@ default_expand_builtin (tree exp ATTRIBUTE_UNUSED,
/* Returns true is EXP represents data that would potentially reside
in a readonly section. */
-static bool
+bool
readonly_data_expr (tree exp)
{
STRIP_NOPS (exp);
@@ -11594,77 +10969,6 @@ fold_builtin_strpbrk (location_t loc, tree s1, tree s2, tree type)
}
}
-/* Simplify a call to the strcat builtin. DST and SRC are the arguments
- to the call.
-
- Return NULL_TREE if no simplification was possible, otherwise return the
- simplified form of the call as a tree.
-
- The simplified form may be a constant or other expression which
- computes the same value, but in a more efficient manner (including
- calls to other builtin functions).
-
- The call may contain arguments which need to be evaluated, but
- which are not useful to determine the result of the call. In
- this case we return a chain of COMPOUND_EXPRs. The LHS of each
- COMPOUND_EXPR will be an argument which must be evaluated.
- COMPOUND_EXPRs are chained through their RHS. The RHS of the last
- COMPOUND_EXPR in the chain will contain the tree for the simplified
- form of the builtin function call. */
-
-tree
-fold_builtin_strcat (location_t loc ATTRIBUTE_UNUSED, tree dst, tree src,
- tree len)
-{
- if (!validate_arg (dst, POINTER_TYPE)
- || !validate_arg (src, POINTER_TYPE))
- return NULL_TREE;
- else
- {
- const char *p = c_getstr (src);
-
- /* If the string length is zero, return the dst parameter. */
- if (p && *p == '\0')
- return dst;
-
- if (optimize_insn_for_speed_p ())
- {
- /* See if we can store by pieces into (dst + strlen(dst)). */
- tree newdst, call;
- tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN);
- tree memcpy_fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
-
- if (!strlen_fn || !memcpy_fn)
- return NULL_TREE;
-
- /* If the length of the source string isn't computable don't
- split strcat into strlen and memcpy. */
- if (! len)
- len = c_strlen (src, 1);
- if (! len || TREE_SIDE_EFFECTS (len))
- return NULL_TREE;
-
- /* Stabilize the argument list. */
- dst = builtin_save_expr (dst);
-
- /* Create strlen (dst). */
- newdst = build_call_expr_loc (loc, strlen_fn, 1, dst);
- /* Create (dst p+ strlen (dst)). */
-
- newdst = fold_build_pointer_plus_loc (loc, dst, newdst);
- newdst = builtin_save_expr (newdst);
-
- len = fold_convert_loc (loc, size_type_node, len);
- len = size_binop_loc (loc, PLUS_EXPR, len,
- build_int_cst (size_type_node, 1));
-
- call = build_call_expr_loc (loc, memcpy_fn, 3, newdst, src, len);
- return build2 (COMPOUND_EXPR, TREE_TYPE (dst), call, dst);
- }
- return NULL_TREE;
- }
-}
-
/* Simplify a call to the strncat builtin. DST, SRC, and LEN are the
arguments to the call.
@@ -11822,84 +11126,6 @@ fold_builtin_strcspn (location_t loc, tree s1, tree s2)
}
}
-/* 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
- actually a call to fputs_unlocked. If LEN in non-NULL, it represents
- the known length of the string. Return NULL_TREE if no simplification
- was possible. */
-
-tree
-fold_builtin_fputs (location_t loc, tree arg0, tree arg1,
- bool ignore, bool unlocked, tree len)
-{
- /* If we're using an unlocked function, assume the other unlocked
- functions exist explicitly. */
- tree const fn_fputc = (unlocked
- ? builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED)
- : builtin_decl_implicit (BUILT_IN_FPUTC));
- tree const fn_fwrite = (unlocked
- ? builtin_decl_explicit (BUILT_IN_FWRITE_UNLOCKED)
- : builtin_decl_implicit (BUILT_IN_FWRITE));
-
- /* If the return value is used, don't do the transformation. */
- if (!ignore)
- return NULL_TREE;
-
- /* Verify the arguments in the original call. */
- if (!validate_arg (arg0, POINTER_TYPE)
- || !validate_arg (arg1, POINTER_TYPE))
- return NULL_TREE;
-
- if (! len)
- len = c_strlen (arg0, 0);
-
- /* Get the length of the string passed to fputs. If the length
- can't be determined, punt. */
- if (!len
- || TREE_CODE (len) != INTEGER_CST)
- return NULL_TREE;
-
- switch (compare_tree_int (len, 1))
- {
- case -1: /* length is 0, delete the call entirely . */
- return omit_one_operand_loc (loc, integer_type_node,
- integer_zero_node, arg1);;
-
- case 0: /* length is 1, call fputc. */
- {
- const char *p = c_getstr (arg0);
-
- if (p != NULL)
- {
- if (fn_fputc)
- return build_call_expr_loc (loc, fn_fputc, 2,
- build_int_cst
- (integer_type_node, p[0]), arg1);
- else
- return NULL_TREE;
- }
- }
- /* FALLTHROUGH */
- case 1: /* length is greater than 1, call fwrite. */
- {
- /* If optimizing for size keep fputs. */
- if (optimize_function_for_size_p (cfun))
- return NULL_TREE;
- /* New argument list transforming fputs(string, stream) to
- fwrite(string, 1, len, stream). */
- if (fn_fwrite)
- return build_call_expr_loc (loc, fn_fwrite, 4, arg0,
- size_one_node, len, arg1);
- else
- return NULL_TREE;
- }
- default:
- gcc_unreachable ();
- }
- return NULL_TREE;
-}
-
/* Fold the next_arg or va_start call EXP. Returns true if there was an error
produced. False otherwise. This is done so that we don't output the error
or warning twice or three times. */
@@ -12571,240 +11797,6 @@ fold_builtin_object_size (tree ptr, tree ost)
return NULL_TREE;
}
-/* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin.
- DEST, SRC, LEN, and SIZE are the arguments to the call.
- IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_*
- code of the builtin. If MAXLEN is not NULL, it is maximum length
- passed as third argument. */
-
-tree
-fold_builtin_memory_chk (location_t loc, tree fndecl,
- tree dest, tree src, tree len, tree size,
- tree maxlen, bool ignore,
- enum built_in_function fcode)
-{
- tree fn;
-
- if (!validate_arg (dest, POINTER_TYPE)
- || !validate_arg (src,
- (fcode == BUILT_IN_MEMSET_CHK
- ? INTEGER_TYPE : POINTER_TYPE))
- || !validate_arg (len, INTEGER_TYPE)
- || !validate_arg (size, INTEGER_TYPE))
- return NULL_TREE;
-
- /* If SRC and DEST are the same (and not volatile), return DEST
- (resp. DEST+LEN for __mempcpy_chk). */
- if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
- {
- if (fcode != BUILT_IN_MEMPCPY_CHK)
- return omit_one_operand_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)),
- dest, len);
- else
- {
- tree temp = fold_build_pointer_plus_loc (loc, dest, len);
- return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), temp);
- }
- }
-
- if (! tree_fits_uhwi_p (size))
- return NULL_TREE;
-
- if (! integer_all_onesp (size))
- {
- if (! tree_fits_uhwi_p (len))
- {
- /* If LEN is not constant, try MAXLEN too.
- For MAXLEN only allow optimizing into non-_ocs function
- if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
- if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
- {
- if (fcode == BUILT_IN_MEMPCPY_CHK && ignore)
- {
- /* (void) __mempcpy_chk () can be optimized into
- (void) __memcpy_chk (). */
- fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
- if (!fn)
- return NULL_TREE;
-
- return build_call_expr_loc (loc, fn, 4, dest, src, len, size);
- }
- return NULL_TREE;
- }
- }
- else
- maxlen = len;
-
- if (tree_int_cst_lt (size, maxlen))
- return NULL_TREE;
- }
-
- fn = NULL_TREE;
- /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
- mem{cpy,pcpy,move,set} is available. */
- switch (fcode)
- {
- case BUILT_IN_MEMCPY_CHK:
- fn = builtin_decl_explicit (BUILT_IN_MEMCPY);
- break;
- case BUILT_IN_MEMPCPY_CHK:
- fn = builtin_decl_explicit (BUILT_IN_MEMPCPY);
- break;
- case BUILT_IN_MEMMOVE_CHK:
- fn = builtin_decl_explicit (BUILT_IN_MEMMOVE);
- break;
- case BUILT_IN_MEMSET_CHK:
- fn = builtin_decl_explicit (BUILT_IN_MEMSET);
- break;
- default:
- break;
- }
-
- if (!fn)
- return NULL_TREE;
-
- return build_call_expr_loc (loc, fn, 3, dest, src, len);
-}
-
-/* Fold a call to the __st[rp]cpy_chk builtin.
- DEST, SRC, and SIZE are the arguments to the call.
- IGNORE is true if return value can be ignored. FCODE is the BUILT_IN_*
- code of the builtin. If MAXLEN is not NULL, it is maximum length of
- strings passed as second argument. */
-
-tree
-fold_builtin_stxcpy_chk (location_t loc, tree fndecl, tree dest,
- tree src, tree size,
- tree maxlen, bool ignore,
- enum built_in_function fcode)
-{
- tree len, fn;
-
- if (!validate_arg (dest, POINTER_TYPE)
- || !validate_arg (src, POINTER_TYPE)
- || !validate_arg (size, INTEGER_TYPE))
- return NULL_TREE;
-
- /* If SRC and DEST are the same (and not volatile), return DEST. */
- if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
- return fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)), dest);
-
- if (! tree_fits_uhwi_p (size))
- return NULL_TREE;
-
- if (! integer_all_onesp (size))
- {
- len = c_strlen (src, 1);
- if (! len || ! tree_fits_uhwi_p (len))
- {
- /* If LEN is not constant, try MAXLEN too.
- For MAXLEN only allow optimizing into non-_ocs function
- if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
- if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
- {
- if (fcode == BUILT_IN_STPCPY_CHK)
- {
- if (! ignore)
- return NULL_TREE;
-
- /* If return value of __stpcpy_chk is ignored,
- optimize into __strcpy_chk. */
- fn = builtin_decl_explicit (BUILT_IN_STRCPY_CHK);
- if (!fn)
- return NULL_TREE;
-
- return build_call_expr_loc (loc, fn, 3, dest, src, size);
- }
-
- if (! len || TREE_SIDE_EFFECTS (len))
- return NULL_TREE;
-
- /* If c_strlen returned something, but not a constant,
- transform __strcpy_chk into __memcpy_chk. */
- fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
- if (!fn)
- return NULL_TREE;
-
- 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 fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fndecl)),
- build_call_expr_loc (loc, fn, 4,
- dest, src, len, size));
- }
- }
- else
- maxlen = len;
-
- if (! tree_int_cst_lt (maxlen, size))
- return NULL_TREE;
- }
-
- /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available. */
- fn = builtin_decl_explicit (fcode == BUILT_IN_STPCPY_CHK
- ? BUILT_IN_STPCPY : BUILT_IN_STRCPY);
- if (!fn)
- return NULL_TREE;
-
- return build_call_expr_loc (loc, fn, 2, dest, src);
-}
-
-/* Fold a call to the __st{r,p}ncpy_chk builtin. DEST, SRC, LEN, and SIZE
- are the arguments to the call. If MAXLEN is not NULL, it is maximum
- length passed as third argument. IGNORE is true if return value can be
- ignored. FCODE is the BUILT_IN_* code of the builtin. */
-
-tree
-fold_builtin_stxncpy_chk (location_t loc, tree dest, tree src,
- tree len, tree size, tree maxlen, bool ignore,
- enum built_in_function fcode)
-{
- tree fn;
-
- if (!validate_arg (dest, POINTER_TYPE)
- || !validate_arg (src, POINTER_TYPE)
- || !validate_arg (len, INTEGER_TYPE)
- || !validate_arg (size, INTEGER_TYPE))
- return NULL_TREE;
-
- if (fcode == BUILT_IN_STPNCPY_CHK && ignore)
- {
- /* If return value of __stpncpy_chk is ignored,
- optimize into __strncpy_chk. */
- fn = builtin_decl_explicit (BUILT_IN_STRNCPY_CHK);
- if (fn)
- return build_call_expr_loc (loc, fn, 4, dest, src, len, size);
- }
-
- if (! tree_fits_uhwi_p (size))
- return NULL_TREE;
-
- if (! integer_all_onesp (size))
- {
- if (! tree_fits_uhwi_p (len))
- {
- /* If LEN is not constant, try MAXLEN too.
- For MAXLEN only allow optimizing into non-_ocs function
- if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
- if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
- return NULL_TREE;
- }
- else
- maxlen = len;
-
- if (tree_int_cst_lt (size, maxlen))
- return NULL_TREE;
- }
-
- /* If __builtin_st{r,p}ncpy_chk is used, assume st{r,p}ncpy is available. */
- fn = builtin_decl_explicit (fcode == BUILT_IN_STPNCPY_CHK
- ? BUILT_IN_STPNCPY : BUILT_IN_STRNCPY);
- if (!fn)
- return NULL_TREE;
-
- return build_call_expr_loc (loc, fn, 3, dest, src, len);
-}
-
/* Fold a call to the __strcat_chk builtin FNDECL. DEST, SRC, and SIZE
are the arguments to the call. */
@@ -12888,201 +11880,6 @@ fold_builtin_strncat_chk (location_t loc, tree fndecl,
return build_call_expr_loc (loc, fn, 3, dest, src, len);
}
-/* Fold a call EXP to __{,v}sprintf_chk having NARGS passed as ARGS.
- Return NULL_TREE if a normal call should be emitted rather than
- expanding the function inline. FCODE is either BUILT_IN_SPRINTF_CHK
- or BUILT_IN_VSPRINTF_CHK. */
-
-static tree
-fold_builtin_sprintf_chk_1 (location_t loc, int nargs, tree *args,
- enum built_in_function fcode)
-{
- tree dest, size, len, fn, fmt, flag;
- const char *fmt_str;
-
- /* Verify the required arguments in the original call. */
- if (nargs < 4)
- return NULL_TREE;
- dest = args[0];
- if (!validate_arg (dest, POINTER_TYPE))
- return NULL_TREE;
- flag = args[1];
- if (!validate_arg (flag, INTEGER_TYPE))
- return NULL_TREE;
- size = args[2];
- if (!validate_arg (size, INTEGER_TYPE))
- return NULL_TREE;
- fmt = args[3];
- if (!validate_arg (fmt, POINTER_TYPE))
- return NULL_TREE;
-
- if (! tree_fits_uhwi_p (size))
- return NULL_TREE;
-
- len = NULL_TREE;
-
- if (!init_target_chars ())
- return NULL_TREE;
-
- /* Check whether the format is a literal string constant. */
- fmt_str = c_getstr (fmt);
- if (fmt_str != NULL)
- {
- /* If the format doesn't contain % args or %%, we know the size. */
- if (strchr (fmt_str, target_percent) == 0)
- {
- if (fcode != BUILT_IN_SPRINTF_CHK || nargs == 4)
- len = build_int_cstu (size_type_node, strlen (fmt_str));
- }
- /* If the format is "%s" and first ... argument is a string literal,
- we know the size too. */
- else if (fcode == BUILT_IN_SPRINTF_CHK
- && strcmp (fmt_str, target_percent_s) == 0)
- {
- tree arg;
-
- if (nargs == 5)
- {
- arg = args[4];
- if (validate_arg (arg, POINTER_TYPE))
- {
- len = c_strlen (arg, 1);
- if (! len || ! tree_fits_uhwi_p (len))
- len = NULL_TREE;
- }
- }
- }
- }
-
- if (! integer_all_onesp (size))
- {
- if (! len || ! tree_int_cst_lt (len, size))
- return NULL_TREE;
- }
-
- /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0
- or if format doesn't contain % chars or is "%s". */
- if (! integer_zerop (flag))
- {
- if (fmt_str == NULL)
- return NULL_TREE;
- if (strchr (fmt_str, target_percent) != NULL
- && strcmp (fmt_str, target_percent_s))
- return NULL_TREE;
- }
-
- /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available. */
- fn = builtin_decl_explicit (fcode == BUILT_IN_VSPRINTF_CHK
- ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF);
- if (!fn)
- return NULL_TREE;
-
- return rewrite_call_expr_array (loc, nargs, args, 4, fn, 2, dest, fmt);
-}
-
-/* Fold a call EXP to __{,v}sprintf_chk. Return NULL_TREE if
- a normal call should be emitted rather than expanding the function
- inline. FCODE is either BUILT_IN_SPRINTF_CHK or BUILT_IN_VSPRINTF_CHK. */
-
-static tree
-fold_builtin_sprintf_chk (location_t loc, tree exp,
- enum built_in_function fcode)
-{
- return fold_builtin_sprintf_chk_1 (loc, call_expr_nargs (exp),
- CALL_EXPR_ARGP (exp), fcode);
-}
-
-/* Fold a call EXP to {,v}snprintf having NARGS passed as ARGS. Return
- NULL_TREE if a normal call should be emitted rather than expanding
- the function inline. FCODE is either BUILT_IN_SNPRINTF_CHK or
- BUILT_IN_VSNPRINTF_CHK. If MAXLEN is not NULL, it is maximum length
- passed as second argument. */
-
-static tree
-fold_builtin_snprintf_chk_1 (location_t loc, int nargs, tree *args,
- tree maxlen, enum built_in_function fcode)
-{
- tree dest, size, len, fn, fmt, flag;
- const char *fmt_str;
-
- /* Verify the required arguments in the original call. */
- if (nargs < 5)
- return NULL_TREE;
- dest = args[0];
- if (!validate_arg (dest, POINTER_TYPE))
- return NULL_TREE;
- len = args[1];
- if (!validate_arg (len, INTEGER_TYPE))
- return NULL_TREE;
- flag = args[2];
- if (!validate_arg (flag, INTEGER_TYPE))
- return NULL_TREE;
- size = args[3];
- if (!validate_arg (size, INTEGER_TYPE))
- return NULL_TREE;
- fmt = args[4];
- if (!validate_arg (fmt, POINTER_TYPE))
- return NULL_TREE;
-
- if (! tree_fits_uhwi_p (size))
- return NULL_TREE;
-
- if (! integer_all_onesp (size))
- {
- if (! tree_fits_uhwi_p (len))
- {
- /* If LEN is not constant, try MAXLEN too.
- For MAXLEN only allow optimizing into non-_ocs function
- if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
- if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
- return NULL_TREE;
- }
- else
- maxlen = len;
-
- if (tree_int_cst_lt (size, maxlen))
- return NULL_TREE;
- }
-
- if (!init_target_chars ())
- return NULL_TREE;
-
- /* Only convert __{,v}snprintf_chk to {,v}snprintf if flag is 0
- or if format doesn't contain % chars or is "%s". */
- if (! integer_zerop (flag))
- {
- fmt_str = c_getstr (fmt);
- if (fmt_str == NULL)
- return NULL_TREE;
- if (strchr (fmt_str, target_percent) != NULL
- && strcmp (fmt_str, target_percent_s))
- return NULL_TREE;
- }
-
- /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is
- available. */
- fn = builtin_decl_explicit (fcode == BUILT_IN_VSNPRINTF_CHK
- ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF);
- if (!fn)
- return NULL_TREE;
-
- return rewrite_call_expr_array (loc, nargs, args, 5, fn, 3, dest, len, fmt);
-}
-
-/* Fold a call EXP to {,v}snprintf. Return NULL_TREE if
- a normal call should be emitted rather than expanding the function
- inline. FCODE is either BUILT_IN_SNPRINTF_CHK or
- BUILT_IN_VSNPRINTF_CHK. If MAXLEN is not NULL, it is maximum length
- passed as second argument. */
-
-static tree
-fold_builtin_snprintf_chk (location_t loc, tree exp, tree maxlen,
- enum built_in_function fcode)
-{
- return fold_builtin_snprintf_chk_1 (loc, call_expr_nargs (exp),
- CALL_EXPR_ARGP (exp), maxlen, fcode);
-}
-
/* Builtins with folding operations that operate on "..." arguments
need special handling; we need to store the arguments in a convenient
data structure before attempting any folding. Fortunately there are
@@ -13099,16 +11896,6 @@ fold_builtin_varargs (location_t loc, tree fndecl, tree exp,
switch (fcode)
{
- case BUILT_IN_SPRINTF_CHK:
- case BUILT_IN_VSPRINTF_CHK:
- ret = fold_builtin_sprintf_chk (loc, exp, fcode);
- break;
-
- case BUILT_IN_SNPRINTF_CHK:
- case BUILT_IN_VSNPRINTF_CHK:
- ret = fold_builtin_snprintf_chk (loc, exp, NULL_TREE, fcode);
- break;
-
case BUILT_IN_FPCLASSIFY:
ret = fold_builtin_fpclassify (loc, exp);
break;
@@ -13376,7 +12163,7 @@ fold_builtin_fprintf (location_t loc, tree fndecl, tree fp,
/* Initialize format string characters in the target charset. */
-static bool
+bool
init_target_chars (void)
{
static bool init;
@@ -13993,76 +12780,6 @@ do_mpc_arg2 (tree arg0, tree arg1, tree type, int do_nonfinite,
return result;
}
-/* Fold a call STMT to __{,v}sprintf_chk. Return NULL_TREE if
- a normal call should be emitted rather than expanding the function
- inline. FCODE is either BUILT_IN_SPRINTF_CHK or BUILT_IN_VSPRINTF_CHK. */
-
-static tree
-gimple_fold_builtin_sprintf_chk (gimple stmt, enum built_in_function fcode)
-{
- int nargs = gimple_call_num_args (stmt);
-
- return fold_builtin_sprintf_chk_1 (gimple_location (stmt), nargs,
- (nargs > 0
- ? gimple_call_arg_ptr (stmt, 0)
- : &error_mark_node), fcode);
-}
-
-/* Fold a call STMT to {,v}snprintf. Return NULL_TREE if
- a normal call should be emitted rather than expanding the function
- inline. FCODE is either BUILT_IN_SNPRINTF_CHK or
- BUILT_IN_VSNPRINTF_CHK. If MAXLEN is not NULL, it is maximum length
- passed as second argument. */
-
-tree
-gimple_fold_builtin_snprintf_chk (gimple stmt, tree maxlen,
- enum built_in_function fcode)
-{
- int nargs = gimple_call_num_args (stmt);
-
- return fold_builtin_snprintf_chk_1 (gimple_location (stmt), nargs,
- (nargs > 0
- ? gimple_call_arg_ptr (stmt, 0)
- : &error_mark_node), maxlen, fcode);
-}
-
-/* Builtins with folding operations that operate on "..." arguments
- need special handling; we need to store the arguments in a convenient
- data structure before attempting any folding. Fortunately there are
- only a few builtins that fall into this category. FNDECL is the
- function, EXP is the CALL_EXPR for the call, and IGNORE is true if the
- result of the function call is ignored. */
-
-static tree
-gimple_fold_builtin_varargs (tree fndecl, gimple stmt,
- bool ignore ATTRIBUTE_UNUSED)
-{
- enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
- tree ret = NULL_TREE;
-
- switch (fcode)
- {
- case BUILT_IN_SPRINTF_CHK:
- case BUILT_IN_VSPRINTF_CHK:
- ret = gimple_fold_builtin_sprintf_chk (stmt, fcode);
- break;
-
- case BUILT_IN_SNPRINTF_CHK:
- case BUILT_IN_VSNPRINTF_CHK:
- ret = gimple_fold_builtin_snprintf_chk (stmt, NULL_TREE, fcode);
-
- default:
- break;
- }
- if (ret)
- {
- ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
- TREE_NO_WARNING (ret) = 1;
- return ret;
- }
- return NULL_TREE;
-}
-
/* A wrapper function for builtin folding that prevents warnings for
"statement without effect" and the like, caused by removing the
call node earlier than the warning is generated. */
@@ -14093,8 +12810,6 @@ fold_call_stmt (gimple stmt, bool ignore)
{
if (nargs <= MAX_ARGS_TO_FOLD_BUILTIN)
ret = fold_builtin_n (loc, fndecl, args, nargs, ignore);
- if (!ret)
- ret = gimple_fold_builtin_varargs (fndecl, stmt, ignore);
if (ret)
{
/* Propagate location information from original call to
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 8be990e..cac2de6 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -71,29 +71,23 @@ extern void expand_builtin_trap (void);
extern rtx expand_builtin (tree, rtx, rtx, enum machine_mode, int);
extern enum built_in_function builtin_mathfn_code (const_tree);
extern tree fold_builtin_expect (location_t, tree, tree, tree);
-extern tree fold_builtin_strcpy (location_t, tree, tree, tree, tree);
-extern tree fold_builtin_strncpy (location_t, tree, tree, tree, tree, tree);
extern tree fold_fma (location_t, tree, tree, tree, tree);
extern bool avoid_folding_inline_builtin (tree);
extern tree fold_call_expr (location_t, tree, bool);
extern tree fold_builtin_call_array (location_t, tree, tree, int, tree *);
extern bool validate_gimple_arglist (const_gimple, ...);
extern rtx default_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
-extern tree fold_builtin_strcat (location_t, tree, tree, tree);
-extern tree fold_builtin_fputs (location_t, tree, tree, bool, bool, tree);
extern bool fold_builtin_next_arg (tree, bool);
-extern tree fold_builtin_memory_chk (location_t, tree, tree, tree, tree, tree,
- tree, bool, enum built_in_function);
-extern tree fold_builtin_stxcpy_chk (location_t, tree, tree, tree, tree, tree,
- bool, enum built_in_function);
-extern tree fold_builtin_stxncpy_chk (location_t, tree, tree, tree, tree, tree,
- bool, enum built_in_function);
-extern tree gimple_fold_builtin_snprintf_chk (gimple, tree,
- enum built_in_function);
extern tree do_mpc_arg2 (tree, tree, tree, int, int (*)(mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t));
extern tree fold_call_stmt (gimple, bool);
extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
extern bool is_simple_builtin (tree);
extern bool is_inexpensive_builtin (tree);
+extern bool readonly_data_expr (tree exp);
+extern const char *c_getstr (tree);
+extern bool init_target_chars (void);
+extern unsigned HOST_WIDE_INT target_percent;
+extern char target_percent_s[3];
+
#endif
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index fa56810..76a243a 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3. If not see
#include "gimplify-me.h"
#include "dbgcnt.h"
#include "builtins.h"
+#include "output.h"
/* Return true when DECL can be referenced from current unit.
FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -626,6 +627,79 @@ fold_gimple_cond (gimple stmt)
return false;
}
+
+/* Replace a statement at *SI_P with a sequence of statements in STMTS,
+ adjusting the replacement stmts location and virtual operands.
+ If the statement has a lhs the last stmt in the sequence is expected
+ to assign to that lhs. */
+
+static void
+gsi_replace_with_seq_vops (gimple_stmt_iterator *si_p, gimple_seq stmts)
+{
+ gimple stmt = gsi_stmt (*si_p);
+
+ if (gimple_has_location (stmt))
+ annotate_all_with_location (stmts, gimple_location (stmt));
+
+ /* First iterate over the replacement statements backward, assigning
+ virtual operands to their defining statements. */
+ gimple laststore = NULL;
+ for (gimple_stmt_iterator i = gsi_last (stmts);
+ !gsi_end_p (i); gsi_prev (&i))
+ {
+ gimple new_stmt = gsi_stmt (i);
+ if ((gimple_assign_single_p (new_stmt)
+ && !is_gimple_reg (gimple_assign_lhs (new_stmt)))
+ || (is_gimple_call (new_stmt)
+ && (gimple_call_flags (new_stmt)
+ & (ECF_NOVOPS | ECF_PURE | ECF_CONST | ECF_NORETURN)) == 0))
+ {
+ tree vdef;
+ if (!laststore)
+ vdef = gimple_vdef (stmt);
+ else
+ vdef = make_ssa_name (gimple_vop (cfun), new_stmt);
+ gimple_set_vdef (new_stmt, vdef);
+ if (vdef && TREE_CODE (vdef) == SSA_NAME)
+ SSA_NAME_DEF_STMT (vdef) = new_stmt;
+ laststore = new_stmt;
+ }
+ }
+
+ /* Second iterate over the statements forward, assigning virtual
+ operands to their uses. */
+ tree reaching_vuse = gimple_vuse (stmt);
+ for (gimple_stmt_iterator i = gsi_start (stmts);
+ !gsi_end_p (i); gsi_next (&i))
+ {
+ gimple new_stmt = gsi_stmt (i);
+ /* If the new statement possibly has a VUSE, update it with exact SSA
+ name we know will reach this one. */
+ if (gimple_has_mem_ops (new_stmt))
+ gimple_set_vuse (new_stmt, reaching_vuse);
+ gimple_set_modified (new_stmt, true);
+ if (gimple_vdef (new_stmt))
+ reaching_vuse = gimple_vdef (new_stmt);
+ }
+
+ /* If the new sequence does not do a store release the virtual
+ definition of the original statement. */
+ if (reaching_vuse
+ && reaching_vuse == gimple_vuse (stmt))
+ {
+ tree vdef = gimple_vdef (stmt);
+ if (vdef
+ && TREE_CODE (vdef) == SSA_NAME)
+ {
+ unlink_stmt_vdef (stmt);
+ release_ssa_name (vdef);
+ }
+ }
+
+ /* Finally replace the original statement with the sequence. */
+ gsi_replace_with_seq (si_p, stmts, false);
+}
+
/* Convert EXPR into a GIMPLE value suitable for substitution on the
RHS of an assignment. Insert the necessary statements before
iterator *SI_P. The statement at *SI_P, which must be a GIMPLE_CALL
@@ -643,8 +717,6 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
gimple stmt, new_stmt;
gimple_stmt_iterator i;
gimple_seq stmts = NULL;
- gimple laststore;
- tree reaching_vuse;
stmt = gsi_stmt (*si_p);
@@ -681,66 +753,587 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
pop_gimplify_context (NULL);
- if (gimple_has_location (stmt))
- annotate_all_with_location (stmts, gimple_location (stmt));
+ gsi_replace_with_seq_vops (si_p, stmts);
+}
- /* First iterate over the replacement statements backward, assigning
- virtual operands to their defining statements. */
- laststore = NULL;
- for (i = gsi_last (stmts); !gsi_end_p (i); gsi_prev (&i))
+
+/* Replace the call at *GSI with the gimple value VAL. */
+
+static void
+replace_call_with_value (gimple_stmt_iterator *gsi, tree val)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ tree lhs = gimple_call_lhs (stmt);
+ gimple repl;
+ if (lhs)
{
- new_stmt = gsi_stmt (i);
- if ((gimple_assign_single_p (new_stmt)
- && !is_gimple_reg (gimple_assign_lhs (new_stmt)))
- || (is_gimple_call (new_stmt)
- && (gimple_call_flags (new_stmt)
- & (ECF_NOVOPS | ECF_PURE | ECF_CONST | ECF_NORETURN)) == 0))
- {
- tree vdef;
- if (!laststore)
- vdef = gimple_vdef (stmt);
- else
- vdef = make_ssa_name (gimple_vop (cfun), new_stmt);
- gimple_set_vdef (new_stmt, vdef);
- if (vdef && TREE_CODE (vdef) == SSA_NAME)
- SSA_NAME_DEF_STMT (vdef) = new_stmt;
- laststore = new_stmt;
- }
+ if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (val)))
+ val = fold_convert (TREE_TYPE (lhs), val);
+ repl = gimple_build_assign (lhs, val);
}
+ else
+ repl = gimple_build_nop ();
+ tree vdef = gimple_vdef (stmt);
+ if (vdef && TREE_CODE (vdef) == SSA_NAME)
+ {
+ unlink_stmt_vdef (stmt);
+ release_ssa_name (vdef);
+ }
+ gsi_replace (gsi, repl, true);
+}
- /* Second iterate over the statements forward, assigning virtual
- operands to their uses. */
- reaching_vuse = gimple_vuse (stmt);
- for (i = gsi_start (stmts); !gsi_end_p (i); gsi_next (&i))
+/* Replace the call at *GSI with the new call REPL and fold that
+ again. */
+
+static void
+replace_call_with_call_and_fold (gimple_stmt_iterator *gsi, gimple repl)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ gimple_call_set_lhs (repl, gimple_call_lhs (stmt));
+ gimple_set_location (repl, gimple_location (stmt));
+ if (gimple_vdef (stmt)
+ && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
{
- new_stmt = gsi_stmt (i);
- /* If the new statement possibly has a VUSE, update it with exact SSA
- name we know will reach this one. */
- if (gimple_has_mem_ops (new_stmt))
- gimple_set_vuse (new_stmt, reaching_vuse);
- gimple_set_modified (new_stmt, true);
- if (gimple_vdef (new_stmt))
- reaching_vuse = gimple_vdef (new_stmt);
+ gimple_set_vdef (repl, gimple_vdef (stmt));
+ gimple_set_vuse (repl, gimple_vuse (stmt));
+ SSA_NAME_DEF_STMT (gimple_vdef (repl)) = repl;
}
+ gsi_replace (gsi, repl, true);
+ fold_stmt (gsi);
+}
- /* If the new sequence does not do a store release the virtual
- definition of the original statement. */
- if (reaching_vuse
- && reaching_vuse == gimple_vuse (stmt))
+/* Return true if VAR is a VAR_DECL or a component thereof. */
+
+static bool
+var_decl_component_p (tree var)
+{
+ tree inner = var;
+ while (handled_component_p (inner))
+ inner = TREE_OPERAND (inner, 0);
+ return SSA_VAR_P (inner);
+}
+
+/* Fold function call to builtin mem{{,p}cpy,move}. Return
+ NULL_TREE if no simplification can be made.
+ If ENDP is 0, return DEST (like memcpy).
+ If ENDP is 1, return DEST+LEN (like mempcpy).
+ If ENDP is 2, return DEST+LEN-1 (like stpcpy).
+ If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
+ (memmove). */
+
+static bool
+gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
+ tree dest, tree src, int endp)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ tree lhs = gimple_call_lhs (stmt);
+ tree len = gimple_call_arg (stmt, 2);
+ tree destvar, srcvar;
+ location_t loc = gimple_location (stmt);
+
+ /* If the LEN parameter is zero, return DEST. */
+ if (integer_zerop (len))
{
+ gimple repl;
+ if (gimple_call_lhs (stmt))
+ repl = gimple_build_assign (gimple_call_lhs (stmt), dest);
+ else
+ repl = gimple_build_nop ();
tree vdef = gimple_vdef (stmt);
- if (vdef
- && TREE_CODE (vdef) == SSA_NAME)
+ if (vdef && TREE_CODE (vdef) == SSA_NAME)
{
unlink_stmt_vdef (stmt);
release_ssa_name (vdef);
}
+ gsi_replace (gsi, repl, true);
+ return true;
}
- /* Finally replace the original statement with the sequence. */
- gsi_replace_with_seq (si_p, stmts, false);
+ /* If SRC and DEST are the same (and not volatile), return
+ DEST{,+LEN,+LEN-1}. */
+ if (operand_equal_p (src, dest, 0))
+ {
+ unlink_stmt_vdef (stmt);
+ if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
+ release_ssa_name (gimple_vdef (stmt));
+ if (!lhs)
+ {
+ gsi_replace (gsi, gimple_build_nop (), true);
+ return true;
+ }
+ goto done;
+ }
+ else
+ {
+ tree srctype, desttype;
+ unsigned int src_align, dest_align;
+ tree off0;
+
+ /* Build accesses at offset zero with a ref-all character type. */
+ off0 = build_int_cst (build_pointer_type_for_mode (char_type_node,
+ ptr_mode, true), 0);
+
+ /* If we can perform the copy efficiently with first doing all loads
+ and then all stores inline it that way. Currently efficiently
+ means that we can load all the memory into a single integer
+ register which is what MOVE_MAX gives us. */
+ src_align = get_pointer_alignment (src);
+ dest_align = get_pointer_alignment (dest);
+ if (tree_fits_uhwi_p (len)
+ && compare_tree_int (len, MOVE_MAX) <= 0
+ /* ??? Don't transform copies from strings with known length this
+ confuses the tree-ssa-strlen.c. This doesn't handle
+ the case in gcc.dg/strlenopt-8.c which is XFAILed for that
+ reason. */
+ && !c_strlen (src, 2))
+ {
+ unsigned ilen = tree_to_uhwi (len);
+ if (exact_log2 (ilen) != -1)
+ {
+ tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
+ if (type
+ && TYPE_MODE (type) != BLKmode
+ && (GET_MODE_SIZE (TYPE_MODE (type)) * BITS_PER_UNIT
+ == ilen * 8)
+ /* If the destination pointer is not aligned we must be able
+ to emit an unaligned store. */
+ && (dest_align >= GET_MODE_ALIGNMENT (TYPE_MODE (type))
+ || !SLOW_UNALIGNED_ACCESS (TYPE_MODE (type), dest_align)))
+ {
+ tree srctype = type;
+ tree desttype = type;
+ if (src_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
+ srctype = build_aligned_type (type, src_align);
+ tree srcmem = fold_build2 (MEM_REF, srctype, src, off0);
+ tree tem = fold_const_aggregate_ref (srcmem);
+ if (tem)
+ srcmem = tem;
+ else if (src_align < GET_MODE_ALIGNMENT (TYPE_MODE (type))
+ && SLOW_UNALIGNED_ACCESS (TYPE_MODE (type),
+ src_align))
+ srcmem = NULL_TREE;
+ if (srcmem)
+ {
+ gimple new_stmt;
+ if (is_gimple_reg_type (TREE_TYPE (srcmem)))
+ {
+ new_stmt = gimple_build_assign (NULL_TREE, srcmem);
+ if (gimple_in_ssa_p (cfun))
+ srcmem = make_ssa_name (TREE_TYPE (srcmem),
+ new_stmt);
+ else
+ srcmem = create_tmp_reg (TREE_TYPE (srcmem),
+ NULL);
+ gimple_assign_set_lhs (new_stmt, srcmem);
+ gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+ gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+ }
+ if (dest_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
+ desttype = build_aligned_type (type, dest_align);
+ new_stmt
+ = gimple_build_assign (fold_build2 (MEM_REF, desttype,
+ dest, off0),
+ srcmem);
+ gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+ gimple_set_vdef (new_stmt, gimple_vdef (stmt));
+ if (gimple_vdef (new_stmt)
+ && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
+ SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
+ if (!lhs)
+ {
+ gsi_replace (gsi, new_stmt, true);
+ return true;
+ }
+ gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+ goto done;
+ }
+ }
+ }
+ }
+
+ if (endp == 3)
+ {
+ /* Both DEST and SRC must be pointer types.
+ ??? This is what old code did. Is the testing for pointer types
+ really mandatory?
+
+ If either SRC is readonly or length is 1, we can use memcpy. */
+ if (!dest_align || !src_align)
+ return false;
+ if (readonly_data_expr (src)
+ || (tree_fits_uhwi_p (len)
+ && (MIN (src_align, dest_align) / BITS_PER_UNIT
+ >= tree_to_uhwi (len))))
+ {
+ tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+ if (!fn)
+ return false;
+ gimple_call_set_fndecl (stmt, fn);
+ gimple_call_set_arg (stmt, 0, dest);
+ gimple_call_set_arg (stmt, 1, src);
+ fold_stmt (gsi);
+ return true;
+ }
+
+ /* If *src and *dest can't overlap, optimize into memcpy as well. */
+ if (TREE_CODE (src) == ADDR_EXPR
+ && TREE_CODE (dest) == ADDR_EXPR)
+ {
+ tree src_base, dest_base, fn;
+ HOST_WIDE_INT src_offset = 0, dest_offset = 0;
+ HOST_WIDE_INT size = -1;
+ HOST_WIDE_INT maxsize = -1;
+
+ srcvar = TREE_OPERAND (src, 0);
+ src_base = get_ref_base_and_extent (srcvar, &src_offset,
+ &size, &maxsize);
+ destvar = TREE_OPERAND (dest, 0);
+ dest_base = get_ref_base_and_extent (destvar, &dest_offset,
+ &size, &maxsize);
+ if (tree_fits_uhwi_p (len))
+ maxsize = tree_to_uhwi (len);
+ else
+ maxsize = -1;
+ src_offset /= BITS_PER_UNIT;
+ dest_offset /= BITS_PER_UNIT;
+ if (SSA_VAR_P (src_base)
+ && SSA_VAR_P (dest_base))
+ {
+ if (operand_equal_p (src_base, dest_base, 0)
+ && ranges_overlap_p (src_offset, maxsize,
+ dest_offset, maxsize))
+ return false;
+ }
+ else if (TREE_CODE (src_base) == MEM_REF
+ && TREE_CODE (dest_base) == MEM_REF)
+ {
+ if (! operand_equal_p (TREE_OPERAND (src_base, 0),
+ TREE_OPERAND (dest_base, 0), 0))
+ return false;
+ offset_int off = mem_ref_offset (src_base) + src_offset;
+ if (!wi::fits_shwi_p (off))
+ return false;
+ src_offset = off.to_shwi ();
+
+ off = mem_ref_offset (dest_base) + dest_offset;
+ if (!wi::fits_shwi_p (off))
+ return false;
+ dest_offset = off.to_shwi ();
+ if (ranges_overlap_p (src_offset, maxsize,
+ dest_offset, maxsize))
+ return false;
+ }
+ else
+ return false;
+
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+ if (!fn)
+ return false;
+ gimple_call_set_fndecl (stmt, fn);
+ gimple_call_set_arg (stmt, 0, dest);
+ gimple_call_set_arg (stmt, 1, src);
+ fold_stmt (gsi);
+ return true;
+ }
+
+ /* If the destination and source do not alias optimize into
+ memcpy as well. */
+ if ((is_gimple_min_invariant (dest)
+ || TREE_CODE (dest) == SSA_NAME)
+ && (is_gimple_min_invariant (src)
+ || TREE_CODE (src) == SSA_NAME))
+ {
+ ao_ref destr, srcr;
+ ao_ref_init_from_ptr_and_size (&destr, dest, len);
+ ao_ref_init_from_ptr_and_size (&srcr, src, len);
+ if (!refs_may_alias_p_1 (&destr, &srcr, false))
+ {
+ tree fn;
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+ if (!fn)
+ return false;
+ gimple_call_set_fndecl (stmt, fn);
+ gimple_call_set_arg (stmt, 0, dest);
+ gimple_call_set_arg (stmt, 1, src);
+ fold_stmt (gsi);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (!tree_fits_shwi_p (len))
+ return false;
+ /* FIXME:
+ This logic lose for arguments like (type *)malloc (sizeof (type)),
+ since we strip the casts of up to VOID return value from malloc.
+ Perhaps we ought to inherit type from non-VOID argument here? */
+ STRIP_NOPS (src);
+ STRIP_NOPS (dest);
+ if (!POINTER_TYPE_P (TREE_TYPE (src))
+ || !POINTER_TYPE_P (TREE_TYPE (dest)))
+ return false;
+ /* In the following try to find a type that is most natural to be
+ used for the memcpy source and destination and that allows
+ the most optimization when memcpy is turned into a plain assignment
+ using that type. In theory we could always use a char[len] type
+ but that only gains us that the destination and source possibly
+ no longer will have their address taken. */
+ /* As we fold (void *)(p + CST) to (void *)p + CST undo this here. */
+ if (TREE_CODE (src) == POINTER_PLUS_EXPR)
+ {
+ tree tem = TREE_OPERAND (src, 0);
+ STRIP_NOPS (tem);
+ if (tem != TREE_OPERAND (src, 0))
+ src = build1 (NOP_EXPR, TREE_TYPE (tem), src);
+ }
+ if (TREE_CODE (dest) == POINTER_PLUS_EXPR)
+ {
+ tree tem = TREE_OPERAND (dest, 0);
+ STRIP_NOPS (tem);
+ if (tem != TREE_OPERAND (dest, 0))
+ dest = build1 (NOP_EXPR, TREE_TYPE (tem), dest);
+ }
+ srctype = TREE_TYPE (TREE_TYPE (src));
+ if (TREE_CODE (srctype) == ARRAY_TYPE
+ && !tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
+ {
+ srctype = TREE_TYPE (srctype);
+ STRIP_NOPS (src);
+ src = build1 (NOP_EXPR, build_pointer_type (srctype), src);
+ }
+ desttype = TREE_TYPE (TREE_TYPE (dest));
+ if (TREE_CODE (desttype) == ARRAY_TYPE
+ && !tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
+ {
+ desttype = TREE_TYPE (desttype);
+ STRIP_NOPS (dest);
+ dest = build1 (NOP_EXPR, build_pointer_type (desttype), dest);
+ }
+ if (TREE_ADDRESSABLE (srctype)
+ || TREE_ADDRESSABLE (desttype))
+ return false;
+
+ /* Make sure we are not copying using a floating-point mode or
+ a type whose size possibly does not match its precision. */
+ if (FLOAT_MODE_P (TYPE_MODE (desttype))
+ || TREE_CODE (desttype) == BOOLEAN_TYPE
+ || TREE_CODE (desttype) == ENUMERAL_TYPE)
+ desttype = bitwise_type_for_mode (TYPE_MODE (desttype));
+ if (FLOAT_MODE_P (TYPE_MODE (srctype))
+ || TREE_CODE (srctype) == BOOLEAN_TYPE
+ || TREE_CODE (srctype) == ENUMERAL_TYPE)
+ srctype = bitwise_type_for_mode (TYPE_MODE (srctype));
+ if (!srctype)
+ srctype = desttype;
+ if (!desttype)
+ desttype = srctype;
+ if (!srctype)
+ return false;
+
+ src_align = get_pointer_alignment (src);
+ dest_align = get_pointer_alignment (dest);
+ if (dest_align < TYPE_ALIGN (desttype)
+ || src_align < TYPE_ALIGN (srctype))
+ return false;
+
+ destvar = dest;
+ STRIP_NOPS (destvar);
+ if (TREE_CODE (destvar) == ADDR_EXPR
+ && var_decl_component_p (TREE_OPERAND (destvar, 0))
+ && tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
+ destvar = fold_build2 (MEM_REF, desttype, destvar, off0);
+ else
+ destvar = NULL_TREE;
+
+ srcvar = src;
+ STRIP_NOPS (srcvar);
+ if (TREE_CODE (srcvar) == ADDR_EXPR
+ && var_decl_component_p (TREE_OPERAND (srcvar, 0))
+ && tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
+ {
+ if (!destvar
+ || src_align >= TYPE_ALIGN (desttype))
+ srcvar = fold_build2 (MEM_REF, destvar ? desttype : srctype,
+ srcvar, off0);
+ else if (!STRICT_ALIGNMENT)
+ {
+ srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype),
+ src_align);
+ srcvar = fold_build2 (MEM_REF, srctype, srcvar, off0);
+ }
+ else
+ srcvar = NULL_TREE;
+ }
+ else
+ srcvar = NULL_TREE;
+
+ if (srcvar == NULL_TREE && destvar == NULL_TREE)
+ return false;
+
+ if (srcvar == NULL_TREE)
+ {
+ STRIP_NOPS (src);
+ if (src_align >= TYPE_ALIGN (desttype))
+ srcvar = fold_build2 (MEM_REF, desttype, src, off0);
+ else
+ {
+ if (STRICT_ALIGNMENT)
+ return false;
+ srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype),
+ src_align);
+ srcvar = fold_build2 (MEM_REF, srctype, src, off0);
+ }
+ }
+ else if (destvar == NULL_TREE)
+ {
+ STRIP_NOPS (dest);
+ if (dest_align >= TYPE_ALIGN (srctype))
+ destvar = fold_build2 (MEM_REF, srctype, dest, off0);
+ else
+ {
+ if (STRICT_ALIGNMENT)
+ return false;
+ desttype = build_aligned_type (TYPE_MAIN_VARIANT (srctype),
+ dest_align);
+ destvar = fold_build2 (MEM_REF, desttype, dest, off0);
+ }
+ }
+
+ gimple new_stmt;
+ if (is_gimple_reg_type (TREE_TYPE (srcvar)))
+ {
+ new_stmt = gimple_build_assign (NULL_TREE, srcvar);
+ if (gimple_in_ssa_p (cfun))
+ srcvar = make_ssa_name (TREE_TYPE (srcvar), new_stmt);
+ else
+ srcvar = create_tmp_reg (TREE_TYPE (srcvar), NULL);
+ gimple_assign_set_lhs (new_stmt, srcvar);
+ gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+ gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+ }
+ new_stmt = gimple_build_assign (destvar, srcvar);
+ gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+ gimple_set_vdef (new_stmt, gimple_vdef (stmt));
+ if (gimple_vdef (new_stmt)
+ && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
+ SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
+ if (!lhs)
+ {
+ gsi_replace (gsi, new_stmt, true);
+ return true;
+ }
+ gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+ }
+
+done:
+ if (endp == 0 || endp == 3)
+ len = NULL_TREE;
+ else if (endp == 2)
+ len = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (len), len,
+ ssize_int (1));
+ if (endp == 2 || endp == 1)
+ dest = fold_build_pointer_plus_loc (loc, dest, len);
+
+ dest = force_gimple_operand_gsi (gsi, dest, false, NULL_TREE, true,
+ GSI_SAME_STMT);
+ gimple repl = gimple_build_assign (lhs, dest);
+ gsi_replace (gsi, repl, true);
+ return true;
}
+/* Fold function call to builtin memset or bzero at *GSI setting the
+ memory of size LEN to VAL. Return whether a simplification was made. */
+
+static bool
+gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ tree etype;
+ unsigned HOST_WIDE_INT length, cval;
+
+ /* If the LEN parameter is zero, return DEST. */
+ if (integer_zerop (len))
+ {
+ replace_call_with_value (gsi, gimple_call_arg (stmt, 0));
+ return true;
+ }
+
+ if (! tree_fits_uhwi_p (len))
+ return false;
+
+ if (TREE_CODE (c) != INTEGER_CST)
+ return false;
+
+ tree dest = gimple_call_arg (stmt, 0);
+ tree var = dest;
+ if (TREE_CODE (var) != ADDR_EXPR)
+ return false;
+
+ var = TREE_OPERAND (var, 0);
+ if (TREE_THIS_VOLATILE (var))
+ return false;
+
+ etype = TREE_TYPE (var);
+ if (TREE_CODE (etype) == ARRAY_TYPE)
+ etype = TREE_TYPE (etype);
+
+ if (!INTEGRAL_TYPE_P (etype)
+ && !POINTER_TYPE_P (etype))
+ return NULL_TREE;
+
+ if (! var_decl_component_p (var))
+ return NULL_TREE;
+
+ length = tree_to_uhwi (len);
+ if (GET_MODE_SIZE (TYPE_MODE (etype)) != length
+ || get_pointer_alignment (dest) / BITS_PER_UNIT < length)
+ return NULL_TREE;
+
+ if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
+ return NULL_TREE;
+
+ if (integer_zerop (c))
+ cval = 0;
+ else
+ {
+ if (CHAR_BIT != 8 || BITS_PER_UNIT != 8 || HOST_BITS_PER_WIDE_INT > 64)
+ return NULL_TREE;
+
+ cval = TREE_INT_CST_LOW (c);
+ cval &= 0xff;
+ cval |= cval << 8;
+ cval |= cval << 16;
+ cval |= (cval << 31) << 1;
+ }
+
+ var = fold_build2 (MEM_REF, etype, dest, build_int_cst (ptr_type_node, 0));
+ gimple store = gimple_build_assign (var, build_int_cst_type (etype, cval));
+ gimple_set_vuse (store, gimple_vuse (stmt));
+ tree vdef = gimple_vdef (stmt);
+ if (vdef && TREE_CODE (vdef) == SSA_NAME)
+ {
+ gimple_set_vdef (store, gimple_vdef (stmt));
+ SSA_NAME_DEF_STMT (gimple_vdef (stmt)) = store;
+ }
+ gsi_insert_before (gsi, store, GSI_SAME_STMT);
+ if (gimple_call_lhs (stmt))
+ {
+ gimple asgn = gimple_build_assign (gimple_call_lhs (stmt), dest);
+ gsi_replace (gsi, asgn, true);
+ }
+ else
+ {
+ gimple_stmt_iterator gsi2 = *gsi;
+ gsi_prev (gsi);
+ gsi_remove (&gsi2, true);
+ }
+
+ return true;
+}
+
+
/* Return the string length, maximum string length or maximum value of
ARG in LENGTH.
If ARG is an SSA name variable, follow its use-def chains. If LENGTH
@@ -866,55 +1459,725 @@ get_maxval_strlen (tree arg, tree *length, bitmap visited, int type)
}
-/* Fold builtin call in statement STMT. Returns a simplified tree.
- We may return a non-constant expression, including another call
- to a different function and with different arguments, e.g.,
- substituting memcpy for strcpy when the string length is known.
- Note that some builtins expand into inline code that may not
- be valid in GIMPLE. Callers must take care. */
+/* 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
+ copied. Return NULL_TREE if no simplification can be made. */
-static tree
-gimple_fold_builtin (gimple stmt)
+static bool
+gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
+ location_t loc, tree dest, tree src, tree len)
{
- tree result, val[3];
- tree callee, a;
- int arg_idx, type;
- bitmap visited;
- bool ignore;
- int nargs;
- location_t loc = gimple_location (stmt);
+ tree fn;
- ignore = (gimple_call_lhs (stmt) == NULL);
+ /* If SRC and DEST are the same (and not volatile), return DEST. */
+ if (operand_equal_p (src, dest, 0))
+ {
+ replace_call_with_value (gsi, dest);
+ return true;
+ }
- /* First try the generic builtin folder. If that succeeds, return the
- result directly. */
- result = fold_call_stmt (stmt, ignore);
- if (result)
+ if (optimize_function_for_size_p (cfun))
+ return false;
+
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+ if (!fn)
+ return false;
+
+ if (!len)
{
- if (ignore)
- STRIP_NOPS (result);
+ len = c_strlen (src, 1);
+ if (! len || TREE_SIDE_EFFECTS (len))
+ return NULL_TREE;
+ }
+
+ len = fold_convert_loc (loc, size_type_node, len);
+ len = size_binop_loc (loc, PLUS_EXPR, len, build_int_cst (size_type_node, 1));
+ len = force_gimple_operand_gsi (gsi, len, true,
+ NULL_TREE, true, GSI_SAME_STMT);
+ gimple repl = gimple_build_call (fn, 3, dest, src, len);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+}
+
+/* Fold function call to builtin strncpy with arguments DEST, SRC, and LEN.
+ If SLEN is not NULL, it represents the length of the source string.
+ 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)
+{
+ tree fn;
+
+ /* If the LEN parameter is zero, return DEST. */
+ if (integer_zerop (len))
+ {
+ replace_call_with_value (gsi, dest);
+ return true;
+ }
+
+ /* We can't compare slen with len as constants below if len is not a
+ constant. */
+ if (len == 0 || 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)
+ return false;
+
+ slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
+
+ /* We do not support simplification of this case, though we do
+ support it when expanding trees into RTL. */
+ /* FIXME: generate a call to __builtin_memset. */
+ if (tree_int_cst_lt (slen, len))
+ return false;
+
+ /* OK transform into builtin memcpy. */
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+ if (!fn)
+ return false;
+
+ len = fold_convert_loc (loc, size_type_node, len);
+ len = force_gimple_operand_gsi (gsi, len, true,
+ NULL_TREE, true, GSI_SAME_STMT);
+ gimple repl = gimple_build_call (fn, 3, dest, src, len);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+}
+
+/* Simplify a call to the strcat builtin. DST and SRC are the arguments
+ to the call.
+
+ Return NULL_TREE if no simplification was possible, otherwise return the
+ simplified form of the call as a tree.
+
+ The simplified form may be a constant or other expression which
+ computes the same value, but in a more efficient manner (including
+ calls to other builtin functions).
+
+ The call may contain arguments which need to be evaluated, but
+ which are not useful to determine the result of the call. In
+ this case we return a chain of COMPOUND_EXPRs. The LHS of each
+ COMPOUND_EXPR will be an argument which must be evaluated.
+ COMPOUND_EXPRs are chained through their RHS. The RHS of the last
+ COMPOUND_EXPR in the chain will contain the tree for the simplified
+ 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 stmt = gsi_stmt (*gsi);
+
+ const char *p = c_getstr (src);
+
+ /* If the string length is zero, return the dst parameter. */
+ if (p && *p == '\0')
+ {
+ replace_call_with_value (gsi, dst);
+ return true;
+ }
+
+ if (!optimize_bb_for_speed_p (gimple_bb (stmt)))
+ return false;
+
+ /* See if we can store by pieces into (dst + strlen(dst)). */
+ tree newdst;
+ tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN);
+ tree memcpy_fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+
+ if (!strlen_fn || !memcpy_fn)
+ return false;
+
+ /* If the length of the source string isn't computable don't
+ split strcat into strlen and memcpy. */
+ if (! len)
+ len = c_strlen (src, 1);
+ if (! len || TREE_SIDE_EFFECTS (len))
+ return false;
+
+ /* Create strlen (dst). */
+ gimple_seq stmts = NULL, stmts2;
+ gimple repl = gimple_build_call (strlen_fn, 1, dst);
+ gimple_set_location (repl, loc);
+ if (gimple_in_ssa_p (cfun))
+ newdst = make_ssa_name (size_type_node, NULL);
+ else
+ newdst = create_tmp_reg (size_type_node, NULL);
+ gimple_call_set_lhs (repl, newdst);
+ gimple_seq_add_stmt_without_update (&stmts, repl);
+
+ /* Create (dst p+ strlen (dst)). */
+ newdst = fold_build_pointer_plus_loc (loc, dst, newdst);
+ newdst = force_gimple_operand (newdst, &stmts2, true, NULL_TREE);
+ gimple_seq_add_seq_without_update (&stmts, stmts2);
+
+ len = fold_convert_loc (loc, size_type_node, len);
+ len = size_binop_loc (loc, PLUS_EXPR, len,
+ build_int_cst (size_type_node, 1));
+ len = force_gimple_operand (len, &stmts2, true, NULL_TREE);
+ gimple_seq_add_seq_without_update (&stmts, stmts2);
+
+ repl = gimple_build_call (memcpy_fn, 3, newdst, src, len);
+ gimple_seq_add_stmt_without_update (&stmts, repl);
+ if (gimple_call_lhs (stmt))
+ {
+ repl = gimple_build_assign (gimple_call_lhs (stmt), dst);
+ gimple_seq_add_stmt_without_update (&stmts, repl);
+ gsi_replace_with_seq_vops (gsi, stmts);
+ /* gsi now points at the assignment to the lhs, get a
+ stmt iterator to the memcpy call.
+ ??? We can't use gsi_for_stmt as that doesn't work when the
+ CFG isn't built yet. */
+ gimple_stmt_iterator gsi2 = *gsi;
+ gsi_prev (&gsi2);
+ fold_stmt (&gsi2);
+ }
+ else
+ {
+ gsi_replace_with_seq_vops (gsi, stmts);
+ fold_stmt (gsi);
+ }
+ return true;
+}
+
+/* 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
+ actually a call to fputs_unlocked. If LEN in non-NULL, it represents
+ the known length of the string. Return NULL_TREE if no simplification
+ was possible. */
+
+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)
+{
+ /* If we're using an unlocked function, assume the other unlocked
+ functions exist explicitly. */
+ tree const fn_fputc = (unlocked
+ ? builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED)
+ : builtin_decl_implicit (BUILT_IN_FPUTC));
+ tree const fn_fwrite = (unlocked
+ ? builtin_decl_explicit (BUILT_IN_FWRITE_UNLOCKED)
+ : builtin_decl_implicit (BUILT_IN_FWRITE));
+
+ /* If the return value is used, don't do the transformation. */
+ if (!ignore)
+ 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. */
+ if (!len
+ || TREE_CODE (len) != INTEGER_CST)
+ return false;
+
+ switch (compare_tree_int (len, 1))
+ {
+ case -1: /* length is 0, delete the call entirely . */
+ replace_call_with_value (gsi, integer_zero_node);
+ return true;
+
+ case 0: /* length is 1, call fputc. */
+ {
+ const char *p = c_getstr (arg0);
+ if (p != NULL)
+ {
+ if (!fn_fputc)
+ return false;
+
+ gimple repl = gimple_build_call (fn_fputc, 2,
+ build_int_cst
+ (integer_type_node, p[0]), arg1);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+ /* FALLTHROUGH */
+ case 1: /* length is greater than 1, call fwrite. */
+ {
+ /* If optimizing for size keep fputs. */
+ if (optimize_function_for_size_p (cfun))
+ return false;
+ /* New argument list transforming fputs(string, stream) to
+ fwrite(string, 1, len, stream). */
+ if (!fn_fwrite)
+ return false;
+
+ gimple repl = gimple_build_call (fn_fwrite, 4, arg0,
+ size_one_node, len, arg1);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ default:
+ gcc_unreachable ();
+ }
+ return false;
+}
+
+/* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin.
+ DEST, SRC, LEN, and SIZE are the arguments to the call.
+ IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_*
+ code of the builtin. If MAXLEN is not NULL, it is maximum length
+ passed as third argument. */
+
+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)
+{
+ tree fn;
+
+ /* If SRC and DEST are the same (and not volatile), return DEST
+ (resp. DEST+LEN for __mempcpy_chk). */
+ if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
+ {
+ if (fcode != BUILT_IN_MEMPCPY_CHK)
+ {
+ replace_call_with_value (gsi, dest);
+ return true;
+ }
else
- result = fold_convert (gimple_call_return_type (stmt), result);
- return result;
+ {
+ tree temp = fold_build_pointer_plus_loc (loc, dest, len);
+ temp = force_gimple_operand_gsi (gsi, temp,
+ false, NULL_TREE, true,
+ GSI_SAME_STMT);
+ replace_call_with_value (gsi, temp);
+ return true;
+ }
}
- /* Ignore MD builtins. */
- callee = gimple_call_fndecl (stmt);
- if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_MD)
- return NULL_TREE;
+ if (! tree_fits_uhwi_p (size))
+ return false;
- /* Give up for always_inline inline builtins until they are
- inlined. */
- if (avoid_folding_inline_builtin (callee))
- return NULL_TREE;
+ if (! integer_all_onesp (size))
+ {
+ if (! tree_fits_uhwi_p (len))
+ {
+ /* If LEN is not constant, try MAXLEN too.
+ For MAXLEN only allow optimizing into non-_ocs function
+ if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
+ if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
+ {
+ if (fcode == BUILT_IN_MEMPCPY_CHK && ignore)
+ {
+ /* (void) __mempcpy_chk () can be optimized into
+ (void) __memcpy_chk (). */
+ fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
+ if (!fn)
+ return false;
+
+ gimple repl = gimple_build_call (fn, 4, dest, src, len, size);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ return false;
+ }
+ }
+ else
+ maxlen = len;
- /* If the builtin could not be folded, and it has no argument list,
- we're done. */
- nargs = gimple_call_num_args (stmt);
- if (nargs == 0)
- return NULL_TREE;
+ if (tree_int_cst_lt (size, maxlen))
+ return false;
+ }
+
+ fn = NULL_TREE;
+ /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
+ mem{cpy,pcpy,move,set} is available. */
+ switch (fcode)
+ {
+ case BUILT_IN_MEMCPY_CHK:
+ fn = builtin_decl_explicit (BUILT_IN_MEMCPY);
+ break;
+ case BUILT_IN_MEMPCPY_CHK:
+ fn = builtin_decl_explicit (BUILT_IN_MEMPCPY);
+ break;
+ case BUILT_IN_MEMMOVE_CHK:
+ fn = builtin_decl_explicit (BUILT_IN_MEMMOVE);
+ break;
+ case BUILT_IN_MEMSET_CHK:
+ fn = builtin_decl_explicit (BUILT_IN_MEMSET);
+ break;
+ default:
+ break;
+ }
+
+ if (!fn)
+ return false;
+
+ gimple repl = gimple_build_call (fn, 3, dest, src, len);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+}
+
+/* Fold a call to the __st[rp]cpy_chk builtin.
+ DEST, SRC, and SIZE are the arguments to the call.
+ IGNORE is true if return value can be ignored. FCODE is the BUILT_IN_*
+ code of the builtin. If MAXLEN is not NULL, it is maximum length of
+ strings passed as second argument. */
+
+static bool
+gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
+ location_t loc, tree dest,
+ tree src, tree size,
+ tree maxlen, bool ignore,
+ enum built_in_function fcode)
+{
+ tree len, fn;
+
+ /* If SRC and DEST are the same (and not volatile), return DEST. */
+ if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
+ {
+ replace_call_with_value (gsi, dest);
+ return true;
+ }
+
+ if (! tree_fits_uhwi_p (size))
+ return false;
+
+ if (! integer_all_onesp (size))
+ {
+ len = c_strlen (src, 1);
+ if (! len || ! tree_fits_uhwi_p (len))
+ {
+ /* If LEN is not constant, try MAXLEN too.
+ For MAXLEN only allow optimizing into non-_ocs function
+ if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
+ if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
+ {
+ if (fcode == BUILT_IN_STPCPY_CHK)
+ {
+ if (! ignore)
+ return false;
+
+ /* If return value of __stpcpy_chk is ignored,
+ optimize into __strcpy_chk. */
+ fn = builtin_decl_explicit (BUILT_IN_STRCPY_CHK);
+ if (!fn)
+ return false;
+
+ gimple repl = gimple_build_call (fn, 3, dest, src, size);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+
+ if (! len || TREE_SIDE_EFFECTS (len))
+ return false;
+
+ /* If c_strlen returned something, but not a constant,
+ transform __strcpy_chk into __memcpy_chk. */
+ fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
+ if (!fn)
+ 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));
+ len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE,
+ true, GSI_SAME_STMT);
+ gimple repl = gimple_build_call (fn, 4, dest, src, len, size);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+ else
+ maxlen = len;
+
+ if (! tree_int_cst_lt (maxlen, size))
+ return false;
+ }
+
+ /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available. */
+ fn = builtin_decl_explicit (fcode == BUILT_IN_STPCPY_CHK
+ ? BUILT_IN_STPCPY : BUILT_IN_STRCPY);
+ if (!fn)
+ return false;
+
+ gimple repl = gimple_build_call (fn, 2, dest, src);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+}
+
+/* Fold a call to the __st{r,p}ncpy_chk builtin. DEST, SRC, LEN, and SIZE
+ are the arguments to the call. If MAXLEN is not NULL, it is maximum
+ length passed as third argument. IGNORE is true if return value can be
+ ignored. FCODE is the BUILT_IN_* code of the builtin. */
+
+static bool
+gimple_fold_builtin_stxncpy_chk (gimple_stmt_iterator *gsi,
+ tree dest, tree src,
+ tree len, tree size, tree maxlen, bool ignore,
+ enum built_in_function fcode)
+{
+ tree fn;
+
+ if (fcode == BUILT_IN_STPNCPY_CHK && ignore)
+ {
+ /* If return value of __stpncpy_chk is ignored,
+ optimize into __strncpy_chk. */
+ fn = builtin_decl_explicit (BUILT_IN_STRNCPY_CHK);
+ if (fn)
+ {
+ gimple repl = gimple_build_call (fn, 4, dest, src, len, size);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+
+ if (! tree_fits_uhwi_p (size))
+ return false;
+
+ if (! integer_all_onesp (size))
+ {
+ if (! tree_fits_uhwi_p (len))
+ {
+ /* If LEN is not constant, try MAXLEN too.
+ For MAXLEN only allow optimizing into non-_ocs function
+ if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
+ if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
+ return false;
+ }
+ else
+ maxlen = len;
+
+ if (tree_int_cst_lt (size, maxlen))
+ return false;
+ }
+
+ /* If __builtin_st{r,p}ncpy_chk is used, assume st{r,p}ncpy is available. */
+ fn = builtin_decl_explicit (fcode == BUILT_IN_STPNCPY_CHK
+ ? BUILT_IN_STPNCPY : BUILT_IN_STRNCPY);
+ if (!fn)
+ return false;
+
+ gimple repl = gimple_build_call (fn, 3, dest, src, len);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+}
+
+/* Fold a call EXP to {,v}snprintf having NARGS passed as ARGS. Return
+ NULL_TREE if a normal call should be emitted rather than expanding
+ the function inline. FCODE is either BUILT_IN_SNPRINTF_CHK or
+ BUILT_IN_VSNPRINTF_CHK. If MAXLEN is not NULL, it is maximum length
+ passed as second argument. */
+
+static bool
+gimple_fold_builtin_snprintf_chk (gimple_stmt_iterator *gsi,
+ tree maxlen, enum built_in_function fcode)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ tree dest, size, len, fn, fmt, flag;
+ const char *fmt_str;
+
+ /* Verify the required arguments in the original call. */
+ if (gimple_call_num_args (stmt) < 5)
+ return false;
+
+ dest = gimple_call_arg (stmt, 0);
+ len = gimple_call_arg (stmt, 1);
+ flag = gimple_call_arg (stmt, 2);
+ size = gimple_call_arg (stmt, 3);
+ fmt = gimple_call_arg (stmt, 4);
+
+ if (! tree_fits_uhwi_p (size))
+ return false;
+
+ if (! integer_all_onesp (size))
+ {
+ if (! tree_fits_uhwi_p (len))
+ {
+ /* If LEN is not constant, try MAXLEN too.
+ For MAXLEN only allow optimizing into non-_ocs function
+ if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
+ if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
+ return false;
+ }
+ else
+ maxlen = len;
+
+ if (tree_int_cst_lt (size, maxlen))
+ return false;
+ }
+
+ if (!init_target_chars ())
+ return false;
+
+ /* Only convert __{,v}snprintf_chk to {,v}snprintf if flag is 0
+ or if format doesn't contain % chars or is "%s". */
+ if (! integer_zerop (flag))
+ {
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return false;
+ if (strchr (fmt_str, target_percent) != NULL
+ && strcmp (fmt_str, target_percent_s))
+ return false;
+ }
+
+ /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is
+ available. */
+ fn = builtin_decl_explicit (fcode == BUILT_IN_VSNPRINTF_CHK
+ ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF);
+ if (!fn)
+ return false;
+
+ /* Replace the called function and the first 5 argument by 3 retaining
+ trailing varargs. */
+ gimple_call_set_fndecl (stmt, fn);
+ gimple_call_set_fntype (stmt, TREE_TYPE (fn));
+ gimple_call_set_arg (stmt, 0, dest);
+ gimple_call_set_arg (stmt, 1, len);
+ gimple_call_set_arg (stmt, 2, fmt);
+ for (unsigned i = 3; i < gimple_call_num_args (stmt) - 2; ++i)
+ gimple_call_set_arg (stmt, i, gimple_call_arg (stmt, i + 2));
+ gimple_set_num_ops (stmt, gimple_num_ops (stmt) - 2);
+ fold_stmt (gsi);
+ return true;
+}
+
+/* Fold a call EXP to __{,v}sprintf_chk having NARGS passed as ARGS.
+ Return NULL_TREE if a normal call should be emitted rather than
+ expanding the function inline. FCODE is either BUILT_IN_SPRINTF_CHK
+ or BUILT_IN_VSPRINTF_CHK. */
+
+static bool
+gimple_fold_builtin_sprintf_chk (gimple_stmt_iterator *gsi,
+ enum built_in_function fcode)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ tree dest, size, len, fn, fmt, flag;
+ const char *fmt_str;
+ unsigned nargs = gimple_call_num_args (stmt);
+
+ /* Verify the required arguments in the original call. */
+ if (nargs < 4)
+ return false;
+ dest = gimple_call_arg (stmt, 0);
+ flag = gimple_call_arg (stmt, 1);
+ size = gimple_call_arg (stmt, 2);
+ fmt = gimple_call_arg (stmt, 3);
+
+ if (! tree_fits_uhwi_p (size))
+ return false;
+
+ len = NULL_TREE;
+
+ if (!init_target_chars ())
+ return false;
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str != NULL)
+ {
+ /* If the format doesn't contain % args or %%, we know the size. */
+ if (strchr (fmt_str, target_percent) == 0)
+ {
+ if (fcode != BUILT_IN_SPRINTF_CHK || nargs == 4)
+ len = build_int_cstu (size_type_node, strlen (fmt_str));
+ }
+ /* If the format is "%s" and first ... argument is a string literal,
+ we know the size too. */
+ else if (fcode == BUILT_IN_SPRINTF_CHK
+ && strcmp (fmt_str, target_percent_s) == 0)
+ {
+ tree arg;
+
+ if (nargs == 5)
+ {
+ arg = gimple_call_arg (stmt, 4);
+ if (POINTER_TYPE_P (TREE_TYPE (arg)))
+ {
+ len = c_strlen (arg, 1);
+ if (! len || ! tree_fits_uhwi_p (len))
+ len = NULL_TREE;
+ }
+ }
+ }
+ }
+
+ if (! integer_all_onesp (size))
+ {
+ if (! len || ! tree_int_cst_lt (len, size))
+ return false;
+ }
+
+ /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0
+ or if format doesn't contain % chars or is "%s". */
+ if (! integer_zerop (flag))
+ {
+ if (fmt_str == NULL)
+ return false;
+ if (strchr (fmt_str, target_percent) != NULL
+ && strcmp (fmt_str, target_percent_s))
+ return false;
+ }
+
+ /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available. */
+ fn = builtin_decl_explicit (fcode == BUILT_IN_VSPRINTF_CHK
+ ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF);
+ if (!fn)
+ return false;
+
+ /* Replace the called function and the first 4 argument by 2 retaining
+ trailing varargs. */
+ gimple_call_set_fndecl (stmt, fn);
+ gimple_call_set_fntype (stmt, TREE_TYPE (fn));
+ gimple_call_set_arg (stmt, 0, dest);
+ gimple_call_set_arg (stmt, 1, fmt);
+ for (unsigned i = 2; i < gimple_call_num_args (stmt) - 2; ++i)
+ gimple_call_set_arg (stmt, i, gimple_call_arg (stmt, i + 2));
+ gimple_set_num_ops (stmt, gimple_num_ops (stmt) - 2);
+ fold_stmt (gsi);
+ return true;
+}
+
+
+/* Fold a call to __builtin_strlen with known length LEN. */
+
+static bool
+gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi, tree len)
+{
+ if (!len)
+ {
+ gimple stmt = gsi_stmt (*gsi);
+ len = c_strlen (gimple_call_arg (stmt, 0), 0);
+ }
+ if (!len)
+ return false;
+ replace_call_with_value (gsi, len);
+ return true;
+}
+
+
+/* Fold builtins at *GSI with knowledge about a length argument. */
+
+static bool
+gimple_fold_builtin_with_strlen (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ tree val[3];
+ 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:
@@ -949,11 +2212,12 @@ gimple_fold_builtin (gimple stmt)
type = 2;
break;
default:
- return NULL_TREE;
+ return false;
}
+ int nargs = gimple_call_num_args (stmt);
if (arg_idx >= nargs)
- return NULL_TREE;
+ return false;
/* Try to use the dataflow information gathered by the CCP process. */
visited = BITMAP_ALLOC (NULL);
@@ -961,120 +2225,150 @@ gimple_fold_builtin (gimple stmt)
memset (val, 0, sizeof (val));
a = gimple_call_arg (stmt, arg_idx);
- if (!get_maxval_strlen (a, &val[arg_idx], visited, type))
+ if (!get_maxval_strlen (a, &val[arg_idx], visited, type)
+ || !is_gimple_val (val[arg_idx]))
val[arg_idx] = NULL_TREE;
BITMAP_FREE (visited);
- result = NULL_TREE;
switch (DECL_FUNCTION_CODE (callee))
{
case BUILT_IN_STRLEN:
- if (val[0] && nargs == 1)
- {
- tree new_val =
- fold_convert (TREE_TYPE (gimple_call_lhs (stmt)), val[0]);
-
- /* If the result is not a valid gimple value, or not a cast
- of a valid gimple value, then we cannot use the result. */
- if (is_gimple_val (new_val)
- || (CONVERT_EXPR_P (new_val)
- && is_gimple_val (TREE_OPERAND (new_val, 0))))
- return new_val;
- }
- break;
+ return gimple_fold_builtin_strlen (gsi, val[0]);
case BUILT_IN_STRCPY:
- if (val[1] && is_gimple_val (val[1]) && nargs == 2)
- result = fold_builtin_strcpy (loc, callee,
- gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- val[1]);
- break;
+ return gimple_fold_builtin_strcpy (gsi, loc,
+ gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1),
+ val[1]);
case BUILT_IN_STRNCPY:
- if (val[1] && is_gimple_val (val[1]) && nargs == 3)
- result = fold_builtin_strncpy (loc, callee,
- gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- gimple_call_arg (stmt, 2),
- val[1]);
- break;
+ return gimple_fold_builtin_strncpy (gsi, loc,
+ gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1),
+ gimple_call_arg (stmt, 2),
+ val[1]);
case BUILT_IN_STRCAT:
- if (val[1] && is_gimple_val (val[1]) && nargs == 2)
- result = fold_builtin_strcat (loc, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- val[1]);
- break;
+ return gimple_fold_builtin_strcat (gsi, loc, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1),
+ val[1]);
case BUILT_IN_FPUTS:
- if (nargs == 2)
- result = fold_builtin_fputs (loc, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- ignore, false, val[0]);
- break;
+ return gimple_fold_builtin_fputs (gsi, loc, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1),
+ ignore, false, val[0]);
case BUILT_IN_FPUTS_UNLOCKED:
- if (nargs == 2)
- result = fold_builtin_fputs (loc, gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- ignore, true, val[0]);
- break;
+ return gimple_fold_builtin_fputs (gsi, loc, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1),
+ ignore, true, val[0]);
case BUILT_IN_MEMCPY_CHK:
case BUILT_IN_MEMPCPY_CHK:
case BUILT_IN_MEMMOVE_CHK:
case BUILT_IN_MEMSET_CHK:
- if (val[2] && is_gimple_val (val[2]) && nargs == 4)
- result = fold_builtin_memory_chk (loc, callee,
- 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));
- break;
+ return gimple_fold_builtin_memory_chk (gsi, loc,
+ 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:
- if (val[1] && is_gimple_val (val[1]) && nargs == 3)
- result = fold_builtin_stxcpy_chk (loc, callee,
- gimple_call_arg (stmt, 0),
- gimple_call_arg (stmt, 1),
- gimple_call_arg (stmt, 2),
- val[1], ignore,
- DECL_FUNCTION_CODE (callee));
- break;
+ return gimple_fold_builtin_stxcpy_chk (gsi, loc,
+ 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:
- if (val[2] && is_gimple_val (val[2]) && nargs == 4)
- result = fold_builtin_stxncpy_chk (loc, 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));
- break;
+ return gimple_fold_builtin_stxncpy_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_SNPRINTF_CHK:
case BUILT_IN_VSNPRINTF_CHK:
- if (val[1] && is_gimple_val (val[1]))
- result = gimple_fold_builtin_snprintf_chk (stmt, val[1],
- DECL_FUNCTION_CODE (callee));
- break;
+ return gimple_fold_builtin_snprintf_chk (gsi, val[1],
+ DECL_FUNCTION_CODE (callee));
default:
gcc_unreachable ();
}
- if (result && ignore)
- result = fold_ignored_result (result);
- return result;
+ 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));
+ default:;
+ }
+
+ /* Try the generic builtin folder. */
+ bool ignore = (gimple_call_lhs (stmt) == NULL);
+ tree result = fold_call_stmt (stmt, ignore);
+ if (result)
+ {
+ if (ignore)
+ STRIP_NOPS (result);
+ else
+ result = fold_convert (gimple_call_return_type (stmt), result);
+ if (!update_call_from_tree (gsi, result))
+ gimplify_and_update_call_from_tree (gsi, result);
+ return true;
+ }
+
+ return false;
+}
+
/* Attempt to fold a call statement referenced by the statement iterator GSI.
The statement may be replaced by another statement, e.g., if the call
simplifies to a constant value. Return true if any changes were made.
@@ -1186,16 +2480,13 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
/* Check for builtins that CCP can handle using information not
available in the generic fold routines. */
- if (gimple_call_builtin_p (stmt))
+ if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+ {
+ if (gimple_fold_builtin (gsi))
+ changed = true;
+ }
+ else if (gimple_call_builtin_p (stmt, BUILT_IN_MD))
{
- tree result = gimple_fold_builtin (stmt);
- if (result)
- {
- if (!update_call_from_tree (gsi, result))
- gimplify_and_update_call_from_tree (gsi, result);
- changed = true;
- }
- else if (gimple_call_builtin_p (stmt, BUILT_IN_MD))
changed |= targetm.gimple_fold_builtin (gsi);
}
else if (gimple_call_internal_p (stmt))
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 4a9d379..2104dd6 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -1178,6 +1178,21 @@ gimple_seq_add_seq (gimple_seq *dst_p, gimple_seq src)
gsi_insert_seq_after (&si, src, GSI_NEW_STMT);
}
+/* Append sequence SRC to the end of sequence *DST_P. If *DST_P is
+ NULL, a new sequence is allocated. This function is
+ similar to gimple_seq_add_seq, but does not scan the operands. */
+
+void
+gimple_seq_add_seq_without_update (gimple_seq *dst_p, gimple_seq src)
+{
+ gimple_stmt_iterator si;
+ if (src == NULL)
+ return;
+
+ si = gsi_last (*dst_p);
+ gsi_insert_seq_after_without_update (&si, src, GSI_NEW_STMT);
+}
+
/* Determine whether to assign a location to the statement GS. */
static bool
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 3dfafc6..a517dee 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -1226,6 +1226,7 @@ gimple gimple_build_predict (enum br_predictor, enum prediction);
extern void gimple_seq_add_stmt (gimple_seq *, gimple);
extern void gimple_seq_add_stmt_without_update (gimple_seq *, gimple);
void gimple_seq_add_seq (gimple_seq *, gimple_seq);
+void gimple_seq_add_seq_without_update (gimple_seq *, gimple_seq);
extern void annotate_all_with_location_after (gimple_seq, gimple_stmt_iterator,
location_t);
extern void annotate_all_with_location (gimple_seq, location_t);
diff --git a/gcc/predict.c b/gcc/predict.c
index 835c618..03657c9 100644
--- a/gcc/predict.c
+++ b/gcc/predict.c
@@ -339,7 +339,8 @@ optimize_function_for_speed_p (struct function *fun)
bool
optimize_bb_for_size_p (const_basic_block bb)
{
- return optimize_function_for_size_p (cfun) || !maybe_hot_bb_p (cfun, bb);
+ return (optimize_function_for_size_p (cfun)
+ || (bb && !maybe_hot_bb_p (cfun, bb)));
}
/* Return TRUE when BB should be optimized for speed. */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index d5343f1..bd881d3 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+2014-08-08 Richard Biener <rguenther@suse.de>
+
+ * gcc.dg/strlenopt-8.c: Remove XFAIL.
+ * gcc.dg/tree-prof/stringop-2.c: Adjust.
+ * gfortran.dg/array_memcpy_4.f90: Likewise.
+ * gfortran.dg/trim_optimize_1.f90: Likewise.
+ * gfortran.dg/trim_optimize_2.f90: Likewise.
+
2014-08-08 Kugan Vivekanandarajah <kuganv@linaro.org>
* gcc.dg/zero_sign_ext_test.c: New test.
diff --git a/gcc/testsuite/gcc.dg/strlenopt-8.c b/gcc/testsuite/gcc.dg/strlenopt-8.c
index 3f9ad51..3aaf660 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-8.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-8.c
@@ -43,8 +43,8 @@ main ()
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" { xfail *-*-* } } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" { xfail *-*-* } } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/stringop-2.c b/gcc/testsuite/gcc.dg/tree-prof/stringop-2.c
index f70fafb..47ebfb0 100644
--- a/gcc/testsuite/gcc.dg/tree-prof/stringop-2.c
+++ b/gcc/testsuite/gcc.dg/tree-prof/stringop-2.c
@@ -19,6 +19,6 @@ main()
}
/* { dg-final-use { scan-ipa-dump "Single value 4 stringop" "profile"} } */
/* The versioned memset of size 4 should be optimized to an assignment. */
-/* { dg-final-use { scan-tree-dump "a\\\[0\\\] = 168430090" "optimized"} } */
+/* { dg-final-use { scan-tree-dump "MEM\\\[\\(void .\\)&a\\\] = 168430090" "optimized"} } */
/* { dg-final-use { cleanup-tree-dump "optimized" } } */
/* { dg-final-use { cleanup-ipa-dump "profile" } } */
diff --git a/gcc/testsuite/gfortran.dg/array_memcpy_4.f90 b/gcc/testsuite/gfortran.dg/array_memcpy_4.f90
index 9f2279d..4a794fa 100644
--- a/gcc/testsuite/gfortran.dg/array_memcpy_4.f90
+++ b/gcc/testsuite/gfortran.dg/array_memcpy_4.f90
@@ -9,5 +9,5 @@
d = s
end
-! { dg-final { scan-tree-dump-times "MEM.*d\\\] = MEM" 1 "original" } }
+! { dg-final { scan-tree-dump-times "memcpy" 1 "original" } }
! { dg-final { cleanup-tree-dump "original" } }
diff --git a/gcc/testsuite/gfortran.dg/trim_optimize_1.f90 b/gcc/testsuite/gfortran.dg/trim_optimize_1.f90
index 26aa5cd..e208a50 100644
--- a/gcc/testsuite/gfortran.dg/trim_optimize_1.f90
+++ b/gcc/testsuite/gfortran.dg/trim_optimize_1.f90
@@ -11,6 +11,6 @@ program main
if (c /= 'abc') call abort
end program main
-! { dg-final { scan-tree-dump-times "memmove" 2 "original" } }
+! { dg-final { scan-tree-dump-times "memmove" 3 "original" } }
! { dg-final { scan-tree-dump-times "string_trim" 0 "original" } }
! { dg-final { cleanup-tree-dump "original" } }
diff --git a/gcc/testsuite/gfortran.dg/trim_optimize_2.f90 b/gcc/testsuite/gfortran.dg/trim_optimize_2.f90
index b7ae1e3..2ac36d1 100644
--- a/gcc/testsuite/gfortran.dg/trim_optimize_2.f90
+++ b/gcc/testsuite/gfortran.dg/trim_optimize_2.f90
@@ -32,6 +32,6 @@ contains
end subroutine foo
end program main
-! { dg-final { scan-tree-dump-times "memmove" 4 "original" } }
+! { dg-final { scan-tree-dump-times "memmove" 6 "original" } }
! { dg-final { scan-tree-dump-times "string_trim" 0 "original" } }
! { dg-final { cleanup-tree-dump "original" } }