This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Patch to do printf transformations in the frontend
- To: gcc-patches at gcc dot gnu dot org
- Subject: Patch to do printf transformations in the frontend
- From: "Kaveh R. Ghazi" <ghazi at caip dot rutgers dot edu>
- Date: Thu, 5 Oct 2000 15:49:12 -0400 (EDT)
- Cc: mark at codesourcery dot com, rth at cygnus dot com
As had been suggested to me, I went about moving the printf expansion
into the language frontend(s). While doing this I had several design
considerations.
1. I wanted to continue using the "builtin" mechanism because it
provides a well established (i.e. tested) framework and known
semantics WRT the __builtin_ prefix, predeclaring prototypes for
builtins, ensuring we only operate on global extern function and
also getting turned off when in freestanding mode.
2. I wanted to have as much of the code as possible reside in
c-common.c to avoid having to write the expander more than once.
While examining the existing framework, I noticed a couple of things
1. There exists a built_in_class called BUILT_IN_FRONTEND which
doesn't appear to be currently handled, noticed or used anywhere in
the compiler. However, its name suggests the designer's purpose was
for something along the lines which I am trying to attempt.
2. There exists a mechanism for the backend to ask the frontend to
handle expansions, namely indirect calls through `lang_expand_expr'.
In C land, this gets into c_expand_expr and in C++ it's
cplus_expand_expr (which itself calls c_expand_expr.)
Given these things, I fleshed out what seems to me an existing and
natural, but unfinished, aspect of the builtin framework. Namely
allowing builtins to be expanded by a frontend. I did the following:
1. Added a new function called c_expand_builtins of obvious purpose.
2. Within the existing function c_expand_expr, if an expression is a
CALL_EXPR to a builtin function of class type BUILT_IN_FRONTEND,
pass it to c_expand_builtins.
3. Within expand_expr, when expanding a builtin function, if the
class type is BUILT_IN_FRONTEND, call lang_expand_expr, otherwise
call expand_builtin as it currently does.
4. Move the expand_builtin_printf function from builtins.c to
c-common.c with appropriate changes, cleanup and the additional
printf("string\n")->puts("string") transformation.
The patch below is what resulted. Bootstrapped on solaris2.7, no
regressions. Okay to install?
Thanks,
--Kaveh
2000-10-05 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* builtins.c (is_valid_printf_arglist, expand_builtin_printf):
Move functions from here ...
* c-common.c (is_valid_printf_arglist, c_expand_builtin_printf):
... to here.
(c_expand_builtin): New function.
(init_function_format_info): Don't set `check_function_format_ptr'.
(c_common_nodes_and_builtins): Set built_in_class type for
printf/__builtin_printf to BUILT_IN_FRONTEND.
(c_expand_expr): Handle CALL_EXPRs that are front-end builtins.
* c-common.h (build_function_call): Declare.
* expr.c (expand_expr): Pass builtins with class BUILT_IN_FRONTEND
to `lang_expand_expr' rather than `expand_builtin'.
* tree.c (check_function_format_ptr): Delete.
* tree.h (check_function_format_ptr): Likewise.
diff -rup orig/egcs-CVS20001002/gcc/builtins.c egcs-CVS20001002/gcc/builtins.c
--- orig/egcs-CVS20001002/gcc/builtins.c Tue Sep 26 18:12:17 2000
+++ egcs-CVS20001002/gcc/builtins.c Tue Oct 3 00:42:16 2000
@@ -111,8 +111,6 @@ static rtx expand_builtin_strlen PARAMS
static rtx expand_builtin_alloca PARAMS ((tree, rtx));
static rtx expand_builtin_ffs PARAMS ((tree, rtx, rtx));
static rtx expand_builtin_frame_address PARAMS ((tree));
-static int is_valid_printf_arglist PARAMS ((tree));
-static rtx expand_builtin_printf PARAMS ((tree, int));
static rtx expand_builtin_fputs PARAMS ((tree, int));
static tree stabilize_va_list PARAMS ((tree, int));
static rtx expand_builtin_expect PARAMS ((tree, rtx));
@@ -2398,120 +2396,6 @@ expand_builtin_fputs (arglist, ignore)
VOIDmode, EXPAND_NORMAL);
}
-/* 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. */
- const int SAVE_pedantic = pedantic;
- int diagnostic_occurred = 0;
-
- /* If we can't check the format, be safe and return false. */
- if (!check_function_format_ptr)
- return 0;
-
- /* Set this to a known value so the user setting won't affect code
- generation. */
- pedantic = 1;
- /* Check to make sure there are no format specifier errors. */
- check_function_format_ptr (&diagnostic_occurred,
- maybe_get_identifier("printf"),
- NULL_TREE, arglist);
-
- /* Restore the value of `pedantic'. */
- pedantic = SAVE_pedantic;
-
- /* If calling `check_function_format_ptr' produces a warning, we
- return false, otherwise we return true. */
- return ! diagnostic_occurred;
-}
-
-/* If the arguments passed to printf are suitable for optimizations,
- we attempt to transform the call. */
-static rtx
-expand_builtin_printf (arglist, ignore)
- tree arglist;
- int ignore;
-{
- tree fn_putchar = built_in_decls[BUILT_IN_PUTCHAR],
- fn_puts = built_in_decls[BUILT_IN_PUTS];
- tree call_expr, fn;
- tree 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_putchar || !fn_puts)
- return 0;
-
- /* Verify the required arguments in the original call. */
- 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 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\n", call __builtin_puts(arg2). */
- if (strcmp (TREE_STRING_POINTER (stripped_string), "%s\n") == 0)
- {
- arglist = TREE_CHAIN (arglist);
- fn = fn_puts;
- }
- /* If the format specifier was "%c", call __builtin_putchar (arg2). */
- else if (strcmp (TREE_STRING_POINTER (stripped_string), "%c") == 0)
- {
- arglist = TREE_CHAIN (arglist);
- fn = fn_putchar;
- }
- else
- {
- /* We can't handle anything else with % args or %% ... yet. */
- if (strchr (TREE_STRING_POINTER (stripped_string), '%'))
- return 0;
-
- /* If the resulting constant string has a length of 1, call
- putchar. Note, TREE_STRING_LENGTH includes the terminating
- NULL in its count. */
- if (TREE_STRING_LENGTH (stripped_string) == 2)
- {
- /* Given printf("c"), (where c is any one character,)
- convert "c"[0] to an int and pass that to the replacement
- function. */
- arglist = build_int_2 (TREE_STRING_POINTER (stripped_string)[0], 0);
- arglist = build_tree_list (NULL_TREE, arglist);
-
- fn = fn_putchar;
- }
- else
- /* 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;
- }
-
- 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, (ignore ? const0_rtx : NULL_RTX),
- VOIDmode, EXPAND_NORMAL);
-}
-
/* Expand a call to __builtin_expect. We return our argument and
emit a NOTE_INSN_EXPECTED_VALUE note. */
@@ -2810,12 +2694,6 @@ expand_builtin (exp, target, subtarget,
case BUILT_IN_FPUTS:
target = expand_builtin_fputs (arglist, ignore);
- if (target)
- return target;
- break;
-
- case BUILT_IN_PRINTF:
- target = expand_builtin_printf (arglist, ignore);
if (target)
return target;
break;
diff -rup orig/egcs-CVS20001002/gcc/c-common.c egcs-CVS20001002/gcc/c-common.c
--- orig/egcs-CVS20001002/gcc/c-common.c Tue Sep 26 18:12:18 2000
+++ egcs-CVS20001002/gcc/c-common.c Tue Oct 3 00:51:49 2000
@@ -1550,6 +1550,10 @@ static int maybe_read_dollar_number PAR
static void finish_dollar_format_checking PARAMS ((int *));
static void check_format_types PARAMS ((int *, format_wanted_type *));
+static int is_valid_printf_arglist PARAMS ((tree));
+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));
/* Initialize the table of functions to perform format checking on.
The ISO C functions are always checked (whether <stdio.h> is
@@ -1615,8 +1619,6 @@ init_function_format_info ()
record_international_format (get_identifier ("dgettext"), NULL_TREE, 2);
record_international_format (get_identifier ("dcgettext"), NULL_TREE, 2);
}
-
- check_function_format_ptr = check_function_format;
}
/* Record information for argument format checking. FUNCTION_IDENT is
@@ -4388,7 +4390,7 @@ c_common_nodes_and_builtins (cplus_mode,
builtin_function ("__builtin_puts", puts_ftype,
BUILT_IN_PUTS, BUILT_IN_NORMAL, "puts");
builtin_function ("__builtin_printf", printf_ftype,
- BUILT_IN_PRINTF, BUILT_IN_NORMAL, "printf");
+ BUILT_IN_PRINTF, BUILT_IN_FRONTEND, "printf");
/* We declare these without argument so that the initial declaration
for these identifiers is a builtin. That allows us to redeclare
them later with argument without worrying about the explicit
@@ -4450,7 +4452,7 @@ c_common_nodes_and_builtins (cplus_mode,
builtin_function ("cosl", ldouble_ftype_ldouble, BUILT_IN_COS,
BUILT_IN_NORMAL, NULL_PTR);
builtin_function ("printf", printf_ftype, BUILT_IN_PRINTF,
- BUILT_IN_NORMAL, NULL_PTR);
+ BUILT_IN_FRONTEND, NULL_PTR);
/* We declare these without argument so that the initial
declaration for these identifiers is a builtin. That allows
us to redeclare them later with argument without worrying
@@ -5028,6 +5030,20 @@ c_expand_expr (exp, target, tmode, modif
}
break;
+ case CALL_EXPR:
+ {
+ if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
+ && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
+ == FUNCTION_DECL)
+ && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
+ && (DECL_BUILT_IN_CLASS (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
+ == BUILT_IN_FRONTEND))
+ return c_expand_builtin (exp, target, tmode, modifier);
+ else
+ abort();
+ }
+ break;
+
default:
abort ();
}
@@ -5111,4 +5127,177 @@ add_c_tree_codes ()
memcpy (tree_code_name + (int) LAST_AND_UNUSED_TREE_CODE,
c_tree_code_name,
(LAST_C_TREE_CODE - (int)LAST_AND_UNUSED_TREE_CODE) * sizeof (char *));
+}
+
+#define CALLED_AS_BUILT_IN(NODE) \
+ (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
+
+static rtx
+c_expand_builtin (exp, target, tmode, modifier)
+ tree exp;
+ rtx target;
+ enum machine_mode tmode;
+ enum expand_modifier modifier;
+{
+ tree type = TREE_TYPE (exp);
+ tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree arglist = TREE_OPERAND (exp, 1);
+ enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+ enum tree_code code = TREE_CODE (exp);
+ const int ignore = (target == const0_rtx
+ || ((code == NON_LVALUE_EXPR || code == NOP_EXPR
+ || code == CONVERT_EXPR || code == REFERENCE_EXPR
+ || code == COND_EXPR)
+ && TREE_CODE (type) == VOID_TYPE));
+
+ if (! optimize && ! CALLED_AS_BUILT_IN (fndecl))
+ return expand_call (exp, target, ignore);
+
+ switch (fcode)
+ {
+ case BUILT_IN_PRINTF:
+ target = c_expand_builtin_printf (arglist, target, tmode,
+ 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)));
+ }
+
+ /* The switch statement above can drop through to cause the function
+ to be called normally. */
+ return expand_call (exp, target, ignore);
+}
+
+/* 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. */
+ const int SAVE_pedantic = pedantic;
+ int diagnostic_occurred = 0;
+
+ /* Set this to a known value so the user setting won't affect code
+ generation. */
+ pedantic = 1;
+ /* Check to make sure there are no format specifier errors. */
+ check_function_format (&diagnostic_occurred,
+ maybe_get_identifier("printf"),
+ NULL_TREE, arglist);
+
+ /* Restore the value of `pedantic'. */
+ pedantic = SAVE_pedantic;
+
+ /* If calling `check_function_format_ptr' produces a warning, we
+ return false, otherwise we return true. */
+ return ! diagnostic_occurred;
+}
+
+/* If the arguments passed to printf are suitable for optimizations,
+ we attempt to transform the call. */
+static rtx
+c_expand_builtin_printf (arglist, target, tmode, modifier, ignore)
+ tree arglist;
+ rtx target;
+ enum machine_mode tmode;
+ enum expand_modifier modifier;
+ int ignore;
+{
+ tree fn_putchar = built_in_decls[BUILT_IN_PUTCHAR],
+ fn_puts = built_in_decls[BUILT_IN_PUTS];
+ 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_putchar || !fn_puts)
+ return 0;
+
+ /* Verify the required arguments in the original call. */
+ 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 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\n", call __builtin_puts(arg2). */
+ if (strcmp (TREE_STRING_POINTER (stripped_string), "%s\n") == 0)
+ {
+ arglist = TREE_CHAIN (arglist);
+ fn = fn_puts;
+ }
+ /* If the format specifier was "%c", call __builtin_putchar (arg2). */
+ else if (strcmp (TREE_STRING_POINTER (stripped_string), "%c") == 0)
+ {
+ arglist = TREE_CHAIN (arglist);
+ fn = fn_putchar;
+ }
+ else
+ {
+ /* We can't handle anything else with % args or %% ... yet. */
+ if (strchr (TREE_STRING_POINTER (stripped_string), '%'))
+ return 0;
+
+ /* If the resulting constant string has a length of 1, call
+ putchar. Note, TREE_STRING_LENGTH includes the terminating
+ NULL in its count. */
+ if (TREE_STRING_LENGTH (stripped_string) == 2)
+ {
+ /* Given printf("c"), (where c is any one character,)
+ convert "c"[0] to an int and pass that to the replacement
+ function. */
+ arglist = build_int_2 (TREE_STRING_POINTER (stripped_string)[0], 0);
+ arglist = build_tree_list (NULL_TREE, arglist);
+
+ fn = fn_putchar;
+ }
+ /* If the resulting constant was "string\n", call
+ __builtin_puts("string"). Ensure "string" has at least one
+ character besides the trailing \n. Note, TREE_STRING_LENGTH
+ includes the terminating NULL in its count. */
+ else if (TREE_STRING_LENGTH (stripped_string) > 2
+ && TREE_STRING_POINTER (stripped_string)
+ [TREE_STRING_LENGTH (stripped_string) - 2] == '\n')
+ {
+ /* Create a NULL-terminated string that's one char shorter
+ than the original, stripping off the trailing '\n'. */
+ const int newlen = TREE_STRING_LENGTH (stripped_string) - 1;
+ char *newstr = (char *) alloca (newlen);
+ memcpy (newstr, TREE_STRING_POINTER (stripped_string), newlen - 1);
+ newstr[newlen - 1] = 0;
+
+ arglist = build_string (newlen, newstr);
+ TREE_TYPE (arglist) =
+ build_type_variant (char_array_type_node, 1, 0);
+ arglist = build_tree_list (NULL_TREE, arglist);
+ fn = fn_puts;
+ }
+ else
+ /* 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);
}
diff -rup orig/egcs-CVS20001002/gcc/c-common.h egcs-CVS20001002/gcc/c-common.h
--- orig/egcs-CVS20001002/gcc/c-common.h Mon Sep 18 23:20:31 2000
+++ egcs-CVS20001002/gcc/c-common.h Tue Oct 3 00:13:29 2000
@@ -683,6 +683,8 @@ extern tree c_add_case_label
tree, tree,
tree));
+extern tree build_function_call PARAMS ((tree, tree));
+
#ifdef RTX_CODE
extern struct rtx_def *c_expand_expr PARAMS ((tree, rtx,
diff -rup orig/egcs-CVS20001002/gcc/expr.c egcs-CVS20001002/gcc/expr.c
--- orig/egcs-CVS20001002/gcc/expr.c Tue Sep 26 18:14:25 2000
+++ egcs-CVS20001002/gcc/expr.c Mon Oct 2 16:12:43 2000
@@ -7156,7 +7156,13 @@ expand_expr (exp, target, tmode, modifie
&& (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
== FUNCTION_DECL)
&& DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
- return expand_builtin (exp, target, subtarget, tmode, ignore);
+ {
+ if (DECL_BUILT_IN_CLASS (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
+ == BUILT_IN_FRONTEND)
+ return (*lang_expand_expr) (exp, original_target, tmode, modifier);
+ else
+ return expand_builtin (exp, target, subtarget, tmode, ignore);
+ }
/* If this call was expanded already by preexpand_calls,
just return the result we got. */
diff -rup orig/egcs-CVS20001002/gcc/tree.c egcs-CVS20001002/gcc/tree.c
--- orig/egcs-CVS20001002/gcc/tree.c Tue Sep 19 14:14:40 2000
+++ egcs-CVS20001002/gcc/tree.c Tue Oct 3 00:45:11 2000
@@ -246,10 +246,6 @@ 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 by the backend, e.g. builtins.c. */
-void (*check_function_format_ptr) PARAMS ((int *, tree, tree, tree)) = 0;
-
/* Here is how primitive or already-canonicalized types' hash
codes are made. */
#define TYPE_HASH(TYPE) ((unsigned long) (TYPE) & 0777777)
diff -rup orig/egcs-CVS20001002/gcc/tree.h egcs-CVS20001002/gcc/tree.h
--- orig/egcs-CVS20001002/gcc/tree.h Tue Sep 19 14:14:42 2000
+++ egcs-CVS20001002/gcc/tree.h Tue Oct 3 00:45:02 2000
@@ -2441,9 +2441,6 @@ extern const char * const language_strin
extern tree builtin_function PARAMS ((const char *, tree, int,
enum built_in_class,
const char *));
-/* Pointer to function to check the format of printf, etc. This is
- used by the backend, e.g. builtins.c. */
-extern void (*check_function_format_ptr) PARAMS ((int *, tree, tree, tree));
/* In tree.c */
extern char *perm_calloc PARAMS ((int, long));