This patch changes the semantics of target_version and target_clones attributes
to match the behavior described in the Arm C Language extension.
The changes to behavior are:
- The scope and signature of an FMV function set is now that of the default
version.
- The FMV resolver is now created at the locations of the default version
implementation. Previously this was at the first call to an FMV function.
- When a TU has a single annotated function version, it gets mangled.
- This includes a lone annotated default version.
This only affects targets with TARRGET_HAS_FMV_TARGET_ATTRIBUTE set to false.
Currently that is aarch64 and riscv.
This is achieved by:
- Skipping the existing FMV dispatching code at C++ gimplification and instead
making use of the target_clones dispatching code in multiple_targets.cc.
(This fixes PR target/118313 for aarch64 and riscv).
- Splitting target_clones pass in two, an early and late pass, where the early
pass handles cases where multiple declarations are used to define a version,
and the late pass handling target semantics targets, and cases where a FMV
set is defined by a single target_clones decl.
- Changing the logic in add_candidates and resolve_address of overloaded
function to prevent resolution of any version except a default version.
(thus making the default version determine scope and signature of the
versioned function set).
- Adding logic for dispatching a lone annotated default version in
multiple_targets.cc
- As as annotated default version gets mangled an alias is created from the
dispatched symbol to the default version as no ifunc resolution is required
in this case. (ie. an alias from `_Z3foov` to `_Z3foov.default`)
- Adding logic to `symbol_table::remove_unreachable_nodes` and analyze_functions
that a reference to the default function version also implies a possible
reference to the other versions (so they shouldnt be deleted and do need to
be analyzed).
gcc/ChangeLog:
PR target/118313
* cgraph.cc (delete_function_version): Made public static member of
cgraph_node.
* cgraph.h (delete_function_version): Ditto.
* cgraphunit.cc (analyze_functions): Add logic for target version
dependencies.
* ipa.cc (symbol_table::remove_unreachable_nodes): Ditto.
* multiple_target.cc (create_dispatcher_calls): Change to support
target version semantics.
(ipa_target_clone): Change to dispatch all function sets in
target_version semantics, and to have early and late pass.
(expand_target_clones): Add logic for cases of target_clones with no
defaults.
(is_simple_target_clones_case): New function.
(class pass_target_clone): New parameter for early or late pass.
* config/aarch64/aarch64.cc: (aarch64_get_function_versions_dispatcher):
Refactor with the assumption that the DECL node will be default.
* config/riscv/riscv.cc: (riscv_get_function_versions_dispatcher):
Refactor with the assumption that the DECL node will be default.
* passes.def: Split target_clones pass into early and late version.
gcc/cp/ChangeLog:
PR target/118313
* call.cc (add_candidates): Change to not resolve non-default versions
in target_version semantics.
* class.cc (resolve_address_of_overloaded_function): Ditto.
* cp-gimplify.cc (cp_genericize_r): Change logic to not apply for
target_version semantics.
* decl.cc (start_decl): Change to mark and therefore mangle all
target_version decls in target_version semantics.
(start_preparsed_function): Ditto.
* typeck.cc (cp_build_function_call_vec): Add error for calling
unresolvable non-default node in target_version semantics.
gcc/testsuite/ChangeLog:
* g++.target/aarch64/mv-1.C: Change for target_version 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-symbols13.C: New test.
* g++.target/aarch64/mv-symbols6.C: New test.
* g++.target/aarch64/mv-symbols7.C: New test.
* g++.target/aarch64/mv-symbols8.C: New test.
* g++.target/aarch64/mv-symbols9.C: New test.
---
gcc/cgraph.cc | 4 +-
gcc/cgraph.h | 2 +
gcc/cgraphunit.cc | 9 +
gcc/config/aarch64/aarch64.cc | 43 ++--
gcc/config/riscv/riscv.cc | 43 ++--
gcc/cp/call.cc | 10 +
gcc/cp/class.cc | 13 +-
gcc/cp/cp-gimplify.cc | 11 +-
gcc/cp/decl.cc | 14 ++
gcc/cp/typeck.cc | 10 +
gcc/ipa.cc | 11 +
gcc/multiple_target.cc | 188 +++++++++++++++---
gcc/passes.def | 3 +-
gcc/testsuite/g++.target/aarch64/mv-1.C | 4 +
.../g++.target/aarch64/mv-symbols10.C | 27 +++
.../g++.target/aarch64/mv-symbols11.C | 30 +++
.../g++.target/aarch64/mv-symbols12.C | 28 +++
.../g++.target/aarch64/mv-symbols13.C | 28 +++
.../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 | 21 ++
.../g++.target/aarch64/mv-symbols7.C | 48 +++++
.../g++.target/aarch64/mv-symbols8.C | 46 +++++
.../g++.target/aarch64/mv-symbols9.C | 43 ++++
.../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 +-
32 files changed, 588 insertions(+), 132 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-symbols13.C
create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols6.C
create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols7.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/cgraph.cc b/gcc/cgraph.cc
index c0be16edfcb..1d86bcec67f 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -333,8 +333,8 @@ cgraph_node::insert_new_function_version (void)
}
/* Remove the cgraph_function_version_info node given by DECL_V. */
-static void
-delete_function_version (cgraph_function_version_info *decl_v)
+void
+cgraph_node::delete_function_version (cgraph_function_version_info *decl_v)
{
if (decl_v == NULL)
return;
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 21f89112769..a719321a538 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1352,6 +1352,8 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node :
public symtab_node
DECL is a duplicate declaration. */
static void delete_function_version_by_decl (tree decl);
+ static void delete_function_version (cgraph_function_version_info *);
+
/* Add the function FNDECL to the call graph.
Unlike finalize_function, this function is intended to be used
by middle end and allows insertion of new function at arbitrary point
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 9f4af63b7dc..a81f685654f 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 function set implies a
+ reference to all versions in the set. */
+ cgraph_function_version_info *node_v = cnode->function_version ();
+ if (node_v && is_function_default_version (node->decl))
+ for (cgraph_function_version_info *fvi = node_v->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)
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 5415af2a73b..441a5cf9b42 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -21052,42 +21052,29 @@ aarch64_generate_version_dispatcher_body (void
*node_p)
return resolver_decl;
}
-/* Make a dispatcher declaration for the multi-versioned function DECL.
- Calls to DECL function will be replaced with calls to the dispatcher
- by the front-end. Returns the decl of the dispatcher function. */
+/* Make a dispatcher declaration for the multi-versioned default function DECL.
+ Calls to DECL function will be replaced with calls to the dispatcher by
+ the target_clones pass. Returns the decl of the dispatcher function. */
tree
aarch64_get_function_versions_dispatcher (void *decl)
{
- tree fn = (tree) decl;
- struct cgraph_node *node = NULL;
- struct cgraph_node *default_node = NULL;
- struct cgraph_function_version_info *node_v = NULL;
-
+ tree default_decl = (tree) decl;
tree dispatch_decl = NULL;
- struct cgraph_function_version_info *default_version_info = NULL;
-
- gcc_assert (fn != NULL && DECL_FUNCTION_VERSIONED (fn));
-
- node = cgraph_node::get (fn);
- gcc_assert (node != NULL);
+ gcc_assert (decl != NULL
+ && DECL_FUNCTION_VERSIONED (default_decl)
+ && is_function_default_version (default_decl));
- node_v = node->function_version ();
- gcc_assert (node_v != NULL);
+ struct cgraph_node *default_node = cgraph_node::get (default_decl);
+ gcc_assert (default_node != NULL);
- if (node_v->dispatcher_resolver != NULL)
- return node_v->dispatcher_resolver;
+ struct cgraph_function_version_info *default_node_v
+ = default_node->function_version ();
+ gcc_assert (default_node_v != NULL && !default_node_v->prev);
- /* The default node is always the beginning of the chain. */
- default_version_info = node_v;
- while (default_version_info->prev)
- default_version_info = default_version_info->prev;
- default_node = default_version_info->this_node;
-
- /* If there is no default node, just return NULL. */
- if (!is_function_default_version (default_node->decl))
- return NULL;
+ if (default_node_v->dispatcher_resolver != NULL)
+ return default_node_v->dispatcher_resolver;
if (targetm.has_ifunc_p ())
{
@@ -21097,7 +21084,7 @@ aarch64_get_function_versions_dispatcher (void *decl)
dispatch_decl = make_dispatcher_decl (default_node->decl);
/* Set the dispatcher for all the versions. */
- it_v = default_version_info;
+ it_v = default_node_v;
while (it_v != NULL)
{
it_v->dispatcher_resolver = dispatch_decl;
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 419eec7ed16..3c953804804 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -14573,42 +14573,29 @@ riscv_generate_version_dispatcher_body (void *node_p)
return resolver_decl;
}
-/* Make a dispatcher declaration for the multi-versioned function DECL.
- Calls to DECL function will be replaced with calls to the dispatcher
- by the front-end. Returns the decl of the dispatcher function. */
+/* Make a dispatcher declaration for the multi-versioned default function DECL.
+ Calls to DECL function will be replaced with calls to the dispatcher by
+ the target_clones pass. Returns the decl of the dispatcher function. */
tree
riscv_get_function_versions_dispatcher (void *decl)
{
- tree fn = (tree) decl;
- struct cgraph_node *node = NULL;
- struct cgraph_node *default_node = NULL;
- struct cgraph_function_version_info *node_v = NULL;
-
+ tree default_decl = (tree) decl;
tree dispatch_decl = NULL;
- struct cgraph_function_version_info *default_version_info = NULL;
-
- gcc_assert (fn != NULL && DECL_FUNCTION_VERSIONED (fn));
-
- node = cgraph_node::get (fn);
- gcc_assert (node != NULL);
-
- node_v = node->function_version ();
- gcc_assert (node_v != NULL);
+ gcc_assert (decl != NULL
+ && DECL_FUNCTION_VERSIONED (default_decl)
+ && is_function_default_version (default_decl));
- if (node_v->dispatcher_resolver != NULL)
- return node_v->dispatcher_resolver;
+ struct cgraph_node *default_node = cgraph_node::get (default_decl);
+ gcc_assert (default_node != NULL);
- /* The default node is always the beginning of the chain. */
- default_version_info = node_v;
- while (default_version_info->prev)
- default_version_info = default_version_info->prev;
- default_node = default_version_info->this_node;
+ struct cgraph_function_version_info *default_node_v
+ = default_node->function_version ();
+ gcc_assert (default_node_v != NULL && !default_node_v->prev);
- /* If there is no default node, just return NULL. */
- if (!is_function_default_version (default_node->decl))
- return NULL;
+ if (default_node_v->dispatcher_resolver != NULL)
+ return default_node_v->dispatcher_resolver;
if (targetm.has_ifunc_p ())
{
@@ -14618,7 +14605,7 @@ riscv_get_function_versions_dispatcher (void *decl)
dispatch_decl = make_dispatcher_decl (default_node->decl);
/* Set the dispatcher for all the versions. */
- it_v = default_version_info;
+ it_v = default_node_v;
while (it_v != NULL)
{
it_v->dispatcher_resolver = dispatch_decl;
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 63cad2aca4c..a6a3fd0d6bb 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -6927,6 +6927,16 @@ 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
+ && TREE_CODE (fn) == FUNCTION_DECL
+ && !is_function_default_version (fn))
+ {
+ add_ignored_candidate (candidates, fn);
+ continue;
+ }
+
if (TREE_CODE (fn) == TEMPLATE_DECL)
add_template_candidate (candidates,
fn,
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 14acb9c23c0..5e9ad1724d5 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -8974,6 +8974,13 @@ resolve_address_of_overloaded_function (tree target_type,
if (!constraints_satisfied_p (fn))
continue;
+ /* For target_version semantics, never resolve a non-default
+ version. */
+ if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ && TREE_CODE (fn) == FUNCTION_DECL
+ && !is_function_default_version (fn))
+ continue;