This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[PATCH] Optimize sprintf into strcpy if possible


Hi!

This patch optimizes sprintf (x, "%s", y) into strcpy (x, y)
and also sprintf (x, "foowithoutpercentcharacter") into
strcpy (x, "foo....").
While writing the testcase, I noticed format checker would not
check some format arguments which c_getstr handles, so I've added
them in.
Bootstrapped on i386-redhat-linux, no regressions.
Ok for mainline?

2001-04-06  Jakub Jelinek  <jakub@redhat.com>

	* builtins.def (BUILT_IN_SPRINTF): Add.
	* c-common.c (c_expand_builtin_sprintf): New.
	(c_common_nodes_and_builtins): Set sprintf_ftype.
	Add __builtin_sprintf and sprintf builtins.
	(c_expand_builtin): Call c_expand_builtin_sprintf.
	* builtins.c (c_strlen, c_getstr): Make non-static.
	Don't cast TREE_INT_CST_LOW to int without checking
	if it does not have upper bits set.
	* expr.h (c_strlen, c_getstr): Add prototypes.
	* c-format.c (check_format_info_recurse): Handle
	PLUS_EXPR for format string.

	* gcc.c-torture/execute/string-opt-13.c: New test.

--- gcc/builtins.def.jj	Fri Mar 30 11:44:42 2001
+++ gcc/builtins.def	Fri Apr  6 18:38:26 2001
@@ -80,6 +80,7 @@ DEF_BUILTIN(BUILT_IN_FPUTC)
 DEF_BUILTIN(BUILT_IN_FPUTS)
 DEF_BUILTIN(BUILT_IN_FWRITE)
 DEF_BUILTIN(BUILT_IN_FPRINTF)
+DEF_BUILTIN(BUILT_IN_SPRINTF)
 
   /* ISO C99 floating point unordered comparisons.  */
 DEF_BUILTIN(BUILT_IN_ISGREATER)
--- gcc/c-common.c.jj	Fri Mar 30 11:44:42 2001
+++ gcc/c-common.c	Fri Apr  6 19:22:18 2001
@@ -1119,6 +1119,8 @@ static rtx c_expand_builtin_printf PARAM
 					    enum expand_modifier, int));
 static rtx c_expand_builtin_fprintf PARAMS ((tree, rtx, enum machine_mode,
 					     enum expand_modifier, int));
+static rtx c_expand_builtin_sprintf PARAMS ((tree, enum expand_modifier,
+					     int));
 
 /* Print a warning if a constant expression had overflow in folding.
    Invoke this function on every expression that the language
@@ -2718,9 +2720,10 @@ c_common_nodes_and_builtins ()
   tree memcpy_ftype, memset_ftype, strlen_ftype;
   tree bzero_ftype, bcmp_ftype, puts_ftype, printf_ftype;
   tree fputs_ftype, fputc_ftype, fwrite_ftype, fprintf_ftype;
+  tree sprintf_ftype;
   tree endlink, int_endlink, double_endlink, unsigned_endlink;
   tree cstring_endlink, sizetype_endlink;
-  tree ptr_ftype, ptr_ftype_unsigned;
+  tree ptr_ftype, ptr_ftype_unsigned, printf_endlink;
   tree void_ftype_any, void_ftype_int, int_ftype_any;
   tree double_ftype_double, double_ftype_double_double;
   tree float_ftype_float, ldouble_ftype_ldouble;
@@ -2918,6 +2921,7 @@ c_common_nodes_and_builtins ()
   double_endlink = tree_cons (NULL_TREE, double_type_node, endlink);
   unsigned_endlink = tree_cons (NULL_TREE, unsigned_type_node, endlink);
   cstring_endlink = tree_cons (NULL_TREE, const_string_type_node, endlink);
+  printf_endlink = tree_cons (NULL_TREE, const_string_type_node, NULL_TREE);
 
   ptr_ftype = build_function_type (ptr_type_node, NULL_TREE);
   ptr_ftype_unsigned = build_function_type (ptr_type_node, unsigned_endlink);
@@ -3100,10 +3104,7 @@ c_common_nodes_and_builtins ()
     = build_function_type (integer_type_node, cstring_endlink);
 
   /* Prototype for printf.  */
-  printf_ftype
-    = build_function_type (integer_type_node,
-			   tree_cons (NULL_TREE, const_string_type_node,
-				      NULL_TREE));
+  printf_ftype = build_function_type (integer_type_node, printf_endlink);
 
   /* These stdio prototypes are declared using void* in place of
      FILE*.  They are only used for __builtin_ style calls, regular
@@ -3132,10 +3133,12 @@ c_common_nodes_and_builtins ()
   /* Prototype for fprintf.  */
   fprintf_ftype
     = build_function_type (integer_type_node,
-			   tree_cons (NULL_TREE, ptr_type_node,
-				      tree_cons (NULL_TREE,
-						 const_string_type_node,
-						 NULL_TREE)));
+			   tree_cons (NULL_TREE, ptr_type_node, printf_endlink));
+
+  /* Prototype for sprintf.  */
+  sprintf_ftype
+    = build_function_type (integer_type_node,
+			   tree_cons (NULL_TREE, string_type_node, printf_endlink));
 
   builtin_function ("__builtin_constant_p", default_function_type,
 		    BUILT_IN_CONSTANT_P, BUILT_IN_NORMAL, NULL_PTR);
@@ -3327,13 +3330,15 @@ c_common_nodes_and_builtins ()
   builtin_function_2 ("__builtin_strrchr", "strrchr",
 		      string_ftype_cstring_int, string_ftype_cstring_int,
 		      BUILT_IN_STRRCHR, BUILT_IN_NORMAL, 1, 0, 0);
-  builtin_function_2 ("__builtin_strcpy", "strcpy",
-		      string_ftype_string_cstring, string_ftype_string_cstring,
-		      BUILT_IN_STRCPY, BUILT_IN_NORMAL, 1, 0, 0);
-  builtin_function_2 ("__builtin_strncpy", "strncpy",
-		      string_ftype_string_cstring_sizet,
-		      string_ftype_string_cstring_sizet,
-		      BUILT_IN_STRNCPY, BUILT_IN_NORMAL, 1, 0, 0);
+  built_in_decls[BUILT_IN_STRCPY] =
+    builtin_function_2 ("__builtin_strcpy", "strcpy",
+			string_ftype_string_cstring, string_ftype_string_cstring,
+			BUILT_IN_STRCPY, BUILT_IN_NORMAL, 1, 0, 0);
+  built_in_decls[BUILT_IN_STRNCPY] =
+    builtin_function_2 ("__builtin_strncpy", "strncpy",
+			string_ftype_string_cstring_sizet,
+			string_ftype_string_cstring_sizet,
+			BUILT_IN_STRNCPY, BUILT_IN_NORMAL, 1, 0, 0);
   built_in_decls[BUILT_IN_STRCAT] =
     builtin_function_2 ("__builtin_strcat", "strcat",
 			string_ftype_string_cstring,
@@ -3423,6 +3428,9 @@ c_common_nodes_and_builtins ()
   builtin_function_2 ("__builtin_fprintf", "fprintf",
 		      fprintf_ftype, fprintf_ftype,
 		      BUILT_IN_FPRINTF, BUILT_IN_FRONTEND, 1, 0, 0);
+  builtin_function_2 ("__builtin_sprintf", "sprintf",
+		      sprintf_ftype, sprintf_ftype,
+		      BUILT_IN_SPRINTF, BUILT_IN_FRONTEND, 1, 0, 0);
   built_in_decls[BUILT_IN_FWRITE] =
     builtin_function ("__builtin_fwrite", fwrite_ftype,
 		      BUILT_IN_FWRITE, BUILT_IN_NORMAL, "fwrite");
@@ -4227,6 +4235,12 @@ c_expand_builtin (exp, target, tmode, mo
 	return target;
       break;
 
+    case BUILT_IN_SPRINTF:
+      target = c_expand_builtin_sprintf (arglist, modifier, ignore);
+      if (target)
+	return target;
+      break;
+
     default:			/* just do library call, if unknown builtin */
       error ("built-in function `%s' not currently supported",
 	     IDENTIFIER_POINTER (DECL_NAME (fndecl)));
@@ -4443,6 +4457,65 @@ c_expand_builtin_fprintf (arglist, targe
   return expand_expr (build_function_call (fn, arglist),
 		      (ignore ? const0_rtx : target),
 		      tmode, modifier);
+}
+
+/* If the arguments passed to sprintf are suitable for optimizations,
+   we attempt to transform the call. */
+static rtx
+c_expand_builtin_sprintf (arglist, modifier, ignore)
+     tree arglist;
+     enum expand_modifier modifier;
+     int ignore;
+{
+  tree fn = built_in_decls[BUILT_IN_STRCPY], format_arg;
+  const char *fmt;
+
+  /* If the return value is used, or the replacement _DECL isn't
+     initialized, don't do the transformation. */
+  if (!ignore || !fn)
+    return 0;
+
+  /* Verify the required arguments in the original call. */
+  if (arglist == 0
+      || (TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
+      || (TREE_CHAIN (arglist) == 0)
+      || (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) !=
+	  POINTER_TYPE))
+    return 0;
+  
+  /* Check the specifier vs. the parameters. */
+  if (!is_valid_printf_arglist (TREE_CHAIN (arglist)))
+    return 0;
+
+  format_arg = TREE_VALUE (TREE_CHAIN (arglist));
+  fmt = c_getstr (format_arg);
+
+  /* If the format specifier isn't a STRING_CST, punt.  */
+  if (fmt == NULL)
+    return 0;
+
+  /* OK!  We can attempt optimization.  */
+
+  /* If the format specifier was "%s", call __builtin_strcpy(arg1, arg3). */
+  if (strcmp (fmt, "%s") == 0)
+    {
+      tree newarglist
+	= build_tree_list (NULL_TREE,
+			   TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))));
+      arglist = tree_cons (NULL_TREE, TREE_VALUE (arglist), newarglist);
+    }
+  else
+    {
+     /* We can't handle anything else with % args or %% ... yet. */
+      if (strchr (fmt, '%'))
+	return 0;
+
+      /* When "string" doesn't contain %, replace all cases of
+         sprintf(x,string) with strcpy(x,string).  */
+    }
+
+  return expand_expr (build_function_call (fn, arglist), const0_rtx,
+		      ptr_mode, modifier);
 }
 
 
--- gcc/builtins.c.jj	Fri Mar 30 11:44:42 2001
+++ gcc/builtins.c	Fri Apr  6 20:16:46 2001
@@ -76,8 +76,6 @@ tree built_in_decls[(int) END_BUILTINS] 
 tree (*lang_type_promotes_to) PARAMS ((tree));
 
 static int get_pointer_alignment	PARAMS ((tree, unsigned));
-static tree c_strlen			PARAMS ((tree));
-static const char *c_getstr		PARAMS ((tree));
 static rtx c_readstr			PARAMS ((const char *,
 						 enum machine_mode));
 static int target_char_cast		PARAMS ((tree, char *)); 
@@ -225,12 +223,13 @@ get_pointer_alignment (exp, max_align)
    Unfortunately, string_constant can't access the values of const char
    arrays with initializers, so neither can we do so here.  */
 
-static tree
+tree
 c_strlen (src)
      tree src;
 {
   tree offset_node;
-  int offset, max;
+  int max;
+  HOST_WIDE_INT offset;
   const char *ptr;
 
   src = string_constant (src, &offset_node);
@@ -293,12 +292,13 @@ c_strlen (src)
 /* Return a char pointer for a C string if it is a string constant
    or sum of string constant and integer constant.  */
 
-static const char *
+const char *
 c_getstr (src)
      tree src;
 {
   tree offset_node;
-  int offset, max;
+  int max;
+  HOST_WIDE_INT offset;
   const char *ptr;
 
   src = string_constant (src, &offset_node);
@@ -310,15 +310,12 @@ c_getstr (src)
 
   if (!offset_node)
     offset = 0;
-  else if (TREE_CODE (offset_node) != INTEGER_CST)
+  else if (!host_integerp (offset_node, 1))
     return 0;
   else
     {
-      /* Did we get a long long offset?  If so, punt.  */
-      if (TREE_INT_CST_HIGH (offset_node) != 0)
-	return 0;
       offset = TREE_INT_CST_LOW (offset_node);
-      if (offset < 0 || offset > max)
+      if (offset > max)
 	return 0;
     }
 
@@ -3585,7 +3582,7 @@ expand_builtin (exp, target, subtarget, 
       if (target)
 	return target;
       break;
-      
+
       /* Various hooks for the DWARF 2 __throw routine.  */
     case BUILT_IN_UNWIND_INIT:
       expand_builtin_unwind_init ();
--- gcc/expr.h.jj	Thu Apr  5 00:12:39 2001
+++ gcc/expr.h	Fri Apr  6 19:23:37 2001
@@ -906,6 +906,8 @@ extern tree expand_tree_builtin PARAMS (
 extern void std_expand_builtin_va_start PARAMS ((int, tree, rtx));
 extern rtx std_expand_builtin_va_arg PARAMS ((tree, tree));
 extern rtx expand_builtin_va_arg PARAMS ((tree, tree));
+extern tree c_strlen PARAMS ((tree));
+extern const char *c_getstr PARAMS ((tree));
 #endif
 
 extern void expand_builtin_setjmp_setup PARAMS ((rtx, rtx));
--- gcc/c-format.c.jj	Mon Mar 12 11:44:59 2001
+++ gcc/c-format.c	Fri Apr  6 20:16:46 2001
@@ -1500,6 +1500,7 @@ check_format_info_recurse (status, res, 
      int arg_num;
 {
   int format_length;
+  HOST_WIDE_INT offset;
   const char *format_chars;
   tree array_size = 0;
   tree array_init;
@@ -1589,6 +1590,35 @@ check_format_info_recurse (status, res, 
       return;
     }
 
+  offset = 0;
+  if (TREE_CODE (format_tree) == PLUS_EXPR)
+    {
+      tree arg0, arg1;
+
+      arg0 = TREE_OPERAND (format_tree, 0);
+      arg1 = TREE_OPERAND (format_tree, 1);
+      STRIP_NOPS (arg0);
+      STRIP_NOPS (arg1);
+      if (TREE_CODE (arg1) == INTEGER_CST)
+	format_tree = arg0;
+      else if (TREE_CODE (arg0) == INTEGER_CST)
+	{
+	  format_tree = arg1;
+	  arg1 = arg0;
+	}
+      else
+	{
+	  res->number_non_literal++;
+	  return;
+	}
+      if (!host_integerp (arg1, 1))
+	{
+	  res->number_non_literal++;
+	  return;
+	}
+
+      offset = TREE_INT_CST_LOW (arg1);
+    }
   if (TREE_CODE (format_tree) != ADDR_EXPR)
     {
       res->number_non_literal++;
@@ -1631,6 +1661,16 @@ check_format_info_recurse (status, res, 
 	      && format_length > array_size_value)
 	    format_length = array_size_value;
 	}
+    }
+  if (offset)
+    {
+      if (offset >= format_length)
+	{
+	  res->number_non_literal++;
+	  return;
+	}
+      format_chars += offset;
+      format_length -= offset;
     }
   if (format_length < 1)
     {
--- gcc/testsuite/gcc.c-torture/execute/string-opt-13.c.jj	Fri Apr  6 14:52:57 2001
+++ gcc/testsuite/gcc.c-torture/execute/string-opt-13.c	Fri Apr  6 19:05:34 2001
@@ -0,0 +1,51 @@
+/* Copyright (C) 2001  Free Software Foundation.
+
+   Ensure all expected transformations of builtin sprintf occur and
+   perform correctly.
+
+   Written by Jakub Jelinek, 4/6/2001.  */
+
+extern void abort (void);
+typedef __SIZE_TYPE__ size_t;
+extern int sprintf (char *, const char *, ...);
+extern int memcmp (const void *, const void *, size_t);
+
+char *foobar = "foobar";
+
+int main ()
+{
+  char dst[64], *d2;
+
+  sprintf (dst, "%s", "hello");
+  if (memcmp (dst, "hello", 6) != 0)
+    abort();
+  sprintf (dst + 5, "world");
+  if (memcmp (dst, "helloworld", 11) != 0)
+    abort();
+  d2 = dst + 6;
+  sprintf (++d2, "UVWX" + 2);
+  if (memcmp (dst, "hellowoWX", 10) != 0 || d2 != dst + 7)
+    abort();
+  sprintf (dst, "%s", foobar + 1);
+  if (memcmp (dst, "oobar", 6) != 0)
+    abort();
+
+  /* Test at least one instance of the __builtin_ style.  We do this
+     to ensure that it works and that the prototype is correct.  */
+  __builtin_sprintf (--d2, "z");
+  if (memcmp (dst, "oobarz", 7 != 0) || d2 != dst + 6)
+    abort ();
+
+  return 0;
+}
+
+#ifdef __OPTIMIZE__
+/* When optimizing, all the above cases should be transformed into
+   something else.  So any remaining calls to the original function
+   should abort.  */
+static int
+sprintf (char *s1, const char *s2, ...)
+{
+  abort();
+}
+#endif

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]