From 02fff24e2c6d4affc47dac1433b2fb182dadd4db Mon Sep 17 00:00:00 2001
From: Jakub Jelinek <jakub@redhat.com>
Date: Tue, 19 Nov 2024 20:34:36 +0100
Subject: c: Fix up __builtin_stdc_rotate_{left,right} lowering [PR117456]

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-parser.cc | 34 ++++++++++++++++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

(limited to 'gcc/c/c-parser.cc')

diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 70fbf94..def6b30 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -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),
-- 
cgit v1.1