diff options
Diffstat (limited to 'gcc/testsuite/gcc.dg')
27 files changed, 1584 insertions, 0 deletions
diff --git a/gcc/testsuite/gcc.dg/strlenopt-1.c b/gcc/testsuite/gcc.dg/strlenopt-1.c new file mode 100644 index 00000000000..5bc4f0cd176 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-1.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-10.c b/gcc/testsuite/gcc.dg/strlenopt-10.c new file mode 100644 index 00000000000..a18c06ae069 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-10.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-11.c b/gcc/testsuite/gcc.dg/strlenopt-11.c new file mode 100644 index 00000000000..03f8790d0c6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-11.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-12.c b/gcc/testsuite/gcc.dg/strlenopt-12.c new file mode 100644 index 00000000000..1005fc6709e --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-12.c @@ -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; +} diff --git a/gcc/testsuite/gcc.dg/strlenopt-12g.c b/gcc/testsuite/gcc.dg/strlenopt-12g.c new file mode 100644 index 00000000000..2b6508f01d9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-12g.c @@ -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" diff --git a/gcc/testsuite/gcc.dg/strlenopt-13.c b/gcc/testsuite/gcc.dg/strlenopt-13.c new file mode 100644 index 00000000000..62effcd64d0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-13.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-14g.c b/gcc/testsuite/gcc.dg/strlenopt-14g.c new file mode 100644 index 00000000000..62a120de07b --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-14g.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-14gf.c b/gcc/testsuite/gcc.dg/strlenopt-14gf.c new file mode 100644 index 00000000000..999759e86f6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-14gf.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-15.c b/gcc/testsuite/gcc.dg/strlenopt-15.c new file mode 100644 index 00000000000..495166ddb9b --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-15.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-16g.c b/gcc/testsuite/gcc.dg/strlenopt-16g.c new file mode 100644 index 00000000000..11e4d319adb --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-16g.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-17g.c b/gcc/testsuite/gcc.dg/strlenopt-17g.c new file mode 100644 index 00000000000..b61bf74b8b2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-17g.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-18g.c b/gcc/testsuite/gcc.dg/strlenopt-18g.c new file mode 100644 index 00000000000..c70daea4b28 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-18g.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-19.c b/gcc/testsuite/gcc.dg/strlenopt-19.c new file mode 100644 index 00000000000..042fd77b002 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-19.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-1f.c b/gcc/testsuite/gcc.dg/strlenopt-1f.c new file mode 100644 index 00000000000..4b0207fd4f7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-1f.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-2.c b/gcc/testsuite/gcc.dg/strlenopt-2.c new file mode 100644 index 00000000000..5e6557b5687 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-2.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-20.c b/gcc/testsuite/gcc.dg/strlenopt-20.c new file mode 100644 index 00000000000..6fe99a422e9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-20.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-2f.c b/gcc/testsuite/gcc.dg/strlenopt-2f.c new file mode 100644 index 00000000000..7996e67618c --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-2f.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-3.c b/gcc/testsuite/gcc.dg/strlenopt-3.c new file mode 100644 index 00000000000..1bab8f37e99 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-3.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-4.c b/gcc/testsuite/gcc.dg/strlenopt-4.c new file mode 100644 index 00000000000..beea4959245 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-4.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-4g.c b/gcc/testsuite/gcc.dg/strlenopt-4g.c new file mode 100644 index 00000000000..7b397366e9c --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-4g.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-4gf.c b/gcc/testsuite/gcc.dg/strlenopt-4gf.c new file mode 100644 index 00000000000..cf99212a152 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-4gf.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-5.c b/gcc/testsuite/gcc.dg/strlenopt-5.c new file mode 100644 index 00000000000..131494ac8e4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-5.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-6.c b/gcc/testsuite/gcc.dg/strlenopt-6.c new file mode 100644 index 00000000000..d9b718758a5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-6.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-7.c b/gcc/testsuite/gcc.dg/strlenopt-7.c new file mode 100644 index 00000000000..6fd940d748b --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-7.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-8.c b/gcc/testsuite/gcc.dg/strlenopt-8.c new file mode 100644 index 00000000000..3aaf660a12c --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-8.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-9.c b/gcc/testsuite/gcc.dg/strlenopt-9.c new file mode 100644 index 00000000000..6590d708ec7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-9.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt.h b/gcc/testsuite/gcc.dg/strlenopt.h new file mode 100644 index 00000000000..ef47e5ac9ad --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt.h @@ -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 |