[PATCH v2] tree-optimization: Fold CDCE {0, N} memset through arbitrary pointers [PR102202]
Naveen <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
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