Patch to add builtin fprintf

Kaveh R. Ghazi ghazi@caip.rutgers.edu
Wed Oct 11 12:45:00 GMT 2000


The following patch adds a builtin fprintf which is similar to the
existing builtin printf.  The new patch adds the following
transformations.

fprintf(stream, "%s", string) -> fputs(string, stream)
fprintf(stream, "%c", char) -> fputc(char, stream)
fprintf(stream, "string") -> fputs("string", stream)  (if string has no %)

There was one difficulty regarding declaring the builtin.  There is a
generic problem declaring builtins requiring the "FILE" type.  For
existing ones like fputs, we got around it by having the internal
declaration omit parameters, e.g. builtin "extern int fputs();".  When
stdio.h is included, the fully prototyped version overrides the
missing arguments of the builtin, but the builtin continues to exist.

However using this same trick for a stdarg function doesn't work.  If
we declare "extern int fprintf();", when the real decl from stdio.h
appears both the C and C++ frontend complain that it "ambiguates" the
builtin one.  In particular, C++ says fprintf(FILE*, const char*, ...)
ambiguates fprintf(...).

Fortunately while investigating alternatives, I noticed that the C
front end has a nice little special case for builtins.  It silently
allows the return type or the first argument to be different as long
as they have the same "mode".  This appears to have been initially
introduced for the ffs builtin because some platforms disagree on what
integer type the first parameter is.

Anyway, this is very cool, because it allows me to declare fprintf
internally as: extern int fprintf(void*, const char *, ...)  and when
the real decl from stdio.h comes along, the mismatch on the first
argument (void* vs FILE*) is silently allowed by this exception.  The
FILE* takes precedence so its perfect for what I want.

The C++ frontend doesn't appear to allow this exception yet, but I
know where to put it if this is acceptable.  I've got a quick patch to
add the exception to cp/decl.c once this one is approved.  I also need
http://gcc.gnu.org/ml/gcc-patches/2000-10/msg00085.html approved
before C++ stdio builtins will work at all.

Anyway, the following patch bootstrapped on solaris2.7, no
regressions.  Okay to install?

		Thanks,
		--Kaveh

2000-10-10  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>

	* builtins.def (BUILT_IN_FPRINTF): New entry.

	* c-common.c (c_expand_builtin_fprintf): New function.
	(init_function_format_info): Handle __builtin_fprintf.
	(c_common_nodes_and_builtins): Declare fprintf/__builtin_fprintf.
	(c_expand_builtin): Handle BUILT_IN_FPRINTF.

	* c-decl.c (duplicate_decls): Adjust comment.
	
diff -rup orig/egcs-CVS20001009/gcc/builtins.def egcs-CVS20001009/gcc/builtins.def
--- orig/egcs-CVS20001009/gcc/builtins.def	Tue Sep 26 18:12:17 2000
+++ egcs-CVS20001009/gcc/builtins.def	Tue Oct 10 12:20:14 2000
@@ -66,6 +66,7 @@ DEF_BUILTIN(BUILT_IN_PRINTF)
 DEF_BUILTIN(BUILT_IN_FPUTC)
 DEF_BUILTIN(BUILT_IN_FPUTS)
 DEF_BUILTIN(BUILT_IN_FWRITE)
+DEF_BUILTIN(BUILT_IN_FPRINTF)
 
   /* ISO C99 floating point unordered comparisons.  */
 DEF_BUILTIN(BUILT_IN_ISGREATER)
diff -rup orig/egcs-CVS20001009/gcc/c-common.c egcs-CVS20001009/gcc/c-common.c
--- orig/egcs-CVS20001009/gcc/c-common.c	Mon Oct  9 12:28:51 2000
+++ egcs-CVS20001009/gcc/c-common.c	Tue Oct 10 12:20:14 2000
@@ -1730,6 +1730,8 @@ static int is_valid_printf_arglist PARAM
 static rtx c_expand_builtin (tree, rtx, enum machine_mode, enum expand_modifier);
 static rtx c_expand_builtin_printf PARAMS ((tree, rtx, enum machine_mode,
 					    enum expand_modifier, int));
+static rtx c_expand_builtin_fprintf PARAMS ((tree, rtx, enum machine_mode,
+					     enum expand_modifier, int));
 
 /* Initialize the table of functions to perform format checking on.
    The ISO C functions are always checked (whether <stdio.h> is
@@ -1755,6 +1757,8 @@ init_function_format_info ()
 			      printf_format_type, 1, 2);
       record_function_format (get_identifier ("fprintf"), NULL_TREE,
 			      printf_format_type, 2, 3);
+      record_function_format (get_identifier ("__builtin_fprintf"), NULL_TREE,
+			      printf_format_type, 2, 3);
       record_function_format (get_identifier ("sprintf"), NULL_TREE,
 			      printf_format_type, 2, 3);
       record_function_format (get_identifier ("scanf"), NULL_TREE,
@@ -4285,7 +4289,7 @@ c_common_nodes_and_builtins (cplus_mode,
 {
   tree temp;
   tree memcpy_ftype, memset_ftype, strlen_ftype;
-  tree bzero_ftype, bcmp_ftype, puts_ftype, printf_ftype;
+  tree bzero_ftype, bcmp_ftype, puts_ftype, printf_ftype, fprintf_ftype;
   tree endlink, int_endlink, double_endlink, unsigned_endlink;
   tree sizetype_endlink;
   tree ptr_ftype, ptr_ftype_unsigned;
@@ -4460,6 +4464,14 @@ c_common_nodes_and_builtins (cplus_mode,
 			   tree_cons (NULL_TREE, const_string_type_node,
 				      NULL_TREE));
 
+  /* 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)));
+
   builtin_function ("__builtin_constant_p", default_function_type,
 		    BUILT_IN_CONSTANT_P, BUILT_IN_NORMAL, NULL_PTR);
 
@@ -4670,6 +4682,8 @@ c_common_nodes_and_builtins (cplus_mode,
   built_in_decls[BUILT_IN_FPUTS] =
     builtin_function ("__builtin_fputs", int_ftype_any,
 		      BUILT_IN_FPUTS, BUILT_IN_NORMAL, "fputs");
+  builtin_function ("__builtin_fprintf", fprintf_ftype,
+		    BUILT_IN_FPRINTF, BUILT_IN_FRONTEND, "fprintf");
 
   if (! no_builtins)
     {
@@ -4727,6 +4741,8 @@ c_common_nodes_and_builtins (cplus_mode,
 			BUILT_IN_NORMAL, NULL_PTR);
       builtin_function ("fputs", int_ftype_any, BUILT_IN_FPUTS,
 			BUILT_IN_NORMAL, NULL_PTR);
+      builtin_function ("fprintf", fprintf_ftype, BUILT_IN_FPRINTF,
+			BUILT_IN_FRONTEND, NULL_PTR);
 
       /* Declare these functions volatile
 	 to avoid spurious "control drops through" warnings.  */
@@ -5421,9 +5437,14 @@ c_expand_builtin (exp, target, tmode, mo
   switch (fcode)
     {
     case BUILT_IN_PRINTF:
-      target = c_expand_builtin_printf (arglist, target, tmode,
-					modifier, ignore);
-      if (target)
+      if ((target = c_expand_builtin_printf (arglist, target, tmode,
+					     modifier, ignore)))
+	return target;
+      break;
+
+    case BUILT_IN_FPRINTF:
+      if ((target = c_expand_builtin_fprintf (arglist, target, tmode,
+					      modifier, ignore)))
 	return target;
       break;
 
@@ -5558,6 +5579,86 @@ c_expand_builtin_printf (arglist, target
 	/* We'd like to arrange to call fputs(string) here, but we
            need stdout and don't have a way to get it ... yet.  */
 	return 0;
+    }
+  
+  return expand_expr (build_function_call (fn, arglist),
+		      (ignore ? const0_rtx : target),
+		      tmode, modifier);
+}
+
+/* If the arguments passed to fprintf are suitable for optimizations,
+   we attempt to transform the call. */
+static rtx
+c_expand_builtin_fprintf (arglist, target, tmode, modifier, ignore)
+     tree arglist;
+     rtx target;
+     enum machine_mode tmode;
+     enum expand_modifier modifier;
+     int ignore;
+{
+  tree fn_fputc = built_in_decls[BUILT_IN_FPUTC],
+    fn_fputs = built_in_decls[BUILT_IN_FPUTS];
+  tree fn, format_arg, stripped_string;
+
+  /* If the return value is used, or the replacement _DECL isn't
+     initialized, don't do the transformation. */
+  if (!ignore || !fn_fputc || !fn_fputs)
+    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));
+  stripped_string = format_arg;
+  STRIP_NOPS (stripped_string);
+  if (stripped_string && TREE_CODE (stripped_string) == ADDR_EXPR)
+    stripped_string = TREE_OPERAND (stripped_string, 0);
+
+  /* If the format specifier isn't a STRING_CST, punt.  */
+  if (TREE_CODE (stripped_string) != STRING_CST)
+    return 0;
+  
+  /* OK!  We can attempt optimization.  */
+
+  /* If the format specifier was "%s", call __builtin_fputs(arg3, arg1). */
+  if (strcmp (TREE_STRING_POINTER (stripped_string), "%s") == 0)
+    {
+      tree newarglist = build_tree_list (NULL_TREE, TREE_VALUE (arglist));
+      arglist = tree_cons (NULL_TREE,
+			   TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))),
+			   newarglist);
+      fn = fn_fputs;
+    }
+  /* If the format specifier was "%c", call __builtin_fputc (arg3, arg1). */
+  else if (strcmp (TREE_STRING_POINTER (stripped_string), "%c") == 0)
+    {
+      tree newarglist = build_tree_list (NULL_TREE, TREE_VALUE (arglist));
+      arglist = tree_cons (NULL_TREE,
+			   TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))),
+			   newarglist);
+      fn = fn_fputc;
+    }
+  else
+    {
+     /* We can't handle anything else with % args or %% ... yet. */
+      if (strchr (TREE_STRING_POINTER (stripped_string), '%'))
+	return 0;
+      
+      /* When "string" doesn't contain %, replace all cases of
+         fprintf(stream,string) with fputs(string,stream).  The fputs
+         builtin will take take of special cases like length==1.  */
+      arglist = tree_cons (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist)),
+			   build_tree_list (NULL_TREE, TREE_VALUE (arglist)));
+      fn = fn_fputs;
     }
   
   return expand_expr (build_function_call (fn, arglist),
diff -rup orig/egcs-CVS20001009/gcc/c-decl.c egcs-CVS20001009/gcc/c-decl.c
--- orig/egcs-CVS20001009/gcc/c-decl.c	Sun Oct  8 21:26:29 2000
+++ egcs-CVS20001009/gcc/c-decl.c	Tue Oct 10 12:20:14 2000
@@ -1556,7 +1556,7 @@ duplicate_decls (newdecl, olddecl, diffe
 		oldtype = trytype;
 	    }
 	  /* Accept harmless mismatch in first argument type also.
-	     This is for ffs.  */
+	     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


More information about the Gcc-patches mailing list