Hi!

This is the C FE counterpart of the C++ FE OpenMP 4.0 user defined
reductions, as C has no templates, class inheritance, doesn't
allow #pragma omp declare reduction in structs/unions, the implementation is
significantly simpler.

Joseph, any comments on this?

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

        * c-tree.h (c_omp_reduction_id, c_omp_reduction_decl,
        c_omp_reduction_lookup, c_check_omp_declare_reduction_r): New
        prototypes.
        * c-typeck.c: Include tree-inline.h.
        (c_clone_udr, c_find_omp_placeholder_r): New functions.
        (c_finish_omp_clauses): Handle user defined reductions.
        * Make-lang.in (c-typeck.o): Depend on $(TREE_INLINE_H).
        * c-parser.c (c_parser_omp_clause_reduction): Handle user
        defined reductions.
        (c_parser_omp_declare_reduction): New function.
        (c_parser_omp_declare): Call it.
        * c-decl.c (c_omp_reduction_id, c_omp_reduction_decl,
        c_omp_reduction_lookup, c_check_omp_declare_reduction_r): New
        functions.
gcc/testsuite/
        * gcc.dg/gomp/udr-1.c: New test.
        * gcc.dg/gomp/udr-2.c: New test.
        * gcc.dg/gomp/udr-3.c: New test.
        * gcc.dg/gomp/udr-4.c: New test.
        * gcc.dg/gomp/clause-1.c: Adjust error messages.
libgomp/
        * testsuite/libgomp.c/simd-4.c: New test.
        * testsuite/libgomp.c/simd-5.c: New test.
        * testsuite/libgomp.c/udr-1.c: New test.
        * testsuite/libgomp.c/udr-2.c: New test.
        * testsuite/libgomp.c/udr-3.c: New test.
        * testsuite/libgomp.c++/udr-9.C: New test.

--- 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;
+}

        Jakub

Reply via email to