Patchwork [gomp4] C++ OpenMP user defined reductions

login
register
mail settings
Submitter Jakub Jelinek
Date Aug. 27, 2013, 4:57 p.m.
Message ID <20130827165706.GK21876@tucnak.zalov.cz>
Download mbox | patch
Permalink /patch/270157/
State New
Headers show

Comments

Jakub Jelinek - Aug. 27, 2013, 4:57 p.m.
Hi!

This is an attempt to implement OpenMP 4.0 user defined reductions.
I got initial 5 questions/issues with the standard answered, and have
another 13 questions/issues pending, this patch implements roughly what I
think should be the resolution, worst case we'll need to tweak it
afterwards.  The only thing known to me left out from the implementation
is the handling of derived classes, if no UDR is found for the derived
class, the spec says:
"If the type is a derived class, then any reduction-identifier that matches
its base classes are also a match, if there is no specific match for the
type."
which I can understand how it would work without initializer clause on the
UDR or with initializer (fn_call (args)) form, but can't see how it would
work with initializer (omp_priv initializer) clause.
And, I haven't added tests for accessibility yet.

Jason, does this look ok for the branch?

2013-08-27  Jakub Jelinek  <jakub@redhat.com>

gcc/
	* tree.h (OMP_CLAUSE_REDUCTION_OMP_ORIG_REF): Define.
	* omp-low.c (lower_rec_input_clauses): Don't force max_vf = 1
	if OMP_CLAUSE_REDUCTION_PLACEHOLDER.  Add barrier also if any
	OMP_CLAUSE_REDUCTION_OMP_ORIG_REF is seen.  For OMP_CLAUSE_PRIVATE
	in simd, fix last argument to omp_clause_default_ctor langhook.
	Handle OMP_CLAUSE_REDUCTION_PLACEHOLDER in simd loops, if
	OMP_CLAUSE_REDUCTION_GIMPLE_INIT is NULL, emit omp_clause_default_ctor
	if any and emit omp_clause_dtor if any.
	(lower_reduction_clauses): Adjust comment for UDRs.
	(lower_omp_taskreg): Emit reduction merges before destructors.
	* tree-pretty-print.c (dump_omp_clause): Don't emit any reduction
	operator name if OMP_CLAUSE_REDUCTION_CODE is ERROR_MARK.
gcc/cp/
	* cp-tree.h (lang_decl_fn): Add omp_declare_reduction_p bitfield.
	(DECL_OMP_DECLARE_REDUCTION_P): Define.
	(omp_reduction_id, cp_remove_omp_priv_cleanup_stmt,
	cp_check_omp_declare_reduction): New prototypes.
	(cxx_omp_create_clause_info): Add another bool argument.
	* decl.c (decls_match): For DECL_OMP_DECLARE_REDUCTION_P decls,
	ignore template and context mismatches.
	(duplicate_decls): Error out for redeclaration of UDRs.
	* parser.c (cp_parser_late_parsing_for_member): Handle UDRs.
	(cp_parser_omp_clause_reduction): Handle UDRs.
	(cp_parser_omp_declare_reduction_exprs,
	cp_parser_omp_declare_reduction): New functions.
	(cp_parser_omp_declare): Uncomment parsing of UDRs.
	* pt.c (instantiate_class_template_1): Call
	cp_check_omp_declare_reduction on UDRs.
	(tsubst_decl): Diagnose UDRs on reference types.
	(tsubst_omp_clauses): Subst OMP_CLAUSE_REDUCTION_PLACEHOLDER
	if needed.
	(tsubst_expr): Handle UDRs.
	(tsubst_omp_udr): New function.
	(instantiate_decl): Handle UDRs.
	* cp-gimplify.c (cxx_omp_finish_clause): Adjust
	cxx_omp_create_clause_info caller.
	* semantics.c (cxx_omp_create_clause_info): Add need_dtor argument.
	Use it instead of need_default_ctor || need_copy_ctor for dtor
	info setup.
	(omp_reduction_id, cp_remove_omp_priv_cleanup_stmt,
	cp_check_omp_declare_reduction_r, cp_check_omp_declare_reduction,
	clone_omp_udr, find_omp_placeholder_r): New functions.
	(struct cp_check_omp_declare_reduction_data): New type.
	(finish_omp_clauses): Adjust cxx_omp_create_clause_info caller.
	Handle UDRs.
gcc/testsuite/
	* g++.dg/gomp/clause-3.C: Adjust error messages.
	* g++.dg/gomp/udr-1.C: New test.
	* g++.dg/gomp/udr-2.C: New test.
	* g++.dg/gomp/udr-3.C: New test.
libgomp/
	* testsuite/libgomp.c++/simd-4.C: New test.
	* testsuite/libgomp.c++/simd-5.C: New test.
	* testsuite/libgomp.c++/udr-1.C: New test.
	* testsuite/libgomp.c++/udr-2.C: New test.
	* testsuite/libgomp.c++/udr-3.C: New test.
	* testsuite/libgomp.c++/udr-4.C: New test.


	Jakub

Patch

--- gcc/tree.h.jj	2013-08-19 12:07:51.000000000 +0200
+++ gcc/tree.h	2013-08-22 17:05:00.521252761 +0200
@@ -629,6 +629,9 @@  struct GTY(()) tree_base {
        OMP_CLAUSE_LINEAR_NO_COPYIN in
 	   OMP_CLAUSE_LINEAR
 
+       OMP_CLAUSE_REDUCTION_OMP_ORIG_REF in
+	   OMP_CLAUSE_REDUCTION
+
        TRANSACTION_EXPR_RELAXED in
 	   TRANSACTION_EXPR
 
@@ -1968,6 +1971,11 @@  extern void protected_set_expr_location
 #define OMP_CLAUSE_REDUCTION_PLACEHOLDER(NODE) \
   OMP_CLAUSE_OPERAND (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_REDUCTION), 3)
 
+/* True if a REDUCTION clause may reference the original list item (omp_orig)
+   in its OMP_CLAUSE_REDUCTION_{,GIMPLE_}INIT.  */
+#define OMP_CLAUSE_REDUCTION_OMP_ORIG_REF(NODE) \
+  (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_REDUCTION)->base.public_flag)
+
 /* True if a LINEAR clause doesn't need copy in.  True for iterator vars which
    are always initialized inside of the loop construct, false otherwise.  */
 #define OMP_CLAUSE_LINEAR_NO_COPYIN(NODE) \
--- gcc/omp-low.c.jj	2013-08-19 12:07:51.000000000 +0200
+++ gcc/omp-low.c	2013-08-27 15:43:45.604757848 +0200
@@ -2679,6 +2679,7 @@  lower_rec_input_clauses (tree clauses, g
   tree c, dtor, copyin_seq, x, ptr;
   bool copyin_by_ref = false;
   bool lastprivate_firstprivate = false;
+  bool reduction_omp_orig_ref = false;
   int pass;
   bool is_simd = (gimple_code (ctx->stmt) == GIMPLE_OMP_FOR
 		  && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD);
@@ -2697,9 +2698,6 @@  lower_rec_input_clauses (tree clauses, g
       switch (OMP_CLAUSE_CODE (c))
 	{
 	case OMP_CLAUSE_REDUCTION:
-	  if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c))
-	    max_vf = 1;
-	  /* FALLTHRU */
 	case OMP_CLAUSE_PRIVATE:
 	case OMP_CLAUSE_FIRSTPRIVATE:
 	case OMP_CLAUSE_LASTPRIVATE:
@@ -2738,9 +2736,12 @@  lower_rec_input_clauses (tree clauses, g
 		}
 	    case OMP_CLAUSE_FIRSTPRIVATE:
 	    case OMP_CLAUSE_COPYIN:
-	    case OMP_CLAUSE_REDUCTION:
 	    case OMP_CLAUSE_LINEAR:
 	      break;
+	    case OMP_CLAUSE_REDUCTION:
+	      if (OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c))
+		reduction_omp_orig_ref = true;
+	      break;
 	    case OMP_CLAUSE__LOOPTEMP_:
 	      /* Handle _looptemp_ clauses only on parallel.  */
 	      if (fd)
@@ -2927,19 +2928,20 @@  lower_rec_input_clauses (tree clauses, g
 	      else
 		x = NULL;
 	    do_private:
-	      x = lang_hooks.decls.omp_clause_default_ctor (c, new_var, x);
+	      tree nx;
+	      nx = lang_hooks.decls.omp_clause_default_ctor (c, new_var, x);
 	      if (is_simd)
 		{
 		  tree y = lang_hooks.decls.omp_clause_dtor (c, new_var);
-		  if ((TREE_ADDRESSABLE (new_var) || x || y
+		  if ((TREE_ADDRESSABLE (new_var) || nx || y
 		       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE)
 		      && lower_rec_simd_input_clauses (new_var, ctx, max_vf,
 						       idx, lane, ivar, lvar))
 		    {
-		      if (x)
+		      if (nx)
 			x = lang_hooks.decls.omp_clause_default_ctor
 						(c, unshare_expr (ivar), x);
-		      if (x)
+		      if (nx && x)
 			gimplify_and_add (x, &llist[0]);
 		      if (y)
 			{
@@ -2956,8 +2958,8 @@  lower_rec_input_clauses (tree clauses, g
 		      break;
 		    }
 		}
-	      if (x)
-		gimplify_and_add (x, ilist);
+	      if (nx)
+		gimplify_and_add (nx, ilist);
 	      /* FALLTHRU */
 
 	    do_dtor:
@@ -3104,19 +3106,73 @@  lower_rec_input_clauses (tree clauses, g
 	      if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c))
 		{
 		  tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c);
+		  gimple tseq;
 		  x = build_outer_var_ref (var, ctx);
 
-		  /* FIXME: Not handled yet.  */
-		  gcc_assert (!is_simd);
 		  if (is_reference (var))
 		    x = build_fold_addr_expr_loc (clause_loc, x);
 		  SET_DECL_VALUE_EXPR (placeholder, x);
 		  DECL_HAS_VALUE_EXPR_P (placeholder) = 1;
-		  lower_omp (&OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c), ctx);
-		  gimple_seq_add_seq (ilist,
-				      OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c));
+		  if (is_simd
+		      && lower_rec_simd_input_clauses (new_var, ctx, max_vf,
+						       idx, lane, ivar, lvar))
+		    {
+		      gcc_assert (DECL_VALUE_EXPR (new_var) == lvar);
+		      SET_DECL_VALUE_EXPR (new_var, ivar);
+		      if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) == NULL)
+			{
+			  x = lang_hooks.decls.omp_clause_default_ctor
+					(c, unshare_expr (ivar),
+					 build_outer_var_ref (var, ctx));
+			  if (x)
+			    gimplify_and_add (x, &llist[0]);
+			}
+		      else
+			{
+			  tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c);
+			  lower_omp (&tseq, ctx);
+			  gimple_seq_add_seq (&llist[0], tseq);
+			}
+		      OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) = NULL;
+		      tseq = OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c);
+		      lower_omp (&tseq, ctx);
+		      gimple_seq_add_seq (&llist[1], tseq);
+		      OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL;
+		      DECL_HAS_VALUE_EXPR_P (placeholder) = 0;
+		      SET_DECL_VALUE_EXPR (new_var, lvar);
+		      x = lang_hooks.decls.omp_clause_dtor (c, ivar);
+		      if (x)
+			{
+			  tseq = NULL;
+			  dtor = x;
+			  gimplify_stmt (&dtor, &tseq);
+			  gimple_seq_add_seq (&llist[1], tseq);
+			}
+		      break;
+		    }
+		  if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) == NULL)
+		    {
+		      x = lang_hooks.decls.omp_clause_default_ctor (c, new_var,
+								    x);
+		      if (x)
+			gimplify_and_add (x, ilist);
+		    }
+		  else
+		    {
+		      tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c);
+		      lower_omp (&tseq, ctx);
+		      gimple_seq_add_seq (ilist, tseq);
+		    }
 		  OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) = NULL;
+		  if (is_simd)
+		    {
+		      tseq = OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c);
+		      lower_omp (&tseq, ctx);
+		      gimple_seq_add_seq (dlist, tseq);
+		      OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL;
+		    }
 		  DECL_HAS_VALUE_EXPR_P (placeholder) = 0;
+		  goto do_dtor;
 		}
 	      else
 		{
@@ -3216,8 +3272,9 @@  lower_rec_input_clauses (tree clauses, g
      master thread doesn't modify it before it is copied over in all
      threads.  Similarly for variables in both firstprivate and
      lastprivate clauses we need to ensure the lastprivate copying
-     happens after firstprivate copying in all threads.  */
-  if (copyin_by_ref || lastprivate_firstprivate)
+     happens after firstprivate copying in all threads.  And similarly
+     for UDRs if initializer expression refers to omp_orig.  */
+  if (copyin_by_ref || lastprivate_firstprivate || reduction_omp_orig_ref)
     {
       /* Don't add any barrier for #pragma omp simd or
 	 #pragma omp distribute.  */
@@ -3406,7 +3463,7 @@  lower_reduction_clauses (tree clauses, g
       {
 	if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c))
 	  {
-	    /* Never use OMP_ATOMIC for array reductions.  */
+	    /* Never use OMP_ATOMIC for array reductions or UDRs.  */
 	    count = -1;
 	    break;
 	  }
@@ -8503,7 +8560,7 @@  lower_omp_taskreg (gimple_stmt_iterator
   tree child_fn, t;
   gimple stmt = gsi_stmt (*gsi_p);
   gimple par_bind, bind;
-  gimple_seq par_body, olist, ilist, par_olist, par_ilist, new_body;
+  gimple_seq par_body, olist, ilist, par_olist, par_rlist, par_ilist, new_body;
   struct gimplify_ctx gctx;
   location_t loc = gimple_location (stmt);
 
@@ -8531,10 +8588,11 @@  lower_omp_taskreg (gimple_stmt_iterator
 
   par_olist = NULL;
   par_ilist = NULL;
+  par_rlist = NULL;
   lower_rec_input_clauses (clauses, &par_ilist, &par_olist, ctx, NULL);
   lower_omp (&par_body, ctx);
   if (gimple_code (stmt) == GIMPLE_OMP_PARALLEL)
-    lower_reduction_clauses (clauses, &par_olist, ctx);
+    lower_reduction_clauses (clauses, &par_rlist, ctx);
 
   /* Declare all the variables created by mapping and the variables
      declared in the scope of the parallel body.  */
@@ -8572,6 +8630,7 @@  lower_omp_taskreg (gimple_stmt_iterator
 
   gimple_seq_add_seq (&new_body, par_ilist);
   gimple_seq_add_seq (&new_body, par_body);
+  gimple_seq_add_seq (&new_body, par_rlist);
   gimple_seq_add_seq (&new_body, par_olist);
   new_body = maybe_catch_exception (new_body);
   if (ctx->cancellable)
--- gcc/tree-pretty-print.c.jj	2013-06-28 17:56:45.000000000 +0200
+++ gcc/tree-pretty-print.c	2013-08-21 13:09:50.802069023 +0200
@@ -330,8 +330,12 @@  dump_omp_clause (pretty_printer *buffer,
 
     case OMP_CLAUSE_REDUCTION:
       pp_string (buffer, "reduction(");
-      pp_string (buffer, op_symbol_code (OMP_CLAUSE_REDUCTION_CODE (clause)));
-      pp_character (buffer, ':');
+      if (OMP_CLAUSE_REDUCTION_CODE (clause) != ERROR_MARK)
+	{
+	  pp_string (buffer,
+		     op_symbol_code (OMP_CLAUSE_REDUCTION_CODE (clause)));
+	  pp_character (buffer, ':');
+	}
       dump_generic_node (buffer, OMP_CLAUSE_DECL (clause),
 			 spc, flags, false);
       pp_character (buffer, ')');
--- gcc/cp/cp-tree.h.jj	2013-08-19 12:07:50.295655045 +0200
+++ gcc/cp/cp-tree.h	2013-08-26 17:17:41.584359486 +0200
@@ -1982,7 +1982,8 @@  struct GTY(()) lang_decl_fn {
   unsigned thunk_p : 1;
   unsigned this_thunk_p : 1;
   unsigned hidden_friend_p : 1;
-  /* 1 spare bit.  */
+  unsigned omp_declare_reduction_p : 1;
+  /* No spare bits on 32-bit hosts, 32 on 64-bit hosts.  */
 
   /* For a non-thunk function decl, this is a tree list of
      friendly classes. For a thunk function decl, it is the
@@ -3187,6 +3188,11 @@  more_aggr_init_expr_args_p (const aggr_i
 #define DECL_HIDDEN_FRIEND_P(NODE) \
   (LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->hidden_friend_p)
 
+/* Nonzero if NODE is an artificial FUNCTION_DECL for
+   #pragma omp declare reduction.  */
+#define DECL_OMP_DECLARE_REDUCTION_P(NODE) \
+  (LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->omp_declare_reduction_p)
+
 /* Nonzero if DECL has been declared threadprivate by
    #pragma omp threadprivate.  */
 #define CP_DECL_THREADPRIVATE_P(DECL) \
@@ -5773,6 +5779,9 @@  extern tree finish_qualified_id_expr		(t
 extern void simplify_aggr_init_expr		(tree *);
 extern void finalize_nrv			(tree *, tree, tree);
 extern void note_decl_for_pch			(tree);
+extern tree omp_reduction_id			(enum tree_code, tree);
+extern tree cp_remove_omp_priv_cleanup_stmt	(tree *, int *, void *);
+extern void cp_check_omp_declare_reduction	(tree);
 extern tree finish_omp_clauses			(tree);
 extern void finish_omp_threadprivate		(tree);
 extern tree begin_omp_structured_block		(void);
@@ -5797,7 +5806,8 @@  extern void finish_omp_cancellation_poin
 extern tree begin_transaction_stmt		(location_t, tree *, int);
 extern void finish_transaction_stmt		(tree, tree, int, tree);
 extern tree build_transaction_expr		(location_t, tree, int, tree);
-extern bool cxx_omp_create_clause_info		(tree, tree, bool, bool, bool);
+extern bool cxx_omp_create_clause_info		(tree, tree, bool, bool,
+						 bool, bool);
 extern tree baselink_for_fns                    (tree);
 extern void finish_static_assert                (tree, tree, location_t,
                                                  bool);
--- gcc/cp/decl.c.jj	2013-08-19 12:07:50.000000000 +0200
+++ gcc/cp/decl.c	2013-08-27 18:06:40.257290042 +0200
@@ -976,12 +976,15 @@  decls_match (tree newdecl, tree olddecl)
       tree t2 = (DECL_USE_TEMPLATE (olddecl)
 		 ? DECL_TI_TEMPLATE (olddecl)
 		 : NULL_TREE);
-      if (t1 != t2)
+      if (t1 != t2 && !DECL_OMP_DECLARE_REDUCTION_P (newdecl))
 	return 0;
 
       if (CP_DECL_CONTEXT (newdecl) != CP_DECL_CONTEXT (olddecl)
 	  && ! (DECL_EXTERN_C_P (newdecl)
-		&& DECL_EXTERN_C_P (olddecl)))
+		&& DECL_EXTERN_C_P (olddecl))
+	  && ! (DECL_OMP_DECLARE_REDUCTION_P (newdecl)
+		&& DECL_CONTEXT (newdecl) == NULL_TREE
+		&& DECL_CONTEXT (olddecl) == current_function_decl))
 	return 0;
 
       /* A new declaration doesn't match a built-in one unless it
@@ -1416,6 +1419,15 @@  duplicate_decls (tree newdecl, tree oldd
 	  type = cp_build_type_attribute_variant (type, attribs);
 	  TREE_TYPE (newdecl) = TREE_TYPE (olddecl) = type;
 	}
+      else if (DECL_OMP_DECLARE_REDUCTION_P (olddecl))
+	{
+	  gcc_assert (DECL_OMP_DECLARE_REDUCTION_P (newdecl));
+	  error_at (DECL_SOURCE_LOCATION (newdecl),
+		    "redeclaration of %<pragma omp declare reduction%>");
+	  error_at (DECL_SOURCE_LOCATION (olddecl),
+		    "previous %<pragma omp declare reduction%> declaration");
+	  return error_mark_node;
+	}
 
       /* If a function is explicitly declared "throw ()", propagate that to
 	 the corresponding builtin.  */
--- gcc/cp/parser.c.jj	2013-08-19 12:07:50.267645778 +0200
+++ gcc/cp/parser.c	2013-08-27 17:53:52.701447995 +0200
@@ -231,6 +231,9 @@  static void cp_parser_initial_pragma
 static tree cp_literal_operator_id
   (const char *);
 
+static bool cp_parser_omp_declare_reduction_exprs
+  (tree, cp_parser *);
+
 /* Manifest constants.  */
 #define CP_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (cp_token))
 #define CP_SAVED_TOKEN_STACK 5
@@ -23083,9 +23086,18 @@  cp_parser_late_parsing_for_member (cp_pa
       if (processing_template_decl)
 	push_deferring_access_checks (dk_no_check);
 
-      /* Now, parse the body of the function.  */
-      cp_parser_function_definition_after_declarator (parser,
-						      /*inline_p=*/true);
+      /* #pragma omp declare reduction needs special parsing.  */
+      if (DECL_OMP_DECLARE_REDUCTION_P (member_function))
+	{
+	  parser->lexer->in_pragma = true;
+	  cp_parser_omp_declare_reduction_exprs (member_function, parser);
+	  finish_function (0);
+	  cp_check_omp_declare_reduction (member_function);
+	}
+      else
+	/* Now, parse the body of the function.  */
+	cp_parser_function_definition_after_declarator (parser,
+							/*inline_p=*/true);
 
       if (processing_template_decl)
 	pop_deferring_access_checks ();
@@ -26952,70 +26964,89 @@  cp_parser_omp_clause_ordered (cp_parser
    OpenMP 3.1:
 
    reduction-operator:
-     One of: + * - & ^ | && || min max  */
+     One of: + * - & ^ | && || min max
+
+   OpenMP 4.0:
+
+   reduction-operator:
+     One of: + * - & ^ | && ||
+     id-expression  */
 
 static tree
 cp_parser_omp_clause_reduction (cp_parser *parser, tree list)
 {
-  enum tree_code code;
-  tree nlist, c;
+  enum tree_code code = ERROR_MARK;
+  tree nlist, c, id = NULL_TREE;
 
   if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
     return list;
 
   switch (cp_lexer_peek_token (parser->lexer)->type)
     {
-    case CPP_PLUS:
-      code = PLUS_EXPR;
-      break;
-    case CPP_MULT:
-      code = MULT_EXPR;
-      break;
-    case CPP_MINUS:
-      code = MINUS_EXPR;
-      break;
-    case CPP_AND:
-      code = BIT_AND_EXPR;
-      break;
-    case CPP_XOR:
-      code = BIT_XOR_EXPR;
-      break;
-    case CPP_OR:
-      code = BIT_IOR_EXPR;
-      break;
-    case CPP_AND_AND:
-      code = TRUTH_ANDIF_EXPR;
-      break;
-    case CPP_OR_OR:
-      code = TRUTH_ORIF_EXPR;
-      break;
-    case CPP_NAME:
-      {
-	tree id = cp_lexer_peek_token (parser->lexer)->u.value;
-	const char *p = IDENTIFIER_POINTER (id);
+    case CPP_PLUS: code = PLUS_EXPR; break;
+    case CPP_MULT: code = MULT_EXPR; break;
+    case CPP_MINUS: code = MINUS_EXPR; break;
+    case CPP_AND: code = BIT_AND_EXPR; break;
+    case CPP_XOR: code = BIT_XOR_EXPR; break;
+    case CPP_OR: code = BIT_IOR_EXPR; break;
+    case CPP_AND_AND: code = TRUTH_ANDIF_EXPR; break;
+    case CPP_OR_OR: code = TRUTH_ORIF_EXPR; break;
+    default: break;
+    }
 
-	if (strcmp (p, "min") == 0)
-	  {
+  if (code != ERROR_MARK)
+    cp_lexer_consume_token (parser->lexer);
+  else
+    {
+      bool saved_colon_corrects_to_scope_p;
+      location_t loc;
+      saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
+      parser->colon_corrects_to_scope_p = false;
+      loc = cp_lexer_peek_token (parser->lexer)->location;
+      id = cp_parser_id_expression (parser, /*template_p=*/false,
+				    /*check_dependency_p=*/true,
+				    /*template_p=*/NULL,
+				    /*declarator_p=*/false,
+				    /*optional_p=*/false);
+      parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
+      if (identifier_p (id))
+	{
+	  const char *p = IDENTIFIER_POINTER (id);
+
+	  if (strcmp (p, "min") == 0)
 	    code = MIN_EXPR;
-	    break;
-	  }
-	if (strcmp (p, "max") == 0)
-	  {
+	  else if (strcmp (p, "max") == 0)
 	    code = MAX_EXPR;
-	    break;
-	  }
-      }
-      /* FALLTHROUGH */
-    default:
-      cp_parser_error (parser, "expected %<+%>, %<*%>, %<-%>, %<&%>, %<^%>, "
-			       "%<|%>, %<&&%>, %<||%>, %<min%> or %<max%>");
-    resync_fail:
-      cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
-					     /*or_comma=*/false,
-					     /*consume_paren=*/true);
-      return list;
+	  else if (id == ansi_opname (PLUS_EXPR))
+	    code = PLUS_EXPR;
+	  else if (id == ansi_opname (MULT_EXPR))
+	    code = MULT_EXPR;
+	  else if (id == ansi_opname (MINUS_EXPR))
+	    code = MINUS_EXPR;
+	  else if (id == ansi_opname (BIT_AND_EXPR))
+	    code = BIT_AND_EXPR;
+	  else if (id == ansi_opname (BIT_IOR_EXPR))
+	    code = BIT_IOR_EXPR;
+	  else if (id == ansi_opname (BIT_XOR_EXPR))
+	    code = BIT_XOR_EXPR;
+	  else if (id == ansi_opname (TRUTH_ANDIF_EXPR))
+	    code = TRUTH_ANDIF_EXPR;
+	  else if (id == ansi_opname (TRUTH_ORIF_EXPR))
+	    code = TRUTH_ORIF_EXPR;
+	  id = omp_reduction_id (code, id);
+	  tree scope = parser->scope;
+	  if (scope)
+	    id = cp_parser_lookup_name_simple (parser, id, loc);
+	}
+      else
+	{
+	 resync_fail:
+	  cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
+						 /*or_comma=*/false,
+						 /*consume_paren=*/true);
+	  return list;
+	}
     }
-  cp_lexer_consume_token (parser->lexer);
 
   if (!cp_parser_require (parser, CPP_COLON, RT_COLON))
     goto resync_fail;
@@ -27023,7 +27054,10 @@  cp_parser_omp_clause_reduction (cp_parse
   nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_REDUCTION, list,
 					  NULL);
   for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c))
-    OMP_CLAUSE_REDUCTION_CODE (c) = code;
+    {
+      OMP_CLAUSE_REDUCTION_CODE (c) = code;
+      OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = id;
+    }
 
   return nlist;
 }
@@ -29839,10 +29873,361 @@  cp_parser_omp_end_declare_target (cp_par
     current_omp_declare_target_attribute--;
 }
 
+/* Helper function of cp_parser_omp_declare_reduction.  Parse the combiner
+   expression and optional initializer clause of
+   #pragma omp declare reduction.  */
+
+static bool
+cp_parser_omp_declare_reduction_exprs (tree fndecl, cp_parser *parser)
+{
+  tree type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
+  gcc_assert (TREE_CODE (type) == REFERENCE_TYPE);
+  type = TREE_TYPE (type);
+  tree omp_out = build_lang_decl (VAR_DECL, get_identifier ("omp_out"), type);
+  DECL_ARTIFICIAL (omp_out) = 1;
+  pushdecl (omp_out);
+  add_decl_expr (omp_out);
+  tree omp_in = build_lang_decl (VAR_DECL, get_identifier ("omp_in"), type);
+  DECL_ARTIFICIAL (omp_in) = 1;
+  pushdecl (omp_in);
+  add_decl_expr (omp_in);
+  tree combiner;
+  tree omp_priv = NULL_TREE, omp_orig = NULL_TREE, initializer = NULL_TREE;
+
+  keep_next_level (true);
+  tree block = begin_omp_structured_block ();
+  combiner = cp_parser_expression (parser, false, NULL);
+  finish_expr_stmt (combiner);
+  block = finish_omp_structured_block (block);
+  add_stmt (block);
+
+  if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+    return false;
+
+  const char *p = "";
+  if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+    {
+      tree id = cp_lexer_peek_token (parser->lexer)->u.value;
+      p = IDENTIFIER_POINTER (id);
+    }
+
+  if (strcmp (p, "initializer") == 0)
+    {
+      cp_lexer_consume_token (parser->lexer);
+      if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+	return false;
+
+      p = "";
+      if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+	{
+	  tree id = cp_lexer_peek_token (parser->lexer)->u.value;
+	  p = IDENTIFIER_POINTER (id);
+	}
+
+      omp_priv = build_lang_decl (VAR_DECL, get_identifier ("omp_priv"), type);
+      DECL_ARTIFICIAL (omp_priv) = 1;
+      pushdecl (omp_priv);
+      add_decl_expr (omp_priv);
+      omp_orig = build_lang_decl (VAR_DECL, get_identifier ("omp_orig"), type);
+      DECL_ARTIFICIAL (omp_orig) = 1;
+      pushdecl (omp_orig);
+      add_decl_expr (omp_orig);
+
+      keep_next_level (true);
+      block = begin_omp_structured_block ();
+
+      if (strcmp (p, "omp_priv") == 0)
+	{
+	  bool is_direct_init, is_non_constant_init;
+	  cp_lexer_consume_token (parser->lexer);
+	  initializer = cp_parser_initializer (parser, &is_direct_init,
+					       &is_non_constant_init);
+	  cp_finish_decl (omp_priv, initializer, !is_non_constant_init,
+			  NULL_TREE, LOOKUP_ONLYCONVERTING);
+	  initializer = NULL_TREE;
+	}
+      else
+	{
+	  cp_finish_decl (omp_priv, NULL_TREE, false, NULL_TREE,
+			  LOOKUP_ONLYCONVERTING);
+	  cp_parser_parse_tentatively (parser);
+	  tree fn_name = cp_parser_id_expression (parser, /*template_p=*/false,
+						  /*check_dependency_p=*/true,
+						  /*template_p=*/NULL,
+						  /*declarator_p=*/false,
+						  /*optional_p=*/false);
+	  vec<tree, va_gc> *args;
+	  if (fn_name == error_mark_node
+	      || cp_parser_error_occurred (parser)
+	      || !cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
+	      || ((args = cp_parser_parenthesized_expression_list
+				(parser, non_attr, /*cast_p=*/false,
+				 /*allow_expansion_p=*/true,
+				 /*non_constant_p=*/NULL)),
+		  cp_parser_error_occurred (parser)))
+	    {
+	      finish_omp_structured_block (block);
+	      cp_parser_abort_tentative_parse (parser);
+	      cp_parser_error (parser, "expected id-expression (arguments)");
+	      return false;
+	    }
+	  unsigned int i;
+	  tree arg;
+	  FOR_EACH_VEC_SAFE_ELT (args, i, arg)
+	    if (arg == omp_priv
+		|| (TREE_CODE (arg) == ADDR_EXPR
+		    && TREE_OPERAND (arg, 0) == omp_priv))
+	      break;
+	  cp_parser_abort_tentative_parse (parser);
+	  if (arg == NULL_TREE)
+	    error ("one of the initializer call arguments should be %<omp_priv%>"
+		   " or %<&omp_priv%>");
+	  initializer = cp_parser_postfix_expression (parser, false, false, false,
+						      false, NULL);
+	  finish_expr_stmt (initializer);
+	}
+
+      block = finish_omp_structured_block (block);
+      cp_walk_tree (&block, cp_remove_omp_priv_cleanup_stmt, omp_priv, NULL);
+      finish_expr_stmt (block);
+
+      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+	return false;
+    }
+
+  if (!cp_lexer_next_token_is (parser->lexer, CPP_PRAGMA_EOL))
+    cp_parser_required_error (parser, RT_PRAGMA_EOL, /*keyword=*/false);
+
+  return true;
+}
+
+/* OpenMP 4.0
+   #pragma omp declare reduction (reduction-id : typename-list : expression) \
+      initializer-clause[opt] new-line
+
+   initializer-clause:
+      initializer (omp_priv initializer)
+      initializer (function-name (argument-list))  */
+
+static void
+cp_parser_omp_declare_reduction (cp_parser *parser, cp_token *pragma_tok,
+				 enum pragma_context)
+{
+  vec<tree> types = vNULL;
+  enum tree_code reduc_code = ERROR_MARK;
+  tree reduc_id = NULL_TREE, orig_reduc_id = NULL_TREE, type;
+  unsigned int i;
+  cp_token *first_token;
+  cp_token_cache *cp;
+  int errs;
+
+  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+    goto fail;
+
+  switch (cp_lexer_peek_token (parser->lexer)->type)
+    {
+    case CPP_PLUS:
+      reduc_code = PLUS_EXPR;
+      break;
+    case CPP_MULT:
+      reduc_code = MULT_EXPR;
+      break;
+    case CPP_MINUS:
+      reduc_code = MINUS_EXPR;
+      break;
+    case CPP_AND:
+      reduc_code = BIT_AND_EXPR;
+      break;
+    case CPP_XOR:
+      reduc_code = BIT_XOR_EXPR;
+      break;
+    case CPP_OR:
+      reduc_code = BIT_IOR_EXPR;
+      break;
+    case CPP_AND_AND:
+      reduc_code = TRUTH_ANDIF_EXPR;
+      break;
+    case CPP_OR_OR:
+      reduc_code = TRUTH_ORIF_EXPR;
+      break;
+    case CPP_NAME:
+      reduc_id = orig_reduc_id = cp_parser_identifier (parser);
+      break;
+    default:
+      cp_parser_error (parser, "expected %<+%>, %<*%>, %<-%>, %<&%>, %<^%>, "
+			       "%<|%>, %<&&%>, %<||%> or identifier");
+      goto fail;
+    }
+
+  if (reduc_code != ERROR_MARK)
+    cp_lexer_consume_token (parser->lexer);
+
+  reduc_id = omp_reduction_id (reduc_code, reduc_id);
+  if (reduc_id == error_mark_node)
+    goto fail;
+
+  if (!cp_parser_require (parser, CPP_COLON, RT_COLON))
+    goto fail;
+
+  /* Types may not be defined in declare reduction type list.  */
+  const char *saved_message;
+  saved_message = parser->type_definition_forbidden_message;
+  parser->type_definition_forbidden_message
+    = G_("types may not be defined in declare reduction type list");
+  bool saved_colon_corrects_to_scope_p;
+  saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
+  parser->colon_corrects_to_scope_p = false;
+
+  while (true)
+    {
+      location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+      type = cp_parser_type_id (parser);
+      if (type == error_mark_node)
+	;
+      else if (ARITHMETIC_TYPE_P (type)
+	       && (orig_reduc_id == NULL_TREE
+		   || (TREE_CODE (type) != COMPLEX_TYPE
+		       && (strcmp (IDENTIFIER_POINTER (orig_reduc_id),
+				   "min") == 0
+			   || strcmp (IDENTIFIER_POINTER (orig_reduc_id),
+				      "max") == 0))))
+	error_at (loc, "predeclared arithmetic type in "
+		       "%<#pragma omp declare reduction%>");
+      else if (TREE_CODE (type) == FUNCTION_TYPE
+	       || TREE_CODE (type) == METHOD_TYPE
+	       || TREE_CODE (type) == ARRAY_TYPE
+	       || TREE_CODE (type) == REFERENCE_TYPE)
+	error_at (loc, "function, array or reference type in "
+		       "%<#pragma omp declare reduction%>");
+      else if (TYPE_QUALS_NO_ADDR_SPACE (type))
+	error_at (loc, "const, volatile or __restrict qualified type in "
+		       "%<#pragma omp declare reduction%>");
+      else
+	types.safe_push (type);
+
+      if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	cp_lexer_consume_token (parser->lexer);
+      else
+	break;
+    }
+
+  /* Restore the saved message.  */
+  parser->type_definition_forbidden_message = saved_message;
+  parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
+
+  if (!cp_parser_require (parser, CPP_COLON, RT_COLON)
+      || types.is_empty ())
+    {
+     fail:
+      cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+      types.release ();
+      return;
+    }      
+
+  first_token = cp_lexer_peek_token (parser->lexer);
+  cp = NULL;
+  errs = errorcount;
+  FOR_EACH_VEC_ELT (types, i, type)
+    {
+      tree fntype
+	= build_function_type_list (void_type_node,
+				    cp_build_reference_type (type, false),
+				    NULL_TREE);
+      tree fndecl = build_lang_decl (FUNCTION_DECL, reduc_id, fntype);
+      DECL_SOURCE_LOCATION (fndecl) = pragma_tok->location;
+      DECL_ARTIFICIAL (fndecl) = 1;
+      DECL_EXTERNAL (fndecl) = 1;
+      DECL_DECLARED_INLINE_P (fndecl) = 1;
+      DECL_IGNORED_P (fndecl) = 1;
+      DECL_OMP_DECLARE_REDUCTION_P (fndecl) = 1;
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (get_identifier ("gnu_inline"), NULL_TREE,
+		     DECL_ATTRIBUTES (fndecl));
+      if (processing_template_decl)
+	fndecl = push_template_decl (fndecl);
+      bool block_scope = false;
+      tree block = NULL_TREE;
+      if (current_function_decl)
+	{
+	  block_scope = true;
+	  if (!processing_template_decl)
+	    pushdecl (fndecl);
+	}
+      else if (current_class_type)
+	{
+	  if (cp == NULL)
+	    {
+	      while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)
+		     && cp_lexer_next_token_is_not (parser->lexer, CPP_EOF))
+		cp_lexer_consume_token (parser->lexer);
+	      if (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL))
+		goto fail;
+	      cp = cp_token_cache_new (first_token,
+				       cp_lexer_peek_nth_token (parser->lexer,
+								2));
+	    }
+	  DECL_STATIC_FUNCTION_P (fndecl) = 1;
+	  finish_member_declaration (fndecl);
+	  DECL_PENDING_INLINE_INFO (fndecl) = cp;
+	  DECL_PENDING_INLINE_P (fndecl) = 1;
+	  vec_safe_push (unparsed_funs_with_definitions, fndecl);
+	  continue;
+	}
+      else
+	{
+	  DECL_CONTEXT (fndecl) = current_namespace;
+	  pushdecl (fndecl);
+	}
+      if (!block_scope)
+	start_preparsed_function (fndecl, NULL_TREE, SF_PRE_PARSED);
+      else
+	block = begin_omp_structured_block ();
+      if (cp)
+	{
+	  cp_parser_push_lexer_for_tokens (parser, cp);
+	  parser->lexer->in_pragma = true;
+	}
+      if (!cp_parser_omp_declare_reduction_exprs (fndecl, parser))
+	{
+	  if (!block_scope)
+	    finish_function (0);
+	  else
+	    DECL_CONTEXT (fndecl) = current_function_decl;
+	  if (cp)
+	    cp_parser_pop_lexer (parser);
+	  goto fail;
+	}
+      if (cp)
+	cp_parser_pop_lexer (parser);
+      if (!block_scope)
+	finish_function (0);
+      else
+	{
+	  DECL_CONTEXT (fndecl) = current_function_decl;
+	  block = finish_omp_structured_block (block);
+	  if (TREE_CODE (block) == BIND_EXPR)
+	    DECL_SAVED_TREE (fndecl) = BIND_EXPR_BODY (block);
+	  else if (TREE_CODE (block) == STATEMENT_LIST)
+	    DECL_SAVED_TREE (fndecl) = block;
+	  if (processing_template_decl)
+	    add_decl_expr (fndecl);
+	}
+      cp_check_omp_declare_reduction (fndecl);
+      if (cp == NULL && types.length () > 1)
+	cp = cp_token_cache_new (first_token,
+				 cp_lexer_peek_nth_token (parser->lexer, 2));
+      if (errs != errorcount)
+	break;
+    }
+
+  cp_parser_require_pragma_eol (parser, pragma_tok);
+  types.release ();
+}
+
 /* OpenMP 4.0
    #pragma omp declare simd declare-simd-clauses[optseq] new-line
    #pragma omp declare reduction (reduction-id : typename-list : expression) \
-      identity-clause[opt] new-line
+      initializer-clause[opt] new-line
    #pragma omp declare target new-line  */
 
 static void
@@ -29862,13 +30247,13 @@  cp_parser_omp_declare (cp_parser *parser
 	  return;
 	}
       cp_ensure_no_omp_declare_simd (parser);
-/*    if (strcmp (p, "reduction") == 0)
+      if (strcmp (p, "reduction") == 0)
 	{
 	  cp_lexer_consume_token (parser->lexer);
 	  cp_parser_omp_declare_reduction (parser, pragma_tok,
 					   context);
 	  return;
-	}  */
+	}
       if (strcmp (p, "target") == 0)
 	{
 	  cp_lexer_consume_token (parser->lexer);
--- gcc/cp/pt.c.jj	2013-08-19 12:07:50.000000000 +0200
+++ gcc/cp/pt.c	2013-08-27 10:09:12.188133311 +0200
@@ -8826,6 +8826,9 @@  instantiate_class_template_1 (tree type)
 	      /* Instantiate members marked with attribute used.  */
 	      if (r != error_mark_node && DECL_PRESERVE_P (r))
 		mark_used (r);
+	      if (TREE_CODE (r) == FUNCTION_DECL
+		  && DECL_OMP_DECLARE_REDUCTION_P (r))
+		cp_check_omp_declare_reduction (r);
 	    }
 	  else
 	    {
@@ -10256,6 +10259,21 @@  tsubst_decl (tree t, tree args, tsubst_f
 	if (excessive_deduction_depth)
 	  RETURN (error_mark_node);
 
+	/* OpenMP UDRs have the only argument a reference to the declared
+	   type.  We want to diagnose if the declared type is a reference,
+	   which is invalid, but as references to references are usually
+	   quietly merged, diagnose it here.  */
+        if (DECL_OMP_DECLARE_REDUCTION_P (t))
+	  {
+	    tree argtype
+	      = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (t))));
+	    argtype = tsubst (argtype, args, complain, in_decl);
+	    if (TREE_CODE (argtype) == REFERENCE_TYPE)
+	      error_at (DECL_SOURCE_LOCATION (t),
+			"function, array or reference type in "
+			"%<#pragma omp declare reduction%>");
+	  }
+
 	/* We do NOT check for matching decls pushed separately at this
 	   point, as they may not represent instantiations of this
 	   template, and in any case are considered separate under the
@@ -12663,7 +12681,6 @@  tsubst_omp_clauses (tree clauses, bool d
 	case OMP_CLAUSE_PRIVATE:
 	case OMP_CLAUSE_SHARED:
 	case OMP_CLAUSE_FIRSTPRIVATE:
-	case OMP_CLAUSE_REDUCTION:
 	case OMP_CLAUSE_COPYIN:
 	case OMP_CLAUSE_COPYPRIVATE:
 	case OMP_CLAUSE_IF:
@@ -12686,6 +12703,19 @@  tsubst_omp_clauses (tree clauses, bool d
 	    = tsubst_expr (OMP_CLAUSE_OPERAND (oc, 0), args, complain, 
 			   in_decl, /*integral_constant_expression_p=*/false);
 	  break;
+	case OMP_CLAUSE_REDUCTION:
+	  if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc))
+	    {
+	      if (!identifier_p (OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc)))
+		OMP_CLAUSE_REDUCTION_PLACEHOLDER (nc)
+		  = tsubst_expr (OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc), args,
+				 complain, in_decl,
+				 /*integral_constant_expression_p=*/false);
+	    }
+	  OMP_CLAUSE_OPERAND (nc, 0)
+	    = tsubst_expr (OMP_CLAUSE_OPERAND (oc, 0), args, complain, 
+			   in_decl, /*integral_constant_expression_p=*/false);
+	  break;
 	case OMP_CLAUSE_LINEAR:
 	case OMP_CLAUSE_ALIGNED:
 	  OMP_CLAUSE_OPERAND (nc, 0)
@@ -13016,6 +13046,17 @@  tsubst_expr (tree t, tree args, tsubst_f
 		  }
 		else if (DECL_IMPLICIT_TYPEDEF_P (t))
 		  /* We already did a pushtag.  */;
+		else if (TREE_CODE (decl) == FUNCTION_DECL
+			 && DECL_OMP_DECLARE_REDUCTION_P (decl)
+			 && DECL_CONTEXT (pattern_decl)
+			 && TREE_CODE (DECL_CONTEXT (pattern_decl))
+			    == FUNCTION_DECL)
+		  {
+		    DECL_CONTEXT (decl) = NULL_TREE;
+		    pushdecl (decl);
+		    DECL_CONTEXT (decl) = current_function_decl;
+		    cp_check_omp_declare_reduction (decl);
+		  }
 		else
 		  {
 		    int const_init = false;
@@ -13520,6 +13561,70 @@  tsubst_expr (tree t, tree args, tsubst_f
 #undef RETURN
 }
 
+/* Instantiate the special body of the artificial DECL_OMP_DECLARE_REDUCTION
+   function.  */
+
+static void
+tsubst_omp_udr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+  if (t == NULL_TREE || t == error_mark_node)
+    return;
+
+  gcc_assert (TREE_CODE (t) == STATEMENT_LIST);
+
+  tree_stmt_iterator tsi;
+  int i;
+  tree stmts[6];
+  memset (stmts, 0, sizeof stmts);
+  for (i = 0, tsi = tsi_start (t);
+       i < 6 && !tsi_end_p (tsi);
+       i++, tsi_next (&tsi))
+    stmts[i] = tsi_stmt (tsi);
+  gcc_assert (tsi_end_p (tsi));
+
+  if (i >= 3)
+    {
+      gcc_assert (TREE_CODE (stmts[0]) == DECL_EXPR
+		  && TREE_CODE (stmts[1]) == DECL_EXPR);
+      tree omp_out = tsubst (DECL_EXPR_DECL (stmts[0]),
+			     args, complain, in_decl);
+      tree omp_in = tsubst (DECL_EXPR_DECL (stmts[1]),
+			    args, complain, in_decl);
+      DECL_CONTEXT (omp_out) = current_function_decl;
+      DECL_CONTEXT (omp_in) = current_function_decl;
+      keep_next_level (true);
+      tree block = begin_omp_structured_block ();
+      tsubst_expr (stmts[2], args, complain, in_decl, false);
+      block = finish_omp_structured_block (block);
+      block = maybe_cleanup_point_expr_void (block);
+      add_decl_expr (omp_out);
+      if (TREE_NO_WARNING (DECL_EXPR_DECL (stmts[0])))
+	TREE_NO_WARNING (omp_out) = 1;
+      add_decl_expr (omp_in);
+      finish_expr_stmt (block);
+    }
+  if (i == 6)
+    {
+      gcc_assert (TREE_CODE (stmts[3]) == DECL_EXPR
+		  && TREE_CODE (stmts[4]) == DECL_EXPR);
+      tree omp_priv = tsubst (DECL_EXPR_DECL (stmts[3]),
+			      args, complain, in_decl);
+      tree omp_orig = tsubst (DECL_EXPR_DECL (stmts[4]),
+			      args, complain, in_decl);
+      DECL_CONTEXT (omp_priv) = current_function_decl;
+      DECL_CONTEXT (omp_orig) = current_function_decl;
+      keep_next_level (true);
+      tree block = begin_omp_structured_block ();
+      tsubst_expr (stmts[5], args, complain, in_decl, false);
+      block = finish_omp_structured_block (block);
+      block = maybe_cleanup_point_expr_void (block);
+      cp_walk_tree (&block, cp_remove_omp_priv_cleanup_stmt, omp_priv, NULL);
+      add_decl_expr (omp_priv);
+      add_decl_expr (omp_orig);
+      finish_expr_stmt (block);
+    }
+}
+
 /* T is a postfix-expression that is not being used in a function
    call.  Return the substituted version of T.  */
 
@@ -19193,6 +19298,7 @@  instantiate_decl (tree d, int defer_ok,
       tree subst_decl;
       tree tmpl_parm;
       tree spec_parm;
+      tree block = NULL_TREE;
 
       /* Save away the current list, in case we are instantiating one
 	 template from within the body of another.  */
@@ -19202,7 +19308,11 @@  instantiate_decl (tree d, int defer_ok,
       local_specializations = pointer_map_create ();
 
       /* Set up context.  */
-      start_preparsed_function (d, NULL_TREE, SF_PRE_PARSED);
+      if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern)
+	  && TREE_CODE (DECL_CONTEXT (code_pattern)) == FUNCTION_DECL)
+	block = push_stmt_list ();
+      else
+	start_preparsed_function (d, NULL_TREE, SF_PRE_PARSED);
 
       /* Some typedefs referenced from within the template code need to be
 	 access checked at template instantiation time, i.e now. These
@@ -19239,26 +19349,44 @@  instantiate_decl (tree d, int defer_ok,
       gcc_assert (!spec_parm);
 
       /* Substitute into the body of the function.  */
-      tsubst_expr (DECL_SAVED_TREE (code_pattern), args,
-		   tf_warning_or_error, tmpl,
-		   /*integral_constant_expression_p=*/false);
-
-      /* Set the current input_location to the end of the function
-         so that finish_function knows where we are.  */
-      input_location = DECL_STRUCT_FUNCTION (code_pattern)->function_end_locus;
+      if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern))
+	tsubst_omp_udr (DECL_SAVED_TREE (code_pattern), args,
+			tf_warning_or_error, tmpl);
+      else
+	{
+	  tsubst_expr (DECL_SAVED_TREE (code_pattern), args,
+		       tf_warning_or_error, tmpl,
+		       /*integral_constant_expression_p=*/false);
+
+	  /* Set the current input_location to the end of the function
+	     so that finish_function knows where we are.  */
+	  input_location
+	    = DECL_STRUCT_FUNCTION (code_pattern)->function_end_locus;
+	}
 
       /* We don't need the local specializations any more.  */
       pointer_map_destroy (local_specializations);
       local_specializations = saved_local_specializations;
 
+      if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern)
+	  && TREE_CODE (DECL_CONTEXT (code_pattern)) == FUNCTION_DECL)
+	DECL_SAVED_TREE (d) = pop_stmt_list (block);
+
       /* We expand all the array notation expressions here.  */
       if (flag_enable_cilkplus
 	  && contains_array_notation_expr (DECL_SAVED_TREE (d)))
 	DECL_SAVED_TREE (d) = expand_array_notation_exprs (DECL_SAVED_TREE (d));
       
       /* Finish the function.  */
-      d = finish_function (0);
-      expand_or_defer_fn (d);
+      if (!DECL_OMP_DECLARE_REDUCTION_P (code_pattern)
+	  || TREE_CODE (DECL_CONTEXT (code_pattern)) != FUNCTION_DECL)
+	{
+	  d = finish_function (0);
+	  expand_or_defer_fn (d);
+	}
+
+      if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern))
+	cp_check_omp_declare_reduction (d);
     }
 
   /* We're not deferring instantiation any more.  */
--- gcc/cp/cp-gimplify.c.jj	2013-06-14 18:46:39.000000000 +0200
+++ gcc/cp/cp-gimplify.c	2013-08-21 15:14:32.225461534 +0200
@@ -1503,7 +1503,7 @@  cxx_omp_finish_clause (tree c)
      for making these queries.  */
   if (!make_shared
       && CLASS_TYPE_P (inner_type)
-      && cxx_omp_create_clause_info (c, inner_type, false, true, false))
+      && cxx_omp_create_clause_info (c, inner_type, false, true, false, true))
     make_shared = true;
 
   if (make_shared)
--- gcc/cp/semantics.c.jj	2013-08-19 12:07:50.287652398 +0200
+++ gcc/cp/semantics.c	2013-08-27 14:39:13.903705979 +0200
@@ -4053,7 +4053,8 @@  finalize_nrv (tree *tp, tree var, tree r
 
 bool
 cxx_omp_create_clause_info (tree c, tree type, bool need_default_ctor,
-			    bool need_copy_ctor, bool need_copy_assignment)
+			    bool need_copy_ctor, bool need_copy_assignment,
+			    bool need_dtor)
 {
   int save_errorcount = errorcount;
   tree info, t;
@@ -4077,8 +4078,7 @@  cxx_omp_create_clause_info (tree c, tree
 	TREE_VEC_ELT (info, 0) = t;
     }
 
-  if ((need_default_ctor || need_copy_ctor)
-      && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
+  if (need_dtor && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
     TREE_VEC_ELT (info, 1) = get_dtor (type, tf_warning_or_error);
 
   if (need_copy_assignment)
@@ -4544,6 +4544,250 @@  handle_omp_array_sections (tree c)
   return false;
 }
 
+/* Return identifier to look up for omp declare reduction.  */
+
+tree
+omp_reduction_id (enum tree_code reduction_code, tree reduction_id)
+{
+  const char *p = NULL;
+  switch (reduction_code)
+    {
+    case PLUS_EXPR:
+    case MULT_EXPR:
+    case MINUS_EXPR:
+    case BIT_AND_EXPR:
+    case BIT_XOR_EXPR:
+    case BIT_IOR_EXPR:
+    case TRUTH_ANDIF_EXPR:
+    case TRUTH_ORIF_EXPR:
+      reduction_id = ansi_opname (reduction_code);
+      break;
+    case MIN_EXPR:
+      p = "min";
+      break;
+    case MAX_EXPR:
+      p = "max";
+      break;
+    default:
+      break;
+    }
+
+  if (p == NULL)
+    {
+      if (TREE_CODE (reduction_id) != IDENTIFIER_NODE)
+	return error_mark_node;
+      p = IDENTIFIER_POINTER (reduction_id);
+    }
+
+  size_t lenp = sizeof "omp declare reduction ";
+  size_t len = strlen (p);
+  char *name = XALLOCAVEC (char, lenp + len);
+  memcpy (name, "omp declare reduction ", lenp - 1);
+  memcpy (name + lenp - 1, p, len + 1);
+  return get_identifier (name);
+}
+
+/* Helper function for cp_parser_omp_declare_reduction_exprs
+   and tsubst_omp_udr.
+   Remove CLEANUP_STMT for data (omp_priv variable).
+   Also append INIT_EXPR for DECL_INITIAL of omp_priv after its
+   DECL_EXPR.  */
+
+tree
+cp_remove_omp_priv_cleanup_stmt (tree *tp, int *walk_subtrees, void *data)
+{
+  if (TYPE_P (*tp))
+    *walk_subtrees = 0;
+  else if (TREE_CODE (*tp) == CLEANUP_STMT && CLEANUP_DECL (*tp) == (tree) data)
+    *tp = CLEANUP_BODY (*tp);
+  else if (TREE_CODE (*tp) == DECL_EXPR)
+    {
+      tree decl = DECL_EXPR_DECL (*tp);
+      if (!processing_template_decl
+	  && decl == (tree) data
+	  && DECL_INITIAL (decl)
+	  && DECL_INITIAL (decl) != error_mark_node)
+	{
+	  tree list = NULL_TREE;
+	  append_to_statement_list_force (*tp, &list);
+	  tree init_expr = build2 (INIT_EXPR, void_type_node,
+				   decl, DECL_INITIAL (decl));
+	  DECL_INITIAL (decl) = NULL_TREE;
+	  append_to_statement_list_force (init_expr, &list);
+	  *tp = list;
+	}
+    }
+  return NULL_TREE;
+}
+
+/* Data passed from cp_check_omp_declare_reduction to
+   cp_check_omp_declare_reduction_r.  */
+
+struct cp_check_omp_declare_reduction_data
+{
+  location_t loc;
+  tree stmts[6];
+  bool combiner_p;
+};
+
+/* Helper function for cp_check_omp_declare_reduction, called via
+   cp_walk_tree.  */
+
+static tree
+cp_check_omp_declare_reduction_r (tree *tp, int *, void *data)
+{
+  struct cp_check_omp_declare_reduction_data *udr_data
+    = (struct cp_check_omp_declare_reduction_data *) data;
+  if (SSA_VAR_P (*tp)
+      && !DECL_ARTIFICIAL (*tp)
+      && *tp != DECL_EXPR_DECL (udr_data->stmts[udr_data->combiner_p ? 0 : 3])
+      && *tp != DECL_EXPR_DECL (udr_data->stmts[udr_data->combiner_p ? 1 : 4]))
+    {
+      location_t loc = udr_data->loc;
+      if (udr_data->combiner_p)
+	error_at (loc, "%<#pragma omp declare reduction%> combiner refers to "
+		       "variable %qD which is not %<omp_out%> nor %<omp_in%>",
+		  *tp);
+      else
+	error_at (loc, "%<#pragma omp declare reduction%> initializer refers "
+		       "to variable %qD which is not %<omp_priv%> nor "
+		       "%<omp_orig%>",
+		  *tp);
+      return *tp;
+    }
+  return NULL_TREE;
+}
+
+/* Diagnose violation of OpenMP #pragma omp declare reduction restrictions.  */
+
+void
+cp_check_omp_declare_reduction (tree udr)
+{
+  tree type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (udr)));
+  gcc_assert (TREE_CODE (type) == REFERENCE_TYPE);
+  type = TREE_TYPE (type);
+  int i;
+  location_t loc = DECL_SOURCE_LOCATION (udr);
+
+  if (type == error_mark_node)
+    return;
+  if (ARITHMETIC_TYPE_P (type))
+    {
+      static enum tree_code predef_codes[]
+	= { PLUS_EXPR, MULT_EXPR, MINUS_EXPR, BIT_AND_EXPR, BIT_XOR_EXPR,
+	    BIT_IOR_EXPR, TRUTH_ANDIF_EXPR, TRUTH_ORIF_EXPR };
+      for (i = 0; i < 8; i++)
+	if (DECL_NAME (udr) == omp_reduction_id (predef_codes[i], NULL_TREE))
+	  break;
+      if (i == 8
+	  && TREE_CODE (type) != COMPLEX_EXPR
+	  && (strcmp (IDENTIFIER_POINTER (DECL_NAME (udr)),
+		      "omp declare reduction min") == 0
+	      || strcmp (IDENTIFIER_POINTER (DECL_NAME (udr)),
+			 "omp declare reduction max") == 0))
+	i = 0;
+      if (i < 8)
+	{
+	  error_at (loc, "predeclared arithmetic type in "
+			 "%<#pragma omp declare reduction%>");
+	  return;
+	}
+    }
+  else if (TREE_CODE (type) == FUNCTION_TYPE
+	   || TREE_CODE (type) == METHOD_TYPE
+	   || TREE_CODE (type) == ARRAY_TYPE
+	   || TREE_CODE (type) == REFERENCE_TYPE)
+    {
+      error_at (loc, "function, array or reference type in "
+		     "%<#pragma omp declare reduction%>");
+      return;
+    }
+  else if (TYPE_QUALS_NO_ADDR_SPACE (type))
+    {
+      error_at (loc, "const, volatile or __restrict qualified type in "
+		     "%<#pragma omp declare reduction%>");
+      return;
+    }
+
+  tree body = DECL_SAVED_TREE (udr);
+  if (body == NULL_TREE || TREE_CODE (body) != STATEMENT_LIST)
+    return;
+
+  tree_stmt_iterator tsi;
+  struct cp_check_omp_declare_reduction_data data;
+  memset (data.stmts, 0, sizeof data.stmts);
+  for (i = 0, tsi = tsi_start (body);
+       i < 6 && !tsi_end_p (tsi);
+       i++, tsi_next (&tsi))
+    data.stmts[i] = tsi_stmt (tsi);
+  data.loc = loc;
+  gcc_assert (tsi_end_p (tsi));
+  if (i >= 3)
+    {
+      gcc_assert (TREE_CODE (data.stmts[0]) == DECL_EXPR
+		  && TREE_CODE (data.stmts[1]) == DECL_EXPR);
+      if (TREE_NO_WARNING (DECL_EXPR_DECL (data.stmts[0])))
+	return;
+      data.combiner_p = true;
+      if (cp_walk_tree (&data.stmts[2], cp_check_omp_declare_reduction_r,
+			&data, NULL))
+	TREE_NO_WARNING (DECL_EXPR_DECL (data.stmts[0])) = 1;
+    }
+  if (i == 6)
+    {
+      gcc_assert (TREE_CODE (data.stmts[3]) == DECL_EXPR
+		  && TREE_CODE (data.stmts[4]) == DECL_EXPR);
+      data.combiner_p = false;
+      if (cp_walk_tree (&data.stmts[5], cp_check_omp_declare_reduction_r,
+			&data, NULL)
+	  || cp_walk_tree (&DECL_INITIAL (DECL_EXPR_DECL (data.stmts[3])),
+			   cp_check_omp_declare_reduction_r, &data, NULL))
+	TREE_NO_WARNING (DECL_EXPR_DECL (data.stmts[0])) = 1;
+    }
+}
+
+/* Helper function of finish_omp_clauses.  Clone STMT as if we were making
+   an inline call.  But, remap
+   the OMP_DECL1 VAR_DECL (omp_out resp. omp_orig) to PLACEHOLDER
+   and OMP_DECL2 VAR_DECL (omp_in resp. omp_priv) to OMP_CLAUSE_DECL (C).  */
+
+static tree
+clone_omp_udr (tree c, tree stmt, tree omp_decl1, tree omp_decl2,
+	       tree placeholder)
+{
+  copy_body_data id;
+  struct pointer_map_t *decl_map = pointer_map_create ();
+
+  *pointer_map_insert (decl_map, omp_decl1) = placeholder;
+  *pointer_map_insert (decl_map, omp_decl2) = OMP_CLAUSE_DECL (c);
+  memset (&id, 0, sizeof (id));
+  id.src_fn = DECL_CONTEXT (omp_decl1);
+  id.dst_fn = current_function_decl;
+  id.src_cfun = DECL_STRUCT_FUNCTION (id.src_fn);
+  id.decl_map = decl_map;
+
+  id.copy_decl = copy_decl_no_change;
+  id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+  id.transform_new_cfg = true;
+  id.transform_return_to_modify = false;
+  id.transform_lang_insert_block = NULL;
+  id.eh_lp_nr = 0;
+  walk_tree (&stmt, copy_tree_body_r, &id, NULL);
+  pointer_map_destroy (decl_map);
+  return stmt;
+}
+
+/* Helper function of finish_omp_clauses, called via cp_walk_tree.
+   Find OMP_CLAUSE_PLACEHOLDER (passed in DATA) in *TP.  */
+
+static tree
+find_omp_placeholder_r (tree *tp, int *, void *data)
+{
+  if (*tp == (tree) data)
+    return *tp;
+  return NULL_TREE;
+}
+
 /* For all elements of CLAUSES, validate them vs OpenMP constraints.
    Remove any elements from the list that are invalid.  */
 
@@ -5063,6 +5307,7 @@  finish_omp_clauses (tree clauses)
       bool need_copy_ctor = false;
       bool need_copy_assignment = false;
       bool need_implicitly_determined = false;
+      bool need_dtor = false;
       tree type, inner_type;
 
       switch (c_kind)
@@ -5075,12 +5320,14 @@  finish_omp_clauses (tree clauses)
 	  name = "private";
 	  need_complete_non_reference = true;
 	  need_default_ctor = true;
+	  need_dtor = true;
 	  need_implicitly_determined = true;
 	  break;
 	case OMP_CLAUSE_FIRSTPRIVATE:
 	  name = "firstprivate";
 	  need_complete_non_reference = true;
 	  need_copy_ctor = true;
+	  need_dtor = true;
 	  need_implicitly_determined = true;
 	  break;
 	case OMP_CLAUSE_LASTPRIVATE:
@@ -5128,34 +5375,189 @@  finish_omp_clauses (tree clauses)
 	{
 	case OMP_CLAUSE_LASTPRIVATE:
 	  if (!bitmap_bit_p (&firstprivate_head, DECL_UID (t)))
-	    need_default_ctor = true;
+	    {
+	      need_default_ctor = true;
+	      need_dtor = true;
+	    }
 	  break;
 
 	case OMP_CLAUSE_REDUCTION:
-	  if (AGGREGATE_TYPE_P (TREE_TYPE (t))
-	      || POINTER_TYPE_P (TREE_TYPE (t)))
-	    {
-	      error ("%qE has invalid type for %<reduction%>", t);
-	      remove = true;
-	    }
-	  else if (FLOAT_TYPE_P (TREE_TYPE (t)))
-	    {
-	      enum tree_code r_code = OMP_CLAUSE_REDUCTION_CODE (c);
-	      switch (r_code)
+	  {
+	    bool predefined = false;
+	    if (ARITHMETIC_TYPE_P (TREE_TYPE (t)))
+	      switch (OMP_CLAUSE_REDUCTION_CODE (c))
 		{
 		case PLUS_EXPR:
 		case MULT_EXPR:
 		case MINUS_EXPR:
+		  predefined = true;
+		  break;
 		case MIN_EXPR:
 		case MAX_EXPR:
+		  if (TREE_CODE (TREE_TYPE (t)) == COMPLEX_TYPE)
+		    break;
+		  predefined = true;
+		  break;
+		case BIT_AND_EXPR:
+		case BIT_IOR_EXPR:
+		case BIT_XOR_EXPR:
+		case TRUTH_ANDIF_EXPR:
+		case TRUTH_ORIF_EXPR:
+		  if (FLOAT_TYPE_P (TREE_TYPE (t)))
+		    break;
+		  predefined = true;
 		  break;
 		default:
-		  error ("%qE has invalid type for %<reduction(%s)%>",
-			 t, operator_name_info[r_code].name);
-		  remove = true;
+		  break;
 		}
-	    }
-	  break;
+	    else if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
+		     || TYPE_READONLY (TREE_TYPE (t)))
+	      {
+		error ("%qE has invalid type for %<reduction%>", t);
+		remove = true;
+		break;
+	      }
+	    else if (!processing_template_decl)
+	      {
+		t = require_complete_type (t);
+		if (t == error_mark_node)
+		  {
+		    remove = true;
+		    break;
+		  }
+	      }
+	    if (predefined)
+	      {
+		OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL_TREE;
+	      }
+	    else if (!processing_template_decl)
+	      {
+		tree type = TYPE_MAIN_VARIANT (TREE_TYPE (t));
+		tree id = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c);
+
+		OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL_TREE;
+		if (id == NULL_TREE)
+		  id = omp_reduction_id (OMP_CLAUSE_REDUCTION_CODE (c),
+					 NULL_TREE);
+		if (identifier_p (id))
+		  {
+		    cp_id_kind idk;
+		    bool nonint_cst_expression_p;
+		    const char *error_msg;
+		    tree decl = lookup_name (id);
+		    if (decl == NULL_TREE)
+		      decl = error_mark_node;
+		    id = finish_id_expression (id, decl, NULL_TREE, &idk,
+					       false, true,
+					       &nonint_cst_expression_p,
+					       false, true, false, false,
+					       &error_msg,
+					       OMP_CLAUSE_LOCATION (c));
+		    if (idk == CP_ID_KIND_UNQUALIFIED
+			&& identifier_p (id))
+		      {
+			vec<tree, va_gc> *args = NULL;
+			vec_safe_push (args, build_reference_type (type));
+			id = perform_koenig_lookup (id, args, false, tf_none);
+		      }
+		  }
+		if (id && is_overloaded_fn (id))
+		  id = get_fns (id);
+		for (; id; id = OVL_NEXT (id))
+		  {
+		    tree fndecl = OVL_CURRENT (id);
+		    if (TREE_CODE (fndecl) == FUNCTION_DECL)
+		      {
+			tree argtype
+			  = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
+			if (same_type_p (TREE_TYPE (argtype), type))
+			  break;
+		      }
+		  }
+		if (id)
+		  {
+		    id = OVL_CURRENT (id);
+		    if (DECL_TEMPLATE_INFO (id))
+		      id = instantiate_decl (id, /*defer_ok*/0, true);
+		    tree body = DECL_SAVED_TREE (id);
+		    if (TREE_CODE (body) == STATEMENT_LIST)
+		      {
+			tree_stmt_iterator tsi;
+			tree placeholder = NULL_TREE;
+			int i;
+			tree stmts[6];
+			memset (stmts, 0, sizeof stmts);
+			for (i = 0, tsi = tsi_start (body);
+			     i < 6 && !tsi_end_p (tsi);
+			     i++, tsi_next (&tsi))
+			  stmts[i] = tsi_stmt (tsi);
+			gcc_assert (tsi_end_p (tsi));
+			if (i >= 3)
+			  {
+			    gcc_assert (TREE_CODE (stmts[0]) == DECL_EXPR
+					&& TREE_CODE (stmts[1]) == DECL_EXPR);
+			    placeholder
+			      = build_lang_decl (VAR_DECL, NULL_TREE, type);
+			    DECL_ARTIFICIAL (placeholder) = 1;
+			    DECL_IGNORED_P (placeholder) = 1;
+			    OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = placeholder;
+			    if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[0])))
+			      cxx_mark_addressable (placeholder);
+			    if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[1])))
+			      cxx_mark_addressable (OMP_CLAUSE_DECL (c));
+			    OMP_CLAUSE_REDUCTION_MERGE (c)
+			      = clone_omp_udr (c, stmts[2],
+					       DECL_EXPR_DECL (stmts[0]),
+					       DECL_EXPR_DECL (stmts[1]),
+					       placeholder);
+			  }
+			if (i == 6)
+			  {
+			    gcc_assert (TREE_CODE (stmts[3]) == DECL_EXPR
+					&& TREE_CODE (stmts[4]) == DECL_EXPR);
+			    if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[3])))
+			      cxx_mark_addressable (OMP_CLAUSE_DECL (c));
+			    if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[4])))
+			      cxx_mark_addressable (placeholder);
+			    OMP_CLAUSE_REDUCTION_INIT (c)
+			      = clone_omp_udr (c, stmts[5],
+					       DECL_EXPR_DECL (stmts[4]),
+					       DECL_EXPR_DECL (stmts[3]),
+					       placeholder);
+			    if (cp_walk_tree (&OMP_CLAUSE_REDUCTION_INIT (c),
+					      find_omp_placeholder_r,
+					      placeholder, NULL))
+			      OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c) = 1;
+			  }
+			else if (i >= 3)
+			  {
+			    if (TYPE_HAS_USER_CONSTRUCTOR (TREE_TYPE (t)))
+			      need_default_ctor = true;
+			    else
+			      {
+				tree init;
+				if (AGGREGATE_TYPE_P (TREE_TYPE (t)))
+				  init = build_constructor (TREE_TYPE (t),
+							    NULL);
+				else
+				  init = fold_convert (TREE_TYPE (t),
+						       integer_zero_node);
+				OMP_CLAUSE_REDUCTION_INIT (c)
+				  = build2 (INIT_EXPR, TREE_TYPE (t), t, init);
+			      }
+			  }
+		      }
+		  }
+		if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c))
+		  need_dtor = true;
+		else
+		  {
+		    error ("user defined reduction not found for %qD", t);
+		    remove = true;
+		  }
+	      }
+	    break;
+	  }
 
 	case OMP_CLAUSE_COPYIN:
 	  if (!VAR_P (t) || !DECL_THREAD_LOCAL_P (t))
@@ -5222,10 +5624,12 @@  finish_omp_clauses (tree clauses)
 	 for making these queries.  */
       if (CLASS_TYPE_P (inner_type)
 	  && COMPLETE_TYPE_P (inner_type)
-	  && (need_default_ctor || need_copy_ctor || need_copy_assignment)
+	  && (need_default_ctor || need_copy_ctor
+	      || need_copy_assignment || need_dtor)
 	  && !type_dependent_expression_p (t)
 	  && cxx_omp_create_clause_info (c, inner_type, need_default_ctor,
-					 need_copy_ctor, need_copy_assignment))
+					 need_copy_ctor, need_copy_assignment,
+					 need_dtor))
 	remove = true;
 
       if (remove)
--- gcc/testsuite/g++.dg/gomp/clause-3.C.jj	2013-08-27 12:56:15.000000000 +0200
+++ gcc/testsuite/g++.dg/gomp/clause-3.C	2013-08-27 12:57:47.206569932 +0200
@@ -42,18 +42,18 @@  foo (int x)
     ;
 #pragma omp p firstprivate (bar) // { dg-error "is not a variable" }
     ;
-#pragma omp p reduction (+:pp) // { dg-error "has invalid type for" }
+#pragma omp p reduction (+:pp) // { dg-error "user defined reduction not found for" }
     ;
-#pragma omp p reduction (*:s) // { dg-error "has invalid type for" }
+#pragma omp p reduction (*:s) // { dg-error "user defined reduction not found for" }
     ;
 #pragma omp p reduction (-:a) // { dg-error "has invalid type for" }
     ;
   d = 0;
 #pragma omp p reduction (*:d)
     ;
-#pragma omp p reduction (|:d) // { dg-error "has invalid type for" }
+#pragma omp p reduction (|:d) // { dg-error "user defined reduction not found for" }
     ;
-#pragma omp p reduction (&&:d) // { dg-error "has invalid type for" }
+#pragma omp p reduction (&&:d) // { dg-error "user defined reduction not found for" }
     ;
 #pragma omp p copyin (d) // { dg-error "must be 'threadprivate'" }
     ;
--- gcc/testsuite/g++.dg/gomp/udr-1.C.jj	2013-08-26 19:15:47.443276396 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-1.C	2013-08-26 19:20:59.821658295 +0200
@@ -0,0 +1,119 @@ 
+// { dg-do compile }
+// { dg-options "-fopenmp" }
+
+namespace N1
+{
+  #pragma omp declare reduction (| : long int : omp_out |= omp_in)	// { dg-error "predeclared arithmetic type" }
+  #pragma omp declare reduction (+ : char : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+  typedef short T;
+  #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+  #pragma omp declare reduction (* : _Complex double : omp_out *= omp_in)// { dg-error "predeclared arithmetic type" }
+}
+namespace N2
+{
+  template <typename T1, typename T2, typename T3, typename T4>
+  struct S
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "predeclared arithmetic type" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+    #pragma omp declare reduction (* : T4 : omp_out *= omp_in)		// { dg-error "predeclared arithmetic type" }
+  };
+  S<long int, char, short, _Complex double> s;
+  template <typename T1, typename T2, typename T3, typename T4>
+  int foo ()
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "predeclared arithmetic type" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+    #pragma omp declare reduction (* : T4 : omp_out *= omp_in)		// { dg-error "predeclared arithmetic type" }
+    return 0;
+  }
+  int x = foo <long int, char, short, _Complex double> ();
+}
+namespace N3
+{
+  void bar ();
+  #pragma omp declare reduction (| : __typeof (bar) : omp_out |= omp_in)// { dg-error "function, array or reference" }
+  #pragma omp declare reduction (+ : char () : omp_out += omp_in)	// { dg-error "function, array or reference" }
+  typedef short T;
+  #pragma omp declare reduction (min : T[2] : omp_out += omp_in)	// { dg-error "function, array or reference" }
+  #pragma omp declare reduction (baz : char & : omp_out *= omp_in)	// { dg-error "function, array or reference" }
+}
+namespace N4
+{
+  void bar ();
+  template <typename T1, typename T2, typename T3, typename T4>
+  struct S
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "function, array or reference" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "function, array or reference" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "function, array or reference" }
+    #pragma omp declare reduction (baz : T4 : omp_out *= omp_in)	// { dg-error "function, array or reference" }
+  };
+  S<__typeof (bar), char (), short [3], char []> s;
+  template <typename T1, typename T2, typename T3, typename T4>
+  int foo ()
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "function, array or reference" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "function, array or reference" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "function, array or reference" }
+    #pragma omp declare reduction (baz : T4 : omp_out *= omp_in)	// { dg-error "function, array or reference" }
+    return 0;
+  }
+  int x = foo <__typeof (bar), char (), short[], char [2]> ();
+}
+namespace N5
+{
+  template <typename T>
+  struct S
+  {
+    #pragma omp declare reduction (baz : T : omp_out *= omp_in)		// { dg-error "function, array or reference" }
+  };
+  S<char &> s;
+  template <typename T>
+  int foo ()
+  {
+    #pragma omp declare reduction (baz : T : omp_out *= omp_in)		// { dg-error "function, array or reference" }
+    return 0;
+  }
+  int x = foo <char &> ();
+}
+namespace N6
+{
+  struct A { int a; A () : a (0) {} };
+  #pragma omp declare reduction (| : const A : omp_out.a |= omp_in.a)	// { dg-error "const, volatile or __restrict" }
+  #pragma omp declare reduction (+ : __const A : omp_out.a += omp_in.a)	// { dg-error "const, volatile or __restrict" }
+  typedef volatile A T;
+  #pragma omp declare reduction (min : T : omp_out.a += omp_in.a)	// { dg-error "const, volatile or __restrict" }
+  #pragma omp declare reduction (* : A *__restrict : omp_out->a *= omp_in->a)// { dg-error "const, volatile or __restrict" }
+}
+namespace N7
+{
+  struct A { int a; A () : a (0) {} };
+  template <typename T1, typename T2, typename T3, typename T4>
+  struct S
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "const, volatile or __restrict" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "const, volatile or __restrict" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "const, volatile or __restrict" }
+    #pragma omp declare reduction (* : T4 : omp_out *= omp_in)		// { dg-error "const, volatile or __restrict" }
+  };
+  S<const A, __const A, volatile A, A *__restrict> s;
+  template <typename T1, typename T2, typename T3, typename T4>
+  int foo ()
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "const, volatile or __restrict" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "const, volatile or __restrict" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "const, volatile or __restrict" }
+    #pragma omp declare reduction (* : T4 : omp_out *= omp_in)		// { dg-error "const, volatile or __restrict" }
+    return 0;
+  }
+  int x = foo <const A, __const A, volatile A, A *__restrict> ();
+}
--- gcc/testsuite/g++.dg/gomp/udr-2.C.jj	2013-08-27 10:41:15.229767043 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-2.C	2013-08-26 19:50:51.000000000 +0200
@@ -0,0 +1,119 @@ 
+// { dg-do compile }
+// { dg-options "-fopenmp" }
+
+struct W { int w; W () : w (0) {} W (int x) : w (x) {} };
+namespace N1
+{
+  int v;
+  #pragma omp declare reduction (foo : long int : omp_out |= v)	// { dg-error "combiner refers to variable" }
+  #pragma omp declare reduction (foo : char : omp_out = v)	// { dg-error "combiner refers to variable" }
+  typedef short T;
+  #pragma omp declare reduction (foo : T : omp_out += N1::v)	// { dg-error "combiner refers to variable" }
+  #pragma omp declare reduction (foo : int : v *= omp_in)	// { dg-error "combiner refers to variable" }
+  #pragma omp declare reduction (foo : W : omp_out.w *= omp_in.w + v) // { dg-error "combiner refers to variable" }
+}
+namespace N2
+{
+  int v;
+  template <typename T1, typename T2, typename T3, typename T4, typename T5>
+  struct S
+  {
+    #pragma omp declare reduction (foo : T1 : omp_out |= v)	// { dg-error "combiner refers to variable" }
+    #pragma omp declare reduction (foo : T2 : omp_out = v)	// { dg-error "combiner refers to variable" }
+    typedef T3 T;
+    #pragma omp declare reduction (foo : T : omp_out += N1::v)	// { dg-error "combiner refers to variable" }
+    #pragma omp declare reduction (foo : T4 : v *= omp_in)	// { dg-error "combiner refers to variable" }
+    #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w + v) // { dg-error "combiner refers to variable" }
+  };
+  S<long int, char, short, _Complex double, W> s;
+  template <typename T1, typename T2, typename T3, typename T4, typename T5>
+  int foo ()
+  {
+    #pragma omp declare reduction (foo : T1 : omp_out |= v)	// { dg-error "combiner refers to variable" }
+    #pragma omp declare reduction (foo : T2 : omp_out = v)	// { dg-error "combiner refers to variable" }
+    typedef T3 T;
+    #pragma omp declare reduction (foo : T : omp_out += N1::v)	// { dg-error "combiner refers to variable" }
+    #pragma omp declare reduction (foo : T4 : v *= omp_in)	// { dg-error "combiner refers to variable" }
+    #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w + v) // { dg-error "combiner refers to variable" }
+    return 0;
+  }
+  int x = foo <long int, char, short, _Complex double, W> ();
+}
+namespace N3
+{
+  int v;
+  #pragma omp declare reduction (foo : long int : omp_out |= omp_in) initializer (omp_priv = v) // { dg-error "initializer refers to variable" }
+  #pragma omp declare reduction (foo : char : omp_out += omp_in) initializer (omp_priv ((char) N3::v)) // { dg-error "initializer refers to variable" }
+  typedef short T;
+  #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (omp_priv = (short) v) // { dg-error "initializer refers to variable" }
+  #pragma omp declare reduction (foo : _Complex double : omp_out *= omp_in) initializer (omp_priv (v)) // { dg-error "initializer refers to variable" }
+  #pragma omp declare reduction (foo : W : omp_out.w *= omp_in.w) initializer (omp_priv (N3::v)) // { dg-error "initializer refers to variable" }
+}
+namespace N4
+{
+  int v;
+  template <typename T1, typename T2, typename T3, typename T4, typename T5>
+  struct S
+  {
+    #pragma omp declare reduction (foo : T1 : omp_out |= omp_in) initializer (omp_priv = v) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T2 : omp_out += omp_in) initializer (omp_priv ((char) N3::v)) // { dg-error "initializer refers to variable" }
+    typedef T3 T;
+    #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (omp_priv = (short) v) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T4 : omp_out *= omp_in) initializer (omp_priv (v)) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w) initializer (omp_priv (N3::v)) // { dg-error "initializer refers to variable" }
+  };
+  S<long int, char, short, _Complex double, W> s;
+  template <typename T1, typename T2, typename T3, typename T4, typename T5>
+  int foo ()
+  {
+    #pragma omp declare reduction (foo : T1 : omp_out |= omp_in) initializer (omp_priv = v) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T2 : omp_out += omp_in) initializer (omp_priv ((char) N3::v)) // { dg-error "initializer refers to variable" }
+    typedef T3 T;
+    #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (omp_priv = (short) v) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T4 : omp_out *= omp_in) initializer (omp_priv (v)) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w) initializer (omp_priv (N3::v)) // { dg-error "initializer refers to variable" }
+    return 0;
+  }
+  int x = foo <long int, char, short, _Complex double, W> ();
+}
+template <typename T>
+void init (T &, int &);
+template <typename T>
+void initializer (T, int &);
+namespace N5
+{
+  int v;
+  #pragma omp declare reduction (foo : long int : omp_out |= omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" }
+  #pragma omp declare reduction (foo : char : omp_out += omp_in) initializer (initializer (&omp_priv, N3::v)) // { dg-error "initializer refers to variable" }
+  typedef short T;
+  #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" }
+  #pragma omp declare reduction (foo : _Complex double : omp_out *= omp_in) initializer (initializer (&omp_priv, v)) // { dg-error "initializer refers to variable" }
+  #pragma omp declare reduction (foo : W : omp_out.w *= omp_in.w) initializer (init (omp_priv, N3::v)) // { dg-error "initializer refers to variable" }
+}
+namespace N6
+{
+  int v;
+  template <typename T1, typename T2, typename T3, typename T4, typename T5>
+  struct S
+  {
+    #pragma omp declare reduction (foo : T1 : omp_out |= omp_in) initializer (initializer (&omp_priv, v)) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T2 : omp_out += omp_in) initializer (init (omp_priv, N3::v)) // { dg-error "initializer refers to variable" }
+    typedef T3 T;
+    #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T4 : omp_out *= omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w) initializer (initializer (&omp_priv, N3::v)) // { dg-error "initializer refers to variable" }
+  };
+  S<long int, char, short, _Complex double, W> s;
+  template <typename T1, typename T2, typename T3, typename T4, typename T5>
+  int foo ()
+  {
+    #pragma omp declare reduction (foo : T1 : omp_out |= omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T2 : omp_out += omp_in) initializer (init (omp_priv, N3::v)) // { dg-error "initializer refers to variable" }
+    typedef T3 T;
+    #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (initializer (&omp_priv, v)) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T4 : omp_out *= omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" }
+    #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w) initializer (initializer (omp_priv, N3::v)) // { dg-error "initializer refers to variable" }
+    return 0;
+  }
+  int x = foo <long int, char, short, _Complex double, W> ();
+}
--- gcc/testsuite/g++.dg/gomp/udr-3.C.jj	2013-08-27 17:26:50.574255582 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-3.C	2013-08-27 17:45:32.000000000 +0200
@@ -0,0 +1,170 @@ 
+// { dg-do compile }
+// { dg-options "-fopenmp" }
+
+struct S { int s; S () : s (0) {} S (int x) : s (x) {} ~S () {} };
+struct T { int t; T () : t (0) {} T (int x) : t (x) {} ~T () {} };
+
+#pragma omp declare reduction (+: ::S: omp_out.s += omp_in.s)
+#pragma omp declare reduction (*: S: omp_out.s *= omp_in.s) \
+		    initializer (omp_priv (1))
+#pragma omp declare reduction (foo: S: omp_out.s += omp_in.s)
+
+void
+f1 ()
+{
+  S s, s2;
+  T t;
+  #pragma omp declare reduction (+: T: omp_out.t += omp_in.t)
+  #pragma omp parallel reduction (+: t) reduction (foo: s) reduction (*: s2)
+  s.s = 1, t.t = 1, s2.s = 2;
+  #pragma omp parallel reduction (::operator +: s)
+  s.s = 1;
+  #pragma omp parallel reduction (+: s)	// { dg-error "user defined reduction not found" }
+  s.s = 1;
+}
+
+template <int N>
+int
+f2 ()
+{
+  S s, s2;
+  T t;
+  #pragma omp declare reduction (+: T: omp_out.t += omp_in.t)
+  #pragma omp parallel reduction (+: t) reduction (foo: s) reduction (*: s2)
+  s.s = 1, t.t = 1, s2.s = 2;
+  #pragma omp parallel reduction (::operator +: s)
+  s.s = 1;
+  #pragma omp parallel reduction (+: s)	// { dg-error "user defined reduction not found" }
+  s.s = 1;
+  return 0;
+}
+
+int x = f2<0> ();
+
+void bar (S &);
+
+void
+f3 ()
+{
+  #pragma omp declare reduction (foo: S: omp_out.s += omp_in.s) initializer (bar (omp_priv))
+  #pragma omp declare reduction (bar: S: omp_out.s += omp_in.s) initializer (bar (omp_orig)) // { dg-error "one of the initializer call arguments should be" }
+}
+
+template <typename T>
+int
+f4 ()
+{
+  #pragma omp declare reduction (foo: T: omp_out.s += omp_in.s) initializer (bar (omp_priv))
+  #pragma omp declare reduction (bar: T: omp_out.s += omp_in.s) initializer (bar (omp_orig)) // { dg-error "one of the initializer call arguments should be" }
+  return 0;
+}
+
+int y = f4 <S> ();
+
+namespace N1
+{
+  #pragma omp declare reduction (+: ::S: omp_out.s *= omp_in.s)		// { dg-error "previous" }
+  #pragma omp declare reduction (+: S: omp_out.s += omp_in.s)		// { dg-error "redeclaration of" }
+  void
+  f5 ()
+  {
+    #pragma omp declare reduction (f5: S: omp_out.s *= omp_in.s)	// { dg-error "previous" }
+    #pragma omp declare reduction (f5: ::S: omp_out.s += omp_in.s)	// { dg-error "redeclaration of" }
+  }
+}
+
+namespace N2
+{
+  struct U
+  {
+    #pragma omp declare reduction (bar: S: omp_out.s *= omp_in.s)	// { dg-error "with" }
+    #pragma omp declare reduction (bar: S: omp_out.s += omp_in.s)	// { dg-error "cannot be overloaded" }
+  };
+}
+
+namespace N3
+{
+  #pragma omp declare reduction (+: ::S: omp_out.s *= omp_in.s)		// { dg-error "previous" }
+  #pragma omp declare reduction (+: T: omp_out.t += omp_in.t)
+  #pragma omp declare reduction (+: S: omp_out.s += omp_in.s)		// { dg-error "redeclaration of" }
+  #pragma omp declare reduction (n3: long: omp_out += omp_in)		// { dg-error "previous" }
+  #pragma omp declare reduction (n3: long int: omp_out += omp_in)	// { dg-error "redeclaration of" }
+  #pragma omp declare reduction (n3: short unsigned: omp_out += omp_in)
+  #pragma omp declare reduction (n3: short int: omp_out += omp_in)
+  void
+  f6 ()
+  {
+    #pragma omp declare reduction (f6: T: omp_out.t += omp_in.t)
+    #pragma omp declare reduction (f6: S: omp_out.s *= omp_in.s)	// { dg-error "previous" }
+    #pragma omp declare reduction (f6: ::S: omp_out.s += omp_in.s)	// { dg-error "redeclaration of" }
+    #pragma omp declare reduction (f6: long: omp_out += omp_in)		// { dg-error "previous" }
+    #pragma omp declare reduction (f6: long int: omp_out += omp_in)	// { dg-error "redeclaration of" }
+    #pragma omp declare reduction (f6: short unsigned: omp_out += omp_in)
+    #pragma omp declare reduction (f6: short int: omp_out += omp_in)
+  }
+}
+
+namespace N4
+{
+  struct U
+  {
+    #pragma omp declare reduction (bar: T: omp_out.t += omp_in.t)
+    #pragma omp declare reduction (bar: S: omp_out.s *= omp_in.s)	// { dg-error "with" }
+    #pragma omp declare reduction (bar: S: omp_out.s += omp_in.s)	// { dg-error "cannot be overloaded" }
+    #pragma omp declare reduction (bar: long: omp_out += omp_in)	// { dg-error "with" }
+    #pragma omp declare reduction (bar: long int: omp_out += omp_in)	// { dg-error "cannot be overloaded" }
+    #pragma omp declare reduction (bar: short unsigned: omp_out += omp_in)
+    #pragma omp declare reduction (bar: short int: omp_out += omp_in)
+  };
+}
+
+namespace N5
+{
+  template <typename T>
+  int
+  f7 ()
+  {
+    #pragma omp declare reduction (f7: T: omp_out.s *= omp_in.s)	// { dg-error "previous" }
+    #pragma omp declare reduction (f7: T: omp_out.s += omp_in.s)	// { dg-error "redeclaration of" }
+    return 0;
+  }
+  int x = f7 <S> ();
+  template <typename T>
+  struct U
+  {
+    #pragma omp declare reduction (bar: T: omp_out.s *= omp_in.s)	// { dg-error "with" }
+    #pragma omp declare reduction (bar: T: omp_out.s += omp_in.s)	// { dg-error "cannot be overloaded" }
+  };
+  U<S> u;
+}
+
+namespace N6
+{
+  template <typename U>
+  int
+  f8 ()
+  {
+    #pragma omp declare reduction (f8: T: omp_out.t += omp_in.t)
+    #pragma omp declare reduction (f8: U: omp_out.s *= omp_in.s)	// { dg-error "previous" }
+    #pragma omp declare reduction (f8: ::S: omp_out.s += omp_in.s)	// { dg-error "redeclaration of" }
+    #pragma omp declare reduction (f8: long: omp_out += omp_in)		// { dg-error "previous" }
+    #pragma omp declare reduction (f8: long int: omp_out += omp_in)	// { dg-error "redeclaration of" }
+    #pragma omp declare reduction (f8: short unsigned: omp_out += omp_in)
+    #pragma omp declare reduction (f8: short int: omp_out += omp_in)
+    return 0;
+  }
+  int x = f8 <S> ();
+  template <typename V>
+  struct U
+  {
+    typedef V V2;
+    #pragma omp declare reduction (bar: T: omp_out.t += omp_in.t)
+    #pragma omp declare reduction (bar: V: omp_out.s *= omp_in.s)	// { dg-error "with" }
+    #pragma omp declare reduction (bar: V2: omp_out.s += omp_in.s)	// { dg-error "cannot be overloaded" }
+    #pragma omp declare reduction (bar: long: omp_out += omp_in)	// { dg-error "with" }
+    #pragma omp declare reduction (bar: long int: omp_out += omp_in)	// { dg-error "cannot be overloaded" }
+    #pragma omp declare reduction (bar: short unsigned: omp_out += omp_in)
+    #pragma omp declare reduction (bar: short int: omp_out += omp_in)
+  };
+  U<S> u;
+}
--- libgomp/testsuite/libgomp.c++/simd-4.C.jj	2013-08-22 15:11:23.186601861 +0200
+++ libgomp/testsuite/libgomp.c++/simd-4.C	2013-08-27 13:19:03.000000000 +0200
@@ -0,0 +1,46 @@ 
+// { dg-do run }
+// { dg-options "-O2" }
+// { dg-additional-options "-msse2" { target sse2_runtime } }
+// { dg-additional-options "-mavx" { target avx_runtime } }
+
+extern "C" void abort ();
+int a[1024] __attribute__((aligned (32))) = { 1 };
+struct S
+{
+  int s;
+  S () : s (0) {}
+  ~S () {}
+};
+#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) \
+  initializer (omp_priv ())
+#pragma omp declare reduction (foo:S:omp_out.s += omp_in.s)
+#pragma omp declare reduction (foo:int:omp_out += omp_in)
+
+__attribute__((noinline, noclone)) int
+foo ()
+{
+  int i, u = 0;
+  S s, t;
+  #pragma omp simd aligned(a : 32) reduction(+:s) reduction(foo:t, u)
+  for (i = 0; i < 1024; i++)
+    {
+      int x = a[i];
+      s.s += x;
+      t.s += x;
+      u += x;
+    }
+  if (t.s != s.s || u != s.s)
+    abort ();
+  return s.s;
+}
+
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 1024; i++)
+    a[i] = (i & 31) + (i / 128);
+  int s = foo ();
+  if (s != 19456)
+    abort ();
+}
--- libgomp/testsuite/libgomp.c++/simd-5.C.jj	2013-08-27 16:14:46.714666520 +0200
+++ libgomp/testsuite/libgomp.c++/simd-5.C	2013-08-27 16:11:06.000000000 +0200
@@ -0,0 +1,48 @@ 
+// { dg-do run }
+// { dg-options "-O2" }
+// { dg-additional-options "-msse2" { target sse2_runtime } }
+// { dg-additional-options "-mavx" { target avx_runtime } }
+
+extern "C" void abort ();
+int a[1024] __attribute__((aligned (32))) = { 1 };
+struct S
+{
+  int s;
+  S () : s (0) {}
+  ~S () {}
+};
+#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) \
+  initializer (omp_priv ())
+#pragma omp declare reduction (foo:S:omp_out.s += omp_in.s)
+#pragma omp declare reduction (foo:int:omp_out += omp_in)
+
+__attribute__((noinline, noclone)) int
+foo ()
+{
+  int i, u = 0, q = 0;
+  S s, t;
+  #pragma omp simd aligned(a : 32) reduction(+:s, q) reduction(foo:t, u) \
+	      safelen(1)
+  for (i = 0; i < 1024; i++)
+    {
+      int x = a[i];
+      s.s += x;
+      t.s += x;
+      u += x;
+      q++;
+    }
+  if (t.s != s.s || u != s.s || q != 1024)
+    abort ();
+  return s.s;
+}
+
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 1024; i++)
+    a[i] = (i & 31) + (i / 128);
+  int s = foo ();
+  if (s != 19456)
+    abort ();
+}
--- libgomp/testsuite/libgomp.c++/udr-1.C.jj	2013-08-21 19:36:58.359959895 +0200
+++ libgomp/testsuite/libgomp.c++/udr-1.C	2013-08-27 13:48:47.000000000 +0200
@@ -0,0 +1,82 @@ 
+// { dg-do run }
+
+extern "C" void abort ();
+
+struct S
+{
+  int s;
+  void foo (S &x) { s += x.s; }
+  void foo (S &x, bool y) { s += x.s; if (y) abort (); }
+  S (const S &x) { s = x.s + 1; }
+  S (const S &x, bool y) { s = x.s + 2; if (y) abort (); }
+  S () { s = 6; }
+  ~S ();
+};
+
+S::~S ()
+{
+  if (s < 6) abort ();
+  s = -1;
+  /* Ensure the above store is not DSEd.  */
+  asm volatile ("" : : "r" (&s) : "memory");
+}
+
+void
+bar (S &x)
+{
+  if (x.s != 6) abort ();
+  x.s = 15;
+}
+
+#pragma omp declare reduction (foo: S: omp_out.foo (omp_in)) \
+	initializer (omp_priv (omp_orig, false))
+#pragma omp declare reduction (foo: char, int, short: omp_out += omp_in - 4) \
+	initializer (omp_priv (4))
+#pragma omp declare reduction (+: S: omp_out.foo (omp_in, false)) \
+	initializer (omp_priv (omp_orig))
+
+namespace N
+{
+  #pragma omp declare reduction (foo: S: omp_out.foo (omp_in)) \
+	initializer (::bar (omp_priv))
+  namespace M {}
+}
+
+int
+main ()
+{
+  S a, b, c, s, t, u;
+  if (a.s != 6 || b.s != 6 || c.s != 6
+      || s.s != 6 || t.s != 6 || u.s != 6) abort ();
+  s.s = 9; t.s = 10; u.s = 11;
+  int d = 0, e = 0, f = 0, g = 0, h = 30, v = 2, q = 0;
+  #pragma omp declare reduction (foo: S: omp_out.foo (omp_in, true)) \
+	initializer (omp_priv = omp_orig)
+  {
+    #pragma omp declare reduction (foo: S: omp_out.foo (omp_in, false)) \
+	initializer (omp_priv = omp_orig)
+    #pragma omp parallel num_threads (4) reduction (N::operator +: q) \
+	reduction (operator +: a, d) reduction (::operator +: b, e) \
+	reduction (+: c, f) reduction (::N::M::operator +: g) \
+	reduction (::N::min: h) reduction (foo: s) reduction (N::foo: t) \
+	reduction (::foo: u) reduction (::foo: v)
+    {
+      if (a.s != 7 || b.s != 7 || c.s != 7
+	  || s.s != 10 || t.s != 15 || u.s != 13
+	  || v != 4 || d || e || f || g || h != __INT_MAX__) abort ();
+      asm volatile ("" : "+m" (a.s), "+m" (b.s));
+      asm volatile ("" : "+m" (c.s), "+r" (d));
+      asm volatile ("" : "+r" (e), "+r" (f));
+      asm volatile ("" : "+r" (g), "+r" (h));
+      asm volatile ("" : "+m" (s.s), "+m" (t.s));
+      asm volatile ("" : "+m" (u.s), "+r" (v));
+      a.s++; b.s++; c.s++; d++; e++; f++; g++; h = t.s;
+      s.s++; t.s++; u.s++; v++; q++;
+    }
+  }
+  if (a.s != 6 + q * 8 || b.s != 6 + q * 8 || c.s != 6 + q * 8
+      || d != q || e != q || f != q || g != q || h != 15
+      || s.s != 9 + q * 11 || t.s != 10 + q * 16 || u.s != 11 + q * 14
+      || v != 2 + q)
+    abort ();
+}
--- libgomp/testsuite/libgomp.c++/udr-2.C.jj	2013-08-22 13:53:23.899460855 +0200
+++ libgomp/testsuite/libgomp.c++/udr-2.C	2013-08-22 13:57:42.000000000 +0200
@@ -0,0 +1,89 @@ 
+// { dg-do run }
+
+extern "C" void abort ();
+
+namespace NS
+{
+  struct U
+  {
+    void foo (U &, bool);
+    U ();
+  };
+  struct S
+  {
+    int s;
+    #pragma omp declare reduction (foo : U, S : omp_out.foo (omp_in, false)) \
+	initializer (omp_priv ())
+    #pragma omp declare reduction (foo : int : omp_out += omp_in) \
+	initializer (omp_priv = int ())
+    void baz (int v)
+    {
+      S s;
+      int q = 0;
+      if (s.s != 6 || v != 0) abort ();
+      s.s = 20;
+      #pragma omp parallel num_threads (4) reduction (foo : s, v) \
+	reduction (::NS::U::operator + : q)
+      {
+	if (s.s != 6 || q != 0 || v != 0) abort ();
+	asm volatile ("" : "+m" (s.s), "+r" (q), "+r" (v));
+	s.s++; q++; v++;
+      }
+      if (s.s != 20 + q * 7 || q != v) abort ();
+    }
+    void foo (S &x) { s += x.s; }
+    void foo (S &x, bool y) { s += x.s; if (y) abort (); }
+    S (const S &x) { s = x.s + 1; }
+    S (const S &x, bool y) { s = x.s + 2; if (y) abort (); }
+    S () { s = 6; }
+    S (int x) { s = x; }
+    ~S ();
+  };
+  #pragma omp declare reduction (bar : S : omp_out.foo (omp_in)) \
+	initializer (omp_priv (8))
+}
+
+NS::S::~S ()
+{
+  if (s < 6) abort ();
+  s = -1;
+  /* Ensure the above store is not DSEd.  */
+  asm volatile ("" : : "r" (&s) : "memory");
+}
+
+struct T : public NS::S
+{
+  void baz ()
+  {
+    S s;
+    int q = 0;
+    if (s.s != 6) abort ();
+    #pragma omp parallel num_threads (4) reduction (foo:s) \
+	reduction (+: q)
+    {
+      if (s.s != 6 || q != 0) abort ();
+      asm volatile ("" : "+m" (s.s), "+r" (q));
+      s.s += 2; q++;
+    }
+    if (s.s != 6 + q * 8) abort ();
+  }
+};
+
+int
+main ()
+{
+  NS::S s;
+  s.baz (0);
+  T t;
+  t.baz ();
+  int q = 0;
+  if (s.s != 6) abort ();
+  // Test ADL
+  #pragma omp parallel num_threads (4) reduction (bar:s) reduction (+:q)
+  {
+    if (s.s != 8 || q != 0) abort ();
+    asm volatile ("" : "+m" (s.s), "+r" (q));
+    s.s += 4; q++;
+  }
+  if (s.s != 6 + q * 12) abort ();
+}
--- libgomp/testsuite/libgomp.c++/udr-3.C.jj	2013-08-26 10:37:03.355891787 +0200
+++ libgomp/testsuite/libgomp.c++/udr-3.C	2013-08-27 14:42:33.000000000 +0200
@@ -0,0 +1,150 @@ 
+// { dg-do run }
+
+extern "C" void abort ();
+
+void
+dblinit (double *p)
+{
+  *p = 2.0;
+}
+
+namespace NS
+{
+  template <int N>
+  struct U
+  {
+    void foo (U &, bool);
+    U ();
+  };
+  template <int N>
+  struct S
+  {
+    int s;
+    #pragma omp declare reduction (foo : U<0>, S : omp_out.foo (omp_in, false)) \
+	initializer (omp_priv ())
+    #pragma omp declare reduction (foo : int : omp_out += omp_in) \
+	initializer (omp_priv = N + 2)
+    #pragma omp declare reduction (foo : double : omp_out += omp_in) \
+	initializer (dblinit (&omp_priv))
+    void baz (int v)
+    {
+      S s;
+      int q = 0;
+      if (s.s != 6 || v != 0) abort ();
+      s.s = 20;
+      double d = 4.0;
+      #pragma omp parallel num_threads (4) reduction (foo : s, v, d) \
+	reduction (::NS::U<N>::operator + : q)
+      {
+	if (s.s != 6 || q != 0 || v != N + 2 || d != 2.0) abort ();
+	asm volatile ("" : "+m" (s.s), "+r" (q), "+r" (v));
+	s.s++; q++; v++;
+      }
+      if (s.s != 20 + q * 7 || (N + 3) * q != v || d != 4.0 + 2.0 * q)
+	abort ();
+    }
+    void foo (S &x) { s += x.s; }
+    void foo (S &x, bool y) { s += x.s; if (y) abort (); }
+    S (const S &x) { s = x.s + 1; }
+    S (const S &x, bool y) { s = x.s + 2; if (y) abort (); }
+    S () { s = 6; }
+    S (int x) { s = x; }
+    ~S ();
+  };
+  #pragma omp declare reduction (bar : S<1> : omp_out.foo (omp_in)) \
+	initializer (omp_priv (8))
+}
+
+template <int N>
+NS::S<N>::~S ()
+{
+  if (s < 6) abort ();
+  s = -1;
+  /* Ensure the above store is not DSEd.  */
+  asm volatile ("" : : "r" (&s) : "memory");
+}
+
+template <int N>
+struct T : public NS::S<N>
+{
+  void baz ()
+  {
+    NS::S<N> s;
+    int q = 0;
+    if (s.s != 6) abort ();
+    #pragma omp parallel num_threads (4) reduction (foo:s) \
+	reduction (+: q)
+    {
+      if (s.s != 6 || q != 0) abort ();
+      asm volatile ("" : "+m" (s.s), "+r" (q));
+      s.s += 2; q++;
+    }
+    if (s.s != 6 + q * 8) abort ();
+  }
+};
+
+struct W
+{
+  int v;
+  W () : v (6) {}
+  ~W () {}
+};
+
+template <typename T, typename D>
+struct V
+{
+  #pragma omp declare reduction (baz: T: omp_out.s += omp_in.s) \
+	initializer (omp_priv (11))
+  #pragma omp declare reduction (baz: D: omp_out += omp_in) \
+	initializer (dblinit (&omp_priv))
+  static void dblinit (D *x) { *x = 3.0; }
+  void baz ()
+  {
+    T t;
+    V v;
+    int q = 0;
+    D d = 4.0;
+    if (t.s != 6 || v.v != 4) abort ();
+    #pragma omp declare reduction (+ : V, W : omp_out.v -= omp_in.v) \
+	initializer (omp_priv (12))
+    {
+      #pragma omp declare reduction (+ : W, V : omp_out.v += omp_in.v) \
+	initializer (omp_priv (9))
+      #pragma omp parallel num_threads (4) reduction (+: v, q) \
+	reduction (baz: t, d)
+      {
+	if (t.s != 11 || v.v != 9 || q != 0 || d != 3.0) abort ();
+        asm volatile ("" : "+m" (t.s), "+m" (v.v), "+r" (q));
+	t.s += 2; v.v += 3; q++;
+      }
+      if (t.s != 6 + 13 * q || v.v != 4 + 12 * q || d != 4.0 + 3.0 * q)
+	abort ();
+    }
+  }
+  int v;
+  V () : v (4) {}
+  V (int x) : v (x) {}
+  ~V () {}
+};
+
+int
+main ()
+{
+  NS::S<0> u;
+  u.baz (0);
+  T<2> t;
+  t.baz ();
+  NS::S<1> s;
+  int q = 0;
+  if (s.s != 6) abort ();
+  // Test ADL
+  #pragma omp parallel num_threads (4) reduction (bar:s) reduction (+:q)
+  {
+    if (s.s != 8 || q != 0) abort ();
+    asm volatile ("" : "+m" (s.s), "+r" (q));
+    s.s += 4; q++;
+  }
+  if (s.s != 6 + q * 12) abort ();
+  V <NS::S <0>, double> v;
+  v.baz ();
+}
--- libgomp/testsuite/libgomp.c++/udr-4.C.jj	2013-08-27 15:33:11.612191890 +0200
+++ libgomp/testsuite/libgomp.c++/udr-4.C	2013-08-27 17:38:00.000000000 +0200
@@ -0,0 +1,33 @@ 
+// { dg-do run }
+
+extern "C" void abort ();
+
+struct S
+{
+  int s;
+  S () : s (0) {}
+  ~S () {}
+};
+
+#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) \
+  initializer (omp_priv ())
+#pragma omp declare reduction (foo:S:omp_out.s += omp_in.s)
+#pragma omp declare reduction (foo:int:omp_out += omp_in)
+
+int
+main ()
+{
+  int i, u = 0, q = 0;
+  S s, t;
+  if (s.s != 0 || t.s != 0) abort ();
+  #pragma omp parallel reduction(+:s, q) reduction(foo:t, u)
+  {
+    if (s.s != 0 || t.s != 0 || u != 0 || q != 0) abort ();
+    s.s = 6;
+    t.s = 8;
+    u = 9;
+    q++;
+  }
+  if (s.s != 6 * q || t.s != 8 * q || u != 9 * q) abort ();
+  return 0;
+}