This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: PATCH RFC: stdio optimization [take 2]
- To: gavin at cygnus dot com
- Subject: Re: PATCH RFC: stdio optimization [take 2]
- From: "Kaveh R. Ghazi" <ghazi at caip dot rutgers dot edu>
- Date: Wed, 22 Dec 1999 11:58:20 -0500 (EST)
- Cc: egcs-patches at egcs dot cygnus dot com, law at cygnus dot com, mark at codesourcery dot com
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));