OpenMP: C front end support for imperfectly-nested loops
OpenMP 5.0 removed the restriction that multiple collapsed loops must be perfectly nested, allowing "intervening code" (including nested BLOCKs) before or after each nested loop. In GCC this code is moved into the inner loop body by the respective front ends. This patch changes the C front end to use recursive descent parsing on nested loops within an "omp for" construct, rather than an iterative approach, in order to preserve proper nesting of compound statements. New common C/C++ testcases are in a separate patch. gcc/c-family/ChangeLog * c-common.h (c_omp_check_loop_binding_exprs): Declare. * c-omp.cc: Include tree-iterator.h. (find_binding_in_body): New. (check_loop_binding_expr_r): New. (LOCATION_OR): New. (check_looop_binding_expr): New. (c_omp_check_loop_binding_exprs): New. gcc/c/ChangeLog * c-parser.cc (struct c_parser): Add omp_for_parse_state field. (struct omp_for_parse_data): New. (check_omp_intervening_code): New. (add_structured_block_stmt): New. (c_parser_compound_statement_nostart): Recognize intervening code, nested loops, and other things that need special handling in OpenMP loop constructs. (c_parser_while_statement): Error on loop in intervening code. (c_parser_do_statement): Likewise. (c_parser_for_statement): Likewise. (c_parser_postfix_expression_after_primary): Error on calls to the OpenMP runtime in intervening code. (c_parser_pragma): Error on OpenMP pragmas in intervening code. (c_parser_omp_loop_nest): New. (c_parser_omp_for_loop): Rewrite to use recursive descent, calling c_parser_omp_loop_nest to do the heavy lifting. gcc/ChangeLog * omp-api.h: New. * omp-general.cc (omp_runtime_api_procname): New. (omp_runtime_api_call): Moved here from omp-low.cc, and make non-static. * omp-general.h: Include omp-api.h. * omp-low.cc (omp_runtime_api_call): Delete this copy. gcc/testsuite/ChangeLog * c-c++-common/goacc/collapse-1.c: Update for new C error behavior. * c-c++-common/goacc/tile-2.c: Likewise. * gcc.dg/gomp/collapse-1.c: Likewise.
This commit is contained in:
parent
a62c8324e7
commit
143151ac20
10 changed files with 940 additions and 394 deletions
|
@ -1299,6 +1299,7 @@ extern tree c_finish_omp_for (location_t, enum tree_code, tree, tree, tree,
|
|||
extern bool c_omp_check_loop_iv (tree, tree, walk_tree_lh);
|
||||
extern bool c_omp_check_loop_iv_exprs (location_t, enum tree_code, tree, int,
|
||||
tree, tree, tree, walk_tree_lh);
|
||||
extern bool c_omp_check_loop_binding_exprs (tree, vec<tree> *);
|
||||
extern tree c_finish_oacc_wait (location_t, tree, tree);
|
||||
extern tree c_oacc_split_loop_clauses (tree, tree *, bool);
|
||||
extern void c_omp_split_clauses (location_t, enum tree_code, omp_clause_mask,
|
||||
|
|
|
@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "gimplify.h"
|
||||
#include "langhooks.h"
|
||||
#include "bitmap.h"
|
||||
#include "tree-iterator.h"
|
||||
|
||||
|
||||
/* Complete a #pragma oacc wait construct. LOC is the location of
|
||||
|
@ -1728,6 +1729,156 @@ c_omp_check_loop_iv_exprs (location_t stmt_loc, enum tree_code code,
|
|||
return !data.fail;
|
||||
}
|
||||
|
||||
|
||||
/* Helper function for c_omp_check_loop_binding_exprs: look for a binding
|
||||
of DECL in BODY. Only traverse things that might be containers for
|
||||
intervening code in an OMP loop. Returns the BIND_EXPR or DECL_EXPR
|
||||
if found, otherwise null. */
|
||||
|
||||
static tree
|
||||
find_binding_in_body (tree decl, tree body)
|
||||
{
|
||||
if (!body)
|
||||
return NULL_TREE;
|
||||
|
||||
switch (TREE_CODE (body))
|
||||
{
|
||||
case BIND_EXPR:
|
||||
for (tree b = BIND_EXPR_VARS (body); b; b = DECL_CHAIN (b))
|
||||
if (b == decl)
|
||||
return body;
|
||||
return find_binding_in_body (decl, BIND_EXPR_BODY (body));
|
||||
|
||||
case DECL_EXPR:
|
||||
if (DECL_EXPR_DECL (body) == decl)
|
||||
return body;
|
||||
return NULL_TREE;
|
||||
|
||||
case STATEMENT_LIST:
|
||||
for (tree_stmt_iterator si = tsi_start (body); !tsi_end_p (si);
|
||||
tsi_next (&si))
|
||||
{
|
||||
tree b = find_binding_in_body (decl, tsi_stmt (si));
|
||||
if (b)
|
||||
return b;
|
||||
}
|
||||
return NULL_TREE;
|
||||
|
||||
case OMP_STRUCTURED_BLOCK:
|
||||
return find_binding_in_body (decl, OMP_BODY (body));
|
||||
|
||||
default:
|
||||
return NULL_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Traversal function for check_loop_binding_expr, to diagnose
|
||||
errors when a binding made in intervening code is referenced outside
|
||||
of the loop. Returns non-null if such a reference is found. DATA points
|
||||
to the tree containing the loop body. */
|
||||
|
||||
static tree
|
||||
check_loop_binding_expr_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
|
||||
void *data)
|
||||
{
|
||||
tree body = *(tree *)data;
|
||||
|
||||
if (DECL_P (*tp) && find_binding_in_body (*tp, body))
|
||||
return *tp;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Helper macro used below. */
|
||||
|
||||
#define LOCATION_OR(loc1, loc2) \
|
||||
((loc1) != UNKNOWN_LOCATION ? (loc1) : (loc2))
|
||||
|
||||
/* Check a single expression EXPR for references to variables bound in
|
||||
intervening code in BODY. Return true if ok, otherwise give an error
|
||||
referencing CONTEXT and return false. Use LOC for the error message
|
||||
if EXPR doesn't have one. */
|
||||
static bool
|
||||
check_loop_binding_expr (tree expr, tree body, const char *context,
|
||||
location_t loc)
|
||||
{
|
||||
tree bad = walk_tree (&expr, check_loop_binding_expr_r, (void *)&body, NULL);
|
||||
|
||||
if (bad)
|
||||
{
|
||||
location_t eloc = EXPR_LOCATION (expr);
|
||||
error_at (LOCATION_OR (eloc, loc),
|
||||
"variable %qD used %s is bound "
|
||||
"in intervening code", bad, context);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* STMT is an OMP_FOR construct. Check all of the iteration variable,
|
||||
initializer, end condition, and increment for bindings inside the
|
||||
loop body. If ORIG_INITS is provided, check those elements too.
|
||||
Return true if OK, false otherwise. */
|
||||
bool
|
||||
c_omp_check_loop_binding_exprs (tree stmt, vec<tree> *orig_inits)
|
||||
{
|
||||
bool ok = true;
|
||||
location_t loc = EXPR_LOCATION (stmt);
|
||||
tree body = OMP_FOR_BODY (stmt);
|
||||
int orig_init_length = orig_inits ? orig_inits->length () : 0;
|
||||
|
||||
for (int i = 1; i < TREE_VEC_LENGTH (OMP_FOR_INIT (stmt)); i++)
|
||||
{
|
||||
tree init = TREE_VEC_ELT (OMP_FOR_INIT (stmt), i);
|
||||
tree cond = TREE_VEC_ELT (OMP_FOR_COND (stmt), i);
|
||||
tree incr = TREE_VEC_ELT (OMP_FOR_INCR (stmt), i);
|
||||
gcc_assert (TREE_CODE (init) == MODIFY_EXPR);
|
||||
tree decl = TREE_OPERAND (init, 0);
|
||||
tree orig_init = i < orig_init_length ? (*orig_inits)[i] : NULL_TREE;
|
||||
tree e;
|
||||
location_t eloc;
|
||||
|
||||
e = TREE_OPERAND (init, 1);
|
||||
eloc = LOCATION_OR (EXPR_LOCATION (init), loc);
|
||||
if (!check_loop_binding_expr (decl, body, "as loop variable", eloc))
|
||||
ok = false;
|
||||
if (!check_loop_binding_expr (e, body, "in initializer", eloc))
|
||||
ok = false;
|
||||
if (orig_init
|
||||
&& !check_loop_binding_expr (orig_init, body,
|
||||
"in initializer", eloc))
|
||||
ok = false;
|
||||
|
||||
/* INCR and/or COND may be null if this is a template with a
|
||||
class iterator. */
|
||||
if (cond)
|
||||
{
|
||||
eloc = LOCATION_OR (EXPR_LOCATION (cond), loc);
|
||||
if (COMPARISON_CLASS_P (cond) && TREE_OPERAND (cond, 0) == decl)
|
||||
e = TREE_OPERAND (cond, 1);
|
||||
else if (COMPARISON_CLASS_P (cond) && TREE_OPERAND (cond, 1) == decl)
|
||||
e = TREE_OPERAND (cond, 0);
|
||||
else
|
||||
e = cond;
|
||||
if (!check_loop_binding_expr (e, body, "in end test", eloc))
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (incr)
|
||||
{
|
||||
eloc = LOCATION_OR (EXPR_LOCATION (incr), loc);
|
||||
/* INCR should be either a MODIFY_EXPR or pre/post
|
||||
increment/decrement. We don't have to check the latter
|
||||
since there are no operands besides the iteration variable. */
|
||||
if (TREE_CODE (incr) == MODIFY_EXPR
|
||||
&& !check_loop_binding_expr (TREE_OPERAND (incr, 1), body,
|
||||
"in increment expression", eloc))
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* This function splits clauses for OpenACC combined loop
|
||||
constructs. OpenACC combined loop constructs are:
|
||||
#pragma acc kernels loop
|
||||
|
|
File diff suppressed because it is too large
Load diff
32
gcc/omp-api.h
Normal file
32
gcc/omp-api.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* Functions for querying whether a function name is reserved by the
|
||||
OpenMP API. This is used for error checking.
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef GCC_OMP_API_H
|
||||
#define GCC_OMP_API_H
|
||||
|
||||
#include "coretypes.h"
|
||||
|
||||
/* In omp-general.cc, but declared in a separate header file for
|
||||
convenience of the Fortran front end. */
|
||||
extern bool omp_runtime_api_procname (const char *name);
|
||||
extern bool omp_runtime_api_call (const_tree fndecl);
|
||||
|
||||
#endif
|
|
@ -3013,4 +3013,138 @@ omp_build_component_ref (tree obj, tree field)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Return true if NAME is the name of an omp_* runtime API call. */
|
||||
bool
|
||||
omp_runtime_api_procname (const char *name)
|
||||
{
|
||||
if (!startswith (name, "omp_"))
|
||||
return false;
|
||||
|
||||
static const char *omp_runtime_apis[] =
|
||||
{
|
||||
/* This array has 3 sections. First omp_* calls that don't
|
||||
have any suffixes. */
|
||||
"aligned_alloc",
|
||||
"aligned_calloc",
|
||||
"alloc",
|
||||
"calloc",
|
||||
"free",
|
||||
"get_mapped_ptr",
|
||||
"realloc",
|
||||
"target_alloc",
|
||||
"target_associate_ptr",
|
||||
"target_disassociate_ptr",
|
||||
"target_free",
|
||||
"target_is_accessible",
|
||||
"target_is_present",
|
||||
"target_memcpy",
|
||||
"target_memcpy_async",
|
||||
"target_memcpy_rect",
|
||||
"target_memcpy_rect_async",
|
||||
NULL,
|
||||
/* Now omp_* calls that are available as omp_* and omp_*_; however, the
|
||||
DECL_NAME is always omp_* without tailing underscore. */
|
||||
"capture_affinity",
|
||||
"destroy_allocator",
|
||||
"destroy_lock",
|
||||
"destroy_nest_lock",
|
||||
"display_affinity",
|
||||
"fulfill_event",
|
||||
"get_active_level",
|
||||
"get_affinity_format",
|
||||
"get_cancellation",
|
||||
"get_default_allocator",
|
||||
"get_default_device",
|
||||
"get_device_num",
|
||||
"get_dynamic",
|
||||
"get_initial_device",
|
||||
"get_level",
|
||||
"get_max_active_levels",
|
||||
"get_max_task_priority",
|
||||
"get_max_teams",
|
||||
"get_max_threads",
|
||||
"get_nested",
|
||||
"get_num_devices",
|
||||
"get_num_places",
|
||||
"get_num_procs",
|
||||
"get_num_teams",
|
||||
"get_num_threads",
|
||||
"get_partition_num_places",
|
||||
"get_place_num",
|
||||
"get_proc_bind",
|
||||
"get_supported_active_levels",
|
||||
"get_team_num",
|
||||
"get_teams_thread_limit",
|
||||
"get_thread_limit",
|
||||
"get_thread_num",
|
||||
"get_wtick",
|
||||
"get_wtime",
|
||||
"in_explicit_task",
|
||||
"in_final",
|
||||
"in_parallel",
|
||||
"init_lock",
|
||||
"init_nest_lock",
|
||||
"is_initial_device",
|
||||
"pause_resource",
|
||||
"pause_resource_all",
|
||||
"set_affinity_format",
|
||||
"set_default_allocator",
|
||||
"set_lock",
|
||||
"set_nest_lock",
|
||||
"test_lock",
|
||||
"test_nest_lock",
|
||||
"unset_lock",
|
||||
"unset_nest_lock",
|
||||
NULL,
|
||||
/* And finally calls available as omp_*, omp_*_ and omp_*_8_; however,
|
||||
as DECL_NAME only omp_* and omp_*_8 appear. */
|
||||
"display_env",
|
||||
"get_ancestor_thread_num",
|
||||
"init_allocator",
|
||||
"get_partition_place_nums",
|
||||
"get_place_num_procs",
|
||||
"get_place_proc_ids",
|
||||
"get_schedule",
|
||||
"get_team_size",
|
||||
"set_default_device",
|
||||
"set_dynamic",
|
||||
"set_max_active_levels",
|
||||
"set_nested",
|
||||
"set_num_teams",
|
||||
"set_num_threads",
|
||||
"set_schedule",
|
||||
"set_teams_thread_limit"
|
||||
};
|
||||
|
||||
int mode = 0;
|
||||
for (unsigned i = 0; i < ARRAY_SIZE (omp_runtime_apis); i++)
|
||||
{
|
||||
if (omp_runtime_apis[i] == NULL)
|
||||
{
|
||||
mode++;
|
||||
continue;
|
||||
}
|
||||
size_t len = strlen (omp_runtime_apis[i]);
|
||||
if (strncmp (name + 4, omp_runtime_apis[i], len) == 0
|
||||
&& (name[4 + len] == '\0'
|
||||
|| (mode > 1 && strcmp (name + 4 + len, "_8") == 0)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return true if FNDECL is an omp_* runtime API call. */
|
||||
|
||||
bool
|
||||
omp_runtime_api_call (const_tree fndecl)
|
||||
{
|
||||
tree declname = DECL_NAME (fndecl);
|
||||
if (!declname
|
||||
|| (DECL_CONTEXT (fndecl) != NULL_TREE
|
||||
&& TREE_CODE (DECL_CONTEXT (fndecl)) != TRANSLATION_UNIT_DECL)
|
||||
|| !TREE_PUBLIC (fndecl))
|
||||
return false;
|
||||
return omp_runtime_api_procname (IDENTIFIER_POINTER (declname));
|
||||
}
|
||||
|
||||
#include "gt-omp-general.h"
|
||||
|
|
|
@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#define GCC_OMP_GENERAL_H
|
||||
|
||||
#include "gomp-constants.h"
|
||||
#include "omp-api.h"
|
||||
|
||||
/* Flags for an OpenACC loop. */
|
||||
|
||||
|
|
129
gcc/omp-low.cc
129
gcc/omp-low.cc
|
@ -4009,135 +4009,6 @@ setjmp_or_longjmp_p (const_tree fndecl)
|
|||
return !strcmp (name, "setjmp") || !strcmp (name, "longjmp");
|
||||
}
|
||||
|
||||
/* Return true if FNDECL is an omp_* runtime API call. */
|
||||
|
||||
static bool
|
||||
omp_runtime_api_call (const_tree fndecl)
|
||||
{
|
||||
tree declname = DECL_NAME (fndecl);
|
||||
if (!declname
|
||||
|| (DECL_CONTEXT (fndecl) != NULL_TREE
|
||||
&& TREE_CODE (DECL_CONTEXT (fndecl)) != TRANSLATION_UNIT_DECL)
|
||||
|| !TREE_PUBLIC (fndecl))
|
||||
return false;
|
||||
|
||||
const char *name = IDENTIFIER_POINTER (declname);
|
||||
if (!startswith (name, "omp_"))
|
||||
return false;
|
||||
|
||||
static const char *omp_runtime_apis[] =
|
||||
{
|
||||
/* This array has 3 sections. First omp_* calls that don't
|
||||
have any suffixes. */
|
||||
"aligned_alloc",
|
||||
"aligned_calloc",
|
||||
"alloc",
|
||||
"calloc",
|
||||
"free",
|
||||
"get_mapped_ptr",
|
||||
"realloc",
|
||||
"target_alloc",
|
||||
"target_associate_ptr",
|
||||
"target_disassociate_ptr",
|
||||
"target_free",
|
||||
"target_is_accessible",
|
||||
"target_is_present",
|
||||
"target_memcpy",
|
||||
"target_memcpy_async",
|
||||
"target_memcpy_rect",
|
||||
"target_memcpy_rect_async",
|
||||
NULL,
|
||||
/* Now omp_* calls that are available as omp_* and omp_*_; however, the
|
||||
DECL_NAME is always omp_* without tailing underscore. */
|
||||
"capture_affinity",
|
||||
"destroy_allocator",
|
||||
"destroy_lock",
|
||||
"destroy_nest_lock",
|
||||
"display_affinity",
|
||||
"fulfill_event",
|
||||
"get_active_level",
|
||||
"get_affinity_format",
|
||||
"get_cancellation",
|
||||
"get_default_allocator",
|
||||
"get_default_device",
|
||||
"get_device_num",
|
||||
"get_dynamic",
|
||||
"get_initial_device",
|
||||
"get_level",
|
||||
"get_max_active_levels",
|
||||
"get_max_task_priority",
|
||||
"get_max_teams",
|
||||
"get_max_threads",
|
||||
"get_nested",
|
||||
"get_num_devices",
|
||||
"get_num_places",
|
||||
"get_num_procs",
|
||||
"get_num_teams",
|
||||
"get_num_threads",
|
||||
"get_partition_num_places",
|
||||
"get_place_num",
|
||||
"get_proc_bind",
|
||||
"get_supported_active_levels",
|
||||
"get_team_num",
|
||||
"get_teams_thread_limit",
|
||||
"get_thread_limit",
|
||||
"get_thread_num",
|
||||
"get_wtick",
|
||||
"get_wtime",
|
||||
"in_explicit_task",
|
||||
"in_final",
|
||||
"in_parallel",
|
||||
"init_lock",
|
||||
"init_nest_lock",
|
||||
"is_initial_device",
|
||||
"pause_resource",
|
||||
"pause_resource_all",
|
||||
"set_affinity_format",
|
||||
"set_default_allocator",
|
||||
"set_lock",
|
||||
"set_nest_lock",
|
||||
"test_lock",
|
||||
"test_nest_lock",
|
||||
"unset_lock",
|
||||
"unset_nest_lock",
|
||||
NULL,
|
||||
/* And finally calls available as omp_*, omp_*_ and omp_*_8_; however,
|
||||
as DECL_NAME only omp_* and omp_*_8 appear. */
|
||||
"display_env",
|
||||
"get_ancestor_thread_num",
|
||||
"init_allocator",
|
||||
"get_partition_place_nums",
|
||||
"get_place_num_procs",
|
||||
"get_place_proc_ids",
|
||||
"get_schedule",
|
||||
"get_team_size",
|
||||
"set_default_device",
|
||||
"set_dynamic",
|
||||
"set_max_active_levels",
|
||||
"set_nested",
|
||||
"set_num_teams",
|
||||
"set_num_threads",
|
||||
"set_schedule",
|
||||
"set_teams_thread_limit"
|
||||
};
|
||||
|
||||
int mode = 0;
|
||||
for (unsigned i = 0; i < ARRAY_SIZE (omp_runtime_apis); i++)
|
||||
{
|
||||
if (omp_runtime_apis[i] == NULL)
|
||||
{
|
||||
mode++;
|
||||
continue;
|
||||
}
|
||||
size_t len = strlen (omp_runtime_apis[i]);
|
||||
if (strncmp (name + 4, omp_runtime_apis[i], len) == 0
|
||||
&& (name[4 + len] == '\0'
|
||||
|| (mode > 1 && strcmp (name + 4 + len, "_8") == 0)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Helper function for scan_omp.
|
||||
|
||||
Callback for walk_gimple_stmt used to scan for OMP directives in
|
||||
|
|
|
@ -8,8 +8,8 @@ f1 (void)
|
|||
{
|
||||
#pragma acc parallel
|
||||
#pragma acc loop collapse (2)
|
||||
for (i = 0; i < 5; i++)
|
||||
; /* { dg-error "not enough perfectly nested" } */
|
||||
for (i = 0; i < 5; i++) /* { dg-error "not enough nested loops" } */
|
||||
;
|
||||
{
|
||||
for (j = 0; j < 5; j++)
|
||||
;
|
||||
|
@ -38,9 +38,9 @@ f3 (void)
|
|||
{
|
||||
#pragma acc parallel
|
||||
#pragma acc loop collapse (2)
|
||||
for (i = 0; i < 5; i++)
|
||||
for (i = 0; i < 5; i++) /* { dg-error "inner loops must be perfectly nested" } */
|
||||
{
|
||||
int k = foo (); /* { dg-error "not enough perfectly nested" } */
|
||||
int k = foo ();
|
||||
{
|
||||
{
|
||||
for (j = 0; j < 5; j++)
|
||||
|
@ -56,12 +56,12 @@ f4 (void)
|
|||
{
|
||||
#pragma acc parallel
|
||||
#pragma acc loop collapse (2)
|
||||
for (i = 0; i < 5; i++)
|
||||
for (i = 0; i < 5; i++) /* { dg-error "inner loops must be perfectly nested" } */
|
||||
{
|
||||
{
|
||||
for (j = 0; j < 5; j++)
|
||||
;
|
||||
foo (); /* { dg-error "collapsed loops not perfectly nested before" } */
|
||||
foo ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,13 +71,13 @@ f5 (void)
|
|||
{
|
||||
#pragma acc parallel
|
||||
#pragma acc loop collapse (2)
|
||||
for (i = 0; i < 5; i++)
|
||||
for (i = 0; i < 5; i++) /* { dg-error "inner loops must be perfectly nested" } */
|
||||
{
|
||||
{
|
||||
for (j = 0; j < 5; j++)
|
||||
;
|
||||
}
|
||||
foo (); /* { dg-error "collapsed loops not perfectly nested before" } */
|
||||
foo ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ int main ()
|
|||
#pragma acc parallel
|
||||
{
|
||||
#pragma acc loop tile (*,*)
|
||||
for (int ix = 0; ix < 30; ix++)
|
||||
; /* { dg-error "not enough" } */
|
||||
for (int ix = 0; ix < 30; ix++) /* { dg-error "not enough" "" { target c } } */
|
||||
; /* { dg-error "not enough" "" { target c++ } } */
|
||||
|
||||
#pragma acc loop tile (*,*)
|
||||
for (int ix = 0; ix < 30; ix++)
|
||||
|
|
|
@ -8,8 +8,8 @@ void
|
|||
f1 (void)
|
||||
{
|
||||
#pragma omp for collapse (2)
|
||||
for (i = 0; i < 5; i++)
|
||||
; /* { dg-error "not enough perfectly nested" } */
|
||||
for (i = 0; i < 5; i++) /* { dg-error "not enough nested loops" } */
|
||||
;
|
||||
{
|
||||
for (j = 0; j < 5; j++)
|
||||
;
|
||||
|
@ -38,7 +38,7 @@ f3 (void)
|
|||
#pragma omp for collapse (2)
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
int k = foo (); /* { dg-error "not enough perfectly nested" } */
|
||||
int k = foo ();
|
||||
{
|
||||
{
|
||||
for (j = 0; j < 5; j++)
|
||||
|
@ -58,7 +58,7 @@ f4 (void)
|
|||
{
|
||||
for (j = 0; j < 5; j++)
|
||||
;
|
||||
foo (); /* { dg-error "collapsed loops not perfectly nested before" } */
|
||||
foo ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ f5 (void)
|
|||
for (j = 0; j < 5; j++)
|
||||
;
|
||||
}
|
||||
foo (); /* { dg-error "collapsed loops not perfectly nested before" } */
|
||||
foo ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue