Hi!

Apparently the middle-end/expansion can only handle {L,R}ROTATE_EXPR
on types with mode precision, or large/huge BITINT_TYPE.
So, the following patch uses the rotate exprs only in those cases
where it can be handled, and emits code with shifts/ior otherwise.
As types without mode precision including small/medium BITINT_TYPE
have unlikely power of two precision and TRUNC_MOD_EXPR is on many targets
quite expensive, I chose to expand e.g. __builtin_stdc_rotate_left (arg1,
arg2) as
((tem = arg1, count = arg2 % prec)
 ? ((tem << count) | (tem >> (prec - count))) : tem)
rather than
(((tem = arg1) << (count = arg2 % prec))
 | (tem >> (-count % prec))
(where the assignments are really save_exprs, so no UB), because
I think another TRUNC_MOD_EXPR would be more costly in most cases
when the shift count is non-constant (and when it is constant,
it folds to 2 shifts by constant and ior in either case).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2024-11-19  Jakub Jelinek  <ja...@redhat.com>

        PR c/117456
gcc/c/
        * c-parser.cc (c_parser_postfix_expression): Use LROTATE_EXPR
        or RROTATE_EXPR only if type_has_mode_precision_p or if arg1
        has BITINT_TYPE with precision larger than MAX_FIXED_MODE_SIZE.
        Otherwise build BIT_IOR_EXPR of LSHIFT_EXPR and RSHIFT_EXPR
        and wrap it into a COND_EXPR depending on if arg2 is 0 or not.
        * c-fold.cc (c_fully_fold_internal): Check for suppression of
        -Wshift-count-overflow warning.
gcc/testsuite/
        * gcc.dg/builtin-stdc-rotate-4.c: New test.

--- gcc/c/c-parser.cc.jj        2024-11-18 12:21:06.775288293 +0100
+++ gcc/c/c-parser.cc   2024-11-18 14:35:10.821025992 +0100
@@ -12638,8 +12638,38 @@ c_parser_postfix_expression (c_parser *p
                                       build_int_cst (utype, prec));
                  }
 
-               expr.value = build2_loc (loc, code, TREE_TYPE (arg1), arg1,
-                                        arg2);
+               /* The middle-end isn't prepared to handle {L,R}ROTATE_EXPR
+                  on types without mode precision, except for large/huge
+                  _BitInt types.  */
+               if (type_has_mode_precision_p (TREE_TYPE (arg1))
+                   || (TREE_CODE (TREE_TYPE (arg1)) == BITINT_TYPE
+                       && prec > MAX_FIXED_MODE_SIZE))
+                 expr.value = build2_loc (loc, code, TREE_TYPE (arg1), arg1,
+                                          arg2);
+               else
+                 {
+                   arg2 = save_expr (arg2);
+                   tree t1 = build2_loc (loc, (code == LROTATE_EXPR
+                                               ? LSHIFT_EXPR : RSHIFT_EXPR),
+                                         TREE_TYPE (arg1), arg1, arg2);
+                   tree t2 = build2_loc (loc, MINUS_EXPR,
+                                         TREE_TYPE (arg2),
+                                         build_int_cst (TREE_TYPE (arg2),
+                                                        prec), arg2);
+                   t2 = build2_loc (loc, (code == LROTATE_EXPR
+                                          ? RSHIFT_EXPR : LSHIFT_EXPR),
+                                    TREE_TYPE (arg1), arg1, t2);
+                   suppress_warning (t2, OPT_Wshift_count_overflow);
+                   tree t3 = build2_loc (loc, BIT_IOR_EXPR,
+                                         TREE_TYPE (arg1), t1, t2);
+                   tree t4 = build2_loc (loc, NE_EXPR, boolean_type_node,
+                                         arg2,
+                                         build_zero_cst (TREE_TYPE (arg2)));
+                   t4 = build2_loc (loc, COMPOUND_EXPR, boolean_type_node,
+                                    arg1, t4);
+                   expr.value = build3_loc (loc, COND_EXPR,
+                                            TREE_TYPE (arg1), t4, t3, arg1);
+                 }
                if (instrument_expr)
                  expr.value = build2_loc (loc, COMPOUND_EXPR,
                                           TREE_TYPE (expr.value),
--- gcc/c/c-fold.cc.jj  2024-10-29 09:06:13.004007965 +0100
+++ gcc/c/c-fold.cc     2024-11-18 13:36:48.315860618 +0100
@@ -410,7 +410,8 @@ c_fully_fold_internal (tree expr, bool i
                    || TREE_CODE (TREE_TYPE (orig_op0)) == FIXED_POINT_TYPE)
                   && compare_tree_int (op1,
                                        TYPE_PRECISION (TREE_TYPE (orig_op0)))
-                     >= 0)
+                     >= 0
+                  && !warning_suppressed_p (expr, OPT_Wshift_count_overflow))
            warning_at (loc, OPT_Wshift_count_overflow,
                        (code == LSHIFT_EXPR
                         ? G_("left shift count >= width of type")
--- gcc/testsuite/gcc.dg/builtin-stdc-rotate-4.c.jj     2024-11-18 
13:26:10.199757170 +0100
+++ gcc/testsuite/gcc.dg/builtin-stdc-rotate-4.c        2024-11-18 
15:02:38.342026090 +0100
@@ -0,0 +1,120 @@
+/* PR c/117456 */
+/* { dg-do run } */
+/* { dg-options "-std=c11" } */
+
+struct S {
+  unsigned s : 5;
+};
+
+unsigned
+f1 (struct S s)
+{
+  return __builtin_stdc_rotate_left (s.s, 3);
+}
+
+unsigned
+f2 (struct S s, int n)
+{
+  return __builtin_stdc_rotate_left (s.s, n);
+}
+
+unsigned
+f3 (struct S s)
+{
+  return __builtin_stdc_rotate_right (s.s, 2);
+}
+
+unsigned
+f4 (struct S s, int n)
+{
+  return __builtin_stdc_rotate_right (s.s, n);
+}
+
+#if __BITINT_MAXWIDTH__ >= 64
+unsigned _BitInt(5)
+f5 (unsigned _BitInt(5) s)
+{
+  return __builtin_stdc_rotate_left (s, 3);
+}
+
+unsigned _BitInt(5)
+f6 (unsigned _BitInt(5) s, int n)
+{
+  return __builtin_stdc_rotate_left (s, n);
+}
+
+unsigned _BitInt(5)
+f7 (unsigned _BitInt(5) s)
+{
+  return __builtin_stdc_rotate_right (s, 2);
+}
+
+unsigned _BitInt(5)
+f8 (unsigned _BitInt(5) s, int n)
+{
+  return __builtin_stdc_rotate_right (s, n);
+}
+#endif
+
+#if __BITINT_MAXWIDTH__ >= 125
+unsigned _BitInt(125)
+f9 (unsigned _BitInt(125) s)
+{
+  return __builtin_stdc_rotate_left (s, 13);
+}
+
+unsigned _BitInt(125)
+f10 (unsigned _BitInt(125) s, int n)
+{
+  return __builtin_stdc_rotate_left (s, n);
+}
+
+unsigned _BitInt(125)
+f11 (unsigned _BitInt(125) s)
+{
+  return __builtin_stdc_rotate_right (s, 42);
+}
+
+unsigned _BitInt(125)
+f12 (unsigned _BitInt(125) s, int n)
+{
+  return __builtin_stdc_rotate_right (s, n);
+}
+#endif
+
+int
+main ()
+{
+  struct S s = { 0x12 };
+  if (f1 (s) != 0x14
+      || f2 (s, 0) != 0x12
+      || f2 (s, 2) != 0xa
+      || f2 (s, 1) != 0x5
+      || f3 (s) != 0x14
+      || f4 (s, 0) != 0x12
+      || f4 (s, 2) != 0x14
+      || f4 (s, 1) != 0x9)
+    __builtin_abort ();
+#if __BITINT_MAXWIDTH__ >= 64
+  if (f5 (0x12uwb) != 0x14uwb
+      || f6 (0x12uwb, 0) != 0x12uwb
+      || f6 (0x12uwb, 2) != 0xauwb
+      || f6 (0x12uwb, 1) != 0x5uwb
+      || f7 (0x12uwb) != 0x14uwb
+      || f8 (0x12uwb, 0) != 0x12uwb
+      || f8 (0x12uwb, 2) != 0x14uwb
+      || f8 (0x12uwb, 1) != 0x9uwb)
+    __builtin_abort ();
+#endif
+#if __BITINT_MAXWIDTH__ >= 125
+  if (f9 (12107255122146692213464668179507246062uwb) != 
32859299037257821061785486091897129243uwb
+      || f10 (12107255122146692213464668179507246062uwb, 0) != 
12107255122146692213464668179507246062uwb
+      || f10 (12107255122146692213464668179507246062uwb, 57) != 
786310737972746809290227161460052307uwb
+      || f10 (12107255122146692213464668179507246062uwb, 1) != 
24214510244293384426929336359014492124uwb
+      || f11 (12107255122146692213464668179507246062uwb) != 
25567301336572975565218391744704605699uwb
+      || f12 (12107255122146692213464668179507246062uwb, 0) != 
12107255122146692213464668179507246062uwb
+      || f12 (12107255122146692213464668179507246062uwb, 22) != 
27217840477347696606051931660144451082uwb
+      || f12 (12107255122146692213464668179507246062uwb, 1) != 
6053627561073346106732334089753623031uwb)
+    __builtin_abort ();
+#endif
+}

        Jakub

Reply via email to