From patchwork Sat Jan 6 18:52:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 83460 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 86559385773F for ; Sat, 6 Jan 2024 18:55:34 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from esa2.mentor.iphmx.com (esa2.mentor.iphmx.com [68.232.141.98]) by sourceware.org (Postfix) with ESMTPS id E92C53858418 for ; Sat, 6 Jan 2024 18:53:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E92C53858418 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=codesourcery.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=mentor.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org E92C53858418 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=68.232.141.98 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704567241; cv=none; b=U4heD2FeTWaE5Lcuv054MM74zopeCutpVmxv7W6Av2bskPAb1TDUXL8W+3+6hfKFTwGKt8TZFfVFKfkaeeozvNYPSc0LzY98AGbrAvGm389DUrb3liA1vALfT2kOVjB4qAu/8caeTiOebjxwWxbN537MSkq9jYAMq8dzN5daK04= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704567241; c=relaxed/simple; bh=v4R7uhf2D/fTSTNzXdin3PjyR1h5h9oGgFbwGUxxeMs=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=qLr9z4v7XxogPUrerya85MhHanIltHmncMU5ZUpKEvZ5+ruy4/Llt00MA+9odjSv1WdLgQ5zD42jof0Yfq8QzarKRYEEFJbF/8j8WyMREjpXCKsjGIuroR4QrW/Vb13krKrEf1CGtMnDLxH/7QUb+X2g1G3gtMuSfXLIsfxt3Vw= ARC-Authentication-Results: i=1; server2.sourceware.org X-CSE-ConnectionGUID: JqWNT01RRgmQern+J9DWnA== X-CSE-MsgGUID: eyg2XUHATba+FydrVcBS0w== X-IronPort-AV: E=Sophos;i="6.04,337,1695715200"; d="scan'208";a="30509740" Received: from orw-gwy-02-in.mentorg.com ([192.94.38.167]) by esa2.mentor.iphmx.com with ESMTP; 06 Jan 2024 10:53:55 -0800 IronPort-SDR: JbQ1cga7ytxY8NJJjnpn2qHzlQo0UAsk79HSEJH15MRpL16HzPESsi5PITRKo8OFU25An46qb2 ASq6DOyKUJFQ7s1XjdbTP4Hey2dw96oIJIv6mIj+XDa9iovFBR7iJ0HQ5ZhVehiZJhpJG9dOKB aGGrlQ6GkOjwCzkTEBIdgNiNUglK4cfy5cVwnbuDHag4nFq1R/Il4U6g4dSUprtfbQUcAc9Jly 2QmxTbZXMh/WRtUGfyb0evkkixGqC/H5jLHp0WBOWk3juqc6wDgAcEzjm8RWj+m6rT6rfdvlG9 7C0= From: Sandra Loosemore To: CC: , , Subject: [PATCH 4/8] OpenMP: C front end support for metadirectives Date: Sat, 6 Jan 2024 11:52:52 -0700 Message-ID: <20240106185257.126445-5-sandra@codesourcery.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240106185257.126445-1-sandra@codesourcery.com> References: <20240106185257.126445-1-sandra@codesourcery.com> MIME-Version: 1.0 X-ClientProxiedBy: svr-orw-mbx-10.mgc.mentorg.com (147.34.90.210) To svr-orw-mbx-13.mgc.mentorg.com (147.34.90.213) X-Spam-Status: No, score=-10.0 required=5.0 tests=BAYES_00, GIT_PATCH_0, HEADER_FROM_DIFFERENT_DOMAINS, KAM_DMARC_STATUS, SPF_HELO_PASS, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org From: Kwok Cheung Yeung This patch adds support to the C front end to parse OpenMP metadirective constructs. It includes support for early parse-time resolution of metadirectives (when possible) that will also be used by the C++ front end. Additional common C/C++ testcases are in a later patch in the series. gcc/c-family/ChangeLog * c-common.h (enum c_omp_directive_kind): Add C_OMP_DIR_META. (c_omp_expand_metadirective): Declare. * c-gimplify.cc: Include omp-general.h. (genericize_omp_metadirective_stmt): New. (c_genericize_control_stmt): Call it. * c-omp.cc (c_omp_directives): Add "metadirective" and fix commented-out stubs for the begin/end form. (c_omp_expand_metadirective_r): New. (c_omp_expand_metadirective): New. * c-pragma.cc (omp_pragmas): Add "metadirective". * c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_METADIRECTIVE. gcc/c/ChangeLog * c-parser.cc (struct c_parser): Add new fields for metadirectives. (c_parser_skip_to_end_of_block_or_statement): Add metadirective_p parameter; use it to control brace and parentheses behavior. (mangle_metadirective_region_label): New. (c_parser_label, c_parser_statement_after_labels): Use it. (c_parser_pragma): Handle metadirective. (c_parser_omp_context_selector): Add metadirective_p flag, use it to gate support for non-constant user condition. (c_parser_omp_context_selector_specification): Add metadirective_p flag. (c_parser_finish_omp_declare_variant): Adjust call. (analyze_metadirective_body): New. (c_parser_omp_metadirective): New. gcc/testsuite/ChangeLog * gcc.dg/gomp/metadirective-1.c: New. Co-Authored-By: Sandra Loosemore --- gcc/c-family/c-common.h | 4 +- gcc/c-family/c-gimplify.cc | 27 ++ gcc/c-family/c-omp.cc | 60 ++- gcc/c-family/c-pragma.cc | 1 + gcc/c-family/c-pragma.h | 1 + gcc/c/c-parser.cc | 489 +++++++++++++++++++- gcc/testsuite/gcc.dg/gomp/metadirective-1.c | 15 + 7 files changed, 577 insertions(+), 20 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/gomp/metadirective-1.c diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 3c2d75a0027..bad12c8119f 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1391,7 +1391,8 @@ enum c_omp_directive_kind { C_OMP_DIR_CONSTRUCT, C_OMP_DIR_DECLARATIVE, C_OMP_DIR_UTILITY, - C_OMP_DIR_INFORMATIONAL + C_OMP_DIR_INFORMATIONAL, + C_OMP_DIR_META }; struct c_omp_directive { @@ -1405,6 +1406,7 @@ extern const struct c_omp_directive c_omp_directives[]; extern const struct c_omp_directive *c_omp_categorize_directive (const char *, const char *, const char *); +extern tree c_omp_expand_metadirective (vec &); /* Return next tree in the chain for chain_next walking of tree nodes. */ inline tree diff --git a/gcc/c-family/c-gimplify.cc b/gcc/c-family/c-gimplify.cc index 494da49791d..c53aca60bcf 100644 --- a/gcc/c-family/c-gimplify.cc +++ b/gcc/c-family/c-gimplify.cc @@ -43,6 +43,7 @@ along with GCC; see the file COPYING3. If not see #include "context.h" #include "tree-pass.h" #include "internal-fn.h" +#include "omp-general.h" /* The gimplification pass converts the language-dependent trees (ld-trees) emitted by the parser into language-independent trees @@ -485,6 +486,27 @@ genericize_omp_for_stmt (tree *stmt_p, int *walk_subtrees, void *data, finish_bc_block (&OMP_FOR_BODY (stmt), bc_continue, clab); } +/* Genericize a OMP_METADIRECTIVE node *STMT_P. */ + +static void +genericize_omp_metadirective_stmt (tree *stmt_p, int *walk_subtrees, + void *data, walk_tree_fn func, + walk_tree_lh lh) +{ + tree stmt = *stmt_p; + + for (tree variant = OMP_METADIRECTIVE_VARIANTS (stmt); + variant != NULL_TREE; + variant = TREE_CHAIN (variant)) + { + walk_tree_1 (&OMP_METADIRECTIVE_VARIANT_DIRECTIVE (variant), + func, data, NULL, lh); + walk_tree_1 (&OMP_METADIRECTIVE_VARIANT_BODY (variant), + func, data, NULL, lh); + } + + *walk_subtrees = 0; +} /* Lower structured control flow tree nodes, such as loops. The STMT_P, WALK_SUBTREES, and DATA arguments are as for the walk_tree_fn @@ -533,6 +555,11 @@ c_genericize_control_stmt (tree *stmt_p, int *walk_subtrees, void *data, genericize_omp_for_stmt (stmt_p, walk_subtrees, data, func, lh); break; + case OMP_METADIRECTIVE: + genericize_omp_metadirective_stmt (stmt_p, walk_subtrees, data, func, + lh); + break; + case STATEMENT_LIST: if (TREE_SIDE_EFFECTS (stmt)) { diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc index 5bceb9cb5d9..974f34594a8 100644 --- a/gcc/c-family/c-omp.cc +++ b/gcc/c-family/c-omp.cc @@ -4131,7 +4131,7 @@ const struct c_omp_directive c_omp_directives[] = { /* { "begin", "declare", "variant", PRAGMA_OMP_BEGIN, C_OMP_DIR_DECLARATIVE, false }, */ /* { "begin", "metadirective", nullptr, PRAGMA_OMP_BEGIN, - C_OMP_DIR_???, ??? }, */ + C_OMP_DIR_META, false }, */ { "cancel", nullptr, nullptr, PRAGMA_OMP_CANCEL, C_OMP_DIR_STANDALONE, false }, { "cancellation", "point", nullptr, PRAGMA_OMP_CANCELLATION_POINT, @@ -4161,7 +4161,7 @@ const struct c_omp_directive c_omp_directives[] = { /* { "end", "declare", "variant", PRAGMA_OMP_END, C_OMP_DIR_DECLARATIVE, false }, */ /* { "end", "metadirective", nullptr, PRAGMA_OMP_END, - C_OMP_DIR_???, ??? }, */ + C_OMP_DIR_META, false }, */ /* error with at(execution) is C_OMP_DIR_STANDALONE. */ { "error", nullptr, nullptr, PRAGMA_OMP_ERROR, C_OMP_DIR_UTILITY, false }, @@ -4179,8 +4179,8 @@ const struct c_omp_directive c_omp_directives[] = { C_OMP_DIR_CONSTRUCT, true }, { "master", nullptr, nullptr, PRAGMA_OMP_MASTER, C_OMP_DIR_CONSTRUCT, true }, - /* { "metadirective", nullptr, nullptr, PRAGMA_OMP_METADIRECTIVE, - C_OMP_DIR_???, ??? }, */ + { "metadirective", nullptr, nullptr, PRAGMA_OMP_METADIRECTIVE, + C_OMP_DIR_META, false }, { "nothing", nullptr, nullptr, PRAGMA_OMP_NOTHING, C_OMP_DIR_UTILITY, false }, /* ordered with depend clause is C_OMP_DIR_STANDALONE. */ @@ -4263,3 +4263,55 @@ c_omp_categorize_directive (const char *first, const char *second, } return NULL; } + +/* Auxilliary helper function for c_omp_expand_metadirective. */ + +static tree +c_omp_expand_metadirective_r (vec &candidates, + hash_map &body_labels, + unsigned index) +{ + struct omp_metadirective_variant &candidate = candidates[index]; + tree if_block = push_stmt_list (); + if (candidate.directive != NULL_TREE) + add_stmt (candidate.directive); + if (candidate.body != NULL_TREE) + { + tree *label = body_labels.get (candidate.body); + if (label != NULL) + add_stmt (build1 (GOTO_EXPR, void_type_node, *label)); + else + { + tree body_label = create_artificial_label (UNKNOWN_LOCATION); + add_stmt (build1 (LABEL_EXPR, void_type_node, body_label)); + add_stmt (candidate.body); + body_labels.put (candidate.body, body_label); + } + } + if_block = pop_stmt_list (if_block); + + if (index == candidates.length () - 1) + return if_block; + + tree cond = candidate.dynamic_selector; + gcc_assert (cond != NULL_TREE); + + tree else_block = c_omp_expand_metadirective_r (candidates, body_labels, + index + 1); + tree ret = push_stmt_list (); + tree stmt = build3 (COND_EXPR, void_type_node, cond, if_block, else_block); + add_stmt (stmt); + ret = pop_stmt_list (ret); + + return ret; +} + +/* Resolve the vector of metadirective variant CANDIDATES to a parse tree + structure. */ + +tree +c_omp_expand_metadirective (vec &candidates) +{ + hash_map body_labels; + return c_omp_expand_metadirective_r (candidates, body_labels, 0); +} diff --git a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc index 1237ee6e62b..8488a4d1142 100644 --- a/gcc/c-family/c-pragma.cc +++ b/gcc/c-family/c-pragma.cc @@ -1529,6 +1529,7 @@ static const struct omp_pragma_def omp_pragmas[] = { { "error", PRAGMA_OMP_ERROR }, { "end", PRAGMA_OMP_END }, { "flush", PRAGMA_OMP_FLUSH }, + { "metadirective", PRAGMA_OMP_METADIRECTIVE }, { "nothing", PRAGMA_OMP_NOTHING }, { "requires", PRAGMA_OMP_REQUIRES }, { "scope", PRAGMA_OMP_SCOPE }, diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h index ce93a52fa57..f933755ea44 100644 --- a/gcc/c-family/c-pragma.h +++ b/gcc/c-family/c-pragma.h @@ -64,6 +64,7 @@ enum pragma_kind { PRAGMA_OMP_NOTHING, PRAGMA_OMP_MASKED, PRAGMA_OMP_MASTER, + PRAGMA_OMP_METADIRECTIVE, PRAGMA_OMP_ORDERED, PRAGMA_OMP_PARALLEL, PRAGMA_OMP_REQUIRES, diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index b3b3838b010..70c6fba90ba 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -266,6 +266,13 @@ struct GTY(()) c_parser { /* Set for omp::decl attribute parsing to the decl to which it appertains. */ tree in_omp_decl_attribute; + + /* Set if we are processing a statement body associated with a + metadirective variant. */ + BOOL_BITFIELD in_metadirective_body : 1; + + vec * GTY((skip)) metadirective_body_labels; + unsigned int metadirective_region_num; }; /* Return a pointer to the Nth token in PARSERs tokens_buf. */ @@ -1437,9 +1444,11 @@ c_parser_skip_to_pragma_eol (c_parser *parser, bool error_if_not_eol = true) have consumed a non-nested ';'. */ static void -c_parser_skip_to_end_of_block_or_statement (c_parser *parser) +c_parser_skip_to_end_of_block_or_statement (c_parser *parser, + bool metadirective_p = false) { unsigned nesting_depth = 0; + int bracket_depth = 0; bool save_error = parser->error; while (true) @@ -1462,7 +1471,7 @@ c_parser_skip_to_end_of_block_or_statement (c_parser *parser) case CPP_SEMICOLON: /* If the next token is a ';', we have reached the end of the statement. */ - if (!nesting_depth) + if (!nesting_depth && (!metadirective_p || bracket_depth <= 0)) { /* Consume the ';'. */ c_parser_consume_token (parser); @@ -1473,7 +1482,8 @@ c_parser_skip_to_end_of_block_or_statement (c_parser *parser) case CPP_CLOSE_BRACE: /* If the next token is a non-nested '}', then we have reached the end of the current block. */ - if (nesting_depth == 0 || --nesting_depth == 0) + if ((nesting_depth == 0 || --nesting_depth == 0) + && (!metadirective_p || bracket_depth <= 0)) { c_parser_consume_token (parser); goto finished; @@ -1486,6 +1496,19 @@ c_parser_skip_to_end_of_block_or_statement (c_parser *parser) ++nesting_depth; break; + case CPP_OPEN_PAREN: + /* Track parentheses in case the statement is a standalone 'for' + statement - we want to skip over the semicolons separating the + operands. */ + if (metadirective_p && nesting_depth == 0) + ++bracket_depth; + break; + + case CPP_CLOSE_PAREN: + if (metadirective_p && nesting_depth == 0) + --bracket_depth; + break; + case CPP_PRAGMA: /* If we see a pragma, consume the whole thing at once. We have some safeguards against consuming pragmas willy-nilly. @@ -1718,6 +1741,7 @@ static void c_parser_omp_taskwait (c_parser *); static void c_parser_omp_taskyield (c_parser *); static void c_parser_omp_cancel (c_parser *); static void c_parser_omp_nothing (c_parser *); +static void c_parser_omp_metadirective (c_parser *, bool *); enum pragma_context { pragma_external, pragma_struct, pragma_param, pragma_stmt, pragma_compound }; @@ -7316,6 +7340,18 @@ c_parser_all_labels (c_parser *parser) c_parser_error (parser, "expected statement"); } + +/* Helper function for c_parser_label: mangle a metadirective region + label NAME. */ +static tree +mangle_metadirective_region_label (c_parser *parser, tree name) +{ + const char *old_name = IDENTIFIER_POINTER (name); + char *new_name = (char *) alloca (strlen (old_name) + 32); + sprintf (new_name, "%s_MDR%u", old_name, parser->metadirective_region_num); + return get_identifier (new_name); +} + /* Parse a label (C90 6.6.1, C99 6.8.1, C11 6.8.1). label: @@ -7387,6 +7423,9 @@ c_parser_label (c_parser *parser, tree std_attrs) gcc_assert (c_parser_next_token_is (parser, CPP_COLON)); c_parser_consume_token (parser); attrs = c_parser_gnu_attributes (parser); + if (parser->in_metadirective_body + && parser->metadirective_body_labels->contains (name)) + name = mangle_metadirective_region_label (parser, name); tlab = define_label (loc2, name); if (tlab) { @@ -7614,8 +7653,11 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, c_parser_consume_token (parser); if (c_parser_next_token_is (parser, CPP_NAME)) { - stmt = c_finish_goto_label (loc, - c_parser_peek_token (parser)->value); + tree name = c_parser_peek_token (parser)->value; + if (parser->in_metadirective_body + && parser->metadirective_body_labels->contains (name)) + name = mangle_metadirective_region_label (parser, name); + stmt = c_finish_goto_label (loc, name); c_parser_consume_token (parser); } else if (c_parser_next_token_is (parser, CPP_MULT)) @@ -14617,6 +14659,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p) c_parser_omp_nothing (parser); return false; + case PRAGMA_OMP_METADIRECTIVE: + c_parser_omp_metadirective (parser, if_p); + return true; + case PRAGMA_OMP_ERROR: return c_parser_omp_error (parser, context); @@ -24393,7 +24439,7 @@ c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context) static tree c_parser_omp_context_selector (c_parser *parser, enum omp_tss_code set, - tree parms) + tree parms, bool metadirective_p) { tree ret = NULL_TREE; do @@ -24539,12 +24585,18 @@ c_parser_omp_context_selector (c_parser *parser, enum omp_tss_code set, { mark_exp_read (t); t = c_fully_fold (t, false, NULL); - /* FIXME: this is bogus, both device_num and - condition selectors allow arbitrary expressions. */ - if (!INTEGRAL_TYPE_P (TREE_TYPE (t)) - || !tree_fits_shwi_p (t)) - error_at (token->location, "property must be " - "constant integer expression"); + /* FIXME: I believe it is an unimplemented feature rather + than a user error to have non-constant expressions + inside "declare variant". */ + if (!metadirective_p + && (!INTEGRAL_TYPE_P (TREE_TYPE (t)) + || !tree_fits_shwi_p (t))) + error_at (token->location, + "property must be constant integer expression"); + else if (metadirective_p + && !INTEGRAL_TYPE_P (TREE_TYPE (t))) + error_at (token->location, + "property must be integer expression"); else properties = make_trait_property (NULL_TREE, t, properties); @@ -24625,7 +24677,8 @@ c_parser_omp_context_selector (c_parser *parser, enum omp_tss_code set, user */ static tree -c_parser_omp_context_selector_specification (c_parser *parser, tree parms) +c_parser_omp_context_selector_specification (c_parser *parser, tree parms, + bool metadirective_p) { tree ret = NULL_TREE; do @@ -24650,7 +24703,8 @@ c_parser_omp_context_selector_specification (c_parser *parser, tree parms) if (!braces.require_open (parser)) return error_mark_node; - tree selectors = c_parser_omp_context_selector (parser, set, parms); + tree selectors = c_parser_omp_context_selector (parser, set, parms, + metadirective_p); if (selectors == error_mark_node) ret = error_mark_node; else if (ret != error_mark_node) @@ -24726,7 +24780,8 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) if (parms == NULL_TREE) parms = error_mark_node; - tree ctx = c_parser_omp_context_selector_specification (parser, parms); + tree ctx = c_parser_omp_context_selector_specification (parser, + parms, false); if (ctx == error_mark_node) goto fail; ctx = omp_check_context_selector (match_loc, ctx, false); @@ -26297,6 +26352,410 @@ c_parser_omp_assumes (c_parser *parser) c_parser_omp_assumption_clauses (parser, false); } +/* Helper function for c_parser_omp_metadirective. */ + +static void +analyze_metadirective_body (c_parser *parser, + vec &tokens, + vec &labels) +{ + int nesting_depth = 0; + int bracket_depth = 0; + bool ignore_label = false; + + /* Read in the body tokens to the tokens for each candidate directive. */ + while (1) + { + c_token *token = c_parser_peek_token (parser); + bool stop = false; + + if (c_parser_next_token_is_keyword (parser, RID_CASE)) + ignore_label = true; + + switch (token->type) + { + case CPP_EOF: + break; + case CPP_NAME: + if (!ignore_label + && c_parser_peek_2nd_token (parser)->type == CPP_COLON) + labels.safe_push (token->value); + goto add; + case CPP_OPEN_BRACE: + ++nesting_depth; + goto add; + case CPP_CLOSE_BRACE: + if (--nesting_depth == 0 && bracket_depth == 0) + stop = true; + goto add; + case CPP_OPEN_PAREN: + ++bracket_depth; + goto add; + case CPP_CLOSE_PAREN: + --bracket_depth; + goto add; + case CPP_COLON: + ignore_label = false; + goto add; + case CPP_SEMICOLON: + if (nesting_depth == 0 && bracket_depth == 0) + stop = true; + goto add; + default: + add: + tokens.safe_push (*token); + if (token->type == CPP_PRAGMA) + c_parser_consume_pragma (parser); + else if (token->type == CPP_PRAGMA_EOL) + c_parser_skip_to_pragma_eol (parser); + else + c_parser_consume_token (parser); + if (stop) + break; + continue; + } + break; + } +} + +/* OpenMP 5.0: + + # pragma omp metadirective [clause[, clause]] +*/ + +static void +c_parser_omp_metadirective (c_parser *parser, bool *if_p) +{ + static unsigned int metadirective_region_count = 0; + + tree ret; + auto_vec directive_tokens; + auto_vec body_tokens; + auto_vec body_labels; + auto_vec directives; + auto_vec ctxs; + vec candidates; + bool default_seen = false; + int directive_token_idx = 0; + tree standalone_body = NULL_TREE; + location_t pragma_loc = c_parser_peek_token (parser)->location; + bool requires_body = false; + + ret = make_node (OMP_METADIRECTIVE); + SET_EXPR_LOCATION (ret, pragma_loc); + TREE_TYPE (ret) = void_type_node; + OMP_METADIRECTIVE_VARIANTS (ret) = NULL_TREE; + + c_parser_consume_pragma (parser); + while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) + { + if (c_parser_next_token_is_not (parser, CPP_NAME) + && c_parser_next_token_is_not (parser, CPP_KEYWORD)) + { + c_parser_error (parser, "expected %, " + "%, or % clause"); + goto error; + } + + location_t match_loc = c_parser_peek_token (parser)->location; + const char *p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + c_parser_consume_token (parser); + bool default_p + = strcmp (p, "default") == 0 || strcmp (p, "otherwise") == 0; + if (default_p) + { + if (default_seen) + { + error_at (match_loc, "too many % or % " + "clauses in %"); + c_parser_skip_to_end_of_block_or_statement (parser, true); + goto error; + } + default_seen = true; + } + if (!(strcmp (p, "when") == 0 || default_p)) + { + error_at (match_loc, "%qs is not valid for %qs", + p, "metadirective"); + c_parser_skip_to_end_of_block_or_statement (parser, true); + goto error; + } + + matching_parens parens; + tree ctx = NULL_TREE; + bool skip = false; + + if (!parens.require_open (parser)) + goto error; + + if (!default_p) + { + ctx = c_parser_omp_context_selector_specification (parser, + NULL_TREE, true); + if (ctx == error_mark_node) + goto error; + ctx = omp_check_context_selector (match_loc, ctx, true); + if (ctx == error_mark_node) + goto error; + + /* Remove the selector from further consideration if can be + evaluated as a non-match at this point. */ + skip = omp_context_selector_matches (ctx, true, true) == 0; + + if (c_parser_next_token_is_not (parser, CPP_COLON)) + { + c_parser_require (parser, CPP_COLON, "expected %<:%>"); + goto error; + } + c_parser_consume_token (parser); + } + + /* Read in the directive type and create a dummy pragma token for + it. */ + location_t loc = c_parser_peek_token (parser)->location; + + const char *directive[3] = {}; + int i; + for (i = 0; i < 3; i++) + { + tree id; + if (c_parser_peek_nth_token (parser, i + 1)->type + == CPP_CLOSE_PAREN) + { + if (i == 0) + directive[i++] = "nothing"; + break; + } + else if (c_parser_peek_nth_token (parser, i + 1)->type + == CPP_NAME) + id = c_parser_peek_nth_token (parser, i + 1)->value; + else if (c_parser_peek_nth_token (parser, i + 1)->keyword + != RID_MAX) + { + enum rid rid + = c_parser_peek_nth_token (parser, i + 1)->keyword; + id = ridpointers[rid]; + } + else + break; + + directive[i] = IDENTIFIER_POINTER (id); + } + if (i == 0) + { + error_at (loc, "expected directive name"); + c_parser_skip_to_end_of_block_or_statement (parser, true); + goto error; + } + + const struct c_omp_directive *omp_directive + = c_omp_categorize_directive (directive[0], + directive[1], + directive[2]); + + if (omp_directive == NULL) + { + for (int j = 0; j < i; j++) + c_parser_consume_token (parser); + c_parser_error (parser, "unknown directive name"); + goto error; + } + else + { + int token_count = 0; + if (omp_directive->first) token_count++; + if (omp_directive->second) token_count++; + if (omp_directive->third) token_count++; + for (int j = 0; j < token_count; j++) + c_parser_consume_token (parser); + } + if (omp_directive->id == PRAGMA_OMP_METADIRECTIVE) + { + c_parser_error (parser, + "metadirectives cannot be used as variants of a " + "%"); + goto error; + } + if (omp_directive->kind == C_OMP_DIR_DECLARATIVE) + { + sorry_at (loc, "declarative directive variants of a " + "% are not supported"); + goto error; + } + if (omp_directive->kind == C_OMP_DIR_CONSTRUCT) + requires_body = true; + + if (!skip) + { + c_token pragma_token; + pragma_token.type = CPP_PRAGMA; + pragma_token.location = loc; + pragma_token.pragma_kind = (enum pragma_kind) omp_directive->id; + + directives.safe_push (omp_directive); + directive_tokens.safe_push (pragma_token); + ctxs.safe_push (ctx); + } + + /* Read in tokens for the directive clauses. */ + int nesting_depth = 0; + while (1) + { + c_token *token = c_parser_peek_token (parser); + switch (token->type) + { + case CPP_EOF: + case CPP_PRAGMA_EOL: + break; + case CPP_OPEN_PAREN: + ++nesting_depth; + goto add; + case CPP_CLOSE_PAREN: + if (nesting_depth-- == 0) + break; + goto add; + default: + add: + if (!skip) + directive_tokens.safe_push (*token); + c_parser_consume_token (parser); + continue; + } + break; + } + + c_parser_consume_token (parser); + + if (!skip) + { + c_token eol_token; + memset (&eol_token, 0, sizeof (eol_token)); + eol_token.type = CPP_PRAGMA_EOL; + directive_tokens.safe_push (eol_token); + } + } + c_parser_skip_to_pragma_eol (parser); + + if (!default_seen) + { + /* Add a default clause that evaluates to 'omp nothing'. */ + const struct c_omp_directive *omp_directive + = c_omp_categorize_directive ("nothing", NULL, NULL); + + c_token pragma_token; + pragma_token.type = CPP_PRAGMA; + pragma_token.location = UNKNOWN_LOCATION; + pragma_token.pragma_kind = PRAGMA_OMP_NOTHING; + + directives.safe_push (omp_directive); + directive_tokens.safe_push (pragma_token); + ctxs.safe_push (NULL_TREE); + + c_token eol_token; + memset (&eol_token, 0, sizeof (eol_token)); + eol_token.type = CPP_PRAGMA_EOL; + directive_tokens.safe_push (eol_token); + } + + if (requires_body) + analyze_metadirective_body (parser, body_tokens, body_labels); + + /* Process each candidate directive. */ + unsigned i; + tree ctx; + + FOR_EACH_VEC_ELT (ctxs, i, ctx) + { + auto_vec tokens; + + /* Add the directive tokens. */ + do + tokens.safe_push (directive_tokens [directive_token_idx++]); + while (tokens.last ().type != CPP_PRAGMA_EOL); + + /* Add the body tokens. */ + gcc_assert (requires_body || body_tokens.is_empty ()); + for (unsigned j = 0; j < body_tokens.length (); j++) + tokens.safe_push (body_tokens[j]); + + /* Make sure nothing tries to read past the end of the tokens. */ + c_token eof_token; + memset (&eof_token, 0, sizeof (eof_token)); + eof_token.type = CPP_EOF; + tokens.safe_push (eof_token); + tokens.safe_push (eof_token); + + unsigned int old_tokens_avail = parser->tokens_avail; + c_token *old_tokens = parser->tokens; + bool old_in_metadirective_body = parser->in_metadirective_body; + vec *old_metadirective_body_labels + = parser->metadirective_body_labels; + unsigned int old_metadirective_region_num + = parser->metadirective_region_num; + + parser->tokens = tokens.address (); + parser->tokens_avail = tokens.length (); + parser->in_metadirective_body = true; + parser->metadirective_body_labels = &body_labels; + parser->metadirective_region_num = ++metadirective_region_count; + + int prev_errorcount = errorcount; + tree directive = c_begin_compound_stmt (true); + + c_parser_pragma (parser, pragma_compound, if_p); + directive = c_end_compound_stmt (pragma_loc, directive, true); + bool standalone_p + = directives[i]->kind == C_OMP_DIR_STANDALONE + || directives[i]->kind == C_OMP_DIR_UTILITY; + if (standalone_p && requires_body) + { + /* Parsing standalone directives will not consume the body + tokens, so do that here. */ + if (standalone_body == NULL_TREE) + { + standalone_body = push_stmt_list (); + c_parser_statement (parser, if_p); + standalone_body = pop_stmt_list (standalone_body); + } + else + c_parser_skip_to_end_of_block_or_statement (parser, true); + } + + tree body = standalone_p ? standalone_body : NULL_TREE; + tree variant = make_omp_metadirective_variant (ctx, directive, body); + OMP_METADIRECTIVE_VARIANTS (ret) + = chainon (OMP_METADIRECTIVE_VARIANTS (ret), variant); + + /* Check that all valid tokens have been consumed if no parse errors + encountered. */ + if (errorcount == prev_errorcount) + { + gcc_assert (parser->tokens_avail == 2); + gcc_assert (c_parser_next_token_is (parser, CPP_EOF)); + gcc_assert (c_parser_peek_2nd_token (parser)->type == CPP_EOF); + } + + parser->tokens = old_tokens; + parser->tokens_avail = old_tokens_avail; + parser->in_metadirective_body = old_in_metadirective_body; + parser->metadirective_body_labels = old_metadirective_body_labels; + parser->metadirective_region_num = old_metadirective_region_num; + } + + /* Try to resolve the metadirective early. */ + candidates = omp_resolve_metadirective (ret); + if (!candidates.is_empty ()) + ret = c_omp_expand_metadirective (candidates); + + add_stmt (ret); + return; + +error: + if (parser->in_pragma) + c_parser_skip_to_pragma_eol (parser); + c_parser_skip_to_end_of_block_or_statement (parser, true); +} + /* Main entry point to parsing most OpenMP pragmas. */ static void diff --git a/gcc/testsuite/gcc.dg/gomp/metadirective-1.c b/gcc/testsuite/gcc.dg/gomp/metadirective-1.c new file mode 100644 index 00000000000..2ac81bfde75 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/metadirective-1.c @@ -0,0 +1,15 @@ +int main (void) +{ + int x, y; + + /* Test nested functions inside statement body. */ + #pragma omp metadirective \ + when (device={arch("nvptx")}: teams num_teams(512)) \ + when (device={arch("gcn")}: teams num_teams(256)) \ + default (teams num_teams(4)) + { + int f (int x) { return x * 3; } + + y = f (x); + } +}