c: Add __builtin_stdc_bit_{width,floor,ceil} builtins

Message ID ZVkTsF8twXnINNrz@tucnak
State Unresolved
Headers
Series c: Add __builtin_stdc_bit_{width,floor,ceil} builtins |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Jakub Jelinek Nov. 18, 2023, 7:42 p.m. UTC
  Hi!

For these 3 type-generic macros I'm out of ideas how to satisfy
all the requirements (no use of ({ ... }), not expanding argument multiple
times, not evaluating side-effects multiple times using some small building
blocks, so the following patch introduces 3 new C only builtins which
can be used to implement those macros.

As a bonus, I think with this and previous 2 patches all the type-generic
stdbit.h macros can be actually used in constant expressions.
While in theory __builtin_stdc_bit_width builtin could be C/C++ in
builtins.def, given that it has unsigned return type rather than the
type-generic type, because it has very similar implementation to the other
two and is not really useful for C++ I chose to implement all 3 in there.

Ok for trunk if it passes bootstrap/regtest?

2023-11-18  Jakub Jelinek  <jakub@redhat.com>

gcc/
	* doc/extend.texi (__builtin_stdc_bit_width, __builtin_stdc_bit_floor,
	__builtin_stdc_bit_ceil): Document.
gcc/c-family/
	* c-common.h (enum rid): Add RID_BUILTIN_STDC_BIT_WIDTH,
	RID_BUILTIN_STDC_BIT_FLOOR and RID_BUILTIN_STDC_BIT_CEIL.
	* c-common.cc (c_common_reswords): Add __builtin_stdc_bit_width,
	__builtin_stdc_bit_floor and __builtin_stdc_bit_ceil.
gcc/c/
	* c-parser.cc (c_parser_postfix_expression): Handle
	RID_BUILTIN_STDC_BIT_WIDTH, RID_BUILTIN_STDC_BIT_FLOOR and
	RID_BUILTIN_STDC_BIT_CEIL.
	* c-decl.cc (names_builtin_p): Likewise.
gcc/testsuite/
	* gcc.dg/builtin-stdc-bit-1.c: New test.
	* gcc.dg/builtin-stdc-bit-2.c: New test.


	Jakub
  

Comments

Jakub Jelinek Nov. 20, 2023, 3:12 p.m. UTC | #1
On Mon, Nov 20, 2023 at 02:55:33PM +0000, Joseph Myers wrote:
> On Sat, 18 Nov 2023, Jakub Jelinek wrote:
> 
> > +@defbuiltin{@var{type} __builtin_stdc_bit_ceil (@var{type} @var{arg})}
> > +The @code{__builtin_stdc_bit_ceil} function is available only
> > +in C.  It is type-generic, the argument can be any unsigned integer
> > +(standard, extended or bit-precise).  No integral argument promotions are
> > +performed on the argument.  It is equivalent to
> > +@code{@var{arg} <= 1 ? (@var{type}) 1
> > +: (@var{type}) 1 << (@var{prec} - __builtin_clzg ((@var{type}) (@var{arg} - 1)))}
> > +where @var{prec} is bit width of @var{type}, except that side-effects
> > +in @var{arg} are evaluated just once.
> > +@enddefbuiltin
> 
> Note that stdc_bit_ceil now has defined behavior (return 0) on overflow: 
> CD2 comment FR-135 was accepted for the DIS at the June WG14 meeting.  
> This affects both the documentation and the implementation, as they need 
> to avoid an undefined shift by the width of the type.  That's why my 
> stdbit.h implementations have two shifts (not claiming that's necessarily 
> the optimal way of ensuring the correct result in the overflow case).
> 
>   return __x <= 1 ? 1 : ((uint64_t) 1) << (__bw64_inline (__x - 1) - 1) << 1;

So
  return __x <= 1 ? 1 : ((uint64_t) 2) << (__bw64_inline (__x - 1) - 1);
then?

	Jakub
  
Jakub Jelinek Nov. 21, 2023, 8:24 a.m. UTC | #2
On Mon, Nov 20, 2023 at 04:29:47PM +0100, Jakub Jelinek wrote:
> On Mon, Nov 20, 2023 at 04:03:07PM +0100, Jakub Jelinek wrote:
> > > Note that stdc_bit_ceil now has defined behavior (return 0) on overflow: 
> > > CD2 comment FR-135 was accepted for the DIS at the June WG14 meeting.  
> > > This affects both the documentation and the implementation, as they need 
> > > to avoid an undefined shift by the width of the type.  That's why my 
> > > stdbit.h implementations have two shifts (not claiming that's necessarily 
> > > the optimal way of ensuring the correct result in the overflow case).
> > > 
> > >   return __x <= 1 ? 1 : ((uint64_t) 1) << (__bw64_inline (__x - 1) - 1) << 1;
> > 
> > Given the feedback from Richi I've in the meantime reworked the patch to
> > add all 14 builtins (but because the enum rid is very close to 256 values
> > and with 14 new ones was already 7 too many, used one RID value for all 14
> > builtins (different spellings)).
> > 
> > Will need to rework it for CD2 FR-135 then...
> 
> Here it is updated to use that
> x <= 1 ? 1 : ((type) 2) << (prec - 1 - __builtin_clzg ((type) (x - 1)))
> I've mentioned.

Now successfully bootstrapped/regtested on x86_64-linux and i686-linux
on top of the https://gcc.gnu.org/pipermail/gcc-patches/2023-November/637540.html
patch (the bug was discovered while working on this patch).

> 2023-11-20  Jakub Jelinek  <jakub@redhat.com>
> 
> gcc/
> 	* doc/extend.texi (__builtin_stdc_bit_ceil, __builtin_stdc_bit_floor,
> 	__builtin_stdc_bit_width, __builtin_stdc_count_ones,
> 	__builtin_stdc_count_zeros, __builtin_stdc_first_leading_one,
> 	__builtin_stdc_first_leading_zero, __builtin_stdc_first_trailing_one,
> 	__builtin_stdc_first_trailing_zero, __builtin_stdc_has_single_bit,
> 	__builtin_stdc_leading_ones, __builtin_stdc_leading_zeros,
> 	__builtin_stdc_trailing_ones, __builtin_stdc_trailing_zeros): Document.
> gcc/c-family/
> 	* c-common.h (enum rid): Add RID_BUILTIN_STDC: New.
> 	* c-common.cc (c_common_reswords): Add __builtin_stdc_bit_ceil,
> 	__builtin_stdc_bit_floor, __builtin_stdc_bit_width,
> 	__builtin_stdc_count_ones, __builtin_stdc_count_zeros,
> 	__builtin_stdc_first_leading_one, __builtin_stdc_first_leading_zero,
> 	__builtin_stdc_first_trailing_one, __builtin_stdc_first_trailing_zero,
> 	__builtin_stdc_has_single_bit, __builtin_stdc_leading_ones,
> 	__builtin_stdc_leading_zeros, __builtin_stdc_trailing_ones and
> 	__builtin_stdc_trailing_zeros.  Move __builtin_assoc_barrier
> 	alphabetically earlier.
> gcc/c/
> 	* c-parser.cc (c_parser_postfix_expression): Handle RID_BUILTIN_STDC.
> 	* c-decl.cc (names_builtin_p): Likewise.
> gcc/testsuite/
> 	* gcc.dg/builtin-stdc-bit-1.c: New test.
> 	* gcc.dg/builtin-stdc-bit-2.c: New test.

	Jakub
  

Patch

--- gcc/doc/extend.texi.jj	2023-11-18 19:29:08.056176234 +0100
+++ gcc/doc/extend.texi	2023-11-18 19:35:08.454176312 +0100
@@ -15074,6 +15074,37 @@  there is no need to specify the argument
 promotions are performed on the argument.
 @enddefbuiltin
 
+@defbuiltin{unsigned int __builtin_stdc_bit_width (@var{type} @var{arg})}
+The @code{__builtin_stdc_bit_width} function is available only
+in C.  It is type-generic, the argument can be any unsigned integer
+(standard, extended or bit-precise).  No integral argument promotions are
+performed on the argument.  It is equivalent to
+@code{(unsigned int) (@var{prec} - __builtin_clzg (@var{arg}, @var{prec}))}
+where @var{prec} is bit width of @var{type}.
+@enddefbuiltin
+
+@defbuiltin{@var{type} __builtin_stdc_bit_floor (@var{type} @var{arg})}
+The @code{__builtin_stdc_bit_floor} function is available only
+in C.  It is type-generic, the argument can be any unsigned integer
+(standard, extended or bit-precise).  No integral argument promotions are
+performed on the argument.  It is equivalent to
+@code{@var{arg} == 0 ? (@var{type}) 0
+: (@var{type}) 1 << (@var{prec} - 1 - __builtin_clzg (@var{arg}))}
+where @var{prec} is bit width of @var{type}, except that side-effects
+in @var{arg} are evaluated just once.
+@enddefbuiltin
+
+@defbuiltin{@var{type} __builtin_stdc_bit_ceil (@var{type} @var{arg})}
+The @code{__builtin_stdc_bit_ceil} function is available only
+in C.  It is type-generic, the argument can be any unsigned integer
+(standard, extended or bit-precise).  No integral argument promotions are
+performed on the argument.  It is equivalent to
+@code{@var{arg} <= 1 ? (@var{type}) 1
+: (@var{type}) 1 << (@var{prec} - __builtin_clzg ((@var{type}) (@var{arg} - 1)))}
+where @var{prec} is bit width of @var{type}, except that side-effects
+in @var{arg} are evaluated just once.
+@enddefbuiltin
+
 @defbuiltin{double __builtin_powi (double, int)}
 @defbuiltinx{float __builtin_powif (float, int)}
 @defbuiltinx{{long double} __builtin_powil (long double, int)}
--- gcc/c-family/c-common.h.jj	2023-11-18 17:05:00.223049400 +0100
+++ gcc/c-family/c-common.h	2023-11-18 18:46:53.162346148 +0100
@@ -110,7 +110,8 @@  enum rid
   RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,	     RID_BUILTIN_SHUFFLE,
   RID_BUILTIN_SHUFFLEVECTOR,   RID_BUILTIN_CONVERTVECTOR,   RID_BUILTIN_TGMATH,
   RID_BUILTIN_HAS_ATTRIBUTE,   RID_BUILTIN_ASSOC_BARRIER,
-  RID_BUILTIN_BIT_COMPLEMENT,
+  RID_BUILTIN_BIT_COMPLEMENT,  RID_BUILTIN_STDC_BIT_WIDTH,
+  RID_BUILTIN_STDC_BIT_CEIL,   RID_BUILTIN_STDC_BIT_FLOOR,
   RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
 
   /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
--- gcc/c-family/c-common.cc.jj	2023-11-18 17:03:17.838466559 +0100
+++ gcc/c-family/c-common.cc	2023-11-18 18:47:47.809588817 +0100
@@ -392,6 +392,9 @@  const struct c_common_resword c_common_r
   { "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
   { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
   { "__builtin_shufflevector", RID_BUILTIN_SHUFFLEVECTOR, 0 },
+  { "__builtin_stdc_bit_ceil", RID_BUILTIN_STDC_BIT_CEIL, D_CONLY },
+  { "__builtin_stdc_bit_floor", RID_BUILTIN_STDC_BIT_FLOOR, D_CONLY },
+  { "__builtin_stdc_bit_width", RID_BUILTIN_STDC_BIT_WIDTH, D_CONLY },
   { "__builtin_tgmath", RID_BUILTIN_TGMATH, D_CONLY },
   { "__builtin_offsetof", RID_OFFSETOF, 0 },
   { "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, D_CONLY },
--- gcc/c/c-parser.cc.jj	2023-11-18 17:16:23.228591823 +0100
+++ gcc/c/c-parser.cc	2023-11-18 20:15:47.609493782 +0100
@@ -11744,14 +11744,18 @@  c_parser_postfix_expression (c_parser *p
 	  }
 	  break;
 	case RID_BUILTIN_BIT_COMPLEMENT:
+	case RID_BUILTIN_STDC_BIT_CEIL:
+	case RID_BUILTIN_STDC_BIT_FLOOR:
+	case RID_BUILTIN_STDC_BIT_WIDTH:
 	  {
 	    vec<c_expr_t, va_gc> *cexpr_list;
 	    c_expr_t *arg_p;
 	    location_t close_paren_loc;
+	    enum rid rid = c_parser_peek_token (parser)->keyword;
+	    const char *name = IDENTIFIER_POINTER (ridpointers[rid]);
 
 	    c_parser_consume_token (parser);
-	    if (!c_parser_get_builtin_args (parser,
-					    "__builtin_bit_complement",
+	    if (!c_parser_get_builtin_args (parser, name,
 					    &cexpr_list, false,
 					    &close_paren_loc))
 	      {
@@ -11761,8 +11765,7 @@  c_parser_postfix_expression (c_parser *p
 
 	    if (vec_safe_length (cexpr_list) != 1)
 	      {
-		error_at (loc, "wrong number of arguments to "
-			       "%<__builtin_bit_complement%>");
+		error_at (loc, "wrong number of arguments to %qs", name);
 		expr.set_error ();
 		break;
 	      }
@@ -11771,16 +11774,92 @@  c_parser_postfix_expression (c_parser *p
 	    *arg_p = convert_lvalue_to_rvalue (loc, *arg_p, true, true);
 	    if (!INTEGRAL_TYPE_P (TREE_TYPE (arg_p->value)))
 	      {
-		error_at (loc, "%<__builtin_bit_complement%> operand "
-			  "not an integral type");
+		error_at (loc, "%qs operand not an integral type", name);
 		expr.set_error ();
 		break;
 	      }
-	    expr.value
-	      = build1_loc (loc, BIT_NOT_EXPR,
-			    TYPE_MAIN_VARIANT (TREE_TYPE (arg_p->value)),
-			    arg_p->value);
+	    tree arg = arg_p->value;
+	    tree type = TYPE_MAIN_VARIANT (TREE_TYPE (arg));
+	    if (rid == RID_BUILTIN_BIT_COMPLEMENT)
+	      {
+		/* Expand __builtin_bit_complement (arg) as ~arg.  */
+		expr.value = build1_loc (loc, BIT_NOT_EXPR, type, arg);
+		set_c_expr_source_range (&expr, loc, close_paren_loc);
+		break;
+	      }
+	    /* Expand __builtin_stdc_bit_width (arg) as
+	       (unsigned int) (prec - __builtin_clzg (arg, prec)).
+	       Expand __builtin_stdc_bit_floor (arg) as
+	       arg == 0 ? (type) 0
+			: (type) 1 << (prec - 1 - __builtin_clzg (arg))
+	       without evaluating arg multiple times.
+	       Expand __builtin_stdc_bit_ceil (arg) as
+	       arg <= 1 ? (type) 1
+			: (type) 1 << (prec - __builtin_clzg (arg - 1))
+	       without evaluating arg multiple times.  */
+	    int prec = TYPE_PRECISION (type);
+	    if (rid != RID_BUILTIN_STDC_BIT_WIDTH)
+	      arg = save_expr (arg);
+	    /* Construct a call to __builtin_clzg.  */
+	    int nargs = rid == RID_BUILTIN_STDC_BIT_WIDTH ? 2 : 1;
+	    vec<tree, va_gc> *args;
+	    vec_alloc (args, nargs);
+	    vec<tree, va_gc> *origtypes;
+	    vec_alloc (origtypes, nargs);
+	    auto_vec<location_t> arg_loc (nargs);
+	    if (rid == RID_BUILTIN_STDC_BIT_CEIL)
+	      args->quick_push (build2_loc (loc, PLUS_EXPR, type,
+					    arg, build_int_cst (type, -1)));
+	    else
+	      args->quick_push (arg);
+	    arg_loc.quick_push (arg_p->get_location ());
+	    origtypes->quick_push (arg_p->original_type);
+	    if (nargs == 2)
+	      {
+		args->quick_push (build_int_cst (integer_type_node, prec));
+		arg_loc.quick_push (loc);
+		origtypes->quick_push (integer_type_node);
+	      }
+	    tree fndecl = builtin_decl_explicit (BUILT_IN_CLZG);
+	    expr.value = c_build_function_call_vec (loc, arg_loc, fndecl,
+						    args, origtypes);
 	    set_c_expr_source_range (&expr, loc, close_paren_loc);
+	    if (expr.value == error_mark_node)
+	      break;
+	    if (rid == RID_BUILTIN_STDC_BIT_FLOOR)
+	      --prec;
+	    expr.value = build2_loc (loc, MINUS_EXPR, integer_type_node,
+				     build_int_cst (integer_type_node, prec),
+				     expr.value);
+	    if (rid == RID_BUILTIN_STDC_BIT_WIDTH)
+	      {
+		expr.value = fold_convert_loc (loc, unsigned_type_node,
+					       expr.value);
+		break;
+	      }
+	    /* For __builtin_stdc_bit_ceil (0U) or __builtin_stdc_bit_ceil (1U)
+	       or __builtin_stdc_bit_floor (0U) avoid bogus -Wshift-count-*
+	       warnings.  The LSHIFT_EXPR is in dead code in that case.  */
+	    if (integer_zerop (arg)
+		|| (rid == RID_BUILTIN_STDC_BIT_CEIL && integer_onep (arg)))
+	      expr.value = build_int_cst (type, 1);
+	    else
+	      expr.value = build2_loc (loc, LSHIFT_EXPR, type,
+				       build_int_cst (type, 1), expr.value);
+	    if (rid == RID_BUILTIN_STDC_BIT_CEIL)
+	      expr.value = build3_loc (loc, COND_EXPR, type,
+				       build2_loc (loc, LE_EXPR,
+						   boolean_type_node, arg,
+						   build_int_cst (type, 1)),
+				       build_int_cst (type, 1),
+				       expr.value);
+	    else
+	      expr.value = build3_loc (loc, COND_EXPR, type,
+				       build2_loc (loc, EQ_EXPR,
+						   boolean_type_node, arg,
+						   build_int_cst (type, 0)),
+				       build_int_cst (type, 0),
+				       expr.value);
 	    break;
 	  }
 	case RID_AT_SELECTOR:
--- gcc/c/c-decl.cc.jj	2023-11-18 17:31:39.298880201 +0100
+++ gcc/c/c-decl.cc	2023-11-18 18:48:26.796048523 +0100
@@ -11376,6 +11376,9 @@  names_builtin_p (const char *name)
     case RID_BUILTIN_HAS_ATTRIBUTE:
     case RID_BUILTIN_SHUFFLE:
     case RID_BUILTIN_SHUFFLEVECTOR:
+    case RID_BUILTIN_STDC_BIT_CEIL:
+    case RID_BUILTIN_STDC_BIT_FLOOR:
+    case RID_BUILTIN_STDC_BIT_WIDTH:
     case RID_CHOOSE_EXPR:
     case RID_OFFSETOF:
     case RID_TYPES_COMPATIBLE_P:
--- gcc/testsuite/gcc.dg/builtin-stdc-bit-1.c.jj	2023-11-18 19:36:34.422984124 +0100
+++ gcc/testsuite/gcc.dg/builtin-stdc-bit-1.c	2023-11-18 20:05:32.678976258 +0100
@@ -0,0 +1,185 @@ 
+/* { dg-do run } */
+/* { dg-options "-std=c11" } */
+
+#define expr_has_type(e, t) _Generic (e, default : 0, t : 1)
+
+int
+main ()
+{
+  if (__builtin_stdc_bit_width ((unsigned char) 0) != 0
+      || !expr_has_type (__builtin_stdc_bit_width ((unsigned char) 0), unsigned int)
+      || __builtin_stdc_bit_width ((unsigned short) 0) != 0
+      || !expr_has_type (__builtin_stdc_bit_width ((unsigned short) 0), unsigned int)
+      || __builtin_stdc_bit_width (0U) != 0
+      || !expr_has_type (__builtin_stdc_bit_width (0U), unsigned int)
+      || __builtin_stdc_bit_width (0UL) != 0
+      || !expr_has_type (__builtin_stdc_bit_width (0UL), unsigned int)
+      || __builtin_stdc_bit_width (0ULL) != 0
+      || !expr_has_type (__builtin_stdc_bit_width (0ULL), unsigned int))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_width ((unsigned char) ~0U) != __CHAR_BIT__
+      || __builtin_stdc_bit_width ((unsigned short) ~0U) != __SIZEOF_SHORT__ * __CHAR_BIT__
+      || __builtin_stdc_bit_width (~0U) != __SIZEOF_INT__ * __CHAR_BIT__
+      || __builtin_stdc_bit_width (~0UL) != __SIZEOF_LONG__ * __CHAR_BIT__
+      || __builtin_stdc_bit_width (~0ULL) != __SIZEOF_LONG_LONG__ * __CHAR_BIT__)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_width ((unsigned char) ((unsigned char) ~0U >> 1)) != __CHAR_BIT__ - 1
+      || __builtin_stdc_bit_width ((unsigned char) 6) != 3
+      || __builtin_stdc_bit_width ((unsigned short) 12U) != 4
+      || __builtin_stdc_bit_width ((unsigned short) ((unsigned short) ~0U >> 5)) != __SIZEOF_SHORT__ * __CHAR_BIT__ - 5
+      || __builtin_stdc_bit_width (137U) != 8
+      || __builtin_stdc_bit_width (269U) != 9
+      || __builtin_stdc_bit_width (39UL) != 6
+      || __builtin_stdc_bit_width (~0UL >> 2) != __SIZEOF_LONG__ * __CHAR_BIT__ - 2
+      || __builtin_stdc_bit_width (1023ULL) != 10
+      || __builtin_stdc_bit_width (1024ULL) != 11)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor ((unsigned char) 0) != 0
+      || !expr_has_type (__builtin_stdc_bit_floor ((unsigned char) 0), unsigned char)
+      || __builtin_stdc_bit_floor ((unsigned short) 0) != 0
+      || !expr_has_type (__builtin_stdc_bit_floor ((unsigned short) 0), unsigned short)
+      || __builtin_stdc_bit_floor (0U) != 0U
+      || !expr_has_type (__builtin_stdc_bit_floor (0U), unsigned int)
+      || __builtin_stdc_bit_floor (0UL) != 0UL
+      || !expr_has_type (__builtin_stdc_bit_floor (0UL), unsigned long)
+      || __builtin_stdc_bit_floor (0ULL) != 0ULL
+      || !expr_has_type (__builtin_stdc_bit_floor (0ULL), unsigned long long))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor ((unsigned char) ~0U) != (1U << (__CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_floor ((unsigned short) ~0U) != (1U << (__SIZEOF_SHORT__ * __CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_floor (~0U) != (1U << (__SIZEOF_INT__ * __CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_floor (~0UL) != (1UL << (__SIZEOF_LONG__ * __CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_floor (~0ULL) != (1ULL << (__SIZEOF_LONG_LONG__ * __CHAR_BIT__ - 1)))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor ((unsigned char) 4) != 4
+      || __builtin_stdc_bit_floor ((unsigned char) 7) != 4
+      || __builtin_stdc_bit_floor ((unsigned short) 8U) != 8
+      || __builtin_stdc_bit_floor ((unsigned short) 31U) != 16
+      || __builtin_stdc_bit_floor (137U) != 128U
+      || __builtin_stdc_bit_floor (269U) != 256U
+      || __builtin_stdc_bit_floor (511UL) != 256UL
+      || __builtin_stdc_bit_floor (512UL) != 512UL
+      || __builtin_stdc_bit_floor (513UL) != 512ULL
+      || __builtin_stdc_bit_floor (1024ULL) != 1024ULL)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil ((unsigned char) 0) != 1
+      || !expr_has_type (__builtin_stdc_bit_ceil ((unsigned char) 0), unsigned char)
+      || __builtin_stdc_bit_ceil ((unsigned short) 0) != 1
+      || !expr_has_type (__builtin_stdc_bit_ceil ((unsigned short) 0), unsigned short)
+      || __builtin_stdc_bit_ceil (0U) != 1U
+      || !expr_has_type (__builtin_stdc_bit_ceil (0U), unsigned int)
+      || __builtin_stdc_bit_ceil (0UL) != 1UL
+      || !expr_has_type (__builtin_stdc_bit_ceil (0UL), unsigned long)
+      || __builtin_stdc_bit_ceil (0ULL) != 1ULL
+      || !expr_has_type (__builtin_stdc_bit_ceil (0ULL), unsigned long long))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil ((unsigned char) ((unsigned char) ~0U >> 1)) != (1U << (__CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_ceil ((unsigned char) ((unsigned char) ~0U >> 1)) != (1U << (__CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_ceil ((unsigned short) ((unsigned short) ~0U >> 1)) != (1U << (__SIZEOF_SHORT__ * __CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_ceil ((unsigned short) ((unsigned short) ~0U >> 1)) != (1U << (__SIZEOF_SHORT__ * __CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_ceil (~0U >> 1) != (1U << (__SIZEOF_INT__ * __CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_ceil (1U << (__SIZEOF_INT__ * __CHAR_BIT__ - 1)) != (1U << (__SIZEOF_INT__ * __CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_ceil (~0UL >> 1) != (1UL << (__SIZEOF_LONG__ * __CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_ceil (~0UL >> 1) != (1UL << (__SIZEOF_LONG__ * __CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_ceil (1ULL << (__SIZEOF_LONG_LONG__ * __CHAR_BIT__ - 1)) != (1ULL << (__SIZEOF_LONG_LONG__ * __CHAR_BIT__ - 1))
+      || __builtin_stdc_bit_ceil (~0ULL >> 1) != (1ULL << (__SIZEOF_LONG_LONG__ * __CHAR_BIT__ - 1)))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil ((unsigned char) 1) != 1
+      || __builtin_stdc_bit_ceil ((unsigned char) 2) != 2
+      || __builtin_stdc_bit_ceil ((unsigned short) 3U) != 4
+      || __builtin_stdc_bit_ceil ((unsigned short) 4U) != 4
+      || __builtin_stdc_bit_ceil (5U) != 8U
+      || __builtin_stdc_bit_ceil (269U) != 512U
+      || __builtin_stdc_bit_ceil (511UL) != 512UL
+      || __builtin_stdc_bit_ceil (512UL) != 512UL
+      || __builtin_stdc_bit_ceil (513ULL) != 1024ULL
+      || __builtin_stdc_bit_ceil (1025ULL) != 2048ULL)
+    __builtin_abort ();
+#ifdef __SIZEOF_INT128__
+  if (__builtin_stdc_bit_width ((unsigned __int128) 0) != 0
+      || !expr_has_type (__builtin_stdc_bit_width ((unsigned __int128) 0), unsigned int)
+      || __builtin_stdc_bit_width (~(unsigned __int128) 0) != __SIZEOF_INT128__ * __CHAR_BIT__)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor ((unsigned __int128) 0) != 0
+      || !expr_has_type (__builtin_stdc_bit_floor ((unsigned __int128) 0), unsigned __int128)
+      || __builtin_stdc_bit_floor (~(unsigned __int128) 0) != ((unsigned __int128) 1) << (__SIZEOF_INT128__ * __CHAR_BIT__ - 1))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil ((unsigned __int128) 0) != 1
+      || !expr_has_type (__builtin_stdc_bit_ceil ((unsigned __int128) 0), unsigned __int128)
+      || __builtin_stdc_bit_ceil ((unsigned __int128) 1) != 1
+      || __builtin_stdc_bit_ceil ((~(unsigned __int128) 0) >> 1) != ((unsigned __int128) 1) << (__SIZEOF_INT128__ * __CHAR_BIT__ - 1))
+    __builtin_abort ();
+#endif
+#if __has_builtin (__builtin_stdc_bit_width) != 1
+#error __builtin_stdc_bit_width not implemented
+#endif
+#if __has_builtin (__builtin_stdc_bit_floor) != 1
+#error __builtin_stdc_bit_floor not implemented
+#endif
+#if __has_builtin (__builtin_stdc_bit_ceil) != 1
+#error __builtin_stdc_bit_ceil not implemented
+#endif
+  unsigned char a = 0;
+  if (__builtin_stdc_bit_width (a++) != 0 || a != 1)
+    __builtin_abort ();
+  unsigned long long b = 0;
+  if (__builtin_stdc_bit_width (b++) != 0 || b != 1)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor (a++) != 1 || a != 2)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor (b++) != 1 || b != 2)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil (a++) != 2 || a != 3)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil (b++) != 2 || b != 3)
+    __builtin_abort ();
+#if BITINT_MAXWIDTH >= 512
+  if (__builtin_stdc_bit_width ((unsigned _BitInt(512)) 0) != 0
+      || !expr_has_type (__builtin_stdc_bit_width ((unsigned _BitInt(512)) 0), unsigned int)
+      || __builtin_stdc_bit_width ((unsigned _BitInt(373)) 0) != 0
+      || !expr_has_type (__builtin_stdc_bit_width ((unsigned _BitInt(373)) 0), unsigned int))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_width (~(unsigned _BitInt(512)) 0) != 512
+      || __builtin_stdc_bit_width (~(unsigned _BitInt(373)) 0) != 373)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_width (((unsigned _BitInt(512)) 1023) << 405) != 405 + 10
+      || __builtin_stdc_bit_width (((unsigned _BitInt(373)) 1024) << 242) != 242 + 11)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor ((unsigned _BitInt(512)) 0) != 0
+      || !expr_has_type (__builtin_stdc_bit_floor ((unsigned _BitInt(512)) 0), unsigned _BitInt(512))
+      || __builtin_stdc_bit_floor ((unsigned _BitInt(373)) 0) != 0
+      || !expr_has_type (__builtin_stdc_bit_floor ((unsigned _BitInt(373)) 0), unsigned _BitInt(373)))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor (~(unsigned _BitInt(512)) 0) != ((unsigned _BitInt(512)) 1) << (512 - 1)
+      || __builtin_stdc_bit_floor (~(unsigned _BitInt(373)) 0) != ((unsigned _BitInt(373)) 1) << (373 - 1))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor (((unsigned _BitInt(512)) 511) << 405) != (((unsigned _BitInt(512)) 256) << 405)
+      || __builtin_stdc_bit_floor (((unsigned _BitInt(373)) 512) << 242) != (((unsigned _BitInt(512)) 512) << 242))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil ((unsigned _BitInt(512)) 0) != 1
+      || !expr_has_type (__builtin_stdc_bit_ceil ((unsigned _BitInt(512)) 0), unsigned _BitInt(512))
+      || __builtin_stdc_bit_ceil ((unsigned _BitInt(373)) 0) != 1
+      || !expr_has_type (__builtin_stdc_bit_ceil ((unsigned _BitInt(373)) 0), unsigned _BitInt(373)))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil (((unsigned _BitInt(512)) 1) << (512 - 1)) != ((unsigned _BitInt(512)) 1) << (512 - 1)
+      || __builtin_stdc_bit_ceil ((~(unsigned _BitInt(373)) 0) >> 1) != ((unsigned _BitInt(373)) 1) << (373 - 1))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil (((unsigned _BitInt(512)) 512) << 405) != (((unsigned _BitInt(512)) 512) << 405)
+      || __builtin_stdc_bit_ceil (((unsigned _BitInt(373)) 513) << 242) != (((unsigned _BitInt(512)) 1024) << 242))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor ((unsigned _BitInt(BITINT_MAXWIDTH)) 0) != 0)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor (~(unsigned _BitInt(BITINT_MAXWIDTH)) 0) != ((unsigned _BitInt(BITINT_MAXWIDTH)) 1) << (BITINT_MAXWIDTH - 1))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_floor (((unsigned _BitInt(BITINT_MAXWIDTH)) 511) << 405) != (((unsigned _BitInt(BITINT_MAXWIDTH)) 256) << 405)
+      || __builtin_stdc_bit_floor (((unsigned _BitInt(BITINT_MAXWIDTH)) 512) << 405) != (((unsigned _BitInt(BITINT_MAXWIDTH)) 512) << 405))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil ((unsigned _BitInt(BITINT_MAXWIDTH)) 0) != 1)
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil (((unsigned _BitInt(BITINT_MAXWIDTH)) 1) << (BITINT_MAXWIDTH - 1)) != ((unsigned _BitInt(BITINT_MAXWIDTH)) 1) << (BITINT_MAXWIDTH - 1))
+    __builtin_abort ();
+  if (__builtin_stdc_bit_ceil (((unsigned _BitInt(BITINT_MAXWIDTH)) 512) << 405) != (((unsigned _BitInt(BITINT_MAXWIDTH)) 512) << 405)
+      || __builtin_stdc_bit_ceil (((unsigned _BitInt(BITINT_MAXWIDTH)) 513) << 405) != (((unsigned _BitInt(BITINT_MAXWIDTH)) 1024) << 405))
+    __builtin_abort ();
+#endif
+}
--- gcc/testsuite/gcc.dg/builtin-stdc-bit-2.c.jj	2023-11-18 19:55:50.956983185 +0100
+++ gcc/testsuite/gcc.dg/builtin-stdc-bit-2.c	2023-11-18 20:18:06.339579450 +0100
@@ -0,0 +1,40 @@ 
+/* { 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_bit_width (0.0f);	/* { dg-error "'__builtin_stdc_bit_width' operand not an integral type" } */
+  __builtin_stdc_bit_width (0.0);	/* { dg-error "'__builtin_stdc_bit_width' operand not an integral type" } */
+  __builtin_stdc_bit_width (0.0L);	/* { dg-error "'__builtin_stdc_bit_width' operand not an integral type" } */
+  __builtin_stdc_bit_width ((V) {});	/* { dg-error "'__builtin_stdc_bit_width' operand not an integral type" } */
+  __builtin_stdc_bit_width ((struct S) { 0 });	/* { dg-error "'__builtin_stdc_bit_width' operand not an integral type" } */
+  __builtin_stdc_bit_width ();		/* { dg-error "wrong number of arguments to '__builtin_stdc_bit_width'" } */
+  __builtin_stdc_bit_width (0U, 0U);	/* { dg-error "wrong number of arguments to '__builtin_stdc_bit_width'" } */
+  __builtin_stdc_bit_width ((_Bool) 0);	/* { dg-error "argument 1 in call to function '__builtin_clzg' has boolean type" } */
+  __builtin_stdc_bit_width ((enum E) E0);	/* { dg-error "argument 1 in call to function '__builtin_clzg' has enumerated type" } */
+  __builtin_stdc_bit_width (0);		/* { dg-error "argument 1 in call to function '__builtin_clzg' has signed type" } */
+  __builtin_stdc_bit_floor (0.0f);	/* { dg-error "'__builtin_stdc_bit_floor' operand not an integral type" } */
+  __builtin_stdc_bit_floor (0.0);	/* { dg-error "'__builtin_stdc_bit_floor' operand not an integral type" } */
+  __builtin_stdc_bit_floor (0.0L);	/* { dg-error "'__builtin_stdc_bit_floor' operand not an integral type" } */
+  __builtin_stdc_bit_floor ((V) {});	/* { dg-error "'__builtin_stdc_bit_floor' operand not an integral type" } */
+  __builtin_stdc_bit_floor ((struct S) { 0 });	/* { dg-error "'__builtin_stdc_bit_floor' operand not an integral type" } */
+  __builtin_stdc_bit_floor ();		/* { dg-error "wrong number of arguments to '__builtin_stdc_bit_floor'" } */
+  __builtin_stdc_bit_floor (0U, 0U);	/* { dg-error "wrong number of arguments to '__builtin_stdc_bit_floor'" } */
+  __builtin_stdc_bit_floor ((_Bool) 0);	/* { dg-error "argument 1 in call to function '__builtin_clzg' has boolean type" } */
+  __builtin_stdc_bit_floor ((enum E) E0);	/* { dg-error "argument 1 in call to function '__builtin_clzg' has enumerated type" } */
+  __builtin_stdc_bit_floor (0);		/* { dg-error "argument 1 in call to function '__builtin_clzg' has signed type" } */
+  __builtin_stdc_bit_ceil (0.0f);	/* { dg-error "'__builtin_stdc_bit_ceil' operand not an integral type" } */
+  __builtin_stdc_bit_ceil (0.0);	/* { dg-error "'__builtin_stdc_bit_ceil' operand not an integral type" } */
+  __builtin_stdc_bit_ceil (0.0L);	/* { dg-error "'__builtin_stdc_bit_ceil' operand not an integral type" } */
+  __builtin_stdc_bit_ceil ((V) {});	/* { dg-error "'__builtin_stdc_bit_ceil' operand not an integral type" } */
+  __builtin_stdc_bit_ceil ((struct S) { 0 });	/* { dg-error "'__builtin_stdc_bit_ceil' operand not an integral type" } */
+  __builtin_stdc_bit_ceil ();		/* { dg-error "wrong number of arguments to '__builtin_stdc_bit_ceil'" } */
+  __builtin_stdc_bit_ceil (0U, 0U);	/* { dg-error "wrong number of arguments to '__builtin_stdc_bit_ceil'" } */
+  __builtin_stdc_bit_ceil ((_Bool) 0);	/* { dg-error "argument 1 in call to function '__builtin_clzg' has boolean type" } */
+  __builtin_stdc_bit_ceil ((enum E) E0);	/* { dg-error "argument 1 in call to function '__builtin_clzg' has enumerated type" } */
+  __builtin_stdc_bit_ceil (0);		/* { dg-error "argument 1 in call to function '__builtin_clzg' has signed type" } */
+}