--- gcc/c/c-tree.h.jj	2013-09-18 12:06:04.051769378 +0200
+++ gcc/c/c-tree.h	2013-09-18 13:07:50.792969161 +0200
@@ -670,6 +670,10 @@ extern enum machine_mode c_default_point
 /* In c-decl.c */
 extern void c_finish_incomplete_decl (tree);
 extern void c_write_global_declarations (void);
+extern tree c_omp_reduction_id (enum tree_code, tree);
+extern tree c_omp_reduction_decl (tree);
+extern tree c_omp_reduction_lookup (tree, tree);
+extern tree c_check_omp_declare_reduction_r (tree *, int *, void *);
 
 /* In c-errors.c */
 extern void pedwarn_c90 (location_t, int opt, const char *, ...) ATTRIBUTE_GCC_DIAG(3,4);
--- gcc/c/c-typeck.c.jj	2013-09-18 12:17:36.814044593 +0200
+++ gcc/c/c-typeck.c	2013-09-18 13:07:50.795968751 +0200
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.
 #include "tree-iterator.h"
 #include "bitmap.h"
 #include "gimple.h"
+#include "tree-inline.h"
 #include "c-family/c-objc.h"
 #include "c-family/c-common.h"
 #include "c-family/c-ubsan.h"
@@ -11209,6 +11210,48 @@ handle_omp_array_sections (tree c)
   return false;
 }
 
+/* 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
+c_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 c_finish_omp_clauses, called via walk_tree.
+   Find OMP_CLAUSE_PLACEHOLDER (passed in DATA) in *TP.  */
+
+static tree
+c_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.  */
 
@@ -11252,14 +11295,8 @@ c_finish_omp_clauses (tree clauses)
 	  name = "reduction";
 	  need_implicitly_determined = true;
 	  t = OMP_CLAUSE_DECL (c);
-	  if (AGGREGATE_TYPE_P (TREE_TYPE (t))
-	      || POINTER_TYPE_P (TREE_TYPE (t)))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"%qE has invalid type for %<reduction%>", t);
-	      remove = true;
-	    }
-	  else if (FLOAT_TYPE_P (TREE_TYPE (t)))
+	  if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) == NULL_TREE
+	      && FLOAT_TYPE_P (TREE_TYPE (t)))
 	    {
 	      enum tree_code r_code = OMP_CLAUSE_REDUCTION_CODE (c);
 	      const char *r_name = NULL;
@@ -11298,6 +11335,73 @@ c_finish_omp_clauses (tree clauses)
 		  remove = true;
 		}
 	    }
+	  else if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) == error_mark_node)
+	    {
+	      error_at (OMP_CLAUSE_LOCATION (c),
+			"user defined reduction not found for %qD", t);
+	      remove = true;
+	    }
+	  else if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c))
+	    {
+	      tree list = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c);
+	      tree type = TYPE_MAIN_VARIANT (TREE_TYPE (t));
+	      tree placeholder = build_decl (OMP_CLAUSE_LOCATION (c),
+					     VAR_DECL, NULL_TREE, type);
+	      OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = placeholder;
+	      DECL_ARTIFICIAL (placeholder) = 1;
+	      DECL_IGNORED_P (placeholder) = 1;
+	      if (TREE_ADDRESSABLE (TREE_VEC_ELT (list, 0)))
+		c_mark_addressable (placeholder);
+	      if (TREE_ADDRESSABLE (TREE_VEC_ELT (list, 1)))
+		c_mark_addressable (OMP_CLAUSE_DECL (c));
+	      OMP_CLAUSE_REDUCTION_MERGE (c)
+		= c_clone_omp_udr (TREE_VEC_ELT (list, 2),
+				   TREE_VEC_ELT (list, 0),
+				   TREE_VEC_ELT (list, 1),
+				   OMP_CLAUSE_DECL (c), placeholder);
+	      OMP_CLAUSE_REDUCTION_MERGE (c)
+		= build3_loc (OMP_CLAUSE_LOCATION (c), BIND_EXPR,
+			      void_type_node, NULL_TREE,
+			       OMP_CLAUSE_REDUCTION_MERGE (c), NULL_TREE);
+	      TREE_SIDE_EFFECTS (OMP_CLAUSE_REDUCTION_MERGE (c)) = 1;
+	      if (TREE_VEC_LENGTH (list) == 6)
+		{
+		  if (TREE_ADDRESSABLE (TREE_VEC_ELT (list, 3)))
+		    c_mark_addressable (OMP_CLAUSE_DECL (c));
+		  if (TREE_ADDRESSABLE (TREE_VEC_ELT (list, 4)))
+		    c_mark_addressable (placeholder);
+		  tree init = TREE_VEC_ELT (list, 5);
+		  if (init == error_mark_node)
+		    init = DECL_INITIAL (TREE_VEC_ELT (list, 3));
+		  OMP_CLAUSE_REDUCTION_INIT (c)
+		    = c_clone_omp_udr (init, TREE_VEC_ELT (list, 4),
+				       TREE_VEC_ELT (list, 3),
+				       OMP_CLAUSE_DECL (c), placeholder);
+		  if (TREE_VEC_ELT (list, 5) == error_mark_node)
+		    OMP_CLAUSE_REDUCTION_INIT (c)
+		      = build2 (INIT_EXPR, TREE_TYPE (t), t,
+				OMP_CLAUSE_REDUCTION_INIT (c));
+		  if (walk_tree (&OMP_CLAUSE_REDUCTION_INIT (c),
+				 c_find_omp_placeholder_r,
+				 placeholder, NULL))
+		    OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c) = 1;
+		}
+	      else
+		{
+		  tree init;
+		  if (AGGREGATE_TYPE_P (TREE_TYPE (t)))
+		    init = build_constructor (TREE_TYPE (t), NULL);
+		  else
+		    init = fold_convert (TREE_TYPE (t), integer_zero_node);
+		  OMP_CLAUSE_REDUCTION_INIT (c)
+		    = build2 (INIT_EXPR, TREE_TYPE (t), t, init);
+		}
+	      OMP_CLAUSE_REDUCTION_INIT (c)
+		= build3_loc (OMP_CLAUSE_LOCATION (c), BIND_EXPR,
+			      void_type_node, NULL_TREE,
+			       OMP_CLAUSE_REDUCTION_INIT (c), NULL_TREE);
+	      TREE_SIDE_EFFECTS (OMP_CLAUSE_REDUCTION_INIT (c)) = 1;
+	    }
 	  goto check_dup_generic;
 
 	case OMP_CLAUSE_COPYPRIVATE:
--- gcc/c/c-parser.c.jj	2013-09-18 12:06:03.995769689 +0200
+++ gcc/c/c-parser.c	2013-09-18 13:07:50.798968362 +0200
@@ -9688,7 +9688,13 @@ c_parser_omp_clause_private (c_parser *p
    OpenMP 3.1:
    
    reduction-operator:
-     One of: + * - & ^ | && || max min  */
+     One of: + * - & ^ | && || max min
+
+   OpenMP 4.0:
+
+   reduction-operator:
+     One of: + * - & ^ | && ||
+     identifier  */
 
 static tree
 c_parser_omp_clause_reduction (c_parser *parser, tree list)
@@ -9696,7 +9702,8 @@ c_parser_omp_clause_reduction (c_parser
   location_t clause_loc = c_parser_peek_token (parser)->location;
   if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
     {
-      enum tree_code code;
+      enum tree_code code = ERROR_MARK;
+      tree reduc_id = NULL_TREE;
 
       switch (c_parser_peek_token (parser)->type)
 	{
@@ -9738,8 +9745,9 @@ c_parser_omp_clause_reduction (c_parser
 		code = MAX_EXPR;
 		break;
 	      }
+	    reduc_id = c_parser_peek_token (parser)->value;
+	    break;
 	  }
-	  /* FALLTHRU */
 	default:
 	  c_parser_error (parser,
 			  "expected %<+%>, %<*%>, %<-%>, %<&%>, "
@@ -9748,6 +9756,7 @@ c_parser_omp_clause_reduction (c_parser
 	  return list;
 	}
       c_parser_consume_token (parser);
+      reduc_id = c_omp_reduction_id (code, reduc_id);
       if (c_parser_require (parser, CPP_COLON, "expected %<:%>"))
 	{
 	  tree nl, c;
@@ -9755,7 +9764,17 @@ c_parser_omp_clause_reduction (c_parser
 	  nl = c_parser_omp_variable_list (parser, clause_loc,
 					   OMP_CLAUSE_REDUCTION, list);
 	  for (c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
-	    OMP_CLAUSE_REDUCTION_CODE (c) = code;
+	    {
+	      tree type = TREE_TYPE (OMP_CLAUSE_DECL (c));
+	      OMP_CLAUSE_REDUCTION_CODE (c) = code;
+	      if (code == ERROR_MARK
+		  || !(INTEGRAL_TYPE_P (type)
+		       || TREE_CODE (type) == REAL_TYPE
+		       || TREE_CODE (type) == COMPLEX_TYPE))
+		OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)
+		  = c_omp_reduction_lookup (reduc_id,
+					    TYPE_MAIN_VARIANT (type));
+	    }
 
 	  list = nl;
 	}
@@ -12430,10 +12449,357 @@ c_parser_omp_end_declare_target (c_parse
     current_omp_declare_target_attribute--;
 }
 
+
+/* 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
+c_parser_omp_declare_reduction (c_parser *parser, enum pragma_context context)
+{
+  unsigned int tokens_avail = 0, i;
+  vec<tree> types = vNULL;
+  vec<c_token> clauses = vNULL;
+  enum tree_code reduc_code = ERROR_MARK;
+  tree reduc_id = NULL_TREE;
+  tree type;
+  location_t rloc = c_parser_peek_token (parser)->location;
+
+  if (context == pragma_struct || context == pragma_param)
+    {
+      error ("%<#pragma omp declare reduction%> not at file or block scope");
+      goto fail;
+    }
+
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+    goto fail;
+
+  switch (c_parser_peek_token (parser)->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:
+      const char *p;
+      p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value);
+      if (strcmp (p, "min") == 0)
+	{
+	  reduc_code = MIN_EXPR;
+	  break;
+	}
+      if (strcmp (p, "max") == 0)
+	{
+	  reduc_code = MAX_EXPR;
+	  break;
+	}
+      reduc_id = c_parser_peek_token (parser)->value;
+      break;
+    default:
+      c_parser_error (parser,
+		      "expected %<+%>, %<*%>, %<-%>, %<&%>, "
+		      "%<^%>, %<|%>, %<&&%>, %<||%>, %<min%> or identifier");
+      goto fail;
+    }
+
+  tree orig_reduc_id, reduc_decl;
+  orig_reduc_id = reduc_id;
+  reduc_id = c_omp_reduction_id (reduc_code, reduc_id);
+  reduc_decl = c_omp_reduction_decl (reduc_id);
+  c_parser_consume_token (parser);
+
+  if (!c_parser_require (parser, CPP_COLON, "expected %<:%>"))
+    goto fail;
+
+  while (true)
+    {
+      location_t loc = c_parser_peek_token (parser)->location;
+      struct c_type_name *ctype = c_parser_type_name (parser);
+      if (ctype != NULL)
+	{
+	  type = groktypename (ctype, NULL, NULL);
+	  if (type == error_mark_node)
+	    ;
+	  else if ((INTEGRAL_TYPE_P (type)
+		    || TREE_CODE (type) == REAL_TYPE
+		    || TREE_CODE (type) == COMPLEX_TYPE)
+		   && orig_reduc_id == NULL_TREE)
+	    error_at (loc, "predeclared arithmetic type in "
+			   "%<#pragma omp declare reduction%>");
+	  else if (TREE_CODE (type) == FUNCTION_TYPE
+		   || TREE_CODE (type) == ARRAY_TYPE)
+	    error_at (loc, "function or array 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
+	    {
+	      tree t;
+	      for (t = DECL_INITIAL (reduc_decl); t; t = TREE_CHAIN (t))
+		if (comptypes (TREE_PURPOSE (t), type))
+		  {
+		    error_at (loc, "redeclaration of %qs "
+				   "%<#pragma omp declare reduction%> for "
+				   "type %qT",
+				   IDENTIFIER_POINTER (reduc_id)
+				   + sizeof ("omp declare reduction ") - 1,
+				   type);
+		    location_t ploc
+		      = DECL_SOURCE_LOCATION (TREE_VEC_ELT (TREE_VALUE (t),
+							    0));
+		    error_at (ploc, "previous %<#pragma omp declare "
+				    "reduction%>");
+		    break;
+		  }
+	      if (t == NULL_TREE)
+		types.safe_push (type);
+	    }
+	  if (c_parser_next_token_is (parser, CPP_COMMA))
+	    c_parser_consume_token (parser);
+	  else
+	    break;
+	}
+      else
+	break;
+    }
+
+  if (!c_parser_require (parser, CPP_COLON, "expected %<:%>")
+      || types.is_empty ())
+    {
+     fail:
+      clauses.release ();
+      types.release ();
+      while (true)
+	{
+	  c_token *token = c_parser_peek_token (parser);
+	  if (token->type == CPP_EOF || token->type == CPP_PRAGMA_EOL)
+	    break;
+	  c_parser_consume_token (parser);
+	}
+      c_parser_skip_to_pragma_eol (parser);
+      return;
+    }
+
+  if (types.length () > 1)
+    {
+      while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL))
+	{
+	  c_token *token = c_parser_peek_token (parser);
+	  if (token->type == CPP_EOF)
+	    goto fail;
+	  clauses.safe_push (*token);
+	  c_parser_consume_token (parser);
+	}
+      clauses.safe_push (*c_parser_peek_token (parser));
+      c_parser_skip_to_pragma_eol (parser);
+
+      /* Make sure nothing tries to read past the end of the tokens.  */
+      c_token eof_token;
+      memset (&eof_token, 0, sizeof (eof_token));
+      eof_token.type = CPP_EOF;
+      clauses.safe_push (eof_token);
+      clauses.safe_push (eof_token);
+    }
+
+  int errs = errorcount;
+  FOR_EACH_VEC_ELT (types, i, type)
+    {
+      tokens_avail = parser->tokens_avail;
+      gcc_assert (parser->tokens == &parser->tokens_buf[0]);
+      if (!clauses.is_empty ())
+	{
+	  parser->tokens = clauses.address ();
+	  parser->tokens_avail = clauses.length ();
+	  parser->in_pragma = true;
+	}
+
+      bool nested = current_function_decl != NULL_TREE;
+      if (nested)
+	c_push_function_context ();
+      tree fndecl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
+				reduc_id, default_function_type);
+      current_function_decl = fndecl;
+      allocate_struct_function (fndecl, true);
+      push_scope ();
+      tree stmt = push_stmt_list ();
+      /* Intentionally BUILTINS_LOCATION, so that -Wshadow doesn't
+	 warn about these.  */
+      tree omp_out = build_decl (BUILTINS_LOCATION, VAR_DECL,
+				 get_identifier ("omp_out"), type);
+      DECL_ARTIFICIAL (omp_out) = 1;
+      DECL_CONTEXT (omp_out) = fndecl;
+      pushdecl (omp_out);
+      tree omp_in = build_decl (BUILTINS_LOCATION, VAR_DECL,
+				get_identifier ("omp_in"), type);
+      DECL_ARTIFICIAL (omp_in) = 1;
+      DECL_CONTEXT (omp_in) = fndecl;
+      pushdecl (omp_in);
+      struct c_expr combiner = c_parser_expression (parser);
+      struct c_expr initializer;
+      tree omp_priv = NULL_TREE, omp_orig = NULL_TREE;
+      bool bad = false;
+      initializer.value = error_mark_node;
+      if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
+	bad = true;
+      else if (c_parser_next_token_is (parser, CPP_NAME)
+	       && strcmp (IDENTIFIER_POINTER
+				(c_parser_peek_token (parser)->value),
+			  "initializer") == 0)
+	{
+	  c_parser_consume_token (parser);
+	  pop_scope ();
+	  push_scope ();
+	  omp_priv = build_decl (BUILTINS_LOCATION, VAR_DECL,
+				 get_identifier ("omp_priv"), type);
+	  DECL_ARTIFICIAL (omp_priv) = 1;
+	  DECL_INITIAL (omp_priv) = error_mark_node;
+	  DECL_CONTEXT (omp_priv) = fndecl;
+	  pushdecl (omp_priv);
+	  omp_orig = build_decl (BUILTINS_LOCATION, VAR_DECL,
+				 get_identifier ("omp_orig"), type);
+	  DECL_ARTIFICIAL (omp_orig) = 1;
+	  DECL_CONTEXT (omp_orig) = fndecl;
+	  pushdecl (omp_orig);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+	    bad = true;
+	  else if (!c_parser_next_token_is (parser, CPP_NAME))
+	    {
+	      c_parser_error (parser, "expected %<omp_priv%> or "
+				      "function-name");
+	      bad = true;
+	    }
+	  else if (strcmp (IDENTIFIER_POINTER
+				(c_parser_peek_token (parser)->value),
+			   "omp_priv") != 0)
+	    {
+	      if (c_parser_peek_2nd_token (parser)->type != CPP_OPEN_PAREN
+		  || c_parser_peek_token (parser)->id_kind != C_ID_ID)
+		{
+		  c_parser_error (parser, "expected function-name %<(%>");
+		  bad = true;
+		}
+	      else
+		initializer = c_parser_postfix_expression (parser);
+	      if (initializer.value
+		  && TREE_CODE (initializer.value) == CALL_EXPR)
+		{
+		  int j;
+		  tree c = initializer.value;
+		  for (j = 0; j < call_expr_nargs (c); j++)
+		    if (TREE_CODE (CALL_EXPR_ARG (c, j)) == ADDR_EXPR
+			&& TREE_OPERAND (CALL_EXPR_ARG (c, j), 0) == omp_priv)
+		      break;
+		  if (j == call_expr_nargs (c))
+		    error ("one of the initializer call arguments should be "
+			   "%<&omp_priv%>");
+		}
+	    }
+	  else
+	    {
+	      c_parser_consume_token (parser);
+	      if (!c_parser_require (parser, CPP_EQ, "expected %<=%>"))
+		bad = true;
+	      else
+		{
+		  tree st = push_stmt_list ();
+		  start_init (omp_priv, NULL_TREE, 0);
+		  location_t loc = c_parser_peek_token (parser)->location;
+		  struct c_expr init = c_parser_initializer (parser);
+		  finish_init ();
+		  finish_decl (omp_priv, loc, init.value,
+		      	       init.original_type, NULL_TREE);
+		  pop_stmt_list (st);
+		}
+	    }
+	  if (!bad
+	      && !c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
+	    bad = true;
+	}
+
+      if (!bad)
+	{
+	  c_parser_skip_to_pragma_eol (parser);
+
+	  tree t = tree_cons (type, make_tree_vec (omp_priv ? 6 : 3),
+			      DECL_INITIAL (reduc_decl));
+	  DECL_INITIAL (reduc_decl) = t;
+	  DECL_SOURCE_LOCATION (omp_out) = rloc;
+	  TREE_VEC_ELT (TREE_VALUE (t), 0) = omp_out;
+	  TREE_VEC_ELT (TREE_VALUE (t), 1) = omp_in;
+	  TREE_VEC_ELT (TREE_VALUE (t), 2) = combiner.value;
+	  walk_tree (&combiner.value, c_check_omp_declare_reduction_r,
+		     &TREE_VEC_ELT (TREE_VALUE (t), 0), NULL);
+	  if (omp_priv)
+	    {
+	      DECL_SOURCE_LOCATION (omp_priv) = rloc;
+	      TREE_VEC_ELT (TREE_VALUE (t), 3) = omp_priv;
+	      TREE_VEC_ELT (TREE_VALUE (t), 4) = omp_orig;
+	      TREE_VEC_ELT (TREE_VALUE (t), 5) = initializer.value;
+	      walk_tree (&initializer.value, c_check_omp_declare_reduction_r,
+			 &TREE_VEC_ELT (TREE_VALUE (t), 3), NULL);
+	      walk_tree (&DECL_INITIAL (omp_priv),
+			 c_check_omp_declare_reduction_r,
+			 &TREE_VEC_ELT (TREE_VALUE (t), 3), NULL);
+	    }
+	}
+
+      pop_stmt_list (stmt);
+      pop_scope ();
+      if (cfun->language != NULL)
+	{
+	  ggc_free (cfun->language);
+	  cfun->language = NULL;
+	}
+      set_cfun (NULL);
+      current_function_decl = NULL_TREE;
+      if (nested)
+	c_pop_function_context ();
+
+      if (!clauses.is_empty ())
+	{
+	  parser->tokens = &parser->tokens_buf[0];
+	  parser->tokens_avail = tokens_avail;
+	}
+      if (bad)
+	goto fail;
+      if (errs != errorcount)
+	break;
+    }
+
+  clauses.release ();
+  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
@@ -12450,12 +12816,12 @@ c_parser_omp_declare (c_parser *parser,
 	  c_parser_omp_declare_simd (parser, context);
 	  return;
 	}
-/*    if (strcmp (p, "reduction") == 0)
+      if (strcmp (p, "reduction") == 0)
 	{
 	  c_parser_consume_token (parser);
-	  c_parser_omp_declare_reduction (parser);
+	  c_parser_omp_declare_reduction (parser, context);
 	  return;
-	}  */
+	}
       if (strcmp (p, "target") == 0)
 	{
 	  c_parser_consume_token (parser);
--- gcc/c/Make-lang.in.jj	2013-09-18 12:06:04.047769396 +0200
+++ gcc/c/Make-lang.in	2013-09-18 13:07:50.795968751 +0200
@@ -189,7 +189,7 @@ c/c-parser.o : c/c-parser.c $(CONFIG_H)
 
 c/c-typeck.o : c/c-typeck.c c/c-lang.h $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
 	$(TREE_H) $(C_TREE_H) $(TARGET_H) $(FLAGS_H) intl.h \
-	langhooks.h tree-iterator.h $(BITMAP_H) $(GIMPLE_H) \
+	langhooks.h tree-iterator.h $(BITMAP_H) $(GIMPLE_H) $(TREE_INLINE_H) \
 	c-family/c-objc.h c-family/c-common.h
 
 c/c-array-notation.o: c/c-array-notation.c c/c-lang.h $(CONFIG_H) \
--- gcc/c/c-decl.c.jj	2013-09-18 12:06:04.084769192 +0200
+++ gcc/c/c-decl.c	2013-09-18 13:07:50.800968109 +0200
@@ -10228,4 +10228,106 @@ c_register_addr_space (const char *word,
   ridpointers [rid] = id;
 }
 
+/* Return identifier to look up for omp declare reduction.  */
+
+tree
+c_omp_reduction_id (enum tree_code reduction_code, tree reduction_id)
+{
+  const char *p = NULL;
+  switch (reduction_code)
+    {
+    case PLUS_EXPR: p = "+"; break;
+    case MULT_EXPR: p = "*"; break;
+    case MINUS_EXPR: p = "-"; break;
+    case BIT_AND_EXPR: p = "&"; break;
+    case BIT_XOR_EXPR: p = "^"; break;
+    case BIT_IOR_EXPR: p = "|"; break;
+    case TRUTH_ANDIF_EXPR: p = "&&"; break;
+    case TRUTH_ORIF_EXPR: p = "||"; 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);
+    }
+
+  const char prefix[] = "omp declare reduction ";
+  size_t lenp = sizeof (prefix);
+  size_t len = strlen (p);
+  char *name = XALLOCAVEC (char, lenp + len);
+  memcpy (name, prefix, lenp - 1);
+  memcpy (name + lenp - 1, p, len + 1);
+  return get_identifier (name);
+}
+
+/* Lookup REDUCTION_ID in the current scope, or create an artificial
+   VAR_DECL, bind it into the current scope and return it.  */
+
+tree
+c_omp_reduction_decl (tree reduction_id)
+{
+  struct c_binding *b = I_SYMBOL_BINDING (reduction_id);
+  if (b != NULL && B_IN_CURRENT_SCOPE (b))
+    return b->decl;
+
+  tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL,
+			  reduction_id, integer_type_node);
+  DECL_ARTIFICIAL (decl) = 1;
+  DECL_EXTERNAL (decl) = 1;
+  TREE_STATIC (decl) = 1;
+  TREE_PUBLIC (decl) = 0;
+  bind (reduction_id, decl, current_scope, true, false, BUILTINS_LOCATION);
+  return decl;
+}
+
+/* Lookup REDUCTION_ID in the first scope where it has entry for TYPE.  */
+
+tree
+c_omp_reduction_lookup (tree reduction_id, tree type)
+{
+  struct c_binding *b = I_SYMBOL_BINDING (reduction_id);
+  while (b)
+    {
+      tree t;
+      for (t = DECL_INITIAL (b->decl); t; t = TREE_CHAIN (t))
+	if (comptypes (TREE_PURPOSE (t), type))
+	  return TREE_VALUE (t);
+      b = b->shadowed;
+    }
+  return error_mark_node;
+}
+
+/* Helper function called via walk_tree, to diagnose invalid
+   #pragma omp declare reduction combiners or initializers.  */
+
+tree
+c_check_omp_declare_reduction_r (tree *tp, int *, void *data)
+{
+  tree *vars = (tree *) data;
+  if (SSA_VAR_P (*tp)
+      && !DECL_ARTIFICIAL (*tp)
+      && *tp != vars[0]
+      && *tp != vars[1])
+    {
+      location_t loc = DECL_SOURCE_LOCATION (vars[0]);
+      if (strcmp (IDENTIFIER_POINTER (DECL_NAME (vars[0])), "omp_out") == 0)
+	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;
+}
+
 #include "gt-c-c-decl.h"
--- gcc/testsuite/gcc.dg/gomp/udr-3.c.jj	2013-09-18 13:07:50.800968109 +0200
+++ gcc/testsuite/gcc.dg/gomp/udr-3.c	2013-09-18 13:07:50.800968109 +0200
@@ -0,0 +1,77 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp" } */
+
+struct S { int s; };
+struct T { int t; };
+struct U { int u; };
+
+#pragma omp declare reduction (+: struct S: omp_out.s += omp_in.s)
+#pragma omp declare reduction (*: struct S: omp_out.s *= omp_in.s) \
+		    initializer (omp_priv = {1})
+#pragma omp declare reduction (foo: struct S: omp_out.s += omp_in.s)
+
+void
+f1 ()
+{
+  struct S s, s2;
+  struct T t;
+  #pragma omp declare reduction (+: struct 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 (+: s)
+  s.s = 1;
+}
+
+void bar (struct S *);
+
+void
+f2 ()
+{
+  #pragma omp declare reduction (foo: struct S: omp_out.s += omp_in.s) initializer (bar (&omp_priv))
+  #pragma omp declare reduction (bar: struct S: omp_out.s += omp_in.s) initializer (bar (&omp_orig)) /* { dg-error "one of the initializer call arguments should be" } */
+}
+
+#pragma omp declare reduction (+: struct U: omp_out.u *= omp_in.u)		/* { dg-error "previous" } */
+#pragma omp declare reduction (+: struct U: omp_out.u += omp_in.u)		/* { dg-error "redeclaration of" } */
+
+void
+f3 ()
+{
+  #pragma omp declare reduction (f3: struct U: omp_out.u *= omp_in.u)		/* { dg-error "previous" } */
+  #pragma omp declare reduction (f3: struct U: omp_out.u += omp_in.u)		/* { dg-error "redeclaration of" } */
+}
+
+struct V
+{
+  #pragma omp declare reduction (bar: struct S: omp_out.s *= omp_in.s)		/* { dg-error "not at file or block scope" } */
+  #pragma omp declare reduction (bar: struct S: omp_out.s += omp_in.s)		/* { dg-error "not at file or block scope" } */
+};
+
+#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
+f4 (void)
+{
+  #pragma omp declare reduction (f4: long: omp_out += omp_in)		/* { dg-error "previous" } */
+  #pragma omp declare reduction (f4: long int: omp_out += omp_in)	/* { dg-error "redeclaration of" } */
+  #pragma omp declare reduction (f4: short unsigned: omp_out += omp_in)
+  #pragma omp declare reduction (f4: short int: omp_out += omp_in)
+}
+
+void
+f5 (void)
+{
+  #pragma omp declare reduction (+: struct S: omp_out.s += omp_in.s) initializer (omp_priv) /* { dg-error "expected" } */
+  #pragma omp declare reduction (+: struct T: omp_out.t += omp_in.t) initializer (omp_priv ()) /* { dg-error "expected" } */
+}
+
+void
+f6 (a, b)
+#pragma omp declare reduction (bar: struct S: omp_out.s *= omp_in.s)	/* { dg-error "expected declaration specifiers before" } */
+  int a;
+  int b;
+{
+}
--- gcc/testsuite/gcc.dg/gomp/udr-1.c.jj	2013-09-18 13:07:50.801967987 +0200
+++ gcc/testsuite/gcc.dg/gomp/udr-1.c	2013-09-18 13:07:50.801967987 +0200
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp" } */
+
+#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" } */
+
+void
+foo (void)
+{
+  #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" } */
+  #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" } */
+}
+
+#pragma omp declare reduction (| : __typeof (foo) : omp_out |= omp_in)	/* { dg-error "function or array" } */
+#pragma omp declare reduction (+ : char () : omp_out += omp_in)		/* { dg-error "function or array" } */
+#pragma omp declare reduction (min : T[2] : omp_out += omp_in)		/* { dg-error "function or array" } */
+
+void
+bar (void)
+{
+  #pragma omp declare reduction (| : __typeof (foo) : omp_out |= omp_in)/* { dg-error "function or array" } */
+  #pragma omp declare reduction (+ : char () : omp_out += omp_in)	/* { dg-error "function or array" } */
+  #pragma omp declare reduction (min : T[2] : omp_out += omp_in)	/* { dg-error "function or array" } */
+}
+
+struct A { int a; };
+#pragma omp declare reduction (| : const struct A : omp_out.a |= omp_in.a)	/* { dg-error "const, volatile or restrict" } */
+#pragma omp declare reduction (+ : __const struct A : omp_out.a += omp_in.a)	/* { dg-error "const, volatile or restrict" } */
+typedef volatile struct A T2;
+#pragma omp declare reduction (min : T2 : omp_out.a += omp_in.a)		/* { dg-error "const, volatile or restrict" } */
+#pragma omp declare reduction (* : struct A *__restrict : omp_out->a *= omp_in->a)/* { dg-error "const, volatile or restrict" } */
+
+void
+baz (void)
+{
+  #pragma omp declare reduction (| : const struct A : omp_out.a |= omp_in.a)	/* { dg-error "const, volatile or restrict" } */
+  #pragma omp declare reduction (+ : __const struct A : omp_out.a += omp_in.a)	/* { dg-error "const, volatile or restrict" } */
+  typedef volatile struct A T3;
+  #pragma omp declare reduction (min : T3 : omp_out.a += omp_in.a)		/* { dg-error "const, volatile or restrict" } */
+  #pragma omp declare reduction (* : struct A *__restrict : omp_out->a *= omp_in->a)/* { dg-error "const, volatile or restrict" } */
+}
--- gcc/testsuite/gcc.dg/gomp/clause-1.c.jj	2013-09-18 12:06:03.484772540 +0200
+++ gcc/testsuite/gcc.dg/gomp/clause-1.c	2013-09-18 13:07:50.801967987 +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,11 +42,11 @@ 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" } */
+#pragma omp p reduction (-:a) /* { dg-error "user defined reduction not found for" } */
     ;
   d = 0;
 #pragma omp p reduction (*:d)
--- gcc/testsuite/gcc.dg/gomp/udr-4.c.jj	2013-09-18 13:07:50.801967987 +0200
+++ gcc/testsuite/gcc.dg/gomp/udr-4.c	2013-09-18 13:07:50.801967987 +0200
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+
+struct S;
+#pragma omp declare reduction (+:struct S:omp_out.s += omp_in.s) /* { dg-error "invalid use of undefined type" } */
+struct S { int s; };
+#pragma omp declare reduction (*:struct S:omp_out.s *= omp_in.s)
--- gcc/testsuite/gcc.dg/gomp/udr-2.c.jj	2013-09-18 13:07:50.801967987 +0200
+++ gcc/testsuite/gcc.dg/gomp/udr-2.c	2013-09-18 13:07:50.801967987 +0200
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp" } */
+
+struct W { int w; };
+void init (struct W *, int, int *);
+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 += 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 : struct W : omp_out.w *= omp_in.w + v) /* { dg-error "combiner refers to variable" } */
+
+void
+foo (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" } */
+  #pragma omp declare reduction (foo : T : omp_out += 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 : struct W : omp_out.w *= omp_in.w + v) /* { dg-error "combiner refers to variable" } */
+}
+
+#pragma omp declare reduction (bar : long int : omp_out |= omp_in) initializer (omp_priv = v) /* { dg-error "initializer refers to variable" } */
+#pragma omp declare reduction (bar : char : omp_out += omp_in) initializer (omp_priv = ((char) v)) /* { dg-error "initializer refers to variable" } */
+#pragma omp declare reduction (bar : T : omp_out += omp_in) initializer (omp_priv = (short) v) /* { dg-error "initializer refers to variable" } */
+#pragma omp declare reduction (bar : _Complex double : omp_out *= omp_in) initializer (omp_priv = (v)) /* { dg-error "initializer refers to variable" } */
+#pragma omp declare reduction (bar : struct W : omp_out.w *= omp_in.w) initializer (omp_priv = { v } ) /* { dg-error "initializer refers to variable" } */
+#pragma omp declare reduction (bar2 : struct W : omp_out.w *= omp_in.w) initializer (init (&omp_priv, v, (int *) 0)) /* { dg-error "initializer refers to variable" } */
+#pragma omp declare reduction (bar3 : struct W : omp_out.w *= omp_in.w) initializer (init (&omp_priv, 0, &v)) /* { dg-error "initializer refers to variable" } */
+
+void
+bar (int v)
+{
+  #pragma omp declare reduction (bar : long int : omp_out |= omp_in) initializer (omp_priv = v) /* { dg-error "initializer refers to variable" } */
+  #pragma omp declare reduction (bar : char : omp_out += omp_in) initializer (omp_priv = ((char) v)) /* { dg-error "initializer refers to variable" } */
+  #pragma omp declare reduction (bar : T : omp_out += omp_in) initializer (omp_priv = (short) v) /* { dg-error "initializer refers to variable" } */
+  #pragma omp declare reduction (bar : _Complex double : omp_out *= omp_in) initializer (omp_priv = (v)) /* { dg-error "initializer refers to variable" } */
+  #pragma omp declare reduction (bar : struct W : omp_out.w *= omp_in.w) initializer (omp_priv = { v }) /* { dg-error "initializer refers to variable" } */
+  #pragma omp declare reduction (bar2 : struct W : omp_out.w *= omp_in.w) initializer (init (&omp_priv, v, (int *) 0)) /* { dg-error "initializer refers to variable" } */
+  #pragma omp declare reduction (bar3 : struct W : omp_out.w *= omp_in.w) initializer (init (&omp_priv, 0, &v)) /* { dg-error "initializer refers to variable" } */
+}
--- libgomp/testsuite/libgomp.c++/udr-9.C.jj	2013-09-18 13:07:50.802967867 +0200
+++ libgomp/testsuite/libgomp.c++/udr-9.C	2013-09-18 13:07:50.802967867 +0200
@@ -0,0 +1,3 @@
+// { dg-do run }
+
+#include "../libgomp.c/udr-1.c"
--- libgomp/testsuite/libgomp.c/udr-3.c.jj	2013-09-18 13:07:50.802967867 +0200
+++ libgomp/testsuite/libgomp.c/udr-3.c	2013-09-18 13:07:50.802967867 +0200
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+
+extern void abort ();
+
+struct S;
+void foo (struct S *, struct S *);
+#pragma omp declare reduction (+:struct S:foo (&omp_out, &omp_in))
+struct S { int s; };
+
+void
+foo (struct S *x, struct S *y)
+{
+  x->s += y->s;
+}
+
+int
+main ()
+{
+  struct S s;
+  int i;
+  s.s = 0;
+  #pragma omp parallel reduction (+:s, i)
+  {
+    if (s.s != 0)
+      abort ();
+    s.s = 2;
+    i = 1;
+  }
+  if (s.s != 2 * i)
+    abort ();
+  return 0;
+}
--- libgomp/testsuite/libgomp.c/simd-4.c.jj	2013-09-18 13:11:46.235706835 +0200
+++ libgomp/testsuite/libgomp.c/simd-4.c	2013-09-18 13:13:12.474247404 +0200
@@ -0,0 +1,42 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-additional-options "-msse2" { target sse2_runtime } } */
+/* { dg-additional-options "-mavx" { target avx_runtime } } */
+
+extern void abort ();
+int a[1024] __attribute__((aligned (32))) = { 1 };
+struct S { int s; };
+#pragma omp declare reduction (+:struct S:omp_out.s += omp_in.s)
+#pragma omp declare reduction (foo:struct S:omp_out.s += omp_in.s)
+#pragma omp declare reduction (foo:int:omp_out += omp_in)
+
+__attribute__((noinline, noclone)) int
+foo (void)
+{
+  int i, u = 0;
+  struct S s, t;
+  s.s = 0; t.s = 0;
+  #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 ();
+  return 0;
+}
--- libgomp/testsuite/libgomp.c/udr-1.c.jj	2013-09-18 13:07:50.802967867 +0200
+++ libgomp/testsuite/libgomp.c/udr-1.c	2013-09-18 13:07:50.802967867 +0200
@@ -0,0 +1,81 @@
+/* { dg-do run } */
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void abort ();
+
+struct S { int s; struct S *t; };
+
+void
+foo (struct S *out, struct S *in)
+{
+  out->s += in->s;
+}
+
+void
+bar (struct S *x)
+{
+  if (x->s != 6) abort ();
+  x->s = 15;
+}
+
+void
+baz (struct S *x, struct S *y)
+{
+  x->s = 6;
+  x->t = x;
+  (void) y;
+}
+
+#pragma omp declare reduction (foo: struct S: foo (&omp_out, &omp_in)) \
+	initializer (omp_priv = { 8, &omp_priv })
+#pragma omp declare reduction (foo: char, int, short: omp_out += omp_in - 4) \
+	initializer (omp_priv = 4)
+#pragma omp declare reduction (+: struct S: foo (&omp_out, &omp_in)) \
+	initializer (baz (&omp_priv, &omp_orig))
+
+void
+test (struct S s, struct S t)
+{
+  int q = 0;
+  #pragma omp parallel num_threads (4) reduction (+: s, q) reduction (foo: t)
+  {
+    if (s.s != 6 || s.t != &s || t.s != 8 || t.t != &t)
+      abort ();
+    s.s = 2;
+    t.s = 3;
+    q = 1;
+  }
+  if (s.s != 12 + 2 * q || t.s != 14 + 3 * q)
+    abort ();
+}
+
+int
+main ()
+{
+  struct S s, t;
+  s.s = 9; t.s = 10;
+  int h = 30, v = 2, q = 0;
+  #pragma omp declare reduction (foo: struct S: omp_out.s *= omp_in.s) \
+	initializer (omp_priv = omp_orig)
+  {
+    #pragma omp declare reduction (foo: struct S: omp_out.s += omp_in.s) \
+	initializer (omp_priv = omp_orig)
+    #pragma omp parallel num_threads (4) reduction (+: t, q) \
+	reduction (min: h) reduction (foo: s, v)
+    {
+      if (s.s != 9 || t.s != 6 || v != 4 || h != __INT_MAX__) abort ();
+      asm volatile ("" : "+m" (s.s), "+m" (t.s));
+      asm volatile ("" : "+r" (h), "+r" (v));
+      h = t.s; s.s++; t.s++; v++; q++;
+    }
+  }
+  if (h != 6 || s.s != 9 + q * 10 || t.s != 10 + q * 7 || v != 2 + q)
+    abort ();
+  s.s = 12;
+  t.s = 14;
+  test (s, t);
+  return 0;
+}
--- libgomp/testsuite/libgomp.c/udr-2.c.jj	2013-09-18 13:07:50.802967867 +0200
+++ libgomp/testsuite/libgomp.c/udr-2.c	2013-09-18 13:07:50.802967867 +0200
@@ -0,0 +1,27 @@
+/* { dg-do run } */
+
+extern void abort ();
+
+struct S { int s; };
+
+#pragma omp declare reduction (+:struct S:omp_out.s += omp_in.s)
+#pragma omp declare reduction (foo:struct 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;
+  struct S s, t;
+  s.s = 0; t.s = 0;
+  #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-5.c.jj	2013-09-18 13:11:49.253690612 +0200
+++ libgomp/testsuite/libgomp.c/simd-5.c	2013-09-18 13:13:54.117026283 +0200
@@ -0,0 +1,44 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-additional-options "-msse2" { target sse2_runtime } } */
+/* { dg-additional-options "-mavx" { target avx_runtime } } */
+
+extern void abort ();
+int a[1024] __attribute__((aligned (32))) = { 1 };
+struct S { int s; };
+#pragma omp declare reduction (+:struct S:omp_out.s += omp_in.s)
+#pragma omp declare reduction (foo:struct S:omp_out.s += omp_in.s)
+#pragma omp declare reduction (foo:int:omp_out += omp_in)
+
+__attribute__((noinline, noclone)) int
+foo (void)
+{
+  int i, u = 0, q = 0;
+  struct S s, t;
+  s.s = 0; t.s = 0;
+  #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 ();
+  return 0;
+}
