]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c: Fix up __builtin_stdc_rotate_{left,right} lowering [PR117456]
authorJakub Jelinek <jakub@redhat.com>
Tue, 19 Nov 2024 19:34:36 +0000 (20:34 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Tue, 19 Nov 2024 19:34:36 +0000 (20:34 +0100)
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).

2024-11-19  Jakub Jelinek  <jakub@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-fold.cc
gcc/c/c-parser.cc
gcc/testsuite/gcc.dg/builtin-stdc-rotate-4.c [new file with mode: 0644]

index deb6896589f5a2ff98838c7213bd40152c7cef71..0c4c5904e40268b3a0e6c554cb124297c8ca3889 100644 (file)
@@ -410,7 +410,8 @@ c_fully_fold_internal (tree expr, bool in_init, bool *maybe_const_operands,
                    || 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")
index 70fbf94083533d5b4bd380adacf03912f6984833..def6b3004f2593b4e23c408090daec9daf096037 100644 (file)
@@ -12638,8 +12638,38 @@ c_parser_postfix_expression (c_parser *parser)
                                       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),
diff --git a/gcc/testsuite/gcc.dg/builtin-stdc-rotate-4.c b/gcc/testsuite/gcc.dg/builtin-stdc-rotate-4.c
new file mode 100644 (file)
index 0000000..590b3ab
--- /dev/null
@@ -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
+}