On Mon, Jun 22, 2026 at 11:37 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. 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;
Given you special-cased the length == 1 case (not sure why we're here
again...), alignment shouldn't be of concern nor existence of the
integer mode which is just QImode and the type would be
unsigned_char_type_node.
That said - the objsz consideration for multi-byte memset transforms
can be mitigated by checking for cfun->curr_properties & PROP_objsz,
possibly in general, since I don't see how this is not an issue for
length == 1 or non-pointer accesses (we just lack test coverage?)
> +
> + 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
>