Move const char * -> int/fp folds to fold-const-call.c
authorRichard Sandiford <richard.sandiford@arm.com>
Sat, 7 Nov 2015 10:10:44 +0000 (10:10 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Sat, 7 Nov 2015 10:10:44 +0000 (10:10 +0000)
This patch moves folds that deal with constant string arguments and
return a constant integer or floating-point value.  For example, it
handles strcmp ("foo", "bar") but not strstr ("foobar", "bar"),
which wouldn't currently be accepted by the gimple folders.

The builtins.c folding for strlen (via c_strlen) is a bit more general
than what the fold-const-call.c code does (and more general than we need
for the gimple folders).  I've therefore left it as-is, even though it
partially duplicates the new code.

Tested on x86_64-linux-gnu, aarch64-linux-gnu and arm-linux-gnueabi.

gcc/
* builtins.c (fold_builtin_nan): Delete.
(fold_builtin_memcmp): Remove case where both arguments are constant.
(fold_builtin_strcmp, fold_builtin_strncmp): Likewise.
(fold_builtin_strspn, fold_builtin_strcspn): Likewise.
(fold_builtin_1): Remove BUILT_IN_NAN* handling.
* fold-const-call.c: Include fold-const.h.
(host_size_t_cst_p): New function.
(build_cmp_result, fold_const_builtin_nan): Likewise.
(fold_const_call_1): New function, split out from...
(fold_const_call): ...here (for all three interfaces).  Handle
constant nan, nans, strlen, strcmp, strncmp, strspn and strcspn.

From-SVN: r229922

gcc/ChangeLog
gcc/builtins.c
gcc/fold-const-call.c

index 7b9f63ce6fe9e38adcdc95861288cac767bc5e3e..443066d97c6fbb1070e6ba8547a72c407f595010 100644 (file)
@@ -1,3 +1,17 @@
+2015-11-07  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * builtins.c (fold_builtin_nan): Delete.
+       (fold_builtin_memcmp): Remove case where both arguments are constant.
+       (fold_builtin_strcmp, fold_builtin_strncmp): Likewise.
+       (fold_builtin_strspn, fold_builtin_strcspn): Likewise.
+       (fold_builtin_1): Remove BUILT_IN_NAN* handling.
+       * fold-const-call.c: Include fold-const.h.
+       (host_size_t_cst_p): New function.
+       (build_cmp_result, fold_const_builtin_nan): Likewise.
+       (fold_const_call_1): New function, split out from...
+       (fold_const_call): ...here (for all three interfaces).  Handle
+       constant nan, nans, strlen, strcmp, strncmp, strspn and strcspn.
+
 2015-11-07  Richard Sandiford  <richard.sandiford@arm.com>
 
        * builtins.c (fold_builtin_bitop, fold_builtin_bswap): Delete.
index 3f7fe3b9bbc9da0da29a93d0a7dc4918d577642a..add9fc8484f778b24c4ffcef60cf6fe2f32b893a 100644 (file)
@@ -143,7 +143,6 @@ static tree fold_builtin_constant_p (tree);
 static tree fold_builtin_classify_type (tree);
 static tree fold_builtin_strlen (location_t, tree, tree);
 static tree fold_builtin_inf (location_t, tree, int);
-static tree fold_builtin_nan (tree, tree, int);
 static tree rewrite_call_expr (location_t, tree, int, tree, int, ...);
 static bool validate_arg (const_tree, enum tree_code code);
 static rtx expand_builtin_fabs (tree, rtx, rtx);
@@ -7264,26 +7263,6 @@ fold_builtin_inf (location_t loc, tree type, int warn)
   return build_real (type, real);
 }
 
-/* Fold a call to __builtin_nan or __builtin_nans with argument ARG.  */
-
-static tree
-fold_builtin_nan (tree arg, tree type, int quiet)
-{
-  REAL_VALUE_TYPE real;
-  const char *str;
-
-  if (!validate_arg (arg, POINTER_TYPE))
-    return NULL_TREE;
-  str = c_getstr (arg);
-  if (!str)
-    return NULL_TREE;
-
-  if (!real_nan (&real, str, quiet, TYPE_MODE (type)))
-    return NULL_TREE;
-
-  return build_real (type, real);
-}
-
 /* Fold function call to builtin sincos, sincosf, or sincosl.  Return
    NULL_TREE if no simplification can be made.  */
 
@@ -7378,8 +7357,6 @@ fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
 static tree
 fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
 {
-  const char *p1, *p2;
-
   if (!validate_arg (arg1, POINTER_TYPE)
       || !validate_arg (arg2, POINTER_TYPE)
       || !validate_arg (len, INTEGER_TYPE))
@@ -7394,25 +7371,6 @@ fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
   if (operand_equal_p (arg1, arg2, 0))
     return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
 
-  p1 = c_getstr (arg1);
-  p2 = c_getstr (arg2);
-
-  /* If all arguments are constant, and the value of len is not greater
-     than the lengths of arg1 and arg2, evaluate at compile-time.  */
-  if (tree_fits_uhwi_p (len) && p1 && p2
-      && compare_tree_int (len, strlen (p1) + 1) <= 0
-      && compare_tree_int (len, strlen (p2) + 1) <= 0)
-    {
-      const int r = memcmp (p1, p2, tree_to_uhwi (len));
-
-      if (r > 0)
-       return integer_one_node;
-      else if (r < 0)
-       return integer_minus_one_node;
-      else
-       return integer_zero_node;
-    }
-
   /* If len parameter is one, return an expression corresponding to
      (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
   if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
@@ -7445,8 +7403,6 @@ fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
 static tree
 fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
 {
-  const char *p1, *p2;
-
   if (!validate_arg (arg1, POINTER_TYPE)
       || !validate_arg (arg2, POINTER_TYPE))
     return NULL_TREE;
@@ -7455,21 +7411,8 @@ fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
   if (operand_equal_p (arg1, arg2, 0))
     return integer_zero_node;
 
-  p1 = c_getstr (arg1);
-  p2 = c_getstr (arg2);
-
-  if (p1 && p2)
-    {
-      const int i = strcmp (p1, p2);
-      if (i < 0)
-       return integer_minus_one_node;
-      else if (i > 0)
-       return integer_one_node;
-      else
-       return integer_zero_node;
-    }
-
   /* If the second arg is "", return *(const unsigned char*)arg1.  */
+  const char *p2 = c_getstr (arg2);
   if (p2 && *p2 == '\0')
     {
       tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
@@ -7484,6 +7427,7 @@ fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
     }
 
   /* If the first arg is "", return -*(const unsigned char*)arg2.  */
+  const char *p1 = c_getstr (arg1);
   if (p1 && *p1 == '\0')
     {
       tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
@@ -7508,8 +7452,6 @@ fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
 static tree
 fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
 {
-  const char *p1, *p2;
-
   if (!validate_arg (arg1, POINTER_TYPE)
       || !validate_arg (arg2, POINTER_TYPE)
       || !validate_arg (len, INTEGER_TYPE))
@@ -7524,22 +7466,9 @@ fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
   if (operand_equal_p (arg1, arg2, 0))
     return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
 
-  p1 = c_getstr (arg1);
-  p2 = c_getstr (arg2);
-
-  if (tree_fits_uhwi_p (len) && p1 && p2)
-    {
-      const int i = strncmp (p1, p2, tree_to_uhwi (len));
-      if (i > 0)
-       return integer_one_node;
-      else if (i < 0)
-       return integer_minus_one_node;
-      else
-       return integer_zero_node;
-    }
-
   /* If the second arg is "", and the length is greater than zero,
      return *(const unsigned char*)arg1.  */
+  const char *p2 = c_getstr (arg2);
   if (p2 && *p2 == '\0'
       && TREE_CODE (len) == INTEGER_CST
       && tree_int_cst_sgn (len) == 1)
@@ -7557,6 +7486,7 @@ fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
 
   /* If the first arg is "", and the length is greater than zero,
      return -*(const unsigned char*)arg2.  */
+  const char *p1 = c_getstr (arg1);
   if (p1 && *p1 == '\0'
       && TREE_CODE (len) == INTEGER_CST
       && tree_int_cst_sgn (len) == 1)
@@ -8261,15 +8191,6 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
     CASE_FLT_FN (BUILT_IN_CARG):
       return fold_builtin_carg (loc, arg0, type);
 
-    CASE_FLT_FN (BUILT_IN_NAN):
-    case BUILT_IN_NAND32:
-    case BUILT_IN_NAND64:
-    case BUILT_IN_NAND128:
-      return fold_builtin_nan (arg0, type, true);
-
-    CASE_FLT_FN (BUILT_IN_NANS):
-      return fold_builtin_nan (arg0, type, false);
-
     case BUILT_IN_ISASCII:
       return fold_builtin_isascii (loc, arg0);
 
@@ -9075,13 +8996,6 @@ fold_builtin_strspn (location_t loc, tree s1, tree s2)
     {
       const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
 
-      /* If both arguments are constants, evaluate at compile-time.  */
-      if (p1 && p2)
-       {
-         const size_t r = strspn (p1, p2);
-         return build_int_cst (size_type_node, r);
-       }
-
       /* If either argument is "", return NULL_TREE.  */
       if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
        /* Evaluate and ignore both arguments in case either one has
@@ -9118,16 +9032,8 @@ fold_builtin_strcspn (location_t loc, tree s1, tree s2)
     return NULL_TREE;
   else
     {
-      const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
-
-      /* If both arguments are constants, evaluate at compile-time.  */
-      if (p1 && p2)
-       {
-         const size_t r = strcspn (p1, p2);
-         return build_int_cst (size_type_node, r);
-       }
-
       /* If the first argument is "", return NULL_TREE.  */
+      const char *p1 = c_getstr (s1);
       if (p1 && *p1 == '\0')
        {
          /* Evaluate and ignore argument s2 in case it has
@@ -9137,6 +9043,7 @@ fold_builtin_strcspn (location_t loc, tree s1, tree s2)
        }
 
       /* If the second argument is "", return __builtin_strlen(s1).  */
+      const char *p2 = c_getstr (s2);
       if (p2 && *p2 == '\0')
        {
          tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
index 48e05a967b34af809051b866f12cfce6165f9233..49793a5ab6f3ff1a6a7d91d87d20ecd34cf561e5 100644 (file)
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree.h"
 #include "stor-layout.h"
 #include "options.h"
+#include "fold-const.h"
 #include "fold-const-call.h"
 #include "tm.h" /* For C[LT]Z_DEFINED_AT_ZERO.  */
 
@@ -48,6 +49,31 @@ complex_cst_p (tree t)
   return TREE_CODE (t) == COMPLEX_CST;
 }
 
+/* Return true if ARG is a constant in the range of the host size_t.
+   Store it in *SIZE_OUT if so.  */
+
+static inline bool
+host_size_t_cst_p (tree t, size_t *size_out)
+{
+  if (integer_cst_p (t)
+      && wi::min_precision (t, UNSIGNED) <= sizeof (size_t) * CHAR_BIT)
+    {
+      *size_out = tree_to_uhwi (t);
+      return true;
+    }
+  return false;
+}
+
+/* RES is the result of a comparison in which < 0 means "less", 0 means
+   "equal" and > 0 means "more".  Canonicalize it to -1, 0 or 1 and
+   return it in type TYPE.  */
+
+static inline tree
+build_cmp_result (tree type, int res)
+{
+  return build_int_cst (type, res < 0 ? -1 : res > 0 ? 1 : 0);
+}
+
 /* M is the result of trying to constant-fold an expression (starting
    with clear MPFR flags) and INEXACT says whether the result in M is
    exact or inexact.  Return true if M can be used as a constant-folded
@@ -527,6 +553,20 @@ fold_const_builtin_load_exponent (real_value *result, const real_value *arg0,
   return real_equal (&initial_result, result);
 }
 
+/* Fold a call to __builtin_nan or __builtin_nans with argument ARG and
+   return type TYPE.  QUIET is true if a quiet rather than signalling
+   NaN is required.  */
+
+static tree
+fold_const_builtin_nan (tree type, tree arg, bool quiet)
+{
+  REAL_VALUE_TYPE real;
+  const char *str = c_getstr (arg);
+  if (str && real_nan (&real, str, quiet, TYPE_MODE (type)))
+    return build_real (type, real);
+  return NULL_TREE;
+}
+
 /* Try to evaluate:
 
       *RESULT = FN (*ARG)
@@ -971,11 +1011,11 @@ fold_const_call_cc (real_value *result_real, real_value *result_imag,
     }
 }
 
-/* Try to fold FN (ARG) to a constant.  Return the constant on success,
-   otherwise return null.  TYPE is the type of the return value.  */
+/* Subroutine of fold_const_call, with the same interface.  Handle cases
+   where the arguments and result are numerical.  */
 
-tree
-fold_const_call (built_in_function fn, tree type, tree arg)
+static tree
+fold_const_call_1 (built_in_function fn, tree type, tree arg)
 {
   machine_mode mode = TYPE_MODE (type);
   machine_mode arg_mode = TYPE_MODE (TREE_TYPE (arg));
@@ -1066,6 +1106,33 @@ fold_const_call (built_in_function fn, tree type, tree arg)
   return NULL_TREE;
 }
 
+/* Try to fold FN (ARG) to a constant.  Return the constant on success,
+   otherwise return null.  TYPE is the type of the return value.  */
+
+tree
+fold_const_call (built_in_function fn, tree type, tree arg)
+{
+  switch (fn)
+    {
+    case BUILT_IN_STRLEN:
+      if (const char *str = c_getstr (arg))
+       return build_int_cst (type, strlen (str));
+      return NULL_TREE;
+
+    CASE_FLT_FN (BUILT_IN_NAN):
+    case BUILT_IN_NAND32:
+    case BUILT_IN_NAND64:
+    case BUILT_IN_NAND128:
+      return fold_const_builtin_nan (type, arg, true);
+
+    CASE_FLT_FN (BUILT_IN_NANS):
+      return fold_const_builtin_nan (type, arg, false);
+
+    default:
+      return fold_const_call_1 (fn, type, arg);
+    }
+}
+
 /* Try to evaluate:
 
       *RESULT = FN (*ARG0, *ARG1)
@@ -1194,11 +1261,11 @@ fold_const_call_ccc (real_value *result_real, real_value *result_imag,
     }
 }
 
-/* Try to fold FN (ARG0, ARG1) to a constant.  Return the constant on success,
-   otherwise return null.  TYPE is the type of the return value.  */
+/* Subroutine of fold_const_call, with the same interface.  Handle cases
+   where the arguments and result are numerical.  */
 
-tree
-fold_const_call (built_in_function fn, tree type, tree arg0, tree arg1)
+static tree
+fold_const_call_1 (built_in_function fn, tree type, tree arg0, tree arg1)
 {
   machine_mode mode = TYPE_MODE (type);
   machine_mode arg0_mode = TYPE_MODE (TREE_TYPE (arg0));
@@ -1286,6 +1353,35 @@ fold_const_call (built_in_function fn, tree type, tree arg0, tree arg1)
   return NULL_TREE;
 }
 
+/* Try to fold FN (ARG0, ARG1) to a constant.  Return the constant on success,
+   otherwise return null.  TYPE is the type of the return value.  */
+
+tree
+fold_const_call (built_in_function fn, tree type, tree arg0, tree arg1)
+{
+  const char *p0, *p1;
+  switch (fn)
+    {
+    case BUILT_IN_STRSPN:
+      if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
+       return build_int_cst (type, strspn (p0, p1));
+      return NULL_TREE;
+
+    case BUILT_IN_STRCSPN:
+      if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
+       return build_int_cst (type, strcspn (p0, p1));
+      return NULL_TREE;
+
+    case BUILT_IN_STRCMP:
+      if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
+       return build_cmp_result (type, strcmp (p0, p1));
+      return NULL_TREE;
+
+    default:
+      return fold_const_call_1 (fn, type, arg0, arg1);
+    }
+}
+
 /* Try to evaluate:
 
       *RESULT = FN (*ARG0, *ARG1, *ARG2)
@@ -1307,12 +1403,12 @@ fold_const_call_ssss (real_value *result, built_in_function fn,
     }
 }
 
-/* Try to fold FN (ARG0, ARG1, ARG2) to a constant.  Return the constant on
-   success, otherwise return null.  TYPE is the type of the return value.  */
+/* Subroutine of fold_const_call, with the same interface.  Handle cases
+   where the arguments and result are numerical.  */
 
-tree
-fold_const_call (built_in_function fn, tree type, tree arg0, tree arg1,
-                tree arg2)
+static tree
+fold_const_call_1 (built_in_function fn, tree type, tree arg0, tree arg1,
+                  tree arg2)
 {
   machine_mode mode = TYPE_MODE (type);
   machine_mode arg0_mode = TYPE_MODE (TREE_TYPE (arg0));
@@ -1342,6 +1438,39 @@ fold_const_call (built_in_function fn, tree type, tree arg0, tree arg1,
   return NULL_TREE;
 }
 
+/* Try to fold FN (ARG0, ARG1, ARG2) to a constant.  Return the constant on
+   success, otherwise return null.  TYPE is the type of the return value.  */
+
+tree
+fold_const_call (built_in_function fn, tree type, tree arg0, tree arg1,
+                tree arg2)
+{
+  const char *p0, *p1;
+  size_t s2;
+  switch (fn)
+    {
+    case BUILT_IN_STRNCMP:
+      if ((p0 = c_getstr (arg0))
+         && (p1 = c_getstr (arg1))
+         && host_size_t_cst_p (arg2, &s2))
+       return build_int_cst (type, strncmp (p0, p1, s2));
+      return NULL_TREE;
+
+    case BUILT_IN_BCMP:
+    case BUILT_IN_MEMCMP:
+      if ((p0 = c_getstr (arg0))
+         && (p1 = c_getstr (arg1))
+         && host_size_t_cst_p (arg2, &s2)
+         && s2 <= strlen (p0)
+         && s2 <= strlen (p1))
+       return build_cmp_result (type, memcmp (p0, p1, s2));
+      return NULL_TREE;
+
+    default:
+      return fold_const_call_1 (fn, type, arg0, arg1, arg2);
+    }
+}
+
 /* Fold a fma operation with arguments ARG[012].  */
 
 tree