The function requires the fill value to be a compile-time INTEGER_CST before
folding. This causes __builtin_memset (d, c, 1) with a non-constant fill to
remain unfolded. For the length-1 case this restriction is unnecessary since
a single-byte store requires no broadcast of the fill value across a wider
access mode.
The patch adds an early special-case path for length == 1 that replaces the
call with a direct MEM_REF byte store. The fill is constant propagating any
qualifiers from the destination's pointed-to type onto the store. If the call
has an LHS the destination pointer is returned as the result matching memset.
PR tree-optimization/102202
gcc/ChangeLog:
* gimple-fold.cc (gimple_fold_builtin_memset): Hoist tree_to_uhwi (len)
before the INTEGER_CST check.
gcc/testsuite/ChangeLog:
* gcc.dg/pr102202-fold.c: New test.
Signed-off-by: Naveen <[email protected]>
---
gcc/gimple-fold.cc | 117 ++++++++++++++++++---------
gcc/testsuite/gcc.dg/pr102202-fold.c | 25 ++++++
2 files changed, 102 insertions(+), 40 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..bbf407a98aa 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -1463,7 +1463,7 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi,
tree c, tree len)
{
gimple *stmt = gsi_stmt (*gsi);
tree etype;
- unsigned HOST_WIDE_INT length, cval;
+ unsigned HOST_WIDE_INT length;
/* If the LEN parameter is zero, return DEST. */
if (integer_zerop (len))
@@ -1478,67 +1478,104 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi,
tree c, tree len)
if (! tree_fits_uhwi_p (len))
return false;
- if (TREE_CODE (c) != INTEGER_CST)
+ length = tree_to_uhwi (len);
+ bool length_one_p = length == 1;
+
+ if (TREE_CODE (c) != INTEGER_CST && !length_one_p)
return false;
tree dest = gimple_call_arg (stmt, 0);
tree var = dest;
- if (TREE_CODE (var) != ADDR_EXPR)
- return false;
- var = TREE_OPERAND (var, 0);
- if (TREE_THIS_VOLATILE (var))
- return false;
+ if (length_one_p)
+ {
+ if (!POINTER_TYPE_P (TREE_TYPE (dest)))
+ return false;
- etype = TREE_TYPE (var);
- if (TREE_CODE (etype) == ARRAY_TYPE)
- etype = TREE_TYPE (etype);
+ tree ptype = TREE_TYPE (TREE_TYPE (dest));
+ etype = build_qualified_type (unsigned_char_type_node,
+ TYPE_QUALS (ptype));
+ }
+ else
+ {
+ if (TREE_CODE (var) != ADDR_EXPR)
+ return false;
- if ((!INTEGRAL_TYPE_P (etype)
- && !POINTER_TYPE_P (etype))
- || BITINT_TYPE_P (etype))
- return false;
+ var = TREE_OPERAND (var, 0);
+ if (TREE_THIS_VOLATILE (var))
+ return false;
- if (! var_decl_component_p (var))
- return false;
+ etype = TREE_TYPE (var);
+ if (TREE_CODE (etype) == ARRAY_TYPE)
+ etype = TREE_TYPE (etype);
- 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)))
- || get_pointer_alignment (dest) / BITS_PER_UNIT < length)
- return false;
+ if ((!INTEGRAL_TYPE_P (etype)
+ && !POINTER_TYPE_P (etype))
+ || BITINT_TYPE_P (etype))
+ return false;
- if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
- return false;
+ if (! var_decl_component_p (var))
+ return false;
- if (!type_has_mode_precision_p (etype))
- etype = lang_hooks.types.type_for_mode (SCALAR_INT_TYPE_MODE (etype),
- TYPE_UNSIGNED (etype));
+ 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)))
+ || get_pointer_alignment (dest) / BITS_PER_UNIT < length)
+ return false;
+
+ if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
+ return false;
+
+ if (!type_has_mode_precision_p (etype))
+ etype = lang_hooks.types.type_for_mode (SCALAR_INT_TYPE_MODE (etype),
+ TYPE_UNSIGNED (etype));
+ }
+
+ location_t loc = gimple_location (stmt);
+ tree cval_tree;
if (integer_zerop (c))
- cval = 0;
+ cval_tree = build_zero_cst (etype);
else
{
- if (CHAR_BIT != 8 || BITS_PER_UNIT != 8 || HOST_BITS_PER_WIDE_INT > 64)
- return NULL_TREE;
+ if (length_one_p)
+ {
+ /* Non-constant C is valid for length one. Convert it to the byte
+ store type as memset specifies before doing single-byte store. */
+ cval_tree = gimple_convert (gsi, true, GSI_SAME_STMT, loc,
+ etype, c);
+ }
+ else
+ {
+ if (CHAR_BIT != 8 || BITS_PER_UNIT != 8
+ || HOST_BITS_PER_WIDE_INT > 64)
+ return false;
- cval = TREE_INT_CST_LOW (c);
- cval &= 0xff;
- cval |= cval << 8;
- cval |= cval << 16;
- cval |= (cval << 31) << 1;
+ unsigned HOST_WIDE_INT cval = TREE_INT_CST_LOW (c);
+ cval &= 0xff;
+ cval |= cval << 8;
+ cval |= cval << 16;
+ cval |= (cval << 31) << 1;
+
+ cval_tree = build_int_cst_type (etype, cval);
+ }
}
var = fold_build2 (MEM_REF, etype, dest, build_int_cst (ptr_type_node, 0));
- gimple *store = gimple_build_assign (var, build_int_cst_type (etype, cval));
+ gimple *store = gimple_build_assign (var, cval_tree);
gimple_move_vops (store, stmt);
- gimple_set_location (store, gimple_location (stmt));
+ gimple_set_location (store, loc);
gsi_insert_before (gsi, store, GSI_SAME_STMT);
- if (gimple_call_lhs (stmt))
+
+ if (tree lhs = gimple_call_lhs (stmt))
{
- gimple *asgn = gimple_build_assign (gimple_call_lhs (stmt), dest);
- gsi_replace (gsi, asgn, false);
+ 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);
}
else
{
diff --git a/gcc/testsuite/gcc.dg/pr102202-fold.c
b/gcc/testsuite/gcc.dg/pr102202-fold.c
new file mode 100644
index 00000000000..3c1a0ab4fa9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr102202-fold.c
@@ -0,0 +1,25 @@
+/* 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);
+}
+
+/* { 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