diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index eb901683b6dc6681f582f08fc715162c750c0e42..608d6310e530c34581f0e6f8b732e6cc229c89db 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7313,7 +7313,7 @@ extern bool maybe_clone_body (tree); /* In parser.cc */ extern tree cp_convert_range_for (tree, tree, tree, tree, unsigned int, bool, unsigned short, bool); -extern void cp_convert_omp_range_for (tree &, vec<tree, va_gc> *, tree &, +extern void cp_convert_omp_range_for (tree &, tree &, tree &, tree &, tree &, tree &, tree &, tree &); extern void cp_finish_omp_range_for (tree, tree); extern bool parsing_nsdmi (void); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index eeb22e44fb4a08fd4d54d6c0dc0b15b2c07b41cf..a192065c060238f3e03c85a7726b2a9efd6115f2 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -261,6 +261,10 @@ static bool cp_parser_omp_declare_reduction_exprs static void cp_finalize_oacc_routine (cp_parser *, tree, bool); +static void check_omp_intervening_code + (cp_parser *); + + /* Manifest constants. */ #define CP_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (cp_token)) #define CP_SAVED_TOKEN_STACK 5 @@ -2100,6 +2104,52 @@ struct cp_parser_expression_stack_entry typedef struct cp_parser_expression_stack_entry cp_parser_expression_stack[NUM_PREC_VALUES]; +/* 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 orig_declv; + auto_vec<tree, 4> orig_inits; + int count; /* Expected nesting depth. */ + int depth; /* Current nesting depth. */ + location_t for_loc; + releasing_vec init_blockv; + releasing_vec body_blockv; + releasing_vec init_placeholderv; + releasing_vec body_placeholderv; + 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; + tree clauses; + tree *cclauses; + tree ordered_cl; +}; + /* Prototypes. */ /* Constructors and destructors. */ @@ -2921,6 +2971,7 @@ static bool cp_parser_skip_up_to_closing_square_bracket static bool cp_parser_skip_to_closing_square_bracket (cp_parser *); static size_t cp_parser_skip_balanced_tokens (cp_parser *, size_t); +static tree cp_parser_omp_loop_nest (cp_parser *, bool *); // -------------------------------------------------------------------------- // // Unevaluated Operand Guard @@ -8004,12 +8055,22 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, complain); else /* All other function calls. */ - postfix_expression - = finish_call_expr (postfix_expression, &args, - /*disallow_virtual=*/false, - koenig_p, - complain); - + { + if (DECL_P (postfix_expression) + && parser->omp_for_parse_state + && parser->omp_for_parse_state->in_intervening_code + && omp_runtime_api_call (postfix_expression)) + { + error_at (loc, "calls to the OpenMP runtime API are " + "not permitted in intervening code"); + parser->omp_for_parse_state->fail = true; + } + postfix_expression + = finish_call_expr (postfix_expression, &args, + /*disallow_virtual=*/false, + koenig_p, + complain); + } if (close_paren_loc != UNKNOWN_LOCATION) postfix_expression.set_location (combined_loc); @@ -12522,9 +12583,15 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr, return so that we can check for a close brace. Otherwise we require a real statement and must go back and read one. */ if (in_compound_for_pragma) - cp_parser_pragma (parser, pragma_compound, if_p); + { + if (cp_parser_pragma (parser, pragma_compound, if_p) + && parser->omp_for_parse_state) + check_omp_intervening_code (parser); + } else if (!cp_parser_pragma (parser, pragma_stmt, if_p)) do_restart = true; + else if (parser->omp_for_parse_state) + check_omp_intervening_code (parser); if (parser->lexer != lexer && lexer->in_omp_attribute_pragma && (!in_omp_attribute_pragma || lexer->orphan_p)) @@ -12960,6 +13027,55 @@ cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr, return compound_stmt; } +/* 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 (cp_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) + { + 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; + } +} + /* Parse an (optional) statement-seq. statement-seq: @@ -12969,6 +13085,11 @@ cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr, static void cp_parser_statement_seq_opt (cp_parser* parser, tree in_statement_expr) { + 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; + /* Scan statements until there aren't any more. */ while (true) { @@ -12996,6 +13117,50 @@ cp_parser_statement_seq_opt (cp_parser* parser, tree in_statement_expr) } } + /* Handle special cases for OMP FOR canonical loop syntax. */ + else if (in_omp_loop_block) + { + bool want_nested_loop = omp_for_parse_state->want_nested_loop; + if (want_nested_loop + && token->type == CPP_KEYWORD && token->keyword == RID_FOR) + { + /* Found the nested loop. */ + omp_for_parse_state->depth++; + add_stmt (cp_parser_omp_loop_nest (parser, NULL)); + omp_for_parse_state->depth--; + } + else if (token->type == CPP_SEMICOLON) + { + /* Prior to implementing the OpenMP 5.1 syntax for canonical + loop form, GCC used to accept an empty statements as not + being intervening code. Continue to do that, as an + extension. */ + /* FIXME: Maybe issue a warning or something here? */ + cp_lexer_consume_token (parser->lexer); + } + else if (want_nested_loop && token->type == CPP_OPEN_BRACE) + /* The nested compound statement may contain the next loop, or + it might just be intervening code. */ + { + cp_parser_statement (parser, in_statement_expr, true, NULL); + if (omp_for_parse_state->want_nested_loop) + check_omp_intervening_code (parser); + } + else + { + /* This must be intervening code. */ + omp_for_parse_state->want_nested_loop = false; + /* Defer calling check_omp_intervening_code on pragmas until + cp_parser_statement, because we can't know until we parse + it whether or not the pragma is a statement. */ + if (token->type != CPP_PRAGMA) + check_omp_intervening_code (parser); + cp_parser_statement (parser, in_statement_expr, true, NULL); + omp_for_parse_state->want_nested_loop = want_nested_loop; + } + continue; + } + /* Parse the statement. */ cp_parser_statement (parser, in_statement_expr, true, NULL); } @@ -14195,6 +14360,15 @@ cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep, statement. */ in_statement = parser->in_statement; + /* Special case for OMP loop intervening code. Parsing of permitted + collapsed loop nests is handled elsewhere. */ + if (parser->omp_for_parse_state) + { + error_at (token->location, + "loop not permitted in intervening code in OpenMP loop body"); + parser->omp_for_parse_state->fail = true; + } + /* See what kind of keyword it is. */ keyword = token->keyword; switch (keyword) @@ -43062,7 +43236,19 @@ cp_parser_omp_for_incr (cp_parser *parser, tree decl) return build2 (MODIFY_EXPR, TREE_TYPE (decl), decl, rhs); } -/* Parse the initialization statement of an OpenMP for loop. +/* Parse the initialization statement of an OpenMP for loop. Range-for + is handled separately in cp_convert_omp_range_for. + + On entry SL is the current statement list. Parsing of some forms + of initialization pops this list and stores its contents in either INIT + or THIS_PRE_BODY, and sets SL to null. Initialization for class + iterators is added directly to SL and it is not popped until later. + + On return, DECL is set if the initialization is by binding the + iteration variable. If the initialization is by assignment, REAL_DECL + is set to point to a variable in an outer scope. ORIG_INIT is set + if the iteration variable is of class type; this is a copy saved for + error checking in finish_omp_for. Return true if the resulting construct should have an OMP_CLAUSE_PRIVATE added to it. */ @@ -43070,7 +43256,7 @@ cp_parser_omp_for_incr (cp_parser *parser, tree decl) static tree cp_parser_omp_for_loop_init (cp_parser *parser, tree &this_pre_body, - releasing_vec &for_block, + tree &sl, tree &init, tree &orig_init, tree &decl, @@ -43168,18 +43354,22 @@ cp_parser_omp_for_loop_init (cp_parser *parser, asm_specification, LOOKUP_ONLYCONVERTING); orig_init = init; + + /* In the case of a class iterator, do not pop sl here. + Both class initialization and finalization must happen in + the enclosing init block scope. For now set the init + expression to null; it'll be filled in properly in + finish_omp_for before stuffing it in the OMP_FOR. */ if (CLASS_TYPE_P (TREE_TYPE (decl))) + init = NULL_TREE; + else /* It is a parameterized type. */ { - vec_safe_push (for_block, this_pre_body); - init = NULL_TREE; - } - else - { - init = pop_stmt_list (this_pre_body); + init = pop_stmt_list (sl); + sl = NULL_TREE; if (init && TREE_CODE (init) == STATEMENT_LIST) { tree_stmt_iterator i = tsi_start (init); - /* Move lambda DECL_EXPRs to FOR_BLOCK. */ + /* Move lambda DECL_EXPRs to the enclosing block. */ while (!tsi_end_p (i)) { tree t = tsi_stmt (i); @@ -43187,7 +43377,7 @@ cp_parser_omp_for_loop_init (cp_parser *parser, && TREE_CODE (DECL_EXPR_DECL (t)) == TYPE_DECL) { tsi_delink (&i); - vec_safe_push (for_block, t); + add_stmt (t); continue; } break; @@ -43201,9 +43391,10 @@ cp_parser_omp_for_loop_init (cp_parser *parser, } } } - this_pre_body = NULL_TREE; } else + /* This is an initialized declaration of non-class, + non-parameterized type iteration variable. */ { /* Consume '='. */ cp_lexer_consume_token (parser->lexer); @@ -43217,6 +43408,8 @@ cp_parser_omp_for_loop_init (cp_parser *parser, /*init_const_expr_p=*/false, asm_specification, LOOKUP_ONLYCONVERTING); + this_pre_body = pop_stmt_list (sl); + sl = NULL_TREE; } if (pushed_scope) @@ -43288,14 +43481,21 @@ cp_parser_omp_for_loop_init (cp_parser *parser, real_decl = TREE_OPERAND (init, 0); } } + this_pre_body = pop_stmt_list (sl); + sl = NULL_TREE; } return add_private_clause; } -/* Helper for cp_parser_omp_for_loop, handle one range-for loop. */ +/* Helper for cp_parser_omp_loop_nest, handle one range-for loop + including introducing new temporaries for the range start and end, + doing auto deduction, and processing decomposition variables. + This function is also called from pt.cc during template instantiation. + In that case SL is NULL_TREE, otherwise it is the current statement + list. */ void -cp_convert_omp_range_for (tree &this_pre_body, vec<tree, va_gc> *for_block, +cp_convert_omp_range_for (tree &this_pre_body, tree &sl, tree &decl, tree &orig_decl, tree &init, tree &orig_init, tree &cond, tree &incr) { @@ -43331,8 +43531,11 @@ cp_convert_omp_range_for (tree &this_pre_body, vec<tree, va_gc> *for_block, cond = global_namespace; incr = NULL_TREE; orig_init = init; - if (this_pre_body) - this_pre_body = pop_stmt_list (this_pre_body); + if (sl) + { + this_pre_body = pop_stmt_list (sl); + sl = NULL_TREE; + } return; } @@ -43412,11 +43615,7 @@ cp_convert_omp_range_for (tree &this_pre_body, vec<tree, va_gc> *for_block, orig_decl = decl; decl = begin; - if (for_block) - { - vec_safe_push (for_block, this_pre_body); - this_pre_body = NULL_TREE; - } + /* Defer popping sl here. */ tree decomp_first_name = NULL_TREE; unsigned decomp_cnt = 0; @@ -43454,6 +43653,15 @@ cp_convert_omp_range_for (tree &this_pre_body, vec<tree, va_gc> *for_block, } } + /* The output ORIG_DECL is not a decl. Instead, it is a tree structure + that holds decls for variables implementing the iterator, represented + as a TREE_LIST whose TREE_CHAIN is a vector. The first two elements + of the vector are decls of scratch variables for the range start and + end that will eventually be bound in the implicit scope surrounding + the whole loop nest. The remaining elements are decls of derived + decomposition variables that are bound inside the loop body. This + structure is further mangled by finish_omp_for into the form required + for the OMP_FOR_ORIG_DECLS field of the OMP_FOR tree node. */ tree v = make_tree_vec (decomp_cnt + 3); TREE_VEC_ELT (v, 0) = range_temp_decl; TREE_VEC_ELT (v, 1) = end; @@ -43708,404 +43916,762 @@ cp_parser_omp_scan_loop_body (cp_parser *parser) braces.require_close (parser); } -/* Parse the restricted form of the for statement allowed by OpenMP. */ -static tree -cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses, - tree *cclauses, bool *if_p) -{ - tree init, orig_init, cond, incr, body, decl, pre_body = NULL_TREE, ret; - tree orig_decl; - tree real_decl, initv, condv, incrv, declv, orig_declv; - tree this_pre_body, cl, ordered_cl = NULL_TREE; - location_t loc_first; - bool collapse_err = false; - int i, collapse = 1, ordered = 0, count, nbraces = 0; - releasing_vec for_block; - auto_vec<tree, 4> orig_inits; - bool tiling = false; - bool inscan = false; +/* This function parses a single level of a loop nest, invoking itself + recursively if necessary. - for (cl = clauses; cl; cl = OMP_CLAUSE_CHAIN (cl)) - if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_COLLAPSE) - collapse = tree_to_shwi (OMP_CLAUSE_COLLAPSE_EXPR (cl)); - else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_TILE) - { - tiling = true; - collapse = list_length (OMP_CLAUSE_TILE_LIST (cl)); - } - else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_ORDERED - && OMP_CLAUSE_ORDERED_EXPR (cl)) - { - ordered_cl = cl; - ordered = tree_to_shwi (OMP_CLAUSE_ORDERED_EXPR (cl)); - } - else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_REDUCTION - && OMP_CLAUSE_REDUCTION_INSCAN (cl) - && (code == OMP_SIMD || code == OMP_FOR)) - inscan = true; + 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 - if (ordered && ordered < collapse) - { - error_at (OMP_CLAUSE_LOCATION (ordered_cl), - "%<ordered%> clause parameter is less than %<collapse%>"); - OMP_CLAUSE_ORDERED_EXPR (ordered_cl) - = build_int_cst (NULL_TREE, collapse); - ordered = collapse; - } + For a collapsed loop nest, only a single OMP_FOR is built, pulling out + all the iterator information from the inner loops into vectors in the + parser->omp_for_parse_state structure. - gcc_assert (tiling || (collapse >= 1 && ordered >= 0)); - count = ordered ? ordered : collapse; + In the "range for" case, it is transformed into a regular "for" iterator + by introducing some temporary variables for the begin/end, + as well as bindings of the actual iteration variables which are + injected into the body of the loop. - declv = make_tree_vec (count); - initv = make_tree_vec (count); - condv = make_tree_vec (count); - incrv = make_tree_vec (count); - orig_declv = NULL_TREE; + Initialization code for iterator variables may end up either in the + init vector (simple assignments), in omp_for_parse_state->pre_body + (decl_exprs for iterators bound in the for statement), or in the + scope surrounding this level of loop initialization. - loc_first = cp_lexer_peek_token (parser->lexer)->location; + The scopes of class iterator variables and their finalizers need to + be adjusted after parsing so that all of the initialization happens + in a scope surrounding all of the intervening and body code. For + this reason we separately store the initialization and body blocks + for each level of loops in the omp_for_parse_state structure and + reassemble/reorder them in cp_parser_omp_for. See additional + comments there about the use of placeholders, etc. */ - for (i = 0; i < count; i++) - { - int bracecount = 0; - tree add_private_clause = NULL_TREE; - location_t loc; +static tree +cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p) +{ + tree decl, cond, incr, init; + tree orig_init, real_decl, orig_decl; + tree init_block, body_block; + tree init_placeholder, body_placeholder; + tree init_scope; + tree this_pre_body = NULL_TREE; + bool moreloops; + unsigned char save_in_statement; + tree add_private_clause = NULL_TREE; + location_t loc; + bool is_range_for = false; + tree sl = NULL_TREE; + 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. */ + gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR)); + loc = cp_lexer_consume_token (parser->lexer)->location; + + /* Forbid break/continue in the loop initializer, condition, and + increment expressions. */ + save_in_statement = parser->in_statement; + parser->in_statement = IN_OMP_BLOCK; + + /* We are not in intervening code now. */ + omp_for_parse_state->in_intervening_code = false; + + /* Don't create location wrapper nodes within an OpenMP "for" + statement. */ + auto_suppress_location_wrappers sentinel; - if (!cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR)) - { - if (!collapse_err) - cp_parser_error (parser, "for statement expected"); - return NULL; - } - loc = cp_lexer_consume_token (parser->lexer)->location; + matching_parens parens; + if (!parens.require_open (parser)) + return NULL; - /* Don't create location wrapper nodes within an OpenMP "for" - statement. */ - auto_suppress_location_wrappers sentinel; + init = orig_init = decl = real_decl = orig_decl = NULL_TREE; - matching_parens parens; - if (!parens.require_open (parser)) - return NULL; + init_placeholder = build_stmt (input_location, EXPR_STMT, + integer_zero_node); + vec_safe_push (omp_for_parse_state->init_placeholderv, init_placeholder); - init = orig_init = decl = real_decl = orig_decl = NULL_TREE; - this_pre_body = push_stmt_list (); + /* The init_block acts as a container for this level of loop goo. */ + init_block = push_stmt_list (); + vec_safe_push (omp_for_parse_state->init_blockv, init_block); - if (code != OACC_LOOP && cxx_dialect >= cxx11) - { - /* Save tokens so that we can put them back. */ - cp_lexer_save_tokens (parser->lexer); + /* Wrap a scope around this entire level of loop to hold bindings + of loop iteration variables. We can't insert them directly + in the containing scope because that would cause their visibility to + be incorrect with respect to intervening code after this loop. + We will combine the nested init_scopes in postprocessing after the + entire loop is parsed. */ + init_scope = begin_compound_stmt (0); - /* Look for ':' that is not nested in () or {}. */ - bool is_range_for - = (cp_parser_skip_to_closing_parenthesis_1 (parser, - /*recovering=*/false, - CPP_COLON, - /*consume_paren=*/ - false) == -1); + /* Now we need another level of statement list container to capture the + initialization (and possible finalization) bits. In some cases this + container may be popped off during initializer parsing to store code in + INIT or THIS_PRE_BODY, depending on the form of initialization. If + we have a class iterator we will pop it at the end of parsing this + level, so the cleanups are handled correctly. */ + sl = push_stmt_list (); - /* Roll back the tokens we skipped. */ - cp_lexer_rollback_tokens (parser->lexer); + if (omp_for_parse_state->code != OACC_LOOP && cxx_dialect >= cxx11) + { + /* Save tokens so that we can put them back. */ + cp_lexer_save_tokens (parser->lexer); - if (is_range_for) - { - bool saved_colon_corrects_to_scope_p - = parser->colon_corrects_to_scope_p; + /* Look for ':' that is not nested in () or {}. */ + is_range_for + = (cp_parser_skip_to_closing_parenthesis_1 (parser, + /*recovering=*/false, + CPP_COLON, + /*consume_paren=*/ + false) == -1); - /* A colon is used in range-based for. */ - parser->colon_corrects_to_scope_p = false; + /* Roll back the tokens we skipped. */ + cp_lexer_rollback_tokens (parser->lexer); - /* Parse the declaration. */ - cp_parser_simple_declaration (parser, - /*function_definition_allowed_p=*/ - false, &decl); - parser->colon_corrects_to_scope_p - = saved_colon_corrects_to_scope_p; + if (is_range_for) + { + bool saved_colon_corrects_to_scope_p + = parser->colon_corrects_to_scope_p; - cp_parser_require (parser, CPP_COLON, RT_COLON); + /* A colon is used in range-based for. */ + parser->colon_corrects_to_scope_p = false; - init = cp_parser_range_for (parser, NULL_TREE, NULL_TREE, decl, - false, 0, false, true); + /* Parse the declaration. */ + cp_parser_simple_declaration (parser, + /*function_definition_allowed_p=*/ + false, &decl); + parser->colon_corrects_to_scope_p + = saved_colon_corrects_to_scope_p; - cp_convert_omp_range_for (this_pre_body, for_block, decl, - orig_decl, init, orig_init, - cond, incr); - if (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; - } + cp_parser_require (parser, CPP_COLON, RT_COLON); - if (ordered_cl) - error_at (OMP_CLAUSE_LOCATION (ordered_cl), - "%<ordered%> clause with parameter on " - "range-based %<for%> loop"); + init = cp_parser_range_for (parser, NULL_TREE, NULL_TREE, decl, + false, 0, false, true); - goto parse_close_paren; - } - } + cp_convert_omp_range_for (this_pre_body, sl, decl, + orig_decl, init, orig_init, + cond, incr); - add_private_clause - = cp_parser_omp_for_loop_init (parser, this_pre_body, for_block, - init, orig_init, decl, real_decl); + if (omp_for_parse_state->ordered_cl) + error_at (OMP_CLAUSE_LOCATION (omp_for_parse_state->ordered_cl), + "%<ordered%> clause with parameter on " + "range-based %<for%> loop"); - cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); - 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; + goto parse_close_paren; } + } - if (decl) - real_decl = decl; - if (cclauses != NULL - && cclauses[C_OMP_CLAUSE_SPLIT_PARALLEL] != NULL - && real_decl != NULL_TREE - && code != OMP_LOOP) - { - tree *c; - for (c = &cclauses[C_OMP_CLAUSE_SPLIT_PARALLEL]; *c ; ) - if (OMP_CLAUSE_CODE (*c) == OMP_CLAUSE_FIRSTPRIVATE - && OMP_CLAUSE_DECL (*c) == real_decl) - { - error_at (loc, "iteration variable %qD" - " should not be firstprivate", real_decl); - *c = OMP_CLAUSE_CHAIN (*c); - } - else if (OMP_CLAUSE_CODE (*c) == OMP_CLAUSE_LASTPRIVATE - && OMP_CLAUSE_DECL (*c) == real_decl) + add_private_clause + = cp_parser_omp_for_loop_init (parser, this_pre_body, sl, + init, orig_init, decl, real_decl); + + cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); + + /* If the iteration variable was introduced via a declaration in the + for statement, DECL points at it. Otherwise DECL is null and + REAL_DECL is a variable previously declared in an outer scope. + Make REAL_DECL point at the iteration variable no matter where it + was introduced. */ + if (decl) + real_decl = decl; + + /* Some clauses treat iterator variables specially. */ + if (omp_for_parse_state->cclauses != NULL + && omp_for_parse_state->cclauses[C_OMP_CLAUSE_SPLIT_PARALLEL] != NULL + && real_decl != NULL_TREE + && omp_for_parse_state->code != OMP_LOOP) + { + tree *c; + for (c = &(omp_for_parse_state->cclauses[C_OMP_CLAUSE_SPLIT_PARALLEL]); + *c ; ) + if (OMP_CLAUSE_CODE (*c) == OMP_CLAUSE_FIRSTPRIVATE + && OMP_CLAUSE_DECL (*c) == real_decl) + { + error_at (loc, "iteration variable %qD" + " should not be firstprivate", real_decl); + *c = OMP_CLAUSE_CHAIN (*c); + } + else if (OMP_CLAUSE_CODE (*c) == OMP_CLAUSE_LASTPRIVATE + && OMP_CLAUSE_DECL (*c) == real_decl) + { + /* Move lastprivate (decl) clause to OMP_FOR_CLAUSES. */ + tree l = *c; + *c = OMP_CLAUSE_CHAIN (*c); + if (omp_for_parse_state->code == OMP_SIMD) { - /* Move lastprivate (decl) clause to OMP_FOR_CLAUSES. */ - tree l = *c; - *c = OMP_CLAUSE_CHAIN (*c); - if (code == OMP_SIMD) - { - OMP_CLAUSE_CHAIN (l) = cclauses[C_OMP_CLAUSE_SPLIT_FOR]; - cclauses[C_OMP_CLAUSE_SPLIT_FOR] = l; - } - else - { - OMP_CLAUSE_CHAIN (l) = clauses; - clauses = l; - } - add_private_clause = NULL_TREE; + OMP_CLAUSE_CHAIN (l) + = omp_for_parse_state->cclauses[C_OMP_CLAUSE_SPLIT_FOR]; + omp_for_parse_state->cclauses[C_OMP_CLAUSE_SPLIT_FOR] = l; } else { - if (OMP_CLAUSE_CODE (*c) == OMP_CLAUSE_PRIVATE - && OMP_CLAUSE_DECL (*c) == real_decl) - add_private_clause = NULL_TREE; - c = &OMP_CLAUSE_CHAIN (*c); + OMP_CLAUSE_CHAIN (l) = omp_for_parse_state->clauses; + omp_for_parse_state->clauses = l; } - } + add_private_clause = NULL_TREE; + } + else + { + if (OMP_CLAUSE_CODE (*c) == OMP_CLAUSE_PRIVATE + && OMP_CLAUSE_DECL (*c) == real_decl) + add_private_clause = NULL_TREE; + c = &OMP_CLAUSE_CHAIN (*c); + } + } - if (add_private_clause) + if (add_private_clause) + { + tree c; + for (c = omp_for_parse_state->clauses; c ; c = OMP_CLAUSE_CHAIN (c)) { - tree c; - for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE) + && OMP_CLAUSE_DECL (c) == decl) + break; + else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE + && OMP_CLAUSE_DECL (c) == decl) + error_at (loc, "iteration variable %qD " + "should not be firstprivate", + decl); + else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION) + && OMP_CLAUSE_DECL (c) == decl) + error_at (loc, "iteration variable %qD should not be reduction", + decl); + } + if (c == NULL) + { + if ((omp_for_parse_state->code == OMP_SIMD + && omp_for_parse_state->count != 1) + || omp_for_parse_state->code == OMP_LOOP) + c = build_omp_clause (loc, OMP_CLAUSE_LASTPRIVATE); + else if (omp_for_parse_state->code != OMP_SIMD) + c = build_omp_clause (loc, OMP_CLAUSE_PRIVATE); + else + c = build_omp_clause (loc, OMP_CLAUSE_LINEAR); + OMP_CLAUSE_DECL (c) = add_private_clause; + c = finish_omp_clauses (c, C_ORT_OMP); + if (c) { - if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE - || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE) - && OMP_CLAUSE_DECL (c) == decl) - break; - else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE - && OMP_CLAUSE_DECL (c) == decl) - error_at (loc, "iteration variable %qD " - "should not be firstprivate", - decl); - else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION - || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION) - && OMP_CLAUSE_DECL (c) == decl) - error_at (loc, "iteration variable %qD should not be reduction", - decl); - } - if (c == NULL) - { - if ((code == OMP_SIMD && collapse != 1) || code == OMP_LOOP) - c = build_omp_clause (loc, OMP_CLAUSE_LASTPRIVATE); - else if (code != OMP_SIMD) - c = build_omp_clause (loc, OMP_CLAUSE_PRIVATE); - else - c = build_omp_clause (loc, OMP_CLAUSE_LINEAR); - OMP_CLAUSE_DECL (c) = add_private_clause; - c = finish_omp_clauses (c, C_ORT_OMP); - if (c) - { - OMP_CLAUSE_CHAIN (c) = clauses; - clauses = c; - /* For linear, signal that we need to fill up - the so far unknown linear step. */ - if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINEAR) - OMP_CLAUSE_LINEAR_STEP (c) = NULL_TREE; - } + OMP_CLAUSE_CHAIN (c) = omp_for_parse_state->clauses; + omp_for_parse_state->clauses = c; + /* For linear, signal that we need to fill up + the so far unknown linear step. */ + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINEAR) + OMP_CLAUSE_LINEAR_STEP (c) = NULL_TREE; } } + } - cond = NULL; - if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) - cond = cp_parser_omp_for_cond (parser, decl, code); - cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); + cond = NULL; + if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) + cond = cp_parser_omp_for_cond (parser, decl, omp_for_parse_state->code); + cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); - incr = NULL; - if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)) - { - /* If decl is an iterator, preserve the operator on decl - until finish_omp_for. */ - if (real_decl - && ((processing_template_decl - && (TREE_TYPE (real_decl) == NULL_TREE - || !INDIRECT_TYPE_P (TREE_TYPE (real_decl)))) - || CLASS_TYPE_P (TREE_TYPE (real_decl)))) - incr = cp_parser_omp_for_incr (parser, real_decl); - else - incr = cp_parser_expression (parser); - protected_set_expr_location_if_unset (incr, input_location); - } + incr = NULL; + if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)) + { + /* If decl is an iterator, preserve the operator on decl + until finish_omp_for. */ + if (real_decl + && ((processing_template_decl + && (TREE_TYPE (real_decl) == NULL_TREE + || !INDIRECT_TYPE_P (TREE_TYPE (real_decl)))) + || CLASS_TYPE_P (TREE_TYPE (real_decl)))) + incr = cp_parser_omp_for_incr (parser, real_decl); + else + incr = cp_parser_expression (parser); + protected_set_expr_location_if_unset (incr, input_location); + } - parse_close_paren: - if (!parens.require_close (parser)) - cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, - /*or_comma=*/false, - /*consume_paren=*/true); + parse_close_paren: + if (!parens.require_close (parser)) + cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, + /*or_comma=*/false, + /*consume_paren=*/true); - TREE_VEC_ELT (declv, i) = decl; - TREE_VEC_ELT (initv, i) = init; - TREE_VEC_ELT (condv, i) = cond; - TREE_VEC_ELT (incrv, i) = incr; - if (orig_init) - { - orig_inits.safe_grow_cleared (i + 1, true); - orig_inits[i] = orig_init; - } - if (orig_decl) + /* We've parsed all the for (...) stuff now. Store the bits. */ + 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; + if (orig_init) + { + omp_for_parse_state->orig_inits.safe_grow_cleared (depth + 1, true); + omp_for_parse_state->orig_inits[depth] = orig_init; + } + if (orig_decl) + { + if (!omp_for_parse_state->orig_declv) + omp_for_parse_state->orig_declv + = copy_node (omp_for_parse_state->declv); + TREE_VEC_ELT (omp_for_parse_state->orig_declv, depth) = orig_decl; + } + else if (omp_for_parse_state->orig_declv) + TREE_VEC_ELT (omp_for_parse_state->orig_declv, depth) = decl; + if (this_pre_body) + append_to_statement_list_force (this_pre_body, + &(omp_for_parse_state->pre_body)); + + /* Start a nested block for the loop body. */ + body_placeholder = build_stmt (input_location, EXPR_STMT, + integer_zero_node); + vec_safe_push (omp_for_parse_state->body_placeholderv, body_placeholder); + body_block = push_stmt_list (); + vec_safe_push (omp_for_parse_state->body_blockv, body_block); + + moreloops = depth < omp_for_parse_state->count - 1; + omp_for_parse_state->want_nested_loop = moreloops; + if (moreloops && cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR)) + { + omp_for_parse_state->depth++; + add_stmt (cp_parser_omp_loop_nest (parser, if_p)); + omp_for_parse_state->depth--; + } + else if (moreloops + && cp_lexer_next_token_is (parser->lexer, 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; + cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); + omp_for_parse_state->in_intervening_code = false; + + if (omp_for_parse_state->want_nested_loop) { - if (!orig_declv) - orig_declv = copy_node (declv); - TREE_VEC_ELT (orig_declv, i) = orig_decl; + /* 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; } - else if (orig_declv) - TREE_VEC_ELT (orig_declv, i) = decl; + 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; + } + parser->in_statement = IN_OMP_FOR; + + /* Generate the parts of range for that belong in the loop body, + to be executed on every iteration. This includes setting the + user-declared decomposition variables from the compiler-generated + temporaries that are the real iteration variables for OMP_FOR. + FIXME: Not sure if this is correct with respect to visibility + of the variables from intervening code. However, putting this + code in each level of loop instead of all around the innermost + body also makes the decomposition variables visible to the + inner for init/bound/step exressions, which is not supposed to + happen and causes test failures. */ + if (omp_for_parse_state->orig_declv) + for (int i = 0; i < omp_for_parse_state->count; i++) + { + tree o = TREE_VEC_ELT (omp_for_parse_state->orig_declv, i); + tree d = TREE_VEC_ELT (omp_for_parse_state->declv, i); + if (o != d) + cp_finish_omp_range_for (o, d); + } - if (i == count - 1) - break; + /* Now parse the final-loop-body for the innermost loop. */ + parser->omp_for_parse_state = NULL; + if (omp_for_parse_state->inscan) + cp_parser_omp_scan_loop_body (parser); + else + cp_parser_statement (parser, NULL_TREE, false, if_p); + parser->omp_for_parse_state = omp_for_parse_state; + } + parser->in_statement = save_in_statement; + omp_for_parse_state->want_nested_loop = false; + omp_for_parse_state->in_intervening_code = true; + + /* Pop and remember the body block. Add the body placeholder + to the surrounding statement list instead. This is just a unique + token that will be replaced when we reassemble the generated + code for the entire omp for statement. */ + body_block = pop_stmt_list (body_block); + omp_for_parse_state->body_blockv[depth] = body_block; + add_stmt (body_placeholder); + + /* Pop and remember the init block. */ + if (sl) + add_stmt (pop_stmt_list (sl)); + finish_compound_stmt (init_scope); + init_block = pop_stmt_list (init_block); + omp_for_parse_state->init_blockv[depth] = init_block; + + /* Return the init placeholder rather than the remembered init block. + Again, this is just a unique cookie that will be used to reassemble + code pieces when the entire omp for statement has been parsed. */ + return init_placeholder; +} + +/* Worker for find_structured_blocks. *TP points to a STATEMENT_LIST + and ITER is the element that is or contains a nested loop. This + function moves the statements before and after ITER into + OMP_STRUCTURED_BLOCKs and modifies *TP. */ +static void +insert_structured_blocks (tree *tp, tree_stmt_iterator iter) +{ + tree sl = push_stmt_list (); + for (tree_stmt_iterator i = tsi_start (*tp); !tsi_end_p (i); ) + if (i == iter) + { + sl = pop_stmt_list (sl); + if (TREE_CODE (sl) != STATEMENT_LIST || !tsi_end_p (tsi_start (sl))) + tsi_link_before (&i, + build1 (OMP_STRUCTURED_BLOCK, void_type_node, sl), + TSI_SAME_STMT); + i++; + sl = push_stmt_list (); + } + else + { + tree s = tsi_stmt (i); + tsi_delink (&i); /* Advances i to next statement. */ + add_stmt (s); + } + sl = pop_stmt_list (sl); + if (TREE_CODE (sl) != STATEMENT_LIST || !tsi_end_p (tsi_start (sl))) + tsi_link_after (&iter, + build1 (OMP_STRUCTURED_BLOCK, void_type_node, sl), + TSI_SAME_STMT); +} - /* 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. */ - cp_parser_parse_tentatively (parser); - for (;;) +/* Helper to find and mark structured blocks in intervening code for a + single loop level with markers for later error checking. *TP is the + piece of code to be marked and INNER is the inner loop placeholder. + Returns true if INNER was found (recursively) in *TP. */ +static bool +find_structured_blocks (tree *tp, tree inner) +{ + if (*tp == inner) + return true; + else if (TREE_CODE (*tp) == BIND_EXPR) + return find_structured_blocks (&(BIND_EXPR_BODY (*tp)), inner); + else if (TREE_CODE (*tp) == STATEMENT_LIST) + { + for (tree_stmt_iterator i = tsi_start (*tp); !tsi_end_p (i); ++i) { - if (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR)) - break; - else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) + tree *p = tsi_stmt_ptr (i); + /* The normal case is that there is no intervening code and we + do not have to insert any OMP_STRUCTURED_BLOCK markers. */ + if (find_structured_blocks (p, inner)) { - cp_lexer_consume_token (parser->lexer); - bracecount++; - } - else if (bracecount - && cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) - cp_lexer_consume_token (parser->lexer); - else - { - loc = cp_lexer_peek_token (parser->lexer)->location; - error_at (loc, "not enough for loops to collapse"); - collapse_err = true; - cp_parser_abort_tentative_parse (parser); - declv = NULL_TREE; - break; + if (!(i == tsi_start (*tp) && i == tsi_last (*tp))) + insert_structured_blocks (tp, i); + return true; } } + return false; + } + else if (TREE_CODE (*tp) == TRY_FINALLY_EXPR) + return find_structured_blocks (&(TREE_OPERAND (*tp, 0)), inner); + else if (TREE_CODE (*tp) == CLEANUP_STMT) + return find_structured_blocks (&(CLEANUP_BODY (*tp)), inner); + else + return false; +} + +/* Helpers used for relinking tree structures: In tree rooted at + CONTEXT, replace ORIG with REPLACEMENT. If FLATTEN is true, try to combine + nested BIND_EXPRs. Gives an assertion if it fails to find ORIG. */ + +struct sit_data { + tree orig; + tree repl; + bool flatten; +}; + +static tree +substitute_in_tree_walker (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, + void *dp) +{ + struct sit_data *sit = (struct sit_data *)dp; + if (*tp == sit->orig) + { + *tp = sit->repl; + return *tp; + } + /* Remove redundant BIND_EXPRs with no bindings even when not specifically + trying to flatten. */ + else if (TREE_CODE (*tp) == BIND_EXPR + && BIND_EXPR_BODY (*tp) == sit->orig + && !BIND_EXPR_VARS (*tp) + && (sit->flatten || TREE_CODE (sit->repl) == BIND_EXPR)) + { + *tp = sit->repl; + return *tp; + } + else if (sit->flatten + && TREE_CODE (*tp) == BIND_EXPR + && TREE_CODE (sit->repl) == BIND_EXPR) + { + if (BIND_EXPR_BODY (*tp) == sit->orig) + { + /* Merge binding lists for two directly nested BIND_EXPRs, + keeping the outer one. */ + BIND_EXPR_VARS (*tp) = chainon (BIND_EXPR_VARS (*tp), + BIND_EXPR_VARS (sit->repl)); + BIND_EXPR_BODY (*tp) = BIND_EXPR_BODY (sit->repl); + return *tp; + } + else if (TREE_CODE (BIND_EXPR_BODY (*tp)) == STATEMENT_LIST) + /* There might be a statement list containing cleanup_points + etc between the two levels of BIND_EXPR. We can still merge + them, again keeping the outer BIND_EXPR. */ + for (tree_stmt_iterator i = tsi_start (BIND_EXPR_BODY (*tp)); + !tsi_end_p (i); ++i) + { + tree *p = tsi_stmt_ptr (i); + if (*p == sit->orig) + { + BIND_EXPR_VARS (*tp) = chainon (BIND_EXPR_VARS (*tp), + BIND_EXPR_VARS (sit->repl)); + *p = BIND_EXPR_BODY (sit->repl); + return *tp; + } + } + } + return NULL; +} + +static void +substitute_in_tree (tree *context, tree orig, tree repl, bool flatten) +{ + struct sit_data data; + + gcc_assert (*context && orig && repl); + if (TREE_CODE (repl) == BIND_EXPR && !BIND_EXPR_VARS (repl)) + repl = BIND_EXPR_BODY (repl); + data.orig = orig; + data.repl = repl; + data.flatten = flatten; + + tree result = cp_walk_tree (context, substitute_in_tree_walker, + (void *)&data, NULL); + gcc_assert (result != NULL_TREE); +} - if (declv) +/* Walker to patch up the BLOCK_NODE hierarchy after the above surgery. + *DP is is the parent block. */ + +static tree +fixup_blocks_walker (tree *tp, int *walk_subtrees, void *dp) +{ + tree superblock = *(tree *)dp; + + if (TREE_CODE (*tp) == BIND_EXPR) + { + tree block = BIND_EXPR_BLOCK (*tp); + if (superblock) { - cp_parser_parse_definitely (parser); - nbraces += bracecount; + BLOCK_SUPERCONTEXT (block) = superblock; + BLOCK_CHAIN (block) = BLOCK_SUBBLOCKS (superblock); + BLOCK_SUBBLOCKS (superblock) = block; } + BLOCK_SUBBLOCKS (block) = NULL_TREE; + cp_walk_tree (&BIND_EXPR_BODY (*tp), fixup_blocks_walker, + (void *)&block, NULL); + *walk_subtrees = 0; } - if (nbraces) - if_p = NULL; + return NULL; +} - /* Note that we saved the original contents of this flag when we entered - the structured block, and so we don't need to re-save it here. */ - parser->in_statement = IN_OMP_FOR; +/* Parse the restricted form of the for statement allowed by OpenMP. */ + +static tree +cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses, + tree *cclauses, bool *if_p) +{ + tree ret; + tree cl, ordered_cl = NULL_TREE; + int collapse = 1, ordered = 0; + unsigned int count; + bool tiling = false; + bool inscan = false; + struct omp_for_parse_data data; + struct omp_for_parse_data *save_data = parser->omp_for_parse_state; + tree result; + location_t loc_first = cp_lexer_peek_token (parser->lexer)->location; + + for (cl = clauses; cl; cl = OMP_CLAUSE_CHAIN (cl)) + if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_COLLAPSE) + collapse = tree_to_shwi (OMP_CLAUSE_COLLAPSE_EXPR (cl)); + else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_TILE) + { + tiling = true; + collapse = list_length (OMP_CLAUSE_TILE_LIST (cl)); + } + else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_ORDERED + && OMP_CLAUSE_ORDERED_EXPR (cl)) + { + ordered_cl = cl; + ordered = tree_to_shwi (OMP_CLAUSE_ORDERED_EXPR (cl)); + } + else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_INSCAN (cl) + && (code == OMP_SIMD || code == OMP_FOR)) + inscan = true; - /* Note that the grammar doesn't call for a structured block here, - though the loop as a whole is a structured block. */ - if (orig_declv) + if (ordered && ordered < collapse) { - body = begin_omp_structured_block (); - for (i = 0; i < count; i++) - if (TREE_VEC_ELT (orig_declv, i) != TREE_VEC_ELT (declv, i)) - cp_finish_omp_range_for (TREE_VEC_ELT (orig_declv, i), - TREE_VEC_ELT (declv, i)); + error_at (OMP_CLAUSE_LOCATION (ordered_cl), + "%<ordered%> clause parameter is less than %<collapse%>"); + OMP_CLAUSE_ORDERED_EXPR (ordered_cl) + = build_int_cst (NULL_TREE, collapse); + ordered = collapse; + } + + gcc_assert (tiling || (collapse >= 1 && ordered >= 0)); + count = ordered ? ordered : collapse; + + if (!cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR)) + { + cp_parser_error (parser, "for statement expected"); + return NULL; } - else - body = push_stmt_list (); - if (inscan) - cp_parser_omp_scan_loop_body (parser); - else - cp_parser_statement (parser, NULL_TREE, false, if_p); - if (orig_declv) - body = finish_omp_structured_block (body); - else - body = pop_stmt_list (body); - if (declv == NULL_TREE) + /* 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.for_loc = cp_lexer_peek_token (parser->lexer)->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; + data.orig_declv = NULL_TREE; + data.clauses = clauses; + data.cclauses = cclauses; + data.ordered_cl = ordered_cl; + parser->omp_for_parse_state = &data; + + cp_parser_omp_loop_nest (parser, if_p); + + /* Bomb out early if there was an error (not enough loops, etc). */ + if (data.fail || data.declv == NULL_TREE) + { + parser->omp_for_parse_state = save_data; + return NULL_TREE; + } + + /* Relink the init and body blocks that were built during parsing. At + this point we have a structure nested like + init 0 + body 0 + init 1 + body 1 + init 2 + body 2 + and we want to turn it into + init 0 + init 1 + init 2 + omp_for + body 0 + body 1 + body 2 + We also need to flatten the init blocks, as some code for later + processing of combined directives gets confused otherwise. */ + + gcc_assert (vec_safe_length (data.init_blockv) == count); + gcc_assert (vec_safe_length (data.body_blockv) == count); + gcc_assert (vec_safe_length (data.init_placeholderv) == count); + gcc_assert (vec_safe_length (data.body_placeholderv) == count); + + /* First insert markers for structured blocks for intervening code in + the loop bodies. */ + for (unsigned int i = 0; i < count - 1; i++) + { + bool good = find_structured_blocks (&(data.body_blockv[i]), + data.init_placeholderv[i+1]); + gcc_assert (good); + } + + /* Do the substitution from the inside out. */ + for (unsigned int i = count - 1; i > 0; i--) + { + substitute_in_tree (&(data.body_blockv[i-1]), + data.init_placeholderv[i], + data.body_blockv[i], false); + substitute_in_tree (&(data.init_blockv[i-1]), + data.body_placeholderv[i-1], + data.init_blockv[i], true); + } + + /* Generate the OMP_FOR. Note finish_omp_for adds the OMP_FOR + (and possibly other stuff) to the current statement list but + returns a pointer to the OMP_FOR itself, or null in case of error. */ + result = push_stmt_list (); + ret = finish_omp_for (loc_first, code, data.declv, data.orig_declv, + data.initv, data.condv, data.incrv, + data.body_blockv[0], + data.pre_body, &data.orig_inits, data.clauses); + result = pop_stmt_list (result); + + /* 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 (ret, &data.orig_inits)) ret = NULL_TREE; - else - ret = finish_omp_for (loc_first, code, declv, orig_declv, initv, condv, - incrv, body, pre_body, &orig_inits, clauses); - while (nbraces) + if (ret) { - if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) - { - cp_lexer_consume_token (parser->lexer); - nbraces--; - } - else if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) - cp_lexer_consume_token (parser->lexer); - else + /* Splice the omp_for into the nest of init blocks. */ + substitute_in_tree (&(data.init_blockv[0]), + data.body_placeholderv[count - 1], + result, true); + + /* Some later processing for combined directives assumes + that the BIND_EXPR containing range for variables appears + at top level in the OMP_FOR body. Fix that up if it's + not the case, e.g. because there is intervening code. */ + if (code != OACC_LOOP) + finish_omp_for_block (data.init_blockv[0], ret); + + /* Clean up the block subblock/superblock links. Per comment in + begin_compound_stmt, "we don't build BLOCK nodes when processing + templates", so skip this step in that case. */ + if (!processing_template_decl) { - if (!collapse_err) - { - error_at (cp_lexer_peek_token (parser->lexer)->location, - "collapsed loops not perfectly nested"); - } - collapse_err = true; - cp_parser_statement_seq_opt (parser, NULL); - if (cp_lexer_next_token_is (parser->lexer, CPP_EOF)) - break; + tree superblock = NULL_TREE; + cp_walk_tree (&data.init_blockv[0], fixup_blocks_walker, + (void *)&superblock, NULL); } - } - while (!for_block->is_empty ()) - { - tree t = for_block->pop (); - if (TREE_CODE (t) == STATEMENT_LIST) - add_stmt (pop_stmt_list (t)); - else - add_stmt (t); + /* Finally record the result. */ + add_stmt (data.init_blockv[0]); } + parser->omp_for_parse_state = save_data; return ret; } @@ -44164,7 +44730,7 @@ cp_parser_omp_loop (cp_parser *parser, cp_token *pragma_tok, ret = cp_parser_omp_for_loop (parser, OMP_LOOP, clauses, cclauses, if_p); cp_parser_end_omp_structured_block (parser, save); - add_stmt (finish_omp_for_block (finish_omp_structured_block (sb), ret)); + add_stmt (finish_omp_structured_block (sb)); return ret; } @@ -44213,7 +44779,7 @@ cp_parser_omp_simd (cp_parser *parser, cp_token *pragma_tok, ret = cp_parser_omp_for_loop (parser, OMP_SIMD, clauses, cclauses, if_p); cp_parser_end_omp_structured_block (parser, save); - add_stmt (finish_omp_for_block (finish_omp_structured_block (sb), ret)); + add_stmt (finish_omp_structured_block (sb)); return ret; } @@ -44315,7 +44881,7 @@ cp_parser_omp_for (cp_parser *parser, cp_token *pragma_tok, ret = cp_parser_omp_for_loop (parser, OMP_FOR, clauses, cclauses, if_p); cp_parser_end_omp_structured_block (parser, save); - add_stmt (finish_omp_for_block (finish_omp_structured_block (sb), ret)); + add_stmt (finish_omp_structured_block (sb)); return ret; } @@ -45163,7 +45729,7 @@ cp_parser_omp_distribute (cp_parser *parser, cp_token *pragma_tok, ret = cp_parser_omp_for_loop (parser, OMP_DISTRIBUTE, clauses, NULL, if_p); cp_parser_end_omp_structured_block (parser, save); - add_stmt (finish_omp_for_block (finish_omp_structured_block (sb), ret)); + add_stmt (finish_omp_structured_block (sb)); return ret; } @@ -46206,7 +46772,15 @@ cp_parser_oacc_loop (cp_parser *parser, cp_token *pragma_tok, char *p_name, int save = cp_parser_begin_omp_structured_block (parser); tree stmt = cp_parser_omp_for_loop (parser, OACC_LOOP, clauses, NULL, if_p); cp_parser_end_omp_structured_block (parser, save); - add_stmt (finish_omp_structured_block (block)); + + /* Later processing of combined acc loop constructs gets confused + by an extra level of empty nested BIND_EXPRs, so flatten them. */ + block = finish_omp_structured_block (block); + if (TREE_CODE (block) == BIND_EXPR + && TREE_CODE (BIND_EXPR_BODY (block)) == BIND_EXPR + && !BIND_EXPR_VARS (block)) + block = BIND_EXPR_BODY (block); + add_stmt (block); return stmt; } @@ -48559,7 +49133,7 @@ cp_parser_omp_taskloop (cp_parser *parser, cp_token *pragma_tok, if_p); cp_parser_end_omp_structured_block (parser, save); - add_stmt (finish_omp_for_block (finish_omp_structured_block (sb), ret)); + add_stmt (finish_omp_structured_block (sb)); return ret; } @@ -49343,6 +49917,17 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) parser->lexer->in_pragma = true; id = cp_parser_pragma_kind (pragma_tok); + if (parser->omp_for_parse_state + && parser->omp_for_parse_state->in_intervening_code + && id >= PRAGMA_OMP__START_ + && id <= PRAGMA_OMP__LAST_) + { + error_at (pragma_tok->location, + "intervening code must not contain OpenMP directives"); + parser->omp_for_parse_state->fail = true; + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + return false; + } if (id != PRAGMA_OMP_DECLARE && id != PRAGMA_OACC_ROUTINE) cp_ensure_no_omp_declare_simd (parser); switch (id) diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index e261d7e16e48e38135d29bbac6b7a0772d5fe15a..6cbb9a8e031499007c3a740ef86a322bcb1c72d9 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -435,6 +435,9 @@ struct GTY(()) cp_parser { specification, if any, or UNKNOWN_LOCATION otherwise. */ location_t innermost_linkage_specification_location; + /* Pointer to state for parsing omp_loops. Managed by + cp_parser_omp_for_loop in parser.cc and not used outside that file. */ + struct omp_for_parse_data * GTY((skip)) omp_for_parse_state; }; /* In parser.cc */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 2223594539c155a16e3c78af962543500812cc43..eaa7adb51dc6c526dd1e086f42bfbec5fbe4a3f9 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -18485,7 +18485,8 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, tree &orig_declv, tree this_pre_body = NULL_TREE; tree orig_init = NULL_TREE; tree orig_decl = NULL_TREE; - cp_convert_omp_range_for (this_pre_body, NULL, decl, orig_decl, init, + tree init_sl = NULL_TREE; + cp_convert_omp_range_for (this_pre_body, init_sl, decl, orig_decl, init, orig_init, cond, incr); if (orig_decl) { diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index e810bc41fc8e13b0b97a5de841184afa0e140fe4..5b539ceefbf7da5f79bf9d05f85fb1b58a1dc90c 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -10541,6 +10541,7 @@ finish_omp_for (location_t locus, enum tree_code code, tree declv, int i; int collapse = 1; int ordered = 0; + auto_vec<location_t> init_locv; gcc_assert (TREE_VEC_LENGTH (declv) == TREE_VEC_LENGTH (initv)); gcc_assert (TREE_VEC_LENGTH (declv) == TREE_VEC_LENGTH (condv)); @@ -10569,6 +10570,28 @@ finish_omp_for (location_t locus, enum tree_code code, tree declv, incr = TREE_VEC_ELT (incrv, i); elocus = locus; + /* We are going to throw out the init's original MODIFY_EXPR or + MODOP_EXPR below. Save its location so we can use it when + reconstructing the expression farther down. Alternatively, if the + initializer is a binding of the iteration variable, save + that location. Any of these locations in the initialization clause + for the current nested loop are better than using the argument locus, + that points to the "for" of the the outermost loop in the nest. */ + if (init && EXPR_HAS_LOCATION (init)) + elocus = EXPR_LOCATION (init); + else if (decl && INDIRECT_REF_P (decl) && EXPR_HAS_LOCATION (decl)) + /* This can happen for class iterators. */ + elocus = EXPR_LOCATION (decl); + else if (decl && DECL_P (decl)) + { + if (DECL_SOURCE_LOCATION (decl) != UNKNOWN_LOCATION) + elocus = DECL_SOURCE_LOCATION (decl); + else if (DECL_INITIAL (decl) + && EXPR_HAS_LOCATION (DECL_INITIAL (decl))) + elocus = EXPR_LOCATION (DECL_INITIAL (decl)); + } + init_locv.safe_push (elocus); + if (decl == NULL) { if (init != NULL) @@ -10597,9 +10620,6 @@ finish_omp_for (location_t locus, enum tree_code code, tree declv, } } - if (init && EXPR_HAS_LOCATION (init)) - elocus = EXPR_LOCATION (init); - if (cond == global_namespace) continue; @@ -10646,8 +10666,8 @@ finish_omp_for (location_t locus, enum tree_code code, tree declv, again and going through the cp_build_modify_expr path below when we instantiate the thing. */ TREE_VEC_ELT (initv, i) - = build2 (MODIFY_EXPR, void_type_node, TREE_VEC_ELT (declv, i), - TREE_VEC_ELT (initv, i)); + = build2_loc (init_locv[i], MODIFY_EXPR, void_type_node, + TREE_VEC_ELT (declv, i), TREE_VEC_ELT (initv, i)); } TREE_TYPE (stmt) = void_type_node; @@ -10676,10 +10696,7 @@ finish_omp_for (location_t locus, enum tree_code code, tree declv, incr = TREE_VEC_ELT (incrv, i); if (orig_incr) TREE_VEC_ELT (orig_incr, i) = incr; - elocus = locus; - - if (init && EXPR_HAS_LOCATION (init)) - elocus = EXPR_LOCATION (init); + elocus = init_locv[i]; if (!DECL_P (decl)) { @@ -10724,7 +10741,7 @@ finish_omp_for (location_t locus, enum tree_code code, tree declv, init = cp_build_modify_expr (elocus, decl, NOP_EXPR, init, tf_warning_or_error); else - init = build2 (MODIFY_EXPR, void_type_node, decl, init); + init = build2_loc (elocus, MODIFY_EXPR, void_type_node, decl, init); if (decl == error_mark_node || init == error_mark_node) return NULL; @@ -10896,47 +10913,71 @@ finish_omp_for (location_t locus, enum tree_code code, tree declv, return omp_for; } -/* Fix up range for decls. Those decls were pushed into BIND's BIND_EXPR_VARS - and need to be moved into the BIND_EXPR inside of the OMP_FOR's body. */ +/* Code walker for finish_omp_for_block: extract binding of DP->var + from its current block and move it to a new BIND_EXPR DP->b + surrounding the body of DP->omp_for. */ + +struct fofb_data { + tree var; + tree b; + tree omp_for; +}; + +static tree +finish_omp_for_block_walker (tree *tp, int *walk_subtrees, void *dp) +{ + struct fofb_data *fofb = (struct fofb_data *)dp; + if (TREE_CODE (*tp) == BIND_EXPR) + for (tree *p = &BIND_EXPR_VARS (*tp); *p; p = &DECL_CHAIN (*p)) + { + if (*p == fofb->var) + { + *p = DECL_CHAIN (*p); + if (fofb->b == NULL_TREE) + { + fofb->b = make_node (BLOCK); + fofb->b = build3 (BIND_EXPR, void_type_node, NULL_TREE, + OMP_FOR_BODY (fofb->omp_for), fofb->b); + TREE_SIDE_EFFECTS (fofb->b) = 1; + OMP_FOR_BODY (fofb->omp_for) = fofb->b; + } + DECL_CHAIN (fofb->var) = BIND_EXPR_VARS (fofb->b); + BIND_EXPR_VARS (fofb->b) = fofb->var; + BLOCK_VARS (BIND_EXPR_BLOCK (fofb->b)) = fofb->var; + BLOCK_VARS (BIND_EXPR_BLOCK (*tp)) = BIND_EXPR_VARS (*tp); + return *tp; + } + } + if (TREE_CODE (*tp) != BIND_EXPR && TREE_CODE (*tp) != STATEMENT_LIST) + *walk_subtrees = false; + return NULL_TREE; +} +/* Fix up range for decls. Those decls were pushed into BIND's + BIND_EXPR_VARS, or that of a nested BIND_EXPR inside its body, + and need to be moved into a new BIND_EXPR surrounding OMP_FOR's body + so that processing of combined loop directives can find them. */ tree finish_omp_for_block (tree bind, tree omp_for) { if (omp_for == NULL_TREE || !OMP_FOR_ORIG_DECLS (omp_for) - || bind == NULL_TREE - || TREE_CODE (bind) != BIND_EXPR) + || bind == NULL_TREE) return bind; - tree b = NULL_TREE; + struct fofb_data fofb; + fofb.b = NULL_TREE; + fofb.omp_for = omp_for; for (int i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (omp_for)); i++) if (TREE_CODE (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (omp_for), i)) == TREE_LIST && TREE_CHAIN (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (omp_for), i))) { tree v = TREE_CHAIN (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (omp_for), i)); - gcc_assert (BIND_EXPR_BLOCK (bind) - && (BIND_EXPR_VARS (bind) - == BLOCK_VARS (BIND_EXPR_BLOCK (bind)))); for (int j = 2; j < TREE_VEC_LENGTH (v); j++) - for (tree *p = &BIND_EXPR_VARS (bind); *p; p = &DECL_CHAIN (*p)) - { - if (*p == TREE_VEC_ELT (v, j)) - { - tree var = *p; - *p = DECL_CHAIN (*p); - if (b == NULL_TREE) - { - b = make_node (BLOCK); - b = build3 (BIND_EXPR, void_type_node, NULL_TREE, - OMP_FOR_BODY (omp_for), b); - TREE_SIDE_EFFECTS (b) = 1; - OMP_FOR_BODY (omp_for) = b; - } - DECL_CHAIN (var) = BIND_EXPR_VARS (b); - BIND_EXPR_VARS (b) = var; - BLOCK_VARS (BIND_EXPR_BLOCK (b)) = var; - } - } - BLOCK_VARS (BIND_EXPR_BLOCK (bind)) = BIND_EXPR_VARS (bind); + { + fofb.var = TREE_VEC_ELT (v, j); + cp_walk_tree (&bind, finish_omp_for_block_walker, + (void *)&fofb, NULL); + } } return bind; } diff --git a/gcc/testsuite/c-c++-common/goacc/tile-2.c b/gcc/testsuite/c-c++-common/goacc/tile-2.c index 98abc903bdcef41b6f8dca0d26050e803432be79..dc3067032602b80d7499a54d60ef99ceb365eee0 100644 --- a/gcc/testsuite/c-c++-common/goacc/tile-2.c +++ b/gcc/testsuite/c-c++-common/goacc/tile-2.c @@ -3,8 +3,8 @@ int main () #pragma acc parallel { #pragma acc loop tile (*,*) - for (int ix = 0; ix < 30; ix++) /* { dg-error "not enough" "" { target c } } */ - ; /* { dg-error "not enough" "" { target c++ } } */ + for (int ix = 0; ix < 30; ix++) /* { dg-error "not enough" } */ + ; #pragma acc loop tile (*,*) for (int ix = 0; ix < 30; ix++) diff --git a/gcc/testsuite/g++.dg/gomp/attrs-imperfect1.C b/gcc/testsuite/g++.dg/gomp/attrs-imperfect1.C new file mode 100644 index 0000000000000000000000000000000000000000..cf293b5081cf460a7c7c81f8fb384d07909dc14b --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-imperfect1.C @@ -0,0 +1,38 @@ +/* { dg-do compile { target c++11 } } */ + +/* This test case is expected to fail due to errors. */ + +int f1 (int depth, int iter); +int f2 (int depth, int iter); + +void s1 (int a1, int a2, int a3) +{ + int i, j, k; + + [[ omp :: directive (for, collapse(3)) ]] + for (i = 0; i < a1; i++) + { + f1 (0, i); + for (j = 0; j < a2; j++) + { + [[ omp :: directive (barrier) ]] ; /* { dg-error "intervening code must not contain OpenMP directives" } */ + f1 (1, j); + if (i == 2) + continue; /* { dg-error "invalid exit" } */ + else + break; /* { dg-error "invalid exit" } */ + for (k = 0; k < a3; k++) + { + f1 (2, k); + f2 (2, k); + } + f2 (1, j); + } + for (k = 0; k < a3; k++) /* { dg-error "loop not permitted in intervening code " } */ + { + f1 (2, k); + f2 (2, k); + } + f2 (0, i); + } +} diff --git a/gcc/testsuite/g++.dg/gomp/attrs-imperfect2.C b/gcc/testsuite/g++.dg/gomp/attrs-imperfect2.C new file mode 100644 index 0000000000000000000000000000000000000000..0c9154dd10ca7c626a5ed7d9197d2f82f0d266f8 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-imperfect2.C @@ -0,0 +1,34 @@ +/* { dg-do compile { target c++11 } } */ + +/* This test case is expected to fail due to errors. */ + +/* These functions that are part of the OpenMP runtime API would ordinarily + be declared in omp.h, but we don't have that here. */ +extern int omp_get_num_threads(void); +extern int omp_get_max_threads(void); + +int f1 (int depth, int iter); +int f2 (int depth, int iter); + +void s1 (int a1, int a2, int a3) +{ + int i, j, k; + [[ omp :: directive (for, collapse(3)) ]] + for (i = 0; i < a1; i++) + { + f1 (0, i); + for (j = 0; j < omp_get_num_threads (); j++) /* This is OK */ + { + f1 (1, omp_get_num_threads ()); /* { dg-error "not permitted in intervening code" } */ + for (k = omp_get_num_threads (); k < a3; k++) /* This is OK */ + { + f1 (2, omp_get_num_threads ()); + f2 (2, omp_get_max_threads ()); + } + f2 (1, omp_get_max_threads ()); /* { dg-error "not permitted in intervening code" } */ + } + f2 (0, i); + } +} + + diff --git a/gcc/testsuite/g++.dg/gomp/attrs-imperfect3.C b/gcc/testsuite/g++.dg/gomp/attrs-imperfect3.C new file mode 100644 index 0000000000000000000000000000000000000000..6b612afd355e555675647db53e5bd57cbd355489 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-imperfect3.C @@ -0,0 +1,33 @@ +/* { dg-do compile { target c++11 } } */ + +/* This test case is expected to fail due to errors. */ + +/* Test that the imperfectly-nested loops with the ordered clause gives + an error, and that there is only one error (and not one on every + intervening statement). */ + +int f1 (int depth, int iter); +int f2 (int depth, int iter); + +void s1 (int a1, int a2, int a3) +{ + int i, j, k; + + [[ omp :: directive (for, ordered(3)) ]] + for (i = 0; i < a1; i++) /* { dg-error "inner loops must be perfectly nested" } */ + { + f1 (0, i); + for (j = 0; j < a2; j++) + { + f1 (1, j); + for (k = 0; k < a3; k++) + { + f1 (2, k); + f2 (2, k); + } + f2 (1, j); + } + f2 (0, i); + } +} + diff --git a/gcc/testsuite/g++.dg/gomp/attrs-imperfect4.C b/gcc/testsuite/g++.dg/gomp/attrs-imperfect4.C new file mode 100644 index 0000000000000000000000000000000000000000..16636ab3eb68e4d4ac4071113e9adbfe888ba7a0 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-imperfect4.C @@ -0,0 +1,33 @@ +/* { dg-do compile { target c++11 } } */ + +/* This test case is expected to fail due to errors. */ + +int f1 (int depth, int iter); +int f2 (int depth, int iter); + +void s1 (int a1, int a2, int a3) +{ + int i, j, k; + + [[ omp :: directive (for, collapse(4)) ]] + for (i = 0; i < a1; i++) /* { dg-error "not enough nested loops" } */ + { + f1 (0, i); + for (j = 0; j < a2; j++) + { + f1 (1, j); + for (k = 0; k < a3; k++) + { + /* According to the grammar, this is intervening code; we + don't know that we are also missing a nested for loop + until we have parsed this whole compound expression. */ + [[ omp :: directive (barrier) ]] ; /* { dg-error "intervening code must not contain OpenMP directives" } */ + f1 (2, k); + f2 (2, k); + } + f2 (1, j); + } + f2 (0, i); + } +} + diff --git a/gcc/testsuite/g++.dg/gomp/attrs-imperfect5.C b/gcc/testsuite/g++.dg/gomp/attrs-imperfect5.C new file mode 100644 index 0000000000000000000000000000000000000000..301307262a9433e802493a345a2d1cd6757f8aa4 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-imperfect5.C @@ -0,0 +1,57 @@ +/* { dg-do compile { target c++11 } } */ + +/* This test case is expected to fail due to errors. */ + +int f1 (int depth, int iter); +int f2 (int depth, int iter); +int ijk (int x, int y, int z); +void f3 (int sum); + +/* This function isn't particularly meaningful, but it should compile without + error. */ +int s1 (int a1, int a2, int a3) +{ + int i, j, k; + int r = 0; + + [[ omp :: directive (simd, collapse(3), reduction (inscan, +:r)) ]] + for (i = 0; i < a1; i++) + { + for (j = 0; j < a2; j++) + { + for (k = 0; k < a3; k++) + { + r = r + ijk (i, j, k); + [[ omp :: directive (scan, exclusive (r)) ]] ; + f3 (r); + } + } + } + return r; +} + +/* Adding intervening code should trigger an error. */ +int s2 (int a1, int a2, int a3) +{ + int i, j, k; + int r = 0; + + [[ omp :: directive (simd, collapse(3), reduction (inscan, +:r)) ]] + for (i = 0; i < a1; i++) /* { dg-error "inner loops must be perfectly nested" } */ + { + f1 (0, i); + for (j = 0; j < a2; j++) + { + f1 (1, j); + for (k = 0; k < a3; k++) + { + r = r + ijk (i, j, k); + [[ omp :: directive (scan, exclusive (r)) ]] ; + f3 (r); + } + f2 (1, j); + } + f2 (0, i); + } + return r; +} diff --git a/gcc/testsuite/g++.dg/gomp/pr41967.C b/gcc/testsuite/g++.dg/gomp/pr41967.C index 0eb489e8beefd2dfc52b1323b92332b471b80451..7b59f831fe04b5a1c956733ef421d8bdfb5500ae 100644 --- a/gcc/testsuite/g++.dg/gomp/pr41967.C +++ b/gcc/testsuite/g++.dg/gomp/pr41967.C @@ -11,7 +11,7 @@ foo () { for (int j = 0; j < 5; ++j) ++sum; - ++sum; // { dg-error "collapsed loops not perfectly nested" } + ++sum; } return sum; } diff --git a/gcc/testsuite/g++.dg/gomp/tpl-imperfect-gotos.C b/gcc/testsuite/g++.dg/gomp/tpl-imperfect-gotos.C new file mode 100644 index 0000000000000000000000000000000000000000..72206128fae644bb0cf13b650c2c5b9f8204bda2 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/tpl-imperfect-gotos.C @@ -0,0 +1,161 @@ +/* { dg-do compile } */ + +/* This file contains tests that are expected to fail. */ + + +/* These jumps are all OK since they are to/from the same structured block. */ + +template<typename T> +void f1a (void) +{ +#pragma omp for collapse(2) + for (T i = 0; i < 64; ++i) + { + goto a; a:; + for (T j = 0; j < 64; ++j) + { + goto c; c:; + } + goto b; b:; + } +} + +/* Jump around loop body to/from different structured blocks of intervening + code. */ +template<typename T> +void f2a (void) +{ +#pragma omp for collapse(2) + for (T i = 0; i < 64; ++i) + { + goto a; a:; + if (i > 16) goto b; /* { dg-error "invalid branch to/from OpenMP structured block" } */ + for (T j = 0; j < 64; ++j) + { + goto c; c:; + } + goto b; b:; + } +} + +/* Jump into loop body from intervening code. */ +template<typename T> +void f3a (void) +{ +#pragma omp for collapse(2) + for (T i = 0; i < 64; ++i) + { + goto a; a:; + if (i > 16) goto c; /* { dg-error "invalid branch to/from OpenMP structured block" } */ + for (T j = 0; j < 64; ++j) + { + c: /* { dg-error "jump to label .c." } */ + ; + } + goto b; b:; + } +} + +/* Jump out of loop body to intervening code. */ +template<typename T> +void f4a (void) +{ +#pragma omp for collapse(2) + for (T i = 0; i < 64; ++i) + { + goto a; a:; + for (T j = 0; j < 64; ++j) + if (i > 16) goto c; /* { dg-error "invalid branch to/from OpenMP structured block" } */ + c: + ; + goto b; b:; + } +} + +/* The next group of tests use the GNU extension for local labels. Expected + behavior is the same as the above group. */ + +/* These jumps are all OK since they are to/from the same structured block. */ + +template<typename T> +void f1b (void) +{ +#pragma omp for collapse(2) + for (T i = 0; i < 64; ++i) + { + __label__ a, b, c; + goto a; a:; + for (T j = 0; j < 64; ++j) + { + goto c; c:; + } + goto b; b:; + } +} + +/* Jump around loop body to/from different structured blocks of intervening + code. */ +template<typename T> +void f2b (void) +{ +#pragma omp for collapse(2) + for (T i = 0; i < 64; ++i) + { + __label__ a, b, c; + goto a; a:; + if (i > 16) goto b; /* { dg-error "invalid branch to/from OpenMP structured block" } */ + for (T j = 0; j < 64; ++j) + { + goto c; c:; + } + goto b; b:; + } +} + +/* Jump into loop body from intervening code. */ +template<typename T> +void f3b (void) +{ +#pragma omp for collapse(2) + for (T i = 0; i < 64; ++i) + { + __label__ a, b, c; + goto a; a:; + if (i > 16) goto c; /* { dg-error "invalid branch to/from OpenMP structured block" } */ + for (T j = 0; j < 64; ++j) + { + c: /* { dg-error "jump to label .c." } */ + ; + } + goto b; b:; + } +} + +/* Jump out of loop body to intervening code. */ +template<typename T> +void f4b (void) +{ +#pragma omp for collapse(2) + for (T i = 0; i < 64; ++i) + { + __label__ a, b, c; + goto a; a:; + for (T j = 0; j < 64; ++j) + if (i > 16) goto c; /* { dg-error "invalid branch to/from OpenMP structured block" } */ + c: + ; + goto b; b:; + } +} + +int main (void) +{ + f1a<int> (); + f2a<int> (); + f3a<int> (); + f4a<int> (); + f1b<int> (); + f2b<int> (); + f3b<int> (); + f4b<int> (); +} diff --git a/gcc/testsuite/g++.dg/gomp/tpl-imperfect-invalid-scope.C b/gcc/testsuite/g++.dg/gomp/tpl-imperfect-invalid-scope.C new file mode 100644 index 0000000000000000000000000000000000000000..1e85e64b14aed8e8ef0a4732fb3477ecd13d1bab --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/tpl-imperfect-invalid-scope.C @@ -0,0 +1,94 @@ +/* { dg-do compile } */ + +/* Check that various cases of invalid references to variables bound + in an intervening code scope are diagnosed and do not ICE. This test + is expected to produce errors. */ + +template<typename T> +extern void foo (T, T); + +template<typename T> +void f1 (void) +{ +#pragma omp for collapse (2) + for (T i = 0; i < 64; i++) + { + T v = (i + 4) * 2; + for (T j = v; j < 64; j++) /* { dg-error "initializer is bound in intervening code" } */ + foo (i, j); + } +} + +template<typename T> +void f2 (void) +{ +#pragma omp for collapse (2) + for (T i = 0; i < 64; i++) + { + T v = (i + 4) * 2; + for (T j = 0; j < v; j++) /* { dg-error "end test is bound in intervening code" } */ + foo (i, j); + } +} + +template<typename T> +void f3 (void) +{ +#pragma omp for collapse (2) + for (T i = 0; i < 64; i++) + { + T v = (i + 4) * 2; + for (T j = 0; j < 64; j = j + v) /* { dg-error "increment expression is bound in intervening code" } */ + foo (i, j); + } +} + +template<typename T> +void f4 (void) +{ +#pragma omp for collapse (2) + for (T i = 0; i < 64; i++) + { + T v = 8; + for (T j = v; j < 64; j++) /* { dg-error "initializer is bound in intervening code" } */ + foo (i, j); + } +} + +template<typename T> +void f5 (void) +{ +#pragma omp for collapse (2) + for (T i = 0; i < 64; i++) + { + T j; + for (j = 0; j < 64; j++) /* { dg-error "loop variable is bound in intervening code" } */ + foo (i, j); + } +} + +template<typename T> +void f6 (void) +{ +#pragma omp for collapse (2) + for (T i = 0; i < 64; i++) + { + T j; + { + T v = 8; + for (j = v; j < 64; j++) /* { dg-error "loop variable is bound in intervening code" } */ + /* { dg-error "initializer is bound in intervening code" "" { target *-*-* } .-1 } */ + foo (i, j); + } + } +} + +int main() +{ + f1<int> (); + f2<int> (); + f3<int> (); + f4<int> (); + f5<int> (); + f6<int> (); +} diff --git a/libgomp/testsuite/libgomp.c++/attrs-imperfect1.C b/libgomp/testsuite/libgomp.c++/attrs-imperfect1.C new file mode 100644 index 0000000000000000000000000000000000000000..4cbea6280ccba59be7606547df3babc603c2c039 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/attrs-imperfect1.C @@ -0,0 +1,76 @@ +/* { dg-do run } */ + +static int f1count[3], f2count[3]; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +int f1 (int depth, int iter) +{ + f1count[depth]++; + return iter; +} + +int f2 (int depth, int iter) +{ + f2count[depth]++; + return iter; +} + +void s1 (int a1, int a2, int a3) +{ + int i, j, k; + + [[ omp :: directive (for, collapse(3)) ]] + for (i = 0; i < a1; i++) + { + f1 (0, i); + for (j = 0; j < a2; j++) + { + f1 (1, j); + for (k = 0; k < a3; k++) + { + f1 (2, k); + f2 (2, k); + } + f2 (1, j); + } + f2 (0, i); + } +} + +int +main (void) +{ + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + s1 (3, 4, 5); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/attrs-imperfect2.C b/libgomp/testsuite/libgomp.c++/attrs-imperfect2.C new file mode 100644 index 0000000000000000000000000000000000000000..9fb82d9c817b45592082f2be7b97766b9cb54a9e --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/attrs-imperfect2.C @@ -0,0 +1,114 @@ +/* { dg-do run } */ + +static int f1count[3], f2count[3]; +static int g1count[3], g2count[3]; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +int f1 (int depth, int iter) +{ + f1count[depth]++; + return iter; +} + +int f2 (int depth, int iter) +{ + f2count[depth]++; + return iter; +} + +int g1 (int depth, int iter) +{ + g1count[depth]++; + return iter; +} + +int g2 (int depth, int iter) +{ + g2count[depth]++; + return iter; +} + +void s1 (int a1, int a2, int a3) +{ + int i, j, k; + + [[ omp :: directive (for, collapse(3)) ]] + for (i = 0; i < a1; i++) + { + f1 (0, i); + { + g1 (0, i); + for (j = 0; j < a2; j++) + { + f1 (1, j); + { + g1 (1, j); + for (k = 0; k < a3; k++) + { + f1 (2, k); + { + g1 (2, k); + g2 (2, k); + } + f2 (2, k); + } + g2 (1, j); + } + f2 (1, j); + } + g2 (0, i); + } + f2 (0, i); + } +} + +int +main (void) +{ + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + g1count[0] = 0; + g1count[1] = 0; + g1count[2] = 0; + g2count[0] = 0; + g2count[1] = 0; + g2count[2] = 0; + + s1 (3, 4, 5); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + if (g1count[0] != f1count[0]) abort (); + if (g2count[0] != f1count[0]) abort (); + if (g1count[1] != f1count[1]) abort (); + if (g2count[1] != f1count[1]) abort (); + if (g1count[2] != f1count[2]) abort (); + if (g2count[2] != f1count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/attrs-imperfect3.C b/libgomp/testsuite/libgomp.c++/attrs-imperfect3.C new file mode 100644 index 0000000000000000000000000000000000000000..51cb23aa02db331282e93f240a542cbfda6c2e39 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/attrs-imperfect3.C @@ -0,0 +1,119 @@ +/* { dg-do run } */ + +/* Like imperfect2.c, but includes bindings in the blocks. */ + +static int f1count[3], f2count[3]; +static int g1count[3], g2count[3]; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +int f1 (int depth, int iter) +{ + f1count[depth]++; + return iter; +} + +int f2 (int depth, int iter) +{ + f2count[depth]++; + return iter; +} + +int g1 (int depth, int iter) +{ + g1count[depth]++; + return iter; +} + +int g2 (int depth, int iter) +{ + g2count[depth]++; + return iter; +} + +void s1 (int a1, int a2, int a3) +{ + int i, j, k; + + [[ omp :: directive (for, collapse(3)) ]] + for (i = 0; i < a1; i++) + { + int local0 = 0; + f1 (local0, i); + { + g1 (local0, i); + for (j = 0; j < a2; j++) + { + int local1 = 1; + f1 (local1, j); + { + g1 (local1, j); + for (k = 0; k < a3; k++) + { + int local2 = 2; + f1 (local2, k); + { + g1 (local2, k); + g2 (local2, k); + } + f2 (local2, k); + } + g2 (local1, j); + } + f2 (local1, j); + } + g2 (local0, i); + } + f2 (local0, i); + } +} + +int +main (void) +{ + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + g1count[0] = 0; + g1count[1] = 0; + g1count[2] = 0; + g2count[0] = 0; + g2count[1] = 0; + g2count[2] = 0; + + s1 (3, 4, 5); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + if (g1count[0] != f1count[0]) abort (); + if (g2count[0] != f1count[0]) abort (); + if (g1count[1] != f1count[1]) abort (); + if (g2count[1] != f1count[1]) abort (); + if (g1count[2] != f1count[2]) abort (); + if (g2count[2] != f1count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/attrs-imperfect4.C b/libgomp/testsuite/libgomp.c++/attrs-imperfect4.C new file mode 100644 index 0000000000000000000000000000000000000000..cc0a034bbedd4a91fde578c2a7b2d5644921d694 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/attrs-imperfect4.C @@ -0,0 +1,117 @@ +/* { dg-do run } */ + +/* Like imperfect2.c, but includes blocks that are themselves intervening + code. */ + +static int f1count[3], f2count[3]; +static int g1count[3], g2count[3]; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +int f1 (int depth, int iter) +{ + f1count[depth]++; + return iter; +} + +int f2 (int depth, int iter) +{ + f2count[depth]++; + return iter; +} + +int g1 (int depth, int iter) +{ + g1count[depth]++; + return iter; +} + +int g2 (int depth, int iter) +{ + g2count[depth]++; + return iter; +} + +void s1 (int a1, int a2, int a3) +{ + int i, j, k; + + [[ omp :: directive (for, collapse(3)) ]] + for (i = 0; i < a1; i++) + { + { f1 (0, i); } + { + g1 (0, i); + for (j = 0; j < a2; j++) + { + { f1 (1, j); } + { + { g1 (1, j); } + for (k = 0; k < a3; k++) + { + f1 (2, k); + { + g1 (2, k); + g2 (2, k); + } + f2 (2, k); + } + { g2 (1, j); } + } + { f2 (1, j); } + } + { g2 (0, i); } + } + { f2 (0, i); } + } +} + +int +main (void) +{ + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + g1count[0] = 0; + g1count[1] = 0; + g1count[2] = 0; + g2count[0] = 0; + g2count[1] = 0; + g2count[2] = 0; + + s1 (3, 4, 5); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + if (g1count[0] != f1count[0]) abort (); + if (g2count[0] != f1count[0]) abort (); + if (g1count[1] != f1count[1]) abort (); + if (g2count[1] != f1count[1]) abort (); + if (g1count[2] != f1count[2]) abort (); + if (g2count[2] != f1count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/attrs-imperfect5.C b/libgomp/testsuite/libgomp.c++/attrs-imperfect5.C new file mode 100644 index 0000000000000000000000000000000000000000..89a969db8ccde9307cd4d1cd0b0183e2c70fc9ee --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/attrs-imperfect5.C @@ -0,0 +1,49 @@ +/* { dg-do run } */ + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +static int inner_loop_count = 0; +static int intervening_code_count = 0; + +void +g (int x, int y) +{ + inner_loop_count++; +} + +int +foo (int imax, int jmax) +{ + int j = 0; + + [[ omp :: directive (for, collapse(2)) ]] + for (int i = 0; i < imax; ++i) + { + /* All the intervening code at the same level must be executed + the same number of times. */ + ++intervening_code_count; + for (int j = 0; j < jmax; ++j) + { + g (i, j); + } + /* This is the outer j, not the one from the inner collapsed loop. */ + ++j; + } + return j; +} + +int +main (void) +{ + int j = foo (5, 3); + if (j != intervening_code_count) + abort (); + if (inner_loop_count != 5 * 3) + abort (); + if (intervening_code_count < 5 || intervening_code_count > 5 * 3) + abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/attrs-imperfect6.C b/libgomp/testsuite/libgomp.c++/attrs-imperfect6.C new file mode 100644 index 0000000000000000000000000000000000000000..01f9be123a65dc7881153313b6cc0bf368f0f594 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/attrs-imperfect6.C @@ -0,0 +1,115 @@ +/* { dg-do run } */ + +/* Like imperfect4.c, but bind the iteration variables in the loops. */ + +static int f1count[3], f2count[3]; +static int g1count[3], g2count[3]; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +int f1 (int depth, int iter) +{ + f1count[depth]++; + return iter; +} + +int f2 (int depth, int iter) +{ + f2count[depth]++; + return iter; +} + +int g1 (int depth, int iter) +{ + g1count[depth]++; + return iter; +} + +int g2 (int depth, int iter) +{ + g2count[depth]++; + return iter; +} + +void s1 (int a1, int a2, int a3) +{ + + [[ omp :: directive (for, collapse(3)) ]] + for (int i = 0; i < a1; i++) + { + { f1 (0, i); } + { + g1 (0, i); + for (int j = 0; j < a2; j++) + { + { f1 (1, j); } + { + { g1 (1, j); } + for (int k = 0; k < a3; k++) + { + f1 (2, k); + { + g1 (2, k); + g2 (2, k); + } + f2 (2, k); + } + { g2 (1, j); } + } + { f2 (1, j); } + } + { g2 (0, i); } + } + { f2 (0, i); } + } +} + +int +main (void) +{ + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + g1count[0] = 0; + g1count[1] = 0; + g1count[2] = 0; + g2count[0] = 0; + g2count[1] = 0; + g2count[2] = 0; + + s1 (3, 4, 5); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + if (g1count[0] != f1count[0]) abort (); + if (g2count[0] != f1count[0]) abort (); + if (g1count[1] != f1count[1]) abort (); + if (g2count[1] != f1count[1]) abort (); + if (g1count[2] != f1count[2]) abort (); + if (g2count[2] != f1count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/imperfect-class-1.C b/libgomp/testsuite/libgomp.c++/imperfect-class-1.C new file mode 100644 index 0000000000000000000000000000000000000000..3c39c42c10757ffe3b3ffe5e7824467562666d10 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/imperfect-class-1.C @@ -0,0 +1,169 @@ +// { dg-do run } +// Test that class iterators and imperfectly-nested loops work together. +// This variant tests initialization by assignment. + +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef int T; +typedef int S; + +class I +{ +public: + typedef ptrdiff_t difference_type; + I (); + ~I (); + I (T *); + I (const I &); + T &operator * (); + T *operator -> (); + T &operator [] (const difference_type &) const; + I &operator = (const I &); + I &operator ++ (); + I operator ++ (int); + I &operator -- (); + I operator -- (int); + I &operator += (const difference_type &); + I &operator -= (const difference_type &); + I operator + (const difference_type &) const; + I operator - (const difference_type &) const; + friend bool operator == (I &, I &); + friend bool operator == (const I &, const I &); + friend bool operator < (I &, I &); + friend bool operator < (const I &, const I &); + friend bool operator <= (I &, I &); + friend bool operator <= (const I &, const I &); + friend bool operator > (I &, I &); + friend bool operator > (const I &, const I &); + friend bool operator >= (I &, I &); + friend bool operator >= (const I &, const I &); + friend typename I::difference_type operator - (I &, I &); + friend typename I::difference_type operator - (const I &, const I &); + friend I operator + (typename I::difference_type , const I &); +private: + T *p; +}; + I::I () : p (0) {} + I::~I () { p = (T *) 0; } + I::I (T *x) : p (x) {} + I::I (const I &x) : p (x.p) {} + T &I::operator * () { return *p; } + T *I::operator -> () { return p; } + T &I::operator [] (const difference_type &x) const { return p[x]; } + I &I::operator = (const I &x) { p = x.p; return *this; } + I &I::operator ++ () { ++p; return *this; } + I I::operator ++ (int) { return I (p++); } + I &I::operator -- () { --p; return *this; } + I I::operator -- (int) { return I (p--); } + I &I::operator += (const difference_type &x) { p += x; return *this; } + I &I::operator -= (const difference_type &x) { p -= x; return *this; } + I I::operator + (const difference_type &x) const { return I (p + x); } + I I::operator - (const difference_type &x) const { return I (p - x); } + bool operator == (I &x, I &y) { return x.p == y.p; } + bool operator == (const I &x, const I &y) { return x.p == y.p; } + bool operator != (I &x, I &y) { return !(x == y); } + bool operator != (const I &x, const I &y) { return !(x == y); } + bool operator < (I &x, I &y) { return x.p < y.p; } + bool operator < (const I &x, const I &y) { return x.p < y.p; } + bool operator <= (I &x, I &y) { return x.p <= y.p; } + bool operator <= (const I &x, const I &y) { return x.p <= y.p; } + bool operator > (I &x, I &y) { return x.p > y.p; } + bool operator > (const I &x, const I &y) { return x.p > y.p; } + bool operator >= (I &x, I &y) { return x.p >= y.p; } + bool operator >= (const I &x, const I &y) { return x.p >= y.p; } + typename I::difference_type operator - (I &x, I &y) { return x.p - y.p; } + typename I::difference_type operator - (const I &x, const I &y) { return x.p - y.p; } + I operator + (typename I::difference_type x, const I &y) { return I (x + y.p); } + +class J +{ + public: + J(const I &x, const I &y) : b (x), e (y) {} + const I &begin (); + const I &end (); + private: + I b, e; +}; + +const I &J::begin () { return b; } +const I &J::end () { return e; } + +static int f1count[3], f2count[3]; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +void f1 (int depth) +{ + f1count[depth]++; +} + +void f2 (int depth) +{ + f2count[depth]++; +} + +void s1 (J a1, J a2, J a3) +{ + I i, j, k; + +#pragma omp for collapse(3) + for (i = a1.begin (); i < a1.end (); i++) + { + f1 (0); + for (j = a2.begin (); j < a2.end (); j++) + { + f1 (1); + for (k = a3.begin (); k < a3.end (); k++) + { + f1 (2); + f2 (2); + } + f2 (1); + } + f2 (0); + } +} + + +int +main (void) +{ + + int index[] = {0, 1, 2, 3, 4, 5}; + + J x (&index[0], &index[3]); + J y (&index[0], &index[4]); + J z (&index[0], &index[5]); + + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + s1 (x, y, z); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/imperfect-class-2.C b/libgomp/testsuite/libgomp.c++/imperfect-class-2.C new file mode 100644 index 0000000000000000000000000000000000000000..c6b657cabbaeea6bf341e040c6541c2b6270a178 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/imperfect-class-2.C @@ -0,0 +1,167 @@ +// { dg-do run } +// Test that class iterators and imperfectly-nested loops work together. +// This variant tests loop initialization by declaration. + +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef int T; +typedef int S; + +class I +{ +public: + typedef ptrdiff_t difference_type; + I (); + ~I (); + I (T *); + I (const I &); + T &operator * (); + T *operator -> (); + T &operator [] (const difference_type &) const; + I &operator = (const I &); + I &operator ++ (); + I operator ++ (int); + I &operator -- (); + I operator -- (int); + I &operator += (const difference_type &); + I &operator -= (const difference_type &); + I operator + (const difference_type &) const; + I operator - (const difference_type &) const; + friend bool operator == (I &, I &); + friend bool operator == (const I &, const I &); + friend bool operator < (I &, I &); + friend bool operator < (const I &, const I &); + friend bool operator <= (I &, I &); + friend bool operator <= (const I &, const I &); + friend bool operator > (I &, I &); + friend bool operator > (const I &, const I &); + friend bool operator >= (I &, I &); + friend bool operator >= (const I &, const I &); + friend typename I::difference_type operator - (I &, I &); + friend typename I::difference_type operator - (const I &, const I &); + friend I operator + (typename I::difference_type , const I &); +private: + T *p; +}; + I::I () : p (0) {} + I::~I () { p = (T *) 0; } + I::I (T *x) : p (x) {} + I::I (const I &x) : p (x.p) {} + T &I::operator * () { return *p; } + T *I::operator -> () { return p; } + T &I::operator [] (const difference_type &x) const { return p[x]; } + I &I::operator = (const I &x) { p = x.p; return *this; } + I &I::operator ++ () { ++p; return *this; } + I I::operator ++ (int) { return I (p++); } + I &I::operator -- () { --p; return *this; } + I I::operator -- (int) { return I (p--); } + I &I::operator += (const difference_type &x) { p += x; return *this; } + I &I::operator -= (const difference_type &x) { p -= x; return *this; } + I I::operator + (const difference_type &x) const { return I (p + x); } + I I::operator - (const difference_type &x) const { return I (p - x); } + bool operator == (I &x, I &y) { return x.p == y.p; } + bool operator == (const I &x, const I &y) { return x.p == y.p; } + bool operator != (I &x, I &y) { return !(x == y); } + bool operator != (const I &x, const I &y) { return !(x == y); } + bool operator < (I &x, I &y) { return x.p < y.p; } + bool operator < (const I &x, const I &y) { return x.p < y.p; } + bool operator <= (I &x, I &y) { return x.p <= y.p; } + bool operator <= (const I &x, const I &y) { return x.p <= y.p; } + bool operator > (I &x, I &y) { return x.p > y.p; } + bool operator > (const I &x, const I &y) { return x.p > y.p; } + bool operator >= (I &x, I &y) { return x.p >= y.p; } + bool operator >= (const I &x, const I &y) { return x.p >= y.p; } + typename I::difference_type operator - (I &x, I &y) { return x.p - y.p; } + typename I::difference_type operator - (const I &x, const I &y) { return x.p - y.p; } + I operator + (typename I::difference_type x, const I &y) { return I (x + y.p); } + +class J +{ + public: + J(const I &x, const I &y) : b (x), e (y) {} + const I &begin (); + const I &end (); + private: + I b, e; +}; + +const I &J::begin () { return b; } +const I &J::end () { return e; } + +static int f1count[3], f2count[3]; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +void f1 (int depth) +{ + f1count[depth]++; +} + +void f2 (int depth) +{ + f2count[depth]++; +} + +void s1 (J a1, J a2, J a3) +{ +#pragma omp for collapse(3) + for (I i = a1.begin (); i < a1.end (); i++) + { + f1 (0); + for (I j = a2.begin (); j < a2.end (); j++) + { + f1 (1); + for (I k = a3.begin (); k < a3.end (); k++) + { + f1 (2); + f2 (2); + } + f2 (1); + } + f2 (0); + } +} + + +int +main (void) +{ + + int index[] = {0, 1, 2, 3, 4, 5}; + + J x (&index[0], &index[3]); + J y (&index[0], &index[4]); + J z (&index[0], &index[5]); + + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + s1 (x, y, z); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/imperfect-class-3.C b/libgomp/testsuite/libgomp.c++/imperfect-class-3.C new file mode 100644 index 0000000000000000000000000000000000000000..c33826a6b362463d7483678fa1c1dd3ee8aaee8e --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/imperfect-class-3.C @@ -0,0 +1,167 @@ +// { dg-do run } +// Test that class iterators and imperfectly-nested loops work together. +// This variant tests range for. + +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef int T; +typedef int S; + +class I +{ +public: + typedef ptrdiff_t difference_type; + I (); + ~I (); + I (T *); + I (const I &); + T &operator * (); + T *operator -> (); + T &operator [] (const difference_type &) const; + I &operator = (const I &); + I &operator ++ (); + I operator ++ (int); + I &operator -- (); + I operator -- (int); + I &operator += (const difference_type &); + I &operator -= (const difference_type &); + I operator + (const difference_type &) const; + I operator - (const difference_type &) const; + friend bool operator == (I &, I &); + friend bool operator == (const I &, const I &); + friend bool operator < (I &, I &); + friend bool operator < (const I &, const I &); + friend bool operator <= (I &, I &); + friend bool operator <= (const I &, const I &); + friend bool operator > (I &, I &); + friend bool operator > (const I &, const I &); + friend bool operator >= (I &, I &); + friend bool operator >= (const I &, const I &); + friend typename I::difference_type operator - (I &, I &); + friend typename I::difference_type operator - (const I &, const I &); + friend I operator + (typename I::difference_type , const I &); +private: + T *p; +}; + I::I () : p (0) {} + I::~I () { p = (T *) 0; } + I::I (T *x) : p (x) {} + I::I (const I &x) : p (x.p) {} + T &I::operator * () { return *p; } + T *I::operator -> () { return p; } + T &I::operator [] (const difference_type &x) const { return p[x]; } + I &I::operator = (const I &x) { p = x.p; return *this; } + I &I::operator ++ () { ++p; return *this; } + I I::operator ++ (int) { return I (p++); } + I &I::operator -- () { --p; return *this; } + I I::operator -- (int) { return I (p--); } + I &I::operator += (const difference_type &x) { p += x; return *this; } + I &I::operator -= (const difference_type &x) { p -= x; return *this; } + I I::operator + (const difference_type &x) const { return I (p + x); } + I I::operator - (const difference_type &x) const { return I (p - x); } + bool operator == (I &x, I &y) { return x.p == y.p; } + bool operator == (const I &x, const I &y) { return x.p == y.p; } + bool operator != (I &x, I &y) { return !(x == y); } + bool operator != (const I &x, const I &y) { return !(x == y); } + bool operator < (I &x, I &y) { return x.p < y.p; } + bool operator < (const I &x, const I &y) { return x.p < y.p; } + bool operator <= (I &x, I &y) { return x.p <= y.p; } + bool operator <= (const I &x, const I &y) { return x.p <= y.p; } + bool operator > (I &x, I &y) { return x.p > y.p; } + bool operator > (const I &x, const I &y) { return x.p > y.p; } + bool operator >= (I &x, I &y) { return x.p >= y.p; } + bool operator >= (const I &x, const I &y) { return x.p >= y.p; } + typename I::difference_type operator - (I &x, I &y) { return x.p - y.p; } + typename I::difference_type operator - (const I &x, const I &y) { return x.p - y.p; } + I operator + (typename I::difference_type x, const I &y) { return I (x + y.p); } + +class J +{ + public: + J(const I &x, const I &y) : b (x), e (y) {} + const I &begin (); + const I &end (); + private: + I b, e; +}; + +const I &J::begin () { return b; } +const I &J::end () { return e; } + +static int f1count[3], f2count[3]; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +void f1 (int depth) +{ + f1count[depth]++; +} + +void f2 (int depth) +{ + f2count[depth]++; +} + +void s1 (J a1, J a2, J a3) +{ +#pragma omp for collapse(3) + for (auto i : a1) + { + f1 (0); + for (auto j : a2) + { + f1 (1); + for (auto k : a3) + { + f1 (2); + f2 (2); + } + f2 (1); + } + f2 (0); + } +} + + +int +main (void) +{ + + int index[] = {0, 1, 2, 3, 4, 5}; + + J x (&index[0], &index[3]); + J y (&index[0], &index[4]); + J z (&index[0], &index[5]); + + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + s1 (x, y, z); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/imperfect-destructor.C b/libgomp/testsuite/libgomp.c++/imperfect-destructor.C new file mode 100644 index 0000000000000000000000000000000000000000..bd87760e076bd5a08017c6f6f486f4dcd908fb57 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/imperfect-destructor.C @@ -0,0 +1,135 @@ +/* { dg-do run } */ + +/* Make sure destructors are called for class variables bound + in intervening code. */ + +static int f1count[3], f2count[3]; +static int g1count[3], g2count[3]; + +static int ccount[3], dcount[3]; + +class C { + public: + int n; + C (int nn) { n = nn; ccount[n]++; } + ~C () { dcount[n]++; n = 0; } +}; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +int f1 (int depth, int iter) +{ + f1count[depth]++; + return iter; +} + +int f2 (int depth, int iter) +{ + f2count[depth]++; + return iter; +} + +int g1 (int depth, int iter) +{ + g1count[depth]++; + return iter; +} + +int g2 (int depth, int iter) +{ + g2count[depth]++; + return iter; +} + +void s1 (int a1, int a2, int a3) +{ + int i, j, k; + +#pragma omp for collapse(3) + for (i = 0; i < a1; i++) + { + C local0(0); + f1 (local0.n, i); + { + g1 (local0.n, i); + for (j = 0; j < a2; j++) + { + C local1(1); + f1 (local1.n, j); + { + g1 (local1.n, j); + for (k = 0; k < a3; k++) + { + C local2(2); + f1 (local2.n, k); + { + g1 (local2.n, k); + g2 (local2.n, k); + } + f2 (local2.n, k); + } + g2 (local1.n, j); + } + f2 (local1.n, j); + } + g2 (local0.n, i); + } + f2 (local0.n, i); + } +} + +int +main (void) +{ + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + g1count[0] = 0; + g1count[1] = 0; + g1count[2] = 0; + g2count[0] = 0; + g2count[1] = 0; + g2count[2] = 0; + + s1 (3, 4, 5); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + if (g1count[0] != f1count[0]) abort (); + if (g2count[0] != f1count[0]) abort (); + if (g1count[1] != f1count[1]) abort (); + if (g2count[1] != f1count[1]) abort (); + if (g1count[2] != f1count[2]) abort (); + if (g2count[2] != f1count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); + + /* Check that each class object declared in intervening code was + constructed and destructed an equal number of times. */ + if (ccount[0] != dcount[0]) abort (); + if (ccount[1] != dcount[1]) abort (); + if (ccount[2] != dcount[2]) abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/imperfect-template-1.C b/libgomp/testsuite/libgomp.c++/imperfect-template-1.C new file mode 100644 index 0000000000000000000000000000000000000000..4ed96c8319bb209a50a22981f8c13351ca71a5cb --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/imperfect-template-1.C @@ -0,0 +1,172 @@ +// { dg-do run } +// Test that template class iterators and imperfectly-nested loops +// work together. +// This variant tests initialization by assignment. + +typedef __PTRDIFF_TYPE__ ptrdiff_t; +extern "C" void abort (); + +template <typename T> +class I +{ +public: + typedef ptrdiff_t difference_type; + I (); + ~I (); + I (T *); + I (const I &); + T &operator * (); + T *operator -> (); + T &operator [] (const difference_type &) const; + I &operator = (const I &); + I &operator ++ (); + I operator ++ (int); + I &operator -- (); + I operator -- (int); + I &operator += (const difference_type &); + I &operator -= (const difference_type &); + I operator + (const difference_type &) const; + I operator - (const difference_type &) const; + template <typename S> friend bool operator == (I<S> &, I<S> &); + template <typename S> friend bool operator == (const I<S> &, const I<S> &); + template <typename S> friend bool operator < (I<S> &, I<S> &); + template <typename S> friend bool operator < (const I<S> &, const I<S> &); + template <typename S> friend bool operator <= (I<S> &, I<S> &); + template <typename S> friend bool operator <= (const I<S> &, const I<S> &); + template <typename S> friend bool operator > (I<S> &, I<S> &); + template <typename S> friend bool operator > (const I<S> &, const I<S> &); + template <typename S> friend bool operator >= (I<S> &, I<S> &); + template <typename S> friend bool operator >= (const I<S> &, const I<S> &); + template <typename S> friend typename I<S>::difference_type operator - (I<S> &, I<S> &); + template <typename S> friend typename I<S>::difference_type operator - (const I<S> &, const I<S> &); + template <typename S> friend I<S> operator + (typename I<S>::difference_type , const I<S> &); +private: + T *p; +}; +template <typename T> I<T>::I () : p (0) {} +template <typename T> I<T>::~I () { p = (T *) 0; } +template <typename T> I<T>::I (T *x) : p (x) {} +template <typename T> I<T>::I (const I &x) : p (x.p) {} +template <typename T> T &I<T>::operator * () { return *p; } +template <typename T> T *I<T>::operator -> () { return p; } +template <typename T> T &I<T>::operator [] (const difference_type &x) const { return p[x]; } +template <typename T> I<T> &I<T>::operator = (const I &x) { p = x.p; return *this; } +template <typename T> I<T> &I<T>::operator ++ () { ++p; return *this; } +template <typename T> I<T> I<T>::operator ++ (int) { return I (p++); } +template <typename T> I<T> &I<T>::operator -- () { --p; return *this; } +template <typename T> I<T> I<T>::operator -- (int) { return I (p--); } +template <typename T> I<T> &I<T>::operator += (const difference_type &x) { p += x; return *this; } +template <typename T> I<T> &I<T>::operator -= (const difference_type &x) { p -= x; return *this; } +template <typename T> I<T> I<T>::operator + (const difference_type &x) const { return I (p + x); } +template <typename T> I<T> I<T>::operator - (const difference_type &x) const { return I (p - x); } +template <typename T> bool operator == (I<T> &x, I<T> &y) { return x.p == y.p; } +template <typename T> bool operator == (const I<T> &x, const I<T> &y) { return x.p == y.p; } +template <typename T> bool operator != (I<T> &x, I<T> &y) { return !(x == y); } +template <typename T> bool operator != (const I<T> &x, const I<T> &y) { return !(x == y); } +template <typename T> bool operator < (I<T> &x, I<T> &y) { return x.p < y.p; } +template <typename T> bool operator < (const I<T> &x, const I<T> &y) { return x.p < y.p; } +template <typename T> bool operator <= (I<T> &x, I<T> &y) { return x.p <= y.p; } +template <typename T> bool operator <= (const I<T> &x, const I<T> &y) { return x.p <= y.p; } +template <typename T> bool operator > (I<T> &x, I<T> &y) { return x.p > y.p; } +template <typename T> bool operator > (const I<T> &x, const I<T> &y) { return x.p > y.p; } +template <typename T> bool operator >= (I<T> &x, I<T> &y) { return x.p >= y.p; } +template <typename T> bool operator >= (const I<T> &x, const I<T> &y) { return x.p >= y.p; } +template <typename T> typename I<T>::difference_type operator - (I<T> &x, I<T> &y) { return x.p - y.p; } +template <typename T> typename I<T>::difference_type operator - (const I<T> &x, const I<T> &y) { return x.p - y.p; } +template <typename T> I<T> operator + (typename I<T>::difference_type x, const I<T> &y) { return I<T> (x + y.p); } + +template <typename T> +class J +{ +public: + J(const I<T> &x, const I<T> &y) : b (x), e (y) {} + const I<T> &begin (); + const I<T> &end (); +private: + I<T> b, e; +}; + +template <typename T> const I<T> &J<T>::begin () { return b; } +template <typename T> const I<T> &J<T>::end () { return e; } + +static int f1count[3], f2count[3]; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +void f1 (int depth) +{ + f1count[depth]++; +} + +void f2 (int depth) +{ + f2count[depth]++; +} + +template <typename T> +void s1 (J<T> a1, J<T> a2, J<T> a3) +{ + I<T> i, j, k; + +#pragma omp for collapse(3) + for (i = a1.begin (); i < a1.end (); i++) + { + f1 (0); + for (j = a2.begin (); j < a2.end (); j++) + { + f1 (1); + for (k = a3.begin (); k < a3.end (); k++) + { + f1 (2); + f2 (2); + } + f2 (1); + } + f2 (0); + } +} + + +int +main (void) +{ + + int index[] = {0, 1, 2, 3, 4, 5}; + + J<int> x (&index[0], &index[3]); + J<int> y (&index[0], &index[4]); + J<int> z (&index[0], &index[5]); + + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + s1<int> (x, y, z); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/imperfect-template-2.C b/libgomp/testsuite/libgomp.c++/imperfect-template-2.C new file mode 100644 index 0000000000000000000000000000000000000000..a41c87c481f9afbac21e0a9f212f7f4c3264c351 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/imperfect-template-2.C @@ -0,0 +1,170 @@ +// { dg-do run } +// Test that template class iterators and imperfectly-nested loops +// work together. +// This variant tests initialization by declaration. + +typedef __PTRDIFF_TYPE__ ptrdiff_t; +extern "C" void abort (); + +template <typename T> +class I +{ +public: + typedef ptrdiff_t difference_type; + I (); + ~I (); + I (T *); + I (const I &); + T &operator * (); + T *operator -> (); + T &operator [] (const difference_type &) const; + I &operator = (const I &); + I &operator ++ (); + I operator ++ (int); + I &operator -- (); + I operator -- (int); + I &operator += (const difference_type &); + I &operator -= (const difference_type &); + I operator + (const difference_type &) const; + I operator - (const difference_type &) const; + template <typename S> friend bool operator == (I<S> &, I<S> &); + template <typename S> friend bool operator == (const I<S> &, const I<S> &); + template <typename S> friend bool operator < (I<S> &, I<S> &); + template <typename S> friend bool operator < (const I<S> &, const I<S> &); + template <typename S> friend bool operator <= (I<S> &, I<S> &); + template <typename S> friend bool operator <= (const I<S> &, const I<S> &); + template <typename S> friend bool operator > (I<S> &, I<S> &); + template <typename S> friend bool operator > (const I<S> &, const I<S> &); + template <typename S> friend bool operator >= (I<S> &, I<S> &); + template <typename S> friend bool operator >= (const I<S> &, const I<S> &); + template <typename S> friend typename I<S>::difference_type operator - (I<S> &, I<S> &); + template <typename S> friend typename I<S>::difference_type operator - (const I<S> &, const I<S> &); + template <typename S> friend I<S> operator + (typename I<S>::difference_type , const I<S> &); +private: + T *p; +}; +template <typename T> I<T>::I () : p (0) {} +template <typename T> I<T>::~I () { p = (T *) 0; } +template <typename T> I<T>::I (T *x) : p (x) {} +template <typename T> I<T>::I (const I &x) : p (x.p) {} +template <typename T> T &I<T>::operator * () { return *p; } +template <typename T> T *I<T>::operator -> () { return p; } +template <typename T> T &I<T>::operator [] (const difference_type &x) const { return p[x]; } +template <typename T> I<T> &I<T>::operator = (const I &x) { p = x.p; return *this; } +template <typename T> I<T> &I<T>::operator ++ () { ++p; return *this; } +template <typename T> I<T> I<T>::operator ++ (int) { return I (p++); } +template <typename T> I<T> &I<T>::operator -- () { --p; return *this; } +template <typename T> I<T> I<T>::operator -- (int) { return I (p--); } +template <typename T> I<T> &I<T>::operator += (const difference_type &x) { p += x; return *this; } +template <typename T> I<T> &I<T>::operator -= (const difference_type &x) { p -= x; return *this; } +template <typename T> I<T> I<T>::operator + (const difference_type &x) const { return I (p + x); } +template <typename T> I<T> I<T>::operator - (const difference_type &x) const { return I (p - x); } +template <typename T> bool operator == (I<T> &x, I<T> &y) { return x.p == y.p; } +template <typename T> bool operator == (const I<T> &x, const I<T> &y) { return x.p == y.p; } +template <typename T> bool operator != (I<T> &x, I<T> &y) { return !(x == y); } +template <typename T> bool operator != (const I<T> &x, const I<T> &y) { return !(x == y); } +template <typename T> bool operator < (I<T> &x, I<T> &y) { return x.p < y.p; } +template <typename T> bool operator < (const I<T> &x, const I<T> &y) { return x.p < y.p; } +template <typename T> bool operator <= (I<T> &x, I<T> &y) { return x.p <= y.p; } +template <typename T> bool operator <= (const I<T> &x, const I<T> &y) { return x.p <= y.p; } +template <typename T> bool operator > (I<T> &x, I<T> &y) { return x.p > y.p; } +template <typename T> bool operator > (const I<T> &x, const I<T> &y) { return x.p > y.p; } +template <typename T> bool operator >= (I<T> &x, I<T> &y) { return x.p >= y.p; } +template <typename T> bool operator >= (const I<T> &x, const I<T> &y) { return x.p >= y.p; } +template <typename T> typename I<T>::difference_type operator - (I<T> &x, I<T> &y) { return x.p - y.p; } +template <typename T> typename I<T>::difference_type operator - (const I<T> &x, const I<T> &y) { return x.p - y.p; } +template <typename T> I<T> operator + (typename I<T>::difference_type x, const I<T> &y) { return I<T> (x + y.p); } + +template <typename T> +class J +{ +public: + J(const I<T> &x, const I<T> &y) : b (x), e (y) {} + const I<T> &begin (); + const I<T> &end (); +private: + I<T> b, e; +}; + +template <typename T> const I<T> &J<T>::begin () { return b; } +template <typename T> const I<T> &J<T>::end () { return e; } + +static int f1count[3], f2count[3]; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +void f1 (int depth) +{ + f1count[depth]++; +} + +void f2 (int depth) +{ + f2count[depth]++; +} + +template <typename T> +void s1 (J<T> a1, J<T> a2, J<T> a3) +{ +#pragma omp for collapse(3) + for (I<T> i = a1.begin (); i < a1.end (); i++) + { + f1 (0); + for (I<T> j = a2.begin (); j < a2.end (); j++) + { + f1 (1); + for (I<T> k = a3.begin (); k < a3.end (); k++) + { + f1 (2); + f2 (2); + } + f2 (1); + } + f2 (0); + } +} + + +int +main (void) +{ + + int index[] = {0, 1, 2, 3, 4, 5}; + + J<int> x (&index[0], &index[3]); + J<int> y (&index[0], &index[4]); + J<int> z (&index[0], &index[5]); + + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + s1<int> (x, y, z); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); +} diff --git a/libgomp/testsuite/libgomp.c++/imperfect-template-3.C b/libgomp/testsuite/libgomp.c++/imperfect-template-3.C new file mode 100644 index 0000000000000000000000000000000000000000..2e464ed551009bc8ac8f58dc279ca5ac8d033d6f --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/imperfect-template-3.C @@ -0,0 +1,170 @@ +// { dg-do run } +// Test that template class iterators and imperfectly-nested loops +// work together. +// This variant tests range for syntax. + +typedef __PTRDIFF_TYPE__ ptrdiff_t; +extern "C" void abort (); + +template <typename T> +class I +{ +public: + typedef ptrdiff_t difference_type; + I (); + ~I (); + I (T *); + I (const I &); + T &operator * (); + T *operator -> (); + T &operator [] (const difference_type &) const; + I &operator = (const I &); + I &operator ++ (); + I operator ++ (int); + I &operator -- (); + I operator -- (int); + I &operator += (const difference_type &); + I &operator -= (const difference_type &); + I operator + (const difference_type &) const; + I operator - (const difference_type &) const; + template <typename S> friend bool operator == (I<S> &, I<S> &); + template <typename S> friend bool operator == (const I<S> &, const I<S> &); + template <typename S> friend bool operator < (I<S> &, I<S> &); + template <typename S> friend bool operator < (const I<S> &, const I<S> &); + template <typename S> friend bool operator <= (I<S> &, I<S> &); + template <typename S> friend bool operator <= (const I<S> &, const I<S> &); + template <typename S> friend bool operator > (I<S> &, I<S> &); + template <typename S> friend bool operator > (const I<S> &, const I<S> &); + template <typename S> friend bool operator >= (I<S> &, I<S> &); + template <typename S> friend bool operator >= (const I<S> &, const I<S> &); + template <typename S> friend typename I<S>::difference_type operator - (I<S> &, I<S> &); + template <typename S> friend typename I<S>::difference_type operator - (const I<S> &, const I<S> &); + template <typename S> friend I<S> operator + (typename I<S>::difference_type , const I<S> &); +private: + T *p; +}; +template <typename T> I<T>::I () : p (0) {} +template <typename T> I<T>::~I () { p = (T *) 0; } +template <typename T> I<T>::I (T *x) : p (x) {} +template <typename T> I<T>::I (const I &x) : p (x.p) {} +template <typename T> T &I<T>::operator * () { return *p; } +template <typename T> T *I<T>::operator -> () { return p; } +template <typename T> T &I<T>::operator [] (const difference_type &x) const { return p[x]; } +template <typename T> I<T> &I<T>::operator = (const I &x) { p = x.p; return *this; } +template <typename T> I<T> &I<T>::operator ++ () { ++p; return *this; } +template <typename T> I<T> I<T>::operator ++ (int) { return I (p++); } +template <typename T> I<T> &I<T>::operator -- () { --p; return *this; } +template <typename T> I<T> I<T>::operator -- (int) { return I (p--); } +template <typename T> I<T> &I<T>::operator += (const difference_type &x) { p += x; return *this; } +template <typename T> I<T> &I<T>::operator -= (const difference_type &x) { p -= x; return *this; } +template <typename T> I<T> I<T>::operator + (const difference_type &x) const { return I (p + x); } +template <typename T> I<T> I<T>::operator - (const difference_type &x) const { return I (p - x); } +template <typename T> bool operator == (I<T> &x, I<T> &y) { return x.p == y.p; } +template <typename T> bool operator == (const I<T> &x, const I<T> &y) { return x.p == y.p; } +template <typename T> bool operator != (I<T> &x, I<T> &y) { return !(x == y); } +template <typename T> bool operator != (const I<T> &x, const I<T> &y) { return !(x == y); } +template <typename T> bool operator < (I<T> &x, I<T> &y) { return x.p < y.p; } +template <typename T> bool operator < (const I<T> &x, const I<T> &y) { return x.p < y.p; } +template <typename T> bool operator <= (I<T> &x, I<T> &y) { return x.p <= y.p; } +template <typename T> bool operator <= (const I<T> &x, const I<T> &y) { return x.p <= y.p; } +template <typename T> bool operator > (I<T> &x, I<T> &y) { return x.p > y.p; } +template <typename T> bool operator > (const I<T> &x, const I<T> &y) { return x.p > y.p; } +template <typename T> bool operator >= (I<T> &x, I<T> &y) { return x.p >= y.p; } +template <typename T> bool operator >= (const I<T> &x, const I<T> &y) { return x.p >= y.p; } +template <typename T> typename I<T>::difference_type operator - (I<T> &x, I<T> &y) { return x.p - y.p; } +template <typename T> typename I<T>::difference_type operator - (const I<T> &x, const I<T> &y) { return x.p - y.p; } +template <typename T> I<T> operator + (typename I<T>::difference_type x, const I<T> &y) { return I<T> (x + y.p); } + +template <typename T> +class J +{ +public: + J(const I<T> &x, const I<T> &y) : b (x), e (y) {} + const I<T> &begin (); + const I<T> &end (); +private: + I<T> b, e; +}; + +template <typename T> const I<T> &J<T>::begin () { return b; } +template <typename T> const I<T> &J<T>::end () { return e; } + +static int f1count[3], f2count[3]; + +#ifndef __cplusplus +extern void abort (void); +#else +extern "C" void abort (void); +#endif + +void f1 (int depth) +{ + f1count[depth]++; +} + +void f2 (int depth) +{ + f2count[depth]++; +} + +template <typename T> +void s1 (J<T> a1, J<T> a2, J<T> a3) +{ +#pragma omp for collapse(3) + for (auto i : a1) + { + f1 (0); + for (auto j : a2) + { + f1 (1); + for (auto k : a3) + { + f1 (2); + f2 (2); + } + f2 (1); + } + f2 (0); + } +} + + +int +main (void) +{ + + int index[] = {0, 1, 2, 3, 4, 5}; + + J<int> x (&index[0], &index[3]); + J<int> y (&index[0], &index[4]); + J<int> z (&index[0], &index[5]); + + f1count[0] = 0; + f1count[1] = 0; + f1count[2] = 0; + f2count[0] = 0; + f2count[1] = 0; + f2count[2] = 0; + + s1<int> (x, y, z); + + /* All intervening code at the same depth must be executed the same + number of times. */ + if (f1count[0] != f2count[0]) abort (); + if (f1count[1] != f2count[1]) abort (); + if (f1count[2] != f2count[2]) abort (); + + /* Intervening code must be executed at least as many times as the loop + that encloses it. */ + if (f1count[0] < 3) abort (); + if (f1count[1] < 3 * 4) abort (); + + /* Intervening code must not be executed more times than the number + of logical iterations. */ + if (f1count[0] > 3 * 4 * 5) abort (); + if (f1count[1] > 3 * 4 * 5) abort (); + + /* Check that the innermost loop body is executed exactly the number + of logical iterations expected. */ + if (f1count[2] != 3 * 4 * 5) abort (); +}