This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: patch to suggest putc/fputs over printf("string") or printf("\n")
- To: law at cygnus dot com
- Subject: Re: patch to suggest putc/fputs over printf("string") or printf("\n")
- From: "Kaveh R. Ghazi" <ghazi at caip dot rutgers dot edu>
- Date: Mon, 11 Jan 1999 23:10:27 -0500 (EST)
- Cc: egcs-patches at cygnus dot com, egcs at cygnus dot com
> 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() */