OSDN Git Service

2008-02-10 Benjamin Kosnik <bkoz@redhat.com>
[pf3gnuchains/gcc-fork.git] / gcc / omp-low.c
index 57f3650..ca00266 100644 (file)
@@ -3,7 +3,7 @@
    marshalling to implement data sharing and copying clauses.
    Contributed by Diego Novillo <dnovillo@redhat.com>
 
-   Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -41,7 +41,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "ggc.h"
 #include "except.h"
 #include "splay-tree.h"
-
+#include "optabs.h"
+#include "cfgloop.h"
 
 /* Lowering of OpenMP parallel and workshare constructs proceeds in two 
    phases.  The first phase scans the function looking for OMP statements
@@ -528,6 +529,7 @@ copy_var_decl (tree var, tree name, tree type)
   DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (var);
   DECL_IGNORED_P (copy) = DECL_IGNORED_P (var);
   DECL_CONTEXT (copy) = DECL_CONTEXT (var);
+  DECL_SOURCE_LOCATION (copy) = DECL_SOURCE_LOCATION (var);
   TREE_USED (copy) = 1;
   DECL_SEEN_IN_BIND_EXPR_P (copy) = 1;
 
@@ -1517,12 +1519,10 @@ lookup_decl_in_outer_ctx (tree decl, omp_context *ctx)
   tree t;
   omp_context *up;
 
-  gcc_assert (ctx->is_nested);
-
   for (up = ctx->outer, t = NULL; up && t == NULL; up = up->outer)
     t = maybe_lookup_decl (decl, up);
 
-  gcc_assert (t || is_global_var (decl));
+  gcc_assert (!ctx->is_nested || t || is_global_var (decl));
 
   return t ? t : decl;
 }
@@ -1537,9 +1537,8 @@ maybe_lookup_decl_in_outer_ctx (tree decl, omp_context *ctx)
   tree t = NULL;
   omp_context *up;
 
-  if (ctx->is_nested)
-    for (up = ctx->outer, t = NULL; up && t == NULL; up = up->outer)
-      t = maybe_lookup_decl (decl, up);
+  for (up = ctx->outer, t = NULL; up && t == NULL; up = up->outer)
+    t = maybe_lookup_decl (decl, up);
 
   return t ? t : decl;
 }
@@ -2011,7 +2010,7 @@ lower_copyprivate_clauses (tree clauses, tree *slist, tree *rlist,
       by_ref = use_pointer_for_field (var, false);
 
       ref = build_sender_ref (var, ctx);
-      x = (ctx->is_nested) ? lookup_decl_in_outer_ctx (var, ctx) : var;
+      x = lookup_decl_in_outer_ctx (var, ctx);
       x = by_ref ? build_fold_addr_expr (x) : x;
       x = build_gimple_modify_stmt (ref, x);
       gimplify_and_add (x, slist);
@@ -2052,9 +2051,8 @@ lower_send_clauses (tree clauses, tree *ilist, tree *olist, omp_context *ctx)
          continue;
        }
 
-      var = val = OMP_CLAUSE_DECL (c);
-      if (ctx->is_nested)
-       var = lookup_decl_in_outer_ctx (val, ctx);
+      val = OMP_CLAUSE_DECL (c);
+      var = lookup_decl_in_outer_ctx (val, ctx);
 
       if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_COPYIN
          && is_global_var (var))
@@ -2126,13 +2124,10 @@ lower_send_shared_vars (tree *ilist, tree *olist, omp_context *ctx)
       if (!nvar || !DECL_HAS_VALUE_EXPR_P (nvar))
        continue;
 
-      var = ovar;
-
       /* If CTX is a nested parallel directive.  Find the immediately
         enclosing parallel or workshare construct that contains a
         mapping for OVAR.  */
-      if (ctx->is_nested)
-       var = lookup_decl_in_outer_ctx (ovar, ctx);
+      var = lookup_decl_in_outer_ctx (ovar, ctx);
 
       if (use_pointer_for_field (ovar, true))
        {
@@ -2432,6 +2427,61 @@ remove_exit_barriers (struct omp_region *region)
     }
 }
 
+/* Optimize omp_get_thread_num () and omp_get_num_threads ()
+   calls.  These can't be declared as const functions, but
+   within one parallel body they are constant, so they can be
+   transformed there into __builtin_omp_get_{thread_num,num_threads} ()
+   which are declared const.  */
+
+static void
+optimize_omp_library_calls (void)
+{
+  basic_block bb;
+  block_stmt_iterator bsi;
+  tree thr_num_id
+    = DECL_ASSEMBLER_NAME (built_in_decls [BUILT_IN_OMP_GET_THREAD_NUM]);
+  tree num_thr_id
+    = DECL_ASSEMBLER_NAME (built_in_decls [BUILT_IN_OMP_GET_NUM_THREADS]);
+
+  FOR_EACH_BB (bb)
+    for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+      {
+       tree stmt = bsi_stmt (bsi);
+       tree call = get_call_expr_in (stmt);
+       tree decl;
+
+       if (call
+           && (decl = get_callee_fndecl (call))
+           && DECL_EXTERNAL (decl)
+           && TREE_PUBLIC (decl)
+           && DECL_INITIAL (decl) == NULL)
+         {
+           tree built_in;
+
+           if (DECL_NAME (decl) == thr_num_id)
+             built_in = built_in_decls [BUILT_IN_OMP_GET_THREAD_NUM];
+           else if (DECL_NAME (decl) == num_thr_id)
+             built_in = built_in_decls [BUILT_IN_OMP_GET_NUM_THREADS];
+           else
+             continue;
+
+           if (DECL_ASSEMBLER_NAME (decl) != DECL_ASSEMBLER_NAME (built_in)
+               || call_expr_nargs (call) != 0)
+             continue;
+
+           if (flag_exceptions && !TREE_NOTHROW (decl))
+             continue;
+
+           if (TREE_CODE (TREE_TYPE (decl)) != FUNCTION_TYPE
+               || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (decl)))
+                  != TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (built_in))))
+             continue;
+
+           CALL_EXPR_FN (call) = build_fold_addr_expr (built_in);
+         }
+      }
+}
+
 /* Expand the OpenMP parallel directive starting at REGION.  */
 
 static void
@@ -2447,6 +2497,9 @@ expand_omp_parallel (struct omp_region *region)
   entry_stmt = last_stmt (region->entry);
   child_fn = OMP_PARALLEL_FN (entry_stmt);
   child_cfun = DECL_STRUCT_FUNCTION (child_fn);
+  /* If this function has been already instrumented, make sure
+     the child function isn't instrumented again.  */
+  child_cfun->after_tree_profile = cfun->after_tree_profile;
 
   entry_bb = region->entry;
   exit_bb = region->exit;
@@ -2594,12 +2647,33 @@ expand_omp_parallel (struct omp_region *region)
       /* Fix the callgraph edges for child_cfun.  Those for cfun will be
         fixed in a following pass.  */
       push_cfun (child_cfun);
+      if (optimize)
+       optimize_omp_library_calls ();
       rebuild_cgraph_edges ();
+
+      /* Some EH regions might become dead, see PR34608.  If
+        pass_cleanup_cfg isn't the first pass to happen with the
+        new child, these dead EH edges might cause problems.
+        Clean them up now.  */
+      if (flag_exceptions)
+       {
+         basic_block bb;
+         tree save_current = current_function_decl;
+         bool changed = false;
+
+         current_function_decl = child_fn;
+         FOR_EACH_BB (bb)
+           changed |= tree_purge_dead_eh_edges (bb);
+         if (changed)
+           cleanup_tree_cfg ();
+         current_function_decl = save_current;
+       }
       pop_cfun ();
     }
-
+  
   /* Emit a library call to launch the children threads.  */
   expand_parallel_call (region, new_bb, entry_stmt, ws_args);
+  update_ssa (TODO_update_ssa_only_virtuals);
 }
 
 
@@ -3266,6 +3340,16 @@ expand_omp_for (struct omp_region *region)
   extract_omp_for_data (last_stmt (region->entry), &fd);
   region->sched_kind = fd.sched_kind;
 
+  gcc_assert (EDGE_COUNT (region->entry->succs) == 2);
+  BRANCH_EDGE (region->entry)->flags &= ~EDGE_ABNORMAL;
+  FALLTHRU_EDGE (region->entry)->flags &= ~EDGE_ABNORMAL;
+  if (region->cont)
+    {
+      gcc_assert (EDGE_COUNT (region->cont->succs) == 2);
+      BRANCH_EDGE (region->cont)->flags &= ~EDGE_ABNORMAL;
+      FALLTHRU_EDGE (region->cont)->flags &= ~EDGE_ABNORMAL;
+    }
+
   if (fd.sched_kind == OMP_CLAUSE_SCHEDULE_STATIC
       && !fd.have_ordered
       && region->cont != NULL)
@@ -3282,6 +3366,8 @@ expand_omp_for (struct omp_region *region)
       int next_ix = BUILT_IN_GOMP_LOOP_STATIC_NEXT + fn_index;
       expand_omp_for_generic (region, &fd, start_ix, next_ix);
     }
+
+  update_ssa (TODO_update_ssa_only_virtuals);
 }
 
 
@@ -3537,6 +3623,355 @@ expand_omp_synch (struct omp_region *region)
     }
 }
 
+/* A subroutine of expand_omp_atomic.  Attempt to implement the atomic
+   operation as a __sync_fetch_and_op builtin.  INDEX is log2 of the
+   size of the data type, and thus usable to find the index of the builtin
+   decl.  Returns false if the expression is not of the proper form.  */
+
+static bool
+expand_omp_atomic_fetch_op (basic_block load_bb,
+                           tree addr, tree loaded_val,
+                           tree stored_val, int index)
+{
+  enum built_in_function base;
+  tree decl, itype, call;
+  enum insn_code *optab;
+  tree rhs;
+  basic_block store_bb = single_succ (load_bb);
+  block_stmt_iterator bsi;
+  tree stmt;
+
+  /* We expect to find the following sequences:
+   
+   load_bb:
+       OMP_ATOMIC_LOAD (tmp, mem)
+
+   store_bb:
+       val = tmp OP something; (or: something OP tmp)
+       OMP_STORE (val) 
+
+  ???FIXME: Allow a more flexible sequence.  
+  Perhaps use data flow to pick the statements.
+  
+  */
+
+  bsi = bsi_after_labels (store_bb);
+  stmt = bsi_stmt (bsi);
+  if (TREE_CODE (stmt) != GIMPLE_MODIFY_STMT)
+    return false;
+  bsi_next (&bsi);
+  if (TREE_CODE (bsi_stmt (bsi)) != OMP_ATOMIC_STORE)
+    return false;
+
+  if (!operand_equal_p (GIMPLE_STMT_OPERAND (stmt, 0), stored_val, 0))
+    return false;
+
+  rhs = GIMPLE_STMT_OPERAND (stmt, 1);
+
+  /* Check for one of the supported fetch-op operations.  */
+  switch (TREE_CODE (rhs))
+    {
+    case PLUS_EXPR:
+    case POINTER_PLUS_EXPR:
+      base = BUILT_IN_FETCH_AND_ADD_N;
+      optab = sync_add_optab;
+      break;
+    case MINUS_EXPR:
+      base = BUILT_IN_FETCH_AND_SUB_N;
+      optab = sync_add_optab;
+      break;
+    case BIT_AND_EXPR:
+      base = BUILT_IN_FETCH_AND_AND_N;
+      optab = sync_and_optab;
+      break;
+    case BIT_IOR_EXPR:
+      base = BUILT_IN_FETCH_AND_OR_N;
+      optab = sync_ior_optab;
+      break;
+    case BIT_XOR_EXPR:
+      base = BUILT_IN_FETCH_AND_XOR_N;
+      optab = sync_xor_optab;
+      break;
+    default:
+      return false;
+    }
+  /* Make sure the expression is of the proper form.  */
+  if (operand_equal_p (TREE_OPERAND (rhs, 0), loaded_val, 0))
+    rhs = TREE_OPERAND (rhs, 1);
+  else if (commutative_tree_code (TREE_CODE (rhs))
+          && operand_equal_p (TREE_OPERAND (rhs, 1), loaded_val, 0))
+    rhs = TREE_OPERAND (rhs, 0);
+  else
+    return false;
+
+  decl = built_in_decls[base + index + 1];
+  itype = TREE_TYPE (TREE_TYPE (decl));
+
+  if (optab[TYPE_MODE (itype)] == CODE_FOR_nothing)
+    return false;
+
+  bsi = bsi_last (load_bb);
+  gcc_assert (TREE_CODE (bsi_stmt (bsi)) == OMP_ATOMIC_LOAD);
+  call = build_call_expr (decl, 2, addr, fold_convert (itype, rhs));
+  force_gimple_operand_bsi (&bsi, call, true, NULL_TREE, true, BSI_SAME_STMT);
+  bsi_remove (&bsi, true);
+
+  bsi = bsi_last (store_bb);
+  gcc_assert (TREE_CODE (bsi_stmt (bsi)) == OMP_ATOMIC_STORE);
+  bsi_remove (&bsi, true);
+  bsi = bsi_last (store_bb);
+  bsi_remove (&bsi, true);
+
+  if (gimple_in_ssa_p (cfun))
+    update_ssa (TODO_update_ssa_no_phi);
+
+  return true;
+}
+
+/* A subroutine of expand_omp_atomic.  Implement the atomic operation as:
+
+      oldval = *addr;
+      repeat:
+        newval = rhs;   // with oldval replacing *addr in rhs
+       oldval = __sync_val_compare_and_swap (addr, oldval, newval);
+       if (oldval != newval)
+         goto repeat;
+
+   INDEX is log2 of the size of the data type, and thus usable to find the
+   index of the builtin decl.  */
+
+static bool
+expand_omp_atomic_pipeline (basic_block load_bb, basic_block store_bb,
+                           tree addr, tree loaded_val, tree stored_val,
+                           int index)
+{
+  tree loadedi, storedi, initial, new_stored, new_storedi, old_vali;
+  tree type, itype, cmpxchg, iaddr;
+  block_stmt_iterator bsi;
+  basic_block loop_header = single_succ (load_bb);
+  tree phi, x;
+  edge e;
+
+  cmpxchg = built_in_decls[BUILT_IN_VAL_COMPARE_AND_SWAP_N + index + 1];
+  type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (addr)));
+  itype = TREE_TYPE (TREE_TYPE (cmpxchg));
+
+  if (sync_compare_and_swap[TYPE_MODE (itype)] == CODE_FOR_nothing)
+    return false;
+
+  /* Load the initial value, replacing the OMP_ATOMIC_LOAD.  */
+  bsi = bsi_last (load_bb);
+  gcc_assert (TREE_CODE (bsi_stmt (bsi)) == OMP_ATOMIC_LOAD);
+  initial = force_gimple_operand_bsi (&bsi, build_fold_indirect_ref (addr),
+                                     true, NULL_TREE, true, BSI_SAME_STMT);
+  /* Move the value to the LOADED_VAL temporary.  */
+  if (gimple_in_ssa_p (cfun))
+    {
+      gcc_assert (phi_nodes (loop_header) == NULL_TREE);
+      phi = create_phi_node (loaded_val, loop_header);
+      SSA_NAME_DEF_STMT (loaded_val) = phi;
+      SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (phi, single_succ_edge (load_bb)),
+              initial);
+    }
+  else
+    bsi_insert_before (&bsi,
+                      build_gimple_modify_stmt (loaded_val, initial),
+                      BSI_SAME_STMT);
+  bsi_remove (&bsi, true);
+
+  bsi = bsi_last (store_bb);
+  gcc_assert (TREE_CODE (bsi_stmt (bsi)) == OMP_ATOMIC_STORE);
+
+  /* For floating-point values, we'll need to view-convert them to integers
+     so that we can perform the atomic compare and swap.  Simplify the 
+     following code by always setting up the "i"ntegral variables.  */
+  if (INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type))
+    {
+      loadedi = loaded_val;
+      storedi = stored_val;
+      iaddr = addr;
+    }
+  else
+    {
+      loadedi = force_gimple_operand_bsi (&bsi,
+                                         build1 (VIEW_CONVERT_EXPR, itype,
+                                                 loaded_val), true,
+                                         NULL_TREE, true, BSI_SAME_STMT);
+      storedi =
+       force_gimple_operand_bsi (&bsi,
+                                 build1 (VIEW_CONVERT_EXPR, itype,
+                                         stored_val), true, NULL_TREE, true,
+                                 BSI_SAME_STMT);
+      iaddr = fold_convert (build_pointer_type (itype), addr);
+    }
+
+  /* Build the compare&swap statement.  */
+  new_storedi = build_call_expr (cmpxchg, 3, iaddr, loadedi, storedi);
+  new_storedi = force_gimple_operand_bsi (&bsi,
+                                         fold_convert (itype, new_storedi),
+                                         true, NULL_TREE,
+                                         true, BSI_SAME_STMT);
+  if (storedi == stored_val)
+    new_stored = new_storedi;
+  else
+    new_stored = force_gimple_operand_bsi (&bsi,
+                                          build1 (VIEW_CONVERT_EXPR, type,
+                                                  new_storedi), true,
+                                          NULL_TREE, true, BSI_SAME_STMT);
+
+  if (gimple_in_ssa_p (cfun))
+    old_vali = loadedi;
+  else
+    {
+      old_vali = create_tmp_var (itype, NULL);
+      x = build_gimple_modify_stmt (old_vali, loadedi);
+      bsi_insert_before (&bsi, x, BSI_SAME_STMT);
+
+      x = build_gimple_modify_stmt (loaded_val, new_stored);
+      bsi_insert_before (&bsi, x, BSI_SAME_STMT);
+    }
+
+  /* Note that we always perform the comparison as an integer, even for
+     floating point.  This allows the atomic operation to properly 
+     succeed even with NaNs and -0.0.  */
+  x = build3 (COND_EXPR, void_type_node,
+             build2 (NE_EXPR, boolean_type_node,
+                     new_storedi, old_vali), NULL_TREE, NULL_TREE);
+  bsi_insert_before (&bsi, x, BSI_SAME_STMT);
+
+  /* Update cfg.  */
+  e = single_succ_edge (store_bb);
+  e->flags &= ~EDGE_FALLTHRU;
+  e->flags |= EDGE_FALSE_VALUE;
+
+  e = make_edge (store_bb, loop_header, EDGE_TRUE_VALUE);
+
+  /* Copy the new value to loaded_val (we already did that before the condition
+     if we are not in SSA).  */
+  if (gimple_in_ssa_p (cfun))
+    {
+      phi = phi_nodes (loop_header);
+      SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (phi, e), new_stored);
+    }
+
+  /* Remove OMP_ATOMIC_STORE.  */
+  bsi_remove (&bsi, true);
+
+  if (gimple_in_ssa_p (cfun))
+    update_ssa (TODO_update_ssa_no_phi);
+
+  return true;
+}
+
+/* A subroutine of expand_omp_atomic.  Implement the atomic operation as:
+
+                                 GOMP_atomic_start ();
+                                 *addr = rhs;
+                                 GOMP_atomic_end ();
+
+   The result is not globally atomic, but works so long as all parallel
+   references are within #pragma omp atomic directives.  According to
+   responses received from omp@openmp.org, appears to be within spec.
+   Which makes sense, since that's how several other compilers handle
+   this situation as well.  
+   LOADED_VAL and ADDR are the operands of OMP_ATOMIC_LOAD we're expanding. 
+   STORED_VAL is the operand of the matching OMP_ATOMIC_STORE.
+
+   We replace 
+   OMP_ATOMIC_LOAD (loaded_val, addr) with  
+   loaded_val = *addr;
+
+   and replace
+   OMP_ATOMIC_ATORE (stored_val)  with
+   *addr = stored_val;  
+*/
+
+static bool
+expand_omp_atomic_mutex (basic_block load_bb, basic_block store_bb,
+                        tree addr, tree loaded_val, tree stored_val)
+{
+  block_stmt_iterator bsi;
+  tree t;
+
+  bsi = bsi_last (load_bb);
+  gcc_assert (TREE_CODE (bsi_stmt (bsi)) == OMP_ATOMIC_LOAD);
+
+  t = built_in_decls[BUILT_IN_GOMP_ATOMIC_START];
+  t = build_function_call_expr (t, 0);
+  force_gimple_operand_bsi (&bsi, t, true, NULL_TREE, true, BSI_SAME_STMT);
+
+  t = build_gimple_modify_stmt (loaded_val, build_fold_indirect_ref (addr));
+  if (gimple_in_ssa_p (cfun))
+    SSA_NAME_DEF_STMT (loaded_val) = t;
+  bsi_insert_before (&bsi, t, BSI_SAME_STMT);
+  bsi_remove (&bsi, true);
+
+  bsi = bsi_last (store_bb);
+  gcc_assert (TREE_CODE (bsi_stmt (bsi)) == OMP_ATOMIC_STORE);
+
+  t = build_gimple_modify_stmt (build_fold_indirect_ref (unshare_expr (addr)),
+                               stored_val);
+  bsi_insert_before (&bsi, t, BSI_SAME_STMT);
+
+  t = built_in_decls[BUILT_IN_GOMP_ATOMIC_END];
+  t = build_function_call_expr (t, 0);
+  force_gimple_operand_bsi (&bsi, t, true, NULL_TREE, true, BSI_SAME_STMT);
+  bsi_remove (&bsi, true);
+
+  if (gimple_in_ssa_p (cfun))
+    update_ssa (TODO_update_ssa_no_phi);
+  return true;
+}
+
+/* Expand an OMP_ATOMIC statement.  We try to expand 
+   using expand_omp_atomic_fetch_op. If it failed, we try to 
+   call expand_omp_atomic_pipeline, and if it fails too, the
+   ultimate fallback is wrapping the operation in a mutex
+   (expand_omp_atomic_mutex).  REGION is the atomic region built 
+   by build_omp_regions_1().  */ 
+
+static void
+expand_omp_atomic (struct omp_region *region)
+{
+  basic_block load_bb = region->entry, store_bb = region->exit;
+  tree load = last_stmt (load_bb), store = last_stmt (store_bb);
+  tree loaded_val = TREE_OPERAND (load, 0);
+  tree addr = TREE_OPERAND (load, 1);
+  tree stored_val = TREE_OPERAND (store, 0);
+  tree type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (addr)));
+  HOST_WIDE_INT index;
+
+  /* Make sure the type is one of the supported sizes.  */
+  index = tree_low_cst (TYPE_SIZE_UNIT (type), 1);
+  index = exact_log2 (index);
+  if (index >= 0 && index <= 4)
+    {
+      unsigned int align = TYPE_ALIGN_UNIT (type);
+
+      /* __sync builtins require strict data alignment.  */
+      if (exact_log2 (align) >= index)
+       {
+         /* When possible, use specialized atomic update functions.  */
+         if ((INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type))
+             && store_bb == single_succ (load_bb))
+           {
+             if (expand_omp_atomic_fetch_op (load_bb, addr,
+                                             loaded_val, stored_val, index))
+               return;
+           }
+
+         /* If we don't have specialized __sync builtins, try and implement
+            as a compare and swap loop.  */
+         if (expand_omp_atomic_pipeline (load_bb, store_bb, addr,
+                                         loaded_val, stored_val, index))
+           return;
+       }
+    }
+
+  /* The ultimate fallback is wrapping the operation in a mutex.  */
+  expand_omp_atomic_mutex (load_bb, store_bb, addr, loaded_val, stored_val);
+}
+
 
 /* Expand the parallel region tree rooted at REGION.  Expansion
    proceeds in depth-first order.  Innermost regions are expanded
@@ -3549,6 +3984,11 @@ expand_omp (struct omp_region *region)
 {
   while (region)
     {
+      /* First, determine whether this is a combined parallel+workshare
+                region.  */
+      if (region->type == OMP_PARALLEL)
+       determine_parallel_type (region);
+
       if (region->inner)
        expand_omp (region->inner);
 
@@ -3581,6 +4021,11 @@ expand_omp (struct omp_region *region)
          expand_omp_synch (region);
          break;
 
+       case OMP_ATOMIC_LOAD:
+         expand_omp_atomic (region);
+         break;
+
+
        default:
          gcc_unreachable ();
        }
@@ -3591,10 +4036,13 @@ expand_omp (struct omp_region *region)
 
 
 /* Helper for build_omp_regions.  Scan the dominator tree starting at
-   block BB.  PARENT is the region that contains BB.  */
+   block BB.  PARENT is the region that contains BB.  If SINGLE_TREE is
+   true, the function ends once a single tree is built (otherwise, whole
+   forest of OMP constructs may be built).  */
 
 static void
-build_omp_regions_1 (basic_block bb, struct omp_region *parent)
+build_omp_regions_1 (basic_block bb, struct omp_region *parent,
+                    bool single_tree)
 {
   block_stmt_iterator si;
   tree stmt;
@@ -3608,7 +4056,6 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent)
 
       stmt = bsi_stmt (si);
       code = TREE_CODE (stmt);
-
       if (code == OMP_RETURN)
        {
          /* STMT is the return point out of region PARENT.  Mark it
@@ -3618,12 +4065,18 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent)
          region = parent;
          region->exit = bb;
          parent = parent->outer;
-
-         /* If REGION is a parallel region, determine whether it is
-            a combined parallel+workshare region.  */
-         if (region->type == OMP_PARALLEL)
-           determine_parallel_type (region);
        }
+      else if (code == OMP_ATOMIC_STORE)
+       {
+         /* OMP_ATOMIC_STORE is analoguous to OMP_RETURN, but matches with
+            OMP_ATOMIC_LOAD.  */
+         gcc_assert (parent);
+         gcc_assert (parent->type == OMP_ATOMIC_LOAD);
+         region = parent;
+         region->exit = bb;
+         parent = parent->outer;
+       }
+
       else if (code == OMP_CONTINUE)
        {
          gcc_assert (parent);
@@ -3632,7 +4085,7 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent)
       else if (code == OMP_SECTIONS_SWITCH)
        {
          /* OMP_SECTIONS_SWITCH is part of OMP_SECTIONS, and we do nothing for
-            it.  */
+            it.  */ ;
        }
       else
        {
@@ -3643,12 +4096,44 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent)
        }
     }
 
+  if (single_tree && !parent)
+    return;
+
   for (son = first_dom_son (CDI_DOMINATORS, bb);
        son;
        son = next_dom_son (CDI_DOMINATORS, son))
-    build_omp_regions_1 (son, parent);
+    build_omp_regions_1 (son, parent, single_tree);
+}
+
+/* Builds the tree of OMP regions rooted at ROOT, storing it to
+   root_omp_region.  */
+
+static void
+build_omp_regions_root (basic_block root)
+{
+  gcc_assert (root_omp_region == NULL);
+  build_omp_regions_1 (root, NULL, true);
+  gcc_assert (root_omp_region != NULL);
 }
 
+/* Expands omp construct (and its subconstructs) starting in HEAD.  */
+
+void
+omp_expand_local (basic_block head)
+{
+  build_omp_regions_root (head);
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "\nOMP region tree\n\n");
+      dump_omp_region (dump_file, root_omp_region, 0);
+      fprintf (dump_file, "\n");
+    }
+
+  remove_exit_barriers (root_omp_region);
+  expand_omp (root_omp_region);
+
+  free_omp_regions ();
+}
 
 /* Scan the CFG and build a tree of OMP regions.  Return the root of
    the OMP region tree.  */
@@ -3658,7 +4143,7 @@ build_omp_regions (void)
 {
   gcc_assert (root_omp_region == NULL);
   calculate_dominance_info (CDI_DOMINATORS);
-  build_omp_regions_1 (ENTRY_BLOCK_PTR, NULL);
+  build_omp_regions_1 (ENTRY_BLOCK_PTR, NULL, false);
 }