Hi Sandra,

thanks for your patch. (Disclaimer: I have not finished reading through your patch.)

Some upfront generic remarks:

[* When first compiling it (incremental build), I did run into the issue that OMP_METADIRECTIVE_CHECK wasn't declared. Thus, there seems to be a dependency issue causing that tree-check.h might generated after code that includes tree.h is processed. (Unrelated to your patch itself, but for completeness …)]

* Not required right now, but eventually we need to check whether https://gcc.gnu.org/PR112779 is fully fixed by this patch set or whether follow-up work is required (and if so which). There is also PR107067 for a Fortran ICE.

* There are some not-implemented/FIXME comments in the patches for missing features. I think we should ensure that those won't get forgotten, e.g. by filing PRs for those. – For declare variant, some PRs might already exist.

Can you eventually take care of the last two items?

(For the last item: e.g. 'target_device' for declare_variant, for which 'sorry' already existed.)

* * *

I might have asked the following question before – and you might have answered it already:

Sandra Loosemore wrote:

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.

I have to admit that I do not understand the part:

+      else if (set == OMP_TRAIT_SET_TARGET_DEVICE)
+/* The target_device set is dynamic, so treat it as always
+   resolvable.  */
+continue;
+

The current code has 3 states:

* 0 - if a trait is false; this directly returns as it cannot be fixed later

* 1 - if the all traits are known to match (initial value)

* -1 - if one trait cannot be evaluated, either because it is too early (e.g. during parsing) or because it is a dynamic context selector.

Thus, I had expected:

(a) ret = -1 as default in this case (not known)

(b) for cases where it is known, a 'return 0' / not-setting -1. In particular:

* n == const → device_num(n) – false if '< -1' and, for '!ENABLE_OFFLOADING || offload_targets == NULL' either false for n > 0 or otherwise false.

* Checks similar to OMP_TRAIT_DEVICE_{KIND,ARCH,ISA}, i.e. kind(any) → true, kind(fpga) → false, arch(something_unknown) → false if not true for any device. With '!ENABLE_OFFLOADING || offload_targets == NULL', the kind_arch_isa check can be done as for the host.

* * *

Have I missed something and is it sensible to return 1 instead of -1 here?

* * *


@@ -1804,6 +1834,12 @@ omp_context_selector_matches (tree ctx)
----<cut>----
           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;
+
                     if (integer_zerop (OMP_TP_VALUE (p)))
                       return 0;
                     if (integer_nonzerop (OMP_TP_VALUE (p)))
                       break;
                     ret = -1;
                   }

----</cut>----

* Comment wording: Please change to imply >= 5.1 not == 5.0 * Comment: I don't see why the non-const only applies to metadirectives; the OpenMP >= 5.1 seems to imply that it is also valid for declare variant. Thus, I would change the wording. * The current code seems to already handle non-const values as expected. ... except that it changes "res" to -1, while the idea seems to be not to modify 'ret' in this case for metadirectives. (Why? Same question as above).
* * *

Quotes from the specifications regarding the expressions:

The current spec has:

"Restrictions to context selectors are as follows:" …

"A variable or procedure that is referenced in an expression that appears in a context selector must be visible at the location of the directive on which the context selector appears unless the directive is a declare_variant directive and the variable is an argument of the
associated base function."

5.1 wording is the following (approx. same except for argument bit):

"All variables that are referenced in an expression that appears in
the context selector of a match clause must be accessible at a call site to the base function
according to the base language rules."

5.0 had (e.g. for C): "The condition(boolean-expr) selector defines a constant expression that must evaluate to true for the selector to be true."

* * *

+                     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)))
@@ -2202,9 +2238,114 @@ 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 <fixed_wide_int_storage <1024> > 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);
+
+      /* The user condition is not dynamic if it is constant.  */
+      if (!tree_fits_shwi_p (OMP_TP_VALUE (expr_list)))
+       expr = OMP_TP_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)
+       /* Generate
+            devnum = (num == -1) ? GOMP_DEVICE_HOST_FALLBACK : num);
+          to remap -1 for GOMP_* functions.  */
+       {
+         tree num = OMP_TP_VALUE (OMP_TS_PROPERTIES (device_num_sel));
+         location_t loc = EXPR_LOCATION (num);
+         tree icv = build_int_cst (integer_type_node, -1);
+         tree compare = fold_build2_loc (loc, EQ_EXPR, unsigned_type_node,
+                                         num, icv);
+         tree fallback = build_int_cst (integer_type_node,
+                                        GOMP_DEVICE_HOST_FALLBACK);
+         device_num = fold_build3_loc (loc, COND_EXPR, integer_type_node,
+                                       compare, fallback, save_expr (num));
+       }
+      else
+       device_num = build_int_cst (integer_type_node, GOMP_DEVICE_ICV);
+      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
@@ -2216,12 +2357,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))
@@ -2406,7 +2556,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);
@@ -2510,7 +2660,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.  */
@@ -2875,6 +3026,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<struct omp_variant>
+omp_get_dynamic_candidates (vec <struct omp_variant> &all_variants,
+                           bool delay_p)
+{
+  auto_vec <struct omp_variant> 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<struct omp_variant>
+omp_early_resolve_metadirective (tree metadirective)
+{
+  auto_vec <struct omp_variant> 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 37f0548befd..8fe25b3108f 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 <fixed_wide_int_storage <1024> > 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<struct omp_variant> 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 4bb946bb0e8..59db597c10d 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -1522,7 +1522,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)
  {
@@ -4048,6 +4048,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, "otherwise:");
+           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 c5089f82cf6..d9d4f82909f 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 85ab182c6f5..155a508f0d3 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -1348,6 +1348,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 28e8e71b036..dfd738353b8 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1600,6 +1600,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)
-- 2.25.1

Reply via email to