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]
Other format: [Raw text]

[PATCH] Optimize sprintf(buffer,"foo")


The following patch optimizes both sprintf(buffer,"foo") and
sprintf(dst,"%s",src) into calls to strcpy, and if the result
of the sprintf is required, potentially an additional call to
strlen.  The performance of strcpy+strlen is better than that
the equivalent sprintf on most platforms, especially as both
strcpy/memcpy and strlen may get inlined, and the strlen often
isn't required or can be determined to be a constant at compile
time.

This optimization fires more often than might be expected.
Consider, for example, the following function from a GCC bootstrap:

  static char string[1024];
  char *c = string;
  int i, j;
  c += sprintf (c, "{ ");
  for (i = 0; i < HARD_REG_SET_LONGS; i++)
    {
      for (j = 0; j < HOST_BITS_PER_WIDE_INT; j++)
          c += sprintf (c, "%s", (1 << j) & s[i] ? "1" : "0");
      c += sprintf (c, "%s", i ? ", " : "");
    }
  c += sprintf (c, " }");
  return string;


With this patch, all four calls to sprintf are optimized away!


Whilst I was writing this patch I also discovered some mistakes
in the ways that argument lists are constructed when optimizing
calls to built-in functions.  These are also fixed by the patch
below.


The following patch has been tested on i686-pc-linux-gnu with a
complete "make bootstrap", all languages except treelang, and
regression tested with a top-level "make -k check", with no new
failures.

Ok for mainline?


2003-06-22  Roger Sayle  <roger@eyesopen.com>

	* builtins.c (expand_builtin_mathfn_2): Use tree_cons to build
	up the stabilized argument list, not build_tree_list.
	(expand_builtin_strcpy): Construct new argument list manually
	instead of using chainon to modify the original argument list.
	(expand_builtin_stpcpy): Construct new argument list manually
	instead of using copy_list and chainon.
	(expand_builtin_sprintf): New function.  Optimize calls to
	sprintf when the format is "%s" or doesn't contain a '%'.
	(expand_builtin): Expand BUILT_IN_SPRINTF using the new function
	expand_builtin_sprintf.

	* gcc.c-torture/execute/string-opt-16.c: New test case.


Index: builtins.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/builtins.c,v
retrieving revision 1.217
diff -c -3 -p -r1.217 builtins.c
*** builtins.c	19 Jun 2003 19:30:34 -0000	1.217
--- builtins.c	22 Jun 2003 18:33:32 -0000
*************** static rtx expand_builtin_alloca (tree,
*** 141,146 ****
--- 141,147 ----
  static rtx expand_builtin_unop (enum machine_mode, tree, rtx, rtx, optab);
  static rtx expand_builtin_frame_address (tree, tree);
  static rtx expand_builtin_fputs (tree, int, int);
+ static rtx expand_builtin_sprintf (tree, rtx, enum machine_mode);
  static tree stabilize_va_list (tree, int);
  static rtx expand_builtin_expect (tree, rtx);
  static tree fold_builtin_constant_p (tree);
*************** expand_builtin_mathfn_2 (tree exp, rtx t
*** 1891,1903 ****
  	case SAVE_EXPR:
  	case REAL_CST:
  	  if (! stable)
! 	    arglist = build_tree_list (temp, arg0);
  	  break;

  	default:
  	  stable = false;
  	  arg0 = save_expr (arg0);
! 	  arglist = build_tree_list (temp, arg0);
  	  break;
  	}

--- 1892,1904 ----
  	case SAVE_EXPR:
  	case REAL_CST:
  	  if (! stable)
! 	    arglist = tree_cons (NULL_TREE, arg0, temp);
  	  break;

  	default:
  	  stable = false;
  	  arg0 = save_expr (arg0);
! 	  arglist = tree_cons (NULL_TREE, arg0, temp);
  	  break;
  	}

*************** expand_builtin_bcopy (tree arglist)
*** 2529,2535 ****
  static rtx
  expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode)
  {
!   tree fn, len;

    if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
      return 0;
--- 2530,2536 ----
  static rtx
  expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode)
  {
!   tree fn, len, src, dst;

    if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
      return 0;
*************** expand_builtin_strcpy (tree arglist, rtx
*** 2538,2549 ****
    if (!fn)
      return 0;

!   len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)));
    if (len == 0)
      return 0;

    len = size_binop (PLUS_EXPR, len, ssize_int (1));
!   chainon (arglist, build_tree_list (NULL_TREE, len));
    return expand_expr (build_function_call_expr (fn, arglist),
  		      target, mode, EXPAND_NORMAL);
  }
--- 2539,2554 ----
    if (!fn)
      return 0;

!   src = TREE_VALUE (TREE_CHAIN (arglist));
!   len = c_strlen (src);
    if (len == 0)
      return 0;

+   dst = TREE_VALUE (arglist);
    len = size_binop (PLUS_EXPR, len, ssize_int (1));
!   arglist = build_tree_list (NULL_TREE, len);
!   arglist = tree_cons (NULL_TREE, src, arglist);
!   arglist = tree_cons (NULL_TREE, dst, arglist);
    return expand_expr (build_function_call_expr (fn, arglist),
  		      target, mode, EXPAND_NORMAL);
  }
*************** expand_builtin_stpcpy (tree arglist, rtx
*** 2560,2567 ****
      return 0;
    else
      {
!       tree newarglist;
!       tree src, len;

        /* If return value is ignored, transform stpcpy into strcpy.  */
        if (target == const0_rtx)
--- 2565,2571 ----
      return 0;
    else
      {
!       tree dst, src, len;

        /* If return value is ignored, transform stpcpy into strcpy.  */
        if (target == const0_rtx)
*************** expand_builtin_stpcpy (tree arglist, rtx
*** 2582,2591 ****
        if (! c_getstr (src) || ! (len = c_strlen (src)))
  	return 0;

        len = fold (size_binop (PLUS_EXPR, len, ssize_int (1)));
!       newarglist = copy_list (arglist);
!       chainon (newarglist, build_tree_list (NULL_TREE, len));
!       return expand_builtin_mempcpy (newarglist, target, mode, /*endp=*/2);
      }
  }

--- 2586,2597 ----
        if (! c_getstr (src) || ! (len = c_strlen (src)))
  	return 0;

+       dst = TREE_VALUE (arglist);
        len = fold (size_binop (PLUS_EXPR, len, ssize_int (1)));
!       arglist = build_tree_list (NULL_TREE, len);
!       arglist = tree_cons (NULL_TREE, src, arglist);
!       arglist = tree_cons (NULL_TREE, dst, arglist);
!       return expand_builtin_mempcpy (arglist, target, mode, /*endp=*/2);
      }
  }

*************** expand_builtin_cabs (tree arglist, rtx t
*** 4259,4264 ****
--- 4265,4361 ----
    return expand_complex_abs (mode, op0, target, 0);
  }

+ /* Expand a call to sprintf with argument list ARGLIST.  Return 0 if
+    a normal call should be emitted rather than expanding the function
+    inline.  If convenient, the result should be placed in TARGET with
+    mode MODE.  */
+
+ static rtx
+ expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode)
+ {
+   tree dest, fmt, stripped;
+   tree orig_arglist;
+
+   orig_arglist = arglist;
+
+   /* Verify the required arguments in the original call.  */
+   if (! arglist)
+     return 0;
+   dest = TREE_VALUE (arglist);
+   if (TREE_CODE (TREE_TYPE (dest)) != POINTER_TYPE)
+     return 0;
+   arglist = TREE_CHAIN (arglist);
+   if (! arglist)
+     return 0;
+   fmt = TREE_VALUE (arglist);
+   if (TREE_CODE (TREE_TYPE (dest)) != POINTER_TYPE)
+     return 0;
+   arglist = TREE_CHAIN (arglist);
+
+   /* Check whether the format is a literal string constant.  */
+   stripped = fmt;
+   STRIP_NOPS (stripped);
+   if (stripped && TREE_CODE (stripped) == ADDR_EXPR)
+     stripped = TREE_OPERAND (stripped, 0);
+   if (TREE_CODE (stripped) != STRING_CST)
+     return 0;
+
+   /* If the format doesn't contain % args or %%, use strcpy.  */
+   if (strchr (TREE_STRING_POINTER (stripped), '%') == 0)
+     {
+       tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+       tree exp;
+
+       if (arglist || !fn)
+ 	return 0;
+       expand_expr (build_function_call_expr (fn, orig_arglist),
+ 		   const0_rtx, VOIDmode, EXPAND_NORMAL);
+       if (target == const0_rtx)
+ 	return const0_rtx;
+       exp = build_int_2 (TREE_STRING_LENGTH (stripped) - 1, 0);
+       exp = fold (build1 (NOP_EXPR, integer_type_node, exp));
+       return expand_expr (exp, target, mode, EXPAND_NORMAL);
+     }
+   /* If the format is "%s", use strcpy and possibly strlen.  */
+   else if (strcmp (TREE_STRING_POINTER (stripped), "%s") == 0)
+     {
+       tree strcpy_fn, strlen_fn, exp, arg;
+       strcpy_fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+
+       if (! strcpy_fn)
+ 	return 0;
+
+       if (! arglist || TREE_CHAIN (arglist))
+ 	return 0;
+       arg = TREE_VALUE (arglist);
+       if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
+ 	return 0;
+
+       if (target != const0_rtx)
+ 	{
+ 	  strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN];
+ 	  if (! strlen_fn)
+ 	    return 0;
+ 	  arg = save_expr (arg);
+ 	}
+       else
+ 	strlen_fn = 0;
+
+       arglist = build_tree_list (NULL_TREE, arg);
+       arglist = tree_cons (NULL_TREE, dest, arglist);
+       expand_expr (build_function_call_expr (strcpy_fn, arglist),
+ 		   const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+       if (target == const0_rtx)
+ 	return const0_rtx;
+
+       exp = build_function_call_expr (strlen_fn, TREE_CHAIN (arglist));
+       exp = fold (build1 (NOP_EXPR, integer_type_node, exp));
+       return expand_expr (exp, target, mode, EXPAND_NORMAL);
+     }
+
+   return 0;
+ }

  /* Expand an expression EXP that calls a built-in function,
     with result going to TARGET if that's convenient
*************** expand_builtin (tree exp, rtx target, rt
*** 4323,4328 ****
--- 4420,4426 ----
        case BUILT_IN_BCOPY:
        case BUILT_IN_INDEX:
        case BUILT_IN_RINDEX:
+       case BUILT_IN_SPRINTF:
        case BUILT_IN_STPCPY:
        case BUILT_IN_STRCHR:
        case BUILT_IN_STRRCHR:
*************** expand_builtin (tree exp, rtx target, rt
*** 4790,4795 ****
--- 4888,4899 ----
        break;
      case BUILT_IN_FPUTS_UNLOCKED:
        target = expand_builtin_fputs (arglist, ignore,/*unlocked=*/ 1);
+       if (target)
+ 	return target;
+       break;
+
+     case BUILT_IN_SPRINTF:
+       target = expand_builtin_sprintf (arglist, target, mode);
        if (target)
  	return target;
        break;


/* Copyright (C) 2003  Free Software Foundation.

   Test sprintf optimizations don't break anything and return the
   correct results.

   Written by Roger Sayle, June 22, 2003.  */

static char buffer[32];

extern void abort ();
typedef __SIZE_TYPE__ size_t;
extern int sprintf(char*, const char*, ...);
extern void *memset(void*, int, size_t);
extern int memcmp(const void*, const void*, size_t);

void test1()
{
  sprintf(buffer,"foo");
}

int test2()
{
  return sprintf(buffer,"foo");
}

void test3()
{
  sprintf(buffer,"%s","bar");
}

int test4()
{
  return sprintf(buffer,"%s","bar");
}

void test5(char *ptr)
{
  sprintf(buffer,"%s",ptr);
}

int test6(char *ptr)
{
  return sprintf(buffer,"%s",ptr);
}

int main()
{
  memset (buffer, 'A', 32);
  test1 ();
  if (memcmp(buffer, "foo", 4) || buffer[4] != 'A')
    abort ();

  memset (buffer, 'A', 32);
  if (test2 () != 3)
    abort ();
  if (memcmp(buffer, "foo", 4) || buffer[4] != 'A')
    abort ();

  memset (buffer, 'A', 32);
  test3 ();
  if (memcmp(buffer, "bar", 4) || buffer[4] != 'A')
    abort ();

  memset (buffer, 'A', 32);
  if (test4 () != 3)
    abort ();
  if (memcmp(buffer, "bar", 4) || buffer[4] != 'A')
    abort ();

  memset (buffer, 'A', 32);
  test5 ("barf");
  if (memcmp(buffer, "barf", 5) || buffer[5] != 'A')
    abort ();

  memset (buffer, 'A', 32);
  if (test6 ("barf") != 4)
    abort ();
  if (memcmp(buffer, "barf", 5) || buffer[5] != 'A')
    abort ();

  return 0;
}

#ifdef __OPTIMIZE__
/* When optimizing, all the above cases should be transformed into
   something else.  So any remaining calls to the original function
   should abort.  */
__attribute__ ((noinline))
static int
sprintf (char *buf, const char *fmt, ...)
{
  abort ();
}
#endif


Roger
--
Roger Sayle,                         E-mail: roger@eyesopen.com
OpenEye Scientific Software,         WWW: http://www.eyesopen.com/
Suite 1107, 3600 Cerrillos Road,     Tel: (+1) 505-473-7385
Santa Fe, New Mexico, 87507.         Fax: (+1) 505-473-0833


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