[PATCH] Optimize sprintf into strcpy if possible (take 2)

Jakub Jelinek jakub@redhat.com
Tue Apr 10 11:11:00 GMT 2001


On Sat, Apr 07, 2001 at 09:18:51AM -0400, Kaveh R. Ghazi wrote:
>  > I don't have access to SunOS at all, is the prototype
>  > char *sprintf();
>  > there?
> 
> Yes, although fixproto will add the standard parameters wrapped by
> _PARAMS().  You can probably temporarily fake it for a test.

Actually this was caught by the same mode code in duplicate_decls and I
don't think there are any 64bit OSes with broken sprintf prototypes of this
kind, is that fair assumption?
But what is broken is compilation with -traditional, because then one ends
up with stuff like:
int fprintf();
and this causes duplicate_decls to warn (the builtin prototype has variable
number of arguments). Should be fixed in the patch below.

> Assuming that works, or you tweek it to work, IMHO you still need to
> generalize it to work with g++.  I.e. builtin fprintf currently
> doesn't work with g++ for this reason and neither will builtin sprintf
> without some help.
> 
> The best thing to do would be to make a separate function (in
> c-common.c) which checks whether the MODEs of the return type and/or
> *all* arguments (not just the first) match.  Then call it from
> duplicate_decls() of both languages.  (Checking all arguments would
> allow me to declare the parameters for builtin fputs, whose stream is
> in the *second* argument.)
> 
> If it sounds like I'm asking you to clean up something I should have
> done, you'd be correct. :-) If you tackle this I'd be very grateful,
> its one of the leftovers I never fixed in my builtin stdio work.

I had a look at C++ stuff, the results are merged below.
The basic changes since my 04-06 patch are:
- duplicate_decls considers non-matching last argument as harmless if it is
pointer type in both cases (and void * in the builtin case). I don't think
the mode rule should be applied to all arguments. This allows fputs to have
prototype.
- sprintf builtin handles "%c" as you suggested, tests added.
- C++ duplicate_decls does something similar to C duplicate_decls, because
e.g. current libstdc++ headers end up with warnings with -Wsystem-headers
otherwise.
- C duplicate_decls has a -traditional check as described above.
- testcase for the c-format changes.

Bootstrapped on i386-redhat-linux, creates one regression -
g++.brendan/misc13.C, but IMHO that test should just be killed.

Issues (basically questions targeted at Mark):
1) why are non-__ builtins declared in std:: only and not in :: as well?
   I mean at least the c_std/ headers put all the string/stdio prototypes
   both in global namespace and std namespace, so even with using namespace std;
   people use the global namespace functions unless writing std::sprintf and
   the likes, but this results in no builtin optimizations.
   Shouldn't there instead be some switch (or -pedantic) which would force
   builtins into std namespace only and keep them by default in global
   namespace as well?
2) c_std/ headers don't use throw lists, which means glibc's hard effort to
   mark functions properly with throw() if they don't throw is useless.
   Shouldn't those headers use some configure macro where configure would
   find if glibc uses throw() in the places c_std/ headers would expect
   and if yes, define this to throw() (nothing otherwise)?
   I can try to code it up if people agree.

2001-04-10  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.
	(adjust_builtin_function_type): New.
	* c-common.h (adjust_builtin_function_type): New prototype.
	* 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.
	* c-decl.c (duplicate_decls): Don't warn if a builtin
	is redeclared with int foo() style prototype under -traditional.
	Use adjust_builtin_function_type.

cp/
	* decl.c (duplicate_decls): Override builtin type if it new
	prototype is sufficiently similar instead of warning and
	throwing the builtin away.

testsuite/
        * gcc.c-torture/execute/string-opt-13.c: New test.
	* g++.old-deja/g++.other/string-opt-1.C: New test.
	* gcc.dg/format/c90-printf-1.c: Add tests for string plus constant
	formats.
	* gcc.dg/builtins-1.c: New test.

--- gcc/cp/decl.c.jj	Tue Apr 10 17:42:46 2001
+++ gcc/cp/decl.c	Tue Apr 10 17:50:12 2001
@@ -3181,9 +3181,33 @@ duplicate_decls (newdecl, olddecl)
 
 	      if (TREE_PUBLIC (newdecl))
 		{
-		  cp_warning ("new declaration `%#D'", newdecl);
-		  cp_warning ("ambiguates built-in declaration `%#D'",
-			      olddecl);
+		  if (DECL_EXTERN_C_P (newdecl)
+		      && DECL_EXTERN_C_P (olddecl)
+		      && DECL_BUILT_IN (olddecl)
+		      && CP_DECL_CONTEXT (newdecl)
+			 == CP_DECL_CONTEXT (olddecl))
+		    {
+		      tree trytype
+			= adjust_builtin_function_type (TREE_TYPE (newdecl),
+							TREE_TYPE (olddecl));
+
+		      if (trytype)
+			{
+			  tree oldtype = TREE_TYPE (olddecl);
+
+			  TREE_TYPE (olddecl) = trytype;
+			  types_match = decls_match (newdecl, olddecl);
+			  if (!types_match)
+			    TREE_TYPE (olddecl) = oldtype;
+			}
+		    }
+
+		  if (!types_match)
+		    {
+		      cp_warning ("new declaration `%#D'", newdecl);
+		      cp_warning ("ambiguates built-in declaration `%#D'",
+				  olddecl);
+		    }
 		}
 	      else if (warn_shadow)
 		cp_warning ("shadowing %s function `%#D'",
--- gcc/testsuite/g++.old-deja/g++.other/string-opt-1.C.jj	Tue Apr 10 17:16:28 2001
+++ gcc/testsuite/g++.old-deja/g++.other/string-opt-1.C	Tue Apr 10 17:50:12 2001
@@ -0,0 +1,61 @@
+/* Copyright (C) 2001  Free Software Foundation.
+
+   Ensure all expected transformations of builtin sprintf occur and
+   perform correctly.
+
+   Written by Jakub Jelinek, 4/10/2001.  */
+
+extern "C" void abort (void);
+typedef __SIZE_TYPE__ size_t;
+namespace std {
+  extern "C" int sprintf (char *, const char *, ...);
+  extern "C" int memcmp (const void *, const void *, size_t);
+};
+
+char *foobar = "foobar";
+
+int main ()
+{
+  char dst[64], *d2;
+
+  std::sprintf (dst, "%s", "hello");
+  if (std::memcmp (dst, "hello", 6) != 0)
+    abort();
+  std::sprintf (dst + 5, "world");
+  if (std::memcmp (dst, "helloworld", 11) != 0)
+    abort();
+  d2 = dst + 6;
+  std::sprintf (++d2, "UVWX" + 2);
+  if (std::memcmp (dst, "hellowoWX", 10) != 0 || d2 != dst + 7)
+    abort();
+  std::sprintf (d2++, "%c", ' ');
+  if (std::memcmp (dst, "hellowo ", 9) != 0 || d2 != dst + 8)
+    abort();
+  std::sprintf (d2--, "%c", 65536 | '0');
+  if (std::memcmp (dst, "hellowo 0", 10) != 0 || d2 != dst + 7)
+    abort();
+  std::sprintf (dst, "%s", foobar + 1);
+  if (std::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 (std::memcmp (dst, "oobar\0z", 8) != 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.  */
+extern "C" {
+static int
+sprintf (char *s1, const char *s2, ...)
+{
+  abort();
+}
+};
+#endif
--- 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	Tue Apr 10 17:50:12 2001
@@ -0,0 +1,57 @@
+/* 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 (d2++, "%c", ' ');
+  if (memcmp (dst, "hellowo ", 9) != 0 || d2 != dst + 8)
+    abort();
+  sprintf (d2--, "%c", 65536 | '0');
+  if (memcmp (dst, "hellowo 0", 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, "oobar\0z", 8) != 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
--- gcc/testsuite/gcc.dg/format/c90-printf-1.c.jj	Sun Jan  7 11:44:59 2001
+++ gcc/testsuite/gcc.dg/format/c90-printf-1.c	Tue Apr 10 17:50:12 2001
@@ -29,6 +29,8 @@ foo (int i, int i1, int i2, unsigned int
   printf ("%.*d\n", i2, i);
   printf ("%*.*ld\n", i1, i2, l);
   printf ("%d %lu\n", i, ul);
+  printf ("%%d\n" + 1, i);
+  printf (5 + "%.-*d%3d\n", i);
   /* GCC has objected to the next one in the past, but it is a valid way
      of specifying zero precision.
   */
@@ -222,6 +224,7 @@ foo (int i, int i1, int i2, unsigned int
   /* Wrong number of arguments.  */
   printf ("%d%d", i); /* { dg-warning "arguments" "wrong number of args" } */
   printf ("%d", i, i); /* { dg-warning "arguments" "wrong number of args" } */
+  printf ("%d%d" + 2, i, i); /* { dg-warning "arguments" "wrong number of args" } */
   /* Miscellaneous bogus constructions.  */
   printf (""); /* { dg-warning "zero-length" "warning for empty format" } */
   printf ("\0"); /* { dg-warning "embedded" "warning for embedded NUL" } */
--- gcc/testsuite/gcc.dg/builtins-1.c.jj	Tue Apr 10 17:07:09 2001
+++ gcc/testsuite/gcc.dg/builtins-1.c	Tue Apr 10 19:52:11 2001
@@ -0,0 +1,20 @@
+/* { dg-do compile { target i?86-*-* } } */
+/* { dg-options -traditional } */
+
+typedef struct {
+  int x;
+} FILE;
+
+/* SunOS has broken sprintf declaration, check whether we cope
+   with it without warning.  */
+char *sprintf ();
+int fprintf ();
+
+char buf[10];
+FILE *f;
+
+main()
+{
+  sprintf (buf, "%s", "foo");
+  fprintf (f, "%s", buf);
+}
--- gcc/builtins.def.jj	Fri Mar 30 11:44:42 2001
+++ gcc/builtins.def	Tue Apr 10 17:50:12 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/expr.h.jj	Thu Apr  5 00:12:39 2001
+++ gcc/expr.h	Tue Apr 10 17:50:12 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-decl.c.jj	Fri Mar 30 11:44:42 2001
+++ gcc/c-decl.c	Tue Apr 10 17:50:12 2001
@@ -1497,43 +1497,21 @@ duplicate_decls (newdecl, olddecl, diffe
 	}
       else if (!types_match)
 	{
-	  /* Accept the return type of the new declaration if same modes.  */
-	  tree oldreturntype = TREE_TYPE (oldtype);
-	  tree newreturntype = TREE_TYPE (newtype);
+	  tree trytype = adjust_builtin_function_type (newtype, oldtype);
 
-	  if (TYPE_MODE (oldreturntype) == TYPE_MODE (newreturntype))
-	    {
-	      /* Function types may be shared, so we can't just modify
-		 the return type of olddecl's function type.  */
-	      tree trytype
-		= build_function_type (newreturntype,
-				       TYPE_ARG_TYPES (oldtype));
+	  types_match = trytype && comptypes (newtype, trytype);
+	  if (types_match)
+	    oldtype = trytype;
+	  else if (flag_traditional && TYPE_ARG_TYPES (newtype) == 0
+		   && comptypes (TREE_TYPE (newtype),
+				 TREE_TYPE (trytype ? trytype : oldtype)))
+	    /* If the new type doesn't specify argument types and the
+	       types don't match while return types do (or at least
+	       their modes), throw away the builtin quietly.
+	       This means the built-in has variable number of arguments
+	       or arguments are affected by default promotions.  */
+	    return 0;
 
-              types_match = comptypes (newtype, trytype);
-	      if (types_match)
-		oldtype = trytype;
-	    }
-	  /* Accept harmless mismatch in first argument type also.
-	     This is for the ffs and fprintf builtins.  */
-	  if (TYPE_ARG_TYPES (TREE_TYPE (newdecl)) != 0
-	      && TYPE_ARG_TYPES (oldtype) != 0
-	      && TREE_VALUE (TYPE_ARG_TYPES (newtype)) != 0
-	      && TREE_VALUE (TYPE_ARG_TYPES (oldtype)) != 0
-	      && (TYPE_MODE (TREE_VALUE (TYPE_ARG_TYPES (newtype)))
-		  == TYPE_MODE (TREE_VALUE (TYPE_ARG_TYPES (oldtype)))))
-	    {
-	      /* Function types may be shared, so we can't just modify
-		 the return type of olddecl's function type.  */
-	      tree trytype
-		= build_function_type (TREE_TYPE (oldtype),
-				       tree_cons (NULL_TREE,
-						  TREE_VALUE (TYPE_ARG_TYPES (newtype)),
-						  TREE_CHAIN (TYPE_ARG_TYPES (oldtype))));
-
-	      types_match = comptypes (newtype, trytype);
-	      if (types_match)
-		oldtype = trytype;
-	    }
 	  if (! different_binding_level)
 	    TREE_TYPE (olddecl) = oldtype;
 	}
--- gcc/c-common.c.jj	Fri Mar 30 11:44:42 2001
+++ gcc/c-common.c	Tue Apr 10 17:50:12 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,9 +3330,10 @@ 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);
+  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);
   builtin_function_2 ("__builtin_strncpy", "strncpy",
 		      string_ftype_string_cstring_sizet,
 		      string_ftype_string_cstring_sizet,
@@ -3423,6 +3427,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");
@@ -3435,7 +3442,7 @@ c_common_nodes_and_builtins ()
      FILE* in the __builtin_ prototype supplied here.  */
   built_in_decls[BUILT_IN_FPUTS] =
     builtin_function_2 ("__builtin_fputs", "fputs",
-			fputs_ftype, int_ftype_any,
+			fputs_ftype, fputs_ftype,
 			BUILT_IN_FPUTS, BUILT_IN_NORMAL, 1, 0, 0);
 
   /* Declare these functions non-returning
@@ -3539,6 +3546,89 @@ builtin_function_2 (builtin_name, name, 
     }
   return (bdecl != 0 ? bdecl : decl);
 }
+
+
+/* Try to adjust type of built-in function OLDTYPE, so that it can be
+   compatible with NEWTYPE.  Returns a new type if it is desirable to
+   modify the built-in type or NULL_TREE if it cannot be adjusted.
+   This routine replaces the return type of OLDTYPE with return type
+   of NEWTYPE if they have same MODE, or replaces first argument in OLDTYPE
+   with first argument in NEWTYPE if they have same mode, or replaces
+   last argument in OLDTYPE with last argument in NEWTYPE if they are both
+   pointer types.  */
+tree
+adjust_builtin_function_type (newtype, oldtype)
+     tree newtype, oldtype;
+{
+  /* Accept the return type of the new declaration if same modes.  */
+  tree oldreturntype = TREE_TYPE (oldtype);
+  tree newreturntype = TREE_TYPE (newtype);
+  tree trytype = oldtype;
+
+  if (oldreturntype != newreturntype
+      && TYPE_MODE (oldreturntype) == TYPE_MODE (newreturntype))
+    /* Function types may be shared, so we can't just modify
+       the return type of olddecl's function type.  */
+    trytype = build_function_type (newreturntype,
+				   TYPE_ARG_TYPES (trytype));
+
+  /* Accept harmless mismatch in first argument type.
+     This is for the ffs and fprintf builtins.  */
+  if (TYPE_ARG_TYPES (newtype) != 0
+      && TYPE_ARG_TYPES (trytype) != 0
+      && TREE_VALUE (TYPE_ARG_TYPES (newtype)) != 0
+      && TREE_VALUE (TYPE_ARG_TYPES (trytype)) != 0
+      && TREE_VALUE (TYPE_ARG_TYPES (newtype))
+	 != TREE_VALUE (TYPE_ARG_TYPES (trytype))
+      && (TYPE_MODE (TREE_VALUE (TYPE_ARG_TYPES (newtype)))
+	  == TYPE_MODE (TREE_VALUE (TYPE_ARG_TYPES (trytype)))))
+    /* Function types may be shared, so we can't just modify
+       the return type of olddecl's function type.  */
+    trytype = build_function_type (TREE_TYPE (trytype),
+				tree_cons (NULL_TREE,
+					   TREE_VALUE (TYPE_ARG_TYPES (newtype)),
+					   TREE_CHAIN (TYPE_ARG_TYPES (trytype))));
+
+  /* And accept harmless mismatch in last argument type also.
+     This is for the fputs builtin.  */
+  if (TYPE_ARG_TYPES (newtype) != 0
+      && TYPE_ARG_TYPES (trytype) != 0)
+    {
+      tree newargs = TREE_CHAIN (TYPE_ARG_TYPES (newtype)), newnext;
+      tree oldargs = TREE_CHAIN (TYPE_ARG_TYPES (trytype)), oldnext;
+
+      while (newargs && oldargs)
+	{
+	  newnext = TREE_CHAIN (newargs);
+	  oldnext = TREE_CHAIN (oldargs);
+	  if (newnext && TREE_VALUE (newnext) == void_type_node
+	      && oldnext && TREE_VALUE (oldnext) == void_type_node)
+	    {
+	      if (TREE_VALUE (newargs) && TREE_VALUE (oldargs)
+		  && TREE_CODE (TREE_VALUE (newargs)) == POINTER_TYPE
+		  && TREE_VALUE (oldargs) == ptr_type_node)
+		{
+		  tree parms, tem, *p = &parms;
+
+		  for (tem = TYPE_ARG_TYPES (trytype); tem != oldargs;
+		       tem = TREE_CHAIN (tem))
+		    {
+		      *p = build_tree_list (NULL_TREE,
+					    TREE_VALUE (tem));
+		      p = &TREE_CHAIN (*p);
+		    }
+		  *p = newargs;
+		  return build_function_type (TREE_TYPE (trytype),
+					      parms);
+		}
+	    }
+	  newargs = newnext;
+	  oldargs = oldnext;
+	}
+    }
+
+  return trytype == oldtype ? NULL_TREE : trytype;
+}
 
 /* Given a type, apply default promotions wrt unnamed function arguments
    and return the new type.  Return NULL_TREE if no change.  */
@@ -4227,6 +4317,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 +4539,88 @@ 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);
+    }
+  /* If the format specifier was "%c", optimize as
+     *(unsigned char *)arg1 = arg3; ((unsigned char *)arg1)[1] = '\0';  */
+  else if (strcmp (fmt, "%c") == 0)
+    {
+      tree dst, src, ptr;
+
+      ptr = save_expr (fold (build1 (NOP_EXPR, string_type_node,
+				     TREE_VALUE (arglist))));
+      dst = build1 (INDIRECT_REF, unsigned_char_type_node, ptr);
+      src = build1 (NOP_EXPR, unsigned_char_type_node,
+		    TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))));
+      expand_expr (fold (build (MODIFY_EXPR, unsigned_char_type_node,
+				dst, src)), const0_rtx, ptr_mode, modifier);
+      src = build_int_2 (1, 0);
+      TREE_TYPE (src) = string_type_node;
+      dst = build1 (INDIRECT_REF, unsigned_char_type_node,
+		    build (PLUS_EXPR, string_type_node, ptr, src));
+      src = build_int_2 (0, 0);
+      TREE_TYPE (src) = unsigned_char_type_node;
+      return expand_expr (fold (build (MODIFY_EXPR, unsigned_char_type_node,
+				       dst, src)), NULL_RTX, ptr_mode,
+				       modifier);
+    }
+  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	Tue Apr 10 17:50:12 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/c-format.c.jj	Mon Mar 12 11:44:59 2001
+++ gcc/c-format.c	Tue Apr 10 17:50:12 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/c-common.h.jj	Fri Mar 30 11:44:42 2001
+++ gcc/c-common.h	Tue Apr 10 17:50:12 2001
@@ -478,6 +478,7 @@ extern tree (*make_fname_decl)          
 extern tree identifier_global_value		PARAMS ((tree));
 extern void record_builtin_type			PARAMS ((enum rid,
 							 const char *, tree));
+extern tree adjust_builtin_function_type	PARAMS ((tree, tree));
 extern tree build_void_list_node		PARAMS ((void));
 
 extern void declare_function_name		PARAMS ((void));


	Jakub



More information about the Gcc-patches mailing list