This patch adds basic support for three new tree node types that will
be used in subsequent patches to support OpenMP metadirectives and
dynamic selectors.

OMP_METADIRECTIVE is the internal representation of parsed OpenMP
metadirective constructs.  It's produced by the front ends and is expanded
during gimplification.

OMP_NEXT_VARIANT is used as a "magic cookie" for late resolution of
variant constructs that cannot be fully resolved during
gimplification, used to set the controlling variable of a switch
statement that branches to the next alternative once the candidate
list can be filtered and sorted.  These nodes are expanded into
constants in the ompdevlow pass.  In some gimple passes, they need to
be treated as constants.

OMP_TARGET_DEVICE_MATCHES is a similar "magic cookie" used to resolve
the target_device dynamic selector.  It is wrapped in an OpenMP target
construct, and can be resolved to a constant in the ompdevlow pass.

gcc/ChangeLog:
        * doc/generic.texi (OpenMP): Document OMP_METADIRECTIVE,
        OMP_NEXT_VARIANT, and OMP_TARGET_DEVICE_MATCHES.
        * fold-const.cc (operand_compare::hash_operand): Ignore
        the new nodes.
        * gimple_expr.cc (is_gimple_val): Allow OMP_NEXT_VARIANT
        and OMP_TARGET_DEVICE_MATCHES.
        * gimple.cc (get_gimple_rhs_num_ops): OMP_NEXT_VARIANT and
        OMP_TARGET_DEVICE_MATCHES are both GIMPLE_SINGLE_RHS.
        * tree-cfg.cc (tree_node_can_be_shared): Allow sharing of
        OMP_NEXT_VARIANT.
        * tree-inline.cc (remap_gimple_op_r): Ignore subtrees of
        OMP_NEXT_VARIANT.
        * tree-pretty-print.cc (dump_generic_node): Handle OMP_METADIRECTIVE,
        OMP_NEXT_VARIANT, and OMP_TARGET_DEVICE_MATCHES.
        * tree-ssa-operands.cc (operands_scanner::get_expr_operands):
        Ignore operands of OMP_NEXT_VARIANT and OMP_TARGET_DEVICE_MATCHES.
        * tree.def (OMP_METADIRECTIVE): New.
        (OMP_NEXT_VARIANT): New.
        (OMP_TARGET_DEVICE_MATCHES): New.
        * tree.h (OMP_METADIRECTIVE_VARIANTS): New.
        (OMP_METADIRECTIVE_VARIANT_SELECTOR): New.
        (OMP_METADIRECTIVE_VARIANT_DIRECTIVE): New.
        (OMP_METADIRECTIVE_VARIANT_BODY): New.
        (OMP_NEXT_VARIANT_INDEX): New.
        (OMP_NEXT_VARIANT_STATE): New.
        (OMP_TARGET_DEVICE_MATCHES_SELECTOR): New.
        (OMP_TARGET_DEVICE_MATCHES_PROPERTIES): New.

Co-Authored-By: Kwok Cheung Yeung <k...@codesourcery.com>
Co-Authored-By: Sandra Loosemore <san...@codesourcery.com>
---
 gcc/doc/generic.texi     | 63 +++++++++++++++++++++++++++++++
 gcc/fold-const.cc        |  2 +
 gcc/gimple-expr.cc       |  5 +++
 gcc/gimple.cc            |  4 +-
 gcc/tree-cfg.cc          |  1 +
 gcc/tree-inline.cc       |  7 ++++
 gcc/tree-pretty-print.cc | 81 ++++++++++++++++++++++++++++++++++++++++
 gcc/tree-ssa-operands.cc |  4 ++
 gcc/tree.def             | 34 +++++++++++++++++
 gcc/tree.h               | 22 +++++++++++
 10 files changed, 222 insertions(+), 1 deletion(-)

diff --git a/gcc/doc/generic.texi b/gcc/doc/generic.texi
index 3de394fd6e0..28e8c1c3deb 100644
--- a/gcc/doc/generic.texi
+++ b/gcc/doc/generic.texi
@@ -2341,6 +2341,9 @@ edge.  Rethrowing the exception is represented using 
@code{RESX_EXPR}.
 @tindex OMP_CONTINUE
 @tindex OMP_ATOMIC
 @tindex OMP_CLAUSE
+@tindex OMP_METADIRECTIVE
+@tindex OMP_NEXT_VARIANT
+@tindex OMP_TARGET_DEVICE_MATCHES
 
 All the statements starting with @code{OMP_} represent directives and
 clauses used by the OpenMP API @w{@uref{https://www.openmp.org}}.
@@ -2558,6 +2561,66 @@ 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{tree.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.
+
+@item OMP_NEXT_VARIANT
+Some OpenMP variant constructs cannot be resolved until the ompdevlow pass,
+in @file{omp-offload.cc}.  The gimplifier turns these into a @code{switch}
+statement in a loop, using @code{OMP_NEXT_VARIANT} as a placeholder to set
+the switch control variable.  The ompdevlow pass replaces these with constant
+integers after resolution.
+
+@code{OMP_NEXT_VARIANT} has two operands.  Operand 0 is
+@code{OMP_NEXT_VARIANT_INDEX}, an @code{INTEGER_CST} for the current current
+index.  Operand 1 is @code{OMP_NEXT_VARIANT_STATE}, a @code{TREE_LIST} shared
+among all @code{OMP_NEXT_VARIANT} expressions for the same variant construct
+that holds resolution state information for that construct.
+
+@item OMP_TARGET_DEVICE_MATCHES
+Similarly to @code{OMP_NEXT_VARIANT}, this tree node is a placeholder that is
+resolved in the ompdevlow pass.  It is used to implement the
+@code{target_device} dynamic selector.  The gimplifier generates these
+nodes and arranges for them to be executed on the @code{device_num} specified
+in the selector.  The ompdevlow pass replaces each
+@code{OMP_TARGET_DEVICE_MATCHES} node with a constant value, depending on the
+corresponding kind, arch, or isa properties configured for the offload 
compiler.
+
+@code{OMP_TARGET_DEVICES} has two operands.  Operand 0 is
+@code{OMP_TARGET_DEVICE_MATCHES_SELECTOR}, an @code{INTEGER_CST}
+encoding one of the constants @code{OMP_TRAIT_DEVICE_KIND},
+@code{OMP_TRAIT_DEVICE_ARCH}, or @code{OMP_TRAIT_DEVICE_ISA}.  Operand
+1 is @code{OMP_TARGET_DEVICE_MATCHES_PROPERTIES}, a @code{TREE_LIST}
+using the same internal representation as the properties part of the selector.
+
 @end table
 
 @node OpenACC
diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 1e8ae1ab493..39ff18564fc 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -3992,6 +3992,8 @@ operand_compare::hash_operand (const_tree t, 
inchash::hash &hstate,
       return;
     case BLOCK:
     case OMP_CLAUSE:
+    case OMP_NEXT_VARIANT:
+    case OMP_TARGET_DEVICE_MATCHES:
       /* Ignore.  */
       return;
     case TREE_LIST:
diff --git a/gcc/gimple-expr.cc b/gcc/gimple-expr.cc
index 0477c9d5f44..00a87b13454 100644
--- a/gcc/gimple-expr.cc
+++ b/gcc/gimple-expr.cc
@@ -841,6 +841,11 @@ is_gimple_val (tree t)
       && !is_gimple_reg (t))
     return false;
 
+  /* These eventually expand into constants, so treat them like that.  */
+  if (TREE_CODE (t) == OMP_NEXT_VARIANT
+      || TREE_CODE (t) == OMP_TARGET_DEVICE_MATCHES)
+    return true;
+
   return (is_gimple_variable (t) || is_gimple_min_invariant (t));
 }
 
diff --git a/gcc/gimple.cc b/gcc/gimple.cc
index f7b313be40e..74458e57299 100644
--- a/gcc/gimple.cc
+++ b/gcc/gimple.cc
@@ -2447,7 +2447,9 @@ get_gimple_rhs_num_ops (enum tree_code code)
       || (SYM) == OBJ_TYPE_REF                                             \
       || (SYM) == ADDR_EXPR                                                \
       || (SYM) == WITH_SIZE_EXPR                                           \
-      || (SYM) == SSA_NAME) ? GIMPLE_SINGLE_RHS                                
    \
+      || (SYM) == SSA_NAME                                                 \
+      || (SYM) == OMP_NEXT_VARIANT                                         \
+      || (SYM) == OMP_TARGET_DEVICE_MATCHES) ? GIMPLE_SINGLE_RHS           \
    : GIMPLE_INVALID_RHS),
 #define END_OF_BASE_TREE_CODES (unsigned char) GIMPLE_INVALID_RHS,
 
diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc
index 3eede0d61cd..78433fcfd89 100644
--- a/gcc/tree-cfg.cc
+++ b/gcc/tree-cfg.cc
@@ -5344,6 +5344,7 @@ tree_node_can_be_shared (tree t)
       || TREE_CODE (t) == SSA_NAME
       || TREE_CODE (t) == IDENTIFIER_NODE
       || TREE_CODE (t) == CASE_LABEL_EXPR
+      || TREE_CODE (t) == OMP_NEXT_VARIANT
       || is_gimple_min_invariant (t))
     return true;
 
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index d16ad6a28de..0ba6acb7ce5 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -1161,6 +1161,13 @@ remap_gimple_op_r (tree *tp, int *walk_subtrees, void 
*data)
 
          *walk_subtrees = 0;
        }
+      else if (TREE_CODE (*tp) == OMP_NEXT_VARIANT)
+       {
+         /* Neither operand is interesting, and walking the selector
+            causes problems because it's not an expression.  */
+         gcc_assert (TREE_CODE (TREE_OPERAND (*tp, 0)) == INTEGER_CST);
+         *walk_subtrees = 0;
+       }
     }
 
   /* Update the TREE_BLOCK for the cloned expr.  */
diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
index eab5c60579d..292487b8092 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -4071,6 +4071,87 @@ 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 OMP_NEXT_VARIANT:
+      {
+       pp_string (pp, "OMP_NEXT_VARIANT <");
+       dump_generic_node (pp, OMP_NEXT_VARIANT_INDEX (node), spc,
+                          flags, false);
+       pp_string (pp, ", ");
+       tree state = OMP_NEXT_VARIANT_STATE (node);
+       gcc_assert (state && TREE_CODE (state) == TREE_LIST);
+       if (TREE_PURPOSE (state))
+         {
+           newline_and_indent (pp, spc + 2);
+           pp_string (pp, "resolution map = ");
+           dump_generic_node (pp, TREE_PURPOSE (state), spc, flags, false);
+         }
+       newline_and_indent (pp, spc + 2);
+       pp_string (pp, "construct context = ");
+       if (TREE_VALUE (state))
+         dump_generic_node (pp, TREE_VALUE (state), spc, flags, false);
+       else
+         pp_string (pp, "NULL");
+
+       tree selectors = TREE_CHAIN (state);
+       for (int i = 0; i < TREE_VEC_LENGTH (selectors); i++)
+         {
+           newline_and_indent (pp, spc + 2);
+           pp_decimal_int (pp, i + 1);
+           pp_string (pp, ": ");
+           dump_omp_context_selector (pp, TREE_VEC_ELT (selectors, i),
+                                      spc + 4, flags);
+         }
+       pp_string (pp, ">");
+      }
+      break;
+
+    case OMP_TARGET_DEVICE_MATCHES:
+      pp_string (pp, "OMP_TARGET_DEVICE_MATCHES <");
+      dump_generic_node (pp, OMP_TARGET_DEVICE_MATCHES_SELECTOR (node), spc,
+                        flags, false);
+      for (tree p = OMP_TARGET_DEVICE_MATCHES_PROPERTIES (node);
+          p; p = TREE_CHAIN (p))
+       {
+         pp_string (pp, ", ");
+         dump_generic_node (pp, OMP_TP_VALUE (p), spc, flags, false);
+       }
+      pp_string (pp, ")>");
+      break;
+
     case TRANSACTION_EXPR:
       if (TRANSACTION_EXPR_OUTER (node))
        pp_string (pp, "__transaction_atomic [[outer]]");
diff --git a/gcc/tree-ssa-operands.cc b/gcc/tree-ssa-operands.cc
index a01aa51f1cc..61517903ec9 100644
--- a/gcc/tree-ssa-operands.cc
+++ b/gcc/tree-ssa-operands.cc
@@ -810,6 +810,10 @@ operands_scanner::get_expr_operands (tree *expr_p, int 
flags)
        add_stmt_operand (expr_p, flags);
       return;
 
+    case OMP_NEXT_VARIANT:
+    case OMP_TARGET_DEVICE_MATCHES:
+      return;
+
     case DEBUG_EXPR_DECL:
       gcc_assert (gimple_debug_bind_p (stmt));
       return;
diff --git a/gcc/tree.def b/gcc/tree.def
index dd60d1ecde7..77fffb92c9b 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -1356,6 +1356,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.  The variants are internally TREE_LISTs, but use
+   make_omp_metadirective_variant to build them.  */
+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.  */
 
@@ -1389,6 +1395,34 @@ DEFTREECODE (OMP_CLAUSE, "omp_clause", tcc_exceptional, 
0)
 /* An OpenMP array section.  */
 DEFTREECODE (OMP_ARRAY_SECTION, "omp_array_section", tcc_expression, 3)
 
+/* OpenMP variant construct selector, used only in the middle end in the
+   expansions of variant constructs that can't be resolved until the
+   ompdevlow pass.  These variants are converted into switch expressions
+   that use OMP_NEXT_VARIANT as a placeholder for the index of next variant
+   to try if a dynamic selector does not match.  The ompdevlow pass
+   replaces these nodes with constant integers after resolution.
+   Operand 0: OMP_NEXT_VARIANT_INDEX: an INTEGER_CST holding the switch
+   index of the current variant.
+   Operand 1: OMP_NEXT_VARIANT_STATE: a TREE_LIST that is shared among all
+   OMP_NEXT_VARIANT expressions for the same variant directive.  The
+   TREE_PURPOSE of this node holds the resolved lookup table, while
+   TREE_VALUE holds the saved construct context and TREE_CHAIN the
+   original vector of selectors that are used to fill in the table.  */
+DEFTREECODE (OMP_NEXT_VARIANT, "omp_next_variant", tcc_expression, 2)
+
+/* OpenMP target_device match placeholder, similarly used only in the middle
+   end in the expansions of variant constructs that need to be resolved in
+   the ompdevlow pass.
+   Operand 0: OMP_TARGET_DEVICE_MATCHES_SELECTOR: INTEGER_CST encoding one
+   of OMP_TRAIT_DEVICE_KIND, OMP_TRAIT_DEVICE_ARCH, or OMP_TRAIT_DEVICE_ISA.
+   Operand 1: OMP_TARGET_DEVICE_MATCHES_PROPERTIES: A TREE_LIST of strings
+   and/or identifiers, corresponding to the OMP_TS_PROPERTIES for the trait
+   selector.
+   This resolves to a boolean truth value if the properties match the
+   trait selector for the offload compiler.  */
+DEFTREECODE (OMP_TARGET_DEVICE_MATCHES, "omp_target_device_matches",
+            tcc_expression, 2)
+
 /* TRANSACTION_EXPR tree code.
    Operand 0: BODY: contains body of the transaction.  */
 DEFTREECODE (TRANSACTION_EXPR, "transaction_expr", tcc_expression, 1)
diff --git a/gcc/tree.h b/gcc/tree.h
index 56c0454a598..2af510d9bf2 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1609,6 +1609,16 @@ 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_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))
+
 #define OMP_SCAN_BODY(NODE)    TREE_OPERAND (OMP_SCAN_CHECK (NODE), 0)
 #define OMP_SCAN_CLAUSES(NODE) TREE_OPERAND (OMP_SCAN_CHECK (NODE), 1)
 
@@ -2074,6 +2084,18 @@ class auto_suppress_location_wrappers
 #define OMP_CLAUSE__SCANTEMP__CONTROL(NODE) \
   TREE_PRIVATE (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE__SCANTEMP_))
 
+/* OpenMP OMP_NEXT_VARIANT accessors.  */
+#define OMP_NEXT_VARIANT_INDEX(NODE)                   \
+  TREE_OPERAND (OMP_NEXT_VARIANT_CHECK (NODE), 0)
+#define OMP_NEXT_VARIANT_STATE(NODE)                   \
+  TREE_OPERAND (OMP_NEXT_VARIANT_CHECK (NODE), 1)
+
+/* OpenMP OMP_TARGET_DEVICE_MATCHES accessors.  */
+#define OMP_TARGET_DEVICE_MATCHES_SELECTOR(NODE)       \
+  TREE_OPERAND (OMP_TARGET_DEVICE_MATCHES_CHECK (NODE), 0)
+#define OMP_TARGET_DEVICE_MATCHES_PROPERTIES(NODE)     \
+  TREE_OPERAND (OMP_TARGET_DEVICE_MATCHES_CHECK (NODE), 1)
+
 /* SSA_NAME accessors.  */
 
 /* Whether SSA_NAME NODE is a virtual operand.  This simply caches the
-- 
2.25.1

Reply via email to