diff mbox series

[fortran] Extend the builtin directive

Message ID f36ea67a-c6a4-f3b1-4f7b-68967ace6cf2@arm.com
State New
Headers show
Series [fortran] Extend the builtin directive | expand

Commit Message

Szabolcs Nagy Nov. 14, 2019, 8:29 p.m. UTC
The builtin directive allows specifying the simd attribute for a builtin
function. Similarly how the C language simd attribtue got extended to
allow declaring a specific vector variant, update the fortran builtin
directive too.

Before the patch, only the masking (inbranch/notinbranch) could be specified,
when declaring the availability of vector variants, e.g.:

 !GCC$ builtin (expf) attributes simd (notinbranch) if('x86_64')

now the simdlen and simdabi (aka ISA) can be specified too, and a different
name may be used instead of the vector ABI name, e.g.:

 !GCC$ builtin (expf) attributes simd (notinbranch, 4, 'b', 'vexpf') if('x86_64')

Tested on aarch64-linux-gnu.

The C language change is at

https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01288.html

Note: I don't have much fortran experience, so i'm not sure if the syntax
makes sense or if i modified the frontend reasonably.

2019-11-14  Szabolcs Nagy  <szabolcs.nagy@arm.com>

	* gfortran.h (struct gfc_vect_builtin): Define.
	* decl.c (gfc_match_gcc_builtin): Parse new flags.
	* trans-intrinsic.c (add_simd_flag_for_built_in): Update.
	(gfc_adjust_builtins): Update.

gcc/testsuite/ChangeLog:

2019-11-14  Szabolcs Nagy  <szabolcs.nagy@arm.com>

	* gfortran.dg/simd-builtins-9.f90: New test.
	* gfortran.dg/simd-builtins-9.h: New test.
	* gfortran.dg/simd-builtins-10.f90: New test.
	* gfortran.dg/simd-builtins-10.h: New test.

Comments

Szabolcs Nagy Nov. 15, 2019, 1:42 p.m. UTC | #1
> +++ b/gcc/testsuite/gfortran.dg/simd-builtins-10.h
> @@ -0,0 +1,6 @@
> +!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'n', 'vec_sinf') if('aarch64')
> +!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'n', 'vec_sinf') if('aarch64_be')
> +!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'n', 'vec_sinf') if('aarch64_ilp32')
> +!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'n', 'vec_sinf') if('aarch64_be_ilp32')
> +!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'b', 'vec_sinf') if('x64_64')

should be if('x86_64'),
will fix it in the next version of the patch.

> +!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'b', 'vec_sinf') if('i386')
Tobias Burnus Nov. 15, 2019, 4:09 p.m. UTC | #2
On 11/14/19 9:29 PM, Szabolcs Nagy wrote:
> The builtin directive allows specifying the simd attribute for a builtin function. Similarly how the C language simd attribtue got extended to allow declaring a specific vector variant, update the fortran builtin directive too.

Sounds useful. For the Fortran side, I think it makes sense to wait 
until the C/C++ side has been solved. (See thread labelled "Extend the 
simd function attribute".)

However, I just noted:

> +      if (gfc_match (" , ") == MATCH_YES)
> +	if (gfc_match (" '%n'", &simdabi) != MATCH_YES)

This explicitly requires that the string is quoted using single quotes 
(') and not double quotes ("). I think that's acceptable for this 
special-purpose use, but, normally, Fortran permits both characters as 
string/character delimiter.

Cheers,

Tobias
diff mbox series

Patch

diff --git a/gcc/fortran/decl.c b/gcc/fortran/decl.c
index 7858973cc20..dab8a323148 100644
--- a/gcc/fortran/decl.c
+++ b/gcc/fortran/decl.c
@@ -105,7 +105,7 @@  bool directive_vector = false;
 bool directive_novector = false;
 
 /* Map of middle-end built-ins that should be vectorized.  */
-hash_map<nofree_string_hash, int> *gfc_vectorized_builtins;
+hash_map<nofree_string_hash, gfc_vect_builtin> *gfc_vectorized_builtins;
 
 /* If a kind expression of a component of a parameterized derived type is
    parameterized, temporarily store the expression here.  */
@@ -11600,9 +11600,13 @@  gfc_match_gcc_unroll (void)
 /* Match a !GCC$ builtin (b) attributes simd flags if('target') form:
 
    The parameter b is name of a middle-end built-in.
-   FLAGS is optional and must be one of:
-     - (inbranch)
-     - (notinbranch)
+   FLAGS is optional and must be of the form:
+     (mask)
+     (mask, simdlen)
+     (mask, simdlen, 'simdabi')
+     (mask, simdlen, 'simdabi', 'name')
+   where mask is inbranch or notinbranch, simdlen is an integer, simdabi
+   and name are strings.
 
    IF('target') is optional and TARGET is a name of a multilib ABI.
 
@@ -11613,15 +11617,44 @@  gfc_match_gcc_builtin (void)
 {
   char builtin[GFC_MAX_SYMBOL_LEN + 1];
   char target[GFC_MAX_SYMBOL_LEN + 1];
+  char simdabi[GFC_MAX_SYMBOL_LEN + 1] = "";
+  char name[GFC_MAX_SYMBOL_LEN + 1] = "";
+  bool inbranch;
+  bool flags = false;
+  int simdlen = 0;
 
   if (gfc_match (" ( %n ) attributes simd", builtin) != MATCH_YES)
     return MATCH_ERROR;
 
-  gfc_simd_clause clause = SIMD_NONE;
-  if (gfc_match (" ( notinbranch ) ") == MATCH_YES)
-    clause = SIMD_NOTINBRANCH;
-  else if (gfc_match (" ( inbranch ) ") == MATCH_YES)
-    clause = SIMD_INBRANCH;
+  if (gfc_match (" ( ") == MATCH_YES)
+    {
+      flags = true;
+      if (gfc_match ("notinbranch") == MATCH_YES)
+	inbranch = false;
+      else if (gfc_match ("inbranch") == MATCH_YES)
+	inbranch = true;
+      else
+	{
+syntax_error:
+	  gfc_error ("Syntax error in !GCC$ BUILTIN directive at %C");
+	  return MATCH_ERROR;
+	}
+
+      if (gfc_match (" , ") == MATCH_YES)
+	if (gfc_match_small_int (&simdlen) != MATCH_YES || simdlen < 0)
+	  goto syntax_error;
+
+      if (gfc_match (" , ") == MATCH_YES)
+	if (gfc_match (" '%n'", &simdabi) != MATCH_YES)
+	  goto syntax_error;
+
+      if (gfc_match (" , ") == MATCH_YES)
+	if (gfc_match (" '%n'", &name) != MATCH_YES)
+	  goto syntax_error;
+
+      if (gfc_match (" ) ") != MATCH_YES)
+	goto syntax_error;
+    }
 
   if (gfc_match (" if ( '%n' ) ", target) == MATCH_YES)
     {
@@ -11631,16 +11664,27 @@  gfc_match_gcc_builtin (void)
     }
 
   if (gfc_vectorized_builtins == NULL)
-    gfc_vectorized_builtins = new hash_map<nofree_string_hash, int> ();
+    gfc_vectorized_builtins =
+      new hash_map<nofree_string_hash, gfc_vect_builtin> ();
 
   char *r = XNEWVEC (char, strlen (builtin) + 32);
   sprintf (r, "__builtin_%s", builtin);
 
   bool existed;
-  int &value = gfc_vectorized_builtins->get_or_insert (r, &existed);
-  value |= clause;
+  gfc_vect_builtin *v = &gfc_vectorized_builtins->get_or_insert (r, &existed);
   if (existed)
-    free (r);
+    {
+      free (r);
+      gfc_vect_builtin *next = v->next;
+      v->next = new gfc_vect_builtin;
+      v = v->next;
+      v->next = next;
+    }
+  v->flags = flags;
+  v->inbranch = inbranch;
+  v->simdlen = simdlen;
+  v->simdabi = simdabi[0] ? xstrdup (simdabi) : 0;
+  v->name = name[0] ? xstrdup (name) : 0;
 
   return MATCH_YES;
 }
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 920acdafc6b..56becb207b2 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -2812,26 +2812,18 @@  extern bool directive_ivdep;
 extern bool directive_vector;
 extern bool directive_novector;
 
-/* SIMD clause enum.  */
-enum gfc_simd_clause
-{
-  SIMD_NONE = (1 << 0),
-  SIMD_INBRANCH = (1 << 1),
-  SIMD_NOTINBRANCH = (1 << 2)
-};
-
-/* Tuple for parsing of vectorized built-ins.  */
-struct gfc_vect_builtin_tuple
-{
-  gfc_vect_builtin_tuple (const char *n, gfc_simd_clause t)
-    : name (n), simd_type (t) {}
-
-  const char *name;
-  gfc_simd_clause simd_type;
+struct gfc_vect_builtin
+{
+  gfc_vect_builtin *next;
+  char *name;
+  char *simdabi;
+  bool flags;
+  bool inbranch;
+  unsigned int simdlen;
 };
 
 /* Map of middle-end built-ins that should be vectorized.  */
-extern hash_map<nofree_string_hash, int> *gfc_vectorized_builtins;
+extern hash_map<nofree_string_hash, gfc_vect_builtin> *gfc_vectorized_builtins;
 
 /* Handling Parameterized Derived Types  */
 bool gfc_insert_kind_parameter_exprs (gfc_expr *);
diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi
index a34ac5aa1bf..05d3a93cb94 100644
--- a/gcc/fortran/gfortran.texi
+++ b/gcc/fortran/gfortran.texi
@@ -3671,12 +3671,25 @@  The syntax of the directive is
 
 You can use this directive to define which middle-end built-ins provide vector
 implementations.  @code{B} is name of the middle-end built-in.  @code{FLAGS}
-are optional and must be either "(inbranch)" or "(notinbranch)".
+are optional and must follow one of the forms
+@itemize
+@item @code{(mask)}
+@item @code{(mask, simdlen)}
+@item @code{(mask, simdlen, 'simdabi')}
+@item @code{(mask, simdlen, 'simdabi', 'name')}
+@end itemize
+where @code{mask} is either inbranch or notinbranch, simdlen is a non-negative
+integer, simdabi is a target specific ISA selector and name is the symbol
+name of the vector function (when not specified the name is generated according
+to the target Vector ABI document, the name is only valid to specify if the
+directive specifies a single vector funtion variant).
+
 @code{IF} statement is optional and is used to filter multilib ABIs
 for the built-in that should be vectorized.  Example usage:
 
 @smallexample
 !GCC$ builtin (sinf) attributes simd (notinbranch) if('x86_64')
+!GCC$ builtin (cosf) attributes simd (notinbranch, 4, 'b') if('x86_64')
 @end smallexample
 
 The purpose of the directive is to provide an API among the GCC compiler and
diff --git a/gcc/fortran/trans-intrinsic.c b/gcc/fortran/trans-intrinsic.c
index c2e0533393a..d2ef3c43892 100644
--- a/gcc/fortran/trans-intrinsic.c
+++ b/gcc/fortran/trans-intrinsic.c
@@ -608,29 +608,45 @@  add_simd_flag_for_built_in (tree fndecl)
     return;
 
   const char *name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
-  int *clauses = gfc_vectorized_builtins->get (name);
-  if (clauses)
+  gfc_vect_builtin *v = gfc_vectorized_builtins->get (name);
+  for (; v != NULL; v = v->next)
     {
-      for (unsigned i = 0; i < 3; i++)
-	if (*clauses & (1 << i))
-	  {
-	    gfc_simd_clause simd_type = (gfc_simd_clause)*clauses;
-	    tree omp_clause = NULL_TREE;
-	    if (simd_type == SIMD_NONE)
-	      ; /* No SIMD clause.  */
-	    else
-	      {
-		omp_clause_code code
-		  = (simd_type == SIMD_INBRANCH
-		     ? OMP_CLAUSE_INBRANCH : OMP_CLAUSE_NOTINBRANCH);
-		omp_clause = build_omp_clause (UNKNOWN_LOCATION, code);
-		omp_clause = build_tree_list (NULL_TREE, omp_clause);
-	      }
-
-	    DECL_ATTRIBUTES (fndecl)
-	      = tree_cons (get_identifier ("omp declare simd"), omp_clause,
-			   DECL_ATTRIBUTES (fndecl));
-	  }
+      tree t = NULL_TREE;
+      if (v->flags)
+	{
+	  omp_clause_code code
+	    = (v->inbranch ? OMP_CLAUSE_INBRANCH : OMP_CLAUSE_NOTINBRANCH);
+	  tree c = build_omp_clause (UNKNOWN_LOCATION, code);
+	  t = c;
+	  if (v->simdlen != 0)
+	    {
+	      c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE_SIMDLEN);
+	      OMP_CLAUSE_SIMDLEN_EXPR (c)
+		= build_int_cst (integer_type_node, v->simdlen);
+	      OMP_CLAUSE_CHAIN (c) = t;
+	      t = c;
+	    }
+	  if (v->simdabi != NULL)
+	    {
+	      c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__SIMDABI_);
+	      OMP_CLAUSE__SIMDABI__EXPR (c)
+		= build_string (strlen (v->simdabi) + 1, v->simdabi);
+	      OMP_CLAUSE_CHAIN (c) = t;
+	      t = c;
+	    }
+	  if (v->name != NULL)
+	    {
+	      c = build_omp_clause (UNKNOWN_LOCATION, OMP_CLAUSE__SIMDNAME_);
+	      OMP_CLAUSE__SIMDNAME__EXPR (c)
+		= build_string (strlen (v->name) + 1, v->name);
+	      OMP_CLAUSE_CHAIN (c) = t;
+	      t = c;
+	    }
+	  t = build_tree_list (NULL_TREE, t);
+	}
+      DECL_ATTRIBUTES (fndecl)
+        = tree_cons (get_identifier ("omp declare simd"), t,
+		     DECL_ATTRIBUTES (fndecl));
     }
 }
 
@@ -659,10 +675,24 @@  gfc_adjust_builtins (void)
   /* Release all strings.  */
   if (gfc_vectorized_builtins != NULL)
     {
-      for (hash_map<nofree_string_hash, int>::iterator it
+      for (hash_map<nofree_string_hash, gfc_vect_builtin>::iterator it
 	   = gfc_vectorized_builtins->begin ();
 	   it != gfc_vectorized_builtins->end (); ++it)
-	free (CONST_CAST (char *, (*it).first));
+	{
+	  free (CONST_CAST (char *, (*it).first));
+	  gfc_vect_builtin *v = &(*it).second;
+	  free (v->name);
+	  free (v->simdabi);
+	  v = v->next;
+	  while (v)
+	    {
+	      gfc_vect_builtin *next = v->next;
+	      free (v->name);
+	      free (v->simdabi);
+	      delete v;
+	      v = next;
+	    }
+	}
 
       delete gfc_vectorized_builtins;
       gfc_vectorized_builtins = NULL;
diff --git a/gcc/testsuite/gfortran.dg/simd-builtins-10.f90 b/gcc/testsuite/gfortran.dg/simd-builtins-10.f90
new file mode 100644
index 00000000000..59ae7bf0c18
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/simd-builtins-10.f90
@@ -0,0 +1,18 @@ 
+! { dg-do compile { target { aarch64*-*-linux* x86_64-*-* i?86-*-* } } }
+! { dg-additional-options "-nostdinc -Ofast -fpre-include=simd-builtins-10.h -fdump-tree-optimized" }
+
+program test_overloaded_intrinsic
+  real(4) :: x4(3200), y4(3200)
+  real(8) :: x8(3200), y8(3200)
+
+  y4 = sin(x4)
+  print *, y4
+
+  y8 = sin(x8)
+  print *, y8
+end
+
+! { dg-final { scan-tree-dump "sinf.simdclone" "optimized" } } */
+! { dg-final { scan-tree-dump-not "sin.simdclone" "optimized" } } */
+
+! { dg-final { scan-assembler "vec_sinf" } } */
diff --git a/gcc/testsuite/gfortran.dg/simd-builtins-10.h b/gcc/testsuite/gfortran.dg/simd-builtins-10.h
new file mode 100644
index 00000000000..0936b134cb0
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/simd-builtins-10.h
@@ -0,0 +1,6 @@ 
+!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'n', 'vec_sinf') if('aarch64')
+!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'n', 'vec_sinf') if('aarch64_be')
+!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'n', 'vec_sinf') if('aarch64_ilp32')
+!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'n', 'vec_sinf') if('aarch64_be_ilp32')
+!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'b', 'vec_sinf') if('x64_64')
+!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'b', 'vec_sinf') if('i386')
diff --git a/gcc/testsuite/gfortran.dg/simd-builtins-9.f90 b/gcc/testsuite/gfortran.dg/simd-builtins-9.f90
new file mode 100644
index 00000000000..0ffae5b14d0
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/simd-builtins-9.f90
@@ -0,0 +1,19 @@ 
+! { dg-do compile { target { aarch64*-*-linux* } } }
+! { dg-additional-options "-nostdinc -Ofast -fpre-include=simd-builtins-9.h -fdump-tree-optimized" }
+
+program test_overloaded_intrinsic
+  real(4) :: x4(3200), y4(3200)
+  real(8) :: x8(3200), y8(3200)
+
+  y4 = sin(x4)
+  print *, y4
+
+  y8 = sin(x8)
+  print *, y8
+end
+
+! { dg-final { scan-tree-dump "sinf.simdclone" "optimized" { target ilp32 } } } */
+! { dg-final { scan-tree-dump-not "sin.simdclone" "optimized" { target ilp32 } } } */
+
+! { dg-final { scan-tree-dump "sin.simdclone" "optimized" { target lp64 } } } */
+! { dg-final { scan-tree-dump-not "sinf.simdclone" "optimized" { target lp64  } } } */
diff --git a/gcc/testsuite/gfortran.dg/simd-builtins-9.h b/gcc/testsuite/gfortran.dg/simd-builtins-9.h
new file mode 100644
index 00000000000..1f50834d869
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/simd-builtins-9.h
@@ -0,0 +1,4 @@ 
+!GCC$ builtin (sin) attributes simd (notinbranch, 2, 'n') if('aarch64')
+!GCC$ builtin (sin) attributes simd (notinbranch, 2, 'n') if('aarch64_be')
+!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'n') if('aarch64_ilp32')
+!GCC$ builtin (sinf) attributes simd (notinbranch, 4, 'n') if('aarch64_be_ilp32')