+2015-12-15  Ilya Verbin  <ilya.verbin@intel.com>
+
+       * cgraphunit.c (output_in_order): Do not assemble "omp declare target
+       link" variables in ACCEL_COMPILER.
+       * gimplify.c (gimplify_adjust_omp_clauses): Do not remove mapping of
+       "omp declare target link" variables.
+       * omp-low.c (scan_sharing_clauses): Do not remove mapping of "omp
+       declare target link" variables.
+       (add_decls_addresses_to_decl_constructor): For "omp declare target link"
+       variables output address of the artificial pointer instead of address of
+       the variable.  Set most significant bit of the size to mark them.
+       (pass_data_omp_target_link): New pass_data.
+       (pass_omp_target_link): New class.
+       (find_link_var_op): New static function.
+       (make_pass_omp_target_link): New function.
+       * passes.def: Add pass_omp_target_link.
+       * tree-pass.h (make_pass_omp_target_link): Declare.
+       * varpool.c (symbol_table::output_variables): Do not assemble "omp
+       declare target link" variables in ACCEL_COMPILER.
+
 2015-12-15  Bernd Schmidt  <bschmidt@redhat.com>
 
        PR middle-end/21273
 
+2015-12-15  Ilya Verbin  <ilya.verbin@intel.com>
+
+       * c-common.c (c_common_attribute_table): Handle "omp declare target
+       link" attribute.
+
 2015-12-14  Jakub Jelinek  <jakub@redhat.com>
 
        PR c/68833
 
                              handle_simd_attribute, false },
   { "omp declare target",     0, 0, true, false, false,
                              handle_omp_declare_target_attribute, false },
+  { "omp declare target link", 0, 0, true, false, false,
+                             handle_omp_declare_target_attribute, false },
   { "alloc_align",           1, 1, false, true, true,
                              handle_alloc_align_attribute, false },
   { "assume_aligned",        1, 2, false, true, true,
 
          break;
 
        case ORDER_VAR:
+#ifdef ACCEL_COMPILER
+         /* Do not assemble "omp declare target link" vars.  */
+         if (DECL_HAS_VALUE_EXPR_P (nodes[i].u.v->decl)
+             && lookup_attribute ("omp declare target link",
+                                  DECL_ATTRIBUTES (nodes[i].u.v->decl)))
+           break;
+#endif
          nodes[i].u.v->assemble_decl ();
          break;
 
 
          n = splay_tree_lookup (ctx->variables, (splay_tree_key) decl);
          if ((ctx->region_type & ORT_TARGET) != 0
              && !(n->value & GOVD_SEEN)
-             && GOMP_MAP_ALWAYS_P (OMP_CLAUSE_MAP_KIND (c)) == 0)
+             && GOMP_MAP_ALWAYS_P (OMP_CLAUSE_MAP_KIND (c)) == 0
+             && !lookup_attribute ("omp declare target link",
+                                   DECL_ATTRIBUTES (decl)))
            {
              remove = true;
              /* For struct element mapping, if struct is never referenced
 
+2015-12-15  Ilya Verbin  <ilya.verbin@intel.com>
+
+       * lto.c: Include stringpool.h and fold-const.h.
+       (offload_handle_link_vars): New static function.
+       (lto_main): Call offload_handle_link_vars.
+
 2015-12-10  Jan Hubicka  <hubicka@ucw.cz>
 
        * lto.c (lto_read_in_decl_state): Unpickle compressed bit.
 
 #include "ipa-utils.h"
 #include "gomp-constants.h"
 #include "lto-symtab.h"
+#include "stringpool.h"
+#include "fold-const.h"
 
 
 /* Number of parallel tasks to run, -1 if we want to use GNU Make jobserver.  */
 #endif
 }
 
+/* Create artificial pointers for "omp declare target link" vars.  */
+
+static void
+offload_handle_link_vars (void)
+{
+#ifdef ACCEL_COMPILER
+  varpool_node *var;
+  FOR_EACH_VARIABLE (var)
+    if (lookup_attribute ("omp declare target link",
+                         DECL_ATTRIBUTES (var->decl)))
+      {
+       tree type = build_pointer_type (TREE_TYPE (var->decl));
+       tree link_ptr_var = make_node (VAR_DECL);
+       TREE_TYPE (link_ptr_var) = type;
+       TREE_USED (link_ptr_var) = 1;
+       TREE_STATIC (link_ptr_var) = 1;
+       DECL_MODE (link_ptr_var) = TYPE_MODE (type);
+       DECL_SIZE (link_ptr_var) = TYPE_SIZE (type);
+       DECL_SIZE_UNIT (link_ptr_var) = TYPE_SIZE_UNIT (type);
+       DECL_ARTIFICIAL (link_ptr_var) = 1;
+       tree var_name = DECL_ASSEMBLER_NAME (var->decl);
+       char *new_name
+         = ACONCAT ((IDENTIFIER_POINTER (var_name), "_linkptr", NULL));
+       DECL_NAME (link_ptr_var) = get_identifier (new_name);
+       SET_DECL_ASSEMBLER_NAME (link_ptr_var, DECL_NAME (link_ptr_var));
+       SET_DECL_VALUE_EXPR (var->decl, build_simple_mem_ref (link_ptr_var));
+       DECL_HAS_VALUE_EXPR_P (var->decl) = 1;
+      }
+#endif
+}
+
 
 /* Main entry point for the GIMPLE front end.  This front end has
    three main personalities:
 
   if (!seen_error ())
     {
+      offload_handle_link_vars ();
+
       /* If WPA is enabled analyze the whole call graph and create an
         optimization plan.  Otherwise, read in all the function
         bodies and continue with optimization.  */
 
          decl = OMP_CLAUSE_DECL (c);
          /* Global variables with "omp declare target" attribute
             don't need to be copied, the receiver side will use them
-            directly.  */
+            directly.  However, global variables with "omp declare target link"
+            attribute need to be copied.  */
          if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
              && DECL_P (decl)
              && ((OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FIRSTPRIVATE_POINTER
                       != GOMP_MAP_FIRSTPRIVATE_REFERENCE))
                  || TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
              && is_global_var (maybe_lookup_decl_in_outer_ctx (decl, ctx))
-             && varpool_node::get_create (decl)->offloadable)
+             && varpool_node::get_create (decl)->offloadable
+             && !lookup_attribute ("omp declare target link",
+                                   DECL_ATTRIBUTES (decl)))
            break;
          if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
              && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER)
   for (unsigned i = 0; i < len; i++)
     {
       tree it = (*v_decls)[i];
-      bool is_function = TREE_CODE (it) != VAR_DECL;
+      bool is_var = TREE_CODE (it) == VAR_DECL;
+      bool is_link_var
+       = is_var
+#ifdef ACCEL_COMPILER
+         && DECL_HAS_VALUE_EXPR_P (it)
+#endif
+         && lookup_attribute ("omp declare target link", DECL_ATTRIBUTES (it));
 
-      CONSTRUCTOR_APPEND_ELT (v_ctor, NULL_TREE, build_fold_addr_expr (it));
-      if (!is_function)
-       CONSTRUCTOR_APPEND_ELT (v_ctor, NULL_TREE,
-                               fold_convert (const_ptr_type_node,
-                                             DECL_SIZE_UNIT (it)));
+      tree size = NULL_TREE;
+      if (is_var)
+       size = fold_convert (const_ptr_type_node, DECL_SIZE_UNIT (it));
+
+      tree addr;
+      if (!is_link_var)
+       addr = build_fold_addr_expr (it);
+      else
+       {
+#ifdef ACCEL_COMPILER
+         /* For "omp declare target link" vars add address of the pointer to
+            the target table, instead of address of the var.  */
+         tree value_expr = DECL_VALUE_EXPR (it);
+         tree link_ptr_decl = TREE_OPERAND (value_expr, 0);
+         varpool_node::finalize_decl (link_ptr_decl);
+         addr = build_fold_addr_expr (link_ptr_decl);
+#else
+         addr = build_fold_addr_expr (it);
+#endif
+
+         /* Most significant bit of the size marks "omp declare target link"
+            vars in host and target tables.  */
+         unsigned HOST_WIDE_INT isize = tree_to_uhwi (size);
+         isize |= 1ULL << (int_size_in_bytes (const_ptr_type_node)
+                           * BITS_PER_UNIT - 1);
+         size = wide_int_to_tree (const_ptr_type_node, isize);
+       }
+
+      CONSTRUCTOR_APPEND_ELT (v_ctor, NULL_TREE, addr);
+      if (is_var)
+       CONSTRUCTOR_APPEND_ELT (v_ctor, NULL_TREE, size);
     }
 }
 
   return new pass_oacc_device_lower (ctxt);
 }
 
+/* "omp declare target link" handling pass.  */
+
+namespace {
+
+const pass_data pass_data_omp_target_link =
+{
+  GIMPLE_PASS,                 /* type */
+  "omptargetlink",             /* name */
+  OPTGROUP_NONE,               /* optinfo_flags */
+  TV_NONE,                     /* tv_id */
+  PROP_ssa,                    /* properties_required */
+  0,                           /* properties_provided */
+  0,                           /* properties_destroyed */
+  0,                           /* todo_flags_start */
+  TODO_update_ssa,             /* todo_flags_finish */
+};
+
+class pass_omp_target_link : public gimple_opt_pass
+{
+public:
+  pass_omp_target_link (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_omp_target_link, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *fun)
+    {
+#ifdef ACCEL_COMPILER
+      tree attrs = DECL_ATTRIBUTES (fun->decl);
+      return lookup_attribute ("omp declare target", attrs)
+            || lookup_attribute ("omp target entrypoint", attrs);
+#else
+      (void) fun;
+      return false;
+#endif
+    }
+
+  virtual unsigned execute (function *);
+};
+
+/* Callback for walk_gimple_stmt used to scan for link var operands.  */
+
+static tree
+find_link_var_op (tree *tp, int *walk_subtrees, void *)
+{
+  tree t = *tp;
+
+  if (TREE_CODE (t) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (t)
+      && lookup_attribute ("omp declare target link", DECL_ATTRIBUTES (t)))
+    {
+      *walk_subtrees = 0;
+      return t;
+    }
+
+  return NULL_TREE;
+}
+
+unsigned
+pass_omp_target_link::execute (function *fun)
+{
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, fun)
+    {
+      gimple_stmt_iterator gsi;
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+       if (walk_gimple_stmt (&gsi, NULL, find_link_var_op, NULL))
+         gimple_regimplify_operands (gsi_stmt (gsi), &gsi);
+    }
+
+  return 0;
+}
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_omp_target_link (gcc::context *ctxt)
+{
+  return new pass_omp_target_link (ctxt);
+}
+
 #include "gt-omp-low.h"
 
   NEXT_PASS (pass_fixup_cfg);
   NEXT_PASS (pass_lower_eh_dispatch);
   NEXT_PASS (pass_oacc_device_lower);
+  NEXT_PASS (pass_omp_target_link);
   NEXT_PASS (pass_all_optimizations);
   PUSH_INSERT_PASSES_WITHIN (pass_all_optimizations)
       NEXT_PASS (pass_remove_cgraph_callee_edges);
 
 extern gimple_opt_pass *make_pass_diagnose_omp_blocks (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_expand_omp (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_expand_omp_ssa (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_omp_target_link (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_oacc_device_lower (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_object_sizes (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_strlen (gcc::context *ctxt);
 
       /* Handled in output_in_order.  */
       if (node->no_reorder)
        continue;
+#ifdef ACCEL_COMPILER
+      /* Do not assemble "omp declare target link" vars.  */
+      if (DECL_HAS_VALUE_EXPR_P (node->decl)
+         && lookup_attribute ("omp declare target link",
+                              DECL_ATTRIBUTES (node->decl)))
+       continue;
+#endif
       if (node->assemble_decl ())
         changed = true;
     }
 
+2015-12-15  Ilya Verbin  <ilya.verbin@intel.com>
+
+       * libgomp.h (REFCOUNT_LINK): Define.
+       (struct splay_tree_key_s): Add link_key.
+       * target.c (gomp_map_vars): Treat REFCOUNT_LINK objects as not mapped.
+       Replace target address of the pointer with target address of newly
+       mapped object in the splay tree.  Set link pointer on target to the
+       device address of the mapped object.
+       (gomp_unmap_vars): Restore target address of the pointer in the splay
+       tree for REFCOUNT_LINK objects after unmapping.
+       (gomp_load_image_to_device): Set refcount to REFCOUNT_LINK for "omp
+       declare target link" objects.
+       (gomp_unload_image_from_device): Replace j with i.  Force unmap of all
+       "omp declare target link" objects, which were mapped for the image.
+       (gomp_exit_data): Restore target address of the pointer in the splay
+       tree for REFCOUNT_LINK objects after unmapping.
+       * testsuite/libgomp.c/target-link-1.c: New file.
+
 2015-12-14  Ilya Verbin  <ilya.verbin@intel.com>
 
        * libgomp.h (gomp_device_state): New enum.
 
 
 /* Special value for refcount - infinity.  */
 #define REFCOUNT_INFINITY (~(uintptr_t) 0)
+/* Special value for refcount - tgt_offset contains target address of the
+   artificial pointer to "omp declare target link" object.  */
+#define REFCOUNT_LINK (~(uintptr_t) 1)
 
 struct splay_tree_key_s {
   /* Address of the host object.  */
   uintptr_t refcount;
   /* Asynchronous reference count.  */
   uintptr_t async_refcount;
+  /* Pointer to the original mapping of "omp declare target link" object.  */
+  splay_tree_key link_key;
 };
 
 /* The comparison function.  */
 
        }
       else
        n = splay_tree_lookup (mem_map, &cur_node);
-      if (n)
+      if (n && n->refcount != REFCOUNT_LINK)
        gomp_map_vars_existing (devicep, n, &cur_node, &tgt->list[i],
                                kind & typemask);
       else
            else
              k->host_end = k->host_start + sizeof (void *);
            splay_tree_key n = splay_tree_lookup (mem_map, k);
-           if (n)
+           if (n && n->refcount != REFCOUNT_LINK)
              gomp_map_vars_existing (devicep, n, k, &tgt->list[i],
                                      kind & typemask);
            else
              {
+               k->link_key = NULL;
+               if (n && n->refcount == REFCOUNT_LINK)
+                 {
+                   /* Replace target address of the pointer with target address
+                      of mapped object in the splay tree.  */
+                   splay_tree_remove (mem_map, n);
+                   k->link_key = n;
+                 }
                size_t align = (size_t) 1 << (kind >> rshift);
                tgt->list[i].key = k;
                k->tgt = tgt;
                    gomp_fatal ("%s: unhandled kind 0x%.2x", __FUNCTION__,
                                kind);
                  }
+
+               if (k->link_key)
+                 {
+                   /* Set link pointer on target to the device address of the
+                      mapped object.  */
+                   void *tgt_addr = (void *) (tgt->tgt_start + k->tgt_offset);
+                   devicep->host2dev_func (devicep->target_id,
+                                           (void *) n->tgt_offset,
+                                           &tgt_addr, sizeof (void *));
+                 }
                array++;
              }
          }
       if (do_unmap)
        {
          splay_tree_remove (&devicep->mem_map, k);
+         if (k->link_key)
+           splay_tree_insert (&devicep->mem_map,
+                              (splay_tree_node) k->link_key);
          if (k->tgt->refcount > 1)
            k->tgt->refcount--;
          else
       k->tgt_offset = target_table[i].start;
       k->refcount = REFCOUNT_INFINITY;
       k->async_refcount = 0;
+      k->link_key = NULL;
       array->left = NULL;
       array->right = NULL;
       splay_tree_insert (&devicep->mem_map, array);
       array++;
     }
 
+  /* Most significant bit of the size in host and target tables marks
+     "omp declare target link" variables.  */
+  const uintptr_t link_bit = 1ULL << (sizeof (uintptr_t) * __CHAR_BIT__ - 1);
+  const uintptr_t size_mask = ~link_bit;
+
   for (i = 0; i < num_vars; i++)
     {
       struct addr_pair *target_var = &target_table[num_funcs + i];
-      if (target_var->end - target_var->start
-         != (uintptr_t) host_var_table[i * 2 + 1])
+      uintptr_t target_size = target_var->end - target_var->start;
+
+      if ((uintptr_t) host_var_table[i * 2 + 1] != target_size)
        {
          gomp_mutex_unlock (&devicep->lock);
          if (is_register_lock)
            gomp_mutex_unlock (®ister_lock);
-         gomp_fatal ("Can't map target variables (size mismatch)");
+         gomp_fatal ("Cannot map target variables (size mismatch)");
        }
 
       splay_tree_key k = &array->key;
       k->host_start = (uintptr_t) host_var_table[i * 2];
-      k->host_end = k->host_start + (uintptr_t) host_var_table[i * 2 + 1];
+      k->host_end
+       = k->host_start + (size_mask & (uintptr_t) host_var_table[i * 2 + 1]);
       k->tgt = tgt;
       k->tgt_offset = target_var->start;
-      k->refcount = REFCOUNT_INFINITY;
+      k->refcount = target_size & link_bit ? REFCOUNT_LINK : REFCOUNT_INFINITY;
       k->async_refcount = 0;
+      k->link_key = NULL;
       array->left = NULL;
       array->right = NULL;
       splay_tree_insert (&devicep->mem_map, array);
   int num_funcs = host_funcs_end - host_func_table;
   int num_vars  = (host_vars_end - host_var_table) / 2;
 
-  unsigned j;
   struct splay_tree_key_s k;
   splay_tree_key node = NULL;
 
   devicep->unload_image_func (devicep->target_id, version, target_data);
 
   /* Remove mappings from splay tree.  */
-  for (j = 0; j < num_funcs; j++)
+  int i;
+  for (i = 0; i < num_funcs; i++)
     {
-      k.host_start = (uintptr_t) host_func_table[j];
+      k.host_start = (uintptr_t) host_func_table[i];
       k.host_end = k.host_start + 1;
       splay_tree_remove (&devicep->mem_map, &k);
     }
 
-  for (j = 0; j < num_vars; j++)
+  /* Most significant bit of the size in host and target tables marks
+     "omp declare target link" variables.  */
+  const uintptr_t link_bit = 1ULL << (sizeof (uintptr_t) * __CHAR_BIT__ - 1);
+  const uintptr_t size_mask = ~link_bit;
+  bool is_tgt_unmapped = false;
+
+  for (i = 0; i < num_vars; i++)
     {
-      k.host_start = (uintptr_t) host_var_table[j * 2];
-      k.host_end = k.host_start + (uintptr_t) host_var_table[j * 2 + 1];
-      splay_tree_remove (&devicep->mem_map, &k);
+      k.host_start = (uintptr_t) host_var_table[i * 2];
+      k.host_end
+       = k.host_start + (size_mask & (uintptr_t) host_var_table[i * 2 + 1]);
+
+      if (!(link_bit & (uintptr_t) host_var_table[i * 2 + 1]))
+       splay_tree_remove (&devicep->mem_map, &k);
+      else
+       {
+         splay_tree_key n = splay_tree_lookup (&devicep->mem_map, &k);
+         splay_tree_remove (&devicep->mem_map, n);
+         if (n->link_key)
+           {
+             if (n->tgt->refcount > 1)
+               n->tgt->refcount--;
+             else
+               {
+                 is_tgt_unmapped = true;
+                 gomp_unmap_tgt (n->tgt);
+               }
+           }
+       }
     }
 
-  if (node)
+  if (node && !is_tgt_unmapped)
     {
       free (node->tgt);
       free (node);
          if (k->refcount == 0)
            {
              splay_tree_remove (&devicep->mem_map, k);
+             if (k->link_key)
+               splay_tree_insert (&devicep->mem_map,
+                                  (splay_tree_node) k->link_key);
              if (k->tgt->refcount > 1)
                k->tgt->refcount--;
              else
 
--- /dev/null
+struct S { int s, t; };
+
+int a = 1, b = 1;
+double c[27];
+struct S d = { 8888, 8888 };
+#pragma omp declare target link (a) to (b) link (c, d)
+
+int
+foo (void)
+{
+  return a++ + b++;
+}
+
+int
+bar (int n)
+{
+  int *p1 = &a;
+  int *p2 = &b;
+  c[n] += 2.0;
+  d.s -= 2;
+  d.t -= 2;
+  return *p1 + *p2 + d.s + d.t;
+}
+
+#pragma omp declare target (foo, bar)
+
+int
+main ()
+{
+  a = b = 2;
+  d.s = 17;
+  d.t = 18;
+
+  int res, n = 10;
+  #pragma omp target map (to: a, b, c, d) map (from: res)
+  {
+    res = foo () + foo ();
+    c[n] = 3.0;
+    res += bar (n);
+  }
+
+  int shared_mem = 0;
+  #pragma omp target map (alloc: shared_mem)
+    shared_mem = 1;
+
+  if ((shared_mem && res != (2 + 2) + (3 + 3) + (4 + 4 + 15 + 16))
+      || (!shared_mem && res != (2 + 1) + (3 + 2) + (4 + 3 + 15 + 16)))
+    __builtin_abort ();
+
+  #pragma omp target enter data map (to: c)
+  #pragma omp target update from (c)
+  res = (int) (c[n] + 0.5);
+  if ((shared_mem && res != 5) || (!shared_mem && res != 0))
+    __builtin_abort ();
+
+  #pragma omp target map (to: a, b) map (from: res)
+    res = foo ();
+
+  if ((shared_mem && res != 4 + 4) || (!shared_mem && res != 2 + 3))
+    __builtin_abort ();
+
+  return 0;
+}