mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			gimple-fold.h (get_range_strlen): Update prototype.
* gimple-fold.h (get_range_strlen): Update prototype. * builtins.c (check_access): Update call to get_range_strlen to use c_strlen_data pointer. Change various variable accesses to instead pull data from the c_strlen_data structure. (check_strncat_sizes, expand_builtin_strncat): Likewise. * calls.c (maybe_warn_nonstring_arg): Likewise. * tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Likewise. Reset minimum length if maximum lengh is unknown. * gimple-ssa-sprintf.c (get_string_length): Likewise. Drop code that used c_strlen, it's no longer needed. Restructure slightly. (format_string): Set unlikely range appropriately. * gimple-fold.c (get_range_strlen): Update comments. Fix minor formatting issues. (get_range_strlen): Accept c_strlen_data pointer for external call sites as well. Pass through to call to internal get_range_strlen. Adjust minlen, maxlen and maxbound as needed. (get_maxval_strlen): Update comments. (gimple_fold_builtin_strlen): Update call to get_range_strlen to use c_strlen_data pointer. Change variable accesses to instead use c_strlen_data data members. * gcc.dg/strlenopt-40.c: Disable a couple tests. * gcc.dg/strlenopt-48.c: Twiddle test. * gcc.dg/strlenopt-59.c: New test. * gcc.dg/tree-ssa/builtin-snprintf-5.c: New test. * g++.dg/init/strlen.C: New test. Co-Authored-By: Jeff Law <law@redhat.com> From-SVN: r267503
This commit is contained in:
		
							parent
							
								
									79b1c2295b
								
							
						
					
					
						commit
						5d6655ebcc
					
				|  | @ -1,6 +1,27 @@ | |||
| 2019-01-01  Martin Sebor  <msebor@redhat.com> | ||||
|             Jeff Law  <law@redhat.com> | ||||
| 
 | ||||
| 	* gimple-fold.h (get_range_strlen): Update prototype. | ||||
| 	* builtins.c (check_access): Update call to get_range_strlen to use | ||||
| 	c_strlen_data pointer.   Change various variable accesses to instead | ||||
| 	pull data from the c_strlen_data structure. | ||||
| 	(check_strncat_sizes, expand_builtin_strncat): Likewise. | ||||
| 	* calls.c (maybe_warn_nonstring_arg): Likewise. | ||||
| 	* tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Likewise.  Reset | ||||
| 	minimum length if maximum lengh is unknown. | ||||
| 	* gimple-ssa-sprintf.c (get_string_length): Likewise.  Drop code | ||||
| 	that used c_strlen, it's no longer needed.  Restructure slightly. | ||||
| 	(format_string): Set unlikely range appropriately. | ||||
| 	* gimple-fold.c (get_range_strlen): Update comments.  Fix minor | ||||
| 	formatting issues. | ||||
| 	(get_range_strlen):  Accept c_strlen_data pointer for external | ||||
| 	call sites as well.  Pass through to call to internal get_range_strlen. | ||||
| 	Adjust minlen, maxlen and maxbound as needed. | ||||
| 	(get_maxval_strlen): Update comments. | ||||
| 	(gimple_fold_builtin_strlen):  Update call to get_range_strlen | ||||
| 	to use c_strlen_data pointer.  Change variable accesses to instead | ||||
| 	use c_strlen_data data members. | ||||
| 
 | ||||
| 	* gimple-fold.c (get_range_strlen): Update prototype. | ||||
| 	(get_range_strlen_tree): Update prototype.  Drop minlen/maxlen | ||||
| 	local variables.  Use pdata to return information to caller. | ||||
|  |  | |||
|  | @ -3341,7 +3341,10 @@ check_access (tree exp, tree, tree, tree dstwrite, | |||
| 	     the upper bound given by MAXREAD add one to it for | ||||
| 	     the terminating nul.  Otherwise, set it to one for | ||||
| 	     the same reason, or to MAXREAD as appropriate.  */ | ||||
| 	  get_range_strlen (srcstr, range); | ||||
| 	  c_strlen_data lendata = { }; | ||||
| 	  get_range_strlen (srcstr, &lendata, /* eltsize = */ 1); | ||||
| 	  range[0] = lendata.minlen; | ||||
| 	  range[1] = lendata.maxbound; | ||||
| 	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST)) | ||||
| 	    { | ||||
| 	      if (maxread && tree_int_cst_le (maxread, range[0])) | ||||
|  | @ -4209,8 +4212,8 @@ check_strncat_sizes (tree exp, tree objsize) | |||
| 
 | ||||
|   /* Try to determine the range of lengths that the source expression
 | ||||
|      refers to.  */ | ||||
|   tree lenrange[2]; | ||||
|   get_range_strlen (src, lenrange); | ||||
|   c_strlen_data lendata = { }; | ||||
|   get_range_strlen (src, &lendata, /* eltsize = */ 1); | ||||
| 
 | ||||
|   /* Try to verify that the destination is big enough for the shortest
 | ||||
|      string.  */ | ||||
|  | @ -4224,8 +4227,8 @@ check_strncat_sizes (tree exp, tree objsize) | |||
|     } | ||||
| 
 | ||||
|   /* Add one for the terminating nul.  */ | ||||
|   tree srclen = (lenrange[0] | ||||
| 		 ? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0], | ||||
|   tree srclen = (lendata.minlen | ||||
| 		 ? fold_build2 (PLUS_EXPR, size_type_node, lendata.minlen, | ||||
| 				size_one_node) | ||||
| 		 : NULL_TREE); | ||||
| 
 | ||||
|  | @ -4277,12 +4280,15 @@ expand_builtin_strncat (tree exp, rtx) | |||
|   tree slen = c_strlen (src, 1); | ||||
| 
 | ||||
|   /* Try to determine the range of lengths that the source expression
 | ||||
|      refers to.  */ | ||||
|   tree lenrange[2]; | ||||
|   if (slen) | ||||
|     lenrange[0] = lenrange[1] = slen; | ||||
|   else | ||||
|     get_range_strlen (src, lenrange); | ||||
|      refers to.  Since the lengths are only used for warning and not | ||||
|      for code generation disable strict mode below.  */ | ||||
|   tree maxlen = slen; | ||||
|   if (!maxlen) | ||||
|     { | ||||
|       c_strlen_data lendata = { }; | ||||
|       get_range_strlen (src, &lendata, /* eltsize = */ 1); | ||||
|       maxlen = lendata.maxbound; | ||||
|     } | ||||
| 
 | ||||
|   /* Try to verify that the destination is big enough for the shortest
 | ||||
|      string.  First try to determine the size of the destination object | ||||
|  | @ -4290,8 +4296,8 @@ expand_builtin_strncat (tree exp, rtx) | |||
|   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1); | ||||
| 
 | ||||
|   /* Add one for the terminating nul.  */ | ||||
|   tree srclen = (lenrange[0] | ||||
| 		 ? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0], | ||||
|   tree srclen = (maxlen | ||||
| 		 ? fold_build2 (PLUS_EXPR, size_type_node, maxlen, | ||||
| 				size_one_node) | ||||
| 		 : NULL_TREE); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										41
									
								
								gcc/calls.c
								
								
								
								
							
							
						
						
									
										41
									
								
								gcc/calls.c
								
								
								
								
							|  | @ -1569,9 +1569,11 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp) | |||
|   /* The bound argument to a bounded string function like strncpy.  */ | ||||
|   tree bound = NULL_TREE; | ||||
| 
 | ||||
|   /* The range of lengths of a string argument to one of the comparison
 | ||||
|      functions.  If the length is less than the bound it is used instead.  */ | ||||
|   tree lenrng[2] = { NULL_TREE, NULL_TREE }; | ||||
|   /* The longest known or possible string argument to one of the comparison
 | ||||
|      functions.  If the length is less than the bound it is used instead. | ||||
|      Since the length is only used for warning and not for code generation | ||||
|      disable strict mode in the calls to get_range_strlen below.  */ | ||||
|   tree maxlen = NULL_TREE; | ||||
| 
 | ||||
|   /* It's safe to call "bounded" string functions with a non-string
 | ||||
|      argument since the functions provide an explicit bound for this | ||||
|  | @ -1591,11 +1593,15 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp) | |||
| 	   and to adjust the range of the bound of the bounded ones.  */ | ||||
| 	for (unsigned argno = 0; | ||||
| 	     argno < MIN (nargs, 2) | ||||
| 	     && !(lenrng[1] && TREE_CODE (lenrng[1]) == INTEGER_CST); argno++) | ||||
| 	       && !(maxlen && TREE_CODE (maxlen) == INTEGER_CST); argno++) | ||||
| 	  { | ||||
| 	    tree arg = CALL_EXPR_ARG (exp, argno); | ||||
| 	    if (!get_attr_nonstring_decl (arg)) | ||||
| 	      get_range_strlen (arg, lenrng); | ||||
| 	      { | ||||
| 		c_strlen_data lendata = { }; | ||||
| 		get_range_strlen (arg, &lendata, /* eltsize = */ 1); | ||||
| 		maxlen = lendata.maxbound; | ||||
| 	      } | ||||
| 	  } | ||||
|       } | ||||
|       /* Fall through.  */ | ||||
|  | @ -1616,8 +1622,11 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp) | |||
|       { | ||||
| 	tree arg = CALL_EXPR_ARG (exp, 0); | ||||
| 	if (!get_attr_nonstring_decl (arg)) | ||||
| 	  get_range_strlen (arg, lenrng); | ||||
| 
 | ||||
| 	  { | ||||
| 	    c_strlen_data lendata = { }; | ||||
| 	    get_range_strlen (arg, &lendata, /* eltsize = */ 1); | ||||
| 	    maxlen = lendata.maxbound; | ||||
| 	  } | ||||
| 	if (nargs > 1) | ||||
| 	  bound = CALL_EXPR_ARG (exp, 1); | ||||
| 	break; | ||||
|  | @ -1658,28 +1667,28 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp) | |||
| 	} | ||||
|     } | ||||
| 
 | ||||
|   if (lenrng[1] && TREE_CODE (lenrng[1]) == INTEGER_CST) | ||||
|   if (maxlen && !integer_all_onesp (maxlen)) | ||||
|     { | ||||
|       /* Add one for the nul.  */ | ||||
|       lenrng[1] = const_binop (PLUS_EXPR, TREE_TYPE (lenrng[1]), | ||||
| 			       lenrng[1], size_one_node); | ||||
|       maxlen = const_binop (PLUS_EXPR, TREE_TYPE (maxlen), maxlen, | ||||
| 			    size_one_node); | ||||
| 
 | ||||
|       if (!bndrng[0]) | ||||
| 	{ | ||||
| 	  /* Conservatively use the upper bound of the lengths for
 | ||||
| 	     both the lower and the upper bound of the operation.  */ | ||||
| 	  bndrng[0] = lenrng[1]; | ||||
| 	  bndrng[1] = lenrng[1]; | ||||
| 	  bndrng[0] = maxlen; | ||||
| 	  bndrng[1] = maxlen; | ||||
| 	  bound = void_type_node; | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  /* Replace the bound on the operation with the upper bound
 | ||||
| 	     of the length of the string if the latter is smaller.  */ | ||||
| 	  if (tree_int_cst_lt (lenrng[1], bndrng[0])) | ||||
| 	    bndrng[0] = lenrng[1]; | ||||
| 	  else if (tree_int_cst_lt (lenrng[1], bndrng[1])) | ||||
| 	    bndrng[1] = lenrng[1]; | ||||
| 	  if (tree_int_cst_lt (maxlen, bndrng[0])) | ||||
| 	    bndrng[0] = maxlen; | ||||
| 	  else if (tree_int_cst_lt (maxlen, bndrng[1])) | ||||
| 	    bndrng[1] = maxlen; | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1502,11 +1502,14 @@ get_range_strlen_tree (tree arg, bitmap *visited, | |||
|   return true; | ||||
| } | ||||
| 
 | ||||
| /* Obtain the minimum and maximum string length or minimum and maximum
 | ||||
|    value of ARG in LENGTH[0] and LENGTH[1], respectively. | ||||
|    If ARG is an SSA name variable, follow its use-def chains.  When | ||||
|    TYPE == 0, if LENGTH[1] is not equal to the length we determine or | ||||
|    if we are unable to determine the length or value, return false. | ||||
| /* For an ARG referencing one or more strings, try to obtain the range
 | ||||
|    of their lengths, or the size of the largest array ARG referes to if | ||||
|    the range of lengths cannot be determined, and store all in *PDATA. | ||||
|    For an integer ARG (when RKIND == SRK_INT_VALUE), try to determine | ||||
|    the maximum constant value. | ||||
|    If ARG is an SSA_NAME, follow its use-def chains.  When RKIND == | ||||
|    SRK_STRLEN, then if PDATA->MAXLEN is not equal to the determined | ||||
|    length or if we are unable to determine the length, return false. | ||||
|    VISITED is a bitmap of visited variables. | ||||
|    RKIND determines the kind of value or range to obtain (see | ||||
|    strlen_range_kind). | ||||
|  | @ -1516,8 +1519,7 @@ get_range_strlen_tree (tree arg, bitmap *visited, | |||
|    Return true if *PDATA was successfully populated and false otherwise.  */ | ||||
| 
 | ||||
| static bool | ||||
| get_range_strlen (tree arg, bitmap *visited, | ||||
| 		  strlen_range_kind rkind, | ||||
| get_range_strlen (tree arg, bitmap *visited, strlen_range_kind rkind, | ||||
| 		  c_strlen_data *pdata, bool *flexp, unsigned eltsize) | ||||
| { | ||||
| 
 | ||||
|  | @ -1612,6 +1614,7 @@ get_range_strlen (tree arg, bitmap *visited, | |||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Determine the minimum and maximum value or string length that ARG
 | ||||
|    refers to and store each in the first two elements of MINMAXLEN. | ||||
|    For expressions that point to strings of unknown lengths that are | ||||
|  | @ -1638,47 +1641,39 @@ get_range_strlen (tree arg, bitmap *visited, | |||
|    4 for wide characer strings.  ELTSIZE is by default 1.  */ | ||||
| 
 | ||||
| bool | ||||
| get_range_strlen (tree arg, tree minmaxlen[2], unsigned eltsize, | ||||
| 		  bool strict, tree *nonstr /* = NULL */) | ||||
| get_range_strlen (tree arg, c_strlen_data *pdata, unsigned eltsize, bool strict) | ||||
| { | ||||
|   bitmap visited = NULL; | ||||
| 
 | ||||
|   minmaxlen[0] = NULL_TREE; | ||||
|   minmaxlen[1] = NULL_TREE; | ||||
| 
 | ||||
|   tree nonstrbuf; | ||||
|   if (!nonstr) | ||||
|     nonstr = &nonstrbuf; | ||||
|   *nonstr = NULL_TREE; | ||||
| 
 | ||||
|   bool flexarray = false; | ||||
|   c_strlen_data lendata = { }; | ||||
|   if (!get_range_strlen (arg, &visited, | ||||
| 			 strict ? SRK_LENRANGE : SRK_LENRANGE_2, | ||||
| 			 &lendata, &flexarray, eltsize)) | ||||
|   if (!get_range_strlen (arg, &visited, strict ? SRK_LENRANGE : SRK_LENRANGE_2, pdata, &flexarray, eltsize)) | ||||
|     { | ||||
|       minmaxlen[0] = NULL_TREE; | ||||
|       minmaxlen[1] = NULL_TREE; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       minmaxlen[0] = lendata.minlen; | ||||
|       minmaxlen[1] = lendata.maxlen; | ||||
|       /* On failure extend the length range to an impossible maximum
 | ||||
| 	 (a valid MAXLEN must be less than PTRDIFF_MAX - 1).  Other | ||||
| 	 members can stay unchanged regardless.  */ | ||||
|       pdata->minlen = ssize_int (0); | ||||
|       pdata->maxlen = build_all_ones_cst (size_type_node); | ||||
|     } | ||||
|   else if (!pdata->minlen) | ||||
|     pdata->minlen = ssize_int (0); | ||||
| 
 | ||||
|   /* Unless its null, leave the more conservative MAXBOUND unchanged.  */ | ||||
|   if (!pdata->maxbound) | ||||
|     pdata->maxbound = pdata->maxlen; | ||||
| 
 | ||||
|   *nonstr = lendata.decl; | ||||
|   if (visited) | ||||
|     BITMAP_FREE (visited); | ||||
| 
 | ||||
|   return flexarray; | ||||
| } | ||||
| 
 | ||||
| /* Return the maximum string length for ARG, counting by TYPE
 | ||||
|    (1, 2 or 4 for normal or wide chars).  NONSTR indicates | ||||
|    if the caller is prepared to handle unterminated strings. | ||||
| /* Return the maximum value for ARG given RKIND (see strlen_range_kind).
 | ||||
|    For ARG of pointer types, NONSTR indicates if the caller is prepared | ||||
|    to handle unterminated strings.   For integer ARG and when RKIND == | ||||
|    SRK_INT_VALUE, NONSTR must be null. | ||||
| 
 | ||||
|    If an unterminated string is discovered and our caller handles | ||||
|    unterminated strings, then bubble up the offending DECL and | ||||
|    If an unterminated array is discovered and our caller handles | ||||
|    unterminated arrays, then bubble up the offending DECL and | ||||
|    return the maximum size.  Otherwise return NULL.  */ | ||||
| 
 | ||||
| static tree | ||||
|  | @ -1692,10 +1687,15 @@ get_maxval_strlen (tree arg, strlen_range_kind rkind, tree *nonstr = NULL) | |||
| 
 | ||||
|   bitmap visited = NULL; | ||||
| 
 | ||||
|   bool dummy; | ||||
|   /* Reset DATA.MAXLEN if the call fails or when DATA.MAXLEN
 | ||||
|      is unbounded.  */ | ||||
|   c_strlen_data lendata = { }; | ||||
|   bool dummy; | ||||
|   if (!get_range_strlen (arg, &visited, rkind, &lendata, &dummy, 1)) | ||||
|     lendata.maxlen = NULL_TREE; | ||||
|   else if (lendata.maxlen && integer_all_onesp (lendata.maxlen)) | ||||
|     lendata.maxlen = NULL_TREE; | ||||
| 
 | ||||
|   if (visited) | ||||
|     BITMAP_FREE (visited); | ||||
| 
 | ||||
|  | @ -3668,21 +3668,19 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi) | |||
|   wide_int minlen; | ||||
|   wide_int maxlen; | ||||
| 
 | ||||
|   /* Set to non-null if ARG refers to an unterminated array.  */ | ||||
|   tree nonstr; | ||||
|   tree lenrange[2]; | ||||
|   if (!get_range_strlen (arg, lenrange, 1, true, &nonstr) | ||||
|       && !nonstr | ||||
|       && lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST | ||||
|       && lenrange[1] && TREE_CODE (lenrange[1]) == INTEGER_CST) | ||||
|   c_strlen_data lendata = { }; | ||||
|   if (!get_range_strlen (arg, &lendata, /* eltsize = */ 1) | ||||
|       && !lendata.decl | ||||
|       && lendata.minlen && TREE_CODE (lendata.minlen) == INTEGER_CST | ||||
|       && lendata.maxlen && TREE_CODE (lendata.maxlen) == INTEGER_CST) | ||||
|     { | ||||
|       /* The range of lengths refers to either a single constant
 | ||||
| 	 string or to the longest and shortest constant string | ||||
| 	 referenced by the argument of the strlen() call, or to | ||||
| 	 the strings that can possibly be stored in the arrays | ||||
| 	 the argument refers to.  */ | ||||
|       minlen = wi::to_wide (lenrange[0]); | ||||
|       maxlen = wi::to_wide (lenrange[1]); | ||||
|       minlen = wi::to_wide (lendata.minlen); | ||||
|       maxlen = wi::to_wide (lendata.maxlen); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|  | @ -3694,9 +3692,12 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi) | |||
| 
 | ||||
|   if (minlen == maxlen) | ||||
|     { | ||||
|       lenrange[0] = force_gimple_operand_gsi (gsi, lenrange[0], true, NULL, | ||||
| 					      true, GSI_SAME_STMT); | ||||
|       replace_call_with_value (gsi, lenrange[0]); | ||||
|       /* Fold the strlen call to a constant.  */ | ||||
|       tree type = TREE_TYPE (lendata.minlen); | ||||
|       tree len = force_gimple_operand_gsi (gsi, | ||||
| 					   wide_int_to_tree (type, minlen), | ||||
| 					   true, NULL, true, GSI_SAME_STMT); | ||||
|       replace_call_with_value (gsi, len); | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,8 +25,8 @@ along with GCC; see the file COPYING3.  If not see | |||
| extern tree create_tmp_reg_or_ssa_name (tree, gimple *stmt = NULL); | ||||
| extern tree canonicalize_constructor_val (tree, tree); | ||||
| extern tree get_symbol_constant_value (tree); | ||||
| extern bool get_range_strlen (tree, tree[2], unsigned = 1, | ||||
| 			      bool = false, tree * = NULL); | ||||
| struct c_strlen_data; | ||||
| extern bool get_range_strlen (tree, c_strlen_data *, unsigned eltsize, bool = false); | ||||
| extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree); | ||||
| extern bool fold_stmt (gimple_stmt_iterator *); | ||||
| extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree)); | ||||
|  |  | |||
|  | @ -2003,90 +2003,73 @@ get_string_length (tree str, unsigned eltsize) | |||
|   if (!str) | ||||
|     return fmtresult (); | ||||
| 
 | ||||
|   c_strlen_data data = { }; | ||||
|   tree slen = c_strlen (str, 1, &data, eltsize); | ||||
|   if (slen && TREE_CODE (slen) == INTEGER_CST) | ||||
|     { | ||||
|       /* The string is properly terminated and
 | ||||
| 	 we know its length.  */ | ||||
|       fmtresult res (tree_to_shwi (slen)); | ||||
|       res.nonstr = NULL_TREE; | ||||
|       return res; | ||||
|     } | ||||
|   else if (!slen | ||||
| 	   && data.decl | ||||
| 	   && data.minlen | ||||
| 	   && TREE_CODE (data.minlen) == INTEGER_CST) | ||||
|     { | ||||
|       /* STR was not properly NUL terminated, but we have
 | ||||
| 	 length information about the unterminated string.  */ | ||||
|       fmtresult res (tree_to_shwi (data.minlen)); | ||||
|       res.nonstr = data.decl; | ||||
|       return res; | ||||
|     } | ||||
| 
 | ||||
|   /* Determine the length of the shortest and longest string referenced
 | ||||
|      by STR.  Strings of unknown lengths are bounded by the sizes of | ||||
|      arrays that subexpressions of STR may refer to.  Pointers that | ||||
|      aren't known to point any such arrays result in LENRANGE[1] set | ||||
|      to SIZE_MAX.  NONSTR is set to the declaration of the constant | ||||
|      array that is known not to be nul-terminated.  */ | ||||
|   tree lenrange[2]; | ||||
|   tree nonstr; | ||||
|   bool flexarray = get_range_strlen (str, lenrange, eltsize, false, &nonstr); | ||||
|      aren't known to point any such arrays result in LENDATA.MAXLEN | ||||
|      set to SIZE_MAX.  */ | ||||
|   c_strlen_data lendata = { }; | ||||
|   bool flexarray = get_range_strlen (str, &lendata, eltsize); | ||||
| 
 | ||||
|   if (lenrange [0] || lenrange [1]) | ||||
|   /* Return the default result when nothing is known about the string. */ | ||||
|   if (integer_all_onesp (lendata.maxbound) | ||||
|       && integer_all_onesp (lendata.maxlen)) | ||||
|     return fmtresult (); | ||||
| 
 | ||||
|   HOST_WIDE_INT min | ||||
|     = (tree_fits_uhwi_p (lendata.minlen) | ||||
|        ? tree_to_uhwi (lendata.minlen) | ||||
|        : 0); | ||||
| 
 | ||||
|   HOST_WIDE_INT max | ||||
|     = (tree_fits_uhwi_p (lendata.maxbound) | ||||
|        ? tree_to_uhwi (lendata.maxbound) | ||||
|        : HOST_WIDE_INT_M1U); | ||||
| 
 | ||||
|   const bool unbounded = flexarray || integer_all_onesp (lendata.maxlen); | ||||
| 
 | ||||
|   /* Set the max/likely counters to unbounded when a minimum is known
 | ||||
|      but the maximum length isn't bounded.  This implies that STR is | ||||
|      a conditional expression involving a string of known length and | ||||
|      and an expression of unknown/unbounded length.  */ | ||||
|   if (min | ||||
|       && (unsigned HOST_WIDE_INT)min < HOST_WIDE_INT_M1U | ||||
|       && unbounded) | ||||
|     max = HOST_WIDE_INT_M1U; | ||||
| 
 | ||||
|   /* get_range_strlen() returns the target value of SIZE_MAX for
 | ||||
|      strings of unknown length.  Bump it up to HOST_WIDE_INT_M1U | ||||
|      which may be bigger.  */ | ||||
|   if ((unsigned HOST_WIDE_INT)min == target_size_max ()) | ||||
|     min = HOST_WIDE_INT_M1U; | ||||
|   if ((unsigned HOST_WIDE_INT)max == target_size_max ()) | ||||
|     max = HOST_WIDE_INT_M1U; | ||||
| 
 | ||||
|   fmtresult res (min, max); | ||||
|   res.nonstr = lendata.decl; | ||||
| 
 | ||||
|   /* Set RES.KNOWNRANGE to true if and only if all strings referenced
 | ||||
|      by STR are known to be bounded (though not necessarily by their | ||||
|      actual length but perhaps by their maximum possible length).  */ | ||||
|   if (res.range.max < target_int_max ()) | ||||
|     { | ||||
|       HOST_WIDE_INT min | ||||
| 	= (tree_fits_uhwi_p (lenrange[0]) | ||||
| 	   ? tree_to_uhwi (lenrange[0]) | ||||
| 	   : 0); | ||||
| 
 | ||||
|       HOST_WIDE_INT max | ||||
| 	= (tree_fits_uhwi_p (lenrange[1]) | ||||
| 	   ? tree_to_uhwi (lenrange[1]) | ||||
| 	   : HOST_WIDE_INT_M1U); | ||||
| 
 | ||||
|       /* get_range_strlen() returns the target value of SIZE_MAX for
 | ||||
| 	 strings of unknown length.  Bump it up to HOST_WIDE_INT_M1U | ||||
| 	 which may be bigger.  */ | ||||
|       if ((unsigned HOST_WIDE_INT)min == target_size_max ()) | ||||
| 	min = HOST_WIDE_INT_M1U; | ||||
|       if ((unsigned HOST_WIDE_INT)max == target_size_max ()) | ||||
| 	max = HOST_WIDE_INT_M1U; | ||||
| 
 | ||||
|       fmtresult res (min, max); | ||||
|       res.nonstr = nonstr; | ||||
| 
 | ||||
|       /* Set RES.KNOWNRANGE to true if and only if all strings referenced
 | ||||
| 	 by STR are known to be bounded (though not necessarily by their | ||||
| 	 actual length but perhaps by their maximum possible length).  */ | ||||
|       if (res.range.max < target_int_max ()) | ||||
| 	{ | ||||
| 	  res.knownrange = true; | ||||
| 	  /* When the the length of the longest string is known and not
 | ||||
| 	     excessive use it as the likely length of the string(s).  */ | ||||
| 	  res.range.likely = res.range.max; | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  /* When the upper bound is unknown (it can be zero or excessive)
 | ||||
| 	     set the likely length to the greater of 1 and the length of | ||||
| 	     the shortest string and reset the lower bound to zero.  */ | ||||
| 	  res.range.likely = res.range.min ? res.range.min : warn_level > 1; | ||||
| 	  res.range.min = 0; | ||||
| 	} | ||||
| 
 | ||||
|       /* If the range of string length has been estimated from the size
 | ||||
| 	 of an array at the end of a struct assume that it's longer than | ||||
| 	 the array bound says it is in case it's used as a poor man's | ||||
| 	 flexible array member, such as in struct S { char a[4]; };  */ | ||||
|       res.range.unlikely = flexarray ? HOST_WIDE_INT_MAX : res.range.max; | ||||
| 
 | ||||
|       return res; | ||||
|       res.knownrange = true; | ||||
|       /* When the the length of the longest string is known and not
 | ||||
| 	 excessive use it as the likely length of the string(s).  */ | ||||
|       res.range.likely = res.range.max; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       /* When the upper bound is unknown (it can be zero or excessive)
 | ||||
| 	 set the likely length to the greater of 1 and the length of | ||||
| 	 the shortest string and reset the lower bound to zero.  */ | ||||
|       res.range.likely = res.range.min ? res.range.min : warn_level > 1; | ||||
|       res.range.min = 0; | ||||
|     } | ||||
| 
 | ||||
|   return fmtresult (); | ||||
|   res.range.unlikely = unbounded ? HOST_WIDE_INT_MAX : res.range.max; | ||||
| 
 | ||||
|   return res; | ||||
| } | ||||
| 
 | ||||
| /* Return the minimum and maximum number of characters formatted
 | ||||
|  | @ -2326,6 +2309,8 @@ format_string (const directive &dir, tree arg, vr_values *) | |||
| 	  if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max) | ||||
| 	    res.range.max = dir.prec[1]; | ||||
| 	  res.range.likely = dir.prec[1] ? warn_level > 1 : 0; | ||||
| 	  if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.unlikely) | ||||
| 	    res.range.unlikely = dir.prec[1]; | ||||
| 	} | ||||
|       else if (slen.range.min >= target_int_max ()) | ||||
| 	{ | ||||
|  | @ -2335,6 +2320,7 @@ format_string (const directive &dir, tree arg, vr_values *) | |||
| 	     empty, while at level 1 they are assumed to be one byte | ||||
| 	     long.  */ | ||||
| 	  res.range.likely = warn_level > 1; | ||||
| 	  res.range.unlikely = HOST_WIDE_INT_MAX; | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
|  | @ -2344,8 +2330,6 @@ format_string (const directive &dir, tree arg, vr_values *) | |||
| 	  if (res.range.likely >= target_int_max ()) | ||||
| 	    res.range.likely = warn_level > 1; | ||||
| 	} | ||||
| 
 | ||||
|       res.range.unlikely = res.range.max; | ||||
|     } | ||||
| 
 | ||||
|   /* If the argument isn't a nul-terminated string and the number
 | ||||
|  |  | |||
|  | @ -1,3 +1,12 @@ | |||
| 2019-01-01  Martin Sebor  <msebor@redhat.com> | ||||
|             Jeff Law  <law@redhat.com> | ||||
| 
 | ||||
| 	* gcc.dg/strlenopt-40.c: Disable a couple tests. | ||||
| 	* gcc.dg/strlenopt-48.c: Twiddle test slightly. | ||||
| 	* gcc.dg/strlenopt-59.c: New test. | ||||
| 	* gcc.dg/tree-ssa/builtin-snprintf-5.c: New test. | ||||
| 	* g++.dg/init/strlen.C: New test. | ||||
| 
 | ||||
| 2019-01-01  Thomas Koenig  <tkoenig@gcc.gnu.org> | ||||
| 
 | ||||
| 	PR fortran/82743 | ||||
|  |  | |||
|  | @ -0,0 +1,43 @@ | |||
| // Test to verify that the strlen() optimization doesn't make assumptions
 | ||||
| // about the static type of the object pointed to by its argument.  See
 | ||||
| // the following thread for background:
 | ||||
| //   https://gcc.gnu.org/ml/gcc-patches/2018-08/msg00260.html
 | ||||
| 
 | ||||
| // { dg-do run }
 | ||||
| // { dg-options "-O2 -Wall -fdump-tree-optimized" }
 | ||||
| 
 | ||||
| typedef __SIZE_TYPE__ size_t; | ||||
| 
 | ||||
| void *operator new[] (size_t, void *p) { return p; } | ||||
| 
 | ||||
| struct S { int x; char a[1]; char b[64]; }; | ||||
| 
 | ||||
| __attribute__ ((noipa)) void | ||||
| init (char *s) | ||||
| { | ||||
|   *s++ = '1'; | ||||
|   *s++ = '\0'; | ||||
| } | ||||
| 
 | ||||
| __attribute__ ((noipa)) void | ||||
| test_dynamic_type (S *p) | ||||
| { | ||||
|   // The placement new call below isn't strictly valid because it
 | ||||
|   // creates an object that is larger than the space of the p->a
 | ||||
|   // subobject in which it is created.  However, the corresponding
 | ||||
|   // GIMPLE considers it valid and there's apparently no way to
 | ||||
|   // distinguish invalid cases from ones like it that might be valid.
 | ||||
|   // If/when GIMPLE changes to make this possible this test can be
 | ||||
|   // removed.
 | ||||
|   char *q = new (p->a) char [16]; | ||||
| 
 | ||||
|   init (q); | ||||
| 
 | ||||
|   if (0 == __builtin_strlen (q)) | ||||
|     __builtin_abort(); | ||||
| } | ||||
| 
 | ||||
| int main () | ||||
| { | ||||
|   test_dynamic_type (new S); | ||||
| } | ||||
|  | @ -124,8 +124,8 @@ void elim_global_arrays (int i) | |||
|   ELIM_TRUE (strlen (a7) < sizeof a7); | ||||
| 
 | ||||
|   ELIM_TRUE (strlen (ax) != DIFF_MAX); | ||||
|   ELIM_TRUE (strlen (ax) != DIFF_MAX - 1); | ||||
|   ELIM_TRUE (strlen (ax) < DIFF_MAX - 1); | ||||
|   /* ELIM_TRUE (strlen (ax) != DIFF_MAX - 1); */ | ||||
|   /* ELIM_TRUE (strlen (ax) < DIFF_MAX - 1); */ | ||||
| } | ||||
| 
 | ||||
| void elim_pointer_to_arrays (void) | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ void f (void) | |||
| { | ||||
|   extern char a[2][1]; | ||||
|   int n = strlen (a[1]); | ||||
|   if (n) | ||||
|   if (n >= sizeof a) | ||||
|     abort(); | ||||
| } | ||||
| 
 | ||||
|  | @ -19,7 +19,7 @@ void g (void) | |||
| { | ||||
|   extern char b[3][2][1]; | ||||
|   int n = strlen (b[2][1]); | ||||
|   if (n) | ||||
|   if (n >= sizeof b) | ||||
|     abort(); | ||||
| } | ||||
| 
 | ||||
|  | @ -27,7 +27,7 @@ void h (void) | |||
| { | ||||
|   extern char c[4][3][2][1]; | ||||
|   int n = strlen (c[3][2][1]); | ||||
|   if (n) | ||||
|   if (n >= sizeof c) | ||||
|     abort(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,73 @@ | |||
| /* Verify that strlen() calls with constant conditional expressions are
 | ||||
|    eliminated as expected. | ||||
| 
 | ||||
|    { dg-do compile } | ||||
|    { dg-options "-O1 -fdump-tree-optimized" }  */ | ||||
| 
 | ||||
| extern void abort (void); | ||||
| extern __SIZE_TYPE__ strlen (const char*); | ||||
| 
 | ||||
| 
 | ||||
| #define CAT(x, y) x ## y | ||||
| #define CONCAT(x, y) CAT (x, y) | ||||
| #define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__) | ||||
| 
 | ||||
| #define FAIL(name) do {				\ | ||||
|     extern void FAILNAME (name) (void);		\ | ||||
|     FAILNAME (name)();				\ | ||||
|   } while (0) | ||||
| 
 | ||||
| /* Macros to emit a call to funcation named
 | ||||
|      call_failed_to_be_eliminated_on_line_NNN() | ||||
|    for each call that's expected to be eliminated.  The dg-final | ||||
|    scan-tree-dump-time directive at the bottom of the test verifies | ||||
|    that no such call appears in output.  */ | ||||
| #define ELIM(expr) \ | ||||
|   if ((expr)) FAIL (test_not_eliminated); else (void)0 | ||||
| 
 | ||||
| extern char a3[3]; | ||||
| extern char a7[7]; | ||||
| 
 | ||||
| struct MemArrays { char a[7], b[9]; }; | ||||
| 
 | ||||
| struct MemArrays ma; | ||||
| 
 | ||||
| void test_elim_condexpr (int i) | ||||
| { | ||||
|   ELIM (6 < strlen (i ? "" : "123456")); | ||||
|   ELIM (6 < strlen (i ? "123456" : "")); | ||||
| 
 | ||||
|   ELIM (4 < strlen (i < 1 ? "a" : i == 1 ? "ab" : "abc")); | ||||
| 
 | ||||
|   ELIM (3 < strlen (i ? "" : a3)); | ||||
|   ELIM (3 < strlen (i ? a3 : "1")); | ||||
| 
 | ||||
|   ELIM (6 < strlen (i ? "12" : a7)); | ||||
|   ELIM (6 < strlen (i ? a7 : "123")); | ||||
| 
 | ||||
|   ELIM (6 < strlen (i ? "1234" : a7)); | ||||
|   ELIM (7 < strlen (i ? a7 : "1234567")); | ||||
| 
 | ||||
|   ELIM (3 < strlen (i < 1 ? "a" : i == 1 ? "ab" : a3)); | ||||
|   ELIM (3 < strlen (i < 1 ? "a" : i == 1 ? a3 : "abc")); | ||||
|   ELIM (3 < strlen (i < 1 ? a3 : i == 1 ? "a" : "abc")); | ||||
| 
 | ||||
|   ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? "ab" : a7)); | ||||
|   ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? a7 : "abc")); | ||||
|   ELIM (6 < strlen (i < 1 ? a7 : i == 1 ? "a" : "abc")); | ||||
| 
 | ||||
|   ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? a7 : a3)); | ||||
|   ELIM (6 < strlen (i < 1 ? a7 : i == 1 ? "a" : a3)); | ||||
| 
 | ||||
|   { | ||||
|     enum { maxlen = sizeof ma - 1 }; | ||||
|     ELIM (maxlen < strlen (ma.a)); | ||||
|   } | ||||
| 
 | ||||
|   { | ||||
|     enum { maxlen = sizeof ma - __builtin_offsetof (struct MemArrays, b) - 1 }; | ||||
|     ELIM (maxlen < strlen (ma.b)); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /* { dg-final { scan-tree-dump-times "test_not_eliminated_" 0 "optimized" } } */ | ||||
|  | @ -0,0 +1,51 @@ | |||
| /*
 | ||||
|   { dg-do compile } | ||||
|   { dg-options "-O2 -Wall -fdump-tree-optimized" } */ | ||||
| 
 | ||||
| typedef __SIZE_TYPE__ size_t; | ||||
| 
 | ||||
| extern void abort (void); | ||||
| extern int snprintf (char*, size_t, const char*, ...); | ||||
| 
 | ||||
| const char s0[] = ""; | ||||
| const char s1[] = "a"; | ||||
| const char s2[] = "ab"; | ||||
| 
 | ||||
| extern char ax[]; | ||||
| extern const char* const ptr; | ||||
| 
 | ||||
| #define CAT(x, y)      x ## y | ||||
| #define CONCAT(x, y)   CAT (x, y) | ||||
| #define TEST           CONCAT (test_on_line_, __LINE__) | ||||
| 
 | ||||
| #define KEEP(expr) do {				\ | ||||
|     if ((expr))	{				\ | ||||
|       extern void TEST (void);			\ | ||||
|       TEST ();					\ | ||||
|     }						\ | ||||
|   } while (0) | ||||
| 
 | ||||
| 
 | ||||
| void test_literal (int i) | ||||
| { | ||||
|   KEEP (0 < snprintf (0, 0, "%s", i ? "" : ax)); | ||||
|   KEEP (1 < snprintf (0, 0, "%s", i ? ax : "1")); | ||||
|   KEEP (2 < snprintf (0, 0, "%s", i ? "12" : ptr)); | ||||
| 
 | ||||
|   KEEP (1 > snprintf (0, 0, "%s", i ? "" : ax)); | ||||
|   KEEP (1 > snprintf (0, 0, "%s", i ? ax : "1")); | ||||
|   KEEP (2 > snprintf (0, 0, "%s", i ? "12" : ptr)); | ||||
| } | ||||
| 
 | ||||
| void test_cststr (int i) | ||||
| { | ||||
|   KEEP (0 < snprintf (0, 0, "%s", i ? s0 : ax)); | ||||
|   KEEP (1 < snprintf (0, 0, "%s", i ? ax : s1)); | ||||
|   KEEP (2 < snprintf (0, 0, "%s", i ? s2 : ptr)); | ||||
| 
 | ||||
|   KEEP (1 > snprintf (0, 0, "%s", i ? s0 : ax)); | ||||
|   KEEP (1 > snprintf (0, 0, "%s", i ? ax : s1)); | ||||
|   KEEP (2 > snprintf (0, 0, "%s", i ? s2 : ptr)); | ||||
| } | ||||
| 
 | ||||
| /* { dg-final { scan-tree-dump-times "test_on_line_" 12 "optimized" } } */ | ||||
|  | @ -1989,15 +1989,18 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt) | |||
|     lenrange[0] = lenrange[1] = wi::shwi (~sidx, prec); | ||||
|   else | ||||
|     { | ||||
|       tree range[2]; | ||||
|       get_range_strlen (src, range); | ||||
|       if (range[0] != NULL_TREE | ||||
| 	  && TREE_CODE (range[0]) == INTEGER_CST | ||||
| 	  && range[1] != NULL_TREE | ||||
| 	  && TREE_CODE (range[1]) == INTEGER_CST) | ||||
|       c_strlen_data lendata = { }; | ||||
|       get_range_strlen (src, &lendata, /* eltsize = */1); | ||||
|       if (TREE_CODE (lendata.minlen) == INTEGER_CST | ||||
| 	  && TREE_CODE (lendata.maxbound) == INTEGER_CST) | ||||
| 	{ | ||||
| 	  lenrange[0] = wi::to_wide (range[0], prec); | ||||
| 	  lenrange[1] = wi::to_wide (range[1], prec); | ||||
| 	  /* When LENDATA.MAXLEN is unknown, reset LENDATA.MINLEN
 | ||||
| 	     which stores the length of the shortest known string.  */ | ||||
| 	  if (integer_all_onesp (lendata.maxlen)) | ||||
| 	    lenrange[0] = wi::shwi (0, prec); | ||||
| 	  else | ||||
| 	    lenrange[0] = wi::to_wide (lendata.minlen, prec); | ||||
| 	  lenrange[1] = wi::to_wide (lendata.maxbound, prec); | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Martin Sebor
						Martin Sebor