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

Reply via email to