https://gcc.gnu.org/g:574d52a9b6e40a466b90f4810e72d3dd072d5160

commit r11-11321-g574d52a9b6e40a466b90f4810e72d3dd072d5160
Author: H.J. Lu <hjl.to...@gmail.com>
Date:   Mon Feb 26 08:38:58 2024 -0800

    tree-profile: Disable indirect call profiling for IFUNC resolvers
    
    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.c (symbol_table::compile): Call
            symtab_node::check_ifunc_callee_symtab_nodes.
            * symtab.c (check_ifunc_resolver): New.
            (ifunc_ref_map): Likewise.
            (is_caller_ifunc_resolver): Likewise.
            (symtab_node::check_ifunc_callee_symtab_nodes): Likewise.
            * tree-profile.c (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.
    
    (cherry picked from commit cab32bacaea268ec062b1fb4fc662d90c9d1cfce)

Diff:
---
 gcc/cgraph.h                    |  9 ++++-
 gcc/cgraphunit.c                |  2 +
 gcc/symtab.c                    | 89 +++++++++++++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/pr114115.c | 24 +++++++++++
 gcc/tree-profile.c              |  8 +++-
 5 files changed, 130 insertions(+), 2 deletions(-)

diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 4a1f89920f5..8e6128bf25d 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -473,6 +473,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);
 
@@ -883,7 +886,9 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public 
symtab_node
       versionable (false), can_change_signature (false),
       redefined_extern_inline (false), tm_may_enter_irr (false),
       ipcp_clone (false), declare_variant_alt (false),
-      calls_declare_variant_alt (false), m_uid (uid), m_summary_id (-1)
+      calls_declare_variant_alt (false),
+      called_by_ifunc_resolver (false),
+      m_uid (uid), m_summary_id (-1)
   {}
 
   /* Remove the node from cgraph and all inline clones inlined into it.
@@ -1475,6 +1480,8 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : 
public symtab_node
   unsigned declare_variant_alt : 1;
   /* True if the function calls declare_variant_alt functions.  */
   unsigned calls_declare_variant_alt : 1;
+  /* Set if the function is called by an IFUNC resolver.  */
+  unsigned called_by_ifunc_resolver : 1;
 
 private:
   /* Unique id of the node.  */
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 802b01d3b97..98c9a492e00 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -2273,6 +2273,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");
diff --git a/gcc/symtab.c b/gcc/symtab.c
index 5530a124a9d..04b8283199c 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -1363,6 +1363,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
index 00000000000..2629f591877
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr114115.c
@@ -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" } } */
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 5a74cc96e13..64d25cf4d17 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -418,7 +418,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 ();

Reply via email to