This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[gomp4, committed] C++ UDRs


Hi!

Here is an updated version of the patch that I've actually committed to the
gomp-4_0-branch, because the C UDR support relies on it.
If there are issues in the C++ FE you'd like to be changed, it can be done
incrementally on the branch and/or during the merge of gomp-4_0-branch to
trunk.

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

gcc/
	* tree.h (OMP_CLAUSE_REDUCTION_OMP_ORIG_REF): Define.
	* tree-core.h (OMP_CLAUSE_REDUCTION_OMP_ORIG_REF): Document.
	* 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.
	(cp_debug_parser): Print colon_doesnt_start_class_def_p.
	(cp_parser_next_token_starts_class_definition_p): Don't allow
	CPP_COLON if colon_doesnt_start_class_def_p flag is true.
	* parser.h (struct cp_parser): Add colon_doesnt_start_class_def_p
	field.
	* 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.
	* g++.dg/gomp/udr-5.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.

--- gcc/tree-core.h.jj	2013-09-13 17:07:35.000000000 +0200
+++ gcc/tree-core.h	2013-09-16 16:03:12.526490432 +0200
@@ -840,6 +840,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
 
--- gcc/fortran/trans-openmp.c.jj	2013-09-13 16:43:02.168240911 +0200
+++ gcc/fortran/trans-openmp.c	2013-09-16 15:52:31.747754133 +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/parser.c.jj	2013-09-16 10:05:36.414118962 +0200
+++ gcc/cp/parser.c	2013-09-17 17:34:10.387171737 +0200
@@ -232,6 +232,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
@@ -542,6 +545,8 @@ cp_debug_parser (FILE *file, cp_parser *
 			      "local class", parser->in_function_body);
   cp_debug_print_flag (file, "Auto correct a colon to a scope operator",
 			      parser->colon_corrects_to_scope_p);
+  cp_debug_print_flag (file, "Colon doesn't start a class definition",
+			      parser->colon_doesnt_start_class_def_p);
   if (parser->type_definition_forbidden_message)
     fprintf (file, "Error message for forbidden type definitions: %s\n",
 	     parser->type_definition_forbidden_message);
@@ -19135,8 +19140,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);
     }
 
@@ -23058,9 +23074,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 ();
@@ -23980,7 +24005,9 @@ cp_parser_next_token_starts_class_defini
   cp_token *token;
 
   token = cp_lexer_peek_token (parser->lexer);
-  return (token->type == CPP_OPEN_BRACE || token->type == CPP_COLON);
+  return (token->type == CPP_OPEN_BRACE
+	  || (token->type == CPP_COLON
+	      && !parser->colon_doesnt_start_class_def_p));
 }
 
 /* Returns TRUE iff the next token is the "," or ">" (or `>>', in
@@ -26933,70 +26960,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;
@@ -27004,7 +27052,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;
 }
@@ -29823,10 +29874,384 @@ 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;
+  bool saved_colon_doesnt_start_class_def_p;
+  saved_colon_doesnt_start_class_def_p
+    = parser->colon_doesnt_start_class_def_p;
+  parser->colon_doesnt_start_class_def_p = true;
+
+  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;
+  parser->colon_doesnt_start_class_def_p
+    = saved_colon_doesnt_start_class_def_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
@@ -29846,13 +30271,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-09-16 10:05:34.220130343 +0200
+++ gcc/cp/pt.c	2013-09-16 15:52:31.775754496 +0200
@@ -8931,6 +8931,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
 	    {
@@ -10399,6 +10402,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.  */
@@ -12856,7 +12877,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:
@@ -12879,6 +12899,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)
@@ -13215,6 +13255,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;
@@ -13719,6 +13770,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.  */
 
@@ -19420,6 +19537,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.  */
@@ -19429,7 +19547,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
@@ -19466,21 +19588,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/parser.h.jj	2013-09-13 17:09:51.000000000 +0200
+++ gcc/cp/parser.h	2013-09-17 17:27:18.181293413 +0200
@@ -332,6 +332,12 @@ typedef struct GTY(()) cp_parser {
   /* TRUE if we can auto-correct a colon to a scope operator.  */
   bool colon_corrects_to_scope_p;
 
+  /* TRUE if : doesn't start a class definition.  Should be only used
+     together with type_definition_forbidden_message non-NULL, in
+     contexts where new types may not be defined, and the type list
+     is terminated by colon.  */
+  bool colon_doesnt_start_class_def_p;
+
   /* If non-NULL, then we are parsing a construct where new type
      definitions are not permitted.  The string stored here will be
      issued as an error message if a type is defined.  */
--- gcc/cp/semantics.c.jj	2013-09-16 10:05:34.659128066 +0200
+++ gcc/cp/semantics.c	2013-09-16 15:52:31.780754471 +0200
@@ -4046,7 +4046,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;
@@ -4070,8 +4071,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)
@@ -4540,6 +4540,343 @@ 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);
+  tree fns = id;
+  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;
+	}
+    }
+  if (id && BASELINK_P (fns))
+    perform_or_defer_access_check (BASELINK_BINFO (fns), id, id,
+				   tf_warning_or_error);
+  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.  */
 
@@ -5065,6 +5402,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)
@@ -5077,12 +5415,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:
@@ -5130,34 +5470,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))
@@ -5219,15 +5753,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/cp/decl.c.jj	2013-09-16 10:05:31.713143350 +0200
+++ gcc/cp/decl.c	2013-09-16 15:52:31.750754216 +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/cp-tree.h.jj	2013-09-16 10:05:37.184114965 +0200
+++ gcc/cp/cp-tree.h	2013-09-16 15:52:31.777754488 +0200
@@ -1979,7 +1979,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
@@ -3181,6 +3182,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) \
@@ -5779,6 +5785,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);
@@ -5803,7 +5812,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-09-13 16:43:00.489249981 +0200
+++ gcc/cp/cp-gimplify.c	2013-09-16 15:52:31.777754488 +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/gimplify.c.jj	2013-09-13 16:48:30.656464455 +0200
+++ gcc/gimplify.c	2013-09-16 15:52:31.782754461 +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/omp-low.c.jj	2013-09-16 15:25:31.683903448 +0200
+++ gcc/omp-low.c	2013-09-16 15:52:31.729753325 +0200
@@ -2883,8 +2883,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;
 }
 
@@ -2900,6 +2903,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);
@@ -2919,9 +2923,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:
@@ -2963,9 +2964,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)
@@ -3066,10 +3070,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;
 
@@ -3155,19 +3156,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)
 			{
@@ -3184,8 +3186,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:
@@ -3332,19 +3334,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
 		{
@@ -3444,8 +3516,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.  */
@@ -3634,7 +3707,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;
 	  }
@@ -3681,7 +3754,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;
@@ -9110,7 +9185,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);
 
@@ -9138,10 +9213,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.  */
@@ -9187,6 +9263,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-13 17:01:39.041897257 +0200
+++ gcc/tree.h	2013-09-16 16:08:22.134892606 +0200
@@ -1286,6 +1301,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/testsuite/g++.dg/gomp/udr-5.C.jj	2013-09-16 15:52:31.784754452 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-5.C	2013-09-16 15:52:31.784754452 +0200
@@ -0,0 +1,41 @@
+// { dg-do compile }
+
+struct S
+{
+  int s;
+  S () : s (0) {}
+private:
+  #pragma omp declare reduction (+:S:omp_out.s += omp_in.s)	// { dg-error "is private" }
+protected:
+  #pragma omp declare reduction (-:S:omp_out.s += omp_in.s)	// { dg-error "is protected" }
+};
+
+struct T : public S
+{
+  void foo ()
+  {
+    S s;
+    #pragma omp parallel reduction (S::operator +:s)	// { dg-error "within this context" }
+    s.s = 1;
+    S t;
+    #pragma omp parallel reduction (S::operator -:t)
+    t.s = 1;
+    S u;
+    #pragma omp parallel reduction (+:u)		// { dg-error "within this context" }
+    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 "within this context" }
+  s.s = 1;
+  S t;
+  #pragma omp parallel reduction (S::operator -:t)	// { dg-error "within this context" }
+  t.s = 1;
+}
--- gcc/testsuite/g++.dg/gomp/udr-4.C.jj	2013-09-16 15:52:31.784754452 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-4.C	2013-09-16 15:52:31.784754452 +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;
+}
--- gcc/testsuite/g++.dg/gomp/clause-3.C.jj	2013-09-13 16:43:01.251245872 +0200
+++ gcc/testsuite/g++.dg/gomp/clause-3.C	2013-09-16 15:52:31.783754457 +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-3.C.jj	2013-09-16 15:52:31.783754457 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-3.C	2013-09-16 15:52:31.783754457 +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/udr-1.C.jj	2013-09-16 15:52:31.783754457 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-1.C	2013-09-16 15:52:31.783754457 +0200
@@ -0,0 +1,119 @@
+// { dg-do compile }
+// { dg-options "-fopenmp" }
+
+namespace N1
+{
+  #pragma omp declare reduction (| : long int : omp_out |= omp_in)	// { dg-error "predeclared arithmetic type" }
+  #pragma omp declare reduction (+ : char : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+  typedef short T;
+  #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+  #pragma omp declare reduction (* : _Complex double : omp_out *= omp_in)// { dg-error "predeclared arithmetic type" }
+}
+namespace N2
+{
+  template <typename T1, typename T2, typename T3, typename T4>
+  struct S
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "predeclared arithmetic type" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+    #pragma omp declare reduction (* : T4 : omp_out *= omp_in)		// { dg-error "predeclared arithmetic type" }
+  };
+  S<long int, char, short, _Complex double> s;
+  template <typename T1, typename T2, typename T3, typename T4>
+  int foo ()
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "predeclared arithmetic type" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "predeclared arithmetic type" }
+    #pragma omp declare reduction (* : T4 : omp_out *= omp_in)		// { dg-error "predeclared arithmetic type" }
+    return 0;
+  }
+  int x = foo <long int, char, short, _Complex double> ();
+}
+namespace N3
+{
+  void bar ();
+  #pragma omp declare reduction (| : __typeof (bar) : omp_out |= omp_in)// { dg-error "function, array or reference" }
+  #pragma omp declare reduction (+ : char () : omp_out += omp_in)	// { dg-error "function, array or reference" }
+  typedef short T;
+  #pragma omp declare reduction (min : T[2] : omp_out += omp_in)	// { dg-error "function, array or reference" }
+  #pragma omp declare reduction (baz : char & : omp_out *= omp_in)	// { dg-error "function, array or reference" }
+}
+namespace N4
+{
+  void bar ();
+  template <typename T1, typename T2, typename T3, typename T4>
+  struct S
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "function, array or reference" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "function, array or reference" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "function, array or reference" }
+    #pragma omp declare reduction (baz : T4 : omp_out *= omp_in)	// { dg-error "function, array or reference" }
+  };
+  S<__typeof (bar), char (), short [3], char []> s;
+  template <typename T1, typename T2, typename T3, typename T4>
+  int foo ()
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "function, array or reference" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "function, array or reference" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "function, array or reference" }
+    #pragma omp declare reduction (baz : T4 : omp_out *= omp_in)	// { dg-error "function, array or reference" }
+    return 0;
+  }
+  int x = foo <__typeof (bar), char (), short[], char [2]> ();
+}
+namespace N5
+{
+  template <typename T>
+  struct S
+  {
+    #pragma omp declare reduction (baz : T : omp_out *= omp_in)		// { dg-error "function, array or reference" }
+  };
+  S<char &> s;
+  template <typename T>
+  int foo ()
+  {
+    #pragma omp declare reduction (baz : T : omp_out *= omp_in)		// { dg-error "function, array or reference" }
+    return 0;
+  }
+  int x = foo <char &> ();
+}
+namespace N6
+{
+  struct A { int a; A () : a (0) {} };
+  #pragma omp declare reduction (| : const A : omp_out.a |= omp_in.a)	// { dg-error "const, volatile or __restrict" }
+  #pragma omp declare reduction (+ : __const A : omp_out.a += omp_in.a)	// { dg-error "const, volatile or __restrict" }
+  typedef volatile A T;
+  #pragma omp declare reduction (min : T : omp_out.a += omp_in.a)	// { dg-error "const, volatile or __restrict" }
+  #pragma omp declare reduction (* : A *__restrict : omp_out->a *= omp_in->a)// { dg-error "const, volatile or __restrict" }
+}
+namespace N7
+{
+  struct A { int a; A () : a (0) {} };
+  template <typename T1, typename T2, typename T3, typename T4>
+  struct S
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "const, volatile or __restrict" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "const, volatile or __restrict" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "const, volatile or __restrict" }
+    #pragma omp declare reduction (* : T4 : omp_out *= omp_in)		// { dg-error "const, volatile or __restrict" }
+  };
+  S<const A, __const A, volatile A, A *__restrict> s;
+  template <typename T1, typename T2, typename T3, typename T4>
+  int foo ()
+  {
+    #pragma omp declare reduction (| : T1 : omp_out |= omp_in)		// { dg-error "const, volatile or __restrict" }
+    #pragma omp declare reduction (+ : T2 : omp_out += omp_in)		// { dg-error "const, volatile or __restrict" }
+    typedef T3 T;
+    #pragma omp declare reduction (min : T : omp_out += omp_in)		// { dg-error "const, volatile or __restrict" }
+    #pragma omp declare reduction (* : T4 : omp_out *= omp_in)		// { dg-error "const, volatile or __restrict" }
+    return 0;
+  }
+  int x = foo <const A, __const A, volatile A, A *__restrict> ();
+}
--- gcc/testsuite/g++.dg/gomp/udr-2.C.jj	2013-09-16 15:52:31.784754452 +0200
+++ gcc/testsuite/g++.dg/gomp/udr-2.C	2013-09-16 15:52:31.784754452 +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/tree-pretty-print.c.jj	2013-09-13 16:45:26.397460689 +0200
+++ gcc/tree-pretty-print.c	2013-09-16 15:52:31.781754465 +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);
--- libgomp/testsuite/libgomp.c++/simd-7.C.jj	2013-09-16 15:52:31.785754447 +0200
+++ libgomp/testsuite/libgomp.c++/simd-7.C	2013-09-16 15:52:31.785754447 +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-5.C.jj	2013-09-16 15:52:31.787754437 +0200
+++ libgomp/testsuite/libgomp.c++/udr-5.C	2013-09-16 15:52:31.787754437 +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 ();
+}
--- libgomp/testsuite/libgomp.c++/udr-6.C.jj	2013-09-16 15:52:31.787754437 +0200
+++ libgomp/testsuite/libgomp.c++/udr-6.C	2013-09-16 15:52:31.787754437 +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-4.C.jj	2013-09-16 15:52:31.786754442 +0200
+++ libgomp/testsuite/libgomp.c++/udr-4.C	2013-09-16 15:52:31.786754442 +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-4.C.jj	2013-09-16 15:52:31.785754447 +0200
+++ libgomp/testsuite/libgomp.c++/simd-4.C	2013-09-16 15:52:31.785754447 +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-8.C.jj	2013-09-16 15:52:31.786754442 +0200
+++ libgomp/testsuite/libgomp.c++/udr-8.C	2013-09-16 15:52:31.786754442 +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-6.C.jj	2013-09-16 15:52:31.787754437 +0200
+++ libgomp/testsuite/libgomp.c++/simd-6.C	2013-09-16 15:52:31.786754442 +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-3.C.jj	2013-09-16 15:52:31.785754447 +0200
+++ libgomp/testsuite/libgomp.c++/udr-3.C	2013-09-16 15:52:31.785754447 +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++/udr-7.C.jj	2013-09-16 15:52:31.785754447 +0200
+++ libgomp/testsuite/libgomp.c++/udr-7.C	2013-09-16 15:52:31.785754447 +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++/udr-1.C.jj	2013-09-16 15:52:31.784754452 +0200
+++ libgomp/testsuite/libgomp.c++/udr-1.C	2013-09-16 15:52:31.784754452 +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++/simd-5.C.jj	2013-09-16 15:52:31.786754442 +0200
+++ libgomp/testsuite/libgomp.c++/simd-5.C	2013-09-16 15:52:31.786754442 +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-2.C.jj	2013-09-16 15:52:31.786754442 +0200
+++ libgomp/testsuite/libgomp.c++/udr-2.C	2013-09-16 15:52:31.786754442 +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 ();
+}

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]