diff mbox series

c, v3: Add __builtin_stdc_rotate_{left,right} builtins [PR117030]

Message ID Zx9C8gMWxSIE4zoI@tucnak
State New
Headers show
Series c, v3: Add __builtin_stdc_rotate_{left,right} builtins [PR117030] | expand

Commit Message

Jakub Jelinek Oct. 28, 2024, 7:53 a.m. UTC
On Fri, Oct 25, 2024 at 08:06:36PM +0000, Joseph Myers wrote:
> If sanitizing makes sense for these built-in functions, surely it should 
> check for all negative shifts, including those that are multiples of the 
> width (and there should be tests for it in the testsuite).  So sanitizing 
> would require more complicated logic to avoid reducing a negative shift 
> modulo the width at all.  Or in the absence of support for sanitizing 
> these functions, the logic for modulo reduction shouldn't need to depend 
> on whether sanitizing.
> 
> > @@ -409,7 +416,9 @@ c_fully_fold_internal (tree expr, bool i
> >  	    warning_at (loc, OPT_Wshift_count_overflow,
> >  			(code == LSHIFT_EXPR
> >  			 ? G_("left shift count >= width of type")
> > -			 : G_("right shift count >= width of type")));
> > +			 : code == RSHIFT_EXPR
> > +			 ? G_("right shift count >= width of type")
> > +			 : G_("rotate count >= width of type")));
> 
> This shouldn't be reachable, since larger rotate counts are valid and 
> should have been reduced modulo the width.

Ok, here is an updated patch, which for ubsan checks just for negative
count and nothing else, does that check before using TRUNC_MOD_EXPR on
the argument and uses it on unsigned types in all cases.
The c_fully_fold_internal new wording is removed in both cases, as the
argument will be modulo shift count and so it can't be negative or
too large anymore.

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

2024-10-28  Jakub Jelinek  <jakub@redhat.com>

	PR c/117030
gcc/
	* doc/extend.texi (__builtin_stdc_rotate_left,
	__builtin_stdc_rotate_right): Document.
gcc/c-family/
	* c-common.cc (c_common_reswords): Add __builtin_stdc_rotate_left
	and __builtin_stdc_rotate_right.
	* c-ubsan.cc (ubsan_instrument_shift): For {L,R}ROTATE_EXPR
	just check if op1 is negative.
gcc/c/
	* c-parser.cc: Include asan.h and c-family/c-ubsan.h.
	(c_parser_postfix_expression): Handle __builtin_stdc_rotate_left
	and __builtin_stdc_rotate_right.
	* c-fold.cc (c_fully_fold_internal): Handle LROTATE_EXPR and
	RROTATE_EXPR.
gcc/testsuite/
	* gcc.dg/builtin-stdc-rotate-1.c: New test.
	* gcc.dg/builtin-stdc-rotate-2.c: New test.
	* gcc.dg/ubsan/builtin-stdc-rotate-1.c: New test.
	* gcc.dg/ubsan/builtin-stdc-rotate-2.c: New test.



	Jakub

Comments

Joseph Myers Oct. 28, 2024, 11:02 p.m. UTC | #1
On Mon, 28 Oct 2024, Jakub Jelinek wrote:

> Ok, here is an updated patch, which for ubsan checks just for negative
> count and nothing else, does that check before using TRUNC_MOD_EXPR on
> the argument and uses it on unsigned types in all cases.
> The c_fully_fold_internal new wording is removed in both cases, as the
> argument will be modulo shift count and so it can't be negative or
> too large anymore.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

This version is OK.
diff mbox series

Patch

--- gcc/c-family/c-common.cc.jj	2024-10-25 10:00:29.314770060 +0200
+++ gcc/c-family/c-common.cc	2024-10-26 11:52:52.332892683 +0200
@@ -448,6 +448,8 @@  const struct c_common_resword c_common_r
   { "__builtin_stdc_has_single_bit", RID_BUILTIN_STDC, D_CONLY },
   { "__builtin_stdc_leading_ones", RID_BUILTIN_STDC, D_CONLY },
   { "__builtin_stdc_leading_zeros", RID_BUILTIN_STDC, D_CONLY },
+  { "__builtin_stdc_rotate_left", RID_BUILTIN_STDC, D_CONLY },
+  { "__builtin_stdc_rotate_right", RID_BUILTIN_STDC, D_CONLY },
   { "__builtin_stdc_trailing_ones", RID_BUILTIN_STDC, D_CONLY },
   { "__builtin_stdc_trailing_zeros", RID_BUILTIN_STDC, D_CONLY },
   { "__builtin_tgmath", RID_BUILTIN_TGMATH, D_CONLY },
--- gcc/c-family/c-ubsan.cc.jj	2024-10-25 10:00:29.317770017 +0200
+++ gcc/c-family/c-ubsan.cc	2024-10-26 11:57:29.199977994 +0200
@@ -176,8 +176,19 @@  ubsan_instrument_shift (location_t loc,
   op0 = unshare_expr (op0);
   op1 = unshare_expr (op1);
 
-  t = fold_convert_loc (loc, op1_utype, op1);
-  t = fold_build2 (GT_EXPR, boolean_type_node, t, uprecm1);
+  if (code == LROTATE_EXPR || code == RROTATE_EXPR)
+    {
+      /* For rotates just check for negative op1.  */
+      if (TYPE_UNSIGNED (type1))
+	return NULL_TREE;
+      t = fold_build2 (LT_EXPR, boolean_type_node, op1,
+		       build_int_cst (type1, 0));
+    }
+  else
+    {
+      t = fold_convert_loc (loc, op1_utype, op1);
+      t = fold_build2 (GT_EXPR, boolean_type_node, t, uprecm1);
+    }
 
   /* If this is not a signed operation, don't perform overflow checks.
      Also punt on bit-fields.  */
--- gcc/c/c-parser.cc.jj	2024-10-25 10:00:29.322769946 +0200
+++ gcc/c/c-parser.cc	2024-10-26 12:05:40.900026757 +0200
@@ -74,6 +74,8 @@  along with GCC; see the file COPYING3.
 #include "bitmap.h"
 #include "analyzer/analyzer-language.h"
 #include "toplev.h"
+#include "asan.h"
+#include "c-family/c-ubsan.h"
 
 /* We need to walk over decls with incomplete struct/union/enum types
    after parsing the whole translation unit.
@@ -12262,12 +12264,15 @@  c_parser_postfix_expression (c_parser *p
 	      C_BUILTIN_STDC_HAS_SINGLE_BIT,
 	      C_BUILTIN_STDC_LEADING_ONES,
 	      C_BUILTIN_STDC_LEADING_ZEROS,
+	      C_BUILTIN_STDC_ROTATE_LEFT,
+	      C_BUILTIN_STDC_ROTATE_RIGHT,
 	      C_BUILTIN_STDC_TRAILING_ONES,
 	      C_BUILTIN_STDC_TRAILING_ZEROS,
 	      C_BUILTIN_STDC_MAX
 	    } stdc_rid = C_BUILTIN_STDC_MAX;
 	    const char *name
 	      = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value);
+	    unsigned num_args = 1;
 	    switch (name[sizeof ("__builtin_stdc_") - 1])
 	      {
 	      case 'b':
@@ -12316,6 +12321,13 @@  c_parser_postfix_expression (c_parser *p
 		else
 		  stdc_rid = C_BUILTIN_STDC_LEADING_ZEROS;
 		break;
+	      case 'r':
+		if (name[sizeof ("__builtin_stdc_rotate_") - 1] == 'l')
+		  stdc_rid = C_BUILTIN_STDC_ROTATE_LEFT;
+		else
+		  stdc_rid = C_BUILTIN_STDC_ROTATE_RIGHT;
+		num_args = 2;
+		break;
 	      case 't':
 		if (name[sizeof ("__builtin_stdc_trailing_") - 1] == 'o')
 		  stdc_rid = C_BUILTIN_STDC_TRAILING_ONES;
@@ -12334,7 +12346,7 @@  c_parser_postfix_expression (c_parser *p
 		break;
 	      }
 
-	    if (vec_safe_length (cexpr_list) != 1)
+	    if (vec_safe_length (cexpr_list) != num_args)
 	      {
 		error_at (loc, "wrong number of arguments to %qs", name);
 		expr.set_error ();
@@ -12406,6 +12418,77 @@  c_parser_postfix_expression (c_parser *p
 	       without evaluating arg multiple times, type being
 	       __typeof (arg) and prec __builtin_popcountg ((type) ~0)).  */
 	    int prec = TYPE_PRECISION (type);
+	    if (num_args == 2)
+	      {
+		/* Expand:
+		   __builtin_stdc_rotate_left (arg1, arg2) as
+		   arg1 r<< (arg2 % prec)
+		   __builtin_stdc_rotate_right (arg1, arg2) as
+		   arg1 r>> (arg2 % prec).  */
+		arg_p = &(*cexpr_list)[1];
+		*arg_p = convert_lvalue_to_rvalue (loc, *arg_p, true, true);
+		if (!INTEGRAL_TYPE_P (TREE_TYPE (arg_p->value)))
+		  {
+		    error_at (loc, "%qs operand not an integral type", name);
+		    expr.set_error ();
+		    break;
+		  }
+		if (TREE_CODE (TREE_TYPE (arg_p->value)) == ENUMERAL_TYPE)
+		  {
+		    error_at (loc, "argument %u in call to function "
+				   "%qs has enumerated type", 2, name);
+		    expr.set_error ();
+		    break;
+		  }
+		tree arg1 = save_expr (arg);
+		tree arg2 = save_expr (arg_p->value);
+		tree_code code;
+		if (stdc_rid == C_BUILTIN_STDC_ROTATE_LEFT)
+		  code = LROTATE_EXPR;
+		else
+		  code = RROTATE_EXPR;
+
+		if (TREE_CODE (arg2) == INTEGER_CST
+		    && tree_int_cst_sgn (arg2) < 0)
+		  warning_at (loc, OPT_Wshift_count_negative,
+			      "rotate count is negative");
+
+		tree instrument_expr = NULL_TREE;
+		if (sanitize_flags_p (SANITIZE_SHIFT))
+		  instrument_expr = ubsan_instrument_shift (loc, code,
+							    arg1, arg2);
+
+		/* Promote arg2 to unsigned just so that we don't
+		   need to deal with arg2 type not being able to represent
+		   prec.  In the end gimplification uses unsigned int
+		   for all shifts/rotates anyway.  */
+		if (TYPE_PRECISION (TREE_TYPE (arg2))
+		    < TYPE_PRECISION (integer_type_node))
+		  arg2 = fold_convert (unsigned_type_node, arg2);
+
+		if (TYPE_UNSIGNED (TREE_TYPE (arg2)))
+		  arg2 = build2_loc (loc, TRUNC_MOD_EXPR, TREE_TYPE (arg2),
+				     arg2, build_int_cst (TREE_TYPE (arg2),
+							  prec));
+		else
+		  {
+		    /* When second argument is signed, just do the modulo in
+		       unsigned type, that results in better generated code
+		       (for power of 2 precisions bitwise AND).  */
+		    tree utype = c_common_unsigned_type (TREE_TYPE (arg2));
+		    arg2 = build2_loc (loc, TRUNC_MOD_EXPR, utype,
+				       fold_convert (utype, arg2),
+				       build_int_cst (utype, prec));
+		  }
+
+		expr.value = build2_loc (loc, code, TREE_TYPE (arg1), arg1,
+					 arg2);
+		if (instrument_expr)
+		  expr.value = build2_loc (loc, COMPOUND_EXPR,
+					   TREE_TYPE (expr.value),
+					   instrument_expr, expr.value);
+		break;
+	      }
 	    tree barg1 = arg;
 	    switch (stdc_rid)
 	      {
--- gcc/c/c-fold.cc.jj	2024-10-25 09:35:28.145154965 +0200
+++ gcc/c/c-fold.cc	2024-10-26 11:54:10.859782375 +0200
@@ -328,6 +328,8 @@  c_fully_fold_internal (tree expr, bool i
     case EXACT_DIV_EXPR:
     case LSHIFT_EXPR:
     case RSHIFT_EXPR:
+    case LROTATE_EXPR:
+    case RROTATE_EXPR:
     case BIT_IOR_EXPR:
     case BIT_XOR_EXPR:
     case BIT_AND_EXPR:
@@ -389,7 +391,10 @@  c_fully_fold_internal (tree expr, bool i
 	  && tree_int_cst_sgn (op0) < 0)
 	warning_at (loc, OPT_Wshift_negative_value,
 		    "left shift of negative value");
-      if ((code == LSHIFT_EXPR || code == RSHIFT_EXPR)
+      if ((code == LSHIFT_EXPR
+	   || code == RSHIFT_EXPR
+	   || code == LROTATE_EXPR
+	   || code == RROTATE_EXPR)
 	  && TREE_CODE (orig_op1) != INTEGER_CST
 	  && TREE_CODE (op1) == INTEGER_CST
 	  && TREE_CODE (TREE_TYPE (orig_op1)) == INTEGER_TYPE
--- gcc/doc/extend.texi.jj	2024-10-25 09:35:28.474150291 +0200
+++ gcc/doc/extend.texi	2024-10-26 11:52:52.340892570 +0200
@@ -16129,6 +16129,32 @@  performed on the argument.  It is equiva
 @code{(unsigned int) __builtin_ctzg (@var{arg}, @var{prec})}
 @enddefbuiltin
 
+@defbuiltin{@var{type1} __builtin_stdc_rotate_left (@var{type1} @var{arg1}, @var{type2} @var{arg2})}
+The @code{__builtin_stdc_rotate_left} function is available only
+in C.  It is type-generic, the first argument can be any unsigned integer
+(standard, extended or bit-precise) and second argument any signed or
+unsigned integer or @code{char}.  No integral argument promotions are
+performed on the arguments.  It is equivalent to
+@code{(@var{type1}) ((@var{arg1} << (@var{arg2} % @var{prec}))
+| (@var{arg1} >> ((-(unsigned @var{type2}) @var{arg2}) % @var{prec})))}
+where @var{prec} is bit width of @var{type1}, except that side-effects
+in @var{arg1} and @var{arg2} are evaluated just once.  The behavior is
+undefined if @var{arg2} is negative.
+@enddefbuiltin
+
+@defbuiltin{@var{type1} __builtin_stdc_rotate_right (@var{type1} @var{arg1}, @var{type2} @var{arg2})}
+The @code{__builtin_stdc_rotate_right} function is available only
+in C.  It is type-generic, the first argument can be any unsigned integer
+(standard, extended or bit-precise) and second argument any signed or
+unsigned integer or @code{char}.  No integral argument promotions are
+performed on the arguments.  It is equivalent to
+@code{(@var{type1}) ((@var{arg1} >> (@var{arg2} % @var{prec}))
+| (@var{arg1} << ((-(unsigned @var{type2}) @var{arg2}) % @var{prec})))}
+where @var{prec} is bit width of @var{type1}, except that side-effects
+in @var{arg1} and @var{arg2} are evaluated just once.  The behavior is
+undefined if @var{arg2} is negative.
+@enddefbuiltin
+
 @defbuiltin{double __builtin_powi (double, int)}
 @defbuiltinx{float __builtin_powif (float, int)}
 @defbuiltinx{{long double} __builtin_powil (long double, int)}
--- gcc/testsuite/gcc.dg/builtin-stdc-rotate-1.c.jj	2024-10-26 11:52:52.341892555 +0200
+++ gcc/testsuite/gcc.dg/builtin-stdc-rotate-1.c	2024-10-26 11:52:52.341892555 +0200
@@ -0,0 +1,235 @@ 
+/* { dg-do run } */
+/* { dg-options "-std=c11" } */
+
+unsigned long long
+rotate_left (unsigned char a, unsigned short b, unsigned int c,
+	     unsigned long d, unsigned long long e, int n)
+{
+  return (__builtin_stdc_rotate_left (a, n)
+	  + __builtin_stdc_rotate_left (b, n)
+	  + __builtin_stdc_rotate_left (c, n)
+	  + __builtin_stdc_rotate_left (d, n)
+	  + __builtin_stdc_rotate_left (e, n));
+}
+
+unsigned long long
+rotate_right (unsigned char a, unsigned short b, unsigned int c,
+	      unsigned long d, unsigned long long e, int n)
+{
+  return (__builtin_stdc_rotate_right (a, n)
+	  + __builtin_stdc_rotate_right (b, n)
+	  + __builtin_stdc_rotate_right (c, n)
+	  + __builtin_stdc_rotate_right (d, n)
+	  + __builtin_stdc_rotate_right (e, n));
+}
+
+#define expr_has_type(e, t) _Generic (e, default : 0, t : 1)
+
+int
+main ()
+{
+  if (__builtin_stdc_rotate_left ((unsigned char) 0, 0) != 0
+      || __builtin_stdc_rotate_left ((unsigned char) 0xdcU, (char) 0) != 0xdcU
+      || __builtin_stdc_rotate_left ((unsigned char) 0xdcU, __CHAR_BIT__) != 0xdcU
+      || __builtin_stdc_rotate_left ((unsigned char) 0xdcU, 2 * __CHAR_BIT__) != 0xdcU
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned char) 0, 0), unsigned char)
+      || __builtin_stdc_rotate_left ((unsigned char) 1, (_Bool) 1) != 2
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned char) 1, (unsigned char) 1), unsigned char)
+      || __builtin_stdc_rotate_left ((unsigned char) ~1U, 3) != (unsigned char) ~8U
+      || __builtin_stdc_rotate_left ((unsigned char) (2U + __SCHAR_MAX__), 1) != 3
+      || __builtin_stdc_rotate_left ((unsigned char) (2U + __SCHAR_MAX__), __CHAR_BIT__ + 1) != 3
+      || __builtin_stdc_rotate_left ((unsigned short) 0, 0) != 0
+      || __builtin_stdc_rotate_left ((unsigned short) 0xdcabU, 0) != 0xdcabU
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned short) 0, (signed char) 0), unsigned short)
+      || __builtin_stdc_rotate_left ((unsigned short) 1, 13) != (1U << 13)
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned short) 1, 13), unsigned short)
+      || __builtin_stdc_rotate_left ((unsigned short) ~1U, 7) != (unsigned short) ~0x80U
+      || __builtin_stdc_rotate_left ((unsigned short) (2U + __SHRT_MAX__), 1) != 3
+      || __builtin_stdc_rotate_left (0U, 0) != 0
+      || __builtin_stdc_rotate_left (0xcdbaU, (short int) 0) != 0xcdbaU
+      || !expr_has_type (__builtin_stdc_rotate_left (0U, 0), unsigned int)
+      || __builtin_stdc_rotate_left (1U, (char) 15) != (1U << 15)
+      || !expr_has_type (__builtin_stdc_rotate_left (1U, 15), unsigned int)
+      || __builtin_stdc_rotate_left (~1U, 9) != ~0x200U
+      || __builtin_stdc_rotate_left (2U + __INT_MAX__, 1) != 3
+      || __builtin_stdc_rotate_left (0UL, 0) != 0
+      || __builtin_stdc_rotate_left (0xdc8971baUL, 0) != 0xdc8971baUL
+      || !expr_has_type (__builtin_stdc_rotate_left (0UL, 0LL), unsigned long)
+      || __builtin_stdc_rotate_left (1UL, 30) != (1UL << 30)
+      || !expr_has_type (__builtin_stdc_rotate_left (1UL, 30), unsigned long)
+      || __builtin_stdc_rotate_left (~1UL, 22) != ~0x400000UL
+      || __builtin_stdc_rotate_left (2UL + __LONG_MAX__, 1) != 3
+      || __builtin_stdc_rotate_left (2UL + __LONG_MAX__, (int) (sizeof (unsigned long) * __CHAR_BIT__) + 1) != 3
+      || __builtin_stdc_rotate_left (0ULL, (_Bool) 0) != 0
+      || __builtin_stdc_rotate_left (0xdc897143985734baULL, 0) != 0xdc897143985734baULL
+      || __builtin_stdc_rotate_left (0xdc897143985734baULL, 4 * (int) (sizeof (unsigned long long) * __CHAR_BIT__)) != 0xdc897143985734baULL
+      || !expr_has_type (__builtin_stdc_rotate_left (0ULL, 0), unsigned long long)
+      || __builtin_stdc_rotate_left (1ULL, 62) != (1ULL << 62)
+      || !expr_has_type (__builtin_stdc_rotate_left (1ULL, 62ULL), unsigned long long)
+      || __builtin_stdc_rotate_left (~1ULL, 53) != ~0x20000000000000ULL
+      || __builtin_stdc_rotate_left (2ULL + __LONG_LONG_MAX__, 1) != 3)
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_right ((unsigned char) 0, 0) != 0
+      || __builtin_stdc_rotate_right ((unsigned char) 0xdcU, 0) != 0xdcU
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned char) 0, 0), unsigned char)
+      || __builtin_stdc_rotate_right ((unsigned char) 1, 1) != (1U + __SCHAR_MAX__)
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned char) 1, 1), unsigned char)
+      || __builtin_stdc_rotate_right ((unsigned char) ~1U, (char) 3) != (unsigned char) ~((1U + __SCHAR_MAX__) >> 2)
+      || __builtin_stdc_rotate_right ((unsigned char) 3, 1) != (2U + __SCHAR_MAX__)
+      || __builtin_stdc_rotate_right ((unsigned short) 0, 0) != 0
+      || __builtin_stdc_rotate_right ((unsigned short) 0xdcabU, 0) != 0xdcabU
+      || __builtin_stdc_rotate_right ((unsigned short) 0xdcabU, sizeof (unsigned short) * __CHAR_BIT__) != 0xdcabU
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned short) 0, 0), unsigned short)
+      || __builtin_stdc_rotate_right ((unsigned short) (1U << 13), 12) != 2
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned short) (1U << 13), 2), unsigned short)
+      || __builtin_stdc_rotate_right ((unsigned short) ~1U, 7) != (unsigned short) ~((1U + __SHRT_MAX__) >> 6)
+      || __builtin_stdc_rotate_right ((unsigned short) 3, 1) != (2U + __SHRT_MAX__)
+      || __builtin_stdc_rotate_right (0U, 0) != 0
+      || __builtin_stdc_rotate_right (0xcdbaU, 0) != 0xcdbaU
+      || !expr_has_type (__builtin_stdc_rotate_right (0U, 0), unsigned int)
+      || __builtin_stdc_rotate_right (1U << 15, 13) != 4U
+      || !expr_has_type (__builtin_stdc_rotate_right (1U << 15, 13), unsigned int)
+      || __builtin_stdc_rotate_right (~1U, 9) != ~((1U + __INT_MAX__) >> 8)
+      || __builtin_stdc_rotate_right (3U, (_Bool) 1) != (2U + __INT_MAX__)
+      || __builtin_stdc_rotate_right (3U, (int) (sizeof (unsigned) * __CHAR_BIT__) + 1) != (2U + __INT_MAX__)
+      || __builtin_stdc_rotate_right (0UL, (_Bool) 0) != 0
+      || __builtin_stdc_rotate_right (0xdc8971baUL, 0) != 0xdc8971baUL
+      || !expr_has_type (__builtin_stdc_rotate_right (0UL, 0), unsigned long)
+      || __builtin_stdc_rotate_right (1UL << 30, 27) != 8UL
+      || !expr_has_type (__builtin_stdc_rotate_right (1UL << 30, 27), unsigned long)
+      || __builtin_stdc_rotate_right (~1UL, 22) != ~((1UL + __LONG_MAX__) >> 21)
+      || __builtin_stdc_rotate_right (3UL, 1) != (2UL + __LONG_MAX__)
+      || __builtin_stdc_rotate_right (0ULL, 0) != 0
+      || __builtin_stdc_rotate_right (0xdc897143985734baULL, 0) != 0xdc897143985734baULL
+      || !expr_has_type (__builtin_stdc_rotate_right (0ULL, 0), unsigned long long)
+      || __builtin_stdc_rotate_right (1ULL << 62, 60) != 4ULL
+      || !expr_has_type (__builtin_stdc_rotate_right (1ULL << 62, 60), unsigned long long)
+      || __builtin_stdc_rotate_right (~1ULL, 53) != ~((1ULL + __LONG_LONG_MAX__) >> 52)
+      || __builtin_stdc_rotate_right (3ULL, 1) != (2ULL + __LONG_LONG_MAX__))
+    __builtin_abort ();
+#ifdef __SIZEOF_INT128__
+  if (__builtin_stdc_rotate_left ((unsigned __int128) 0U, 0) != 0
+      || __builtin_stdc_rotate_left (((unsigned __int128) 0x43256567547ULL) << 64 | 0xdc897143985734baULL, 0) != (((unsigned __int128) 0x43256567547ULL) << 64 | 0xdc897143985734baULL)
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned __int128) 0U, 0), unsigned __int128)
+      || __builtin_stdc_rotate_left ((unsigned __int128) 1U, 115) != ((unsigned __int128) 1U << 115)
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned __int128) 1U, 115), unsigned __int128)
+      || __builtin_stdc_rotate_left (~(unsigned __int128) 1ULL, 53) != ~(unsigned __int128) 0x20000000000000ULL
+      || __builtin_stdc_rotate_left (1ULL + ((unsigned __int128) 1U << 127), 1) != 3)
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_right (0ULL, 0) != 0
+      || __builtin_stdc_rotate_right (((unsigned __int128) 0x43256567547ULL) << 64 | 0xdc897143985734baULL, 0) != (((unsigned __int128) 0x43256567547ULL) << 64 | 0xdc897143985734baULL)
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned __int128) 0U, 0), unsigned __int128)
+      || __builtin_stdc_rotate_right ((unsigned __int128) 1ULL << 115, 110) != 32U
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned __int128) 1ULL << 115, 110), unsigned __int128)
+      || __builtin_stdc_rotate_right (~(unsigned __int128) 1ULL, 53) != ~((unsigned __int128) 1 << 75)
+      || __builtin_stdc_rotate_right ((unsigned __int128) 3ULL, 1) != 1U + ((unsigned __int128) 1U << 127))
+    __builtin_abort ();
+#endif
+#if __has_builtin (__builtin_stdc_rotate_left) != 1
+#error __builtin_stdc_rotate_left not implemented
+#endif
+#if __has_builtin (__builtin_stdc_rotate_right) != 1
+#error __builtin_stdc_rotate_right not implemented
+#endif
+  unsigned char a = 1;
+  unsigned short b = 1;
+  unsigned int c = 1;
+  unsigned long d = 1;
+  unsigned long long e = 1;
+  int f = 1;
+  if (__builtin_stdc_rotate_left (a++, f++) != 2 || a != 2 || f != 2)
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_left (b++, f++) != 4 || b != 2 || f != 3)
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_left (c++, f++) != 8 || c != 2 || f != 4)
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_left (d++, f++) != 16 || d != 2 || f != 5)
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_left (e++, f++) != 32 || e != 2 || f != 6)
+    __builtin_abort ();
+  f = 1;
+  if (__builtin_stdc_rotate_right (a++, f++) != 1 || a != 3 || f != 2)
+    __builtin_abort ();
+  f = 1;
+  if (__builtin_stdc_rotate_right (b++, f++) != 1 || b != 3 || f != 2)
+    __builtin_abort ();
+  f = 1;
+  if (__builtin_stdc_rotate_right (c++, f++) != 1 || c != 3 || f != 2)
+    __builtin_abort ();
+  f = 1;
+  if (__builtin_stdc_rotate_right (d++, f++) != 1 || d != 3 || f != 2)
+    __builtin_abort ();
+  f = 1;
+  if (__builtin_stdc_rotate_right (e++, f++) != 1 || e != 3 || f != 2)
+    __builtin_abort ();
+#ifdef __SIZEOF_INT128__
+  unsigned __int128 g = 1;
+  if (__builtin_stdc_rotate_left (g++, f++) != 4 || g != 2 || f != 3)
+    __builtin_abort ();
+  f = 1;
+  if (__builtin_stdc_rotate_right (g++, f++) != 1 || g != 3 || f != 2)
+    __builtin_abort ();
+#endif
+#if __BITINT_MAXWIDTH__ >= 64
+  if (__builtin_stdc_rotate_left (0uwb, 0) != 0
+      || __builtin_stdc_rotate_left (1uwb, 0) != 1uwb
+      || !expr_has_type (__builtin_stdc_rotate_left (0uwb, 0), unsigned _BitInt(1)))
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_left ((unsigned _BitInt(2)) 0, 0) != 0
+      || __builtin_stdc_rotate_left ((unsigned _BitInt(2)) 1, (_BitInt(27)) 1) != 2uwb
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned _BitInt(2)) 0, 0), unsigned _BitInt(2))
+      || __builtin_stdc_rotate_left ((unsigned _BitInt(2)) 1, 1) != 2
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned _BitInt(2)) 1, (unsigned _BitInt(2)) 1), unsigned _BitInt(2))
+      || __builtin_stdc_rotate_left ((unsigned _BitInt(2)) 2, 1) != 1)
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_right (0uwb, 0) != 0
+      || __builtin_stdc_rotate_right (1uwb, 0) != 1uwb
+      || !expr_has_type (__builtin_stdc_rotate_right (0uwb, 0), unsigned _BitInt(1)))
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_right ((unsigned _BitInt(2)) 0, (_BitInt(3)) 0) != 0
+      || __builtin_stdc_rotate_right ((unsigned _BitInt(2)) 1, 1) != 2uwb
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned _BitInt(2)) 0, 0), unsigned _BitInt(2))
+      || __builtin_stdc_rotate_right ((unsigned _BitInt(2)) 1, 1) != 2
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned _BitInt(2)) 1, 1), unsigned _BitInt(2))
+      || __builtin_stdc_rotate_right ((unsigned _BitInt(2)) 2, 1) != 1)
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_left ((unsigned _BitInt(59)) 0U, 0) != 0
+      || __builtin_stdc_rotate_left ((unsigned _BitInt(59)) 0x43256567547ULL, 0) != ((unsigned _BitInt(59)) 0x43256567547ULL)
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned _BitInt(59)) 0U, 0), unsigned _BitInt(59))
+      || __builtin_stdc_rotate_left ((unsigned _BitInt(59)) 1U, 57) != ((unsigned _BitInt(59)) 1U << 57)
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned _BitInt(59)) 1U, 57), unsigned _BitInt(59))
+      || __builtin_stdc_rotate_left (~(unsigned _BitInt(59)) 1ULL, 53) != ~(unsigned _BitInt(59)) 0x20000000000000ULL
+      || __builtin_stdc_rotate_left (1uwb + ((unsigned _BitInt(59)) 1U << 58), 1) != 3)
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_right (0ULL, 0) != 0
+      || __builtin_stdc_rotate_right ((unsigned _BitInt(59)) 0x43256567547ULL, 0) != ((unsigned _BitInt(59)) 0x43256567547ULL)
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned _BitInt(59)) 0U, 0), unsigned _BitInt(59))
+      || __builtin_stdc_rotate_right ((unsigned _BitInt(59)) 1ULL << 57, 55) != 4U
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned _BitInt(59)) 1U << 57, 55), unsigned _BitInt(59))
+      || __builtin_stdc_rotate_right (~(unsigned _BitInt(59)) 1ULL, 53) != ~((unsigned _BitInt(59)) 1 << 6)
+      || __builtin_stdc_rotate_right ((unsigned _BitInt(59)) 3ULL, 1) != 1uwb + ((unsigned _BitInt(59)) 1U << 58))
+    __builtin_abort ();
+#endif
+#if __BITINT_MAXWIDTH__ >= 512
+  if (__builtin_stdc_rotate_left ((unsigned _BitInt(510)) 0U, (_BitInt(503)) 0) != 0
+      || __builtin_stdc_rotate_left (((unsigned _BitInt(510)) 0x43256567547ULL) << 64 | 0xdc897143985734baULL, 0) != (((unsigned _BitInt(510)) 0x43256567547ULL) << 64 | 0xdc897143985734baULL)
+      || __builtin_stdc_rotate_left (((unsigned _BitInt(510)) 0x43256567547ULL) << 64 | 0xdc897143985734baULL, 510) != (((unsigned _BitInt(510)) 0x43256567547ULL) << 64 | 0xdc897143985734baULL)
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned _BitInt(510)) 0U, 0), unsigned _BitInt(510))
+      || __builtin_stdc_rotate_left ((unsigned _BitInt(510)) 1U, 508) != ((unsigned _BitInt(510)) 1U << 508)
+      || !expr_has_type (__builtin_stdc_rotate_left ((unsigned _BitInt(510)) 1U, (unsigned _BitInt(505)) 508), unsigned _BitInt(510))
+      || __builtin_stdc_rotate_left (~(unsigned _BitInt(510)) 1ULL, 53) != ~(unsigned _BitInt(510)) 0x20000000000000ULL
+      || __builtin_stdc_rotate_left (1uwb + ((unsigned _BitInt(510)) 1U << 509), 1) != 3
+      || __builtin_stdc_rotate_left (1uwb + ((unsigned _BitInt(510)) 1U << 509), 511U) != 3)
+    __builtin_abort ();
+  if (__builtin_stdc_rotate_right (0ULL, 0) != 0
+      || __builtin_stdc_rotate_right (((unsigned _BitInt(510)) 0x43256567547ULL) << 64 | 0xdc897143985734baULL, 0) != (((unsigned _BitInt(510)) 0x43256567547ULL) << 64 | 0xdc897143985734baULL)
+      || __builtin_stdc_rotate_right (((unsigned _BitInt(510)) 0x43256567547ULL) << 64 | 0xdc897143985734baULL, 510U * 2) != (((unsigned _BitInt(510)) 0x43256567547ULL) << 64 | 0xdc897143985734baULL)
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned _BitInt(510)) 0U, 0), unsigned _BitInt(510))
+      || __builtin_stdc_rotate_right ((unsigned _BitInt(510)) 1ULL << 508, 503) != 32U
+      || !expr_has_type (__builtin_stdc_rotate_right ((unsigned _BitInt(510)) 1ULL << 508, 503), unsigned _BitInt(510))
+      || __builtin_stdc_rotate_right (~(unsigned _BitInt(510)) 1ULL, 499) != ~((unsigned _BitInt(510)) 1 << 11)
+      || __builtin_stdc_rotate_right ((unsigned _BitInt(510)) 3ULL, 1) != 1uwb + ((unsigned _BitInt(510)) 1U << 509)
+      || __builtin_stdc_rotate_right ((unsigned _BitInt(510)) 3ULL, 511) != 1uwb + ((unsigned _BitInt(510)) 1U << 509))
+    __builtin_abort ();
+#endif
+}
--- gcc/testsuite/gcc.dg/builtin-stdc-rotate-2.c.jj	2024-10-26 11:52:52.341892555 +0200
+++ gcc/testsuite/gcc.dg/builtin-stdc-rotate-2.c	2024-10-26 11:52:52.341892555 +0200
@@ -0,0 +1,70 @@ 
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void
+foo (void)
+{
+  typedef int V __attribute__ ((vector_size (4 * sizeof (int))));
+  struct S { int s; };
+  enum E { E0, E1 };
+  __builtin_stdc_rotate_left (0.0f, 0);			/* { dg-error "'__builtin_stdc_rotate_left' operand not an integral type" } */
+  __builtin_stdc_rotate_left (0.0, 0);			/* { dg-error "'__builtin_stdc_rotate_left' operand not an integral type" } */
+  __builtin_stdc_rotate_left (0.0L, 0);			/* { dg-error "'__builtin_stdc_rotate_left' operand not an integral type" } */
+  __builtin_stdc_rotate_left ((V) {}, 0);		/* { dg-error "'__builtin_stdc_rotate_left' operand not an integral type" } */
+  __builtin_stdc_rotate_left ((struct S) { 0 }, 0);	/* { dg-error "'__builtin_stdc_rotate_left' operand not an integral type" } */
+  __builtin_stdc_rotate_left ();			/* { dg-error "wrong number of arguments to '__builtin_stdc_rotate_left'" } */
+  __builtin_stdc_rotate_left (0U);			/* { dg-error "wrong number of arguments to '__builtin_stdc_rotate_left'" } */
+  __builtin_stdc_rotate_left (0U, 0U, 0U);		/* { dg-error "wrong number of arguments to '__builtin_stdc_rotate_left'" } */
+  __builtin_stdc_rotate_left ((_Bool) 0, 0);		/* { dg-error "argument 1 in call to function '__builtin_stdc_rotate_left' has boolean type" } */
+  __builtin_stdc_rotate_left ((enum E) E0, 0);		/* { dg-error "argument 1 in call to function '__builtin_stdc_rotate_left' has enumerated type" } */
+  __builtin_stdc_rotate_left (0, 0);			/* { dg-error "argument 1 in call to function '__builtin_stdc_rotate_left' has signed type" } */
+  __builtin_stdc_rotate_left (0U, 0.0f);		/* { dg-error "'__builtin_stdc_rotate_left' operand not an integral type" } */
+  __builtin_stdc_rotate_left (0U, 0.0);			/* { dg-error "'__builtin_stdc_rotate_left' operand not an integral type" } */
+  __builtin_stdc_rotate_left (0U, 0.0L);		/* { dg-error "'__builtin_stdc_rotate_left' operand not an integral type" } */
+  __builtin_stdc_rotate_left (0U, (V) {});		/* { dg-error "'__builtin_stdc_rotate_left' operand not an integral type" } */
+  __builtin_stdc_rotate_left (0U, (struct S) { 0 });	/* { dg-error "'__builtin_stdc_rotate_left' operand not an integral type" } */
+  __builtin_stdc_rotate_left (0U, (enum E) E0);		/* { dg-error "argument 2 in call to function '__builtin_stdc_rotate_left' has enumerated type" } */
+  __builtin_stdc_rotate_right (0.0f, 0);		/* { dg-error "'__builtin_stdc_rotate_right' operand not an integral type" } */
+  __builtin_stdc_rotate_right (0.0, 0);			/* { dg-error "'__builtin_stdc_rotate_right' operand not an integral type" } */
+  __builtin_stdc_rotate_right (0.0L, 0);		/* { dg-error "'__builtin_stdc_rotate_right' operand not an integral type" } */
+  __builtin_stdc_rotate_right ((V) {}, 0);		/* { dg-error "'__builtin_stdc_rotate_right' operand not an integral type" } */
+  __builtin_stdc_rotate_right ((struct S) { 0 }, 0);	/* { dg-error "'__builtin_stdc_rotate_right' operand not an integral type" } */
+  __builtin_stdc_rotate_right ();			/* { dg-error "wrong number of arguments to '__builtin_stdc_rotate_right'" } */
+  __builtin_stdc_rotate_right (0U);			/* { dg-error "wrong number of arguments to '__builtin_stdc_rotate_right'" } */
+  __builtin_stdc_rotate_right (0U, 0U, 0U);		/* { dg-error "wrong number of arguments to '__builtin_stdc_rotate_right'" } */
+  __builtin_stdc_rotate_right ((_Bool) 0, 0);		/* { dg-error "argument 1 in call to function '__builtin_stdc_rotate_right' has boolean type" } */
+  __builtin_stdc_rotate_right ((enum E) E0, 0);		/* { dg-error "argument 1 in call to function '__builtin_stdc_rotate_right' has enumerated type" } */
+  __builtin_stdc_rotate_right (0, 0);			/* { dg-error "argument 1 in call to function '__builtin_stdc_rotate_right' has signed type" } */
+  __builtin_stdc_rotate_right (0U, 0.0f);		/* { dg-error "'__builtin_stdc_rotate_right' operand not an integral type" } */
+  __builtin_stdc_rotate_right (0U, 0.0);		/* { dg-error "'__builtin_stdc_rotate_right' operand not an integral type" } */
+  __builtin_stdc_rotate_right (0U, 0.0L);		/* { dg-error "'__builtin_stdc_rotate_right' operand not an integral type" } */
+  __builtin_stdc_rotate_right (0U, (V) {});		/* { dg-error "'__builtin_stdc_rotate_right' operand not an integral type" } */
+  __builtin_stdc_rotate_right (0U, (struct S) { 0 });	/* { dg-error "'__builtin_stdc_rotate_right' operand not an integral type" } */
+  __builtin_stdc_rotate_right (0U, (enum E) E0);	/* { dg-error "argument 2 in call to function '__builtin_stdc_rotate_right' has enumerated type" } */
+  __builtin_stdc_rotate_left ((unsigned char) 0, -1);	/* { dg-warning "rotate count is negative" } */
+  __builtin_stdc_rotate_right ((unsigned char) 0, -1);	/* { dg-warning "rotate count is negative" } */
+  __builtin_stdc_rotate_left ((unsigned short) 0, -1);	/* { dg-warning "rotate count is negative" } */
+  __builtin_stdc_rotate_right ((unsigned short) 0, -1);	/* { dg-warning "rotate count is negative" } */
+  __builtin_stdc_rotate_left (0U, -1);			/* { dg-warning "rotate count is negative" } */
+  __builtin_stdc_rotate_right (0U, -1);			/* { dg-warning "rotate count is negative" } */
+  __builtin_stdc_rotate_left (0UL, -1);			/* { dg-warning "rotate count is negative" } */
+  __builtin_stdc_rotate_right (0UL, -1);		/* { dg-warning "rotate count is negative" } */
+  __builtin_stdc_rotate_left (0ULL, -1);		/* { dg-warning "rotate count is negative" } */
+  __builtin_stdc_rotate_right (0ULL, -1);		/* { dg-warning "rotate count is negative" } */
+#ifdef __SIZEOF_INT128__
+  __builtin_stdc_rotate_left ((unsigned __int128) 0, -1); /* { dg-warning "rotate count is negative" "" { target int128 } } */
+  __builtin_stdc_rotate_right ((unsigned __int128) 0, -1); /* { dg-warning "rotate count is negative" "" { target int128 } } */
+#endif
+#if __BITINT_MAXWIDTH__ >= 64
+  __builtin_stdc_rotate_left (0uwb, -1);		/* { dg-warning "rotate count is negative" "" { target bitint } } */
+  __builtin_stdc_rotate_right (0uwb, -1);		/* { dg-warning "rotate count is negative" "" { target bitint } } */
+  __builtin_stdc_rotate_left ((unsigned _BitInt(2)) 0, -1); /* { dg-warning "rotate count is negative" "" { target bitint } } */
+  __builtin_stdc_rotate_right ((unsigned _BitInt(2)) 0, -1); /* { dg-warning "rotate count is negative" "" { target bitint } } */
+  __builtin_stdc_rotate_left ((unsigned _BitInt(59)) 0, -1); /* { dg-warning "rotate count is negative" "" { target bitint } } */
+  __builtin_stdc_rotate_right ((unsigned _BitInt(59)) 0, -1); /* { dg-warning "rotate count is negative" "" { target bitint } } */
+#endif
+#if __BITINT_MAXWIDTH__ >= 575
+  __builtin_stdc_rotate_left ((unsigned _BitInt(525)) 0, -1); /* { dg-warning "rotate count is negative" "" { target bitint575 } } */
+  __builtin_stdc_rotate_right ((unsigned _BitInt(525)) 0, -1); /* { dg-warning "rotate count is negative" "" { target bitint575 } } */
+#endif
+}
--- gcc/testsuite/gcc.dg/ubsan/builtin-stdc-rotate-1.c.jj	2024-10-26 12:08:49.361362620 +0200
+++ gcc/testsuite/gcc.dg/ubsan/builtin-stdc-rotate-1.c	2024-10-26 12:12:16.435435374 +0200
@@ -0,0 +1,14 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=shift -std=c2y" } */
+
+int
+main ()
+{
+  int a = -42;
+  unsigned b = 42;
+  unsigned c = __builtin_stdc_rotate_left (b, a);
+  unsigned d = __builtin_stdc_rotate_right (b, a - 1);
+  volatile int e = c + d;
+}
+/* { dg-output "shift exponent -42 is negative\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent -43 is negative" } */
--- gcc/testsuite/gcc.dg/ubsan/builtin-stdc-rotate-2.c.jj	2024-10-26 12:12:24.586320150 +0200
+++ gcc/testsuite/gcc.dg/ubsan/builtin-stdc-rotate-2.c	2024-10-26 12:14:52.270232462 +0200
@@ -0,0 +1,14 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=shift -fno-sanitize-recover=shift -std=c2y" } */
+
+int
+main ()
+{
+  int a = sizeof (unsigned) * __CHAR_BIT__ + 1;
+  unsigned b = 42;
+  unsigned c = __builtin_stdc_rotate_left (b, a);
+  unsigned d = __builtin_stdc_rotate_right (b, a);
+  volatile int e = c + d;
+  if (c != 84 || d != 21)
+    __builtin_abort ();
+}