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 <[email protected]>
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