]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c: Add __builtin_stdc_rotate_{left,right} builtins [PR117030]
authorJakub Jelinek <jakub@redhat.com>
Tue, 29 Oct 2024 08:06:25 +0000 (09:06 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Tue, 29 Oct 2024 08:06:25 +0000 (09:06 +0100)
I believe the new C2Y <stdbit.h> type-generic functions
stdc_rotate_{left,right} have the same problems the other stdc_*
type-generic functions had.  If we want to support arbitrary
unsigned _BitInt(N), don't want to use statement expressions
(so that one can actually use them in static variable initializers),
don't want to evaluate the arguments multiple times and don't want
to expand the arguments multiple times during preprocessing to avoid the
old tgmath preprocessing bloat, we need a built-in for those.

The following patch adds those.  And as we need to support rotations by 0
and tree-ssa-forwprop.cc is only able to pattern recognize with BIT_AND_EXPR
for that case (i.e. for power of two widths), the patch just constructs
LROTATE_EXPR/RROTATE_EXPR right away.  Negative second arguments are
considered UB, while positive ones are modulo precision.

2024-10-29  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.

gcc/c-family/c-common.cc
gcc/c-family/c-ubsan.cc
gcc/c/c-fold.cc
gcc/c/c-parser.cc
gcc/doc/extend.texi
gcc/testsuite/gcc.dg/builtin-stdc-rotate-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-stdc-rotate-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ubsan/builtin-stdc-rotate-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ubsan/builtin-stdc-rotate-2.c [new file with mode: 0644]

index a0e6a3118d73fdca69351a6231e23bbd151840da..88827ac2bdba61a53a83400e4611760a931f1ab3 100644 (file)
@@ -448,6 +448,8 @@ const struct c_common_resword c_common_reswords[] =
   { "__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 },
index a49158b7ad6d8f8eb320cf9d00f09606e19f1792..ab67635d1644cf1e8fd8080d86fb1e811cb55f76 100644 (file)
@@ -176,8 +176,19 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
   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.  */
index 06085f5f58f53f763fd49af53da291f7114aee67..deb6896589f5a2ff98838c7213bd40152c7cef71 100644 (file)
@@ -328,6 +328,8 @@ c_fully_fold_internal (tree expr, bool in_init, bool *maybe_const_operands,
     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 in_init, bool *maybe_const_operands,
          && 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
index 195cace0a04d32e69ef3693657ce01c050707fbc..88dd0be855c297612eebbb9b6e35657851837f5a 100644 (file)
@@ -74,6 +74,8 @@ along with GCC; see the file COPYING3.  If not see
 #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 *parser)
              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 *parser)
                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 *parser)
                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 *parser)
               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)
              {
index 6c2d6a610cd657585217c5fbe6b7797971b15e5c..f97e008cb8448a83ce8063da5b6e04aaa118992d 100644 (file)
@@ -16129,6 +16129,32 @@ performed on the argument.  It is equivalent to
 @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)}
diff --git a/gcc/testsuite/gcc.dg/builtin-stdc-rotate-1.c b/gcc/testsuite/gcc.dg/builtin-stdc-rotate-1.c
new file mode 100644 (file)
index 0000000..6f23095
--- /dev/null
@@ -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
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-stdc-rotate-2.c b/gcc/testsuite/gcc.dg/builtin-stdc-rotate-2.c
new file mode 100644 (file)
index 0000000..d874196
--- /dev/null
@@ -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
+}
diff --git a/gcc/testsuite/gcc.dg/ubsan/builtin-stdc-rotate-1.c b/gcc/testsuite/gcc.dg/ubsan/builtin-stdc-rotate-1.c
new file mode 100644 (file)
index 0000000..50da7ba
--- /dev/null
@@ -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" } */
diff --git a/gcc/testsuite/gcc.dg/ubsan/builtin-stdc-rotate-2.c b/gcc/testsuite/gcc.dg/ubsan/builtin-stdc-rotate-2.c
new file mode 100644 (file)
index 0000000..97f3ebc
--- /dev/null
@@ -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 ();
+}