@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3. If not see
#include "context.h"
#include "tree-pass.h"
#include "internal-fn.h"
+#include "calls.h"
/* The gimplification pass converts the language-dependent trees
(ld-trees) emitted by the parser into language-independent trees
@@ -904,6 +905,16 @@ c_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED,
case CALL_EXPR:
{
tree fndecl = get_callee_fndecl (*expr_p);
+
+ /* Change any calls to a multiversioned function to instead
+ * be a call to the dispatched symbol. */
+ if (fndecl && DECL_FUNCTION_VERSIONED (fndecl))
+ if (tree dis = get_c_function_version_dispatcher (fndecl))
+ {
+ dis = build_fold_addr_expr_loc (EXPR_LOCATION (dis), dis);
+ CALL_EXPR_FN (*expr_p) = dis;
+ }
+
if (fndecl
&& fndecl_built_in_p (fndecl, BUILT_IN_CLZG, BUILT_IN_CTZG)
&& call_expr_nargs (*expr_p) == 2
@@ -62,6 +62,7 @@ along with GCC; see the file COPYING3. If not see
#include "omp-general.h"
#include "omp-offload.h" /* For offload_vars. */
#include "c-parser.h"
+#include "tree.h"
#include "tree-pretty-print.h"
@@ -2098,6 +2099,30 @@ previous_tag (tree type)
return NULL_TREE;
}
+/* Subroutine to mark functions as versioned when using the attribute
+ 'target_version'. */
+
+static void
+maybe_mark_function_versioned (tree decl)
+{
+ if (!DECL_FUNCTION_VERSIONED (decl))
+ {
+ DECL_FUNCTION_VERSIONED (decl) = 1;
+
+ /* Do not mangle the name of the default version of this function since
+ the dispatcher creation in 'create_dispatcher_calls' already mangles
+ the default version. */
+ tree attr_target = lookup_attribute ("target_version",
+ DECL_ATTRIBUTES (decl));
+
+ gcc_assert (attr_target && TREE_VALUE (attr_target));
+
+ tree mangled_name
+ = targetm.mangle_decl_assembler_name (decl, DECL_NAME (decl));
+ SET_DECL_ASSEMBLER_NAME (decl, mangled_name);
+ }
+}
+
/* Subroutine of duplicate_decls. Compare NEWDECL to OLDDECL.
Returns true if the caller should proceed to merge the two, false
if OLDDECL should simply be discarded. As a side effect, issues
@@ -2418,12 +2443,44 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
}
}
- if (DECL_INITIAL (newdecl))
+ if ((DECL_INITIAL (newdecl) || DECL_EXTERNAL (newdecl))
+ && (DECL_INITIAL (olddecl) || DECL_EXTERNAL (olddecl))
+ && targetm.target_option.function_versions (newdecl, olddecl))
+ {
+ /* Check if we are dealing with Function Multi Versioning using
+ the 'target_version' attribute. */
+ tree olddecl_args = TYPE_ARG_TYPES (TREE_TYPE (olddecl));
+ tree newdecl_args = TYPE_ARG_TYPES (TREE_TYPE (newdecl));
+ /* Function Multi Versioned functions need to have the same
+ prototype and the target must support these too, otherwise
+ error.
+ TODO: should we be checking any other properties? */
+ if (!comptypes (TREE_TYPE (olddecl), TREE_TYPE (newdecl))
+ || !comptypes (olddecl_args, newdecl_args))
+ {
+ auto_diagnostic_group d;
+ error ("redefinition of %q+D", newdecl);
+ locate_old_decl (olddecl);
+ return false;
+ }
+ /* Only record if we are seeing either of these for the first
+ time. */
+ bool record = (!DECL_FUNCTION_VERSIONED (newdecl)
+ || !DECL_FUNCTION_VERSIONED (olddecl));
+
+ maybe_mark_function_versioned (newdecl);
+ maybe_mark_function_versioned (olddecl);
+
+ if (record)
+ cgraph_node::record_function_versions (olddecl, newdecl);
+ }
+
+ else if (DECL_INITIAL (newdecl))
{
if (DECL_INITIAL (olddecl))
{
/* If the new declaration isn't overriding an extern inline
- reject the new decl. In c99, no overriding is allowed
+ reject the new decl. In c99, no overriding is allowed
in the same translation unit. */
if (!DECL_EXTERN_INLINE (olddecl)
|| DECL_EXTERN_INLINE (newdecl)
@@ -3191,6 +3248,14 @@ duplicate_decls (tree newdecl, tree olddecl)
return false;
}
+ /* If both new and old are Function Multi Versioned functions then they are
+ not duplicates. */
+ if (TREE_CODE (newdecl) == FUNCTION_DECL
+ && TREE_CODE (olddecl) == FUNCTION_DECL
+ && DECL_FUNCTION_VERSIONED (newdecl)
+ && DECL_FUNCTION_VERSIONED (olddecl))
+ return false;
+
merge_decls (newdecl, olddecl, newtype, oldtype);
/* The NEWDECL will no longer be needed.
@@ -13686,6 +13751,10 @@ c_parse_final_cleanups (void)
c_write_global_declarations_1 (BLOCK_VARS (DECL_INITIAL (t)));
c_write_global_declarations_1 (BLOCK_VARS (ext_block));
+ /* Call this to set cpp_implicit_aliases_done on all nodes. This is
+ important for function multiversioning aliases to get resolved. */
+ symtab->process_same_body_aliases ();
+
if (!in_lto_p)
free_attr_access_data ();
@@ -5386,3 +5386,26 @@ cxx17_empty_base_field_p (const_tree field)
&& RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))
&& !lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (field)));
}
+
+/* Returns the decl of the dispatcher function if FN is a function version. */
+
+tree
+get_c_function_version_dispatcher (tree fn)
+{
+ tree dispatcher_decl = NULL;
+
+ gcc_assert (TREE_CODE (fn) == FUNCTION_DECL
+ && DECL_FUNCTION_VERSIONED (fn));
+
+ gcc_assert (targetm.get_function_versions_dispatcher);
+ dispatcher_decl = targetm.get_function_versions_dispatcher (fn);
+
+ if (dispatcher_decl == NULL)
+ {
+ error_at (input_location, "use of multiversioned function "
+ "without a default");
+ return NULL;
+ }
+
+ return dispatcher_decl;
+}
@@ -134,5 +134,6 @@ extern void maybe_complain_about_tail_call (tree, const char *);
extern rtx rtx_for_static_chain (const_tree, bool);
extern bool cxx17_empty_base_field_p (const_tree);
+extern tree get_c_function_version_dispatcher (tree fn);
#endif // GCC_CALLS_H
new file mode 100644
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_version("default")))
+int foo ()
+{
+ return 1;
+}
+
+/* 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 } } */
new file mode 100644
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_version("default")))
+int foo ()
+{
+ return 1;
+}
+
+__attribute__((target_version("rng")))
+int foo ()
+{
+ return 1;
+}
+
+__attribute__((target_version("flagm")))
+int foo ()
+{
+ return 1;
+}
+
+__attribute__((target_version("rng+flagm")))
+int foo ()
+{
+ return 1;
+}
+
+int bar()
+{
+ return foo ();
+}
+
+/* Check usage of the first two FMV features, in case of off-by-one errors. */
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mrng:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MrngMflagm:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mflagm:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
new file mode 100644
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// Basic case of fmv correctness with all functions and use in one TU.
+
+__attribute__((target_version("default")))
+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 ();
+}
+
+/* When updating any of the symbol names in these tests, make sure to also
+ update any tests for their absence in mv-symbolsN.C */
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
new file mode 100644
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with definitions but no call
+
+__attribute__((target_version("default")))
+int foo ()
+{
+ return 1;
+}
+
+__attribute__((target_version("dotprod")))
+int foo ()
+{
+ return 3;
+}
+__attribute__((target_version("sve+sve2")))
+int foo ()
+{
+ return 5;
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
new file mode 100644
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with declarations but no implementation
+
+__attribute__((target_version("default")))
+int foo ();
+
+__attribute__((target_version("dotprod")))
+int foo ();
+
+__attribute__((target_version("sve+sve2")))
+int foo ();
+
+int bar()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
new file mode 100644
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with a default implementation and declarations of other versions
+
+__attribute__((target_version("default")))
+int foo ()
+{
+ return 1;
+}
+
+__attribute__((target_version("dotprod")))
+int foo ();
+
+__attribute__((target_version("sve+sve2")))
+int foo ();
+
+int bar()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
new file mode 100644
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with default declaration, and implementations of other
+// versions.
+
+__attribute__((target_version("default")))
+int foo ();
+
+__attribute__((target_version("dotprod")))
+int foo ()
+{
+ return 3;
+}
+__attribute__((target_version("sve+sve2")))
+int foo ()
+{
+ return 5;
+}
+
+int bar()
+{
+ return foo ();
+}
+
+/* When updating any of the symbol names in these tests, make sure to also
+ update any tests for their absence in mvc-symbolsN.C */
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
new file mode 100644
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_version("default")))
+int foo ()
+{
+ return 1;
+}
+
+
+/* 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 "\nfoo:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
new file mode 100644
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_clones("default", "dotprod", "sve+sve2")))
+int foo ()
+{
+ return 1;
+}
+
+int bar()
+{
+ return foo ();
+}
+
+
+/* When updating any of the symbol names in these tests, make sure to also
+ update any tests for their absence in mvc-symbolsN.C */
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
new file mode 100644
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_clones("default", "dotprod", "sve+sve2")))
+int foo ()
+{
+ return 1;
+}
+
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
new file mode 100644
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_clones("default", "dotprod", "sve+sve2")))
+int foo ();
+
+int bar()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
+
new file mode 100644
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_clones("default", "dotprod", "sve+sve2")))
+int foo ();
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
From: Alfie Richards <alfie.richards@arm.com> This patch adds support for `target_version` function multiversioning to the C frontend. The functionality and behavior matches the CPP frontend. This is likely to need to be changed later down the line for the Aarch64 backend to match the ACLE but that is future work. Note, in particular this adds a call to process_same_body_aliases to the C frontend. this function is seems to be intended to be used in the CPP and D frontends. However, not calling this function was meaning cpp_implicit_aliases_done was false for the C FMV cgraph nodes and so the references to the dispatched symbol were not being followed resulting in the nodes getting deleted and a segfault later. gcc/c-family/ChangeLog: * c-gimplify.cc (c_gimplify_expr): Process calls to FMV functions. gcc/c/ChangeLog: * c-decl.cc (maybe_mark_function_versioned): New function. (diagnose_mismatched_decls): Add logic for target_version functions. (duplicate_decls): Add logic to differentiate target_version functions. (c_parse_final_cleanups): Add call to process_same_body_aliases. gcc/ChangeLog: * calls.cc (get_c_function_version_dispatcher): New function. * calls.h (get_c_function_version_dispatcher): New function. gcc/testsuite/ChangeLog: * g++.target/aarch64/mv-symbols6.C: New test. * gcc.target/aarch64/mv-1.c: New test. * gcc.target/aarch64/mv-symbols1.c: New test. * gcc.target/aarch64/mv-symbols2.c: New test. * gcc.target/aarch64/mv-symbols3.c: New test. * gcc.target/aarch64/mv-symbols4.c: New test. * gcc.target/aarch64/mv-symbols5.c: New test. * gcc.target/aarch64/mv-symbols6.c: New test. * gcc.target/aarch64/mvc-symbols1.c: New test. * gcc.target/aarch64/mvc-symbols2.c: New test. * gcc.target/aarch64/mvc-symbols3.c: New test. * gcc.target/aarch64/mvc-symbols4.c: New test. This has been reg tested on Aarch64 and X86. Bootstrapped for aarch64-none-linux-gnu. OK for master? --- gcc/c-family/c-gimplify.cc | 11 +++ gcc/c/c-decl.cc | 73 ++++++++++++++++++- gcc/calls.cc | 23 ++++++ gcc/calls.h | 1 + .../g++.target/aarch64/mv-symbols6.C | 16 ++++ gcc/testsuite/gcc.target/aarch64/mv-1.c | 39 ++++++++++ .../gcc.target/aarch64/mv-symbols1.c | 37 ++++++++++ .../gcc.target/aarch64/mv-symbols2.c | 28 +++++++ .../gcc.target/aarch64/mv-symbols3.c | 26 +++++++ .../gcc.target/aarch64/mv-symbols4.c | 29 ++++++++ .../gcc.target/aarch64/mv-symbols5.c | 35 +++++++++ .../gcc.target/aarch64/mv-symbols6.c | 17 +++++ .../gcc.target/aarch64/mvc-symbols1.c | 25 +++++++ .../gcc.target/aarch64/mvc-symbols2.c | 16 ++++ .../gcc.target/aarch64/mvc-symbols3.c | 19 +++++ .../gcc.target/aarch64/mvc-symbols4.c | 12 +++ 16 files changed, 405 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols6.C create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols2.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols3.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols4.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols5.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols6.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-symbols1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-symbols2.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-symbols3.c create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-symbols4.c