common.opt: Add -foptimize-strlen option.

* common.opt: Add -foptimize-strlen option.
	* Makefile.in (OBJS): Add tree-ssa-strlen.o.
	(tree-sssa-strlen.o): Add dependencies.
	* opts.c (default_options_table): Enable -foptimize-strlen
	by default at -O2 if not -Os.
	* passes.c (init_optimization_passes): Add pass_strlen
	after pass_object_sizes.
	* timevar.def (TV_TREE_STRLEN): New timevar.
	* params.def (PARAM_MAX_TRACKED_STRLENS): New parameter.
	* tree-pass.h (pass_strlen): Declare.
	* tree-ssa-strlen.c: New file.
	* c-decl.c (merge_decls): If compatible stpcpy prototype
	is seen, set implicit_built_in_decls[BUILT_IN_STPCPY].
cp/
	* decl.c (duplicate_decls): If compatible stpcpy prototype
	is seen, set implicit_built_in_decls[BUILT_IN_STPCPY].
testsuite/
	* gcc.dg/strlenopt-1.c: New test.
	* gcc.dg/strlenopt-1f.c: New test.
	* gcc.dg/strlenopt-2.c: New test.
	* gcc.dg/strlenopt-2f.c: New test.
	* gcc.dg/strlenopt-3.c: New test.
	* gcc.dg/strlenopt-4.c: New test.
	* gcc.dg/strlenopt-4g.c: New test.
	* gcc.dg/strlenopt-4gf.c: New test.
	* gcc.dg/strlenopt-5.c: New test.
	* gcc.dg/strlenopt-6.c: New test.
	* gcc.dg/strlenopt-7.c: New test.
	* gcc.dg/strlenopt-8.c: New test.
	* gcc.dg/strlenopt-9.c: New test.
	* gcc.dg/strlenopt-10.c: New test.
	* gcc.dg/strlenopt-11.c: New test.
	* gcc.dg/strlenopt-12.c: New test.
	* gcc.dg/strlenopt-12g.c: New test.
	* gcc.dg/strlenopt-13.c: New test.
	* gcc.dg/strlenopt-14g.c: New test.
	* gcc.dg/strlenopt-14gf.c: New test.
	* gcc.dg/strlenopt-15.c: New test.
	* gcc.dg/strlenopt-16g.c: New test.
	* gcc.dg/strlenopt-17g.c: New test.
	* gcc.dg/strlenopt-18g.c: New test.
	* gcc.dg/strlenopt.h: New file.

From-SVN: r179277
This commit is contained in:
Jakub Jelinek 2011-09-27 18:15:46 +02:00 committed by Jakub Jelinek
parent baaa40aeca
commit 8b57bfebe0
38 changed files with 3671 additions and 4 deletions

View File

@ -1,3 +1,19 @@
2011-09-27 Jakub Jelinek <jakub@redhat.com>
* common.opt: Add -foptimize-strlen option.
* Makefile.in (OBJS): Add tree-ssa-strlen.o.
(tree-sssa-strlen.o): Add dependencies.
* opts.c (default_options_table): Enable -foptimize-strlen
by default at -O2 if not -Os.
* passes.c (init_optimization_passes): Add pass_strlen
after pass_object_sizes.
* timevar.def (TV_TREE_STRLEN): New timevar.
* params.def (PARAM_MAX_TRACKED_STRLENS): New parameter.
* tree-pass.h (pass_strlen): Declare.
* tree-ssa-strlen.c: New file.
* c-decl.c (merge_decls): If compatible stpcpy prototype
is seen, set implicit_built_in_decls[BUILT_IN_STPCPY].
2011-09-27 Tom de Vries <tom@codesourcery.com>
PR middle-end/43864

View File

@ -1475,6 +1475,7 @@ OBJS = \
tree-ssa-reassoc.o \
tree-ssa-sccvn.o \
tree-ssa-sink.o \
tree-ssa-strlen.o \
tree-ssa-structalias.o \
tree-ssa-tail-merge.o \
tree-ssa-ter.o \
@ -3168,6 +3169,9 @@ tree-ssa-ccp.o : tree-ssa-ccp.c $(TREE_FLOW_H) $(CONFIG_H) \
$(TREE_DUMP_H) $(BASIC_BLOCK_H) $(TREE_PASS_H) langhooks.h $(PARAMS_H) \
tree-ssa-propagate.h value-prof.h $(FLAGS_H) $(TARGET_H) $(DIAGNOSTIC_CORE_H) \
$(DBGCNT_H) tree-pretty-print.h gimple-pretty-print.h gimple-fold.h
tree-ssa-strlen.o : tree-ssa-strlen.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
$(TREE_FLOW_H) $(TREE_PASS_H) domwalk.h alloc-pool.h tree-ssa-propagate.h \
gimple-pretty-print.h $(PARAMS_H)
tree-sra.o : tree-sra.c $(CONFIG_H) $(SYSTEM_H) coretypes.h alloc-pool.h \
$(TM_H) $(TREE_H) $(GIMPLE_H) $(CGRAPH_H) $(TREE_FLOW_H) \
$(IPA_PROP_H) $(DIAGNOSTIC_H) statistics.h $(TREE_DUMP_H) $(TIMEVAR_H) \

View File

@ -2369,7 +2369,21 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
DECL_FUNCTION_CODE (newdecl) = DECL_FUNCTION_CODE (olddecl);
C_DECL_DECLARED_BUILTIN (newdecl) = 1;
if (new_is_prototype)
C_DECL_BUILTIN_PROTOTYPE (newdecl) = 0;
{
C_DECL_BUILTIN_PROTOTYPE (newdecl) = 0;
if (DECL_BUILT_IN_CLASS (newdecl) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (newdecl))
{
/* If a compatible prototype of these builtin functions
is seen, assume the runtime implements it with the
expected semantics. */
case BUILT_IN_STPCPY:
implicit_built_in_decls[DECL_FUNCTION_CODE (newdecl)]
= built_in_decls[DECL_FUNCTION_CODE (newdecl)];
default:
break;
}
}
else
C_DECL_BUILTIN_PROTOTYPE (newdecl)
= C_DECL_BUILTIN_PROTOTYPE (olddecl);

View File

@ -1961,6 +1961,10 @@ ftree-fre
Common Report Var(flag_tree_fre) Optimization
Enable Full Redundancy Elimination (FRE) on trees
foptimize-strlen
Common Report Var(flag_optimize_strlen) Optimization
Enable string length optimizations on trees
ftree-loop-distribution
Common Report Var(flag_tree_loop_distribution) Optimization
Enable loop distribution on trees

View File

@ -1,3 +1,8 @@
2011-09-27 Jakub Jelinek <jakub@redhat.com>
* decl.c (duplicate_decls): If compatible stpcpy prototype
is seen, set implicit_built_in_decls[BUILT_IN_STPCPY].
2011-09-26 Jason Merrill <jason@redhat.com>
PR c++/45012

View File

@ -2135,6 +2135,18 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
/* If we're keeping the built-in definition, keep the rtl,
regardless of declaration matches. */
COPY_DECL_RTL (olddecl, newdecl);
if (DECL_BUILT_IN_CLASS (newdecl) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (newdecl))
{
/* If a compatible prototype of these builtin functions
is seen, assume the runtime implements it with the
expected semantics. */
case BUILT_IN_STPCPY:
implicit_built_in_decls[DECL_FUNCTION_CODE (newdecl)]
= built_in_decls[DECL_FUNCTION_CODE (newdecl)];
default:
break;
}
}
DECL_RESULT (newdecl) = DECL_RESULT (olddecl);

View File

@ -1,7 +1,7 @@
/* Top level of GCC compilers (cc1, cc1plus, etc.)
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2011 Free Software Foundation, Inc.
This file is part of GCC.
@ -1321,6 +1321,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_forwprop);
NEXT_PASS (pass_phiopt);
NEXT_PASS (pass_object_sizes);
NEXT_PASS (pass_strlen);
NEXT_PASS (pass_ccp);
NEXT_PASS (pass_copy_prop);
NEXT_PASS (pass_cse_sincos);

View File

@ -1,3 +1,31 @@
2011-09-27 Jakub Jelinek <jakub@redhat.com>
* gcc.dg/strlenopt-1.c: New test.
* gcc.dg/strlenopt-1f.c: New test.
* gcc.dg/strlenopt-2.c: New test.
* gcc.dg/strlenopt-2f.c: New test.
* gcc.dg/strlenopt-3.c: New test.
* gcc.dg/strlenopt-4.c: New test.
* gcc.dg/strlenopt-4g.c: New test.
* gcc.dg/strlenopt-4gf.c: New test.
* gcc.dg/strlenopt-5.c: New test.
* gcc.dg/strlenopt-6.c: New test.
* gcc.dg/strlenopt-7.c: New test.
* gcc.dg/strlenopt-8.c: New test.
* gcc.dg/strlenopt-9.c: New test.
* gcc.dg/strlenopt-10.c: New test.
* gcc.dg/strlenopt-11.c: New test.
* gcc.dg/strlenopt-12.c: New test.
* gcc.dg/strlenopt-12g.c: New test.
* gcc.dg/strlenopt-13.c: New test.
* gcc.dg/strlenopt-14g.c: New test.
* gcc.dg/strlenopt-14gf.c: New test.
* gcc.dg/strlenopt-15.c: New test.
* gcc.dg/strlenopt-16g.c: New test.
* gcc.dg/strlenopt-17g.c: New test.
* gcc.dg/strlenopt-18g.c: New test.
* gcc.dg/strlenopt.h: New file.
2011-09-27 Tom de Vries <tom@codesourcery.com>
PR middle-end/43864

View File

@ -0,0 +1,47 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) char *
foo (char *p, char *r)
{
char *q = malloc (strlen (p) + strlen (r) + 64);
if (q == NULL) return NULL;
/* This strcpy can be optimized into memcpy, using the remembered
strlen (p). */
strcpy (q, p);
/* These two strcat can be optimized into memcpy. The first one
could be even optimized into a *ptr = '/'; store as the '\0'
is immediately overwritten. */
strcat (q, "/");
strcat (q, "abcde");
/* Due to inefficient PTA (PR50262) the above calls invalidate
string length of r, so it is optimized just into strcpy instead
of memcpy. */
strcat (q, r);
return q;
}
int
main ()
{
char *volatile p = "string1";
char *volatile r = "string2";
char *q = foo (p, r);
if (q != NULL)
{
if (strcmp (q, "string1/abcdestring2"))
abort ();
free (q);
}
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,80 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) size_t
fn1 (char *p)
{
char *q;
/* This can be optimized into memcpy and the size can be decreased to one,
as it is immediately overwritten. */
strcpy (p, "z");
q = strchr (p, '\0');
*q = 32;
/* This strlen can't be optimized away, string length is unknown here. */
return strlen (p);
}
__attribute__((noinline, noclone)) void
fn2 (char *p, const char *z, size_t *lp)
{
char *q, *r;
char buf[64];
size_t l[10];
/* The first strlen stays, all the strcpy calls can be optimized
into memcpy and all other strlen calls and all strchr calls
optimized away. */
l[0] = strlen (z);
strcpy (buf, z);
strcpy (p, "abcde");
q = strchr (p, '\0');
strcpy (q, "efghi");
r = strchr (q, '\0');
strcpy (r, "jkl");
l[1] = strlen (p);
l[2] = strlen (q);
l[3] = strlen (r);
strcpy (r, buf);
l[4] = strlen (p);
l[5] = strlen (q);
l[6] = strlen (r);
strcpy (r, "mnopqr");
l[7] = strlen (p);
l[8] = strlen (q);
l[9] = strlen (r);
memcpy (lp, l, sizeof l);
}
int
main ()
{
char buf[64];
size_t l[10];
const char *volatile z = "ABCDEFG";
memset (buf, '\0', sizeof buf);
if (fn1 (buf) != 2 || buf[0] != 'z' || buf[1] != 32 || buf[2] != '\0')
abort ();
fn2 (buf, z, l);
if (memcmp (buf, "abcdeefghimnopqr", 17) != 0)
abort ();
if (l[0] != 7)
abort ();
if (l[1] != 13 || l[2] != 8 || l[3] != 3)
abort ();
if (l[4] != 17 || l[5] != 12 || l[6] != 7)
abort ();
if (l[7] != 16 || l[8] != 11 || l[9] != 6)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 8 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "\\*q_\[0-9\]* = 32;" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(\[^\n\r\]*, 1\\)" 1 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,70 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) void
fn1 (char *p, const char *z, size_t *lp)
{
char *q, *r, *s;
char buf[64];
size_t l[11];
/* The first strlen stays, all the strcpy calls can be optimized
into memcpy and most other strlen calls and all strchr calls
optimized away. l[6] = strlen (r); and l[9] = strlen (r); need
to stay, because we need to invalidate the knowledge about
r strlen after strcpy (q, "jklmnopqrst"). */
l[0] = strlen (z);
strcpy (buf, z);
strcpy (p, "abcde");
q = strchr (p, '\0');
strcpy (q, "efghi");
r = strchr (q, '\0');
strcpy (r, buf);
l[1] = strlen (p);
l[2] = strlen (q);
l[3] = strlen (r);
strcpy (q, "jklmnopqrst");
l[4] = strlen (p);
l[5] = strlen (q);
l[6] = strlen (r);
s = strchr (q, '\0');
strcpy (s, buf);
l[7] = strlen (p);
l[8] = strlen (q);
l[9] = strlen (r);
l[10] = strlen (s);
memcpy (lp, l, sizeof l);
}
int
main ()
{
char buf[64];
size_t l[11];
const char *volatile z = "ABCDEFG";
memset (buf, '\0', sizeof buf);
fn1 (buf, z, l);
if (memcmp (buf, "abcdejklmnopqrstABCDEFG", 24) != 0)
abort ();
if (l[0] != 7)
abort ();
if (l[1] != 17 || l[2] != 12 || l[3] != 7)
abort ();
if (l[4] != 16 || l[5] != 11 || l[6] != 6)
abort ();
if (l[7] != 23 || l[8] != 18 || l[9] != 13 || l[10] != 7)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times " D\.\[0-9_\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.0. = " 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times " D\.\[0-9_\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.6. = " 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times " D\.\[0-9_\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.9. = " 1 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,90 @@
/* { dg-do run } */
/* { dg-options "-O2" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) char *
fn1 (char *p, size_t *l)
{
char *q = strcat (p, "abcde");
*l = strlen (p);
return q;
}
__attribute__((noinline, noclone)) char *
fn2 (char *p, const char *q, size_t *l1, size_t *l2)
{
size_t l = strlen (q);
char *r = strcat (p, q);
*l1 = l;
*l2 = strlen (p);
return r;
}
__attribute__((noinline, noclone)) char *
fn3 (char *p, const char *q, size_t *l)
{
char *r = strcpy (p, q);
*l = strlen (p);
return r;
}
__attribute__((noinline, noclone)) char *
fn4 (char *p, const char *q, size_t *l)
{
char *r = strcat (p, q);
*l = strlen (p);
return r;
}
__attribute__((noinline, noclone)) char *
fn5 (char *p, const char *q, size_t *l1, size_t *l2, size_t *l3)
{
size_t l = strlen (q);
size_t ll = strlen (p);
char *r = strcat (p, q);
*l1 = l;
*l2 = strlen (p);
*l3 = ll;
return r;
}
__attribute__((noinline, noclone)) char *
fn6 (char *p, const char *q, size_t *l1, size_t *l2)
{
size_t l = strlen (p);
char *r = strcat (p, q);
*l1 = strlen (p);
*l2 = l;
return r;
}
int
main ()
{
char buf[64];
const char *volatile q = "fgh";
size_t l, l1, l2, l3;
memset (buf, '\0', sizeof buf);
memset (buf, 'a', 3);
if (fn1 (buf, &l) != buf || l != 8 || memcmp (buf, "aaaabcde", 9) != 0)
abort ();
if (fn2 (buf, q, &l1, &l2) != buf || l1 != 3 || l2 != 11
|| memcmp (buf, "aaaabcdefgh", 12) != 0)
abort ();
if (fn3 (buf, q, &l) != buf || l != 3
|| memcmp (buf, "fgh\0bcdefgh", 12) != 0)
abort ();
if (fn4 (buf, q, &l) != buf || l != 6
|| memcmp (buf, "fghfgh\0efgh", 12) != 0)
abort ();
l1 = 0;
l2 = 0;
if (fn5 (buf, q, &l1, &l2, &l3) != buf || l1 != 3 || l2 != 9 || l3 != 6
|| memcmp (buf, "fghfghfgh\0h", 12) != 0)
abort ();
if (fn6 (buf, q, &l1, &l2) != buf || l1 != 12 || l2 != 9
|| memcmp (buf, "fghfghfghfgh", 13) != 0)
abort ();
return 0;
}

View File

@ -0,0 +1,6 @@
/* This test needs runtime that provides stpcpy function. */
/* { dg-do run { target *-*-linux* } } */
/* { dg-options "-O2" } */
#define USE_GNU
#include "strlenopt-12.c"

View File

@ -0,0 +1,68 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) void
fn1 (char *p, const char *y, const char *z, size_t *lp)
{
char *q, *r, *s;
char buf1[64], buf2[64];
size_t l[8];
/* These two strlen calls stay, all strcpy calls are optimized into
memcpy, all strchr calls optimized away, and most other strlen
calls too. */
l[0] = strlen (y);
l[1] = strlen (z);
strcpy (buf1, y);
strcpy (buf2, z);
strcpy (p, "abcde");
q = strchr (p, '\0');
strcpy (q, "efghi");
r = strchr (q, '\0');
strcpy (r, buf1);
l[2] = strlen (p);
l[3] = strlen (q);
l[4] = strlen (r);
strcpy (r, buf2);
/* Except for these two calls, strlen (r) before and after the above
is non-constant, so adding l[4] - l[1] to all previous strlens
might make the expressions already too complex. */
l[5] = strlen (p);
l[6] = strlen (q);
/* This one is of course optimized, it is l[1]. */
l[7] = strlen (r);
memcpy (lp, l, sizeof l);
}
int
main ()
{
char buf[64];
size_t l[8];
const char *volatile y = "ABCDEFG";
const char *volatile z = "HIJK";
memset (buf, '\0', sizeof buf);
fn1 (buf, y, z, l);
if (memcmp (buf, "abcdeefghiHIJK", 15) != 0)
abort ();
if (l[0] != 7 || l[1] != 4)
abort ();
if (l[2] != 17 || l[3] != 12 || l[4] != 7)
abort ();
if (l[5] != 14 || l[6] != 9 || l[7] != 4)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times " D\.\[0-9_\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.0. = " 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times " D\.\[0-9_\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.1. = " 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times " D\.\[0-9_\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.5. = " 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times " D\.\[0-9_\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.6. = " 1 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,115 @@
/* This test needs runtime that provides stpcpy and mempcpy functions. */
/* { dg-do run { target *-*-linux* } } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#define USE_GNU
#include "strlenopt.h"
__attribute__((noinline, noclone)) char *
fn1 (char *p, size_t *l1, size_t *l2)
{
char *a = mempcpy (p, "abcde", 6);
/* This strlen needs to stay. */
size_t la = strlen (a);
/* This strlen can be optimized into 5. */
size_t lp = strlen (p);
*l1 = la;
*l2 = lp;
return a;
}
__attribute__((noinline, noclone)) char *
fn2 (char *p, const char *q, size_t *l1, size_t *l2, size_t *l3)
{
/* This strlen needs to stay. */
size_t lq = strlen (q);
char *a = mempcpy (p, q, lq + 1);
/* This strlen needs to stay. */
size_t la = strlen (a);
/* This strlen can be optimized into lq. */
size_t lp = strlen (p);
*l1 = lq;
*l2 = la;
*l3 = lp;
return a;
}
__attribute__((noinline, noclone)) char *
fn3 (char *p, size_t *l1, size_t *l2)
{
char *a = stpcpy (p, "abcde");
/* This strlen can be optimized into 0. */
size_t la = strlen (a);
/* This strlen can be optimized into 5. */
size_t lp = strlen (p);
*l1 = la;
*l2 = lp;
return a;
}
__attribute__((noinline, noclone)) char *
fn4 (char *p, const char *q, size_t *l1, size_t *l2, size_t *l3)
{
/* This strlen needs to stay. */
size_t lq = strlen (q);
char *a = stpcpy (p, q);
/* This strlen can be optimized into 0. */
size_t la = strlen (a);
/* This strlen can be optimized into lq. */
size_t lp = strlen (p);
*l1 = lq;
*l2 = la;
*l3 = lp;
return a;
}
__attribute__((noinline, noclone)) char *
fn5 (char *p, const char *q, size_t *l1, size_t *l2)
{
char *a = stpcpy (p, q);
/* This strlen can be optimized into 0. */
size_t la = strlen (a);
/* This strlen can be optimized into a - p. */
size_t lp = strlen (p);
*l1 = la;
*l2 = lp;
return a;
}
int
main ()
{
char buf[64];
const char *volatile q = "ABCDEFGH";
size_t l1, l2, l3;
memset (buf, '\0', sizeof buf);
memset (buf + 6, 'z', 7);
if (fn1 (buf, &l1, &l2) != buf + 6 || l1 != 7 || l2 != 5
|| memcmp (buf, "abcde\0zzzzzzz", 14) != 0)
abort ();
if (fn2 (buf, q, &l1, &l2, &l3) != buf + 9 || l1 != 8 || l2 != 4 || l3 != 8
|| memcmp (buf, "ABCDEFGH\0zzzz", 14) != 0)
abort ();
if (fn3 (buf, &l1, &l2) != buf + 5 || l1 != 0 || l2 != 5
|| memcmp (buf, "abcde\0GH\0zzzz", 14) != 0)
abort ();
l3 = 0;
memset (buf, 'n', 9);
if (fn4 (buf, q, &l1, &l2, &l3) != buf + 8 || l1 != 8 || l2 != 0 || l3 != 8
|| memcmp (buf, "ABCDEFGH\0zzzz", 14) != 0)
abort ();
memset (buf, 'm', 9);
if (fn5 (buf, q, &l1, &l2) != buf + 8 || l1 != 0 || l2 != 8
|| memcmp (buf, "ABCDEFGH\0zzzz", 14) != 0)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,24 @@
/* This test needs runtime that provides stpcpy, mempcpy and __*_chk
functions. */
/* { dg-do run { target *-*-linux* } } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#define FORTIFY_SOURCE 2
#include "strlenopt-14g.c"
/* Compared to strlenopt-14gf.c, strcpy_chk with string literal as
second argument isn't being optimized by builtins.c into
memcpy. */
/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__mempcpy_chk \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 3 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,60 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) size_t
fn1 (char *p, size_t l)
{
memcpy (p, "abcdef", l);
/* This strlen can't be optimized, as l is unknown. */
return strlen (p);
}
__attribute__((noinline, noclone)) size_t
fn2 (char *p, const char *q, size_t *lp)
{
size_t l = strlen (q), l2;
memcpy (p, q, 7);
/* This strlen can't be optimized, as l might be bigger than 7. */
l2 = strlen (p);
*lp = l;
return l2;
}
__attribute__((noinline, noclone)) char *
fn3 (char *p)
{
*p = 0;
return p + 1;
}
int
main ()
{
char buf[64];
const char *volatile q = "ABCDEFGH";
const char *volatile q2 = "IJ\0KLMNOPQRS";
size_t l;
memset (buf, '\0', sizeof buf);
memset (buf + 2, 'a', 7);
if (fn1 (buf, 3) != 9 || memcmp (buf, "abcaaaaaa", 10) != 0)
abort ();
if (fn1 (buf, 7) != 6 || memcmp (buf, "abcdef\0aa", 10) != 0)
abort ();
if (fn2 (buf, q, &l) != 9 || l != 8 || memcmp (buf, "ABCDEFGaa", 10) != 0)
abort ();
if (fn2 (buf, q2, &l) != 2 || l != 2 || memcmp (buf, "IJ\0KLMNaa", 10) != 0)
abort ();
if (fn3 (buf) != buf + 1 || memcmp (buf, "\0J\0KLMNaa", 10) != 0)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,34 @@
/* This test needs runtime that provides stpcpy function. */
/* { dg-do run { target *-*-linux* } } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#define USE_GNU
#include "strlenopt.h"
__attribute__((noinline, noclone)) char *
fn1 (char *p, const char *q)
{
/* This strcpy can be optimized into stpcpy. */
strcpy (p, q);
/* And this strchr into the return value from it. */
return strchr (p, '\0');
}
int
main ()
{
char buf[64];
const char *volatile q = "ABCDEFGH";
if (fn1 (buf, q) != buf + 8 || memcmp (buf, "ABCDEFGH", 9) != 0)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,57 @@
/* This test needs runtime that provides stpcpy function. */
/* { dg-do run { target *-*-linux* } } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#define USE_GNU
#include "strlenopt.h"
__attribute__((noinline, noclone)) int
foo (const char *p)
{
static int c;
const char *q[] = { "123498765abcde", "123498765..", "129abcde", "129abcde" };
if (strcmp (p, q[c]) != 0)
abort ();
return c++;
}
__attribute__((noinline, noclone)) void
bar (const char *p, const char *q)
{
size_t l;
/* This strlen stays. */
char *a = __builtin_alloca (strlen (p) + 50);
/* strcpy can be optimized into memcpy. */
strcpy (a, p);
/* strcat into stpcpy. */
strcat (a, q);
/* This strlen can be optimized away. */
l = strlen (a);
/* This becomes memcpy. */
strcat (a, "abcde");
if (!foo (a))
/* And this one too. */
strcpy (a + l, "..");
foo (a);
}
int
main ()
{
const char *volatile s1 = "1234";
const char *volatile s2 = "98765";
const char *volatile s3 = "12";
const char *volatile s4 = "9";
bar (s1, s2);
bar (s3, s4);
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen" } } */
/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,82 @@
/* This test needs runtime that provides stpcpy function. */
/* { dg-do run { target *-*-linux* } } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#define USE_GNU
#include "strlenopt.h"
__attribute__((noinline, noclone)) char *
fn1 (int x, int y, int z)
{
static char buf[40];
const char *p;
switch (x)
{
case 0:
p = "abcd";
break;
case 1:
p = "efgh";
break;
case 2:
p = "ijkl";
break;
default:
p = "mnopq";
break;
}
if (y)
{
strcpy (buf, p);
if (z)
strcat (buf, "ABCDEFG");
else
strcat (buf, "HIJKLMN");
}
else
{
strcpy (buf, p + 1);
if (z)
strcat (buf, "OPQ");
else
strcat (buf, "RST");
}
return buf;
}
int
main ()
{
int i;
for (i = 0; i < 5; i++)
{
const char *p = "abcdefghijklmnopq" + (i < 3 ? i : 3) * 4;
const char *q;
int j = i >= 3;
fn1 (i ? 0 : 1, 1, 1);
q = fn1 (i, 0, 0);
if (memcmp (q, p + 1, 3 + j) != 0 || memcmp (q + 3 + j, "RST", 4) != 0)
abort ();
fn1 (i ? 0 : 1, 0, 1);
q = fn1 (i, 1, 0);
if (memcmp (q, p, 4 + j) != 0 || memcmp (q + 4 + j, "HIJKLMN", 8) != 0)
abort ();
fn1 (i ? 0 : 1, 1, 0);
q = fn1 (i, 0, 1);
if (memcmp (q, p + 1, 3 + j) != 0 || memcmp (q + 3 + j, "OPQ", 4) != 0)
abort ();
fn1 (i ? 0 : 1, 0, 0);
q = fn1 (i, 1, 1);
if (memcmp (q, p, 4 + j) != 0 || memcmp (q + 4 + j, "ABCDEFG", 8) != 0)
abort ();
}
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,81 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) char *
fn1 (int x, int y, int z)
{
static char buf[40];
const char *p;
switch (x)
{
case 0:
p = "abcd";
/* Prevent cswitch optimization. */
asm volatile ("" : : : "memory");
break;
case 1:
p = "efgh";
break;
case 2:
p = "ijkl";
break;
default:
p = "mnop";
break;
}
if (y)
{
strcpy (buf, p);
if (z)
strcat (buf, "ABCDEFG");
else
strcat (buf, "HIJKLMN");
}
else
{
strcpy (buf, p + 1);
if (z)
strcat (buf, "OPQ");
else
strcat (buf, "RST");
}
return buf;
}
int
main ()
{
int i;
for (i = 0; i < 5; i++)
{
const char *p = "abcdefghijklmnop" + (i < 3 ? i : 3) * 4;
const char *q;
fn1 (i ? 0 : 1, 1, 1);
q = fn1 (i, 0, 0);
if (memcmp (q, p + 1, 3) != 0 || memcmp (q + 3, "RST", 4) != 0)
abort ();
fn1 (i ? 0 : 1, 0, 1);
q = fn1 (i, 1, 0);
if (memcmp (q, p, 4) != 0 || memcmp (q + 4, "HIJKLMN", 8) != 0)
abort ();
fn1 (i ? 0 : 1, 1, 0);
q = fn1 (i, 0, 1);
if (memcmp (q, p + 1, 3) != 0 || memcmp (q + 3, "OPQ", 4) != 0)
abort ();
fn1 (i ? 0 : 1, 0, 0);
q = fn1 (i, 1, 1);
if (memcmp (q, p, 4) != 0 || memcmp (q + 4, "ABCDEFG", 8) != 0)
abort ();
}
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,18 @@
/* This test needs runtime that provides __*_chk functions. */
/* { dg-do run { target *-*-linux* } } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#define FORTIFY_SOURCE 2
#include "strlenopt-1.c"
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 3 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,49 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) char *
foo (char *p, char *r)
{
char buf[26];
if (strlen (p) + strlen (r) + 9 > 26)
return NULL;
/* This strcpy can be optimized into memcpy, using the remembered
strlen (p). */
strcpy (buf, p);
/* These two strcat can be optimized into memcpy. The first one
could be even optimized into a *ptr = '/'; store as the '\0'
is immediately overwritten. */
strcat (buf, "/");
strcat (buf, "abcde");
/* This strcpy can be optimized into memcpy, using the remembered
strlen (r). */
strcat (buf, r);
/* And this can be optimized into memcpy too. */
strcat (buf, "fg");
return strdup (buf);
}
int
main ()
{
char *volatile p = "string1";
char *volatile r = "string2";
char *q = foo (p, r);
if (q != NULL)
{
if (strcmp (q, "string1/abcdestring2fg"))
abort ();
free (q);
}
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 5 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,95 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) const char *
fn1 (int x, int y)
{
const char *p;
switch (x)
{
case 0:
p = "abcd";
/* Prevent cswitch optimization. */
asm volatile ("" : : : "memory");
break;
case 1:
p = "efgh";
break;
case 2:
p = "ijkl";
break;
default:
p = "mnop";
break;
}
if (y)
/* strchr should be optimized into p + 4 here. */
return strchr (p, '\0');
else
/* and strlen into 3. */
return p + strlen (p + 1);
}
__attribute__((noinline, noclone)) size_t
fn2 (char *p, char *q)
{
size_t l;
/* Both strcpy calls can be optimized into memcpy, strlen needs to stay. */
strcpy (p, "abc");
p[3] = 'd';
l = strlen (p);
strcpy (q, p);
return l;
}
__attribute__((noinline, noclone)) char *
fn3 (char *p)
{
char *c;
/* The strcpy call can be optimized into memcpy, strchr needs to stay,
strcat is optimized into memcpy. */
strcpy (p, "abc");
p[3] = 'd';
c = strchr (p, '\0');
strcat (p, "efgh");
return c;
}
int
main ()
{
int i;
char buf[64], buf2[64];
for (i = 0; i < 5; i++)
{
const char *p = "abcdefghijklmnop" + (i < 3 ? i : 3) * 4;
const char *q;
q = fn1 (i, 1);
if (memcmp (q - 4, p, 4) != 0 || q[0] != '\0')
abort ();
q = fn1 (i, 0);
if (memcmp (q - 3, p, 4) != 0 || q[1] != '\0')
abort ();
}
memset (buf, '\0', sizeof buf);
memset (buf + 4, 'z', 2);
if (fn2 (buf, buf2) != 6
|| memcmp (buf, "abcdzz", 7) != 0
|| memcmp (buf2, "abcdzz", 7) != 0)
abort ();
memset (buf, '\0', sizeof buf);
memset (buf + 4, 'z', 2);
if (fn3 (buf) != buf + 6 || memcmp (buf, "abcdzzefgh", 11) != 0)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,18 @@
/* This test needs runtime that provides __*_chk functions. */
/* { dg-do run { target *-*-linux* } } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#define FORTIFY_SOURCE 2
#include "strlenopt-2.c"
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 5 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,66 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen -fdump-tree-optimized" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) size_t
fn1 (char *p, char *q)
{
size_t s = strlen (q);
strcpy (p, q);
return s - strlen (p);
}
__attribute__((noinline, noclone)) size_t
fn2 (char *p, char *q)
{
size_t s = strlen (q);
memcpy (p, q, s + 1);
return s - strlen (p);
}
__attribute__((noinline, noclone)) size_t
fn3 (char *p)
{
memcpy (p, "abcd", 5);
return strlen (p);
}
__attribute__((noinline, noclone)) size_t
fn4 (char *p)
{
memcpy (p, "efg\0hij", 6);
return strlen (p);
}
int
main ()
{
char buf[64];
char *volatile p = buf;
char *volatile q = "ABCDEF";
buf[7] = 'G';
if (fn1 (p, q) != 0 || memcmp (buf, "ABCDEF\0G", 8))
abort ();
q = "HIJ";
if (fn2 (p + 1, q) != 0 || memcmp (buf, "AHIJ\0F\0G", 8))
abort ();
buf[6] = 'K';
if (fn3 (p + 1) != 4 || memcmp (buf, "Aabcd\0KG", 8))
abort ();
if (fn4 (p) != 3 || memcmp (buf, "efg\0hiKG", 8))
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */
/* { dg-final { scan-tree-dump-times "return 0" 3 "optimized" } } */
/* { dg-final { scan-tree-dump-times "return 4" 1 "optimized" } } */
/* { dg-final { scan-tree-dump-times "return 3" 1 "optimized" } } */
/* { dg-final { cleanup-tree-dump "optimized" } } */

View File

@ -0,0 +1,75 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
/* If stpcpy can't be used, this is optimized into
strcpy (p, q); strcat (p, r); memcpy (p + strlen (p), "abcd", 5);
If stpcpy can be used (see strlenopt-4g.c test),
this is optimized into
memcpy (stpcpy (stpcpy (p, q), r), "abcd", 5); */
__attribute__((noinline, noclone)) void
foo (char *p, const char *q, const char *r)
{
strcpy (p, q);
strcat (p, r);
strcat (p, "abcd");
}
/* If stpcpy can't be used, this is optimized into
memcpy (p, "abcd", 4); strcpy (p + 4, q); strcat (p, r);
If stpcpy can be used, this is optimized into
memcpy (p, "abcd", 4); strcpy (stpcpy (p + 4, q), r); */
__attribute__((noinline, noclone)) void
bar (char *p, const char *q, const char *r)
{
strcpy (p, "abcd");
strcat (p, q);
strcat (p, r);
}
/* If stpcpy can't be used, this is optimized into
strcat (p, q); memcpy (t1 = p + strlen (p), "abcd", 4);
strcpy (t1 + 4, r); memcpy (p + strlen (p), "efgh", 5);
If stpcpy can be used, this is optimized into
t1 = stpcpy (p + strlen (p), q); memcpy (t1, "abcd", 4);
memcpy (stpcpy (t1 + 4, r), "efgh", 5); */
__attribute__((noinline, noclone)) void
baz (char *p, const char *q, const char *r)
{
strcat (p, q);
strcat (p, "abcd");
strcat (p, r);
strcat (p, "efgh");
}
char buf[64];
int
main ()
{
char *volatile p = buf;
const char *volatile q = "ij";
const char *volatile r = "klmno";
foo (p, q, r);
if (memcmp (buf, "ijklmnoabcd\0\0\0\0\0\0\0\0", 20) != 0)
abort ();
memset (buf, '\0', sizeof buf);
bar (p, q, r);
if (memcmp (buf, "abcdijklmno\0\0\0\0\0\0\0\0", 20) != 0)
abort ();
memset (buf, 'v', 3);
memset (buf + 3, '\0', -3 + sizeof buf);
baz (p, q, r);
if (memcmp (buf, "vvvijabcdklmnoefgh\0", 20) != 0)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 3 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 3 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,14 @@
/* This test needs runtime that provides stpcpy function. */
/* { dg-do run { target *-*-linux* } } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#define USE_GNU
#include "strlenopt-4.c"
/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,19 @@
/* This test needs runtime that provides stpcpy and __*_chk functions. */
/* { dg-do run { target *-*-linux* } } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#define USE_GNU
#define FORTIFY_SOURCE 2
#include "strlenopt-4.c"
/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 5 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,57 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) char *
foo (char *p, const char *q)
{
char *e = strchr (p, '\0');
strcat (p, q);
return e;
}
__attribute__((noinline, noclone)) char *
bar (char *p)
{
memcpy (p, "abcd", 5);
return strchr (p, '\0');
}
__attribute__((noinline, noclone)) void
baz (char *p)
{
char *e = strchr (p, '\0');
strcat (e, "abcd");
}
char buf[64];
int
main ()
{
char *volatile p = buf;
const char *volatile q = "ij";
memset (buf, 'v', 3);
if (foo (p, q) != buf + 3
|| memcmp (buf, "vvvij\0\0\0\0", 10) != 0)
abort ();
memset (buf, '\0', sizeof buf);
if (bar (p) != buf + 4
|| memcmp (buf, "abcd\0\0\0\0\0", 10) != 0)
abort ();
memset (buf, 'v', 2);
memset (buf + 2, '\0', -2 + sizeof buf);
baz (p);
if (memcmp (buf, "vvabcd\0\0\0", 10) != 0)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,86 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) char *
foo (char *x)
{
#ifdef PR50262_FIXED
/* Once PTA is fixed, we'll need just one strlen here,
without the memcpy. */
char *p = x;
char *q = malloc (strlen (p) + 64);
#else
/* This is here just because PTA can't figure that
*q = '\0' store below can't change p's length.
In this case we have one strlen and one memcpy here. */
char b[64];
char *q = malloc (strlen (x) + 64);
char *p = strcpy (b, x);
#endif
char *r;
if (q == NULL) return NULL;
/* This store can be optimized away once strcat is
replaced with memcpy. */
*q = '\0';
/* These two strcat calls can be optimized into memcpy calls. */
strcat (q, p);
strcat (q, "/");
/* The strchr can be optimized away, as we know the current
string length as well as end pointer. */
r = strchr (q, '\0');
/* This store can go, as it is overwriting '\0' with the same
character. */
*r = '\0';
/* And this strcat can be again optimized into memcpy call. */
strcat (q, "abcde");
return q;
}
__attribute__((noinline, noclone)) char *
bar (char *p)
{
char buf[26];
char *r;
if (strlen (p) + 9 > 26)
return NULL;
*buf = '\0';
strcat (buf, p);
strcat (buf, "/");
r = strchr (buf, '\0');
*r = '\0';
strcat (buf, "abcde");
return strdup (buf);
}
int
main ()
{
char *volatile p = "string1";
char *volatile r = "string2";
char *q = foo (p);
if (q != NULL)
{
if (strcmp (q, "string1/abcde"))
abort ();
memset (q, '\0', 14);
free (q);
}
q = bar (p);
if (q != NULL)
{
if (strcmp (q, "string1/abcde"))
abort ();
free (q);
}
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,53 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen -fdump-tree-optimized" } */
#include "strlenopt.h"
char buf[64];
__attribute__((noinline, noclone)) size_t
foo (void)
{
char *p = memcpy (buf, "abcdefgh", 9);
/* This store can be optimized away as... */
*p = '\0';
/* ... the following strcat can be optimized into memcpy,
which overwrites that '\0'. */
strcat (p, "ijk");
/* This should be optimized into return 3. */
return strlen (p);
}
__attribute__((noinline, noclone)) size_t
bar (char *p)
{
char *r = strchr (p, '\0');
/* This store shouldn't be optimized away, because we
want to crash if p is e.g. a string literal. */
*r = '\0';
/* This strlen can be optimized into 0. */
return strlen (r);
}
int
main ()
{
char *volatile p = buf;
if (foo () != 3 || memcmp (buf, "ijk\0efgh\0", 10) != 0)
abort ();
if (bar (p) != 0 || memcmp (buf, "ijk\0efgh\0", 10) != 0)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "\\*r_\[0-9\]* = 0;" 1 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */
/* { dg-final { scan-tree-dump-times "return 3;" 1 "optimized" } } */
/* { dg-final { scan-tree-dump-times "return 0;" 2 "optimized" } } */
/* { dg-final { cleanup-tree-dump "optimized" } } */

View File

@ -0,0 +1,52 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen" } */
#include "strlenopt.h"
/* Yes, there are people who write code like this. */
__attribute__((noinline, noclone)) char *
foo (int r)
{
char buf[10] = "";
strcat (buf, r ? "r" : "w");
strcat (buf, "b");
return strdup (buf);
}
__attribute__((noinline, noclone)) char *
bar (int r)
{
char buf[10] = {};
strcat (buf, r ? "r" : "w");
strcat (buf, "b");
return strdup (buf);
}
int
main ()
{
char *q = foo (1);
if (q != NULL)
{
if (strcmp (q, "rb"))
abort ();
free (q);
}
q = bar (0);
if (q != NULL)
{
if (strcmp (q, "wb"))
abort ();
free (q);
}
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */

View File

@ -0,0 +1,109 @@
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-strlen -fdump-tree-optimized" } */
#include "strlenopt.h"
__attribute__((noinline, noclone)) char *
fn1 (int r)
{
char *p = r ? "a" : "bc";
/* String length for p varies, therefore strchr below isn't
optimized away. */
return strchr (p, '\0');
}
__attribute__((noinline, noclone)) size_t
fn2 (int r)
{
char *p, q[10];
strcpy (q, "abc");
p = r ? "a" : q;
/* String length for p varies, therefore strlen below isn't
optimized away. */
return strlen (p);
}
__attribute__((noinline, noclone)) size_t
fn3 (char *p, int n)
{
int i;
p = strchr (p, '\0');
/* strcat here can be optimized into memcpy. */
strcat (p, "abcd");
for (i = 0; i < n; i++)
if ((i % 123) == 53)
/* strcat here is optimized into strlen and memcpy. */
strcat (p, "efg");
/* The strlen here can't be optimized away, as in the loop string
length of p might change. */
return strlen (p);
}
char buf[64];
__attribute__((noinline, noclone)) size_t
fn4 (char *x, int n)
{
int i;
size_t l;
char a[64];
char *p = strchr (x, '\0');
/* strcpy here is optimized into memcpy, length computed as p - x + 1. */
strcpy (a, x);
/* strcat here is optimized into memcpy. */
strcat (p, "abcd");
for (i = 0; i < n; i++)
if ((i % 123) == 53)
/* strcat here is optimized into strlen and memcpy. */
strcat (a, "efg");
/* The strlen should be optimized here into 4. */
l = strlen (p);
/* This stays strcpy. */
strcpy (buf, a);
return l;
}
int
main ()
{
volatile int l = 1;
char b[64];
if (memcmp (fn1 (l) - 1, "a", 2) != 0)
abort ();
if (memcmp (fn1 (!l) - 2, "bc", 3) != 0)
abort ();
if (fn2 (l) != 1 || fn2 (!l) != 3)
abort ();
memset (b, '\0', sizeof b);
memset (b, 'a', 3);
if (fn3 (b, 10) != 4 || memcmp (b, "aaaabcd", 8) != 0)
abort ();
if (fn3 (b, 128) != 7 || memcmp (b, "aaaabcdabcdefg", 15) != 0)
abort ();
if (fn3 (b, 256) != 10 || memcmp (b, "aaaabcdabcdefgabcdefgefg", 25) != 0)
abort ();
if (fn4 (b, 10) != 4
|| memcmp (b, "aaaabcdabcdefgabcdefgefgabcd", 29) != 0
|| memcmp (buf, "aaaabcdabcdefgabcdefgefg", 25) != 0)
abort ();
if (fn4 (b, 128) != 4
|| memcmp (b, "aaaabcdabcdefgabcdefgefgabcdabcd", 33) != 0
|| memcmp (buf, "aaaabcdabcdefgabcdefgefgabcdefg", 32) != 0)
abort ();
if (fn4 (b, 256) != 4
|| memcmp (b, "aaaabcdabcdefgabcdefgefgabcdabcdabcd", 37) != 0
|| memcmp (buf, "aaaabcdabcdefgabcdefgefgabcdabcdefgefg", 39) != 0)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 3 "strlen" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
/* { dg-final { cleanup-tree-dump "strlen" } } */
/* { dg-final { scan-tree-dump-times "return 4;" 1 "optimized" } } */
/* { dg-final { cleanup-tree-dump "optimized" } } */

View File

@ -0,0 +1,59 @@
/* This is a replacement of needed parts from stdlib.h and string.h
for -foptimize-strlen testing, to ensure we are testing the builtins
rather than whatever the OS has in its headers. */
#define NULL ((void *) 0)
typedef __SIZE_TYPE__ size_t;
extern void abort (void);
void *malloc (size_t);
void free (void *);
char *strdup (const char *);
size_t strlen (const char *);
void *memcpy (void *__restrict, const void *__restrict, size_t);
char *strcpy (char *__restrict, const char *__restrict);
char *strcat (char *__restrict, const char *__restrict);
char *strchr (const char *, int);
void *memset (void *, int, size_t);
int memcmp (const void *, const void *, size_t);
int strcmp (const char *, const char *);
#ifdef USE_GNU
void *mempcpy (void *__restrict, const void *__restrict, size_t);
char *stpcpy (char *__restrict, const char *__restrict);
#endif
#if defined(FORTIFY_SOURCE) && FORTIFY_SOURCE > 0 && __OPTIMIZE__
# define bos(ptr) __builtin_object_size (ptr, FORTIFY_SOURCE > 0)
# define bos0(ptr) __builtin_object_size (ptr, 0)
extern inline __attribute__((gnu_inline, always_inline, artificial)) void *
memcpy (void *__restrict dest, const void *__restrict src, size_t len)
{
return __builtin___memcpy_chk (dest, src, len, bos0 (dest));
}
extern inline __attribute__((gnu_inline, always_inline, artificial)) char *
strcpy (char *__restrict dest, const char *__restrict src)
{
return __builtin___strcpy_chk (dest, src, bos (dest));
}
extern inline __attribute__((gnu_inline, always_inline, artificial)) char *
strcat (char *__restrict dest, const char *__restrict src)
{
return __builtin___strcat_chk (dest, src, bos (dest));
}
# ifdef USE_GNU
extern inline __attribute__((gnu_inline, always_inline, artificial)) void *
mempcpy (void *__restrict dest, const void *__restrict src, size_t len)
{
return __builtin___mempcpy_chk (dest, src, len, bos0 (dest));
}
extern inline __attribute__((gnu_inline, always_inline, artificial)) char *
stpcpy (char *__restrict dest, const char *__restrict src)
{
return __builtin___stpcpy_chk (dest, src, bos (dest));
}
# endif
#endif

View File

@ -1,7 +1,7 @@
/* This file contains the definitions for timing variables used to
measure run-time performance of the compiler.
Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
2009, 2010
2009, 2010, 2011
Free Software Foundation, Inc.
Contributed by Alex Samuel <samuel@codesourcery.com>
@ -184,6 +184,7 @@ DEFTIMEVAR (TV_TREE_COPY_RENAME , "tree rename SSA copies")
DEFTIMEVAR (TV_TREE_SSA_VERIFY , "tree SSA verifier")
DEFTIMEVAR (TV_TREE_STMT_VERIFY , "tree STMT verifier")
DEFTIMEVAR (TV_TREE_SWITCH_CONVERSION, "tree switch initialization conversion")
DEFTIMEVAR (TV_TREE_STRLEN , "tree strlen optimization")
DEFTIMEVAR (TV_CGRAPH_VERIFY , "callgraph verifier")
DEFTIMEVAR (TV_DOM_FRONTIERS , "dominance frontiers")
DEFTIMEVAR (TV_DOMINANCE , "dominance computation")

View File

@ -413,6 +413,7 @@ extern struct gimple_opt_pass pass_diagnose_omp_blocks;
extern struct gimple_opt_pass pass_expand_omp;
extern struct gimple_opt_pass pass_expand_omp_ssa;
extern struct gimple_opt_pass pass_object_sizes;
extern struct gimple_opt_pass pass_strlen;
extern struct gimple_opt_pass pass_fold_builtins;
extern struct gimple_opt_pass pass_stdarg;
extern struct gimple_opt_pass pass_early_warn_uninitialized;

1997
gcc/tree-ssa-strlen.c Normal file

File diff suppressed because it is too large Load Diff