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.n...@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.n...@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.
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')