diff mbox series

Fortran: Fixes to OpenMP 'interop' directive parsing support

Message ID c81e0c16-b5db-4a38-b444-b730d5ae08a2@baylibre.com
State New
Headers show
Series Fortran: Fixes to OpenMP 'interop' directive parsing support | expand

Commit Message

Tobias Burnus Sept. 12, 2024, 5 p.m. UTC
This patch fixes a couple of issues, like a missing white-space gobbling 
after matching an expression.

It also reorganizes some code to handle 'identifier_"string"' vs. 
'identifier' better as there were some diagnostic issues.

(OpenMP requires for 'fr' that the argument is either an identifier 
(that is a scalar integer parameter) or a string; while for the older 
syntax, it can be any constant integer expression.)

However, the two main changes are:

* 'fr' and 'attr' actually support a list of arguments. While I believe 
'attr("x", "y") and "attr("x"),attr("y")' are semantically identically, 
supporting more than one (or zero) values for 'fr' required a different 
encoding.

* Jakub additionally suggested that for 'fr', which supports constant 
integers and string literals, we could pass on integer values – and do 
some checking.

That's what this patch does: Known string values are converted to their 
associated integer values, others to 0. And if the integer/string value 
is unknown, a warning is printed [-Wopenmp].

Known values are those in the "OpenMP API Additional Definitions" 
document, https://www.openmp.org/specifications/ – with the addition of 
hsa / 7, which has been voted at spec level (no idea about ARB level) 
but not yet published.

Note that that's the warning is based on what is defined there, i.e. 
'level_zero' there is no warning, even though GCC does not support it. 
Obviously, if will add another value next year, GCC 15 will not support 
it and warn, even if the code is perfectly valid. — But I guess we can 
live with a warning in that case.

Comments, remarks, suggestions? — Especially regarding the internal 
representation?

Tobias

PS: Next step will be to get the C/C++ parsing working, which also 
implies encoding this representation into 'tree'. (Then doing the tree 
conversion for Fortran.) Once satisfied with that, the middle end + 
libgomp part that links those bits will come next. And the question 
whether there should be one call per 'interop' directive or might be 
multiple (e.g. one per interop object in 'init'/'use'/'destroy').
diff mbox series

Patch

Fortran: Fixes to OpenMP 'interop' directive parsing support

Handle lists as argument to 'fr' and 'attr'; fix parsing corner cases.
Additionally, 'fr' values are now internally stored as integer, permitting
the diagnoses (warning) for values not defined in the OpenMP additional
definitions document.

	PR fortran/116661

gcc/fortran/ChangeLog:

	* gfortran.h (gfc_omp_namelist): Rename 'init' members for clarity.
	* match.cc (gfc_free_omp_namelist): Handle renaming.
	* dump-parse-tree.cc (show_omp_namelist): Update for new format
	and features.
	* openmp.cc (gfc_match_omp_prefer_type): Parse list to 'fr' and 'attr';
	store 'fr' values as integer.
	(gfc_match_omp_init): Rename variable names.

gcc/ChangeLog:

	* omp-api.h (omp_get_fr_id_from_name, omp_get_name_from_fr_id): New
	prototypes.
	* omp-general.cc (omp_get_fr_id_from_name, omp_get_name_from_fr_id):
	New.

include/ChangeLog:

	* gomp-constants.h (GOMP_INTEROP_IFR_LAST,
	GOMP_INTEROP_IFR_SEPARATOR, GOMP_INTEROP_IFR_NONE): New.

gcc/testsuite/ChangeLog:

	* gfortran.dg/gomp/interop-1.f90: Extend, update dg-*.
	* gfortran.dg/gomp/interop-2.f90: Update dg-error.
	* gfortran.dg/gomp/interop-3.f90: Add dg-warning.

 gcc/fortran/dump-parse-tree.cc               |  84 +++++---
 gcc/fortran/gfortran.h                       |   4 +-
 gcc/fortran/match.cc                         |  10 +-
 gcc/fortran/openmp.cc                        | 305 ++++++++++++++++-----------
 gcc/omp-api.h                                |   3 +
 gcc/omp-general.cc                           |  29 +++
 gcc/testsuite/gfortran.dg/gomp/interop-1.f90 |  32 ++-
 gcc/testsuite/gfortran.dg/gomp/interop-2.f90 |   2 +-
 gcc/testsuite/gfortran.dg/gomp/interop-3.f90 |   2 +-
 include/gomp-constants.h                     |   5 +
 10 files changed, 314 insertions(+), 162 deletions(-)

diff --git a/gcc/fortran/dump-parse-tree.cc b/gcc/fortran/dump-parse-tree.cc
index 8fc6141611c..3547d7f8aca 100644
--- a/gcc/fortran/dump-parse-tree.cc
+++ b/gcc/fortran/dump-parse-tree.cc
@@ -37,6 +37,8 @@  along with GCC; see the file COPYING3.  If not see
 #include "constructor.h"
 #include "version.h"
 #include "parse.h"  /* For gfc_ascii_statement.  */
+#include "omp-api.h"  /* For omp_get_name_from_fr_id.  */
+#include "gomp-constants.h"  /* For GOMP_INTEROP_IFR_SEPARATOR.  */
 
 /* Keep track of indentation for symbol tree dumps.  */
 static int show_level = 0;
@@ -1537,35 +1539,69 @@  show_omp_namelist (int list_type, gfc_omp_namelist *n)
 	}
       else if (list_type == OMP_LIST_INIT)
 	{
-	  int i = 0;
 	  if (n->u.init.target)
 	    fputs ("target,", dumpfile);
 	  if (n->u.init.targetsync)
 	    fputs ("targetsync,", dumpfile);
-	  char *prefer_type = n->u.init.str;
-	  if (n->u.init.len)
-	    fputs ("prefer_type(", dumpfile);
-	  if (n->u.init.len)
-	    while (*prefer_type)
-	      {
-		fputc ('{', dumpfile);
-		if (n->u2.interop_int && n->u2.interop_int[i] != 0)
-		  fprintf (dumpfile, "fr(%d),", n->u2.interop_int[i]);
-		else if (prefer_type[0] != ' ' || prefer_type[1] != '\0')
-		  fprintf (dumpfile, "fr(\"%s\"),", prefer_type);
-		prefer_type += 1 + strlen (prefer_type);
-
-		while (*prefer_type)
-		  {
-		    fprintf (dumpfile, "attr(\"%s\"),", prefer_type);
-		    prefer_type += 1 + strlen (prefer_type);
-		  }
-		fputc ('}', dumpfile);
-		++prefer_type;
-		++i;
+	  if (n->u2.init_interop_fr)
+	    {
+	      char *attr_str = n->u.init.attr;
+	      int idx = 0;
+	      int fr_id;
+	      fputs ("prefer_type(", dumpfile);
+	      do
+		{
+		  fr_id = n->u2.init_interop_fr[idx];
+		  fputc ('{', dumpfile);
+		  if (fr_id != GOMP_INTEROP_IFR_NONE)
+		    {
+		      fputs ("fr(", dumpfile);
+		      do
+			{
+			  const char *fr_str = omp_get_name_from_fr_id (fr_id);
+			  if (fr_str)
+			    fprintf (dumpfile, "\"%s\"", fr_str);
+			  else
+			    fprintf (dumpfile, "%d", fr_id);
+			  fr_id = n->u2.init_interop_fr[++idx];
+			  if (fr_id != GOMP_INTEROP_IFR_SEPARATOR)
+			    fputc (',', dumpfile);
+			}
+		      while (fr_id != GOMP_INTEROP_IFR_SEPARATOR);
+		      fputc (')', dumpfile);
+		      if (attr_str && (attr_str[0] != ' ' || attr_str[1] != '\0'))
+			fputc (',', dumpfile);
+		    }
+		  else
+		    fr_id = n->u2.init_interop_fr[++idx];
+		  if (attr_str && attr_str[0] == ' ' && attr_str[1] == '\0')
+		    attr_str += 2;
+		  else if (attr_str)
+		    {
+		      fputs ("attr(\"", dumpfile);
+		      do
+			{
+			  fputs ((char *) attr_str, dumpfile);
+			  fputc ('"', dumpfile);
+			  attr_str += strlen (attr_str) + 1;
+			  if (attr_str[0] == '\0')
+			    break;
+			  fputs (",\"", dumpfile);
+			}
+		      while (true);
+		      fputc (')', dumpfile);
+		    }
+		  fputc ('}', dumpfile);
+		  fr_id = n->u2.init_interop_fr[++idx];
+		  if (fr_id == GOMP_INTEROP_IFR_SEPARATOR)
+		    break;
+		  fputc (',', dumpfile);
+		  if (attr_str)
+		    ++attr_str;
+		}
+	      while (true);
+	      fputc (')', dumpfile);
 	    }
-	  if (n->u.init.len)
-	    fputc (')', dumpfile);
 	  fputc (':', dumpfile);
 	}
       fprintf (dumpfile, "%s", n->sym ? n->sym->name : "omp_all_memory");
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 797d4ed07f5..37c28691f41 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -1389,7 +1389,7 @@  typedef struct gfc_omp_namelist
       bool present_modifier;
       struct
 	{
-	  char *str;
+	  char *attr;
 	  int len;
 	  bool target;
 	  bool targetsync;
@@ -1402,7 +1402,7 @@  typedef struct gfc_omp_namelist
       gfc_expr *allocator;
       struct gfc_symbol *traits_sym;
       struct gfc_omp_namelist *duplicate_of;
-      int *interop_int;
+      char *init_interop_fr;
     } u2;
   struct gfc_omp_namelist *next;
   locus where;
diff --git a/gcc/fortran/match.cc b/gcc/fortran/match.cc
index f3767c928a7..0cd78a57a2f 100644
--- a/gcc/fortran/match.cc
+++ b/gcc/fortran/match.cc
@@ -5551,7 +5551,7 @@  gfc_free_omp_namelist (gfc_omp_namelist *name, bool free_ns,
 {
   gfc_omp_namelist *n;
   gfc_expr *last_allocator = NULL;
-  char *last_init_str = NULL;
+  char *last_init_attr = NULL;
 
   for (; name; name = n)
     {
@@ -5575,11 +5575,11 @@  gfc_free_omp_namelist (gfc_omp_namelist *name, bool free_ns,
 	{ }  /* name->u2.traits_sym: shall not call gfc_free_symbol here. */
       else if (free_init)
 	{
-	  if (name->u.init.str != last_init_str)
+	  if (name->u.init.attr != last_init_attr)
 	    {
-	      last_init_str = name->u.init.str;
-	      free (name->u.init.str);
-	      free (name->u2.interop_int);
+	      last_init_attr = name->u.init.attr;
+	      free (name->u.init.attr);
+	      free (name->u2.init_interop_fr);
 	    }
 	}
       else if (name->u2.udr)
diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc
index 1145e2ff890..050409e00a0 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -1827,16 +1827,31 @@  error:
    where 'fr' takes an integer named constant or a string literal
    and 'attr takes a string literal, starting with 'ompx_')
 
-Document string + int format
-*/
+   For the foreign runtime identifiers, string values are converted to
+   their integer value; unknown string or integer values are set to 0.
+
+   For the simple syntax, pref_int_array contains alternatingly the
+   fr_id integer value and GOMP_INTEROP_IFR_SEPARATOR followed by a
+   GOMP_INTEROP_IFR_SEPARATOR as last item.
+   For the complex syntax, it contains the values associated with a
+   'fr(...)' followed by GOMP_INTEROP_IFR_SEPARATOR.  If there is no
+   'fr' in a curly-brace block, it is GOMP_INTEROP_IFR_NONE followed
+   by GOMP_INTEROP_IFR_SEPARATOR.  An additional GOMP_INTEROP_IFR_SEPARATOR
+   at the end terminates the array.
+
+   For attributes, if the simply syntax is used, it is NULL - likewise if no
+   'attr' appears.  For the complex syntax it is: For reach curly-brace block,
+   it is \0\0 is no attr appears and otherwise a concatenation (including
+   the \0) of all 'attr' strings followed by a tailing '\0'. At the end,
+   another '\0' follows.  */
 
 static match
-gfc_match_omp_prefer_type (char **pref_str, int *pref_str_len, int **pref_int_array)
+gfc_match_omp_prefer_type (char **fr_int_array, char **attr_str, int *attr_str_len)
 {
   gfc_expr *e;
-  size_t cnt = 0;
-  std::vector<int> int_list;
-  std::string pref_string;
+  int cnt_brace_grp = 0;
+  std::vector<char> int_list;
+  std::string attr_string;
   /* New syntax.  */
   if (gfc_peek_ascii_char () == '{')
     do
@@ -1846,8 +1861,8 @@  gfc_match_omp_prefer_type (char **pref_str, int *pref_str_len, int **pref_int_ar
 	    gfc_error ("Expected %<{%> at %C");
 	    return MATCH_ERROR;
 	  }
-	std::string attr;
 	bool fr_found = false;
+	bool attr_found = false;
 	do
 	  {
 	    if (gfc_match ("fr ( ") == MATCH_YES)
@@ -1859,99 +1874,129 @@  gfc_match_omp_prefer_type (char **pref_str, int *pref_str_len, int **pref_int_ar
 		    return MATCH_ERROR;
 		  }
 		fr_found = true;
-		gfc_symbol *sym = NULL;
-		e = NULL;
-		locus loc = gfc_current_locus;
-		if (gfc_match_symbol (&sym, 0) != MATCH_YES
-		    || gfc_match (" _") == MATCH_YES)
+		do
 		  {
-		    gfc_current_locus = loc;
-		    if (gfc_match_expr (&e) == MATCH_ERROR)
+		    if (gfc_match_expr (&e) != MATCH_YES)
 		      return MATCH_ERROR;
-		  }
-		if ((!sym && !e)
-		    || (e && (!gfc_resolve_expr (e)
-			      || e->expr_type != EXPR_CONSTANT
-			      || e->ts.type != BT_CHARACTER
-			      || e->ts.kind != gfc_default_character_kind
-			      || e->value.character.length == 0))
-		    || (sym && (sym->attr.flavor != FL_PARAMETER
-				|| sym->ts.type != BT_INTEGER
-				|| !mpz_fits_sint_p (sym->value->value.integer)
-				|| sym->attr.dimension)))
-		  {
-		    gfc_error ("Expected constant integer identifier or "
-			       "non-empty default-kind character literal at %L",
-			       &loc);
-		    gfc_free_expr (e);
+		    if (e->expr_type != EXPR_CONSTANT
+			|| e->ref != NULL
+			|| !gfc_resolve_expr (e)
+			|| (e->ts.type != BT_INTEGER
+			    && e->ts.type != BT_CHARACTER)
+			|| (e->ts.type == BT_INTEGER
+			    && (!e->symtree
+				|| e->symtree->n.sym->attr.flavor != FL_PARAMETER
+				|| !mpz_fits_sint_p (e->value.integer)))
+			|| (e->ts.type == BT_CHARACTER
+			    && (e->ts.kind != gfc_default_character_kind
+				|| e->value.character.length == 0)))
+		      {
+			gfc_error ("Expected scalar integer parameter or "
+				   "non-empty default-kind character literal "
+				   "at %L", &e->where);
+			gfc_free_expr (e);
+			return MATCH_ERROR;
+		      }
+		    gfc_gobble_whitespace ();
+		    int val;
+		    if (e->ts.type == BT_INTEGER)
+		      {
+			val = mpz_get_si (e->value.integer);
+			if (val < 1 || val > GOMP_INTEROP_IFR_LAST)
+			  {
+			    gfc_warning (OPT_Wopenmp,
+					 "Unknown foreign runtime identifier "
+					 "%qd at %L", val, &e->where);
+			    val = 0;
+			  }
+		      }
+		    else
+		      {
+			char *str = XALLOCAVEC (char,
+						e->value.character.length+1);
+			for (int i = 0; i < e->value.character.length + 1; i++)
+			  str[i] = e->value.character.string[i];
+			if (memchr (str, '\0', e->value.character.length) != 0)
+			  {
+			    gfc_error ("Unexpected null character in character "
+				       "literal at %L", &e->where);
+			    return MATCH_ERROR;
+			  }
+			val = omp_get_fr_id_from_name (str);
+			if (val == 0)
+			  gfc_warning (OPT_Wopenmp,
+				       "Unknown foreign runtime identifier %qs "
+				       "at %L", str, &e->where);
+		      }
+		    int_list.push_back (val);
+		    if (gfc_match (", ") == MATCH_YES)
+		      continue;
+		    if (gfc_match (") ") == MATCH_YES)
+		      break;
+		    gfc_error ("Expected %<,%> or %<)%> at %C");
 		    return MATCH_ERROR;
 		  }
-		if (sym)
-		  {
-		    for (size_t i = int_list.size(); i < cnt; ++i)
-		      int_list.push_back (0);
-		    int_list.push_back (mpz_get_si (sym->value->value.integer));
-		    pref_string += ' ';
-		    pref_string += '\0';
-		  }
-		else
-		  {
-		    char *str = XALLOCAVEC (char, e->value.character.length+1);
-		    for (int i = 0; i < e->value.character.length + 1; i++)
-		      str[i] = e->value.character.string[i];
-		   if (memchr (str, '\0', e->value.character.length) != 0)
-		     {
-		       gfc_error ("Unexpected null character in character "
-				  "literal at %L", &loc);
-		       return MATCH_ERROR;
-		     }
-		    pref_string += str;
-		    pref_string += '\0';
-		  }
+		while (true);
 	      }
 	    else if (gfc_match ("attr ( ") == MATCH_YES)
 	      {
-		locus loc = gfc_current_locus;
-		if (gfc_match_expr (&e) != MATCH_YES
-		    || e->expr_type != EXPR_CONSTANT
-		    || e->ts.type != BT_CHARACTER)
-		  {
-		    gfc_error ("Expected default-kind character literal at %L",
-			       &loc);
-		    gfc_free_expr (e);
-		    return MATCH_ERROR;
-		  }
-		char *str = XALLOCAVEC (char, e->value.character.length+1);
-		for (int i = 0; i < e->value.character.length + 1; i++)
-		  str[i] = e->value.character.string[i];
-		if (!startswith (str, "ompx_"))
-		  {
-		    gfc_error ("Character literal at %L must start with "
-			      "%<ompx_%>", &e->where);
-		    gfc_free_expr (e);
-		    return MATCH_ERROR;
-		  }
-		if (memchr (str, '\0', e->value.character.length) != 0
-		    || memchr (str, ',', e->value.character.length) != 0)
+		attr_found = true;
+		if (attr_string.empty ())
+		  for (int i = 0; i < cnt_brace_grp; ++i)
+		    {
+		      /* Add dummy elements for previous curly-brace blocks.  */
+		      attr_string += ' ';
+		      attr_string += '\0';
+		      attr_string += '\0';
+		    }
+		do
 		  {
-		    gfc_error ("Unexpected null or %<,%> character in "
-			       "character literal at %L", &e->where);
+		    if (gfc_match_expr (&e) != MATCH_YES)
+		      return MATCH_ERROR;
+		    if (e->expr_type != EXPR_CONSTANT
+			|| e->rank != 0
+			|| e->ts.type != BT_CHARACTER
+		    || e->ts.kind != gfc_default_character_kind)
+		      {
+			gfc_error ("Expected default-kind character literal "
+				   "at %L", &e->where);
+			gfc_free_expr (e);
+			return MATCH_ERROR;
+		      }
+		    gfc_gobble_whitespace ();
+		    char *str = XALLOCAVEC (char, e->value.character.length+1);
+		    for (int i = 0; i < e->value.character.length + 1; i++)
+		      str[i] = e->value.character.string[i];
+		    if (!startswith (str, "ompx_"))
+		      {
+			gfc_error ("Character literal at %L must start with "
+				   "%<ompx_%>", &e->where);
+			gfc_free_expr (e);
+			return MATCH_ERROR;
+		      }
+		    if (memchr (str, '\0', e->value.character.length) != 0
+			|| memchr (str, ',', e->value.character.length) != 0)
+		      {
+			gfc_error ("Unexpected null or %<,%> character in "
+				   "character literal at %L", &e->where);
+			return MATCH_ERROR;
+		      }
+		    attr_string += str;
+		    attr_string += '\0';
+		    if (gfc_match (", ") == MATCH_YES)
+		      continue;
+		    if (gfc_match (") ") == MATCH_YES)
+		      break;
+		    gfc_error ("Expected %<,%> or %<)%> at %C");
 		    return MATCH_ERROR;
 		  }
-		attr += str;
-		attr += '\0';
+		while (true);
 	      }
 	    else
 	      {
 		gfc_error ("Expected %<fr(%> or %<attr(%> at %C");
 		return MATCH_ERROR;
 	      }
-	    ++cnt;
-	    if (gfc_match (") ") != MATCH_YES)
-	      {
-		gfc_error ("Expected %<)%> at %C");
-		return MATCH_ERROR;
-	      }
 	    if (gfc_match (", ") == MATCH_YES)
 	      continue;
 	    if (gfc_match ("} ") == MATCH_YES)
@@ -1960,13 +2005,20 @@  gfc_match_omp_prefer_type (char **pref_str, int *pref_str_len, int **pref_int_ar
 	    return MATCH_ERROR;
 	  }
 	while (true);
+	++cnt_brace_grp;
 	if (!fr_found)
+	  int_list.push_back (GOMP_INTEROP_IFR_NONE);
+	int_list.push_back (GOMP_INTEROP_IFR_SEPARATOR);
+	if (!attr_string.empty ())
 	  {
-	    pref_string += ' ';
-	    pref_string += '\0';
+	    if (!attr_found)
+	      {
+		/* Dummy entry.  */
+		attr_string += ' ';
+		attr_string += '\0';
+	      }
+	    attr_string += '\0';
 	  }
-	pref_string += attr;
-	pref_string += '\0';
 
 	if (gfc_match (", ") == MATCH_YES)
 	  continue;
@@ -1982,6 +2034,7 @@  gfc_match_omp_prefer_type (char **pref_str, int *pref_str_len, int **pref_int_ar
 	if (gfc_match_expr (&e) != MATCH_YES)
 	  return MATCH_ERROR;
 	if (!gfc_resolve_expr (e)
+	    || e->rank != 0
 	    || e->expr_type != EXPR_CONSTANT
 	    || (e->ts.type != BT_INTEGER && e->ts.type != BT_CHARACTER)
 	    || (e->ts.type == BT_INTEGER
@@ -1990,17 +2043,23 @@  gfc_match_omp_prefer_type (char **pref_str, int *pref_str_len, int **pref_int_ar
 		&& (e->ts.kind != gfc_default_character_kind
 		    || e->value.character.length == 0)))
 	  {
-	    gfc_error ("Expected constant integer expression or non-empty "
-		       "default-kind character literal at %L", &e->where);
+	    gfc_error ("Expected constant scalar integer expression or "
+		       "non-empty default-kind character literal at %L", &e->where);
 	    gfc_free_expr (e);
 	    return MATCH_ERROR;
 	  }
+	gfc_gobble_whitespace ();
+	int val;
 	if (e->ts.type == BT_INTEGER)
 	  {
-	    for (size_t i = int_list.size(); i < cnt; ++i)
-	      int_list.push_back (0);
-	    int_list.push_back (mpz_get_si (e->value.integer));
-	    pref_string += ' ';
+	    val = mpz_get_si (e->value.integer);
+	    if (val < 1 || val > GOMP_INTEROP_IFR_LAST)
+	      {
+		gfc_warning (OPT_Wopenmp,
+			     "Unknown foreign runtime identifier %qd at %L",
+			     val, &e->where);
+		val = 0;
+	      }
 	  }
 	else
 	  {
@@ -2009,15 +2068,18 @@  gfc_match_omp_prefer_type (char **pref_str, int *pref_str_len, int **pref_int_ar
 	      str[i] = e->value.character.string[i];
 	    if (memchr (str, '\0', e->value.character.length) != 0)
 	      {
-		gfc_error ("Unexpected null character in character literal "
-			   "at %L", &e->where);
+		gfc_error ("Unexpected null character in character "
+			   "literal at %L", &e->where);
 		return MATCH_ERROR;
 	      }
-	    pref_string += str;
+	    val = omp_get_fr_id_from_name (str);
+	    if (val == 0)
+	      gfc_warning (OPT_Wopenmp,
+			   "Unknown foreign runtime identifier %qs at %L",
+			   str, &e->where);
 	  }
-	pref_string += '\0';
-	pref_string += '\0';
-	++cnt;
+	int_list.push_back (val);
+	int_list.push_back (GOMP_INTEROP_IFR_SEPARATOR);
 	gfc_free_expr (e);
 	if (gfc_match (", ") == MATCH_YES)
 	  continue;
@@ -2027,19 +2089,16 @@  gfc_match_omp_prefer_type (char **pref_str, int *pref_str_len, int **pref_int_ar
 	return MATCH_ERROR;
       }
     while (true);
-  if (!int_list.empty())
-    for (size_t i = int_list.size(); i < cnt; ++i)
-     int_list.push_back (0);
-
-  pref_string += '\0';
+  int_list.push_back (GOMP_INTEROP_IFR_SEPARATOR);
+  *fr_int_array = XNEWVEC (char, int_list.size ());
+  memcpy (*fr_int_array, int_list.data (), sizeof (char) * int_list.size ());
 
-  *pref_str_len = pref_string.length();
-  *pref_str = XNEWVEC (char, pref_string.length ());
-  memcpy (*pref_str, pref_string.data (), pref_string.length ());
-  if (!int_list.empty ())
+  if (!attr_string.empty ())
     {
-      *pref_int_array = XNEWVEC (int, cnt);
-      memcpy (*pref_int_array, int_list.data (), sizeof (int) * cnt);
+      attr_string += '\0';
+      *attr_str_len = attr_string.length();
+      *attr_str = XNEWVEC (char, attr_string.length ());
+      memcpy (*attr_str, attr_string.data (), attr_string.length ());
     }
   return MATCH_YES;
 }
@@ -2052,21 +2111,21 @@  static match
 gfc_match_omp_init (gfc_omp_namelist **list)
 {
   bool target = false, targetsync = false;
-  char *pref_str = NULL;
-  int pref_str_len = 0;
-  int *pref_int_array = NULL;
+  char *fr_int_array = NULL;
+  char *attr_str = NULL;
+  int attr_str_len = 0;
   match m;
   locus old_loc = gfc_current_locus;
   do {
        if (gfc_match ("prefer_type ( ") == MATCH_YES)
 	{
-	  if (pref_str)
+	  if (fr_int_array)
 	    {
 	      gfc_error ("Duplicate %<prefer_type%> modifier at %C");
 	      return MATCH_ERROR;
 	    }
-	  m = gfc_match_omp_prefer_type (&pref_str, &pref_str_len,
-					 &pref_int_array);
+	  m = gfc_match_omp_prefer_type (&fr_int_array, &attr_str,
+					 &attr_str_len);
 	  if (m != MATCH_YES)
 	    return m;
 	  if (gfc_match (", ") == MATCH_YES)
@@ -2084,7 +2143,7 @@  gfc_match_omp_init (gfc_omp_namelist **list)
 	  if (gfc_match (": ") == MATCH_YES)
 	    break;
 	  gfc_char_t c = gfc_peek_char ();
-	  if (!pref_str
+	  if (!fr_int_array
 	      && (c == ')'
 		  || (gfc_current_form != FORM_FREE
 		      && (c == '_' || ISALPHA (c)))))
@@ -2103,7 +2162,7 @@  gfc_match_omp_init (gfc_omp_namelist **list)
 	  if (gfc_match (": ") == MATCH_YES)
 	    break;
 	  gfc_char_t c = gfc_peek_char ();
-	  if (!pref_str
+	  if (!fr_int_array
 	      && (c == ')'
 		  || (gfc_current_form != FORM_FREE
 		      && (c == '_' || ISALPHA (c)))))
@@ -2114,7 +2173,7 @@  gfc_match_omp_init (gfc_omp_namelist **list)
 	  gfc_error ("Expected %<,%> or %<:%> at %C");
 	  return MATCH_ERROR;
 	}
-      if (pref_str)
+      if (fr_int_array)
 	{
 	  gfc_error ("Expected %<target%> or %<targetsync%> at %C");
 	  return MATCH_ERROR;
@@ -2131,9 +2190,9 @@  gfc_match_omp_init (gfc_omp_namelist **list)
    {
      n->u.init.target = target;
      n->u.init.targetsync = targetsync;
-     n->u.init.str = pref_str;
-     n->u.init.len = pref_str_len;
-     n->u2.interop_int = pref_int_array;
+     n->u.init.attr = attr_str;
+     n->u.init.len = attr_str_len;
+     n->u2.init_interop_fr = fr_int_array;
    }
  return MATCH_YES;
 }
diff --git a/gcc/omp-api.h b/gcc/omp-api.h
index 0884e51c61c..1b877f257f0 100644
--- a/gcc/omp-api.h
+++ b/gcc/omp-api.h
@@ -29,4 +29,7 @@  along with GCC; see the file COPYING3.  If not see
 extern bool omp_runtime_api_procname (const char *name);
 extern bool omp_runtime_api_call (const_tree fndecl);
 
+extern int omp_get_fr_id_from_name (const char *);
+extern const char *omp_get_name_from_fr_id (int);
+
 #endif
diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc
index aaa179afe13..de91ba8a4a7 100644
--- a/gcc/omp-general.cc
+++ b/gcc/omp-general.cc
@@ -3385,6 +3385,35 @@  omp_runtime_api_call (const_tree fndecl)
   return omp_runtime_api_procname (IDENTIFIER_POINTER (declname));
 }
 
+/* See "Additional Definitions for the OpenMP API Specification" document;
+   associated IDs are 1, 2, ...  */
+static const char* omp_interop_fr_str[] = {"cuda", "cuda_driver", "opencl",
+					   "sycl", "hip", "level_zero", "hsa"};
+
+/* Returns the foreign-runtime ID if found or 0 otherwise.  */
+
+int
+omp_get_fr_id_from_name (const char *str)
+{
+  static_assert (GOMP_INTEROP_IFR_LAST == ARRAY_SIZE (omp_interop_fr_str), "");
+
+  for (unsigned i = 0; i < ARRAY_SIZE (omp_interop_fr_str); ++i)
+    if (!strcmp (str, omp_interop_fr_str[i]))
+      return i + 1;
+  return 0;
+}
+
+/* Returns the string value to a foreign-runtime integer value or NULL if value
+   is not known.  */
+
+const char *
+omp_get_name_from_fr_id (int fr_id)
+{
+  if (fr_id < 1 || fr_id > (int) ARRAY_SIZE (omp_interop_fr_str))
+    return NULL;
+  return omp_interop_fr_str[fr_id-1];
+}
+
 namespace omp_addr_tokenizer {
 
 /* We scan an expression by recursive descent, and build a vector of
diff --git a/gcc/testsuite/gfortran.dg/gomp/interop-1.f90 b/gcc/testsuite/gfortran.dg/gomp/interop-1.f90
index bbb1dea1be6..8c99fc97f88 100644
--- a/gcc/testsuite/gfortran.dg/gomp/interop-1.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/interop-1.f90
@@ -28,6 +28,8 @@  implicit none
 
 !$omp requires reverse_offload
 
+integer(omp_interop_fr_kind), parameter :: ifr_array(2) = [omp_ifr_cuda, omp_ifr_hip]
+
 integer(omp_interop_kind) :: obj1, obj2, obj3, obj4, obj5
 integer :: x
 
@@ -37,7 +39,7 @@  integer :: x
 !$omp&        destroy(obj2, obj3) depend(inout: x) use(obj4, obj5) device(device_num: 0)
 
 !$omp assume contains(interop)
-  !$omp interop init(prefer_type("cu"//char(1)//"da") : obj3)
+  !$omp interop init(prefer_type("cu"//char(1)//"da") : obj3)  ! { dg-warning "Unknown foreign runtime identifier 'cu\\\\x01da'" }
 !$omp end assume
 
 !$omp interop init(prefer_type("cu"//char(0)//"da") : obj3) ! { dg-error "Unexpected null character in character literal" }
@@ -52,11 +54,29 @@  integer :: x
 
 !$omp interop init ( target , prefer_type( { fr("hsa"), attr("ompx_nothing") , fr("hsa" ) }) :obj1) ! { dg-error "Duplicated 'fr' preference-selector-name" }
 
-!$omp interop init ( prefer_type( 4, omp_ifr_hip*4) : obj1)
-!$omp interop init ( prefer_type( sin(3.3) : obj1)  ! { dg-error "Expected constant integer expression or non-empty default-kind character literal" }
-!$omp interop init ( prefer_type( {fr(4) }) : obj1) ! { dg-error "Expected constant integer identifier or non-empty default-kind character literal" }
-!$omp interop init ( prefer_type( {fr(4_"cuda") }) : obj1) ! { dg-error "Expected constant integer identifier or non-empty default-kind character literal" }
+!$omp interop init ( prefer_type( 4, omp_ifr_hip*4) : obj1)  ! { dg-warning "Unknown foreign runtime identifier '20'" }
+!$omp interop init ( prefer_type( sin(3.3) : obj1)  ! { dg-error "Expected constant scalar integer expression or non-empty default-kind character literal" }
+!$omp interop init ( prefer_type( {fr(4 ) }) : obj1) ! { dg-error "Expected scalar integer parameter or non-empty default-kind character literal" }
+!$omp interop init ( prefer_type( {fr(4_"cuda" ) }) : obj1) ! { dg-error "Expected scalar integer parameter or non-empty default-kind character literal" }
 !$omp interop init ( prefer_type( {fr(c_char_"cuda") }) : obj1) ! OK
-!$omp interop init ( prefer_type( {fr(1_"cuda") }) : obj1) ! OK
+!$omp interop init ( prefer_type( {fr(1_"cuda" ) }) : obj1) ! OK
+!$omp interop init ( prefer_type( {fr(omp_ifr_level_zero ) }, {fr(omp_ifr_hip)}) : obj1) ! OK
+!$omp interop init ( prefer_type( {fr(omp_ifr_level_zero + 1) }) : obj1) ! { dg-error "Expected scalar integer parameter or non-empty default-kind character literal" }
+!$omp interop init ( prefer_type( {fr(x) }) : obj1) ! { dg-error "Expected scalar integer parameter or non-empty default-kind character literal" }
+!$omp interop init ( prefer_type( {fr(ifr_array ) }) : obj1) ! { dg-error "Expected scalar integer parameter or non-empty default-kind character literal" }
+!$omp interop init ( prefer_type( {fr(ifr_array(1) ) }) : obj1) ! { dg-error "Expected scalar integer parameter or non-empty default-kind character literal" }
+
+!$omp interop init ( prefer_type( omp_ifr_level_zero, omp_ifr_hip ) : obj1) ! OK
+!$omp interop init ( prefer_type( omp_ifr_level_zero +1 ) : obj1) ! OK
+!$omp interop init ( prefer_type( x ) : obj1) ! { dg-error "Expected constant scalar integer expression or non-empty default-kind character literal" }
+!$omp interop init ( prefer_type( ifr_array ) : obj1) ! { dg-error "Expected constant scalar integer expression or non-empty default-kind character literal" }
+!$omp interop init ( prefer_type( ifr_array(2) ) : obj1) ! OK
+
+!$omp interop init ( prefer_type( 4, omp_ifr_hip*4) : obj1) ! { dg-warning "Unknown foreign runtime identifier '20'" }
+!$omp interop init ( prefer_type( 4, 1, 3) : obj1)
+
+!$omp interop init ( prefer_type( {fr("cuda","sycl") }, {fr(omp_ifr_hsa,omp_ifr_level_zero)} , {attr("ompx_a") } , {fr(omp_ifr_hip) }) : obj1)
+!$omp interop init ( prefer_type( {fr("cuda","sycl"), attr("ompx_1", "ompx_2"), attr("ompx_3") }, {attr("ompx_4", "ompx_5"),fr(omp_ifr_hsa,omp_ifr_level_zero)} ) : obj1)
+!$omp interop init ( prefer_type( { fr("cuda","sycl"), attr("ompx_1") }, {fr(omp_ifr_hsa,omp_ifr_level_zero)} , {attr("ompx_a") } ) : obj1)
 
 end
diff --git a/gcc/testsuite/gfortran.dg/gomp/interop-2.f90 b/gcc/testsuite/gfortran.dg/gomp/interop-2.f90
index c7673a662d0..f3391bf88f0 100644
--- a/gcc/testsuite/gfortran.dg/gomp/interop-2.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/interop-2.f90
@@ -26,7 +26,7 @@  implicit none
 integer(omp_interop_kind) :: obj1, obj2, obj3, obj4, obj5
 integer :: x
 
-!$omp interop init ( prefer_type( {fr(1_"") }) : obj1) ! { dg-error "Expected constant integer identifier or non-empty default-kind character literal" }
+!$omp interop init ( prefer_type( {fr(1_"") }) : obj1) ! { dg-error "Expected scalar integer parameter or non-empty default-kind character literal" }
 !$omp interop init ( prefer_type( {fr(1_"hip") , attr(omp_ifr_cuda) }) : obj1) ! { dg-error "Expected default-kind character literal" }
 
 !$omp interop init ( prefer_type( {fr(1_"hip") , attr("myooption") }) : obj1) ! { dg-error "Character literal at .1. must start with 'ompx_'" }
diff --git a/gcc/testsuite/gfortran.dg/gomp/interop-3.f90 b/gcc/testsuite/gfortran.dg/gomp/interop-3.f90
index a6d2cc460fb..462ed4f2e4b 100644
--- a/gcc/testsuite/gfortran.dg/gomp/interop-3.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/interop-3.f90
@@ -33,7 +33,7 @@  integer :: x
 !$omp&        destroy(obj2, obj3) depend(inout: x) use(obj4, obj5) device(device_num: 0)
 
 !$omp assume contains(interop)
-  !$omp interop init(prefer_type("cu"//char(1)//"da") : obj3)
+  !$omp interop init(prefer_type("cu"//char(1)//"da") : obj3)  ! { dg-warning "Unknown foreign runtime identifier 'cu\\\\x01da'" }
 !$omp end assume
 
 !$omp interop init(obj1, obj2, obj1), use(obj4) destroy(obj4)
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 775fc4e8f64..0fae337f9d6 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -388,6 +388,11 @@  enum gomp_map_kind
 #define GOMP_REQUIRES_REVERSE_OFFLOAD       0x80
 #define GOMP_REQUIRES_TARGET_USED           0x200
 
+/* Interop foreign-runtime data.  */
+#define GOMP_INTEROP_IFR_LAST	7
+#define GOMP_INTEROP_IFR_SEPARATOR -1
+#define GOMP_INTEROP_IFR_NONE -2
+
 /* HSA specific data structures.  */
 
 /* Identifiers of device-specific target arguments.  */