Patchwork [gomp4] C++ OpenMP 4.0 atomics support

login
register
mail settings
Submitter Jakub Jelinek
Date March 15, 2013, 4:27 p.m.
Message ID <20130315162738.GN12913@tucnak.redhat.com>
Download mbox | patch
Permalink /patch/228087/
State New
Headers show

Comments

Jakub Jelinek - March 15, 2013, 4:27 p.m.
Hi!

As the updated comments show, OpenMP 4.0 (rc2 so far) has added a bunch of
new #pragma omp {,update,capture} forms.  Here is C++ support for that,
depending on http://gcc.gnu.org/ml/gcc-patches/2013-03/msg00546.html
Queued for gomp-4_0-branch (to be created next week).  Comments?

I'm afraid C support will be harder, given the lack of tentative parsing
support and no counterpart to cp_tree_equal (operand_equal_p won't do it,
that one returns false on side-effects etc.).

2013-03-15  Jakub Jelinek  <jakub@redhat.com>

	* c-parser.c (c_parser_omp_atomic): Adjust comment.
	Add another argument to c_finish_omp_atomic.

	* parser.c (cp_parser_binary_expression): Handle no_toplevel_fold_p
	even for binary operations other than comparison.
	(cp_parser_omp_atomic): Handle parsing OpenMP 4.0 atomics.
	* pt.c (tsubst_expr) <case OMP_ATOMIC>: Handle atomic exchange.
	* semantics.c (finish_omp_atomic): Use cp_tree_equal to diagnose
	expression mismatches and to find out if c_finish_omp_atomic
	should be called with swapped set to true or false.

	* c-omp.c (c_finish_omp_atomic): Add swapped argument, if true,
	build the operation first with rhs, lhs arguments and use NOP_EXPR
	build_modify_expr.
	* c-common.h (c_finish_omp_atomic): Adjust prototype.

	* c-c++-common/gomp/atomic-15.c: Remove error test that is now
	valid in OpenMP 4.0.

	* testsuite/libgomp.c++/atomic-10.C: New test.
	* testsuite/libgomp.c++/atomic-11.C: New test.
	* testsuite/libgomp.c++/atomic-12.C: New test.
	* testsuite/libgomp.c++/atomic-13.C: New test.


	Jakub
Toon Moene - March 15, 2013, 6:35 p.m.
On 03/15/2013 05:27 PM, Jakub Jelinek wrote:

> Queued for gomp-4_0-branch (to be created next week).  Comments?

I heard from colleagues on the Fortran Standardization Committee 
(http://j3-fortran.org) that 4.0 doubled in size w.r.t. the 3.x standard.

I wish you lots of success implementing this - it is really hard to get 
a cross-language standard like this one correct, let alone its 
implementation.

The reports I receive on the OpenMP implementation in GCC (from the 
gfortran users' side) are without exception positive.

Thanks !

Patch

--- gcc/c/c-parser.c.jj	2013-02-13 09:29:17.000000000 +0100
+++ gcc/c/c-parser.c	2013-03-15 14:40:29.145039411 +0100
@@ -9500,10 +9500,18 @@  c_parser_omp_structured_block (c_parser
    update-stmt:
      expression-stmt | x = x binop expr
    capture-stmt:
-     v = x binop= expr | v = x++ | v = ++x | v = x-- | v = --x
+     v = expression-stmt
    capture-block:
      { v = x; update-stmt; } | { update-stmt; v = x; }
 
+   OpenMP 4.0:
+   update-stmt:
+     expression-stmt | x = x binop expr | x = expr binop x
+   capture-stmt:
+     v = update-stmt
+   capture-block:
+     { v = x; update-stmt; } | { update-stmt; v = x; } | { v = x; x = expr; }
+
   where x and v are lvalue expressions with scalar type.
 
   LOC is the location of the #pragma token.  */
@@ -9826,7 +9834,8 @@  stmt_done:
       c_parser_require (parser, CPP_CLOSE_BRACE, "expected %<}%>");
     }
 done:
-  stmt = c_finish_omp_atomic (loc, code, opcode, lhs, rhs, v, lhs1, rhs1);
+  stmt = c_finish_omp_atomic (loc, code, opcode, lhs, rhs, v, lhs1, rhs1,
+			      false);
   if (stmt != error_mark_node)
     add_stmt (stmt);
 
--- gcc/cp/parser.c.jj	2013-03-07 15:32:40.000000000 +0100
+++ gcc/cp/parser.c	2013-03-15 14:20:15.361064904 +0100
@@ -7471,9 +7471,11 @@  cp_parser_binary_expression (cp_parser*
       */
       if (no_toplevel_fold_p
 	  && lookahead_prec <= current.prec
-	  && sp == stack
-	  && TREE_CODE_CLASS (current.tree_type) == tcc_comparison)
-	current.lhs = build2 (current.tree_type, boolean_type_node,
+	  && sp == stack)
+	current.lhs = build2 (current.tree_type,
+			      TREE_CODE_CLASS (current.tree_type)
+			      == tcc_comparison
+			      ? boolean_type_node : TREE_TYPE (current.lhs),
 			      current.lhs, rhs);
       else
 	current.lhs = build_x_binary_op (current.loc, current.tree_type,
@@ -26426,10 +26428,18 @@  cp_parser_omp_structured_block (cp_parse
    update-stmt:
      expression-stmt | x = x binop expr
    capture-stmt:
-     v = x binop= expr | v = x++ | v = ++x | v = x-- | v = --x
+     v = expression-stmt
    capture-block:
      { v = x; update-stmt; } | { update-stmt; v = x; }
 
+   OpenMP 4.0:
+   update-stmt:
+     expression-stmt | x = x binop expr | x = expr binop x
+   capture-stmt:
+     v = update-stmt
+   capture-block:
+     { v = x; update-stmt; } | { update-stmt; v = x; } | { v = x; x = expr; }
+
   where x and v are lvalue expressions with scalar type.  */
 
 static void
@@ -26596,75 +26606,139 @@  restart:
 	  opcode = BIT_XOR_EXPR;
 	  break;
 	case CPP_EQ:
-	  if (structured_block || code == OMP_ATOMIC)
+	  enum cp_parser_prec oprec;
+	  cp_token *token;
+	  cp_lexer_consume_token (parser->lexer);
+	  cp_parser_parse_tentatively (parser);
+	  rhs1 = cp_parser_simple_cast_expression (parser);
+	  if (rhs1 == error_mark_node)
 	    {
-	      enum cp_parser_prec oprec;
-	      cp_token *token;
-	      cp_lexer_consume_token (parser->lexer);
-	      rhs1 = cp_parser_unary_expression (parser, /*address_p=*/false,
-						 /*cast_p=*/false, NULL);
-	      if (rhs1 == error_mark_node)
-		goto saw_error;
-	      token = cp_lexer_peek_token (parser->lexer);
-	      switch (token->type)
+	      cp_parser_abort_tentative_parse (parser);
+	      cp_parser_simple_cast_expression (parser);
+	      goto saw_error;
+	    }
+	  token = cp_lexer_peek_token (parser->lexer);
+	  if (token->type != CPP_SEMICOLON && !cp_tree_equal (lhs, rhs1))
+	    {
+	      cp_parser_abort_tentative_parse (parser);
+	      cp_parser_parse_tentatively (parser);
+	      rhs = cp_parser_binary_expression (parser, false, true,
+						 PREC_NOT_OPERATOR, NULL);
+	      if (rhs == error_mark_node)
+		{
+		  cp_parser_abort_tentative_parse (parser);
+		  cp_parser_binary_expression (parser, false, true,
+					       PREC_NOT_OPERATOR, NULL);
+		  goto saw_error;
+		}
+	      switch (TREE_CODE (rhs))
 		{
-		case CPP_SEMICOLON:
-		  if (code == OMP_ATOMIC_CAPTURE_NEW)
+		case MULT_EXPR:
+		case TRUNC_DIV_EXPR:
+		case PLUS_EXPR:
+		case MINUS_EXPR:
+		case LSHIFT_EXPR:
+		case RSHIFT_EXPR:
+		case BIT_AND_EXPR:
+		case BIT_IOR_EXPR:
+		case BIT_XOR_EXPR:
+		  if (cp_tree_equal (lhs, TREE_OPERAND (rhs, 1)))
 		    {
-		      code = OMP_ATOMIC_CAPTURE_OLD;
-		      v = lhs;
-		      lhs = NULL_TREE;
-		      lhs1 = rhs1;
-		      rhs1 = NULL_TREE;
-		      cp_lexer_consume_token (parser->lexer);
-		      goto restart;
+		      if (cp_parser_parse_definitely (parser))
+			{
+			  opcode = TREE_CODE (rhs);
+			  rhs1 = TREE_OPERAND (rhs, 0);
+			  rhs = TREE_OPERAND (rhs, 1);
+			  goto stmt_done;
+			}
+		      else
+			goto saw_error;
 		    }
-		  cp_parser_error (parser,
-				   "invalid form of %<#pragma omp atomic%>");
-		  goto saw_error;
-		case CPP_MULT:
-		  opcode = MULT_EXPR;
-		  break;
-		case CPP_DIV:
-		  opcode = TRUNC_DIV_EXPR;
-		  break;
-		case CPP_PLUS:
-		  opcode = PLUS_EXPR;
-		  break;
-		case CPP_MINUS:
-		  opcode = MINUS_EXPR;
-		  break;
-		case CPP_LSHIFT:
-		  opcode = LSHIFT_EXPR;
-		  break;
-		case CPP_RSHIFT:
-		  opcode = RSHIFT_EXPR;
-		  break;
-		case CPP_AND:
-		  opcode = BIT_AND_EXPR;
-		  break;
-		case CPP_OR:
-		  opcode = BIT_IOR_EXPR;
-		  break;
-		case CPP_XOR:
-		  opcode = BIT_XOR_EXPR;
 		  break;
 		default:
-		  cp_parser_error (parser,
-				   "invalid operator for %<#pragma omp atomic%>");
-		  goto saw_error;
+		  break;
 		}
-	      oprec = TOKEN_PRECEDENCE (token);
-	      gcc_assert (oprec != PREC_NOT_OPERATOR);
-	      if (commutative_tree_code (opcode))
-		oprec = (enum cp_parser_prec) (oprec - 1);
-	      cp_lexer_consume_token (parser->lexer);
-	      rhs = cp_parser_binary_expression (parser, false, false,
-						 oprec, NULL);
-	      if (rhs == error_mark_node)
-		goto saw_error;
-	      goto stmt_done;
+	      cp_parser_abort_tentative_parse (parser);
+	      if (structured_block && code == OMP_ATOMIC_CAPTURE_OLD)
+		{
+		  rhs = cp_parser_expression (parser, /*cast_p=*/false, NULL);
+		  if (rhs == error_mark_node)
+		    goto saw_error;
+		  opcode = NOP_EXPR;
+		  rhs1 = NULL_TREE;
+		  goto stmt_done;
+		}
+	      cp_parser_error (parser,
+			       "invalid form of %<#pragma omp atomic%>");
+	      goto saw_error;
+	    }
+	  if (!cp_parser_parse_definitely (parser))
+	    goto saw_error;
+	  switch (token->type)
+	    {
+	    case CPP_SEMICOLON:
+	      if (code == OMP_ATOMIC_CAPTURE_NEW)
+		{
+		  code = OMP_ATOMIC_CAPTURE_OLD;
+		  v = lhs;
+		  lhs = NULL_TREE;
+		  lhs1 = rhs1;
+		  rhs1 = NULL_TREE;
+		  cp_lexer_consume_token (parser->lexer);
+		  goto restart;
+		}
+	      else if (structured_block)
+		{
+		  opcode = NOP_EXPR;
+		  rhs = rhs1;
+		  rhs1 = NULL_TREE;
+		  goto stmt_done;
+		}
+	      cp_parser_error (parser,
+			       "invalid form of %<#pragma omp atomic%>");
+	      goto saw_error;
+	    case CPP_MULT:
+	      opcode = MULT_EXPR;
+	      break;
+	    case CPP_DIV:
+	      opcode = TRUNC_DIV_EXPR;
+	      break;
+	    case CPP_PLUS:
+	      opcode = PLUS_EXPR;
+	      break;
+	    case CPP_MINUS:
+	      opcode = MINUS_EXPR;
+	      break;
+	    case CPP_LSHIFT:
+	      opcode = LSHIFT_EXPR;
+	      break;
+	    case CPP_RSHIFT:
+	      opcode = RSHIFT_EXPR;
+	      break;
+	    case CPP_AND:
+	      opcode = BIT_AND_EXPR;
+	      break;
+	    case CPP_OR:
+	      opcode = BIT_IOR_EXPR;
+	      break;
+	    case CPP_XOR:
+	      opcode = BIT_XOR_EXPR;
+	      break;
+	    default:
+	      cp_parser_error (parser,
+			       "invalid operator for %<#pragma omp atomic%>");
+	      goto saw_error;
 	    }
+	  oprec = TOKEN_PRECEDENCE (token);
+	  gcc_assert (oprec != PREC_NOT_OPERATOR);
+	  if (commutative_tree_code (opcode))
+	    oprec = (enum cp_parser_prec) (oprec - 1);
+	  cp_lexer_consume_token (parser->lexer);
+	  rhs = cp_parser_binary_expression (parser, false, false,
+					     oprec, NULL);
+	  if (rhs == error_mark_node)
+	    goto saw_error;
+	  goto stmt_done;
 	  /* FALLTHROUGH */
 	default:
 	  cp_parser_error (parser,
--- gcc/cp/pt.c.jj	2013-02-27 22:32:13.000000000 +0100
+++ gcc/cp/pt.c	2013-03-15 16:53:03.047114106 +0100
@@ -13268,6 +13268,8 @@  tsubst_expr (tree t, tree args, tsubst_f
 	      lhs = RECUR (TREE_OPERAND (op11, 0));
 	      rhs = RECUR (TREE_OPERAND (op11, 1));
 	      opcode = TREE_CODE (op11);
+	      if (opcode == MODIFY_EXPR)
+		opcode = NOP_EXPR;
 	    }
 	  else
 	    {
--- gcc/cp/semantics.c.jj	2013-03-12 09:59:36.000000000 +0100
+++ gcc/cp/semantics.c	2013-03-15 15:13:18.410956888 +0100
@@ -5009,8 +5009,36 @@  finish_omp_atomic (enum tree_code code,
     }
   if (!dependent_p)
     {
+      bool swapped = false;
+      if (rhs1 && cp_tree_equal (lhs, rhs))
+	{
+	  tree tem = rhs;
+	  rhs = rhs1;
+	  rhs1 = tem;
+	  swapped = !commutative_tree_code (opcode);
+	}
+      if (rhs1 && !cp_tree_equal (lhs, rhs1))
+	{
+	  if (code == OMP_ATOMIC)
+	    error ("%<#pragma omp atomic update%> uses two different "
+		   "expressions for memory");
+	  else
+	    error ("%<#pragma omp atomic capture%> uses two different "
+		   "expressions for memory");
+	  return;
+	}
+      if (lhs1 && !cp_tree_equal (lhs, lhs1))
+	{
+	  if (code == OMP_ATOMIC)
+	    error ("%<#pragma omp atomic update%> uses two different "
+		   "expressions for memory");
+	  else
+	    error ("%<#pragma omp atomic capture%> uses two different "
+		   "expressions for memory");
+	  return;
+	}
       stmt = c_finish_omp_atomic (input_location, code, opcode, lhs, rhs,
-				  v, lhs1, rhs1);
+				  v, lhs1, rhs1, swapped);
       if (stmt == error_mark_node)
 	return;
     }
--- gcc/c-family/c-omp.c.jj	2013-02-13 17:05:52.000000000 +0100
+++ gcc/c-family/c-omp.c	2013-03-15 15:27:13.938149171 +0100
@@ -122,7 +122,7 @@  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)
+		     tree v, tree lhs1, tree rhs1, bool swapped)
 {
   tree x, type, addr;
 
@@ -176,8 +176,12 @@  c_finish_omp_atomic (location_t loc, enu
   /* There are lots of warnings, errors, and conversions that need to happen
      in the course of interpreting a statement.  Use the normal mechanisms
      to do this, and then take it apart again.  */
-  x = build_modify_expr (input_location, lhs, NULL_TREE, opcode,
-      			 input_location, rhs, NULL_TREE);
+  if (swapped)
+    {
+      rhs = build2_loc (loc, opcode, TREE_TYPE (lhs), rhs, lhs);
+      opcode = NOP_EXPR;
+    }
+  x = build_modify_expr (loc, lhs, NULL_TREE, opcode, loc, rhs, NULL_TREE);
   if (x == error_mark_node)
     return error_mark_node;
   gcc_assert (TREE_CODE (x) == MODIFY_EXPR);
--- gcc/c-family/c-common.h.jj	2013-02-13 23:48:14.000000000 +0100
+++ gcc/c-family/c-common.h	2013-03-15 14:55:47.394049396 +0100
@@ -1035,7 +1035,7 @@  extern tree c_finish_omp_critical (locat
 extern tree c_finish_omp_ordered (location_t, 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);
+				 tree, tree, tree, tree, tree, bool);
 extern void c_finish_omp_flush (location_t);
 extern void c_finish_omp_taskwait (location_t);
 extern void c_finish_omp_taskyield (location_t);
--- gcc/testsuite/c-c++-common/gomp/atomic-15.c.jj	2011-11-07 12:40:45.000000000 +0100
+++ gcc/testsuite/c-c++-common/gomp/atomic-15.c	2013-03-15 11:35:26.030192060 +0100
@@ -20,8 +20,6 @@  main ()
   #pragma omp atomic
     x = x / 7 / 2;	/* { dg-error "expected" } */
   #pragma omp atomic capture
-    v = x = x | 6;	/* { dg-error "invalid operator" } */
-  #pragma omp atomic capture
     { v = x; x = x * 7 + 6; }	/* { dg-error "expected" } */
   #pragma omp atomic capture
     { v = x; x = x * 7 ^ 6; }	/* { dg-error "expected" } */
--- libgomp/testsuite/libgomp.c++/atomic-10.C.jj	2013-03-15 12:33:27.115827431 +0100
+++ libgomp/testsuite/libgomp.c++/atomic-10.C	2013-03-15 15:58:52.751175890 +0100
@@ -0,0 +1,99 @@ 
+// { dg-do run }
+
+extern "C" void abort (void);
+int x = 6;
+
+int
+main ()
+{
+  int v, l = 2, s = 1;
+  #pragma omp atomic
+    x = -3 + x;
+  #pragma omp atomic read
+    v = x;
+  if (v != 3)
+    abort ();
+  #pragma omp atomic update
+    x = 3 * 2 * 1 + x;
+  #pragma omp atomic read
+    v = x;
+  if (v != 9)
+    abort ();
+  #pragma omp atomic capture
+    v = x = x | 16;
+  if (v != 25)
+    abort ();
+  #pragma omp atomic capture
+    v = x = x + 14 * 2 / 4;
+  if (v != 32)
+    abort ();
+  #pragma omp atomic capture
+    v = x = 5 | x;
+  if (v != 37)
+    abort ();
+  #pragma omp atomic capture
+    v = x = 40 + 12 - 2 - 7 - x;
+  if (v != 6)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 6)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = 3 + x; }
+  if (v != 6)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = -1 * -1 * -1 * -1 - x; }
+  if (v != 9)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != -8)
+    abort ();
+  #pragma omp atomic capture
+    { x = 2 * 2 - x; v = x; }
+  if (v != 12)
+    abort ();
+  #pragma omp atomic capture
+    { x = 7 & x; v = x; }
+  if (v != 4)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = 6; }
+  if (v != 4)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 6)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = 7 * 8 + 23; }
+  if (v != 6)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 79)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = 23 + 6 * 4; }
+  if (v != 79)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 47)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = l ? 17 : 12; }
+  if (v != 47)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = l = s++ + 3; }
+  if (v != 17 || l != 4 || s != 2)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 4)
+    abort ();
+  return 0;
+}
--- libgomp/testsuite/libgomp.c++/atomic-11.C.jj	2013-03-15 15:57:41.557587843 +0100
+++ libgomp/testsuite/libgomp.c++/atomic-11.C	2013-03-15 15:59:51.916833343 +0100
@@ -0,0 +1,108 @@ 
+// { dg-do run }
+
+extern "C" void abort (void);
+
+template <typename T>
+void
+foo ()
+{
+  extern T x;
+  T v, l = 2, s = 1;
+  #pragma omp atomic
+    x = -3 + x;
+  #pragma omp atomic read
+    v = x;
+  if (v != 3)
+    abort ();
+  #pragma omp atomic update
+    x = 3 * 2 * 1 + x;
+  #pragma omp atomic read
+    v = x;
+  if (v != 9)
+    abort ();
+  #pragma omp atomic capture
+    v = x = x | 16;
+  if (v != 25)
+    abort ();
+  #pragma omp atomic capture
+    v = x = x + 14 * 2 / 4;
+  if (v != 32)
+    abort ();
+  #pragma omp atomic capture
+    v = x = 5 | x;
+  if (v != 37)
+    abort ();
+  #pragma omp atomic capture
+    v = x = 40 + 12 - 2 - 7 - x;
+  if (v != 6)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 6)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = 3 + x; }
+  if (v != 6)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = -1 * -1 * -1 * -1 - x; }
+  if (v != 9)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != -8)
+    abort ();
+  #pragma omp atomic capture
+    { x = 2 * 2 - x; v = x; }
+  if (v != 12)
+    abort ();
+  #pragma omp atomic capture
+    { x = 7 & x; v = x; }
+  if (v != 4)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = 6; }
+  if (v != 4)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 6)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = 7 * 8 + 23; }
+  if (v != 6)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 79)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = 23 + 6 * 4; }
+  if (v != 79)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 47)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = l ? 17 : 12; }
+  if (v != 47)
+    abort ();
+  #pragma omp atomic capture
+    { v = x; x = l = s++ + 3; }
+  if (v != 17 || l != 4 || s != 2)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 4)
+    abort ();
+}
+
+int x = 6;
+
+int
+main ()
+{
+  foo <int> ();
+  return 0;
+}
--- libgomp/testsuite/libgomp.c++/atomic-12.C.jj	2013-03-15 16:03:22.572614293 +0100
+++ libgomp/testsuite/libgomp.c++/atomic-12.C	2013-03-15 16:30:56.801464851 +0100
@@ -0,0 +1,58 @@ 
+// { dg-do run }
+
+extern "C" void abort ();
+int x = 6, cnt;
+
+int
+foo ()
+{
+  return cnt++;
+}
+
+int
+main ()
+{
+  int v, *p;
+  p = &x;
+  #pragma omp atomic update
+    p[foo (), 0] = 16 + 6 - p[foo (), 0];
+  #pragma omp atomic read
+    v = x;
+  if (cnt != 2 || v != 16)
+    abort ();
+  #pragma omp atomic capture
+    v = p[foo () + foo (), 0] = p[foo () + foo (), 0] + 3;
+  if (cnt != 6 || v != 19)
+    abort ();
+  #pragma omp atomic capture
+    v = p[foo (), 0] = 12 * 1 / 2 + (foo (), 0) + p[foo (), 0];
+  if (cnt != 9 || v != 25)
+    abort ();
+  #pragma omp atomic capture
+    {
+      v = p[foo () & 0]; p[foo () & 0] = (foo (), 1) * 9 - p[foo () & 0];
+    }
+  if (cnt != 13 || v != 25)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != -16)
+    abort ();
+  #pragma omp atomic capture
+    {
+      p[0 & foo ()] = 16 - 2 + 3 + p[0 & foo ()]; v = p[0 & foo ()];
+    }
+  if (cnt != 16 || v != 1)
+    abort ();
+  #pragma omp atomic capture
+    {
+      v = p[foo (), 0]; p[foo (), 0] = (foo (), 7) ? 13 : foo () + 6;
+    }
+  if (cnt != 19 || v != 1)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 13)
+    abort ();
+  return 0;
+}
--- libgomp/testsuite/libgomp.c++/atomic-13.C.jj	2013-03-15 16:31:29.522285808 +0100
+++ libgomp/testsuite/libgomp.c++/atomic-13.C	2013-03-15 16:50:41.906915812 +0100
@@ -0,0 +1,68 @@ 
+// { dg-do run }
+
+extern "C" void abort ();
+int cnt;
+
+int
+foo ()
+{
+  return cnt++;
+}
+
+template <typename T>
+void
+bar ()
+{
+  extern T x;
+  T v, *p;
+  p = &x;
+  #pragma omp atomic update
+    p[foo (), 0] = 16 + 6 - p[foo (), 0];
+  #pragma omp atomic read
+    v = x;
+  if (cnt != 2 || v != 16)
+    abort ();
+  #pragma omp atomic capture
+    v = p[foo () + foo (), 0] = p[foo () + foo (), 0] + 3;
+  if (cnt != 6 || v != 19)
+    abort ();
+  #pragma omp atomic capture
+    v = p[foo (), 0] = 12 * 1 / 2 + (foo (), 0) + p[foo (), 0];
+  if (cnt != 9 || v != 25)
+    abort ();
+  #pragma omp atomic capture
+    {
+      v = p[foo () & 0]; p[foo () & 0] = (foo (), 1) * 9 - p[foo () & 0];
+    }
+  if (cnt != 13 || v != 25)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != -16)
+    abort ();
+  #pragma omp atomic capture
+    {
+      p[0 & foo ()] = 16 - 2 + 3 + p[0 & foo ()]; v = p[0 & foo ()];
+    }
+  if (cnt != 16 || v != 1)
+    abort ();
+  #pragma omp atomic capture
+    {
+      v = p[foo (), 0]; p[foo (), 0] = (foo (), 7) ? 13 : foo () + 6;
+    }
+  if (cnt != 19 || v != 1)
+    abort ();
+  #pragma omp atomic read
+    v = x;
+  if (v != 13)
+    abort ();
+}
+
+int x = 6;
+
+int
+main ()
+{
+  bar <int> ();
+  return 0;
+}