2009-03-18 Jakub Jelinek * builtins.c (fold_builtin_memory_op): Optimize memmove into memcpy if we can prove source and destination don't overlap. * gcc.dg/memmove-2.c: New test. * gcc.dg/memmove-3.c: New test. --- gcc/builtins.c.jj 2009-03-04 20:06:31.000000000 +0100 +++ gcc/builtins.c 2009-03-18 18:19:28.000000000 +0100 @@ -8882,17 +8882,74 @@ fold_builtin_memory_op (tree dest, tree really mandatory? If either SRC is readonly or length is 1, we can use memcpy. */ - if (dest_align && src_align - && (readonly_data_expr (src) - || (host_integerp (len, 1) - && (MIN (src_align, dest_align) / BITS_PER_UNIT >= - tree_low_cst (len, 1))))) + if (!dest_align || !src_align) + return NULL_TREE; + if (readonly_data_expr (src) + || (host_integerp (len, 1) + && (MIN (src_align, dest_align) / BITS_PER_UNIT >= + tree_low_cst (len, 1)))) { tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; if (!fn) return NULL_TREE; return build_call_expr (fn, 3, dest, src, len); } + + /* If *src and *dest can't overlap, optimize into memcpy as well. */ + srcvar = build_fold_indirect_ref (src); + destvar = build_fold_indirect_ref (dest); + if (srcvar && !TREE_THIS_VOLATILE (srcvar) + && destvar && !TREE_THIS_VOLATILE (destvar)) + { + 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; + + src_base = srcvar; + if (handled_component_p (src_base)) + src_base = get_ref_base_and_extent (src_base, &src_offset, + &size, &maxsize); + dest_base = destvar; + if (handled_component_p (dest_base)) + dest_base = get_ref_base_and_extent (dest_base, &dest_offset, + &size, &maxsize); + if (host_integerp (len, 1)) + { + maxsize = tree_low_cst (len, 1); + if (maxsize + > INTTYPE_MAXIMUM (HOST_WIDE_INT) / BITS_PER_UNIT) + maxsize = -1; + else + maxsize *= BITS_PER_UNIT; + } + else + maxsize = -1; + 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) == INDIRECT_REF + && TREE_CODE (dest_base) == INDIRECT_REF) + { + if (! operand_equal_p (TREE_OPERAND (src_base, 0), + TREE_OPERAND (dest_base, 0), 0) + || ranges_overlap_p (src_offset, maxsize, + dest_offset, maxsize)) + return NULL_TREE; + } + else + return NULL_TREE; + + fn = implicit_built_in_decls[BUILT_IN_MEMCPY]; + if (!fn) + return NULL_TREE; + return build_call_expr (fn, 3, dest, src, len); + } return NULL_TREE; } --- gcc/testsuite/gcc.dg/memmove-2.c.jj 2009-03-18 18:30:17.000000000 +0100 +++ gcc/testsuite/gcc.dg/memmove-2.c 2009-03-18 18:30:49.000000000 +0100 @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times "memmove" 0 "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ + +char a[40]; +extern void bar (char *); + +void +foo (void) +{ + char b[10]; + __builtin_memmove (&a[0], &a[20], 20); + __builtin_memmove (&b[1], &a[25], 9); + bar (b); +} --- gcc/testsuite/gcc.dg/memmove-3.c.jj 2009-03-18 18:30:19.000000000 +0100 +++ gcc/testsuite/gcc.dg/memmove-3.c 2009-03-18 18:31:01.000000000 +0100 @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times "memmove" 3 "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ + +char a[40]; +struct A { char a[30]; }; + +void +foo (struct A *p, char *q, char *r) +{ + char b[10]; + __builtin_memmove (&a[1], &a[19], 20); + __builtin_memmove (&p->a[1], &p->a[9], 10); + __builtin_memmove (q, r, 9); +}