openmp: Add omp_all_memory support (C/C++ only so far)

The ugly part is that OpenMP 5.1 made omp_all_memory a reserved identifier
which isn't allowed to be used anywhere but in the depend clause, this is
against how everything else has been handled in OpenMP so far (where
some identifiers could have special meaning in some OpenMP clauses or
pragmas but not elsewhere).
The patch handles it by making it a conditional keyword (for -fopenmp
only) and emitting a better diagnostics when it is used in a primary
expression.  Having a nicer diagnostics when e.g. trying to do
int omp_all_memory;
or
int *omp_all_memory[10];
etc. would mean changing too many spots and hooking into name lookups
to reject declaring any such symbols would be too ugly and I'm afraid
there are way too many spots where one can introduce a name
(variables, functions, namespaces, struct, enum, enumerators, template
arguments, ...).

Otherwise, the handling is quite simple, normal depend clauses lower
into addresses of variables being handed over to the library, for
omp_all_memory I'm using NULL pointers.  omp_all_memory can only be
used with inout or out depend kinds and means that a task is dependent
on all previously created sibling tasks that have any dependency (of
any depend kind) and that any later created sibling tasks will be
dependent on it if they have any dependency.

2022-05-12  Jakub Jelinek  <jakub@redhat.com>

gcc/
	* gimplify.cc (gimplify_omp_depend): Don't build_fold_addr_expr
	if null_pointer_node.
	(gimplify_scan_omp_clauses): Likewise.
	* tree-pretty-print.cc (dump_omp_clause): Print null_pointer_node
	as omp_all_memory.
gcc/c-family/
	* c-common.h (enum rid): Add RID_OMP_ALL_MEMORY.
	* c-omp.cc (c_finish_omp_depobj): Don't build_fold_addr_expr
	if null_pointer_node.
gcc/c/
	* c-parser.cc (c_parse_init): Register omp_all_memory as keyword
	if flag_openmp.
	(c_parser_postfix_expression): Diagnose uses of omp_all_memory
	in postfix expressions.
	(c_parser_omp_variable_list): Handle omp_all_memory in depend
	clause.
	* c-typeck.cc (c_finish_omp_clauses): Handle omp_all_memory
	keyword in depend clause as null_pointer_node, diagnose invalid
	uses.
gcc/cp/
	* lex.cc (init_reswords): Register omp_all_memory as keyword
	if flag_openmp.
	* parser.cc (cp_parser_primary_expression): Diagnose uses of
	omp_all_memory in postfix expressions.
	(cp_parser_omp_var_list_no_open): Handle omp_all_memory in depend
	clause.
	* semantics.cc (finish_omp_clauses): Handle omp_all_memory
	keyword in depend clause as null_pointer_node, diagnose invalid
	uses.
	* pt.cc (tsubst_omp_clause_decl): Pass through omp_all_memory.
gcc/testsuite/
	* c-c++-common/gomp/all-memory-1.c: New test.
	* c-c++-common/gomp/all-memory-2.c: New test.
	* c-c++-common/gomp/all-memory-3.c: New test.
	* g++.dg/gomp/all-memory-1.C: New test.
	* g++.dg/gomp/all-memory-2.C: New test.
libgomp/
	* libgomp.h (struct gomp_task): Add depend_all_memory member.
	* task.c (gomp_init_task): Initialize depend_all_memory.
	(gomp_task_handle_depend): Handle omp_all_memory.
	(gomp_task_run_post_handle_depend_hash): Clear
	parent->depend_all_memory if equal to current task.
	(gomp_task_maybe_wait_for_dependencies): Handle omp_all_memory.
	* testsuite/libgomp.c-c++-common/depend-1.c: New test.
	* testsuite/libgomp.c-c++-common/depend-2.c: New test.
	* testsuite/libgomp.c-c++-common/depend-3.c: New test.
This commit is contained in:
Jakub Jelinek 2022-05-12 08:31:20 +02:00
parent 8585185cc4
commit 7f78783dbe
20 changed files with 817 additions and 36 deletions

View file

@ -219,6 +219,9 @@ enum rid
RID_AT_INTERFACE,
RID_AT_IMPLEMENTATION,
/* OpenMP */
RID_OMP_ALL_MEMORY,
/* Named address support, mapping the keyword to a particular named address
number. Named address space 0 is reserved for the generic address. If
there are more than 254 named addresses, the addr_space_t type will need

View file

@ -757,7 +757,7 @@ c_finish_omp_depobj (location_t loc, tree depobj,
t = build2 (COMPOUND_EXPR, TREE_TYPE (t1), TREE_OPERAND (t, 0),
t1);
}
else
else if (t != null_pointer_node)
t = build_fold_addr_expr (t);
break;
default:

View file

@ -165,6 +165,14 @@ c_parse_init (void)
C_SET_RID_CODE (id, RID_FIRST_INT_N + i);
C_IS_RESERVED_WORD (id) = 1;
}
if (flag_openmp)
{
id = get_identifier ("omp_all_memory");
C_SET_RID_CODE (id, RID_OMP_ALL_MEMORY);
C_IS_RESERVED_WORD (id) = 1;
ridpointers [RID_OMP_ALL_MEMORY] = id;
}
}
/* A parser structure recording information about the state and
@ -10202,6 +10210,13 @@ c_parser_postfix_expression (c_parser *parser)
case RID_GENERIC:
expr = c_parser_generic_selection (parser);
break;
case RID_OMP_ALL_MEMORY:
gcc_assert (flag_openmp);
c_parser_consume_token (parser);
error_at (loc, "%<omp_all_memory%> may only be used in OpenMP "
"%<depend%> clause");
expr.set_error ();
break;
default:
c_parser_error (parser, "expected expression");
expr.set_error ();
@ -13025,7 +13040,19 @@ c_parser_omp_variable_list (c_parser *parser,
if (c_parser_next_token_is_not (parser, CPP_NAME)
|| c_parser_peek_token (parser)->id_kind != C_ID_ID)
{
struct c_expr expr = c_parser_expr_no_commas (parser, NULL);
struct c_expr expr;
if (kind == OMP_CLAUSE_DEPEND
&& c_parser_next_token_is_keyword (parser,
RID_OMP_ALL_MEMORY)
&& (c_parser_peek_2nd_token (parser)->type == CPP_COMMA
|| (c_parser_peek_2nd_token (parser)->type
== CPP_CLOSE_PAREN)))
{
expr.value = ridpointers[RID_OMP_ALL_MEMORY];
c_parser_consume_token (parser);
}
else
expr = c_parser_expr_no_commas (parser, NULL);
if (expr.value != error_mark_node)
{
tree u = build_omp_clause (clause_loc, kind);

View file

@ -14832,6 +14832,18 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
}
if (t == error_mark_node)
remove = true;
else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND
&& t == ridpointers[RID_OMP_ALL_MEMORY])
{
if (OMP_CLAUSE_DEPEND_KIND (c) != OMP_CLAUSE_DEPEND_OUT
&& OMP_CLAUSE_DEPEND_KIND (c) != OMP_CLAUSE_DEPEND_INOUT)
{
error_at (OMP_CLAUSE_LOCATION (c),
"%<omp_all_memory%> used with %<depend%> kind "
"other than %<out%> or %<inout%>");
remove = true;
}
}
else if (!lvalue_p (t))
{
error_at (OMP_CLAUSE_LOCATION (c),
@ -14873,24 +14885,32 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
}
if (!remove)
{
tree addr = build_unary_op (OMP_CLAUSE_LOCATION (c), ADDR_EXPR,
t, false);
if (addr == error_mark_node)
remove = true;
if (t == ridpointers[RID_OMP_ALL_MEMORY])
t = null_pointer_node;
else
{
tree addr = build_unary_op (OMP_CLAUSE_LOCATION (c),
ADDR_EXPR, t, false);
if (addr == error_mark_node)
{
remove = true;
break;
}
t = build_indirect_ref (OMP_CLAUSE_LOCATION (c), addr,
RO_UNARY_STAR);
if (t == error_mark_node)
remove = true;
else if (TREE_CODE (OMP_CLAUSE_DECL (c)) == TREE_LIST
&& TREE_PURPOSE (OMP_CLAUSE_DECL (c))
&& (TREE_CODE (TREE_PURPOSE (OMP_CLAUSE_DECL (c)))
== TREE_VEC))
TREE_VALUE (OMP_CLAUSE_DECL (c)) = t;
else
OMP_CLAUSE_DECL (c) = t;
{
remove = true;
break;
}
}
if (TREE_CODE (OMP_CLAUSE_DECL (c)) == TREE_LIST
&& TREE_PURPOSE (OMP_CLAUSE_DECL (c))
&& (TREE_CODE (TREE_PURPOSE (OMP_CLAUSE_DECL (c)))
== TREE_VEC))
TREE_VALUE (OMP_CLAUSE_DECL (c)) = t;
else
OMP_CLAUSE_DECL (c) = t;
}
break;

View file

@ -273,6 +273,14 @@ init_reswords (void)
C_SET_RID_CODE (id, RID_FIRST_INT_N + i);
set_identifier_kind (id, cik_keyword);
}
if (flag_openmp)
{
id = get_identifier ("omp_all_memory");
C_SET_RID_CODE (id, RID_OMP_ALL_MEMORY);
set_identifier_kind (id, cik_keyword);
ridpointers [RID_OMP_ALL_MEMORY] = id;
}
}
static void

View file

@ -5876,6 +5876,14 @@ cp_parser_primary_expression (cp_parser *parser,
case RID_AT_SELECTOR:
return cp_parser_objc_expression (parser);
case RID_OMP_ALL_MEMORY:
gcc_assert (flag_openmp);
cp_lexer_consume_token (parser->lexer);
error_at (token->location,
"%<omp_all_memory%> may only be used in OpenMP "
"%<depend%> clause");
return error_mark_node;
case RID_TEMPLATE:
if (parser->in_function_body
&& (cp_lexer_peek_nth_token (parser->lexer, 2)->type
@ -36735,6 +36743,15 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
decl = cp_parser_primary_expression (parser, false, false, false,
&idk);
}
else if (kind == OMP_CLAUSE_DEPEND
&& cp_parser_is_keyword (token, RID_OMP_ALL_MEMORY)
&& (cp_lexer_nth_token_is (parser->lexer, 2, CPP_COMMA)
|| cp_lexer_nth_token_is (parser->lexer, 2,
CPP_CLOSE_PAREN)))
{
decl = ridpointers[RID_OMP_ALL_MEMORY];
cp_lexer_consume_token (parser->lexer);
}
else
{
name = cp_parser_id_expression (parser, /*template_p=*/false,

View file

@ -17601,8 +17601,8 @@ static tree
tsubst_omp_clause_decl (tree decl, tree args, tsubst_flags_t complain,
tree in_decl, tree *iterator_cache)
{
if (decl == NULL_TREE)
return NULL_TREE;
if (decl == NULL_TREE || decl == ridpointers[RID_OMP_ALL_MEMORY])
return decl;
/* Handle OpenMP iterators. */
if (TREE_CODE (decl) == TREE_LIST

View file

@ -7815,6 +7815,20 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
}
if (t == error_mark_node)
remove = true;
else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND
&& t == ridpointers[RID_OMP_ALL_MEMORY])
{
if (OMP_CLAUSE_DEPEND_KIND (c) != OMP_CLAUSE_DEPEND_OUT
&& OMP_CLAUSE_DEPEND_KIND (c) != OMP_CLAUSE_DEPEND_INOUT)
{
error_at (OMP_CLAUSE_LOCATION (c),
"%<omp_all_memory%> used with %<depend%> kind "
"other than %<out%> or %<inout%>");
remove = true;
}
if (processing_template_decl)
break;
}
else if (processing_template_decl && TREE_CODE (t) != OVERLOAD)
break;
else if (!lvalue_p (t))
@ -7867,24 +7881,32 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
}
if (!remove)
{
tree addr = cp_build_addr_expr (t, tf_warning_or_error);
if (addr == error_mark_node)
remove = true;
if (t == ridpointers[RID_OMP_ALL_MEMORY])
t = null_pointer_node;
else
{
tree addr = cp_build_addr_expr (t, tf_warning_or_error);
if (addr == error_mark_node)
{
remove = true;
break;
}
t = cp_build_indirect_ref (OMP_CLAUSE_LOCATION (c),
addr, RO_UNARY_STAR,
tf_warning_or_error);
if (t == error_mark_node)
remove = true;
else if (TREE_CODE (OMP_CLAUSE_DECL (c)) == TREE_LIST
&& TREE_PURPOSE (OMP_CLAUSE_DECL (c))
&& (TREE_CODE (TREE_PURPOSE (OMP_CLAUSE_DECL (c)))
== TREE_VEC))
TREE_VALUE (OMP_CLAUSE_DECL (c)) = t;
else
OMP_CLAUSE_DECL (c) = t;
{
remove = true;
break;
}
}
if (TREE_CODE (OMP_CLAUSE_DECL (c)) == TREE_LIST
&& TREE_PURPOSE (OMP_CLAUSE_DECL (c))
&& (TREE_CODE (TREE_PURPOSE (OMP_CLAUSE_DECL (c)))
== TREE_VEC))
TREE_VALUE (OMP_CLAUSE_DECL (c)) = t;
else
OMP_CLAUSE_DECL (c) = t;
}
break;
case OMP_CLAUSE_DETACH:

View file

@ -8623,7 +8623,8 @@ gimplify_omp_depend (tree *list_p, gimple_seq *pre_p)
}
if (error_operand_p (TREE_VALUE (t)))
return 2;
TREE_VALUE (t) = build_fold_addr_expr (TREE_VALUE (t));
if (TREE_VALUE (t) != null_pointer_node)
TREE_VALUE (t) = build_fold_addr_expr (TREE_VALUE (t));
r = build4 (ARRAY_REF, ptr_type_node, array, cnts[i],
NULL_TREE, NULL_TREE);
tem = build2_loc (OMP_CLAUSE_LOCATION (c), MODIFY_EXPR,
@ -8650,7 +8651,8 @@ gimplify_omp_depend (tree *list_p, gimple_seq *pre_p)
}
if (error_operand_p (OMP_CLAUSE_DECL (c)))
return 2;
OMP_CLAUSE_DECL (c) = build_fold_addr_expr (OMP_CLAUSE_DECL (c));
if (OMP_CLAUSE_DECL (c) != null_pointer_node)
OMP_CLAUSE_DECL (c) = build_fold_addr_expr (OMP_CLAUSE_DECL (c));
if (gimplify_expr (&OMP_CLAUSE_DECL (c), pre_p, NULL,
is_gimple_val, fb_rvalue) == GS_ERROR)
return 2;
@ -10346,12 +10348,15 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
remove = true;
break;
}
OMP_CLAUSE_DECL (c) = build_fold_addr_expr (OMP_CLAUSE_DECL (c));
if (gimplify_expr (&OMP_CLAUSE_DECL (c), pre_p, NULL,
is_gimple_val, fb_rvalue) == GS_ERROR)
if (OMP_CLAUSE_DECL (c) != null_pointer_node)
{
remove = true;
break;
OMP_CLAUSE_DECL (c) = build_fold_addr_expr (OMP_CLAUSE_DECL (c));
if (gimplify_expr (&OMP_CLAUSE_DECL (c), pre_p, NULL,
is_gimple_val, fb_rvalue) == GS_ERROR)
{
remove = true;
break;
}
}
if (code == OMP_TASK)
ctx->has_depend = true;

View file

@ -0,0 +1,52 @@
int omp_all_memory; /* { dg-error "expected" } */
void
foo (void)
{
int p = (&omp_all_memory)[0]; /* { dg-error "'omp_all_memory' may only be used in OpenMP 'depend' clause" } */
}
void
bar (void)
{
int *omp_all_memory; /* { dg-error "expected" } */
}
void
baz (void)
{
struct omp_all_memory { int a; }; /* { dg-error "expected" } */
}
void
qux (void)
{
union omp_all_memory { int a; }; /* { dg-error "expected" } */
}
void
corge (void)
{
enum omp_all_memory { OAM; }; /* { dg-error "expected" } */
}
void
garply (void)
{
enum E { omp_all_memory }; } /* { dg-error "expected" } */
void
boo (void)
{
int x, y;
#pragma omp task private (omp_all_memory) /* { dg-error "expected" } */
;
#pragma omp task depend(inout: *&omp_all_memory) /* { dg-error "'omp_all_memory' may only be used in OpenMP 'depend' clause" } */
;
#pragma omp task depend(inout: omp_all_memory[0]) /* { dg-error "'omp_all_memory' may only be used in OpenMP 'depend' clause" } */
;
#pragma omp task depend(in: omp_all_memory) /* { dg-error "'omp_all_memory' used with 'depend' kind other than 'out' or 'inout'" } */
;
#pragma omp task depend(mutexinoutset: omp_all_memory) /* { dg-error "'omp_all_memory' used with 'depend' kind other than 'out' or 'inout'" } */
;
}

View file

@ -0,0 +1,55 @@
/* { dg-options "-fno-openmp" } */
int omp_all_memory; /* { dg-bogus "expected" } */
void
foo (void)
{
int p = (&omp_all_memory)[0]; /* { dg-bogus "'omp_all_memory' may only be used in OpenMP 'depend' clause" } */
}
void
bar (void)
{
int *omp_all_memory; /* { dg-bogus "expected" } */
}
void
baz (void)
{
struct omp_all_memory { int a; }; /* { dg-bogus "expected" } */
}
void
qux (void)
{
union omp_all_memory { int a; }; /* { dg-bogus "expected" } */
}
void
corge (void)
{
enum omp_all_memory { OAM }; /* { dg-bogus "expected" } */
}
void
garply (void)
{
enum E { omp_all_memory }; /* { dg-bogus "expected" } */
}
void
boo (void)
{
int x, y;
#pragma omp task private (omp_all_memory)
;
#pragma omp task depend(inout: *&omp_all_memory)
;
#pragma omp task depend(inout: omp_all_memory[0])
;
#pragma omp task depend(in: omp_all_memory)
;
#pragma omp task depend(mutexinoutset: omp_all_memory)
;
}

View file

@ -0,0 +1,22 @@
typedef struct __attribute__((__aligned__ (sizeof (void *)))) omp_depend_t {
char __omp_depend_t__[2 * sizeof (void *)];
} omp_depend_t;
omp_depend_t z;
void
foo (void)
{
int x = 0, y = 0;
#pragma omp task depend(out: omp_all_memory)
;
#pragma omp task depend(inout: omp_all_memory)
;
#pragma omp task depend(out: x, omp_all_memory, y)
;
#pragma omp task depend(inout: omp_all_memory, y)
;
#pragma omp task depend(out: x, omp_all_memory)
;
#pragma omp depobj (z) depend (inout: omp_all_memory)
}

View file

@ -0,0 +1,24 @@
namespace A
{
namespace omp_all_memory // { dg-error "expected" }
{
}
}
namespace B
{
template <int N>
void omp_all_memory () {} // { dg-error "expected" }
}
namespace C
{
template <int N>
struct omp_all_memory {}; // { dg-error "expected" }
}
namespace D
{
template <int omp_all_memory> // { dg-error "expected" }
struct S {};
}

View file

@ -0,0 +1,26 @@
// { dg-options "-fno-openmp" }
namespace A
{
namespace omp_all_memory // { dg-bogus "expected" }
{
}
}
namespace B
{
template <int N>
void omp_all_memory () {} // { dg-bogus "expected" }
}
namespace C
{
template <int N>
struct omp_all_memory {}; // { dg-bogus "expected" }
}
namespace D
{
template <int omp_all_memory> // { dg-bogus "expected" }
struct S {};
}

View file

@ -850,7 +850,10 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, dump_flags_t flags)
pp_string (pp, name);
pp_colon (pp);
}
dump_generic_node (pp, t, spc, flags, false);
if (t == null_pointer_node)
pp_string (pp, "omp_all_memory");
else
dump_generic_node (pp, t, spc, flags, false);
pp_right_paren (pp);
}
break;

View file

@ -574,6 +574,8 @@ struct gomp_task
struct gomp_dependers_vec *dependers;
struct htab *depend_hash;
struct gomp_taskwait *taskwait;
/* Last depend({,in}out:omp_all_memory) child if any. */
struct gomp_task *depend_all_memory;
/* Number of items in DEPEND. */
size_t depend_count;
/* Number of tasks this task depends on. Once this counter reaches

View file

@ -80,6 +80,7 @@ gomp_init_task (struct gomp_task *task, struct gomp_task *parent_task,
task->dependers = NULL;
task->depend_hash = NULL;
task->taskwait = NULL;
task->depend_all_memory = NULL;
task->depend_count = 0;
task->completion_sem = NULL;
task->deferred_p = false;
@ -171,6 +172,7 @@ gomp_task_handle_depend (struct gomp_task *task, struct gomp_task *parent,
size_t ndepend = (uintptr_t) depend[0];
size_t i;
hash_entry_type ent;
bool all_memory = false;
if (ndepend)
{
@ -181,6 +183,7 @@ gomp_task_handle_depend (struct gomp_task *task, struct gomp_task *parent,
{
task->depend[i].addr = depend[2 + i];
task->depend[i].is_in = i >= nout;
all_memory |= i < nout && depend[2 + i] == NULL;
}
}
else
@ -201,6 +204,8 @@ gomp_task_handle_depend (struct gomp_task *task, struct gomp_task *parent,
{
case GOMP_DEPEND_OUT:
case GOMP_DEPEND_INOUT:
all_memory |= d[0] == NULL;
break;
case GOMP_DEPEND_MUTEXINOUTSET:
break;
case GOMP_DEPEND_IN:
@ -226,8 +231,126 @@ gomp_task_handle_depend (struct gomp_task *task, struct gomp_task *parent,
task->depend[n++].is_in = 1;
}
}
task->depend_count = ndepend;
task->num_dependees = 0;
if (__builtin_expect (parent->depend_all_memory && ndepend, false))
{
struct gomp_task *tsk = parent->depend_all_memory;
if (tsk->dependers == NULL)
{
tsk->dependers
= gomp_malloc (sizeof (struct gomp_dependers_vec)
+ 6 * sizeof (struct gomp_task *));
tsk->dependers->n_elem = 1;
tsk->dependers->allocated = 6;
tsk->dependers->elem[0] = task;
}
else
{
if (tsk->dependers->n_elem == tsk->dependers->allocated)
{
tsk->dependers->allocated
= tsk->dependers->allocated * 2 + 2;
tsk->dependers
= gomp_realloc (tsk->dependers,
sizeof (struct gomp_dependers_vec)
+ (tsk->dependers->allocated
* sizeof (struct gomp_task *)));
}
tsk->dependers->elem[tsk->dependers->n_elem++] = task;
}
task->num_dependees++;
}
if (__builtin_expect (all_memory, false))
{
/* A task with depend(inout: omp_all_memory) depends on all previous
sibling tasks which have any dependencies and all later sibling
tasks which have any dependencies depend on it. */
task->depend_count = 1;
task->depend[0].addr = NULL;
task->depend[0].next = NULL;
task->depend[0].prev = NULL;
task->depend[0].task = task;
task->depend[0].redundant = true;
task->depend[0].redundant_out = false;
if (parent->depend_hash)
{
/* Inlined htab_traverse + htab_clear. All newer siblings can
just depend on this task. Add dependencies on all previous
sibling tasks with dependencies and make them redundant and
clear the hash table. */
hash_entry_type *slot = &parent->depend_hash->entries[0];
hash_entry_type *end = slot + htab_size (parent->depend_hash);
for (; slot != end; ++slot)
{
if (*slot == HTAB_EMPTY_ENTRY)
continue;
if (*slot != HTAB_DELETED_ENTRY)
{
for (ent = *slot; ent; ent = ent->next)
{
struct gomp_task *tsk = ent->task;
if (ent->redundant_out)
break;
ent->redundant = true;
if (tsk->dependers == NULL)
{
tsk->dependers
= gomp_malloc (sizeof (struct gomp_dependers_vec)
+ 6 * sizeof (struct gomp_task *));
tsk->dependers->n_elem = 1;
tsk->dependers->allocated = 6;
tsk->dependers->elem[0] = task;
task->num_dependees++;
continue;
}
/* We already have some other dependency on tsk from
earlier depend clause. */
else if (tsk->dependers->n_elem
&& (tsk->dependers->elem[tsk->dependers->n_elem
- 1] == task))
continue;
else if (tsk->dependers->n_elem
== tsk->dependers->allocated)
{
tsk->dependers->allocated
= tsk->dependers->allocated * 2 + 2;
tsk->dependers
= gomp_realloc (tsk->dependers,
sizeof (struct gomp_dependers_vec)
+ (tsk->dependers->allocated
* sizeof (struct gomp_task *)));
}
tsk->dependers->elem[tsk->dependers->n_elem++] = task;
task->num_dependees++;
}
while (ent)
{
ent->redundant = true;
ent = ent->next;
}
}
*slot = HTAB_EMPTY_ENTRY;
}
if (htab_size (parent->depend_hash) <= 32)
{
parent->depend_hash->n_elements = 0;
parent->depend_hash->n_deleted = 0;
}
else
{
/* Shrink the hash table if it would be too large.
We don't want to walk e.g. megabytes of empty hash
table for every depend(inout: omp_all_memory). */
free (parent->depend_hash);
parent->depend_hash = htab_create (12);
}
}
parent->depend_all_memory = task;
return;
}
task->depend_count = ndepend;
if (parent->depend_hash == NULL)
parent->depend_hash = htab_create (2 * ndepend > 12 ? 2 * ndepend : 12);
for (i = 0; i < ndepend; i++)
@ -1175,6 +1298,8 @@ gomp_task_run_post_handle_depend_hash (struct gomp_task *child_task)
struct gomp_task *parent = child_task->parent;
size_t i;
if (parent->depend_all_memory == child_task)
parent->depend_all_memory = NULL;
for (i = 0; i < child_task->depend_count; i++)
if (!child_task->depend[i].redundant)
{
@ -1738,6 +1863,17 @@ gomp_task_maybe_wait_for_dependencies (void **depend)
n = 5;
}
gomp_mutex_lock (&team->task_lock);
if (__builtin_expect (task->depend_all_memory && ndepend, false))
{
struct gomp_task *tsk = task->depend_all_memory;
if (!tsk->parent_depends_on)
{
tsk->parent_depends_on = true;
++num_awaited;
if (tsk->num_dependees == 0 && tsk->kind == GOMP_TASK_WAITING)
priority_queue_upgrade_task (tsk, task);
}
}
for (i = 0; i < ndepend; i++)
{
elem.addr = depend[i + n];
@ -1760,6 +1896,36 @@ gomp_task_maybe_wait_for_dependencies (void **depend)
}
elem.addr = d[0];
}
if (__builtin_expect (elem.addr == NULL && !elem.is_in, false))
{
size_t size = htab_size (task->depend_hash);
if (htab_elements (task->depend_hash) * 8 < size && size > 32)
htab_expand (task->depend_hash);
/* depend(inout: omp_all_memory) - depend on all previous
sibling tasks that do have dependencies. Inlined
htab_traverse. */
hash_entry_type *slot = &task->depend_hash->entries[0];
hash_entry_type *end = slot + htab_size (task->depend_hash);
for (; slot != end; ++slot)
{
if (*slot == HTAB_EMPTY_ENTRY || *slot == HTAB_DELETED_ENTRY)
continue;
for (ent = *slot; ent; ent = ent->next)
{
struct gomp_task *tsk = ent->task;
if (!tsk->parent_depends_on)
{
tsk->parent_depends_on = true;
++num_awaited;
if (tsk->num_dependees == 0
&& tsk->kind == GOMP_TASK_WAITING)
priority_queue_upgrade_task (tsk, task);
}
}
}
break;
}
ent = htab_find (task->depend_hash, &elem);
for (; ent; ent = ent->next)
if (elem.is_in && ent->is_in)

View file

@ -0,0 +1,110 @@
#include <stdlib.h>
#include <unistd.h>
void
test (int ifval)
{
int a[8], b[8], i;
for (i = 0; i < 8; i++)
{
a[i] = i;
b[i] = 2 * i;
}
#pragma omp parallel
#pragma omp single
{
#pragma omp task shared(a) depend(in: a[0])
{
usleep (5000);
a[0] = 42;
}
#pragma omp task shared(a) depend(out: a[1])
{
usleep (5000);
a[1] = 43;
}
#pragma omp task shared(a) depend(inout: a[2])
{
usleep (5000);
a[2] = 44;
}
#pragma omp task shared(a) depend(mutexinoutset: a[3])
{
usleep (5000);
a[3] = 45;
}
#pragma omp task shared(a)
{
usleep (15000);
a[4] = 46;
}
#pragma omp task shared(b) depend(in: b[0])
{
usleep (5000);
b[0] = 47;
}
#pragma omp task shared(b) depend(in: b[4])
{
usleep (5000);
b[4] = 48;
}
/* None of the above tasks depend on each other.
The following task depends on all but the a[4] = 46; one. */
#pragma omp task shared(a, b) depend(out: omp_all_memory) private(i) if(ifval)
{
if (a[0] != 42 || a[1] != 43 || a[2] != 44 || a[3] != 45
|| a[5] != 5 || a[6] != 6 || a[7] != 7
|| b[0] != 47 || b[1] != 2 || b[2] != 4 || b[3] != 6
|| b[4] != 48 || b[5] != 10 || b[6] != 12 || b[7] != 14)
abort ();
for (i = 0; i < 8; ++i)
if (i != 4)
a[i] = 3 * i + 7;
for (i = 0; i < 8; ++i)
b[i] = 4 * i - 7;
}
/* The following task depends on both b[0] = 47; and
above omp_all_memory tasks, but as the latter depends on
the former, effectively it is dependent just on the omp_all_memory
task. */
#pragma omp task shared(b) depend(inout: b[0])
{
usleep (5000);
b[0] = 49;
}
/* The following task depends on all the above except a[4] = 46; one,
but it can be reduced to dependency on the above omp_all_memory
one and b[0] = 49; one. */
#pragma omp task shared(a, b) depend(inout: b[7], omp_all_memory, b[6]) \
private(i) if(ifval)
{
for (i = 0; i < 8; ++i)
if (i != 4)
{
if (a[i] != 3 * i + 7)
abort ();
a[i] = 5 * i + 50;
}
if (b[0] != 49)
abort ();
b[0] = 6 * i + 57;
for (i = 1; i < 8; ++i)
{
if (b[i] != 4 * i - 7)
abort ();
b[i] = 6 * i + 57;
}
}
#pragma omp taskwait
if (a[4] != 46)
abort ();
}
}
int
main ()
{
test (1);
test (0);
return 0;
}

View file

@ -0,0 +1,116 @@
#include <omp.h>
#include <stdlib.h>
#include <unistd.h>
void
test (int ifval)
{
int a[8], b[8], i;
omp_depend_t d1, d2;
#pragma omp depobj (d1) depend(inout: omp_all_memory)
#pragma omp depobj (d2) depend(out: omp_all_memory)
for (i = 0; i < 8; i++)
{
a[i] = i;
b[i] = 2 * i;
}
#pragma omp parallel
#pragma omp single
{
#pragma omp task shared(a) depend(in: a[0])
{
usleep (5000);
a[0] = 42;
}
#pragma omp task shared(a) depend(out: a[1])
{
usleep (5000);
a[1] = 43;
}
#pragma omp task shared(a) depend(inout: a[2])
{
usleep (5000);
a[2] = 44;
}
#pragma omp task shared(a) depend(mutexinoutset: a[3])
{
usleep (5000);
a[3] = 45;
}
#pragma omp task shared(a)
{
usleep (15000);
a[4] = 46;
}
#pragma omp task shared(b) depend(in: b[0])
{
usleep (5000);
b[0] = 47;
}
#pragma omp task shared(b) depend(in: b[4])
{
usleep (5000);
b[4] = 48;
}
/* None of the above tasks depend on each other.
The following task depends on all but the a[4] = 46; one. */
#pragma omp task shared(a, b) depend(depobj: d1) private(i) if(ifval)
{
if (a[0] != 42 || a[1] != 43 || a[2] != 44 || a[3] != 45
|| a[5] != 5 || a[6] != 6 || a[7] != 7
|| b[0] != 47 || b[1] != 2 || b[2] != 4 || b[3] != 6
|| b[4] != 48 || b[5] != 10 || b[6] != 12 || b[7] != 14)
abort ();
for (i = 0; i < 8; ++i)
if (i != 4)
a[i] = 3 * i + 7;
for (i = 0; i < 8; ++i)
b[i] = 4 * i - 7;
}
/* The following task depends on both b[0] = 47; and
above omp_all_memory tasks, but as the latter depends on
the former, effectively it is dependent just on the omp_all_memory
task. */
#pragma omp task shared(b) depend(inout: b[0])
{
usleep (5000);
b[0] = 49;
}
/* The following task depends on all the above except a[4] = 46; one,
but it can be reduced to dependency on the above omp_all_memory
one and b[0] = 49; one. */
#pragma omp task shared(a, b) depend(inout: b[6]) depend(depobj: d2) \
depend(out: b[7]) private(i) if(ifval)
{
for (i = 0; i < 8; ++i)
if (i != 4)
{
if (a[i] != 3 * i + 7)
abort ();
a[i] = 5 * i + 50;
}
if (b[0] != 49)
abort ();
b[0] = 6 * i + 57;
for (i = 1; i < 8; ++i)
{
if (b[i] != 4 * i - 7)
abort ();
b[i] = 6 * i + 57;
}
}
#pragma omp taskwait
if (a[4] != 46)
abort ();
}
#pragma omp depobj (d2) destroy
#pragma omp depobj (d1) destroy
}
int
main ()
{
test (1);
test (0);
return 0;
}

View file

@ -0,0 +1,103 @@
#include <stdlib.h>
#include <unistd.h>
int
main ()
{
int a[8], b[8], i;
for (i = 0; i < 8; i++)
{
a[i] = i;
b[i] = 2 * i;
}
#pragma omp parallel
#pragma omp single
{
#pragma omp task shared(a) depend(in: a[0])
{
usleep (5000);
a[0] = 42;
}
#pragma omp task shared(a) depend(out: a[1])
{
usleep (5000);
a[1] = 43;
}
#pragma omp task shared(a) depend(inout: a[2])
{
usleep (5000);
a[2] = 44;
}
#pragma omp task shared(a) depend(mutexinoutset: a[3])
{
usleep (5000);
a[3] = 45;
}
#pragma omp task shared(a)
{
usleep (15000);
a[4] = 46;
}
#pragma omp task shared(b) depend(in: b[0])
{
usleep (5000);
b[0] = 47;
}
#pragma omp task shared(b) depend(in: b[4])
{
usleep (5000);
b[4] = 48;
}
/* None of the above tasks depend on each other.
The following task depends on all but the a[4] = 46; one. */
#pragma omp task shared(a, b) depend(iterator (j=0:7), inout: omp_all_memory) private(i)
{
if (a[0] != 42 || a[1] != 43 || a[2] != 44 || a[3] != 45
|| a[5] != 5 || a[6] != 6 || a[7] != 7
|| b[0] != 47 || b[1] != 2 || b[2] != 4 || b[3] != 6
|| b[4] != 48 || b[5] != 10 || b[6] != 12 || b[7] != 14)
abort ();
for (i = 0; i < 8; ++i)
if (i != 4)
a[i] = 3 * i + 7;
for (i = 0; i < 8; ++i)
b[i] = 4 * i - 7;
}
/* The following task depends on both b[0] = 47; and
above omp_all_memory tasks, but as the latter depends on
the former, effectively it is dependent just on the omp_all_memory
task. */
#pragma omp task shared(b) depend(inout: b[0])
{
usleep (5000);
b[0] = 49;
}
/* The following task depends on all the above except a[4] = 46; one,
but it can be reduced to dependency on the above omp_all_memory
one and b[0] = 49; one. */
#pragma omp task shared(a, b) depend(inout: b[7]) depend(iterator(j=4:5), out: omp_all_memory) \
depend(inout: b[6]) private(i)
{
for (i = 0; i < 8; ++i)
if (i != 4)
{
if (a[i] != 3 * i + 7)
abort ();
a[i] = 5 * i + 50;
}
if (b[0] != 49)
abort ();
b[0] = 6 * i + 57;
for (i = 1; i < 8; ++i)
{
if (b[i] != 4 * i - 7)
abort ();
b[i] = 6 * i + 57;
}
}
#pragma omp taskwait
if (a[4] != 46)
abort ();
}
return 0;
}