Do not describe -std=c11 etc. as experimental in c.opt help text.
[gcc.git] / gcc / omp-low.c
index aa2a598238d0a19eae76f14e76121c3db8e06620..cdcf9d6d9c8477cef406a5c70c1e07c867f93ec9 100644 (file)
@@ -82,7 +82,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "lto-section-names.h"
 #include "gomp-constants.h"
 
-
 /* Lowering of OMP parallel and workshare constructs proceeds in two
    phases.  The first phase scans the function looking for OMP statements
    and then for variables that must be replaced to satisfy data sharing
@@ -152,7 +151,7 @@ struct omp_context
 
   /* The tree of contexts corresponding to the encountered constructs.  */
   struct omp_context *outer;
-  gimple stmt;
+  gimple *stmt;
 
   /* Map variables to fields in a structure that allows communication
      between sending and receiving threads.  */
@@ -734,7 +733,7 @@ static bool
 workshare_safe_to_combine_p (basic_block ws_entry_bb)
 {
   struct omp_for_data fd;
-  gimple ws_stmt = last_stmt (ws_entry_bb);
+  gimple *ws_stmt = last_stmt (ws_entry_bb);
 
   if (gimple_code (ws_stmt) == GIMPLE_OMP_SECTIONS)
     return true;
@@ -768,7 +767,7 @@ workshare_safe_to_combine_p (basic_block ws_entry_bb)
    expanded.  */
 
 static vec<tree, va_gc> *
-get_ws_args_for (gimple par_stmt, gimple ws_stmt)
+get_ws_args_for (gimple *par_stmt, gimple *ws_stmt)
 {
   tree t;
   location_t loc = gimple_location (ws_stmt);
@@ -864,8 +863,8 @@ determine_parallel_type (struct omp_region *region)
          || (last_and_only_stmt (ws_entry_bb)
              && last_and_only_stmt (par_exit_bb))))
     {
-      gimple par_stmt = last_stmt (par_entry_bb);
-      gimple ws_stmt = last_stmt (ws_entry_bb);
+      gimple *par_stmt = last_stmt (par_entry_bb);
+      gimple *ws_stmt = last_stmt (ws_entry_bb);
 
       if (region->inner->type == GIMPLE_OMP_FOR)
        {
@@ -1469,7 +1468,7 @@ free_omp_regions (void)
 /* Create a new context, with OUTER_CTX being the surrounding context.  */
 
 static omp_context *
-new_omp_context (gimple stmt, omp_context *outer_ctx)
+new_omp_context (gimple *stmt, omp_context *outer_ctx)
 {
   omp_context *ctx = XCNEW (omp_context);
 
@@ -2238,7 +2237,7 @@ find_combined_for (gimple_stmt_iterator *gsi_p,
                   bool *handled_ops_p,
                   struct walk_stmt_info *wi)
 {
-  gimple stmt = gsi_stmt (*gsi_p);
+  gimple *stmt = gsi_stmt (*gsi_p);
 
   *handled_ops_p = true;
   switch (gimple_code (stmt))
@@ -2289,7 +2288,7 @@ scan_omp_parallel (gimple_stmt_iterator *gsi, omp_context *outer_ctx)
                       find_combined_for, NULL, &wi);
       if (wi.info)
        {
-         gomp_for *for_stmt = as_a <gomp_for *> ((gimple) wi.info);
+         gomp_for *for_stmt = as_a <gomp_for *> ((gimple *) wi.info);
          struct omp_for_data fd;
          extract_omp_for_data (for_stmt, &fd, NULL);
          /* We need two temporaries with fd.loop.v type (istart/iend)
@@ -2500,7 +2499,7 @@ enclosing_target_ctx (omp_context *ctx)
 }
 
 static bool
-oacc_loop_or_target_p (gimple stmt)
+oacc_loop_or_target_p (gimple *stmt)
 {
   enum gimple_code outer_type = gimple_code (stmt);
   return ((outer_type == GIMPLE_OMP_TARGET
@@ -2708,7 +2707,7 @@ scan_omp_teams (gomp_teams *stmt, omp_context *outer_ctx)
 
 /* Check nesting restrictions.  */
 static bool
-check_omp_nesting_restrictions (gimple stmt, omp_context *ctx)
+check_omp_nesting_restrictions (gimple *stmt, omp_context *ctx)
 {
   /* No nesting of non-OpenACC STMT (that is, an OpenMP one, or a GOMP builtin)
      inside an OpenACC CTX.  */
@@ -3134,7 +3133,7 @@ static tree
 scan_omp_1_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
                 struct walk_stmt_info *wi)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
   omp_context *ctx = (omp_context *) wi->info;
 
   if (gimple_has_location (stmt))
@@ -3270,7 +3269,7 @@ scan_omp (gimple_seq *body_p, omp_context *ctx)
 
 /* Build a call to GOMP_barrier.  */
 
-static gimple
+static gimple *
 build_omp_barrier (tree lhs)
 {
   tree fndecl = builtin_decl_explicit (lhs ? BUILT_IN_GOMP_BARRIER_CANCEL
@@ -3284,7 +3283,7 @@ build_omp_barrier (tree lhs)
 /* If a context was created for STMT when it was scanned, return it.  */
 
 static omp_context *
-maybe_lookup_ctx (gimple stmt)
+maybe_lookup_ctx (gimple *stmt)
 {
   splay_tree_node n;
   n = splay_tree_lookup (all_contexts, (splay_tree_key) stmt);
@@ -3372,13 +3371,12 @@ maybe_lookup_decl_in_outer_ctx (tree decl, omp_context *ctx)
 }
 
 
-/* Construct the initialization value for reduction CLAUSE.  */
+/* Construct the initialization value for reduction operation OP.  */
 
 tree
-omp_reduction_init (tree clause, tree type)
+omp_reduction_init_op (location_t loc, enum tree_code op, tree type)
 {
-  location_t loc = OMP_CLAUSE_LOCATION (clause);
-  switch (OMP_CLAUSE_REDUCTION_CODE (clause))
+  switch (op)
     {
     case PLUS_EXPR:
     case MINUS_EXPR:
@@ -3451,6 +3449,15 @@ omp_reduction_init (tree clause, tree type)
     }
 }
 
+/* Construct the initialization value for reduction CLAUSE.  */
+
+tree
+omp_reduction_init (tree clause, tree type)
+{
+  return omp_reduction_init_op (OMP_CLAUSE_LOCATION (clause),
+                               OMP_CLAUSE_REDUCTION_CODE (clause), type);
+}
+
 /* Return alignment to be assumed for var in CLAUSE, which should be
    OMP_CLAUSE_ALIGNED.  */
 
@@ -4054,7 +4061,7 @@ lower_rec_input_clauses (tree clauses, gimple_seq *ilist, gimple_seq *dlist,
              if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c))
                {
                  tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c);
-                 gimple tseq;
+                 gimple *tseq;
                  x = build_outer_var_ref (var, ctx);
 
                  if (is_reference (var)
@@ -4212,7 +4219,7 @@ lower_rec_input_clauses (tree clauses, gimple_seq *ilist, gimple_seq *dlist,
       /* Don't want uninit warnings on simduid, it is always uninitialized,
         but we use it not for the value, but for the DECL_UID only.  */
       TREE_NO_WARNING (uid) = 1;
-      gimple g
+      gimple *g
        = gimple_build_call_internal (IFN_GOMP_SIMD_LANE, 1, uid);
       gimple_call_set_lhs (g, lane);
       gimple_stmt_iterator gsi = gsi_start_1 (gimple_omp_body_ptr (ctx->stmt));
@@ -4457,7 +4464,7 @@ oacc_lower_reduction_var_helper (gimple_seq *stmt_seqp, omp_context *ctx,
 
   tree t = NULL_TREE, array, x;
   tree type = get_base_type (var);
-  gimple stmt;
+  gimple *stmt;
 
   /* Now insert the partial reductions into the array.  */
 
@@ -4503,7 +4510,7 @@ static void
 lower_reduction_clauses (tree clauses, gimple_seq *stmt_seqp, omp_context *ctx)
 {
   gimple_seq sub_seq = NULL;
-  gimple stmt;
+  gimple *stmt;
   tree x, c, tid = NULL_TREE;
   int count = 0;
 
@@ -4535,7 +4542,7 @@ lower_reduction_clauses (tree clauses, gimple_seq *stmt_seqp, omp_context *ctx)
       /* Get the current thread id.  */
       tree call = builtin_decl_explicit (BUILT_IN_GOACC_GET_THREAD_NUM);
       tid = create_tmp_var (TREE_TYPE (TREE_TYPE (call)));
-      gimple stmt = gimple_build_call (call, 0);
+      gimple *stmt = gimple_build_call (call, 0);
       gimple_call_set_lhs (stmt, tid);
       gimple_seq_add_stmt (stmt_seqp, stmt);
     }
@@ -4837,7 +4844,7 @@ expand_parallel_call (struct omp_region *region, basic_block bb,
 {
   tree t, t1, t2, val, cond, c, clauses, flags;
   gimple_stmt_iterator gsi;
-  gimple stmt;
+  gimple *stmt;
   enum built_in_function start_ix;
   int start_ix2;
   location_t clause_loc;
@@ -5100,7 +5107,7 @@ expand_task_call (basic_block bb, gomp_task *entry_stmt)
 static gimple_seq
 maybe_catch_exception (gimple_seq body)
 {
-  gimple g;
+  gimple *g;
   tree decl;
 
   if (!flag_exceptions)
@@ -5149,7 +5156,7 @@ remove_exit_barrier (struct omp_region *region)
   basic_block exit_bb;
   edge_iterator ei;
   edge e;
-  gimple stmt;
+  gimple *stmt;
   int any_addressable_vars = -1;
 
   exit_bb = region->exit;
@@ -5253,7 +5260,7 @@ remove_exit_barriers (struct omp_region *region)
    scheduling point.  */
 
 static void
-optimize_omp_library_calls (gimple entry_stmt)
+optimize_omp_library_calls (gimple *entry_stmt)
 {
   basic_block bb;
   gimple_stmt_iterator gsi;
@@ -5268,7 +5275,7 @@ optimize_omp_library_calls (gimple entry_stmt)
   FOR_EACH_BB_FN (bb, cfun)
     for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
       {
-       gimple call = gsi_stmt (gsi);
+       gimple *call = gsi_stmt (gsi);
        tree decl;
 
        if (is_gimple_call (call)
@@ -5336,7 +5343,7 @@ expand_omp_build_assign (gimple_stmt_iterator *gsi_p, tree to, tree from)
   bool simple_p = DECL_P (to) && TREE_ADDRESSABLE (to);
   from = force_gimple_operand_gsi (gsi_p, from, simple_p, NULL_TREE,
                                   true, GSI_SAME_STMT);
-  gimple stmt = gimple_build_assign (to, from);
+  gimple *stmt = gimple_build_assign (to, from);
   gsi_insert_before (gsi_p, stmt, GSI_SAME_STMT);
   if (walk_tree (&from, expand_omp_regimplify_p, NULL, NULL)
       || walk_tree (&to, expand_omp_regimplify_p, NULL, NULL))
@@ -5355,7 +5362,7 @@ expand_omp_taskreg (struct omp_region *region)
   struct function *child_cfun;
   tree child_fn, block, t;
   gimple_stmt_iterator gsi;
-  gimple entry_stmt, stmt;
+  gimple *entry_stmt, *stmt;
   edge e;
   vec<tree, va_gc> *ws_args;
 
@@ -5430,11 +5437,11 @@ expand_omp_taskreg (struct omp_region *region)
            = single_succ_p (entry_bb) ? single_succ (entry_bb)
                                       : FALLTHRU_EDGE (entry_bb)->dest;
          tree arg;
-         gimple parcopy_stmt = NULL;
+         gimple *parcopy_stmt = NULL;
 
          for (gsi = gsi_start_bb (entry_succ_bb); ; gsi_next (&gsi))
            {
-             gimple stmt;
+             gimple *stmt;
 
              gcc_assert (!gsi_end_p (gsi));
              stmt = gsi_stmt (gsi);
@@ -5836,7 +5843,7 @@ expand_omp_for_init_counts (struct omp_for_data *fd, gimple_stmt_iterator *gsi,
 
 static void
 expand_omp_for_init_vars (struct omp_for_data *fd, gimple_stmt_iterator *gsi,
-                         tree *counts, gimple inner_stmt, tree startvar)
+                         tree *counts, gimple *inner_stmt, tree startvar)
 {
   int i;
   if (gimple_omp_for_combined_p (fd->for_stmt))
@@ -5935,7 +5942,7 @@ extract_omp_for_update_vars (struct omp_for_data *fd, basic_block cont_bb,
   gimple_stmt_iterator gsi;
   edge e;
   tree t;
-  gimple stmt;
+  gimple *stmt;
 
   last_bb = cont_bb;
   for (i = fd->collapse - 1; i >= 0; i--)
@@ -6097,7 +6104,7 @@ expand_omp_for_generic (struct omp_region *region,
                        struct omp_for_data *fd,
                        enum built_in_function start_fn,
                        enum built_in_function next_fn,
-                       gimple inner_stmt)
+                       gimple *inner_stmt)
 {
   tree type, istart0, iend0, iend;
   tree t, vmain, vback, bias = NULL_TREE;
@@ -6422,7 +6429,7 @@ expand_omp_for_generic (struct omp_region *region,
       phis = phi_nodes (l3_bb);
       for (gsi = gsi_start (phis); !gsi_end_p (gsi); gsi_next (&gsi))
        {
-         gimple phi = gsi_stmt (gsi);
+         gimple *phi = gsi_stmt (gsi);
          SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (phi, ne),
                   PHI_ARG_DEF_FROM_EDGE (phi, e));
        }
@@ -6520,7 +6527,7 @@ expand_omp_for_generic (struct omp_region *region,
 static void
 expand_omp_for_static_nochunk (struct omp_region *region,
                               struct omp_for_data *fd,
-                              gimple inner_stmt)
+                              gimple *inner_stmt)
 {
   tree n, q, s0, e0, e, t, tt, nthreads, threadid;
   tree type, itype, vmain, vback;
@@ -6885,6 +6892,24 @@ expand_omp_for_static_nochunk (struct omp_region *region,
     }
 }
 
+/* Return phi in E->DEST with ARG on edge E.  */
+
+static gphi *
+find_phi_with_arg_on_edge (tree arg, edge e)
+{
+  basic_block bb = e->dest;
+
+  for (gphi_iterator gpi = gsi_start_phis (bb);
+       !gsi_end_p (gpi);
+       gsi_next (&gpi))
+    {
+      gphi *phi = gpi.phi ();
+      if (PHI_ARG_DEF_FROM_EDGE (phi, e) == arg)
+       return phi;
+    }
+
+  return NULL;
+}
 
 /* A subroutine of expand_omp_for.  Generate code for a parallel
    loop with static schedule and a specified chunk size.  Given
@@ -6926,7 +6951,7 @@ expand_omp_for_static_nochunk (struct omp_region *region,
 
 static void
 expand_omp_for_static_chunk (struct omp_region *region,
-                            struct omp_for_data *fd, gimple inner_stmt)
+                            struct omp_for_data *fd, gimple *inner_stmt)
 {
   tree n, s0, e0, e, t;
   tree trip_var, trip_init, trip_main, trip_back, nthreads, threadid;
@@ -6960,7 +6985,8 @@ expand_omp_for_static_chunk (struct omp_region *region,
   body_bb = single_succ (seq_start_bb);
   if (!broken_loop)
     {
-      gcc_assert (BRANCH_EDGE (cont_bb)->dest == body_bb);
+      gcc_assert (BRANCH_EDGE (cont_bb)->dest == body_bb
+                 || single_succ (BRANCH_EDGE (cont_bb)->dest) == body_bb);
       gcc_assert (EDGE_COUNT (cont_bb->succs) == 2);
       trip_update_bb = split_edge (FALLTHRU_EDGE (cont_bb));
     }
@@ -7016,7 +7042,7 @@ expand_omp_for_static_chunk (struct omp_region *region,
       se->probability = REG_BR_PROB_BASE / 2000 - 1;
       if (gimple_in_ssa_p (cfun))
        {
-         int dest_idx = find_edge (entry_bb, fin_bb)->dest_idx;
+         int dest_idx = find_edge (iter_part_bb, fin_bb)->dest_idx;
          for (gphi_iterator gpi = gsi_start_phis (fin_bb);
               !gsi_end_p (gpi); gsi_next (&gpi))
            {
@@ -7261,6 +7287,11 @@ expand_omp_for_static_chunk (struct omp_region *region,
   if (!broken_loop)
     {
       se = find_edge (cont_bb, body_bb);
+      if (se == NULL)
+       {
+         se = BRANCH_EDGE (cont_bb);
+         gcc_assert (single_succ (se->dest) == body_bb);
+       }
       if (gimple_omp_for_combined_p (fd->for_stmt))
        {
          remove_edge (se);
@@ -7292,7 +7323,7 @@ expand_omp_for_static_chunk (struct omp_region *region,
       /* When we redirect the edge from trip_update_bb to iter_part_bb, we
         remove arguments of the phi nodes in fin_bb.  We need to create
         appropriate phi nodes in iter_part_bb instead.  */
-      se = single_pred_edge (fin_bb);
+      se = find_edge (iter_part_bb, fin_bb);
       re = single_succ_edge (trip_update_bb);
       vec<edge_var_map> *head = redirect_edge_var_map_vector (re);
       ene = single_succ_edge (entry_bb);
@@ -7307,6 +7338,10 @@ expand_omp_for_static_chunk (struct omp_region *region,
          phi = psi.phi ();
          t = gimple_phi_result (phi);
          gcc_assert (t == redirect_edge_var_map_result (vm));
+
+         if (!single_pred_p (fin_bb))
+           t = copy_ssa_name (t, phi);
+
          nphi = create_phi_node (t, iter_part_bb);
 
          t = PHI_ARG_DEF_FROM_EDGE (phi, se);
@@ -7318,17 +7353,33 @@ expand_omp_for_static_chunk (struct omp_region *region,
            t = vextra;
          add_phi_arg (nphi, t, ene, locus);
          locus = redirect_edge_var_map_location (vm);
-         add_phi_arg (nphi, redirect_edge_var_map_def (vm), re, locus);
+         tree back_arg = redirect_edge_var_map_def (vm);
+         add_phi_arg (nphi, back_arg, re, locus);
+         edge ce = find_edge (cont_bb, body_bb);
+         if (ce == NULL)
+           {
+             ce = BRANCH_EDGE (cont_bb);
+             gcc_assert (single_succ (ce->dest) == body_bb);
+             ce = single_succ_edge (ce->dest);
+           }
+         gphi *inner_loop_phi = find_phi_with_arg_on_edge (back_arg, ce);
+         gcc_assert (inner_loop_phi != NULL);
+         add_phi_arg (inner_loop_phi, gimple_phi_result (nphi),
+                      find_edge (seq_start_bb, body_bb), locus);
+
+         if (!single_pred_p (fin_bb))
+           add_phi_arg (phi, gimple_phi_result (nphi), se, locus);
        }
-      gcc_assert (gsi_end_p (psi) && i == head->length ());
+      gcc_assert (gsi_end_p (psi) && (head == NULL || i == head->length ()));
       redirect_edge_var_map_clear (re);
-      while (1)
-       {
-         psi = gsi_start_phis (fin_bb);
-         if (gsi_end_p (psi))
-           break;
-         remove_phi_node (&psi, false);
-       }
+      if (single_pred_p (fin_bb))
+       while (1)
+         {
+           psi = gsi_start_phis (fin_bb);
+           if (gsi_end_p (psi))
+             break;
+           remove_phi_node (&psi, false);
+         }
 
       /* Make phi node for trip.  */
       phi = create_phi_node (trip_main, iter_part_bb);
@@ -7351,14 +7402,24 @@ expand_omp_for_static_chunk (struct omp_region *region,
 
   if (!broken_loop)
     {
+      struct loop *loop = body_bb->loop_father;
       struct loop *trip_loop = alloc_loop ();
       trip_loop->header = iter_part_bb;
       trip_loop->latch = trip_update_bb;
       add_loop (trip_loop, iter_part_bb->loop_father);
 
+      if (loop != entry_bb->loop_father)
+       {
+         gcc_assert (loop->header == body_bb);
+         gcc_assert (loop->latch == region->cont
+                     || single_pred (loop->latch) == region->cont);
+         trip_loop->inner = loop;
+         return;
+       }
+
       if (!gimple_omp_for_combined_p (fd->for_stmt))
        {
-         struct loop *loop = alloc_loop ();
+         loop = alloc_loop ();
          loop->header = body_bb;
          if (collapse_bb == NULL)
            loop->latch = cont_bb;
@@ -7454,7 +7515,7 @@ expand_cilk_for (struct omp_region *region, struct omp_for_data *fd)
   tree n1 = low_val;
   tree n2 = high_val;
 
-  gimple stmt = gimple_build_assign (ind_var, n1);
+  gimple *stmt = gimple_build_assign (ind_var, n1);
 
   /* Replace the GIMPLE_OMP_FOR statement.  */
   gsi_replace (&gsi, stmt, true);
@@ -7620,7 +7681,7 @@ expand_omp_simd (struct omp_region *region, struct omp_for_data *fd)
   tree type, t;
   basic_block entry_bb, cont_bb, exit_bb, l0_bb, l1_bb, l2_bb, l2_dom_bb;
   gimple_stmt_iterator gsi;
-  gimple stmt;
+  gimple *stmt;
   gcond *cond_stmt;
   bool broken_loop = region->cont == NULL;
   edge e, ne;
@@ -7870,7 +7931,7 @@ expand_omp_simd (struct omp_region *region, struct omp_for_data *fd)
 /* Expand the OMP loop defined by REGION.  */
 
 static void
-expand_omp_for (struct omp_region *region, gimple inner_stmt)
+expand_omp_for (struct omp_region *region, gimple *inner_stmt)
 {
   struct omp_for_data fd;
   struct omp_for_data_loop *loops;
@@ -7976,7 +8037,7 @@ expand_omp_sections (struct omp_region *region)
   basic_block entry_bb, l0_bb, l1_bb, l2_bb, default_bb;
   gimple_stmt_iterator si, switch_si;
   gomp_sections *sections_stmt;
-  gimple stmt;
+  gimple *stmt;
   gomp_continue *cont;
   edge_iterator ei;
   edge e;
@@ -8232,7 +8293,7 @@ expand_omp_atomic_load (basic_block load_bb, tree addr,
   gimple_stmt_iterator gsi;
   basic_block store_bb;
   location_t loc;
-  gimple stmt;
+  gimple *stmt;
   tree decl, call, type, itype;
 
   gsi = gsi_last_bb (load_bb);
@@ -8286,7 +8347,7 @@ expand_omp_atomic_store (basic_block load_bb, tree addr,
   gimple_stmt_iterator gsi;
   basic_block store_bb = single_succ (load_bb);
   location_t loc;
-  gimple stmt;
+  gimple *stmt;
   tree decl, call, type, itype;
   machine_mode imode;
   bool exchange;
@@ -8367,7 +8428,7 @@ expand_omp_atomic_fetch_op (basic_block load_bb,
   tree lhs, rhs;
   basic_block store_bb = single_succ (load_bb);
   gimple_stmt_iterator gsi;
-  gimple stmt;
+  gimple *stmt;
   location_t loc;
   enum tree_code code;
   bool need_old, need_new;
@@ -8513,7 +8574,7 @@ expand_omp_atomic_pipeline (basic_block load_bb, basic_block store_bb,
   tree type, itype, cmpxchg, iaddr;
   gimple_stmt_iterator si;
   basic_block loop_header = single_succ (load_bb);
-  gimple phi, stmt;
+  gimple *phi, *stmt;
   edge e;
   enum built_in_function fncode;
 
@@ -8807,6 +8868,110 @@ expand_omp_atomic (struct omp_region *region)
 }
 
 
+/* Encode an oacc launc argument.  This matches the GOMP_LAUNCH_PACK
+   macro on gomp-constants.h.  We do not check for overflow.  */
+
+static tree
+oacc_launch_pack (unsigned code, tree device, unsigned op)
+{
+  tree res;
+  
+  res = build_int_cst (unsigned_type_node, GOMP_LAUNCH_PACK (code, 0, op));
+  if (device)
+    {
+      device = fold_build2 (LSHIFT_EXPR, unsigned_type_node,
+                           device, build_int_cst (unsigned_type_node,
+                                                  GOMP_LAUNCH_DEVICE_SHIFT));
+      res = fold_build2 (BIT_IOR_EXPR, unsigned_type_node, res, device);
+    }
+  return res;
+}
+
+/* Look for compute grid dimension clauses and convert to an attribute
+   attached to FN.  This permits the target-side code to (a) massage
+   the dimensions, (b) emit that data and (c) optimize.  Non-constant
+   dimensions are pushed onto ARGS.
+
+   The attribute value is a TREE_LIST.  A set of dimensions is
+   represented as a list of INTEGER_CST.  Those that are runtime
+   expres are represented as an INTEGER_CST of zero.
+
+   TOOO. Normally the attribute will just contain a single such list.  If
+   however it contains a list of lists, this will represent the use of
+   device_type.  Each member of the outer list is an assoc list of
+   dimensions, keyed by the device type.  The first entry will be the
+   default.  Well, that's the plan.  */
+
+#define OACC_FN_ATTRIB "oacc function"
+
+/* Replace any existing oacc fn attribute with updated dimensions.  */
+
+void
+replace_oacc_fn_attrib (tree fn, tree dims)
+{
+  tree ident = get_identifier (OACC_FN_ATTRIB);
+  tree attribs = DECL_ATTRIBUTES (fn);
+
+  /* If we happen to be present as the first attrib, drop it.  */
+  if (attribs && TREE_PURPOSE (attribs) == ident)
+    attribs = TREE_CHAIN (attribs);
+  DECL_ATTRIBUTES (fn) = tree_cons (ident, dims, attribs);
+}
+
+/* Scan CLAUSES for launch dimensions and attach them to the oacc
+   function attribute.  Push any that are non-constant onto the ARGS
+   list, along with an appropriate GOMP_LAUNCH_DIM tag.  */
+
+static void
+set_oacc_fn_attrib (tree fn, tree clauses, vec<tree> *args)
+{
+  /* Must match GOMP_DIM ordering.  */
+  static const omp_clause_code ids[]
+    = { OMP_CLAUSE_NUM_GANGS, OMP_CLAUSE_NUM_WORKERS,
+       OMP_CLAUSE_VECTOR_LENGTH };
+  unsigned ix;
+  tree dims[GOMP_DIM_MAX];
+  tree attr = NULL_TREE;
+  unsigned non_const = 0;
+
+  for (ix = GOMP_DIM_MAX; ix--;)
+    {
+      tree clause = find_omp_clause (clauses, ids[ix]);
+      tree dim = NULL_TREE;
+
+      if (clause)
+       dim = OMP_CLAUSE_EXPR (clause, ids[ix]);
+      dims[ix] = dim;
+      if (dim && TREE_CODE (dim) != INTEGER_CST)
+       {
+         dim = integer_zero_node;
+         non_const |= GOMP_DIM_MASK (ix);
+       }
+      attr = tree_cons (NULL_TREE, dim, attr);
+    }
+
+  replace_oacc_fn_attrib (fn, attr);
+
+  if (non_const)
+    {
+      /* Push a dynamic argument set.  */
+      args->safe_push (oacc_launch_pack (GOMP_LAUNCH_DIM,
+                                        NULL_TREE, non_const));
+      for (unsigned ix = 0; ix != GOMP_DIM_MAX; ix++)
+       if (non_const & GOMP_DIM_MASK (ix))
+         args->safe_push (dims[ix]);
+    }
+}
+
+/* Retrieve the oacc function attrib and return it.  Non-oacc
+   functions will return NULL.  */
+
+tree
+get_oacc_fn_attrib (tree fn)
+{
+  return lookup_attribute (OACC_FN_ATTRIB, DECL_ATTRIBUTES (fn));
+}
+
 /* Expand the GIMPLE_OMP_TARGET starting at REGION.  */
 
 static void
@@ -8817,7 +8982,7 @@ expand_omp_target (struct omp_region *region)
   tree child_fn, block, t;
   gimple_stmt_iterator gsi;
   gomp_target *entry_stmt;
-  gimple stmt;
+  gimple *stmt;
   edge e;
   bool offloaded, data_region;
 
@@ -8827,10 +8992,10 @@ expand_omp_target (struct omp_region *region)
   offloaded = is_gimple_omp_offloaded (entry_stmt);
   switch (gimple_omp_target_kind (entry_stmt))
     {
-    case GF_OMP_TARGET_KIND_REGION:
-    case GF_OMP_TARGET_KIND_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_PARALLEL:
     case GF_OMP_TARGET_KIND_OACC_KERNELS:
+    case GF_OMP_TARGET_KIND_REGION:
+    case GF_OMP_TARGET_KIND_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_UPDATE:
     case GF_OMP_TARGET_KIND_OACC_ENTER_EXIT_DATA:
       data_region = false;
@@ -8882,7 +9047,7 @@ expand_omp_target (struct omp_region *region)
          basic_block entry_succ_bb = single_succ (entry_bb);
          gimple_stmt_iterator gsi;
          tree arg;
-         gimple tgtcopy_stmt = NULL;
+         gimple *tgtcopy_stmt = NULL;
          tree sender = TREE_VEC_ELT (data_arg, 0);
 
          for (gsi = gsi_start_bb (entry_succ_bb); ; gsi_next (&gsi))
@@ -9161,7 +9326,8 @@ expand_omp_target (struct omp_region *region)
       t4 = build_fold_addr_expr (TREE_VEC_ELT (t, 2));
     }
 
-  gimple g;
+  gimple *g;
+  bool tagging = false;
   /* The maximum number used by any start_ix, without varargs.  */
   auto_vec<tree, 11> args;
   args.quick_push (device);
@@ -9197,88 +9363,87 @@ expand_omp_target (struct omp_region *region)
       break;
     case BUILT_IN_GOACC_PARALLEL:
       {
-       tree t_num_gangs, t_num_workers, t_vector_length;
-
-       /* Default values for num_gangs, num_workers, and vector_length.  */
-       t_num_gangs = t_num_workers = t_vector_length
-         = fold_convert_loc (gimple_location (entry_stmt),
-                             integer_type_node, integer_one_node);
-       /* ..., but if present, use the value specified by the respective
-          clause, making sure that are of the correct type.  */
-       c = find_omp_clause (clauses, OMP_CLAUSE_NUM_GANGS);
-       if (c)
-         t_num_gangs = fold_convert_loc (OMP_CLAUSE_LOCATION (c),
-                                         integer_type_node,
-                                         OMP_CLAUSE_NUM_GANGS_EXPR (c));
-       c = find_omp_clause (clauses, OMP_CLAUSE_NUM_WORKERS);
-       if (c)
-         t_num_workers = fold_convert_loc (OMP_CLAUSE_LOCATION (c),
-                                           integer_type_node,
-                                           OMP_CLAUSE_NUM_WORKERS_EXPR (c));
-       c = find_omp_clause (clauses, OMP_CLAUSE_VECTOR_LENGTH);
-       if (c)
-         t_vector_length = fold_convert_loc (OMP_CLAUSE_LOCATION (c),
-                                             integer_type_node,
-                                             OMP_CLAUSE_VECTOR_LENGTH_EXPR (c));
-       args.quick_push (t_num_gangs);
-       args.quick_push (t_num_workers);
-       args.quick_push (t_vector_length);
+       set_oacc_fn_attrib (child_fn, clauses, &args);
+       tagging = true;
       }
       /* FALLTHRU */
     case BUILT_IN_GOACC_ENTER_EXIT_DATA:
     case BUILT_IN_GOACC_UPDATE:
       {
-       tree t_async;
-       int t_wait_idx;
-
-       /* Default values for t_async.  */
-       t_async = fold_convert_loc (gimple_location (entry_stmt),
-                                   integer_type_node,
-                                   build_int_cst (integer_type_node,
-                                                  GOMP_ASYNC_SYNC));
-       /* ..., but if present, use the value specified by the respective
+       tree t_async = NULL_TREE;
+
+       /* If present, use the value specified by the respective
           clause, making sure that is of the correct type.  */
        c = find_omp_clause (clauses, OMP_CLAUSE_ASYNC);
        if (c)
          t_async = fold_convert_loc (OMP_CLAUSE_LOCATION (c),
                                      integer_type_node,
                                      OMP_CLAUSE_ASYNC_EXPR (c));
-
-       args.quick_push (t_async);
-       /* Save the index, and... */
-       t_wait_idx = args.length ();
-       /* ... push a default value.  */
-       args.quick_push (fold_convert_loc (gimple_location (entry_stmt),
-                                          integer_type_node,
-                                          integer_zero_node));
-       c = find_omp_clause (clauses, OMP_CLAUSE_WAIT);
-       if (c)
+       else if (!tagging)
+         /* Default values for t_async.  */
+         t_async = fold_convert_loc (gimple_location (entry_stmt),
+                                     integer_type_node,
+                                     build_int_cst (integer_type_node,
+                                                    GOMP_ASYNC_SYNC));
+       if (tagging && t_async)
          {
-           int n = 0;
+           unsigned HOST_WIDE_INT i_async;
 
-           for (; c; c = OMP_CLAUSE_CHAIN (c))
+           if (TREE_CODE (t_async) == INTEGER_CST)
              {
-               if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_WAIT)
-                 {
-                   args.safe_push (fold_convert_loc (OMP_CLAUSE_LOCATION (c),
-                                                     integer_type_node,
-                                                     OMP_CLAUSE_WAIT_EXPR (c)));
-                   n++;
-                 }
+               /* See if we can pack the async arg in to the tag's
+                  operand.  */
+               i_async = TREE_INT_CST_LOW (t_async);
+
+               if (i_async < GOMP_LAUNCH_OP_MAX)
+                 t_async = NULL_TREE;
              }
+           if (t_async)
+             i_async = GOMP_LAUNCH_OP_MAX;
+           args.safe_push (oacc_launch_pack
+                           (GOMP_LAUNCH_ASYNC, NULL_TREE, i_async));
+         }
+       if (t_async)
+         args.safe_push (t_async);
+
+       /* Save the argument index, and ... */
+       unsigned t_wait_idx = args.length ();
+       unsigned num_waits = 0;
+       c = find_omp_clause (clauses, OMP_CLAUSE_WAIT);
+       if (!tagging || c)
+         /* ... push a placeholder.  */
+         args.safe_push (integer_zero_node);
+
+       for (; c; c = OMP_CLAUSE_CHAIN (c))
+         if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_WAIT)
+           {
+             args.safe_push (fold_convert_loc (OMP_CLAUSE_LOCATION (c),
+                                               integer_type_node,
+                                               OMP_CLAUSE_WAIT_EXPR (c)));
+             num_waits++;
+           }
+
+       if (!tagging || num_waits)
+         {
+           tree len;
 
-           /* Now that we know the number, replace the default value.  */
-           args.ordered_remove (t_wait_idx);
-           args.quick_insert (t_wait_idx,
-                              fold_convert_loc (gimple_location (entry_stmt),
-                                                integer_type_node,
-                                                build_int_cst (integer_type_node, n)));
+           /* Now that we know the number, update the placeholder.  */
+           if (tagging)
+             len = oacc_launch_pack (GOMP_LAUNCH_WAIT, NULL_TREE, num_waits);
+           else
+             len = build_int_cst (integer_type_node, num_waits);
+           len = fold_convert_loc (gimple_location (entry_stmt),
+                                   unsigned_type_node, len);
+           args[t_wait_idx] = len;
          }
       }
       break;
     default:
       gcc_unreachable ();
     }
+  if (tagging)
+    /*  Push terminal marker - zero.  */
+    args.safe_push (oacc_launch_pack (0, NULL_TREE, 0));
 
   g = gimple_build_call_vec (builtin_decl_explicit (start_ix), args);
   gimple_set_location (g, gimple_location (entry_stmt));
@@ -9312,7 +9477,7 @@ expand_omp (struct omp_region *region)
   while (region)
     {
       location_t saved_location;
-      gimple inner_stmt = NULL;
+      gimple *inner_stmt = NULL;
 
       /* First, determine whether this is a combined parallel+workshare
                 region.  */
@@ -9390,7 +9555,7 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent,
                     bool single_tree)
 {
   gimple_stmt_iterator gsi;
-  gimple stmt;
+  gimple *stmt;
   basic_block son;
 
   gsi = gsi_last_bb (bb);
@@ -9639,7 +9804,7 @@ make_pass_expand_omp_ssa (gcc::context *ctxt)
 static void
 oacc_gimple_assign (tree dest, tree_code op, tree src, gimple_seq *seq)
 {
-  gimple stmt;
+  gimple *stmt;
 
   if (TREE_CODE (TREE_TYPE (dest)) != COMPLEX_TYPE)
     {
@@ -9724,7 +9889,7 @@ oacc_initialize_reduction_data (tree clauses, tree nthreads,
                                gimple_seq *stmt_seqp, omp_context *ctx)
 {
   tree c, t, oc;
-  gimple stmt;
+  gimple *stmt;
   omp_context *octx;
 
   /* Find the innermost OpenACC parallel context.  */
@@ -9800,7 +9965,7 @@ oacc_finalize_reduction_data (tree clauses, tree nthreads,
                              gimple_seq *stmt_seqp, omp_context *ctx)
 {
   tree c, x, var, array, loop_header, loop_body, loop_exit, type;
-  gimple stmt;
+  gimple *stmt;
 
   /* Create for loop.
 
@@ -9920,7 +10085,7 @@ oacc_process_reduction_data (gimple_seq *body, gimple_seq *in_stmt_seqp,
   gsi = gsi_start (*body);
   while (!gsi_end_p (gsi))
     {
-      gimple stmt = gsi_stmt (gsi);
+      gimple *stmt = gsi_stmt (gsi);
       if (gbind *bind_stmt = dyn_cast <gbind *> (stmt))
        {
          inner = gimple_bind_body (bind_stmt);
@@ -9939,7 +10104,7 @@ oacc_process_reduction_data (gimple_seq *body, gimple_seq *in_stmt_seqp,
        enter, exit;
       bool reduction_found = false;
 
-      gimple stmt = gsi_stmt (gsi);
+      gimple *stmt = gsi_stmt (gsi);
 
       switch (gimple_code (stmt))
        {
@@ -10011,7 +10176,7 @@ oacc_process_reduction_data (gimple_seq *body, gimple_seq *in_stmt_seqp,
 static void
 maybe_add_implicit_barrier_cancel (omp_context *ctx, gimple_seq *body)
 {
-  gimple omp_return = gimple_seq_last_stmt (*body);
+  gimple *omp_return = gimple_seq_last_stmt (*body);
   gcc_assert (gimple_code (omp_return) == GIMPLE_OMP_RETURN);
   if (gimple_omp_return_nowait_p (omp_return))
     return;
@@ -10024,7 +10189,7 @@ maybe_add_implicit_barrier_cancel (omp_context *ctx, gimple_seq *body)
       tree lhs = create_tmp_var (c_bool_type);
       gimple_omp_return_set_lhs (omp_return, lhs);
       tree fallthru_label = create_artificial_label (UNKNOWN_LOCATION);
-      gimple g = gimple_build_cond (NE_EXPR, lhs,
+      gimple *g = gimple_build_cond (NE_EXPR, lhs,
                                    fold_convert (c_bool_type,
                                                  boolean_false_node),
                                    ctx->outer->cancel_label, fallthru_label);
@@ -10042,7 +10207,7 @@ lower_omp_sections (gimple_stmt_iterator *gsi_p, omp_context *ctx)
   tree block, control;
   gimple_stmt_iterator tgsi;
   gomp_sections *stmt;
-  gimple t;
+  gimple *t;
   gbind *new_stmt, *bind;
   gimple_seq ilist, dlist, olist, new_body;
 
@@ -10061,7 +10226,7 @@ lower_omp_sections (gimple_stmt_iterator *gsi_p, omp_context *ctx)
   for (; !gsi_end_p (tgsi); gsi_next (&tgsi))
     {
       omp_context *sctx;
-      gimple sec_start;
+      gimple *sec_start;
 
       sec_start = gsi_stmt (tgsi);
       sctx = maybe_lookup_ctx (sec_start);
@@ -10146,7 +10311,7 @@ lower_omp_single_simple (gomp_single *single_stmt, gimple_seq *pre_p)
   location_t loc = gimple_location (single_stmt);
   tree tlabel = create_artificial_label (loc);
   tree flabel = create_artificial_label (loc);
-  gimple call, cond;
+  gimple *call, *cond;
   tree lhs, decl;
 
   decl = builtin_decl_explicit (BUILT_IN_GOMP_SINGLE_START);
@@ -10253,7 +10418,7 @@ static void
 lower_omp_single (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 {
   tree block;
-  gimple t;
+  gimple *t;
   gomp_single *single_stmt = as_a <gomp_single *> (gsi_stmt (*gsi_p));
   gbind *bind;
   gimple_seq bind_body, bind_body_tail = NULL, dlist;
@@ -10313,7 +10478,7 @@ static void
 lower_omp_master (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 {
   tree block, lab = NULL, x, bfn_decl;
-  gimple stmt = gsi_stmt (*gsi_p);
+  gimple *stmt = gsi_stmt (*gsi_p);
   gbind *bind;
   location_t loc = gimple_location (stmt);
   gimple_seq tseq;
@@ -10354,7 +10519,7 @@ lower_omp_master (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 static void
 lower_omp_taskgroup (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 {
-  gimple stmt = gsi_stmt (*gsi_p);
+  gimple *stmt = gsi_stmt (*gsi_p);
   gcall *x;
   gbind *bind;
   tree block = make_node (BLOCK);
@@ -10384,7 +10549,7 @@ static void
 lower_omp_ordered (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 {
   tree block;
-  gimple stmt = gsi_stmt (*gsi_p);
+  gimple *stmt = gsi_stmt (*gsi_p);
   gcall *x;
   gbind *bind;
 
@@ -10746,7 +10911,7 @@ check_combined_parallel (gimple_stmt_iterator *gsi_p,
                         struct walk_stmt_info *wi)
 {
   int *info = (int *) wi->info;
-  gimple stmt = gsi_stmt (*gsi_p);
+  gimple *stmt = gsi_stmt (*gsi_p);
 
   *handled_ops_p = true;
   switch (gimple_code (stmt))
@@ -11045,10 +11210,10 @@ create_task_copyfn (gomp_task *task_stmt, omp_context *ctx)
 }
 
 static void
-lower_depend_clauses (gimple stmt, gimple_seq *iseq, gimple_seq *oseq)
+lower_depend_clauses (gimple *stmt, gimple_seq *iseq, gimple_seq *oseq)
 {
   tree c, clauses;
-  gimple g;
+  gimple *g;
   size_t n_in = 0, n_out = 0, idx = 2, i;
 
   clauses = find_omp_clause (gimple_omp_task_clauses (stmt),
@@ -11114,7 +11279,7 @@ lower_omp_taskreg (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 {
   tree clauses;
   tree child_fn, t;
-  gimple stmt = gsi_stmt (*gsi_p);
+  gimple *stmt = gsi_stmt (*gsi_p);
   gbind *par_bind, *bind, *dep_bind = NULL;
   gimple_seq par_body, olist, ilist, par_olist, par_rlist, par_ilist, new_body;
   location_t loc = gimple_location (stmt);
@@ -11669,7 +11834,7 @@ lower_omp_teams (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 
   location_t loc = gimple_location (teams_stmt);
   tree decl = builtin_decl_explicit (BUILT_IN_GOMP_TEAMS);
-  gimple call = gimple_build_call (decl, 2, num_teams, thread_limit);
+  gimple *call = gimple_build_call (decl, 2, num_teams, thread_limit);
   gimple_set_location (call, loc);
   gimple_seq_add_stmt (&bind_body, call);
 
@@ -11720,7 +11885,7 @@ lower_omp_regimplify_p (tree *tp, int *walk_subtrees,
 static void
 lower_omp_1 (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 {
-  gimple stmt = gsi_stmt (*gsi_p);
+  gimple *stmt = gsi_stmt (*gsi_p);
   struct walk_stmt_info wi;
   gcall *call_stmt;
 
@@ -11876,7 +12041,7 @@ lower_omp_1 (gimple_stmt_iterator *gsi_p, omp_context *ctx)
            gimple_call_set_lhs (call_stmt, lhs);
            tree fallthru_label;
            fallthru_label = create_artificial_label (UNKNOWN_LOCATION);
-           gimple g;
+           gimple *g;
            g = gimple_build_label (fallthru_label);
            gsi_insert_after (gsi_p, g, GSI_SAME_STMT);
            g = gimple_build_cond (NE_EXPR, lhs,
@@ -12015,7 +12180,7 @@ static splay_tree all_labels;
 
 static bool
 diagnose_sb_0 (gimple_stmt_iterator *gsi_p,
-              gimple branch_ctx, gimple label_ctx)
+              gimple *branch_ctx, gimple *label_ctx)
 {
   gcc_checking_assert (!branch_ctx || is_gimple_omp (branch_ctx));
   gcc_checking_assert (!label_ctx || is_gimple_omp (label_ctx));
@@ -12106,9 +12271,9 @@ static tree
 diagnose_sb_1 (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
               struct walk_stmt_info *wi)
 {
-  gimple context = (gimple) wi->info;
-  gimple inner_context;
-  gimple stmt = gsi_stmt (*gsi_p);
+  gimple *context = (gimple *) wi->info;
+  gimple *inner_context;
+  gimple *stmt = gsi_stmt (*gsi_p);
 
   *handled_ops_p = true;
 
@@ -12166,9 +12331,9 @@ static tree
 diagnose_sb_2 (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
               struct walk_stmt_info *wi)
 {
-  gimple context = (gimple) wi->info;
+  gimple *context = (gimple *) wi->info;
   splay_tree_node n;
-  gimple stmt = gsi_stmt (*gsi_p);
+  gimple *stmt = gsi_stmt (*gsi_p);
 
   *handled_ops_p = true;
 
@@ -12211,7 +12376,7 @@ diagnose_sb_2 (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
              n = splay_tree_lookup (all_labels,
                                     (splay_tree_key) lab);
              diagnose_sb_0 (gsi_p, context,
-                            n ? (gimple) n->value : NULL);
+                            n ? (gimple *) n->value : NULL);
            }
          lab = gimple_cond_false_label (cond_stmt);
          if (lab)
@@ -12219,7 +12384,7 @@ diagnose_sb_2 (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
              n = splay_tree_lookup (all_labels,
                                     (splay_tree_key) lab);
              diagnose_sb_0 (gsi_p, context,
-                            n ? (gimple) n->value : NULL);
+                            n ? (gimple *) n->value : NULL);
            }
        }
       break;
@@ -12231,7 +12396,7 @@ diagnose_sb_2 (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
          break;
 
        n = splay_tree_lookup (all_labels, (splay_tree_key) lab);
-       diagnose_sb_0 (gsi_p, context, n ? (gimple) n->value : NULL);
+       diagnose_sb_0 (gsi_p, context, n ? (gimple *) n->value : NULL);
       }
       break;
 
@@ -12243,7 +12408,7 @@ diagnose_sb_2 (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
          {
            tree lab = CASE_LABEL (gimple_switch_label (switch_stmt, i));
            n = splay_tree_lookup (all_labels, (splay_tree_key) lab);
-           if (n && diagnose_sb_0 (gsi_p, context, (gimple) n->value))
+           if (n && diagnose_sb_0 (gsi_p, context, (gimple *) n->value))
              break;
          }
       }
@@ -12266,7 +12431,7 @@ bool
 make_gimple_omp_edges (basic_block bb, struct omp_region **region,
                       int *region_idx)
 {
-  gimple last = last_stmt (bb);
+  gimple *last = last_stmt (bb);
   enum gimple_code code = gimple_code (last);
   struct omp_region *cur_region = *region;
   bool fallthru = false;
@@ -13088,7 +13253,7 @@ simd_clone_init_simd_arrays (struct cgraph_node *node,
 
 struct modify_stmt_info {
   ipa_parm_adjustment_vec adjustments;
-  gimple stmt;
+  gimple *stmt;
   /* True if the parent statement was modified by
      ipa_simd_modify_stmt_ops.  */
   bool modified;
@@ -13142,7 +13307,7 @@ ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data)
   if (tp != orig_tp)
     {
       repl = build_fold_addr_expr (repl);
-      gimple stmt;
+      gimple *stmt;
       if (is_gimple_debug (info->stmt))
        {
          tree vexpr = make_node (DEBUG_EXPR_DECL);
@@ -13242,7 +13407,7 @@ ipa_simd_modify_function_body (struct cgraph_node *node,
                    set_ssa_default_def (cfun, adjustments[j].base, NULL_TREE);
                    SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var);
                    SSA_NAME_IS_DEFAULT_DEF (name) = 0;
-                   gimple stmt = gimple_build_assign (name, new_decl);
+                   gimple *stmt = gimple_build_assign (name, new_decl);
                    gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
                  }
                else
@@ -13261,7 +13426,7 @@ ipa_simd_modify_function_body (struct cgraph_node *node,
       gsi = gsi_start_bb (bb);
       while (!gsi_end_p (gsi))
        {
-         gimple stmt = gsi_stmt (gsi);
+         gimple *stmt = gsi_stmt (gsi);
          info.stmt = stmt;
          struct walk_stmt_info wi;
 
@@ -13351,7 +13516,7 @@ simd_clone_adjust (struct cgraph_node *node)
   edge e = make_edge (incr_bb, EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
   e->probability = REG_BR_PROB_BASE;
   gsi = gsi_last_bb (incr_bb);
-  gimple g = gimple_build_assign (iter2, PLUS_EXPR, iter1,
+  gimple *g = gimple_build_assign (iter2, PLUS_EXPR, iter1,
                                  build_int_cst (unsigned_type_node, 1));
   gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING);
 
@@ -13524,7 +13689,7 @@ simd_clone_adjust (struct cgraph_node *node)
 
            imm_use_iterator iter;
            use_operand_p use_p;
-           gimple use_stmt;
+           gimple *use_stmt;
            tree repl = gimple_get_lhs (g);
            FOR_EACH_IMM_USE_STMT (use_stmt, iter, def)
              if (is_gimple_debug (use_stmt) || use_stmt == call)
@@ -13578,7 +13743,7 @@ simd_clone_adjust (struct cgraph_node *node)
 
            imm_use_iterator iter;
            use_operand_p use_p;
-           gimple use_stmt;
+           gimple *use_stmt;
            if (TREE_ADDRESSABLE (orig_arg))
              {
                gsi = gsi_after_labels (body_bb);
@@ -13855,4 +14020,146 @@ omp_finish_file (void)
     }
 }
 
+/* Validate and update the dimensions for offloaded FN.  ATTRS is the
+   raw attribute.  DIMS is an array of dimensions, which is returned.
+   Returns the function level dimensionality --  the level at which an
+   offload routine wishes to partition a loop.  */
+
+static int
+oacc_validate_dims (tree fn, tree attrs, int *dims)
+{
+  tree purpose[GOMP_DIM_MAX];
+  unsigned ix;
+  tree pos = TREE_VALUE (attrs);
+  int fn_level = -1;
+
+  /* Make sure the attribute creator attached the dimension
+     information.  */
+  gcc_assert (pos);
+
+  for (ix = 0; ix != GOMP_DIM_MAX; ix++)
+    {
+      purpose[ix] = TREE_PURPOSE (pos);
+
+      if (purpose[ix])
+       {
+         if (integer_zerop (purpose[ix]))
+           fn_level = ix + 1;
+         else if (fn_level < 0)
+           fn_level = ix;
+       }
+
+      tree val = TREE_VALUE (pos);
+      dims[ix] = val ? TREE_INT_CST_LOW (val) : -1;
+      pos = TREE_CHAIN (pos);
+    }
+
+  bool changed = targetm.goacc.validate_dims (fn, dims, fn_level);
+
+  /* Default anything left to 1.  */
+  for (ix = 0; ix != GOMP_DIM_MAX; ix++)
+    if (dims[ix] < 0)
+      {
+       dims[ix] = 1;
+       changed = true;
+      }
+
+  if (changed)
+    {
+      /* Replace the attribute with new values.  */
+      pos = NULL_TREE;
+      for (ix = GOMP_DIM_MAX; ix--;)
+       pos = tree_cons (purpose[ix],
+                        build_int_cst (integer_type_node, dims[ix]),
+                        pos);
+      replace_oacc_fn_attrib (fn, pos);
+    }
+
+  return fn_level;
+}
+
+/* Main entry point for oacc transformations which run on the device
+   compiler after LTO, so we know what the target device is at this
+   point (including the host fallback).  */
+
+static unsigned int
+execute_oacc_device_lower ()
+{
+  tree attrs = get_oacc_fn_attrib (current_function_decl);
+  int dims[GOMP_DIM_MAX];
+  
+  if (!attrs)
+    /* Not an offloaded function.  */
+    return 0;
+
+  oacc_validate_dims (current_function_decl, attrs, dims);
+  
+  return 0;
+}
+
+/* Default launch dimension validator.  Force everything to 1.  A
+   backend that wants to provide larger dimensions must override this
+   hook.  */
+
+bool
+default_goacc_validate_dims (tree ARG_UNUSED (decl), int *dims,
+                            int ARG_UNUSED (fn_level))
+{
+  bool changed = false;
+
+  for (unsigned ix = 0; ix != GOMP_DIM_MAX; ix++)
+    {
+      if (dims[ix] != 1)
+       {
+         dims[ix] = 1;
+         changed = true;
+       }
+    }
+
+  return changed;
+}
+
+namespace {
+
+const pass_data pass_data_oacc_device_lower =
+{
+  GIMPLE_PASS, /* type */
+  "oaccdevlow", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_cfg, /* properties_required */
+  0 /* Possibly PROP_gimple_eomp.  */, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa | TODO_cleanup_cfg, /* todo_flags_finish */
+};
+
+class pass_oacc_device_lower : public gimple_opt_pass
+{
+public:
+  pass_oacc_device_lower (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_oacc_device_lower, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual unsigned int execute (function *)
+    {
+      bool gate = (flag_openacc != 0 && !seen_error ());
+
+      if (!gate)
+       return 0;
+
+      return execute_oacc_device_lower ();
+    }
+
+}; // class pass_oacc_transform
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_oacc_device_lower (gcc::context *ctxt)
+{
+  return new pass_oacc_device_lower (ctxt);
+}
+
 #include "gt-omp-low.h"