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]

Re: PATCH RFC: stdio optimization [take 2]


	Here is the latest version of my stdio optimization patch.
I've completed transformations for the following constructs and
verified by checking the .s file that they actually work: :-)

fputc -> putc (if !optimize_size)
fputs(one-char-string) -> fputc/putc (depending on optimize_size)

printf(string) -> fputs/fputc/putc (depending on strlen/optimize_size)
printf("%s", string) -> fputs(string, stdout)
printf("%c", char) -> fputc/putc(char, stdout) (depending on optimize_size)
printf("%s\n", string) -> puts(string)

fprintf(stream, string) -> fputs/fputc/putc (depending on strlen/optimize_size)
fprintf(stream, "%s", string) -> fputs(string, stream)
fprintf(stream, "%c", char) -> fputc/putc(char, stream)

sprintf(buf, string) -> memcpy(buf, string, len)


Now here are the shortcomings: :-(

1.  It works for C and Objc, but not for C++.  I don't know why, but
C++ won't expand the inline wrapper functions.  It emits them as
extern functions (which I thought was impossible if they were declared
as extern __inline__) and then calls them explicitely.

2.  IMO, there are too many wrappers in stdio-opt.h.  I needed these
to maintain the return value and signature of each function so I
wouldn't have to jump through too many hoops to make a replacement.
Suggestions (and instructions) for reducing the number would be great.

E.g. I could elimintate those only needed for the return value if I
could detect when the return value was not used and punt in those
cases.  But I don't know how to detect that.  I could also use some
help on how to build arglists with different argument types.  E.g.
given a constant string parameter, how do I take string[0] and
transform it into a char/int in a function call in a language
independent manner?

I don't think we can eliminate the wrappers entirely because at least
fputc->putc needs the macro expansion of putc, and printf->fputs/fputc
needs the definitions of stdout.  (Suggestions for the rest are
welcome.)

3.  The setup forces you to include stdio-opt.h for now.  I'll
eventually have stdio.h include stdio-opt.h automatically.
(Suggestions for the best way to do this are welcome.)

4.  These optimizations only operate when called directly as a builtin
right now.  This is intentional and easily remedied.  Don't worry
about this one.


Here's a sample testcase and the patch follows.

		--Kaveh

--------------------------------------------------------------------------
#include "stdio-opt.h"

int main()
{
  char foo[1024];
  
  __builtin_sprintf (foo, "hello world\n");
  printf ("%s", foo); /* should not tranform */
  

  __builtin_fprintf (stdout, "%c", 'D');
  __builtin_fprintf (stdout, "%s", "hello world");
  __builtin_fprintf (stdout, "%s", "hello world", 5); /* should not tranform */
  __builtin_fprintf (stdout, "RR");
  __builtin_fprintf (stdout, "R");
  __builtin_printf ("hello %% world\n"); /* should not tranform */
  __builtin_printf ("%d\n", 5); /* should not tranform */
  __builtin_printf ("%s\n", "hello world");
  __builtin_printf ("G1");
  __builtin_printf ("\n");
  __builtin_fputs ("G2", stdout);
  __builtin_fputs ("k", stdout);
  __builtin_fputc ('K', stdout);
  fputc ('\n', stdout); /* should not tranform */

  return 0;
}
--------------------------------------------------------------------------
diff -rup orig/egcs-CVS19991221/gcc/Makefile.in egcs-CVS19991221/gcc/Makefile.in
--- orig/egcs-CVS19991221/gcc/Makefile.in	Tue Dec 21 15:35:45 1999
+++ egcs-CVS19991221/gcc/Makefile.in	Wed Dec 22 10:28:26 1999
@@ -148,6 +148,7 @@ INSTALL_HEADERS_DIR = @build_install_hea
 USER_H = $(srcdir)/ginclude/stdarg.h $(srcdir)/ginclude/stddef.h \
     $(srcdir)/ginclude/varargs.h $(srcdir)/ginclude/proto.h \
     $(srcdir)/ginclude/stdbool.h $(srcdir)/ginclude/iso646.h \
+    $(srcdir)/ginclude/stdio-opt.h \
     $(EXTRA_HEADERS) $(LANG_EXTRA_HEADERS)
 
 # Target to use whe installing assert.h.  Some systems may
@@ -1500,7 +1501,7 @@ expr.o : expr.c $(CONFIG_H) system.h $(R
 builtins.o : builtins.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h \
    function.h $(REGS_H) insn-flags.h insn-codes.h $(EXPR_H) insn-config.h \
    $(RECOG_H) output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h \
-   except.h
+   except.h ggc.h
 calls.o : calls.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h $(EXPR_H) \
    insn-flags.h $(REGS_H) toplev.h output.h function.h
 expmed.o : expmed.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h  \
diff -rup orig/egcs-CVS19991221/gcc/builtins.c egcs-CVS19991221/gcc/builtins.c
--- orig/egcs-CVS19991221/gcc/builtins.c	Wed Dec 15 22:53:59 1999
+++ egcs-CVS19991221/gcc/builtins.c	Tue Dec 21 18:38:41 1999
@@ -38,6 +38,7 @@ Boston, MA 02111-1307, USA.  */
 #include "typeclass.h"
 #include "defaults.h"
 #include "toplev.h"
+#include "ggc.h"
 #include "tm_p.h"
 
 #define CALLED_AS_BUILT_IN(NODE) \
@@ -84,6 +85,14 @@ static rtx expand_builtin_strlen	PROTO((
 static rtx expand_builtin_alloca	PROTO((tree, rtx));
 static rtx expand_builtin_ffs		PROTO((tree, rtx, rtx));
 static rtx expand_builtin_frame_address	PROTO((tree));
+static rtx expand_builtin_fputc		PROTO((tree));
+static rtx expand_builtin_fputs		PROTO((tree));
+static rtx expand_builtin_printf	PROTO((tree));
+static rtx expand_builtin_fprintf	PROTO((tree));
+static rtx expand_builtin_sprintf	PROTO((tree));
+static tree call_get_function_decl	PROTO((const char *));
+static void build_extern_function_decl	PROTO((tree *, const char *));
+static int is_valid_printf_arglist	PROTO((tree));
 static tree stabilize_va_list		PROTO((tree, int));
 
 /* Return the alignment in bits of EXP, a pointer valued expression.
@@ -2240,6 +2249,403 @@ expand_builtin_ffs (arglist, target, sub
     abort ();
   return target;
 }
+
+static const char *const fputc_via_putc = "__gcc_builtin_fputc_via_putc";
+static const char *const fputs_string_via_fputc = "__gcc_builtin_fputs_string_via_fputc";
+static const char *const fputs_char_via_fputc = "__gcc_builtin_fputs_char_via_fputc";
+static const char *const printf_via_fputs = "__gcc_builtin_printf_via_fputs";
+static const char *const printf_string_via_fputc = "__gcc_builtin_printf_string_via_fputc";
+static const char *const printf_char_via_fputc = "__gcc_builtin_printf_char_via_fputc";
+static const char *const sprintf_via_memcpy = "__gcc_builtin_sprintf_via_memcpy";
+
+/* This function calls get_function_decl_p on the argument and returns
+   the result.  FNAME should be the literal name of an inline function
+   for which we want to find the FUNCTION_DECL. */
+static tree
+call_get_function_decl (fname)
+  const char *fname;
+{
+  tree id = maybe_get_identifier (fname), fndecl = NULL_TREE;
+  
+  /* Only call `get_function_decl_p' if it has been set by the
+     language front end. */
+  if (get_function_decl_p)
+    {
+      /* If our inline replacement hasn't been defined for some
+	 reason, don't do the optimization.  */
+      if (!id)
+	{
+	  warning ("Could not find a definition for `%s'", fname);
+	  return 0;
+	}
+      
+      fndecl = get_function_decl_p (id);
+      if (!fndecl)
+	warning ("Could not find the FUNCTION_DECL for `%s'", fname);
+    }
+
+  return fndecl;
+}
+
+/* Create a FUNCTION_DECL to call an extern int function. */
+static void
+build_extern_function_decl (fn, name)
+  tree *fn;
+  const char *name;
+{
+  tree fntype;
+  
+  /* This was copied from except.c, I don't know if all this is
+     necessary in this context or not.  */
+  *fn = get_identifier (name);
+  push_obstacks_nochange ();
+  end_temporary_allocation ();
+  fntype = build_pointer_type (integer_type_node);
+  fntype = build_function_type (fntype, NULL_TREE);
+  *fn = build_decl (FUNCTION_DECL, *fn, fntype);
+  ggc_add_tree_root (fn, 1);
+  DECL_EXTERNAL (*fn) = 1;
+  TREE_PUBLIC (*fn) = 1;
+  DECL_ARTIFICIAL (*fn) = 1;
+  make_decl_rtl (*fn, NULL_PTR, 1);
+  assemble_external (*fn);
+  pop_obstacks ();
+}
+
+/* If we are not optimizing for size, we attempt to transform fputc()
+   into the expansion of the putc() macro which should be contained in
+   an inline function defined for us and named in `fputc_via_putc'. */
+static rtx
+expand_builtin_fputc (arglist)
+  tree arglist;
+{
+  tree call_expr;
+  static tree fn;
+
+  /* Try to lookup the replacement function's FUNCTION_DECL.  If we
+     don't find it, return zero to indicate we failed to optimize.
+     Once we've found it, don't do the lookup again. */
+  if (!fn && ! (fn = call_get_function_decl (fputc_via_putc)))
+    return 0;
+  
+  /* Verify the arguments in the original call to fputc. */
+  if (arglist == 0
+      || (TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE)
+      || TREE_CHAIN (arglist) == 0
+      || (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist))))
+	  != POINTER_TYPE))
+    return 0;
+  
+  /* If -Os, then don't do this. */
+  if (optimize_size)
+    return 0;
+
+  call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
+  call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
+		     call_expr, arglist, NULL_TREE);
+  TREE_SIDE_EFFECTS (call_expr) = 1;
+  return expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
+}
+
+/* If the string passed to fputs is a constant and is one character
+   long, we attempt to transform this call into __builtin_fputc(). */
+static rtx
+expand_builtin_fputs (arglist)
+  tree arglist;
+{
+  tree call_expr, len;
+  static tree fn;
+
+  /* Try to lookup the replacement function's FUNCTION_DECL.  If we
+     don't find it, return zero to indicate we failed to optimize.
+     Once we've found it, don't do the lookup again. */
+  if (!fn && ! (fn = call_get_function_decl (fputs_string_via_fputc)))
+    return 0;
+
+  /* Verify the arguments in the original call to fputs. */
+  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;
+
+  /* Get the length of the string passed to fputs. */
+  len = c_strlen (TREE_VALUE (arglist));
+  
+  /* If the length != 1, punt. */
+  if (len == 0 || TREE_INT_CST_LOW (len) != 1)
+    return 0;
+
+  call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
+  call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
+		     call_expr, arglist, NULL_TREE);
+  TREE_SIDE_EFFECTS (call_expr) = 1;
+  return expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
+}
+
+/* Check an arglist to *printf for problems.  The arglist should start
+   at the format specifier, with the remaining arguments immediately
+   following it. */
+static int
+is_valid_printf_arglist (arglist)
+  tree arglist;
+{
+  /* Save this value so we can restore it later. */
+  int SAVE_check_diagnostics_silently = check_diagnostics_silently;
+
+  /* If we can't check the format, be safe and return false. */
+  if (!check_function_format_p)
+    return 0;
+  
+  /* Don't be noisy when calling `check_function_format_p'. */
+  check_diagnostics_silently = 1;
+  diagnostic_occurred = 0;
+
+  /* Check to make sure there are no format specifier errors. */
+  check_function_format_p (maybe_get_identifier("printf"), NULL_TREE, arglist);
+
+  /* Restore the value of `check_diagnostics_silently'. */
+  check_diagnostics_silently = SAVE_check_diagnostics_silently;
+
+  /* If calling `check_function_format_p' produces a warning, we
+     return false, otherwise we return true. */
+  return ! diagnostic_occurred;
+}
+
+/* If the string passed to printf can be resolved at compile time,
+   then pass it to fputs/fputc/putc.
+
+   Also handle these format specifiers:
+   "%s\n" -> puts
+   "%s" -> fputs
+   "%c" -> fputc/putc.  */
+static rtx
+expand_builtin_printf (arglist)
+  tree arglist;
+{
+  static tree fn_s, fn_c, fn_c2, fn_p;
+  tree call_expr, fn;
+  tree format_arg, stripped_string;
+
+  /* Try to lookup the replacement function's FUNCTION_DECL.  If we
+     don't find it, return zero to indicate we failed to optimize.
+     Once we've found it, don't do the lookup again. */
+  if (!fn_s && ! (fn_s = call_get_function_decl (printf_via_fputs)))
+    return 0;
+  if (!fn_c && ! (fn_c = call_get_function_decl (printf_string_via_fputc)))
+    return 0;
+  if (!fn_c2 && ! (fn_c2 = call_get_function_decl (printf_char_via_fputc)))
+    return 0;
+  /* Static lookup, only done once. */
+  if (fn_p == NULL_TREE)
+    build_extern_function_decl (&fn_p, "puts");
+
+  /* Verify the required arguments in the original call to printf. */
+  if (arglist == 0
+      || (TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE))
+    return 0;
+  
+  /* Check the specifier vs. the parameters. */
+  if (!is_valid_printf_arglist (arglist))
+    return 0;
+  
+  format_arg = TREE_VALUE (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 was "%s\n", call puts with the second arg. */
+  if (strcmp (TREE_STRING_POINTER (stripped_string), "%s\n") == 0)
+    {
+      arglist = TREE_CHAIN (arglist);
+      fn = fn_p;
+    }
+  else if (strcmp (TREE_STRING_POINTER (stripped_string), "%s") == 0)
+    {
+      arglist = TREE_CHAIN (arglist);
+      fn = fn_s;
+    }
+  else if (strcmp (TREE_STRING_POINTER (stripped_string), "%c") == 0)
+    {
+      arglist = TREE_CHAIN (arglist);
+      fn = fn_c2;
+    }
+  else
+    {
+      /* We can't handle anything with % args or %% right now. */
+      if (strchr (TREE_STRING_POINTER (stripped_string), '%'))
+	return 0;
+      
+      /* If the resulting constant string has a length of 1, call fputc,
+	 otherwise call fputs. */
+      if (strlen (TREE_STRING_POINTER (stripped_string)) == 1)
+	fn = fn_c;
+      else
+	fn = fn_s;
+    }
+  
+  call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
+  call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
+		     call_expr, arglist, NULL_TREE);
+  TREE_SIDE_EFFECTS (call_expr) = 1;
+  return expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
+}
+
+/* If the string passed to fprintf can be resolved at compile time,
+   then pass it to fputs/fputc/putc.
+
+   Also handle these format specifiers:
+   "%s" -> fputs
+   "%c" -> fputc/putc.  */
+static rtx
+expand_builtin_fprintf (arglist)
+  tree arglist;
+{
+  static tree fn_s, fn_c, fn_c2;
+  tree call_expr, fn;
+  tree format_arg, stripped_string;
+
+  /* Try to lookup the replacement function's FUNCTION_DECL.  If we
+     don't find it, return zero to indicate we failed to optimize.
+     Once we've found it, don't do the lookup again. */
+  if (!fn_s && ! (fn_s = call_get_function_decl ("__builtin_fputs")))
+    return 0;
+  if (!fn_c && ! (fn_c = call_get_function_decl (fputs_string_via_fputc)))
+    return 0;
+  if (!fn_c2 && ! (fn_c2 = call_get_function_decl (fputs_char_via_fputc)))
+    return 0;
+
+  /* Verify the required arguments in the original call to fprintf. */
+  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 was "%s", call fputs with the third arg. */
+  if (strcmp (TREE_STRING_POINTER (stripped_string), "%s") == 0)
+    {
+      tree newarglist;
+      
+      newarglist = TREE_CHAIN (TREE_CHAIN (arglist));
+      TREE_CHAIN (newarglist) = arglist;
+      TREE_CHAIN (TREE_CHAIN (newarglist)) = 0;
+      arglist = newarglist;
+
+      fn = fn_s;
+    }
+  else if (strcmp (TREE_STRING_POINTER (stripped_string), "%c") == 0)
+    {
+      tree newarglist;
+      
+      newarglist = TREE_CHAIN (TREE_CHAIN (arglist));
+      TREE_CHAIN (newarglist) = arglist;
+      TREE_CHAIN (TREE_CHAIN (newarglist)) = 0;
+      arglist = newarglist;
+
+      fn = fn_c2;
+    }
+  else
+    {
+      tree newarglist;
+      
+      /* We can't handle anything with % args or %% right now. */
+      if (strchr (TREE_STRING_POINTER (stripped_string), '%'))
+	return 0;
+      
+      /* If the resulting constant string has a length of 1, call fputc,
+	 otherwise call fputs. */
+      if (strlen (TREE_STRING_POINTER (stripped_string)) == 1)
+	fn = fn_c;
+      else
+	fn = fn_s;
+
+      /* The arguments of fputc/fputs should be the first and second
+         args of fprintf reversed. */
+      newarglist = TREE_CHAIN (arglist);
+      TREE_CHAIN (newarglist) = arglist;
+      TREE_CHAIN (TREE_CHAIN (newarglist)) = 0;
+      arglist = newarglist;
+    }
+  
+  call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
+  call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
+		     call_expr, arglist, NULL_TREE);
+  TREE_SIDE_EFFECTS (call_expr) = 1;
+  return expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
+}
+
+/* If the string passed to sprintf can be resolved at compile time,
+   then pass it to memcpy.
+
+   Also handle these format specifiers:
+   "%s"  */
+static rtx
+expand_builtin_sprintf (arglist)
+  tree arglist;
+{
+  static tree fn;
+  tree call_expr;
+  tree format_arg, stripped_string;
+
+  /* Try to lookup the replacement function's FUNCTION_DECL.  If we
+     don't find it, return zero to indicate we failed to optimize.
+     Once we've found it, don't do the lookup again. */
+  if (!fn && ! (fn = call_get_function_decl (sprintf_via_memcpy)))
+    return 0;
+
+  /* Verify the required arguments in the original call to sprintf. */
+  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);
+
+  {
+    tree len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)));
+
+    if (len == 0)
+      return 0;
+
+    /* We can't handle anything with % args or %% right now. */
+    if (strchr (TREE_STRING_POINTER (stripped_string), '%'))
+      return 0;
+
+    /* Add one to account for NULL. */
+    len = size_binop (PLUS_EXPR, len, integer_one_node);
+    chainon (arglist, build_tree_list (NULL_TREE, len));
+  }
+  
+  call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
+  call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
+		     call_expr, arglist, NULL_TREE);
+  TREE_SIDE_EFFECTS (call_expr) = 1;
+  return expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
+}
 
 /* Expand an expression EXP that calls a built-in function,
    with result going to TARGET if that's convenient
@@ -2271,7 +2677,10 @@ expand_builtin (exp, target, subtarget, 
 	  || fcode == BUILT_IN_FSQRT || fcode == BUILT_IN_MEMSET
 	  || fcode == BUILT_IN_MEMCPY || fcode == BUILT_IN_MEMCMP
 	  || fcode == BUILT_IN_STRLEN || fcode == BUILT_IN_STRCPY
-	  || fcode == BUILT_IN_STRCMP || fcode == BUILT_IN_FFS))
+	  || fcode == BUILT_IN_STRCMP || fcode == BUILT_IN_FFS
+	  || fcode == BUILT_IN_FPUTC || fcode == BUILT_IN_FPUTS
+	  || fcode == BUILT_IN_PRINTF || fcode == BUILT_IN_FPRINTF
+	  || fcode == BUILT_IN_SPRINTF))
     return expand_call (exp, target, ignore);
 
   switch (fcode)
@@ -2475,6 +2884,36 @@ expand_builtin (exp, target, subtarget, 
 	error ("__builtin_trap not supported by this target");
       emit_barrier ();
       return const0_rtx;
+
+    case BUILT_IN_FPUTC:
+      target = expand_builtin_fputc (arglist);
+      if (target)
+	return target;
+      break;
+
+    case BUILT_IN_FPUTS:
+      target = expand_builtin_fputs (arglist);
+      if (target)
+	return target;
+      break;
+
+    case BUILT_IN_PRINTF:
+      target = expand_builtin_printf (arglist);
+      if (target)
+	return target;
+      break;
+      
+    case BUILT_IN_FPRINTF:
+      target = expand_builtin_fprintf (arglist);
+      if (target)
+	return target;
+      break;
+      
+    case BUILT_IN_SPRINTF:
+      target = expand_builtin_sprintf (arglist);
+      if (target)
+	return target;
+      break;
 
       /* Various hooks for the DWARF 2 __throw routine.  */
     case BUILT_IN_UNWIND_INIT:
diff -rup orig/egcs-CVS19991221/gcc/c-common.c egcs-CVS19991221/gcc/c-common.c
--- orig/egcs-CVS19991221/gcc/c-common.c	Fri Dec 10 07:36:38 1999
+++ egcs-CVS19991221/gcc/c-common.c	Tue Dec 21 18:38:41 1999
@@ -1314,10 +1314,18 @@ init_function_format_info ()
 			  printf_format_type, 2, 0);
   record_function_format (get_identifier ("strftime"), NULL_TREE,
 			  strftime_format_type, 3, 0);
+  record_function_format (get_identifier ("__builtin_printf"), NULL_TREE,
+			  printf_format_type, 1, 2);
+  record_function_format (get_identifier ("__builtin_fprintf"), NULL_TREE,
+			  printf_format_type, 2, 3);
+  record_function_format (get_identifier ("__builtin_sprintf"), NULL_TREE,
+			  printf_format_type, 2, 3);
 
   record_international_format (get_identifier ("gettext"), NULL_TREE, 1);
   record_international_format (get_identifier ("dgettext"), NULL_TREE, 2);
   record_international_format (get_identifier ("dcgettext"), NULL_TREE, 2);
+
+  check_function_format_p = check_function_format;
 }
 
 /* Record information for argument format checking.  FUNCTION_IDENT is
@@ -3509,6 +3517,7 @@ c_common_nodes_and_builtins (cplus_mode,
     int cplus_mode, no_builtins, no_nonansi_builtins;
 {
   tree temp;
+  tree fputc_ftype, fputs_ftype, printf_ftype, fprintf_ftype, sprintf_ftype;
   tree memcpy_ftype, memset_ftype, strlen_ftype;
   tree endlink, int_endlink, double_endlink, unsigned_endlink;
   tree sizetype_endlink;
@@ -3632,6 +3641,43 @@ c_common_nodes_and_builtins (cplus_mode,
 						 tree_cons (NULL_TREE,
 							    sizetype,
 							    endlink))));
+  /* Prototype for fputc. */
+  fputc_ftype
+    = build_function_type (integer_type_node,
+			   tree_cons (NULL_TREE, integer_type_node,
+				      tree_cons (NULL_TREE,
+						 ptr_type_node,
+						 endlink)));
+
+  /* Prototype for fputs. */
+  fputs_ftype
+    = build_function_type (integer_type_node,
+			   tree_cons (NULL_TREE, const_string_type_node,
+				      tree_cons (NULL_TREE,
+						 ptr_type_node,
+						 endlink)));
+
+  /* Prototype for printf. */
+  printf_ftype
+    = build_function_type (integer_type_node,
+			   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)));
+
+  /* Prototype for sprintf. */
+  sprintf_ftype
+    = build_function_type (integer_type_node,
+			   tree_cons (NULL_TREE, string_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);
@@ -3779,6 +3825,16 @@ c_common_nodes_and_builtins (cplus_mode,
 		    BUILT_IN_COS, BUILT_IN_NORMAL, "cos");
   builtin_function ("__builtin_cosl", ldouble_ftype_ldouble, 
 		    BUILT_IN_COS, BUILT_IN_NORMAL, "cosl");
+  builtin_function ("__builtin_fputc", fputc_ftype, 
+		    BUILT_IN_FPUTC, BUILT_IN_NORMAL, "fputc");
+  builtin_function ("__builtin_fputs", fputs_ftype, 
+		    BUILT_IN_FPUTS, BUILT_IN_NORMAL, "fputs");
+  builtin_function ("__builtin_printf", printf_ftype, 
+		    BUILT_IN_PRINTF, BUILT_IN_NORMAL, "printf");
+  builtin_function ("__builtin_fprintf", fprintf_ftype, 
+		    BUILT_IN_FPRINTF, BUILT_IN_NORMAL, "fprintf");
+  builtin_function ("__builtin_sprintf", sprintf_ftype, 
+		    BUILT_IN_SPRINTF, BUILT_IN_NORMAL, "sprintf");
 
   if (! no_builtins)
     {
diff -rup orig/egcs-CVS19991221/gcc/c-lex.c egcs-CVS19991221/gcc/c-lex.c
--- orig/egcs-CVS19991221/gcc/c-lex.c	Thu Dec  2 07:35:58 1999
+++ egcs-CVS19991221/gcc/c-lex.c	Tue Dec 21 18:38:41 1999
@@ -168,6 +168,7 @@ static int readescape			PROTO((int *));
 static void parse_float			PROTO((PTR));
 static void extend_token_buffer_to	PROTO((int));
 static int read_line_number		PROTO((int *));
+static tree get_function_decl		PROTO((tree));
 
 /* Do not insert generated code into the source, instead, include it.
    This allows us to build gcc automatically even for targets that
@@ -257,6 +258,8 @@ init_parse (filename)
   init_lex ();
   init_pragma ();
 
+  get_function_decl_p = get_function_decl;
+
   return filename;
 }
 
@@ -2453,4 +2456,11 @@ set_yydebug (value)
 #else
   warning ("YYDEBUG not defined.");
 #endif
+}
+
+static tree
+get_function_decl (identifier)
+  tree identifier;
+{
+  return IDENTIFIER_GLOBAL_VALUE (identifier);
 }
diff -rup orig/egcs-CVS19991221/gcc/cp/lex.c egcs-CVS19991221/gcc/cp/lex.c
--- orig/egcs-CVS19991221/gcc/cp/lex.c	Wed Dec 15 22:54:13 1999
+++ egcs-CVS19991221/gcc/cp/lex.c	Tue Dec 21 18:38:41 1999
@@ -92,6 +92,7 @@ static int read_line_number PROTO((int *
 static int token_getch PROTO ((void));
 static void token_put_back PROTO ((int));
 static void mark_impl_file_chain PROTO ((void *));
+static tree get_function_decl PROTO ((tree));
 
 /* Given a file name X, return the nondirectory portion.
    Keep in mind that X can be computed more than once.  */
@@ -928,6 +929,9 @@ init_parse (filename)
   ggc_add_tree_root (&filename_times, 1);
   ggc_add_root (&impl_file_chain, 1, sizeof (impl_file_chain),
 		mark_impl_file_chain);
+
+  get_function_decl_p = get_function_decl;
+  
   return filename;
 }
 
@@ -4889,4 +4893,11 @@ cp_type_qual_from_rid (rid)
 
   my_friendly_abort (0);
   return TYPE_UNQUALIFIED;
+}
+
+static tree
+get_function_decl (identifier)
+  tree identifier;
+{
+  return BINDING_VALUE (IDENTIFIER_NAMESPACE_BINDINGS (identifier));
 }
diff -rup orig/egcs-CVS19991221/gcc/diagnostic.c egcs-CVS19991221/gcc/diagnostic.c
--- orig/egcs-CVS19991221/gcc/diagnostic.c	Fri Dec 17 20:45:40 1999
+++ egcs-CVS19991221/gcc/diagnostic.c	Tue Dec 21 18:38:41 1999
@@ -485,6 +485,12 @@ int
 count_error (warningp)
      int warningp;
 {
+  if (check_diagnostics_silently)
+    {
+      diagnostic_occurred = 1;
+      return 0;
+    }
+
   if (warningp && inhibit_warnings)
     return 0;
 
diff -rup orig/egcs-CVS19991221/gcc/ginclude/stdio-opt.h egcs-CVS19991221/gcc/ginclude/stdio-opt.h
--- orig/egcs-CVS19991221/gcc/ginclude/stdio-opt.h	Wed Dec 22 10:35:33 1999
+++ egcs-CVS19991221/gcc/ginclude/stdio-opt.h	Tue Dec 21 18:38:41 1999
@@ -0,0 +1,124 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you include this header file into source
+   files compiled by GCC, this header file does not by itself cause
+   the resulting executable to be covered by the GNU General Public
+   License.  This exception does not however invalidate any other
+   reasons why the executable file might be covered by the GNU General
+   Public License.  */
+
+/* The purpose of this file is to provide inline replacements for
+   certain stdio functions when we detect they are optimizable.  The
+   reason we provide these is to maintain the same return value and
+   function signature of the original builtin we are replacing.  We
+   also need the definition of stdout when replacing printf with fputs
+   or fputc.
+
+   We should be able to automatically handle the following conversions
+   within the compiler itself:
+
+   fprintf (stream, string) -> fputs (string, stream)
+   fprintf (stream, "%s", string) -> fputs (string, stream)
+   printf ("%s\n", string) -> puts (string)
+
+   The other builtin conversions use the definitions below.
+*/
+
+#ifndef __GCC_STDIO_OPTIMIZE__
+#define __GCC_STDIO_OPTIMIZE__
+
+#ifndef __GNUC__
+ #error "This file is only used by GCC."
+#endif
+
+#include <stdio.h>
+
+#if defined (__OPTIMIZE__)
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#if ! defined(__OPTIMIZE_SIZE__) && defined (putc)
+/* We convert fputc -> putc when !optimize_size.  Only do this if putc
+   is a macro. */
+extern __inline__ int
+__gcc_builtin_fputc_via_putc (int c, FILE *stream)
+{
+  return putc (c, stream);
+}
+#endif /* ! __OPTIMIZE_SIZE__ && putc */
+
+/* We use this function when we want fputc to mimic the return value
+   of fputs.  */
+extern __inline__ int
+__gcc_builtin_fputs_char_via_fputc (int c, FILE *stream)
+{
+  return __builtin_fputc (c, stream) == EOF ? EOF : 1;
+}
+
+/* We use this when `fputs(string, stream)' is one character long.
+   Note, we must replicate the return value behavior of fputs.  */
+extern __inline__ int
+__gcc_builtin_fputs_string_via_fputc (const char *string, FILE *stream)
+{
+  return __gcc_builtin_fputs_char_via_fputc (string[0], stream);
+}
+
+/* We use this if we can calculate `printf(string)' at compile time.
+   We need this as an inline to get the definition of stdout. */
+extern __inline__ int
+__gcc_builtin_printf_via_fputs (const char *string)
+{
+  return fputs (string, stdout);
+}
+
+/* We use this if we can calculate `printf(string)' at compile time.
+   We need this as an inline to get the definition of stdout. */
+extern __inline__ int
+__gcc_builtin_printf_string_via_fputc (const char *string)
+{
+  return __gcc_builtin_fputs_string_via_fputc (string, stdout);
+}
+
+/* We use this if we can calculate `printf("%c")' at compile time.
+   We need this as an inline to get the definition of stdout. */
+extern __inline__ int
+__gcc_builtin_printf_char_via_fputc (int c)
+{
+  return __builtin_fputc (c, stdout);
+}
+
+/* We use this if we can calculate `sprintf(buf, string)' at compile time.
+   Note we must replicate the return value behavior of sprintf. */
+extern __inline__ int
+__gcc_builtin_sprintf_via_memcpy (char *buf, const char *string, size_t len)
+{
+  __builtin_memcpy (buf, string, len);
+  return len;
+}
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __OPTIMIZE__ */
+
+#endif /* ! __GCC_STDIO_OPTIMIZE__ */
diff -rup orig/egcs-CVS19991221/gcc/toplev.c egcs-CVS19991221/gcc/toplev.c
--- orig/egcs-CVS19991221/gcc/toplev.c	Mon Dec 20 15:00:15 1999
+++ egcs-CVS19991221/gcc/toplev.c	Tue Dec 21 18:38:41 1999
@@ -1180,6 +1180,14 @@ target_options [] = TARGET_OPTIONS;
 
 /* Options controlling warnings */
 
+/* When nonzero, check for warnings/errors but do so silently.  This
+   is different from inhibit_warnings, which avoids incrementing
+   warningcount/errorcount at all. */
+int check_diagnostics_silently = 0;
+
+/* When checking diagnostics silently, set this if a diagnostic occured. */
+int diagnostic_occurred = 0;
+
 /* Don't print warning messages.  -w.  */
 
 int inhibit_warnings = 0;
diff -rup orig/egcs-CVS19991221/gcc/toplev.h egcs-CVS19991221/gcc/toplev.h
--- orig/egcs-CVS19991221/gcc/toplev.h	Wed Nov 10 12:32:07 1999
+++ egcs-CVS19991221/gcc/toplev.h	Tue Dec 21 18:38:42 1999
@@ -130,6 +130,8 @@ extern void check_global_declarations   
 extern int errorcount;
 extern int warningcount;
 extern int sorrycount;
+extern int check_diagnostics_silently;
+extern int diagnostic_occurred;
 
 extern const char *progname;
 #endif /* __GCC_TOPLEV_H */
diff -rup orig/egcs-CVS19991221/gcc/tree.c egcs-CVS19991221/gcc/tree.c
--- orig/egcs-CVS19991221/gcc/tree.c	Tue Dec 21 07:36:22 1999
+++ egcs-CVS19991221/gcc/tree.c	Tue Dec 21 18:38:42 1999
@@ -243,6 +243,16 @@ static int next_decl_uid;
 /* Unique id for next type created.  */
 static int next_type_uid = 1;
 
+/* Pointer to function to check the format of printf, etc.  This is
+   used in builtins.c. */
+
+void (*check_function_format_p) PROTO ((tree, tree, tree)) = 0;
+
+/* Pointer to function which, when given an identifier, returns a
+   FUNCTION_DECL from a language dependent location in the tree. */
+
+tree (*get_function_decl_p) PROTO ((tree)) = 0;
+
 /* The language-specific function for alias analysis.  If NULL, the
    language does not do any special alias analysis.  */
 int (*lang_get_alias_set) PROTO((tree));
diff -rup orig/egcs-CVS19991221/gcc/tree.h egcs-CVS19991221/gcc/tree.h
--- orig/egcs-CVS19991221/gcc/tree.h	Fri Dec 17 07:36:04 1999
+++ egcs-CVS19991221/gcc/tree.h	Tue Dec 21 18:38:42 1999
@@ -115,6 +115,11 @@ enum built_in_function
   BUILT_IN_SETJMP,
   BUILT_IN_LONGJMP,
   BUILT_IN_TRAP,
+  BUILT_IN_FPUTC,
+  BUILT_IN_FPUTS,
+  BUILT_IN_PRINTF,
+  BUILT_IN_FPRINTF,
+  BUILT_IN_SPRINTF,
 
   /* Various hooks for the DWARF 2 __throw routine.  */
   BUILT_IN_UNWIND_INIT,
@@ -2059,6 +2064,15 @@ extern const char * const language_strin
 extern tree builtin_function			PROTO((const char *, tree, int,
 						       enum built_in_class,
 						       const char *));
+
+/* Pointer to function to check the format of printf, etc.  This is
+   used in builtins.c. */
+extern void (*check_function_format_p) PROTO ((tree, tree, tree));
+
+/* Pointer to function which, when given an identifier, returns a
+   FUNCTION_DECL from a language dependent location in the tree. */
+
+extern tree (*get_function_decl_p) PROTO ((tree));
 
 /* In tree.c */
 extern char *perm_calloc			PROTO((int, long));


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