Alfie Richards <alfie.richards@arm.com> writes:
> This changes behavior of target_clones and target_version attributes
> to be inline with what is specified in the Arm C Language Extension.
>
> Notably this changes the scope and signature of multiversioned functions
> to that of the default version, and changes the resolver to be
> created at the implementation of the default version.
>
> This is achieved by changing the C++ front end to no longer resolve any
> non-default version decls in lookup, and by moving dipatching
> for default_target sets to reuse the dispatching logic for target_clones
> in multiple_target.cc.
>
> This also fixes https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118313
> for aarch64 and riscv.
>
> This changes the behavior of both the aarch64, and riscv targets.
>
> gcc/ChangeLog:
>
> * cgraphunit.cc (analyze_functions): Add dependency from default node
> to non-default versions.
> * ipa.cc (symbol_table::remove_unreachable_nodes): Ditto.
> * multiple_target.cc (ipa_target_clone): Change logic to conditionally
> dispatch target_clones and to dispatch some target_version sets.
>
> gcc/cp/ChangeLog:
>
> * call.cc (add_candidates): For target_version semantics don't resolve
> non-default versions.
> * class.cc (resolve_address_of_overloaded_function): Ditto.
> * cp-gimplify.cc (cp_genericize_r): For target_version semantics don't
> redirect calls to versioned functions (done later at
> multiple_target.cc.)
> * decl.cc (start_decl): Mangle and mark all non-default function
> decls.
> (start_preparsed_function): Ditto.
> * typeck.cc (cp_build_function_call_vec): Add error if target has no
> default implementation.
>
> gcc/testsuite/ChangeLog:
>
> * g++.target/aarch64/mv-1.C: Change for new semantics.
> * g++.target/aarch64/mv-symbols2.C: Ditto.
> * g++.target/aarch64/mv-symbols3.C: Ditto.
> * g++.target/aarch64/mv-symbols4.C: Ditto.
> * g++.target/aarch64/mv-symbols5.C: Ditto.
> * g++.target/aarch64/mvc-symbols3.C: Ditto.
> * g++.target/riscv/mv-symbols2.C: Ditto.
> * g++.target/riscv/mv-symbols3.C: Ditto.
> * g++.target/riscv/mv-symbols4.C: Ditto.
> * g++.target/riscv/mv-symbols5.C: Ditto.
> * g++.target/riscv/mvc-symbols3.C: Ditto.
> * g++.target/aarch64/mv-symbols10.C: New test.
> * g++.target/aarch64/mv-symbols11.C: New test.
> * g++.target/aarch64/mv-symbols12.C: New test.
> * g++.target/aarch64/mv-symbols14.C: New test.
> * g++.target/aarch64/mv-symbols15.C: New test.
> * g++.target/aarch64/mv-symbols6.C: New test.
> * g++.target/aarch64/mv-symbols8.C: New test.
> * g++.target/aarch64/mv-symbols9.C: New test.
Nice! I agree that the tests test for what the ACLE asks for.
Some comments below.
> ---
> gcc/cgraphunit.cc | 9 ++++
> gcc/cp/call.cc | 8 ++++
> gcc/cp/class.cc | 11 ++++-
> gcc/cp/cp-gimplify.cc | 6 ++-
> gcc/cp/decl.cc | 24 ++++++++++
> gcc/cp/typeck.cc | 8 ++++
> gcc/ipa.cc | 11 +++++
> gcc/multiple_target.cc | 13 ++++-
> gcc/testsuite/g++.target/aarch64/mv-1.C | 4 ++
> .../g++.target/aarch64/mv-symbols10.C | 43 +++++++++++++++++
> .../g++.target/aarch64/mv-symbols11.C | 27 +++++++++++
> .../g++.target/aarch64/mv-symbols12.C | 18 +++++++
> .../g++.target/aarch64/mv-symbols14.C | 16 +++++++
> .../g++.target/aarch64/mv-symbols15.C | 16 +++++++
> .../g++.target/aarch64/mv-symbols2.C | 12 ++---
> .../g++.target/aarch64/mv-symbols3.C | 6 +--
> .../g++.target/aarch64/mv-symbols4.C | 6 +--
> .../g++.target/aarch64/mv-symbols5.C | 6 +--
> .../g++.target/aarch64/mv-symbols6.C | 23 +++++++++
> .../g++.target/aarch64/mv-symbols8.C | 48 +++++++++++++++++++
> .../g++.target/aarch64/mv-symbols9.C | 46 ++++++++++++++++++
> .../g++.target/aarch64/mvc-symbols3.C | 12 ++---
> gcc/testsuite/g++.target/riscv/mv-symbols2.C | 12 ++---
> gcc/testsuite/g++.target/riscv/mv-symbols3.C | 6 +--
> gcc/testsuite/g++.target/riscv/mv-symbols4.C | 6 +--
> gcc/testsuite/g++.target/riscv/mv-symbols5.C | 6 +--
> gcc/testsuite/g++.target/riscv/mvc-symbols3.C | 12 ++---
> 27 files changed, 368 insertions(+), 47 deletions(-)
> create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols10.C
> create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols11.C
> create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols12.C
> create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols14.C
> create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols15.C
> create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols6.C
> create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols8.C
> create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols9.C
>
> diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
> index 82f205488e9..f7f8957e618 100644
> --- a/gcc/cgraphunit.cc
> +++ b/gcc/cgraphunit.cc
> @@ -1264,6 +1264,15 @@ analyze_functions (bool first_time)
> if (!cnode->analyzed)
> cnode->analyze ();
>
> + /* A reference to a default node in a funciton set implies a
function
> + refrence to all versions in the set. */
reference
> + if (cnode->function_version ()
> + && is_function_default_version (node->decl))
> + for (cgraph_function_version_info *fvi
> + = cnode->function_version ()->next;
> + fvi; fvi = fvi->next)
> + enqueue_node (fvi->this_node);
> +
Here too it seems worth caching cnode->function_version ().
> for (edge = cnode->callees; edge; edge = edge->next_callee)
> if (edge->callee->definition
> && (!DECL_EXTERNAL (edge->callee->decl)
> [...]
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 9e1a2e0a9bd..552b1845352 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -6174,6 +6174,12 @@ start_decl (const cp_declarator *declarator,
>
> was_public = TREE_PUBLIC (decl);
>
> + /* Mark any non-default function as versioned as it needs to be mangled
> + even when on its own in a TU. */
> + if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE && TREE_CODE (decl) == FUNCTION_DECL
> + && !is_function_default_version (decl))
> + maybe_mark_function_versioned (decl);
> +
Sorry for the formatting nit, but: usually the convention is to have one
subcondition per line if the whole condition spans multiple lines.
> /* Set the assembler string for any versioned function. */
> if (TREE_CODE (decl) == FUNCTION_DECL
> && (lookup_attribute (TARGET_HAS_FMV_TARGET_ATTRIBUTE ? "target"
> @@ -6186,6 +6192,12 @@ start_decl (const cp_declarator *declarator,
> node->insert_new_function_version ();
> if (!node->function_version ()->assembler_name)
> node->function_version ()->assembler_name = DECL_ASSEMBLER_NAME (decl);
> +
> + /* In target_version semantics mangle non-default versions even if no
> + other versions are present. */
> + if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
> + && !is_function_default_version (decl))
> + maybe_mark_function_versioned (decl);
> }
>
> if ((DECL_EXTERNAL (decl) || TREE_CODE (decl) == FUNCTION_DECL)
This is probably just because I'm being lazy and not applying the
patches locally to try them out, but: could you explain why you need
to do this twice in the same function? Same for start_preparsed_function.
> [...]
> diff --git a/gcc/ipa.cc b/gcc/ipa.cc
> index 8a7b067a526..39ab0cc3de5 100644
> --- a/gcc/ipa.cc
> +++ b/gcc/ipa.cc
> @@ -433,6 +433,17 @@ symbol_table::remove_unreachable_nodes (FILE *file)
> e, &first, &reachable);
> }
> }
> +
> + /* A refrence to the default node implies use of all the other
reference
> + versions (they get used in the function resolver made later
> + in multiple_target.cc) */
> + if (cnode->function_version ()
> + && is_function_default_version (node->decl))
> + for (cgraph_function_version_info *fvi
> + = cnode->function_version ()->next;
> + fvi; fvi = fvi->next)
> + enqueue_node (fvi->this_node, &first, &reachable);
> +
I was going to complain about the cut-&-paste with cgraphunit.cc,
but I see this is actually a different enqueue_node...
> for (e = cnode->callees; e; e = e->next_callee)
> {
> symtab_node *body = e->callee->function_symbol ();
> [...]
> diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols10.C b/gcc/testsuite/g++.target/aarch64/mv-symbols10.C
> new file mode 100644
> index 00000000000..dc5a8ff6572
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols10.C
> @@ -0,0 +1,43 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O0" } */
> +/* { dg-additional-options "-Wno-experimental-fmv-target" } */
> +
> +int
> +foo ();
> +
> +int
> +foo ()
> +{
> + return 1;
> +}
> +
> +__attribute__ ((target_version ("dotprod"))) int
> +foo ()
> +{
> + return 3;
> +}
> +__attribute__ ((target_version ("sve+sve2"))) int
> +foo ()
> +{
> + return 5;
> +}
> +
> +int
> +bar ()
> +{
> + return foo ();
> +}
> +
> +/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 1 } } */
> +/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 1 } } */
> +/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 1 } } */
> +
> +/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
> +/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\._MsveMsve2\n" 1 } } */
It'd be safer to allow x[0-9]+, since the instruction could use x10 or above.
Same for other tests.
> +/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\._Mdotprod\n" 1 } } */
> +/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\.default\n" 1 } } */
> +
> +/* { dg-final { scan-assembler-times "\n\tbl\t_Z3foov\n" 1 } } */
> +
> +/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
> +/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
> [...]
> diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols12.C b/gcc/testsuite/g++.target/aarch64/mv-symbols12.C
> new file mode 100644
> index 00000000000..05d915f19ab
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols12.C
> @@ -0,0 +1,18 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O0" } */
> +/* { dg-additional-options "-Wno-experimental-fmv-target" } */
> +
> +__attribute__ ((target_version ("default"))) int
> +foo () { return 1; }
> +
> +__attribute__ ((target_version ("dotprod"))) int
> +foo () { return 3; }
> +
> +int (*test)();
> +
> +int bar ()
> +{
> + test = foo;
> +
> + return test();
> +}
What's this testing? The code seems to compile ok for unpatched compilers
and the test doesn't seem to check anything beyond that. Same question
for mv-symbols14 and mv-symbols15.
(Very minor, but: it'd be nice to renumber the tests to avoid the gap at 13,
unless it's a superstition thing.)
I don't know how much this will add in practice, but how about having a
test that contains a default version definition, a non-default version
definition, and a non-default version declaration (for a different feature)?
It looks like all the current tests have all non-default versions defined
locally or no non-default versions defined locally.
Thanks,
Richard
> diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols14.C b/gcc/testsuite/g++.target/aarch64/mv-symbols14.C
> new file mode 100644
> index 00000000000..fbee7800ba2
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols14.C
> @@ -0,0 +1,16 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O0" } */
> +/* { dg-additional-options "-Wno-experimental-fmv-target" } */
> +
> +int foo () {
> + return 1;
> +}
> +
> +void
> +bar ()
> +{
> + foo ();
> +}
> +
> +__attribute__ ((target_version ("dotprod"))) int
> +foo ();
> diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols15.C b/gcc/testsuite/g++.target/aarch64/mv-symbols15.C
> new file mode 100644
> index 00000000000..a10c0bfb990
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols15.C
> @@ -0,0 +1,16 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O0" } */
> +/* { dg-additional-options "-Wno-experimental-fmv-target" } */
> +
> +int foo () {
> + return 1;
> +}
> +
> +void bar ()
> +{
> + int (*test)() = foo;
> +
> + test();
> +}
> +
> +__attribute__ ((target_version ("dotprod"))) int foo ();
@@ -1264,6 +1264,15 @@ analyze_functions (bool first_time)
if (!cnode->analyzed)
cnode->analyze ();
+ /* A reference to a default node in a funciton set implies a
+ refrence to all versions in the set. */
+ if (cnode->function_version ()
+ && is_function_default_version (node->decl))
+ for (cgraph_function_version_info *fvi
+ = cnode->function_version ()->next;
+ fvi; fvi = fvi->next)
+ enqueue_node (fvi->this_node);
+
for (edge = cnode->callees; edge; edge = edge->next_callee)
if (edge->callee->definition
&& (!DECL_EXTERNAL (edge->callee->decl)
@@ -6785,6 +6785,14 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
continue;
}
+ /* Do not resolve any non-default function. Only the default version
+ is resolvable (for the target_version attribute semantics.) */
+ if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE && !is_function_default_version (fn))
+ {
+ add_ignored_candidate (candidates, fn);
+ continue;
+ }
+
if (TREE_CODE (fn) == TEMPLATE_DECL)
add_template_candidate (candidates,
fn,
@@ -8789,6 +8789,11 @@ resolve_address_of_overloaded_function (tree target_type,
if (!constraints_satisfied_p (fn))
continue;
+ /* Never resolve a non-default version. */
+ if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ && !is_function_default_version (fn))
+ continue;
+
if (undeduced_auto_decl (fn))
{
/* Force instantiation to do return type deduction. */
@@ -9014,8 +9019,10 @@ resolve_address_of_overloaded_function (tree target_type,
/* If a pointer to a function that is multi-versioned is requested, the
pointer to the dispatcher function is returned instead. This works
well because indirectly calling the function will dispatch the right
- function version at run-time. */
- if (DECL_FUNCTION_VERSIONED (fn))
+ function version at run-time.
+ This is done at multiple_target.cc for target_version semantics. */
+
+ if (DECL_FUNCTION_VERSIONED (fn) && TARGET_HAS_FMV_TARGET_ATTRIBUTE)
{
fn = get_function_version_dispatcher (fn);
if (fn == NULL)
@@ -2130,10 +2130,12 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
returns the function with the highest target priority, that is,
the version that will checked for dispatching first. If this
version is inlinable, a direct call to this version can be made
- otherwise the call should go through the dispatcher. */
+ otherwise the call should go through the dispatcher.
+ This is done at multiple_target.cc for target_version semantics. */
{
tree fn = cp_get_callee_fndecl_nofold (stmt);
- if (fn && DECL_FUNCTION_VERSIONED (fn)
+ if (fn && TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ && DECL_FUNCTION_VERSIONED (fn)
&& (current_function_decl == NULL
|| !targetm.target_option.can_inline_p (current_function_decl,
fn)))
@@ -6174,6 +6174,12 @@ start_decl (const cp_declarator *declarator,
was_public = TREE_PUBLIC (decl);
+ /* Mark any non-default function as versioned as it needs to be mangled
+ even when on its own in a TU. */
+ if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE && TREE_CODE (decl) == FUNCTION_DECL
+ && !is_function_default_version (decl))
+ maybe_mark_function_versioned (decl);
+
/* Set the assembler string for any versioned function. */
if (TREE_CODE (decl) == FUNCTION_DECL
&& (lookup_attribute (TARGET_HAS_FMV_TARGET_ATTRIBUTE ? "target"
@@ -6186,6 +6192,12 @@ start_decl (const cp_declarator *declarator,
node->insert_new_function_version ();
if (!node->function_version ()->assembler_name)
node->function_version ()->assembler_name = DECL_ASSEMBLER_NAME (decl);
+
+ /* In target_version semantics mangle non-default versions even if no
+ other versions are present. */
+ if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ && !is_function_default_version (decl))
+ maybe_mark_function_versioned (decl);
}
if ((DECL_EXTERNAL (decl) || TREE_CODE (decl) == FUNCTION_DECL)
@@ -18776,6 +18788,12 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
if (!DECL_OMP_DECLARE_REDUCTION_P (decl1))
start_lambda_scope (decl1);
+ /* Mark any non-default function as versioned as it needs to be mangled
+ even when on its own in a TU. */
+ if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE && TREE_CODE (decl1) == FUNCTION_DECL
+ && !is_function_default_version (decl1))
+ maybe_mark_function_versioned (decl1);
+
/* Set the assembler string for any versioned function. */
if (TREE_CODE (decl1) == FUNCTION_DECL
&& (lookup_attribute (TARGET_HAS_FMV_TARGET_ATTRIBUTE ? "target"
@@ -18788,6 +18806,12 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
node->insert_new_function_version ();
if (!node->function_version ()->assembler_name)
node->function_version ()->assembler_name = DECL_ASSEMBLER_NAME (decl1);
+
+ /* In target_version semantics mangle non-default versions even if no
+ other versions are present. */
+ if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ && !is_function_default_version (decl1))
+ maybe_mark_function_versioned (decl1);
}
return true;
@@ -4439,6 +4439,14 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
return error_mark_node;
fndecl = function;
+ if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ && !is_function_default_version (fndecl))
+ {
+ if (complain & tf_error)
+ error ("no default version in scope");
+ return error_mark_node;
+ }
+
/* Convert anything with function type to a pointer-to-function. */
if (DECL_MAIN_P (function))
{
@@ -433,6 +433,17 @@ symbol_table::remove_unreachable_nodes (FILE *file)
e, &first, &reachable);
}
}
+
+ /* A refrence to the default node implies use of all the other
+ versions (they get used in the function resolver made later
+ in multiple_target.cc) */
+ if (cnode->function_version ()
+ && is_function_default_version (node->decl))
+ for (cgraph_function_version_info *fvi
+ = cnode->function_version ()->next;
+ fvi; fvi = fvi->next)
+ enqueue_node (fvi->this_node, &first, &reachable);
+
for (e = cnode->callees; e; e = e->next_callee)
{
symtab_node *body = e->callee->function_symbol ();
@@ -424,9 +424,20 @@ ipa_target_clone (void)
auto_vec<cgraph_node *> to_dispatch;
FOR_EACH_FUNCTION (node)
- if (expand_target_clones (node, node->definition))
+ /* Expand all target versions. */
+ if (expand_target_clones (node, node->definition)
+ && TARGET_HAS_FMV_TARGET_ATTRIBUTE)
+ /* In non target_version semantics, dispatch all target clone sets. */
to_dispatch.safe_push (node);
+ /* In target_version semantics dispatch all FMV function sets with a default
+ implementation. */
+ if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE)
+ FOR_EACH_FUNCTION (node)
+ if (is_function_default_version (node->decl)
+ && DECL_FUNCTION_VERSIONED (node->decl) && TREE_STATIC (node->decl))
+ to_dispatch.safe_push (node);
+
for (unsigned i = 0; i < to_dispatch.length (); i++)
create_dispatcher_calls (to_dispatch[i]);
@@ -37,3 +37,7 @@ int bar()
/* { dg-final { scan-assembler-times "\n_Z3foov\._Mrng:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\._MrngMflagm:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\._Mflagm:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\t_Z3foov\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
new file mode 100644
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+int
+foo ();
+
+int
+foo ()
+{
+ return 1;
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+ return 3;
+}
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+ return 5;
+}
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\t_Z3foov\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
new file mode 100644
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("default"))) void
+foo (int a = 3);
+
+__attribute__ ((target_version ("sve"))) void
+foo (int a = 4);
+
+void bar() {
+ foo();
+}
+
+__attribute__ ((target_version ("sve"))) void
+foo2 (int a = 6);
+
+__attribute__ ((target_version ("default"))) void
+foo2 (int a = 5);
+
+void bar2() {
+ foo2();
+}
+
+
+/* { dg-final { scan-assembler-times "\n\tmov\tw\[0-9\]\+, 3\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tmov\tw\[0-9\]\+, 5\n" 1 } } */
new file mode 100644
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("default"))) int
+foo () { return 1; }
+
+__attribute__ ((target_version ("dotprod"))) int
+foo () { return 3; }
+
+int (*test)();
+
+int bar ()
+{
+ test = foo;
+
+ return test();
+}
new file mode 100644
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+int foo () {
+ return 1;
+}
+
+void
+bar ()
+{
+ foo ();
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
new file mode 100644
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+int foo () {
+ return 1;
+}
+
+void bar ()
+{
+ int (*test)() = foo;
+
+ test();
+}
+
+__attribute__ ((target_version ("dotprod"))) int foo ();
@@ -41,13 +41,13 @@ int foo (int)
/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\._Mdotprod:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\._MsveMsve2:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
@@ -29,10 +29,10 @@ int bar()
/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n\tbl\t_Z3foov\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\._Mdotprod:\n" 0 } } */
@@ -44,6 +44,6 @@ int bar()
/* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\._Mdotprod:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\._MsveMsve2:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
@@ -44,10 +44,10 @@ int bar()
/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n\tbl\t_Z3foov\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\._Mdotprod:\n" 1 } } */
new file mode 100644
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+ return 1;
+}
+
+int bar()
+{
+ return foo();
+}
+
+/* It is not overly clear what the correct behaviour is in this case.
+ This test serves more as a test of consistency for this case rather
+ than a test of correctness. */
+
+/* { dg-final { scan-assembler-times "\n_Z3foov:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "bl\t_Z3foov\n" 1 } } */
new file mode 100644
@@ -0,0 +1,48 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+ return 5;
+}
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+ return 3;
+}
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+ return 1;
+}
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\t_Z3foov\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
new file mode 100644
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+int
+foo ()
+{
+ return 1;
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+ return 3;
+}
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+ return 5;
+}
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\t_Z3foov\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
@@ -22,15 +22,15 @@ int bar(int x)
/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n\tbl\t_Z3foov\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\._Mdotprod:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\._MsveMsve2:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n\tbl\t_Z3fooi\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */
@@ -47,15 +47,15 @@ int foo (int)
/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\.arch__v:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\.arch__zba__zbb:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n\tcall\t_Z3foov\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, @gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, @gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__v:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__zba__zbb:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n\tcall\t_Z3fooi\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
@@ -36,10 +36,10 @@ int bar()
/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\.arch__v:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\.arch__zba__zbb:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n\tcall\t_Z3foov\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, @gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, @gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__v:\n" 0 } } */
@@ -50,7 +50,7 @@ int bar()
/* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__v:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__zba__zbb:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n\tcall\t_Z3fooi\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
@@ -48,10 +48,10 @@ int bar()
/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\.arch__v:\n" 1 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\.arch__zba__zbb:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n\tcall\t_Z3foov\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, @gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, @gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__v:\n" 1 } } */
@@ -28,15 +28,15 @@ int bar(int x)
/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\.arch__v:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3foov\.arch__zba__zbb:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n\tcall\t_Z3foov\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, @gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, @gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__v:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__zba__zbb:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
/* { dg-final { scan-assembler-times "\n\tcall\t_Z3fooi\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */