public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* openmp: Implement OpenMP 5.1 atomics, so far for C only
@ 2021-09-10 18:46 Jakub Jelinek
  2021-09-13 11:57 ` Christophe Lyon
  0 siblings, 1 reply; 4+ messages in thread
From: Jakub Jelinek @ 2021-09-10 18:46 UTC (permalink / raw)
  To: gcc-patches; +Cc: Tobias Burnus

Hi!

This patch implements OpenMP 5.1 atomics (with clarifications from upcoming 5.2).
The most important changes are that it is now possible to write (for C/C++,
for Fortran it was possible before already) min/max atomics and more importantly
compare and exchange in various forms.
Also, acq_rel is now allowed on read/write and acq_rel/acquire are allowed on
update, and there are new compare, weak and fail clauses.

Bootstrapped/regtested on x86_64-linux and i686-linux, committed to trunk.

C++ support will follow next week hopefully.  Various new tests in
c-c++-common are now with { target c }, that is temporary until the C++
support is there.

2021-09-10  Jakub Jelinek  <jakub@redhat.com>

gcc/
	* tree-core.h (enum omp_memory_order): Add OMP_MEMORY_ORDER_MASK,
	OMP_FAIL_MEMORY_ORDER_UNSPECIFIED, OMP_FAIL_MEMORY_ORDER_RELAXED,
	OMP_FAIL_MEMORY_ORDER_ACQUIRE, OMP_FAIL_MEMORY_ORDER_RELEASE,
	OMP_FAIL_MEMORY_ORDER_ACQ_REL, OMP_FAIL_MEMORY_ORDER_SEQ_CST and
	OMP_FAIL_MEMORY_ORDER_MASK enumerators.
	(OMP_FAIL_MEMORY_ORDER_SHIFT): Define.
	* gimple-pretty-print.c (dump_gimple_omp_atomic_load,
	dump_gimple_omp_atomic_store): Print [weak] for weak atomic
	load/store.
	* gimple.h (enum gf_mask): Change GF_OMP_ATOMIC_MEMORY_ORDER
	to 6-bit mask, adjust GF_OMP_ATOMIC_NEED_VALUE value and add
	GF_OMP_ATOMIC_WEAK.
	(gimple_omp_atomic_weak_p, gimple_omp_atomic_set_weak): New inline
	functions.
	* tree.h (OMP_ATOMIC_WEAK): Define.
	* tree-pretty-print.c (dump_omp_atomic_memory_order): Adjust for
	fail memory order being encoded in the same enum and also print
	fail clause if present.
	(dump_generic_node): Print weak clause if OMP_ATOMIC_WEAK.
	* gimplify.c (goa_stabilize_expr): Add target_expr and rhs arguments,
	handle pre_p == NULL case as a test mode that only returns value
	but doesn't change gimplify nor change anything otherwise, adjust
	recursive calls, add MODIFY_EXPR, ADDR_EXPR, COND_EXPR, TARGET_EXPR
	and CALL_EXPR handling, adjust COMPOUND_EXPR handling for
	__builtin_clear_padding calls, for !rhs gimplify as lvalue rather
	than rvalue.
	(gimplify_omp_atomic): Adjust goa_stabilize_expr caller.  Handle
	COND_EXPR rhs.  Set weak flag on gimple load/store for
	OMP_ATOMIC_WEAK.
	* omp-expand.c (omp_memory_order_to_fail_memmodel): New function.
	(omp_memory_order_to_memmodel): Adjust for fail clause encoded
	in the same enum.
	(expand_omp_atomic_cas): New function.
	(expand_omp_atomic_pipeline): Use omp_memory_order_to_fail_memmodel
	function.
	(expand_omp_atomic): Attempt to optimize atomic compare and exchange
	using expand_omp_atomic_cas.
gcc/c-family/
	* c-common.h (c_finish_omp_atomic): Add r and weak arguments.
	* c-omp.c: Include gimple-fold.h.
	(c_finish_omp_atomic): Add r and weak arguments.  Add support for
	OpenMP 5.1 atomics.
gcc/c/
	* c-parser.c (c_parser_conditional_expression): If omp_atomic_lhs and
	cond.value is >, < or == with omp_atomic_lhs as one of the operands,
	don't call build_conditional_expr, instead build a COND_EXPR directly.
	(c_parser_binary_expression): Avoid calling parser_build_binary_op
	if omp_atomic_lhs even in more cases for >, < or ==.
	(c_parser_omp_atomic): Update function comment for OpenMP 5.1 atomics,
	parse OpenMP 5.1 atomics and fail, compare and weak clauses, allow
	acq_rel on atomic read/write and acq_rel/acquire clauses on update.
	* c-typeck.c (build_binary_op): For flag_openmp only handle
	MIN_EXPR/MAX_EXPR.
gcc/cp/
	* parser.c (cp_parser_omp_atomic): Allow acq_rel on atomic read/write
	and acq_rel/acquire clauses on update.
	* semantics.c (finish_omp_atomic): Adjust c_finish_omp_atomic caller.
gcc/testsuite/
	* c-c++-common/gomp/atomic-17.c (foo): Add tests for atomic read,
	write or update with acq_rel clause and atomic update with acquire clause.
	* c-c++-common/gomp/atomic-18.c (foo): Adjust expected diagnostics
	wording, remove tests moved to atomic-17.c.
	* c-c++-common/gomp/atomic-21.c: Expect only 2 omp atomic release and
	2 omp atomic acq_rel directives instead of 4 omp atomic release.
	* c-c++-common/gomp/atomic-25.c: New test.
	* c-c++-common/gomp/atomic-26.c: New test.
	* c-c++-common/gomp/atomic-27.c: New test.
	* c-c++-common/gomp/atomic-28.c: New test.
	* c-c++-common/gomp/atomic-29.c: New test.
	* c-c++-common/gomp/atomic-30.c: New test.
	* c-c++-common/goacc-gomp/atomic.c: Expect 1 omp atomic release and
	1 omp atomic_acq_rel instead of 2 omp atomic release directives.
	* gcc.dg/gomp/atomic-5.c: Adjust expected error diagnostic wording.
	* g++.dg/gomp/atomic-18.C:Expect 4 omp atomic release and
	1 omp atomic_acq_rel instead of 5 omp atomic release directives.
libgomp/
	* testsuite/libgomp.c-c++-common/atomic-19.c: New test.
	* testsuite/libgomp.c-c++-common/atomic-20.c: New test.
	* testsuite/libgomp.c-c++-common/atomic-21.c: New test.

--- gcc/tree-core.h.jj	2021-09-09 10:40:20.116260919 +0200
+++ gcc/tree-core.h	2021-09-09 10:41:42.631084540 +0200
@@ -583,8 +583,17 @@ enum omp_memory_order {
   OMP_MEMORY_ORDER_ACQUIRE,
   OMP_MEMORY_ORDER_RELEASE,
   OMP_MEMORY_ORDER_ACQ_REL,
-  OMP_MEMORY_ORDER_SEQ_CST
+  OMP_MEMORY_ORDER_SEQ_CST,
+  OMP_MEMORY_ORDER_MASK = 7,
+  OMP_FAIL_MEMORY_ORDER_UNSPECIFIED = OMP_MEMORY_ORDER_UNSPECIFIED * 8,
+  OMP_FAIL_MEMORY_ORDER_RELAXED = OMP_MEMORY_ORDER_RELAXED * 8,
+  OMP_FAIL_MEMORY_ORDER_ACQUIRE = OMP_MEMORY_ORDER_ACQUIRE * 8,
+  OMP_FAIL_MEMORY_ORDER_RELEASE = OMP_MEMORY_ORDER_RELEASE * 8,
+  OMP_FAIL_MEMORY_ORDER_ACQ_REL = OMP_MEMORY_ORDER_ACQ_REL * 8,
+  OMP_FAIL_MEMORY_ORDER_SEQ_CST = OMP_MEMORY_ORDER_SEQ_CST * 8,
+  OMP_FAIL_MEMORY_ORDER_MASK = OMP_MEMORY_ORDER_MASK * 8
 };
+#define OMP_FAIL_MEMORY_ORDER_SHIFT 3
 
 /* There is a TYPE_QUAL value for each type qualifier.  They can be
    combined by bitwise-or to form the complete set of qualifiers for a
--- gcc/gimple-pretty-print.c.jj	2021-09-09 10:40:20.032262117 +0200
+++ gcc/gimple-pretty-print.c	2021-09-09 10:41:42.625084625 +0200
@@ -2563,6 +2563,8 @@ dump_gimple_omp_atomic_load (pretty_prin
 				    gimple_omp_atomic_memory_order (gs));
       if (gimple_omp_atomic_need_value_p (gs))
 	pp_string (buffer, " [needed]");
+      if (gimple_omp_atomic_weak_p (gs))
+	pp_string (buffer, " [weak]");
       newline_and_indent (buffer, spc + 2);
       dump_generic_node (buffer, gimple_omp_atomic_load_lhs (gs),
 	  		 spc, flags, false);
@@ -2597,6 +2599,8 @@ dump_gimple_omp_atomic_store (pretty_pri
       pp_space (buffer);
       if (gimple_omp_atomic_need_value_p (gs))
 	pp_string (buffer, "[needed] ");
+      if (gimple_omp_atomic_weak_p (gs))
+	pp_string (buffer, "[weak] ");
       pp_left_paren (buffer);
       dump_generic_node (buffer, gimple_omp_atomic_store_val (gs),
 	  		 spc, flags, false);
--- gcc/gimple.h.jj	2021-09-09 10:40:20.041261988 +0200
+++ gcc/gimple.h	2021-09-09 10:41:42.625084625 +0200
@@ -194,8 +194,9 @@ enum gf_mask {
     GF_OMP_RETURN_NOWAIT	= 1 << 0,
 
     GF_OMP_SECTION_LAST		= 1 << 0,
-    GF_OMP_ATOMIC_MEMORY_ORDER  = (1 << 3) - 1,
-    GF_OMP_ATOMIC_NEED_VALUE	= 1 << 3,
+    GF_OMP_ATOMIC_MEMORY_ORDER  = (1 << 6) - 1,
+    GF_OMP_ATOMIC_NEED_VALUE	= 1 << 6,
+    GF_OMP_ATOMIC_WEAK		= 1 << 7,
     GF_PREDICT_TAKEN		= 1 << 15
 };
 
@@ -2446,6 +2447,29 @@ gimple_omp_atomic_set_need_value (gimple
 }
 
 
+/* Return true if OMP atomic load/store statement G has the
+   GF_OMP_ATOMIC_WEAK flag set.  */
+
+static inline bool
+gimple_omp_atomic_weak_p (const gimple *g)
+{
+  if (gimple_code (g) != GIMPLE_OMP_ATOMIC_LOAD)
+    GIMPLE_CHECK (g, GIMPLE_OMP_ATOMIC_STORE);
+  return (gimple_omp_subcode (g) & GF_OMP_ATOMIC_WEAK) != 0;
+}
+
+
+/* Set the GF_OMP_ATOMIC_WEAK flag on G.  */
+
+static inline void
+gimple_omp_atomic_set_weak (gimple *g)
+{
+  if (gimple_code (g) != GIMPLE_OMP_ATOMIC_LOAD)
+    GIMPLE_CHECK (g, GIMPLE_OMP_ATOMIC_STORE);
+  g->subcode |= GF_OMP_ATOMIC_WEAK;
+}
+
+
 /* Return the memory order of the OMP atomic load/store statement G.  */
 
 static inline enum omp_memory_order
--- gcc/tree.h.jj	2021-09-09 10:40:20.132260691 +0200
+++ gcc/tree.h	2021-09-09 10:41:42.626084611 +0200
@@ -1529,6 +1529,11 @@ class auto_suppress_location_wrappers
   (TREE_RANGE_CHECK (NODE, OMP_ATOMIC, \
 		     OMP_ATOMIC_CAPTURE_NEW)->base.u.omp_atomic_memory_order)
 
+/* Weak clause on OMP_ATOMIC*.  */
+#define OMP_ATOMIC_WEAK(NODE) \
+  (TREE_RANGE_CHECK (NODE, OMP_ATOMIC, \
+		     OMP_ATOMIC_CAPTURE_NEW)->base.public_flag)
+
 /* True on a PRIVATE clause if its decl is kept around for debugging
    information only and its DECL_VALUE_EXPR is supposed to point
    to what it has been remapped to.  */
--- gcc/tree-pretty-print.c.jj	2021-09-09 10:40:20.131260705 +0200
+++ gcc/tree-pretty-print.c	2021-09-09 10:41:42.626084611 +0200
@@ -1492,7 +1492,7 @@ dump_block_node (pretty_printer *pp, tre
 void
 dump_omp_atomic_memory_order (pretty_printer *pp, enum omp_memory_order mo)
 {
-  switch (mo)
+  switch (mo & OMP_MEMORY_ORDER_MASK)
     {
     case OMP_MEMORY_ORDER_RELAXED:
       pp_string (pp, " relaxed");
@@ -1514,6 +1514,22 @@ dump_omp_atomic_memory_order (pretty_pri
     default:
       gcc_unreachable ();
     }
+  switch (mo & OMP_FAIL_MEMORY_ORDER_MASK)
+    {
+    case OMP_FAIL_MEMORY_ORDER_RELAXED:
+      pp_string (pp, " fail(relaxed)");
+      break;
+    case OMP_FAIL_MEMORY_ORDER_SEQ_CST:
+      pp_string (pp, " fail(seq_cst)");
+      break;
+    case OMP_FAIL_MEMORY_ORDER_ACQUIRE:
+      pp_string (pp, " fail(acquire)");
+      break;
+    case OMP_FAIL_MEMORY_ORDER_UNSPECIFIED:
+      break;
+    default:
+      gcc_unreachable ();
+    }
 }
 
 /* Helper to dump a MEM_REF node.  */
@@ -3629,6 +3645,8 @@ dump_generic_node (pretty_printer *pp, t
 
     case OMP_ATOMIC:
       pp_string (pp, "#pragma omp atomic");
+      if (OMP_ATOMIC_WEAK (node))
+	pp_string (pp, " weak");
       dump_omp_atomic_memory_order (pp, OMP_ATOMIC_MEMORY_ORDER (node));
       newline_and_indent (pp, spc + 2);
       dump_generic_node (pp, TREE_OPERAND (node, 0), spc, flags, false);
@@ -3649,6 +3667,8 @@ dump_generic_node (pretty_printer *pp, t
     case OMP_ATOMIC_CAPTURE_OLD:
     case OMP_ATOMIC_CAPTURE_NEW:
       pp_string (pp, "#pragma omp atomic capture");
+      if (OMP_ATOMIC_WEAK (node))
+	pp_string (pp, " weak");
       dump_omp_atomic_memory_order (pp, OMP_ATOMIC_MEMORY_ORDER (node));
       newline_and_indent (pp, spc + 2);
       dump_generic_node (pp, TREE_OPERAND (node, 0), spc, flags, false);
--- gcc/gimplify.c.jj	2021-09-09 10:40:20.057261760 +0200
+++ gcc/gimplify.c	2021-09-10 18:29:35.243673591 +0200
@@ -13725,14 +13725,15 @@ goa_lhs_expr_p (tree expr, tree addr)
 
 static int
 goa_stabilize_expr (tree *expr_p, gimple_seq *pre_p, tree lhs_addr,
-		    tree lhs_var)
+		    tree lhs_var, tree &target_expr, bool rhs)
 {
   tree expr = *expr_p;
   int saw_lhs;
 
   if (goa_lhs_expr_p (expr, lhs_addr))
     {
-      *expr_p = lhs_var;
+      if (pre_p)
+	*expr_p = lhs_var;
       return 1;
     }
   if (is_gimple_val (expr))
@@ -13744,11 +13745,11 @@ goa_stabilize_expr (tree *expr_p, gimple
     case tcc_binary:
     case tcc_comparison:
       saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p, lhs_addr,
-				     lhs_var);
+				     lhs_var, target_expr, true);
       /* FALLTHRU */
     case tcc_unary:
       saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p, lhs_addr,
-				     lhs_var);
+				     lhs_var, target_expr, true);
       break;
     case tcc_expression:
       switch (TREE_CODE (expr))
@@ -13760,36 +13761,131 @@ goa_stabilize_expr (tree *expr_p, gimple
 	case TRUTH_XOR_EXPR:
 	case BIT_INSERT_EXPR:
 	  saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p,
-					 lhs_addr, lhs_var);
+					 lhs_addr, lhs_var, target_expr, true);
 	  /* FALLTHRU */
 	case TRUTH_NOT_EXPR:
 	  saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p,
-					 lhs_addr, lhs_var);
+					 lhs_addr, lhs_var, target_expr, true);
+	  break;
+	case MODIFY_EXPR:
+	  saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p,
+					 lhs_addr, lhs_var, target_expr, true);
+	  /* FALLTHRU */
+	case ADDR_EXPR:
+	  saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p,
+					 lhs_addr, lhs_var, target_expr, false);
 	  break;
 	case COMPOUND_EXPR:
+	  /* Special-case __builtin_clear_padding call before
+	     __builtin_memcmp.  */
+	  if (TREE_CODE (TREE_OPERAND (expr, 0)) == CALL_EXPR)
+	    {
+	      tree fndecl = get_callee_fndecl (TREE_OPERAND (expr, 0));
+	      if (fndecl
+		  && fndecl_built_in_p (fndecl, BUILT_IN_CLEAR_PADDING)
+		  && VOID_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0))))
+		{
+		  saw_lhs = goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p,
+						lhs_addr, lhs_var,
+						target_expr, true);
+		  if (!saw_lhs)
+		    {
+		      expr = TREE_OPERAND (expr, 1);
+		      if (!pre_p)
+			return goa_stabilize_expr (&expr, pre_p, lhs_addr,
+						   lhs_var, target_expr, true);
+		      *expr_p = expr;
+		      return goa_stabilize_expr (expr_p, pre_p, lhs_addr,
+						 lhs_var, target_expr, true);
+		    }
+		  else
+		    {
+		      saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1),
+						     pre_p, lhs_addr, lhs_var,
+						     target_expr, rhs);
+		      break;
+		    }
+		}
+	    }
 	  /* Break out any preevaluations from cp_build_modify_expr.  */
 	  for (; TREE_CODE (expr) == COMPOUND_EXPR;
 	       expr = TREE_OPERAND (expr, 1))
-	    gimplify_stmt (&TREE_OPERAND (expr, 0), pre_p);
+	    if (pre_p)
+	      gimplify_stmt (&TREE_OPERAND (expr, 0), pre_p);
+	  if (!pre_p)
+	    return goa_stabilize_expr (&expr, pre_p, lhs_addr, lhs_var,
+				       target_expr, rhs);
 	  *expr_p = expr;
-	  return goa_stabilize_expr (expr_p, pre_p, lhs_addr, lhs_var);
+	  return goa_stabilize_expr (expr_p, pre_p, lhs_addr, lhs_var,
+				     target_expr, rhs);
+	case COND_EXPR:
+	  if (!goa_stabilize_expr (&TREE_OPERAND (expr, 0), NULL, lhs_addr,
+				   lhs_var, target_expr, true))
+	    break;
+	  saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p,
+					 lhs_addr, lhs_var, target_expr, true);
+	  saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p,
+					 lhs_addr, lhs_var, target_expr, true);
+	  saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 2), pre_p,
+					 lhs_addr, lhs_var, target_expr, true);
+	  break;
+	case TARGET_EXPR:
+	  if (TARGET_EXPR_INITIAL (expr))
+	    {
+	      if (expr == target_expr)
+		saw_lhs = 1;
+	      else
+		{
+		  saw_lhs = goa_stabilize_expr (&TARGET_EXPR_INITIAL (expr),
+						pre_p, lhs_addr, lhs_var,
+						target_expr, true);
+		  if (saw_lhs && target_expr == NULL_TREE && pre_p)
+		    target_expr = expr;
+		}
+	    }
+	  break;
 	default:
 	  break;
 	}
       break;
     case tcc_reference:
-      if (TREE_CODE (expr) == BIT_FIELD_REF)
+      if (TREE_CODE (expr) == BIT_FIELD_REF
+	  || TREE_CODE (expr) == VIEW_CONVERT_EXPR)
 	saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p,
-				       lhs_addr, lhs_var);
+				       lhs_addr, lhs_var, target_expr, true);
+      break;
+    case tcc_vl_exp:
+      if (TREE_CODE (expr) == CALL_EXPR)
+	{
+	  if (tree fndecl = get_callee_fndecl (expr))
+	    if (fndecl_built_in_p (fndecl, BUILT_IN_CLEAR_PADDING)
+		|| fndecl_built_in_p (fndecl, BUILT_IN_MEMCMP))
+	      {
+		int nargs = call_expr_nargs (expr);
+		for (int i = 0; i < nargs; i++)
+		  saw_lhs |= goa_stabilize_expr (&CALL_EXPR_ARG (expr, i),
+						 pre_p, lhs_addr, lhs_var,
+						 target_expr, true);
+	      }
+	  if (saw_lhs == 0 && VOID_TYPE_P (TREE_TYPE (expr)))
+	    {
+	      if (pre_p)
+		gimplify_stmt (&expr, pre_p);
+	      return 0;
+	    }
+	}
       break;
     default:
       break;
     }
 
-  if (saw_lhs == 0)
+  if (saw_lhs == 0 && pre_p)
     {
       enum gimplify_status gs;
-      gs = gimplify_expr (expr_p, pre_p, NULL, is_gimple_val, fb_rvalue);
+      if (rhs)
+	gs = gimplify_expr (expr_p, pre_p, NULL, is_gimple_val, fb_rvalue);
+      else
+	gs = gimplify_expr (expr_p, pre_p, NULL, is_gimple_lvalue, fb_lvalue);
       if (gs != GS_ALL_DONE)
 	saw_lhs = -1;
     }
@@ -13809,9 +13905,12 @@ gimplify_omp_atomic (tree *expr_p, gimpl
   tree tmp_load;
   gomp_atomic_load *loadstmt;
   gomp_atomic_store *storestmt;
+  tree target_expr = NULL_TREE;
 
   tmp_load = create_tmp_reg (type);
-  if (rhs && goa_stabilize_expr (&rhs, pre_p, addr, tmp_load) < 0)
+  if (rhs
+      && goa_stabilize_expr (&rhs, pre_p, addr, tmp_load, target_expr,
+			     true) < 0)
     return GS_ERROR;
 
   if (gimplify_expr (&addr, pre_p, NULL, is_gimple_val, fb_rvalue)
@@ -13825,11 +13924,14 @@ gimplify_omp_atomic (tree *expr_p, gimpl
     {
       /* BIT_INSERT_EXPR is not valid for non-integral bitfield
 	 representatives.  Use BIT_FIELD_REF on the lhs instead.  */
-      if (TREE_CODE (rhs) == BIT_INSERT_EXPR
+      tree rhsarg = rhs;
+      if (TREE_CODE (rhs) == COND_EXPR)
+	rhsarg = TREE_OPERAND (rhs, 1);
+      if (TREE_CODE (rhsarg) == BIT_INSERT_EXPR
 	  && !INTEGRAL_TYPE_P (TREE_TYPE (tmp_load)))
 	{
-	  tree bitpos = TREE_OPERAND (rhs, 2);
-	  tree op1 = TREE_OPERAND (rhs, 1);
+	  tree bitpos = TREE_OPERAND (rhsarg, 2);
+	  tree op1 = TREE_OPERAND (rhsarg, 1);
 	  tree bitsize;
 	  tree tmp_store = tmp_load;
 	  if (TREE_CODE (*expr_p) == OMP_ATOMIC_CAPTURE_OLD)
@@ -13838,17 +13940,25 @@ gimplify_omp_atomic (tree *expr_p, gimpl
 	    bitsize = bitsize_int (TYPE_PRECISION (TREE_TYPE (op1)));
 	  else
 	    bitsize = TYPE_SIZE (TREE_TYPE (op1));
-	  gcc_assert (TREE_OPERAND (rhs, 0) == tmp_load);
-	  tree t = build2_loc (EXPR_LOCATION (rhs),
+	  gcc_assert (TREE_OPERAND (rhsarg, 0) == tmp_load);
+	  tree t = build2_loc (EXPR_LOCATION (rhsarg),
 			       MODIFY_EXPR, void_type_node,
-			       build3_loc (EXPR_LOCATION (rhs), BIT_FIELD_REF,
-					   TREE_TYPE (op1), tmp_store, bitsize,
-					   bitpos), op1);
+			       build3_loc (EXPR_LOCATION (rhsarg),
+					   BIT_FIELD_REF, TREE_TYPE (op1),
+					   tmp_store, bitsize, bitpos), op1);
+	  if (TREE_CODE (rhs) == COND_EXPR)
+	    t = build3_loc (EXPR_LOCATION (rhs), COND_EXPR, void_type_node,
+			    TREE_OPERAND (rhs, 0), t, void_node);
 	  gimplify_and_add (t, pre_p);
 	  rhs = tmp_store;
 	}
-      if (gimplify_expr (&rhs, pre_p, NULL, is_gimple_val, fb_rvalue)
-	  != GS_ALL_DONE)
+      bool save_allow_rhs_cond_expr = gimplify_ctxp->allow_rhs_cond_expr;
+      if (TREE_CODE (rhs) == COND_EXPR)
+	gimplify_ctxp->allow_rhs_cond_expr = true;
+      enum gimplify_status gs = gimplify_expr (&rhs, pre_p, NULL,
+					       is_gimple_val, fb_rvalue);
+      gimplify_ctxp->allow_rhs_cond_expr = save_allow_rhs_cond_expr;
+      if (gs != GS_ALL_DONE)
 	return GS_ERROR;
     }
 
@@ -13856,6 +13966,11 @@ gimplify_omp_atomic (tree *expr_p, gimpl
     rhs = tmp_load;
   storestmt
     = gimple_build_omp_atomic_store (rhs, OMP_ATOMIC_MEMORY_ORDER (*expr_p));
+  if (TREE_CODE (*expr_p) != OMP_ATOMIC_READ && OMP_ATOMIC_WEAK (*expr_p))
+    {
+      gimple_omp_atomic_set_weak (loadstmt);
+      gimple_omp_atomic_set_weak (storestmt);
+    }
   gimplify_seq_add_stmt (pre_p, storestmt);
   switch (TREE_CODE (*expr_p))
     {
--- gcc/omp-expand.c.jj	2021-09-09 10:40:20.072261546 +0200
+++ gcc/omp-expand.c	2021-09-10 15:05:47.315510567 +0200
@@ -8487,22 +8487,58 @@ expand_omp_synch (struct omp_region *reg
     }
 }
 
+/* Translate enum omp_memory_order to enum memmodel for the embedded
+   fail clause in there.  */
+
+static enum memmodel
+omp_memory_order_to_fail_memmodel (enum omp_memory_order mo)
+{
+  switch (mo & OMP_FAIL_MEMORY_ORDER_MASK)
+    {
+    case OMP_FAIL_MEMORY_ORDER_UNSPECIFIED:
+      switch (mo & OMP_MEMORY_ORDER_MASK)
+	{
+	case OMP_MEMORY_ORDER_RELAXED: return MEMMODEL_RELAXED;
+	case OMP_MEMORY_ORDER_ACQUIRE: return MEMMODEL_ACQUIRE;
+	case OMP_MEMORY_ORDER_RELEASE: return MEMMODEL_RELAXED;
+	case OMP_MEMORY_ORDER_ACQ_REL: return MEMMODEL_ACQUIRE;
+	case OMP_MEMORY_ORDER_SEQ_CST: return MEMMODEL_SEQ_CST;
+	default: break;
+	}
+      gcc_unreachable ();
+    case OMP_FAIL_MEMORY_ORDER_RELAXED: return MEMMODEL_RELAXED;
+    case OMP_FAIL_MEMORY_ORDER_ACQUIRE: return MEMMODEL_ACQUIRE;
+    case OMP_FAIL_MEMORY_ORDER_SEQ_CST: return MEMMODEL_SEQ_CST;
+    default: gcc_unreachable ();
+    }
+}
+
 /* Translate enum omp_memory_order to enum memmodel.  The two enums
    are using different numbers so that OMP_MEMORY_ORDER_UNSPECIFIED
-   is 0.  */
+   is 0 and omp_memory_order has the fail mode encoded in it too.  */
 
 static enum memmodel
 omp_memory_order_to_memmodel (enum omp_memory_order mo)
 {
-  switch (mo)
+  enum memmodel ret, fail_ret;
+  switch (mo & OMP_MEMORY_ORDER_MASK)
     {
-    case OMP_MEMORY_ORDER_RELAXED: return MEMMODEL_RELAXED;
-    case OMP_MEMORY_ORDER_ACQUIRE: return MEMMODEL_ACQUIRE;
-    case OMP_MEMORY_ORDER_RELEASE: return MEMMODEL_RELEASE;
-    case OMP_MEMORY_ORDER_ACQ_REL: return MEMMODEL_ACQ_REL;
-    case OMP_MEMORY_ORDER_SEQ_CST: return MEMMODEL_SEQ_CST;
+    case OMP_MEMORY_ORDER_RELAXED: ret = MEMMODEL_RELAXED; break;
+    case OMP_MEMORY_ORDER_ACQUIRE: ret = MEMMODEL_ACQUIRE; break;
+    case OMP_MEMORY_ORDER_RELEASE: ret = MEMMODEL_RELEASE; break;
+    case OMP_MEMORY_ORDER_ACQ_REL: ret = MEMMODEL_ACQ_REL; break;
+    case OMP_MEMORY_ORDER_SEQ_CST: ret = MEMMODEL_SEQ_CST; break;
     default: gcc_unreachable ();
     }
+  /* If we drop the -Winvalid-memory-model warning for C++17 P0418R2,
+     we can just return ret here unconditionally.  Otherwise, work around
+     it here and make sure fail memmodel is not stronger.  */
+  if ((mo & OMP_FAIL_MEMORY_ORDER_MASK) == OMP_FAIL_MEMORY_ORDER_UNSPECIFIED)
+    return ret;
+  fail_ret = omp_memory_order_to_fail_memmodel (mo);
+  if (fail_ret > ret)
+    return fail_ret;
+  return ret;
 }
 
 /* A subroutine of expand_omp_atomic.  Attempt to implement the atomic
@@ -8782,6 +8818,261 @@ expand_omp_atomic_fetch_op (basic_block
   return true;
 }
 
+/* A subroutine of expand_omp_atomic.  Attempt to implement the atomic
+   compare and exchange as an ATOMIC_COMPARE_EXCHANGE internal function.
+   Returns false if the expression is not of the proper form.  */
+
+static bool
+expand_omp_atomic_cas (basic_block load_bb, tree addr,
+		       tree loaded_val, tree stored_val, int index)
+{
+  /* We expect to find the following sequences:
+
+   load_bb:
+       GIMPLE_OMP_ATOMIC_LOAD (tmp, mem)
+
+   store_bb:
+       val = tmp == e ? d : tmp;
+       GIMPLE_OMP_ATOMIC_STORE (val)
+
+     or in store_bb instead:
+       tmp2 = tmp == e;
+       val = tmp2 ? d : tmp;
+       GIMPLE_OMP_ATOMIC_STORE (val)
+
+     or:
+       tmp3 = VIEW_CONVERT_EXPR<integral_type>(tmp);
+       val = e == tmp3 ? d : tmp;
+       GIMPLE_OMP_ATOMIC_STORE (val)
+
+     etc.  */
+
+
+  basic_block store_bb = single_succ (load_bb);
+  gimple_stmt_iterator gsi = gsi_last_nondebug_bb (store_bb);
+  gimple *store_stmt = gsi_stmt (gsi);
+  if (!store_stmt || gimple_code (store_stmt) != GIMPLE_OMP_ATOMIC_STORE)
+    return false;
+  gsi_prev_nondebug (&gsi);
+  if (gsi_end_p (gsi))
+    return false;
+  gimple *condexpr_stmt = gsi_stmt (gsi);
+  if (!is_gimple_assign (condexpr_stmt)
+      || gimple_assign_rhs_code (condexpr_stmt) != COND_EXPR)
+    return false;
+  if (!operand_equal_p (gimple_assign_lhs (condexpr_stmt), stored_val, 0))
+    return false;
+  gimple *cond_stmt = NULL;
+  gimple *vce_stmt = NULL;
+  gsi_prev_nondebug (&gsi);
+  if (!gsi_end_p (gsi))
+    {
+      cond_stmt = gsi_stmt (gsi);
+      if (!is_gimple_assign (cond_stmt))
+	return false;
+      if (gimple_assign_rhs_code (cond_stmt) == EQ_EXPR)
+	{
+	  gsi_prev_nondebug (&gsi);
+	  if (!gsi_end_p (gsi))
+	    {
+	      vce_stmt = gsi_stmt (gsi);
+	      if (!is_gimple_assign (vce_stmt)
+		  || gimple_assign_rhs_code (vce_stmt) != VIEW_CONVERT_EXPR)
+		return false;
+	    }
+	}
+      else if (gimple_assign_rhs_code (cond_stmt) == VIEW_CONVERT_EXPR)
+	std::swap (vce_stmt, cond_stmt);
+      else
+	return false;
+      if (vce_stmt)
+	{
+	  tree vce_rhs = gimple_assign_rhs1 (vce_stmt);
+	  if (TREE_CODE (vce_rhs) != VIEW_CONVERT_EXPR
+	      || !operand_equal_p (TREE_OPERAND (vce_rhs, 0), loaded_val))
+	    return false;
+	  if (!INTEGRAL_TYPE_P (TREE_TYPE (vce_rhs))
+	      || !SCALAR_FLOAT_TYPE_P (TREE_TYPE (loaded_val))
+	      || !tree_int_cst_equal (TYPE_SIZE (TREE_TYPE (vce_rhs)),
+				      TYPE_SIZE (TREE_TYPE (loaded_val))))
+	    return false;
+	  gsi_prev_nondebug (&gsi);
+	  if (!gsi_end_p (gsi))
+	    return false;
+	}
+    }
+  tree cond = gimple_assign_rhs1 (condexpr_stmt);
+  tree cond_op1, cond_op2;
+  if (cond_stmt)
+    {
+      if (!operand_equal_p (cond, gimple_assign_lhs (cond_stmt)))
+	return false;
+      cond_op1 = gimple_assign_rhs1 (cond_stmt);
+      cond_op2 = gimple_assign_rhs2 (cond_stmt);
+    }
+  else if (TREE_CODE (cond) != EQ_EXPR && TREE_CODE (cond) != NE_EXPR)
+    return false;
+  else
+    {
+      cond_op1 = TREE_OPERAND (cond, 0);
+      cond_op2 = TREE_OPERAND (cond, 1);
+    }
+  tree d;
+  if (TREE_CODE (cond) == NE_EXPR)
+    {
+      if (!operand_equal_p (gimple_assign_rhs2 (condexpr_stmt), loaded_val))
+	return false;
+      d = gimple_assign_rhs3 (condexpr_stmt);
+    }
+  else if (!operand_equal_p (gimple_assign_rhs3 (condexpr_stmt), loaded_val))
+    return false;
+  else
+    d = gimple_assign_rhs2 (condexpr_stmt);
+  tree e = vce_stmt ? gimple_assign_lhs (vce_stmt) : loaded_val;
+  if (operand_equal_p (e, cond_op1))
+    e = cond_op2;
+  else if (operand_equal_p (e, cond_op2))
+    e = cond_op1;
+  else
+    return false;
+
+  location_t loc = gimple_location (store_stmt);
+  gimple *load_stmt = last_stmt (load_bb);
+  bool need_new = gimple_omp_atomic_need_value_p (store_stmt);
+  bool need_old = gimple_omp_atomic_need_value_p (load_stmt);
+  bool weak = gimple_omp_atomic_weak_p (load_stmt);
+  enum omp_memory_order omo = gimple_omp_atomic_memory_order (load_stmt);
+  tree mo = build_int_cst (NULL, omp_memory_order_to_memmodel (omo));
+  tree fmo = build_int_cst (NULL, omp_memory_order_to_fail_memmodel (omo));
+  gcc_checking_assert (!need_old || !need_new);
+
+  enum built_in_function fncode
+    = (enum built_in_function) ((int) BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_N
+				+ index + 1);
+  tree cmpxchg = builtin_decl_explicit (fncode);
+  if (cmpxchg == NULL_TREE)
+    return false;
+  tree itype = TREE_TYPE (TREE_TYPE (cmpxchg));
+
+  if (!can_compare_and_swap_p (TYPE_MODE (itype), true)
+      || !can_atomic_load_p (TYPE_MODE (itype)))
+    return false;
+
+  tree type = TYPE_MAIN_VARIANT (TREE_TYPE (loaded_val));
+  if (SCALAR_FLOAT_TYPE_P (type) && !vce_stmt)
+    return false;
+
+  gsi = gsi_for_stmt (store_stmt);
+  if (!useless_type_conversion_p (itype, TREE_TYPE (e)))
+    {
+      tree ne = create_tmp_reg (itype);
+      gimple *g = gimple_build_assign (ne, NOP_EXPR, e);
+      gimple_set_location (g, loc);
+      gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+      e = ne;
+    }
+  if (!useless_type_conversion_p (itype, TREE_TYPE (d)))
+    {
+      tree nd = create_tmp_reg (itype);
+      enum tree_code code;
+      if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (d)))
+	{
+	  code = VIEW_CONVERT_EXPR;
+	  d = build1 (VIEW_CONVERT_EXPR, itype, d);
+	}
+      else
+	code = NOP_EXPR;
+      gimple *g = gimple_build_assign (nd, code, d);
+      gimple_set_location (g, loc);
+      gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+      d = nd;
+    }
+
+  tree ctype = build_complex_type (itype);
+  int flag = int_size_in_bytes (itype) + (weak ? 256 : 0);
+  gimple *g
+    = gimple_build_call_internal (IFN_ATOMIC_COMPARE_EXCHANGE, 6, addr, e, d,
+				  build_int_cst (integer_type_node, flag),
+				  mo, fmo);
+  tree cres = create_tmp_reg (ctype);
+  gimple_call_set_lhs (g, cres);
+  gimple_set_location (g, loc);
+  gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+
+  if (cond_stmt || need_old || need_new)
+    {
+      tree im = create_tmp_reg (itype);
+      g = gimple_build_assign (im, IMAGPART_EXPR,
+			       build1 (IMAGPART_EXPR, itype, cres));
+      gimple_set_location (g, loc);
+      gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+
+      tree re = NULL_TREE;
+      if (need_old || need_new)
+	{
+	  re = create_tmp_reg (itype);
+	  g = gimple_build_assign (re, REALPART_EXPR,
+				   build1 (REALPART_EXPR, itype, cres));
+	  gimple_set_location (g, loc);
+	  gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+	}
+
+      if (cond_stmt)
+	{
+	  g = gimple_build_assign (gimple_assign_lhs (cond_stmt),
+				   NOP_EXPR, im);
+	  gimple_set_location (g, loc);
+	  gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+	}
+      else if (need_new)
+	{
+	  g = gimple_build_assign (create_tmp_reg (itype), COND_EXPR,
+				   build2 (NE_EXPR, boolean_type_node,
+					   im, build_zero_cst (itype)),
+				   d, re);
+	  gimple_set_location (g, loc);
+	  gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+	  re = gimple_assign_lhs (g);
+	}
+
+      if (need_old || need_new)
+	{
+	  tree v = need_old ? loaded_val : stored_val;
+	  enum tree_code code;
+	  if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (v)))
+	    {
+	      code = VIEW_CONVERT_EXPR;
+	      re = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (v), re);
+	    }
+	  else if (!useless_type_conversion_p (TREE_TYPE (v), itype))
+	    code = NOP_EXPR;
+	  else
+	    code = TREE_CODE (re);
+	  g = gimple_build_assign (v, code, re);
+	  gimple_set_location (g, loc);
+	  gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+	}
+    }
+
+  gsi_remove (&gsi, true);
+  gsi = gsi_for_stmt (load_stmt);
+  gsi_remove (&gsi, true);
+  gsi = gsi_for_stmt (condexpr_stmt);
+  gsi_remove (&gsi, true);
+  if (cond_stmt)
+    {
+      gsi = gsi_for_stmt (cond_stmt);
+      gsi_remove (&gsi, true);
+    }
+  if (vce_stmt)
+    {
+      gsi = gsi_for_stmt (vce_stmt);
+      gsi_remove (&gsi, true);
+    }
+
+  return true;
+}
+
 /* A subroutine of expand_omp_atomic.  Implement the atomic operation as:
 
       oldval = *addr;
@@ -8825,13 +9116,8 @@ expand_omp_atomic_pipeline (basic_block
   gcc_assert (gimple_code (gsi_stmt (si)) == GIMPLE_OMP_ATOMIC_LOAD);
   location_t loc = gimple_location (gsi_stmt (si));
   enum omp_memory_order omo = gimple_omp_atomic_memory_order (gsi_stmt (si));
-  enum memmodel imo = omp_memory_order_to_memmodel (omo);
-  tree mo = build_int_cst (NULL, imo);
-  if (imo == MEMMODEL_RELEASE)
-    imo = MEMMODEL_RELAXED;
-  else if (imo == MEMMODEL_ACQ_REL)
-    imo = MEMMODEL_ACQUIRE;
-  tree fmo = build_int_cst (NULL, imo);
+  tree mo = build_int_cst (NULL, omp_memory_order_to_memmodel (omo));
+  tree fmo = build_int_cst (NULL, omp_memory_order_to_fail_memmodel (omo));
 
   /* For floating-point values, we'll need to view-convert them to integers
      so that we can perform the atomic compare and swap.  Simplify the
@@ -9114,6 +9400,13 @@ expand_omp_atomic (struct omp_region *re
 					     loaded_val, stored_val, index))
 	    return;
 
+	  /* When possible, use ATOMIC_COMPARE_EXCHANGE ifn without a loop.  */
+	  if (store_bb == single_succ (load_bb)
+	      && !gimple_in_ssa_p (cfun)
+	      && expand_omp_atomic_cas (load_bb, addr, loaded_val, stored_val,
+					index))
+	    return;
+
 	  /* If we don't have specialized __sync builtins, try and implement
 	     as a compare and swap loop.  */
 	  if (expand_omp_atomic_pipeline (load_bb, store_bb, addr,
--- gcc/c-family/c-common.h.jj	2021-09-09 10:40:19.928263599 +0200
+++ gcc/c-family/c-common.h	2021-09-09 10:42:11.865667757 +0200
@@ -1223,8 +1223,8 @@ extern tree c_finish_omp_critical (locat
 extern tree c_finish_omp_ordered (location_t, tree, tree);
 extern void c_finish_omp_barrier (location_t);
 extern tree c_finish_omp_atomic (location_t, enum tree_code, enum tree_code,
-				 tree, tree, tree, tree, tree, bool,
-				 enum omp_memory_order, bool = false);
+				 tree, tree, tree, tree, tree, tree, bool,
+				 enum omp_memory_order, bool, bool = false);
 extern bool c_omp_depend_t_p (tree);
 extern void c_finish_omp_depobj (location_t, tree, enum omp_clause_depend_kind,
 				 tree);
--- gcc/c-family/c-omp.c.jj	2021-09-09 10:40:19.937263471 +0200
+++ gcc/c-family/c-omp.c	2021-09-10 17:59:26.574281430 +0200
@@ -36,6 +36,7 @@ along with GCC; see the file COPYING3.
 #include "gimplify.h"
 #include "langhooks.h"
 #include "bitmap.h"
+#include "gimple-fold.h"
 
 
 /* Complete a #pragma oacc wait construct.  LOC is the location of
@@ -215,15 +216,17 @@ c_finish_omp_taskyield (location_t loc)
 tree
 c_finish_omp_atomic (location_t loc, enum tree_code code,
 		     enum tree_code opcode, tree lhs, tree rhs,
-		     tree v, tree lhs1, tree rhs1, bool swapped,
-		     enum omp_memory_order memory_order, bool test)
+		     tree v, tree lhs1, tree rhs1, tree r, bool swapped,
+		     enum omp_memory_order memory_order, bool weak,
+		     bool test)
 {
-  tree x, type, addr, pre = NULL_TREE;
+  tree x, type, addr, pre = NULL_TREE, rtmp = NULL_TREE, vtmp = NULL_TREE;
   HOST_WIDE_INT bitpos = 0, bitsize = 0;
+  enum tree_code orig_opcode = opcode;
 
   if (lhs == error_mark_node || rhs == error_mark_node
       || v == error_mark_node || lhs1 == error_mark_node
-      || rhs1 == error_mark_node)
+      || rhs1 == error_mark_node || r == error_mark_node)
     return error_mark_node;
 
   /* ??? According to one reading of the OpenMP spec, complex type are
@@ -243,6 +246,12 @@ c_finish_omp_atomic (location_t loc, enu
       error_at (loc, "%<_Atomic%> expression in %<#pragma omp atomic%>");
       return error_mark_node;
     }
+  if (r && r != void_list_node && !INTEGRAL_TYPE_P (TREE_TYPE (r)))
+    {
+      error_at (loc, "%<#pragma omp atomic compare capture%> with non-integral "
+		     "comparison result");
+      return error_mark_node;
+    }
 
   if (opcode == RDIV_EXPR)
     opcode = TRUNC_DIV_EXPR;
@@ -299,6 +308,7 @@ c_finish_omp_atomic (location_t loc, enu
       x = build1 (OMP_ATOMIC_READ, type, addr);
       SET_EXPR_LOCATION (x, loc);
       OMP_ATOMIC_MEMORY_ORDER (x) = memory_order;
+      gcc_assert (!weak);
       if (blhs)
 	x = build3_loc (loc, BIT_FIELD_REF, TREE_TYPE (blhs), x,
 			bitsize_int (bitsize), bitsize_int (bitpos));
@@ -313,12 +323,29 @@ c_finish_omp_atomic (location_t loc, enu
     {
       lhs = build3_loc (loc, BIT_FIELD_REF, TREE_TYPE (blhs), lhs,
 			bitsize_int (bitsize), bitsize_int (bitpos));
-      if (swapped)
+      if (opcode == COND_EXPR)
+	{
+	  bool save = in_late_binary_op;
+	  in_late_binary_op = true;
+	  std::swap (rhs, rhs1);
+	  rhs1 = build_binary_op (loc, EQ_EXPR, lhs, rhs1, true);
+	  in_late_binary_op = save;
+	}
+      else if (swapped)
 	rhs = build_binary_op (loc, opcode, rhs, lhs, true);
       else if (opcode != NOP_EXPR)
 	rhs = build_binary_op (loc, opcode, lhs, rhs, true);
       opcode = NOP_EXPR;
     }
+  else if (opcode == COND_EXPR)
+    {
+      bool save = in_late_binary_op;
+      in_late_binary_op = true;
+      std::swap (rhs, rhs1);
+      rhs1 = build_binary_op (loc, EQ_EXPR, lhs, rhs1, true);
+      in_late_binary_op = save;
+      opcode = NOP_EXPR;
+    }
   else if (swapped)
     {
       rhs = build_binary_op (loc, opcode, rhs, lhs, true);
@@ -343,6 +370,100 @@ c_finish_omp_atomic (location_t loc, enu
   if (blhs)
     rhs = build3_loc (loc, BIT_INSERT_EXPR, type, new_lhs,
 		      rhs, bitsize_int (bitpos));
+  if (orig_opcode == COND_EXPR)
+    {
+      if (error_operand_p (rhs1))
+	return error_mark_node;
+      gcc_assert (TREE_CODE (rhs1) == EQ_EXPR);
+      tree cmptype = TREE_TYPE (TREE_OPERAND (rhs1, 0));
+      if (SCALAR_FLOAT_TYPE_P (cmptype))
+	{
+	  bool clear_padding = false;
+	  if (BITS_PER_UNIT == 8 && CHAR_BIT == 8)
+	    {
+	      HOST_WIDE_INT sz = int_size_in_bytes (cmptype), i;
+	      gcc_assert (sz > 0);
+	      unsigned char *buf = XALLOCAVEC (unsigned char, sz);
+	      memset (buf, ~0, sz);
+	      clear_type_padding_in_mask (cmptype, buf);
+	      for (i = 0; i < sz; i++)
+		if (buf[i] != (unsigned char) ~0)
+		  {
+		    clear_padding = true;
+		    break;
+		  }
+	    }
+	  tree inttype = NULL_TREE;
+	  if (!clear_padding && tree_fits_uhwi_p (TYPE_SIZE (cmptype)))
+	    {
+	      HOST_WIDE_INT prec = tree_to_uhwi (TYPE_SIZE (cmptype));
+	      inttype = c_common_type_for_size (prec, 1);
+	      if (inttype
+		  && (!tree_int_cst_equal (TYPE_SIZE (cmptype),
+					   TYPE_SIZE (inttype))
+		      || TYPE_PRECISION (inttype) != prec))
+		inttype = NULL_TREE;
+	    }
+	  if (inttype)
+	    {
+	      TREE_OPERAND (rhs1, 0)
+		= build1_loc (loc, VIEW_CONVERT_EXPR, inttype,
+			      TREE_OPERAND (rhs1, 0));
+	      TREE_OPERAND (rhs1, 1)
+		= build1_loc (loc, VIEW_CONVERT_EXPR, inttype,
+			      TREE_OPERAND (rhs1, 1));
+	    }
+	  else
+	    {
+	      tree pcmptype = build_pointer_type (cmptype);
+	      tree tmp1 = create_tmp_var_raw (cmptype);
+	      TREE_ADDRESSABLE (tmp1) = 1;
+	      DECL_CONTEXT (tmp1) = current_function_decl;
+	      tmp1 = build4 (TARGET_EXPR, cmptype, tmp1,
+			     TREE_OPERAND (rhs1, 0), NULL, NULL);
+	      tmp1 = build1 (ADDR_EXPR, pcmptype, tmp1);
+	      tree tmp2 = create_tmp_var_raw (cmptype);
+	      TREE_ADDRESSABLE (tmp2) = 1;
+	      DECL_CONTEXT (tmp2) = current_function_decl;
+	      tmp2 = build4 (TARGET_EXPR, cmptype, tmp2,
+			     TREE_OPERAND (rhs1, 1), NULL, NULL);
+	      tmp2 = build1 (ADDR_EXPR, pcmptype, tmp2);
+	      tree fndecl = builtin_decl_explicit (BUILT_IN_MEMCMP);
+	      rhs1 = build_call_expr_loc (loc, fndecl, 3, tmp1, tmp2,
+					  TYPE_SIZE_UNIT (cmptype));
+	      rhs1 = build2 (EQ_EXPR, boolean_type_node, rhs1,
+			     integer_zero_node);
+	      if (clear_padding)
+		{
+		  fndecl = builtin_decl_explicit (BUILT_IN_CLEAR_PADDING);
+		  tree cp1 = build_call_expr_loc (loc, fndecl, 1, tmp1);
+		  tree cp2 = build_call_expr_loc (loc, fndecl, 1, tmp2);
+		  rhs1 = omit_two_operands_loc (loc, boolean_type_node,
+						rhs1, cp2, cp1);
+		}
+	    }
+	}
+      if (r)
+	{
+	  tree var = create_tmp_var (boolean_type_node);
+	  DECL_CONTEXT (var) = current_function_decl;
+	  rtmp = build4 (TARGET_EXPR, boolean_type_node, var,
+			 NULL, NULL, NULL);
+	  save = in_late_binary_op;
+	  in_late_binary_op = true;
+	  x = build_modify_expr (loc, var, NULL_TREE, NOP_EXPR,
+				 loc, rhs1, NULL_TREE);
+	  in_late_binary_op = save;
+	  if (x == error_mark_node)
+	    return error_mark_node;
+	  gcc_assert (TREE_CODE (x) == MODIFY_EXPR
+		      && TREE_OPERAND (x, 0) == var);
+	  TREE_OPERAND (x, 0) = rtmp;
+	  rhs1 = omit_one_operand_loc (loc, boolean_type_node, x, rtmp);
+	}
+      rhs = build3_loc (loc, COND_EXPR, type, rhs1, rhs, new_lhs);
+      rhs1 = NULL_TREE;
+    }
 
   /* Punt the actual generation of atomic operations to common code.  */
   if (code == OMP_ATOMIC)
@@ -350,6 +471,7 @@ c_finish_omp_atomic (location_t loc, enu
   x = build2 (code, type, addr, rhs);
   SET_EXPR_LOCATION (x, loc);
   OMP_ATOMIC_MEMORY_ORDER (x) = memory_order;
+  OMP_ATOMIC_WEAK (x) = weak;
 
   /* Generally it is hard to prove lhs1 and lhs are the same memory
      location, just diagnose different variables.  */
@@ -412,8 +534,25 @@ c_finish_omp_atomic (location_t loc, enu
 			  bitsize_int (bitsize), bitsize_int (bitpos));
 	  type = TREE_TYPE (blhs);
 	}
-      x = build_modify_expr (loc, v, NULL_TREE, NOP_EXPR,
+      if (r)
+	{
+	  vtmp = create_tmp_var (TREE_TYPE (x));
+	  DECL_CONTEXT (vtmp) = current_function_decl;
+	}
+      else
+	vtmp = v;
+      x = build_modify_expr (loc, vtmp, NULL_TREE, NOP_EXPR,
 			     loc, x, NULL_TREE);
+      if (x == error_mark_node)
+	return error_mark_node;
+      if (r)
+	{
+	  vtmp = build4 (TARGET_EXPR, boolean_type_node, vtmp,
+			 NULL, NULL, NULL);
+	  gcc_assert (TREE_CODE (x) == MODIFY_EXPR
+		      && TREE_OPERAND (x, 0) == TARGET_EXPR_SLOT (vtmp));
+	  TREE_OPERAND (x, 0) = vtmp;
+	}
       if (rhs1 && rhs1 != orig_lhs)
 	{
 	  tree rhs1addr = build_unary_op (loc, ADDR_EXPR, rhs1, false);
@@ -446,6 +585,28 @@ c_finish_omp_atomic (location_t loc, enu
 
   if (pre)
     x = omit_one_operand_loc (loc, type, x, pre);
+  if (r && r != void_list_node)
+    {
+      in_late_binary_op = true;
+      tree x2 = build_modify_expr (loc, r, NULL_TREE, NOP_EXPR,
+				   loc, rtmp, NULL_TREE);
+      in_late_binary_op = save;
+      if (x2 == error_mark_node)
+	return error_mark_node;
+      x = omit_one_operand_loc (loc, TREE_TYPE (x2), x2, x);
+    }
+  if (v && vtmp != v)
+    {
+      in_late_binary_op = true;
+      tree x2 = build_modify_expr (loc, v, NULL_TREE, NOP_EXPR,
+				   loc, vtmp, NULL_TREE);
+      in_late_binary_op = save;
+      if (x2 == error_mark_node)
+	return error_mark_node;
+      x2 = build3_loc (loc, COND_EXPR, void_type_node, rtmp,
+		       void_node, x2);
+      x = omit_one_operand_loc (loc, TREE_TYPE (x2), x2, x);
+    }
   return x;
 }
 
--- gcc/c/c-parser.c.jj	2021-09-09 10:40:19.956263200 +0200
+++ gcc/c/c-parser.c	2021-09-10 17:06:10.966650277 +0200
@@ -7663,10 +7663,21 @@ c_parser_conditional_expression (c_parse
   c_inhibit_evaluation_warnings -= cond.value == truthvalue_true_node;
   location_t loc1 = make_location (exp1.get_start (), exp1.src_range);
   location_t loc2 = make_location (exp2.get_start (), exp2.src_range);
-  ret.value = build_conditional_expr (colon_loc, cond.value,
-				      cond.original_code == C_MAYBE_CONST_EXPR,
-				      exp1.value, exp1.original_type, loc1,
-				      exp2.value, exp2.original_type, loc2);
+  if (__builtin_expect (omp_atomic_lhs != NULL, 0)
+      && (TREE_CODE (cond.value) == GT_EXPR
+	  || TREE_CODE (cond.value) == LT_EXPR
+	  || TREE_CODE (cond.value) == EQ_EXPR)
+      && c_tree_equal (exp2.value, omp_atomic_lhs)
+      && (c_tree_equal (TREE_OPERAND (cond.value, 0), omp_atomic_lhs)
+	  || c_tree_equal (TREE_OPERAND (cond.value, 1), omp_atomic_lhs)))
+    ret.value = build3_loc (colon_loc, COND_EXPR, TREE_TYPE (omp_atomic_lhs),
+			    cond.value, exp1.value, exp2.value);
+  else
+    ret.value
+      = build_conditional_expr (colon_loc, cond.value,
+				cond.original_code == C_MAYBE_CONST_EXPR,
+				exp1.value, exp1.original_type, loc1,
+				exp2.value, exp2.original_type, loc2);
   ret.original_code = ERROR_MARK;
   if (exp1.value == error_mark_node || exp2.value == error_mark_node)
     ret.original_type = NULL;
@@ -7849,15 +7860,27 @@ c_parser_binary_expression (c_parser *pa
       = convert_lvalue_to_rvalue (stack[sp].loc,			      \
 				  stack[sp].expr, true, true);		      \
     if (__builtin_expect (omp_atomic_lhs != NULL_TREE, 0) && sp == 1	      \
-	&& c_parser_peek_token (parser)->type == CPP_SEMICOLON		      \
-	&& ((1 << stack[sp].prec)					      \
-	    & ((1 << PREC_BITOR) | (1 << PREC_BITXOR) | (1 << PREC_BITAND)    \
-	       | (1 << PREC_SHIFT) | (1 << PREC_ADD) | (1 << PREC_MULT)))     \
+	&& ((c_parser_next_token_is (parser, CPP_SEMICOLON)		      \
+	     && ((1 << stack[sp].prec)					      \
+		 & ((1 << PREC_BITOR) | (1 << PREC_BITXOR)		      \
+		     | (1 << PREC_BITAND) | (1 << PREC_SHIFT)		      \
+		     | (1 << PREC_ADD) | (1 << PREC_MULT)		      \
+		     | (1 << PREC_EQ))))				      \
+	    || ((c_parser_next_token_is (parser, CPP_QUERY)		      \
+		 || (omp_atomic_lhs == void_list_node			      \
+		     && c_parser_next_token_is (parser, CPP_CLOSE_PAREN)))    \
+		&& (stack[sp].prec == PREC_REL || stack[sp].prec == PREC_EQ)))\
 	&& stack[sp].op != TRUNC_MOD_EXPR				      \
+	&& stack[sp].op != GE_EXPR					      \
+	&& stack[sp].op != LE_EXPR					      \
+	&& stack[sp].op != NE_EXPR					      \
 	&& stack[0].expr.value != error_mark_node			      \
 	&& stack[1].expr.value != error_mark_node			      \
-	&& (c_tree_equal (stack[0].expr.value, omp_atomic_lhs)		      \
-	    || c_tree_equal (stack[1].expr.value, omp_atomic_lhs)))	      \
+	&& (omp_atomic_lhs == void_list_node				      \
+	    || c_tree_equal (stack[0].expr.value, omp_atomic_lhs)	      \
+	    || c_tree_equal (stack[1].expr.value, omp_atomic_lhs)	      \
+	    || (stack[sp].op == EQ_EXPR					      \
+		&& c_parser_peek_2nd_token (parser)->keyword == RID_IF)))     \
       {									      \
 	tree t = make_node (stack[1].op);				      \
 	TREE_TYPE (t) = TREE_TYPE (stack[0].expr.value);		      \
@@ -17655,14 +17678,45 @@ c_parser_omp_allocate (location_t loc, c
    capture-block:
      { v = x; update-stmt; } | { update-stmt; v = x; } | { v = x; x = expr; }
 
-  where x and v are lvalue expressions with scalar type.
+   OpenMP 5.1:
+   # pragma omp atomic compare new-line
+     conditional-update-atomic
+
+   # pragma omp atomic compare capture new-line
+     conditional-update-capture-atomic
+
+   conditional-update-atomic:
+     cond-expr-stmt | cond-update-stmt
+   cond-expr-stmt:
+     x = expr ordop x ? expr : x;
+     x = x ordop expr ? expr : x;
+     x = x == e ? d : x;
+   cond-update-stmt:
+     if (expr ordop x) { x = expr; }
+     if (x ordop expr) { x = expr; }
+     if (x == e) { x = d; }
+   ordop:
+     <, >
+   conditional-update-capture-atomic:
+     v = cond-expr-stmt
+     { v = x; cond-expr-stmt }
+     { cond-expr-stmt v = x; }
+     { v = x; cond-update-stmt }
+     { cond-update-stmt v = x; }
+     if (x == e) { x = d; } else { v = x; }
+     { r = x == e; if (r) { x = d; } }
+     { r = x == e; if (r) { x = d; } else { v = x; } }
+
+  where x, r and v are lvalue expressions with scalar type,
+  expr, e and d are expressions with scalar type and e might be
+  the same as v.
 
   LOC is the location of the #pragma token.  */
 
 static void
 c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc)
 {
-  tree lhs = NULL_TREE, rhs = NULL_TREE, v = NULL_TREE;
+  tree lhs = NULL_TREE, rhs = NULL_TREE, v = NULL_TREE, r = NULL_TREE;
   tree lhs1 = NULL_TREE, rhs1 = NULL_TREE;
   tree stmt, orig_lhs, unfolded_lhs = NULL_TREE, unfolded_lhs1 = NULL_TREE;
   enum tree_code code = ERROR_MARK, opcode = NOP_EXPR;
@@ -17674,6 +17728,12 @@ c_parser_omp_atomic (location_t loc, c_p
   bool non_lvalue_p;
   bool first = true;
   tree clauses = NULL_TREE;
+  bool capture = false;
+  bool compare = false;
+  bool weak = false;
+  enum omp_memory_order fail = OMP_MEMORY_ORDER_UNSPECIFIED;
+  bool no_semicolon = false;
+  bool extra_scope = false;
 
   while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL))
     {
@@ -17692,6 +17752,10 @@ c_parser_omp_atomic (location_t loc, c_p
 	  enum tree_code new_code = ERROR_MARK;
 	  enum omp_memory_order new_memory_order
 	    = OMP_MEMORY_ORDER_UNSPECIFIED;
+	  bool new_capture = false;
+	  bool new_compare = false;
+	  bool new_weak = false;
+	  enum omp_memory_order new_fail = OMP_MEMORY_ORDER_UNSPECIFIED;
 
 	  if (!strcmp (p, "read"))
 	    new_code = OMP_ATOMIC_READ;
@@ -17699,7 +17763,7 @@ c_parser_omp_atomic (location_t loc, c_p
 	    new_code = NOP_EXPR;
 	  else if (!strcmp (p, "update"))
 	    new_code = OMP_ATOMIC;
-	  else if (!strcmp (p, "capture"))
+	  else if (openacc && !strcmp (p, "capture"))
 	    new_code = OMP_ATOMIC_CAPTURE_NEW;
 	  else if (openacc)
 	    {
@@ -17707,6 +17771,47 @@ c_parser_omp_atomic (location_t loc, c_p
 	      error_at (cloc, "expected %<read%>, %<write%>, %<update%>, "
 			      "or %<capture%> clause");
 	    }
+	  else if (!strcmp (p, "capture"))
+	    new_capture = true;
+	  else if (!strcmp (p, "compare"))
+	    new_compare = true;
+	  else if (!strcmp (p, "weak"))
+	    new_weak = true;
+	  else if (!strcmp (p, "fail"))
+	    {
+	      matching_parens parens;
+
+	      c_parser_consume_token (parser);
+	      if (!parens.require_open (parser))
+		continue;
+
+	      if (c_parser_next_token_is (parser, CPP_NAME))
+		{
+		  const char *q
+		    = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value);
+
+		  if (!strcmp (q, "seq_cst"))
+		    new_fail = OMP_MEMORY_ORDER_SEQ_CST;
+		  else if (!strcmp (q, "acquire"))
+		    new_fail = OMP_MEMORY_ORDER_ACQUIRE;
+		  else if (!strcmp (q, "relaxed"))
+		    new_fail = OMP_MEMORY_ORDER_RELAXED;
+		}
+
+	      if (new_fail != OMP_MEMORY_ORDER_UNSPECIFIED)
+		{
+		  c_parser_consume_token (parser);
+		  if (fail != OMP_MEMORY_ORDER_UNSPECIFIED)
+		    error_at (cloc, "too many %qs clauses", "fail");
+		  else
+		    fail = new_fail;
+		}
+	      else
+		c_parser_error (parser, "expected %<seq_cst%>, %<acquire%> "
+					"or %<relaxed%>");
+	      parens.skip_until_found_close (parser);
+	      continue;
+	    }
 	  else if (!strcmp (p, "seq_cst"))
 	    new_memory_order = OMP_MEMORY_ORDER_SEQ_CST;
 	  else if (!strcmp (p, "acq_rel"))
@@ -17727,8 +17832,9 @@ c_parser_omp_atomic (location_t loc, c_p
 	    {
 	      p = NULL;
 	      error_at (cloc, "expected %<read%>, %<write%>, %<update%>, "
-			      "%<capture%>, %<seq_cst%>, %<acq_rel%>, "
-			      "%<release%>, %<relaxed%> or %<hint%> clause");
+			      "%<capture%>, %<compare%>, %<weak%>, %<fail%>, "
+			      "%<seq_cst%>, %<acq_rel%>, %<release%>, "
+			      "%<relaxed%> or %<hint%> clause");
 	    }
 	  if (p)
 	    {
@@ -17751,6 +17857,27 @@ c_parser_omp_atomic (location_t loc, c_p
 		  else
 		    memory_order = new_memory_order;
 		}
+	      else if (new_capture)
+		{
+		  if (capture)
+		    error_at (cloc, "too many %qs clauses", "capture");
+		  else
+		    capture = true;
+		}
+	      else if (new_compare)
+		{
+		  if (compare)
+		    error_at (cloc, "too many %qs clauses", "compare");
+		  else
+		    compare = true;
+		}
+	      else if (new_weak)
+		{
+		  if (weak)
+		    error_at (cloc, "too many %qs clauses", "weak");
+		  else
+		    weak = true;
+		}
 	      c_parser_consume_token (parser);
 	      continue;
 	    }
@@ -17761,6 +17888,30 @@ c_parser_omp_atomic (location_t loc, c_p
 
   if (code == ERROR_MARK)
     code = OMP_ATOMIC;
+  if (capture)
+    {
+      if (code != OMP_ATOMIC)
+	error_at (loc, "%qs clause is incompatible with %<read%> or %<write%> "
+		       "clauses", "capture");
+      else
+	code = OMP_ATOMIC_CAPTURE_NEW;
+    }
+  if (compare && code != OMP_ATOMIC && code != OMP_ATOMIC_CAPTURE_NEW)
+    {
+      error_at (loc, "%qs clause is incompatible with %<read%> or %<write%> "
+		      "clauses", "compare");
+      compare = false;
+    }
+  if (fail != OMP_MEMORY_ORDER_UNSPECIFIED && !compare)
+    {
+      error_at (loc, "%qs clause requires %qs clause", "fail", "compare");
+      fail = OMP_MEMORY_ORDER_UNSPECIFIED;
+    }
+  if (weak && !compare)
+    {
+      error_at (loc, "%qs clause requires %qs clause", "weak", "compare");
+      weak = false;
+    }
   if (openacc)
     memory_order = OMP_MEMORY_ORDER_RELAXED;
   else if (memory_order == OMP_MEMORY_ORDER_UNSPECIFIED)
@@ -17785,7 +17936,6 @@ c_parser_omp_atomic (location_t loc, c_p
 	      memory_order = OMP_MEMORY_ORDER_ACQUIRE;
 	      break;
 	    case NOP_EXPR: /* atomic write */
-	    case OMP_ATOMIC:
 	      memory_order = OMP_MEMORY_ORDER_RELEASE;
 	      break;
 	    default:
@@ -17801,36 +17951,32 @@ c_parser_omp_atomic (location_t loc, c_p
     switch (code)
       {
       case OMP_ATOMIC_READ:
-	if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
-	    || memory_order == OMP_MEMORY_ORDER_RELEASE)
+	if (memory_order == OMP_MEMORY_ORDER_RELEASE)
 	  {
 	    error_at (loc, "%<#pragma omp atomic read%> incompatible with "
-			   "%<acq_rel%> or %<release%> clauses");
+			   "%<release%> clause");
 	    memory_order = OMP_MEMORY_ORDER_SEQ_CST;
 	  }
+	else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL)
+	  memory_order = OMP_MEMORY_ORDER_ACQUIRE;
 	break;
       case NOP_EXPR: /* atomic write */
-	if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
-	    || memory_order == OMP_MEMORY_ORDER_ACQUIRE)
+	if (memory_order == OMP_MEMORY_ORDER_ACQUIRE)
 	  {
 	    error_at (loc, "%<#pragma omp atomic write%> incompatible with "
-			   "%<acq_rel%> or %<acquire%> clauses");
-	    memory_order = OMP_MEMORY_ORDER_SEQ_CST;
-	  }
-	break;
-      case OMP_ATOMIC:
-     /* case OMP_ATOMIC_CAPTURE_NEW: - or update to OpenMP 5.1 */
-	if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
-	    || memory_order == OMP_MEMORY_ORDER_ACQUIRE)
-	  {
-	    error_at (loc, "%<#pragma omp atomic update%> incompatible with "
-			   "%<acq_rel%> or %<acquire%> clauses");
+			   "%<acquire%> clause");
 	    memory_order = OMP_MEMORY_ORDER_SEQ_CST;
 	  }
+	else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL)
+	  memory_order = OMP_MEMORY_ORDER_RELEASE;
 	break;
       default:
 	break;
       }
+  if (fail != OMP_MEMORY_ORDER_UNSPECIFIED)
+    memory_order
+      = (enum omp_memory_order) (memory_order
+				 | (fail << OMP_FAIL_MEMORY_ORDER_SHIFT));
 
   switch (code)
     {
@@ -17879,6 +18025,9 @@ c_parser_omp_atomic (location_t loc, c_p
 	  c_parser_consume_token (parser);
 	  structured_block = true;
 	}
+      else if (compare
+	       && c_parser_next_token_is_keyword (parser, RID_IF))
+	break;
       else
 	{
 	  v = c_parser_cast_expression (parser, NULL).value;
@@ -17890,6 +18039,12 @@ c_parser_omp_atomic (location_t loc, c_p
 	    v = non_lvalue (v);
 	  if (!c_parser_require (parser, CPP_EQ, "expected %<=%>"))
 	    goto saw_error;
+	  if (compare && c_parser_next_token_is_keyword (parser, RID_IF))
+	    {
+	      eloc = c_parser_peek_token (parser)->location;
+	      error_at (eloc, "expected expression");
+	      goto saw_error;
+	    }
 	}
       break;
     default:
@@ -17899,6 +18054,179 @@ c_parser_omp_atomic (location_t loc, c_p
   /* For structured_block case we don't know yet whether
      old or new x should be captured.  */
 restart:
+  if (compare && c_parser_next_token_is_keyword (parser, RID_IF))
+    {
+      c_parser_consume_token (parser);
+
+      matching_parens parens;
+      if (!parens.require_open (parser))
+	goto saw_error;
+      eloc = c_parser_peek_token (parser)->location;
+      c_expr cmp_expr;
+      if (r)
+	{
+	  cmp_expr = c_parser_cast_expression (parser, NULL);
+	  cmp_expr = default_function_array_conversion (eloc, cmp_expr);
+	}
+      else
+	cmp_expr = c_parser_binary_expression (parser, NULL, void_list_node);
+      parens.skip_until_found_close (parser);
+      if (cmp_expr.value == error_mark_node)
+	goto saw_error;
+      if (r)
+	{
+	  if (!c_tree_equal (cmp_expr.value, unfolded_lhs))
+	    goto bad_if;
+	  cmp_expr.value = rhs1;
+	  rhs1 = NULL_TREE;
+	  gcc_assert (TREE_CODE (cmp_expr.value) == EQ_EXPR);
+	}
+      if (TREE_CODE (cmp_expr.value) == EQ_EXPR)
+	;
+      else if (!structured_block && code == OMP_ATOMIC_CAPTURE_NEW)
+	{
+	  error_at (EXPR_LOC_OR_LOC (cmp_expr.value, eloc),
+		    "expected %<==%> comparison in %<if%> condition");
+	  goto saw_error;
+	}
+      else if (TREE_CODE (cmp_expr.value) != GT_EXPR
+	       && TREE_CODE (cmp_expr.value) != LT_EXPR)
+	{
+	  error_at (EXPR_LOC_OR_LOC (cmp_expr.value, eloc),
+		    "expected %<==%>, %<<%> or %<>%> comparison in %<if%> "
+		    "condition");
+	  goto saw_error;
+	}
+      if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>"))
+	goto saw_error;
+
+      extra_scope = true;
+      eloc = c_parser_peek_token (parser)->location;
+      expr = c_parser_cast_expression (parser, NULL);
+      lhs = expr.value;
+      expr = default_function_array_conversion (eloc, expr);
+      unfolded_lhs = expr.value;
+      lhs = c_fully_fold (lhs, false, NULL, true);
+      orig_lhs = lhs;
+      if (lhs == error_mark_node)
+	goto saw_error;
+      if (!lvalue_p (unfolded_lhs))
+	lhs = non_lvalue (lhs);
+      if (!c_parser_next_token_is (parser, CPP_EQ))
+	{
+	  c_parser_error (parser, "expected %<=%>");
+	  goto saw_error;
+	}
+      c_parser_consume_token (parser);
+      eloc = c_parser_peek_token (parser)->location;
+      expr = c_parser_expr_no_commas (parser, NULL);
+      rhs1 = expr.value;
+
+      if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
+	goto saw_error;
+
+      if (!c_parser_require (parser, CPP_CLOSE_BRACE, "expected %<}%>"))
+	goto saw_error;
+
+      extra_scope = false;
+      no_semicolon = true;
+
+      if (c_tree_equal (TREE_OPERAND (cmp_expr.value, 0), unfolded_lhs))
+	{
+	  if (TREE_CODE (cmp_expr.value) == EQ_EXPR)
+	    {
+	      opcode = COND_EXPR;
+	      rhs = c_fully_fold (TREE_OPERAND (cmp_expr.value, 1),
+				  false, NULL, true);
+	      rhs1 = c_fully_fold (rhs1, false, NULL, true);
+	    }
+	  else if (c_tree_equal (TREE_OPERAND (cmp_expr.value, 1), rhs1))
+	    {
+	      opcode = (TREE_CODE (cmp_expr.value) == GT_EXPR
+			? MIN_EXPR : MAX_EXPR);
+	      rhs = c_fully_fold (rhs1, false, NULL, true);
+	      rhs1 = c_fully_fold (TREE_OPERAND (cmp_expr.value, 0),
+				   false, NULL, true);
+	    }
+	  else
+	    goto bad_if;
+	}
+      else if (TREE_CODE (cmp_expr.value) == EQ_EXPR)
+	goto bad_if;
+      else if (c_tree_equal (TREE_OPERAND (cmp_expr.value, 1), unfolded_lhs)
+	       && c_tree_equal (TREE_OPERAND (cmp_expr.value, 0), rhs1))
+	{
+	  opcode = (TREE_CODE (cmp_expr.value) == GT_EXPR
+		    ? MAX_EXPR : MIN_EXPR);
+	  rhs = c_fully_fold (rhs1, false, NULL, true);
+	  rhs1 = c_fully_fold (TREE_OPERAND (cmp_expr.value, 1),
+			       false, NULL, true);
+	}
+      else
+	{
+	bad_if:
+	  c_parser_error (parser,
+			  "invalid form of %<#pragma omp atomic compare%>");
+	  goto saw_error;
+	}
+
+      if (c_parser_next_token_is_keyword (parser, RID_ELSE))
+	{
+	  if (code != OMP_ATOMIC_CAPTURE_NEW
+	      || (structured_block && r == NULL_TREE)
+	      || TREE_CODE (cmp_expr.value) != EQ_EXPR)
+	    {
+	      eloc = c_parser_peek_token (parser)->location;
+	      error_at (eloc, "unexpected %<else%>");
+	      goto saw_error;
+	    }
+
+	  c_parser_consume_token (parser);
+
+	  if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>"))
+	    goto saw_error;
+
+	  extra_scope = true;
+	  v = c_parser_cast_expression (parser, NULL).value;
+	  non_lvalue_p = !lvalue_p (v);
+	  v = c_fully_fold (v, false, NULL, true);
+	  if (v == error_mark_node)
+	    goto saw_error;
+	  if (non_lvalue_p)
+	    v = non_lvalue (v);
+	  if (!c_parser_require (parser, CPP_EQ, "expected %<=%>"))
+	    goto saw_error;
+
+	  expr = c_parser_expr_no_commas (parser, NULL);
+
+	  if (!c_tree_equal (expr.value, unfolded_lhs))
+	    goto bad_if;
+
+	  if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
+	    goto saw_error;
+
+	  if (!c_parser_require (parser, CPP_CLOSE_BRACE, "expected %<}%>"))
+	    goto saw_error;
+
+	  extra_scope = false;
+	  code = OMP_ATOMIC_CAPTURE_OLD;
+	  if (r == NULL_TREE)
+	    /* Signal to c_finish_omp_atomic that in
+	       if (x == e) { x = d; } else { v = x; }
+	       case the store to v should be conditional.  */
+	    r = void_list_node;
+	}
+      else if (code == OMP_ATOMIC_CAPTURE_NEW && !structured_block)
+	{
+	  c_parser_require_keyword (parser, RID_ELSE, "expected %<else%>");
+	  goto saw_error;
+	}
+      else if (code == OMP_ATOMIC_CAPTURE_NEW
+	       && r != NULL_TREE
+	       && v == NULL_TREE)
+	code = OMP_ATOMIC;
+      goto stmt_done;
+    }
   eloc = c_parser_peek_token (parser)->location;
   expr = c_parser_cast_expression (parser, NULL);
   lhs = expr.value;
@@ -17908,9 +18236,14 @@ restart:
   orig_lhs = lhs;
   switch (TREE_CODE (lhs))
     {
+    invalid_compare:
+      error_at (eloc, "invalid form of %<pragma omp atomic compare%>");
+      /* FALLTHRU */
     case ERROR_MARK:
     saw_error:
       c_parser_skip_to_end_of_block_or_statement (parser);
+      if (extra_scope && c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
+	c_parser_consume_token (parser);
       if (structured_block)
 	{
 	  if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
@@ -17933,6 +18266,8 @@ restart:
       unfolded_lhs = NULL_TREE;
       opcode = PLUS_EXPR;
       rhs = integer_one_node;
+      if (compare)
+	goto invalid_compare;
       break;
 
     case POSTDECREMENT_EXPR:
@@ -17944,6 +18279,8 @@ restart:
       unfolded_lhs = NULL_TREE;
       opcode = MINUS_EXPR;
       rhs = integer_one_node;
+      if (compare)
+	goto invalid_compare;
       break;
 
     case COMPOUND_EXPR:
@@ -17973,6 +18310,8 @@ restart:
 		  && !structured_block
 		  && TREE_CODE (orig_lhs) == COMPOUND_EXPR)
 		code = OMP_ATOMIC_CAPTURE_OLD;
+	      if (compare)
+		goto invalid_compare;
 	      break;
 	    }
 	  if (TREE_CODE (TREE_OPERAND (lhs, 1)) == TRUTH_NOT_EXPR
@@ -17988,6 +18327,8 @@ restart:
 		  && !structured_block
 		  && TREE_CODE (orig_lhs) == COMPOUND_EXPR)
 		code = OMP_ATOMIC_CAPTURE_OLD;
+	      if (compare)
+		goto invalid_compare;
 	      break;
 	    }
 	}
@@ -17995,6 +18336,11 @@ restart:
     default:
       if (!lvalue_p (unfolded_lhs))
 	lhs = non_lvalue (lhs);
+      if (compare && !c_parser_next_token_is (parser, CPP_EQ))
+	{
+	  c_parser_error (parser, "expected %<=%>");
+	  goto saw_error;
+	}
       switch (c_parser_peek_token (parser)->type)
 	{
 	case CPP_MULT_EQ:
@@ -18041,6 +18387,8 @@ restart:
 	    case BIT_AND_EXPR:
 	    case BIT_IOR_EXPR:
 	    case BIT_XOR_EXPR:
+	      if (compare)
+		break;
 	      if (c_tree_equal (TREE_OPERAND (rhs1, 0), unfolded_lhs))
 		{
 		  opcode = TREE_CODE (rhs1);
@@ -18061,6 +18409,78 @@ restart:
 		  goto stmt_done;
 		}
 	      break;
+	    case COND_EXPR:
+	      if (!compare)
+		break;
+	      if (TREE_CODE (TREE_OPERAND (rhs1, 0)) != GT_EXPR
+		  && TREE_CODE (TREE_OPERAND (rhs1, 0)) != LT_EXPR
+		  && TREE_CODE (TREE_OPERAND (rhs1, 0)) != EQ_EXPR)
+		break;
+	      if (!TREE_OPERAND (rhs1, 1))
+		break;
+	      if (!c_tree_equal (TREE_OPERAND (rhs1, 2), unfolded_lhs))
+		break;
+	      if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0),
+				unfolded_lhs))
+		{
+		  if (TREE_CODE (TREE_OPERAND (rhs1, 0)) == EQ_EXPR)
+		    {
+		      opcode = COND_EXPR;
+		      rhs = c_fully_fold (TREE_OPERAND (TREE_OPERAND (rhs1,
+								      0), 1),
+					  false, NULL, true);
+		      rhs1 = c_fully_fold (TREE_OPERAND (rhs1, 1), false,
+					   NULL, true);
+		      goto stmt_done;
+		    }
+		  if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1),
+				    TREE_OPERAND (rhs1, 1)))
+		    {
+		      opcode = (TREE_CODE (TREE_OPERAND (rhs1, 0)) == GT_EXPR
+				? MIN_EXPR : MAX_EXPR);
+		      rhs = c_fully_fold (TREE_OPERAND (rhs1, 1), false, NULL,
+					  true);
+		      rhs1 = c_fully_fold (TREE_OPERAND (TREE_OPERAND (rhs1,
+								       0), 0),
+					   false, NULL, true);
+		      goto stmt_done;
+		    }
+		}
+	      else if (TREE_CODE (TREE_OPERAND (rhs1, 0)) == EQ_EXPR)
+		break;
+	      else if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1),
+				     unfolded_lhs))
+		{
+		  if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0),
+				    TREE_OPERAND (rhs1, 1)))
+		    {
+		      opcode = (TREE_CODE (TREE_OPERAND (rhs1, 0)) == GT_EXPR
+				? MAX_EXPR : MIN_EXPR);
+		      rhs = c_fully_fold (TREE_OPERAND (rhs1, 1), false, NULL,
+					  true);
+		      rhs1 = c_fully_fold (TREE_OPERAND (TREE_OPERAND (rhs1,
+								       0), 1),
+					   false, NULL, true);
+		      goto stmt_done;
+		    }
+		}
+	      break;
+	    case EQ_EXPR:
+	      if (!compare
+		  || code != OMP_ATOMIC_CAPTURE_NEW
+		  || !structured_block
+		  || v
+		  || r)
+		break;
+	      if (c_parser_next_token_is (parser, CPP_SEMICOLON)
+		  && c_parser_peek_2nd_token (parser)->keyword == RID_IF)
+		{
+		  r = lhs;
+		  lhs = NULL_TREE;
+		  c_parser_consume_token (parser);
+		  goto restart;
+		}
+	      break;
 	    case ERROR_MARK:
 	      goto saw_error;
 	    default:
@@ -18109,10 +18529,12 @@ restart:
       break;
     }
 stmt_done:
-  if (structured_block && code == OMP_ATOMIC_CAPTURE_NEW)
+  if (structured_block && code == OMP_ATOMIC_CAPTURE_NEW && r == NULL_TREE)
     {
-      if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
+      if (!no_semicolon
+	  && !c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
 	goto saw_error;
+      no_semicolon = false;
       v = c_parser_cast_expression (parser, NULL).value;
       non_lvalue_p = !lvalue_p (v);
       v = c_fully_fold (v, false, NULL, true);
@@ -18135,10 +18557,16 @@ stmt_done:
     }
   if (structured_block)
     {
-      c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
+      if (!no_semicolon)
+	c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
       c_parser_require (parser, CPP_CLOSE_BRACE, "expected %<}%>");
     }
 done:
+  if (weak && opcode != COND_EXPR)
+    {
+      error_at (loc, "%<weak%> clause requires atomic equality comparison");
+      weak = false;
+    }
   if (unfolded_lhs && unfolded_lhs1
       && !c_tree_equal (unfolded_lhs, unfolded_lhs1))
     {
@@ -18147,12 +18575,12 @@ done:
       stmt = error_mark_node;
     }
   else
-    stmt = c_finish_omp_atomic (loc, code, opcode, lhs, rhs, v, lhs1, rhs1,
-				swapped, memory_order);
+    stmt = c_finish_omp_atomic (loc, code, opcode, lhs, rhs, v, lhs1, rhs1, r,
+				swapped, memory_order, weak);
   if (stmt != error_mark_node)
     add_stmt (stmt);
 
-  if (!structured_block)
+  if (!structured_block && !no_semicolon)
     c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
 }
 
--- gcc/c/c-typeck.c.jj	2021-09-09 10:40:19.979262872 +0200
+++ gcc/c/c-typeck.c	2021-09-09 10:41:42.630084554 +0200
@@ -12426,6 +12426,13 @@ build_binary_op (location_t location, en
 	maybe_warn_bool_compare (location, code, orig_op0, orig_op1);
       break;
 
+    case MIN_EXPR:
+    case MAX_EXPR:
+      /* Used for OpenMP atomics.  */
+      gcc_assert (flag_openmp);
+      common = 1;
+      break;
+
     default:
       gcc_unreachable ();
     }
--- gcc/cp/parser.c.jj	2021-09-09 10:40:19.996262630 +0200
+++ gcc/cp/parser.c	2021-09-09 10:41:42.623084654 +0200
@@ -40193,7 +40193,6 @@ cp_parser_omp_atomic (cp_parser *parser,
 	      memory_order = OMP_MEMORY_ORDER_ACQUIRE;
 	      break;
 	    case NOP_EXPR: /* atomic write */
-	    case OMP_ATOMIC:
 	      memory_order = OMP_MEMORY_ORDER_RELEASE;
 	      break;
 	    default:
@@ -40209,31 +40208,24 @@ cp_parser_omp_atomic (cp_parser *parser,
     switch (code)
       {
       case OMP_ATOMIC_READ:
-	if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
-	    || memory_order == OMP_MEMORY_ORDER_RELEASE)
+	if (memory_order == OMP_MEMORY_ORDER_RELEASE)
 	  {
 	    error_at (loc, "%<#pragma omp atomic read%> incompatible with "
-			   "%<acq_rel%> or %<release%> clauses");
+			   "%<release%> clause");
 	    memory_order = OMP_MEMORY_ORDER_SEQ_CST;
 	  }
+	else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL)
+	  memory_order = OMP_MEMORY_ORDER_ACQUIRE;
 	break;
       case NOP_EXPR: /* atomic write */
-	if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
-	    || memory_order == OMP_MEMORY_ORDER_ACQUIRE)
+	if (memory_order == OMP_MEMORY_ORDER_ACQUIRE)
 	  {
 	    error_at (loc, "%<#pragma omp atomic write%> incompatible with "
-			   "%<acq_rel%> or %<acquire%> clauses");
-	    memory_order = OMP_MEMORY_ORDER_SEQ_CST;
-	  }
-	break;
-      case OMP_ATOMIC:
-	if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
-	    || memory_order == OMP_MEMORY_ORDER_ACQUIRE)
-	  {
-	    error_at (loc, "%<#pragma omp atomic update%> incompatible with "
-			   "%<acq_rel%> or %<acquire%> clauses");
+			   "%<acquire%> clause");
 	    memory_order = OMP_MEMORY_ORDER_SEQ_CST;
 	  }
+	else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL)
+	  memory_order = OMP_MEMORY_ORDER_RELEASE;
 	break;
       default:
 	break;
--- gcc/cp/semantics.c.jj	2021-09-09 10:40:26.093175709 +0200
+++ gcc/cp/semantics.c	2021-09-09 10:59:17.762032066 +0200
@@ -9956,7 +9956,7 @@ finish_omp_atomic (location_t loc, enum
 	  return;
 	}
       stmt = c_finish_omp_atomic (loc, code, opcode, lhs, rhs,
-				  v, lhs1, rhs1, swapped, mo,
+				  v, lhs1, rhs1, NULL_TREE, swapped, mo, false,
 				  processing_template_decl != 0);
       if (stmt == error_mark_node)
 	return;
--- gcc/testsuite/c-c++-common/gomp/atomic-17.c.jj	2021-09-09 10:40:20.088261318 +0200
+++ gcc/testsuite/c-c++-common/gomp/atomic-17.c	2021-09-09 10:41:42.624084639 +0200
@@ -22,8 +22,18 @@ foo ()
   v = i = i + 1;
   #pragma omp atomic read acquire
   v = i;
+  #pragma omp atomic acq_rel read
+  v = i;
   #pragma omp atomic release,write
   i = v;
+  #pragma omp atomic write,acq_rel
+  i = v;
   #pragma omp atomic hint(1),update,release
   f = f + 2.0;
+  #pragma omp atomic update ,acquire
+  i = i + 1;
+  #pragma omp atomic acq_rel update
+  i = i + 1;
+  #pragma omp atomic acq_rel,hint(0)
+  i = i + 1;
 }
--- gcc/testsuite/c-c++-common/gomp/atomic-18.c.jj	2021-09-09 10:40:20.088261318 +0200
+++ gcc/testsuite/c-c++-common/gomp/atomic-18.c	2021-09-09 10:41:42.624084639 +0200
@@ -8,28 +8,18 @@ foo (int j)
   i = i + 1;
   #pragma omp atomic seq_cst release	/* { dg-error "too many memory order clauses" } */
   i = i + 1;
-  #pragma omp atomic read,release	/* { dg-error "incompatible with 'acq_rel' or 'release' clauses" } */
+  #pragma omp atomic read,release	/* { dg-error "incompatible with 'release' clause" } */
   v = i;
-  #pragma omp atomic acq_rel read	/* { dg-error "incompatible with 'acq_rel' or 'release' clauses" } */
-  v = i;
-  #pragma omp atomic write acq_rel	/* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */
-  i = v;
-  #pragma omp atomic acquire , write	/* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */
+  #pragma omp atomic acquire , write	/* { dg-error "incompatible with 'acquire' clause" } */
   i = v;
-  #pragma omp atomic update ,acquire	/* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */
-  i = i + 1;
-  #pragma omp atomic acq_rel update	/* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */
-  i = i + 1;
-  #pragma omp atomic acq_rel,hint(0)	/* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */
-  i = i + 1;
-  #pragma omp atomic acquire		/* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */
-  i = i + 1;
-  #pragma omp atomic capture hint (0) capture	/* { dg-error "too many atomic clauses" } */
+  #pragma omp atomic capture hint (0) capture	/* { dg-error "too many 'capture' clauses" "" { target c } } */
+					/* { dg-error "too many atomic clauses" "" { target c++ } .-1 } */
   v = i = i + 1;
   #pragma omp atomic hint(j + 2)	/* { dg-error "constant integer expression" } */
   i = i + 1;
   #pragma omp atomic hint(f)		/* { dg-error "integ" } */
   i = i + 1;
-  #pragma omp atomic foobar		/* { dg-error "expected 'read', 'write', 'update', 'capture', 'seq_cst', 'acq_rel', 'release', 'relaxed' or 'hint' clause" } */
-  i = i + 1;				/* { dg-error "expected end of line before" "" { target *-*-* } .-1 } */
+  #pragma omp atomic foobar		/* { dg-error "expected 'read', 'write', 'update', 'capture', 'compare', 'weak', 'fail', 'seq_cst', 'acq_rel', 'release', 'relaxed' or 'hint' clause" "" { target c } } */
+					/* { dg-error "expected 'read', 'write', 'update', 'capture', 'seq_cst', 'acq_rel', 'release', 'relaxed' or 'hint' clause" "" { target c++ } .-1 } */
+  i = i + 1;				/* { dg-error "expected end of line before" "" { target *-*-* } .-2 } */
 }
--- gcc/testsuite/c-c++-common/gomp/atomic-21.c.jj	2020-04-15 09:58:30.369649495 +0200
+++ gcc/testsuite/c-c++-common/gomp/atomic-21.c	2021-09-09 17:25:18.782025085 +0200
@@ -1,6 +1,7 @@
 /* { dg-do compile } */
 /* { dg-additional-options "-fdump-tree-original" } */
-/* { dg-final { scan-tree-dump-times "omp atomic release" 4 "original" } } */
+/* { dg-final { scan-tree-dump-times "omp atomic release" 2 "original" } } */
+/* { dg-final { scan-tree-dump-times "omp atomic acq_rel" 2 "original" } } */
 /* { dg-final { scan-tree-dump-times "omp atomic read acquire" 1 "original" } } */
 /* { dg-final { scan-tree-dump-times "omp atomic capture acq_rel" 1 "original" } } */
 
--- gcc/testsuite/c-c++-common/gomp/atomic-25.c.jj	2021-09-09 10:41:42.624084639 +0200
+++ gcc/testsuite/c-c++-common/gomp/atomic-25.c	2021-09-10 13:18:43.096258564 +0200
@@ -0,0 +1,50 @@
+/* { dg-do compile { target c } } */
+
+int x, r, z;
+double d, v;
+long double ld;
+
+void
+foo (int y, double e, long double f)
+{
+  #pragma omp atomic compare update seq_cst
+  x = x > y ? y : x;
+  #pragma omp atomic compare relaxed
+  d = e > d ? e : d;
+  #pragma omp atomic compare
+  d = f < d ? f : d;
+  #pragma omp atomic compare seq_cst fail(relaxed)
+  x = 12U < x ? 12U : x;
+  #pragma omp atomic compare
+  x = x == 7 ? 24 : x;
+  #pragma omp atomic compare
+  x = x == 123UL ? 256LL : x;
+  #pragma omp atomic compare
+  ld = ld == f ? f + 5.0L : ld;
+  #pragma omp atomic compare
+  if (x == 9) { x = 5; }
+  #pragma omp atomic compare
+  if (x > 5) { x = 5; }
+  #pragma omp atomic compare
+  if (7 > x) { x = 7; }
+  #pragma omp atomic compare update capture seq_cst fail(acquire)
+  v = d = f > d ? f : d;
+  #pragma omp atomic update capture compare
+  v = x = x < 24ULL ? 24ULL : x;
+  #pragma omp atomic compare, capture, update
+  v = x = x == e ? f : x;
+  #pragma omp atomic capture compare
+  { v = d; if (d > e) { d = e; } }
+  #pragma omp atomic compare capture
+  { if (e < d) { d = e; } v = d; }
+  #pragma omp atomic compare capture
+  { y = x; if (x == 42) { x = 7; } }
+  #pragma omp atomic capture compare weak
+  { if (x == 42) { x = 7; } y = x; }
+  #pragma omp atomic capture compare fail(seq_cst)
+  if (d == 8.0) { d = 16.0; } else { v = d; }
+  #pragma omp atomic capture compare
+  { r = x == 8; if (r) { x = 24; } }
+  #pragma omp atomic compare capture
+  { r = x == y; if (r) { x = y + 6; } else { z = x; } }
+}
--- gcc/testsuite/c-c++-common/gomp/atomic-26.c.jj	2021-09-09 10:41:42.624084639 +0200
+++ gcc/testsuite/c-c++-common/gomp/atomic-26.c	2021-09-10 15:16:10.111808506 +0200
@@ -0,0 +1,63 @@
+/* { dg-do compile { target c } } */
+
+int x;
+double d;
+
+double
+foo (int y, double e, long double f)
+{
+  double v;
+  #pragma omp atomic compare compare	/* { dg-error "too many 'compare' clauses" } */
+  x = x > y ? y : x;
+  #pragma omp atomic compare fail(seq_cst) fail(seq_cst)	/* { dg-error "too many 'fail' clauses" } */
+  d = e > d ? e : d;
+  #pragma omp atomic compare,fail(seq_cst),fail(relaxed)	/* { dg-error "too many 'fail' clauses" } */
+  d = e > d ? e : d;
+  #pragma omp atomic compare weak weak	/* { dg-error "too many 'weak' clauses" } */
+  d = d == e ? e + 1.0 : d;
+  #pragma omp atomic read capture	/* { dg-error "'capture' clause is incompatible with 'read' or 'write' clauses" } */
+  v = d;
+  #pragma omp atomic capture, write	/* { dg-error "'capture' clause is incompatible with 'read' or 'write' clauses" } */
+  d = v;
+  #pragma omp atomic read compare	/* { dg-error "'compare' clause is incompatible with 'read' or 'write' clauses" } */
+  v = d;
+  #pragma omp atomic compare, write	/* { dg-error "'compare' clause is incompatible with 'read' or 'write' clauses" } */
+  d = v;
+  #pragma omp atomic read fail(seq_cst)	/* { dg-error "'fail' clause requires 'compare' clause" } */
+  v = d;
+  #pragma omp atomic fail(relaxed), write	/* { dg-error "'fail' clause requires 'compare' clause" } */
+  d = v;
+  #pragma omp atomic fail(relaxed) update	/* { dg-error "'fail' clause requires 'compare' clause" } */
+  d += 3.0;
+  #pragma omp atomic fail(relaxed)	/* { dg-error "'fail' clause requires 'compare' clause" } */
+  d += 3.0;
+  #pragma omp atomic capture fail(relaxed)	/* { dg-error "'fail' clause requires 'compare' clause" } */
+  v = d += 3.0;
+  #pragma omp atomic read weak		/* { dg-error "'weak' clause requires 'compare' clause" } */
+  v = d;
+  #pragma omp atomic weak, write	/* { dg-error "'weak' clause requires 'compare' clause" } */
+  d = v;
+  #pragma omp atomic weak update	/* { dg-error "'weak' clause requires 'compare' clause" } */
+  d += 3.0;
+  #pragma omp atomic weak		/* { dg-error "'weak' clause requires 'compare' clause" } */
+  d += 3.0;
+  #pragma omp atomic capture weak	/* { dg-error "'weak' clause requires 'compare' clause" } */
+  v = d += 3.0;
+  #pragma omp atomic compare,weak	/* { dg-error "'weak' clause requires atomic equality comparison" } */
+  d = e > d ? e : d;
+  #pragma omp atomic compare fail	/* { dg-error "expected '\\\(' before end of line" } */
+  d = e > d ? e : d;
+  #pragma omp atomic compare fail(	/* { dg-error "expected 'seq_cst', 'acquire' or 'relaxed' before end of line" } */
+  d = e > d ? e : d;
+  #pragma omp atomic compare fail()	/* { dg-error "expected 'seq_cst', 'acquire' or 'relaxed' before '\\\)' token" } */
+  d = e > d ? e : d;
+  #pragma omp atomic compare fail(foobar)	/* { dg-error "expected 'seq_cst', 'acquire' or 'relaxed' before 'foobar'" } */
+  d = e > d ? e : d;
+  #pragma omp atomic compare fail(acq_rel)	/* { dg-error "expected 'seq_cst', 'acquire' or 'relaxed' before 'acq_rel'" } */
+  d = e > d ? e : d;
+  #pragma omp atomic compare fail(release)	/* { dg-error "expected 'seq_cst', 'acquire' or 'relaxed' before 'release'" } */
+  d = e > d ? e : d;
+  #pragma omp atomic compare fail(seq_cst	/* { dg-error "expected '\\\)' before end of line" } */
+  d = e > d ? e : d;
+  return v;
+}
--- gcc/testsuite/c-c++-common/gomp/atomic-27.c.jj	2021-09-09 16:58:07.052709219 +0200
+++ gcc/testsuite/c-c++-common/gomp/atomic-27.c	2021-09-09 17:05:13.271786779 +0200
@@ -0,0 +1,41 @@
+/* PR middle-end/88968 */
+/* { dg-do compile { target c } } */
+
+struct __attribute__((packed)) S {
+  unsigned int a : 16;
+  unsigned int b : 1;
+} s;
+
+void
+foo (int y, int z)
+{
+  #pragma omp atomic compare
+  s.a = s.a == y ? z : s.a;
+}
+
+int
+bar (int y, int z)
+{
+  int r;
+  #pragma omp atomic compare capture
+  { r = s.a == y; if (r) { s.a = z; } }
+  return r;
+}
+
+int
+baz (int y, int z)
+{
+  int v;
+  #pragma omp atomic compare capture
+  if (s.a == y) { s.a = z; } else { v = s.a; }
+  return v;
+}
+
+int
+qux (int y, int z)
+{
+  int v;
+  #pragma omp atomic compare capture
+  v = s.a = s.a == y ? z : s.a;
+  return v;
+}
--- gcc/testsuite/c-c++-common/gomp/atomic-28.c.jj	2021-09-10 11:47:09.193274972 +0200
+++ gcc/testsuite/c-c++-common/gomp/atomic-28.c	2021-09-10 11:52:05.398115961 +0200
@@ -0,0 +1,43 @@
+/* { dg-do compile { target c } } */
+/* { dg-additional-options "-O2 -fdump-tree-ompexp" } */
+/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 4, 5, 5\\\);" 1 "ompexp" { target sync_int_long } } } */
+/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 4, 4, 2\\\);" 1 "ompexp" { target sync_int_long } } } */
+/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 260, 5, 0\\\);" 1 "ompexp" { target sync_int_long } } } */
+/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 4, 0, 0\\\);" 1 "ompexp" { target sync_int_long } } } */
+/* { dg-final { scan-tree-dump-not "__atomic_load_4 \\\(" "ompexp" { target sync_int_long } } } */
+
+int x;
+
+void
+foo (int y, int z)
+{
+  #pragma omp atomic compare seq_cst
+  x = x == y ? z : x;
+}
+
+int
+bar (int y, int z)
+{
+  int r;
+  #pragma omp atomic compare capture acq_rel fail (acquire)
+  { r = x == y; if (r) { x = z; } }
+  return r;
+}
+
+int
+baz (int y, int z)
+{
+  int v;
+  #pragma omp atomic compare capture seq_cst fail (relaxed) weak
+  if (x == y) { x = z; } else { v = x; }
+  return v;
+}
+
+int
+qux (int y, int z)
+{
+  int v;
+  #pragma omp atomic compare capture
+  v = x = x == y ? z : x;
+  return v;
+}
--- gcc/testsuite/c-c++-common/gomp/atomic-29.c.jj	2021-09-10 11:47:17.093164041 +0200
+++ gcc/testsuite/c-c++-common/gomp/atomic-29.c	2021-09-10 11:52:33.428722747 +0200
@@ -0,0 +1,43 @@
+/* { dg-do compile { target c } } */
+/* { dg-additional-options "-O2 -fdump-tree-ompexp" } */
+/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 8, 5, 5\\\);" 1 "ompexp" { target sync_int_long } } } */
+/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 8, 4, 2\\\);" 1 "ompexp" { target sync_int_long } } } */
+/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 264, 5, 0\\\);" 1 "ompexp" { target sync_int_long } } } */
+/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 8, 0, 0\\\);" 1 "ompexp" { target sync_int_long } } } */
+/* { dg-final { scan-tree-dump-not "__atomic_load_8 \\\(" "ompexp" { target sync_int_long } } } */
+
+double x;
+
+void
+foo (double y, double z)
+{
+  #pragma omp atomic compare seq_cst
+  x = x == y ? z : x;
+}
+
+double
+bar (double y, double z)
+{
+  int r;
+  #pragma omp atomic compare capture acq_rel fail (acquire)
+  { r = x == y; if (r) { x = z; } }
+  return r;
+}
+
+double
+baz (double y, double z)
+{
+  double v;
+  #pragma omp atomic compare capture seq_cst fail (relaxed) weak
+  if (x == y) { x = z; } else { v = x; }
+  return v;
+}
+
+double
+qux (double y, double z)
+{
+  double v;
+  #pragma omp atomic compare capture
+  v = x = x == y ? z : x;
+  return v;
+}
--- gcc/testsuite/c-c++-common/gomp/atomic-30.c.jj	2021-09-10 15:25:01.955425201 +0200
+++ gcc/testsuite/c-c++-common/gomp/atomic-30.c	2021-09-10 18:02:50.229520066 +0200
@@ -0,0 +1,137 @@
+/* { dg-do compile { target c } } */
+
+int x;
+double d, g;
+
+double
+foo (int y, double e, long double f)
+{
+  double v;
+  int r, r2 = 0;
+  #pragma omp atomic capture compare
+  v = if (d == e) { d = f; };	/* { dg-error "expected expression" } */
+  #pragma omp atomic compare
+  if;				/* { dg-error "expected '\\\(' before ';' token" } */
+  #pragma omp atomic compare
+  if (d >= e) { d = e; }	/* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */
+  #pragma omp atomic compare
+  if (d <= e) { d = e; }	/* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */
+  #pragma omp atomic compare
+  if (d != e) { d = e; }	/* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */
+  #pragma omp atomic compare
+  if (d + e) { d = e; }		/* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */
+  #pragma omp atomic capture compare
+  { r = d >= e; if (r) { d = f; } }	/* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */
+  #pragma omp atomic capture compare
+  { r = d <= e; if (r) { d = f; } }	/* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */
+  #pragma omp atomic capture compare
+  { r = d > e; if (r) { d = f; } }	/* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */
+  #pragma omp atomic capture compare
+  { r = d < e; if (r) { d = f; } }	/* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */
+  #pragma omp atomic capture compare
+  { r = d != e; if (r) { d = f; } }	/* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */
+  #pragma omp atomic capture compare
+  { r = d + e; if (r) { d = f; } }	/* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */
+  #pragma omp atomic capture compare
+  { r = d == e; if (r2) { d = f; } }	/* { dg-error "invalid form of '#pragma omp atomic compare' before '\{' token" } */
+  #pragma omp atomic capture compare
+  if (d > e) { d = e; }			/* { dg-error "expected '==' comparison in 'if' condition" } */
+  #pragma omp atomic capture compare
+  if (d < e) { d = e; }			/* { dg-error "expected '==' comparison in 'if' condition" } */
+  #pragma omp atomic compare
+  if (d < e) d = e;			/* { dg-error "expected '\{' before 'd'" } */
+  #pragma omp atomic compare
+  if (d == e) d = e + 1.0;		/* { dg-error "expected '\{' before 'd'" } */
+  #pragma omp atomic compare
+  if (d < e) { d += e; }		/* { dg-error "expected '=' before '\\\+=' token" } */
+  #pragma omp atomic compare
+  if (d < e) { d = e };			/* { dg-error "expected ';' before '\}' token" } */
+  #pragma omp atomic compare
+  if (d < e) { d = e; e = 1.0; }	/* { dg-error "expected '\}' before 'e'" } */
+  #pragma omp atomic compare
+  if (e == d) { d = f; };		/* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */
+  #pragma omp atomic compare
+  if (e == d) { g = f; };		/* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */
+  #pragma omp atomic compare
+  if (d < e) { g = e; };		/* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */
+  #pragma omp atomic compare
+  if (d > e) { g = e; };		/* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */
+  #pragma omp atomic compare
+  if (d < e) { d = g; };		/* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */
+  #pragma omp atomic compare
+  if (d > e) { d = g; };		/* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */
+  #pragma omp atomic compare
+  if (d == e) { d = f; } else ;		/* { dg-error "unexpected 'else'" } */
+  #pragma omp atomic compare capture
+  { if (d == e) { d = f; } else { v = d; } v = d; }		/* { dg-error "unexpected 'else'" } */
+  #pragma omp atomic compare
+  if (d < e) { d = e; } else { v = d; }	/* { dg-error "unexpected 'else'" } */
+  #pragma omp atomic compare capture
+  if (d == e) { d = f; } else v = d;	/* { dg-error "expected '\{' before 'v'" } */
+  #pragma omp atomic compare capture
+  if (d == e) { d = f; } else { v += d;	}	/* { dg-error "expected '=' before '\\\+=' token" } */
+  #pragma omp atomic compare capture
+  if (d == e) { d = f; } else { v = e; }	/* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */
+  #pragma omp atomic compare capture
+  if (d == e) { d = f; } else { v = d };	/* { dg-error "expected ';' before '\}' token" } */
+  #pragma omp atomic compare capture
+  if (d == e) { d = f; };		/* { dg-error "expected 'else' before ';' token" } */
+  #pragma omp atomic compare
+  x++;					/* { dg-error "invalid form of 'pragma omp atomic compare'" } */
+  #pragma omp atomic compare
+  x--;					/* { dg-error "invalid form of 'pragma omp atomic compare'" } */
+  #pragma omp atomic compare
+  ++x;					/* { dg-error "invalid form of 'pragma omp atomic compare'" } */
+  #pragma omp atomic compare
+  --x;					/* { dg-error "invalid form of 'pragma omp atomic compare'" } */
+  #pragma omp atomic compare
+  x += 3;				/* { dg-error "expected '=' before '\\\+=' token" } */
+  #pragma omp atomic compare
+  x -= 5;				/* { dg-error "expected '=' before '-=' token" } */
+  #pragma omp atomic compare
+  x *= 2;				/* { dg-error "expected '=' before '\\\*=' token" } */
+  #pragma omp atomic compare
+  x |= 5;				/* { dg-error "expected '=' before '\\\|=' token" } */
+  #pragma omp atomic compare
+  x &= ~5;				/* { dg-error "expected '=' before '\\\&=' token" } */
+  #pragma omp atomic compare
+  x ^= 5;				/* { dg-error "expected '=' before '\\\^=' token" } */
+  #pragma omp atomic compare
+  x = x + 3;				/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x - 5;				/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = 2 * x;				/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = 5 | x;				/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x & ~5;				/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x | 5;				/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x >= 5 ? 5 : x;			/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x <= 5 ? 5 : x;			/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x != 5 ? 7 : x;			/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = 5 == x ? 7 : x;			/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x == 5 ? x : 7;			/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x == 5 ? 9 : 7;			/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x > 5 ? 6 : x;			/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x < 5 ? 6 : x;			/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x > 5 ? x : 6;			/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic compare
+  x = x < 5 ? x : 6;			/* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */
+  #pragma omp atomic capture
+  r = x == 5;				/* { dg-error "invalid operator for '#pragma omp atomic' before '==' token" } */
+  #pragma omp atomic capture compare
+  r = x == 5;				/* { dg-error "expected '=' before '==' token" } */
+  #pragma omp atomic capture compare	/* { dg-error "'#pragma omp atomic compare capture' with non-integral comparison result" } */
+  { v = x == 5; if (v) { x = 6; } }
+}
--- gcc/testsuite/c-c++-common/goacc-gomp/atomic.c.jj	2020-11-06 11:14:19.942201229 +0100
+++ gcc/testsuite/c-c++-common/goacc-gomp/atomic.c	2021-09-09 17:10:58.686986511 +0200
@@ -37,7 +37,8 @@ foo ()
 
 /* { dg-final { scan-tree-dump-times "i = #pragma omp atomic read acquire" 1 "original" } } */
 /* { dg-final { scan-tree-dump-times "i = #pragma omp atomic read relaxed" 1 "original" } } */
-/* { dg-final { scan-tree-dump-times "#pragma omp atomic release" 2 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp atomic acq_rel" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp atomic release" 1 "original" } } */
 /* { dg-final { scan-tree-dump-times "#pragma omp atomic relaxed" 2 "original" } } */
 /* { dg-final { scan-tree-dump-times "v = #pragma omp atomic capture acq_rel" 1  "original" } } */
 /* { dg-final { scan-tree-dump-times "v = #pragma omp atomic capture relaxed" 2 "original" } } */
--- gcc/testsuite/gcc.dg/gomp/atomic-5.c.jj	2021-09-09 10:40:20.099261161 +0200
+++ gcc/testsuite/gcc.dg/gomp/atomic-5.c	2021-09-09 10:41:42.625084625 +0200
@@ -27,7 +27,7 @@ void f1(void)
   #pragma omp atomic
     bar() += 1;		/* { dg-error "lvalue required" } */
   #pragma omp atomic a	/* { dg-error "expected end of line" } */
-    x++;		/* { dg-error "expected 'read', 'write', 'update', 'capture', 'seq_cst', 'acq_rel', 'release', 'relaxed' or 'hint' clause" "" { target *-*-* } .-1 } */
+    x++;		/* { dg-error "expected 'read', 'write', 'update', 'capture', 'compare', 'weak', 'fail', 'seq_cst', 'acq_rel', 'release', 'relaxed' or 'hint' clause" "" { target *-*-* } .-1 } */
   #pragma omp atomic
     ;			/* { dg-error "expected expression" } */
   #pragma omp atomic
--- gcc/testsuite/g++.dg/gomp/atomic-18.C.jj	2020-04-15 09:58:30.372649450 +0200
+++ gcc/testsuite/g++.dg/gomp/atomic-18.C	2021-09-09 17:27:28.671222445 +0200
@@ -1,6 +1,7 @@
 // { dg-do compile }
 // { dg-additional-options "-fdump-tree-original" }
-// { dg-final { scan-tree-dump-times "omp atomic release" 5 "original" } }
+// { dg-final { scan-tree-dump-times "omp atomic release" 4 "original" } }
+// { dg-final { scan-tree-dump-times "omp atomic acq_rel" 1 "original" } }
 // { dg-final { scan-tree-dump-times "omp atomic seq_cst" 1 "original" } }
 // { dg-final { scan-tree-dump-times "omp atomic relaxed" 2 "original" } }
 // { dg-final { scan-tree-dump-times "omp atomic capture acq_rel" 3 "original" } }
--- libgomp/testsuite/libgomp.c-c++-common/atomic-19.c.jj	2021-09-10 12:09:43.320275112 +0200
+++ libgomp/testsuite/libgomp.c-c++-common/atomic-19.c	2021-09-10 14:24:03.200540163 +0200
@@ -0,0 +1,274 @@
+// { dg-do run { target c } }
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void abort (void);
+int x = 6;
+int w, y;
+
+int *
+foo (void)
+{
+  if (w)
+    abort ();
+  return &y;
+}
+
+int
+main ()
+{
+  int v, r;
+  #pragma omp atomic compare
+  x = x > 8 ? 8 : x;
+  #pragma omp atomic read
+  v = x;
+  if (v != 6)
+    abort ();
+  #pragma omp atomic compare
+  x = x > 4 ? 4 : x;
+  #pragma omp atomic read
+  v = x;
+  if (v != 4)
+    abort ();
+  #pragma omp atomic compare capture
+  v = x = x < 8 ? 8 : x;
+  if (v != 8)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 8)
+    abort ();
+  #pragma omp atomic capture compare
+  { v = x; x = x < 12 ? 12 : x; }
+  if (v != 8)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12)
+    abort ();
+  #pragma omp atomic capture compare
+  { v = x; x = x < 4 ? 4 : x; }
+  if (v != 12)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12)
+    abort ();
+  #pragma omp atomic write
+  x = -32;
+  #pragma omp atomic capture compare seq_cst fail(relaxed)
+  { x = 12U < x ? 12U : x; v = x; }
+  if (v != 12)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12)
+    abort ();
+  #pragma omp atomic compare
+  x = x == 12 ? 16 : x;
+  #pragma omp atomic read
+  v = x;
+  if (v != 16)
+    abort ();
+  r = 57;
+  #pragma omp atomic compare capture
+  v = x = x == 15 ? r + 7 : x;
+  if (v != 16)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 16)
+    abort ();
+  #pragma omp atomic capture, update, compare seq_cst fail(acquire)
+  { v = x; x = x == 73ULL - r ? 12LL : x; }
+  if (v != 16)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12)
+    abort ();
+  #pragma omp atomic update, compare, capture
+  { x = x == 69LL - r ? (unsigned char) 6 : x; v = x; }
+  if (v != 6)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 6)
+    abort ();
+  #pragma omp atomic compare
+  if (x > 8) { x = 8; }
+  #pragma omp atomic read
+  v = x;
+  if (v != 6)
+    abort ();
+  #pragma omp atomic compare
+  if (x > 4) { x = 4; }
+  #pragma omp atomic read
+  v = x;
+  if (v != 4)
+    abort ();
+  #pragma omp atomic compare capture
+  { if (x < 8) { x = 8; } v = x; }
+  if (v != 8)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 8)
+    abort ();
+  #pragma omp atomic capture compare
+  { v = x; if (x < 12) { x = 12; } }
+  if (v != 8)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12)
+    abort ();
+  #pragma omp atomic capture compare
+  { v = x; if (x < 4) { x = 4; } }
+  if (v != 12)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12)
+    abort ();
+  #pragma omp atomic write
+  x = -32;
+  #pragma omp atomic capture compare seq_cst fail(relaxed)
+  { if (12U < x) { x = 12U; } v = x; }
+  if (v != 12)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12)
+    abort ();
+  #pragma omp atomic compare
+  if (x == 12) { x = 16; }
+  #pragma omp atomic read
+  v = x;
+  if (v != 16)
+    abort ();
+  r = 57;
+  #pragma omp atomic compare capture
+  { if (x == 15) { x = r + 7; } v = x; }
+  if (v != 16)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 16)
+    abort ();
+  #pragma omp atomic capture, update, compare seq_cst fail(acquire)
+  { v = x; if (x == 73ULL - r) { x = 12LL; } }
+  if (v != 16)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12)
+    abort ();
+  #pragma omp atomic update, compare, capture
+  { if (x == 69LL - r) { x = (unsigned char) 6; } v = x; }
+  if (v != 6)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 6)
+    abort ();
+  v = 24;
+  #pragma omp atomic compare capture
+  if (x == 12) { x = 16; } else { v = x; }
+  if (v != 6)
+    abort ();
+  v = 32;
+  #pragma omp atomic read
+  v = x;
+  if (v != 6)
+    abort ();
+  v = 147;
+  #pragma omp atomic capture compare
+  if (x == 6) { x = 57; } else { v = x; }
+  if (v != 147)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 57)
+    abort ();
+  #pragma omp atomic update, capture, compare, weak, seq_cst, fail (relaxed)
+  { r = x == 137; if (r) { x = 174; } }
+  if (r)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 57)
+    abort ();
+  #pragma omp atomic compare capture fail (relaxed)
+  { r = x == 57; if (r) { x = 6; } }
+  if (r != 1)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 6)
+    abort ();
+  v = -5;
+  #pragma omp atomic capture compare
+  { r = x == 17; if (r) { x = 25; } else { v = x; } }
+  if (r || v != 6)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 6)
+    abort ();
+  v = 15;
+  #pragma omp atomic capture compare
+  { r = x == 6; if (r) { x = 23; } else { v = x; } }
+  if (r != 1 || v != 15)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 23)
+    abort ();
+  w = 1;
+  #pragma omp atomic compare capture
+  if (x == 23) { x = 57; } else { foo ()[0] = x; }
+  #pragma omp atomic read
+  v = x;
+  if (v != 57)
+    abort ();
+  #pragma omp atomic capture update compare
+  { r = x == 57; if (r) { x = 23; } else { foo ()[0] = x; } }
+  if (r != 1)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 23)
+    abort ();
+  w = 0;
+  #pragma omp atomic compare capture
+  if (x == 24) { x = 57; } else { foo ()[0] = x; }
+  if (y != 23)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 23)
+    abort ();
+  y = -5;
+  #pragma omp atomic capture update compare
+  {
+    r = x == 57;
+    if (r)
+      {
+	x = 27;
+      }
+    else
+      {
+	foo ()[0] = x;
+      }
+  }
+  if (r || y != 23)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 23)
+    abort ();
+  return 0;
+}
--- libgomp/testsuite/libgomp.c-c++-common/atomic-20.c.jj	2021-09-10 14:08:45.330336955 +0200
+++ libgomp/testsuite/libgomp.c-c++-common/atomic-20.c	2021-09-10 14:27:14.746865949 +0200
@@ -0,0 +1,203 @@
+// { dg-do run { target c } }
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void abort (void);
+float x = 6.0f;
+
+int
+main ()
+{
+  float v;
+  int r;
+  #pragma omp atomic compare
+  x = x > 8.0f ? 8.0f : x;
+  #pragma omp atomic read
+  v = x;
+  if (v != 6.0f)
+    abort ();
+  #pragma omp atomic compare
+  x = x > 4.0f ? 4.0f : x;
+  #pragma omp atomic read
+  v = x;
+  if (v != 4.0f)
+    abort ();
+  #pragma omp atomic compare capture
+  v = x = x < 8.0f ? 8.0f : x;
+  if (v != 8.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 8)
+    abort ();
+  #pragma omp atomic capture compare
+  { v = x; x = x < 12.0f ? 12.0f : x; }
+  if (v != 8.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12.0f)
+    abort ();
+  #pragma omp atomic capture compare
+  { v = x; x = x < 4.0f ? 4.0f : x; }
+  if (v != 12.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12.0f)
+    abort ();
+  #pragma omp atomic compare
+  x = x == 12.0 ? 16.0L : x;
+  #pragma omp atomic read
+  v = x;
+  if (v != 16.0)
+    abort ();
+  r = 57;
+  #pragma omp atomic compare capture
+  v = x = x == 15.0f ? r + 7.0f : x;
+  if (v != 16.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 16.0f)
+    abort ();
+  #pragma omp atomic capture, update, compare seq_cst fail(acquire)
+  { v = x; x = x == 73.0L - r ? 12.0f : x; }
+  if (v != 16.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12.0f)
+    abort ();
+  #pragma omp atomic update, compare, capture
+  { x = x == 69.0 - r ? 6.0f : x; v = x; }
+  if (v != 6.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 6.0f)
+    abort ();
+  #pragma omp atomic compare
+  if (x > 8.0f) { x = 8.0f; }
+  #pragma omp atomic read
+  v = x;
+  if (v != 6.0f)
+    abort ();
+  #pragma omp atomic compare
+  if (x > 4.0) { x = 4.0; }
+  #pragma omp atomic read
+  v = x;
+  if (v != 4.0f)
+    abort ();
+  #pragma omp atomic compare capture
+  { if (x < 8.0f) { x = 8.0f; } v = x; }
+  if (v != 8.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 8.0f)
+    abort ();
+  #pragma omp atomic capture compare
+  { v = x; if (x < 12.0f) { x = 12.0f; } }
+  if (v != 8.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12.0f)
+    abort ();
+  #pragma omp atomic capture compare
+  { v = x; if (x < 4.0L) { x = 4.0L; } }
+  if (v != 12.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12.0f)
+    abort ();
+  #pragma omp atomic compare
+  if (x == 12.0f) { x = 16.0L; }
+  #pragma omp atomic read
+  v = x;
+  if (v != 16.0f)
+    abort ();
+  r = 57.0;
+  #pragma omp atomic compare capture
+  { if (x == 15.0f) { x = r + 7.0f; } v = x; }
+  if (v != 16.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 16.0f)
+    abort ();
+  #pragma omp atomic capture, update, compare seq_cst fail(acquire)
+  { v = x; if (x == 73.0L - r) { x = 12.0L; } }
+  if (v != 16.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 12.0f)
+    abort ();
+  #pragma omp atomic update, compare, capture
+  { if (x == 69.0L - r) { x = 6.0; } v = x; }
+  if (v != 6.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 6.0f)
+    abort ();
+  v = 24;
+  #pragma omp atomic compare capture
+  if (x == 12.0f) { x = 16.0f; } else { v = x; }
+  if (v != 6.0f)
+    abort ();
+  v = 32.0f;
+  #pragma omp atomic read
+  v = x;
+  if (v != 6.0f)
+    abort ();
+  v = 147.0f;
+  #pragma omp atomic capture compare
+  if (x == 6.0f) { x = 57.0f; } else { v = x; }
+  if (v != 147.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 57.0f)
+    abort ();
+  #pragma omp atomic update, capture, compare, weak, seq_cst, fail (relaxed)
+  { r = x == 137.0f; if (r) { x = 174.0f; } }
+  if (r)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 57.0f)
+    abort ();
+  #pragma omp atomic compare capture fail (relaxed)
+  { r = x == 57.0f; if (r) { x = 6.0f; } }
+  if (r != 1)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 6.0f)
+    abort ();
+  v = -5.0f;
+  #pragma omp atomic capture compare
+  { r = x == 17.0L; if (r) { x = 25.0; } else { v = x; } }
+  if (r || v != 6.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 6.0f)
+    abort ();
+  v = 15.0f;
+  #pragma omp atomic capture compare
+  { r = x == 6.0f; if (r) { x = 23.0f; } else { v = x; } }
+  if (r != 1 || v != 15.0f)
+    abort ();
+  #pragma omp atomic read
+  v = x;
+  if (v != 23.0f)
+    abort ();
+  return 0;
+}
--- libgomp/testsuite/libgomp.c-c++-common/atomic-21.c.jj	2021-09-10 14:27:19.051805671 +0200
+++ libgomp/testsuite/libgomp.c-c++-common/atomic-21.c	2021-09-10 15:05:47.316510552 +0200
@@ -0,0 +1,49 @@
+// { dg-do run { target c } }
+
+double d;
+long double ld;
+
+int
+main ()
+{
+  double e = __builtin_copysign (0.0, -1.0), v;
+  long double le = __builtin_copysignl (0.0L, -1.0L), lv;
+  if (__builtin_memcmp (&d, &e, sizeof (d)) != 0)
+    {
+      /* Verify == comparison for atomics is done as with memcmp.  */
+      #pragma omp atomic compare
+      d = d == e ? 5.0 : d;
+      #pragma omp atomic read
+      v = d;
+      if (v != 0.0)
+	__builtin_abort ();
+      #pragma omp atomic compare
+      d = d == 0.0 ? 5.0 : d;
+      #pragma omp atomic read
+      v = d;
+      if (v != 5.0)
+	__builtin_abort ();
+    }
+  if (__builtin_memcmp (&ld, &le, sizeof (ld)) != 0)
+    {
+      __builtin_memset (&ld, 0xff, sizeof (ld));
+      #pragma omp atomic write
+      ld = 0.0L;
+      __asm volatile ("" : : "g" (&ld) : "memory");
+      /* Verify == comparison for atomics is done as with memcmp
+	 with __builtin_clear_padding if needed.  */
+      #pragma omp atomic compare
+      ld = ld == le ? 5.0L : ld;
+      #pragma omp atomic read
+      lv = ld;
+      if (lv != 0.0L)
+	__builtin_abort ();
+      #pragma omp atomic compare
+      ld = ld == 0.0L ? 5.0L : ld;
+      #pragma omp atomic read
+      lv = ld;
+      if (lv != 5.0L)
+	__builtin_abort ();
+    }
+  return 0;
+}

	Jakub


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: openmp: Implement OpenMP 5.1 atomics, so far for C only
  2021-09-10 18:46 openmp: Implement OpenMP 5.1 atomics, so far for C only Jakub Jelinek
@ 2021-09-13 11:57 ` Christophe Lyon
  2021-09-13 14:40   ` Jakub Jelinek
  0 siblings, 1 reply; 4+ messages in thread
From: Christophe Lyon @ 2021-09-13 11:57 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches, Tobias Burnus

On Fri, Sep 10, 2021 at 8:47 PM Jakub Jelinek via Gcc-patches <
gcc-patches@gcc.gnu.org> wrote:

> Hi!
>
> This patch implements OpenMP 5.1 atomics (with clarifications from
> upcoming 5.2).
> The most important changes are that it is now possible to write (for C/C++,
> for Fortran it was possible before already) min/max atomics and more
> importantly
> compare and exchange in various forms.
> Also, acq_rel is now allowed on read/write and acq_rel/acquire are allowed
> on
> update, and there are new compare, weak and fail clauses.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, committed to trunk.
>
> C++ support will follow next week hopefully.  Various new tests in
> c-c++-common are now with { target c }, that is temporary until the C++
> support is there.
>
> 2021-09-10  Jakub Jelinek  <jakub@redhat.com>
>
> gcc/
>         * tree-core.h (enum omp_memory_order): Add OMP_MEMORY_ORDER_MASK,
>         OMP_FAIL_MEMORY_ORDER_UNSPECIFIED, OMP_FAIL_MEMORY_ORDER_RELAXED,
>         OMP_FAIL_MEMORY_ORDER_ACQUIRE, OMP_FAIL_MEMORY_ORDER_RELEASE,
>         OMP_FAIL_MEMORY_ORDER_ACQ_REL, OMP_FAIL_MEMORY_ORDER_SEQ_CST and
>         OMP_FAIL_MEMORY_ORDER_MASK enumerators.
>         (OMP_FAIL_MEMORY_ORDER_SHIFT): Define.
>         * gimple-pretty-print.c (dump_gimple_omp_atomic_load,
>         dump_gimple_omp_atomic_store): Print [weak] for weak atomic
>         load/store.
>         * gimple.h (enum gf_mask): Change GF_OMP_ATOMIC_MEMORY_ORDER
>         to 6-bit mask, adjust GF_OMP_ATOMIC_NEED_VALUE value and add
>         GF_OMP_ATOMIC_WEAK.
>         (gimple_omp_atomic_weak_p, gimple_omp_atomic_set_weak): New inline
>         functions.
>         * tree.h (OMP_ATOMIC_WEAK): Define.
>         * tree-pretty-print.c (dump_omp_atomic_memory_order): Adjust for
>         fail memory order being encoded in the same enum and also print
>         fail clause if present.
>         (dump_generic_node): Print weak clause if OMP_ATOMIC_WEAK.
>         * gimplify.c (goa_stabilize_expr): Add target_expr and rhs
> arguments,
>         handle pre_p == NULL case as a test mode that only returns value
>         but doesn't change gimplify nor change anything otherwise, adjust
>         recursive calls, add MODIFY_EXPR, ADDR_EXPR, COND_EXPR, TARGET_EXPR
>         and CALL_EXPR handling, adjust COMPOUND_EXPR handling for
>         __builtin_clear_padding calls, for !rhs gimplify as lvalue rather
>         than rvalue.
>         (gimplify_omp_atomic): Adjust goa_stabilize_expr caller.  Handle
>         COND_EXPR rhs.  Set weak flag on gimple load/store for
>         OMP_ATOMIC_WEAK.
>         * omp-expand.c (omp_memory_order_to_fail_memmodel): New function.
>         (omp_memory_order_to_memmodel): Adjust for fail clause encoded
>         in the same enum.
>         (expand_omp_atomic_cas): New function.
>         (expand_omp_atomic_pipeline): Use omp_memory_order_to_fail_memmodel
>         function.
>         (expand_omp_atomic): Attempt to optimize atomic compare and
> exchange
>         using expand_omp_atomic_cas.
> gcc/c-family/
>         * c-common.h (c_finish_omp_atomic): Add r and weak arguments.
>         * c-omp.c: Include gimple-fold.h.
>         (c_finish_omp_atomic): Add r and weak arguments.  Add support for
>         OpenMP 5.1 atomics.
> gcc/c/
>         * c-parser.c (c_parser_conditional_expression): If omp_atomic_lhs
> and
>         cond.value is >, < or == with omp_atomic_lhs as one of the
> operands,
>         don't call build_conditional_expr, instead build a COND_EXPR
> directly.
>         (c_parser_binary_expression): Avoid calling parser_build_binary_op
>         if omp_atomic_lhs even in more cases for >, < or ==.
>         (c_parser_omp_atomic): Update function comment for OpenMP 5.1
> atomics,
>         parse OpenMP 5.1 atomics and fail, compare and weak clauses, allow
>         acq_rel on atomic read/write and acq_rel/acquire clauses on update.
>         * c-typeck.c (build_binary_op): For flag_openmp only handle
>         MIN_EXPR/MAX_EXPR.
> gcc/cp/
>         * parser.c (cp_parser_omp_atomic): Allow acq_rel on atomic
> read/write
>         and acq_rel/acquire clauses on update.
>         * semantics.c (finish_omp_atomic): Adjust c_finish_omp_atomic
> caller.
> gcc/testsuite/
>         * c-c++-common/gomp/atomic-17.c (foo): Add tests for atomic read,
>         write or update with acq_rel clause and atomic update with acquire
> clause.
>         * c-c++-common/gomp/atomic-18.c (foo): Adjust expected diagnostics
>         wording, remove tests moved to atomic-17.c.
>         * c-c++-common/gomp/atomic-21.c: Expect only 2 omp atomic release
> and
>         2 omp atomic acq_rel directives instead of 4 omp atomic release.
>         * c-c++-common/gomp/atomic-25.c: New test.
>         * c-c++-common/gomp/atomic-26.c: New test.
>         * c-c++-common/gomp/atomic-27.c: New test.
>         * c-c++-common/gomp/atomic-28.c: New test.
>         * c-c++-common/gomp/atomic-29.c: New test.
>         * c-c++-common/gomp/atomic-30.c: New test.
>         * c-c++-common/goacc-gomp/atomic.c: Expect 1 omp atomic release and
>         1 omp atomic_acq_rel instead of 2 omp atomic release directives.
>         * gcc.dg/gomp/atomic-5.c: Adjust expected error diagnostic wording.
>         * g++.dg/gomp/atomic-18.C:Expect 4 omp atomic release and
>         1 omp atomic_acq_rel instead of 5 omp atomic release directives.
> libgomp/
>         * testsuite/libgomp.c-c++-common/atomic-19.c: New test.
>         * testsuite/libgomp.c-c++-common/atomic-20.c: New test.
>         * testsuite/libgomp.c-c++-common/atomic-21.c: New test.
>
> --- gcc/tree-core.h.jj  2021-09-09 10:40:20.116260919 +0200
> +++ gcc/tree-core.h     2021-09-09 10:41:42.631084540 +0200
> @@ -583,8 +583,17 @@ enum omp_memory_order {
>    OMP_MEMORY_ORDER_ACQUIRE,
>    OMP_MEMORY_ORDER_RELEASE,
>    OMP_MEMORY_ORDER_ACQ_REL,
> -  OMP_MEMORY_ORDER_SEQ_CST
> +  OMP_MEMORY_ORDER_SEQ_CST,
> +  OMP_MEMORY_ORDER_MASK = 7,
> +  OMP_FAIL_MEMORY_ORDER_UNSPECIFIED = OMP_MEMORY_ORDER_UNSPECIFIED * 8,
> +  OMP_FAIL_MEMORY_ORDER_RELAXED = OMP_MEMORY_ORDER_RELAXED * 8,
> +  OMP_FAIL_MEMORY_ORDER_ACQUIRE = OMP_MEMORY_ORDER_ACQUIRE * 8,
> +  OMP_FAIL_MEMORY_ORDER_RELEASE = OMP_MEMORY_ORDER_RELEASE * 8,
> +  OMP_FAIL_MEMORY_ORDER_ACQ_REL = OMP_MEMORY_ORDER_ACQ_REL * 8,
> +  OMP_FAIL_MEMORY_ORDER_SEQ_CST = OMP_MEMORY_ORDER_SEQ_CST * 8,
> +  OMP_FAIL_MEMORY_ORDER_MASK = OMP_MEMORY_ORDER_MASK * 8
>  };
> +#define OMP_FAIL_MEMORY_ORDER_SHIFT 3
>
>  /* There is a TYPE_QUAL value for each type qualifier.  They can be
>     combined by bitwise-or to form the complete set of qualifiers for a
> --- gcc/gimple-pretty-print.c.jj        2021-09-09 10:40:20.032262117 +0200
> +++ gcc/gimple-pretty-print.c   2021-09-09 10:41:42.625084625 +0200
> @@ -2563,6 +2563,8 @@ dump_gimple_omp_atomic_load (pretty_prin
>                                     gimple_omp_atomic_memory_order (gs));
>        if (gimple_omp_atomic_need_value_p (gs))
>         pp_string (buffer, " [needed]");
> +      if (gimple_omp_atomic_weak_p (gs))
> +       pp_string (buffer, " [weak]");
>        newline_and_indent (buffer, spc + 2);
>        dump_generic_node (buffer, gimple_omp_atomic_load_lhs (gs),
>                          spc, flags, false);
> @@ -2597,6 +2599,8 @@ dump_gimple_omp_atomic_store (pretty_pri
>        pp_space (buffer);
>        if (gimple_omp_atomic_need_value_p (gs))
>         pp_string (buffer, "[needed] ");
> +      if (gimple_omp_atomic_weak_p (gs))
> +       pp_string (buffer, "[weak] ");
>        pp_left_paren (buffer);
>        dump_generic_node (buffer, gimple_omp_atomic_store_val (gs),
>                          spc, flags, false);
> --- gcc/gimple.h.jj     2021-09-09 10:40:20.041261988 +0200
> +++ gcc/gimple.h        2021-09-09 10:41:42.625084625 +0200
> @@ -194,8 +194,9 @@ enum gf_mask {
>      GF_OMP_RETURN_NOWAIT       = 1 << 0,
>
>      GF_OMP_SECTION_LAST                = 1 << 0,
> -    GF_OMP_ATOMIC_MEMORY_ORDER  = (1 << 3) - 1,
> -    GF_OMP_ATOMIC_NEED_VALUE   = 1 << 3,
> +    GF_OMP_ATOMIC_MEMORY_ORDER  = (1 << 6) - 1,
> +    GF_OMP_ATOMIC_NEED_VALUE   = 1 << 6,
> +    GF_OMP_ATOMIC_WEAK         = 1 << 7,
>      GF_PREDICT_TAKEN           = 1 << 15
>  };
>
> @@ -2446,6 +2447,29 @@ gimple_omp_atomic_set_need_value (gimple
>  }
>
>
> +/* Return true if OMP atomic load/store statement G has the
> +   GF_OMP_ATOMIC_WEAK flag set.  */
> +
> +static inline bool
> +gimple_omp_atomic_weak_p (const gimple *g)
> +{
> +  if (gimple_code (g) != GIMPLE_OMP_ATOMIC_LOAD)
> +    GIMPLE_CHECK (g, GIMPLE_OMP_ATOMIC_STORE);
> +  return (gimple_omp_subcode (g) & GF_OMP_ATOMIC_WEAK) != 0;
> +}
> +
> +
> +/* Set the GF_OMP_ATOMIC_WEAK flag on G.  */
> +
> +static inline void
> +gimple_omp_atomic_set_weak (gimple *g)
> +{
> +  if (gimple_code (g) != GIMPLE_OMP_ATOMIC_LOAD)
> +    GIMPLE_CHECK (g, GIMPLE_OMP_ATOMIC_STORE);
> +  g->subcode |= GF_OMP_ATOMIC_WEAK;
> +}
> +
> +
>  /* Return the memory order of the OMP atomic load/store statement G.  */
>
>  static inline enum omp_memory_order
> --- gcc/tree.h.jj       2021-09-09 10:40:20.132260691 +0200
> +++ gcc/tree.h  2021-09-09 10:41:42.626084611 +0200
> @@ -1529,6 +1529,11 @@ class auto_suppress_location_wrappers
>    (TREE_RANGE_CHECK (NODE, OMP_ATOMIC, \
>
>  OMP_ATOMIC_CAPTURE_NEW)->base.u.omp_atomic_memory_order)
>
> +/* Weak clause on OMP_ATOMIC*.  */
> +#define OMP_ATOMIC_WEAK(NODE) \
> +  (TREE_RANGE_CHECK (NODE, OMP_ATOMIC, \
> +                    OMP_ATOMIC_CAPTURE_NEW)->base.public_flag)
> +
>  /* True on a PRIVATE clause if its decl is kept around for debugging
>     information only and its DECL_VALUE_EXPR is supposed to point
>     to what it has been remapped to.  */
> --- gcc/tree-pretty-print.c.jj  2021-09-09 10:40:20.131260705 +0200
> +++ gcc/tree-pretty-print.c     2021-09-09 10:41:42.626084611 +0200
> @@ -1492,7 +1492,7 @@ dump_block_node (pretty_printer *pp, tre
>  void
>  dump_omp_atomic_memory_order (pretty_printer *pp, enum omp_memory_order
> mo)
>  {
> -  switch (mo)
> +  switch (mo & OMP_MEMORY_ORDER_MASK)
>      {
>      case OMP_MEMORY_ORDER_RELAXED:
>        pp_string (pp, " relaxed");
> @@ -1514,6 +1514,22 @@ dump_omp_atomic_memory_order (pretty_pri
>      default:
>        gcc_unreachable ();
>      }
> +  switch (mo & OMP_FAIL_MEMORY_ORDER_MASK)
> +    {
> +    case OMP_FAIL_MEMORY_ORDER_RELAXED:
> +      pp_string (pp, " fail(relaxed)");
> +      break;
> +    case OMP_FAIL_MEMORY_ORDER_SEQ_CST:
> +      pp_string (pp, " fail(seq_cst)");
> +      break;
> +    case OMP_FAIL_MEMORY_ORDER_ACQUIRE:
> +      pp_string (pp, " fail(acquire)");
> +      break;
> +    case OMP_FAIL_MEMORY_ORDER_UNSPECIFIED:
> +      break;
> +    default:
> +      gcc_unreachable ();
> +    }
>  }
>
>  /* Helper to dump a MEM_REF node.  */
> @@ -3629,6 +3645,8 @@ dump_generic_node (pretty_printer *pp, t
>
>      case OMP_ATOMIC:
>        pp_string (pp, "#pragma omp atomic");
> +      if (OMP_ATOMIC_WEAK (node))
> +       pp_string (pp, " weak");
>        dump_omp_atomic_memory_order (pp, OMP_ATOMIC_MEMORY_ORDER (node));
>        newline_and_indent (pp, spc + 2);
>        dump_generic_node (pp, TREE_OPERAND (node, 0), spc, flags, false);
> @@ -3649,6 +3667,8 @@ dump_generic_node (pretty_printer *pp, t
>      case OMP_ATOMIC_CAPTURE_OLD:
>      case OMP_ATOMIC_CAPTURE_NEW:
>        pp_string (pp, "#pragma omp atomic capture");
> +      if (OMP_ATOMIC_WEAK (node))
> +       pp_string (pp, " weak");
>        dump_omp_atomic_memory_order (pp, OMP_ATOMIC_MEMORY_ORDER (node));
>        newline_and_indent (pp, spc + 2);
>        dump_generic_node (pp, TREE_OPERAND (node, 0), spc, flags, false);
> --- gcc/gimplify.c.jj   2021-09-09 10:40:20.057261760 +0200
> +++ gcc/gimplify.c      2021-09-10 18:29:35.243673591 +0200
> @@ -13725,14 +13725,15 @@ goa_lhs_expr_p (tree expr, tree addr)
>
>  static int
>  goa_stabilize_expr (tree *expr_p, gimple_seq *pre_p, tree lhs_addr,
> -                   tree lhs_var)
> +                   tree lhs_var, tree &target_expr, bool rhs)
>  {
>    tree expr = *expr_p;
>    int saw_lhs;
>
>    if (goa_lhs_expr_p (expr, lhs_addr))
>      {
> -      *expr_p = lhs_var;
> +      if (pre_p)
> +       *expr_p = lhs_var;
>        return 1;
>      }
>    if (is_gimple_val (expr))
> @@ -13744,11 +13745,11 @@ goa_stabilize_expr (tree *expr_p, gimple
>      case tcc_binary:
>      case tcc_comparison:
>        saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p,
> lhs_addr,
> -                                    lhs_var);
> +                                    lhs_var, target_expr, true);
>        /* FALLTHRU */
>      case tcc_unary:
>        saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p,
> lhs_addr,
> -                                    lhs_var);
> +                                    lhs_var, target_expr, true);
>        break;
>      case tcc_expression:
>        switch (TREE_CODE (expr))
> @@ -13760,36 +13761,131 @@ goa_stabilize_expr (tree *expr_p, gimple
>         case TRUTH_XOR_EXPR:
>         case BIT_INSERT_EXPR:
>           saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p,
> -                                        lhs_addr, lhs_var);
> +                                        lhs_addr, lhs_var, target_expr,
> true);
>           /* FALLTHRU */
>         case TRUTH_NOT_EXPR:
>           saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p,
> -                                        lhs_addr, lhs_var);
> +                                        lhs_addr, lhs_var, target_expr,
> true);
> +         break;
> +       case MODIFY_EXPR:
> +         saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p,
> +                                        lhs_addr, lhs_var, target_expr,
> true);
> +         /* FALLTHRU */
> +       case ADDR_EXPR:
> +         saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p,
> +                                        lhs_addr, lhs_var, target_expr,
> false);
>           break;
>         case COMPOUND_EXPR:
> +         /* Special-case __builtin_clear_padding call before
> +            __builtin_memcmp.  */
> +         if (TREE_CODE (TREE_OPERAND (expr, 0)) == CALL_EXPR)
> +           {
> +             tree fndecl = get_callee_fndecl (TREE_OPERAND (expr, 0));
> +             if (fndecl
> +                 && fndecl_built_in_p (fndecl, BUILT_IN_CLEAR_PADDING)
> +                 && VOID_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0))))
> +               {
> +                 saw_lhs = goa_stabilize_expr (&TREE_OPERAND (expr, 0),
> pre_p,
> +                                               lhs_addr, lhs_var,
> +                                               target_expr, true);
> +                 if (!saw_lhs)
> +                   {
> +                     expr = TREE_OPERAND (expr, 1);
> +                     if (!pre_p)
> +                       return goa_stabilize_expr (&expr, pre_p, lhs_addr,
> +                                                  lhs_var, target_expr,
> true);
> +                     *expr_p = expr;
> +                     return goa_stabilize_expr (expr_p, pre_p, lhs_addr,
> +                                                lhs_var, target_expr,
> true);
> +                   }
> +                 else
> +                   {
> +                     saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr,
> 1),
> +                                                    pre_p, lhs_addr,
> lhs_var,
> +                                                    target_expr, rhs);
> +                     break;
> +                   }
> +               }
> +           }
>           /* Break out any preevaluations from cp_build_modify_expr.  */
>           for (; TREE_CODE (expr) == COMPOUND_EXPR;
>                expr = TREE_OPERAND (expr, 1))
> -           gimplify_stmt (&TREE_OPERAND (expr, 0), pre_p);
> +           if (pre_p)
> +             gimplify_stmt (&TREE_OPERAND (expr, 0), pre_p);
> +         if (!pre_p)
> +           return goa_stabilize_expr (&expr, pre_p, lhs_addr, lhs_var,
> +                                      target_expr, rhs);
>           *expr_p = expr;
> -         return goa_stabilize_expr (expr_p, pre_p, lhs_addr, lhs_var);
> +         return goa_stabilize_expr (expr_p, pre_p, lhs_addr, lhs_var,
> +                                    target_expr, rhs);
> +       case COND_EXPR:
> +         if (!goa_stabilize_expr (&TREE_OPERAND (expr, 0), NULL, lhs_addr,
> +                                  lhs_var, target_expr, true))
> +           break;
> +         saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p,
> +                                        lhs_addr, lhs_var, target_expr,
> true);
> +         saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p,
> +                                        lhs_addr, lhs_var, target_expr,
> true);
> +         saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 2), pre_p,
> +                                        lhs_addr, lhs_var, target_expr,
> true);
> +         break;
> +       case TARGET_EXPR:
> +         if (TARGET_EXPR_INITIAL (expr))
> +           {
> +             if (expr == target_expr)
> +               saw_lhs = 1;
> +             else
> +               {
> +                 saw_lhs = goa_stabilize_expr (&TARGET_EXPR_INITIAL
> (expr),
> +                                               pre_p, lhs_addr, lhs_var,
> +                                               target_expr, true);
> +                 if (saw_lhs && target_expr == NULL_TREE && pre_p)
> +                   target_expr = expr;
> +               }
> +           }
> +         break;
>         default:
>           break;
>         }
>        break;
>      case tcc_reference:
> -      if (TREE_CODE (expr) == BIT_FIELD_REF)
> +      if (TREE_CODE (expr) == BIT_FIELD_REF
> +         || TREE_CODE (expr) == VIEW_CONVERT_EXPR)
>         saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p,
> -                                      lhs_addr, lhs_var);
> +                                      lhs_addr, lhs_var, target_expr,
> true);
> +      break;
> +    case tcc_vl_exp:
> +      if (TREE_CODE (expr) == CALL_EXPR)
> +       {
> +         if (tree fndecl = get_callee_fndecl (expr))
> +           if (fndecl_built_in_p (fndecl, BUILT_IN_CLEAR_PADDING)
> +               || fndecl_built_in_p (fndecl, BUILT_IN_MEMCMP))
> +             {
> +               int nargs = call_expr_nargs (expr);
> +               for (int i = 0; i < nargs; i++)
> +                 saw_lhs |= goa_stabilize_expr (&CALL_EXPR_ARG (expr, i),
> +                                                pre_p, lhs_addr, lhs_var,
> +                                                target_expr, true);
> +             }
> +         if (saw_lhs == 0 && VOID_TYPE_P (TREE_TYPE (expr)))
> +           {
> +             if (pre_p)
> +               gimplify_stmt (&expr, pre_p);
> +             return 0;
> +           }
> +       }
>        break;
>      default:
>        break;
>      }
>
> -  if (saw_lhs == 0)
> +  if (saw_lhs == 0 && pre_p)
>      {
>        enum gimplify_status gs;
> -      gs = gimplify_expr (expr_p, pre_p, NULL, is_gimple_val, fb_rvalue);
> +      if (rhs)
> +       gs = gimplify_expr (expr_p, pre_p, NULL, is_gimple_val, fb_rvalue);
> +      else
> +       gs = gimplify_expr (expr_p, pre_p, NULL, is_gimple_lvalue,
> fb_lvalue);
>        if (gs != GS_ALL_DONE)
>         saw_lhs = -1;
>      }
> @@ -13809,9 +13905,12 @@ gimplify_omp_atomic (tree *expr_p, gimpl
>    tree tmp_load;
>    gomp_atomic_load *loadstmt;
>    gomp_atomic_store *storestmt;
> +  tree target_expr = NULL_TREE;
>
>    tmp_load = create_tmp_reg (type);
> -  if (rhs && goa_stabilize_expr (&rhs, pre_p, addr, tmp_load) < 0)
> +  if (rhs
> +      && goa_stabilize_expr (&rhs, pre_p, addr, tmp_load, target_expr,
> +                            true) < 0)
>      return GS_ERROR;
>
>    if (gimplify_expr (&addr, pre_p, NULL, is_gimple_val, fb_rvalue)
> @@ -13825,11 +13924,14 @@ gimplify_omp_atomic (tree *expr_p, gimpl
>      {
>        /* BIT_INSERT_EXPR is not valid for non-integral bitfield
>          representatives.  Use BIT_FIELD_REF on the lhs instead.  */
> -      if (TREE_CODE (rhs) == BIT_INSERT_EXPR
> +      tree rhsarg = rhs;
> +      if (TREE_CODE (rhs) == COND_EXPR)
> +       rhsarg = TREE_OPERAND (rhs, 1);
> +      if (TREE_CODE (rhsarg) == BIT_INSERT_EXPR
>           && !INTEGRAL_TYPE_P (TREE_TYPE (tmp_load)))
>         {
> -         tree bitpos = TREE_OPERAND (rhs, 2);
> -         tree op1 = TREE_OPERAND (rhs, 1);
> +         tree bitpos = TREE_OPERAND (rhsarg, 2);
> +         tree op1 = TREE_OPERAND (rhsarg, 1);
>           tree bitsize;
>           tree tmp_store = tmp_load;
>           if (TREE_CODE (*expr_p) == OMP_ATOMIC_CAPTURE_OLD)
> @@ -13838,17 +13940,25 @@ gimplify_omp_atomic (tree *expr_p, gimpl
>             bitsize = bitsize_int (TYPE_PRECISION (TREE_TYPE (op1)));
>           else
>             bitsize = TYPE_SIZE (TREE_TYPE (op1));
> -         gcc_assert (TREE_OPERAND (rhs, 0) == tmp_load);
> -         tree t = build2_loc (EXPR_LOCATION (rhs),
> +         gcc_assert (TREE_OPERAND (rhsarg, 0) == tmp_load);
> +         tree t = build2_loc (EXPR_LOCATION (rhsarg),
>                                MODIFY_EXPR, void_type_node,
> -                              build3_loc (EXPR_LOCATION (rhs),
> BIT_FIELD_REF,
> -                                          TREE_TYPE (op1), tmp_store,
> bitsize,
> -                                          bitpos), op1);
> +                              build3_loc (EXPR_LOCATION (rhsarg),
> +                                          BIT_FIELD_REF, TREE_TYPE (op1),
> +                                          tmp_store, bitsize, bitpos),
> op1);
> +         if (TREE_CODE (rhs) == COND_EXPR)
> +           t = build3_loc (EXPR_LOCATION (rhs), COND_EXPR, void_type_node,
> +                           TREE_OPERAND (rhs, 0), t, void_node);
>           gimplify_and_add (t, pre_p);
>           rhs = tmp_store;
>         }
> -      if (gimplify_expr (&rhs, pre_p, NULL, is_gimple_val, fb_rvalue)
> -         != GS_ALL_DONE)
> +      bool save_allow_rhs_cond_expr = gimplify_ctxp->allow_rhs_cond_expr;
> +      if (TREE_CODE (rhs) == COND_EXPR)
> +       gimplify_ctxp->allow_rhs_cond_expr = true;
> +      enum gimplify_status gs = gimplify_expr (&rhs, pre_p, NULL,
> +                                              is_gimple_val, fb_rvalue);
> +      gimplify_ctxp->allow_rhs_cond_expr = save_allow_rhs_cond_expr;
> +      if (gs != GS_ALL_DONE)
>         return GS_ERROR;
>      }
>
> @@ -13856,6 +13966,11 @@ gimplify_omp_atomic (tree *expr_p, gimpl
>      rhs = tmp_load;
>    storestmt
>      = gimple_build_omp_atomic_store (rhs, OMP_ATOMIC_MEMORY_ORDER
> (*expr_p));
> +  if (TREE_CODE (*expr_p) != OMP_ATOMIC_READ && OMP_ATOMIC_WEAK (*expr_p))
> +    {
> +      gimple_omp_atomic_set_weak (loadstmt);
> +      gimple_omp_atomic_set_weak (storestmt);
> +    }
>    gimplify_seq_add_stmt (pre_p, storestmt);
>    switch (TREE_CODE (*expr_p))
>      {
> --- gcc/omp-expand.c.jj 2021-09-09 10:40:20.072261546 +0200
> +++ gcc/omp-expand.c    2021-09-10 15:05:47.315510567 +0200
> @@ -8487,22 +8487,58 @@ expand_omp_synch (struct omp_region *reg
>      }
>  }
>
> +/* Translate enum omp_memory_order to enum memmodel for the embedded
> +   fail clause in there.  */
> +
> +static enum memmodel
> +omp_memory_order_to_fail_memmodel (enum omp_memory_order mo)
> +{
> +  switch (mo & OMP_FAIL_MEMORY_ORDER_MASK)
> +    {
> +    case OMP_FAIL_MEMORY_ORDER_UNSPECIFIED:
> +      switch (mo & OMP_MEMORY_ORDER_MASK)
> +       {
> +       case OMP_MEMORY_ORDER_RELAXED: return MEMMODEL_RELAXED;
> +       case OMP_MEMORY_ORDER_ACQUIRE: return MEMMODEL_ACQUIRE;
> +       case OMP_MEMORY_ORDER_RELEASE: return MEMMODEL_RELAXED;
> +       case OMP_MEMORY_ORDER_ACQ_REL: return MEMMODEL_ACQUIRE;
> +       case OMP_MEMORY_ORDER_SEQ_CST: return MEMMODEL_SEQ_CST;
> +       default: break;
> +       }
> +      gcc_unreachable ();
> +    case OMP_FAIL_MEMORY_ORDER_RELAXED: return MEMMODEL_RELAXED;
> +    case OMP_FAIL_MEMORY_ORDER_ACQUIRE: return MEMMODEL_ACQUIRE;
> +    case OMP_FAIL_MEMORY_ORDER_SEQ_CST: return MEMMODEL_SEQ_CST;
> +    default: gcc_unreachable ();
> +    }
> +}
> +
>  /* Translate enum omp_memory_order to enum memmodel.  The two enums
>     are using different numbers so that OMP_MEMORY_ORDER_UNSPECIFIED
> -   is 0.  */
> +   is 0 and omp_memory_order has the fail mode encoded in it too.  */
>
>  static enum memmodel
>  omp_memory_order_to_memmodel (enum omp_memory_order mo)
>  {
> -  switch (mo)
> +  enum memmodel ret, fail_ret;
> +  switch (mo & OMP_MEMORY_ORDER_MASK)
>      {
> -    case OMP_MEMORY_ORDER_RELAXED: return MEMMODEL_RELAXED;
> -    case OMP_MEMORY_ORDER_ACQUIRE: return MEMMODEL_ACQUIRE;
> -    case OMP_MEMORY_ORDER_RELEASE: return MEMMODEL_RELEASE;
> -    case OMP_MEMORY_ORDER_ACQ_REL: return MEMMODEL_ACQ_REL;
> -    case OMP_MEMORY_ORDER_SEQ_CST: return MEMMODEL_SEQ_CST;
> +    case OMP_MEMORY_ORDER_RELAXED: ret = MEMMODEL_RELAXED; break;
> +    case OMP_MEMORY_ORDER_ACQUIRE: ret = MEMMODEL_ACQUIRE; break;
> +    case OMP_MEMORY_ORDER_RELEASE: ret = MEMMODEL_RELEASE; break;
> +    case OMP_MEMORY_ORDER_ACQ_REL: ret = MEMMODEL_ACQ_REL; break;
> +    case OMP_MEMORY_ORDER_SEQ_CST: ret = MEMMODEL_SEQ_CST; break;
>      default: gcc_unreachable ();
>      }
> +  /* If we drop the -Winvalid-memory-model warning for C++17 P0418R2,
> +     we can just return ret here unconditionally.  Otherwise, work around
> +     it here and make sure fail memmodel is not stronger.  */
> +  if ((mo & OMP_FAIL_MEMORY_ORDER_MASK) ==
> OMP_FAIL_MEMORY_ORDER_UNSPECIFIED)
> +    return ret;
> +  fail_ret = omp_memory_order_to_fail_memmodel (mo);
> +  if (fail_ret > ret)
> +    return fail_ret;
> +  return ret;
>  }
>
>  /* A subroutine of expand_omp_atomic.  Attempt to implement the atomic
> @@ -8782,6 +8818,261 @@ expand_omp_atomic_fetch_op (basic_block
>    return true;
>  }
>
> +/* A subroutine of expand_omp_atomic.  Attempt to implement the atomic
> +   compare and exchange as an ATOMIC_COMPARE_EXCHANGE internal function.
> +   Returns false if the expression is not of the proper form.  */
> +
> +static bool
> +expand_omp_atomic_cas (basic_block load_bb, tree addr,
> +                      tree loaded_val, tree stored_val, int index)
> +{
> +  /* We expect to find the following sequences:
> +
> +   load_bb:
> +       GIMPLE_OMP_ATOMIC_LOAD (tmp, mem)
> +
> +   store_bb:
> +       val = tmp == e ? d : tmp;
> +       GIMPLE_OMP_ATOMIC_STORE (val)
> +
> +     or in store_bb instead:
> +       tmp2 = tmp == e;
> +       val = tmp2 ? d : tmp;
> +       GIMPLE_OMP_ATOMIC_STORE (val)
> +
> +     or:
> +       tmp3 = VIEW_CONVERT_EXPR<integral_type>(tmp);
> +       val = e == tmp3 ? d : tmp;
> +       GIMPLE_OMP_ATOMIC_STORE (val)
> +
> +     etc.  */
> +
> +
> +  basic_block store_bb = single_succ (load_bb);
> +  gimple_stmt_iterator gsi = gsi_last_nondebug_bb (store_bb);
> +  gimple *store_stmt = gsi_stmt (gsi);
> +  if (!store_stmt || gimple_code (store_stmt) != GIMPLE_OMP_ATOMIC_STORE)
> +    return false;
> +  gsi_prev_nondebug (&gsi);
> +  if (gsi_end_p (gsi))
> +    return false;
> +  gimple *condexpr_stmt = gsi_stmt (gsi);
> +  if (!is_gimple_assign (condexpr_stmt)
> +      || gimple_assign_rhs_code (condexpr_stmt) != COND_EXPR)
> +    return false;
> +  if (!operand_equal_p (gimple_assign_lhs (condexpr_stmt), stored_val, 0))
> +    return false;
> +  gimple *cond_stmt = NULL;
> +  gimple *vce_stmt = NULL;
> +  gsi_prev_nondebug (&gsi);
> +  if (!gsi_end_p (gsi))
> +    {
> +      cond_stmt = gsi_stmt (gsi);
> +      if (!is_gimple_assign (cond_stmt))
> +       return false;
> +      if (gimple_assign_rhs_code (cond_stmt) == EQ_EXPR)
> +       {
> +         gsi_prev_nondebug (&gsi);
> +         if (!gsi_end_p (gsi))
> +           {
> +             vce_stmt = gsi_stmt (gsi);
> +             if (!is_gimple_assign (vce_stmt)
> +                 || gimple_assign_rhs_code (vce_stmt) !=
> VIEW_CONVERT_EXPR)
> +               return false;
> +           }
> +       }
> +      else if (gimple_assign_rhs_code (cond_stmt) == VIEW_CONVERT_EXPR)
> +       std::swap (vce_stmt, cond_stmt);
> +      else
> +       return false;
> +      if (vce_stmt)
> +       {
> +         tree vce_rhs = gimple_assign_rhs1 (vce_stmt);
> +         if (TREE_CODE (vce_rhs) != VIEW_CONVERT_EXPR
> +             || !operand_equal_p (TREE_OPERAND (vce_rhs, 0), loaded_val))
> +           return false;
> +         if (!INTEGRAL_TYPE_P (TREE_TYPE (vce_rhs))
> +             || !SCALAR_FLOAT_TYPE_P (TREE_TYPE (loaded_val))
> +             || !tree_int_cst_equal (TYPE_SIZE (TREE_TYPE (vce_rhs)),
> +                                     TYPE_SIZE (TREE_TYPE (loaded_val))))
> +           return false;
> +         gsi_prev_nondebug (&gsi);
> +         if (!gsi_end_p (gsi))
> +           return false;
> +       }
> +    }
> +  tree cond = gimple_assign_rhs1 (condexpr_stmt);
> +  tree cond_op1, cond_op2;
> +  if (cond_stmt)
> +    {
> +      if (!operand_equal_p (cond, gimple_assign_lhs (cond_stmt)))
> +       return false;
> +      cond_op1 = gimple_assign_rhs1 (cond_stmt);
> +      cond_op2 = gimple_assign_rhs2 (cond_stmt);
> +    }
> +  else if (TREE_CODE (cond) != EQ_EXPR && TREE_CODE (cond) != NE_EXPR)
> +    return false;
> +  else
> +    {
> +      cond_op1 = TREE_OPERAND (cond, 0);
> +      cond_op2 = TREE_OPERAND (cond, 1);
> +    }
> +  tree d;
> +  if (TREE_CODE (cond) == NE_EXPR)
> +    {
> +      if (!operand_equal_p (gimple_assign_rhs2 (condexpr_stmt),
> loaded_val))
> +       return false;
> +      d = gimple_assign_rhs3 (condexpr_stmt);
> +    }
> +  else if (!operand_equal_p (gimple_assign_rhs3 (condexpr_stmt),
> loaded_val))
> +    return false;
> +  else
> +    d = gimple_assign_rhs2 (condexpr_stmt);
> +  tree e = vce_stmt ? gimple_assign_lhs (vce_stmt) : loaded_val;
> +  if (operand_equal_p (e, cond_op1))
> +    e = cond_op2;
> +  else if (operand_equal_p (e, cond_op2))
> +    e = cond_op1;
> +  else
> +    return false;
> +
> +  location_t loc = gimple_location (store_stmt);
> +  gimple *load_stmt = last_stmt (load_bb);
> +  bool need_new = gimple_omp_atomic_need_value_p (store_stmt);
> +  bool need_old = gimple_omp_atomic_need_value_p (load_stmt);
> +  bool weak = gimple_omp_atomic_weak_p (load_stmt);
> +  enum omp_memory_order omo = gimple_omp_atomic_memory_order (load_stmt);
> +  tree mo = build_int_cst (NULL, omp_memory_order_to_memmodel (omo));
> +  tree fmo = build_int_cst (NULL, omp_memory_order_to_fail_memmodel
> (omo));
> +  gcc_checking_assert (!need_old || !need_new);
> +
> +  enum built_in_function fncode
> +    = (enum built_in_function) ((int) BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_N
> +                               + index + 1);
> +  tree cmpxchg = builtin_decl_explicit (fncode);
> +  if (cmpxchg == NULL_TREE)
> +    return false;
> +  tree itype = TREE_TYPE (TREE_TYPE (cmpxchg));
> +
> +  if (!can_compare_and_swap_p (TYPE_MODE (itype), true)
> +      || !can_atomic_load_p (TYPE_MODE (itype)))
> +    return false;
> +
> +  tree type = TYPE_MAIN_VARIANT (TREE_TYPE (loaded_val));
> +  if (SCALAR_FLOAT_TYPE_P (type) && !vce_stmt)
> +    return false;
> +
> +  gsi = gsi_for_stmt (store_stmt);
> +  if (!useless_type_conversion_p (itype, TREE_TYPE (e)))
> +    {
> +      tree ne = create_tmp_reg (itype);
> +      gimple *g = gimple_build_assign (ne, NOP_EXPR, e);
> +      gimple_set_location (g, loc);
> +      gsi_insert_before (&gsi, g, GSI_SAME_STMT);
> +      e = ne;
> +    }
> +  if (!useless_type_conversion_p (itype, TREE_TYPE (d)))
> +    {
> +      tree nd = create_tmp_reg (itype);
> +      enum tree_code code;
> +      if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (d)))
> +       {
> +         code = VIEW_CONVERT_EXPR;
> +         d = build1 (VIEW_CONVERT_EXPR, itype, d);
> +       }
> +      else
> +       code = NOP_EXPR;
> +      gimple *g = gimple_build_assign (nd, code, d);
> +      gimple_set_location (g, loc);
> +      gsi_insert_before (&gsi, g, GSI_SAME_STMT);
> +      d = nd;
> +    }
> +
> +  tree ctype = build_complex_type (itype);
> +  int flag = int_size_in_bytes (itype) + (weak ? 256 : 0);
> +  gimple *g
> +    = gimple_build_call_internal (IFN_ATOMIC_COMPARE_EXCHANGE, 6, addr,
> e, d,
> +                                 build_int_cst (integer_type_node, flag),
> +                                 mo, fmo);
> +  tree cres = create_tmp_reg (ctype);
> +  gimple_call_set_lhs (g, cres);
> +  gimple_set_location (g, loc);
> +  gsi_insert_before (&gsi, g, GSI_SAME_STMT);
> +
> +  if (cond_stmt || need_old || need_new)
> +    {
> +      tree im = create_tmp_reg (itype);
> +      g = gimple_build_assign (im, IMAGPART_EXPR,
> +                              build1 (IMAGPART_EXPR, itype, cres));
> +      gimple_set_location (g, loc);
> +      gsi_insert_before (&gsi, g, GSI_SAME_STMT);
> +
> +      tree re = NULL_TREE;
> +      if (need_old || need_new)
> +       {
> +         re = create_tmp_reg (itype);
> +         g = gimple_build_assign (re, REALPART_EXPR,
> +                                  build1 (REALPART_EXPR, itype, cres));
> +         gimple_set_location (g, loc);
> +         gsi_insert_before (&gsi, g, GSI_SAME_STMT);
> +       }
> +
> +      if (cond_stmt)
> +       {
> +         g = gimple_build_assign (gimple_assign_lhs (cond_stmt),
> +                                  NOP_EXPR, im);
> +         gimple_set_location (g, loc);
> +         gsi_insert_before (&gsi, g, GSI_SAME_STMT);
> +       }
> +      else if (need_new)
> +       {
> +         g = gimple_build_assign (create_tmp_reg (itype), COND_EXPR,
> +                                  build2 (NE_EXPR, boolean_type_node,
> +                                          im, build_zero_cst (itype)),
> +                                  d, re);
> +         gimple_set_location (g, loc);
> +         gsi_insert_before (&gsi, g, GSI_SAME_STMT);
> +         re = gimple_assign_lhs (g);
> +       }
> +
> +      if (need_old || need_new)
> +       {
> +         tree v = need_old ? loaded_val : stored_val;
> +         enum tree_code code;
> +         if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (v)))
> +           {
> +             code = VIEW_CONVERT_EXPR;
> +             re = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (v), re);
> +           }
> +         else if (!useless_type_conversion_p (TREE_TYPE (v), itype))
> +           code = NOP_EXPR;
> +         else
> +           code = TREE_CODE (re);
> +         g = gimple_build_assign (v, code, re);
> +         gimple_set_location (g, loc);
> +         gsi_insert_before (&gsi, g, GSI_SAME_STMT);
> +       }
> +    }
> +
> +  gsi_remove (&gsi, true);
> +  gsi = gsi_for_stmt (load_stmt);
> +  gsi_remove (&gsi, true);
> +  gsi = gsi_for_stmt (condexpr_stmt);
> +  gsi_remove (&gsi, true);
> +  if (cond_stmt)
> +    {
> +      gsi = gsi_for_stmt (cond_stmt);
> +      gsi_remove (&gsi, true);
> +    }
> +  if (vce_stmt)
> +    {
> +      gsi = gsi_for_stmt (vce_stmt);
> +      gsi_remove (&gsi, true);
> +    }
> +
> +  return true;
> +}
> +
>  /* A subroutine of expand_omp_atomic.  Implement the atomic operation as:
>
>        oldval = *addr;
> @@ -8825,13 +9116,8 @@ expand_omp_atomic_pipeline (basic_block
>    gcc_assert (gimple_code (gsi_stmt (si)) == GIMPLE_OMP_ATOMIC_LOAD);
>    location_t loc = gimple_location (gsi_stmt (si));
>    enum omp_memory_order omo = gimple_omp_atomic_memory_order (gsi_stmt
> (si));
> -  enum memmodel imo = omp_memory_order_to_memmodel (omo);
> -  tree mo = build_int_cst (NULL, imo);
> -  if (imo == MEMMODEL_RELEASE)
> -    imo = MEMMODEL_RELAXED;
> -  else if (imo == MEMMODEL_ACQ_REL)
> -    imo = MEMMODEL_ACQUIRE;
> -  tree fmo = build_int_cst (NULL, imo);
> +  tree mo = build_int_cst (NULL, omp_memory_order_to_memmodel (omo));
> +  tree fmo = build_int_cst (NULL, omp_memory_order_to_fail_memmodel
> (omo));
>
>    /* For floating-point values, we'll need to view-convert them to
> integers
>       so that we can perform the atomic compare and swap.  Simplify the
> @@ -9114,6 +9400,13 @@ expand_omp_atomic (struct omp_region *re
>                                              loaded_val, stored_val,
> index))
>             return;
>
> +         /* When possible, use ATOMIC_COMPARE_EXCHANGE ifn without a
> loop.  */
> +         if (store_bb == single_succ (load_bb)
> +             && !gimple_in_ssa_p (cfun)
> +             && expand_omp_atomic_cas (load_bb, addr, loaded_val,
> stored_val,
> +                                       index))
> +           return;
> +
>           /* If we don't have specialized __sync builtins, try and
> implement
>              as a compare and swap loop.  */
>           if (expand_omp_atomic_pipeline (load_bb, store_bb, addr,
> --- gcc/c-family/c-common.h.jj  2021-09-09 10:40:19.928263599 +0200
> +++ gcc/c-family/c-common.h     2021-09-09 10:42:11.865667757 +0200
> @@ -1223,8 +1223,8 @@ extern tree c_finish_omp_critical (locat
>  extern tree c_finish_omp_ordered (location_t, tree, tree);
>  extern void c_finish_omp_barrier (location_t);
>  extern tree c_finish_omp_atomic (location_t, enum tree_code, enum
> tree_code,
> -                                tree, tree, tree, tree, tree, bool,
> -                                enum omp_memory_order, bool = false);
> +                                tree, tree, tree, tree, tree, tree, bool,
> +                                enum omp_memory_order, bool, bool =
> false);
>  extern bool c_omp_depend_t_p (tree);
>  extern void c_finish_omp_depobj (location_t, tree, enum
> omp_clause_depend_kind,
>                                  tree);
> --- gcc/c-family/c-omp.c.jj     2021-09-09 10:40:19.937263471 +0200
> +++ gcc/c-family/c-omp.c        2021-09-10 17:59:26.574281430 +0200
> @@ -36,6 +36,7 @@ along with GCC; see the file COPYING3.
>  #include "gimplify.h"
>  #include "langhooks.h"
>  #include "bitmap.h"
> +#include "gimple-fold.h"
>
>
>  /* Complete a #pragma oacc wait construct.  LOC is the location of
> @@ -215,15 +216,17 @@ c_finish_omp_taskyield (location_t loc)
>  tree
>  c_finish_omp_atomic (location_t loc, enum tree_code code,
>                      enum tree_code opcode, tree lhs, tree rhs,
> -                    tree v, tree lhs1, tree rhs1, bool swapped,
> -                    enum omp_memory_order memory_order, bool test)
> +                    tree v, tree lhs1, tree rhs1, tree r, bool swapped,
> +                    enum omp_memory_order memory_order, bool weak,
> +                    bool test)
>  {
> -  tree x, type, addr, pre = NULL_TREE;
> +  tree x, type, addr, pre = NULL_TREE, rtmp = NULL_TREE, vtmp = NULL_TREE;
>    HOST_WIDE_INT bitpos = 0, bitsize = 0;
> +  enum tree_code orig_opcode = opcode;
>
>    if (lhs == error_mark_node || rhs == error_mark_node
>        || v == error_mark_node || lhs1 == error_mark_node
> -      || rhs1 == error_mark_node)
> +      || rhs1 == error_mark_node || r == error_mark_node)
>      return error_mark_node;
>
>    /* ??? According to one reading of the OpenMP spec, complex type are
> @@ -243,6 +246,12 @@ c_finish_omp_atomic (location_t loc, enu
>        error_at (loc, "%<_Atomic%> expression in %<#pragma omp atomic%>");
>        return error_mark_node;
>      }
> +  if (r && r != void_list_node && !INTEGRAL_TYPE_P (TREE_TYPE (r)))
> +    {
> +      error_at (loc, "%<#pragma omp atomic compare capture%> with
> non-integral "
> +                    "comparison result");
> +      return error_mark_node;
> +    }
>
>    if (opcode == RDIV_EXPR)
>      opcode = TRUNC_DIV_EXPR;
> @@ -299,6 +308,7 @@ c_finish_omp_atomic (location_t loc, enu
>        x = build1 (OMP_ATOMIC_READ, type, addr);
>        SET_EXPR_LOCATION (x, loc);
>        OMP_ATOMIC_MEMORY_ORDER (x) = memory_order;
> +      gcc_assert (!weak);
>        if (blhs)
>         x = build3_loc (loc, BIT_FIELD_REF, TREE_TYPE (blhs), x,
>                         bitsize_int (bitsize), bitsize_int (bitpos));
> @@ -313,12 +323,29 @@ c_finish_omp_atomic (location_t loc, enu
>      {
>        lhs = build3_loc (loc, BIT_FIELD_REF, TREE_TYPE (blhs), lhs,
>                         bitsize_int (bitsize), bitsize_int (bitpos));
> -      if (swapped)
> +      if (opcode == COND_EXPR)
> +       {
> +         bool save = in_late_binary_op;
> +         in_late_binary_op = true;
> +         std::swap (rhs, rhs1);
> +         rhs1 = build_binary_op (loc, EQ_EXPR, lhs, rhs1, true);
> +         in_late_binary_op = save;
> +       }
> +      else if (swapped)
>         rhs = build_binary_op (loc, opcode, rhs, lhs, true);
>        else if (opcode != NOP_EXPR)
>         rhs = build_binary_op (loc, opcode, lhs, rhs, true);
>        opcode = NOP_EXPR;
>      }
> +  else if (opcode == COND_EXPR)
> +    {
> +      bool save = in_late_binary_op;
> +      in_late_binary_op = true;
> +      std::swap (rhs, rhs1);
> +      rhs1 = build_binary_op (loc, EQ_EXPR, lhs, rhs1, true);
> +      in_late_binary_op = save;
> +      opcode = NOP_EXPR;
> +    }
>    else if (swapped)
>      {
>        rhs = build_binary_op (loc, opcode, rhs, lhs, true);
> @@ -343,6 +370,100 @@ c_finish_omp_atomic (location_t loc, enu
>    if (blhs)
>      rhs = build3_loc (loc, BIT_INSERT_EXPR, type, new_lhs,
>                       rhs, bitsize_int (bitpos));
> +  if (orig_opcode == COND_EXPR)
> +    {
> +      if (error_operand_p (rhs1))
> +       return error_mark_node;
> +      gcc_assert (TREE_CODE (rhs1) == EQ_EXPR);
> +      tree cmptype = TREE_TYPE (TREE_OPERAND (rhs1, 0));
> +      if (SCALAR_FLOAT_TYPE_P (cmptype))
> +       {
> +         bool clear_padding = false;
> +         if (BITS_PER_UNIT == 8 && CHAR_BIT == 8)
> +           {
> +             HOST_WIDE_INT sz = int_size_in_bytes (cmptype), i;
> +             gcc_assert (sz > 0);
> +             unsigned char *buf = XALLOCAVEC (unsigned char, sz);
> +             memset (buf, ~0, sz);
> +             clear_type_padding_in_mask (cmptype, buf);
> +             for (i = 0; i < sz; i++)
> +               if (buf[i] != (unsigned char) ~0)
> +                 {
> +                   clear_padding = true;
> +                   break;
> +                 }
> +           }
> +         tree inttype = NULL_TREE;
> +         if (!clear_padding && tree_fits_uhwi_p (TYPE_SIZE (cmptype)))
> +           {
> +             HOST_WIDE_INT prec = tree_to_uhwi (TYPE_SIZE (cmptype));
> +             inttype = c_common_type_for_size (prec, 1);
> +             if (inttype
> +                 && (!tree_int_cst_equal (TYPE_SIZE (cmptype),
> +                                          TYPE_SIZE (inttype))
> +                     || TYPE_PRECISION (inttype) != prec))
> +               inttype = NULL_TREE;
> +           }
> +         if (inttype)
> +           {
> +             TREE_OPERAND (rhs1, 0)
> +               = build1_loc (loc, VIEW_CONVERT_EXPR, inttype,
> +                             TREE_OPERAND (rhs1, 0));
> +             TREE_OPERAND (rhs1, 1)
> +               = build1_loc (loc, VIEW_CONVERT_EXPR, inttype,
> +                             TREE_OPERAND (rhs1, 1));
> +           }
> +         else
> +           {
> +             tree pcmptype = build_pointer_type (cmptype);
> +             tree tmp1 = create_tmp_var_raw (cmptype);
> +             TREE_ADDRESSABLE (tmp1) = 1;
> +             DECL_CONTEXT (tmp1) = current_function_decl;
> +             tmp1 = build4 (TARGET_EXPR, cmptype, tmp1,
> +                            TREE_OPERAND (rhs1, 0), NULL, NULL);
> +             tmp1 = build1 (ADDR_EXPR, pcmptype, tmp1);
> +             tree tmp2 = create_tmp_var_raw (cmptype);
> +             TREE_ADDRESSABLE (tmp2) = 1;
> +             DECL_CONTEXT (tmp2) = current_function_decl;
> +             tmp2 = build4 (TARGET_EXPR, cmptype, tmp2,
> +                            TREE_OPERAND (rhs1, 1), NULL, NULL);
> +             tmp2 = build1 (ADDR_EXPR, pcmptype, tmp2);
> +             tree fndecl = builtin_decl_explicit (BUILT_IN_MEMCMP);
> +             rhs1 = build_call_expr_loc (loc, fndecl, 3, tmp1, tmp2,
> +                                         TYPE_SIZE_UNIT (cmptype));
> +             rhs1 = build2 (EQ_EXPR, boolean_type_node, rhs1,
> +                            integer_zero_node);
> +             if (clear_padding)
> +               {
> +                 fndecl = builtin_decl_explicit (BUILT_IN_CLEAR_PADDING);
> +                 tree cp1 = build_call_expr_loc (loc, fndecl, 1, tmp1);
> +                 tree cp2 = build_call_expr_loc (loc, fndecl, 1, tmp2);
> +                 rhs1 = omit_two_operands_loc (loc, boolean_type_node,
> +                                               rhs1, cp2, cp1);
> +               }
> +           }
> +       }
> +      if (r)
> +       {
> +         tree var = create_tmp_var (boolean_type_node);
> +         DECL_CONTEXT (var) = current_function_decl;
> +         rtmp = build4 (TARGET_EXPR, boolean_type_node, var,
> +                        NULL, NULL, NULL);
> +         save = in_late_binary_op;
> +         in_late_binary_op = true;
> +         x = build_modify_expr (loc, var, NULL_TREE, NOP_EXPR,
> +                                loc, rhs1, NULL_TREE);
> +         in_late_binary_op = save;
> +         if (x == error_mark_node)
> +           return error_mark_node;
> +         gcc_assert (TREE_CODE (x) == MODIFY_EXPR
> +                     && TREE_OPERAND (x, 0) == var);
> +         TREE_OPERAND (x, 0) = rtmp;
> +         rhs1 = omit_one_operand_loc (loc, boolean_type_node, x, rtmp);
> +       }
> +      rhs = build3_loc (loc, COND_EXPR, type, rhs1, rhs, new_lhs);
> +      rhs1 = NULL_TREE;
> +    }
>
>    /* Punt the actual generation of atomic operations to common code.  */
>    if (code == OMP_ATOMIC)
> @@ -350,6 +471,7 @@ c_finish_omp_atomic (location_t loc, enu
>    x = build2 (code, type, addr, rhs);
>    SET_EXPR_LOCATION (x, loc);
>    OMP_ATOMIC_MEMORY_ORDER (x) = memory_order;
> +  OMP_ATOMIC_WEAK (x) = weak;
>
>    /* Generally it is hard to prove lhs1 and lhs are the same memory
>       location, just diagnose different variables.  */
> @@ -412,8 +534,25 @@ c_finish_omp_atomic (location_t loc, enu
>                           bitsize_int (bitsize), bitsize_int (bitpos));
>           type = TREE_TYPE (blhs);
>         }
> -      x = build_modify_expr (loc, v, NULL_TREE, NOP_EXPR,
> +      if (r)
> +       {
> +         vtmp = create_tmp_var (TREE_TYPE (x));
> +         DECL_CONTEXT (vtmp) = current_function_decl;
> +       }
> +      else
> +       vtmp = v;
> +      x = build_modify_expr (loc, vtmp, NULL_TREE, NOP_EXPR,
>                              loc, x, NULL_TREE);
> +      if (x == error_mark_node)
> +       return error_mark_node;
> +      if (r)
> +       {
> +         vtmp = build4 (TARGET_EXPR, boolean_type_node, vtmp,
> +                        NULL, NULL, NULL);
> +         gcc_assert (TREE_CODE (x) == MODIFY_EXPR
> +                     && TREE_OPERAND (x, 0) == TARGET_EXPR_SLOT (vtmp));
> +         TREE_OPERAND (x, 0) = vtmp;
> +       }
>        if (rhs1 && rhs1 != orig_lhs)
>         {
>           tree rhs1addr = build_unary_op (loc, ADDR_EXPR, rhs1, false);
> @@ -446,6 +585,28 @@ c_finish_omp_atomic (location_t loc, enu
>
>    if (pre)
>      x = omit_one_operand_loc (loc, type, x, pre);
> +  if (r && r != void_list_node)
> +    {
> +      in_late_binary_op = true;
> +      tree x2 = build_modify_expr (loc, r, NULL_TREE, NOP_EXPR,
> +                                  loc, rtmp, NULL_TREE);
> +      in_late_binary_op = save;
> +      if (x2 == error_mark_node)
> +       return error_mark_node;
> +      x = omit_one_operand_loc (loc, TREE_TYPE (x2), x2, x);
> +    }
> +  if (v && vtmp != v)
> +    {
> +      in_late_binary_op = true;
> +      tree x2 = build_modify_expr (loc, v, NULL_TREE, NOP_EXPR,
> +                                  loc, vtmp, NULL_TREE);
> +      in_late_binary_op = save;
> +      if (x2 == error_mark_node)
> +       return error_mark_node;
> +      x2 = build3_loc (loc, COND_EXPR, void_type_node, rtmp,
> +                      void_node, x2);
> +      x = omit_one_operand_loc (loc, TREE_TYPE (x2), x2, x);
> +    }
>    return x;
>  }
>
> --- gcc/c/c-parser.c.jj 2021-09-09 10:40:19.956263200 +0200
> +++ gcc/c/c-parser.c    2021-09-10 17:06:10.966650277 +0200
> @@ -7663,10 +7663,21 @@ c_parser_conditional_expression (c_parse
>    c_inhibit_evaluation_warnings -= cond.value == truthvalue_true_node;
>    location_t loc1 = make_location (exp1.get_start (), exp1.src_range);
>    location_t loc2 = make_location (exp2.get_start (), exp2.src_range);
> -  ret.value = build_conditional_expr (colon_loc, cond.value,
> -                                     cond.original_code ==
> C_MAYBE_CONST_EXPR,
> -                                     exp1.value, exp1.original_type, loc1,
> -                                     exp2.value, exp2.original_type,
> loc2);
> +  if (__builtin_expect (omp_atomic_lhs != NULL, 0)
> +      && (TREE_CODE (cond.value) == GT_EXPR
> +         || TREE_CODE (cond.value) == LT_EXPR
> +         || TREE_CODE (cond.value) == EQ_EXPR)
> +      && c_tree_equal (exp2.value, omp_atomic_lhs)
> +      && (c_tree_equal (TREE_OPERAND (cond.value, 0), omp_atomic_lhs)
> +         || c_tree_equal (TREE_OPERAND (cond.value, 1), omp_atomic_lhs)))
> +    ret.value = build3_loc (colon_loc, COND_EXPR, TREE_TYPE
> (omp_atomic_lhs),
> +                           cond.value, exp1.value, exp2.value);
> +  else
> +    ret.value
> +      = build_conditional_expr (colon_loc, cond.value,
> +                               cond.original_code == C_MAYBE_CONST_EXPR,
> +                               exp1.value, exp1.original_type, loc1,
> +                               exp2.value, exp2.original_type, loc2);
>    ret.original_code = ERROR_MARK;
>    if (exp1.value == error_mark_node || exp2.value == error_mark_node)
>      ret.original_type = NULL;
> @@ -7849,15 +7860,27 @@ c_parser_binary_expression (c_parser *pa
>        = convert_lvalue_to_rvalue (stack[sp].loc,
>    \
>                                   stack[sp].expr, true, true);
>     \
>      if (__builtin_expect (omp_atomic_lhs != NULL_TREE, 0) && sp == 1
>    \
> -       && c_parser_peek_token (parser)->type == CPP_SEMICOLON
>     \
> -       && ((1 << stack[sp].prec)
>    \
> -           & ((1 << PREC_BITOR) | (1 << PREC_BITXOR) | (1 <<
> PREC_BITAND)    \
> -              | (1 << PREC_SHIFT) | (1 << PREC_ADD) | (1 << PREC_MULT)))
>    \
> +       && ((c_parser_next_token_is (parser, CPP_SEMICOLON)
>    \
> +            && ((1 << stack[sp].prec)
>     \
> +                & ((1 << PREC_BITOR) | (1 << PREC_BITXOR)
>     \
> +                    | (1 << PREC_BITAND) | (1 << PREC_SHIFT)
>    \
> +                    | (1 << PREC_ADD) | (1 << PREC_MULT)
>    \
> +                    | (1 << PREC_EQ))))
>     \
> +           || ((c_parser_next_token_is (parser, CPP_QUERY)
>    \
> +                || (omp_atomic_lhs == void_list_node
>    \
> +                    && c_parser_next_token_is (parser,
> CPP_CLOSE_PAREN)))    \
> +               && (stack[sp].prec == PREC_REL || stack[sp].prec ==
> PREC_EQ)))\
>         && stack[sp].op != TRUNC_MOD_EXPR
>    \
> +       && stack[sp].op != GE_EXPR
>     \
> +       && stack[sp].op != LE_EXPR
>     \
> +       && stack[sp].op != NE_EXPR
>     \
>         && stack[0].expr.value != error_mark_node
>    \
>         && stack[1].expr.value != error_mark_node
>    \
> -       && (c_tree_equal (stack[0].expr.value, omp_atomic_lhs)
>     \
> -           || c_tree_equal (stack[1].expr.value, omp_atomic_lhs)))
>    \
> +       && (omp_atomic_lhs == void_list_node
>     \
> +           || c_tree_equal (stack[0].expr.value, omp_atomic_lhs)
>    \
> +           || c_tree_equal (stack[1].expr.value, omp_atomic_lhs)
>    \
> +           || (stack[sp].op == EQ_EXPR
>    \
> +               && c_parser_peek_2nd_token (parser)->keyword == RID_IF)))
>    \
>        {
>             \
>         tree t = make_node (stack[1].op);
>    \
>         TREE_TYPE (t) = TREE_TYPE (stack[0].expr.value);
>     \
> @@ -17655,14 +17678,45 @@ c_parser_omp_allocate (location_t loc, c
>     capture-block:
>       { v = x; update-stmt; } | { update-stmt; v = x; } | { v = x; x =
> expr; }
>
> -  where x and v are lvalue expressions with scalar type.
> +   OpenMP 5.1:
> +   # pragma omp atomic compare new-line
> +     conditional-update-atomic
> +
> +   # pragma omp atomic compare capture new-line
> +     conditional-update-capture-atomic
> +
> +   conditional-update-atomic:
> +     cond-expr-stmt | cond-update-stmt
> +   cond-expr-stmt:
> +     x = expr ordop x ? expr : x;
> +     x = x ordop expr ? expr : x;
> +     x = x == e ? d : x;
> +   cond-update-stmt:
> +     if (expr ordop x) { x = expr; }
> +     if (x ordop expr) { x = expr; }
> +     if (x == e) { x = d; }
> +   ordop:
> +     <, >
> +   conditional-update-capture-atomic:
> +     v = cond-expr-stmt
> +     { v = x; cond-expr-stmt }
> +     { cond-expr-stmt v = x; }
> +     { v = x; cond-update-stmt }
> +     { cond-update-stmt v = x; }
> +     if (x == e) { x = d; } else { v = x; }
> +     { r = x == e; if (r) { x = d; } }
> +     { r = x == e; if (r) { x = d; } else { v = x; } }
> +
> +  where x, r and v are lvalue expressions with scalar type,
> +  expr, e and d are expressions with scalar type and e might be
> +  the same as v.
>
>    LOC is the location of the #pragma token.  */
>
>  static void
>  c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc)
>  {
> -  tree lhs = NULL_TREE, rhs = NULL_TREE, v = NULL_TREE;
> +  tree lhs = NULL_TREE, rhs = NULL_TREE, v = NULL_TREE, r = NULL_TREE;
>    tree lhs1 = NULL_TREE, rhs1 = NULL_TREE;
>    tree stmt, orig_lhs, unfolded_lhs = NULL_TREE, unfolded_lhs1 =
> NULL_TREE;
>    enum tree_code code = ERROR_MARK, opcode = NOP_EXPR;
> @@ -17674,6 +17728,12 @@ c_parser_omp_atomic (location_t loc, c_p
>    bool non_lvalue_p;
>    bool first = true;
>    tree clauses = NULL_TREE;
> +  bool capture = false;
> +  bool compare = false;
> +  bool weak = false;
> +  enum omp_memory_order fail = OMP_MEMORY_ORDER_UNSPECIFIED;
> +  bool no_semicolon = false;
> +  bool extra_scope = false;
>
>    while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL))
>      {
> @@ -17692,6 +17752,10 @@ c_parser_omp_atomic (location_t loc, c_p
>           enum tree_code new_code = ERROR_MARK;
>           enum omp_memory_order new_memory_order
>             = OMP_MEMORY_ORDER_UNSPECIFIED;
> +         bool new_capture = false;
> +         bool new_compare = false;
> +         bool new_weak = false;
> +         enum omp_memory_order new_fail = OMP_MEMORY_ORDER_UNSPECIFIED;
>
>           if (!strcmp (p, "read"))
>             new_code = OMP_ATOMIC_READ;
> @@ -17699,7 +17763,7 @@ c_parser_omp_atomic (location_t loc, c_p
>             new_code = NOP_EXPR;
>           else if (!strcmp (p, "update"))
>             new_code = OMP_ATOMIC;
> -         else if (!strcmp (p, "capture"))
> +         else if (openacc && !strcmp (p, "capture"))
>             new_code = OMP_ATOMIC_CAPTURE_NEW;
>           else if (openacc)
>             {
> @@ -17707,6 +17771,47 @@ c_parser_omp_atomic (location_t loc, c_p
>               error_at (cloc, "expected %<read%>, %<write%>, %<update%>, "
>                               "or %<capture%> clause");
>             }
> +         else if (!strcmp (p, "capture"))
> +           new_capture = true;
> +         else if (!strcmp (p, "compare"))
> +           new_compare = true;
> +         else if (!strcmp (p, "weak"))
> +           new_weak = true;
> +         else if (!strcmp (p, "fail"))
> +           {
> +             matching_parens parens;
> +
> +             c_parser_consume_token (parser);
> +             if (!parens.require_open (parser))
> +               continue;
> +
> +             if (c_parser_next_token_is (parser, CPP_NAME))
> +               {
> +                 const char *q
> +                   = IDENTIFIER_POINTER (c_parser_peek_token
> (parser)->value);
> +
> +                 if (!strcmp (q, "seq_cst"))
> +                   new_fail = OMP_MEMORY_ORDER_SEQ_CST;
> +                 else if (!strcmp (q, "acquire"))
> +                   new_fail = OMP_MEMORY_ORDER_ACQUIRE;
> +                 else if (!strcmp (q, "relaxed"))
> +                   new_fail = OMP_MEMORY_ORDER_RELAXED;
> +               }
> +
> +             if (new_fail != OMP_MEMORY_ORDER_UNSPECIFIED)
> +               {
> +                 c_parser_consume_token (parser);
> +                 if (fail != OMP_MEMORY_ORDER_UNSPECIFIED)
> +                   error_at (cloc, "too many %qs clauses", "fail");
> +                 else
> +                   fail = new_fail;
> +               }
> +             else
> +               c_parser_error (parser, "expected %<seq_cst%>, %<acquire%>
> "
> +                                       "or %<relaxed%>");
> +             parens.skip_until_found_close (parser);
> +             continue;
> +           }
>           else if (!strcmp (p, "seq_cst"))
>             new_memory_order = OMP_MEMORY_ORDER_SEQ_CST;
>           else if (!strcmp (p, "acq_rel"))
> @@ -17727,8 +17832,9 @@ c_parser_omp_atomic (location_t loc, c_p
>             {
>               p = NULL;
>               error_at (cloc, "expected %<read%>, %<write%>, %<update%>, "
> -                             "%<capture%>, %<seq_cst%>, %<acq_rel%>, "
> -                             "%<release%>, %<relaxed%> or %<hint%>
> clause");
> +                             "%<capture%>, %<compare%>, %<weak%>,
> %<fail%>, "
> +                             "%<seq_cst%>, %<acq_rel%>, %<release%>, "
> +                             "%<relaxed%> or %<hint%> clause");
>             }
>           if (p)
>             {
> @@ -17751,6 +17857,27 @@ c_parser_omp_atomic (location_t loc, c_p
>                   else
>                     memory_order = new_memory_order;
>                 }
> +             else if (new_capture)
> +               {
> +                 if (capture)
> +                   error_at (cloc, "too many %qs clauses", "capture");
> +                 else
> +                   capture = true;
> +               }
> +             else if (new_compare)
> +               {
> +                 if (compare)
> +                   error_at (cloc, "too many %qs clauses", "compare");
> +                 else
> +                   compare = true;
> +               }
> +             else if (new_weak)
> +               {
> +                 if (weak)
> +                   error_at (cloc, "too many %qs clauses", "weak");
> +                 else
> +                   weak = true;
> +               }
>               c_parser_consume_token (parser);
>               continue;
>             }
> @@ -17761,6 +17888,30 @@ c_parser_omp_atomic (location_t loc, c_p
>
>    if (code == ERROR_MARK)
>      code = OMP_ATOMIC;
> +  if (capture)
> +    {
> +      if (code != OMP_ATOMIC)
> +       error_at (loc, "%qs clause is incompatible with %<read%> or
> %<write%> "
> +                      "clauses", "capture");
> +      else
> +       code = OMP_ATOMIC_CAPTURE_NEW;
> +    }
> +  if (compare && code != OMP_ATOMIC && code != OMP_ATOMIC_CAPTURE_NEW)
> +    {
> +      error_at (loc, "%qs clause is incompatible with %<read%> or
> %<write%> "
> +                     "clauses", "compare");
> +      compare = false;
> +    }
> +  if (fail != OMP_MEMORY_ORDER_UNSPECIFIED && !compare)
> +    {
> +      error_at (loc, "%qs clause requires %qs clause", "fail", "compare");
> +      fail = OMP_MEMORY_ORDER_UNSPECIFIED;
> +    }
> +  if (weak && !compare)
> +    {
> +      error_at (loc, "%qs clause requires %qs clause", "weak", "compare");
> +      weak = false;
> +    }
>    if (openacc)
>      memory_order = OMP_MEMORY_ORDER_RELAXED;
>    else if (memory_order == OMP_MEMORY_ORDER_UNSPECIFIED)
> @@ -17785,7 +17936,6 @@ c_parser_omp_atomic (location_t loc, c_p
>               memory_order = OMP_MEMORY_ORDER_ACQUIRE;
>               break;
>             case NOP_EXPR: /* atomic write */
> -           case OMP_ATOMIC:
>               memory_order = OMP_MEMORY_ORDER_RELEASE;
>               break;
>             default:
> @@ -17801,36 +17951,32 @@ c_parser_omp_atomic (location_t loc, c_p
>      switch (code)
>        {
>        case OMP_ATOMIC_READ:
> -       if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
> -           || memory_order == OMP_MEMORY_ORDER_RELEASE)
> +       if (memory_order == OMP_MEMORY_ORDER_RELEASE)
>           {
>             error_at (loc, "%<#pragma omp atomic read%> incompatible with "
> -                          "%<acq_rel%> or %<release%> clauses");
> +                          "%<release%> clause");
>             memory_order = OMP_MEMORY_ORDER_SEQ_CST;
>           }
> +       else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL)
> +         memory_order = OMP_MEMORY_ORDER_ACQUIRE;
>         break;
>        case NOP_EXPR: /* atomic write */
> -       if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
> -           || memory_order == OMP_MEMORY_ORDER_ACQUIRE)
> +       if (memory_order == OMP_MEMORY_ORDER_ACQUIRE)
>           {
>             error_at (loc, "%<#pragma omp atomic write%> incompatible with
> "
> -                          "%<acq_rel%> or %<acquire%> clauses");
> -           memory_order = OMP_MEMORY_ORDER_SEQ_CST;
> -         }
> -       break;
> -      case OMP_ATOMIC:
> -     /* case OMP_ATOMIC_CAPTURE_NEW: - or update to OpenMP 5.1 */
> -       if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
> -           || memory_order == OMP_MEMORY_ORDER_ACQUIRE)
> -         {
> -           error_at (loc, "%<#pragma omp atomic update%> incompatible
> with "
> -                          "%<acq_rel%> or %<acquire%> clauses");
> +                          "%<acquire%> clause");
>             memory_order = OMP_MEMORY_ORDER_SEQ_CST;
>           }
> +       else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL)
> +         memory_order = OMP_MEMORY_ORDER_RELEASE;
>         break;
>        default:
>         break;
>        }
> +  if (fail != OMP_MEMORY_ORDER_UNSPECIFIED)
> +    memory_order
> +      = (enum omp_memory_order) (memory_order
> +                                | (fail << OMP_FAIL_MEMORY_ORDER_SHIFT));
>
>    switch (code)
>      {
> @@ -17879,6 +18025,9 @@ c_parser_omp_atomic (location_t loc, c_p
>           c_parser_consume_token (parser);
>           structured_block = true;
>         }
> +      else if (compare
> +              && c_parser_next_token_is_keyword (parser, RID_IF))
> +       break;
>        else
>         {
>           v = c_parser_cast_expression (parser, NULL).value;
> @@ -17890,6 +18039,12 @@ c_parser_omp_atomic (location_t loc, c_p
>             v = non_lvalue (v);
>           if (!c_parser_require (parser, CPP_EQ, "expected %<=%>"))
>             goto saw_error;
> +         if (compare && c_parser_next_token_is_keyword (parser, RID_IF))
> +           {
> +             eloc = c_parser_peek_token (parser)->location;
> +             error_at (eloc, "expected expression");
> +             goto saw_error;
> +           }
>         }
>        break;
>      default:
> @@ -17899,6 +18054,179 @@ c_parser_omp_atomic (location_t loc, c_p
>    /* For structured_block case we don't know yet whether
>       old or new x should be captured.  */
>  restart:
> +  if (compare && c_parser_next_token_is_keyword (parser, RID_IF))
> +    {
> +      c_parser_consume_token (parser);
> +
> +      matching_parens parens;
> +      if (!parens.require_open (parser))
> +       goto saw_error;
> +      eloc = c_parser_peek_token (parser)->location;
> +      c_expr cmp_expr;
> +      if (r)
> +       {
> +         cmp_expr = c_parser_cast_expression (parser, NULL);
> +         cmp_expr = default_function_array_conversion (eloc, cmp_expr);
> +       }
> +      else
> +       cmp_expr = c_parser_binary_expression (parser, NULL,
> void_list_node);
> +      parens.skip_until_found_close (parser);
> +      if (cmp_expr.value == error_mark_node)
> +       goto saw_error;
> +      if (r)
> +       {
> +         if (!c_tree_equal (cmp_expr.value, unfolded_lhs))
> +           goto bad_if;
> +         cmp_expr.value = rhs1;
> +         rhs1 = NULL_TREE;
> +         gcc_assert (TREE_CODE (cmp_expr.value) == EQ_EXPR);
> +       }
> +      if (TREE_CODE (cmp_expr.value) == EQ_EXPR)
> +       ;
> +      else if (!structured_block && code == OMP_ATOMIC_CAPTURE_NEW)
> +       {
> +         error_at (EXPR_LOC_OR_LOC (cmp_expr.value, eloc),
> +                   "expected %<==%> comparison in %<if%> condition");
> +         goto saw_error;
> +       }
> +      else if (TREE_CODE (cmp_expr.value) != GT_EXPR
> +              && TREE_CODE (cmp_expr.value) != LT_EXPR)
> +       {
> +         error_at (EXPR_LOC_OR_LOC (cmp_expr.value, eloc),
> +                   "expected %<==%>, %<<%> or %<>%> comparison in %<if%> "
> +                   "condition");
> +         goto saw_error;
> +       }
> +      if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>"))
> +       goto saw_error;
> +
> +      extra_scope = true;
> +      eloc = c_parser_peek_token (parser)->location;
> +      expr = c_parser_cast_expression (parser, NULL);
> +      lhs = expr.value;
> +      expr = default_function_array_conversion (eloc, expr);
> +      unfolded_lhs = expr.value;
> +      lhs = c_fully_fold (lhs, false, NULL, true);
> +      orig_lhs = lhs;
> +      if (lhs == error_mark_node)
> +       goto saw_error;
> +      if (!lvalue_p (unfolded_lhs))
> +       lhs = non_lvalue (lhs);
> +      if (!c_parser_next_token_is (parser, CPP_EQ))
> +       {
> +         c_parser_error (parser, "expected %<=%>");
> +         goto saw_error;
> +       }
> +      c_parser_consume_token (parser);
> +      eloc = c_parser_peek_token (parser)->location;
> +      expr = c_parser_expr_no_commas (parser, NULL);
> +      rhs1 = expr.value;
> +
> +      if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
> +       goto saw_error;
> +
> +      if (!c_parser_require (parser, CPP_CLOSE_BRACE, "expected %<}%>"))
> +       goto saw_error;
> +
> +      extra_scope = false;
> +      no_semicolon = true;
> +
> +      if (c_tree_equal (TREE_OPERAND (cmp_expr.value, 0), unfolded_lhs))
> +       {
> +         if (TREE_CODE (cmp_expr.value) == EQ_EXPR)
> +           {
> +             opcode = COND_EXPR;
> +             rhs = c_fully_fold (TREE_OPERAND (cmp_expr.value, 1),
> +                                 false, NULL, true);
> +             rhs1 = c_fully_fold (rhs1, false, NULL, true);
> +           }
> +         else if (c_tree_equal (TREE_OPERAND (cmp_expr.value, 1), rhs1))
> +           {
> +             opcode = (TREE_CODE (cmp_expr.value) == GT_EXPR
> +                       ? MIN_EXPR : MAX_EXPR);
> +             rhs = c_fully_fold (rhs1, false, NULL, true);
> +             rhs1 = c_fully_fold (TREE_OPERAND (cmp_expr.value, 0),
> +                                  false, NULL, true);
> +           }
> +         else
> +           goto bad_if;
> +       }
> +      else if (TREE_CODE (cmp_expr.value) == EQ_EXPR)
> +       goto bad_if;
> +      else if (c_tree_equal (TREE_OPERAND (cmp_expr.value, 1),
> unfolded_lhs)
> +              && c_tree_equal (TREE_OPERAND (cmp_expr.value, 0), rhs1))
> +       {
> +         opcode = (TREE_CODE (cmp_expr.value) == GT_EXPR
> +                   ? MAX_EXPR : MIN_EXPR);
> +         rhs = c_fully_fold (rhs1, false, NULL, true);
> +         rhs1 = c_fully_fold (TREE_OPERAND (cmp_expr.value, 1),
> +                              false, NULL, true);
> +       }
> +      else
> +       {
> +       bad_if:
> +         c_parser_error (parser,
> +                         "invalid form of %<#pragma omp atomic
> compare%>");
> +         goto saw_error;
> +       }
> +
> +      if (c_parser_next_token_is_keyword (parser, RID_ELSE))
> +       {
> +         if (code != OMP_ATOMIC_CAPTURE_NEW
> +             || (structured_block && r == NULL_TREE)
> +             || TREE_CODE (cmp_expr.value) != EQ_EXPR)
> +           {
> +             eloc = c_parser_peek_token (parser)->location;
> +             error_at (eloc, "unexpected %<else%>");
> +             goto saw_error;
> +           }
> +
> +         c_parser_consume_token (parser);
> +
> +         if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>"))
> +           goto saw_error;
> +
> +         extra_scope = true;
> +         v = c_parser_cast_expression (parser, NULL).value;
> +         non_lvalue_p = !lvalue_p (v);
> +         v = c_fully_fold (v, false, NULL, true);
> +         if (v == error_mark_node)
> +           goto saw_error;
> +         if (non_lvalue_p)
> +           v = non_lvalue (v);
> +         if (!c_parser_require (parser, CPP_EQ, "expected %<=%>"))
> +           goto saw_error;
> +
> +         expr = c_parser_expr_no_commas (parser, NULL);
> +
> +         if (!c_tree_equal (expr.value, unfolded_lhs))
> +           goto bad_if;
> +
> +         if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
> +           goto saw_error;
> +
> +         if (!c_parser_require (parser, CPP_CLOSE_BRACE, "expected
> %<}%>"))
> +           goto saw_error;
> +
> +         extra_scope = false;
> +         code = OMP_ATOMIC_CAPTURE_OLD;
> +         if (r == NULL_TREE)
> +           /* Signal to c_finish_omp_atomic that in
> +              if (x == e) { x = d; } else { v = x; }
> +              case the store to v should be conditional.  */
> +           r = void_list_node;
> +       }
> +      else if (code == OMP_ATOMIC_CAPTURE_NEW && !structured_block)
> +       {
> +         c_parser_require_keyword (parser, RID_ELSE, "expected %<else%>");
> +         goto saw_error;
> +       }
> +      else if (code == OMP_ATOMIC_CAPTURE_NEW
> +              && r != NULL_TREE
> +              && v == NULL_TREE)
> +       code = OMP_ATOMIC;
> +      goto stmt_done;
> +    }
>    eloc = c_parser_peek_token (parser)->location;
>    expr = c_parser_cast_expression (parser, NULL);
>    lhs = expr.value;
> @@ -17908,9 +18236,14 @@ restart:
>    orig_lhs = lhs;
>    switch (TREE_CODE (lhs))
>      {
> +    invalid_compare:
> +      error_at (eloc, "invalid form of %<pragma omp atomic compare%>");
> +      /* FALLTHRU */
>      case ERROR_MARK:
>      saw_error:
>        c_parser_skip_to_end_of_block_or_statement (parser);
> +      if (extra_scope && c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
> +       c_parser_consume_token (parser);
>        if (structured_block)
>         {
>           if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
> @@ -17933,6 +18266,8 @@ restart:
>        unfolded_lhs = NULL_TREE;
>        opcode = PLUS_EXPR;
>        rhs = integer_one_node;
> +      if (compare)
> +       goto invalid_compare;
>        break;
>
>      case POSTDECREMENT_EXPR:
> @@ -17944,6 +18279,8 @@ restart:
>        unfolded_lhs = NULL_TREE;
>        opcode = MINUS_EXPR;
>        rhs = integer_one_node;
> +      if (compare)
> +       goto invalid_compare;
>        break;
>
>      case COMPOUND_EXPR:
> @@ -17973,6 +18310,8 @@ restart:
>                   && !structured_block
>                   && TREE_CODE (orig_lhs) == COMPOUND_EXPR)
>                 code = OMP_ATOMIC_CAPTURE_OLD;
> +             if (compare)
> +               goto invalid_compare;
>               break;
>             }
>           if (TREE_CODE (TREE_OPERAND (lhs, 1)) == TRUTH_NOT_EXPR
> @@ -17988,6 +18327,8 @@ restart:
>                   && !structured_block
>                   && TREE_CODE (orig_lhs) == COMPOUND_EXPR)
>                 code = OMP_ATOMIC_CAPTURE_OLD;
> +             if (compare)
> +               goto invalid_compare;
>               break;
>             }
>         }
> @@ -17995,6 +18336,11 @@ restart:
>      default:
>        if (!lvalue_p (unfolded_lhs))
>         lhs = non_lvalue (lhs);
> +      if (compare && !c_parser_next_token_is (parser, CPP_EQ))
> +       {
> +         c_parser_error (parser, "expected %<=%>");
> +         goto saw_error;
> +       }
>        switch (c_parser_peek_token (parser)->type)
>         {
>         case CPP_MULT_EQ:
> @@ -18041,6 +18387,8 @@ restart:
>             case BIT_AND_EXPR:
>             case BIT_IOR_EXPR:
>             case BIT_XOR_EXPR:
> +             if (compare)
> +               break;
>               if (c_tree_equal (TREE_OPERAND (rhs1, 0), unfolded_lhs))
>                 {
>                   opcode = TREE_CODE (rhs1);
> @@ -18061,6 +18409,78 @@ restart:
>                   goto stmt_done;
>                 }
>               break;
> +           case COND_EXPR:
> +             if (!compare)
> +               break;
> +             if (TREE_CODE (TREE_OPERAND (rhs1, 0)) != GT_EXPR
> +                 && TREE_CODE (TREE_OPERAND (rhs1, 0)) != LT_EXPR
> +                 && TREE_CODE (TREE_OPERAND (rhs1, 0)) != EQ_EXPR)
> +               break;
> +             if (!TREE_OPERAND (rhs1, 1))
> +               break;
> +             if (!c_tree_equal (TREE_OPERAND (rhs1, 2), unfolded_lhs))
> +               break;
> +             if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0),
> +                               unfolded_lhs))
> +               {
> +                 if (TREE_CODE (TREE_OPERAND (rhs1, 0)) == EQ_EXPR)
> +                   {
> +                     opcode = COND_EXPR;
> +                     rhs = c_fully_fold (TREE_OPERAND (TREE_OPERAND (rhs1,
> +                                                                     0),
> 1),
> +                                         false, NULL, true);
> +                     rhs1 = c_fully_fold (TREE_OPERAND (rhs1, 1), false,
> +                                          NULL, true);
> +                     goto stmt_done;
> +                   }
> +                 if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0),
> 1),
> +                                   TREE_OPERAND (rhs1, 1)))
> +                   {
> +                     opcode = (TREE_CODE (TREE_OPERAND (rhs1, 0)) ==
> GT_EXPR
> +                               ? MIN_EXPR : MAX_EXPR);
> +                     rhs = c_fully_fold (TREE_OPERAND (rhs1, 1), false,
> NULL,
> +                                         true);
> +                     rhs1 = c_fully_fold (TREE_OPERAND (TREE_OPERAND
> (rhs1,
> +                                                                      0),
> 0),
> +                                          false, NULL, true);
> +                     goto stmt_done;
> +                   }
> +               }
> +             else if (TREE_CODE (TREE_OPERAND (rhs1, 0)) == EQ_EXPR)
> +               break;
> +             else if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0),
> 1),
> +                                    unfolded_lhs))
> +               {
> +                 if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0),
> 0),
> +                                   TREE_OPERAND (rhs1, 1)))
> +                   {
> +                     opcode = (TREE_CODE (TREE_OPERAND (rhs1, 0)) ==
> GT_EXPR
> +                               ? MAX_EXPR : MIN_EXPR);
> +                     rhs = c_fully_fold (TREE_OPERAND (rhs1, 1), false,
> NULL,
> +                                         true);
> +                     rhs1 = c_fully_fold (TREE_OPERAND (TREE_OPERAND
> (rhs1,
> +                                                                      0),
> 1),
> +                                          false, NULL, true);
> +                     goto stmt_done;
> +                   }
> +               }
> +             break;
> +           case EQ_EXPR:
> +             if (!compare
> +                 || code != OMP_ATOMIC_CAPTURE_NEW
> +                 || !structured_block
> +                 || v
> +                 || r)
> +               break;
> +             if (c_parser_next_token_is (parser, CPP_SEMICOLON)
> +                 && c_parser_peek_2nd_token (parser)->keyword == RID_IF)
> +               {
> +                 r = lhs;
> +                 lhs = NULL_TREE;
> +                 c_parser_consume_token (parser);
> +                 goto restart;
> +               }
> +             break;
>             case ERROR_MARK:
>               goto saw_error;
>             default:
> @@ -18109,10 +18529,12 @@ restart:
>        break;
>      }
>  stmt_done:
> -  if (structured_block && code == OMP_ATOMIC_CAPTURE_NEW)
> +  if (structured_block && code == OMP_ATOMIC_CAPTURE_NEW && r ==
> NULL_TREE)
>      {
> -      if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
> +      if (!no_semicolon
> +         && !c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
>         goto saw_error;
> +      no_semicolon = false;
>        v = c_parser_cast_expression (parser, NULL).value;
>        non_lvalue_p = !lvalue_p (v);
>        v = c_fully_fold (v, false, NULL, true);
> @@ -18135,10 +18557,16 @@ stmt_done:
>      }
>    if (structured_block)
>      {
> -      c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
> +      if (!no_semicolon)
> +       c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected
> %<;%>");
>        c_parser_require (parser, CPP_CLOSE_BRACE, "expected %<}%>");
>      }
>  done:
> +  if (weak && opcode != COND_EXPR)
> +    {
> +      error_at (loc, "%<weak%> clause requires atomic equality
> comparison");
> +      weak = false;
> +    }
>    if (unfolded_lhs && unfolded_lhs1
>        && !c_tree_equal (unfolded_lhs, unfolded_lhs1))
>      {
> @@ -18147,12 +18575,12 @@ done:
>        stmt = error_mark_node;
>      }
>    else
> -    stmt = c_finish_omp_atomic (loc, code, opcode, lhs, rhs, v, lhs1,
> rhs1,
> -                               swapped, memory_order);
> +    stmt = c_finish_omp_atomic (loc, code, opcode, lhs, rhs, v, lhs1,
> rhs1, r,
> +                               swapped, memory_order, weak);
>    if (stmt != error_mark_node)
>      add_stmt (stmt);
>
> -  if (!structured_block)
> +  if (!structured_block && !no_semicolon)
>      c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
>  }
>
> --- gcc/c/c-typeck.c.jj 2021-09-09 10:40:19.979262872 +0200
> +++ gcc/c/c-typeck.c    2021-09-09 10:41:42.630084554 +0200
> @@ -12426,6 +12426,13 @@ build_binary_op (location_t location, en
>         maybe_warn_bool_compare (location, code, orig_op0, orig_op1);
>        break;
>
> +    case MIN_EXPR:
> +    case MAX_EXPR:
> +      /* Used for OpenMP atomics.  */
> +      gcc_assert (flag_openmp);
> +      common = 1;
> +      break;
> +
>      default:
>        gcc_unreachable ();
>      }
> --- gcc/cp/parser.c.jj  2021-09-09 10:40:19.996262630 +0200
> +++ gcc/cp/parser.c     2021-09-09 10:41:42.623084654 +0200
> @@ -40193,7 +40193,6 @@ cp_parser_omp_atomic (cp_parser *parser,
>               memory_order = OMP_MEMORY_ORDER_ACQUIRE;
>               break;
>             case NOP_EXPR: /* atomic write */
> -           case OMP_ATOMIC:
>               memory_order = OMP_MEMORY_ORDER_RELEASE;
>               break;
>             default:
> @@ -40209,31 +40208,24 @@ cp_parser_omp_atomic (cp_parser *parser,
>      switch (code)
>        {
>        case OMP_ATOMIC_READ:
> -       if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
> -           || memory_order == OMP_MEMORY_ORDER_RELEASE)
> +       if (memory_order == OMP_MEMORY_ORDER_RELEASE)
>           {
>             error_at (loc, "%<#pragma omp atomic read%> incompatible with "
> -                          "%<acq_rel%> or %<release%> clauses");
> +                          "%<release%> clause");
>             memory_order = OMP_MEMORY_ORDER_SEQ_CST;
>           }
> +       else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL)
> +         memory_order = OMP_MEMORY_ORDER_ACQUIRE;
>         break;
>        case NOP_EXPR: /* atomic write */
> -       if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
> -           || memory_order == OMP_MEMORY_ORDER_ACQUIRE)
> +       if (memory_order == OMP_MEMORY_ORDER_ACQUIRE)
>           {
>             error_at (loc, "%<#pragma omp atomic write%> incompatible with
> "
> -                          "%<acq_rel%> or %<acquire%> clauses");
> -           memory_order = OMP_MEMORY_ORDER_SEQ_CST;
> -         }
> -       break;
> -      case OMP_ATOMIC:
> -       if (memory_order == OMP_MEMORY_ORDER_ACQ_REL
> -           || memory_order == OMP_MEMORY_ORDER_ACQUIRE)
> -         {
> -           error_at (loc, "%<#pragma omp atomic update%> incompatible
> with "
> -                          "%<acq_rel%> or %<acquire%> clauses");
> +                          "%<acquire%> clause");
>             memory_order = OMP_MEMORY_ORDER_SEQ_CST;
>           }
> +       else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL)
> +         memory_order = OMP_MEMORY_ORDER_RELEASE;
>         break;
>        default:
>         break;
> --- gcc/cp/semantics.c.jj       2021-09-09 10:40:26.093175709 +0200
> +++ gcc/cp/semantics.c  2021-09-09 10:59:17.762032066 +0200
> @@ -9956,7 +9956,7 @@ finish_omp_atomic (location_t loc, enum
>           return;
>         }
>        stmt = c_finish_omp_atomic (loc, code, opcode, lhs, rhs,
> -                                 v, lhs1, rhs1, swapped, mo,
> +                                 v, lhs1, rhs1, NULL_TREE, swapped, mo,
> false,
>                                   processing_template_decl != 0);
>        if (stmt == error_mark_node)
>         return;
> --- gcc/testsuite/c-c++-common/gomp/atomic-17.c.jj      2021-09-09
> 10:40:20.088261318 +0200
> +++ gcc/testsuite/c-c++-common/gomp/atomic-17.c 2021-09-09
> 10:41:42.624084639 +0200
> @@ -22,8 +22,18 @@ foo ()
>    v = i = i + 1;
>    #pragma omp atomic read acquire
>    v = i;
> +  #pragma omp atomic acq_rel read
> +  v = i;
>    #pragma omp atomic release,write
>    i = v;
> +  #pragma omp atomic write,acq_rel
> +  i = v;
>    #pragma omp atomic hint(1),update,release
>    f = f + 2.0;
> +  #pragma omp atomic update ,acquire
> +  i = i + 1;
> +  #pragma omp atomic acq_rel update
> +  i = i + 1;
> +  #pragma omp atomic acq_rel,hint(0)
> +  i = i + 1;
>  }
> --- gcc/testsuite/c-c++-common/gomp/atomic-18.c.jj      2021-09-09
> 10:40:20.088261318 +0200
> +++ gcc/testsuite/c-c++-common/gomp/atomic-18.c 2021-09-09
> 10:41:42.624084639 +0200
> @@ -8,28 +8,18 @@ foo (int j)
>    i = i + 1;
>    #pragma omp atomic seq_cst release   /* { dg-error "too many memory
> order clauses" } */
>    i = i + 1;
> -  #pragma omp atomic read,release      /* { dg-error "incompatible with
> 'acq_rel' or 'release' clauses" } */
> +  #pragma omp atomic read,release      /* { dg-error "incompatible with
> 'release' clause" } */
>    v = i;
> -  #pragma omp atomic acq_rel read      /* { dg-error "incompatible with
> 'acq_rel' or 'release' clauses" } */
> -  v = i;
> -  #pragma omp atomic write acq_rel     /* { dg-error "incompatible with
> 'acq_rel' or 'acquire' clauses" } */
> -  i = v;
> -  #pragma omp atomic acquire , write   /* { dg-error "incompatible with
> 'acq_rel' or 'acquire' clauses" } */
> +  #pragma omp atomic acquire , write   /* { dg-error "incompatible with
> 'acquire' clause" } */
>    i = v;
> -  #pragma omp atomic update ,acquire   /* { dg-error "incompatible with
> 'acq_rel' or 'acquire' clauses" } */
> -  i = i + 1;
> -  #pragma omp atomic acq_rel update    /* { dg-error "incompatible with
> 'acq_rel' or 'acquire' clauses" } */
> -  i = i + 1;
> -  #pragma omp atomic acq_rel,hint(0)   /* { dg-error "incompatible with
> 'acq_rel' or 'acquire' clauses" } */
> -  i = i + 1;
> -  #pragma omp atomic acquire           /* { dg-error "incompatible with
> 'acq_rel' or 'acquire' clauses" } */
> -  i = i + 1;
> -  #pragma omp atomic capture hint (0) capture  /* { dg-error "too many
> atomic clauses" } */
> +  #pragma omp atomic capture hint (0) capture  /* { dg-error "too many
> 'capture' clauses" "" { target c } } */
> +                                       /* { dg-error "too many atomic
> clauses" "" { target c++ } .-1 } */
>    v = i = i + 1;
>    #pragma omp atomic hint(j + 2)       /* { dg-error "constant integer
> expression" } */
>    i = i + 1;
>    #pragma omp atomic hint(f)           /* { dg-error "integ" } */
>    i = i + 1;
> -  #pragma omp atomic foobar            /* { dg-error "expected 'read',
> 'write', 'update', 'capture', 'seq_cst', 'acq_rel', 'release', 'relaxed' or
> 'hint' clause" } */
> -  i = i + 1;                           /* { dg-error "expected end of
> line before" "" { target *-*-* } .-1 } */
> +  #pragma omp atomic foobar            /* { dg-error "expected 'read',
> 'write', 'update', 'capture', 'compare', 'weak', 'fail', 'seq_cst',
> 'acq_rel', 'release', 'relaxed' or 'hint' clause" "" { target c } } */
> +                                       /* { dg-error "expected 'read',
> 'write', 'update', 'capture', 'seq_cst', 'acq_rel', 'release', 'relaxed' or
> 'hint' clause" "" { target c++ } .-1 } */
> +  i = i + 1;                           /* { dg-error "expected end of
> line before" "" { target *-*-* } .-2 } */
>  }
> --- gcc/testsuite/c-c++-common/gomp/atomic-21.c.jj      2020-04-15
> 09:58:30.369649495 +0200
> +++ gcc/testsuite/c-c++-common/gomp/atomic-21.c 2021-09-09
> 17:25:18.782025085 +0200
> @@ -1,6 +1,7 @@
>  /* { dg-do compile } */
>  /* { dg-additional-options "-fdump-tree-original" } */
> -/* { dg-final { scan-tree-dump-times "omp atomic release" 4 "original" }
> } */
> +/* { dg-final { scan-tree-dump-times "omp atomic release" 2 "original" }
> } */
> +/* { dg-final { scan-tree-dump-times "omp atomic acq_rel" 2 "original" }
> } */
>  /* { dg-final { scan-tree-dump-times "omp atomic read acquire" 1
> "original" } } */
>  /* { dg-final { scan-tree-dump-times "omp atomic capture acq_rel" 1
> "original" } } */
>
> --- gcc/testsuite/c-c++-common/gomp/atomic-25.c.jj      2021-09-09
> 10:41:42.624084639 +0200
> +++ gcc/testsuite/c-c++-common/gomp/atomic-25.c 2021-09-10
> 13:18:43.096258564 +0200
> @@ -0,0 +1,50 @@
> +/* { dg-do compile { target c } } */
> +
> +int x, r, z;
> +double d, v;
> +long double ld;
> +
> +void
> +foo (int y, double e, long double f)
> +{
> +  #pragma omp atomic compare update seq_cst
> +  x = x > y ? y : x;
> +  #pragma omp atomic compare relaxed
> +  d = e > d ? e : d;
> +  #pragma omp atomic compare
> +  d = f < d ? f : d;
> +  #pragma omp atomic compare seq_cst fail(relaxed)
> +  x = 12U < x ? 12U : x;
> +  #pragma omp atomic compare
> +  x = x == 7 ? 24 : x;
> +  #pragma omp atomic compare
> +  x = x == 123UL ? 256LL : x;
> +  #pragma omp atomic compare
> +  ld = ld == f ? f + 5.0L : ld;
> +  #pragma omp atomic compare
> +  if (x == 9) { x = 5; }
> +  #pragma omp atomic compare
> +  if (x > 5) { x = 5; }
> +  #pragma omp atomic compare
> +  if (7 > x) { x = 7; }
> +  #pragma omp atomic compare update capture seq_cst fail(acquire)
> +  v = d = f > d ? f : d;
> +  #pragma omp atomic update capture compare
> +  v = x = x < 24ULL ? 24ULL : x;
> +  #pragma omp atomic compare, capture, update
> +  v = x = x == e ? f : x;
> +  #pragma omp atomic capture compare
> +  { v = d; if (d > e) { d = e; } }
> +  #pragma omp atomic compare capture
> +  { if (e < d) { d = e; } v = d; }
> +  #pragma omp atomic compare capture
> +  { y = x; if (x == 42) { x = 7; } }
> +  #pragma omp atomic capture compare weak
> +  { if (x == 42) { x = 7; } y = x; }
> +  #pragma omp atomic capture compare fail(seq_cst)
> +  if (d == 8.0) { d = 16.0; } else { v = d; }
> +  #pragma omp atomic capture compare
> +  { r = x == 8; if (r) { x = 24; } }
> +  #pragma omp atomic compare capture
> +  { r = x == y; if (r) { x = y + 6; } else { z = x; } }
> +}
> --- gcc/testsuite/c-c++-common/gomp/atomic-26.c.jj      2021-09-09
> 10:41:42.624084639 +0200
> +++ gcc/testsuite/c-c++-common/gomp/atomic-26.c 2021-09-10
> 15:16:10.111808506 +0200
> @@ -0,0 +1,63 @@
> +/* { dg-do compile { target c } } */
> +
> +int x;
> +double d;
> +
> +double
> +foo (int y, double e, long double f)
> +{
> +  double v;
> +  #pragma omp atomic compare compare   /* { dg-error "too many 'compare'
> clauses" } */
> +  x = x > y ? y : x;
> +  #pragma omp atomic compare fail(seq_cst) fail(seq_cst)       /* {
> dg-error "too many 'fail' clauses" } */
> +  d = e > d ? e : d;
> +  #pragma omp atomic compare,fail(seq_cst),fail(relaxed)       /* {
> dg-error "too many 'fail' clauses" } */
> +  d = e > d ? e : d;
> +  #pragma omp atomic compare weak weak /* { dg-error "too many 'weak'
> clauses" } */
> +  d = d == e ? e + 1.0 : d;
> +  #pragma omp atomic read capture      /* { dg-error "'capture' clause is
> incompatible with 'read' or 'write' clauses" } */
> +  v = d;
> +  #pragma omp atomic capture, write    /* { dg-error "'capture' clause is
> incompatible with 'read' or 'write' clauses" } */
> +  d = v;
> +  #pragma omp atomic read compare      /* { dg-error "'compare' clause is
> incompatible with 'read' or 'write' clauses" } */
> +  v = d;
> +  #pragma omp atomic compare, write    /* { dg-error "'compare' clause is
> incompatible with 'read' or 'write' clauses" } */
> +  d = v;
> +  #pragma omp atomic read fail(seq_cst)        /* { dg-error "'fail'
> clause requires 'compare' clause" } */
> +  v = d;
> +  #pragma omp atomic fail(relaxed), write      /* { dg-error "'fail'
> clause requires 'compare' clause" } */
> +  d = v;
> +  #pragma omp atomic fail(relaxed) update      /* { dg-error "'fail'
> clause requires 'compare' clause" } */
> +  d += 3.0;
> +  #pragma omp atomic fail(relaxed)     /* { dg-error "'fail' clause
> requires 'compare' clause" } */
> +  d += 3.0;
> +  #pragma omp atomic capture fail(relaxed)     /* { dg-error "'fail'
> clause requires 'compare' clause" } */
> +  v = d += 3.0;
> +  #pragma omp atomic read weak         /* { dg-error "'weak' clause
> requires 'compare' clause" } */
> +  v = d;
> +  #pragma omp atomic weak, write       /* { dg-error "'weak' clause
> requires 'compare' clause" } */
> +  d = v;
> +  #pragma omp atomic weak update       /* { dg-error "'weak' clause
> requires 'compare' clause" } */
> +  d += 3.0;
> +  #pragma omp atomic weak              /* { dg-error "'weak' clause
> requires 'compare' clause" } */
> +  d += 3.0;
> +  #pragma omp atomic capture weak      /* { dg-error "'weak' clause
> requires 'compare' clause" } */
> +  v = d += 3.0;
> +  #pragma omp atomic compare,weak      /* { dg-error "'weak' clause
> requires atomic equality comparison" } */
> +  d = e > d ? e : d;
> +  #pragma omp atomic compare fail      /* { dg-error "expected '\\\('
> before end of line" } */
> +  d = e > d ? e : d;
> +  #pragma omp atomic compare fail(     /* { dg-error "expected 'seq_cst',
> 'acquire' or 'relaxed' before end of line" } */
> +  d = e > d ? e : d;
> +  #pragma omp atomic compare fail()    /* { dg-error "expected 'seq_cst',
> 'acquire' or 'relaxed' before '\\\)' token" } */
> +  d = e > d ? e : d;
> +  #pragma omp atomic compare fail(foobar)      /* { dg-error "expected
> 'seq_cst', 'acquire' or 'relaxed' before 'foobar'" } */
> +  d = e > d ? e : d;
> +  #pragma omp atomic compare fail(acq_rel)     /* { dg-error "expected
> 'seq_cst', 'acquire' or 'relaxed' before 'acq_rel'" } */
> +  d = e > d ? e : d;
> +  #pragma omp atomic compare fail(release)     /* { dg-error "expected
> 'seq_cst', 'acquire' or 'relaxed' before 'release'" } */
> +  d = e > d ? e : d;
> +  #pragma omp atomic compare fail(seq_cst      /* { dg-error "expected
> '\\\)' before end of line" } */
> +  d = e > d ? e : d;
> +  return v;
> +}
> --- gcc/testsuite/c-c++-common/gomp/atomic-27.c.jj      2021-09-09
> 16:58:07.052709219 +0200
> +++ gcc/testsuite/c-c++-common/gomp/atomic-27.c 2021-09-09
> 17:05:13.271786779 +0200
> @@ -0,0 +1,41 @@
> +/* PR middle-end/88968 */
> +/* { dg-do compile { target c } } */
> +
> +struct __attribute__((packed)) S {
> +  unsigned int a : 16;
> +  unsigned int b : 1;
> +} s;
> +
> +void
> +foo (int y, int z)
> +{
> +  #pragma omp atomic compare
> +  s.a = s.a == y ? z : s.a;
> +}
> +
> +int
> +bar (int y, int z)
> +{
> +  int r;
> +  #pragma omp atomic compare capture
> +  { r = s.a == y; if (r) { s.a = z; } }
> +  return r;
> +}
> +
> +int
> +baz (int y, int z)
> +{
> +  int v;
> +  #pragma omp atomic compare capture
> +  if (s.a == y) { s.a = z; } else { v = s.a; }
> +  return v;
> +}
> +
> +int
> +qux (int y, int z)
> +{
> +  int v;
> +  #pragma omp atomic compare capture
> +  v = s.a = s.a == y ? z : s.a;
> +  return v;
> +}
> --- gcc/testsuite/c-c++-common/gomp/atomic-28.c.jj      2021-09-10
> 11:47:09.193274972 +0200
> +++ gcc/testsuite/c-c++-common/gomp/atomic-28.c 2021-09-10
> 11:52:05.398115961 +0200
> @@ -0,0 +1,43 @@
> +/* { dg-do compile { target c } } */
> +/* { dg-additional-options "-O2 -fdump-tree-ompexp" } */
> +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> \\\(\[^\n\r]*, 4, 5, 5\\\);" 1 "ompexp" { target sync_int_long } } } */
> +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> \\\(\[^\n\r]*, 4, 4, 2\\\);" 1 "ompexp" { target sync_int_long } } } */
> +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> \\\(\[^\n\r]*, 260, 5, 0\\\);" 1 "ompexp" { target sync_int_long } } } */
> +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> \\\(\[^\n\r]*, 4, 0, 0\\\);" 1 "ompexp" { target sync_int_long } } } */
> +/* { dg-final { scan-tree-dump-not "__atomic_load_4 \\\(" "ompexp" {
> target sync_int_long } } } */
> +
> +int x;
> +
> +void
> +foo (int y, int z)
> +{
> +  #pragma omp atomic compare seq_cst
> +  x = x == y ? z : x;
> +}
> +
> +int
> +bar (int y, int z)
> +{
> +  int r;
> +  #pragma omp atomic compare capture acq_rel fail (acquire)
> +  { r = x == y; if (r) { x = z; } }
> +  return r;
> +}
> +
> +int
> +baz (int y, int z)
> +{
> +  int v;
> +  #pragma omp atomic compare capture seq_cst fail (relaxed) weak
> +  if (x == y) { x = z; } else { v = x; }
> +  return v;
> +}
> +
> +int
> +qux (int y, int z)
> +{
> +  int v;
> +  #pragma omp atomic compare capture
> +  v = x = x == y ? z : x;
> +  return v;
> +}
> --- gcc/testsuite/c-c++-common/gomp/atomic-29.c.jj      2021-09-10
> 11:47:17.093164041 +0200
> +++ gcc/testsuite/c-c++-common/gomp/atomic-29.c 2021-09-10
> 11:52:33.428722747 +0200
> @@ -0,0 +1,43 @@
> +/* { dg-do compile { target c } } */
> +/* { dg-additional-options "-O2 -fdump-tree-ompexp" } */
> +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> \\\(\[^\n\r]*, 8, 5, 5\\\);" 1 "ompexp" { target sync_int_long } } } */
> +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> \\\(\[^\n\r]*, 8, 4, 2\\\);" 1 "ompexp" { target sync_int_long } } } */
> +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> \\\(\[^\n\r]*, 264, 5, 0\\\);" 1 "ompexp" { target sync_int_long } } } */
> +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> \\\(\[^\n\r]*, 8, 0, 0\\\);" 1 "ompexp" { target sync_int_long } } } */
> +/* { dg-final { scan-tree-dump-not "__atomic_load_8 \\\(" "ompexp" {
> target sync_int_long } } } */
> +
>

This test fails on arm*linux when forcing old CPU/ARCH (eg  -march=armv5t).
Not sure how easy it is to fix?
sync_int_long returns true for arm*-*-linux-*,
but for other arm targets, it depends on the result
of check_effective_target_arm_acq_rel.

Is it just a matter of removing arm*-*-linux-* from sync_int_long and
always rely on arm_acq_rel?

Christophe

+double x;
> +
> +void
> +foo (double y, double z)
> +{
> +  #pragma omp atomic compare seq_cst
> +  x = x == y ? z : x;
> +}
> +
> +double
> +bar (double y, double z)
> +{
> +  int r;
> +  #pragma omp atomic compare capture acq_rel fail (acquire)
> +  { r = x == y; if (r) { x = z; } }
> +  return r;
> +}
> +
> +double
> +baz (double y, double z)
> +{
> +  double v;
> +  #pragma omp atomic compare capture seq_cst fail (relaxed) weak
> +  if (x == y) { x = z; } else { v = x; }
> +  return v;
> +}
> +
> +double
> +qux (double y, double z)
> +{
> +  double v;
> +  #pragma omp atomic compare capture
> +  v = x = x == y ? z : x;
> +  return v;
> +}
> --- gcc/testsuite/c-c++-common/gomp/atomic-30.c.jj      2021-09-10
> 15:25:01.955425201 +0200
> +++ gcc/testsuite/c-c++-common/gomp/atomic-30.c 2021-09-10
> 18:02:50.229520066 +0200
> @@ -0,0 +1,137 @@
> +/* { dg-do compile { target c } } */
> +
> +int x;
> +double d, g;
> +
> +double
> +foo (int y, double e, long double f)
> +{
> +  double v;
> +  int r, r2 = 0;
> +  #pragma omp atomic capture compare
> +  v = if (d == e) { d = f; };  /* { dg-error "expected expression" } */
> +  #pragma omp atomic compare
> +  if;                          /* { dg-error "expected '\\\(' before ';'
> token" } */
> +  #pragma omp atomic compare
> +  if (d >= e) { d = e; }       /* { dg-error "expected '==', '<' or '>'
> comparison in 'if' condition" } */
> +  #pragma omp atomic compare
> +  if (d <= e) { d = e; }       /* { dg-error "expected '==', '<' or '>'
> comparison in 'if' condition" } */
> +  #pragma omp atomic compare
> +  if (d != e) { d = e; }       /* { dg-error "expected '==', '<' or '>'
> comparison in 'if' condition" } */
> +  #pragma omp atomic compare
> +  if (d + e) { d = e; }                /* { dg-error "expected '==', '<'
> or '>' comparison in 'if' condition" } */
> +  #pragma omp atomic capture compare
> +  { r = d >= e; if (r) { d = f; } }    /* { dg-error "expected '==', '<'
> or '>' comparison in 'if' condition" } */
> +  #pragma omp atomic capture compare
> +  { r = d <= e; if (r) { d = f; } }    /* { dg-error "expected '==', '<'
> or '>' comparison in 'if' condition" } */
> +  #pragma omp atomic capture compare
> +  { r = d > e; if (r) { d = f; } }     /* { dg-error "expected '==', '<'
> or '>' comparison in 'if' condition" } */
> +  #pragma omp atomic capture compare
> +  { r = d < e; if (r) { d = f; } }     /* { dg-error "expected '==', '<'
> or '>' comparison in 'if' condition" } */
> +  #pragma omp atomic capture compare
> +  { r = d != e; if (r) { d = f; } }    /* { dg-error "expected '==', '<'
> or '>' comparison in 'if' condition" } */
> +  #pragma omp atomic capture compare
> +  { r = d + e; if (r) { d = f; } }     /* { dg-error "expected '==', '<'
> or '>' comparison in 'if' condition" } */
> +  #pragma omp atomic capture compare
> +  { r = d == e; if (r2) { d = f; } }   /* { dg-error "invalid form of
> '#pragma omp atomic compare' before '\{' token" } */
> +  #pragma omp atomic capture compare
> +  if (d > e) { d = e; }                        /* { dg-error "expected
> '==' comparison in 'if' condition" } */
> +  #pragma omp atomic capture compare
> +  if (d < e) { d = e; }                        /* { dg-error "expected
> '==' comparison in 'if' condition" } */
> +  #pragma omp atomic compare
> +  if (d < e) d = e;                    /* { dg-error "expected '\{'
> before 'd'" } */
> +  #pragma omp atomic compare
> +  if (d == e) d = e + 1.0;             /* { dg-error "expected '\{'
> before 'd'" } */
> +  #pragma omp atomic compare
> +  if (d < e) { d += e; }               /* { dg-error "expected '=' before
> '\\\+=' token" } */
> +  #pragma omp atomic compare
> +  if (d < e) { d = e };                        /* { dg-error "expected
> ';' before '\}' token" } */
> +  #pragma omp atomic compare
> +  if (d < e) { d = e; e = 1.0; }       /* { dg-error "expected '\}'
> before 'e'" } */
> +  #pragma omp atomic compare
> +  if (e == d) { d = f; };              /* { dg-error "invalid form of
> '#pragma omp atomic compare' before ';' token" } */
> +  #pragma omp atomic compare
> +  if (e == d) { g = f; };              /* { dg-error "invalid form of
> '#pragma omp atomic compare' before ';' token" } */
> +  #pragma omp atomic compare
> +  if (d < e) { g = e; };               /* { dg-error "invalid form of
> '#pragma omp atomic compare' before ';' token" } */
> +  #pragma omp atomic compare
> +  if (d > e) { g = e; };               /* { dg-error "invalid form of
> '#pragma omp atomic compare' before ';' token" } */
> +  #pragma omp atomic compare
> +  if (d < e) { d = g; };               /* { dg-error "invalid form of
> '#pragma omp atomic compare' before ';' token" } */
> +  #pragma omp atomic compare
> +  if (d > e) { d = g; };               /* { dg-error "invalid form of
> '#pragma omp atomic compare' before ';' token" } */
> +  #pragma omp atomic compare
> +  if (d == e) { d = f; } else ;                /* { dg-error "unexpected
> 'else'" } */
> +  #pragma omp atomic compare capture
> +  { if (d == e) { d = f; } else { v = d; } v = d; }            /* {
> dg-error "unexpected 'else'" } */
> +  #pragma omp atomic compare
> +  if (d < e) { d = e; } else { v = d; }        /* { dg-error "unexpected
> 'else'" } */
> +  #pragma omp atomic compare capture
> +  if (d == e) { d = f; } else v = d;   /* { dg-error "expected '\{'
> before 'v'" } */
> +  #pragma omp atomic compare capture
> +  if (d == e) { d = f; } else { v += d;        }       /* { dg-error
> "expected '=' before '\\\+=' token" } */
> +  #pragma omp atomic compare capture
> +  if (d == e) { d = f; } else { v = e; }       /* { dg-error "invalid
> form of '#pragma omp atomic compare' before ';' token" } */
> +  #pragma omp atomic compare capture
> +  if (d == e) { d = f; } else { v = d };       /* { dg-error "expected
> ';' before '\}' token" } */
> +  #pragma omp atomic compare capture
> +  if (d == e) { d = f; };              /* { dg-error "expected 'else'
> before ';' token" } */
> +  #pragma omp atomic compare
> +  x++;                                 /* { dg-error "invalid form of
> 'pragma omp atomic compare'" } */
> +  #pragma omp atomic compare
> +  x--;                                 /* { dg-error "invalid form of
> 'pragma omp atomic compare'" } */
> +  #pragma omp atomic compare
> +  ++x;                                 /* { dg-error "invalid form of
> 'pragma omp atomic compare'" } */
> +  #pragma omp atomic compare
> +  --x;                                 /* { dg-error "invalid form of
> 'pragma omp atomic compare'" } */
> +  #pragma omp atomic compare
> +  x += 3;                              /* { dg-error "expected '=' before
> '\\\+=' token" } */
> +  #pragma omp atomic compare
> +  x -= 5;                              /* { dg-error "expected '=' before
> '-=' token" } */
> +  #pragma omp atomic compare
> +  x *= 2;                              /* { dg-error "expected '=' before
> '\\\*=' token" } */
> +  #pragma omp atomic compare
> +  x |= 5;                              /* { dg-error "expected '=' before
> '\\\|=' token" } */
> +  #pragma omp atomic compare
> +  x &= ~5;                             /* { dg-error "expected '=' before
> '\\\&=' token" } */
> +  #pragma omp atomic compare
> +  x ^= 5;                              /* { dg-error "expected '=' before
> '\\\^=' token" } */
> +  #pragma omp atomic compare
> +  x = x + 3;                           /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x - 5;                           /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = 2 * x;                           /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = 5 | x;                           /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x & ~5;                          /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x | 5;                           /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x >= 5 ? 5 : x;                  /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x <= 5 ? 5 : x;                  /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x != 5 ? 7 : x;                  /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = 5 == x ? 7 : x;                  /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x == 5 ? x : 7;                  /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x == 5 ? 9 : 7;                  /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x > 5 ? 6 : x;                   /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x < 5 ? 6 : x;                   /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x > 5 ? x : 6;                   /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic compare
> +  x = x < 5 ? x : 6;                   /* { dg-error "invalid form of
> '#pragma omp atomic' before ';' token" } */
> +  #pragma omp atomic capture
> +  r = x == 5;                          /* { dg-error "invalid operator
> for '#pragma omp atomic' before '==' token" } */
> +  #pragma omp atomic capture compare
> +  r = x == 5;                          /* { dg-error "expected '=' before
> '==' token" } */
> +  #pragma omp atomic capture compare   /* { dg-error "'#pragma omp atomic
> compare capture' with non-integral comparison result" } */
> +  { v = x == 5; if (v) { x = 6; } }
> +}
> --- gcc/testsuite/c-c++-common/goacc-gomp/atomic.c.jj   2020-11-06
> 11:14:19.942201229 +0100
> +++ gcc/testsuite/c-c++-common/goacc-gomp/atomic.c      2021-09-09
> 17:10:58.686986511 +0200
> @@ -37,7 +37,8 @@ foo ()
>
>  /* { dg-final { scan-tree-dump-times "i = #pragma omp atomic read
> acquire" 1 "original" } } */
>  /* { dg-final { scan-tree-dump-times "i = #pragma omp atomic read
> relaxed" 1 "original" } } */
> -/* { dg-final { scan-tree-dump-times "#pragma omp atomic release" 2
> "original" } } */
> +/* { dg-final { scan-tree-dump-times "#pragma omp atomic acq_rel" 1
> "original" } } */
> +/* { dg-final { scan-tree-dump-times "#pragma omp atomic release" 1
> "original" } } */
>  /* { dg-final { scan-tree-dump-times "#pragma omp atomic relaxed" 2
> "original" } } */
>  /* { dg-final { scan-tree-dump-times "v = #pragma omp atomic capture
> acq_rel" 1  "original" } } */
>  /* { dg-final { scan-tree-dump-times "v = #pragma omp atomic capture
> relaxed" 2 "original" } } */
> --- gcc/testsuite/gcc.dg/gomp/atomic-5.c.jj     2021-09-09
> 10:40:20.099261161 +0200
> +++ gcc/testsuite/gcc.dg/gomp/atomic-5.c        2021-09-09
> 10:41:42.625084625 +0200
> @@ -27,7 +27,7 @@ void f1(void)
>    #pragma omp atomic
>      bar() += 1;                /* { dg-error "lvalue required" } */
>    #pragma omp atomic a /* { dg-error "expected end of line" } */
> -    x++;               /* { dg-error "expected 'read', 'write', 'update',
> 'capture', 'seq_cst', 'acq_rel', 'release', 'relaxed' or 'hint' clause" ""
> { target *-*-* } .-1 } */
> +    x++;               /* { dg-error "expected 'read', 'write', 'update',
> 'capture', 'compare', 'weak', 'fail', 'seq_cst', 'acq_rel', 'release',
> 'relaxed' or 'hint' clause" "" { target *-*-* } .-1 } */
>    #pragma omp atomic
>      ;                  /* { dg-error "expected expression" } */
>    #pragma omp atomic
> --- gcc/testsuite/g++.dg/gomp/atomic-18.C.jj    2020-04-15
> 09:58:30.372649450 +0200
> +++ gcc/testsuite/g++.dg/gomp/atomic-18.C       2021-09-09
> 17:27:28.671222445 +0200
> @@ -1,6 +1,7 @@
>  // { dg-do compile }
>  // { dg-additional-options "-fdump-tree-original" }
> -// { dg-final { scan-tree-dump-times "omp atomic release" 5 "original" } }
> +// { dg-final { scan-tree-dump-times "omp atomic release" 4 "original" } }
> +// { dg-final { scan-tree-dump-times "omp atomic acq_rel" 1 "original" } }
>  // { dg-final { scan-tree-dump-times "omp atomic seq_cst" 1 "original" } }
>  // { dg-final { scan-tree-dump-times "omp atomic relaxed" 2 "original" } }
>  // { dg-final { scan-tree-dump-times "omp atomic capture acq_rel" 3
> "original" } }
> --- libgomp/testsuite/libgomp.c-c++-common/atomic-19.c.jj       2021-09-10
> 12:09:43.320275112 +0200
> +++ libgomp/testsuite/libgomp.c-c++-common/atomic-19.c  2021-09-10
> 14:24:03.200540163 +0200
> @@ -0,0 +1,274 @@
> +// { dg-do run { target c } }
> +
> +extern
> +#ifdef __cplusplus
> +"C"
> +#endif
> +void abort (void);
> +int x = 6;
> +int w, y;
> +
> +int *
> +foo (void)
> +{
> +  if (w)
> +    abort ();
> +  return &y;
> +}
> +
> +int
> +main ()
> +{
> +  int v, r;
> +  #pragma omp atomic compare
> +  x = x > 8 ? 8 : x;
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6)
> +    abort ();
> +  #pragma omp atomic compare
> +  x = x > 4 ? 4 : x;
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 4)
> +    abort ();
> +  #pragma omp atomic compare capture
> +  v = x = x < 8 ? 8 : x;
> +  if (v != 8)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 8)
> +    abort ();
> +  #pragma omp atomic capture compare
> +  { v = x; x = x < 12 ? 12 : x; }
> +  if (v != 8)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic capture compare
> +  { v = x; x = x < 4 ? 4 : x; }
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic write
> +  x = -32;
> +  #pragma omp atomic capture compare seq_cst fail(relaxed)
> +  { x = 12U < x ? 12U : x; v = x; }
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic compare
> +  x = x == 12 ? 16 : x;
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 16)
> +    abort ();
> +  r = 57;
> +  #pragma omp atomic compare capture
> +  v = x = x == 15 ? r + 7 : x;
> +  if (v != 16)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 16)
> +    abort ();
> +  #pragma omp atomic capture, update, compare seq_cst fail(acquire)
> +  { v = x; x = x == 73ULL - r ? 12LL : x; }
> +  if (v != 16)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic update, compare, capture
> +  { x = x == 69LL - r ? (unsigned char) 6 : x; v = x; }
> +  if (v != 6)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6)
> +    abort ();
> +  #pragma omp atomic compare
> +  if (x > 8) { x = 8; }
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6)
> +    abort ();
> +  #pragma omp atomic compare
> +  if (x > 4) { x = 4; }
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 4)
> +    abort ();
> +  #pragma omp atomic compare capture
> +  { if (x < 8) { x = 8; } v = x; }
> +  if (v != 8)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 8)
> +    abort ();
> +  #pragma omp atomic capture compare
> +  { v = x; if (x < 12) { x = 12; } }
> +  if (v != 8)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic capture compare
> +  { v = x; if (x < 4) { x = 4; } }
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic write
> +  x = -32;
> +  #pragma omp atomic capture compare seq_cst fail(relaxed)
> +  { if (12U < x) { x = 12U; } v = x; }
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic compare
> +  if (x == 12) { x = 16; }
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 16)
> +    abort ();
> +  r = 57;
> +  #pragma omp atomic compare capture
> +  { if (x == 15) { x = r + 7; } v = x; }
> +  if (v != 16)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 16)
> +    abort ();
> +  #pragma omp atomic capture, update, compare seq_cst fail(acquire)
> +  { v = x; if (x == 73ULL - r) { x = 12LL; } }
> +  if (v != 16)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12)
> +    abort ();
> +  #pragma omp atomic update, compare, capture
> +  { if (x == 69LL - r) { x = (unsigned char) 6; } v = x; }
> +  if (v != 6)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6)
> +    abort ();
> +  v = 24;
> +  #pragma omp atomic compare capture
> +  if (x == 12) { x = 16; } else { v = x; }
> +  if (v != 6)
> +    abort ();
> +  v = 32;
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6)
> +    abort ();
> +  v = 147;
> +  #pragma omp atomic capture compare
> +  if (x == 6) { x = 57; } else { v = x; }
> +  if (v != 147)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 57)
> +    abort ();
> +  #pragma omp atomic update, capture, compare, weak, seq_cst, fail
> (relaxed)
> +  { r = x == 137; if (r) { x = 174; } }
> +  if (r)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 57)
> +    abort ();
> +  #pragma omp atomic compare capture fail (relaxed)
> +  { r = x == 57; if (r) { x = 6; } }
> +  if (r != 1)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6)
> +    abort ();
> +  v = -5;
> +  #pragma omp atomic capture compare
> +  { r = x == 17; if (r) { x = 25; } else { v = x; } }
> +  if (r || v != 6)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6)
> +    abort ();
> +  v = 15;
> +  #pragma omp atomic capture compare
> +  { r = x == 6; if (r) { x = 23; } else { v = x; } }
> +  if (r != 1 || v != 15)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 23)
> +    abort ();
> +  w = 1;
> +  #pragma omp atomic compare capture
> +  if (x == 23) { x = 57; } else { foo ()[0] = x; }
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 57)
> +    abort ();
> +  #pragma omp atomic capture update compare
> +  { r = x == 57; if (r) { x = 23; } else { foo ()[0] = x; } }
> +  if (r != 1)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 23)
> +    abort ();
> +  w = 0;
> +  #pragma omp atomic compare capture
> +  if (x == 24) { x = 57; } else { foo ()[0] = x; }
> +  if (y != 23)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 23)
> +    abort ();
> +  y = -5;
> +  #pragma omp atomic capture update compare
> +  {
> +    r = x == 57;
> +    if (r)
> +      {
> +       x = 27;
> +      }
> +    else
> +      {
> +       foo ()[0] = x;
> +      }
> +  }
> +  if (r || y != 23)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 23)
> +    abort ();
> +  return 0;
> +}
> --- libgomp/testsuite/libgomp.c-c++-common/atomic-20.c.jj       2021-09-10
> 14:08:45.330336955 +0200
> +++ libgomp/testsuite/libgomp.c-c++-common/atomic-20.c  2021-09-10
> 14:27:14.746865949 +0200
> @@ -0,0 +1,203 @@
> +// { dg-do run { target c } }
> +
> +extern
> +#ifdef __cplusplus
> +"C"
> +#endif
> +void abort (void);
> +float x = 6.0f;
> +
> +int
> +main ()
> +{
> +  float v;
> +  int r;
> +  #pragma omp atomic compare
> +  x = x > 8.0f ? 8.0f : x;
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6.0f)
> +    abort ();
> +  #pragma omp atomic compare
> +  x = x > 4.0f ? 4.0f : x;
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 4.0f)
> +    abort ();
> +  #pragma omp atomic compare capture
> +  v = x = x < 8.0f ? 8.0f : x;
> +  if (v != 8.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 8)
> +    abort ();
> +  #pragma omp atomic capture compare
> +  { v = x; x = x < 12.0f ? 12.0f : x; }
> +  if (v != 8.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12.0f)
> +    abort ();
> +  #pragma omp atomic capture compare
> +  { v = x; x = x < 4.0f ? 4.0f : x; }
> +  if (v != 12.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12.0f)
> +    abort ();
> +  #pragma omp atomic compare
> +  x = x == 12.0 ? 16.0L : x;
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 16.0)
> +    abort ();
> +  r = 57;
> +  #pragma omp atomic compare capture
> +  v = x = x == 15.0f ? r + 7.0f : x;
> +  if (v != 16.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 16.0f)
> +    abort ();
> +  #pragma omp atomic capture, update, compare seq_cst fail(acquire)
> +  { v = x; x = x == 73.0L - r ? 12.0f : x; }
> +  if (v != 16.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12.0f)
> +    abort ();
> +  #pragma omp atomic update, compare, capture
> +  { x = x == 69.0 - r ? 6.0f : x; v = x; }
> +  if (v != 6.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6.0f)
> +    abort ();
> +  #pragma omp atomic compare
> +  if (x > 8.0f) { x = 8.0f; }
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6.0f)
> +    abort ();
> +  #pragma omp atomic compare
> +  if (x > 4.0) { x = 4.0; }
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 4.0f)
> +    abort ();
> +  #pragma omp atomic compare capture
> +  { if (x < 8.0f) { x = 8.0f; } v = x; }
> +  if (v != 8.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 8.0f)
> +    abort ();
> +  #pragma omp atomic capture compare
> +  { v = x; if (x < 12.0f) { x = 12.0f; } }
> +  if (v != 8.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12.0f)
> +    abort ();
> +  #pragma omp atomic capture compare
> +  { v = x; if (x < 4.0L) { x = 4.0L; } }
> +  if (v != 12.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12.0f)
> +    abort ();
> +  #pragma omp atomic compare
> +  if (x == 12.0f) { x = 16.0L; }
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 16.0f)
> +    abort ();
> +  r = 57.0;
> +  #pragma omp atomic compare capture
> +  { if (x == 15.0f) { x = r + 7.0f; } v = x; }
> +  if (v != 16.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 16.0f)
> +    abort ();
> +  #pragma omp atomic capture, update, compare seq_cst fail(acquire)
> +  { v = x; if (x == 73.0L - r) { x = 12.0L; } }
> +  if (v != 16.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 12.0f)
> +    abort ();
> +  #pragma omp atomic update, compare, capture
> +  { if (x == 69.0L - r) { x = 6.0; } v = x; }
> +  if (v != 6.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6.0f)
> +    abort ();
> +  v = 24;
> +  #pragma omp atomic compare capture
> +  if (x == 12.0f) { x = 16.0f; } else { v = x; }
> +  if (v != 6.0f)
> +    abort ();
> +  v = 32.0f;
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6.0f)
> +    abort ();
> +  v = 147.0f;
> +  #pragma omp atomic capture compare
> +  if (x == 6.0f) { x = 57.0f; } else { v = x; }
> +  if (v != 147.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 57.0f)
> +    abort ();
> +  #pragma omp atomic update, capture, compare, weak, seq_cst, fail
> (relaxed)
> +  { r = x == 137.0f; if (r) { x = 174.0f; } }
> +  if (r)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 57.0f)
> +    abort ();
> +  #pragma omp atomic compare capture fail (relaxed)
> +  { r = x == 57.0f; if (r) { x = 6.0f; } }
> +  if (r != 1)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6.0f)
> +    abort ();
> +  v = -5.0f;
> +  #pragma omp atomic capture compare
> +  { r = x == 17.0L; if (r) { x = 25.0; } else { v = x; } }
> +  if (r || v != 6.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 6.0f)
> +    abort ();
> +  v = 15.0f;
> +  #pragma omp atomic capture compare
> +  { r = x == 6.0f; if (r) { x = 23.0f; } else { v = x; } }
> +  if (r != 1 || v != 15.0f)
> +    abort ();
> +  #pragma omp atomic read
> +  v = x;
> +  if (v != 23.0f)
> +    abort ();
> +  return 0;
> +}
> --- libgomp/testsuite/libgomp.c-c++-common/atomic-21.c.jj       2021-09-10
> 14:27:19.051805671 +0200
> +++ libgomp/testsuite/libgomp.c-c++-common/atomic-21.c  2021-09-10
> 15:05:47.316510552 +0200
> @@ -0,0 +1,49 @@
> +// { dg-do run { target c } }
> +
> +double d;
> +long double ld;
> +
> +int
> +main ()
> +{
> +  double e = __builtin_copysign (0.0, -1.0), v;
> +  long double le = __builtin_copysignl (0.0L, -1.0L), lv;
> +  if (__builtin_memcmp (&d, &e, sizeof (d)) != 0)
> +    {
> +      /* Verify == comparison for atomics is done as with memcmp.  */
> +      #pragma omp atomic compare
> +      d = d == e ? 5.0 : d;
> +      #pragma omp atomic read
> +      v = d;
> +      if (v != 0.0)
> +       __builtin_abort ();
> +      #pragma omp atomic compare
> +      d = d == 0.0 ? 5.0 : d;
> +      #pragma omp atomic read
> +      v = d;
> +      if (v != 5.0)
> +       __builtin_abort ();
> +    }
> +  if (__builtin_memcmp (&ld, &le, sizeof (ld)) != 0)
> +    {
> +      __builtin_memset (&ld, 0xff, sizeof (ld));
> +      #pragma omp atomic write
> +      ld = 0.0L;
> +      __asm volatile ("" : : "g" (&ld) : "memory");
> +      /* Verify == comparison for atomics is done as with memcmp
> +        with __builtin_clear_padding if needed.  */
> +      #pragma omp atomic compare
> +      ld = ld == le ? 5.0L : ld;
> +      #pragma omp atomic read
> +      lv = ld;
> +      if (lv != 0.0L)
> +       __builtin_abort ();
> +      #pragma omp atomic compare
> +      ld = ld == 0.0L ? 5.0L : ld;
> +      #pragma omp atomic read
> +      lv = ld;
> +      if (lv != 5.0L)
> +       __builtin_abort ();
> +    }
> +  return 0;
> +}
>
>         Jakub
>
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: openmp: Implement OpenMP 5.1 atomics, so far for C only
  2021-09-13 11:57 ` Christophe Lyon
@ 2021-09-13 14:40   ` Jakub Jelinek
  2021-09-13 14:53     ` Christophe Lyon
  0 siblings, 1 reply; 4+ messages in thread
From: Jakub Jelinek @ 2021-09-13 14:40 UTC (permalink / raw)
  To: Christophe Lyon; +Cc: GCC Patches, Tobias Burnus

On Mon, Sep 13, 2021 at 01:57:52PM +0200, Christophe Lyon wrote:
> > --- gcc/testsuite/c-c++-common/gomp/atomic-29.c.jj      2021-09-10
> > 11:47:17.093164041 +0200
> > +++ gcc/testsuite/c-c++-common/gomp/atomic-29.c 2021-09-10
> > 11:52:33.428722747 +0200
> > @@ -0,0 +1,43 @@
> > +/* { dg-do compile { target c } } */
> > +/* { dg-additional-options "-O2 -fdump-tree-ompexp" } */
> > +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> > \\\(\[^\n\r]*, 8, 5, 5\\\);" 1 "ompexp" { target sync_int_long } } } */
> > +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> > \\\(\[^\n\r]*, 8, 4, 2\\\);" 1 "ompexp" { target sync_int_long } } } */
> > +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> > \\\(\[^\n\r]*, 264, 5, 0\\\);" 1 "ompexp" { target sync_int_long } } } */
> > +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> > \\\(\[^\n\r]*, 8, 0, 0\\\);" 1 "ompexp" { target sync_int_long } } } */
> > +/* { dg-final { scan-tree-dump-not "__atomic_load_8 \\\(" "ompexp" {
> > target sync_int_long } } } */
> > +
> >
> 
> This test fails on arm*linux when forcing old CPU/ARCH (eg  -march=armv5t).
> Not sure how easy it is to fix?
> sync_int_long returns true for arm*-*-linux-*,
> but for other arm targets, it depends on the result
> of check_effective_target_arm_acq_rel.
> 
> Is it just a matter of removing arm*-*-linux-* from sync_int_long and
> always rely on arm_acq_rel?

So, atomic-28.c passes and just atomic-29.c fails?
atomic-29.c tests for double atomics aka 64-bit, so probably I should use
sync_long_long effective target instead of sync_int_long.
But, it is unclear how would that help for arm because sync_long_long is
enabled for all arm targets...
There is also sync_long_long_runtime effective target, but this is a compile
test, so it would be weird to rely on that when it ought to test the runtime
behavior of that.

	Jakub


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: openmp: Implement OpenMP 5.1 atomics, so far for C only
  2021-09-13 14:40   ` Jakub Jelinek
@ 2021-09-13 14:53     ` Christophe Lyon
  0 siblings, 0 replies; 4+ messages in thread
From: Christophe Lyon @ 2021-09-13 14:53 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches, Tobias Burnus

On Mon, Sep 13, 2021 at 4:40 PM Jakub Jelinek <jakub@redhat.com> wrote:

> On Mon, Sep 13, 2021 at 01:57:52PM +0200, Christophe Lyon wrote:
> > > --- gcc/testsuite/c-c++-common/gomp/atomic-29.c.jj      2021-09-10
> > > 11:47:17.093164041 +0200
> > > +++ gcc/testsuite/c-c++-common/gomp/atomic-29.c 2021-09-10
> > > 11:52:33.428722747 +0200
> > > @@ -0,0 +1,43 @@
> > > +/* { dg-do compile { target c } } */
> > > +/* { dg-additional-options "-O2 -fdump-tree-ompexp" } */
> > > +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> > > \\\(\[^\n\r]*, 8, 5, 5\\\);" 1 "ompexp" { target sync_int_long } } } */
> > > +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> > > \\\(\[^\n\r]*, 8, 4, 2\\\);" 1 "ompexp" { target sync_int_long } } } */
> > > +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> > > \\\(\[^\n\r]*, 264, 5, 0\\\);" 1 "ompexp" { target sync_int_long } } }
> */
> > > +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE
> > > \\\(\[^\n\r]*, 8, 0, 0\\\);" 1 "ompexp" { target sync_int_long } } } */
> > > +/* { dg-final { scan-tree-dump-not "__atomic_load_8 \\\(" "ompexp" {
> > > target sync_int_long } } } */
> > > +
> > >
> >
> > This test fails on arm*linux when forcing old CPU/ARCH (eg
> -march=armv5t).
> > Not sure how easy it is to fix?
> > sync_int_long returns true for arm*-*-linux-*,
> > but for other arm targets, it depends on the result
> > of check_effective_target_arm_acq_rel.
> >
> > Is it just a matter of removing arm*-*-linux-* from sync_int_long and
> > always rely on arm_acq_rel?
>
> So, atomic-28.c passes and just atomic-29.c fails?
>
Yes.


> atomic-29.c tests for double atomics aka 64-bit, so probably I should use
> sync_long_long effective target instead of sync_int_long.
> But, it is unclear how would that help for arm because sync_long_long is
> enabled for all arm targets...
>
Indeed :-(


> There is also sync_long_long_runtime effective target, but this is a
> compile
> test, so it would be weird to rely on that when it ought to test the
> runtime
> behavior of that.
>
Agreed



>
>         Jakub
>
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-09-13 14:53 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-10 18:46 openmp: Implement OpenMP 5.1 atomics, so far for C only Jakub Jelinek
2021-09-13 11:57 ` Christophe Lyon
2021-09-13 14:40   ` Jakub Jelinek
2021-09-13 14:53     ` Christophe Lyon

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).