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