From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-io1-xd31.google.com (mail-io1-xd31.google.com [IPv6:2607:f8b0:4864:20::d31]) by sourceware.org (Postfix) with ESMTPS id 5EB3538449C7 for ; Sat, 4 May 2024 21:22:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5EB3538449C7 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=baylibre.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 5EB3538449C7 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::d31 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857733; cv=none; b=GloUELLW1S59qkW6Qqp1ChFoYVtFsybTSxsaan2JBMap4zkS2p9TfOieQU0pTaSmjGsN5wmpqzKZbzqrb2+rLCkPUrhQcChWIjc6mnAyn72TfnotcXynMiP5kzp5Y6/I2+HEd2lXAnu7Sf7SsyEwdDJOm4SkK6ZppI7usTc7JLM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857733; c=relaxed/simple; bh=Dac+jrZlMPKN8iG7YBWDINg9vzLmY3qHSay4AdKdyN8=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=peMEu4UvbDXDTbE5NVYn//B2nlCTAxJ+U9qZJ5PUuniTJKytSZzqreB1AvuG8riMFDmi/FqRIjkYA/orzhQzLq0EdconV8sFP0VgJVMOsd9xy0kyvo1RFvjkMNJ2JLqqU6jbZ9HzhPkiDTKGu5A28wohBa/QwZv+oa7ea4ERnoo= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-io1-xd31.google.com with SMTP id ca18e2360f4ac-7dee0dfd57aso40806939f.1 for ; Sat, 04 May 2024 14:22:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857728; x=1715462528; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=qDJYVlzfUhk8ooG6Hv2jWo+Aw2kZx43s7Ot190RMLYk=; b=DBIec5RprnbSEoA1BvvdcjBecjX/tkrhEuG9mhx4SNVhvspDv7ILNxHiVQhcx7PhtK 7tv9JBGJvnp8wrS92v/rnd3292iTkDevkTglW9kgICzqnKhQG747jr8rxGqypy3LKfS3 05aaCNiIpLthdNMvEk/d51Xo/w3EeycBqYKdOrren/wm6TY93Y0ZE+9TADQeWZPQuyWo xh8C4NFekeHWWNSOcf2ESuNalChRdkLlBe161uHsuwvt8bZTNVNI8LCZ53QkhOn2lBog Jzh0IZEJsDaAZETGHi1PveDr4DwWNZPGwYgqXf1ixvMlCPHqWeQEiO1bTNf5cVGd++EP qNnA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857728; x=1715462528; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=qDJYVlzfUhk8ooG6Hv2jWo+Aw2kZx43s7Ot190RMLYk=; b=XDOJ2/mFlksi0XZGtRbH0KMI5tlnUpZs5+Dtg848p/5rxvKt91O7WTmwnaoqEV4kjW i/s+CjRz6dgZTkcZFZJxOHh/5Pm+NMvCL6Sywt8sSll5qhpH/oO3+ABfk51S0qclvXvT 0g1jkXDhouyoNgDbVtUrB1rk/sjenxlTbRDBo2xx+klhBBB0rYbRu+dxU3+Ujmc/Tm+K KdOWBXTlcaizbl3qjxLRhxu9xZ4/UPYmPsdcteOmbqglMAo92XezSzCZ3ZgFYPsNg9ki S/DMpgnslw+Ief5JNvDCFW82TBFqvvSpT3pJV+8OfktfuEQ+wzjAZGfsyb5sMuwvwF2w cZeQ== X-Gm-Message-State: AOJu0YwWYi95la4JamJC3PCuHZOXCYhjUat3Y9FObwPTSCFhukRYZGFe ZLq+6sjqiKwxoGy6/O5PuxnZX/v0jh9xZadmHCaL2nKZgmJEKZVl/6epGLalfL2U4S9fcVPkQNM 8 X-Google-Smtp-Source: AGHT+IHdvQs+7DRq2sqX7Ptqto5/TGYOeS2UZhyMAkKHMU8s7vI7kERlUBxJOfmLM5dO4Xv9YrtAJQ== X-Received: by 2002:a6b:3e09:0:b0:7de:e94d:5cc3 with SMTP id l9-20020a6b3e09000000b007dee94d5cc3mr6313641ioa.21.1714857727313; Sat, 04 May 2024 14:22:07 -0700 (PDT) Received: from pondscum.hsd1.co.comcast.net ([2601:281:d901:5620:3e29:4728:ec99:5098]) by smtp.gmail.com with ESMTPSA id ez3-20020a056638614300b004877be21febsm1559468jab.62.2024.05.04.14.22.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:06 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 04/12] OpenMP: C front end support for metadirectives Date: Sat, 4 May 2024 15:21:44 -0600 Message-Id: <20240504212153.3561429-5-sloosemore@baylibre.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240504212153.3561429-1-sloosemore@baylibre.com> References: <20240504212153.3561429-1-sloosemore@baylibre.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.0 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: 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 argument. (c_parser_finish_omp_declare_variant): Adjust call to above. (analyze_metadirective_body): New. (c_parser_omp_metadirective): New. gcc/testsuite/ChangeLog * gcc.dg/gomp/metadirective-1.c: New. Co-Authored-By: Kwok Cheung Yeung 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 2d5f5399885..03f62571531 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 c0e02aa422f..9426c0af2ce 100644 --- a/gcc/c-family/c-omp.cc +++ b/gcc/c-family/c-omp.cc @@ -4177,7 +4177,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, @@ -4207,7 +4207,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 }, @@ -4225,8 +4225,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. */ @@ -4309,3 +4309,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_variant &candidate = candidates[index]; + tree if_block = push_stmt_list (); + if (candidate.alternative != NULL_TREE) + add_stmt (candidate.alternative); + 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 41e8c923fcd..e6546bcb32b 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 }; @@ -7317,6 +7341,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: @@ -7388,6 +7424,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) { @@ -7615,8 +7654,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)) @@ -14666,6 +14708,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); @@ -24589,7 +24635,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 @@ -24736,12 +24782,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); @@ -24822,7 +24874,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 @@ -24847,7 +24900,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) @@ -24923,7 +24977,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); @@ -26494,6 +26549,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_early_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); + } +} -- 2.25.1