From patchwork Sat May 4 21:21:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 1931419 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=UBRoHvRt; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX0xr4JyGz1ybC for ; Sun, 5 May 2024 07:22:40 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C8533384474A for ; Sat, 4 May 2024 21:22:38 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-il1-x130.google.com (mail-il1-x130.google.com [IPv6:2607:f8b0:4864:20::130]) by sourceware.org (Postfix) with ESMTPS id 7BC643858402 for ; Sat, 4 May 2024 21:22:04 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7BC643858402 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 7BC643858402 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::130 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857729; cv=none; b=B3cmlTwZziXHZSuA6HfEZn0EnpKvxB/5V5pMd8QndaSLGjlnRXsmvosJ8QtIiCwEg5yIEQNOJ70DSDkf/5toyu2DSM4V5d4237250LGnPbVuMiDdlReY2GqX4WiKEZsHPUOdeD4PMVPvD2BuvTJgtIeuxOHBSbgB1+as2yYpDuw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857729; c=relaxed/simple; bh=cH2Kv8jRfhskI0HmnemyMQ/xFxa5hJ3+6RXEMlgItkM=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=N27gc9+UIuASrfRlR6Fz0X1WWdQ5oxvdvyzkqmxVHwQbZgiSTENR6g+jN4774InSHpRb96E3dD8ckYBLfpKwJoh4i9ye/NsJlCMP+9a1bLHp1AWXfy/pCS8eZZI08QSi+BC3qTRrrefHTxSggzL8zf7d17kFnu5Vxx06iJciWCs= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-il1-x130.google.com with SMTP id e9e14a558f8ab-36c791e9faaso2727005ab.1 for ; Sat, 04 May 2024 14:22:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857723; x=1715462523; 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=yfPSjtcQENtDu4YkXt59G+EDbzXZ4gRnVK59KQ99Yv4=; b=UBRoHvRtu8Q+dEapSWkegjqI85brCUnGneZXAPctTQ/YeZN32ybXQEj0phA8OjOv1r /TQR8Nq0SUWZkb1Pyd1IYxwWVvPz2aMM5KHB4MZdC8hNqm2GJNjV/LIP8zAibwtyxGQq Y79Ci2FD4bG+uG/aWWH8nkrnWDCtVJoxkOX/OIYkzL/OB3FdrfQH4ga+EGjCFk6VwZWO GGUhbmaYIKk1qVOwd+B6BSQLXtxMszBo1ee1EJUXuljJVh/ozwhsUpg/58Fb/iAMcA7a I2B3SULw9NdexyuxiLjmLU/O48TH5Q8lkFPtgjGQUuOitJk21pwdV+vQtNrVoE/QXePS eRSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857723; x=1715462523; 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=yfPSjtcQENtDu4YkXt59G+EDbzXZ4gRnVK59KQ99Yv4=; b=klVi4k5kBXbgkzKW/s3GpSf/805cBcy0z0S/JunDXNS9tVnSsOhsdlYPx7Fcf9LJUd G+bY609jljosEyfRK8GxiesQ4yulCcqNnYlfdGxNIP52TTohVksoWvo/cA9YRNqU6BGu UzxvhMXWwTZ8zXfKfusvp0YxfDdoLih8CGd3dicfKlzI2ZAziVq8vohu3UFHmzS9TbvF IRXEB5XSV144UO1+s7q2VVZLq4/x6xXtcyyitVXC6xnq8qLOY+uMeTuuR1PSE/N59r/Z smDuQTJRUI+yOu/ewB70onRWhYzvb/GEPmKCNgUQoTHPWQbICdcktjBzIQ3lK2TyU7jh RuWQ== X-Gm-Message-State: AOJu0Yx3vVT0m6RE9B4XUOjmswnmJ4IIg73VDTV2oh+OIohFjdD48jnR 0GvVu9a00+3x8u40FVV3AsDf9hc6dX1vfirAJ/uRqkAEZUgM9+wmvJ4nGOJzGODN6jg+E4ilNml 5 X-Google-Smtp-Source: AGHT+IFwbfXYa0PvoCpV3aL8jYtpsjyPPI9e2EPD59qDqwHMwuoFftrQTZkbHNIYFTXNy6rEi4oVeg== X-Received: by 2002:a05:6e02:20c3:b0:36c:4f4f:ad62 with SMTP id 3-20020a056e0220c300b0036c4f4fad62mr8164062ilq.13.1714857722451; Sat, 04 May 2024 14:22:02 -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.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:01 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 01/12] OpenMP: metadirective tree data structures and front-end interfaces Date: Sat, 4 May 2024 15:21:41 -0600 Message-Id: <20240504212153.3561429-2-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 X-Spam-Status: No, score=-9.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLACK 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+incoming=patchwork.ozlabs.org@gcc.gnu.org This patch adds the OMP_METADIRECTIVE tree node and shared tree-level support for manipulating metadirectives. It defines/exposes interfaces that will be used in subsequent patches that add front-end and middle-end support, but nothing generates these nodes yet. This patch also adds compile-time support for dynamic context selectors (the target_device selector set and the condition selector of the user selector set) for metadirectives only. The "declare variant" directive still supports only static selectors. gcc/ChangeLog * Makefile.in (GTFILES): Move omp-general.h earlier in the list. * builtin-types.def (BT_FN_BOOL_INT_CONST_PTR_CONST_PTR_CONST_PTR): New. * doc/generic.texi (OpenMP): Document OMP_METADIRECTIVE and context selector interfaces. * omp-builtins.def (BUILT_IN_GOMP_EVALUATE_TARGET_DEVICE): New. * omp-general.cc (omp_check_context_selector): Add metadirective_p parameter, use it to conditionalize target_device support. (make_omp_metadirective_variant): New. (omp_context_selector_matches): Add metadirective_p and delay_p parameters, use them to control things that can only be matched late. Handle OMP_TRAIT_SET_TARGET_DEVICE. (score_wide_int): Move definition to omp-general.h. (omp_encode_kind_arch_isa_props): New. (omp_dynamic_cond): New. (omp_context_compute_score): Handle OMP_TRAIT_SET_TARGET_DEVICE. (omp_resolve_late_declare_variant, omp_resolve_declare_variant): Adjust calls to omp_context_selector_matches. (sort_variant): New. (omp_get_dynamic_candidates): New. (omp_early_resolve_metadirective): New. * omp-general.h (score_wide_int): Moved here from omp-general.cc. (struct omp_variant): New. (OMP_METADIRECTIVE_VARIANT_SELECTOR): New. (OMP_METADIRECTIVE_VARIANT_DIRECTIVE): New. (OMP_METADIRECTIVE_VARIANT_BODY): New. (make_omp_metadirective_variant): Declare. (omp_check_context_selector): Adjust to match definition. (omp_context_selector_matches): Likewise. (omp_early_resolve_metadirective): New. * tree-pretty-print.cc (dump_omp_context_selector): Remove static qualifier. (dump_generic_node): Handle OMP_METADIRECTIVE. * tree-pretty-print.h (dump_omp_context_selector): Declare. * tree.def (OMP_METADIRECTIVE): New. * tree.h (OMP_METADIRECTIVE_VARIANTS): New. gcc/c/ChangeLog * c-parser.cc (c_finish_omp_declare_variant): Update calls to omp_check_context_selector and omp_context_selector_matches. gcc/cp/ChangeLog * decl.cc (omp_declare_variant_finalize_one): Update call to omp_context_selector_matches to pass additional arguments. * parser.cc (cp_finish_omp_declare_variant): Likewise for omp_check_context_selector. gcc/fortran/ChangeLog * trans-openmp.cc (gfc_trans_omp_declare_variant): Update calls to omp_check_context_selector and omp_context_selector_matches. * types.def (BT_FN_BOOL_INT_CONST_PTR_CONST_PTR_CONST_PTR): New. Co-Authored-By: Kwok Cheung Yeung Co-Authored-By: Sandra Loosemore --- gcc/Makefile.in | 2 +- gcc/builtin-types.def | 2 + gcc/c/c-parser.cc | 4 +- gcc/cp/decl.cc | 2 +- gcc/cp/parser.cc | 2 +- gcc/doc/generic.texi | 32 ++++ gcc/fortran/trans-openmp.cc | 4 +- gcc/fortran/types.def | 2 + gcc/omp-builtins.def | 3 + gcc/omp-general.cc | 357 ++++++++++++++++++++++++++++++++++-- gcc/omp-general.h | 31 +++- gcc/tree-pretty-print.cc | 36 +++- gcc/tree-pretty-print.h | 2 + gcc/tree.def | 6 + gcc/tree.h | 3 + 15 files changed, 461 insertions(+), 27 deletions(-) diff --git a/gcc/Makefile.in b/gcc/Makefile.in index a74761b7ab3..65638c7b5d6 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -2868,6 +2868,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/tree-ssa-operands.h \ $(srcdir)/tree-profile.cc $(srcdir)/tree-nested.cc \ $(srcdir)/omp-offload.h \ + $(srcdir)/omp-general.h \ $(srcdir)/omp-general.cc \ $(srcdir)/omp-low.cc \ $(srcdir)/targhooks.cc $(out_file) $(srcdir)/passes.cc \ @@ -2894,7 +2895,6 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/ipa-strub.cc \ $(srcdir)/internal-fn.h \ $(srcdir)/calls.cc \ - $(srcdir)/omp-general.h \ $(srcdir)/analyzer/analyzer-language.cc \ @all_gtfiles@ diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def index c97d6bad1de..605a38ab84d 100644 --- a/gcc/builtin-types.def +++ b/gcc/builtin-types.def @@ -878,6 +878,8 @@ DEF_FUNCTION_TYPE_4 (BT_FN_VOID_UINT_PTR_INT_PTR, BT_VOID, BT_INT, BT_PTR, BT_INT, BT_PTR) DEF_FUNCTION_TYPE_4 (BT_FN_BOOL_UINT_UINT_UINT_BOOL, BT_BOOL, BT_UINT, BT_UINT, BT_UINT, BT_BOOL) +DEF_FUNCTION_TYPE_4 (BT_FN_BOOL_INT_CONST_PTR_CONST_PTR_CONST_PTR, + BT_BOOL, BT_INT, BT_CONST_PTR, BT_CONST_PTR, BT_CONST_PTR) DEF_FUNCTION_TYPE_5 (BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG, BT_INT, BT_STRING, BT_INT, BT_SIZE, BT_CONST_STRING, diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 00f8bf4376e..41e8c923fcd 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -24926,7 +24926,7 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) tree ctx = c_parser_omp_context_selector_specification (parser, parms); if (ctx == error_mark_node) goto fail; - ctx = omp_check_context_selector (match_loc, ctx); + ctx = omp_check_context_selector (match_loc, ctx, false); if (ctx != error_mark_node && variant != error_mark_node) { if (TREE_CODE (variant) != FUNCTION_DECL) @@ -24959,7 +24959,7 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) tree construct = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT); omp_mark_declare_variant (match_loc, variant, construct); - if (omp_context_selector_matches (ctx)) + if (omp_context_selector_matches (ctx, false, true)) { tree attr = tree_cons (get_identifier ("omp declare variant base"), diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 2af026d255d..2f989b9b869 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -8328,7 +8328,7 @@ omp_declare_variant_finalize_one (tree decl, tree attr) tree construct = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT); omp_mark_declare_variant (match_loc, variant, construct); - if (!omp_context_selector_matches (ctx)) + if (!omp_context_selector_matches (ctx, false, true)) return true; TREE_PURPOSE (TREE_VALUE (attr)) = variant; } diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 598380dda08..8b819b2ebfd 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -48489,7 +48489,7 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, tree ctx = cp_parser_omp_context_selector_specification (parser, true); if (ctx == error_mark_node) goto fail; - ctx = omp_check_context_selector (match_loc, ctx); + ctx = omp_check_context_selector (match_loc, ctx, false); if (ctx != error_mark_node && variant != error_mark_node) { tree match_loc_node = maybe_wrap_with_location (integer_zero_node, diff --git a/gcc/doc/generic.texi b/gcc/doc/generic.texi index c596b7d44b2..e869203df0e 100644 --- a/gcc/doc/generic.texi +++ b/gcc/doc/generic.texi @@ -2335,6 +2335,7 @@ edge. Rethrowing the exception is represented using @code{RESX_EXPR}. @tindex OMP_CONTINUE @tindex OMP_ATOMIC @tindex OMP_CLAUSE +@tindex OMP_METADIRECTIVE All the statements starting with @code{OMP_} represent directives and clauses used by the OpenMP API @w{@uref{https://www.openmp.org}}. @@ -2552,6 +2553,37 @@ same clause @code{C} need to be represented as multiple @code{C} clauses chained together. This facilitates adding new clauses during compilation. +@item OMP_METADIRECTIVE + +Represents @code{#pragma omp metadirective}. This node has one field, +accessed by the @code{OMP_METADIRECTIVE_VARIANTS (@var{node})} macro. + +Metadirective variants are represented internally as @code{TREE_LIST} nodes +but you should use the interface provided in @file{omp-general.h} to +access their components. + +@code{OMP_METADIRECTIVE_VARIANT_SELECTOR (@var{variant})} +is the selector associated with the variant; this is null for the +@samp{otherwise}/@samp{default} alternative. + +@code{OMP_METADIRECTIVE_VARIANT_DIRECTIVE (@var{variant})} is the +nested directive for the variant. + +@code{OMP_METADIRECTIVE_VARIANT_BODY (@var{variant})} represents +statements following a nested standalone or utility directive. +In other cases, this field is null and the body is part of the +nested directive instead. + +Metadirective context selectors (as well as context selectors for +@code{#pragma omp declare variant}) are also represented internally using +a @code{TREE_LIST} representation but with accessors and constructors +declared in @file{omp-general.h}. A complete context selector is a list of +trait-set selectors, which are in turn composed of a list of trait selectors, +each of which may have a list of trait properties. +Identifiers for trait-set selectors and trait selectors are enums +defined in @file{omp-selectors.h}, while trait property identifiers are +string constants. + @end table @node OpenACC diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc index f867e2240bf..99ec0320258 100644 --- a/gcc/fortran/trans-openmp.cc +++ b/gcc/fortran/trans-openmp.cc @@ -8504,7 +8504,7 @@ gfc_trans_omp_declare_variant (gfc_namespace *ns) continue; } set_selectors = omp_check_context_selector - (gfc_get_location (&odv->where), set_selectors); + (gfc_get_location (&odv->where), set_selectors, false); if (set_selectors != error_mark_node) { if (!variant_proc_sym->attr.implicit_type @@ -8541,7 +8541,7 @@ gfc_trans_omp_declare_variant (gfc_namespace *ns) omp_mark_declare_variant (gfc_get_location (&odv->where), gfc_get_symbol_decl (variant_proc_sym), construct); - if (omp_context_selector_matches (set_selectors)) + if (omp_context_selector_matches (set_selectors, false, true)) { tree id = get_identifier ("omp declare variant base"); tree variant = gfc_get_symbol_decl (variant_proc_sym); diff --git a/gcc/fortran/types.def b/gcc/fortran/types.def index 390cc9542f7..f1e2973e5ff 100644 --- a/gcc/fortran/types.def +++ b/gcc/fortran/types.def @@ -176,6 +176,8 @@ DEF_FUNCTION_TYPE_4 (BT_FN_VOID_UINT_PTR_INT_PTR, BT_VOID, BT_INT, BT_PTR, BT_INT, BT_PTR) DEF_FUNCTION_TYPE_4 (BT_FN_BOOL_UINT_UINT_UINT_BOOL, BT_BOOL, BT_UINT, BT_UINT, BT_UINT, BT_BOOL) +DEF_FUNCTION_TYPE_4 (BT_FN_BOOL_INT_CONST_PTR_CONST_PTR_CONST_PTR, + BT_BOOL, BT_INT, BT_CONST_PTR, BT_CONST_PTR, BT_CONST_PTR) DEF_FUNCTION_TYPE_5 (BT_FN_VOID_OMPFN_PTR_UINT_UINT_UINT, BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, BT_UINT, diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def index 044d5d087b6..ecaace8de31 100644 --- a/gcc/omp-builtins.def +++ b/gcc/omp-builtins.def @@ -476,3 +476,6 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_WARNING, "GOMP_warning", BT_FN_VOID_CONST_PTR_SIZE, ATTR_NOTHROW_LEAF_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_ERROR, "GOMP_error", BT_FN_VOID_CONST_PTR_SIZE, ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_EVALUATE_TARGET_DEVICE, "GOMP_evaluate_target_device", + BT_FN_BOOL_INT_CONST_PTR_CONST_PTR_CONST_PTR, + ATTR_NOTHROW_LEAF_LIST) diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc index 2c095200d5b..e4c84d15644 100644 --- a/gcc/omp-general.cc +++ b/gcc/omp-general.cc @@ -1269,7 +1269,7 @@ omp_context_name_list_prop (tree prop) it is correct or error_mark_node otherwise. */ tree -omp_check_context_selector (location_t loc, tree ctx) +omp_check_context_selector (location_t loc, tree ctx, bool metadirective_p) { bool tss_seen[OMP_TRAIT_SET_LAST], ts_seen[OMP_TRAIT_LAST]; @@ -1278,9 +1278,10 @@ omp_check_context_selector (location_t loc, tree ctx) { enum omp_tss_code tss_code = OMP_TSS_CODE (tss); - /* We can parse this, but not handle it yet. */ - if (tss_code == OMP_TRAIT_SET_TARGET_DEVICE) - sorry_at (loc, "% selector set is not supported yet"); + /* FIXME: not implemented yet. */ + if (!metadirective_p && tss_code == OMP_TRAIT_SET_TARGET_DEVICE) + sorry_at (loc, "% selector set is not supported " + "yet for %"); /* Each trait-set-selector-name can only be specified once. */ if (tss_seen[tss_code]) @@ -1432,14 +1433,29 @@ make_trait_property (tree name, tree value, tree chain) return tree_cons (name, value, chain); } +/* Constructor for metadirective variants. */ +tree +make_omp_metadirective_variant (tree selector, tree directive, tree body) +{ + return build_tree_list (selector, build_tree_list (directive, body)); +} + + /* Return 1 if context selector matches the current OpenMP context, 0 if it does not and -1 if it is unknown and need to be determined later. Some properties can be checked right away during parsing (this routine), others need to wait until the whole TU is parsed, others need to wait until - IPA, others until vectorization. */ + IPA, others until vectorization. + + METADIRECTIVE_P is true if this is a metadirective context, and DELAY_P + is true if it's too early in compilation to determine whether some + properties match. + + Dynamic properties (which are evaluated at run-time) should always + return 1. */ int -omp_context_selector_matches (tree ctx) +omp_context_selector_matches (tree ctx, bool metadirective_p, bool delay_p) { int ret = 1; for (tree tss = ctx; tss; tss = TREE_CHAIN (tss)) @@ -1512,6 +1528,11 @@ omp_context_selector_matches (tree ctx) ret = -1; continue; } + else if (set == OMP_TRAIT_SET_TARGET_DEVICE) + /* The target_device set is dynamic, so treat it as always + resolvable. */ + continue; + for (tree ts = selectors; ts; ts = TREE_CHAIN (ts)) { enum omp_ts_code sel = OMP_TS_CODE (ts); @@ -1581,6 +1602,9 @@ omp_context_selector_matches (tree ctx) const char *arch = omp_context_name_list_prop (p); if (arch == NULL) return 0; + if (metadirective_p && delay_p) + return -1; + int r = 0; if (targetm.omp.device_kind_arch_isa != NULL) r = targetm.omp.device_kind_arch_isa (omp_device_arch, @@ -1703,6 +1727,9 @@ omp_context_selector_matches (tree ctx) #endif continue; } + if (metadirective_p && delay_p) + return -1; + int r = 0; if (targetm.omp.device_kind_arch_isa != NULL) r = targetm.omp.device_kind_arch_isa (omp_device_kind, @@ -1742,6 +1769,9 @@ omp_context_selector_matches (tree ctx) const char *isa = omp_context_name_list_prop (p); if (isa == NULL) return 0; + if (metadirective_p && delay_p) + return -1; + int r = 0; if (targetm.omp.device_kind_arch_isa != NULL) r = targetm.omp.device_kind_arch_isa (omp_device_isa, @@ -1793,6 +1823,12 @@ omp_context_selector_matches (tree ctx) for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) if (OMP_TP_NAME (p) == NULL_TREE) { + /* OpenMP 5.1 allows non-constant conditions for + metadirectives. */ + if (metadirective_p + && !tree_fits_shwi_p (OMP_TP_VALUE (p))) + break; + if (integer_zerop (OMP_TP_VALUE (p))) return 0; if (integer_nonzerop (OMP_TP_VALUE (p))) @@ -2191,9 +2227,107 @@ omp_lookup_ts_code (enum omp_tss_code set, const char *s) return OMP_TRAIT_INVALID; } -/* Needs to be a GC-friendly widest_int variant, but precision is - desirable to be the same on all targets. */ -typedef generic_wide_int > score_wide_int; +/* Helper for omp_dynamic_cond: encode the kind/arch/isa property-lists + into strings for GOMP_evaluate_target_device. The property-list + strings are encoded similarly to those in omp_offload_kind_arch_isa, + above: each trait is passed as a string, with each property for the + string separated by '\0', and an extra '\0' at the end of the string. */ +static tree +omp_encode_kind_arch_isa_props (tree props) +{ + if (!props) + return NULL_TREE; + size_t length = 1; + for (tree p = props; p; p = TREE_CHAIN (p)) + length += strlen (omp_context_name_list_prop (p)) + 1; + char *buffer = (char *) alloca (length); + size_t n = 0; + for (tree p = props; p; p = TREE_CHAIN (p)) + { + const char *str = omp_context_name_list_prop (p); + strcpy (buffer + n, str); + n += strlen (str) + 1; + } + *(buffer + n) = '\0'; + return build_string_literal (length, buffer); +} + +/* Return a tree expression representing the dynamic part of the context + selector CTX. */ +static tree +omp_dynamic_cond (tree ctx) +{ + tree expr = NULL_TREE; + + tree user = omp_get_context_selector (ctx, OMP_TRAIT_SET_USER, + OMP_TRAIT_USER_CONDITION); + if (user) + { + tree expr_list = OMP_TS_PROPERTIES (user); + + gcc_assert (OMP_TP_NAME (expr_list) == NULL_TREE); + + /* The user condition is not dynamic if it is constant. */ + if (!tree_fits_shwi_p (TREE_VALUE (expr_list))) + expr = TREE_VALUE (expr_list); + } + + tree target_device + = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_TARGET_DEVICE); + if (target_device) + { + tree device_num = null_pointer_node; + tree kind = null_pointer_node; + tree arch = null_pointer_node; + tree isa = null_pointer_node; + + tree device_num_sel + = omp_get_context_selector (ctx, OMP_TRAIT_SET_TARGET_DEVICE, + OMP_TRAIT_DEVICE_NUM); + if (device_num_sel) + device_num = OMP_TP_VALUE (OMP_TS_PROPERTIES (device_num_sel)); + else + /* omp_initial_device is -1, omp_invalid_device is -4; choose + a value that isn't otherwise defined to indicate the default + device. */ + device_num = build_int_cst (integer_type_node, -2); + + tree kind_sel + = omp_get_context_selector (ctx, OMP_TRAIT_SET_TARGET_DEVICE, + OMP_TRAIT_DEVICE_KIND); + /* "any" is equivalent to omitting this trait selector. */ + if (kind_sel + && strcmp (omp_context_name_list_prop (OMP_TS_PROPERTIES (kind_sel)), + "any")) + kind = omp_encode_kind_arch_isa_props (OMP_TS_PROPERTIES (kind_sel)); + + + tree arch_sel + = omp_get_context_selector (ctx, OMP_TRAIT_SET_TARGET_DEVICE, + OMP_TRAIT_DEVICE_ARCH); + if (arch_sel) + arch = omp_encode_kind_arch_isa_props (OMP_TS_PROPERTIES (arch_sel)); + + tree isa_sel + = omp_get_context_selector (ctx, OMP_TRAIT_SET_TARGET_DEVICE, + OMP_TRAIT_DEVICE_ISA); + if (isa_sel) + isa = omp_encode_kind_arch_isa_props (OMP_TS_PROPERTIES (isa_sel)); + + /* Generate a call to GOMP_evaluate_target_device. */ + tree builtin_fn + = builtin_decl_explicit (BUILT_IN_GOMP_EVALUATE_TARGET_DEVICE); + tree call = build_call_expr (builtin_fn, 4, device_num, kind, arch, isa); + + if (expr == NULL_TREE) + expr = call; + else + expr = fold_build2 (TRUTH_ANDIF_EXPR, boolean_type_node, expr, call); + } + + return expr; +} + /* Compute *SCORE for context selector CTX. Return true if the score would be different depending on whether it is a declare simd clone or @@ -2205,12 +2339,21 @@ omp_context_compute_score (tree ctx, score_wide_int *score, bool declare_simd) { tree selectors = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT); - bool has_kind = omp_get_context_selector (ctx, OMP_TRAIT_SET_DEVICE, - OMP_TRAIT_DEVICE_KIND); - bool has_arch = omp_get_context_selector (ctx, OMP_TRAIT_SET_DEVICE, - OMP_TRAIT_DEVICE_ARCH); - bool has_isa = omp_get_context_selector (ctx, OMP_TRAIT_SET_DEVICE, - OMP_TRAIT_DEVICE_ISA); + bool has_kind + = (omp_get_context_selector (ctx, OMP_TRAIT_SET_DEVICE, + OMP_TRAIT_DEVICE_KIND) + || omp_get_context_selector (ctx, OMP_TRAIT_SET_TARGET_DEVICE, + OMP_TRAIT_DEVICE_KIND)); + bool has_arch + = (omp_get_context_selector (ctx, OMP_TRAIT_SET_DEVICE, + OMP_TRAIT_DEVICE_ARCH) + || omp_get_context_selector (ctx, OMP_TRAIT_SET_TARGET_DEVICE, + OMP_TRAIT_DEVICE_ARCH)); + bool has_isa + = (omp_get_context_selector (ctx, OMP_TRAIT_SET_DEVICE, + OMP_TRAIT_DEVICE_ISA) + || omp_get_context_selector (ctx, OMP_TRAIT_SET_TARGET_DEVICE, + OMP_TRAIT_DEVICE_ISA)); bool ret = false; *score = 1; for (tree tss = ctx; tss; tss = TREE_CHAIN (tss)) @@ -2395,7 +2538,7 @@ omp_resolve_late_declare_variant (tree alt) nmatches++; continue; } - switch (omp_context_selector_matches (varentry1->ctx)) + switch (omp_context_selector_matches (varentry1->ctx, false, true)) { case 0: matches.safe_push (false); @@ -2499,7 +2642,8 @@ omp_resolve_declare_variant (tree base) don't process it again. */ if (node && node->declare_variant_alt) return base; - switch (omp_context_selector_matches (TREE_VALUE (TREE_VALUE (attr)))) + switch (omp_context_selector_matches (TREE_VALUE (TREE_VALUE (attr)), + false, true)) { case 0: /* No match, ignore. */ @@ -2864,6 +3008,185 @@ omp_lto_input_declare_variant_alt (lto_input_block *ib, cgraph_node *node, INSERT) = entryp; } +/* Comparison function for sorting routines, to sort OpenMP metadirective + variants by decreasing score. */ + +static int +sort_variant (const void * a, const void *b, void *) +{ + score_wide_int score1 + = ((const struct omp_variant *) a)->score; + score_wide_int score2 + = ((const struct omp_variant *) b)->score; + + if (score1 > score2) + return -1; + else if (score1 < score2) + return 1; + else + return 0; +} + +/* Return a vector of dynamic replacement candidates for the directive + candidates in ALL_VARIANTS. Return an empty vector if the metadirective + cannot be resolved. */ + +static vec +omp_get_dynamic_candidates (vec &all_variants, + bool delay_p) +{ + auto_vec variants; + struct omp_variant default_variant; + bool default_found = false; + + for (unsigned int i = 0; i < all_variants.length (); i++) + { + struct omp_variant variant = all_variants[i]; + + if (variant.selector == NULL_TREE) + { + gcc_assert (!default_found); + default_found = true; + default_variant = variant; + default_variant.score = 0; + default_variant.resolvable_p = true; + default_variant.dynamic_selector = NULL_TREE; + if (dump_file) + fprintf (dump_file, + "Considering default selector as candidate\n"); + continue; + } + + variant.resolvable_p = true; + + if (dump_file) + { + fprintf (dump_file, "Considering selector "); + print_omp_context_selector (dump_file, variant.selector, TDF_NONE); + fprintf (dump_file, " as candidate - "); + } + + switch (omp_context_selector_matches (variant.selector, true, delay_p)) + { + case -1: + variant.resolvable_p = false; + if (dump_file) + fprintf (dump_file, "unresolvable"); + /* FALLTHRU */ + case 1: + /* TODO: Handle SIMD score?. */ + omp_context_compute_score (variant.selector, &variant.score, false); + variant.dynamic_selector = omp_dynamic_cond (variant.selector); + variants.safe_push (variant); + if (dump_file && variant.resolvable_p) + { + if (variant.dynamic_selector) + fprintf (dump_file, "matched, dynamic"); + else + fprintf (dump_file, "matched, non-dynamic"); + } + break; + case 0: + if (dump_file) + fprintf (dump_file, "no match"); + break; + } + + if (dump_file) + fprintf (dump_file, "\n"); + } + + /* There must be one default variant. */ + gcc_assert (default_found); + + /* A context selector that is a strict subset of another context selector + has a score of zero. */ + for (unsigned int i = 0; i < variants.length (); i++) + for (unsigned int j = i + 1; j < variants.length (); j++) + { + int r = omp_context_selector_compare (variants[i].selector, + variants[j].selector); + if (r == -1) + { + /* variant1 is a strict subset of variant2. */ + variants[i].score = 0; + break; + } + else if (r == 1) + /* variant2 is a strict subset of variant1. */ + variants[j].score = 0; + } + + /* Sort the variants by decreasing score, preserving the original order + in case of a tie. */ + variants.stablesort (sort_variant, NULL); + + /* Add the default as a final choice. */ + variants.safe_push (default_variant); + + /* Build the dynamic candidate list. */ + for (unsigned i = 0; i < variants.length (); i++) + { + /* If one of the candidates is unresolvable, give up for now. */ + if (!variants[i].resolvable_p) + { + variants.truncate (0); + break; + } + + if (dump_file) + { + fprintf (dump_file, "Adding directive variant with "); + + if (variants[i].selector) + { + fprintf (dump_file, "selector "); + print_omp_context_selector (dump_file, variants[i].selector, + TDF_NONE); + } + else + fprintf (dump_file, "default selector"); + + fprintf (dump_file, " as candidate.\n"); + } + + /* The last of the candidates is ended by a static selector. */ + if (!variants[i].dynamic_selector) + { + variants.truncate (i + 1); + break; + } + } + + return variants.copy (); +} + +/* Return a vector of dynamic replacement candidates for the metadirective + statement in METADIRECTIVE. Return an empty vector if the metadirective + cannot be resolved. */ + +vec +omp_early_resolve_metadirective (tree metadirective) +{ + auto_vec candidates; + tree variant = OMP_METADIRECTIVE_VARIANTS (metadirective); + + gcc_assert (variant); + while (variant) + { + struct omp_variant candidate; + + candidate.selector = OMP_METADIRECTIVE_VARIANT_SELECTOR (variant); + candidate.alternative = OMP_METADIRECTIVE_VARIANT_DIRECTIVE (variant); + candidate.body = OMP_METADIRECTIVE_VARIANT_BODY (variant); + + candidates.safe_push (candidate); + variant = TREE_CHAIN (variant); + } + + return omp_get_dynamic_candidates (candidates, true); +} + /* Encode an oacc launch argument. This matches the GOMP_LAUNCH_PACK macro on gomp-constants.h. We do not check for overflow. */ diff --git a/gcc/omp-general.h b/gcc/omp-general.h index b7d85a768ce..5807bc42cd7 100644 --- a/gcc/omp-general.h +++ b/gcc/omp-general.h @@ -91,6 +91,22 @@ struct omp_for_data tree adjn1; }; +/* Needs to be a GC-friendly widest_int variant, but precision is + desirable to be the same on all targets. */ +typedef generic_wide_int > score_wide_int; + +/* A structure describing a variant in a metadirective. */ + +struct GTY(()) omp_variant +{ + score_wide_int score; + tree selector; + tree alternative; + tree body; + tree dynamic_selector; + bool resolvable_p : 1; +}; + #define OACC_FN_ATTRIB "oacc function" /* Accessors for OMP context selectors, used by variant directives. @@ -150,6 +166,15 @@ extern tree make_trait_set_selector (enum omp_tss_code, tree, tree); extern tree make_trait_selector (enum omp_ts_code, tree, tree, tree); extern tree make_trait_property (tree, tree, tree); +/* Accessors and constructor for metadirective variants. */ +#define OMP_METADIRECTIVE_VARIANT_SELECTOR(v) \ + TREE_PURPOSE (v) +#define OMP_METADIRECTIVE_VARIANT_DIRECTIVE(v) \ + TREE_PURPOSE (TREE_VALUE (v)) +#define OMP_METADIRECTIVE_VARIANT_BODY(v) \ + TREE_VALUE (TREE_VALUE (v)) +extern tree make_omp_metadirective_variant (tree, tree, tree); + extern tree omp_find_clause (tree clauses, enum omp_clause_code kind); extern bool omp_is_allocatable_or_ptr (tree decl); extern tree omp_check_optional_argument (tree decl, bool for_present_check); @@ -166,15 +191,17 @@ extern poly_uint64 omp_max_vf (void); extern int omp_max_simt_vf (void); extern const char *omp_context_name_list_prop (tree); extern void omp_construct_traits_to_codes (tree, int, enum tree_code *); -extern tree omp_check_context_selector (location_t loc, tree ctx); +extern tree omp_check_context_selector (location_t loc, tree ctx, + bool metadirective_p); extern void omp_mark_declare_variant (location_t loc, tree variant, tree construct); -extern int omp_context_selector_matches (tree); +extern int omp_context_selector_matches (tree, bool, bool); extern int omp_context_selector_set_compare (enum omp_tss_code, tree, tree); extern tree omp_get_context_selector (tree, enum omp_tss_code, enum omp_ts_code); extern tree omp_get_context_selector_list (tree, enum omp_tss_code); extern tree omp_resolve_declare_variant (tree); +extern vec omp_early_resolve_metadirective (tree); extern tree oacc_launch_pack (unsigned code, tree device, unsigned op); extern tree oacc_replace_fn_attrib_attr (tree attribs, tree dims); extern void oacc_replace_fn_attrib (tree fn, tree dims); diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc index c935a7da7d1..3cdf74d5caf 100644 --- a/gcc/tree-pretty-print.cc +++ b/gcc/tree-pretty-print.cc @@ -1503,7 +1503,7 @@ dump_omp_clauses (pretty_printer *pp, tree clause, int spc, dump_flags_t flags, } /* Dump an OpenMP context selector CTX to PP. */ -static void +void dump_omp_context_selector (pretty_printer *pp, tree ctx, int spc, dump_flags_t flags) { @@ -4029,6 +4029,40 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags, is_expr = false; break; + case OMP_METADIRECTIVE: + { + pp_string (pp, "#pragma omp metadirective"); + newline_and_indent (pp, spc + 2); + pp_left_brace (pp); + + tree variant = OMP_METADIRECTIVE_VARIANTS (node); + while (variant != NULL_TREE) + { + tree selector = OMP_METADIRECTIVE_VARIANT_SELECTOR (variant); + tree directive = OMP_METADIRECTIVE_VARIANT_DIRECTIVE (variant); + tree body = OMP_METADIRECTIVE_VARIANT_BODY (variant); + + newline_and_indent (pp, spc + 4); + if (selector == NULL_TREE) + pp_string (pp, "default:"); + else + { + pp_string (pp, "when ("); + dump_omp_context_selector (pp, selector, spc + 4, flags); + pp_string (pp, "):"); + } + newline_and_indent (pp, spc + 6); + + dump_generic_node (pp, directive, spc + 6, flags, false); + newline_and_indent (pp, spc + 6); + dump_generic_node (pp, body, spc + 6, flags, false); + variant = TREE_CHAIN (variant); + } + newline_and_indent (pp, spc + 2); + pp_right_brace (pp); + } + break; + case TRANSACTION_EXPR: if (TRANSACTION_EXPR_OUTER (node)) pp_string (pp, "__transaction_atomic [[outer]]"); diff --git a/gcc/tree-pretty-print.h b/gcc/tree-pretty-print.h index 0da6242629b..ac6467935c1 100644 --- a/gcc/tree-pretty-print.h +++ b/gcc/tree-pretty-print.h @@ -45,6 +45,8 @@ extern void dump_omp_atomic_memory_order (pretty_printer *, enum omp_memory_order); extern void dump_omp_loop_non_rect_expr (pretty_printer *, tree, int, dump_flags_t); +extern void dump_omp_context_selector (pretty_printer *, tree, int, + dump_flags_t); extern void print_omp_context_selector (FILE *, tree, dump_flags_t); extern int dump_generic_node (pretty_printer *, tree, int, dump_flags_t, bool); extern void print_declaration (pretty_printer *, tree, int, dump_flags_t); diff --git a/gcc/tree.def b/gcc/tree.def index 24128e1e039..f909ae0a443 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -1340,6 +1340,12 @@ DEFTREECODE (OMP_TARGET_ENTER_DATA, "omp_target_enter_data", tcc_statement, 1) Operand 0: OMP_TARGET_EXIT_DATA_CLAUSES: List of clauses. */ DEFTREECODE (OMP_TARGET_EXIT_DATA, "omp_target_exit_data", tcc_statement, 1) +/* OpenMP - #pragma omp metadirective [variant1 ... variantN] + Operand 0: OMP_METADIRECTIVE_VARIANTS: List of selectors and directive + variants. Use the interface in omp-general.h to construct variants + and access their fields. */ +DEFTREECODE (OMP_METADIRECTIVE, "omp_metadirective", tcc_statement, 1) + /* OMP_ATOMIC through OMP_ATOMIC_CAPTURE_NEW must be consecutive, or OMP_ATOMIC_SEQ_CST needs adjusting. */ diff --git a/gcc/tree.h b/gcc/tree.h index ee2aae332a4..5e3f27fe6c6 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -1596,6 +1596,9 @@ class auto_suppress_location_wrappers #define OMP_TARGET_EXIT_DATA_CLAUSES(NODE)\ TREE_OPERAND (OMP_TARGET_EXIT_DATA_CHECK (NODE), 0) +#define OMP_METADIRECTIVE_VARIANTS(NODE) \ + TREE_OPERAND (OMP_METADIRECTIVE_CHECK (NODE), 0) + #define OMP_SCAN_BODY(NODE) TREE_OPERAND (OMP_SCAN_CHECK (NODE), 0) #define OMP_SCAN_CLAUSES(NODE) TREE_OPERAND (OMP_SCAN_CHECK (NODE), 1) From patchwork Sat May 4 21:21:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 1931420 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=PoaR1g8U; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX0yM4Xxfz1ybC for ; Sun, 5 May 2024 07:23:07 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DEC86384477E for ; Sat, 4 May 2024 21:23:05 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-il1-x135.google.com (mail-il1-x135.google.com [IPv6:2607:f8b0:4864:20::135]) by sourceware.org (Postfix) with ESMTPS id 520643858283 for ; Sat, 4 May 2024 21:22:06 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 520643858283 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 520643858283 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::135 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857733; cv=none; b=j2s42/0LUBOs+K3ufr/YOCmVDqtt3DJCZsba0VU5KyGW/U0b7+Z+N0f9KPBB+KZJN8z+XYQn3D6eepNfLiAp2rFyOPvH/9+fc0Pje/5g9FR+6ioXPk1iIlnS4x5laTFob2HPKbxHMAZ/HodDF1qcKpJAXm37D6XW9iXinw9BADE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857733; c=relaxed/simple; bh=OdJ3ytzWZQVZTp6sjCEvv3R18+GKGh5bX7CBojb6k8A=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=OOIwLsI1lRlmFHcDoaaGzvh7iQxX2wGl2uB9TkL/7AK2cSNX+RSvIwPYLX1EDZaDYCMjj5eR1QG79qUtASWEf8tmHqdCp000Nhd/cIZI2OqTcKwAyoJ8bdjNgnBOhASzbWY6EitXFonVXKBpBB8XWFEHtuFTuYLxnBLU+Nym7UI= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-il1-x135.google.com with SMTP id e9e14a558f8ab-36c809db200so3570555ab.3 for ; Sat, 04 May 2024 14:22:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857725; x=1715462525; 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=OCJwUWj8CALFEygVf6HXJaF8/N28/1BODPUBz9sseIg=; b=PoaR1g8UGdLdTy6ZO2R4r5kpHIjcPCWFhRsXOqrCItSVpz8cxT8YxnQZ+ILtuXKjme vSAnMmsMBZFZPVrHQgCsp52lBHJoIaTlQzY0S76ddbz2qeDsNMaYHqtj7HPKyB5x/49A dWlHq6LMi88pMWlJIsF6dOnkpi/RWVoDSN6Vz1fLrA9aFCInE6axOHES+kki229GVf3k Z9YCaYtCSHpBLEr9Q9o8/XIHSSZF6FXqo6/oMhLksLX62qF92t3EkSRDMVpbZnpwKBq1 s5S+ESkC2wfq1azxUP6EyUTWijEQc/syBzvx+5SzGVrTXWsOz/sqIsSyscAo7HJ/Xj6u ymHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857725; x=1715462525; 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=OCJwUWj8CALFEygVf6HXJaF8/N28/1BODPUBz9sseIg=; b=B4ji8V0DW+CUSERQv70LtF3rLei85b+X2qKL8trlY/QQK/RqAcfCXq4xmapA85nxWW OJ853BJL/Z2uSQ7+ari0N2MOlPZlQm4Vzn+lli+3KWRiUV5b+JiBC3G3xjW+9SfGZtFF ZJVh7s6IX0V3bBIHWbmAgJQZMifexYPJQgPpQK1yZKvVpnApezfcQern8WyytHUkySs1 bINY/OpT1J7TwPgrpYacI0EOAeeuGXCCRd9y1G34Aru1qX2TVajX1lt0mk/Pz9i4iFsn Np87NbNPkASpkHBuA2qGUDh3ziOQqGMMDkuqWzrbz3sRo7SgNvcurUI0RcmQujUcot5P ufLw== X-Gm-Message-State: AOJu0YyOYAPwv/duCzy4rXvI1AweyTeSGZOfkJaqeUbtKWzTvety0SOm /w/19ewX8Ojmcdf8vVBY5lCW92jiCnCtpfk+D5aapTfKNB8R7fuTz+oi8ZPgfCcuxE9nf3Br0Kw 7 X-Google-Smtp-Source: AGHT+IEoOrbN3KjsEEMD5n9wNJVwH+vQVnwcjKF0bKUi9REm0bd/Mnej3XNT77Oq/iYxlGMfYDhu8w== X-Received: by 2002:a05:6e02:20c3:b0:36c:f0b:4f54 with SMTP id 3-20020a056e0220c300b0036c0f0b4f54mr7774205ilq.21.1714857724170; Sat, 04 May 2024 14:22:04 -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.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:03 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 02/12] OpenMP: middle-end support for metadirectives Date: Sat, 4 May 2024 15:21:42 -0600 Message-Id: <20240504212153.3561429-3-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 X-Spam-Status: No, score=-10.5 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 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+incoming=patchwork.ozlabs.org@gcc.gnu.org This patch adds middle-end support for OpenMP metadirectives. Some context selectors can be resolved during gimplification, but others need to be deferred until the omp_device_lower pass, which requires that cgraph, LTO streaming, inlining, etc all know about this construct as well. gcc/ChangeLog * cgraph.h (struct cgraph_node): Add has_metadirectives flag. * cgraphclones.cc (cgraph_node::create_clone): Copy has_metadirectives flag. * doc/gimple.texi (Class hierarchy of GIMPLE statements): Document gomp_metadirective and gomp_variant. * gimple-low.cc (lower_omp_metadirective): New. (lower_stmt): Call it. * gimple-pretty-print.cc (dump_gimple_omp_metadirective): New. (pp_gimple_stmt_1): Call it. * gimple-streamer-in.cc (input_gimple_stmt): Handle GIMPLE_OMP_METADIRECTIVE. * gimple-streamer-out.cc (output_gimple_stmt): Likewise. * gimple-walk.cc (walk_gimple_op): Likewise. (walk_gimple_stmt): Likewise. * gimple.cc (gimple_alloc_omp_metadirective): New. (gimple_build_omp_metadirective): New. (gimple_build_omp_variant): New. * gimple.def (GIMPLE_OMP_METADIRECTIVE): New. (GIMPLE_OMP_METADIRECTIVE_VARIANT): New. * gimple.h (gomp_variant, gomp_metadirective): New. (is_a_helper ::test): New. (is_a_helper ::test): New. (is_a_helper ::test): New. (is_a_helper ::test): New. (gimple_alloc_omp_metadirective): New. (gimple_build_omp_metadirective): New. (gimple_build_omp_variant): New. (gimple_has_substatements): Handle GIMPLE_OMP_METADIRECTIVE. (gimple_has_ops): Likewise. (gimple_omp_metadirective_label): New. (gimple_omp_metadirective_set_label): New. (gimple_omp_variants): New. (gimple_omp_metadirective_set_variants): New. (gimple_return_set_retval): Handle GIMPLE_OMP_METADIRECTIVE. * gimplify.cc (is_gimple_stmt): HANDLE OMP_METADIRECTIVE. (expand_omp_metadirective): New. (gimplify_omp_metadirective): New. (gimplify_expr): Call it. * gsstruct.def (GSS_OMP_METADIRECTIVE): New. (GSS_OMP_METADIRECTIVE_VARIANT): New. * lto-cgraph.cc (lto_output_node): Handle has_metadirectives flag. (input_overwrite_node): Likewise. * omp-expand.cc (expand_omp_target): Propagate has_metadirectives flag. (build_omp_regions_1): Handle GIMPLE_OMP_METADIRECTIVE. (omp_make_gimple_edges): Likewise. * omp-general.cc (omp_late_resolve_metadirective): New. * omp-general.h (omp_late_resolve_metadirective): Declare. * omp-low.cc (struct omp_context): Add next_clone field. (new_omp_context): Handle next_clone field. (clone_omp_context): New. (delete_omp_context): Delete clones. (create_omp_child_function): Propagate has_metadirectives bit. (scan_omp_metadirective): New. (scan_omp_1_stmt): Handle GIMPLE_OMP_METADIRECTIVE. (lower_omp_metadirective): New. (lower_omp_1): Handle GIMPLE_OMP_METADIRECTIVE. Warn about direct calls to offloadable functions containing metadirectives. * omp-offload.cc: Include cfganal.h and cfghooks.h. (omp_expand_metadirective): New. (execute_omp_device_lower): Handle metadirectives. (pass_omp_device_lower::gate): Check has_metadirectives bit. * omp-simd-clone.cc (simd_clone_create): Propagate has_metadirectives flag. * tree-cfg.cc (cleanup_dead_labels): Handle GIMPLE_OMP_METADIRECTIVE. (gimple_redirect_edge_and_branch): Likewise. * tree-inline.cc (remap_gimple_stmt): Handle GIMPLE_OMP_METADIRECTIVE. (estimate_num_instructions): Likewise. (expand_call_inline): Propagate has_metadirectives flag. (tree_function_versioning): Likewise. * tree-ssa-operands.cc: Include omp-general.h. (operands_scanner::parse_ssa_operands): Handle GIMPLE_OMP_METADIRECTIVE. Co-Authored-By: Kwok Cheung Yeung Co-Authored-By: Sandra Loosemore Co-Authored-By: Marcel Vollweiler --- gcc/cgraph.h | 3 + gcc/cgraphclones.cc | 1 + gcc/doc/gimple.texi | 6 ++ gcc/gimple-low.cc | 36 ++++++++ gcc/gimple-pretty-print.cc | 64 +++++++++++++ gcc/gimple-streamer-in.cc | 10 ++ gcc/gimple-streamer-out.cc | 6 ++ gcc/gimple-walk.cc | 28 ++++++ gcc/gimple.cc | 35 +++++++ gcc/gimple.def | 7 ++ gcc/gimple.h | 100 +++++++++++++++++++- gcc/gimplify.cc | 184 +++++++++++++++++++++++++++++++++++++ gcc/gsstruct.def | 2 + gcc/lto-cgraph.cc | 2 + gcc/omp-expand.cc | 30 ++++++ gcc/omp-general.cc | 22 +++++ gcc/omp-general.h | 1 + gcc/omp-low.cc | 83 +++++++++++++++++ gcc/omp-offload.cc | 105 ++++++++++++++++++++- gcc/omp-simd-clone.cc | 1 + gcc/tree-cfg.cc | 24 +++++ gcc/tree-inline.cc | 39 ++++++++ gcc/tree-ssa-operands.cc | 17 ++++ 23 files changed, 804 insertions(+), 2 deletions(-) diff --git a/gcc/cgraph.h b/gcc/cgraph.h index a8c3224802c..6653ce19c3e 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -900,6 +900,7 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node ipcp_clone (false), declare_variant_alt (false), calls_declare_variant_alt (false), gc_candidate (false), called_by_ifunc_resolver (false), + has_metadirectives (false), m_uid (uid), m_summary_id (-1) {} @@ -1501,6 +1502,8 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node unsigned gc_candidate : 1; /* Set if the function is called by an IFUNC resolver. */ unsigned called_by_ifunc_resolver : 1; + /* True if the function contains unresolved metadirectives. */ + unsigned has_metadirectives : 1; private: /* Unique id of the node. */ diff --git a/gcc/cgraphclones.cc b/gcc/cgraphclones.cc index 4fff6873a36..e6312b5c0ab 100644 --- a/gcc/cgraphclones.cc +++ b/gcc/cgraphclones.cc @@ -389,6 +389,7 @@ cgraph_node::create_clone (tree new_decl, profile_count prof_count, prof_count = count.combine_with_ipa_count (prof_count); new_node->count = prof_count; new_node->calls_declare_variant_alt = this->calls_declare_variant_alt; + new_node->has_metadirectives = this->has_metadirectives; /* Update IPA profile. Local profiles need no updating in original. */ if (update_original) diff --git a/gcc/doc/gimple.texi b/gcc/doc/gimple.texi index 5f241b1c64f..3de82992394 100644 --- a/gcc/doc/gimple.texi +++ b/gcc/doc/gimple.texi @@ -310,6 +310,9 @@ kinds, along with their relationships to @code{GSS_} values (layouts) and + gimple_statement_with_ops_base | | (no GSS layout) | | + | + gomp_metadirective + | | code: GIMPLE_OMP_METADIRECTIVE + | | | + gimple_statement_with_ops | | | layout: GSS_WITH_OPS | | | @@ -358,6 +361,9 @@ kinds, along with their relationships to @code{GSS_} values (layouts) and | + gomp_for | | layout: GSS_OMP_FOR, code: GIMPLE_OMP_FOR | | + | + gomp_variant + | | code: GIMPLE_OMP_METADIRECTIVE_VARIANT + | | | + gomp_parallel_layout | | | layout: GSS_OMP_PARALLEL_LAYOUT | | | diff --git a/gcc/gimple-low.cc b/gcc/gimple-low.cc index e0371988705..2a8f1e0f7d0 100644 --- a/gcc/gimple-low.cc +++ b/gcc/gimple-low.cc @@ -229,6 +229,36 @@ lower_sequence (gimple_seq *seq, struct lower_data *data) lower_stmt (&gsi, data); } +/* Lower the OpenMP metadirective statement pointed by GSI. */ + +static void +lower_omp_metadirective (gimple_stmt_iterator *gsi, struct lower_data *data) +{ + gimple *stmt = gsi_stmt (*gsi); + gimple_seq variant_seq = gimple_omp_variants (stmt); + gimple_stmt_iterator variant_gsi = gsi_start (variant_seq); + unsigned i; + + /* The variants are not used after lowering. */ + gimple_omp_metadirective_set_variants (stmt, NULL); + + for (i = 0; i < gimple_num_ops (stmt); i++) + { + gimple *variant = gsi_stmt (variant_gsi); + tree label = create_artificial_label (UNKNOWN_LOCATION); + gimple_omp_metadirective_set_label (stmt, i, label); + gsi_insert_after (gsi, gimple_build_label (label), GSI_CONTINUE_LINKING); + + gimple_seq *directive_ptr = gimple_omp_body_ptr (variant); + lower_sequence (directive_ptr, data); + gsi_insert_seq_after (gsi, *directive_ptr, GSI_CONTINUE_LINKING); + + gsi_next (&variant_gsi); + } + + gsi_next (gsi); +} + /* Lower the OpenMP directive statement pointed by GSI. DATA is passed through the recursion. */ @@ -843,6 +873,12 @@ lower_stmt (gimple_stmt_iterator *gsi, struct lower_data *data) lower_assumption (gsi, data); return; + case GIMPLE_OMP_METADIRECTIVE: + data->cannot_fallthru = false; + lower_omp_metadirective (gsi, data); + data->cannot_fallthru = false; + return; + case GIMPLE_TRANSACTION: lower_sequence (gimple_transaction_body_ptr ( as_a (stmt)), diff --git a/gcc/gimple-pretty-print.cc b/gcc/gimple-pretty-print.cc index abda8871f97..ff22833c211 100644 --- a/gcc/gimple-pretty-print.cc +++ b/gcc/gimple-pretty-print.cc @@ -2075,6 +2075,64 @@ dump_gimple_assume (pretty_printer *buffer, const gimple *gs, } } +/* Dump a GIMPLE_OMP_METADIRECTIVE tuple on the pretty_printer BUFFER. */ + +static void +dump_gimple_omp_metadirective (pretty_printer *buffer, const gimple *gs, + int spc, dump_flags_t flags) +{ + if (flags & TDF_RAW) + dump_gimple_fmt (buffer, spc, flags, "%G <%+BODY <%S> >", gs, + gimple_omp_body (gs)); + else + { + pp_string (buffer, "#pragma omp metadirective"); + newline_and_indent (buffer, spc + 2); + + gimple_seq variant_seq = gimple_omp_variants (gs); + gimple_stmt_iterator gsi = gsi_start (variant_seq); + + for (unsigned i = 0; i < gimple_num_ops (gs); i++) + { + tree selector = gimple_op (gs, i); + + if (selector == NULL_TREE) + pp_string (buffer, "default:"); + else + { + pp_string (buffer, "when ("); + dump_omp_context_selector (buffer, selector, spc, flags); + pp_string (buffer, "):"); + } + + gimple *variant = gsi_stmt (gsi); + + if (variant != NULL) + { + newline_and_indent (buffer, spc + 4); + pp_left_brace (buffer); + pp_newline (buffer); + dump_gimple_seq (buffer, gimple_omp_body (variant), spc + 6, + flags); + newline_and_indent (buffer, spc + 4); + pp_right_brace (buffer); + + gsi_next (&gsi); + } + else + { + tree label = gimple_omp_metadirective_label (gs, i); + + pp_string (buffer, " "); + dump_generic_node (buffer, label, spc, flags, false); + } + + if (i != gimple_num_ops (gs) - 1) + newline_and_indent (buffer, spc + 2); + } + } +} + /* Dump a GIMPLE_TRANSACTION tuple on the pretty_printer BUFFER. */ static void @@ -2823,6 +2881,12 @@ pp_gimple_stmt_1 (pretty_printer *buffer, const gimple *gs, int spc, flags); break; + case GIMPLE_OMP_METADIRECTIVE: + dump_gimple_omp_metadirective (buffer, + as_a (gs), + spc, flags); + break; + case GIMPLE_CATCH: dump_gimple_catch (buffer, as_a (gs), spc, flags); break; diff --git a/gcc/gimple-streamer-in.cc b/gcc/gimple-streamer-in.cc index 61f6d069875..1482d34e9a8 100644 --- a/gcc/gimple-streamer-in.cc +++ b/gcc/gimple-streamer-in.cc @@ -151,6 +151,7 @@ input_gimple_stmt (class lto_input_block *ib, class data_in *data_in, case GIMPLE_COND: case GIMPLE_GOTO: case GIMPLE_DEBUG: + case GIMPLE_OMP_METADIRECTIVE: for (i = 0; i < num_ops; i++) { tree *opp, op = stream_read_tree (ib, data_in); @@ -188,6 +189,15 @@ input_gimple_stmt (class lto_input_block *ib, class data_in *data_in, else gimple_call_set_fntype (call_stmt, stream_read_tree (ib, data_in)); } + if (gomp_metadirective *metadirective_stmt + = dyn_cast (stmt)) + { + gimple_alloc_omp_metadirective (metadirective_stmt); + for (i = 0; i < num_ops; i++) + gimple_omp_metadirective_set_label (metadirective_stmt, i, + stream_read_tree (ib, + data_in)); + } break; case GIMPLE_NOP: diff --git a/gcc/gimple-streamer-out.cc b/gcc/gimple-streamer-out.cc index e63d8b4df0c..ccb11fec1da 100644 --- a/gcc/gimple-streamer-out.cc +++ b/gcc/gimple-streamer-out.cc @@ -127,6 +127,7 @@ output_gimple_stmt (struct output_block *ob, struct function *fn, gimple *stmt) case GIMPLE_COND: case GIMPLE_GOTO: case GIMPLE_DEBUG: + case GIMPLE_OMP_METADIRECTIVE: for (i = 0; i < gimple_num_ops (stmt); i++) { tree op = gimple_op (stmt, i); @@ -169,6 +170,11 @@ output_gimple_stmt (struct output_block *ob, struct function *fn, gimple *stmt) else stream_write_tree (ob, gimple_call_fntype (stmt), true); } + if (gimple_code (stmt) == GIMPLE_OMP_METADIRECTIVE) + for (i = 0; i < gimple_num_ops (stmt); i++) + stream_write_tree (ob, gimple_omp_metadirective_label (stmt, i), + true); + break; case GIMPLE_NOP: diff --git a/gcc/gimple-walk.cc b/gcc/gimple-walk.cc index 9f768ca20fd..1290c919116 100644 --- a/gcc/gimple-walk.cc +++ b/gcc/gimple-walk.cc @@ -501,6 +501,20 @@ walk_gimple_op (gimple *stmt, walk_tree_fn callback_op, return ret; break; + case GIMPLE_OMP_METADIRECTIVE: + { + gimple_seq variant_seq = gimple_omp_variants (stmt); + for (gimple_stmt_iterator gsi = gsi_start (variant_seq); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + ret = walk_gimple_op (gimple_omp_body (gsi_stmt (gsi)), + callback_op, wi); + if (ret) + return ret; + } + } + break; + case GIMPLE_TRANSACTION: { gtransaction *txn = as_a (stmt); @@ -717,6 +731,20 @@ walk_gimple_stmt (gimple_stmt_iterator *gsi, walk_stmt_fn callback_stmt, return wi->callback_result; break; + case GIMPLE_OMP_METADIRECTIVE: + { + gimple_seq variant_seq = gimple_omp_variants (stmt); + for (gimple_stmt_iterator gsi = gsi_start (variant_seq); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + ret = walk_gimple_seq_mod (gimple_omp_body_ptr (gsi_stmt (gsi)), + callback_stmt, callback_op, wi); + if (ret) + return wi->callback_result; + } + } + break; + case GIMPLE_WITH_CLEANUP_EXPR: ret = walk_gimple_seq_mod (gimple_wce_cleanup_ptr (stmt), callback_stmt, callback_op, wi); diff --git a/gcc/gimple.cc b/gcc/gimple.cc index a9f968cb038..303b1b029ec 100644 --- a/gcc/gimple.cc +++ b/gcc/gimple.cc @@ -1312,6 +1312,41 @@ gimple_build_assume (tree guard, gimple_seq body) return p; } +/* Allocate extra memory for a GIMPLE_OMP_METADIRECTIVE statement. */ + +void +gimple_alloc_omp_metadirective (gimple *g) +{ + gomp_metadirective *p = as_a (g); + + p->labels = ggc_cleared_vec_alloc (gimple_num_ops (p)); +} + +/* Build a GIMPLE_OMP_METADIRECTIVE statement. */ + +gomp_metadirective * +gimple_build_omp_metadirective (int num_variants) +{ + gomp_metadirective *p + = as_a (gimple_alloc (GIMPLE_OMP_METADIRECTIVE, + num_variants)); + gimple_alloc_omp_metadirective (p); + gimple_omp_metadirective_set_variants (p, NULL); + + return p; +} + +/* Build a GIMPLE_OMP_METADIRECTIVE_VARIANT statement. */ + +gomp_variant * +gimple_build_omp_variant (gimple_seq body) +{ + gomp_variant *variant = as_a + (gimple_alloc (GIMPLE_OMP_METADIRECTIVE_VARIANT, 0)); + gimple_omp_set_body (variant, body); + return variant; +} + /* Build a GIMPLE_TRANSACTION statement. */ gtransaction * diff --git a/gcc/gimple.def b/gcc/gimple.def index fbcd727f945..41e69d56bb4 100644 --- a/gcc/gimple.def +++ b/gcc/gimple.def @@ -398,6 +398,13 @@ DEFGSCODE(GIMPLE_OMP_TEAMS, "gimple_omp_teams", GSS_OMP_PARALLEL_LAYOUT) CLAUSES is an OMP_CLAUSE chain holding the associated clauses. */ DEFGSCODE(GIMPLE_OMP_ORDERED, "gimple_omp_ordered", GSS_OMP_SINGLE_LAYOUT) +/* GIMPLE_OMP_METADIRECTIVE represents #pragma omp metadirective. */ +DEFGSCODE(GIMPLE_OMP_METADIRECTIVE, "gimple_omp_metadirective", + GSS_OMP_METADIRECTIVE) + +DEFGSCODE(GIMPLE_OMP_METADIRECTIVE_VARIANT, + "gimple_omp_variant", GSS_OMP_METADIRECTIVE_VARIANT) + /* GIMPLE_PREDICT specifies a hint for branch prediction. PREDICT is one of the predictors from predict.def. diff --git a/gcc/gimple.h b/gcc/gimple.h index 8a8ca109bbf..b608ccd2ceb 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -840,6 +840,30 @@ struct GTY((tag("GSS_ASSUME"))) gimple_seq body; }; +struct GTY((tag("GSS_OMP_METADIRECTIVE_VARIANT"))) + gomp_variant : public gimple_statement_omp +{ + /* The body in the base class contains the directive for this variant. */ + + /* No extra fields; adds invariant: + stmt->code == GIMPLE_OMP_METADIRECTIVE_VARIANT. */}; + +struct GTY((tag("GSS_OMP_METADIRECTIVE"))) + gomp_metadirective : public gimple_statement_with_ops_base +{ + /* [ WORD 1-7 ] : base class */ + + /* [ WORD 8 ] : a list of bodies associated with the directive variants. */ + gomp_variant *variants; + + /* [ WORD 9 ] : label vector. */ + tree * GTY((length ("%h.num_ops"))) labels; + + /* [ WORD 10 ] : operand vector. Used to hold the selectors for the + directive variants. */ + tree GTY((length ("%h.num_ops"))) op[1]; +}; + /* GIMPLE_TRANSACTION. */ /* Bits to be stored in the GIMPLE_TRANSACTION subcode. */ @@ -1251,6 +1275,22 @@ is_a_helper ::test (gimple *gs) return gs->code == GIMPLE_OMP_TASK; } +template <> +template <> +inline bool +is_a_helper ::test (gimple *gs) +{ + return gs->code == GIMPLE_OMP_METADIRECTIVE; +} + +template <> +template <> +inline bool +is_a_helper ::test (gimple *gs) +{ + return gs->code == GIMPLE_OMP_METADIRECTIVE_VARIANT; +} + template <> template <> inline bool @@ -1501,6 +1541,22 @@ is_a_helper ::test (const gimple *gs) return gs->code == GIMPLE_OMP_TASK; } +template <> +template <> +inline bool +is_a_helper ::test (const gimple *gs) +{ + return gs->code == GIMPLE_OMP_METADIRECTIVE; +} + +template <> +template <> +inline bool +is_a_helper ::test (const gimple *gs) +{ + return gs->code == GIMPLE_OMP_METADIRECTIVE_VARIANT; +} + template <> template <> inline bool @@ -1609,6 +1665,9 @@ gomp_teams *gimple_build_omp_teams (gimple_seq, tree); gomp_atomic_load *gimple_build_omp_atomic_load (tree, tree, enum omp_memory_order); gomp_atomic_store *gimple_build_omp_atomic_store (tree, enum omp_memory_order); +void gimple_alloc_omp_metadirective (gimple *g); +gomp_metadirective *gimple_build_omp_metadirective (int num_variants); +gomp_variant *gimple_build_omp_variant (gimple_seq body); gimple *gimple_build_assume (tree, gimple_seq); gtransaction *gimple_build_transaction (gimple_seq); extern void gimple_seq_add_stmt (gimple_seq *, gimple *); @@ -1890,6 +1949,7 @@ gimple_has_substatements (gimple *g) case GIMPLE_OMP_TARGET: case GIMPLE_OMP_TEAMS: case GIMPLE_OMP_CRITICAL: + case GIMPLE_OMP_METADIRECTIVE: case GIMPLE_WITH_CLEANUP_EXPR: case GIMPLE_TRANSACTION: return true; @@ -2148,7 +2208,8 @@ gimple_init_singleton (gimple *g) inline bool gimple_has_ops (const gimple *g) { - return gimple_code (g) >= GIMPLE_COND && gimple_code (g) <= GIMPLE_RETURN; + return (gimple_code (g) >= GIMPLE_COND && gimple_code (g) <= GIMPLE_RETURN) + || gimple_code (g) == GIMPLE_OMP_METADIRECTIVE; } template <> @@ -6630,6 +6691,42 @@ gimple_assume_body (const gimple *gs) return assume_stmt->body; } + +static inline tree +gimple_omp_metadirective_label (const gimple *g, unsigned i) +{ + const gomp_metadirective *omp_metadirective + = as_a (g); + return omp_metadirective->labels[i]; +} + + +static inline void +gimple_omp_metadirective_set_label (gimple *g, unsigned i, tree label) +{ + gomp_metadirective *omp_metadirective = as_a (g); + omp_metadirective->labels[i] = label; +} + + +static inline gomp_variant * +gimple_omp_variants (const gimple *g) +{ + const gomp_metadirective *omp_metadirective + = as_a (g); + return omp_metadirective->variants; +} + + +static inline void +gimple_omp_metadirective_set_variants (gimple *g, gimple *variants) +{ + gomp_metadirective *omp_metadirective = as_a (g); + omp_metadirective->variants + = variants ? as_a (variants) : NULL; +} + + /* Return a pointer to the body for the GIMPLE_TRANSACTION statement TRANSACTION_STMT. */ @@ -6781,6 +6878,7 @@ gimple_return_set_retval (greturn *gs, tree retval) case GIMPLE_OMP_RETURN: \ case GIMPLE_OMP_ATOMIC_LOAD: \ case GIMPLE_OMP_ATOMIC_STORE: \ + case GIMPLE_OMP_METADIRECTIVE: \ case GIMPLE_OMP_CONTINUE inline bool diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc index 457b33a4293..ccbb5afbec7 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -6313,6 +6313,7 @@ is_gimple_stmt (tree t) case OMP_TASKGROUP: case OMP_ORDERED: case OMP_CRITICAL: + case OMP_METADIRECTIVE: case OMP_TASK: case OMP_TARGET: case OMP_TARGET_DATA: @@ -17611,6 +17612,184 @@ gimplify_omp_ordered (tree expr, gimple_seq body) return gimple_build_omp_ordered (body, OMP_ORDERED_CLAUSES (expr)); } +/* Replace a metadirective with the candidate directive variants in + CANDIDATES. */ + +static enum gimplify_status +expand_omp_metadirective (vec &candidates, + gimple_seq *pre_p) +{ + auto_vec selectors; + auto_vec directive_labels; + auto_vec directive_bodies; + tree body_label = NULL_TREE; + tree end_label = create_artificial_label (UNKNOWN_LOCATION); + + /* Construct bodies for each candidate. */ + for (unsigned i = 0; i < candidates.length(); i++) + { + struct omp_variant &candidate = candidates[i]; + gimple_seq body = NULL; + + selectors.safe_push (candidate.dynamic_selector); + directive_labels.safe_push (create_artificial_label (UNKNOWN_LOCATION)); + + gimplify_seq_add_stmt (&body, + gimple_build_label (directive_labels.last ())); + if (candidate.alternative != NULL_TREE) + gimplify_stmt (&candidate.alternative, &body); + if (candidate.body != NULL_TREE) + { + if (body_label != NULL_TREE) + gimplify_seq_add_stmt (&body, gimple_build_goto (body_label)); + else + { + body_label = create_artificial_label (UNKNOWN_LOCATION); + gimplify_seq_add_stmt (&body, gimple_build_label (body_label)); + gimplify_stmt (&candidate.body, &body); + } + } + + directive_bodies.safe_push (body); + } + + auto_vec cond_labels; + + cond_labels.safe_push (NULL_TREE); + for (unsigned i = 1; i < candidates.length () - 1; i++) + cond_labels.safe_push (create_artificial_label (UNKNOWN_LOCATION)); + if (candidates.length () > 1) + cond_labels.safe_push (directive_labels.last ()); + + /* Generate conditionals to test each dynamic selector in turn, executing + the directive candidate if successful. */ + for (unsigned i = 0; i < candidates.length () - 1; i++) + { + if (i != 0) + gimplify_seq_add_stmt (pre_p, gimple_build_label (cond_labels [i])); + + enum gimplify_status ret = gimplify_expr (&selectors[i], pre_p, NULL, + is_gimple_val, fb_rvalue); + if (ret == GS_ERROR || ret == GS_UNHANDLED) + return ret; + + gcond *cond_stmt + = gimple_build_cond_from_tree (selectors[i], directive_labels[i], + cond_labels[i + 1]); + + gimplify_seq_add_stmt (pre_p, cond_stmt); + gimplify_seq_add_seq (pre_p, directive_bodies[i]); + gimplify_seq_add_stmt (pre_p, gimple_build_goto (end_label)); + } + + gimplify_seq_add_seq (pre_p, directive_bodies.last ()); + gimplify_seq_add_stmt (pre_p, gimple_build_label (end_label)); + + return GS_ALL_DONE; +} + +/* Gimplify an OMP_METADIRECTIVE construct. EXPR is the tree version. + The metadirective will be resolved at this point if possible. */ + +static enum gimplify_status +gimplify_omp_metadirective (tree *expr_p, gimple_seq *pre_p, gimple_seq *, + bool (*) (tree), fallback_t) +{ + auto_vec selectors; + + /* Mark offloadable functions containing metadirectives that specify + a 'construct' selector with a 'target' constructor. */ + if (offloading_function_p (current_function_decl)) + { + for (tree variant = OMP_METADIRECTIVE_VARIANTS (*expr_p); + variant != NULL_TREE; variant = TREE_CHAIN (variant)) + { + tree selector = OMP_METADIRECTIVE_VARIANT_SELECTOR (variant); + + if (omp_get_context_selector (selector, OMP_TRAIT_SET_CONSTRUCT, + OMP_TRAIT_CONSTRUCT_TARGET)) + { + tree id = get_identifier ("omp metadirective construct target"); + + DECL_ATTRIBUTES (current_function_decl) + = tree_cons (id, NULL_TREE, + DECL_ATTRIBUTES (current_function_decl)); + break; + } + } + } + + /* Try to resolve the metadirective. */ + vec candidates + = omp_early_resolve_metadirective (*expr_p); + if (!candidates.is_empty ()) + return expand_omp_metadirective (candidates, pre_p); + + /* The metadirective cannot be resolved yet. */ + + gomp_variant *first_variant = NULL; + gomp_variant *prev_variant = NULL; + gimple_seq standalone_body = NULL; + tree body_label = NULL; + tree end_label = create_artificial_label (UNKNOWN_LOCATION); + + for (tree variant = OMP_METADIRECTIVE_VARIANTS (*expr_p); variant != NULL_TREE; + variant = TREE_CHAIN (variant)) + { + tree selector = OMP_METADIRECTIVE_VARIANT_SELECTOR (variant); + tree directive = OMP_METADIRECTIVE_VARIANT_DIRECTIVE (variant); + tree body = OMP_METADIRECTIVE_VARIANT_BODY (variant); + + selectors.safe_push (selector); + gomp_variant *omp_variant + = gimple_build_omp_variant (NULL); + gimple_seq *directive_p = gimple_omp_body_ptr (omp_variant); + + gimplify_stmt (&directive, directive_p); + if (body != NULL_TREE) + { + if (standalone_body == NULL) + { + gimplify_stmt (&body, &standalone_body); + body_label = create_artificial_label (UNKNOWN_LOCATION); + } + gimplify_seq_add_stmt (directive_p, gimple_build_goto (body_label)); + } + else + gimplify_seq_add_stmt (directive_p, gimple_build_goto (end_label)); + + if (!first_variant) + first_variant = omp_variant; + if (prev_variant) + { + prev_variant->next = omp_variant; + omp_variant->prev = prev_variant; + } + prev_variant = omp_variant; + } + + gomp_metadirective *stmt + = gimple_build_omp_metadirective (selectors.length ()); + gimple_omp_metadirective_set_variants (stmt, first_variant); + + tree selector; + unsigned int i; + FOR_EACH_VEC_ELT (selectors, i, selector) + gimple_set_op (stmt, i, selector); + + gimplify_seq_add_stmt (pre_p, stmt); + if (standalone_body) + { + gimplify_seq_add_stmt (pre_p, gimple_build_label (body_label)); + gimplify_seq_add_stmt (pre_p, standalone_body); + } + gimplify_seq_add_stmt (pre_p, gimple_build_label (end_label)); + + cgraph_node::get (cfun->decl)->has_metadirectives = 1; + + return GS_ALL_DONE; +} + /* Convert the GENERIC expression tree *EXPR_P to GIMPLE. If the expression produces a value to be used as an operand inside a GIMPLE statement, the value will be stored back in *EXPR_P. This value will @@ -18537,6 +18716,11 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, ret = gimplify_omp_atomic (expr_p, pre_p); break; + case OMP_METADIRECTIVE: + ret = gimplify_omp_metadirective (expr_p, pre_p, post_p, + gimple_test_f, fallback); + break; + case TRANSACTION_EXPR: ret = gimplify_transaction (expr_p, pre_p); break; diff --git a/gcc/gsstruct.def b/gcc/gsstruct.def index 91fef093f41..7708dc35fbf 100644 --- a/gcc/gsstruct.def +++ b/gcc/gsstruct.def @@ -51,4 +51,6 @@ DEFGSSTRUCT(GSS_OMP_CONTINUE, gomp_continue, false) DEFGSSTRUCT(GSS_OMP_ATOMIC_LOAD, gomp_atomic_load, false) DEFGSSTRUCT(GSS_OMP_ATOMIC_STORE_LAYOUT, gomp_atomic_store, false) DEFGSSTRUCT(GSS_ASSUME, gimple_statement_assume, false) +DEFGSSTRUCT(GSS_OMP_METADIRECTIVE, gomp_metadirective, true) +DEFGSSTRUCT(GSS_OMP_METADIRECTIVE_VARIANT, gomp_variant, false) DEFGSSTRUCT(GSS_TRANSACTION, gtransaction, false) diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc index 6395033ab9d..5bd9916fd2c 100644 --- a/gcc/lto-cgraph.cc +++ b/gcc/lto-cgraph.cc @@ -551,6 +551,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node, bp_pack_value (&bp, node->parallelized_function, 1); bp_pack_value (&bp, node->declare_variant_alt, 1); bp_pack_value (&bp, node->calls_declare_variant_alt, 1); + bp_pack_value (&bp, node->has_metadirectives, 1); /* Stream thunk info always because we use it in ipa_polymorphic_call_context::ipa_polymorphic_call_context @@ -1252,6 +1253,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data, node->parallelized_function = bp_unpack_value (bp, 1); node->declare_variant_alt = bp_unpack_value (bp, 1); node->calls_declare_variant_alt = bp_unpack_value (bp, 1); + node->has_metadirectives = bp_unpack_value (bp, 1); *has_thunk_info = bp_unpack_value (bp, 1); node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution, LDPR_NUM_KNOWN); diff --git a/gcc/omp-expand.cc b/gcc/omp-expand.cc index 24287826444..f44ba204123 100644 --- a/gcc/omp-expand.cc +++ b/gcc/omp-expand.cc @@ -10016,6 +10016,8 @@ expand_omp_target (struct omp_region *region) child_cfun->has_force_vectorize_loops |= cfun->has_force_vectorize_loops; cgraph_node *node = cgraph_node::get_create (child_fn); node->parallelized_function = 1; + node->has_metadirectives + |= cgraph_node::get (cfun->decl)->has_metadirectives; cgraph_node::add_new_function (child_fn, true); /* Add the new function to the offload table. */ @@ -10752,6 +10754,10 @@ build_omp_regions_1 (basic_block bb, struct omp_region *parent, /* GIMPLE_OMP_SECTIONS_SWITCH is part of GIMPLE_OMP_SECTIONS, and we do nothing for it. */ } + else if (code == GIMPLE_OMP_METADIRECTIVE) + { + /* Do nothing for metadirectives. */ + } else { region = new_omp_region (bb, code, parent); @@ -11137,6 +11143,30 @@ omp_make_gimple_edges (basic_block bb, struct omp_region **region, } break; + case GIMPLE_OMP_METADIRECTIVE: + /* Create an edge to the beginning of the body of each candidate + directive. */ + { + gimple *stmt = last_nondebug_stmt (bb); + unsigned i; + bool seen_default = false; + + for (i = 0; i < gimple_num_ops (stmt); i++) + { + tree dest = gimple_omp_metadirective_label (stmt, i); + basic_block dest_bb = label_to_block (cfun, dest); + make_edge (bb, dest_bb, 0); + + if (gimple_op (stmt, i) == NULL_TREE) + seen_default = true; + } + + gcc_assert (seen_default); + + fallthru = false; + } + break; + default: gcc_unreachable (); } diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc index e4c84d15644..6f36b5d163f 100644 --- a/gcc/omp-general.cc +++ b/gcc/omp-general.cc @@ -3187,6 +3187,28 @@ omp_early_resolve_metadirective (tree metadirective) return omp_get_dynamic_candidates (candidates, true); } +/* Return a vector of dynamic replacement candidates for the metadirective + Gimple statement in GS. Return an empty vector if the metadirective + cannot be resolved. */ + +vec +omp_late_resolve_metadirective (gimple *gs) +{ + auto_vec variants; + + for (unsigned i = 0; i < gimple_num_ops (gs); i++) + { + struct omp_variant variant; + + variant.selector = gimple_op (gs, i); + variant.alternative = gimple_omp_metadirective_label (gs, i); + + variants.safe_push (variant); + } + + return omp_get_dynamic_candidates (variants, false); +} + /* Encode an oacc launch argument. This matches the GOMP_LAUNCH_PACK macro on gomp-constants.h. We do not check for overflow. */ diff --git a/gcc/omp-general.h b/gcc/omp-general.h index 5807bc42cd7..b3e9efb93db 100644 --- a/gcc/omp-general.h +++ b/gcc/omp-general.h @@ -202,6 +202,7 @@ extern tree omp_get_context_selector (tree, enum omp_tss_code, extern tree omp_get_context_selector_list (tree, enum omp_tss_code); extern tree omp_resolve_declare_variant (tree); extern vec omp_early_resolve_metadirective (tree); +extern vec omp_late_resolve_metadirective (gimple *); extern tree oacc_launch_pack (unsigned code, tree device, unsigned op); extern tree oacc_replace_fn_attrib_attr (tree attribs, tree dims); extern void oacc_replace_fn_attrib (tree fn, tree dims); diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc index 4d003f42098..66915bdab4d 100644 --- a/gcc/omp-low.cc +++ b/gcc/omp-low.cc @@ -183,6 +183,10 @@ struct omp_context /* Candidates for adjusting OpenACC privatization level. */ vec oacc_privatization_candidates; + + /* Only used for omp metadirectives. Links to the next shallow + clone of this context. */ + struct omp_context *next_clone; }; static splay_tree all_contexts; @@ -974,6 +978,7 @@ new_omp_context (gimple *stmt, omp_context *outer_ctx) splay_tree_insert (all_contexts, (splay_tree_key) stmt, (splay_tree_value) ctx); ctx->stmt = stmt; + ctx->next_clone = NULL; if (outer_ctx) { @@ -1003,6 +1008,18 @@ new_omp_context (gimple *stmt, omp_context *outer_ctx) return ctx; } +static omp_context * +clone_omp_context (omp_context *ctx) +{ + omp_context *clone_ctx = XCNEW (omp_context); + + memcpy (clone_ctx, ctx, sizeof (omp_context)); + ctx->next_clone = clone_ctx; + clone_ctx->next_clone = NULL; + + return clone_ctx; +} + static gimple_seq maybe_catch_exception (gimple_seq); /* Finalize task copyfn. */ @@ -1049,6 +1066,15 @@ delete_omp_context (splay_tree_value value) { omp_context *ctx = (omp_context *) value; + /* Delete clones. */ + omp_context *clone = ctx->next_clone; + while (clone) + { + omp_context *next_clone = clone->next_clone; + XDELETE (clone); + clone = next_clone; + } + delete ctx->cb.decl_map; if (ctx->field_map) @@ -2093,6 +2119,9 @@ create_omp_child_function (omp_context *ctx, bool task_copy) DECL_FUNCTION_VERSIONED (decl) = DECL_FUNCTION_VERSIONED (current_function_decl); + if (cgraph_node::get (cfun->decl)->has_metadirectives) + cgraph_node::get_create (decl)->has_metadirectives = 1; + if (omp_maybe_offloaded_ctx (ctx)) { cgraph_node::get_create (decl)->offloadable = 1; @@ -3182,6 +3211,22 @@ scan_omp_teams (gomp_teams *stmt, omp_context *outer_ctx) ctx->record_type = ctx->receiver_decl = NULL; } +/* Scan an OpenMP metadirective. */ + +static void +scan_omp_metadirective (gomp_metadirective *stmt, omp_context *outer_ctx) +{ + gimple_seq variant_seq = gimple_omp_variants (stmt); + for (gimple_stmt_iterator gsi = gsi_start (variant_seq); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple_seq *directive_p = gimple_omp_body_ptr (gsi_stmt (gsi)); + omp_context *ctx = outer_ctx ? clone_omp_context (outer_ctx) : NULL; + + scan_omp (directive_p, ctx); + } +} + /* Check nesting restrictions. */ static bool check_omp_nesting_restrictions (gimple *stmt, omp_context *ctx) @@ -4245,6 +4290,10 @@ scan_omp_1_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p, scan_omp_teams (as_a (stmt), ctx); break; + case GIMPLE_OMP_METADIRECTIVE: + scan_omp_metadirective (as_a (stmt), ctx); + break; + case GIMPLE_BIND: { tree var; @@ -10702,6 +10751,19 @@ oacc_privatization_scan_decl_chain (omp_context *ctx, tree decls) } } +static void +lower_omp_metadirective (gimple_stmt_iterator *gsi_p, omp_context *ctx) +{ + gimple *stmt = gsi_stmt (*gsi_p); + gimple_seq variant_seq = gimple_omp_variants (stmt); + for (gimple_stmt_iterator gsi = gsi_start (variant_seq); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple_seq *directive_p = gimple_omp_body_ptr (gsi_stmt (gsi)); + lower_omp (directive_p, ctx); + } +} + /* Callback for walk_gimple_seq. Find #pragma omp scan statement. */ static tree @@ -14458,10 +14520,31 @@ lower_omp_1 (gimple_stmt_iterator *gsi_p, omp_context *ctx) else lower_omp_teams (gsi_p, ctx); break; + case GIMPLE_OMP_METADIRECTIVE: + lower_omp_metadirective (gsi_p, ctx); + break; case GIMPLE_CALL: tree fndecl; call_stmt = as_a (stmt); fndecl = gimple_call_fndecl (call_stmt); + if (fndecl + && lookup_attribute ("omp metadirective construct target", + DECL_ATTRIBUTES (fndecl))) + { + bool in_target_ctx = false; + + for (omp_context *up = ctx; up; up = up->outer) + if (gimple_code (up->stmt) == GIMPLE_OMP_TARGET) + { + in_target_ctx = true; + break; + } + if (!ctx || !in_target_ctx) + warning_at (gimple_location (stmt), 0, + "direct calls to an offloadable function containing " + "metadirectives with a % " + "selector may produce unexpected results"); + } if (fndecl && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)) switch (DECL_FUNCTION_CODE (fndecl)) diff --git a/gcc/omp-offload.cc b/gcc/omp-offload.cc index 35313c2ecf3..bbfc6beff87 100644 --- a/gcc/omp-offload.cc +++ b/gcc/omp-offload.cc @@ -55,6 +55,8 @@ along with GCC; see the file COPYING3. If not see #include "context.h" #include "convert.h" #include "opts.h" +#include "cfganal.h" +#include "cfghooks.h" /* Describe the OpenACC looping structure of a function. The entire function is held in a 'NULL' loop. */ @@ -1954,6 +1956,92 @@ is_sync_builtin_call (gcall *call) return false; } +/* Resolve an OpenMP metadirective in the function FUN, in the basic block + BB. The metadirective should be the last statement in BB. */ + +static void +omp_expand_metadirective (function *fun, basic_block bb) +{ + gimple *stmt = last_nondebug_stmt (bb); + vec candidates + = omp_late_resolve_metadirective (stmt); + + /* This is the last chance for the metadirective to be resolved. */ + gcc_assert (!candidates.is_empty ()); + + auto_vec labels; + + for (unsigned int i = 0; i < candidates.length (); i++) + labels.safe_push (candidates[i].alternative); + + /* Delete BBs for all variants not in the candidate list. */ + for (unsigned i = 0; i < gimple_num_ops (stmt); i++) + { + tree label = gimple_omp_metadirective_label (stmt, i); + if (!labels.contains (label)) + { + edge e = find_edge (bb, label_to_block (fun, label)); + remove_edge_and_dominated_blocks (e); + labels.safe_push (label); + } + } + + /* Remove the metadirective statement. */ + gimple_stmt_iterator gsi = gsi_last_bb (bb); + gsi_remove (&gsi, true); + + if (candidates.length () == 1) + { + /* Special case if there is only one selector - there should be one + remaining edge from BB to the selected variant. */ + edge e = find_edge (bb, label_to_block (fun, + candidates.last ().alternative)); + e->flags |= EDGE_FALLTHRU; + + return; + } + + basic_block cur_bb = bb; + + /* For each candidate, create a conditional that checks the dynamic + condition, branching to the candidate directive if true, to the + next candidate check if false. */ + for (unsigned i = 0; i < candidates.length () - 1; i++) + { + basic_block next_bb = NULL; + gcond *cond_stmt + = gimple_build_cond_from_tree (candidates[i].dynamic_selector, + NULL_TREE, NULL_TREE); + gsi = gsi_last_bb (cur_bb); + gsi_insert_seq_after (&gsi, cond_stmt, GSI_NEW_STMT); + + if (i < candidates.length () - 2) + { + edge e_false = split_block (cur_bb, cond_stmt); + e_false->flags &= ~EDGE_FALLTHRU; + e_false->flags |= EDGE_FALSE_VALUE; + e_false->probability = profile_probability::uninitialized (); + + next_bb = e_false->dest; + } + + /* Redirect the source of the edge from BB to the candidate directive + to the conditional. Reusing the edge avoids disturbing phi nodes in + the destination BB. */ + edge e = find_edge (bb, label_to_block (fun, candidates[i].alternative)); + redirect_edge_pred (e, cur_bb); + e->flags |= EDGE_TRUE_VALUE; + + if (next_bb) + cur_bb = next_bb; + } + + /* The last of the candidates is always static. */ + edge e = find_edge (cur_bb, label_to_block (fun, + candidates.last ().alternative)); + e->flags |= EDGE_FALSE_VALUE; +} + /* Main entry point for oacc transformations which run on the device compiler after LTO, so we know what the target device is at this point (including the host fallback). */ @@ -2632,6 +2720,7 @@ execute_omp_device_lower () gimple_stmt_iterator gsi; bool calls_declare_variant_alt = cgraph_node::get (cfun->decl)->calls_declare_variant_alt; + auto_vec metadirective_bbs; #ifdef ACCEL_COMPILER bool omp_redirect_indirect_calls = vec_safe_length (offload_ind_funcs) > 0; tree map_ptr_fn @@ -2641,6 +2730,8 @@ execute_omp_device_lower () for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { gimple *stmt = gsi_stmt (gsi); + if (is_a (stmt)) + metadirective_bbs.safe_push (bb); if (!is_gimple_call (stmt)) continue; if (!gimple_call_internal_p (stmt)) @@ -2790,6 +2881,16 @@ execute_omp_device_lower () } if (vf != 1) cfun->has_force_vectorize_loops = false; + if (!metadirective_bbs.is_empty ()) + { + calculate_dominance_info (CDI_DOMINATORS); + + for (unsigned i = 0; i < metadirective_bbs.length (); i++) + omp_expand_metadirective (cfun, metadirective_bbs[i]); + + free_dominance_info (cfun, CDI_DOMINATORS); + mark_virtual_operands_for_renaming (cfun); + } return 0; } @@ -2818,6 +2919,7 @@ public: /* opt_pass methods: */ bool gate (function *fun) final override { + cgraph_node *node = cgraph_node::get (fun->decl); #ifdef ACCEL_COMPILER bool offload_ind_funcs_p = vec_safe_length (offload_ind_funcs) > 0; #else @@ -2825,7 +2927,8 @@ public: #endif return (!(fun->curr_properties & PROP_gimple_lomp_dev) || (flag_openmp - && (cgraph_node::get (fun->decl)->calls_declare_variant_alt + && (node->calls_declare_variant_alt + || node->has_metadirectives || offload_ind_funcs_p))); } unsigned int execute (function *) final override diff --git a/gcc/omp-simd-clone.cc b/gcc/omp-simd-clone.cc index 864586207ee..fa80b6b3bb9 100644 --- a/gcc/omp-simd-clone.cc +++ b/gcc/omp-simd-clone.cc @@ -690,6 +690,7 @@ simd_clone_create (struct cgraph_node *old_node, bool force_local) new_node->externally_visible = old_node->externally_visible; new_node->calls_declare_variant_alt = old_node->calls_declare_variant_alt; + new_node->has_metadirectives = old_node->has_metadirectives; } /* Mark clones with internal linkage as gc'able, so they will not be diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc index b1ba33018fd..90194f057db 100644 --- a/gcc/tree-cfg.cc +++ b/gcc/tree-cfg.cc @@ -1752,6 +1752,18 @@ cleanup_dead_labels (void) } break; + case GIMPLE_OMP_METADIRECTIVE: + { + for (unsigned i = 0; i < gimple_num_ops (stmt); i++) + { + label = gimple_omp_metadirective_label (stmt, i); + new_label = main_block_label (label, label_for_bb); + if (new_label != label) + gimple_omp_metadirective_set_label (stmt, i, new_label); + } + } + break; + default: break; } @@ -6315,6 +6327,18 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest) gimple_block_label (dest)); break; + case GIMPLE_OMP_METADIRECTIVE: + { + for (unsigned i = 0; i < gimple_num_ops (stmt); i++) + { + tree label = gimple_omp_metadirective_label (stmt, i); + if (label_to_block (cfun, label) == e->dest) + gimple_omp_metadirective_set_label (stmt, i, + gimple_block_label (dest)); + } + } + break; + default: /* Otherwise it must be a fallthru edge, and we don't need to do anything besides redirecting it. */ diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc index 238afb7de80..c34d2ce1592 100644 --- a/gcc/tree-inline.cc +++ b/gcc/tree-inline.cc @@ -1672,6 +1672,36 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id) (s1, gimple_omp_masked_clauses (stmt)); break; + case GIMPLE_OMP_METADIRECTIVE: + copy = gimple_build_omp_metadirective (gimple_num_ops (stmt)); + { + gimple *first_variant = NULL; + gimple **prev_next = &first_variant; + gimple_seq variant_seq = gimple_omp_variants (stmt); + for (gimple_stmt_iterator gsi = gsi_start (variant_seq); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + s1 = remap_gimple_seq (gimple_omp_body (gsi_stmt (gsi)), id); + gimple *new_variant + = gimple_build_omp_variant (s1); + + *prev_next = new_variant; + prev_next = &new_variant->next; + } + gimple_omp_metadirective_set_variants (copy, first_variant); + } + + memset (&wi, 0, sizeof (wi)); + wi.info = id; + for (unsigned i = 0; i < gimple_num_ops (stmt); i++) + { + tree label = gimple_omp_metadirective_label (stmt, i); + walk_tree (&label, remap_gimple_op_r, &wi, NULL); + gimple_omp_metadirective_set_label (copy, i, label); + gimple_set_op (copy, i, gimple_op (stmt, i)); + } + break; + case GIMPLE_OMP_SCOPE: s1 = remap_gimple_seq (gimple_omp_body (stmt), id); copy = gimple_build_omp_scope @@ -4607,6 +4637,13 @@ estimate_num_insns (gimple *stmt, eni_weights *weights) return (weights->omp_cost + estimate_num_insns_seq (gimple_omp_body (stmt), weights)); + case GIMPLE_OMP_METADIRECTIVE: + /* The actual instruction will disappear eventually, so metadirective + statements have zero additional cost (if only static selectors + are used). */ + /* TODO: Estimate the cost of evaluating dynamic selectors */ + return 0; + case GIMPLE_TRANSACTION: return (weights->tm_cost + estimate_num_insns_seq (gimple_transaction_body ( @@ -5021,6 +5058,7 @@ expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id, dst_cfun->calls_eh_return |= id->src_cfun->calls_eh_return; id->dst_node->calls_declare_variant_alt |= id->src_node->calls_declare_variant_alt; + id->dst_node->has_metadirectives |= id->src_node->has_metadirectives; gcc_assert (!id->src_cfun->after_inlining); @@ -6276,6 +6314,7 @@ tree_function_versioning (tree old_decl, tree new_decl, new_entry ? new_entry->count : old_entry_block->count); new_version_node->calls_declare_variant_alt = old_version_node->calls_declare_variant_alt; + new_version_node->has_metadirectives = old_version_node->has_metadirectives; if (DECL_STRUCT_FUNCTION (new_decl)->gimple_df) DECL_STRUCT_FUNCTION (new_decl)->gimple_df->ipa_pta = id.src_cfun->gimple_df->ipa_pta; diff --git a/gcc/tree-ssa-operands.cc b/gcc/tree-ssa-operands.cc index 1dbf6b9c7c4..30d8d209742 100644 --- a/gcc/tree-ssa-operands.cc +++ b/gcc/tree-ssa-operands.cc @@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple-pretty-print.h" #include "diagnostic-core.h" #include "stmt.h" +#include "omp-general.h" #include "print-tree.h" #include "dumpfile.h" @@ -972,6 +973,22 @@ operands_scanner::parse_ssa_operands () append_vuse (gimple_vop (fn)); goto do_default; + case GIMPLE_OMP_METADIRECTIVE: + n = gimple_num_ops (stmt); + for (i = start; i < n; i++) + for (tree tss = gimple_op (stmt, i); + tss != NULL; tss = TREE_CHAIN (tss)) + if (OMP_TSS_CODE (tss) == OMP_TRAIT_SET_USER + || OMP_TSS_CODE (tss) == OMP_TRAIT_SET_TARGET_DEVICE) + for (tree ts = OMP_TSS_TRAIT_SELECTORS (tss); + ts != NULL; ts = TREE_CHAIN (ts)) + if (OMP_TS_CODE (ts) == OMP_TRAIT_USER_CONDITION + || OMP_TS_CODE (ts) == OMP_TRAIT_DEVICE_NUM) + for (tree tp = OMP_TS_PROPERTIES (ts); + tp != NULL; tp = TREE_CHAIN (tp)) + get_expr_operands (&OMP_TP_VALUE (tp), opf_use); + break; + case GIMPLE_CALL: /* Add call-clobbered operands, if needed. */ maybe_add_call_vops (as_a (stmt)); From patchwork Sat May 4 21:21:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 1931421 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=fqPjiiyQ; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX0yN6yVZz1ybC for ; Sun, 5 May 2024 07:23:08 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 24E8138449C7 for ; Sat, 4 May 2024 21:23:07 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-io1-xd34.google.com (mail-io1-xd34.google.com [IPv6:2607:f8b0:4864:20::d34]) by sourceware.org (Postfix) with ESMTPS id 695273849ACE for ; Sat, 4 May 2024 21:22:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 695273849ACE 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 695273849ACE Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::d34 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857733; cv=none; b=wSmpvXSVcsQuzdL8AeYlFd/r+BJBlvd11ZwzwDR5RnywvDTZC2uq89GJJUkQCfM08ki5Zuh8fKGTqAB2WZ69hCOmyBSJh+HSjJCmPC3jNJ1PTPAoRPJN1wEYL/q4pOBYVD5mfPMA11u9P0zFf8RlelynMFGraoHAWlgLrj5JyHU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857733; c=relaxed/simple; bh=ndsnfJQqXGERZCCt+rY2YN95ayqiWgtsNW+eHnJHa5k=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=JxbN1oMH9pH4BcxP+v3eJu50iV7B3osdn0QuI8a2P08GtTbT7wfJhfQbKXSg0uyjj9SE9imCtqo2wsIlBhXBuQQsvBFOzBWhrADojI1so16b78J/OCH5TpBNH+dFchfGfKDOB7Ps8Qpwmis5XQ1+6C7z6ZidNt89LeZs0DCnKMY= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-io1-xd34.google.com with SMTP id ca18e2360f4ac-7da3ec3e044so38859839f.2 for ; Sat, 04 May 2024 14:22:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857727; x=1715462527; 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=yWsgxQ5ekIpybCtEgKPYt/gh2YFEIAwsqKnJtLolOkk=; b=fqPjiiyQnSEJL+wt6WJIkDPge+Osq43Udsp8ESJvhEdgkaEk9ySi2+N5r6VuucZbU5 ThKoDpHu9TmrBMRRM3C2i//LMqeWE3HRz4Pa6z5AFZsp6iZEjaKryxGSzVGm2BXQqboQ 1lo2H61dVdRskdKy6OL66+uRLYMsUeEo6yPOU0Om0bqJbPmeLoq1+PRupjOu9cBYMro/ pyabcXA9wpetRdUK4WHv/BUz0j+Mq/qJ9LU8S/LwCybanlK7xHBI6Pqm+rXleq/MbY/r 152rkaUWvl8AoZa3INAQMP7jUqdWuvsQe3ZbXK4eBs3//ixwApUQ5Ct+95lxxeJiEW5J qNvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857727; x=1715462527; 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=yWsgxQ5ekIpybCtEgKPYt/gh2YFEIAwsqKnJtLolOkk=; b=NaeuRkq+BDaSg7uiRc9Vjdd42tMCepo1vBhBTjO8/FX0UWF4g8a8qNuLdMTmDsWp7I wyE5ME00BsVccTWlI5YL0lHRKPC1zq5iEloEXmnT6Qrc/w1BacobJGFVOF5HvIZNHOoN +XrwTmBHVjmEmBUzdIY589SbV4PLJ+5UW7DFtacQBCzabGjMrZ4haka5fnETQlrtHvp4 AEYdod3dccBtCTfs7CnX/Bn6Pj5L9VaP8PzXE16U4SMh55J3O64Fg9cvh/s/l4VJ+iEp DBA8J9Rj7udaPVM3UnwY8uqruDBzqmNdHSOV1gKn0sBW7FmgLWCyT8jU+p68cv9Uv6GK cGxQ== X-Gm-Message-State: AOJu0Yx5FhR/R8/IEiT8dZOheky8r09pVNz5nKhbX9nY7md9i3nQHAsK p6toyH6xB+VKilKSGHGnJ1rVPPYdIWYdMZMgVWNIxSFcd5z3FuSLtpnbIlsPMettAdL5HO8klCQ 0 X-Google-Smtp-Source: AGHT+IHzLBYU51AHq7PY8S3iZzjGn39mGX33Wu1r42udTjdBX45AJzA3H3Q498zrzJuBX70DBREAwA== X-Received: by 2002:a5d:9915:0:b0:7de:e497:ac7b with SMTP id x21-20020a5d9915000000b007dee497ac7bmr7316382iol.0.1714857725963; Sat, 04 May 2024 14:22:05 -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.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:05 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 03/12] libgomp: runtime support for target_device selector Date: Sat, 4 May 2024 15:21:43 -0600 Message-Id: <20240504212153.3561429-4-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 X-Spam-Status: No, score=-10.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, KAM_SHORT, 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 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+incoming=patchwork.ozlabs.org@gcc.gnu.org This patch implements the libgomp runtime support for the dynamic target_device selector via the GOMP_evaluate_target_device function. include/ChangeLog * cuda/cuda.h (CUdevice_attribute): Add definitions for CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR and CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR. libgomp/ChangeLog * Makefile.am (libgomp_la_SOURCES): Add selector.c. * Makefile.in: Regenerate. * config/gcn/selector.c: New. * config/linux/selector.c: New. * config/linux/x86/selector.c: New. * config/nvptx/selector.c: New. * libgomp-plugin.h (GOMP_OFFLOAD_evaluate_device): New. * libgomp.h (struct gomp_device_descr): Add evaluate_device_func field. * libgomp.map (GOMP_5.1.3): New, add GOMP_evaluate_target_device. * libgomp.texi (OpenMP Context Selectors): Document dynamic selector matching of kind/arch/isa. * libgomp_g.h (GOMP_evaluate_current_device): New. (GOMP_evaluate_target_device): New. * oacc-host.c (host_evaluate_device): New. (host_openacc_exec): Initialize evaluate_device_func field to host_evaluate_device. * plugin/plugin-gcn.c (gomp_match_selectors): New. (gomp_match_isa): New. (GOMP_OFFLOAD_evaluate_device): New. * plugin/plugin-nvptx.c (struct ptx_device): Add compute_major and compute_minor fields. (nvptx_open_device): Read compute capability information from device. (gomp_match_selectors): New. (gomp_match_selector): New. (CHECK_ISA): New macro. (GOMP_OFFLOAD_evaluate_device): New. * selector.c: New. * target.c (GOMP_evaluate_target_device): New. (gomp_load_plugin_for_device): Load evaluate_device plugin function. Co-Authored-By: Kwok Cheung Yeung Co-Authored-By: Sandra Loosemore --- include/cuda/cuda.h | 2 + libgomp/Makefile.am | 2 +- libgomp/Makefile.in | 5 +- libgomp/config/gcn/selector.c | 102 +++++++ libgomp/config/linux/selector.c | 65 +++++ libgomp/config/linux/x86/selector.c | 406 ++++++++++++++++++++++++++++ libgomp/config/nvptx/selector.c | 77 ++++++ libgomp/libgomp-plugin.h | 2 + libgomp/libgomp.h | 1 + libgomp/libgomp.map | 5 + libgomp/libgomp.texi | 18 +- libgomp/libgomp_g.h | 8 + libgomp/oacc-host.c | 11 + libgomp/plugin/plugin-gcn.c | 52 ++++ libgomp/plugin/plugin-nvptx.c | 82 ++++++ libgomp/selector.c | 64 +++++ libgomp/target.c | 40 +++ 17 files changed, 936 insertions(+), 6 deletions(-) create mode 100644 libgomp/config/gcn/selector.c create mode 100644 libgomp/config/linux/selector.c create mode 100644 libgomp/config/linux/x86/selector.c create mode 100644 libgomp/config/nvptx/selector.c create mode 100644 libgomp/selector.c diff --git a/include/cuda/cuda.h b/include/cuda/cuda.h index 0dca4b3a5c0..a775450df03 100644 --- a/include/cuda/cuda.h +++ b/include/cuda/cuda.h @@ -83,6 +83,8 @@ typedef enum { CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_MULTIPROCESSOR = 39, CU_DEVICE_ATTRIBUTE_ASYNC_ENGINE_COUNT = 40, CU_DEVICE_ATTRIBUTE_UNIFIED_ADDRESSING = 41, + CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR = 75, + CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR = 76, CU_DEVICE_ATTRIBUTE_MAX_REGISTERS_PER_MULTIPROCESSOR = 82 } CUdevice_attribute; diff --git a/libgomp/Makefile.am b/libgomp/Makefile.am index 1871590596d..87658da2d5d 100644 --- a/libgomp/Makefile.am +++ b/libgomp/Makefile.am @@ -72,7 +72,7 @@ libgomp_la_SOURCES = alloc.c atomic.c barrier.c critical.c env.c error.c \ target.c splay-tree.c libgomp-plugin.c oacc-parallel.c oacc-host.c \ oacc-init.c oacc-mem.c oacc-async.c oacc-plugin.c oacc-cuda.c \ priority_queue.c affinity-fmt.c teams.c allocator.c oacc-profiling.c \ - oacc-target.c target-indirect.c + oacc-target.c target-indirect.c selector.c include $(top_srcdir)/plugin/Makefrag.am diff --git a/libgomp/Makefile.in b/libgomp/Makefile.in index 11480d6a953..30e57571404 100644 --- a/libgomp/Makefile.in +++ b/libgomp/Makefile.in @@ -219,7 +219,7 @@ am_libgomp_la_OBJECTS = alloc.lo atomic.lo barrier.lo critical.lo \ oacc-parallel.lo oacc-host.lo oacc-init.lo oacc-mem.lo \ oacc-async.lo oacc-plugin.lo oacc-cuda.lo priority_queue.lo \ affinity-fmt.lo teams.lo allocator.lo oacc-profiling.lo \ - oacc-target.lo target-indirect.lo $(am__objects_1) + oacc-target.lo target-indirect.lo selector.lo $(am__objects_1) libgomp_la_OBJECTS = $(am_libgomp_la_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -552,7 +552,7 @@ libgomp_la_SOURCES = alloc.c atomic.c barrier.c critical.c env.c \ oacc-parallel.c oacc-host.c oacc-init.c oacc-mem.c \ oacc-async.c oacc-plugin.c oacc-cuda.c priority_queue.c \ affinity-fmt.c teams.c allocator.c oacc-profiling.c \ - oacc-target.c target-indirect.c $(am__append_3) + oacc-target.c target-indirect.c selector.c $(am__append_3) # Nvidia PTX OpenACC plugin. @PLUGIN_NVPTX_TRUE@libgomp_plugin_nvptx_version_info = -version-info $(libtool_VERSION) @@ -777,6 +777,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptrlock.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scope.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sections.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/selector.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sem.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/single.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/splay-tree.Plo@am__quote@ diff --git a/libgomp/config/gcn/selector.c b/libgomp/config/gcn/selector.c new file mode 100644 index 00000000000..7e099a00b97 --- /dev/null +++ b/libgomp/config/gcn/selector.c @@ -0,0 +1,102 @@ +/* Copyright (C) 2022 Free Software Foundation, Inc. + Contributed by Mentor, a Siemens Business. + + This file is part of the GNU Offloading and Multi Processing Library + (libgomp). + + Libgomp is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . */ + +/* This file contains an implementation of GOMP_evaluate_current_device for + an AMD GCN GPU. */ + +#include "libgomp.h" +#include + +/* The selectors are passed as strings, but are actually sets of multiple + trait property names, separated by '\0' and with an extra '\0' at + the end. Match such a string SELECTORS against an array of strings + CHOICES, that is terminated by a null pointer. + matches. */ +static bool +gomp_match_selectors (const char *selectors, const char **choices) +{ + while (*selectors != '\0') + { + bool match = false; + for (int i = 0; !match && choices[i]; i++) + match = !strcmp (selectors, choices[i]); + if (!match) + return false; + selectors += strlen (selectors) + 1; + } + return true; +} + +bool +GOMP_evaluate_current_device (const char *kind, const char *arch, + const char *isa) +{ + static const char *kind_choices[] = { "gpu", "nohost", NULL }; + static const char *arch_choices[] = { "gcn", "amdgcn", NULL }; + static const char *isa_choices[] + = { +#ifdef __fiji__ + "fiji", "gfx803", +#endif +#ifdef __gfx900__ + "gfx900", +#endif +#ifdef __gfx906__ + "gfx906", +#endif +#ifdef __gfx908__ + "gfx908", +#endif +#ifdef __gfx90a__ + "gfx90a", +#endif +#ifdef __gfx90c__ + "gfx90c", +#endif +#ifdef __gfx1030__ + "gfx1030", +#endif +#ifdef __gfx1036__ + "gfx1036", +#endif +#ifdef __gfx1100__ + "gfx1100", +#endif +#ifdef __gfx1103__ + "gfx1103", +#endif + NULL }; + + if (kind && !gomp_match_selectors (kind, kind_choices)) + return false; + + if (arch && !gomp_match_selectors (arch, arch_choices)) + return false; + + if (isa && !gomp_match_selectors (isa, isa_choices)) + return false; + + return true; +} diff --git a/libgomp/config/linux/selector.c b/libgomp/config/linux/selector.c new file mode 100644 index 00000000000..064cb937ecc --- /dev/null +++ b/libgomp/config/linux/selector.c @@ -0,0 +1,65 @@ +/* Copyright (C) 2022 Free Software Foundation, Inc. + Contributed by Mentor, a Siemens Business. + + This file is part of the GNU Offloading and Multi Processing Library + (libgomp). + + Libgomp is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . */ + +/* This file contains a generic implementation of + GOMP_evaluate_current_device when run on a Linux host. */ + +#include +#include "libgomp.h" + +/* The selectors are passed as strings, but are actually sets of multiple + trait property names, separated by '\0' and with an extra '\0' at + the end. Match such a string SELECTORS against an array of strings + CHOICES, that is terminated by a null pointer. + matches. */ +static bool +gomp_match_selectors (const char *selectors, const char **choices) +{ + while (*selectors != '\0') + { + bool match = false; + for (int i = 0; !match && choices[i]; i++) + match = !strcmp (selectors, choices[i]); + if (!match) + return false; + selectors += strlen (selectors) + 1; + } + return true; +} + +bool +GOMP_evaluate_current_device (const char *kind, const char *arch, + const char *isa) +{ + static const char *kind_choices[] = { "cpu", "host", NULL }; + + if (kind && !gomp_match_selectors (kind, kind_choices)) + return false; + + if (arch || isa) + return false; + + return true; +} diff --git a/libgomp/config/linux/x86/selector.c b/libgomp/config/linux/x86/selector.c new file mode 100644 index 00000000000..13cd2e14389 --- /dev/null +++ b/libgomp/config/linux/x86/selector.c @@ -0,0 +1,406 @@ +/* Copyright (C) 2022 Free Software Foundation, Inc. + Contributed by Mentor, a Siemens Business. + + This file is part of the GNU Offloading and Multi Processing Library + (libgomp). + + Libgomp is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . */ + +/* This file contains an implementation of GOMP_evaluate_current_device for + an x86/x64-based Linux host. */ + +#include +#include "libgomp.h" + +/* The selectors are passed as strings, but are actually sets of multiple + trait property names, separated by '\0' and with an extra '\0' at + the end. Match such a string SELECTORS against an array of strings + CHOICES, that is terminated by a null pointer. + matches. */ +static bool +gomp_match_selectors (const char *selectors, const char **choices) +{ + while (*selectors != '\0') + { + bool match = false; + for (int i = 0; !match && choices[i]; i++) + match = !strcmp (selectors, choices[i]); + if (!match) + return false; + selectors += strlen (selectors) + 1; + } + return true; +} + +bool +GOMP_evaluate_current_device (const char *kind, const char *arch, + const char *isa) +{ + static const char *kind_choices[] = { "cpu", "host", NULL }; + + static const char *arch_choices[] + = { "x86", + "ia32", +#ifdef __x86_64__ + "x86_64", +#endif +#ifdef __ILP32__ + "x32", +#endif + "i386", +#ifdef __i486__ + "i486", +#endif +#ifdef __i586__ + "i586", +#endif +#ifdef __i686__ + "i686", +#endif + NULL }; + + static const char *isa_choices[] + = { +#ifdef __WBNOINVD__ + "wbnoinvd", +#endif +#ifdef __AVX512VP2INTERSECT__ + "avx512vp2intersect", +#endif +#ifdef __MMX__ + "mmx", +#endif +#ifdef __3dNOW__ + "3dnow", +#endif +#ifdef __3dNOW_A__ + "3dnowa", +#endif +#ifdef __SSE__ + "sse", +#endif +#ifdef __SSE2__ + "sse2", +#endif +#ifdef __SSE3__ + "sse3", +#endif +#ifdef __SSSE3__ + "ssse3", +#endif +#ifdef __SSE4_1__ + "sse4.1", +#endif +#ifdef __SSE4_2__ + "sse4", + "sse4.2", +#endif +#ifdef __AES__ + "aes", +#endif +#ifdef __SHA__ + "sha", +#endif +#ifdef __PCLMUL__ + "pclmul", +#endif +#ifdef __AVX__ + "avx", +#endif +#ifdef __AVX2__ + "avx2", +#endif +#ifdef __AVX512F__ + "avx512f", +#endif +#ifdef __AVX512ER__ + "avx512er", +#endif +#ifdef __AVX512CD__ + "avx512cd", +#endif +#ifdef __AVX512PF__ + "avx512pf", +#endif +#ifdef __AVX512DQ__ + "avx512dq", +#endif +#ifdef __AVX512BW__ + "avx512bw", +#endif +#ifdef __AVX512VL__ + "avx512vl", +#endif +#ifdef __AVX512VBMI__ + "avx512vbmi", +#endif +#ifdef __AVX512IFMA__ + "avx512ifma", +#endif +#ifdef __AVX5124VNNIW__ + "avx5124vnniw", +#endif +#ifdef __AVX512VBMI2__ + "avx512vbmi2", +#endif +#ifdef __AVX512VNNI__ + "avx512vnni", +#endif +#ifdef __PCONFIG__ + "pconfig", +#endif +#ifdef __SGX__ + "sgx", +#endif +#ifdef __AVX5124FMAPS__ + "avx5124fmaps", +#endif +#ifdef __AVX512BITALG__ + "avx512bitalg", +#endif +#ifdef __AVX512VPOPCNTDQ__ + "avx512vpopcntdq", +#endif +#ifdef __FMA__ + "fma", +#endif +#ifdef __RTM__ + "rtm", +#endif +#ifdef __SSE4A__ + "sse4a", +#endif +#ifdef __FMA4__ + "fma4", +#endif +#ifdef __XOP__ + "xop", +#endif +#ifdef __LWP__ + "lwp", +#endif +#ifdef __ABM__ + "abm", +#endif +#ifdef __BMI__ + "bmi", +#endif +#ifdef __BMI2__ + "bmi2", +#endif +#ifdef __LZCNT__ + "lzcnt", +#endif +#ifdef __TBM__ + "tbm", +#endif +#ifdef __CRC32__ + "crc32", +#endif +#ifdef __POPCNT__ + "popcnt", +#endif +#ifdef __FSGSBASE__ + "fsgsbase", +#endif +#ifdef __RDRND__ + "rdrnd", +#endif +#ifdef __F16C__ + "f16c", +#endif +#ifdef __RDSEED__ + "rdseed", +#endif +#ifdef __PRFCHW__ + "prfchw", +#endif +#ifdef __ADX__ + "adx", +#endif +#ifdef __FXSR__ + "fxsr", +#endif +#ifdef __XSAVE__ + "xsave", +#endif +#ifdef __XSAVEOPT__ + "xsaveopt", +#endif +#ifdef __PREFETCHWT1__ + "prefetchwt1", +#endif +#ifdef __CLFLUSHOPT__ + "clflushopt", +#endif +#ifdef __CLZERO__ + "clzero", +#endif +#ifdef __XSAVEC__ + "xsavec", +#endif +#ifdef __XSAVES__ + "xsaves", +#endif +#ifdef __CLWB__ + "clwb", +#endif +#ifdef __MWAITX__ + "mwaitx", +#endif +#ifdef __PKU__ + "pku", +#endif +#ifdef __RDPID__ + "rdpid", +#endif +#ifdef __GFNI__ + "gfni", +#endif +#ifdef __SHSTK__ + "shstk", +#endif +#ifdef __VAES__ + "vaes", +#endif +#ifdef __VPCLMULQDQ__ + "vpclmulqdq", +#endif +#ifdef __MOVDIRI__ + "movdiri", +#endif +#ifdef __MOVDIR64B__ + "movdir64b", +#endif +#ifdef __WAITPKG__ + "waitpkg", +#endif +#ifdef __CLDEMOTE__ + "cldemote", +#endif +#ifdef __SERIALIZE__ + "serialize", +#endif +#ifdef __PTWRITE__ + "ptwrite", +#endif +#ifdef __AVX512BF16__ + "avx512bf16", +#endif +#ifdef __AVX512FP16__ + "avx512fp16", +#endif +#ifdef __ENQCMD__ + "enqcmd", +#endif +#ifdef __TSXLDTRK__ + "tsxldtrk", +#endif +#ifdef __AMX_TILE__ + "amx-tile", +#endif +#ifdef __AMX_INT8__ + "amx-int8", +#endif +#ifdef __AMX_BF16__ + "amx-bf16", +#endif +#ifdef __LAHF_SAHF__ + "sahf", +#endif +#ifdef __MOVBE__ + "movbe", +#endif +#ifdef __UINTR__ + "uintr", +#endif +#ifdef __HRESET__ + "hreset", +#endif +#ifdef __KL__ + "kl", +#endif +#ifdef __WIDEKL__ + "widekl", +#endif +#ifdef __AVXVNNI__ + "avxvnni", +#endif +#ifdef __AVXIFMA_ + "avxifma",_ +#endif +#ifdef __AVXVNNIINT8__ + "avxvnniint8", +#endif +#ifdef __AVXNECONVERT__ + "avxneconvert", +#endif +#ifdef __CMPCCXADD__ + "cmpccxadd", +#endif +#ifdef __AMX_FP16__ + "amx-fp16", +#endif +#ifdef __PREFETCHI__ + "prefetchi", +#endif +#ifdef __RAOINT__ + "raoint", +#endif +#ifdef __AMX_COMPLEX__ + "amx-complex", +#endif +#ifdef __AVXVNNIINT16__ + "amxvnniint16", +#endif +#ifdef __SM3__ + "sm3", +#endif +#ifdef __SHA512__ + "sha512", +#endif +#ifdef __SM4__ + "sm4", +#endif +#ifdef __EVEX512__ + "evex512", +#endif +#ifdef __USER_MSR__ + "usermsr", +#endif +#ifdef __AVX10_1_256__ + "avx10.1-256", +#endif +#ifdef __AVX10_1_512__ + "avx10.1-512", +#endif +#ifdef __APX_F__ + "apxf", +#endif + NULL }; + + if (kind && !gomp_match_selectors (kind, kind_choices)) + return false; + if (arch && !gomp_match_selectors (arch, arch_choices)) + return false; + if (isa && !gomp_match_selectors (isa, isa_choices)) + return false; + return true; +} diff --git a/libgomp/config/nvptx/selector.c b/libgomp/config/nvptx/selector.c new file mode 100644 index 00000000000..c1e81efca28 --- /dev/null +++ b/libgomp/config/nvptx/selector.c @@ -0,0 +1,77 @@ +/* Copyright (C) 2022 Free Software Foundation, Inc. + Contributed by Mentor, a Siemens Business. + + This file is part of the GNU Offloading and Multi Processing Library + (libgomp). + + Libgomp is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . */ + +/* This file contains an implementation of GOMP_evaluate_current_device for + a Nvidia GPU. */ + +#include "libgomp.h" +#include + +static bool +gomp_match_selectors (const char *selectors, const char **choices) +{ + while (*selectors != '\0') + { + bool match = false; + for (int i = 0; !match && choices[i]; i++) + match = !strcmp (selectors, choices[i]); + if (!match) + return false; + selectors += strlen (selectors) + 1; + } + return true; +} + +bool +GOMP_evaluate_current_device (const char *kind, const char *arch, + const char *isa) +{ + static const char *kind_choices[] = { "gpu", "nohost", NULL }; + static const char *arch_choices[] = { "nvptx", NULL }; + static const char *isa_choices[] + = { + "sm_30", +#if __PTX_SM__ >= 350 + "sm_35", +#endif +#if __PTX_SM__ >= 530 + "sm_53", +#endif +#if __PTX_SM__ >= 750 + "sm_75", +#endif +#if __PTX_SM__ >= 800 + "sm_80", +#endif + NULL }; + + if (kind && !gomp_match_selectors (kind, kind_choices)) + return false; + if (arch && !gomp_match_selectors (arch, arch_choices)) + return false; + if (isa && !gomp_match_selectors (isa, isa_choices)) + return false; + return true; +} diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h index 0c9c28c65cf..73f880ffa2f 100644 --- a/libgomp/libgomp-plugin.h +++ b/libgomp/libgomp-plugin.h @@ -152,6 +152,8 @@ extern int GOMP_OFFLOAD_memcpy3d (int, int, size_t, size_t, size_t, void *, extern bool GOMP_OFFLOAD_can_run (void *); extern void GOMP_OFFLOAD_run (int, void *, void *, void **); extern void GOMP_OFFLOAD_async_run (int, void *, void *, void **, void *); +extern bool GOMP_OFFLOAD_evaluate_device (int, const char *, const char *, + const char *); extern void GOMP_OFFLOAD_openacc_exec (void (*) (void *), size_t, void **, void **, unsigned *, void *); diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h index 089393846d1..4dad4bc321a 100644 --- a/libgomp/libgomp.h +++ b/libgomp/libgomp.h @@ -1417,6 +1417,7 @@ struct gomp_device_descr __typeof (GOMP_OFFLOAD_can_run) *can_run_func; __typeof (GOMP_OFFLOAD_run) *run_func; __typeof (GOMP_OFFLOAD_async_run) *async_run_func; + __typeof (GOMP_OFFLOAD_evaluate_device) *evaluate_device_func; /* Splay tree containing information about mapped memory regions. */ struct splay_tree_s mem_map; diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map index 65901dff235..70a48874417 100644 --- a/libgomp/libgomp.map +++ b/libgomp/libgomp.map @@ -428,6 +428,11 @@ GOMP_5.1.2 { GOMP_target_map_indirect_ptr; } GOMP_5.1.1; +GOMP_5.1.3 { + global: + GOMP_evaluate_target_device; +} GOMP_5.1.2; + OACC_2.0 { global: acc_get_num_devices; diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi index 71d62105a20..43048da4d6e 100644 --- a/libgomp/libgomp.texi +++ b/libgomp/libgomp.texi @@ -6181,9 +6181,10 @@ smaller number. On non-host devices, the value of the @c has to be implemented; cf. also PR target/105640. @c For offload devices, add *additionally* gcc/config/*/t-omp-device. -For the host compiler, @code{kind} always matches @code{host}; for the -offloading architectures AMD GCN and Nvidia PTX, @code{kind} always matches -@code{gpu}. For the x86 family of computers, AMD GCN and Nvidia PTX +For the host compiler, @code{kind} always matches @code{host} and @code{cpu}; +for the offloading architectures AMD GCN and Nvidia PTX, @code{kind} +always matches @code{gpu} and @code{nohost}. +For the x86 family of computers, AMD GCN and Nvidia PTX the following traits are supported in addition; while OpenMP is supported on more architectures, GCC currently does not match any @code{arch} or @code{isa} traits for those. @@ -6200,6 +6201,17 @@ on more architectures, GCC currently does not match any @code{arch} or @tab See @code{-march=} in ``Nvidia PTX Options'' @end multitable +For x86, note that the set of matching @code{arch} and @code{isa} +selectors is determined by command-line options rather than the actual +hardware. This is particularly true of dynamic selectors, which match +the options used to build libgomp rather than the options used to +build user programs (which may also differ between compilation units). + +For the @code{target_device} selector on AMD GCN and Nvidia PTX, +the actual hardware is checked at run time. On AMD GCN, an exact match +of the @code{isa} selector is required, while on Nvidia PTX lower-numbered +revisions also match. + @node Memory allocation @section Memory allocation diff --git a/libgomp/libgomp_g.h b/libgomp/libgomp_g.h index c0cc03ae61f..e9d60238e2b 100644 --- a/libgomp/libgomp_g.h +++ b/libgomp/libgomp_g.h @@ -337,6 +337,11 @@ extern void GOMP_single_copy_end (void *); extern void GOMP_scope_start (uintptr_t *); +/* selector.c */ + +extern bool GOMP_evaluate_current_device (const char *, const char *, + const char *); + /* target.c */ extern void GOMP_target (int, void (*) (void *), const void *, @@ -359,6 +364,9 @@ extern void GOMP_teams (unsigned int, unsigned int); extern bool GOMP_teams4 (unsigned int, unsigned int, unsigned int, bool); extern void *GOMP_target_map_indirect_ptr (void *); +extern bool GOMP_evaluate_target_device (int, const char *, const char *, + const char *); + /* teams.c */ extern void GOMP_teams_reg (void (*) (void *), void *, unsigned, unsigned, diff --git a/libgomp/oacc-host.c b/libgomp/oacc-host.c index 5efdf7fb796..b6883850250 100644 --- a/libgomp/oacc-host.c +++ b/libgomp/oacc-host.c @@ -136,6 +136,16 @@ host_run (int n __attribute__ ((unused)), void *fn_ptr, void *vars, fn (vars); } +static bool +host_evaluate_device (int device_num __attribute__ ((unused)), + const char *kind __attribute__ ((unused)), + const char *arch __attribute__ ((unused)), + const char *isa __attribute__ ((unused))) +{ + __builtin_unreachable (); + return false; +} + static void host_openacc_exec (void (*fn) (void *), size_t mapnum __attribute__ ((unused)), @@ -285,6 +295,7 @@ static struct gomp_device_descr host_dispatch = .memcpy2d_func = NULL, .memcpy3d_func = NULL, .run_func = host_run, + .evaluate_device_func = host_evaluate_device, .mem_map = { NULL }, .mem_map_rev = { NULL }, diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c index 3cdc7ba929f..9d9f34cf767 100644 --- a/libgomp/plugin/plugin-gcn.c +++ b/libgomp/plugin/plugin-gcn.c @@ -4395,6 +4395,58 @@ GOMP_OFFLOAD_async_run (int device, void *tgt_fn, void *tgt_vars, GOMP_PLUGIN_target_task_completion, async_data); } +/* The selectors are passed as strings, but are actually sets of multiple + trait property names, separated by '\0' and with an extra '\0' at + the end. Match such a string SELECTORS against an array of strings + CHOICES, that is terminated by a null pointer. + matches. */ +static bool +gomp_match_selectors (const char *selectors, const char **choices) +{ + while (*selectors != '\0') + { + bool match = false; + for (int i = 0; !match && choices[i]; i++) + match = !strcmp (selectors, choices[i]); + if (!match) + return false; + selectors += strlen (selectors) + 1; + } + return true; +} + +/* Here we can only have one possible match and it must be + the only selector provided. */ +static bool +gomp_match_isa (const char *selectors, gcn_isa isa) +{ + if (isa_code (selectors) != isa) + return false; + if (*(selectors + strlen (selectors) + 1) != '\0') + return false; + return true; +} + +bool +GOMP_OFFLOAD_evaluate_device (int device_num, const char *kind, + const char *arch, const char *isa) +{ + static const char *kind_choices[] = { "gpu", "nohost", NULL }; + static const char *arch_choices[] = { "gcn", "amdgcn", NULL }; + struct agent_info *agent = get_agent_info (device_num); + + if (kind && !gomp_match_selectors (kind, kind_choices)) + return false; + + if (arch && !gomp_match_selectors (arch, arch_choices)) + return false; + + if (isa && !gomp_match_isa (isa, agent->device_isa)) + return false; + + return true; +} + /* }}} */ /* {{{ OpenACC Plugin API */ diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c index 5aad3448a8d..5126720eb5c 100644 --- a/libgomp/plugin/plugin-nvptx.c +++ b/libgomp/plugin/plugin-nvptx.c @@ -317,6 +317,7 @@ struct ptx_device int max_threads_per_block; int max_threads_per_multiprocessor; int default_dims[GOMP_DIM_MAX]; + int compute_major, compute_minor; /* Length as used by the CUDA Runtime API ('struct cudaDeviceProp'). */ char name[256]; @@ -541,6 +542,14 @@ nvptx_open_device (int n) for (int i = 0; i != GOMP_DIM_MAX; i++) ptx_dev->default_dims[i] = 0; + CUDA_CALL_ERET (NULL, cuDeviceGetAttribute, &pi, + CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, dev); + ptx_dev->compute_major = pi; + + CUDA_CALL_ERET (NULL, cuDeviceGetAttribute, &pi, + CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, dev); + ptx_dev->compute_minor = pi; + CUDA_CALL_ERET (NULL, cuDeviceGetName, ptx_dev->name, sizeof ptx_dev->name, dev); @@ -2314,3 +2323,76 @@ GOMP_OFFLOAD_run (int ord, void *tgt_fn, void *tgt_vars, void **args) } /* TODO: Implement GOMP_OFFLOAD_async_run. */ + +/* The selectors are passed as strings, but are actually sets of multiple + trait property names, separated by '\0' and with an extra '\0' at + the end. Match such a string SELECTORS against an array of strings + CHOICES, that is terminated by a null pointer. + matches. */ +static bool +gomp_match_selectors (const char *selectors, const char **choices) +{ + while (*selectors != '\0') + { + bool match = false; + for (int i = 0; !match && choices[i]; i++) + match = !strcmp (selectors, choices[i]); + if (!match) + return false; + selectors += strlen (selectors) + 1; + } + return true; +} + +/* Here we can only have one possible match and it must be + the only selector provided. */ +static bool +gomp_match_selector (const char *selectors, const char *choice) +{ + if (!strcmp (selectors, choice)) + return false; + if (*(selectors + strlen (selectors) + 1) != '\0') + return false; + return true; +} + +#define CHECK_ISA(major, minor) \ + if (device->compute_major >= major \ + && device->compute_minor >= minor \ + && gomp_match_selector (isa, "sm_"#major#minor)) \ + return true + +bool +GOMP_OFFLOAD_evaluate_device (int device_num, const char *kind, + const char *arch, const char *isa) +{ + static const char *kind_choices[] = { "gpu", "nohost", NULL }; + static const char *arch_choices[] = { "nvptx", NULL }; + if (kind && !gomp_match_selectors (kind, kind_choices)) + return false; + + if (arch && !gomp_match_selectors (arch, arch_choices)) + return false; + + if (!isa) + return true; + + struct ptx_device *device = ptx_devices[device_num]; + + CHECK_ISA (3, 0); + CHECK_ISA (3, 5); + CHECK_ISA (3, 7); + CHECK_ISA (5, 0); + CHECK_ISA (5, 2); + CHECK_ISA (5, 3); + CHECK_ISA (6, 0); + CHECK_ISA (6, 1); + CHECK_ISA (6, 2); + CHECK_ISA (7, 0); + CHECK_ISA (7, 2); + CHECK_ISA (7, 5); + CHECK_ISA (8, 0); + CHECK_ISA (8, 6); + + return false; +} diff --git a/libgomp/selector.c b/libgomp/selector.c new file mode 100644 index 00000000000..5b21e582844 --- /dev/null +++ b/libgomp/selector.c @@ -0,0 +1,64 @@ +/* Copyright (C) 2022 Free Software Foundation, Inc. + Contributed by Mentor, a Siemens Business. + + This file is part of the GNU Offloading and Multi Processing Library + (libgomp). + + Libgomp is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . */ + +/* This file contains a placeholder implementation of + GOMP_evaluate_current_device. */ + +#include "libgomp.h" + +/* The selectors are passed as strings, but are actually sets of multiple + trait property names, separated by '\0' and with an extra '\0' at + the end. Match such a string SELECTORS against an array of strings + CHOICES, that is terminated by a null pointer. + matches. */ +static bool +gomp_match_selectors (const char *selectors, const char **choices) +{ + while (*selectors != '\0') + { + bool match = false; + for (int i = 0; !match && choices[i]; i++) + match = !strcmp (selectors, choices[i]); + if (!match) + return false; + selectors += strlen (selectors) + 1; + } + return true; +} + +bool +GOMP_evaluate_current_device (const char *kind, const char *arch, + const char *isa) +{ + static const char *kind_choices[] = { "cpu", "host", NULL }; + + if (kind && !gomp_match_selectors (kind, kind_choices)) + return false; + + if (arch || isa) + return false; + + return true; +} diff --git a/libgomp/target.c b/libgomp/target.c index 5ec19ae489e..02973ee2f40 100644 --- a/libgomp/target.c +++ b/libgomp/target.c @@ -5092,6 +5092,45 @@ omp_pause_resource_all (omp_pause_resource_t kind) ialias (omp_pause_resource) ialias (omp_pause_resource_all) +bool +GOMP_evaluate_target_device (int device_num, const char *kind, + const char *arch, const char *isa) +{ + bool result = true; + + /* -2 is a magic number to indicate the device number was not specified; + in that case it's supposed to use the default device. */ + if (device_num == -2) + device_num = omp_get_default_device (); + + if (kind && strcmp (kind, "any") == 0) + kind = NULL; + + gomp_debug (1, "%s: device_num = %u, kind=%s, arch=%s, isa=%s", + __FUNCTION__, device_num, kind, arch, isa); + + if (omp_get_device_num () == device_num) + result = GOMP_evaluate_current_device (kind, arch, isa); + else + { + if (!omp_is_initial_device ()) + /* Accelerators are not expected to know about other devices. */ + result = false; + else + { + struct gomp_device_descr *device = resolve_device (device_num, true); + if (device == NULL) + result = false; + else if (device->evaluate_device_func) + result = device->evaluate_device_func (device_num, kind, arch, + isa); + } + } + + gomp_debug (1, " -> %s\n", result ? "true" : "false"); + return result; +} + #ifdef PLUGIN_SUPPORT /* This function tries to load a plugin for DEVICE. Name of plugin is passed @@ -5144,6 +5183,7 @@ gomp_load_plugin_for_device (struct gomp_device_descr *device, DLSYM (free); DLSYM (dev2host); DLSYM (host2dev); + DLSYM (evaluate_device); DLSYM_OPT (memcpy2d, memcpy2d); DLSYM_OPT (memcpy3d, memcpy3d); device->capabilities = device->get_caps_func (); From patchwork Sat May 4 21:21:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 1931423 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=DBIec5Rp; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX0zB2KjQz1ybC for ; Sun, 5 May 2024 07:23:50 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9E13F384403E for ; Sat, 4 May 2024 21:23:48 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org 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 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 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+incoming=patchwork.ozlabs.org@gcc.gnu.org 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); + } +} From patchwork Sat May 4 21:21:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 1931426 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=VB7nLLX+; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX10C2zhzz1ybC for ; Sun, 5 May 2024 07:24:43 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9ACB4386F434 for ; Sat, 4 May 2024 21:24:41 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-il1-x130.google.com (mail-il1-x130.google.com [IPv6:2607:f8b0:4864:20::130]) by sourceware.org (Postfix) with ESMTPS id 2E1FB384476F for ; Sat, 4 May 2024 21:22:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2E1FB384476F 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 2E1FB384476F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::130 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857737; cv=none; b=RWQk7CFsXE0D17KOXcgJhvADrI5oUGA4u/e8ATLWOXJFqzquSu07AD6sPVv8/oJeVsxrwQGIqApHAGzJ3lyYIoOl1HPQ0L6DyC7148odGtnr/u8WDzTgjCRHA+S+ma44m504NNOYZycS8/3fJZ84Pm2JmuphnXTOx/+g9wjnvHU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857737; c=relaxed/simple; bh=q0NTDWtU2jdqJxn3QpO2Q5ZUdwccFoz+6mx7co7gN9A=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=MocfgGonTRH/PV9rdd9E8lGutWD/vZCPYXAPIl5UAj5pIo9GqXdM8Ji74TLn3cqDin1KXQUxSN63YwIo7HceIIuu6UIrmJCCwfjoVjmfcvIMEjF4QGm1jKhXlipFvgRTgoZMoHydPstJRP6few/oxbafSX3oRBZUUfiAgn/IZNo= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-il1-x130.google.com with SMTP id e9e14a558f8ab-36c82ca80adso4079965ab.3 for ; Sat, 04 May 2024 14:22:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857729; x=1715462529; 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=sDkDHgtBmQ8sixNL4cbsof8xJVJJPfFcuzgogLJij10=; b=VB7nLLX+zsBIb0OjCZyog5nQoJWNf3Bmby02vi3AqjC6wFly8tdqTKmPDzclB89JhJ OSf+0X7qu9JPV81WGMXl4GENtlGtVxV0L6+8QLXMRHod/jmJFcyoiznbtdU6+UEtMN/2 nkau6UD708LI8jexEVQ3drd91HT0bxASbq4oSJA77Kb4aO8UMBTzpKmkdFoTQg5UtpNq vgJIiBeniLarf+Qky9H7/gFColmfchgqTQnIjxERY/n6mwiq0V9iCyiNpn5S3zFSIHvq mva598MGQ+OHuRHvYUSIzDZjp5721CmXFoOy7Jio/ezuFgyztxM82t3DZ9F/6thmPQDS m4Fg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857729; x=1715462529; 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=sDkDHgtBmQ8sixNL4cbsof8xJVJJPfFcuzgogLJij10=; b=Da67Vte4krG7iHLI2brsHt8gsfz4VJ8V5Ik7eiRulgLPwquxXGr+q4ALHVkHmca2Vu ScU4h49KJel5+DCVw3aQ6Ha55Z2e5BGBwwfe58ihqxDo6qZe6ujNkxmbSCXcTh4bVRQJ 5fb/iQ8nXVPXEXk8oe9FpZCSSmAG3HzptNrEtdfIWaV0dwHod3tEU2DEFBl/O+8aPdG1 9ywfMepqivvfieCrjC0JSbQcm5uABzP99U+BrFyfqzXM5ZyE91wRJwA013ASvvLlNdnE fIaolDstttz5q65R3T4n/fBhfS3ioR98r1pxWVtHGlTXDIUE/rgds/0WQ+Jy7nzJo9s9 Y1UQ== X-Gm-Message-State: AOJu0YxbOYCu+QoFOsZYaEHW8ljvfIRZybpSvgb3jeSDfasjfiHBgKQ0 h8xQgbOL0MZm9onGOaQlTm+dJBGP+4/MaPrt6S2l/EveFU2VYSTaWlWVRvigLXyF4KgWC+E7yVi 9 X-Google-Smtp-Source: AGHT+IE0zYOoeRKw4k13ALPu9qJcF4vQ4EDIFsms0Jm2quPMCEV3x6vvwhGTYGIWb9aOQ86j3+TwUQ== X-Received: by 2002:a05:6e02:16cd:b0:36c:a4e:9ea8 with SMTP id 13-20020a056e0216cd00b0036c0a4e9ea8mr8487287ilx.17.1714857729082; Sat, 04 May 2024 14:22:09 -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.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:08 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 05/12] OpenMP: C++ front-end support for metadirectives Date: Sat, 4 May 2024 15:21:45 -0600 Message-Id: <20240504212153.3561429-6-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 X-Spam-Status: No, score=-11.1 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 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+incoming=patchwork.ozlabs.org@gcc.gnu.org This patch adds C++ support for metadirectives. It uses the c-family support committed with the corresponding C front end patch to do early parse-time metadirective resolution when possible. Additional C/C++ common testcases are provided in a subsequent patch in the series. gcc/cp/ChangeLog * parser.cc (cp_parser_skip_to_end_of_block_or_statement): Add metadirective_p parameter, use it to control brace/parentheses behavior for metadirectives. (mangle_metadirective_region_label): New. (cp_parser_label_for_labeled_statement): Use it. (cp_parser_jump_statement): Likewise. (cp_parser_omp_context_selector): Add metadirective_p parameter, use it to control error behavior for non-constant exprs properties. (cp_parser_omp_context_selector_specification): Add metadirective_p parameter, use it for cp_parser_omp_context_selector call. (cp_finish_omp_declare_variant): Adjust call to cp_parser_omp_context_selector_specification. (analyze_metadirective_body): New. (cp_parser_omp_metadirective): New. (cp_parser_pragma): Handle PRAGMA_OMP_METADIRECTIVE. * parser.h (struct cp_parser): Add fields for metadirective parsing state. * pt.cc (tsubst_omp_context_selector): New. (tsubst_stmt): Handle OMP_METADIRECTIVE. gcc/testsuite/ChangeLog * g++.dg/gomp/attrs-metadirective-1.C: New. * g++.dg/gomp/attrs-metadirective-2.C: New. * g++.dg/gomp/attrs-metadirective-3.C: New. * g++.dg/gomp/attrs-metadirective-4.C: New. * g++.dg/gomp/attrs-metadirective-5.C: New. * g++.dg/gomp/attrs-metadirective-6.C: New. * g++.dg/gomp/attrs-metadirective-7.C: New. * g++.dg/gomp/attrs-metadirective-8.C: New. libgomp/ChangeLog * testsuite/libgomp.c++/metadirective-template-1.C: New. * testsuite/libgomp.c++/metadirective-template-2.C: New. * testsuite/libgomp.c++/metadirective-template-3.C: New. Co-Authored-By: Kwok Cheung Yeung Co-Authored-By: Sandra Loosemore --- gcc/cp/parser.cc | 524 +++++++++++++++++- gcc/cp/parser.h | 7 + gcc/cp/pt.cc | 119 ++++ .../g++.dg/gomp/attrs-metadirective-1.C | 40 ++ .../g++.dg/gomp/attrs-metadirective-2.C | 74 +++ .../g++.dg/gomp/attrs-metadirective-3.C | 31 ++ .../g++.dg/gomp/attrs-metadirective-4.C | 41 ++ .../g++.dg/gomp/attrs-metadirective-5.C | 24 + .../g++.dg/gomp/attrs-metadirective-6.C | 31 ++ .../g++.dg/gomp/attrs-metadirective-7.C | 31 ++ .../g++.dg/gomp/attrs-metadirective-8.C | 16 + .../libgomp.c++/metadirective-template-1.C | 37 ++ .../libgomp.c++/metadirective-template-2.C | 41 ++ .../libgomp.c++/metadirective-template-3.C | 41 ++ 14 files changed, 1044 insertions(+), 13 deletions(-) create mode 100644 gcc/testsuite/g++.dg/gomp/attrs-metadirective-1.C create mode 100644 gcc/testsuite/g++.dg/gomp/attrs-metadirective-2.C create mode 100644 gcc/testsuite/g++.dg/gomp/attrs-metadirective-3.C create mode 100644 gcc/testsuite/g++.dg/gomp/attrs-metadirective-4.C create mode 100644 gcc/testsuite/g++.dg/gomp/attrs-metadirective-5.C create mode 100644 gcc/testsuite/g++.dg/gomp/attrs-metadirective-6.C create mode 100644 gcc/testsuite/g++.dg/gomp/attrs-metadirective-7.C create mode 100644 gcc/testsuite/g++.dg/gomp/attrs-metadirective-8.C create mode 100644 libgomp/testsuite/libgomp.c++/metadirective-template-1.C create mode 100644 libgomp/testsuite/libgomp.c++/metadirective-template-2.C create mode 100644 libgomp/testsuite/libgomp.c++/metadirective-template-3.C diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 8b819b2ebfd..4bb9b086095 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -3001,7 +3001,7 @@ static void cp_parser_skip_to_end_of_statement static void cp_parser_consume_semicolon_at_end_of_statement (cp_parser *); static void cp_parser_skip_to_end_of_block_or_statement - (cp_parser *); + (cp_parser *, bool = false); static bool cp_parser_skip_to_closing_brace (cp_parser *); static bool cp_parser_skip_entire_template_parameter_list @@ -4177,9 +4177,11 @@ cp_parser_consume_semicolon_at_end_of_statement (cp_parser *parser) have consumed a non-nested `;'. */ static void -cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser) +cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser, + bool metadirective_p) { int nesting_depth = 0; + int bracket_depth = 0; /* Unwind generic function template scope if necessary. */ if (parser->fully_implicit_function_template_p) @@ -4201,7 +4203,7 @@ cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser) case CPP_SEMICOLON: /* Stop if this is an unnested ';'. */ - if (!nesting_depth) + if (!nesting_depth && (!metadirective_p || bracket_depth <= 0)) nesting_depth = -1; break; @@ -4220,6 +4222,19 @@ cp_parser_skip_to_end_of_block_or_statement (cp_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_KEYWORD: if (!cp_token_is_module_directive (token)) break; @@ -12999,6 +13014,18 @@ attr_chainon (tree attrs, tree attr) return chainon (attrs, attr); } + +/* Helper function for cp_parser_label: mangle a metadirective region + label NAME. */ +static tree +mangle_metadirective_region_label (cp_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 the label for a labeled-statement, i.e. label: @@ -13101,7 +13128,12 @@ cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes) default: /* Anything else must be an ordinary label. */ - label = finish_label_stmt (cp_parser_identifier (parser)); + cp_expr identifier = cp_parser_identifier (parser); + if (identifier != error_mark_node + && parser->in_metadirective_body + && parser->metadirective_body_labels->contains (*identifier)) + *identifier = mangle_metadirective_region_label (parser, *identifier); + label = finish_label_stmt (identifier); if (label && TREE_CODE (label) == LABEL_DECL) FALLTHROUGH_LABEL_P (label) = fallthrough_p; break; @@ -14906,7 +14938,15 @@ cp_parser_jump_statement (cp_parser* parser) finish_goto_stmt (cp_parser_expression (parser)); } else - finish_goto_stmt (cp_parser_identifier (parser)); + { + cp_expr identifier = cp_parser_identifier (parser); + if (identifier != error_mark_node + && parser->in_metadirective_body + && parser->metadirective_body_labels->contains (*identifier)) + *identifier = mangle_metadirective_region_label (parser, + *identifier); + finish_goto_stmt (identifier); + } /* Look for the final `;'. */ cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); break; @@ -47899,7 +47939,7 @@ cp_parser_omp_declare_simd (cp_parser *parser, cp_token *pragma_tok, static tree cp_parser_omp_context_selector (cp_parser *parser, enum omp_tss_code set, - bool has_parms_p) + bool has_parms_p, bool metadirective_p) { tree ret = NULL_TREE; do @@ -48060,17 +48100,25 @@ cp_parser_omp_context_selector (cp_parser *parser, enum omp_tss_code set, break; case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR: case OMP_TRAIT_PROPERTY_BOOL_EXPR: - /* FIXME: this is bogus, the expression need - not be constant. */ - t = cp_parser_constant_expression (parser); + /* FIXME: I believe it is an unimplemented feature rather + than a user error to have non-constant expressions + inside "declare variant". */ + t = metadirective_p + ? cp_parser_expression (parser) + : cp_parser_constant_expression (parser); if (t != error_mark_node) { t = fold_non_dependent_expr (t); - if (!value_dependent_expression_p (t) + if (!metadirective_p + && !value_dependent_expression_p (t) && (!INTEGRAL_TYPE_P (TREE_TYPE (t)) || !tree_fits_shwi_p (t))) error_at (token->location, "property must be " "constant integer expression"); + 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); @@ -48149,7 +48197,8 @@ cp_parser_omp_context_selector (cp_parser *parser, enum omp_tss_code set, static tree cp_parser_omp_context_selector_specification (cp_parser *parser, - bool has_parms_p) + bool has_parms_p, + bool metadirective_p) { tree ret = NULL_TREE; do @@ -48176,7 +48225,8 @@ cp_parser_omp_context_selector_specification (cp_parser *parser, return error_mark_node; tree selectors - = cp_parser_omp_context_selector (parser, set, has_parms_p); + = cp_parser_omp_context_selector (parser, set, has_parms_p, + metadirective_p); if (selectors == error_mark_node) { cp_parser_skip_to_closing_brace (parser); @@ -48486,7 +48536,8 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, if (!parens.require_open (parser)) goto fail; - tree ctx = cp_parser_omp_context_selector_specification (parser, true); + tree ctx = cp_parser_omp_context_selector_specification (parser, true, + false); if (ctx == error_mark_node) goto fail; ctx = omp_check_context_selector (match_loc, ctx, false); @@ -49119,6 +49170,449 @@ cp_parser_omp_end (cp_parser *parser, cp_token *pragma_tok) } } + +/* Helper function for cp_parser_omp_metadirective. */ + +static void +analyze_metadirective_body (cp_parser *parser, + vec &tokens, + vec &labels) +{ + int nesting_depth = 0; + int bracket_depth = 0; + bool in_case = false; + bool in_label_decl = false; + cp_token *pragma_tok = NULL; + + while (1) + { + cp_token *token = cp_lexer_peek_token (parser->lexer); + bool stop = false; + + if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CASE)) + in_case = true; + else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL)) + in_label_decl = true; + + switch (token->type) + { + case CPP_EOF: + break; + case CPP_NAME: + if ((!in_case + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON)) + || in_label_decl) + labels.safe_push (token->u.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: + in_case = false; + goto add; + case CPP_SEMICOLON: + if (nesting_depth == 0 && bracket_depth == 0) + stop = true; + /* Local label declarations are terminated by a semicolon. */ + in_label_decl = false; + goto add; + case CPP_PRAGMA: + parser->lexer->in_pragma = true; + pragma_tok = token; + goto add; + case CPP_PRAGMA_EOL: + /* C++ attribute syntax for OMP directives lexes as a pragma, + but we must reset the associated lexer state when we reach + the end in order to get the tokens for the statement that + come after it. */ + tokens.safe_push (*token); + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + pragma_tok = NULL; + continue; + default: + add: + tokens.safe_push (*token); + cp_lexer_consume_token (parser->lexer); + if (stop) + break; + continue; + } + break; + } +} + +/* OpenMP 5.0: + + # pragma omp metadirective [clause[, clause]] +*/ + +static void +cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok, + bool *if_p) +{ + static unsigned int metadirective_region_count = 0; + + auto_vec directive_tokens; + auto_vec body_tokens; + auto_vec body_labels; + auto_vec directives; + auto_vec ctxs; + bool default_seen = false; + int directive_token_idx = 0; + location_t pragma_loc = pragma_tok->location; + tree standalone_body = NULL_TREE; + vec candidates; + bool requires_body = false; + + tree ret = make_node (OMP_METADIRECTIVE); + SET_EXPR_LOCATION (ret, pragma_loc); + TREE_TYPE (ret) = void_type_node; + OMP_METADIRECTIVE_VARIANTS (ret) = NULL_TREE; + + while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)) + { + if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) + cp_lexer_consume_token (parser->lexer); + if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME) + && cp_lexer_next_token_is_not (parser->lexer, CPP_KEYWORD)) + { + cp_parser_error (parser, "expected %, " + "%, or % clause"); + goto fail; + } + + location_t match_loc = cp_lexer_peek_token (parser->lexer)->location; + const char *p + = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value); + cp_lexer_consume_token (parser->lexer); + 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 %"); + cp_parser_skip_to_end_of_block_or_statement (parser, true); + goto fail; + } + else + default_seen = true; + } + if (!strcmp (p, "when") == 0 && !default_p) + { + error_at (match_loc, "%qs is not valid for %qs", + p, "metadirective"); + cp_parser_skip_to_end_of_block_or_statement (parser, true); + goto fail; + } + + matching_parens parens; + tree ctx = NULL_TREE; + bool skip = false; + + if (!parens.require_open (parser)) + goto fail; + + if (!default_p) + { + ctx = cp_parser_omp_context_selector_specification (parser, false, + true); + if (ctx == error_mark_node) + goto fail; + ctx = omp_check_context_selector (match_loc, ctx, true); + if (ctx == error_mark_node) + goto fail; + + /* Remove the selector from further consideration if it can be + evaluated as a non-match at this point. */ + /* FIXME: we could still do this if the context selector + doesn't have any dependent subexpressions. */ + skip = (!processing_template_decl + && omp_context_selector_matches (ctx, true, true) == 0); + + if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)) + { + cp_parser_require (parser, CPP_COLON, RT_COLON); + goto fail; + } + cp_lexer_consume_token (parser->lexer); + } + + /* Read in the directive type and create a dummy pragma token for + it. */ + location_t loc = cp_lexer_peek_token (parser->lexer)->location; + + const char *directive[3] = {}; + int i; + for (i = 0; i < 3; i++) + { + tree id; + if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->type + == CPP_CLOSE_PAREN) + { + if (i == 0) + directive[i++] = "nothing"; + break; + } + else if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->type + == CPP_NAME) + id = cp_lexer_peek_nth_token (parser->lexer, i + 1)->u.value; + else if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->keyword + != RID_MAX) + { + enum rid rid + = cp_lexer_peek_nth_token (parser->lexer, i + 1)->keyword; + id = ridpointers[rid]; + } + else + break; + + directive[i] = IDENTIFIER_POINTER (id); + } + if (i == 0) + { + error_at (loc, "expected directive name"); + cp_parser_skip_to_end_of_block_or_statement (parser, true); + goto fail; + } + + 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++) + cp_lexer_consume_token (parser->lexer); + cp_parser_error (parser, "unknown directive name"); + goto fail; + } + 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++) + cp_lexer_consume_token (parser->lexer); + } + if (p == NULL) + { + cp_parser_error (parser, "expected directive name"); + goto fail; + } + if (omp_directive->id == PRAGMA_OMP_METADIRECTIVE) + { + cp_parser_error (parser, + "metadirectives cannot be used as variants of a " + "%"); + goto fail; + } + if (omp_directive->kind == C_OMP_DIR_DECLARATIVE) + { + sorry_at (loc, "declarative directive variants of a " + "% are not supported"); + goto fail; + } + if (omp_directive->kind == C_OMP_DIR_CONSTRUCT) + requires_body = true; + + if (!skip) + { + cp_token pragma_token; + pragma_token.type = CPP_PRAGMA; + pragma_token.location = loc; + pragma_token.u.value = build_int_cst (NULL, 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) + { + cp_token *token = cp_lexer_peek_token (parser->lexer); + 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); + cp_lexer_consume_token (parser->lexer); + continue; + } + break; + } + + cp_lexer_consume_token (parser->lexer); + + if (!skip) + { + cp_token eol_token = {}; + eol_token.type = CPP_PRAGMA_EOL; + eol_token.keyword = RID_MAX; + directive_tokens.safe_push (eol_token); + } + } + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + + 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); + + cp_token pragma_token = {}; + pragma_token.type = CPP_PRAGMA; + pragma_token.keyword = RID_MAX; + pragma_token.location = UNKNOWN_LOCATION; + pragma_token.u.value = build_int_cst (NULL, PRAGMA_OMP_NOTHING); + + directives.safe_push (omp_directive); + directive_tokens.safe_push (pragma_token); + ctxs.safe_push (NULL_TREE); + + cp_token eol_token = {}; + eol_token.type = CPP_PRAGMA_EOL; + eol_token.keyword = RID_MAX; + 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; + cp_lexer *lexer; + + lexer = cp_lexer_alloc (); + lexer->debugging_p = parser->lexer->debugging_p; + vec_safe_reserve (lexer->buffer, + directive_tokens.length () + body_tokens.length () + 2); + + FOR_EACH_VEC_ELT (ctxs, i, ctx) + { + lexer->buffer->truncate (0); + + /* Add the directive tokens. */ + do + lexer->buffer->quick_push (directive_tokens [directive_token_idx++]); + while (lexer->buffer->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++) + lexer->buffer->quick_push (body_tokens[j]); + + /* Make sure nothing tries to read past the end of the tokens. */ + cp_token eof_token = {}; + eof_token.type = CPP_EOF; + eof_token.keyword = RID_MAX; + lexer->buffer->quick_push (eof_token); + lexer->buffer->quick_push (eof_token); + + lexer->next_token = lexer->buffer->address(); + lexer->last_token = lexer->next_token + lexer->buffer->length () - 1; + + cp_lexer *old_lexer = parser->lexer; + 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->lexer = lexer; + cp_lexer_set_source_position_from_token (lexer->next_token); + parser->in_metadirective_body = true; + parser->metadirective_body_labels = &body_labels; + parser->metadirective_region_num = ++metadirective_region_count; + + int prev_errorcount = errorcount; + tree directive = push_stmt_list (); + tree directive_stmt = begin_compound_stmt (0); + + cp_parser_pragma (parser, pragma_compound, if_p); + finish_compound_stmt (directive_stmt); + directive = pop_stmt_list (directive); + + 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 (); + cp_parser_statement (parser, NULL_TREE, false, if_p); + standalone_body = pop_stmt_list (standalone_body); + } + else + cp_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. */ + gcc_assert (errorcount != prev_errorcount + || cp_lexer_next_token_is (parser->lexer, CPP_EOF)); + + parser->lexer = old_lexer; + cp_lexer_set_source_position_from_token (old_lexer->next_token); + 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. */ + if (!processing_template_decl) + { + candidates = omp_early_resolve_metadirective (ret); + if (!candidates.is_empty ()) + ret = c_omp_expand_metadirective (candidates); + } + + add_stmt (ret); + return; + +fail: + /* Skip the metadirective pragma. */ + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + + /* Skip the metadirective body. */ + cp_parser_skip_to_end_of_block_or_statement (parser, true); +} + + /* Helper function of cp_parser_omp_declare_reduction. Parse the combiner expression and optional initializer clause of #pragma omp declare reduction. We store the expression(s) as @@ -51051,6 +51545,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) cp_parser_omp_nothing (parser, pragma_tok); return false; + case PRAGMA_OMP_METADIRECTIVE: + cp_parser_omp_metadirective (parser, pragma_tok, if_p); + return true; + case PRAGMA_OMP_ERROR: return cp_parser_omp_error (parser, pragma_tok, context); diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index 373e78f3ea4..1260898774c 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -446,6 +446,13 @@ struct GTY(()) cp_parser { /* 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; + + /* Set if we are processing a statement body associated with a + metadirective variant. */ + bool in_metadirective_body; + + vec * GTY((skip)) metadirective_body_labels; + unsigned metadirective_region_num; }; /* In parser.cc */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 3b2106dd3f6..409c4df68bc 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -17859,6 +17859,79 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort, return new_clauses; } +/* Like tsubst_copy, but specifically for OpenMP context selectors. */ +static tree +tsubst_omp_context_selector (tree ctx, tree args, tsubst_flags_t complain, + tree in_decl) +{ + tree new_ctx = NULL_TREE; + for (tree set = ctx; set; set = TREE_CHAIN (set)) + { + tree selectors = NULL_TREE; + for (tree sel = OMP_TSS_TRAIT_SELECTORS (set); sel; + sel = TREE_CHAIN (sel)) + { + enum omp_ts_code code = OMP_TS_CODE (sel); + tree properties = NULL_TREE; + tree score = OMP_TS_SCORE (sel); + tree t; + + if (score) + { + score = tsubst_expr (score, args, complain, in_decl); + score = fold_non_dependent_expr (score); + if (!INTEGRAL_TYPE_P (TREE_TYPE (score)) + || TREE_CODE (score) != INTEGER_CST) + { + error_at (cp_expr_loc_or_input_loc (score), + "% argument must " + "be constant integer expression"); + score = NULL_TREE; + } + else if (tree_int_cst_sgn (score) < 0) + { + error_at (cp_expr_loc_or_input_loc (score), + "% argument must be non-negative"); + score = NULL_TREE; + } + } + + switch (omp_ts_map[OMP_TS_CODE (sel)].tp_type) + { + case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR: + case OMP_TRAIT_PROPERTY_BOOL_EXPR: + t = tsubst_expr (OMP_TP_VALUE (OMP_TS_PROPERTIES (sel)), + args, complain, in_decl); + t = fold_non_dependent_expr (t); + if (!INTEGRAL_TYPE_P (TREE_TYPE (t))) + error_at (cp_expr_loc_or_input_loc (t), + "property must be integer expression"); + properties = make_trait_property (NULL_TREE, t, NULL_TREE); + break; + case OMP_TRAIT_PROPERTY_CLAUSE_LIST: + if (OMP_TS_CODE (sel) == OMP_TRAIT_CONSTRUCT_SIMD) + properties = tsubst_omp_clauses (OMP_TS_PROPERTIES (sel), + C_ORT_OMP_DECLARE_SIMD, + args, complain, in_decl); + break; + default: + /* Nothing to do here, just copy. */ + for (tree prop = OMP_TS_PROPERTIES (sel); + prop; prop = TREE_CHAIN (prop)) + properties = make_trait_property (OMP_TP_NAME (prop), + OMP_TP_VALUE (prop), + properties); + } + selectors = make_trait_selector (code, score, properties, selectors); + } + new_ctx = make_trait_set_selector (OMP_TSS_CODE (set), + nreverse (selectors), + new_ctx); + } + return nreverse (new_ctx); +} + + /* Like tsubst_expr, but unshare TREE_LIST nodes. */ static tree @@ -19406,6 +19479,52 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl) } break; + case OMP_METADIRECTIVE: + { + tree variants = NULL_TREE; + for (tree v = OMP_METADIRECTIVE_VARIANTS (t); v; v = TREE_CHAIN (v)) + { + tree ctx = OMP_METADIRECTIVE_VARIANT_SELECTOR (v); + tree directive = OMP_METADIRECTIVE_VARIANT_DIRECTIVE (v); + tree body = OMP_METADIRECTIVE_VARIANT_BODY (v); + tree s; + + /* CTX is null if this is the default variant. */ + if (ctx) + { + ctx = tsubst_omp_context_selector (ctx, args, complain, + in_decl); + /* Remove the selector from further consideration if it can be + evaluated as a non-match at this point. */ + if (omp_context_selector_matches (ctx, true, true) == 0) + continue; + } + s = push_stmt_list (); + RECUR (directive); + directive = pop_stmt_list (s); + if (body) + { + s = push_stmt_list (); + RECUR (body); + body = pop_stmt_list (s); + } + variants + = chainon (variants, + make_omp_metadirective_variant (ctx, directive, + body)); + } + t = copy_node (t); + OMP_METADIRECTIVE_VARIANTS (t) = variants; + + /* Try to resolve the metadirective early. */ + vec candidates + = omp_early_resolve_metadirective (t); + if (!candidates.is_empty ()) + t = c_omp_expand_metadirective (candidates); + add_stmt (t); + break; + } + case TRANSACTION_EXPR: { int flags = 0; diff --git a/gcc/testsuite/g++.dg/gomp/attrs-metadirective-1.C b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-1.C new file mode 100644 index 00000000000..a0b09088d3a --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-1.C @@ -0,0 +1,40 @@ +// { dg-do compile { target c++11 } } + +#define N 100 + +void f (int a[], int b[], int c[]) +{ + int i; + + [[omp::directive (metadirective + default (teams loop) + default (parallel loop))]] /* { dg-error "too many 'otherwise' or 'default' clauses in 'metadirective'" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + [[omp::directive (metadirective + default (bad_directive))]] /* { dg-error "unknown directive name before '\\)' token" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + [[omp::directive (metadirective + default (teams loop) + where (device={arch("nvptx")}: parallel loop))]] /* { dg-error "'where' is not valid for 'metadirective'" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + [[omp::directive (metadirective + default (teams loop) + when (device={arch("nvptx")} parallel loop))]] /* { dg-error "expected ':' before 'parallel'" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + [[omp::directive (metadirective + default (metadirective default (flush)))]] /* { dg-error "metadirectives cannot be used as variants of a 'metadirective' before 'default'" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + /* Test improperly nested metadirectives - even though the second + metadirective resolves to 'omp nothing', that is not the same as there + being literally nothing there. */ + [[omp::directive (metadirective + when (implementation={vendor("gnu")}: parallel for))]] + [[omp::directive (metadirective /* { dg-error "'#pragma' is not allowed here" } */ + when (implementation={vendor("cray")}: parallel for))]] + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; +} diff --git a/gcc/testsuite/g++.dg/gomp/attrs-metadirective-2.C b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-2.C new file mode 100644 index 00000000000..44c87df1776 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-2.C @@ -0,0 +1,74 @@ +// { dg-do compile { target c++11 } } + +#define N 100 + +int main (void) +{ + int x = 0; + int y = 0; + + /* Test implicit default (nothing). */ + [[omp::directive (metadirective, + when (device={arch("nvptx")}: barrier))]] + x = 1; + + /* Test with multiple standalone directives. */ + [[omp::directive (metadirective, + when (device={arch("nvptx")}: barrier), + default (flush))]] + x = 1; + + /* Test combining a standalone directive with one that takes a statement + body. */ + [[omp::directive (metadirective, + when (device={arch("nvptx")}: parallel), + default (barrier))]] + x = 1; + + /* Test combining a standalone directive with one that takes a for loop. */ + [[omp::directive (metadirective, + when (device={arch("nvptx")}: parallel for), + default (barrier))]] + for (int i = 0; i < N; i++) + x += i; + + /* Test combining a directive that takes a for loop with one that takes + a regular statement body. */ + [[omp::directive (metadirective, + when (device={arch("nvptx")}: parallel for), + default (parallel))]] + for (int i = 0; i < N; i++) + x += i; + + /* Test labels inside statement body. */ + [[omp::directive (metadirective, + when (device={arch("nvptx")}: teams num_teams(512)), + when (device={arch("gcn")}: teams num_teams(256)), + default (teams num_teams(4)))]] + { + if (x) + goto l1; + else + goto l2; + l1: ; + l2: ; + } + + /* Test local labels inside statement body. */ + [[omp::directive (metadirective, + when (device={arch("nvptx")}: teams num_teams(512)), + when (device={arch("gcn")}: teams num_teams(256)), + default (teams num_teams(4)))]] + { + //__label__ l1, l2; + + if (x) + goto l1; + else + goto l2; + l1: ; + l2: ; + } + + return 0; +} diff --git a/gcc/testsuite/g++.dg/gomp/attrs-metadirective-3.C b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-3.C new file mode 100644 index 00000000000..0c2bbdd2f10 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-3.C @@ -0,0 +1,31 @@ +// { dg-do compile { target c++11 } } +/* { dg-additional-options "-fdump-tree-original" } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ +/* { dg-additional-options "-fdump-tree-optimized" } */ + +#define N 100 + +void f (int x[], int y[], int z[]) +{ + int i; + + [[omp::sequence (directive (target map(to: x, y) map(from: z)), + directive (metadirective + when (device={arch("nvptx")}: teams loop) + default (parallel loop)))]] + for (i = 0; i < N; i++) + z[i] = x[i] * y[i]; +} + +/* The metadirective should be resolved after Gimplification. */ + +/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "when \\(device = .*arch.*nvptx.*\\):" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp teams" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "default:" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp loop" 2 "original" } } */ + +/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "gimple" } } */ + +/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/attrs-metadirective-4.C b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-4.C new file mode 100644 index 00000000000..43b939be43b --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-4.C @@ -0,0 +1,41 @@ +// { dg-do compile { target c++11 } } + +/* { dg-additional-options "-fdump-tree-original" } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +#define N 100 + +#pragma omp declare target +void f(double a[], double x) { + int i; + + [[omp::directive (metadirective + when (construct={target}: distribute parallel for) + default (parallel for simd))]] + for (i = 0; i < N; i++) + a[i] = x * i; +} +#pragma omp end declare target + + int main() +{ + double a[N]; + + #pragma omp target teams map(from: a[0:N]) + f (a, 3.14159); + + /* TODO: This does not execute a version of f with the default clause + active as might be expected. */ + f (a, 2.71828); /* { dg-warning "direct calls to an offloadable function containing metadirectives with a 'construct={target}' selector may produce unexpected results" } */ + + return 0; + } + + /* The metadirective should be resolved during Gimplification. */ + +/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "when \\(construct = .*target.*\\):" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "default:" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 2 "original" } } */ + +/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/attrs-metadirective-5.C b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-5.C new file mode 100644 index 00000000000..1a9cee15be3 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-5.C @@ -0,0 +1,24 @@ +// { dg-do compile { target c++11 } } +/* { dg-additional-options "-fdump-tree-original" } */ + +#define N 100 + +void f (int a[], int flag) +{ + int i; + [[omp::directive (metadirective + when (user={condition(flag)}: + target teams distribute parallel for map(from: a[0:N])) + default (parallel for))]] + for (i = 0; i < N; i++) + a[i] = i; +} + +/* The metadirective should be resolved at parse time. */ + +/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp target" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp teams" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp distribute" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp for" 2 "original" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/attrs-metadirective-6.C b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-6.C new file mode 100644 index 00000000000..8a104ff2ebe --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-6.C @@ -0,0 +1,31 @@ +// { dg-do compile { target c++11 } } +/* { dg-additional-options "-fdump-tree-original" } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +#define N 100 + +void bar (int a[], int run_parallel, int run_guided) +{ + [[omp::directive (metadirective + when (user={condition(run_parallel)}: parallel))]] + { + int i; + [[omp::directive (metadirective + when (construct={parallel}, user={condition(run_guided)}: + for schedule(guided)) + when (construct={parallel}: for schedule(static)))]] + for (i = 0; i < N; i++) + a[i] = i; + } + } + +/* The outer metadirective should be resolved at parse time. */ +/* The inner metadirective should be resolved during Gimplificiation. */ + +/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp for" 4 "original" } } */ +/* { dg-final { scan-tree-dump-times "when \\(construct = .parallel" 4 "original" } } */ +/* { dg-final { scan-tree-dump-times "default:" 2 "original" } } */ + +/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/attrs-metadirective-7.C b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-7.C new file mode 100644 index 00000000000..33861ec077e --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-7.C @@ -0,0 +1,31 @@ +// { dg-do compile { target c++11 } } +/* { dg-additional-options "-fdump-tree-gimple" } */ + +#define N 256 + +void f (int a[], int num) +{ + int i; + + [[omp::directive (metadirective + when (target_device={device_num(num), kind("gpu"), arch("nvptx")}: + target parallel for map(tofrom: a[0:N])) + when (target_device={device_num(num), kind("gpu"), + arch("amdgcn"), isa("gfx906")}: + target parallel for) + when (target_device={device_num(num), kind("cpu"), arch("x86_64")}: + parallel for))]] + for (i = 0; i < N; i++) + a[i] += i; + + [[omp::directive (metadirective + when (target_device={kind("gpu"), arch("nvptx")}: + target parallel for map(tofrom: a[0:N])))]] + for (i = 0; i < N; i++) + a[i] += i; +} + +/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(num, &\"gpu.x00\"\\\[0\\\], &\"amdgcn.x00\"\\\[0\\\], &\"gfx906.x00\"\\\[0\\\]\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(num, &\"gpu.x00\"\\\[0\\\], &\"nvptx.x00\"\\\[0\\\], 0B\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(num, &\"cpu.x00\"\\\[0\\\], &\"x86_64.x00\"\\\[0\\\], 0B\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(-2, &\"gpu.x00\"\\\[0\\\], &\"nvptx.x00\"\\\[0\\\], 0B\\)" "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/attrs-metadirective-8.C b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-8.C new file mode 100644 index 00000000000..dcb3c365b80 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-8.C @@ -0,0 +1,16 @@ +// { dg-do compile { target c++11 } } + +#define N 256 + +void f () +{ + int i; + int a[N]; + + [[omp::directive (metadirective + when( device={kind(nohost)}: nothing ) + when( device={arch("nvptx")}: nothing) + default( parallel for))]] + for (i = 0; i < N; i++) + a[i] = i; +} diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-1.C b/libgomp/testsuite/libgomp.c++/metadirective-template-1.C new file mode 100644 index 00000000000..2d826574861 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/metadirective-template-1.C @@ -0,0 +1,37 @@ +# include + +template int +fib (int n) +{ + int i, j; + if (n < 2) + return n; + else if ( tasking && n < 8 ) // serial/taskless cutoff for n<8 + return fib (n); + else + { +#pragma omp metadirective \ + when (user = {condition (tasking)}: task shared(i)) + i = fib (n - 1); +#pragma omp metadirective \ + when (user = {condition (score(10): tasking)}: task shared(j)) + j = fib (n - 2); +#pragma omp metadirective \ + when (user = {condition (tasking)}: taskwait) + return i + j; + } +} + +int main () +{ + int n = 15, o = 610; +#pragma omp parallel +#pragma omp single + { + if (fib (n) != o) + __builtin_abort (); + if (fib (n) != o) + __builtin_abort (); + } + return 0; +} diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-2.C b/libgomp/testsuite/libgomp.c++/metadirective-template-2.C new file mode 100644 index 00000000000..9134fefc7ac --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/metadirective-template-2.C @@ -0,0 +1,41 @@ +# include + +template int +fib (int n, bool flag) +{ + int i, j; + if (n < 2) + return n; + else if ( tasking && flag && n < 8 ) // serial/taskless cutoff for n<8 + return fib (n, false); + else + { +#pragma omp metadirective \ + when (user = {condition (tasking && flag)}: task shared(i)) + i = fib (n - 1, flag); +#pragma omp metadirective \ + when (user = {condition (score(10): tasking && flag)}: task shared(j)) + j = fib (n - 2, flag); +#pragma omp metadirective \ + when (user = {condition (tasking && flag)}: taskwait) + return i + j; + } +} + +int main () +{ + int n = 15, o = 610; +#pragma omp parallel +#pragma omp single + { + if (fib (n, true) != o) + __builtin_abort (); + if (fib (n, false) != o) + __builtin_abort (); + if (fib (n, false) != o) + __builtin_abort (); + if (fib (n, true) != o) + __builtin_abort (); + } + return 0; +} diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-3.C b/libgomp/testsuite/libgomp.c++/metadirective-template-3.C new file mode 100644 index 00000000000..cc48fde0fda --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/metadirective-template-3.C @@ -0,0 +1,41 @@ +# include + +template int +fib (int n, bool flag) +{ + int i, j; + if (n < 2) + return n; + else if ( tasking && flag && n < 8 ) // serial/taskless cutoff for n<8 + return fib (n, false); + else + { +#pragma omp metadirective \ + when (user = {condition (tasking && flag)}: task shared(i)) \ + when (user = {condition (!tasking && !flag)}: nothing) \ + otherwise (error at(execution) message("oops 1")) + i = fib (n - 1, flag); +#pragma omp metadirective \ + when (user = {condition (score(10): tasking && flag)}: task shared(j)) \ + when (user = {condition (tasking || flag)} : \ + error at(execution) message ("oops 2")) + j = fib (n - 2, flag); +#pragma omp metadirective \ + when (user = {condition (tasking && flag)}: taskwait) + return i + j; + } +} + +int main () +{ + int n = 15, o = 610; +#pragma omp parallel +#pragma omp single + { + if (fib (n, true) != o) + __builtin_abort (); + if (fib (n, false) != o) + __builtin_abort (); + } + return 0; +} From patchwork Sat May 4 21:21:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 1931425 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=p4Itnmhw; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX0zf6bkcz1ybC for ; Sun, 5 May 2024 07:24:14 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 228923858C53 for ; Sat, 4 May 2024 21:24:13 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-il1-x133.google.com (mail-il1-x133.google.com [IPv6:2607:f8b0:4864:20::133]) by sourceware.org (Postfix) with ESMTPS id 9C3F83844073 for ; Sat, 4 May 2024 21:22:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 9C3F83844073 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 9C3F83844073 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::133 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857752; cv=none; b=eUjyivFuGj0AsEVyMwZbAVEFnOpYFh29E6ciI40jnBsoF5NLpx8tEXyF/cRUFiUI4bGmnCUepNUlUVuRaLAiAZXUEB3ms9yvFDcQgN/ZQ+NvUpMn6CbvWBL/fLbvnp/U1tZMfKEnOvjIScRMYpqUTRSIKI+ti9nssvy1ODWVPv0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857752; c=relaxed/simple; bh=T/iAEUFiqLHZ9/HiIkjxUtWlpyjidb4X05n9VPa/zo0=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=tDSlZ8y8C7oNNHbgWfwwTXYZbnUzZMfCRLmmRwWiUxZQb1oQynBdBl8XRc2jkop7vTYa5wFTU5h2dAfMKFVSRj491+F54BtAWJxGapf/9rMuk4lAZCvHkXifluM3B9xo4JB3MobhimmLlFkQ9S8XbCk2EdgTBTSKA/WjngW3Uyo= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-il1-x133.google.com with SMTP id e9e14a558f8ab-36c791e9faaso2727525ab.1 for ; Sat, 04 May 2024 14:22:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857736; x=1715462536; 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=6nn0rIKxBt8J8RXzFm8P3tAV9vuSMqoKxTcHGGXBbxw=; b=p4Itnmhwj7D11Smd6KCVDK7h6+i+K7onlcbkok4EI4S4GGU/kokN+7WTDF+JHgk8am urHjMPPj4eC2ljV+oqvqRCp2LQ5b65X2qWwjIHc+XMaXYJ9Yy6C3PAVazOnqNZDfBTiZ QuMI1uPCaY3mt5uyAJ2s2gGzQvqnx8C7HLBWpb2Q3UljBKaR83cbHTaw7XZQYg7Ey+3R ZSG56y/er7cT0XvbUJKWhFHCqsM6Sr617tIgthBKooCru8wWm3EyUblnk2SWWFDx4elE 5WbWDTo+o6aA+3hlIRoxv+/b0gQHSY1GV5e0STMQTOkRdboKsMn/OhworOLvaID8DH/I Fv/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857736; x=1715462536; 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=6nn0rIKxBt8J8RXzFm8P3tAV9vuSMqoKxTcHGGXBbxw=; b=rFbF9oJhpAiwO39afhhtkWE6DYwojPneFhtVh34w67azMHsp9fmPvBRC98g2Uxqbt3 47KlVqQdK47wkL+Obuw8+LYXKmzlg3tJmL+kHW630rbNGkU0xgmcua4YXoBn1loqHO0c DP9LiSOUE8Nwj/ITE5DdiZzKWmESyQNfBId6STHw0LQhjntdaMxm4h/CktRxCNO/f3W7 +xlly8kxuxzH23tHSpcTF77lP1WfG9u1k7PxVf0M1p8BAbgLJrsytiKrdsuDGfMbZUx8 ZbA9FUgpIaxX60B02gEMCWAHaqxzyWUunmCQwdo+I+EmirgB6U3BrJ+ZK2fQqlG8L4Bu +Vew== X-Gm-Message-State: AOJu0Yw+nhJCcwY82HkrHofCvxpsB2AKF8hUJIAJXePekMlYQu0xaaNh 2Czk4gfXc1dXG7AotklLZRKUIdCZ1dQ7CngQV9E0LNkZ3hevItzjF3b//i7WiDe9l+g7AWmU8DU t X-Google-Smtp-Source: AGHT+IHxTbd4PfAjGq+JMH1VZrCp3e73EPinMK89Fhu4p6XPwH4AZozVEMLQUZFD5dFug/u+W1cixg== X-Received: by 2002:a05:6e02:1a89:b0:36c:4d7d:26c2 with SMTP id k9-20020a056e021a8900b0036c4d7d26c2mr7376947ilv.22.1714857730888; Sat, 04 May 2024 14:22:10 -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.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:10 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 06/12] OpenMP: common c/c++ testcases for metadirectives Date: Sat, 4 May 2024 15:21:46 -0600 Message-Id: <20240504212153.3561429-7-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 X-Spam-Status: No, score=-11.2 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 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+incoming=patchwork.ozlabs.org@gcc.gnu.org gcc/testsuite/ChangeLog * c-c++-common/gomp/metadirective-1.c: New. * c-c++-common/gomp/metadirective-2.c: New. * c-c++-common/gomp/metadirective-3.c: New. * c-c++-common/gomp/metadirective-4.c: New. * c-c++-common/gomp/metadirective-5.c: New. * c-c++-common/gomp/metadirective-6.c: New. * c-c++-common/gomp/metadirective-7.c: New. * c-c++-common/gomp/metadirective-8.c: New. * c-c++-common/gomp/metadirective-construct.c: New. * c-c++-common/gomp/metadirective-device.c: New. * c-c++-common/gomp/metadirective-no-score.c: New. * c-c++-common/gomp/metadirective-target-device.c: New. libgomp/ChangeLog * testsuite/libgomp.c-c++-common/metadirective-1.c: New. * testsuite/libgomp.c-c++-common/metadirective-2.c: New. * testsuite/libgomp.c-c++-common/metadirective-3.c: New. * testsuite/libgomp.c-c++-common/metadirective-4.c: New. * testsuite/libgomp.c-c++-common/metadirective-5.c: New. Co-Authored-By: Kwok Cheung Yeung Co-Authored-By: Sandra Loosemore --- .../c-c++-common/gomp/metadirective-1.c | 52 +++++ .../c-c++-common/gomp/metadirective-2.c | 74 ++++++++ .../c-c++-common/gomp/metadirective-3.c | 31 +++ .../c-c++-common/gomp/metadirective-4.c | 40 ++++ .../c-c++-common/gomp/metadirective-5.c | 24 +++ .../c-c++-common/gomp/metadirective-6.c | 31 +++ .../c-c++-common/gomp/metadirective-7.c | 31 +++ .../c-c++-common/gomp/metadirective-8.c | 16 ++ .../gomp/metadirective-construct.c | 177 ++++++++++++++++++ .../c-c++-common/gomp/metadirective-device.c | 147 +++++++++++++++ .../gomp/metadirective-no-score.c | 95 ++++++++++ .../gomp/metadirective-target-device.c | 147 +++++++++++++++ .../libgomp.c-c++-common/metadirective-1.c | 35 ++++ .../libgomp.c-c++-common/metadirective-2.c | 41 ++++ .../libgomp.c-c++-common/metadirective-3.c | 34 ++++ .../libgomp.c-c++-common/metadirective-4.c | 52 +++++ .../libgomp.c-c++-common/metadirective-5.c | 46 +++++ 17 files changed, 1073 insertions(+) create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-1.c create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-2.c create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-3.c create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-4.c create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-5.c create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-6.c create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-7.c create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-8.c create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-construct.c create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-device.c create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-no-score.c create mode 100644 gcc/testsuite/c-c++-common/gomp/metadirective-target-device.c create mode 100644 libgomp/testsuite/libgomp.c-c++-common/metadirective-1.c create mode 100644 libgomp/testsuite/libgomp.c-c++-common/metadirective-2.c create mode 100644 libgomp/testsuite/libgomp.c-c++-common/metadirective-3.c create mode 100644 libgomp/testsuite/libgomp.c-c++-common/metadirective-4.c create mode 100644 libgomp/testsuite/libgomp.c-c++-common/metadirective-5.c diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-1.c b/gcc/testsuite/c-c++-common/gomp/metadirective-1.c new file mode 100644 index 00000000000..37b56237531 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-1.c @@ -0,0 +1,52 @@ +/* { dg-do compile } */ + +#define N 100 + +void f (int a[], int b[], int c[]) +{ + int i; + + #pragma omp metadirective \ + default (teams loop) \ + default (parallel loop) /* { dg-error "too many 'otherwise' or 'default' clauses in 'metadirective'" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + #pragma omp metadirective \ + otherwise (teams loop) \ + default (parallel loop) /* { dg-error "too many 'otherwise' or 'default' clauses in 'metadirective'" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + #pragma omp metadirective \ + otherwise (teams loop) \ + otherwise (parallel loop) /* { dg-error "too many 'otherwise' or 'default' clauses in 'metadirective'" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + #pragma omp metadirective \ + default (bad_directive) /* { dg-error "unknown directive name before '\\)' token" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + #pragma omp metadirective \ + default (teams loop) \ + where (device={arch("nvptx")}: parallel loop) /* { dg-error "'where' is not valid for 'metadirective'" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + #pragma omp metadirective \ + default (teams loop) \ + when (device={arch("nvptx")} parallel loop) /* { dg-error "expected ':' before 'parallel'" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + #pragma omp metadirective \ + default (metadirective default (flush)) /* { dg-error "metadirectives cannot be used as variants of a 'metadirective' before 'default'" } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; + + /* Test improperly nested metadirectives - even though the second + metadirective resolves to 'omp nothing', that is not the same as there + being literally nothing there. */ + #pragma omp metadirective \ + when (implementation={vendor("gnu")}: parallel for) + #pragma omp metadirective \ + when (implementation={vendor("cray")}: parallel for) + /* { dg-error "for statement expected before '#pragma'" "" { target c } .-2 } */ + /* { dg-error "'#pragma' is not allowed here" "" { target c++ } .-3 } */ + for (i = 0; i < N; i++) c[i] = a[i] * b[i]; +} diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-2.c b/gcc/testsuite/c-c++-common/gomp/metadirective-2.c new file mode 100644 index 00000000000..ea6904c9c12 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-2.c @@ -0,0 +1,74 @@ +/* { dg-do compile } */ + +#define N 100 + +int main (void) +{ + int x = 0; + int y = 0; + + /* Test implicit default (nothing). */ + #pragma omp metadirective \ + when (device={arch("nvptx")}: barrier) + x = 1; + + /* Test with multiple standalone directives. */ + #pragma omp metadirective \ + when (device={arch("nvptx")}: barrier) \ + default (flush) + x = 1; + + /* Test combining a standalone directive with one that takes a statement + body. */ + #pragma omp metadirective \ + when (device={arch("nvptx")}: parallel) \ + default (barrier) + x = 1; + + /* Test combining a standalone directive with one that takes a for loop. */ + #pragma omp metadirective \ + when (device={arch("nvptx")}: parallel for) \ + default (barrier) + for (int i = 0; i < N; i++) + x += i; + + /* Test combining a directive that takes a for loop with one that takes + a regular statement body. */ + #pragma omp metadirective \ + when (device={arch("nvptx")}: parallel for) \ + default (parallel) + for (int i = 0; i < N; i++) + x += i; + + /* Test labels 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)) + { + if (x) + goto l1; + else + goto l2; + l1: ; + l2: ; + } + + /* Test local labels 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)) + { + //__label__ l1, l2; + + if (x) + goto l1; + else + goto l2; + l1: ; + l2: ; + } + + return 0; +} diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-3.c b/gcc/testsuite/c-c++-common/gomp/metadirective-3.c new file mode 100644 index 00000000000..7a2818dd710 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-3.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-original" } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ +/* { dg-additional-options "-fdump-tree-optimized" } */ + +#define N 100 + +void f (int x[], int y[], int z[]) +{ + int i; + + #pragma omp target map(to: x, y) map(from: z) + #pragma omp metadirective \ + when (device={arch("nvptx")}: teams loop) \ + default (parallel loop) + for (i = 0; i < N; i++) + z[i] = x[i] * y[i]; +} + +/* The metadirective should be resolved after Gimplification. */ + +/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "when \\(device = .*arch.*nvptx.*\\):" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp teams" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "default:" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp loop" 2 "original" } } */ + +/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "gimple" } } */ + +/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "optimized" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-4.c b/gcc/testsuite/c-c++-common/gomp/metadirective-4.c new file mode 100644 index 00000000000..5fa86561a23 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-4.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-original" } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +#define N 100 + +#pragma omp declare target +void f(double a[], double x) { + int i; + + #pragma omp metadirective \ + when (construct={target}: distribute parallel for) \ + default (parallel for simd) + for (i = 0; i < N; i++) + a[i] = x * i; +} +#pragma omp end declare target + + int main() +{ + double a[N]; + + #pragma omp target teams map(from: a[0:N]) + f (a, 3.14159); + + /* TODO: This does not execute a version of f with the default clause + active as might be expected. */ + f (a, 2.71828); /* { dg-warning "direct calls to an offloadable function containing metadirectives with a 'construct={target}' selector may produce unexpected results" } */ + + return 0; + } + + /* The metadirective should be resolved during Gimplification. */ + +/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "when \\(construct = .*target.*\\):" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "default:" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 2 "original" } } */ + +/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-5.c b/gcc/testsuite/c-c++-common/gomp/metadirective-5.c new file mode 100644 index 00000000000..4a9f1aa85a6 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-5.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-original" } */ + +#define N 100 + +void f (int a[], int flag) +{ + int i; + #pragma omp metadirective \ + when (user={condition(flag)}: \ + target teams distribute parallel for map(from: a[0:N])) \ + default (parallel for) + for (i = 0; i < N; i++) + a[i] = i; +} + +/* The metadirective should be resolved at parse time. */ + +/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp target" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp teams" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp distribute" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp for" 2 "original" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-6.c b/gcc/testsuite/c-c++-common/gomp/metadirective-6.c new file mode 100644 index 00000000000..26c7a008e95 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-6.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-original" } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +#define N 100 + +void bar (int a[], int run_parallel, int run_guided) +{ + #pragma omp metadirective \ + when (user={condition(run_parallel)}: parallel) + { + int i; + #pragma omp metadirective \ + when (construct={parallel}, user={condition(run_guided)}: \ + for schedule(guided)) \ + when (construct={parallel}: for schedule(static)) + for (i = 0; i < N; i++) + a[i] = i; + } + } + +/* The outer metadirective should be resolved at parse time. */ +/* The inner metadirective should be resolved during Gimplificiation. */ + +/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp for" 4 "original" } } */ +/* { dg-final { scan-tree-dump-times "when \\(construct = .parallel" 4 "original" } } */ +/* { dg-final { scan-tree-dump-times "default:" 2 "original" } } */ + +/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-7.c b/gcc/testsuite/c-c++-common/gomp/metadirective-7.c new file mode 100644 index 00000000000..4e763cd03d4 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-7.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +#define N 256 + +void f (int a[], int num) +{ + int i; + + #pragma omp metadirective \ + when (target_device={device_num(num), kind("gpu"), arch("nvptx")}: \ + target parallel for map(tofrom: a[0:N])) \ + when (target_device={device_num(num), kind("gpu"), \ + arch("amdgcn"), isa("gfx906")}: \ + target parallel for) \ + when (target_device={device_num(num), kind("cpu"), arch("x86_64")}: \ + parallel for) + for (i = 0; i < N; i++) + a[i] += i; + + #pragma omp metadirective \ + when (target_device={kind("gpu"), arch("nvptx")}: \ + target parallel for map(tofrom: a[0:N])) + for (i = 0; i < N; i++) + a[i] += i; +} + +/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(num, &\"gpu.x00\"\\\[0\\\], &\"amdgcn.x00\"\\\[0\\\], &\"gfx906.x00\"\\\[0\\\]\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(num, &\"gpu.x00\"\\\[0\\\], &\"nvptx.x00\"\\\[0\\\], 0B\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(num, &\"cpu.x00\"\\\[0\\\], &\"x86_64.x00\"\\\[0\\\], 0B\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(-2, &\"gpu.x00\"\\\[0\\\], &\"nvptx.x00\"\\\[0\\\], 0B\\)" "gimple" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-8.c b/gcc/testsuite/c-c++-common/gomp/metadirective-8.c new file mode 100644 index 00000000000..c7d9c31ed73 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-8.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ + +#define N 256 + +void f () +{ + int i; + int a[N]; + + #pragma omp metadirective \ + when( device={kind(nohost)}: nothing ) \ + when( device={arch("nvptx")}: nothing) \ + default( parallel for) + for (i = 0; i < N; i++) + a[i] = i; +} diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-construct.c b/gcc/testsuite/c-c++-common/gomp/metadirective-construct.c new file mode 100644 index 00000000000..a61966aa899 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-construct.c @@ -0,0 +1,177 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-foffload=disable -fdump-tree-optimized" } */ + +#include + +static void +init (int n, double *a) +{ + for (int i = 0; i < n; i++) + a[i] = (double) i; +} + +static void +check (int n, double *a, double s) +{ + for (int i = 0; i < n; i++) + if (a[i] != (double) i * s) + abort (); +} + +typedef void (transform_fn) (int, double *, double); + +static void doit (transform_fn *f, int n, double *a, double s) +{ + init (n, a); + (*f) (n, a, s); + check (n, a, s); +} + +/* Check various combinations for enforcing correct ordering of + construct matches. */ +static void +f1 (int n, double* a, double s) +{ +#pragma omp target teams +#pragma omp parallel +#pragma omp metadirective \ + when (construct={target} \ + : for) \ + default (error at(execution) message("f1 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +static void +f2 (int n, double* a, double s) +{ +#pragma omp target teams +#pragma omp parallel +#pragma omp metadirective \ + when (construct={teams, parallel} \ + : for) \ + default (error at(execution) message("f2 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +static void +f3 (int n, double* a, double s) +{ +#pragma omp target teams +#pragma omp parallel +#pragma omp metadirective \ + when (construct={target, teams, parallel} \ + : for) \ + default (error at(execution) message("f3 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +static void +f4 (int n, double* a, double s) +{ +#pragma omp target teams +#pragma omp parallel +#pragma omp metadirective \ + when (construct={target, parallel} \ + : for) \ + default (error at(execution) message("f4 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +static void +f5 (int n, double* a, double s) +{ +#pragma omp target teams +#pragma omp parallel +#pragma omp metadirective \ + when (construct={target, teams} \ + : for) \ + default (error at(execution) message("f5 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +/* Next batch is for things where the construct doesn't match the context. */ +static void +f6 (int n, double* a, double s) +{ +#pragma omp target +#pragma omp teams +#pragma omp metadirective \ + when (construct={parallel} \ + : error at(execution) message("f6 match failed")) \ + default (parallel for) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +static void +f7 (int n, double* a, double s) +{ +#pragma omp target +#pragma omp teams +#pragma omp metadirective \ + when (construct={target, parallel} \ + : error at(execution) message("f7 match failed")) \ + default (parallel for) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +static void +f8 (int n, double* a, double s) +{ +#pragma omp target +#pragma omp teams +#pragma omp metadirective \ + when (construct={parallel, target} \ + : error at(execution) message("f8 match failed")) \ + default (parallel for) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +/* Next test choosing the best alternative when there are multiple + matches. */ +static void +f9 (int n, double* a, double s) +{ +#pragma omp target teams +#pragma omp parallel +#pragma omp metadirective \ + when (construct={teams, parallel} \ + : error at(execution) message("f9 match incorrect 1")) \ + when (construct={target, teams, parallel} \ + : for) \ + when (construct={target, teams} \ + : error at(execution) message("f9 match incorrect 2")) \ + default (error at(execution) message("f9 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +/* Note there are no tests for the matching the extended simd clause + syntax, which is only useful for "declare variant". */ + +#define N 10 +#define S 2.0 + +int main (void) +{ + double a[N]; + doit (f1, N, a, S); + doit (f2, N, a, S); + doit (f3, N, a, S); + doit (f4, N, a, S); + doit (f5, N, a, S); + doit (f6, N, a, S); + doit (f7, N, a, S); + doit (f8, N, a, S); + doit (f9, N, a, S); +} + +/* All the error calls should be optimized away. */ +/* { dg-final { scan-tree-dump-not "__builtin_GOMP_error" "optimized" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-device.c b/gcc/testsuite/c-c++-common/gomp/metadirective-device.c new file mode 100644 index 00000000000..9261331260c --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-device.c @@ -0,0 +1,147 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-foffload=disable -fdump-tree-optimized" } */ +/* { dg-additional-options "-DDEVICE_ARCH=x86_64 -DDEVICE_ISA=sse -msse" { target x86_64-*-* } } */ + +#include + +static void +init (int n, double *a) +{ + for (int i = 0; i < n; i++) + a[i] = (double) i; +} + +static void +check (int n, double *a, double s) +{ + for (int i = 0; i < n; i++) + if (a[i] != (double) i * s) + abort (); +} + +typedef void (transform_fn) (int, double *, double); + +static void doit (transform_fn *f, int n, double *a, double s) +{ + init (n, a); + (*f) (n, a, s); + check (n, a, s); +} + +/* Check kind=host matches (with offloading disabled). */ +static void +f1 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (device={kind(host)} \ + : parallel for) \ + default (error at(execution) message("f1 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +/* Check kind=nohost does not match (with offloading disabled). */ +static void +f2 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (device={kind(nohost)} \ + : error at(execution) message("f2 match failed")) \ + default (parallel for) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +/* Check arch. Either DEVICE_ARCH is defined by command-line option, + or we know it is not x86_64. */ +#ifdef DEVICE_ARCH +static void +f3 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (device={arch(DEVICE_ARCH)} \ + : parallel for) \ + default (error at(execution) message("f3 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#else +static void +f3 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (device={arch("x86_64")} \ + : error at(execution) message("f3 match failed")) \ + default (parallel for) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#endif + +/* Check both kind and arch together. */ +#ifdef DEVICE_ARCH +static void +f4 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (device={arch(DEVICE_ARCH), kind(host)} \ + : parallel for) \ + default (error at(execution) message("f4 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#else +static void +f4 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (device={arch("x86_64"), kind(host)} \ + : error at(execution) message("f4 match failed")) \ + default (parallel for) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#endif + +/* Check kind, arch, and ISA together. */ +#if defined(DEVICE_ARCH) && defined(DEVICE_ISA) +static void +f5 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (device={arch(DEVICE_ARCH), kind(host), isa(DEVICE_ISA)} \ + : parallel for) \ + default (error at(execution) message("f5 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#else +static void +f5 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (device={arch("x86_64"), kind(host), isa("sse")} \ + : error at(execution) message("f5 match failed")) \ + default (parallel for) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#endif + +#define N 10 +#define S 2.0 + +int main (void) +{ + double a[N]; + doit (f1, N, a, S); + doit (f2, N, a, S); + doit (f3, N, a, S); + doit (f4, N, a, S); + doit (f5, N, a, S); +} + +/* All the metadirectives involving the device selector should be + fully resolved and the error calls optimized away. */ + +/* { dg-final { scan-tree-dump-not "__builtin_GOMP_error" "optimized" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-no-score.c b/gcc/testsuite/c-c++-common/gomp/metadirective-no-score.c new file mode 100644 index 00000000000..1f1053eaffa --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-no-score.c @@ -0,0 +1,95 @@ +/* { dg-do compile { target x86_64-*-* } } */ +/* { dg-additional-options "-foffload=disable" } */ + +/* This test is expected to fail with compile-time errors: + "A trait-score cannot be specified in traits from the construct, + device or target_device trait-selector-sets." */ + +/* Define this to avoid dependence on libgomp header files. */ + +#define omp_initial_device -1 + +void +f1 (int n, double *a, double s) +{ +#pragma omp metadirective \ + when (device={kind (score(5) : host)} \ + : parallel for) + /* { dg-error ".score. cannot be specified in traits in the .device. trait-selector-set" "" { target *-*-*} .-2 } */ + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +void +f2 (int n, double *a, double s) +{ +#pragma omp metadirective \ + when (device={kind (host), arch (score(6) : x86_64), isa (avx512f)} \ + : parallel for) + /* { dg-error ".score. cannot be specified in traits in the .device. trait-selector-set" "" { target *-*-*} .-2 } */ + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +void +f3 (int n, double *a, double s) +{ +#pragma omp metadirective \ + when (device={kind (host), arch (score(6) : x86_64), \ + isa (score(7): avx512f)} \ + : parallel for) + /* { dg-error ".score. cannot be specified in traits in the .device. trait-selector-set" "" { target *-*-*} .-3 } */ + /* { dg-error ".score. cannot be specified in traits in the .device. trait-selector-set" "" { target *-*-*} .-3 } */ + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +void +f4 (int n, double *a, double s) +{ +#pragma omp metadirective \ + when (target_device={device_num (score(42) : omp_initial_device), \ + kind (host)} \ + : parallel for) + /* { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-3 } */ + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +void +f5 (int n, double *a, double s) +{ +#pragma omp metadirective \ + when (target_device={device_num(omp_initial_device), \ + kind (score(5) : host)} \ + : parallel for) + /* { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-2 } */ + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +void +f6 (int n, double *a, double s) +{ +#pragma omp metadirective \ + when (target_device={device_num(omp_initial_device), kind (host), \ + arch (score(6) : x86_64), isa (avx512f)} \ + : parallel for) + /* { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-2 } */ + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +void +f7 (int n, double *a, double s) +{ +#pragma omp metadirective \ + when (target_device={device_num(omp_initial_device), kind (host), \ + arch (score(6) : x86_64), \ + isa (score(7): avx512f)} \ + : parallel for) + /* { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-3 } */ + /* { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-3 } */ + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-target-device.c b/gcc/testsuite/c-c++-common/gomp/metadirective-target-device.c new file mode 100644 index 00000000000..335db416a4b --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-target-device.c @@ -0,0 +1,147 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-foffload=disable -fdump-tree-optimized" } */ +/* { dg-additional-options "-DDEVICE_ARCH=x86_64 -DDEVICE_ISA=mmx -mmmx" { target x86_64-*-* } } */ + +#include + +static void +init (int n, double *a) +{ + for (int i = 0; i < n; i++) + a[i] = (double) i; +} + +static void +check (int n, double *a, double s) +{ + for (int i = 0; i < n; i++) + if (a[i] != (double) i * s) + abort (); +} + +typedef void (transform_fn) (int, double *, double); + +static void doit (transform_fn *f, int n, double *a, double s) +{ + init (n, a); + (*f) (n, a, s); + check (n, a, s); +} + +/* Check kind=host matches (with offloading disabled). */ +static void +f1 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (target_device={kind(host)} \ + : parallel for) \ + default (error at(execution) message("f1 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +/* Check kind=nohost does not match (with offloading disabled). */ +static void +f2 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (target_device={kind(nohost)} \ + : error at(execution) message("f2 match failed")) \ + default (parallel for) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} + +/* Check arch. Either DEVICE_ARCH is defined by command-line option, + or we know it is not x86_64. */ +#ifdef DEVICE_ARCH +static void +f3 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (target_device={arch(DEVICE_ARCH)} \ + : parallel for) \ + default (error at(execution) message("f3 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#else +static void +f3 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (target_device={arch("x86_64")} \ + : error at(execution) message("f3 match failed")) \ + default (parallel for) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#endif + +/* Check both kind and arch together. */ +#ifdef DEVICE_ARCH +static void +f4 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (target_device={arch(DEVICE_ARCH), kind(host)} \ + : parallel for) \ + default (error at(execution) message("f4 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#else +static void +f4 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (target_device={arch("x86_64"), kind(host)} \ + : error at(execution) message("f4 match failed")) \ + default (parallel for) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#endif + +/* Check kind, arch, and ISA together. */ +#if defined(DEVICE_ARCH) && defined(DEVICE_ISA) +static void +f5 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (target_device={arch(DEVICE_ARCH), kind(host), isa(DEVICE_ISA)} \ + : parallel for) \ + default (error at(execution) message("f5 match failed")) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#else +static void +f5 (int n, double* a, double s) +{ +#pragma omp metadirective \ + when (target_device={arch("x86_64"), kind(host), isa("mmx")} \ + : error at(execution) message("f5 match failed")) \ + default (parallel for) + for (int i = 0; i < n; i++) + a[i] = a[i] * s; +} +#endif + +#define N 10 +#define S 2.0 + +int main (void) +{ + double a[N]; + doit (f1, N, a, S); + doit (f2, N, a, S); + doit (f3, N, a, S); + doit (f4, N, a, S); + doit (f5, N, a, S); +} + +/* Since the target_device selector is dynamic, none of + error checks tests can be optimized away. */ + +/* { dg-final { scan-tree-dump-times "__builtin_GOMP_error" 5 "optimized" } } */ diff --git a/libgomp/testsuite/libgomp.c-c++-common/metadirective-1.c b/libgomp/testsuite/libgomp.c-c++-common/metadirective-1.c new file mode 100644 index 00000000000..0de59cbe3d3 --- /dev/null +++ b/libgomp/testsuite/libgomp.c-c++-common/metadirective-1.c @@ -0,0 +1,35 @@ +/* { dg-do run } */ + +#define N 100 + +void f (int x[], int y[], int z[]) +{ + int i; + + #pragma omp target map(to: x[0:N], y[0:N]) map(from: z[0:N]) + #pragma omp metadirective \ + when (device={arch("nvptx")}: teams loop) \ + default (parallel loop) + for (i = 0; i < N; i++) + z[i] = x[i] * y[i]; +} + +int main (void) +{ + int x[N], y[N], z[N]; + int i; + + for (i = 0; i < N; i++) + { + x[i] = i; + y[i] = -i; + } + + f (x, y, z); + + for (i = 0; i < N; i++) + if (z[i] != x[i] * y[i]) + return 1; + + return 0; +} diff --git a/libgomp/testsuite/libgomp.c-c++-common/metadirective-2.c b/libgomp/testsuite/libgomp.c-c++-common/metadirective-2.c new file mode 100644 index 00000000000..55a6098e525 --- /dev/null +++ b/libgomp/testsuite/libgomp.c-c++-common/metadirective-2.c @@ -0,0 +1,41 @@ +/* { dg-do run } */ + +#include + +#define N 100 +#define EPSILON 0.001 + +#pragma omp declare target +void f(double a[], double x) { + int i; + + #pragma omp metadirective \ + when (construct={target}: distribute parallel for) \ + default (parallel for simd) + for (i = 0; i < N; i++) + a[i] = x * i; +} +#pragma omp end declare target + + int main() +{ + double a[N]; + int i; + + #pragma omp target teams map(from: a[0:N]) + f (a, M_PI); + + for (i = 0; i < N; i++) + if (fabs (a[i] - (M_PI * i)) > EPSILON) + return 1; + + /* TODO: This does not execute a version of f with the default clause + active as might be expected. */ + f (a, M_E); /* { dg-warning "direct calls to an offloadable function containing metadirectives with a 'construct={target}' selector may produce unexpected results" } */ + + for (i = 0; i < N; i++) + if (fabs (a[i] - (M_E * i)) > EPSILON) + return 1; + + return 0; + } diff --git a/libgomp/testsuite/libgomp.c-c++-common/metadirective-3.c b/libgomp/testsuite/libgomp.c-c++-common/metadirective-3.c new file mode 100644 index 00000000000..e31daf2cb64 --- /dev/null +++ b/libgomp/testsuite/libgomp.c-c++-common/metadirective-3.c @@ -0,0 +1,34 @@ +/* { dg-do run } */ + +#define N 100 + +int f (int a[], int flag) +{ + int i; + int res = 0; + + #pragma omp metadirective \ + when (user={condition(!flag)}: \ + target teams distribute parallel for \ + map(from: a[0:N]) private(res)) \ + default (parallel for) + for (i = 0; i < N; i++) + { + a[i] = i; + res = 1; + } + + return res; +} + +int main (void) +{ + int a[N]; + + if (f (a, 0)) + return 1; + if (!f (a, 1)) + return 1; + + return 0; +} diff --git a/libgomp/testsuite/libgomp.c-c++-common/metadirective-4.c b/libgomp/testsuite/libgomp.c-c++-common/metadirective-4.c new file mode 100644 index 00000000000..7fc601eaba6 --- /dev/null +++ b/libgomp/testsuite/libgomp.c-c++-common/metadirective-4.c @@ -0,0 +1,52 @@ +/* { dg-do run } */ + +#include + +#define N 100 + +int f (int a[], int run_parallel, int run_static) +{ + int is_parallel = 0; + int is_static = 0; + + #pragma omp metadirective \ + when (user={condition(run_parallel)}: parallel) + { + int i; + + if (omp_in_parallel ()) + is_parallel = 1; + + #pragma omp metadirective \ + when (construct={parallel}, user={condition(!run_static)}: \ + for schedule(guided) private(is_static)) \ + when (construct={parallel}: for schedule(static)) + for (i = 0; i < N; i++) + { + a[i] = i; + is_static = 1; + } + } + + return (is_parallel << 1) | is_static; +} + +int main (void) +{ + int a[N]; + + /* is_static is always set if run_parallel is false. */ + if (f (a, 0, 0) != 1) + return 1; + + if (f (a, 0, 1) != 1) + return 1; + + if (f (a, 1, 0) != 2) + return 1; + + if (f (a, 1, 1) != 3) + return 1; + + return 0; +} diff --git a/libgomp/testsuite/libgomp.c-c++-common/metadirective-5.c b/libgomp/testsuite/libgomp.c-c++-common/metadirective-5.c new file mode 100644 index 00000000000..e8ab7ccb166 --- /dev/null +++ b/libgomp/testsuite/libgomp.c-c++-common/metadirective-5.c @@ -0,0 +1,46 @@ +/* { dg-do run } */ + +#define N 100 + +#include +#include + +int f(int a[], int num) +{ + int on_device = 0; + int i; + + #pragma omp metadirective \ + when (target_device={device_num(num), kind("gpu")}: \ + target parallel for map(to: a[0:N]), map(from: on_device)) \ + default (parallel for private (on_device)) + for (i = 0; i < N; i++) + { + a[i] += i; + on_device = 1; + } + + return on_device; +} + +int main (void) +{ + int a[N]; + int on_device_count = 0; + int i; + + for (i = 0; i < N; i++) + a[i] = i; + + for (i = 0; i <= omp_get_num_devices (); i++) + on_device_count += f (a, i); + + if (on_device_count != omp_get_num_devices ()) + return 1; + + for (i = 0; i < N; i++) + if (a[i] != 2 * i) + return 2; + + return 0; +} From patchwork Sat May 4 21:21:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 1931429 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=CQ5chUnz; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX11W52xwz1xnT for ; Sun, 5 May 2024 07:25:51 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D68053846079 for ; Sat, 4 May 2024 21:25:49 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-il1-x12c.google.com (mail-il1-x12c.google.com [IPv6:2607:f8b0:4864:20::12c]) by sourceware.org (Postfix) with ESMTPS id D9D3B3844754 for ; Sat, 4 May 2024 21:22:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D9D3B3844754 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 D9D3B3844754 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::12c ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857753; cv=none; b=x5jIwvamob9lW0vZpjHJKOAEUo62Dchw1u/POsJmRRp2NB7q9yOukWvfR29nkS5yB4MfiC9DXwmhcXWN3KWSYlJj+nUM6+z57rooG1ape0XDPZQArRBB7bMz4NPSJAYElLC10K5E5GOyutUpLQV+7VkS5r/g+emmaIdLndJE8AQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857753; c=relaxed/simple; bh=C1ZdRvms5pAUZ47+qkT28enhvbTZ0PiBP/yriRpCclw=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=DB01XV484nGNQKUV1AtGuyDcnBUv06QJCHJVaI7eGXuK7GjfD+ISxdxKYekB3YZFsdqMNPJn2z9np67lBOVqSwbEoYcNChCBlvyDiDCyW5kBBT5AK9v6QbEOQAvaTHPbNmmxJJyU8zBpBpMXdFfWfTsVU+5GyZctvLFZWi5l02E= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-il1-x12c.google.com with SMTP id e9e14a558f8ab-36c82cf5cdeso3029515ab.0 for ; Sat, 04 May 2024 14:22:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857739; x=1715462539; 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=xL4giRTJ3QSsKhfo3STpX5Ji+034iuMRILlmc5eABrI=; b=CQ5chUnzCnXKb9EKac+yMhz1QBJWXZnQY7fE7eDMjvk1CqyYQ9D/gpcPCvEqvqVmqk EeK6x5mS1E2MHdxpCtRpvOV6qZeXtxciE+6nuT+h/J3DXzWIssrrh6b7ddDgnEERbxUG /LFRD7j4cRER4sShIpsVcPEilcLE8sz7bttlNIjNKEv7zdCRmGmg8vg6pp58Mx04OBwy +iR4X+EiBGhRZh8PaeObBME/jZfVRhEnHWl5QLVkcIvvvYrqHG650wcOvdKBdiA7JJq+ 3SVqRlUZ4/2FsnpPamlXPIEymqGHD3+YKQqdNkYLL+xG2zJKZePjuxYol7XiWvrK/KJ3 3ybg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857739; x=1715462539; 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=xL4giRTJ3QSsKhfo3STpX5Ji+034iuMRILlmc5eABrI=; b=Cd9GY6+k+v1TgnyoVjgL87FoqkFxVyRL4tO91WUm/T6a+mJbjywe5dqLWj4gEbyTzE B8KzubwNAN+gf4AywK5bN45HByQvW3ZZvrXRZrvD10D7MyRPhtp26I6fdrneUB2+SSSL E1HtyLU4gS9FP3WSRvvGxT1gL2o2EjSwsJlSKaPIim11jpyvxSBkOGcBriRN1c6RaDw+ n0Viu4Z/8q26wVCJbSzGOH+qb6JAO/FbJhbHpgPhd39t7alna+i5tYridBdD0sKuNpvt vBFS6/6GnJg5VEEkeMLvEMrLGRK132/gXnVSwKWsUDRMCAQPfLvK+F1ksXsbvO4sEIZp nN+A== X-Gm-Message-State: AOJu0Yzzqcih6z7PdFC3PWzEDaQdubHGp6U+JokGX3HdhBv/9Cy3vWhv /fe6hAKPcxeK7eXp4j+O+emT9RAdfE1SKNM5qgt3K+cWbs1x52M4fpF4bz3uDFLcjn6+P0kJzpV 7 X-Google-Smtp-Source: AGHT+IEfp79STE2Fa5UJA4pykRrK6qSsk87ep5PWVtTxcTwxXNsymNDt624jaJN9twviyvK00cRcmQ== X-Received: by 2002:a05:6e02:1a04:b0:36c:4ca4:2871 with SMTP id s4-20020a056e021a0400b0036c4ca42871mr9839772ild.4.1714857737334; Sat, 04 May 2024 14:22:17 -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.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:16 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 07/12] OpenMP: Fortran front-end support for metadirectives. Date: Sat, 4 May 2024 15:21:47 -0600 Message-Id: <20240504212153.3561429-8-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 X-Spam-Status: No, score=-10.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLACK 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+incoming=patchwork.ozlabs.org@gcc.gnu.org This patch adds support for metadirectives to the Fortran front end. gcc/fortran/ChangeLog * decl.cc (gfc_match_end): Handle metadirectives. * dump-parse-tree.cc (show_omp_node): Likewise. (show_code_node): Likewise. * gfortran.h (enum gfc_statement): Add ST_OMP_METADIRECTIVE. (struct gfc_omp_clauses): Rename target_first_st_is_teams field to target_first_st_is_teams_or_meta. (struct gfc_omp_variant): New. (struct gfc_st_label): Add omp_region field. (gfc_exec_op): Add EXEC_OMP_METADIRECTIVE. (struct gfc_code): Add omp_variants field. (gfc_free_omp_variants): Declare. (match_omp_directive): Declare. (is_omp_declarative_stmt): Declare. * io.cc (format_asterisk): Add initializer for new omp_region field. * match.h (gfc_match_omp_begin_metadirective): Declare. (gfc_match_omp_metadirective): Declare. * openmp.cc (gfc_match_omp_eos): Special case for matching an OpenMP context selector. (gfc_free_omp_variants): New. (gfc_match_omp_clauses): Remove context_selector parameter. (match_omp): Adjust call to gfc_match_omp_clauses. (gfc_match_omp_context_selector): Add metadirective_p parameter. Adjust error-checking logic and calls to gfc_match_omp_clauses. Set gfc_matching_omp_context_selector. (gfc_match_omp_context_selector_specification): Generalize to take a set selector list pointer as parameter, instead of a declare variant pointer. (gfc_match_omp_declare_variant): Adjust call to match above change. (match_omp_metadirective): New. (gfc_match_omp_begin_metadirective): New. (gfc_match_omp_metadirective): New. (resolve_omp_metadirective): New. (resolve_omp_target): Handle metadirectives. (gfc_resolve_omp_directive): Handle metadirectives. * parse.cc (gfc_matching_omp_context_selector): New. (gfc_in_metadirective_body): New. (gfc_omp_region_count): New. (decode_omp_directive): Handle "begin metadirective", "end metadirective", and "metadirective". (match_omp_directive): New. (case_omp_structured_block): New define. (case_omp_do): New define. (gfc_ascii_statement): Handle ST_OMP_BEGIN_METADIRECTIVE, ST_OMP_END_METADIRECTIVE, and ST_OMP_METADIRECTIVE. (accept_statement): Handle ST_OMP_BEGIN_METADIRECTIVE and ST_OMP_METADIRECTIVE. (gfc_omp_end_stmt): New. (parse_omp_do): Use gfc_omp_end_stmt. Special-case "omp end metadirective" to end the current construct. (parse_omp_structured_block): Likewise. Adjust setting of target_first_st_is_teams_or_meta flag. (parse_omp_metadirective_body): New. (parse_executable): Handle metadirectives. Use case_omp_structured_block and case_omp_do here. (gfc_parse_file): Initialize gfc_omp_region_count, gfc_in_metadirective_body, and gfc_matching_omp_context_selector. (is_omp_declarative_stmt): New. * parse.h (enum gfc_compile_state): Add metadirective constructs. (gfc_omp_end_stmt): Declare. (gfc_matching_omp_context_selector): Declare. (gfc_in_metadirective_body): Declare. (gfc_omp_region_count): Declare. * resolve.cc (gfc_resolve_code): Handle EXEC_OMP_METADIRECTIVE. * st.cc (gfc_free_statement): Handle EXEC_OMP_METADIRECTIVE. * symbol.cc (compare_st_labels): Compare omp_region, not just the value. (gfc_get_st_label): Likewise. Initialize the omp_region field when creating a new label. * trans-decl.cc (gfc_get_label_decl): Encode the omp_region in the label name. * trans-openmp.cc (gfc_trans_omp_directive): Handle EXEC_OMP_METADIRECTIVE. (gfc_trans_omp_set_selector): New, split from... (gfc_trans_omp_declare_variant): ...here. (gfc_trans_omp_metadirective): New. * trans-stmt.h (gfc_trans_omp_metadirective): Declare. * trans.cc (trans_code): Handle EXEC_OMP_METADIRECTIVE. gcc/testsuite/ChangeLog * gfortran.dg/gomp/metadirective-1.f90: New. * gfortran.dg/gomp/metadirective-10.f90: New. * gfortran.dg/gomp/metadirective-11.f90: New. * gfortran.dg/gomp/metadirective-2.f90: New. * gfortran.dg/gomp/metadirective-3.f90: New. * gfortran.dg/gomp/metadirective-4.f90: New. * gfortran.dg/gomp/metadirective-5.f90: New. * gfortran.dg/gomp/metadirective-6.f90: New. * gfortran.dg/gomp/metadirective-7.f90: New. * gfortran.dg/gomp/metadirective-8.f90: New. * gfortran.dg/gomp/metadirective-9.f90: New. * gfortran.dg/gomp/metadirective-construct.f90: New. * gfortran.dg/gomp/metadirective-no-score.f90: New. * gfortran.dg/gomp/pure-1.f90: Add metadirective test. * gfortran.dg/gomp/pure-2.f90: Remove metadirective test. libgomp/ChangeLog * testsuite/libgomp.fortran/metadirective-1.f90: New. * testsuite/libgomp.fortran/metadirective-2.f90: New. * testsuite/libgomp.fortran/metadirective-3.f90: New. * testsuite/libgomp.fortran/metadirective-4.f90: New. * testsuite/libgomp.fortran/metadirective-5.f90: New. * testsuite/libgomp.fortran/metadirective-6.f90: New. Co-Authored-By: Kwok Cheung Yeung Co-Authored-By: Sandra Loosemore Co-Authored-By: Tobias Burnus Co-Authored-By: Paul-Antoine Arras --- gcc/fortran/decl.cc | 29 + gcc/fortran/dump-parse-tree.cc | 21 + gcc/fortran/gfortran.h | 20 +- gcc/fortran/io.cc | 2 +- gcc/fortran/match.h | 2 + gcc/fortran/openmp.cc | 294 +++++++-- gcc/fortran/parse.cc | 571 +++++++++++------- gcc/fortran/parse.h | 8 +- gcc/fortran/resolve.cc | 6 + gcc/fortran/st.cc | 4 + gcc/fortran/symbol.cc | 25 +- gcc/fortran/trans-decl.cc | 5 +- gcc/fortran/trans-openmp.cc | 233 ++++--- gcc/fortran/trans-stmt.h | 1 + gcc/fortran/trans.cc | 1 + .../gfortran.dg/gomp/metadirective-1.f90 | 55 ++ .../gfortran.dg/gomp/metadirective-10.f90 | 40 ++ .../gfortran.dg/gomp/metadirective-11.f90 | 33 + .../gfortran.dg/gomp/metadirective-2.f90 | 62 ++ .../gfortran.dg/gomp/metadirective-3.f90 | 34 ++ .../gfortran.dg/gomp/metadirective-4.f90 | 39 ++ .../gfortran.dg/gomp/metadirective-5.f90 | 30 + .../gfortran.dg/gomp/metadirective-6.f90 | 31 + .../gfortran.dg/gomp/metadirective-7.f90 | 36 ++ .../gfortran.dg/gomp/metadirective-8.f90 | 22 + .../gfortran.dg/gomp/metadirective-9.f90 | 30 + .../gomp/metadirective-construct.f90 | 260 ++++++++ .../gomp/metadirective-no-score.f90 | 122 ++++ gcc/testsuite/gfortran.dg/gomp/pure-1.f90 | 7 + gcc/testsuite/gfortran.dg/gomp/pure-2.f90 | 8 - .../libgomp.fortran/metadirective-1.f90 | 61 ++ .../libgomp.fortran/metadirective-2.f90 | 40 ++ .../libgomp.fortran/metadirective-3.f90 | 29 + .../libgomp.fortran/metadirective-4.f90 | 46 ++ .../libgomp.fortran/metadirective-5.f90 | 44 ++ .../libgomp.fortran/metadirective-6.f90 | 58 ++ 36 files changed, 1947 insertions(+), 362 deletions(-) create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-1.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-10.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-11.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-2.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-3.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-4.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-5.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-6.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-7.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-8.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-9.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-construct.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/metadirective-no-score.f90 create mode 100644 libgomp/testsuite/libgomp.fortran/metadirective-1.f90 create mode 100644 libgomp/testsuite/libgomp.fortran/metadirective-2.f90 create mode 100644 libgomp/testsuite/libgomp.fortran/metadirective-3.f90 create mode 100644 libgomp/testsuite/libgomp.fortran/metadirective-4.f90 create mode 100644 libgomp/testsuite/libgomp.fortran/metadirective-5.f90 create mode 100644 libgomp/testsuite/libgomp.fortran/metadirective-6.f90 diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc index b8308aeee55..b6633a913e7 100644 --- a/gcc/fortran/decl.cc +++ b/gcc/fortran/decl.cc @@ -8422,6 +8422,7 @@ gfc_match_end (gfc_statement *st) case COMP_CONTAINS: case COMP_DERIVED_CONTAINS: + case COMP_OMP_BEGIN_METADIRECTIVE: state = gfc_state_stack->previous->state; block_name = gfc_state_stack->previous->sym == NULL ? NULL : gfc_state_stack->previous->sym->name; @@ -8429,6 +8430,28 @@ gfc_match_end (gfc_statement *st) && gfc_state_stack->previous->sym->abr_modproc_decl; break; + case COMP_OMP_METADIRECTIVE: + { + /* Metadirectives can be nested, so we need to drill down to the + first state that is not COMP_OMP_METADIRECTIVE. */ + gfc_state_data *state_data = gfc_state_stack; + + do + { + state_data = state_data->previous; + state = state_data->state; + block_name = (state_data->sym == NULL + ? NULL : state_data->sym->name); + abbreviated_modproc_decl = (state_data->sym + && state_data->sym->abr_modproc_decl); + } + while (state == COMP_OMP_METADIRECTIVE); + + if (block_name && startswith (block_name, "block@")) + block_name = NULL; + } + break; + default: break; } @@ -8574,6 +8597,12 @@ gfc_match_end (gfc_statement *st) gfc_free_enum_history (); break; + case COMP_OMP_BEGIN_METADIRECTIVE: + *st = ST_OMP_END_METADIRECTIVE; + target = " metadirective"; + eos_ok = 0; + break; + default: gfc_error ("Unexpected END statement at %C"); goto cleanup; diff --git a/gcc/fortran/dump-parse-tree.cc b/gcc/fortran/dump-parse-tree.cc index 87a65036a3d..28dfdbb1a32 100644 --- a/gcc/fortran/dump-parse-tree.cc +++ b/gcc/fortran/dump-parse-tree.cc @@ -2173,6 +2173,7 @@ show_omp_node (int level, gfc_code *c) case EXEC_OMP_MASTER: name = "MASTER"; break; case EXEC_OMP_MASTER_TASKLOOP: name = "MASTER TASKLOOP"; break; case EXEC_OMP_MASTER_TASKLOOP_SIMD: name = "MASTER TASKLOOP SIMD"; break; + case EXEC_OMP_METADIRECTIVE: name = "METADIRECTIVE"; break; case EXEC_OMP_ORDERED: name = "ORDERED"; break; case EXEC_OMP_DEPOBJ: name = "DEPOBJ"; break; case EXEC_OMP_PARALLEL: name = "PARALLEL"; break; @@ -2370,6 +2371,25 @@ show_omp_node (int level, gfc_code *c) d = d->block; } } + else if (c->op == EXEC_OMP_METADIRECTIVE) + { + gfc_omp_variant *variant + = c->ext.omp_variants; + + while (variant) + { + code_indent (level + 1, 0); + if (variant->selectors) + fputs ("WHEN ()\n", dumpfile); + else + fputs ("DEFAULT ()\n", dumpfile); + /* TODO: Print selector. */ + show_code (level + 2, variant->code); + if (variant->next) + fputs ("\n", dumpfile); + variant = variant->next; + } + } else show_code (level + 1, c->block->next); if (c->op == EXEC_OMP_ATOMIC) @@ -3513,6 +3533,7 @@ show_code_node (int level, gfc_code *c) case EXEC_OMP_MASTER: case EXEC_OMP_MASTER_TASKLOOP: case EXEC_OMP_MASTER_TASKLOOP_SIMD: + case EXEC_OMP_METADIRECTIVE: case EXEC_OMP_ORDERED: case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h index 58505446bac..ae5fcf8f847 100644 --- a/gcc/fortran/gfortran.h +++ b/gcc/fortran/gfortran.h @@ -317,6 +317,7 @@ enum gfc_statement ST_OMP_END_PARALLEL_MASKED_TASKLOOP_SIMD, ST_OMP_MASKED_TASKLOOP, ST_OMP_END_MASKED_TASKLOOP, ST_OMP_MASKED_TASKLOOP_SIMD, ST_OMP_END_MASKED_TASKLOOP_SIMD, ST_OMP_SCOPE, ST_OMP_END_SCOPE, + ST_OMP_METADIRECTIVE, ST_OMP_BEGIN_METADIRECTIVE, ST_OMP_END_METADIRECTIVE, ST_OMP_ERROR, ST_OMP_ASSUME, ST_OMP_END_ASSUME, ST_OMP_ASSUMES, ST_OMP_ALLOCATE, ST_OMP_ALLOCATE_EXEC, ST_OMP_ALLOCATORS, ST_OMP_END_ALLOCATORS, @@ -1588,7 +1589,7 @@ typedef struct gfc_omp_clauses unsigned order_unconstrained:1, order_reproducible:1, capture:1; unsigned grainsize_strict:1, num_tasks_strict:1, compare:1, weak:1; unsigned non_rectangular:1, order_concurrent:1; - unsigned contains_teams_construct:1, target_first_st_is_teams:1; + unsigned contains_teams_construct:1, target_first_st_is_teams_or_meta:1; unsigned contained_in_target_construct:1, indirect:1; ENUM_BITFIELD (gfc_omp_sched_kind) sched_kind:3; ENUM_BITFIELD (gfc_omp_device_type) device_type:2; @@ -1708,6 +1709,17 @@ typedef struct gfc_omp_declare_variant gfc_omp_declare_variant; #define gfc_get_omp_declare_variant() XCNEW (gfc_omp_declare_variant) +typedef struct gfc_omp_variant +{ + struct gfc_omp_variant *next; + locus where; /* Where the metadirective clause occurred. */ + + gfc_omp_set_selector *selectors; + enum gfc_statement stmt; + struct gfc_code *code; + +} gfc_omp_variant; +#define gfc_get_omp_variant() XCNEW (gfc_omp_variant) typedef struct gfc_omp_udr { @@ -1756,6 +1768,7 @@ typedef struct gfc_st_label locus where; gfc_namespace *ns; + int omp_region; } gfc_st_label; @@ -3028,6 +3041,7 @@ enum gfc_exec_op EXEC_OMP_TARGET_TEAMS_LOOP, EXEC_OMP_MASKED, EXEC_OMP_PARALLEL_MASKED, EXEC_OMP_PARALLEL_MASKED_TASKLOOP, EXEC_OMP_PARALLEL_MASKED_TASKLOOP_SIMD, EXEC_OMP_MASKED_TASKLOOP, EXEC_OMP_MASKED_TASKLOOP_SIMD, EXEC_OMP_SCOPE, + EXEC_OMP_METADIRECTIVE, EXEC_OMP_ERROR, EXEC_OMP_ALLOCATE, EXEC_OMP_ALLOCATORS }; @@ -3086,6 +3100,7 @@ typedef struct gfc_code gfc_omp_clauses *omp_clauses; const char *omp_name; gfc_omp_namelist *omp_namelist; + gfc_omp_variant *omp_variants; bool omp_bool; } ext; /* Points to additional structures required by statement */ @@ -3686,6 +3701,7 @@ void gfc_free_omp_declare_variant_list (gfc_omp_declare_variant *list); void gfc_free_omp_declare_simd (gfc_omp_declare_simd *); void gfc_free_omp_declare_simd_list (gfc_omp_declare_simd *); void gfc_free_omp_udr (gfc_omp_udr *); +void gfc_free_omp_variants (gfc_omp_variant *); gfc_omp_udr *gfc_omp_udr_find (gfc_symtree *, gfc_typespec *); void gfc_resolve_omp_allocate (gfc_namespace *, gfc_omp_namelist *); void gfc_resolve_omp_assumptions (gfc_omp_assumptions *); @@ -3970,6 +3986,8 @@ void debug (gfc_expr *); bool gfc_parse_file (void); void gfc_global_used (gfc_gsymbol *, locus *); gfc_namespace* gfc_build_block_ns (gfc_namespace *); +gfc_statement match_omp_directive (void); +bool is_omp_declarative_stmt (gfc_statement); /* dependency.cc */ int gfc_dep_compare_functions (gfc_expr *, gfc_expr *, bool); diff --git a/gcc/fortran/io.cc b/gcc/fortran/io.cc index 6fd69f7c9a8..9dfb4e1ef25 100644 --- a/gcc/fortran/io.cc +++ b/gcc/fortran/io.cc @@ -29,7 +29,7 @@ along with GCC; see the file COPYING3. If not see gfc_st_label format_asterisk = {0, NULL, NULL, -1, ST_LABEL_FORMAT, ST_LABEL_FORMAT, NULL, - 0, {NULL, NULL}, NULL}; + 0, {NULL, NULL}, NULL, 0}; typedef struct { diff --git a/gcc/fortran/match.h b/gcc/fortran/match.h index b09921357fd..b0f5528ee72 100644 --- a/gcc/fortran/match.h +++ b/gcc/fortran/match.h @@ -155,6 +155,7 @@ match gfc_match_omp_assume (void); match gfc_match_omp_assumes (void); match gfc_match_omp_atomic (void); match gfc_match_omp_barrier (void); +match gfc_match_omp_begin_metadirective (void); match gfc_match_omp_cancel (void); match gfc_match_omp_cancellation_point (void); match gfc_match_omp_critical (void); @@ -178,6 +179,7 @@ match gfc_match_omp_masked_taskloop_simd (void); match gfc_match_omp_master (void); match gfc_match_omp_master_taskloop (void); match gfc_match_omp_master_taskloop_simd (void); +match gfc_match_omp_metadirective (void); match gfc_match_omp_nothing (void); match gfc_match_omp_ordered (void); match gfc_match_omp_ordered_depend (void); diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc index 315ec68f259..c40205853f8 100644 --- a/gcc/fortran/openmp.cc +++ b/gcc/fortran/openmp.cc @@ -113,7 +113,8 @@ static const struct gfc_omp_directive gfc_omp_directives[] = { /* Match an end of OpenMP directive. End of OpenMP directive is optional - whitespace, followed by '\n' or comment '!'. */ + whitespace, followed by '\n' or comment '!'. In the special case where a + context selector is being matched, match against ')' instead. */ static match gfc_match_omp_eos (void) @@ -124,17 +125,25 @@ gfc_match_omp_eos (void) old_loc = gfc_current_locus; gfc_gobble_whitespace (); - c = gfc_next_ascii_char (); - switch (c) + if (gfc_matching_omp_context_selector) { - case '!': - do - c = gfc_next_ascii_char (); - while (c != '\n'); - /* Fall through */ + if (gfc_peek_ascii_char () == ')') + return MATCH_YES; + } + else + { + c = gfc_next_ascii_char (); + switch (c) + { + case '!': + do + c = gfc_next_ascii_char (); + while (c != '\n'); + /* Fall through */ - case '\n': - return MATCH_YES; + case '\n': + return MATCH_YES; + } } gfc_current_locus = old_loc; @@ -340,6 +349,19 @@ gfc_free_omp_udr (gfc_omp_udr *omp_udr) } } +/* Free variants of an !$omp metadirective construct. */ + +void +gfc_free_omp_variants (gfc_omp_variant *variant) +{ + while (variant) + { + gfc_omp_variant *next_variant = variant->next; + gfc_free_omp_set_selector_list (variant->selectors); + free (variant); + variant = next_variant; + } +} static gfc_omp_udr * gfc_find_omp_udr (gfc_namespace *ns, const char *name, gfc_typespec *ts) @@ -1868,8 +1890,7 @@ gfc_match_dupl_atomic (bool not_dupl, const char *name) static match gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask, bool first = true, bool needs_space = true, - bool openacc = false, bool context_selector = false, - bool openmp_target = false) + bool openacc = false, bool openmp_target = false) { bool error = false; gfc_omp_clauses *c = gfc_get_omp_clauses (); @@ -3831,9 +3852,7 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask, } end: - if (error - || (context_selector && gfc_peek_ascii_char () != ')') - || (!context_selector && gfc_match_omp_eos () != MATCH_YES)) + if (error || gfc_match_omp_eos () != MATCH_YES) { if (!gfc_error_flag_test ()) gfc_error ("Failed to match clause at %C"); @@ -4536,7 +4555,7 @@ static match match_omp (gfc_exec_op op, const omp_mask mask) { gfc_omp_clauses *c; - if (gfc_match_omp_clauses (&c, mask, true, true, false, false, + if (gfc_match_omp_clauses (&c, mask, true, true, false, op == EXEC_OMP_TARGET) != MATCH_YES) return MATCH_ERROR; new_st.op = op; @@ -5717,7 +5736,8 @@ gfc_ignore_trait_property_extension_list (void) score(score-expression) */ match -gfc_match_omp_context_selector (gfc_omp_set_selector *oss) +gfc_match_omp_context_selector (gfc_omp_set_selector *oss, + bool metadirective_p) { do { @@ -5877,14 +5897,27 @@ gfc_match_omp_context_selector (gfc_omp_set_selector *oss) || (property_kind == OMP_TRAIT_PROPERTY_DEV_NUM_EXPR && otp->expr->ts.type != BT_INTEGER) || otp->expr->rank != 0 - || otp->expr->expr_type != EXPR_CONSTANT) + || (!metadirective_p + && otp->expr->expr_type != EXPR_CONSTANT)) { - if (property_kind == OMP_TRAIT_PROPERTY_BOOL_EXPR) - gfc_error ("property must be a constant logical expression " - "at %C"); + if (metadirective_p) + { + if (property_kind == OMP_TRAIT_PROPERTY_BOOL_EXPR) + gfc_error ("property must be a " + "logical expression at %C"); + else + gfc_error ("property must be an " + "integer expression at %C"); + } else - gfc_error ("property must be a constant integer expression " - "at %C"); + { + if (property_kind == OMP_TRAIT_PROPERTY_BOOL_EXPR) + gfc_error ("property must be a constant " + "logical expression at %C"); + else + gfc_error ("property must be a constant " + "integer expression at %C"); + } return MATCH_ERROR; } /* Device number must be conforming, which includes @@ -5904,14 +5937,17 @@ gfc_match_omp_context_selector (gfc_omp_set_selector *oss) { if (os->code == OMP_TRAIT_CONSTRUCT_SIMD) { + gfc_matching_omp_context_selector = true; if (gfc_match_omp_clauses (&otp->clauses, OMP_DECLARE_SIMD_CLAUSES, - true, false, false, true) + true, false, false) != MATCH_YES) { + gfc_matching_omp_context_selector = false; gfc_error ("expected simd clause at %C"); return MATCH_ERROR; } + gfc_matching_omp_context_selector = false; } else if (os->code == OMP_TRAIT_IMPLEMENTATION_REQUIRES) { @@ -5968,7 +6004,8 @@ gfc_match_omp_context_selector (gfc_omp_set_selector *oss) user */ match -gfc_match_omp_context_selector_specification (gfc_omp_declare_variant *odv) +gfc_match_omp_context_selector_specification (gfc_omp_set_selector **oss_head, + bool metadirective_p) { do { @@ -6001,11 +6038,11 @@ gfc_match_omp_context_selector_specification (gfc_omp_declare_variant *odv) } gfc_omp_set_selector *oss = gfc_get_omp_set_selector (); - oss->next = odv->set_selectors; + oss->next = *oss_head; oss->code = set; - odv->set_selectors = oss; + *oss_head = oss; - if (gfc_match_omp_context_selector (oss) != MATCH_YES) + if (gfc_match_omp_context_selector (oss, metadirective_p) != MATCH_YES) return MATCH_ERROR; m = gfc_match (" }"); @@ -6104,7 +6141,9 @@ gfc_match_omp_declare_variant (void) return MATCH_ERROR; } - if (gfc_match_omp_context_selector_specification (odv) != MATCH_YES) + if (gfc_match_omp_context_selector_specification (&odv->set_selectors, + false) + != MATCH_YES) return MATCH_ERROR; if (gfc_match (" )") != MATCH_YES) @@ -6120,6 +6159,154 @@ gfc_match_omp_declare_variant (void) } +static match +match_omp_metadirective (bool begin_p) +{ + locus old_loc = gfc_current_locus; + gfc_omp_variant *variants_head; + gfc_omp_variant **next_variant = &variants_head; + bool default_seen = false; + + /* Parse the context selectors. */ + for (;;) + { + bool default_p = false; + gfc_omp_set_selector *selectors = NULL; + locus variant_locus = gfc_current_locus; + + if (gfc_match (" default ( ") == MATCH_YES) + default_p = true; + else if (gfc_match (" otherwise ( ") == MATCH_YES) + default_p = true; + else if (gfc_match_eos () == MATCH_YES) + break; + else if (gfc_match (" when ( ") != MATCH_YES) + { + gfc_error ("expected %, %, or % at %C"); + gfc_current_locus = old_loc; + return MATCH_ERROR; + } + + if (default_p && default_seen) + { + gfc_error ("too many % or % clauses " + "in % at %C"); + gfc_current_locus = old_loc; + return MATCH_ERROR; + } + + if (!default_p) + { + if (gfc_match_omp_context_selector_specification (&selectors, true) + != MATCH_YES) + return MATCH_ERROR; + + if (gfc_match (" : ") != MATCH_YES) + { + gfc_error ("expected %<:%> at %C"); + gfc_current_locus = old_loc; + return MATCH_ERROR; + } + + gfc_commit_symbols (); + } + + gfc_matching_omp_context_selector = true; + gfc_statement directive = match_omp_directive (); + gfc_matching_omp_context_selector = false; + + if (is_omp_declarative_stmt (directive)) + sorry ("declarative directive variants are not supported"); + + if (gfc_error_flag_test ()) + { + gfc_current_locus = old_loc; + return MATCH_ERROR; + } + + if (gfc_match (" )") != MATCH_YES) + { + gfc_error ("Expected %<)%> at %C"); + gfc_current_locus = old_loc; + return MATCH_ERROR; + } + + gfc_commit_symbols (); + + if (begin_p && directive != ST_NONE + && gfc_omp_end_stmt (directive) == ST_NONE) + { + gfc_error ("variant directive used in OMP BEGIN METADIRECTIVE " + "at %C must have a corresponding end directive"); + gfc_current_locus = old_loc; + return MATCH_ERROR; + } + + if (default_p) + default_seen = true; + + gfc_omp_variant *omv = gfc_get_omp_variant (); + omv->selectors = selectors; + omv->stmt = directive; + omv->where = variant_locus; + + if (directive == ST_NONE) + { + /* The directive was a 'nothing' directive. */ + omv->code = gfc_get_code (EXEC_CONTINUE); + omv->code->ext.omp_clauses = NULL; + } + else + { + omv->code = gfc_get_code (new_st.op); + omv->code->ext.omp_clauses = new_st.ext.omp_clauses; + /* Prevent the OpenMP clauses from being freed via NEW_ST. */ + new_st.ext.omp_clauses = NULL; + } + + *next_variant = omv; + next_variant = &omv->next; + } + + if (gfc_match_omp_eos () != MATCH_YES) + { + gfc_error ("Unexpected junk after OMP METADIRECTIVE at %C"); + gfc_current_locus = old_loc; + return MATCH_ERROR; + } + + /* Add a 'default (nothing)' clause if no default is explicitly given. */ + if (!default_seen) + { + gfc_omp_variant *omv = gfc_get_omp_variant (); + omv->stmt = ST_NONE; + omv->code = gfc_get_code (EXEC_CONTINUE); + omv->code->ext.omp_clauses = NULL; + omv->where = old_loc; + omv->selectors = NULL; + + *next_variant = omv; + next_variant = &omv->next; + } + + new_st.op = EXEC_OMP_METADIRECTIVE; + new_st.ext.omp_variants = variants_head; + + return MATCH_YES; +} + +match +gfc_match_omp_begin_metadirective (void) +{ + return match_omp_metadirective (true); +} + +match +gfc_match_omp_metadirective (void) +{ + return match_omp_metadirective (false); +} + match gfc_match_omp_threadprivate (void) { @@ -11013,6 +11200,19 @@ resolve_omp_do (gfc_code *code) restructure_intervening_code (&(code->block->next), code, count); } +static void +resolve_omp_metadirective (gfc_code *code, gfc_namespace *ns) +{ + gfc_omp_variant *variant = code->ext.omp_variants; + + while (variant) + { + gfc_code *variant_code = variant->code; + gfc_resolve_code (variant_code, ns); + variant = variant->next; + } +} + static gfc_statement omp_code_to_statement (gfc_code *code) @@ -11556,13 +11756,32 @@ resolve_omp_target (gfc_code *code) gfc_code *c = code->block->next; if (c->op == EXEC_BLOCK) c = c->ext.block.ns->code; - if (code->ext.omp_clauses->target_first_st_is_teams - && ((GFC_IS_TEAMS_CONSTRUCT (c->op) && c->next == NULL) - || (c->op == EXEC_BLOCK - && c->next - && GFC_IS_TEAMS_CONSTRUCT (c->next->op) - && c->next->next == NULL))) - return; + if (code->ext.omp_clauses->target_first_st_is_teams_or_meta) + { + if (c->op == EXEC_OMP_METADIRECTIVE) + { + struct gfc_omp_variant *mc + = c->ext.omp_variants; + /* All mc->(next...->)code should be identical with regards + to the diagnostic below. */ + do + { + if (mc->stmt != ST_NONE + && GFC_IS_TEAMS_CONSTRUCT (mc->code->op)) + { + if (c->next == NULL && mc->code->next == NULL) + return; + c = mc->code; + break; + } + mc = mc->next; + } + while (mc); + } + else if (GFC_IS_TEAMS_CONSTRUCT (c->op) && c->next == NULL) + return; + } + while (c && !GFC_IS_TEAMS_CONSTRUCT (c->op)) c = c->next; if (c) @@ -11689,6 +11908,9 @@ gfc_resolve_omp_directive (gfc_code *code, gfc_namespace *ns) code->ext.omp_clauses->if_present = false; resolve_omp_clauses (code, code->ext.omp_clauses, ns); break; + case EXEC_OMP_METADIRECTIVE: + resolve_omp_metadirective (code, ns); + break; default: break; } diff --git a/gcc/fortran/parse.cc b/gcc/fortran/parse.cc index 79c810c86ba..24a444cb542 100644 --- a/gcc/fortran/parse.cc +++ b/gcc/fortran/parse.cc @@ -48,6 +48,10 @@ gfc_state_data *gfc_state_stack; static bool last_was_use_stmt = false; bool in_exec_part; +bool gfc_matching_omp_context_selector; +bool gfc_in_metadirective_body; +int gfc_omp_region_count; + /* TODO: Re-order functions to kill these forward decls. */ static void check_statement_label (gfc_statement); static void undo_new_statement (void); @@ -1041,6 +1045,8 @@ decode_omp_directive (void) break; case 'b': matcho ("barrier", gfc_match_omp_barrier, ST_OMP_BARRIER); + matcho ("begin metadirective", gfc_match_omp_begin_metadirective, + ST_OMP_BEGIN_METADIRECTIVE); break; case 'c': matcho ("cancellation% point", gfc_match_omp_cancellation_point, @@ -1085,6 +1091,8 @@ decode_omp_directive (void) matcho ("end master taskloop", gfc_match_omp_eos_error, ST_OMP_END_MASTER_TASKLOOP); matcho ("end master", gfc_match_omp_eos_error, ST_OMP_END_MASTER); + matcho ("end metadirective", gfc_match_omp_eos_error, + ST_OMP_END_METADIRECTIVE); matchs ("end ordered", gfc_match_omp_eos_error, ST_OMP_END_ORDERED); matchs ("end parallel do simd", gfc_match_omp_eos_error, ST_OMP_END_PARALLEL_DO_SIMD); @@ -1168,6 +1176,8 @@ decode_omp_directive (void) matcho ("master taskloop", gfc_match_omp_master_taskloop, ST_OMP_MASTER_TASKLOOP); matcho ("master", gfc_match_omp_master, ST_OMP_MASTER); + matcho ("metadirective", gfc_match_omp_metadirective, + ST_OMP_METADIRECTIVE); break; case 'n': matcho ("nothing", gfc_match_omp_nothing, ST_NONE); @@ -1296,6 +1306,10 @@ decode_omp_directive (void) gfc_error_now ("Unclassifiable OpenMP directive at %C"); } + /* If parsing a metadirective, let the caller deal with the cleanup. */ + if (gfc_matching_omp_context_selector) + return ST_NONE; + reject_statement (); gfc_error_recovery (); @@ -1413,6 +1427,12 @@ decode_omp_directive (void) return ST_GET_FCN_CHARACTERISTICS; } +gfc_statement +match_omp_directive (void) +{ + return decode_omp_directive (); +} + static gfc_statement decode_gcc_attribute (void) { @@ -1936,6 +1956,43 @@ next_statement (void) case ST_OMP_DECLARE_VARIANT: case ST_OMP_ALLOCATE: case ST_OMP_ASSUMES: \ case ST_OMP_REQUIRES: case ST_OACC_ROUTINE: case ST_OACC_DECLARE +/* OpenMP statements that are followed by a structured block. */ + +#define case_omp_structured_block case ST_OMP_ASSUME: case ST_OMP_PARALLEL: \ + case ST_OMP_PARALLEL_MASKED: case ST_OMP_PARALLEL_MASTER: \ + case ST_OMP_PARALLEL_SECTIONS: case ST_OMP_ORDERED: \ + case ST_OMP_CRITICAL: case ST_OMP_MASKED: case ST_OMP_MASTER: \ + case ST_OMP_SCOPE: case ST_OMP_SECTIONS: case ST_OMP_SINGLE: \ + case ST_OMP_TARGET: case ST_OMP_TARGET_DATA: case ST_OMP_TARGET_PARALLEL: \ + case ST_OMP_TARGET_TEAMS: case ST_OMP_TEAMS: case ST_OMP_TASK: \ + case ST_OMP_TASKGROUP: \ + case ST_OMP_WORKSHARE: case ST_OMP_PARALLEL_WORKSHARE + +/* OpenMP statements that are followed by a do loop. */ + +#define case_omp_do case ST_OMP_DISTRIBUTE: \ + case ST_OMP_DISTRIBUTE_PARALLEL_DO: \ + case ST_OMP_DISTRIBUTE_PARALLEL_DO_SIMD: case ST_OMP_DISTRIBUTE_SIMD: \ + case ST_OMP_DO: case ST_OMP_DO_SIMD: case ST_OMP_LOOP: \ + case ST_OMP_PARALLEL_DO: case ST_OMP_PARALLEL_DO_SIMD: \ + case ST_OMP_PARALLEL_LOOP: case ST_OMP_PARALLEL_MASKED_TASKLOOP: \ + case ST_OMP_PARALLEL_MASKED_TASKLOOP_SIMD: \ + case ST_OMP_PARALLEL_MASTER_TASKLOOP: \ + case ST_OMP_PARALLEL_MASTER_TASKLOOP_SIMD: \ + case ST_OMP_MASKED_TASKLOOP: case ST_OMP_MASKED_TASKLOOP_SIMD: \ + case ST_OMP_MASTER_TASKLOOP: case ST_OMP_MASTER_TASKLOOP_SIMD: \ + case ST_OMP_SIMD: \ + case ST_OMP_TARGET_PARALLEL_DO: case ST_OMP_TARGET_PARALLEL_DO_SIMD: \ + case ST_OMP_TARGET_PARALLEL_LOOP: case ST_OMP_TARGET_SIMD: \ + case ST_OMP_TARGET_TEAMS_DISTRIBUTE: \ + case ST_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO: \ + case ST_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD: \ + case ST_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD: case ST_OMP_TARGET_TEAMS_LOOP: \ + case ST_OMP_TASKLOOP: case ST_OMP_TASKLOOP_SIMD: \ + case ST_OMP_TEAMS_DISTRIBUTE: case ST_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO: \ + case ST_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD: \ + case ST_OMP_TEAMS_DISTRIBUTE_SIMD: case ST_OMP_TEAMS_LOOP + /* Block end statements. Errors associated with interchanging these are detected in gfc_match_end(). */ @@ -2573,6 +2630,9 @@ gfc_ascii_statement (gfc_statement st, bool strip_sentinel) case ST_OMP_BARRIER: p = "!$OMP BARRIER"; break; + case ST_OMP_BEGIN_METADIRECTIVE: + p = "!$OMP BEGIN METADIRECTIVE"; + break; case ST_OMP_CANCEL: p = "!$OMP CANCEL"; break; @@ -2672,6 +2732,9 @@ gfc_ascii_statement (gfc_statement st, bool strip_sentinel) case ST_OMP_END_MASTER_TASKLOOP_SIMD: p = "!$OMP END MASTER TASKLOOP SIMD"; break; + case ST_OMP_END_METADIRECTIVE: + p = "!$OMP END METADIRECTIVE"; + break; case ST_OMP_END_ORDERED: p = "!$OMP END ORDERED"; break; @@ -2816,6 +2879,9 @@ gfc_ascii_statement (gfc_statement st, bool strip_sentinel) case ST_OMP_MASTER_TASKLOOP_SIMD: p = "!$OMP MASTER TASKLOOP SIMD"; break; + case ST_OMP_METADIRECTIVE: + p = "!$OMP METADIRECTIVE"; + break; case ST_OMP_ORDERED: case ST_OMP_ORDERED_DEPEND: p = "!$OMP ORDERED"; @@ -3076,6 +3142,8 @@ accept_statement (gfc_statement st) break; case ST_ENTRY: + case ST_OMP_METADIRECTIVE: + case ST_OMP_BEGIN_METADIRECTIVE: case_executable: case_exec_markers: add_statement (); @@ -5437,6 +5505,140 @@ loop: accept_statement (st); } +/* Get the corresponding ending statement type for the OpenMP directive + OMP_ST. If it does not have one, return ST_NONE. */ + +gfc_statement +gfc_omp_end_stmt (gfc_statement omp_st, + bool omp_do_p, bool omp_structured_p) +{ + if (omp_do_p) + { + switch (omp_st) + { + case ST_OMP_DISTRIBUTE: return ST_OMP_END_DISTRIBUTE; + case ST_OMP_DISTRIBUTE_PARALLEL_DO: + return ST_OMP_END_DISTRIBUTE_PARALLEL_DO; + case ST_OMP_DISTRIBUTE_PARALLEL_DO_SIMD: + return ST_OMP_END_DISTRIBUTE_PARALLEL_DO_SIMD; + case ST_OMP_DISTRIBUTE_SIMD: + return ST_OMP_END_DISTRIBUTE_SIMD; + case ST_OMP_DO: return ST_OMP_END_DO; + case ST_OMP_DO_SIMD: return ST_OMP_END_DO_SIMD; + case ST_OMP_LOOP: return ST_OMP_END_LOOP; + case ST_OMP_PARALLEL_DO: return ST_OMP_END_PARALLEL_DO; + case ST_OMP_PARALLEL_DO_SIMD: + return ST_OMP_END_PARALLEL_DO_SIMD; + case ST_OMP_PARALLEL_LOOP: + return ST_OMP_END_PARALLEL_LOOP; + case ST_OMP_SIMD: return ST_OMP_END_SIMD; + case ST_OMP_TARGET_PARALLEL_DO: + return ST_OMP_END_TARGET_PARALLEL_DO; + case ST_OMP_TARGET_PARALLEL_DO_SIMD: + return ST_OMP_END_TARGET_PARALLEL_DO_SIMD; + case ST_OMP_TARGET_PARALLEL_LOOP: + return ST_OMP_END_TARGET_PARALLEL_LOOP; + case ST_OMP_TARGET_SIMD: return ST_OMP_END_TARGET_SIMD; + case ST_OMP_TARGET_TEAMS_DISTRIBUTE: + return ST_OMP_END_TARGET_TEAMS_DISTRIBUTE; + case ST_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO: + return ST_OMP_END_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO; + case ST_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD: + return ST_OMP_END_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD; + case ST_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD: + return ST_OMP_END_TARGET_TEAMS_DISTRIBUTE_SIMD; + case ST_OMP_TARGET_TEAMS_LOOP: + return ST_OMP_END_TARGET_TEAMS_LOOP; + case ST_OMP_TASKLOOP: return ST_OMP_END_TASKLOOP; + case ST_OMP_TASKLOOP_SIMD: return ST_OMP_END_TASKLOOP_SIMD; + case ST_OMP_MASKED_TASKLOOP: return ST_OMP_END_MASKED_TASKLOOP; + case ST_OMP_MASKED_TASKLOOP_SIMD: + return ST_OMP_END_MASKED_TASKLOOP_SIMD; + case ST_OMP_MASTER_TASKLOOP: return ST_OMP_END_MASTER_TASKLOOP; + case ST_OMP_MASTER_TASKLOOP_SIMD: + return ST_OMP_END_MASTER_TASKLOOP_SIMD; + case ST_OMP_PARALLEL_MASKED_TASKLOOP: + return ST_OMP_END_PARALLEL_MASKED_TASKLOOP; + case ST_OMP_PARALLEL_MASKED_TASKLOOP_SIMD: + return ST_OMP_END_PARALLEL_MASKED_TASKLOOP_SIMD; + case ST_OMP_PARALLEL_MASTER_TASKLOOP: + return ST_OMP_END_PARALLEL_MASTER_TASKLOOP; + case ST_OMP_PARALLEL_MASTER_TASKLOOP_SIMD: + return ST_OMP_END_PARALLEL_MASTER_TASKLOOP_SIMD; + case ST_OMP_TEAMS_DISTRIBUTE: + return ST_OMP_END_TEAMS_DISTRIBUTE; + case ST_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO: + return ST_OMP_END_TEAMS_DISTRIBUTE_PARALLEL_DO; + case ST_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD: + return ST_OMP_END_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD; + case ST_OMP_TEAMS_DISTRIBUTE_SIMD: + return ST_OMP_END_TEAMS_DISTRIBUTE_SIMD; + case ST_OMP_TEAMS_LOOP: + return ST_OMP_END_TEAMS_LOOP; + default: + break; + } + } + + if (omp_structured_p) + { + switch (omp_st) + { + case ST_OMP_ASSUME: + return ST_OMP_END_ASSUME; + case ST_OMP_PARALLEL: + return ST_OMP_END_PARALLEL; + case ST_OMP_PARALLEL_MASKED: + return ST_OMP_END_PARALLEL_MASKED; + case ST_OMP_PARALLEL_MASTER: + return ST_OMP_END_PARALLEL_MASTER; + case ST_OMP_PARALLEL_SECTIONS: + return ST_OMP_END_PARALLEL_SECTIONS; + case ST_OMP_SCOPE: + return ST_OMP_END_SCOPE; + case ST_OMP_SECTIONS: + return ST_OMP_END_SECTIONS; + case ST_OMP_ORDERED: + return ST_OMP_END_ORDERED; + case ST_OMP_CRITICAL: + return ST_OMP_END_CRITICAL; + case ST_OMP_MASKED: + return ST_OMP_END_MASKED; + case ST_OMP_MASTER: + return ST_OMP_END_MASTER; + case ST_OMP_SINGLE: + return ST_OMP_END_SINGLE; + case ST_OMP_TARGET: + return ST_OMP_END_TARGET; + case ST_OMP_TARGET_DATA: + return ST_OMP_END_TARGET_DATA; + case ST_OMP_TARGET_PARALLEL: + return ST_OMP_END_TARGET_PARALLEL; + case ST_OMP_TARGET_TEAMS: + return ST_OMP_END_TARGET_TEAMS; + case ST_OMP_TASK: + return ST_OMP_END_TASK; + case ST_OMP_TASKGROUP: + return ST_OMP_END_TASKGROUP; + case ST_OMP_TEAMS: + return ST_OMP_END_TEAMS; + case ST_OMP_TEAMS_DISTRIBUTE: + return ST_OMP_END_TEAMS_DISTRIBUTE; + case ST_OMP_DISTRIBUTE: + return ST_OMP_END_DISTRIBUTE; + case ST_OMP_WORKSHARE: + return ST_OMP_END_WORKSHARE; + case ST_OMP_PARALLEL_WORKSHARE: + return ST_OMP_END_PARALLEL_WORKSHARE; + case ST_OMP_BEGIN_METADIRECTIVE: + return ST_OMP_END_METADIRECTIVE; + default: + break; + } + } + + return ST_NONE; +} /* Parse the statements of OpenMP do/parallel do. */ @@ -5487,94 +5689,16 @@ parse_omp_do (gfc_statement omp_st) pop_state (); st = next_statement (); - gfc_statement omp_end_st = ST_OMP_END_DO; - switch (omp_st) - { - case ST_OMP_DISTRIBUTE: omp_end_st = ST_OMP_END_DISTRIBUTE; break; - case ST_OMP_DISTRIBUTE_PARALLEL_DO: - omp_end_st = ST_OMP_END_DISTRIBUTE_PARALLEL_DO; - break; - case ST_OMP_DISTRIBUTE_PARALLEL_DO_SIMD: - omp_end_st = ST_OMP_END_DISTRIBUTE_PARALLEL_DO_SIMD; - break; - case ST_OMP_DISTRIBUTE_SIMD: - omp_end_st = ST_OMP_END_DISTRIBUTE_SIMD; - break; - case ST_OMP_DO: omp_end_st = ST_OMP_END_DO; break; - case ST_OMP_DO_SIMD: omp_end_st = ST_OMP_END_DO_SIMD; break; - case ST_OMP_LOOP: omp_end_st = ST_OMP_END_LOOP; break; - case ST_OMP_PARALLEL_DO: omp_end_st = ST_OMP_END_PARALLEL_DO; break; - case ST_OMP_PARALLEL_DO_SIMD: - omp_end_st = ST_OMP_END_PARALLEL_DO_SIMD; - break; - case ST_OMP_PARALLEL_LOOP: - omp_end_st = ST_OMP_END_PARALLEL_LOOP; - break; - case ST_OMP_SIMD: omp_end_st = ST_OMP_END_SIMD; break; - case ST_OMP_TARGET_PARALLEL_DO: - omp_end_st = ST_OMP_END_TARGET_PARALLEL_DO; - break; - case ST_OMP_TARGET_PARALLEL_DO_SIMD: - omp_end_st = ST_OMP_END_TARGET_PARALLEL_DO_SIMD; - break; - case ST_OMP_TARGET_PARALLEL_LOOP: - omp_end_st = ST_OMP_END_TARGET_PARALLEL_LOOP; - break; - case ST_OMP_TARGET_SIMD: omp_end_st = ST_OMP_END_TARGET_SIMD; break; - case ST_OMP_TARGET_TEAMS_DISTRIBUTE: - omp_end_st = ST_OMP_END_TARGET_TEAMS_DISTRIBUTE; - break; - case ST_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO: - omp_end_st = ST_OMP_END_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO; - break; - case ST_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD: - omp_end_st = ST_OMP_END_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD; - break; - case ST_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD: - omp_end_st = ST_OMP_END_TARGET_TEAMS_DISTRIBUTE_SIMD; - break; - case ST_OMP_TARGET_TEAMS_LOOP: - omp_end_st = ST_OMP_END_TARGET_TEAMS_LOOP; - break; - case ST_OMP_TASKLOOP: omp_end_st = ST_OMP_END_TASKLOOP; break; - case ST_OMP_TASKLOOP_SIMD: omp_end_st = ST_OMP_END_TASKLOOP_SIMD; break; - case ST_OMP_MASKED_TASKLOOP: omp_end_st = ST_OMP_END_MASKED_TASKLOOP; break; - case ST_OMP_MASKED_TASKLOOP_SIMD: - omp_end_st = ST_OMP_END_MASKED_TASKLOOP_SIMD; - break; - case ST_OMP_MASTER_TASKLOOP: omp_end_st = ST_OMP_END_MASTER_TASKLOOP; break; - case ST_OMP_MASTER_TASKLOOP_SIMD: - omp_end_st = ST_OMP_END_MASTER_TASKLOOP_SIMD; - break; - case ST_OMP_PARALLEL_MASKED_TASKLOOP: - omp_end_st = ST_OMP_END_PARALLEL_MASKED_TASKLOOP; - break; - case ST_OMP_PARALLEL_MASKED_TASKLOOP_SIMD: - omp_end_st = ST_OMP_END_PARALLEL_MASKED_TASKLOOP_SIMD; - break; - case ST_OMP_PARALLEL_MASTER_TASKLOOP: - omp_end_st = ST_OMP_END_PARALLEL_MASTER_TASKLOOP; - break; - case ST_OMP_PARALLEL_MASTER_TASKLOOP_SIMD: - omp_end_st = ST_OMP_END_PARALLEL_MASTER_TASKLOOP_SIMD; - break; - case ST_OMP_TEAMS_DISTRIBUTE: - omp_end_st = ST_OMP_END_TEAMS_DISTRIBUTE; - break; - case ST_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO: - omp_end_st = ST_OMP_END_TEAMS_DISTRIBUTE_PARALLEL_DO; - break; - case ST_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD: - omp_end_st = ST_OMP_END_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD; - break; - case ST_OMP_TEAMS_DISTRIBUTE_SIMD: - omp_end_st = ST_OMP_END_TEAMS_DISTRIBUTE_SIMD; - break; - case ST_OMP_TEAMS_LOOP: - omp_end_st = ST_OMP_END_TEAMS_LOOP; - break; - default: gcc_unreachable (); - } + gfc_statement omp_end_st = gfc_omp_end_stmt (omp_st, true, false); + if (omp_st == ST_NONE) + gcc_unreachable (); + + /* If handling a metadirective variant, treat 'omp end metadirective' + as the expected end statement for the current construct. */ + if (st == ST_OMP_END_METADIRECTIVE + && gfc_state_stack->state == COMP_OMP_BEGIN_METADIRECTIVE) + st = omp_end_st; + if (st == omp_end_st) { if (new_st.op == EXEC_OMP_END_NOWAIT) @@ -5886,80 +6010,15 @@ parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only) np->op = cp->op; np->block = NULL; - switch (omp_st) - { - case ST_OMP_ASSUME: - omp_end_st = ST_OMP_END_ASSUME; - break; - case ST_OMP_PARALLEL: - omp_end_st = ST_OMP_END_PARALLEL; - break; - case ST_OMP_PARALLEL_MASKED: - omp_end_st = ST_OMP_END_PARALLEL_MASKED; - break; - case ST_OMP_PARALLEL_MASTER: - omp_end_st = ST_OMP_END_PARALLEL_MASTER; - break; - case ST_OMP_PARALLEL_SECTIONS: - omp_end_st = ST_OMP_END_PARALLEL_SECTIONS; - break; - case ST_OMP_SCOPE: - omp_end_st = ST_OMP_END_SCOPE; - break; - case ST_OMP_SECTIONS: - omp_end_st = ST_OMP_END_SECTIONS; - break; - case ST_OMP_ORDERED: - omp_end_st = ST_OMP_END_ORDERED; - break; - case ST_OMP_CRITICAL: - omp_end_st = ST_OMP_END_CRITICAL; - break; - case ST_OMP_MASKED: - omp_end_st = ST_OMP_END_MASKED; - break; - case ST_OMP_MASTER: - omp_end_st = ST_OMP_END_MASTER; - break; - case ST_OMP_SINGLE: - omp_end_st = ST_OMP_END_SINGLE; - break; - case ST_OMP_TARGET: - omp_end_st = ST_OMP_END_TARGET; - break; - case ST_OMP_TARGET_DATA: - omp_end_st = ST_OMP_END_TARGET_DATA; - break; - case ST_OMP_TARGET_PARALLEL: - omp_end_st = ST_OMP_END_TARGET_PARALLEL; - break; - case ST_OMP_TARGET_TEAMS: - omp_end_st = ST_OMP_END_TARGET_TEAMS; - break; - case ST_OMP_TASK: - omp_end_st = ST_OMP_END_TASK; - break; - case ST_OMP_TASKGROUP: - omp_end_st = ST_OMP_END_TASKGROUP; - break; - case ST_OMP_TEAMS: - omp_end_st = ST_OMP_END_TEAMS; - break; - case ST_OMP_TEAMS_DISTRIBUTE: - omp_end_st = ST_OMP_END_TEAMS_DISTRIBUTE; - break; - case ST_OMP_DISTRIBUTE: - omp_end_st = ST_OMP_END_DISTRIBUTE; - break; - case ST_OMP_WORKSHARE: - omp_end_st = ST_OMP_END_WORKSHARE; - break; - case ST_OMP_PARALLEL_WORKSHARE: - omp_end_st = ST_OMP_END_PARALLEL_WORKSHARE; - break; - default: - gcc_unreachable (); - } + omp_end_st = gfc_omp_end_stmt (omp_st, false, true); + if (omp_end_st == ST_NONE) + gcc_unreachable (); + + /* If handling a metadirective variant, treat 'omp end metadirective' + as the expected end statement for the current construct. */ + if (gfc_state_stack->previous != NULL + && gfc_state_stack->previous->state == COMP_OMP_BEGIN_METADIRECTIVE) + omp_end_st = ST_OMP_END_METADIRECTIVE; bool block_construct = false; gfc_namespace *my_ns = NULL; @@ -6005,11 +6064,13 @@ parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only) case ST_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO: case ST_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD: case ST_OMP_TEAMS_LOOP: + case ST_OMP_METADIRECTIVE: + case ST_OMP_BEGIN_METADIRECTIVE: { gfc_state_data *stk = gfc_state_stack->previous; if (stk->state == COMP_OMP_STRICTLY_STRUCTURED_BLOCK) stk = stk->previous; - stk->tail->ext.omp_clauses->target_first_st_is_teams = true; + stk->tail->ext.omp_clauses->target_first_st_is_teams_or_meta = true; break; } default: @@ -6182,6 +6243,88 @@ parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only) return st; } +static gfc_statement +parse_omp_metadirective_body (gfc_statement omp_st) +{ + gfc_omp_variant *variant + = new_st.ext.omp_variants; + locus body_locus = gfc_current_locus; + + accept_statement (omp_st); + + gfc_statement next_st = ST_NONE; + + while (variant) + { + gfc_current_locus = body_locus; + gfc_state_data s; + bool workshare_p + = (variant->stmt == ST_OMP_WORKSHARE + || variant->stmt == ST_OMP_PARALLEL_WORKSHARE); + enum gfc_compile_state new_state + = (omp_st == ST_OMP_METADIRECTIVE + ? COMP_OMP_METADIRECTIVE : COMP_OMP_BEGIN_METADIRECTIVE); + + new_st = *variant->code; + push_state (&s, new_state, NULL); + + gfc_statement st; + bool old_in_metadirective_body = gfc_in_metadirective_body; + gfc_in_metadirective_body = true; + + gfc_omp_region_count++; + switch (variant->stmt) + { + case_omp_structured_block: + st = parse_omp_structured_block (variant->stmt, workshare_p); + break; + case_omp_do: + st = parse_omp_do (variant->stmt); + /* TODO: Does st == ST_IMPLIED_ENDDO need special handling? */ + break; + default: + accept_statement (variant->stmt); + st = parse_executable (next_statement ()); + break; + } + + if (gfc_state_stack->state == COMP_OMP_METADIRECTIVE + && startswith (gfc_ascii_statement (st), "!$OMP END ")) + { + for (gfc_state_data *p = gfc_state_stack; p; p = p->previous) + if (p->state == COMP_OMP_STRUCTURED_BLOCK + || p->state == COMP_OMP_BEGIN_METADIRECTIVE) + goto finish; + gfc_error ( + "Unexpected %s statement in an OMP METADIRECTIVE block at %C", + gfc_ascii_statement (st)); + reject_statement (); + st = next_statement (); + } + finish: + + gfc_in_metadirective_body = old_in_metadirective_body; + + if (gfc_state_stack->head) + *variant->code = *gfc_state_stack->head; + pop_state (); + + gfc_commit_symbols (); + gfc_warning_check (); + if (variant->next) + gfc_clear_new_st (); + + /* Sanity-check that each variant finishes parsing at the same place. */ + if (next_st == ST_NONE) + next_st = st; + else + gcc_assert (st == next_st); + + variant = variant->next; + } + + return next_st; +} /* Accept a series of executable statements. We return the first statement that doesn't fit to the caller. Any block statements are @@ -6192,6 +6335,7 @@ static gfc_statement parse_executable (gfc_statement st) { int close_flag; + bool one_stmt_p = false; in_exec_part = true; if (st == ST_NONE) @@ -6199,6 +6343,12 @@ parse_executable (gfc_statement st) for (;;) { + /* Only parse one statement for the form of metadirective without + an explicit begin..end. */ + if (gfc_state_stack->state == COMP_OMP_METADIRECTIVE && one_stmt_p) + return st; + one_stmt_p = true; + close_flag = check_do_closure (); if (close_flag) switch (st) @@ -6308,68 +6458,13 @@ parse_executable (gfc_statement st) st = parse_openmp_allocate_block (st); continue; - case ST_OMP_ASSUME: - case ST_OMP_PARALLEL: - case ST_OMP_PARALLEL_MASKED: - case ST_OMP_PARALLEL_MASTER: - case ST_OMP_PARALLEL_SECTIONS: - case ST_OMP_ORDERED: - case ST_OMP_CRITICAL: - case ST_OMP_MASKED: - case ST_OMP_MASTER: - case ST_OMP_SCOPE: - case ST_OMP_SECTIONS: - case ST_OMP_SINGLE: - case ST_OMP_TARGET: - case ST_OMP_TARGET_DATA: - case ST_OMP_TARGET_PARALLEL: - case ST_OMP_TARGET_TEAMS: - case ST_OMP_TEAMS: - case ST_OMP_TASK: - case ST_OMP_TASKGROUP: - st = parse_omp_structured_block (st, false); + case_omp_structured_block: + st = parse_omp_structured_block (st, + st == ST_OMP_WORKSHARE + || st == ST_OMP_PARALLEL_WORKSHARE); continue; - case ST_OMP_WORKSHARE: - case ST_OMP_PARALLEL_WORKSHARE: - st = parse_omp_structured_block (st, true); - continue; - - case ST_OMP_DISTRIBUTE: - case ST_OMP_DISTRIBUTE_PARALLEL_DO: - case ST_OMP_DISTRIBUTE_PARALLEL_DO_SIMD: - case ST_OMP_DISTRIBUTE_SIMD: - case ST_OMP_DO: - case ST_OMP_DO_SIMD: - case ST_OMP_LOOP: - case ST_OMP_PARALLEL_DO: - case ST_OMP_PARALLEL_DO_SIMD: - case ST_OMP_PARALLEL_LOOP: - case ST_OMP_PARALLEL_MASKED_TASKLOOP: - case ST_OMP_PARALLEL_MASKED_TASKLOOP_SIMD: - case ST_OMP_PARALLEL_MASTER_TASKLOOP: - case ST_OMP_PARALLEL_MASTER_TASKLOOP_SIMD: - case ST_OMP_MASKED_TASKLOOP: - case ST_OMP_MASKED_TASKLOOP_SIMD: - case ST_OMP_MASTER_TASKLOOP: - case ST_OMP_MASTER_TASKLOOP_SIMD: - case ST_OMP_SIMD: - case ST_OMP_TARGET_PARALLEL_DO: - case ST_OMP_TARGET_PARALLEL_DO_SIMD: - case ST_OMP_TARGET_PARALLEL_LOOP: - case ST_OMP_TARGET_SIMD: - case ST_OMP_TARGET_TEAMS_DISTRIBUTE: - case ST_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO: - case ST_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD: - case ST_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD: - case ST_OMP_TARGET_TEAMS_LOOP: - case ST_OMP_TASKLOOP: - case ST_OMP_TASKLOOP_SIMD: - case ST_OMP_TEAMS_DISTRIBUTE: - case ST_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO: - case ST_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD: - case ST_OMP_TEAMS_DISTRIBUTE_SIMD: - case ST_OMP_TEAMS_LOOP: + case_omp_do: st = parse_omp_do (st); if (st == ST_IMPLIED_ENDDO) return st; @@ -6383,6 +6478,19 @@ parse_executable (gfc_statement st) st = parse_omp_oacc_atomic (true); continue; + case ST_OMP_METADIRECTIVE: + case ST_OMP_BEGIN_METADIRECTIVE: + st = parse_omp_metadirective_body (st); + continue; + + case ST_OMP_END_METADIRECTIVE: + if (gfc_state_stack->state == COMP_OMP_BEGIN_METADIRECTIVE) + { + st = next_statement (); + return st; + } + /* FALLTHRU */ + default: return st; } @@ -7148,6 +7256,10 @@ gfc_parse_file (void) gfc_statement_label = NULL; + gfc_omp_region_count = 0; + gfc_in_metadirective_body = false; + gfc_matching_omp_context_selector = false; + if (setjmp (eof_buf)) return false; /* Come here on unexpected EOF */ @@ -7460,3 +7572,16 @@ is_oacc (gfc_state_data *sd) return false; } } + +/* Return true if ST is a declarative OpenMP statement. */ +bool +is_omp_declarative_stmt (gfc_statement st) +{ + switch (st) + { + case_omp_decl: + return true; + default: + return false; + } +} diff --git a/gcc/fortran/parse.h b/gcc/fortran/parse.h index ce19d4deb07..2c5b3adec3b 100644 --- a/gcc/fortran/parse.h +++ b/gcc/fortran/parse.h @@ -31,7 +31,8 @@ enum gfc_compile_state COMP_STRUCTURE, COMP_UNION, COMP_MAP, COMP_DO, COMP_SELECT, COMP_FORALL, COMP_WHERE, COMP_CONTAINS, COMP_ENUM, COMP_SELECT_TYPE, COMP_SELECT_RANK, COMP_OMP_STRUCTURED_BLOCK, COMP_CRITICAL, - COMP_DO_CONCURRENT, COMP_OMP_STRICTLY_STRUCTURED_BLOCK + COMP_DO_CONCURRENT, COMP_OMP_STRICTLY_STRUCTURED_BLOCK, + COMP_OMP_METADIRECTIVE, COMP_OMP_BEGIN_METADIRECTIVE }; /* Stack element for the current compilation state. These structures @@ -67,10 +68,15 @@ bool gfc_check_do_variable (gfc_symtree *); bool gfc_find_state (gfc_compile_state); gfc_state_data *gfc_enclosing_unit (gfc_compile_state *); const char *gfc_ascii_statement (gfc_statement, bool strip_sentinel = false) ; +gfc_statement gfc_omp_end_stmt (gfc_statement, bool = true, bool = true); match gfc_match_enum (void); match gfc_match_enumerator_def (void); void gfc_free_enum_history (void); extern bool gfc_matching_function; +extern bool gfc_matching_omp_context_selector; +extern bool gfc_in_metadirective_body; +extern int gfc_omp_region_count; + match gfc_match_prefix (gfc_typespec *); bool is_oacc (gfc_state_data *); #endif /* GFC_PARSE_H */ diff --git a/gcc/fortran/resolve.cc b/gcc/fortran/resolve.cc index 4368627041e..16c20ada92a 100644 --- a/gcc/fortran/resolve.cc +++ b/gcc/fortran/resolve.cc @@ -12527,6 +12527,11 @@ gfc_resolve_code (gfc_code *code, gfc_namespace *ns) gfc_resolve_forall (code, ns, forall_save); forall_flag = 2; } + else if (code->op == EXEC_OMP_METADIRECTIVE) + for (gfc_omp_variant *variant + = code->ext.omp_variants; + variant; variant = variant->next) + gfc_resolve_code (variant->code, ns); else if (code->block) { omp_workshare_save = -1; @@ -13059,6 +13064,7 @@ start: case EXEC_OMP_MASKED: case EXEC_OMP_MASKED_TASKLOOP: case EXEC_OMP_MASKED_TASKLOOP_SIMD: + case EXEC_OMP_METADIRECTIVE: case EXEC_OMP_ORDERED: case EXEC_OMP_SCAN: case EXEC_OMP_SCOPE: diff --git a/gcc/fortran/st.cc b/gcc/fortran/st.cc index 6a605ad91d4..cc1e8e7448b 100644 --- a/gcc/fortran/st.cc +++ b/gcc/fortran/st.cc @@ -299,6 +299,10 @@ gfc_free_statement (gfc_code *p) case EXEC_OMP_TASKYIELD: break; + case EXEC_OMP_METADIRECTIVE: + gfc_free_omp_variants (p->ext.omp_variants); + break; + default: gfc_internal_error ("gfc_free_statement(): Bad statement"); } diff --git a/gcc/fortran/symbol.cc b/gcc/fortran/symbol.cc index 8f7deac1d1e..ce9a3bd065e 100644 --- a/gcc/fortran/symbol.cc +++ b/gcc/fortran/symbol.cc @@ -2704,10 +2704,13 @@ free_components (gfc_component *p) static int compare_st_labels (void *a1, void *b1) { - int a = ((gfc_st_label *) a1)->value; - int b = ((gfc_st_label *) b1)->value; + gfc_st_label *a = (gfc_st_label *) a1; + gfc_st_label *b = (gfc_st_label *) b1; - return (b - a); + if (a->omp_region == b->omp_region) + return b->value - a->value; + else + return b->omp_region - a->omp_region; } @@ -2757,6 +2760,7 @@ gfc_get_st_label (int labelno) { gfc_st_label *lp; gfc_namespace *ns; + int omp_region = gfc_in_metadirective_body ? gfc_omp_region_count : 0; if (gfc_current_state () == COMP_DERIVED) ns = gfc_current_block ()->f2k_derived; @@ -2773,10 +2777,16 @@ gfc_get_st_label (int labelno) lp = ns->st_labels; while (lp) { - if (lp->value == labelno) - return lp; - - if (lp->value < labelno) + if (lp->omp_region == omp_region) + { + if (lp->value == labelno) + return lp; + if (lp->value < labelno) + lp = lp->left; + else + lp = lp->right; + } + else if (lp->omp_region < omp_region) lp = lp->left; else lp = lp->right; @@ -2788,6 +2798,7 @@ gfc_get_st_label (int labelno) lp->defined = ST_LABEL_UNKNOWN; lp->referenced = ST_LABEL_UNKNOWN; lp->ns = ns; + lp->omp_region = omp_region; gfc_insert_bbt (&ns->st_labels, lp, compare_st_labels); diff --git a/gcc/fortran/trans-decl.cc b/gcc/fortran/trans-decl.cc index 301439baaf5..16c6fc904c6 100644 --- a/gcc/fortran/trans-decl.cc +++ b/gcc/fortran/trans-decl.cc @@ -327,7 +327,10 @@ gfc_get_label_decl (gfc_st_label * lp) gcc_assert (lp != NULL && lp->value <= MAX_LABEL_VALUE); /* Build a mangled name for the label. */ - sprintf (label_name, "__label_%.6d", lp->value); + if (lp->omp_region) + sprintf (label_name, "__label_%d_%.6d", lp->omp_region, lp->value); + else + sprintf (label_name, "__label_%.6d", lp->value); /* Build the LABEL_DECL node. */ label_decl = gfc_build_label_decl (get_identifier (label_name)); diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc index 99ec0320258..854d3ac14f0 100644 --- a/gcc/fortran/trans-openmp.cc +++ b/gcc/fortran/trans-openmp.cc @@ -8240,6 +8240,8 @@ gfc_trans_omp_directive (gfc_code *code) case EXEC_OMP_MASTER_TASKLOOP: case EXEC_OMP_MASTER_TASKLOOP_SIMD: return gfc_trans_omp_master_masked_taskloop (code, code->op); + case EXEC_OMP_METADIRECTIVE: + return gfc_trans_omp_metadirective (code); case EXEC_OMP_ORDERED: return gfc_trans_omp_ordered (code); case EXEC_OMP_PARALLEL: @@ -8331,6 +8333,100 @@ gfc_trans_omp_declare_simd (gfc_namespace *ns) } } +/* Translate the context selector list GFC_SELECTORS, using WHERE as the + locus for error messages. */ + +static tree +gfc_trans_omp_set_selector (gfc_omp_set_selector *gfc_selectors, locus where) +{ + tree set_selectors = NULL_TREE; + gfc_omp_set_selector *oss; + + for (oss = gfc_selectors; oss; oss = oss->next) + { + tree selectors = NULL_TREE; + gfc_omp_selector *os; + enum omp_tss_code set = oss->code; + gcc_assert (set != OMP_TRAIT_SET_INVALID); + + for (os = oss->trait_selectors; os; os = os->next) + { + tree scoreval = NULL_TREE; + tree properties = NULL_TREE; + gfc_omp_trait_property *otp; + enum omp_ts_code sel = os->code; + + /* Per the spec, "Implementations can ignore specified + selectors that are not those described in this section"; + however, we must record such selectors because they + cause match failures. */ + if (sel == OMP_TRAIT_INVALID) + { + selectors = make_trait_selector (sel, NULL_TREE, NULL_TREE, + selectors); + continue; + } + + for (otp = os->properties; otp; otp = otp->next) + { + switch (otp->property_kind) + { + case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR: + case OMP_TRAIT_PROPERTY_BOOL_EXPR: + { + tree expr = NULL_TREE; + gfc_se se; + gfc_init_se (&se, NULL); + gfc_conv_expr (&se, otp->expr); + expr = se.expr; + properties = make_trait_property (NULL_TREE, expr, + properties); + } + break; + case OMP_TRAIT_PROPERTY_ID: + properties + = make_trait_property (get_identifier (otp->name), + NULL_TREE, properties); + break; + case OMP_TRAIT_PROPERTY_NAME_LIST: + { + tree prop = OMP_TP_NAMELIST_NODE; + tree value = NULL_TREE; + if (otp->is_name) + value = get_identifier (otp->name); + else + value = gfc_conv_constant_to_tree (otp->expr); + + properties = make_trait_property (prop, value, + properties); + } + break; + case OMP_TRAIT_PROPERTY_CLAUSE_LIST: + properties = gfc_trans_omp_clauses (NULL, otp->clauses, + where, true); + break; + default: + gcc_unreachable (); + } + } + + if (os->score) + { + gfc_se se; + gfc_init_se (&se, NULL); + gfc_conv_expr (&se, os->score); + scoreval = se.expr; + } + + selectors = make_trait_selector (sel, scoreval, + properties, selectors); + } + set_selectors = make_trait_set_selector (set, selectors, set_selectors); + } + return set_selectors; +} + + void gfc_trans_omp_declare_variant (gfc_namespace *ns) { @@ -8406,90 +8502,8 @@ gfc_trans_omp_declare_variant (gfc_namespace *ns) && strcmp (odv->base_proc_symtree->name, ns->proc_name->name))) continue; - tree set_selectors = NULL_TREE; - gfc_omp_set_selector *oss; - - for (oss = odv->set_selectors; oss; oss = oss->next) - { - tree selectors = NULL_TREE; - gfc_omp_selector *os; - enum omp_tss_code set = oss->code; - gcc_assert (set != OMP_TRAIT_SET_INVALID); - - for (os = oss->trait_selectors; os; os = os->next) - { - tree scoreval = NULL_TREE; - tree properties = NULL_TREE; - gfc_omp_trait_property *otp; - enum omp_ts_code sel = os->code; - - /* Per the spec, "Implementations can ignore specified - selectors that are not those described in this section"; - however, we must record such selectors because they - cause match failures. */ - if (sel == OMP_TRAIT_INVALID) - { - selectors = make_trait_selector (sel, NULL_TREE, NULL_TREE, - selectors); - continue; - } - - for (otp = os->properties; otp; otp = otp->next) - { - switch (otp->property_kind) - { - case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR: - case OMP_TRAIT_PROPERTY_BOOL_EXPR: - { - gfc_se se; - gfc_init_se (&se, NULL); - gfc_conv_expr (&se, otp->expr); - properties = make_trait_property (NULL_TREE, se.expr, - properties); - } - break; - case OMP_TRAIT_PROPERTY_ID: - properties - = make_trait_property (get_identifier (otp->name), - NULL_TREE, properties); - break; - case OMP_TRAIT_PROPERTY_NAME_LIST: - { - tree prop = OMP_TP_NAMELIST_NODE; - tree value = NULL_TREE; - if (otp->is_name) - value = get_identifier (otp->name); - else - value = gfc_conv_constant_to_tree (otp->expr); - - properties = make_trait_property (prop, value, - properties); - } - break; - case OMP_TRAIT_PROPERTY_CLAUSE_LIST: - properties = gfc_trans_omp_clauses (NULL, otp->clauses, - odv->where, true); - break; - default: - gcc_unreachable (); - } - } - - if (os->score) - { - gfc_se se; - gfc_init_se (&se, NULL); - gfc_conv_expr (&se, os->score); - scoreval = se.expr; - } - - selectors = make_trait_selector (sel, scoreval, - properties, selectors); - } - set_selectors = make_trait_set_selector (set, selectors, - set_selectors); - } - + tree set_selectors = gfc_trans_omp_set_selector (odv->set_selectors, + odv->where); const char *variant_proc_name = odv->variant_proc_symtree->name; gfc_symbol *variant_proc_sym = odv->variant_proc_symtree->n.sym; if (variant_proc_sym == NULL || variant_proc_sym->attr.implicit_type) @@ -8590,3 +8604,54 @@ gfc_omp_call_is_alloc (tree ptr) } return build_call_expr_loc (input_location, fn, 1, ptr); } + +tree +gfc_trans_omp_metadirective (gfc_code *code) +{ + gfc_omp_variant *variant = code->ext.omp_variants; + + tree metadirective_tree = make_node (OMP_METADIRECTIVE); + SET_EXPR_LOCATION (metadirective_tree, gfc_get_location (&code->loc)); + TREE_TYPE (metadirective_tree) = void_type_node; + OMP_METADIRECTIVE_VARIANTS (metadirective_tree) = NULL_TREE; + + tree tree_body = NULL_TREE; + + while (variant) + { + tree ctx = gfc_trans_omp_set_selector (variant->selectors, + variant->where); + ctx = omp_check_context_selector (gfc_get_location (&variant->where), + ctx, true); + if (ctx == error_mark_node) + return error_mark_node; + + /* If the selector doesn't match, drop the whole variant. */ + if (!omp_context_selector_matches (ctx, true, true)) + { + variant = variant->next; + continue; + } + + gfc_code *next_code = variant->code->next; + if (next_code && tree_body == NULL_TREE) + tree_body = gfc_trans_code (next_code); + + if (next_code) + variant->code->next = NULL; + tree directive = gfc_trans_code (variant->code); + if (next_code) + variant->code->next = next_code; + + tree body = next_code ? tree_body : NULL_TREE; + tree omp_variant = make_omp_metadirective_variant (ctx, directive, body); + OMP_METADIRECTIVE_VARIANTS (metadirective_tree) + = chainon (OMP_METADIRECTIVE_VARIANTS (metadirective_tree), + omp_variant); + variant = variant->next; + } + + /* TODO: Resolve the metadirective here if possible. */ + + return metadirective_tree; +} diff --git a/gcc/fortran/trans-stmt.h b/gcc/fortran/trans-stmt.h index 0f0f99931ca..d19e161cf11 100644 --- a/gcc/fortran/trans-stmt.h +++ b/gcc/fortran/trans-stmt.h @@ -71,6 +71,7 @@ tree gfc_trans_deallocate (gfc_code *); tree gfc_trans_omp_directive (gfc_code *); void gfc_trans_omp_declare_simd (gfc_namespace *); void gfc_trans_omp_declare_variant (gfc_namespace *); +tree gfc_trans_omp_metadirective (gfc_code *code); tree gfc_trans_oacc_directive (gfc_code *); tree gfc_trans_oacc_declare (gfc_namespace *); diff --git a/gcc/fortran/trans.cc b/gcc/fortran/trans.cc index badad6ae892..25255a8b43e 100644 --- a/gcc/fortran/trans.cc +++ b/gcc/fortran/trans.cc @@ -2611,6 +2611,7 @@ trans_code (gfc_code * code, tree cond) case EXEC_OMP_MASTER: case EXEC_OMP_MASTER_TASKLOOP: case EXEC_OMP_MASTER_TASKLOOP_SIMD: + case EXEC_OMP_METADIRECTIVE: case EXEC_OMP_ORDERED: case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-1.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-1.f90 new file mode 100644 index 00000000000..c5b3946341d --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-1.f90 @@ -0,0 +1,55 @@ +! { dg-do compile } + +program main + integer, parameter :: N = 10 + integer, dimension(N) :: a + integer, dimension(N) :: b + integer, dimension(N) :: c + integer :: i + + do i = 1, N + a(i) = i * 2 + b(i) = i * 3 + end do + + !$omp metadirective & + !$omp& default (teams loop) & + !$omp& default (parallel loop) ! { dg-error "too many 'otherwise' or 'default' clauses in 'metadirective' at .1." } + do i = 1, N + c(i) = a(i) * b(i) + end do + + !$omp metadirective & + !$omp& otherwise (teams loop) & + !$omp& default (parallel loop) ! { dg-error "too many 'otherwise' or 'default' clauses in 'metadirective' at .1." } + do i = 1, N + c(i) = a(i) * b(i) + end do + + !$omp metadirective & + !$omp& otherwise (teams loop) & + !$omp& otherwise (parallel loop) ! { dg-error "too many 'otherwise' or 'default' clauses in 'metadirective' at .1." } + do i = 1, N + c(i) = a(i) * b(i) + end do + + !$omp metadirective default (xyz) ! { dg-error "Unclassifiable OpenMP directive at .1." } + do i = 1, N + c(i) = a(i) * b(i) + end do + + !$omp metadirective & + !$omp& default (teams loop) & ! { dg-error "expected 'when', 'otherwise', or 'default' at .1." } + !$omp& where (device={arch("nvptx")}: parallel loop) + do i = 1, N + c(i) = a(i) * b(i) + end do + + !$omp begin metadirective & + !$omp& when (device={arch("nvptx")}: parallel do) & + !$omp& default (barrier) ! { dg-error "variant directive used in OMP BEGIN METADIRECTIVE at .1. must have a corresponding end directive" } + do i = 1, N + c(i) = a(i) * b(i) + end do + !$omp end metadirective ! { dg-error "Unexpected !.OMP END METADIRECTIVE statement at .1." } +end program diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-10.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-10.f90 new file mode 100644 index 00000000000..5dad5d29eb6 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-10.f90 @@ -0,0 +1,40 @@ +! { dg-do compile } + +program metadirectives + implicit none + logical :: UseDevice + + !$OMP metadirective & + !$OMP when ( user = { condition ( UseDevice ) } & + !$OMP : parallel ) & + !$OMP default ( parallel ) + block + call bar() + end block + + !$OMP metadirective & + !$OMP when ( user = { condition ( UseDevice ) } & + !$OMP : parallel ) & + !$OMP default ( parallel ) + call bar() + !$omp end parallel ! Accepted, because all cases have 'parallel' + + !$OMP begin metadirective & + !$OMP when ( user = { condition ( UseDevice ) } & + !$OMP : nothing ) & + !$OMP default ( parallel ) + call bar() + block + call foo() + end block + !$OMP end metadirective + + !$OMP begin metadirective & + !$OMP when ( user = { condition ( UseDevice ) } & + !$OMP : parallel ) & + !$OMP default ( parallel ) + call bar() + !$omp end parallel ! { dg-error "Unexpected !.OMP END PARALLEL statement at .1." } +end program ! { dg-error "Unexpected END statement at .1." } + +! { dg-error "Unexpected end of file" "" { target *-*-* } 0 } diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-11.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-11.f90 new file mode 100644 index 00000000000..e7de70e6259 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-11.f90 @@ -0,0 +1,33 @@ +! { dg-do compile } +! { dg-ice "Statements following a block in a metadirective" } +! PR fortran/107067 + +program metadirectives + implicit none + logical :: UseDevice + + !$OMP begin metadirective & + !$OMP when ( user = { condition ( UseDevice ) } & + !$OMP : nothing ) & + !$OMP default ( parallel ) + block + call foo() + end block + call bar() ! FIXME/XFAIL ICE in parse_omp_metadirective_body() + !$omp end metadirective + + + !$OMP begin metadirective & + !$OMP when ( user = { condition ( UseDevice ) } & + !$OMP : nothing ) & + !$OMP default ( parallel ) + block + call bar() + end block + block ! FIXME/XFAIL ICE in parse_omp_metadirective_body() + call foo() + end block + !$omp end metadirective +end program + + diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-2.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-2.f90 new file mode 100644 index 00000000000..cdd5e85068e --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-2.f90 @@ -0,0 +1,62 @@ +! { dg-do compile } + +program main + integer, parameter :: N = 100 + integer :: x = 0 + integer :: y = 0 + integer :: i + + ! Test implicit default directive + !$omp metadirective & + !$omp& when (device={arch("nvptx")}: barrier) + x = 1 + + ! Test implicit default directive combined with a directive that takes a + ! do loop. + !$omp metadirective & + !$omp& when (device={arch("nvptx")}: parallel do) + do i = 1, N + x = x + i + end do + + ! Test with multiple standalone directives. + !$omp metadirective & + !$omp& when (device={arch("nvptx")}: barrier) & + !$omp& default (flush) + x = 1 + + ! Test combining a standalone directive with one that takes a do loop. + !$omp metadirective & + !$omp& when (device={arch("nvptx")}: parallel do) & + !$omp& default (barrier) + do i = 1, N + x = x + i + end do + + ! Test combining a directive that takes a do loop with one that takes + ! a statement body. + !$omp begin metadirective & + !$omp& when (device={arch("nvptx")}: parallel do) & + !$omp& default (parallel) + do i = 1, N + x = x + i + end do + !$omp end metadirective + + ! Test labels in the body. + !$omp begin metadirective & + !$omp& when (device={arch("nvptx")}: parallel do) & + !$omp& when (device={arch("gcn")}: parallel) + do i = 1, N + x = x + i + if (x .gt. N/2) goto 10 +10 x = x + 1 + goto 20 + x = x + 2 +20 continue + end do + !$omp end metadirective + + ! Test empty metadirective. + !$omp metadirective +end program diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-3.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-3.f90 new file mode 100644 index 00000000000..eca389a7842 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-3.f90 @@ -0,0 +1,34 @@ +! { dg-do compile } +! { dg-additional-options "-fdump-tree-original" } +! { dg-additional-options "-fdump-tree-gimple" } +! { dg-additional-options "-fdump-tree-optimized" } + +module test + integer, parameter :: N = 100 +contains + subroutine f (x, y, z) + integer :: x(N), y(N), z(N) + + !$omp target map (to: v1, v2) map(from: v3) + !$omp metadirective & + !$omp& when(device={arch("nvptx")}: teams loop) & + !$omp& default(parallel loop) + do i = 1, N + z(i) = x(i) * y(i) + enddo + !$omp end target + end subroutine +end module + +! The metadirective should be resolved after Gimplification. + +! { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "original" } } +! { dg-final { scan-tree-dump-times "when \\(device =.*arch.*nvptx.*\\):" 1 "original" } } +! { dg-final { scan-tree-dump-times "#pragma omp teams" 1 "original" } } +! { dg-final { scan-tree-dump-times "default:" 1 "original" } } +! { dg-final { scan-tree-dump-times "#pragma omp parallel" 1 "original" } } +! { dg-final { scan-tree-dump-times "#pragma omp loop" 2 "original" } } + +! { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "gimple" } } + +! { dg-final { scan-tree-dump-not "#pragma omp metadirective" "optimized" } } diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-4.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-4.f90 new file mode 100644 index 00000000000..2fdc7f3c515 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-4.f90 @@ -0,0 +1,39 @@ +! { dg-do compile } +! { dg-additional-options "-fdump-tree-original" } +! { dg-additional-options "-fdump-tree-gimple" } + +program test + implicit none + integer, parameter :: N = 100 + real :: a(N) + + !$omp target map(from: a) + call f (a, 3.14159) + !$omp end target + + ! TODO: This does not execute a version of f with the default clause + ! active as might be expected. + call f (a, 2.71828) ! { dg-warning "direct calls to an offloadable function containing metadirectives with a 'construct={target}' selector may produce unexpected results" } +contains + subroutine f (a, x) + integer :: i + real :: a(N), x + !$omp declare target + + !$omp metadirective & + !$omp& when (construct={target}: distribute parallel do ) & + !$omp& default(parallel do simd) + do i = 1, N + a(i) = x * i + end do + end subroutine +end program + +! The metadirective should be resolved during Gimplification. + +! { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "original" } } +! { dg-final { scan-tree-dump-times "when \\(construct = .*target.*\\):" 1 "original" } } +! { dg-final { scan-tree-dump-times "default:" 1 "original" } } +! { dg-final { scan-tree-dump-times "#pragma omp parallel" 2 "original" } } + +! { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-5.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-5.f90 new file mode 100644 index 00000000000..03970393eb4 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-5.f90 @@ -0,0 +1,30 @@ +! { dg-do compile } +! { dg-additional-options "-fdump-tree-gimple" } + +module test + integer, parameter :: N = 100 +contains + subroutine f (a, flag) + integer :: a(N) + logical :: flag + integer :: i + + !$omp metadirective & + !$omp& when (user={condition(flag)}: & + !$omp& target teams distribute parallel do map(from: a(1:N))) & + !$omp& default(parallel do) + do i = 1, N + a(i) = i + end do + end subroutine +end module + +! The metadirective should be resolved at parse time, but is currently +! resolved during Gimplification + +! { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } +! { dg-final { scan-tree-dump-times "#pragma omp target" 1 "gimple" } } +! { dg-final { scan-tree-dump-times "#pragma omp teams" 1 "gimple" } } +! { dg-final { scan-tree-dump-times "#pragma omp distribute" 1 "gimple" } } +! { dg-final { scan-tree-dump-times "#pragma omp parallel" 2 "gimple" } } +! { dg-final { scan-tree-dump-times "#pragma omp for" 2 "gimple" } } diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-6.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-6.f90 new file mode 100644 index 00000000000..9b6c371296f --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-6.f90 @@ -0,0 +1,31 @@ +! { dg-do compile } +! { dg-additional-options "-fdump-tree-gimple" } + +module test + integer, parameter :: N = 100 +contains + subroutine f (a, run_parallel, run_guided) + integer :: a(N) + logical :: run_parallel, run_guided + integer :: i + + !$omp begin metadirective when(user={condition(run_parallel)}: parallel) + !$omp metadirective & + !$omp& when(construct={parallel}, user={condition(run_guided)}: & + !$omp& do schedule(guided)) & + !$omp& when(construct={parallel}: do schedule(static)) + do i = 1, N + a(i) = i + end do + !$omp end metadirective + end subroutine +end module + +! The outer metadirective should be resolved at parse time, but is +! currently resolved during Gimplification. + +! The inner metadirective should be resolved during Gimplificiation. + +! { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } +! { dg-final { scan-tree-dump-times "#pragma omp parallel" 1 "gimple" } } +! { dg-final { scan-tree-dump-times "#pragma omp for" 2 "gimple" } } diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-7.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-7.f90 new file mode 100644 index 00000000000..31a44c47359 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-7.f90 @@ -0,0 +1,36 @@ +! { dg-do compile } +! { dg-additional-options "-fdump-tree-gimple" } + +program main + integer, parameter :: N = 256 +contains + subroutine f (a, num) + integer :: a(N) + integer :: num + integer :: i + + !$omp metadirective & + !$omp& when (target_device={device_num(num), kind("gpu"), arch("nvptx")}: & + !$omp& target parallel do map(tofrom: a(1:N))) & + !$omp& when (target_device={device_num(num), kind("gpu"), & + !$omp& arch("amdgcn"), isa("gfx906")}: & + !$omp& target parallel do) & + !$omp& when (target_device={device_num(num), kind("cpu"), arch("x86_64")}: & + !$omp& parallel do) + do i = 1, N + a(i) = a(i) + i + end do + + !$omp metadirective & + !$omp& when (target_device={kind("gpu"), arch("nvptx")}: & + !$omp& target parallel do map(tofrom: a(1:N))) + do i = 1, N + a(i) = a(i) + i + end do + end subroutine +end program + +! { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(.+, &\"gpu.x00\"\\\[0\\\], &\"amdgcn.x00\"\\\[0\\\], &\"gfx906.x00\"\\\[0\\\]\\)" "gimple" } } +! { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(.+, &\"gpu.x00\"\\\[0\\\], &\"nvptx.x00\"\\\[0\\\], 0B\\)" "gimple" } } +! { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(.+, &\"cpu.x00\"\\\[0\\\], &\"x86_64.x00\"\\\[0\\\], 0B\\)" "gimple" } } +! { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(-2, &\"gpu.x00\"\\\[0\\\], &\"nvptx.x00\"\\\[0\\\], 0B\\)" "gimple" } } diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-8.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-8.f90 new file mode 100644 index 00000000000..1ebcd33a7be --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-8.f90 @@ -0,0 +1,22 @@ +! { dg-do compile } + +program test + integer :: i + integer, parameter :: N = 100 + integer :: sum = 0 + + ! The compiler should never consider a situation where both metadirectives + ! match, but that does not matter because the spec says "Replacement of + ! the metadirective with the directive variant associated with any of the + ! dynamic replacement candidates must result in a conforming OpenMP + ! program. So the second metadirective is rejected as not being + ! a valid loop-nest even if the first one does not match. + +!$omp metadirective when (implementation={vendor("ibm")}: & + !$omp& target teams distribute) + !$omp metadirective when (implementation={vendor("gnu")}: parallel do) ! { dg-error "Unexpected !.OMP METADIRECTIVE statement" } + do i = 1, N + sum = sum + i + end do +end program + diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-9.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-9.f90 new file mode 100644 index 00000000000..e6ab3fc0a65 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-9.f90 @@ -0,0 +1,30 @@ +! { dg-do compile } + +program OpenMP_Metadirective_WrongEnd_Test + implicit none + + integer :: & + iaVS, iV, jV, kV + integer, dimension ( 3 ) :: & + lV, uV + logical :: & + UseDevice + + !$OMP metadirective & + !$OMP when ( user = { condition ( UseDevice ) } & + !$OMP : target teams distribute parallel do simd collapse ( 3 ) & + !$OMP private ( iaVS ) ) & + !$OMP default ( parallel do simd collapse ( 3 ) private ( iaVS ) ) + do kV = lV ( 3 ), uV ( 3 ) + do jV = lV ( 2 ), uV ( 2 ) + do iV = lV ( 1 ), uV ( 1 ) + + + end do + end do + end do + !$OMP end target teams distribute parallel do simd ! { dg-error "Unexpected !.OMP END TARGET TEAMS DISTRIBUTE PARALLEL DO SIMD statement in an OMP METADIRECTIVE block at .1." } + + +end program + diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-construct.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-construct.f90 new file mode 100644 index 00000000000..ec1f0ee3d9d --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-construct.f90 @@ -0,0 +1,260 @@ +! { dg-do compile } +! { dg-additional-options "-foffload=disable -fdump-tree-original -fdump-tree-gimple" } + +program main +implicit none + +integer, parameter :: N = 10 +double precision, parameter :: S = 2.0 +double precision :: a(N) + +call init (N, a) +call f1 (N, a, S) +call check (N, a, S) + +call init (N, a) +call f2 (N, a, S) +call check (N, a, S) + +call init (N, a) +call f3 (N, a, S) +call check (N, a, S) + +call init (N, a) +call f4 (N, a, S) +call check (N, a, S) + +call init (N, a) +call f5 (N, a, S) +call check (N, a, S) + +call init (N, a) +call f6 (N, a, S) +call check (N, a, S) + +call init (N, a) +call f7 (N, a, S) +call check (N, a, S) + +call init (N, a) +call f8 (N, a, S) +call check (N, a, S) + +call init (N, a) +call f9 (N, a, S) +call check (N, a, S) + +contains + +subroutine init (n, a) + implicit none + integer :: n + double precision :: a(n) + integer :: i + do i = 1, n + a(i) = i + end do +end subroutine + +subroutine check (n, a, s) + implicit none + integer :: n + double precision :: a(n) + double precision :: s + integer :: i + do i = 1, n + if (a(i) /= i * s) error stop + end do +end subroutine + +! Check various combinations for enforcing correct ordering of +! construct matches. +subroutine f1 (n, a, s) + implicit none + integer :: n + double precision :: a(n) + double precision :: s + integer :: i +!$omp target teams +!$omp parallel +!$omp metadirective & +!$omp & when (construct={target} & +!$omp & : do) & +!$omp & default (error at(execution) message("f1 match failed")) + do i = 1, n + a(i) = a(i) * s + end do +!$omp end parallel +!$omp end target teams +end subroutine + +subroutine f2 (n, a, s) + implicit none + integer :: n + double precision :: a(n) + double precision :: s + integer :: i +!$omp target teams +!$omp parallel +!$omp metadirective & +!$omp & when (construct={teams, parallel} & +!$omp & : do) & +!$omp & default (error at(execution) message("f2 match failed")) + do i = 1, n + a(i) = a(i) * s + end do +!$omp end parallel +!$omp end target teams +end subroutine + +subroutine f3 (n, a, s) + implicit none + integer :: n + double precision :: a(n) + double precision :: s + integer :: i +!$omp target teams +!$omp parallel +!$omp metadirective & +!$omp & when (construct={target, teams, parallel} & +!$omp & : do) & +!$omp & default (error at(execution) message("f3 match failed")) + do i = 1, n + a(i) = a(i) * s + end do +!$omp end parallel +!$omp end target teams +end subroutine + +subroutine f4 (n, a, s) + implicit none + integer :: n + double precision :: a(n) + double precision :: s + integer :: i +!$omp target teams +!$omp parallel +!$omp metadirective & +!$omp & when (construct={target, parallel} & +!$omp & : do) & +!$omp & default (error at(execution) message("f4 match failed")) + do i = 1, n + a(i) = a(i) * s + end do +!$omp end parallel +!$omp end target teams +end subroutine + +subroutine f5 (n, a, s) + implicit none + integer :: n + double precision :: a(n) + double precision :: s + integer :: i +!$omp target teams +!$omp parallel +!$omp metadirective & +!$omp & when (construct={target, teams} & +!$omp & : do) & +!$omp & default (error at(execution) message("f5 match failed")) + do i = 1, n + a(i) = a(i) * s + end do +!$omp end parallel +!$omp end target teams +end subroutine + +! Next batch is for things where the construct doesn't match the context. +subroutine f6 (n, a, s) + implicit none + integer :: n + double precision :: a(n) + double precision :: s + integer :: i +!$omp target +!$omp teams +!$omp metadirective & +!$omp & when (construct={parallel} & +!$omp & : error at(execution) message("f6 match failed")) & +!$omp & default (parallel do) + do i = 1, n + a(i) = a(i) * s + end do +!$omp end teams +!$omp end target +end subroutine + +subroutine f7 (n, a, s) + implicit none + integer :: n + double precision :: a(n) + double precision :: s + integer :: i +!$omp target +!$omp teams +!$omp metadirective & +!$omp & when (construct={target, parallel} & +!$omp & : error at(execution) message("f7 match failed")) & +!$omp & default (parallel do) + do i = 1, n + a(i) = a(i) * s + end do +!$omp end teams +!$omp end target +end subroutine + +subroutine f8 (n, a, s) + implicit none + integer :: n + double precision :: a(n) + double precision :: s + integer :: i +!$omp target +!$omp teams +!$omp metadirective & +!$omp & when (construct={parallel, target} & +!$omp & : error at(execution) message("f8 match failed")) & +!$omp & default (parallel do) + do i = 1, n + a(i) = a(i) * s + end do +!$omp end teams +!$omp end target +end subroutine + +! Next test choosing the best alternative when there are multiple +! matches. +subroutine f9 (n, a, s) + implicit none + integer :: n + double precision :: a(n) + double precision :: s + integer :: i +!$omp target teams +!$omp parallel +!$omp metadirective & +!$omp & when (construct={teams, parallel} & +!$omp & : error at(execution) message("f9 match incorrect 1")) & +!$omp & when (construct={target, teams, parallel} & +!$omp & : do) & +!$omp & when (construct={target, teams} & +!$omp & : error at(execution) message("f9 match incorrect 2")) & +!$omp & default (error at(execution) message("f9 match failed")) + do i = 1, n + a(i) = a(i) * s + end do +!$omp end parallel +!$omp end target teams +end subroutine + +end program + +! Note there are no tests for the matching the extended simd clause +! syntax, which is only useful for "declare variant". + + +! After parsing, there should be a runtime error call for each of the +! failure cases, but they should all be optimized away during OMP +! lowering. +! { dg-final { scan-tree-dump-times "__builtin_GOMP_error" 11 "original" } } +! { dg-final { scan-tree-dump-not "__builtin_GOMP_error" "gimple" } } diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-no-score.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-no-score.f90 new file mode 100644 index 00000000000..968ce609b10 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-no-score.f90 @@ -0,0 +1,122 @@ +! { dg-do compile { target x86_64-*-* } } +! { dg-additional-options "-foffload=disable" } + +! This test is expected to fail with compile-time errors: +! "A trait-score cannot be specified in traits from the construct, +! device or target_device trait-selector-sets." + + +subroutine f1 (n, a, s) + implicit none + integer :: n + double precision :: a(*) + double precision :: s + integer :: i +!$omp metadirective & +!$omp& when (device={kind (score(5) : host)} & +!$omp& : parallel do) + ! { dg-error ".score. cannot be specified in traits in the .device. trait-selector-set" "" { target *-*-*} .-2 } + do i = 1, n + a(i) = a(i) * s; + end do +end subroutine + +subroutine f2 (n, a, s) + implicit none + integer :: n + double precision :: a(*) + double precision :: s + integer :: i +!$omp metadirective & +!$omp& when (device={kind (host), arch (score(6) : x86_64), isa (avx512f)} & +!$omp& : parallel do) + ! { dg-error ".score. cannot be specified in traits in the .device. trait-selector-set" "" { target *-*-*} .-2 } + do i = 1, n + a(i) = a(i) * s; + end do +end subroutine + +subroutine f3 (n, a, s) + implicit none + integer :: n + double precision :: a(*) + double precision :: s + integer :: i +!$omp metadirective & +!$omp& when (device={kind (host), arch (score(6) : x86_64), & +!$omp& isa (score(7): avx512f)} & +!$omp& : parallel do) + ! { dg-error ".score. cannot be specified in traits in the .device. trait-selector-set" "" { target *-*-*} .-3 } + do i = 1, n + a(i) = a(i) * s; + end do +end subroutine + +subroutine f4 (n, a, s) + implicit none + integer :: n + double precision :: a(*) + double precision :: s + integer :: i + integer, parameter :: omp_initial_device = -1 +!$omp metadirective & +!$omp& when (target_device={device_num (score(42) : omp_initial_device), & +!$omp& kind (host)} & +!$omp& : parallel do) + ! { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-3 } + do i = 1, n + a(i) = a(i) * s; + end do +end subroutine + +subroutine f5 (n, a, s) + implicit none + integer :: n + double precision :: a(*) + double precision :: s + integer :: i + integer, parameter :: omp_initial_device = -1 +!$omp metadirective & +!$omp& when (target_device={device_num(omp_initial_device), & +!$omp& kind (score(5) : host)} & +!$omp& : parallel do) + ! { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-2 } + do i = 1, n + a(i) = a(i) * s; + end do +end subroutine + +subroutine f6 (n, a, s) + implicit none + integer :: n + double precision :: a(*) + double precision :: s + integer :: i + integer, parameter :: omp_initial_device = -1 +!$omp metadirective & +!$omp& when (target_device={device_num(omp_initial_device), kind (host), & +!$omp& arch (score(6) : x86_64), isa (avx512f)} & +!$omp& : parallel do) + ! { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-2 } + do i = 1, n + a(i) = a(i) * s; + end do +end subroutine + +subroutine f7 (n, a, s) + implicit none + integer :: n + double precision :: a(*) + double precision :: s + integer :: i + integer, parameter :: omp_initial_device = -1 +!$omp metadirective & +!$omp& when (target_device={device_num(omp_initial_device), kind (host), & +!$omp& arch (score(6) : x86_64), & +!$omp& isa (score(7): avx512f)} & +!$omp& : parallel do) + ! { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-3 } + do i = 1, n + a(i) = a(i) * s; + end do +end subroutine diff --git a/gcc/testsuite/gfortran.dg/gomp/pure-1.f90 b/gcc/testsuite/gfortran.dg/gomp/pure-1.f90 index 598e455d2e9..15681ac604a 100644 --- a/gcc/testsuite/gfortran.dg/gomp/pure-1.f90 +++ b/gcc/testsuite/gfortran.dg/gomp/pure-1.f90 @@ -86,3 +86,10 @@ pure integer function func_simd(n) end do func_simd = r end + +!pure logical function func_metadirective() +logical function func_metadirective() + implicit none + !$omp metadirective + func_metadirective = .false. +end diff --git a/gcc/testsuite/gfortran.dg/gomp/pure-2.f90 b/gcc/testsuite/gfortran.dg/gomp/pure-2.f90 index 1e3cf8c9416..25d6069b236 100644 --- a/gcc/testsuite/gfortran.dg/gomp/pure-2.f90 +++ b/gcc/testsuite/gfortran.dg/gomp/pure-2.f90 @@ -26,14 +26,6 @@ logical function func_interchange(n) end do end - -!pure logical function func_metadirective() -logical function func_metadirective() - implicit none - !$omp metadirective ! { dg-error "Unclassifiable OpenMP directive" } - func_metadirective = .false. -end - !pure logical function func_reverse(n) logical function func_reverse(n) implicit none diff --git a/libgomp/testsuite/libgomp.fortran/metadirective-1.f90 b/libgomp/testsuite/libgomp.fortran/metadirective-1.f90 new file mode 100644 index 00000000000..7b3e09f7c2a --- /dev/null +++ b/libgomp/testsuite/libgomp.fortran/metadirective-1.f90 @@ -0,0 +1,61 @@ +! { dg-do run } + +program test + implicit none + + integer, parameter :: N = 100 + integer :: x(N), y(N), z(N) + integer :: i + + do i = 1, N + x(i) = i; + y(i) = -i; + end do + + call f (x, y, z) + + do i = 1, N + if (z(i) .ne. x(i) * y(i)) stop 1 + end do + + ! ----- + do i = 1, N + x(i) = i; + y(i) = -i; + end do + + call g (x, y, z) + + do i = 1, N + if (z(i) .ne. x(i) * y(i)) stop 1 + end do + +contains + subroutine f (x, y, z) + integer :: x(N), y(N), z(N) + + !$omp target map (to: x, y) map(from: z) + block + !$omp metadirective & + !$omp& when(device={arch("nvptx")}: teams loop) & + !$omp& default(parallel loop) + do i = 1, N + z(i) = x(i) * y(i) + enddo + end block + end subroutine + subroutine g (x, y, z) + integer :: x(N), y(N), z(N) + + !$omp target map (to: x, y) map(from: z) + block + !$omp metadirective & + !$omp& when(device={arch("nvptx")}: teams loop) & + !$omp& default(parallel loop) + do i = 1, N + z(i) = x(i) * y(i) + enddo + end block + !$omp end target + end subroutine +end program diff --git a/libgomp/testsuite/libgomp.fortran/metadirective-2.f90 b/libgomp/testsuite/libgomp.fortran/metadirective-2.f90 new file mode 100644 index 00000000000..d83474cf2db --- /dev/null +++ b/libgomp/testsuite/libgomp.fortran/metadirective-2.f90 @@ -0,0 +1,40 @@ +! { dg-do run } + +program test + implicit none + integer, parameter :: N = 100 + real, parameter :: PI_CONST = 3.14159 + real, parameter :: E_CONST = 2.71828 + real, parameter :: EPSILON = 0.001 + integer :: i + real :: a(N) + + !$omp target map(from: a) + call f (a, PI_CONST) + !$omp end target + + do i = 1, N + if (abs (a(i) - (PI_CONST * i)) .gt. EPSILON) stop 1 + end do + + ! TODO: This does not execute a version of f with the default clause + ! active as might be expected. + call f (a, E_CONST) ! { dg-warning "direct calls to an offloadable function containing metadirectives with a 'construct={target}' selector may produce unexpected results" } + + do i = 1, N + if (abs (a(i) - (E_CONST * i)) .gt. EPSILON) stop 2 + end do +contains + subroutine f (a, x) + integer :: i + real :: a(N), x + !$omp declare target + + !$omp metadirective & + !$omp& when (construct={target}: distribute parallel do ) & + !$omp& default(parallel do simd) + do i = 1, N + a(i) = x * i + end do + end subroutine +end program diff --git a/libgomp/testsuite/libgomp.fortran/metadirective-3.f90 b/libgomp/testsuite/libgomp.fortran/metadirective-3.f90 new file mode 100644 index 00000000000..693c40bca5a --- /dev/null +++ b/libgomp/testsuite/libgomp.fortran/metadirective-3.f90 @@ -0,0 +1,29 @@ +! { dg-do run } + +program test + implicit none + + integer, parameter :: N = 100 + integer :: a(N) + integer :: res + + if (f (a, .false.)) stop 1 + if (.not. f (a, .true.)) stop 2 +contains + logical function f (a, flag) + integer :: a(N) + logical :: flag + logical :: res = .false. + integer :: i + f = .false. + !$omp metadirective & + !$omp& when (user={condition(.not. flag)}: & + !$omp& target teams distribute parallel do & + !$omp& map(from: a(1:N)) private(res)) & + !$omp& default(parallel do) + do i = 1, N + a(i) = i + f = .true. + end do + end function +end program diff --git a/libgomp/testsuite/libgomp.fortran/metadirective-4.f90 b/libgomp/testsuite/libgomp.fortran/metadirective-4.f90 new file mode 100644 index 00000000000..04fdf61489c --- /dev/null +++ b/libgomp/testsuite/libgomp.fortran/metadirective-4.f90 @@ -0,0 +1,46 @@ +! { dg-do run } + +program test + use omp_lib + + implicit none + integer, parameter :: N = 100 + integer :: a(N) + logical :: is_parallel, is_static + + ! is_static is always set if run_parallel is false. + call f (a, .false., .false., is_parallel, is_static) + if (is_parallel .or. .not. is_static) stop 1 + + call f (a, .false., .true., is_parallel, is_static) + if (is_parallel .or. .not. is_static) stop 2 + + call f (a, .true., .false., is_parallel, is_static) + if (.not. is_parallel .or. is_static) stop 3 + + call f (a, .true., .true., is_parallel, is_static) + if (.not. is_parallel .or. .not. is_static) stop 4 +contains + subroutine f (a, run_parallel, run_static, is_parallel, is_static) + integer :: a(N) + logical, intent(in) :: run_parallel, run_static + logical, intent(out) :: is_parallel, is_static + integer :: i + + is_parallel = .false. + is_static = .false. + + !$omp begin metadirective when(user={condition(run_parallel)}: parallel) + if (omp_in_parallel ()) is_parallel = .true. + + !$omp metadirective & + !$omp& when(construct={parallel}, user={condition(.not. run_static)}: & + !$omp& do schedule(guided) private(is_static)) & + !$omp& when(construct={parallel}: do schedule(static)) + do i = 1, N + a(i) = i + is_static = .true. + end do + !$omp end metadirective + end subroutine +end program diff --git a/libgomp/testsuite/libgomp.fortran/metadirective-5.f90 b/libgomp/testsuite/libgomp.fortran/metadirective-5.f90 new file mode 100644 index 00000000000..3992286dc08 --- /dev/null +++ b/libgomp/testsuite/libgomp.fortran/metadirective-5.f90 @@ -0,0 +1,44 @@ +! { dg-do run } + +program main + use omp_lib + + implicit none + + integer, parameter :: N = 100 + integer :: a(N) + integer :: on_device_count = 0 + integer :: i + + do i = 1, N + a(i) = i + end do + + do i = 0, omp_get_num_devices () + on_device_count = on_device_count + f (a, i) + end do + + if (on_device_count .ne. omp_get_num_devices ()) stop 1 + + do i = 1, N + if (a(i) .ne. 2 * i) stop 2; + end do +contains + integer function f (a, num) + integer, intent(inout) :: a(N) + integer, intent(in) :: num + integer :: on_device + integer :: i + + on_device = 0 + !$omp metadirective & + !$omp& when (target_device={device_num(num), kind("gpu")}: & + !$omp& target parallel do map(to: a(1:N)), map(from: on_device)) & + !$omp& default (parallel do private(on_device)) + do i = 1, N + a(i) = a(i) + i + on_device = 1 + end do + f = on_device; + end function +end program diff --git a/libgomp/testsuite/libgomp.fortran/metadirective-6.f90 b/libgomp/testsuite/libgomp.fortran/metadirective-6.f90 new file mode 100644 index 00000000000..436fdbade2f --- /dev/null +++ b/libgomp/testsuite/libgomp.fortran/metadirective-6.f90 @@ -0,0 +1,58 @@ +! { dg-do compile } + +program test + implicit none + + integer, parameter :: N = 100 + integer :: x(N), y(N), z(N) + integer :: i + +contains + subroutine f (x, y, z) + integer :: x(N), y(N), z(N) + + !$omp target map (to: x, y) map(from: z) ! { dg-error "OMP TARGET region at .1. with a nested TEAMS at .2. may not contain any other statement, declaration or directive outside of the single TEAMS construct" } + block + !$omp metadirective & + !$omp& when(device={arch("nvptx")}: teams loop) & + !$omp& default(parallel loop) ! { dg-error "\\(1\\)" } + ! FIXME: The line above should be the same error as above but some fails here with -fno-diagnostics-show-caret + ! Seems as if some gcc/testsuite/ fix is missing for libgomp/testsuite + do i = 1, N + z(i) = x(i) * y(i) + enddo + z(N) = z(N) + 1 ! <<< invalid + end block + end subroutine + + subroutine f2 (x, y, z) + integer :: x(N), y(N), z(N) + + !$omp target map (to: x, y) map(from: z) ! { dg-error "OMP TARGET region at .1. with a nested TEAMS may not contain any other statement, declaration or directive outside of the single TEAMS construct" } + block + integer :: i ! << invalid + !$omp metadirective & + !$omp& when(device={arch("nvptx")}: teams loop) & + !$omp& default(parallel loop) + do i = 1, N + z(i) = x(i) * y(i) + enddo + end block + end subroutine + subroutine g (x, y, z) + integer :: x(N), y(N), z(N) + + !$omp target map (to: x, y) map(from: z) ! { dg-error "OMP TARGET region at .1. with a nested TEAMS may not contain any other statement, declaration or directive outside of the single TEAMS construct" } + block + !$omp metadirective & ! <<<< invalid + !$omp& when(device={arch("nvptx")}: flush) & + !$omp& default(nothing) + !$omp teams loop + do i = 1, N + z(i) = x(i) * y(i) + enddo + end block + !$omp end target + end subroutine + +end program From patchwork Sat May 4 21:21:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 1931424 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=LpZK8uwq; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX0zT2ZXxz1ybC for ; Sun, 5 May 2024 07:24:05 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A6AC9386F43A for ; Sat, 4 May 2024 21:24:03 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-io1-xd2c.google.com (mail-io1-xd2c.google.com [IPv6:2607:f8b0:4864:20::d2c]) by sourceware.org (Postfix) with ESMTPS id 4799F386F43B for ; Sat, 4 May 2024 21:22:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 4799F386F43B 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 4799F386F43B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::d2c ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857752; cv=none; b=M5kzBXHKb9vjRwF3+z6wAqu1pLpMe4n/0WHy9i/OrtWVpEHqGOq80Cz00UrUkL9Wq6sihzW+XL1O/6j9lpM3at5mCLRAjzfzB4NM6WYQ3T752gYbOHXtFV1mLNJu2LEyEtSccA7IjrLg5POpIC0cBJtCICVO9FEPkaCm8Fsu6mc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857752; c=relaxed/simple; bh=dAvdJ9+owCJYT12bJ4TEo+3V/5swlk4mW6XKzd5krKc=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=X7iiFYduu0fr9WY1cY6RJfqivKZAJQOwv/FXPIedgwWfF+7ClPJ6LlvNtwSbK1DMLeHCeQBqekNOTeWZDwEewP2NL/1gh+pz1i6S4iLu93WdCyoxl2aQq6l8lP9+JncIpK/fCovcQOS9OPbcXs7nIb2cWVsXkcsDlTeOL+ZERNk= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-io1-xd2c.google.com with SMTP id ca18e2360f4ac-7ded214dbdcso35137539f.0 for ; Sat, 04 May 2024 14:22:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857739; x=1715462539; 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=4I/2jtyIYA7RtxAjbWMfqkp3O6ny3eJmFmPZmp8IemI=; b=LpZK8uwqoAhBaNUC/5h7wD1mHOccM2ZlMlaVqxG8ydNPwWMsAGm18xBpng45eUuLlH idoLP4/C1MXwdz8HI9eysD5U2V++2+l3uHrcKeQLOjh3Y/tbswQnwIlMD5uKW3hmc+OQ A1/I1EpYT5zT149WoQL1bIiLQuXquAcOxUfrYBlI4PmL4Jqzx0ZSrbQ+UOkvVy82l68C IjpZOHEk7gUOgs3LBhBZeFMFu1avcjz8S+1FOiYD08Pbhjl3s02PaTUu4XcsZYdRV+Mp 3HNX4fHzvvLKZv+XSqNAgP1TEA0u1ldsiPm5g+zWV3saAlzYLr8kA7qFM7lFzfzNAI9Z QnqA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857739; x=1715462539; 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=4I/2jtyIYA7RtxAjbWMfqkp3O6ny3eJmFmPZmp8IemI=; b=MnElDJPrEvzEFL/wo5ckS9GikAPGsK8uPmFC0KVez0ddTG3eiIAZTjCnAK9Gk16xGt EQ0yaMqaiZnlxJdNUL6d8syqx2TDXGf7u/vZ8ahiZPhN71t636hw1v12fFpAsT2NHInS k525wcWAEKCvVGFQ5hmEf41ifGk9EIomecEIKc0gtbqaOq7pkmjoNjXTbP2kUXx0HBwL 3gdZesQhtaVHMDAAutDGBLZZ3iF4Pu0tRdlL0yGY4wg70E60/vreAcrenJQMVqUHX3Vg i35k/ovsiyEjSk7SWTyCzorqpQ0guBxx0AmbRK4PQILaG9PjGkBLsORUAk2lwU3Vc9da NKXA== X-Gm-Message-State: AOJu0Yyw2Vdyw07eQ4Y7TI11j6wnv0ButCeWFZpHRCbfaiG/FwMywIpV BI+/YUNZ9G/k9yTyie5VT1XDYcptcTgJUwraOl2YaiG1UChM+6GFvraLYJb7+zhCOLCW8odePZU X X-Google-Smtp-Source: AGHT+IHBLuRpqjjzHJAIWXyBJqCgpauJmrrJMCCTZI/ydusaRJUI2BGgFvm0YJCIzP8hyC4IP5CFsQ== X-Received: by 2002:a92:c542:0:b0:36c:6062:8787 with SMTP id a2-20020a92c542000000b0036c60628787mr8237983ilj.26.1714857738673; Sat, 04 May 2024 14:22:18 -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.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:18 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 08/12] OpenMP: Reject other properties with kind(any) Date: Sat, 4 May 2024 15:21:48 -0600 Message-Id: <20240504212153.3561429-9-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 X-Spam-Status: No, score=-11.3 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 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+incoming=patchwork.ozlabs.org@gcc.gnu.org The OpenMP spec says: "If trait-property any is specified in the kind trait-selector of the device selector set or the target_device selector sets, no other trait-property may be specified in the same selector set." GCC was not previously enforcing this restriction and several testcases included such valid constructs. This patch fixes it. gcc/ChangeLog * omp-general.cc (omp_check_context_selector): Reject other properties in the same selector set with kind(any). gcc/testsuite/ChangeLog * c-c++-common/gomp/declare-variant-10.c: Fix broken tests. * c-c++-common/gomp/declare-variant-3.c: Likewise. * c-c++-common/gomp/declare-variant-9.c: Likewise. * c-c++-common/gomp/declare-variant-any.c: New. * gfortran.dg/gomp/declare-variant-10.f90: Fix broken tests. * gfortran.dg/gomp/declare-variant-3.f90: Likewise. * gfortran.dg/gomp/declare-variant-9.f90: Likewise. * gfortran.dg/gomp/declare-variant-any.f90: Likewise. --- gcc/omp-general.cc | 31 +++++++++++++++++++ .../c-c++-common/gomp/declare-variant-10.c | 4 +-- .../c-c++-common/gomp/declare-variant-3.c | 10 ++---- .../c-c++-common/gomp/declare-variant-9.c | 4 +-- .../c-c++-common/gomp/declare-variant-any.c | 10 ++++++ .../gfortran.dg/gomp/declare-variant-10.f90 | 4 +-- .../gfortran.dg/gomp/declare-variant-3.f90 | 12 ++----- .../gfortran.dg/gomp/declare-variant-9.f90 | 2 +- .../gfortran.dg/gomp/declare-variant-any.f90 | 28 +++++++++++++++++ 9 files changed, 82 insertions(+), 23 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/gomp/declare-variant-any.c create mode 100644 gcc/testsuite/gfortran.dg/gomp/declare-variant-any.f90 diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc index 6f36b5d163f..23072b10d75 100644 --- a/gcc/omp-general.cc +++ b/gcc/omp-general.cc @@ -1277,6 +1277,8 @@ omp_check_context_selector (location_t loc, tree ctx, bool metadirective_p) for (tree tss = ctx; tss; tss = TREE_CHAIN (tss)) { enum omp_tss_code tss_code = OMP_TSS_CODE (tss); + bool saw_any_prop = false; + bool saw_other_prop = false; /* FIXME: not implemented yet. */ if (!metadirective_p && tss_code == OMP_TRAIT_SET_TARGET_DEVICE) @@ -1314,6 +1316,27 @@ omp_check_context_selector (location_t loc, tree ctx, bool metadirective_p) else ts_seen[ts_code] = true; + + /* If trait-property "any" is specified in the "kind" + trait-selector of the "device" selector set or the + "target_device" selector sets, no other trait-property + may be specified in the same selector set. */ + if (ts_code == OMP_TRAIT_DEVICE_KIND) + for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) + { + const char *prop = omp_context_name_list_prop (p); + if (!prop) + continue; + else if (strcmp (prop, "any") == 0) + saw_any_prop = true; + else + saw_other_prop = true; + } + else if (ts_code == OMP_TRAIT_DEVICE_ARCH + || ts_code == OMP_TRAIT_DEVICE_ISA + || ts_code == OMP_TRAIT_DEVICE_NUM) + saw_other_prop = true; + if (omp_ts_map[ts_code].valid_properties == NULL) continue; @@ -1366,6 +1389,14 @@ omp_check_context_selector (location_t loc, tree ctx, bool metadirective_p) break; } } + + if (saw_any_prop && saw_other_prop) + { + error_at (loc, + "no other trait-property may be specified " + "in the same selector set with %"); + return error_mark_node; + } } return ctx; } diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-10.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-10.c index 2b8a39425b1..e77693430d1 100644 --- a/gcc/testsuite/c-c++-common/gomp/declare-variant-10.c +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-10.c @@ -7,7 +7,7 @@ void f01 (void); #pragma omp declare variant (f01) match (device={isa(avx512f,avx512bw)}) void f02 (void); void f03 (void); -#pragma omp declare variant (f03) match (device={kind("any"),arch(x86_64),isa(avx512f,avx512bw)}) +#pragma omp declare variant (f03) match (device={arch(x86_64),isa(avx512f,avx512bw)}) void f04 (void); void f05 (void); #pragma omp declare variant (f05) match (device={kind(gpu)}) @@ -28,7 +28,7 @@ void f15 (void); #pragma omp declare variant (f15) match (device={isa(sse4,ssse3),arch(i386)}) void f16 (void); void f17 (void); -#pragma omp declare variant (f17) match (device={kind(any,fpga)}) +#pragma omp declare variant (f17) match (device={kind(fpga)}) void f18 (void); #pragma omp declare target diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-3.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-3.c index f5d7797f458..0d772d7aaab 100644 --- a/gcc/testsuite/c-c++-common/gomp/declare-variant-3.c +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-3.c @@ -29,13 +29,13 @@ void f17 (void); void f18 (void); #pragma omp declare variant (f13) match (device={kind(fpga)}) void f19 (void); -#pragma omp declare variant (f13) match (device={kind(any,any)}) +#pragma omp declare variant (f13) match (device={kind(any)}) void f20 (void); #pragma omp declare variant (f13) match (device={kind(host,nohost)}) void f21 (void); #pragma omp declare variant (f13) match (device={kind("cpu","gpu","fpga")}) void f22 (void); -#pragma omp declare variant (f13) match (device={kind(any,cpu,nohost)}) +#pragma omp declare variant (f13) match (device={kind(cpu,nohost)}) void f23 (void); #pragma omp declare variant (f13) match (device={isa(avx)}) void f24 (void); @@ -139,12 +139,8 @@ void f72 (void); void f73 (void); #pragma omp declare variant (f13) match (user={condition(score(25):1)}) void f74 (void); -#pragma omp declare variant (f13) match (device={kind(any,"any")}) +#pragma omp declare variant (f13) match (device={kind("any")}) void f75 (void); -#pragma omp declare variant (f13) match (device={kind("any","any")}) -void f76 (void); -#pragma omp declare variant (f13) match (device={kind("any",any)}) -void f77 (void); #pragma omp declare variant (f13) match (implementation={vendor(nvidia)}) void f78 (void); #pragma omp declare variant (f13) match (user={condition(score(0):0)}) diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-9.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-9.c index 5ee75892f2d..da96c81eb6f 100644 --- a/gcc/testsuite/c-c++-common/gomp/declare-variant-9.c +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-9.c @@ -7,7 +7,7 @@ void f01 (void); #pragma omp declare variant (f01) match (device={isa("avx512f",avx512bw)}) void f02 (void); void f03 (void); -#pragma omp declare variant (f03) match (device={kind(any),arch(x86_64),isa("avx512f","avx512bw")}) +#pragma omp declare variant (f03) match (device={arch(x86_64),isa("avx512f","avx512bw")}) void f04 (void); void f05 (void); #pragma omp declare variant (f05) match (device={kind(gpu)}) @@ -28,7 +28,7 @@ void f15 (void); #pragma omp declare variant (f15) match (device={isa(sse4,ssse3),arch(i386)}) void f16 (void); void f17 (void); -#pragma omp declare variant (f17) match (device={kind("any","fpga")}) +#pragma omp declare variant (f17) match (device={kind("fpga")}) void f18 (void); void diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-any.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-any.c new file mode 100644 index 00000000000..ad932109077 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-any.c @@ -0,0 +1,10 @@ +extern int f1 (int); +extern int f2 (int); +extern int f3 (int); +extern int f4 (int); + +#pragma omp declare variant (f1) match (device={kind(any,gpu)}) /* { dg-error "no other trait-property may be specified" } */ +#pragma omp declare variant (f2) match (device={kind(cpu,"any")}) /* { dg-error "no other trait-property may be specified" } */ +#pragma omp declare variant (f3) match (device={kind("any"),arch(x86_64)}) /* { dg-error "no other trait-property may be specified" } */ +#pragma omp declare variant (f4) match (device={arch(x86_64),kind(any)}) /* { dg-error "no other trait-property may be specified" } */ +int f (int); diff --git a/gcc/testsuite/gfortran.dg/gomp/declare-variant-10.f90 b/gcc/testsuite/gfortran.dg/gomp/declare-variant-10.f90 index 2f09146a10d..01f59c52808 100644 --- a/gcc/testsuite/gfortran.dg/gomp/declare-variant-10.f90 +++ b/gcc/testsuite/gfortran.dg/gomp/declare-variant-10.f90 @@ -15,7 +15,7 @@ contains subroutine f03 () end subroutine subroutine f04 () - !$omp declare variant (f03) match (device={kind("any"),arch(x86_64),isa(avx512f,avx512bw)}) + !$omp declare variant (f03) match (device={arch(x86_64),isa(avx512f,avx512bw)}) end subroutine subroutine f05 () end subroutine @@ -50,7 +50,7 @@ contains subroutine f17 () end subroutine subroutine f18 () - !$omp declare variant (f17) match (device={kind(any,fpga)}) + !$omp declare variant (f17) match (device={kind(fpga)}) end subroutine subroutine test1 () diff --git a/gcc/testsuite/gfortran.dg/gomp/declare-variant-3.f90 b/gcc/testsuite/gfortran.dg/gomp/declare-variant-3.f90 index 6b23d40e410..30733209e14 100644 --- a/gcc/testsuite/gfortran.dg/gomp/declare-variant-3.f90 +++ b/gcc/testsuite/gfortran.dg/gomp/declare-variant-3.f90 @@ -51,7 +51,7 @@ contains !$omp declare variant (f13) match (device={kind(fpga)}) end subroutine subroutine f20 () - !$omp declare variant (f13) match (device={kind(any,any)}) + !$omp declare variant (f13) match (device={kind(any)}) end subroutine subroutine f21 () !$omp declare variant (f13) match (device={kind(host,nohost)}) @@ -60,7 +60,7 @@ contains !$omp declare variant (f13) match (device={kind("cpu","gpu","fpga")}) end subroutine subroutine f23 () - !$omp declare variant (f13) match (device={kind(any,cpu,nohost)}) + !$omp declare variant (f13) match (device={kind(cpu,nohost)}) end subroutine subroutine f24 () !$omp declare variant (f13) match (device={isa(avx)}) @@ -219,13 +219,7 @@ contains !$omp declare variant (f13) match (user={condition(score(25):.true.)}) end subroutine subroutine f75 () - !$omp declare variant (f13) match (device={kind(any,"any")}) - end subroutine - subroutine f76 () - !$omp declare variant (f13) match (device={kind("any","any")}) - end subroutine - subroutine f77 () - !$omp declare variant (f13) match (device={kind("any",any)}) + !$omp declare variant (f13) match (device={kind("any")}) end subroutine subroutine f78 () !$omp declare variant (f13) match (implementation={vendor(nvidia)}) diff --git a/gcc/testsuite/gfortran.dg/gomp/declare-variant-9.f90 b/gcc/testsuite/gfortran.dg/gomp/declare-variant-9.f90 index ebd066609f3..297bff97d5e 100644 --- a/gcc/testsuite/gfortran.dg/gomp/declare-variant-9.f90 +++ b/gcc/testsuite/gfortran.dg/gomp/declare-variant-9.f90 @@ -38,7 +38,7 @@ contains subroutine f17 () end subroutine subroutine f18 () - !$omp declare variant (f17) match (device={kind("any","fpga")}) + !$omp declare variant (f17) match (device={kind("fpga")}) end subroutine subroutine test1 () diff --git a/gcc/testsuite/gfortran.dg/gomp/declare-variant-any.f90 b/gcc/testsuite/gfortran.dg/gomp/declare-variant-any.f90 new file mode 100644 index 00000000000..01540008724 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/declare-variant-any.f90 @@ -0,0 +1,28 @@ +integer function f1 (x) + integer, intent(in) :: x + f1 = x + 1 +end function +integer function f2 (x) + integer, intent(in) :: x + f2 = x + 2 +end function +integer function f3 (x) + integer, intent(in) :: x + f3 = x + 3 +end function +integer function f4 (x) + integer, intent(in) :: x + f4 = x + 4 +end function + +integer function f (x) + integer, intent(in) :: x + + !$omp declare variant (f1) match (device={kind(any,gpu)}) ! { dg-error "no other trait-property may be specified" } + !$omp declare variant (f2) match (device={kind(cpu,"any")}) ! { dg-error "no other trait-property may be specified" } + !$omp declare variant (f3) match (device={kind("any"),arch(x86_64)}) ! { dg-error "no other trait-property may be specified" } + !$omp declare variant (f4) match (device={arch(x86_64),kind(any)}) ! { dg-error "no other trait-property may be specified" } + + f = x +end function + From patchwork Sat May 4 21:21:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 1931430 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=Da2NrGp4; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX1376nz4z1xnT for ; Sun, 5 May 2024 07:27:15 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0444C384476C for ; Sat, 4 May 2024 21:27:14 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-il1-x12d.google.com (mail-il1-x12d.google.com [IPv6:2607:f8b0:4864:20::12d]) by sourceware.org (Postfix) with ESMTPS id 7051A384477B for ; Sat, 4 May 2024 21:22:23 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7051A384477B 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 7051A384477B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::12d ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857758; cv=none; b=YJj8Z0elV3SHX3yHShw82QLHljT7T7N47QK62K3RjGrY5F/LBZozsiCDfMEDqitLtKNJ/A5piQMilioD9/fzKnbnlmNsl+oMp8rQ3CimTxvPrIgrzFv48+NNSpNjexQaY7rGd0F3NQ7RwKUlPCpIpVf+YUeNZhCLwvzk6VQwXXE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857758; c=relaxed/simple; bh=5IOe1HOUDJ+R04BwdPCjc9aTBKW/PZgBKPVkIBABXg4=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=AZewva3wH2QNeDkxGQqHmslWrFAgz/316HVj82qhwcJkouHD9dKbA10H06v9+Z6DNbNJmfKPUk6dEI5T5iHBhaeI5lLOd8tXxmRkTRSg1nYdscvIW3cYQpGsIuShhv8otb+qz/6Nvn0y8EB6DxEb+QAfi9ZffEmA7s7aIR54yIQ= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-il1-x12d.google.com with SMTP id e9e14a558f8ab-36c82cf5cdeso3029555ab.0 for ; Sat, 04 May 2024 14:22:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857742; x=1715462542; 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=PlgV4xAswfkjSWH+CVknNg93KQZLTFHnoW4QsIRm96E=; b=Da2NrGp4KPI5D4CVxth5rbPUSgPSSht7rb/yxc1Pjac+O9Gyt+Eejz/8zgGuQMieaG YoWtLsH4FFsdiGmKOBl8Eh+2WNdSot0Jbz+uuth+lk4O/OHA93m+r7QTrWe9bH6B2R1H ewQ/JxLQf/ba5/XlTEf7hw6F1Q5peq7ius+eKkNk/TZUxatyPcii39mL3pAoL/apuKLy Nv1wck8dDMyzZfeipuAzrhjvRBGFLBV84PWb0KPIFd2Udqi0/vO3SJ6PnbByDcwavtUa 4Lo6ZxuCM4BBYRKQ5fzJlERGsInXEHUKYvV+J1hnnoFS5mfJ8+O7WYycqr+DIdkTmHv2 Y/iA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857742; x=1715462542; 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=PlgV4xAswfkjSWH+CVknNg93KQZLTFHnoW4QsIRm96E=; b=o5To3z3GGY6q3x/ooddY4LlIYmEVagCr0eSgyqmM8u2dGtgiVMzdWw4MHv36Rg0qZU 2i/hbNRZT/FK4my4G3ylbuvTDoL7n4l9h+OkxHRbkS3j7pV956/kJ1Qc9a+/Eag7Hwvi d/uRP2QrvSMJLVviSenCd7ejxJckCMpX5ImMio0UOVfwcsDTkq2CtGCiv1mgOXxilYN9 BSTTcT88Uf4ne0EXVA8HD2w9ZsYaDwECMqhGuv1MoY8De0mseCGfwZosFAMI+j/fG52A 8pei+YFSy3uvZfdrL/ywCn88csX1iKLAYtRcwXgmCdwS2kdieAzCX5GiJGtgARtAwR1X zqbw== X-Gm-Message-State: AOJu0YwpNtMHpvRY8s0XaWECMIbHztYPJqjuNjt1VaXusr8qViEFsXsd 0Q6G4pHJOch3pE3UcPLMHYobPoPCubJBIEjdnC2V4bRyn148XAljuK3acy7IcbOces0ELXbbywS 5 X-Google-Smtp-Source: AGHT+IEoD+wFNQHy8gklfl14kA9kcgLEaXCl8kLmyTfz711iAgVg2MZXq2g/eLO24U6/auOW3yveZA== X-Received: by 2002:a05:6e02:1d11:b0:36b:2fdc:b962 with SMTP id i17-20020a056e021d1100b0036b2fdcb962mr8826439ila.16.1714857740650; Sat, 04 May 2024 14:22:20 -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.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:20 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 09/12] OpenMP: Extend dynamic selector support to declare variant Date: Sat, 4 May 2024 15:21:49 -0600 Message-Id: <20240504212153.3561429-10-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 X-Spam-Status: No, score=-11.6 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 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+incoming=patchwork.ozlabs.org@gcc.gnu.org This patch extends the mechanisms previously added to support dynamic selectors in metavariant constructs to also apply to "declare variant". The front-end mechanisms used to handle "declare variant" via attributes attached to the function decls remain the same, but the gimplifier now uses the same internal data structures and helper functions as metadirective to score and sort "declare variant" alternatives, and constructs a gomp_metadirective node for variant calls that cannot be resolved at gimplification time. During late resolution, this gomp_metadirective is processed in exactly the same way as for real metadirectives. During implementation of this functionality, a number of bugs were discovered in the previous selector scoring and matching code: * Metadirective resolution was failing to account for scoring in "declare simd" clones, and was also relying on calling a function to match construct constructors that's only useful during gimplification during late resolution long after that pass. * The construct constructor scoring was previously implemented backwards from the specification (PR114596); a number of testcases were also broken in the same way as the implementation. * The special rules for matching simdlen and aligned properties on simd selectors were not implemented (nor were these properties on metadirectives being rejected per the OpenMP spec). This patch includes a new implementation of this functionality that has cleaner interfaces and is hopefully(!) easier to correlate to requirements of the OpenMP specification. Instead of relying on the gimplifier to score construct selectors, the scoring code has been consolidated in omp-general.cc with the gimplifier only providing the OpenMP construct context surrounding the metadirective or variant call. This is cached on the gomp_metadirective if necessary for late resolution. An additional improvement added in this patch is that for both metadirective and "declare variant", if late resolution is required the gimplifier now discards all alternatives that are known not to match. Note that this patch leaves a substantial amount of dead code that was used to support the former late "declare variant" resolution strategy, notably the declare_variant_alt and calls_declare_variant_alt flags on cgraph_node and all the code that touches those fields. The next patch in this series removes that unused code. Another issue not addressed in this patch is the special scoping rules for expressions in "declare variant" dynamic selectors, which is still under discussion in PR113904. We expect this to be fixed separately. gcc/c/ChangeLog * c-parser.c (c_parser_omp_context_selector): Remove metadirective_p parameter and conditionalization. (c_parser_omp_context_selector_specification): Remove metadirective_p parameter and adjust call not to pass it on. (c_finish_omp_declare_variant): Adjust arguments on calls to c_parser_omp_context_selector_specification and omp_context_selector_matches. (c_parser_omp_metadirective): Likewise. gcc/cp/ChangeLog * cp-tree.h (struct saved_scope): Add new field x_processing_omp_trait_property_expr. (processing_omp_trait_property_expr): Define * decl.cc (omp_declare_variant_finalize_one): Adjust arguments to omp_context_selector_matches. * parser.cc (cp_parser_omp_context_selector): Remove metadirective_p argument and conditionalization. (cp_parser_omp_context_selector_specification): Remove metadirective_p argument and adjust call not to pass it on. (cp_finish_omp_declare_variant): Adjust arguments on call to above. (cp_parser_omp_metadirective): Likewise. * pt.cc (tsubst_omp_context_selector): Adjust error behavior. (tsubst_stmt): Adjust call to omp_context_selector_matches. * semantics.cc (finish_id_expression_1): Do not diagnose error for use of parameter in declare variant selector here. gcc/fortran/ChangeLog * trans-openmp.cc (gfc_trans_omp_declare_variant): Adjust arguments to omp_context_selector_matches. (gfc_trans_omp_metadirective): Likewise. gcc/Changelog * gimple-streamer-in.cc (input_gimple_stmt): Restore gomp_metadirective context. * gimple-streamer-out.cc (output_gimple_stmt): Save gomp_metadirective context. * gimple.cc (gimple_build_omp_metadirective): Initialize gomp_metadirective context. * gimple.def (GIMPLE_OMP_METADIRECTIVE): Update comments. * gimple.h (gomp_metadirective): Add context field and update comments. (gimple_omp_metadirective_context): New. (gimple_omp_metadirective_set_context): New. * gimplify.cc (omp_resolved_variant_calls): New. (gimplify_variant_call_expr): New. (gimplify_call_expr): Adjust parameters. Call gimplify_variant_call_expr to handle declare variant substitution. (omp_construct_selector_matches): Delete. (omp_get_construct_context): New. (gimplify_omp_metadirective): Use filtered list of candidates to construct the gomp_metadirective structure. Save the construct context. (gimplify_expr): Adjust arguments to gimplify_call_expr. (gimplify_function_tree): Initialize and free omp_resolved_variant_calls around the call to gimplify_body. * gimplify.h (omp_construct_selector_matches): Delete. (omp_get_construct_context): New. * omp-general.cc (omp_construct_traits_to_codes): Delete. (omp_maybe_offloaded): Add construct_context parameter and comments. Use construct_context to check for nesting in a target directive instead of calling omp_construct_selector_matches. (expr_uses_parm_decl): New. (omp_check_context_selector): Don't reject target_device selector for "declare variant". Add missing check for invalid simd properties. Reject dynamic selectors that reference parameter variables in "declare variant" with a "sorry". (omp_construct_traits_match): New. (omp_context_selector_matches): Adjust parameters to pass in construct_context. Rewrite construct selector matching to use omp_construct_traits_match. Replace unnecessary conditionals checking that traits match the right selector set with asserts. (omp_construct_simd_compare): Add match_p parameter, use it to enable additional matching rules for simdlen and align clauses. (omp_context_selector_set_compare): Make static. Adjust call to omp_construct_simd_compare). (omp_dynamic_cond): Clean up code missed in a previously-committed patch. (omp_context_compare_score): Adjust parameters, rewrite and add comments. (omp_complete_construct_context): New. (omp_resolve_late_declare_variant): Delete. (omp_declare_variant_remove_hook): Delete. (omp_resolve_declare_variant): Delete. (omp_get_dynamic_candidates): Make non-static and adjust parameters. Call omp_complete_construct_context and pass the result to omp_context_selector_matches. Add more comments, debug output, and logic to allow resolution in some cases where candidates cannot be scored accurately. (omp_declare_variant_candidates): New. (omp_metadirective_candidates): New, split from... (omp_early_resolve_metadirective): ...here. (omp_late_resolve_metadirective): Explicitly initialize dynamic_selector field. Adjust call to omp_get_dynamic_candidates. * omp-general.h (struct omp_variant): Add comments explaining how this is used for "declare variant". (omp_construct_traits_to_codes): Delete. (omp_context_selector_matches): Adjust parameters. (omp_context_selector_set_compare): Delete. (omp_resolve_declare_variant): Delete. (omp_declare_variant_candidates): Declare. (omp_metadirective_candidates): Declare. (omp_get_dynamic_candidates): Declare. * omp-offload.cc (execute_omp_device_lower): Remove logic for the old way of handling declare variant. * tree-inline.cc (remap_gimple_stmt): Copy metadirective context. gcc/testsuite/ChangeLog * c-c++-common/gomp/declare-variant-12.c: Adjust expected behavior. * c-c++-common/gomp/declare-variant-13.c: Likewise. * c-c++-common/gomp/declare-variant-2.c: Likewise. * c-c++-common/gomp/declare-variant-arg-exprs.c: New. * c-c++-common/gomp/declare-dynamic-1.c: New. * c-c++-common/gomp/declare-dynamic-2.c: New. * c-c++-common/gomp/metadirective-3.c: Adjust expected behavior. * g++.dg/gomp/attrs-metadirective-3.C: Likewise. * g++.dg/gomp/declare-variant-class-1.C: New. * g++.dg/gomp/declare-variant-class-2.C: New. * gfortran.dg/gomp/declare-variant-12.f90: Adjust expected behavior. * gfortran.dg/gomp/declare-variant-13.f90: Likewise. * gfortran.dg/gomp/metadirective-1.f90: Likewise. * gfortran.dg/gomp/metadirective-3.f90: Likewise. --- gcc/c/c-parser.cc | 28 +- gcc/cp/cp-tree.h | 2 + gcc/cp/decl.cc | 2 +- gcc/cp/parser.cc | 54 +- gcc/cp/pt.cc | 5 +- gcc/cp/semantics.cc | 3 +- gcc/fortran/trans-openmp.cc | 5 +- gcc/gimple-streamer-in.cc | 3 + gcc/gimple-streamer-out.cc | 8 +- gcc/gimple.cc | 1 + gcc/gimple.def | 3 +- gcc/gimple.h | 26 +- gcc/gimplify.cc | 402 ++-- gcc/gimplify.h | 2 +- gcc/omp-general.cc | 1671 ++++++++--------- gcc/omp-general.h | 26 +- gcc/omp-offload.cc | 12 +- .../c-c++-common/gomp/declare-variant-12.c | 14 +- .../c-c++-common/gomp/declare-variant-13.c | 6 +- .../c-c++-common/gomp/declare-variant-2.c | 4 +- .../gomp/declare-variant-arg-exprs.c | 29 + .../gomp/declare-variant-dynamic-1.c | 26 + .../gomp/declare-variant-dynamic-2.c | 30 + .../c-c++-common/gomp/metadirective-3.c | 18 +- .../g++.dg/gomp/attrs-metadirective-3.C | 14 +- .../g++.dg/gomp/declare-variant-class-1.C | 32 + .../g++.dg/gomp/declare-variant-class-2.C | 37 + .../gfortran.dg/gomp/declare-variant-12.f90 | 14 +- .../gfortran.dg/gomp/declare-variant-13.f90 | 28 +- .../gfortran.dg/gomp/metadirective-1.f90 | 22 +- .../gfortran.dg/gomp/metadirective-3.f90 | 18 +- gcc/tree-inline.cc | 2 + 32 files changed, 1405 insertions(+), 1142 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/gomp/declare-variant-arg-exprs.c create mode 100644 gcc/testsuite/c-c++-common/gomp/declare-variant-dynamic-1.c create mode 100644 gcc/testsuite/c-c++-common/gomp/declare-variant-dynamic-2.c create mode 100644 gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C create mode 100644 gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index e6546bcb32b..562819faea3 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -24635,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, bool metadirective_p) + tree parms) { tree ret = NULL_TREE; do @@ -24782,16 +24782,7 @@ 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: 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))) + if (!INTEGRAL_TYPE_P (TREE_TYPE (t))) error_at (token->location, "property must be integer expression"); else @@ -24874,8 +24865,7 @@ 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, - bool metadirective_p) +c_parser_omp_context_selector_specification (c_parser *parser, tree parms) { tree ret = NULL_TREE; do @@ -24900,8 +24890,7 @@ 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, - metadirective_p); + tree selectors = c_parser_omp_context_selector (parser, set, parms); if (selectors == error_mark_node) ret = error_mark_node; else if (ret != error_mark_node) @@ -24977,8 +24966,7 @@ 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, false); + tree ctx = c_parser_omp_context_selector_specification (parser, parms); if (ctx == error_mark_node) goto fail; ctx = omp_check_context_selector (match_loc, ctx, false); @@ -25014,7 +25002,7 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) tree construct = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT); omp_mark_declare_variant (match_loc, variant, construct); - if (omp_context_selector_matches (ctx, false, true)) + if (omp_context_selector_matches (ctx, NULL_TREE, false)) { tree attr = tree_cons (get_identifier ("omp declare variant base"), @@ -26688,7 +26676,7 @@ c_parser_omp_metadirective (c_parser *parser, bool *if_p) if (!default_p) { ctx = c_parser_omp_context_selector_specification (parser, - NULL_TREE, true); + NULL_TREE); if (ctx == error_mark_node) goto error; ctx = omp_check_context_selector (match_loc, ctx, true); @@ -26697,7 +26685,7 @@ c_parser_omp_metadirective (c_parser *parser, bool *if_p) /* 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; + skip = (omp_context_selector_matches (ctx, NULL_TREE, false) == 0); if (c_parser_next_token_is_not (parser, CPP_COLON)) { diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index bafdf63dc63..3f18f33ab1e 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1891,6 +1891,7 @@ struct GTY(()) saved_scope { int suppress_location_wrappers; BOOL_BITFIELD x_processing_explicit_instantiation : 1; BOOL_BITFIELD need_pop_function_context : 1; + BOOL_BITFIELD x_processing_omp_trait_property_expr : 1; /* Nonzero if we are parsing the discarded statement of a constexpr if-statement. */ @@ -1962,6 +1963,7 @@ extern GTY(()) struct saved_scope *scope_chain; #define processing_template_decl scope_chain->x_processing_template_decl #define processing_specialization scope_chain->x_processing_specialization #define processing_explicit_instantiation scope_chain->x_processing_explicit_instantiation +#define processing_omp_trait_property_expr scope_chain->x_processing_omp_trait_property_expr /* Nonzero if we are parsing the conditional expression of a contract condition. These expressions appear outside the paramter list (like a diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 2f989b9b869..5aedaebdf28 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -8328,7 +8328,7 @@ omp_declare_variant_finalize_one (tree decl, tree attr) tree construct = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT); omp_mark_declare_variant (match_loc, variant, construct); - if (!omp_context_selector_matches (ctx, false, true)) + if (!omp_context_selector_matches (ctx, NULL_TREE, false)) return true; TREE_PURPOSE (TREE_VALUE (attr)) = variant; } diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 4bb9b086095..58eef2b3d9f 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -47939,7 +47939,7 @@ cp_parser_omp_declare_simd (cp_parser *parser, cp_token *pragma_tok, static tree cp_parser_omp_context_selector (cp_parser *parser, enum omp_tss_code set, - bool has_parms_p, bool metadirective_p) + bool has_parms_p) { tree ret = NULL_TREE; do @@ -48100,31 +48100,24 @@ cp_parser_omp_context_selector (cp_parser *parser, enum omp_tss_code set, break; case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR: case OMP_TRAIT_PROPERTY_BOOL_EXPR: - /* FIXME: I believe it is an unimplemented feature rather - than a user error to have non-constant expressions - inside "declare variant". */ - t = metadirective_p - ? cp_parser_expression (parser) - : cp_parser_constant_expression (parser); - if (t != error_mark_node) + processing_omp_trait_property_expr = true; + /* This actually parses a not-necessarily-constant + conditional-expression. */ + t = cp_parser_constant_expression (parser, true, NULL, false); + processing_omp_trait_property_expr = false; + if (t == error_mark_node) + return error_mark_node; + if (!type_dependent_expression_p (t)) { t = fold_non_dependent_expr (t); - if (!metadirective_p - && !value_dependent_expression_p (t) - && (!INTEGRAL_TYPE_P (TREE_TYPE (t)) - || !tree_fits_shwi_p (t))) - error_at (token->location, "property must be " - "constant integer expression"); - 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); + if (!INTEGRAL_TYPE_P (TREE_TYPE (t))) + { + error_at (token->location, + "property must be integer expression"); + return error_mark_node; + } } - else - return error_mark_node; + properties = make_trait_property (NULL_TREE, t, properties); break; case OMP_TRAIT_PROPERTY_CLAUSE_LIST: if (sel == OMP_TRAIT_CONSTRUCT_SIMD) @@ -48197,8 +48190,7 @@ cp_parser_omp_context_selector (cp_parser *parser, enum omp_tss_code set, static tree cp_parser_omp_context_selector_specification (cp_parser *parser, - bool has_parms_p, - bool metadirective_p) + bool has_parms_p) { tree ret = NULL_TREE; do @@ -48225,8 +48217,7 @@ cp_parser_omp_context_selector_specification (cp_parser *parser, return error_mark_node; tree selectors - = cp_parser_omp_context_selector (parser, set, has_parms_p, - metadirective_p); + = cp_parser_omp_context_selector (parser, set, has_parms_p); if (selectors == error_mark_node) { cp_parser_skip_to_closing_brace (parser); @@ -48536,8 +48527,7 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, if (!parens.require_open (parser)) goto fail; - tree ctx = cp_parser_omp_context_selector_specification (parser, true, - false); + tree ctx = cp_parser_omp_context_selector_specification (parser, true); if (ctx == error_mark_node) goto fail; ctx = omp_check_context_selector (match_loc, ctx, false); @@ -49326,8 +49316,7 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok, if (!default_p) { - ctx = cp_parser_omp_context_selector_specification (parser, false, - true); + ctx = cp_parser_omp_context_selector_specification (parser, false); if (ctx == error_mark_node) goto fail; ctx = omp_check_context_selector (match_loc, ctx, true); @@ -49339,8 +49328,7 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok, /* FIXME: we could still do this if the context selector doesn't have any dependent subexpressions. */ skip = (!processing_template_decl - && omp_context_selector_matches (ctx, true, true) == 0); - + && !omp_context_selector_matches (ctx, NULL_TREE, false)); if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)) { cp_parser_require (parser, CPP_COLON, RT_COLON); diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 409c4df68bc..4acc39e344d 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -17906,7 +17906,8 @@ tsubst_omp_context_selector (tree ctx, tree args, tsubst_flags_t complain, if (!INTEGRAL_TYPE_P (TREE_TYPE (t))) error_at (cp_expr_loc_or_input_loc (t), "property must be integer expression"); - properties = make_trait_property (NULL_TREE, t, NULL_TREE); + else + properties = make_trait_property (NULL_TREE, t, NULL_TREE); break; case OMP_TRAIT_PROPERTY_CLAUSE_LIST: if (OMP_TS_CODE (sel) == OMP_TRAIT_CONSTRUCT_SIMD) @@ -19496,7 +19497,7 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl) in_decl); /* Remove the selector from further consideration if it can be evaluated as a non-match at this point. */ - if (omp_context_selector_matches (ctx, true, true) == 0) + if (omp_context_selector_matches (ctx, NULL_TREE, false) == 0) continue; } s = push_stmt_list (); diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 02c7c1bf5a4..37d803d9181 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -4384,7 +4384,8 @@ finish_id_expression_1 (tree id_expression, if (TREE_CODE (decl) == PARM_DECL && DECL_CONTEXT (decl) == NULL_TREE && !cp_unevaluated_operand - && !processing_contract_condition) + && !processing_contract_condition + && !processing_omp_trait_property_expr) { *error_msg = G_("use of parameter outside function body"); return error_mark_node; diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc index 854d3ac14f0..46a8be8e2bb 100644 --- a/gcc/fortran/trans-openmp.cc +++ b/gcc/fortran/trans-openmp.cc @@ -8555,7 +8555,8 @@ gfc_trans_omp_declare_variant (gfc_namespace *ns) omp_mark_declare_variant (gfc_get_location (&odv->where), gfc_get_symbol_decl (variant_proc_sym), construct); - if (omp_context_selector_matches (set_selectors, false, true)) + if (omp_context_selector_matches (set_selectors, + NULL_TREE, false)) { tree id = get_identifier ("omp declare variant base"); tree variant = gfc_get_symbol_decl (variant_proc_sym); @@ -8627,7 +8628,7 @@ gfc_trans_omp_metadirective (gfc_code *code) return error_mark_node; /* If the selector doesn't match, drop the whole variant. */ - if (!omp_context_selector_matches (ctx, true, true)) + if (!omp_context_selector_matches (ctx, NULL_TREE, false)) { variant = variant->next; continue; diff --git a/gcc/gimple-streamer-in.cc b/gcc/gimple-streamer-in.cc index 1482d34e9a8..e7321189ea0 100644 --- a/gcc/gimple-streamer-in.cc +++ b/gcc/gimple-streamer-in.cc @@ -193,6 +193,9 @@ input_gimple_stmt (class lto_input_block *ib, class data_in *data_in, = dyn_cast (stmt)) { gimple_alloc_omp_metadirective (metadirective_stmt); + gimple_omp_metadirective_set_context (metadirective_stmt, + stream_read_tree (ib, + data_in)); for (i = 0; i < num_ops; i++) gimple_omp_metadirective_set_label (metadirective_stmt, i, stream_read_tree (ib, diff --git a/gcc/gimple-streamer-out.cc b/gcc/gimple-streamer-out.cc index ccb11fec1da..f2e76051e13 100644 --- a/gcc/gimple-streamer-out.cc +++ b/gcc/gimple-streamer-out.cc @@ -171,9 +171,13 @@ output_gimple_stmt (struct output_block *ob, struct function *fn, gimple *stmt) stream_write_tree (ob, gimple_call_fntype (stmt), true); } if (gimple_code (stmt) == GIMPLE_OMP_METADIRECTIVE) - for (i = 0; i < gimple_num_ops (stmt); i++) - stream_write_tree (ob, gimple_omp_metadirective_label (stmt, i), + { + stream_write_tree (ob, gimple_omp_metadirective_context (stmt), true); + for (i = 0; i < gimple_num_ops (stmt); i++) + stream_write_tree (ob, gimple_omp_metadirective_label (stmt, i), + true); + } break; diff --git a/gcc/gimple.cc b/gcc/gimple.cc index 303b1b029ec..ebe4513ca9b 100644 --- a/gcc/gimple.cc +++ b/gcc/gimple.cc @@ -1331,6 +1331,7 @@ gimple_build_omp_metadirective (int num_variants) = as_a (gimple_alloc (GIMPLE_OMP_METADIRECTIVE, num_variants)); gimple_alloc_omp_metadirective (p); + gimple_omp_metadirective_set_context (p, NULL); gimple_omp_metadirective_set_variants (p, NULL); return p; diff --git a/gcc/gimple.def b/gcc/gimple.def index 41e69d56bb4..a1bce89c60f 100644 --- a/gcc/gimple.def +++ b/gcc/gimple.def @@ -398,7 +398,8 @@ DEFGSCODE(GIMPLE_OMP_TEAMS, "gimple_omp_teams", GSS_OMP_PARALLEL_LAYOUT) CLAUSES is an OMP_CLAUSE chain holding the associated clauses. */ DEFGSCODE(GIMPLE_OMP_ORDERED, "gimple_omp_ordered", GSS_OMP_SINGLE_LAYOUT) -/* GIMPLE_OMP_METADIRECTIVE represents #pragma omp metadirective. */ +/* GIMPLE_OMP_METADIRECTIVE represents both #pragma omp metadirective and + a call to a function with "declare variant" variants. */ DEFGSCODE(GIMPLE_OMP_METADIRECTIVE, "gimple_omp_metadirective", GSS_OMP_METADIRECTIVE) diff --git a/gcc/gimple.h b/gcc/gimple.h index b608ccd2ceb..43d4ef2d0ce 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -856,10 +856,14 @@ struct GTY((tag("GSS_OMP_METADIRECTIVE"))) /* [ WORD 8 ] : a list of bodies associated with the directive variants. */ gomp_variant *variants; - /* [ WORD 9 ] : label vector. */ + /* [ WORD 9 ] : the cached OpenMP context for this directive, used for + post-gimplification resolution. */ + tree context; + + /* [ WORD 10 ] : label vector. */ tree * GTY((length ("%h.num_ops"))) labels; - /* [ WORD 10 ] : operand vector. Used to hold the selectors for the + /* [ WORD 11 ] : operand vector. Used to hold the selectors for the directive variants. */ tree GTY((length ("%h.num_ops"))) op[1]; }; @@ -6692,6 +6696,24 @@ gimple_assume_body (const gimple *gs) } +static inline tree +gimple_omp_metadirective_context (const gimple *g) +{ + const gomp_metadirective *omp_metadirective + = as_a (g); + return omp_metadirective->context; +} + + +static inline void +gimple_omp_metadirective_set_context (gimple *g, tree context) +{ + gomp_metadirective *omp_metadirective + = as_a (g); + omp_metadirective->context = context; +} + + static inline tree gimple_omp_metadirective_label (const gimple *g, unsigned i) { diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc index ccbb5afbec7..19e6d209265 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -96,6 +96,11 @@ reset_cond_uid () /* Hash set of poisoned variables in a bind expr. */ static hash_set *asan_poisoned_variables = NULL; +/* Hash set of already-resolved calls to OpenMP "declare variant" + functions. A call can resolve to the original function and + we don't want to repeat the resolution multiple times. */ +static hash_set *omp_resolved_variant_calls = NULL; + enum gimplify_omp_var_data { GOVD_SEEN = 0x000001, @@ -3799,12 +3804,185 @@ maybe_fold_stmt (gimple_stmt_iterator *gsi) return fold_stmt (gsi); } +/* Helper function for gimplify_call_expr: handle "declare variant" + resolution and expansion. Arguments are as for gimplify_call_expr. + If *EXPR_P is unchanged, the return value should be ignored and the + normal gimplify_call_expr handling should be applied. Otherwise GS_OK + is returned if the new *EXPR_P is something that needs to be further + gimplified, or GS_ALL_DONE if *EXPR_P has been translated into a + GIMPLE_OMP_METADIRECTIVE. */ + +static enum gimplify_status +gimplify_variant_call_expr (tree *expr_p, gimple_seq *pre_p, + fallback_t fallback) +{ + /* If we've already processed this call, stop now. This can happen + if the variant call resolves to the original function, or to + a dynamic conditional that includes the default call to the original + function. */ + gcc_assert (omp_resolved_variant_calls != NULL); + if (omp_resolved_variant_calls->contains (*expr_p)) + return GS_OK; + + tree fndecl = get_callee_fndecl (*expr_p); + tree fnptrtype = TREE_TYPE (CALL_EXPR_FN (*expr_p)); + location_t loc = EXPR_LOCATION (*expr_p); + tree construct_context = omp_get_construct_context (); + vec all_candidates + = omp_declare_variant_candidates (fndecl, construct_context); + gcc_assert (!all_candidates.is_empty ()); + vec candidates + = omp_get_dynamic_candidates (all_candidates, construct_context); + + /* If the variant call could be resolved now, build a nest of COND_EXPRs + if there are dynamic candidates, and/or a new CALL_EXPR for each + candidate call. */ + if (!candidates.is_empty ()) + { + int n = candidates.length (); + tree tail = NULL_TREE; + + for (int i = n - 1; i >= 0; i--) + { + if (tail) + gcc_assert (candidates[i].dynamic_selector); + else + gcc_assert (!candidates[i].dynamic_selector); + if (candidates[i].alternative == fndecl) + { + /* We should only get the original function back as the + default. */ + gcc_assert (!tail); + omp_resolved_variant_calls->add (*expr_p); + tail = *expr_p; + } + else + { + /* Special case: if there are no adjust_args/append_args for + the final static selector, we can re-use the old CALL_EXPR + and just replace the function. */ + /* FIXME: also test for adjust_args/append_args. */ + tree thiscall = tail ? unshare_expr (*expr_p) : *expr_p; + CALL_EXPR_FN (thiscall) = build1 (ADDR_EXPR, fnptrtype, + candidates[i].alternative); + /* FIXME: Handle adjust_args/append_args here too. */ + if (!tail) + tail = thiscall; + else + tail = build3 (COND_EXPR, TREE_TYPE (*expr_p), + candidates[i].dynamic_selector, + thiscall, tail); + } + } + *expr_p = tail; + return GS_OK; + } + + /* If we couldn't resolve the variant call now, replace the call with a + GIMPLE_OMP_METADIRECTIVE that has gimplified calls in each of its + alternatives. Yes, this is badly named, but the same logic is used + to replace both things in the late resolution from the ompdevlow pass. */ + else + { + /* If we need a usable return value, we need a temporary + and an assignment in each alternative. This logic was borrowed + from gimplify_cond_expr. */ + tree type = TREE_TYPE (*expr_p); + bool want_value = (fallback != fb_none && !VOID_TYPE_P (type)); + bool pointerize = false; + tree tmp = NULL_TREE, result = NULL_TREE; + + if (want_value) + { + /* If either an rvalue is ok or we do not require an lvalue, + create the temporary. But we cannot do that if the type is + addressable. */ + if (((fallback & fb_rvalue) || !(fallback & fb_lvalue)) + && !TREE_ADDRESSABLE (type)) + { + tmp = create_tmp_var (type, "iftmp"); + result = tmp; + } + + /* Otherwise, only create and copy references to the values. */ + else + { + pointerize = true; + type = build_pointer_type (type); + tmp = create_tmp_var (type, "iftmp"); + result = build_simple_mem_ref_loc (loc, tmp); + } + } + + /* The following code was more or less stolen from + gimplify_omp_metadirective. FIXME: do we also need to copy + the "omp metadirective construct target" part too? */ + gomp_variant *first_variant = NULL; + gomp_variant *prev_variant = NULL; + gomp_metadirective *stmt + = gimple_build_omp_metadirective (all_candidates.length ()); + tree end_label = create_artificial_label (UNKNOWN_LOCATION); + + for (unsigned int i = 0; i < all_candidates.length (); i++) + { + tree decl = all_candidates[i].alternative; + gimple_set_op (stmt, i, all_candidates[i].selector); + gomp_variant *omp_variant + = gimple_build_omp_variant (NULL); + gimple_seq *directive_p = gimple_omp_body_ptr (omp_variant); + tree thiscall; + + /* We need to turn the decl from the candidate into a function + call and possible assignment, gimplify it, and stuff that in + the directive seq of the gomp_variant. */ + if (decl == fndecl) + { + thiscall = *expr_p; + omp_resolved_variant_calls->add (*expr_p); + } + else + { + /* FIXME: handle adjust_args/append_args here too. */ + thiscall = unshare_expr (*expr_p); + CALL_EXPR_FN (thiscall) = build1 (ADDR_EXPR, fnptrtype, decl); + } + if (pointerize) + thiscall = build_fold_addr_expr_loc (loc, thiscall); + if (want_value) + thiscall = build2 (INIT_EXPR, type, tmp, thiscall); + + gimplify_stmt (&thiscall, directive_p); + gimplify_seq_add_stmt (directive_p, gimple_build_goto (end_label)); + + if (!first_variant) + first_variant = omp_variant; + if (prev_variant) + { + prev_variant->next = omp_variant; + omp_variant->prev = prev_variant; + } + prev_variant = omp_variant; + } + + gimple_omp_metadirective_set_context (stmt, construct_context); + gimple_omp_metadirective_set_variants (stmt, first_variant); + + gimplify_seq_add_stmt (pre_p, stmt); + gimplify_seq_add_stmt (pre_p, gimple_build_label (end_label)); + cgraph_node::get (cfun->decl)->has_metadirectives = 1; + + *expr_p = result; + return GS_ALL_DONE; + } +} + /* Gimplify the CALL_EXPR node *EXPR_P into the GIMPLE sequence PRE_P. WANT_VALUE is true if the result of the call is desired. */ static enum gimplify_status -gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value) +gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, fallback_t fallback) { + bool want_value = (fallback != fb_none); tree fndecl, parms, p, fnptrtype; enum gimplify_status ret; int i, nargs; @@ -3970,14 +4148,23 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value) /* Remember the original function pointer type. */ fnptrtype = TREE_TYPE (CALL_EXPR_FN (*expr_p)); + /* Handle "declare variant" substitution. */ if (flag_openmp && fndecl && cfun - && (cfun->curr_properties & PROP_gimple_any) == 0) + && (cfun->curr_properties & PROP_gimple_any) == 0 + && lookup_attribute ("omp declare variant base", + DECL_ATTRIBUTES (fndecl))) { - tree variant = omp_resolve_declare_variant (fndecl); - if (variant != fndecl) - CALL_EXPR_FN (*expr_p) = build1 (ADDR_EXPR, fnptrtype, variant); + tree orig = *expr_p; + enum gimplify_status ret + = gimplify_variant_call_expr (expr_p, pre_p, fallback); + /* This may resolve to the same call, or the call expr with just + the function replaced, in which case we should just continue to + gimplify it normally. Otherwise, if we get something else back, + stop here. */ + if (*expr_p != orig) + return ret; } /* There is a sequence point before the call, so any side effects in @@ -14634,57 +14821,59 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p, delete_omp_context (ctx); } -/* Return 0 if CONSTRUCTS selectors don't match the OpenMP context, - -1 if unknown yet (simd is involved, won't be known until vectorization) - and 1 if they do. If SCORES is non-NULL, it should point to an array - of at least 2*NCONSTRUCTS+2 ints, and will be filled with the positions - of the CONSTRUCTS (position -1 if it will never match) followed by - number of constructs in the OpenMP context construct trait. If the - score depends on whether it will be in a declare simd clone or not, - the function returns 2 and there will be two sets of the scores, the first - one for the case that it is not in a declare simd clone, the other - that it is in a declare simd clone. */ +/* Collect a list of traits for enclosing constructs in the current + OpenMP context. The list is in the same format as the trait selector + list of construct trait sets built by the front ends. -int -omp_construct_selector_matches (enum tree_code *constructs, int nconstructs, - int *scores) + Per the OpenMP specification, the construct trait set includes constructs + up to an enclosing "target" construct. If there is no "target" construct, + then additional things may be added to the construct trait set (simd for + simd clones, additional constructs associated with "declare variant", + the target trait for "declare target"); those are not handled here. + In particular simd clones are not known during gimplification so + matching/scoring of context selectors that might involve them needs + to be deferred to the omp_device_lower pass. */ + +tree +omp_get_construct_context (void) { - int matched = 0, cnt = 0; - bool simd_seen = false; - bool target_seen = false; - int declare_simd_cnt = -1; - auto_vec codes; + tree result = NULL_TREE; for (struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp; ctx;) { - if (((ctx->region_type & ORT_PARALLEL) && ctx->code == OMP_PARALLEL) - || ((ctx->region_type & (ORT_TARGET | ORT_IMPLICIT_TARGET | ORT_ACC)) - == ORT_TARGET && ctx->code == OMP_TARGET) - || ((ctx->region_type & ORT_TEAMS) && ctx->code == OMP_TEAMS) - || (ctx->region_type == ORT_WORKSHARE && ctx->code == OMP_FOR) - || (ctx->region_type == ORT_SIMD - && ctx->code == OMP_SIMD - && !omp_find_clause (ctx->clauses, OMP_CLAUSE_BIND))) + if (((ctx->region_type & (ORT_TARGET | ORT_IMPLICIT_TARGET | ORT_ACC)) + == ORT_TARGET) + && ctx->code == OMP_TARGET) { - ++cnt; - if (scores) - codes.safe_push (ctx->code); - else if (matched < nconstructs && ctx->code == constructs[matched]) - { - if (ctx->code == OMP_SIMD) - { - if (matched) - return 0; - simd_seen = true; - } - ++matched; - } - if (ctx->code == OMP_TARGET) - { - if (scores == NULL) - return matched < nconstructs ? 0 : simd_seen ? -1 : 1; - target_seen = true; - break; - } + result = make_trait_selector (OMP_TRAIT_CONSTRUCT_TARGET, + NULL_TREE, NULL_TREE, result); + /* We're not interested in any outer constructs. */ + break; + } + else if ((ctx->region_type & ORT_PARALLEL) && ctx->code == OMP_PARALLEL) + result = make_trait_selector (OMP_TRAIT_CONSTRUCT_PARALLEL, + NULL_TREE, NULL_TREE, result); + else if ((ctx->region_type & ORT_TEAMS) && ctx->code == OMP_TEAMS) + result = make_trait_selector (OMP_TRAIT_CONSTRUCT_TEAMS, + NULL_TREE, NULL_TREE, result); + else if (ctx->region_type == ORT_WORKSHARE && ctx->code == OMP_FOR) + result = make_trait_selector (OMP_TRAIT_CONSTRUCT_FOR, + NULL_TREE, NULL_TREE, result); + else if (ctx->region_type == ORT_SIMD + && ctx->code == OMP_SIMD + && !omp_find_clause (ctx->clauses, OMP_CLAUSE_BIND)) + { + tree props = NULL_TREE; + tree *last = &props; + for (tree c = ctx->clauses; c; c = OMP_CLAUSE_CHAIN (c)) + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_SIMDLEN + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_INBRANCH + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_NOTINBRANCH) + { + *last = unshare_expr (c); + last = &(OMP_CLAUSE_CHAIN (c)); + } + result = make_trait_selector (OMP_TRAIT_CONSTRUCT_SIMD, + NULL_TREE, props, result); } else if (ctx->region_type == ORT_WORKSHARE && ctx->code == OMP_LOOP @@ -14696,89 +14885,8 @@ omp_construct_selector_matches (enum tree_code *constructs, int nconstructs, ctx = ctx->outer_context->outer_context; ctx = ctx->outer_context; } - if (!target_seen - && lookup_attribute ("omp declare simd", - DECL_ATTRIBUTES (current_function_decl))) - { - /* Declare simd is a maybe case, it is supposed to be added only to the - omp-simd-clone.cc added clones and not to the base function. */ - declare_simd_cnt = cnt++; - if (scores) - codes.safe_push (OMP_SIMD); - else if (cnt == 0 - && constructs[0] == OMP_SIMD) - { - gcc_assert (matched == 0); - simd_seen = true; - if (++matched == nconstructs) - return -1; - } - } - if (tree attr = lookup_attribute ("omp declare variant variant", - DECL_ATTRIBUTES (current_function_decl))) - { - tree selectors = TREE_VALUE (attr); - int variant_nconstructs = list_length (selectors); - enum tree_code *variant_constructs = NULL; - if (!target_seen && variant_nconstructs) - { - variant_constructs - = (enum tree_code *) alloca (variant_nconstructs - * sizeof (enum tree_code)); - omp_construct_traits_to_codes (selectors, variant_nconstructs, - variant_constructs); - } - for (int i = 0; i < variant_nconstructs; i++) - { - ++cnt; - if (scores) - codes.safe_push (variant_constructs[i]); - else if (matched < nconstructs - && variant_constructs[i] == constructs[matched]) - { - if (variant_constructs[i] == OMP_SIMD) - { - if (matched) - return 0; - simd_seen = true; - } - ++matched; - } - } - } - if (!target_seen - && lookup_attribute ("omp declare target block", - DECL_ATTRIBUTES (current_function_decl))) - { - if (scores) - codes.safe_push (OMP_TARGET); - else if (matched < nconstructs && constructs[matched] == OMP_TARGET) - ++matched; - } - if (scores) - { - for (int pass = 0; pass < (declare_simd_cnt == -1 ? 1 : 2); pass++) - { - int j = codes.length () - 1; - for (int i = nconstructs - 1; i >= 0; i--) - { - while (j >= 0 - && (pass != 0 || declare_simd_cnt != j) - && constructs[i] != codes[j]) - --j; - if (pass == 0 && declare_simd_cnt != -1 && j > declare_simd_cnt) - *scores++ = j - 1; - else - *scores++ = j; - } - *scores++ = ((pass == 0 && declare_simd_cnt != -1) - ? codes.length () - 1 : codes.length ()); - } - return declare_simd_cnt == -1 ? 1 : 2; - } - if (matched == nconstructs) - return simd_seen ? -1 : 1; - return 0; + + return result; } /* Gimplify OACC_CACHE. */ @@ -17720,8 +17828,11 @@ gimplify_omp_metadirective (tree *expr_p, gimple_seq *pre_p, gimple_seq *, } /* Try to resolve the metadirective. */ + tree construct_context = omp_get_construct_context (); + vec all_candidates + = omp_metadirective_candidates (*expr_p, construct_context); vec candidates - = omp_early_resolve_metadirective (*expr_p); + = omp_get_dynamic_candidates (all_candidates, construct_context); if (!candidates.is_empty ()) return expand_omp_metadirective (candidates, pre_p); @@ -17733,12 +17844,11 @@ gimplify_omp_metadirective (tree *expr_p, gimple_seq *pre_p, gimple_seq *, tree body_label = NULL; tree end_label = create_artificial_label (UNKNOWN_LOCATION); - for (tree variant = OMP_METADIRECTIVE_VARIANTS (*expr_p); variant != NULL_TREE; - variant = TREE_CHAIN (variant)) + for (unsigned int i = 0; i < all_candidates.length (); i++) { - tree selector = OMP_METADIRECTIVE_VARIANT_SELECTOR (variant); - tree directive = OMP_METADIRECTIVE_VARIANT_DIRECTIVE (variant); - tree body = OMP_METADIRECTIVE_VARIANT_BODY (variant); + tree selector = all_candidates[i].selector; + tree directive = all_candidates[i].alternative; + tree body = all_candidates[i].body; selectors.safe_push (selector); gomp_variant *omp_variant @@ -17770,6 +17880,7 @@ gimplify_omp_metadirective (tree *expr_p, gimple_seq *pre_p, gimple_seq *, gomp_metadirective *stmt = gimple_build_omp_metadirective (selectors.length ()); + gimple_omp_metadirective_set_context (stmt, construct_context); gimple_omp_metadirective_set_variants (stmt, first_variant); tree selector; @@ -18029,7 +18140,7 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, break; case CALL_EXPR: - ret = gimplify_call_expr (expr_p, pre_p, fallback != fb_none); + ret = gimplify_call_expr (expr_p, pre_p, fallback); /* C99 code may assign to an array in a structure returned from a function, and this has undefined behavior only on @@ -19533,7 +19644,16 @@ gimplify_function_tree (tree fndecl) if (asan_sanitize_use_after_scope ()) asan_poisoned_variables = new hash_set (); + if (flag_openmp) + omp_resolved_variant_calls = new hash_set (); + bind = gimplify_body (fndecl, true); + + if (omp_resolved_variant_calls) + { + delete omp_resolved_variant_calls; + omp_resolved_variant_calls = NULL; + } if (asan_poisoned_variables) { delete asan_poisoned_variables; diff --git a/gcc/gimplify.h b/gcc/gimplify.h index ac3cc8eb552..8e1b75f528b 100644 --- a/gcc/gimplify.h +++ b/gcc/gimplify.h @@ -76,7 +76,7 @@ extern void omp_firstprivatize_variable (struct gimplify_omp_ctx *, tree); extern enum gimplify_status gimplify_expr (tree *, gimple_seq *, gimple_seq *, bool (*) (tree), fallback_t); -int omp_construct_selector_matches (enum tree_code *, int, int *); +extern tree omp_get_construct_context (void); extern void gimplify_type_sizes (tree, gimple_seq *); extern void gimplify_one_sizepos (tree *, gimple_seq *); diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc index 23072b10d75..13cf43d272f 100644 --- a/gcc/omp-general.cc +++ b/gcc/omp-general.cc @@ -1024,31 +1024,6 @@ omp_max_simt_vf (void) return 0; } -/* Store the construct selectors as tree codes from last to first. - CTX is a list of trait selectors, nconstructs must be equal to its - length, and the array CONSTRUCTS holds the output. */ - -void -omp_construct_traits_to_codes (tree ctx, int nconstructs, - enum tree_code *constructs) -{ - int i = nconstructs - 1; - - /* Order must match the OMP_TRAIT_CONSTRUCT_* enumerators in - enum omp_ts_code. */ - static enum tree_code code_map[] - = { OMP_TARGET, OMP_TEAMS, OMP_PARALLEL, OMP_FOR, OMP_SIMD }; - - for (tree ts = ctx; ts; ts = TREE_CHAIN (ts), i--) - { - enum omp_ts_code sel = OMP_TS_CODE (ts); - int j = (int)sel - (int)OMP_TRAIT_CONSTRUCT_TARGET; - gcc_assert (j >= 0 && (unsigned int) j < ARRAY_SIZE (code_map)); - constructs[i] = code_map[j]; - } - gcc_assert (i == -1); -} - /* Return true if PROP is possibly present in one of the offloading target's OpenMP contexts. The format of PROPS string is always offloading target's name terminated by '\0', followed by properties for that offloading @@ -1096,29 +1071,36 @@ omp_offload_device_kind_arch_isa (const char *props, const char *prop) region or when unsure, return false otherwise. */ static bool -omp_maybe_offloaded (void) +omp_maybe_offloaded (tree construct_context) { + /* No offload targets available? */ if (!ENABLE_OFFLOADING) return false; const char *names = getenv ("OFFLOAD_TARGET_NAMES"); if (names == NULL || *names == '\0') return false; + /* Parsing is too early to tell. */ if (symtab->state == PARSING) /* Maybe. */ return true; + + /* Late resolution of offloaded code happens in the offload compiler, + where it's treated as native code instead. So return false here. */ if (cfun && cfun->after_inlining) return false; + + /* The function is explicitly offloaded? */ if (current_function_decl && lookup_attribute ("omp declare target", DECL_ATTRIBUTES (current_function_decl))) return true; - if (cfun && (cfun->curr_properties & PROP_gimple_any) == 0) - { - enum tree_code construct = OMP_TARGET; - if (omp_construct_selector_matches (&construct, 1, NULL)) - return true; - } + + /* Check for nesting inside a target directive. */ + for (tree ts = construct_context; ts; ts = TREE_CHAIN (ts)) + if (OMP_TS_CODE (ts) == OMP_TRAIT_CONSTRUCT_TARGET) + return true; + return false; } @@ -1265,6 +1247,18 @@ omp_context_name_list_prop (tree prop) } } + +/* Helper function called via walk_tree, to determine if *TP is a + PARM_DECL. */ +static tree +expr_uses_parm_decl (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ + if (TREE_CODE (*tp) == PARM_DECL) + return *tp; + return NULL_TREE; +} + /* Diagnose errors in an OpenMP context selector, return CTX if it is correct or error_mark_node otherwise. */ @@ -1280,11 +1274,6 @@ omp_check_context_selector (location_t loc, tree ctx, bool metadirective_p) bool saw_any_prop = false; bool saw_other_prop = false; - /* FIXME: not implemented yet. */ - if (!metadirective_p && tss_code == OMP_TRAIT_SET_TARGET_DEVICE) - sorry_at (loc, "% selector set is not supported " - "yet for %"); - /* Each trait-set-selector-name can only be specified once. */ if (tss_seen[tss_code]) { @@ -1337,6 +1326,33 @@ omp_check_context_selector (location_t loc, tree ctx, bool metadirective_p) || ts_code == OMP_TRAIT_DEVICE_NUM) saw_other_prop = true; + /* This restriction is documented in the spec in the section + for the metadirective "when" clause (7.4.1 in the 5.2 spec). */ + if (metadirective_p && ts_code == OMP_TRAIT_CONSTRUCT_SIMD) + { + error_at (loc, + "properties must not be specified for the % " + "selector in a % context-selector"); + return error_mark_node; + } + + /* Reject expressions that reference parameter variables in + "declare variant", as this is not yet implemented. FIXME; + see PR middle-end/113094. */ + if (!metadirective_p + && (ts_code == OMP_TRAIT_DEVICE_NUM + || ts_code == OMP_TRAIT_USER_CONDITION)) + { + tree exp = OMP_TS_PROPERTIES (ts); + if (walk_tree (&exp, expr_uses_parm_decl, NULL, NULL)) + { + sorry_at (loc, + "reference to function parameter in " + "% dynamic selector expression"); + return error_mark_node; + } + } + if (omp_ts_map[ts_code].valid_properties == NULL) continue; @@ -1401,6 +1417,9 @@ omp_check_context_selector (location_t loc, tree ctx, bool metadirective_p) return ctx; } +/* Forward declarations. */ +static int omp_context_selector_set_compare (enum omp_tss_code, tree, tree); +static int omp_construct_simd_compare (tree, tree, bool); /* Register VARIANT as variant of some base function marked with #pragma omp declare variant. CONSTRUCT is corresponding list of @@ -1471,24 +1490,118 @@ make_omp_metadirective_variant (tree selector, tree directive, tree body) return build_tree_list (selector, build_tree_list (directive, body)); } +/* If the construct selector traits SELECTOR_TRAITS match the corresponding + OpenMP context traits CONTEXT_TRAITS, return true and set *SCORE to the + corresponding score if it is non-null. */ +static bool +omp_construct_traits_match (tree selector_traits, tree context_traits, + score_wide_int *score) +{ + int slength = list_length (selector_traits); + int clength = list_length (context_traits); -/* Return 1 if context selector matches the current OpenMP context, 0 + /* Trivial failure: the selector has more traits than the OpenMP context. */ + if (slength > clength) + return false; + + /* There's only one trait in the selector and it doesn't have any properties + to match. */ + if (slength == 1 && !OMP_TS_PROPERTIES (selector_traits)) + { + int p = 0, i = 1; + enum omp_ts_code code = OMP_TS_CODE (selector_traits); + for (tree t = context_traits; t; t = TREE_CHAIN (t), i++) + if (OMP_TS_CODE (t) == code) + p = i; + if (p != 0) + { + if (score) + *score = wi::shifted_mask (p - 1, 1, false); + return true; + } + else + return false; + } + + /* Now handle the more general cases. + Both lists of traits are ordered from outside in, corresponding to + the c1, ..., cN numbering for the OpenMP context specified in + in section 7.1 of the OpenMP 5.2 spec. Section 7.3 of the spec says + "if the traits that correspond to the construct selector set appear + multiple times in the OpenMP context, the highest valued subset of + context traits that contains all trait selectors in the same order + are used". This means that we want to start the search for a match + from the end of the list, rather than the beginning. To facilitate + that, transfer the lists to temporary arrays to allow random access + to the elements (their order remains outside in). */ + int i, j; + tree s, c; + + tree *sarray = (tree *) alloca (slength * sizeof (tree)); + for (s = selector_traits, i = 0; s; s = TREE_CHAIN (s), i++) + sarray[i] = s; + + tree *carray = (tree *) alloca (clength * sizeof (tree)); + for (c = context_traits, j = 0; c; c = TREE_CHAIN (c), j++) + carray[j] = c; + + /* The variable "i" indexes the selector, "j" indexes the OpenMP context. + Find the "j" corresponding to each sarray[i]. Note that the spec uses + "p" as the 1-based position, but "j" is zero-based, e.g. equal to + p - 1. */ + score_wide_int result = 0; + j = clength - 1; + for (i = slength - 1; i >= 0; i--) + { + enum omp_ts_code code = OMP_TS_CODE (sarray[i]); + tree props = OMP_TS_PROPERTIES (sarray[i]); + for (; j >= 0; j--) + { + if (OMP_TS_CODE (carray[j]) != code) + continue; + if (code == OMP_TRAIT_CONSTRUCT_SIMD + && props + && omp_construct_simd_compare (props, + OMP_TS_PROPERTIES (carray[j]), + true) > 0) + continue; + break; + } + /* If j >= 0, we have a match for this trait at position j. */ + if (j < 0) + return false; + result += wi::shifted_mask (j, 1, false); + j--; + } + if (score) + *score = result; + return true; +} + +/* Return 1 if context selector CTX matches the current OpenMP context, 0 if it does not and -1 if it is unknown and need to be determined later. - Some properties can be checked right away during parsing (this routine), - others need to wait until the whole TU is parsed, others need to wait until + Some properties can be checked right away during parsing, others need + to wait until the whole TU is parsed, others need to wait until IPA, others until vectorization. - METADIRECTIVE_P is true if this is a metadirective context, and DELAY_P - is true if it's too early in compilation to determine whether some - properties match. + CONSTRUCT_CONTEXT is a list of construct traits from the OpenMP context, + which must be collected by omp_get_construct_context during + gimplification. It is ignored (and may be null) if this function is + called during parsing. Otherwise COMPLETE_P should indicate whether + CONSTRUCT_CONTEXT is known to be complete and not missing constructs + filled in later during compilation. Dynamic properties (which are evaluated at run-time) should always return 1. */ int -omp_context_selector_matches (tree ctx, bool metadirective_p, bool delay_p) +omp_context_selector_matches (tree ctx, + tree construct_context, + bool complete_p) { int ret = 1; + bool maybe_offloaded = omp_maybe_offloaded (construct_context); + for (tree tss = ctx; tss; tss = TREE_CHAIN (tss)) { enum omp_tss_code set = OMP_TSS_CODE (tss); @@ -1502,61 +1615,45 @@ omp_context_selector_matches (tree ctx, bool metadirective_p, bool delay_p) if (set == OMP_TRAIT_SET_CONSTRUCT) { - /* For now, ignore the construct set. While something can be - determined already during parsing, we don't know until end of TU - whether additional constructs aren't added through declare variant - unless "omp declare variant variant" attribute exists already - (so in most of the cases), and we'd need to maintain set of - surrounding OpenMP constructs, which is better handled during - gimplification. */ + /* We cannot resolve the construct selector during parsing because + the OpenMP context (and CONSTRUCT_CONTEXT) isn't available + until gimplification. */ if (symtab->state == PARSING) { ret = -1; continue; } - int nconstructs = list_length (selectors); - enum tree_code *constructs = NULL; - if (nconstructs) - { - /* Even though this alloca appears in a loop over selector - sets, it does not repeatedly grow the stack, because - there can be only one construct selector set specified. - This is enforced by omp_check_context_selector. */ - constructs - = (enum tree_code *) alloca (nconstructs - * sizeof (enum tree_code)); - omp_construct_traits_to_codes (selectors, nconstructs, - constructs); - } + gcc_assert (selectors); - if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) + /* During gimplification, CONSTRUCT_CONTEXT is partial, and doesn't + include a construct for "declare simd" that may be added + when there is not an enclosing "target" construct. We might + be able to find a positive match against the partial context + (although we cannot yet score it accurately), but if we can't, + treat it as unknown instead of no match. */ + if (!omp_construct_traits_match (selectors, construct_context, NULL)) { - if (!cfun->after_inlining) - { - ret = -1; - continue; - } - int i; - for (i = 0; i < nconstructs; ++i) - if (constructs[i] == OMP_SIMD) - break; - if (i < nconstructs) - { - ret = -1; - continue; - } - /* If there is no simd, assume it is ok after IPA, - constructs should have been checked before. */ - continue; - } + /* If we've got a complete context, it's definitely a failed + match. */ + if (complete_p) + return 0; - int r = omp_construct_selector_matches (constructs, nconstructs, - NULL); - if (r == 0) - return 0; - if (r == -1) - ret = -1; + /* If the selector doesn't include simd, then we don't have + to worry about whether "declare simd" would cause it to + match; so this is also a definite failure. */ + bool have_simd = false; + for (tree ts = construct_context; ts; ts = TREE_CHAIN (ts)) + if (OMP_TS_CODE (ts) == OMP_TRAIT_CONSTRUCT_SIMD) + { + have_simd = true; + break; + } + if (!have_simd) + return 0; + else + ret = -1; + } continue; } else if (set == OMP_TRAIT_SET_TARGET_DEVICE) @@ -1570,302 +1667,293 @@ omp_context_selector_matches (tree ctx, bool metadirective_p, bool delay_p) switch (sel) { case OMP_TRAIT_IMPLEMENTATION_VENDOR: - if (set == OMP_TRAIT_SET_IMPLEMENTATION) - for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) - { - const char *prop = omp_context_name_list_prop (p); - if (prop == NULL) - return 0; - if (!strcmp (prop, "gnu")) - continue; - return 0; - } - break; - case OMP_TRAIT_IMPLEMENTATION_EXTENSION: - if (set == OMP_TRAIT_SET_IMPLEMENTATION) - /* We don't support any extensions right now. */ - return 0; - break; - case OMP_TRAIT_IMPLEMENTATION_ADMO: - if (set == OMP_TRAIT_SET_IMPLEMENTATION) + gcc_assert (set == OMP_TRAIT_SET_IMPLEMENTATION); + for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) { - if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) - break; - - enum omp_memory_order omo - = ((enum omp_memory_order) - (omp_requires_mask - & OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER)); - if (omo == OMP_MEMORY_ORDER_UNSPECIFIED) - { - /* We don't know yet, until end of TU. */ - if (symtab->state == PARSING) - { - ret = -1; - break; - } - else - omo = OMP_MEMORY_ORDER_RELAXED; - } - tree p = OMP_TS_PROPERTIES (ts); - const char *prop = IDENTIFIER_POINTER (OMP_TP_NAME (p)); - if (!strcmp (prop, "relaxed") - && omo != OMP_MEMORY_ORDER_RELAXED) - return 0; - else if (!strcmp (prop, "seq_cst") - && omo != OMP_MEMORY_ORDER_SEQ_CST) - return 0; - else if (!strcmp (prop, "acq_rel") - && omo != OMP_MEMORY_ORDER_ACQ_REL) - return 0; - else if (!strcmp (prop, "acquire") - && omo != OMP_MEMORY_ORDER_ACQUIRE) - return 0; - else if (!strcmp (prop, "release") - && omo != OMP_MEMORY_ORDER_RELEASE) + const char *prop = omp_context_name_list_prop (p); + if (prop == NULL) return 0; + if (!strcmp (prop, "gnu")) + continue; + return 0; } break; - case OMP_TRAIT_DEVICE_ARCH: - if (set == OMP_TRAIT_SET_DEVICE) - for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) - { - const char *arch = omp_context_name_list_prop (p); - if (arch == NULL) - return 0; - if (metadirective_p && delay_p) - return -1; + case OMP_TRAIT_IMPLEMENTATION_EXTENSION: + gcc_assert (set == OMP_TRAIT_SET_IMPLEMENTATION); + /* We don't support any extensions right now. */ + return 0; + break; + case OMP_TRAIT_IMPLEMENTATION_ADMO: + gcc_assert (set == OMP_TRAIT_SET_IMPLEMENTATION); + if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) + break; - int r = 0; - if (targetm.omp.device_kind_arch_isa != NULL) - r = targetm.omp.device_kind_arch_isa (omp_device_arch, - arch); - if (r == 0 || (r == -1 && symtab->state != PARSING)) + { + enum omp_memory_order omo + = ((enum omp_memory_order) + (omp_requires_mask + & OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER)); + if (omo == OMP_MEMORY_ORDER_UNSPECIFIED) + { + /* We don't know yet, until end of TU. */ + if (symtab->state == PARSING) { - /* If we are or might be in a target region or - declare target function, need to take into account - also offloading values. */ - if (!omp_maybe_offloaded ()) - return 0; - if (ENABLE_OFFLOADING) - { - const char *arches = omp_offload_device_arch; - if (omp_offload_device_kind_arch_isa (arches, - arch)) - { - ret = -1; - continue; - } - } - return 0; + ret = -1; + break; } - else if (r == -1) - ret = -1; - /* If arch matches on the host, it still might not match - in the offloading region. */ - else if (omp_maybe_offloaded ()) - ret = -1; + else + omo = OMP_MEMORY_ORDER_RELAXED; } + tree p = OMP_TS_PROPERTIES (ts); + const char *prop = IDENTIFIER_POINTER (OMP_TP_NAME (p)); + if (!strcmp (prop, "relaxed") + && omo != OMP_MEMORY_ORDER_RELAXED) + return 0; + else if (!strcmp (prop, "seq_cst") + && omo != OMP_MEMORY_ORDER_SEQ_CST) + return 0; + else if (!strcmp (prop, "acq_rel") + && omo != OMP_MEMORY_ORDER_ACQ_REL) + return 0; + else if (!strcmp (prop, "acquire") + && omo != OMP_MEMORY_ORDER_ACQUIRE) + return 0; + else if (!strcmp (prop, "release") + && omo != OMP_MEMORY_ORDER_RELEASE) + return 0; + } + break; + case OMP_TRAIT_DEVICE_ARCH: + gcc_assert (set == OMP_TRAIT_SET_DEVICE); + for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) + { + const char *arch = omp_context_name_list_prop (p); + if (arch == NULL) + return 0; + int r = 0; + if (targetm.omp.device_kind_arch_isa != NULL) + r = targetm.omp.device_kind_arch_isa (omp_device_arch, + arch); + if (r == 0 || (r == -1 && symtab->state != PARSING)) + { + /* If we are or might be in a target region or + declare target function, need to take into account + also offloading values. + Note that maybe_offloaded is always false in late + resolution; that's handled as native code (the + above case) in the offload compiler instead. */ + if (!maybe_offloaded) + return 0; + if (ENABLE_OFFLOADING) + { + const char *arches = omp_offload_device_arch; + if (omp_offload_device_kind_arch_isa (arches, arch)) + { + ret = -1; + continue; + } + } + return 0; + } + else if (r == -1) + ret = -1; + /* If arch matches on the host, it still might not match + in the offloading region. */ + else if (maybe_offloaded) + ret = -1; + } break; case OMP_TRAIT_IMPLEMENTATION_UNIFIED_ADDRESS: - if (set == OMP_TRAIT_SET_IMPLEMENTATION) - { - if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) - break; + gcc_assert (set == OMP_TRAIT_SET_IMPLEMENTATION); + if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) + break; - if ((omp_requires_mask & OMP_REQUIRES_UNIFIED_ADDRESS) == 0) - { - if (symtab->state == PARSING) - ret = -1; - else - return 0; - } + if ((omp_requires_mask & OMP_REQUIRES_UNIFIED_ADDRESS) == 0) + { + if (symtab->state == PARSING) + ret = -1; + else + return 0; } break; case OMP_TRAIT_IMPLEMENTATION_UNIFIED_SHARED_MEMORY: - if (set == OMP_TRAIT_SET_IMPLEMENTATION) - { - if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) - break; + gcc_assert (set == OMP_TRAIT_SET_IMPLEMENTATION); + if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) + break; - if ((omp_requires_mask - & OMP_REQUIRES_UNIFIED_SHARED_MEMORY) == 0) - { - if (symtab->state == PARSING) - ret = -1; - else - return 0; - } + if ((omp_requires_mask + & OMP_REQUIRES_UNIFIED_SHARED_MEMORY) == 0) + { + if (symtab->state == PARSING) + ret = -1; + else + return 0; } break; case OMP_TRAIT_IMPLEMENTATION_DYNAMIC_ALLOCATORS: - if (set == OMP_TRAIT_SET_IMPLEMENTATION) - { - if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) - break; + gcc_assert (set == OMP_TRAIT_SET_IMPLEMENTATION); + if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) + break; - if ((omp_requires_mask - & OMP_REQUIRES_DYNAMIC_ALLOCATORS) == 0) - { - if (symtab->state == PARSING) - ret = -1; - else - return 0; - } + if ((omp_requires_mask + & OMP_REQUIRES_DYNAMIC_ALLOCATORS) == 0) + { + if (symtab->state == PARSING) + ret = -1; + else + return 0; } break; case OMP_TRAIT_IMPLEMENTATION_REVERSE_OFFLOAD: - if (set == OMP_TRAIT_SET_IMPLEMENTATION) - { - if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) - break; + gcc_assert (set == OMP_TRAIT_SET_IMPLEMENTATION); + if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) + break; - if ((omp_requires_mask & OMP_REQUIRES_REVERSE_OFFLOAD) == 0) - { - if (symtab->state == PARSING) - ret = -1; - else - return 0; - } + if ((omp_requires_mask & OMP_REQUIRES_REVERSE_OFFLOAD) == 0) + { + if (symtab->state == PARSING) + ret = -1; + else + return 0; } break; case OMP_TRAIT_DEVICE_KIND: - if (set == OMP_TRAIT_SET_DEVICE) - for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) - { - const char *prop = omp_context_name_list_prop (p); - if (prop == NULL) - return 0; - if (!strcmp (prop, "any")) - continue; - if (!strcmp (prop, "host")) - { + gcc_assert (set == OMP_TRAIT_SET_DEVICE); + for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) + { + const char *prop = omp_context_name_list_prop (p); + if (prop == NULL) + return 0; + if (!strcmp (prop, "any")) + continue; + if (!strcmp (prop, "host")) + { #ifdef ACCEL_COMPILER - return 0; + return 0; #else - if (omp_maybe_offloaded ()) - ret = -1; - continue; + if (maybe_offloaded) + ret = -1; + continue; #endif - } - if (!strcmp (prop, "nohost")) - { + } + if (!strcmp (prop, "nohost")) + { #ifndef ACCEL_COMPILER - if (omp_maybe_offloaded ()) - ret = -1; - else - return 0; -#endif - continue; - } - if (metadirective_p && delay_p) - return -1; - - int r = 0; - if (targetm.omp.device_kind_arch_isa != NULL) - r = targetm.omp.device_kind_arch_isa (omp_device_kind, - prop); - else - r = strcmp (prop, "cpu") == 0; - if (r == 0 || (r == -1 && symtab->state != PARSING)) - { - /* If we are or might be in a target region or - declare target function, need to take into account - also offloading values. */ - if (!omp_maybe_offloaded ()) - return 0; - if (ENABLE_OFFLOADING) - { - const char *kinds = omp_offload_device_kind; - if (omp_offload_device_kind_arch_isa (kinds, prop)) - { - ret = -1; - continue; - } - } + if (maybe_offloaded) + ret = -1; + else return 0; - } - else if (r == -1) - ret = -1; - /* If kind matches on the host, it still might not match - in the offloading region. */ - else if (omp_maybe_offloaded ()) - ret = -1; - } +#endif + continue; + } + + int r = 0; + if (targetm.omp.device_kind_arch_isa != NULL) + r = targetm.omp.device_kind_arch_isa (omp_device_kind, + prop); + else + r = strcmp (prop, "cpu") == 0; + if (r == 0 || (r == -1 && symtab->state != PARSING)) + { + /* If we are or might be in a target region or + declare target function, need to take into account + also offloading values. + Note that maybe_offloaded is always false in late + resolution; that's handled as native code (the + above case) in the offload compiler instead. */ + if (!maybe_offloaded) + return 0; + if (ENABLE_OFFLOADING) + { + const char *kinds = omp_offload_device_kind; + if (omp_offload_device_kind_arch_isa (kinds, prop)) + { + ret = -1; + continue; + } + } + return 0; + } + else if (r == -1) + ret = -1; + /* If kind matches on the host, it still might not match + in the offloading region. */ + else if (maybe_offloaded) + ret = -1; + } break; case OMP_TRAIT_DEVICE_ISA: - if (set == OMP_TRAIT_SET_DEVICE) - for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) - { - const char *isa = omp_context_name_list_prop (p); - if (isa == NULL) - return 0; - if (metadirective_p && delay_p) - return -1; - - int r = 0; - if (targetm.omp.device_kind_arch_isa != NULL) - r = targetm.omp.device_kind_arch_isa (omp_device_isa, - isa); - if (r == 0 || (r == -1 && symtab->state != PARSING)) - { - /* If isa is valid on the target, but not in the - current function and current function has - #pragma omp declare simd on it, some simd clones - might have the isa added later on. */ - if (r == -1 - && targetm.simd_clone.compute_vecsize_and_simdlen - && (cfun == NULL || !cfun->after_inlining)) - { - tree attrs - = DECL_ATTRIBUTES (current_function_decl); - if (lookup_attribute ("omp declare simd", attrs)) - { - ret = -1; - continue; - } - } - /* If we are or might be in a target region or - declare target function, need to take into account - also offloading values. */ - if (!omp_maybe_offloaded ()) - return 0; - if (ENABLE_OFFLOADING) - { - const char *isas = omp_offload_device_isa; - if (omp_offload_device_kind_arch_isa (isas, isa)) - { - ret = -1; - continue; - } - } + gcc_assert (set == OMP_TRAIT_SET_DEVICE); + for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) + { + const char *isa = omp_context_name_list_prop (p); + if (isa == NULL) + return 0; + int r = 0; + if (targetm.omp.device_kind_arch_isa != NULL) + r = targetm.omp.device_kind_arch_isa (omp_device_isa, + isa); + if (r == 0 || (r == -1 && symtab->state != PARSING)) + { + /* If isa is valid on the target, but not in the + current function and current function has + #pragma omp declare simd on it, some simd clones + might have the isa added later on. */ + if (r == -1 + && targetm.simd_clone.compute_vecsize_and_simdlen + && (cfun == NULL || !cfun->after_inlining)) + { + tree attrs + = DECL_ATTRIBUTES (current_function_decl); + if (lookup_attribute ("omp declare simd", attrs)) + { + ret = -1; + continue; + } + } + /* If we are or might be in a target region or + declare target function, need to take into account + also offloading values. + Note that maybe_offloaded is always false in late + resolution; that's handled as native code (the + above case) in the offload compiler instead. */ + if (!maybe_offloaded) return 0; - } - else if (r == -1) - ret = -1; - /* If isa matches on the host, it still might not match - in the offloading region. */ - else if (omp_maybe_offloaded ()) - ret = -1; - } + if (ENABLE_OFFLOADING) + { + const char *isas = omp_offload_device_isa; + if (omp_offload_device_kind_arch_isa (isas, isa)) + { + ret = -1; + continue; + } + } + return 0; + } + else if (r == -1) + ret = -1; + /* If isa matches on the host, it still might not match + in the offloading region. */ + else if (maybe_offloaded) + ret = -1; + } break; case OMP_TRAIT_USER_CONDITION: - if (set == OMP_TRAIT_SET_USER) - for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) - if (OMP_TP_NAME (p) == NULL_TREE) - { - /* OpenMP 5.1 allows non-constant conditions for - metadirectives. */ - if (metadirective_p - && !tree_fits_shwi_p (OMP_TP_VALUE (p))) - break; + gcc_assert (set == OMP_TRAIT_SET_USER); + for (tree p = OMP_TS_PROPERTIES (ts); p; p = TREE_CHAIN (p)) + if (OMP_TP_NAME (p) == NULL_TREE) + { + /* If the expression is not a constant, the selector + is dynamic. */ + if (!tree_fits_shwi_p (OMP_TP_VALUE (p))) + break; - if (integer_zerop (OMP_TP_VALUE (p))) - return 0; - if (integer_nonzerop (OMP_TP_VALUE (p))) - break; - ret = -1; - } + if (integer_zerop (OMP_TP_VALUE (p))) + return 0; + if (integer_nonzerop (OMP_TP_VALUE (p))) + break; + ret = -1; + } break; default: break; @@ -1876,10 +1964,13 @@ omp_context_selector_matches (tree ctx, bool metadirective_p, bool delay_p) } /* Compare construct={simd} CLAUSES1 with CLAUSES2, return 0/-1/1/2 as - in omp_context_selector_set_compare. */ + in omp_context_selector_set_compare. If MATCH_P is true, additionally + apply the special matching rules for the "simdlen" and "aligned" clauses + used to determine whether the selector CLAUSES1 is part of matches + the OpenMP context containing CLAUSES2. */ static int -omp_construct_simd_compare (tree clauses1, tree clauses2) +omp_construct_simd_compare (tree clauses1, tree clauses2, bool match_p) { if (clauses1 == NULL_TREE) return clauses2 == NULL_TREE ? 0 : -1; @@ -1896,6 +1987,7 @@ omp_construct_simd_compare (tree clauses1, tree clauses2) : inbranch(false), notinbranch(false), simdlen(NULL_TREE) {} } data[2]; unsigned int i; + tree e0, e1; for (i = 0; i < 2; i++) for (tree c = i ? clauses2 : clauses1; c; c = OMP_CLAUSE_CHAIN (c)) { @@ -1934,10 +2026,23 @@ omp_construct_simd_compare (tree clauses1, tree clauses2) r |= data[0].inbranch ? 2 : 1; if (data[0].notinbranch != data[1].notinbranch) r |= data[0].notinbranch ? 2 : 1; - if (!simple_cst_equal (data[0].simdlen, data[1].simdlen)) + e0 = data[0].simdlen; + e1 = data[1].simdlen; + if (!simple_cst_equal (e0, e1)) { - if (data[0].simdlen && data[1].simdlen) - return 2; + if (e0 && e1) + { + if (match_p && tree_fits_uhwi_p (e0) && tree_fits_uhwi_p (e1)) + { + /* The two simdlen clauses match if m is a multiple of n. */ + unsigned HOST_WIDE_INT n = tree_to_uhwi (e0); + unsigned HOST_WIDE_INT m = tree_to_uhwi (e1); + if (m % n != 0) + return 2; + } + else + return 2; + } r |= data[0].simdlen ? 2 : 1; } if (data[0].data_sharing.length () < data[1].data_sharing.length () @@ -1978,9 +2083,22 @@ omp_construct_simd_compare (tree clauses1, tree clauses2) } if (c1 == NULL_TREE) continue; - if (!simple_cst_equal (OMP_CLAUSE_ALIGNED_ALIGNMENT (c1), - OMP_CLAUSE_ALIGNED_ALIGNMENT (c2))) - return 2; + e0 = OMP_CLAUSE_ALIGNED_ALIGNMENT (c1); + e1 = OMP_CLAUSE_ALIGNED_ALIGNMENT (c2); + if (!simple_cst_equal (e0, e1)) + { + if (e0 && e1 + && match_p && tree_fits_uhwi_p (e0) && tree_fits_uhwi_p (e1)) + { + /* The two aligned clauses match if n is a multiple of m. */ + unsigned HOST_WIDE_INT n = tree_to_uhwi (e0); + unsigned HOST_WIDE_INT m = tree_to_uhwi (e1); + if (n % m != 0) + return 2; + } + else + return 2; + } } switch (r) { @@ -2059,7 +2177,7 @@ omp_context_selector_props_compare (enum omp_tss_code set, 1 if CTX2 is a strict subset of CTX1, or 2 if neither context is a subset of another one. */ -int +static int omp_context_selector_set_compare (enum omp_tss_code set, tree ctx1, tree ctx2) { @@ -2096,7 +2214,8 @@ omp_context_selector_set_compare (enum omp_tss_code set, tree ctx1, tree ctx2) int r = 0; if (OMP_TS_CODE (ts1) == OMP_TRAIT_CONSTRUCT_SIMD) r = omp_construct_simd_compare (OMP_TS_PROPERTIES (ts1), - OMP_TS_PROPERTIES (ts2)); + OMP_TS_PROPERTIES (ts2), + false); if (r == 2 || (ret && r && (ret < 0) != (r < 0))) return 2; if (ret == 0) @@ -2296,11 +2415,9 @@ omp_dynamic_cond (tree ctx) { tree expr_list = OMP_TS_PROPERTIES (user); - gcc_assert (OMP_TP_NAME (expr_list) == NULL_TREE); - /* The user condition is not dynamic if it is constant. */ - if (!tree_fits_shwi_p (TREE_VALUE (expr_list))) - expr = TREE_VALUE (expr_list); + if (!tree_fits_shwi_p (OMP_TP_VALUE (expr_list))) + expr = OMP_TP_VALUE (expr_list); } tree target_device @@ -2359,86 +2476,145 @@ omp_dynamic_cond (tree ctx) return expr; } +/* Compute *SCORE for context selector CTX, which is already known to match. + CONSTRUCT_CONTEXT is the OpenMP construct context; if this is null or + incomplete (e.g., during parsing or gimplification) then it may not be + possible to compute the score accurately. In this case it does a best + guess based on the incomplete context and returns false; otherwise it + returns true. -/* Compute *SCORE for context selector CTX. Return true if the score - would be different depending on whether it is a declare simd clone or - not. DECLARE_SIMD should be true for the case when it would be - a declare simd clone. */ + Cited text in the comments is from section 7.2 of the OpenMP 5.2 + specification. */ static bool -omp_context_compute_score (tree ctx, score_wide_int *score, bool declare_simd) +omp_context_compute_score (tree ctx, tree construct_context, + bool complete_p, score_wide_int *score) { - tree selectors - = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT); - bool has_kind - = (omp_get_context_selector (ctx, OMP_TRAIT_SET_DEVICE, - OMP_TRAIT_DEVICE_KIND) - || omp_get_context_selector (ctx, OMP_TRAIT_SET_TARGET_DEVICE, - OMP_TRAIT_DEVICE_KIND)); - bool has_arch - = (omp_get_context_selector (ctx, OMP_TRAIT_SET_DEVICE, - OMP_TRAIT_DEVICE_ARCH) - || omp_get_context_selector (ctx, OMP_TRAIT_SET_TARGET_DEVICE, - OMP_TRAIT_DEVICE_ARCH)); - bool has_isa - = (omp_get_context_selector (ctx, OMP_TRAIT_SET_DEVICE, - OMP_TRAIT_DEVICE_ISA) - || omp_get_context_selector (ctx, OMP_TRAIT_SET_TARGET_DEVICE, - OMP_TRAIT_DEVICE_ISA)); - bool ret = false; + int l = list_length (construct_context); + bool retval = true; + + /* "the final score is the sum of the values of all specified selectors + plus 1". */ *score = 1; for (tree tss = ctx; tss; tss = TREE_CHAIN (tss)) - if (OMP_TSS_TRAIT_SELECTORS (tss) != selectors) - for (tree ts = OMP_TSS_TRAIT_SELECTORS (tss); ts; ts = TREE_CHAIN (ts)) + { + if (OMP_TSS_CODE (tss) == OMP_TRAIT_SET_CONSTRUCT) { - tree s = OMP_TS_SCORE (ts); - if (s && TREE_CODE (s) == INTEGER_CST) - *score += score_wide_int::from (wi::to_wide (s), - TYPE_SIGN (TREE_TYPE (s))); + /* "Each trait selector for which the corresponding trait appears + in the context trait set in the OpenMP context..." */ + score_wide_int tss_score; + omp_construct_traits_match (OMP_TSS_TRAIT_SELECTORS (tss), + construct_context, &tss_score); + *score += tss_score; + if (!complete_p) + retval = false; + } + else if (OMP_TSS_CODE (tss) == OMP_TRAIT_SET_DEVICE + || OMP_TSS_CODE (tss) == OMP_TRAIT_SET_TARGET_DEVICE) + { + /* "The kind, arch, and isa selectors, if specified, are given + the values 2**l, 2**(l+1), and 2**(l+2), respectively..." + FIXME: the spec isn't clear what should happen if there are + both "device" and "target_device" selector sets specified. + This implementation adds up the bits rather than ORs them. */ + for (tree ts = OMP_TSS_TRAIT_SELECTORS (tss); ts; + ts = TREE_CHAIN (ts)) + { + enum omp_ts_code code = OMP_TS_CODE (ts); + if (code == OMP_TRAIT_DEVICE_KIND) + *score += wi::shifted_mask (l, 1, false); + else if (code == OMP_TRAIT_DEVICE_ARCH) + *score += wi::shifted_mask (l + 1, 1, false); + else if (code == OMP_TRAIT_DEVICE_ISA) + *score += wi::shifted_mask (l + 2, 1, false); + } + if (!complete_p) + retval = false; + } + else + { + /* "Trait selectors for which a trait-score is specified..." + Note that there are no implementation-defined selectors, and + "other selectors are given a value of zero". */ + for (tree ts = OMP_TSS_TRAIT_SELECTORS (tss); ts; + ts = TREE_CHAIN (ts)) + { + tree s = OMP_TS_SCORE (ts); + if (s && TREE_CODE (s) == INTEGER_CST) + *score += score_wide_int::from (wi::to_wide (s), + TYPE_SIGN (TREE_TYPE (s))); + } + } + } + return retval; +} + +/* CONSTRUCT_CONTEXT contains "the directive names, each being a trait, + of all enclosing constructs at that point in the program up to a target + construct", per section 7.1 of the 5.2 specification. The traits are + collected during gimplification and are listed outermost first. + + This function attempts to apply the "if the point in the program is not + enclosed by a target construct, the following rules are applied in order" + requirements that follow in the same paragraph. This may not be possible, + depending on the compilation phase; in particular, "declare simd" clones + are not known until late resolution. + + The augmented context is returned, and *COMPLETEP is set to true if + the context is known to be complete, false otherwise. */ +static tree +omp_complete_construct_context (tree construct_context, bool *completep) +{ + /* The point in the program is enclosed by a target construct. */ + if (construct_context + && OMP_TS_CODE (construct_context) == OMP_TRAIT_CONSTRUCT_TARGET) + *completep = true; + + /* At parse time we have none of the information we need to collect + the missing pieces. */ + else if (symtab->state == PARSING) + *completep = false; + + else + { + tree attributes = DECL_ATTRIBUTES (current_function_decl); + + /* Add simd trait when in a simd clone. This information is only + available during late resolution in the omp_device_lower pass, + however we can also rule out cases where we know earlier that + cfun is not a candidate for cloning. */ + if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) + { + cgraph_node *node = cgraph_node::get (cfun->decl); + if (node->simdclone) + construct_context = make_trait_selector (OMP_TRAIT_CONSTRUCT_SIMD, + NULL_TREE, NULL_TREE, + construct_context); + *completep = true; + } + else if (lookup_attribute ("omp declare simd", attributes)) + *completep = false; + else + *completep = true; + + /* Add construct selector set within a "declare variant" function. */ + tree variant_attr + = lookup_attribute ("omp declare variant variant", attributes); + if (variant_attr) + { + tree temp = NULL_TREE; + for (tree t = TREE_VALUE (variant_attr); t; t = TREE_CHAIN (t)) + temp = chainon (temp, copy_node (t)); + construct_context = chainon (temp, construct_context); } - if (selectors || has_kind || has_arch || has_isa) - { - int nconstructs = list_length (selectors); - enum tree_code *constructs = NULL; - if (nconstructs) - { - constructs - = (enum tree_code *) alloca (nconstructs - * sizeof (enum tree_code)); - omp_construct_traits_to_codes (selectors, nconstructs, constructs); - } - int *scores - = (int *) alloca ((2 * nconstructs + 2) * sizeof (int)); - if (omp_construct_selector_matches (constructs, nconstructs, scores) - == 2) - ret = true; - int b = declare_simd ? nconstructs + 1 : 0; - if (scores[b + nconstructs] + 4U < score->get_precision ()) - { - for (int n = 0; n < nconstructs; ++n) - { - if (scores[b + n] < 0) - { - *score = -1; - return ret; - } - *score += wi::shifted_mask (scores[b + n], 1, false); - } - if (has_kind) - *score += wi::shifted_mask (scores[b + nconstructs], - 1, false); - if (has_arch) - *score += wi::shifted_mask (scores[b + nconstructs] + 1, - 1, false); - if (has_isa) - *score += wi::shifted_mask (scores[b + nconstructs] + 2, - 1, false); - } - else /* FIXME: Implement this. */ - gcc_unreachable (); + /* Add target trait when in a target variant. */ + if (lookup_attribute ("omp declare target block", attributes)) + construct_context = make_trait_selector (OMP_TRAIT_CONSTRUCT_TARGET, + NULL_TREE, NULL_TREE, + construct_context); } - return ret; + return construct_context; } /* Class describing a single variant. */ @@ -2537,388 +2713,6 @@ omp_declare_variant_alt_hasher::equal (omp_declare_variant_base_entry *x, static GTY(()) hash_table *omp_declare_variant_alt; -/* Try to resolve declare variant after gimplification. */ - -static tree -omp_resolve_late_declare_variant (tree alt) -{ - cgraph_node *node = cgraph_node::get (alt); - cgraph_node *cur_node = cgraph_node::get (cfun->decl); - if (node == NULL - || !node->declare_variant_alt - || !cfun->after_inlining) - return alt; - - omp_declare_variant_base_entry entry; - entry.base = NULL; - entry.node = node; - entry.variants = NULL; - omp_declare_variant_base_entry *entryp - = omp_declare_variant_alt->find_with_hash (&entry, DECL_UID (alt)); - - unsigned int i, j; - omp_declare_variant_entry *varentry1, *varentry2; - auto_vec matches; - unsigned int nmatches = 0; - FOR_EACH_VEC_SAFE_ELT (entryp->variants, i, varentry1) - { - if (varentry1->matches) - { - /* This has been checked to be ok already. */ - matches.safe_push (true); - nmatches++; - continue; - } - switch (omp_context_selector_matches (varentry1->ctx, false, true)) - { - case 0: - matches.safe_push (false); - break; - case -1: - return alt; - default: - matches.safe_push (true); - nmatches++; - break; - } - } - - if (nmatches == 0) - return entryp->base->decl; - - /* A context selector that is a strict subset of another context selector - has a score of zero. */ - FOR_EACH_VEC_SAFE_ELT (entryp->variants, i, varentry1) - if (matches[i]) - { - for (j = i + 1; - vec_safe_iterate (entryp->variants, j, &varentry2); ++j) - if (matches[j]) - { - int r = omp_context_selector_compare (varentry1->ctx, - varentry2->ctx); - if (r == -1) - { - /* ctx1 is a strict subset of ctx2, ignore ctx1. */ - matches[i] = false; - break; - } - else if (r == 1) - /* ctx2 is a strict subset of ctx1, remove ctx2. */ - matches[j] = false; - } - } - - score_wide_int max_score = -1; - varentry2 = NULL; - FOR_EACH_VEC_SAFE_ELT (entryp->variants, i, varentry1) - if (matches[i]) - { - score_wide_int score - = (cur_node->simdclone ? varentry1->score_in_declare_simd_clone - : varentry1->score); - if (score > max_score) - { - max_score = score; - varentry2 = varentry1; - } - } - return varentry2->variant->decl; -} - -/* Hook to adjust hash tables on cgraph_node removal. */ - -static void -omp_declare_variant_remove_hook (struct cgraph_node *node, void *) -{ - if (!node->declare_variant_alt) - return; - - /* Drop this hash table completely. */ - omp_declare_variants = NULL; - /* And remove node from the other hash table. */ - if (omp_declare_variant_alt) - { - omp_declare_variant_base_entry entry; - entry.base = NULL; - entry.node = node; - entry.variants = NULL; - omp_declare_variant_alt->remove_elt_with_hash (&entry, - DECL_UID (node->decl)); - } -} - -/* Try to resolve declare variant, return the variant decl if it should - be used instead of base, or base otherwise. */ - -tree -omp_resolve_declare_variant (tree base) -{ - tree variant1 = NULL_TREE, variant2 = NULL_TREE; - if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) - return omp_resolve_late_declare_variant (base); - - auto_vec variants; - auto_vec defer; - bool any_deferred = false; - for (tree attr = DECL_ATTRIBUTES (base); attr; attr = TREE_CHAIN (attr)) - { - attr = lookup_attribute ("omp declare variant base", attr); - if (attr == NULL_TREE) - break; - if (TREE_CODE (TREE_PURPOSE (TREE_VALUE (attr))) != FUNCTION_DECL) - continue; - cgraph_node *node = cgraph_node::get (base); - /* If this is already a magic decl created by this function, - don't process it again. */ - if (node && node->declare_variant_alt) - return base; - switch (omp_context_selector_matches (TREE_VALUE (TREE_VALUE (attr)), - false, true)) - { - case 0: - /* No match, ignore. */ - break; - case -1: - /* Needs to be deferred. */ - any_deferred = true; - variants.safe_push (attr); - defer.safe_push (true); - break; - default: - variants.safe_push (attr); - defer.safe_push (false); - break; - } - } - if (variants.length () == 0) - return base; - - if (any_deferred) - { - score_wide_int max_score1 = 0; - score_wide_int max_score2 = 0; - bool first = true; - unsigned int i; - tree attr1, attr2; - omp_declare_variant_base_entry entry; - entry.base = cgraph_node::get_create (base); - entry.node = NULL; - vec_alloc (entry.variants, variants.length ()); - FOR_EACH_VEC_ELT (variants, i, attr1) - { - score_wide_int score1; - score_wide_int score2; - bool need_two; - tree ctx = TREE_VALUE (TREE_VALUE (attr1)); - need_two = omp_context_compute_score (ctx, &score1, false); - if (need_two) - omp_context_compute_score (ctx, &score2, true); - else - score2 = score1; - if (first) - { - first = false; - max_score1 = score1; - max_score2 = score2; - if (!defer[i]) - { - variant1 = attr1; - variant2 = attr1; - } - } - else - { - if (max_score1 == score1) - variant1 = NULL_TREE; - else if (score1 > max_score1) - { - max_score1 = score1; - variant1 = defer[i] ? NULL_TREE : attr1; - } - if (max_score2 == score2) - variant2 = NULL_TREE; - else if (score2 > max_score2) - { - max_score2 = score2; - variant2 = defer[i] ? NULL_TREE : attr1; - } - } - omp_declare_variant_entry varentry; - varentry.variant - = cgraph_node::get_create (TREE_PURPOSE (TREE_VALUE (attr1))); - varentry.score = score1; - varentry.score_in_declare_simd_clone = score2; - varentry.ctx = ctx; - varentry.matches = !defer[i]; - entry.variants->quick_push (varentry); - } - - /* If there is a clear winner variant with the score which is not - deferred, verify it is not a strict subset of any other context - selector and if it is not, it is the best alternative no matter - whether the others do or don't match. */ - if (variant1 && variant1 == variant2) - { - tree ctx1 = TREE_VALUE (TREE_VALUE (variant1)); - FOR_EACH_VEC_ELT (variants, i, attr2) - { - if (attr2 == variant1) - continue; - tree ctx2 = TREE_VALUE (TREE_VALUE (attr2)); - int r = omp_context_selector_compare (ctx1, ctx2); - if (r == -1) - { - /* The winner is a strict subset of ctx2, can't - decide now. */ - variant1 = NULL_TREE; - break; - } - } - if (variant1) - { - vec_free (entry.variants); - return TREE_PURPOSE (TREE_VALUE (variant1)); - } - } - - static struct cgraph_node_hook_list *node_removal_hook_holder; - if (!node_removal_hook_holder) - node_removal_hook_holder - = symtab->add_cgraph_removal_hook (omp_declare_variant_remove_hook, - NULL); - - if (omp_declare_variants == NULL) - omp_declare_variants - = hash_table::create_ggc (64); - omp_declare_variant_base_entry **slot - = omp_declare_variants->find_slot (&entry, INSERT); - if (*slot != NULL) - { - vec_free (entry.variants); - return (*slot)->node->decl; - } - - *slot = ggc_cleared_alloc (); - (*slot)->base = entry.base; - (*slot)->node = entry.base; - (*slot)->variants = entry.variants; - tree alt = build_decl (DECL_SOURCE_LOCATION (base), FUNCTION_DECL, - DECL_NAME (base), TREE_TYPE (base)); - DECL_ARTIFICIAL (alt) = 1; - DECL_IGNORED_P (alt) = 1; - TREE_STATIC (alt) = 1; - tree attributes = DECL_ATTRIBUTES (base); - if (lookup_attribute ("noipa", attributes) == NULL) - { - attributes = tree_cons (get_identifier ("noipa"), NULL, attributes); - if (lookup_attribute ("noinline", attributes) == NULL) - attributes = tree_cons (get_identifier ("noinline"), NULL, - attributes); - if (lookup_attribute ("noclone", attributes) == NULL) - attributes = tree_cons (get_identifier ("noclone"), NULL, - attributes); - if (lookup_attribute ("no_icf", attributes) == NULL) - attributes = tree_cons (get_identifier ("no_icf"), NULL, - attributes); - } - DECL_ATTRIBUTES (alt) = attributes; - DECL_INITIAL (alt) = error_mark_node; - (*slot)->node = cgraph_node::create (alt); - (*slot)->node->declare_variant_alt = 1; - (*slot)->node->create_reference (entry.base, IPA_REF_ADDR); - omp_declare_variant_entry *varentry; - FOR_EACH_VEC_SAFE_ELT (entry.variants, i, varentry) - (*slot)->node->create_reference (varentry->variant, IPA_REF_ADDR); - if (omp_declare_variant_alt == NULL) - omp_declare_variant_alt - = hash_table::create_ggc (64); - *omp_declare_variant_alt->find_slot_with_hash (*slot, DECL_UID (alt), - INSERT) = *slot; - return alt; - } - - if (variants.length () == 1) - return TREE_PURPOSE (TREE_VALUE (variants[0])); - - /* A context selector that is a strict subset of another context selector - has a score of zero. */ - tree attr1, attr2; - unsigned int i, j; - FOR_EACH_VEC_ELT (variants, i, attr1) - if (attr1) - { - tree ctx1 = TREE_VALUE (TREE_VALUE (attr1)); - FOR_EACH_VEC_ELT_FROM (variants, j, attr2, i + 1) - if (attr2) - { - tree ctx2 = TREE_VALUE (TREE_VALUE (attr2)); - int r = omp_context_selector_compare (ctx1, ctx2); - if (r == -1) - { - /* ctx1 is a strict subset of ctx2, remove - attr1 from the vector. */ - variants[i] = NULL_TREE; - break; - } - else if (r == 1) - /* ctx2 is a strict subset of ctx1, remove attr2 - from the vector. */ - variants[j] = NULL_TREE; - } - } - score_wide_int max_score1 = 0; - score_wide_int max_score2 = 0; - bool first = true; - FOR_EACH_VEC_ELT (variants, i, attr1) - if (attr1) - { - if (variant1) - { - score_wide_int score1; - score_wide_int score2; - bool need_two; - tree ctx; - if (first) - { - first = false; - ctx = TREE_VALUE (TREE_VALUE (variant1)); - need_two = omp_context_compute_score (ctx, &max_score1, false); - if (need_two) - omp_context_compute_score (ctx, &max_score2, true); - else - max_score2 = max_score1; - } - ctx = TREE_VALUE (TREE_VALUE (attr1)); - need_two = omp_context_compute_score (ctx, &score1, false); - if (need_two) - omp_context_compute_score (ctx, &score2, true); - else - score2 = score1; - if (score1 > max_score1) - { - max_score1 = score1; - variant1 = attr1; - } - if (score2 > max_score2) - { - max_score2 = score2; - variant2 = attr1; - } - } - else - { - variant1 = attr1; - variant2 = attr1; - } - } - /* If there is a disagreement on which variant has the highest score - depending on whether it will be in a declare simd clone or not, - punt for now and defer until after IPA where we will know that. */ - return ((variant1 && variant1 == variant2) - ? TREE_PURPOSE (TREE_VALUE (variant1)) : base); -} - void omp_lto_output_declare_variant_alt (lto_simple_output_block *ob, cgraph_node *node, @@ -3059,16 +2853,36 @@ sort_variant (const void * a, const void *b, void *) } /* Return a vector of dynamic replacement candidates for the directive - candidates in ALL_VARIANTS. Return an empty vector if the metadirective + candidates in ALL_VARIANTS. Return an empty vector if the candidates cannot be resolved. */ -static vec +vec omp_get_dynamic_candidates (vec &all_variants, - bool delay_p) + tree construct_context) { auto_vec variants; struct omp_variant default_variant; bool default_found = false; + bool complete_p; + + construct_context + = omp_complete_construct_context (construct_context, &complete_p); + + if (dump_file) + { + fprintf (dump_file, "\nIn omp_get_dynamic_candidates:\n"); + if (symtab->state == PARSING) + fprintf (dump_file, "invoked during parsing\n"); + else if (cfun && (cfun->curr_properties & PROP_gimple_any) == 0) + fprintf (dump_file, "invoked during gimplification\n"); + else if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0) + fprintf (dump_file, "invoked during late resolution\n"); + else + fprintf (dump_file, "confused about invocation context?!?\n"); + fprintf (dump_file, "construct_context has %d traits (%s)\n", + (construct_context ? list_length (construct_context) : 0), + (complete_p ? "complete" : "incomplete")); + } for (unsigned int i = 0; i < all_variants.length (); i++) { @@ -3097,19 +2911,24 @@ omp_get_dynamic_candidates (vec &all_variants, fprintf (dump_file, " as candidate - "); } - switch (omp_context_selector_matches (variant.selector, true, delay_p)) + switch (omp_context_selector_matches (variant.selector, + construct_context, complete_p)) { case -1: - variant.resolvable_p = false; + /* Give up for now. This can only happen during early resolution, + prior to or during gimplification. */ if (dump_file) - fprintf (dump_file, "unresolvable"); - /* FALLTHRU */ + fprintf (dump_file, "unresolvable\n"); + gcc_assert (!cfun || (cfun->curr_properties & PROP_gimple_any) == 0); + variants.truncate (0); + return variants.copy (); case 1: - /* TODO: Handle SIMD score?. */ - omp_context_compute_score (variant.selector, &variant.score, false); + variant.resolvable_p + = omp_context_compute_score (variant.selector, construct_context, + complete_p, &variant.score); variant.dynamic_selector = omp_dynamic_cond (variant.selector); variants.safe_push (variant); - if (dump_file && variant.resolvable_p) + if (dump_file) { if (variant.dynamic_selector) fprintf (dump_file, "matched, dynamic"); @@ -3130,6 +2949,21 @@ omp_get_dynamic_candidates (vec &all_variants, /* There must be one default variant. */ gcc_assert (default_found); + /* If there are no matching selectors, return the default. */ + if (variants.length () == 0) + { + variants.safe_push (default_variant); + return variants.copy (); + } + + /* If there is only one matching selector, use it. */ + if (variants.length () == 1) + { + if (variants[0].dynamic_selector) + variants.safe_push (default_variant); + return variants.copy (); + } + /* A context selector that is a strict subset of another context selector has a score of zero. */ for (unsigned int i = 0; i < variants.length (); i++) @@ -3155,14 +2989,80 @@ omp_get_dynamic_candidates (vec &all_variants, /* Add the default as a final choice. */ variants.safe_push (default_variant); + if (dump_file) + { + fprintf (dump_file, "Sorted variants are:\n"); + for (unsigned i = 0; i < variants.length (); i++) + { + HOST_WIDE_INT score = variants[i].score.to_shwi (); + fprintf (dump_file, "score %d ", (int)score); + if (variants[i].selector) + { + fprintf (dump_file, "selector "); + print_omp_context_selector (dump_file, variants[i].selector, + TDF_NONE); + fprintf (dump_file, "\n"); + } + else + fprintf (dump_file, "default selector\n"); + } + } + /* Build the dynamic candidate list. */ for (unsigned i = 0; i < variants.length (); i++) { - /* If one of the candidates is unresolvable, give up for now. */ + /* In general, we can't proceed if we can't accurately score any + of the selectors, since the sorting may be incorrect. But, since + the actual score will never be lower than the guessed value, we + can use the variant if it is the first one and either the next + one is resolvable or we can make a direct comparison of the + isa/arch/kind bits. */ if (!variants[i].resolvable_p) { - variants.truncate (0); - break; + bool ok = true; + if (i != 0) + ok = false; + else if (!variants[i+1].resolvable_p) + { + /* To keep comparisons simple, reject selectors that contain + sets other than device, target_device, or construct. */ + for (tree tss = variants[i].selector; + tss && ok; tss = TREE_CHAIN (tss)) + { + enum omp_tss_code code = OMP_TSS_CODE (tss); + if (code != OMP_TRAIT_SET_DEVICE + && code != OMP_TRAIT_SET_TARGET_DEVICE + && code != OMP_TRAIT_SET_CONSTRUCT) + ok = false; + } + for (tree tss = variants[i+1].selector; + tss && ok; tss = TREE_CHAIN (tss)) + { + enum omp_tss_code code = OMP_TSS_CODE (tss); + if (code != OMP_TRAIT_SET_DEVICE + && code != OMP_TRAIT_SET_TARGET_DEVICE + && code != OMP_TRAIT_SET_CONSTRUCT) + ok = false; + } + /* Ignore the construct bits of the score. If the isa/arch/kind + bits are strictly ordered, we're good to go. Since + "the final score is the sum of the values of all specified + selectors plus 1", subtract that 1 from both scores before + getting rid of the low bits. */ + if (ok) + { + size_t l = list_length (construct_context); + if ((variants[i].score - 1) >> l + <= (variants[i+1].score - 1) >> l) + ok = false; + } + } + + if (!ok) + { + variants.truncate (0); + break; + } } if (dump_file) @@ -3192,35 +3092,106 @@ omp_get_dynamic_candidates (vec &all_variants, return variants.copy (); } +/* Two attempts are made to resolve calls to "declare variant" functions: + early resolution in the gimplifier, and late resolution in the + omp_device_lower pass. If early resolution is not possible, the + original function call is gimplified into the same form as metadirective + and goes through the same late resolution code as metadirective. */ + +/* Collect "declare variant" candidates for BASE. CONSTRUCT_CONTEXT + is the un-augmented context, or NULL_TREE if that information is not + available yet. */ +vec +omp_declare_variant_candidates (tree base, tree construct_context) +{ + auto_vec candidates; + bool complete_p; + tree augmented_context + = omp_complete_construct_context (construct_context, &complete_p); + + /* The variants are stored on (possible multiple) "omp declare variant base" + attributes on the base function. */ + for (tree attr = DECL_ATTRIBUTES (base); attr; attr = TREE_CHAIN (attr)) + { + attr = lookup_attribute ("omp declare variant base", attr); + if (attr == NULL_TREE) + break; + + tree fndecl = TREE_PURPOSE (TREE_VALUE (attr)); + tree selector = TREE_VALUE (TREE_VALUE (attr)); + + if (TREE_CODE (fndecl) != FUNCTION_DECL) + continue; + + /* Ignore this variant if its selector is known not to match. */ + if (!omp_context_selector_matches (selector, augmented_context, + complete_p)) + continue; + + struct omp_variant candidate; + candidate.selector = selector; + candidate.dynamic_selector = NULL_TREE; + candidate.alternative = fndecl; + candidates.safe_push (candidate); + } + + /* Add a default that is the base function. */ + struct omp_variant v; + v.selector = NULL_TREE; + v.dynamic_selector = NULL_TREE; + v.alternative = base; + candidates.safe_push (v); + return candidates.copy (); +} + +/* Collect metadirective candidates for METADIRECTIVE. CONSTRUCT_CONTEXT + is the un-augmented context, or NULL_TREE if that information is not + available yet. */ +vec +omp_metadirective_candidates (tree metadirective, tree construct_context) +{ + auto_vec candidates; + tree variant = OMP_METADIRECTIVE_VARIANTS (metadirective); + bool complete_p; + tree augmented_context + = omp_complete_construct_context (construct_context, &complete_p); + + gcc_assert (variant); + for (; variant; variant = TREE_CHAIN (variant)) + { + tree selector = OMP_METADIRECTIVE_VARIANT_SELECTOR (variant); + + /* Ignore this variant if its selector is known not to match. */ + if (!omp_context_selector_matches (selector, augmented_context, + complete_p)) + continue; + + struct omp_variant candidate; + candidate.selector = selector; + candidate.dynamic_selector = NULL_TREE; + candidate.alternative = OMP_METADIRECTIVE_VARIANT_DIRECTIVE (variant); + candidate.body = OMP_METADIRECTIVE_VARIANT_BODY (variant); + candidates.safe_push (candidate); + } + return candidates.copy (); +} + /* Return a vector of dynamic replacement candidates for the metadirective statement in METADIRECTIVE. Return an empty vector if the metadirective - cannot be resolved. */ + cannot be resolved. This function is intended to be called from the + front ends, prior to gimplification. */ vec omp_early_resolve_metadirective (tree metadirective) { - auto_vec candidates; - tree variant = OMP_METADIRECTIVE_VARIANTS (metadirective); - - gcc_assert (variant); - while (variant) - { - struct omp_variant candidate; - - candidate.selector = OMP_METADIRECTIVE_VARIANT_SELECTOR (variant); - candidate.alternative = OMP_METADIRECTIVE_VARIANT_DIRECTIVE (variant); - candidate.body = OMP_METADIRECTIVE_VARIANT_BODY (variant); - - candidates.safe_push (candidate); - variant = TREE_CHAIN (variant); - } - - return omp_get_dynamic_candidates (candidates, true); + vec candidates + = omp_metadirective_candidates (metadirective, NULL_TREE); + return omp_get_dynamic_candidates (candidates, NULL_TREE); } /* Return a vector of dynamic replacement candidates for the metadirective - Gimple statement in GS. Return an empty vector if the metadirective - cannot be resolved. */ + Gimple statement in GS. This version is called during late resolution + in the ompdevlow pass. */ vec omp_late_resolve_metadirective (gimple *gs) @@ -3232,12 +3203,14 @@ omp_late_resolve_metadirective (gimple *gs) struct omp_variant variant; variant.selector = gimple_op (gs, i); + variant.dynamic_selector = NULL_TREE; variant.alternative = gimple_omp_metadirective_label (gs, i); variants.safe_push (variant); } - return omp_get_dynamic_candidates (variants, false); + tree construct_context = gimple_omp_metadirective_context (gs); + return omp_get_dynamic_candidates (variants, construct_context); } /* Encode an oacc launch argument. This matches the GOMP_LAUNCH_PACK diff --git a/gcc/omp-general.h b/gcc/omp-general.h index b3e9efb93db..318ed477233 100644 --- a/gcc/omp-general.h +++ b/gcc/omp-general.h @@ -95,16 +95,27 @@ struct omp_for_data desirable to be the same on all targets. */ typedef generic_wide_int > score_wide_int; -/* A structure describing a variant in a metadirective. */ - +/* A structure describing a variant alternative in a metadirective or + variant function, used for matching and scoring during resolution. */ struct GTY(()) omp_variant { - score_wide_int score; + /* Context selector. This is NULL_TREE for the default. */ tree selector; + /* For early resolution of "metadirective", contains the nested directive. + For early resolution of "declare variant", contains the function decl + for this alternative. For late resolution of both, contains the label + that is the branch target for this alternative. */ tree alternative; + /* Common body, used for metadirective. */ tree body; + /* If the selector is dynamic, this is the dynamic part; otherwise + NULL_TREE. Filled in during resolution. */ tree dynamic_selector; + /* A selector can match but not be resolvable due to its score not + being computable yet. */ bool resolvable_p : 1; + /* The score, if resolvable_p is true. */ + score_wide_int score; }; #define OACC_FN_ATTRIB "oacc function" @@ -190,17 +201,18 @@ extern tree find_combined_omp_for (tree *, int *, void *); extern poly_uint64 omp_max_vf (void); extern int omp_max_simt_vf (void); extern const char *omp_context_name_list_prop (tree); -extern void omp_construct_traits_to_codes (tree, int, enum tree_code *); extern tree omp_check_context_selector (location_t loc, tree ctx, bool metadirective_p); extern void omp_mark_declare_variant (location_t loc, tree variant, tree construct); -extern int omp_context_selector_matches (tree, bool, bool); -extern int omp_context_selector_set_compare (enum omp_tss_code, tree, tree); +extern int omp_context_selector_matches (tree, tree, bool); extern tree omp_get_context_selector (tree, enum omp_tss_code, enum omp_ts_code); extern tree omp_get_context_selector_list (tree, enum omp_tss_code); -extern tree omp_resolve_declare_variant (tree); +extern vec omp_declare_variant_candidates (tree, tree); +extern vec omp_metadirective_candidates (tree, tree); +extern vec +omp_get_dynamic_candidates (vec&, tree); extern vec omp_early_resolve_metadirective (tree); extern vec omp_late_resolve_metadirective (gimple *); extern tree oacc_launch_pack (unsigned code, tree device, unsigned op); diff --git a/gcc/omp-offload.cc b/gcc/omp-offload.cc index bbfc6beff87..c093440bc09 100644 --- a/gcc/omp-offload.cc +++ b/gcc/omp-offload.cc @@ -2736,16 +2736,8 @@ execute_omp_device_lower () continue; if (!gimple_call_internal_p (stmt)) { - if (calls_declare_variant_alt) - if (tree fndecl = gimple_call_fndecl (stmt)) - { - tree new_fndecl = omp_resolve_declare_variant (fndecl); - if (new_fndecl != fndecl) - { - gimple_call_set_fndecl (stmt, new_fndecl); - update_stmt (stmt); - } - } + /* FIXME: this is a leftover of obsolete code. */ + gcc_assert (!calls_declare_variant_alt); #ifdef ACCEL_COMPILER if (omp_redirect_indirect_calls && gimple_call_fndecl (stmt) == NULL_TREE) diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-12.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-12.c index 3515d9ae44e..f9150773b0e 100644 --- a/gcc/testsuite/c-c++-common/gomp/declare-variant-12.c +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-12.c @@ -29,29 +29,29 @@ void f13 (void); void f14 (void); void f15 (void); void f16 (void); -#pragma omp declare variant (f14) match (construct={teams,parallel,for}) /* 16+8+4 */ -#pragma omp declare variant (f15) match (construct={parallel},user={condition(score(19):1)}) /* 8+19 */ -#pragma omp declare variant (f16) match (implementation={atomic_default_mem_order(score(27):seq_cst)}) +#pragma omp declare variant (f14) match (construct={teams,parallel,for}) /* 1+8+16 */ +#pragma omp declare variant (f15) match (construct={parallel},user={condition(score(16):1)}) /* 8+16 */ +#pragma omp declare variant (f16) match (implementation={atomic_default_mem_order(score(24):seq_cst)}) void f17 (void); void f18 (void); void f19 (void); void f20 (void); -#pragma omp declare variant (f18) match (construct={teams,parallel,for}) /* 16+8+4 */ +#pragma omp declare variant (f18) match (construct={teams,parallel,for}) /* 1+8+6 */ #pragma omp declare variant (f19) match (construct={for},user={condition(score(25):1)}) /* 4+25 */ #pragma omp declare variant (f20) match (implementation={atomic_default_mem_order(score(28):seq_cst)}) void f21 (void); void f22 (void); void f23 (void); void f24 (void); -#pragma omp declare variant (f22) match (construct={parallel,for}) /* 2+1 */ +#pragma omp declare variant (f22) match (construct={parallel,for}) /* 8+16 */ #pragma omp declare variant (f23) match (construct={for}) /* 0 */ #pragma omp declare variant (f24) match (implementation={atomic_default_mem_order(score(2):seq_cst)}) void f25 (void); void f26 (void); void f27 (void); void f28 (void); -#pragma omp declare variant (f26) match (construct={parallel,for}) /* 2+1 */ -#pragma omp declare variant (f27) match (construct={for},user={condition(1)}) /* 4 */ +#pragma omp declare variant (f26) match (construct={parallel,for}) /* 8+16 */ +#pragma omp declare variant (f27) match (construct={for},user={condition(score(25):1)}) /* 16 + 25 */ #pragma omp declare variant (f28) match (implementation={atomic_default_mem_order(score(3):seq_cst)}) void f29 (void); diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-13.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-13.c index 68e6a897950..7d386ecea5c 100644 --- a/gcc/testsuite/c-c++-common/gomp/declare-variant-13.c +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-13.c @@ -1,5 +1,5 @@ /* { dg-do compile { target vect_simd_clones } } */ -/* { dg-additional-options "-fdump-tree-gimple" } */ +/* { dg-additional-options "-fdump-tree-ompdevlow" } */ /* { dg-additional-options "-mno-sse3" { target { i?86-*-* x86_64-*-* } } } */ int f01 (int); @@ -20,5 +20,7 @@ test1 (int x) isa has score 2^2 or 2^3. We can't decide on whether avx512f will match or not, that also depends on whether it is a declare simd clone or not and which one, but the f03 variant has a higher score anyway. */ - return f05 (x); /* { dg-final { scan-tree-dump-times "f03 \\\(x" 1 "gimple" } } */ + return f05 (x); + /* { dg-final { scan-tree-dump "f03 \\\(x" "ompdevlow" } } */ + /* { dg-final { scan-tree-dump-not "f05 \\\(x" "ompdevlow" } } */ } diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c index 05e485ef6a8..43657486c99 100644 --- a/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c @@ -38,8 +38,8 @@ void f18 (void); void f19 (void); #pragma omp declare variant (f1) match(user={condition()}) /* { dg-error "expected \[^\n\r]*expression before '\\)' token" } */ void f20 (void); -#pragma omp declare variant (f1) match(user={condition(f1)}) /* { dg-error "property must be constant integer expression" "" { target { c || c++11 } } } */ -void f21 (void); /* { dg-error "cannot appear in a constant-expression" "" { target c++98_only } .-1 } */ +#pragma omp declare variant (f1) match(user={condition(f1)}) /* { dg-error "property must be integer expression" } */ +void f21 (void); #pragma omp declare variant (f1) match(user={condition(1, 2, 3)}) /* { dg-error "expected '\\)' before ',' token" } */ void f22 (void); #pragma omp declare variant (f1) match(construct={master}) /* { dg-warning "unknown selector 'master' for context selector set 'construct'" } */ diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-arg-exprs.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-arg-exprs.c new file mode 100644 index 00000000000..38bfe928c74 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-arg-exprs.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-foffload=disable" } */ +/* { dg-additional-options "-mavx512bw -mavx512vl" { target { i?86-*-* x86_64-*-* } } } */ + +/* References to function parameters in dynamic selector expressions for + "declare variant" isn't supported yet; see PR 113904. Check to see that + a proper error is diagnosed meanwhile and GCC doesn't just wander off + into the weeds and ICE. */ + +extern int frob (int); + +void f01 (int, int); +void f02 (int, int); +void f03 (int, int); +#pragma omp declare variant (f01) match (target_device={device_num (devnum), isa("avx512f","avx512vl")}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ +#pragma omp declare variant (f02) match (implementation={vendor(score(15):gnu)}) +#pragma omp declare variant (f03) match (user={condition(score(11):frob (ok + 42))}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ +void f04 (int devnum, int ok); + +void +test1 (void) +{ + int i; + #pragma omp parallel for + for (i = 0; i < 1; i++) + f04 (17, 1); +} + + diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-dynamic-1.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-dynamic-1.c new file mode 100644 index 00000000000..b406a31eb30 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-dynamic-1.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +extern int foo_p (int); +extern int bar; + +int f01 (int); +int f02 (int); +int f03 (int); +int f04 (int); +#pragma omp declare variant (f01) match (device={isa("avx512f")}) /* 4 */ +#pragma omp declare variant (f02) match (implementation={vendor(score(3):gnu)},device={kind(cpu)}) /* 1 + 3 */ +#pragma omp declare variant (f03) match (user={condition(score(9):foo_p (bar))}) +#pragma omp declare variant (f04) match (implementation={vendor(score(6):gnu)},device={kind(host)}) /* 1 + 6 */ +int f05 (int); + + +int +test1 (int x) +{ + return f05 (x); +} + +/* { dg-final { scan-tree-dump "f03 \\\(x" "gimple" } } */ +/* { dg-final { scan-tree-dump "f04 \\\(x" "gimple" } } */ +/* { dg-final { scan-tree-dump-not "f05 \\\(x" "gimple" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-dynamic-2.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-dynamic-2.c new file mode 100644 index 00000000000..c078123e2e6 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-dynamic-2.c @@ -0,0 +1,30 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +extern int foo_p (int); +extern int bar; +extern int omp_get_default_device (void); + +int f01 (int); +int f02 (int); +int f03 (int); +int f04 (int); +#pragma omp declare variant (f01) match (target_device={device_num(omp_get_default_device()), isa("avx512f")}) /* 4 */ +#pragma omp declare variant (f02) match (user={condition(score(6):0)}) +#pragma omp declare variant (f03) match (user={condition(score(5):foo_p (bar))}) +#pragma omp declare variant (f04) match (user={condition(score(3):0)}) +int f05 (int); + +int +test1 (int x) +{ + return f05 (x); +} + +/* f01 and f03 are the dynamic selectors, the fall-through is f05. + f02 and f04 are static selectors and do not match. */ +/* { dg-final { scan-tree-dump "f01 \\\(x" "gimple" } } */ +/* { dg-final { scan-tree-dump "f03 \\\(x" "gimple" } } */ +/* { dg-final { scan-tree-dump "f05 \\\(x" "gimple" } } */ +/* { dg-final { scan-tree-dump-not "f02 \\\(x" "gimple" } } */ +/* { dg-final { scan-tree-dump-not "f04 \\\(x" "gimple" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-3.c b/gcc/testsuite/c-c++-common/gomp/metadirective-3.c index 7a2818dd710..f7258ffb5f7 100644 --- a/gcc/testsuite/c-c++-common/gomp/metadirective-3.c +++ b/gcc/testsuite/c-c++-common/gomp/metadirective-3.c @@ -1,7 +1,5 @@ /* { dg-do compile } */ -/* { dg-additional-options "-fdump-tree-original" } */ /* { dg-additional-options "-fdump-tree-gimple" } */ -/* { dg-additional-options "-fdump-tree-optimized" } */ #define N 100 @@ -17,15 +15,7 @@ void f (int x[], int y[], int z[]) z[i] = x[i] * y[i]; } -/* The metadirective should be resolved after Gimplification. */ - -/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "when \\(device = .*arch.*nvptx.*\\):" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "#pragma omp teams" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "default:" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "#pragma omp loop" 2 "original" } } */ - -/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "gimple" } } */ - -/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "optimized" } } */ +/* If offload device "nvptx" isn't supported, the front end can eliminate + that alternative and not produce a metadirective at all. Otherwise this + is supposed to be resolvable during gimplification. */ +/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/attrs-metadirective-3.C b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-3.C index 0c2bbdd2f10..d9b6d3cc0e4 100644 --- a/gcc/testsuite/g++.dg/gomp/attrs-metadirective-3.C +++ b/gcc/testsuite/g++.dg/gomp/attrs-metadirective-3.C @@ -1,7 +1,5 @@ // { dg-do compile { target c++11 } } -/* { dg-additional-options "-fdump-tree-original" } */ /* { dg-additional-options "-fdump-tree-gimple" } */ -/* { dg-additional-options "-fdump-tree-optimized" } */ #define N 100 @@ -18,14 +16,4 @@ void f (int x[], int y[], int z[]) } /* The metadirective should be resolved after Gimplification. */ - -/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "when \\(device = .*arch.*nvptx.*\\):" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "#pragma omp teams" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "default:" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "#pragma omp loop" 2 "original" } } */ - -/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "gimple" } } */ - -/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "optimized" } } */ +/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C b/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C new file mode 100644 index 00000000000..35b25de860f --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-foffload=disable" } */ +/* { dg-additional-options "-mavx512bw -mavx512vl" { target { i?86-*-* x86_64-*-* } } } */ + +/* References to function parameters in dynamic selector expressions for + "declare variant" isn't supported yet; see PR 113904. Check to see that + a proper error is diagnosed meanwhile and GCC doesn't just wander off + into the weeds and ICE. */ + +extern int frob (int); + +class junk +{ + public: + int data; + static void f01 (int, int); + static void f02 (int, int); + static void f03 (int, int); +#pragma omp declare variant (f01) match (target_device={device_num (devnum), isa("avx512f","avx512vl")}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ +#pragma omp declare variant (f02) match (implementation={vendor(score(15):gnu)}) +#pragma omp declare variant (f03) match (user={condition(score(11):frob (ok + 42))}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ + static void f04 (int devnum, int ok); +}; + +void +test1 (void) +{ + int i; + #pragma omp parallel for + for (i = 0; i < 1; i++) + junk::f04 (17, 1); +} diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C b/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C new file mode 100644 index 00000000000..b30243f70c6 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C @@ -0,0 +1,37 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-foffload=disable" } */ +/* { dg-additional-options "-mavx512bw -mavx512vl" { target { i?86-*-* x86_64-*-* } } } */ + +/* References to function parameters in dynamic selector expressions for + "declare variant" isn't supported yet; see PR 113904. Check to see that + a proper error is diagnosed meanwhile and GCC doesn't just wander off + into the weeds and ICE. */ + +extern int frob (int); +extern int frobmore (class junk *); + +class junk +{ + public: + int data; + void f01 (int, int); + void f02 (int, int); + void f03 (int, int); + void f04 (int, int); + void f05 (int, int); +#pragma omp declare variant (f01) match (target_device={device_num (devnum), isa("avx512f","avx512vl")}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ +#pragma omp declare variant (f02) match (implementation={vendor(score(15):gnu)}) +#pragma omp declare variant (f03) match (user={condition(score(11):frob (ok + 42))}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ +#pragma omp declare variant (f04) match (user={condition(score(11):data)}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ +#pragma omp declare variant (f05) match (user={condition(score(11):frobmore (this))}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ + void f06 (int devnum, int ok); +}; + +void +test1 (junk *j) +{ + int i; + #pragma omp parallel for + for (i = 0; i < 1; i++) + j->f06 (17, 1); +} diff --git a/gcc/testsuite/gfortran.dg/gomp/declare-variant-12.f90 b/gcc/testsuite/gfortran.dg/gomp/declare-variant-12.f90 index f1b4a2280ec..dd8d7c24d00 100644 --- a/gcc/testsuite/gfortran.dg/gomp/declare-variant-12.f90 +++ b/gcc/testsuite/gfortran.dg/gomp/declare-variant-12.f90 @@ -64,9 +64,9 @@ contains end subroutine subroutine f17 () - !$omp declare variant (f14) match (construct={teams,parallel,do}) ! 16+8+4 - !$omp declare variant (f15) match (construct={parallel},user={condition(score(19):.true.)}) ! 8+19 - !$omp declare variant (f16) match (implementation={atomic_default_mem_order(score(27):seq_cst)}) + !$omp declare variant (f14) match (construct={teams,parallel,do}) ! 1+8+16 + !$omp declare variant (f15) match (construct={parallel},user={condition(score(16):.true.)}) ! 8+16 + !$omp declare variant (f16) match (implementation={atomic_default_mem_order(score(24):seq_cst)}) end subroutine subroutine f18 () @@ -79,7 +79,7 @@ contains end subroutine subroutine f21 () - !$omp declare variant (f18) match (construct={teams,parallel,do}) ! 16+8+4 + !$omp declare variant (f18) match (construct={teams,parallel,do}) ! 1+8+16 !$omp declare variant (f19) match (construct={do},user={condition(score(25):.true.)}) ! 4+25 !$omp declare variant (f20) match (implementation={atomic_default_mem_order(score(28):seq_cst)}) end subroutine @@ -94,7 +94,7 @@ contains end subroutine subroutine f25 () - !$omp declare variant (f22) match (construct={parallel,do}) ! 2+1 + !$omp declare variant (f22) match (construct={parallel,do}) ! 8+16 !$omp declare variant (f23) match (construct={do}) ! 0 !$omp declare variant (f24) match (implementation={atomic_default_mem_order(score(2):seq_cst)}) end subroutine @@ -109,8 +109,8 @@ contains end subroutine subroutine f29 () - !$omp declare variant (f26) match (construct={parallel,do}) ! 2+1 - !$omp declare variant (f27) match (construct={do},user={condition(.true.)}) ! 4 + !$omp declare variant (f26) match (construct={parallel,do}) ! 8+16 + !$omp declare variant (f27) match (construct={do},user={condition(score(25):.true.)}) ! 16+25 !$omp declare variant (f28) match (implementation={atomic_default_mem_order(score(3):seq_cst)}) end subroutine diff --git a/gcc/testsuite/gfortran.dg/gomp/declare-variant-13.f90 b/gcc/testsuite/gfortran.dg/gomp/declare-variant-13.f90 index 97484a63d0b..870aeb2dad9 100644 --- a/gcc/testsuite/gfortran.dg/gomp/declare-variant-13.f90 +++ b/gcc/testsuite/gfortran.dg/gomp/declare-variant-13.f90 @@ -1,28 +1,27 @@ ! { dg-do compile { target vect_simd_clones } } -! { dg-additional-options "-fdump-tree-gimple" } +! { dg-additional-options "-O0 -fdump-tree-ompdevlow" } ! { dg-additional-options "-mno-sse3" { target { i?86-*-* x86_64-*-* } } } -program main - implicit none +module foo contains integer function f01 (x) integer, intent(in) :: x - f01 = x + f01 = x + 1 end function integer function f02 (x) integer, intent(in) :: x - f02 = x + f02 = x + 2 end function integer function f03 (x) integer, intent(in) :: x - f03 = x + f03 = x + 3 end function integer function f04 (x) integer, intent(in) :: x - f04 = x + f04 = x + 4 end function integer function f05 (x) @@ -32,9 +31,17 @@ contains !$omp declare variant (f02) match (implementation={vendor(score(3):gnu)},device={kind(cpu)}) ! (1 or 2) + 3 !$omp declare variant (f03) match (user={condition(score(9):.true.)}) !$omp declare variant (f04) match (implementation={vendor(score(6):gnu)},device={kind(host)}) ! (1 or 2) + 6 - f05 = x + f05 = x + 5 end function +end module +program main + use :: foo + implicit none + + if (test1 (42) /= 42 + 3) stop 100 + +contains integer function test1 (x) !$omp declare simd integer, intent(in) :: x @@ -43,6 +50,9 @@ contains ! isa has score 2^2 or 2^3. We can't decide on whether avx512f will match or ! not, that also depends on whether it is a declare simd clone or not and which ! one, but the f03 variant has a higher score anyway. */ - test1 = f05 (x) ! { dg-final { scan-tree-dump-times "f03 \\\(x" 1 "gimple" } } + test1 = f05 (x) + ! { dg-final { scan-tree-dump "f03 \\\(x" "ompdevlow" } } + ! { dg-final { scan-tree-dump-not "f05 \\\(x" "ompdevlow" } } end function + end program diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-1.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-1.f90 index c5b3946341d..1de5bc8a3a4 100644 --- a/gcc/testsuite/gfortran.dg/gomp/metadirective-1.f90 +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-1.f90 @@ -44,8 +44,26 @@ program main do i = 1, N c(i) = a(i) * b(i) end do - - !$omp begin metadirective & + + !$omp metadirective & + !$omp& default (teams loop) & + !$omp& when (device={arch("nvptx")} parallel loop) ! { dg-error "expected .:." } + do i = 1, N + c(i) = a(i) * b(i) + end do + + ! Test improperly nested metadirectives - even though the second + ! metadirective resolves to 'omp nothing', that is not the same as there + ! being literally nothing there. + !$omp metadirective & + !$omp& when (implementation={vendor("gnu")}: parallel do) + !$omp metadirective & + !$omp& when (implementation={vendor("cray")}: parallel do) ! { dg-error "Unexpected !.OMP METADIRECTIVE statement" } + do i = 1, N + c(i) = a(i) * b(i) + end do + +!$omp begin metadirective & !$omp& when (device={arch("nvptx")}: parallel do) & !$omp& default (barrier) ! { dg-error "variant directive used in OMP BEGIN METADIRECTIVE at .1. must have a corresponding end directive" } do i = 1, N diff --git a/gcc/testsuite/gfortran.dg/gomp/metadirective-3.f90 b/gcc/testsuite/gfortran.dg/gomp/metadirective-3.f90 index eca389a7842..5f5235cc887 100644 --- a/gcc/testsuite/gfortran.dg/gomp/metadirective-3.f90 +++ b/gcc/testsuite/gfortran.dg/gomp/metadirective-3.f90 @@ -1,7 +1,5 @@ ! { dg-do compile } -! { dg-additional-options "-fdump-tree-original" } ! { dg-additional-options "-fdump-tree-gimple" } -! { dg-additional-options "-fdump-tree-optimized" } module test integer, parameter :: N = 100 @@ -20,15 +18,7 @@ contains end subroutine end module -! The metadirective should be resolved after Gimplification. - -! { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "original" } } -! { dg-final { scan-tree-dump-times "when \\(device =.*arch.*nvptx.*\\):" 1 "original" } } -! { dg-final { scan-tree-dump-times "#pragma omp teams" 1 "original" } } -! { dg-final { scan-tree-dump-times "default:" 1 "original" } } -! { dg-final { scan-tree-dump-times "#pragma omp parallel" 1 "original" } } -! { dg-final { scan-tree-dump-times "#pragma omp loop" 2 "original" } } - -! { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "gimple" } } - -! { dg-final { scan-tree-dump-not "#pragma omp metadirective" "optimized" } } +! If offload device "nvptx" isn't supported, the front end can eliminate +! that alternative and not produce a metadirective at all. Otherwise this +! is supposed to be resolvable during gimplification. +! { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc index c34d2ce1592..0a3ad616d84 100644 --- a/gcc/tree-inline.cc +++ b/gcc/tree-inline.cc @@ -1678,6 +1678,7 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id) gimple *first_variant = NULL; gimple **prev_next = &first_variant; gimple_seq variant_seq = gimple_omp_variants (stmt); + tree context = gimple_omp_metadirective_context (stmt); for (gimple_stmt_iterator gsi = gsi_start (variant_seq); !gsi_end_p (gsi); gsi_next (&gsi)) { @@ -1689,6 +1690,7 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id) prev_next = &new_variant->next; } gimple_omp_metadirective_set_variants (copy, first_variant); + gimple_omp_metadirective_set_context (copy, context); } memset (&wi, 0, sizeof (wi)); From patchwork Sat May 4 21:21:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 1931427 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=ua5emd/y; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX10j2GJmz1ydV for ; Sun, 5 May 2024 07:25:09 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 82A9E3844036 for ; Sat, 4 May 2024 21:25:07 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-io1-xd29.google.com (mail-io1-xd29.google.com [IPv6:2607:f8b0:4864:20::d29]) by sourceware.org (Postfix) with ESMTPS id 704D23844764 for ; Sat, 4 May 2024 21:22:23 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 704D23844764 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 704D23844764 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::d29 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857754; cv=none; b=QCyTa+wCP2cdc68sUHVSQqGe+CBB6+XFbGD77Vy4HpvCW3ms7ydtxJOxOTjMOd7WpfQCLwvSMlwP0Uy/nV3nC4M7mxU5zXSI2aoZUw/C1A4pblaEjuV2a4b6Q1U9ftOZ8bzbSTOjloC0NmNNamN14r4x5owQkF+otRFAZTb+1kQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857754; c=relaxed/simple; bh=VAI6qjd3gX6jD7IX2paDIVbCl6rqCMnptgJMKwyJfvU=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=SbPfB7/63QAwnstbrG7sFHwlu5JwhjnrTEXPLMEcQSozHwrPiLHWr51leihGOlMeSrMvNX6EgzDla9PqCY5YjhLBlaAb3fejUS6LT+PGmc4tpudRJVl8c2GoDsvZHJ7NvuXgT2YdoSbogly10GyFfJcGiLvtrY3n87cNuMGP2EE= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-io1-xd29.google.com with SMTP id ca18e2360f4ac-7d86adab5a7so42763139f.3 for ; Sat, 04 May 2024 14:22:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857742; x=1715462542; 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=/AXTiIGpKX/68DgQOEodDAbBZq7MeQn5ncnz2k2SO10=; b=ua5emd/yT18rD4R0CfsX1eyAsC7fkBXq9VjpDIKhlSa7SnPOCjQyFAlwyYhYgYnGGD UL7ZAt1/+kMVibJT91BOpjt+XH9uxIIm/z1QPCcWHYi1D/UNYa84I5oH5yaEruUZ9kWw rVMNMv6YO+lTuQySiW/NdOsrBbjQcEv7EpB7K8DxIR41Oh9qrp9D6m9ae7Og/kguu04Q tJmrdwYjRxFg0RY+XeYmI2c/BY6SkhmJgPmrOTSrQMykTtlB8vWp5vlAV1CrXBRdJH8A 9vXmK49pWhQsAbKPnTOBfBl2C3jG6Jmn9p+0EvXW9NjksjDanQcSVpDdCvbE6B3I1IpC jkcw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857742; x=1715462542; 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=/AXTiIGpKX/68DgQOEodDAbBZq7MeQn5ncnz2k2SO10=; b=jf2D08T3qwv6HaB/HYFMX0ew9saULplJ+zzo3HpYRGtC5Q3Kzdjot4xuYEoWwSNa9X K9ykjVbXcwswLt05mczdTyPZnIWy2b2O59UO5uxl8DtzGzKVMdtkA6viQ824evNz3hSi zdUkHAW6QX5pZ0oen0A8W/7Y1Hf02dRxSaHRx9+X7GpcyrP8fLc9J+dt7+RBGdxAEQzh D+KQ9HCaQJqxaP2WaAmaTlCWLeLjZEaA3yPyB6evf3v1TjhFsTiIF741I4xOYyEXG/sQ a5N8RNFY6UZ2cCisXryhJ2TLoLxbgAJiYTGCfOHB5WqymDZvTiZZT7FT1Lp1fop/zcJS vC3A== X-Gm-Message-State: AOJu0YzBvlz5WR1bqoYRxr5TAYRF09zmtxqsIr7vG0D1IhHume94aBYQ RXJz/8HIAWtlns689PDlyRmzFKJOMeUGE1WkTD/8KyDrqyttSKqHQGcUVGrSPePPLHMXN+UsCrg m X-Google-Smtp-Source: AGHT+IHG249Xd0CYiRIgwtrBGRJf7es4vUW3zhO30UehDYd/Mn9TrRlFX6aXnNrxHADdYXwJX/SjMA== X-Received: by 2002:a6b:6818:0:b0:7da:303c:9a5f with SMTP id d24-20020a6b6818000000b007da303c9a5fmr7096188ioc.20.1714857742004; Sat, 04 May 2024 14:22:22 -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.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:21 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 10/12] OpenMP: Remove dead code from declare variant reimplementation Date: Sat, 4 May 2024 15:21:50 -0600 Message-Id: <20240504212153.3561429-11-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 X-Spam-Status: No, score=-10.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, KAM_STOCKGEN, 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 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+incoming=patchwork.ozlabs.org@gcc.gnu.org After reimplementing late resolution of "declare variant" to use the same mechanisms as metadirective, the declare_variant_alt and calls_declare_variant_alt flags on struct cgraph_node are no longer used by anything. For the purposes of marking functions that need late resolution, the has_metadirectives flag has replaced calls_declare_variant_alt. Likewise struct omp_declare_variant_entry, struct omp_declare_variant_base_entry, and the hash tables used to store these structures are no longer needed, since the information needed for late resolution is now stored in the gomp_metadirective nodes. There are no functional changes in this patch, just removing dead code. gcc/ChangeLog * cgraph.cc (symbol_table::create_edge): Don't set calls_declare_variant_alt in the caller. * cgraph.h (struct cgraph_node): Remove declare_variant_alt and calls_declare_variant_alt flags. * cgraphclones.cc (cgraph_node::create_clone): Don't copy calls_declare_variant_alt bit. * ipa-free-lang-data.cc (free_lang_data_in_decl): Adjust code referencing declare_variant_alt bit. * ipa.cc (symbol_table::remove_unreachable_nodes): Likewise. * lto-cgraph.cc (lto_output_node): Remove references to deleted bits. (output_refs): Adjust code referencing declare_variant_alt bit. (input_overwrite_node): Remove references to deleted bits. (input_refs): Adjust code referencing declare_variant_alt bit. * lto-streamer-out.cc (lto_output): Likewise. * lto-streamer.h (omp_lto_output_declare_variant_alt): Delete. (omp_lto_input_declare_variant_alt): Delete. * lto/lto-partition.cc (lto_balanced_map): Adjust code referencing deleted declare_variant_alt bit. * omp-expand.cc (expand_omp_target): Use has_metadirectives bit to trigger pass_omp_device_lower instead of calls_declare_variant_alt. * omp-general.cc (struct omp_declare_variant_entry): Delete. (struct omp_declare_variant_base_entry): Delete. (struct omp_declare_variant_hasher): Delete. (omp_declare_variant_hasher::hash): Delete. (omp_declare_variant_hasher::equal): Delete. (omp_declare_variants): Delete. (omp_declare_variant_alt_hasher): Delete. (omp_declare_variant_alt_hasher::hash): Delete. (omp_declare_variant_alt_hasher::equal): Delete. (omp_declare_variant_alt): Delete. (omp_lto_output_declare_variant_alt): Delete. (omp_lto_input_declare_variant_alt): Delete. (includes): Delete unnecessary include of gt-omp-general.h. * omp-offload.cc (execute_omp_device_lower): Remove references to deleted bit. (pass_omp_device_lower::gate): Likewise. * omp-simd-clone.cc (simd_clone_create): Likewise. * passes.cc (ipa_write_summaries): Likeise. * symtab.cc (symtab_node::get_partitioning_class): Likewise. * tree-inline.cc (expand_call_inline): Likewise. (tree_function_versioning): Likewise. --- gcc/cgraph.cc | 2 - gcc/cgraph.h | 11 +- gcc/cgraphclones.cc | 1 - gcc/ipa-free-lang-data.cc | 2 +- gcc/ipa.cc | 3 - gcc/lto-cgraph.cc | 10 -- gcc/lto-streamer-out.cc | 3 +- gcc/lto-streamer.h | 6 -- gcc/lto/lto-partition.cc | 5 +- gcc/omp-expand.cc | 2 +- gcc/omp-general.cc | 218 -------------------------------------- gcc/omp-offload.cc | 8 +- gcc/omp-simd-clone.cc | 2 - gcc/passes.cc | 3 +- gcc/symtab.cc | 2 +- gcc/tree-inline.cc | 4 - 16 files changed, 10 insertions(+), 272 deletions(-) diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc index 473d8410bc9..103bc2c0332 100644 --- a/gcc/cgraph.cc +++ b/gcc/cgraph.cc @@ -931,8 +931,6 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee, caller->decl); else edge->in_polymorphic_cdtor = caller->thunk; - if (callee) - caller->calls_declare_variant_alt |= callee->declare_variant_alt; if (callee && symtab->state != LTO_STREAMING && edge->callee->comdat_local_p ()) diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 6653ce19c3e..dd210842df7 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -897,10 +897,8 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node split_part (false), indirect_call_target (false), local (false), versionable (false), can_change_signature (false), redefined_extern_inline (false), tm_may_enter_irr (false), - ipcp_clone (false), declare_variant_alt (false), - calls_declare_variant_alt (false), gc_candidate (false), - called_by_ifunc_resolver (false), - has_metadirectives (false), + ipcp_clone (false), gc_candidate (false), + called_by_ifunc_resolver (false), has_metadirectives (false), m_uid (uid), m_summary_id (-1) {} @@ -1491,11 +1489,6 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node unsigned tm_may_enter_irr : 1; /* True if this was a clone created by ipa-cp. */ unsigned ipcp_clone : 1; - /* True if this is the deferred declare variant resolution artificial - function. */ - unsigned declare_variant_alt : 1; - /* True if the function calls declare_variant_alt functions. */ - unsigned calls_declare_variant_alt : 1; /* True if the function should only be emitted if it is used. This flag is set for local SIMD clones when they are created and cleared if the vectorizer uses them. */ diff --git a/gcc/cgraphclones.cc b/gcc/cgraphclones.cc index e6312b5c0ab..2b16730c10b 100644 --- a/gcc/cgraphclones.cc +++ b/gcc/cgraphclones.cc @@ -388,7 +388,6 @@ cgraph_node::create_clone (tree new_decl, profile_count prof_count, if (!new_inlined_to) prof_count = count.combine_with_ipa_count (prof_count); new_node->count = prof_count; - new_node->calls_declare_variant_alt = this->calls_declare_variant_alt; new_node->has_metadirectives = this->has_metadirectives; /* Update IPA profile. Local profiles need no updating in original. */ diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc index 3ad203fec4c..dc9f1f58e9e 100644 --- a/gcc/ipa-free-lang-data.cc +++ b/gcc/ipa-free-lang-data.cc @@ -575,7 +575,7 @@ free_lang_data_in_decl (tree decl, class free_lang_data_d *fld) if (!(node = cgraph_node::get (decl)) || (!node->definition && !node->clones)) { - if (node && !node->declare_variant_alt) + if (node) node->release_body (); else { diff --git a/gcc/ipa.cc b/gcc/ipa.cc index c453fca5d9b..c2bf8d42407 100644 --- a/gcc/ipa.cc +++ b/gcc/ipa.cc @@ -451,9 +451,6 @@ symbol_table::remove_unreachable_nodes (FILE *file) reachable.add (body); reachable.add (e->callee); } - else if (e->callee->declare_variant_alt - && !e->callee->in_other_partition) - reachable.add (e->callee); enqueue_node (e->callee, &first, &reachable); } diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc index 5bd9916fd2c..a3dc76f219f 100644 --- a/gcc/lto-cgraph.cc +++ b/gcc/lto-cgraph.cc @@ -549,8 +549,6 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node, bp_pack_value (&bp, node->merged_extern_inline, 1); bp_pack_value (&bp, node->thunk, 1); bp_pack_value (&bp, node->parallelized_function, 1); - bp_pack_value (&bp, node->declare_variant_alt, 1); - bp_pack_value (&bp, node->calls_declare_variant_alt, 1); bp_pack_value (&bp, node->has_metadirectives, 1); /* Stream thunk info always because we use it in @@ -781,9 +779,6 @@ output_refs (lto_symtab_encoder_t encoder) for (int i = 0; node->iterate_reference (i, ref); i++) lto_output_ref (ob, ref, encoder); } - if (cgraph_node *cnode = dyn_cast (node)) - if (cnode->declare_variant_alt) - omp_lto_output_declare_variant_alt (ob, cnode, encoder); } streamer_write_uhwi_stream (ob->main_stream, 0); @@ -1251,8 +1246,6 @@ input_overwrite_node (struct lto_file_decl_data *file_data, node->merged_extern_inline = bp_unpack_value (bp, 1); node->thunk = bp_unpack_value (bp, 1); node->parallelized_function = bp_unpack_value (bp, 1); - node->declare_variant_alt = bp_unpack_value (bp, 1); - node->calls_declare_variant_alt = bp_unpack_value (bp, 1); node->has_metadirectives = bp_unpack_value (bp, 1); *has_thunk_info = bp_unpack_value (bp, 1); node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution, @@ -1663,9 +1656,6 @@ input_refs (class lto_input_block *ib, input_ref (ib, node, nodes); count--; } - if (cgraph_node *cnode = dyn_cast (node)) - if (cnode->declare_variant_alt) - omp_lto_input_declare_variant_alt (ib, cnode, nodes); } } diff --git a/gcc/lto-streamer-out.cc b/gcc/lto-streamer-out.cc index d4f728094ed..42456d34823 100644 --- a/gcc/lto-streamer-out.cc +++ b/gcc/lto-streamer-out.cc @@ -2815,8 +2815,7 @@ lto_output (void) && flag_incremental_link != INCREMENTAL_LINK_LTO) /* Thunks have no body but they may be synthetized at WPA time. */ - || DECL_ARGUMENTS (cnode->decl) - || cnode->declare_variant_alt)) + || DECL_ARGUMENTS (cnode->decl))) output_function (cnode); else if ((vnode = dyn_cast (snode)) && (DECL_INITIAL (vnode->decl) != error_mark_node diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h index e8dbba471ed..895aa27692b 100644 --- a/gcc/lto-streamer.h +++ b/gcc/lto-streamer.h @@ -934,12 +934,6 @@ bool reachable_from_this_partition_p (struct cgraph_node *, lto_symtab_encoder_t compute_ltrans_boundary (lto_symtab_encoder_t encoder); void select_what_to_stream (void); -/* In omp-general.cc. */ -void omp_lto_output_declare_variant_alt (lto_simple_output_block *, - cgraph_node *, lto_symtab_encoder_t); -void omp_lto_input_declare_variant_alt (lto_input_block *, cgraph_node *, - vec); - /* In options-save.cc. */ void cl_target_option_stream_out (struct output_block *, struct bitpack_d *, struct cl_target_option *); diff --git a/gcc/lto/lto-partition.cc b/gcc/lto/lto-partition.cc index 19f91e5d660..0985631dee0 100644 --- a/gcc/lto/lto-partition.cc +++ b/gcc/lto/lto-partition.cc @@ -593,8 +593,7 @@ lto_balanced_map (int n_lto_partitions, int max_partition_size) last_visited_node++; - gcc_assert (node->definition || node->weakref - || node->declare_variant_alt); + gcc_assert (node->definition || node->weakref); /* Compute boundary cost of callgraph edges. */ for (edge = node->callees; edge; edge = edge->next_callee) @@ -705,7 +704,7 @@ lto_balanced_map (int n_lto_partitions, int max_partition_size) int index; node = dyn_cast (ref->referring); - gcc_assert (node->definition || node->declare_variant_alt); + gcc_assert (node->definition); index = lto_symtab_encoder_lookup (partition->encoder, node); if (index != LCC_NOT_FOUND diff --git a/gcc/omp-expand.cc b/gcc/omp-expand.cc index f44ba204123..5abd2240711 100644 --- a/gcc/omp-expand.cc +++ b/gcc/omp-expand.cc @@ -10109,7 +10109,7 @@ expand_omp_target (struct omp_region *region) /* Enable pass_omp_device_lower pass. */ fn2_node = cgraph_node::get (DECL_CONTEXT (child_fn)); - fn2_node->calls_declare_variant_alt = 1; + fn2_node->has_metadirectives = 1; t = build_decl (DECL_SOURCE_LOCATION (child_fn), RESULT_DECL, NULL_TREE, void_type_node); diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc index 13cf43d272f..b7eca439ad9 100644 --- a/gcc/omp-general.cc +++ b/gcc/omp-general.cc @@ -2617,222 +2617,6 @@ omp_complete_construct_context (tree construct_context, bool *completep) return construct_context; } -/* Class describing a single variant. */ -struct GTY(()) omp_declare_variant_entry { - /* NODE of the variant. */ - cgraph_node *variant; - /* Score if not in declare simd clone. */ - score_wide_int score; - /* Score if in declare simd clone. */ - score_wide_int score_in_declare_simd_clone; - /* Context selector for the variant. */ - tree ctx; - /* True if the context selector is known to match already. */ - bool matches; -}; - -/* Class describing a function with variants. */ -struct GTY((for_user)) omp_declare_variant_base_entry { - /* NODE of the base function. */ - cgraph_node *base; - /* NODE of the artificial function created for the deferred variant - resolution. */ - cgraph_node *node; - /* Vector of the variants. */ - vec *variants; -}; - -struct omp_declare_variant_hasher - : ggc_ptr_hash { - static hashval_t hash (omp_declare_variant_base_entry *); - static bool equal (omp_declare_variant_base_entry *, - omp_declare_variant_base_entry *); -}; - -hashval_t -omp_declare_variant_hasher::hash (omp_declare_variant_base_entry *x) -{ - inchash::hash hstate; - hstate.add_int (DECL_UID (x->base->decl)); - hstate.add_int (x->variants->length ()); - omp_declare_variant_entry *variant; - unsigned int i; - FOR_EACH_VEC_SAFE_ELT (x->variants, i, variant) - { - hstate.add_int (DECL_UID (variant->variant->decl)); - hstate.add_wide_int (variant->score); - hstate.add_wide_int (variant->score_in_declare_simd_clone); - hstate.add_ptr (variant->ctx); - hstate.add_int (variant->matches); - } - return hstate.end (); -} - -bool -omp_declare_variant_hasher::equal (omp_declare_variant_base_entry *x, - omp_declare_variant_base_entry *y) -{ - if (x->base != y->base - || x->variants->length () != y->variants->length ()) - return false; - omp_declare_variant_entry *variant; - unsigned int i; - FOR_EACH_VEC_SAFE_ELT (x->variants, i, variant) - if (variant->variant != (*y->variants)[i].variant - || variant->score != (*y->variants)[i].score - || (variant->score_in_declare_simd_clone - != (*y->variants)[i].score_in_declare_simd_clone) - || variant->ctx != (*y->variants)[i].ctx - || variant->matches != (*y->variants)[i].matches) - return false; - return true; -} - -static GTY(()) hash_table *omp_declare_variants; - -struct omp_declare_variant_alt_hasher - : ggc_ptr_hash { - static hashval_t hash (omp_declare_variant_base_entry *); - static bool equal (omp_declare_variant_base_entry *, - omp_declare_variant_base_entry *); -}; - -hashval_t -omp_declare_variant_alt_hasher::hash (omp_declare_variant_base_entry *x) -{ - return DECL_UID (x->node->decl); -} - -bool -omp_declare_variant_alt_hasher::equal (omp_declare_variant_base_entry *x, - omp_declare_variant_base_entry *y) -{ - return x->node == y->node; -} - -static GTY(()) hash_table - *omp_declare_variant_alt; - -void -omp_lto_output_declare_variant_alt (lto_simple_output_block *ob, - cgraph_node *node, - lto_symtab_encoder_t encoder) -{ - gcc_assert (node->declare_variant_alt); - - omp_declare_variant_base_entry entry; - entry.base = NULL; - entry.node = node; - entry.variants = NULL; - omp_declare_variant_base_entry *entryp - = omp_declare_variant_alt->find_with_hash (&entry, DECL_UID (node->decl)); - gcc_assert (entryp); - - int nbase = lto_symtab_encoder_lookup (encoder, entryp->base); - gcc_assert (nbase != LCC_NOT_FOUND); - streamer_write_hwi_stream (ob->main_stream, nbase); - - streamer_write_hwi_stream (ob->main_stream, entryp->variants->length ()); - - unsigned int i; - omp_declare_variant_entry *varentry; - FOR_EACH_VEC_SAFE_ELT (entryp->variants, i, varentry) - { - int nvar = lto_symtab_encoder_lookup (encoder, varentry->variant); - gcc_assert (nvar != LCC_NOT_FOUND); - streamer_write_hwi_stream (ob->main_stream, nvar); - - for (score_wide_int *w = &varentry->score; ; - w = &varentry->score_in_declare_simd_clone) - { - unsigned len = w->get_len (); - streamer_write_hwi_stream (ob->main_stream, len); - const HOST_WIDE_INT *val = w->get_val (); - for (unsigned j = 0; j < len; j++) - streamer_write_hwi_stream (ob->main_stream, val[j]); - if (w == &varentry->score_in_declare_simd_clone) - break; - } - - HOST_WIDE_INT cnt = -1; - HOST_WIDE_INT i = varentry->matches ? 1 : 0; - for (tree attr = DECL_ATTRIBUTES (entryp->base->decl); - attr; attr = TREE_CHAIN (attr), i += 2) - { - attr = lookup_attribute ("omp declare variant base", attr); - if (attr == NULL_TREE) - break; - - if (varentry->ctx == TREE_VALUE (TREE_VALUE (attr))) - { - cnt = i; - break; - } - } - - gcc_assert (cnt != -1); - streamer_write_hwi_stream (ob->main_stream, cnt); - } -} - -void -omp_lto_input_declare_variant_alt (lto_input_block *ib, cgraph_node *node, - vec nodes) -{ - gcc_assert (node->declare_variant_alt); - omp_declare_variant_base_entry *entryp - = ggc_cleared_alloc (); - entryp->base = dyn_cast (nodes[streamer_read_hwi (ib)]); - entryp->node = node; - unsigned int len = streamer_read_hwi (ib); - vec_alloc (entryp->variants, len); - - for (unsigned int i = 0; i < len; i++) - { - omp_declare_variant_entry varentry; - varentry.variant - = dyn_cast (nodes[streamer_read_hwi (ib)]); - for (score_wide_int *w = &varentry.score; ; - w = &varentry.score_in_declare_simd_clone) - { - unsigned len2 = streamer_read_hwi (ib); - HOST_WIDE_INT arr[WIDE_INT_MAX_HWIS (1024)]; - gcc_assert (len2 <= WIDE_INT_MAX_HWIS (1024)); - for (unsigned int j = 0; j < len2; j++) - arr[j] = streamer_read_hwi (ib); - *w = score_wide_int::from_array (arr, len2, true); - if (w == &varentry.score_in_declare_simd_clone) - break; - } - - HOST_WIDE_INT cnt = streamer_read_hwi (ib); - HOST_WIDE_INT j = 0; - varentry.ctx = NULL_TREE; - varentry.matches = (cnt & 1) ? true : false; - cnt &= ~HOST_WIDE_INT_1; - for (tree attr = DECL_ATTRIBUTES (entryp->base->decl); - attr; attr = TREE_CHAIN (attr), j += 2) - { - attr = lookup_attribute ("omp declare variant base", attr); - if (attr == NULL_TREE) - break; - - if (cnt == j) - { - varentry.ctx = TREE_VALUE (TREE_VALUE (attr)); - break; - } - } - gcc_assert (varentry.ctx != NULL_TREE); - entryp->variants->quick_push (varentry); - } - if (omp_declare_variant_alt == NULL) - omp_declare_variant_alt - = hash_table::create_ggc (64); - *omp_declare_variant_alt->find_slot_with_hash (entryp, DECL_UID (node->decl), - INSERT) = entryp; -} - /* Comparison function for sorting routines, to sort OpenMP metadirective variants by decreasing score. */ @@ -4138,5 +3922,3 @@ debug_omp_tokenized_addr (vec &addr_tokens, fputs ("\n", stderr); } - -#include "gt-omp-general.h" diff --git a/gcc/omp-offload.cc b/gcc/omp-offload.cc index c093440bc09..613b77571e6 100644 --- a/gcc/omp-offload.cc +++ b/gcc/omp-offload.cc @@ -2718,8 +2718,6 @@ execute_omp_device_lower () bool regimplify = false; basic_block bb; gimple_stmt_iterator gsi; - bool calls_declare_variant_alt - = cgraph_node::get (cfun->decl)->calls_declare_variant_alt; auto_vec metadirective_bbs; #ifdef ACCEL_COMPILER bool omp_redirect_indirect_calls = vec_safe_length (offload_ind_funcs) > 0; @@ -2736,8 +2734,6 @@ execute_omp_device_lower () continue; if (!gimple_call_internal_p (stmt)) { - /* FIXME: this is a leftover of obsolete code. */ - gcc_assert (!calls_declare_variant_alt); #ifdef ACCEL_COMPILER if (omp_redirect_indirect_calls && gimple_call_fndecl (stmt) == NULL_TREE) @@ -2919,9 +2915,7 @@ public: #endif return (!(fun->curr_properties & PROP_gimple_lomp_dev) || (flag_openmp - && (node->calls_declare_variant_alt - || node->has_metadirectives - || offload_ind_funcs_p))); + && (node->has_metadirectives || offload_ind_funcs_p))); } unsigned int execute (function *) final override { diff --git a/gcc/omp-simd-clone.cc b/gcc/omp-simd-clone.cc index fa80b6b3bb9..52806620ed0 100644 --- a/gcc/omp-simd-clone.cc +++ b/gcc/omp-simd-clone.cc @@ -688,8 +688,6 @@ simd_clone_create (struct cgraph_node *old_node, bool force_local) the old node. */ new_node->local = old_node->local; new_node->externally_visible = old_node->externally_visible; - new_node->calls_declare_variant_alt - = old_node->calls_declare_variant_alt; new_node->has_metadirectives = old_node->has_metadirectives; } diff --git a/gcc/passes.cc b/gcc/passes.cc index d73f8ba97b6..e9d18b9d22e 100644 --- a/gcc/passes.cc +++ b/gcc/passes.cc @@ -2881,8 +2881,7 @@ ipa_write_summaries (void) { struct cgraph_node *node = order[i]; - if ((node->definition || node->declare_variant_alt) - && node->need_lto_streaming) + if (node->definition && node->need_lto_streaming) { if (gimple_has_body_p (node->decl)) lto_prepare_function_for_streaming (node); diff --git a/gcc/symtab.cc b/gcc/symtab.cc index 3b018ab3ea2..5660857fa51 100644 --- a/gcc/symtab.cc +++ b/gcc/symtab.cc @@ -2160,7 +2160,7 @@ symtab_node::get_partitioning_class (void) if (DECL_ABSTRACT_P (decl)) return SYMBOL_EXTERNAL; - if (cnode && (cnode->inlined_to || cnode->declare_variant_alt)) + if (cnode && cnode->inlined_to) return SYMBOL_DUPLICATE; /* Transparent aliases are always duplicated. */ diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc index 0a3ad616d84..8674baec5f2 100644 --- a/gcc/tree-inline.cc +++ b/gcc/tree-inline.cc @@ -5058,8 +5058,6 @@ expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id, if (src_properties != prop_mask) dst_cfun->curr_properties &= src_properties | ~prop_mask; dst_cfun->calls_eh_return |= id->src_cfun->calls_eh_return; - id->dst_node->calls_declare_variant_alt - |= id->src_node->calls_declare_variant_alt; id->dst_node->has_metadirectives |= id->src_node->has_metadirectives; gcc_assert (!id->src_cfun->after_inlining); @@ -6314,8 +6312,6 @@ tree_function_versioning (tree old_decl, tree new_decl, DECL_ARGUMENTS (new_decl) = DECL_ARGUMENTS (old_decl); initialize_cfun (new_decl, old_decl, new_entry ? new_entry->count : old_entry_block->count); - new_version_node->calls_declare_variant_alt - = old_version_node->calls_declare_variant_alt; new_version_node->has_metadirectives = old_version_node->has_metadirectives; if (DECL_STRUCT_FUNCTION (new_decl)->gimple_df) DECL_STRUCT_FUNCTION (new_decl)->gimple_df->ipa_pta From patchwork Sat May 4 21:21:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 1931428 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=2ZybkorU; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX10n6HzYz1xnT for ; Sun, 5 May 2024 07:25:13 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 17AB1384474A for ; Sat, 4 May 2024 21:25:12 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-il1-x12f.google.com (mail-il1-x12f.google.com [IPv6:2607:f8b0:4864:20::12f]) by sourceware.org (Postfix) with ESMTPS id 7055A384404C for ; Sat, 4 May 2024 21:22:25 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7055A384404C 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 7055A384404C Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::12f ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857753; cv=none; b=HHSadqHG+mVKzYJYTCwWyz6Yq+MOfQ7JN9snWLEx7CQ6UG9XYrJXVLXdGcXvkDSQGl98quNySCXKdwDSsRw5gvmQC3OdjYz1xkR412QXMAwwjVTpSsMvx6fx52k6cLLkIpe2K8+50H8ICDBIfjUoIr+nHCH6s+d+84Pdi2+9CTY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857753; c=relaxed/simple; bh=1C41AJldPVs/uhGF1F+hoJMPOwpdsmQNSpvnY0IRBzk=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=xEMrdJEAKtloJmzm+sTYBZeju4IP8q8Tsq4Lrp1wVZjjcZu+bd2DRYS65POURkv2yQjs8nNnLrjeh+7TOVa64JvHUNR5H/WBE5iTwCUyvAtjJ4PtJkQvcAKnxosy/6SA0SuZTfbVw5CS9veHG8VuDL50BTxk/VBIr/YqFvw7Tis= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-il1-x12f.google.com with SMTP id e9e14a558f8ab-36c5f81dc4bso4040905ab.0 for ; Sat, 04 May 2024 14:22:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857743; x=1715462543; 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=ShN4k7hmX+49NGPR3EnU4I6Ow25+gWb9DIJsBvN2KR4=; b=2ZybkorUTsZkq0hINX3TTR8jxD0rYMVTnhPMBYYnTt2gngZkGdBet7eB7nXPq8Qg2V Yf8MpEvp+qeM8LyOHKPn/9R1kMU6yFX1qEkA3ivoXebwQbvNVvNJczdcGxlc+EInWFIG 87RSLyf2usTr2krlCIVJ0FggjZ2LAg9u9C3Bh8TAsbALofFje0pWF2L/4RRTeIrW4azw CcW4LFpQ/8/Vp14GeynuhcP0IBTjZb9bL9Z97YhsbyxET/hkWBiUCp9RfSx08TeNke+z eEFpXHXf4uIcG/mE0eDw4DYuKb0ACEBhy3u+N4h0T+93P3OqKT7AQLeJC+yg4qt2wsgi CXbw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857743; x=1715462543; 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=ShN4k7hmX+49NGPR3EnU4I6Ow25+gWb9DIJsBvN2KR4=; b=tPTTiiGFO39VgUqEA1RtB798zXpH7WLVCY7/AoHa3Gx9xT5WI4PQeBL2UC6cCvthhu FrI//M3BPLsq+lTzH2k/lSo5xIDgtLHqo3xhsuRWcIiVvEvEZ1wvskg7Mh1iAZAF7rFV jmxMXvzK3WgVjLdHj8GMlqnMOEs4YMFRn6OBbTVyqqUFOvHRNkNXuA7YzkfZJWFrG5R3 rBwfgyB4NvrFLwBTnFZ/ELUWmEvU8fSXvci01AlCIUOFPBnuBgZZT0ClkgPM/yGAelpN EnPMp4UFx2DLY6LQr7k0qTb+ZySfAurdxyNMA6Q7ck79+IaAUGHlmJywknStIHGpQMCf POfQ== X-Gm-Message-State: AOJu0Yzb+Zz63kppAVSBkQr3UwhqsH2y/oCB/hJnIyjPCCVujSpDJoLA VN/kljQGeB3Ocubc/nKloWWn7utO79hJ2jR8Ui8v5KLgMSrkIYqrIjdZIFJaEa2JBjchSACdyfb h X-Google-Smtp-Source: AGHT+IE6iu5zh+mPAt/+lbUWbmebgZL9vsfgrts84AAgSKgIH+NkhegVZ32i/MUMMU8I1OSe9jJpTw== X-Received: by 2002:a92:cd82:0:b0:36c:504f:587e with SMTP id r2-20020a92cd82000000b0036c504f587emr8192331ilb.3.1714857743242; Sat, 04 May 2024 14:22:23 -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.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:22 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 11/12] OpenMP: Update "declare target"/OpenMP context interaction Date: Sat, 4 May 2024 15:21:51 -0600 Message-Id: <20240504212153.3561429-12-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 X-Spam-Status: No, score=-11.5 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 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+incoming=patchwork.ozlabs.org@gcc.gnu.org The code and test case previously implemented the OpenMP 5.0 spec, which said in section 2.3.1: "For functions within a declare target block, the target trait is added to the beginning of the set..." In OpenMP 5.1, this was changed to "For device routines, the target trait is added to the beginning of the set..." In OpenMP 5.2 and TR12, it says: "For procedures that are determined to be target function variants by a declare target directive..." The definition of "device routine" in OpenMP 5.1 is confusing, but certainly the intent of the later versions of the spec is clear that it doesn't just apply to functions within a begin declare target/end declare target block. The only use of the "omp declare target block" function attribute was to support the 5.0 language, so it can be removed. This patch changes the context augmentation to use the "omp declare target" attribute instead. gcc/c-family/ChangeLog * c-attribs.cc (c_common_gnu_attributes): Delete "omp declare target block". gcc/c/ChangeLog * c-decl.cc (c_decl_attributes): Don't add "omp declare target block". gcc/cp/decl2.cc * decl2.cc (cplus_decl_attributes): Don't add "omp declare target block". gcc/ChangeLog * omp-general.cc (omp_complete_construct_context): Check "omp declare target" attribute, not "omp declare target block". gcc/testsuite/ChangeLog * c-c++-common/gomp/declare-target-indirect-2.c : Adjust expected output for removal of "omp declare target block". * c-c++-common/gomp/declare-variant-8.c: Likewise, the variant call to f20 is now resolved differently. * c-c++-common/gomp/reverse-offload-1.c: Adjust expected output. * gfortran.dg/gomp/declare-variant-8.f90: Likewise, both f18 and f20 now resolve to the variant. Delete obsolete comments. --- gcc/c-family/c-attribs.cc | 2 -- gcc/c/c-decl.cc | 8 ++------ gcc/cp/decl2.cc | 9 ++------- gcc/omp-general.cc | 2 +- .../c-c++-common/gomp/declare-target-indirect-2.c | 10 +++++----- gcc/testsuite/c-c++-common/gomp/declare-variant-8.c | 4 ++-- gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c | 2 +- gcc/testsuite/gfortran.dg/gomp/declare-variant-8.f90 | 12 ++---------- 8 files changed, 15 insertions(+), 34 deletions(-) diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index 04e39b41bdf..582d99ada1b 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -570,8 +570,6 @@ const struct attribute_spec c_common_gnu_attributes[] = handle_omp_declare_target_attribute, NULL }, { "omp declare target nohost", 0, 0, true, false, false, false, handle_omp_declare_target_attribute, NULL }, - { "omp declare target block", 0, 0, true, false, false, false, - handle_omp_declare_target_attribute, NULL }, { "non overlapping", 0, 0, true, false, false, false, handle_non_overlapping_attribute, NULL }, { "alloc_align", 1, 1, false, true, true, false, diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 52af8f32998..4ab7cd86030 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -5414,12 +5414,8 @@ c_decl_attributes (tree *node, tree attributes, int flags) attributes = tree_cons (get_identifier ("omp declare target implicit"), NULL_TREE, attributes); else - { - attributes = tree_cons (get_identifier ("omp declare target"), - NULL_TREE, attributes); - attributes = tree_cons (get_identifier ("omp declare target block"), - NULL_TREE, attributes); - } + attributes = tree_cons (get_identifier ("omp declare target"), + NULL_TREE, attributes); if (TREE_CODE (*node) == FUNCTION_DECL) { int device_type diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 806a2a4bc69..028105a5b26 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -1777,13 +1777,8 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags) = tree_cons (get_identifier ("omp declare target implicit"), NULL_TREE, attributes); else - { - attributes = tree_cons (get_identifier ("omp declare target"), - NULL_TREE, attributes); - attributes - = tree_cons (get_identifier ("omp declare target block"), - NULL_TREE, attributes); - } + attributes = tree_cons (get_identifier ("omp declare target"), + NULL_TREE, attributes); if (TREE_CODE (*decl) == FUNCTION_DECL) { cp_omp_declare_target_attr &last diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc index b7eca439ad9..986f9e4f558 100644 --- a/gcc/omp-general.cc +++ b/gcc/omp-general.cc @@ -2609,7 +2609,7 @@ omp_complete_construct_context (tree construct_context, bool *completep) } /* Add target trait when in a target variant. */ - if (lookup_attribute ("omp declare target block", attributes)) + if (lookup_attribute ("omp declare target", attributes)) construct_context = make_trait_selector (OMP_TRAIT_CONSTRUCT_TARGET, NULL_TREE, NULL_TREE, construct_context); diff --git a/gcc/testsuite/c-c++-common/gomp/declare-target-indirect-2.c b/gcc/testsuite/c-c++-common/gomp/declare-target-indirect-2.c index 6ba278b3ef0..75a205feb95 100644 --- a/gcc/testsuite/c-c++-common/gomp/declare-target-indirect-2.c +++ b/gcc/testsuite/c-c++-common/gomp/declare-target-indirect-2.c @@ -4,12 +4,12 @@ #pragma omp begin declare target indirect void fn1 (void) { } #pragma omp end declare target -/* { dg-final { scan-tree-dump "__attribute__\\\(\\\(omp declare target, omp declare target block, omp declare target indirect\\\)\\\)\\\nvoid fn1" "gimple" } } */ +/* { dg-final { scan-tree-dump "__attribute__\\\(\\\(omp declare target, omp declare target indirect\\\)\\\)\\\nvoid fn1" "gimple" } } */ #pragma omp begin declare target indirect (0) void fn2 (void) { } #pragma omp end declare target -/* { dg-final { scan-tree-dump "__attribute__\\\(\\\(omp declare target, omp declare target block\\\)\\\)\\\nvoid fn2" "gimple" } } */ +/* { dg-final { scan-tree-dump "__attribute__\\\(\\\(omp declare target\\\)\\\)\\\nvoid fn2" "gimple" } } */ void fn3 (void) { } #pragma omp declare target indirect to (fn3) @@ -27,6 +27,6 @@ void fn4 (void) { } #pragma omp declare target indirect enter(baz) #pragma omp end declare target #pragma omp end declare target -/* { dg-final { scan-tree-dump "__attribute__\\\(\\\(omp declare target, omp declare target block, omp declare target indirect\\\)\\\)\\\nint foo" "gimple" } } */ -/* { dg-final { scan-tree-dump "__attribute__\\\(\\\(omp declare target, omp declare target block\\\)\\\)\\\nint bar" "gimple" } } */ -/* { dg-final { scan-tree-dump "__attribute__\\\(\\\(omp declare target indirect, omp declare target, omp declare target block\\\)\\\)\\\nint baz" "gimple" } } */ +/* { dg-final { scan-tree-dump "__attribute__\\\(\\\(omp declare target, omp declare target indirect\\\)\\\)\\\nint foo" "gimple" } } */ +/* { dg-final { scan-tree-dump "__attribute__\\\(\\\(omp declare target\\\)\\\)\\\nint bar" "gimple" } } */ +/* { dg-final { scan-tree-dump "__attribute__\\\(\\\(omp declare target indirect, omp declare target\\\)\\\)\\\nint baz" "gimple" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-8.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-8.c index a7a3ba41b97..9cd706e896f 100644 --- a/gcc/testsuite/c-c++-common/gomp/declare-variant-8.c +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-8.c @@ -1,4 +1,4 @@ -/* { dg-do compile { target c } } */ +/* { dg-do compile } */ /* { dg-additional-options "-fdump-tree-gimple" } */ void f01 (void); @@ -102,7 +102,7 @@ void test3 (void) { #pragma omp parallel - f20 (); /* { dg-final { scan-tree-dump-times "f20 \\\(\\\);" 1 "gimple" } } */ + f20 (); /* { dg-final { scan-tree-dump-times "f19 \\\(\\\);" 1 "gimple" } } */ } void diff --git a/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c b/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c index 9a3fa5230f8..6abaddcd5f4 100644 --- a/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c +++ b/gcc/testsuite/c-c++-common/gomp/reverse-offload-1.c @@ -4,7 +4,7 @@ /* { dg-final { scan-tree-dump-times "__attribute__\\(\\(omp declare target\\)\\)\[\n\r\]*int called_in_target1" 1 "omplower" } } */ /* { dg-final { scan-tree-dump-times "__attribute__\\(\\(omp declare target\\)\\)\[\n\r\]*int called_in_target2" 1 "omplower" } } */ -/* { dg-final { scan-tree-dump-times "__attribute__\\(\\(omp declare target, omp declare target block\\)\\)\[\n\r\]*void tg_fn" 1 "omplower" } } */ +/* { dg-final { scan-tree-dump-times "__attribute__\\(\\(omp declare target\\)\\)\[\n\r\]*void tg_fn" 1 "omplower" } } */ /* { dg-prune-output "'reverse_offload' clause on 'requires' directive not supported yet" } */ diff --git a/gcc/testsuite/gfortran.dg/gomp/declare-variant-8.f90 b/gcc/testsuite/gfortran.dg/gomp/declare-variant-8.f90 index d69e552eeb7..e3935768bc4 100644 --- a/gcc/testsuite/gfortran.dg/gomp/declare-variant-8.f90 +++ b/gcc/testsuite/gfortran.dg/gomp/declare-variant-8.f90 @@ -167,23 +167,15 @@ contains end subroutine subroutine test2 () - ! OpenMP 5.0 specifies that the 'target' trait should be added for - ! functions within a declare target block, but Fortran does not have - ! the notion of a declare target _block_, so the variant is not used here. - ! This may change in later versions of OpenMP. - !$omp declare target !$omp parallel - call f18 () ! { dg-final { scan-tree-dump-times "f18 \\\(\\\);" 1 "gimple" } } + call f18 () ! { dg-final { scan-tree-dump-times "f17 \\\(\\\);" 1 "gimple" } } !$omp end parallel end subroutine subroutine test3 () - ! In the C version, this test was used to check that the - ! 'declare target to' form of the directive did not result in the variant - ! being used. !$omp parallel - call f20 () ! { dg-final { scan-tree-dump-times "f20 \\\(\\\);" 1 "gimple" } } + call f20 () ! { dg-final { scan-tree-dump-times "f19 \\\(\\\);" 1 "gimple" } } !$omp end parallel end subroutine From patchwork Sat May 4 21:21: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: 1931422 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=eSKLKY7X; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VX0yj6WKnz1ybC for ; Sun, 5 May 2024 07:23:25 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DD8C4386F420 for ; Sat, 4 May 2024 21:23:23 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-io1-xd36.google.com (mail-io1-xd36.google.com [IPv6:2607:f8b0:4864:20::d36]) by sourceware.org (Postfix) with ESMTPS id 570913844036 for ; Sat, 4 May 2024 21:22:26 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 570913844036 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 570913844036 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::d36 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857754; cv=none; b=IV2edHrAHPupXXvr8HcRviSjfRtefipcF8SmIaRxhcAP+KBfrB7P4WVL3YMP7DV+pZXApMOu2G0za15otUDXDlxch6RhODjiGROb7JWhepTlOziaPF5OtLde2Uxk2vf23a4CUy3DdkPmDfLuphA/Z/A+ZdTiaxMkURa1MPwsLFM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714857754; c=relaxed/simple; bh=O+2RpxoY6SCT9rLpLqfE7Tq2mcTzunjvyM6JScAbkU8=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=rzx9+eT4GpCGKihjP5yl/BtnAYWpsQ/jeMQpUko+5rqD0UJmiIlEZaq7/mYU+7ra+wcm3jc8zNJJKaFUwi37rzr8ItIV9iYTHGtuHIn9/RbpFr/KqJMvjcn0qCjajYmBgJ3hOv4y8HwEw3oZ85XR65dUctyQFRsHsIH+8d+HKg4= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-io1-xd36.google.com with SMTP id ca18e2360f4ac-7dee37ea263so52578139f.2 for ; Sat, 04 May 2024 14:22:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1714857744; x=1715462544; 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=mXko7STqZ23PEHoKwb9wyjyBW8iYaby0gSgCcLREK4E=; b=eSKLKY7XFBaBo44/6tj7JvrGh7ju2OEXJNwnxh6LVWgxIC615MJtOBZzVOPX76wufy 49FkFaeHgYhtJm+WcBv4vVSMg90/d66iWRh/msz0xCvx/47W4QNnLlU6j+Jsu6tIBGQC yYqw/2emdb4mH9jGDPUyx/1xY4bLHBoUfPp81amJDalVCctPiXTXldi5Dby47Jocjv4l oea4/MjDltUMzh099itSIm7e4oc+04JEDw4+mng1L1Q4kMpiQGRl7bNofmy6ZHpiAcY9 7vGYfGr/XG5S5eSLYSO1vzVPSYzWZQb1s+NWyo7F4wyRLDXn3VIsUNLvVl+4kWTrphsT vvIA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714857744; x=1715462544; 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=mXko7STqZ23PEHoKwb9wyjyBW8iYaby0gSgCcLREK4E=; b=oEpvBgRWQA5azWeb5xyZ81YW0V3A8XOSlvQbdDihHe8e9RJQto+2OEiwhFu2IezLC/ mRZCNzf5EbkoouKEntT4Mi8We/iYrNitkI65ukp/naxvUdX2Lsnn0NHGJM7fWcHHf/GC UILfCquKalhLnbAUGdl3iDgeqKFoer960Tb7YUmmpJMFUFEMrYv0e8ppdu7nhlKXokQS DzJs0g1XLhLduU8VvZhg+VZqfRi7c4EL7S+YvgmHfN0KOCFZfJUQYGRiUQFl87jUpl+W dvMtpEse44g+XhM5zEgfRouy5rc2GBfhaiDgou+HambxPjiP/beJUnFfUPKICQ+U4e94 6yYg== X-Gm-Message-State: AOJu0YyJ4Y2OueHrwfgDTs1NugTZc2SjTkDlUY9P80rjFDgOb8NNQyar LeSSN4UumrzFu29bhGZxtpHyMP/25voik210/9OSqX2HczrbtT8HTfdZ1Vhg07+jha/puQ4vdcW e X-Google-Smtp-Source: AGHT+IGj8NjyPuH/ueq7tuhiXcPwMFnmsyKVEsXi9DdPNHw4vfCeIGM8gS9XQ1JPbAJxb3FdjqjeTA== X-Received: by 2002:a5d:894f:0:b0:7de:af76:b6c6 with SMTP id b15-20020a5d894f000000b007deaf76b6c6mr7118559iot.2.1714857744610; Sat, 04 May 2024 14:22:24 -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.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 04 May 2024 14:22:24 -0700 (PDT) From: Sandra Loosemore To: gcc-patches@gcc.gnu.org Cc: jakub@redhat.com, tburnus@baylibre.com Subject: [PATCH 12/12] OpenMP: Update documentation of metadirective implementation status. Date: Sat, 4 May 2024 15:21:52 -0600 Message-Id: <20240504212153.3561429-13-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 X-Spam-Status: No, score=-11.5 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 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+incoming=patchwork.ozlabs.org@gcc.gnu.org libgomp/ChangeLog * libgomp.texi (OpenMP 5.0): Mark metadirective and declare variant as implemented. (OpenMP 5.1): Mark target_device as supported. Add changed interaction between declare target and OpenMP context and dynamic selector support. (OpenMP 5.2): Mark otherwise clause as supported, note that default is also still accepted. --- libgomp/libgomp.texi | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi index 43048da4d6e..af7af63c504 100644 --- a/libgomp/libgomp.texi +++ b/libgomp/libgomp.texi @@ -192,9 +192,8 @@ The OpenMP 4.5 specification is fully supported. @item Array shaping @tab N @tab @item Array sections with non-unit strides in C and C++ @tab N @tab @item Iterators @tab Y @tab -@item @code{metadirective} directive @tab N @tab -@item @code{declare variant} directive - @tab P @tab @emph{simd} traits not handled correctly +@item @code{metadirective} directive @tab Y @tab +@item @code{declare variant} directive @tab Y @tab @item @var{target-offload-var} ICV and @code{OMP_TARGET_OFFLOAD} env variable @tab Y @tab @item Nested-parallel changes to @var{max-active-levels-var} ICV @tab Y @tab @@ -289,8 +288,8 @@ The OpenMP 4.5 specification is fully supported. @headitem Description @tab Status @tab Comments @item OpenMP directive as C++ attribute specifiers @tab Y @tab @item @code{omp_all_memory} reserved locator @tab Y @tab -@item @emph{target_device trait} in OpenMP Context @tab N @tab -@item @code{target_device} selector set in context selectors @tab N @tab +@item @emph{target_device trait} in OpenMP Context @tab Y +@item @code{target_device} selector set in context selectors @tab Y @tab @item C/C++'s @code{declare variant} directive: elision support of preprocessed code @tab N @tab @item @code{declare variant}: new clauses @code{adjust_args} and @@ -366,6 +365,12 @@ to address of matching mapped list item per 5.1, Sect. 2.21.7.2 @tab N @tab @item @code{device_type(nohost)}/@code{device_type(host)} for variables @tab N @tab @item @code{present} modifier to the @code{map}, @code{to} and @code{from} clauses @tab Y @tab +@item Changed interaction between @code{declare target} and OpenMP context + @tab Y @tab +@item Dynamic selector support in @code{metadirective} @tab Y @tab +@item Dynamic selector support in @code{declare variant} @tab P + @tab Fortran rejects non-constant expressions in dynamic selectors; + C/C++ reject expressions using argument variables. @end multitable @@ -413,8 +418,10 @@ to address of matching mapped list item per 5.1, Sect. 2.21.7.2 @tab N @tab @item Deprecation of traits array following the allocator_handle expression in @code{uses_allocators} @tab N @tab @item New @code{otherwise} clause as alias for @code{default} on metadirectives - @tab N @tab -@item Deprecation of @code{default} clause on metadirectives @tab N @tab + @tab Y @tab +@item Deprecation of @code{default} clause on metadirectives @tab N + @tab Both @code{otherwise} and @code{default} are accepted + without diagnostics. @item Deprecation of delimited form of @code{declare target} @tab N @tab @item Reproducible semantics changed for @code{order(concurrent)} @tab N @tab @item @code{allocate} and @code{firstprivate} clauses on @code{scope}