+/* Fold a call to the {,v}fprintf{,_unlocked} and __{,v}printf_chk builtins.
+ FP, FMT, and ARG are the arguments to the call. We don't fold calls with
+ more than 3 arguments, and ARG may be null in the 2-argument case.
+
+ Return NULL_TREE if no simplification was possible, otherwise return the
+ simplified form of the call as a tree. FCODE is the BUILT_IN_*
+ code of the function to be simplified. */
+
+static bool
+gimple_fold_builtin_fprintf (gimple_stmt_iterator *gsi,
+ tree fp, tree fmt, tree arg,
+ enum built_in_function fcode)
+{
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+ tree fn_fputc, fn_fputs;
+ const char *fmt_str = NULL;
+
+ /* If the return value is used, don't do the transformation. */
+ if (gimple_call_lhs (stmt) != NULL_TREE)
+ return false;
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return false;
+
+ if (fcode == BUILT_IN_FPRINTF_UNLOCKED)
+ {
+ /* If we're using an unlocked function, assume the other
+ unlocked functions exist explicitly. */
+ fn_fputc = builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED);
+ fn_fputs = builtin_decl_explicit (BUILT_IN_FPUTS_UNLOCKED);
+ }
+ else
+ {
+ fn_fputc = builtin_decl_implicit (BUILT_IN_FPUTC);
+ fn_fputs = builtin_decl_implicit (BUILT_IN_FPUTS);
+ }
+
+ if (!init_target_chars ())
+ return false;
+
+ /* If the format doesn't contain % args or %%, use strcpy. */
+ if (strchr (fmt_str, target_percent) == NULL)
+ {
+ if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK
+ && arg)
+ return false;
+
+ /* If the format specifier was "", fprintf does nothing. */
+ if (fmt_str[0] == '\0')
+ {
+ replace_call_with_value (gsi, NULL_TREE);
+ return true;
+ }
+
+ /* When "string" doesn't contain %, replace all cases of
+ fprintf (fp, string) with fputs (string, fp). The fputs
+ builtin will take care of special cases like length == 1. */
+ if (fn_fputs)
+ {
+ gcall *repl = gimple_build_call (fn_fputs, 2, fmt, fp);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+
+ /* The other optimizations can be done only on the non-va_list variants. */
+ else if (fcode == BUILT_IN_VFPRINTF || fcode == BUILT_IN_VFPRINTF_CHK)
+ return false;
+
+ /* If the format specifier was "%s", call __builtin_fputs (arg, fp). */
+ else if (strcmp (fmt_str, target_percent_s) == 0)
+ {
+ if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+ return false;
+ if (fn_fputs)
+ {
+ gcall *repl = gimple_build_call (fn_fputs, 2, arg, fp);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+
+ /* If the format specifier was "%c", call __builtin_fputc (arg, fp). */
+ else if (strcmp (fmt_str, target_percent_c) == 0)
+ {
+ if (!arg
+ || ! useless_type_conversion_p (integer_type_node, TREE_TYPE (arg)))
+ return false;
+ if (fn_fputc)
+ {
+ gcall *repl = gimple_build_call (fn_fputc, 2, arg, fp);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins.
+ FMT and ARG are the arguments to the call; we don't fold cases with
+ more than 2 arguments, and ARG may be null if this is a 1-argument case.
+
+ Return NULL_TREE if no simplification was possible, otherwise return the
+ simplified form of the call as a tree. FCODE is the BUILT_IN_*
+ code of the function to be simplified. */
+
+static bool
+gimple_fold_builtin_printf (gimple_stmt_iterator *gsi, tree fmt,
+ tree arg, enum built_in_function fcode)
+{
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+ tree fn_putchar, fn_puts, newarg;
+ const char *fmt_str = NULL;
+
+ /* If the return value is used, don't do the transformation. */
+ if (gimple_call_lhs (stmt) != NULL_TREE)
+ return false;
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return false;
+
+ if (fcode == BUILT_IN_PRINTF_UNLOCKED)
+ {
+ /* If we're using an unlocked function, assume the other
+ unlocked functions exist explicitly. */
+ fn_putchar = builtin_decl_explicit (BUILT_IN_PUTCHAR_UNLOCKED);
+ fn_puts = builtin_decl_explicit (BUILT_IN_PUTS_UNLOCKED);
+ }
+ else
+ {
+ fn_putchar = builtin_decl_implicit (BUILT_IN_PUTCHAR);
+ fn_puts = builtin_decl_implicit (BUILT_IN_PUTS);
+ }
+
+ if (!init_target_chars ())
+ return false;
+
+ if (strcmp (fmt_str, target_percent_s) == 0
+ || strchr (fmt_str, target_percent) == NULL)
+ {
+ const char *str;
+
+ if (strcmp (fmt_str, target_percent_s) == 0)
+ {
+ if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+ return false;
+
+ if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+ return false;
+
+ str = c_getstr (arg);
+ if (str == NULL)
+ return false;
+ }
+ else
+ {
+ /* The format specifier doesn't contain any '%' characters. */
+ if (fcode != BUILT_IN_VPRINTF && fcode != BUILT_IN_VPRINTF_CHK
+ && arg)
+ return false;
+ str = fmt_str;
+ }
+
+ /* If the string was "", printf does nothing. */
+ if (str[0] == '\0')
+ {
+ replace_call_with_value (gsi, NULL_TREE);
+ return true;
+ }
+
+ /* If the string has length of 1, call putchar. */
+ if (str[1] == '\0')
+ {
+ /* Given printf("c"), (where c is any one character,)
+ convert "c"[0] to an int and pass that to the replacement
+ function. */
+ newarg = build_int_cst (integer_type_node, str[0]);
+ if (fn_putchar)
+ {
+ gcall *repl = gimple_build_call (fn_putchar, 1, newarg);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+ else
+ {
+ /* If the string was "string\n", call puts("string"). */
+ size_t len = strlen (str);
+ if ((unsigned char)str[len - 1] == target_newline
+ && (size_t) (int) len == len
+ && (int) len > 0)
+ {
+ char *newstr;
+ tree offset_node, string_cst;
+
+ /* Create a NUL-terminated string that's one char shorter
+ than the original, stripping off the trailing '\n'. */
+ newarg = build_string_literal (len, str);
+ string_cst = string_constant (newarg, &offset_node);
+ gcc_checking_assert (string_cst
+ && (TREE_STRING_LENGTH (string_cst)
+ == (int) len)
+ && integer_zerop (offset_node)
+ && (unsigned char)
+ TREE_STRING_POINTER (string_cst)[len - 1]
+ == target_newline);
+ /* build_string_literal creates a new STRING_CST,
+ modify it in place to avoid double copying. */
+ newstr = CONST_CAST (char *, TREE_STRING_POINTER (string_cst));
+ newstr[len - 1] = '\0';
+ if (fn_puts)
+ {
+ gcall *repl = gimple_build_call (fn_puts, 1, newarg);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+ else
+ /* We'd like to arrange to call fputs(string,stdout) here,
+ but we need stdout and don't have a way to get it yet. */
+ return false;
+ }
+ }
+
+ /* The other optimizations can be done only on the non-va_list variants. */
+ else if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+ return false;
+
+ /* If the format specifier was "%s\n", call __builtin_puts(arg). */
+ else if (strcmp (fmt_str, target_percent_s_newline) == 0)
+ {
+ if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+ return false;
+ if (fn_puts)
+ {
+ gcall *repl = gimple_build_call (fn_puts, 1, arg);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+
+ /* If the format specifier was "%c", call __builtin_putchar(arg). */
+ else if (strcmp (fmt_str, target_percent_c) == 0)
+ {
+ if (!arg || ! useless_type_conversion_p (integer_type_node,
+ TREE_TYPE (arg)))
+ return false;
+ if (fn_putchar)
+ {
+ gcall *repl = gimple_build_call (fn_putchar, 1, arg);
+ replace_call_with_call_and_fold (gsi, repl);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+