marshalling to implement data sharing and copying clauses.
Contributed by Diego Novillo <dnovillo@redhat.com>
- Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
Free Software Foundation, Inc.
This file is part of GCC.
if (TREE_ADDRESSABLE (decl))
return true;
+ /* lower_send_shared_vars only uses copy-in, but not copy-out
+ for these. */
+ if (TREE_READONLY (decl)
+ || ((TREE_CODE (decl) == RESULT_DECL
+ || TREE_CODE (decl) == PARM_DECL)
+ && DECL_BY_REFERENCE (decl)))
+ return false;
+
/* Disallow copy-in/out in nested parallel if
decl is shared in outer parallel, otherwise
each thread could store the shared variable
in its own copy-in location, making the
variable no longer really shared. */
- if (!TREE_READONLY (decl) && shared_ctx->is_nested)
+ if (shared_ctx->is_nested)
{
omp_context *up;
}
}
- /* For tasks avoid using copy-in/out, unless they are readonly
- (in which case just copy-in is used). As tasks can be
+ /* For tasks avoid using copy-in/out. As tasks can be
deferred or executed in different thread, when GOMP_task
returns, the task hasn't necessarily terminated. */
- if (!TREE_READONLY (decl) && is_task_ctx (shared_ctx))
+ if (is_task_ctx (shared_ctx))
{
tree outer;
maybe_mark_addressable_and_ret:
return omp_copy_decl_2 (var, DECL_NAME (var), TREE_TYPE (var), ctx);
}
+/* Build COMPONENT_REF and set TREE_THIS_VOLATILE and TREE_READONLY on it
+ as appropriate. */
+static tree
+omp_build_component_ref (tree obj, tree field)
+{
+ tree ret = build3 (COMPONENT_REF, TREE_TYPE (field), obj, field, NULL);
+ if (TREE_THIS_VOLATILE (field))
+ TREE_THIS_VOLATILE (ret) |= 1;
+ if (TREE_READONLY (field))
+ TREE_READONLY (ret) |= 1;
+ return ret;
+}
+
/* Build tree nodes to access the field for VAR on the receiver side. */
static tree
field = x;
x = build_simple_mem_ref (ctx->receiver_decl);
- x = build3 (COMPONENT_REF, TREE_TYPE (field), x, field, NULL);
+ x = omp_build_component_ref (x, field);
if (by_ref)
x = build_simple_mem_ref (x);
build_sender_ref (tree var, omp_context *ctx)
{
tree field = lookup_sfield (var, ctx);
- return build3 (COMPONENT_REF, TREE_TYPE (field),
- ctx->sender_decl, field, NULL);
+ return omp_build_component_ref (ctx->sender_decl, field);
}
/* Add a new field for VAR inside the structure CTX->SENDER_DECL. */
old_fn = current_function_decl;
push_cfun (child_cfun);
current_function_decl = child_fn;
- bind = gimplify_body (&DECL_SAVED_TREE (child_fn), child_fn, false);
+ bind = gimplify_body (child_fn, false);
seq = gimple_seq_alloc ();
gimple_seq_add_stmt (&seq, bind);
new_seq = maybe_catch_exception (seq);
/* Check OpenMP nesting restrictions. */
-static void
-check_omp_nesting_restrictions (gimple stmt, omp_context *ctx)
+static bool
+check_omp_nesting_restrictions (gimple stmt, omp_context *ctx)
{
switch (gimple_code (stmt))
{
case GIMPLE_OMP_TASK:
if (is_gimple_call (stmt))
{
- warning (0, "barrier region may not be closely nested inside "
- "of work-sharing, critical, ordered, master or "
- "explicit task region");
- return;
+ error_at (gimple_location (stmt),
+ "barrier region may not be closely nested inside "
+ "of work-sharing, critical, ordered, master or "
+ "explicit task region");
+ return false;
}
- warning (0, "work-sharing region may not be closely nested inside "
- "of work-sharing, critical, ordered, master or explicit "
- "task region");
- return;
+ error_at (gimple_location (stmt),
+ "work-sharing region may not be closely nested inside "
+ "of work-sharing, critical, ordered, master or explicit "
+ "task region");
+ return false;
case GIMPLE_OMP_PARALLEL:
- return;
+ return true;
default:
break;
}
case GIMPLE_OMP_SECTIONS:
case GIMPLE_OMP_SINGLE:
case GIMPLE_OMP_TASK:
- warning (0, "master region may not be closely nested inside "
- "of work-sharing or explicit task region");
- return;
+ error_at (gimple_location (stmt),
+ "master region may not be closely nested inside "
+ "of work-sharing or explicit task region");
+ return false;
case GIMPLE_OMP_PARALLEL:
- return;
+ return true;
default:
break;
}
{
case GIMPLE_OMP_CRITICAL:
case GIMPLE_OMP_TASK:
- warning (0, "ordered region may not be closely nested inside "
- "of critical or explicit task region");
- return;
+ error_at (gimple_location (stmt),
+ "ordered region may not be closely nested inside "
+ "of critical or explicit task region");
+ return false;
case GIMPLE_OMP_FOR:
if (find_omp_clause (gimple_omp_for_clauses (ctx->stmt),
OMP_CLAUSE_ORDERED) == NULL)
- warning (0, "ordered region must be closely nested inside "
+ {
+ error_at (gimple_location (stmt),
+ "ordered region must be closely nested inside "
"a loop region with an ordered clause");
- return;
+ return false;
+ }
+ return true;
case GIMPLE_OMP_PARALLEL:
- return;
+ return true;
default:
break;
}
&& (gimple_omp_critical_name (stmt)
== gimple_omp_critical_name (ctx->stmt)))
{
- warning (0, "critical region may not be nested inside a critical "
- "region with the same name");
- return;
+ error_at (gimple_location (stmt),
+ "critical region may not be nested inside a critical "
+ "region with the same name");
+ return false;
}
break;
default:
break;
}
+ return true;
}
/* Check the OpenMP nesting restrictions. */
if (ctx != NULL)
{
+ bool remove = false;
if (is_gimple_omp (stmt))
- check_omp_nesting_restrictions (stmt, ctx);
+ remove = !check_omp_nesting_restrictions (stmt, ctx);
else if (is_gimple_call (stmt))
{
tree fndecl = gimple_call_fndecl (stmt);
if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
&& DECL_FUNCTION_CODE (fndecl) == BUILT_IN_GOMP_BARRIER)
- check_omp_nesting_restrictions (stmt, ctx);
+ remove = !check_omp_nesting_restrictions (stmt, ctx);
+ }
+ if (remove)
+ {
+ stmt = gimple_build_nop ();
+ gsi_replace (gsi, stmt, false);
}
}
{
int fn_index, start_ix, next_ix;
+ if (fd.chunk_size == NULL
+ && fd.sched_kind == OMP_CLAUSE_SCHEDULE_STATIC)
+ fd.chunk_size = integer_zero_node;
gcc_assert (fd.sched_kind != OMP_CLAUSE_SCHEDULE_AUTO);
fn_index = (fd.sched_kind == OMP_CLAUSE_SCHEDULE_RUNTIME)
? 3 : fd.sched_kind;
unsigned i, casei;
bool exit_reachable = region->cont != NULL;
- gcc_assert (exit_reachable == (region->exit != NULL));
+ gcc_assert (region->exit != NULL);
entry_bb = region->entry;
l0_bb = single_succ (entry_bb);
l1_bb = region->cont;
l2_bb = region->exit;
- if (exit_reachable)
+ if (single_pred_p (l2_bb) && single_pred (l2_bb) == l0_bb)
+ l2 = gimple_block_label (l2_bb);
+ else
{
- if (single_pred_p (l2_bb) && single_pred (l2_bb) == l0_bb)
- l2 = gimple_block_label (l2_bb);
+ /* This can happen if there are reductions. */
+ len = EDGE_COUNT (l0_bb->succs);
+ gcc_assert (len > 0);
+ e = EDGE_SUCC (l0_bb, len - 1);
+ si = gsi_last_bb (e->dest);
+ l2 = NULL_TREE;
+ if (gsi_end_p (si)
+ || gimple_code (gsi_stmt (si)) != GIMPLE_OMP_SECTION)
+ l2 = gimple_block_label (e->dest);
else
- {
- /* This can happen if there are reductions. */
- len = EDGE_COUNT (l0_bb->succs);
- gcc_assert (len > 0);
- e = EDGE_SUCC (l0_bb, len - 1);
- si = gsi_last_bb (e->dest);
- l2 = NULL_TREE;
- if (gsi_end_p (si)
- || gimple_code (gsi_stmt (si)) != GIMPLE_OMP_SECTION)
- l2 = gimple_block_label (e->dest);
- else
- FOR_EACH_EDGE (e, ei, l0_bb->succs)
+ FOR_EACH_EDGE (e, ei, l0_bb->succs)
+ {
+ si = gsi_last_bb (e->dest);
+ if (gsi_end_p (si)
+ || gimple_code (gsi_stmt (si)) != GIMPLE_OMP_SECTION)
{
- si = gsi_last_bb (e->dest);
- if (gsi_end_p (si)
- || gimple_code (gsi_stmt (si)) != GIMPLE_OMP_SECTION)
- {
- l2 = gimple_block_label (e->dest);
- break;
- }
+ l2 = gimple_block_label (e->dest);
+ break;
}
- }
- default_bb = create_empty_bb (l1_bb->prev_bb);
+ }
}
+ if (exit_reachable)
+ default_bb = create_empty_bb (l1_bb->prev_bb);
else
- {
- default_bb = create_empty_bb (l0_bb);
- l2 = gimple_block_label (default_bb);
- }
+ default_bb = create_empty_bb (l0_bb);
/* We will build a switch() with enough cases for all the
GIMPLE_OMP_SECTION regions, a '0' case to handle the end of more work
vnext = NULL_TREE;
}
- i = 0;
- if (exit_reachable)
- {
- t = build_case_label (build_int_cst (unsigned_type_node, 0), NULL, l2);
- VEC_quick_push (tree, label_vec, t);
- i++;
- }
+ t = build_case_label (build_int_cst (unsigned_type_node, 0), NULL, l2);
+ VEC_quick_push (tree, label_vec, t);
+ i = 1;
/* Convert each GIMPLE_OMP_SECTION into a CASE_LABEL_EXPR. */
for (inner = region->inner, casei = 1;
gsi_remove (&si, true);
single_succ_edge (l1_bb)->flags = EDGE_FALLTHRU;
-
- /* Cleanup function replaces GIMPLE_OMP_RETURN in EXIT_BB. */
- si = gsi_last_bb (l2_bb);
- if (gimple_omp_return_nowait_p (gsi_stmt (si)))
- t = builtin_decl_explicit (BUILT_IN_GOMP_SECTIONS_END_NOWAIT);
- else
- t = builtin_decl_explicit (BUILT_IN_GOMP_SECTIONS_END);
- stmt = gimple_build_call (t, 0);
- gsi_insert_after (&si, stmt, GSI_SAME_STMT);
- gsi_remove (&si, true);
}
+ /* Cleanup function replaces GIMPLE_OMP_RETURN in EXIT_BB. */
+ si = gsi_last_bb (l2_bb);
+ if (gimple_omp_return_nowait_p (gsi_stmt (si)))
+ t = builtin_decl_explicit (BUILT_IN_GOMP_SECTIONS_END_NOWAIT);
+ else
+ t = builtin_decl_explicit (BUILT_IN_GOMP_SECTIONS_END);
+ stmt = gimple_build_call (t, 0);
+ gsi_insert_after (&si, stmt, GSI_SAME_STMT);
+ gsi_remove (&si, true);
+
set_immediate_dominator (CDI_DOMINATORS, default_bb, l0_bb);
}
operation as a normal volatile load. */
static bool
-expand_omp_atomic_load (basic_block load_bb, tree addr, tree loaded_val)
+expand_omp_atomic_load (basic_block load_bb, tree addr,
+ tree loaded_val, int index)
{
- /* FIXME */
- (void) load_bb;
- (void) addr;
- (void) loaded_val;
- return false;
+ enum built_in_function tmpbase;
+ gimple_stmt_iterator gsi;
+ basic_block store_bb;
+ location_t loc;
+ gimple stmt;
+ tree decl, call, type, itype;
+
+ gsi = gsi_last_bb (load_bb);
+ stmt = gsi_stmt (gsi);
+ gcc_assert (gimple_code (stmt) == GIMPLE_OMP_ATOMIC_LOAD);
+ loc = gimple_location (stmt);
+
+ /* ??? If the target does not implement atomic_load_optab[mode], and mode
+ is smaller than word size, then expand_atomic_load assumes that the load
+ is atomic. We could avoid the builtin entirely in this case. */
+
+ tmpbase = (enum built_in_function) (BUILT_IN_ATOMIC_LOAD_N + index + 1);
+ decl = builtin_decl_explicit (tmpbase);
+ if (decl == NULL_TREE)
+ return false;
+
+ type = TREE_TYPE (loaded_val);
+ itype = TREE_TYPE (TREE_TYPE (decl));
+
+ call = build_call_expr_loc (loc, decl, 2, addr,
+ build_int_cst (NULL, MEMMODEL_RELAXED));
+ if (!useless_type_conversion_p (type, itype))
+ call = fold_build1_loc (loc, VIEW_CONVERT_EXPR, type, call);
+ call = build2_loc (loc, MODIFY_EXPR, void_type_node, loaded_val, call);
+
+ force_gimple_operand_gsi (&gsi, call, true, NULL_TREE, true, GSI_SAME_STMT);
+ gsi_remove (&gsi, true);
+
+ store_bb = single_succ (load_bb);
+ gsi = gsi_last_bb (store_bb);
+ gcc_assert (gimple_code (gsi_stmt (gsi)) == GIMPLE_OMP_ATOMIC_STORE);
+ gsi_remove (&gsi, true);
+
+ if (gimple_in_ssa_p (cfun))
+ update_ssa (TODO_update_ssa_no_phi);
+
+ return true;
}
/* A subroutine of expand_omp_atomic. Attempt to implement the atomic
operation as a normal volatile store. */
static bool
-expand_omp_atomic_store (basic_block load_bb, tree addr)
+expand_omp_atomic_store (basic_block load_bb, tree addr,
+ tree loaded_val, tree stored_val, int index)
{
- /* FIXME */
- (void) load_bb;
- (void) addr;
- return false;
+ enum built_in_function tmpbase;
+ gimple_stmt_iterator gsi;
+ basic_block store_bb = single_succ (load_bb);
+ location_t loc;
+ gimple stmt;
+ tree decl, call, type, itype;
+ enum machine_mode imode;
+ bool exchange;
+
+ gsi = gsi_last_bb (load_bb);
+ stmt = gsi_stmt (gsi);
+ gcc_assert (gimple_code (stmt) == GIMPLE_OMP_ATOMIC_LOAD);
+
+ /* If the load value is needed, then this isn't a store but an exchange. */
+ exchange = gimple_omp_atomic_need_value_p (stmt);
+
+ gsi = gsi_last_bb (store_bb);
+ stmt = gsi_stmt (gsi);
+ gcc_assert (gimple_code (stmt) == GIMPLE_OMP_ATOMIC_STORE);
+ loc = gimple_location (stmt);
+
+ /* ??? If the target does not implement atomic_store_optab[mode], and mode
+ is smaller than word size, then expand_atomic_store assumes that the store
+ is atomic. We could avoid the builtin entirely in this case. */
+
+ tmpbase = (exchange ? BUILT_IN_ATOMIC_EXCHANGE_N : BUILT_IN_ATOMIC_STORE_N);
+ tmpbase = (enum built_in_function) ((int) tmpbase + index + 1);
+ decl = builtin_decl_explicit (tmpbase);
+ if (decl == NULL_TREE)
+ return false;
+
+ type = TREE_TYPE (stored_val);
+
+ /* Dig out the type of the function's second argument. */
+ itype = TREE_TYPE (decl);
+ itype = TYPE_ARG_TYPES (itype);
+ itype = TREE_CHAIN (itype);
+ itype = TREE_VALUE (itype);
+ imode = TYPE_MODE (itype);
+
+ if (exchange && !can_atomic_exchange_p (imode, true))
+ return false;
+
+ if (!useless_type_conversion_p (itype, type))
+ stored_val = fold_build1_loc (loc, VIEW_CONVERT_EXPR, itype, stored_val);
+ call = build_call_expr_loc (loc, decl, 3, addr, stored_val,
+ build_int_cst (NULL, MEMMODEL_RELAXED));
+ if (exchange)
+ {
+ if (!useless_type_conversion_p (type, itype))
+ call = build1_loc (loc, VIEW_CONVERT_EXPR, type, call);
+ call = build2_loc (loc, MODIFY_EXPR, void_type_node, loaded_val, call);
+ }
+
+ force_gimple_operand_gsi (&gsi, call, true, NULL_TREE, true, GSI_SAME_STMT);
+ gsi_remove (&gsi, true);
+
+ /* Remove the GIMPLE_OMP_ATOMIC_LOAD that we verified above. */
+ gsi = gsi_last_bb (load_bb);
+ gsi_remove (&gsi, true);
+
+ if (gimple_in_ssa_p (cfun))
+ update_ssa (TODO_update_ssa_no_phi);
+
+ return true;
}
/* A subroutine of expand_omp_atomic. Attempt to implement the atomic
loaded_val = *addr;
and replace
- GIMPLE_OMP_ATOMIC_ATORE (stored_val) with
+ GIMPLE_OMP_ATOMIC_STORE (stored_val) with
*addr = stored_val;
*/
/* __sync builtins require strict data alignment. */
if (exact_log2 (align) >= index)
{
- /* Atomic load. FIXME: have some target hook signalize what loads
- are actually atomic? */
+ /* Atomic load. */
if (loaded_val == stored_val
&& (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_INT
|| GET_MODE_CLASS (TYPE_MODE (type)) == MODE_FLOAT)
&& GET_MODE_BITSIZE (TYPE_MODE (type)) <= BITS_PER_WORD
- && expand_omp_atomic_load (load_bb, addr, loaded_val))
+ && expand_omp_atomic_load (load_bb, addr, loaded_val, index))
return;
- /* Atomic store. FIXME: have some target hook signalize what
- stores are actually atomic? */
+ /* Atomic store. */
if ((GET_MODE_CLASS (TYPE_MODE (type)) == MODE_INT
|| GET_MODE_CLASS (TYPE_MODE (type)) == MODE_FLOAT)
&& GET_MODE_BITSIZE (TYPE_MODE (type)) <= BITS_PER_WORD
&& store_bb == single_succ (load_bb)
&& first_stmt (store_bb) == store
- && expand_omp_atomic_store (load_bb, addr))
+ && expand_omp_atomic_store (load_bb, addr, loaded_val,
+ stored_val, index))
return;
/* 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;
- }
+ && store_bb == single_succ (load_bb)
+ && 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. */
sf = (tree) n->value;
sf = *(tree *) pointer_map_contains (tcctx.cb.decl_map, sf);
src = build_simple_mem_ref_loc (loc, sarg);
- src = build3 (COMPONENT_REF, TREE_TYPE (sf), src, sf, NULL);
+ src = omp_build_component_ref (src, sf);
t = build2 (MODIFY_EXPR, TREE_TYPE (*p), *p, src);
append_to_statement_list (t, &list);
}
if (tcctx.cb.decl_map)
sf = *(tree *) pointer_map_contains (tcctx.cb.decl_map, sf);
src = build_simple_mem_ref_loc (loc, sarg);
- src = build3 (COMPONENT_REF, TREE_TYPE (sf), src, sf, NULL);
+ src = omp_build_component_ref (src, sf);
dst = build_simple_mem_ref_loc (loc, arg);
- dst = build3 (COMPONENT_REF, TREE_TYPE (f), dst, f, NULL);
+ dst = omp_build_component_ref (dst, f);
t = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src);
append_to_statement_list (t, &list);
break;
if (tcctx.cb.decl_map)
sf = *(tree *) pointer_map_contains (tcctx.cb.decl_map, sf);
src = build_simple_mem_ref_loc (loc, sarg);
- src = build3 (COMPONENT_REF, TREE_TYPE (sf), src, sf, NULL);
+ src = omp_build_component_ref (src, sf);
if (use_pointer_for_field (decl, NULL) || is_reference (decl))
src = build_simple_mem_ref_loc (loc, src);
}
else
src = decl;
dst = build_simple_mem_ref_loc (loc, arg);
- dst = build3 (COMPONENT_REF, TREE_TYPE (f), dst, f, NULL);
+ dst = omp_build_component_ref (dst, f);
t = lang_hooks.decls.omp_clause_copy_ctor (c, dst, src);
append_to_statement_list (t, &list);
break;
if (tcctx.cb.decl_map)
sf = *(tree *) pointer_map_contains (tcctx.cb.decl_map, sf);
src = build_simple_mem_ref_loc (loc, sarg);
- src = build3 (COMPONENT_REF, TREE_TYPE (sf), src, sf, NULL);
+ src = omp_build_component_ref (src, sf);
if (use_pointer_for_field (decl, NULL))
src = build_simple_mem_ref_loc (loc, src);
}
else
src = decl;
dst = build_simple_mem_ref_loc (loc, arg);
- dst = build3 (COMPONENT_REF, TREE_TYPE (f), dst, f, NULL);
+ dst = omp_build_component_ref (dst, f);
t = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src);
append_to_statement_list (t, &list);
break;
sf = (tree) n->value;
sf = *(tree *) pointer_map_contains (tcctx.cb.decl_map, sf);
src = build_simple_mem_ref_loc (loc, sarg);
- src = build3 (COMPONENT_REF, TREE_TYPE (sf), src, sf, NULL);
+ src = omp_build_component_ref (src, sf);
src = build_simple_mem_ref_loc (loc, src);
dst = build_simple_mem_ref_loc (loc, arg);
- dst = build3 (COMPONENT_REF, TREE_TYPE (f), dst, f, NULL);
+ dst = omp_build_component_ref (dst, f);
t = lang_hooks.decls.omp_clause_copy_ctor (c, dst, src);
append_to_statement_list (t, &list);
n = splay_tree_lookup (ctx->field_map,
df = (tree) n->value;
df = *(tree *) pointer_map_contains (tcctx.cb.decl_map, df);
ptr = build_simple_mem_ref_loc (loc, arg);
- ptr = build3 (COMPONENT_REF, TREE_TYPE (df), ptr, df, NULL);
+ ptr = omp_build_component_ref (ptr, df);
t = build2 (MODIFY_EXPR, TREE_TYPE (ptr), ptr,
build_fold_addr_expr_loc (loc, dst));
append_to_statement_list (t, &list);
lower_omp (gimple_try_eval (stmt), ctx);
lower_omp (gimple_try_cleanup (stmt), ctx);
break;
+ case GIMPLE_TRANSACTION:
+ lower_omp (gimple_transaction_body (stmt), ctx);
+ break;
case GIMPLE_BIND:
lower_omp (gimple_bind_body (stmt), ctx);
break;