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 <jason@redhat.com>
This commit is contained in:
parent
f6e9c1c919
commit
1c7bec8bfb
@ -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));
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
19
gcc/testsuite/g++.target/i386/pr99108.C
Normal file
19
gcc/testsuite/g++.target/i386/pr99108.C
Normal file
@ -0,0 +1,19 @@
|
||||
/* PR c++/99108 */
|
||||
/* { dg-require-ifunc "" } */
|
||||
|
||||
struct A {
|
||||
template <class T>
|
||||
void foo(T);
|
||||
};
|
||||
template <class T>
|
||||
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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user