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]

Patch to do printf transformations in the frontend


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));

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