This patch adds the TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL flag which changes FMV behavior for target_version functions to match the Arm C Language Extension.
The functional differences consist of: 1. Generating the resolver for the dispatched symbol at the site of the default version definition. 2. Mangling non-default FMV annotated functions even when no other versions are present. This allows for better behavior when definitions are spread across different TU's as one resolver will be created and the [ACLE](https://github.com/ARM-software/acle/blob/main/main/acle.md#function-multi-versioning) stipulates that the default implementation must have visibility of all other versions so that resolver will be complete. This also matches Clangs behavior. The only remaining discrepancy I'm aware of when targeting AArch64 is we do not allow the coexistence of target_version and target_clone, which is specified as supported in the ACLE and will be addressed later. Note patch only enables the hook for Aarch64, with the aim that other targets are not effected. To enable these functionality changes I added mangling to the initial processing of functions in the C++ frontend, and changed the logic for the creation of resolver bodies to create at default declaration implementations. Additionally, the previous naming logic relied on the fact that if there existed a call to a dispatched function the resolver would also be created which would do some renaming. As that no longer is guaranteed this patch hacks on the assembler names to make them correct. Reg tested on AArch64 and X86_64. Bootstrapped tested on aarch64-none-linux-gnu and x86_64-unknown-linux-gnu. gcc/ChangeLog: * attribs.cc (make_dispatcher_decl): Add optional name parameter. (is_function_default_version): Add check_versioned parameter. * attribs.h (make_dispatcher_decl): Add optional name parameter. (is_function_default_version): Add optional check_versioned parameter. * cgraphunit.cc (cgraph_node::analyze): Change dispatcher creation logic. * config/aarch64/aarch64.cc (get_assembler_name_without_default): New. (get_suffixed_assembler_name): Change to use get_assembler_name_without_default. (aarch64_get_function_versions_dispatcher): Change to use get_assembler_name_without_default. * config/aarch64/aarch64.h (TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL): New. * defaults.h (TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL): New. gcc/cp/ChangeLog: * class.cc (add_method): Update call * cp-tree.h (maybe_version_functions): Remove unecessary argument. * decl.cc (decls_match): Remove unecessary argument. (maybe_version_functions): Remove unecessary parameter. (start_preparsed_function): Change logic. gcc/testsuite/ChangeLog: * g++.target/aarch64/mv-1.C: Update. * g++.target/aarch64/mv-symbols2.C: Update. * g++.target/aarch64/mv-symbols3.C: Update. * g++.target/aarch64/mv-symbols4.C: Update. * g++.target/aarch64/mv-symbols5.C: Update. * g++.target/aarch64/mv-symbols6.C: New test. --- gcc/attribs.cc | 32 +++++++++++----- gcc/attribs.h | 2 +- gcc/cgraphunit.cc | 38 ++++++++++++++++--- gcc/config/aarch64/aarch64.cc | 28 ++++++++++++-- gcc/config/aarch64/aarch64.h | 2 + gcc/cp/class.cc | 2 +- gcc/cp/cp-tree.h | 2 +- gcc/cp/decl.cc | 20 +++++----- gcc/defaults.h | 10 +++++ gcc/testsuite/g++.target/aarch64/mv-1.C | 4 ++ .../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 | 22 +++++++++++ 15 files changed, 146 insertions(+), 46 deletions(-) create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols6.C
diff --git a/gcc/attribs.cc b/gcc/attribs.cc index 1d6589835a1..acb4e9574bc 100644 --- a/gcc/attribs.cc +++ b/gcc/attribs.cc @@ -1247,16 +1247,21 @@ common_function_versions (tree fn1, tree fn2) /* 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. Return the decl created. */ + by the front-end. NAME, optionally, specifies the name of the created decl. + 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), @@ -1277,17 +1282,26 @@ 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. */ +/* Returns true if DECL could be a default version of a FMV function set + If TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_DECL is false, this is when DECL + is marked with an attribute. Otherwise this is when DECL is not annotated + with a non-default multi-version. */ bool is_function_default_version (const tree decl) { if (TREE_CODE (decl) != FUNCTION_DECL - || !DECL_FUNCTION_VERSIONED (decl)) + || (!TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL + && !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 (TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL && !attr) + return true; gcc_assert (attr); attr = TREE_VALUE (TREE_VALUE (attr)); return (TREE_CODE (attr) == STRING_CST diff --git a/gcc/attribs.h b/gcc/attribs.h index 00a83a785b4..4115973ea12 100644 --- a/gcc/attribs.h +++ b/gcc/attribs.h @@ -55,7 +55,7 @@ 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 tree make_dispatcher_decl (const tree, const char *name = NULL); extern bool is_function_default_version (const tree); extern void handle_ignored_attributes_option (vec<char *> *); diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc index 9dbee9b6fae..594b9b8876f 100644 --- a/gcc/cgraphunit.cc +++ b/gcc/cgraphunit.cc @@ -635,6 +635,7 @@ cgraph_node::analyze (void) input_location = DECL_SOURCE_LOCATION (decl); semantic_interposition = opt_for_fn (decl, flag_semantic_interposition); + cgraph_function_version_info *version_info = function_version (); if (thunk) { thunk_info *info = thunk_info::get (this); @@ -661,12 +662,29 @@ 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)) + bool has_default_version = version_info + && version_info->next + && is_function_default_version + (version_info->next->this_node->decl); + + /* Check for the default version. */ + if (TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL + && !has_default_version + && callers) + { + error_at (callers->call_stmt->location, + "Call to multiversioned function without a default version " + "declaration in scope."); + return; + } + + /* Generate the dispatcher body of multi-versioned functions at + the first point where the dispatched symbol has been called. */ + if ((!TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL + || (TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL + && has_default_version + && version_info->next->this_node->definition)) + && (version_info && version_info->dispatcher_resolver == NULL_TREE)) { tree resolver = NULL_TREE; gcc_assert (targetm.generate_version_dispatcher_body); @@ -703,6 +721,14 @@ cgraph_node::analyze (void) pop_cfun (); } + + /* We need to always process the dispatcher resolver in the + TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL fmv case. */ + if (version_info && TARGET_CREATE_FMV_DISPATCHER_AT_DEFAULT_IMPL + && !dispatcher_function && is_function_default_version (decl)) + enqueue_node (cgraph_node::get_create + (targetm.get_function_versions_dispatcher (decl))); + analyzed = true; input_location = saved_loc; diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index f0672420aed..8cb0a369d33 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -20364,18 +20364,28 @@ aarch64_mangle_decl_assembler_name (tree decl, tree id) return id; } -/* Return an identifier for the base assembler name of a versioned function. +/* Return the base assembler name of a versioned function as a string. This is computed by taking the default version's assembler name, and stripping off the ".default" suffix if it's already been appended. */ -static tree -get_suffixed_assembler_name (tree default_decl, const char *suffix) +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. */ + +static tree +get_suffixed_assembler_name (tree default_decl, const char *suffix) +{ + std::string name = get_assembler_name_without_default (default_decl); name += suffix; return get_identifier (name.c_str()); } @@ -20839,8 +20849,18 @@ 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 .*/ + DECL_ASSEMBLER_NAME_SET_P(decl) = true; + /* 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); diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h index f463f1061e9..f45bef7d849 100644 --- a/gcc/config/aarch64/aarch64.h +++ b/gcc/config/aarch64/aarch64.h @@ -1412,6 +1412,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. */ diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index c11b91d75ce..f98177b180e 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -1403,7 +1403,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)) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index b3c909b05c4..3c652fa8d83 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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, diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index b4e7ceefedb..dffff180e76 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -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; } } @@ -1281,12 +1279,11 @@ maybe_mark_function_versioned (tree decl) } } -/* NEWDECL and OLDDECL have identical signatures. If they are - different versions adjust them and return true. - If RECORD is set to true, record function versions. */ +/* NEWDECL and OLDDECL have identical signatures. If they are different + versions adjust them, record function versions, and return true. */ 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 +1306,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; } @@ -18409,6 +18405,12 @@ 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 + && !is_function_default_version (decl1)) + maybe_mark_function_versioned (decl1); + return true; } diff --git a/gcc/defaults.h b/gcc/defaults.h index 918e3ec2f24..63cc70a1201 100644 --- a/gcc/defaults.h +++ b/gcc/defaults.h @@ -879,6 +879,16 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #define TARGET_CLONES_ATTR_SEPARATOR ',' #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 ensures better 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 special support here and encode direct references. */ diff --git a/gcc/testsuite/g++.target/aarch64/mv-1.C b/gcc/testsuite/g++.target/aarch64/mv-1.C index b4b0e5e3fea..5798e41d155 100644 --- a/gcc/testsuite/g++.target/aarch64/mv-1.C +++ b/gcc/testsuite/g++.target/aarch64/mv-1.C @@ -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 } } */ diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols2.C b/gcc/testsuite/g++.target/aarch64/mv-symbols2.C index f0c7967a97a..7c8e1bcbed1 100644 --- a/gcc/testsuite/g++.target/aarch64/mv-symbols2.C +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols2.C @@ -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 } } */ diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols3.C b/gcc/testsuite/g++.target/aarch64/mv-symbols3.C index 3d30e27deb8..2e8a27f0522 100644 --- a/gcc/testsuite/g++.target/aarch64/mv-symbols3.C +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols3.C @@ -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 } } */ diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols4.C b/gcc/testsuite/g++.target/aarch64/mv-symbols4.C index 73e3279ec31..04cac9d118a 100644 --- a/gcc/testsuite/g++.target/aarch64/mv-symbols4.C +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols4.C @@ -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 } } */ diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols5.C b/gcc/testsuite/g++.target/aarch64/mv-symbols5.C index 05d1379f53e..faa67909c64 100644 --- a/gcc/testsuite/g++.target/aarch64/mv-symbols5.C +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols5.C @@ -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 } } */ diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols6.C b/gcc/testsuite/g++.target/aarch64/mv-symbols6.C new file mode 100644 index 00000000000..70d21c832ab --- /dev/null +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols6.C @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O0" } */ + +__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 } } */