Commit 23aa9f7c by Martin Sebor Committed by Martin Sebor

PR tree-optimization/71625 - missing strlen optimization on different array initialization style

gcc/c/ChangeLog:

	PR tree-optimization/71625
	* c-parser.c (c_parser_declaration_or_fndef): Call
	braced_list_to_string.

gcc/c-family/ChangeLog:

	PR tree-optimization/71625
	* c-common.c (braced_list_to_string): New function.
	* c-common.h (braced_list_to_string): Declare it.

gcc/cp/ChangeLog:

	PR tree-optimization/71625
	* decl.c (check_initializer):  Call braced_list_to_string.
	(eval_check_narrowing): New function.
	* gcc/cp/typeck2.c (digest_init_r): Accept strings literals
	as initilizers for all narrow character types.

gcc/testsuite/ChangeLog:

	PR tree-optimization/71625

	* g++.dg/init/string2.C: New test.
	* g++.dg/init/string3.C: New test.
	* g++.dg/init/string4.C: New test.
	* gcc.dg/init-string-3.c: New test.
	* gcc.dg/strlenopt-55.c: New test.
	* gcc.dg/strlenopt-56.c: New test.

From-SVN: r263511
parent e6a1e5fe
2018-08-13 Martin Sebor <msebor@redhat.com>
PR tree-optimization/71625
* c-common.c (braced_list_to_string): New function.
* c-common.h (braced_list_to_string): Declare it.
2018-08-08 Nathan Sidwell <nathan@acm.org> 2018-08-08 Nathan Sidwell <nathan@acm.org>
* c-common.c (try_to_locate_new_include_inertion_point): Use * c-common.c (try_to_locate_new_include_inertion_point): Use
......
...@@ -8509,4 +8509,96 @@ maybe_add_include_fixit (rich_location *richloc, const char *header) ...@@ -8509,4 +8509,96 @@ maybe_add_include_fixit (rich_location *richloc, const char *header)
free (text); free (text);
} }
/* Attempt to convert a braced array initializer list CTOR for array
TYPE into a STRING_CST for convenience and efficiency. When non-null,
use EVAL to attempt to evalue constants (used by C++). Return
the converted string on success or null on failure. */
tree
braced_list_to_string (tree type, tree ctor, tree (*eval)(tree, tree))
{
unsigned HOST_WIDE_INT nelts = CONSTRUCTOR_NELTS (ctor);
/* If the array has an explicit bound, use it to constrain the size
of the string. If it doesn't, be sure to create a string that's
as long as implied by the index of the last zero specified via
a designator, as in:
const char a[] = { [7] = 0 }; */
unsigned HOST_WIDE_INT maxelts = HOST_WIDE_INT_M1U;
if (tree size = TYPE_SIZE_UNIT (type))
{
if (tree_fits_uhwi_p (size))
{
maxelts = tree_to_uhwi (size);
maxelts /= tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (type)));
/* Avoid converting initializers for zero-length arrays. */
if (!maxelts)
return NULL_TREE;
}
}
else if (!nelts)
/* Avoid handling the undefined/erroneous case of an empty
initializer for an arrays with unspecified bound. */
return NULL_TREE;
tree eltype = TREE_TYPE (type);
auto_vec<char> str;
str.reserve (nelts + 1);
unsigned HOST_WIDE_INT i;
tree index, value;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), i, index, value)
{
unsigned HOST_WIDE_INT idx = index ? tree_to_uhwi (index) : i;
/* auto_vec is limited to UINT_MAX elements. */
if (idx > UINT_MAX)
return NULL_TREE;
/* Attempt to evaluate constants. */
if (eval)
value = eval (eltype, value);
/* Avoid non-constant initializers. */
if (!tree_fits_shwi_p (value))
return NULL_TREE;
/* Skip over embedded nuls except the last one (initializer
elements are in ascending order of indices). */
HOST_WIDE_INT val = tree_to_shwi (value);
if (!val && i + 1 < nelts)
continue;
/* Bail if the CTOR has a block of more than 256 embedded nuls
due to implicitly initialized elements. */
unsigned nchars = (idx - str.length ()) + 1;
if (nchars > 256)
return NULL_TREE;
if (nchars > 1)
{
str.reserve (idx);
str.quick_grow_cleared (idx);
}
if (idx > maxelts)
return NULL_TREE;
str.safe_insert (idx, val);
}
if (!nelts)
/* Append a nul for the empty initializer { }. */
str.safe_push (0);
/* Build a STRING_CST with the same type as the array, which
may be an array of unknown bound. */
tree res = build_string (str.length (), str.begin ());
TREE_TYPE (res) = type;
return res;
}
#include "gt-c-family-c-common.h" #include "gt-c-family-c-common.h"
...@@ -1331,6 +1331,7 @@ extern void maybe_add_include_fixit (rich_location *, const char *); ...@@ -1331,6 +1331,7 @@ extern void maybe_add_include_fixit (rich_location *, const char *);
extern void maybe_suggest_missing_token_insertion (rich_location *richloc, extern void maybe_suggest_missing_token_insertion (rich_location *richloc,
enum cpp_ttype token_type, enum cpp_ttype token_type,
location_t prev_token_loc); location_t prev_token_loc);
extern tree braced_list_to_string (tree, tree, tree (*)(tree, tree) = NULL);
#if CHECKING_P #if CHECKING_P
namespace selftest { namespace selftest {
......
2018-08-13 Martin Sebor <msebor@redhat.com>
PR tree-optimization/71625
* c-parser.c (c_parser_declaration_or_fndef): Call
braced_list_to_string.
2018-08-03 Bogdan Harjoc <harjoc@gmail.com> 2018-08-03 Bogdan Harjoc <harjoc@gmail.com>
PR c/86690 PR c/86690
......
...@@ -2126,6 +2126,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, ...@@ -2126,6 +2126,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
if (d != error_mark_node) if (d != error_mark_node)
{ {
maybe_warn_string_init (init_loc, TREE_TYPE (d), init); maybe_warn_string_init (init_loc, TREE_TYPE (d), init);
/* Try to convert a string CONSTRUCTOR into a STRING_CST. */
tree valtype = TREE_TYPE (init.value);
if (TREE_CODE (init.value) == CONSTRUCTOR
&& TREE_CODE (valtype) == ARRAY_TYPE
&& TYPE_STRING_FLAG (TREE_TYPE (valtype)))
if (tree str = braced_list_to_string (valtype, init.value))
init.value = str;
finish_decl (d, init_loc, init.value, finish_decl (d, init_loc, init.value,
init.original_type, asm_name); init.original_type, asm_name);
} }
......
2018-08-13 Martin Sebor <msebor@redhat.com>
PR tree-optimization/71625
* decl.c (check_initializer): Call braced_list_to_string.
(eval_check_narrowing): New function.
* gcc/cp/typeck2.c (digest_init_r): Accept strings literals
as initilizers for all narrow character types.
2018-08-13 Marek Polacek <polacek@redhat.com> 2018-08-13 Marek Polacek <polacek@redhat.com>
P0806R2 - Deprecate implicit capture of this via [=] P0806R2 - Deprecate implicit capture of this via [=]
......
...@@ -6282,6 +6282,30 @@ build_aggr_init_full_exprs (tree decl, tree init, int flags) ...@@ -6282,6 +6282,30 @@ build_aggr_init_full_exprs (tree decl, tree init, int flags)
return build_aggr_init (decl, init, flags, tf_warning_or_error); return build_aggr_init (decl, init, flags, tf_warning_or_error);
} }
/* Attempt to determine the constant VALUE of integral type and convert
it to TYPE, issuing narrowing warnings/errors as necessary. Return
the constant result or null on failure. Callback for
braced_list_to_string. */
static tree
eval_check_narrowing (tree type, tree value)
{
if (tree valtype = TREE_TYPE (value))
{
if (TREE_CODE (valtype) != INTEGER_TYPE)
return NULL_TREE;
}
else
return NULL_TREE;
value = scalar_constant_value (value);
if (!value)
return NULL_TREE;
check_narrowing (type, value, tf_warning_or_error);
return value;
}
/* Verify INIT (the initializer for DECL), and record the /* Verify INIT (the initializer for DECL), and record the
initialization in DECL_INITIAL, if appropriate. CLEANUP is as for initialization in DECL_INITIAL, if appropriate. CLEANUP is as for
grok_reference_init. grok_reference_init.
...@@ -6397,7 +6421,17 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups) ...@@ -6397,7 +6421,17 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
} }
else else
{ {
init = reshape_init (type, init, tf_warning_or_error); /* Try to convert a string CONSTRUCTOR into a STRING_CST. */
tree valtype = TREE_TYPE (decl);
if (TREE_CODE (valtype) == ARRAY_TYPE
&& TYPE_STRING_FLAG (TREE_TYPE (valtype))
&& BRACE_ENCLOSED_INITIALIZER_P (init))
if (tree str = braced_list_to_string (valtype, init,
eval_check_narrowing))
init = str;
if (TREE_CODE (init) != STRING_CST)
init = reshape_init (type, init, tf_warning_or_error);
flags |= LOOKUP_NO_NARROWING; flags |= LOOKUP_NO_NARROWING;
} }
} }
......
...@@ -1056,7 +1056,9 @@ digest_init_r (tree type, tree init, int nested, int flags, ...@@ -1056,7 +1056,9 @@ digest_init_r (tree type, tree init, int nested, int flags,
if (TYPE_PRECISION (typ1) == BITS_PER_UNIT) if (TYPE_PRECISION (typ1) == BITS_PER_UNIT)
{ {
if (char_type != char_type_node) if (char_type != char_type_node
&& char_type != signed_char_type_node
&& char_type != unsigned_char_type_node)
{ {
if (complain & tf_error) if (complain & tf_error)
error_at (loc, "char-array initialized from wide string"); error_at (loc, "char-array initialized from wide string");
......
2018-08-13 Martin Sebor <msebor@redhat.com>
PR tree-optimization/71625
* c-c++-common/attr-nonstring-3.c: Temporarily xfail a test case.
* g++.dg/init/string2.C: New test.
* g++.dg/init/string3.C: New test.
* g++.dg/init/string4.C: New test.
* gcc.dg/init-string-3.c: New test.
* gcc.dg/strlenopt-55.c: New test.
* gcc.dg/strlenopt-56.c: New test.
2018-08-13 Marek Polacek <polacek@redhat.com> 2018-08-13 Marek Polacek <polacek@redhat.com>
P0806R2 - Deprecate implicit capture of this via [=] P0806R2 - Deprecate implicit capture of this via [=]
......
...@@ -406,7 +406,7 @@ void test_strlen (struct MemArrays *p, char *s NONSTRING, size_t n) ...@@ -406,7 +406,7 @@ void test_strlen (struct MemArrays *p, char *s NONSTRING, size_t n)
{ {
char a[] __attribute__ ((nonstring)) = { 1, 2, 3 }; char a[] __attribute__ ((nonstring)) = { 1, 2, 3 };
T (strlen (a)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ T (strlen (a)); /* { dg-warning "argument 1 declared attribute .nonstring." "pr86688" { xfail *-*-* } } */
} }
{ {
......
// PR tree-optimization/71625 - missing strlen optimization on different
// array initialization style
//
// Verify that strlen() calls with constant character array arguments
// initialized with string constants are folded. (This is a small
// subset of pr71625).
// { dg-do compile }
// { dg-options "-O0 -Wno-error=narrowing -fdump-tree-gimple" }
#define A(expr) do { typedef char A[-1 + 2 * !!(expr)]; } while (0)
/* This is undefined but accepted without -Wpedantic. Verify that
the size is zero. */
const char ax[] = { };
void size0 ()
{
A (sizeof ax == 0);
}
const char a0[] = { 'a', 'b', 'c', '\0' };
int len0 ()
{
return __builtin_strlen (a0);
}
// Verify that narrowing warnings are preserved.
const signed char
sa0[] = { 'a', 'b', 255, '\0' }; // { dg-warning "\\\[\(-Wnarrowing|-Woverflow\)" "" { target { ! c++98_only } } }
int lens0 ()
{
return __builtin_strlen ((const char*)sa0);
}
const unsigned char
ua0[] = { 'a', 'b', -1, '\0' }; // { dg-warning "\\\[\(-Wnarrowing|-Woverflow\)" "" { target { ! c++98_only } } }
int lenu0 ()
{
return __builtin_strlen ((const char*)ua0);
}
const char c = 0;
const char a1[] = { 'a', 'b', 'c', c };
int len1 ()
{
return __builtin_strlen (a1);
}
template <class T>
int tmplen ()
{
static const T
a[] = { 1, 2, 333, 0 }; // { dg-warning "\\\[\(-Wnarrowing|-Woverflow\)" "" { target { ! c++98_only } } }
return __builtin_strlen (a);
}
template int tmplen<char>();
const wchar_t ws4[] = { 1, 2, 3, 4 };
const wchar_t ws7[] = { 1, 2, 3, 4, 0, 0, 0 };
const wchar_t ws9[9] = { 1, 2, 3, 4, 0 };
void wsize ()
{
A (sizeof ws4 == 4 * sizeof *ws4);
A (ws4[0] == 1 && ws4[1] == 2 && ws4[2] == 3 && ws4[3] == 4);
A (sizeof ws7 == 7 * sizeof *ws7);
A (ws7[0] == 1 && ws7[1] == 2 && ws7[2] == 3 && ws7[4] == 4
&& !ws7[5] && !ws7[6]);
A (sizeof ws9 == 9 * sizeof *ws9);
A (ws9[0] == 1 && ws9[1] == 2 && ws9[2] == 3 && ws9[4] == 4
&& !ws9[5] && !ws9[6] && !ws9[7] && !ws9[8]);
}
#if 0
// The following aren't handled.
const char &cref = c;
const char a2[] = { 'a', 'b', 'c', cref };
int len2 ()
{
return __builtin_strlen (a2);
}
const char* const cptr = &cref;
const char a3[] = { 'a', 'b', 'c', *cptr };
int len3 ()
{
return __builtin_strlen (a3);
}
#endif
// { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
// PR tree-optimization/71625 - missing strlen optimization on different
// array initialization style
//
// Verify that strlen() call with a constant character array argument
// initialized with non-constant elements isn't folded.
//
// { dg-do compile }
// { dg-options "-O2 -fdump-tree-optimized" }
extern const char c;
const char a0[] = { 'a', 'b', 'c', c };
int len0 ()
{
return __builtin_strlen (a0);
}
const char &ref = c;
const char a1[] = { 'a', 'b', 'c', ref };
int len1 ()
{
return __builtin_strlen (a1);
}
const char* const ptr = &c;
const char a2[] = { 'a', 'b', 'c', *ptr };
int len2 ()
{
return __builtin_strlen (a2);
}
// { dg-final { scan-tree-dump-times "strlen" 3 "optimized" } }
// PR tree-optimization/71625 - missing strlen optimization on different
// array initialization style
// Verify that zero-length array initialization results in the expected
// array sizes and in the expected diagnostics. See init-string-3.c
// for the corresponding C test.
// { dg-do compile }
// { dg-options "-Wall -Wno-unused-local-typedefs -fpermissive" }
#define A(expr) typedef char A[-1 + 2 * !!(expr)];
const char a[] = { };
A (sizeof a == 0);
const char b[0] = { };
A (sizeof b == 0);
// Also verify that the error is "too many initializers for
// 'const char [0]'" and not "initializer-string is too long."
const char c[0] = { 1 }; // { dg-error "too many initializers for .const char \\\[0]" }
A (sizeof c == 0);
void test_auto_empty (void)
{
const char a[] = { };
A (sizeof a == 0);
}
void test_auto_zero_length (void)
{
const char a[0] = { };
A (sizeof a == 0);
const char b[0] = { 0 }; // { dg-error "too many initializers" }
A (sizeof b == 0);
const char c[0] = ""; // { dg-warning "too long" }
A (sizeof c == 0);
}
void test_compound_zero_length (void)
{
A (sizeof (const char[]){ } == 0);
A (sizeof (const char[0]){ } == 0);
A (sizeof (const char[0]){ 0 } == 0); // { dg-error "too many" }
A (sizeof (const char[0]){ 1 } == 0); // { dg-error "too many" }
A (sizeof (const char[0]){ "" } == 0); // { dg-warning "too long" }
A (sizeof (const char[0]){ "1" } == 0); // { dg-warning "too long" }
}
/* PR tree-optimization/71625 - missing strlen optimization on different
array initialization style
Verify that zero-length array initialization results in the expected
array sizes.
{ dg-do compile }
{ dg-options "-Wall -Wno-unused-local-typedefs" } */
#define A(expr) typedef char A[-1 + 2 * !!(expr)];
const char a[] = { };
A (sizeof a == 0);
const char b[0] = { };
A (sizeof b == 0);
const char c[0] = { 1 }; /* { dg-warning "excess elements" } */
A (sizeof c == 0);
void test_auto_empty (void)
{
const char a[] = { };
A (sizeof a == 0);
}
void test_auto_zero_length (void)
{
const char a[0] = { };
A (sizeof a == 0);
const char b[0] = { 0 }; /* { dg-warning "excess elements" } */
A (sizeof b == 0);
const char c[0] = "";
A (sizeof c == 0);
}
void test_compound_zero_length (void)
{
A (sizeof (const char[]){ } == 0);
A (sizeof (const char[0]){ } == 0);
A (sizeof (const char[0]){ 0 } == 0); /* { dg-warning "excess elements" } */
A (sizeof (const char[0]){ 1 } == 0); /* { dg-warning "excess elements" } */
A (sizeof (const char[0]){ "" } == 0);
A (sizeof (const char[0]){ "1" } == 0); /* { dg-warning "too long" } */
}
/* PR tree-optimization/71625 - conversion of braced initializers to strings
Verify that array elements have the expected values regardless of sign
and non-ASCII execution character set.
{ dg-do compile }
{ dg-require-iconv "IBM1047" }
{ dg-options "-O -Wall -fexec-charset=IBM1047 -fdump-tree-gimple -fdump-tree-optimized" } */
#include "strlenopt.h"
const char a[] = { 'a', 129, 0 };
const signed char b[] = { 'b', 130, 0 };
const unsigned char c[] = { 'c', 131, 0 };
const char s[] = "a\201";
const signed char ss[] = "b\202";
const unsigned char us[] = "c\203";
#define A(expr) ((expr) ? (void)0 : __builtin_abort ())
void test_values (void)
{
A (a[0] == a[1]);
A (a[1] == 'a');
A (b[0] == b[1]);
A (b[1] == (signed char)'b');
A (c[0] == c[1]);
A (c[1] == (unsigned char)'c');
}
void test_lengths (void)
{
A (2 == strlen (a));
A (2 == strlen ((const char*)b));
A (2 == strlen ((const char*)c));
}
void test_contents (void)
{
A (0 == strcmp (a, s));
A (0 == strcmp ((const char*)b, (const char*)ss));
A (0 == strcmp ((const char*)c, (const char*)us));
}
/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "strcmp" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "abort" 0 "optimized" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment