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 add __builtin_printf


This patch adds __builtin_printf.  It uses the same mechanism for
obtaining the FUNCTION_DECL for the replacements as does builtin
fputs, namely declare stub replacement builtins and cache the result.

The patch makes use of the recent updates to the format checking
routines which facilitated retrieving a status rather than issuing a
warning.  I also make sure to set `pedantic' to a known value (and
restore it afterwards) so that its user setting doesn't affect code
generation.

The transformations currently done are:

printf("%s\n", foo) -> puts(foo)
printf("%c", foo) -> putchar(foo)
printf("c") -> putchar('c')  (where `c' is any one character.)

More on the way, but this establishes the framework.

I bootstrapped this on solaris2.7, there was one regression due to a
testsuite entry declaring printf wrong, which conflicted with the
builtin decl.  I fixed that and it was otherwise clean.

I also manually inspected the sparc assembler output to make sure it
DTRT.  By chance I noticed, that tail calls for the replacement
functions weren't being optimized into jumps.  This was due to not
setting target to const0_rtx when the return value wasn't used.  I
fixed that and also fixed it for builtin fputs.

Okay to install?

		Thanks,
		--Kaveh


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

	* builtins.c (is_valid_printf_arglist, expand_builtin_printf): New
	functions.
	(expand_builtin_fputs): Set `target' parameter for `expand_expr'.
	(expand_builtin): Handle BUILT_IN_PUTCHAR, BUILT_IN_PUTS and
	BUILT_IN_PRINTF.

	* builtins.def (BUILT_IN_PUTCHAR, BUILT_IN_PUTS, BUILT_IN_PRINTF):
	New entries.

	* c-common.c (init_function_format_info): Handle __builtin_printf.
	Set `check_function_format_ptr'.
	(c_common_nodes_and_builtins): Set `puts_ftype' and
	`printf_ftype'.  Declare __builtin_putchar, __builtin_puts,
	__builtin_printf and printf.

	* tree.c, tree.h (check_function_format_ptr): Declare.

testsuite:
	* g++.old-deja/g++.other/virtual8.C: Declare printf correctly.


diff -rup orig/egcs-CVS20000918/gcc/builtins.c egcs-CVS20000918/gcc/builtins.c
--- orig/egcs-CVS20000918/gcc/builtins.c	Sun Sep 17 11:00:15 2000
+++ egcs-CVS20000918/gcc/builtins.c	Tue Sep 19 11:00:56 2000
@@ -111,6 +111,8 @@ 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));
@@ -2367,7 +2369,122 @@ expand_builtin_fputs (arglist, ignore)
   call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
 		     call_expr, newarglist, NULL_TREE);
   TREE_SIDE_EFFECTS (call_expr) = 1;
-  return expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
+  return expand_expr (call_expr, (ignore ? const0_rtx : NULL_RTX),
+		      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
@@ -2444,7 +2561,9 @@ expand_builtin (exp, target, subtarget, 
 	  || fcode == BUILT_IN_BCMP || fcode == BUILT_IN_BZERO
 	  || fcode == BUILT_IN_STRLEN || fcode == BUILT_IN_STRCPY
 	  || fcode == BUILT_IN_STRCMP || fcode == BUILT_IN_FFS
-	  || fcode == BUILT_IN_FPUTC || fcode == BUILT_IN_FPUTS))
+	  || fcode == BUILT_IN_PUTCHAR || fcode == BUILT_IN_PUTS
+	  || fcode == BUILT_IN_PRINTF || fcode == BUILT_IN_FPUTC
+	  || fcode == BUILT_IN_FPUTS))
     return expand_call (exp, target, ignore);
 
   switch (fcode)
@@ -2657,11 +2776,19 @@ expand_builtin (exp, target, subtarget, 
       emit_barrier ();
       return const0_rtx;
 
+    case BUILT_IN_PUTCHAR:
+    case BUILT_IN_PUTS:
     case BUILT_IN_FPUTC:
       break;
       
     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-CVS20000918/gcc/builtins.def egcs-CVS20000918/gcc/builtins.def
--- orig/egcs-CVS20000918/gcc/builtins.def	Tue Sep 12 10:53:33 2000
+++ egcs-CVS20000918/gcc/builtins.def	Mon Sep 18 23:47:52 2000
@@ -59,6 +59,9 @@ DEF_BUILTIN(BUILT_IN_LONGJMP)
 DEF_BUILTIN(BUILT_IN_TRAP)
 
   /* Stdio builtins.  */
+DEF_BUILTIN(BUILT_IN_PUTCHAR)
+DEF_BUILTIN(BUILT_IN_PUTS)
+DEF_BUILTIN(BUILT_IN_PRINTF)
 DEF_BUILTIN(BUILT_IN_FPUTC)
 DEF_BUILTIN(BUILT_IN_FPUTS)
 
diff -rup orig/egcs-CVS20000918/gcc/c-common.c egcs-CVS20000918/gcc/c-common.c
--- orig/egcs-CVS20000918/gcc/c-common.c	Mon Sep 18 23:20:31 2000
+++ egcs-CVS20000918/gcc/c-common.c	Tue Sep 19 00:08:03 2000
@@ -1566,6 +1566,8 @@ init_function_format_info ()
       /* Functions from ISO/IEC 9899:1990.  */
       record_function_format (get_identifier ("printf"), NULL_TREE,
 			      printf_format_type, 1, 2);
+      record_function_format (get_identifier ("__builtin_printf"), NULL_TREE,
+			      printf_format_type, 1, 2);
       record_function_format (get_identifier ("fprintf"), NULL_TREE,
 			      printf_format_type, 2, 3);
       record_function_format (get_identifier ("sprintf"), NULL_TREE,
@@ -1608,6 +1610,8 @@ 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
@@ -4006,7 +4010,7 @@ c_common_nodes_and_builtins (cplus_mode,
 {
   tree temp;
   tree memcpy_ftype, memset_ftype, strlen_ftype;
-  tree bzero_ftype, bcmp_ftype;
+  tree bzero_ftype, bcmp_ftype, puts_ftype, printf_ftype;
   tree endlink, int_endlink, double_endlink, unsigned_endlink;
   tree sizetype_endlink;
   tree ptr_ftype, ptr_ftype_unsigned;
@@ -4162,6 +4166,18 @@ c_common_nodes_and_builtins (cplus_mode,
 						 traditional_cptr_type_node,
 						 traditional_len_endlink)));
 
+  /* Prototype for puts.  */
+  puts_ftype
+    = build_function_type (integer_type_node,
+			   tree_cons (NULL_TREE, const_string_type_node,
+				      endlink));
+
+  /* Prototype for printf.  */
+  printf_ftype
+    = build_function_type (integer_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);
 
@@ -4348,6 +4364,14 @@ 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");
+  built_in_decls[BUILT_IN_PUTCHAR] =
+    builtin_function ("__builtin_putchar", int_ftype_int,
+		      BUILT_IN_PUTCHAR, BUILT_IN_NORMAL, "putchar");
+  built_in_decls[BUILT_IN_PUTS] =
+    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");
   /* 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
@@ -4401,6 +4425,8 @@ c_common_nodes_and_builtins (cplus_mode,
       builtin_function ("cos", double_ftype_double, BUILT_IN_COS,
 			BUILT_IN_NORMAL, NULL_PTR);
       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);
       /* We declare these without argument so that the initial
          declaration for these identifiers is a builtin.  That allows
diff -rup orig/egcs-CVS20000918/gcc/testsuite/g++.old-deja/g++.other/virtual8.C egcs-CVS20000918/gcc/testsuite/g++.old-deja/g++.other/virtual8.C
--- orig/egcs-CVS20000918/gcc/testsuite/g++.old-deja/g++.other/virtual8.C	Thu Jun 22 21:14:34 2000
+++ egcs-CVS20000918/gcc/testsuite/g++.old-deja/g++.other/virtual8.C	Tue Sep 19 11:03:19 2000
@@ -1,4 +1,4 @@
-extern "C" void printf (const char*, ...);
+extern "C" int printf (const char*, ...);
 
 struct A
 {
diff -rup orig/egcs-CVS20000918/gcc/tree.c egcs-CVS20000918/gcc/tree.c
--- orig/egcs-CVS20000918/gcc/tree.c	Sun Sep 17 11:00:36 2000
+++ egcs-CVS20000918/gcc/tree.c	Tue Sep 19 00:08:12 2000
@@ -246,6 +246,10 @@ 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-CVS20000918/gcc/tree.h egcs-CVS20000918/gcc/tree.h
--- orig/egcs-CVS20000918/gcc/tree.h	Sat Sep 16 23:03:54 2000
+++ egcs-CVS20000918/gcc/tree.h	Tue Sep 19 00:08:07 2000
@@ -2441,6 +2441,9 @@ 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]