Remove lower case characters from the range of to_upper and likewise, upper case characters from to_lower.

I looked at also only adding the upper case characters for which there is a lower_case character in the range,  but it seemed of limited use Given some odd usage patterns we emit. .  Instead, I simply took the incoming range, removed the "from" character set, and added the "to" character set.  This'll preserve any odd things that are passed into it while providing the basic functionality.

Easy enough for someone to enhance if they feel so inclined.

Bootstrapped on  x86_64-pc-linux-gnu  with no regressions. Pushed.

commit 1ce0b26e6e1e6c348b1d54f1f462a44df6fe47f5
Author: Andrew MacLeod <amacl...@redhat.com>
Date:   Mon Jul 26 09:40:32 2021 -0400

    Adjust ranges for to_upper and to_lower.
    
    Exclude lower case chars from to_upper and upper case chars from to_lower.
    
            gcc/
            PR tree-optimization/78888
            * gimple-range-fold.cc (fold_using_range::range_of_builtin_call): Add cases
            for CFN_BUILT_IN_TOUPPER and CFN_BUILT_IN_TOLOWER.
    
            gcc/testsuite/
            * gcc.dg/pr78888.c: New.

diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index f95af3d5866..8465b4a82f6 100644
--- a/gcc/gimple-range-fold.cc
+++ b/gcc/gimple-range-fold.cc
@@ -868,6 +868,38 @@ fold_using_range::range_of_builtin_call (irange &r, gcall *call,
 	}
       break;
 
+    case CFN_BUILT_IN_TOUPPER:
+      {
+	arg = gimple_call_arg (call, 0);
+	if (!src.get_operand (r, arg))
+	  return false;
+	// Return the range passed in without any lower case characters,
+	// but including all the upper case ones.
+	int_range<2> exclude (build_int_cst (type, 'a'),
+			      build_int_cst (type, 'z'), VR_ANTI_RANGE);
+	r.intersect (exclude);
+	int_range<2> uppers (build_int_cst (type, 'A'),
+			      build_int_cst (type, 'Z'));
+	r.union_ (uppers);
+	return true;
+      }
+
+     case CFN_BUILT_IN_TOLOWER:
+      {
+	arg = gimple_call_arg (call, 0);
+	if (!src.get_operand (r, arg))
+	  return false;
+	// Return the range passed in without any upper case characters,
+	// but including all the lower case ones.
+	int_range<2> exclude (build_int_cst (type, 'A'),
+			      build_int_cst (type, 'Z'), VR_ANTI_RANGE);
+	r.intersect (exclude);
+	int_range<2> lowers (build_int_cst (type, 'a'),
+			      build_int_cst (type, 'z'));
+	r.union_ (lowers);
+	return true;
+      }
+
     CASE_CFN_FFS:
     CASE_CFN_POPCOUNT:
       // __builtin_ffs* and __builtin_popcount* return [0, prec].
diff --git a/gcc/testsuite/gcc.dg/pr78888.c b/gcc/testsuite/gcc.dg/pr78888.c
new file mode 100644
index 00000000000..77a130cf11c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr78888.c
@@ -0,0 +1,29 @@
+/* PR tree-optimization/78888 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-evrp" } */
+
+void kill (void);
+void keep (void);
+
+void g (int x)
+{
+  if (__builtin_toupper ((unsigned char)x) == 'a')
+    kill ();
+  if (__builtin_toupper ((unsigned char)x) == 'z')
+    kill ();
+  if (__builtin_tolower ((unsigned char)x) == 'A')
+    kill ();
+  if (__builtin_tolower ((unsigned char)x) == 'Z')
+    kill ();
+
+  if (__builtin_toupper ((unsigned char)x) == 'A')
+    keep ();
+  if (__builtin_toupper ((unsigned char)x) == 'Z')
+    keep ();
+  if (__builtin_tolower ((unsigned char)x) == 'a')
+    keep ();
+  if (__builtin_tolower ((unsigned char)x) == 'z')
+    keep ();
+}
+/* { dg-final { scan-tree-dump-not "kill" "evrp" } }  */
+/* { dg-final { scan-tree-dump-times "keep" 4 "evrp"} } */

Reply via email to