diff mbox

[gomp4] C++ OpenMP user defined reductions (take 2)

Message ID 20130912085544.GS1817@tucnak.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek Sept. 12, 2013, 8:55 a.m. UTC
On Tue, Aug 27, 2013 at 06:57:06PM +0200, Jakub Jelinek wrote:
> 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.

Here is an updated patch, I got the remaining 13 questions answered too
(though one has is still fuzzy) and posted 2 further questions for which
I've just assumed a resolution.

Changes since the last patch:
1) mangled type name is appended to the UDR name, because UDRs for different
   types are supposed to be independent, UDR with the same
   reduction-identifier, but for unrelated type is not supposed to hide
   UDR in some later scope
2) UDRs in classes are parsed before any other pending inline function
   bodies (so that those bodies can reference those UDRs) - we
   "inline" the UDRs during finish_omp_clauses already
3) diagnostics had been added for invalid reduction-identifier
   id-expressions in reduction clause, e.g. reduction (S::~S: something)
4) initializer (omp_priv) and initializer (omp_priv ()) are now rejected
5) base types handled (if lookup using the original type fails, we continue
   asking for base types)
6) fixed handling of is_invisiref_parm decls in reduction clauses
7) added handling of reference type decls in reduction clauses

What is still missing is accessibility checking, in the attached udr5.C
I'd assume we should error on the dg-error marked lines (because one of the
UDRs is protected and another one is private).  Not sure what I'm doing
wrong that it doesn't complain.

Jason, does this look ok for the branch?

2013-09-12  Jakub Jelinek  <jakub@redhat.com>

gcc/
	* tree.h (OMP_CLAUSE_REDUCTION_OMP_ORIG_REF): Define.
	* omp-low.c (lower_rec_simd_input_clauses): Don't set DECL_VALUE_EXPR
	on new_var if it is not a DECL_P.
	(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.  Handle C++ references in
	OMP_CLAUSE_REDUCTION clauses.
	(lower_reduction_clauses): Adjust comment for UDRs.  Handle
	C++ references in OMP_CLAUSE_REDUCTION clauses.
	(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.
	* gimplify.c (omp_add_variable): Ignore GOVD_LOCAL decls for which
	privatize_by_reference returns true.
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_class_specifier_1): Handle UDRs before all
	other function bodies.
	(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 (cp_genericize_r): Handle invisiref parm decls
	in OMP_CLAUSE_REDUCTION.
	(cxx_omp_privatize_by_reference): Return true also for decls with
	REFERENCE_TYPE.
	(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, omp_reduction_lookup,
	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.  Handle decls with REFERENCE_TYPE type.
gcc/fortran/
	* trans-openmp.c (gfc_omp_clause_default_ctor,
	gfc_omp_clause_dtor): Return NULL for OMP_CLAUSE_REDUCTION.
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.
	* g++.dg/gomp/udr-4.C: New test.
libgomp/
	* testsuite/libgomp.c++/simd-4.C: New test.
	* testsuite/libgomp.c++/simd-5.C: New test.
	* testsuite/libgomp.c++/simd-6.C: New test.
	* testsuite/libgomp.c++/simd-7.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.
	* testsuite/libgomp.c++/udr-5.C: New test.
	* testsuite/libgomp.c++/udr-6.C: New test.
	* testsuite/libgomp.c++/udr-7.C: New test.
	* testsuite/libgomp.c++/udr-8.C: New test.



	Jakub
// { dg-do compile }

struct S
{
  int s;
  S () : s (0) {}
private:
  #pragma omp declare reduction (+:S:omp_out.s += omp_in.s)
protected:
  #pragma omp declare reduction (-:S:omp_out.s += omp_in.s)
};

struct T : public S
{
  void foo ()
  {
    S s;
    #pragma omp parallel reduction (S::operator +:s)	// { dg-error "" }
    s.s = 1;
    S t;
    #pragma omp parallel reduction (S::operator -:t)
    t.s = 1;
    S u;
    #pragma omp parallel reduction (+:u)		// { dg-error "" }
    u.s = 1;
    S v;
    #pragma omp parallel reduction (-:v)
    v.s = 1;
  }
};

void
foo ()
{
  S s;
  #pragma omp parallel reduction (S::operator +:s)	// { dg-error "" }
  s.s = 1;
  S t;
  #pragma omp parallel reduction (S::operator -:t)	// { dg-error "" }
  t.s = 1;
}

Comments

Jason Merrill Sept. 20, 2013, 4 p.m. UTC | #1
On 09/12/2013 04:55 AM, Jakub Jelinek wrote:
> -      if (t1 != t2)
> +      if (t1 != t2 && !DECL_OMP_DECLARE_REDUCTION_P (newdecl))
>   	return 0;

What's the theory here?  Why should decls_match return true for 
reductions with mismatching templates?

> +	  && ! (DECL_OMP_DECLARE_REDUCTION_P (newdecl)
> +		&& DECL_CONTEXT (newdecl) == NULL_TREE
> +		&& DECL_CONTEXT (olddecl) == current_function_decl))

And this looks like you need to set DECL_CONTEXT sooner on reductions.

> +	    if (TREE_CODE (argtype) == REFERENCE_TYPE)
> +	      error_at (DECL_SOURCE_LOCATION (t),
> +			"function, array or reference type in "
> +			"%<#pragma omp declare reduction%>");

Let's just say "reference type", since we know that's what it is.

> +			 && DECL_CONTEXT (pattern_decl)
> +			 && TREE_CODE (DECL_CONTEXT (pattern_decl))
> +			    == FUNCTION_DECL)

This is DECL_FUNCTION_SCOPE_P.

> +  return TREE_CODE (TREE_TYPE (decl)) == REFERENCE_TYPE
> +	 || is_invisiref_parm (decl);

Needs parens to protect indentation.

> +/* Instantiate the special body of the artificial DECL_OMP_DECLARE_REDUCTION
> +   function.  */

We could use documentation of what this special body looks like, either 
here, in cp_check_omp_declare_reduction, or elsewhere.

> +      for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++)
> +	{
> +	  id = omp_reduction_lookup (loc, orig_id, BINFO_TYPE (base_binfo));
> +	  if (id != NULL_TREE)
> +	    return id;

This should check for ambiguity rather than returning the first match.

> +   Also append INIT_EXPR for DECL_INITIAL of omp_priv after its
> +   DECL_EXPR.  */

Why not let the DECL_EXPR handle initialization?

Let's break out the finish_omp_clauses reduction code into a separate 
function, as it's rather large.

> +		    if (DECL_TEMPLATE_INFO (id))
> +		      id = instantiate_decl (id, /*defer_ok*/0, true);

Let's use mark_used instead.

> +		    if (TREE_CODE (body) == STATEMENT_LIST)
> +		      {

Maybe this should be an assert?

Jason
Jakub Jelinek Sept. 20, 2013, 4:25 p.m. UTC | #2
Hi!

On Fri, Sep 20, 2013 at 12:00:41PM -0400, Jason Merrill wrote:

Thanks for the review, I'll try to get to most of that next week.

> On 09/12/2013 04:55 AM, Jakub Jelinek wrote:
> >-      if (t1 != t2)
> >+      if (t1 != t2 && !DECL_OMP_DECLARE_REDUCTION_P (newdecl))
> >  	return 0;
> 
> What's the theory here?  Why should decls_match return true for
> reductions with mismatching templates?

In templates the UDRs are always FUNCTION_DECLs in classes or
at function block scope, the above one liner was I believe for the latter,
where without it duplicate_decls was returning incorrectly 0; the UDRs
from mismatching templates would actually never be seen by duplicate_decls,
but t1 was different from t2.  That was before the changes to
add the mangled names to the UDR DECL_NAMEs though, I can try to remove it
and see if the whole testsuite still passes.

> >+	  && ! (DECL_OMP_DECLARE_REDUCTION_P (newdecl)
> >+		&& DECL_CONTEXT (newdecl) == NULL_TREE
> >+		&& DECL_CONTEXT (olddecl) == current_function_decl))
> 
> And this looks like you need to set DECL_CONTEXT sooner on reductions.

I know it is ugly, but setting FUNCTION_DECL DECL_CONTEXT too early resulted
in all kinds of problems, when the C++ frontend doesn't support nested
functions.  So the patch doesn't set DECL_CONTEXT until it is pushdecled
into the block scope.  The combiner/initializer expressions can't use vars
other than omp_{out,in,priv,orig}, so the UDRs aren't really nested in the
C nested function sense, and after all, after parsing the FUNCTION_DECL
for UDRs is just a container holding the expressions and artificial
VAR_DECLs, and is a FUNCTION_DECL so that normal C++ lookups can be
performed on it.

But like with the above, perhaps it isn't needed anymore, as with the
mangled type names in DECL_NAMEs we shouldn't really use overloads for UDRs.

> >+	    if (TREE_CODE (argtype) == REFERENCE_TYPE)
> >+	      error_at (DECL_SOURCE_LOCATION (t),
> >+			"function, array or reference type in "
> >+			"%<#pragma omp declare reduction%>");
> 
> Let's just say "reference type", since we know that's what it is.

That is true, but I wanted to match the same error message elsewhere,
otherwise the error will be different (more specific) for instantiation
vs. in non-template code.  Though, I could of course in that second spot
just special case REFERENCE_TYPE with a separate error message
and just have one about function or array type.
> 
> >+			 && DECL_CONTEXT (pattern_decl)
> >+			 && TREE_CODE (DECL_CONTEXT (pattern_decl))
> >+			    == FUNCTION_DECL)
> 
> This is DECL_FUNCTION_SCOPE_P.
>
> >+  return TREE_CODE (TREE_TYPE (decl)) == REFERENCE_TYPE
> >+	 || is_invisiref_parm (decl);
> 
> Needs parens to protect indentation.
> 
> >+/* Instantiate the special body of the artificial DECL_OMP_DECLARE_REDUCTION
> >+   function.  */
> 
> We could use documentation of what this special body looks like,
> either here, in cp_check_omp_declare_reduction, or elsewhere.

3xOk.
> 
> >+      for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++)
> >+	{
> >+	  id = omp_reduction_lookup (loc, orig_id, BINFO_TYPE (base_binfo));
> >+	  if (id != NULL_TREE)
> >+	    return id;
> 
> This should check for ambiguity rather than returning the first match.

I believe I need to discuss that on omp-lang, what exactly is the intended
behavior (and get the standard clarified).  Returning the first base class
is an option (and, depth-first vs. breadth-first?), or erroring out if more
than one base class has an UDR is another.

> >+   Also append INIT_EXPR for DECL_INITIAL of omp_priv after its
> >+   DECL_EXPR.  */
> 
> Why not let the DECL_EXPR handle initialization?

I can try that.

> Let's break out the finish_omp_clauses reduction code into a
> separate function, as it's rather large.

Will try that.
> 
> >+		    if (DECL_TEMPLATE_INFO (id))
> >+		      id = instantiate_decl (id, /*defer_ok*/0, true);
> 
> Let's use mark_used instead.

Will that always instantiate it?

	Jakub
Jason Merrill Sept. 21, 2013, 11:45 a.m. UTC | #3
On 09/20/2013 12:25 PM, Jakub Jelinek wrote:
> In templates the UDRs are always FUNCTION_DECLs in classes or
> at function block scope, the above one liner was I believe for the latter,
> where without it duplicate_decls was returning incorrectly 0; the UDRs
> from mismatching templates would actually never be seen by duplicate_decls,
> but t1 was different from t2.  That was before the changes to
> add the mangled names to the UDR DECL_NAMEs though, I can try to remove it
> and see if the whole testsuite still passes.

Please.

>>> +	  && ! (DECL_OMP_DECLARE_REDUCTION_P (newdecl)
>>> +		&& DECL_CONTEXT (newdecl) == NULL_TREE
>>> +		&& DECL_CONTEXT (olddecl) == current_function_decl))
>>
>> And this looks like you need to set DECL_CONTEXT sooner on reductions.
>
> I know it is ugly, but setting FUNCTION_DECL DECL_CONTEXT too early resulted
> in all kinds of problems, when the C++ frontend doesn't support nested
> functions.  So the patch doesn't set DECL_CONTEXT until it is pushdecled
> into the block scope.

What is calling decls_match before pushdecl?

>>> +	    if (TREE_CODE (argtype) == REFERENCE_TYPE)
>>> +	      error_at (DECL_SOURCE_LOCATION (t),
>>> +			"function, array or reference type in "
>>> +			"%<#pragma omp declare reduction%>");
>>
>> Let's just say "reference type", since we know that's what it is.
>
> That is true, but I wanted to match the same error message elsewhere,
> otherwise the error will be different (more specific) for instantiation
> vs. in non-template code.

It's more important to be specific during instantiation because we can't 
always tell what the types involved actually are.  So let's also add the 
type in question to the diagnostic.

> Though, I could of course in that second spot
> just special case REFERENCE_TYPE with a separate error message
> and just have one about function or array type.

That would be fine too, of course.

>>> +      for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++)
>>> +	{
>>> +	  id = omp_reduction_lookup (loc, orig_id, BINFO_TYPE (base_binfo));
>>> +	  if (id != NULL_TREE)
>>> +	    return id;
>>
>> This should check for ambiguity rather than returning the first match.
>
> I believe I need to discuss that on omp-lang, what exactly is the intended
> behavior (and get the standard clarified).  Returning the first base class
> is an option (and, depth-first vs. breadth-first?), or erroring out if more
> than one base class has an UDR is another.

Normal C++ lookup behavior is to check for ambiguity, so I think that's 
the best bet for what the eventual defined semantics will be.

>>> +		    if (DECL_TEMPLATE_INFO (id))
>>> +		      id = instantiate_decl (id, /*defer_ok*/0, true);
>>
>> Let's use mark_used instead.
>
> Will that always instantiate it?

In contexts where that makes sense, which I would expect to be all 
contexts where you can see a reduction.

Jason
diff mbox

Patch

--- gcc/omp-low.c.jj	2013-09-06 16:39:15.256197473 +0200
+++ gcc/omp-low.c	2013-09-11 18:19:39.099359153 +0200
@@ -2870,8 +2870,11 @@  lower_rec_simd_input_clauses (tree new_v
 		 NULL_TREE, NULL_TREE);
   lvar = build4 (ARRAY_REF, TREE_TYPE (new_var), avar, lane,
 		 NULL_TREE, NULL_TREE);
-  SET_DECL_VALUE_EXPR (new_var, lvar);
-  DECL_HAS_VALUE_EXPR_P (new_var) = 1;
+  if (DECL_P (new_var))
+    {
+      SET_DECL_VALUE_EXPR (new_var, lvar);
+      DECL_HAS_VALUE_EXPR_P (new_var) = 1;
+    }
   return true;
 }
 
@@ -2887,6 +2890,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);
@@ -2906,9 +2910,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:
@@ -2950,9 +2951,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)
@@ -3053,10 +3057,7 @@  lower_rec_input_clauses (tree clauses, g
 		 allocate new backing storage for the new pointer
 		 variable.  This allows us to avoid changing all the
 		 code that expects a pointer to something that expects
-		 a direct variable.  Note that this doesn't apply to
-		 C++, since reference types are disallowed in data
-		 sharing clauses there, except for NRV optimized
-		 return values.  */
+		 a direct variable.  */
 	      if (pass == 0)
 		continue;
 
@@ -3142,19 +3143,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)
 			{
@@ -3171,8 +3173,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:
@@ -3319,19 +3321,89 @@  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))
+		  if (is_reference (var)
+		      && !useless_type_conversion_p (TREE_TYPE (placeholder),
+						     TREE_TYPE (x)))
 		    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));
+		  tree new_vard = new_var;
+		  if (is_reference (var))
+		    {
+		      gcc_assert (TREE_CODE (new_var) == MEM_REF);
+		      new_vard = TREE_OPERAND (new_var, 0);
+		      gcc_assert (DECL_P (new_vard));
+		    }
+		  if (is_simd
+		      && lower_rec_simd_input_clauses (new_var, ctx, max_vf,
+						       idx, lane, ivar, lvar))
+		    {
+		      if (new_vard == new_var)
+			{
+			  gcc_assert (DECL_VALUE_EXPR (new_var) == lvar);
+			  SET_DECL_VALUE_EXPR (new_var, ivar);
+			}
+		      else
+			{
+			  SET_DECL_VALUE_EXPR (new_vard,
+					       build_fold_addr_expr (ivar));
+			  DECL_HAS_VALUE_EXPR_P (new_vard) = 1;
+			}
+		      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]);
+		      if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c))
+			{
+			  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;
+		      if (new_vard == new_var)
+			SET_DECL_VALUE_EXPR (new_var, lvar);
+		      else
+			SET_DECL_VALUE_EXPR (new_vard,
+					     build_fold_addr_expr (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;
+		    }
+		  x = lang_hooks.decls.omp_clause_default_ctor
+				(c, new_var, unshare_expr (x));
+		  if (x)
+		    gimplify_and_add (x, ilist);
+		  if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c))
+		    {
+		      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
 		{
@@ -3431,8 +3503,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.  */
@@ -3621,7 +3694,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;
 	  }
@@ -3668,7 +3741,9 @@  lower_reduction_clauses (tree clauses, g
 	{
 	  tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c);
 
-	  if (is_reference (var))
+	  if (is_reference (var)
+	      && !useless_type_conversion_p (TREE_TYPE (placeholder),
+					     TREE_TYPE (ref)))
 	    ref = build_fold_addr_expr_loc (clause_loc, ref);
 	  SET_DECL_VALUE_EXPR (placeholder, ref);
 	  DECL_HAS_VALUE_EXPR_P (placeholder) = 1;
@@ -9097,7 +9172,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);
 
@@ -9125,10 +9200,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.  */
@@ -9174,6 +9250,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.h.jj	2013-09-05 09:19:03.309844499 +0200
+++ gcc/tree.h	2013-09-09 17:43:11.241759508 +0200
@@ -632,6 +632,9 @@  struct GTY(()) tree_base {
        OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION in
 	   OMP_CLAUSE_MAP
 
+       OMP_CLAUSE_REDUCTION_OMP_ORIG_REF in
+	   OMP_CLAUSE_REDUCTION
+
        TRANSACTION_EXPR_RELAXED in
 	   TRANSACTION_EXPR
 
@@ -1974,6 +1977,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/fortran/trans-openmp.c.jj	2013-06-26 12:15:38.000000000 +0200
+++ gcc/fortran/trans-openmp.c	2013-09-11 11:29:56.722018716 +0200
@@ -159,6 +159,9 @@  gfc_omp_clause_default_ctor (tree clause
       || GFC_TYPE_ARRAY_AKIND (type) != GFC_ARRAY_ALLOCATABLE)
     return NULL;
 
+  if (OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_REDUCTION)
+    return NULL;
+
   gcc_assert (outer != NULL);
   gcc_assert (OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_PRIVATE
 	      || OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_LASTPRIVATE);
@@ -323,6 +326,9 @@  gfc_omp_clause_dtor (tree clause ATTRIBU
       || GFC_TYPE_ARRAY_AKIND (type) != GFC_ARRAY_ALLOCATABLE)
     return NULL;
 
+  if (OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_REDUCTION)
+    return NULL;
+
   /* Allocatable arrays in FIRSTPRIVATE/LASTPRIVATE etc. clauses need
      to be deallocated if they were allocated.  */
   return gfc_trans_dealloc_allocated (decl, false, NULL);
--- gcc/cp/decl.c.jj	2013-08-27 20:50:53.458551851 +0200
+++ gcc/cp/decl.c	2013-09-09 17:43:11.302759290 +0200
@@ -978,12 +978,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
@@ -1419,6 +1422,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-09-05 09:19:03.316844461 +0200
+++ gcc/cp/parser.c	2013-09-11 18:36:43.140063780 +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
@@ -19058,8 +19061,19 @@  cp_parser_class_specifier_1 (cp_parser*
       if (pushed_scope)
 	pop_scope (pushed_scope);
       /* Now parse the body of the functions.  */
-      FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_definitions, ix, decl)
-	cp_parser_late_parsing_for_member (parser, decl);
+      if (flag_openmp)
+        {
+	  /* OpenMP UDRs need to be parsed before all other functions.  */
+          FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_definitions, ix, decl)
+	    if (DECL_OMP_DECLARE_REDUCTION_P (decl))
+	      cp_parser_late_parsing_for_member (parser, decl);
+          FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_definitions, ix, decl)
+	    if (!DECL_OMP_DECLARE_REDUCTION_P (decl))
+	      cp_parser_late_parsing_for_member (parser, decl);
+        }
+      else
+        FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_definitions, ix, decl)
+          cp_parser_late_parsing_for_member (parser, decl);
       vec_safe_truncate (unparsed_funs_with_definitions, 0);
     }
 
@@ -22972,9 +22986,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 ();
@@ -26847,70 +26870,91 @@  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;
+      saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
+      parser->colon_corrects_to_scope_p = false;
+      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, NULL_TREE);
+	  tree scope = parser->scope;
+	  if (scope)
+	    id = build_qualified_name (NULL_TREE, scope, id, false);
+	  parser->scope = NULL_TREE;
+	  parser->qualifying_scope = NULL_TREE;
+	  parser->object_scope = NULL_TREE;
+	}
+      else
+	{
+	  error ("invalid reduction-identifier");
+	 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;
@@ -26918,7 +26962,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;
 }
@@ -29737,10 +29784,378 @@  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 ();
+
+      bool ctor = false;
+      if (strcmp (p, "omp_priv") == 0)
+	{
+	  bool is_direct_init, is_non_constant_init;
+	  ctor = true;
+	  cp_lexer_consume_token (parser->lexer);
+	  /* Reject initializer (omp_priv) and initializer (omp_priv ()).  */
+	  if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)
+	      || (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
+		  && cp_lexer_peek_nth_token (parser->lexer, 2)->type
+		     == CPP_CLOSE_PAREN
+		  && cp_lexer_peek_nth_token (parser->lexer, 3)->type
+		     == CPP_CLOSE_PAREN))
+	    {
+	      finish_omp_structured_block (block);
+	      error ("invalid initializer clause");
+	      return false;
+	    }
+	  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);
+	}
+      else
+	{
+	  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 (ctor)
+	add_decl_expr (omp_orig);
+
+      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, NULL_TREE);
+  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 this_reduc_id = reduc_id;
+      if (!dependent_type_p (type))
+	this_reduc_id = omp_reduction_id (ERROR_MARK, reduc_id, type);
+      tree fndecl = build_lang_decl (FUNCTION_DECL, this_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
@@ -29760,13 +30175,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-27 20:50:55.833540151 +0200
+++ gcc/cp/pt.c	2013-09-11 10:18:40.342254675 +0200
@@ -8905,6 +8905,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
 	    {
@@ -10352,6 +10355,24 @@  tsubst_decl (tree t, tree args, tsubst_f
 	  DECL_INITIAL (r) = NULL_TREE;
 	DECL_CONTEXT (r) = ctx;
 
+	/* 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%>");
+	    if (strchr (IDENTIFIER_POINTER (DECL_NAME (t)), '~') == NULL)
+	      DECL_NAME (r) = omp_reduction_id (ERROR_MARK, DECL_NAME (t),
+						argtype);
+	  }
+
 	if (member && DECL_CONV_FN_P (r))
 	  /* Type-conversion operator.  Reconstruct the name, in
 	     case it's the name of one of the template's parameters.  */
@@ -12747,7 +12768,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:
@@ -12770,6 +12790,26 @@  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))
+	    {
+	      tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc);
+	      if (TREE_CODE (placeholder) == SCOPE_REF)
+		{
+		  tree scope = tsubst (TREE_OPERAND (placeholder, 0), args,
+				       complain, in_decl);
+		  OMP_CLAUSE_REDUCTION_PLACEHOLDER (nc)
+		    = build_qualified_name (NULL_TREE, scope,
+					    TREE_OPERAND (placeholder, 1),
+					    false);
+		}
+	      else
+		gcc_assert (identifier_p (placeholder));
+	    }
+	  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)
@@ -13100,6 +13140,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;
@@ -13604,6 +13655,72 @@  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[7];
+  memset (stmts, 0, sizeof stmts);
+  for (i = 0, tsi = tsi_start (t);
+       i < 7 && !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);
+      if (i == 7)
+	add_decl_expr (omp_orig);
+    }
+}
+
 /* T is a postfix-expression that is not being used in a function
    call.  Return the substituted version of T.  */
 
@@ -19288,6 +19405,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.  */
@@ -19297,7 +19415,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
@@ -19334,21 +19456,37 @@  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;
 
       /* 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)
+	DECL_SAVED_TREE (d) = pop_stmt_list (block);
+      else
+	{
+	  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-tree.h.jj	2013-08-27 21:01:21.864303729 +0200
+++ gcc/cp/cp-tree.h	2013-09-09 18:11:39.656840266 +0200
@@ -1980,7 +1980,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
@@ -3183,6 +3184,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) \
@@ -5777,6 +5783,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, 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);
@@ -5801,7 +5810,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/cp-gimplify.c.jj	2013-08-27 20:50:54.802547089 +0200
+++ gcc/cp/cp-gimplify.c	2013-09-11 17:20:57.264555222 +0200
@@ -936,7 +936,16 @@  cp_genericize_r (tree *stmt_p, int *walk
 	  *walk_subtrees = 0;
 	break;
       case OMP_CLAUSE_REDUCTION:
-	gcc_assert (!is_invisiref_parm (OMP_CLAUSE_DECL (stmt)));
+	if (is_invisiref_parm (OMP_CLAUSE_DECL (stmt)))
+	  {
+	    *walk_subtrees = 0;
+	    if (OMP_CLAUSE_REDUCTION_INIT (stmt))
+	      cp_walk_tree (&OMP_CLAUSE_REDUCTION_INIT (stmt),
+			    cp_genericize_r, data, NULL);
+	    if (OMP_CLAUSE_REDUCTION_MERGE (stmt))
+	      cp_walk_tree (&OMP_CLAUSE_REDUCTION_MERGE (stmt),
+			    cp_genericize_r, data, NULL);
+	  }
 	break;
       default:
 	break;
@@ -1406,7 +1415,8 @@  cxx_omp_clause_dtor (tree clause, tree d
 bool
 cxx_omp_privatize_by_reference (const_tree decl)
 {
-  return is_invisiref_parm (decl);
+  return TREE_CODE (TREE_TYPE (decl)) == REFERENCE_TYPE
+	 || is_invisiref_parm (decl);
 }
 
 /* Return true if DECL is const qualified var having no mutable member.  */
@@ -1509,7 +1519,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-09-05 15:57:01.106488431 +0200
+++ gcc/cp/semantics.c	2013-09-11 13:02:49.970923223 +0200
@@ -4040,7 +4040,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;
@@ -4064,8 +4065,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)
@@ -4534,6 +4534,339 @@  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, tree type)
+{
+  const char *p = NULL;
+  const char *m = 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);
+    }
+
+  if (type != NULL_TREE)
+    m = mangle_type_string (TYPE_MAIN_VARIANT (type));
+
+  const char prefix[] = "omp declare reduction ";
+  size_t lenp = sizeof (prefix);
+  if (strncmp (p, prefix, lenp - 1) == 0)
+    lenp = 1;
+  size_t len = strlen (p);
+  size_t lenm = m ? strlen (m) + 1 : 0;
+  char *name = XALLOCAVEC (char, lenp + len + lenm);
+  if (lenp > 1)
+    memcpy (name, prefix, lenp - 1);
+  memcpy (name + lenp - 1, p, len + 1);
+  if (m)
+    {
+      name[lenp + len - 1] = '~';
+      memcpy (name + lenp + len, m, lenm);
+    }
+  return get_identifier (name);
+}
+
+/* Lookup OpenMP UDR ID for TYPE, return the corresponding artificial
+   FUNCTION_DECL or NULL_TREE if not found.  */
+
+static tree
+omp_reduction_lookup (location_t loc, tree id, tree type)
+{
+  tree orig_id = id;
+  if (identifier_p (id))
+    {
+      cp_id_kind idk;
+      bool nonint_cst_expression_p;
+      const char *error_msg;
+      id = omp_reduction_id (ERROR_MARK, id, type);
+      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, loc);
+      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);
+	}
+    }
+  else if (TREE_CODE (id) == SCOPE_REF)
+    id = lookup_qualified_name (TREE_OPERAND (id, 0),
+				omp_reduction_id (ERROR_MARK,
+						  TREE_OPERAND (id, 1),
+						  type),
+				false, false);
+  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 == NULL_TREE && CLASS_TYPE_P (type) && TYPE_BINFO (type))
+    {
+      tree binfo = TYPE_BINFO (type), base_binfo;
+      unsigned int ix;
+      for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++)
+	{
+	  id = omp_reduction_lookup (loc, orig_id, BINFO_TYPE (base_binfo));
+	  if (id != NULL_TREE)
+	    return id;
+	}
+    }
+  return id;
+}
+
+/* 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[7];
+  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++)
+	{
+	  tree id = omp_reduction_id (predef_codes[i], NULL_TREE, NULL_TREE);
+	  const char *n1 = IDENTIFIER_POINTER (DECL_NAME (udr));
+	  const char *n2 = IDENTIFIER_POINTER (id);
+	  if (strncmp (n1, n2, IDENTIFIER_LENGTH (id)) == 0
+	      && (n1[IDENTIFIER_LENGTH (id)] == '~'
+		  || n1[IDENTIFIER_LENGTH (id)] == '\0'))
+	    break;
+	}
+
+      if (i == 8
+	  && TREE_CODE (type) != COMPLEX_EXPR)
+	{
+	  const char prefix_minmax[] = "omp declare reduction m";
+	  size_t prefix_size = sizeof (prefix_minmax) - 1;
+	  const char *n = IDENTIFIER_POINTER (DECL_NAME (udr));
+	  if (strncmp (IDENTIFIER_POINTER (DECL_NAME (udr)),
+		       prefix_minmax, prefix_size) == 0
+	      && ((n[prefix_size] == 'i' && n[prefix_size + 1] == 'n')
+		  || (n[prefix_size] == 'a' && n[prefix_size + 1] == 'x'))
+	      && (n[prefix_size + 2] == '~' || n[prefix_size + 2] == '\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 < 7 && !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;
+      if (i == 7)
+	gcc_assert (TREE_CODE (data.stmts[6]) == DECL_EXPR);
+    }
+}
+
+/* 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 DECL.  */
+
+static tree
+clone_omp_udr (tree stmt, tree omp_decl1, tree omp_decl2,
+	       tree decl, 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) = decl;
+  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.  */
 
@@ -5059,6 +5392,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)
@@ -5071,12 +5405,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:
@@ -5124,34 +5460,228 @@  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;
+	    tree type = TREE_TYPE (t);
+	    if (TREE_CODE (type) == REFERENCE_TYPE)
+	      type = TREE_TYPE (type);
+	    if (ARITHMETIC_TYPE_P (type))
+	      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 (type) == 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 (type))
+		    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 (type) == ARRAY_TYPE
+		     || TYPE_READONLY (type))
+	      {
+		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 id = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c);
+
+		type = TYPE_MAIN_VARIANT (TREE_TYPE (t));
+		if (TREE_CODE (type) == REFERENCE_TYPE)
+		  type = TREE_TYPE (type);
+		OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL_TREE;
+		if (id == NULL_TREE)
+		  id = omp_reduction_id (OMP_CLAUSE_REDUCTION_CODE (c),
+					 NULL_TREE, NULL_TREE);
+		id = omp_reduction_lookup (OMP_CLAUSE_LOCATION (c), id, type);
+		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[7];
+			tree atype
+			  = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (id)));
+			atype = TREE_TYPE (atype);
+			bool need_static_cast = !same_type_p (type, atype);
+			memset (stmts, 0, sizeof stmts);
+			for (i = 0, tsi = tsi_start (body);
+			     i < 7 && !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]))
+				&& TREE_CODE (TREE_TYPE (OMP_CLAUSE_DECL (c)))
+				   != REFERENCE_TYPE)
+			      cxx_mark_addressable (OMP_CLAUSE_DECL (c));
+			    tree omp_out = placeholder;
+			    tree omp_in
+			      = convert_from_reference (OMP_CLAUSE_DECL (c));
+			    if (need_static_cast)
+			      {
+				tree ptype = build_pointer_type (atype);
+				omp_out = build_fold_addr_expr (omp_out);
+				omp_out
+				  = build_static_cast (ptype, omp_out,
+						       tf_warning_or_error);
+				omp_in = build_fold_addr_expr (omp_in);
+				omp_in
+				  = build_static_cast (ptype, omp_in,
+						       tf_warning_or_error);
+				if (omp_out == error_mark_node
+				    || omp_in == error_mark_node)
+				  {
+				    remove = true;
+				    break;
+				  }
+				omp_out
+				  = build1 (INDIRECT_REF, atype, omp_out);
+				omp_in
+				  = build1 (INDIRECT_REF, atype, omp_in);
+			      }
+			    OMP_CLAUSE_REDUCTION_MERGE (c)
+			      = clone_omp_udr (stmts[2],
+					       DECL_EXPR_DECL (stmts[0]),
+					       DECL_EXPR_DECL (stmts[1]),
+					       omp_in, omp_out);
+			  }
+			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);
+			    tree omp_priv
+			      = convert_from_reference (OMP_CLAUSE_DECL (c));
+			    tree omp_orig = placeholder;
+			    if (need_static_cast)
+			      {
+				if (i == 7)
+				  {
+				    error_at (OMP_CLAUSE_LOCATION (c),
+					      "user defined reduction with "
+					      "constructor initializer for "
+					      "base class %qT", atype);
+				    remove = true;
+				    break;
+				  }
+				tree ptype = build_pointer_type (atype);
+				omp_priv = build_fold_addr_expr (omp_priv);
+				omp_priv
+				  = build_static_cast (ptype, omp_priv,
+						       tf_warning_or_error);
+				omp_orig = build_fold_addr_expr (omp_orig);
+				omp_orig
+				  = build_static_cast (ptype, omp_orig,
+						       tf_warning_or_error);
+				if (omp_priv == error_mark_node
+				    || omp_orig == error_mark_node)
+				  {
+				    remove = true;
+				    break;
+				  }
+				omp_priv
+				  = build1 (INDIRECT_REF, atype, omp_priv);
+				omp_orig
+				  = build1 (INDIRECT_REF, atype, omp_orig);
+			      }
+			    if (i == 6)
+			      need_default_ctor = true;
+			    OMP_CLAUSE_REDUCTION_INIT (c)
+			      = clone_omp_udr (stmts[5],
+					       DECL_EXPR_DECL (stmts[4]),
+					       DECL_EXPR_DECL (stmts[3]),
+					       omp_priv, omp_orig);
+			    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 (CLASS_TYPE_P (type) && !pod_type_p (type))
+			      need_default_ctor = true;
+			    else
+			      {
+				tree init;
+				tree v = convert_from_reference (t);
+				if (AGGREGATE_TYPE_P (TREE_TYPE (v)))
+				  init = build_constructor (TREE_TYPE (v),
+							    NULL);
+				else
+				  init = fold_convert (TREE_TYPE (v),
+						       integer_zero_node);
+				OMP_CLAUSE_REDUCTION_INIT (c)
+				  = build2 (INIT_EXPR, TREE_TYPE (v), v, 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))
@@ -5213,15 +5743,21 @@  finish_omp_clauses (tree clauses)
       while (TREE_CODE (inner_type) == ARRAY_TYPE)
 	inner_type = TREE_TYPE (inner_type);
 
+      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION
+	  && TREE_CODE (inner_type) == REFERENCE_TYPE)
+	inner_type = TREE_TYPE (inner_type);
+
       /* Check for special function availability by building a call to one.
 	 Save the results, because later we won't be in the right context
 	 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/tree-pretty-print.c.jj	2013-09-09 17:43:11.278759395 +0200
+++ gcc/tree-pretty-print.c	2013-09-09 17:44:27.323364451 +0200
@@ -332,8 +332,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_colon (buffer);
+      if (OMP_CLAUSE_REDUCTION_CODE (clause) != ERROR_MARK)
+	{
+	  pp_string (buffer,
+		     op_symbol_code (OMP_CLAUSE_REDUCTION_CODE (clause)));
+	  pp_colon (buffer);
+	}
       dump_generic_node (buffer, OMP_CLAUSE_DECL (clause),
 			 spc, flags, false);
       pp_right_paren (buffer);
--- gcc/gimplify.c.jj	2013-09-06 14:47:13.000000000 +0200
+++ gcc/gimplify.c	2013-09-11 17:18:39.944280480 +0200
@@ -5894,10 +5894,9 @@  omp_add_variable (struct gimplify_omp_ct
 	       && DECL_P (TYPE_SIZE_UNIT (TREE_TYPE (decl))))
 	omp_notice_variable (ctx, TYPE_SIZE_UNIT (TREE_TYPE (decl)), true);
     }
-  else if ((flags & GOVD_MAP) == 0
+  else if ((flags & (GOVD_MAP | GOVD_LOCAL)) == 0
 	   && lang_hooks.decls.omp_privatize_by_reference (decl))
     {
-      gcc_assert ((flags & GOVD_LOCAL) == 0);
       omp_firstprivatize_type_sizes (ctx, TREE_TYPE (decl));
 
       /* Similar to the direct variable sized case above, we'll need the
--- gcc/testsuite/g++.dg/gomp/udr-1.C.jj	2013-09-09 17:43:11.367758947 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-1.C	2013-09-09 17:43:11.367758947 +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-3.C.jj	2013-09-09 17:43:11.367758947 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-3.C	2013-09-11 12:25:26.000000000 +0200
@@ -0,0 +1,191 @@ 
+// { 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)
+  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)
+  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;
+}
+
+namespace N7
+{
+  #pragma omp declare reduction (+: S: omp_out.s += omp_in.s) initializer (omp_priv) // { dg-error "invalid initializer clause" }
+  #pragma omp declare reduction (+: T: omp_out.t += omp_in.t) initializer (omp_priv ()) // { dg-error "invalid initializer clause" }
+}
+
+namespace N8
+{
+  struct A { int a; A (); ~A (); };
+  struct B { int b; B (); ~B (); B (int); };
+  struct C : public A, B { int c; C (); ~C (); };
+  #pragma omp declare reduction (+:B:omp_out.b += omp_in.b) initializer (omp_priv (4))
+  void bar (C &);
+  void baz ()
+  {
+    C a;
+    #pragma omp parallel reduction (+:a) // { dg-error "user defined reduction with constructor initializer for base class" }
+    bar (a);
+  }
+}
--- gcc/testsuite/g++.dg/gomp/clause-3.C.jj	2013-09-09 17:43:11.367758947 +0200
+++ gcc/testsuite/g++.dg/gomp/clause-3.C	2013-09-09 17:58:17.781026154 +0200
@@ -11,7 +11,7 @@  int t;
 void
 foo (int x)
 {
-  char *p;
+  char *pp;
   struct S { int i; int j; } s;
   char a[32];
   double d;
@@ -42,18 +42,18 @@  foo (int x)
     ;
 #pragma omp p firstprivate (bar) // { dg-error "is not a variable" }
     ;
-#pragma omp p reduction (+:p) // { 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-2.C.jj	2013-09-09 17:43:11.367758947 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-2.C	2013-09-09 17:43:11.367758947 +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-4.C.jj	2013-09-11 18:42:04.963355505 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-4.C	2013-09-11 18:41:54.000000000 +0200
@@ -0,0 +1,14 @@ 
+// { dg-do compile }
+
+struct S;					// { dg-error "forward declaration" }
+#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) // { dg-error "invalid use of incomplete type" }
+struct S { int s; S () : s (1) {} };
+#pragma omp declare reduction (*:S:omp_out.s *= omp_in.s)
+
+void
+foo ()
+{
+  S s;
+  #pragma omp parallel reduction (S::~S:s)	// { dg-error "invalid reduction-identifier" }
+  s.s = 1;
+}
--- libgomp/testsuite/libgomp.c++/udr-1.C.jj	2013-09-09 17:43:11.368758942 +0200
+++ libgomp/testsuite/libgomp.c++/udr-1.C	2013-09-09 17:43:11.368758942 +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-3.C.jj	2013-09-09 17:43:11.369758937 +0200
+++ libgomp/testsuite/libgomp.c++/udr-3.C	2013-09-11 12:06:42.960002990 +0200
@@ -0,0 +1,149 @@ 
+// { 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))
+    #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++/simd-4.C.jj	2013-09-09 17:43:11.368758942 +0200
+++ libgomp/testsuite/libgomp.c++/simd-4.C	2013-09-11 12:16:57.052674813 +0200
@@ -0,0 +1,45 @@ 
+// { 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)
+#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++/udr-7.C.jj	2013-09-11 17:37:36.341363569 +0200
+++ libgomp/testsuite/libgomp.c++/udr-7.C	2013-09-11 17:33:10.000000000 +0200
@@ -0,0 +1,72 @@ 
+// { dg-do run }
+
+extern "C" void abort ();
+
+struct S
+{
+  int s;
+  void foo (S &x) { s += x.s; }
+  S (const S &x) { s = x.s + 1; }
+  S () { s = 6; }
+  ~S () {}
+};
+
+void
+bar (S &x, S &y)
+{
+  if (x.s != 6 || y.s != 6)
+    abort ();
+  x.s = 8;
+}
+
+#pragma omp declare reduction (foo: S: omp_out.foo (omp_in)) \
+	initializer (omp_priv (omp_orig))
+#pragma omp declare reduction (bar : S: omp_out.foo (omp_in)) \
+	initializer (bar (omp_priv, omp_orig))
+
+S
+baz (S x)
+{
+  S r;
+  int i = 0;
+  if (x.s != 7 || r.s != 6)
+    abort ();
+  #pragma omp parallel reduction (foo: x) reduction (bar: r) \
+		       reduction (+: i)
+  {
+    if (x.s != 8 || r.s != 8)
+      abort ();
+    x.s = 12;
+    r.s = 14;
+    i = 1;
+  }
+  if (x.s != 7 + 12 * i || r.s != 6 + 14 * i)
+    abort ();
+  return r;
+}
+
+void
+baz (S &x, S &y)
+{
+  int i = 0, &j = i;
+  #pragma omp parallel reduction (foo: x) reduction (bar: y) \
+		       reduction (+: i)
+  {
+    if (x.s != 7 || y.s != 8)
+      abort ();
+    x.s = 12;
+    y.s = 14;
+    i = 1;
+  }
+  if (x.s != 6 + 12 * j || y.s != 6 + 14 * j)
+    abort ();
+}
+
+int
+main ()
+{
+  S s;
+  baz (s);
+  S t, u;
+  baz (t, u);
+}
--- libgomp/testsuite/libgomp.c++/simd-7.C.jj	2013-09-11 18:22:16.180549560 +0200
+++ libgomp/testsuite/libgomp.c++/simd-7.C	2013-09-11 18:25:58.836399590 +0200
@@ -0,0 +1,72 @@ 
+// { 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 (int x) : s (x) {}
+  ~S () {}
+};
+#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) \
+		    initializer (omp_priv (0))
+#pragma omp declare reduction (foo:S:omp_out.s += omp_in.s) \
+		    initializer (omp_priv (0))
+#pragma omp declare reduction (foo:int:omp_out += omp_in) \
+		    initializer (omp_priv = 0)
+
+__attribute__((noinline, noclone)) S
+foo (S s)
+{
+  int i, v = 0, &u = v;
+  S t;
+  #pragma omp simd aligned(a : 32) reduction(+:s) reduction(foo:t, u) \
+		   safelen(1)
+  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 t;
+}
+
+__attribute__((noinline, noclone)) int
+bar (S &s, S &t)
+{
+  int i, v = 0, &u = v;
+  #pragma omp simd aligned(a : 32) reduction(+:s) reduction(foo:t, u) \
+		   safelen(1)
+  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);
+  S q;
+  int s = foo (q).s;
+  if (s != 19456)
+    abort ();
+  S r, v;
+  if (bar (r, v) != s)
+    abort ();
+}
--- libgomp/testsuite/libgomp.c++/udr-2.C.jj	2013-09-09 17:43:11.369758937 +0200
+++ libgomp/testsuite/libgomp.c++/udr-2.C	2013-09-11 12:06:35.261046352 +0200
@@ -0,0 +1,88 @@ 
+// { 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))
+    #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-8.C.jj	2013-09-11 17:53:23.295568197 +0200
+++ libgomp/testsuite/libgomp.c++/udr-8.C	2013-09-11 17:53:11.000000000 +0200
@@ -0,0 +1,39 @@ 
+// { dg-do run }
+
+extern "C" void abort ();
+
+struct S;
+void foo (S *, S *);
+void bar (S &, S &);
+#pragma omp declare reduction (+:S:foo (&omp_out, &omp_in))
+#pragma omp declare reduction (*:S:bar (omp_out, omp_in))
+struct S { int s; S () : s (0) {} };
+
+void
+foo (S *x, S *y)
+{
+  x->s += y->s;
+}
+
+void
+bar (S &x, S &y)
+{
+  x.s += y.s;
+}
+
+int
+main ()
+{
+  S s, t;
+  int i;
+  #pragma omp parallel reduction (+:s, i) reduction (*:t)
+  {
+    if (s.s != 0 || t.s != 0)
+      abort ();
+    s.s = 2;
+    t.s = 3;
+    i = 1;
+  }
+  if (s.s != 2 * i || t.s != 3 * i)
+    abort ();
+}
--- libgomp/testsuite/libgomp.c++/simd-5.C.jj	2013-09-09 17:43:11.368758942 +0200
+++ libgomp/testsuite/libgomp.c++/simd-5.C	2013-09-11 12:17:06.042629927 +0200
@@ -0,0 +1,47 @@ 
+// { 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)
+#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-4.C.jj	2013-09-09 17:43:11.369758937 +0200
+++ libgomp/testsuite/libgomp.c++/udr-4.C	2013-09-11 12:07:02.964893591 +0200
@@ -0,0 +1,32 @@ 
+// { 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)
+#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;
+}
--- libgomp/testsuite/libgomp.c++/simd-6.C.jj	2013-09-11 17:54:49.046125920 +0200
+++ libgomp/testsuite/libgomp.c++/simd-6.C	2013-09-11 18:00:37.414316598 +0200
@@ -0,0 +1,70 @@ 
+// { 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 (int x) : s (x) {}
+  ~S () {}
+};
+#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) \
+		    initializer (omp_priv (0))
+#pragma omp declare reduction (foo:S:omp_out.s += omp_in.s) \
+		    initializer (omp_priv (0))
+#pragma omp declare reduction (foo:int:omp_out += omp_in) \
+		    initializer (omp_priv = 0)
+
+__attribute__((noinline, noclone)) S
+foo (S s)
+{
+  int i, v = 0, &u = v;
+  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 t;
+}
+
+__attribute__((noinline, noclone)) int
+bar (S &s, S &t)
+{
+  int i, v = 0, &u = v;
+  #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);
+  S q;
+  int s = foo (q).s;
+  if (s != 19456)
+    abort ();
+  S r, v;
+  if (bar (r, v) != s)
+    abort ();
+}
--- libgomp/testsuite/libgomp.c++/udr-6.C.jj	2013-09-11 17:37:33.174380830 +0200
+++ libgomp/testsuite/libgomp.c++/udr-6.C	2013-09-11 12:05:38.000000000 +0200
@@ -0,0 +1,70 @@ 
+// { dg-do run }
+
+extern "C" void abort ();
+
+struct A { int a; A () : a (6) {} };
+struct B { int b; B () : b (5) {} };
+struct C { int c; C () : c (4) {} };
+struct D { int d; D () : d (3) {} };
+struct E : A, B {};
+struct F : C, D {};
+struct G : E, F {};
+void foo (B &);
+void foo (F &);
+#pragma omp declare reduction (+:F:omp_out.c += omp_in.c) \
+		    initializer(foo (omp_priv))
+#pragma omp declare reduction (+:B:omp_out.b += omp_in.b) \
+		    initializer(foo (omp_priv))
+
+void
+foo (B &x)
+{
+  if (x.b != 5)
+    abort ();
+  x.b = 9;
+}
+
+template <typename T>
+void bar (T &x, T &y, int z)
+{
+  if (z)
+    abort ();
+  x.a += y.a;
+}
+
+namespace N1
+{
+  struct A { int a; A () : a (0) {} };
+  #pragma omp declare reduction (+:A:bar (omp_out, omp_in, 0))
+};
+namespace N2
+{
+  struct B : N1::A { };
+  #pragma omp declare reduction (+:N1::A:bar (omp_out, omp_in, 1))
+};
+
+int
+main ()
+{
+  G g;
+  int i = 0;
+  #pragma omp parallel reduction(+:g, i)
+    {
+      if (g.a != 6 || g.b != 9 || g.c != 4 || g.d != 3)
+	abort ();
+      g.a = 1, g.b = 2, g.c = 3, g.d = 4, i = 1;
+    }
+  if (g.a != 6 || g.b != 5 + 2 * i || g.c != 4 || g.d != 3)
+    abort ();
+  N2::B b;
+  i = 0;
+  #pragma omp parallel reduction (+:b, i)
+    {
+      if (b.a != 0)
+	abort ();
+      b.a = 4;
+      i = 1;
+    }
+  if (b.a != 4 * i)
+    abort ();
+}  
--- libgomp/testsuite/libgomp.c++/udr-5.C.jj	2013-09-10 18:46:05.235622414 +0200
+++ libgomp/testsuite/libgomp.c++/udr-5.C	2013-09-10 18:43:35.000000000 +0200
@@ -0,0 +1,49 @@ 
+// { dg-do run }
+
+extern "C" void abort ();
+
+struct S
+{
+  void foo ()
+  {
+    S s;
+    int j = 0;
+    #pragma omp declare reduction (bar : int : omp_out += omp_in)
+    #pragma omp parallel reduction (bar : s) reduction(S::operator+ : j)
+    s.a = 4, j = 1;
+    if (s.a != 4 * j) abort ();
+  }
+  #pragma omp declare reduction (bar : S : baz (omp_out, omp_in))
+  static void baz (S &x, S &y) { x.a += y.a; }
+  S () : a (0) {}
+  int a;
+};
+
+template <int N>
+struct T
+{
+  void foo ()
+  {
+    S s;
+    T t;
+    int j = 0;
+    #pragma omp declare reduction (bar : int : omp_out += omp_in)
+    #pragma omp parallel reduction (bar : t) reduction (S::bar : s) \
+			 reduction(T<N>::operator+ : j)
+    s.a = 4, t.a = 5, j = 1;
+    if (s.a != 4 * j || t.a != 5 * j) abort ();
+  }
+  #pragma omp declare reduction (bar : T<N> : baz (omp_out, omp_in))
+  static void baz (T &x, T &y) { x.a += y.a; }
+  T () : a (N) {}
+  int a;
+};
+
+int
+main ()
+{
+  S s;
+  s.foo ();
+  T<0> t;
+  t.foo ();
+}