Patch for builtin fputs (first stdio opt ready for install)

Kaveh R. Ghazi ghazi@caip.rutgers.edu
Thu Apr 13 07:45:00 GMT 2000


I've finally worked out the kinks in the first stdio optimization.
This patch adds support for builtin fputs.  (Others will follow.)  Its
mainly infrastructure for higher level fprintf, etc, and to get wider
testing to make sure the basic scheme works everywhere.  The
optimization itself simply reroutes fputs to fputc when the string is
one character long.  In my own timing tests, this is about 2.9 times
faster on x86-linux.

Regarding the FILE* hackery, for now I've declared fputs without
arguments.  I also figured out why this didn't work with G++ testsuite
before.  The G++ testsuite uses -pedantic-errors which causes int
fputs(...) to conflict with the fully qualified stdio.h definition.
So in c-common.c, I don't activate the builtin in this case.

Bootstraped on x86-linux and sparc-solaris2.7.  Okay to install?

		--Kaveh



2000-04-12  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>

	* builtins.c (call_get_decl, expand_builtin_fputs): New functions.
	(expand_builtin): Handle builtin fputs.

	* builtins.def (BUILT_IN_FPUTS): New.

	* c-common.c (c_common_nodes_and_builtins): Declare builtin fputs.

	* c-lex.c (get_decl): New function.
	(init_parse): Set `get_decl_ptr'.

	* tree.c (get_decl_ptr): New.

	* tree.h (get_decl_ptr): Declare.

cp:
	* lex.c (get_decl): New function.
	(init_parse): Set `get_decl_ptr'.
	
diff -rup orig/egcs-CVS20000411/gcc/builtins.c egcs-CVS20000411/gcc/builtins.c
--- orig/egcs-CVS20000411/gcc/builtins.c	Tue Apr  4 17:26:10 2000
+++ egcs-CVS20000411/gcc/builtins.c	Tue Apr 11 12:06:08 2000
@@ -102,6 +102,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 rtx expand_builtin_fputs		PARAMS ((tree, int));
+static tree call_get_decl		PARAMS ((const char *));
 static tree stabilize_va_list		PARAMS ((tree, int));
 
 /* Return the alignment in bits of EXP, a pointer valued expression.
@@ -2291,6 +2293,97 @@ expand_builtin_ffs (arglist, target, sub
     abort ();
   return target;
 }
+
+/* This function calls get_decl_ptr on the argument and returns the
+   result.  FNAME should be the literal string name of a function for
+   which we want to find the FUNCTION_DECL. */
+static tree
+call_get_decl (fname)
+  const char *fname;
+{
+  tree id = maybe_get_identifier (fname), fndecl = NULL_TREE;
+  
+  /* Only call `get_decl_ptr' if it has been set by the language front end. */
+  if (get_decl_ptr)
+    {
+      /* If our inline replacement hasn't been defined for some
+	 reason, don't do the optimization.  */
+      if (!id)
+	{
+#ifdef TEST_STDIO_OPTS
+	  warning ("Could not find a definition for `%s'", fname);
+#endif
+	  return 0;
+	}
+      
+      fndecl = get_decl_ptr (id);
+#ifdef TEST_STDIO_OPTS
+      if (!fndecl)
+	warning ("Could not find the FUNCTION_DECL for `%s'", fname);
+#endif
+    }
+
+  return fndecl;
+}
+
+/* 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, ignore)
+  tree arglist;
+  int ignore;
+{
+  tree call_expr, len, stripped_string, newarglist;
+  static tree fn;
+
+  /* If the return value is used, don't do the transformation. */
+  if (!ignore)
+    return 0;
+
+  /* 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_decl ("fputc")))
+    return 0;
+
+  /* Verify the arguments in the original call. */
+  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 || compare_tree_int (len, 1))
+    return 0;
+
+  stripped_string = TREE_VALUE (arglist);
+  STRIP_NOPS (stripped_string);
+  if (stripped_string && TREE_CODE (stripped_string) == ADDR_EXPR)
+    stripped_string = TREE_OPERAND (stripped_string, 0);
+
+  /* New argument list transforming fputs(string, stream) to
+     fputc(string[0], stream).  */
+  newarglist = build_tree_list (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist)));
+  newarglist =
+    tree_cons (NULL_TREE, 
+	       build_int_2 (TREE_STRING_POINTER (stripped_string)[0], 0),
+	       newarglist);
+  
+#ifdef TEST_STDIO_OPTS
+  warning ("Converted fputs(one-char-string, FILE*) -> fputc(char, FILE*)");
+#endif
+
+  call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
+  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);
+}
 
 /* Expand an expression EXP that calls a built-in function,
    with result going to TARGET if that's convenient
@@ -2322,6 +2415,7 @@ expand_builtin (exp, target, subtarget, 
 	  || fcode == BUILT_IN_FSQRT || fcode == BUILT_IN_MEMSET
 	  || fcode == BUILT_IN_MEMCPY || fcode == BUILT_IN_MEMCMP
 	  || fcode == BUILT_IN_BCMP || fcode == BUILT_IN_BZERO
+	  || fcode == BUILT_IN_FPUTS
 	  || fcode == BUILT_IN_STRLEN || fcode == BUILT_IN_STRCPY
 	  || fcode == BUILT_IN_STRCMP || fcode == BUILT_IN_FFS))
     return expand_call (exp, target, ignore);
@@ -2487,6 +2581,12 @@ expand_builtin (exp, target, subtarget, 
     case BUILT_IN_MEMCMP:
       break;
 #endif
+
+    case BUILT_IN_FPUTS:
+      target = expand_builtin_fputs (arglist, ignore);
+      if (target)
+        return target;
+      break;
 
     case BUILT_IN_SETJMP:
       if (arglist == 0
diff -rup orig/egcs-CVS20000411/gcc/builtins.def egcs-CVS20000411/gcc/builtins.def
--- orig/egcs-CVS20000411/gcc/builtins.def	Fri Mar 24 15:13:15 2000
+++ egcs-CVS20000411/gcc/builtins.def	Tue Apr 11 12:06:08 2000
@@ -58,6 +58,9 @@ DEF_BUILTIN(BUILT_IN_SETJMP)
 DEF_BUILTIN(BUILT_IN_LONGJMP)
 DEF_BUILTIN(BUILT_IN_TRAP)
 
+  /* Stdio builtins.  */
+DEF_BUILTIN(BUILT_IN_FPUTS)
+
   /* ISO C99 floating point unordered comparisons.  */
 DEF_BUILTIN(BUILT_IN_ISGREATER)
 DEF_BUILTIN(BUILT_IN_ISGREATEREQUAL)
diff -rup orig/egcs-CVS20000411/gcc/c-common.c egcs-CVS20000411/gcc/c-common.c
--- orig/egcs-CVS20000411/gcc/c-common.c	Thu Mar 30 00:02:38 2000
+++ egcs-CVS20000411/gcc/c-common.c	Wed Apr 12 07:43:30 2000
@@ -3792,6 +3792,12 @@ c_common_nodes_and_builtins (cplus_mode,
   builtin_function ("__builtin_cosl", ldouble_ftype_ldouble,
 		    BUILT_IN_COS, BUILT_IN_NORMAL, "cosl");
 
+  /* G++ in pedantic mode will not accept "int foo(...);" followed by
+     "int foo(something-specific);".  */
+  if (!(cplus_mode && pedantic))
+    builtin_function ("__builtin_fputs", int_ftype_any,
+		      BUILT_IN_FPUTS, BUILT_IN_NORMAL, "fputs");
+
   if (! no_builtins)
     {
       builtin_function ("abs", int_ftype_int, BUILT_IN_ABS,
@@ -3835,6 +3841,12 @@ c_common_nodes_and_builtins (cplus_mode,
       builtin_function ("cosl", ldouble_ftype_ldouble, BUILT_IN_COS,
 			BUILT_IN_NORMAL, NULL_PTR);
 
+      /* G++ in pedantic mode will not accept "int foo(...);" followed by
+	 "int foo(something-specific);".  */
+      if (!(cplus_mode && pedantic))
+	builtin_function ("fputs", int_ftype_any, BUILT_IN_FPUTS,
+			  BUILT_IN_NORMAL, NULL_PTR);
+      
       /* Declare these functions volatile
 	 to avoid spurious "control drops through" warnings.  */
       temp = builtin_function ("abort", cplus_mode ? void_ftype : void_ftype_any,
diff -rup orig/egcs-CVS20000411/gcc/c-lex.c egcs-CVS20000411/gcc/c-lex.c
--- orig/egcs-CVS20000411/gcc/c-lex.c	Tue Mar 14 21:33:02 2000
+++ egcs-CVS20000411/gcc/c-lex.c	Tue Apr 11 12:06:08 2000
@@ -170,6 +170,7 @@ static int readescape			PARAMS ((int *))
 static void parse_float			PARAMS ((PTR));
 static void extend_token_buffer_to	PARAMS ((int));
 static int read_line_number		PARAMS ((int *));
+static tree get_decl			PARAMS ((tree));
 
 /* Do not insert generated code into the source, instead, include it.
    This allows us to build gcc automatically even for targets that
@@ -259,6 +260,8 @@ init_parse (filename)
   init_lex ();
   init_pragma ();
 
+  get_decl_ptr = get_decl;
+  
   return filename;
 }
 
@@ -2454,4 +2457,11 @@ set_yydebug (value)
 #else
   warning ("YYDEBUG not defined.");
 #endif
+}
+
+static tree
+get_decl (identifier)
+     tree identifier;
+{
+  return IDENTIFIER_GLOBAL_VALUE (identifier);
 }
diff -rup orig/egcs-CVS20000411/gcc/cp/lex.c egcs-CVS20000411/gcc/cp/lex.c
--- orig/egcs-CVS20000411/gcc/cp/lex.c	Tue Apr 11 16:52:11 2000
+++ egcs-CVS20000411/gcc/cp/lex.c	Tue Apr 11 12:06:08 2000
@@ -93,6 +93,7 @@ static void mark_impl_file_chain PARAMS 
 static int read_ucs PARAMS ((int));
 static int is_extended_char PARAMS ((int));
 static int is_extended_char_1 PARAMS ((int));
+static tree get_decl PARAMS ((tree));
 
 /* Given a file name X, return the nondirectory portion.
    Keep in mind that X can be computed more than once.  */
@@ -925,6 +926,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_decl_ptr = get_decl;
+
   return filename;
 }
 
@@ -5146,4 +5150,11 @@ cp_type_qual_from_rid (rid)
 
   my_friendly_abort (0);
   return TYPE_UNQUALIFIED;
+}
+
+static tree
+get_decl (identifier)
+     tree identifier;
+{
+  return IDENTIFIER_GLOBAL_VALUE (identifier);
 }
diff -rup orig/egcs-CVS20000411/gcc/tree.c egcs-CVS20000411/gcc/tree.c
--- orig/egcs-CVS20000411/gcc/tree.c	Sat Apr  8 00:33:34 2000
+++ egcs-CVS20000411/gcc/tree.c	Tue Apr 11 12:06:08 2000
@@ -245,6 +245,10 @@ static int next_decl_uid;
 /* Unique id for next type created.  */
 static int next_type_uid = 1;
 
+/* Pointer to function which, when given an identifier, returns a
+ *_DECL from a language dependent location in the tree. */
+tree (*get_decl_ptr) PARAMS ((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) PARAMS ((tree));
diff -rup orig/egcs-CVS20000411/gcc/tree.h egcs-CVS20000411/gcc/tree.h
--- orig/egcs-CVS20000411/gcc/tree.h	Sat Apr  8 00:26:24 2000
+++ egcs-CVS20000411/gcc/tree.h	Tue Apr 11 12:06:08 2000
@@ -2185,6 +2185,10 @@ extern const char * const language_strin
 extern tree builtin_function			PARAMS ((const char *, tree, int,
 						       enum built_in_class,
 						       const char *));
+
+/* Pointer to function which, when given an identifier, returns a
+ *_DECL from a language dependent location in the tree. */
+extern tree (*get_decl_ptr) PARAMS ((tree));
 
 /* In tree.c */
 extern char *perm_calloc			PARAMS ((int, long));


More information about the Gcc-patches mailing list