On Fri, Jul 3, 2026 at 7:42 AM Naveen <[email protected]> wrote: > > gimple_fold_builtin_memset currently folds constant-length memset calls > into scalar stores only when the destination is an ADDR_EXPR. Calls > through an arbitrary pointer, such as a function parameter are left as > library calls and non-constant fill values are rejected even for the > length-one case where byte replication is unnecessary. > > Add a dedicated folding path for length-one memset calls through > arbitrary pointer destinations. Since the access is exactly one byte, > use unsigned_char_type_node for the store. This supports both constant > and non-constant fill values: constant fills are converted with > fold_convert while non-constant fills use gimple_convert. Volatile > qualification is preserved from the pointed-to type and the store is > emitted as a MEM_REF at offset zero using the usual ref-all character > pointer type. > > Defer this new arbitrary-pointer fold until after object-size analysis > has run so that the original memset remains available to object-size > based diagnostics. The existing ADDR_EXPR-based folding path for other > constant lengths is unchanged.
OK. Thanks, Richard. > gcc/ChangeLog: > PR tree-optimization/102202 > * gimple-fold.cc (gimple_fold_builtin_memset): Hoist > tree_to_uhwi (len) before the INTEGER_CST guard. Fold > length-one memset calls through arbitrary pointer destinations. > Build a ref-all MEM_REF at offset zero and preserve the call > result when it is used. > > gcc/testsuite/ChangeLog: > PR tree-optimization/102202 > * gcc.dg/pr102202-fold.c: New test. > > Signed-off-by: Naveen <[email protected]> > --- > gcc/gimple-fold.cc | 63 +++++++++++++++++++++++++++- > gcc/testsuite/gcc.dg/pr102202-fold.c | 35 ++++++++++++++++ > 2 files changed, 96 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 4422a48383d..fdb9b419766 100644 > --- a/gcc/gimple-fold.cc > +++ b/gcc/gimple-fold.cc > @@ -1478,10 +1478,70 @@ 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))) > + { > + /* Keep the original call until object-size analysis has inspected it. > */ > + if (!(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; > + > + etype = unsigned_char_type_node; > + 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 +1562,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..5e2e58193c9 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/pr102202-fold.c > @@ -0,0 +1,35 @@ > +/* PR tree-optimization/102202 */ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-optimized" } */ > + > +/* A constant-length-one memset is folded to a single byte store, even for a > + store through an arbitrary pointer. */ > + > +void * > +g1 (char *d, int c) > +{ > + return __builtin_memset (d, c, 1); > +} > + > +void > +g2 (char *d, int c) > +{ > + __builtin_memset (d, c, 1); > +} > + > +void * > +g3 (int *p, int c) > +{ > + return __builtin_memset (p, c, 1); > +} > + > +void * > +g4 (char *d) > +{ > + return __builtin_memset (d, 7, 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" > } } */ > +/* { dg-final { scan-tree-dump-times {MEM[^;\n\r]*= 7;} 1 "optimized" } } */ > -- > 2.34.1 >
