@@ -1246,13 +1246,17 @@ common_function_versions (tree fn1, tree fn2)
by the front-end. Return the decl created. */
tree
-make_dispatcher_decl (const tree decl)
+make_dispatcher_decl (const tree decl, const char *name)
{
tree func_decl;
- char *func_name;
tree fn_type, func_type;
- func_name = xstrdup (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
+ char *func_name;
+
+ if (name)
+ func_name = xstrdup (name);
+ else
+ func_name = xstrdup (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
fn_type = TREE_TYPE (decl);
func_type = build_function_type (TREE_TYPE (fn_type),
@@ -1273,17 +1277,25 @@ make_dispatcher_decl (const tree decl)
return func_decl;
}
-/* Returns true if DECL is multi-versioned using the target attribute, and this
- is the default version. This function can only be used for targets that do
- not support the "target_version" attribute. */
+/* If allow_unversioned is false, returns true if DECL has been marked as
+ multiversioned, is multi-versioned using the an attribute, and this is
+ the default version.
+ If allow_unversioned is true, then does not require the DECL is marked as
+ versioned and returns true if the function could be a default function,
+ ie could be unannotated. */
bool
-is_function_default_version (const tree decl)
+is_function_default_version (const tree decl, bool allow_unversioned)
{
if (TREE_CODE (decl) != FUNCTION_DECL
- || !DECL_FUNCTION_VERSIONED (decl))
+ || (allow_unversioned && !DECL_FUNCTION_VERSIONED (decl)))
return false;
- tree attr = lookup_attribute ("target", DECL_ATTRIBUTES (decl));
+ tree attr = lookup_attribute (TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ ? "target" : "target_version",
+ DECL_ATTRIBUTES (decl));
+ /* An unannotated function can be default */
+ if (!allow_unversioned && !attr)
+ return true;
gcc_assert (attr);
attr = TREE_VALUE (TREE_VALUE (attr));
return (TREE_CODE (attr) == STRING_CST
@@ -55,8 +55,8 @@ extern struct scoped_attributes *
extern char *sorted_attr_string (tree);
extern bool common_function_versions (tree, tree);
-extern tree make_dispatcher_decl (const tree);
-extern bool is_function_default_version (const tree);
+extern tree make_dispatcher_decl (const tree, const char * = NULL);
+extern bool is_function_default_version (const tree, bool check_versioned = true);
extern void handle_ignored_attributes_option (vec<char *> *);
/* Return a type like TTYPE except that its TYPE_ATTRIBUTES
@@ -10928,6 +10928,15 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
warn_parm_array_mismatch (origloc, old_decl, parms);
}
+ /* To enable versions to be created across TU's we mark and mangle all
+ non-default versioned functions. */
+ if (TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL
+ && lookup_attribute (TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ ? "target" : "target_version",
+ DECL_ATTRIBUTES (decl1)))
+ if (!is_function_default_version (decl1, false))
+ maybe_mark_function_versioned (decl1);
+
/* Record the decl so that the function name is defined.
If we already have a decl for this name, and it is a FUNCTION_DECL,
use the old decl. */
@@ -660,21 +660,39 @@ cgraph_node::analyze (void)
resolve_alias (cgraph_node::get (alias_target), transparent_alias);
else if (dispatcher_function)
{
- /* Generate the dispatcher body of multi-versioned functions. */
- cgraph_function_version_info *dispatcher_version_info
- = function_version ();
- if (dispatcher_version_info != NULL
- && (dispatcher_version_info->dispatcher_resolver
- == NULL_TREE))
+ if (!TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL)
{
- tree resolver = NULL_TREE;
- gcc_assert (targetm.generate_version_dispatcher_body);
- resolver = targetm.generate_version_dispatcher_body (this);
- gcc_assert (resolver != NULL_TREE);
+ /* Generate the dispatcher body of multi-versioned functions at
+ the first point where the dispatched symbol has been called. */
+ cgraph_function_version_info *dispatcher_version_info
+ = function_version ();
+ if (dispatcher_version_info != NULL
+ && (dispatcher_version_info->dispatcher_resolver
+ == NULL_TREE))
+ {
+ tree resolver = NULL_TREE;
+ gcc_assert (targetm.generate_version_dispatcher_body);
+ resolver = targetm.generate_version_dispatcher_body (this);
+ gcc_assert (resolver != NULL_TREE);
+ }
}
}
else
{
+ /* Create the dispatcher at the default version implementation. */
+ if (TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL
+ && DECL_FUNCTION_VERSIONED (decl)
+ && is_function_default_version(decl, false) && definition)
+ {
+ tree idecl = targetm.get_function_versions_dispatcher (decl);
+ cgraph_node *inode = cgraph_node::get (idecl);
+
+ tree resolver = NULL_TREE;
+ gcc_assert (targetm.generate_version_dispatcher_body);
+ resolver = targetm.generate_version_dispatcher_body (inode);
+ gcc_assert (resolver != NULL_TREE);
+ }
+
push_cfun (DECL_STRUCT_FUNCTION (decl));
assign_assembler_name_if_needed (decl);
@@ -20202,6 +20202,18 @@ aarch64_mangle_decl_assembler_name (tree decl, tree id)
return id;
}
+static std::string
+get_assembler_name_without_default (tree default_decl)
+{
+ std::string name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (default_decl));
+
+ auto size = name.size ();
+ if (size >= 8 && name.compare (size - 8, 8, ".default") == 0)
+ name.resize (size - 8);
+
+ return name;
+}
+
/* Return an identifier for the base assembler name of a versioned function.
This is computed by taking the default version's assembler name, and
stripping off the ".default" suffix if it's already been appended. */
@@ -20209,11 +20221,7 @@ aarch64_mangle_decl_assembler_name (tree decl, tree id)
static tree
get_suffixed_assembler_name (tree default_decl, const char *suffix)
{
- std::string name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (default_decl));
-
- auto size = name.size ();
- if (size >= 8 && name.compare (size - 8, 8, ".default") == 0)
- name.resize (size - 8);
+ std::string name = get_assembler_name_without_default(default_decl);
name += suffix;
return get_identifier (name.c_str());
}
@@ -20670,8 +20678,19 @@ aarch64_get_function_versions_dispatcher (void *decl)
struct cgraph_node *dispatcher_node = NULL;
struct cgraph_function_version_info *dispatcher_version_info = NULL;
+ /* Strip the suffix from the default version of the function to get the
+ dispatcher name. */
+
+ std::string name =
+ get_assembler_name_without_default(default_node->decl);
+
+ dispatch_decl = make_dispatcher_decl (default_node->decl, name.c_str ());
+
+ /* Mark the assembler name as set to prevent it getting mangled again .*/
+ SET_DECL_ASSEMBLER_NAME (dispatch_decl,
+ DECL_ASSEMBLER_NAME (dispatch_decl));
+
/* Right now, the dispatching is done via ifunc. */
- dispatch_decl = make_dispatcher_decl (default_node->decl);
TREE_NOTHROW (dispatch_decl) = TREE_NOTHROW (fn);
dispatcher_node = cgraph_node::get_create (dispatch_decl);
@@ -1393,6 +1393,8 @@ extern enum aarch64_code_model aarch64_cmodel;
#define TARGET_HAS_FMV_TARGET_ATTRIBUTE 0
+#define TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL 1
+
#define TARGET_SUPPORTS_WIDE_INT 1
/* Modes valid for AdvSIMD D registers, i.e. that fit in half a Q register. */
@@ -1402,7 +1402,7 @@ add_method (tree type, tree method, bool via_using)
/* If these are versions of the same function, process and
move on. */
if (TREE_CODE (fn) == FUNCTION_DECL
- && maybe_version_functions (method, fn, true))
+ && maybe_version_functions (method, fn))
continue;
if (DECL_INHERITED_CTOR (method))
@@ -6998,7 +6998,7 @@ extern void determine_local_discriminator (tree, tree = NULL_TREE);
extern bool member_like_constrained_friend_p (tree);
extern bool fns_correspond (tree, tree);
extern int decls_match (tree, tree, bool = true);
-extern bool maybe_version_functions (tree, tree, bool);
+extern bool maybe_version_functions (tree, tree);
extern bool validate_constexpr_redeclaration (tree, tree);
extern bool merge_default_template_args (tree, tree, bool);
extern tree duplicate_decls (tree, tree,
@@ -1213,9 +1213,7 @@ decls_match (tree newdecl, tree olddecl, bool record_versions /* = true */)
&& targetm.target_option.function_versions (newdecl, olddecl))
{
if (record_versions)
- maybe_version_functions (newdecl, olddecl,
- (!DECL_FUNCTION_VERSIONED (newdecl)
- || !DECL_FUNCTION_VERSIONED (olddecl)));
+ maybe_version_functions (newdecl, olddecl);
return 0;
}
}
@@ -1286,7 +1284,7 @@ maybe_mark_function_versioned (tree decl)
If RECORD is set to true, record function versions. */
bool
-maybe_version_functions (tree newdecl, tree olddecl, bool record)
+maybe_version_functions (tree newdecl, tree olddecl)
{
if (!targetm.target_option.function_versions (newdecl, olddecl))
return false;
@@ -1309,8 +1307,7 @@ maybe_version_functions (tree newdecl, tree olddecl, bool record)
maybe_mark_function_versioned (newdecl);
}
- if (record)
- cgraph_node::record_function_versions (olddecl, newdecl);
+ cgraph_node::record_function_versions (olddecl, newdecl);
return true;
}
@@ -18378,6 +18375,15 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
if (!DECL_OMP_DECLARE_REDUCTION_P (decl1))
start_lambda_scope (decl1);
+ /* To enable versions to be created across TU's we mark and mangle all
+ non-default versioned functions. */
+ if (TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL
+ && lookup_attribute (TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ ? "target" : "target_version",
+ DECL_ATTRIBUTES (decl1)))
+ if (!is_function_default_version (decl1, false))
+ maybe_mark_function_versioned (decl1);
+
return true;
}
@@ -874,6 +874,15 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#define TARGET_HAS_FMV_TARGET_ATTRIBUTE 1
#endif
+/* Indicates if the target should generate FMV dispatchers at the
+ site of the default version implementation rather than at the call sites
+ to the function.
+ The creation at default dispatcher is as defined in the Arm C language
+ extension and esures batter behavior when defining function versions
+ accross translation units. */
+#ifndef TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL
+#define TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL 0
+#endif
/* Select a format to encode pointers in exception handling data. We
prefer those that result in fewer dynamic relocations. Assume no
@@ -36,3 +36,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 } } */
@@ -40,13 +40,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 } } */
@@ -28,10 +28,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 } } */
@@ -43,6 +43,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 } } */
@@ -43,10 +43,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 } } */
@@ -7,6 +7,11 @@ 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. */
@@ -14,3 +19,4 @@ foo ()
/* { 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 } } */
@@ -3,8 +3,7 @@
// Basic case of fmv correctness with all functions and use in one TU.
-__attribute__ ((target_version ("default"))) int
-foo ()
+int foo ()
{
return 1;
}
@@ -23,6 +23,6 @@ foo ()
/* { 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 } } */
+/* { 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 } } */
@@ -21,7 +21,7 @@ bar ()
/* { 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 "\nfoo\.resolver:\n" 0 } } */
/* { 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 } } */
+/* { 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 } } */
@@ -30,7 +30,7 @@ bar ()
/* { 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 "\nfoo\.resolver:\n" 0 } } */
/* { 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 } } */
+/* { 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 } } */
@@ -7,10 +7,15 @@ 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 "\nfoo:\n" 1 } } */
+/* { dg-final { scan-assembler-times "bl\tfoo\n" 1 } } */
/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */