aboutsummaryrefslogtreecommitdiff
path: root/gcc/c/c-parser.cc
diff options
context:
space:
mode:
authorSandra Loosemore <sandra@codesourcery.com>2023-08-24 17:35:00 +0000
committerSandra Loosemore <sandra@codesourcery.com>2023-08-25 19:42:50 +0000
commit143151ac2013c22e471dd674d02c7dec0798d3bd (patch)
treeb958fa3c3789eccc89afb62055e3f2d94e522a98 /gcc/c/c-parser.cc
parenta62c8324e7e31ae6614f549bdf9d8a653233f8fc (diff)
downloadgcc-143151ac2013c22e471dd674d02c7dec0798d3bd.zip
gcc-143151ac2013c22e471dd674d02c7dec0798d3bd.tar.gz
gcc-143151ac2013c22e471dd674d02c7dec0798d3bd.tar.bz2
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.
Diffstat (limited to 'gcc/c/c-parser.cc')
-rw-r--r--gcc/c/c-parser.cc860
1 files changed, 608 insertions, 252 deletions
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 2f3afb2..cae10ba 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -249,6 +249,10 @@ struct GTY(()) c_parser {
/* Location of the last consumed token. */
location_t last_token_location;
+
+ /* Holds state for parsing collapsed OMP_FOR loops. Managed by
+ c_parser_omp_for_loop. */
+ struct omp_for_parse_data * GTY((skip)) omp_for_parse_state;
};
/* Return a pointer to the Nth token in PARSERs tokens_buf. */
@@ -1525,6 +1529,44 @@ struct oacc_routine_data {
/* Used for parsing objc foreach statements. */
static tree objc_foreach_break_label, objc_foreach_continue_label;
+/* Used for parsing OMP for loops.
+
+ Some notes on flags used for context:
+ parser->omp_for_parse_state is non-null anywhere inside the OMP FOR
+ construct, except for the final-loop-body.
+ The want_nested_loop flag is true if inside a {} sequence where
+ a loop-nest (or another {} sequence containing a loop-nest) is expected,
+ but has not yet been seen. It's false when parsing intervening code
+ statements or their substatements that cannot contain a loop-nest.
+ The in_intervening_code flag is true when parsing any intervening code,
+ including substatements, and whether or not want_nested_loop is true.
+
+ And, about error handling:
+ The saw_intervening_code flag is set if the loop is not perfectly
+ nested, even in the usual case where this is not an error.
+ perfect_nesting_fail is set if an error has been diagnosed because an
+ imperfectly-nested loop was found where a perfectly-nested one is
+ required (we diagnose this only once).
+ fail is set if any kind of structural error in the loop nest
+ has been found and diagnosed.
+ */
+struct omp_for_parse_data {
+ enum tree_code code;
+ tree declv, condv, incrv, initv;
+ tree pre_body;
+ tree bindings;
+ int count; /* Expected nesting depth. */
+ int depth; /* Current nesting depth. */
+ location_t for_loc;
+ bool ordered : 1;
+ bool inscan : 1;
+ bool want_nested_loop : 1;
+ bool in_intervening_code : 1;
+ bool saw_intervening_code: 1;
+ bool perfect_nesting_fail : 1;
+ bool fail : 1;
+};
+
static bool c_parser_nth_token_starts_std_attributes (c_parser *,
unsigned int);
static tree c_parser_std_attribute_specifier_sequence (c_parser *);
@@ -1618,6 +1660,7 @@ static void c_parser_omp_threadprivate (c_parser *);
static void c_parser_omp_barrier (c_parser *);
static void c_parser_omp_depobj (c_parser *);
static void c_parser_omp_flush (c_parser *);
+static tree c_parser_omp_loop_nest (c_parser *, bool *);
static tree c_parser_omp_for_loop (location_t, c_parser *, enum tree_code,
tree, tree *, bool *);
static void c_parser_omp_taskwait (c_parser *);
@@ -6187,6 +6230,68 @@ c_parser_compound_statement (c_parser *parser, location_t *endlocp)
return c_end_compound_stmt (brace_loc, stmt, true);
}
+/* Diagnose errors related to imperfectly nested loops in an OMP
+ loop construct. This function is called when such code is seen.
+ Only issue one such diagnostic no matter how much invalid
+ intervening code there is in the loop.
+ FIXME: maybe the location associated with the diagnostic should
+ be the current parser token instead of the location of the outer loop
+ nest. */
+
+static void
+check_omp_intervening_code (c_parser *parser)
+{
+ struct omp_for_parse_data *omp_for_parse_state = parser->omp_for_parse_state;
+ gcc_assert (omp_for_parse_state);
+
+ if (!omp_for_parse_state->in_intervening_code)
+ return;
+ omp_for_parse_state->saw_intervening_code = true;
+
+ /* Only diagnose errors related to perfect nesting once. */
+ if (!omp_for_parse_state->perfect_nesting_fail)
+ {
+
+ /* OpenACC does not (yet) permit intervening code, in
+ addition to situations forbidden by the OpenMP spec. */
+ if (omp_for_parse_state->code == OACC_LOOP)
+ {
+ error_at (omp_for_parse_state->for_loc,
+ "inner loops must be perfectly nested in "
+ "%<#pragma acc loop%>");
+ omp_for_parse_state->perfect_nesting_fail = true;
+ }
+ else if (omp_for_parse_state->ordered)
+ {
+ error_at (omp_for_parse_state->for_loc,
+ "inner loops must be perfectly nested with "
+ "%<ordered%> clause");
+ omp_for_parse_state->perfect_nesting_fail = true;
+ }
+ else if (omp_for_parse_state->inscan)
+ {
+ error_at (omp_for_parse_state->for_loc,
+ "inner loops must be perfectly nested with "
+ "%<reduction%> %<inscan%> clause");
+ omp_for_parse_state->perfect_nesting_fail = true;
+ }
+ /* TODO: Also reject loops with TILE directive. */
+ if (omp_for_parse_state->perfect_nesting_fail)
+ omp_for_parse_state->fail = true;
+ }
+}
+
+/* Helper function for below: wrap an OMP_STRUCTURED_BLOCK around SL
+ and add the statement to the current list. If SL is an empty statement
+ list, do nothing. */
+static void
+add_structured_block_stmt (tree sl)
+{
+ if (TREE_CODE (sl) != STATEMENT_LIST
+ || !tsi_end_p (tsi_start (sl)))
+ add_stmt (build1 (OMP_STRUCTURED_BLOCK, void_type_node, sl));
+}
+
/* Parse a compound statement except for the opening brace. This is
used for parsing both compound statements and statement expressions
(which follow different paths to handling the opening). */
@@ -6198,6 +6303,12 @@ c_parser_compound_statement_nostart (c_parser *parser)
bool last_label = false;
bool save_valid_for_pragma = valid_location_for_stdc_pragma_p ();
location_t label_loc = UNKNOWN_LOCATION; /* Quiet warning. */
+ struct omp_for_parse_data *omp_for_parse_state
+ = parser->omp_for_parse_state;
+ bool in_omp_loop_block
+ = omp_for_parse_state ? omp_for_parse_state->want_nested_loop : false;
+ tree sl = NULL_TREE;
+
if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
{
location_t endloc = c_parser_peek_token (parser)->location;
@@ -6205,12 +6316,20 @@ c_parser_compound_statement_nostart (c_parser *parser)
c_parser_consume_token (parser);
return endloc;
}
+
+ /* If we're parsing a {} sequence in an OMP_FOR body, start a
+ statement list for intervening code. */
+ if (in_omp_loop_block)
+ sl = push_stmt_list ();
+
mark_valid_location_for_stdc_pragma (true);
if (c_parser_next_token_is_keyword (parser, RID_LABEL))
{
/* Read zero or more forward-declarations for labels that nested
functions can jump to. */
mark_valid_location_for_stdc_pragma (false);
+ if (in_omp_loop_block)
+ check_omp_intervening_code (parser);
while (c_parser_next_token_is_keyword (parser, RID_LABEL))
{
label_loc = c_parser_peek_token (parser)->location;
@@ -6252,6 +6371,76 @@ c_parser_compound_statement_nostart (c_parser *parser)
{
location_t loc = c_parser_peek_token (parser)->location;
loc = expansion_point_location_if_in_system_header (loc);
+
+ bool want_nested_loop = (omp_for_parse_state
+ ? omp_for_parse_state->want_nested_loop
+ : false);
+
+ /* First take care of special cases for OpenMP "canonical loop
+ nest form", that do not allow standard attributes, labels, or
+ __extension__ before the nested statement. */
+ if (in_omp_loop_block && !last_label)
+ {
+ if (want_nested_loop
+ && c_parser_next_token_is_keyword (parser, RID_FOR))
+ {
+ /* Found the next nested loop. If there were intervening
+ code statements collected before now, wrap them in an
+ OMP_STRUCTURED_BLOCK node, and start a new structured
+ block to hold statements that may come after the FOR. */
+ gcc_assert (sl);
+ add_structured_block_stmt (pop_stmt_list (sl));
+ omp_for_parse_state->depth++;
+ add_stmt (c_parser_omp_loop_nest (parser, NULL));
+ omp_for_parse_state->depth--;
+ sl = push_stmt_list ();
+ parser->error = false;
+ continue;
+ }
+ else if (want_nested_loop
+ && c_parser_next_token_is (parser, CPP_OPEN_BRACE))
+ {
+ /* If this nested compound statement contains the nested loop,
+ we need to separate the other statements in the current
+ statement into separate blocks of intervening code. If
+ there's no nested loop, it's all part of the same
+ chunk of intervening code. */
+ tree pre_sl = pop_stmt_list (sl);
+ tree nested_sl = push_stmt_list ();
+ mark_valid_location_for_stdc_pragma (false);
+ c_parser_statement_after_labels (parser, NULL);
+ nested_sl = pop_stmt_list (nested_sl);
+ if (omp_for_parse_state->want_nested_loop)
+ {
+ /* This block didn't contain a loop-nest, so it's
+ all part of the same chunk of intervening code. */
+ check_omp_intervening_code (parser);
+ sl = push_stmt_list ();
+ add_stmt (pre_sl);
+ add_stmt (nested_sl);
+ }
+ else
+ {
+ /* It contains the nested loop. */
+ add_structured_block_stmt (pre_sl);
+ add_stmt (nested_sl);
+ sl = push_stmt_list ();
+ }
+ parser->error = false;
+ continue;
+ }
+ else if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+ {
+ /* Prior to implementing the OpenMP 5.1 syntax for canonical
+ loop form, GCC used to accept an empty statements that
+ would now be flagged as intervening code. Continue to
+ do that, as an extension. */
+ /* FIXME: Maybe issue a warning or something here? */
+ c_parser_consume_token (parser);
+ continue;
+ }
+ }
+
/* Standard attributes may start a label, statement or declaration. */
bool have_std_attrs
= c_parser_nth_token_starts_std_attributes (parser, 1);
@@ -6270,6 +6459,8 @@ c_parser_compound_statement_nostart (c_parser *parser)
last_label = true;
last_stmt = false;
mark_valid_location_for_stdc_pragma (false);
+ if (in_omp_loop_block)
+ check_omp_intervening_code (parser);
c_parser_label (parser, std_attrs);
}
else if (c_parser_next_tokens_start_declaration (parser)
@@ -6280,7 +6471,13 @@ c_parser_compound_statement_nostart (c_parser *parser)
pedwarn_c11 (c_parser_peek_token (parser)->location, OPT_Wpedantic,
"a label can only be part of a statement and "
"a declaration is not a statement");
-
+ /* It's unlikely we'll see a nested loop in a declaration in
+ intervening code in an OMP loop, but disallow it anyway. */
+ if (in_omp_loop_block)
+ {
+ check_omp_intervening_code (parser);
+ omp_for_parse_state->want_nested_loop = false;
+ }
mark_valid_location_for_stdc_pragma (false);
bool fallthru_attr_p = false;
c_parser_declaration_or_fndef (parser, true, !have_std_attrs,
@@ -6288,6 +6485,8 @@ c_parser_compound_statement_nostart (c_parser *parser)
NULL, have_std_attrs, std_attrs,
NULL, &fallthru_attr_p);
+ if (in_omp_loop_block)
+ omp_for_parse_state->want_nested_loop = want_nested_loop;
if (last_stmt && !fallthru_attr_p)
pedwarn_c90 (loc, OPT_Wdeclaration_after_statement,
"ISO C90 forbids mixed declarations and code");
@@ -6315,9 +6514,18 @@ c_parser_compound_statement_nostart (c_parser *parser)
ext = disable_extension_diagnostics ();
c_parser_consume_token (parser);
last_label = false;
+ /* It's unlikely we'll see a nested loop in a declaration in
+ intervening code in an OMP loop, but disallow it anyway. */
+ if (in_omp_loop_block)
+ {
+ check_omp_intervening_code (parser);
+ omp_for_parse_state->want_nested_loop = false;
+ }
mark_valid_location_for_stdc_pragma (false);
c_parser_declaration_or_fndef (parser, true, true, true, true,
true);
+ if (in_omp_loop_block)
+ omp_for_parse_state->want_nested_loop = want_nested_loop;
/* Following the old parser, __extension__ does not
disable this diagnostic. */
restore_extension_diagnostics (ext);
@@ -6338,10 +6546,19 @@ c_parser_compound_statement_nostart (c_parser *parser)
syntactically. This ensures that the user doesn't put them
places that would turn into syntax errors if the directive
were ignored. */
+ if (omp_for_parse_state)
+ omp_for_parse_state->want_nested_loop = false;
if (c_parser_pragma (parser,
last_label ? pragma_stmt : pragma_compound,
NULL))
- last_label = false, last_stmt = true;
+ {
+ last_label = false;
+ last_stmt = true;
+ if (omp_for_parse_state)
+ check_omp_intervening_code (parser);
+ }
+ if (omp_for_parse_state)
+ omp_for_parse_state->want_nested_loop = want_nested_loop;
}
else if (c_parser_next_token_is (parser, CPP_EOF))
{
@@ -6371,7 +6588,20 @@ c_parser_compound_statement_nostart (c_parser *parser)
last_label = false;
last_stmt = true;
mark_valid_location_for_stdc_pragma (false);
- c_parser_statement_after_labels (parser, NULL);
+ if (!omp_for_parse_state)
+ c_parser_statement_after_labels (parser, NULL);
+ else
+ {
+ /* In canonical loop nest form, nested loops can only appear
+ directly, or in a directly nested compound statement. We
+ already took care of those cases above, so now we have
+ something else. This statement and everything inside
+ it must be intervening code. */
+ omp_for_parse_state->want_nested_loop = false;
+ check_omp_intervening_code (parser);
+ c_parser_statement_after_labels (parser, NULL);
+ omp_for_parse_state->want_nested_loop = want_nested_loop;
+ }
}
parser->error = false;
@@ -6380,8 +6610,21 @@ c_parser_compound_statement_nostart (c_parser *parser)
pedwarn_c11 (label_loc, OPT_Wpedantic, "label at end of compound statement");
location_t endloc = c_parser_peek_token (parser)->location;
c_parser_consume_token (parser);
+
/* Restore the value we started with. */
mark_valid_location_for_stdc_pragma (save_valid_for_pragma);
+
+ /* Package leftover intervening code, or the whole contents of the
+ compound statement if we were looking for a nested loop in an OMP_FOR
+ construct and didn't find one. */
+ if (sl)
+ {
+ sl = pop_stmt_list (sl);
+ if (omp_for_parse_state->want_nested_loop)
+ add_stmt (sl);
+ else
+ add_structured_block_stmt (sl);
+ }
return endloc;
}
@@ -7213,6 +7456,14 @@ c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll,
gcc_assert (c_parser_next_token_is_keyword (parser, RID_WHILE));
token_indent_info while_tinfo
= get_token_indent_info (c_parser_peek_token (parser));
+
+ if (parser->omp_for_parse_state)
+ {
+ error_at (c_parser_peek_token (parser)->location,
+ "loop not permitted in intervening code in OpenMP loop body");
+ parser->omp_for_parse_state->fail = true;
+ }
+
c_parser_consume_token (parser);
block = c_begin_compound_stmt (flag_isoc99);
loc = c_parser_peek_token (parser)->location;
@@ -7270,6 +7521,14 @@ c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll,
unsigned char save_in_statement;
location_t loc;
gcc_assert (c_parser_next_token_is_keyword (parser, RID_DO));
+
+ if (parser->omp_for_parse_state)
+ {
+ error_at (c_parser_peek_token (parser)->location,
+ "loop not permitted in intervening code in OpenMP loop body");
+ parser->omp_for_parse_state->fail = true;
+ }
+
c_parser_consume_token (parser);
if (c_parser_next_token_is (parser, CPP_SEMICOLON))
warning_at (c_parser_peek_token (parser)->location,
@@ -7381,6 +7640,14 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR));
token_indent_info for_tinfo
= get_token_indent_info (c_parser_peek_token (parser));
+
+ if (parser->omp_for_parse_state)
+ {
+ error_at (for_loc,
+ "loop not permitted in intervening code in OpenMP loop body");
+ parser->omp_for_parse_state->fail = true;
+ }
+
c_parser_consume_token (parser);
/* Open a compound statement in Objective-C as well, just in case this is
as foreach expression. */
@@ -11335,6 +11602,14 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
&& fndecl_built_in_p (expr.value, BUILT_IN_NORMAL)
&& vec_safe_length (exprlist) == 1)
warn_for_abs (expr_loc, expr.value, (*exprlist)[0]);
+ if (parser->omp_for_parse_state
+ && parser->omp_for_parse_state->in_intervening_code
+ && omp_runtime_api_call (expr.value))
+ {
+ error_at (expr_loc, "calls to the OpenMP runtime API are "
+ "not permitted in intervening code");
+ parser->omp_for_parse_state->fail = true;
+ }
}
start = expr.get_start ();
@@ -13179,6 +13454,17 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p)
input_location = c_parser_peek_token (parser)->location;
id = c_parser_peek_token (parser)->pragma_kind;
gcc_assert (id != PRAGMA_NONE);
+ if (parser->omp_for_parse_state
+ && parser->omp_for_parse_state->in_intervening_code
+ && id >= PRAGMA_OMP__START_
+ && id <= PRAGMA_OMP__LAST_)
+ {
+ error_at (input_location,
+ "intervening code must not contain OpenMP directives");
+ parser->omp_for_parse_state->fail = true;
+ c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL);
+ return false;
+ }
switch (id)
{
@@ -20394,6 +20680,274 @@ c_parser_omp_scan_loop_body (c_parser *parser, bool open_brace_parsed)
"expected %<}%>");
}
+
+/* This function parses a single level of a loop nest, invoking itself
+ recursively if necessary.
+
+ loop-nest :: for (...) loop-body
+ loop-body :: loop-nest
+ | { [intervening-code] loop-body [intervening-code] }
+ | final-loop-body
+ intervening-code :: structured-block-sequence
+ final-loop-body :: structured-block
+
+ For a collapsed loop nest, only a single OMP_FOR is built, pulling out
+ all the iterator information from the inner loops into the
+ parser->omp_for_parse_state structure.
+
+ The iterator decl, init, cond, and incr are stored in vectors.
+
+ Initialization code for iterator variables is collected into
+ parser->omp_for_parse_state->pre_body and ends up inserted directly
+ into the OMP_FOR structure. */
+
+static tree
+c_parser_omp_loop_nest (c_parser *parser, bool *if_p)
+{
+ tree decl, cond, incr, init;
+ tree body = NULL_TREE;
+ matching_parens parens;
+ bool moreloops;
+ unsigned char save_in_statement;
+ tree loop_scope;
+ location_t loc;
+ struct omp_for_parse_data *omp_for_parse_state
+ = parser->omp_for_parse_state;
+ gcc_assert (omp_for_parse_state);
+ int depth = omp_for_parse_state->depth;
+
+ /* We have already matched the FOR token but not consumed it yet. */
+ loc = c_parser_peek_token (parser)->location;
+ gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR));
+ c_parser_consume_token (parser);
+
+ /* Forbid break/continue in the loop initializer, condition, and
+ increment expressions. */
+ save_in_statement = in_statement;
+ in_statement = IN_OMP_BLOCK;
+
+ /* We are not in intervening code now. */
+ omp_for_parse_state->in_intervening_code = false;
+
+ if (!parens.require_open (parser))
+ {
+ omp_for_parse_state->fail = true;
+ return NULL_TREE;
+ }
+
+ /* An implicit scope block surrounds each level of FOR loop, for
+ declarations of iteration variables at this loop depth. */
+ loop_scope = c_begin_compound_stmt (true);
+
+ /* Parse the initialization declaration or expression. */
+ if (c_parser_next_tokens_start_declaration (parser))
+ {
+ /* This is a declaration, which must be added to the pre_body code. */
+ tree this_pre_body = push_stmt_list ();
+ c_in_omp_for = true;
+ c_parser_declaration_or_fndef (parser, true, true, true, true, true);
+ c_in_omp_for = false;
+ this_pre_body = pop_stmt_list (this_pre_body);
+ append_to_statement_list_force (this_pre_body,
+ &(omp_for_parse_state->pre_body));
+ decl = check_for_loop_decls (omp_for_parse_state->for_loc, flag_isoc99);
+ if (decl == NULL)
+ goto error_init;
+ if (DECL_INITIAL (decl) == error_mark_node)
+ decl = error_mark_node;
+ init = decl;
+ }
+ else if (c_parser_next_token_is (parser, CPP_NAME)
+ && c_parser_peek_2nd_token (parser)->type == CPP_EQ)
+ {
+ struct c_expr decl_exp;
+ struct c_expr init_exp;
+ location_t init_loc;
+
+ decl_exp = c_parser_postfix_expression (parser);
+ decl = decl_exp.value;
+
+ c_parser_require (parser, CPP_EQ, "expected %<=%>");
+
+ init_loc = c_parser_peek_token (parser)->location;
+ init_exp = c_parser_expr_no_commas (parser, NULL);
+ init_exp = default_function_array_read_conversion (init_loc,
+ init_exp);
+ c_in_omp_for = true;
+ init = build_modify_expr (init_loc, decl, decl_exp.original_type,
+ NOP_EXPR, init_loc, init_exp.value,
+ init_exp.original_type);
+ c_in_omp_for = false;
+ init = c_process_expr_stmt (init_loc, init);
+
+ c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
+ }
+ else
+ {
+ error_init:
+ c_parser_error (parser,
+ "expected iteration declaration or initialization");
+ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+ "expected %<)%>");
+ omp_for_parse_state->fail = true;
+ goto parse_next;
+ }
+
+ /* Parse the loop condition. */
+ cond = NULL_TREE;
+ if (c_parser_next_token_is_not (parser, CPP_SEMICOLON))
+ {
+ location_t cond_loc = c_parser_peek_token (parser)->location;
+ c_in_omp_for = true;
+ struct c_expr cond_expr
+ = c_parser_binary_expression (parser, NULL, NULL_TREE);
+ c_in_omp_for = false;
+
+ cond = cond_expr.value;
+ cond = c_objc_common_truthvalue_conversion (cond_loc, cond);
+ switch (cond_expr.original_code)
+ {
+ case GT_EXPR:
+ case GE_EXPR:
+ case LT_EXPR:
+ case LE_EXPR:
+ break;
+ case NE_EXPR:
+ if (omp_for_parse_state->code != OACC_LOOP)
+ break;
+ /* FALLTHRU. */
+ default:
+ /* Can't be cond = error_mark_node, because we want to preserve
+ the location until c_finish_omp_for. */
+ cond = build1 (NOP_EXPR, boolean_type_node, error_mark_node);
+ break;
+ }
+ protected_set_expr_location (cond, cond_loc);
+ }
+ c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
+
+ /* Parse the increment expression. */
+ incr = NULL_TREE;
+ if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN))
+ {
+ location_t incr_loc = c_parser_peek_token (parser)->location;
+
+ incr = c_process_expr_stmt (incr_loc,
+ c_parser_expression (parser).value);
+ }
+ parens.skip_until_found_close (parser);
+
+ if (decl == NULL || decl == error_mark_node || init == error_mark_node)
+ omp_for_parse_state->fail = true;
+ else
+ {
+ TREE_VEC_ELT (omp_for_parse_state->declv, depth) = decl;
+ TREE_VEC_ELT (omp_for_parse_state->initv, depth) = init;
+ TREE_VEC_ELT (omp_for_parse_state->condv, depth) = cond;
+ TREE_VEC_ELT (omp_for_parse_state->incrv, depth) = incr;
+ }
+
+parse_next:
+ moreloops = depth < omp_for_parse_state->count - 1;
+ omp_for_parse_state->want_nested_loop = moreloops;
+ if (moreloops && c_parser_next_token_is_keyword (parser, RID_FOR))
+ {
+ omp_for_parse_state->depth++;
+ body = c_parser_omp_loop_nest (parser, if_p);
+ omp_for_parse_state->depth--;
+ }
+ else if (moreloops && c_parser_next_token_is (parser, CPP_OPEN_BRACE))
+ {
+ /* This is the open brace in the loop-body grammar production. Rather
+ than trying to special-case braces, just parse it as a compound
+ statement and handle the nested loop-body case there. Note that
+ when we see a further open brace inside the compound statement
+ loop-body, we don't know whether it is the start of intervening
+ code that is a compound statement, or a level of braces
+ surrounding a nested loop-body. Use the WANT_NESTED_LOOP state
+ bit to ensure we have only one nested loop at each level. */
+ omp_for_parse_state->in_intervening_code = true;
+ body = c_parser_compound_statement (parser, NULL);
+ omp_for_parse_state->in_intervening_code = false;
+ if (omp_for_parse_state->want_nested_loop)
+ {
+ /* We have already parsed the whole loop body and not found a
+ nested loop. */
+ error_at (omp_for_parse_state->for_loc,
+ "not enough nested loops");
+ omp_for_parse_state->fail = true;
+ }
+ if_p = NULL;
+ }
+ else
+ {
+ /* This is the final-loop-body case in the grammar: we have
+ something that is not a FOR and not an open brace. */
+ if (moreloops)
+ {
+ /* If we were expecting a nested loop, give an error and mark
+ that parsing has failed, and try to recover by parsing the
+ body as regular code without further collapsing. */
+ error_at (omp_for_parse_state->for_loc,
+ "not enough nested loops");
+ omp_for_parse_state->fail = true;
+ }
+ in_statement = IN_OMP_FOR;
+ parser->omp_for_parse_state = NULL;
+ body = push_stmt_list ();
+ if (omp_for_parse_state->inscan)
+ c_parser_omp_scan_loop_body (parser, false);
+ else
+ add_stmt (c_parser_c99_block_statement (parser, if_p));
+ body = pop_stmt_list (body);
+ parser->omp_for_parse_state = omp_for_parse_state;
+ }
+ in_statement = save_in_statement;
+ omp_for_parse_state->want_nested_loop = false;
+ omp_for_parse_state->in_intervening_code = true;
+
+ /* Pop and return the implicit scope surrounding this level of loop.
+ If the iteration variable at this depth was bound in the for loop,
+ pull out and save the binding. Later in c_parser_omp_for_loop,
+ these bindings will be moved to the scope surrounding the entire
+ OMP_FOR. That keeps the gimplifier happy later on, and meanwhile
+ we have already resolved all references to the iteration variable
+ in its true scope. */
+ add_stmt (body);
+ body = c_end_compound_stmt (loc, loop_scope, true);
+ if (decl && TREE_CODE (body) == BIND_EXPR)
+ {
+ tree t = BIND_EXPR_VARS (body);
+ tree prev = NULL_TREE, next = NULL_TREE;
+ while (t)
+ {
+ next = DECL_CHAIN (t);
+ if (t == decl)
+ {
+ if (prev)
+ DECL_CHAIN (prev) = next;
+ else
+ {
+ BIND_EXPR_VARS (body) = next;
+ BLOCK_VARS (BIND_EXPR_BLOCK (body)) = next;
+ }
+ DECL_CHAIN (t) = omp_for_parse_state->bindings;
+ omp_for_parse_state->bindings = t;
+ break;
+ }
+ else
+ {
+ prev = t;
+ t = next;
+ }
+ }
+ if (BIND_EXPR_VARS (body) == NULL_TREE)
+ body = BIND_EXPR_BODY (body);
+ }
+
+ return body;
+}
+
/* Parse the restricted form of loop statements allowed by OpenACC and OpenMP.
The real trick here is to determine the loop control variable early
so that we can push a new decl if necessary to make it private.
@@ -20404,17 +20958,14 @@ static tree
c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
tree clauses, tree *cclauses, bool *if_p)
{
- tree decl, cond, incr, body, init, stmt, cl;
- unsigned char save_in_statement;
- tree declv, condv, incrv, initv, ret = NULL_TREE;
- tree pre_body = NULL_TREE, this_pre_body;
+ tree body, stmt, cl;
+ tree ret = NULL_TREE;
tree ordered_cl = NULL_TREE;
- bool fail = false, open_brace_parsed = false;
- int i, collapse = 1, ordered = 0, count, nbraces = 0;
- location_t for_loc;
+ int i, collapse = 1, ordered = 0, count;
bool tiling = false;
bool inscan = false;
- vec<tree, va_gc> *for_block = make_tree_vector ();
+ struct omp_for_parse_data data;
+ struct omp_for_parse_data *save_data = parser->omp_for_parse_state;
for (cl = clauses; cl; cl = OMP_CLAUSE_CHAIN (cl))
if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_COLLAPSE)
@@ -20447,250 +20998,62 @@ c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
gcc_assert (tiling || (collapse >= 1 && ordered >= 0));
count = ordered ? ordered : collapse;
- declv = make_tree_vec (count);
- initv = make_tree_vec (count);
- condv = make_tree_vec (count);
- incrv = make_tree_vec (count);
-
if (!c_parser_next_token_is_keyword (parser, RID_FOR))
{
c_parser_error (parser, "for statement expected");
return NULL;
}
- for_loc = c_parser_peek_token (parser)->location;
- c_parser_consume_token (parser);
-
- /* Forbid break/continue in the loop initializer, condition, and
- increment expressions. */
- save_in_statement = in_statement;
- in_statement = IN_OMP_BLOCK;
-
- for (i = 0; i < count; i++)
- {
- int bracecount = 0;
-
- matching_parens parens;
- if (!parens.require_open (parser))
- goto pop_scopes;
-
- /* Parse the initialization declaration or expression. */
- if (c_parser_next_tokens_start_declaration (parser))
- {
- if (i > 0)
- vec_safe_push (for_block, c_begin_compound_stmt (true));
- this_pre_body = push_stmt_list ();
- c_in_omp_for = true;
- c_parser_declaration_or_fndef (parser, true, true, true, true, true);
- c_in_omp_for = false;
- if (this_pre_body)
- {
- this_pre_body = pop_stmt_list (this_pre_body);
- if (pre_body)
- {
- tree t = pre_body;
- pre_body = push_stmt_list ();
- add_stmt (t);
- add_stmt (this_pre_body);
- pre_body = pop_stmt_list (pre_body);
- }
- else
- pre_body = this_pre_body;
- }
- decl = check_for_loop_decls (for_loc, flag_isoc99);
- if (decl == NULL)
- goto error_init;
- if (DECL_INITIAL (decl) == error_mark_node)
- decl = error_mark_node;
- init = decl;
- }
- else if (c_parser_next_token_is (parser, CPP_NAME)
- && c_parser_peek_2nd_token (parser)->type == CPP_EQ)
- {
- struct c_expr decl_exp;
- struct c_expr init_exp;
- location_t init_loc;
-
- decl_exp = c_parser_postfix_expression (parser);
- decl = decl_exp.value;
-
- c_parser_require (parser, CPP_EQ, "expected %<=%>");
-
- init_loc = c_parser_peek_token (parser)->location;
- init_exp = c_parser_expr_no_commas (parser, NULL);
- init_exp = default_function_array_read_conversion (init_loc,
- init_exp);
- c_in_omp_for = true;
- init = build_modify_expr (init_loc, decl, decl_exp.original_type,
- NOP_EXPR, init_loc, init_exp.value,
- init_exp.original_type);
- c_in_omp_for = false;
- init = c_process_expr_stmt (init_loc, init);
-
- c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
- }
- else
- {
- error_init:
- c_parser_error (parser,
- "expected iteration declaration or initialization");
- c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
- "expected %<)%>");
- fail = true;
- goto parse_next;
- }
-
- /* Parse the loop condition. */
- cond = NULL_TREE;
- if (c_parser_next_token_is_not (parser, CPP_SEMICOLON))
- {
- location_t cond_loc = c_parser_peek_token (parser)->location;
- c_in_omp_for = true;
- struct c_expr cond_expr
- = c_parser_binary_expression (parser, NULL, NULL_TREE);
- c_in_omp_for = false;
-
- cond = cond_expr.value;
- cond = c_objc_common_truthvalue_conversion (cond_loc, cond);
- switch (cond_expr.original_code)
- {
- case GT_EXPR:
- case GE_EXPR:
- case LT_EXPR:
- case LE_EXPR:
- break;
- case NE_EXPR:
- if (code != OACC_LOOP)
- break;
- /* FALLTHRU. */
- default:
- /* Can't be cond = error_mark_node, because we want to preserve
- the location until c_finish_omp_for. */
- cond = build1 (NOP_EXPR, boolean_type_node, error_mark_node);
- break;
- }
- protected_set_expr_location (cond, cond_loc);
- }
- c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
-
- /* Parse the increment expression. */
- incr = NULL_TREE;
- if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN))
- {
- location_t incr_loc = c_parser_peek_token (parser)->location;
-
- incr = c_process_expr_stmt (incr_loc,
- c_parser_expression (parser).value);
- }
- parens.skip_until_found_close (parser);
-
- if (decl == NULL || decl == error_mark_node || init == error_mark_node)
- fail = true;
- else
- {
- TREE_VEC_ELT (declv, i) = decl;
- TREE_VEC_ELT (initv, i) = init;
- TREE_VEC_ELT (condv, i) = cond;
- TREE_VEC_ELT (incrv, i) = incr;
- }
-
- parse_next:
- if (i == count - 1)
- break;
-
- /* FIXME: OpenMP 3.0 draft isn't very clear on what exactly is allowed
- in between the collapsed for loops to be still considered perfectly
- nested. Hopefully the final version clarifies this.
- For now handle (multiple) {'s and empty statements. */
- do
- {
- if (c_parser_next_token_is_keyword (parser, RID_FOR))
- {
- c_parser_consume_token (parser);
- break;
- }
- else if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
- {
- c_parser_consume_token (parser);
- bracecount++;
- }
- else if (bracecount
- && c_parser_next_token_is (parser, CPP_SEMICOLON))
- c_parser_consume_token (parser);
- else
- {
- c_parser_error (parser, "not enough perfectly nested loops");
- if (bracecount)
- {
- open_brace_parsed = true;
- bracecount--;
- }
- fail = true;
- count = 0;
- break;
- }
- }
- while (1);
-
- nbraces += bracecount;
- }
-
- if (nbraces)
- if_p = NULL;
- in_statement = IN_OMP_FOR;
- body = push_stmt_list ();
-
- if (inscan)
- c_parser_omp_scan_loop_body (parser, open_brace_parsed);
- else if (open_brace_parsed)
- {
- location_t here = c_parser_peek_token (parser)->location;
- stmt = c_begin_compound_stmt (true);
- c_parser_compound_statement_nostart (parser);
- add_stmt (c_end_compound_stmt (here, stmt, true));
- }
- else
- add_stmt (c_parser_c99_block_statement (parser, if_p));
-
- body = pop_stmt_list (body);
- in_statement = save_in_statement;
-
- while (nbraces)
- {
- if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
- {
- c_parser_consume_token (parser);
- nbraces--;
- }
- else if (c_parser_next_token_is (parser, CPP_SEMICOLON))
- c_parser_consume_token (parser);
- else
- {
- c_parser_error (parser, "collapsed loops not perfectly nested");
- while (nbraces)
- {
- location_t here = c_parser_peek_token (parser)->location;
- stmt = c_begin_compound_stmt (true);
- add_stmt (body);
- c_parser_compound_statement_nostart (parser);
- body = c_end_compound_stmt (here, stmt, true);
- nbraces--;
- }
- goto pop_scopes;
- }
+ /* Initialize parse state for recursive descent. */
+ data.declv = make_tree_vec (count);
+ data.initv = make_tree_vec (count);
+ data.condv = make_tree_vec (count);
+ data.incrv = make_tree_vec (count);
+ data.pre_body = NULL_TREE;;
+ data.bindings = NULL_TREE;
+ data.for_loc = c_parser_peek_token (parser)->location;
+ data.count = count;
+ data.depth = 0;
+ data.want_nested_loop = true;
+ data.ordered = ordered > 0;
+ data.in_intervening_code = false;
+ data.perfect_nesting_fail = false;
+ data.fail = false;
+ data.inscan = inscan;
+ data.saw_intervening_code = false;
+ data.code = code;
+ parser->omp_for_parse_state = &data;
+
+ body = c_parser_omp_loop_nest (parser, if_p);
+
+ /* Add saved bindings for iteration variables that were declared in
+ the nested for loop to the scope surrounding the entire loop. */
+ for (tree t = data.bindings; t; )
+ {
+ tree n = TREE_CHAIN (t);
+ TREE_CHAIN (t) = NULL_TREE;
+ pushdecl (t);
+ t = n;
}
/* Only bother calling c_finish_omp_for if we haven't already generated
an error from the initialization parsing. */
- if (!fail)
+ if (!data.fail)
{
c_in_omp_for = true;
- stmt = c_finish_omp_for (loc, code, declv, NULL, initv, condv,
- incrv, body, pre_body, true);
+ stmt = c_finish_omp_for (loc, code, data.declv, NULL, data.initv,
+ data.condv, data.incrv,
+ body, data.pre_body, true);
c_in_omp_for = false;
/* Check for iterators appearing in lb, b or incr expressions. */
- if (stmt && !c_omp_check_loop_iv (stmt, declv, NULL))
+ if (stmt && !c_omp_check_loop_iv (stmt, data.declv, NULL))
+ stmt = NULL_TREE;
+
+ /* Check for errors involving lb/ub/incr expressions referencing
+ variables declared in intervening code. */
+ if (data.saw_intervening_code
+ && !c_omp_check_loop_binding_exprs (stmt, NULL))
stmt = NULL_TREE;
if (stmt)
@@ -20742,7 +21105,7 @@ c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
else
{
for (i = 0; i < count; i++)
- if (TREE_VEC_ELT (declv, i) == OMP_CLAUSE_DECL (*c))
+ if (TREE_VEC_ELT (data.declv, i) == OMP_CLAUSE_DECL (*c))
break;
if (i == count)
c = &OMP_CLAUSE_CHAIN (*c);
@@ -20755,7 +21118,8 @@ c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
}
else
{
- /* Move lastprivate (decl) clause to OMP_FOR_CLAUSES. */
+ /* Move lastprivate (decl) clause to
+ OMP_FOR_CLAUSES. */
tree l = *c;
*c = OMP_CLAUSE_CHAIN (*c);
if (code == OMP_SIMD)
@@ -20776,16 +21140,8 @@ c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
}
ret = stmt;
}
-pop_scopes:
- while (!for_block->is_empty ())
- {
- /* FIXME diagnostics: LOC below should be the actual location of
- this particular for block. We need to build a list of
- locations to go along with FOR_BLOCK. */
- stmt = c_end_compound_stmt (loc, for_block->pop (), true);
- add_stmt (stmt);
- }
- release_tree_vector (for_block);
+
+ parser->omp_for_parse_state = save_data;
return ret;
}