return true;
}
+/* Simplify mempcpy call stmt at GSI, returning true if simplified.
+ Currently only handling mempcpy -> memcpy when the return value
+ is ignored. */
+
+static bool
+gimple_fold_builtin_mempcpy (gimple_stmt_iterator *gsi)
+{
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+
+ if (gimple_call_lhs (stmt) != NULL_TREE)
+ return false;
+
+ tree fn = builtin_decl_explicit (BUILT_IN_MEMCPY);
+ if (!fn)
+ return false;
+
+ tree dest = gimple_call_arg (stmt, 0);
+ tree src = gimple_call_arg (stmt, 1);
+ tree n = gimple_call_arg (stmt, 2);
+
+ gcall *repl = gimple_build_call (fn, 3, dest, src, n);
+ 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
return gimple_fold_builtin_memset (gsi,
gimple_call_arg (stmt, 1),
gimple_call_arg (stmt, 2));
- case BUILT_IN_MEMCPY:
case BUILT_IN_MEMPCPY:
+ if (gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
+ gimple_call_arg (stmt, 1), fcode))
+ return true;
+ return gimple_fold_builtin_mempcpy (gsi);
+ case BUILT_IN_MEMCPY:
case BUILT_IN_MEMMOVE:
return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
gimple_call_arg (stmt, 1), fcode);
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fdump-tree-lower" } */
+
+/* Basic MRE from bug report */
+void test_bare (void *d, const void *s, __SIZE_TYPE__ n) {
+ __builtin_mempcpy (d, s, n);
+}
+
+/* { dg-final { scan-tree-dump "__builtin_memcpy" "lower" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-optimized" } */
+
+/* Indirectly unused result */
+void test_unused_indirect (void *d, const void *s, __SIZE_TYPE__ n) {
+ void *a = __builtin_mempcpy (d, s, n);
+ void *b = a;
+}
+
+/* Simple used result (in statement) */
+void *test_used_simple (void *d, const void *s, __SIZE_TYPE__ n) {
+ return __builtin_mempcpy (d, s, n);
+}
+
+/* More complicated used result (in expression) */
+__SIZE_TYPE__ test_used_in_expr (char *d, const char *s, __SIZE_TYPE__ n) {
+ return (char *)__builtin_mempcpy (d, s, n) - d;
+}
+
+/* Unused in all paths */
+void *test_unused_indirect2 (void *d, const void *s, __SIZE_TYPE__ n) {
+ void *a = __builtin_mempcpy (d, s, n);
+ if (n > 20) {
+ return (void *)20;
+ }
+ return (void *)7;
+}
+
+/* Used in at least one path */
+void *test_maybe_used (void *d, const void *s, __SIZE_TYPE__ n) {
+ void *a = __builtin_mempcpy (d, s, n);
+ if (n > 20) {
+ return a;
+ }
+ return (void *)0;
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_memcpy" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_mempcpy" 3 "optimized" } } */
void test_mempcpy (void)
{
- mempcpy (d, s, range ()); /* { dg-warning ".mempcpy. writing 4 or more bytes into a region of size 3 overflows the destination" } */
+ mempcpy (d, s, range ()); /* { dg-warning ".memcpy. writing 4 or more bytes into a region of size 3 overflows the destination" } */
}
void test_memmove (void)
--- /dev/null
+/* PR middle-end/79223 - missing -Wstringop-overflow on a memmove overflow
+ { dg-do compile }
+ { dg-additional-options "-O2 -Wall -Wno-array-bounds -std=gnu99" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void*, const void*, size_t);
+extern void* mempcpy (void*, const void*, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+char d[3];
+char s[4];
+void *sink;
+
+size_t range (void)
+{
+ extern size_t size ();
+ size_t n = size ();
+ if (n <= sizeof d)
+ return sizeof d + 1;
+
+ return n;
+}
+
+void test_memcpy (void)
+{
+ memcpy (d, s, range ()); /* { dg-warning ".memcpy. writing 4 or more bytes into a region of size 3 overflows the destination" } */
+}
+
+void test_mempcpy (void)
+{
+ sink = mempcpy (d, s, range ()); /* { dg-warning ".mempcpy. writing 4 or more bytes into a region of size 3 overflows the destination" } */
+}
+
+void test_memmove (void)
+{
+ memmove (d + 1, d, range ()); /* { dg-warning ".memmove. writing 4 or more bytes into a region of size 2 overflows the destination" } */
+}
--- /dev/null
+/* { dg-options "-O2 -fdump-ipa-profile-optimized -mtune=core2" } */
+/* { dg-skip-if "" { ! { i?86-*-* x86_64-*-* } } } */
+
+char *buffer1;
+char *buffer2;
+
+/* Bzero is not tested because it gets transformed into memset. */
+
+#define DEFINE_TEST(N) \
+__attribute__((noinline)) \
+void memcpy_test_ ## N (int len) \
+{ \
+ __builtin_memcpy (buffer1, buffer2, len); \
+} \
+ \
+__attribute__((noinline)) \
+void mempcpy_test_ ## N (int len) \
+{ \
+ __builtin_mempcpy (buffer1, buffer2, len); \
+} \
+ \
+__attribute__((noinline)) \
+void memset_test_ ## N (int len) \
+{ \
+ __builtin_memset (buffer1, 'c', len); \
+} \
+__attribute__((noinline)) \
+void memmove_test_ ## N (int len) \
+{ \
+ __builtin_memmove (buffer1, buffer2, len); \
+} \
+ \
+void test_stringops_ ## N(int len) \
+{ \
+ memcpy_test_## N (len); \
+ mempcpy_test_ ## N (len); \
+ memset_test_ ## N (len); \
+ memmove_test_ ## N (len); \
+} \
+ \
+void test_stringops_with_values_ ## N (int common, int not_common) \
+{ \
+ int i; \
+ for (i = 0; i < 1000; i++) \
+ { \
+ if (i > 990) \
+ test_stringops_ ## N (not_common); \
+ else \
+ test_stringops_ ## N (common); \
+ } \
+}
+
+DEFINE_TEST(0);
+DEFINE_TEST(1);
+DEFINE_TEST(2);
+
+int main() {
+ buffer1 = __builtin_malloc (1000);
+ buffer2 = __builtin_malloc (1000);
+
+ test_stringops_with_values_0 (8, 55);
+ test_stringops_with_values_1 (55, 55);
+ test_stringops_with_values_2 (257, 55);
+
+ return 0;
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Transformation done: single value 8 stringop for BUILT_IN_MEMCPY" "profile" } } */
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Transformation done: single value 55 stringop for BUILT_IN_MEMCPY" "profile" } } */
+/* { dg-final-use-not-autofdo { scan-ipa-dump-times "Transformation done: single value 32 stringop for BUILT_IN_MEMCPY" 0 "profile" } } */
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Transformation done: single value 8 stringop for BUILT_IN_MEMCPY" "profile" } } */
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Transformation done: single value 55 stringop for BUILT_IN_MEMCPY" "profile" } } */
+/* { dg-final-use-not-autofdo { scan-ipa-dump-times "Transformation done: single value 32 stringop for BUILT_IN_MEMPCPY" 0 "profile" } } */
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Transformation done: single value 8 stringop for BUILT_IN_MEMSET" "profile" } } */
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Transformation done: single value 55 stringop for BUILT_IN_MEMSET" "profile" } } */
+/* { dg-final-use-not-autofdo { scan-ipa-dump-times "Transformation done: single value 32 stringop for BUILT_IN_MEMSET" 0 "profile" } } */
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Transformation done: single value 8 stringop for BUILT_IN_MEMMOVE" "profile" } } */
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Transformation done: single value 55 stringop for BUILT_IN_MEMMOVE" "profile" } } */
+/* { dg-final-use-not-autofdo { scan-ipa-dump-times "Transformation done: single value 32 stringop for BUILT_IN_MEMMOVE" 0 "profile" } } */
__attribute__((noinline)) \
void mempcpy_test_ ## N (int len) \
{ \
- __builtin_mempcpy (buffer1, buffer2, len); \
+ void * volatile res = __builtin_mempcpy (buffer1, buffer2, len); \
} \
\
__attribute__((noinline)) \
AR (mempcpy, short, 2, SHRT_MAX, 1);
AR (mempcpy, int, 2, INT_MAX, 1);
-/* { dg-final { scan-tree-dump-times "builtin_memcpy" 8 "optimized" } }
- { dg-final { scan-tree-dump-times "builtin_memmove" 2 "optimized" } }
- { dg-final { scan-tree-dump-times "builtin_mempcpy" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "builtin_memcpy" 10 "optimized" } }
+ { dg-final { scan-tree-dump-times "builtin_memmove" 2 "optimized" } } */
--- /dev/null
+/* PR 81908 - FAIL: gfortran.dg/alloc_comp_auto_array_2.f90 -O3 -g -m32
+ Test to verify that calls to memcpy et al. where the size is in a range
+ with more than one valid value are not eliminated (this test complements
+ builtins-folding-gimple-2.c).
+ { dg-do compile }
+ { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+
+#define SHRT_MAX __SHRT_MAX__
+#define SHRT_MIN (-SHRT_MAX - 1)
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-INT_MAX - 1)
+
+#define UNIQUE_FUNCNAME(func, line) test_ ## func ## _ ## line
+#define FUNCNAME(func, line) UNIQUE_FUNCNAME (func, line)
+
+void *sink;
+
+#define AR(func, type, min, max, val) \
+ void __attribute__ ((noclone, noinline)) \
+ FUNCNAME (func, __LINE__) (char *d, const char *s, type n) \
+ { \
+ if ((type)min <= n && n <= (type)max) \
+ n = val; \
+ sink = __builtin_ ## func (d, s, n); \
+ } typedef void DummyType
+
+AR (memcpy, short, SHRT_MIN, 0, 1);
+AR (memcpy, short, SHRT_MIN, 1, 2);
+AR (memcpy, short, 2, SHRT_MAX, 1);
+
+AR (memcpy, int, INT_MIN, 0, 1);
+AR (memcpy, int, INT_MIN, 1, 2);
+AR (memcpy, int, INT_MIN, 2, 3);
+AR (memcpy, int, 2, INT_MAX, 1);
+AR (memcpy, int, 2, INT_MAX, 1);
+
+AR (memmove, short, 2, SHRT_MAX, 1);
+AR (memmove, int, 2, INT_MAX, 1);
+
+AR (mempcpy, short, 2, SHRT_MAX, 1);
+AR (mempcpy, int, 2, INT_MAX, 1);
+
+/* { dg-final { scan-tree-dump-times "builtin_memcpy" 8 "optimized" } }
+ { dg-final { scan-tree-dump-times "builtin_memmove" 2 "optimized" } }
+ { dg-final { scan-tree-dump-times "builtin_mempcpy" 2 "optimized" } } */