]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
tree-profile: Disable indirect call profiling for IFUNC resolvers
authorH.J. Lu <hjl.tools@gmail.com>
Mon, 26 Feb 2024 16:38:58 +0000 (08:38 -0800)
committerH.J. Lu <(no_default)>
Wed, 3 Apr 2024 13:49:45 +0000 (06:49 -0700)
We can't profile indirect calls to IFUNC resolvers nor their callees as
it requires TLS which hasn't been set up yet when the dynamic linker is
resolving IFUNC symbols.

Add an IFUNC resolver caller marker to cgraph_node and set it if the
function is called by an IFUNC resolver.  Disable indirect call profiling
for IFUNC resolvers and their callees.

Tested with profiledbootstrap on Fedora 39/x86-64.

gcc/ChangeLog:

PR tree-optimization/114115
* cgraph.h (symtab_node): Add check_ifunc_callee_symtab_nodes.
(cgraph_node): Add called_by_ifunc_resolver.
* cgraphunit.cc (symbol_table::compile): Call
symtab_node::check_ifunc_callee_symtab_nodes.
* symtab.cc (check_ifunc_resolver): New.
(ifunc_ref_map): Likewise.
(is_caller_ifunc_resolver): Likewise.
(symtab_node::check_ifunc_callee_symtab_nodes): Likewise.
* tree-profile.cc (gimple_gen_ic_func_profiler): Disable indirect
call profiling for IFUNC resolvers and their callees.

gcc/testsuite/ChangeLog:

PR tree-optimization/114115
* gcc.dg/pr114115.c: New test.

gcc/cgraph.h
gcc/cgraphunit.cc
gcc/symtab.cc
gcc/testsuite/gcc.dg/pr114115.c [new file with mode: 0644]
gcc/tree-profile.cc

index 47f35e8078df288ef01d25a72583f1988c09ef3a..a8c3224802c1d017dd84918a93d11d50b4570200 100644 (file)
@@ -479,6 +479,9 @@ public:
      Return NULL if there's no such node.  */
   static symtab_node *get_for_asmname (const_tree asmname);
 
+  /* Check symbol table for callees of IFUNC resolvers.  */
+  static void check_ifunc_callee_symtab_nodes (void);
+
   /* Verify symbol table for internal consistency.  */
   static DEBUG_FUNCTION void verify_symtab_nodes (void);
 
@@ -896,6 +899,7 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
       redefined_extern_inline (false), tm_may_enter_irr (false),
       ipcp_clone (false), declare_variant_alt (false),
       calls_declare_variant_alt (false), gc_candidate (false),
+      called_by_ifunc_resolver (false),
       m_uid (uid), m_summary_id (-1)
   {}
 
@@ -1495,6 +1499,8 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
      is set for local SIMD clones when they are created and cleared if the
      vectorizer uses them.  */
   unsigned gc_candidate : 1;
+  /* Set if the function is called by an IFUNC resolver.  */
+  unsigned called_by_ifunc_resolver : 1;
 
 private:
   /* Unique id of the node.  */
index d200166f7e93a53aaeab652fdb77195caf6bb23e..2bd0289ffba5d0af4789fe1f56be97bdac90ea1c 100644 (file)
@@ -2317,6 +2317,8 @@ symbol_table::compile (void)
 
   symtab_node::checking_verify_symtab_nodes ();
 
+  symtab_node::check_ifunc_callee_symtab_nodes ();
+
   timevar_push (TV_CGRAPHOPT);
   if (pre_ipa_mem_report)
     dump_memory_report ("Memory consumption before IPA");
index 4c7e3c135caf03a9a93dae88f496b3e819aef5a7..3256133891da9c7a23b6daed0166ffad04cad72a 100644 (file)
@@ -1369,6 +1369,95 @@ symtab_node::verify (void)
   timevar_pop (TV_CGRAPH_VERIFY);
 }
 
+/* Return true and set *DATA to true if NODE is an ifunc resolver.  */
+
+static bool
+check_ifunc_resolver (cgraph_node *node, void *data)
+{
+  if (node->ifunc_resolver)
+    {
+      bool *is_ifunc_resolver = (bool *) data;
+      *is_ifunc_resolver = true;
+      return true;
+    }
+  return false;
+}
+
+static auto_bitmap ifunc_ref_map;
+
+/* Return true if any caller of NODE is an ifunc resolver.  */
+
+static bool
+is_caller_ifunc_resolver (cgraph_node *node)
+{
+  bool is_ifunc_resolver = false;
+
+  for (cgraph_edge *e = node->callers; e; e = e->next_caller)
+    {
+      /* Return true if caller is known to be an IFUNC resolver.  */
+      if (e->caller->called_by_ifunc_resolver)
+       return true;
+
+      /* Check for recursive call.  */
+      if (e->caller == node)
+       continue;
+
+      /* Skip if it has been visited.  */
+      unsigned int uid = e->caller->get_uid ();
+      if (bitmap_bit_p (ifunc_ref_map, uid))
+       continue;
+      bitmap_set_bit (ifunc_ref_map, uid);
+
+      if (is_caller_ifunc_resolver (e->caller))
+       {
+         /* Return true if caller is an IFUNC resolver.  */
+         e->caller->called_by_ifunc_resolver = true;
+         return true;
+       }
+
+      /* Check if caller's alias is an IFUNC resolver.  */
+      e->caller->call_for_symbol_and_aliases (check_ifunc_resolver,
+                                             &is_ifunc_resolver,
+                                             true);
+      if (is_ifunc_resolver)
+       {
+         /* Return true if caller's alias is an IFUNC resolver.  */
+         e->caller->called_by_ifunc_resolver = true;
+         return true;
+       }
+    }
+
+  return false;
+}
+
+/* Check symbol table for callees of IFUNC resolvers.  */
+
+void
+symtab_node::check_ifunc_callee_symtab_nodes (void)
+{
+  symtab_node *node;
+
+  FOR_EACH_SYMBOL (node)
+    {
+      cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+      if (!cnode)
+       continue;
+
+      unsigned int uid = cnode->get_uid ();
+      if (bitmap_bit_p (ifunc_ref_map, uid))
+       continue;
+      bitmap_set_bit (ifunc_ref_map, uid);
+
+      bool is_ifunc_resolver = false;
+      cnode->call_for_symbol_and_aliases (check_ifunc_resolver,
+                                         &is_ifunc_resolver, true);
+      if (is_ifunc_resolver || is_caller_ifunc_resolver (cnode))
+       cnode->called_by_ifunc_resolver = true;
+    }
+
+  bitmap_clear (ifunc_ref_map);
+}
+
 /* Verify symbol table for internal consistency.  */
 
 DEBUG_FUNCTION void
diff --git a/gcc/testsuite/gcc.dg/pr114115.c b/gcc/testsuite/gcc.dg/pr114115.c
new file mode 100644 (file)
index 0000000..2629f59
--- /dev/null
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -fprofile-generate -fdump-tree-optimized" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-require-ifunc "" } */
+
+void *foo_ifunc2() __attribute__((ifunc("foo_resolver")));
+
+void bar(void)
+{
+}
+
+static int f3()
+{
+  bar ();
+  return 5;
+}
+
+void (*foo_resolver(void))(void)
+{
+  f3();
+  return bar;
+}
+
+/* { dg-final { scan-tree-dump-not "__gcov_indirect_call_profiler_v" "optimized" } } */
index aed13e2b1bc30d8485ceb4881abbcf6767a0b4e0..625f7e9229ade58c35c14d209067bc835a99d0e7 100644 (file)
@@ -520,7 +520,13 @@ gimple_gen_ic_func_profiler (void)
   gcall *stmt1;
   tree tree_uid, cur_func, void0;
 
-  if (c_node->only_called_directly_p ())
+  /* Disable indirect call profiling for an IFUNC resolver and its
+     callees since it requires TLS which hasn't been set up yet when
+     the dynamic linker is resolving IFUNC symbols.  See
+     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114115
+   */
+  if (c_node->only_called_directly_p ()
+      || c_node->called_by_ifunc_resolver)
     return;
 
   gimple_init_gcov_profiler ();