public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-3106] c++, c: Implement C++23 P1774R8 - Portable assumptions [PR106654]
@ 2022-10-06  7:00 Jakub Jelinek
  0 siblings, 0 replies; only message in thread
From: Jakub Jelinek @ 2022-10-06  7:00 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:08b51baddc53d64aa4c5e7a81ef3c4bf320293be

commit r13-3106-g08b51baddc53d64aa4c5e7a81ef3c4bf320293be
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Oct 6 08:56:48 2022 +0200

    c++, c: Implement C++23 P1774R8 - Portable assumptions [PR106654]
    
    The following patch implements C++23 P1774R8 - Portable assumptions
    paper, by introducing support for [[assume (cond)]]; attribute for C++.
    In addition to that the patch adds [[gnu::assume (cond)]]; and
    __attribute__((assume (cond))); support to both C and C++.
    As described in C++23, the attribute argument is conditional-expression
    rather than the usual assignment-expression for attribute arguments,
    the condition is contextually converted to bool (for C truthvalue conversion
    is done on it) and is never evaluated at runtime.
    For C++ constant expression evaluation, I only check the simplest conditions
    for undefined behavior, because otherwise I'd need to undo changes to
    *ctx->global which happened during the evaluation (but I believe the spec
    allows that and we can further improve later).
    The patch uses a new internal function, .ASSUME, to hold the condition
    in the FEs.  At gimplification time, if the condition is simple/without
    side-effects, it is gimplified as if (cond) ; else __builtin_unreachable ();
    and otherwise for now dropped on the floor.  The intent is to incrementally
    outline the conditions into separate artificial functions and use
    .ASSUME further to tell the ranger and perhaps other optimization passes
    about the assumptions, as detailed in the PR.
    
    When implementing it, I found that assume entry hasn't been added to
    https://eel.is/c++draft/cpp.cond#6
    Jonathan said he'll file a NB comment about it, this patch assumes it
    has been added into the table as 202207L when the paper has been voted in.
    
    With the attributes for both C/C++, I'd say we don't need to add
    __builtin_assume with similar purpose, especially when __builtin_assume
    in LLVM is just weird.  It is strange for side-effects in function call's
    argument not to be evaluated, and LLVM in that case (annoyingly) warns
    and ignores the side-effects (but doesn't do then anything with it),
    if there are no side-effects, it will work like our
    if (!cond) __builtin_unreachable ();
    
    2022-10-06  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/106654
    gcc/
            * internal-fn.def (ASSUME): New internal function.
            * internal-fn.h (expand_ASSUME): Declare.
            * internal-fn.cc (expand_ASSUME): Define.
            * gimplify.cc (gimplify_call_expr): Gimplify IFN_ASSUME.
            * fold-const.h (simple_condition_p): Declare.
            * fold-const.cc (simple_operand_p_2): Rename to ...
            (simple_condition_p): ... this.  Remove forward declaration.
            No longer static.  Adjust function comment and fix a typo in it.
            Adjust recursive call.
            (simple_operand_p): Adjust function comment.
            (fold_truth_andor): Adjust simple_operand_p_2 callers to call
            simple_condition_p.
            * doc/extend.texi: Document assume attribute.  Move fallthrough
            attribute example to its section.
    gcc/c-family/
            * c-attribs.cc (handle_assume_attribute): New function.
            (c_common_attribute_table): Add entry for assume attribute.
            * c-lex.cc (c_common_has_attribute): Handle
            __have_cpp_attribute (assume).
    gcc/c/
            * c-parser.cc (handle_assume_attribute): New function.
            (c_parser_declaration_or_fndef): Handle assume attribute.
            (c_parser_attribute_arguments): Add assume_attr argument,
            if true, parse first argument as conditional expression.
            (c_parser_gnu_attribute, c_parser_std_attribute): Adjust
            c_parser_attribute_arguments callers.
            (c_parser_statement_after_labels) <case RID_ATTRIBUTE>: Handle
            assume attribute.
    gcc/cp/
            * cp-tree.h (process_stmt_assume_attribute): Implement C++23
            P1774R8 - Portable assumptions.  Declare.
            (diagnose_failing_condition): Declare.
            (find_failing_clause): Likewise.
            * parser.cc (assume_attr): New enumerator.
            (cp_parser_parenthesized_expression_list): Handle assume_attr.
            Remove identifier variable, for id_attr push the identifier into
            expression_list right away instead of inserting it before all the
            others at the end.
            (cp_parser_conditional_expression): New function.
            (cp_parser_constant_expression): Use it.
            (cp_parser_statement): Handle assume attribute.
            (cp_parser_expression_statement): Likewise.
            (cp_parser_gnu_attribute_list): Use assume_attr for assume
            attribute.
            (cp_parser_std_attribute): Likewise.  Handle standard assume
            attribute like gnu::assume.
            * cp-gimplify.cc (process_stmt_assume_attribute): New function.
            * constexpr.cc: Include fold-const.h.
            (find_failing_clause_r, find_failing_clause): New functions,
            moved from semantics.cc with ctx argument added and if non-NULL,
            call cxx_eval_constant_expression rather than fold_non_dependent_expr.
            (cxx_eval_internal_function): Handle IFN_ASSUME.
            (potential_constant_expression_1): Likewise.
            * pt.cc (tsubst_copy_and_build): Likewise.
            * semantics.cc (diagnose_failing_condition): New function.
            (find_failing_clause_r, find_failing_clause): Moved to constexpr.cc.
            (finish_static_assert): Use it.  Add auto_diagnostic_group.
    gcc/testsuite/
            * gcc.dg/attr-assume-1.c: New test.
            * gcc.dg/attr-assume-2.c: New test.
            * gcc.dg/attr-assume-3.c: New test.
            * g++.dg/cpp2a/feat-cxx2a.C: Add colon to C++20 features
            comment, add C++20 attributes comment and move C++20
            new features after the attributes before them.
            * g++.dg/cpp23/feat-cxx2b.C: Likewise.  Test
            __has_cpp_attribute(assume).
            * g++.dg/cpp23/attr-assume1.C: New test.
            * g++.dg/cpp23/attr-assume2.C: New test.
            * g++.dg/cpp23/attr-assume3.C: New test.
            * g++.dg/cpp23/attr-assume4.C: New test.

Diff:
---
 gcc/c-family/c-attribs.cc                 |  15 +++
 gcc/c-family/c-lex.cc                     |   2 +
 gcc/c/c-parser.cc                         | 113 +++++++++++++++--
 gcc/cp/constexpr.cc                       |  90 ++++++++++++++
 gcc/cp/cp-gimplify.cc                     |  44 +++++++
 gcc/cp/cp-tree.h                          |   4 +
 gcc/cp/parser.cc                          | 101 ++++++++-------
 gcc/cp/pt.cc                              |  27 ++++
 gcc/cp/semantics.cc                       |  75 ++++-------
 gcc/doc/extend.texi                       |  49 ++++++--
 gcc/fold-const.cc                         |  28 ++---
 gcc/fold-const.h                          |   1 +
 gcc/gimplify.cc                           |  19 +++
 gcc/internal-fn.cc                        |   6 +
 gcc/internal-fn.def                       |   4 +
 gcc/internal-fn.h                         |   1 +
 gcc/testsuite/g++.dg/cpp23/attr-assume1.C | 191 ++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp23/attr-assume2.C |  83 +++++++++++++
 gcc/testsuite/g++.dg/cpp23/attr-assume3.C | 198 ++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp23/attr-assume4.C | 136 ++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C   |  88 +++++++------
 gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C   |  76 ++++++------
 gcc/testsuite/gcc.dg/attr-assume-1.c      |  69 +++++++++++
 gcc/testsuite/gcc.dg/attr-assume-2.c      |  66 ++++++++++
 gcc/testsuite/gcc.dg/attr-assume-3.c      |  35 ++++++
 25 files changed, 1319 insertions(+), 202 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 8bb80e251dc..671ea38fd1b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -144,6 +144,7 @@ static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
 static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
 static tree handle_alloc_align_attribute (tree *, tree, tree, int, bool *);
 static tree handle_assume_aligned_attribute (tree *, tree, tree, int, bool *);
+static tree handle_assume_attribute (tree *, tree, tree, int, bool *);
 static tree handle_target_attribute (tree *, tree, tree, int, bool *);
 static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *);
 static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
@@ -530,6 +531,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_designated_init_attribute, NULL },
   { "fallthrough",	      0, 0, false, false, false, false,
 			      handle_fallthrough_attribute, NULL },
+  { "assume",		      1, 1, false, false, false, false,
+			      handle_assume_attribute, NULL },
   { "patchable_function_entry",	1, 2, true, false, false, false,
 			      handle_patchable_function_entry_attribute,
 			      NULL },
@@ -5741,6 +5744,18 @@ handle_fallthrough_attribute (tree *, tree name, tree, int,
   return NULL_TREE;
 }
 
+/* Handle a "assume" attribute; arguments as in struct
+   attribute_spec.handler.  */
+
+tree
+handle_assume_attribute (tree *, tree name, tree, int,
+			 bool *no_add_attrs)
+{
+  pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored", name);
+  *no_add_attrs = true;
+  return NULL_TREE;
+}
+
 /* Handle a "patchable_function_entry" attributes; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc
index d4e448a0132..050fa775a95 100644
--- a/gcc/c-family/c-lex.cc
+++ b/gcc/c-family/c-lex.cc
@@ -378,6 +378,8 @@ c_common_has_attribute (cpp_reader *pfile, bool std_syntax)
 		result = 201803;
 	      else if (is_attribute_p ("nodiscard", attr_name))
 		result = 201907;
+	      else if (is_attribute_p ("assume", attr_name))
+		result = 202207;
 	    }
 	  else
 	    {
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index f6a94ba31d8..9b7663739dd 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -1823,6 +1823,46 @@ add_debug_begin_stmt (location_t loc)
   add_stmt (stmt);
 }
 
+/* Helper function for c_parser_declaration_or_fndef and
+   Handle assume attribute(s).  */
+
+static tree
+handle_assume_attribute (location_t here, tree attrs, bool nested)
+{
+  if (nested)
+    for (tree attr = lookup_attribute ("gnu", "assume", attrs); attr;
+	 attr = lookup_attribute ("gnu", "assume", TREE_CHAIN (attr)))
+      {
+	tree args = TREE_VALUE (attr);
+	int nargs = list_length (args);
+	if (nargs != 1)
+	  {
+	    error_at (here, "wrong number of arguments specified "
+			    "for %qE attribute",
+		      get_attribute_name (attr));
+	    inform (here, "expected %i, found %i", 1, nargs);
+	  }
+	else
+	  {
+	    tree arg = TREE_VALUE (args);
+	    arg = c_objc_common_truthvalue_conversion (here, arg);
+	    arg = c_fully_fold (arg, false, NULL);
+	    if (arg != error_mark_node)
+	      {
+		tree fn = build_call_expr_internal_loc (here, IFN_ASSUME,
+							void_type_node, 1,
+							arg);
+		add_stmt (fn);
+	      }
+	  }
+      }
+  else
+    pedwarn (here, OPT_Wattributes,
+	     "%<assume%> attribute at top level");
+
+  return remove_attribute ("gnu", "assume", attrs);
+}
+
 /* Parse a declaration or function definition (C90 6.5, 6.7.1, C99
    6.7, 6.9.1, C11 6.7, 6.9.1).  If FNDEF_OK is true, a function definition
    is accepted; otherwise (old-style parameter declarations) only other
@@ -2037,6 +2077,14 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
   bool auto_type_p = specs->typespec_word == cts_auto_type;
   if (c_parser_next_token_is (parser, CPP_SEMICOLON))
     {
+      bool handled_assume = false;
+      if (specs->typespec_kind == ctsk_none
+	  && lookup_attribute ("gnu", "assume", specs->attrs))
+	{
+	  handled_assume = true;
+	  specs->attrs
+	    = handle_assume_attribute (here, specs->attrs, nested);
+	}
       if (auto_type_p)
 	error_at (here, "%<__auto_type%> in empty declaration");
       else if (specs->typespec_kind == ctsk_none
@@ -2054,13 +2102,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 	    pedwarn (here, OPT_Wattributes,
 		     "%<fallthrough%> attribute at top level");
 	}
-      else if (empty_ok && !(have_attrs
-			     && specs->non_std_attrs_seen_p))
+      else if (empty_ok
+	       && !(have_attrs && specs->non_std_attrs_seen_p)
+	       && !handled_assume)
 	shadow_tag (specs);
       else
 	{
 	  shadow_tag_warned (specs, 1);
-	  pedwarn (here, 0, "empty declaration");
+	  if (!handled_assume)
+	    pedwarn (here, 0, "empty declaration");
 	}
       c_parser_consume_token (parser);
       if (oacc_routine_data)
@@ -2160,6 +2210,9 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
   else if (attribute_fallthrough_p (specs->attrs))
     warning_at (here, OPT_Wattributes,
 		"%<fallthrough%> attribute not followed by %<;%>");
+  else if (lookup_attribute ("gnu", "assume", specs->attrs))
+    warning_at (here, OPT_Wattributes,
+		"%<assume%> attribute not followed by %<;%>");
 
   pending_xref_error ();
   prefix_attrs = specs->attrs;
@@ -4598,7 +4651,8 @@ c_parser_gnu_attribute_any_word (c_parser *parser)
 
 static tree
 c_parser_attribute_arguments (c_parser *parser, bool takes_identifier,
-			      bool require_string, bool allow_empty_args)
+			      bool require_string, bool assume_attr,
+			      bool allow_empty_args)
 {
   vec<tree, va_gc> *expr_list;
   tree attr_args;
@@ -4617,6 +4671,7 @@ c_parser_attribute_arguments (c_parser *parser, bool takes_identifier,
 	      == CPP_CLOSE_PAREN))
       && (takes_identifier
 	  || (c_dialect_objc ()
+	      && !assume_attr
 	      && c_parser_peek_token (parser)->id_kind
 	      == C_ID_CLASSNAME)))
     {
@@ -4653,6 +4708,23 @@ c_parser_attribute_arguments (c_parser *parser, bool takes_identifier,
 	  tree string = c_parser_string_literal (parser, false, true).value;
 	  attr_args = build_tree_list (NULL_TREE, string);
 	}
+      else if (assume_attr)
+	{
+	  tree cond
+	    = c_parser_conditional_expression (parser, NULL, NULL_TREE).value;
+	  if (!c_parser_next_token_is (parser, CPP_COMMA))
+	    attr_args = build_tree_list (NULL_TREE, cond);
+	  else
+	    {
+	      tree tree_list;
+	      c_parser_consume_token (parser);
+	      expr_list = c_parser_expr_list (parser, false, true,
+					      NULL, NULL, NULL, NULL);
+	      tree_list = build_tree_list_vec (expr_list);
+	      attr_args = tree_cons (NULL_TREE, cond, tree_list);
+	      release_tree_vector (expr_list);
+	    }
+	}
       else
 	{
 	  expr_list = c_parser_expr_list (parser, false, true,
@@ -4736,7 +4808,9 @@ c_parser_gnu_attribute (c_parser *parser, tree attrs,
   tree attr_args
     = c_parser_attribute_arguments (parser,
 				    attribute_takes_identifier_p (attr_name),
-				    false, true);
+				    false,
+				    is_attribute_p ("assume", attr_name),
+				    true);
 
   attr = build_tree_list (attr_name, attr_args);
   if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
@@ -4982,9 +5056,13 @@ c_parser_std_attribute (c_parser *parser, bool for_tm)
 	  = (ns == NULL_TREE
 	     && (strcmp (IDENTIFIER_POINTER (name), "deprecated") == 0
 		 || strcmp (IDENTIFIER_POINTER (name), "nodiscard") == 0));
+	bool assume_attr
+	  = (ns != NULL_TREE
+	     && strcmp (IDENTIFIER_POINTER (ns), "gnu") == 0
+	     && strcmp (IDENTIFIER_POINTER (name), "assume") == 0);
 	TREE_VALUE (attribute)
 	  = c_parser_attribute_arguments (parser, takes_identifier,
-					  require_string, false);
+					  require_string, assume_attr, false);
       }
     else
       c_parser_balanced_token_sequence (parser);
@@ -6264,8 +6342,21 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p,
 	  break;
 	case RID_ATTRIBUTE:
 	  {
-	    /* Allow '__attribute__((fallthrough));'.  */
+	    /* Allow '__attribute__((fallthrough));' or
+	       '__attribute__((assume(cond)));'.  */
 	    tree attrs = c_parser_gnu_attributes (parser);
+	    bool has_assume = lookup_attribute ("assume", attrs);
+	    if (has_assume)
+	      {
+		if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+		  attrs = handle_assume_attribute (loc, attrs, true);
+		else
+		  {
+		    warning_at (loc, OPT_Wattributes,
+				"%<assume%> attribute not followed by %<;%>");
+		    has_assume = false;
+		  }
+	      }
 	    if (attribute_fallthrough_p (attrs))
 	      {
 		if (c_parser_next_token_is (parser, CPP_SEMICOLON))
@@ -6282,9 +6373,13 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p,
 			      "%<fallthrough%> attribute not followed "
 			      "by %<;%>");
 	      }
+	    else if (has_assume)
+	      /* Eat the ';'.  */
+	      c_parser_consume_token (parser);
 	    else if (attrs != NULL_TREE)
-	      warning_at (loc, OPT_Wattributes, "only attribute %<fallthrough%>"
-			  " can be applied to a null statement");
+	      warning_at (loc, OPT_Wattributes,
+			  "only attribute %<fallthrough%> or %<assume%> can "
+			  "be applied to a null statement");
 	    break;
 	  }
 	default:
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index db7571d7d71..06dcd71c926 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "opts.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "fold-const.h"
 
 static bool verify_constant (tree, bool, bool *, bool *);
 #define VERIFY_CONSTANT(X)						\
@@ -1818,6 +1819,52 @@ cx_error_context (void)
   return r;
 }
 
+/* If we have a condition in conjunctive normal form (CNF), find the first
+   failing clause.  In other words, given an expression like
+
+     true && true && false && true && false
+
+   return the first 'false'.  EXPR is the expression.  */
+
+static tree
+find_failing_clause_r (constexpr_ctx *ctx, tree expr)
+{
+  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
+    {
+      /* First check the left side...  */
+      tree e = find_failing_clause_r (ctx, TREE_OPERAND (expr, 0));
+      if (e == NULL_TREE)
+	/* ...if we didn't find a false clause, check the right side.  */
+	e = find_failing_clause_r (ctx, TREE_OPERAND (expr, 1));
+      return e;
+    }
+  tree e = contextual_conv_bool (expr, tf_none);
+  if (ctx)
+    {
+      bool new_non_constant_p = false, new_overflow_p = false;
+      e = cxx_eval_constant_expression (ctx, e, vc_prvalue,
+					&new_non_constant_p,
+					&new_overflow_p);
+    }
+  else
+    e = fold_non_dependent_expr (e, tf_none, /*manifestly_const_eval=*/true);
+  if (integer_zerop (e))
+    /* This is the failing clause.  */
+    return expr;
+  return NULL_TREE;
+}
+
+/* Wrapper for find_failing_clause_r.  */
+
+tree
+find_failing_clause (constexpr_ctx *ctx, tree expr)
+{
+  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
+    if (tree e = find_failing_clause_r (ctx, expr))
+      expr = e;
+  return expr;
+}
+
 /* Evaluate a call T to a GCC internal function when possible and return
    the evaluated result or, under the control of CTX, give an error, set
    NON_CONSTANT_P, and return the unevaluated call T otherwise.  */
@@ -1837,6 +1884,48 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
     case IFN_FALLTHROUGH:
       return void_node;
 
+    case IFN_ASSUME:
+      /* For now, restrict constexpr evaluation of [[assume (cond)]]
+	 only to the cases which don't have side-effects.  Evaluating
+	 it even when it does would mean we'd need to somehow undo
+	 all the side-effects e.g. in ctx->global->values.  */
+      if (!TREE_SIDE_EFFECTS (CALL_EXPR_ARG (t, 0))
+	  /* And it needs to be a potential constant expression.  */
+	  && potential_rvalue_constant_expression (CALL_EXPR_ARG (t, 0)))
+	{
+	  constexpr_ctx new_ctx = *ctx;
+	  new_ctx.quiet = true;
+	  tree arg = CALL_EXPR_ARG (t, 0);
+	  bool new_non_constant_p = false, new_overflow_p = false;
+	  arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
+					      &new_non_constant_p,
+					      &new_overflow_p);
+	  if (!new_non_constant_p && !new_overflow_p && integer_zerop (arg))
+	    {
+	      if (!*non_constant_p && !ctx->quiet)
+		{
+		  /* See if we can find which clause was failing
+		     (for logical AND).  */
+		  tree bad = find_failing_clause (&new_ctx,
+						  CALL_EXPR_ARG (t, 0));
+		  /* If not, or its location is unusable, fall back to the
+		     previous location.  */
+		  location_t cloc = cp_expr_loc_or_loc (bad, EXPR_LOCATION (t));
+
+		  auto_diagnostic_group d;
+
+		  /* Report the error. */
+		  error_at (cloc,
+			    "failed %<assume%> attribute assumption");
+		  diagnose_failing_condition (bad, cloc, false);
+		}
+
+	      *non_constant_p = true;
+	      return t;
+	    }
+	}
+      return void_node;
+
     case IFN_ADD_OVERFLOW:
       opcode = PLUS_EXPR;
       break;
@@ -8706,6 +8795,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
 		case IFN_UBSAN_BOUNDS:
 		case IFN_UBSAN_VPTR:
 		case IFN_FALLTHROUGH:
+		case IFN_ASSUME:
 		  return true;
 
 		case IFN_ADD_OVERFLOW:
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 404a7699a72..b4599fc34d8 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -3081,6 +3081,50 @@ process_stmt_hotness_attribute (tree std_attrs, location_t attrs_loc)
   return std_attrs;
 }
 
+/* If [[assume (cond)]] appears on this statement, handle it.  */
+
+tree
+process_stmt_assume_attribute (tree std_attrs, tree statement,
+			       location_t attrs_loc)
+{
+  if (std_attrs == error_mark_node)
+    return std_attrs;
+  tree attr = lookup_attribute ("gnu", "assume", std_attrs);
+  if (!attr)
+    return std_attrs;
+  /* The next token after the assume attribute is not ';'.  */
+  if (statement)
+    {
+      warning_at (attrs_loc, OPT_Wattributes,
+		  "%<assume%> attribute not followed by %<;%>");
+      attr = NULL_TREE;
+    }
+  for (; attr; attr = lookup_attribute ("gnu", "assume", TREE_CHAIN (attr)))
+    {
+      tree args = TREE_VALUE (attr);
+      int nargs = list_length (args);
+      if (nargs != 1)
+	{
+	  auto_diagnostic_group d;
+	  error_at (attrs_loc, "wrong number of arguments specified for "
+			       "%qE attribute", get_attribute_name (attr));
+	  inform (attrs_loc, "expected %i, found %i", 1, nargs);
+	}
+      else
+	{
+	  tree arg = TREE_VALUE (args);
+	  if (!type_dependent_expression_p (arg))
+	    arg = contextual_conv_bool (arg, tf_warning_or_error);
+	  if (error_operand_p (arg))
+	    continue;
+	  statement = build_call_expr_internal_loc (attrs_loc, IFN_ASSUME,
+						    void_type_node, 1, arg);
+	  finish_expr_stmt (statement);
+	}
+    }
+  return remove_attribute ("gnu", "assume", std_attrs);
+}
+
 /* Helper of fold_builtin_source_location, return the
    std::source_location::__impl type after performing verification
    on it.  LOC is used for reporting any errors.  */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8cf97075073..8bc1c2dc7fd 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7715,6 +7715,7 @@ extern tree build_transaction_expr		(location_t, tree, int, tree);
 extern bool cxx_omp_create_clause_info		(tree, tree, bool, bool,
 						 bool, bool);
 extern tree baselink_for_fns                    (tree);
+extern void diagnose_failing_condition		(tree, location_t, bool);
 extern void finish_static_assert                (tree, tree, location_t,
 						 bool, bool);
 extern tree finish_decltype_type                (tree, bool, tsubst_flags_t);
@@ -8242,6 +8243,7 @@ extern tree predeclare_vla			(tree);
 extern void clear_fold_cache			(void);
 extern tree lookup_hotness_attribute		(tree);
 extern tree process_stmt_hotness_attribute	(tree, location_t);
+extern tree process_stmt_assume_attribute	(tree, tree, location_t);
 extern bool simple_empty_class_p		(tree, tree, tree_code);
 extern tree fold_builtin_source_location	(location_t);
 
@@ -8447,6 +8449,8 @@ extern tree fold_sizeof_expr			(tree);
 extern void clear_cv_and_fold_caches		(void);
 extern tree unshare_constructor			(tree CXX_MEM_STAT_INFO);
 extern bool decl_implicit_constexpr_p		(tree);
+struct constexpr_ctx;
+extern tree find_failing_clause			(constexpr_ctx *ctx, tree);
 extern bool replace_decl			(tree *, tree, tree);
 
 /* An RAII sentinel used to restrict constexpr evaluation so that it
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 7b416352ef8..baa808a4634 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -2258,7 +2258,7 @@ static vec<tree, va_gc> *cp_parser_parenthesized_expression_list
   (cp_parser *, int, bool, bool, bool *, location_t * = NULL,
    bool = false);
 /* Values for the second parameter of cp_parser_parenthesized_expression_list.  */
-enum { non_attr = 0, normal_attr = 1, id_attr = 2 };
+enum { non_attr = 0, normal_attr = 1, id_attr = 2, assume_attr = 3 };
 static void cp_parser_pseudo_destructor_name
   (cp_parser *, tree, tree *, tree *);
 static cp_expr cp_parser_unary_expression
@@ -2287,6 +2287,7 @@ static cp_expr cp_parser_binary_expression
   (cp_parser *, bool, bool, enum cp_parser_prec, cp_id_kind *);
 static tree cp_parser_question_colon_clause
   (cp_parser *, cp_expr);
+static cp_expr cp_parser_conditional_expression (cp_parser *);
 static cp_expr cp_parser_assignment_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
 static enum tree_code cp_parser_assignment_operator_opt
@@ -8480,7 +8481,6 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 					 bool wrap_locations_p)
 {
   vec<tree, va_gc> *expression_list;
-  tree identifier = NULL_TREE;
   bool saved_greater_than_is_operator_p;
 
   /* Assume all the expressions will be constant.  */
@@ -8509,33 +8509,26 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 	   next token is an identifier.  */
 	if (is_attribute_list == id_attr
 	    && cp_lexer_peek_token (parser->lexer)->type == CPP_NAME)
-	  {
-	    cp_token *token;
-
-	    /* Consume the identifier.  */
-	    token = cp_lexer_consume_token (parser->lexer);
-	    /* Save the identifier.  */
-	    identifier = token->u.value;
-	  }
+	  expr = cp_lexer_consume_token (parser->lexer)->u.value;
+	else if (is_attribute_list == assume_attr)
+	  expr = cp_parser_conditional_expression (parser);
 	else
-	  {
-	    expr
-	      = cp_parser_parenthesized_expression_list_elt (parser, cast_p,
-							     allow_expansion_p,
-							     non_constant_p);
+	  expr
+	    = cp_parser_parenthesized_expression_list_elt (parser, cast_p,
+							   allow_expansion_p,
+							   non_constant_p);
 
-	    if (wrap_locations_p)
-	      expr.maybe_add_location_wrapper ();
+	if (wrap_locations_p)
+	  expr.maybe_add_location_wrapper ();
 
-	     /* Add it to the list.  We add error_mark_node
-		expressions to the list, so that we can still tell if
-		the correct form for a parenthesized expression-list
-		is found. That gives better errors.  */
-	    vec_safe_push (expression_list, expr.get_value ());
+	/* Add it to the list.  We add error_mark_node
+	   expressions to the list, so that we can still tell if
+	   the correct form for a parenthesized expression-list
+	   is found. That gives better errors.  */
+	vec_safe_push (expression_list, expr.get_value ());
 
-	    if (expr == error_mark_node)
-	      goto skip_comma;
-	  }
+	if (expr == error_mark_node)
+	  goto skip_comma;
 
 	/* After the first item, attribute lists look the same as
 	   expression lists.  */
@@ -8577,9 +8570,6 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
   parser->greater_than_is_operator_p
     = saved_greater_than_is_operator_p;
 
-  if (identifier)
-    vec_safe_insert (expression_list, 0, identifier);
-
   return expression_list;
 }
 
@@ -10310,7 +10300,8 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
    logical-or-expression that started the conditional-expression.
    Returns a representation of the entire conditional-expression.
 
-   This routine is used by cp_parser_assignment_expression.
+   This routine is used by cp_parser_assignment_expression
+   and cp_parser_conditional_expression.
 
      ? expression : assignment-expression
 
@@ -10377,6 +10368,28 @@ cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr)
                                    tf_warning_or_error);
 }
 
+/* Parse a conditional-expression.
+
+   conditional-expression:
+     logical-or-expression
+     logical-or-expression ? expression : assignment-expression
+
+   GNU Extensions:
+
+     logical-or-expression ? : assignment-expression  */
+
+static cp_expr
+cp_parser_conditional_expression (cp_parser *parser)
+{
+  cp_expr expr = cp_parser_binary_expression (parser, false, false, false,
+					      PREC_NOT_OPERATOR, NULL);
+  /* If the next token is a `?' then we're actually looking at
+     a conditional-expression; otherwise we're done.  */
+  if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY))
+    return cp_parser_question_colon_clause (parser, expr);
+  return expr;
+}
+
 /* Parse an assignment-expression.
 
    assignment-expression:
@@ -10702,15 +10715,7 @@ cp_parser_constant_expression (cp_parser* parser,
      determine whether a particular assignment-expression is in fact
      constant.  */
   if (strict_p)
-    {
-      /* Parse the binary expressions (logical-or-expression).  */
-      expression = cp_parser_binary_expression (parser, false, false, false,
-						PREC_NOT_OPERATOR, NULL);
-      /* If the next token is a `?' then we're actually looking at
-	 a conditional-expression; otherwise we're done.  */
-      if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY))
-	expression = cp_parser_question_colon_clause (parser, expression);
-    }
+    expression = cp_parser_conditional_expression (parser);
   else
     expression = cp_parser_assignment_expression (parser);
   /* Restore the old settings.  */
@@ -12503,6 +12508,9 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
       /* Look for an expression-statement instead.  */
       statement = cp_parser_expression_statement (parser, in_statement_expr);
 
+      std_attrs = process_stmt_assume_attribute (std_attrs, statement,
+						 attrs_loc);
+
       /* Handle [[fallthrough]];.  */
       if (attribute_fallthrough_p (std_attrs))
 	{
@@ -12526,7 +12534,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
   if (statement && STATEMENT_CODE_P (TREE_CODE (statement)))
     SET_EXPR_LOCATION (statement, statement_location);
 
-  /* Allow "[[fallthrough]];", but warn otherwise.  */
+  /* Allow "[[fallthrough]];" or "[[assume(cond)]];", but warn otherwise.  */
   if (std_attrs != NULL_TREE)
     warning_at (attrs_loc,
 		OPT_Wattributes,
@@ -12718,6 +12726,8 @@ cp_parser_expression_statement (cp_parser* parser, tree in_statement_expr)
 	}
     }
 
+  attr = process_stmt_assume_attribute (attr, statement, loc);
+
   /* Handle [[fallthrough]];.  */
   if (attribute_fallthrough_p (attr))
     {
@@ -28876,6 +28886,8 @@ cp_parser_gnu_attribute_list (cp_parser* parser, bool exactly_one /* = false */)
 	      vec<tree, va_gc> *vec;
 	      int attr_flag = (attribute_takes_identifier_p (identifier)
 			       ? id_attr : normal_attr);
+	      if (is_attribute_p ("assume", identifier))
+		attr_flag = assume_attr;
 	      vec = cp_parser_parenthesized_expression_list
 		    (parser, attr_flag, /*cast_p=*/false,
 		    /*allow_expansion_p=*/false,
@@ -29127,6 +29139,9 @@ cp_parser_std_attribute (cp_parser *parser, tree attr_ns)
       /* C++17 fallthrough attribute is equivalent to GNU's.  */
       else if (is_attribute_p ("fallthrough", attr_id))
 	TREE_PURPOSE (TREE_PURPOSE (attribute)) = gnu_identifier;
+      /* C++23 assume attribute is equivalent to GNU's.  */
+      else if (is_attribute_p ("assume", attr_id))
+	TREE_PURPOSE (TREE_PURPOSE (attribute)) = gnu_identifier;
       /* Transactional Memory TS optimize_for_synchronized attribute is
 	 equivalent to GNU transaction_callable.  */
       else if (is_attribute_p ("optimize_for_synchronized", attr_id))
@@ -29171,8 +29186,12 @@ cp_parser_std_attribute (cp_parser *parser, tree attr_ns)
 	return error_mark_node;
       }
 
-    if (attr_ns == gnu_identifier
-	&& attribute_takes_identifier_p (attr_id))
+    if (is_attribute_p ("assume", attr_id)
+	&& (attr_ns == NULL_TREE || attr_ns == gnu_identifier))
+      /* The assume attribute needs special handling of the argument.  */
+      attr_flag = assume_attr;
+    else if (attr_ns == gnu_identifier
+	     && attribute_takes_identifier_p (attr_id))
       /* A GNU attribute that takes an identifier in parameter.  */
       attr_flag = id_attr;
 
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index bce2a777922..bf4ae028eb0 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -21163,6 +21163,33 @@ tsubst_copy_and_build (tree t,
 		break;
 	      }
 
+	    case IFN_ASSUME:
+	      gcc_assert (nargs == 1);
+	      if (vec_safe_length (call_args) != 1)
+		{
+		  error_at (cp_expr_loc_or_input_loc (t),
+			    "wrong number of arguments to "
+			    "%<assume%> attribute");
+		  ret = error_mark_node;
+		}
+	      else
+		{
+		  tree &arg = (*call_args)[0];
+		  if (!type_dependent_expression_p (arg))
+		    arg = contextual_conv_bool (arg, tf_warning_or_error);
+		  if (error_operand_p (arg))
+		    {
+		      ret = error_mark_node;
+		      break;
+		    }
+		  ret = build_call_expr_internal_loc (EXPR_LOCATION (t),
+						      IFN_ASSUME,
+						      void_type_node, 1,
+						      arg);
+		  RETURN (ret);
+		}
+	      break;
+
 	    default:
 	      /* Unsupported internal function with arguments.  */
 	      gcc_unreachable ();
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 30cf2f99a55..39b11eeab8a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -11172,42 +11172,31 @@ init_cp_semantics (void)
 }
 \f
 
-/* If we have a condition in conjunctive normal form (CNF), find the first
-   failing clause.  In other words, given an expression like
+/* Emit additional diagnostics for failing condition BAD.
+   Used by finish_static_assert and IFN_ASSUME constexpr diagnostics.
+   If SHOW_EXPR_P is true, print the condition (because it was
+   instantiation-dependent).  */
 
-     true && true && false && true && false
-
-   return the first 'false'.  EXPR is the expression.  */
-
-static tree
-find_failing_clause_r (tree expr)
+void
+diagnose_failing_condition (tree bad, location_t cloc, bool show_expr_p)
 {
-  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
+  /* Nobody wants to see the artificial (bool) cast.  */
+  bad = tree_strip_nop_conversions (bad);
+
+  /* Actually explain the failure if this is a concept check or a
+     requires-expression.  */
+  if (concept_check_p (bad) || TREE_CODE (bad) == REQUIRES_EXPR)
+    diagnose_constraints (cloc, bad, NULL_TREE);
+  else if (COMPARISON_CLASS_P (bad)
+	   && ARITHMETIC_TYPE_P (TREE_TYPE (TREE_OPERAND (bad, 0))))
     {
-      /* First check the left side...  */
-      tree e = find_failing_clause_r (TREE_OPERAND (expr, 0));
-      if (e == NULL_TREE)
-	/* ...if we didn't find a false clause, check the right side.  */
-	e = find_failing_clause_r (TREE_OPERAND (expr, 1));
-      return e;
+      tree op0 = fold_non_dependent_expr (TREE_OPERAND (bad, 0));
+      tree op1 = fold_non_dependent_expr (TREE_OPERAND (bad, 1));
+      tree cond = build2 (TREE_CODE (bad), boolean_type_node, op0, op1);
+      inform (cloc, "the comparison reduces to %qE", cond);
     }
-  tree e = contextual_conv_bool (expr, tf_none);
-  e = fold_non_dependent_expr (e, tf_none, /*manifestly_const_eval=*/true);
-  if (integer_zerop (e))
-    /* This is the failing clause.  */
-    return expr;
-  return NULL_TREE;
-}
-
-/* Wrapper for find_failing_clause_r.  */
-
-static tree
-find_failing_clause (tree expr)
-{
-  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
-    if (tree e = find_failing_clause_r (expr))
-      expr = e;
-  return expr;
+  else if (show_expr_p)
+    inform (cloc, "%qE evaluates to false", bad);
 }
 
 /* Build a STATIC_ASSERT for a static assertion with the condition
@@ -11274,12 +11263,12 @@ finish_static_assert (tree condition, tree message, location_t location,
 	  int len = TREE_STRING_LENGTH (message) / sz - 1;
 
 	  /* See if we can find which clause was failing (for logical AND).  */
-	  tree bad = find_failing_clause (orig_condition);
+	  tree bad = find_failing_clause (NULL, orig_condition);
 	  /* If not, or its location is unusable, fall back to the previous
 	     location.  */
 	  location_t cloc = cp_expr_loc_or_loc (bad, location);
-	  /* Nobody wants to see the artificial (bool) cast.  */
-	  bad = tree_strip_nop_conversions (bad);
+
+	  auto_diagnostic_group d;
 
           /* Report the error. */
 	  if (len == 0)
@@ -11288,21 +11277,7 @@ finish_static_assert (tree condition, tree message, location_t location,
 	    error_at (cloc, "static assertion failed: %s",
 		      TREE_STRING_POINTER (message));
 
-	  /* Actually explain the failure if this is a concept check or a
-	     requires-expression.  */
-	  if (concept_check_p (bad)
-	      || TREE_CODE (bad) == REQUIRES_EXPR)
-	    diagnose_constraints (location, bad, NULL_TREE);
-	  else if (COMPARISON_CLASS_P (bad)
-		   && ARITHMETIC_TYPE_P (TREE_TYPE (TREE_OPERAND (bad, 0))))
-	    {
-	      tree op0 = fold_non_dependent_expr (TREE_OPERAND (bad, 0));
-	      tree op1 = fold_non_dependent_expr (TREE_OPERAND (bad, 1));
-	      tree cond = build2 (TREE_CODE (bad), boolean_type_node, op0, op1);
-	      inform (cloc, "the comparison reduces to %qE", cond);
-	    }
-	  else if (show_expr_p)
-	    inform (cloc, "%qE evaluates to false", bad);
+	  diagnose_failing_condition (bad, cloc, show_expr_p);
 	}
       else if (condition && condition != error_mark_node)
 	{
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index a5afb467d23..9ddfcf77e7a 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -9187,6 +9187,20 @@ available for functions (@pxref{Function Attributes}), variables
 (@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), enumerators
 (@pxref{Enumerator Attributes}), and for types (@pxref{Type Attributes}).
 
+@table @code
+@item fallthrough
+@cindex @code{fallthrough} statement attribute
+The @code{fallthrough} attribute with a null statement serves as a
+fallthrough statement.  It hints to the compiler that a statement
+that falls through to another case label, or user-defined label
+in a switch statement is intentional and thus the
+@option{-Wimplicit-fallthrough} warning must not trigger.  The
+fallthrough attribute may appear at most once in each attribute
+list, and may not be mixed with other attributes.  It can only
+be used in a switch statement (the compiler will issue an error
+otherwise), after a preceding statement and before a logically
+succeeding case label, or user-defined label.
+
 This example uses the @code{fallthrough} statement attribute to indicate that
 the @option{-Wimplicit-fallthrough} warning should not be emitted:
 
@@ -9201,19 +9215,28 @@ switch (cond)
   @}
 @end smallexample
 
-@table @code
-@item fallthrough
-@cindex @code{fallthrough} statement attribute
-The @code{fallthrough} attribute with a null statement serves as a
-fallthrough statement.  It hints to the compiler that a statement
-that falls through to another case label, or user-defined label
-in a switch statement is intentional and thus the
-@option{-Wimplicit-fallthrough} warning must not trigger.  The
-fallthrough attribute may appear at most once in each attribute
-list, and may not be mixed with other attributes.  It can only
-be used in a switch statement (the compiler will issue an error
-otherwise), after a preceding statement and before a logically
-succeeding case label, or user-defined label.
+@item assume
+@cindex @code{assume} statement attribute
+The @code{assume} attribute with a null statement serves as portable
+assumption.  It should have a single argument, a conditional expression,
+which is not evaluated.  If the argument would evaluate to true
+at the point where it appears, it has no effect, otherwise there
+is undefined behavior.  This is a GNU variant of the ISO C++23
+standard @code{assume} attribute, but it can be used in any version of
+both C and C++.
+
+@smallexample
+int
+foo (int x, int y)
+@{
+  __attribute__((assume(x == 42)));
+  __attribute__((assume(++y == 43)));
+  return x + y;
+@}
+@end smallexample
+
+@code{y} is not actually incremented and the compiler can but does not
+have to optimize it to just @code{return 42 + 42;}.
 
 @end table
 
diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 4f4ec81c8d4..9f7beae14e5 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -130,7 +130,6 @@ static tree eval_subst (location_t, tree, tree, tree, tree, tree);
 static tree optimize_bit_field_compare (location_t, enum tree_code,
 					tree, tree, tree);
 static bool simple_operand_p (const_tree);
-static bool simple_operand_p_2 (tree);
 static tree range_binop (enum tree_code, tree, tree, int, tree, int);
 static tree range_predecessor (tree);
 static tree range_successor (tree);
@@ -4868,8 +4867,8 @@ sign_bit_p (tree exp, const_tree val)
   return NULL_TREE;
 }
 
-/* Subroutine for fold_truth_andor_1: determine if an operand is simple enough
-   to be evaluated unconditionally.  */
+/* Subroutine for fold_truth_andor_1 and simple_condition_p: determine if an
+   operand is simple enough to be evaluated unconditionally.  */
 
 static bool
 simple_operand_p (const_tree exp)
@@ -4897,13 +4896,12 @@ simple_operand_p (const_tree exp)
 	      && (! TREE_STATIC (exp) || DECL_REGISTER (exp))));
 }
 
-/* Subroutine for fold_truth_andor: determine if an operand is simple enough
-   to be evaluated unconditionally.
-   I addition to simple_operand_p, we assume that comparisons, conversions,
+/* Determine if an operand is simple enough to be evaluated unconditionally.
+   In addition to simple_operand_p, we assume that comparisons, conversions,
    and logic-not operations are simple, if their operands are simple, too.  */
 
-static bool
-simple_operand_p_2 (tree exp)
+bool
+simple_condition_p (tree exp)
 {
   enum tree_code code;
 
@@ -4920,7 +4918,7 @@ simple_operand_p_2 (tree exp)
 	    && simple_operand_p (TREE_OPERAND (exp, 1)));
 
   if (code == TRUTH_NOT_EXPR)
-      return simple_operand_p_2 (TREE_OPERAND (exp, 0));
+    return simple_condition_p (TREE_OPERAND (exp, 0));
 
   return simple_operand_p (exp);
 }
@@ -9787,10 +9785,10 @@ fold_truth_andor (location_t loc, enum tree_code code, tree type,
 	 side-effects, or isn't simple, then we can't add to it,
 	 as otherwise we might destroy if-sequence.  */
       if (TREE_CODE (arg0) == icode
-	  && simple_operand_p_2 (arg1)
+	  && simple_condition_p (arg1)
 	  /* Needed for sequence points to handle trappings, and
 	     side-effects.  */
-	  && simple_operand_p_2 (TREE_OPERAND (arg0, 1)))
+	  && simple_condition_p (TREE_OPERAND (arg0, 1)))
 	{
 	  tem = fold_build2_loc (loc, ncode, type, TREE_OPERAND (arg0, 1),
 				 arg1);
@@ -9800,10 +9798,10 @@ fold_truth_andor (location_t loc, enum tree_code code, tree type,
 	/* Same as above but for (A AND[-IF] (B AND-IF C)) -> ((A AND B) AND-IF C),
 	   or (A OR[-IF] (B OR-IF C) -> ((A OR B) OR-IF C).  */
       else if (TREE_CODE (arg1) == icode
-	  && simple_operand_p_2 (arg0)
+	  && simple_condition_p (arg0)
 	  /* Needed for sequence points to handle trappings, and
 	     side-effects.  */
-	  && simple_operand_p_2 (TREE_OPERAND (arg1, 0)))
+	  && simple_condition_p (TREE_OPERAND (arg1, 0)))
 	{
 	  tem = fold_build2_loc (loc, ncode, type, 
 				 arg0, TREE_OPERAND (arg1, 0));
@@ -9814,8 +9812,8 @@ fold_truth_andor (location_t loc, enum tree_code code, tree type,
 	 into (A OR B).
 	 For sequence point consistancy, we need to check for trapping,
 	 and side-effects.  */
-      else if (code == icode && simple_operand_p_2 (arg0)
-               && simple_operand_p_2 (arg1))
+      else if (code == icode && simple_condition_p (arg0)
+	       && simple_condition_p (arg1))
 	return fold_build2_loc (loc, ncode, type, arg0, arg1);
     }
 
diff --git a/gcc/fold-const.h b/gcc/fold-const.h
index fe78a4d0a58..fa284c712bf 100644
--- a/gcc/fold-const.h
+++ b/gcc/fold-const.h
@@ -215,6 +215,7 @@ extern tree build_range_check (location_t, tree, tree, int, tree, tree);
 extern bool merge_ranges (int *, tree *, tree *, int, tree, tree, int,
 			  tree, tree);
 extern tree sign_bit_p (tree, const_tree);
+extern bool simple_condition_p (tree);
 extern tree exact_inverse (tree, tree);
 extern bool expr_not_equal_to (tree t, const wide_int &);
 extern tree const_unop (enum tree_code, tree, tree);
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 9e0e3429958..95e16f600b5 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -3554,6 +3554,25 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
       enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
       auto_vec<tree> vargs (nargs);
 
+      if (ifn == IFN_ASSUME)
+	{
+	  if (simple_condition_p (CALL_EXPR_ARG (*expr_p, 0)))
+	    {
+	      /* If the [[assume (cond)]]; condition is simple
+		 enough and can be evaluated unconditionally
+		 without side-effects, expand it as
+		 if (!cond) __builtin_unreachable ();  */
+	      tree fndecl = builtin_decl_explicit (BUILT_IN_UNREACHABLE);
+	      *expr_p = build3 (COND_EXPR, void_type_node,
+				CALL_EXPR_ARG (*expr_p, 0), void_node,
+				build_call_expr_loc (EXPR_LOCATION (*expr_p),
+						     fndecl, 0));
+	      return GS_OK;
+	    }
+	  /* FIXME: Otherwise expand it specially.  */
+	  return GS_ALL_DONE;
+	}
+
       for (i = 0; i < nargs; i++)
 	{
 	  gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
index c306240c2ac..de608bd4802 100644
--- a/gcc/internal-fn.cc
+++ b/gcc/internal-fn.cc
@@ -4522,3 +4522,9 @@ expand_TRAP (internal_fn, gcall *)
 {
   expand_builtin_trap ();
 }
+
+void
+expand_ASSUME (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index f49b395be7f..61516dab66d 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -462,6 +462,10 @@ DEF_INTERNAL_FN (TRAP, ECF_CONST | ECF_LEAF | ECF_NORETURN
 		       | ECF_NOTHROW | ECF_COLD | ECF_LOOPING_CONST_OR_PURE,
 		 NULL)
 
+/* [[assume (cond)]].  */
+DEF_INTERNAL_FN (ASSUME, ECF_CONST | ECF_LEAF | ECF_NOTHROW
+			 | ECF_LOOPING_CONST_OR_PURE, NULL)
+
 #undef DEF_INTERNAL_INT_FN
 #undef DEF_INTERNAL_FLT_FN
 #undef DEF_INTERNAL_FLT_FLOATN_FN
diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
index c4c94f80d51..21b1ce43df6 100644
--- a/gcc/internal-fn.h
+++ b/gcc/internal-fn.h
@@ -243,6 +243,7 @@ extern void expand_PHI (internal_fn, gcall *);
 extern void expand_SHUFFLEVECTOR (internal_fn, gcall *);
 extern void expand_SPACESHIP (internal_fn, gcall *);
 extern void expand_TRAP (internal_fn, gcall *);
+extern void expand_ASSUME (internal_fn, gcall *);
 
 extern bool vectorized_internal_fn_supported_p (internal_fn, tree);
 
diff --git a/gcc/testsuite/g++.dg/cpp23/attr-assume1.C b/gcc/testsuite/g++.dg/cpp23/attr-assume1.C
new file mode 100644
index 00000000000..76b61e91f18
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/attr-assume1.C
@@ -0,0 +1,191 @@
+// P1774R8 - Portable assumptions
+// { dg-do run { target c++11 } }
+
+namespace std
+{
+  constexpr bool
+  isfinite (float x)
+  { return __builtin_isfinite (x); }
+
+  constexpr bool
+  isfinite (double x)
+  { return __builtin_isfinite (x); }
+
+  constexpr bool
+  isfinite (long double x)
+  { return __builtin_isfinite (x); }
+
+  constexpr float
+  sqrt (float x)
+  { return __builtin_sqrtf (x); }
+
+  constexpr double
+  sqrt (double x)
+  { return __builtin_sqrt (x); }
+
+  constexpr long double
+  sqrt (long double x)
+  { return __builtin_sqrtl (x); }
+
+  extern "C" void
+  abort ();
+}
+
+constexpr int
+f1 (int i)
+{
+#if __cpp_constexpr >= 201603L
+  auto f = [=] { [[assume (i == 0)]]; };
+  return sizeof (f);
+#else
+  return sizeof (int);
+#endif
+}
+
+void
+f2 ()
+{
+  static_assert (f1 (0) >= sizeof (int), "");
+}
+
+int
+f3 (int i)
+{
+  [[assume (i == 42)]];
+  return i;
+}
+
+int
+f4 (int i)
+{
+  [[assume (++i == 44)]];
+  return i;
+}
+
+int a;
+int *volatile c;
+
+bool
+f5 ()
+{
+  ++a;
+  return true;
+}
+
+constexpr int
+f6 ()
+{
+#if __cpp_constexpr >= 201304L
+  [[assume (f5 ())]];
+#endif
+  return 1;
+}
+
+template <int ...args>
+bool
+f7 ()
+{
+#if __cpp_fold_expressions >= 201411L
+  [[assume (((args >= 0) && ...))]];
+  return ((args >= 0) && ...);
+#else
+  return true;
+#endif
+}
+
+bool
+f8 (double x)
+{
+  [[assume (std::isfinite (x) && x >= 0.0)]];
+  return std::isfinite (std::sqrt (x));
+}
+
+double
+f9 (double x)
+{
+  [[assume (std::isfinite (std::sqrt (x)))]];
+  return std::sqrt (x);
+}
+
+template <typename T, T N>
+T
+f10 (T x)
+{
+  [[assume (x == N)]];
+  return x;
+}
+
+int
+f11 (int x)
+{
+  [[assume (x == 93 ? true : throw 1)]];
+  return x;
+}
+
+constexpr int
+f12 (int x)
+{
+#if __cpp_constexpr >= 201304L
+  [[assume (++x == 43)]];
+#endif
+  return x;
+}
+
+static_assert (f12 (42) == 42, "");
+
+struct S
+{
+  operator bool () { return true; }
+};
+
+int
+f13 ()
+{
+  S s;
+  [[assume (s)]];
+  return 0;
+}
+
+template <typename T>
+int
+f14 ()
+{
+  T t;
+  [[assume (t)]];
+  return 0;
+}
+
+int
+main ()
+{
+  int b = 42;
+  double d = 42.0, e = 43.0;
+  c = &b;
+  [[assume (f5 ())]];
+  if (a)
+    std::abort ();
+  [[assume (++b == 43)]];
+  if (b != 42 || *c != 42)
+    std::abort ();
+  static_assert (f6 () == 1, "");
+  if (f6 () != 1)
+    std::abort ();
+  if (a)
+    std::abort ();
+  if (!f7 <0> () || !f7 <1, 2, 3, 4> ())
+    std::abort ();
+  [[assume (d < e)]];
+  if (f10 <int, 45> (45) != 45
+      || f10 <long long, 128LL> (128LL) != 128LL
+#if __cpp_nontype_template_args >= 201911L
+      || f10 <long double, -42.0L> (-42.0L) != -42.0L
+#endif
+      || false)
+    std::abort ();
+  int i = 90, j = 91, k = 92;
+  [[assume (i == 90), assume (j <= 91)]] [[assume (k >= 92)]];
+  if (f11 (93) != 93)
+    std::abort ();
+  if (f14 <S> () != 0)
+    std::abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/attr-assume2.C b/gcc/testsuite/g++.dg/cpp23/attr-assume2.C
new file mode 100644
index 00000000000..9e54c14974f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/attr-assume2.C
@@ -0,0 +1,83 @@
+// P1774R8 - Portable assumptions
+// { dg-do compile { target c++11 } }
+
+[[assume (true)]] void f1 ();		// { dg-error "'assume' attribute ignored" }
+typedef int intx [[assume (true)]];	// { dg-error "'assume' attribute ignored" }
+[[assume (true)]];			// { dg-warning "attribute ignored" }
+
+void
+foo ()
+{
+  int i;
+  [[assume]];				// { dg-error "wrong number of arguments specified for 'assume' attribute" }
+					// { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
+  [[assume ()]];			// { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" }
+					// { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 }
+					// { dg-message "expected 1, found 0" "" { target *-*-* } .-2 }
+  [[assume (true, true)]];		// { dg-error "wrong number of arguments specified for 'assume' attribute" }
+					// { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
+  [[assume (true)]] i = 1;		// { dg-warning "'assume' attribute not followed by ';'" }
+  [[assume (throw 1)]];			// { dg-error "expected primary-expression before 'throw'" }
+  [[assume (i = 1)]];			// { dg-error "expected '\\\)' before '=' token" }
+}
+
+constexpr int
+f2 (int x)
+{
+#if __cpp_constexpr >= 201304L
+  [[assume (x == 42)]];			// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
+#endif					// { dg-message "the comparison reduces to '\\\(x == 42\\\)'" "" { target c++14 } .-1 }
+  return x;
+}
+
+constexpr int a = f2 (44);
+
+int
+f3 (int x)
+{
+  __asm ("" : "+r" (x));
+  return x;
+}
+
+constexpr int
+f4 (int x)
+{
+#if __cpp_constexpr >= 201304L
+  [[assume (f3 (42) == 42)]];
+#endif
+  return x;
+}
+
+static_assert (f4 (42) == 42, "");
+
+struct S {};
+
+int
+f5 ()
+{
+  S s;
+  [[assume (s)]];			// { dg-error "could not convert 's' from 'S' to 'bool'" }
+  return 0;
+}
+
+template <typename T>
+int
+f6 ()
+{
+  T t;
+  [[assume (t)]];			// { dg-error "could not convert 't' from 'S' to 'bool'" }
+  return 0;
+}
+
+int z = f6 <S> ();
+
+constexpr int
+f7 (int x, int y, int z, int w)
+{
+#if __cpp_constexpr >= 201304L
+  [[assume (x == 42 && y == 43 && z == 44 && w == 45)]];	// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
+#endif					// { dg-message "the comparison reduces to '\\\(z == 44\\\)'" "" { target c++14 } .-1 }
+  return x;
+}
+
+constexpr int w = f7 (42, 43, 45, 44);
diff --git a/gcc/testsuite/g++.dg/cpp23/attr-assume3.C b/gcc/testsuite/g++.dg/cpp23/attr-assume3.C
new file mode 100644
index 00000000000..0be28c7ac83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/attr-assume3.C
@@ -0,0 +1,198 @@
+// P1774R8 - Portable assumptions
+// { dg-do run { target c++11 } }
+
+namespace std
+{
+  constexpr bool
+  isfinite (float x)
+  { return __builtin_isfinite (x); }
+
+  constexpr bool
+  isfinite (double x)
+  { return __builtin_isfinite (x); }
+
+  constexpr bool
+  isfinite (long double x)
+  { return __builtin_isfinite (x); }
+
+  constexpr float
+  sqrt (float x)
+  { return __builtin_sqrtf (x); }
+
+  constexpr double
+  sqrt (double x)
+  { return __builtin_sqrt (x); }
+
+  constexpr long double
+  sqrt (long double x)
+  { return __builtin_sqrtl (x); }
+
+  extern "C" void
+  abort ();
+}
+
+constexpr int
+f1 (int i)
+{
+#if __cpp_constexpr >= 201603L
+  auto f = [=] { [[__assume__ (i == 0)]]; };
+  return sizeof (f);
+#else
+  return sizeof (int);
+#endif
+}
+
+void
+f2 ()
+{
+  static_assert (f1 (0) >= sizeof (int), "");
+}
+
+int
+f3 (int i)
+{
+  [[gnu::assume (i == 42)]];
+  return i;
+}
+
+int
+f4 (int i)
+{
+  __attribute__ ((assume (++i == 44)));
+  return i;
+}
+
+int a;
+int *volatile c;
+
+bool
+f5 ()
+{
+  ++a;
+  return true;
+}
+
+constexpr int
+f6 ()
+{
+#if __cpp_constexpr >= 201304L
+  [[__assume__ (f5 ())]];
+#endif
+  return 1;
+}
+
+template <int ...args>
+bool
+f7 ()
+{
+#if __cpp_fold_expressions >= 201411L
+  [[__gnu__::__assume__ (((args >= 0) && ...))]];
+  return ((args >= 0) && ...);
+#else
+  return true;
+#endif
+}
+
+bool
+f8 (double x)
+{
+  [[gnu::assume (std::isfinite (x) && x >= 0.0)]];
+  return std::isfinite (std::sqrt (x));
+}
+
+double
+f9 (double x)
+{
+  __attribute__((assume (std::isfinite (std::sqrt (x)))));
+  return std::sqrt (x);
+}
+
+template <typename T, T N>
+T
+f10 (T x)
+{
+  [[__assume__ (x == N)]];
+  return x;
+}
+
+int
+f11 (int x)
+{
+  [[gnu::assume (x == 93 ? true : throw 1)]];
+  return x;
+}
+
+constexpr int
+f12 (int x)
+{
+#if __cpp_constexpr >= 201304L
+  __attribute__((assume (++x == 43)));
+#endif
+  return x;
+}
+
+static_assert (f12 (42) == 42, "");
+
+struct S
+{
+  operator bool () { return true; }
+};
+
+int
+f13 ()
+{
+  S s;
+  [[__gnu__::__assume__ (s)]];
+  return 0;
+}
+
+template <typename T>
+int
+f14 ()
+{
+  T t;
+  __attribute__((assume (t)));
+  return 0;
+}
+
+int
+main ()
+{
+  int b = 42;
+  double d = 42.0, e = 43.0;
+  c = &b;
+  [[__assume__ (f5 ())]];
+  if (a)
+    std::abort ();
+  [[gnu::assume (++b == 43)]];
+  if (b != 42 || *c != 42)
+    std::abort ();
+  static_assert (f6 () == 1, "");
+  if (f6 () != 1)
+    std::abort ();
+  if (a)
+    std::abort ();
+  if (!f7 <0> () || !f7 <1, 2, 3, 4> ())
+    std::abort ();
+  __attribute__((assume (d < e)));
+  if (f10 <int, 45> (45) != 45
+      || f10 <long long, 128LL> (128LL) != 128LL
+#if __cpp_nontype_template_args >= 201911L
+      || f10 <long double, -42.0L> (-42.0L) != -42.0L
+#endif
+      || false)
+    std::abort ();
+  int i = 90, j = 91, k = 92;
+  [[__assume__ (i == 90), gnu::assume (j <= 91)]]
+#if __cplusplus >= 201703L
+  [[using gnu:assume (k >= 92)]]
+#else
+  [[gnu::assume (k >= 92)]]
+#endif
+  ;
+  __attribute__((__assume__ (i == 90), assume (j <= 91))) __attribute__((assume (k >= 92)));
+  if (f11 (93) != 93)
+    std::abort ();
+  if (f14 <S> () != 0)
+    std::abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/attr-assume4.C b/gcc/testsuite/g++.dg/cpp23/attr-assume4.C
new file mode 100644
index 00000000000..059d47bbd92
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/attr-assume4.C
@@ -0,0 +1,136 @@
+// P1774R8 - Portable assumptions
+// { dg-do compile { target c++11 } }
+
+[[__assume__ (true)]] void f1 ();		// { dg-error "'assume' attribute ignored" }
+typedef int intx [[__assume__ (true)]];		// { dg-error "'assume' attribute ignored" }
+[[__assume__ (true)]];				// { dg-warning "attribute ignored" }
+[[gnu::assume (true)]] void f1a ();		// { dg-error "'assume' attribute ignored" }
+typedef int inty [[gnu::__assume__ (true)]];	// { dg-error "'assume' attribute ignored" }
+[[__gnu__::assume (true)]];			// { dg-warning "attribute ignored" }
+__attribute__((assume (true))) void f1b ();	// { dg-error "'assume' attribute ignored" }
+typedef int intz __attribute__((assume (true)));// { dg-error "'assume' attribute ignored" }
+
+void
+foo ()
+{
+  int i;
+  [[__assume__]];			// { dg-error "wrong number of arguments specified for 'assume' attribute" }
+					// { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
+  [[__assume__ ()]];			// { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" }
+					// { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 }
+					// { dg-message "expected 1, found 0" "" { target *-*-* } .-2 }
+  [[__assume__ (true, true)]];		// { dg-error "wrong number of arguments specified for 'assume' attribute" }
+					// { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
+  [[__assume__ (true)]] i = 1;		// { dg-warning "'assume' attribute not followed by ';'" }
+  [[__assume__ (throw 1)]];		// { dg-error "expected primary-expression before 'throw'" }
+  [[__assume__ (i = 1)]];		// { dg-error "expected '\\\)' before '=' token" }
+  [[gnu::assume]];			// { dg-error "wrong number of arguments specified for 'assume' attribute" }
+					// { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
+  [[gnu::assume ()]];			// { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" }
+					// { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 }
+					// { dg-message "expected 1, found 0" "" { target *-*-* } .-2 }
+  [[gnu::assume (true, true)]];		// { dg-error "wrong number of arguments specified for 'assume' attribute" }
+					// { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
+  [[gnu::assume (true)]] i = 1;		// { dg-warning "'assume' attribute not followed by ';'" }
+  [[gnu::assume (throw 1)]];		// { dg-error "expected primary-expression before 'throw'" }
+  [[gnu::assume (i = 1)]];		// { dg-error "expected '\\\)' before '=' token" }
+  __attribute__((assume));		// { dg-error "wrong number of arguments specified for 'assume' attribute" }
+					// { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
+  __attribute__((assume ()));		// { dg-error "wrong number of arguments specified for 'assume' attribute" }
+					// { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
+  __attribute__((assume (true, true)));	// { dg-error "wrong number of arguments specified for 'assume' attribute" }
+					// { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
+  __attribute__((assume (true))) i = 1;	// { dg-warning "'assume' attribute not followed by ';'" }
+  __attribute__((assume (throw 1)));	// { dg-error "expected primary-expression before 'throw'" }
+  __attribute__((assume (i = 1)));	// { dg-error "expected '\\\)' before '=' token" }
+}
+
+constexpr int
+f2 (int x)
+{
+#if __cpp_constexpr >= 201304L
+  [[__assume__ (x == 42)]];		// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
+#endif
+  return x;
+}
+
+constexpr int
+f2a (int x)
+{
+#if __cpp_constexpr >= 201304L
+  [[gnu::__assume__ (x == 42)]];	// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
+#endif
+  return x;
+}
+
+constexpr int
+f2b (int x)
+{
+#if __cpp_constexpr >= 201304L
+  __attribute__((__assume__ (x == 42)));// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
+#endif
+  return x;
+}
+
+constexpr int a = f2 (44);
+constexpr int aa = f2a (44);
+constexpr int ab = f2b (44);
+
+int
+f3 (int x)
+{
+  __asm ("" : "+r" (x));
+  return x;
+}
+
+constexpr int
+f4 (int x)
+{
+#if __cpp_constexpr >= 201304L
+  [[__assume__ (f3 (42) == 42)]];
+#endif
+  return x;
+}
+
+constexpr int
+f4a (int x)
+{
+#if __cpp_constexpr >= 201304L
+  [[gnu::assume (f3 (42) == 42)]];
+#endif
+  return x;
+}
+
+constexpr int
+f4b (int x)
+{
+#if __cpp_constexpr >= 201304L
+  __attribute__((assume (f3 (42) == 42)));
+#endif
+  return x;
+}
+
+static_assert (f4 (42) == 42, "");
+static_assert (f4a (42) == 42, "");
+static_assert (f4b (42) == 42, "");
+
+struct S {};
+
+int
+f5 ()
+{
+  S s;
+  [[gnu::assume (s)]];			// { dg-error "could not convert 's' from 'S' to 'bool'" }
+  return 0;
+}
+
+template <typename T>
+int
+f6 ()
+{
+  T t;
+  __attribute__((assume (t)));		// { dg-error "could not convert 't' from 'S' to 'bool'" }
+  return 0;
+}
+
+int z = f6 <S> ();
diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
index b52cf378b41..efe97703a98 100644
--- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
+++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
@@ -422,7 +422,7 @@
 #  error "__cpp_nontype_template_parameter_auto != 201606"
 #endif
 
-// C++20 features
+// C++20 features:
 
 #ifndef __cpp_conditional_explicit
 #  error "__cpp_conditional_explicit"
@@ -460,6 +460,44 @@
 #  error "__cpp_aggregate_paren_init != 201902"
 #endif
 
+#ifndef __cpp_char8_t
+#  error "__cpp_char8_t"
+#elif __cpp_char8_t != 202207
+#  error "__cpp_char8_t != 202207"
+#endif
+
+#ifndef __cpp_designated_initializers
+#  error "__cpp_designated_initializers"
+#elif __cpp_designated_initializers != 201707
+#  error "__cpp_designated_initializers != 201707"
+#endif
+
+#ifndef __cpp_constexpr_in_decltype
+#  error "__cpp_constexpr_in_decltype"
+#elif __cpp_constexpr_in_decltype != 201711
+#  error "__cpp_constexpr_in_decltype != 201711"
+#endif
+
+#ifndef __cpp_consteval
+#  error "__cpp_consteval"
+#elif __cpp_consteval != 201811
+#  error "__cpp_consteval != 201811"
+#endif
+
+#ifndef __cpp_concepts
+#  error "__cpp_concepts"
+#elif __cpp_concepts != 202002
+#  error "__cpp_concepts != 202002"
+#endif
+
+#ifndef __cpp_using_enum
+#  error "__cpp_using_enum"
+#elif __cpp_using_enum != 201907
+#  error "__cpp_using_enum != 201907"
+#endif
+
+// C++20 attributes:
+
 #ifdef __has_cpp_attribute
 
 #  if ! __has_cpp_attribute(maybe_unused)
@@ -502,42 +540,6 @@
 #  error "__has_cpp_attribute"
 #endif
 
-#ifndef __cpp_char8_t
-#  error "__cpp_char8_t"
-#elif __cpp_char8_t != 202207
-#  error "__cpp_char8_t != 202207"
-#endif
-
-#ifndef __cpp_designated_initializers
-#  error "__cpp_designated_initializers"
-#elif __cpp_designated_initializers != 201707
-#  error "__cpp_designated_initializers != 201707"
-#endif
-
-#ifndef __cpp_constexpr_in_decltype
-#  error "__cpp_constexpr_in_decltype"
-#elif __cpp_constexpr_in_decltype != 201711
-#  error "__cpp_constexpr_in_decltype != 201711"
-#endif
-
-#ifndef __cpp_consteval
-#  error "__cpp_consteval"
-#elif __cpp_consteval != 201811
-#  error "__cpp_consteval != 201811"
-#endif
-
-#ifndef __cpp_concepts
-#  error "__cpp_concepts"
-#elif __cpp_concepts != 202002
-#  error "__cpp_concepts != 202002"
-#endif
-
-#ifndef __cpp_using_enum
-#  error "__cpp_using_enum"
-#elif __cpp_using_enum != 201907
-#  error "__cpp_using_enum != 201907"
-#endif
-
 // C++23 features:
 
 #ifndef __cpp_size_t_suffix
@@ -575,3 +577,15 @@
 #elif __cpp_implicit_move != 202207
 #  error "__cpp_implicit_move != 202207"
 #endif
+
+//  C++23 attributes:
+
+#ifdef __has_cpp_attribute
+#  if ! __has_cpp_attribute(assume)
+#    error "__has_cpp_attribute(assume)"
+#  elif __has_cpp_attribute(assume) != 202207
+#    error "__has_cpp_attribute(assume) != 202207"
+#  endif
+#else
+#  error "__has_cpp_attribute"
+#endif
diff --git a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 02f3a377fd0..16bc0b85395 100644
--- a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -422,7 +422,7 @@
 #  error "__cpp_nontype_template_parameter_auto != 201606"
 #endif
 
-// C++20 features
+// C++20 features:
 
 #ifndef __cpp_conditional_explicit
 #  error "__cpp_conditional_explicit"
@@ -460,6 +460,44 @@
 #  error "__cpp_aggregate_paren_init != 201902"
 #endif
 
+#ifndef __cpp_char8_t
+#  error "__cpp_char8_t"
+#elif __cpp_char8_t != 202207
+#  error "__cpp_char8_t != 202207"
+#endif
+
+#ifndef __cpp_designated_initializers
+#  error "__cpp_designated_initializers"
+#elif __cpp_designated_initializers != 201707
+#  error "__cpp_designated_initializers != 201707"
+#endif
+
+#ifndef __cpp_constexpr_in_decltype
+#  error "__cpp_constexpr_in_decltype"
+#elif __cpp_constexpr_in_decltype != 201711
+#  error "__cpp_constexpr_in_decltype != 201711"
+#endif
+
+#ifndef __cpp_consteval
+#  error "__cpp_consteval"
+#elif __cpp_consteval != 201811
+#  error "__cpp_consteval != 201811"
+#endif
+
+#ifndef __cpp_concepts
+#  error "__cpp_concepts"
+#elif __cpp_concepts != 202002
+#  error "__cpp_concepts != 202002"
+#endif
+
+#ifndef __cpp_using_enum
+#  error "__cpp_using_enum"
+#elif __cpp_using_enum != 201907
+#  error "__cpp_using_enum != 201907"
+#endif
+
+// C++20 attributes:
+
 #ifdef __has_cpp_attribute
 
 #  if ! __has_cpp_attribute(maybe_unused)
@@ -501,39 +539,3 @@
 #else
 #  error "__has_cpp_attribute"
 #endif
-
-#ifndef __cpp_char8_t
-#  error "__cpp_char8_t"
-#elif __cpp_char8_t != 202207
-#  error "__cpp_char8_t != 202207"
-#endif
-
-#ifndef __cpp_designated_initializers
-#  error "__cpp_designated_initializers"
-#elif __cpp_designated_initializers != 201707
-#  error "__cpp_designated_initializers != 201707"
-#endif
-
-#ifndef __cpp_constexpr_in_decltype
-#  error "__cpp_constexpr_in_decltype"
-#elif __cpp_constexpr_in_decltype != 201711
-#  error "__cpp_constexpr_in_decltype != 201711"
-#endif
-
-#ifndef __cpp_consteval
-#  error "__cpp_consteval"
-#elif __cpp_consteval != 201811
-#  error "__cpp_consteval != 201811"
-#endif
-
-#ifndef __cpp_concepts
-#  error "__cpp_concepts"
-#elif __cpp_concepts != 202002
-#  error "__cpp_concepts != 202002"
-#endif
-
-#ifndef __cpp_using_enum
-#  error "__cpp_using_enum"
-#elif __cpp_using_enum != 201907
-#  error "__cpp_using_enum != 201907"
-#endif
diff --git a/gcc/testsuite/gcc.dg/attr-assume-1.c b/gcc/testsuite/gcc.dg/attr-assume-1.c
new file mode 100644
index 00000000000..16e919ef7bc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-assume-1.c
@@ -0,0 +1,69 @@
+/* Portable assumptions */
+/* { dg-do run } */
+/* { dg-options "-std=c2x" } */
+
+int
+f1 (int i)
+{
+  [[gnu::assume (i == 42)]];
+  return i;
+}
+
+int
+f2 (int i)
+{
+  __attribute__ ((assume (++i == 44)));
+  return i;
+}
+
+int a;
+int *volatile c;
+
+int
+f3 ()
+{
+  ++a;
+  return 1;
+}
+
+int
+f4 (double x)
+{
+  [[gnu::assume (__builtin_isfinite (x) && x >= 0.0)]];
+  return __builtin_isfinite (__builtin_sqrt (x));
+}
+
+double
+f5 (double x)
+{
+  __attribute__((assume (__builtin_isfinite (__builtin_sqrt (x)))));
+  return __builtin_sqrt (x);
+}
+
+int
+f6 (int x)
+{
+  [[gnu::assume (x == 93 ? 1 : 0)]];
+  return x;
+}
+
+int
+main ()
+{
+  int b = 42;
+  double d = 42.0, e = 43.0;
+  c = &b;
+  [[__gnu__::__assume__ (f3 ())]];
+  if (a)
+    __builtin_abort ();
+  [[gnu::assume (++b == 43)]];
+  if (b != 42 || *c != 42)
+    __builtin_abort ();
+  __attribute__((assume (d < e)));
+  int i = 90, j = 91, k = 92;
+  [[gnu::__assume__ (i == 90), gnu::assume (j <= 91)]] [[gnu::assume (k >= 92)]]
+  ;
+  __attribute__((__assume__ (i == 90), assume (j <= 91))) __attribute__((assume (k >= 92)));
+  if (f6 (93) != 93)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.dg/attr-assume-2.c b/gcc/testsuite/gcc.dg/attr-assume-2.c
new file mode 100644
index 00000000000..aa782e737e3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-assume-2.c
@@ -0,0 +1,66 @@
+/* Portable assumptions */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x" } */
+
+[[gnu::__assume__ (1)]] void f1 (void);	/* { dg-warning "'assume' attribute not followed by ';'" } */
+					/* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */
+typedef int intx [[gnu::assume (1)]];	/* { dg-warning "'assume' attribute ignored" } */
+[[__gnu__::assume (1)]];		/* { dg-warning "'assume' attribute at top level" } */
+__attribute__((assume (1))) void f1b ();/* { dg-warning "'assume' attribute not followed by ';'" } */
+					/* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */
+typedef int inty __attribute__((assume (1)));	/* { dg-warning "'assume' attribute ignored" } */
+
+void
+foo ()
+{
+  int i;
+  [[gnu::assume]];			/* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
+					/* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */
+  [[gnu::__assume__ ()]];		/* { dg-error "parentheses must be omitted if attribute argument list is empty" } */
+					/* { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 } */
+					/* { dg-message "expected 1, found 0" "" { target *-*-* } .-2 } */
+  [[gnu::assume (1, 1)]];		/* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
+					/* { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } */
+  [[gnu::assume (1)]] i = 1;		/* { dg-warning "'assume' attribute ignored" } */
+  [[gnu::assume (i = 1)]];		/* { dg-error "expected" } */
+					/* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */
+  __attribute__((assume));		/* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
+					/* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */
+  __attribute__((assume ()));		/* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
+					/* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */
+  __attribute__((assume (1, 1)));	/* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
+					/* { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } */
+  __attribute__((assume (i = 1)));	/* { dg-error "expected" } */
+}
+
+int
+f2 (int x)
+{
+  __asm ("" : "+r" (x));
+  return x;
+}
+
+int
+f3 (int x)
+{
+  [[gnu::assume (f2 (42) == 42)]];
+  return x;
+}
+
+int
+f3a (int x)
+{
+  __attribute__((assume (f2 (42) == 42)));
+  return x;
+}
+
+struct S {};
+
+int
+f4 ()
+{
+  struct S s;
+  [[gnu::assume (s)]];			/* { dg-error "used struct type value where scalar is required" } */
+  __attribute__((assume (s)));		/* { dg-error "used struct type value where scalar is required" } */
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/attr-assume-3.c b/gcc/testsuite/gcc.dg/attr-assume-3.c
new file mode 100644
index 00000000000..c611a8f8e74
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-assume-3.c
@@ -0,0 +1,35 @@
+/* Portable assumptions */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x" } */
+
+void
+foo (int x)
+{
+  if (x == 1)
+    goto l1;						/* { dg-error "jump into statement expression" } */
+  else if (x == 2)
+    goto l2;						/* { dg-error "jump into statement expression" } */
+  else if (x == 3)
+    goto l3;						/* { dg-error "jump into statement expression" } */
+  [[gnu::assume (({ l0:; if (x == 0) goto l0; 1; }))]];
+  [[gnu::assume (({ if (x == 0) __builtin_abort (); 1; }))]];
+  [[gnu::assume (({ l1:; 1; }))]];			/* { dg-message "label 'l1' defined here" } */
+  [[gnu::assume (({ l2:; 1; }))]];			/* { dg-message "label 'l2' defined here" } */
+  __attribute__((assume (({ l3:; 1; }))));		/* { dg-message "label 'l3' defined here" } */
+  [[gnu::assume (({ l4:; 1; }))]];			/* { dg-message "label 'l4' defined here" } */
+  [[gnu::assume (({ l5:; 1; }))]];			/* { dg-message "label 'l5' defined here" } */
+  __attribute__((assume (({ l6:; 1; }))));		/* { dg-message "label 'l6' defined here" } */
+  switch (x)						/* { dg-message "switch starts here" } */
+    {
+    case 7:
+      [[gnu::assume (({ case 8:; 1; }))]];		/* { dg-error "switch jumps into statement expression" } */
+      __attribute__((assume (({ default:; 1; }))));	/* { dg-error "switch jumps into statement expression" } */
+      break;
+    }
+  if (x == 4)
+    goto l4;						/* { dg-error "jump into statement expression" } */
+  else if (x == 5)
+    goto l5;						/* { dg-error "jump into statement expression" } */
+  else if (x == 6)
+    goto l6;						/* { dg-error "jump into statement expression" } */
+}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-10-06  7:00 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-06  7:00 [gcc r13-3106] c++, c: Implement C++23 P1774R8 - Portable assumptions [PR106654] Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).