public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [OpenACC] declare directive
@ 2015-10-27 20:20 James Norris
  2015-10-28 16:33 ` Cesar Philippidis
                   ` (3 more replies)
  0 siblings, 4 replies; 51+ messages in thread
From: James Norris @ 2015-10-27 20:20 UTC (permalink / raw)
  To: GCC Patches
  Cc: Tobias Burnus, Jakub Jelinek, hubicka, Nathan Sidwell, Joseph S. Myers

[-- Attachment #1: Type: text/plain, Size: 311 bytes --]

Hi!

     This patch adds the processing of OpenACC declare directive in C
     and C++. (Note: Support in Fortran is already in trunk.)
     Commentary on the changes is included as an attachment (NOTES).

     All of the code is in the gomp-4_0-branch.

     Regtested on x86_64-linux.

     Thanks!
     Jim

[-- Attachment #2: NOTES --]
[-- Type: text/plain, Size: 5126 bytes --]

    Background
        The declare directive is used to allocate device memory for the
        entire scope of a variable / array within a program, function,
        or subroutine. Consider the following example.

            int a[10];
            #pragma acc declare create (a)

            void func (int *a)
            {
                int b[10];
                #pragma acc declare create (b)

                #pragma acc parallel present (a, b)
                {
                  int i;

                  for (i = 0; i < 10; i++)
                  {
                    b[i] = a[i];
                    a[i] = b[i] + i;
                  }

                }

                return;
            }
                
            int main (int argc, char **argv)
            {
                func (&a[0]);

                return 0;
            }

        In the example, array 'a' will be allocated on the device at the
        outset of device activity and be available for the duration.
        Whereas, array 'b', will only be available when 'func' is executing.
        In other words, array 'b' will be allocated at the outset of
        execution of 'func' and deallocated at the return from 'func'. In
        some instances, the clause may require that the host copy of a
        variable / array be updated prior to a return from a function or
        subroutine or exiting of the program.

    C and C++ front-ends

        Definitions for use in C and C++ were added to identify the
        declare directive pragma and its' valid clauses. After the
        clauses have been validated, if the declare directive is for a
	global variable, then an attribute is created and chained.
        These attributes will be used during gimplification.

        Once the user-specified clauses have been parsed, the clauses
        have to be examined and potentially altered and/or added to.
        As mentioned in the previous section, with some clauses, e.g.,
        e.g, copy, movement of data has to occur at the entry to 
        something like a function as well as at exit. Hence the need
        to examine/modify/add to the clauses so as to effect the
        correct data movement.

        For all instances of the declare directive, there is at least
        one set of 'entry' clauses. If the clauses pertain to global
        variables, a constructor is created. This constructor will
        'register' the variable(s) / arrays so that at beginning of
        OpenACC runtime the variable / arrays will be allocated and
        be made available throughout program execution.

        If on the other hand, the 'entry' clauses are not found to be
        of a global type, then a node is created and the clauses are
        associated with it. Also note that the 'return' clauses are
        also associated with the node. Notice that there are 'return'
        clauses only for non-global variables / arrays. The clauses
        available for global variables / arrays only allow for data
        movement at the initiation of program execution.

	Middle-end

        The OACC_DECLARE node is handled much the same as other OpenACC
        nodes that represent directives. However, there is one thing
        unique to declare, and that is the handling of the 'return'
        clauses. The 'return' clauses are scanned and then a gimple
        statment is created, but is not added. However, it is saved to
        be added after the body has been gimplified.

        The intent of this last-minute addition is to allow this statement
        to be executed prior to returning from a function. JAKUB: While
        this has been working, I'm not completely sure this is the proper
        means by which to do this in order to guarantee this statement
        is the last one executed. Please advise otherwise.
	
	Callgraph

        The make_offload functionality has been refactored to handle 
        OpenACC variables / arrays that are not external and and have
        'oacc declare' attributes associated with them. Once 'offloaded',
        the attributes are removed.

	libgomp

        A function has been added to handle the 'registration' of
        global variables that will be allocated on the device. This
        function is called by the constructor which was generated as
        part of handling the declare directive by the compiler.

        An additional function has been added to allocate device memory
        from the list of registered variables / arrays. Another function
        has been added to deallocate that device. These functions are
        called at the appropriate places in the runtime.

        Finally, a function has been added to handle the declare builtin
        which is emitted by the compiler.

    Testing
    
        New compile and runtime tests have been added. (NOTE: The numbering
        for the runtime tests has a gap in it. These tests use both the
        declare and routine directive. The support for the routine directive
        has yet to be added to trunk, so these tests will appear once
        the support has been committed.)

[-- Attachment #3: ChangeLog --]
[-- Type: text/plain, Size: 3037 bytes --]

2015-10-27  James Norris  <jnorris@codesourcery.com>

	gcc/
	* builtin-types.def (BT_FN_VOID_PTR_INT_UINT): New type.
	* c-family/c-common.c (c_common_attribute_table): New oacc_declare.
	* c-family/c-pragma.c (oacc_pragmas): Add entry for declare directive. 
	* c-family/c-pragma.h (enum pragma_kind): Add PRAGMA_OACC_DECLARE.
	(enum pragma_omp_clause): Add PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT.
	* c/c-parser.c (c_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	(c_parser_omp_clause_name): Handle 'device_resident' clause.
	(c_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(c_parser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OACC_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(c_parser_oacc_declare): New function.
	* cp/parser.c (cp_parser_omp_clause_name): Handle 'device_resident'
	clause.
	(cp_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(cp_parser_oacc_declare): New function.
	(cp_paser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(cp_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	* cp/pt.c (tsubst_expr): Handle OACC_DECLARE.
	* fortran/types.def (BT_FN_VOID_PTR_INT_UINT): New type.
	* gimple.h (enum gf_mask): Add GF_OMP_TARGET_KIND_OACC_DECLARE.
	(is_gomple_omp_oacc): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* gimplify.c (struct gimplify_omp_ctx): New field.
	(new_omp_context): Initialize new field.
	(omp_default_clause): Handle device resident variable.
	(gimplify_oacc_declare): New function.
	(device_resident_p): New function.
	(gimplify_expr): Handle OACC_DECLARE.
	(gimplify_body): Handle updating of declare'd variables.
	* omp-builtins.def (BUILT_IN_GOACC_STATIC, BUILT_IN_GOACC_DECLARE):
	New builtins.
	* omp-low.c (expand_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE and BUILTIN_GOACC_DECLARE.
	(lower_omp_target): Handle GF_OMP_TARGET_KIND_OACC_DECLARE,
	GOMP_MAP_DEVICE_RESIDENT and GOMP_MAP_LINK.
	(make_gimple_omp_edges): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* testsuite/c-c++-common/goacc/declare-1.c: New test.
	* testsuite/c-c++-common/goacc/declare-2.c: Likewise.
	* tree.def (OACC_DECLARE): Update operands.
	* tree.h (OACC_DECLARE_RETURN_CLAUSES): New definition.
	* varpool.c (make_offloadable_1, make_offloadable): New functions.
	(get_create): Refactor offload functionality.

	include/
	* gomp-constants.h (enum gomp_map_kind): Add GOMP_MAP_DEVICE_RESIDENT
	and GOMP_MAP_LINK.

	libgomp/
	* libgomp.map (GOACC_2.0): Export GOACC_declare.
	* oacc-init.c (acc_shutdown_1): Add call.
	* oacc-int.h (goacc_allocate_static, goacc_deallocate_static): New
	declarations.
	* oacc-parallel.c (struct oacc_static): New struture.
	(goacc_allocate_static, goacc_deallocate_static, GOACC_register_static,
	GOACC_declare): New functions.
	* testsuite/libgomp.oacc-c-c++-common/declare-1.c: New test.
	* testsuite/libgomp.oacc-c-c++-common/declare-5.c: Likewise.

[-- Attachment #4: declare.patch --]
[-- Type: text/x-patch, Size: 47669 bytes --]

diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index b561436..a109806 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -450,6 +450,7 @@ DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONG_ULONG_ULONGPTR, BT_BOOL, BT_ULONG,
 		     BT_ULONG, BT_PTR_ULONG)
 DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, BT_BOOL,
 		     BT_ULONGLONG, BT_ULONGLONG, BT_PTR_ULONGLONG)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 1c75921..ffaa983 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -832,6 +832,7 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_bnd_legacy, false },
   { "bnd_instrument",         0, 0, true, false, false,
 			      handle_bnd_instrument, false },
+  { "oacc declare",           0, -1, true,  false, false, NULL, false },
   { NULL,                     0, 0, false, false, false, NULL, false }
 };
 
diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
index 834a916..fbeb8ecd 100644
--- a/gcc/c-family/c-pragma.c
+++ b/gcc/c-family/c-pragma.c
@@ -1212,6 +1212,7 @@ struct omp_pragma_def { const char *name; unsigned int id; };
 static const struct omp_pragma_def oacc_pragmas[] = {
   { "cache", PRAGMA_OACC_CACHE },
   { "data", PRAGMA_OACC_DATA },
+  { "declare", PRAGMA_OACC_DECLARE },
   { "enter", PRAGMA_OACC_ENTER_DATA },
   { "exit", PRAGMA_OACC_EXIT_DATA },
   { "kernels", PRAGMA_OACC_KERNELS },
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index cec920f..dcba221 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -29,6 +29,7 @@ enum pragma_kind {
 
   PRAGMA_OACC_CACHE,
   PRAGMA_OACC_DATA,
+  PRAGMA_OACC_DECLARE,
   PRAGMA_OACC_ENTER_DATA,
   PRAGMA_OACC_EXIT_DATA,
   PRAGMA_OACC_KERNELS,
@@ -150,6 +151,7 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_CREATE,
   PRAGMA_OACC_CLAUSE_DELETE,
   PRAGMA_OACC_CLAUSE_DEVICEPTR,
+  PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT,
   PRAGMA_OACC_CLAUSE_GANG,
   PRAGMA_OACC_CLAUSE_HOST,
   PRAGMA_OACC_CLAUSE_NUM_GANGS,
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index c8c6a2d..6af7229 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1256,6 +1256,7 @@ static bool c_parser_omp_target (c_parser *, enum pragma_context);
 static void c_parser_omp_end_declare_target (c_parser *);
 static void c_parser_omp_declare (c_parser *, enum pragma_context);
 static bool c_parser_omp_ordered (c_parser *, enum pragma_context);
+static void c_parser_oacc_declare (c_parser *parser);
 
 /* These Objective-C parser functions are only ever called when
    compiling Objective-C.  */
@@ -9702,6 +9703,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context)
 
   switch (id)
     {
+    case PRAGMA_OACC_DECLARE:
+      c_parser_oacc_declare (parser);
+      return false;
+
     case PRAGMA_OACC_ENTER_DATA:
       c_parser_oacc_enter_exit_data (parser, true);
       return false;
@@ -9987,6 +9992,8 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -10419,10 +10426,16 @@ c_parser_oacc_data_clause (c_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OMP_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -12425,6 +12438,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = c_parser_omp_clause_firstprivate (parser, clauses);
 	  c_name = "firstprivate";
@@ -12437,6 +12454,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_omp_clause_if (parser, clauses, false);
 	  c_name = "if";
 	  break;
+	case PRAGMA_OMP_CLAUSE_LINK:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  clauses = c_parser_omp_clause_num_gangs (parser, clauses);
 	  c_name = "num_gangs";
@@ -12929,6 +12950,281 @@ c_parser_oacc_kernels (location_t loc, c_parser *parser, char *p_name)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+static int oacc_dcl_idx = 0;
+
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINK)			\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE) )
+
+static void
+c_parser_oacc_declare (c_parser *parser)
+{
+  location_t pragma_loc = c_parser_peek_token (parser)->location;
+  tree c, clauses, ret_clauses, stmt, t;
+
+  bool error = false;
+
+  c_parser_consume_pragma (parser);
+
+  clauses = c_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+				       "#pragma acc declare");
+  if (!clauses)
+    {
+      error_at (pragma_loc,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return;
+    }
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      tree decl = OMP_CLAUSE_DECL (t);
+      tree devres = NULL_TREE;
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "subarray in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	  break;
+
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  devres = t;
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p () && !DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      tree decl_for_attr = decl;
+      tree prev_attr = lookup_attribute ("oacc declare",
+					 DECL_ATTRIBUTES (decl));
+      if (prev_attr)
+	{
+	  tree p = TREE_VALUE (prev_attr);
+	  tree cl = TREE_VALUE (p);
+
+	  if (!devres && OMP_CLAUSE_MAP_KIND (cl) != GOMP_MAP_DEVICE_RESIDENT)
+	    {
+	      error_at (loc, "variable %qD used more than once with "
+			     "%<#pragma acc declare%>", decl);
+	      inform (OMP_CLAUSE_LOCATION (cl), "previous directive was here");
+	      error = true;
+	      continue;
+	    }
+	}
+
+      if (!error && global_bindings_p ())
+	{
+	  tree attr = tree_cons (NULL_TREE, clauses, NULL_TREE);
+	  tree attrs = tree_cons (get_identifier ("oacc declare"),
+				  attr, NULL_TREE);
+	  decl_attributes (&decl_for_attr, attrs, 0);
+	}
+    }
+
+  if (error)
+    return;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_LINK:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = copy_node (c);
+
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+  if (global_bindings_p ())
+    {
+      char buf[128];
+      struct c_declarator *target;
+      tree stmt, attrs;
+      c_arg_info *arg_info = build_arg_info ();
+      struct c_declarator *declarator;
+      struct c_declspecs *specs;
+      struct c_typespec spec;
+      location_t loc = UNKNOWN_LOCATION;
+      tree f, t, fnbody, call_fn;
+
+      sprintf (buf, "__openacc_c_constructor__%d", oacc_dcl_idx++);
+      target = build_id_declarator (get_identifier (buf));
+      arg_info->types = void_list_node;
+      declarator = build_function_declarator (arg_info, target);
+
+      specs = build_null_declspecs ();
+      spec.kind = ctsk_resword;
+      spec.spec = get_identifier ("void");
+      spec.expr = NULL_TREE;
+      spec.expr_const_operands = true;
+
+      declspecs_add_type (pragma_loc, specs, spec);
+      finish_declspecs (specs);
+
+      attrs = tree_cons (get_identifier ("constructor") , NULL_TREE, NULL_TREE);
+      start_function (specs, declarator, attrs);
+      store_parm_decls ();
+      f = c_begin_compound_stmt (true);
+      TREE_USED (current_function_decl) = 1;
+      call_fn = builtin_decl_explicit (BUILT_IN_GOACC_STATIC);
+      TREE_SIDE_EFFECTS (call_fn) = 1;
+
+      for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+	{
+	  tree d, a1, a2, a3;
+	  vec<tree, va_gc> *args;
+	  vec_alloc (args, 3);
+
+	  d = OMP_CLAUSE_DECL (t);
+
+	  a1 = build_unary_op (loc, ADDR_EXPR, d, 0);
+	  a2 = DECL_SIZE_UNIT (d);
+	  a3 = build_int_cst (unsigned_type_node, OMP_CLAUSE_MAP_KIND (t));
+
+	  args->quick_push (a1);
+	  args->quick_push (a2);
+	  args->quick_push (a3);
+
+	  stmt = build_function_call_vec (loc, vNULL, call_fn, args, NULL);
+	  add_stmt (stmt);
+	}
+
+	fnbody = c_end_compound_stmt (loc, f, true);
+	add_stmt (fnbody);
+
+      finish_function ();
+    }
+  else
+    {
+      stmt = make_node (OACC_DECLARE);
+      TREE_TYPE (stmt) = void_type_node;
+      OACC_DECLARE_CLAUSES (stmt) = clauses;
+      OACC_DECLARE_RETURN_CLAUSES (stmt) = ret_clauses;
+      SET_EXPR_LOCATION (stmt, pragma_loc);
+
+      add_stmt (stmt);
+    }
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 7555bf3..4ec16de 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2179,6 +2179,9 @@ static vec<constructor_elt, va_gc> *cp_parser_initializer_list
 static bool cp_parser_ctor_initializer_opt_and_function_body
   (cp_parser *, bool);
 
+static tree cp_parser_oacc_all_clauses (cp_parser *, omp_clause_mask,
+					const char *, cp_token *, bool);
+
 static tree cp_parser_late_parsing_omp_declare_simd
   (cp_parser *, tree);
 
@@ -29110,6 +29113,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -29511,10 +29516,16 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OMP_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -29543,6 +29554,281 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+static int oacc_dcl_idx = 0;
+
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINK)			\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE))
+
+static tree
+cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
+{
+  tree c, clauses, ret_clauses, stmt, t;
+  bool error = false;
+
+  clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+					"#pragma acc declare", pragma_tok, true);
+
+
+  if (find_omp_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
+    {
+      error_at (pragma_tok->location,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return NULL_TREE;
+    }
+
+  for (tree t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      tree decl = OMP_CLAUSE_DECL (t);
+      tree devres = NULL_TREE;
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "subarray in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+      gcc_assert (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_MAP);
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	  break;
+
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  devres = t;
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p () && !DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      tree decl_for_attr = decl;
+      tree prev_attr = lookup_attribute ("oacc declare",
+					     DECL_ATTRIBUTES (decl));
+      if (prev_attr)
+	{
+	  tree p = TREE_VALUE (prev_attr);
+	  tree cl = TREE_VALUE (p);
+
+	  if (!devres && OMP_CLAUSE_MAP_KIND (cl) != GOMP_MAP_DEVICE_RESIDENT)
+	    {
+	      error_at (loc, "variable %qD used more than once with "
+			"%<#pragma acc declare%>", decl);
+	      inform (OMP_CLAUSE_LOCATION (TREE_VALUE (p)),
+		      "previous directive was here");
+	      error = true;
+	      continue;
+	    }
+	}
+
+      if (!error && global_bindings_p ())
+	{
+	  tree attr = tree_cons (NULL_TREE, t, NULL_TREE);
+	  tree attrs = tree_cons (get_identifier ("oacc declare"),
+				  attr, NULL_TREE);
+	  decl_attributes (&decl_for_attr, attrs, 0);
+	}
+    }
+
+  if (error)
+    return NULL_TREE;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  case GOMP_MAP_LINK:
+	    continue;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = copy_node (c);
+
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+  if (global_bindings_p ())
+    {
+      char buf[128];
+      cp_decl_specifier_seq decl_specifiers;
+      cp_declarator *declarator;
+      tree attrs, parms;
+      tree f, t, call_fn, stmt;
+      location_t loc = UNKNOWN_LOCATION;
+      void *p;
+
+      p = obstack_alloc (&declarator_obstack, 0);
+      clear_decl_specs (&decl_specifiers);
+      decl_specifiers.type = void_type_node;
+      sprintf (buf, "__openacc_cp_constructor__%d", oacc_dcl_idx++);
+
+      declarator = make_id_declarator (NULL_TREE, get_identifier (buf),
+				       sfk_none);
+      parms = void_list_node;
+      declarator = make_call_declarator (declarator, parms,
+					 TYPE_UNQUALIFIED,
+					 VIRT_SPEC_UNSPECIFIED,
+					 REF_QUAL_NONE,
+					 /*tx_qualifier=*/NULL_TREE,
+					 /*exception_specification=*/NULL_TREE,
+					 /*late_return_type=*/NULL_TREE,
+					 /*requires_clause=*/NULL_TREE);
+      attrs = tree_cons (get_identifier ("constructor") , NULL_TREE, NULL_TREE);
+      start_function (&decl_specifiers, declarator, attrs);
+      f = begin_compound_stmt (0);
+      call_fn = builtin_decl_explicit (BUILT_IN_GOACC_STATIC);
+      TREE_SIDE_EFFECTS (call_fn) = 1;
+
+      for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+	{
+	  tree d, a1, a2, a3;
+	  vec<tree, va_gc> *args;
+	  vec_alloc (args, 3);
+
+	  d = OMP_CLAUSE_DECL (t);
+
+	  a1 = build_unary_op (loc, ADDR_EXPR, d, 0);
+	  a2 = DECL_SIZE_UNIT (d);
+	  a3 = build_int_cst (unsigned_type_node, OMP_CLAUSE_MAP_KIND (t));
+
+	  args->quick_push (a1);
+	  args->quick_push (a2);
+	  args->quick_push (a3);
+
+	  stmt = build_function_call_vec (loc, vNULL, call_fn, args, NULL);
+	  finish_expr_stmt (stmt);
+	}
+
+      finish_compound_stmt (f);
+      expand_or_defer_fn (finish_function (0));
+      obstack_free (&declarator_obstack, p);
+    }
+  else
+    {
+      stmt = make_node (OACC_DECLARE);
+      TREE_TYPE (stmt) = void_type_node;
+      OACC_DECLARE_CLAUSES (stmt) = clauses;
+      OACC_DECLARE_RETURN_CLAUSES (stmt) = ret_clauses;
+      SET_EXPR_LOCATION (stmt, pragma_tok->location);
+
+      add_stmt (stmt);
+    }
+
+  return NULL_TREE;
+}
+
+/* OpenACC 2.0:
    deviceptr ( variable-list ) */
 
 static tree
@@ -31338,6 +31624,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  clauses = cp_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_HOST:
 	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
 	  c_name = "host";
@@ -31346,6 +31636,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  clauses = cp_parser_omp_clause_if (parser, clauses, here, false);
 	  c_name = "if";
 	  break;
+	case PRAGMA_OMP_CLAUSE_LINK:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  clauses = cp_parser_omp_clause_num_gangs (parser, clauses);
 	  c_name = "num_gangs";
@@ -35853,6 +36147,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context)
       cp_parser_omp_declare (parser, pragma_tok, context);
       return false;
 
+    case PRAGMA_OACC_DECLARE:
+      cp_parser_oacc_declare (parser, pragma_tok);
+      return false;
+
     case PRAGMA_OACC_CACHE:
     case PRAGMA_OACC_DATA:
     case PRAGMA_OACC_ENTER_DATA:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 142245a..85ed05d 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15329,6 +15329,17 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       add_stmt (t);
       break;
 
+    case OACC_DECLARE:
+      t = copy_node (t);
+      tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_CLAUSES (t) = tmp;
+      tmp = tsubst_omp_clauses (OACC_DECLARE_RETURN_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_RETURN_CLAUSES (t) = tmp;
+      add_stmt (t);
+      break;
+
     case OMP_TARGET_UPDATE:
     case OMP_TARGET_ENTER_DATA:
     case OMP_TARGET_EXIT_DATA:
diff --git a/gcc/fortran/types.def b/gcc/fortran/types.def
index ca75654..6d993db 100644
--- a/gcc/fortran/types.def
+++ b/gcc/fortran/types.def
@@ -145,6 +145,7 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I2_INT, BT_VOID, BT_VOLATILE_PTR, BT_I2, BT
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I4_INT, BT_VOID, BT_VOLATILE_PTR, BT_I4, BT_INT)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I8_INT, BT_VOID, BT_VOLATILE_PTR, BT_I8, BT_INT)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I16_INT, BT_VOID, BT_VOLATILE_PTR, BT_I16, BT_INT)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_VOID_OMPFN_PTR_UINT_UINT,
                      BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, BT_UINT)
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 02d0db5..2b1d9e7 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -170,6 +170,7 @@ enum gf_mask {
     GF_OMP_TARGET_KIND_OACC_DATA = 7,
     GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
     GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
+    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,
 
     /* True on an GIMPLE_OMP_RETURN statement if the return does not require
        a thread synchronization via some sort of barrier.  The exact barrier
@@ -5989,6 +5990,7 @@ is_gimple_omp_oacc (const gimple *stmt)
 	case GF_OMP_TARGET_KIND_OACC_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  return true;
 	default:
 	  return false;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index ab9e540..0bf5ce69 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -163,6 +163,7 @@ struct gimplify_omp_ctx
   bool target_map_scalars_firstprivate;
   bool target_map_pointers_as_0len_arrays;
   bool target_firstprivatize_array_bases;
+  gomp_target *declare_returns;
 };
 
 static struct gimplify_ctx *gimplify_ctxp;
@@ -382,6 +383,7 @@ new_omp_context (enum omp_region_type region_type)
     c->default_kind = OMP_CLAUSE_DEFAULT_SHARED;
   else
     c->default_kind = OMP_CLAUSE_DEFAULT_UNSPECIFIED;
+  c->declare_returns = NULL;
 
   return c;
 }
@@ -5798,6 +5800,26 @@ omp_notice_threadprivate_variable (struct gimplify_omp_ctx *ctx, tree decl,
   return false;
 }
 
+/* Return true if global var DECL is device resident.  */
+
+static bool
+device_resident_p (tree decl)
+{
+  tree attr = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
+
+  if (!attr)
+    return false;
+  
+  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
+    {
+      tree c = TREE_VALUE (t);
+      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
+	return true;
+    }
+
+  return false;
+}
+
 /* Determine outer default flags for DECL mentioned in an OMP region
    but not declared in an enclosing clause.
 
@@ -5847,6 +5869,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
       flags |= GOVD_FIRSTPRIVATE;
       break;
     case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
+      if (is_global_var (decl) && device_resident_p (decl))
+	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
       /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED.  */
       gcc_assert ((ctx->region_type & ORT_TASK) != 0);
       if (struct gimplify_omp_ctx *octx = ctx->outer_context)
@@ -7529,6 +7553,62 @@ gimplify_oacc_cache (tree *expr_p, gimple_seq *pre_p)
   *expr_p = NULL_TREE;
 }
 
+/* Gimplify OACC_DECLARE.  */
+
+static void
+gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
+{
+  tree expr = *expr_p;
+  gomp_target *stmt;
+  tree clauses, t;
+
+  clauses = OACC_DECLARE_CLAUSES (expr);
+
+  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      tree attrs, decl = OMP_CLAUSE_DECL (t);
+
+      if (TREE_CODE (decl) == MEM_REF)
+	continue;
+
+      omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
+
+      attrs = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
+      if (attrs)
+	DECL_ATTRIBUTES (decl) = remove_attribute ("oacc declare", attrs);
+    }
+
+  stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				  clauses);
+
+  gimplify_seq_add_stmt (pre_p, stmt);
+
+  clauses = OACC_DECLARE_RETURN_CLAUSES (expr);
+  if (clauses)
+    {
+      struct gimplify_omp_ctx *c;
+
+      /* Any clauses that affect the state of a variable prior
+         to return are saved and dealt with after the body has
+         been gimplified.  */
+
+      gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA,
+				 OACC_DECLARE);
+
+      c = gimplify_omp_ctxp;
+      gimplify_omp_ctxp = c->outer_context;
+      delete_omp_context (c);
+
+      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				      clauses);
+      gimplify_omp_ctxp->declare_returns = stmt;
+    }
+
+  *expr_p = NULL_TREE;
+}
+
 /* Gimplify the contents of an OMP_PARALLEL statement.  This involves
    gimplification of the body, as well as scanning the body for used
    variables.  We need to do this scan now, because variable-sized
@@ -9595,11 +9675,15 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  break;
 
 	case OACC_HOST_DATA:
-	case OACC_DECLARE:
 	  sorry ("directive not yet implemented");
 	  ret = GS_ALL_DONE;
 	  break;
 
+	case OACC_DECLARE:
+	  gimplify_oacc_declare (expr_p, pre_p);
+	  ret = GS_ALL_DONE;
+	  break;
+
 	case OACC_KERNELS:
 	  if (OACC_KERNELS_COMBINED (*expr_p))
 	    sorry ("directive not yet implemented");
@@ -10274,6 +10358,28 @@ gimplify_body (tree fndecl, bool do_parms)
       gimplify_seq_add_stmt (&seq, outer_stmt);
     }
 
+  if (flag_openacc && gimplify_omp_ctxp)
+    {
+      while (gimplify_omp_ctxp)
+	{
+	  struct gimplify_omp_ctx *c;
+
+	  if (gimplify_omp_ctxp->declare_returns)
+	    {
+              /* Clauses are present that affect the state of a
+                 variable, insert the statment to handle this
+                 as the very last statement.  */
+
+	      gimplify_seq_add_stmt (&seq, gimplify_omp_ctxp->declare_returns);
+	      gimplify_omp_ctxp->declare_returns = NULL;
+	    }
+
+	  c = gimplify_omp_ctxp;
+	  gimplify_omp_ctxp = c->outer_context;
+	  delete_omp_context (c);
+	}
+    }
+
   /* The body must contain exactly one statement, a GIMPLE_BIND.  If this is
      not the case, wrap everything in a GIMPLE_BIND to make it so.  */
   if (gimple_code (outer_stmt) == GIMPLE_BIND
diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def
index ea9cf0d..4af3640 100644
--- a/gcc/omp-builtins.def
+++ b/gcc/omp-builtins.def
@@ -317,3 +317,7 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TARGET_ENTER_EXIT_DATA,
 		  BT_FN_VOID_INT_SIZE_PTR_PTR_PTR_UINT_PTR, ATTR_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS, "GOMP_teams",
 		  BT_FN_VOID_UINT_UINT, ATTR_NOTHROW_LIST)
+DEF_GOACC_BUILTIN (BUILT_IN_GOACC_STATIC, "GOACC_register_static",
+		   BT_FN_VOID_PTR_INT_UINT, ATTR_NOTHROW_LIST)
+DEF_GOACC_BUILTIN (BUILT_IN_GOACC_DECLARE, "GOACC_declare",
+		   BT_FN_VOID_INT_SIZE_PTR_PTR_PTR, ATTR_NOTHROW_LIST)
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index ad7c017..2e75c8f 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -11330,6 +11330,7 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -11563,6 +11564,9 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       start_ix = BUILT_IN_GOACC_ENTER_EXIT_DATA;
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      start_ix = BUILT_IN_GOACC_DECLARE;
+      break;
     default:
       gcc_unreachable ();
     }
@@ -11685,6 +11689,7 @@ expand_omp_target (struct omp_region *region)
   switch (start_ix)
     {
     case BUILT_IN_GOACC_DATA_START:
+    case BUILT_IN_GOACC_DECLARE:
     case BUILT_IN_GOMP_TARGET_DATA:
       break;
     case BUILT_IN_GOMP_TARGET:
@@ -11967,6 +11972,7 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent,
 		case GF_OMP_TARGET_KIND_EXIT_DATA:
 		case GF_OMP_TARGET_KIND_OACC_UPDATE:
 		case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+		case GF_OMP_TARGET_KIND_OACC_DECLARE:
 		  /* ..., other than for those stand-alone directives...  */
 		  region = NULL;
 		  break;
@@ -14095,6 +14101,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -14169,6 +14176,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	  case GOMP_MAP_FORCE_TOFROM:
 	  case GOMP_MAP_FORCE_PRESENT:
 	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_LINK:
 	    gcc_assert (is_gimple_omp_oacc (stmt));
 	    break;
 	  default:
@@ -15839,6 +15848,7 @@ make_gimple_omp_edges (basic_block bb, struct omp_region **region,
 	case GF_OMP_TARGET_KIND_EXIT_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  cur_region = cur_region->outer;
 	  break;
 	default:
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
new file mode 100644
index 0000000..b036c63
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -0,0 +1,83 @@
+/* Test valid uses of declare directive.  */
+/* { dg-do compile } */
+
+int v0;
+#pragma acc declare create(v0)
+
+int v1;
+#pragma acc declare copyin(v1)
+
+int *v2;
+#pragma acc declare deviceptr(v2)
+
+int v3;
+#pragma acc declare device_resident(v3)
+
+int v4;
+#pragma acc declare link(v4)
+
+int v5, v6, v7, v8;
+#pragma acc declare create(v5, v6) copyin(v7, v8)
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare create(va0)
+
+  int va1;
+#pragma acc declare copyin(va1)
+
+  int *va2;
+#pragma acc declare deviceptr(va2)
+
+  int va3;
+#pragma acc declare device_resident(va3)
+
+  extern int ve0;
+#pragma acc declare create(ve0)
+
+  extern int ve1;
+#pragma acc declare copyin(ve1)
+
+  extern int *ve2;
+#pragma acc declare deviceptr(ve2)
+
+  extern int ve3;
+#pragma acc declare device_resident(ve3)
+
+  extern int ve4;
+#pragma acc declare link(ve4)
+
+  int va5;
+#pragma acc declare copy(va5)
+
+  int va6;
+#pragma acc declare copyout(va6)
+
+  int va7;
+#pragma acc declare present(va7)
+
+  int va8;
+#pragma acc declare present_or_copy(va8)
+
+  int va9;
+#pragma acc declare present_or_copyin(va9)
+
+  int va10;
+#pragma acc declare present_or_copyout(va10)
+
+  int va11;
+#pragma acc declare present_or_create(va11)
+
+ a:
+  {
+    int va0;
+#pragma acc declare create(va0)
+    if (v1)
+      goto a;
+    else
+      goto b;
+  }
+ b:;
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
new file mode 100644
index 0000000..7979f0c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -0,0 +1,68 @@
+/* Test invalid uses of declare directive.  */
+/* { dg-do compile } */
+
+#pragma acc declare /* { dg-error "no valid clauses" } */
+
+#pragma acc declare create(undeclared) /* { dg-error "undeclared" } */
+/* { dg-error "no valid clauses" "second error" { target *-*-* } 6 } */
+
+int v0[10];
+#pragma acc declare create(v0[1:3]) /* { dg-error "subarray" } */
+
+int v1;
+#pragma acc declare create(v1, v1) /* { dg-error "more than once" } */
+
+int v2;
+#pragma acc declare create(v2) /* { dg-message "previous directive" } */
+#pragma acc declare copyin(v2) /* { dg-error "more than once" } */
+
+int v3;
+#pragma acc declare copy(v3) /* { dg-error "at file scope" } */
+
+int v4;
+#pragma acc declare copyout(v4) /* { dg-error "at file scope" } */
+
+int v5;
+#pragma acc declare present(v5) /* { dg-error "at file scope" } */
+
+int v6;
+#pragma acc declare present_or_copy(v6) /* { dg-error "at file scope" } */
+
+int v7;
+#pragma acc declare present_or_copyin(v7) /* { dg-error "at file scope" } */
+
+int v8;
+#pragma acc declare present_or_copyout(v8) /* { dg-error "at file scope" } */
+
+int v9;
+#pragma acc declare present_or_create(v9) /* { dg-error "at file scope" } */
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare link(va0) /* { dg-error "global variable" } */
+
+  extern int ve0;
+#pragma acc declare copy(ve0) /* { dg-error "invalid use of" } */
+
+  extern int ve1;
+#pragma acc declare copyout(ve1) /* { dg-error "invalid use of" } */
+
+  extern int ve2;
+#pragma acc declare present(ve2) /* { dg-error "invalid use of" } */
+
+  extern int ve3;
+#pragma acc declare present_or_copy(ve3) /* { dg-error "invalid use of" } */
+
+  extern int ve4;
+#pragma acc declare present_or_copyin(ve4) /* { dg-error "invalid use of" } */
+
+  extern int ve5;
+#pragma acc declare present_or_copyout(ve5) /* { dg-error "invalid use of" } */
+
+  extern int ve6;
+#pragma acc declare present_or_create(ve6) /* { dg-error "invalid use of" } */
+
+#pragma acc declare present (v9) /* { dg-error "invalid use of" } */
+}
diff --git a/gcc/tree.def b/gcc/tree.def
index d0a3bd6..d183c5c 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -1186,8 +1186,9 @@ DEFTREECODE (OMP_TASKGROUP, "omp_taskgroup", tcc_statement, 1)
 DEFTREECODE (OACC_CACHE, "oacc_cache", tcc_statement, 1)
 
 /* OpenACC - #pragma acc declare [clause1 ... clauseN]
-   Operand 0: OACC_DECLARE_CLAUSES: List of clauses.  */
-DEFTREECODE (OACC_DECLARE, "oacc_declare", tcc_statement, 1)
+   Operand 0: OACC_DECLARE_CLAUSES: List of clauses.
+   Operand 1: OACC_DECLARE_RETURN_CLAUSES: List of clauses for returns.  */
+DEFTREECODE (OACC_DECLARE, "oacc_declare", tcc_statement, 2)
 
 /* OpenACC - #pragma acc enter data [clause1 ... clauseN]
    Operand 0: OACC_ENTER_DATA_CLAUSES: List of clauses.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index ece083b..f325ea6 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1243,6 +1243,8 @@ extern void protected_set_expr_location (tree, location_t);
 
 #define OACC_DECLARE_CLAUSES(NODE) \
   TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 0)
+#define OACC_DECLARE_RETURN_CLAUSES(NODE) \
+  TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 1)
 
 #define OACC_ENTER_DATA_CLAUSES(NODE) \
   TREE_OPERAND (OACC_ENTER_DATA_CHECK (NODE), 0)
diff --git a/gcc/varpool.c b/gcc/varpool.c
index 7d11e20..c83877f 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -143,6 +143,42 @@ varpool_node::create_empty (void)
   return node;
 }   
 
+static void
+make_offloadable_1 (varpool_node *node, tree decl ATTRIBUTE_UNUSED)
+{
+  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+  g->have_offload = true;
+  if (!in_lto_p)
+    vec_safe_push (offload_vars, decl);
+  node->force_output = 1;
+#endif
+}
+
+void
+make_offloadable (varpool_node *node, tree decl)
+{
+  tree attrs;
+
+  if (node->offloadable)
+    return;
+
+  if (flag_openmp)
+    {
+      make_offloadable_1 (node, decl);
+      return;
+    }
+
+  attrs = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
+  if (attrs)
+    {
+      make_offloadable_1 (node, decl);
+
+      DECL_ATTRIBUTES (decl)
+	  = remove_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
+    }
+}
+
 /* Return varpool node assigned to DECL.  Create new one when needed.  */
 varpool_node *
 varpool_node::get_create (tree decl)
@@ -150,22 +186,18 @@ varpool_node::get_create (tree decl)
   varpool_node *node = varpool_node::get (decl);
   gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
   if (node)
-    return node;
+    {
+      if (flag_openacc && !DECL_EXTERNAL (decl))
+	make_offloadable (node, decl);
+      return node;
+    }
 
   node = varpool_node::create_empty ();
   node->decl = decl;
 
   if ((flag_openacc || flag_openmp) && !DECL_EXTERNAL (decl)
       && lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
-    {
-      node->offloadable = 1;
-#ifdef ENABLE_OFFLOADING
-      g->have_offload = true;
-      if (!in_lto_p)
-	vec_safe_push (offload_vars, decl);
-      node->force_output = 1;
-#endif
-    }
+    make_offloadable (node, decl);
 
   node->register_symbol ();
   return node;
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index f834dec..4128912 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -73,6 +73,11 @@ enum gomp_map_kind
        POINTER_SIZE_UNITS.  */
     GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
     /* Do not map, copy bits for firstprivate instead.  */
+    /* OpenACC device_resident.  */
+    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
+    /* OpenACC link.  */
+    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
+    /* Allocate.  */
     GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
     /* Similarly, but store the value in the pointer rather than
        pointed by the pointer.  */
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 2153661..5f97e32 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -372,12 +372,14 @@ GOACC_2.0 {
   global:
 	GOACC_data_end;
 	GOACC_data_start;
+	GOACC_declare;
 	GOACC_enter_exit_data;
 	GOACC_parallel;
 	GOACC_update;
 	GOACC_wait;
 	GOACC_get_thread_num;
 	GOACC_get_num_threads;
+	GOACC_register_static;
 };
 
 GOACC_2.0.1 {
diff --git a/libgomp/oacc-init.c b/libgomp/oacc-init.c
index a0e62a4..6f31e2a 100644
--- a/libgomp/oacc-init.c
+++ b/libgomp/oacc-init.c
@@ -250,6 +250,8 @@ acc_shutdown_1 (acc_device_t d)
   /* Get the base device for this device type.  */
   base_dev = resolve_device (d, true);
 
+  goacc_deallocate_static (d);
+
   ndevs = base_dev->get_num_devices_func ();
 
   /* Unload all the devices of this type that have been opened.  */
@@ -432,7 +434,9 @@ goacc_attach_host_thread_to_device (int ord)
 void
 acc_init (acc_device_t d)
 {
-  if (!cached_base_dev)
+  bool init = !cached_base_dev;
+
+  if (init)
     gomp_init_targets_once ();
 
   gomp_mutex_lock (&acc_device_lock);
@@ -440,6 +444,9 @@ acc_init (acc_device_t d)
   cached_base_dev = acc_init_1 (d);
 
   gomp_mutex_unlock (&acc_device_lock);
+
+  if (init)
+    goacc_allocate_static (d);
   
   goacc_attach_host_thread_to_device (-1);
 }
diff --git a/libgomp/oacc-int.h b/libgomp/oacc-int.h
index f11e216..560f68b 100644
--- a/libgomp/oacc-int.h
+++ b/libgomp/oacc-int.h
@@ -99,6 +99,9 @@ void goacc_restore_bind (void);
 void goacc_lazy_initialize (void);
 void goacc_host_init (void);
 
+void goacc_allocate_static (acc_device_t);
+void goacc_deallocate_static (acc_device_t);
+
 #ifdef HAVE_ATTRIBUTE_VISIBILITY
 # pragma GCC visibility pop
 #endif
diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index b150106..e374045 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -49,6 +49,68 @@ find_pset (int pos, size_t mapnum, unsigned short *kinds)
   return kind == GOMP_MAP_TO_PSET;
 }
 
+static struct oacc_static
+{
+  void *addr;
+  size_t size;
+  unsigned short mask;
+  bool free;
+  struct oacc_static *next;
+} *oacc_statics;
+
+static bool alloc_done = false;
+
+void
+goacc_allocate_static (acc_device_t d)
+{
+  struct oacc_static *s;
+
+  if (alloc_done)
+    assert (0);
+
+  for (s = oacc_statics; s; s = s->next)
+    {
+      void *d;
+
+      switch (s->mask)
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	  break;
+
+	case GOMP_MAP_FORCE_TO:
+	  d = acc_deviceptr (s->addr);
+	  acc_memcpy_to_device (d, s->addr, s->size);
+	  break;
+
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	case GOMP_MAP_DEVICE_RESIDENT:
+	case GOMP_MAP_LINK:
+	  break;
+
+	default:
+	  assert (0);
+	  break;
+	}
+    }
+
+  alloc_done = true;
+}
+
+void
+goacc_deallocate_static (acc_device_t d)
+{
+  struct oacc_static *s;
+  unsigned short mask = GOMP_MAP_FORCE_DEALLOC;
+
+  if (!alloc_done)
+    return;
+
+  for (s = oacc_statics; s; s = s->next)
+    GOACC_enter_exit_data (d, 1, &s->addr, &s->size, &mask, 0, 0);
+
+  alloc_done = false;
+}
+
 static void goacc_wait (int async, int num_waits, va_list *ap);
 
 
@@ -501,3 +563,77 @@ GOACC_get_thread_num (void)
 {
   return 0;
 }
+
+void
+GOACC_register_static (void *addr, int size, unsigned int mask)
+{
+  struct oacc_static *s;
+
+  s = (struct oacc_static *) malloc (sizeof (struct oacc_static));
+  s->addr = addr;
+  s->size = (size_t) size;
+  s->mask = mask;
+  s->free = false;
+  s->next = oacc_statics;
+
+  oacc_statics = s;
+}
+
+void
+GOACC_declare (int device, size_t mapnum,
+	       void **hostaddrs, size_t *sizes, unsigned short *kinds)
+{
+  int i;
+
+  for (i = 0; i < mapnum; i++)
+    {
+      unsigned char kind = kinds[i] & 0xff;
+
+      if (kind == GOMP_MAP_POINTER || kind == GOMP_MAP_TO_PSET)
+	continue;
+
+      switch (kind)
+	{
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_DEALLOC:
+	  case GOMP_MAP_FORCE_FROM:
+	  case GOMP_MAP_FORCE_TO:
+	  case GOMP_MAP_POINTER:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	    break;
+
+	  case GOMP_MAP_ALLOC:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      {
+		GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	      }
+	    break;
+
+	  case GOMP_MAP_TO:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    kinds[i] = GOMP_MAP_FORCE_FROM;
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_PRESENT:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      gomp_fatal ("[%p,%zd] is not mapped", hostaddrs[i], sizes[i]);
+	    break;
+
+	  default:
+	    assert (0);
+	    break;
+	}
+    }
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
new file mode 100644
index 0000000..8fbec4d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
@@ -0,0 +1,122 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <openacc.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define N 8
+
+void
+subr2 (int *a)
+{
+  int i;
+  int f[N];
+#pragma acc declare copyout (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i] + f[i];
+      }
+  }
+}
+
+void
+subr1 (int *a)
+{
+  int f[N];
+#pragma acc declare copy (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    int i;
+
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i];
+      }
+  }
+}
+
+int b[8];
+#pragma acc declare create (b)
+
+int d[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+#pragma acc declare copyin (d)
+
+int
+main (int argc, char **argv)
+{
+  int a[N];
+  int e[N];
+#pragma acc declare create (e)
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = i + 1;
+
+  if (!acc_is_present (&b, sizeof (b)))
+    abort ();
+
+  if (!acc_is_present (&d, sizeof (d)))
+    abort ();
+
+  if (!acc_is_present (&e, sizeof (e)))
+    abort ();
+
+#pragma acc parallel copyin (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        b[i] = a[i];
+        a[i] = b[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != i + 1)
+	abort ();
+    }
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        e[i] = a[i] + d[i];
+	a[i] = e[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != (i + 1) * 2)
+	abort ();
+    }
+
+  for (i = 0; i < N; i++)
+    {
+      a[i] = 1234;
+    }
+
+  subr1 (&a[0]);
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != 1234 * 2)
+	abort ();
+    }
+
+  subr2 (&a[0]);
+
+  for (i = 0; i < 1; i++)
+    {
+      if (a[i] != 1234 * 6)
+	abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c
new file mode 100644
index 0000000..1e2f6ce
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c
@@ -0,0 +1,13 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+int
+main (int argc, char **argv)
+{
+  int a[8] __attribute__((unused));
+
+  __builtin_printf ("CheCKpOInT\n");
+#pragma acc declare present (a)
+}
+
+/* { dg-output "CheCKpOInT" } */
+/* { dg-shouldfail "" } */

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-10-27 20:20 [OpenACC] declare directive James Norris
@ 2015-10-28 16:33 ` Cesar Philippidis
  2015-10-28 16:33   ` James Norris
  2015-11-03 16:31 ` James Norris
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 51+ messages in thread
From: Cesar Philippidis @ 2015-10-28 16:33 UTC (permalink / raw)
  To: James Norris, GCC Patches
  Cc: Tobias Burnus, Jakub Jelinek, hubicka, Nathan Sidwell, Joseph S. Myers

On 10/27/2015 01:18 PM, James Norris wrote:

>     This patch adds the processing of OpenACC declare directive in C
>     and C++. (Note: Support in Fortran is already in trunk.)
>     Commentary on the changes is included as an attachment (NOTES).

A quick diff of gomp4 and trunk reveals quite a few fortran changes that
aren't present in trunk. Can you post those changes as a separate patch?

Thanks,
Cesar

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-10-28 16:33 ` Cesar Philippidis
@ 2015-10-28 16:33   ` James Norris
  0 siblings, 0 replies; 51+ messages in thread
From: James Norris @ 2015-10-28 16:33 UTC (permalink / raw)
  To: Cesar Philippidis, GCC Patches
  Cc: Tobias Burnus, Jakub Jelinek, hubicka, Nathan Sidwell, Joseph S. Myers

Cesar,

On 10/28/2015 11:32 AM, Cesar Philippidis wrote:
> On 10/27/2015 01:18 PM, James Norris wrote:
>
>> >     This patch adds the processing of OpenACC declare directive in C
>> >     and C++. (Note: Support in Fortran is already in trunk.)
>> >     Commentary on the changes is included as an attachment (NOTES).
> A quick diff of gomp4 and trunk reveals quite a few fortran changes that
> aren't present in trunk. Can you post those changes as a separate patch?
>
> Thanks,
> Cesar
>

Yes, I will be doing that.

Thanks,
Jim

^ permalink raw reply	[flat|nested] 51+ messages in thread

* OpenACC declare directive updates
@ 2015-11-02 13:46 James Norris
  2015-11-04 12:32 ` James Norris
  0 siblings, 1 reply; 51+ messages in thread
From: James Norris @ 2015-11-02 13:46 UTC (permalink / raw)
  To: GCC Patches, fortran, Tobias Burnus

[-- Attachment #1: Type: text/plain, Size: 419 bytes --]


     This patch updates the processing of OpenACC declare directive for
     Fortran in the following areas:

         1) module support
         2) device_resident and link clauses
         3) clause checking
         4) directive generation

     Commentary on the changes is included as an attachment (NOTES).

     All of the code is in the gomp-4_0-branch.

     Regtested on x86_64-linux.

     Thanks!
     Jim

[-- Attachment #2: NOTES --]
[-- Type: text/plain, Size: 2670 bytes --]

    Background

        The declare directive is used to allocate device memory for the
        entire scope of a variable / array within a program, function,
        or subroutine. Consider the following example.

            module vars
              integer b
              !$acc declare device_resident (b)
              integer c
              !$acc declare link (c)
            end module vars
            
            program main
              use vars
              integer, parameter :: N = 8
              integer :: a(N)
            
              a(:) = 2
              c = 12
            
              !$acc parallel copy (a) present (b) copyin(c)
              do i = 1, N
                b = a(i)
                c = b
                a(i) = c + i
              end do
              !$acc end parallel
            
            end program

        In the example, 'b' will be allocated on the device at the outset
        of device activity and be available for the duration. Whereas the
        allocation of 'c' will be delayed until the parallel region is
        entered. The device memory for 'c' will be deallocated upon exit
        of the parallel region.

    Fortran front-end

        The changes are concentrated into four (4) areas.

        1) module support
            The neccesary functionality has been added to handle the
            reading in and writing out of the appropriate attributes
            for the declare directive. Additional functionality has
            been added at read in time to setup the required declare
            handling.

        2) device_resident and link clauses
            Add the functionality necessary to process the link and
            device_resident clauses.

        3) clause checking
            The clause checking has been cleaned up to better check
            for duplicates and correctness.

        4) directive generation

            Prior to handling the fortran execution body a code
            body is created to handle the clause(s) that accompany
            the declare directive(s). Each clause is examined and
            determined whether the clause need to be modified to 
            perform an action at the beginning of the module, function,
            subroutine, or program. Furthermore, an additional
            clause may be added to the list to perform an action
            at the time the function or subroutine returns.

            Once all the clauses have been handled, the code body
            is added to the chain.

    libgomp

        TODO

    Testing

        New compile and runtime tests have been added. Also some have
        been modified.

[-- Attachment #3: declare2.patch --]
[-- Type: text/x-patch, Size: 43734 bytes --]

diff --git a/gcc/fortran/dump-parse-tree.c b/gcc/fortran/dump-parse-tree.c
index 83ecbaa..e953160 100644
--- a/gcc/fortran/dump-parse-tree.c
+++ b/gcc/fortran/dump-parse-tree.c
@@ -2572,10 +2572,14 @@ show_namespace (gfc_namespace *ns)
 
   if (ns->oacc_declare_clauses)
     {
+      struct gfc_oacc_declare *decl;
       /* Dump !$ACC DECLARE clauses.  */
-      show_indent ();
-      fprintf (dumpfile, "!$ACC DECLARE");
-      show_omp_clauses (ns->oacc_declare_clauses);
+      for (decl = ns->oacc_declare_clauses; decl; decl = decl->next)
+	{
+	  show_indent ();
+	  fprintf (dumpfile, "!$ACC DECLARE");
+	  show_omp_clauses (decl->clauses);
+	}
     }
 
   fputc ('\n', dumpfile);
diff --git a/gcc/fortran/f95-lang.c b/gcc/fortran/f95-lang.c
index 67b0bac..2758a28 100644
--- a/gcc/fortran/f95-lang.c
+++ b/gcc/fortran/f95-lang.c
@@ -103,6 +103,8 @@ static const struct attribute_spec gfc_attribute_table[] =
        affects_type_identity } */
   { "omp declare target", 0, 0, true,  false, false,
     gfc_handle_omp_declare_target_attribute, false },
+  { "oacc declare", 0, 0, true,  false, false,
+    gfc_handle_omp_declare_target_attribute, false },
   { NULL,		  0, 0, false, false, false, NULL, false }
 };
 
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 90f63cf..17c2357 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -840,6 +840,13 @@ typedef struct
   /* Mentioned in OMP DECLARE TARGET.  */
   unsigned omp_declare_target:1;
 
+  /* Mentioned in OACC DECLARE.  */
+  unsigned oacc_declare_create:1;
+  unsigned oacc_declare_copyin:1;
+  unsigned oacc_declare_deviceptr:1;
+  unsigned oacc_declare_device_resident:1;
+  unsigned oacc_declare_link:1;
+
   /* Attributes set by compiler extensions (!GCC$ ATTRIBUTES).  */
   unsigned ext_attr:EXT_ATTR_NUM;
 
@@ -1105,7 +1112,9 @@ enum gfc_omp_map_op
   OMP_MAP_FORCE_FROM,
   OMP_MAP_FORCE_TOFROM,
   OMP_MAP_FORCE_PRESENT,
-  OMP_MAP_FORCE_DEVICEPTR
+  OMP_MAP_FORCE_DEVICEPTR,
+  OMP_MAP_DEVICE_RESIDENT,
+  OMP_MAP_LINK
 };
 
 /* For use in OpenMP clauses in case we need extra information
@@ -1146,6 +1155,7 @@ enum
   OMP_LIST_FROM,
   OMP_LIST_REDUCTION,
   OMP_LIST_DEVICE_RESIDENT,
+  OMP_LIST_LINK,
   OMP_LIST_USE_DEVICE,
   OMP_LIST_CACHE,
   OMP_LIST_NUM
@@ -1232,6 +1242,20 @@ gfc_omp_clauses;
 #define gfc_get_omp_clauses() XCNEW (gfc_omp_clauses)
 
 
+/* Node in the linked list used for storing !$oacc declare constructs.  */
+
+typedef struct gfc_oacc_declare
+{
+  struct gfc_oacc_declare *next;
+  bool module_var;
+  gfc_omp_clauses *clauses;
+  gfc_omp_clauses *return_clauses;
+}
+gfc_oacc_declare;
+
+#define gfc_get_oacc_declare() XCNEW (gfc_oacc_declare)
+
+
 /* Node in the linked list used for storing !$omp declare simd constructs.  */
 
 typedef struct gfc_omp_declare_simd
@@ -1644,7 +1668,7 @@ typedef struct gfc_namespace
   struct gfc_data *data, *old_data;
 
   /* !$ACC DECLARE clauses.  */
-  gfc_omp_clauses *oacc_declare_clauses;
+  struct gfc_oacc_declare *oacc_declare_clauses;
 
   gfc_charlen *cl_list, *old_cl_list;
 
@@ -2321,7 +2345,7 @@ enum gfc_exec_op
   EXEC_OACC_KERNELS_LOOP, EXEC_OACC_PARALLEL_LOOP,
   EXEC_OACC_PARALLEL, EXEC_OACC_KERNELS, EXEC_OACC_DATA, EXEC_OACC_HOST_DATA,
   EXEC_OACC_LOOP, EXEC_OACC_UPDATE, EXEC_OACC_WAIT, EXEC_OACC_CACHE,
-  EXEC_OACC_ENTER_DATA, EXEC_OACC_EXIT_DATA,
+  EXEC_OACC_ENTER_DATA, EXEC_OACC_EXIT_DATA, EXEC_OACC_DECLARE,
   EXEC_OMP_CRITICAL, EXEC_OMP_DO, EXEC_OMP_FLUSH, EXEC_OMP_MASTER,
   EXEC_OMP_ORDERED, EXEC_OMP_PARALLEL, EXEC_OMP_PARALLEL_DO,
   EXEC_OMP_PARALLEL_SECTIONS, EXEC_OMP_PARALLEL_WORKSHARE,
@@ -2403,6 +2427,7 @@ typedef struct gfc_code
     struct gfc_code *which_construct;
     int stop_code;
     gfc_entry_list *entry;
+    gfc_oacc_declare *oacc_declare;
     gfc_omp_clauses *omp_clauses;
     const char *omp_name;
     gfc_omp_namelist *omp_namelist;
@@ -2905,6 +2930,7 @@ gfc_expr *gfc_get_parentheses (gfc_expr *);
 /* openmp.c */
 struct gfc_omp_saved_state { void *ptrs[2]; int ints[1]; };
 void gfc_free_omp_clauses (gfc_omp_clauses *);
+void gfc_free_oacc_declare_clauses (struct gfc_oacc_declare *);
 void gfc_free_omp_declare_simd (gfc_omp_declare_simd *);
 void gfc_free_omp_declare_simd_list (gfc_omp_declare_simd *);
 void gfc_free_omp_udr (gfc_omp_udr *);
@@ -3222,4 +3248,8 @@ gfc_expr *gfc_simplify_ieee_functions (gfc_expr *);
 
 bool gfc_is_reallocatable_lhs (gfc_expr *);
 
+/* trans-decl.c */
+
+void finish_oacc_declare (gfc_namespace *, enum sym_flavor);
+
 #endif /* GCC_GFORTRAN_H  */
diff --git a/gcc/fortran/module.c b/gcc/fortran/module.c
index f0d84a4..63d8444 100644
--- a/gcc/fortran/module.c
+++ b/gcc/fortran/module.c
@@ -1987,7 +1987,9 @@ enum ab_attribute
   AB_IS_CLASS, AB_PROCEDURE, AB_PROC_POINTER, AB_ASYNCHRONOUS, AB_CODIMENSION,
   AB_COARRAY_COMP, AB_VTYPE, AB_VTAB, AB_CONTIGUOUS, AB_CLASS_POINTER,
   AB_IMPLICIT_PURE, AB_ARTIFICIAL, AB_UNLIMITED_POLY, AB_OMP_DECLARE_TARGET,
-  AB_ARRAY_OUTER_DEPENDENCY, AB_MODULE_PROCEDURE
+  AB_ARRAY_OUTER_DEPENDENCY, AB_MODULE_PROCEDURE, AB_OACC_DECLARE_CREATE,
+  AB_OACC_DECLARE_COPYIN, AB_OACC_DECLARE_DEVICEPTR,
+  AB_OACC_DECLARE_DEVICE_RESIDENT, AB_OACC_DECLARE_LINK
 };
 
 static const mstring attr_bits[] =
@@ -2044,6 +2046,11 @@ static const mstring attr_bits[] =
     minit ("OMP_DECLARE_TARGET", AB_OMP_DECLARE_TARGET),
     minit ("ARRAY_OUTER_DEPENDENCY", AB_ARRAY_OUTER_DEPENDENCY),
     minit ("MODULE_PROCEDURE", AB_MODULE_PROCEDURE),
+    minit ("OACC_DECLARE_CREATE", AB_OACC_DECLARE_CREATE),
+    minit ("OACC_DECLARE_COPYIN", AB_OACC_DECLARE_COPYIN),
+    minit ("OACC_DECLARE_DEVICEPTR", AB_OACC_DECLARE_DEVICEPTR),
+    minit ("OACC_DECLARE_DEVICE_RESIDENT", AB_OACC_DECLARE_DEVICE_RESIDENT),
+    minit ("OACC_DECLARE_LINK", AB_OACC_DECLARE_LINK),
     minit (NULL, -1)
 };
 
@@ -2231,6 +2238,16 @@ mio_symbol_attribute (symbol_attribute *attr)
 	MIO_NAME (ab_attribute) (AB_MODULE_PROCEDURE, attr_bits);
 	  no_module_procedures = false;
 	}
+      if (attr->oacc_declare_create)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_CREATE, attr_bits);
+      if (attr->oacc_declare_copyin)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_COPYIN, attr_bits);
+      if (attr->oacc_declare_deviceptr)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_DEVICEPTR, attr_bits);
+      if (attr->oacc_declare_device_resident)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_DEVICE_RESIDENT, attr_bits);
+      if (attr->oacc_declare_link)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_LINK, attr_bits);
 
       mio_rparen ();
 
@@ -2403,6 +2420,21 @@ mio_symbol_attribute (symbol_attribute *attr)
 	    case AB_MODULE_PROCEDURE:
 	      attr->module_procedure =1;
 	      break;
+	    case AB_OACC_DECLARE_CREATE:
+	      attr->oacc_declare_create = 1;
+	      break;
+	    case AB_OACC_DECLARE_COPYIN:
+	      attr->oacc_declare_copyin = 1;
+	      break;
+	    case AB_OACC_DECLARE_DEVICEPTR:
+	      attr->oacc_declare_deviceptr = 1;
+	      break;
+	    case AB_OACC_DECLARE_DEVICE_RESIDENT:
+	      attr->oacc_declare_device_resident = 1;
+	      break;
+	    case AB_OACC_DECLARE_LINK:
+	      attr->oacc_declare_link = 1;
+	      break;
 	    }
 	}
     }
diff --git a/gcc/fortran/openmp.c b/gcc/fortran/openmp.c
index 6c78c97..485fac3 100644
--- a/gcc/fortran/openmp.c
+++ b/gcc/fortran/openmp.c
@@ -90,6 +90,25 @@ gfc_free_omp_clauses (gfc_omp_clauses *c)
   free (c);
 }
 
+/* Free oacc_declare structures.  */
+
+void
+gfc_free_oacc_declare_clauses (struct gfc_oacc_declare *oc)
+{
+  struct gfc_oacc_declare *decl = oc;
+
+  do
+    {
+      struct gfc_oacc_declare *next;
+
+      next = decl->next;
+      gfc_free_omp_clauses (decl->clauses);
+      free (decl);
+      decl = next;
+    }
+  while (decl);
+}
+
 /* Free expression list. */
 void
 gfc_free_expr_list (gfc_expr_list *list)
@@ -451,6 +470,7 @@ match_oacc_clause_gang (gfc_omp_clauses *cp)
 #define OMP_CLAUSE_DELETE		((uint64_t) 1 << 55)
 #define OMP_CLAUSE_AUTO			((uint64_t) 1 << 56)
 #define OMP_CLAUSE_TILE			((uint64_t) 1 << 57)
+#define OMP_CLAUSE_LINK			((uint64_t) 1 << 58)
 
 /* Helper function for OpenACC and OpenMP clauses involving memory
    mapping.  */
@@ -689,6 +709,12 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, uint64_t mask,
 					  true)
 	     == MATCH_YES)
 	continue;
+      if ((mask & OMP_CLAUSE_LINK)
+	  && gfc_match_omp_variable_list ("link (",
+					  &c->lists[OMP_LIST_LINK],
+					  true)
+	     == MATCH_YES)
+	continue;
       if ((mask & OMP_CLAUSE_OACC_DEVICE)
 	  && gfc_match ("device ( ") == MATCH_YES
 	  && gfc_match_omp_map_clause (&c->lists[OMP_LIST_MAP],
@@ -1171,7 +1197,7 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, uint64_t mask,
    | OMP_CLAUSE_CREATE | OMP_CLAUSE_DEVICEPTR | OMP_CLAUSE_DEVICE_RESIDENT    \
    | OMP_CLAUSE_PRESENT | OMP_CLAUSE_PRESENT_OR_COPY                          \
    | OMP_CLAUSE_PRESENT_OR_COPYIN | OMP_CLAUSE_PRESENT_OR_COPYOUT             \
-   | OMP_CLAUSE_PRESENT_OR_CREATE)
+   | OMP_CLAUSE_PRESENT_OR_CREATE | OMP_CLAUSE_LINK)
 #define OACC_UPDATE_CLAUSES \
   (OMP_CLAUSE_IF | OMP_CLAUSE_ASYNC | OMP_CLAUSE_HOST_SELF \
    | OMP_CLAUSE_OACC_DEVICE | OMP_CLAUSE_WAIT)
@@ -1288,12 +1314,86 @@ match
 gfc_match_oacc_declare (void)
 {
   gfc_omp_clauses *c;
+  gfc_omp_namelist *n;
+  gfc_namespace *ns = gfc_current_ns;
+  gfc_oacc_declare *new_oc;
+  bool module_var = false;
+
   if (gfc_match_omp_clauses (&c, OACC_DECLARE_CLAUSES, false, false, true)
       != MATCH_YES)
     return MATCH_ERROR;
 
-  new_st.ext.omp_clauses = c;
-  new_st.ext.omp_clauses->loc = gfc_current_locus;
+  for (n = c->lists[OMP_LIST_DEVICE_RESIDENT]; n != NULL; n = n->next)
+    n->sym->attr.oacc_declare_device_resident = 1;
+
+  for (n = c->lists[OMP_LIST_LINK]; n != NULL; n = n->next)
+    n->sym->attr.oacc_declare_link = 1;
+
+  for (n = c->lists[OMP_LIST_MAP]; n != NULL; n = n->next)
+    {
+      gfc_symbol *s = n->sym;
+      locus where = gfc_current_locus;
+
+      if (s->ns->proc_name && s->ns->proc_name->attr.proc == PROC_MODULE)
+	{
+	  if (n->u.map_op != OMP_MAP_FORCE_ALLOC
+	      && n->u.map_op != OMP_MAP_FORCE_TO)
+	    {
+	      gfc_error ("Invalid clause in module with $!ACC DECLARE at %L",
+			 &where);
+	      return MATCH_ERROR;
+	    }
+
+	  module_var = true;
+	}
+
+      if (s->attr.in_common)
+	{
+	  gfc_error ("Variable in a common block with $!ACC DECLARE at %L",
+		     &where);
+	  return MATCH_ERROR;
+	}
+
+      if (s->attr.use_assoc)
+	{
+	  gfc_error ("Variable is USE-associated with $!ACC DECLARE at %L",
+		     &where);
+	  return MATCH_ERROR;
+	}
+
+      if ((s->attr.dimension || s->attr.codimension)
+	  && s->attr.dummy && s->as->type != AS_EXPLICIT)
+	{
+	  gfc_error ("Assumed-size dummy array with $!ACC DECLARE at %L",
+		     &where);
+	  return MATCH_ERROR;
+	}
+
+      switch (n->u.map_op)
+	{
+	  case OMP_MAP_FORCE_ALLOC:
+	    s->attr.oacc_declare_create = 1;
+	    break;
+
+	  case OMP_MAP_FORCE_TO:
+	    s->attr.oacc_declare_copyin = 1;
+	    break;
+
+	  case OMP_MAP_FORCE_DEVICEPTR:
+	    s->attr.oacc_declare_deviceptr = 1;
+	    break;
+
+	  default:
+	    break;
+	}
+    }
+
+  new_oc = gfc_get_oacc_declare ();
+  new_oc->next = ns->oacc_declare_clauses;
+  new_oc->module_var = module_var;
+  new_oc->clauses = c;
+  ns->oacc_declare_clauses = new_oc;
+
   return MATCH_YES;
 }
 
@@ -2857,6 +2957,42 @@ oacc_compatible_clauses (gfc_omp_clauses *clauses, int list,
   return false;
 }
 
+/* Check if a variable appears in multiple clauses.  */
+
+static void
+resolve_omp_duplicate_list (gfc_omp_namelist *clause_list, bool openacc,
+			    int list)
+{
+  gfc_omp_namelist *n;
+  const char *error_msg = "Symbol %qs present on multiple clauses at %L";
+
+  /* OpenACC reduction clauses are compatible with everything.  We only
+     need to check if a reduction variable is used more than once.  */
+  if (openacc && list == OMP_LIST_REDUCTION)
+    {
+      hash_set<gfc_symbol *> reductions;
+
+      for (n = clause_list; n; n = n->next)
+	{
+	  if (reductions.contains (n->sym))
+	    gfc_error (error_msg, n->sym->name, &n->expr->where);
+	  else
+	    reductions.add (n->sym);
+	}
+
+      return;
+    }
+
+  /* Ensure that variables are only used in one clause.  */
+  for (n = clause_list; n; n = n->next)
+    {
+      if (n->sym->mark)
+	gfc_error (error_msg, n->sym->name, &n->expr->where);
+      else
+	n->sym->mark = 1;
+    }
+}
+
 /* OpenMP directive resolving routines.  */
 
 static void
@@ -4598,41 +4734,59 @@ resolve_oacc_loop (gfc_code *code)
 }
 
 
+/* Helper function for gfc_resolve_oacc_declare.  Scan omp_map_list LIST
+   in DECLARE at location LOC.  */
+
+static void
+resolve_oacc_declare_map (gfc_oacc_declare *declare, int list)
+{
+  gfc_oacc_declare *oc;
+  gfc_omp_namelist *n;
+
+  for (oc = declare; oc; oc = oc->next)
+    for (n = oc->clauses->lists[list]; n; n = n->next)
+      n->sym->mark = 0;
+
+  for (oc = declare; oc; oc = oc->next)
+    resolve_omp_duplicate_list (oc->clauses->lists[list], false, list);
+
+  for (oc = declare; oc; oc = oc->next)
+    for (n = oc->clauses->lists[list]; n; n = n->next)
+      n->sym->mark = 0;
+}
+
 void
 gfc_resolve_oacc_declare (gfc_namespace *ns)
 {
-  int list;
   gfc_omp_namelist *n;
-  locus loc;
+  gfc_oacc_declare *oc;
 
   if (ns->oacc_declare_clauses == NULL)
     return;
 
-  loc = ns->oacc_declare_clauses->loc;
+  for (oc = ns->oacc_declare_clauses; oc; oc = oc->next)
+    {
+      for (n = oc->clauses->lists[OMP_LIST_DEVICE_RESIDENT]; n; n = n->next)
+	{
+	  n->sym->mark = 0;
+	  if (n->sym->attr.flavor == FL_PARAMETER)
+	    gfc_error ("PARAMETER object %qs is not allowed at %L",
+		       n->sym->name, &gfc_current_locus);
 
-  for (list = OMP_LIST_DEVICE_RESIDENT;
-       list <= OMP_LIST_DEVICE_RESIDENT; list++)
-    for (n = ns->oacc_declare_clauses->lists[list]; n; n = n->next)
-      {
-	n->sym->mark = 0;
-	if (n->sym->attr.flavor == FL_PARAMETER)
-	  gfc_error ("PARAMETER object %qs is not allowed at %L", n->sym->name, &loc);
-      }
+	  check_array_not_assumed (n->sym, gfc_current_locus,
+				   "DEVICE_RESIDENT");	  
+	}
 
-  for (list = OMP_LIST_DEVICE_RESIDENT;
-       list <= OMP_LIST_DEVICE_RESIDENT; list++)
-    for (n = ns->oacc_declare_clauses->lists[list]; n; n = n->next)
-      {
-	if (n->sym->mark)
-	  gfc_error ("Symbol %qs present on multiple clauses at %L",
-		     n->sym->name, &loc);
-	else
-	  n->sym->mark = 1;
-      }
+      for (n = oc->clauses->lists[OMP_LIST_MAP]; n; n = n->next)
+	if (n->expr && n->expr->ref->type == REF_ARRAY)
+	  gfc_error ("Subarray %qs is not allowed in $!ACC DECLARE at %L",
+		     n->sym->name, &n->expr->where);
+    }
 
-  for (n = ns->oacc_declare_clauses->lists[OMP_LIST_DEVICE_RESIDENT]; n;
-       n = n->next)
-    check_array_not_assumed (n->sym, loc, "DEVICE_RESIDENT");
+  /* Check for duplicate link, device_resident and data clauses.  */
+  resolve_oacc_declare_map (ns->oacc_declare_clauses, OMP_LIST_LINK);
+  resolve_oacc_declare_map (ns->oacc_declare_clauses, OMP_LIST_DEVICE_RESIDENT);
+  resolve_oacc_declare_map (ns->oacc_declare_clauses, OMP_LIST_MAP);
 }
 
 
diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
index 4925c7e..be9d0c7 100644
--- a/gcc/fortran/parse.c
+++ b/gcc/fortran/parse.c
@@ -1381,7 +1381,7 @@ next_statement (void)
   case ST_EQUIVALENCE: case ST_NAMELIST: case ST_STATEMENT_FUNCTION: \
   case ST_TYPE: case ST_INTERFACE: case ST_OMP_THREADPRIVATE: \
   case ST_PROCEDURE: case ST_OMP_DECLARE_SIMD: case ST_OMP_DECLARE_REDUCTION: \
-  case ST_OMP_DECLARE_TARGET: case ST_OACC_ROUTINE
+  case ST_OMP_DECLARE_TARGET: case ST_OACC_ROUTINE: case ST_OACC_DECLARE
 
 /* Block end statements.  Errors associated with interchanging these
    are detected in gfc_match_end().  */
@@ -2439,7 +2439,6 @@ verify_st_order (st_state *p, gfc_statement st, bool silent)
     case ST_PUBLIC:
     case ST_PRIVATE:
     case ST_DERIVED_DECL:
-    case ST_OACC_DECLARE:
     case_decl:
       if (p->state >= ORDER_EXEC)
 	goto order;
@@ -3351,19 +3350,6 @@ declSt:
       st = next_statement ();
       goto loop;
 
-    case ST_OACC_DECLARE:
-      if (!verify_st_order(&ss, st, false))
-	{
-	  reject_statement ();
-	  st = next_statement ();
-	  goto loop;
-	}
-      if (gfc_state_stack->ext.oacc_declare_clauses == NULL)
-	gfc_state_stack->ext.oacc_declare_clauses = new_st.ext.omp_clauses;
-      accept_statement (st);
-      st = next_statement ();
-      goto loop;
-
     default:
       break;
     }
@@ -5189,13 +5175,6 @@ contains:
 
 done:
   gfc_current_ns->code = gfc_state_stack->head;
-  if (gfc_state_stack->state == COMP_PROGRAM
-      || gfc_state_stack->state == COMP_MODULE
-      || gfc_state_stack->state == COMP_SUBROUTINE
-      || gfc_state_stack->state == COMP_FUNCTION
-      || gfc_state_stack->state == COMP_BLOCK)
-    gfc_current_ns->oacc_declare_clauses
-      = gfc_state_stack->ext.oacc_declare_clauses;
 }
 
 
diff --git a/gcc/fortran/parse.h b/gcc/fortran/parse.h
index bcd714d..83977bb 100644
--- a/gcc/fortran/parse.h
+++ b/gcc/fortran/parse.h
@@ -48,7 +48,7 @@ typedef struct gfc_state_data
   union
   {
     gfc_st_label *end_do_label;
-    gfc_omp_clauses *oacc_declare_clauses;
+    struct gfc_oacc_declare *oacc_declare_clauses;
   }
   ext;
 }
diff --git a/gcc/fortran/resolve.c b/gcc/fortran/resolve.c
index 8798d4d..51a0b04 100644
--- a/gcc/fortran/resolve.c
+++ b/gcc/fortran/resolve.c
@@ -9373,6 +9373,7 @@ gfc_resolve_blocks (gfc_code *b, gfc_namespace *ns)
 	case EXEC_OACC_CACHE:
 	case EXEC_OACC_ENTER_DATA:
 	case EXEC_OACC_EXIT_DATA:
+	case EXEC_OACC_DECLARE:
 	case EXEC_OMP_ATOMIC:
 	case EXEC_OMP_CRITICAL:
 	case EXEC_OMP_DISTRIBUTE:
@@ -10645,6 +10646,7 @@ start:
 	case EXEC_OACC_CACHE:
 	case EXEC_OACC_ENTER_DATA:
 	case EXEC_OACC_EXIT_DATA:
+	case EXEC_OACC_DECLARE:
 	  gfc_resolve_oacc_directive (code, ns);
 	  break;
 
diff --git a/gcc/fortran/st.c b/gcc/fortran/st.c
index 116af15..b77a22f 100644
--- a/gcc/fortran/st.c
+++ b/gcc/fortran/st.c
@@ -185,6 +185,11 @@ gfc_free_statement (gfc_code *p)
       gfc_free_forall_iterator (p->ext.forall_iterator);
       break;
 
+    case EXEC_OACC_DECLARE:
+      if (p->ext.oacc_declare)
+	gfc_free_oacc_declare_clauses (p->ext.oacc_declare);
+      break;
+
     case EXEC_OACC_PARALLEL_LOOP:
     case EXEC_OACC_PARALLEL:
     case EXEC_OACC_KERNELS_LOOP:
diff --git a/gcc/fortran/symbol.c b/gcc/fortran/symbol.c
index bd7758b..43fd25d 100644
--- a/gcc/fortran/symbol.c
+++ b/gcc/fortran/symbol.c
@@ -375,6 +375,11 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
     *contiguous = "CONTIGUOUS", *generic = "GENERIC";
   static const char *threadprivate = "THREADPRIVATE";
   static const char *omp_declare_target = "OMP DECLARE TARGET";
+  static const char *oacc_declare_copyin = "OACC DECLARE COPYIN";
+  static const char *oacc_declare_create = "OACC DECLARE CREATE";
+  static const char *oacc_declare_deviceptr = "OACC DECLARE DEVICEPTR";
+  static const char *oacc_declare_device_resident =
+						"OACC DECLARE DEVICE_RESIDENT";
 
   const char *a1, *a2;
   int standard;
@@ -511,6 +516,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (in_equivalence, allocatable);
   conf (in_equivalence, threadprivate);
   conf (in_equivalence, omp_declare_target);
+  conf (in_equivalence, oacc_declare_create);
+  conf (in_equivalence, oacc_declare_copyin);
+  conf (in_equivalence, oacc_declare_deviceptr);
+  conf (in_equivalence, oacc_declare_device_resident);
 
   conf (dummy, result);
   conf (entry, result);
@@ -560,6 +569,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (cray_pointee, in_equivalence);
   conf (cray_pointee, threadprivate);
   conf (cray_pointee, omp_declare_target);
+  conf (cray_pointee, oacc_declare_create);
+  conf (cray_pointee, oacc_declare_copyin);
+  conf (cray_pointee, oacc_declare_deviceptr);
+  conf (cray_pointee, oacc_declare_device_resident);
 
   conf (data, dummy);
   conf (data, function);
@@ -614,6 +627,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (proc_pointer, abstract)
 
   conf (entry, omp_declare_target)
+  conf (entry, oacc_declare_create)
+  conf (entry, oacc_declare_copyin)
+  conf (entry, oacc_declare_deviceptr)
+  conf (entry, oacc_declare_device_resident)
 
   a1 = gfc_code2string (flavors, attr->flavor);
 
@@ -651,6 +668,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
       conf2 (subroutine);
       conf2 (threadprivate);
       conf2 (omp_declare_target);
+      conf2 (oacc_declare_create);
+      conf2 (oacc_declare_copyin);
+      conf2 (oacc_declare_deviceptr);
+      conf2 (oacc_declare_device_resident);
 
       if (attr->access == ACCESS_PUBLIC || attr->access == ACCESS_PRIVATE)
 	{
@@ -733,6 +754,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
       conf2 (threadprivate);
       conf2 (result);
       conf2 (omp_declare_target);
+      conf2 (oacc_declare_create);
+      conf2 (oacc_declare_copyin);
+      conf2 (oacc_declare_deviceptr);
+      conf2 (oacc_declare_device_resident);
 
       if (attr->intent != INTENT_UNKNOWN)
 	{
@@ -1244,6 +1269,62 @@ gfc_add_omp_declare_target (symbol_attribute *attr, const char *name,
 
 
 bool
+gfc_add_oacc_declare_create (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_create)
+    return true;
+
+  attr->oacc_declare_create = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_copyin (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_copyin)
+    return true;
+
+  attr->oacc_declare_copyin = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_deviceptr (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_deviceptr)
+    return true;
+
+  attr->oacc_declare_deviceptr = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_device_resident (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_device_resident)
+    return true;
+
+  attr->oacc_declare_device_resident = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
 gfc_add_target (symbol_attribute *attr, locus *where)
 {
 
@@ -1820,6 +1901,18 @@ gfc_copy_attr (symbol_attribute *dest, symbol_attribute *src, locus *where)
   if (src->omp_declare_target
       && !gfc_add_omp_declare_target (dest, NULL, where))
     goto fail;
+  if (src->oacc_declare_create
+      && !gfc_add_oacc_declare_create (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_copyin
+      && !gfc_add_oacc_declare_copyin (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_deviceptr
+      && !gfc_add_oacc_declare_deviceptr (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_device_resident
+      && !gfc_add_oacc_declare_device_resident (dest, NULL, where))
+    goto fail;
   if (src->target && !gfc_add_target (dest, where))
     goto fail;
   if (src->dummy && !gfc_add_dummy (dest, NULL, where))
diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c
index 269c235..28b3c2c 100644
--- a/gcc/fortran/trans-decl.c
+++ b/gcc/fortran/trans-decl.c
@@ -1309,6 +1309,15 @@ add_attributes_to_decl (symbol_attribute sym_attr, tree list)
     list = tree_cons (get_identifier ("omp declare target"),
 		      NULL_TREE, list);
 
+  if (sym_attr.oacc_declare_create
+      || sym_attr.oacc_declare_copyin
+      || sym_attr.oacc_declare_deviceptr
+      || sym_attr.oacc_declare_device_resident
+      || sym_attr.oacc_declare_link)
+    {
+      list = tree_cons (get_identifier ("oacc declare"),
+			NULL_TREE, list);
+    }
   return list;
 }
 
@@ -5754,6 +5763,192 @@ is_ieee_module_used (gfc_namespace *ns)
 }
 
 
+static gfc_omp_clauses *module_oacc_clauses;
+
+
+static void
+add_clause (gfc_symbol *sym, gfc_omp_map_op map_op)
+{
+  gfc_omp_namelist *n;
+
+  n = gfc_get_omp_namelist ();
+  n->sym = sym;
+  n->u.map_op = map_op;
+
+  if (!module_oacc_clauses)
+    module_oacc_clauses = gfc_get_omp_clauses ();
+
+  if (module_oacc_clauses->lists[OMP_LIST_MAP])
+    n->next = module_oacc_clauses->lists[OMP_LIST_MAP];
+
+  module_oacc_clauses->lists[OMP_LIST_MAP] = n;
+}
+
+
+static void
+find_module_oacc_declare_clauses (gfc_symbol *sym)
+{
+  if (sym->attr.use_assoc)
+    {
+      gfc_omp_map_op map_op;
+
+      if (sym->attr.oacc_declare_create)
+	map_op = OMP_MAP_FORCE_ALLOC;
+
+      if (sym->attr.oacc_declare_copyin)
+	map_op = OMP_MAP_FORCE_TO;
+
+      if (sym->attr.oacc_declare_deviceptr)
+	map_op = OMP_MAP_FORCE_DEVICEPTR;
+
+      if (sym->attr.oacc_declare_device_resident)
+	map_op = OMP_MAP_DEVICE_RESIDENT;
+
+      if (sym->attr.oacc_declare_create
+	  || sym->attr.oacc_declare_copyin
+	  || sym->attr.oacc_declare_deviceptr
+	  || sym->attr.oacc_declare_device_resident)
+	{
+	  sym->attr.referenced = 1;
+	  add_clause (sym, map_op);
+	}
+    }
+}
+
+
+void
+finish_oacc_declare (gfc_namespace *ns, enum sym_flavor flavor)
+{
+  gfc_code *code;
+  gfc_oacc_declare *oc;
+  gfc_omp_namelist *n;
+  locus where = gfc_current_locus;
+
+  gfc_traverse_ns (ns, find_module_oacc_declare_clauses);
+
+  if (module_oacc_clauses && flavor == FL_PROGRAM)
+    {
+      gfc_oacc_declare *new_oc;
+
+      new_oc = gfc_get_oacc_declare ();
+      new_oc->next = ns->oacc_declare_clauses;
+      new_oc->clauses = module_oacc_clauses;
+
+      ns->oacc_declare_clauses = new_oc;
+      module_oacc_clauses = NULL;
+    }
+
+  if (!ns->oacc_declare_clauses)
+    return;
+
+  for (oc = ns->oacc_declare_clauses; oc; oc = oc->next)
+    {
+      gfc_omp_clauses *omp_clauses, *ret_clauses;
+
+      if (oc->module_var)
+	continue;
+
+      if (oc->clauses)
+	{
+	   code = XCNEW (gfc_code);
+	   code->op = EXEC_OACC_DECLARE;
+	   code->loc = where;
+
+	   ret_clauses = NULL;
+	   omp_clauses = oc->clauses;
+
+	   for (n = omp_clauses->lists[OMP_LIST_MAP]; n; n = n->next)
+	     {
+		bool ret = false;
+		gfc_omp_map_op new_op;
+
+		switch (n->u.map_op)
+		  {
+		    case OMP_MAP_ALLOC:
+		    case OMP_MAP_FORCE_ALLOC:
+		      new_op = OMP_MAP_FORCE_DEALLOC;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_DEVICE_RESIDENT:
+		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
+		      new_op = OMP_MAP_FORCE_DEALLOC;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_FROM:
+		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
+		      new_op = OMP_MAP_FORCE_FROM;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_TO:
+		      new_op = OMP_MAP_FORCE_DEALLOC;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_TOFROM:
+		      n->u.map_op = OMP_MAP_FORCE_TO;
+		      new_op = OMP_MAP_FORCE_FROM;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FROM:
+		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
+		      new_op = OMP_MAP_FROM;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_DEVICEPTR:
+		    case OMP_MAP_FORCE_PRESENT:
+		    case OMP_MAP_LINK:
+		    case OMP_MAP_TO:
+		      break;
+
+		    case OMP_MAP_TOFROM:
+		      n->u.map_op = OMP_MAP_TO;
+		      new_op = OMP_MAP_FROM;
+		      ret = true;
+		      break;
+
+		    default:
+		      gcc_unreachable ();
+		      break;
+		  }
+
+		if (ret)
+		  {
+		    gfc_omp_namelist *new_n;
+
+		    new_n = gfc_get_omp_namelist ();
+		    new_n->sym = n->sym;
+		    new_n->u.map_op = new_op;
+
+		    if (!ret_clauses)
+		      ret_clauses = gfc_get_omp_clauses ();
+
+		    if (ret_clauses->lists[OMP_LIST_MAP])
+		      new_n->next = ret_clauses->lists[OMP_LIST_MAP];
+
+		    ret_clauses->lists[OMP_LIST_MAP] = new_n;
+		    ret = false;
+		  }
+	     }
+
+	   code->ext.oacc_declare = gfc_get_oacc_declare ();
+	   code->ext.oacc_declare->clauses = omp_clauses;
+	   code->ext.oacc_declare->return_clauses = ret_clauses;
+
+	   if (ns->code)
+	     code->next = ns->code;
+	   ns->code = code;
+	}
+    }
+
+  return;
+}
+
+
 /* Generate code for a function.  */
 
 void
@@ -5891,11 +6086,7 @@ gfc_generate_function_code (gfc_namespace * ns)
     add_argument_checking (&body, sym);
 
   /* Generate !$ACC DECLARE directive. */
-  if (ns->oacc_declare_clauses)
-    {
-      tree tmp = gfc_trans_oacc_declare (&body, ns);
-      gfc_add_expr_to_block (&body, tmp);
-    }
+  finish_oacc_declare  (ns, sym->attr.flavor);
 
   tmp = gfc_trans_code (ns->code);
   gfc_add_expr_to_block (&body, tmp);
diff --git a/gcc/fortran/trans-openmp.c b/gcc/fortran/trans-openmp.c
index 3be9f51..327a47a 100644
--- a/gcc/fortran/trans-openmp.c
+++ b/gcc/fortran/trans-openmp.c
@@ -1925,6 +1925,9 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 	      if (!n->sym->attr.referenced)
 		continue;
 
+	      if (n->sym->attr.use_assoc && n->sym->attr.oacc_declare_link)
+		continue;
+
 	      tree node = build_omp_clause (input_location, OMP_CLAUSE_MAP);
 	      tree node2 = NULL_TREE;
 	      tree node3 = NULL_TREE;
@@ -4377,13 +4380,27 @@ gfc_trans_omp_workshare (gfc_code *code, gfc_omp_clauses *clauses)
 }
 
 tree
-gfc_trans_oacc_declare (stmtblock_t *block, gfc_namespace *ns)
+gfc_trans_oacc_declare (gfc_code *code)
 {
-  tree oacc_clauses;
-  oacc_clauses = gfc_trans_omp_clauses (block, ns->oacc_declare_clauses,
-					ns->oacc_declare_clauses->loc);
-  return build1_loc (ns->oacc_declare_clauses->loc.lb->location,
-		     OACC_DECLARE, void_type_node, oacc_clauses);
+  stmtblock_t block;
+  tree stmt, c1, c2;
+  enum tree_code construct_code;
+
+  gfc_start_block (&block);
+
+  construct_code = OACC_DECLARE;
+
+  gfc_start_block (&block);
+  c1 = gfc_trans_omp_clauses (&block, code->ext.oacc_declare->clauses,
+			      code->loc);
+
+  c2 = gfc_trans_omp_clauses (&block, code->ext.oacc_declare->return_clauses,
+			      code->loc);
+
+  stmt = build2_loc (input_location, construct_code, void_type_node, c1, c2);
+  gfc_add_expr_to_block (&block, stmt);
+
+  return gfc_finish_block (&block);
 }
 
 tree
@@ -4409,6 +4426,8 @@ gfc_trans_oacc_directive (gfc_code *code)
       return gfc_trans_oacc_executable_directive (code);
     case EXEC_OACC_WAIT:
       return gfc_trans_oacc_wait_directive (code);
+    case EXEC_OACC_DECLARE:
+      return gfc_trans_oacc_declare (code);
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/fortran/trans-stmt.c b/gcc/fortran/trans-stmt.c
index 85558f0..a993b1c 100644
--- a/gcc/fortran/trans-stmt.c
+++ b/gcc/fortran/trans-stmt.c
@@ -1579,11 +1579,7 @@ gfc_trans_block_construct (gfc_code* code)
   code->exit_label = exit_label;
 
   /* Generate !$ACC DECLARE directive. */
-  if (ns->oacc_declare_clauses)
-    {
-      tree tmp = gfc_trans_oacc_declare (&body, ns);
-      gfc_add_expr_to_block (&body, tmp);
-    }
+  finish_oacc_declare (ns, FL_UNKNOWN);
 
   gfc_add_expr_to_block (&body, gfc_trans_code (ns->code));
   gfc_add_expr_to_block (&body, build1_v (LABEL_EXPR, exit_label));
diff --git a/gcc/fortran/trans-stmt.h b/gcc/fortran/trans-stmt.h
index 2f2a0b3..0ff93c4 100644
--- a/gcc/fortran/trans-stmt.h
+++ b/gcc/fortran/trans-stmt.h
@@ -67,7 +67,7 @@ void gfc_trans_omp_declare_simd (gfc_namespace *);
 
 /* trans-openacc.c */
 tree gfc_trans_oacc_directive (gfc_code *);
-tree gfc_trans_oacc_declare (stmtblock_t *block, gfc_namespace *);
+tree gfc_trans_oacc_declare (gfc_namespace *);
 
 /* trans-io.c */
 tree gfc_trans_open (gfc_code *);
diff --git a/gcc/fortran/trans.c b/gcc/fortran/trans.c
index 4eaea53..7de5db2 100644
--- a/gcc/fortran/trans.c
+++ b/gcc/fortran/trans.c
@@ -1903,6 +1903,7 @@ trans_code (gfc_code * code, tree cond)
 	case EXEC_OACC_PARALLEL_LOOP:
 	case EXEC_OACC_ENTER_DATA:
 	case EXEC_OACC_EXIT_DATA:
+	case EXEC_OACC_DECLARE:
 	  res = gfc_trans_oacc_directive (code);
 	  break;
 
diff --git a/gcc/fortran/types.def b/gcc/fortran/types.def
index ca75654..6d993db 100644
--- a/gcc/fortran/types.def
+++ b/gcc/fortran/types.def
@@ -145,6 +145,7 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I2_INT, BT_VOID, BT_VOLATILE_PTR, BT_I2, BT
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I4_INT, BT_VOID, BT_VOLATILE_PTR, BT_I4, BT_INT)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I8_INT, BT_VOID, BT_VOLATILE_PTR, BT_I8, BT_INT)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I16_INT, BT_VOID, BT_VOLATILE_PTR, BT_I16, BT_INT)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_VOID_OMPFN_PTR_UINT_UINT,
                      BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, BT_UINT)
diff --git a/gcc/testsuite/gfortran.dg/goacc/declare-1.f95 b/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
index 5cf737f..3129f04 100644
--- a/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
+++ b/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
@@ -15,5 +15,4 @@ contains
     END BLOCK
   end function foo
 end program test
-! { dg-prune-output "unimplemented" }
-! { dg-final { scan-tree-dump-times "pragma acc declare map\\(force_tofrom:i\\)" 2 "original" } } 
+! { dg-final { scan-tree-dump-times "pragma acc declare map\\(force_to:i\\)" 2 "original" } }
diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index e374045..ff6faed 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -359,7 +359,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
 
       if (kind == GOMP_MAP_FORCE_ALLOC
 	  || kind == GOMP_MAP_FORCE_PRESENT
-	  || kind == GOMP_MAP_FORCE_TO)
+	  || kind == GOMP_MAP_FORCE_TO
+	  || kind == GOMP_MAP_TO
+	  || kind == GOMP_MAP_ALLOC)
 	{
 	  data_enter = true;
 	  break;
@@ -386,6 +388,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
 	    {
 	      switch (kind)
 		{
+		case GOMP_MAP_ALLOC:
+		  acc_present_or_create (hostaddrs[i], sizes[i]);
+		  break;
 		case GOMP_MAP_POINTER:
 		  gomp_acc_insert_pointer (1, &hostaddrs[i], &sizes[i],
 					&kinds[i]);
@@ -397,6 +402,7 @@ GOACC_enter_exit_data (int device, size_t mapnum,
 		  acc_present_or_copyin (hostaddrs[i], sizes[i]);
 		  break;
 		case GOMP_MAP_FORCE_TO:
+		case GOMP_MAP_TO:
 		  acc_present_or_copyin (hostaddrs[i], sizes[i]);
 		  break;
 		default:
diff --git a/gcc/testsuite/gfortran.dg/goacc/declare-2.f95 b/gcc/testsuite/gfortran.dg/goacc/declare-2.f95
new file mode 100644
index 0000000..f9ffe9e
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/goacc/declare-2.f95
@@ -0,0 +1,54 @@
+
+module amod
+
+contains
+
+subroutine asubr (b)
+  implicit none
+  integer :: b(8)
+
+  !$acc declare copy (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare copyout (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copy (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copyin (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copyout (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_create (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare deviceptr (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare create (b) copyin (b) ! { dg-error "present on multiple clauses" }
+
+end subroutine
+
+end module
+
+subroutine bsubr (foo)
+  implicit none
+
+  integer, dimension (:) :: foo
+
+  !$acc declare copy (foo) ! { dg-error "Assumed-size dummy array" }
+  !$acc declare copy (foo(1:2)) ! { dg-error "Assumed-size dummy array" }
+
+end subroutine bsubr
+
+subroutine multiline
+  integer :: b(8)
+
+  !$acc declare copyin (b) ! { dg-error "present on multiple clauses" }
+  !$acc declare copyin (b)
+
+end subroutine multiline
+
+subroutine subarray
+  integer :: c(8)
+
+  !$acc declare copy (c(1:2)) ! { dg-error "Subarray 'c' is not allowed" }
+
+end subroutine subarray
+
+program test
+  integer :: a(8)
+
+  !$acc declare create (a) copyin (a) ! { dg-error "present on multiple clauses" }
+
+end program
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
new file mode 100644
index 0000000..18dd1bb
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
@@ -0,0 +1,236 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  integer z
+  !$acc declare create (z)
+end module vars
+
+subroutine subr6 (a, d)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare deviceptr (a)
+  integer :: d(N)
+
+  i = 0
+
+  !$acc parallel copy (d)
+    do i = 1, N
+      d(i) = a(i) + a(i)
+    end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr5 (a, b, c, d)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present_or_copyin (a)
+  integer :: b(N)
+  !$acc declare present_or_create (b)
+  integer :: c(N)
+  !$acc declare present_or_copyout (c)
+  integer :: d(N)
+  !$acc declare present_or_copy (d)
+
+  i = 0
+
+  !$acc parallel
+    do i = 1, N
+      b(i) = a(i)
+      c(i) = b(i)
+      d(i) = d(i) + b(i)
+    end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr4 (a, b)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: b(N)
+  !$acc declare copyout (b)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    b(i) = a(i)
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr3 (a, c)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: c(N)
+  !$acc declare copyin (c)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    a(i) = c(i)
+    c(i) = 0
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr2 (a, b, c)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: b(N)
+  !$acc declare create (b)
+  integer :: c(N)
+  !$acc declare copy (c)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    b(i) = a(i)
+    c(i) = b(i) + c(i) + 1
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr1 (a)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    a(i) = a(i) + 1
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine test (a, e)
+  use openacc
+  logical :: e
+  integer, parameter :: N = 8
+  integer :: a(N)
+
+  if (acc_is_present (a) .neqv. e) call abort
+
+end subroutine
+
+subroutine subr0 (a, b, c, d)
+  integer, parameter :: N = 8
+  integer :: a(N)
+  !$acc declare copy (a)
+  integer :: b(N)
+  integer :: c(N)
+  integer :: d(N)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  call subr1 (a)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  call subr2 (a, b, c)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (c(i) .ne. 8) call abort
+  end do
+
+  call subr3 (a, c)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (a(i) .ne. 2) call abort
+    if (c(i) .ne. 8) call abort
+  end do
+
+  call subr4 (a, b)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (b(i) .ne. 8) call abort
+  end do
+
+  call subr5 (a, b, c, d)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (c(i) .ne. 8) call abort
+    if (d(i) .ne. 13) call abort
+  end do
+
+  call subr6 (a, d)
+
+  call test (a, .true.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (d(i) .ne. 16) call abort
+  end do
+
+end subroutine
+
+program main
+  use vars
+  use openacc
+  integer, parameter :: N = 8
+  integer :: a(N)
+  integer :: b(N)
+  integer :: c(N)
+  integer :: d(N)
+
+  a(:) = 2
+  b(:) = 3
+  c(:) = 4
+  d(:) = 5
+
+  if (acc_is_present (z) .neqv. .true.) call abort
+
+  call subr0 (a, b, c, d)
+
+  call test (a, .false.)
+  call test (b, .false.)
+  call test (c, .false.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (a(i) .ne. 8) call abort
+    if (b(i) .ne. 8) call abort
+    if (c(i) .ne. 8) call abort
+    if (d(i) .ne. 16) call abort
+  end do
+
+
+end program
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
new file mode 100644
index 0000000..9b75aa1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
@@ -0,0 +1,14 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module globalvars
+  integer a
+  !$acc declare create (a)
+end module globalvars
+
+program test
+  use globalvars
+  use openacc
+
+  if (acc_is_present (a) .neqv. .true.) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
new file mode 100644
index 0000000..79fc011
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
@@ -0,0 +1,65 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module globalvars
+  real b
+  !$acc declare link (b)
+end module globalvars
+
+program test
+  use openacc
+
+  real a
+  real c
+  !$acc declare link (c)
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+  if (acc_is_present (c) .neqv. .false.) call abort
+
+  a = 0.0
+  b = 1.0
+
+  !$acc parallel copy (a) copyin (b)
+    b = b + 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 5.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) create (b)
+    b = 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) copy (b)
+    b = 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+  if (b .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) copy (b) copy (c)
+    b = 4.0
+    c = b
+    a = c
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
new file mode 100644
index 0000000..997c8ac
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
@@ -0,0 +1,27 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  real b
+ !$acc declare create (b)
+end module vars
+
+program test
+  use vars
+  use openacc
+  real a
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  a = 2.0
+
+  !$acc parallel copy (a)
+    b = a
+    a = 1.0
+    a = a + b
+   !$acc end parallel
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  if (a .ne. 3.0) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
new file mode 100644
index 0000000..d7c9bac
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
@@ -0,0 +1,28 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  implicit none
+  real b
+ !$acc declare device_resident (b)
+end module vars
+
+program test
+  use vars
+  use openacc
+  real a
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  a = 2.0
+
+  !$acc parallel copy (a)
+    b = a
+    a = 1.0
+    a = a + b
+   !$acc end parallel
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  if (a .ne. 3.0) call abort
+
+end program test

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-10-27 20:20 [OpenACC] declare directive James Norris
  2015-10-28 16:33 ` Cesar Philippidis
@ 2015-11-03 16:31 ` James Norris
  2015-11-04 16:49   ` Thomas Schwinge
  2015-11-06 13:48 ` [OpenACC] declare directive James Norris
  2021-06-10 11:15 ` Thomas Schwinge
  3 siblings, 1 reply; 51+ messages in thread
From: James Norris @ 2015-11-03 16:31 UTC (permalink / raw)
  To: GCC Patches, Tobias Burnus, Jakub Jelinek, hubicka, Nathan Sidwell
  Cc: Joseph S. Myers

[-- Attachment #1: Type: text/plain, Size: 829 bytes --]

On 10/27/2015 03:18 PM, James Norris wrote:
> Hi!
>
>      This patch adds the processing of OpenACC declare directive in C
>      and C++. (Note: Support in Fortran is already in trunk.)
>      Commentary on the changes is included as an attachment (NOTES).
>
>      All of the code is in the gomp-4_0-branch.
>
>      Regtested on x86_64-linux.
>
>      Thanks!
>      Jim

     Ping!

     I've revised the patch since I originally submitted it for review
     (https://gcc.gnu.org/ml/gcc-patches/2015-10/msg02967.html). The
     revision is due to Jakub and et al OpenMP 4.5 work in the area of
     'omp declare target'. I now exploit that functionality and have
     revised the patch accordingly.

     Updated ChangeLog, patch, and commentary (NOTES) are attached.

     Regtested on x86_64-linux

     Thanks!
     Jim


[-- Attachment #2: NOTES --]
[-- Type: text/plain, Size: 4715 bytes --]

    Background
        The declare directive is used to allocate device memory for the
        entire scope of a variable / array within a program, function,
        or subroutine. Consider the following example.

            int a[10];
            #pragma acc declare create (a)

            void func (int *a)
            {
                int b[10];
                #pragma acc declare create (b)

                #pragma acc parallel present (a, b)
                {
                  int i;

                  for (i = 0; i < 10; i++)
                  {
                    b[i] = a[i];
                    a[i] = b[i] + i;
                  }

                }

                return;
            }
                
            int main (int argc, char **argv)
            {
                func (&a[0]);

                return 0;
            }

        In the example, array 'a' will be allocated on the device at the
        outset of device activity and be available for the duration.
        Whereas, array 'b', will only be available when 'func' is executing.
        In other words, array 'b' will be allocated at the outset of
        execution of 'func' and deallocated at the return from 'func'. In
        some instances, the clause may require that the host copy of a
        variable / array be updated prior to a return from a function or
        subroutine or exiting of the program.

    C and C++ front-ends

        Definitions for use in C and C++ were added to identify the
        declare directive pragma and its' valid clauses. After the
        clauses have been validated, if the declare directive is for a
	    global variable, then an attribute is created and chained.
        These attributes will be used during gimplification.

        Once the user-specified clauses have been parsed, the clauses
        have to be examined and potentially altered and/or added to.
        As mentioned in the previous section, with some clauses, e.g.,
        e.g, copy, movement of data has to occur at the entry to 
        something like a function as well as at exit. Hence the need
        to examine/modify/add to the clauses so as to effect the
        correct data movement.

        For all instances of the declare directive, there is at least
        one set of 'entry' clauses. If the clauses pertain to global
        variables, a constructor is created. This constructor will
        'register' the variable(s) / arrays so that at beginning of
        OpenACC runtime the variable / arrays will be allocated and
        be made available throughout program execution.

        If on the other hand, the 'entry' clauses are not found to be
        of a global type, then a node is created and the clauses are
        associated with it. Also note that the 'return' clauses are
        also associated with the node. Notice that there are 'return'
        clauses only for non-global variables / arrays. The clauses
        available for global variables / arrays only allow for data
        movement at the initiation of program execution.

	Middle-end

        The OACC_DECLARE node is handled much the same as other OpenACC
        nodes that represent directives. However, there is one thing
        unique to declare, and that is the handling of the 'return'
        clauses. The 'return' clauses are scanned and then a gimple
        statment is created, but is not added. However, it is saved to
        be added after the body has been gimplified.

        The intent of this last-minute addition is to allow this statement
        to be executed prior to returning from a function. JAKUB: While
        this has been working, I'm not completely sure this is the proper
        means by which to do this in order to guarantee this statement
        is the last one executed. Please advise otherwise.
	
	Callgraph

        The 'make offload" functionality has been refactored to handle 
        OpenACC variables / arrays. A variable is an OpenACC declare'd
        is not known at the time the varpool node is created. This
        requires that a check of the offloadable bit to determine
        whether make_offloadable should be called to assert the bit if
        the right conditions are met.

	libgomp

        A function has been added to handle the declare builtin which is
        emitted by the compiler.

    Testing
    
        New compile and runtime tests have been added. (NOTE: The numbering
        for the runtime tests has a gap in it. These tests use both the
        declare and routine directive. The support for the routine directive
        has yet to be added to trunk, so these tests will appear once
        the support has been committed.)

[-- Attachment #3: ChangeLog --]
[-- Type: text/plain, Size: 2779 bytes --]

2015-10-27  James Norris  <jnorris@codesourcery.com>

	gcc/
	* builtin-types.def (BT_FN_VOID_PTR_INT_UINT): New type.
	* c-family/c-common.c (c_common_attribute_table): New oacc_declare.
	* c-family/c-pragma.c (oacc_pragmas): Add entry for declare directive. 
	* c-family/c-pragma.h (enum pragma_kind): Add PRAGMA_OACC_DECLARE.
	(enum pragma_omp_clause): Add PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT.
	* c/c-parser.c (c_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	(c_parser_omp_clause_name): Handle 'device_resident' clause.
	(c_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(c_parser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OACC_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(c_parser_oacc_declare): New function.
	* cp/parser.c (cp_parser_omp_clause_name): Handle 'device_resident'
	clause.
	(cp_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(cp_parser_oacc_declare): New function.
	(cp_paser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(cp_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	* cp/pt.c (tsubst_expr): Handle OACC_DECLARE.
	* fortran/types.def (BT_FN_VOID_PTR_INT_UINT): New type.
	* gimple.h (enum gf_mask): Add GF_OMP_TARGET_KIND_OACC_DECLARE.
	(is_gomple_omp_oacc): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* gimplify.c (struct gimplify_omp_ctx): New field.
	(new_omp_context): Initialize new field.
	(omp_default_clause): Handle device resident variable.
	(gimplify_oacc_declare): New function.
	(device_resident_p): New function.
	(gimplify_expr): Handle OACC_DECLARE.
	(gimplify_body): Handle updating of declare'd variables.
	* omp-builtins.def (BUILT_IN_GOACC_STATIC, BUILT_IN_GOACC_DECLARE):
	New builtins.
	* omp-low.c (expand_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE and BUILTIN_GOACC_DECLARE.
	(lower_omp_target): Handle GF_OMP_TARGET_KIND_OACC_DECLARE,
	GOMP_MAP_DEVICE_RESIDENT and GOMP_MAP_LINK.
	(make_gimple_omp_edges): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* tree.def (OACC_DECLARE): Update operands.
	* tree.h (OACC_DECLARE_RETURN_CLAUSES): New definition.
	* varpool.c (make_offloadable): New function.
	(get_create): Refactor offload functionality.

	gcc/testsuite
	* c-c++-common/goacc/declare-1.c: New test.
	* c-c++-common/goacc/declare-2.c: Likewise.

	include/
	* gomp-constants.h (enum gomp_map_kind): Add GOMP_MAP_DEVICE_RESIDENT
	and GOMP_MAP_LINK.

	libgomp/

	* libgomp.map (GOACC_2.0.1): Export GOACC_declare.
	* oacc-parallel.c (GOACC_declare): New function.
	* testsuite/libgomp.oacc-c-c++-common/declare-1.c: New test.
	* testsuite/libgomp.oacc-c-c++-common/declare-5.c: Likewise.

[-- Attachment #4: declare.patch --]
[-- Type: text/x-patch, Size: 40862 bytes --]

diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index b561436..a109806 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -450,6 +450,7 @@ DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONG_ULONG_ULONGPTR, BT_BOOL, BT_ULONG,
 		     BT_ULONG, BT_PTR_ULONG)
 DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, BT_BOOL,
 		     BT_ULONGLONG, BT_ULONGLONG, BT_PTR_ULONGLONG)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index c87704b..53f92f7 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -830,6 +830,7 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_bnd_legacy, false },
   { "bnd_instrument",         0, 0, true, false, false,
 			      handle_bnd_instrument, false },
+  { "oacc declare",           0, -1, true,  false, false, NULL, false },
   { NULL,                     0, 0, false, false, false, NULL, false }
 };
 
diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
index d99c2af..ad8cdbf 100644
--- a/gcc/c-family/c-pragma.c
+++ b/gcc/c-family/c-pragma.c
@@ -1206,6 +1206,7 @@ struct omp_pragma_def { const char *name; unsigned int id; };
 static const struct omp_pragma_def oacc_pragmas[] = {
   { "cache", PRAGMA_OACC_CACHE },
   { "data", PRAGMA_OACC_DATA },
+  { "declare", PRAGMA_OACC_DECLARE },
   { "enter", PRAGMA_OACC_ENTER_DATA },
   { "exit", PRAGMA_OACC_EXIT_DATA },
   { "kernels", PRAGMA_OACC_KERNELS },
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index cec920f..dcba221 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -29,6 +29,7 @@ enum pragma_kind {
 
   PRAGMA_OACC_CACHE,
   PRAGMA_OACC_DATA,
+  PRAGMA_OACC_DECLARE,
   PRAGMA_OACC_ENTER_DATA,
   PRAGMA_OACC_EXIT_DATA,
   PRAGMA_OACC_KERNELS,
@@ -150,6 +151,7 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_CREATE,
   PRAGMA_OACC_CLAUSE_DELETE,
   PRAGMA_OACC_CLAUSE_DEVICEPTR,
+  PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT,
   PRAGMA_OACC_CLAUSE_GANG,
   PRAGMA_OACC_CLAUSE_HOST,
   PRAGMA_OACC_CLAUSE_NUM_GANGS,
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 90038d5..c21a274 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1229,6 +1229,7 @@ static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
 					     unsigned int * = NULL);
+static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
 static void c_parser_omp_construct (c_parser *);
@@ -9695,6 +9696,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context)
 
   switch (id)
     {
+    case PRAGMA_OACC_DECLARE:
+      c_parser_oacc_declare (parser);
+      return false;
+
     case PRAGMA_OACC_ENTER_DATA:
       c_parser_oacc_enter_exit_data (parser, true);
       return false;
@@ -9980,6 +9985,8 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -10412,10 +10419,16 @@ c_parser_oacc_data_clause (c_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OMP_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -12584,6 +12597,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = c_parser_omp_clause_firstprivate (parser, clauses);
 	  c_name = "firstprivate";
@@ -12601,6 +12618,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_omp_clause_if (parser, clauses, false);
 	  c_name = "if";
 	  break;
+	case PRAGMA_OMP_CLAUSE_LINK:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  clauses = c_parser_omp_clause_num_gangs (parser, clauses);
 	  c_name = "num_gangs";
@@ -13054,6 +13075,220 @@ c_parser_oacc_data (location_t loc, c_parser *parser)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINK)			\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE) )
+
+static void
+c_parser_oacc_declare (c_parser *parser)
+{
+  location_t pragma_loc = c_parser_peek_token (parser)->location;
+  tree c, clauses, ret_clauses, stmt, t;
+
+  bool error = false;
+
+  c_parser_consume_pragma (parser);
+
+  clauses = c_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+				       "#pragma acc declare");
+  if (!clauses)
+    {
+      error_at (pragma_loc,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return;
+    }
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      tree decl = OMP_CLAUSE_DECL (t);
+      tree devres = NULL_TREE;
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "subarray in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	  break;
+
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  devres = t;
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p () && !DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      tree id;
+      tree prev_attr = lookup_attribute ("omp declare target",
+					 DECL_ATTRIBUTES (decl));
+
+      if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
+	id = get_identifier ("omp declare target link");
+      else
+        id = get_identifier ("omp declare target");
+
+      if (prev_attr)
+	{
+	  tree p = TREE_VALUE (prev_attr);
+	  tree cl = TREE_VALUE (p);
+
+	  if (!devres && OMP_CLAUSE_MAP_KIND (cl) != GOMP_MAP_DEVICE_RESIDENT)
+	    {
+	      error_at (loc, "variable %qD used more than once with "
+			     "%<#pragma acc declare%>", decl);
+	      inform (OMP_CLAUSE_LOCATION (cl), "previous directive was here");
+	      error = true;
+	      continue;
+	    }
+	}
+
+      if (!error)
+	DECL_ATTRIBUTES (decl) =
+			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+    }
+
+  if (error || global_bindings_p ())
+    return;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_LINK:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = copy_node (c);
+
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+    stmt = make_node (OACC_DECLARE);
+    TREE_TYPE (stmt) = void_type_node;
+    OACC_DECLARE_CLAUSES (stmt) = clauses;
+    OACC_DECLARE_RETURN_CLAUSES (stmt) = ret_clauses;
+    SET_EXPR_LOCATION (stmt, pragma_loc);
+
+    add_stmt (stmt);
+
+    return;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 24cb47f..6d47352 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -29094,6 +29094,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -29503,10 +29505,16 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OMP_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -31475,6 +31483,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  clauses = cp_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_GANG:
 	  c_name = "gang";
 	  clauses = cp_parser_oacc_shape_clause (parser, OMP_CLAUSE_GANG,
@@ -31488,6 +31500,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  clauses = cp_parser_omp_clause_if (parser, clauses, here, false);
 	  c_name = "if";
 	  break;
+	case PRAGMA_OMP_CLAUSE_LINK:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  clauses = cp_parser_omp_clause_num_gangs (parser, clauses);
 	  c_name = "num_gangs";
@@ -34380,6 +34396,221 @@ cp_parser_oacc_data (cp_parser *parser, cp_token *pragma_tok)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINK)			\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE))
+
+static tree
+cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
+{
+  tree c, clauses, ret_clauses, stmt, t;
+  bool error = false;
+
+  clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+					"#pragma acc declare", pragma_tok, true);
+
+
+  if (find_omp_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
+    {
+      error_at (pragma_tok->location,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return NULL_TREE;
+    }
+
+  for (tree t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      tree decl = OMP_CLAUSE_DECL (t);
+      tree devres = NULL_TREE;
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "subarray in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+      gcc_assert (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_MAP);
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	  break;
+
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  devres = t;
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p () && !DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      tree id;
+      tree prev_attr = lookup_attribute ("oacc declare",
+					     DECL_ATTRIBUTES (decl));
+
+      if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
+	id = get_identifier ("omp declare target link");
+      else
+        id = get_identifier ("omp declare target");
+
+      if (prev_attr)
+	{
+	  tree p = TREE_VALUE (prev_attr);
+	  tree cl = TREE_VALUE (p);
+
+	  if (!devres && OMP_CLAUSE_MAP_KIND (cl) != GOMP_MAP_DEVICE_RESIDENT)
+	    {
+	      error_at (loc, "variable %qD used more than once with "
+			"%<#pragma acc declare%>", decl);
+	      inform (OMP_CLAUSE_LOCATION (TREE_VALUE (p)),
+		      "previous directive was here");
+	      error = true;
+	      continue;
+	    }
+	}
+
+      if (!error)
+	DECL_ATTRIBUTES (decl) =
+			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+    }
+
+  if (error || global_bindings_p ())
+    return NULL_TREE;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  case GOMP_MAP_LINK:
+	    continue;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = copy_node (c);
+
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+  stmt = make_node (OACC_DECLARE);
+  TREE_TYPE (stmt) = void_type_node;
+  OACC_DECLARE_CLAUSES (stmt) = clauses;
+  OACC_DECLARE_RETURN_CLAUSES (stmt) = ret_clauses;
+  SET_EXPR_LOCATION (stmt, pragma_tok->location);
+
+  add_stmt (stmt);
+
+  return NULL_TREE;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
@@ -36040,6 +36271,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context)
       cp_parser_omp_declare (parser, pragma_tok, context);
       return false;
 
+    case PRAGMA_OACC_DECLARE:
+      cp_parser_oacc_declare (parser, pragma_tok);
+      return false;
+
     case PRAGMA_OACC_CACHE:
     case PRAGMA_OACC_DATA:
     case PRAGMA_OACC_ENTER_DATA:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index e836ec7..0a6b190 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15314,6 +15314,17 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       add_stmt (t);
       break;
 
+    case OACC_DECLARE:
+      t = copy_node (t);
+      tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_CLAUSES (t) = tmp;
+      tmp = tsubst_omp_clauses (OACC_DECLARE_RETURN_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_RETURN_CLAUSES (t) = tmp;
+      add_stmt (t);
+      break;
+
     case OMP_TARGET_UPDATE:
     case OMP_TARGET_ENTER_DATA:
     case OMP_TARGET_EXIT_DATA:
diff --git a/gcc/fortran/types.def b/gcc/fortran/types.def
index ca75654..6d993db 100644
--- a/gcc/fortran/types.def
+++ b/gcc/fortran/types.def
@@ -145,6 +145,7 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I2_INT, BT_VOID, BT_VOLATILE_PTR, BT_I2, BT
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I4_INT, BT_VOID, BT_VOLATILE_PTR, BT_I4, BT_INT)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I8_INT, BT_VOID, BT_VOLATILE_PTR, BT_I8, BT_INT)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I16_INT, BT_VOID, BT_VOLATILE_PTR, BT_I16, BT_INT)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_VOID_OMPFN_PTR_UINT_UINT,
                      BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, BT_UINT)
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 781801b..e45162d 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -170,6 +170,7 @@ enum gf_mask {
     GF_OMP_TARGET_KIND_OACC_DATA = 7,
     GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
     GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
+    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,
 
     /* True on an GIMPLE_OMP_RETURN statement if the return does not require
        a thread synchronization via some sort of barrier.  The exact barrier
@@ -6004,6 +6005,7 @@ is_gimple_omp_oacc (const gimple *stmt)
 	case GF_OMP_TARGET_KIND_OACC_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  return true;
 	default:
 	  return false;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 03203c0..0b685b9 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -154,6 +154,7 @@ struct gimplify_omp_ctx
   bool target_map_scalars_firstprivate;
   bool target_map_pointers_as_0len_arrays;
   bool target_firstprivatize_array_bases;
+  gomp_target *declare_returns;
 };
 
 static struct gimplify_ctx *gimplify_ctxp;
@@ -373,6 +374,7 @@ new_omp_context (enum omp_region_type region_type)
     c->default_kind = OMP_CLAUSE_DEFAULT_SHARED;
   else
     c->default_kind = OMP_CLAUSE_DEFAULT_UNSPECIFIED;
+  c->declare_returns = NULL;
 
   return c;
 }
@@ -5789,6 +5791,26 @@ omp_notice_threadprivate_variable (struct gimplify_omp_ctx *ctx, tree decl,
   return false;
 }
 
+/* Return true if global var DECL is device resident.  */
+
+static bool
+device_resident_p (tree decl)
+{
+  tree attr = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
+
+  if (!attr)
+    return false;
+  
+  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
+    {
+      tree c = TREE_VALUE (t);
+      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
+	return true;
+    }
+
+  return false;
+}
+
 /* Determine outer default flags for DECL mentioned in an OMP region
    but not declared in an enclosing clause.
 
@@ -5838,6 +5860,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
       flags |= GOVD_FIRSTPRIVATE;
       break;
     case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
+      if (is_global_var (decl) && device_resident_p (decl))
+	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
       /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED.  */
       gcc_assert ((ctx->region_type & ORT_TASK) != 0);
       if (struct gimplify_omp_ctx *octx = ctx->outer_context)
@@ -7520,6 +7544,62 @@ gimplify_oacc_cache (tree *expr_p, gimple_seq *pre_p)
   *expr_p = NULL_TREE;
 }
 
+/* Gimplify OACC_DECLARE.  */
+
+static void
+gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
+{
+  tree expr = *expr_p;
+  gomp_target *stmt;
+  tree clauses, t;
+
+  clauses = OACC_DECLARE_CLAUSES (expr);
+
+  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      tree attrs, decl = OMP_CLAUSE_DECL (t);
+
+      if (TREE_CODE (decl) == MEM_REF)
+	continue;
+
+      omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
+
+      attrs = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
+      if (attrs)
+	DECL_ATTRIBUTES (decl) = remove_attribute ("oacc declare", attrs);
+    }
+
+  stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				  clauses);
+
+  gimplify_seq_add_stmt (pre_p, stmt);
+
+  clauses = OACC_DECLARE_RETURN_CLAUSES (expr);
+  if (clauses)
+    {
+      struct gimplify_omp_ctx *c;
+
+      /* Any clauses that affect the state of a variable prior
+         to return are saved and dealt with after the body has
+         been gimplified.  */
+
+      gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA,
+				 OACC_DECLARE);
+
+      c = gimplify_omp_ctxp;
+      gimplify_omp_ctxp = c->outer_context;
+      delete_omp_context (c);
+
+      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				      clauses);
+      gimplify_omp_ctxp->declare_returns = stmt;
+    }
+
+  *expr_p = NULL_TREE;
+}
+
 /* Gimplify the contents of an OMP_PARALLEL statement.  This involves
    gimplification of the body, as well as scanning the body for used
    variables.  We need to do this scan now, because variable-sized
@@ -9586,11 +9666,15 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  break;
 
 	case OACC_HOST_DATA:
-	case OACC_DECLARE:
 	  sorry ("directive not yet implemented");
 	  ret = GS_ALL_DONE;
 	  break;
 
+	case OACC_DECLARE:
+	  gimplify_oacc_declare (expr_p, pre_p);
+	  ret = GS_ALL_DONE;
+	  break;
+
 	case OACC_DATA:
 	case OACC_KERNELS:
 	case OACC_PARALLEL:
@@ -10251,6 +10335,28 @@ gimplify_body (tree fndecl, bool do_parms)
       gimplify_seq_add_stmt (&seq, outer_stmt);
     }
 
+  if (flag_openacc && gimplify_omp_ctxp)
+    {
+      while (gimplify_omp_ctxp)
+	{
+	  struct gimplify_omp_ctx *c;
+
+	  if (gimplify_omp_ctxp->declare_returns)
+	    {
+              /* Clauses are present that affect the state of a
+                 variable, insert the statment to handle this
+                 as the very last statement.  */
+
+	      gimplify_seq_add_stmt (&seq, gimplify_omp_ctxp->declare_returns);
+	      gimplify_omp_ctxp->declare_returns = NULL;
+	    }
+
+	  c = gimplify_omp_ctxp;
+	  gimplify_omp_ctxp = c->outer_context;
+	  delete_omp_context (c);
+	}
+    }
+
   /* The body must contain exactly one statement, a GIMPLE_BIND.  If this is
      not the case, wrap everything in a GIMPLE_BIND to make it so.  */
   if (gimple_code (outer_stmt) == GIMPLE_BIND
diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def
index ea9cf0d..4af3640 100644
--- a/gcc/omp-builtins.def
+++ b/gcc/omp-builtins.def
@@ -317,3 +317,7 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TARGET_ENTER_EXIT_DATA,
 		  BT_FN_VOID_INT_SIZE_PTR_PTR_PTR_UINT_PTR, ATTR_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS, "GOMP_teams",
 		  BT_FN_VOID_UINT_UINT, ATTR_NOTHROW_LIST)
+DEF_GOACC_BUILTIN (BUILT_IN_GOACC_STATIC, "GOACC_register_static",
+		   BT_FN_VOID_PTR_INT_UINT, ATTR_NOTHROW_LIST)
+DEF_GOACC_BUILTIN (BUILT_IN_GOACC_DECLARE, "GOACC_declare",
+		   BT_FN_VOID_INT_SIZE_PTR_PTR_PTR, ATTR_NOTHROW_LIST)
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index d0264e9..215adfa 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -12120,6 +12120,7 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -12351,6 +12352,9 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       start_ix = BUILT_IN_GOACC_ENTER_EXIT_DATA;
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      start_ix = BUILT_IN_GOACC_DECLARE;
+      break;
     default:
       gcc_unreachable ();
     }
@@ -12473,6 +12477,7 @@ expand_omp_target (struct omp_region *region)
   switch (start_ix)
     {
     case BUILT_IN_GOACC_DATA_START:
+    case BUILT_IN_GOACC_DECLARE:
     case BUILT_IN_GOMP_TARGET_DATA:
       break;
     case BUILT_IN_GOMP_TARGET:
@@ -12755,6 +12760,7 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent,
 		case GF_OMP_TARGET_KIND_EXIT_DATA:
 		case GF_OMP_TARGET_KIND_OACC_UPDATE:
 		case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+		case GF_OMP_TARGET_KIND_OACC_DECLARE:
 		  /* ..., other than for those stand-alone directives...  */
 		  region = NULL;
 		  break;
@@ -14968,6 +14974,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -15042,6 +15049,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	  case GOMP_MAP_FORCE_TOFROM:
 	  case GOMP_MAP_FORCE_PRESENT:
 	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_LINK:
 	    gcc_assert (is_gimple_omp_oacc (stmt));
 	    break;
 	  default:
@@ -16710,6 +16719,7 @@ make_gimple_omp_edges (basic_block bb, struct omp_region **region,
 	case GF_OMP_TARGET_KIND_EXIT_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  cur_region = cur_region->outer;
 	  break;
 	default:
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
new file mode 100644
index 0000000..b036c63
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -0,0 +1,83 @@
+/* Test valid uses of declare directive.  */
+/* { dg-do compile } */
+
+int v0;
+#pragma acc declare create(v0)
+
+int v1;
+#pragma acc declare copyin(v1)
+
+int *v2;
+#pragma acc declare deviceptr(v2)
+
+int v3;
+#pragma acc declare device_resident(v3)
+
+int v4;
+#pragma acc declare link(v4)
+
+int v5, v6, v7, v8;
+#pragma acc declare create(v5, v6) copyin(v7, v8)
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare create(va0)
+
+  int va1;
+#pragma acc declare copyin(va1)
+
+  int *va2;
+#pragma acc declare deviceptr(va2)
+
+  int va3;
+#pragma acc declare device_resident(va3)
+
+  extern int ve0;
+#pragma acc declare create(ve0)
+
+  extern int ve1;
+#pragma acc declare copyin(ve1)
+
+  extern int *ve2;
+#pragma acc declare deviceptr(ve2)
+
+  extern int ve3;
+#pragma acc declare device_resident(ve3)
+
+  extern int ve4;
+#pragma acc declare link(ve4)
+
+  int va5;
+#pragma acc declare copy(va5)
+
+  int va6;
+#pragma acc declare copyout(va6)
+
+  int va7;
+#pragma acc declare present(va7)
+
+  int va8;
+#pragma acc declare present_or_copy(va8)
+
+  int va9;
+#pragma acc declare present_or_copyin(va9)
+
+  int va10;
+#pragma acc declare present_or_copyout(va10)
+
+  int va11;
+#pragma acc declare present_or_create(va11)
+
+ a:
+  {
+    int va0;
+#pragma acc declare create(va0)
+    if (v1)
+      goto a;
+    else
+      goto b;
+  }
+ b:;
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
new file mode 100644
index 0000000..7979f0c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -0,0 +1,68 @@
+/* Test invalid uses of declare directive.  */
+/* { dg-do compile } */
+
+#pragma acc declare /* { dg-error "no valid clauses" } */
+
+#pragma acc declare create(undeclared) /* { dg-error "undeclared" } */
+/* { dg-error "no valid clauses" "second error" { target *-*-* } 6 } */
+
+int v0[10];
+#pragma acc declare create(v0[1:3]) /* { dg-error "subarray" } */
+
+int v1;
+#pragma acc declare create(v1, v1) /* { dg-error "more than once" } */
+
+int v2;
+#pragma acc declare create(v2) /* { dg-message "previous directive" } */
+#pragma acc declare copyin(v2) /* { dg-error "more than once" } */
+
+int v3;
+#pragma acc declare copy(v3) /* { dg-error "at file scope" } */
+
+int v4;
+#pragma acc declare copyout(v4) /* { dg-error "at file scope" } */
+
+int v5;
+#pragma acc declare present(v5) /* { dg-error "at file scope" } */
+
+int v6;
+#pragma acc declare present_or_copy(v6) /* { dg-error "at file scope" } */
+
+int v7;
+#pragma acc declare present_or_copyin(v7) /* { dg-error "at file scope" } */
+
+int v8;
+#pragma acc declare present_or_copyout(v8) /* { dg-error "at file scope" } */
+
+int v9;
+#pragma acc declare present_or_create(v9) /* { dg-error "at file scope" } */
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare link(va0) /* { dg-error "global variable" } */
+
+  extern int ve0;
+#pragma acc declare copy(ve0) /* { dg-error "invalid use of" } */
+
+  extern int ve1;
+#pragma acc declare copyout(ve1) /* { dg-error "invalid use of" } */
+
+  extern int ve2;
+#pragma acc declare present(ve2) /* { dg-error "invalid use of" } */
+
+  extern int ve3;
+#pragma acc declare present_or_copy(ve3) /* { dg-error "invalid use of" } */
+
+  extern int ve4;
+#pragma acc declare present_or_copyin(ve4) /* { dg-error "invalid use of" } */
+
+  extern int ve5;
+#pragma acc declare present_or_copyout(ve5) /* { dg-error "invalid use of" } */
+
+  extern int ve6;
+#pragma acc declare present_or_create(ve6) /* { dg-error "invalid use of" } */
+
+#pragma acc declare present (v9) /* { dg-error "invalid use of" } */
+}
diff --git a/gcc/tree.def b/gcc/tree.def
index fc7490a..d6269c0 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -1186,8 +1186,9 @@ DEFTREECODE (OMP_TASKGROUP, "omp_taskgroup", tcc_statement, 1)
 DEFTREECODE (OACC_CACHE, "oacc_cache", tcc_statement, 1)
 
 /* OpenACC - #pragma acc declare [clause1 ... clauseN]
-   Operand 0: OACC_DECLARE_CLAUSES: List of clauses.  */
-DEFTREECODE (OACC_DECLARE, "oacc_declare", tcc_statement, 1)
+   Operand 0: OACC_DECLARE_CLAUSES: List of clauses.
+   Operand 1: OACC_DECLARE_RETURN_CLAUSES: List of clauses for returns.  */
+DEFTREECODE (OACC_DECLARE, "oacc_declare", tcc_statement, 2)
 
 /* OpenACC - #pragma acc enter data [clause1 ... clauseN]
    Operand 0: OACC_ENTER_DATA_CLAUSES: List of clauses.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index 65c3117..66b97bc 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1232,6 +1232,8 @@ extern void protected_set_expr_location (tree, location_t);
 
 #define OACC_DECLARE_CLAUSES(NODE) \
   TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 0)
+#define OACC_DECLARE_RETURN_CLAUSES(NODE) \
+  TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 1)
 
 #define OACC_ENTER_DATA_CLAUSES(NODE) \
   TREE_OPERAND (OACC_ENTER_DATA_CHECK (NODE), 0)
diff --git a/gcc/varpool.c b/gcc/varpool.c
index 478f365..a8cdb1c 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -137,18 +137,9 @@ varpool_node::create_empty (void)
   return node;
 }   
 
-/* Return varpool node assigned to DECL.  Create new one when needed.  */
-varpool_node *
-varpool_node::get_create (tree decl)
+static void
+make_offloadable (varpool_node *node, tree decl)
 {
-  varpool_node *node = varpool_node::get (decl);
-  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
-  if (node)
-    return node;
-
-  node = varpool_node::create_empty ();
-  node->decl = decl;
-
   if ((flag_openacc || flag_openmp) && !DECL_EXTERNAL (decl)
       && lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
     {
@@ -160,6 +151,25 @@ varpool_node::get_create (tree decl)
       node->force_output = 1;
 #endif
     }
+}
+
+/* Return varpool node assigned to DECL.  Create new one when needed.  */
+varpool_node *
+varpool_node::get_create (tree decl)
+{
+  varpool_node *node = varpool_node::get (decl);
+  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
+  if (node)
+    {
+      if (!node->offloadable)
+	make_offloadable (node, decl);
+      return node;
+    }
+
+  node = varpool_node::create_empty ();
+  node->decl = decl;
+
+  make_offloadable (node, decl);
 
   node->register_symbol ();
   return node;
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index f834dec..4128912 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -73,6 +73,11 @@ enum gomp_map_kind
        POINTER_SIZE_UNITS.  */
     GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
     /* Do not map, copy bits for firstprivate instead.  */
+    /* OpenACC device_resident.  */
+    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
+    /* OpenACC link.  */
+    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
+    /* Allocate.  */
     GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
     /* Similarly, but store the value in the pointer rather than
        pointed by the pointer.  */
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 2153661..e96f929 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -378,10 +378,12 @@ GOACC_2.0 {
 	GOACC_wait;
 	GOACC_get_thread_num;
 	GOACC_get_num_threads;
+	GOACC_register_static;
 };
 
 GOACC_2.0.1 {
   global:
+	GOACC_declare;
 	GOACC_parallel_keyed;
 } GOACC_2.0;
 
diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index b150106..fd9348c 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -297,7 +297,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
 
       if (kind == GOMP_MAP_FORCE_ALLOC
 	  || kind == GOMP_MAP_FORCE_PRESENT
-	  || kind == GOMP_MAP_FORCE_TO)
+	  || kind == GOMP_MAP_FORCE_TO
+	  || kind == GOMP_MAP_TO
+	  || kind == GOMP_MAP_ALLOC)
 	{
 	  data_enter = true;
 	  break;
@@ -324,6 +326,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
 	    {
 	      switch (kind)
 		{
+		case GOMP_MAP_ALLOC:
+		  acc_present_or_create (hostaddrs[i], sizes[i]);
+		  break;
 		case GOMP_MAP_POINTER:
 		  gomp_acc_insert_pointer (1, &hostaddrs[i], &sizes[i],
 					&kinds[i]);
@@ -332,6 +337,7 @@ GOACC_enter_exit_data (int device, size_t mapnum,
 		  acc_create (hostaddrs[i], sizes[i]);
 		  break;
 		case GOMP_MAP_FORCE_PRESENT:
+		case GOMP_MAP_TO:
 		  acc_present_or_copyin (hostaddrs[i], sizes[i]);
 		  break;
 		case GOMP_MAP_FORCE_TO:
@@ -501,3 +507,62 @@ GOACC_get_thread_num (void)
 {
   return 0;
 }
+
+void
+GOACC_declare (int device, size_t mapnum,
+	       void **hostaddrs, size_t *sizes, unsigned short *kinds)
+{
+  int i;
+
+  for (i = 0; i < mapnum; i++)
+    {
+      unsigned char kind = kinds[i] & 0xff;
+
+      if (kind == GOMP_MAP_POINTER || kind == GOMP_MAP_TO_PSET)
+	continue;
+
+      switch (kind)
+	{
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_DEALLOC:
+	  case GOMP_MAP_FORCE_FROM:
+	  case GOMP_MAP_FORCE_TO:
+	  case GOMP_MAP_POINTER:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	    break;
+
+	  case GOMP_MAP_ALLOC:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      {
+		GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	      }
+	    break;
+
+	  case GOMP_MAP_TO:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    kinds[i] = GOMP_MAP_FORCE_FROM;
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_PRESENT:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      gomp_fatal ("[%p,%zd] is not mapped", hostaddrs[i], sizes[i]);
+	    break;
+
+	  default:
+	    assert (0);
+	    break;
+	}
+    }
+}
diff --git a/libgomp/testsuite/declare-1.c b/libgomp/testsuite/declare-1.c
new file mode 100644
index 0000000..8fbec4d
--- /dev/null
+++ b/libgomp/testsuite/declare-1.c
@@ -0,0 +1,122 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <openacc.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define N 8
+
+void
+subr2 (int *a)
+{
+  int i;
+  int f[N];
+#pragma acc declare copyout (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i] + f[i];
+      }
+  }
+}
+
+void
+subr1 (int *a)
+{
+  int f[N];
+#pragma acc declare copy (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    int i;
+
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i];
+      }
+  }
+}
+
+int b[8];
+#pragma acc declare create (b)
+
+int d[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+#pragma acc declare copyin (d)
+
+int
+main (int argc, char **argv)
+{
+  int a[N];
+  int e[N];
+#pragma acc declare create (e)
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = i + 1;
+
+  if (!acc_is_present (&b, sizeof (b)))
+    abort ();
+
+  if (!acc_is_present (&d, sizeof (d)))
+    abort ();
+
+  if (!acc_is_present (&e, sizeof (e)))
+    abort ();
+
+#pragma acc parallel copyin (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        b[i] = a[i];
+        a[i] = b[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != i + 1)
+	abort ();
+    }
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        e[i] = a[i] + d[i];
+	a[i] = e[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != (i + 1) * 2)
+	abort ();
+    }
+
+  for (i = 0; i < N; i++)
+    {
+      a[i] = 1234;
+    }
+
+  subr1 (&a[0]);
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != 1234 * 2)
+	abort ();
+    }
+
+  subr2 (&a[0]);
+
+  for (i = 0; i < 1; i++)
+    {
+      if (a[i] != 1234 * 6)
+	abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/declare-5.c b/libgomp/testsuite/declare-5.c
new file mode 100644
index 0000000..1e2f6ce
--- /dev/null
+++ b/libgomp/testsuite/declare-5.c
@@ -0,0 +1,13 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+int
+main (int argc, char **argv)
+{
+  int a[8] __attribute__((unused));
+
+  __builtin_printf ("CheCKpOInT\n");
+#pragma acc declare present (a)
+}
+
+/* { dg-output "CheCKpOInT" } */
+/* { dg-shouldfail "" } */

^ permalink raw reply	[flat|nested] 51+ messages in thread

* OpenACC declare directive updates
  2015-11-02 13:46 OpenACC declare directive updates James Norris
@ 2015-11-04 12:32 ` James Norris
  2015-11-06 13:46   ` James Norris
  2015-11-06 19:31   ` Jakub Jelinek
  0 siblings, 2 replies; 51+ messages in thread
From: James Norris @ 2015-11-04 12:32 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 419 bytes --]


     This patch updates the processing of OpenACC declare directive for
     Fortran in the following areas:

         1) module support
         2) device_resident and link clauses
         3) clause checking
         4) directive generation

     Commentary on the changes is included as an attachment (NOTES).

     All of the code is in the gomp-4_0-branch.

     Regtested on x86_64-linux.

     Thanks!
     Jim

[-- Attachment #2: NOTES --]
[-- Type: text/plain, Size: 2670 bytes --]

    Background

        The declare directive is used to allocate device memory for the
        entire scope of a variable / array within a program, function,
        or subroutine. Consider the following example.

            module vars
              integer b
              !$acc declare device_resident (b)
              integer c
              !$acc declare link (c)
            end module vars
            
            program main
              use vars
              integer, parameter :: N = 8
              integer :: a(N)
            
              a(:) = 2
              c = 12
            
              !$acc parallel copy (a) present (b) copyin(c)
              do i = 1, N
                b = a(i)
                c = b
                a(i) = c + i
              end do
              !$acc end parallel
            
            end program

        In the example, 'b' will be allocated on the device at the outset
        of device activity and be available for the duration. Whereas the
        allocation of 'c' will be delayed until the parallel region is
        entered. The device memory for 'c' will be deallocated upon exit
        of the parallel region.

    Fortran front-end

        The changes are concentrated into four (4) areas.

        1) module support
            The neccesary functionality has been added to handle the
            reading in and writing out of the appropriate attributes
            for the declare directive. Additional functionality has
            been added at read in time to setup the required declare
            handling.

        2) device_resident and link clauses
            Add the functionality necessary to process the link and
            device_resident clauses.

        3) clause checking
            The clause checking has been cleaned up to better check
            for duplicates and correctness.

        4) directive generation

            Prior to handling the fortran execution body a code
            body is created to handle the clause(s) that accompany
            the declare directive(s). Each clause is examined and
            determined whether the clause need to be modified to 
            perform an action at the beginning of the module, function,
            subroutine, or program. Furthermore, an additional
            clause may be added to the list to perform an action
            at the time the function or subroutine returns.

            Once all the clauses have been handled, the code body
            is added to the chain.

    libgomp

        TODO

    Testing

        New compile and runtime tests have been added. Also some have
        been modified.

[-- Attachment #3: ChangeLog --]
[-- Type: text/plain, Size: 2765 bytes --]

2015-10-29  James Norris  <jnorris@codesourcery.com>
	    Cesar Philippidis  <cesar@codesourcery.com>

	gcc/fortran/
	* dump-parse-tree.c (show_namespace): Reimplement.
	* f95-lang.c (gfc_attribute_table): New entry.
	* gfortran.h (struct symbol_attribute): New fields.
	(enum gfc_omp_map_map): Add OMP_MAP_DEVICE_RESIDENT and OMP_MAP_LINK.
	(OMP_LIST_LINK): New enum.
	(struct gfc_oacc_declare): New structure.
	(gfc_get_oacc_declare): New definition.
	(struct gfc_namespace): Change type.
	(enum gfc_exec_op): Add EXEC_OACC_DECLARE.
	(struct gfc_code): New field.
	* module.c (enum ab_attribute): Add AB_OACC_DECLARE_CREATE,
	AB_OACC_DECLARE_COPYIN, AB_OACC_DECLARE_DEVICEPTR,
	AB_OACC_DECLARE_DEVICE_RESIDENT, AB_OACC_DECLARE_LINK
	(attr_bits): Add new initializers.
	(mio_symbol_attribute): Handle new atributes.
	* openmp.c (gfc_free_oacc_declare_clauses): New.
	(OMP_CLAUSE_LINK): New definition.
	(gfc_match_omp_clauses): Handle OMP_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSES): Add OMP_CLAUSE_LINK
	(gfc_match_oacc_declare): Add checking and module handling.
	(resolve_omp_duplicate_list, resolve_oacc_declare_map): New.
	(gfc_resolve_oacc_declare): Use duplicate detection.
	* parse.c (case_decl): Add ST_OACC_DECLARE.
	(parse_spec): Remove handling.
	(parse_progunit): Remove handling.
	* parse.h (struct gfc_state_data): Change type.
	* resolve.c (gfc_resolve_blocks): Handle EXEC_OACC_DECLARE.
	* st.c (gfc_free_statement): Handle EXEC_OACC_DECLARE.
	* symbol.c (check_conflict): Add conflict checks.
	(gfc_add_oacc_declare_create, gfc_add_oacc_declare_copyin, 
	gfc_add_oacc_declare_deviceptr, gfc_add_oacc_declare_device_resident):
	New functions.
	(gfc_copy_attr): Handle new symbols.
	* trans-decl.c (add_attributes_to_decl): Create identifier.
	(add_clause, find_module_oacc_declare_clauses, finish_oacc_declare):
	New functions.
	(gfc_generate_function_code): Replace with call.
	* trans-openmp.c (gfc_trans_omp_clauses): Add conditional.
	(gfc_trans_oacc_declare): Reimplement.
	(gfc_trans_oacc_directive): Handle EXEC_OACC_DECLARE.
	* trans-stmt.c (gfc_trans_block_construct): Replace with call.
	* trans-stmt.h (gfc_trans_oacc_declare): Remove argument.
	* trans.c (trans_code): Handle EXEC_OACC_DECLARE.
	* types.def (BT_FN_VOID_PTR_INT_UINT): New type.
	gcc/testsuite
	* gfortran.dg/goacc/declare-1.f95: Update test.
	* gfortran.dg/goacc/declare-2.f95: New test.
	libgomp/
	* oacc-parallel (GOACC_enter_exit_data): Handle GOMP_MAP_TO and
	GOMP_MAP_ALLOC.
	* testsuite/libgomp.oacc-fortran/declare-1.f90: New test.
	* testsuite/libgomp.oacc-fortran/declare-2.f90: Likewise.
	* testsuite/libgomp.oacc-fortran/declare-3.f90: Likewise.
	* testsuite/libgomp.oacc-fortran/declare-4.f90: Likewise.
	* testsuite/libgomp.oacc-fortran/declare-5.f90: Likewise.

[-- Attachment #4: declare2.patch --]
[-- Type: text/x-patch, Size: 43734 bytes --]

diff --git a/gcc/fortran/dump-parse-tree.c b/gcc/fortran/dump-parse-tree.c
index 83ecbaa..e953160 100644
--- a/gcc/fortran/dump-parse-tree.c
+++ b/gcc/fortran/dump-parse-tree.c
@@ -2572,10 +2572,14 @@ show_namespace (gfc_namespace *ns)
 
   if (ns->oacc_declare_clauses)
     {
+      struct gfc_oacc_declare *decl;
       /* Dump !$ACC DECLARE clauses.  */
-      show_indent ();
-      fprintf (dumpfile, "!$ACC DECLARE");
-      show_omp_clauses (ns->oacc_declare_clauses);
+      for (decl = ns->oacc_declare_clauses; decl; decl = decl->next)
+	{
+	  show_indent ();
+	  fprintf (dumpfile, "!$ACC DECLARE");
+	  show_omp_clauses (decl->clauses);
+	}
     }
 
   fputc ('\n', dumpfile);
diff --git a/gcc/fortran/f95-lang.c b/gcc/fortran/f95-lang.c
index 67b0bac..2758a28 100644
--- a/gcc/fortran/f95-lang.c
+++ b/gcc/fortran/f95-lang.c
@@ -103,6 +103,8 @@ static const struct attribute_spec gfc_attribute_table[] =
        affects_type_identity } */
   { "omp declare target", 0, 0, true,  false, false,
     gfc_handle_omp_declare_target_attribute, false },
+  { "oacc declare", 0, 0, true,  false, false,
+    gfc_handle_omp_declare_target_attribute, false },
   { NULL,		  0, 0, false, false, false, NULL, false }
 };
 
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 90f63cf..17c2357 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -840,6 +840,13 @@ typedef struct
   /* Mentioned in OMP DECLARE TARGET.  */
   unsigned omp_declare_target:1;
 
+  /* Mentioned in OACC DECLARE.  */
+  unsigned oacc_declare_create:1;
+  unsigned oacc_declare_copyin:1;
+  unsigned oacc_declare_deviceptr:1;
+  unsigned oacc_declare_device_resident:1;
+  unsigned oacc_declare_link:1;
+
   /* Attributes set by compiler extensions (!GCC$ ATTRIBUTES).  */
   unsigned ext_attr:EXT_ATTR_NUM;
 
@@ -1105,7 +1112,9 @@ enum gfc_omp_map_op
   OMP_MAP_FORCE_FROM,
   OMP_MAP_FORCE_TOFROM,
   OMP_MAP_FORCE_PRESENT,
-  OMP_MAP_FORCE_DEVICEPTR
+  OMP_MAP_FORCE_DEVICEPTR,
+  OMP_MAP_DEVICE_RESIDENT,
+  OMP_MAP_LINK
 };
 
 /* For use in OpenMP clauses in case we need extra information
@@ -1146,6 +1155,7 @@ enum
   OMP_LIST_FROM,
   OMP_LIST_REDUCTION,
   OMP_LIST_DEVICE_RESIDENT,
+  OMP_LIST_LINK,
   OMP_LIST_USE_DEVICE,
   OMP_LIST_CACHE,
   OMP_LIST_NUM
@@ -1232,6 +1242,20 @@ gfc_omp_clauses;
 #define gfc_get_omp_clauses() XCNEW (gfc_omp_clauses)
 
 
+/* Node in the linked list used for storing !$oacc declare constructs.  */
+
+typedef struct gfc_oacc_declare
+{
+  struct gfc_oacc_declare *next;
+  bool module_var;
+  gfc_omp_clauses *clauses;
+  gfc_omp_clauses *return_clauses;
+}
+gfc_oacc_declare;
+
+#define gfc_get_oacc_declare() XCNEW (gfc_oacc_declare)
+
+
 /* Node in the linked list used for storing !$omp declare simd constructs.  */
 
 typedef struct gfc_omp_declare_simd
@@ -1644,7 +1668,7 @@ typedef struct gfc_namespace
   struct gfc_data *data, *old_data;
 
   /* !$ACC DECLARE clauses.  */
-  gfc_omp_clauses *oacc_declare_clauses;
+  struct gfc_oacc_declare *oacc_declare_clauses;
 
   gfc_charlen *cl_list, *old_cl_list;
 
@@ -2321,7 +2345,7 @@ enum gfc_exec_op
   EXEC_OACC_KERNELS_LOOP, EXEC_OACC_PARALLEL_LOOP,
   EXEC_OACC_PARALLEL, EXEC_OACC_KERNELS, EXEC_OACC_DATA, EXEC_OACC_HOST_DATA,
   EXEC_OACC_LOOP, EXEC_OACC_UPDATE, EXEC_OACC_WAIT, EXEC_OACC_CACHE,
-  EXEC_OACC_ENTER_DATA, EXEC_OACC_EXIT_DATA,
+  EXEC_OACC_ENTER_DATA, EXEC_OACC_EXIT_DATA, EXEC_OACC_DECLARE,
   EXEC_OMP_CRITICAL, EXEC_OMP_DO, EXEC_OMP_FLUSH, EXEC_OMP_MASTER,
   EXEC_OMP_ORDERED, EXEC_OMP_PARALLEL, EXEC_OMP_PARALLEL_DO,
   EXEC_OMP_PARALLEL_SECTIONS, EXEC_OMP_PARALLEL_WORKSHARE,
@@ -2403,6 +2427,7 @@ typedef struct gfc_code
     struct gfc_code *which_construct;
     int stop_code;
     gfc_entry_list *entry;
+    gfc_oacc_declare *oacc_declare;
     gfc_omp_clauses *omp_clauses;
     const char *omp_name;
     gfc_omp_namelist *omp_namelist;
@@ -2905,6 +2930,7 @@ gfc_expr *gfc_get_parentheses (gfc_expr *);
 /* openmp.c */
 struct gfc_omp_saved_state { void *ptrs[2]; int ints[1]; };
 void gfc_free_omp_clauses (gfc_omp_clauses *);
+void gfc_free_oacc_declare_clauses (struct gfc_oacc_declare *);
 void gfc_free_omp_declare_simd (gfc_omp_declare_simd *);
 void gfc_free_omp_declare_simd_list (gfc_omp_declare_simd *);
 void gfc_free_omp_udr (gfc_omp_udr *);
@@ -3222,4 +3248,8 @@ gfc_expr *gfc_simplify_ieee_functions (gfc_expr *);
 
 bool gfc_is_reallocatable_lhs (gfc_expr *);
 
+/* trans-decl.c */
+
+void finish_oacc_declare (gfc_namespace *, enum sym_flavor);
+
 #endif /* GCC_GFORTRAN_H  */
diff --git a/gcc/fortran/module.c b/gcc/fortran/module.c
index f0d84a4..63d8444 100644
--- a/gcc/fortran/module.c
+++ b/gcc/fortran/module.c
@@ -1987,7 +1987,9 @@ enum ab_attribute
   AB_IS_CLASS, AB_PROCEDURE, AB_PROC_POINTER, AB_ASYNCHRONOUS, AB_CODIMENSION,
   AB_COARRAY_COMP, AB_VTYPE, AB_VTAB, AB_CONTIGUOUS, AB_CLASS_POINTER,
   AB_IMPLICIT_PURE, AB_ARTIFICIAL, AB_UNLIMITED_POLY, AB_OMP_DECLARE_TARGET,
-  AB_ARRAY_OUTER_DEPENDENCY, AB_MODULE_PROCEDURE
+  AB_ARRAY_OUTER_DEPENDENCY, AB_MODULE_PROCEDURE, AB_OACC_DECLARE_CREATE,
+  AB_OACC_DECLARE_COPYIN, AB_OACC_DECLARE_DEVICEPTR,
+  AB_OACC_DECLARE_DEVICE_RESIDENT, AB_OACC_DECLARE_LINK
 };
 
 static const mstring attr_bits[] =
@@ -2044,6 +2046,11 @@ static const mstring attr_bits[] =
     minit ("OMP_DECLARE_TARGET", AB_OMP_DECLARE_TARGET),
     minit ("ARRAY_OUTER_DEPENDENCY", AB_ARRAY_OUTER_DEPENDENCY),
     minit ("MODULE_PROCEDURE", AB_MODULE_PROCEDURE),
+    minit ("OACC_DECLARE_CREATE", AB_OACC_DECLARE_CREATE),
+    minit ("OACC_DECLARE_COPYIN", AB_OACC_DECLARE_COPYIN),
+    minit ("OACC_DECLARE_DEVICEPTR", AB_OACC_DECLARE_DEVICEPTR),
+    minit ("OACC_DECLARE_DEVICE_RESIDENT", AB_OACC_DECLARE_DEVICE_RESIDENT),
+    minit ("OACC_DECLARE_LINK", AB_OACC_DECLARE_LINK),
     minit (NULL, -1)
 };
 
@@ -2231,6 +2238,16 @@ mio_symbol_attribute (symbol_attribute *attr)
 	MIO_NAME (ab_attribute) (AB_MODULE_PROCEDURE, attr_bits);
 	  no_module_procedures = false;
 	}
+      if (attr->oacc_declare_create)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_CREATE, attr_bits);
+      if (attr->oacc_declare_copyin)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_COPYIN, attr_bits);
+      if (attr->oacc_declare_deviceptr)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_DEVICEPTR, attr_bits);
+      if (attr->oacc_declare_device_resident)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_DEVICE_RESIDENT, attr_bits);
+      if (attr->oacc_declare_link)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_LINK, attr_bits);
 
       mio_rparen ();
 
@@ -2403,6 +2420,21 @@ mio_symbol_attribute (symbol_attribute *attr)
 	    case AB_MODULE_PROCEDURE:
 	      attr->module_procedure =1;
 	      break;
+	    case AB_OACC_DECLARE_CREATE:
+	      attr->oacc_declare_create = 1;
+	      break;
+	    case AB_OACC_DECLARE_COPYIN:
+	      attr->oacc_declare_copyin = 1;
+	      break;
+	    case AB_OACC_DECLARE_DEVICEPTR:
+	      attr->oacc_declare_deviceptr = 1;
+	      break;
+	    case AB_OACC_DECLARE_DEVICE_RESIDENT:
+	      attr->oacc_declare_device_resident = 1;
+	      break;
+	    case AB_OACC_DECLARE_LINK:
+	      attr->oacc_declare_link = 1;
+	      break;
 	    }
 	}
     }
diff --git a/gcc/fortran/openmp.c b/gcc/fortran/openmp.c
index 6c78c97..485fac3 100644
--- a/gcc/fortran/openmp.c
+++ b/gcc/fortran/openmp.c
@@ -90,6 +90,25 @@ gfc_free_omp_clauses (gfc_omp_clauses *c)
   free (c);
 }
 
+/* Free oacc_declare structures.  */
+
+void
+gfc_free_oacc_declare_clauses (struct gfc_oacc_declare *oc)
+{
+  struct gfc_oacc_declare *decl = oc;
+
+  do
+    {
+      struct gfc_oacc_declare *next;
+
+      next = decl->next;
+      gfc_free_omp_clauses (decl->clauses);
+      free (decl);
+      decl = next;
+    }
+  while (decl);
+}
+
 /* Free expression list. */
 void
 gfc_free_expr_list (gfc_expr_list *list)
@@ -451,6 +470,7 @@ match_oacc_clause_gang (gfc_omp_clauses *cp)
 #define OMP_CLAUSE_DELETE		((uint64_t) 1 << 55)
 #define OMP_CLAUSE_AUTO			((uint64_t) 1 << 56)
 #define OMP_CLAUSE_TILE			((uint64_t) 1 << 57)
+#define OMP_CLAUSE_LINK			((uint64_t) 1 << 58)
 
 /* Helper function for OpenACC and OpenMP clauses involving memory
    mapping.  */
@@ -689,6 +709,12 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, uint64_t mask,
 					  true)
 	     == MATCH_YES)
 	continue;
+      if ((mask & OMP_CLAUSE_LINK)
+	  && gfc_match_omp_variable_list ("link (",
+					  &c->lists[OMP_LIST_LINK],
+					  true)
+	     == MATCH_YES)
+	continue;
       if ((mask & OMP_CLAUSE_OACC_DEVICE)
 	  && gfc_match ("device ( ") == MATCH_YES
 	  && gfc_match_omp_map_clause (&c->lists[OMP_LIST_MAP],
@@ -1171,7 +1197,7 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, uint64_t mask,
    | OMP_CLAUSE_CREATE | OMP_CLAUSE_DEVICEPTR | OMP_CLAUSE_DEVICE_RESIDENT    \
    | OMP_CLAUSE_PRESENT | OMP_CLAUSE_PRESENT_OR_COPY                          \
    | OMP_CLAUSE_PRESENT_OR_COPYIN | OMP_CLAUSE_PRESENT_OR_COPYOUT             \
-   | OMP_CLAUSE_PRESENT_OR_CREATE)
+   | OMP_CLAUSE_PRESENT_OR_CREATE | OMP_CLAUSE_LINK)
 #define OACC_UPDATE_CLAUSES \
   (OMP_CLAUSE_IF | OMP_CLAUSE_ASYNC | OMP_CLAUSE_HOST_SELF \
    | OMP_CLAUSE_OACC_DEVICE | OMP_CLAUSE_WAIT)
@@ -1288,12 +1314,86 @@ match
 gfc_match_oacc_declare (void)
 {
   gfc_omp_clauses *c;
+  gfc_omp_namelist *n;
+  gfc_namespace *ns = gfc_current_ns;
+  gfc_oacc_declare *new_oc;
+  bool module_var = false;
+
   if (gfc_match_omp_clauses (&c, OACC_DECLARE_CLAUSES, false, false, true)
       != MATCH_YES)
     return MATCH_ERROR;
 
-  new_st.ext.omp_clauses = c;
-  new_st.ext.omp_clauses->loc = gfc_current_locus;
+  for (n = c->lists[OMP_LIST_DEVICE_RESIDENT]; n != NULL; n = n->next)
+    n->sym->attr.oacc_declare_device_resident = 1;
+
+  for (n = c->lists[OMP_LIST_LINK]; n != NULL; n = n->next)
+    n->sym->attr.oacc_declare_link = 1;
+
+  for (n = c->lists[OMP_LIST_MAP]; n != NULL; n = n->next)
+    {
+      gfc_symbol *s = n->sym;
+      locus where = gfc_current_locus;
+
+      if (s->ns->proc_name && s->ns->proc_name->attr.proc == PROC_MODULE)
+	{
+	  if (n->u.map_op != OMP_MAP_FORCE_ALLOC
+	      && n->u.map_op != OMP_MAP_FORCE_TO)
+	    {
+	      gfc_error ("Invalid clause in module with $!ACC DECLARE at %L",
+			 &where);
+	      return MATCH_ERROR;
+	    }
+
+	  module_var = true;
+	}
+
+      if (s->attr.in_common)
+	{
+	  gfc_error ("Variable in a common block with $!ACC DECLARE at %L",
+		     &where);
+	  return MATCH_ERROR;
+	}
+
+      if (s->attr.use_assoc)
+	{
+	  gfc_error ("Variable is USE-associated with $!ACC DECLARE at %L",
+		     &where);
+	  return MATCH_ERROR;
+	}
+
+      if ((s->attr.dimension || s->attr.codimension)
+	  && s->attr.dummy && s->as->type != AS_EXPLICIT)
+	{
+	  gfc_error ("Assumed-size dummy array with $!ACC DECLARE at %L",
+		     &where);
+	  return MATCH_ERROR;
+	}
+
+      switch (n->u.map_op)
+	{
+	  case OMP_MAP_FORCE_ALLOC:
+	    s->attr.oacc_declare_create = 1;
+	    break;
+
+	  case OMP_MAP_FORCE_TO:
+	    s->attr.oacc_declare_copyin = 1;
+	    break;
+
+	  case OMP_MAP_FORCE_DEVICEPTR:
+	    s->attr.oacc_declare_deviceptr = 1;
+	    break;
+
+	  default:
+	    break;
+	}
+    }
+
+  new_oc = gfc_get_oacc_declare ();
+  new_oc->next = ns->oacc_declare_clauses;
+  new_oc->module_var = module_var;
+  new_oc->clauses = c;
+  ns->oacc_declare_clauses = new_oc;
+
   return MATCH_YES;
 }
 
@@ -2857,6 +2957,42 @@ oacc_compatible_clauses (gfc_omp_clauses *clauses, int list,
   return false;
 }
 
+/* Check if a variable appears in multiple clauses.  */
+
+static void
+resolve_omp_duplicate_list (gfc_omp_namelist *clause_list, bool openacc,
+			    int list)
+{
+  gfc_omp_namelist *n;
+  const char *error_msg = "Symbol %qs present on multiple clauses at %L";
+
+  /* OpenACC reduction clauses are compatible with everything.  We only
+     need to check if a reduction variable is used more than once.  */
+  if (openacc && list == OMP_LIST_REDUCTION)
+    {
+      hash_set<gfc_symbol *> reductions;
+
+      for (n = clause_list; n; n = n->next)
+	{
+	  if (reductions.contains (n->sym))
+	    gfc_error (error_msg, n->sym->name, &n->expr->where);
+	  else
+	    reductions.add (n->sym);
+	}
+
+      return;
+    }
+
+  /* Ensure that variables are only used in one clause.  */
+  for (n = clause_list; n; n = n->next)
+    {
+      if (n->sym->mark)
+	gfc_error (error_msg, n->sym->name, &n->expr->where);
+      else
+	n->sym->mark = 1;
+    }
+}
+
 /* OpenMP directive resolving routines.  */
 
 static void
@@ -4598,41 +4734,59 @@ resolve_oacc_loop (gfc_code *code)
 }
 
 
+/* Helper function for gfc_resolve_oacc_declare.  Scan omp_map_list LIST
+   in DECLARE at location LOC.  */
+
+static void
+resolve_oacc_declare_map (gfc_oacc_declare *declare, int list)
+{
+  gfc_oacc_declare *oc;
+  gfc_omp_namelist *n;
+
+  for (oc = declare; oc; oc = oc->next)
+    for (n = oc->clauses->lists[list]; n; n = n->next)
+      n->sym->mark = 0;
+
+  for (oc = declare; oc; oc = oc->next)
+    resolve_omp_duplicate_list (oc->clauses->lists[list], false, list);
+
+  for (oc = declare; oc; oc = oc->next)
+    for (n = oc->clauses->lists[list]; n; n = n->next)
+      n->sym->mark = 0;
+}
+
 void
 gfc_resolve_oacc_declare (gfc_namespace *ns)
 {
-  int list;
   gfc_omp_namelist *n;
-  locus loc;
+  gfc_oacc_declare *oc;
 
   if (ns->oacc_declare_clauses == NULL)
     return;
 
-  loc = ns->oacc_declare_clauses->loc;
+  for (oc = ns->oacc_declare_clauses; oc; oc = oc->next)
+    {
+      for (n = oc->clauses->lists[OMP_LIST_DEVICE_RESIDENT]; n; n = n->next)
+	{
+	  n->sym->mark = 0;
+	  if (n->sym->attr.flavor == FL_PARAMETER)
+	    gfc_error ("PARAMETER object %qs is not allowed at %L",
+		       n->sym->name, &gfc_current_locus);
 
-  for (list = OMP_LIST_DEVICE_RESIDENT;
-       list <= OMP_LIST_DEVICE_RESIDENT; list++)
-    for (n = ns->oacc_declare_clauses->lists[list]; n; n = n->next)
-      {
-	n->sym->mark = 0;
-	if (n->sym->attr.flavor == FL_PARAMETER)
-	  gfc_error ("PARAMETER object %qs is not allowed at %L", n->sym->name, &loc);
-      }
+	  check_array_not_assumed (n->sym, gfc_current_locus,
+				   "DEVICE_RESIDENT");	  
+	}
 
-  for (list = OMP_LIST_DEVICE_RESIDENT;
-       list <= OMP_LIST_DEVICE_RESIDENT; list++)
-    for (n = ns->oacc_declare_clauses->lists[list]; n; n = n->next)
-      {
-	if (n->sym->mark)
-	  gfc_error ("Symbol %qs present on multiple clauses at %L",
-		     n->sym->name, &loc);
-	else
-	  n->sym->mark = 1;
-      }
+      for (n = oc->clauses->lists[OMP_LIST_MAP]; n; n = n->next)
+	if (n->expr && n->expr->ref->type == REF_ARRAY)
+	  gfc_error ("Subarray %qs is not allowed in $!ACC DECLARE at %L",
+		     n->sym->name, &n->expr->where);
+    }
 
-  for (n = ns->oacc_declare_clauses->lists[OMP_LIST_DEVICE_RESIDENT]; n;
-       n = n->next)
-    check_array_not_assumed (n->sym, loc, "DEVICE_RESIDENT");
+  /* Check for duplicate link, device_resident and data clauses.  */
+  resolve_oacc_declare_map (ns->oacc_declare_clauses, OMP_LIST_LINK);
+  resolve_oacc_declare_map (ns->oacc_declare_clauses, OMP_LIST_DEVICE_RESIDENT);
+  resolve_oacc_declare_map (ns->oacc_declare_clauses, OMP_LIST_MAP);
 }
 
 
diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
index 4925c7e..be9d0c7 100644
--- a/gcc/fortran/parse.c
+++ b/gcc/fortran/parse.c
@@ -1381,7 +1381,7 @@ next_statement (void)
   case ST_EQUIVALENCE: case ST_NAMELIST: case ST_STATEMENT_FUNCTION: \
   case ST_TYPE: case ST_INTERFACE: case ST_OMP_THREADPRIVATE: \
   case ST_PROCEDURE: case ST_OMP_DECLARE_SIMD: case ST_OMP_DECLARE_REDUCTION: \
-  case ST_OMP_DECLARE_TARGET: case ST_OACC_ROUTINE
+  case ST_OMP_DECLARE_TARGET: case ST_OACC_ROUTINE: case ST_OACC_DECLARE
 
 /* Block end statements.  Errors associated with interchanging these
    are detected in gfc_match_end().  */
@@ -2439,7 +2439,6 @@ verify_st_order (st_state *p, gfc_statement st, bool silent)
     case ST_PUBLIC:
     case ST_PRIVATE:
     case ST_DERIVED_DECL:
-    case ST_OACC_DECLARE:
     case_decl:
       if (p->state >= ORDER_EXEC)
 	goto order;
@@ -3351,19 +3350,6 @@ declSt:
       st = next_statement ();
       goto loop;
 
-    case ST_OACC_DECLARE:
-      if (!verify_st_order(&ss, st, false))
-	{
-	  reject_statement ();
-	  st = next_statement ();
-	  goto loop;
-	}
-      if (gfc_state_stack->ext.oacc_declare_clauses == NULL)
-	gfc_state_stack->ext.oacc_declare_clauses = new_st.ext.omp_clauses;
-      accept_statement (st);
-      st = next_statement ();
-      goto loop;
-
     default:
       break;
     }
@@ -5189,13 +5175,6 @@ contains:
 
 done:
   gfc_current_ns->code = gfc_state_stack->head;
-  if (gfc_state_stack->state == COMP_PROGRAM
-      || gfc_state_stack->state == COMP_MODULE
-      || gfc_state_stack->state == COMP_SUBROUTINE
-      || gfc_state_stack->state == COMP_FUNCTION
-      || gfc_state_stack->state == COMP_BLOCK)
-    gfc_current_ns->oacc_declare_clauses
-      = gfc_state_stack->ext.oacc_declare_clauses;
 }
 
 
diff --git a/gcc/fortran/parse.h b/gcc/fortran/parse.h
index bcd714d..83977bb 100644
--- a/gcc/fortran/parse.h
+++ b/gcc/fortran/parse.h
@@ -48,7 +48,7 @@ typedef struct gfc_state_data
   union
   {
     gfc_st_label *end_do_label;
-    gfc_omp_clauses *oacc_declare_clauses;
+    struct gfc_oacc_declare *oacc_declare_clauses;
   }
   ext;
 }
diff --git a/gcc/fortran/resolve.c b/gcc/fortran/resolve.c
index 8798d4d..51a0b04 100644
--- a/gcc/fortran/resolve.c
+++ b/gcc/fortran/resolve.c
@@ -9373,6 +9373,7 @@ gfc_resolve_blocks (gfc_code *b, gfc_namespace *ns)
 	case EXEC_OACC_CACHE:
 	case EXEC_OACC_ENTER_DATA:
 	case EXEC_OACC_EXIT_DATA:
+	case EXEC_OACC_DECLARE:
 	case EXEC_OMP_ATOMIC:
 	case EXEC_OMP_CRITICAL:
 	case EXEC_OMP_DISTRIBUTE:
@@ -10645,6 +10646,7 @@ start:
 	case EXEC_OACC_CACHE:
 	case EXEC_OACC_ENTER_DATA:
 	case EXEC_OACC_EXIT_DATA:
+	case EXEC_OACC_DECLARE:
 	  gfc_resolve_oacc_directive (code, ns);
 	  break;
 
diff --git a/gcc/fortran/st.c b/gcc/fortran/st.c
index 116af15..b77a22f 100644
--- a/gcc/fortran/st.c
+++ b/gcc/fortran/st.c
@@ -185,6 +185,11 @@ gfc_free_statement (gfc_code *p)
       gfc_free_forall_iterator (p->ext.forall_iterator);
       break;
 
+    case EXEC_OACC_DECLARE:
+      if (p->ext.oacc_declare)
+	gfc_free_oacc_declare_clauses (p->ext.oacc_declare);
+      break;
+
     case EXEC_OACC_PARALLEL_LOOP:
     case EXEC_OACC_PARALLEL:
     case EXEC_OACC_KERNELS_LOOP:
diff --git a/gcc/fortran/symbol.c b/gcc/fortran/symbol.c
index bd7758b..43fd25d 100644
--- a/gcc/fortran/symbol.c
+++ b/gcc/fortran/symbol.c
@@ -375,6 +375,11 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
     *contiguous = "CONTIGUOUS", *generic = "GENERIC";
   static const char *threadprivate = "THREADPRIVATE";
   static const char *omp_declare_target = "OMP DECLARE TARGET";
+  static const char *oacc_declare_copyin = "OACC DECLARE COPYIN";
+  static const char *oacc_declare_create = "OACC DECLARE CREATE";
+  static const char *oacc_declare_deviceptr = "OACC DECLARE DEVICEPTR";
+  static const char *oacc_declare_device_resident =
+						"OACC DECLARE DEVICE_RESIDENT";
 
   const char *a1, *a2;
   int standard;
@@ -511,6 +516,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (in_equivalence, allocatable);
   conf (in_equivalence, threadprivate);
   conf (in_equivalence, omp_declare_target);
+  conf (in_equivalence, oacc_declare_create);
+  conf (in_equivalence, oacc_declare_copyin);
+  conf (in_equivalence, oacc_declare_deviceptr);
+  conf (in_equivalence, oacc_declare_device_resident);
 
   conf (dummy, result);
   conf (entry, result);
@@ -560,6 +569,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (cray_pointee, in_equivalence);
   conf (cray_pointee, threadprivate);
   conf (cray_pointee, omp_declare_target);
+  conf (cray_pointee, oacc_declare_create);
+  conf (cray_pointee, oacc_declare_copyin);
+  conf (cray_pointee, oacc_declare_deviceptr);
+  conf (cray_pointee, oacc_declare_device_resident);
 
   conf (data, dummy);
   conf (data, function);
@@ -614,6 +627,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (proc_pointer, abstract)
 
   conf (entry, omp_declare_target)
+  conf (entry, oacc_declare_create)
+  conf (entry, oacc_declare_copyin)
+  conf (entry, oacc_declare_deviceptr)
+  conf (entry, oacc_declare_device_resident)
 
   a1 = gfc_code2string (flavors, attr->flavor);
 
@@ -651,6 +668,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
       conf2 (subroutine);
       conf2 (threadprivate);
       conf2 (omp_declare_target);
+      conf2 (oacc_declare_create);
+      conf2 (oacc_declare_copyin);
+      conf2 (oacc_declare_deviceptr);
+      conf2 (oacc_declare_device_resident);
 
       if (attr->access == ACCESS_PUBLIC || attr->access == ACCESS_PRIVATE)
 	{
@@ -733,6 +754,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
       conf2 (threadprivate);
       conf2 (result);
       conf2 (omp_declare_target);
+      conf2 (oacc_declare_create);
+      conf2 (oacc_declare_copyin);
+      conf2 (oacc_declare_deviceptr);
+      conf2 (oacc_declare_device_resident);
 
       if (attr->intent != INTENT_UNKNOWN)
 	{
@@ -1244,6 +1269,62 @@ gfc_add_omp_declare_target (symbol_attribute *attr, const char *name,
 
 
 bool
+gfc_add_oacc_declare_create (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_create)
+    return true;
+
+  attr->oacc_declare_create = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_copyin (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_copyin)
+    return true;
+
+  attr->oacc_declare_copyin = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_deviceptr (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_deviceptr)
+    return true;
+
+  attr->oacc_declare_deviceptr = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_device_resident (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_device_resident)
+    return true;
+
+  attr->oacc_declare_device_resident = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
 gfc_add_target (symbol_attribute *attr, locus *where)
 {
 
@@ -1820,6 +1901,18 @@ gfc_copy_attr (symbol_attribute *dest, symbol_attribute *src, locus *where)
   if (src->omp_declare_target
       && !gfc_add_omp_declare_target (dest, NULL, where))
     goto fail;
+  if (src->oacc_declare_create
+      && !gfc_add_oacc_declare_create (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_copyin
+      && !gfc_add_oacc_declare_copyin (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_deviceptr
+      && !gfc_add_oacc_declare_deviceptr (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_device_resident
+      && !gfc_add_oacc_declare_device_resident (dest, NULL, where))
+    goto fail;
   if (src->target && !gfc_add_target (dest, where))
     goto fail;
   if (src->dummy && !gfc_add_dummy (dest, NULL, where))
diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c
index 269c235..28b3c2c 100644
--- a/gcc/fortran/trans-decl.c
+++ b/gcc/fortran/trans-decl.c
@@ -1309,6 +1309,15 @@ add_attributes_to_decl (symbol_attribute sym_attr, tree list)
     list = tree_cons (get_identifier ("omp declare target"),
 		      NULL_TREE, list);
 
+  if (sym_attr.oacc_declare_create
+      || sym_attr.oacc_declare_copyin
+      || sym_attr.oacc_declare_deviceptr
+      || sym_attr.oacc_declare_device_resident
+      || sym_attr.oacc_declare_link)
+    {
+      list = tree_cons (get_identifier ("oacc declare"),
+			NULL_TREE, list);
+    }
   return list;
 }
 
@@ -5754,6 +5763,192 @@ is_ieee_module_used (gfc_namespace *ns)
 }
 
 
+static gfc_omp_clauses *module_oacc_clauses;
+
+
+static void
+add_clause (gfc_symbol *sym, gfc_omp_map_op map_op)
+{
+  gfc_omp_namelist *n;
+
+  n = gfc_get_omp_namelist ();
+  n->sym = sym;
+  n->u.map_op = map_op;
+
+  if (!module_oacc_clauses)
+    module_oacc_clauses = gfc_get_omp_clauses ();
+
+  if (module_oacc_clauses->lists[OMP_LIST_MAP])
+    n->next = module_oacc_clauses->lists[OMP_LIST_MAP];
+
+  module_oacc_clauses->lists[OMP_LIST_MAP] = n;
+}
+
+
+static void
+find_module_oacc_declare_clauses (gfc_symbol *sym)
+{
+  if (sym->attr.use_assoc)
+    {
+      gfc_omp_map_op map_op;
+
+      if (sym->attr.oacc_declare_create)
+	map_op = OMP_MAP_FORCE_ALLOC;
+
+      if (sym->attr.oacc_declare_copyin)
+	map_op = OMP_MAP_FORCE_TO;
+
+      if (sym->attr.oacc_declare_deviceptr)
+	map_op = OMP_MAP_FORCE_DEVICEPTR;
+
+      if (sym->attr.oacc_declare_device_resident)
+	map_op = OMP_MAP_DEVICE_RESIDENT;
+
+      if (sym->attr.oacc_declare_create
+	  || sym->attr.oacc_declare_copyin
+	  || sym->attr.oacc_declare_deviceptr
+	  || sym->attr.oacc_declare_device_resident)
+	{
+	  sym->attr.referenced = 1;
+	  add_clause (sym, map_op);
+	}
+    }
+}
+
+
+void
+finish_oacc_declare (gfc_namespace *ns, enum sym_flavor flavor)
+{
+  gfc_code *code;
+  gfc_oacc_declare *oc;
+  gfc_omp_namelist *n;
+  locus where = gfc_current_locus;
+
+  gfc_traverse_ns (ns, find_module_oacc_declare_clauses);
+
+  if (module_oacc_clauses && flavor == FL_PROGRAM)
+    {
+      gfc_oacc_declare *new_oc;
+
+      new_oc = gfc_get_oacc_declare ();
+      new_oc->next = ns->oacc_declare_clauses;
+      new_oc->clauses = module_oacc_clauses;
+
+      ns->oacc_declare_clauses = new_oc;
+      module_oacc_clauses = NULL;
+    }
+
+  if (!ns->oacc_declare_clauses)
+    return;
+
+  for (oc = ns->oacc_declare_clauses; oc; oc = oc->next)
+    {
+      gfc_omp_clauses *omp_clauses, *ret_clauses;
+
+      if (oc->module_var)
+	continue;
+
+      if (oc->clauses)
+	{
+	   code = XCNEW (gfc_code);
+	   code->op = EXEC_OACC_DECLARE;
+	   code->loc = where;
+
+	   ret_clauses = NULL;
+	   omp_clauses = oc->clauses;
+
+	   for (n = omp_clauses->lists[OMP_LIST_MAP]; n; n = n->next)
+	     {
+		bool ret = false;
+		gfc_omp_map_op new_op;
+
+		switch (n->u.map_op)
+		  {
+		    case OMP_MAP_ALLOC:
+		    case OMP_MAP_FORCE_ALLOC:
+		      new_op = OMP_MAP_FORCE_DEALLOC;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_DEVICE_RESIDENT:
+		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
+		      new_op = OMP_MAP_FORCE_DEALLOC;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_FROM:
+		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
+		      new_op = OMP_MAP_FORCE_FROM;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_TO:
+		      new_op = OMP_MAP_FORCE_DEALLOC;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_TOFROM:
+		      n->u.map_op = OMP_MAP_FORCE_TO;
+		      new_op = OMP_MAP_FORCE_FROM;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FROM:
+		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
+		      new_op = OMP_MAP_FROM;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_DEVICEPTR:
+		    case OMP_MAP_FORCE_PRESENT:
+		    case OMP_MAP_LINK:
+		    case OMP_MAP_TO:
+		      break;
+
+		    case OMP_MAP_TOFROM:
+		      n->u.map_op = OMP_MAP_TO;
+		      new_op = OMP_MAP_FROM;
+		      ret = true;
+		      break;
+
+		    default:
+		      gcc_unreachable ();
+		      break;
+		  }
+
+		if (ret)
+		  {
+		    gfc_omp_namelist *new_n;
+
+		    new_n = gfc_get_omp_namelist ();
+		    new_n->sym = n->sym;
+		    new_n->u.map_op = new_op;
+
+		    if (!ret_clauses)
+		      ret_clauses = gfc_get_omp_clauses ();
+
+		    if (ret_clauses->lists[OMP_LIST_MAP])
+		      new_n->next = ret_clauses->lists[OMP_LIST_MAP];
+
+		    ret_clauses->lists[OMP_LIST_MAP] = new_n;
+		    ret = false;
+		  }
+	     }
+
+	   code->ext.oacc_declare = gfc_get_oacc_declare ();
+	   code->ext.oacc_declare->clauses = omp_clauses;
+	   code->ext.oacc_declare->return_clauses = ret_clauses;
+
+	   if (ns->code)
+	     code->next = ns->code;
+	   ns->code = code;
+	}
+    }
+
+  return;
+}
+
+
 /* Generate code for a function.  */
 
 void
@@ -5891,11 +6086,7 @@ gfc_generate_function_code (gfc_namespace * ns)
     add_argument_checking (&body, sym);
 
   /* Generate !$ACC DECLARE directive. */
-  if (ns->oacc_declare_clauses)
-    {
-      tree tmp = gfc_trans_oacc_declare (&body, ns);
-      gfc_add_expr_to_block (&body, tmp);
-    }
+  finish_oacc_declare  (ns, sym->attr.flavor);
 
   tmp = gfc_trans_code (ns->code);
   gfc_add_expr_to_block (&body, tmp);
diff --git a/gcc/fortran/trans-openmp.c b/gcc/fortran/trans-openmp.c
index 3be9f51..327a47a 100644
--- a/gcc/fortran/trans-openmp.c
+++ b/gcc/fortran/trans-openmp.c
@@ -1925,6 +1925,9 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 	      if (!n->sym->attr.referenced)
 		continue;
 
+	      if (n->sym->attr.use_assoc && n->sym->attr.oacc_declare_link)
+		continue;
+
 	      tree node = build_omp_clause (input_location, OMP_CLAUSE_MAP);
 	      tree node2 = NULL_TREE;
 	      tree node3 = NULL_TREE;
@@ -4377,13 +4380,27 @@ gfc_trans_omp_workshare (gfc_code *code, gfc_omp_clauses *clauses)
 }
 
 tree
-gfc_trans_oacc_declare (stmtblock_t *block, gfc_namespace *ns)
+gfc_trans_oacc_declare (gfc_code *code)
 {
-  tree oacc_clauses;
-  oacc_clauses = gfc_trans_omp_clauses (block, ns->oacc_declare_clauses,
-					ns->oacc_declare_clauses->loc);
-  return build1_loc (ns->oacc_declare_clauses->loc.lb->location,
-		     OACC_DECLARE, void_type_node, oacc_clauses);
+  stmtblock_t block;
+  tree stmt, c1, c2;
+  enum tree_code construct_code;
+
+  gfc_start_block (&block);
+
+  construct_code = OACC_DECLARE;
+
+  gfc_start_block (&block);
+  c1 = gfc_trans_omp_clauses (&block, code->ext.oacc_declare->clauses,
+			      code->loc);
+
+  c2 = gfc_trans_omp_clauses (&block, code->ext.oacc_declare->return_clauses,
+			      code->loc);
+
+  stmt = build2_loc (input_location, construct_code, void_type_node, c1, c2);
+  gfc_add_expr_to_block (&block, stmt);
+
+  return gfc_finish_block (&block);
 }
 
 tree
@@ -4409,6 +4426,8 @@ gfc_trans_oacc_directive (gfc_code *code)
       return gfc_trans_oacc_executable_directive (code);
     case EXEC_OACC_WAIT:
       return gfc_trans_oacc_wait_directive (code);
+    case EXEC_OACC_DECLARE:
+      return gfc_trans_oacc_declare (code);
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/fortran/trans-stmt.c b/gcc/fortran/trans-stmt.c
index 85558f0..a993b1c 100644
--- a/gcc/fortran/trans-stmt.c
+++ b/gcc/fortran/trans-stmt.c
@@ -1579,11 +1579,7 @@ gfc_trans_block_construct (gfc_code* code)
   code->exit_label = exit_label;
 
   /* Generate !$ACC DECLARE directive. */
-  if (ns->oacc_declare_clauses)
-    {
-      tree tmp = gfc_trans_oacc_declare (&body, ns);
-      gfc_add_expr_to_block (&body, tmp);
-    }
+  finish_oacc_declare (ns, FL_UNKNOWN);
 
   gfc_add_expr_to_block (&body, gfc_trans_code (ns->code));
   gfc_add_expr_to_block (&body, build1_v (LABEL_EXPR, exit_label));
diff --git a/gcc/fortran/trans-stmt.h b/gcc/fortran/trans-stmt.h
index 2f2a0b3..0ff93c4 100644
--- a/gcc/fortran/trans-stmt.h
+++ b/gcc/fortran/trans-stmt.h
@@ -67,7 +67,7 @@ void gfc_trans_omp_declare_simd (gfc_namespace *);
 
 /* trans-openacc.c */
 tree gfc_trans_oacc_directive (gfc_code *);
-tree gfc_trans_oacc_declare (stmtblock_t *block, gfc_namespace *);
+tree gfc_trans_oacc_declare (gfc_namespace *);
 
 /* trans-io.c */
 tree gfc_trans_open (gfc_code *);
diff --git a/gcc/fortran/trans.c b/gcc/fortran/trans.c
index 4eaea53..7de5db2 100644
--- a/gcc/fortran/trans.c
+++ b/gcc/fortran/trans.c
@@ -1903,6 +1903,7 @@ trans_code (gfc_code * code, tree cond)
 	case EXEC_OACC_PARALLEL_LOOP:
 	case EXEC_OACC_ENTER_DATA:
 	case EXEC_OACC_EXIT_DATA:
+	case EXEC_OACC_DECLARE:
 	  res = gfc_trans_oacc_directive (code);
 	  break;
 
diff --git a/gcc/fortran/types.def b/gcc/fortran/types.def
index ca75654..6d993db 100644
--- a/gcc/fortran/types.def
+++ b/gcc/fortran/types.def
@@ -145,6 +145,7 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I2_INT, BT_VOID, BT_VOLATILE_PTR, BT_I2, BT
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I4_INT, BT_VOID, BT_VOLATILE_PTR, BT_I4, BT_INT)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I8_INT, BT_VOID, BT_VOLATILE_PTR, BT_I8, BT_INT)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I16_INT, BT_VOID, BT_VOLATILE_PTR, BT_I16, BT_INT)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_VOID_OMPFN_PTR_UINT_UINT,
                      BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, BT_UINT)
diff --git a/gcc/testsuite/gfortran.dg/goacc/declare-1.f95 b/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
index 5cf737f..3129f04 100644
--- a/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
+++ b/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
@@ -15,5 +15,4 @@ contains
     END BLOCK
   end function foo
 end program test
-! { dg-prune-output "unimplemented" }
-! { dg-final { scan-tree-dump-times "pragma acc declare map\\(force_tofrom:i\\)" 2 "original" } } 
+! { dg-final { scan-tree-dump-times "pragma acc declare map\\(force_to:i\\)" 2 "original" } }
diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index e374045..ff6faed 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -359,7 +359,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
 
       if (kind == GOMP_MAP_FORCE_ALLOC
 	  || kind == GOMP_MAP_FORCE_PRESENT
-	  || kind == GOMP_MAP_FORCE_TO)
+	  || kind == GOMP_MAP_FORCE_TO
+	  || kind == GOMP_MAP_TO
+	  || kind == GOMP_MAP_ALLOC)
 	{
 	  data_enter = true;
 	  break;
@@ -386,6 +388,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
 	    {
 	      switch (kind)
 		{
+		case GOMP_MAP_ALLOC:
+		  acc_present_or_create (hostaddrs[i], sizes[i]);
+		  break;
 		case GOMP_MAP_POINTER:
 		  gomp_acc_insert_pointer (1, &hostaddrs[i], &sizes[i],
 					&kinds[i]);
@@ -397,6 +402,7 @@ GOACC_enter_exit_data (int device, size_t mapnum,
 		  acc_present_or_copyin (hostaddrs[i], sizes[i]);
 		  break;
 		case GOMP_MAP_FORCE_TO:
+		case GOMP_MAP_TO:
 		  acc_present_or_copyin (hostaddrs[i], sizes[i]);
 		  break;
 		default:
diff --git a/gcc/testsuite/gfortran.dg/goacc/declare-2.f95 b/gcc/testsuite/gfortran.dg/goacc/declare-2.f95
new file mode 100644
index 0000000..f9ffe9e
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/goacc/declare-2.f95
@@ -0,0 +1,54 @@
+
+module amod
+
+contains
+
+subroutine asubr (b)
+  implicit none
+  integer :: b(8)
+
+  !$acc declare copy (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare copyout (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copy (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copyin (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copyout (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_create (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare deviceptr (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare create (b) copyin (b) ! { dg-error "present on multiple clauses" }
+
+end subroutine
+
+end module
+
+subroutine bsubr (foo)
+  implicit none
+
+  integer, dimension (:) :: foo
+
+  !$acc declare copy (foo) ! { dg-error "Assumed-size dummy array" }
+  !$acc declare copy (foo(1:2)) ! { dg-error "Assumed-size dummy array" }
+
+end subroutine bsubr
+
+subroutine multiline
+  integer :: b(8)
+
+  !$acc declare copyin (b) ! { dg-error "present on multiple clauses" }
+  !$acc declare copyin (b)
+
+end subroutine multiline
+
+subroutine subarray
+  integer :: c(8)
+
+  !$acc declare copy (c(1:2)) ! { dg-error "Subarray 'c' is not allowed" }
+
+end subroutine subarray
+
+program test
+  integer :: a(8)
+
+  !$acc declare create (a) copyin (a) ! { dg-error "present on multiple clauses" }
+
+end program
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
new file mode 100644
index 0000000..18dd1bb
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
@@ -0,0 +1,236 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  integer z
+  !$acc declare create (z)
+end module vars
+
+subroutine subr6 (a, d)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare deviceptr (a)
+  integer :: d(N)
+
+  i = 0
+
+  !$acc parallel copy (d)
+    do i = 1, N
+      d(i) = a(i) + a(i)
+    end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr5 (a, b, c, d)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present_or_copyin (a)
+  integer :: b(N)
+  !$acc declare present_or_create (b)
+  integer :: c(N)
+  !$acc declare present_or_copyout (c)
+  integer :: d(N)
+  !$acc declare present_or_copy (d)
+
+  i = 0
+
+  !$acc parallel
+    do i = 1, N
+      b(i) = a(i)
+      c(i) = b(i)
+      d(i) = d(i) + b(i)
+    end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr4 (a, b)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: b(N)
+  !$acc declare copyout (b)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    b(i) = a(i)
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr3 (a, c)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: c(N)
+  !$acc declare copyin (c)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    a(i) = c(i)
+    c(i) = 0
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr2 (a, b, c)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: b(N)
+  !$acc declare create (b)
+  integer :: c(N)
+  !$acc declare copy (c)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    b(i) = a(i)
+    c(i) = b(i) + c(i) + 1
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr1 (a)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    a(i) = a(i) + 1
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine test (a, e)
+  use openacc
+  logical :: e
+  integer, parameter :: N = 8
+  integer :: a(N)
+
+  if (acc_is_present (a) .neqv. e) call abort
+
+end subroutine
+
+subroutine subr0 (a, b, c, d)
+  integer, parameter :: N = 8
+  integer :: a(N)
+  !$acc declare copy (a)
+  integer :: b(N)
+  integer :: c(N)
+  integer :: d(N)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  call subr1 (a)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  call subr2 (a, b, c)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (c(i) .ne. 8) call abort
+  end do
+
+  call subr3 (a, c)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (a(i) .ne. 2) call abort
+    if (c(i) .ne. 8) call abort
+  end do
+
+  call subr4 (a, b)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (b(i) .ne. 8) call abort
+  end do
+
+  call subr5 (a, b, c, d)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (c(i) .ne. 8) call abort
+    if (d(i) .ne. 13) call abort
+  end do
+
+  call subr6 (a, d)
+
+  call test (a, .true.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (d(i) .ne. 16) call abort
+  end do
+
+end subroutine
+
+program main
+  use vars
+  use openacc
+  integer, parameter :: N = 8
+  integer :: a(N)
+  integer :: b(N)
+  integer :: c(N)
+  integer :: d(N)
+
+  a(:) = 2
+  b(:) = 3
+  c(:) = 4
+  d(:) = 5
+
+  if (acc_is_present (z) .neqv. .true.) call abort
+
+  call subr0 (a, b, c, d)
+
+  call test (a, .false.)
+  call test (b, .false.)
+  call test (c, .false.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (a(i) .ne. 8) call abort
+    if (b(i) .ne. 8) call abort
+    if (c(i) .ne. 8) call abort
+    if (d(i) .ne. 16) call abort
+  end do
+
+
+end program
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
new file mode 100644
index 0000000..9b75aa1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
@@ -0,0 +1,14 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module globalvars
+  integer a
+  !$acc declare create (a)
+end module globalvars
+
+program test
+  use globalvars
+  use openacc
+
+  if (acc_is_present (a) .neqv. .true.) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
new file mode 100644
index 0000000..79fc011
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
@@ -0,0 +1,65 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module globalvars
+  real b
+  !$acc declare link (b)
+end module globalvars
+
+program test
+  use openacc
+
+  real a
+  real c
+  !$acc declare link (c)
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+  if (acc_is_present (c) .neqv. .false.) call abort
+
+  a = 0.0
+  b = 1.0
+
+  !$acc parallel copy (a) copyin (b)
+    b = b + 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 5.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) create (b)
+    b = 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) copy (b)
+    b = 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+  if (b .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) copy (b) copy (c)
+    b = 4.0
+    c = b
+    a = c
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
new file mode 100644
index 0000000..997c8ac
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
@@ -0,0 +1,27 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  real b
+ !$acc declare create (b)
+end module vars
+
+program test
+  use vars
+  use openacc
+  real a
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  a = 2.0
+
+  !$acc parallel copy (a)
+    b = a
+    a = 1.0
+    a = a + b
+   !$acc end parallel
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  if (a .ne. 3.0) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
new file mode 100644
index 0000000..d7c9bac
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
@@ -0,0 +1,28 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  implicit none
+  real b
+ !$acc declare device_resident (b)
+end module vars
+
+program test
+  use vars
+  use openacc
+  real a
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  a = 2.0
+
+  !$acc parallel copy (a)
+    b = a
+    a = 1.0
+    a = a + b
+   !$acc end parallel
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  if (a .ne. 3.0) call abort
+
+end program test

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-03 16:31 ` James Norris
@ 2015-11-04 16:49   ` Thomas Schwinge
  2015-11-04 17:12     ` James Norris
  2015-11-06 16:08     ` James Norris
  0 siblings, 2 replies; 51+ messages in thread
From: Thomas Schwinge @ 2015-11-04 16:49 UTC (permalink / raw)
  To: James Norris, Jakub Jelinek
  Cc: Joseph S. Myers, GCC Patches, Tobias Burnus, hubicka, Nathan Sidwell

[-- Attachment #1: Type: text/plain, Size: 9390 bytes --]

Hi Jim!

On Tue, 3 Nov 2015 10:31:32 -0600, James Norris <jnorris@codesourcery.com> wrote:
> On 10/27/2015 03:18 PM, James Norris wrote:
> >      This patch adds the processing of OpenACC declare directive in C
> >      and C++. (Note: Support in Fortran is already in trunk.)

..., and a patch adjusting some Fortran front end things is awaiting
review,
<http://news.gmane.org/find-root.php?message_id=%3C5637692F.7050306%40codesourcery.com%3E>.

>      I've revised the patch since I originally submitted it for review
>      (https://gcc.gnu.org/ml/gcc-patches/2015-10/msg02967.html). The
>      revision is due to Jakub and et al OpenMP 4.5 work in the area of
>      'omp declare target'. I now exploit that functionality and have
>      revised the patch accordingly.

Oh, wow, you could remove a lot of code!

Just a superficial review on your patch; patch re-ordered a bit for
review.

> --- a/gcc/builtin-types.def
> +++ b/gcc/builtin-types.def

> +DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)

> --- a/gcc/fortran/types.def
> +++ b/gcc/fortran/types.def

> +DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)

> --- a/gcc/omp-builtins.def
> +++ b/gcc/omp-builtins.def

> +DEF_GOACC_BUILTIN (BUILT_IN_GOACC_STATIC, "GOACC_register_static",
> +		   BT_FN_VOID_PTR_INT_UINT, ATTR_NOTHROW_LIST)

> --- a/libgomp/libgomp.map
> +++ b/libgomp/libgomp.map

> +	GOACC_register_static;

I think these changes can be dropped -- assuming you have not
unintentionally dropped the GOACC_register_static function/usage in your
v2 patch.

> --- a/gcc/c-family/c-common.c
> +++ b/gcc/c-family/c-common.c

> @@ -830,6 +830,7 @@ const struct attribute_spec c_common_attribute_table[] =

> +  { "oacc declare",           0, -1, true,  false, false, NULL, false },

As far as I can tell, nothing is setting this attribute anymore in your
v2 patch, so I guess this and all handling code ("lookup_attribute",
"remove_attribute") can also be dropped?

> --- a/gcc/c/c-parser.c
> +++ b/gcc/c/c-parser.c

>  /* OpenACC 2.0:
> +   # pragma acc declare oacc-data-clause[optseq] new-line
> +*/
> +
> +#define OACC_DECLARE_CLAUSE_MASK					\
> +	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINK)			\

For uniformity, please use/add a new alias "PRAGMA_OACC_CLAUSE_* =
PRAGMA_OMP_CLAUSE_LINK" instead of using PRAGMA_OMP_CLAUSE_* here, and
also in c_parser_oacc_data_clause, c_parser_oacc_all_clauses, and in the
C++ front end.

> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE) )
> +
> +static void
> +c_parser_oacc_declare (c_parser *parser)
> +{

> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c

> +static tree
> +cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
> +{
> +  [...]
> +      tree prev_attr = lookup_attribute ("oacc declare",
> +					     DECL_ATTRIBUTES (decl));

Per my comment above, this would always be NULL_TREE.  The C front end is
different?

> +
> +      if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
> +	id = get_identifier ("omp declare target link");
> +      else
> +        id = get_identifier ("omp declare target");
> +
> +      if (prev_attr)
> +	{
> +	  tree p = TREE_VALUE (prev_attr);
> +	  tree cl = TREE_VALUE (p);
> +
> +	  if (!devres && OMP_CLAUSE_MAP_KIND (cl) != GOMP_MAP_DEVICE_RESIDENT)
> +	    {
> +	      error_at (loc, "variable %qD used more than once with "
> +			"%<#pragma acc declare%>", decl);
> +	      inform (OMP_CLAUSE_LOCATION (TREE_VALUE (p)),
> +		      "previous directive was here");
> +	      error = true;
> +	      continue;
> +	    }
> +	}

> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -15314,6 +15314,17 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
>        add_stmt (t);
>        break;
>  
> +    case OACC_DECLARE:
> +      t = copy_node (t);
> +      tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
> +				args, complain, in_decl);
> +      OACC_DECLARE_CLAUSES (t) = tmp;
> +      tmp = tsubst_omp_clauses (OACC_DECLARE_RETURN_CLAUSES (t), false, false,
> +				args, complain, in_decl);
> +      OACC_DECLARE_RETURN_CLAUSES (t) = tmp;
> +      add_stmt (t);
> +      break;

Note to Jakub et al.: code for handling OACC_* is generally missing here,
also for other constructs and clauses; we'll be adding that.

> --- a/gcc/gimple.h
> +++ b/gcc/gimple.h
> @@ -170,6 +170,7 @@ enum gf_mask {
>      GF_OMP_TARGET_KIND_OACC_DATA = 7,
>      GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
>      GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
> +    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,

Need to update gcc/gimple-pretty-print.c:dump_gimple_omp_target.

> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c

> +/* Return true if global var DECL is device resident.  */
> +
> +static bool
> +device_resident_p (tree decl)
> +{
> +  tree attr = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));

Will always be NULL_TREE, as far as I can tell, so...

> +
> +  if (!attr)
> +    return false;
> +  

... will always return "false" here, and this is dead code:

> +  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
> +    {
> +      tree c = TREE_VALUE (t);
> +      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
> +	return true;
> +    }
> +
> +  return false;
> +}

> @@ -5838,6 +5860,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
>        flags |= GOVD_FIRSTPRIVATE;
>        break;
>      case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
> +      if (is_global_var (decl) && device_resident_p (decl))
> +	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;

Unreachable condition if device_resident_p always returns "false".

>        /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED.  */
>        gcc_assert ((ctx->region_type & ORT_TASK) != 0);
>        if (struct gimplify_omp_ctx *octx = ctx->outer_context)

> +/* Gimplify OACC_DECLARE.  */
> +
> +static void
> +gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
> +{
> +  tree expr = *expr_p;
> +  gomp_target *stmt;
> +  tree clauses, t;
> +
> +  clauses = OACC_DECLARE_CLAUSES (expr);
> +
> +  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
> +
> +  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
> +    {
> +      tree attrs, decl = OMP_CLAUSE_DECL (t);
> +
> +      if (TREE_CODE (decl) == MEM_REF)
> +	continue;
> +
> +      omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
> +
> +      attrs = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
> +      if (attrs)
> +	DECL_ATTRIBUTES (decl) = remove_attribute ("oacc declare", attrs);

As above, obsolete "oacc declare" attribute.

> --- a/include/gomp-constants.h
> +++ b/include/gomp-constants.h
> @@ -73,6 +73,11 @@ enum gomp_map_kind
>         POINTER_SIZE_UNITS.  */
>      GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
>      /* Do not map, copy bits for firstprivate instead.  */
> +    /* OpenACC device_resident.  */
> +    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
> +    /* OpenACC link.  */
> +    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
> +    /* Allocate.  */
>      GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
>      /* Similarly, but store the value in the pointer rather than
>         pointed by the pointer.  */

Confused -- I don't see these two getting handled in libgomp?

> --- a/libgomp/oacc-parallel.c
> +++ b/libgomp/oacc-parallel.c
> @@ -297,7 +297,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
>  
>        if (kind == GOMP_MAP_FORCE_ALLOC
>  	  || kind == GOMP_MAP_FORCE_PRESENT
> -	  || kind == GOMP_MAP_FORCE_TO)
> +	  || kind == GOMP_MAP_FORCE_TO
> +	  || kind == GOMP_MAP_TO
> +	  || kind == GOMP_MAP_ALLOC)
>  	{
>  	  data_enter = true;
>  	  break;
> @@ -324,6 +326,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
>  	    {
>  	      switch (kind)
>  		{
> +		case GOMP_MAP_ALLOC:
> +		  acc_present_or_create (hostaddrs[i], sizes[i]);
> +		  break;
>  		case GOMP_MAP_POINTER:
>  		  gomp_acc_insert_pointer (1, &hostaddrs[i], &sizes[i],
>  					&kinds[i]);
> @@ -332,6 +337,7 @@ GOACC_enter_exit_data (int device, size_t mapnum,
>  		  acc_create (hostaddrs[i], sizes[i]);
>  		  break;
>  		case GOMP_MAP_FORCE_PRESENT:
> +		case GOMP_MAP_TO:
>  		  acc_present_or_copyin (hostaddrs[i], sizes[i]);
>  		  break;
>  		case GOMP_MAP_FORCE_TO:

(As far as I can tell, these three hunks are not related to OpenACC
declare, but a bug fix for OpenACC enter/exit data.  Will submit that
later on, with test cases.)


Grüße
 Thomas

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 472 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-04 16:49   ` Thomas Schwinge
@ 2015-11-04 17:12     ` James Norris
  2015-11-06 16:08     ` James Norris
  1 sibling, 0 replies; 51+ messages in thread
From: James Norris @ 2015-11-04 17:12 UTC (permalink / raw)
  To: Thomas Schwinge, James Norris, Jakub Jelinek
  Cc: Joseph S. Myers, GCC Patches, Tobias Burnus, hubicka, Nathan Sidwell

Hi Thomas,

On 11/04/2015 10:49 AM, Thomas Schwinge wrote:
> Hi Jim!
>
> On Tue, 3 Nov 2015 10:31:32 -0600, James Norris <jnorris@codesourcery.com> wrote:
>> On 10/27/2015 03:18 PM, James Norris wrote:
>>>       This patch adds the processing of OpenACC declare directive in C
>>>       and C++. (Note: Support in Fortran is already in trunk.)
>
> ..., and a patch adjusting some Fortran front end things is awaiting
> review,
> <http://news.gmane.org/find-root.php?message_id=%3C5637692F.7050306%40codesourcery.com%3E>.
>
>>       I've revised the patch since I originally submitted it for review
>>       (https://gcc.gnu.org/ml/gcc-patches/2015-10/msg02967.html). The
>>       revision is due to Jakub and et al OpenMP 4.5 work in the area of
>>       'omp declare target'. I now exploit that functionality and have
>>       revised the patch accordingly.
>
> Oh, wow, you could remove a lot of code!

Yes, I missed that patch when it entered into the code base. My bad.

>
> Just a superficial review on your patch; patch re-ordered a bit for
> review.
>
>> --- a/gcc/builtin-types.def
>> +++ b/gcc/builtin-types.def
>
>> +DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
>
>> --- a/gcc/fortran/types.def
>> +++ b/gcc/fortran/types.def
>
>> +DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
>
>> --- a/gcc/omp-builtins.def
>> +++ b/gcc/omp-builtins.def
>
>> +DEF_GOACC_BUILTIN (BUILT_IN_GOACC_STATIC, "GOACC_register_static",
>> +		   BT_FN_VOID_PTR_INT_UINT, ATTR_NOTHROW_LIST)
>
>> --- a/libgomp/libgomp.map
>> +++ b/libgomp/libgomp.map
>
>> +	GOACC_register_static;
>
> I think these changes can be dropped -- assuming you have not
> unintentionally dropped the GOACC_register_static function/usage in your
> v2 patch.

Will fix.

>
>> --- a/gcc/c-family/c-common.c
>> +++ b/gcc/c-family/c-common.c
>
>> @@ -830,6 +830,7 @@ const struct attribute_spec c_common_attribute_table[] =
>
>> +  { "oacc declare",           0, -1, true,  false, false, NULL, false },
>
> As far as I can tell, nothing is setting this attribute anymore in your
> v2 patch, so I guess this and all handling code ("lookup_attribute",
> "remove_attribute") can also be dropped?

Will fix.

>
>> --- a/gcc/c/c-parser.c
>> +++ b/gcc/c/c-parser.c
>
>>   /* OpenACC 2.0:
>> +   # pragma acc declare oacc-data-clause[optseq] new-line
>> +*/
>> +
>> +#define OACC_DECLARE_CLAUSE_MASK					\
>> +	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
>> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
>> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
>> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
>> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
>> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
>> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINK)			\
>
> For uniformity, please use/add a new alias "PRAGMA_OACC_CLAUSE_* =
> PRAGMA_OMP_CLAUSE_LINK" instead of using PRAGMA_OMP_CLAUSE_* here, and
> also in c_parser_oacc_data_clause, c_parser_oacc_all_clauses, and in the
> C++ front end.

Will fix.

>
>> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
>> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
>> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
>> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
>> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE) )
>> +
>> +static void
>> +c_parser_oacc_declare (c_parser *parser)
>> +{
>
>> --- a/gcc/cp/parser.c
>> +++ b/gcc/cp/parser.c
>
>> +static tree
>> +cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
>> +{
>> +  [...]
>> +      tree prev_attr = lookup_attribute ("oacc declare",
>> +					     DECL_ATTRIBUTES (decl));
>
> Per my comment above, this would always be NULL_TREE.  The C front end is
> different?

Will fix.

>
>> +
>> +      if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
>> +	id = get_identifier ("omp declare target link");
>> +      else
>> +        id = get_identifier ("omp declare target");
>> +
>> +      if (prev_attr)
>> +	{
>> +	  tree p = TREE_VALUE (prev_attr);
>> +	  tree cl = TREE_VALUE (p);
>> +
>> +	  if (!devres && OMP_CLAUSE_MAP_KIND (cl) != GOMP_MAP_DEVICE_RESIDENT)
>> +	    {
>> +	      error_at (loc, "variable %qD used more than once with "
>> +			"%<#pragma acc declare%>", decl);
>> +	      inform (OMP_CLAUSE_LOCATION (TREE_VALUE (p)),
>> +		      "previous directive was here");
>> +	      error = true;
>> +	      continue;
>> +	    }
>> +	}
>
>> --- a/gcc/cp/pt.c
>> +++ b/gcc/cp/pt.c
>> @@ -15314,6 +15314,17 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
>>         add_stmt (t);
>>         break;
>>
>> +    case OACC_DECLARE:
>> +      t = copy_node (t);
>> +      tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
>> +				args, complain, in_decl);
>> +      OACC_DECLARE_CLAUSES (t) = tmp;
>> +      tmp = tsubst_omp_clauses (OACC_DECLARE_RETURN_CLAUSES (t), false, false,
>> +				args, complain, in_decl);
>> +      OACC_DECLARE_RETURN_CLAUSES (t) = tmp;
>> +      add_stmt (t);
>> +      break;
>
> Note to Jakub et al.: code for handling OACC_* is generally missing here,
> also for other constructs and clauses; we'll be adding that.
>
>> --- a/gcc/gimple.h
>> +++ b/gcc/gimple.h
>> @@ -170,6 +170,7 @@ enum gf_mask {
>>       GF_OMP_TARGET_KIND_OACC_DATA = 7,
>>       GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
>>       GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
>> +    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,
>
> Need to update gcc/gimple-pretty-print.c:dump_gimple_omp_target.
>
>> --- a/gcc/gimplify.c
>> +++ b/gcc/gimplify.c
>
>> +/* Return true if global var DECL is device resident.  */
>> +
>> +static bool
>> +device_resident_p (tree decl)
>> +{
>> +  tree attr = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
>
> Will always be NULL_TREE, as far as I can tell, so...

Will fix.

>
>> +
>> +  if (!attr)
>> +    return false;
>> +
>
> ... will always return "false" here, and this is dead code:

Will fix.

>
>> +  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
>> +    {
>> +      tree c = TREE_VALUE (t);
>> +      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
>> +	return true;
>> +    }
>> +
>> +  return false;
>> +}
>
>> @@ -5838,6 +5860,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
>>         flags |= GOVD_FIRSTPRIVATE;
>>         break;
>>       case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
>> +      if (is_global_var (decl) && device_resident_p (decl))
>> +	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
>
> Unreachable condition if device_resident_p always returns "false".

Will fix.

>
>>         /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED.  */
>>         gcc_assert ((ctx->region_type & ORT_TASK) != 0);
>>         if (struct gimplify_omp_ctx *octx = ctx->outer_context)
>
>> +/* Gimplify OACC_DECLARE.  */
>> +
>> +static void
>> +gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
>> +{
>> +  tree expr = *expr_p;
>> +  gomp_target *stmt;
>> +  tree clauses, t;
>> +
>> +  clauses = OACC_DECLARE_CLAUSES (expr);
>> +
>> +  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
>> +
>> +  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
>> +    {
>> +      tree attrs, decl = OMP_CLAUSE_DECL (t);
>> +
>> +      if (TREE_CODE (decl) == MEM_REF)
>> +	continue;
>> +
>> +      omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
>> +
>> +      attrs = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
>> +      if (attrs)
>> +	DECL_ATTRIBUTES (decl) = remove_attribute ("oacc declare", attrs);
>
> As above, obsolete "oacc declare" attribute.
>

Will fix.

>> --- a/include/gomp-constants.h
>> +++ b/include/gomp-constants.h
>> @@ -73,6 +73,11 @@ enum gomp_map_kind
>>          POINTER_SIZE_UNITS.  */
>>       GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
>>       /* Do not map, copy bits for firstprivate instead.  */
>> +    /* OpenACC device_resident.  */
>> +    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
>> +    /* OpenACC link.  */
>> +    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
>> +    /* Allocate.  */
>>       GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
>>       /* Similarly, but store the value in the pointer rather than
>>          pointed by the pointer.  */
>
> Confused -- I don't see these two getting handled in libgomp?

These won't be 'seen' by libgomp. So should these
be defined by some other means?

>
>> --- a/libgomp/oacc-parallel.c
>> +++ b/libgomp/oacc-parallel.c
>> @@ -297,7 +297,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
>>
>>         if (kind == GOMP_MAP_FORCE_ALLOC
>>   	  || kind == GOMP_MAP_FORCE_PRESENT
>> -	  || kind == GOMP_MAP_FORCE_TO)
>> +	  || kind == GOMP_MAP_FORCE_TO
>> +	  || kind == GOMP_MAP_TO
>> +	  || kind == GOMP_MAP_ALLOC)
>>   	{
>>   	  data_enter = true;
>>   	  break;
>> @@ -324,6 +326,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
>>   	    {
>>   	      switch (kind)
>>   		{
>> +		case GOMP_MAP_ALLOC:
>> +		  acc_present_or_create (hostaddrs[i], sizes[i]);
>> +		  break;
>>   		case GOMP_MAP_POINTER:
>>   		  gomp_acc_insert_pointer (1, &hostaddrs[i], &sizes[i],
>>   					&kinds[i]);
>> @@ -332,6 +337,7 @@ GOACC_enter_exit_data (int device, size_t mapnum,
>>   		  acc_create (hostaddrs[i], sizes[i]);
>>   		  break;
>>   		case GOMP_MAP_FORCE_PRESENT:
>> +		case GOMP_MAP_TO:
>>   		  acc_present_or_copyin (hostaddrs[i], sizes[i]);
>>   		  break;
>>   		case GOMP_MAP_FORCE_TO:
>
> (As far as I can tell, these three hunks are not related to OpenACC
> declare, but a bug fix for OpenACC enter/exit data.  Will submit that
> later on, with test cases.)
>

I'll eliminate the three hunks from the patch.

Thank you for taking the time to review the patch.

Jim



^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: OpenACC declare directive updates
  2015-11-04 12:32 ` James Norris
@ 2015-11-06 13:46   ` James Norris
  2015-11-06 19:31   ` Jakub Jelinek
  1 sibling, 0 replies; 51+ messages in thread
From: James Norris @ 2015-11-06 13:46 UTC (permalink / raw)
  To: James Norris, Jakub Jelinek; +Cc: GCC Patches

Jakub,

Ping

Do you need more information before you can review this patch?

Thanks!
Jim


On 11/04/2015 06:32 AM, James Norris wrote:
>
>      This patch updates the processing of OpenACC declare directive for
>      Fortran in the following areas:
>
>          1) module support
>          2) device_resident and link clauses
>          3) clause checking
>          4) directive generation
>
>      Commentary on the changes is included as an attachment (NOTES).
>
>      All of the code is in the gomp-4_0-branch.
>
>      Regtested on x86_64-linux.
>
>      Thanks!
>      Jim

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-10-27 20:20 [OpenACC] declare directive James Norris
  2015-10-28 16:33 ` Cesar Philippidis
  2015-11-03 16:31 ` James Norris
@ 2015-11-06 13:48 ` James Norris
  2021-06-10 11:15 ` Thomas Schwinge
  3 siblings, 0 replies; 51+ messages in thread
From: James Norris @ 2015-11-06 13:48 UTC (permalink / raw)
  To: James Norris, GCC Patches; +Cc: Jakub Jelinek

Jakub,

Ping.

Do you need more information before you can review this patch?

Thanks!
Jim


On 10/27/2015 03:18 PM, James Norris wrote:
> Hi!
>
>      This patch adds the processing of OpenACC declare directive in C
>      and C++. (Note: Support in Fortran is already in trunk.)
>      Commentary on the changes is included as an attachment (NOTES).
>
>      All of the code is in the gomp-4_0-branch.
>
>      Regtested on x86_64-linux.
>
>      Thanks!
>      Jim

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-04 16:49   ` Thomas Schwinge
  2015-11-04 17:12     ` James Norris
@ 2015-11-06 16:08     ` James Norris
  2015-11-06 16:16       ` James Norris
  2015-11-06 19:04       ` Jakub Jelinek
  1 sibling, 2 replies; 51+ messages in thread
From: James Norris @ 2015-11-06 16:08 UTC (permalink / raw)
  To: Thomas Schwinge, James Norris, Jakub Jelinek; +Cc: Joseph S. Myers, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 10903 bytes --]

Thomas,

I've added the issues that you pointed out. Attached
is the updated patch and ChangeLog.

Thanks for taking the time for your review!

Jakub,

Could you queue this patch ahead of my other one for
today? If it doesn't pass review, I'd like to work
the weekend and have the updated patch ready for
you when you walk in on your Monday.

Thank you,
Jim

> On 11/04/2015 10:49 AM, Thomas Schwinge wrote:
>> Hi Jim!
>>
>> On Tue, 3 Nov 2015 10:31:32 -0600, James Norris <jnorris@codesourcery.com> wrote:
>>> On 10/27/2015 03:18 PM, James Norris wrote:
>>>>       This patch adds the processing of OpenACC declare directive in C
>>>>       and C++. (Note: Support in Fortran is already in trunk.)
>>
>> ..., and a patch adjusting some Fortran front end things is awaiting
>> review,
>> <http://news.gmane.org/find-root.php?message_id=%3C5637692F.7050306%40codesourcery.com%3E>.
>>
>>>       I've revised the patch since I originally submitted it for review
>>>       (https://gcc.gnu.org/ml/gcc-patches/2015-10/msg02967.html). The
>>>       revision is due to Jakub and et al OpenMP 4.5 work in the area of
>>>       'omp declare target'. I now exploit that functionality and have
>>>       revised the patch accordingly.
>>
>> Oh, wow, you could remove a lot of code!
>
> Yes, I missed that patch when it entered into the code base. My bad.
>
>>
>> Just a superficial review on your patch; patch re-ordered a bit for
>> review.
>>
>>> --- a/gcc/builtin-types.def
>>> +++ b/gcc/builtin-types.def
>>
>>> +DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
>>
>>> --- a/gcc/fortran/types.def
>>> +++ b/gcc/fortran/types.def
>>
>>> +DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
>>
>>> --- a/gcc/omp-builtins.def
>>> +++ b/gcc/omp-builtins.def
>>
>>> +DEF_GOACC_BUILTIN (BUILT_IN_GOACC_STATIC, "GOACC_register_static",
>>> +           BT_FN_VOID_PTR_INT_UINT, ATTR_NOTHROW_LIST)
>>
>>> --- a/libgomp/libgomp.map
>>> +++ b/libgomp/libgomp.map
>>
>>> +    GOACC_register_static;
>>
>> I think these changes can be dropped -- assuming you have not
>> unintentionally dropped the GOACC_register_static function/usage in your
>> v2 patch.
>
> Will fix.
>
>>
>>> --- a/gcc/c-family/c-common.c
>>> +++ b/gcc/c-family/c-common.c
>>
>>> @@ -830,6 +830,7 @@ const struct attribute_spec c_common_attribute_table[] =
>>
>>> +  { "oacc declare",           0, -1, true,  false, false, NULL, false },
>>
>> As far as I can tell, nothing is setting this attribute anymore in your
>> v2 patch, so I guess this and all handling code ("lookup_attribute",
>> "remove_attribute") can also be dropped?
>
> Will fix.
>
>>
>>> --- a/gcc/c/c-parser.c
>>> +++ b/gcc/c/c-parser.c
>>
>>>   /* OpenACC 2.0:
>>> +   # pragma acc declare oacc-data-clause[optseq] new-line
>>> +*/
>>> +
>>> +#define OACC_DECLARE_CLAUSE_MASK                    \
>>> +    ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)        \
>>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)        \
>>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)        \
>>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)        \
>>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)        \
>>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)    \
>>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINK)            \
>>
>> For uniformity, please use/add a new alias "PRAGMA_OACC_CLAUSE_* =
>> PRAGMA_OMP_CLAUSE_LINK" instead of using PRAGMA_OMP_CLAUSE_* here, and
>> also in c_parser_oacc_data_clause, c_parser_oacc_all_clauses, and in the
>> C++ front end.
>
> Will fix.
>
>>
>>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)        \
>>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)    \
>>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)    \
>>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)    \
>>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE) )
>>> +
>>> +static void
>>> +c_parser_oacc_declare (c_parser *parser)
>>> +{
>>
>>> --- a/gcc/cp/parser.c
>>> +++ b/gcc/cp/parser.c
>>
>>> +static tree
>>> +cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
>>> +{
>>> +  [...]
>>> +      tree prev_attr = lookup_attribute ("oacc declare",
>>> +                         DECL_ATTRIBUTES (decl));
>>
>> Per my comment above, this would always be NULL_TREE.  The C front end is
>> different?
>
> Will fix.
>
>>
>>> +
>>> +      if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
>>> +    id = get_identifier ("omp declare target link");
>>> +      else
>>> +        id = get_identifier ("omp declare target");
>>> +
>>> +      if (prev_attr)
>>> +    {
>>> +      tree p = TREE_VALUE (prev_attr);
>>> +      tree cl = TREE_VALUE (p);
>>> +
>>> +      if (!devres && OMP_CLAUSE_MAP_KIND (cl) != GOMP_MAP_DEVICE_RESIDENT)
>>> +        {
>>> +          error_at (loc, "variable %qD used more than once with "
>>> +            "%<#pragma acc declare%>", decl);
>>> +          inform (OMP_CLAUSE_LOCATION (TREE_VALUE (p)),
>>> +              "previous directive was here");
>>> +          error = true;
>>> +          continue;
>>> +        }
>>> +    }
>>
>>> --- a/gcc/cp/pt.c
>>> +++ b/gcc/cp/pt.c
>>> @@ -15314,6 +15314,17 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
>>>         add_stmt (t);
>>>         break;
>>>
>>> +    case OACC_DECLARE:
>>> +      t = copy_node (t);
>>> +      tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
>>> +                args, complain, in_decl);
>>> +      OACC_DECLARE_CLAUSES (t) = tmp;
>>> +      tmp = tsubst_omp_clauses (OACC_DECLARE_RETURN_CLAUSES (t), false, false,
>>> +                args, complain, in_decl);
>>> +      OACC_DECLARE_RETURN_CLAUSES (t) = tmp;
>>> +      add_stmt (t);
>>> +      break;
>>
>> Note to Jakub et al.: code for handling OACC_* is generally missing here,
>> also for other constructs and clauses; we'll be adding that.
>>
>>> --- a/gcc/gimple.h
>>> +++ b/gcc/gimple.h
>>> @@ -170,6 +170,7 @@ enum gf_mask {
>>>       GF_OMP_TARGET_KIND_OACC_DATA = 7,
>>>       GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
>>>       GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
>>> +    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,
>>
>> Need to update gcc/gimple-pretty-print.c:dump_gimple_omp_target.
>>
>>> --- a/gcc/gimplify.c
>>> +++ b/gcc/gimplify.c
>>
>>> +/* Return true if global var DECL is device resident.  */
>>> +
>>> +static bool
>>> +device_resident_p (tree decl)
>>> +{
>>> +  tree attr = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
>>
>> Will always be NULL_TREE, as far as I can tell, so...
>
> Will fix.
>
>>
>>> +
>>> +  if (!attr)
>>> +    return false;
>>> +
>>
>> ... will always return "false" here, and this is dead code:
>
> Will fix.
>
>>
>>> +  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
>>> +    {
>>> +      tree c = TREE_VALUE (t);
>>> +      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
>>> +    return true;
>>> +    }
>>> +
>>> +  return false;
>>> +}
>>
>>> @@ -5838,6 +5860,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
>>>         flags |= GOVD_FIRSTPRIVATE;
>>>         break;
>>>       case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
>>> +      if (is_global_var (decl) && device_resident_p (decl))
>>> +    flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
>>
>> Unreachable condition if device_resident_p always returns "false".
>
> Will fix.
>
>>
>>>         /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED.  */
>>>         gcc_assert ((ctx->region_type & ORT_TASK) != 0);
>>>         if (struct gimplify_omp_ctx *octx = ctx->outer_context)
>>
>>> +/* Gimplify OACC_DECLARE.  */
>>> +
>>> +static void
>>> +gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
>>> +{
>>> +  tree expr = *expr_p;
>>> +  gomp_target *stmt;
>>> +  tree clauses, t;
>>> +
>>> +  clauses = OACC_DECLARE_CLAUSES (expr);
>>> +
>>> +  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
>>> +
>>> +  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
>>> +    {
>>> +      tree attrs, decl = OMP_CLAUSE_DECL (t);
>>> +
>>> +      if (TREE_CODE (decl) == MEM_REF)
>>> +    continue;
>>> +
>>> +      omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
>>> +
>>> +      attrs = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
>>> +      if (attrs)
>>> +    DECL_ATTRIBUTES (decl) = remove_attribute ("oacc declare", attrs);
>>
>> As above, obsolete "oacc declare" attribute.
>>
>
> Will fix.
>
>>> --- a/include/gomp-constants.h
>>> +++ b/include/gomp-constants.h
>>> @@ -73,6 +73,11 @@ enum gomp_map_kind
>>>          POINTER_SIZE_UNITS.  */
>>>       GOMP_MAP_FORCE_DEVICEPTR =        (GOMP_MAP_FLAG_SPECIAL_1 | 0),
>>>       /* Do not map, copy bits for firstprivate instead.  */
>>> +    /* OpenACC device_resident.  */
>>> +    GOMP_MAP_DEVICE_RESIDENT =        (GOMP_MAP_FLAG_SPECIAL_1 | 1),
>>> +    /* OpenACC link.  */
>>> +    GOMP_MAP_LINK =            (GOMP_MAP_FLAG_SPECIAL_1 | 2),
>>> +    /* Allocate.  */
>>>       GOMP_MAP_FIRSTPRIVATE =        (GOMP_MAP_FLAG_SPECIAL | 0),
>>>       /* Similarly, but store the value in the pointer rather than
>>>          pointed by the pointer.  */
>>
>> Confused -- I don't see these two getting handled in libgomp?
>
> These won't be 'seen' by libgomp. So should these
> be defined by some other means?
>
>>
>>> --- a/libgomp/oacc-parallel.c
>>> +++ b/libgomp/oacc-parallel.c
>>> @@ -297,7 +297,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
>>>
>>>         if (kind == GOMP_MAP_FORCE_ALLOC
>>>         || kind == GOMP_MAP_FORCE_PRESENT
>>> -      || kind == GOMP_MAP_FORCE_TO)
>>> +      || kind == GOMP_MAP_FORCE_TO
>>> +      || kind == GOMP_MAP_TO
>>> +      || kind == GOMP_MAP_ALLOC)
>>>       {
>>>         data_enter = true;
>>>         break;
>>> @@ -324,6 +326,9 @@ GOACC_enter_exit_data (int device, size_t mapnum,
>>>           {
>>>             switch (kind)
>>>           {
>>> +        case GOMP_MAP_ALLOC:
>>> +          acc_present_or_create (hostaddrs[i], sizes[i]);
>>> +          break;
>>>           case GOMP_MAP_POINTER:
>>>             gomp_acc_insert_pointer (1, &hostaddrs[i], &sizes[i],
>>>                       &kinds[i]);
>>> @@ -332,6 +337,7 @@ GOACC_enter_exit_data (int device, size_t mapnum,
>>>             acc_create (hostaddrs[i], sizes[i]);
>>>             break;
>>>           case GOMP_MAP_FORCE_PRESENT:
>>> +        case GOMP_MAP_TO:
>>>             acc_present_or_copyin (hostaddrs[i], sizes[i]);
>>>             break;
>>>           case GOMP_MAP_FORCE_TO:
>>
>> (As far as I can tell, these three hunks are not related to OpenACC
>> declare, but a bug fix for OpenACC enter/exit data.  Will submit that
>> later on, with test cases.)
>>
>
> I'll eliminate the three hunks from the patch.
>
> Thank you for taking the time to review the patch.
>
> Jim
>
>
>


[-- Attachment #2: ChangeLog --]
[-- Type: text/plain, Size: 2639 bytes --]

2015-10-27  James Norris  <jnorris@codesourcery.com>
	    Joseph Myers  <joseph@codesourcery.com>

	gcc/
	* c-family/c-pragma.c (oacc_pragmas): Add entry for declare directive. 
	* c-family/c-pragma.h (enum pragma_kind): Add PRAGMA_OACC_DECLARE.
	(enum pragma_omp_clause): Add PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT.
	* c/c-parser.c (c_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	(c_parser_omp_clause_name): Handle 'device_resident' clause.
	(c_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(c_parser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OACC_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(c_parser_oacc_declare): New function.
	* cp/parser.c (cp_parser_omp_clause_name): Handle 'device_resident'
	clause.
	(cp_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(cp_parser_oacc_declare): New function.
	(cp_paser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(cp_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	* cp/pt.c (tsubst_expr): Handle OACC_DECLARE.
	* gimple.h (enum gf_mask): Add GF_OMP_TARGET_KIND_OACC_DECLARE.
	(is_gomple_omp_oacc): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* gimplify.c (struct gimplify_omp_ctx): New field.
	(new_omp_context): Initialize new field.
	(omp_default_clause): Handle device resident variable.
	(gimplify_oacc_declare): New function.
	(device_resident_p): New function.
	(gimplify_expr): Handle OACC_DECLARE.
	(gimplify_body): Handle updating of declare'd variables.
	* omp-builtins.def (BUILT_IN_GOACC_STATIC, BUILT_IN_GOACC_DECLARE):
	New builtins.
	* omp-low.c (expand_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE and BUILTIN_GOACC_DECLARE.
	(lower_omp_target): Handle GF_OMP_TARGET_KIND_OACC_DECLARE,
	GOMP_MAP_DEVICE_RESIDENT and GOMP_MAP_LINK.
	(make_gimple_omp_edges): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* tree.def (OACC_DECLARE): Update operands.
	* tree.h (OACC_DECLARE_RETURN_CLAUSES): New definition.
	* varpool.c (make_offloadable): New function.
	(get_create): Refactor offload functionality.

	gcc/testsuite
	* c-c++-common/goacc/declare-1.c: New test.
	* c-c++-common/goacc/declare-2.c: Likewise.

	include/
	* gomp-constants.h (enum gomp_map_kind): Add GOMP_MAP_DEVICE_RESIDENT
	and GOMP_MAP_LINK.

	libgomp/

	* libgomp.map (GOACC_2.0.1): Export GOACC_declare.
	* oacc-parallel.c (GOACC_declare): New function.
	* testsuite/libgomp.oacc-c-c++-common/declare-1.c: New test.
	* testsuite/libgomp.oacc-c-c++-common/declare-5.c: Likewise.

[-- Attachment #3: declare.patch --]
[-- Type: text/x-patch, Size: 37528 bytes --]

diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
index ac11838..cd0cc27 100644
--- a/gcc/c-family/c-pragma.c
+++ b/gcc/c-family/c-pragma.c
@@ -1207,6 +1207,7 @@ static const struct omp_pragma_def oacc_pragmas[] = {
   { "atomic", PRAGMA_OACC_ATOMIC },
   { "cache", PRAGMA_OACC_CACHE },
   { "data", PRAGMA_OACC_DATA },
+  { "declare", PRAGMA_OACC_DECLARE },
   { "enter", PRAGMA_OACC_ENTER_DATA },
   { "exit", PRAGMA_OACC_EXIT_DATA },
   { "kernels", PRAGMA_OACC_KERNELS },
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 953c4e3..c6a2981 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -30,6 +30,7 @@ enum pragma_kind {
   PRAGMA_OACC_ATOMIC,
   PRAGMA_OACC_CACHE,
   PRAGMA_OACC_DATA,
+  PRAGMA_OACC_DECLARE,
   PRAGMA_OACC_ENTER_DATA,
   PRAGMA_OACC_EXIT_DATA,
   PRAGMA_OACC_KERNELS,
@@ -151,6 +152,7 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_CREATE,
   PRAGMA_OACC_CLAUSE_DELETE,
   PRAGMA_OACC_CLAUSE_DEVICEPTR,
+  PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT,
   PRAGMA_OACC_CLAUSE_GANG,
   PRAGMA_OACC_CLAUSE_HOST,
   PRAGMA_OACC_CLAUSE_INDEPENDENT,
@@ -175,7 +177,8 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_FIRSTPRIVATE = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE,
   PRAGMA_OACC_CLAUSE_IF = PRAGMA_OMP_CLAUSE_IF,
   PRAGMA_OACC_CLAUSE_PRIVATE = PRAGMA_OMP_CLAUSE_PRIVATE,
-  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION
+  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION,
+  PRAGMA_OACC_CLAUSE_LINK = PRAGMA_OMP_CLAUSE_LINK
 };
 
 extern struct cpp_reader* parse_in;
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 23d0107..212420f 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1231,6 +1231,7 @@ static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
 					     unsigned int * = NULL);
+static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
 static void c_parser_omp_construct (c_parser *);
@@ -9697,6 +9698,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context)
 
   switch (id)
     {
+    case PRAGMA_OACC_DECLARE:
+      c_parser_oacc_declare (parser);
+      return false;
+
     case PRAGMA_OACC_ENTER_DATA:
       c_parser_oacc_enter_exit_data (parser, true);
       return false;
@@ -9982,6 +9987,8 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -10418,10 +10425,16 @@ c_parser_oacc_data_clause (c_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -12703,6 +12716,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = c_parser_omp_clause_firstprivate (parser, clauses);
 	  c_name = "firstprivate";
@@ -12725,6 +12742,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 						clauses);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  clauses = c_parser_omp_clause_num_gangs (parser, clauses);
 	  c_name = "num_gangs";
@@ -13182,6 +13203,213 @@ c_parser_oacc_data (location_t loc, c_parser *parser)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE) )
+
+static void
+c_parser_oacc_declare (c_parser *parser)
+{
+  location_t pragma_loc = c_parser_peek_token (parser)->location;
+  tree c, clauses, ret_clauses, stmt, t;
+
+  bool error = false;
+
+  c_parser_consume_pragma (parser);
+
+  clauses = c_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+				       "#pragma acc declare");
+  if (!clauses)
+    {
+      error_at (pragma_loc,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return;
+    }
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      tree decl = OMP_CLAUSE_DECL (t);
+      tree devres = NULL_TREE;
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "subarray in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	  break;
+
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  devres = t;
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p () && !DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl) =
+			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_LINK:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = copy_node (c);
+
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+    stmt = make_node (OACC_DECLARE);
+    TREE_TYPE (stmt) = void_type_node;
+    OACC_DECLARE_CLAUSES (stmt) = clauses;
+    OACC_DECLARE_RETURN_CLAUSES (stmt) = ret_clauses;
+    SET_EXPR_LOCATION (stmt, pragma_loc);
+
+    add_stmt (stmt);
+
+    return;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c6f5729..3dca6c1 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -29099,6 +29099,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -29512,10 +29514,16 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -31516,6 +31524,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  clauses = cp_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_FIRSTPRIVATE,
 					    clauses);
@@ -31540,6 +31552,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 						  clauses, here);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  code = OMP_CLAUSE_NUM_GANGS;
 	  c_name = "num_gangs";
@@ -34497,6 +34513,213 @@ cp_parser_oacc_data (cp_parser *parser, cp_token *pragma_tok)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE))
+
+static tree
+cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
+{
+  tree c, clauses, ret_clauses, stmt, t;
+  bool error = false;
+
+  clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+					"#pragma acc declare", pragma_tok, true);
+
+
+  if (find_omp_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
+    {
+      error_at (pragma_tok->location,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return NULL_TREE;
+    }
+
+  for (tree t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      tree decl = OMP_CLAUSE_DECL (t);
+      tree devres = NULL_TREE;
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "subarray in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+      gcc_assert (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_MAP);
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	  break;
+
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  devres = t;
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p () && !DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl) =
+			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return NULL_TREE;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  case GOMP_MAP_LINK:
+	    continue;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = copy_node (c);
+
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+  stmt = make_node (OACC_DECLARE);
+  TREE_TYPE (stmt) = void_type_node;
+  OACC_DECLARE_CLAUSES (stmt) = clauses;
+  OACC_DECLARE_RETURN_CLAUSES (stmt) = ret_clauses;
+  SET_EXPR_LOCATION (stmt, pragma_tok->location);
+
+  add_stmt (stmt);
+
+  return NULL_TREE;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
@@ -36183,6 +36406,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context)
       cp_parser_omp_declare (parser, pragma_tok, context);
       return false;
 
+    case PRAGMA_OACC_DECLARE:
+      cp_parser_oacc_declare (parser, pragma_tok);
+      return false;
+
     case PRAGMA_OACC_ATOMIC:
     case PRAGMA_OACC_CACHE:
     case PRAGMA_OACC_DATA:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 45eda3a..3e03f02 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15422,6 +15422,17 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       add_stmt (t);
       break;
 
+    case OACC_DECLARE:
+      t = copy_node (t);
+      tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_CLAUSES (t) = tmp;
+      tmp = tsubst_omp_clauses (OACC_DECLARE_RETURN_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_RETURN_CLAUSES (t) = tmp;
+      add_stmt (t);
+      break;
+
     case OMP_TARGET_UPDATE:
     case OMP_TARGET_ENTER_DATA:
     case OMP_TARGET_EXIT_DATA:
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 781801b..e45162d 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -170,6 +170,7 @@ enum gf_mask {
     GF_OMP_TARGET_KIND_OACC_DATA = 7,
     GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
     GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
+    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,
 
     /* True on an GIMPLE_OMP_RETURN statement if the return does not require
        a thread synchronization via some sort of barrier.  The exact barrier
@@ -6004,6 +6005,7 @@ is_gimple_omp_oacc (const gimple *stmt)
 	case GF_OMP_TARGET_KIND_OACC_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  return true;
 	default:
 	  return false;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index fa34858..fbd4a69 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -157,6 +157,7 @@ struct gimplify_omp_ctx
   bool target_map_scalars_firstprivate;
   bool target_map_pointers_as_0len_arrays;
   bool target_firstprivatize_array_bases;
+  gomp_target *declare_returns;
 };
 
 static struct gimplify_ctx *gimplify_ctxp;
@@ -376,6 +377,7 @@ new_omp_context (enum omp_region_type region_type)
     c->default_kind = OMP_CLAUSE_DEFAULT_SHARED;
   else
     c->default_kind = OMP_CLAUSE_DEFAULT_UNSPECIFIED;
+  c->declare_returns = NULL;
 
   return c;
 }
@@ -5792,6 +5794,26 @@ omp_notice_threadprivate_variable (struct gimplify_omp_ctx *ctx, tree decl,
   return false;
 }
 
+/* Return true if global var DECL is device resident.  */
+
+static bool
+device_resident_p (tree decl)
+{
+  tree attr = lookup_attribute ("oacc declare target", DECL_ATTRIBUTES (decl));
+
+  if (!attr)
+    return false;
+  
+  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
+    {
+      tree c = TREE_VALUE (t);
+      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
+	return true;
+    }
+
+  return false;
+}
+
 /* Determine outer default flags for DECL mentioned in an OMP region
    but not declared in an enclosing clause.
 
@@ -5841,6 +5863,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
       flags |= GOVD_FIRSTPRIVATE;
       break;
     case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
+      if (is_global_var (decl) && device_resident_p (decl))
+	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
       /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED.  */
       gcc_assert ((ctx->region_type & ORT_TASK) != 0);
       if (struct gimplify_omp_ctx *octx = ctx->outer_context)
@@ -7712,6 +7736,58 @@ gimplify_oacc_cache (tree *expr_p, gimple_seq *pre_p)
   *expr_p = NULL_TREE;
 }
 
+/* Gimplify OACC_DECLARE.  */
+
+static void
+gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
+{
+  tree expr = *expr_p;
+  gomp_target *stmt;
+  tree clauses, t;
+
+  clauses = OACC_DECLARE_CLAUSES (expr);
+
+  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      tree attrs, decl = OMP_CLAUSE_DECL (t);
+
+      if (TREE_CODE (decl) == MEM_REF)
+	continue;
+
+      omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
+    }
+
+  stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				  clauses);
+
+  gimplify_seq_add_stmt (pre_p, stmt);
+
+  clauses = OACC_DECLARE_RETURN_CLAUSES (expr);
+  if (clauses)
+    {
+      struct gimplify_omp_ctx *c;
+
+      /* Any clauses that affect the state of a variable prior
+         to return are saved and dealt with after the body has
+         been gimplified.  */
+
+      gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA,
+				 OACC_DECLARE);
+
+      c = gimplify_omp_ctxp;
+      gimplify_omp_ctxp = c->outer_context;
+      delete_omp_context (c);
+
+      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				      clauses);
+      gimplify_omp_ctxp->declare_returns = stmt;
+    }
+
+  *expr_p = NULL_TREE;
+}
+
 /* Gimplify the contents of an OMP_PARALLEL statement.  This involves
    gimplification of the body, as well as scanning the body for used
    variables.  We need to do this scan now, because variable-sized
@@ -10063,11 +10139,15 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  break;
 
 	case OACC_HOST_DATA:
-	case OACC_DECLARE:
 	  sorry ("directive not yet implemented");
 	  ret = GS_ALL_DONE;
 	  break;
 
+	case OACC_DECLARE:
+	  gimplify_oacc_declare (expr_p, pre_p);
+	  ret = GS_ALL_DONE;
+	  break;
+
 	case OACC_DATA:
 	case OACC_KERNELS:
 	case OACC_PARALLEL:
@@ -10728,6 +10808,28 @@ gimplify_body (tree fndecl, bool do_parms)
       gimplify_seq_add_stmt (&seq, outer_stmt);
     }
 
+  if (flag_openacc && gimplify_omp_ctxp)
+    {
+      while (gimplify_omp_ctxp)
+	{
+	  struct gimplify_omp_ctx *c;
+
+	  if (gimplify_omp_ctxp->declare_returns)
+	    {
+              /* Clauses are present that affect the state of a
+                 variable, insert the statment to handle this
+                 as the very last statement.  */
+
+	      gimplify_seq_add_stmt (&seq, gimplify_omp_ctxp->declare_returns);
+	      gimplify_omp_ctxp->declare_returns = NULL;
+	    }
+
+	  c = gimplify_omp_ctxp;
+	  gimplify_omp_ctxp = c->outer_context;
+	  delete_omp_context (c);
+	}
+    }
+
   /* The body must contain exactly one statement, a GIMPLE_BIND.  If this is
      not the case, wrap everything in a GIMPLE_BIND to make it so.  */
   if (gimple_code (outer_stmt) == GIMPLE_BIND
diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def
index fc87a3f..0365bc4 100644
--- a/gcc/omp-builtins.def
+++ b/gcc/omp-builtins.def
@@ -357,3 +357,5 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TARGET_ENTER_EXIT_DATA,
 		  BT_FN_VOID_INT_SIZE_PTR_PTR_PTR_UINT_PTR, ATTR_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS, "GOMP_teams",
 		  BT_FN_VOID_UINT_UINT, ATTR_NOTHROW_LIST)
+DEF_GOACC_BUILTIN (BUILT_IN_GOACC_DECLARE, "GOACC_declare",
+		   BT_FN_VOID_INT_SIZE_PTR_PTR_PTR, ATTR_NOTHROW_LIST)
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 5ffb276..0119e44 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -12344,6 +12344,7 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -12587,6 +12588,9 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       start_ix = BUILT_IN_GOACC_ENTER_EXIT_DATA;
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      start_ix = BUILT_IN_GOACC_DECLARE;
+      break;
     default:
       gcc_unreachable ();
     }
@@ -12709,6 +12713,7 @@ expand_omp_target (struct omp_region *region)
   switch (start_ix)
     {
     case BUILT_IN_GOACC_DATA_START:
+    case BUILT_IN_GOACC_DECLARE:
     case BUILT_IN_GOMP_TARGET_DATA:
       break;
     case BUILT_IN_GOMP_TARGET:
@@ -13023,6 +13028,7 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent,
 		case GF_OMP_TARGET_KIND_EXIT_DATA:
 		case GF_OMP_TARGET_KIND_OACC_UPDATE:
 		case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+		case GF_OMP_TARGET_KIND_OACC_DECLARE:
 		  /* ..., other than for those stand-alone directives...  */
 		  region = NULL;
 		  break;
@@ -14806,6 +14812,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -14876,6 +14883,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	  case GOMP_MAP_FORCE_TOFROM:
 	  case GOMP_MAP_FORCE_PRESENT:
 	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_LINK:
 	    gcc_assert (is_gimple_omp_oacc (stmt));
 	    break;
 	  default:
@@ -16542,6 +16551,7 @@ make_gimple_omp_edges (basic_block bb, struct omp_region **region,
 	case GF_OMP_TARGET_KIND_EXIT_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  cur_region = cur_region->outer;
 	  break;
 	default:
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
new file mode 100644
index 0000000..b036c63
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -0,0 +1,83 @@
+/* Test valid uses of declare directive.  */
+/* { dg-do compile } */
+
+int v0;
+#pragma acc declare create(v0)
+
+int v1;
+#pragma acc declare copyin(v1)
+
+int *v2;
+#pragma acc declare deviceptr(v2)
+
+int v3;
+#pragma acc declare device_resident(v3)
+
+int v4;
+#pragma acc declare link(v4)
+
+int v5, v6, v7, v8;
+#pragma acc declare create(v5, v6) copyin(v7, v8)
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare create(va0)
+
+  int va1;
+#pragma acc declare copyin(va1)
+
+  int *va2;
+#pragma acc declare deviceptr(va2)
+
+  int va3;
+#pragma acc declare device_resident(va3)
+
+  extern int ve0;
+#pragma acc declare create(ve0)
+
+  extern int ve1;
+#pragma acc declare copyin(ve1)
+
+  extern int *ve2;
+#pragma acc declare deviceptr(ve2)
+
+  extern int ve3;
+#pragma acc declare device_resident(ve3)
+
+  extern int ve4;
+#pragma acc declare link(ve4)
+
+  int va5;
+#pragma acc declare copy(va5)
+
+  int va6;
+#pragma acc declare copyout(va6)
+
+  int va7;
+#pragma acc declare present(va7)
+
+  int va8;
+#pragma acc declare present_or_copy(va8)
+
+  int va9;
+#pragma acc declare present_or_copyin(va9)
+
+  int va10;
+#pragma acc declare present_or_copyout(va10)
+
+  int va11;
+#pragma acc declare present_or_create(va11)
+
+ a:
+  {
+    int va0;
+#pragma acc declare create(va0)
+    if (v1)
+      goto a;
+    else
+      goto b;
+  }
+ b:;
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
new file mode 100644
index 0000000..7979f0c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -0,0 +1,68 @@
+/* Test invalid uses of declare directive.  */
+/* { dg-do compile } */
+
+#pragma acc declare /* { dg-error "no valid clauses" } */
+
+#pragma acc declare create(undeclared) /* { dg-error "undeclared" } */
+/* { dg-error "no valid clauses" "second error" { target *-*-* } 6 } */
+
+int v0[10];
+#pragma acc declare create(v0[1:3]) /* { dg-error "subarray" } */
+
+int v1;
+#pragma acc declare create(v1, v1) /* { dg-error "more than once" } */
+
+int v2;
+#pragma acc declare create(v2) /* { dg-message "previous directive" } */
+#pragma acc declare copyin(v2) /* { dg-error "more than once" } */
+
+int v3;
+#pragma acc declare copy(v3) /* { dg-error "at file scope" } */
+
+int v4;
+#pragma acc declare copyout(v4) /* { dg-error "at file scope" } */
+
+int v5;
+#pragma acc declare present(v5) /* { dg-error "at file scope" } */
+
+int v6;
+#pragma acc declare present_or_copy(v6) /* { dg-error "at file scope" } */
+
+int v7;
+#pragma acc declare present_or_copyin(v7) /* { dg-error "at file scope" } */
+
+int v8;
+#pragma acc declare present_or_copyout(v8) /* { dg-error "at file scope" } */
+
+int v9;
+#pragma acc declare present_or_create(v9) /* { dg-error "at file scope" } */
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare link(va0) /* { dg-error "global variable" } */
+
+  extern int ve0;
+#pragma acc declare copy(ve0) /* { dg-error "invalid use of" } */
+
+  extern int ve1;
+#pragma acc declare copyout(ve1) /* { dg-error "invalid use of" } */
+
+  extern int ve2;
+#pragma acc declare present(ve2) /* { dg-error "invalid use of" } */
+
+  extern int ve3;
+#pragma acc declare present_or_copy(ve3) /* { dg-error "invalid use of" } */
+
+  extern int ve4;
+#pragma acc declare present_or_copyin(ve4) /* { dg-error "invalid use of" } */
+
+  extern int ve5;
+#pragma acc declare present_or_copyout(ve5) /* { dg-error "invalid use of" } */
+
+  extern int ve6;
+#pragma acc declare present_or_create(ve6) /* { dg-error "invalid use of" } */
+
+#pragma acc declare present (v9) /* { dg-error "invalid use of" } */
+}
diff --git a/gcc/tree.def b/gcc/tree.def
index 41064a8..6e30a1a 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -1186,8 +1186,9 @@ DEFTREECODE (OMP_TASKGROUP, "omp_taskgroup", tcc_statement, 1)
 DEFTREECODE (OACC_CACHE, "oacc_cache", tcc_statement, 1)
 
 /* OpenACC - #pragma acc declare [clause1 ... clauseN]
-   Operand 0: OACC_DECLARE_CLAUSES: List of clauses.  */
-DEFTREECODE (OACC_DECLARE, "oacc_declare", tcc_statement, 1)
+   Operand 0: OACC_DECLARE_CLAUSES: List of clauses.
+   Operand 1: OACC_DECLARE_RETURN_CLAUSES: List of clauses for returns.  */
+DEFTREECODE (OACC_DECLARE, "oacc_declare", tcc_statement, 2)
 
 /* OpenACC - #pragma acc enter data [clause1 ... clauseN]
    Operand 0: OACC_ENTER_DATA_CLAUSES: List of clauses.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index 6768b3b..a84d11a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1232,6 +1232,8 @@ extern void protected_set_expr_location (tree, location_t);
 
 #define OACC_DECLARE_CLAUSES(NODE) \
   TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 0)
+#define OACC_DECLARE_RETURN_CLAUSES(NODE) \
+  TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 1)
 
 #define OACC_ENTER_DATA_CLAUSES(NODE) \
   TREE_OPERAND (OACC_ENTER_DATA_CHECK (NODE), 0)
diff --git a/gcc/varpool.c b/gcc/varpool.c
index 478f365..a8cdb1c 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -137,18 +137,9 @@ varpool_node::create_empty (void)
   return node;
 }   
 
-/* Return varpool node assigned to DECL.  Create new one when needed.  */
-varpool_node *
-varpool_node::get_create (tree decl)
+static void
+make_offloadable (varpool_node *node, tree decl)
 {
-  varpool_node *node = varpool_node::get (decl);
-  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
-  if (node)
-    return node;
-
-  node = varpool_node::create_empty ();
-  node->decl = decl;
-
   if ((flag_openacc || flag_openmp) && !DECL_EXTERNAL (decl)
       && lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
     {
@@ -160,6 +151,25 @@ varpool_node::get_create (tree decl)
       node->force_output = 1;
 #endif
     }
+}
+
+/* Return varpool node assigned to DECL.  Create new one when needed.  */
+varpool_node *
+varpool_node::get_create (tree decl)
+{
+  varpool_node *node = varpool_node::get (decl);
+  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
+  if (node)
+    {
+      if (!node->offloadable)
+	make_offloadable (node, decl);
+      return node;
+    }
+
+  node = varpool_node::create_empty ();
+  node->decl = decl;
+
+  make_offloadable (node, decl);
 
   node->register_symbol ();
   return node;
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 7671dd7..dffd631 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -72,6 +72,11 @@ enum gomp_map_kind
        POINTER_SIZE_UNITS.  */
     GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
     /* Do not map, copy bits for firstprivate instead.  */
+    /* OpenACC device_resident.  */
+    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
+    /* OpenACC link.  */
+    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
+    /* Allocate.  */
     GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
     /* Similarly, but store the value in the pointer rather than
        pointed by the pointer.  */
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 39faba9..d16710f 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -392,6 +392,7 @@ GOACC_2.0 {
 
 GOACC_2.0.1 {
   global:
+	GOACC_declare;
 	GOACC_parallel_keyed;
 } GOACC_2.0;
 
diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index 525846b..9de9e55 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -501,3 +501,62 @@ GOACC_get_thread_num (void)
 {
   return 0;
 }
+
+void
+GOACC_declare (int device, size_t mapnum,
+	       void **hostaddrs, size_t *sizes, unsigned short *kinds)
+{
+  int i;
+
+  for (i = 0; i < mapnum; i++)
+    {
+      unsigned char kind = kinds[i] & 0xff;
+
+      if (kind == GOMP_MAP_POINTER || kind == GOMP_MAP_TO_PSET)
+	continue;
+
+      switch (kind)
+	{
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_DEALLOC:
+	  case GOMP_MAP_FORCE_FROM:
+	  case GOMP_MAP_FORCE_TO:
+	  case GOMP_MAP_POINTER:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	    break;
+
+	  case GOMP_MAP_ALLOC:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      {
+		GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	      }
+	    break;
+
+	  case GOMP_MAP_TO:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    kinds[i] = GOMP_MAP_FORCE_FROM;
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_PRESENT:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      gomp_fatal ("[%p,%zd] is not mapped", hostaddrs[i], sizes[i]);
+	    break;
+
+	  default:
+	    assert (0);
+	    break;
+	}
+    }
+}
diff --git a/libgomp/testsuite/declare-1.c b/libgomp/testsuite/declare-1.c
new file mode 100644
index 0000000..8fbec4d
--- /dev/null
+++ b/libgomp/testsuite/declare-1.c
@@ -0,0 +1,122 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <openacc.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define N 8
+
+void
+subr2 (int *a)
+{
+  int i;
+  int f[N];
+#pragma acc declare copyout (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i] + f[i];
+      }
+  }
+}
+
+void
+subr1 (int *a)
+{
+  int f[N];
+#pragma acc declare copy (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    int i;
+
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i];
+      }
+  }
+}
+
+int b[8];
+#pragma acc declare create (b)
+
+int d[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+#pragma acc declare copyin (d)
+
+int
+main (int argc, char **argv)
+{
+  int a[N];
+  int e[N];
+#pragma acc declare create (e)
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = i + 1;
+
+  if (!acc_is_present (&b, sizeof (b)))
+    abort ();
+
+  if (!acc_is_present (&d, sizeof (d)))
+    abort ();
+
+  if (!acc_is_present (&e, sizeof (e)))
+    abort ();
+
+#pragma acc parallel copyin (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        b[i] = a[i];
+        a[i] = b[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != i + 1)
+	abort ();
+    }
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        e[i] = a[i] + d[i];
+	a[i] = e[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != (i + 1) * 2)
+	abort ();
+    }
+
+  for (i = 0; i < N; i++)
+    {
+      a[i] = 1234;
+    }
+
+  subr1 (&a[0]);
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != 1234 * 2)
+	abort ();
+    }
+
+  subr2 (&a[0]);
+
+  for (i = 0; i < 1; i++)
+    {
+      if (a[i] != 1234 * 6)
+	abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/declare-5.c b/libgomp/testsuite/declare-5.c
new file mode 100644
index 0000000..1e2f6ce
--- /dev/null
+++ b/libgomp/testsuite/declare-5.c
@@ -0,0 +1,13 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+int
+main (int argc, char **argv)
+{
+  int a[8] __attribute__((unused));
+
+  __builtin_printf ("CheCKpOInT\n");
+#pragma acc declare present (a)
+}
+
+/* { dg-output "CheCKpOInT" } */
+/* { dg-shouldfail "" } */

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-06 16:08     ` James Norris
@ 2015-11-06 16:16       ` James Norris
  2015-11-06 19:04       ` Jakub Jelinek
  1 sibling, 0 replies; 51+ messages in thread
From: James Norris @ 2015-11-06 16:16 UTC (permalink / raw)
  To: James Norris, Thomas Schwinge, Jakub Jelinek; +Cc: GCC Patches

On 11/06/2015 10:08 AM, James Norris wrote:
> Thomas,
>
> I've added the issues that you pointed out. Attached
> is the updated patch and ChangeLog.
>
> Thanks for taking the time for your review!

Errr, make that 'addressed the issues that you pointed out."

Opps,
Jim

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-06 16:08     ` James Norris
  2015-11-06 16:16       ` James Norris
@ 2015-11-06 19:04       ` Jakub Jelinek
  2015-11-06 19:23         ` Jakub Jelinek
                           ` (2 more replies)
  1 sibling, 3 replies; 51+ messages in thread
From: Jakub Jelinek @ 2015-11-06 19:04 UTC (permalink / raw)
  To: James Norris; +Cc: Thomas Schwinge, Joseph S. Myers, GCC Patches

On Fri, Nov 06, 2015 at 10:08:26AM -0600, James Norris wrote:
> 2015-10-27  James Norris  <jnorris@codesourcery.com>
> 	    Joseph Myers  <joseph@codesourcery.com>
> 
> 	gcc/
> 	* c-family/c-pragma.c (oacc_pragmas): Add entry for declare directive. 
> 	* c-family/c-pragma.h (enum pragma_kind): Add PRAGMA_OACC_DECLARE.

c-family/, c/, cp/ have all their own ChangeLog files, and the entries
that go in there should not have the path prefixes.

> +  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
> +    {
> +      location_t loc = OMP_CLAUSE_LOCATION (t);
> +      tree decl = OMP_CLAUSE_DECL (t);
> +      tree devres = NULL_TREE;
> +      if (!DECL_P (decl))
> +	{
> +	  error_at (loc, "subarray in %<#pragma acc declare%>");

Is that the OpenACC term that you use everywhere?  In OpenMP
those are array sections.

> +	case GOMP_MAP_LINK:
> +	  if (!global_bindings_p () && !DECL_EXTERNAL (decl))
> +	    {
> +	      error_at (loc,
> +			"%qD must be a global variable in"
> +			"%<#pragma acc declare link%>",
> +			decl);
> +	      error = true;
> +	      continue;

What about TREE_STATIC?

> diff --git a/gcc/gimplify.c b/gcc/gimplify.c
> index fa34858..fbd4a69 100644
> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c
> @@ -157,6 +157,7 @@ struct gimplify_omp_ctx
>    bool target_map_scalars_firstprivate;
>    bool target_map_pointers_as_0len_arrays;
>    bool target_firstprivatize_array_bases;
> +  gomp_target *declare_returns;
>  };

This declare_returns stuff looks broken.
What exactly do you want to achieve?  Why do you record it
in gimplify_omp_ctx, but then only look at it in gimplify_body?
The way you abuse gimplify_omp_ctx for that is just too ugly.
All the gimplification code expects that when you enter some OpenMP/OpenACC
construct, you create new context if it needs one, then process the body
of the construct, then pop it up.  The declare_returns stuff
violates all of this.  Do you want to enqueue all the statements
at the end of the body?  Then just stick it into some gimple_seq
outside of the gimplify_omp_ctx, and process in there.  Or if you
want to process it when leaving some construct, arrange for that.

> @@ -5841,6 +5863,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
>        flags |= GOVD_FIRSTPRIVATE;
>        break;
>      case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
> +      if (is_global_var (decl) && device_resident_p (decl))
> +	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;

I don't think you want to do this except for (selected or all?)
OpenACC contexts.  Say, I don't see why one couldn't e.g. try to mix
OpenMP host parallelization or tasking with OpenACC offloading,
and that affecting in weird way OpenMP semantics.

> +      /* Any clauses that affect the state of a variable prior
> +         to return are saved and dealt with after the body has
> +         been gimplified.  */
> +
> +      gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA,
> +				 OACC_DECLARE);
> +
> +      c = gimplify_omp_ctxp;
> +      gimplify_omp_ctxp = c->outer_context;
> +      delete_omp_context (c);

Why don't you call gimplify_adjust_omp_clauses instead?

> +      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
> +				      clauses);
> +      gimplify_omp_ctxp->declare_returns = stmt;

But the above is the main thing I have trouble with.
What happens if you have multiple #pragma acc declare directives?
Is the stmt from the other ones lost?
I'd expect something like gimple_seq in there instead and pushing
stmts into the sequence.
I don't know what is the semantics of the construct, if it is something
close to say target data in OpenMP or acc data in OpenACC, the addition
of code to unmap the variables is performed using a cleanup, otherwise
how do you handle exceptions, or goto and all other kinds of abnormal
returns from the function.

> @@ -160,6 +151,25 @@ varpool_node::get_create (tree decl)
>        node->force_output = 1;
>  #endif
>      }
> +}
> +
> +/* Return varpool node assigned to DECL.  Create new one when needed.  */
> +varpool_node *
> +varpool_node::get_create (tree decl)
> +{
> +  varpool_node *node = varpool_node::get (decl);
> +  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
> +  if (node)
> +    {
> +      if (!node->offloadable)
> +	make_offloadable (node, decl);

I don't like this change at all, that is significant extra work
every time somebody calls this function.
Just do what we do on the OpenMP side, if you add some "omp declare target"
or similar attribute and the varpool node for it has been already created,
set offloadable in the FE.

	Jakub

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-06 19:04       ` Jakub Jelinek
@ 2015-11-06 19:23         ` Jakub Jelinek
  2015-11-06 20:18           ` James Norris
  2015-11-06 20:16         ` James Norris
  2015-11-08 15:35         ` James Norris
  2 siblings, 1 reply; 51+ messages in thread
From: Jakub Jelinek @ 2015-11-06 19:23 UTC (permalink / raw)
  To: James Norris; +Cc: Thomas Schwinge, Joseph S. Myers, GCC Patches

On Fri, Nov 06, 2015 at 08:03:52PM +0100, Jakub Jelinek wrote:
> What exactly do you want to achieve?  Why do you record it
> in gimplify_omp_ctx, but then only look at it in gimplify_body?
> The way you abuse gimplify_omp_ctx for that is just too ugly.
> All the gimplification code expects that when you enter some OpenMP/OpenACC
> construct, you create new context if it needs one, then process the body
> of the construct, then pop it up.  The declare_returns stuff
> violates all of this.  Do you want to enqueue all the statements
> at the end of the body?  Then just stick it into some gimple_seq
> outside of the gimplify_omp_ctx, and process in there.  Or if you
> want to process it when leaving some construct, arrange for that.

If the unmap operation is supposed to sit where the corresponding variable
goes out of scope, supposedly you would need to find out in which bind
(gimplify_ctx (not omp!)) the variable is declared and arrange for the
statement to be added as a cleanup of that region.
If you queue everything to the end of function, I'm afraid with e.g.
void foo (void)
{
  {
    char buf[2048];
    #acc declare whatever(buf)
    // ...
  }
  {
    char buf2[2048];
    #acc declare whatever(buf2)
    // ...
  }
}
both vars will be assigned overlapping ranges and you might run into
trouble trying to map buf2 while buf is still mapped.

	Jakub

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: OpenACC declare directive updates
  2015-11-04 12:32 ` James Norris
  2015-11-06 13:46   ` James Norris
@ 2015-11-06 19:31   ` Jakub Jelinek
  2015-11-06 19:45     ` James Norris
  1 sibling, 1 reply; 51+ messages in thread
From: Jakub Jelinek @ 2015-11-06 19:31 UTC (permalink / raw)
  To: James Norris; +Cc: GCC Patches

On Wed, Nov 04, 2015 at 06:32:00AM -0600, James Norris wrote:
> +/* Node in the linked list used for storing !$oacc declare constructs.  */
> +
> +typedef struct gfc_oacc_declare
> +{
> +  struct gfc_oacc_declare *next;
> +  bool module_var;
> +  gfc_omp_clauses *clauses;
> +  gfc_omp_clauses *return_clauses;
> +}
> +gfc_oacc_declare;
> +
> +#define gfc_get_oacc_declare() XCNEW (gfc_oacc_declare)
> +
> +
>  /* Node in the linked list used for storing !$omp declare simd constructs.  */
>  
>  typedef struct gfc_omp_declare_simd
> @@ -1644,7 +1668,7 @@ typedef struct gfc_namespace
>    struct gfc_data *data, *old_data;
>  
>    /* !$ACC DECLARE clauses.  */
> -  gfc_omp_clauses *oacc_declare_clauses;
> +  struct gfc_oacc_declare *oacc_declare_clauses;

This should be renamed now that it doesn't hold just clauses,
perhaps just oacc_declare?  Also, no need to use struct keyword.

> @@ -2857,6 +2957,42 @@ oacc_compatible_clauses (gfc_omp_clauses *clauses, int list,
>    return false;
>  }
>  
> +/* Check if a variable appears in multiple clauses.  */
> +
> +static void
> +resolve_omp_duplicate_list (gfc_omp_namelist *clause_list, bool openacc,
> +			    int list)
> +{
> +  gfc_omp_namelist *n;
> +  const char *error_msg = "Symbol %qs present on multiple clauses at %L";
> +
> +  /* OpenACC reduction clauses are compatible with everything.  We only
> +     need to check if a reduction variable is used more than once.  */
> +  if (openacc && list == OMP_LIST_REDUCTION)
> +    {
> +      hash_set<gfc_symbol *> reductions;
> +
> +      for (n = clause_list; n; n = n->next)
> +	{
> +	  if (reductions.contains (n->sym))
> +	    gfc_error (error_msg, n->sym->name, &n->expr->where);
> +	  else
> +	    reductions.add (n->sym);
> +	}
> +
> +      return;
> +    }
> +
> +  /* Ensure that variables are only used in one clause.  */
> +  for (n = clause_list; n; n = n->next)
> +    {
> +      if (n->sym->mark)
> +	gfc_error (error_msg, n->sym->name, &n->expr->where);
> +      else
> +	n->sym->mark = 1;
> +    }
> +}

You are readding a function that has been rejected, OMP_LIST_REDUCTION
is handled differently now.

	Jakub

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: OpenACC declare directive updates
  2015-11-06 19:31   ` Jakub Jelinek
@ 2015-11-06 19:45     ` James Norris
  2015-11-06 19:49       ` Jakub Jelinek
  2015-11-08 15:29       ` OpenACC declare directive updates James Norris
  0 siblings, 2 replies; 51+ messages in thread
From: James Norris @ 2015-11-06 19:45 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

Jakub,


On 11/06/2015 01:31 PM, Jakub Jelinek wrote:
> On Wed, Nov 04, 2015 at 06:32:00AM -0600, James Norris wrote:
>> +/* Node in the linked list used for storing !$oacc declare constructs.  */
>> +
>> +typedef struct gfc_oacc_declare
>> +{
>> +  struct gfc_oacc_declare *next;
>> +  bool module_var;
>> +  gfc_omp_clauses *clauses;
>> +  gfc_omp_clauses *return_clauses;
>> +}
>> +gfc_oacc_declare;
>> +
>> +#define gfc_get_oacc_declare() XCNEW (gfc_oacc_declare)
>> +
>> +
>>   /* Node in the linked list used for storing !$omp declare simd constructs.  */
>>   
>>   typedef struct gfc_omp_declare_simd
>> @@ -1644,7 +1668,7 @@ typedef struct gfc_namespace
>>     struct gfc_data *data, *old_data;
>>   
>>     /* !$ACC DECLARE clauses.  */
>> -  gfc_omp_clauses *oacc_declare_clauses;
>> +  struct gfc_oacc_declare *oacc_declare_clauses;
> This should be renamed now that it doesn't hold just clauses,
> perhaps just oacc_declare?  Also, no need to use struct keyword.

That's fine, I'll fix that.

>
>> @@ -2857,6 +2957,42 @@ oacc_compatible_clauses (gfc_omp_clauses *clauses, int list,
>>     return false;
>>   }
>>   
>> +/* Check if a variable appears in multiple clauses.  */
>> +
>> +static void
>> +resolve_omp_duplicate_list (gfc_omp_namelist *clause_list, bool openacc,
>> +			    int list)
>> +{
>> +  gfc_omp_namelist *n;
>> +  const char *error_msg = "Symbol %qs present on multiple clauses at %L";
>> +
>> +  /* OpenACC reduction clauses are compatible with everything.  We only
>> +     need to check if a reduction variable is used more than once.  */
>> +  if (openacc && list == OMP_LIST_REDUCTION)
>> +    {
>> +      hash_set<gfc_symbol *> reductions;
>> +
>> +      for (n = clause_list; n; n = n->next)
>> +	{
>> +	  if (reductions.contains (n->sym))
>> +	    gfc_error (error_msg, n->sym->name, &n->expr->where);
>> +	  else
>> +	    reductions.add (n->sym);
>> +	}
>> +
>> +      return;
>> +    }
>> +
>> +  /* Ensure that variables are only used in one clause.  */
>> +  for (n = clause_list; n; n = n->next)
>> +    {
>> +      if (n->sym->mark)
>> +	gfc_error (error_msg, n->sym->name, &n->expr->where);
>> +      else
>> +	n->sym->mark = 1;
>> +    }
>> +}
> You are readding a function that has been rejected, OMP_LIST_REDUCTION
> is handled differently now.
>
> 	Jakub

Okay, I'll fix this.

After fixing, OK to commit?

Thank you for taking the time for the review.

Jim



^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: OpenACC declare directive updates
  2015-11-06 19:45     ` James Norris
@ 2015-11-06 19:49       ` Jakub Jelinek
  2015-11-06 20:20         ` James Norris
  2015-11-19 16:22         ` James Norris
  2015-11-08 15:29       ` OpenACC declare directive updates James Norris
  1 sibling, 2 replies; 51+ messages in thread
From: Jakub Jelinek @ 2015-11-06 19:49 UTC (permalink / raw)
  To: James Norris; +Cc: GCC Patches

On Fri, Nov 06, 2015 at 01:45:09PM -0600, James Norris wrote:
> Okay, I'll fix this.
> 
> After fixing, OK to commit?
> 
> Thank you for taking the time for the review.

Well, isn't this patch really dependent on the other one?

Also, wonder about BLOCK stmt in Fortran, that can give you variables that
don't live through the whole function, but only a portion of it even in
Fortran.

	Jakub

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-06 19:04       ` Jakub Jelinek
  2015-11-06 19:23         ` Jakub Jelinek
@ 2015-11-06 20:16         ` James Norris
  2015-11-08 15:35         ` James Norris
  2 siblings, 0 replies; 51+ messages in thread
From: James Norris @ 2015-11-06 20:16 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Thomas Schwinge, GCC Patches

Jakub,

On 11/06/2015 01:03 PM, Jakub Jelinek wrote:
> On Fri, Nov 06, 2015 at 10:08:26AM -0600, James Norris wrote:
>> 2015-10-27  James Norris  <jnorris@codesourcery.com>
>> 	    Joseph Myers  <joseph@codesourcery.com>
>>
>> 	gcc/
>> 	* c-family/c-pragma.c (oacc_pragmas): Add entry for declare directive.
>> 	* c-family/c-pragma.h (enum pragma_kind): Add PRAGMA_OACC_DECLARE.
> c-family/, c/, cp/ have all their own ChangeLog files, and the entries
> that go in there should not have the path prefixes.

Will fix.

>
>> +  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
>> +    {
>> +      location_t loc = OMP_CLAUSE_LOCATION (t);
>> +      tree decl = OMP_CLAUSE_DECL (t);
>> +      tree devres = NULL_TREE;
>> +      if (!DECL_P (decl))
>> +	{
>> +	  error_at (loc, "subarray in %<#pragma acc declare%>");
> Is that the OpenACC term that you use everywhere?  In OpenMP
> those are array sections.

I can change the term so that both OpenACC and OpenMP
use the same terminology.

>
>> +	case GOMP_MAP_LINK:
>> +	  if (!global_bindings_p () && !DECL_EXTERNAL (decl))
>> +	    {
>> +	      error_at (loc,
>> +			"%qD must be a global variable in"
>> +			"%<#pragma acc declare link%>",
>> +			decl);
>> +	      error = true;
>> +	      continue;
> What about TREE_STATIC?

Will fix.

>
>> diff --git a/gcc/gimplify.c b/gcc/gimplify.c
>> index fa34858..fbd4a69 100644
>> --- a/gcc/gimplify.c
>> +++ b/gcc/gimplify.c
>> @@ -157,6 +157,7 @@ struct gimplify_omp_ctx
>>     bool target_map_scalars_firstprivate;
>>     bool target_map_pointers_as_0len_arrays;
>>     bool target_firstprivatize_array_bases;
>> +  gomp_target *declare_returns;
>>   };
> This declare_returns stuff looks broken.
> What exactly do you want to achieve?  Why do you record it
> in gimplify_omp_ctx, but then only look at it in gimplify_body?
> The way you abuse gimplify_omp_ctx for that is just too ugly.
> All the gimplification code expects that when you enter some OpenMP/OpenACC
> construct, you create new context if it needs one, then process the body
> of the construct, then pop it up.  The declare_returns stuff
> violates all of this.  Do you want to enqueue all the statements
> at the end of the body?  Then just stick it into some gimple_seq
> outside of the gimplify_omp_ctx, and process in there.  Or if you
> want to process it when leaving some construct, arrange for that.

For example:

void func (int *a)
{
    int b[10];
    #pragma acc declare copyout (b)

     .
     .
     .
}

What I was trying to do was for a copyout to be done at the
time the function return'ed. It also may be the case where
a mapping operation must be done at entry and exit to a
function. I think the approach you outline below (target data/
acc data) is the approach to use.

>> @@ -5841,6 +5863,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
>>         flags |= GOVD_FIRSTPRIVATE;
>>         break;
>>       case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
>> +      if (is_global_var (decl) && device_resident_p (decl))
>> +	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
> I don't think you want to do this except for (selected or all?)
> OpenACC contexts.  Say, I don't see why one couldn't e.g. try to mix
> OpenMP host parallelization or tasking with OpenACC offloading,
> and that affecting in weird way OpenMP semantics.

Will fix.

>
>> +      /* Any clauses that affect the state of a variable prior
>> +         to return are saved and dealt with after the body has
>> +         been gimplified.  */
>> +
>> +      gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA,
>> +				 OACC_DECLARE);
>> +
>> +      c = gimplify_omp_ctxp;
>> +      gimplify_omp_ctxp = c->outer_context;
>> +      delete_omp_context (c);
> Why don't you call gimplify_adjust_omp_clauses instead?

What I was attempting to do was to create to sets of clauses.
One used for mapping operations at entry to a function and
the other at exit. Obviously, I've made a mess of things with
this approach.

>
>> +      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
>> +				      clauses);
>> +      gimplify_omp_ctxp->declare_returns = stmt;
> But the above is the main thing I have trouble with.
> What happens if you have multiple #pragma acc declare directives?
> Is the stmt from the other ones lost?
> I'd expect something like gimple_seq in there instead and pushing
> stmts into the sequence.
> I don't know what is the semantics of the construct, if it is something
> close to say target data in OpenMP or acc data in OpenACC, the addition
> of code to unmap the variables is performed using a cleanup, otherwise
> how do you handle exceptions, or goto and all other kinds of abnormal
> returns from the function.

I think the approach you've outlined is the way to go.

>
>> @@ -160,6 +151,25 @@ varpool_node::get_create (tree decl)
>>         node->force_output = 1;
>>   #endif
>>       }
>> +}
>> +
>> +/* Return varpool node assigned to DECL.  Create new one when needed.  */
>> +varpool_node *
>> +varpool_node::get_create (tree decl)
>> +{
>> +  varpool_node *node = varpool_node::get (decl);
>> +  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
>> +  if (node)
>> +    {
>> +      if (!node->offloadable)
>> +	make_offloadable (node, decl);
> I don't like this change at all, that is significant extra work
> every time somebody calls this function.
> Just do what we do on the OpenMP side, if you add some "omp declare target"
> or similar attribute and the varpool node for it has been already created,
> set offloadable in the FE.

Yes setting node->offloadable = 1 is much easier.

>
> 	Jakub

Thank you for taking time for the review.

Will resubmit patch for review after corrections.

Thanks!
Jim

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-06 19:23         ` Jakub Jelinek
@ 2015-11-06 20:18           ` James Norris
  2015-11-06 20:28             ` Jakub Jelinek
  0 siblings, 1 reply; 51+ messages in thread
From: James Norris @ 2015-11-06 20:18 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Thomas Schwinge, GCC Patches

Jakub,

On 11/06/2015 01:22 PM, Jakub Jelinek wrote:
> On Fri, Nov 06, 2015 at 08:03:52PM +0100, Jakub Jelinek wrote:
>> What exactly do you want to achieve?  Why do you record it
>> in gimplify_omp_ctx, but then only look at it in gimplify_body?
>> The way you abuse gimplify_omp_ctx for that is just too ugly.
>> All the gimplification code expects that when you enter some OpenMP/OpenACC
>> construct, you create new context if it needs one, then process the body
>> of the construct, then pop it up.  The declare_returns stuff
>> violates all of this.  Do you want to enqueue all the statements
>> at the end of the body?  Then just stick it into some gimple_seq
>> outside of the gimplify_omp_ctx, and process in there.  Or if you
>> want to process it when leaving some construct, arrange for that.
> If the unmap operation is supposed to sit where the corresponding variable
> goes out of scope, supposedly you would need to find out in which bind
> (gimplify_ctx (not omp!)) the variable is declared and arrange for the
> statement to be added as a cleanup of that region.
> If you queue everything to the end of function, I'm afraid with e.g.
> void foo (void)
> {
>    {
>      char buf[2048];
>      #acc declare whatever(buf)
>      // ...
>    }
>    {
>      char buf2[2048];
>      #acc declare whatever(buf2)
>      // ...
>    }
> }
> both vars will be assigned overlapping ranges and you might run into
> trouble trying to map buf2 while buf is still mapped.
>
> 	Jakub

Good point.

Thank you again,
Jim

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: OpenACC declare directive updates
  2015-11-06 19:49       ` Jakub Jelinek
@ 2015-11-06 20:20         ` James Norris
  2015-11-19 16:22         ` James Norris
  1 sibling, 0 replies; 51+ messages in thread
From: James Norris @ 2015-11-06 20:20 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

Jakub,

On 11/06/2015 01:49 PM, Jakub Jelinek wrote:
> On Fri, Nov 06, 2015 at 01:45:09PM -0600, James Norris wrote:
>> Okay, I'll fix this.
>>
>> After fixing, OK to commit?
>>
>> Thank you for taking the time for the review.
> Well, isn't this patch really dependent on the other one?

Yes. Should I combine the patches into a single patch?

>
> Also, wonder about BLOCK stmt in Fortran, that can give you variables that
> don't live through the whole function, but only a portion of it even in
> Fortran.

Let me scratch my head on that one.
>
> 	Jakub

Thanks again,
Jim

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-06 20:18           ` James Norris
@ 2015-11-06 20:28             ` Jakub Jelinek
  2015-11-06 20:35               ` James Norris
  0 siblings, 1 reply; 51+ messages in thread
From: Jakub Jelinek @ 2015-11-06 20:28 UTC (permalink / raw)
  To: James Norris; +Cc: Thomas Schwinge, GCC Patches

On Fri, Nov 06, 2015 at 02:18:11PM -0600, James Norris wrote:
> On 11/06/2015 01:22 PM, Jakub Jelinek wrote:
> >On Fri, Nov 06, 2015 at 08:03:52PM +0100, Jakub Jelinek wrote:
> >>What exactly do you want to achieve?  Why do you record it
> >>in gimplify_omp_ctx, but then only look at it in gimplify_body?
> >>The way you abuse gimplify_omp_ctx for that is just too ugly.
> >>All the gimplification code expects that when you enter some OpenMP/OpenACC
> >>construct, you create new context if it needs one, then process the body
> >>of the construct, then pop it up.  The declare_returns stuff
> >>violates all of this.  Do you want to enqueue all the statements
> >>at the end of the body?  Then just stick it into some gimple_seq
> >>outside of the gimplify_omp_ctx, and process in there.  Or if you
> >>want to process it when leaving some construct, arrange for that.
> >If the unmap operation is supposed to sit where the corresponding variable
> >goes out of scope, supposedly you would need to find out in which bind
> >(gimplify_ctx (not omp!)) the variable is declared and arrange for the
> >statement to be added as a cleanup of that region.
> >If you queue everything to the end of function, I'm afraid with e.g.
> >void foo (void)
> >{
> >   {
> >     char buf[2048];
> >     #acc declare whatever(buf)
> >     // ...
> >   }
> >   {
> >     char buf2[2048];
> >     #acc declare whatever(buf2)
> >     // ...
> >   }
> >}
> >both vars will be assigned overlapping ranges and you might run into
> >trouble trying to map buf2 while buf is still mapped.
> >
> >	Jakub
> 
> Good point.

Perhaps what would work is stick the "exit" clauses you need for automatic
vars in the function inside of some pointer map / hash table / whatever,
and then in gimplify_bind_expr in the
  /* Add clobbers for all variables that go out of scope.  */
if if flag_openacc && the pointer_map /hash table has any entries look
up each variable in there and collect the clauses from those vars that go
out of scope, after the loop if any were collected construct the statement
you need prepend it to cleanup (so that it works before restoring VLA memory
and before the clobber stmts).

	Jakub

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-06 20:28             ` Jakub Jelinek
@ 2015-11-06 20:35               ` James Norris
  0 siblings, 0 replies; 51+ messages in thread
From: James Norris @ 2015-11-06 20:35 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Thomas Schwinge, GCC Patches

On 11/06/2015 02:28 PM, Jakub Jelinek wrote:
> On Fri, Nov 06, 2015 at 02:18:11PM -0600, James Norris wrote:
>> On 11/06/2015 01:22 PM, Jakub Jelinek wrote:
>>> On Fri, Nov 06, 2015 at 08:03:52PM +0100, Jakub Jelinek wrote:
>>>> What exactly do you want to achieve?  Why do you record it
>>>> in gimplify_omp_ctx, but then only look at it in gimplify_body?
>>>> The way you abuse gimplify_omp_ctx for that is just too ugly.
>>>> All the gimplification code expects that when you enter some OpenMP/OpenACC
>>>> construct, you create new context if it needs one, then process the body
>>>> of the construct, then pop it up.  The declare_returns stuff
>>>> violates all of this.  Do you want to enqueue all the statements
>>>> at the end of the body?  Then just stick it into some gimple_seq
>>>> outside of the gimplify_omp_ctx, and process in there.  Or if you
>>>> want to process it when leaving some construct, arrange for that.
>>> If the unmap operation is supposed to sit where the corresponding variable
>>> goes out of scope, supposedly you would need to find out in which bind
>>> (gimplify_ctx (not omp!)) the variable is declared and arrange for the
>>> statement to be added as a cleanup of that region.
>>> If you queue everything to the end of function, I'm afraid with e.g.
>>> void foo (void)
>>> {
>>>    {
>>>      char buf[2048];
>>>      #acc declare whatever(buf)
>>>      // ...
>>>    }
>>>    {
>>>      char buf2[2048];
>>>      #acc declare whatever(buf2)
>>>      // ...
>>>    }
>>> }
>>> both vars will be assigned overlapping ranges and you might run into
>>> trouble trying to map buf2 while buf is still mapped.
>>>
>>> 	Jakub
>> Good point.
> Perhaps what would work is stick the "exit" clauses you need for automatic
> vars in the function inside of some pointer map / hash table / whatever,
> and then in gimplify_bind_expr in the
>    /* Add clobbers for all variables that go out of scope.  */
> if if flag_openacc && the pointer_map /hash table has any entries look
> up each variable in there and collect the clauses from those vars that go
> out of scope, after the loop if any were collected construct the statement
> you need prepend it to cleanup (so that it works before restoring VLA memory
> and before the clobber stmts).

I like that. Thank you again.

>
> 	Jakub

Jim


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: OpenACC declare directive updates
  2015-11-06 19:45     ` James Norris
  2015-11-06 19:49       ` Jakub Jelinek
@ 2015-11-08 15:29       ` James Norris
  2015-11-09  2:30         ` Cesar Philippidis
  1 sibling, 1 reply; 51+ messages in thread
From: James Norris @ 2015-11-08 15:29 UTC (permalink / raw)
  To: James Norris, Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 554 bytes --]

Jakub,

The attached patch and ChangeLog reflect the updates from your
review: https://gcc.gnu.org/ml/gcc-patches/2015-11/msg00714.html.
All of the issues pointed out, have been address.

With the changes made in this patch I think I'm handling the
situation that you pointed out here correctly:

On Fri, Nov 06, 2015 at 01:45:09PM -0600, James Norris wrote:

Also, wonder about BLOCK stmt in Fortran, that can give you variables that
don't live through the whole function, but only a portion of it even in
Fortran.

OK to commit to trunk?

Thanks!
Jim


[-- Attachment #2: ChangeLog --]
[-- Type: text/plain, Size: 2642 bytes --]

2015-XX-XX  James Norris  <jnorris@codesourcery.com>
	    Cesar Philippidis  <cesar@codesourcery.com>

	gcc/fortran/
	* dump-parse-tree.c (show_namespace): Handle declares.
	* f95-lang.c (gfc_attribute_table): New entry.
	* gfortran.h (struct symbol_attribute): New fields.
	(enum gfc_omp_map_map): Add OMP_MAP_DEVICE_RESIDENT and OMP_MAP_LINK.
	(OMP_LIST_LINK): New enum.
	(struct gfc_oacc_declare): New structure.
	(gfc_get_oacc_declare): New definition.
	(struct gfc_namespace): Change type.
	(enum gfc_exec_op): Add EXEC_OACC_DECLARE.
	(struct gfc_code): New field.
	* module.c (enum ab_attribute): Add AB_OACC_DECLARE_CREATE,
	AB_OACC_DECLARE_COPYIN, AB_OACC_DECLARE_DEVICEPTR,
	AB_OACC_DECLARE_DEVICE_RESIDENT, AB_OACC_DECLARE_LINK
	(attr_bits): Add new initializers.
	(mio_symbol_attribute): Handle new atributes.
	* openmp.c (gfc_free_oacc_declare_clauses): New function.
	(OMP_CLAUSE_LINK): New definition.
	(gfc_match_omp_clauses): Handle OMP_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSES): Add OMP_CLAUSE_LINK
	(gfc_match_oacc_declare): Add checking and module handling.
	(gfc_resolve_oacc_declare): Use duplicate detection.
	* parse.c (case_decl): Add ST_OACC_DECLARE.
	(parse_spec): Remove handling.
	(parse_progunit): Remove handling.
	* parse.h (struct gfc_state_data): Change type.
	* resolve.c (gfc_resolve_blocks): Handle EXEC_OACC_DECLARE.
	* st.c (gfc_free_statement): Handle EXEC_OACC_DECLARE.
	* symbol.c (check_conflict): Add conflict checks.
	(gfc_add_oacc_declare_create, gfc_add_oacc_declare_copyin, 
	gfc_add_oacc_declare_deviceptr, gfc_add_oacc_declare_device_resident):
	New functions.
	(gfc_copy_attr): Handle new symbols.
	* trans-decl.c (add_attributes_to_decl): Create identifier.
	(struct oacc_return): New structure.
	(find_oacc_return, add_clause, find_module_oacc_declare_clauses,
	finish_oacc_declare): New functions.
	(gfc_generate_function_code): Replace with call.
	* trans-openmp.c (gfc_trans_omp_clauses): Add conditional.
	(gfc_trans_oacc_declare): Reimplement.
	(gfc_trans_oacc_directive): Handle EXEC_OACC_DECLARE.
	* trans-stmt.c (gfc_trans_block_construct): Replace with call.
	* trans-stmt.h (gfc_trans_oacc_declare): Remove argument.
	* trans.c (trans_code): Handle EXEC_OACC_DECLARE.

	gcc/testsuite
	* gfortran.dg/goacc/declare-1.f95: Update test.
	* gfortran.dg/goacc/declare-2.f95: New test.

	libgomp/
	* testsuite/libgomp.oacc-fortran/declare-1.f90: New test.
	* testsuite/libgomp.oacc-fortran/declare-2.f90: Likewise.
	* testsuite/libgomp.oacc-fortran/declare-3.f90: Likewise.
	* testsuite/libgomp.oacc-fortran/declare-4.f90: Likewise.
	* testsuite/libgomp.oacc-fortran/declare-5.f90: Likewise.

[-- Attachment #3: declare.patch --]
[-- Type: text/x-patch, Size: 42169 bytes --]

diff --git a/gcc/fortran/dump-parse-tree.c b/gcc/fortran/dump-parse-tree.c
index 83ecbaa..48476af 100644
--- a/gcc/fortran/dump-parse-tree.c
+++ b/gcc/fortran/dump-parse-tree.c
@@ -2570,12 +2570,16 @@ show_namespace (gfc_namespace *ns)
   for (eq = ns->equiv; eq; eq = eq->next)
     show_equiv (eq);
 
-  if (ns->oacc_declare_clauses)
+  if (ns->oacc_declare)
     {
+      struct gfc_oacc_declare *decl;
       /* Dump !$ACC DECLARE clauses.  */
-      show_indent ();
-      fprintf (dumpfile, "!$ACC DECLARE");
-      show_omp_clauses (ns->oacc_declare_clauses);
+      for (decl = ns->oacc_declare; decl; decl = decl->next)
+	{
+	  show_indent ();
+	  fprintf (dumpfile, "!$ACC DECLARE");
+	  show_omp_clauses (decl->clauses);
+	}
     }
 
   fputc ('\n', dumpfile);
diff --git a/gcc/fortran/f95-lang.c b/gcc/fortran/f95-lang.c
index 2e91470..a8458b0 100644
--- a/gcc/fortran/f95-lang.c
+++ b/gcc/fortran/f95-lang.c
@@ -99,6 +99,8 @@ static const struct attribute_spec gfc_attribute_table[] =
        affects_type_identity } */
   { "omp declare target", 0, 0, true,  false, false,
     gfc_handle_omp_declare_target_attribute, false },
+  { "oacc declare", 0, 0, true,  false, false,
+    gfc_handle_omp_declare_target_attribute, false },
   { NULL,		  0, 0, false, false, false, NULL, false }
 };
 
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index e13b4d4..3965b08 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -841,6 +841,13 @@ typedef struct
   /* Mentioned in OMP DECLARE TARGET.  */
   unsigned omp_declare_target:1;
 
+  /* Mentioned in OACC DECLARE.  */
+  unsigned oacc_declare_create:1;
+  unsigned oacc_declare_copyin:1;
+  unsigned oacc_declare_deviceptr:1;
+  unsigned oacc_declare_device_resident:1;
+  unsigned oacc_declare_link:1;
+
   /* Attributes set by compiler extensions (!GCC$ ATTRIBUTES).  */
   unsigned ext_attr:EXT_ATTR_NUM;
 
@@ -1106,7 +1113,9 @@ enum gfc_omp_map_op
   OMP_MAP_FORCE_FROM,
   OMP_MAP_FORCE_TOFROM,
   OMP_MAP_FORCE_PRESENT,
-  OMP_MAP_FORCE_DEVICEPTR
+  OMP_MAP_FORCE_DEVICEPTR,
+  OMP_MAP_DEVICE_RESIDENT,
+  OMP_MAP_LINK
 };
 
 /* For use in OpenMP clauses in case we need extra information
@@ -1148,6 +1157,7 @@ enum
   OMP_LIST_FROM,
   OMP_LIST_REDUCTION,
   OMP_LIST_DEVICE_RESIDENT,
+  OMP_LIST_LINK,
   OMP_LIST_USE_DEVICE,
   OMP_LIST_CACHE,
   OMP_LIST_NUM
@@ -1234,6 +1244,19 @@ gfc_omp_clauses;
 #define gfc_get_omp_clauses() XCNEW (gfc_omp_clauses)
 
 
+/* Node in the linked list used for storing !$oacc declare constructs.  */
+
+typedef struct gfc_oacc_declare
+{
+  struct gfc_oacc_declare *next;
+  bool module_var;
+  gfc_omp_clauses *clauses;
+}
+gfc_oacc_declare;
+
+#define gfc_get_oacc_declare() XCNEW (gfc_oacc_declare)
+
+
 /* Node in the linked list used for storing !$omp declare simd constructs.  */
 
 typedef struct gfc_omp_declare_simd
@@ -1645,8 +1668,8 @@ typedef struct gfc_namespace
      this namespace.  */
   struct gfc_data *data, *old_data;
 
-  /* !$ACC DECLARE clauses.  */
-  gfc_omp_clauses *oacc_declare_clauses;
+  /* !$ACC DECLARE.  */
+  gfc_oacc_declare *oacc_declare;
 
   gfc_charlen *cl_list, *old_cl_list;
 
@@ -2324,6 +2347,7 @@ enum gfc_exec_op
   EXEC_OACC_PARALLEL, EXEC_OACC_KERNELS, EXEC_OACC_DATA, EXEC_OACC_HOST_DATA,
   EXEC_OACC_LOOP, EXEC_OACC_UPDATE, EXEC_OACC_WAIT, EXEC_OACC_CACHE,
   EXEC_OACC_ENTER_DATA, EXEC_OACC_EXIT_DATA, EXEC_OACC_ATOMIC,
+  EXEC_OACC_DECLARE,
   EXEC_OMP_CRITICAL, EXEC_OMP_DO, EXEC_OMP_FLUSH, EXEC_OMP_MASTER,
   EXEC_OMP_ORDERED, EXEC_OMP_PARALLEL, EXEC_OMP_PARALLEL_DO,
   EXEC_OMP_PARALLEL_SECTIONS, EXEC_OMP_PARALLEL_WORKSHARE,
@@ -2405,6 +2429,7 @@ typedef struct gfc_code
     struct gfc_code *which_construct;
     int stop_code;
     gfc_entry_list *entry;
+    gfc_oacc_declare *oacc_declare;
     gfc_omp_clauses *omp_clauses;
     const char *omp_name;
     gfc_omp_namelist *omp_namelist;
@@ -2907,6 +2932,7 @@ gfc_expr *gfc_get_parentheses (gfc_expr *);
 /* openmp.c */
 struct gfc_omp_saved_state { void *ptrs[2]; int ints[1]; };
 void gfc_free_omp_clauses (gfc_omp_clauses *);
+void gfc_free_oacc_declare_clauses (struct gfc_oacc_declare *);
 void gfc_free_omp_declare_simd (gfc_omp_declare_simd *);
 void gfc_free_omp_declare_simd_list (gfc_omp_declare_simd *);
 void gfc_free_omp_udr (gfc_omp_udr *);
@@ -3224,4 +3250,8 @@ gfc_expr *gfc_simplify_ieee_functions (gfc_expr *);
 
 bool gfc_is_reallocatable_lhs (gfc_expr *);
 
+/* trans-decl.c */
+
+void finish_oacc_declare (gfc_namespace *, enum sym_flavor);
+
 #endif /* GCC_GFORTRAN_H  */
diff --git a/gcc/fortran/module.c b/gcc/fortran/module.c
index 91d2bd6..76fb3bc 100644
--- a/gcc/fortran/module.c
+++ b/gcc/fortran/module.c
@@ -1987,7 +1987,9 @@ enum ab_attribute
   AB_IS_CLASS, AB_PROCEDURE, AB_PROC_POINTER, AB_ASYNCHRONOUS, AB_CODIMENSION,
   AB_COARRAY_COMP, AB_VTYPE, AB_VTAB, AB_CONTIGUOUS, AB_CLASS_POINTER,
   AB_IMPLICIT_PURE, AB_ARTIFICIAL, AB_UNLIMITED_POLY, AB_OMP_DECLARE_TARGET,
-  AB_ARRAY_OUTER_DEPENDENCY, AB_MODULE_PROCEDURE
+  AB_ARRAY_OUTER_DEPENDENCY, AB_MODULE_PROCEDURE, AB_OACC_DECLARE_CREATE,
+  AB_OACC_DECLARE_COPYIN, AB_OACC_DECLARE_DEVICEPTR,
+  AB_OACC_DECLARE_DEVICE_RESIDENT, AB_OACC_DECLARE_LINK
 };
 
 static const mstring attr_bits[] =
@@ -2044,6 +2046,11 @@ static const mstring attr_bits[] =
     minit ("OMP_DECLARE_TARGET", AB_OMP_DECLARE_TARGET),
     minit ("ARRAY_OUTER_DEPENDENCY", AB_ARRAY_OUTER_DEPENDENCY),
     minit ("MODULE_PROCEDURE", AB_MODULE_PROCEDURE),
+    minit ("OACC_DECLARE_CREATE", AB_OACC_DECLARE_CREATE),
+    minit ("OACC_DECLARE_COPYIN", AB_OACC_DECLARE_COPYIN),
+    minit ("OACC_DECLARE_DEVICEPTR", AB_OACC_DECLARE_DEVICEPTR),
+    minit ("OACC_DECLARE_DEVICE_RESIDENT", AB_OACC_DECLARE_DEVICE_RESIDENT),
+    minit ("OACC_DECLARE_LINK", AB_OACC_DECLARE_LINK),
     minit (NULL, -1)
 };
 
@@ -2231,6 +2238,16 @@ mio_symbol_attribute (symbol_attribute *attr)
 	MIO_NAME (ab_attribute) (AB_MODULE_PROCEDURE, attr_bits);
 	  no_module_procedures = false;
 	}
+      if (attr->oacc_declare_create)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_CREATE, attr_bits);
+      if (attr->oacc_declare_copyin)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_COPYIN, attr_bits);
+      if (attr->oacc_declare_deviceptr)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_DEVICEPTR, attr_bits);
+      if (attr->oacc_declare_device_resident)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_DEVICE_RESIDENT, attr_bits);
+      if (attr->oacc_declare_link)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_LINK, attr_bits);
 
       mio_rparen ();
 
@@ -2403,6 +2420,21 @@ mio_symbol_attribute (symbol_attribute *attr)
 	    case AB_MODULE_PROCEDURE:
 	      attr->module_procedure =1;
 	      break;
+	    case AB_OACC_DECLARE_CREATE:
+	      attr->oacc_declare_create = 1;
+	      break;
+	    case AB_OACC_DECLARE_COPYIN:
+	      attr->oacc_declare_copyin = 1;
+	      break;
+	    case AB_OACC_DECLARE_DEVICEPTR:
+	      attr->oacc_declare_deviceptr = 1;
+	      break;
+	    case AB_OACC_DECLARE_DEVICE_RESIDENT:
+	      attr->oacc_declare_device_resident = 1;
+	      break;
+	    case AB_OACC_DECLARE_LINK:
+	      attr->oacc_declare_link = 1;
+	      break;
 	    }
 	}
     }
diff --git a/gcc/fortran/openmp.c b/gcc/fortran/openmp.c
index a7c7a19..17a62a4 100644
--- a/gcc/fortran/openmp.c
+++ b/gcc/fortran/openmp.c
@@ -90,6 +90,25 @@ gfc_free_omp_clauses (gfc_omp_clauses *c)
   free (c);
 }
 
+/* Free oacc_declare structures.  */
+
+void
+gfc_free_oacc_declare_clauses (struct gfc_oacc_declare *oc)
+{
+  struct gfc_oacc_declare *decl = oc;
+
+  do
+    {
+      struct gfc_oacc_declare *next;
+
+      next = decl->next;
+      gfc_free_omp_clauses (decl->clauses);
+      free (decl);
+      decl = next;
+    }
+  while (decl);
+}
+
 /* Free expression list. */
 void
 gfc_free_expr_list (gfc_expr_list *list)
@@ -453,6 +472,7 @@ match_oacc_clause_gang (gfc_omp_clauses *cp)
 #define OMP_CLAUSE_DELETE		((uint64_t) 1 << 55)
 #define OMP_CLAUSE_AUTO			((uint64_t) 1 << 56)
 #define OMP_CLAUSE_TILE			((uint64_t) 1 << 57)
+#define OMP_CLAUSE_LINK			((uint64_t) 1 << 58)
 
 /* Helper function for OpenACC and OpenMP clauses involving memory
    mapping.  */
@@ -691,6 +711,12 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, uint64_t mask,
 					  true)
 	     == MATCH_YES)
 	continue;
+      if ((mask & OMP_CLAUSE_LINK)
+	  && gfc_match_omp_variable_list ("link (",
+					  &c->lists[OMP_LIST_LINK],
+					  true)
+	     == MATCH_YES)
+	continue;
       if ((mask & OMP_CLAUSE_OACC_DEVICE)
 	  && gfc_match ("device ( ") == MATCH_YES
 	  && gfc_match_omp_map_clause (&c->lists[OMP_LIST_MAP],
@@ -1176,7 +1202,7 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, uint64_t mask,
    | OMP_CLAUSE_CREATE | OMP_CLAUSE_DEVICEPTR | OMP_CLAUSE_DEVICE_RESIDENT    \
    | OMP_CLAUSE_PRESENT | OMP_CLAUSE_PRESENT_OR_COPY                          \
    | OMP_CLAUSE_PRESENT_OR_COPYIN | OMP_CLAUSE_PRESENT_OR_COPYOUT             \
-   | OMP_CLAUSE_PRESENT_OR_CREATE)
+   | OMP_CLAUSE_PRESENT_OR_CREATE | OMP_CLAUSE_LINK)
 #define OACC_UPDATE_CLAUSES \
   (OMP_CLAUSE_IF | OMP_CLAUSE_ASYNC | OMP_CLAUSE_HOST_SELF \
    | OMP_CLAUSE_OACC_DEVICE | OMP_CLAUSE_WAIT)
@@ -1293,12 +1319,86 @@ match
 gfc_match_oacc_declare (void)
 {
   gfc_omp_clauses *c;
+  gfc_omp_namelist *n;
+  gfc_namespace *ns = gfc_current_ns;
+  gfc_oacc_declare *new_oc;
+  bool module_var = false;
+
   if (gfc_match_omp_clauses (&c, OACC_DECLARE_CLAUSES, false, false, true)
       != MATCH_YES)
     return MATCH_ERROR;
 
-  new_st.ext.omp_clauses = c;
-  new_st.ext.omp_clauses->loc = gfc_current_locus;
+  for (n = c->lists[OMP_LIST_DEVICE_RESIDENT]; n != NULL; n = n->next)
+    n->sym->attr.oacc_declare_device_resident = 1;
+
+  for (n = c->lists[OMP_LIST_LINK]; n != NULL; n = n->next)
+    n->sym->attr.oacc_declare_link = 1;
+
+  for (n = c->lists[OMP_LIST_MAP]; n != NULL; n = n->next)
+    {
+      gfc_symbol *s = n->sym;
+      locus where = gfc_current_locus;
+
+      if (s->ns->proc_name && s->ns->proc_name->attr.proc == PROC_MODULE)
+	{
+	  if (n->u.map_op != OMP_MAP_FORCE_ALLOC
+	      && n->u.map_op != OMP_MAP_FORCE_TO)
+	    {
+	      gfc_error ("Invalid clause in module with $!ACC DECLARE at %L",
+			 &where);
+	      return MATCH_ERROR;
+	    }
+
+	  module_var = true;
+	}
+
+      if (s->attr.in_common)
+	{
+	  gfc_error ("Variable in a common block with $!ACC DECLARE at %L",
+		     &where);
+	  return MATCH_ERROR;
+	}
+
+      if (s->attr.use_assoc)
+	{
+	  gfc_error ("Variable is USE-associated with $!ACC DECLARE at %L",
+		     &where);
+	  return MATCH_ERROR;
+	}
+
+      if ((s->attr.dimension || s->attr.codimension)
+	  && s->attr.dummy && s->as->type != AS_EXPLICIT)
+	{
+	  gfc_error ("Assumed-size dummy array with $!ACC DECLARE at %L",
+		     &where);
+	  return MATCH_ERROR;
+	}
+
+      switch (n->u.map_op)
+	{
+	  case OMP_MAP_FORCE_ALLOC:
+	    s->attr.oacc_declare_create = 1;
+	    break;
+
+	  case OMP_MAP_FORCE_TO:
+	    s->attr.oacc_declare_copyin = 1;
+	    break;
+
+	  case OMP_MAP_FORCE_DEVICEPTR:
+	    s->attr.oacc_declare_deviceptr = 1;
+	    break;
+
+	  default:
+	    break;
+	}
+    }
+
+  new_oc = gfc_get_oacc_declare ();
+  new_oc->next = ns->oacc_declare;
+  new_oc->module_var = module_var;
+  new_oc->clauses = c;
+  ns->oacc_declare = new_oc;
+
   return MATCH_YES;
 }
 
@@ -4613,44 +4713,80 @@ resolve_oacc_loop (gfc_code *code)
   resolve_oacc_nested_loops (code, do_code, collapse, "collapsed");
 }
 
-
 void
 gfc_resolve_oacc_declare (gfc_namespace *ns)
 {
   int list;
   gfc_omp_namelist *n;
   locus loc;
+  gfc_oacc_declare *oc;
 
-  if (ns->oacc_declare_clauses == NULL)
+  if (ns->oacc_declare == NULL)
     return;
 
-  loc = ns->oacc_declare_clauses->loc;
+  loc = gfc_current_locus;
 
-  for (list = OMP_LIST_DEVICE_RESIDENT;
-       list <= OMP_LIST_DEVICE_RESIDENT; list++)
-    for (n = ns->oacc_declare_clauses->lists[list]; n; n = n->next)
-      {
-	n->sym->mark = 0;
-	if (n->sym->attr.flavor == FL_PARAMETER)
-	  gfc_error ("PARAMETER object %qs is not allowed at %L", n->sym->name, &loc);
-      }
+  for (oc = ns->oacc_declare; oc; oc = oc->next)
+    {
+      for (list = OMP_LIST_DEVICE_RESIDENT;
+	   list <= OMP_LIST_DEVICE_RESIDENT; list++)
+	for (n = oc->clauses->lists[list]; n; n = n->next)
+	  {
+	    n->sym->mark = 0;
+	    if (n->sym->attr.flavor == FL_PARAMETER)
+	      gfc_error ("PARAMETER object %qs is not allowed at %L",
+			 n->sym->name, &loc);
+	  }
 
-  for (list = OMP_LIST_DEVICE_RESIDENT;
-       list <= OMP_LIST_DEVICE_RESIDENT; list++)
-    for (n = ns->oacc_declare_clauses->lists[list]; n; n = n->next)
-      {
-	if (n->sym->mark)
-	  gfc_error ("Symbol %qs present on multiple clauses at %L",
-		     n->sym->name, &loc);
-	else
-	  n->sym->mark = 1;
-      }
+      for (list = OMP_LIST_DEVICE_RESIDENT;
+	    list <= OMP_LIST_DEVICE_RESIDENT; list++)
+	for (n = oc->clauses->lists[list]; n; n = n->next)
+	  {
+	    if (n->sym->mark)
+	      gfc_error ("Symbol %qs present on multiple clauses at %L",
+			 n->sym->name, &loc);
+	    else
+	      n->sym->mark = 1;
+	  }
 
-  for (n = ns->oacc_declare_clauses->lists[OMP_LIST_DEVICE_RESIDENT]; n;
-       n = n->next)
-    check_array_not_assumed (n->sym, loc, "DEVICE_RESIDENT");
-}
+      for (n = oc->clauses->lists[OMP_LIST_DEVICE_RESIDENT]; n; n = n->next)
+	check_array_not_assumed (n->sym, loc, "DEVICE_RESIDENT");
+
+      for (n = oc->clauses->lists[OMP_LIST_MAP]; n; n = n->next)
+	{
+	  if (n->expr && n->expr->ref->type == REF_ARRAY)
+	      gfc_error ("Array sections: %qs not allowed in"
+			 " $!ACC DECLARE at %L", n->sym->name, &loc);
+	}
+    }
+
+  for (oc = ns->oacc_declare; oc; oc = oc->next)
+    {
+      for (list = OMP_LIST_LINK; list <= OMP_LIST_LINK; list++)
+	for (n = oc->clauses->lists[list]; n; n = n->next)
+	  n->sym->mark = 0;
+    }
 
+  for (oc = ns->oacc_declare; oc; oc = oc->next)
+    {
+      for (list = OMP_LIST_LINK; list <= OMP_LIST_LINK; list++)
+	for (n = oc->clauses->lists[list]; n; n = n->next)
+	  {
+	    if (n->sym->mark)
+	      gfc_error ("Symbol %qs present on multiple clauses at %L",
+			 n->sym->name, &loc);
+	    else
+	      n->sym->mark = 1;
+	  }
+    }
+
+  for (oc = ns->oacc_declare; oc; oc = oc->next)
+    {
+      for (list = OMP_LIST_LINK; list <= OMP_LIST_LINK; list++)
+	for (n = oc->clauses->lists[list]; n; n = n->next)
+	  n->sym->mark = 0;
+    }
+}
 
 void
 gfc_resolve_oacc_directive (gfc_code *code, gfc_namespace *ns ATTRIBUTE_UNUSED)
diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
index b98dda1..b1c5034 100644
--- a/gcc/fortran/parse.c
+++ b/gcc/fortran/parse.c
@@ -1386,7 +1386,7 @@ next_statement (void)
   case ST_EQUIVALENCE: case ST_NAMELIST: case ST_STATEMENT_FUNCTION: \
   case ST_TYPE: case ST_INTERFACE: case ST_OMP_THREADPRIVATE: \
   case ST_PROCEDURE: case ST_OMP_DECLARE_SIMD: case ST_OMP_DECLARE_REDUCTION: \
-  case ST_OMP_DECLARE_TARGET: case ST_OACC_ROUTINE
+  case ST_OMP_DECLARE_TARGET: case ST_OACC_ROUTINE: case ST_OACC_DECLARE
 
 /* Block end statements.  Errors associated with interchanging these
    are detected in gfc_match_end().  */
@@ -2450,7 +2450,6 @@ verify_st_order (st_state *p, gfc_statement st, bool silent)
     case ST_PUBLIC:
     case ST_PRIVATE:
     case ST_DERIVED_DECL:
-    case ST_OACC_DECLARE:
     case_decl:
       if (p->state >= ORDER_EXEC)
 	goto order;
@@ -3362,19 +3361,6 @@ declSt:
       st = next_statement ();
       goto loop;
 
-    case ST_OACC_DECLARE:
-      if (!verify_st_order(&ss, st, false))
-	{
-	  reject_statement ();
-	  st = next_statement ();
-	  goto loop;
-	}
-      if (gfc_state_stack->ext.oacc_declare_clauses == NULL)
-	gfc_state_stack->ext.oacc_declare_clauses = new_st.ext.omp_clauses;
-      accept_statement (st);
-      st = next_statement ();
-      goto loop;
-
     default:
       break;
     }
@@ -5214,13 +5200,6 @@ contains:
 
 done:
   gfc_current_ns->code = gfc_state_stack->head;
-  if (gfc_state_stack->state == COMP_PROGRAM
-      || gfc_state_stack->state == COMP_MODULE
-      || gfc_state_stack->state == COMP_SUBROUTINE
-      || gfc_state_stack->state == COMP_FUNCTION
-      || gfc_state_stack->state == COMP_BLOCK)
-    gfc_current_ns->oacc_declare_clauses
-      = gfc_state_stack->ext.oacc_declare_clauses;
 }
 
 
diff --git a/gcc/fortran/parse.h b/gcc/fortran/parse.h
index bcd714d..94b2ada 100644
--- a/gcc/fortran/parse.h
+++ b/gcc/fortran/parse.h
@@ -48,7 +48,7 @@ typedef struct gfc_state_data
   union
   {
     gfc_st_label *end_do_label;
-    gfc_omp_clauses *oacc_declare_clauses;
+    gfc_oacc_declare *oacc_declare_clauses;
   }
   ext;
 }
diff --git a/gcc/fortran/resolve.c b/gcc/fortran/resolve.c
index bf2837c..7719201 100644
--- a/gcc/fortran/resolve.c
+++ b/gcc/fortran/resolve.c
@@ -10646,6 +10646,7 @@ start:
 	case EXEC_OACC_ENTER_DATA:
 	case EXEC_OACC_EXIT_DATA:
 	case EXEC_OACC_ATOMIC:
+	case EXEC_OACC_DECLARE:
 	  gfc_resolve_oacc_directive (code, ns);
 	  break;
 
diff --git a/gcc/fortran/st.c b/gcc/fortran/st.c
index 629b51d..d0a11aa 100644
--- a/gcc/fortran/st.c
+++ b/gcc/fortran/st.c
@@ -185,6 +185,11 @@ gfc_free_statement (gfc_code *p)
       gfc_free_forall_iterator (p->ext.forall_iterator);
       break;
 
+    case EXEC_OACC_DECLARE:
+      if (p->ext.oacc_declare)
+	gfc_free_oacc_declare_clauses (p->ext.oacc_declare);
+      break;
+
     case EXEC_OACC_PARALLEL_LOOP:
     case EXEC_OACC_PARALLEL:
     case EXEC_OACC_KERNELS_LOOP:
diff --git a/gcc/fortran/symbol.c b/gcc/fortran/symbol.c
index bd7758b..43fd25d 100644
--- a/gcc/fortran/symbol.c
+++ b/gcc/fortran/symbol.c
@@ -375,6 +375,11 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
     *contiguous = "CONTIGUOUS", *generic = "GENERIC";
   static const char *threadprivate = "THREADPRIVATE";
   static const char *omp_declare_target = "OMP DECLARE TARGET";
+  static const char *oacc_declare_copyin = "OACC DECLARE COPYIN";
+  static const char *oacc_declare_create = "OACC DECLARE CREATE";
+  static const char *oacc_declare_deviceptr = "OACC DECLARE DEVICEPTR";
+  static const char *oacc_declare_device_resident =
+						"OACC DECLARE DEVICE_RESIDENT";
 
   const char *a1, *a2;
   int standard;
@@ -511,6 +516,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (in_equivalence, allocatable);
   conf (in_equivalence, threadprivate);
   conf (in_equivalence, omp_declare_target);
+  conf (in_equivalence, oacc_declare_create);
+  conf (in_equivalence, oacc_declare_copyin);
+  conf (in_equivalence, oacc_declare_deviceptr);
+  conf (in_equivalence, oacc_declare_device_resident);
 
   conf (dummy, result);
   conf (entry, result);
@@ -560,6 +569,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (cray_pointee, in_equivalence);
   conf (cray_pointee, threadprivate);
   conf (cray_pointee, omp_declare_target);
+  conf (cray_pointee, oacc_declare_create);
+  conf (cray_pointee, oacc_declare_copyin);
+  conf (cray_pointee, oacc_declare_deviceptr);
+  conf (cray_pointee, oacc_declare_device_resident);
 
   conf (data, dummy);
   conf (data, function);
@@ -614,6 +627,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (proc_pointer, abstract)
 
   conf (entry, omp_declare_target)
+  conf (entry, oacc_declare_create)
+  conf (entry, oacc_declare_copyin)
+  conf (entry, oacc_declare_deviceptr)
+  conf (entry, oacc_declare_device_resident)
 
   a1 = gfc_code2string (flavors, attr->flavor);
 
@@ -651,6 +668,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
       conf2 (subroutine);
       conf2 (threadprivate);
       conf2 (omp_declare_target);
+      conf2 (oacc_declare_create);
+      conf2 (oacc_declare_copyin);
+      conf2 (oacc_declare_deviceptr);
+      conf2 (oacc_declare_device_resident);
 
       if (attr->access == ACCESS_PUBLIC || attr->access == ACCESS_PRIVATE)
 	{
@@ -733,6 +754,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
       conf2 (threadprivate);
       conf2 (result);
       conf2 (omp_declare_target);
+      conf2 (oacc_declare_create);
+      conf2 (oacc_declare_copyin);
+      conf2 (oacc_declare_deviceptr);
+      conf2 (oacc_declare_device_resident);
 
       if (attr->intent != INTENT_UNKNOWN)
 	{
@@ -1244,6 +1269,62 @@ gfc_add_omp_declare_target (symbol_attribute *attr, const char *name,
 
 
 bool
+gfc_add_oacc_declare_create (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_create)
+    return true;
+
+  attr->oacc_declare_create = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_copyin (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_copyin)
+    return true;
+
+  attr->oacc_declare_copyin = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_deviceptr (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_deviceptr)
+    return true;
+
+  attr->oacc_declare_deviceptr = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_device_resident (symbol_attribute *attr, const char *name, locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_device_resident)
+    return true;
+
+  attr->oacc_declare_device_resident = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
 gfc_add_target (symbol_attribute *attr, locus *where)
 {
 
@@ -1820,6 +1901,18 @@ gfc_copy_attr (symbol_attribute *dest, symbol_attribute *src, locus *where)
   if (src->omp_declare_target
       && !gfc_add_omp_declare_target (dest, NULL, where))
     goto fail;
+  if (src->oacc_declare_create
+      && !gfc_add_oacc_declare_create (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_copyin
+      && !gfc_add_oacc_declare_copyin (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_deviceptr
+      && !gfc_add_oacc_declare_deviceptr (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_device_resident
+      && !gfc_add_oacc_declare_device_resident (dest, NULL, where))
+    goto fail;
   if (src->target && !gfc_add_target (dest, where))
     goto fail;
   if (src->dummy && !gfc_add_dummy (dest, NULL, where))
diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c
index 098a327..545d80f 100644
--- a/gcc/fortran/trans-decl.c
+++ b/gcc/fortran/trans-decl.c
@@ -1307,6 +1307,15 @@ add_attributes_to_decl (symbol_attribute sym_attr, tree list)
     list = tree_cons (get_identifier ("omp declare target"),
 		      NULL_TREE, list);
 
+  if (sym_attr.oacc_declare_create
+      || sym_attr.oacc_declare_copyin
+      || sym_attr.oacc_declare_deviceptr
+      || sym_attr.oacc_declare_device_resident
+      || sym_attr.oacc_declare_link)
+    {
+      list = tree_cons (get_identifier ("oacc declare"),
+			NULL_TREE, list);
+    }
   return list;
 }
 
@@ -5761,6 +5770,258 @@ is_ieee_module_used (gfc_namespace *ns)
   return seen_ieee_symbol;
 }
 
+static struct oacc_return
+{
+  gfc_code *code;
+  struct oacc_return *next;
+} *oacc_returns;
+
+static void
+find_oacc_return (gfc_code *code)
+{
+  if (code->next)
+    {
+      if (code->next->op == EXEC_RETURN
+	  || code->next->op == EXEC_END_PROCEDURE)
+	{
+	  struct oacc_return *r;
+
+	  r = XCNEW (struct oacc_return);
+	  r->code = code;
+	  r->next = NULL;
+
+	  if (oacc_returns)
+	    r->next = oacc_returns;
+
+	  oacc_returns = r;
+	}
+      else
+	{
+	  find_oacc_return (code->next);
+	}
+    }
+  else if (code->block)
+    find_oacc_return (code->block);
+  else
+    {
+      struct oacc_return *r;
+
+      r = XCNEW (struct oacc_return);
+      r->code = code;
+      r->next = NULL;
+
+      if (oacc_returns)
+	    r->next = oacc_returns;
+
+      oacc_returns = r;
+    }
+
+  return;
+}
+
+static gfc_omp_clauses *module_oacc_clauses;
+
+static void
+add_clause (gfc_symbol *sym, gfc_omp_map_op map_op)
+{
+  gfc_omp_namelist *n;
+
+  n = gfc_get_omp_namelist ();
+  n->sym = sym;
+  n->u.map_op = map_op;
+
+  if (!module_oacc_clauses)
+    module_oacc_clauses = gfc_get_omp_clauses ();
+
+  if (module_oacc_clauses->lists[OMP_LIST_MAP])
+    n->next = module_oacc_clauses->lists[OMP_LIST_MAP];
+
+  module_oacc_clauses->lists[OMP_LIST_MAP] = n;
+}
+
+
+static void
+find_module_oacc_declare_clauses (gfc_symbol *sym)
+{
+  if (sym->attr.use_assoc)
+    {
+      gfc_omp_map_op map_op;
+
+      if (sym->attr.oacc_declare_create)
+	map_op = OMP_MAP_FORCE_ALLOC;
+
+      if (sym->attr.oacc_declare_copyin)
+	map_op = OMP_MAP_FORCE_TO;
+
+      if (sym->attr.oacc_declare_deviceptr)
+	map_op = OMP_MAP_FORCE_DEVICEPTR;
+
+      if (sym->attr.oacc_declare_device_resident)
+	map_op = OMP_MAP_DEVICE_RESIDENT;
+
+      if (sym->attr.oacc_declare_create
+	  || sym->attr.oacc_declare_copyin
+	  || sym->attr.oacc_declare_deviceptr
+	  || sym->attr.oacc_declare_device_resident)
+	{
+	  sym->attr.referenced = 1;
+	  add_clause (sym, map_op);
+	}
+    }
+}
+
+void
+finish_oacc_declare (gfc_namespace *ns, enum sym_flavor flavor)
+{
+  gfc_code *code;
+  gfc_oacc_declare *oc;
+  gfc_omp_namelist *n;
+  locus where = gfc_current_locus;
+  gfc_omp_clauses *ret_clauses = NULL;
+
+  gfc_traverse_ns (ns, find_module_oacc_declare_clauses);
+
+  if (module_oacc_clauses && flavor == FL_PROGRAM)
+    {
+      gfc_oacc_declare *new_oc;
+
+      new_oc = gfc_get_oacc_declare ();
+      new_oc->next = ns->oacc_declare;
+      new_oc->clauses = module_oacc_clauses;
+
+      ns->oacc_declare = new_oc;
+      module_oacc_clauses = NULL;
+    }
+
+  if (!ns->oacc_declare)
+    return;
+
+  for (oc = ns->oacc_declare; oc; oc = oc->next)
+    {
+      gfc_omp_clauses *omp_clauses;
+
+      if (oc->module_var)
+	continue;
+
+      if (oc->clauses)
+	{
+	   code = XCNEW (gfc_code);
+	   code->op = EXEC_OACC_DECLARE;
+	   code->loc = where;
+
+	   omp_clauses = oc->clauses;
+
+	   for (n = omp_clauses->lists[OMP_LIST_MAP]; n; n = n->next)
+	     {
+		bool ret = false;
+		gfc_omp_map_op new_op;
+
+		switch (n->u.map_op)
+		  {
+		    case OMP_MAP_ALLOC:
+		    case OMP_MAP_FORCE_ALLOC:
+		      new_op = OMP_MAP_FORCE_DEALLOC;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_DEVICE_RESIDENT:
+		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
+		      new_op = OMP_MAP_FORCE_DEALLOC;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_FROM:
+		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
+		      new_op = OMP_MAP_FORCE_FROM;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_TO:
+		      new_op = OMP_MAP_FORCE_DEALLOC;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_TOFROM:
+		      n->u.map_op = OMP_MAP_FORCE_TO;
+		      new_op = OMP_MAP_FORCE_FROM;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FROM:
+		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
+		      new_op = OMP_MAP_FROM;
+		      ret = true;
+		      break;
+
+		    case OMP_MAP_FORCE_DEVICEPTR:
+		    case OMP_MAP_FORCE_PRESENT:
+		    case OMP_MAP_LINK:
+		    case OMP_MAP_TO:
+		      break;
+
+		    case OMP_MAP_TOFROM:
+		      n->u.map_op = OMP_MAP_TO;
+		      new_op = OMP_MAP_FROM;
+		      ret = true;
+		      break;
+
+		    default:
+		      gcc_unreachable ();
+		      break;
+		  }
+
+		if (ret)
+		  {
+		    gfc_omp_namelist *new_n;
+
+		    new_n = gfc_get_omp_namelist ();
+		    new_n->sym = n->sym;
+		    new_n->u.map_op = new_op;
+
+		    if (!ret_clauses)
+		      ret_clauses = gfc_get_omp_clauses ();
+
+		    if (ret_clauses->lists[OMP_LIST_MAP])
+		      new_n->next = ret_clauses->lists[OMP_LIST_MAP];
+
+		    ret_clauses->lists[OMP_LIST_MAP] = new_n;
+		    ret = false;
+		  }
+	     }
+
+	   code->ext.oacc_declare = gfc_get_oacc_declare ();
+	   code->ext.oacc_declare->clauses = omp_clauses;
+
+	   if (ns->code)
+	     code->next = ns->code;
+	   ns->code = code;
+	}
+    }
+
+    find_oacc_return (ns->code);
+
+    while (oacc_returns)
+      {
+	struct oacc_return *r;
+
+	r = oacc_returns;
+
+	code = XCNEW (gfc_code);
+	code->op = EXEC_OACC_DECLARE;
+	code->loc = where;
+
+	code->ext.oacc_declare = gfc_get_oacc_declare ();
+	code->ext.oacc_declare->clauses = ret_clauses;
+	code->next = r->code->next;
+	r->code->next = code;
+
+	oacc_returns = r->next;
+	free (r);
+      }
+
+  return;
+}
+
 
 /* Generate code for a function.  */
 
@@ -5899,11 +6160,7 @@ gfc_generate_function_code (gfc_namespace * ns)
     add_argument_checking (&body, sym);
 
   /* Generate !$ACC DECLARE directive. */
-  if (ns->oacc_declare_clauses)
-    {
-      tree tmp = gfc_trans_oacc_declare (&body, ns);
-      gfc_add_expr_to_block (&body, tmp);
-    }
+  finish_oacc_declare  (ns, sym->attr.flavor);
 
   tmp = gfc_trans_code (ns->code);
   gfc_add_expr_to_block (&body, tmp);
diff --git a/gcc/fortran/trans-openmp.c b/gcc/fortran/trans-openmp.c
index 9f0d533..57d63ae 100644
--- a/gcc/fortran/trans-openmp.c
+++ b/gcc/fortran/trans-openmp.c
@@ -1925,6 +1925,9 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 	      if (!n->sym->attr.referenced)
 		continue;
 
+	      if (n->sym->attr.use_assoc && n->sym->attr.oacc_declare_link)
+		continue;
+
 	      tree node = build_omp_clause (input_location, OMP_CLAUSE_MAP);
 	      tree node2 = NULL_TREE;
 	      tree node3 = NULL_TREE;
@@ -4423,13 +4426,24 @@ gfc_trans_omp_workshare (gfc_code *code, gfc_omp_clauses *clauses)
 }
 
 tree
-gfc_trans_oacc_declare (stmtblock_t *block, gfc_namespace *ns)
+gfc_trans_oacc_declare (gfc_code *code)
 {
-  tree oacc_clauses;
-  oacc_clauses = gfc_trans_omp_clauses (block, ns->oacc_declare_clauses,
-					ns->oacc_declare_clauses->loc);
-  return build1_loc (ns->oacc_declare_clauses->loc.lb->location,
-		     OACC_DECLARE, void_type_node, oacc_clauses);
+  stmtblock_t block;
+  tree stmt, c;
+  enum tree_code construct_code;
+
+  gfc_start_block (&block);
+
+  construct_code = OACC_DECLARE;
+
+  gfc_start_block (&block);
+  c = gfc_trans_omp_clauses (&block, code->ext.oacc_declare->clauses,
+			     code->loc);
+
+  stmt = build1_loc (input_location, construct_code, void_type_node, c);
+  gfc_add_expr_to_block (&block, stmt);
+
+  return gfc_finish_block (&block);
 }
 
 tree
@@ -4457,6 +4471,8 @@ gfc_trans_oacc_directive (gfc_code *code)
       return gfc_trans_oacc_wait_directive (code);
     case EXEC_OACC_ATOMIC:
       return gfc_trans_omp_atomic (code);
+    case EXEC_OACC_DECLARE:
+      return gfc_trans_oacc_declare (code);
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/fortran/trans-stmt.c b/gcc/fortran/trans-stmt.c
index 1c61606..321cd80 100644
--- a/gcc/fortran/trans-stmt.c
+++ b/gcc/fortran/trans-stmt.c
@@ -1579,11 +1579,7 @@ gfc_trans_block_construct (gfc_code* code)
   code->exit_label = exit_label;
 
   /* Generate !$ACC DECLARE directive. */
-  if (ns->oacc_declare_clauses)
-    {
-      tree tmp = gfc_trans_oacc_declare (&body, ns);
-      gfc_add_expr_to_block (&body, tmp);
-    }
+  finish_oacc_declare (ns, FL_UNKNOWN);
 
   gfc_add_expr_to_block (&body, gfc_trans_code (ns->code));
   gfc_add_expr_to_block (&body, build1_v (LABEL_EXPR, exit_label));
diff --git a/gcc/fortran/trans-stmt.h b/gcc/fortran/trans-stmt.h
index 2f2a0b3..0ff93c4 100644
--- a/gcc/fortran/trans-stmt.h
+++ b/gcc/fortran/trans-stmt.h
@@ -67,7 +67,7 @@ void gfc_trans_omp_declare_simd (gfc_namespace *);
 
 /* trans-openacc.c */
 tree gfc_trans_oacc_directive (gfc_code *);
-tree gfc_trans_oacc_declare (stmtblock_t *block, gfc_namespace *);
+tree gfc_trans_oacc_declare (gfc_namespace *);
 
 /* trans-io.c */
 tree gfc_trans_open (gfc_code *);
diff --git a/gcc/fortran/trans.c b/gcc/fortran/trans.c
index 9495450..880c309 100644
--- a/gcc/fortran/trans.c
+++ b/gcc/fortran/trans.c
@@ -1904,6 +1904,7 @@ trans_code (gfc_code * code, tree cond)
 	case EXEC_OACC_ENTER_DATA:
 	case EXEC_OACC_EXIT_DATA:
 	case EXEC_OACC_ATOMIC:
+	case EXEC_OACC_DECLARE:
 	  res = gfc_trans_oacc_directive (code);
 	  break;
 
diff --git a/gcc/testsuite/gfortran.dg/goacc/declare-1.f95 b/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
index 5cf737f..3129f04 100644
--- a/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
+++ b/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
@@ -15,5 +15,4 @@ contains
     END BLOCK
   end function foo
 end program test
-! { dg-prune-output "unimplemented" }
-! { dg-final { scan-tree-dump-times "pragma acc declare map\\(force_tofrom:i\\)" 2 "original" } } 
+! { dg-final { scan-tree-dump-times "pragma acc declare map\\(force_to:i\\)" 2 "original" } }
diff --git a/gcc/testsuite/gfortran.dg/goacc/declare-2.f95 b/gcc/testsuite/gfortran.dg/goacc/declare-2.f95
index e69de29..9450c8b 100644
--- a/gcc/testsuite/gfortran.dg/goacc/declare-2.f95
+++ b/gcc/testsuite/gfortran.dg/goacc/declare-2.f95
@@ -0,0 +1,44 @@
+
+module amod
+
+contains
+
+subroutine asubr (b)
+  implicit none
+  integer :: b(8)
+
+  !$acc declare copy (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare copyout (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copy (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copyin (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copyout (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_create (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare deviceptr (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare create (b) copyin (b) ! { dg-error "present on multiple clauses" }
+
+end subroutine
+
+end module
+
+subroutine bsubr (foo)
+  implicit none
+
+  integer, dimension (:) :: foo
+
+  !$acc declare copy (foo) ! { dg-error "assumed-size dummy array" }
+  !$acc declare copy (foo(1:2)) ! { dg-error "assumed-size dummy array" }
+
+end subroutine
+
+program test
+  integer :: a(8)
+  integer :: b(8)
+  integer :: c(8)
+
+  !$acc declare create (a) copyin (a) ! { dg-error "present on multiple clauses" }
+  !$acc declare copyin (b)
+  !$acc declare copyin (b) ! { dg-error "present on multiple clauses" }
+  !$acc declare copy (c(1:2)) ! { dg-error "Array sections: 'c' not allowed" }
+
+end program
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
index e69de29..18dd1bb 100644
--- a/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
@@ -0,0 +1,236 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  integer z
+  !$acc declare create (z)
+end module vars
+
+subroutine subr6 (a, d)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare deviceptr (a)
+  integer :: d(N)
+
+  i = 0
+
+  !$acc parallel copy (d)
+    do i = 1, N
+      d(i) = a(i) + a(i)
+    end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr5 (a, b, c, d)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present_or_copyin (a)
+  integer :: b(N)
+  !$acc declare present_or_create (b)
+  integer :: c(N)
+  !$acc declare present_or_copyout (c)
+  integer :: d(N)
+  !$acc declare present_or_copy (d)
+
+  i = 0
+
+  !$acc parallel
+    do i = 1, N
+      b(i) = a(i)
+      c(i) = b(i)
+      d(i) = d(i) + b(i)
+    end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr4 (a, b)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: b(N)
+  !$acc declare copyout (b)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    b(i) = a(i)
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr3 (a, c)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: c(N)
+  !$acc declare copyin (c)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    a(i) = c(i)
+    c(i) = 0
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr2 (a, b, c)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: b(N)
+  !$acc declare create (b)
+  integer :: c(N)
+  !$acc declare copy (c)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    b(i) = a(i)
+    c(i) = b(i) + c(i) + 1
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr1 (a)
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    a(i) = a(i) + 1
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine test (a, e)
+  use openacc
+  logical :: e
+  integer, parameter :: N = 8
+  integer :: a(N)
+
+  if (acc_is_present (a) .neqv. e) call abort
+
+end subroutine
+
+subroutine subr0 (a, b, c, d)
+  integer, parameter :: N = 8
+  integer :: a(N)
+  !$acc declare copy (a)
+  integer :: b(N)
+  integer :: c(N)
+  integer :: d(N)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  call subr1 (a)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  call subr2 (a, b, c)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (c(i) .ne. 8) call abort
+  end do
+
+  call subr3 (a, c)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (a(i) .ne. 2) call abort
+    if (c(i) .ne. 8) call abort
+  end do
+
+  call subr4 (a, b)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (b(i) .ne. 8) call abort
+  end do
+
+  call subr5 (a, b, c, d)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (c(i) .ne. 8) call abort
+    if (d(i) .ne. 13) call abort
+  end do
+
+  call subr6 (a, d)
+
+  call test (a, .true.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (d(i) .ne. 16) call abort
+  end do
+
+end subroutine
+
+program main
+  use vars
+  use openacc
+  integer, parameter :: N = 8
+  integer :: a(N)
+  integer :: b(N)
+  integer :: c(N)
+  integer :: d(N)
+
+  a(:) = 2
+  b(:) = 3
+  c(:) = 4
+  d(:) = 5
+
+  if (acc_is_present (z) .neqv. .true.) call abort
+
+  call subr0 (a, b, c, d)
+
+  call test (a, .false.)
+  call test (b, .false.)
+  call test (c, .false.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (a(i) .ne. 8) call abort
+    if (b(i) .ne. 8) call abort
+    if (c(i) .ne. 8) call abort
+    if (d(i) .ne. 16) call abort
+  end do
+
+
+end program
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
index e69de29..9b75aa1 100644
--- a/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
@@ -0,0 +1,14 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module globalvars
+  integer a
+  !$acc declare create (a)
+end module globalvars
+
+program test
+  use globalvars
+  use openacc
+
+  if (acc_is_present (a) .neqv. .true.) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
index e69de29..79fc011 100644
--- a/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
@@ -0,0 +1,65 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module globalvars
+  real b
+  !$acc declare link (b)
+end module globalvars
+
+program test
+  use openacc
+
+  real a
+  real c
+  !$acc declare link (c)
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+  if (acc_is_present (c) .neqv. .false.) call abort
+
+  a = 0.0
+  b = 1.0
+
+  !$acc parallel copy (a) copyin (b)
+    b = b + 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 5.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) create (b)
+    b = 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) copy (b)
+    b = 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+  if (b .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) copy (b) copy (c)
+    b = 4.0
+    c = b
+    a = c
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
index e69de29..997c8ac 100644
--- a/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
@@ -0,0 +1,27 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  real b
+ !$acc declare create (b)
+end module vars
+
+program test
+  use vars
+  use openacc
+  real a
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  a = 2.0
+
+  !$acc parallel copy (a)
+    b = a
+    a = 1.0
+    a = a + b
+   !$acc end parallel
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  if (a .ne. 3.0) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
index e69de29..d7c9bac 100644
--- a/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
@@ -0,0 +1,28 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  implicit none
+  real b
+ !$acc declare device_resident (b)
+end module vars
+
+program test
+  use vars
+  use openacc
+  real a
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  a = 2.0
+
+  !$acc parallel copy (a)
+    b = a
+    a = 1.0
+    a = a + b
+   !$acc end parallel
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  if (a .ne. 3.0) call abort
+
+end program test

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-06 19:04       ` Jakub Jelinek
  2015-11-06 19:23         ` Jakub Jelinek
  2015-11-06 20:16         ` James Norris
@ 2015-11-08 15:35         ` James Norris
  2015-11-09 16:01           ` James Norris
  2 siblings, 1 reply; 51+ messages in thread
From: James Norris @ 2015-11-08 15:35 UTC (permalink / raw)
  To: Jakub Jelinek, James Norris, James Norris
  Cc: Thomas Schwinge, Joseph S. Myers, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 1965 bytes --]

Jakub,

The attached patch and ChangeLog reflect the updates from your
review: https://gcc.gnu.org/ml/gcc-patches/2015-11/msg00703.html.

The most significant change was the one that you suggestion:

On 11/06/2015 02:28 PM, Jakub Jelinek wrote:
 > Perhaps what would work is stick the exit clauses you need for automatic
 > vars in the function inside of some pointer map / hash table / whatever,
 > and then in gimplify_bind_expr in the
 >    /* Add clobbers for all variables that go out of scope.  */
 > if if flag_openacc && the pointer_map /hash table has any entries look
 > up each variable in there and collect the clauses from those vars that go
 > out of scope, after the loop if any were collected construct the statement
 > you need prepend it to cleanup (so that it works before restoring VLA memory
 > and before the clobber stmts).

This particular change allowed for the removal of all the
'stuff looks broken' code. Thanks for the suggestion.

The following change I did not address:

On 11/06/2015 01:03 PM, Jakub Jelinek wrote
 >> @@ -5841,6 +5863,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree 
decl,
 >>         flags |= GOVD_FIRSTPRIVATE;
 >>         break;
 >>       case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
 >> +      if (is_global_var (decl) && device_resident_p (decl))
 >> +flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
 >
 > I don't think you want to do this except for (selected or all?)
 > OpenACC contexts.  Say, I don't see why one couldn't e.g. try to mix
 > OpenMP host parallelization or tasking with OpenACC offloading,
 > and that affecting in weird way OpenMP semantics.

A colleague is adding code to allow for the detection of OpenACC contexts.
This change has yet make it to trunk. I need some guidance from you whether
I can leave the code as is and resolve the issue at stage3 time,
or remove the code and the associated function device_resident_()
and address the issue at stage 3.

OK to commit to trunk?

Thanks!
Jim


[-- Attachment #2: ChangeLog --]
[-- Type: text/plain, Size: 2359 bytes --]

2015-XX-XX  James Norris  <jnorris@codesourcery.com>
	    Joseph Myers  <joseph@codesourcery.com>

	gcc/c-family/
	* c-pragma.c (oacc_pragmas): Add entry for declare directive. 
	* c-pragma.h (enum pragma_kind): Add PRAGMA_OACC_DECLARE.
	(enum pragma_omp_clause): Add PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT and
	PRAGMA_OACC_CLAUSE_LINK.

	gcc/c/
	* c-parser.c (c_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	(c_parser_omp_clause_name): Handle 'device_resident' clause.
	(c_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(c_parser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OACC_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(c_parser_oacc_declare): New function.

	gcc/cp/
	* parser.c (cp_parser_omp_clause_name): Handle 'device_resident'
	clause.
	(cp_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(cp_paser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(cp_parser_oacc_declare): New function.
	(cp_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	* pt.c (tsubst_expr): Handle OACC_DECLARE.

	gcc/
	* gimple-pretty-print.c (dump_gimple_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE. 
	* gimple.h (enum gf_mask): Add GF_OMP_TARGET_KIND_OACC_DECLARE.
	(is_gomple_omp_oacc): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* gimplify.c (gimplify_bind_expr): Prepend 'exit' stmt to cleanup.
	* omp-builtins.def (BUILT_IN_GOACC_DECLARE): New builtin.
	* omp-low.c (expand_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE and BUILTIN_GOACC_DECLARE.
	(build_omp_regions_1): Handlde GF_OMP_TARGET_KIND_OACC_DECLARE.
	(lower_omp_target): Handle GF_OMP_TARGET_KIND_OACC_DECLARE,
	GOMP_MAP_DEVICE_RESIDENT and GOMP_MAP_LINK.
	(make_gimple_omp_edges): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.

	gcc/testsuite
	* c-c++-common/goacc/declare-1.c: New test.
	* c-c++-common/goacc/declare-2.c: Likewise.

	include/
	* gomp-constants.h (enum gomp_map_kind): Add GOMP_MAP_DEVICE_RESIDENT
	and GOMP_MAP_LINK.

	libgomp/

	* libgomp.map (GOACC_2.0.1): Export GOACC_declare.
	* oacc-parallel.c (GOACC_declare): New function.
	* testsuite/libgomp.oacc-c-c++-common/declare-1.c: New test.
	* testsuite/libgomp.oacc-c-c++-common/declare-5.c: Likewise.

[-- Attachment #3: declare.patch --]
[-- Type: text/x-patch, Size: 37301 bytes --]

diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
index ac11838..cd0cc27 100644
--- a/gcc/c-family/c-pragma.c
+++ b/gcc/c-family/c-pragma.c
@@ -1207,6 +1207,7 @@ static const struct omp_pragma_def oacc_pragmas[] = {
   { "atomic", PRAGMA_OACC_ATOMIC },
   { "cache", PRAGMA_OACC_CACHE },
   { "data", PRAGMA_OACC_DATA },
+  { "declare", PRAGMA_OACC_DECLARE },
   { "enter", PRAGMA_OACC_ENTER_DATA },
   { "exit", PRAGMA_OACC_EXIT_DATA },
   { "kernels", PRAGMA_OACC_KERNELS },
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 953c4e3..c6a2981 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -30,6 +30,7 @@ enum pragma_kind {
   PRAGMA_OACC_ATOMIC,
   PRAGMA_OACC_CACHE,
   PRAGMA_OACC_DATA,
+  PRAGMA_OACC_DECLARE,
   PRAGMA_OACC_ENTER_DATA,
   PRAGMA_OACC_EXIT_DATA,
   PRAGMA_OACC_KERNELS,
@@ -151,6 +152,7 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_CREATE,
   PRAGMA_OACC_CLAUSE_DELETE,
   PRAGMA_OACC_CLAUSE_DEVICEPTR,
+  PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT,
   PRAGMA_OACC_CLAUSE_GANG,
   PRAGMA_OACC_CLAUSE_HOST,
   PRAGMA_OACC_CLAUSE_INDEPENDENT,
@@ -175,7 +177,8 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_FIRSTPRIVATE = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE,
   PRAGMA_OACC_CLAUSE_IF = PRAGMA_OMP_CLAUSE_IF,
   PRAGMA_OACC_CLAUSE_PRIVATE = PRAGMA_OMP_CLAUSE_PRIVATE,
-  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION
+  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION,
+  PRAGMA_OACC_CLAUSE_LINK = PRAGMA_OMP_CLAUSE_LINK
 };
 
 extern struct cpp_reader* parse_in;
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 23d0107..b5439e9 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1231,6 +1231,7 @@ static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
 					     unsigned int * = NULL);
+static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
 static void c_parser_omp_construct (c_parser *);
@@ -9697,6 +9698,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context)
 
   switch (id)
     {
+    case PRAGMA_OACC_DECLARE:
+      c_parser_oacc_declare (parser);
+      return false;
+
     case PRAGMA_OACC_ENTER_DATA:
       c_parser_oacc_enter_exit_data (parser, true);
       return false;
@@ -9982,6 +9987,8 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -10418,10 +10425,16 @@ c_parser_oacc_data_clause (c_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -12703,6 +12716,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = c_parser_omp_clause_firstprivate (parser, clauses);
 	  c_name = "firstprivate";
@@ -12725,6 +12742,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 						clauses);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  clauses = c_parser_omp_clause_num_gangs (parser, clauses);
 	  c_name = "num_gangs";
@@ -13182,6 +13203,249 @@ c_parser_oacc_data (location_t loc, c_parser *parser)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE) )
+
+hash_map<tree, tree> *JMN;
+
+static void
+c_parser_oacc_declare (c_parser *parser)
+{
+  location_t pragma_loc = c_parser_peek_token (parser)->location;
+  tree c, clauses, ret_clauses, stmt, t, decl;
+
+  bool error = false;
+
+  c_parser_consume_pragma (parser);
+
+  clauses = c_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+				       "#pragma acc declare");
+  if (!clauses)
+    {
+      error_at (pragma_loc,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return;
+    }
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      decl = OMP_CLAUSE_DECL (t);
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "array section in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p ()
+	      && (!TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl) =
+			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+
+	  if (global_bindings_p ())
+	    {
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
+	    }
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_LINK:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = build_omp_clause (OMP_CLAUSE_LOCATION (c) , OMP_CLAUSE_MAP);
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+	  OMP_CLAUSE_DECL (t) = OMP_CLAUSE_DECL (c);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+    if (ret_clauses)
+      {
+	tree fndecl = current_function_decl;
+	tree attrs = lookup_attribute ("oacc declare returns",
+				       DECL_ATTRIBUTES (fndecl));
+
+	if (attrs)
+	  {
+	    OMP_CLAUSE_CHAIN (ret_clauses) = TREE_VALUE (attrs);
+	    TREE_VALUE (attrs) = ret_clauses;
+	  }
+	else
+	  {
+	    tree id = get_identifier ("oacc declare returns");
+	    DECL_ATTRIBUTES (fndecl) =
+			tree_cons (id, ret_clauses, DECL_ATTRIBUTES (fndecl));
+
+	  }
+      }
+
+    stmt = make_node (OACC_DECLARE);
+    TREE_TYPE (stmt) = void_type_node;
+    OACC_DECLARE_CLAUSES (stmt) = clauses;
+    SET_EXPR_LOCATION (stmt, pragma_loc);
+
+    add_stmt (stmt);
+
+    return;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c6f5729..6432a34 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -29099,6 +29099,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -29512,10 +29514,16 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -31516,6 +31524,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  clauses = cp_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_FIRSTPRIVATE,
 					    clauses);
@@ -31540,6 +31552,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 						  clauses, here);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  code = OMP_CLAUSE_NUM_GANGS;
 	  c_name = "num_gangs";
@@ -34497,6 +34513,246 @@ cp_parser_oacc_data (cp_parser *parser, cp_token *pragma_tok)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE))
+
+static tree
+cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
+{
+  tree c, clauses, ret_clauses, stmt, t;
+  bool error = false;
+
+  clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+					"#pragma acc declare", pragma_tok, true);
+
+
+  if (find_omp_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
+    {
+      error_at (pragma_tok->location,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return NULL_TREE;
+    }
+
+  for (tree t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      tree decl = OMP_CLAUSE_DECL (t);
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "array section in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+      gcc_assert (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_MAP);
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p ()
+	      && (TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl) =
+			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+	  if (global_bindings_p ())
+	    {
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
+	    }
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return NULL_TREE;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  case GOMP_MAP_LINK:
+	    continue;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = build_omp_clause (OMP_CLAUSE_LOCATION (c) , OMP_CLAUSE_MAP);
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+	  OMP_CLAUSE_DECL (t) = OMP_CLAUSE_DECL (c);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+    if (ret_clauses)
+      {
+	tree fndecl = current_function_decl;
+	tree attrs = lookup_attribute ("oacc declare returns",
+				       DECL_ATTRIBUTES (fndecl));
+
+	if (attrs)
+	  {
+	    OMP_CLAUSE_CHAIN (ret_clauses) = TREE_VALUE (attrs);
+	    TREE_VALUE (attrs) = ret_clauses;
+	  }
+	else
+	  {
+	    tree id = get_identifier ("oacc declare returns");
+	    DECL_ATTRIBUTES (fndecl) =
+			tree_cons (id, ret_clauses, DECL_ATTRIBUTES (fndecl));
+
+	  }
+      }
+
+  stmt = make_node (OACC_DECLARE);
+  TREE_TYPE (stmt) = void_type_node;
+  OACC_DECLARE_CLAUSES (stmt) = clauses;
+  SET_EXPR_LOCATION (stmt, pragma_tok->location);
+
+  add_stmt (stmt);
+
+  return NULL_TREE;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
@@ -36183,6 +36439,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context)
       cp_parser_omp_declare (parser, pragma_tok, context);
       return false;
 
+    case PRAGMA_OACC_DECLARE:
+      cp_parser_oacc_declare (parser, pragma_tok);
+      return false;
+
     case PRAGMA_OACC_ATOMIC:
     case PRAGMA_OACC_CACHE:
     case PRAGMA_OACC_DATA:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 45eda3a..3e03f02 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15422,6 +15422,17 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       add_stmt (t);
       break;
 
+    case OACC_DECLARE:
+      t = copy_node (t);
+      tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_CLAUSES (t) = tmp;
+      tmp = tsubst_omp_clauses (OACC_DECLARE_RETURN_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_RETURN_CLAUSES (t) = tmp;
+      add_stmt (t);
+      break;
+
     case OMP_TARGET_UPDATE:
     case OMP_TARGET_ENTER_DATA:
     case OMP_TARGET_EXIT_DATA:
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 7b50cdf..7764201 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -1353,6 +1353,9 @@ dump_gimple_omp_target (pretty_printer *buffer, gomp_target *gs,
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       kind = " oacc_enter_exit_data";
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      kind = " oacc_declare";
+      break;
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 781801b..e45162d 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -170,6 +170,7 @@ enum gf_mask {
     GF_OMP_TARGET_KIND_OACC_DATA = 7,
     GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
     GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
+    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,
 
     /* True on an GIMPLE_OMP_RETURN statement if the return does not require
        a thread synchronization via some sort of barrier.  The exact barrier
@@ -6004,6 +6005,7 @@ is_gimple_omp_oacc (const gimple *stmt)
 	case GF_OMP_TARGET_KIND_OACC_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  return true;
 	default:
 	  return false;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index fa34858..a25f07c 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1065,6 +1065,7 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
   gimple_seq body, cleanup;
   gcall *stack_save;
   location_t start_locus = 0, end_locus = 0;
+  tree ret_clauses = NULL;
 
   tree temp = voidify_wrapper_expr (bind_expr, NULL);
 
@@ -1166,9 +1167,56 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 	  clobber_stmt = gimple_build_assign (t, clobber);
 	  gimple_set_location (clobber_stmt, end_locus);
 	  gimplify_seq_add_stmt (&cleanup, clobber_stmt);
+
+	  if (flag_openacc)
+	    {
+	      tree attrs = lookup_attribute ("oacc declare returns",
+				       DECL_ATTRIBUTES (current_function_decl));
+	      tree clauses, c, c_next = NULL, c_prev = NULL;
+
+	      if (!attrs)
+		break;
+
+	      clauses = TREE_VALUE (attrs);
+
+	      for (c = clauses; c; c_prev = c, c = c_next)
+		{
+		  c_next = OMP_CLAUSE_CHAIN (c);
+
+		  if (t == OMP_CLAUSE_DECL (c))
+		    {
+		      if (ret_clauses)
+			OMP_CLAUSE_CHAIN (c) = ret_clauses;
+
+		      ret_clauses = c;
+
+		      if (c_prev == NULL)
+			clauses = c_next;
+		      else
+			OMP_CLAUSE_CHAIN (c_prev) = c_next;
+		    }
+		}
+
+	      if (clauses == NULL)
+		{
+		  DECL_ATTRIBUTES (current_function_decl) =
+			    remove_attribute ("oacc declare returns",
+				    DECL_ATTRIBUTES (current_function_decl));
+		}
+	    }
 	}
     }
 
+  if (ret_clauses)
+    {
+      gomp_target *stmt;
+      gimple_stmt_iterator si = gsi_start (cleanup);
+
+      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				      ret_clauses);
+      gsi_insert_seq_before_without_update (&si, stmt, GSI_NEW_STMT);
+    }
+
   if (cleanup)
     {
       gtry *gs;
@@ -5792,6 +5840,26 @@ omp_notice_threadprivate_variable (struct gimplify_omp_ctx *ctx, tree decl,
   return false;
 }
 
+/* Return true if global var DECL is device resident.  */
+
+static bool
+device_resident_p (tree decl)
+{
+  tree attr = lookup_attribute ("oacc declare target", DECL_ATTRIBUTES (decl));
+
+  if (!attr)
+    return false;
+
+  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
+    {
+      tree c = TREE_VALUE (t);
+      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
+	return true;
+    }
+
+  return false;
+}
+
 /* Determine outer default flags for DECL mentioned in an OMP region
    but not declared in an enclosing clause.
 
@@ -5841,6 +5909,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
       flags |= GOVD_FIRSTPRIVATE;
       break;
     case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
+      if (is_global_var (decl) && device_resident_p (decl))
+	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
       /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED.  */
       gcc_assert ((ctx->region_type & ORT_TASK) != 0);
       if (struct gimplify_omp_ctx *octx = ctx->outer_context)
@@ -7712,6 +7782,37 @@ gimplify_oacc_cache (tree *expr_p, gimple_seq *pre_p)
   *expr_p = NULL_TREE;
 }
 
+/* Gimplify OACC_DECLARE.  */
+
+static void
+gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
+{
+  tree expr = *expr_p;
+  gomp_target *stmt;
+  tree clauses, t;
+
+  clauses = OACC_DECLARE_CLAUSES (expr);
+
+  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      tree decl = OMP_CLAUSE_DECL (t);
+
+      if (TREE_CODE (decl) == MEM_REF)
+	continue;
+
+      omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
+    }
+
+  stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				  clauses);
+
+  gimplify_seq_add_stmt (pre_p, stmt);
+
+  *expr_p = NULL_TREE;
+}
+
 /* Gimplify the contents of an OMP_PARALLEL statement.  This involves
    gimplification of the body, as well as scanning the body for used
    variables.  We need to do this scan now, because variable-sized
@@ -10063,11 +10164,15 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  break;
 
 	case OACC_HOST_DATA:
-	case OACC_DECLARE:
 	  sorry ("directive not yet implemented");
 	  ret = GS_ALL_DONE;
 	  break;
 
+	case OACC_DECLARE:
+	  gimplify_oacc_declare (expr_p, pre_p);
+	  ret = GS_ALL_DONE;
+	  break;
+
 	case OACC_DATA:
 	case OACC_KERNELS:
 	case OACC_PARALLEL:
diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def
index fc87a3f..0365bc4 100644
--- a/gcc/omp-builtins.def
+++ b/gcc/omp-builtins.def
@@ -357,3 +357,5 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TARGET_ENTER_EXIT_DATA,
 		  BT_FN_VOID_INT_SIZE_PTR_PTR_PTR_UINT_PTR, ATTR_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS, "GOMP_teams",
 		  BT_FN_VOID_UINT_UINT, ATTR_NOTHROW_LIST)
+DEF_GOACC_BUILTIN (BUILT_IN_GOACC_DECLARE, "GOACC_declare",
+		   BT_FN_VOID_INT_SIZE_PTR_PTR_PTR, ATTR_NOTHROW_LIST)
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 5ffb276..0119e44 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -12344,6 +12344,7 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -12587,6 +12588,9 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       start_ix = BUILT_IN_GOACC_ENTER_EXIT_DATA;
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      start_ix = BUILT_IN_GOACC_DECLARE;
+      break;
     default:
       gcc_unreachable ();
     }
@@ -12709,6 +12713,7 @@ expand_omp_target (struct omp_region *region)
   switch (start_ix)
     {
     case BUILT_IN_GOACC_DATA_START:
+    case BUILT_IN_GOACC_DECLARE:
     case BUILT_IN_GOMP_TARGET_DATA:
       break;
     case BUILT_IN_GOMP_TARGET:
@@ -13023,6 +13028,7 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent,
 		case GF_OMP_TARGET_KIND_EXIT_DATA:
 		case GF_OMP_TARGET_KIND_OACC_UPDATE:
 		case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+		case GF_OMP_TARGET_KIND_OACC_DECLARE:
 		  /* ..., other than for those stand-alone directives...  */
 		  region = NULL;
 		  break;
@@ -14806,6 +14812,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -14876,6 +14883,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	  case GOMP_MAP_FORCE_TOFROM:
 	  case GOMP_MAP_FORCE_PRESENT:
 	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_LINK:
 	    gcc_assert (is_gimple_omp_oacc (stmt));
 	    break;
 	  default:
@@ -16542,6 +16551,7 @@ make_gimple_omp_edges (basic_block bb, struct omp_region **region,
 	case GF_OMP_TARGET_KIND_EXIT_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  cur_region = cur_region->outer;
 	  break;
 	default:
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
new file mode 100644
index 0000000..b036c63
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -0,0 +1,83 @@
+/* Test valid uses of declare directive.  */
+/* { dg-do compile } */
+
+int v0;
+#pragma acc declare create(v0)
+
+int v1;
+#pragma acc declare copyin(v1)
+
+int *v2;
+#pragma acc declare deviceptr(v2)
+
+int v3;
+#pragma acc declare device_resident(v3)
+
+int v4;
+#pragma acc declare link(v4)
+
+int v5, v6, v7, v8;
+#pragma acc declare create(v5, v6) copyin(v7, v8)
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare create(va0)
+
+  int va1;
+#pragma acc declare copyin(va1)
+
+  int *va2;
+#pragma acc declare deviceptr(va2)
+
+  int va3;
+#pragma acc declare device_resident(va3)
+
+  extern int ve0;
+#pragma acc declare create(ve0)
+
+  extern int ve1;
+#pragma acc declare copyin(ve1)
+
+  extern int *ve2;
+#pragma acc declare deviceptr(ve2)
+
+  extern int ve3;
+#pragma acc declare device_resident(ve3)
+
+  extern int ve4;
+#pragma acc declare link(ve4)
+
+  int va5;
+#pragma acc declare copy(va5)
+
+  int va6;
+#pragma acc declare copyout(va6)
+
+  int va7;
+#pragma acc declare present(va7)
+
+  int va8;
+#pragma acc declare present_or_copy(va8)
+
+  int va9;
+#pragma acc declare present_or_copyin(va9)
+
+  int va10;
+#pragma acc declare present_or_copyout(va10)
+
+  int va11;
+#pragma acc declare present_or_create(va11)
+
+ a:
+  {
+    int va0;
+#pragma acc declare create(va0)
+    if (v1)
+      goto a;
+    else
+      goto b;
+  }
+ b:;
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
new file mode 100644
index 0000000..7979f0c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -0,0 +1,68 @@
+/* Test invalid uses of declare directive.  */
+/* { dg-do compile } */
+
+#pragma acc declare /* { dg-error "no valid clauses" } */
+
+#pragma acc declare create(undeclared) /* { dg-error "undeclared" } */
+/* { dg-error "no valid clauses" "second error" { target *-*-* } 6 } */
+
+int v0[10];
+#pragma acc declare create(v0[1:3]) /* { dg-error "subarray" } */
+
+int v1;
+#pragma acc declare create(v1, v1) /* { dg-error "more than once" } */
+
+int v2;
+#pragma acc declare create(v2) /* { dg-message "previous directive" } */
+#pragma acc declare copyin(v2) /* { dg-error "more than once" } */
+
+int v3;
+#pragma acc declare copy(v3) /* { dg-error "at file scope" } */
+
+int v4;
+#pragma acc declare copyout(v4) /* { dg-error "at file scope" } */
+
+int v5;
+#pragma acc declare present(v5) /* { dg-error "at file scope" } */
+
+int v6;
+#pragma acc declare present_or_copy(v6) /* { dg-error "at file scope" } */
+
+int v7;
+#pragma acc declare present_or_copyin(v7) /* { dg-error "at file scope" } */
+
+int v8;
+#pragma acc declare present_or_copyout(v8) /* { dg-error "at file scope" } */
+
+int v9;
+#pragma acc declare present_or_create(v9) /* { dg-error "at file scope" } */
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare link(va0) /* { dg-error "global variable" } */
+
+  extern int ve0;
+#pragma acc declare copy(ve0) /* { dg-error "invalid use of" } */
+
+  extern int ve1;
+#pragma acc declare copyout(ve1) /* { dg-error "invalid use of" } */
+
+  extern int ve2;
+#pragma acc declare present(ve2) /* { dg-error "invalid use of" } */
+
+  extern int ve3;
+#pragma acc declare present_or_copy(ve3) /* { dg-error "invalid use of" } */
+
+  extern int ve4;
+#pragma acc declare present_or_copyin(ve4) /* { dg-error "invalid use of" } */
+
+  extern int ve5;
+#pragma acc declare present_or_copyout(ve5) /* { dg-error "invalid use of" } */
+
+  extern int ve6;
+#pragma acc declare present_or_create(ve6) /* { dg-error "invalid use of" } */
+
+#pragma acc declare present (v9) /* { dg-error "invalid use of" } */
+}
diff --git a/gcc/tree.h b/gcc/tree.h
index 6768b3b..a84d11a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1232,6 +1232,8 @@ extern void protected_set_expr_location (tree, location_t);
 
 #define OACC_DECLARE_CLAUSES(NODE) \
   TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 0)
+#define OACC_DECLARE_RETURN_CLAUSES(NODE) \
+  TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 1)
 
 #define OACC_ENTER_DATA_CLAUSES(NODE) \
   TREE_OPERAND (OACC_ENTER_DATA_CHECK (NODE), 0)
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 7671dd7..dffd631 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -72,6 +72,11 @@ enum gomp_map_kind
        POINTER_SIZE_UNITS.  */
     GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
     /* Do not map, copy bits for firstprivate instead.  */
+    /* OpenACC device_resident.  */
+    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
+    /* OpenACC link.  */
+    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
+    /* Allocate.  */
     GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
     /* Similarly, but store the value in the pointer rather than
        pointed by the pointer.  */
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 39faba9..d16710f 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -392,6 +392,7 @@ GOACC_2.0 {
 
 GOACC_2.0.1 {
   global:
+	GOACC_declare;
 	GOACC_parallel_keyed;
 } GOACC_2.0;
 
diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index 525846b..9de9e55 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -501,3 +501,62 @@ GOACC_get_thread_num (void)
 {
   return 0;
 }
+
+void
+GOACC_declare (int device, size_t mapnum,
+	       void **hostaddrs, size_t *sizes, unsigned short *kinds)
+{
+  int i;
+
+  for (i = 0; i < mapnum; i++)
+    {
+      unsigned char kind = kinds[i] & 0xff;
+
+      if (kind == GOMP_MAP_POINTER || kind == GOMP_MAP_TO_PSET)
+	continue;
+
+      switch (kind)
+	{
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_DEALLOC:
+	  case GOMP_MAP_FORCE_FROM:
+	  case GOMP_MAP_FORCE_TO:
+	  case GOMP_MAP_POINTER:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	    break;
+
+	  case GOMP_MAP_ALLOC:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      {
+		GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	      }
+	    break;
+
+	  case GOMP_MAP_TO:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    kinds[i] = GOMP_MAP_FORCE_FROM;
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_PRESENT:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      gomp_fatal ("[%p,%zd] is not mapped", hostaddrs[i], sizes[i]);
+	    break;
+
+	  default:
+	    assert (0);
+	    break;
+	}
+    }
+}
diff --git a/libgomp/testsuite/declare-1.c b/libgomp/testsuite/declare-1.c
new file mode 100644
index 0000000..8fbec4d
--- /dev/null
+++ b/libgomp/testsuite/declare-1.c
@@ -0,0 +1,122 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <openacc.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define N 8
+
+void
+subr2 (int *a)
+{
+  int i;
+  int f[N];
+#pragma acc declare copyout (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i] + f[i];
+      }
+  }
+}
+
+void
+subr1 (int *a)
+{
+  int f[N];
+#pragma acc declare copy (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    int i;
+
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i];
+      }
+  }
+}
+
+int b[8];
+#pragma acc declare create (b)
+
+int d[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+#pragma acc declare copyin (d)
+
+int
+main (int argc, char **argv)
+{
+  int a[N];
+  int e[N];
+#pragma acc declare create (e)
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = i + 1;
+
+  if (!acc_is_present (&b, sizeof (b)))
+    abort ();
+
+  if (!acc_is_present (&d, sizeof (d)))
+    abort ();
+
+  if (!acc_is_present (&e, sizeof (e)))
+    abort ();
+
+#pragma acc parallel copyin (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        b[i] = a[i];
+        a[i] = b[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != i + 1)
+	abort ();
+    }
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        e[i] = a[i] + d[i];
+	a[i] = e[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != (i + 1) * 2)
+	abort ();
+    }
+
+  for (i = 0; i < N; i++)
+    {
+      a[i] = 1234;
+    }
+
+  subr1 (&a[0]);
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != 1234 * 2)
+	abort ();
+    }
+
+  subr2 (&a[0]);
+
+  for (i = 0; i < 1; i++)
+    {
+      if (a[i] != 1234 * 6)
+	abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/declare-5.c b/libgomp/testsuite/declare-5.c
new file mode 100644
index 0000000..1e2f6ce
--- /dev/null
+++ b/libgomp/testsuite/declare-5.c
@@ -0,0 +1,13 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+int
+main (int argc, char **argv)
+{
+  int a[8] __attribute__((unused));
+
+  __builtin_printf ("CheCKpOInT\n");
+#pragma acc declare present (a)
+}
+
+/* { dg-output "CheCKpOInT" } */
+/* { dg-shouldfail "" } */

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: Re: OpenACC declare directive updates
  2015-11-08 15:29       ` OpenACC declare directive updates James Norris
@ 2015-11-09  2:30         ` Cesar Philippidis
  2015-11-09  4:53           ` James Norris
  0 siblings, 1 reply; 51+ messages in thread
From: Cesar Philippidis @ 2015-11-09  2:30 UTC (permalink / raw)
  To: James Norris, Jakub Jelinek; +Cc: GCC Patches

On 11/08/2015 07:29 AM, James Norris wrote:

> The attached patch and ChangeLog reflect the updates from your
> review: https://gcc.gnu.org/ml/gcc-patches/2015-11/msg00714.html.
> All of the issues pointed out, have been address.
> 
> With the changes made in this patch I think I'm handling the
> situation that you pointed out here correctly:
> 
> On Fri, Nov 06, 2015 at 01:45:09PM -0600, James Norris wrote:
> 
> Also, wonder about BLOCK stmt in Fortran, that can give you variables that
> don't live through the whole function, but only a portion of it even in
> Fortran.
> 
> OK to commit to trunk?

I'll defer to Jakub, but here are a couple of comments.

>  void
>  gfc_resolve_oacc_declare (gfc_namespace *ns)
>  {
>    int list;
>    gfc_omp_namelist *n;
>    locus loc;
> +  gfc_oacc_declare *oc;
>  
> -  if (ns->oacc_declare_clauses == NULL)
> +  if (ns->oacc_declare == NULL)
>      return;
>  
> -  loc = ns->oacc_declare_clauses->loc;
> +  loc = gfc_current_locus;
>  
> -  for (list = OMP_LIST_DEVICE_RESIDENT;
> -       list <= OMP_LIST_DEVICE_RESIDENT; list++)
> -    for (n = ns->oacc_declare_clauses->lists[list]; n; n = n->next)
> -      {
> -	n->sym->mark = 0;
> -	if (n->sym->attr.flavor == FL_PARAMETER)
> -	  gfc_error ("PARAMETER object %qs is not allowed at %L", n->sym->name, &loc);
> -      }
> +  for (oc = ns->oacc_declare; oc; oc = oc->next)
> +    {
> +      for (list = OMP_LIST_DEVICE_RESIDENT;
> +	   list <= OMP_LIST_DEVICE_RESIDENT; list++)

Why is this loop necessary?

> +	for (n = oc->clauses->lists[list]; n; n = n->next)
> +	  {
> +	    n->sym->mark = 0;
> +	    if (n->sym->attr.flavor == FL_PARAMETER)
> +	      gfc_error ("PARAMETER object %qs is not allowed at %L",
> +			 n->sym->name, &loc);
> +	  }
>  
> -  for (list = OMP_LIST_DEVICE_RESIDENT;
> -       list <= OMP_LIST_DEVICE_RESIDENT; list++)
> -    for (n = ns->oacc_declare_clauses->lists[list]; n; n = n->next)
> -      {
> -	if (n->sym->mark)
> -	  gfc_error ("Symbol %qs present on multiple clauses at %L",
> -		     n->sym->name, &loc);
> -	else
> -	  n->sym->mark = 1;
> -      }
> +      for (list = OMP_LIST_DEVICE_RESIDENT;
> +	    list <= OMP_LIST_DEVICE_RESIDENT; list++)

And here.

> +	for (n = oc->clauses->lists[list]; n; n = n->next)
> +	  {
> +	    if (n->sym->mark)
> +	      gfc_error ("Symbol %qs present on multiple clauses at %L",
> +			 n->sym->name, &loc);
> +	    else
> +	      n->sym->mark = 1;
> +	  }
>  
> -  for (n = ns->oacc_declare_clauses->lists[OMP_LIST_DEVICE_RESIDENT]; n;
> -       n = n->next)
> -    check_array_not_assumed (n->sym, loc, "DEVICE_RESIDENT");
> -}
> +      for (n = oc->clauses->lists[OMP_LIST_DEVICE_RESIDENT]; n; n = n->next)

This is better.

> +	check_array_not_assumed (n->sym, loc, "DEVICE_RESIDENT");
> +
> +      for (n = oc->clauses->lists[OMP_LIST_MAP]; n; n = n->next)
> +	{
> +	  if (n->expr && n->expr->ref->type == REF_ARRAY)
> +	      gfc_error ("Array sections: %qs not allowed in"
> +			 " $!ACC DECLARE at %L", n->sym->name, &loc);
> +	}
> +    }
> +
> +  for (oc = ns->oacc_declare; oc; oc = oc->next)
> +    {
> +      for (list = OMP_LIST_LINK; list <= OMP_LIST_LINK; list++)

?

> +	for (n = oc->clauses->lists[list]; n; n = n->next)
> +	  n->sym->mark = 0;
> +    }
>  
> +  for (oc = ns->oacc_declare; oc; oc = oc->next)
> +    {
> +      for (list = OMP_LIST_LINK; list <= OMP_LIST_LINK; list++)

?

> +	for (n = oc->clauses->lists[list]; n; n = n->next)
> +	  {
> +	    if (n->sym->mark)
> +	      gfc_error ("Symbol %qs present on multiple clauses at %L",
> +			 n->sym->name, &loc);
> +	    else
> +	      n->sym->mark = 1;
> +	  }
> +    }
> +
> +  for (oc = ns->oacc_declare; oc; oc = oc->next)
> +    {
> +      for (list = OMP_LIST_LINK; list <= OMP_LIST_LINK; list++)

?

> +	for (n = oc->clauses->lists[list]; n; n = n->next)
> +	  n->sym->mark = 0;
> +    }
> +}

I only noticed these because I thought I fixed them in the patch you
asked me to revert from gomp-4_0-branch. At the very least, please try
to be consistent on iterating OMP_LIST_*.

Cesar

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: OpenACC declare directive updates
  2015-11-09  2:30         ` Cesar Philippidis
@ 2015-11-09  4:53           ` James Norris
  2015-11-18 20:09             ` Cesar Philippidis
  0 siblings, 1 reply; 51+ messages in thread
From: James Norris @ 2015-11-09  4:53 UTC (permalink / raw)
  To: Cesar Philippidis, Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 741 bytes --]

Cesar,

> I only noticed these because I thought I fixed them in the patch you
> asked me to revert from gomp-4_0-branch. At the very least, please try
> to be consistent on iterating OMP_LIST_*.
Thank you for noticing!

Jakub,

The attached patch and ChangeLog reflect the updates from your
review: https://gcc.gnu.org/ml/gcc-patches/2015-11/msg00714.html
and Cesar's review: https://gcc.gnu.org/ml/gcc-patches/2015-11/msg00885.html.

With the changes made in this patch I think I'm handling the
situation that you pointed out here correctly:

"Also, wonder about BLOCK stmt in Fortran, that can give you variables that
don't live through the whole function, but only a portion of it even in
Fortran."

OK to commit to trunk?

Thanks!
Jim


[-- Attachment #2: ChangeLog --]
[-- Type: text/plain, Size: 2359 bytes --]

2015-XX-XX  James Norris  <jnorris@codesourcery.com>
	    Joseph Myers  <joseph@codesourcery.com>

	gcc/c-family/
	* c-pragma.c (oacc_pragmas): Add entry for declare directive. 
	* c-pragma.h (enum pragma_kind): Add PRAGMA_OACC_DECLARE.
	(enum pragma_omp_clause): Add PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT and
	PRAGMA_OACC_CLAUSE_LINK.

	gcc/c/
	* c-parser.c (c_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	(c_parser_omp_clause_name): Handle 'device_resident' clause.
	(c_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(c_parser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OACC_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(c_parser_oacc_declare): New function.

	gcc/cp/
	* parser.c (cp_parser_omp_clause_name): Handle 'device_resident'
	clause.
	(cp_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(cp_paser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(cp_parser_oacc_declare): New function.
	(cp_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	* pt.c (tsubst_expr): Handle OACC_DECLARE.

	gcc/
	* gimple-pretty-print.c (dump_gimple_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE. 
	* gimple.h (enum gf_mask): Add GF_OMP_TARGET_KIND_OACC_DECLARE.
	(is_gomple_omp_oacc): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* gimplify.c (gimplify_bind_expr): Prepend 'exit' stmt to cleanup.
	* omp-builtins.def (BUILT_IN_GOACC_DECLARE): New builtin.
	* omp-low.c (expand_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE and BUILTIN_GOACC_DECLARE.
	(build_omp_regions_1): Handlde GF_OMP_TARGET_KIND_OACC_DECLARE.
	(lower_omp_target): Handle GF_OMP_TARGET_KIND_OACC_DECLARE,
	GOMP_MAP_DEVICE_RESIDENT and GOMP_MAP_LINK.
	(make_gimple_omp_edges): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.

	gcc/testsuite
	* c-c++-common/goacc/declare-1.c: New test.
	* c-c++-common/goacc/declare-2.c: Likewise.

	include/
	* gomp-constants.h (enum gomp_map_kind): Add GOMP_MAP_DEVICE_RESIDENT
	and GOMP_MAP_LINK.

	libgomp/

	* libgomp.map (GOACC_2.0.1): Export GOACC_declare.
	* oacc-parallel.c (GOACC_declare): New function.
	* testsuite/libgomp.oacc-c-c++-common/declare-1.c: New test.
	* testsuite/libgomp.oacc-c-c++-common/declare-5.c: Likewise.

[-- Attachment #3: declare.patch --]
[-- Type: text/x-patch, Size: 37301 bytes --]

diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
index ac11838..cd0cc27 100644
--- a/gcc/c-family/c-pragma.c
+++ b/gcc/c-family/c-pragma.c
@@ -1207,6 +1207,7 @@ static const struct omp_pragma_def oacc_pragmas[] = {
   { "atomic", PRAGMA_OACC_ATOMIC },
   { "cache", PRAGMA_OACC_CACHE },
   { "data", PRAGMA_OACC_DATA },
+  { "declare", PRAGMA_OACC_DECLARE },
   { "enter", PRAGMA_OACC_ENTER_DATA },
   { "exit", PRAGMA_OACC_EXIT_DATA },
   { "kernels", PRAGMA_OACC_KERNELS },
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 953c4e3..c6a2981 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -30,6 +30,7 @@ enum pragma_kind {
   PRAGMA_OACC_ATOMIC,
   PRAGMA_OACC_CACHE,
   PRAGMA_OACC_DATA,
+  PRAGMA_OACC_DECLARE,
   PRAGMA_OACC_ENTER_DATA,
   PRAGMA_OACC_EXIT_DATA,
   PRAGMA_OACC_KERNELS,
@@ -151,6 +152,7 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_CREATE,
   PRAGMA_OACC_CLAUSE_DELETE,
   PRAGMA_OACC_CLAUSE_DEVICEPTR,
+  PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT,
   PRAGMA_OACC_CLAUSE_GANG,
   PRAGMA_OACC_CLAUSE_HOST,
   PRAGMA_OACC_CLAUSE_INDEPENDENT,
@@ -175,7 +177,8 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_FIRSTPRIVATE = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE,
   PRAGMA_OACC_CLAUSE_IF = PRAGMA_OMP_CLAUSE_IF,
   PRAGMA_OACC_CLAUSE_PRIVATE = PRAGMA_OMP_CLAUSE_PRIVATE,
-  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION
+  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION,
+  PRAGMA_OACC_CLAUSE_LINK = PRAGMA_OMP_CLAUSE_LINK
 };
 
 extern struct cpp_reader* parse_in;
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 23d0107..b5439e9 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1231,6 +1231,7 @@ static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
 					     unsigned int * = NULL);
+static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
 static void c_parser_omp_construct (c_parser *);
@@ -9697,6 +9698,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context)
 
   switch (id)
     {
+    case PRAGMA_OACC_DECLARE:
+      c_parser_oacc_declare (parser);
+      return false;
+
     case PRAGMA_OACC_ENTER_DATA:
       c_parser_oacc_enter_exit_data (parser, true);
       return false;
@@ -9982,6 +9987,8 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -10418,10 +10425,16 @@ c_parser_oacc_data_clause (c_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -12703,6 +12716,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = c_parser_omp_clause_firstprivate (parser, clauses);
 	  c_name = "firstprivate";
@@ -12725,6 +12742,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 						clauses);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  clauses = c_parser_omp_clause_num_gangs (parser, clauses);
 	  c_name = "num_gangs";
@@ -13182,6 +13203,249 @@ c_parser_oacc_data (location_t loc, c_parser *parser)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE) )
+
+hash_map<tree, tree> *JMN;
+
+static void
+c_parser_oacc_declare (c_parser *parser)
+{
+  location_t pragma_loc = c_parser_peek_token (parser)->location;
+  tree c, clauses, ret_clauses, stmt, t, decl;
+
+  bool error = false;
+
+  c_parser_consume_pragma (parser);
+
+  clauses = c_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+				       "#pragma acc declare");
+  if (!clauses)
+    {
+      error_at (pragma_loc,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return;
+    }
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      decl = OMP_CLAUSE_DECL (t);
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "array section in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p ()
+	      && (!TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl) =
+			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+
+	  if (global_bindings_p ())
+	    {
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
+	    }
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_LINK:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = build_omp_clause (OMP_CLAUSE_LOCATION (c) , OMP_CLAUSE_MAP);
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+	  OMP_CLAUSE_DECL (t) = OMP_CLAUSE_DECL (c);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+    if (ret_clauses)
+      {
+	tree fndecl = current_function_decl;
+	tree attrs = lookup_attribute ("oacc declare returns",
+				       DECL_ATTRIBUTES (fndecl));
+
+	if (attrs)
+	  {
+	    OMP_CLAUSE_CHAIN (ret_clauses) = TREE_VALUE (attrs);
+	    TREE_VALUE (attrs) = ret_clauses;
+	  }
+	else
+	  {
+	    tree id = get_identifier ("oacc declare returns");
+	    DECL_ATTRIBUTES (fndecl) =
+			tree_cons (id, ret_clauses, DECL_ATTRIBUTES (fndecl));
+
+	  }
+      }
+
+    stmt = make_node (OACC_DECLARE);
+    TREE_TYPE (stmt) = void_type_node;
+    OACC_DECLARE_CLAUSES (stmt) = clauses;
+    SET_EXPR_LOCATION (stmt, pragma_loc);
+
+    add_stmt (stmt);
+
+    return;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c6f5729..6432a34 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -29099,6 +29099,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -29512,10 +29514,16 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -31516,6 +31524,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  clauses = cp_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_FIRSTPRIVATE,
 					    clauses);
@@ -31540,6 +31552,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 						  clauses, here);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  code = OMP_CLAUSE_NUM_GANGS;
 	  c_name = "num_gangs";
@@ -34497,6 +34513,246 @@ cp_parser_oacc_data (cp_parser *parser, cp_token *pragma_tok)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE))
+
+static tree
+cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
+{
+  tree c, clauses, ret_clauses, stmt, t;
+  bool error = false;
+
+  clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+					"#pragma acc declare", pragma_tok, true);
+
+
+  if (find_omp_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
+    {
+      error_at (pragma_tok->location,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return NULL_TREE;
+    }
+
+  for (tree t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      tree decl = OMP_CLAUSE_DECL (t);
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "array section in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+      gcc_assert (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_MAP);
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p ()
+	      && (TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl) =
+			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+	  if (global_bindings_p ())
+	    {
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
+	    }
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return NULL_TREE;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  case GOMP_MAP_LINK:
+	    continue;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = build_omp_clause (OMP_CLAUSE_LOCATION (c) , OMP_CLAUSE_MAP);
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+	  OMP_CLAUSE_DECL (t) = OMP_CLAUSE_DECL (c);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+    if (ret_clauses)
+      {
+	tree fndecl = current_function_decl;
+	tree attrs = lookup_attribute ("oacc declare returns",
+				       DECL_ATTRIBUTES (fndecl));
+
+	if (attrs)
+	  {
+	    OMP_CLAUSE_CHAIN (ret_clauses) = TREE_VALUE (attrs);
+	    TREE_VALUE (attrs) = ret_clauses;
+	  }
+	else
+	  {
+	    tree id = get_identifier ("oacc declare returns");
+	    DECL_ATTRIBUTES (fndecl) =
+			tree_cons (id, ret_clauses, DECL_ATTRIBUTES (fndecl));
+
+	  }
+      }
+
+  stmt = make_node (OACC_DECLARE);
+  TREE_TYPE (stmt) = void_type_node;
+  OACC_DECLARE_CLAUSES (stmt) = clauses;
+  SET_EXPR_LOCATION (stmt, pragma_tok->location);
+
+  add_stmt (stmt);
+
+  return NULL_TREE;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
@@ -36183,6 +36439,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context)
       cp_parser_omp_declare (parser, pragma_tok, context);
       return false;
 
+    case PRAGMA_OACC_DECLARE:
+      cp_parser_oacc_declare (parser, pragma_tok);
+      return false;
+
     case PRAGMA_OACC_ATOMIC:
     case PRAGMA_OACC_CACHE:
     case PRAGMA_OACC_DATA:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 45eda3a..3e03f02 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15422,6 +15422,17 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       add_stmt (t);
       break;
 
+    case OACC_DECLARE:
+      t = copy_node (t);
+      tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_CLAUSES (t) = tmp;
+      tmp = tsubst_omp_clauses (OACC_DECLARE_RETURN_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_RETURN_CLAUSES (t) = tmp;
+      add_stmt (t);
+      break;
+
     case OMP_TARGET_UPDATE:
     case OMP_TARGET_ENTER_DATA:
     case OMP_TARGET_EXIT_DATA:
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 7b50cdf..7764201 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -1353,6 +1353,9 @@ dump_gimple_omp_target (pretty_printer *buffer, gomp_target *gs,
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       kind = " oacc_enter_exit_data";
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      kind = " oacc_declare";
+      break;
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 781801b..e45162d 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -170,6 +170,7 @@ enum gf_mask {
     GF_OMP_TARGET_KIND_OACC_DATA = 7,
     GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
     GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
+    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,
 
     /* True on an GIMPLE_OMP_RETURN statement if the return does not require
        a thread synchronization via some sort of barrier.  The exact barrier
@@ -6004,6 +6005,7 @@ is_gimple_omp_oacc (const gimple *stmt)
 	case GF_OMP_TARGET_KIND_OACC_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  return true;
 	default:
 	  return false;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index fa34858..a25f07c 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1065,6 +1065,7 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
   gimple_seq body, cleanup;
   gcall *stack_save;
   location_t start_locus = 0, end_locus = 0;
+  tree ret_clauses = NULL;
 
   tree temp = voidify_wrapper_expr (bind_expr, NULL);
 
@@ -1166,9 +1167,56 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 	  clobber_stmt = gimple_build_assign (t, clobber);
 	  gimple_set_location (clobber_stmt, end_locus);
 	  gimplify_seq_add_stmt (&cleanup, clobber_stmt);
+
+	  if (flag_openacc)
+	    {
+	      tree attrs = lookup_attribute ("oacc declare returns",
+				       DECL_ATTRIBUTES (current_function_decl));
+	      tree clauses, c, c_next = NULL, c_prev = NULL;
+
+	      if (!attrs)
+		break;
+
+	      clauses = TREE_VALUE (attrs);
+
+	      for (c = clauses; c; c_prev = c, c = c_next)
+		{
+		  c_next = OMP_CLAUSE_CHAIN (c);
+
+		  if (t == OMP_CLAUSE_DECL (c))
+		    {
+		      if (ret_clauses)
+			OMP_CLAUSE_CHAIN (c) = ret_clauses;
+
+		      ret_clauses = c;
+
+		      if (c_prev == NULL)
+			clauses = c_next;
+		      else
+			OMP_CLAUSE_CHAIN (c_prev) = c_next;
+		    }
+		}
+
+	      if (clauses == NULL)
+		{
+		  DECL_ATTRIBUTES (current_function_decl) =
+			    remove_attribute ("oacc declare returns",
+				    DECL_ATTRIBUTES (current_function_decl));
+		}
+	    }
 	}
     }
 
+  if (ret_clauses)
+    {
+      gomp_target *stmt;
+      gimple_stmt_iterator si = gsi_start (cleanup);
+
+      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				      ret_clauses);
+      gsi_insert_seq_before_without_update (&si, stmt, GSI_NEW_STMT);
+    }
+
   if (cleanup)
     {
       gtry *gs;
@@ -5792,6 +5840,26 @@ omp_notice_threadprivate_variable (struct gimplify_omp_ctx *ctx, tree decl,
   return false;
 }
 
+/* Return true if global var DECL is device resident.  */
+
+static bool
+device_resident_p (tree decl)
+{
+  tree attr = lookup_attribute ("oacc declare target", DECL_ATTRIBUTES (decl));
+
+  if (!attr)
+    return false;
+
+  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
+    {
+      tree c = TREE_VALUE (t);
+      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
+	return true;
+    }
+
+  return false;
+}
+
 /* Determine outer default flags for DECL mentioned in an OMP region
    but not declared in an enclosing clause.
 
@@ -5841,6 +5909,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
       flags |= GOVD_FIRSTPRIVATE;
       break;
     case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
+      if (is_global_var (decl) && device_resident_p (decl))
+	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
       /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED.  */
       gcc_assert ((ctx->region_type & ORT_TASK) != 0);
       if (struct gimplify_omp_ctx *octx = ctx->outer_context)
@@ -7712,6 +7782,37 @@ gimplify_oacc_cache (tree *expr_p, gimple_seq *pre_p)
   *expr_p = NULL_TREE;
 }
 
+/* Gimplify OACC_DECLARE.  */
+
+static void
+gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
+{
+  tree expr = *expr_p;
+  gomp_target *stmt;
+  tree clauses, t;
+
+  clauses = OACC_DECLARE_CLAUSES (expr);
+
+  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      tree decl = OMP_CLAUSE_DECL (t);
+
+      if (TREE_CODE (decl) == MEM_REF)
+	continue;
+
+      omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
+    }
+
+  stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				  clauses);
+
+  gimplify_seq_add_stmt (pre_p, stmt);
+
+  *expr_p = NULL_TREE;
+}
+
 /* Gimplify the contents of an OMP_PARALLEL statement.  This involves
    gimplification of the body, as well as scanning the body for used
    variables.  We need to do this scan now, because variable-sized
@@ -10063,11 +10164,15 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  break;
 
 	case OACC_HOST_DATA:
-	case OACC_DECLARE:
 	  sorry ("directive not yet implemented");
 	  ret = GS_ALL_DONE;
 	  break;
 
+	case OACC_DECLARE:
+	  gimplify_oacc_declare (expr_p, pre_p);
+	  ret = GS_ALL_DONE;
+	  break;
+
 	case OACC_DATA:
 	case OACC_KERNELS:
 	case OACC_PARALLEL:
diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def
index fc87a3f..0365bc4 100644
--- a/gcc/omp-builtins.def
+++ b/gcc/omp-builtins.def
@@ -357,3 +357,5 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TARGET_ENTER_EXIT_DATA,
 		  BT_FN_VOID_INT_SIZE_PTR_PTR_PTR_UINT_PTR, ATTR_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS, "GOMP_teams",
 		  BT_FN_VOID_UINT_UINT, ATTR_NOTHROW_LIST)
+DEF_GOACC_BUILTIN (BUILT_IN_GOACC_DECLARE, "GOACC_declare",
+		   BT_FN_VOID_INT_SIZE_PTR_PTR_PTR, ATTR_NOTHROW_LIST)
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 5ffb276..0119e44 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -12344,6 +12344,7 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -12587,6 +12588,9 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       start_ix = BUILT_IN_GOACC_ENTER_EXIT_DATA;
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      start_ix = BUILT_IN_GOACC_DECLARE;
+      break;
     default:
       gcc_unreachable ();
     }
@@ -12709,6 +12713,7 @@ expand_omp_target (struct omp_region *region)
   switch (start_ix)
     {
     case BUILT_IN_GOACC_DATA_START:
+    case BUILT_IN_GOACC_DECLARE:
     case BUILT_IN_GOMP_TARGET_DATA:
       break;
     case BUILT_IN_GOMP_TARGET:
@@ -13023,6 +13028,7 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent,
 		case GF_OMP_TARGET_KIND_EXIT_DATA:
 		case GF_OMP_TARGET_KIND_OACC_UPDATE:
 		case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+		case GF_OMP_TARGET_KIND_OACC_DECLARE:
 		  /* ..., other than for those stand-alone directives...  */
 		  region = NULL;
 		  break;
@@ -14806,6 +14812,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -14876,6 +14883,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	  case GOMP_MAP_FORCE_TOFROM:
 	  case GOMP_MAP_FORCE_PRESENT:
 	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_LINK:
 	    gcc_assert (is_gimple_omp_oacc (stmt));
 	    break;
 	  default:
@@ -16542,6 +16551,7 @@ make_gimple_omp_edges (basic_block bb, struct omp_region **region,
 	case GF_OMP_TARGET_KIND_EXIT_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  cur_region = cur_region->outer;
 	  break;
 	default:
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
new file mode 100644
index 0000000..b036c63
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -0,0 +1,83 @@
+/* Test valid uses of declare directive.  */
+/* { dg-do compile } */
+
+int v0;
+#pragma acc declare create(v0)
+
+int v1;
+#pragma acc declare copyin(v1)
+
+int *v2;
+#pragma acc declare deviceptr(v2)
+
+int v3;
+#pragma acc declare device_resident(v3)
+
+int v4;
+#pragma acc declare link(v4)
+
+int v5, v6, v7, v8;
+#pragma acc declare create(v5, v6) copyin(v7, v8)
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare create(va0)
+
+  int va1;
+#pragma acc declare copyin(va1)
+
+  int *va2;
+#pragma acc declare deviceptr(va2)
+
+  int va3;
+#pragma acc declare device_resident(va3)
+
+  extern int ve0;
+#pragma acc declare create(ve0)
+
+  extern int ve1;
+#pragma acc declare copyin(ve1)
+
+  extern int *ve2;
+#pragma acc declare deviceptr(ve2)
+
+  extern int ve3;
+#pragma acc declare device_resident(ve3)
+
+  extern int ve4;
+#pragma acc declare link(ve4)
+
+  int va5;
+#pragma acc declare copy(va5)
+
+  int va6;
+#pragma acc declare copyout(va6)
+
+  int va7;
+#pragma acc declare present(va7)
+
+  int va8;
+#pragma acc declare present_or_copy(va8)
+
+  int va9;
+#pragma acc declare present_or_copyin(va9)
+
+  int va10;
+#pragma acc declare present_or_copyout(va10)
+
+  int va11;
+#pragma acc declare present_or_create(va11)
+
+ a:
+  {
+    int va0;
+#pragma acc declare create(va0)
+    if (v1)
+      goto a;
+    else
+      goto b;
+  }
+ b:;
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
new file mode 100644
index 0000000..7979f0c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -0,0 +1,68 @@
+/* Test invalid uses of declare directive.  */
+/* { dg-do compile } */
+
+#pragma acc declare /* { dg-error "no valid clauses" } */
+
+#pragma acc declare create(undeclared) /* { dg-error "undeclared" } */
+/* { dg-error "no valid clauses" "second error" { target *-*-* } 6 } */
+
+int v0[10];
+#pragma acc declare create(v0[1:3]) /* { dg-error "subarray" } */
+
+int v1;
+#pragma acc declare create(v1, v1) /* { dg-error "more than once" } */
+
+int v2;
+#pragma acc declare create(v2) /* { dg-message "previous directive" } */
+#pragma acc declare copyin(v2) /* { dg-error "more than once" } */
+
+int v3;
+#pragma acc declare copy(v3) /* { dg-error "at file scope" } */
+
+int v4;
+#pragma acc declare copyout(v4) /* { dg-error "at file scope" } */
+
+int v5;
+#pragma acc declare present(v5) /* { dg-error "at file scope" } */
+
+int v6;
+#pragma acc declare present_or_copy(v6) /* { dg-error "at file scope" } */
+
+int v7;
+#pragma acc declare present_or_copyin(v7) /* { dg-error "at file scope" } */
+
+int v8;
+#pragma acc declare present_or_copyout(v8) /* { dg-error "at file scope" } */
+
+int v9;
+#pragma acc declare present_or_create(v9) /* { dg-error "at file scope" } */
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare link(va0) /* { dg-error "global variable" } */
+
+  extern int ve0;
+#pragma acc declare copy(ve0) /* { dg-error "invalid use of" } */
+
+  extern int ve1;
+#pragma acc declare copyout(ve1) /* { dg-error "invalid use of" } */
+
+  extern int ve2;
+#pragma acc declare present(ve2) /* { dg-error "invalid use of" } */
+
+  extern int ve3;
+#pragma acc declare present_or_copy(ve3) /* { dg-error "invalid use of" } */
+
+  extern int ve4;
+#pragma acc declare present_or_copyin(ve4) /* { dg-error "invalid use of" } */
+
+  extern int ve5;
+#pragma acc declare present_or_copyout(ve5) /* { dg-error "invalid use of" } */
+
+  extern int ve6;
+#pragma acc declare present_or_create(ve6) /* { dg-error "invalid use of" } */
+
+#pragma acc declare present (v9) /* { dg-error "invalid use of" } */
+}
diff --git a/gcc/tree.h b/gcc/tree.h
index 6768b3b..a84d11a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1232,6 +1232,8 @@ extern void protected_set_expr_location (tree, location_t);
 
 #define OACC_DECLARE_CLAUSES(NODE) \
   TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 0)
+#define OACC_DECLARE_RETURN_CLAUSES(NODE) \
+  TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 1)
 
 #define OACC_ENTER_DATA_CLAUSES(NODE) \
   TREE_OPERAND (OACC_ENTER_DATA_CHECK (NODE), 0)
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 7671dd7..dffd631 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -72,6 +72,11 @@ enum gomp_map_kind
        POINTER_SIZE_UNITS.  */
     GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
     /* Do not map, copy bits for firstprivate instead.  */
+    /* OpenACC device_resident.  */
+    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
+    /* OpenACC link.  */
+    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
+    /* Allocate.  */
     GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
     /* Similarly, but store the value in the pointer rather than
        pointed by the pointer.  */
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 39faba9..d16710f 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -392,6 +392,7 @@ GOACC_2.0 {
 
 GOACC_2.0.1 {
   global:
+	GOACC_declare;
 	GOACC_parallel_keyed;
 } GOACC_2.0;
 
diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index 525846b..9de9e55 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -501,3 +501,62 @@ GOACC_get_thread_num (void)
 {
   return 0;
 }
+
+void
+GOACC_declare (int device, size_t mapnum,
+	       void **hostaddrs, size_t *sizes, unsigned short *kinds)
+{
+  int i;
+
+  for (i = 0; i < mapnum; i++)
+    {
+      unsigned char kind = kinds[i] & 0xff;
+
+      if (kind == GOMP_MAP_POINTER || kind == GOMP_MAP_TO_PSET)
+	continue;
+
+      switch (kind)
+	{
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_DEALLOC:
+	  case GOMP_MAP_FORCE_FROM:
+	  case GOMP_MAP_FORCE_TO:
+	  case GOMP_MAP_POINTER:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	    break;
+
+	  case GOMP_MAP_ALLOC:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      {
+		GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	      }
+	    break;
+
+	  case GOMP_MAP_TO:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    kinds[i] = GOMP_MAP_FORCE_FROM;
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_PRESENT:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      gomp_fatal ("[%p,%zd] is not mapped", hostaddrs[i], sizes[i]);
+	    break;
+
+	  default:
+	    assert (0);
+	    break;
+	}
+    }
+}
diff --git a/libgomp/testsuite/declare-1.c b/libgomp/testsuite/declare-1.c
new file mode 100644
index 0000000..8fbec4d
--- /dev/null
+++ b/libgomp/testsuite/declare-1.c
@@ -0,0 +1,122 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <openacc.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define N 8
+
+void
+subr2 (int *a)
+{
+  int i;
+  int f[N];
+#pragma acc declare copyout (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i] + f[i];
+      }
+  }
+}
+
+void
+subr1 (int *a)
+{
+  int f[N];
+#pragma acc declare copy (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    int i;
+
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i];
+      }
+  }
+}
+
+int b[8];
+#pragma acc declare create (b)
+
+int d[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+#pragma acc declare copyin (d)
+
+int
+main (int argc, char **argv)
+{
+  int a[N];
+  int e[N];
+#pragma acc declare create (e)
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = i + 1;
+
+  if (!acc_is_present (&b, sizeof (b)))
+    abort ();
+
+  if (!acc_is_present (&d, sizeof (d)))
+    abort ();
+
+  if (!acc_is_present (&e, sizeof (e)))
+    abort ();
+
+#pragma acc parallel copyin (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        b[i] = a[i];
+        a[i] = b[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != i + 1)
+	abort ();
+    }
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        e[i] = a[i] + d[i];
+	a[i] = e[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != (i + 1) * 2)
+	abort ();
+    }
+
+  for (i = 0; i < N; i++)
+    {
+      a[i] = 1234;
+    }
+
+  subr1 (&a[0]);
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != 1234 * 2)
+	abort ();
+    }
+
+  subr2 (&a[0]);
+
+  for (i = 0; i < 1; i++)
+    {
+      if (a[i] != 1234 * 6)
+	abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/declare-5.c b/libgomp/testsuite/declare-5.c
new file mode 100644
index 0000000..1e2f6ce
--- /dev/null
+++ b/libgomp/testsuite/declare-5.c
@@ -0,0 +1,13 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+int
+main (int argc, char **argv)
+{
+  int a[8] __attribute__((unused));
+
+  __builtin_printf ("CheCKpOInT\n");
+#pragma acc declare present (a)
+}
+
+/* { dg-output "CheCKpOInT" } */
+/* { dg-shouldfail "" } */

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-08 15:35         ` James Norris
@ 2015-11-09 16:01           ` James Norris
  2015-11-09 16:21             ` Jakub Jelinek
  0 siblings, 1 reply; 51+ messages in thread
From: James Norris @ 2015-11-09 16:01 UTC (permalink / raw)
  To: James Norris, Jakub Jelinek; +Cc: Thomas Schwinge, Joseph S. Myers, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 264 bytes --]

Jakub,

This is an update of the patch from:
https://gcc.gnu.org/ml/gcc-patches/2015-11/msg00893.html.
There was an unused variable in c/c-parser.c that was
removed.

I've included the ChangeLog as a convenience, but nothing
was changed in the file.

Thanks!
Jim


[-- Attachment #2: ChangeLog --]
[-- Type: text/plain, Size: 2359 bytes --]

2015-XX-XX  James Norris  <jnorris@codesourcery.com>
	    Joseph Myers  <joseph@codesourcery.com>

	gcc/c-family/
	* c-pragma.c (oacc_pragmas): Add entry for declare directive. 
	* c-pragma.h (enum pragma_kind): Add PRAGMA_OACC_DECLARE.
	(enum pragma_omp_clause): Add PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT and
	PRAGMA_OACC_CLAUSE_LINK.

	gcc/c/
	* c-parser.c (c_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	(c_parser_omp_clause_name): Handle 'device_resident' clause.
	(c_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(c_parser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OACC_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(c_parser_oacc_declare): New function.

	gcc/cp/
	* parser.c (cp_parser_omp_clause_name): Handle 'device_resident'
	clause.
	(cp_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(cp_paser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(cp_parser_oacc_declare): New function.
	(cp_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	* pt.c (tsubst_expr): Handle OACC_DECLARE.

	gcc/
	* gimple-pretty-print.c (dump_gimple_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE. 
	* gimple.h (enum gf_mask): Add GF_OMP_TARGET_KIND_OACC_DECLARE.
	(is_gomple_omp_oacc): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* gimplify.c (gimplify_bind_expr): Prepend 'exit' stmt to cleanup.
	* omp-builtins.def (BUILT_IN_GOACC_DECLARE): New builtin.
	* omp-low.c (expand_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE and BUILTIN_GOACC_DECLARE.
	(build_omp_regions_1): Handlde GF_OMP_TARGET_KIND_OACC_DECLARE.
	(lower_omp_target): Handle GF_OMP_TARGET_KIND_OACC_DECLARE,
	GOMP_MAP_DEVICE_RESIDENT and GOMP_MAP_LINK.
	(make_gimple_omp_edges): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.

	gcc/testsuite
	* c-c++-common/goacc/declare-1.c: New test.
	* c-c++-common/goacc/declare-2.c: Likewise.

	include/
	* gomp-constants.h (enum gomp_map_kind): Add GOMP_MAP_DEVICE_RESIDENT
	and GOMP_MAP_LINK.

	libgomp/

	* libgomp.map (GOACC_2.0.1): Export GOACC_declare.
	* oacc-parallel.c (GOACC_declare): New function.
	* testsuite/libgomp.oacc-c-c++-common/declare-1.c: New test.
	* testsuite/libgomp.oacc-c-c++-common/declare-5.c: Likewise.

[-- Attachment #3: declare.patch --]
[-- Type: text/x-patch, Size: 37271 bytes --]

diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
index ac11838..cd0cc27 100644
--- a/gcc/c-family/c-pragma.c
+++ b/gcc/c-family/c-pragma.c
@@ -1207,6 +1207,7 @@ static const struct omp_pragma_def oacc_pragmas[] = {
   { "atomic", PRAGMA_OACC_ATOMIC },
   { "cache", PRAGMA_OACC_CACHE },
   { "data", PRAGMA_OACC_DATA },
+  { "declare", PRAGMA_OACC_DECLARE },
   { "enter", PRAGMA_OACC_ENTER_DATA },
   { "exit", PRAGMA_OACC_EXIT_DATA },
   { "kernels", PRAGMA_OACC_KERNELS },
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 953c4e3..c6a2981 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -30,6 +30,7 @@ enum pragma_kind {
   PRAGMA_OACC_ATOMIC,
   PRAGMA_OACC_CACHE,
   PRAGMA_OACC_DATA,
+  PRAGMA_OACC_DECLARE,
   PRAGMA_OACC_ENTER_DATA,
   PRAGMA_OACC_EXIT_DATA,
   PRAGMA_OACC_KERNELS,
@@ -151,6 +152,7 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_CREATE,
   PRAGMA_OACC_CLAUSE_DELETE,
   PRAGMA_OACC_CLAUSE_DEVICEPTR,
+  PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT,
   PRAGMA_OACC_CLAUSE_GANG,
   PRAGMA_OACC_CLAUSE_HOST,
   PRAGMA_OACC_CLAUSE_INDEPENDENT,
@@ -175,7 +177,8 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_FIRSTPRIVATE = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE,
   PRAGMA_OACC_CLAUSE_IF = PRAGMA_OMP_CLAUSE_IF,
   PRAGMA_OACC_CLAUSE_PRIVATE = PRAGMA_OMP_CLAUSE_PRIVATE,
-  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION
+  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION,
+  PRAGMA_OACC_CLAUSE_LINK = PRAGMA_OMP_CLAUSE_LINK
 };
 
 extern struct cpp_reader* parse_in;
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 23d0107..8edf745 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1231,6 +1231,7 @@ static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
 					     unsigned int * = NULL);
+static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
 static void c_parser_omp_construct (c_parser *);
@@ -9697,6 +9698,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context)
 
   switch (id)
     {
+    case PRAGMA_OACC_DECLARE:
+      c_parser_oacc_declare (parser);
+      return false;
+
     case PRAGMA_OACC_ENTER_DATA:
       c_parser_oacc_enter_exit_data (parser, true);
       return false;
@@ -9982,6 +9987,8 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -10418,10 +10425,16 @@ c_parser_oacc_data_clause (c_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -12703,6 +12716,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = c_parser_omp_clause_firstprivate (parser, clauses);
 	  c_name = "firstprivate";
@@ -12725,6 +12742,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 						clauses);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  clauses = c_parser_omp_clause_num_gangs (parser, clauses);
 	  c_name = "num_gangs";
@@ -13182,6 +13203,247 @@ c_parser_oacc_data (location_t loc, c_parser *parser)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE) )
+
+static void
+c_parser_oacc_declare (c_parser *parser)
+{
+  location_t pragma_loc = c_parser_peek_token (parser)->location;
+  tree c, clauses, ret_clauses, stmt, t, decl;
+
+  bool error = false;
+
+  c_parser_consume_pragma (parser);
+
+  clauses = c_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+				       "#pragma acc declare");
+  if (!clauses)
+    {
+      error_at (pragma_loc,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return;
+    }
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      decl = OMP_CLAUSE_DECL (t);
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "array section in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p ()
+	      && (!TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl) =
+			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+
+	  if (global_bindings_p ())
+	    {
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
+	    }
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_LINK:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = build_omp_clause (OMP_CLAUSE_LOCATION (c) , OMP_CLAUSE_MAP);
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+	  OMP_CLAUSE_DECL (t) = OMP_CLAUSE_DECL (c);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+    if (ret_clauses)
+      {
+	tree fndecl = current_function_decl;
+	tree attrs = lookup_attribute ("oacc declare returns",
+				       DECL_ATTRIBUTES (fndecl));
+
+	if (attrs)
+	  {
+	    OMP_CLAUSE_CHAIN (ret_clauses) = TREE_VALUE (attrs);
+	    TREE_VALUE (attrs) = ret_clauses;
+	  }
+	else
+	  {
+	    tree id = get_identifier ("oacc declare returns");
+	    DECL_ATTRIBUTES (fndecl) =
+			tree_cons (id, ret_clauses, DECL_ATTRIBUTES (fndecl));
+
+	  }
+      }
+
+    stmt = make_node (OACC_DECLARE);
+    TREE_TYPE (stmt) = void_type_node;
+    OACC_DECLARE_CLAUSES (stmt) = clauses;
+    SET_EXPR_LOCATION (stmt, pragma_loc);
+
+    add_stmt (stmt);
+
+    return;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c6f5729..6432a34 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -29099,6 +29099,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -29512,10 +29514,16 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -31516,6 +31524,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  clauses = cp_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_FIRSTPRIVATE,
 					    clauses);
@@ -31540,6 +31552,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 						  clauses, here);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  code = OMP_CLAUSE_NUM_GANGS;
 	  c_name = "num_gangs";
@@ -34497,6 +34513,246 @@ cp_parser_oacc_data (cp_parser *parser, cp_token *pragma_tok)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE))
+
+static tree
+cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
+{
+  tree c, clauses, ret_clauses, stmt, t;
+  bool error = false;
+
+  clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+					"#pragma acc declare", pragma_tok, true);
+
+
+  if (find_omp_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
+    {
+      error_at (pragma_tok->location,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return NULL_TREE;
+    }
+
+  for (tree t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      tree decl = OMP_CLAUSE_DECL (t);
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "array section in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+      gcc_assert (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_MAP);
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p ()
+	      && (TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl) =
+			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+	  if (global_bindings_p ())
+	    {
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
+	    }
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return NULL_TREE;
+
+  ret_clauses = NULL_TREE;
+
+  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      bool ret = false;
+      HOST_WIDE_INT kind, new_op;
+
+      kind = OMP_CLAUSE_MAP_KIND (c);
+
+      switch (kind)
+	{
+	  case GOMP_MAP_ALLOC:
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_TO:
+	    new_op = GOMP_MAP_FORCE_DEALLOC;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FORCE_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
+	    new_op = GOMP_MAP_FORCE_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_TOFROM:
+	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+	    new_op = GOMP_MAP_FROM;
+	    ret = true;
+	    break;
+
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_FORCE_PRESENT:
+	  case GOMP_MAP_POINTER:
+	  case GOMP_MAP_TO:
+	    break;
+
+	  case GOMP_MAP_LINK:
+	    continue;
+
+	  default:
+	    gcc_unreachable ();
+	    break;
+	}
+
+      if (ret)
+	{
+	  t = build_omp_clause (OMP_CLAUSE_LOCATION (c) , OMP_CLAUSE_MAP);
+	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
+	  OMP_CLAUSE_DECL (t) = OMP_CLAUSE_DECL (c);
+
+	  if (ret_clauses)
+	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
+
+	  ret_clauses = t;
+	}
+    }
+
+    if (ret_clauses)
+      {
+	tree fndecl = current_function_decl;
+	tree attrs = lookup_attribute ("oacc declare returns",
+				       DECL_ATTRIBUTES (fndecl));
+
+	if (attrs)
+	  {
+	    OMP_CLAUSE_CHAIN (ret_clauses) = TREE_VALUE (attrs);
+	    TREE_VALUE (attrs) = ret_clauses;
+	  }
+	else
+	  {
+	    tree id = get_identifier ("oacc declare returns");
+	    DECL_ATTRIBUTES (fndecl) =
+			tree_cons (id, ret_clauses, DECL_ATTRIBUTES (fndecl));
+
+	  }
+      }
+
+  stmt = make_node (OACC_DECLARE);
+  TREE_TYPE (stmt) = void_type_node;
+  OACC_DECLARE_CLAUSES (stmt) = clauses;
+  SET_EXPR_LOCATION (stmt, pragma_tok->location);
+
+  add_stmt (stmt);
+
+  return NULL_TREE;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
@@ -36183,6 +36439,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context)
       cp_parser_omp_declare (parser, pragma_tok, context);
       return false;
 
+    case PRAGMA_OACC_DECLARE:
+      cp_parser_oacc_declare (parser, pragma_tok);
+      return false;
+
     case PRAGMA_OACC_ATOMIC:
     case PRAGMA_OACC_CACHE:
     case PRAGMA_OACC_DATA:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 45eda3a..3e03f02 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15422,6 +15422,17 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       add_stmt (t);
       break;
 
+    case OACC_DECLARE:
+      t = copy_node (t);
+      tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_CLAUSES (t) = tmp;
+      tmp = tsubst_omp_clauses (OACC_DECLARE_RETURN_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_RETURN_CLAUSES (t) = tmp;
+      add_stmt (t);
+      break;
+
     case OMP_TARGET_UPDATE:
     case OMP_TARGET_ENTER_DATA:
     case OMP_TARGET_EXIT_DATA:
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 7b50cdf..7764201 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -1353,6 +1353,9 @@ dump_gimple_omp_target (pretty_printer *buffer, gomp_target *gs,
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       kind = " oacc_enter_exit_data";
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      kind = " oacc_declare";
+      break;
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 781801b..e45162d 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -170,6 +170,7 @@ enum gf_mask {
     GF_OMP_TARGET_KIND_OACC_DATA = 7,
     GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
     GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
+    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,
 
     /* True on an GIMPLE_OMP_RETURN statement if the return does not require
        a thread synchronization via some sort of barrier.  The exact barrier
@@ -6004,6 +6005,7 @@ is_gimple_omp_oacc (const gimple *stmt)
 	case GF_OMP_TARGET_KIND_OACC_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  return true;
 	default:
 	  return false;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index fa34858..a25f07c 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1065,6 +1065,7 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
   gimple_seq body, cleanup;
   gcall *stack_save;
   location_t start_locus = 0, end_locus = 0;
+  tree ret_clauses = NULL;
 
   tree temp = voidify_wrapper_expr (bind_expr, NULL);
 
@@ -1166,9 +1167,56 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 	  clobber_stmt = gimple_build_assign (t, clobber);
 	  gimple_set_location (clobber_stmt, end_locus);
 	  gimplify_seq_add_stmt (&cleanup, clobber_stmt);
+
+	  if (flag_openacc)
+	    {
+	      tree attrs = lookup_attribute ("oacc declare returns",
+				       DECL_ATTRIBUTES (current_function_decl));
+	      tree clauses, c, c_next = NULL, c_prev = NULL;
+
+	      if (!attrs)
+		break;
+
+	      clauses = TREE_VALUE (attrs);
+
+	      for (c = clauses; c; c_prev = c, c = c_next)
+		{
+		  c_next = OMP_CLAUSE_CHAIN (c);
+
+		  if (t == OMP_CLAUSE_DECL (c))
+		    {
+		      if (ret_clauses)
+			OMP_CLAUSE_CHAIN (c) = ret_clauses;
+
+		      ret_clauses = c;
+
+		      if (c_prev == NULL)
+			clauses = c_next;
+		      else
+			OMP_CLAUSE_CHAIN (c_prev) = c_next;
+		    }
+		}
+
+	      if (clauses == NULL)
+		{
+		  DECL_ATTRIBUTES (current_function_decl) =
+			    remove_attribute ("oacc declare returns",
+				    DECL_ATTRIBUTES (current_function_decl));
+		}
+	    }
 	}
     }
 
+  if (ret_clauses)
+    {
+      gomp_target *stmt;
+      gimple_stmt_iterator si = gsi_start (cleanup);
+
+      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				      ret_clauses);
+      gsi_insert_seq_before_without_update (&si, stmt, GSI_NEW_STMT);
+    }
+
   if (cleanup)
     {
       gtry *gs;
@@ -5792,6 +5840,26 @@ omp_notice_threadprivate_variable (struct gimplify_omp_ctx *ctx, tree decl,
   return false;
 }
 
+/* Return true if global var DECL is device resident.  */
+
+static bool
+device_resident_p (tree decl)
+{
+  tree attr = lookup_attribute ("oacc declare target", DECL_ATTRIBUTES (decl));
+
+  if (!attr)
+    return false;
+
+  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
+    {
+      tree c = TREE_VALUE (t);
+      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
+	return true;
+    }
+
+  return false;
+}
+
 /* Determine outer default flags for DECL mentioned in an OMP region
    but not declared in an enclosing clause.
 
@@ -5841,6 +5909,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
       flags |= GOVD_FIRSTPRIVATE;
       break;
     case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
+      if (is_global_var (decl) && device_resident_p (decl))
+	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
       /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED.  */
       gcc_assert ((ctx->region_type & ORT_TASK) != 0);
       if (struct gimplify_omp_ctx *octx = ctx->outer_context)
@@ -7712,6 +7782,37 @@ gimplify_oacc_cache (tree *expr_p, gimple_seq *pre_p)
   *expr_p = NULL_TREE;
 }
 
+/* Gimplify OACC_DECLARE.  */
+
+static void
+gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
+{
+  tree expr = *expr_p;
+  gomp_target *stmt;
+  tree clauses, t;
+
+  clauses = OACC_DECLARE_CLAUSES (expr);
+
+  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      tree decl = OMP_CLAUSE_DECL (t);
+
+      if (TREE_CODE (decl) == MEM_REF)
+	continue;
+
+      omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
+    }
+
+  stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				  clauses);
+
+  gimplify_seq_add_stmt (pre_p, stmt);
+
+  *expr_p = NULL_TREE;
+}
+
 /* Gimplify the contents of an OMP_PARALLEL statement.  This involves
    gimplification of the body, as well as scanning the body for used
    variables.  We need to do this scan now, because variable-sized
@@ -10063,11 +10164,15 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  break;
 
 	case OACC_HOST_DATA:
-	case OACC_DECLARE:
 	  sorry ("directive not yet implemented");
 	  ret = GS_ALL_DONE;
 	  break;
 
+	case OACC_DECLARE:
+	  gimplify_oacc_declare (expr_p, pre_p);
+	  ret = GS_ALL_DONE;
+	  break;
+
 	case OACC_DATA:
 	case OACC_KERNELS:
 	case OACC_PARALLEL:
diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def
index fc87a3f..0365bc4 100644
--- a/gcc/omp-builtins.def
+++ b/gcc/omp-builtins.def
@@ -357,3 +357,5 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TARGET_ENTER_EXIT_DATA,
 		  BT_FN_VOID_INT_SIZE_PTR_PTR_PTR_UINT_PTR, ATTR_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS, "GOMP_teams",
 		  BT_FN_VOID_UINT_UINT, ATTR_NOTHROW_LIST)
+DEF_GOACC_BUILTIN (BUILT_IN_GOACC_DECLARE, "GOACC_declare",
+		   BT_FN_VOID_INT_SIZE_PTR_PTR_PTR, ATTR_NOTHROW_LIST)
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 5ffb276..0119e44 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -12344,6 +12344,7 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -12587,6 +12588,9 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       start_ix = BUILT_IN_GOACC_ENTER_EXIT_DATA;
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      start_ix = BUILT_IN_GOACC_DECLARE;
+      break;
     default:
       gcc_unreachable ();
     }
@@ -12709,6 +12713,7 @@ expand_omp_target (struct omp_region *region)
   switch (start_ix)
     {
     case BUILT_IN_GOACC_DATA_START:
+    case BUILT_IN_GOACC_DECLARE:
     case BUILT_IN_GOMP_TARGET_DATA:
       break;
     case BUILT_IN_GOMP_TARGET:
@@ -13023,6 +13028,7 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent,
 		case GF_OMP_TARGET_KIND_EXIT_DATA:
 		case GF_OMP_TARGET_KIND_OACC_UPDATE:
 		case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+		case GF_OMP_TARGET_KIND_OACC_DECLARE:
 		  /* ..., other than for those stand-alone directives...  */
 		  region = NULL;
 		  break;
@@ -14806,6 +14812,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -14876,6 +14883,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	  case GOMP_MAP_FORCE_TOFROM:
 	  case GOMP_MAP_FORCE_PRESENT:
 	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_LINK:
 	    gcc_assert (is_gimple_omp_oacc (stmt));
 	    break;
 	  default:
@@ -16542,6 +16551,7 @@ make_gimple_omp_edges (basic_block bb, struct omp_region **region,
 	case GF_OMP_TARGET_KIND_EXIT_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  cur_region = cur_region->outer;
 	  break;
 	default:
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
new file mode 100644
index 0000000..b036c63
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -0,0 +1,83 @@
+/* Test valid uses of declare directive.  */
+/* { dg-do compile } */
+
+int v0;
+#pragma acc declare create(v0)
+
+int v1;
+#pragma acc declare copyin(v1)
+
+int *v2;
+#pragma acc declare deviceptr(v2)
+
+int v3;
+#pragma acc declare device_resident(v3)
+
+int v4;
+#pragma acc declare link(v4)
+
+int v5, v6, v7, v8;
+#pragma acc declare create(v5, v6) copyin(v7, v8)
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare create(va0)
+
+  int va1;
+#pragma acc declare copyin(va1)
+
+  int *va2;
+#pragma acc declare deviceptr(va2)
+
+  int va3;
+#pragma acc declare device_resident(va3)
+
+  extern int ve0;
+#pragma acc declare create(ve0)
+
+  extern int ve1;
+#pragma acc declare copyin(ve1)
+
+  extern int *ve2;
+#pragma acc declare deviceptr(ve2)
+
+  extern int ve3;
+#pragma acc declare device_resident(ve3)
+
+  extern int ve4;
+#pragma acc declare link(ve4)
+
+  int va5;
+#pragma acc declare copy(va5)
+
+  int va6;
+#pragma acc declare copyout(va6)
+
+  int va7;
+#pragma acc declare present(va7)
+
+  int va8;
+#pragma acc declare present_or_copy(va8)
+
+  int va9;
+#pragma acc declare present_or_copyin(va9)
+
+  int va10;
+#pragma acc declare present_or_copyout(va10)
+
+  int va11;
+#pragma acc declare present_or_create(va11)
+
+ a:
+  {
+    int va0;
+#pragma acc declare create(va0)
+    if (v1)
+      goto a;
+    else
+      goto b;
+  }
+ b:;
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
new file mode 100644
index 0000000..7979f0c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -0,0 +1,68 @@
+/* Test invalid uses of declare directive.  */
+/* { dg-do compile } */
+
+#pragma acc declare /* { dg-error "no valid clauses" } */
+
+#pragma acc declare create(undeclared) /* { dg-error "undeclared" } */
+/* { dg-error "no valid clauses" "second error" { target *-*-* } 6 } */
+
+int v0[10];
+#pragma acc declare create(v0[1:3]) /* { dg-error "subarray" } */
+
+int v1;
+#pragma acc declare create(v1, v1) /* { dg-error "more than once" } */
+
+int v2;
+#pragma acc declare create(v2) /* { dg-message "previous directive" } */
+#pragma acc declare copyin(v2) /* { dg-error "more than once" } */
+
+int v3;
+#pragma acc declare copy(v3) /* { dg-error "at file scope" } */
+
+int v4;
+#pragma acc declare copyout(v4) /* { dg-error "at file scope" } */
+
+int v5;
+#pragma acc declare present(v5) /* { dg-error "at file scope" } */
+
+int v6;
+#pragma acc declare present_or_copy(v6) /* { dg-error "at file scope" } */
+
+int v7;
+#pragma acc declare present_or_copyin(v7) /* { dg-error "at file scope" } */
+
+int v8;
+#pragma acc declare present_or_copyout(v8) /* { dg-error "at file scope" } */
+
+int v9;
+#pragma acc declare present_or_create(v9) /* { dg-error "at file scope" } */
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare link(va0) /* { dg-error "global variable" } */
+
+  extern int ve0;
+#pragma acc declare copy(ve0) /* { dg-error "invalid use of" } */
+
+  extern int ve1;
+#pragma acc declare copyout(ve1) /* { dg-error "invalid use of" } */
+
+  extern int ve2;
+#pragma acc declare present(ve2) /* { dg-error "invalid use of" } */
+
+  extern int ve3;
+#pragma acc declare present_or_copy(ve3) /* { dg-error "invalid use of" } */
+
+  extern int ve4;
+#pragma acc declare present_or_copyin(ve4) /* { dg-error "invalid use of" } */
+
+  extern int ve5;
+#pragma acc declare present_or_copyout(ve5) /* { dg-error "invalid use of" } */
+
+  extern int ve6;
+#pragma acc declare present_or_create(ve6) /* { dg-error "invalid use of" } */
+
+#pragma acc declare present (v9) /* { dg-error "invalid use of" } */
+}
diff --git a/gcc/tree.h b/gcc/tree.h
index 6768b3b..a84d11a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1232,6 +1232,8 @@ extern void protected_set_expr_location (tree, location_t);
 
 #define OACC_DECLARE_CLAUSES(NODE) \
   TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 0)
+#define OACC_DECLARE_RETURN_CLAUSES(NODE) \
+  TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 1)
 
 #define OACC_ENTER_DATA_CLAUSES(NODE) \
   TREE_OPERAND (OACC_ENTER_DATA_CHECK (NODE), 0)
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 7671dd7..dffd631 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -72,6 +72,11 @@ enum gomp_map_kind
        POINTER_SIZE_UNITS.  */
     GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
     /* Do not map, copy bits for firstprivate instead.  */
+    /* OpenACC device_resident.  */
+    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
+    /* OpenACC link.  */
+    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
+    /* Allocate.  */
     GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
     /* Similarly, but store the value in the pointer rather than
        pointed by the pointer.  */
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 39faba9..d16710f 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -392,6 +392,7 @@ GOACC_2.0 {
 
 GOACC_2.0.1 {
   global:
+	GOACC_declare;
 	GOACC_parallel_keyed;
 } GOACC_2.0;
 
diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index 525846b..9de9e55 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -501,3 +501,62 @@ GOACC_get_thread_num (void)
 {
   return 0;
 }
+
+void
+GOACC_declare (int device, size_t mapnum,
+	       void **hostaddrs, size_t *sizes, unsigned short *kinds)
+{
+  int i;
+
+  for (i = 0; i < mapnum; i++)
+    {
+      unsigned char kind = kinds[i] & 0xff;
+
+      if (kind == GOMP_MAP_POINTER || kind == GOMP_MAP_TO_PSET)
+	continue;
+
+      switch (kind)
+	{
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_DEALLOC:
+	  case GOMP_MAP_FORCE_FROM:
+	  case GOMP_MAP_FORCE_TO:
+	  case GOMP_MAP_POINTER:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	    break;
+
+	  case GOMP_MAP_ALLOC:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      {
+		GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	      }
+	    break;
+
+	  case GOMP_MAP_TO:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    kinds[i] = GOMP_MAP_FORCE_FROM;
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_PRESENT:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      gomp_fatal ("[%p,%zd] is not mapped", hostaddrs[i], sizes[i]);
+	    break;
+
+	  default:
+	    assert (0);
+	    break;
+	}
+    }
+}
diff --git a/libgomp/testsuite/declare-1.c b/libgomp/testsuite/declare-1.c
new file mode 100644
index 0000000..8fbec4d
--- /dev/null
+++ b/libgomp/testsuite/declare-1.c
@@ -0,0 +1,122 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <openacc.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define N 8
+
+void
+subr2 (int *a)
+{
+  int i;
+  int f[N];
+#pragma acc declare copyout (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i] + f[i];
+      }
+  }
+}
+
+void
+subr1 (int *a)
+{
+  int f[N];
+#pragma acc declare copy (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    int i;
+
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i];
+      }
+  }
+}
+
+int b[8];
+#pragma acc declare create (b)
+
+int d[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+#pragma acc declare copyin (d)
+
+int
+main (int argc, char **argv)
+{
+  int a[N];
+  int e[N];
+#pragma acc declare create (e)
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = i + 1;
+
+  if (!acc_is_present (&b, sizeof (b)))
+    abort ();
+
+  if (!acc_is_present (&d, sizeof (d)))
+    abort ();
+
+  if (!acc_is_present (&e, sizeof (e)))
+    abort ();
+
+#pragma acc parallel copyin (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        b[i] = a[i];
+        a[i] = b[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != i + 1)
+	abort ();
+    }
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+        e[i] = a[i] + d[i];
+	a[i] = e[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != (i + 1) * 2)
+	abort ();
+    }
+
+  for (i = 0; i < N; i++)
+    {
+      a[i] = 1234;
+    }
+
+  subr1 (&a[0]);
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != 1234 * 2)
+	abort ();
+    }
+
+  subr2 (&a[0]);
+
+  for (i = 0; i < 1; i++)
+    {
+      if (a[i] != 1234 * 6)
+	abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/declare-5.c b/libgomp/testsuite/declare-5.c
new file mode 100644
index 0000000..1e2f6ce
--- /dev/null
+++ b/libgomp/testsuite/declare-5.c
@@ -0,0 +1,13 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+int
+main (int argc, char **argv)
+{
+  int a[8] __attribute__((unused));
+
+  __builtin_printf ("CheCKpOInT\n");
+#pragma acc declare present (a)
+}
+
+/* { dg-output "CheCKpOInT" } */
+/* { dg-shouldfail "" } */

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-09 16:01           ` James Norris
@ 2015-11-09 16:21             ` Jakub Jelinek
  2015-11-09 16:31               ` James Norris
  2015-11-09 23:11               ` James Norris
  0 siblings, 2 replies; 51+ messages in thread
From: Jakub Jelinek @ 2015-11-09 16:21 UTC (permalink / raw)
  To: James Norris; +Cc: Thomas Schwinge, Joseph S. Myers, GCC Patches

On Mon, Nov 09, 2015 at 10:01:32AM -0600, James Norris wrote:
> +      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))

Here you only look up "omp declare target", not "omp declare target link".
So, what happens if you mix that (once in some copy clause, once in link),
or mention twice in link, etc.?  Needs testsuite coverage and clear rules.

> +	  DECL_ATTRIBUTES (decl) =
> +			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));

Incorrect formatting, = goes already on the following line, no whitespace
at end of line, and next line is indented below CL from DECL.

> +	  t = build_omp_clause (OMP_CLAUSE_LOCATION (c) , OMP_CLAUSE_MAP);

Wrong formatting, no space before ,.

> +    if (ret_clauses)
> +      {
> +	tree fndecl = current_function_decl;
> +	tree attrs = lookup_attribute ("oacc declare returns",
> +				       DECL_ATTRIBUTES (fndecl));

Why do you use an attribute for this?  I think adding the automatic
vars to hash_map during gimplification of the OACC_DECLARE is best.

> +	    tree id = get_identifier ("oacc declare returns");
> +	    DECL_ATTRIBUTES (fndecl) =
> +			tree_cons (id, ret_clauses, DECL_ATTRIBUTES (fndecl));

Formatting error.

> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c
> @@ -1065,6 +1065,7 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
>    gimple_seq body, cleanup;
>    gcall *stack_save;
>    location_t start_locus = 0, end_locus = 0;
> +  tree ret_clauses = NULL;
>  
>    tree temp = voidify_wrapper_expr (bind_expr, NULL);
>  
> @@ -1166,9 +1167,56 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
>  	  clobber_stmt = gimple_build_assign (t, clobber);
>  	  gimple_set_location (clobber_stmt, end_locus);
>  	  gimplify_seq_add_stmt (&cleanup, clobber_stmt);
> +
> +	  if (flag_openacc)
> +	    {
> +	      tree attrs = lookup_attribute ("oacc declare returns",
> +				       DECL_ATTRIBUTES (current_function_decl));
> +	      tree clauses, c, c_next = NULL, c_prev = NULL;
> +
> +	      if (!attrs)
> +		break;
> +
> +	      clauses = TREE_VALUE (attrs);
> +
> +	      for (c = clauses; c; c_prev = c, c = c_next)
> +		{
> +		  c_next = OMP_CLAUSE_CHAIN (c);
> +
> +		  if (t == OMP_CLAUSE_DECL (c))
> +		    {
> +		      if (ret_clauses)
> +			OMP_CLAUSE_CHAIN (c) = ret_clauses;
> +
> +		      ret_clauses = c;
> +
> +		      if (c_prev == NULL)
> +			clauses = c_next;
> +		      else
> +			OMP_CLAUSE_CHAIN (c_prev) = c_next;
> +		    }
> +		}

This doesn't really scale.  Consider 10000 clauses on various
oacc declare constructs in a single function, and 1000000 automatic
variables in such a function.
So, what I'm suggesting is during gimplification of OACC_DECLARE,
if you find a clause on an automatic variable in the current function
that you want to unmap afterwards, have a
static hash_map<tree, tree> *oacc_declare_returns;
and you just add into the hash map the VAR_DECL -> the clause you want,
then in this spot you check
  if (oacc_declare_returns)
    {
      clause = lookup in hash_map (t);
      if (clause)
	{
	  ...
	}
    }

> +
> +	      if (clauses == NULL)
> +		{
> +		  DECL_ATTRIBUTES (current_function_decl) =
> +			    remove_attribute ("oacc declare returns",
> +				    DECL_ATTRIBUTES (current_function_decl));

Wrong formatting.

	Jakub

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-09 16:21             ` Jakub Jelinek
@ 2015-11-09 16:31               ` James Norris
  2015-11-09 23:11               ` James Norris
  1 sibling, 0 replies; 51+ messages in thread
From: James Norris @ 2015-11-09 16:31 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Thomas Schwinge, Joseph S. Myers, GCC Patches

Jakub,

On 11/09/2015 10:21 AM, Jakub Jelinek wrote:
> On Mon, Nov 09, 2015 at 10:01:32AM -0600, James Norris wrote:
>> +      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
> Here you only look up "omp declare target", not "omp declare target link".
> So, what happens if you mix that (once in some copy clause, once in link),
> or mention twice in link, etc.?  Needs testsuite coverage and clear rules.

Will fix.

>
>> +	  DECL_ATTRIBUTES (decl) =
>> +			tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
> Incorrect formatting, = goes already on the following line, no whitespace
> at end of line, and next line is indented below CL from DECL.

Will fix.

>
>> +	  t = build_omp_clause (OMP_CLAUSE_LOCATION (c) , OMP_CLAUSE_MAP);
> Wrong formatting, no space before ,.

Will fix.

>> +    if (ret_clauses)
>> +      {
>> +	tree fndecl = current_function_decl;
>> +	tree attrs = lookup_attribute ("oacc declare returns",
>> +				       DECL_ATTRIBUTES (fndecl));
> Why do you use an attribute for this?  I think adding the automatic
> vars to hash_map during gimplification of the OACC_DECLARE is best.

See below (This doesn't scale...)

>
>> +	    tree id = get_identifier ("oacc declare returns");
>> +	    DECL_ATTRIBUTES (fndecl) =
>> +			tree_cons (id, ret_clauses, DECL_ATTRIBUTES (fndecl));
> Formatting error.

Will fix.

>
>> --- a/gcc/gimplify.c
>> +++ b/gcc/gimplify.c
>> @@ -1065,6 +1065,7 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
>>     gimple_seq body, cleanup;
>>     gcall *stack_save;
>>     location_t start_locus = 0, end_locus = 0;
>> +  tree ret_clauses = NULL;
>>   
>>     tree temp = voidify_wrapper_expr (bind_expr, NULL);
>>   
>> @@ -1166,9 +1167,56 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
>>   	  clobber_stmt = gimple_build_assign (t, clobber);
>>   	  gimple_set_location (clobber_stmt, end_locus);
>>   	  gimplify_seq_add_stmt (&cleanup, clobber_stmt);
>> +
>> +	  if (flag_openacc)
>> +	    {
>> +	      tree attrs = lookup_attribute ("oacc declare returns",
>> +				       DECL_ATTRIBUTES (current_function_decl));
>> +	      tree clauses, c, c_next = NULL, c_prev = NULL;
>> +
>> +	      if (!attrs)
>> +		break;
>> +
>> +	      clauses = TREE_VALUE (attrs);
>> +
>> +	      for (c = clauses; c; c_prev = c, c = c_next)
>> +		{
>> +		  c_next = OMP_CLAUSE_CHAIN (c);
>> +
>> +		  if (t == OMP_CLAUSE_DECL (c))
>> +		    {
>> +		      if (ret_clauses)
>> +			OMP_CLAUSE_CHAIN (c) = ret_clauses;
>> +
>> +		      ret_clauses = c;
>> +
>> +		      if (c_prev == NULL)
>> +			clauses = c_next;
>> +		      else
>> +			OMP_CLAUSE_CHAIN (c_prev) = c_next;
>> +		    }
>> +		}
> This doesn't really scale.  Consider 10000 clauses on various
> oacc declare constructs in a single function, and 1000000 automatic
> variables in such a function.
> So, what I'm suggesting is during gimplification of OACC_DECLARE,
> if you find a clause on an automatic variable in the current function
> that you want to unmap afterwards, have a
> static hash_map<tree, tree> *oacc_declare_returns;
> and you just add into the hash map the VAR_DECL -> the clause you want,
> then in this spot you check
>    if (oacc_declare_returns)
>      {
>        clause = lookup in hash_map (t);
>        if (clause)
> 	{
> 	  ...
> 	}
>      }

Now I see what you were getting at in using the hash_map. I didn't
consider creating a static hash_map and populating it as you suggest.

Thank you!

>
>> +
>> +	      if (clauses == NULL)
>> +		{
>> +		  DECL_ATTRIBUTES (current_function_decl) =
>> +			    remove_attribute ("oacc declare returns",
>> +				    DECL_ATTRIBUTES (current_function_decl));
> Wrong formatting.

Will fix.

>
> 	Jakub

Thanks for taking the time to review.

Jim


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-09 16:21             ` Jakub Jelinek
  2015-11-09 16:31               ` James Norris
@ 2015-11-09 23:11               ` James Norris
  2015-11-11  8:32                 ` Jakub Jelinek
  1 sibling, 1 reply; 51+ messages in thread
From: James Norris @ 2015-11-09 23:11 UTC (permalink / raw)
  To: Jakub Jelinek, James Norris; +Cc: Thomas Schwinge, Joseph S. Myers, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 368 bytes --]

Jakub,


The attached patch and ChangeLog reflects the updates from your
review: https://gcc.gnu.org/ml/gcc-patches/2015-11/msg01002.html

All of the issues you pointed out have been addressed. I've also
added a test that uses C++ templates. A bug was also fixed in the
parsers which dealt with determining which identifier to use  with
an attribute.

Thanks!
Jim





[-- Attachment #2: ChangeLog --]
[-- Type: text/plain, Size: 2731 bytes --]

2015-XX-XX  James Norris  <jnorris@codesourcery.com>
	    Joseph Myers  <joseph@codesourcery.com>

	gcc/c-family/
	* c-pragma.c (oacc_pragmas): Add entry for declare directive. 
	* c-pragma.h (enum pragma_kind): Add PRAGMA_OACC_DECLARE.
	(enum pragma_omp_clause): Add PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT and
	PRAGMA_OACC_CLAUSE_LINK.

	gcc/c/
	* c-parser.c (c_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	(c_parser_omp_clause_name): Handle 'device_resident' clause.
	(c_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(c_parser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OACC_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(c_parser_oacc_declare): New function.

	gcc/cp/
	* parser.c (cp_parser_omp_clause_name): Handle 'device_resident'
	clause.
	(cp_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(cp_paser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(cp_parser_oacc_declare): New function.
	(cp_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	* pt.c (tsubst_expr): Handle OACC_DECLARE.

	gcc/
	* gimple-pretty-print.c (dump_gimple_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE. 
	* gimple.h (enum gf_mask): Add GF_OMP_TARGET_KIND_OACC_DECLARE.
	(is_gomple_omp_oacc): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* gimplify.c (oacc_declare_returns): New.
	(gimplify_bind_expr): Prepend 'exit' stmt to cleanup.
	(device_resident_p): New function.
	(omp_default_clause): Handle device_resident clause.
	(gimplify_oacc_declare_1, gimplify_oacc_declare): New functions.
	(gimplify_expr): Handle OACC_DECLARE.
	* omp-builtins.def (BUILT_IN_GOACC_DECLARE): New builtin.
	* omp-low.c (expand_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE and BUILTIN_GOACC_DECLARE.
	(build_omp_regions_1): Handlde GF_OMP_TARGET_KIND_OACC_DECLARE.
	(lower_omp_target): Handle GF_OMP_TARGET_KIND_OACC_DECLARE,
	GOMP_MAP_DEVICE_RESIDENT and GOMP_MAP_LINK.
	(make_gimple_omp_edges): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* tree-pretty-print.c (dump_omp_clause): Handle GOMP_MAP_LINK and
	GOMP_MAP_DEVICE_RESIDENT.

	gcc/testsuite
	* c-c++-common/goacc/declare-1.c: New test.
	* c-c++-common/goacc/declare-2.c: Likewise.

	include/
	* gomp-constants.h (enum gomp_map_kind): Add GOMP_MAP_DEVICE_RESIDENT
	and GOMP_MAP_LINK.

	libgomp/

	* libgomp.map (GOACC_2.0.1): Export GOACC_declare.
	* oacc-parallel.c (GOACC_declare): New function.
	* testsuite/libgomp.oacc-c-c++-common/declare-1.c: New test.
	* testsuite/libgomp.oacc-c-c++-common/declare-5.c: Likewise.
	* testsuite/libgomp.oacc-c++/declare-1.C: Likewise.

[-- Attachment #3: declare.patch --]
[-- Type: text/x-patch, Size: 36379 bytes --]

diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
index ac11838..cd0cc27 100644
--- a/gcc/c-family/c-pragma.c
+++ b/gcc/c-family/c-pragma.c
@@ -1207,6 +1207,7 @@ static const struct omp_pragma_def oacc_pragmas[] = {
   { "atomic", PRAGMA_OACC_ATOMIC },
   { "cache", PRAGMA_OACC_CACHE },
   { "data", PRAGMA_OACC_DATA },
+  { "declare", PRAGMA_OACC_DECLARE },
   { "enter", PRAGMA_OACC_ENTER_DATA },
   { "exit", PRAGMA_OACC_EXIT_DATA },
   { "kernels", PRAGMA_OACC_KERNELS },
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 953c4e3..c6a2981 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -30,6 +30,7 @@ enum pragma_kind {
   PRAGMA_OACC_ATOMIC,
   PRAGMA_OACC_CACHE,
   PRAGMA_OACC_DATA,
+  PRAGMA_OACC_DECLARE,
   PRAGMA_OACC_ENTER_DATA,
   PRAGMA_OACC_EXIT_DATA,
   PRAGMA_OACC_KERNELS,
@@ -151,6 +152,7 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_CREATE,
   PRAGMA_OACC_CLAUSE_DELETE,
   PRAGMA_OACC_CLAUSE_DEVICEPTR,
+  PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT,
   PRAGMA_OACC_CLAUSE_GANG,
   PRAGMA_OACC_CLAUSE_HOST,
   PRAGMA_OACC_CLAUSE_INDEPENDENT,
@@ -175,7 +177,8 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_FIRSTPRIVATE = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE,
   PRAGMA_OACC_CLAUSE_IF = PRAGMA_OMP_CLAUSE_IF,
   PRAGMA_OACC_CLAUSE_PRIVATE = PRAGMA_OMP_CLAUSE_PRIVATE,
-  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION
+  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION,
+  PRAGMA_OACC_CLAUSE_LINK = PRAGMA_OMP_CLAUSE_LINK
 };
 
 extern struct cpp_reader* parse_in;
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 23d0107..90affcf 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1231,6 +1231,7 @@ static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
 					     unsigned int * = NULL);
+static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
 static void c_parser_omp_construct (c_parser *);
@@ -9697,6 +9698,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context)
 
   switch (id)
     {
+    case PRAGMA_OACC_DECLARE:
+      c_parser_oacc_declare (parser);
+      return false;
+
     case PRAGMA_OACC_ENTER_DATA:
       c_parser_oacc_enter_exit_data (parser, true);
       return false;
@@ -9982,6 +9987,8 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -10418,10 +10425,16 @@ c_parser_oacc_data_clause (c_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -12703,6 +12716,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = c_parser_omp_clause_firstprivate (parser, clauses);
 	  c_name = "firstprivate";
@@ -12725,6 +12742,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 						clauses);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  clauses = c_parser_omp_clause_num_gangs (parser, clauses);
 	  c_name = "num_gangs";
@@ -13182,6 +13203,161 @@ c_parser_oacc_data (location_t loc, c_parser *parser)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE) )
+
+static void
+c_parser_oacc_declare (c_parser *parser)
+{
+  location_t pragma_loc = c_parser_peek_token (parser)->location;
+  tree clauses, stmt, t, decl;
+
+  bool error = false;
+
+  c_parser_consume_pragma (parser);
+
+  clauses = c_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+				       "#pragma acc declare");
+  if (!clauses)
+    {
+      error_at (pragma_loc,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return;
+    }
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      decl = OMP_CLAUSE_DECL (t);
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "array section in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p ()
+	      && (TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl))
+	  || lookup_attribute ("omp declare target link",
+			       DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_MAP_KIND (t) == GOMP_MAP_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl)
+			   = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+
+	  if (global_bindings_p ())
+	    {
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
+	    }
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return;
+
+  stmt = make_node (OACC_DECLARE);
+  TREE_TYPE (stmt) = void_type_node;
+  OACC_DECLARE_CLAUSES (stmt) = clauses;
+  SET_EXPR_LOCATION (stmt, pragma_loc);
+
+  add_stmt (stmt);
+
+  return;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c6f5729..a4c9763 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -29099,6 +29099,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -29512,10 +29514,16 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -31516,6 +31524,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  clauses = cp_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_FIRSTPRIVATE,
 					    clauses);
@@ -31540,6 +31552,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 						  clauses, here);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  code = OMP_CLAUSE_NUM_GANGS;
 	  c_name = "num_gangs";
@@ -34497,6 +34513,158 @@ cp_parser_oacc_data (cp_parser *parser, cp_token *pragma_tok)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE))
+
+static tree
+cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
+{
+  tree clauses, stmt, t;
+  bool error = false;
+
+  clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+					"#pragma acc declare", pragma_tok, true);
+
+
+  if (find_omp_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
+    {
+      error_at (pragma_tok->location,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return NULL_TREE;
+    }
+
+  for (tree t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      tree decl = OMP_CLAUSE_DECL (t);
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "array section in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+      gcc_assert (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_MAP);
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p ()
+	      && (TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl))
+	  || lookup_attribute ("omp declare target link",
+			       DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_MAP_KIND (t) == GOMP_MAP_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl)
+			   = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+	  if (global_bindings_p ())
+	    {
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
+	    }
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return NULL_TREE;
+
+  stmt = make_node (OACC_DECLARE);
+  TREE_TYPE (stmt) = void_type_node;
+  OACC_DECLARE_CLAUSES (stmt) = clauses;
+  SET_EXPR_LOCATION (stmt, pragma_tok->location);
+
+  add_stmt (stmt);
+
+  return NULL_TREE;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
@@ -36183,6 +36351,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context)
       cp_parser_omp_declare (parser, pragma_tok, context);
       return false;
 
+    case PRAGMA_OACC_DECLARE:
+      cp_parser_oacc_declare (parser, pragma_tok);
+      return false;
+
     case PRAGMA_OACC_ATOMIC:
     case PRAGMA_OACC_CACHE:
     case PRAGMA_OACC_DATA:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 45eda3a..ff27914 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15422,6 +15422,14 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       add_stmt (t);
       break;
 
+    case OACC_DECLARE:
+      t = copy_node (t);
+      tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_CLAUSES (t) = tmp;
+      add_stmt (t);
+      break;
+
     case OMP_TARGET_UPDATE:
     case OMP_TARGET_ENTER_DATA:
     case OMP_TARGET_EXIT_DATA:
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 7b50cdf..7764201 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -1353,6 +1353,9 @@ dump_gimple_omp_target (pretty_printer *buffer, gomp_target *gs,
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       kind = " oacc_enter_exit_data";
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      kind = " oacc_declare";
+      break;
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 781801b..e45162d 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -170,6 +170,7 @@ enum gf_mask {
     GF_OMP_TARGET_KIND_OACC_DATA = 7,
     GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
     GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
+    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,
 
     /* True on an GIMPLE_OMP_RETURN statement if the return does not require
        a thread synchronization via some sort of barrier.  The exact barrier
@@ -6004,6 +6005,7 @@ is_gimple_omp_oacc (const gimple *stmt)
 	case GF_OMP_TARGET_KIND_OACC_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  return true;
 	default:
 	  return false;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index fa34858..791be44 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -164,6 +164,7 @@ static struct gimplify_omp_ctx *gimplify_omp_ctxp;
 
 /* Forward declaration.  */
 static enum gimplify_status gimplify_compound_expr (tree *, gimple_seq *, bool);
+static hash_map<tree, tree> *oacc_declare_returns;
 
 /* Shorter alias name for the above function for use in gimplify.c
    only.  */
@@ -1065,6 +1066,7 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
   gimple_seq body, cleanup;
   gcall *stack_save;
   location_t start_locus = 0, end_locus = 0;
+  tree ret_clauses = NULL;
 
   tree temp = voidify_wrapper_expr (bind_expr, NULL);
 
@@ -1166,9 +1168,39 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 	  clobber_stmt = gimple_build_assign (t, clobber);
 	  gimple_set_location (clobber_stmt, end_locus);
 	  gimplify_seq_add_stmt (&cleanup, clobber_stmt);
+
+	  if (flag_openacc && oacc_declare_returns != NULL)
+	    {
+	      tree *c = oacc_declare_returns->get (t);
+	      if (c != NULL)
+		{
+		  if (ret_clauses)
+		    OMP_CLAUSE_CHAIN (*c) = ret_clauses;
+
+		  ret_clauses = *c;
+
+		  oacc_declare_returns->remove (t);
+
+		  if (oacc_declare_returns->elements () == 0)
+		    {
+		      delete oacc_declare_returns;
+		      oacc_declare_returns = NULL;
+		    }
+		}
+	    }
 	}
     }
 
+  if (ret_clauses)
+    {
+      gomp_target *stmt;
+      gimple_stmt_iterator si = gsi_start (cleanup);
+
+      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				      ret_clauses);
+      gsi_insert_seq_before_without_update (&si, stmt, GSI_NEW_STMT);
+    }
+
   if (cleanup)
     {
       gtry *gs;
@@ -5792,6 +5824,26 @@ omp_notice_threadprivate_variable (struct gimplify_omp_ctx *ctx, tree decl,
   return false;
 }
 
+/* Return true if global var DECL is device resident.  */
+
+static bool
+device_resident_p (tree decl)
+{
+  tree attr = lookup_attribute ("oacc declare target", DECL_ATTRIBUTES (decl));
+
+  if (!attr)
+    return false;
+
+  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
+    {
+      tree c = TREE_VALUE (t);
+      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
+	return true;
+    }
+
+  return false;
+}
+
 /* Determine outer default flags for DECL mentioned in an OMP region
    but not declared in an enclosing clause.
 
@@ -5841,6 +5893,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
       flags |= GOVD_FIRSTPRIVATE;
       break;
     case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
+      if (is_global_var (decl) && device_resident_p (decl))
+	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
       /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED.  */
       gcc_assert ((ctx->region_type & ORT_TASK) != 0);
       if (struct gimplify_omp_ctx *octx = ctx->outer_context)
@@ -7712,6 +7766,121 @@ gimplify_oacc_cache (tree *expr_p, gimple_seq *pre_p)
   *expr_p = NULL_TREE;
 }
 
+/* Helper function of gimplify_oacc_declare.  The helper's purpose is to,
+   if required, translate 'kind' in CLAUSE into an 'entry' kind and 'exit'
+   kind.  The entry kind will replace the one in CLAUSE, while the exit
+   kind will be used in a new omp_clause and returned to the caller.  */
+
+static tree
+gimplify_oacc_declare_1 (tree clause)
+{
+  HOST_WIDE_INT kind, new_op;
+  bool ret = false;
+  tree c = NULL;
+
+  kind = OMP_CLAUSE_MAP_KIND (clause);
+
+  switch (kind)
+    {
+      case GOMP_MAP_ALLOC:
+      case GOMP_MAP_FORCE_ALLOC:
+      case GOMP_MAP_FORCE_TO:
+	new_op = GOMP_MAP_FORCE_DEALLOC;
+	ret = true;
+	break;
+
+      case GOMP_MAP_FORCE_FROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_FORCE_ALLOC);
+	new_op = GOMP_MAP_FORCE_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_FORCE_TOFROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_FORCE_TO);
+	new_op = GOMP_MAP_FORCE_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_FROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_FORCE_ALLOC);
+	new_op = GOMP_MAP_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_TOFROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_TO);
+	new_op = GOMP_MAP_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_DEVICE_RESIDENT:
+      case GOMP_MAP_FORCE_DEVICEPTR:
+      case GOMP_MAP_FORCE_PRESENT:
+      case GOMP_MAP_LINK:
+      case GOMP_MAP_POINTER:
+      case GOMP_MAP_TO:
+	break;
+
+      default:
+	gcc_unreachable ();
+	break;
+    }
+
+  if (ret)
+    {
+      c = build_omp_clause (OMP_CLAUSE_LOCATION (clause), OMP_CLAUSE_MAP);
+      OMP_CLAUSE_SET_MAP_KIND (c, new_op);
+      OMP_CLAUSE_DECL (c) = OMP_CLAUSE_DECL (clause);
+    }
+
+  return c;
+}
+
+/* Gimplify OACC_DECLARE.  */
+
+static void
+gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
+{
+  tree expr = *expr_p;
+  gomp_target *stmt;
+  tree clauses, t;
+
+  clauses = OACC_DECLARE_CLAUSES (expr);
+
+  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      tree decl = OMP_CLAUSE_DECL (t);
+
+      if (TREE_CODE (decl) == MEM_REF)
+	continue;
+
+      if (TREE_CODE (decl) == VAR_DECL
+	  && !is_global_var (decl)
+	  && DECL_CONTEXT (decl) == current_function_decl)
+	{
+	  tree c = gimplify_oacc_declare_1 (t);
+	  if (c)
+	    {
+	      if (oacc_declare_returns == NULL)
+		oacc_declare_returns = new hash_map<tree, tree>;
+
+	      oacc_declare_returns->put (decl, c);
+	    }
+	}
+
+      omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
+    }
+
+  stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				  clauses);
+
+  gimplify_seq_add_stmt (pre_p, stmt);
+
+  *expr_p = NULL_TREE;
+}
+
 /* Gimplify the contents of an OMP_PARALLEL statement.  This involves
    gimplification of the body, as well as scanning the body for used
    variables.  We need to do this scan now, because variable-sized
@@ -10063,11 +10232,15 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  break;
 
 	case OACC_HOST_DATA:
-	case OACC_DECLARE:
 	  sorry ("directive not yet implemented");
 	  ret = GS_ALL_DONE;
 	  break;
 
+	case OACC_DECLARE:
+	  gimplify_oacc_declare (expr_p, pre_p);
+	  ret = GS_ALL_DONE;
+	  break;
+
 	case OACC_DATA:
 	case OACC_KERNELS:
 	case OACC_PARALLEL:
diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def
index fc87a3f..0365bc4 100644
--- a/gcc/omp-builtins.def
+++ b/gcc/omp-builtins.def
@@ -357,3 +357,5 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TARGET_ENTER_EXIT_DATA,
 		  BT_FN_VOID_INT_SIZE_PTR_PTR_PTR_UINT_PTR, ATTR_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS, "GOMP_teams",
 		  BT_FN_VOID_UINT_UINT, ATTR_NOTHROW_LIST)
+DEF_GOACC_BUILTIN (BUILT_IN_GOACC_DECLARE, "GOACC_declare",
+		   BT_FN_VOID_INT_SIZE_PTR_PTR_PTR, ATTR_NOTHROW_LIST)
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 5ffb276..0119e44 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -12344,6 +12344,7 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -12587,6 +12588,9 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       start_ix = BUILT_IN_GOACC_ENTER_EXIT_DATA;
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      start_ix = BUILT_IN_GOACC_DECLARE;
+      break;
     default:
       gcc_unreachable ();
     }
@@ -12709,6 +12713,7 @@ expand_omp_target (struct omp_region *region)
   switch (start_ix)
     {
     case BUILT_IN_GOACC_DATA_START:
+    case BUILT_IN_GOACC_DECLARE:
     case BUILT_IN_GOMP_TARGET_DATA:
       break;
     case BUILT_IN_GOMP_TARGET:
@@ -13023,6 +13028,7 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent,
 		case GF_OMP_TARGET_KIND_EXIT_DATA:
 		case GF_OMP_TARGET_KIND_OACC_UPDATE:
 		case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+		case GF_OMP_TARGET_KIND_OACC_DECLARE:
 		  /* ..., other than for those stand-alone directives...  */
 		  region = NULL;
 		  break;
@@ -14806,6 +14812,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -14876,6 +14883,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	  case GOMP_MAP_FORCE_TOFROM:
 	  case GOMP_MAP_FORCE_PRESENT:
 	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_LINK:
 	    gcc_assert (is_gimple_omp_oacc (stmt));
 	    break;
 	  default:
@@ -16542,6 +16551,7 @@ make_gimple_omp_edges (basic_block bb, struct omp_region **region,
 	case GF_OMP_TARGET_KIND_EXIT_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  cur_region = cur_region->outer;
 	  break;
 	default:
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
new file mode 100644
index 0000000..b036c63
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -0,0 +1,83 @@
+/* Test valid uses of declare directive.  */
+/* { dg-do compile } */
+
+int v0;
+#pragma acc declare create(v0)
+
+int v1;
+#pragma acc declare copyin(v1)
+
+int *v2;
+#pragma acc declare deviceptr(v2)
+
+int v3;
+#pragma acc declare device_resident(v3)
+
+int v4;
+#pragma acc declare link(v4)
+
+int v5, v6, v7, v8;
+#pragma acc declare create(v5, v6) copyin(v7, v8)
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare create(va0)
+
+  int va1;
+#pragma acc declare copyin(va1)
+
+  int *va2;
+#pragma acc declare deviceptr(va2)
+
+  int va3;
+#pragma acc declare device_resident(va3)
+
+  extern int ve0;
+#pragma acc declare create(ve0)
+
+  extern int ve1;
+#pragma acc declare copyin(ve1)
+
+  extern int *ve2;
+#pragma acc declare deviceptr(ve2)
+
+  extern int ve3;
+#pragma acc declare device_resident(ve3)
+
+  extern int ve4;
+#pragma acc declare link(ve4)
+
+  int va5;
+#pragma acc declare copy(va5)
+
+  int va6;
+#pragma acc declare copyout(va6)
+
+  int va7;
+#pragma acc declare present(va7)
+
+  int va8;
+#pragma acc declare present_or_copy(va8)
+
+  int va9;
+#pragma acc declare present_or_copyin(va9)
+
+  int va10;
+#pragma acc declare present_or_copyout(va10)
+
+  int va11;
+#pragma acc declare present_or_create(va11)
+
+ a:
+  {
+    int va0;
+#pragma acc declare create(va0)
+    if (v1)
+      goto a;
+    else
+      goto b;
+  }
+ b:;
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
new file mode 100644
index 0000000..e138a69
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -0,0 +1,79 @@
+/* Test invalid uses of declare directive.  */
+/* { dg-do compile } */
+
+#pragma acc declare /* { dg-error "no valid clauses" } */
+
+#pragma acc declare create(undeclared) /* { dg-error "undeclared" } */
+/* { dg-error "no valid clauses" "second error" { target *-*-* } 6 } */
+
+int v0[10];
+#pragma acc declare create(v0[1:3]) /* { dg-error "array section" } */
+
+int v1;
+#pragma acc declare create(v1, v1) /* { dg-error "more than once" } */
+
+int v2;
+#pragma acc declare create(v2) /* { dg-message "previous directive" } */
+#pragma acc declare copyin(v2) /* { dg-error "more than once" } */
+
+int v3;
+#pragma acc declare copy(v3) /* { dg-error "at file scope" } */
+
+int v4;
+#pragma acc declare copyout(v4) /* { dg-error "at file scope" } */
+
+int v5;
+#pragma acc declare present(v5) /* { dg-error "at file scope" } */
+
+int v6;
+#pragma acc declare present_or_copy(v6) /* { dg-error "at file scope" } */
+
+int v7;
+#pragma acc declare present_or_copyin(v7) /* { dg-error "at file scope" } */
+
+int v8;
+#pragma acc declare present_or_copyout(v8) /* { dg-error "at file scope" } */
+
+int v9;
+#pragma acc declare present_or_create(v9) /* { dg-error "at file scope" } */
+
+int va10;
+#pragma acc declare create (va10)
+#pragma acc declare link (va10) /* { dg-error "more than once" } */
+
+int va11;
+#pragma acc declare link (va11)
+#pragma acc declare link (va11) /* { dg-error "more than once" } */
+
+int va12;
+#pragma acc declare create (va12) link (va12) /* { dg-error "more than once" } */
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare link(va0) /* { dg-error "global variable" } */
+
+  extern int ve0;
+#pragma acc declare copy(ve0) /* { dg-error "invalid use of" } */
+
+  extern int ve1;
+#pragma acc declare copyout(ve1) /* { dg-error "invalid use of" } */
+
+  extern int ve2;
+#pragma acc declare present(ve2) /* { dg-error "invalid use of" } */
+
+  extern int ve3;
+#pragma acc declare present_or_copy(ve3) /* { dg-error "invalid use of" } */
+
+  extern int ve4;
+#pragma acc declare present_or_copyin(ve4) /* { dg-error "invalid use of" } */
+
+  extern int ve5;
+#pragma acc declare present_or_copyout(ve5) /* { dg-error "invalid use of" } */
+
+  extern int ve6;
+#pragma acc declare present_or_create(ve6) /* { dg-error "invalid use of" } */
+
+#pragma acc declare present (v9) /* { dg-error "invalid use of" } */
+}
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index b1685fd..bf056fd 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -654,6 +654,12 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, int flags)
 	case GOMP_MAP_ALWAYS_POINTER:
 	  pp_string (pp, "always_pointer");
 	  break;
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  pp_string (pp, "device_resident");
+	  break;
+	case GOMP_MAP_LINK:
+	  pp_string (pp, "link");
+	  break;
 	default:
 	  gcc_unreachable ();
 	}
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 7671dd7..dffd631 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -72,6 +72,11 @@ enum gomp_map_kind
        POINTER_SIZE_UNITS.  */
     GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
     /* Do not map, copy bits for firstprivate instead.  */
+    /* OpenACC device_resident.  */
+    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
+    /* OpenACC link.  */
+    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
+    /* Allocate.  */
     GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
     /* Similarly, but store the value in the pointer rather than
        pointed by the pointer.  */
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 39faba9..d16710f 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -392,6 +392,7 @@ GOACC_2.0 {
 
 GOACC_2.0.1 {
   global:
+	GOACC_declare;
 	GOACC_parallel_keyed;
 } GOACC_2.0;
 
diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index 525846b..9de9e55 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -501,3 +501,62 @@ GOACC_get_thread_num (void)
 {
   return 0;
 }
+
+void
+GOACC_declare (int device, size_t mapnum,
+	       void **hostaddrs, size_t *sizes, unsigned short *kinds)
+{
+  int i;
+
+  for (i = 0; i < mapnum; i++)
+    {
+      unsigned char kind = kinds[i] & 0xff;
+
+      if (kind == GOMP_MAP_POINTER || kind == GOMP_MAP_TO_PSET)
+	continue;
+
+      switch (kind)
+	{
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_DEALLOC:
+	  case GOMP_MAP_FORCE_FROM:
+	  case GOMP_MAP_FORCE_TO:
+	  case GOMP_MAP_POINTER:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	    break;
+
+	  case GOMP_MAP_ALLOC:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      {
+		GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	      }
+	    break;
+
+	  case GOMP_MAP_TO:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    kinds[i] = GOMP_MAP_FORCE_FROM;
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_PRESENT:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      gomp_fatal ("[%p,%zd] is not mapped", hostaddrs[i], sizes[i]);
+	    break;
+
+	  default:
+	    assert (0);
+	    break;
+	}
+    }
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c++/declare-1.C b/libgomp/testsuite/libgomp.oacc-c++/declare-1.C
new file mode 100644
index 0000000..0286955
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-c++/declare-1.C
@@ -0,0 +1,31 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <stdlib.h>
+
+template<class T>
+T foo()
+{
+  T a, b;
+  #pragma acc declare create (a)
+
+  #pragma acc parallel copyout (b)
+  {
+    a = 5;
+    b = a;
+  }
+
+  return b;
+}
+
+int
+main (void)
+{
+  int rc;
+
+  rc = foo<int>();
+
+  if (rc != 5)
+    abort ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
new file mode 100644
index 0000000..c63a68d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
@@ -0,0 +1,122 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <openacc.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define N 8
+
+void
+subr2 (int *a)
+{
+  int i;
+  int f[N];
+#pragma acc declare copyout (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i] + f[i];
+      }
+  }
+}
+
+void
+subr1 (int *a)
+{
+  int f[N];
+#pragma acc declare copy (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    int i;
+
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i];
+      }
+  }
+}
+
+int b[8];
+#pragma acc declare create (b)
+
+int d[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+#pragma acc declare copyin (d)
+
+int
+main (int argc, char **argv)
+{
+  int a[N];
+  int e[N];
+#pragma acc declare create (e)
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = i + 1;
+
+  if (!acc_is_present (&b, sizeof (b)))
+    abort ();
+
+  if (!acc_is_present (&d, sizeof (d)))
+    abort ();
+
+  if (!acc_is_present (&e, sizeof (e)))
+    abort ();
+
+#pragma acc parallel copyin (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	b[i] = a[i];
+	a[i] = b[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != i + 1)
+	abort ();
+    }
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	e[i] = a[i] + d[i];
+	a[i] = e[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != (i + 1) * 2)
+	abort ();
+    }
+
+  for (i = 0; i < N; i++)
+    {
+      a[i] = 1234;
+    }
+
+  subr1 (&a[0]);
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != 1234 * 2)
+	abort ();
+    }
+
+  subr2 (&a[0]);
+
+  for (i = 0; i < 1; i++)
+    {
+      if (a[i] != 1234 * 6)
+	abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c
new file mode 100644
index 0000000..1e2f6ce
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c
@@ -0,0 +1,13 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+int
+main (int argc, char **argv)
+{
+  int a[8] __attribute__((unused));
+
+  __builtin_printf ("CheCKpOInT\n");
+#pragma acc declare present (a)
+}
+
+/* { dg-output "CheCKpOInT" } */
+/* { dg-shouldfail "" } */

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-09 23:11               ` James Norris
@ 2015-11-11  8:32                 ` Jakub Jelinek
  2015-11-11 10:08                   ` Thomas Schwinge
  2015-11-12  1:08                   ` James Norris
  0 siblings, 2 replies; 51+ messages in thread
From: Jakub Jelinek @ 2015-11-11  8:32 UTC (permalink / raw)
  To: James Norris; +Cc: Thomas Schwinge, Joseph S. Myers, GCC Patches

On Mon, Nov 09, 2015 at 05:11:44PM -0600, James Norris wrote:
> diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
> index 953c4e3..c6a2981 100644
> --- a/gcc/c-family/c-pragma.h
> +++ b/gcc/c-family/c-pragma.h
> @@ -30,6 +30,7 @@ enum pragma_kind {
>    PRAGMA_OACC_ATOMIC,
>    PRAGMA_OACC_CACHE,
>    PRAGMA_OACC_DATA,
> +  PRAGMA_OACC_DECLARE,
>    PRAGMA_OACC_ENTER_DATA,
>    PRAGMA_OACC_EXIT_DATA,
>    PRAGMA_OACC_KERNELS,

This change will make PR68271 even worse, so would be really nice to
get that fixed first.

> +	  case GOMP_MAP_ALLOC:
> +	    if (!acc_is_present (hostaddrs[i], sizes[i]))
> +	      {
> +		GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
> +				       &kinds[i], 0, 0);
> +	      }

No {}s around single statement body.

> +	  case GOMP_MAP_FORCE_PRESENT:
> +	    if (!acc_is_present (hostaddrs[i], sizes[i]))
> +	      gomp_fatal ("[%p,%zd] is not mapped", hostaddrs[i], sizes[i]);

This isn't portable unfortunately to all targets that build libgomp.
Looking around, we use various ways to print sizes in gomp_fatal:
1) use %ld and cast to unsigned long
2) use %d and cast to int
3)
#ifdef HAVE_INTTYPES_H
                      gomp_fatal ("present clause: !acc_is_present (%p, "
                                  "%"PRIu64" (0x%"PRIx64"))",
                                  (void *) k->host_start,
                                  (uint64_t) size, (uint64_t) size);
#else
                      gomp_fatal ("present clause: !acc_is_present (%p, "
                                  "%lu (0x%lx))", (void *) k->host_start,
                                  (unsigned long) size, (unsigned long) size);
#endif

I'd say use any of those for now, and if you or one of your collegues could
clean all this up, it would be greatly appreciated.
The best might be to handle this somewhere in libgomp.h by testing something
like:
#if defined(__GLIBC__) // or some configure check whether %zd works
# define GOMP_PRIuSIZE_T "zu"
# define GOMP_PRIxSIZE_T "zx"
typedef size_t gomp_prisize_t;
#elif __SIZEOF_SIZE_T__ == __SIZEOF_INT__
# define GOMP_PRIuSIZE_T "u"
# define GOMP_PRIxSIZE_T "x"
typedef unsigned int gomp_prisize_t;
#elif __SIZEOF_SIZE_T__ == __SIZEOF_LONG__
# define GOMP_PRIuSIZE_T "lu"
# define GOMP_PRIxSIZE_T "lx"
typedef unsigned long gomp_prisize_t;
#elif defined (HAVE_INTTYPES_H) && __SIZEOF_SIZE_T__ == 8 && __CHAR_BIT__ == 8
# define GOMP_PRIuSIZE_T PRIu64
# define GOMP_PRIxSIZE_T PRIx64
typedef uint64_t gomp_prisize_t;
#else
# define GOMP_PRIuSIZE_T "lu"
# define GOMP_PRIxSIZE_T "lx"
typedef unsigned long gomp_prisize_t;
#endif
and then use those macros and always cast size_t arguments to
gomp_prisize_t in the various gomp_fatal or other printing calls.

> +int
> +main (int argc, char **argv)
> +{
> +  int a[8] __attribute__((unused));
> +
> +  __builtin_printf ("CheCKpOInT\n");
> +#pragma acc declare present (a)
> +}
> +
> +/* { dg-output "CheCKpOInT" } */
> +/* { dg-shouldfail "" } */

Are you sure printf will have the buffers flushed before abort on all
targets?

	Jakub

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-11  8:32                 ` Jakub Jelinek
@ 2015-11-11 10:08                   ` Thomas Schwinge
  2015-11-11 17:29                     ` Jakub Jelinek
  2015-11-12  1:08                   ` James Norris
  1 sibling, 1 reply; 51+ messages in thread
From: Thomas Schwinge @ 2015-11-11 10:08 UTC (permalink / raw)
  To: Jakub Jelinek, James Norris; +Cc: Joseph S. Myers, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 1799 bytes --]

Hi!

On Wed, 11 Nov 2015 09:32:33 +0100, Jakub Jelinek <jakub@redhat.com> wrote:
> On Mon, Nov 09, 2015 at 05:11:44PM -0600, James Norris wrote:
> > diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
> > index 953c4e3..c6a2981 100644
> > --- a/gcc/c-family/c-pragma.h
> > +++ b/gcc/c-family/c-pragma.h
> > @@ -30,6 +30,7 @@ enum pragma_kind {
> >    PRAGMA_OACC_ATOMIC,
> >    PRAGMA_OACC_CACHE,
> >    PRAGMA_OACC_DATA,
> > +  PRAGMA_OACC_DECLARE,
> >    PRAGMA_OACC_ENTER_DATA,
> >    PRAGMA_OACC_EXIT_DATA,
> >    PRAGMA_OACC_KERNELS,
> 
> This change will make PR68271 even worse, so would be really nice to
> get that fixed first.

"Would be really nice" means that you're asking us to work on and resolve
PR68271 before installing this patch?


> > +	  case GOMP_MAP_FORCE_PRESENT:
> > +	    if (!acc_is_present (hostaddrs[i], sizes[i]))
> > +	      gomp_fatal ("[%p,%zd] is not mapped", hostaddrs[i], sizes[i]);
> 
> This isn't portable unfortunately to all targets that build libgomp.
> Looking around, we use various ways to print sizes in gomp_fatal: [...]
> [...] if you or one of your collegues could
> clean all this up, it would be greatly appreciated.  [...]

ACK; will put it on the list of things to do, later on.


> > +int
> > +main (int argc, char **argv)
> > +{
> > +  int a[8] __attribute__((unused));
> > +
> > +  __builtin_printf ("CheCKpOInT\n");
> > +#pragma acc declare present (a)
> > +}
> > +
> > +/* { dg-output "CheCKpOInT" } */
> > +/* { dg-shouldfail "" } */
> 
> Are you sure printf will have the buffers flushed before abort on all
> targets?

No, have to print to stderr; see
<http://news.gmane.org/find-root.php?message_id=%3C87pp017fwh.fsf%40kepler.schwinge.homeip.net%3E>.


Grüße
 Thomas

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 472 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-11 10:08                   ` Thomas Schwinge
@ 2015-11-11 17:29                     ` Jakub Jelinek
  0 siblings, 0 replies; 51+ messages in thread
From: Jakub Jelinek @ 2015-11-11 17:29 UTC (permalink / raw)
  To: Thomas Schwinge; +Cc: James Norris, Joseph S. Myers, GCC Patches

On Wed, Nov 11, 2015 at 11:08:21AM +0100, Thomas Schwinge wrote:
> Hi!
> 
> On Wed, 11 Nov 2015 09:32:33 +0100, Jakub Jelinek <jakub@redhat.com> wrote:
> > On Mon, Nov 09, 2015 at 05:11:44PM -0600, James Norris wrote:
> > > diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
> > > index 953c4e3..c6a2981 100644
> > > --- a/gcc/c-family/c-pragma.h
> > > +++ b/gcc/c-family/c-pragma.h
> > > @@ -30,6 +30,7 @@ enum pragma_kind {
> > >    PRAGMA_OACC_ATOMIC,
> > >    PRAGMA_OACC_CACHE,
> > >    PRAGMA_OACC_DATA,
> > > +  PRAGMA_OACC_DECLARE,
> > >    PRAGMA_OACC_ENTER_DATA,
> > >    PRAGMA_OACC_EXIT_DATA,
> > >    PRAGMA_OACC_KERNELS,
> > 
> > This change will make PR68271 even worse, so would be really nice to
> > get that fixed first.
> 
> "Would be really nice" means that you're asking us to work on and resolve
> PR68271 before installing this patch?

Dominique has committed a quick hack for this, so it is not urgent, but
would be nice to get it resolved.  If somebody from Mentor gets to that,
perfect, otherwise I (or somebody else) will get to that eventually.

	Jakub

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-11  8:32                 ` Jakub Jelinek
  2015-11-11 10:08                   ` Thomas Schwinge
@ 2015-11-12  1:08                   ` James Norris
  2015-11-12  9:10                     ` Jakub Jelinek
  2019-06-18 22:21                     ` [committed] [PR90862] OpenACC 'declare' ICE when nested inside another construct " Thomas Schwinge
  1 sibling, 2 replies; 51+ messages in thread
From: James Norris @ 2015-11-12  1:08 UTC (permalink / raw)
  To: Jakub Jelinek, James Norris; +Cc: Thomas Schwinge, Joseph S. Myers, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 1834 bytes --]

Jakub,

The attached patch and ChangeLog reflect the updates from your
review: https://gcc.gnu.org/ml/gcc-patches/2015-11/msg01317.html.

Highlights....

The following issue was handled by Dominique d'Humières
in: https://gcc.gnu.org/ml/gcc-patches/2015-11/msg01375.html

On 11/11/2015 02:32 AM, Jakub Jelinek wrote:
> On Mon, Nov 09, 2015 at 05:11:44PM -0600, James Norris wrote:
>> >diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
>> >index 953c4e3..c6a2981 100644
>> >--- a/gcc/c-family/c-pragma.h
>> >+++ b/gcc/c-family/c-pragma.h
>> >@@ -30,6 +30,7 @@ enum pragma_kind {
>> >    PRAGMA_OACC_ATOMIC,
>> >    PRAGMA_OACC_CACHE,
>> >    PRAGMA_OACC_DATA,
>> >+  PRAGMA_OACC_DECLARE,
>> >    PRAGMA_OACC_ENTER_DATA,
>> >    PRAGMA_OACC_EXIT_DATA,
>> >    PRAGMA_OACC_KERNELS,
> This change will make PR68271 even worse, so would be really nice to
> get that fixed first.

With the addition of: https://gcc.gnu.org/ml/gcc-patches/2015-11/msg01372.html,
additional conditions were added to the following as you called
out in your review of: https://gcc.gnu.org/ml/gcc-patches/2015-11/msg00703.html.


On 11/06/2015 01:03 PM, Jakub Jelinek wrote:
 >> @@ -5841,6 +5863,8 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree 
decl,
 >>         flags |= GOVD_FIRSTPRIVATE;
 >>         break;
 >>       case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
 >> +      if (is_global_var (decl) && device_resident_p (decl))
 >> +	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
 >
 > I don't think you want to do this except for (selected or all?)
 > OpenACC contexts.  Say, I don't see why one couldn't e.g. try to mix
 > OpenMP host parallelization or tasking with OpenACC offloading,
 > and that affecting in weird way OpenMP semantics.
 >

With the addition of routine directive support, additional run-time tests
were added.

OK?

Thanks,
Jim

[-- Attachment #2: ChangeLog --]
[-- Type: text/plain, Size: 2855 bytes --]

2015-XX-XX  James Norris  <jnorris@codesourcery.com>
	    Joseph Myers  <joseph@codesourcery.com>

	gcc/c-family/
	* c-pragma.c (oacc_pragmas): Add entry for declare directive. 
	* c-pragma.h (enum pragma_kind): Add PRAGMA_OACC_DECLARE.
	(enum pragma_omp_clause): Add PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT and
	PRAGMA_OACC_CLAUSE_LINK.

	gcc/c/
	* c-parser.c (c_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	(c_parser_omp_clause_name): Handle 'device_resident' clause.
	(c_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(c_parser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OACC_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(c_parser_oacc_declare): New function.

	gcc/cp/
	* parser.c (cp_parser_omp_clause_name): Handle 'device_resident'
	clause.
	(cp_parser_oacc_data_clause): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(cp_paser_oacc_all_clauses): Handle PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT
	and PRAGMA_OMP_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSE_MASK): New definition.
	(cp_parser_oacc_declare): New function.
	(cp_parser_pragma): Handle PRAGMA_OACC_DECLARE.
	* pt.c (tsubst_expr): Handle OACC_DECLARE.

	gcc/
	* gimple-pretty-print.c (dump_gimple_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE. 
	* gimple.h (enum gf_mask): Add GF_OMP_TARGET_KIND_OACC_DECLARE.
	(is_gomple_omp_oacc): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* gimplify.c (oacc_declare_returns): New.
	(gimplify_bind_expr): Prepend 'exit' stmt to cleanup.
	(device_resident_p): New function.
	(omp_default_clause): Handle device_resident clause.
	(gimplify_oacc_declare_1, gimplify_oacc_declare): New functions.
	(gimplify_expr): Handle OACC_DECLARE.
	* omp-builtins.def (BUILT_IN_GOACC_DECLARE): New builtin.
	* omp-low.c (expand_omp_target): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE and BUILTIN_GOACC_DECLARE.
	(build_omp_regions_1): Handlde GF_OMP_TARGET_KIND_OACC_DECLARE.
	(lower_omp_target): Handle GF_OMP_TARGET_KIND_OACC_DECLARE,
	GOMP_MAP_DEVICE_RESIDENT and GOMP_MAP_LINK.
	(make_gimple_omp_edges): Handle GF_OMP_TARGET_KIND_OACC_DECLARE.
	* tree-pretty-print.c (dump_omp_clause): Handle GOMP_MAP_LINK and
	GOMP_MAP_DEVICE_RESIDENT.

	gcc/testsuite
	* c-c++-common/goacc/declare-1.c: New test.
	* c-c++-common/goacc/declare-2.c: Likewise.

	include/
	* gomp-constants.h (enum gomp_map_kind): Add GOMP_MAP_DEVICE_RESIDENT
	and GOMP_MAP_LINK.

	libgomp/

	* libgomp.map (GOACC_2.0.1): Export GOACC_declare.
	* oacc-parallel.c (GOACC_declare): New function.
	* testsuite/libgomp.oacc-c-c++-common/declare-1.c: New test.
	* testsuite/libgomp.oacc-c-c++-common/declare-2.c: Likewise.
	* testsuite/libgomp.oacc-c-c++-common/declare-4.c: Likewise.
	* testsuite/libgomp.oacc-c-c++-common/declare-5.c: Likewise.
	* testsuite/libgomp.oacc-c++/declare-1.C: Likewise.

[-- Attachment #3: declare.patch --]
[-- Type: text/x-patch, Size: 38231 bytes --]

diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
index f86ed38..12c3e75 100644
--- a/gcc/c-family/c-pragma.c
+++ b/gcc/c-family/c-pragma.c
@@ -1248,6 +1248,7 @@ static const struct omp_pragma_def oacc_pragmas[] = {
   { "atomic", PRAGMA_OACC_ATOMIC },
   { "cache", PRAGMA_OACC_CACHE },
   { "data", PRAGMA_OACC_DATA },
+  { "declare", PRAGMA_OACC_DECLARE },
   { "enter", PRAGMA_OACC_ENTER_DATA },
   { "exit", PRAGMA_OACC_EXIT_DATA },
   { "kernels", PRAGMA_OACC_KERNELS },
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index afeceff..999ac67 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -30,6 +30,7 @@ enum pragma_kind {
   PRAGMA_OACC_ATOMIC,
   PRAGMA_OACC_CACHE,
   PRAGMA_OACC_DATA,
+  PRAGMA_OACC_DECLARE,
   PRAGMA_OACC_ENTER_DATA,
   PRAGMA_OACC_EXIT_DATA,
   PRAGMA_OACC_KERNELS,
@@ -152,6 +153,7 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_CREATE,
   PRAGMA_OACC_CLAUSE_DELETE,
   PRAGMA_OACC_CLAUSE_DEVICEPTR,
+  PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT,
   PRAGMA_OACC_CLAUSE_GANG,
   PRAGMA_OACC_CLAUSE_HOST,
   PRAGMA_OACC_CLAUSE_INDEPENDENT,
@@ -176,7 +178,8 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_FIRSTPRIVATE = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE,
   PRAGMA_OACC_CLAUSE_IF = PRAGMA_OMP_CLAUSE_IF,
   PRAGMA_OACC_CLAUSE_PRIVATE = PRAGMA_OMP_CLAUSE_PRIVATE,
-  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION
+  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION,
+  PRAGMA_OACC_CLAUSE_LINK = PRAGMA_OMP_CLAUSE_LINK
 };
 
 extern struct cpp_reader* parse_in;
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 2484b92..b7806bd 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1228,6 +1228,7 @@ static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
 					     unsigned int * = NULL);
+static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
 static void c_parser_omp_construct (c_parser *);
@@ -9714,6 +9715,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context)
 
   switch (id)
     {
+    case PRAGMA_OACC_DECLARE:
+      c_parser_oacc_declare (parser);
+      return false;
+
     case PRAGMA_OACC_ENTER_DATA:
       c_parser_oacc_enter_exit_data (parser, true);
       return false;
@@ -10003,6 +10008,8 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -10439,10 +10446,16 @@ c_parser_oacc_data_clause (c_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -12724,6 +12737,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = c_parser_omp_clause_firstprivate (parser, clauses);
 	  c_name = "firstprivate";
@@ -12746,6 +12763,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 						clauses);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  clauses = c_parser_omp_clause_num_gangs (parser, clauses);
 	  c_name = "num_gangs";
@@ -13203,6 +13224,161 @@ c_parser_oacc_data (location_t loc, c_parser *parser)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE) )
+
+static void
+c_parser_oacc_declare (c_parser *parser)
+{
+  location_t pragma_loc = c_parser_peek_token (parser)->location;
+  tree clauses, stmt, t, decl;
+
+  bool error = false;
+
+  c_parser_consume_pragma (parser);
+
+  clauses = c_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+				       "#pragma acc declare");
+  if (!clauses)
+    {
+      error_at (pragma_loc,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return;
+    }
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      decl = OMP_CLAUSE_DECL (t);
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "array section in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p ()
+	      && (TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl))
+	  || lookup_attribute ("omp declare target link",
+			       DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_MAP_KIND (t) == GOMP_MAP_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl)
+			   = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+
+	  if (global_bindings_p ())
+	    {
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
+	    }
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return;
+
+  stmt = make_node (OACC_DECLARE);
+  TREE_TYPE (stmt) = void_type_node;
+  OACC_DECLARE_CLAUSES (stmt) = clauses;
+  SET_EXPR_LOCATION (stmt, pragma_loc);
+
+  add_stmt (stmt);
+
+  return;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index a87675e..0ab5275 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -29128,6 +29128,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
 	  else if (!strcmp ("deviceptr", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
+	  else if (!strcmp ("device_resident", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -29541,10 +29543,16 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
     case PRAGMA_OACC_CLAUSE_DEVICE:
       kind = GOMP_MAP_FORCE_TO;
       break;
+    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+      kind = GOMP_MAP_DEVICE_RESIDENT;
+      break;
     case PRAGMA_OACC_CLAUSE_HOST:
     case PRAGMA_OACC_CLAUSE_SELF:
       kind = GOMP_MAP_FORCE_FROM;
       break;
+    case PRAGMA_OACC_CLAUSE_LINK:
+      kind = GOMP_MAP_LINK;
+      break;
     case PRAGMA_OACC_CLAUSE_PRESENT:
       kind = GOMP_MAP_FORCE_PRESENT;
       break;
@@ -31545,6 +31553,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  clauses = cp_parser_oacc_data_clause_deviceptr (parser, clauses);
 	  c_name = "deviceptr";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "device_resident";
+	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_FIRSTPRIVATE,
 					    clauses);
@@ -31569,6 +31581,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 						  clauses, here);
 	  c_name = "independent";
 	  break;
+	case PRAGMA_OACC_CLAUSE_LINK:
+	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
+	  c_name = "link";
+	  break;
 	case PRAGMA_OACC_CLAUSE_NUM_GANGS:
 	  code = OMP_CLAUSE_NUM_GANGS;
 	  c_name = "num_gangs";
@@ -34526,6 +34542,158 @@ cp_parser_oacc_data (cp_parser *parser, cp_token *pragma_tok)
 }
 
 /* OpenACC 2.0:
+   # pragma acc declare oacc-data-clause[optseq] new-line
+*/
+
+#define OACC_DECLARE_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT)		\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPY)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYIN)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_COPYOUT)	\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT_OR_CREATE))
+
+static tree
+cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
+{
+  tree clauses, stmt, t;
+  bool error = false;
+
+  clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
+					"#pragma acc declare", pragma_tok, true);
+
+
+  if (find_omp_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
+    {
+      error_at (pragma_tok->location,
+		"no valid clauses specified in %<#pragma acc declare%>");
+      return NULL_TREE;
+    }
+
+  for (tree t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      location_t loc = OMP_CLAUSE_LOCATION (t);
+      tree decl = OMP_CLAUSE_DECL (t);
+      if (!DECL_P (decl))
+	{
+	  error_at (loc, "array section in %<#pragma acc declare%>");
+	  error = true;
+	  continue;
+	}
+      gcc_assert (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_MAP);
+      switch (OMP_CLAUSE_MAP_KIND (t))
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  break;
+
+	case GOMP_MAP_POINTER:
+	  /* Generated by c_finish_omp_clauses from array sections;
+	     avoid spurious diagnostics.  */
+	  break;
+
+	case GOMP_MAP_LINK:
+	  if (!global_bindings_p ()
+	      && (TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
+	    {
+	      error_at (loc,
+			"%qD must be a global variable in"
+			"%<#pragma acc declare link%>",
+			decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  if (global_bindings_p ())
+	    {
+	      error_at (loc, "invalid OpenACC clause at file scope");
+	      error = true;
+	      continue;
+	    }
+	  if (DECL_EXTERNAL (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<extern%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  else if (TREE_PUBLIC (decl))
+	    {
+	      error_at (loc,
+			"invalid use of %<global%> variable %qD "
+			"in %<#pragma acc declare%>", decl);
+	      error = true;
+	      continue;
+	    }
+	  break;
+	}
+
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl))
+	  || lookup_attribute ("omp declare target link",
+			       DECL_ATTRIBUTES (decl)))
+	{
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
+
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_MAP_KIND (t) == GOMP_MAP_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl)
+			   = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+	  if (global_bindings_p ())
+	    {
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
+	    }
+	}
+    }
+
+  if (error || global_bindings_p ())
+    return NULL_TREE;
+
+  stmt = make_node (OACC_DECLARE);
+  TREE_TYPE (stmt) = void_type_node;
+  OACC_DECLARE_CLAUSES (stmt) = clauses;
+  SET_EXPR_LOCATION (stmt, pragma_tok->location);
+
+  add_stmt (stmt);
+
+  return NULL_TREE;
+}
+
+/* OpenACC 2.0:
    # pragma acc enter data oacc-enter-data-clause[optseq] new-line
 
    or
@@ -36354,6 +36522,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context)
       cp_parser_omp_declare (parser, pragma_tok, context);
       return false;
 
+    case PRAGMA_OACC_DECLARE:
+      cp_parser_oacc_declare (parser, pragma_tok);
+      return false;
+
     case PRAGMA_OACC_ROUTINE:
       cp_parser_oacc_routine (parser, pragma_tok, context);
       return false;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 053a4ef..18d0561 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15403,6 +15403,14 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       add_stmt (t);
       break;
 
+    case OACC_DECLARE:
+      t = copy_node (t);
+      tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
+				args, complain, in_decl);
+      OACC_DECLARE_CLAUSES (t) = tmp;
+      add_stmt (t);
+      break;
+
     case OMP_TARGET_UPDATE:
     case OMP_TARGET_ENTER_DATA:
     case OMP_TARGET_EXIT_DATA:
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 7b50cdf..7764201 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -1353,6 +1353,9 @@ dump_gimple_omp_target (pretty_printer *buffer, gomp_target *gs,
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       kind = " oacc_enter_exit_data";
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      kind = " oacc_declare";
+      break;
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 781801b..e45162d 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -170,6 +170,7 @@ enum gf_mask {
     GF_OMP_TARGET_KIND_OACC_DATA = 7,
     GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
     GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
+    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,
 
     /* True on an GIMPLE_OMP_RETURN statement if the return does not require
        a thread synchronization via some sort of barrier.  The exact barrier
@@ -6004,6 +6005,7 @@ is_gimple_omp_oacc (const gimple *stmt)
 	case GF_OMP_TARGET_KIND_OACC_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  return true;
 	default:
 	  return false;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 66e5168..7a7458d 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -176,6 +176,7 @@ static struct gimplify_omp_ctx *gimplify_omp_ctxp;
 
 /* Forward declaration.  */
 static enum gimplify_status gimplify_compound_expr (tree *, gimple_seq *, bool);
+static hash_map<tree, tree> *oacc_declare_returns;
 
 /* Shorter alias name for the above function for use in gimplify.c
    only.  */
@@ -1078,6 +1079,7 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
   gimple_seq body, cleanup;
   gcall *stack_save;
   location_t start_locus = 0, end_locus = 0;
+  tree ret_clauses = NULL;
 
   tree temp = voidify_wrapper_expr (bind_expr, NULL);
 
@@ -1179,9 +1181,39 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 	  clobber_stmt = gimple_build_assign (t, clobber);
 	  gimple_set_location (clobber_stmt, end_locus);
 	  gimplify_seq_add_stmt (&cleanup, clobber_stmt);
+
+	  if (flag_openacc && oacc_declare_returns != NULL)
+	    {
+	      tree *c = oacc_declare_returns->get (t);
+	      if (c != NULL)
+		{
+		  if (ret_clauses)
+		    OMP_CLAUSE_CHAIN (*c) = ret_clauses;
+
+		  ret_clauses = *c;
+
+		  oacc_declare_returns->remove (t);
+
+		  if (oacc_declare_returns->elements () == 0)
+		    {
+		      delete oacc_declare_returns;
+		      oacc_declare_returns = NULL;
+		    }
+		}
+	    }
 	}
     }
 
+  if (ret_clauses)
+    {
+      gomp_target *stmt;
+      gimple_stmt_iterator si = gsi_start (cleanup);
+
+      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				      ret_clauses);
+      gsi_insert_seq_before_without_update (&si, stmt, GSI_NEW_STMT);
+    }
+
   if (cleanup)
     {
       gtry *gs;
@@ -5809,6 +5841,26 @@ omp_notice_threadprivate_variable (struct gimplify_omp_ctx *ctx, tree decl,
   return false;
 }
 
+/* Return true if global var DECL is device resident.  */
+
+static bool
+device_resident_p (tree decl)
+{
+  tree attr = lookup_attribute ("oacc declare target", DECL_ATTRIBUTES (decl));
+
+  if (!attr)
+    return false;
+
+  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
+    {
+      tree c = TREE_VALUE (t);
+      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
+	return true;
+    }
+
+  return false;
+}
+
 /* Determine outer default flags for DECL mentioned in an OMP region
    but not declared in an enclosing clause.
 
@@ -5858,6 +5910,10 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
       flags |= GOVD_FIRSTPRIVATE;
       break;
     case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
+      if (is_global_var (decl)
+	  && ctx->region_type & (ORT_ACC_PARALLEL | ORT_ACC_KERNELS)
+	  && device_resident_p (decl))
+	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
       /* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED.  */
       gcc_assert ((ctx->region_type & ORT_TASK) != 0);
       if (struct gimplify_omp_ctx *octx = ctx->outer_context)
@@ -7763,6 +7819,121 @@ gimplify_oacc_cache (tree *expr_p, gimple_seq *pre_p)
   *expr_p = NULL_TREE;
 }
 
+/* Helper function of gimplify_oacc_declare.  The helper's purpose is to,
+   if required, translate 'kind' in CLAUSE into an 'entry' kind and 'exit'
+   kind.  The entry kind will replace the one in CLAUSE, while the exit
+   kind will be used in a new omp_clause and returned to the caller.  */
+
+static tree
+gimplify_oacc_declare_1 (tree clause)
+{
+  HOST_WIDE_INT kind, new_op;
+  bool ret = false;
+  tree c = NULL;
+
+  kind = OMP_CLAUSE_MAP_KIND (clause);
+
+  switch (kind)
+    {
+      case GOMP_MAP_ALLOC:
+      case GOMP_MAP_FORCE_ALLOC:
+      case GOMP_MAP_FORCE_TO:
+	new_op = GOMP_MAP_FORCE_DEALLOC;
+	ret = true;
+	break;
+
+      case GOMP_MAP_FORCE_FROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_FORCE_ALLOC);
+	new_op = GOMP_MAP_FORCE_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_FORCE_TOFROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_FORCE_TO);
+	new_op = GOMP_MAP_FORCE_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_FROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_FORCE_ALLOC);
+	new_op = GOMP_MAP_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_TOFROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_TO);
+	new_op = GOMP_MAP_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_DEVICE_RESIDENT:
+      case GOMP_MAP_FORCE_DEVICEPTR:
+      case GOMP_MAP_FORCE_PRESENT:
+      case GOMP_MAP_LINK:
+      case GOMP_MAP_POINTER:
+      case GOMP_MAP_TO:
+	break;
+
+      default:
+	gcc_unreachable ();
+	break;
+    }
+
+  if (ret)
+    {
+      c = build_omp_clause (OMP_CLAUSE_LOCATION (clause), OMP_CLAUSE_MAP);
+      OMP_CLAUSE_SET_MAP_KIND (c, new_op);
+      OMP_CLAUSE_DECL (c) = OMP_CLAUSE_DECL (clause);
+    }
+
+  return c;
+}
+
+/* Gimplify OACC_DECLARE.  */
+
+static void
+gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
+{
+  tree expr = *expr_p;
+  gomp_target *stmt;
+  tree clauses, t;
+
+  clauses = OACC_DECLARE_CLAUSES (expr);
+
+  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
+
+  for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
+    {
+      tree decl = OMP_CLAUSE_DECL (t);
+
+      if (TREE_CODE (decl) == MEM_REF)
+	continue;
+
+      if (TREE_CODE (decl) == VAR_DECL
+	  && !is_global_var (decl)
+	  && DECL_CONTEXT (decl) == current_function_decl)
+	{
+	  tree c = gimplify_oacc_declare_1 (t);
+	  if (c)
+	    {
+	      if (oacc_declare_returns == NULL)
+		oacc_declare_returns = new hash_map<tree, tree>;
+
+	      oacc_declare_returns->put (decl, c);
+	    }
+	}
+
+      omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
+    }
+
+  stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				  clauses);
+
+  gimplify_seq_add_stmt (pre_p, stmt);
+
+  *expr_p = NULL_TREE;
+}
+
 /* Gimplify the contents of an OMP_PARALLEL statement.  This involves
    gimplification of the body, as well as scanning the body for used
    variables.  We need to do this scan now, because variable-sized
@@ -10123,11 +10294,15 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  break;
 
 	case OACC_HOST_DATA:
-	case OACC_DECLARE:
 	  sorry ("directive not yet implemented");
 	  ret = GS_ALL_DONE;
 	  break;
 
+	case OACC_DECLARE:
+	  gimplify_oacc_declare (expr_p, pre_p);
+	  ret = GS_ALL_DONE;
+	  break;
+
 	case OACC_DATA:
 	case OACC_KERNELS:
 	case OACC_PARALLEL:
diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def
index 0b6bd58..d540dab 100644
--- a/gcc/omp-builtins.def
+++ b/gcc/omp-builtins.def
@@ -353,3 +353,5 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TARGET_ENTER_EXIT_DATA,
 		  BT_FN_VOID_INT_SIZE_PTR_PTR_PTR_UINT_PTR, ATTR_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS, "GOMP_teams",
 		  BT_FN_VOID_UINT_UINT, ATTR_NOTHROW_LIST)
+DEF_GOACC_BUILTIN (BUILT_IN_GOACC_DECLARE, "GOACC_declare",
+		   BT_FN_VOID_INT_SIZE_PTR_PTR_PTR, ATTR_NOTHROW_LIST)
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 51b471c..f7584de 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -12454,6 +12454,7 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -12697,6 +12698,9 @@ expand_omp_target (struct omp_region *region)
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       start_ix = BUILT_IN_GOACC_ENTER_EXIT_DATA;
       break;
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
+      start_ix = BUILT_IN_GOACC_DECLARE;
+      break;
     default:
       gcc_unreachable ();
     }
@@ -12819,6 +12823,7 @@ expand_omp_target (struct omp_region *region)
   switch (start_ix)
     {
     case BUILT_IN_GOACC_DATA_START:
+    case BUILT_IN_GOACC_DECLARE:
     case BUILT_IN_GOMP_TARGET_DATA:
       break;
     case BUILT_IN_GOMP_TARGET:
@@ -13133,6 +13138,7 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent,
 		case GF_OMP_TARGET_KIND_EXIT_DATA:
 		case GF_OMP_TARGET_KIND_OACC_UPDATE:
 		case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+		case GF_OMP_TARGET_KIND_OACC_DECLARE:
 		  /* ..., other than for those stand-alone directives...  */
 		  region = NULL;
 		  break;
@@ -14916,6 +14922,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+    case GF_OMP_TARGET_KIND_OACC_DECLARE:
       data_region = false;
       break;
     case GF_OMP_TARGET_KIND_DATA:
@@ -14987,6 +14994,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	  case GOMP_MAP_FORCE_TOFROM:
 	  case GOMP_MAP_FORCE_PRESENT:
 	  case GOMP_MAP_FORCE_DEVICEPTR:
+	  case GOMP_MAP_DEVICE_RESIDENT:
+	  case GOMP_MAP_LINK:
 	    gcc_assert (is_gimple_omp_oacc (stmt));
 	    break;
 	  default:
@@ -16713,6 +16722,7 @@ make_gimple_omp_edges (basic_block bb, struct omp_region **region,
 	case GF_OMP_TARGET_KIND_EXIT_DATA:
 	case GF_OMP_TARGET_KIND_OACC_UPDATE:
 	case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
+	case GF_OMP_TARGET_KIND_OACC_DECLARE:
 	  cur_region = cur_region->outer;
 	  break;
 	default:
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
new file mode 100644
index 0000000..b036c63
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -0,0 +1,83 @@
+/* Test valid uses of declare directive.  */
+/* { dg-do compile } */
+
+int v0;
+#pragma acc declare create(v0)
+
+int v1;
+#pragma acc declare copyin(v1)
+
+int *v2;
+#pragma acc declare deviceptr(v2)
+
+int v3;
+#pragma acc declare device_resident(v3)
+
+int v4;
+#pragma acc declare link(v4)
+
+int v5, v6, v7, v8;
+#pragma acc declare create(v5, v6) copyin(v7, v8)
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare create(va0)
+
+  int va1;
+#pragma acc declare copyin(va1)
+
+  int *va2;
+#pragma acc declare deviceptr(va2)
+
+  int va3;
+#pragma acc declare device_resident(va3)
+
+  extern int ve0;
+#pragma acc declare create(ve0)
+
+  extern int ve1;
+#pragma acc declare copyin(ve1)
+
+  extern int *ve2;
+#pragma acc declare deviceptr(ve2)
+
+  extern int ve3;
+#pragma acc declare device_resident(ve3)
+
+  extern int ve4;
+#pragma acc declare link(ve4)
+
+  int va5;
+#pragma acc declare copy(va5)
+
+  int va6;
+#pragma acc declare copyout(va6)
+
+  int va7;
+#pragma acc declare present(va7)
+
+  int va8;
+#pragma acc declare present_or_copy(va8)
+
+  int va9;
+#pragma acc declare present_or_copyin(va9)
+
+  int va10;
+#pragma acc declare present_or_copyout(va10)
+
+  int va11;
+#pragma acc declare present_or_create(va11)
+
+ a:
+  {
+    int va0;
+#pragma acc declare create(va0)
+    if (v1)
+      goto a;
+    else
+      goto b;
+  }
+ b:;
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
new file mode 100644
index 0000000..d24cb22
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -0,0 +1,79 @@
+/* Test invalid uses of declare directive.  */
+/* { dg-do compile } */
+
+#pragma acc declare /* { dg-error "no valid clauses" } */
+
+#pragma acc declare create(undeclared) /* { dg-error "undeclared" } */
+/* { dg-error "no valid clauses" "second error" { target *-*-* } 6 } */
+
+int v0[10];
+#pragma acc declare create(v0[1:3]) /* { dg-error "array section" } */
+
+int v1;
+#pragma acc declare create(v1, v1) /* { dg-error "more than once" } */
+
+int v2;
+#pragma acc declare create(v2)
+#pragma acc declare copyin(v2) /* { dg-error "more than once" } */
+
+int v3;
+#pragma acc declare copy(v3) /* { dg-error "at file scope" } */
+
+int v4;
+#pragma acc declare copyout(v4) /* { dg-error "at file scope" } */
+
+int v5;
+#pragma acc declare present(v5) /* { dg-error "at file scope" } */
+
+int v6;
+#pragma acc declare present_or_copy(v6) /* { dg-error "at file scope" } */
+
+int v7;
+#pragma acc declare present_or_copyin(v7) /* { dg-error "at file scope" } */
+
+int v8;
+#pragma acc declare present_or_copyout(v8) /* { dg-error "at file scope" } */
+
+int v9;
+#pragma acc declare present_or_create(v9) /* { dg-error "at file scope" } */
+
+int va10;
+#pragma acc declare create (va10)
+#pragma acc declare link (va10) /* { dg-error "more than once" } */
+
+int va11;
+#pragma acc declare link (va11)
+#pragma acc declare link (va11) /* { dg-error "more than once" } */
+
+int va12;
+#pragma acc declare create (va12) link (va12) /* { dg-error "more than once" } */
+
+void
+f (void)
+{
+  int va0;
+#pragma acc declare link(va0) /* { dg-error "global variable" } */
+
+  extern int ve0;
+#pragma acc declare copy(ve0) /* { dg-error "invalid use of" } */
+
+  extern int ve1;
+#pragma acc declare copyout(ve1) /* { dg-error "invalid use of" } */
+
+  extern int ve2;
+#pragma acc declare present(ve2) /* { dg-error "invalid use of" } */
+
+  extern int ve3;
+#pragma acc declare present_or_copy(ve3) /* { dg-error "invalid use of" } */
+
+  extern int ve4;
+#pragma acc declare present_or_copyin(ve4) /* { dg-error "invalid use of" } */
+
+  extern int ve5;
+#pragma acc declare present_or_copyout(ve5) /* { dg-error "invalid use of" } */
+
+  extern int ve6;
+#pragma acc declare present_or_create(ve6) /* { dg-error "invalid use of" } */
+
+#pragma acc declare present (v9) /* { dg-error "invalid use of" } */
+}
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index 3f0a4e6..caec760 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -654,6 +654,12 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, int flags)
 	case GOMP_MAP_ALWAYS_POINTER:
 	  pp_string (pp, "always_pointer");
 	  break;
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  pp_string (pp, "device_resident");
+	  break;
+	case GOMP_MAP_LINK:
+	  pp_string (pp, "link");
+	  break;
 	default:
 	  gcc_unreachable ();
 	}
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 7671dd7..dffd631 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -72,6 +72,11 @@ enum gomp_map_kind
        POINTER_SIZE_UNITS.  */
     GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
     /* Do not map, copy bits for firstprivate instead.  */
+    /* OpenACC device_resident.  */
+    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
+    /* OpenACC link.  */
+    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
+    /* Allocate.  */
     GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
     /* Similarly, but store the value in the pointer rather than
        pointed by the pointer.  */
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 39faba9..d16710f 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -392,6 +392,7 @@ GOACC_2.0 {
 
 GOACC_2.0.1 {
   global:
+	GOACC_declare;
 	GOACC_parallel_keyed;
 } GOACC_2.0;
 
diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index 525846b..f76943a 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -501,3 +501,61 @@ GOACC_get_thread_num (void)
 {
   return 0;
 }
+
+void
+GOACC_declare (int device, size_t mapnum,
+	       void **hostaddrs, size_t *sizes, unsigned short *kinds)
+{
+  int i;
+
+  for (i = 0; i < mapnum; i++)
+    {
+      unsigned char kind = kinds[i] & 0xff;
+
+      if (kind == GOMP_MAP_POINTER || kind == GOMP_MAP_TO_PSET)
+	continue;
+
+      switch (kind)
+	{
+	  case GOMP_MAP_FORCE_ALLOC:
+	  case GOMP_MAP_FORCE_DEALLOC:
+	  case GOMP_MAP_FORCE_FROM:
+	  case GOMP_MAP_FORCE_TO:
+	  case GOMP_MAP_POINTER:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_DEVICEPTR:
+	    break;
+
+	  case GOMP_MAP_ALLOC:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				     &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_TO:
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], 0, 0);
+
+	    break;
+
+	  case GOMP_MAP_FROM:
+	    kinds[i] = GOMP_MAP_FORCE_FROM;
+	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				       &kinds[i], 0, 0);
+	    break;
+
+	  case GOMP_MAP_FORCE_PRESENT:
+	    if (!acc_is_present (hostaddrs[i], sizes[i]))
+	      gomp_fatal ("[%p,%ld] is not mapped", hostaddrs[i],
+			  (unsigned long) sizes[i]);
+	    break;
+
+	  default:
+	    assert (0);
+	    break;
+	}
+    }
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c++/declare-1.C b/libgomp/testsuite/libgomp.oacc-c++/declare-1.C
new file mode 100644
index 0000000..0286955
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-c++/declare-1.C
@@ -0,0 +1,31 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <stdlib.h>
+
+template<class T>
+T foo()
+{
+  T a, b;
+  #pragma acc declare create (a)
+
+  #pragma acc parallel copyout (b)
+  {
+    a = 5;
+    b = a;
+  }
+
+  return b;
+}
+
+int
+main (void)
+{
+  int rc;
+
+  rc = foo<int>();
+
+  if (rc != 5)
+    abort ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
new file mode 100644
index 0000000..c63a68d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
@@ -0,0 +1,122 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <openacc.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define N 8
+
+void
+subr2 (int *a)
+{
+  int i;
+  int f[N];
+#pragma acc declare copyout (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i] + f[i];
+      }
+  }
+}
+
+void
+subr1 (int *a)
+{
+  int f[N];
+#pragma acc declare copy (f)
+
+#pragma acc parallel copy (a[0:N])
+  {
+    int i;
+
+    for (i = 0; i < N; i++)
+      {
+	f[i] = a[i];
+	a[i] = f[i] + f[i];
+      }
+  }
+}
+
+int b[8];
+#pragma acc declare create (b)
+
+int d[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+#pragma acc declare copyin (d)
+
+int
+main (int argc, char **argv)
+{
+  int a[N];
+  int e[N];
+#pragma acc declare create (e)
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = i + 1;
+
+  if (!acc_is_present (&b, sizeof (b)))
+    abort ();
+
+  if (!acc_is_present (&d, sizeof (d)))
+    abort ();
+
+  if (!acc_is_present (&e, sizeof (e)))
+    abort ();
+
+#pragma acc parallel copyin (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	b[i] = a[i];
+	a[i] = b[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != i + 1)
+	abort ();
+    }
+
+#pragma acc parallel copy (a[0:N])
+  {
+    for (i = 0; i < N; i++)
+      {
+	e[i] = a[i] + d[i];
+	a[i] = e[i];
+      }
+  }
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != (i + 1) * 2)
+	abort ();
+    }
+
+  for (i = 0; i < N; i++)
+    {
+      a[i] = 1234;
+    }
+
+  subr1 (&a[0]);
+
+  for (i = 0; i < N; i++)
+    {
+      if (a[i] != 1234 * 2)
+	abort ();
+    }
+
+  subr2 (&a[0]);
+
+  for (i = 0; i < 1; i++)
+    {
+      if (a[i] != 1234 * 6)
+	abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-2.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-2.c
new file mode 100644
index 0000000..2078a33
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-2.c
@@ -0,0 +1,64 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <stdlib.h>
+
+#define N 16
+
+float c[N];
+#pragma acc declare device_resident (c)
+
+#pragma acc routine
+float
+subr2 (float a)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    c[i] = 2.0;
+
+  for (i = 0; i < N; i++)
+    a += c[i];
+
+  return a;
+}
+
+float b[N];
+#pragma acc declare copyin (b)
+
+#pragma acc routine
+float
+subr1 (float a)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a += b[i];
+
+  return a;
+}
+
+int
+main (int argc, char **argv)
+{
+  float a;
+  int i;
+
+  for (i = 0; i < 16; i++)
+    b[i] = 1.0;
+
+  a = 0.0;
+
+  a = subr1 (a);
+
+  if (a != 16.0)
+    abort ();
+
+  a = 0.0;
+
+  a = subr2 (a);
+
+  if (a != 32.0)
+    abort ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-4.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-4.c
new file mode 100644
index 0000000..013310e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-4.c
@@ -0,0 +1,41 @@
+/* { dg-do run  { target openacc_nvidia_accel_selected } } */
+
+#include <stdlib.h>
+#include <openacc.h>
+
+float b;
+#pragma acc declare link (b)
+
+#pragma acc routine
+int
+func (int a)
+{
+  b = a + 1;
+
+  return b;
+}
+
+int
+main (int argc, char **argv)
+{
+  float a;
+
+  a = 2.0;
+
+#pragma acc parallel copy (a)
+  {
+    b = a;
+    a = 1.0;
+    a = a + b;
+  }
+
+  if (a != 3.0)
+    abort ();
+
+  a = func (a);
+
+  if (a != 4.0)
+    abort ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c
new file mode 100644
index 0000000..38c5de0
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c
@@ -0,0 +1,15 @@
+/* { dg-do run { target openacc_nvidia_accel_selected } } */
+
+#include <stdio.h>
+
+int
+main (int argc, char **argv)
+{
+  int a[8] __attribute__((unused));
+
+  fprintf (stderr, "CheCKpOInT\n");
+#pragma acc declare present (a)
+}
+
+/* { dg-output "CheCKpOInT" } */
+/* { dg-shouldfail "" } */

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-12  1:08                   ` James Norris
@ 2015-11-12  9:10                     ` Jakub Jelinek
  2015-11-12 13:34                       ` James Norris
  2019-06-18 22:21                     ` [committed] [PR90862] OpenACC 'declare' ICE when nested inside another construct " Thomas Schwinge
  1 sibling, 1 reply; 51+ messages in thread
From: Jakub Jelinek @ 2015-11-12  9:10 UTC (permalink / raw)
  To: James Norris; +Cc: Thomas Schwinge, Joseph S. Myers, GCC Patches

On Wed, Nov 11, 2015 at 07:07:58PM -0600, James Norris wrote:
> +		  oacc_declare_returns->remove (t);
> +
> +		  if (oacc_declare_returns->elements () == 0)
> +		    {
> +		      delete oacc_declare_returns;
> +		      oacc_declare_returns = NULL;
> +		    }

Something for incremental patch:
1) might be nice to have some assertion that at the end of gimplify_body
   or so oacc_declare_returns is NULL
2) what happens if you refer to automatic variables of other functions
   (C or Fortran nested functions, maybe C++ lambdas); shall those be
   unmapped at the end of the (nested) function's body?

> @@ -5858,6 +5910,10 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
>        flags |= GOVD_FIRSTPRIVATE;
>        break;
>      case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
> +      if (is_global_var (decl)
> +	  && ctx->region_type & (ORT_ACC_PARALLEL | ORT_ACC_KERNELS)

Please put this condition as cheapest first.  I'd also surround
it into (), just to make it clear that the bitwise & is intentional.
Perhaps () != 0.

> +	  && device_resident_p (decl))
> +	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;

> +	  case GOMP_MAP_FROM:
> +	    kinds[i] = GOMP_MAP_FORCE_FROM;
> +	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
> +				       &kinds[i], 0, 0);

Wrong indentation.

Ok with those two changes and please think about the incremental stuff.

	Jakub

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-12  9:10                     ` Jakub Jelinek
@ 2015-11-12 13:34                       ` James Norris
  2015-11-23 12:41                         ` Thomas Schwinge
  0 siblings, 1 reply; 51+ messages in thread
From: James Norris @ 2015-11-12 13:34 UTC (permalink / raw)
  To: Jakub Jelinek, James Norris; +Cc: Thomas Schwinge, Joseph S. Myers, GCC Patches

Jakub

On 11/12/2015 03:09 AM, Jakub Jelinek wrote:
> On Wed, Nov 11, 2015 at 07:07:58PM -0600, James Norris wrote:
>> +		  oacc_declare_returns->remove (t);
>> +
>> +		  if (oacc_declare_returns->elements () == 0)
>> +		    {
>> +		      delete oacc_declare_returns;
>> +		      oacc_declare_returns = NULL;
>> +		    }
>
> Something for incremental patch:
> 1) might be nice to have some assertion that at the end of gimplify_body
>     or so oacc_declare_returns is NULL
> 2) what happens if you refer to automatic variables of other functions
>     (C or Fortran nested functions, maybe C++ lambdas); shall those be
>     unmapped at the end of the (nested) function's body?
>

Ok. Thanks! Will put on my TODO list.

>> @@ -5858,6 +5910,10 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
>>         flags |= GOVD_FIRSTPRIVATE;
>>         break;
>>       case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
>> +      if (is_global_var (decl)
>> +	  && ctx->region_type & (ORT_ACC_PARALLEL | ORT_ACC_KERNELS)
>
> Please put this condition as cheapest first.  I'd also surround
> it into (), just to make it clear that the bitwise & is intentional.
> Perhaps () != 0.
>
>> +	  && device_resident_p (decl))
>> +	flags |= GOVD_MAP_TO_ONLY | GOVD_MAP;
>
>> +	  case GOMP_MAP_FROM:
>> +	    kinds[i] = GOMP_MAP_FORCE_FROM;
>> +	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
>> +				       &kinds[i], 0, 0);
>
> Wrong indentation.
>

Fixed.

> Ok with those two changes and please think about the incremental stuff.

Again, thanks for taking the time for the review.

Jim

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH] Fix unused variable.
@ 2015-11-12 23:25                             ` James Norris
  2015-11-22 19:11                               ` [PATCH] fortran/openmp.c -- Fix bootstrap Steve Kargl
  0 siblings, 1 reply; 51+ messages in thread
From: James Norris @ 2015-11-12 23:25 UTC (permalink / raw)
  To: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 167 bytes --]

Hi,

As a result of an unused variable from my patch
of today, it broke bootstrap. Dominique kindly
pointed this out. Thank you.

Committed to trunk as obvious.

Jim


[-- Attachment #2: fix.patch --]
[-- Type: text/x-patch, Size: 883 bytes --]

Index: gcc/cp/ChangeLog
===================================================================
--- gcc/cp/ChangeLog	(revision 230275)
+++ gcc/cp/ChangeLog	(working copy)
@@ -1,4 +1,8 @@
 2015-11-12  James Norris  <jnorris@codesourcery.com>
+
+	* parser.c (cp_parser_oacc_declare): Remove unused.
+
+2015-11-12  James Norris  <jnorris@codesourcery.com>
 	    Joseph Myers  <joseph@codesourcery.com>
 
 	* parser.c (cp_parser_omp_clause_name): Handle 'device_resident'
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 230275)
+++ gcc/cp/parser.c	(working copy)
@@ -34562,7 +34562,7 @@
 static tree
 cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
 {
-  tree clauses, stmt, t;
+  tree clauses, stmt;
   bool error = false;
 
   clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: Re: OpenACC declare directive updates
  2015-11-09  4:53           ` James Norris
@ 2015-11-18 20:09             ` Cesar Philippidis
  0 siblings, 0 replies; 51+ messages in thread
From: Cesar Philippidis @ 2015-11-18 20:09 UTC (permalink / raw)
  To: James Norris, Jakub Jelinek; +Cc: GCC Patches, Fortran List

On 11/08/2015 08:53 PM, James Norris wrote:

> The attached patch and ChangeLog reflect the updates from your
> review: https://gcc.gnu.org/ml/gcc-patches/2015-11/msg00714.html
> and Cesar's review:
> https://gcc.gnu.org/ml/gcc-patches/2015-11/msg00885.html.
> 
> With the changes made in this patch I think I'm handling the
> situation that you pointed out here correctly:
> 
> "Also, wonder about BLOCK stmt in Fortran, that can give you variables that
> don't live through the whole function, but only a portion of it even in
> Fortran."

What block stmt? The most recent version of Fortran OpenACC 2.0a
supports is 2003. The block construct is a 2008 feature. I don't think
that's applicable to this version. Jim, maybe you should add an error
message for variables defined in blocks.

Thinking about this some more, I wonder if we should emit an error if
any acc constructs are used inside blocks? That's probably overly
pessimistic though.

Cesar

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: OpenACC declare directive updates
  2015-11-06 19:49       ` Jakub Jelinek
  2015-11-06 20:20         ` James Norris
@ 2015-11-19 16:22         ` James Norris
  2015-11-20 10:53           ` Jakub Jelinek
  2015-11-27 11:41           ` [gomp4] " Thomas Schwinge
  1 sibling, 2 replies; 51+ messages in thread
From: James Norris @ 2015-11-19 16:22 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Cesar Philippidis, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 1296 bytes --]

Jakub,

Here's the updated version of the Fortran changes. More test
cases have been added as well as the issues that Cesar
pointed on in error checking have been addressed (Thanks).
I've also addressed the issue, described below, in dealing
with declare directives when found within a BLOCK construct.

On 11/06/2015 01:49 PM, Jakub Jelinek wrote:
> On Fri, Nov 06, 2015 at 01:45:09PM -0600, James Norris wrote:
>> Okay, I'll fix this.
>>
>> After fixing, OK to commit?
>>
>> Thank you for taking the time for the review.
>
> Well, isn't this patch really dependent on the other one?
>
> Also, wonder about BLOCK stmt in Fortran, that can give you variables that
> don't live through the whole function, but only a portion of it even in
> Fortran.
>


On 11/18/2015 02:09 PM, Cesar Philippidis wrote:
 > On 11/08/2015 08:53 PM, James Norris wrote:
 >
 >
 > What block stmt? The most recent version of Fortran OpenACC 2.0a
 > supports is 2003. The block construct is a 2008 feature. I don't think
 > that's applicable to this version. Jim, maybe you should add an error
 > message for variables defined in blocks.
 >
 > Thinking about this some more, I wonder if we should emit an error if
 > any acc constructs are used inside blocks? That's probably overly
 > pessimistic though.

Thanks!
Jim


[-- Attachment #2: ChangeLog --]
[-- Type: text/plain, Size: 2536 bytes --]

2015-XX-XX  James Norris  <jnorris@codesourcery.com>
	    Cesar Philippidis  <cesar@codesourcery.com>

	gcc/fortran/
	* dump-parse-tree.c (show_namespace): Handle declares.
	* gfortran.h (struct symbol_attribute): New fields.
	(enum gfc_omp_map_map): Add OMP_MAP_DEVICE_RESIDENT and OMP_MAP_LINK.
	(OMP_LIST_LINK): New enum.
	(struct gfc_oacc_declare): New structure.
	(gfc_get_oacc_declare): New definition.
	(struct gfc_namespace): Change type.
	(enum gfc_exec_op): Add EXEC_OACC_DECLARE.
	(struct gfc_code): New field.
	* module.c (enum ab_attribute): Add AB_OACC_DECLARE_CREATE,
	AB_OACC_DECLARE_COPYIN, AB_OACC_DECLARE_DEVICEPTR,
	AB_OACC_DECLARE_DEVICE_RESIDENT, AB_OACC_DECLARE_LINK
	(attr_bits): Add new initializers.
	(mio_symbol_attribute): Handle new atributes.
	* openmp.c (gfc_free_oacc_declare_clauses): New function.
	(gfc_match_oacc_clause_link: Likewise.
	(OMP_CLAUSE_LINK): New definition.
	(gfc_match_omp_clauses): Handle OMP_CLAUSE_LINK.
	(OACC_DECLARE_CLAUSES): Add OMP_CLAUSE_LINK
	(gfc_match_oacc_declare): Add checking and module handling.
	(gfc_resolve_oacc_declare): Reimplement.
	* parse.c (case_decl): Add ST_OACC_DECLARE.
	(parse_spec): Remove handling.
	(parse_progunit): Remove handling.
	* parse.h (struct gfc_state_data): Change type.
	* resolve.c (gfc_resolve_blocks): Handle EXEC_OACC_DECLARE.
	* st.c (gfc_free_statement): Handle EXEC_OACC_DECLARE.
	* symbol.c (check_conflict): Add conflict checks.
	(gfc_add_oacc_declare_create, gfc_add_oacc_declare_copyin, 
	gfc_add_oacc_declare_deviceptr, gfc_add_oacc_declare_device_resident):
	New functions.
	(gfc_copy_attr): Handle new symbols.
	* trans-decl.c (add_clause, find_module_oacc_declare_clauses,
	finish_oacc_declare): New functions.
	(gfc_generate_function_code): Replace with call.
	* trans-openmp.c (gfc_trans_oacc_declare): Reimplement.
	(gfc_trans_oacc_directive): Handle EXEC_OACC_DECLARE.
	* trans-stmt.c (gfc_trans_block_construct): Replace with call.
	* trans-stmt.h (gfc_trans_oacc_declare): Remove argument.
	* trans.c (trans_code): Handle EXEC_OACC_DECLARE.

	gcc/testsuite
	* gfortran.dg/goacc/declare-1.f95: Update test.
	* gfortran.dg/goacc/declare-2.f95: New test.

	libgomp/
	* testsuite/libgomp.oacc-fortran/declare-1.f90: New test.
	* testsuite/libgomp.oacc-fortran/declare-2.f90: Likewise.
	* testsuite/libgomp.oacc-fortran/declare-3.f90: Likewise.
	* testsuite/libgomp.oacc-fortran/declare-4.f90: Likewise.
	* testsuite/libgomp.oacc-fortran/declare-5.f90: Likewise.
	* testsuite/libgomp.oacc-fortran/declare-5.f90: Likewise.

[-- Attachment #3: declare.patch --]
[-- Type: text/x-patch, Size: 42712 bytes --]

diff --git a/gcc/fortran/dump-parse-tree.c b/gcc/fortran/dump-parse-tree.c
index 83ecbaa..48476af 100644
--- a/gcc/fortran/dump-parse-tree.c
+++ b/gcc/fortran/dump-parse-tree.c
@@ -2570,12 +2570,16 @@ show_namespace (gfc_namespace *ns)
   for (eq = ns->equiv; eq; eq = eq->next)
     show_equiv (eq);
 
-  if (ns->oacc_declare_clauses)
+  if (ns->oacc_declare)
     {
+      struct gfc_oacc_declare *decl;
       /* Dump !$ACC DECLARE clauses.  */
-      show_indent ();
-      fprintf (dumpfile, "!$ACC DECLARE");
-      show_omp_clauses (ns->oacc_declare_clauses);
+      for (decl = ns->oacc_declare; decl; decl = decl->next)
+	{
+	  show_indent ();
+	  fprintf (dumpfile, "!$ACC DECLARE");
+	  show_omp_clauses (decl->clauses);
+	}
     }
 
   fputc ('\n', dumpfile);
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index e13b4d4..5487c93 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -841,6 +841,13 @@ typedef struct
   /* Mentioned in OMP DECLARE TARGET.  */
   unsigned omp_declare_target:1;
 
+  /* Mentioned in OACC DECLARE.  */
+  unsigned oacc_declare_create:1;
+  unsigned oacc_declare_copyin:1;
+  unsigned oacc_declare_deviceptr:1;
+  unsigned oacc_declare_device_resident:1;
+  unsigned oacc_declare_link:1;
+
   /* Attributes set by compiler extensions (!GCC$ ATTRIBUTES).  */
   unsigned ext_attr:EXT_ATTR_NUM;
 
@@ -1106,7 +1113,9 @@ enum gfc_omp_map_op
   OMP_MAP_FORCE_FROM,
   OMP_MAP_FORCE_TOFROM,
   OMP_MAP_FORCE_PRESENT,
-  OMP_MAP_FORCE_DEVICEPTR
+  OMP_MAP_FORCE_DEVICEPTR,
+  OMP_MAP_DEVICE_RESIDENT,
+  OMP_MAP_LINK
 };
 
 /* For use in OpenMP clauses in case we need extra information
@@ -1148,6 +1157,7 @@ enum
   OMP_LIST_FROM,
   OMP_LIST_REDUCTION,
   OMP_LIST_DEVICE_RESIDENT,
+  OMP_LIST_LINK,
   OMP_LIST_USE_DEVICE,
   OMP_LIST_CACHE,
   OMP_LIST_NUM
@@ -1234,6 +1244,20 @@ gfc_omp_clauses;
 #define gfc_get_omp_clauses() XCNEW (gfc_omp_clauses)
 
 
+/* Node in the linked list used for storing !$oacc declare constructs.  */
+
+typedef struct gfc_oacc_declare
+{
+  struct gfc_oacc_declare *next;
+  bool module_var;
+  gfc_omp_clauses *clauses;
+  locus loc;
+}
+gfc_oacc_declare;
+
+#define gfc_get_oacc_declare() XCNEW (gfc_oacc_declare)
+
+
 /* Node in the linked list used for storing !$omp declare simd constructs.  */
 
 typedef struct gfc_omp_declare_simd
@@ -1645,8 +1669,8 @@ typedef struct gfc_namespace
      this namespace.  */
   struct gfc_data *data, *old_data;
 
-  /* !$ACC DECLARE clauses.  */
-  gfc_omp_clauses *oacc_declare_clauses;
+  /* !$ACC DECLARE.  */
+  gfc_oacc_declare *oacc_declare;
 
   gfc_charlen *cl_list, *old_cl_list;
 
@@ -2324,6 +2348,7 @@ enum gfc_exec_op
   EXEC_OACC_PARALLEL, EXEC_OACC_KERNELS, EXEC_OACC_DATA, EXEC_OACC_HOST_DATA,
   EXEC_OACC_LOOP, EXEC_OACC_UPDATE, EXEC_OACC_WAIT, EXEC_OACC_CACHE,
   EXEC_OACC_ENTER_DATA, EXEC_OACC_EXIT_DATA, EXEC_OACC_ATOMIC,
+  EXEC_OACC_DECLARE,
   EXEC_OMP_CRITICAL, EXEC_OMP_DO, EXEC_OMP_FLUSH, EXEC_OMP_MASTER,
   EXEC_OMP_ORDERED, EXEC_OMP_PARALLEL, EXEC_OMP_PARALLEL_DO,
   EXEC_OMP_PARALLEL_SECTIONS, EXEC_OMP_PARALLEL_WORKSHARE,
@@ -2405,6 +2430,7 @@ typedef struct gfc_code
     struct gfc_code *which_construct;
     int stop_code;
     gfc_entry_list *entry;
+    gfc_oacc_declare *oacc_declare;
     gfc_omp_clauses *omp_clauses;
     const char *omp_name;
     gfc_omp_namelist *omp_namelist;
@@ -2907,6 +2933,7 @@ gfc_expr *gfc_get_parentheses (gfc_expr *);
 /* openmp.c */
 struct gfc_omp_saved_state { void *ptrs[2]; int ints[1]; };
 void gfc_free_omp_clauses (gfc_omp_clauses *);
+void gfc_free_oacc_declare_clauses (struct gfc_oacc_declare *);
 void gfc_free_omp_declare_simd (gfc_omp_declare_simd *);
 void gfc_free_omp_declare_simd_list (gfc_omp_declare_simd *);
 void gfc_free_omp_udr (gfc_omp_udr *);
@@ -3224,4 +3251,8 @@ gfc_expr *gfc_simplify_ieee_functions (gfc_expr *);
 
 bool gfc_is_reallocatable_lhs (gfc_expr *);
 
+/* trans-decl.c */
+
+void finish_oacc_declare (gfc_namespace *, gfc_symbol *, bool);
+
 #endif /* GCC_GFORTRAN_H  */
diff --git a/gcc/fortran/module.c b/gcc/fortran/module.c
index 54777f7..6b544ee 100644
--- a/gcc/fortran/module.c
+++ b/gcc/fortran/module.c
@@ -1986,7 +1986,9 @@ enum ab_attribute
   AB_IS_CLASS, AB_PROCEDURE, AB_PROC_POINTER, AB_ASYNCHRONOUS, AB_CODIMENSION,
   AB_COARRAY_COMP, AB_VTYPE, AB_VTAB, AB_CONTIGUOUS, AB_CLASS_POINTER,
   AB_IMPLICIT_PURE, AB_ARTIFICIAL, AB_UNLIMITED_POLY, AB_OMP_DECLARE_TARGET,
-  AB_ARRAY_OUTER_DEPENDENCY, AB_MODULE_PROCEDURE
+  AB_ARRAY_OUTER_DEPENDENCY, AB_MODULE_PROCEDURE, AB_OACC_DECLARE_CREATE,
+  AB_OACC_DECLARE_COPYIN, AB_OACC_DECLARE_DEVICEPTR,
+  AB_OACC_DECLARE_DEVICE_RESIDENT, AB_OACC_DECLARE_LINK
 };
 
 static const mstring attr_bits[] =
@@ -2043,6 +2045,11 @@ static const mstring attr_bits[] =
     minit ("OMP_DECLARE_TARGET", AB_OMP_DECLARE_TARGET),
     minit ("ARRAY_OUTER_DEPENDENCY", AB_ARRAY_OUTER_DEPENDENCY),
     minit ("MODULE_PROCEDURE", AB_MODULE_PROCEDURE),
+    minit ("OACC_DECLARE_CREATE", AB_OACC_DECLARE_CREATE),
+    minit ("OACC_DECLARE_COPYIN", AB_OACC_DECLARE_COPYIN),
+    minit ("OACC_DECLARE_DEVICEPTR", AB_OACC_DECLARE_DEVICEPTR),
+    minit ("OACC_DECLARE_DEVICE_RESIDENT", AB_OACC_DECLARE_DEVICE_RESIDENT),
+    minit ("OACC_DECLARE_LINK", AB_OACC_DECLARE_LINK),
     minit (NULL, -1)
 };
 
@@ -2230,6 +2237,16 @@ mio_symbol_attribute (symbol_attribute *attr)
 	MIO_NAME (ab_attribute) (AB_MODULE_PROCEDURE, attr_bits);
 	  no_module_procedures = false;
 	}
+      if (attr->oacc_declare_create)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_CREATE, attr_bits);
+      if (attr->oacc_declare_copyin)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_COPYIN, attr_bits);
+      if (attr->oacc_declare_deviceptr)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_DEVICEPTR, attr_bits);
+      if (attr->oacc_declare_device_resident)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_DEVICE_RESIDENT, attr_bits);
+      if (attr->oacc_declare_link)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_LINK, attr_bits);
 
       mio_rparen ();
 
@@ -2402,6 +2419,21 @@ mio_symbol_attribute (symbol_attribute *attr)
 	    case AB_MODULE_PROCEDURE:
 	      attr->module_procedure =1;
 	      break;
+	    case AB_OACC_DECLARE_CREATE:
+	      attr->oacc_declare_create = 1;
+	      break;
+	    case AB_OACC_DECLARE_COPYIN:
+	      attr->oacc_declare_copyin = 1;
+	      break;
+	    case AB_OACC_DECLARE_DEVICEPTR:
+	      attr->oacc_declare_deviceptr = 1;
+	      break;
+	    case AB_OACC_DECLARE_DEVICE_RESIDENT:
+	      attr->oacc_declare_device_resident = 1;
+	      break;
+	    case AB_OACC_DECLARE_LINK:
+	      attr->oacc_declare_link = 1;
+	      break;
 	    }
 	}
     }
diff --git a/gcc/fortran/openmp.c b/gcc/fortran/openmp.c
index 4af139a..d7bd0ab 100644
--- a/gcc/fortran/openmp.c
+++ b/gcc/fortran/openmp.c
@@ -90,6 +90,25 @@ gfc_free_omp_clauses (gfc_omp_clauses *c)
   free (c);
 }
 
+/* Free oacc_declare structures.  */
+
+void
+gfc_free_oacc_declare_clauses (struct gfc_oacc_declare *oc)
+{
+  struct gfc_oacc_declare *decl = oc;
+
+  do
+    {
+      struct gfc_oacc_declare *next;
+
+      next = decl->next;
+      gfc_free_omp_clauses (decl->clauses);
+      free (decl);
+      decl = next;
+    }
+  while (decl);
+}
+
 /* Free expression list. */
 void
 gfc_free_expr_list (gfc_expr_list *list)
@@ -393,6 +412,109 @@ match_oacc_clause_gang (gfc_omp_clauses *cp)
   return gfc_match (" %e )", &cp->gang_expr);
 }
 
+static match
+gfc_match_oacc_clause_link (const char *str, gfc_omp_namelist **list)
+{
+  gfc_omp_namelist *head, *tail, *p;
+  locus old_loc;
+  char n[GFC_MAX_SYMBOL_LEN+1];
+  gfc_symbol *sym;
+  match m;
+  gfc_symtree *st;
+
+  old_loc = gfc_current_locus;
+
+  m = gfc_match (str);
+  if (m != MATCH_YES)
+    return m;
+
+  m = gfc_match (" (");
+
+  for (;;)
+    {
+      m = gfc_match_symbol (&sym, 0);
+      switch (m)
+	{
+	case MATCH_YES:
+	  if (sym->attr.in_common)
+	    {
+	      gfc_error_now ("Variable at %C is an element of a COMMON block");
+	      goto cleanup;
+	    }
+	  gfc_set_sym_referenced (sym);
+	  p = gfc_get_omp_namelist ();
+	  if (head == NULL)
+	    head = tail = p;
+	  else
+	    {
+	      tail->next = p;
+	      tail = tail->next;
+	    }
+	  tail->sym = sym;
+	  tail->expr = NULL;
+	  tail->where = gfc_current_locus;
+	  goto next_item;
+	case MATCH_NO:
+	  break;
+
+	case MATCH_ERROR:
+	  goto cleanup;
+	}
+
+      m = gfc_match (" / %n /", n);
+      if (m == MATCH_ERROR)
+	goto cleanup;
+      if (m == MATCH_NO || n[0] == '\0')
+	goto syntax;
+
+      st = gfc_find_symtree (gfc_current_ns->common_root, n);
+      if (st == NULL)
+	{
+	  gfc_error ("COMMON block /%s/ not found at %C", n);
+	  goto cleanup;
+	}
+
+      for (sym = st->n.common->head; sym; sym = sym->common_next)
+	{
+	  gfc_set_sym_referenced (sym);
+	  p = gfc_get_omp_namelist ();
+	  if (head == NULL)
+	    head = tail = p;
+	  else
+	    {
+	      tail->next = p;
+	      tail = tail->next;
+	    }
+	  tail->sym = sym;
+	  tail->where = gfc_current_locus;
+	}
+
+    next_item:
+      if (gfc_match_char (')') == MATCH_YES)
+	break;
+      if (gfc_match_char (',') != MATCH_YES)
+	goto syntax;
+    }
+
+  if (gfc_match_omp_eos () != MATCH_YES)
+    {
+      gfc_error ("Unexpected junk after !$ACC DECLARE at %C");
+      goto cleanup;
+    }
+
+  while (*list)
+    list = &(*list)->next;
+  *list = head;
+  return MATCH_YES;
+
+syntax:
+  gfc_error ("Syntax error in !$ACC DECLARE list at %C");
+
+cleanup:
+  gfc_current_locus = old_loc;
+  return MATCH_ERROR;
+}
+
 #define OMP_CLAUSE_PRIVATE		((uint64_t) 1 << 0)
 #define OMP_CLAUSE_FIRSTPRIVATE		((uint64_t) 1 << 1)
 #define OMP_CLAUSE_LASTPRIVATE		((uint64_t) 1 << 2)
@@ -453,6 +575,7 @@ match_oacc_clause_gang (gfc_omp_clauses *cp)
 #define OMP_CLAUSE_DELETE		((uint64_t) 1 << 55)
 #define OMP_CLAUSE_AUTO			((uint64_t) 1 << 56)
 #define OMP_CLAUSE_TILE			((uint64_t) 1 << 57)
+#define OMP_CLAUSE_LINK			((uint64_t) 1 << 58)
 
 /* Helper function for OpenACC and OpenMP clauses involving memory
    mapping.  */
@@ -691,6 +814,11 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, uint64_t mask,
 					  true)
 	     == MATCH_YES)
 	continue;
+      if ((mask & OMP_CLAUSE_LINK)
+	  && gfc_match_oacc_clause_link ("link (",
+					  &c->lists[OMP_LIST_LINK])
+	     == MATCH_YES)
+	continue;
       if ((mask & OMP_CLAUSE_OACC_DEVICE)
 	  && gfc_match ("device ( ") == MATCH_YES
 	  && gfc_match_omp_map_clause (&c->lists[OMP_LIST_MAP],
@@ -1176,7 +1304,7 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, uint64_t mask,
    | OMP_CLAUSE_CREATE | OMP_CLAUSE_DEVICEPTR | OMP_CLAUSE_DEVICE_RESIDENT    \
    | OMP_CLAUSE_PRESENT | OMP_CLAUSE_PRESENT_OR_COPY                          \
    | OMP_CLAUSE_PRESENT_OR_COPYIN | OMP_CLAUSE_PRESENT_OR_COPYOUT             \
-   | OMP_CLAUSE_PRESENT_OR_CREATE)
+   | OMP_CLAUSE_PRESENT_OR_CREATE | OMP_CLAUSE_LINK)
 #define OACC_UPDATE_CLAUSES \
   (OMP_CLAUSE_IF | OMP_CLAUSE_ASYNC | OMP_CLAUSE_HOST_SELF \
    | OMP_CLAUSE_OACC_DEVICE | OMP_CLAUSE_WAIT)
@@ -1293,12 +1421,80 @@ match
 gfc_match_oacc_declare (void)
 {
   gfc_omp_clauses *c;
+  gfc_omp_namelist *n;
+  gfc_namespace *ns = gfc_current_ns;
+  gfc_oacc_declare *new_oc;
+  bool module_var = false;
+  locus where = gfc_current_locus;
+
   if (gfc_match_omp_clauses (&c, OACC_DECLARE_CLAUSES, false, false, true)
       != MATCH_YES)
     return MATCH_ERROR;
 
-  new_st.ext.omp_clauses = c;
-  new_st.ext.omp_clauses->loc = gfc_current_locus;
+  for (n = c->lists[OMP_LIST_DEVICE_RESIDENT]; n != NULL; n = n->next)
+    n->sym->attr.oacc_declare_device_resident = 1;
+
+  for (n = c->lists[OMP_LIST_LINK]; n != NULL; n = n->next)
+    n->sym->attr.oacc_declare_link = 1;
+
+  for (n = c->lists[OMP_LIST_MAP]; n != NULL; n = n->next)
+    {
+      gfc_symbol *s = n->sym;
+
+      if (s->ns->proc_name && s->ns->proc_name->attr.proc == PROC_MODULE)
+	{
+	  if (n->u.map_op != OMP_MAP_FORCE_ALLOC
+	      && n->u.map_op != OMP_MAP_FORCE_TO)
+	    {
+	      gfc_error ("Invalid clause in module with $!ACC DECLARE at %L",
+			 &where);
+	      return MATCH_ERROR;
+	    }
+
+	  module_var = true;
+	}
+
+      if (s->attr.use_assoc)
+	{
+	  gfc_error ("Variable is USE-associated with $!ACC DECLARE at %L",
+		     &where);
+	  return MATCH_ERROR;
+	}
+
+      if ((s->attr.dimension || s->attr.codimension)
+	  && s->attr.dummy && s->as->type != AS_EXPLICIT)
+	{
+	  gfc_error ("Assumed-size dummy array with $!ACC DECLARE at %L",
+		     &where);
+	  return MATCH_ERROR;
+	}
+
+      switch (n->u.map_op)
+	{
+	  case OMP_MAP_FORCE_ALLOC:
+	    s->attr.oacc_declare_create = 1;
+	    break;
+
+	  case OMP_MAP_FORCE_TO:
+	    s->attr.oacc_declare_copyin = 1;
+	    break;
+
+	  case OMP_MAP_FORCE_DEVICEPTR:
+	    s->attr.oacc_declare_deviceptr = 1;
+	    break;
+
+	  default:
+	    break;
+	}
+    }
+
+  new_oc = gfc_get_oacc_declare ();
+  new_oc->next = ns->oacc_declare;
+  new_oc->module_var = module_var;
+  new_oc->clauses = c;
+  new_oc->loc = gfc_current_locus;
+  ns->oacc_declare = new_oc;
+
   return MATCH_YES;
 }
 
@@ -4613,44 +4809,64 @@ resolve_oacc_loop (gfc_code *code)
   resolve_oacc_nested_loops (code, do_code, collapse, "collapsed");
 }
 
-
 void
 gfc_resolve_oacc_declare (gfc_namespace *ns)
 {
   int list;
   gfc_omp_namelist *n;
-  locus loc;
+  gfc_oacc_declare *oc;
 
-  if (ns->oacc_declare_clauses == NULL)
+  if (ns->oacc_declare == NULL)
     return;
 
-  loc = ns->oacc_declare_clauses->loc;
+  for (oc = ns->oacc_declare; oc; oc = oc->next)
+    {
+      for (list = 0; list <= OMP_LIST_NUM; list++)
+	for (n = oc->clauses->lists[list]; n; n = n->next)
+	  {
+	    n->sym->mark = 0;
+	    if (n->sym->attr.flavor == FL_PARAMETER)
+	      {
+		gfc_error ("PARAMETER object %qs is not allowed at %L",
+			   n->sym->name, &oc->loc);
+		continue;
+	      }
 
-  for (list = OMP_LIST_DEVICE_RESIDENT;
-       list <= OMP_LIST_DEVICE_RESIDENT; list++)
-    for (n = ns->oacc_declare_clauses->lists[list]; n; n = n->next)
-      {
-	n->sym->mark = 0;
-	if (n->sym->attr.flavor == FL_PARAMETER)
-	  gfc_error ("PARAMETER object %qs is not allowed at %L", n->sym->name, &loc);
-      }
+	    if (n->expr && n->expr->ref->type == REF_ARRAY)
+	      {
+		gfc_error ("Array sections: %qs not allowed in"
+			   " $!ACC DECLARE at %L", n->sym->name, &oc->loc);
+		continue;
+	      }
+	  }
 
-  for (list = OMP_LIST_DEVICE_RESIDENT;
-       list <= OMP_LIST_DEVICE_RESIDENT; list++)
-    for (n = ns->oacc_declare_clauses->lists[list]; n; n = n->next)
-      {
-	if (n->sym->mark)
-	  gfc_error ("Symbol %qs present on multiple clauses at %L",
-		     n->sym->name, &loc);
-	else
-	  n->sym->mark = 1;
-      }
+      for (n = oc->clauses->lists[OMP_LIST_DEVICE_RESIDENT]; n; n = n->next)
+	check_array_not_assumed (n->sym, oc->loc, "DEVICE_RESIDENT");
+    }
 
-  for (n = ns->oacc_declare_clauses->lists[OMP_LIST_DEVICE_RESIDENT]; n;
-       n = n->next)
-    check_array_not_assumed (n->sym, loc, "DEVICE_RESIDENT");
-}
+  for (oc = ns->oacc_declare; oc; oc = oc->next)
+    {
+      for (list = 0; list <= OMP_LIST_NUM; list++)
+	for (n = oc->clauses->lists[list]; n; n = n->next)
+	  {
+	    if (n->sym->mark)
+	      {
+		gfc_error ("Symbol %qs present on multiple clauses at %L",
+			   n->sym->name, &oc->loc);
+		continue;
+	      }
+	    else
+	      n->sym->mark = 1;
+	  }
+    }
 
+  for (oc = ns->oacc_declare; oc; oc = oc->next)
+    {
+      for (list = 0; list <= OMP_LIST_NUM; list++)
+	for (n = oc->clauses->lists[list]; n; n = n->next)
+	  n->sym->mark = 0;
+    }
+}
 
 void
 gfc_resolve_oacc_directive (gfc_code *code, gfc_namespace *ns ATTRIBUTE_UNUSED)
diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
index bdb5731..b280621 100644
--- a/gcc/fortran/parse.c
+++ b/gcc/fortran/parse.c
@@ -1385,7 +1385,7 @@ next_statement (void)
   case ST_EQUIVALENCE: case ST_NAMELIST: case ST_STATEMENT_FUNCTION: \
   case ST_TYPE: case ST_INTERFACE: case ST_OMP_THREADPRIVATE: \
   case ST_PROCEDURE: case ST_OMP_DECLARE_SIMD: case ST_OMP_DECLARE_REDUCTION: \
-  case ST_OMP_DECLARE_TARGET: case ST_OACC_ROUTINE
+  case ST_OMP_DECLARE_TARGET: case ST_OACC_ROUTINE: case ST_OACC_DECLARE
 
 /* Block end statements.  Errors associated with interchanging these
    are detected in gfc_match_end().  */
@@ -2449,7 +2449,6 @@ verify_st_order (st_state *p, gfc_statement st, bool silent)
     case ST_PUBLIC:
     case ST_PRIVATE:
     case ST_DERIVED_DECL:
-    case ST_OACC_DECLARE:
     case_decl:
       if (p->state >= ORDER_EXEC)
 	goto order;
@@ -3361,19 +3360,6 @@ declSt:
       st = next_statement ();
       goto loop;
 
-    case ST_OACC_DECLARE:
-      if (!verify_st_order(&ss, st, false))
-	{
-	  reject_statement ();
-	  st = next_statement ();
-	  goto loop;
-	}
-      if (gfc_state_stack->ext.oacc_declare_clauses == NULL)
-	gfc_state_stack->ext.oacc_declare_clauses = new_st.ext.omp_clauses;
-      accept_statement (st);
-      st = next_statement ();
-      goto loop;
-
     default:
       break;
     }
@@ -5213,13 +5199,6 @@ contains:
 
 done:
   gfc_current_ns->code = gfc_state_stack->head;
-  if (gfc_state_stack->state == COMP_PROGRAM
-      || gfc_state_stack->state == COMP_MODULE
-      || gfc_state_stack->state == COMP_SUBROUTINE
-      || gfc_state_stack->state == COMP_FUNCTION
-      || gfc_state_stack->state == COMP_BLOCK)
-    gfc_current_ns->oacc_declare_clauses
-      = gfc_state_stack->ext.oacc_declare_clauses;
 }
 
 
diff --git a/gcc/fortran/parse.h b/gcc/fortran/parse.h
index bcd714d..94b2ada 100644
--- a/gcc/fortran/parse.h
+++ b/gcc/fortran/parse.h
@@ -48,7 +48,7 @@ typedef struct gfc_state_data
   union
   {
     gfc_st_label *end_do_label;
-    gfc_omp_clauses *oacc_declare_clauses;
+    gfc_oacc_declare *oacc_declare_clauses;
   }
   ext;
 }
diff --git a/gcc/fortran/resolve.c b/gcc/fortran/resolve.c
index bf2837c..7719201 100644
--- a/gcc/fortran/resolve.c
+++ b/gcc/fortran/resolve.c
@@ -10646,6 +10646,7 @@ start:
 	case EXEC_OACC_ENTER_DATA:
 	case EXEC_OACC_EXIT_DATA:
 	case EXEC_OACC_ATOMIC:
+	case EXEC_OACC_DECLARE:
 	  gfc_resolve_oacc_directive (code, ns);
 	  break;
 
diff --git a/gcc/fortran/st.c b/gcc/fortran/st.c
index 629b51d..d0a11aa 100644
--- a/gcc/fortran/st.c
+++ b/gcc/fortran/st.c
@@ -185,6 +185,11 @@ gfc_free_statement (gfc_code *p)
       gfc_free_forall_iterator (p->ext.forall_iterator);
       break;
 
+    case EXEC_OACC_DECLARE:
+      if (p->ext.oacc_declare)
+	gfc_free_oacc_declare_clauses (p->ext.oacc_declare);
+      break;
+
     case EXEC_OACC_PARALLEL_LOOP:
     case EXEC_OACC_PARALLEL:
     case EXEC_OACC_KERNELS_LOOP:
diff --git a/gcc/fortran/symbol.c b/gcc/fortran/symbol.c
index bd7758b..ff9aff9 100644
--- a/gcc/fortran/symbol.c
+++ b/gcc/fortran/symbol.c
@@ -375,6 +375,11 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
     *contiguous = "CONTIGUOUS", *generic = "GENERIC";
   static const char *threadprivate = "THREADPRIVATE";
   static const char *omp_declare_target = "OMP DECLARE TARGET";
+  static const char *oacc_declare_copyin = "OACC DECLARE COPYIN";
+  static const char *oacc_declare_create = "OACC DECLARE CREATE";
+  static const char *oacc_declare_deviceptr = "OACC DECLARE DEVICEPTR";
+  static const char *oacc_declare_device_resident =
+						"OACC DECLARE DEVICE_RESIDENT";
 
   const char *a1, *a2;
   int standard;
@@ -511,6 +516,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (in_equivalence, allocatable);
   conf (in_equivalence, threadprivate);
   conf (in_equivalence, omp_declare_target);
+  conf (in_equivalence, oacc_declare_create);
+  conf (in_equivalence, oacc_declare_copyin);
+  conf (in_equivalence, oacc_declare_deviceptr);
+  conf (in_equivalence, oacc_declare_device_resident);
 
   conf (dummy, result);
   conf (entry, result);
@@ -560,6 +569,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (cray_pointee, in_equivalence);
   conf (cray_pointee, threadprivate);
   conf (cray_pointee, omp_declare_target);
+  conf (cray_pointee, oacc_declare_create);
+  conf (cray_pointee, oacc_declare_copyin);
+  conf (cray_pointee, oacc_declare_deviceptr);
+  conf (cray_pointee, oacc_declare_device_resident);
 
   conf (data, dummy);
   conf (data, function);
@@ -614,6 +627,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
   conf (proc_pointer, abstract)
 
   conf (entry, omp_declare_target)
+  conf (entry, oacc_declare_create)
+  conf (entry, oacc_declare_copyin)
+  conf (entry, oacc_declare_deviceptr)
+  conf (entry, oacc_declare_device_resident)
 
   a1 = gfc_code2string (flavors, attr->flavor);
 
@@ -651,6 +668,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
       conf2 (subroutine);
       conf2 (threadprivate);
       conf2 (omp_declare_target);
+      conf2 (oacc_declare_create);
+      conf2 (oacc_declare_copyin);
+      conf2 (oacc_declare_deviceptr);
+      conf2 (oacc_declare_device_resident);
 
       if (attr->access == ACCESS_PUBLIC || attr->access == ACCESS_PRIVATE)
 	{
@@ -733,6 +754,10 @@ check_conflict (symbol_attribute *attr, const char *name, locus *where)
       conf2 (threadprivate);
       conf2 (result);
       conf2 (omp_declare_target);
+      conf2 (oacc_declare_create);
+      conf2 (oacc_declare_copyin);
+      conf2 (oacc_declare_deviceptr);
+      conf2 (oacc_declare_device_resident);
 
       if (attr->intent != INTENT_UNKNOWN)
 	{
@@ -1244,6 +1269,66 @@ gfc_add_omp_declare_target (symbol_attribute *attr, const char *name,
 
 
 bool
+gfc_add_oacc_declare_create (symbol_attribute *attr, const char *name,
+			     locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_create)
+    return true;
+
+  attr->oacc_declare_create = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_copyin (symbol_attribute *attr, const char *name,
+			     locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_copyin)
+    return true;
+
+  attr->oacc_declare_copyin = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_deviceptr (symbol_attribute *attr, const char *name,
+				locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_deviceptr)
+    return true;
+
+  attr->oacc_declare_deviceptr = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_device_resident (symbol_attribute *attr, const char *name,
+				      locus *where)
+{
+  if (check_used (attr, name, where))
+    return false;
+
+  if (attr->oacc_declare_device_resident)
+    return true;
+
+  attr->oacc_declare_device_resident = 1;
+  return check_conflict (attr, name, where);
+}
+
+
+bool
 gfc_add_target (symbol_attribute *attr, locus *where)
 {
 
@@ -1820,6 +1905,18 @@ gfc_copy_attr (symbol_attribute *dest, symbol_attribute *src, locus *where)
   if (src->omp_declare_target
       && !gfc_add_omp_declare_target (dest, NULL, where))
     goto fail;
+  if (src->oacc_declare_create
+      && !gfc_add_oacc_declare_create (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_copyin
+      && !gfc_add_oacc_declare_copyin (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_deviceptr
+      && !gfc_add_oacc_declare_deviceptr (dest, NULL, where))
+    goto fail;
+  if (src->oacc_declare_device_resident
+      && !gfc_add_oacc_declare_device_resident (dest, NULL, where))
+    goto fail;
   if (src->target && !gfc_add_target (dest, where))
     goto fail;
   if (src->dummy && !gfc_add_dummy (dest, NULL, where))
diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c
index 7e05e67..0da46ba 100644
--- a/gcc/fortran/trans-decl.c
+++ b/gcc/fortran/trans-decl.c
@@ -5759,6 +5759,149 @@ is_ieee_module_used (gfc_namespace *ns)
 }
 
 
+static gfc_omp_clauses *module_oacc_clauses;
+
+
+static void
+add_clause (gfc_symbol *sym, gfc_omp_map_op map_op)
+{
+  gfc_omp_namelist *n;
+
+  n = gfc_get_omp_namelist ();
+  n->sym = sym;
+  n->u.map_op = map_op;
+
+  if (!module_oacc_clauses)
+    module_oacc_clauses = gfc_get_omp_clauses ();
+
+  if (module_oacc_clauses->lists[OMP_LIST_MAP])
+    n->next = module_oacc_clauses->lists[OMP_LIST_MAP];
+
+  module_oacc_clauses->lists[OMP_LIST_MAP] = n;
+}
+
+
+static void
+find_module_oacc_declare_clauses (gfc_symbol *sym)
+{
+  if (sym->attr.use_assoc)
+    {
+      gfc_omp_map_op map_op;
+
+      if (sym->attr.oacc_declare_create)
+	map_op = OMP_MAP_FORCE_ALLOC;
+
+      if (sym->attr.oacc_declare_copyin)
+	map_op = OMP_MAP_FORCE_TO;
+
+      if (sym->attr.oacc_declare_deviceptr)
+	map_op = OMP_MAP_FORCE_DEVICEPTR;
+
+      if (sym->attr.oacc_declare_device_resident)
+	map_op = OMP_MAP_DEVICE_RESIDENT;
+
+      if (sym->attr.oacc_declare_create
+	  || sym->attr.oacc_declare_copyin
+	  || sym->attr.oacc_declare_deviceptr
+	  || sym->attr.oacc_declare_device_resident)
+	{
+	  sym->attr.referenced = 1;
+	  add_clause (sym, map_op);
+	}
+    }
+}
+
+
+void
+finish_oacc_declare (gfc_namespace *ns, gfc_symbol *sym, bool block)
+{
+  gfc_code *code;
+  gfc_oacc_declare *oc;
+  locus where = gfc_current_locus;
+  gfc_omp_clauses *omp_clauses = NULL;
+  gfc_omp_namelist *n, *p;
+
+  gfc_traverse_ns (ns, find_module_oacc_declare_clauses);
+
+  if (module_oacc_clauses && sym->attr.flavor == FL_PROGRAM)
+    {
+      gfc_oacc_declare *new_oc;
+
+      new_oc = gfc_get_oacc_declare ();
+      new_oc->next = ns->oacc_declare;
+      new_oc->clauses = module_oacc_clauses;
+
+      ns->oacc_declare = new_oc;
+      module_oacc_clauses = NULL;
+    }
+
+  if (!ns->oacc_declare)
+    return;
+
+  for (oc = ns->oacc_declare; oc; oc = oc->next)
+    {
+      if (oc->module_var)
+	continue;
+
+      if (block)
+	gfc_error ("Sorry, $!ACC DECLARE at %L is not allowed "
+		   "in BLOCK construct", &oc->loc);
+
+
+      if (oc->clauses && oc->clauses->lists[OMP_LIST_MAP])
+	{
+	  if (omp_clauses == NULL)
+	    {
+	      omp_clauses = oc->clauses;
+	      continue;
+	    }
+
+	  for (n = oc->clauses->lists[OMP_LIST_MAP]; n; p = n, n = n->next)
+	    ;
+
+	  gcc_assert (p->next == NULL);
+
+	  p->next = omp_clauses->lists[OMP_LIST_MAP];
+	  omp_clauses = oc->clauses;
+	}
+    }
+
+  if (!omp_clauses)
+    return;
+
+  for (n = omp_clauses->lists[OMP_LIST_MAP]; n; n = n->next)
+    {
+      switch (n->u.map_op)
+	{
+	  case OMP_MAP_DEVICE_RESIDENT:
+	    n->u.map_op = OMP_MAP_FORCE_ALLOC;
+	    break;
+
+	  default:
+	    break;
+	}
+    }
+
+  code = XCNEW (gfc_code);
+  code->op = EXEC_OACC_DECLARE;
+  code->loc = where;
+
+  code->ext.oacc_declare = gfc_get_oacc_declare ();
+  code->ext.oacc_declare->clauses = omp_clauses;
+
+  code->block = XCNEW (gfc_code);
+  code->block->op = EXEC_OACC_DECLARE;
+  code->block->loc = where;
+
+  if (ns->code)
+    code->block->next = ns->code;
+
+  ns->code = code;
+
+  return;
+}
+
+
 /* Generate code for a function.  */
 
 void
@@ -5895,12 +6038,7 @@ gfc_generate_function_code (gfc_namespace * ns)
   if ((gfc_option.rtcheck & GFC_RTCHECK_BOUNDS) && !sym->attr.is_bind_c)
     add_argument_checking (&body, sym);
 
-  /* Generate !$ACC DECLARE directive. */
-  if (ns->oacc_declare_clauses)
-    {
-      tree tmp = gfc_trans_oacc_declare (&body, ns);
-      gfc_add_expr_to_block (&body, tmp);
-    }
+  finish_oacc_declare (ns, sym, false);
 
   tmp = gfc_trans_code (ns->code);
   gfc_add_expr_to_block (&body, tmp);
diff --git a/gcc/fortran/trans-openmp.c b/gcc/fortran/trans-openmp.c
index f29f408..261291c 100644
--- a/gcc/fortran/trans-openmp.c
+++ b/gcc/fortran/trans-openmp.c
@@ -4421,13 +4421,24 @@ gfc_trans_omp_workshare (gfc_code *code, gfc_omp_clauses *clauses)
 }
 
 tree
-gfc_trans_oacc_declare (stmtblock_t *block, gfc_namespace *ns)
+gfc_trans_oacc_declare (gfc_code *code)
 {
-  tree oacc_clauses;
-  oacc_clauses = gfc_trans_omp_clauses (block, ns->oacc_declare_clauses,
-					ns->oacc_declare_clauses->loc);
-  return build1_loc (ns->oacc_declare_clauses->loc.lb->location,
-		     OACC_DECLARE, void_type_node, oacc_clauses);
+  stmtblock_t block;
+  tree stmt, oacc_clauses;
+  enum tree_code construct_code;
+
+  construct_code = OACC_DATA;
+
+  gfc_start_block (&block);
+
+  oacc_clauses = gfc_trans_omp_clauses (&block, code->ext.oacc_declare->clauses,
+					code->loc);
+  stmt = gfc_trans_omp_code (code->block->next, true);
+  stmt = build2_loc (input_location, construct_code, void_type_node, stmt,
+		     oacc_clauses);
+  gfc_add_expr_to_block (&block, stmt);
+
+  return gfc_finish_block (&block);
 }
 
 tree
@@ -4455,6 +4466,8 @@ gfc_trans_oacc_directive (gfc_code *code)
       return gfc_trans_oacc_wait_directive (code);
     case EXEC_OACC_ATOMIC:
       return gfc_trans_omp_atomic (code);
+    case EXEC_OACC_DECLARE:
+      return gfc_trans_oacc_declare (code);
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/fortran/trans-stmt.c b/gcc/fortran/trans-stmt.c
index 1af2ad1..a481eaf 100644
--- a/gcc/fortran/trans-stmt.c
+++ b/gcc/fortran/trans-stmt.c
@@ -1575,12 +1575,7 @@ gfc_trans_block_construct (gfc_code* code)
   exit_label = gfc_build_label_decl (NULL_TREE);
   code->exit_label = exit_label;
 
-  /* Generate !$ACC DECLARE directive. */
-  if (ns->oacc_declare_clauses)
-    {
-      tree tmp = gfc_trans_oacc_declare (&body, ns);
-      gfc_add_expr_to_block (&body, tmp);
-    }
+  finish_oacc_declare (ns, sym, true);
 
   gfc_add_expr_to_block (&body, gfc_trans_code (ns->code));
   gfc_add_expr_to_block (&body, build1_v (LABEL_EXPR, exit_label));
diff --git a/gcc/fortran/trans-stmt.h b/gcc/fortran/trans-stmt.h
index 2f2a0b3..0ff93c4 100644
--- a/gcc/fortran/trans-stmt.h
+++ b/gcc/fortran/trans-stmt.h
@@ -67,7 +67,7 @@ void gfc_trans_omp_declare_simd (gfc_namespace *);
 
 /* trans-openacc.c */
 tree gfc_trans_oacc_directive (gfc_code *);
-tree gfc_trans_oacc_declare (stmtblock_t *block, gfc_namespace *);
+tree gfc_trans_oacc_declare (gfc_namespace *);
 
 /* trans-io.c */
 tree gfc_trans_open (gfc_code *);
diff --git a/gcc/fortran/trans.c b/gcc/fortran/trans.c
index d9ab346..eee9740 100644
--- a/gcc/fortran/trans.c
+++ b/gcc/fortran/trans.c
@@ -1901,6 +1901,7 @@ trans_code (gfc_code * code, tree cond)
 	case EXEC_OACC_ENTER_DATA:
 	case EXEC_OACC_EXIT_DATA:
 	case EXEC_OACC_ATOMIC:
+	case EXEC_OACC_DECLARE:
 	  res = gfc_trans_oacc_directive (code);
 	  break;
 
diff --git a/gcc/testsuite/gfortran.dg/goacc/declare-1.f95 b/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
index 5cf737f..1ff8e6a 100644
--- a/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
+++ b/gcc/testsuite/gfortran.dg/goacc/declare-1.f95
@@ -1,5 +1,4 @@
 ! { dg-do compile } 
-! { dg-additional-options "-fdump-tree-original" } 
 
 program test
   implicit none
@@ -11,9 +10,7 @@ contains
     integer, value :: n
     BLOCK
        integer i
-       !$acc declare copy(i)
+       !$acc declare copy(i) ! { dg-error "is not allowed" }
     END BLOCK
   end function foo
 end program test
-! { dg-prune-output "unimplemented" }
-! { dg-final { scan-tree-dump-times "pragma acc declare map\\(force_tofrom:i\\)" 2 "original" } } 
diff --git a/gcc/testsuite/gfortran.dg/goacc/declare-2.f95 b/gcc/testsuite/gfortran.dg/goacc/declare-2.f95
new file mode 100644
index 0000000..aa1704f
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/goacc/declare-2.f95
@@ -0,0 +1,71 @@
+
+module amod
+
+contains
+
+subroutine asubr (b)
+  implicit none
+  integer :: b(8)
+
+  !$acc declare copy (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare copyout (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copy (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copyin (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_copyout (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare present_or_create (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare deviceptr (b) ! { dg-error "Invalid clause in module" }
+  !$acc declare create (b) copyin (b) ! { dg-error "present on multiple clauses" }
+
+end subroutine
+
+end module
+
+module bmod
+
+  implicit none
+  integer :: a, b, c, d, e, f, g, h, i
+  common /data1/ a, b, c
+  common /data2/ d, e, f
+  common /data3/ g, h, i
+  !$acc declare link (a) ! { dg-error "element of a COMMON" }
+  !$acc declare link (/data1/)
+  !$acc declare link (a, b, c) ! { dg-error "element of a COMMON" }
+  !$acc declare link (/foo/) ! { dg-error "not found" }
+  !$acc declare device_resident (/data2/)
+  !$acc declare device_resident (/data3/) ! { dg-error "present on multiple clauses" }
+  !$acc declare device_resident (g, h, i)
+
+end module
+
+subroutine bsubr (foo)
+  implicit none
+
+  integer, dimension (:) :: foo
+
+  !$acc declare copy (foo) ! { dg-error "Assumed-size dummy array" }
+  !$acc declare copy (foo(1:2)) ! { dg-error "Assumed-size dummy array" }
+
+end subroutine bsubr
+
+subroutine multiline
+  integer :: b(8)
+
+  !$acc declare copyin (b) ! { dg-error "present on multiple clauses" }
+  !$acc declare copyin (b)
+
+end subroutine multiline
+
+subroutine subarray
+  integer :: c(8)
+
+  !$acc declare copy (c(1:2)) ! { dg-error "Array sections: 'c' not allowed" }
+
+end subroutine subarray
+
+program test
+  integer :: a(8)
+
+  !$acc declare create (a) copyin (a) ! { dg-error "present on multiple clauses" }
+
+end program
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
new file mode 100644
index 0000000..f717d1b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
@@ -0,0 +1,248 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  implicit none
+  integer z
+  !$acc declare create (z)
+end module vars
+
+subroutine subr6 (a, d)
+  implicit none
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare deviceptr (a)
+  integer :: d(N)
+
+  i = 0
+
+  !$acc parallel copy (d)
+    do i = 1, N
+      d(i) = a(i) + a(i)
+    end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr5 (a, b, c, d)
+  implicit none
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present_or_copyin (a)
+  integer :: b(N)
+  !$acc declare present_or_create (b)
+  integer :: c(N)
+  !$acc declare present_or_copyout (c)
+  integer :: d(N)
+  !$acc declare present_or_copy (d)
+
+  i = 0
+
+  !$acc parallel
+    do i = 1, N
+      b(i) = a(i)
+      c(i) = b(i)
+      d(i) = d(i) + b(i)
+    end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr4 (a, b)
+  implicit none
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: b(N)
+  !$acc declare copyout (b)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    b(i) = a(i)
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr3 (a, c)
+  implicit none
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: c(N)
+  !$acc declare copyin (c)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    a(i) = c(i)
+    c(i) = 0
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr2 (a, b, c)
+  implicit none
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+  integer :: b(N)
+  !$acc declare create (b)
+  integer :: c(N)
+  !$acc declare copy (c)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    b(i) = a(i)
+    c(i) = b(i) + c(i) + 1
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine subr1 (a)
+  implicit none
+  integer, parameter :: N = 8
+  integer :: i
+  integer :: a(N)
+  !$acc declare present (a)
+
+  i = 0
+
+  !$acc parallel
+  do i = 1, N
+    a(i) = a(i) + 1
+  end do
+  !$acc end parallel
+
+end subroutine
+
+subroutine test (a, e)
+  use openacc
+  implicit none
+  logical :: e
+  integer, parameter :: N = 8
+  integer :: a(N)
+
+  if (acc_is_present (a) .neqv. e) call abort
+
+end subroutine
+
+subroutine subr0 (a, b, c, d)
+  implicit none
+  integer, parameter :: N = 8
+  integer :: a(N)
+  !$acc declare copy (a)
+  integer :: b(N)
+  integer :: c(N)
+  integer :: d(N)
+  integer :: i
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  call subr1 (a)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  call subr2 (a, b, c)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (c(i) .ne. 8) call abort
+  end do
+
+  call subr3 (a, c)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (a(i) .ne. 2) call abort
+    if (c(i) .ne. 8) call abort
+  end do
+
+  call subr4 (a, b)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+
+  do i = 1, N
+    if (b(i) .ne. 8) call abort
+  end do
+
+  call subr5 (a, b, c, d)
+
+  call test (a, .true.)
+  call test (b, .false.)
+  call test (c, .false.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (c(i) .ne. 8) call abort
+    if (d(i) .ne. 13) call abort
+  end do
+
+  call subr6 (a, d)
+
+  call test (a, .true.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (d(i) .ne. 16) call abort
+  end do
+
+end subroutine
+
+program main
+  use vars
+  use openacc
+  implicit none
+  integer, parameter :: N = 8
+  integer :: a(N)
+  integer :: b(N)
+  integer :: c(N)
+  integer :: d(N)
+  integer :: i
+
+  a(:) = 2
+  b(:) = 3
+  c(:) = 4
+  d(:) = 5
+
+  if (acc_is_present (z) .neqv. .true.) call abort
+
+  call subr0 (a, b, c, d)
+
+  call test (a, .false.)
+  call test (b, .false.)
+  call test (c, .false.)
+  call test (d, .false.)
+
+  do i = 1, N
+    if (a(i) .ne. 8) call abort
+    if (b(i) .ne. 8) call abort
+    if (c(i) .ne. 8) call abort
+    if (d(i) .ne. 16) call abort
+  end do
+
+
+end program
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
new file mode 100644
index 0000000..2aa7907
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
@@ -0,0 +1,16 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module globalvars
+  implicit none
+  integer a
+  !$acc declare create (a)
+end module globalvars
+
+program test
+  use globalvars
+  use openacc
+  implicit none
+
+  if (acc_is_present (a) .neqv. .true.) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
new file mode 100644
index 0000000..3a6b420
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
@@ -0,0 +1,68 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module globalvars
+  implicit none
+  real b
+  !$acc declare link (b)
+end module globalvars
+
+program test
+  use openacc
+  use globalvars
+  implicit none
+
+  real a
+  real c
+  !$acc declare link (c)
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+  if (acc_is_present (c) .neqv. .false.) call abort
+
+  a = 0.0
+  b = 1.0
+
+  !$acc parallel copy (a) copyin (b)
+    b = b + 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 5.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) create (b)
+    b = 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) copy (b)
+    b = 4.0
+    a = b
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+  if (b .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+  a = 0.0
+
+  !$acc parallel copy (a) copy (b) copy (c)
+    b = 4.0
+    c = b
+    a = c
+  !$acc end parallel
+
+  if (a .ne. 4.0) call abort
+
+  if (acc_is_present (b) .neqv. .false.) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
new file mode 100644
index 0000000..226264e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
@@ -0,0 +1,29 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  implicit none
+  real b
+ !$acc declare create (b)
+end module vars
+
+program test
+  use vars
+  use openacc
+  implicit none
+  real a
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  a = 2.0
+
+  !$acc parallel copy (a)
+    b = a
+    a = 1.0
+    a = a + b
+   !$acc end parallel
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  if (a .ne. 3.0) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
new file mode 100644
index 0000000..bcd9c9c
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
@@ -0,0 +1,29 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  implicit none
+  real b
+ !$acc declare device_resident (b)
+end module vars
+
+program test
+  use vars
+  use openacc
+  implicit none
+  real a
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  a = 2.0
+
+  !$acc parallel copy (a)
+    b = a
+    a = 1.0
+    a = a + b
+   !$acc end parallel
+
+  if (acc_is_present (b) .neqv. .true.) call abort
+
+  if (a .ne. 3.0) call abort
+
+end program test
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/declare-6.f90 b/libgomp/testsuite/libgomp.oacc-fortran/declare-6.f90
new file mode 100644
index 0000000..a4ce1e5
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-fortran/declare-6.f90
@@ -0,0 +1,66 @@
+! { dg-do run  { target openacc_nvidia_accel_selected } }
+
+module vars
+  implicit none
+  integer :: a(8)
+  !$acc declare device_resident (a)
+
+end module vars
+
+program test
+  use vars
+  implicit none
+  integer, parameter :: N = 8
+  integer :: i, v
+
+  v = 5
+  v = foo (v)
+  if (v .ne. 15) call abort
+  v = foo (v)
+  if (v .ne. 25) call abort
+
+  !$acc parallel copy (v)
+    do i = 1, N
+      a(i) = v * 2
+    end do
+  !$acc end parallel
+
+  !$acc update host (a)
+
+   do i = 1, N
+    if (a(i) .ne. 50) call abort
+   end do
+
+contains
+
+  integer function foo(n)
+    integer, value :: n
+    integer :: i
+    !$acc declare create (i)
+
+    i = 12
+
+    !$acc update device (i)
+
+    i = 13
+
+    BLOCK
+      integer :: i
+      !$acc declare create(i)
+
+      !$acc parallel copy (n)
+        i = n
+        n = 10
+        n = i + n
+      !$acc end parallel
+
+      foo = n
+    END BLOCK
+
+    !$acc update host (i)
+
+    if (i .ne. 12) call abort
+
+  end function foo
+
+end program test

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: OpenACC declare directive updates
  2015-11-19 16:22         ` James Norris
@ 2015-11-20 10:53           ` Jakub Jelinek
  2015-11-27 11:41           ` [gomp4] " Thomas Schwinge
  1 sibling, 0 replies; 51+ messages in thread
From: Jakub Jelinek @ 2015-11-20 10:53 UTC (permalink / raw)
  To: James Norris; +Cc: Cesar Philippidis, GCC Patches

On Thu, Nov 19, 2015 at 10:22:16AM -0600, James Norris wrote:
> 2015-XX-XX  James Norris  <jnorris@codesourcery.com>
> 	    Cesar Philippidis  <cesar@codesourcery.com>
> 
> 	gcc/fortran/
> 	* dump-parse-tree.c (show_namespace): Handle declares.
> 	* gfortran.h (struct symbol_attribute): New fields.
> 	(enum gfc_omp_map_map): Add OMP_MAP_DEVICE_RESIDENT and OMP_MAP_LINK.
> 	(OMP_LIST_LINK): New enum.
> 	(struct gfc_oacc_declare): New structure.
> 	(gfc_get_oacc_declare): New definition.
> 	(struct gfc_namespace): Change type.
> 	(enum gfc_exec_op): Add EXEC_OACC_DECLARE.
> 	(struct gfc_code): New field.
> 	* module.c (enum ab_attribute): Add AB_OACC_DECLARE_CREATE,
> 	AB_OACC_DECLARE_COPYIN, AB_OACC_DECLARE_DEVICEPTR,
> 	AB_OACC_DECLARE_DEVICE_RESIDENT, AB_OACC_DECLARE_LINK
> 	(attr_bits): Add new initializers.
> 	(mio_symbol_attribute): Handle new atributes.
> 	* openmp.c (gfc_free_oacc_declare_clauses): New function.
> 	(gfc_match_oacc_clause_link: Likewise.
> 	(OMP_CLAUSE_LINK): New definition.
> 	(gfc_match_omp_clauses): Handle OMP_CLAUSE_LINK.
> 	(OACC_DECLARE_CLAUSES): Add OMP_CLAUSE_LINK
> 	(gfc_match_oacc_declare): Add checking and module handling.
> 	(gfc_resolve_oacc_declare): Reimplement.
> 	* parse.c (case_decl): Add ST_OACC_DECLARE.
> 	(parse_spec): Remove handling.
> 	(parse_progunit): Remove handling.
> 	* parse.h (struct gfc_state_data): Change type.
> 	* resolve.c (gfc_resolve_blocks): Handle EXEC_OACC_DECLARE.
> 	* st.c (gfc_free_statement): Handle EXEC_OACC_DECLARE.
> 	* symbol.c (check_conflict): Add conflict checks.
> 	(gfc_add_oacc_declare_create, gfc_add_oacc_declare_copyin, 
> 	gfc_add_oacc_declare_deviceptr, gfc_add_oacc_declare_device_resident):
> 	New functions.
> 	(gfc_copy_attr): Handle new symbols.
> 	* trans-decl.c (add_clause, find_module_oacc_declare_clauses,
> 	finish_oacc_declare): New functions.
> 	(gfc_generate_function_code): Replace with call.
> 	* trans-openmp.c (gfc_trans_oacc_declare): Reimplement.
> 	(gfc_trans_oacc_directive): Handle EXEC_OACC_DECLARE.
> 	* trans-stmt.c (gfc_trans_block_construct): Replace with call.
> 	* trans-stmt.h (gfc_trans_oacc_declare): Remove argument.
> 	* trans.c (trans_code): Handle EXEC_OACC_DECLARE.
> 
> 	gcc/testsuite
> 	* gfortran.dg/goacc/declare-1.f95: Update test.
> 	* gfortran.dg/goacc/declare-2.f95: New test.
> 
> 	libgomp/
> 	* testsuite/libgomp.oacc-fortran/declare-1.f90: New test.
> 	* testsuite/libgomp.oacc-fortran/declare-2.f90: Likewise.
> 	* testsuite/libgomp.oacc-fortran/declare-3.f90: Likewise.
> 	* testsuite/libgomp.oacc-fortran/declare-4.f90: Likewise.
> 	* testsuite/libgomp.oacc-fortran/declare-5.f90: Likewise.
> 	* testsuite/libgomp.oacc-fortran/declare-5.f90: Likewise.

Ok.

	Jakub

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH] fortran/openmp.c -- Fix bootstrap
@ 2015-11-22 19:11                               ` Steve Kargl
  2015-11-22 19:27                                 ` Jerry DeLisle
  2015-11-22 20:38                                 ` James Norris
  0 siblings, 2 replies; 51+ messages in thread
From: Steve Kargl @ 2015-11-22 19:11 UTC (permalink / raw)
  To: fortran, gcc-patches

I have no idea if this is actually correct, but it restores bootstrap.
OK to commit?

2015-11-22  Steven G. Kargl  <kargl@gcc.gnu.org>

	* openmp.c (match_oacc_clause_gang): Fix bootstrap.


Index: openmp.c
===================================================================
--- openmp.c	(revision 230723)
+++ openmp.c	(working copy)
@@ -415,7 +415,8 @@ match_oacc_clause_gang (gfc_omp_clauses 
 static match
 gfc_match_oacc_clause_link (const char *str, gfc_omp_namelist **list)
 {
-  gfc_omp_namelist *head, *tail, *p;
+  gfc_omp_namelist *head = NULL;
+  gfc_omp_namelist *tail, *p;
   locus old_loc;
   char n[GFC_MAX_SYMBOL_LEN+1];
   gfc_symbol *sym;
@@ -4821,7 +4822,7 @@ gfc_resolve_oacc_declare (gfc_namespace 
 
   for (oc = ns->oacc_declare; oc; oc = oc->next)
     {
-      for (list = 0; list <= OMP_LIST_NUM; list++)
+      for (list = 0; list < OMP_LIST_NUM; list++)
 	for (n = oc->clauses->lists[list]; n; n = n->next)
 	  {
 	    n->sym->mark = 0;
@@ -4846,7 +4847,7 @@ gfc_resolve_oacc_declare (gfc_namespace 
 
   for (oc = ns->oacc_declare; oc; oc = oc->next)
     {
-      for (list = 0; list <= OMP_LIST_NUM; list++)
+      for (list = 0; list < OMP_LIST_NUM; list++)
 	for (n = oc->clauses->lists[list]; n; n = n->next)
 	  {
 	    if (n->sym->mark)
@@ -4862,7 +4863,7 @@ gfc_resolve_oacc_declare (gfc_namespace 
 
   for (oc = ns->oacc_declare; oc; oc = oc->next)
     {
-      for (list = 0; list <= OMP_LIST_NUM; list++)
+      for (list = 0; list < OMP_LIST_NUM; list++)
 	for (n = oc->clauses->lists[list]; n; n = n->next)
 	  n->sym->mark = 0;
     }
-- 
Steve

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] fortran/openmp.c -- Fix bootstrap
  2015-11-22 19:11                               ` [PATCH] fortran/openmp.c -- Fix bootstrap Steve Kargl
@ 2015-11-22 19:27                                 ` Jerry DeLisle
  2015-11-22 20:38                                 ` James Norris
  1 sibling, 0 replies; 51+ messages in thread
From: Jerry DeLisle @ 2015-11-22 19:27 UTC (permalink / raw)
  To: Steve Kargl, fortran, gcc-patches

On 11/22/2015 10:59 AM, Steve Kargl wrote:
> I have no idea if this is actually correct, but it restores bootstrap.
> OK to commit?
> 

This looks correct to me. There should be no more in the lists than
OMP_LIST_NUM. Was there a PR for this and do we know when it broke?

Jerry



^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH] fortran/openmp.c -- Fix bootstrap
  2015-11-22 19:11                               ` [PATCH] fortran/openmp.c -- Fix bootstrap Steve Kargl
  2015-11-22 19:27                                 ` Jerry DeLisle
@ 2015-11-22 20:38                                 ` James Norris
  1 sibling, 0 replies; 51+ messages in thread
From: James Norris @ 2015-11-22 20:38 UTC (permalink / raw)
  To: Steve Kargl, fortran, gcc-patches

Hi,

Patch committed to trunk as obvious. Thanks to Dominique and Steve.

Jim
====

On 11/22/2015 12:59 PM, Steve Kargl wrote:
> I have no idea if this is actually correct, but it restores bootstrap.
> OK to commit?
>
> 2015-11-22  Steven G. Kargl  <kargl@gcc.gnu.org>
>
> 	* openmp.c (match_oacc_clause_gang): Fix bootstrap.
>
>
> Index: openmp.c
> ===================================================================
> --- openmp.c	(revision 230723)
> +++ openmp.c	(working copy)
> @@ -415,7 +415,8 @@ match_oacc_clause_gang (gfc_omp_clauses
>   static match
>   gfc_match_oacc_clause_link (const char *str, gfc_omp_namelist **list)
>   {
> -  gfc_omp_namelist *head, *tail, *p;
> +  gfc_omp_namelist *head = NULL;
> +  gfc_omp_namelist *tail, *p;
>     locus old_loc;
>     char n[GFC_MAX_SYMBOL_LEN+1];
>     gfc_symbol *sym;
> @@ -4821,7 +4822,7 @@ gfc_resolve_oacc_declare (gfc_namespace
>
>     for (oc = ns->oacc_declare; oc; oc = oc->next)
>       {
> -      for (list = 0; list <= OMP_LIST_NUM; list++)
> +      for (list = 0; list < OMP_LIST_NUM; list++)
>   	for (n = oc->clauses->lists[list]; n; n = n->next)
>   	  {
>   	    n->sym->mark = 0;
> @@ -4846,7 +4847,7 @@ gfc_resolve_oacc_declare (gfc_namespace
>
>     for (oc = ns->oacc_declare; oc; oc = oc->next)
>       {
> -      for (list = 0; list <= OMP_LIST_NUM; list++)
> +      for (list = 0; list < OMP_LIST_NUM; list++)
>   	for (n = oc->clauses->lists[list]; n; n = n->next)
>   	  {
>   	    if (n->sym->mark)
> @@ -4862,7 +4863,7 @@ gfc_resolve_oacc_declare (gfc_namespace
>
>     for (oc = ns->oacc_declare; oc; oc = oc->next)
>       {
> -      for (list = 0; list <= OMP_LIST_NUM; list++)
> +      for (list = 0; list < OMP_LIST_NUM; list++)
>   	for (n = oc->clauses->lists[list]; n; n = n->next)
>   	  n->sym->mark = 0;
>       }
>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-11-12 13:34                       ` James Norris
@ 2015-11-23 12:41                         ` Thomas Schwinge
  2015-11-24  8:45                           ` [gomp4] " Thomas Schwinge
  2019-06-18 22:45                           ` [committed] Fix description of 'GOMP_MAP_FIRSTPRIVATE' (was: [OpenACC] declare directive) Thomas Schwinge
  0 siblings, 2 replies; 51+ messages in thread
From: Thomas Schwinge @ 2015-11-23 12:41 UTC (permalink / raw)
  To: James Norris; +Cc: Joseph S. Myers, GCC Patches, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 9321 bytes --]

Hi Jim!

A few things I noticed when working on merging your trunk r230275 into
gomp-4_0-branch.  Please fix these (on trunk).

| --- gcc/c-family/c-pragma.h
| +++ gcc/c-family/c-pragma.h

| @@ -176,7 +178,8 @@ enum pragma_omp_clause {
|    PRAGMA_OACC_CLAUSE_FIRSTPRIVATE = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE,
|    PRAGMA_OACC_CLAUSE_IF = PRAGMA_OMP_CLAUSE_IF,
|    PRAGMA_OACC_CLAUSE_PRIVATE = PRAGMA_OMP_CLAUSE_PRIVATE,
| -  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION
| +  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION,
| +  PRAGMA_OACC_CLAUSE_LINK = PRAGMA_OMP_CLAUSE_LINK
|  };

Maintain alphabetic sorting (as it had been present on gomp-4_0-branch, I
think?).

| --- gcc/c/c-parser.c
| +++ gcc/c/c-parser.c

| @@ -10018,6 +10023,8 @@ c_parser_omp_clause_name (c_parser *parser)
|  	    result = PRAGMA_OMP_CLAUSE_DEVICE;
|  	  else if (!strcmp ("deviceptr", p))
|  	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
| +	  else if (!strcmp ("device_resident", p))
| +	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
|  	  else if (!strcmp ("dist_schedule", p))
|  	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;

Lower-case "device_resident" sorts before "deviceptr".

| @@ -10454,10 +10461,16 @@ c_parser_oacc_data_clause (c_parser *parser, pragma_omp_clause c_kind,
|      case PRAGMA_OACC_CLAUSE_DEVICE:
|        kind = GOMP_MAP_FORCE_TO;
|        break;
| +    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
| +      kind = GOMP_MAP_DEVICE_RESIDENT;
| +      break;
|      case PRAGMA_OACC_CLAUSE_HOST:
|      case PRAGMA_OACC_CLAUSE_SELF:
|        kind = GOMP_MAP_FORCE_FROM;
|        break;
| +    case PRAGMA_OACC_CLAUSE_LINK:
| +      kind = GOMP_MAP_LINK;
| +      break;
|      case PRAGMA_OACC_CLAUSE_PRESENT:
|        kind = GOMP_MAP_FORCE_PRESENT;
|        break;

Update accepted syntax comment for c_parser_oacc_data_clause function (as
present on gomp-4_0-branch).

| --- gcc/cp/parser.c
| +++ gcc/cp/parser.c
| @@ -29128,6 +29128,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
|  	    result = PRAGMA_OMP_CLAUSE_DEVICE;
|  	  else if (!strcmp ("deviceptr", p))
|  	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
| +	  else if (!strcmp ("device_resident", p))
| +	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
|  	  else if (!strcmp ("dist_schedule", p))
|  	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;

As in gcc/c/c-parser.c.

| @@ -29541,10 +29543,16 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
|      case PRAGMA_OACC_CLAUSE_DEVICE:
|        kind = GOMP_MAP_FORCE_TO;
|        break;
| +    case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
| +      kind = GOMP_MAP_DEVICE_RESIDENT;
| +      break;
|      case PRAGMA_OACC_CLAUSE_HOST:
|      case PRAGMA_OACC_CLAUSE_SELF:
|        kind = GOMP_MAP_FORCE_FROM;
|        break;
| +    case PRAGMA_OACC_CLAUSE_LINK:
| +      kind = GOMP_MAP_LINK;
| +      break;
|      case PRAGMA_OACC_CLAUSE_PRESENT:
|        kind = GOMP_MAP_FORCE_PRESENT;
|        break;

Likewise.

| +static tree
| +cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
| +{
| +  tree clauses, stmt, t;
| +  bool error = false;
| +
| +  clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
| +					"#pragma acc declare", pragma_tok, true);
| +
| +
| +  if (find_omp_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
| +    {
| +      error_at (pragma_tok->location,
| +		"no valid clauses specified in %<#pragma acc declare%>");

The last parameter to cp_parser_oacc_all_clauses, true, is the default
anyway, and is not specified at other call sites, so remove that.  Also
remove the following double blank lines.

In the C front end, instead of find_omp_clause OMP_CLAUSE_MAP, you just
check for "!clauses" -- unless there is a reason to be different here,
for uniformity settle on one variant.

That said, even if it doesn't make sense, is it actually a hard error to
not specify any clauses with the declare directive?

| --- gcc/gimplify.c
| +++ gcc/gimplify.c

| +/* Return true if global var DECL is device resident.  */
| +
| +static bool
| +device_resident_p (tree decl)

I suggest to improve that function's very generic name, and its
descriptive comment.  Without more context, the casual reader will not
understand what "device resident" means, for example.  At least note that
this relates to the OpenACC declare directive, or something like that.

| +{
| +  tree attr = lookup_attribute ("oacc declare target", DECL_ATTRIBUTES (decl));
| +
| +  if (!attr)
| +    return false;

As discussed in
<http://news.gmane.org/find-root.php?message_id=%3C87611h1zi7.fsf%40kepler.schwinge.homeip.net%3E>
already, for "oacc declare" used in an earlier version of this patch:
there is no "oacc declare target" attribute defined/ever set, so I
suspect device_resident_p will always return false, and thus isn't doing
what it's intended to be doing?

| +
| +  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
| +    {
| +      tree c = TREE_VALUE (t);
| +      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
| +	return true;
| +    }
| +
| +  return false;
| +}

And thus here:

| @@ -5908,6 +5960,15 @@ static unsigned
|  oacc_default_clause (struct gimplify_omp_ctx *ctx, tree decl, unsigned flags)
|  {
|    const char *rkind;
| +  bool on_device = false;
| +
| +  if ((ctx->region_type & (ORT_ACC_PARALLEL | ORT_ACC_KERNELS)) != 0
| +      && is_global_var (decl)
| +      && device_resident_p (decl))
| +    {
| +      on_device = true;
| +      flags |= GOVD_MAP_TO_ONLY;
| +    }
|  
|    switch (ctx->region_type)
|      {
| @@ -5928,7 +5989,7 @@ oacc_default_clause (struct gimplify_omp_ctx *ctx, tree decl, unsigned flags)
|  	    || POINTER_TYPE_P (type))
|  	  type = TREE_TYPE (type);
|  
| -	if (AGGREGATE_TYPE_P (type))
| +	if (on_device || AGGREGATE_TYPE_P (type))
|  	  /* Aggregates default to 'present_or_copy'.  */
|  	  flags |= GOVD_MAP;
|  	else

... on_device will always be set to false?  Missing testsuite coverage?

| +static void
| +gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
| +{
| +  tree expr = *expr_p;
| +  gomp_target *stmt;
| +  tree clauses, t;
| +
| +  clauses = OACC_DECLARE_CLAUSES (expr);
| +
| +  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);

Add a comment why ORT_TARGET_DATA.

It eventually follows ever other gimplify_scan_omp_clauses call, but no
gimplify_adjust_omp_clauses call is needed here?

| --- gcc/omp-builtins.def
| +++ gcc/omp-builtins.def
| @@ -353,3 +353,5 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TARGET_ENTER_EXIT_DATA,
|  		  BT_FN_VOID_INT_SIZE_PTR_PTR_PTR_UINT_PTR, ATTR_NOTHROW_LIST)
|  DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS, "GOMP_teams",
|  		  BT_FN_VOID_UINT_UINT, ATTR_NOTHROW_LIST)
| +DEF_GOACC_BUILTIN (BUILT_IN_GOACC_DECLARE, "GOACC_declare",
| +		   BT_FN_VOID_INT_SIZE_PTR_PTR_PTR, ATTR_NOTHROW_LIST)

Sort alphabetically with the other OpenACC builtins.

| --- include/gomp-constants.h
| +++ include/gomp-constants.h
| @@ -72,6 +72,11 @@ enum gomp_map_kind
|         POINTER_SIZE_UNITS.  */
|      GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
|      /* Do not map, copy bits for firstprivate instead.  */
| +    /* OpenACC device_resident.  */
| +    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
| +    /* OpenACC link.  */
| +    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
| +    /* Allocate.  */
|      GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
|      /* Similarly, but store the value in the pointer rather than
|         pointed by the pointer.  */

I suspect the "Do not map, copy bits for firstprivate instead" comment
still applies to GOMP_MAP_FIRSTPRIVATE only, which here (unintended?) got
an "Allocate" comment added?

To answer your question from
<http://news.gmane.org/find-root.php?message_id=%3C563A3C70.6060500%40codesourcery.com%3E>:

On Wed, 4 Nov 2015 11:12:16 -0600, James Norris <jnorris@codesourcery.com> wrote:
> On 11/04/2015 10:49 AM, Thomas Schwinge wrote:
> > On Tue, 3 Nov 2015 10:31:32 -0600, James Norris <jnorris@codesourcery.com> wrote:
> >> --- a/include/gomp-constants.h
> >> +++ b/include/gomp-constants.h
> >> @@ -73,6 +73,11 @@ enum gomp_map_kind
> >>          POINTER_SIZE_UNITS.  */
> >>       GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
> >>       /* Do not map, copy bits for firstprivate instead.  */
> >> +    /* OpenACC device_resident.  */
> >> +    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
> >> +    /* OpenACC link.  */
> >> +    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
> >> +    /* Allocate.  */
> >>       GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
> >>       /* Similarly, but store the value in the pointer rather than
> >>          pointed by the pointer.  */
> >
> > Confused -- I don't see these two getting handled in libgomp?
> 
> These won't be 'seen' by libgomp. So should these
> be defined by some other means?

In this case, I suspect they should be moved later in enum gomp_map_kind,
into the "Internal to GCC, not used in libgomp" section.


Grüße
 Thomas

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 472 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [gomp4] Re: [OpenACC] declare directive
  2015-11-23 12:41                         ` Thomas Schwinge
@ 2015-11-24  8:45                           ` Thomas Schwinge
  2015-11-12 23:25                             ` [PATCH] Fix unused variable James Norris
  2019-06-18 22:45                           ` [committed] Fix description of 'GOMP_MAP_FIRSTPRIVATE' (was: [OpenACC] declare directive) Thomas Schwinge
  1 sibling, 1 reply; 51+ messages in thread
From: Thomas Schwinge @ 2015-11-24  8:45 UTC (permalink / raw)
  To: James Norris; +Cc: Joseph S. Myers, GCC Patches, Jakub Jelinek

Hi!

On Mon, 23 Nov 2015 13:37:20 +0100, I wrote:
> A few things I noticed when working on merging your trunk r230275 into
> gomp-4_0-branch.  [...]

I have now committed the merge of your trunk r230275 into
gomp-4_0-branch, in r230787.  This was non-trivial: the implementation of
OpenACC declare on gomp-4_0-branch has been rather different from what
got committed to trunk, so aside from resolving all the textual merge
conflicts, I also had to fix that up, clean up things which were only
relevant/needed for earlier revisions of OpenACC declare support on
gomp-4_0-branch: "oacc declare" function attribute removed,
BUILT_IN_GOACC_STATIC/GOACC_register_static builtin and
BT_FN_VOID_PTR_INT_UINT function type removed, goacc_allocate_static and
goacc_deallocate_static functions removed in libgomp, OACC_DECLARE tree
code restored to trunk variant, OACC_DECLARE_RETURN_CLAUSES removed, stmt
member of struct gimplify_omp_ctx removed,
gcc/varpool.c:varpool_node::get_create changes reverted.  I also had to
XFAIL a few Fortran test cases, because on gomp-4_0-branch the Fortran
front end has not yet been updated to match the changes in the generic
middle end and libgomp parts of the OpenACC declare implementation, that
are now done differently on trunk.  I expect to remove these XFAILs once
(soon) merging into gomp-4_0-branch the respective trunk commit
containing these Fortran front end changes.  So, I now effectively
applied the following patch to gomp-4_0-branch; please verify that's
alright:

commit d60891e4995b13368b305db2db1d3babebb018c9
Merge: 4002b8b 2fc5e98
Author: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Tue Nov 24 08:10:01 2015 +0000

    svn merge -r 230274:230275 svn+ssh://gcc.gnu.org/svn/gcc/trunk
    
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gomp-4_0-branch@230787 138bc75d-0d04-0410-961f-82ee72b054a4

 gcc/ChangeLog                                      |  23 +++
 gcc/builtin-types.def                              |   1 -
 gcc/c-family/ChangeLog                             |   8 +
 gcc/c-family/c-common.c                            |   1 -
 gcc/c-family/c-pragma.h                            |   4 +-
 gcc/c/ChangeLog                                    |  12 ++
 gcc/c/c-parser.c                                   | 227 +++++----------------
 gcc/cp/ChangeLog                                   |  14 ++
 gcc/cp/parser.c                                    | 224 +++++---------------
 gcc/cp/pt.c                                        |   3 -
 gcc/fortran/f95-lang.c                             |   2 -
 gcc/fortran/gfortran.h                             |   2 +-
 gcc/fortran/trans-decl.c                           |  19 +-
 gcc/fortran/trans-openmp.c                         |   6 +-
 gcc/fortran/types.def                              |   1 -
 gcc/gimplify.c                                     | 205 +++++++++++++------
 gcc/omp-builtins.def                               |   2 -
 gcc/testsuite/ChangeLog                            |   6 +
 gcc/testsuite/c-c++-common/goacc/declare-2.c       |  15 +-
 gcc/tree-pretty-print.c                            |  12 +-
 gcc/tree.def                                       |   9 +-
 gcc/tree.h                                         |   2 -
 gcc/varpool.c                                      |  52 +----
 include/ChangeLog                                  |   6 +
 include/gomp-constants.h                           |   3 +-
 libgomp/ChangeLog                                  |  11 +
 libgomp/libgomp.map                                |   3 +-
 libgomp/oacc-init.c                                |   9 +-
 libgomp/oacc-int.h                                 |   3 -
 libgomp/oacc-parallel.c                            |  88 +-------
 libgomp/testsuite/libgomp.oacc-c++/declare-1.C     |  14 +-
 .../libgomp.oacc-c-c++-common/declare-1.c          |   6 +-
 .../libgomp.oacc-c-c++-common/declare-5.c          |   4 +-
 .../testsuite/libgomp.oacc-fortran/declare-1.f90   |   1 +
 .../testsuite/libgomp.oacc-fortran/declare-3.f90   |   1 +
 35 files changed, 405 insertions(+), 594 deletions(-)

[diff --git gcc/ChangeLog gcc/ChangeLog]
diff --git gcc/builtin-types.def gcc/builtin-types.def
index 7744f01..c68fb19 100644
--- gcc/builtin-types.def
+++ gcc/builtin-types.def
@@ -450,7 +450,6 @@ DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONG_ULONG_ULONGPTR, BT_BOOL, BT_ULONG,
 		     BT_ULONG, BT_PTR_ULONG)
 DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, BT_BOOL,
 		     BT_ULONGLONG, BT_ULONGLONG, BT_PTR_ULONGLONG)
-DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
[diff --git gcc/c-family/ChangeLog gcc/c-family/ChangeLog]
diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
index cbe2f01..eff919c 100644
--- gcc/c-family/c-common.c
+++ gcc/c-family/c-common.c
@@ -831,7 +831,6 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_bnd_legacy, false },
   { "bnd_instrument",         0, 0, true, false, false,
 			      handle_bnd_instrument, false },
-  { "oacc declare",           0, -1, true,  false, false, NULL, false },
   { NULL,                     0, 0, false, false, false, NULL, false }
 };
 
diff --git gcc/c-family/c-pragma.h gcc/c-family/c-pragma.h
index 7049b79..aaebc50 100644
--- gcc/c-family/c-pragma.h
+++ gcc/c-family/c-pragma.h
@@ -181,9 +181,9 @@ enum pragma_omp_clause {
   PRAGMA_OACC_CLAUSE_DEFAULT = PRAGMA_OMP_CLAUSE_DEFAULT,
   PRAGMA_OACC_CLAUSE_FIRSTPRIVATE = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE,
   PRAGMA_OACC_CLAUSE_IF = PRAGMA_OMP_CLAUSE_IF,
-  PRAGMA_OACC_CLAUSE_LINK = PRAGMA_OMP_CLAUSE_LINK,
   PRAGMA_OACC_CLAUSE_PRIVATE = PRAGMA_OMP_CLAUSE_PRIVATE,
-  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION
+  PRAGMA_OACC_CLAUSE_REDUCTION = PRAGMA_OMP_CLAUSE_REDUCTION,
+  PRAGMA_OACC_CLAUSE_LINK = PRAGMA_OMP_CLAUSE_LINK
 };
 
 extern struct cpp_reader* parse_in;
[diff --git gcc/c/ChangeLog gcc/c/ChangeLog]
diff --git gcc/c/c-parser.c gcc/c/c-parser.c
index 3f66e6a..fc88e3e 100644
--- gcc/c/c-parser.c
+++ gcc/c/c-parser.c
@@ -1228,6 +1228,7 @@ static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
 					     unsigned int * = NULL);
+static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
 static void c_parser_omp_construct (c_parser *);
@@ -1248,7 +1249,6 @@ static bool c_parser_omp_target (c_parser *, enum pragma_context);
 static void c_parser_omp_end_declare_target (c_parser *);
 static void c_parser_omp_declare (c_parser *, enum pragma_context);
 static bool c_parser_omp_ordered (c_parser *, enum pragma_context);
-static void c_parser_oacc_declare (c_parser *parser);
 static void c_parser_oacc_routine (c_parser *parser, enum pragma_context);
 
 /* These Objective-C parser functions are only ever called when
@@ -10054,13 +10054,13 @@ c_parser_omp_clause_name (c_parser *parser, bool consume_token = true)
 	    result = PRAGMA_OMP_CLAUSE_DEPEND;
 	  else if (!strcmp ("device", p))
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
+	  else if (!strcmp ("deviceptr", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
 	  else if (!strcmp ("device_resident", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("device_type", p)
 		   || !strcmp ("dtype", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICE_TYPE;
-	  else if (!strcmp ("deviceptr", p))
-	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -12914,6 +12914,10 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
 	  c_name = "device";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICEPTR:
+	  clauses = c_parser_oacc_data_clause_deviceptr (parser, clauses);
+	  c_name = "deviceptr";
+	  break;
 	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
 	  clauses = c_parser_oacc_data_clause (parser, c_kind, clauses);
 	  c_name = "device_resident";
@@ -12924,10 +12928,6 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  c_name = "device_type";
 	  seen_dtype = true;
 	  break;
-	case PRAGMA_OACC_CLAUSE_DEVICEPTR:
-	  clauses = c_parser_oacc_data_clause_deviceptr (parser, clauses);
-	  c_name = "deviceptr";
-	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = c_parser_omp_clause_firstprivate (parser, clauses);
 	  c_name = "firstprivate";
@@ -13419,8 +13419,6 @@ c_parser_oacc_data (location_t loc, c_parser *parser)
    # pragma acc declare oacc-data-clause[optseq] new-line
 */
 
-static int oacc_dcl_idx = 0;
-
 #define OACC_DECLARE_CLAUSE_MASK					\
 	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
@@ -13439,7 +13437,7 @@ static void
 c_parser_oacc_declare (c_parser *parser)
 {
   location_t pragma_loc = c_parser_peek_token (parser)->location;
-  tree c, clauses, ret_clauses, stmt, t;
+  tree clauses, stmt, t, decl;
 
   bool error = false;
 
@@ -13457,11 +13455,10 @@ c_parser_oacc_declare (c_parser *parser)
   for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
     {
       location_t loc = OMP_CLAUSE_LOCATION (t);
-      tree decl = OMP_CLAUSE_DECL (t);
-      tree devres = NULL_TREE;
+      decl = OMP_CLAUSE_DECL (t);
       if (!DECL_P (decl))
 	{
-	  error_at (loc, "subarray in %<#pragma acc declare%>");
+	  error_at (loc, "array section in %<#pragma acc declare%>");
 	  error = true;
 	  continue;
 	}
@@ -13471,10 +13468,7 @@ c_parser_oacc_declare (c_parser *parser)
 	case GOMP_MAP_FORCE_ALLOC:
 	case GOMP_MAP_FORCE_TO:
 	case GOMP_MAP_FORCE_DEVICEPTR:
-	  break;
-
 	case GOMP_MAP_DEVICE_RESIDENT:
-	  devres = t;
 	  break;
 
 	case GOMP_MAP_POINTER:
@@ -13483,7 +13477,9 @@ c_parser_oacc_declare (c_parser *parser)
 	  break;
 
 	case GOMP_MAP_LINK:
-	  if (!global_bindings_p () && !DECL_EXTERNAL (decl))
+	  if (!global_bindings_p ()
+	      && (TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
 	    {
 	      error_at (loc,
 			"%qD must be a global variable in"
@@ -13520,173 +13516,58 @@ c_parser_oacc_declare (c_parser *parser)
 	  break;
 	}
 
-      tree decl_for_attr = decl;
-      tree prev_attr = lookup_attribute ("oacc declare",
-					 DECL_ATTRIBUTES (decl));
-      if (prev_attr)
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl))
+	  || lookup_attribute ("omp declare target link",
+			       DECL_ATTRIBUTES (decl)))
 	{
-	  tree p = TREE_VALUE (prev_attr);
-	  tree cl = TREE_VALUE (p);
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
 
-	  if (!devres && OMP_CLAUSE_MAP_KIND (cl) != GOMP_MAP_DEVICE_RESIDENT)
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_MAP_KIND (t) == GOMP_MAP_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl)
+			   = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+
+	  if (global_bindings_p ())
 	    {
-	      error_at (loc, "variable %qD used more than once with "
-			     "%<#pragma acc declare%>", decl);
-	      inform (OMP_CLAUSE_LOCATION (cl), "previous directive was here");
-	      error = true;
-	      continue;
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
 	    }
 	}
-
-      if (!error && global_bindings_p ())
-	{
-	  tree attr = tree_cons (NULL_TREE, clauses, NULL_TREE);
-	  tree attrs = tree_cons (get_identifier ("oacc declare"),
-				  attr, NULL_TREE);
-	  decl_attributes (&decl_for_attr, attrs, 0);
-	}
     }
 
-  if (error)
+  if (error || global_bindings_p ())
     return;
 
-  ret_clauses = NULL_TREE;
+  stmt = make_node (OACC_DECLARE);
+  TREE_TYPE (stmt) = void_type_node;
+  OACC_DECLARE_CLAUSES (stmt) = clauses;
+  SET_EXPR_LOCATION (stmt, pragma_loc);
 
-  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
-    {
-      bool ret = false;
-      HOST_WIDE_INT kind, new_op;
+  add_stmt (stmt);
 
-      kind = OMP_CLAUSE_MAP_KIND (c);
-
-      switch (kind)
-	{
-	  case GOMP_MAP_ALLOC:
-	  case GOMP_MAP_FORCE_ALLOC:
-	  case GOMP_MAP_FORCE_TO:
-	    new_op = GOMP_MAP_FORCE_DEALLOC;
-	    ret = true;
-	    break;
-
-	  case GOMP_MAP_FORCE_FROM:
-	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
-	    new_op = GOMP_MAP_FORCE_FROM;
-	    ret = true;
-	    break;
-
-	  case GOMP_MAP_FORCE_TOFROM:
-	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
-	    new_op = GOMP_MAP_FORCE_FROM;
-	    ret = true;
-	    break;
-
-	  case GOMP_MAP_FROM:
-	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
-	    new_op = GOMP_MAP_FROM;
-	    ret = true;
-	    break;
-
-	  case GOMP_MAP_TOFROM:
-	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
-	    new_op = GOMP_MAP_FROM;
-	    ret = true;
-	    break;
-
-	  case GOMP_MAP_DEVICE_RESIDENT:
-	  case GOMP_MAP_FORCE_DEVICEPTR:
-	  case GOMP_MAP_FORCE_PRESENT:
-	  case GOMP_MAP_LINK:
-	  case GOMP_MAP_POINTER:
-	  case GOMP_MAP_TO:
-	    break;
-
-	  default:
-	    gcc_unreachable ();
-	    break;
-	}
-
-      if (ret)
-	{
-	  t = copy_node (c);
-
-	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
-
-	  if (ret_clauses)
-	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
-
-	  ret_clauses = t;
-	}
-    }
-
-  if (global_bindings_p ())
-    {
-      char buf[128];
-      struct c_declarator *target;
-      tree stmt, attrs;
-      c_arg_info *arg_info = build_arg_info ();
-      struct c_declarator *declarator;
-      struct c_declspecs *specs;
-      struct c_typespec spec;
-      location_t loc = UNKNOWN_LOCATION;
-      tree f, t, fnbody, call_fn;
-
-      sprintf (buf, "__openacc_c_constructor__%d", oacc_dcl_idx++);
-      target = build_id_declarator (get_identifier (buf));
-      arg_info->types = void_list_node;
-      declarator = build_function_declarator (arg_info, target);
-
-      specs = build_null_declspecs ();
-      spec.kind = ctsk_resword;
-      spec.spec = get_identifier ("void");
-      spec.expr = NULL_TREE;
-      spec.expr_const_operands = true;
-
-      declspecs_add_type (pragma_loc, specs, spec);
-      finish_declspecs (specs);
-
-      attrs = tree_cons (get_identifier ("constructor") , NULL_TREE, NULL_TREE);
-      start_function (specs, declarator, attrs);
-      store_parm_decls ();
-      f = c_begin_compound_stmt (true);
-      TREE_USED (current_function_decl) = 1;
-      call_fn = builtin_decl_explicit (BUILT_IN_GOACC_STATIC);
-      TREE_SIDE_EFFECTS (call_fn) = 1;
-
-      for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
-	{
-	  tree d, a1, a2, a3;
-	  vec<tree, va_gc> *args;
-	  vec_alloc (args, 3);
-
-	  d = OMP_CLAUSE_DECL (t);
-
-	  a1 = build_unary_op (loc, ADDR_EXPR, d, 0);
-	  a2 = DECL_SIZE_UNIT (d);
-	  a3 = build_int_cst (unsigned_type_node, OMP_CLAUSE_MAP_KIND (t));
-
-	  args->quick_push (a1);
-	  args->quick_push (a2);
-	  args->quick_push (a3);
-
-	  stmt = build_function_call_vec (loc, vNULL, call_fn, args, NULL);
-	  add_stmt (stmt);
-	}
-
-	fnbody = c_end_compound_stmt (loc, f, true);
-	add_stmt (fnbody);
-
-      finish_function ();
-    }
-  else
-    {
-      stmt = make_node (OACC_DECLARE);
-      TREE_TYPE (stmt) = void_type_node;
-      OACC_DECLARE_CLAUSES (stmt) = clauses;
-      OACC_DECLARE_RETURN_CLAUSES (stmt) = ret_clauses;
-      SET_EXPR_LOCATION (stmt, pragma_loc);
-
-      add_stmt (stmt);
-    }
+  return;
 }
 
 /* OpenACC 2.0:
[diff --git gcc/cp/ChangeLog gcc/cp/ChangeLog]
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 1e15e79..3158905 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -29133,13 +29133,13 @@ cp_parser_omp_clause_name (cp_parser *parser, bool consume_token = true)
 	    result = PRAGMA_OMP_CLAUSE_DEPEND;
 	  else if (!strcmp ("device", p))
 	    result = PRAGMA_OMP_CLAUSE_DEVICE;
+	  else if (!strcmp ("deviceptr", p))
+	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
 	  else if (!strcmp ("device_resident", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT;
 	  else if (!strcmp ("device_type", p)
 		   || !strcmp ("dtype", p))
 	    result = PRAGMA_OACC_CLAUSE_DEVICE_TYPE;
-	  else if (!strcmp ("deviceptr", p))
-	    result = PRAGMA_OACC_CLAUSE_DEVICEPTR;
 	  else if (!strcmp ("dist_schedule", p))
 	    result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE;
 	  break;
@@ -31696,6 +31696,10 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
 	  c_name = "device";
 	  break;
+	case PRAGMA_OACC_CLAUSE_DEVICEPTR:
+	  clauses = cp_parser_oacc_data_clause_deviceptr (parser, clauses);
+	  c_name = "deviceptr";
+	  break;
 	case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT:
 	  clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses);
 	  c_name = "device_resident";
@@ -31706,10 +31710,6 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  c_name = "device_type";
 	  seen_dtype = true;
 	  break;
-	case PRAGMA_OACC_CLAUSE_DEVICEPTR:
-	  clauses = cp_parser_oacc_data_clause_deviceptr (parser, clauses);
-	  c_name = "deviceptr";
-	  break;
 	case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE:
 	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_FIRSTPRIVATE,
 					    clauses);
@@ -34707,8 +34707,6 @@ cp_parser_oacc_data (cp_parser *parser, cp_token *pragma_tok)
    # pragma acc declare oacc-data-clause[optseq] new-line
 */
 
-static int oacc_dcl_idx = 0;
-
 #define OACC_DECLARE_CLAUSE_MASK					\
 	( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY)		\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN)		\
@@ -34726,14 +34724,12 @@ static int oacc_dcl_idx = 0;
 static tree
 cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
 {
-  tree c, clauses, ret_clauses, stmt, t;
+  tree clauses, stmt;
   bool error = false;
 
-
   clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
 					"#pragma acc declare", pragma_tok);
 
-
   if (find_omp_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
     {
       error_at (pragma_tok->location,
@@ -34745,10 +34741,9 @@ cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
     {
       location_t loc = OMP_CLAUSE_LOCATION (t);
       tree decl = OMP_CLAUSE_DECL (t);
-      tree devres = NULL_TREE;
       if (!DECL_P (decl))
 	{
-	  error_at (loc, "subarray in %<#pragma acc declare%>");
+	  error_at (loc, "array section in %<#pragma acc declare%>");
 	  error = true;
 	  continue;
 	}
@@ -34758,10 +34753,7 @@ cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
 	case GOMP_MAP_FORCE_ALLOC:
 	case GOMP_MAP_FORCE_TO:
 	case GOMP_MAP_FORCE_DEVICEPTR:
-	  break;
-
 	case GOMP_MAP_DEVICE_RESIDENT:
-	  devres = t;
 	  break;
 
 	case GOMP_MAP_POINTER:
@@ -34770,7 +34762,9 @@ cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
 	  break;
 
 	case GOMP_MAP_LINK:
-	  if (!global_bindings_p () && !DECL_EXTERNAL (decl))
+	  if (!global_bindings_p ()
+	      && (TREE_STATIC (decl)
+	       || !DECL_EXTERNAL (decl)))
 	    {
 	      error_at (loc,
 			"%qD must be a global variable in"
@@ -34807,173 +34801,55 @@ cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
 	  break;
 	}
 
-      tree decl_for_attr = decl;
-      tree prev_attr = lookup_attribute ("oacc declare",
-					     DECL_ATTRIBUTES (decl));
-      if (prev_attr)
+      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl))
+	  || lookup_attribute ("omp declare target link",
+			       DECL_ATTRIBUTES (decl)))
 	{
-	  tree p = TREE_VALUE (prev_attr);
-	  tree cl = TREE_VALUE (p);
+	  error_at (loc, "variable %qD used more than once with "
+		    "%<#pragma acc declare%>", decl);
+	  error = true;
+	  continue;
+	}
 
-	  if (!devres && OMP_CLAUSE_MAP_KIND (cl) != GOMP_MAP_DEVICE_RESIDENT)
+      if (!error)
+	{
+	  tree id;
+
+	  if (OMP_CLAUSE_MAP_KIND (t) == GOMP_MAP_LINK)
+	    id = get_identifier ("omp declare target link");
+	  else
+	    id = get_identifier ("omp declare target");
+
+	  DECL_ATTRIBUTES (decl)
+			   = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl));
+	  if (global_bindings_p ())
 	    {
-	      error_at (loc, "variable %qD used more than once with "
-			"%<#pragma acc declare%>", decl);
-	      inform (OMP_CLAUSE_LOCATION (TREE_VALUE (p)),
-		      "previous directive was here");
-	      error = true;
-	      continue;
+	      symtab_node *node = symtab_node::get (decl);
+	      if (node != NULL)
+		{
+		  node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+		  g->have_offload = true;
+		  if (is_a <varpool_node *> (node))
+		    {
+		      vec_safe_push (offload_vars, decl);
+		      node->force_output = 1;
+		    }
+#endif
+		}
 	    }
 	}
-
-      if (!error && global_bindings_p ())
-	{
-	  tree attr = tree_cons (NULL_TREE, t, NULL_TREE);
-	  tree attrs = tree_cons (get_identifier ("oacc declare"),
-				  attr, NULL_TREE);
-	  decl_attributes (&decl_for_attr, attrs, 0);
-	}
     }
 
-  if (error)
+  if (error || global_bindings_p ())
     return NULL_TREE;
 
-  ret_clauses = NULL_TREE;
+  stmt = make_node (OACC_DECLARE);
+  TREE_TYPE (stmt) = void_type_node;
+  OACC_DECLARE_CLAUSES (stmt) = clauses;
+  SET_EXPR_LOCATION (stmt, pragma_tok->location);
 
-  for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
-    {
-      bool ret = false;
-      HOST_WIDE_INT kind, new_op;
-
-      kind = OMP_CLAUSE_MAP_KIND (c);
-
-      switch (kind)
-	{
-	  case GOMP_MAP_ALLOC:
-	  case GOMP_MAP_FORCE_ALLOC:
-	  case GOMP_MAP_FORCE_TO:
-	    new_op = GOMP_MAP_FORCE_DEALLOC;
-	    ret = true;
-	    break;
-
-	  case GOMP_MAP_FORCE_FROM:
-	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
-	    new_op = GOMP_MAP_FORCE_FROM;
-	    ret = true;
-	    break;
-
-	  case GOMP_MAP_FORCE_TOFROM:
-	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_TO);
-	    new_op = GOMP_MAP_FORCE_FROM;
-	    ret = true;
-	    break;
-
-	  case GOMP_MAP_FROM:
-	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FORCE_ALLOC);
-	    new_op = GOMP_MAP_FROM;
-	    ret = true;
-	    break;
-
-	  case GOMP_MAP_TOFROM:
-	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
-	    new_op = GOMP_MAP_FROM;
-	    ret = true;
-	    break;
-
-	  case GOMP_MAP_DEVICE_RESIDENT:
-	  case GOMP_MAP_FORCE_DEVICEPTR:
-	  case GOMP_MAP_FORCE_PRESENT:
-	  case GOMP_MAP_POINTER:
-	  case GOMP_MAP_TO:
-	    break;
-
-	  case GOMP_MAP_LINK:
-	    continue;
-
-	  default:
-	    gcc_unreachable ();
-	    break;
-	}
-
-      if (ret)
-	{
-	  t = copy_node (c);
-
-	  OMP_CLAUSE_SET_MAP_KIND (t, new_op);
-
-	  if (ret_clauses)
-	    OMP_CLAUSE_CHAIN (t) = ret_clauses;
-
-	  ret_clauses = t;
-	}
-    }
-
-  if (global_bindings_p ())
-    {
-      char buf[128];
-      cp_decl_specifier_seq decl_specifiers;
-      cp_declarator *declarator;
-      tree attrs, parms;
-      tree f, t, call_fn, stmt;
-      location_t loc = UNKNOWN_LOCATION;
-      void *p;
-
-      p = obstack_alloc (&declarator_obstack, 0);
-      clear_decl_specs (&decl_specifiers);
-      decl_specifiers.type = void_type_node;
-      sprintf (buf, "__openacc_cp_constructor__%d", oacc_dcl_idx++);
-
-      declarator = make_id_declarator (NULL_TREE, get_identifier (buf),
-				       sfk_none);
-      parms = void_list_node;
-      declarator = make_call_declarator (declarator, parms,
-					 TYPE_UNQUALIFIED,
-					 VIRT_SPEC_UNSPECIFIED,
-					 REF_QUAL_NONE,
-					 /*tx_qualifier=*/NULL_TREE,
-					 /*exception_specification=*/NULL_TREE,
-					 /*late_return_type=*/NULL_TREE,
-					 /*requires_clause=*/NULL_TREE);
-      attrs = tree_cons (get_identifier ("constructor") , NULL_TREE, NULL_TREE);
-      start_function (&decl_specifiers, declarator, attrs);
-      f = begin_compound_stmt (0);
-      call_fn = builtin_decl_explicit (BUILT_IN_GOACC_STATIC);
-      TREE_SIDE_EFFECTS (call_fn) = 1;
-
-      for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
-	{
-	  tree d, a1, a2, a3;
-	  vec<tree, va_gc> *args;
-	  vec_alloc (args, 3);
-
-	  d = OMP_CLAUSE_DECL (t);
-
-	  a1 = build_unary_op (loc, ADDR_EXPR, d, 0);
-	  a2 = DECL_SIZE_UNIT (d);
-	  a3 = build_int_cst (unsigned_type_node, OMP_CLAUSE_MAP_KIND (t));
-
-	  args->quick_push (a1);
-	  args->quick_push (a2);
-	  args->quick_push (a3);
-
-	  stmt = build_function_call_vec (loc, vNULL, call_fn, args, NULL);
-	  finish_expr_stmt (stmt);
-	}
-
-      finish_compound_stmt (f);
-      expand_or_defer_fn (finish_function (0));
-      obstack_free (&declarator_obstack, p);
-    }
-  else
-    {
-      stmt = make_node (OACC_DECLARE);
-      TREE_TYPE (stmt) = void_type_node;
-      OACC_DECLARE_CLAUSES (stmt) = clauses;
-      OACC_DECLARE_RETURN_CLAUSES (stmt) = ret_clauses;
-      SET_EXPR_LOCATION (stmt, pragma_tok->location);
-
-      add_stmt (stmt);
-    }
+  add_stmt (stmt);
 
   return NULL_TREE;
 }
diff --git gcc/cp/pt.c gcc/cp/pt.c
index cfaf9b2..f70a444 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -15416,9 +15416,6 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       tmp = tsubst_omp_clauses (OACC_DECLARE_CLAUSES (t), false, false,
 				args, complain, in_decl);
       OACC_DECLARE_CLAUSES (t) = tmp;
-      tmp = tsubst_omp_clauses (OACC_DECLARE_RETURN_CLAUSES (t), false, false,
-				args, complain, in_decl);
-      OACC_DECLARE_RETURN_CLAUSES (t) = tmp;
       add_stmt (t);
       break;
 
diff --git gcc/fortran/f95-lang.c gcc/fortran/f95-lang.c
index 7038fa3..0958102 100644
--- gcc/fortran/f95-lang.c
+++ gcc/fortran/f95-lang.c
@@ -94,8 +94,6 @@ static const struct attribute_spec gfc_attribute_table[] =
        affects_type_identity } */
   { "omp declare target", 0, 0, true,  false, false,
     gfc_handle_omp_declare_target_attribute, false },
-  { "oacc declare", 0, 0, true,  false, false,
-    gfc_handle_omp_declare_target_attribute, false },
   { "oacc function", 0, -1, true,  false, false,
     gfc_handle_omp_declare_target_attribute, false },
   { NULL,		  0, 0, false, false, false, NULL, false }
diff --git gcc/fortran/gfortran.h gcc/fortran/gfortran.h
index 5e7db13..c8401cf 100644
--- gcc/fortran/gfortran.h
+++ gcc/fortran/gfortran.h
@@ -1250,7 +1250,7 @@ gfc_omp_clauses;
 
 #define gfc_get_omp_clauses() XCNEW (gfc_omp_clauses)
 
-/* Node in the linked list used for storing !$oacc declare constructs.  */
+/* Node in the linked list used for storing OpenACC declare constructs.  */
 
 typedef struct gfc_oacc_declare
 {
diff --git gcc/fortran/trans-decl.c gcc/fortran/trans-decl.c
index 976a518..058fd17 100644
--- gcc/fortran/trans-decl.c
+++ gcc/fortran/trans-decl.c
@@ -1301,19 +1301,16 @@ add_attributes_to_decl (symbol_attribute sym_attr, tree list)
 	list = chainon (list, attr);
       }
 
-  if (sym_attr.omp_declare_target)
-    list = tree_cons (get_identifier ("omp declare target"),
-		      NULL_TREE, list);
-
-  if (sym_attr.oacc_declare_create
+  if (sym_attr.omp_declare_target
+      || sym_attr.oacc_declare_create
       || sym_attr.oacc_declare_copyin
       || sym_attr.oacc_declare_deviceptr
-      || sym_attr.oacc_declare_device_resident
-      || sym_attr.oacc_declare_link)
-    {
-      list = tree_cons (get_identifier ("oacc declare"),
-			NULL_TREE, list);
-    }
+      || sym_attr.oacc_declare_device_resident)
+    list = tree_cons (get_identifier ("omp declare target"),
+		      NULL_TREE, list);
+  if (sym_attr.oacc_declare_link)
+    list = tree_cons (get_identifier ("omp declare target link"),
+		      NULL_TREE, list);
 
   if (sym_attr.oacc_function)
     {
diff --git gcc/fortran/trans-openmp.c gcc/fortran/trans-openmp.c
index c95c2e6..87ecc5a 100644
--- gcc/fortran/trans-openmp.c
+++ gcc/fortran/trans-openmp.c
@@ -4672,7 +4672,7 @@ tree
 gfc_trans_oacc_declare (gfc_code *code)
 {
   stmtblock_t block;
-  tree stmt, c1, c2;
+  tree stmt, c1;
   enum tree_code construct_code;
 
   gfc_start_block (&block);
@@ -4683,10 +4683,12 @@ gfc_trans_oacc_declare (gfc_code *code)
   c1 = gfc_trans_omp_clauses (&block, code->ext.oacc_declare->clauses,
 			      code->loc);
 
+#if 0 /* TODO */
   c2 = gfc_trans_omp_clauses (&block, code->ext.oacc_declare->return_clauses,
 			      code->loc);
+#endif
 
-  stmt = build2_loc (input_location, construct_code, void_type_node, c1, c2);
+  stmt = build1_loc (input_location, construct_code, void_type_node, c1);
   gfc_add_expr_to_block (&block, stmt);
 
   return gfc_finish_block (&block);
diff --git gcc/fortran/types.def gcc/fortran/types.def
index 7def2a8..a37e856 100644
--- gcc/fortran/types.def
+++ gcc/fortran/types.def
@@ -145,7 +145,6 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I2_INT, BT_VOID, BT_VOLATILE_PTR, BT_I2, BT
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I4_INT, BT_VOID, BT_VOLATILE_PTR, BT_I4, BT_INT)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I8_INT, BT_VOID, BT_VOLATILE_PTR, BT_I8, BT_INT)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I16_INT, BT_VOID, BT_VOLATILE_PTR, BT_I16, BT_INT)
-DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_UINT, BT_VOID, BT_PTR, BT_INT, BT_UINT)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_VOID_OMPFN_PTR_UINT_UINT,
                      BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, BT_UINT)
diff --git gcc/gimplify.c gcc/gimplify.c
index b91d69e..aea8bff 100644
--- gcc/gimplify.c
+++ gcc/gimplify.c
@@ -177,7 +177,6 @@ struct gimplify_omp_ctx
   bool target_map_scalars_firstprivate;
   bool target_map_pointers_as_0len_arrays;
   bool target_firstprivatize_array_bases;
-  gomp_target *stmt;
 };
 
 struct privatize_reduction
@@ -190,6 +189,7 @@ static struct gimplify_omp_ctx *gimplify_omp_ctxp;
 
 /* Forward declaration.  */
 static enum gimplify_status gimplify_compound_expr (tree *, gimple_seq *, bool);
+static hash_map<tree, tree> *oacc_declare_returns;
 
 /* Shorter alias name for the above function for use in gimplify.c
    only.  */
@@ -1092,6 +1092,7 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
   gimple_seq body, cleanup;
   gcall *stack_save;
   location_t start_locus = 0, end_locus = 0;
+  tree ret_clauses = NULL;
 
   tree temp = voidify_wrapper_expr (bind_expr, NULL);
 
@@ -1193,9 +1194,39 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 	  clobber_stmt = gimple_build_assign (t, clobber);
 	  gimple_set_location (clobber_stmt, end_locus);
 	  gimplify_seq_add_stmt (&cleanup, clobber_stmt);
+
+	  if (flag_openacc && oacc_declare_returns != NULL)
+	    {
+	      tree *c = oacc_declare_returns->get (t);
+	      if (c != NULL)
+		{
+		  if (ret_clauses)
+		    OMP_CLAUSE_CHAIN (*c) = ret_clauses;
+
+		  ret_clauses = *c;
+
+		  oacc_declare_returns->remove (t);
+
+		  if (oacc_declare_returns->elements () == 0)
+		    {
+		      delete oacc_declare_returns;
+		      oacc_declare_returns = NULL;
+		    }
+		}
+	    }
 	}
     }
 
+  if (ret_clauses)
+    {
+      gomp_target *stmt;
+      gimple_stmt_iterator si = gsi_start (cleanup);
+
+      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
+				      ret_clauses);
+      gsi_insert_seq_before_without_update (&si, stmt, GSI_NEW_STMT);
+    }
+
   if (cleanup)
     {
       gtry *gs;
@@ -5823,6 +5854,26 @@ omp_notice_threadprivate_variable (struct gimplify_omp_ctx *ctx, tree decl,
   return false;
 }
 
+/* Return true if global var DECL is device resident.  */
+
+static bool
+device_resident_p (tree decl)
+{
+  tree attr = lookup_attribute ("oacc declare target", DECL_ATTRIBUTES (decl));
+
+  if (!attr)
+    return false;
+
+  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
+    {
+      tree c = TREE_VALUE (t);
+      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
+	return true;
+    }
+
+  return false;
+}
+
 /* Determine outer default flags for DECL mentioned in an OMP region
    but not declared in an enclosing clause.
 
@@ -5914,25 +5965,6 @@ omp_default_clause (struct gimplify_omp_ctx *ctx, tree decl,
   return flags;
 }
 
-/* Return true if global var DECL is device resident.  */
-
-static bool
-device_resident_p (tree decl)
-{
-  tree attr = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
-
-  if (!attr)
-    return false;
-  
-  for (tree t = TREE_VALUE (attr); t; t = TREE_PURPOSE (t))
-    {
-      tree c = TREE_VALUE (t);
-      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DEVICE_RESIDENT)
-	return true;
-    }
-
-  return false;
-}
 
 /* Determine outer default flags for DECL mentioned in an OACC region
    but not declared in an enclosing clause.  */
@@ -5941,10 +5973,15 @@ static unsigned
 oacc_default_clause (struct gimplify_omp_ctx *ctx, tree decl, unsigned flags)
 {
   const char *rkind;
-  bool on_device = is_global_var (decl) && device_resident_p (decl);
+  bool on_device = false;
 
-  if (on_device)
-    flags |= GOVD_MAP_TO_ONLY;
+  if ((ctx->region_type & (ORT_ACC_PARALLEL | ORT_ACC_KERNELS)) != 0
+      && is_global_var (decl)
+      && device_resident_p (decl))
+    {
+      on_device = true;
+      flags |= GOVD_MAP_TO_ONLY;
+    }
 
   switch (ctx->region_type)
     {
@@ -7925,6 +7962,76 @@ gimplify_oacc_cache (tree *expr_p, gimple_seq *pre_p)
   *expr_p = NULL_TREE;
 }
 
+/* Helper function of gimplify_oacc_declare.  The helper's purpose is to,
+   if required, translate 'kind' in CLAUSE into an 'entry' kind and 'exit'
+   kind.  The entry kind will replace the one in CLAUSE, while the exit
+   kind will be used in a new omp_clause and returned to the caller.  */
+
+static tree
+gimplify_oacc_declare_1 (tree clause)
+{
+  HOST_WIDE_INT kind, new_op;
+  bool ret = false;
+  tree c = NULL;
+
+  kind = OMP_CLAUSE_MAP_KIND (clause);
+
+  switch (kind)
+    {
+      case GOMP_MAP_ALLOC:
+      case GOMP_MAP_FORCE_ALLOC:
+      case GOMP_MAP_FORCE_TO:
+	new_op = GOMP_MAP_FORCE_DEALLOC;
+	ret = true;
+	break;
+
+      case GOMP_MAP_FORCE_FROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_FORCE_ALLOC);
+	new_op = GOMP_MAP_FORCE_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_FORCE_TOFROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_FORCE_TO);
+	new_op = GOMP_MAP_FORCE_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_FROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_FORCE_ALLOC);
+	new_op = GOMP_MAP_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_TOFROM:
+	OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_TO);
+	new_op = GOMP_MAP_FROM;
+	ret = true;
+	break;
+
+      case GOMP_MAP_DEVICE_RESIDENT:
+      case GOMP_MAP_FORCE_DEVICEPTR:
+      case GOMP_MAP_FORCE_PRESENT:
+      case GOMP_MAP_LINK:
+      case GOMP_MAP_POINTER:
+      case GOMP_MAP_TO:
+	break;
+
+      default:
+	gcc_unreachable ();
+	break;
+    }
+
+  if (ret)
+    {
+      c = build_omp_clause (OMP_CLAUSE_LOCATION (clause), OMP_CLAUSE_MAP);
+      OMP_CLAUSE_SET_MAP_KIND (c, new_op);
+      OMP_CLAUSE_DECL (c) = OMP_CLAUSE_DECL (clause);
+    }
+
+  return c;
+}
+
 /* Gimplify OACC_DECLARE.  */
 
 static void
@@ -7936,22 +8043,30 @@ gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
 
   clauses = OACC_DECLARE_CLAUSES (expr);
 
-  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_ACC_DATA, OACC_DECLARE);
-
-  gimplify_omp_ctxp->stmt = NULL;
+  gimplify_scan_omp_clauses (&clauses, pre_p, ORT_TARGET_DATA, OACC_DECLARE);
 
   for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t))
     {
-      tree attrs, decl = OMP_CLAUSE_DECL (t);
+      tree decl = OMP_CLAUSE_DECL (t);
 
       if (TREE_CODE (decl) == MEM_REF)
 	continue;
 
+      if (TREE_CODE (decl) == VAR_DECL
+	  && !is_global_var (decl)
+	  && DECL_CONTEXT (decl) == current_function_decl)
+	{
+	  tree c = gimplify_oacc_declare_1 (t);
+	  if (c)
+	    {
+	      if (oacc_declare_returns == NULL)
+		oacc_declare_returns = new hash_map<tree, tree>;
+
+	      oacc_declare_returns->put (decl, c);
+	    }
+	}
+
       omp_add_variable (gimplify_omp_ctxp, decl, GOVD_SEEN);
-
-      attrs = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
-      if (attrs)
-	DECL_ATTRIBUTES (decl) = remove_attribute ("oacc declare", attrs);
     }
 
   stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
@@ -7959,23 +8074,6 @@ gimplify_oacc_declare (tree *expr_p, gimple_seq *pre_p)
 
   gimplify_seq_add_stmt (pre_p, stmt);
 
-  clauses = OACC_DECLARE_RETURN_CLAUSES (expr);
-
-  if (clauses)
-    {
-      struct gimplify_omp_ctx *c;
-
-      gimplify_scan_omp_clauses (&clauses, pre_p, ORT_ACC_DATA, OACC_DECLARE);
-
-      c = gimplify_omp_ctxp;
-      gimplify_omp_ctxp = c->outer_context;
-      delete_omp_context (c);
-
-      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
-				      clauses);
-      gimplify_omp_ctxp->stmt = stmt;
-    }
-
   *expr_p = NULL_TREE;
 }
 
@@ -11215,17 +11313,6 @@ gimplify_body (tree fndecl, bool do_parms)
       gimplify_seq_add_stmt (&seq, outer_stmt);
     }
 
-  if (flag_openacc)
-    while (gimplify_omp_ctxp)
-      {
-	if (gimplify_omp_ctxp->stmt)
-	  gimplify_seq_add_stmt (&seq, gimplify_omp_ctxp->stmt);
-
-	struct gimplify_omp_ctx *c = gimplify_omp_ctxp;
-	gimplify_omp_ctxp = c->outer_context;
-	delete_omp_context (c);
-    }
-
   /* The body must contain exactly one statement, a GIMPLE_BIND.  If this is
      not the case, wrap everything in a GIMPLE_BIND to make it so.  */
   if (gimple_code (outer_stmt) == GIMPLE_BIND
diff --git gcc/omp-builtins.def gcc/omp-builtins.def
index 30cf24d..63e5e6e 100644
--- gcc/omp-builtins.def
+++ gcc/omp-builtins.def
@@ -355,7 +355,5 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TARGET_ENTER_EXIT_DATA,
 		  BT_FN_VOID_INT_SIZE_PTR_PTR_PTR_UINT_PTR, ATTR_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS, "GOMP_teams",
 		  BT_FN_VOID_UINT_UINT, ATTR_NOTHROW_LIST)
-DEF_GOACC_BUILTIN (BUILT_IN_GOACC_STATIC, "GOACC_register_static",
-		   BT_FN_VOID_PTR_INT_UINT, ATTR_NOTHROW_LIST)
 DEF_GOACC_BUILTIN (BUILT_IN_GOACC_DECLARE, "GOACC_declare",
 		   BT_FN_VOID_INT_SIZE_PTR_PTR_PTR, ATTR_NOTHROW_LIST)
[diff --git gcc/testsuite/ChangeLog gcc/testsuite/ChangeLog]
diff --git gcc/testsuite/c-c++-common/goacc/declare-2.c gcc/testsuite/c-c++-common/goacc/declare-2.c
index 7979f0c..d24cb22 100644
--- gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -7,13 +7,13 @@
 /* { dg-error "no valid clauses" "second error" { target *-*-* } 6 } */
 
 int v0[10];
-#pragma acc declare create(v0[1:3]) /* { dg-error "subarray" } */
+#pragma acc declare create(v0[1:3]) /* { dg-error "array section" } */
 
 int v1;
 #pragma acc declare create(v1, v1) /* { dg-error "more than once" } */
 
 int v2;
-#pragma acc declare create(v2) /* { dg-message "previous directive" } */
+#pragma acc declare create(v2)
 #pragma acc declare copyin(v2) /* { dg-error "more than once" } */
 
 int v3;
@@ -37,6 +37,17 @@ int v8;
 int v9;
 #pragma acc declare present_or_create(v9) /* { dg-error "at file scope" } */
 
+int va10;
+#pragma acc declare create (va10)
+#pragma acc declare link (va10) /* { dg-error "more than once" } */
+
+int va11;
+#pragma acc declare link (va11)
+#pragma acc declare link (va11) /* { dg-error "more than once" } */
+
+int va12;
+#pragma acc declare create (va12) link (va12) /* { dg-error "more than once" } */
+
 void
 f (void)
 {
diff --git gcc/tree-pretty-print.c gcc/tree-pretty-print.c
index 6e82cf4..1f6502a 100644
--- gcc/tree-pretty-print.c
+++ gcc/tree-pretty-print.c
@@ -630,12 +630,6 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, int flags)
 	case GOMP_MAP_FORCE_DEVICEPTR:
 	  pp_string (pp, "force_deviceptr");
 	  break;
-	case GOMP_MAP_DEVICE_RESIDENT:
-	  pp_string (pp, "device_resident");
-	  break;
-	case GOMP_MAP_LINK:
-	  pp_string (pp, "link");
-	  break;
 	case GOMP_MAP_ALWAYS_TO:
 	  pp_string (pp, "always,to");
 	  break;
@@ -660,6 +654,12 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, int flags)
 	case GOMP_MAP_ALWAYS_POINTER:
 	  pp_string (pp, "always_pointer");
 	  break;
+	case GOMP_MAP_DEVICE_RESIDENT:
+	  pp_string (pp, "device_resident");
+	  break;
+	case GOMP_MAP_LINK:
+	  pp_string (pp, "link");
+	  break;
 	default:
 	  gcc_unreachable ();
 	}
diff --git gcc/tree.def gcc/tree.def
index 1c0d42c..41064a8 100644
--- gcc/tree.def
+++ gcc/tree.def
@@ -1064,11 +1064,6 @@ DEFTREECODE (OACC_KERNELS, "oacc_kernels", tcc_statement, 2)
 
 DEFTREECODE (OACC_DATA, "oacc_data", tcc_statement, 2)
 
-/* OpenACC - #pragma acc declare [clause1 ... clauseN]
-   Operand 0: OACC_DECLARE_CLAUSES: List of clauses.
-   Operand 1: OACC_DECLARE_RETURN_CLAUSES: List of clauses for returns.  */
-DEFTREECODE (OACC_DECLARE, "oacc_declare", tcc_statement, 2)
-
 /* OpenACC - #pragma acc host_data [clause1 ... clauseN]
    Operand 0: OACC_HOST_DATA_BODY: Host_data construct body.
    Operand 1: OACC_HOST_DATA_CLAUSES: List of clauses.  */
@@ -1190,6 +1185,10 @@ DEFTREECODE (OMP_TASKGROUP, "omp_taskgroup", tcc_statement, 1)
 	OMP_CLAUSE__CACHE_ clauses).  */
 DEFTREECODE (OACC_CACHE, "oacc_cache", tcc_statement, 1)
 
+/* OpenACC - #pragma acc declare [clause1 ... clauseN]
+   Operand 0: OACC_DECLARE_CLAUSES: List of clauses.  */
+DEFTREECODE (OACC_DECLARE, "oacc_declare", tcc_statement, 1)
+
 /* OpenACC - #pragma acc enter data [clause1 ... clauseN]
    Operand 0: OACC_ENTER_DATA_CLAUSES: List of clauses.  */
 DEFTREECODE (OACC_ENTER_DATA, "oacc_enter_data", tcc_statement, 1)
diff --git gcc/tree.h gcc/tree.h
index 2b03de8..f04a2b1 100644
--- gcc/tree.h
+++ gcc/tree.h
@@ -1253,8 +1253,6 @@ extern void protected_set_expr_location (tree, location_t);
 
 #define OACC_DECLARE_CLAUSES(NODE) \
   TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 0)
-#define OACC_DECLARE_RETURN_CLAUSES(NODE) \
-  TREE_OPERAND (OACC_DECLARE_CHECK (NODE), 1)
 
 #define OACC_ENTER_DATA_CLAUSES(NODE) \
   TREE_OPERAND (OACC_ENTER_DATA_CHECK (NODE), 0)
diff --git gcc/varpool.c gcc/varpool.c
index 3f3d6b2..478f365 100644
--- gcc/varpool.c
+++ gcc/varpool.c
@@ -137,42 +137,6 @@ varpool_node::create_empty (void)
   return node;
 }   
 
-static void
-make_offloadable_1 (varpool_node *node, tree decl ATTRIBUTE_UNUSED)
-{
-  node->offloadable = 1;
-#ifdef ENABLE_OFFLOADING
-  g->have_offload = true;
-  if (!in_lto_p)
-    vec_safe_push (offload_vars, decl);
-  node->force_output = 1;
-#endif
-}
-
-void
-make_offloadable (varpool_node *node, tree decl)
-{
-  tree attrs;
-
-  if (node->offloadable)
-    return;
-
-  if (flag_openmp)
-    {
-      make_offloadable_1 (node, decl);
-      return;
-    }
-
-  attrs = lookup_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
-  if (attrs)
-    {
-      make_offloadable_1 (node, decl);
-
-      DECL_ATTRIBUTES (decl)
-	  = remove_attribute ("oacc declare", DECL_ATTRIBUTES (decl));
-    }
-}
-
 /* Return varpool node assigned to DECL.  Create new one when needed.  */
 varpool_node *
 varpool_node::get_create (tree decl)
@@ -180,18 +144,22 @@ varpool_node::get_create (tree decl)
   varpool_node *node = varpool_node::get (decl);
   gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
   if (node)
-    {
-      if (flag_openacc && !DECL_EXTERNAL (decl))
-	make_offloadable (node, decl);
-      return node;
-    }
+    return node;
 
   node = varpool_node::create_empty ();
   node->decl = decl;
 
   if ((flag_openacc || flag_openmp) && !DECL_EXTERNAL (decl)
       && lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
-    make_offloadable (node, decl);
+    {
+      node->offloadable = 1;
+#ifdef ENABLE_OFFLOADING
+      g->have_offload = true;
+      if (!in_lto_p)
+	vec_safe_push (offload_vars, decl);
+      node->force_output = 1;
+#endif
+    }
 
   node->register_symbol ();
   return node;
[diff --git include/ChangeLog include/ChangeLog]
diff --git include/gomp-constants.h include/gomp-constants.h
index d2d632e..dffd631 100644
--- include/gomp-constants.h
+++ include/gomp-constants.h
@@ -71,11 +71,12 @@ enum gomp_map_kind
     /* Is a device pointer.  OMP_CLAUSE_SIZE for these is unused; is implicitly
        POINTER_SIZE_UNITS.  */
     GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
+    /* Do not map, copy bits for firstprivate instead.  */
     /* OpenACC device_resident.  */
     GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
     /* OpenACC link.  */
     GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
-    /* Do not map, copy bits for firstprivate instead.  */
+    /* Allocate.  */
     GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
     /* Similarly, but store the value in the pointer rather than
        pointed by the pointer.  */
[diff --git libgomp/ChangeLog libgomp/ChangeLog]
diff --git libgomp/libgomp.map libgomp/libgomp.map
index 7a2faf2..98a86cd 100644
--- libgomp/libgomp.map
+++ libgomp/libgomp.map
@@ -382,7 +382,6 @@ GOACC_2.0 {
   global:
 	GOACC_data_end;
 	GOACC_data_start;
-	GOACC_declare;
 	GOACC_enter_exit_data;
 	GOACC_parallel;
 	GOACC_update;
@@ -393,13 +392,13 @@ GOACC_2.0 {
 
 GOACC_2.0.1 {
   global:
+	GOACC_declare;
 	GOACC_parallel_keyed;
 } GOACC_2.0;
 
 GOACC_2.0.GOMP_4_BRANCH {
   global:
 	GOACC_deviceptr;
-	GOACC_register_static;
 	GOMP_set_offload_targets;
 } GOACC_2.0.1;
 
diff --git libgomp/oacc-init.c libgomp/oacc-init.c
index 5e8525f..d6fad51 100644
--- libgomp/oacc-init.c
+++ libgomp/oacc-init.c
@@ -256,8 +256,6 @@ acc_shutdown_1 (acc_device_t d)
   /* Get the base device for this device type.  */
   base_dev = resolve_device (d, true);
 
-  goacc_deallocate_static (d);
-
   ndevs = base_dev->get_num_devices_func ();
 
   /* Unload all the devices of this type that have been opened.  */
@@ -443,9 +441,7 @@ goacc_attach_host_thread_to_device (int ord)
 void
 acc_init (acc_device_t d)
 {
-  bool init = !cached_base_dev;
-
-  if (init)
+  if (!cached_base_dev)
     gomp_init_targets_once ();
 
   gomp_mutex_lock (&acc_device_lock);
@@ -453,9 +449,6 @@ acc_init (acc_device_t d)
   cached_base_dev = acc_init_1 (d);
 
   gomp_mutex_unlock (&acc_device_lock);
-
-  if (init)
-    goacc_allocate_static (d);
   
   goacc_attach_host_thread_to_device (-1);
 }
diff --git libgomp/oacc-int.h libgomp/oacc-int.h
index 560f68b..f11e216 100644
--- libgomp/oacc-int.h
+++ libgomp/oacc-int.h
@@ -99,9 +99,6 @@ void goacc_restore_bind (void);
 void goacc_lazy_initialize (void);
 void goacc_host_init (void);
 
-void goacc_allocate_static (acc_device_t);
-void goacc_deallocate_static (acc_device_t);
-
 #ifdef HAVE_ATTRIBUTE_VISIBILITY
 # pragma GCC visibility pop
 #endif
diff --git libgomp/oacc-parallel.c libgomp/oacc-parallel.c
index 8dfa622..d66e343 100644
--- libgomp/oacc-parallel.c
+++ libgomp/oacc-parallel.c
@@ -57,68 +57,6 @@ find_pointer (int pos, size_t mapnum, unsigned short *kinds)
   return 0;
 }
 
-static struct oacc_static
-{
-  void *addr;
-  size_t size;
-  unsigned short mask;
-  bool free;
-  struct oacc_static *next;
-} *oacc_statics;
-
-static bool alloc_done = false;
-
-void
-goacc_allocate_static (acc_device_t d)
-{
-  struct oacc_static *s;
-
-  if (alloc_done)
-    assert (0);
-
-  for (s = oacc_statics; s; s = s->next)
-    {
-      void *d;
-
-      switch (s->mask)
-	{
-	case GOMP_MAP_FORCE_ALLOC:
-	  break;
-
-	case GOMP_MAP_FORCE_TO:
-	  d = acc_deviceptr (s->addr);
-	  acc_memcpy_to_device (d, s->addr, s->size);
-	  break;
-
-	case GOMP_MAP_FORCE_DEVICEPTR:
-	case GOMP_MAP_DEVICE_RESIDENT:
-	case GOMP_MAP_LINK:
-	  break;
-
-	default:
-	  assert (0);
-	  break;
-	}
-    }
-
-  alloc_done = true;
-}
-
-void
-goacc_deallocate_static (acc_device_t d)
-{
-  struct oacc_static *s;
-  unsigned short mask = GOMP_MAP_FORCE_DEALLOC;
-
-  if (!alloc_done)
-    return;
-
-  for (s = oacc_statics; s; s = s->next)
-    GOACC_enter_exit_data (d, 1, &s->addr, &s->size, &mask, 0, 0);
-
-  alloc_done = false;
-}
-
 static void goacc_wait (int async, int num_waits, va_list *ap);
 
 
@@ -630,21 +568,6 @@ GOACC_get_thread_num (int gang, int worker, int vector)
 }
 
 void
-GOACC_register_static (void *addr, int size, unsigned int mask)
-{
-  struct oacc_static *s;
-
-  s = (struct oacc_static *) malloc (sizeof (struct oacc_static));
-  s->addr = addr;
-  s->size = (size_t) size;
-  s->mask = mask;
-  s->free = false;
-  s->next = oacc_statics;
-
-  oacc_statics = s;
-}
-
-void
 GOACC_declare (int device, size_t mapnum,
 	       void **hostaddrs, size_t *sizes, unsigned short *kinds)
 {
@@ -673,10 +596,8 @@ GOACC_declare (int device, size_t mapnum,
 
 	  case GOMP_MAP_ALLOC:
 	    if (!acc_is_present (hostaddrs[i], sizes[i]))
-	      {
-		GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
-				       &kinds[i], 0, 0);
-	      }
+	      GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
+				     &kinds[i], 0, 0);
 	    break;
 
 	  case GOMP_MAP_TO:
@@ -688,12 +609,13 @@ GOACC_declare (int device, size_t mapnum,
 	  case GOMP_MAP_FROM:
 	    kinds[i] = GOMP_MAP_FORCE_FROM;
 	    GOACC_enter_exit_data (device, 1, &hostaddrs[i], &sizes[i],
-				       &kinds[i], 0, 0);
+				   &kinds[i], 0, 0);
 	    break;
 
 	  case GOMP_MAP_FORCE_PRESENT:
 	    if (!acc_is_present (hostaddrs[i], sizes[i]))
-	      gomp_fatal ("[%p,%zd] is not mapped", hostaddrs[i], sizes[i]);
+	      gomp_fatal ("[%p,%ld] is not mapped", hostaddrs[i],
+			  (unsigned long) sizes[i]);
 	    break;
 
 	  default:
diff --git libgomp/testsuite/libgomp.oacc-c++/declare-1.C libgomp/testsuite/libgomp.oacc-c++/declare-1.C
index e82a8e5..0286955 100644
--- libgomp/testsuite/libgomp.oacc-c++/declare-1.C
+++ libgomp/testsuite/libgomp.oacc-c++/declare-1.C
@@ -1,17 +1,20 @@
 /* { dg-do run { target openacc_nvidia_accel_selected } } */
 
+#include <stdlib.h>
+
 template<class T>
 T foo()
 {
-  T a = 0;
+  T a, b;
   #pragma acc declare create (a)
 
-  #pragma acc parallel
+  #pragma acc parallel copyout (b)
   {
     a = 5;
+    b = a;
   }
 
-  return a;
+  return b;
 }
 
 int
@@ -21,5 +24,8 @@ main (void)
 
   rc = foo<int>();
 
-  return rc;
+  if (rc != 5)
+    abort ();
+
+  return 0;
 }
diff --git libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
index 8fbec4d..c63a68d 100644
--- libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
+++ libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
@@ -71,8 +71,8 @@ main (int argc, char **argv)
   {
     for (i = 0; i < N; i++)
       {
-        b[i] = a[i];
-        a[i] = b[i];
+	b[i] = a[i];
+	a[i] = b[i];
       }
   }
 
@@ -86,7 +86,7 @@ main (int argc, char **argv)
   {
     for (i = 0; i < N; i++)
       {
-        e[i] = a[i] + d[i];
+	e[i] = a[i] + d[i];
 	a[i] = e[i];
       }
   }
diff --git libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c
index 1e2f6ce..38c5de0 100644
--- libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c
+++ libgomp/testsuite/libgomp.oacc-c-c++-common/declare-5.c
@@ -1,11 +1,13 @@
 /* { dg-do run { target openacc_nvidia_accel_selected } } */
 
+#include <stdio.h>
+
 int
 main (int argc, char **argv)
 {
   int a[8] __attribute__((unused));
 
-  __builtin_printf ("CheCKpOInT\n");
+  fprintf (stderr, "CheCKpOInT\n");
 #pragma acc declare present (a)
 }
 
diff --git libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90 libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
index 18dd1bb..dffbedd 100644
--- libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
+++ libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
@@ -1,4 +1,5 @@
 ! { dg-do run  { target openacc_nvidia_accel_selected } }
+! { dg-xfail-run-if "TODO" { *-*-* } }
 
 module vars
   integer z
diff --git libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90 libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
index 79fc011..1d19bfd 100644
--- libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
+++ libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
@@ -1,4 +1,5 @@
 ! { dg-do run  { target openacc_nvidia_accel_selected } }
+! { dg-xfail-if "TODO" { *-*-* } }
 
 module globalvars
   real b


Grüße
 Thomas

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [gomp4] Re: OpenACC declare directive updates
  2015-11-19 16:22         ` James Norris
  2015-11-20 10:53           ` Jakub Jelinek
@ 2015-11-27 11:41           ` Thomas Schwinge
  2019-06-18 23:02             ` [committed] [PR85221] Set 'omp declare target', 'omp declare target link' attributes for Fortran OpenACC 'declare'd variables (was: [gomp4] Re: OpenACC declare directive updates) Thomas Schwinge
  1 sibling, 1 reply; 51+ messages in thread
From: Thomas Schwinge @ 2015-11-27 11:41 UTC (permalink / raw)
  To: James Norris; +Cc: Cesar Philippidis, GCC Patches, Jakub Jelinek, fortran

[-- Attachment #1: Type: text/plain, Size: 35824 bytes --]

Hi!

On Thu, 19 Nov 2015 10:22:16 -0600, James Norris <jnorris@codesourcery.com> wrote:
> --- a/gcc/fortran/dump-parse-tree.c
> +++ b/gcc/fortran/dump-parse-tree.c

Don't you need to handle OMP_LIST_LINK in
gcc/fortran/dump-parse-tree.c:show_omp_clauses; OMP_LIST_DEVICE_RESIDENT
is being handled there (but maps to the wrong string?).  (See
gomp-4_0-branch.)  When touching that, please sort the "case OMP_LIST_*"s
corresponding to the order the OMP_LIST_* are defined in
gcc/fortran/gfortran.h.

> --- a/gcc/fortran/openmp.c
> +++ b/gcc/fortran/openmp.c

I see OMP_LIST_DEVICE_RESIDENT being handled in
gcc/fortran/openmp.c:resolve_omp_clauses and
gcc/fortran/openmp.c:gfc_resolve_oacc_declare, but not OMP_LIST_LINK --
is that correct?  Likewise, in
gcc/fortran/trans-openmp.c:gfc_trans_omp_clauses.

Also, oacc_declare_device_resident is handled in a lot more places
compared to oacc_declare_link -- is that correct?  In fact, there doesn't
seem to be any "consumer" for the latter, but I see the OpenACC link
clause being used in the test cases you added, so I wonder how that
works.


Merging your trunk r230722 and r230725 with the existing Fortran OpenACC
declare implementation present on gomp-4_0-branch, I effectively applied
the following to gomp-4_0-branch in 231002.  Please verify this.

Regarding my Fortran XFAIL comments in
<http://news.gmane.org/find-root.php?message_id=%3C878u5n7pqk.fsf%40hertz.schwinge.homeip.net%3E>,
with some of my earlier changes "#if 0"ed in
gcc/fortran/trans-decl.c:add_attributes_to_decl,
libgomp.oacc-fortran/declare-3.f90 again PASSes.  But I don't understand
(why something like) this code (isn't needed/done differently in C/C++).
The XFAIL in libgomp.oacc-fortran/declare-1.f90 means to be resolved
(gomp-4_0-branch only; not seen on trunk): "libgomp: cuStreamSynchronize
error: an illegal memory access was encountered".

commit 95e909a492b001df6d6faffdfa6047a5e9919561
Merge: 8373bdf e18d05e
Author: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Fri Nov 27 09:41:03 2015 +0000

    svn merge -r 230720:230725 svn+ssh://gcc.gnu.org/svn/gcc/trunk
    
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gomp-4_0-branch@231002 138bc75d-0d04-0410-961f-82ee72b054a4

 gcc/fortran/ChangeLog                              |  51 +++++
 gcc/fortran/gfortran.h                             |  17 +-
 gcc/fortran/openmp.c                               | 235 +++++++++++++--------
 gcc/fortran/parse.c                                |   2 +-
 gcc/fortran/parse.h                                |   2 +-
 gcc/fortran/resolve.c                              |   1 -
 gcc/fortran/st.c                                   |   2 +-
 gcc/fortran/symbol.c                               |  12 +-
 gcc/fortran/trans-decl.c                           | 198 +++++------------
 gcc/fortran/trans-openmp.c                         |  29 +--
 gcc/fortran/trans-stmt.c                           |   3 +-
 gcc/testsuite/ChangeLog                            |   6 +
 gcc/testsuite/gfortran.dg/goacc/declare-1.f95      |   4 +-
 gcc/testsuite/gfortran.dg/goacc/declare-2.f95      |  43 +++-
 libgomp/ChangeLog                                  |   9 +
 .../testsuite/libgomp.oacc-fortran/declare-1.f90   |  13 ++
 .../testsuite/libgomp.oacc-fortran/declare-2.f90   |   2 +
 .../testsuite/libgomp.oacc-fortran/declare-3.f90   |   4 +-
 .../testsuite/libgomp.oacc-fortran/declare-4.f90   |   2 +
 .../testsuite/libgomp.oacc-fortran/declare-5.f90   |   1 +
 20 files changed, 347 insertions(+), 289 deletions(-)

[diff --git gcc/fortran/ChangeLog gcc/fortran/ChangeLog]
diff --git gcc/fortran/gfortran.h gcc/fortran/gfortran.h
index c8401cf..dd186b5 100644
--- gcc/fortran/gfortran.h
+++ gcc/fortran/gfortran.h
@@ -1250,17 +1250,18 @@ gfc_omp_clauses;
 
 #define gfc_get_omp_clauses() XCNEW (gfc_omp_clauses)
 
-/* Node in the linked list used for storing OpenACC declare constructs.  */
+
+/* Node in the linked list used for storing !$oacc declare constructs.  */
 
 typedef struct gfc_oacc_declare
 {
   struct gfc_oacc_declare *next;
-  locus where;
   bool module_var;
   gfc_omp_clauses *clauses;
-  gfc_omp_clauses *return_clauses;
+  locus loc;
 }
 gfc_oacc_declare;
+
 #define gfc_get_oacc_declare() XCNEW (gfc_oacc_declare)
 
 
@@ -1685,8 +1686,8 @@ typedef struct gfc_namespace
      this namespace.  */
   struct gfc_data *data, *old_data;
 
-  /* !$ACC DECLARE clauses.  */
-  struct gfc_oacc_declare *oacc_declare;
+  /* !$ACC DECLARE.  */
+  gfc_oacc_declare *oacc_declare;
 
   /* !$ACC ROUTINE clauses.  */
   gfc_omp_clauses *oacc_routine_clauses;
@@ -2455,8 +2456,8 @@ typedef struct gfc_code
     struct gfc_code *which_construct;
     int stop_code;
     gfc_entry_list *entry;
-    gfc_omp_clauses *omp_clauses;
     gfc_oacc_declare *oacc_declare;
+    gfc_omp_clauses *omp_clauses;
     const char *omp_name;
     gfc_omp_namelist *omp_namelist;
     bool omp_bool;
@@ -2958,7 +2959,7 @@ gfc_expr *gfc_get_parentheses (gfc_expr *);
 /* openmp.c */
 struct gfc_omp_saved_state { void *ptrs[2]; int ints[1]; };
 void gfc_free_omp_clauses (gfc_omp_clauses *);
-void gfc_free_oacc_declares (struct gfc_oacc_declare *);
+void gfc_free_oacc_declare_clauses (struct gfc_oacc_declare *);
 void gfc_free_omp_declare_simd (gfc_omp_declare_simd *);
 void gfc_free_omp_declare_simd_list (gfc_omp_declare_simd *);
 void gfc_free_omp_udr (gfc_omp_udr *);
@@ -3278,6 +3279,6 @@ bool gfc_is_reallocatable_lhs (gfc_expr *);
 
 /* trans-decl.c */
 
-void finish_oacc_declare (gfc_namespace *, enum sym_flavor);
+void finish_oacc_declare (gfc_namespace *, gfc_symbol *, bool);
 
 #endif /* GCC_GFORTRAN_H  */
diff --git gcc/fortran/openmp.c gcc/fortran/openmp.c
index e8e8071..c6db847 100644
--- gcc/fortran/openmp.c
+++ gcc/fortran/openmp.c
@@ -94,7 +94,7 @@ gfc_free_omp_clauses (gfc_omp_clauses *c)
 /* Free oacc_declare structures.  */
 
 void
-gfc_free_oacc_declares (struct gfc_oacc_declare *oc)
+gfc_free_oacc_declare_clauses (struct gfc_oacc_declare *oc)
 {
   struct gfc_oacc_declare *decl = oc;
 
@@ -413,6 +413,110 @@ match_oacc_clause_gang (gfc_omp_clauses *cp)
   return gfc_match (" %e )", &cp->gang_expr);
 }
 
+static match
+gfc_match_oacc_clause_link (const char *str, gfc_omp_namelist **list)
+{
+  gfc_omp_namelist *head = NULL;
+  gfc_omp_namelist *tail, *p;
+  locus old_loc;
+  char n[GFC_MAX_SYMBOL_LEN+1];
+  gfc_symbol *sym;
+  match m;
+  gfc_symtree *st;
+
+  old_loc = gfc_current_locus;
+
+  m = gfc_match (str);
+  if (m != MATCH_YES)
+    return m;
+
+  m = gfc_match (" (");
+
+  for (;;)
+    {
+      m = gfc_match_symbol (&sym, 0);
+      switch (m)
+	{
+	case MATCH_YES:
+	  if (sym->attr.in_common)
+	    {
+	      gfc_error_now ("Variable at %C is an element of a COMMON block");
+	      goto cleanup;
+	    }
+	  gfc_set_sym_referenced (sym);
+	  p = gfc_get_omp_namelist ();
+	  if (head == NULL)
+	    head = tail = p;
+	  else
+	    {
+	      tail->next = p;
+	      tail = tail->next;
+	    }
+	  tail->sym = sym;
+	  tail->expr = NULL;
+	  tail->where = gfc_current_locus;
+	  goto next_item;
+	case MATCH_NO:
+	  break;
+
+	case MATCH_ERROR:
+	  goto cleanup;
+	}
+
+      m = gfc_match (" / %n /", n);
+      if (m == MATCH_ERROR)
+	goto cleanup;
+      if (m == MATCH_NO || n[0] == '\0')
+	goto syntax;
+
+      st = gfc_find_symtree (gfc_current_ns->common_root, n);
+      if (st == NULL)
+	{
+	  gfc_error ("COMMON block /%s/ not found at %C", n);
+	  goto cleanup;
+	}
+
+      for (sym = st->n.common->head; sym; sym = sym->common_next)
+	{
+	  gfc_set_sym_referenced (sym);
+	  p = gfc_get_omp_namelist ();
+	  if (head == NULL)
+	    head = tail = p;
+	  else
+	    {
+	      tail->next = p;
+	      tail = tail->next;
+	    }
+	  tail->sym = sym;
+	  tail->where = gfc_current_locus;
+	}
+
+    next_item:
+      if (gfc_match_char (')') == MATCH_YES)
+	break;
+      if (gfc_match_char (',') != MATCH_YES)
+	goto syntax;
+    }
+
+  if (gfc_match_omp_eos () != MATCH_YES)
+    {
+      gfc_error ("Unexpected junk after !$ACC DECLARE at %C");
+      goto cleanup;
+    }
+
+  while (*list)
+    list = &(*list)->next;
+  *list = head;
+  return MATCH_YES;
+
+syntax:
+  gfc_error ("Syntax error in !$ACC DECLARE list at %C");
+
+cleanup:
+  gfc_current_locus = old_loc;
+  return MATCH_ERROR;
+}
+
 #define OMP_CLAUSE_PRIVATE		((uint64_t) 1 << 0)
 #define OMP_CLAUSE_FIRSTPRIVATE		((uint64_t) 1 << 1)
 #define OMP_CLAUSE_LASTPRIVATE		((uint64_t) 1 << 2)
@@ -473,10 +577,10 @@ match_oacc_clause_gang (gfc_omp_clauses *cp)
 #define OMP_CLAUSE_DELETE		((uint64_t) 1 << 55)
 #define OMP_CLAUSE_AUTO			((uint64_t) 1 << 56)
 #define OMP_CLAUSE_TILE			((uint64_t) 1 << 57)
-#define OMP_CLAUSE_BIND			((uint64_t) 1 << 58)
-#define OMP_CLAUSE_NOHOST		((uint64_t) 1 << 59)
-#define OMP_CLAUSE_DEVICE_TYPE		((uint64_t) 1 << 60)
-#define OMP_CLAUSE_LINK			((uint64_t) 1 << 61)
+#define OMP_CLAUSE_LINK			((uint64_t) 1 << 58)
+#define OMP_CLAUSE_BIND			((uint64_t) 1 << 59)
+#define OMP_CLAUSE_NOHOST		((uint64_t) 1 << 60)
+#define OMP_CLAUSE_DEVICE_TYPE		((uint64_t) 1 << 61)
 
 /* Helper function for OpenACC and OpenMP clauses involving memory
    mapping.  */
@@ -739,9 +843,8 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, uint64_t mask,
 	     == MATCH_YES)
 	continue;
       if ((mask & OMP_CLAUSE_LINK)
-	  && gfc_match_omp_variable_list ("link (",
-					  &c->lists[OMP_LIST_LINK],
-					  true)
+	  && gfc_match_oacc_clause_link ("link (",
+					  &c->lists[OMP_LIST_LINK])
 	     == MATCH_YES)
 	continue;
       if ((mask & OMP_CLAUSE_OACC_DEVICE)
@@ -1444,8 +1547,9 @@ gfc_match_oacc_declare (void)
   gfc_omp_clauses *c;
   gfc_omp_namelist *n;
   gfc_namespace *ns = gfc_current_ns;
-  gfc_oacc_declare *new_oc, *oc;
+  gfc_oacc_declare *new_oc;
   bool module_var = false;
+  locus where = gfc_current_locus;
 
   if (gfc_match_omp_clauses (&c, OACC_DECLARE_CLAUSES, 0, false, false, true)
       != MATCH_YES)
@@ -1466,8 +1570,8 @@ gfc_match_oacc_declare (void)
 	  if (n->u.map_op != OMP_MAP_FORCE_ALLOC
 	      && n->u.map_op != OMP_MAP_FORCE_TO)
 	    {
-	      gfc_error ("Invalid clause in module with "
-			 "$!ACC DECLARE at %C");
+	      gfc_error ("Invalid clause in module with $!ACC DECLARE at %L",
+			 &where);
 	      return MATCH_ERROR;
 	    }
 
@@ -1476,29 +1580,23 @@ gfc_match_oacc_declare (void)
 
       if (ns->proc_name->attr.oacc_function)
 	{
-	  gfc_error ("Invalid declare in routine with " "$!ACC DECLARE at %C");
-	  return MATCH_ERROR;
-	}
-
-      if (s->attr.in_common)
-	{
-	  gfc_error ("Unsupported: variable in a common block with "
-		     "$!ACC DECLARE at %C");
+	  gfc_error ("Invalid declare in routine with $!ACC DECLARE at %L",
+		     &where);
 	  return MATCH_ERROR;
 	}
 
       if (s->attr.use_assoc)
 	{
-	  gfc_error ("Unsupported: variable is USE-associated with "
-		     "$!ACC DECLARE at %C");
+	  gfc_error ("Variable is USE-associated with $!ACC DECLARE at %L",
+		     &where);
 	  return MATCH_ERROR;
 	}
 
       if ((s->attr.dimension || s->attr.codimension)
 	  && s->attr.dummy && s->as->type != AS_EXPLICIT)
 	{
-	  gfc_error ("Unsupported: assumed-size dummy array with "
-		     "$!ACC DECLARE at %C");
+	  gfc_error ("Assumed-size dummy array with $!ACC DECLARE at %L",
+		     &where);
 	  return MATCH_ERROR;
 	}
 
@@ -1525,38 +1623,7 @@ gfc_match_oacc_declare (void)
   new_oc->next = ns->oacc_declare;
   new_oc->module_var = module_var;
   new_oc->clauses = c;
-  new_oc->where = gfc_current_locus;
-
-  for (oc = new_oc; oc; oc = oc->next)
-    {
-      c = oc->clauses;
-      for (n = c->lists[OMP_LIST_MAP]; n != NULL; n = n->next)
-	n->sym->mark = 0;
-    }
-
-  for (oc = new_oc; oc; oc = oc->next)
-    {
-      c = oc->clauses;
-      for (n = c->lists[OMP_LIST_MAP]; n != NULL; n = n->next)
-	{
-	  if (n->sym->mark)
-	    {
-	      gfc_error ("Symbol %qs present on multiple clauses at %C",
-			 n->sym->name);
-	      return MATCH_ERROR;
-	    }
-	  else
-	    n->sym->mark = 1;
-	}
-    }
-
-  for (oc = new_oc; oc; oc = oc->next)
-    {
-      c = oc->clauses;
-      for (n = c->lists[OMP_LIST_MAP]; n != NULL; n = n->next)
-	n->sym->mark = 1;
-    }
-
+  new_oc->loc = gfc_current_locus;
   ns->oacc_declare = new_oc;
 
   return MATCH_YES;
@@ -4936,13 +5003,11 @@ resolve_oacc_loop (gfc_code *code)
   resolve_oacc_nested_loops (code, do_code, collapse, "collapsed");
 }
 
-
 void
 gfc_resolve_oacc_declare (gfc_namespace *ns)
 {
   int list;
   gfc_omp_namelist *n;
-  locus loc;
   gfc_oacc_declare *oc;
 
   if (ns->oacc_declare == NULL)
@@ -4950,55 +5015,40 @@ gfc_resolve_oacc_declare (gfc_namespace *ns)
 
   for (oc = ns->oacc_declare; oc; oc = oc->next)
     {
-      loc = oc->where;
-
-      for (list = OMP_LIST_DEVICE_RESIDENT;
-	   list <= OMP_LIST_DEVICE_RESIDENT; list++)
+      for (list = 0; list < OMP_LIST_NUM; list++)
 	for (n = oc->clauses->lists[list]; n; n = n->next)
 	  {
 	    n->sym->mark = 0;
 	    if (n->sym->attr.flavor == FL_PARAMETER)
-	      gfc_error ("PARAMETER object %qs is not allowed at %L",
-			 n->sym->name, &loc);
-	  }
+	      {
+		gfc_error ("PARAMETER object %qs is not allowed at %L",
+			   n->sym->name, &oc->loc);
+		continue;
+	      }
 
-      for (list = OMP_LIST_DEVICE_RESIDENT;
-	    list <= OMP_LIST_DEVICE_RESIDENT; list++)
-	for (n = oc->clauses->lists[list]; n; n = n->next)
-	  {
-	    if (n->sym->mark)
-	      gfc_error ("Symbol %qs present on multiple clauses at %L",
-			 n->sym->name, &loc);
-	    else
-	      n->sym->mark = 1;
+	    if (n->expr && n->expr->ref->type == REF_ARRAY)
+	      {
+		gfc_error ("Array sections: %qs not allowed in"
+			   " $!ACC DECLARE at %L", n->sym->name, &oc->loc);
+		continue;
+	      }
 	  }
 
       for (n = oc->clauses->lists[OMP_LIST_DEVICE_RESIDENT]; n; n = n->next)
-	check_array_not_assumed (n->sym, loc, "DEVICE_RESIDENT");
-
-      for (n = oc->clauses->lists[OMP_LIST_MAP]; n; n = n->next)
-	{
-	  if (n->expr && n->expr->ref->type == REF_ARRAY)
-	      gfc_error ("Subarray: %qs not allowed in $!ACC DECLARE at %L",
-			 n->sym->name, &loc);
-	}
-    }
-
-  for (oc = ns->oacc_declare; oc; oc = oc->next)
-    {
-      for (list = OMP_LIST_LINK; list <= OMP_LIST_LINK; list++)
-	for (n = oc->clauses->lists[list]; n; n = n->next)
-	  n->sym->mark = 0;
+	check_array_not_assumed (n->sym, oc->loc, "DEVICE_RESIDENT");
     }
 
   for (oc = ns->oacc_declare; oc; oc = oc->next)
     {
-      for (list = OMP_LIST_LINK; list <= OMP_LIST_LINK; list++)
+      for (list = 0; list < OMP_LIST_NUM; list++)
 	for (n = oc->clauses->lists[list]; n; n = n->next)
 	  {
 	    if (n->sym->mark)
-	      gfc_error ("Symbol %qs present on multiple clauses at %L",
-			 n->sym->name, &loc);
+	      {
+		gfc_error ("Symbol %qs present on multiple clauses at %L",
+			   n->sym->name, &oc->loc);
+		continue;
+	      }
 	    else
 	      n->sym->mark = 1;
 	  }
@@ -5006,13 +5056,12 @@ gfc_resolve_oacc_declare (gfc_namespace *ns)
 
   for (oc = ns->oacc_declare; oc; oc = oc->next)
     {
-      for (list = OMP_LIST_LINK; list <= OMP_LIST_LINK; list++)
+      for (list = 0; list < OMP_LIST_NUM; list++)
 	for (n = oc->clauses->lists[list]; n; n = n->next)
 	  n->sym->mark = 0;
     }
 }
 
-
 void
 gfc_resolve_oacc_directive (gfc_code *code, gfc_namespace *ns ATTRIBUTE_UNUSED)
 {
diff --git gcc/fortran/parse.c gcc/fortran/parse.c
index 6c4d195..b2d15a8 100644
--- gcc/fortran/parse.c
+++ gcc/fortran/parse.c
@@ -1406,7 +1406,7 @@ push_state (gfc_state_data *p, gfc_compile_state new_state, gfc_symbol *sym)
   p->head = p->tail = NULL;
   p->do_variable = NULL;
   if (p->state != COMP_DO && p->state != COMP_DO_CONCURRENT)
-    p->ext.oacc_declare = NULL;
+    p->ext.oacc_declare_clauses = NULL;
 
   /* If this the state of a construct like BLOCK, DO or IF, the corresponding
      construct statement was accepted right before pushing the state.  Thus,
diff --git gcc/fortran/parse.h gcc/fortran/parse.h
index f343550..94b2ada 100644
--- gcc/fortran/parse.h
+++ gcc/fortran/parse.h
@@ -48,7 +48,7 @@ typedef struct gfc_state_data
   union
   {
     gfc_st_label *end_do_label;
-    struct gfc_oacc_declare *oacc_declare;
+    gfc_oacc_declare *oacc_declare_clauses;
   }
   ext;
 }
diff --git gcc/fortran/resolve.c gcc/fortran/resolve.c
index 1d38d23..febf0fa 100644
--- gcc/fortran/resolve.c
+++ gcc/fortran/resolve.c
@@ -9374,7 +9374,6 @@ gfc_resolve_blocks (gfc_code *b, gfc_namespace *ns)
 	case EXEC_OACC_EXIT_DATA:
 	case EXEC_OACC_ATOMIC:
 	case EXEC_OACC_ROUTINE:
-	case EXEC_OACC_DECLARE:
 	case EXEC_OMP_ATOMIC:
 	case EXEC_OMP_CRITICAL:
 	case EXEC_OMP_DISTRIBUTE:
diff --git gcc/fortran/st.c gcc/fortran/st.c
index 78099b8..566150b 100644
--- gcc/fortran/st.c
+++ gcc/fortran/st.c
@@ -187,7 +187,7 @@ gfc_free_statement (gfc_code *p)
 
     case EXEC_OACC_DECLARE:
       if (p->ext.oacc_declare)
-	gfc_free_oacc_declares (p->ext.oacc_declare);
+	gfc_free_oacc_declare_clauses (p->ext.oacc_declare);
       break;
 
     case EXEC_OACC_PARALLEL_LOOP:
diff --git gcc/fortran/symbol.c gcc/fortran/symbol.c
index 43fd25d..ff9aff9 100644
--- gcc/fortran/symbol.c
+++ gcc/fortran/symbol.c
@@ -1269,7 +1269,8 @@ gfc_add_omp_declare_target (symbol_attribute *attr, const char *name,
 
 
 bool
-gfc_add_oacc_declare_create (symbol_attribute *attr, const char *name, locus *where)
+gfc_add_oacc_declare_create (symbol_attribute *attr, const char *name,
+			     locus *where)
 {
   if (check_used (attr, name, where))
     return false;
@@ -1283,7 +1284,8 @@ gfc_add_oacc_declare_create (symbol_attribute *attr, const char *name, locus *wh
 
 
 bool
-gfc_add_oacc_declare_copyin (symbol_attribute *attr, const char *name, locus *where)
+gfc_add_oacc_declare_copyin (symbol_attribute *attr, const char *name,
+			     locus *where)
 {
   if (check_used (attr, name, where))
     return false;
@@ -1297,7 +1299,8 @@ gfc_add_oacc_declare_copyin (symbol_attribute *attr, const char *name, locus *wh
 
 
 bool
-gfc_add_oacc_declare_deviceptr (symbol_attribute *attr, const char *name, locus *where)
+gfc_add_oacc_declare_deviceptr (symbol_attribute *attr, const char *name,
+				locus *where)
 {
   if (check_used (attr, name, where))
     return false;
@@ -1311,7 +1314,8 @@ gfc_add_oacc_declare_deviceptr (symbol_attribute *attr, const char *name, locus
 
 
 bool
-gfc_add_oacc_declare_device_resident (symbol_attribute *attr, const char *name, locus *where)
+gfc_add_oacc_declare_device_resident (symbol_attribute *attr, const char *name,
+				      locus *where)
 {
   if (check_used (attr, name, where))
     return false;
diff --git gcc/fortran/trans-decl.c gcc/fortran/trans-decl.c
index 56bc797..eaf46cb 100644
--- gcc/fortran/trans-decl.c
+++ gcc/fortran/trans-decl.c
@@ -1302,15 +1302,20 @@ add_attributes_to_decl (symbol_attribute sym_attr, tree list)
       }
 
   if (sym_attr.omp_declare_target
+#if 0 /* TODO */
       || sym_attr.oacc_declare_create
       || sym_attr.oacc_declare_copyin
       || sym_attr.oacc_declare_deviceptr
-      || sym_attr.oacc_declare_device_resident)
+      || sym_attr.oacc_declare_device_resident
+#endif
+      )
     list = tree_cons (get_identifier ("omp declare target"),
 		      NULL_TREE, list);
+#if 0 /* TODO */
   if (sym_attr.oacc_declare_link)
     list = tree_cons (get_identifier ("omp declare target link"),
 		      NULL_TREE, list);
+#endif
 
   if (sym_attr.oacc_function)
     {
@@ -5782,61 +5787,6 @@ is_ieee_module_used (gfc_namespace *ns)
 }
 
 
-static struct oacc_return
-{
-  gfc_code *code;
-  struct oacc_return *next;
-} *oacc_returns;
-
-
-static void
-find_oacc_return (gfc_code *code)
-{
-  if (code->next)
-    {
-      if (code->next->op == EXEC_RETURN)
-	{
-	  struct oacc_return *r;
-
-	  r = XCNEW (struct oacc_return);
-	  r->code = code;
-	  r->next = NULL;
-
-	  if (oacc_returns)
-	    r->next = oacc_returns;
-
-	  oacc_returns = r;
-	}
-      else
-	{
-	  find_oacc_return (code->next);
-	}
-    }
-
-  if (code->block)
-    find_oacc_return (code->block);
-
-  return;
-}
-
-
-static gfc_code *
-find_end (gfc_code *code)
-{
-  gcc_assert (code);
-
-  if (code->next)
-    {
-      if (code->next->op == EXEC_END_PROCEDURE)
-	return code;
-      else
-	return find_end (code->next);
-    }
-
-  return NULL;
-}
-
-
 static gfc_omp_clauses *module_oacc_clauses;
 
 
@@ -5891,16 +5841,17 @@ find_module_oacc_declare_clauses (gfc_symbol *sym)
 
 
 void
-finish_oacc_declare (gfc_namespace *ns, enum sym_flavor flavor)
+finish_oacc_declare (gfc_namespace *ns, gfc_symbol *sym, bool block)
 {
   gfc_code *code;
   gfc_oacc_declare *oc;
-  gfc_omp_namelist *n;
   locus where = gfc_current_locus;
+  gfc_omp_clauses *omp_clauses = NULL;
+  gfc_omp_namelist *n, *p;
 
   gfc_traverse_ns (ns, find_module_oacc_declare_clauses);
 
-  if (module_oacc_clauses && flavor == FL_PROGRAM)
+  if (module_oacc_clauses && sym->attr.flavor == FL_PROGRAM)
     {
       gfc_oacc_declare *new_oc;
 
@@ -5917,107 +5868,63 @@ finish_oacc_declare (gfc_namespace *ns, enum sym_flavor flavor)
 
   for (oc = ns->oacc_declare; oc; oc = oc->next)
     {
-      gfc_omp_clauses *omp_clauses, *ret_clauses;
-
       if (oc->module_var)
 	continue;
 
-      if (oc->clauses)
-	{
-	   code = XCNEW (gfc_code);
-	   code->op = EXEC_OACC_DECLARE;
-	   code->loc = where;
-
-	   ret_clauses = NULL;
-	   omp_clauses = oc->clauses;
-
-	   for (n = omp_clauses->lists[OMP_LIST_MAP]; n; n = n->next)
-	     {
-		bool ret = false;
-		gfc_omp_map_op new_op;
-
-		switch (n->u.map_op)
-		  {
-		    case OMP_MAP_ALLOC:
-		    case OMP_MAP_FORCE_ALLOC:
-		      new_op = OMP_MAP_FORCE_DEALLOC;
-		      ret = true;
-		      break;
+      if (block)
+	gfc_error ("Sorry, $!ACC DECLARE at %L is not allowed "
+		   "in BLOCK construct", &oc->loc);
 
-		    case OMP_MAP_DEVICE_RESIDENT:
-		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
-		      new_op = OMP_MAP_FORCE_DEALLOC;
-		      ret = true;
-		      break;
 
-		    case OMP_MAP_FORCE_FROM:
-		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
-		      new_op = OMP_MAP_FORCE_FROM;
-		      ret = true;
-		      break;
-
-		    case OMP_MAP_FORCE_TO:
-		      new_op = OMP_MAP_FORCE_DEALLOC;
-		      ret = true;
-		      break;
-
-		    case OMP_MAP_FORCE_TOFROM:
-		      n->u.map_op = OMP_MAP_FORCE_TO;
-		      new_op = OMP_MAP_FORCE_FROM;
-		      ret = true;
-		      break;
+      if (oc->clauses && oc->clauses->lists[OMP_LIST_MAP])
+	{
+	  if (omp_clauses == NULL)
+	    {
+	      omp_clauses = oc->clauses;
+	      continue;
+	    }
 
-		    case OMP_MAP_FROM:
-		      n->u.map_op = OMP_MAP_FORCE_ALLOC;
-		      new_op = OMP_MAP_FROM;
-		      ret = true;
-		      break;
+	  for (n = oc->clauses->lists[OMP_LIST_MAP]; n; p = n, n = n->next)
+	    ;
 
-		    case OMP_MAP_FORCE_DEVICEPTR:
-		    case OMP_MAP_FORCE_PRESENT:
-		    case OMP_MAP_LINK:
-		    case OMP_MAP_TO:
-		      break;
+	  gcc_assert (p->next == NULL);
 
-		    case OMP_MAP_TOFROM:
-		      n->u.map_op = OMP_MAP_TO;
-		      new_op = OMP_MAP_FROM;
-		      ret = true;
-		      break;
+	  p->next = omp_clauses->lists[OMP_LIST_MAP];
+	  omp_clauses = oc->clauses;
+	}
+    }
 
-		    default:
-		      gcc_unreachable ();
-		      break;
-		  }
+  if (!omp_clauses)
+    return;
 
-		if (ret)
-		  {
-		    gfc_omp_namelist *new_n;
+  for (n = omp_clauses->lists[OMP_LIST_MAP]; n; n = n->next)
+    {
+      switch (n->u.map_op)
+	{
+	  case OMP_MAP_DEVICE_RESIDENT:
+	    n->u.map_op = OMP_MAP_FORCE_ALLOC;
+	    break;
 
-		    new_n = gfc_get_omp_namelist ();
-		    new_n->sym = n->sym;
-		    new_n->u.map_op = new_op;
+	  default:
+	    break;
+	}
+    }
 
-		    if (!ret_clauses)
-		      ret_clauses = gfc_get_omp_clauses ();
+  code = XCNEW (gfc_code);
+  code->op = EXEC_OACC_DECLARE;
+  code->loc = where;
 
-		    if (ret_clauses->lists[OMP_LIST_MAP])
-		      new_n->next = ret_clauses->lists[OMP_LIST_MAP];
+  code->ext.oacc_declare = gfc_get_oacc_declare ();
+  code->ext.oacc_declare->clauses = omp_clauses;
 
-		    ret_clauses->lists[OMP_LIST_MAP] = new_n;
-		    ret = false;
-		  }
-	     }
+  code->block = XCNEW (gfc_code);
+  code->block->op = EXEC_OACC_DECLARE;
+  code->block->loc = where;
 
-	   code->ext.oacc_declare = gfc_get_oacc_declare ();
-	   code->ext.oacc_declare->clauses = omp_clauses;
-	   code->ext.oacc_declare->return_clauses = ret_clauses;
+  if (ns->code)
+    code->block->next = ns->code;
 
-	   if (ns->code)
-	     code->next = ns->code;
-	   ns->code = code;
-	}
-    }
+  ns->code = code;
 
   return;
 }
@@ -6159,8 +6066,7 @@ gfc_generate_function_code (gfc_namespace * ns)
   if ((gfc_option.rtcheck & GFC_RTCHECK_BOUNDS) && !sym->attr.is_bind_c)
     add_argument_checking (&body, sym);
 
-  /* Generate !$ACC DECLARE directive. */
-  finish_oacc_declare (ns, sym->attr.flavor);
+  finish_oacc_declare (ns, sym, false);
 
   tmp = gfc_trans_code (ns->code);
   gfc_add_expr_to_block (&body, tmp);
diff --git gcc/fortran/trans-openmp.c gcc/fortran/trans-openmp.c
index 87ecc5a..e98a29c 100644
--- gcc/fortran/trans-openmp.c
+++ gcc/fortran/trans-openmp.c
@@ -1776,8 +1776,8 @@ gfc_trans_omp_clauses_1 (stmtblock_t *block, gfc_omp_clauses *clauses,
 	  clause_code = OMP_CLAUSE_USE_DEVICE;
 	  goto add_clause;
 	case OMP_LIST_DEVICE_RESIDENT:
-	case OMP_LIST_LINK:
-	  continue;
+	  clause_code = OMP_CLAUSE_DEVICE_RESIDENT;
+	  goto add_clause;
 
 	add_clause:
 	  omp_clauses
@@ -1925,9 +1925,6 @@ gfc_trans_omp_clauses_1 (stmtblock_t *block, gfc_omp_clauses *clauses,
 	      if (!n->sym->attr.referenced)
 		continue;
 
-	      if (n->sym->attr.use_assoc && n->sym->attr.oacc_declare_link)
-		continue;
-
 	      tree node = build_omp_clause (input_location, OMP_CLAUSE_MAP);
 	      tree node2 = NULL_TREE;
 	      tree node3 = NULL_TREE;
@@ -2141,9 +2138,6 @@ gfc_trans_omp_clauses_1 (stmtblock_t *block, gfc_omp_clauses *clauses,
 		case OMP_MAP_FORCE_DEVICEPTR:
 		  OMP_CLAUSE_SET_MAP_KIND (node, GOMP_MAP_FORCE_DEVICEPTR);
 		  break;
-		case OMP_MAP_DEVICE_RESIDENT:
-		  OMP_CLAUSE_SET_MAP_KIND (node, GOMP_MAP_DEVICE_RESIDENT);
-		  break;
 		default:
 		  gcc_unreachable ();
 		}
@@ -4672,23 +4666,18 @@ tree
 gfc_trans_oacc_declare (gfc_code *code)
 {
   stmtblock_t block;
-  tree stmt, c1;
+  tree stmt, oacc_clauses;
   enum tree_code construct_code;
 
-  gfc_start_block (&block);
-
-  construct_code = OACC_DECLARE;
+  construct_code = OACC_DATA;
 
   gfc_start_block (&block);
-  c1 = gfc_trans_omp_clauses (&block, code->ext.oacc_declare->clauses,
-			      code->loc);
-
-#if 0 /* TODO */
-  c2 = gfc_trans_omp_clauses (&block, code->ext.oacc_declare->return_clauses,
-			      code->loc);
-#endif
 
-  stmt = build1_loc (input_location, construct_code, void_type_node, c1);
+  oacc_clauses = gfc_trans_omp_clauses (&block, code->ext.oacc_declare->clauses,
+					code->loc);
+  stmt = gfc_trans_omp_code (code->block->next, true);
+  stmt = build2_loc (input_location, construct_code, void_type_node, stmt,
+		     oacc_clauses);
   gfc_add_expr_to_block (&block, stmt);
 
   return gfc_finish_block (&block);
diff --git gcc/fortran/trans-stmt.c gcc/fortran/trans-stmt.c
index 4a9c98a..06591a3 100644
--- gcc/fortran/trans-stmt.c
+++ gcc/fortran/trans-stmt.c
@@ -1575,8 +1575,7 @@ gfc_trans_block_construct (gfc_code* code)
   exit_label = gfc_build_label_decl (NULL_TREE);
   code->exit_label = exit_label;
 
-  /* Generate !$ACC DECLARE directive. */
-  finish_oacc_declare (ns, FL_UNKNOWN);
+  finish_oacc_declare (ns, sym, true);
 
   gfc_add_expr_to_block (&body, gfc_trans_code (ns->code));
   gfc_add_expr_to_block (&body, build1_v (LABEL_EXPR, exit_label));
[diff --git gcc/testsuite/ChangeLog gcc/testsuite/ChangeLog]
diff --git gcc/testsuite/gfortran.dg/goacc/declare-1.f95 gcc/testsuite/gfortran.dg/goacc/declare-1.f95
index 3129f04..1ff8e6a 100644
--- gcc/testsuite/gfortran.dg/goacc/declare-1.f95
+++ gcc/testsuite/gfortran.dg/goacc/declare-1.f95
@@ -1,5 +1,4 @@
 ! { dg-do compile } 
-! { dg-additional-options "-fdump-tree-original" } 
 
 program test
   implicit none
@@ -11,8 +10,7 @@ contains
     integer, value :: n
     BLOCK
        integer i
-       !$acc declare copy(i)
+       !$acc declare copy(i) ! { dg-error "is not allowed" }
     END BLOCK
   end function foo
 end program test
-! { dg-final { scan-tree-dump-times "pragma acc declare map\\(force_to:i\\)" 2 "original" } }
diff --git gcc/testsuite/gfortran.dg/goacc/declare-2.f95 gcc/testsuite/gfortran.dg/goacc/declare-2.f95
index afdbe2e..aa1704f 100644
--- gcc/testsuite/gfortran.dg/goacc/declare-2.f95
+++ gcc/testsuite/gfortran.dg/goacc/declare-2.f95
@@ -21,24 +21,51 @@ end subroutine
 
 end module
 
+module bmod
+
+  implicit none
+  integer :: a, b, c, d, e, f, g, h, i
+  common /data1/ a, b, c
+  common /data2/ d, e, f
+  common /data3/ g, h, i
+  !$acc declare link (a) ! { dg-error "element of a COMMON" }
+  !$acc declare link (/data1/)
+  !$acc declare link (a, b, c) ! { dg-error "element of a COMMON" }
+  !$acc declare link (/foo/) ! { dg-error "not found" }
+  !$acc declare device_resident (/data2/)
+  !$acc declare device_resident (/data3/) ! { dg-error "present on multiple clauses" }
+  !$acc declare device_resident (g, h, i)
+
+end module
+
 subroutine bsubr (foo)
   implicit none
 
   integer, dimension (:) :: foo
 
-  !$acc declare copy (foo) ! { dg-error "assumed-size dummy array" }
-  !$acc declare copy (foo(1:2)) ! { dg-error "assumed-size dummy array" }
+  !$acc declare copy (foo) ! { dg-error "Assumed-size dummy array" }
+  !$acc declare copy (foo(1:2)) ! { dg-error "Assumed-size dummy array" }
 
-end subroutine
+end subroutine bsubr
+
+subroutine multiline
+  integer :: b(8)
+
+  !$acc declare copyin (b) ! { dg-error "present on multiple clauses" }
+  !$acc declare copyin (b)
+
+end subroutine multiline
+
+subroutine subarray
+  integer :: c(8)
+
+  !$acc declare copy (c(1:2)) ! { dg-error "Array sections: 'c' not allowed" }
+
+end subroutine subarray
 
 program test
   integer :: a(8)
-  integer :: b(8)
-  integer :: c(8)
 
   !$acc declare create (a) copyin (a) ! { dg-error "present on multiple clauses" }
-  !$acc declare copyin (b)
-  !$acc declare copyin (b) ! { dg-error "present on multiple clauses" }
-  !$acc declare copy (c(1:2)) ! { dg-error "Subarray: 'c' not allowed" }
 
 end program
[diff --git libgomp/ChangeLog libgomp/ChangeLog]
diff --git libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90 libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
index dffbedd..430cd24 100644
--- libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
+++ libgomp/testsuite/libgomp.oacc-fortran/declare-1.f90
@@ -1,12 +1,15 @@
 ! { dg-do run  { target openacc_nvidia_accel_selected } }
+! libgomp: cuStreamSynchronize error: an illegal memory access was encountered
 ! { dg-xfail-run-if "TODO" { *-*-* } }
 
 module vars
+  implicit none
   integer z
   !$acc declare create (z)
 end module vars
 
 subroutine subr6 (a, d)
+  implicit none
   integer, parameter :: N = 8
   integer :: i
   integer :: a(N)
@@ -24,6 +27,7 @@ subroutine subr6 (a, d)
 end subroutine
 
 subroutine subr5 (a, b, c, d)
+  implicit none
   integer, parameter :: N = 8
   integer :: i
   integer :: a(N)
@@ -48,6 +52,7 @@ subroutine subr5 (a, b, c, d)
 end subroutine
 
 subroutine subr4 (a, b)
+  implicit none
   integer, parameter :: N = 8
   integer :: i
   integer :: a(N)
@@ -66,6 +71,7 @@ subroutine subr4 (a, b)
 end subroutine
 
 subroutine subr3 (a, c)
+  implicit none
   integer, parameter :: N = 8
   integer :: i
   integer :: a(N)
@@ -85,6 +91,7 @@ subroutine subr3 (a, c)
 end subroutine
 
 subroutine subr2 (a, b, c)
+  implicit none
   integer, parameter :: N = 8
   integer :: i
   integer :: a(N)
@@ -106,6 +113,7 @@ subroutine subr2 (a, b, c)
 end subroutine
 
 subroutine subr1 (a)
+  implicit none
   integer, parameter :: N = 8
   integer :: i
   integer :: a(N)
@@ -123,6 +131,7 @@ end subroutine
 
 subroutine test (a, e)
   use openacc
+  implicit none
   logical :: e
   integer, parameter :: N = 8
   integer :: a(N)
@@ -132,12 +141,14 @@ subroutine test (a, e)
 end subroutine
 
 subroutine subr0 (a, b, c, d)
+  implicit none
   integer, parameter :: N = 8
   integer :: a(N)
   !$acc declare copy (a)
   integer :: b(N)
   integer :: c(N)
   integer :: d(N)
+  integer :: i
 
   call test (a, .true.)
   call test (b, .false.)
@@ -206,11 +217,13 @@ end subroutine
 program main
   use vars
   use openacc
+  implicit none
   integer, parameter :: N = 8
   integer :: a(N)
   integer :: b(N)
   integer :: c(N)
   integer :: d(N)
+  integer :: i
 
   a(:) = 2
   b(:) = 3
diff --git libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90 libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
index 9b75aa1..2aa7907 100644
--- libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
+++ libgomp/testsuite/libgomp.oacc-fortran/declare-2.f90
@@ -1,6 +1,7 @@
 ! { dg-do run  { target openacc_nvidia_accel_selected } }
 
 module globalvars
+  implicit none
   integer a
   !$acc declare create (a)
 end module globalvars
@@ -8,6 +9,7 @@ end module globalvars
 program test
   use globalvars
   use openacc
+  implicit none
 
   if (acc_is_present (a) .neqv. .true.) call abort
 
diff --git libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90 libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
index 1d19bfd..3a6b420 100644
--- libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
+++ libgomp/testsuite/libgomp.oacc-fortran/declare-3.f90
@@ -1,13 +1,15 @@
 ! { dg-do run  { target openacc_nvidia_accel_selected } }
-! { dg-xfail-if "TODO" { *-*-* } }
 
 module globalvars
+  implicit none
   real b
   !$acc declare link (b)
 end module globalvars
 
 program test
   use openacc
+  use globalvars
+  implicit none
 
   real a
   real c
diff --git libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90 libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
index 997c8ac..226264e 100644
--- libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
+++ libgomp/testsuite/libgomp.oacc-fortran/declare-4.f90
@@ -1,6 +1,7 @@
 ! { dg-do run  { target openacc_nvidia_accel_selected } }
 
 module vars
+  implicit none
   real b
  !$acc declare create (b)
 end module vars
@@ -8,6 +9,7 @@ end module vars
 program test
   use vars
   use openacc
+  implicit none
   real a
 
   if (acc_is_present (b) .neqv. .true.) call abort
diff --git libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90 libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
index d7c9bac..bcd9c9c 100644
--- libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
+++ libgomp/testsuite/libgomp.oacc-fortran/declare-5.f90
@@ -9,6 +9,7 @@ end module vars
 program test
   use vars
   use openacc
+  implicit none
   real a
 
   if (acc_is_present (b) .neqv. .true.) call abort


Grüße
 Thomas

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 472 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [committed] [PR90862] OpenACC 'declare' ICE when nested inside another construct (was: [OpenACC] declare directive)
  2015-11-12  1:08                   ` James Norris
  2015-11-12  9:10                     ` Jakub Jelinek
@ 2019-06-18 22:21                     ` Thomas Schwinge
  1 sibling, 0 replies; 51+ messages in thread
From: Thomas Schwinge @ 2019-06-18 22:21 UTC (permalink / raw)
  To: gcc-patches


[-- Attachment #1.1: Type: text/plain, Size: 593 bytes --]

Hi!

On Wed, 11 Nov 2015 19:07:58 -0600, James Norris <jnorris@codesourcery.com> wrote:
> [...]
> --- a/gcc/gimple.h
> +++ b/gcc/gimple.h
> @@ -170,6 +170,7 @@ enum gf_mask {
>      GF_OMP_TARGET_KIND_OACC_DATA = 7,
>      GF_OMP_TARGET_KIND_OACC_UPDATE = 8,
>      GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA = 9,
> +    GF_OMP_TARGET_KIND_OACC_DECLARE = 10,
> [...]

This forgot to update 'check_omp_nesting_restrictions', giving rise to
PR90862 "OpenACC 'declare' ICE when nested inside another construct".
Now fixed on trunk in r272444, see attached.


Grüße
 Thomas



[-- Attachment #1.2: 0001-PR90862-OpenACC-declare-ICE-when-nested-inside.trunk.patch --]
[-- Type: text/x-diff, Size: 8886 bytes --]

From acb4157074770f715968c3a9c1e6929f98fcddc8 Mon Sep 17 00:00:00 2001
From: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Tue, 18 Jun 2019 22:13:54 +0000
Subject: [PATCH] [PR90862] OpenACC 'declare' ICE when nested inside another
 construct

	gcc/
	PR middle-end/90862
	* omp-low.c (check_omp_nesting_restrictions): Handle
	GF_OMP_TARGET_KIND_OACC_DECLARE.
	gcc/testsuite/
	PR middle-end/90862
	* c-c++-common/goacc/declare-1.c: Update.
	* c-c++-common/goacc/declare-2.c: Likewise.
	libgomp/
	PR middle-end/90862
	* testsuite/libgomp.oacc-c-c++-common/declare-1.c: Update.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@272444 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/ChangeLog                                 |  6 ++
 gcc/omp-low.c                                 |  1 +
 gcc/testsuite/ChangeLog                       |  6 ++
 gcc/testsuite/c-c++-common/goacc/declare-1.c  | 82 +++++++++++++++-
 gcc/testsuite/c-c++-common/goacc/declare-2.c  | 35 ++++++-
 libgomp/ChangeLog                             |  5 +
 .../libgomp.oacc-c-c++-common/declare-1.c     | 98 +++++++++++++++++--
 7 files changed, 223 insertions(+), 10 deletions(-)

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index cbf6915c8286..43a0a232dc21 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,9 @@
+2019-06-18  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR middle-end/90862
+	* omp-low.c (check_omp_nesting_restrictions): Handle
+	GF_OMP_TARGET_KIND_OACC_DECLARE.
+
 2019-06-18  Uroš Bizjak  <ubizjak@gmail.com>
 
 	* config/i386/i386.md (@cmp<mode>_1): Rename from cmp<mode>_1.
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 9df21a4d0466..b0f1d94abf73 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -3119,6 +3119,7 @@ check_omp_nesting_restrictions (gimple *stmt, omp_context *ctx)
 	    case GF_OMP_TARGET_KIND_OACC_UPDATE: stmt_name = "update"; break;
 	    case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
 	      stmt_name = "enter/exit data"; break;
+	    case GF_OMP_TARGET_KIND_OACC_DECLARE: stmt_name = "declare"; break;
 	    case GF_OMP_TARGET_KIND_OACC_HOST_DATA: stmt_name = "host_data";
 	      break;
 	    default: gcc_unreachable ();
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 2848d2ceecab..473fd66d39fd 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2019-06-18  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR middle-end/90862
+	* c-c++-common/goacc/declare-1.c: Update.
+	* c-c++-common/goacc/declare-2.c: Likewise.
+
 2019-06-18  Marek Polacek  <polacek@redhat.com>
 
 	PR c++/84698
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c b/gcc/testsuite/c-c++-common/goacc/declare-1.c
index 35b1ccd367bd..7c4380f4f041 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
@@ -1,5 +1,5 @@
-/* Test valid uses of declare directive.  */
-/* { dg-do compile } */
+/* Test valid use of the OpenACC 'declare' directive.  */
+
 
 int v0;
 #pragma acc declare create(v0)
@@ -25,6 +25,7 @@ int v9;
 int v10;
 #pragma acc declare present_or_create(v10)
 
+
 void
 f (void)
 {
@@ -93,3 +94,80 @@ f (void)
   }
  b:;
 }
+
+
+/* The same as 'f' but everything contained in an OpenACC 'data' construct.  */
+
+void
+f_data (void)
+{
+#pragma acc data
+  {
+    int va0;
+# pragma acc declare create(va0)
+
+    int va1;
+# pragma acc declare copyin(va1)
+
+    int *va2;
+# pragma acc declare deviceptr(va2)
+
+    int va3;
+# pragma acc declare device_resident(va3)
+
+#if 0 /* TODO */
+    extern int ve0;
+# pragma acc declare create(ve0)
+
+    extern int ve1;
+# pragma acc declare copyin(ve1)
+
+    extern int *ve2;
+# pragma acc declare deviceptr(ve2)
+
+    extern int ve3;
+# pragma acc declare device_resident(ve3)
+
+    extern int ve4;
+# pragma acc declare link(ve4)
+
+    extern int ve5;
+# pragma acc declare present_or_copyin(ve5)
+ 
+    extern int ve6;
+# pragma acc declare present_or_create(ve6)
+#endif
+
+    int va5;
+# pragma acc declare copy(va5)
+
+    int va6;
+# pragma acc declare copyout(va6)
+
+    int va7;
+# pragma acc declare present(va7)
+
+    int va8;
+# pragma acc declare present_or_copy(va8)
+
+    int va9;
+# pragma acc declare present_or_copyin(va9)
+
+    int va10;
+# pragma acc declare present_or_copyout(va10)
+
+    int va11;
+# pragma acc declare present_or_create(va11)
+
+  a:
+    {
+      int va0;
+# pragma acc declare create(va0)
+      if (v1)
+	goto a;
+      else
+	goto b;
+    }
+  b:;
+  }
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c b/gcc/testsuite/c-c++-common/goacc/declare-2.c
index 33b82459bfc5..af43b6bc8162 100644
--- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
@@ -1,5 +1,5 @@
-/* Test invalid uses of declare directive.  */
-/* { dg-do compile } */
+/* Test invalid use of the OpenACC 'declare' directive.  */
+
 
 #pragma acc declare /* { dg-error "no valid clauses" } */
 
@@ -42,6 +42,7 @@ int va11;
 int va12;
 #pragma acc declare create (va12) link (va12) /* { dg-error "more than once" } */
 
+
 void
 f (void)
 {
@@ -65,3 +66,33 @@ f (void)
 
 #pragma acc declare present (v2) /* { dg-error "invalid use of" } */
 }
+
+
+/* The same as 'f' but everything contained in an OpenACC 'data' construct.  */
+
+void
+f_data (void)
+{
+#pragma acc data
+  {
+    int va0;
+# pragma acc declare link(va0) /* { dg-error "global variable" } */
+
+    extern int ve0;
+# pragma acc declare copy(ve0) /* { dg-error "invalid use of" } */
+
+    extern int ve1;
+# pragma acc declare copyout(ve1) /* { dg-error "invalid use of" } */
+
+    extern int ve2;
+# pragma acc declare present(ve2) /* { dg-error "invalid use of" } */
+
+    extern int ve3;
+# pragma acc declare present_or_copy(ve3) /* { dg-error "invalid use of" } */
+
+    extern int ve4;
+# pragma acc declare present_or_copyout(ve4) /* { dg-error "invalid use of" } */
+
+# pragma acc declare present (v2) /* { dg-error "invalid use of" } */
+  }
+}
diff --git a/libgomp/ChangeLog b/libgomp/ChangeLog
index 827bab2d8961..06004aafde98 100644
--- a/libgomp/ChangeLog
+++ b/libgomp/ChangeLog
@@ -1,3 +1,8 @@
+2019-06-18  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR middle-end/90862
+	* testsuite/libgomp.oacc-c-c++-common/declare-1.c: Update.
+
 2019-06-16  Tom de Vries  <tdevries@suse.de>
 
 	PR tree-optimization/89376
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
index bc726174252d..087b95456926 100644
--- a/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/declare-1.c
@@ -1,6 +1,5 @@
 #include <openacc.h>
 #include <stdlib.h>
-#include <stdio.h>
 
 #define N 8
 
@@ -39,14 +38,14 @@ subr1 (int *a)
   }
 }
 
-int b[8];
+int b[N];
 #pragma acc declare create (b)
 
-int d[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+int d[N] = { 1, 2, 3, 4, 5, 6, 7, 8 };
 #pragma acc declare copyin (d)
 
-int
-main (int argc, char **argv)
+static void
+f (void)
 {
   int a[N];
   int e[N];
@@ -110,11 +109,98 @@ main (int argc, char **argv)
 
   subr2 (&a[0]);
 
-  for (i = 0; i < 1; i++)
+  for (i = 0; i < N; i++)
     {
       if (a[i] != 1234 * 6)
 	abort ();
     }
+}
+
+
+/* The same as 'f' but everything contained in an OpenACC 'data' construct.  */
+
+static void
+f_data (void)
+{
+#pragma acc data
+  {
+    int a[N];
+    int e[N];
+# pragma acc declare create (e)
+    int i;
+
+    for (i = 0; i < N; i++)
+      a[i] = i + 1;
+
+    if (!acc_is_present (&b, sizeof (b)))
+      abort ();
+
+    if (!acc_is_present (&d, sizeof (d)))
+      abort ();
+
+    if (!acc_is_present (&e, sizeof (e)))
+      abort ();
+
+# pragma acc parallel copyin (a[0:N])
+    {
+      for (i = 0; i < N; i++)
+	{
+	  b[i] = a[i];
+	  a[i] = b[i];
+	}
+    }
+
+    for (i = 0; i < N; i++)
+      {
+	if (a[i] != i + 1)
+	  abort ();
+      }
+
+# pragma acc parallel copy (a[0:N])
+    {
+      for (i = 0; i < N; i++)
+	{
+	  e[i] = a[i] + d[i];
+	  a[i] = e[i];
+	}
+    }
+
+    for (i = 0; i < N; i++)
+      {
+	if (a[i] != (i + 1) * 2)
+	  abort ();
+      }
+
+    for (i = 0; i < N; i++)
+      {
+	a[i] = 1234;
+      }
+
+    subr1 (&a[0]);
+
+    for (i = 0; i < N; i++)
+      {
+	if (a[i] != 1234 * 2)
+	  abort ();
+      }
+
+    subr2 (&a[0]);
+
+    for (i = 0; i < N; i++)
+      {
+	if (a[i] != 1234 * 6)
+	  abort ();
+      }
+  }
+}
+
+
+int
+main (int argc, char **argv)
+{
+  f ();
+
+  f_data ();
 
   return 0;
 }
-- 
2.20.1


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 658 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [committed] Fix description of 'GOMP_MAP_FIRSTPRIVATE' (was: [OpenACC] declare directive)
  2015-11-23 12:41                         ` Thomas Schwinge
  2015-11-24  8:45                           ` [gomp4] " Thomas Schwinge
@ 2019-06-18 22:45                           ` Thomas Schwinge
  1 sibling, 0 replies; 51+ messages in thread
From: Thomas Schwinge @ 2019-06-18 22:45 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jakub Jelinek


[-- Attachment #1.1: Type: text/plain, Size: 1199 bytes --]

Hi!

On Mon, 23 Nov 2015 13:37:20 +0100, I wrote:
> Hi Jim!
> 
> A few things I noticed when working on merging your trunk r230275 into
> gomp-4_0-branch.  Please fix these (on trunk).

> | --- include/gomp-constants.h
> | +++ include/gomp-constants.h
> | @@ -72,6 +72,11 @@ enum gomp_map_kind
> |         POINTER_SIZE_UNITS.  */
> |      GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
> |      /* Do not map, copy bits for firstprivate instead.  */
> | +    /* OpenACC device_resident.  */
> | +    GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
> | +    /* OpenACC link.  */
> | +    GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
> | +    /* Allocate.  */
> |      GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
> |      /* Similarly, but store the value in the pointer rather than
> |         pointed by the pointer.  */
> 
> I suspect the "Do not map, copy bits for firstprivate instead" comment
> still applies to GOMP_MAP_FIRSTPRIVATE only, which here (unintended?) got
> an "Allocate" comment added?

As obvious, now fixed on trunk in r272450 "Fix description of
'GOMP_MAP_FIRSTPRIVATE'", see attached.


Grüße
 Thomas



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: 0001-Fix-description-of-GOMP_MAP_FIRSTPRIVATE.trunk.patch --]
[-- Type: text/x-diff, Size: 1924 bytes --]

From 2a0899eaf3a9aabe64cb0649fd7a7fa263ebfcaa Mon Sep 17 00:00:00 2001
From: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Tue, 18 Jun 2019 22:14:52 +0000
Subject: [PATCH] Fix description of 'GOMP_MAP_FIRSTPRIVATE'

..., which got garbled in r230275.

	include/
	* gomp-constants.h (enum gomp_map_kind): Fix description of
	'GOMP_MAP_FIRSTPRIVATE'.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@272450 138bc75d-0d04-0410-961f-82ee72b054a4
---
 include/ChangeLog        | 5 +++++
 include/gomp-constants.h | 3 +--
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/include/ChangeLog b/include/ChangeLog
index 28f4664aa557..6d09a8d6e07b 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,8 @@
+2019-06-18  Thomas Schwinge  <thomas@codesourcery.com>
+
+	* gomp-constants.h (enum gomp_map_kind): Fix description of
+	'GOMP_MAP_FIRSTPRIVATE'.
+
 2019-06-10  Martin Liska  <mliska@suse.cz>
 
 	* ansidecl.h (ATTRIBUTE_WARN_UNUSED_RESULT): New macro.
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 8b93634f1b8b..82e9094c9342 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -71,12 +71,11 @@ enum gomp_map_kind
     /* Is a device pointer.  OMP_CLAUSE_SIZE for these is unused; is implicitly
        POINTER_SIZE_UNITS.  */
     GOMP_MAP_FORCE_DEVICEPTR =		(GOMP_MAP_FLAG_SPECIAL_1 | 0),
-    /* Do not map, copy bits for firstprivate instead.  */
     /* OpenACC device_resident.  */
     GOMP_MAP_DEVICE_RESIDENT =		(GOMP_MAP_FLAG_SPECIAL_1 | 1),
     /* OpenACC link.  */
     GOMP_MAP_LINK =			(GOMP_MAP_FLAG_SPECIAL_1 | 2),
-    /* Allocate.  */
+    /* Do not map, copy bits for firstprivate instead.  */
     GOMP_MAP_FIRSTPRIVATE =		(GOMP_MAP_FLAG_SPECIAL | 0),
     /* Similarly, but store the value in the pointer rather than
        pointed by the pointer.  */
-- 
2.20.1


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 658 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [committed] [PR85221] Set 'omp declare target', 'omp declare target link' attributes for Fortran OpenACC 'declare'd variables (was: [gomp4] Re: OpenACC declare directive updates)
  2015-11-27 11:41           ` [gomp4] " Thomas Schwinge
@ 2019-06-18 23:02             ` Thomas Schwinge
  0 siblings, 0 replies; 51+ messages in thread
From: Thomas Schwinge @ 2019-06-18 23:02 UTC (permalink / raw)
  To: gcc-patches, fortran; +Cc: Jakub Jelinek, Tom de Vries


[-- Attachment #1.1: Type: text/plain, Size: 2032 bytes --]

Hi!

On Fri, 27 Nov 2015 12:37:23 +0100, I wrote:
> On Thu, 19 Nov 2015 10:22:16 -0600, James Norris <jnorris@codesourcery.com> wrote:
> > [...]

> Merging your trunk r230722 and r230725 with the existing Fortran OpenACC
> declare implementation present on gomp-4_0-branch, I effectively applied
> the following to gomp-4_0-branch in 231002.  Please verify this.
> 
> Regarding my Fortran XFAIL comments in
> <http://mid.mail-archive.com/878u5n7pqk.fsf@hertz.schwinge.homeip.net>,
> with some of my earlier changes "#if 0"ed in
> gcc/fortran/trans-decl.c:add_attributes_to_decl,
> libgomp.oacc-fortran/declare-3.f90 again PASSes.  But I don't understand
> (why something like) this code (isn't needed/done differently in C/C++).

There remains a lot of mystery to be resolved regarding the OpenACC
'declare' implementation...  :-(

> --- gcc/fortran/trans-decl.c
> +++ gcc/fortran/trans-decl.c
> @@ -1302,15 +1302,20 @@ add_attributes_to_decl (symbol_attribute sym_attr, tree list)
>        }
>  
>    if (sym_attr.omp_declare_target
> +#if 0 /* TODO */
>        || sym_attr.oacc_declare_create
>        || sym_attr.oacc_declare_copyin
>        || sym_attr.oacc_declare_deviceptr
> -      || sym_attr.oacc_declare_device_resident)
> +      || sym_attr.oacc_declare_device_resident
> +#endif
> +      )
>      list = tree_cons (get_identifier ("omp declare target"),
>  		      NULL_TREE, list);
> +#if 0 /* TODO */
>    if (sym_attr.oacc_declare_link)
>      list = tree_cons (get_identifier ("omp declare target link"),
>  		      NULL_TREE, list);
> +#endif

As PR85221 "[openacc] ICE in install_var_field, at omp-low.c:657" tells
us, yes, these are actually necessary.

I'm confused why not all OpenACC 'declare' clauses are handled here, but
looking into that is for another day, or week.

Committed to trunk in r272453 "[PR85221] Set 'omp declare target', 'omp
declare target link' attributes for Fortran OpenACC 'declare'd
variables", see attached.


Grüße
 Thomas



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: 0001-PR85221-Set-omp-declare-target-omp-declare-tar.trunk.patch --]
[-- Type: text/x-diff, Size: 3745 bytes --]

From b7194d24d942998da2ab8f6f5dc080e3fff81972 Mon Sep 17 00:00:00 2001
From: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Tue, 18 Jun 2019 22:15:43 +0000
Subject: [PATCH] [PR85221] Set 'omp declare target', 'omp declare target link'
 attributes for Fortran OpenACC 'declare'd variables

	gcc/fortran/
	PR fortran/85221
	* trans-decl.c (add_attributes_to_decl): Handle OpenACC 'declare'
	directive.
	gcc/testsuite/
	PR fortran/85221
	* gfortran.dg/goacc/declare-3.f95: New file.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@272453 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/fortran/ChangeLog                         |  6 +++
 gcc/fortran/trans-decl.c                      |  9 +++-
 gcc/testsuite/ChangeLog                       |  3 ++
 gcc/testsuite/gfortran.dg/goacc/declare-3.f95 | 47 +++++++++++++++++++
 4 files changed, 63 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/goacc/declare-3.f95

diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog
index d30fa2e50a88..6fd97b61ce05 100644
--- a/gcc/fortran/ChangeLog
+++ b/gcc/fortran/ChangeLog
@@ -1,3 +1,9 @@
+2019-06-18  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR fortran/85221
+	* trans-decl.c (add_attributes_to_decl): Handle OpenACC 'declare'
+	directive.
+
 2019-06-16  Thomas Koenig  <tkoenig@gcc.gnu.org>
 
 	* dump_parse_tree (debug): Add verison for formal arglist.
diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c
index b8e07274febd..f504c279c31b 100644
--- a/gcc/fortran/trans-decl.c
+++ b/gcc/fortran/trans-decl.c
@@ -1432,10 +1432,15 @@ add_attributes_to_decl (symbol_attribute sym_attr, tree list)
       list = oacc_replace_fn_attrib_attr (list, dims);
     }
 
-  if (sym_attr.omp_declare_target_link)
+  if (sym_attr.omp_declare_target_link
+      || sym_attr.oacc_declare_link)
     list = tree_cons (get_identifier ("omp declare target link"),
 		      NULL_TREE, list);
-  else if (sym_attr.omp_declare_target)
+  else if (sym_attr.omp_declare_target
+	   || sym_attr.oacc_declare_create
+	   || sym_attr.oacc_declare_copyin
+	   || sym_attr.oacc_declare_deviceptr
+	   || sym_attr.oacc_declare_device_resident)
     list = tree_cons (get_identifier ("omp declare target"),
 		      clauses, list);
 
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 59d39e8c179a..552ccc6fbd68 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,8 @@
 2019-06-18  Thomas Schwinge  <thomas@codesourcery.com>
 
+	PR fortran/85221
+	* gfortran.dg/goacc/declare-3.f95: New file.
+
 	PR middle-end/90859
 	* c-c++-common/goacc/firstprivate-mappings-1.c: Update.
 
diff --git a/gcc/testsuite/gfortran.dg/goacc/declare-3.f95 b/gcc/testsuite/gfortran.dg/goacc/declare-3.f95
new file mode 100644
index 000000000000..ec5d4c5a062a
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/goacc/declare-3.f95
@@ -0,0 +1,47 @@
+! Test valid usage of the OpenACC 'declare' directive.
+
+module mod_a
+  implicit none
+  integer :: a
+  !$acc declare create (a)
+end module
+
+module mod_b
+  implicit none
+  integer :: b
+  !$acc declare copyin (b)
+end module
+
+module mod_c
+  implicit none
+  integer :: c
+  !$acc declare deviceptr (c)
+end module
+
+module mod_d
+  implicit none
+  integer :: d
+  !$acc declare device_resident (d)
+end module
+
+module mod_e
+  implicit none
+  integer :: e
+  !$acc declare link (e)
+end module
+
+subroutine sub1
+  use mod_a
+  use mod_b
+  use mod_c
+  use mod_d
+  use mod_e
+end subroutine sub1
+
+program test
+  use mod_a
+  use mod_b
+  use mod_c
+  use mod_d
+  use mod_e
+end program test
-- 
2.20.1


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 658 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
  2015-10-27 20:20 [OpenACC] declare directive James Norris
                   ` (2 preceding siblings ...)
  2015-11-06 13:48 ` [OpenACC] declare directive James Norris
@ 2021-06-10 11:15 ` Thomas Schwinge
  3 siblings, 0 replies; 51+ messages in thread
From: Thomas Schwinge @ 2021-06-10 11:15 UTC (permalink / raw)
  To: gcc-patches; +Cc: Andrew Stubbs

[-- Attachment #1: Type: text/plain, Size: 1475 bytes --]

Hi!

While working on something else...  ;-)

On 2015-10-27T15:18:00-0500, James Norris <James_Norris@mentor.com> wrote:
> --- a/libgomp/oacc-parallel.c
> +++ b/libgomp/oacc-parallel.c

> +GOACC_declare (int device, size_t mapnum,
> +            void **hostaddrs, size_t *sizes, unsigned short *kinds)
> +{
> +  int i;
> +
> +  for (i = 0; i < mapnum; i++)
> +    {
> +      unsigned char kind = kinds[i] & 0xff;
> +
> +      if (kind == GOMP_MAP_POINTER || kind == GOMP_MAP_TO_PSET)
> +     continue;
> +
> +      switch (kind)
> +     {
> +       case GOMP_MAP_FORCE_ALLOC:
> +       case GOMP_MAP_FORCE_DEALLOC:
> +       case GOMP_MAP_FORCE_FROM:
> +       case GOMP_MAP_FORCE_TO:
> +       case GOMP_MAP_POINTER:
> +[...]

... Andrew had noticed that given that we 'continue' for
'GOMP_MAP_POINTER', we cannot possibly encounter it afterwards.

I've pushed "Clean up 'GOMP_MAP_POINTER' handling in
'libgomp/oacc-parallel.c:GOACC_declare'" to master branch in
commit ae33c6deb158911548a5f1d383b683abb799be4a, see attached.


Also, to facilitate later changes I've pushed "Move
'libgomp/oacc-parallel.c:GOACC_declare' into 'libgomp/oacc-mem.c'" to
master branch in commit 0a77c7033ae4ed05a2f7e78600522610a8d82225, see
attached.


Grüße
 Thomas


-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstrasse 201, 80634 München Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Frank Thürauf

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Clean-up-GOMP_MAP_POINTER-handling-in-libgomp-oacc-p.patch --]
[-- Type: text/x-diff, Size: 1139 bytes --]

From ae33c6deb158911548a5f1d383b683abb799be4a Mon Sep 17 00:00:00 2001
From: Andrew Stubbs <ams@codesourcery.com>
Date: Wed, 29 Jul 2020 15:05:45 +0100
Subject: [PATCH 1/2] Clean up 'GOMP_MAP_POINTER' handling in
 'libgomp/oacc-parallel.c:GOACC_declare'

Given that we 'continue' for 'GOMP_MAP_POINTER', we cannot possibly encounter
it afterwards.

Small fix-up for r230275 (commit 6e232ba4246ca324a663ec5ddf0ba4db5cf3fbad)
"[OpenACC] declare directive".

	libgomp/
	* oacc-parallel.c (GOACC_declare): Clean up 'GOMP_MAP_POINTER'
	handling.

Co-Authored-By: Thomas Schwinge <thomas@codesourcery.com>
---
 libgomp/oacc-parallel.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index cf1baf6291d..d05b3d87097 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -747,7 +747,6 @@ GOACC_declare (int flags_m, size_t mapnum,
 	  case GOMP_MAP_FORCE_ALLOC:
 	  case GOMP_MAP_FORCE_FROM:
 	  case GOMP_MAP_FORCE_TO:
-	  case GOMP_MAP_POINTER:
 	  case GOMP_MAP_RELEASE:
 	  case GOMP_MAP_DELETE:
 	    GOACC_enter_exit_data (flags_m, 1, &hostaddrs[i], &sizes[i],
-- 
2.30.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-Move-libgomp-oacc-parallel.c-GOACC_declare-into-libg.patch --]
[-- Type: text/x-diff, Size: 4675 bytes --]

From 0a77c7033ae4ed05a2f7e78600522610a8d82225 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Tue, 8 Jun 2021 17:15:19 +0200
Subject: [PATCH 2/2] Move 'libgomp/oacc-parallel.c:GOACC_declare' into
 'libgomp/oacc-mem.c'

This deals with data management, after all.

Small fix-up for r230275 (commit 6e232ba4246ca324a663ec5ddf0ba4db5cf3fbad)
"[OpenACC] declare directive".

	libgomp/
	* oacc-parallel.c (GOACC_declare): Move...
	* oacc-mem.c: ... here.
	* libgomp_g.h: Adjust.
---
 libgomp/libgomp_g.h     |  2 +-
 libgomp/oacc-mem.c      | 54 ++++++++++++++++++++++++++++++++++++++
 libgomp/oacc-parallel.c | 57 -----------------------------------------
 3 files changed, 55 insertions(+), 58 deletions(-)

diff --git a/libgomp/libgomp_g.h b/libgomp/libgomp_g.h
index 3cbe0a4ca89..b66b6978202 100644
--- a/libgomp/libgomp_g.h
+++ b/libgomp/libgomp_g.h
@@ -370,6 +370,7 @@ extern void GOACC_wait (int, int, ...);
 
 extern void GOACC_enter_exit_data (int, size_t, void **, size_t *,
 				   unsigned short *, int, int, ...);
+extern void GOACC_declare (int, size_t, void **, size_t *, unsigned short *);
 
 /* oacc-parallel.c */
 
@@ -384,6 +385,5 @@ extern void GOACC_update (int, size_t, void **, size_t *,
 			  unsigned short *, int, int, ...);
 extern int GOACC_get_num_threads (void);
 extern int GOACC_get_thread_num (void);
-extern void GOACC_declare (int, size_t, void **, size_t *, unsigned short *);
 
 #endif /* LIBGOMP_G_H */
diff --git a/libgomp/oacc-mem.c b/libgomp/oacc-mem.c
index 405574dfa2b..056600aca52 100644
--- a/libgomp/oacc-mem.c
+++ b/libgomp/oacc-mem.c
@@ -1461,3 +1461,57 @@ GOACC_enter_exit_data (int flags_m, size_t mapnum, void **hostaddrs,
       thr->api_info = NULL;
     }
 }
+
+void
+GOACC_declare (int flags_m, size_t mapnum,
+	       void **hostaddrs, size_t *sizes, unsigned short *kinds)
+{
+  for (size_t i = 0; i < mapnum; i++)
+    {
+      unsigned char kind = kinds[i] & 0xff;
+
+      if (kind == GOMP_MAP_POINTER || kind == GOMP_MAP_TO_PSET)
+	continue;
+
+      switch (kind)
+	{
+	case GOMP_MAP_FORCE_ALLOC:
+	case GOMP_MAP_FORCE_FROM:
+	case GOMP_MAP_FORCE_TO:
+	case GOMP_MAP_RELEASE:
+	case GOMP_MAP_DELETE:
+	  GOACC_enter_exit_data (flags_m, 1, &hostaddrs[i], &sizes[i],
+				 &kinds[i], GOMP_ASYNC_SYNC, 0);
+	  break;
+
+	case GOMP_MAP_FORCE_DEVICEPTR:
+	  break;
+
+	case GOMP_MAP_ALLOC:
+	  if (!acc_is_present (hostaddrs[i], sizes[i]))
+	    GOACC_enter_exit_data (flags_m, 1, &hostaddrs[i], &sizes[i],
+				   &kinds[i], GOMP_ASYNC_SYNC, 0);
+	  break;
+
+	case GOMP_MAP_TO:
+	  GOACC_enter_exit_data (flags_m, 1, &hostaddrs[i], &sizes[i],
+				 &kinds[i], GOMP_ASYNC_SYNC, 0);
+	  break;
+
+	case GOMP_MAP_FROM:
+	  GOACC_enter_exit_data (flags_m, 1, &hostaddrs[i], &sizes[i],
+				 &kinds[i], GOMP_ASYNC_SYNC, 0);
+	  break;
+
+	case GOMP_MAP_FORCE_PRESENT:
+	  if (!acc_is_present (hostaddrs[i], sizes[i]))
+	    gomp_fatal ("[%p,%ld] is not mapped", hostaddrs[i],
+			(unsigned long) sizes[i]);
+	  break;
+
+	default:
+	  assert (0);
+	  break;
+	}
+    }
+}
diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c
index d05b3d87097..939e09f2b0c 100644
--- a/libgomp/oacc-parallel.c
+++ b/libgomp/oacc-parallel.c
@@ -728,60 +728,3 @@ GOACC_get_thread_num (void)
 {
   return 0;
 }
-
-void
-GOACC_declare (int flags_m, size_t mapnum,
-	       void **hostaddrs, size_t *sizes, unsigned short *kinds)
-{
-  int i;
-
-  for (i = 0; i < mapnum; i++)
-    {
-      unsigned char kind = kinds[i] & 0xff;
-
-      if (kind == GOMP_MAP_POINTER || kind == GOMP_MAP_TO_PSET)
-	continue;
-
-      switch (kind)
-	{
-	  case GOMP_MAP_FORCE_ALLOC:
-	  case GOMP_MAP_FORCE_FROM:
-	  case GOMP_MAP_FORCE_TO:
-	  case GOMP_MAP_RELEASE:
-	  case GOMP_MAP_DELETE:
-	    GOACC_enter_exit_data (flags_m, 1, &hostaddrs[i], &sizes[i],
-				   &kinds[i], GOMP_ASYNC_SYNC, 0);
-	    break;
-
-	  case GOMP_MAP_FORCE_DEVICEPTR:
-	    break;
-
-	  case GOMP_MAP_ALLOC:
-	    if (!acc_is_present (hostaddrs[i], sizes[i]))
-	      GOACC_enter_exit_data (flags_m, 1, &hostaddrs[i], &sizes[i],
-				     &kinds[i], GOMP_ASYNC_SYNC, 0);
-	    break;
-
-	  case GOMP_MAP_TO:
-	    GOACC_enter_exit_data (flags_m, 1, &hostaddrs[i], &sizes[i],
-				   &kinds[i], GOMP_ASYNC_SYNC, 0);
-
-	    break;
-
-	  case GOMP_MAP_FROM:
-	    GOACC_enter_exit_data (flags_m, 1, &hostaddrs[i], &sizes[i],
-				   &kinds[i], GOMP_ASYNC_SYNC, 0);
-	    break;
-
-	  case GOMP_MAP_FORCE_PRESENT:
-	    if (!acc_is_present (hostaddrs[i], sizes[i]))
-	      gomp_fatal ("[%p,%ld] is not mapped", hostaddrs[i],
-			  (unsigned long) sizes[i]);
-	    break;
-
-	  default:
-	    assert (0);
-	    break;
-	}
-    }
-}
-- 
2.30.2


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [OpenACC] declare directive
@ 2015-11-11 19:43 Dominique d'Humières
  0 siblings, 0 replies; 51+ messages in thread
From: Dominique d'Humières @ 2015-11-11 19:43 UTC (permalink / raw)
  To: thomas; +Cc: jakub Jelinek, gcc-patches

> > "Would be really nice" means that you're asking us to work on and resolve
> > PR68271 before installing this patch?
>
> Dominique has committed a quick hack for this, so it is not urgent, but
> would be nice to get it resolved.  If somebody from Mentor gets to that,
> perfect, otherwise I (or somebody else) will get to that eventually.
>
> 	Jakub
Could you please mail me any related patch before committing it? I prefer to see any possible problem before the commit rather than after.

TIA

Dominique

^ permalink raw reply	[flat|nested] 51+ messages in thread

end of thread, other threads:[~2021-06-10 11:15 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-10-27 20:20 [OpenACC] declare directive James Norris
2015-10-28 16:33 ` Cesar Philippidis
2015-10-28 16:33   ` James Norris
2015-11-03 16:31 ` James Norris
2015-11-04 16:49   ` Thomas Schwinge
2015-11-04 17:12     ` James Norris
2015-11-06 16:08     ` James Norris
2015-11-06 16:16       ` James Norris
2015-11-06 19:04       ` Jakub Jelinek
2015-11-06 19:23         ` Jakub Jelinek
2015-11-06 20:18           ` James Norris
2015-11-06 20:28             ` Jakub Jelinek
2015-11-06 20:35               ` James Norris
2015-11-06 20:16         ` James Norris
2015-11-08 15:35         ` James Norris
2015-11-09 16:01           ` James Norris
2015-11-09 16:21             ` Jakub Jelinek
2015-11-09 16:31               ` James Norris
2015-11-09 23:11               ` James Norris
2015-11-11  8:32                 ` Jakub Jelinek
2015-11-11 10:08                   ` Thomas Schwinge
2015-11-11 17:29                     ` Jakub Jelinek
2015-11-12  1:08                   ` James Norris
2015-11-12  9:10                     ` Jakub Jelinek
2015-11-12 13:34                       ` James Norris
2015-11-23 12:41                         ` Thomas Schwinge
2015-11-24  8:45                           ` [gomp4] " Thomas Schwinge
2015-11-12 23:25                             ` [PATCH] Fix unused variable James Norris
2015-11-22 19:11                               ` [PATCH] fortran/openmp.c -- Fix bootstrap Steve Kargl
2015-11-22 19:27                                 ` Jerry DeLisle
2015-11-22 20:38                                 ` James Norris
2019-06-18 22:45                           ` [committed] Fix description of 'GOMP_MAP_FIRSTPRIVATE' (was: [OpenACC] declare directive) Thomas Schwinge
2019-06-18 22:21                     ` [committed] [PR90862] OpenACC 'declare' ICE when nested inside another construct " Thomas Schwinge
2015-11-06 13:48 ` [OpenACC] declare directive James Norris
2021-06-10 11:15 ` Thomas Schwinge
2015-11-02 13:46 OpenACC declare directive updates James Norris
2015-11-04 12:32 ` James Norris
2015-11-06 13:46   ` James Norris
2015-11-06 19:31   ` Jakub Jelinek
2015-11-06 19:45     ` James Norris
2015-11-06 19:49       ` Jakub Jelinek
2015-11-06 20:20         ` James Norris
2015-11-19 16:22         ` James Norris
2015-11-20 10:53           ` Jakub Jelinek
2015-11-27 11:41           ` [gomp4] " Thomas Schwinge
2019-06-18 23:02             ` [committed] [PR85221] Set 'omp declare target', 'omp declare target link' attributes for Fortran OpenACC 'declare'd variables (was: [gomp4] Re: OpenACC declare directive updates) Thomas Schwinge
2015-11-08 15:29       ` OpenACC declare directive updates James Norris
2015-11-09  2:30         ` Cesar Philippidis
2015-11-09  4:53           ` James Norris
2015-11-18 20:09             ` Cesar Philippidis
2015-11-11 19:43 [OpenACC] declare directive Dominique d'Humières

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).