From 1c7bec8bfbc5457c1b57d0e3b67f5d6bc8812e57 Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Wed, 3 Mar 2021 09:38:55 +0100 Subject: c++: support target attr for DECL_LOCAL_DECL_P fns [PR99108] We crash when target attribute get_function_versions_dispatcher is called for a function that is not registered in call graph. This was happening because we were calling it for the function-local decls that aren't in the symbol table, instead of the corresponding namespace-scope decls that are. gcc/cp/ChangeLog: PR c++/99108 * call.c (get_function_version_dispatcher): Handle DECL_LOCAL_DECL_P. * decl.c (maybe_version_functions): Likewise. (maybe_mark_function_versioned): New. * name-lookup.c (push_local_extern_decl_alias): No longer static. * name-lookup.h (push_local_extern_decl_alias): Adjust. gcc/testsuite/ChangeLog: PR c++/99108 * g++.target/i386/pr99108.C: New test. Co-authored-by: Jason Merrill --- gcc/cp/call.c | 3 +++ gcc/cp/decl.c | 35 +++++++++++++++++++++++++-------- gcc/cp/name-lookup.c | 4 ++-- gcc/cp/name-lookup.h | 1 + gcc/testsuite/g++.target/i386/pr99108.C | 19 ++++++++++++++++++ 5 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 gcc/testsuite/g++.target/i386/pr99108.C diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 33278b5..29f4b50 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -8469,6 +8469,9 @@ get_function_version_dispatcher (tree fn) { tree dispatcher_decl = NULL; + if (DECL_LOCAL_DECL_P (fn)) + fn = DECL_LOCAL_DECL_ALIAS (fn); + gcc_assert (TREE_CODE (fn) == FUNCTION_DECL && DECL_FUNCTION_VERSIONED (fn)); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 1b671ce..56092eb 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -1110,6 +1110,21 @@ decls_match (tree newdecl, tree olddecl, bool record_versions /* = true */) return types_match; } +/* Mark DECL as versioned if it isn't already. */ + +static void +maybe_mark_function_versioned (tree decl) +{ + if (!DECL_FUNCTION_VERSIONED (decl)) + { + DECL_FUNCTION_VERSIONED (decl) = 1; + /* If DECL_ASSEMBLER_NAME has already been set, re-mangle + to include the version marker. */ + if (DECL_ASSEMBLER_NAME_SET_P (decl)) + mangle_decl (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. */ @@ -1120,18 +1135,22 @@ maybe_version_functions (tree newdecl, tree olddecl, bool record) if (!targetm.target_option.function_versions (newdecl, olddecl)) return false; - if (!DECL_FUNCTION_VERSIONED (olddecl)) + maybe_mark_function_versioned (olddecl); + if (DECL_LOCAL_DECL_P (olddecl)) { - DECL_FUNCTION_VERSIONED (olddecl) = 1; - if (DECL_ASSEMBLER_NAME_SET_P (olddecl)) - mangle_decl (olddecl); + olddecl = DECL_LOCAL_DECL_ALIAS (olddecl); + maybe_mark_function_versioned (olddecl); } - if (!DECL_FUNCTION_VERSIONED (newdecl)) + maybe_mark_function_versioned (newdecl); + if (DECL_LOCAL_DECL_P (newdecl)) { - DECL_FUNCTION_VERSIONED (newdecl) = 1; - if (DECL_ASSEMBLER_NAME_SET_P (newdecl)) - mangle_decl (newdecl); + /* Unfortunately, we can get here before pushdecl naturally calls + push_local_extern_decl_alias, so we need to call it directly. */ + if (!DECL_LOCAL_DECL_ALIAS (newdecl)) + push_local_extern_decl_alias (newdecl); + newdecl = DECL_LOCAL_DECL_ALIAS (newdecl); + maybe_mark_function_versioned (newdecl); } if (record) diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 9382a47..a6257f5 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -3374,7 +3374,7 @@ set_decl_context_in_fn (tree ctx, tree decl) /* DECL is a local extern decl. Find or create the namespace-scope decl that it aliases. Also, determines the linkage of DECL. */ -static void +void push_local_extern_decl_alias (tree decl) { if (dependent_type_p (TREE_TYPE (decl))) @@ -3408,7 +3408,7 @@ push_local_extern_decl_alias (tree decl) if (binding && TREE_CODE (binding) != TREE_LIST) for (ovl_iterator iter (binding); iter; ++iter) - if (decls_match (*iter, decl)) + if (decls_match (decl, *iter, /*record_versions*/false)) { alias = *iter; break; diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index 67e923f..f63c4f5 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -453,6 +453,7 @@ extern void cp_emit_debug_info_for_using (tree, tree); extern void finish_nonmember_using_decl (tree scope, tree name); extern void finish_using_directive (tree target, tree attribs); +void push_local_extern_decl_alias (tree decl); extern tree pushdecl (tree, bool hiding = false); extern tree pushdecl_outermost_localscope (tree); extern tree pushdecl_top_level (tree); diff --git a/gcc/testsuite/g++.target/i386/pr99108.C b/gcc/testsuite/g++.target/i386/pr99108.C new file mode 100644 index 0000000..b1f0a71 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr99108.C @@ -0,0 +1,19 @@ +/* PR c++/99108 */ +/* { dg-require-ifunc "" } */ + +struct A { + template + void foo(T); +}; +template +void A::foo(T) +{ + int f(void) __attribute__((target("default"))); + int f(void) __attribute__((target("arch=atom"))); + int b = f(); +} +void bar(void) +{ + A c; + c.foo(7); +} -- cgit v1.1