The following fixes behavior of IPA PTA with IFUNCs. Those were treated as simple aliases (since ->alias is set) and thus we just considered the ifunc resolver as body. The following instead associates an IFUNC call with an indirect call to a var initialized from the resolver result.
Bootstrapped on x86_64-unknown-linux-gnu, testing in progress. LTO bootstrapped with -fipa-pta in BOOT_CFLAGS on x86_64-unknown-linux-gnu. Testing coverage isn't too big with IPA PTA so I'm not considering to backport this for the moment. Richard. 2018-05-30 Richard Biener <rguent...@suse.de> PR ipa/85960 * tree-ssa-structalias.c (get_function_part_constraint): Handle NULL fi->decl. (find_func_aliases_for_call): Properly handle indirect fi from direct call. (find_func_clobbers): Likewise. (ipa_pta_execute): Likewise. (create_variable_info_for): For functions that are ifunc_resolver resolve to a varinfo that contains the result of the resolver call. (associate_varinfo_to_alias): Do not treat ifunc resolvers as aliases. * gcc.dg/ipa/ipa-pta-19.c: New testcase. Index: gcc/tree-ssa-structalias.c =================================================================== --- gcc/tree-ssa-structalias.c (revision 260950) +++ gcc/tree-ssa-structalias.c (working copy) @@ -3903,7 +3903,7 @@ get_function_part_constraint (varinfo_t c.offset = 0; c.type = SCALAR; } - else if (TREE_CODE (fi->decl) == FUNCTION_DECL) + else if (fi->decl && TREE_CODE (fi->decl) == FUNCTION_DECL) { varinfo_t ai = first_vi_for_offset (fi, part); if (ai) @@ -4733,7 +4733,7 @@ find_func_aliases_for_call (struct funct fi = get_fi_for_callee (t); if (!in_ipa_mode - || (fndecl && !fi->is_fn_info)) + || (fi->decl && fndecl && !fi->is_fn_info)) { auto_vec<ce_s, 16> rhsc; int flags = gimple_call_flags (t); @@ -5350,7 +5350,7 @@ find_func_clobbers (struct function *fn, /* For callees without function info (that's external functions), ESCAPED is clobbered and used. */ - if (gimple_call_fndecl (t) + if (cfi->decl && !cfi->is_fn_info) { varinfo_t vi; @@ -6113,6 +6113,27 @@ create_variable_info_for_1 (tree decl, c static unsigned int create_variable_info_for (tree decl, const char *name, bool add_id) { + /* First see if we are dealing with an ifunc resolver call and + assiociate that with a call to the resolver function result. */ + cgraph_node *node; + if (in_ipa_mode + && TREE_CODE (decl) == FUNCTION_DECL + && (node = cgraph_node::get (decl))->ifunc_resolver) + { + varinfo_t fi = get_vi_for_tree (node->get_alias_target ()->decl); + constraint_expr rhs + = get_function_part_constraint (fi, fi_result); + fi = new_var_info (NULL_TREE, "ifuncres", true); + fi->is_reg_var = true; + constraint_expr lhs; + lhs.type = SCALAR; + lhs.var = fi->id; + lhs.offset = 0; + process_constraint (new_constraint (lhs, rhs)); + insert_vi_for_tree (decl, fi); + return fi->id; + } + varinfo_t vi = create_variable_info_for_1 (decl, name, add_id, false, NULL); unsigned int id = vi->id; @@ -7713,7 +7734,8 @@ associate_varinfo_to_alias (struct cgrap if ((node->alias || (node->thunk.thunk_p && ! node->global.inlined_to)) - && node->analyzed) + && node->analyzed + && !node->ifunc_resolver) insert_vi_for_tree (node->decl, (varinfo_t)data); return false; } @@ -8085,7 +8107,7 @@ ipa_pta_execute (void) (node->decl, first_vi_for_offset (fi, fi_uses)); } /* Handle direct calls to external functions. */ - else if (decl) + else if (decl && (!fi || fi->decl)) { pt = gimple_call_use_set (stmt); if (gimple_call_flags (stmt) & ECF_CONST) @@ -8130,8 +8152,7 @@ ipa_pta_execute (void) } } /* Handle indirect calls. */ - else if (!decl - && (fi = get_fi_for_callee (stmt))) + else if ((fi = get_fi_for_callee (stmt))) { /* We need to accumulate all clobbers/uses of all possible callees. */ @@ -8187,6 +8208,8 @@ ipa_pta_execute (void) } } } + else + gcc_unreachable (); } } Index: gcc/testsuite/gcc.dg/ipa/ipa-pta-19.c =================================================================== --- gcc/testsuite/gcc.dg/ipa/ipa-pta-19.c (nonexistent) +++ gcc/testsuite/gcc.dg/ipa/ipa-pta-19.c (working copy) @@ -0,0 +1,46 @@ +/* { dg-do run } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O2 -fipa-pta" } */ + +#include <stdlib.h> +#include <stdbool.h> + +#define hot __attribute__((hot)) + +static hot void multiver_default(unsigned int in, bool *ret) +{ + if ( in & 1 ) { + *ret = false; + }else{ + *ret = true; + } +} + +static void (*resolve_multiver(void))(unsigned int in, bool *out) +{ + return &multiver_default; +} + +__attribute__ ((ifunc("resolve_multiver"))) +static void multiver_test(unsigned int val, bool *ret); + +static hot bool do_test(unsigned int val) +{ + bool ret = false; + + multiver_test(val, &ret); + + return (ret == !(val & 0x1)); +} + +volatile unsigned int x = 2; +int main() +{ + int i; + for(i = 1; i < x; i++) { + unsigned int val = x; + if ( !do_test(val) ) + abort (); + } + return 0; +}