gimple_fold_builtin_memset currently folds constant-length memset calls into scalar stores only when the destination is an ADDR_EXPR. A length-one memset through an arbitrary pointer destination such as a function parameter is left as a call.
For a length-one memset, byte replication is unnecessary: the stored value is simply the low byte of the fill argument. Extend the fold to handle this case for arbitrary pointer destinations including non-constant fill values. The new path derives an appropriate byte store type and mode using the same mode-selection scheme as gimple_fold_builtin_memory_op. It verifies that the destination alignment is suitable for the selected mode taking targetm.slow_unaligned_access and movmisalign_optab into account. Before replacing the call, run check_bounds_or_overlap so that access-related diagnostics are preserved. The generated MEM_REF store retains qualifiers from the pointed-to type including volatile qualification and uses an aligned type when required. Constant fill values are converted with fold_convert while non-constant values are converted with gimple_convert. The transformation is intentionally restricted to length-one memset calls. Relaxing the ADDR_EXPR restriction for multi-byte memset calls can remove information needed by object-size analysis, -Wstringop-overflow diagnostics and -fanalyzer before those analyses have inspected the original call. Signed-off-by: Naveen <[email protected]> --- gcc/gimple-fold.cc | 77 +++++++++++++++++++++++++++- gcc/testsuite/gcc.dg/pr102202-fold.c | 37 +++++++++++++ 2 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/pr102202-fold.c diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc index fab4886b0f2..703cad2349c 100644 --- a/gcc/gimple-fold.cc +++ b/gcc/gimple-fold.cc @@ -1478,10 +1478,84 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len) if (! tree_fits_uhwi_p (len)) return false; + length = tree_to_uhwi (len); + + tree dest = gimple_call_arg (stmt, 0); + if (length == 1 && POINTER_TYPE_P (TREE_TYPE (dest))) + { + /* 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; + + unsigned int dest_align = get_pointer_alignment (dest); + scalar_int_mode imode; + machine_mode mode; + if (!int_mode_for_size (BITS_PER_UNIT, 0).exists (&imode) + || !bitwise_mode_for_size (BITS_PER_UNIT).exists (&mode) + || !known_eq (GET_MODE_BITSIZE (mode), BITS_PER_UNIT) + /* If the destination pointer is not aligned we must be able to emit + an unaligned store. */ + || (dest_align < GET_MODE_ALIGNMENT (mode) + && targetm.slow_unaligned_access (mode, dest_align) + && (optab_handler (movmisalign_optab, mode) + == CODE_FOR_nothing))) + return false; + + etype = bitwise_type_for_mode (mode); + if (etype == NULL_TREE) + return false; + + if (dest_align < GET_MODE_ALIGNMENT (mode)) + etype = build_aligned_type (etype, dest_align); + + tree ptype = TREE_TYPE (TREE_TYPE (dest)); + if (TYPE_VOLATILE (ptype)) + etype = build_qualified_type (etype, TYPE_QUAL_VOLATILE); + + 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); + + /* 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 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) return false; - tree dest = gimple_call_arg (stmt, 0); tree var = dest; if (TREE_CODE (var) != ADDR_EXPR) return false; @@ -1502,7 +1576,6 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len) if (! var_decl_component_p (var)) return false; - length = tree_to_uhwi (len); if (GET_MODE_SIZE (SCALAR_INT_TYPE_MODE (etype)) != length || (GET_MODE_PRECISION (SCALAR_INT_TYPE_MODE (etype)) != GET_MODE_BITSIZE (SCALAR_INT_TYPE_MODE (etype))) diff --git a/gcc/testsuite/gcc.dg/pr102202-fold.c b/gcc/testsuite/gcc.dg/pr102202-fold.c new file mode 100644 index 00000000000..7bec2320957 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr102202-fold.c @@ -0,0 +1,37 @@ +/* PR tree-optimization/102202 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +void * +g5 (char *d, int c) +{ + return __builtin_memset (d, c, 1); +} + +void +g6 (char *d, int c) +{ + __builtin_memset (d, c, 1); +} + +void * +g7 (int *p, int c) +{ + return __builtin_memset (p, c, 1); +} + +void * +g8 (char *d) +{ + return __builtin_memset (d, 0, 1); +} + +void +g9 (char *d) +{ + __builtin_memset (d, 42, 1); +} + +/* { dg-final { scan-tree-dump-not "__builtin_memset" "optimized" } } */ +/* { dg-final { scan-tree-dump-times {\(unsigned char\) c_[0-9]+\(D\)} 3 "optimized" } } */ +/* { dg-final { scan-tree-dump-times {MEM[^;\n\r]*= _[0-9]+;} 3 "optimized" } } */ -- 2.34.1
