[2/7] openmp: Add middle-end support for metadirectives

Message ID ea4793e8-937a-b16b-6b10-b13a0c9d1236@codesourcery.com
State New
Headers
Series openmp: OpenMP metadirectives support |

Commit Message

Kwok Cheung Yeung Dec. 10, 2021, 5:33 p.m. UTC
  This patch contains the required support for metadirectives in the 
middle-end.

The tree metadirective representation is gimplified into the high Gimple 
representation, which is structured like this:

#pragma omp metadirective
   when (<selector1>):
     <directive body 1>
     goto body_label|end_label
   when (<selector2>>:
     <directive body 2>
     goto body_label|end_label
   default:
     <directive body N>
     goto body_label|end_label
body_label:
   <standalone body>
end_label:

Each variant ends with an explicit goto to either the shared standalone 
body (if the variant uses it) or to the point after the body (if it does 
not).

When lowered to low Gimple, the directive bodies move outside of the 
metadirective statement, retaining only the labels to the bodies, so it 
looks like this instead:

#pragma omp metadirective
   when (<selector1>): goto body1_label
   when (<selector2>>: goto body2_label
   default: goto default_label
body1_label:
   <directive body 1>
   goto body_label|end_label
body2_label:
   <directive body 2>
   goto body_label|end_label
default_label:
   <directive body N>
   goto body_label|end_label
body_label:
   <standalone body>
end_label:

When scanning the OpenMP regions in the ompexp pass, we create a 'clone' 
of the surrounding context when recursively scanning the directive 
variants. If the same outer context was used for all variants, then it 
would appear as if all the variants were inside the region at the same 
time (only one variant of the metadirective is ever active at a time), 
which can lead to spurious errors.

The rest of the code is the plumbing required to allow the Gimple 
metadirective statement to pass through the middle-end. GCC will emit an 
ICE if it makes it through to the back-end though, as the metadirective 
is supposed to be eliminated before it gets that far.

Kwok
From 1a2fcbb2191fd1dd694ea5730e54fab19d6465b4 Mon Sep 17 00:00:00 2001
From: Kwok Cheung Yeung <kcy@codesourcery.com>
Date: Mon, 6 Dec 2021 22:29:34 +0000
Subject: [PATCH 2/7] openmp: Add middle-end support for metadirectives

This adds a new Gimple statement type GIMPLE_OMP_METADIRECTIVE, which
represents the metadirective in Gimple. In high Gimple, the statement
contains the body of the directive variants, whereas in low Gimple, it
only contains labels to the bodies.

This patch adds support for converting metadirectives from tree to Gimple
form, and handling of the Gimple form (Gimple lowering, OpenMP lowering
and expansion, inlining, SSA handling etc).

Metadirectives should be resolved before they reach the back-end, otherwise
the compiler will crash as GCC does not know how to convert metadirective
Gimple statements to RTX.

2021-12-10  Kwok Cheung Yeung  <kcy@codesourcery.com>

	gcc/
	* gimple-low.c (lower_omp_metadirective): New.
	(lower_stmt): Handle GIMPLE_OMP_METADIRECTIVE.
	* gimple-pretty-print.c (dump_gimple_omp_metadirective): New.
	(pp_gimple_stmt_1): Handle GIMPLE_OMP_METADIRECTIVE.
	* gimple-walk.c (walk_gimple_op): Handle GIMPLE_OMP_METADIRECTIVE.
	(walk_gimple_stmt): Likewise.
	* gimple.c (gimple_alloc_omp_metadirective): New.
	(gimple_build_omp_metadirective): New.
	(gimple_build_omp_metadirective_variant): New.
	* gimple.def (GIMPLE_OMP_METADIRECTIVE): New.
	(GIMPLE_OMP_METADIRECTIVE_VARIANT): New.
	* gimple.h (gomp_metadirective_variant): New.
	(gomp_metadirective): New.
	(is_a_helper <gomp_metadirective *>::test): New.
	(is_a_helper <gomp_metadirective_variant *>::test): New.
	(is_a_helper <const gomp_metadirective *>::test): New.
	(is_a_helper <const gomp_metadirective_variant *>::test): New.
	(gimple_alloc_omp_metadirective): New prototype.
	(gimple_build_omp_metadirective): New prototype.
	(gimple_build_omp_metadirective_variant): New prototype.
	(gimple_has_substatements): Add GIMPLE_OMP_METADIRECTIVE case.
	(gimple_has_ops): Add GIMPLE_OMP_METADIRECTIVE.
	(gimple_omp_metadirective_label): New.
	(gimple_omp_metadirective_set_label): New.
	(gimple_omp_metadirective_variants): New.
	(gimple_omp_metadirective_set_variants): New.
	(CASE_GIMPLE_OMP): Add GIMPLE_OMP_METADIRECTIVE.
	* gimplify.c (is_gimple_stmt): Add OMP_METADIRECTIVE.
	(expand_omp_metadirective): New.
	(gimplify_omp_metadirective): New.
	(gimplify_expr): Add case for OMP_METADIRECTIVE.
	* gsstruct.def (GSS_OMP_METADIRECTIVE): New.
	(GSS_OMP_METADIRECTIVE_VARIANT): New.
	* omp-expand.c (build_omp_regions_1): Handle GIMPLE_OMP_METADIRECTIVE.
	(omp_make_gimple_edges): Likewise.
	* omp-low.c (struct omp_context): Add next_clone field.
	(new_omp_context): Initialize next_clone field.
	(clone_omp_context): New.
	(delete_omp_context): Delete clone contexts.
	(scan_omp_metadirective): New.
	(scan_omp_1_stmt): Handle GIMPLE_OMP_METADIRECTIVE.
	(lower_omp_metadirective): New.
	(lower_omp_1): Handle GIMPLE_OMP_METADIRECTIVE.
	* tree-cfg.c (cleanup_dead_labels): Handle GIMPLE_OMP_METADIRECTIVE.
	(gimple_redirect_edge_and_branch): Likewise.
	* tree-inline.c (remap_gimple_stmt): Handle GIMPLE_OMP_METADIRECTIVE.
	(estimate_num_insns): Likewise.
	* tree-pretty-print.c (dump_generic_node): Handle OMP_METADIRECTIVE.
	* tree-ssa-operands.c (parse_ssa_operands): Handle
	GIMPLE_OMP_METADIRECTIVE.
---
 gcc/gimple-low.c          |  34 +++++++++++++
 gcc/gimple-pretty-print.c |  63 ++++++++++++++++++++++++
 gcc/gimple-walk.c         |  31 ++++++++++++
 gcc/gimple.c              |  35 +++++++++++++
 gcc/gimple.def            |   7 +++
 gcc/gimple.h              | 100 +++++++++++++++++++++++++++++++++++++-
 gcc/gimplify.c            |  94 +++++++++++++++++++++++++++++++++++
 gcc/gsstruct.def          |   2 +
 gcc/omp-expand.c          |  28 +++++++++++
 gcc/omp-low.c             |  66 +++++++++++++++++++++++++
 gcc/tree-cfg.c            |  24 +++++++++
 gcc/tree-inline.c         |  36 ++++++++++++++
 gcc/tree-pretty-print.c   |  34 +++++++++++++
 gcc/tree-ssa-operands.c   |  27 ++++++++++
 14 files changed, 580 insertions(+), 1 deletion(-)
  

Comments

Jakub Jelinek May 30, 2022, 10:54 a.m. UTC | #1
On Fri, Dec 10, 2021 at 05:33:25PM +0000, Kwok Cheung Yeung wrote:
> 2021-12-10  Kwok Cheung Yeung  <kcy@codesourcery.com>
> 
> 	gcc/
> 	* gimple-low.c (lower_omp_metadirective): New.
> 	(lower_stmt): Handle GIMPLE_OMP_METADIRECTIVE.
> 	* gimple-pretty-print.c (dump_gimple_omp_metadirective): New.
> 	(pp_gimple_stmt_1): Handle GIMPLE_OMP_METADIRECTIVE.
> 	* gimple-walk.c (walk_gimple_op): Handle GIMPLE_OMP_METADIRECTIVE.
> 	(walk_gimple_stmt): Likewise.
> 	* gimple.c (gimple_alloc_omp_metadirective): New.
> 	(gimple_build_omp_metadirective): New.
> 	(gimple_build_omp_metadirective_variant): New.
> 	* gimple.def (GIMPLE_OMP_METADIRECTIVE): New.
> 	(GIMPLE_OMP_METADIRECTIVE_VARIANT): New.
> 	* gimple.h (gomp_metadirective_variant): New.
> 	(gomp_metadirective): New.
> 	(is_a_helper <gomp_metadirective *>::test): New.
> 	(is_a_helper <gomp_metadirective_variant *>::test): New.
> 	(is_a_helper <const gomp_metadirective *>::test): New.
> 	(is_a_helper <const gomp_metadirective_variant *>::test): New.
> 	(gimple_alloc_omp_metadirective): New prototype.
> 	(gimple_build_omp_metadirective): New prototype.
> 	(gimple_build_omp_metadirective_variant): New prototype.
> 	(gimple_has_substatements): Add GIMPLE_OMP_METADIRECTIVE case.
> 	(gimple_has_ops): Add GIMPLE_OMP_METADIRECTIVE.
> 	(gimple_omp_metadirective_label): New.
> 	(gimple_omp_metadirective_set_label): New.
> 	(gimple_omp_metadirective_variants): New.
> 	(gimple_omp_metadirective_set_variants): New.
> 	(CASE_GIMPLE_OMP): Add GIMPLE_OMP_METADIRECTIVE.
> 	* gimplify.c (is_gimple_stmt): Add OMP_METADIRECTIVE.
> 	(expand_omp_metadirective): New.
> 	(gimplify_omp_metadirective): New.
> 	(gimplify_expr): Add case for OMP_METADIRECTIVE.
> 	* gsstruct.def (GSS_OMP_METADIRECTIVE): New.
> 	(GSS_OMP_METADIRECTIVE_VARIANT): New.
> 	* omp-expand.c (build_omp_regions_1): Handle GIMPLE_OMP_METADIRECTIVE.
> 	(omp_make_gimple_edges): Likewise.
> 	* omp-low.c (struct omp_context): Add next_clone field.
> 	(new_omp_context): Initialize next_clone field.
> 	(clone_omp_context): New.
> 	(delete_omp_context): Delete clone contexts.
> 	(scan_omp_metadirective): New.
> 	(scan_omp_1_stmt): Handle GIMPLE_OMP_METADIRECTIVE.
> 	(lower_omp_metadirective): New.
> 	(lower_omp_1): Handle GIMPLE_OMP_METADIRECTIVE.
> 	* tree-cfg.c (cleanup_dead_labels): Handle GIMPLE_OMP_METADIRECTIVE.
> 	(gimple_redirect_edge_and_branch): Likewise.
> 	* tree-inline.c (remap_gimple_stmt): Handle GIMPLE_OMP_METADIRECTIVE.
> 	(estimate_num_insns): Likewise.
> 	* tree-pretty-print.c (dump_generic_node): Handle OMP_METADIRECTIVE.
> 	* tree-ssa-operands.c (parse_ssa_operands): Handle
> 	GIMPLE_OMP_METADIRECTIVE.

> --- a/gcc/gimple-pretty-print.c
> +++ b/gcc/gimple-pretty-print.c
> @@ -2051,6 +2051,63 @@ dump_gimple_omp_return (pretty_printer *buffer, const gimple *gs, int spc,
>      }
>  }
>  
> +/* 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));
> +    }

No need for {}s around a single statement.

> +  else
> +    {
> +      pp_string (buffer, "#pragma omp metadirective");
> +      newline_and_indent (buffer, spc + 2);
> +
> +      gimple *variant = gimple_omp_metadirective_variants (gs);
> +
> +      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_generic_node (buffer, selector, spc, flags, false);
> +	      pp_string (buffer, "):");
> +	    }
> +
> +	  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);
> +
> +	      variant = variant->next;
> +	    }
> +	  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);

I think better would be to use a gimple_stmt_iterator to walk the variants,
so no variant->next etc., but gimple_omp_metadirective_variants returning
a gimple_seq instead of gimple * (it is the same thing under the hood),
then
      gimple_seq variant_seq = gimple_omp_metadirective_variants (gs);
      gimple_stmt_iterator gsi = gsi_start (variant_seq);
and in the loop
	  gimple *variant = gsi_stmt (gsi);
and gsi_next (&gsi); at the end.  Similarly for all other spots that walk
gimple_omp_metadirective_variants.


> +    case GIMPLE_OMP_METADIRECTIVE:
> +      {
> +	gimple *variant = gimple_omp_metadirective_variants (stmt);
> +
> +	while (variant)
> +	  {
> +	    ret = walk_gimple_op (gimple_omp_body (variant), callback_op, wi);
> +	    if (ret)
> +	      return ret;
> +
> +	    variant = variant->next;
> +	  }

So here too...

> +      }
> +      break;
> +
>      case GIMPLE_TRANSACTION:
>        {
>  	gtransaction *txn = as_a <gtransaction *> (stmt);
> @@ -700,6 +715,22 @@ walk_gimple_stmt (gimple_stmt_iterator *gsi, walk_stmt_fn callback_stmt,
>  	return wi->callback_result;
>        break;
>  
> +    case GIMPLE_OMP_METADIRECTIVE:
> +      {
> +	gimple *variant = gimple_omp_metadirective_variants (stmt);
> +
> +	while (variant)
> +	  {
> +	    ret = walk_gimple_seq_mod (gimple_omp_body_ptr (variant),
> +				       callback_stmt, callback_op, wi);
> +	    if (ret)
> +	      return wi->callback_result;
> +
> +	    variant = variant->next;
> +	  }

and here etc.
> --- a/gcc/omp-expand.c
> +++ b/gcc/omp-expand.c
> @@ -10418,6 +10418,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.  */
> +	}

No {}s around the comment, just /* ...  */;

> --- a/gcc/tree-cfg.c
> +++ b/gcc/tree-cfg.c
> @@ -1670,6 +1670,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;

Why the {} around the for?

> @@ -6147,6 +6159,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;

Likewise.

> --- a/gcc/tree-ssa-operands.c
> +++ b/gcc/tree-ssa-operands.c
> @@ -973,6 +973,33 @@ 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++)
> +	{

Why the {}s around the inner for?

> +	  for (tree selector = gimple_op (stmt, i);
> +	       selector != NULL;
> +	       selector = TREE_CHAIN (selector))
> +	    {

Why the {}s around the if ?

> +	      if (TREE_PURPOSE (selector) == get_identifier ("user"))
> +		{
> +		  for (tree property = TREE_VALUE (selector);
> +		       property != NULL;
> +		       property = TREE_CHAIN (property))
> +		    if (TREE_PURPOSE (property)
> +			== get_identifier ("condition"))
> +		      {
> +			for (tree condition = TREE_VALUE (property);
> +			     condition != NULL;
> +			     condition = TREE_CHAIN (condition))
> +			  get_expr_operands (&TREE_VALUE (condition),
> +					     opf_use);
> +		      }
> +		}
> +	    }
> +	}
> +      break;
> +

Also, I wonder how does LTO saving/restoring handle the
GIMPLE_OMP_METADIRECTIVE statements.

Otherwise LGTM.

	Jakub
  

Patch

diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c
index 7e39c22df44..723c8b1d516 100644
--- a/gcc/gimple-low.c
+++ b/gcc/gimple-low.c
@@ -234,6 +234,34 @@  lower_omp_directive (gimple_stmt_iterator *gsi, struct lower_data *data)
   gsi_next (gsi);
 }
 
+/* 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 *variant = gimple_omp_metadirective_variants (stmt);
+  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++)
+    {
+      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);
+
+      variant = variant->next;
+    }
+
+  gsi_next (gsi);
+}
+
 
 /* Lower statement GSI.  DATA is passed through the recursion.  We try to
    track the fallthruness of statements and get rid of unreachable return
@@ -400,6 +428,12 @@  lower_stmt (gimple_stmt_iterator *gsi, struct lower_data *data)
       data->cannot_fallthru = false;
       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 <gtransaction *> (stmt)),
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 1cd1597359e..da263137f5b 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -2051,6 +2051,63 @@  dump_gimple_omp_return (pretty_printer *buffer, const gimple *gs, int spc,
     }
 }
 
+/* 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 *variant = gimple_omp_metadirective_variants (gs);
+
+      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_generic_node (buffer, selector, spc, flags, false);
+	      pp_string (buffer, "):");
+	    }
+
+	  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);
+
+	      variant = variant->next;
+	    }
+	  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 +2880,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 <const gomp_metadirective *> (gs),
+				     spc, flags);
+      break;
+
     case GIMPLE_CATCH:
       dump_gimple_catch (buffer, as_a <const gcatch *> (gs), spc, flags);
       break;
diff --git a/gcc/gimple-walk.c b/gcc/gimple-walk.c
index e15fd4697ba..b8db0fe34b2 100644
--- a/gcc/gimple-walk.c
+++ b/gcc/gimple-walk.c
@@ -485,6 +485,21 @@  walk_gimple_op (gimple *stmt, walk_tree_fn callback_op,
       }
       break;
 
+    case GIMPLE_OMP_METADIRECTIVE:
+      {
+	gimple *variant = gimple_omp_metadirective_variants (stmt);
+
+	while (variant)
+	  {
+	    ret = walk_gimple_op (gimple_omp_body (variant), callback_op, wi);
+	    if (ret)
+	      return ret;
+
+	    variant = variant->next;
+	  }
+      }
+      break;
+
     case GIMPLE_TRANSACTION:
       {
 	gtransaction *txn = as_a <gtransaction *> (stmt);
@@ -700,6 +715,22 @@  walk_gimple_stmt (gimple_stmt_iterator *gsi, walk_stmt_fn callback_stmt,
 	return wi->callback_result;
       break;
 
+    case GIMPLE_OMP_METADIRECTIVE:
+      {
+	gimple *variant = gimple_omp_metadirective_variants (stmt);
+
+	while (variant)
+	  {
+	    ret = walk_gimple_seq_mod (gimple_omp_body_ptr (variant),
+				       callback_stmt, callback_op, wi);
+	    if (ret)
+	      return wi->callback_result;
+
+	    variant = variant->next;
+	  }
+      }
+      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.c b/gcc/gimple.c
index 037c6e4c827..99f3a8de2ea 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -1267,6 +1267,41 @@  gimple_build_omp_atomic_store (tree val, enum omp_memory_order mo)
   return p;
 }
 
+/* Allocate extra memory for a GIMPLE_OMP_METADIRECTIVE statement.  */
+
+void
+gimple_alloc_omp_metadirective (gimple *g)
+{
+  gomp_metadirective *p = as_a <gomp_metadirective *> (g);
+
+  p->labels = ggc_cleared_vec_alloc<tree> (gimple_num_ops (p));
+}
+
+/* Build a GIMPLE_OMP_METADIRECTIVE statement.  */
+
+gomp_metadirective *
+gimple_build_omp_metadirective (int num_variants)
+{
+  gomp_metadirective *p
+    = as_a <gomp_metadirective *> (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_metadirective_variant *
+gimple_build_omp_metadirective_variant (gimple_seq body)
+{
+  gomp_metadirective_variant *variant = as_a <gomp_metadirective_variant *>
+    (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 193b2506523..55ff9883193 100644
--- a/gcc/gimple.def
+++ b/gcc/gimple.def
@@ -393,6 +393,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_metadirective_variant", GSS_OMP_METADIRECTIVE_VARIANT)
+
 /* GIMPLE_PREDICT <PREDICT, OUTCOME> 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 f7fdefc5362..8554d288e42 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -824,6 +824,30 @@  struct GTY((tag("GSS_OMP_ATOMIC_STORE_LAYOUT")))
          stmt->code == GIMPLE_OMP_RETURN.  */
 };
 
+struct GTY((tag("GSS_OMP_METADIRECTIVE_VARIANT")))
+  gomp_metadirective_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_metadirective_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.  */
@@ -1235,6 +1259,22 @@  is_a_helper <gomp_task *>::test (gimple *gs)
   return gs->code == GIMPLE_OMP_TASK;
 }
 
+template <>
+template <>
+inline bool
+is_a_helper <gomp_metadirective *>::test (gimple *gs)
+{
+  return gs->code == GIMPLE_OMP_METADIRECTIVE;
+}
+
+template <>
+template <>
+inline bool
+is_a_helper <gomp_metadirective_variant *>::test (gimple *gs)
+{
+  return gs->code == GIMPLE_OMP_METADIRECTIVE_VARIANT;
+}
+
 template <>
 template <>
 inline bool
@@ -1477,6 +1517,22 @@  is_a_helper <const gomp_task *>::test (const gimple *gs)
   return gs->code == GIMPLE_OMP_TASK;
 }
 
+template <>
+template <>
+inline bool
+is_a_helper <const gomp_metadirective *>::test (const gimple *gs)
+{
+  return gs->code == GIMPLE_OMP_METADIRECTIVE;
+}
+
+template <>
+template <>
+inline bool
+is_a_helper <const gomp_metadirective_variant *>::test (const gimple *gs)
+{
+  return gs->code == GIMPLE_OMP_METADIRECTIVE_VARIANT;
+}
+
 template <>
 template <>
 inline bool
@@ -1576,6 +1632,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_metadirective_variant *gimple_build_omp_metadirective_variant (gimple_seq body);
 gtransaction *gimple_build_transaction (gimple_seq);
 extern void gimple_seq_add_stmt (gimple_seq *, gimple *);
 extern void gimple_seq_add_stmt_without_update (gimple_seq *, gimple *);
@@ -1853,6 +1912,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;
@@ -2110,7 +2170,8 @@  gimple_init_singleton (gimple *g)
 static 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 <>
@@ -6488,6 +6549,42 @@  gimple_omp_continue_set_control_use (gomp_continue *cont_stmt, tree use)
   cont_stmt->control_use = use;
 }
 
+
+static inline tree
+gimple_omp_metadirective_label (const gimple *g, unsigned i)
+{
+  const gomp_metadirective *omp_metadirective
+    = as_a <const gomp_metadirective *> (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 <gomp_metadirective *> (g);
+  omp_metadirective->labels[i] = label;
+}
+
+
+static inline gomp_metadirective_variant *
+gimple_omp_metadirective_variants (const gimple *g)
+{
+  const gomp_metadirective *omp_metadirective
+    = as_a <const gomp_metadirective *> (g);
+  return omp_metadirective->variants;
+}
+
+
+static inline void
+gimple_omp_metadirective_set_variants (gimple *g, gimple *variants)
+{
+  gomp_metadirective *omp_metadirective = as_a <gomp_metadirective *> (g);
+  omp_metadirective->variants
+    = variants ? as_a <gomp_metadirective_variant *> (variants) : NULL;
+}
+
+
 /* Return a pointer to the body for the GIMPLE_TRANSACTION statement
    TRANSACTION_STMT.  */
 
@@ -6638,6 +6735,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
 
 static inline bool
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index b118c72f62c..ed72162bb7f 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -5787,6 +5787,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:
@@ -14680,6 +14681,94 @@  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<struct omp_metadirective_variant> &,
+			  gimple_seq *)
+{
+  return GS_ERROR;
+}
+
+/* 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<tree> selectors;
+
+  /* Try to resolve the metadirective.  */
+  vec<struct omp_metadirective_variant> candidates
+    = omp_resolve_metadirective (*expr_p);
+  if (!candidates.is_empty ())
+    return expand_omp_metadirective (candidates, pre_p);
+
+  /* The metadirective cannot be resolved yet.  */
+
+  gomp_metadirective_variant *first_variant = NULL;
+  gomp_metadirective_variant *prev_variant = NULL;
+  gimple_seq standalone_body = NULL;
+  tree body_label = NULL;
+  tree end_label = create_artificial_label (UNKNOWN_LOCATION);
+
+  for (tree clause = OMP_METADIRECTIVE_CLAUSES (*expr_p); clause != NULL_TREE;
+       clause = TREE_CHAIN (clause))
+    {
+      tree selector = TREE_PURPOSE (clause);
+      tree directive = TREE_PURPOSE (TREE_VALUE (clause));
+      tree body = TREE_VALUE (TREE_VALUE (clause));
+
+      selectors.safe_push (selector);
+      gomp_metadirective_variant *variant
+	= gimple_build_omp_metadirective_variant (NULL);
+      gimple_seq *directive_p = gimple_omp_body_ptr (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 = variant;
+      if (prev_variant)
+	{
+	  prev_variant->next = variant;
+	  variant->prev = prev_variant;
+	}
+      prev_variant = 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));
+
+  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
@@ -15586,6 +15675,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 8f777e2bb95..ff10605baec 100644
--- a/gcc/gsstruct.def
+++ b/gcc/gsstruct.def
@@ -50,4 +50,6 @@  DEFGSSTRUCT(GSS_OMP_SINGLE_LAYOUT, gimple_statement_omp_single_layout, false)
 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_OMP_METADIRECTIVE, gomp_metadirective, true)
+DEFGSSTRUCT(GSS_OMP_METADIRECTIVE_VARIANT, gomp_metadirective_variant, false)
 DEFGSSTRUCT(GSS_TRANSACTION, gtransaction, false)
diff --git a/gcc/omp-expand.c b/gcc/omp-expand.c
index c5fa5a01aac..3bf81e1ae95 100644
--- a/gcc/omp-expand.c
+++ b/gcc/omp-expand.c
@@ -10418,6 +10418,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);
@@ -10791,6 +10795,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_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-low.c b/gcc/omp-low.c
index 2a07beb4eaf..accea81e8af 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -183,6 +183,10 @@  struct omp_context
 
   /* Candidates for adjusting OpenACC privatization level.  */
   vec<tree> 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;
@@ -985,6 +989,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)
     {
@@ -1014,6 +1019,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.  */
@@ -1060,6 +1077,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)
@@ -3091,6 +3117,24 @@  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)
+{
+  gomp_metadirective_variant *variant
+    = gimple_omp_metadirective_variants (stmt);
+
+  while (variant)
+    {
+      gimple_seq *directive_p = gimple_omp_body_ptr (variant);
+      omp_context *ctx = outer_ctx ? clone_omp_context (outer_ctx) : NULL;
+
+      scan_omp (directive_p, ctx);
+      variant = (gomp_metadirective_variant *) variant->next;
+    }
+}
+
 /* Check nesting restrictions.  */
 static bool
 check_omp_nesting_restrictions (gimple *stmt, omp_context *ctx)
@@ -4235,6 +4279,10 @@  scan_omp_1_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
 	scan_omp_teams (as_a <gomp_teams *> (stmt), ctx);
       break;
 
+    case GIMPLE_OMP_METADIRECTIVE:
+      scan_omp_metadirective (as_a <gomp_metadirective *> (stmt), ctx);
+      break;
+
     case GIMPLE_BIND:
       {
 	tree var;
@@ -10654,6 +10702,21 @@  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);
+  gomp_metadirective_variant *variant
+    = gimple_omp_metadirective_variants (stmt);
+  while (variant)
+    {
+      gimple_seq *directive_p = gimple_omp_body_ptr (variant);
+      lower_omp (directive_p, ctx);
+
+      variant = (gomp_metadirective_variant *) (variant->next);
+    }
+}
+
 /* Callback for walk_gimple_seq.  Find #pragma omp scan statement.  */
 
 static tree
@@ -14230,6 +14293,9 @@  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 <gcall *> (stmt);
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index ebbd894ae03..7066d9fb471 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -1670,6 +1670,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;
       }
@@ -6147,6 +6159,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.c b/gcc/tree-inline.c
index bc5ff0bb052..0f0035fef3b 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -1674,6 +1674,35 @@  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;
+	    for (gimple *variant = gimple_omp_metadirective_variants (stmt);
+		 variant; variant = variant->next)
+	      {
+		s1 = remap_gimple_seq (gimple_omp_body (variant), id);
+		gimple *new_variant
+		  = gimple_build_omp_metadirective_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
@@ -4590,6 +4619,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 (
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index a81ba401ef9..eb45f7d6bdf 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -3751,6 +3751,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 clause = OMP_METADIRECTIVE_CLAUSES (node);
+	while (clause != NULL_TREE)
+	  {
+	    newline_and_indent (pp, spc + 4);
+	    if (TREE_PURPOSE (clause) == NULL_TREE)
+	      pp_string (pp, "default:");
+	    else
+	      {
+		pp_string (pp, "when (");
+		dump_generic_node (pp, TREE_PURPOSE (clause), spc + 4, flags,
+				   false);
+		pp_string (pp, "):");
+	      }
+	    newline_and_indent (pp, spc + 6);
+
+	    tree variant = TREE_VALUE (clause);
+	    dump_generic_node (pp, TREE_PURPOSE (variant), spc + 6, flags,
+			       false);
+	    newline_and_indent (pp, spc + 6);
+	    dump_generic_node (pp, TREE_VALUE (variant), spc + 6, flags,
+			       false);
+	    clause = TREE_CHAIN (clause);
+	  }
+	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-ssa-operands.c b/gcc/tree-ssa-operands.c
index ebf7eea3b04..d17e4144df7 100644
--- a/gcc/tree-ssa-operands.c
+++ b/gcc/tree-ssa-operands.c
@@ -973,6 +973,33 @@  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 selector = gimple_op (stmt, i);
+	       selector != NULL;
+	       selector = TREE_CHAIN (selector))
+	    {
+	      if (TREE_PURPOSE (selector) == get_identifier ("user"))
+		{
+		  for (tree property = TREE_VALUE (selector);
+		       property != NULL;
+		       property = TREE_CHAIN (property))
+		    if (TREE_PURPOSE (property)
+			== get_identifier ("condition"))
+		      {
+			for (tree condition = TREE_VALUE (property);
+			     condition != NULL;
+			     condition = TREE_CHAIN (condition))
+			  get_expr_operands (&TREE_VALUE (condition),
+					     opf_use);
+		      }
+		}
+	    }
+	}
+      break;
+
     case GIMPLE_CALL:
       /* Add call-clobbered operands, if needed.  */
       maybe_add_call_vops (as_a <gcall *> (stmt));