[10/12] C _BitInt support [PR102989]

Message ID ZNPaCv0aI4+M+I5E@tucnak
State Unresolved
Headers
Series GCC _BitInt support [PR102989] |

Checks

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

Commit Message

Jakub Jelinek Aug. 9, 2023, 6:25 p.m. UTC
  Hi!

This patch adds the C FE support, c-family support, small libcpp change
so that 123wb and 42uwb suffixes are handled plus glimits.h change
to define BITINT_MAXWIDTH macro.

The previous patches really do nothing without this, which enables
all the support.

2023-08-09  Jakub Jelinek  <jakub@redhat.com>

	PR c/102989
gcc/
	* glimits.h (BITINT_MAXWIDTH): Define if __BITINT_MAXWIDTH__ is
	predefined.
gcc/c-family/
	* c-common.cc (c_common_reswords): Add _BitInt as keyword.
	(c_common_signed_or_unsigned_type): Handle BITINT_TYPE.
	(check_builtin_function_arguments): Handle BITINT_TYPE like
	INTEGER_TYPE.
	(sync_resolve_size): Add ORIG_FORMAT argument.  If
	FETCH && !ORIG_FORMAT, type is BITINT_TYPE, return -1 if size isn't
	one of 1, 2, 4, 8 or 16 or if it is 16 but TImode is not supported.
	(atomic_bitint_fetch_using_cas_loop): New function.
	(resolve_overloaded_builtin): Adjust sync_resolve_size caller.  If
	-1 is returned, use atomic_bitint_fetch_using_cas_loop to lower it.
	Formatting fix.
	(keyword_begins_type_specifier): Handle RID_BITINT.
	* c-common.h (enum rid): Add RID_BITINT enumerator.
	* c-cppbuiltin.cc (c_cpp_builtins): For C call
	targetm.c.bitint_type_info and predefine __BITINT_MAXWIDTH__
	and for -fbuilding-libgcc also __LIBGCC_BITINT_LIMB_WIDTH__ and
	__LIBGCC_BITINT_ORDER__ macros if _BitInt is supported.
	* c-lex.cc (interpret_integer): Handle CPP_N_BITINT.
	* c-pretty-print.cc (c_pretty_printer::simple_type_specifier,
	c_pretty_printer::direct_abstract_declarator): Handle BITINT_TYPE.
	(pp_c_integer_constant): Handle printing of large precision wide_ints
	which would buffer overflow digit_buffer.
gcc/c/
	* c-convert.cc (c_convert): Handle BITINT_TYPE like INTEGER_TYPE.
	* c-decl.cc (check_bitfield_type_and_width): Allow BITINT_TYPE
	bit-fields.
	(finish_struct): Prefer to use BITINT_TYPE for BITINT_TYPE bit-fields
	if possible.
	(declspecs_add_type): Formatting fixes.  Handle cts_bitint.  Adjust
	for added union in *specs.  Handle RID_BITINT.
	(finish_declspecs): Handle cts_bitint.  Adjust for added union
	in *specs.
	* c-parser.cc (c_keyword_starts_typename, c_token_starts_declspecs,
	c_parser_declspecs, c_parser_gnu_attribute_any_word): Handle
	RID_BITINT.
	* c-tree.h (enum c_typespec_keyword): Mention _BitInt in comment.
	Add cts_bitint enumerator.
	(struct c_declspecs): Move int_n_idx and floatn_nx_idx into a union
	and add bitint_prec there as well.
	* c-typeck.cc (composite_type, c_common_type, comptypes_internal):
	Handle BITINT_TYPE.
	(perform_integral_promotions): Promote BITINT_TYPE bit-fields to
	their declared type.
	(build_array_ref, build_unary_op, build_conditional_expr,
	convert_for_assignment, digest_init, build_binary_op): Likewise.
libcpp/
	* expr.cc (interpret_int_suffix): Handle wb and WB suffixes.
	* include/cpplib.h (CPP_N_BITINT): Define.


	Jakub
  

Comments

Joseph Myers Sept. 5, 2023, 10:40 p.m. UTC | #1
On Wed, 9 Aug 2023, Jakub Jelinek via Gcc-patches wrote:

> Hi!
> 
> This patch adds the C FE support, c-family support, small libcpp change
> so that 123wb and 42uwb suffixes are handled plus glimits.h change
> to define BITINT_MAXWIDTH macro.
> 
> The previous patches really do nothing without this, which enables
> all the support.

Additional tests I think should be added (for things I expect should 
already work):

* Tests for BITINT_MAXWIDTH in <limits.h>.  Test that it's defined for 
C2x, but not defined for C11/C17 (the latter independent of whether the 
target has _BitInt support).  Test the value as well: _BitInt 
(BITINT_MAXWIDTH) should be OK (both signed and unsigned) but _BitInt 
(BITINT_MAXWIDTH + 1) should not be OK.  Also test that BITINT_MAXWIDTH >= 
ULLONG_MAX.

* Test _BitInt (N) where N is a constexpr variable or enum constant (I 
expect these should work - the required call to convert_lvalue_to_rvalue 
for constexpr to work is present - but I don't see such tests in the 
testsuite).

* Test that -funsigned-bitfields does not affect the signedness of _BitInt 
(N) bit-fields (the standard wording isn't entirely clear, but that's 
what's implemented in the patches).

* Test the errors for _Sat used with _BitInt (though such a test might not 
actually run at present because no target supports both features).

I looked at places in c-family/ and c/ that refer to INTEGER_TYPE to 
consider whether they should handle BITINT_TYPE and whether that matches 
what the patch does there.  I think the following places that don't handle 
it probably should (with appropriate testcases added for the relevant 
functionality, e.g. warnings) unless there is some specific reason in each 
case why that's unnecessary or incorrect.  c-pretty-print.cc: 
c_pretty_printer::direct_declarator and c_pretty_printer::declarator.  
c-warn.cc throughout.  Maybe c-ada-spec.cc, though it might be best to ask 
the Ada maintainers there.  c-common.cc: unsafe_conversion_p, 
c_common_truthvalue_conversion warnings, c_common_get_alias_set, 
check_builtin_function_arguments BUILT_IN_ASSUME_ALIGNED case.  
c-aux-info.cc (might need other support for generating appropriate names 
in output).  c-parser.cc:c_parser_omp_clause_schedule.  c-fold.cc 
throughout.  c-typeck.c: the build_conditional_expr case where one operand 
is EXCESS_PRECISION_EXPR; build_c_cast; convert_for_assignment checks for 
integer/pointer conversions.

In the parser, the syntax comment on c_parser_declspecs should have 
_BitInt syntax added.

> +		      error_at (loc, "%<_BitInt%> argument %qE is not "
> +				     "positive integer constant expression",

"is not positive" should be "is not a positive" here.

> --- gcc/c/c-typeck.cc.jj	2023-08-08 15:54:33.822622158 +0200
> +++ gcc/c/c-typeck.cc	2023-08-08 16:15:41.008877766 +0200
> @@ -413,10 +413,14 @@ composite_type (tree t1, tree t2)
>       the composite type.  */
>  
>    if (code1 == ENUMERAL_TYPE
> -      && (code2 == INTEGER_TYPE || code2 == BOOLEAN_TYPE))
> +      && (code2 == INTEGER_TYPE
> +	  || code2 == BOOLEAN_TYPE
> +	  || code2 == BITINT_TYPE))
>      return t1;
>    if (code2 == ENUMERAL_TYPE
> -      && (code1 == INTEGER_TYPE || code1 == BOOLEAN_TYPE))
> +      && (code1 == INTEGER_TYPE
> +	  || code1 == BOOLEAN_TYPE
> +	  || code1 == BITINT_TYPE))
>      return t2;

I think these changes to handle BITINT_TYPE are unnecessary (don't do 
anything), since enumerated types can't have bit-precise underlying type 
(this code is about making a predictable choice of the enumerated type as 
the composite of an enumerated type and its underlying integer type).

OK with those fixes.
  

Patch

--- gcc/glimits.h.jj	2023-08-08 15:54:34.481612931 +0200
+++ gcc/glimits.h	2023-08-08 16:12:02.321939910 +0200
@@ -157,6 +157,11 @@  see the files COPYING3 and COPYING.RUNTI
 # undef BOOL_WIDTH
 # define BOOL_WIDTH 1
 
+# ifdef __BITINT_MAXWIDTH__
+#  undef BITINT_MAXWIDTH
+#  define BITINT_MAXWIDTH __BITINT_MAXWIDTH__
+# endif
+
 # define __STDC_VERSION_LIMITS_H__	202311L
 #endif
 
--- gcc/c-family/c-common.cc.jj	2023-08-08 15:55:05.243182143 +0200
+++ gcc/c-family/c-common.cc	2023-08-08 16:19:29.102683903 +0200
@@ -349,6 +349,7 @@  const struct c_common_resword c_common_r
   { "_Alignas",		RID_ALIGNAS,   D_CONLY },
   { "_Alignof",		RID_ALIGNOF,   D_CONLY },
   { "_Atomic",		RID_ATOMIC,    D_CONLY },
+  { "_BitInt",		RID_BITINT,    D_CONLY },
   { "_Bool",		RID_BOOL,      D_CONLY },
   { "_Complex",		RID_COMPLEX,	0 },
   { "_Imaginary",	RID_IMAGINARY, D_CONLY },
@@ -2728,6 +2729,9 @@  c_common_signed_or_unsigned_type (int un
       || TYPE_UNSIGNED (type) == unsignedp)
     return type;
 
+  if (TREE_CODE (type) == BITINT_TYPE)
+    return build_bitint_type (TYPE_PRECISION (type), unsignedp);
+
 #define TYPE_OK(node)							    \
   (TYPE_MODE (type) == TYPE_MODE (node)					    \
    && TYPE_PRECISION (type) == TYPE_PRECISION (node))
@@ -6341,8 +6345,10 @@  check_builtin_function_arguments (locati
 	  code0 = TREE_CODE (TREE_TYPE (args[0]));
 	  code1 = TREE_CODE (TREE_TYPE (args[1]));
 	  if (!((code0 == REAL_TYPE && code1 == REAL_TYPE)
-		|| (code0 == REAL_TYPE && code1 == INTEGER_TYPE)
-		|| (code0 == INTEGER_TYPE && code1 == REAL_TYPE)))
+		|| (code0 == REAL_TYPE
+		    && (code1 == INTEGER_TYPE || code1 == BITINT_TYPE))
+		|| ((code0 == INTEGER_TYPE || code0 == BITINT_TYPE)
+		    && code1 == REAL_TYPE)))
 	    {
 	      error_at (loc, "non-floating-point arguments in call to "
 			"function %qE", fndecl);
@@ -7185,12 +7191,16 @@  speculation_safe_value_resolve_return (t
 /* A helper function for resolve_overloaded_builtin in resolving the
    overloaded __sync_ builtins.  Returns a positive power of 2 if the
    first operand of PARAMS is a pointer to a supported data type.
-   Returns 0 if an error is encountered.
+   Returns 0 if an error is encountered.  Return -1 for _BitInt
+   __atomic*fetch* with unsupported type which should be handled by
+   a cas loop.
    FETCH is true when FUNCTION is one of the _FETCH_OP_ or _OP_FETCH_
+   built-ins.  ORIG_FORMAT is for __sync_* rather than __atomic_*
    built-ins.  */
 
 static int
-sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch)
+sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch,
+		   bool orig_format)
 {
   /* Type of the argument.  */
   tree argtype;
@@ -7225,9 +7235,19 @@  sync_resolve_size (tree function, vec<tr
     goto incompatible;
 
   size = tree_to_uhwi (TYPE_SIZE_UNIT (type));
+  if (size == 16
+      && fetch
+      && !orig_format
+      && TREE_CODE (type) == BITINT_TYPE
+      && !targetm.scalar_mode_supported_p (TImode))
+    return -1;
+
   if (size == 1 || size == 2 || size == 4 || size == 8 || size == 16)
     return size;
 
+  if (fetch && !orig_format && TREE_CODE (type) == BITINT_TYPE)
+    return -1;
+
  incompatible:
   /* Issue the diagnostic only if the argument is valid, otherwise
      it would be redundant at best and could be misleading.  */
@@ -7844,6 +7864,223 @@  resolve_overloaded_atomic_store (locatio
 }
 
 
+/* Emit __atomic*fetch* on _BitInt which doesn't have a size of
+   1, 2, 4, 8 or 16 bytes using __atomic_compare_exchange loop.
+   ORIG_CODE is the DECL_FUNCTION_CODE of ORIG_FUNCTION and
+   ORIG_PARAMS arguments of the call.  */
+
+static tree
+atomic_bitint_fetch_using_cas_loop (location_t loc,
+				    enum built_in_function orig_code,
+				    tree orig_function,
+				    vec<tree, va_gc> *orig_params)
+{
+  enum tree_code code = ERROR_MARK;
+  bool return_old_p = false;
+  switch (orig_code)
+    {
+    case BUILT_IN_ATOMIC_ADD_FETCH_N:
+      code = PLUS_EXPR;
+      break;
+    case BUILT_IN_ATOMIC_SUB_FETCH_N:
+      code = MINUS_EXPR;
+      break;
+    case BUILT_IN_ATOMIC_AND_FETCH_N:
+      code = BIT_AND_EXPR;
+      break;
+    case BUILT_IN_ATOMIC_NAND_FETCH_N:
+      break;
+    case BUILT_IN_ATOMIC_XOR_FETCH_N:
+      code = BIT_XOR_EXPR;
+      break;
+    case BUILT_IN_ATOMIC_OR_FETCH_N:
+      code = BIT_IOR_EXPR;
+      break;
+    case BUILT_IN_ATOMIC_FETCH_ADD_N:
+      code = PLUS_EXPR;
+      return_old_p = true;
+      break;
+    case BUILT_IN_ATOMIC_FETCH_SUB_N:
+      code = MINUS_EXPR;
+      return_old_p = true;
+      break;
+    case BUILT_IN_ATOMIC_FETCH_AND_N:
+      code = BIT_AND_EXPR;
+      return_old_p = true;
+      break;
+    case BUILT_IN_ATOMIC_FETCH_NAND_N:
+      return_old_p = true;
+      break;
+    case BUILT_IN_ATOMIC_FETCH_XOR_N:
+      code = BIT_XOR_EXPR;
+      return_old_p = true;
+      break;
+    case BUILT_IN_ATOMIC_FETCH_OR_N:
+      code = BIT_IOR_EXPR;
+      return_old_p = true;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  if (orig_params->length () != 3)
+    {
+      if (orig_params->length () < 3)
+	error_at (loc, "too few arguments to function %qE", orig_function);
+      else
+	error_at (loc, "too many arguments to function %qE", orig_function);
+      return error_mark_node;
+    }
+
+  tree stmts = push_stmt_list ();
+
+  tree nonatomic_lhs_type = TREE_TYPE (TREE_TYPE ((*orig_params)[0]));
+  nonatomic_lhs_type = TYPE_MAIN_VARIANT (nonatomic_lhs_type);
+  gcc_assert (TREE_CODE (nonatomic_lhs_type) == BITINT_TYPE);
+
+  tree lhs_addr = (*orig_params)[0];
+  tree val = convert (nonatomic_lhs_type, (*orig_params)[1]);
+  tree model = convert (integer_type_node, (*orig_params)[2]);
+  if (TREE_SIDE_EFFECTS (lhs_addr))
+    {
+      tree var = create_tmp_var_raw (TREE_TYPE (lhs_addr));
+      lhs_addr = build4 (TARGET_EXPR, TREE_TYPE (lhs_addr), var, lhs_addr,
+			 NULL_TREE, NULL_TREE);
+      add_stmt (lhs_addr);
+    }
+  if (TREE_SIDE_EFFECTS (val))
+    {
+      tree var = create_tmp_var_raw (nonatomic_lhs_type);
+      val = build4 (TARGET_EXPR, nonatomic_lhs_type, var, val, NULL_TREE,
+		    NULL_TREE);
+      add_stmt (val);
+    }
+  if (TREE_SIDE_EFFECTS (model))
+    {
+      tree var = create_tmp_var_raw (integer_type_node);
+      model = build4 (TARGET_EXPR, integer_type_node, var, model, NULL_TREE,
+		      NULL_TREE);
+      add_stmt (model);
+    }
+
+  tree old = create_tmp_var_raw (nonatomic_lhs_type);
+  tree old_addr = build_unary_op (loc, ADDR_EXPR, old, false);
+  TREE_ADDRESSABLE (old) = 1;
+  suppress_warning (old);
+
+  tree newval = create_tmp_var_raw (nonatomic_lhs_type);
+  tree newval_addr = build_unary_op (loc, ADDR_EXPR, newval, false);
+  TREE_ADDRESSABLE (newval) = 1;
+  suppress_warning (newval);
+
+  tree loop_decl = create_artificial_label (loc);
+  tree loop_label = build1 (LABEL_EXPR, void_type_node, loop_decl);
+
+  tree done_decl = create_artificial_label (loc);
+  tree done_label = build1 (LABEL_EXPR, void_type_node, done_decl);
+
+  vec<tree, va_gc> *params;
+  vec_alloc (params, 6);
+
+  /* __atomic_load (addr, &old, SEQ_CST).  */
+  tree fndecl = builtin_decl_explicit (BUILT_IN_ATOMIC_LOAD);
+  params->quick_push (lhs_addr);
+  params->quick_push (old_addr);
+  params->quick_push (build_int_cst (integer_type_node, MEMMODEL_RELAXED));
+  tree func_call = resolve_overloaded_builtin (loc, fndecl, params);
+  if (func_call == NULL_TREE)
+    func_call = build_function_call_vec (loc, vNULL, fndecl, params, NULL);
+  old = build4 (TARGET_EXPR, nonatomic_lhs_type, old, func_call, NULL_TREE,
+		NULL_TREE);
+  add_stmt (old);
+  params->truncate (0);
+
+  /* loop:  */
+  add_stmt (loop_label);
+
+  /* newval = old + val;  */
+  tree rhs;
+  switch (code)
+    {
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+      if (!TYPE_OVERFLOW_WRAPS (nonatomic_lhs_type))
+	{
+	  tree utype
+	    = build_bitint_type (TYPE_PRECISION (nonatomic_lhs_type), 1);
+	  rhs = convert (nonatomic_lhs_type,
+			 build2_loc (loc, code, utype,
+				     convert (utype, old),
+				     convert (utype, val)));
+	}
+      else
+	rhs = build2_loc (loc, code, nonatomic_lhs_type, old, val);
+      break;
+    case BIT_AND_EXPR:
+    case BIT_IOR_EXPR:
+    case BIT_XOR_EXPR:
+      rhs = build2_loc (loc, code, nonatomic_lhs_type, old, val);
+      break;
+    case ERROR_MARK:
+      rhs = build2_loc (loc, BIT_AND_EXPR, nonatomic_lhs_type,
+			build1_loc (loc, BIT_NOT_EXPR,
+				    nonatomic_lhs_type, old), val);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  rhs = build4 (TARGET_EXPR, nonatomic_lhs_type, newval, rhs, NULL_TREE,
+		NULL_TREE);
+  SET_EXPR_LOCATION (rhs, loc);
+  add_stmt (rhs);
+
+  /* if (__atomic_compare_exchange (addr, &old, &new, false, model, model))
+       goto done;  */
+  fndecl = builtin_decl_explicit (BUILT_IN_ATOMIC_COMPARE_EXCHANGE);
+  params->quick_push (lhs_addr);
+  params->quick_push (old_addr);
+  params->quick_push (newval_addr);
+  params->quick_push (integer_zero_node);
+  params->quick_push (model);
+  if (tree_fits_uhwi_p (model)
+      && (tree_to_uhwi (model) == MEMMODEL_RELEASE
+	  || tree_to_uhwi (model) == MEMMODEL_ACQ_REL))
+    params->quick_push (build_int_cst (integer_type_node, MEMMODEL_RELAXED));
+  else
+    params->quick_push (model);
+  func_call = resolve_overloaded_builtin (loc, fndecl, params);
+  if (func_call == NULL_TREE)
+    func_call = build_function_call_vec (loc, vNULL, fndecl, params, NULL);
+
+  tree goto_stmt = build1 (GOTO_EXPR, void_type_node, done_decl);
+  SET_EXPR_LOCATION (goto_stmt, loc);
+
+  tree stmt
+    = build3 (COND_EXPR, void_type_node, func_call, goto_stmt, NULL_TREE);
+  SET_EXPR_LOCATION (stmt, loc);
+  add_stmt (stmt);
+
+  /* goto loop;  */
+  goto_stmt = build1 (GOTO_EXPR, void_type_node, loop_decl);
+  SET_EXPR_LOCATION (goto_stmt, loc);
+  add_stmt (goto_stmt);
+
+  /* done:  */
+  add_stmt (done_label);
+
+  tree ret = create_tmp_var_raw (nonatomic_lhs_type);
+  stmt = build2_loc (loc, MODIFY_EXPR, void_type_node, ret,
+		     return_old_p ? old : newval);
+  add_stmt (stmt);
+
+  /* Finish the compound statement.  */
+  stmts = pop_stmt_list (stmts);
+
+  return build4 (TARGET_EXPR, nonatomic_lhs_type, ret, stmts, NULL_TREE,
+		 NULL_TREE);
+}
+
+
 /* Some builtin functions are placeholders for other expressions.  This
    function should be called immediately after parsing the call expression
    before surrounding code has committed to the type of the expression.
@@ -8025,19 +8262,22 @@  resolve_overloaded_builtin (location_t l
 	/* The following are not _FETCH_OPs and must be accepted with
 	   pointers to _Bool (or C++ bool).  */
 	if (fetch_op)
-	  fetch_op =
-	    (orig_code != BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_N
-	     && orig_code != BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_N
-	     && orig_code != BUILT_IN_SYNC_LOCK_TEST_AND_SET_N
-	     && orig_code != BUILT_IN_SYNC_LOCK_RELEASE_N);
+	  fetch_op = (orig_code != BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_N
+		      && orig_code != BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_N
+		      && orig_code != BUILT_IN_SYNC_LOCK_TEST_AND_SET_N
+		      && orig_code != BUILT_IN_SYNC_LOCK_RELEASE_N);
 
-	int n = sync_resolve_size (function, params, fetch_op);
+	int n = sync_resolve_size (function, params, fetch_op, orig_format);
 	tree new_function, first_param, result;
 	enum built_in_function fncode;
 
 	if (n == 0)
 	  return error_mark_node;
 
+	if (n == -1)
+	  return atomic_bitint_fetch_using_cas_loop (loc, orig_code,
+						     function, params);
+
 	fncode = (enum built_in_function)((int)orig_code + exact_log2 (n) + 1);
 	new_function = builtin_decl_explicit (fncode);
 	if (!sync_resolve_params (loc, function, new_function, params,
@@ -8402,6 +8642,7 @@  keyword_begins_type_specifier (enum rid
     case RID_FRACT:
     case RID_ACCUM:
     case RID_BOOL:
+    case RID_BITINT:
     case RID_WCHAR:
     case RID_CHAR8:
     case RID_CHAR16:
--- gcc/c-family/c-common.h.jj	2023-08-08 15:55:05.244182129 +0200
+++ gcc/c-family/c-common.h	2023-08-08 16:12:02.331939770 +0200
@@ -101,7 +101,7 @@  enum rid
   RID_ENUM,    RID_STRUCT, RID_UNION,    RID_IF,     RID_ELSE,
   RID_WHILE,   RID_DO,     RID_FOR,      RID_SWITCH, RID_CASE,
   RID_DEFAULT, RID_BREAK,  RID_CONTINUE, RID_RETURN, RID_GOTO,
-  RID_SIZEOF,
+  RID_SIZEOF,  RID_BITINT,
 
   /* C extensions */
   RID_ASM,       RID_TYPEOF,   RID_TYPEOF_UNQUAL, RID_ALIGNOF,  RID_ATTRIBUTE,
--- gcc/c-family/c-cppbuiltin.cc.jj	2023-08-08 15:54:33.448627395 +0200
+++ gcc/c-family/c-cppbuiltin.cc	2023-08-08 16:12:02.331939770 +0200
@@ -1190,6 +1190,29 @@  c_cpp_builtins (cpp_reader *pfile)
   builtin_define_type_width ("__PTRDIFF_WIDTH__", ptrdiff_type_node, NULL_TREE);
   builtin_define_type_width ("__SIZE_WIDTH__", size_type_node, NULL_TREE);
 
+  if (!c_dialect_cxx ())
+    {
+      struct bitint_info info;
+      /* For now, restrict __BITINT_MAXWIDTH__ to what can be represented in
+	 wide_int and widest_int.  */
+      if (targetm.c.bitint_type_info (WIDE_INT_MAX_PRECISION - 1, &info))
+	{
+	  cpp_define_formatted (pfile, "__BITINT_MAXWIDTH__=%d",
+				(int) WIDE_INT_MAX_PRECISION - 1);
+	  if (flag_building_libgcc)
+	    {
+	      scalar_int_mode limb_mode
+		= as_a <scalar_int_mode> (info.limb_mode);
+	      cpp_define_formatted (pfile, "__LIBGCC_BITINT_LIMB_WIDTH__=%d",
+				    (int) GET_MODE_PRECISION (limb_mode));
+	      cpp_define_formatted (pfile, "__LIBGCC_BITINT_ORDER__=%s",
+				    info.big_endian
+				    ? "__ORDER_BIG_ENDIAN__"
+				    : "__ORDER_LITTLE_ENDIAN__");
+	    }
+	}
+    }
+
   if (c_dialect_cxx ())
     for (i = 0; i < NUM_INT_N_ENTS; i ++)
       if (int_n_enabled_p[i])
--- gcc/c-family/c-lex.cc.jj	2023-08-08 15:55:05.245182116 +0200
+++ gcc/c-family/c-lex.cc	2023-08-08 16:12:02.332939756 +0200
@@ -837,6 +837,170 @@  interpret_integer (const cpp_token *toke
 
   *overflow = OT_NONE;
 
+  if (UNLIKELY (flags & CPP_N_BITINT))
+    {
+      unsigned int suffix_len = 2 + ((flags & CPP_N_UNSIGNED) ? 1 : 0);
+      int max_bits_per_digit = 4; // ceil (log2 (10))
+      unsigned int prefix_len = 0;
+      bool hex = false;
+      const int bitint_maxwidth = WIDE_INT_MAX_PRECISION - 1;
+      if ((flags & CPP_N_RADIX) == CPP_N_OCTAL)
+	{
+	  max_bits_per_digit = 3;
+	  prefix_len = 1;
+	}
+      else if ((flags & CPP_N_RADIX) == CPP_N_HEX)
+	{
+	  max_bits_per_digit = 4;
+	  prefix_len = 2;
+	  hex = true;
+	}
+      else if ((flags & CPP_N_RADIX) == CPP_N_BINARY)
+	{
+	  max_bits_per_digit = 1;
+	  prefix_len = 2;
+	}
+      int max_digits
+	= TYPE_PRECISION (intmax_type_node) >> max_bits_per_digit;
+      const int max_buf = 128;
+      if (max_digits > max_buf)
+	max_digits = max_buf;
+
+      widest_int wval;
+      unsigned int prec;
+      gcc_checking_assert (token->val.str.len > prefix_len + suffix_len
+			   || token->val.str.len == 1 + suffix_len);
+      if (token->val.str.len - (prefix_len + suffix_len)
+	  <= (unsigned) max_digits)
+	{
+	  integer = cpp_interpret_integer (parse_in, token,
+					   (flags & CPP_N_RADIX)
+					   | CPP_N_UNSIGNED);
+	  ival[0] = integer.low;
+	  ival[1] = integer.high;
+	  ival[2] = 0;
+	  wval = widest_int::from_array (ival, 3);
+	}
+      else
+	{
+	  unsigned char buf[3 + max_buf];
+	  memcpy (buf, token->val.str.text, prefix_len);
+	  wval = 0U;
+	  const unsigned char *p = token->val.str.text + prefix_len;
+	  cpp_token tok = *token;
+	  tok.val.str.text = buf;
+	  if (!prefix_len)
+	    max_digits = 19;
+	  do
+	    {
+	      unsigned char *q = buf + prefix_len;
+	      do
+		{
+		  unsigned char c = *p++;
+		  if (ISDIGIT (c) || (hex && ISXDIGIT (c)))
+		    {
+		      *q++ = c;
+		      if (q == buf + prefix_len + max_digits)
+			break;
+		    }
+		  else if (c != '\'')
+		    {
+		      --p;
+		      break;
+		    }
+		}
+	      while (1);
+	      if (q == buf + prefix_len)
+		break;
+	      else
+		{
+		  wi::overflow_type wioverflow;
+		  *q = '\0';
+		  tok.val.str.len = q - buf;
+		  if (wval == 0)
+		    ;
+		  else if (prefix_len)
+		    {
+		      prec = wi::min_precision (wval, UNSIGNED);
+		      unsigned HOST_WIDE_INT shift
+			= (tok.val.str.len - prefix_len) * max_bits_per_digit;
+		      if (prec + shift > bitint_maxwidth)
+			goto bitint_overflow;
+		      wval = wi::lshift (wval, shift);
+		    }
+		  else
+		    {
+		      static unsigned HOST_WIDE_INT tens[]
+			= { 1U, 10U, 100U, 1000U,
+			    HOST_WIDE_INT_UC (10000),
+			    HOST_WIDE_INT_UC (100000),
+			    HOST_WIDE_INT_UC (1000000),
+			    HOST_WIDE_INT_UC (10000000),
+			    HOST_WIDE_INT_UC (100000000),
+			    HOST_WIDE_INT_UC (1000000000),
+			    HOST_WIDE_INT_UC (10000000000),
+			    HOST_WIDE_INT_UC (100000000000),
+			    HOST_WIDE_INT_UC (1000000000000),
+			    HOST_WIDE_INT_UC (10000000000000),
+			    HOST_WIDE_INT_UC (100000000000000),
+			    HOST_WIDE_INT_UC (1000000000000000),
+			    HOST_WIDE_INT_UC (10000000000000000),
+			    HOST_WIDE_INT_UC (100000000000000000),
+			    HOST_WIDE_INT_UC (1000000000000000000),
+			    HOST_WIDE_INT_UC (10000000000000000000) };
+		      widest_int ten = tens[q - buf];
+		      wval = wi::umul (wval, ten, &wioverflow);
+		      if (wioverflow)
+			goto bitint_overflow;
+		    }
+		  integer = cpp_interpret_integer (parse_in, &tok,
+						   (flags & CPP_N_RADIX)
+						   | CPP_N_UNSIGNED);
+		  ival[0] = integer.low;
+		  ival[1] = integer.high;
+		  ival[2] = 0;
+		  if (prefix_len)
+		    wval = wval + widest_int::from_array (ival, 3);
+		  else
+		    {
+		      widest_int addend = widest_int::from_array (ival, 3);
+		      wval = wi::add (wval, addend, UNSIGNED, &wioverflow);
+		      if (wioverflow)
+			goto bitint_overflow;
+		    }
+		}
+	    }
+	  while (1);
+	}
+
+      prec = wi::min_precision (wval, UNSIGNED);
+      if (prec == 0)
+	prec = 1;
+      if ((flags & CPP_N_UNSIGNED) == 0)
+	++prec;
+      if (prec > bitint_maxwidth)
+	{
+	bitint_overflow:
+	  if ((flags & CPP_N_UNSIGNED) != 0)
+	    error ("integer constant is too large for "
+		   "%<unsigned _BitInt(%d)%> type", bitint_maxwidth);
+	  else
+	    error ("integer constant is too large for "
+		   "%<_BitInt(%d)%> type", bitint_maxwidth);
+	  return integer_zero_node;
+	}
+
+      struct bitint_info info;
+      if (!targetm.c.bitint_type_info (prec, &info))
+	{
+	  sorry ("%<_BitInt(%d)%> is not supported on this target", prec);
+	  return integer_zero_node;
+	}
+
+      type = build_bitint_type (prec, (flags & CPP_N_UNSIGNED) != 0);
+      return wide_int_to_tree (type, wval);
+    }
+
   integer = cpp_interpret_integer (parse_in, token, flags);
   if (integer.overflow)
     *overflow = OT_OVERFLOW;
--- gcc/c-family/c-pretty-print.cc.jj	2023-08-08 15:54:33.553625925 +0200
+++ gcc/c-family/c-pretty-print.cc	2023-08-08 16:12:02.332939756 +0200
@@ -399,6 +399,23 @@  c_pretty_printer::simple_type_specifier
 	}
       break;
 
+    case BITINT_TYPE:
+      if (TYPE_NAME (t))
+	{
+	  t = TYPE_NAME (t);
+	  simple_type_specifier (t);
+	}
+      else
+	{
+	  int prec = TYPE_PRECISION (t);
+	  if (TYPE_UNSIGNED (t))
+	    pp_c_ws_string (this, "unsigned");
+	  pp_c_ws_string (this, "_BitInt(");;
+	  pp_decimal_int (this, prec);
+	  pp_right_paren (this);
+	}
+      break;
+
     case TYPE_DECL:
       if (DECL_NAME (t))
 	id_expression (t);
@@ -688,6 +705,7 @@  c_pretty_printer::direct_abstract_declar
     case REAL_TYPE:
     case FIXED_POINT_TYPE:
     case ENUMERAL_TYPE:
+    case BITINT_TYPE:
     case RECORD_TYPE:
     case UNION_TYPE:
     case VECTOR_TYPE:
@@ -1019,8 +1037,18 @@  pp_c_integer_constant (c_pretty_printer
 	  pp_minus (pp);
 	  wi = -wi;
 	}
-      print_hex (wi, pp_buffer (pp)->digit_buffer);
-      pp_string (pp, pp_buffer (pp)->digit_buffer);
+      unsigned int prec = wi.get_precision ();
+      if ((prec + 3) / 4 > sizeof (pp_buffer (pp)->digit_buffer) - 3)
+	{
+	  char *buf = XALLOCAVEC (char, (prec + 3) / 4 + 3);
+	  print_hex (wi, buf);
+	  pp_string (pp, buf);
+	}
+      else
+	{
+	  print_hex (wi, pp_buffer (pp)->digit_buffer);
+	  pp_string (pp, pp_buffer (pp)->digit_buffer);
+	}
     }
 }
 
--- gcc/c/c-convert.cc.jj	2023-08-08 15:54:33.642624678 +0200
+++ gcc/c/c-convert.cc	2023-08-08 16:12:02.332939756 +0200
@@ -117,6 +117,7 @@  c_convert (tree type, tree expr, bool in
       gcc_fallthrough ();
 
     case INTEGER_TYPE:
+    case BITINT_TYPE:
       if (sanitize_flags_p (SANITIZE_FLOAT_CAST)
 	  && current_function_decl != NULL_TREE
 	  && SCALAR_FLOAT_TYPE_P (TREE_TYPE (expr))
--- gcc/c/c-decl.cc.jj	2023-08-08 15:55:05.277181667 +0200
+++ gcc/c/c-decl.cc	2023-08-08 16:15:41.006877794 +0200
@@ -6390,7 +6390,8 @@  check_bitfield_type_and_width (location_
   /* Detect invalid bit-field type.  */
   if (TREE_CODE (*type) != INTEGER_TYPE
       && TREE_CODE (*type) != BOOLEAN_TYPE
-      && TREE_CODE (*type) != ENUMERAL_TYPE)
+      && TREE_CODE (*type) != ENUMERAL_TYPE
+      && TREE_CODE (*type) != BITINT_TYPE)
     {
       error_at (loc, "bit-field %qs has invalid type", name);
       *type = unsigned_type_node;
@@ -9330,8 +9331,14 @@  finish_struct (location_t loc, tree t, t
 	  tree type = TREE_TYPE (field);
 	  if (width != TYPE_PRECISION (type))
 	    {
-	      TREE_TYPE (field)
-		= c_build_bitfield_integer_type (width, TYPE_UNSIGNED (type));
+	      if (TREE_CODE (type) == BITINT_TYPE
+		  && (width > 1 || TYPE_UNSIGNED (type)))
+		TREE_TYPE (field)
+		  = build_bitint_type (width, TYPE_UNSIGNED (type));
+	      else
+		TREE_TYPE (field)
+		  = c_build_bitfield_integer_type (width,
+						   TYPE_UNSIGNED (type));
 	      SET_DECL_MODE (field, TYPE_MODE (TREE_TYPE (field)));
 	    }
 	  DECL_INITIAL (field) = NULL_TREE;
@@ -11546,14 +11553,18 @@  declspecs_add_type (location_t loc, stru
 			  ("both %<long%> and %<void%> in "
 			   "declaration specifiers"));
 	      else if (specs->typespec_word == cts_int_n)
-		  error_at (loc,
-			    ("both %<long%> and %<__int%d%> in "
-			     "declaration specifiers"),
-			    int_n_data[specs->int_n_idx].bitsize);
+		error_at (loc,
+			  ("both %<long%> and %<__int%d%> in "
+			   "declaration specifiers"),
+			  int_n_data[specs->u.int_n_idx].bitsize);
 	      else if (specs->typespec_word == cts_bool)
 		error_at (loc,
 			  ("both %<long%> and %<_Bool%> in "
 			   "declaration specifiers"));
+	      else if (specs->typespec_word == cts_bitint)
+		error_at (loc,
+			  ("both %<long%> and %<_BitInt%> in "
+			   "declaration specifiers"));
 	      else if (specs->typespec_word == cts_char)
 		error_at (loc,
 			  ("both %<long%> and %<char%> in "
@@ -11566,8 +11577,8 @@  declspecs_add_type (location_t loc, stru
 		error_at (loc,
 			  ("both %<long%> and %<_Float%d%s%> in "
 			   "declaration specifiers"),
-			  floatn_nx_types[specs->floatn_nx_idx].n,
-			  (floatn_nx_types[specs->floatn_nx_idx].extended
+			  floatn_nx_types[specs->u.floatn_nx_idx].n,
+			  (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			   ? "x"
 			   : ""));
 	      else if (specs->typespec_word == cts_dfloat32)
@@ -11606,11 +11617,15 @@  declspecs_add_type (location_t loc, stru
 		error_at (loc,
 			  ("both %<short%> and %<__int%d%> in "
 			   "declaration specifiers"),
-			  int_n_data[specs->int_n_idx].bitsize);
+			  int_n_data[specs->u.int_n_idx].bitsize);
 	      else if (specs->typespec_word == cts_bool)
 		error_at (loc,
 			  ("both %<short%> and %<_Bool%> in "
 			   "declaration specifiers"));
+	      else if (specs->typespec_word == cts_bitint)
+		error_at (loc,
+			  ("both %<short%> and %<_BitInt%> in "
+			   "declaration specifiers"));
 	      else if (specs->typespec_word == cts_char)
 		error_at (loc,
 			  ("both %<short%> and %<char%> in "
@@ -11627,8 +11642,8 @@  declspecs_add_type (location_t loc, stru
 		error_at (loc,
 			  ("both %<short%> and %<_Float%d%s%> in "
 			   "declaration specifiers"),
-			  floatn_nx_types[specs->floatn_nx_idx].n,
-			  (floatn_nx_types[specs->floatn_nx_idx].extended
+			  floatn_nx_types[specs->u.floatn_nx_idx].n,
+			  (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			   ? "x"
 			   : ""));
 	      else if (specs->typespec_word == cts_dfloat32)
@@ -11679,8 +11694,8 @@  declspecs_add_type (location_t loc, stru
 		error_at (loc,
 			  ("both %<signed%> and %<_Float%d%s%> in "
 			   "declaration specifiers"),
-			  floatn_nx_types[specs->floatn_nx_idx].n,
-			  (floatn_nx_types[specs->floatn_nx_idx].extended
+			  floatn_nx_types[specs->u.floatn_nx_idx].n,
+			  (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			   ? "x"
 			   : ""));
 	      else if (specs->typespec_word == cts_dfloat32)
@@ -11731,8 +11746,8 @@  declspecs_add_type (location_t loc, stru
 		error_at (loc,
 			  ("both %<unsigned%> and %<_Float%d%s%> in "
 			   "declaration specifiers"),
-			  floatn_nx_types[specs->floatn_nx_idx].n,
-			  (floatn_nx_types[specs->floatn_nx_idx].extended
+			  floatn_nx_types[specs->u.floatn_nx_idx].n,
+			  (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			   ? "x"
 			   : ""));
               else if (specs->typespec_word == cts_dfloat32)
@@ -11770,6 +11785,10 @@  declspecs_add_type (location_t loc, stru
 		error_at (loc,
 			  ("both %<complex%> and %<_Bool%> in "
 			   "declaration specifiers"));
+	      else if (specs->typespec_word == cts_bitint)
+		error_at (loc,
+			  ("both %<complex%> and %<_BitInt%> in "
+			   "declaration specifiers"));
               else if (specs->typespec_word == cts_dfloat32)
 		error_at (loc,
 			  ("both %<complex%> and %<_Decimal32%> in "
@@ -11809,7 +11828,7 @@  declspecs_add_type (location_t loc, stru
 		  error_at (loc,
 			    ("both %<_Sat%> and %<__int%d%> in "
 			     "declaration specifiers"),
-			    int_n_data[specs->int_n_idx].bitsize);
+			    int_n_data[specs->u.int_n_idx].bitsize);
 	        }
 	      else if (specs->typespec_word == cts_auto_type)
 		error_at (loc,
@@ -11823,6 +11842,10 @@  declspecs_add_type (location_t loc, stru
 		error_at (loc,
 			  ("both %<_Sat%> and %<_Bool%> in "
 			   "declaration specifiers"));
+	      else if (specs->typespec_word == cts_bitint)
+		error_at (loc,
+			  ("both %<_Sat%> and %<_BitInt%> in "
+			   "declaration specifiers"));
 	      else if (specs->typespec_word == cts_char)
 		error_at (loc,
 			  ("both %<_Sat%> and %<char%> in "
@@ -11843,8 +11866,8 @@  declspecs_add_type (location_t loc, stru
 		error_at (loc,
 			  ("both %<_Sat%> and %<_Float%d%s%> in "
 			   "declaration specifiers"),
-			  floatn_nx_types[specs->floatn_nx_idx].n,
-			  (floatn_nx_types[specs->floatn_nx_idx].extended
+			  floatn_nx_types[specs->u.floatn_nx_idx].n,
+			  (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			   ? "x"
 			   : ""));
               else if (specs->typespec_word == cts_dfloat32)
@@ -11882,7 +11905,7 @@  declspecs_add_type (location_t loc, stru
 	{
 	  /* "void", "_Bool", "char", "int", "float", "double",
 	     "_FloatN", "_FloatNx", "_Decimal32", "__intN",
-	     "_Decimal64", "_Decimal128", "_Fract", "_Accum" or
+	     "_Decimal64", "_Decimal128", "_Fract", "_Accum", "_BitInt(N)" or
 	     "__auto_type".  */
 	  if (specs->typespec_word != cts_none)
 	    {
@@ -11927,7 +11950,7 @@  declspecs_add_type (location_t loc, stru
 	    case RID_INT_N_1:
 	    case RID_INT_N_2:
 	    case RID_INT_N_3:
-	      specs->int_n_idx = i - RID_INT_N_0;
+	      specs->u.int_n_idx = i - RID_INT_N_0;
 	      if (!in_system_header_at (input_location)
 		  /* If the INT_N type ends in "__", and so is of the format
 		     "__intN__", don't pedwarn.  */
@@ -11935,29 +11958,29 @@  declspecs_add_type (location_t loc, stru
 			       + (IDENTIFIER_LENGTH (type) - 2), "__", 2) != 0))
 		pedwarn (loc, OPT_Wpedantic,
 			 "ISO C does not support %<__int%d%> types",
-			 int_n_data[specs->int_n_idx].bitsize);
+			 int_n_data[specs->u.int_n_idx].bitsize);
 
 	      if (specs->long_p)
 		error_at (loc,
 			  ("both %<__int%d%> and %<long%> in "
 			   "declaration specifiers"),
-			  int_n_data[specs->int_n_idx].bitsize);
+			  int_n_data[specs->u.int_n_idx].bitsize);
 	      else if (specs->saturating_p)
 		error_at (loc,
 			  ("both %<_Sat%> and %<__int%d%> in "
 			   "declaration specifiers"),
-			  int_n_data[specs->int_n_idx].bitsize);
+			  int_n_data[specs->u.int_n_idx].bitsize);
 	      else if (specs->short_p)
 		error_at (loc,
 			  ("both %<__int%d%> and %<short%> in "
 			   "declaration specifiers"),
-			  int_n_data[specs->int_n_idx].bitsize);
-	      else if (! int_n_enabled_p[specs->int_n_idx])
+			  int_n_data[specs->u.int_n_idx].bitsize);
+	      else if (! int_n_enabled_p[specs->u.int_n_idx])
 		{
 		  specs->typespec_word = cts_int_n;
 		  error_at (loc,
 			    "%<__int%d%> is not supported on this target",
-			    int_n_data[specs->int_n_idx].bitsize);
+			    int_n_data[specs->u.int_n_idx].bitsize);
 		}
 	      else
 		{
@@ -12115,12 +12138,12 @@  declspecs_add_type (location_t loc, stru
 		}
 	      return specs;
 	    CASE_RID_FLOATN_NX:
-	      specs->floatn_nx_idx = i - RID_FLOATN_NX_FIRST;
+	      specs->u.floatn_nx_idx = i - RID_FLOATN_NX_FIRST;
 	      if (!in_system_header_at (input_location))
 		pedwarn (loc, OPT_Wpedantic,
 			 "ISO C does not support the %<_Float%d%s%> type",
-			 floatn_nx_types[specs->floatn_nx_idx].n,
-			 (floatn_nx_types[specs->floatn_nx_idx].extended
+			 floatn_nx_types[specs->u.floatn_nx_idx].n,
+			 (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			  ? "x"
 			  : ""));
 
@@ -12128,49 +12151,49 @@  declspecs_add_type (location_t loc, stru
 		error_at (loc,
 			  ("both %<long%> and %<_Float%d%s%> in "
 			   "declaration specifiers"),
-			  floatn_nx_types[specs->floatn_nx_idx].n,
-			  (floatn_nx_types[specs->floatn_nx_idx].extended
+			  floatn_nx_types[specs->u.floatn_nx_idx].n,
+			  (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			   ? "x"
 			   : ""));
 	      else if (specs->short_p)
 		error_at (loc,
 			  ("both %<short%> and %<_Float%d%s%> in "
 			   "declaration specifiers"),
-			  floatn_nx_types[specs->floatn_nx_idx].n,
-			  (floatn_nx_types[specs->floatn_nx_idx].extended
+			  floatn_nx_types[specs->u.floatn_nx_idx].n,
+			  (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			   ? "x"
 			   : ""));
 	      else if (specs->signed_p)
 		error_at (loc,
 			  ("both %<signed%> and %<_Float%d%s%> in "
 			   "declaration specifiers"),
-			  floatn_nx_types[specs->floatn_nx_idx].n,
-			  (floatn_nx_types[specs->floatn_nx_idx].extended
+			  floatn_nx_types[specs->u.floatn_nx_idx].n,
+			  (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			   ? "x"
 			   : ""));
 	      else if (specs->unsigned_p)
 		error_at (loc,
 			  ("both %<unsigned%> and %<_Float%d%s%> in "
 			   "declaration specifiers"),
-			  floatn_nx_types[specs->floatn_nx_idx].n,
-			  (floatn_nx_types[specs->floatn_nx_idx].extended
+			  floatn_nx_types[specs->u.floatn_nx_idx].n,
+			  (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			   ? "x"
 			   : ""));
 	      else if (specs->saturating_p)
 		error_at (loc,
 			  ("both %<_Sat%> and %<_Float%d%s%> in "
 			   "declaration specifiers"),
-			  floatn_nx_types[specs->floatn_nx_idx].n,
-			  (floatn_nx_types[specs->floatn_nx_idx].extended
+			  floatn_nx_types[specs->u.floatn_nx_idx].n,
+			  (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			   ? "x"
 			   : ""));
-	      else if (FLOATN_NX_TYPE_NODE (specs->floatn_nx_idx) == NULL_TREE)
+	      else if (FLOATN_NX_TYPE_NODE (specs->u.floatn_nx_idx) == NULL_TREE)
 		{
 		  specs->typespec_word = cts_floatn_nx;
 		  error_at (loc,
 			    "%<_Float%d%s%> is not supported on this target",
-			    floatn_nx_types[specs->floatn_nx_idx].n,
-			    (floatn_nx_types[specs->floatn_nx_idx].extended
+			    floatn_nx_types[specs->u.floatn_nx_idx].n,
+			    (floatn_nx_types[specs->u.floatn_nx_idx].extended
 			     ? "x"
 			     : ""));
 		}
@@ -12267,6 +12290,63 @@  declspecs_add_type (location_t loc, stru
 	      pedwarn (loc, OPT_Wpedantic,
 		       "ISO C does not support fixed-point types");
 	      return specs;
+	    case RID_BITINT:
+	      if (specs->long_p)
+		error_at (loc,
+			  ("both %<long%> and %<_BitInt%> in "
+			   "declaration specifiers"));
+	      else if (specs->short_p)
+		error_at (loc,
+			  ("both %<short%> and %<_BitInt%> in "
+			   "declaration specifiers"));
+	      else if (specs->complex_p)
+		error_at (loc,
+			  ("both %<complex%> and %<_BitInt%> in "
+			   "declaration specifiers"));
+	      else if (specs->saturating_p)
+		error_at (loc,
+			  ("both %<_Sat%> and %<_BitInt%> in "
+			   "declaration specifiers"));
+	      else
+		{
+		  specs->typespec_word = cts_bitint;
+		  specs->locations[cdw_typespec] = loc;
+		  specs->u.bitint_prec = -1;
+		  if (error_operand_p (spec.expr))
+		    return specs;
+		  if (TREE_CODE (spec.expr) != INTEGER_CST
+		      || !INTEGRAL_TYPE_P (TREE_TYPE (spec.expr)))
+		    {
+		      error_at (loc, "%<_BitInt%> argument is not an integer "
+				     "constant expression");
+		      return specs;
+		    }
+		  if (tree_int_cst_sgn (spec.expr) <= 0)
+		    {
+		      error_at (loc, "%<_BitInt%> argument %qE is not "
+				     "positive integer constant expression",
+				spec.expr);
+		      return specs;
+		    }
+		  if (wi::to_widest (spec.expr) > WIDE_INT_MAX_PRECISION - 1)
+		    {
+		      error_at (loc, "%<_BitInt%> argument %qE is larger than "
+				     "%<BITINT_MAXWIDTH%> %qd",
+				spec.expr, (int) WIDE_INT_MAX_PRECISION - 1);
+		      return specs;
+		    }
+		  specs->u.bitint_prec = tree_to_uhwi (spec.expr);
+		  struct bitint_info info;
+		  if (!targetm.c.bitint_type_info (specs->u.bitint_prec,
+						   &info))
+		    {
+		      sorry_at (loc, "%<_BitInt(%d)%> is not supported on "
+				     "this target", specs->u.bitint_prec);
+		      specs->u.bitint_prec = -1;
+		      return specs;
+		    }
+		}
+	      return specs;
 	    default:
 	      /* ObjC reserved word "id", handled below.  */
 	      break;
@@ -12668,12 +12748,12 @@  finish_declspecs (struct c_declspecs *sp
     case cts_int_n:
       gcc_assert (!specs->long_p && !specs->short_p && !specs->long_long_p);
       gcc_assert (!(specs->signed_p && specs->unsigned_p));
-      if (! int_n_enabled_p[specs->int_n_idx])
+      if (! int_n_enabled_p[specs->u.int_n_idx])
 	specs->type = integer_type_node;
       else
 	specs->type = (specs->unsigned_p
-		       ? int_n_trees[specs->int_n_idx].unsigned_type
-		       : int_n_trees[specs->int_n_idx].signed_type);
+		       ? int_n_trees[specs->u.int_n_idx].unsigned_type
+		       : int_n_trees[specs->u.int_n_idx].signed_type);
       if (specs->complex_p)
 	{
 	  pedwarn (specs->locations[cdw_complex], OPT_Wpedantic,
@@ -12733,12 +12813,12 @@  finish_declspecs (struct c_declspecs *sp
     case cts_floatn_nx:
       gcc_assert (!specs->long_p && !specs->short_p
 		  && !specs->signed_p && !specs->unsigned_p);
-      if (FLOATN_NX_TYPE_NODE (specs->floatn_nx_idx) == NULL_TREE)
+      if (FLOATN_NX_TYPE_NODE (specs->u.floatn_nx_idx) == NULL_TREE)
 	specs->type = integer_type_node;
       else if (specs->complex_p)
-	specs->type = COMPLEX_FLOATN_NX_TYPE_NODE (specs->floatn_nx_idx);
+	specs->type = COMPLEX_FLOATN_NX_TYPE_NODE (specs->u.floatn_nx_idx);
       else
-	specs->type = FLOATN_NX_TYPE_NODE (specs->floatn_nx_idx);
+	specs->type = FLOATN_NX_TYPE_NODE (specs->u.floatn_nx_idx);
       break;
     case cts_dfloat32:
     case cts_dfloat64:
@@ -12840,6 +12920,22 @@  finish_declspecs (struct c_declspecs *sp
 			  : accum_type_node;
 	}
       break;
+    case cts_bitint:
+      gcc_assert (!specs->long_p && !specs->short_p
+		  && !specs->complex_p);
+      if (!specs->unsigned_p && specs->u.bitint_prec == 1)
+	{
+	  error_at (specs->locations[cdw_typespec],
+		    "%<signed _BitInt%> argument must be at least 2");
+	  specs->type = integer_type_node;
+	  break;
+	}
+      if (specs->u.bitint_prec == -1)
+	specs->type = integer_type_node;
+      else
+	specs->type = build_bitint_type (specs->u.bitint_prec,
+					 specs->unsigned_p);
+      break;
     default:
       gcc_unreachable ();
     }
--- gcc/c/c-parser.cc.jj	2023-08-08 15:55:05.286181541 +0200
+++ gcc/c/c-parser.cc	2023-08-08 16:12:02.336939700 +0200
@@ -580,6 +580,7 @@  c_keyword_starts_typename (enum rid keyw
     case RID_DFLOAT128:
     CASE_RID_FLOATN_NX:
     case RID_BOOL:
+    case RID_BITINT:
     case RID_ENUM:
     case RID_STRUCT:
     case RID_UNION:
@@ -783,6 +784,7 @@  c_token_starts_declspecs (c_token *token
 	case RID_DFLOAT128:
 	CASE_RID_FLOATN_NX:
 	case RID_BOOL:
+	case RID_BITINT:
 	case RID_ENUM:
 	case RID_STRUCT:
 	case RID_UNION:
@@ -3358,6 +3360,30 @@  c_parser_declspecs (c_parser *parser, st
 	  t = c_parser_typeof_specifier (parser);
 	  declspecs_add_type (loc, specs, t);
 	  break;
+	case RID_BITINT:
+	  if (!typespec_ok)
+	    goto out;
+	  else
+	    {
+	      attrs_ok = true;
+	      seen_type = true;
+	      t.kind = ctsk_resword;
+	      t.spec = c_parser_peek_token (parser)->value;
+	      t.expr = error_mark_node;
+	      t.expr_const_operands = true;
+	      t.has_enum_type_specifier = false;
+	      c_parser_consume_token (parser);
+	      matching_parens parens;
+	      if (parens.require_open (parser))
+		{
+		  c_expr expr = c_parser_expr_no_commas (parser, NULL);
+		  t.expr = convert_lvalue_to_rvalue (loc, expr, true,
+						     true).value;
+		  parens.skip_until_found_close (parser);
+		}
+	      declspecs_add_type (loc, specs, t);
+	    }
+	  break;
 	case RID_ATOMIC:
 	  /* C parser handling of Objective-C constructs needs
 	     checking for correct lvalue-to-rvalue conversions, and
@@ -5005,6 +5031,7 @@  c_parser_gnu_attribute_any_word (c_parse
 	case RID_DFLOAT128:
 	CASE_RID_FLOATN_NX:
 	case RID_BOOL:
+	case RID_BITINT:
 	case RID_FRACT:
 	case RID_ACCUM:
 	case RID_SAT:
--- gcc/c/c-tree.h.jj	2023-08-08 15:54:33.812622298 +0200
+++ gcc/c/c-tree.h	2023-08-08 16:12:02.336939700 +0200
@@ -270,7 +270,7 @@  enum c_storage_class {
 
 /* A type specifier keyword "void", "_Bool", "char", "int", "float",
    "double", "_Decimal32", "_Decimal64", "_Decimal128", "_Fract", "_Accum",
-   or none of these.  */
+   "_BitInt", or none of these.  */
 enum c_typespec_keyword {
   cts_none,
   cts_void,
@@ -286,6 +286,7 @@  enum c_typespec_keyword {
   cts_floatn_nx,
   cts_fract,
   cts_accum,
+  cts_bitint,
   cts_auto_type
 };
 
@@ -366,11 +367,16 @@  struct c_declspecs {
      specifier, in bytes, or -1 if no such specifiers with nonzero
      alignment.  */
   int align_log;
-  /* For the __intN declspec, this stores the index into the int_n_* arrays.  */
-  int int_n_idx;
-  /* For the _FloatN and _FloatNx declspec, this stores the index into
-     the floatn_nx_types array.  */
-  int floatn_nx_idx;
+  union {
+    /* For the __intN declspec, this stores the index into the int_n_*
+       arrays.  */
+    int int_n_idx;
+    /* For the _FloatN and _FloatNx declspec, this stores the index into
+       the floatn_nx_types array.  */
+    int floatn_nx_idx;
+    /* For _BitInt(N) this stores the N.  */
+    int bitint_prec;
+  } u;
   /* The storage class specifier, or csc_none if none.  */
   enum c_storage_class storage_class;
   /* Any type specifier keyword used such as "int", not reflecting
--- gcc/c/c-typeck.cc.jj	2023-08-08 15:54:33.822622158 +0200
+++ gcc/c/c-typeck.cc	2023-08-08 16:15:41.008877766 +0200
@@ -413,10 +413,14 @@  composite_type (tree t1, tree t2)
      the composite type.  */
 
   if (code1 == ENUMERAL_TYPE
-      && (code2 == INTEGER_TYPE || code2 == BOOLEAN_TYPE))
+      && (code2 == INTEGER_TYPE
+	  || code2 == BOOLEAN_TYPE
+	  || code2 == BITINT_TYPE))
     return t1;
   if (code2 == ENUMERAL_TYPE
-      && (code1 == INTEGER_TYPE || code1 == BOOLEAN_TYPE))
+      && (code1 == INTEGER_TYPE
+	  || code1 == BOOLEAN_TYPE
+	  || code1 == BITINT_TYPE))
     return t2;
 
   gcc_assert (code1 == code2);
@@ -764,10 +768,10 @@  c_common_type (tree t1, tree t2)
 
   gcc_assert (code1 == VECTOR_TYPE || code1 == COMPLEX_TYPE
 	      || code1 == FIXED_POINT_TYPE || code1 == REAL_TYPE
-	      || code1 == INTEGER_TYPE);
+	      || code1 == INTEGER_TYPE || code1 == BITINT_TYPE);
   gcc_assert (code2 == VECTOR_TYPE || code2 == COMPLEX_TYPE
 	      || code2 == FIXED_POINT_TYPE || code2 == REAL_TYPE
-	      || code2 == INTEGER_TYPE);
+	      || code2 == INTEGER_TYPE || code2 == BITINT_TYPE);
 
   /* When one operand is a decimal float type, the other operand cannot be
      a generic float type or a complex type.  We also disallow vector types
@@ -1004,6 +1008,20 @@  c_common_type (tree t1, tree t2)
     if (mv1 == FLOATNX_TYPE_NODE (i) || mv2 == FLOATNX_TYPE_NODE (i))
       return FLOATNX_TYPE_NODE (i);
 
+  if ((code1 == BITINT_TYPE || code2 == BITINT_TYPE) && code1 != code2)
+    {
+      /* Prefer any other integral types over bit-precise integer types.  */
+      if (TYPE_UNSIGNED (t1) == TYPE_UNSIGNED (t2))
+	return code1 == BITINT_TYPE ? t2 : t1;
+      /* If BITINT_TYPE is unsigned and the other type is signed
+	 non-BITINT_TYPE with the same precision, the latter has higher rank.
+	 In that case:
+	 Otherwise, both operands are converted to the unsigned integer type
+	 corresponding to the type of the operand with signed integer type.  */
+      if (TYPE_UNSIGNED (code1 == BITINT_TYPE ? t1 : t2))
+	return c_common_unsigned_type (code1 == BITINT_TYPE ? t2 : t1);
+    }
+
   /* Otherwise prefer the unsigned one.  */
 
   if (TYPE_UNSIGNED (t1))
@@ -1177,6 +1195,7 @@  comptypes_internal (const_tree type1, co
     case INTEGER_TYPE:
     case FIXED_POINT_TYPE:
     case REAL_TYPE:
+    case BITINT_TYPE:
       /* With these nodes, we can't determine type equivalence by
 	 looking at what is stored in the nodes themselves, because
 	 two nodes might have different TYPE_MAIN_VARIANTs but still
@@ -2260,12 +2279,17 @@  perform_integral_promotions (tree exp)
   /* ??? This should no longer be needed now bit-fields have their
      proper types.  */
   if (TREE_CODE (exp) == COMPONENT_REF
-      && DECL_C_BIT_FIELD (TREE_OPERAND (exp, 1))
+      && DECL_C_BIT_FIELD (TREE_OPERAND (exp, 1)))
+    {
+      if (TREE_CODE (DECL_BIT_FIELD_TYPE (TREE_OPERAND (exp, 1)))
+	  == BITINT_TYPE)
+	return convert (DECL_BIT_FIELD_TYPE (TREE_OPERAND (exp, 1)), exp);
       /* If it's thinner than an int, promote it like a
 	 c_promoting_integer_type_p, otherwise leave it alone.  */
-      && compare_tree_int (DECL_SIZE (TREE_OPERAND (exp, 1)),
-			   TYPE_PRECISION (integer_type_node)) < 0)
-    return convert (integer_type_node, exp);
+      if (compare_tree_int (DECL_SIZE (TREE_OPERAND (exp, 1)),
+			    TYPE_PRECISION (integer_type_node)) < 0)
+	return convert (integer_type_node, exp);
+    }
 
   if (c_promoting_integer_type_p (type))
     {
@@ -2790,7 +2814,8 @@  build_array_ref (location_t loc, tree ar
   if (index == error_mark_node)
     return error_mark_node;
 
-  gcc_assert (TREE_CODE (TREE_TYPE (index)) == INTEGER_TYPE);
+  gcc_assert (TREE_CODE (TREE_TYPE (index)) == INTEGER_TYPE
+	      || TREE_CODE (TREE_TYPE (index)) == BITINT_TYPE);
 
   bool was_vector = VECTOR_TYPE_P (TREE_TYPE (array));
   bool non_lvalue = convert_vector_to_array_for_subscript (loc, &array, index);
@@ -4558,6 +4583,7 @@  build_unary_op (location_t location, enu
 	 associativity, but won't generate any code.  */
       if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE
 	    || typecode == FIXED_POINT_TYPE || typecode == COMPLEX_TYPE
+	    || typecode == BITINT_TYPE
 	    || gnu_vector_type_p (TREE_TYPE (arg))))
 	{
 	  error_at (location, "wrong type argument to unary plus");
@@ -4571,6 +4597,7 @@  build_unary_op (location_t location, enu
     case NEGATE_EXPR:
       if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE
 	    || typecode == FIXED_POINT_TYPE || typecode == COMPLEX_TYPE
+	    || typecode == BITINT_TYPE
 	    || gnu_vector_type_p (TREE_TYPE (arg))))
 	{
 	  error_at (location, "wrong type argument to unary minus");
@@ -4583,6 +4610,7 @@  build_unary_op (location_t location, enu
     case BIT_NOT_EXPR:
       /* ~ works on integer types and non float vectors. */
       if (typecode == INTEGER_TYPE
+	  || typecode == BITINT_TYPE
 	  || (gnu_vector_type_p (TREE_TYPE (arg))
 	      && !VECTOR_FLOAT_TYPE_P (TREE_TYPE (arg))))
 	{
@@ -4657,7 +4685,8 @@  build_unary_op (location_t location, enu
     case TRUTH_NOT_EXPR:
       if (typecode != INTEGER_TYPE && typecode != FIXED_POINT_TYPE
 	  && typecode != REAL_TYPE && typecode != POINTER_TYPE
-	  && typecode != COMPLEX_TYPE && typecode != NULLPTR_TYPE)
+	  && typecode != COMPLEX_TYPE && typecode != NULLPTR_TYPE
+	  && typecode != BITINT_TYPE)
 	{
 	  error_at (location,
 		    "wrong type argument to unary exclamation mark");
@@ -4769,7 +4798,7 @@  build_unary_op (location_t location, enu
 
       if (typecode != POINTER_TYPE && typecode != FIXED_POINT_TYPE
 	  && typecode != INTEGER_TYPE && typecode != REAL_TYPE
-	  && typecode != COMPLEX_TYPE
+	  && typecode != COMPLEX_TYPE && typecode != BITINT_TYPE
 	  && !gnu_vector_type_p (TREE_TYPE (arg)))
 	{
 	  if (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR)
@@ -5394,9 +5423,9 @@  build_conditional_expr (location_t colon
 	result_type = TYPE_MAIN_VARIANT (type1);
     }
   else if ((code1 == INTEGER_TYPE || code1 == REAL_TYPE
-	    || code1 == COMPLEX_TYPE)
+	    || code1 == COMPLEX_TYPE || code1 == BITINT_TYPE)
 	   && (code2 == INTEGER_TYPE || code2 == REAL_TYPE
-	       || code2 == COMPLEX_TYPE))
+	       || code2 == COMPLEX_TYPE || code2 == BITINT_TYPE))
     {
       /* In C11, a conditional expression between a floating-point
 	 type and an integer type should convert the integer type to
@@ -5583,7 +5612,8 @@  build_conditional_expr (location_t colon
 			  (build_qualified_type (void_type_node, qual));
 	}
     }
-  else if (code1 == POINTER_TYPE && code2 == INTEGER_TYPE)
+  else if (code1 == POINTER_TYPE
+	   && (code2 == INTEGER_TYPE || code2 == BITINT_TYPE))
     {
       if (!null_pointer_constant_p (orig_op2))
 	pedwarn (colon_loc, 0,
@@ -5594,7 +5624,8 @@  build_conditional_expr (location_t colon
 	}
       result_type = type1;
     }
-  else if (code2 == POINTER_TYPE && code1 == INTEGER_TYPE)
+  else if (code2 == POINTER_TYPE
+	   && (code1 == INTEGER_TYPE || code1 == BITINT_TYPE))
     {
       if (!null_pointer_constant_p (orig_op1))
 	pedwarn (colon_loc, 0,
@@ -7131,11 +7162,11 @@  convert_for_assignment (location_t locat
   else if ((codel == INTEGER_TYPE || codel == REAL_TYPE
 	    || codel == FIXED_POINT_TYPE
 	    || codel == ENUMERAL_TYPE || codel == COMPLEX_TYPE
-	    || codel == BOOLEAN_TYPE)
+	    || codel == BOOLEAN_TYPE || codel == BITINT_TYPE)
 	   && (coder == INTEGER_TYPE || coder == REAL_TYPE
 	       || coder == FIXED_POINT_TYPE
 	       || coder == ENUMERAL_TYPE || coder == COMPLEX_TYPE
-	       || coder == BOOLEAN_TYPE))
+	       || coder == BOOLEAN_TYPE || coder == BITINT_TYPE))
     {
       if (warnopt && errtype == ic_argpass)
 	maybe_warn_builtin_no_proto_arg (expr_loc, fundecl, parmnum, type,
@@ -8562,7 +8593,8 @@  digest_init (location_t init_loc, tree t
 
   if (code == INTEGER_TYPE || code == REAL_TYPE || code == FIXED_POINT_TYPE
       || code == POINTER_TYPE || code == ENUMERAL_TYPE || code == BOOLEAN_TYPE
-      || code == COMPLEX_TYPE || code == VECTOR_TYPE || code == NULLPTR_TYPE)
+      || code == COMPLEX_TYPE || code == VECTOR_TYPE || code == NULLPTR_TYPE
+      || code == BITINT_TYPE)
     {
       tree unconverted_init = inside_init;
       if (TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE
@@ -12357,12 +12389,14 @@  build_binary_op (location_t location, en
     {
     case PLUS_EXPR:
       /* Handle the pointer + int case.  */
-      if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+      if (code0 == POINTER_TYPE
+	  && (code1 == INTEGER_TYPE || code1 == BITINT_TYPE))
 	{
 	  ret = pointer_int_sum (location, PLUS_EXPR, op0, op1);
 	  goto return_build_binary_op;
 	}
-      else if (code1 == POINTER_TYPE && code0 == INTEGER_TYPE)
+      else if (code1 == POINTER_TYPE
+	       && (code0 == INTEGER_TYPE || code0 == BITINT_TYPE))
 	{
 	  ret = pointer_int_sum (location, PLUS_EXPR, op1, op0);
 	  goto return_build_binary_op;
@@ -12381,7 +12415,8 @@  build_binary_op (location_t location, en
 	  goto return_build_binary_op;
 	}
       /* Handle pointer minus int.  Just like pointer plus int.  */
-      else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+      else if (code0 == POINTER_TYPE
+	       && (code1 == INTEGER_TYPE || code1 == BITINT_TYPE))
 	{
 	  ret = pointer_int_sum (location, MINUS_EXPR, op0, op1);
 	  goto return_build_binary_op;
@@ -12403,11 +12438,11 @@  build_binary_op (location_t location, en
       warn_for_div_by_zero (location, op1);
 
       if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE
-	   || code0 == FIXED_POINT_TYPE
+	   || code0 == FIXED_POINT_TYPE || code0 == BITINT_TYPE
 	   || code0 == COMPLEX_TYPE
 	   || gnu_vector_type_p (type0))
 	  && (code1 == INTEGER_TYPE || code1 == REAL_TYPE
-	      || code1 == FIXED_POINT_TYPE
+	      || code1 == FIXED_POINT_TYPE || code1 == BITINT_TYPE
 	      || code1 == COMPLEX_TYPE
 	      || gnu_vector_type_p (type1)))
 	{
@@ -12418,8 +12453,9 @@  build_binary_op (location_t location, en
 	  if (code1 == COMPLEX_TYPE || code1 == VECTOR_TYPE)
 	    tcode1 = TREE_CODE (TREE_TYPE (TREE_TYPE (op1)));
 
-	  if (!((tcode0 == INTEGER_TYPE && tcode1 == INTEGER_TYPE)
-	      || (tcode0 == FIXED_POINT_TYPE && tcode1 == FIXED_POINT_TYPE)))
+	  if (!(((tcode0 == INTEGER_TYPE || tcode0 == BITINT_TYPE)
+		 && (tcode1 == INTEGER_TYPE || tcode1 == BITINT_TYPE))
+		|| (tcode0 == FIXED_POINT_TYPE && tcode1 == FIXED_POINT_TYPE)))
 	    resultcode = RDIV_EXPR;
 	  else
 	    /* Although it would be tempting to shorten always here, that
@@ -12435,7 +12471,8 @@  build_binary_op (location_t location, en
     case BIT_AND_EXPR:
     case BIT_IOR_EXPR:
     case BIT_XOR_EXPR:
-      if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+      if ((code0 == INTEGER_TYPE || code0 == BITINT_TYPE)
+	  && (code1 == INTEGER_TYPE || code1 == BITINT_TYPE))
 	shorten = -1;
       /* Allow vector types which are not floating point types.   */
       else if (gnu_vector_type_p (type0)
@@ -12455,7 +12492,8 @@  build_binary_op (location_t location, en
 	  && TREE_CODE (TREE_TYPE (type0)) == INTEGER_TYPE
 	  && TREE_CODE (TREE_TYPE (type1)) == INTEGER_TYPE)
 	common = 1;
-      else if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+      else if ((code0 == INTEGER_TYPE || code0 == BITINT_TYPE)
+	       && (code1 == INTEGER_TYPE || code1 == BITINT_TYPE))
 	{
 	  /* Although it would be tempting to shorten always here, that loses
 	     on some targets, since the modulo instruction is undefined if the
@@ -12473,10 +12511,12 @@  build_binary_op (location_t location, en
     case TRUTH_XOR_EXPR:
       if ((code0 == INTEGER_TYPE || code0 == POINTER_TYPE
 	   || code0 == REAL_TYPE || code0 == COMPLEX_TYPE
-	   || code0 == FIXED_POINT_TYPE || code0 == NULLPTR_TYPE)
+	   || code0 == FIXED_POINT_TYPE || code0 == NULLPTR_TYPE
+	   || code0 == BITINT_TYPE)
 	  && (code1 == INTEGER_TYPE || code1 == POINTER_TYPE
 	      || code1 == REAL_TYPE || code1 == COMPLEX_TYPE
-	      || code1 == FIXED_POINT_TYPE || code1 ==  NULLPTR_TYPE))
+	      || code1 == FIXED_POINT_TYPE || code1 == NULLPTR_TYPE
+	      || code1 == BITINT_TYPE))
 	{
 	  /* Result of these operations is always an int,
 	     but that does not mean the operands should be
@@ -12539,9 +12579,10 @@  build_binary_op (location_t location, en
 	  converted = 1;
 	}
       else if ((code0 == INTEGER_TYPE || code0 == FIXED_POINT_TYPE
+		|| code0 == BITINT_TYPE
 		|| (gnu_vector_type_p (type0)
 		    && TREE_CODE (TREE_TYPE (type0)) == INTEGER_TYPE))
-	       && code1 == INTEGER_TYPE)
+	       && (code1 == INTEGER_TYPE || code1 == BITINT_TYPE))
 	{
 	  doing_shift = true;
 	  if (TREE_CODE (op1) == INTEGER_CST)
@@ -12599,9 +12640,10 @@  build_binary_op (location_t location, en
 	  converted = 1;
 	}
       else if ((code0 == INTEGER_TYPE || code0 == FIXED_POINT_TYPE
+		|| code0 == BITINT_TYPE
 		|| (gnu_vector_type_p (type0)
 		    && TREE_CODE (TREE_TYPE (type0)) == INTEGER_TYPE))
-	       && code1 == INTEGER_TYPE)
+	       && (code1 == INTEGER_TYPE || code1 == BITINT_TYPE))
 	{
 	  doing_shift = true;
 	  if (TREE_CODE (op0) == INTEGER_CST
@@ -12715,9 +12757,10 @@  build_binary_op (location_t location, en
       /* Result of comparison is always int,
 	 but don't convert the args to int!  */
       build_type = integer_type_node;
-      if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE
+      if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE || code0 == BITINT_TYPE
 	   || code0 == FIXED_POINT_TYPE || code0 == COMPLEX_TYPE)
 	  && (code1 == INTEGER_TYPE || code1 == REAL_TYPE
+	      || code1 == BITINT_TYPE
 	      || code1 == FIXED_POINT_TYPE || code1 == COMPLEX_TYPE))
 	short_compare = 1;
       else if (code0 == POINTER_TYPE
@@ -12778,12 +12821,14 @@  build_binary_op (location_t location, en
 			      (build_qualified_type (void_type_node, qual));
 	    }
 	}
-      else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+      else if (code0 == POINTER_TYPE
+	       && (code1 == INTEGER_TYPE || code1 == BITINT_TYPE))
 	{
 	  result_type = type0;
 	  pedwarn (location, 0, "comparison between pointer and integer");
 	}
-      else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE)
+      else if ((code0 == INTEGER_TYPE || code0 == BITINT_TYPE)
+	       && code1 == POINTER_TYPE)
 	{
 	  result_type = type1;
 	  pedwarn (location, 0, "comparison between pointer and integer");
@@ -12871,9 +12916,9 @@  build_binary_op (location_t location, en
         }
       build_type = integer_type_node;
       if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE
-	   || code0 == FIXED_POINT_TYPE)
+	   || code0 == BITINT_TYPE || code0 == FIXED_POINT_TYPE)
 	  && (code1 == INTEGER_TYPE || code1 == REAL_TYPE
-	      || code1 == FIXED_POINT_TYPE))
+	      || code1 == BITINT_TYPE || code1 == FIXED_POINT_TYPE))
 	short_compare = 1;
       else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
 	{
@@ -12932,12 +12977,14 @@  build_binary_op (location_t location, en
 	    warning_at (location, OPT_Wextra,
 			"ordered comparison of pointer with integer zero");
 	}
-      else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+      else if (code0 == POINTER_TYPE
+	       && (code1 == INTEGER_TYPE || code1 == BITINT_TYPE))
 	{
 	  result_type = type0;
 	  pedwarn (location, 0, "comparison between pointer and integer");
 	}
-      else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE)
+      else if ((code0 == INTEGER_TYPE || code0 == BITINT_TYPE)
+	       && code1 == POINTER_TYPE)
 	{
 	  result_type = type1;
 	  pedwarn (location, 0, "comparison between pointer and integer");
@@ -12991,12 +13038,11 @@  build_binary_op (location_t location, en
     }
 
   if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE || code0 == COMPLEX_TYPE
-       || code0 == FIXED_POINT_TYPE
+       || code0 == FIXED_POINT_TYPE || code0 == BITINT_TYPE
        || gnu_vector_type_p (type0))
-      &&
-      (code1 == INTEGER_TYPE || code1 == REAL_TYPE || code1 == COMPLEX_TYPE
-       || code1 == FIXED_POINT_TYPE
-       || gnu_vector_type_p (type1)))
+      && (code1 == INTEGER_TYPE || code1 == REAL_TYPE || code1 == COMPLEX_TYPE
+	  || code1 == FIXED_POINT_TYPE || code1 == BITINT_TYPE
+	  || gnu_vector_type_p (type1)))
     {
       bool first_complex = (code0 == COMPLEX_TYPE);
       bool second_complex = (code1 == COMPLEX_TYPE);
--- libcpp/expr.cc.jj	2023-08-08 15:54:35.666596337 +0200
+++ libcpp/expr.cc	2023-08-08 16:12:02.346939560 +0200
@@ -327,9 +327,9 @@  static unsigned int
 interpret_int_suffix (cpp_reader *pfile, const uchar *s, size_t len)
 {
   size_t orig_len = len;
-  size_t u, l, i, z;
+  size_t u, l, i, z, wb;
 
-  u = l = i = z = 0;
+  u = l = i = z = wb = 0;
 
   while (len--)
     switch (s[len])
@@ -343,11 +343,23 @@  interpret_int_suffix (cpp_reader *pfile,
 	if (l == 2 && s[len] != s[len + 1])
 	  return 0;
 	break;
+      case 'b':
+	if (len == 0 || s[len - 1] != 'w')
+	  return 0;
+	wb++;
+	len--;
+	break;
+      case 'B':
+	if (len == 0 || s[len - 1] != 'W')
+	  return 0;
+	wb++;
+	len--;
+	break;
       default:
 	return 0;
       }
 
-  if (l > 2 || u > 1 || i > 1 || z > 1)
+  if (l > 2 || u > 1 || i > 1 || z > 1 || wb > 1)
     return 0;
 
   if (z)
@@ -358,6 +370,14 @@  interpret_int_suffix (cpp_reader *pfile,
 	return 0;
     }
 
+  if (wb)
+    {
+      if (CPP_OPTION (pfile, cplusplus))
+	return 0;
+      if (l > 0 || i > 0 || z > 0)
+	return 0;
+    }
+
   if (i)
     {
       if (!CPP_OPTION (pfile, ext_numeric_literals))
@@ -376,7 +396,8 @@  interpret_int_suffix (cpp_reader *pfile,
 	  | (u ? CPP_N_UNSIGNED : 0)
 	  | ((l == 0) ? CPP_N_SMALL
 	     : (l == 1) ? CPP_N_MEDIUM : CPP_N_LARGE)
-	  | (z ? CPP_N_SIZE_T : 0));
+	  | (z ? CPP_N_SIZE_T : 0)
+	  | (wb ? CPP_N_BITINT : 0));
 }
 
 /* Return the classification flags for an int suffix.  */
--- libcpp/include/cpplib.h.jj	2023-08-08 15:54:35.691595987 +0200
+++ libcpp/include/cpplib.h	2023-08-08 16:12:02.346939560 +0200
@@ -1284,6 +1284,7 @@  struct cpp_num
 
 #define CPP_N_SIZE_T	0x2000000 /* C++23 size_t literal.  */
 #define CPP_N_BFLOAT16	0x4000000 /* std::bfloat16_t type.  */
+#define CPP_N_BITINT	0x8000000 /* C2X _BitInt literal.  */
 
 #define CPP_N_WIDTH_FLOATN_NX	0xF0000000 /* _FloatN / _FloatNx value
 					      of N, divided by 16.  */