The attached patch enhances the get_size_range() function to
extract more accurate ranges out of MIN_EXPR and MAX_EXPR nodes
with SSA_NAME operand(s).  This helps -Wstringop-overflow avoid
false positives on some targets in cases like some of those
reported in bug 85623 - strncmp() warns about attribute nonstring
incorrectly in -Wstringop-overflow

When first trying to expand calls to __builtin_strncmp, most back
ends succeed even when the expansion results in a call to the library
function.  The powerpc64 back-end, however, fails and and allows
the generic expansion to take place.  There is a subtle difference
between the two cases that shows up when examining the bound of
the strncmp call expression (the third argument): in the original
call expression (in expand_builtin_strncmp) the bound is or can be
an SSA_NAME.  But when the early expansion fails,
expand_builtin_strncmp transforms the original call expression into
a new one, substituting MIN_EXPR (bound, CST) for the bound where
CST is the constant result of strlen (STR), with STR being either
the first or second strncmp argument.  When the bound is an SSA_NAME
the subsequent attempt to determine its range from the MIN_EXPR fails.

Martin
PR testsuite/85888 - New test case c-c++-common/attr-nonstring-6.c from r260541 fails with excess errors

gcc/ChangeLog:

	PR testsuite/85888
	* calls.c (get_size_range): Handle MIN_EXPR and MAX_EXPR.

diff --git a/gcc/calls.c b/gcc/calls.c
index 35bcff7..4998780 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1295,7 +1295,7 @@ alloc_max_size (void)
 
 /* Return true when EXP's range can be determined and set RANGE[] to it
    after adjusting it if necessary to make EXP a represents a valid size
-   of object, or a valid size argument to an allocation function declared
+   of an object, or a valid size argument to an allocation function declared
    with attribute alloc_size (whose argument may be signed), or to a string
    manipulation function like memset.  When ALLOW_ZERO is true, allow
    returning a range of [0, 0] for a size in an anti-range [1, N] where
@@ -1317,12 +1317,53 @@ get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
   bool integral = INTEGRAL_TYPE_P (exptype);
 
   wide_int min, max;
-  enum value_range_type range_type;
+  enum value_range_type range_type = VR_VARYING;
 
-  if (TREE_CODE (exp) == SSA_NAME && integral)
-    range_type = get_range_info (exp, &min, &max);
-  else
-    range_type = VR_VARYING;
+  if (integral)
+    {
+      bool min_expr = TREE_CODE (exp) == MIN_EXPR;
+      if (min_expr || TREE_CODE (exp) == MAX_EXPR)
+	{
+	  /* EXP may be an SSA_NAME wrapped in a MIN_/MAX_EXPR.  Try
+	     to extract the range from the SSA_NAME and, if successful,
+	     set the final range to the intersection of the ranges
+	     extracted from the expressions' arguments.  */
+	  tree arg0 = TREE_OPERAND (exp, 0);
+	  tree arg1 = TREE_OPERAND (exp, 1);
+
+	  STRIP_NOPS (arg0);
+	  STRIP_NOPS (arg1);
+
+	  tree rng0[2], rng1[2];
+	  if (get_size_range (arg0, rng0, allow_zero)
+	      && get_size_range (arg1, rng1, allow_zero))
+	    {
+	      if (min_expr)
+		{
+		  range[0]
+		    = tree_int_cst_lt (rng0[0], rng1[0]) ? rng0[0] : rng1[0];
+		  range[1]
+		    = tree_int_cst_lt (rng0[1], rng1[1]) ? rng0[1] : rng1[1];
+		}
+	      else
+		{
+		  range[0]
+		    = tree_int_cst_lt (rng1[0], rng0[0]) ? rng0[0] : rng1[0];
+		  range[1]
+		    = tree_int_cst_lt (rng1[1], rng0[1]) ? rng0[1] : rng1[1];
+		}
+
+	      inform (UNKNOWN_LOCATION,
+		      "%E (%s_EXPR): [%E, %E] U [%E, %E] ==> [%E, %E]",
+		      exp, TREE_CODE (exp) == MIN_EXPR ? "MIN" : "MAX",
+		      rng0[0], rng0[1], rng1[0], rng1[1], range[0], range[1]);
+	      return true;
+	    }
+	}
+
+      if (TREE_CODE (exp) == SSA_NAME)
+	range_type = get_range_info (exp, &min, &max);
+    }
 
   if (range_type == VR_VARYING)
     {

Reply via email to