The current patch addresses the review feedback by removing the CDCE-specific
entry into the memset folder. Instead the ordinary memset fold now handles
supported small constant-length calls through arbitrary pointer destinations.
The wider fold is deliberately delayed until after the final access-warning
point. This preserves late -Wstringop-overflow diagnostics for complex
pointer expressions such as PHI/MIN/MAX cases fixing the regressions seen in
Wstringop-overflow-58.c, Wstringop-overflow-59.c and Wstringop-overflow-62.c.
The fold is limited to constant lengths no larger than MOVE_MAX requires an
exact integer/bitwise mode, checks target alignment support and uses
check_bounds_or_overlap before replacing the call. Existing length-one
behavior after object-size analysis is preserved.
Bootstrapped and regtested on X86-64 and Aarch64.
gcc/ChangeLog:
PR tree-optimization/102202
* gimple-fold.cc (gimple_fold_builtin_memset): Fold supported
constant-length memset calls through arbitrary pointer destinations.
Defer wider folds until after the final access-warning point.
* tree-cfgcleanup.cc (execute_cleanup_cfg_post_optimizing): Set
PROP_warn_access_done and sweep surviving BUILT_IN_MEMSET calls.
* tree-pass.h (PROP_warn_access_done): New pass property.
gcc/testsuite/ChangeLog:
PR tree-optimization/102202
* gcc.dg/pr102202-fold-warn.c: New test.
* gcc.dg/pr102202-fold-zero-n.c: New test.
* gcc.dg/pr86010-2.c: Accept an inline scalar store.
* gcc.dg/pr86010.c: Likewise.
* gcc.dg/tree-ssa/calloc-5.c: Likewise.
* gcc.target/aarch64/pr102202-fold-no-mode.c: New test
Signed-off-by: Naveen <[email protected]>
---
gcc/gimple-fold.cc | 170 +++++++++++++-----
gcc/testsuite/gcc.dg/pr102202-fold-warn.c | 13 ++
gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c | 55 ++++++
gcc/testsuite/gcc.dg/pr86010-2.c | 2 +-
gcc/testsuite/gcc.dg/pr86010.c | 2 +-
gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c | 8 +-
.../aarch64/pr102202-fold-no-mode.c | 12 ++
gcc/tree-cfgcleanup.cc | 26 +++
gcc/tree-pass.h | 1 +
9 files changed, 236 insertions(+), 53 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/pr102202-fold-warn.c
create mode 100644 gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/pr102202-fold-no-mode.c
diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 24041466108..5c04b7b7566 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -1481,62 +1481,138 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi,
tree c, tree len)
length = tree_to_uhwi (len);
tree dest = gimple_call_arg (stmt, 0);
- if (length == 1
- && POINTER_TYPE_P (TREE_TYPE (dest)))
+ if (POINTER_TYPE_P (TREE_TYPE (dest)))
{
- /* Keep the original call until object-size analysis has inspected it.
*/
- if (!(cfun->curr_properties & PROP_objsz))
+ /* Preserve existing length-one timing after object-size analysis.
+ Defer wider arbitrary-pointer folds until after final access warnings
so
+ -Wstringop-overflow can still diagnose complex pointer expressions. */
+ if (length == 1 && !(cfun->curr_properties & PROP_objsz))
return false;
- /* Detect out-of-bounds accesses without issuing warnings.
- Avoid folding out-of-bounds accesses but to avoid false
- positives for unreachable code defer warning until after
- DCE has worked its magic.
- -Wrestrict is still diagnosed. */
- if (int warning = check_bounds_or_overlap (as_a <gcall *>(stmt),
- dest, NULL_TREE, len,
- NULL_TREE, false, false))
- if (warning != OPT_Wrestrict)
- return false;
+ if (cfun->curr_properties & PROP_objsz)
+ {
+ unsigned int dest_align = get_pointer_alignment (dest);
+ machine_mode mode = VOIDmode;
+ etype = NULL_TREE;
- etype = unsigned_char_type_node;
- tree ptype = TREE_TYPE (TREE_TYPE (dest));
- if (TYPE_VOLATILE (ptype))
- etype = build_qualified_type (etype, TYPE_QUAL_VOLATILE);
+ /* Preserve the established length-one timing after object-size
+ analysis. Only the new wider fold needs delaying for late
+ range-based diagnostics. */
+ if (length == 1)
+ etype = unsigned_char_type_node;
+ else if ((cfun->curr_properties & PROP_warn_access_done)
+ && length <= MOVE_MAX
+ && length <= HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
+ {
+ unsigned HOST_WIDE_INT bits = length * BITS_PER_UNIT;
+ if (int_mode_for_size (bits, 0).exists ()
+ && bitwise_mode_for_size (bits).exists (&mode)
+ && known_eq (GET_MODE_BITSIZE (mode), bits)
+ /* If DEST is not naturally aligned, require an efficient
+ unaligned store or a movmisalign pattern. */
+ && (dest_align >= GET_MODE_ALIGNMENT (mode)
+ || !targetm.slow_unaligned_access (mode, dest_align)
+ || (optab_handler (movmisalign_optab, mode)
+ != CODE_FOR_nothing)))
+ {
+ etype = bitwise_type_for_mode (mode);
+ if (etype && !INTEGRAL_TYPE_P (etype))
+ etype = NULL_TREE;
+ if (etype && dest_align < GET_MODE_ALIGNMENT (mode))
+ etype = build_aligned_type (etype, dest_align);
+ }
+ }
- location_t loc = gimple_location (stmt);
- tree cval_tree;
- if (TREE_CODE (c) == INTEGER_CST)
- cval_tree = fold_convert (etype, c);
- else
- cval_tree = gimple_convert (gsi, true, GSI_SAME_STMT, loc, etype, c);
+ if (etype)
+ {
+ /* Avoid folding accesses that still need diagnostics. Emit no
+ warnings here; unreachable-code pruning and the warning passes
+ handle them later while -Wrestrict remains diagnosed. */
+ if (int warning = check_bounds_or_overlap (as_a <gcall *>(stmt),
+ dest, NULL_TREE, len,
+ NULL_TREE, false, false))
+ if (warning != OPT_Wrestrict)
+ return false;
- /* Build accesses at offset zero with a ref-all character type. */
- tree off0
- = build_int_cst (build_pointer_type_for_mode (char_type_node,
- ptr_mode, true), 0);
- tree var = fold_build2_loc (loc, MEM_REF, etype, dest, off0);
- gimple *store = gimple_build_assign (var, cval_tree);
- gimple_move_vops (store, stmt);
- gimple_set_location (store, loc);
- copy_warning (store, stmt);
+ tree ptype = TREE_TYPE (TREE_TYPE (dest));
+ if (TYPE_VOLATILE (ptype))
+ etype = build_qualified_type (etype, TYPE_QUAL_VOLATILE);
- tree lhs = gimple_call_lhs (stmt);
- if (!lhs)
- {
- gsi_replace (gsi, store, false);
- return true;
- }
+ location_t loc = gimple_location (stmt);
+ tree cval_tree;
+ if (length == 1)
+ {
+ if (TREE_CODE (c) == INTEGER_CST)
+ cval_tree = fold_convert (etype, c);
+ else
+ cval_tree = gimple_convert (gsi, true, GSI_SAME_STMT, loc,
+ etype, c);
+ }
+ else
+ {
+ tree value_type = TYPE_MAIN_VARIANT (etype);
+ tree byte;
+ if (TREE_CODE (c) == INTEGER_CST)
+ byte = fold_convert (unsigned_char_type_node, c);
+ else
+ byte = gimple_convert (gsi, true, GSI_SAME_STMT, loc,
+ unsigned_char_type_node, c);
- gsi_insert_before (gsi, store, GSI_SAME_STMT);
- tree ret = dest;
- if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (dest)))
- ret = gimple_convert (gsi, true, GSI_SAME_STMT, loc,
- TREE_TYPE (lhs), dest);
- gimple *asgn = gimple_build_assign (lhs, ret);
- gsi_replace (gsi, asgn, false);
+ tree byte_value;
+ if (TREE_CODE (byte) == INTEGER_CST)
+ byte_value = fold_convert (value_type, byte);
+ else
+ byte_value = gimple_convert (gsi, true, GSI_SAME_STMT, loc,
+ value_type, byte);
+
+ unsigned HOST_WIDE_INT multiplier = 1;
+ for (unsigned HOST_WIDE_INT i = 1; i < length; ++i)
+ multiplier = (multiplier << BITS_PER_UNIT) | 1;
+ tree multiplier_tree
+ = build_int_cst_type (value_type, multiplier);
+ if (TREE_CODE (byte_value) == INTEGER_CST)
+ cval_tree = fold_build2_loc (loc, MULT_EXPR, value_type,
+ byte_value, multiplier_tree);
+ else
+ cval_tree = gimple_build (gsi, true, GSI_SAME_STMT, loc,
+ MULT_EXPR, value_type, byte_value,
+ multiplier_tree);
+ if (!useless_type_conversion_p (etype,
+ TREE_TYPE (cval_tree)))
+ cval_tree = gimple_convert (gsi, true, GSI_SAME_STMT, loc,
+ etype, cval_tree);
+ }
- return true;
+ /* Build a store at offset zero with the selected scalar type. */
+ tree off0
+ = build_int_cst
+ (build_pointer_type_for_mode (char_type_node,
+ ptr_mode, true), 0);
+ tree var = fold_build2_loc (loc, MEM_REF, etype, dest, off0);
+ gimple *store = gimple_build_assign (var, cval_tree);
+ gimple_move_vops (store, stmt);
+ gimple_set_location (store, loc);
+ copy_warning (store, stmt);
+
+ tree lhs = gimple_call_lhs (stmt);
+ if (!lhs)
+ {
+ gsi_replace (gsi, store, false);
+ return true;
+ }
+
+ gsi_insert_before (gsi, store, GSI_SAME_STMT);
+ tree ret = dest;
+ if (!useless_type_conversion_p (TREE_TYPE (lhs),
+ TREE_TYPE (dest)))
+ ret = gimple_convert (gsi, true, GSI_SAME_STMT, loc,
+ TREE_TYPE (lhs), dest);
+ gimple *asgn = gimple_build_assign (lhs, ret);
+ gsi_replace (gsi, asgn, false);
+
+ return true;
+ }
+ }
}
if (TREE_CODE (c) != INTEGER_CST)
diff --git a/gcc/testsuite/gcc.dg/pr102202-fold-warn.c
b/gcc/testsuite/gcc.dg/pr102202-fold-warn.c
new file mode 100644
index 00000000000..9bfe566c9f0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr102202-fold-warn.c
@@ -0,0 +1,13 @@
+/* PR tree-optimization/102202 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wstringop-overflow=2" } */
+
+extern void sink (void *);
+
+void
+f (int c)
+{
+ void *p = __builtin_malloc (1);
+ __builtin_memset (p, c, 2); /* { dg-warning "writing 2 bytes into a region
of size 1" } */
+ sink (p);
+}
diff --git a/gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c
b/gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c
new file mode 100644
index 00000000000..275cc0bd034
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr102202-fold-zero-n.c
@@ -0,0 +1,55 @@
+/* PR tree-optimization/102202 */
+/* { dg-do compile } */
+/* { dg-require-effective-target int32plus } */
+/* { dg-options "-O2 -fdump-tree-cdce-details -fdump-tree-optimized" } */
+
+/* CDCE turns the exact range {0, 2} into a zero-length bypass and a call
+ with constant length two. The latter should then fold to one scalar
+ store even though the destination is an arbitrary pointer. */
+
+void
+g1 (unsigned int n, int c, unsigned short *d)
+{
+ __SIZE_TYPE__ len = (n & 1) ? 2 : 0;
+ __builtin_memset (d, c, len);
+}
+
+void *
+g2 (unsigned int n, int c, unsigned short *d)
+{
+ __SIZE_TYPE__ len = (n & 1) ? 2 : 0;
+ return __builtin_memset (d, c, len);
+}
+
+void
+g3 (unsigned int n, unsigned short *d)
+{
+ __SIZE_TYPE__ len = (n & 1) ? 2 : 0;
+ __builtin_memset (d, 7, len);
+}
+
+/* Ordinary constant-length calls use the same fold. */
+
+void
+g4 (int c, unsigned short *d)
+{
+ __builtin_memset (d, c, 2);
+}
+
+void *
+g5 (int c, unsigned short *d)
+{
+ return __builtin_memset (d, c, 2);
+}
+
+void
+g6 (unsigned short *d)
+{
+ __builtin_memset (d, 7, 2);
+}
+
+/* Only the first three calls have exact {0, 2} lengths. */
+/* { dg-final { scan-tree-dump-times "function call is shrink-wrapped into
error conditions" 3 "cdce" } } */
+
+/* Both CDCE-produced and ordinary constant-length calls are scalarized. */
+/* { dg-final { scan-tree-dump-not "__builtin_memset" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/pr86010-2.c b/gcc/testsuite/gcc.dg/pr86010-2.c
index 4c82e65aeb5..4da26ea64b8 100644
--- a/gcc/testsuite/gcc.dg/pr86010-2.c
+++ b/gcc/testsuite/gcc.dg/pr86010-2.c
@@ -19,4 +19,4 @@ void h (char *a)
f (a);
}
-/* { dg-final { scan-tree-dump-times "__builtin_memset" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times {__builtin_memset|MEM[^;\n\r]*=} 2
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/pr86010.c b/gcc/testsuite/gcc.dg/pr86010.c
index ac279893268..5af32f4adb8 100644
--- a/gcc/testsuite/gcc.dg/pr86010.c
+++ b/gcc/testsuite/gcc.dg/pr86010.c
@@ -21,4 +21,4 @@ void h (void)
f (a);
}
-/* { dg-final { scan-tree-dump-times "__builtin_memset" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times {__builtin_memset|MEM[^;\n\r]*=} 2
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c
b/gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c
index 3d3e5a14542..9c4dbe9d4db 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c
@@ -1,8 +1,8 @@
/* PR tree-optimization/83821 - local aggregate initialization defeats
strlen optimization
- Verify that with DSE disabled, a memset() call to zero out a subregion
- of memory allocated by calloc() is not eliminated after a non-zero byte
- is written into it using memset() in between the two calls.
+ Verify that with DSE disabled, a write that zeroes a subregion of memory
+ allocated by calloc() is not eliminated after a nonzero value is written
+ into it between the two writes.
{ dg-do compile }
{ dg-options "-O2 -fno-tree-dse -fdump-tree-optimized" } */
@@ -19,4 +19,4 @@ char* keep_memset_calls (void)
/* { dg-final { scan-tree-dump-not "malloc" "optimized" } }
{ dg-final { scan-tree-dump-times "_calloc \\\(" 1 "optimized" } }
- { dg-final { scan-tree-dump-times "_memset \\\(" 2 "optimized" } } */
+ { dg-final { scan-tree-dump-times {__builtin_memset|MEM[^;\n\r]*=} 2
"optimized" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/pr102202-fold-no-mode.c
b/gcc/testsuite/gcc.target/aarch64/pr102202-fold-no-mode.c
new file mode 100644
index 00000000000..ad66c66cabc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr102202-fold-no-mode.c
@@ -0,0 +1,12 @@
+/* PR tree-optimization/102202 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+void
+f (int c, unsigned char *d)
+{
+ __builtin_memset (d, c, 3);
+}
+
+/* AArch64 has no three-byte integer mode, so keep the call. */
+/* { dg-final { scan-tree-dump-times "__builtin_memset" 1 "optimized" } } */
diff --git a/gcc/tree-cfgcleanup.cc b/gcc/tree-cfgcleanup.cc
index 36a3d02f32f..33e77d45aca 100644
--- a/gcc/tree-cfgcleanup.cc
+++ b/gcc/tree-cfgcleanup.cc
@@ -1445,6 +1445,32 @@ execute_cleanup_cfg_post_optimizing (void)
has already happened. */
todo &= ~TODO_cleanup_cfg;
+ cfun->curr_properties |= PROP_warn_access_done;
+
+ /* Fold any remaining constant-length memset calls that were deferred past
+ the final access-warning point to preserve -Wstringop-overflow
+ diagnostics for complex pointer expressions. */
+ if (cfun->curr_properties & PROP_objsz)
+ {
+ basic_block fbb;
+ FOR_EACH_BB_FN (fbb, cfun)
+ {
+ for (gimple_stmt_iterator fgsi = gsi_start_bb (fbb);
+ !gsi_end_p (fgsi); )
+ {
+ if (gimple_call_builtin_p (gsi_stmt (fgsi), BUILT_IN_MEMSET))
+ {
+ if (fold_stmt (&fgsi))
+ {
+ todo |= TODO_update_ssa;
+ continue;
+ }
+ }
+ gsi_next (&fgsi);
+ }
+ }
+ }
+
basic_block bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (bb);
/* If the first (and only) bb and the only non debug
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a3b35e009e0..07cd386df76 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -231,6 +231,7 @@ protected:
around. */
#define PROP_gimple_lbitint (1 << 20) /* lowered large _BitInt */
#define PROP_last_full_fold (1 << 21) /* Start of last forwprop. */
+#define PROP_warn_access_done (1 << 22) /* Final access warning point
done. */
#define PROP_gimple \
(PROP_gimple_any | PROP_gimple_lcf | PROP_gimple_leh | PROP_gimple_lomp)
--
2.34.1