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]

Re: patch to suggest putc/fputs over printf("string") or printf("\n")


 > From: Jeffrey A Law <law@hurl.cygnus.com>
 > 
 > Actually, I'd consider a patch which conditionally converted them for
 > the user.  We've kicked that idea around quite a bit inside Cygnus, but
 > never got excited enough to implement it.  See also:
 > 
 > http://www.nullstone.com/htmls/category/printf.htm



	I thought a bit about how/when to optimize printf.  Here is the
algorithm/pseudo-code I came up with.

WARNING: its very "pseudo" but should be readable.  :-)



In addition to changing simple cases like

 > printf ("hello world\n");

into this:

 > fputs ("hello world\n", stdout);

It also optimizes cases like this:

 > printf (" %d %s %f\n", 10, "hello", 4.5);

into this:

 > fputs (" 10 hello 4.5000\n", stdout);

I.e. if all the parameters are compile time constants, it'll evaluate
the printf call at compile time (using vasprintf) and the resulting string
will be output via fputs at runtime.



	It'll also optimize these:

 > printf("%c", char);
 > printf("%s", char*);
 > printf("%s\n", char*);

 > fprintf(stream, "%c", char);
 > fprintf(stream, "%s", char*);
 > fprintf(stdout, "%s\n", char*);

 > sprintf(buffer, "%s", char*);

into these:

 > fputc (char, stdout);
 > fputs (char*, stdout);
 > puts (char*);

 > fputc (char, stream);
 > fputs (char*, stream);
 > puts (char*);

 > strcpy (buffer, char*);


Finally, it combines sequential putc/fputc/puts/fputs calls, if they are
printing compile time constants to the same stream.  In merges
compatible calls of these functions into one call to fputs. 

	Comments/feedback welcome.

		--Kaveh

PS: Later on, we may also wish to experiment with splitting stuff like this:

 > printf ("This is some text %d this is some more text\n", variable);

into this:

 > fputs ("This is some text ", stdout);
 > printf ("%d", variable);
 > fputs ("this is some more text\n", stdout);

but my algorithm doesn't do that yet.  I don't know if this
transformation is actually slower or faster than the original.


-------------------------------------------------------------------------
Pseudo-code algorithm to optimize *printf/putc/fputc/puts/fputs calls
-------------------------------------------------------------------------

/* Given a list of statements, attempt to optimize calls to *printf, and
   putc/fputc/puts/fputs.  Doing this optimization must imply -Wformat or
   we might crash at compile time. */

static void
optimize_printf_and_put(stmts)
{
  reduce_printfs(stmts)
  combine_put_calls(stmts)
}


/* In function calls to printf/fprintf/sprintf, if no arguments follow
   the format specifier or if all the arguments following the format
   specifier are compile time constants, evaluate the *printf call at
   compile time and substitute fputs/strcpy as appropriate. */

static void
reduce_printfs(stmts)
{
  foreach stmt in (stmts)
  {
    if (is_function_call(stmt) && is_printf_type_call(stmt)
        && is_const_string(stmt.format))
    {
      /* Sanity check, only proceed if the statement passes the
         -Wformat checks.  We'll modify c-common.c:check_function_format()
         to return a status code. */
      if ( ! check_function_format(stmt))
        continue;

      if (no_args_follow_format(stmt) || all_args_are_constant(stmt))
      {
        newstring = vasprintf(stmt.format, stmt.va_arg)

        if (is_printf(stmt))
          change_stmt (stmt, "fputs(newstring, stdio)")

        if (is_fprintf(stmt))
          change_stmt (stmt, "fputs(newstring, stmt.stream)")

        if (is_sprintf(stmt))
          change_stmt (stmt, "strcpy(stmt.buffer, newstring)")
      }
      else if (is_printf(stmt))
      {
        if (stmt.format == "%c")
          change_stmt (stmt, "fputc(stmt.va_arg[1], stdout)")
        else if (stmt.format == "%s")
          change_stmt (stmt, "fputs(stmt.va_arg[1], stdout)")
        else if (stmt.format == "%s\n")
          change_stmt (stmt, "puts(stmt.va_arg[1])")
      }
      else if (is_fprintf(stmt))
      {
        if (stmt.format == "%c")
          change_stmt (stmt, "fputc(stmt.va_arg[1], stmt.stream)")
        else if (stmt.format == "%s")
          change_stmt (stmt, "fputs(stmt.va_arg[1], stmt.stream)")
        else if (stmt.format == "%s\n" && stmt.stream == stdout)
          change_stmt (stmt, "puts(stmt.va_arg[1])")
      }
      else if (is_sprintf(stmt))
      {
        if (stmt.format == "%s")
          change_stmt (stmt, "strcpy(stmt.buffer, stmt.va_arg[1])")
      }
    } /* End of if statement */
  } /* End of foreach statement */
} /* End of function reduce_printfs() */


/* Combine sequential putc/fputc/puts/fputs calls into one call to fputs. */
/* FIXME:  We should probably apply brakes after the string reaches a
   threshold size.  Perhaps when strlen == BUFSIZ ? */

static void
combine_put_calls(stmts)
{
  foreach stmt in (stmts)
  {
    if (is_function_call(stmt) && is_any_put_type_call(stmt)
        && first_arg_is_compile_time_constant(stmt))
    {
      next = stmt.next
      while (is_function_call(next) && is_any_put_type_call(next)
             && first_arg_is_compile_time_constant(next))
      {
        switch(stmt)
        {
        case putc:
        case fputc:
          if ((is_putc(next) || is_fputc(next)) && stmt.stream == next.stream)
          {
            newstring = concat (stmt.char, next.char)
            newstream = stmt.stream

            change_stmt (stmt, "fputs(newstring, newstream)")
            stmt.next = next.next; free (next); next = stmt.next
          }
          else if (is_puts(next) && stmt.stream == stdout)
          {
            newstring = concat (stmt.char, next.string, '\n')
            newstream = stmt.stream

            change_stmt (stmt, "fputs(newstring, newstream)")
            stmt.next = next.next; free (next); next = stmt.next
          }
          else if (is_fputs(next) && stmt.stream == next.stream)
          {
            newstring = concat (stmt.char, next.string)
            newstream = stmt.stream

            change_stmt (stmt, "fputs(newstring, newstream)")
            stmt.next = next.next; free (next); next = stmt.next
          }
          break    
        case puts:
          if ((is_putc(next) || is_fputc(next)) && next.stream == stdout)
          {
            newstring = concat (stmt.string, '\n', next.char)
            newstream = stdout

            change_stmt (stmt, "fputs(newstring, newstream)")
            stmt.next = next.next; free (next); next = stmt.next
          }
          else if (is_puts(next))
          {
            newstring = concat (stmt.string, '\n', next.string, '\n')
            newstream = stdout

            change_stmt (stmt, "fputs(newstring, newstream)")
            stmt.next = next.next; free (next); next = stmt.next
          }
          else if (is_fputs(next) && next.stream == stdout)
          {
            newstring = concat (stmt.string, '\n', next.string)
            newstream = stdout

            change_stmt (stmt, "fputs(newstring, newstream)")
            stmt.next = next.next; free (next); next = stmt.next
          }
          break    
        case fputs:
          if ((is_putc(next) || is_fputc(next)) && stmt.stream == next.stream)
          {
            newstring = concat (stmt.string, next.char)
            newstream = stmt.stream

            change_stmt (stmt, "fputs(newstring, newstream)")
            stmt.next = next.next; free (next); next = stmt.next
          }
          else if (is_puts(next) && stmt.stream == stdout)
          {
            newstring = concat (stmt.string, next.string, '\n')
            newstream = stmt.stream

            change_stmt (stmt, "fputs(newstring, newstream)")
            stmt.next = next.next; free (next); next = stmt.next
          }
          else if (is_fputs(next) && next.stream == stdout)
          {
            newstring = concat (stmt.string, next.string)
            newstream = stmt.stream

            change_stmt (stmt, "fputs(newstring, newstream)")
            stmt.next = next.next; free (next); next = stmt.next
          }
          break    
        } /* End of switch statement */
      } /* End of while statement */
    } /* End of if statement */
  } /* End of foreach loop */
} /* End of function combine_put_calls() */


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