> Hi,
> 
> this patch extends the ipa-devirt pass with the ability to add
> speculative calls for indirect calls where the target was loaded from
> a record_type data structure and we have seen writes of address of
> only one particular function to the same offset of that record
> type (including static initializers).
> 
> The idea is basically taken from Christoph Müllner's patch from 2022
> (https://gcc.gnu.org/pipermail/gcc-patches/2022-November/605934.html)
> but I have re-worked it so that it stores the required information in
> GC memory (which I belive is necessary) and does not require an
> additional pass through gimple statements of all functions because it
> uses the analysis phase of ipa-cp/ipa-fnsummary (which was an approach
> we agreed on with Honza).
> 
> It also performs simple verification that the collected types match
> the type of the record field.  We could verify that the function
> determined as the likely target matches the call statement
> expectations, but for that we would need to stream both types which is
> something I decided not to do.
> 
> Bootsstrapped and LTO-bootstrapped and tested on x86_64-linux.  OK for
> master?
> 
> Thanks,
> 
> Martin
> 
> 
> gcc/ChangeLog:
> 
> 2025-10-24  Martin Jambor  <[email protected]>
> 
>       PR ipa/107666
>       * cgraph.h (cgraph_simple_indirect_info): New fields rec_type and
>       fld_offset.
>       * ipa-prop.h (ipa_analyze_var_static_initializer): Declare.
>       (ipa_dump_noted_record_fnptrs): Likewise.
>       (ipa_debug_noted_record_fnptrs): Likewise.
>       (ipa_single_noted_fnptr_in_record): Likewise.
>       (ipa_free_noted_fnptr_calls): Likewise.
>       * ipa-cp.cc (ipcp_generate_summary): Call
>       ipa_analyze_var_static_initializer on each varbool node with a static
>       initializer.
>       * ipa-devirt.cc (struct devirt_stats): New type.
>       (devirt_target_ok_p): New function.
>       (ipa_devirt): Move statistics counters to the new structure.  dump
>       noted function pointers stored in records.  Check for edge hotness
>       first and for odr_types only for polymorphic edges.  Moved a number of
>       checks to devirt_target_ok_p.  Also add speculative direct calls for
>       non-polymorphic indirect ones when ipa_single_noted_fnptr_in_record
>       finds a likely target.  Call ipa_free_noted_fnptr_calls.
>       * ipa-prop.cc (noted_fnptr_store): New type.
>       (struct noted_fnptr_hasher): Likewise.
>       (noted_fnptr_hasher::hash): New function.
>       (noted_fnptr_hasher::equal): Likewise.
>       (noted_fnptrs_in_records): New.
>       (is_func_ptr_from_record): New function.
>       (ipa_analyze_indirect_call_uses): Also simple create indirect info
>       structures with fnptr_loaded_from_record set.
>       (note_fnptr_in_record): New function.
>       (ipa_dump_noted_record_fnptrs): Likewise.
>       (ipa_debug_noted_record_fnptrs): Likewise.
>       (ipa_single_noted_fnptr_in_record): Likewise.
>       (ipa_free_noted_fnptr_calls): Likewise.
>       (ipa_analyze_stmt_uses): Also look for stroes of function pointers to
>       record structures.
>       (ipa_analyze_var_static_initializer): New function.
>       (ipa_write_indirect_edge_info): Also stream fnptr_loaded_from_record
>       indirec infos.
>       (ipa_read_indirect_edge_info): Likewise.
>       (ipa_prop_write_jump_functions): Also stream the contents of
>       noted_fnptrs_in_records.
>       (ipa_prop_read_section): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 2025-10-24  Martin Jambor  <[email protected]>
> 
>       * gcc.dg/lto/fnptr-from-rec-1_0.c: New test.
>       * gcc.dg/lto/fnptr-from-rec-1_1.c: Likewise.
>       * gcc.dg/lto/fnptr-from-rec-2_0.c: Likewise.
>       * gcc.dg/lto/fnptr-from-rec-2_1.c: Likewise.
>       * gcc.dg/lto/fnptr-from-rec-3_0.c: Likewise.
>       * gcc.dg/lto/fnptr-from-rec-3_1.c: Likewise.
> 
> Co-authored by: Christoph Müllner <[email protected]>
> ---
>  gcc/cgraph.h                                  |  13 +-
>  gcc/ipa-cp.cc                                 |   4 +
>  gcc/ipa-devirt.cc                             | 195 +++++++----
>  gcc/ipa-prop.cc                               | 330 +++++++++++++++++-
>  gcc/ipa-prop.h                                |   5 +
>  gcc/testsuite/gcc.dg/lto/fnptr-from-rec-1_0.c |  41 +++
>  gcc/testsuite/gcc.dg/lto/fnptr-from-rec-1_1.c |  19 +
>  gcc/testsuite/gcc.dg/lto/fnptr-from-rec-2_0.c |  43 +++
>  gcc/testsuite/gcc.dg/lto/fnptr-from-rec-2_1.c |  22 ++
>  gcc/testsuite/gcc.dg/lto/fnptr-from-rec-3_0.c |  43 +++
>  gcc/testsuite/gcc.dg/lto/fnptr-from-rec-3_1.c |  19 +
>  11 files changed, 651 insertions(+), 83 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/lto/fnptr-from-rec-1_0.c
>  create mode 100644 gcc/testsuite/gcc.dg/lto/fnptr-from-rec-1_1.c
>  create mode 100644 gcc/testsuite/gcc.dg/lto/fnptr-from-rec-2_0.c
>  create mode 100644 gcc/testsuite/gcc.dg/lto/fnptr-from-rec-2_1.c
>  create mode 100644 gcc/testsuite/gcc.dg/lto/fnptr-from-rec-3_0.c
>  create mode 100644 gcc/testsuite/gcc.dg/lto/fnptr-from-rec-3_1.c
> 
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index c1329015342..062ad0f65ad 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -1746,19 +1746,30 @@ class GTY((tag ("CIIK_SIMPLE")))
>  public:
>    cgraph_simple_indirect_info (int flags)
>      : cgraph_indirect_call_info (CIIK_SIMPLE, flags), offset (0),
> -    agg_contents (false), member_ptr (false), by_ref (false),
> +    rec_type (NULL_TREE), fld_offset (0), agg_contents (false),
> +    member_ptr (false), fnptr_loaded_from_record (false), by_ref (false),
>      guaranteed_unmodified (false)
>      {}
>  
>    /* When agg_content is set, an offset where the call pointer is located
>       within the aggregate.  */
>    HOST_WIDE_INT offset;
> +  /* Only meaningful if fnptr_loaded_from_record is set.  Then it contains 
> the
> +     type of the record from which the target of the call was loaded. */
> +  tree rec_type;
> +  /* Only meaningful if fnptr_loaded_from_record is set.  Then it contains 
> the
> +     offset in bytes within the type above from which the target of the call
> +     was loaded.  */
> +  unsigned fld_offset;

I think this is exlusive with aggregate handling, so perhaps offset, can
be (ab)used for both.
> +/* If REF is a memory access that loads a function pointer (but not a method
> +   pointer) from a RECORD_TYPE, return true and store the type of the RECORD 
> to
> +   *REC_TYPE and the byte offset of the field to *FLD_OFFSET.  Otherwise 
> return
> +   false.  OHS es the "other hand side" which is used to check type
> +   compatibility with field in question, when possible.  */
> +
> +static bool
> +is_func_ptr_from_record (tree ref, tree *rec_type, unsigned *fld_offset,
> +                      tree ohs)
> +{
> +  if (!POINTER_TYPE_P (TREE_TYPE (ref))
> +      || TREE_CODE (TREE_TYPE (TREE_TYPE (ref))) != FUNCTION_TYPE)
> +    return false;
> +
> +  if (TREE_CODE (ref) == COMPONENT_REF
> +      && TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 0))) == RECORD_TYPE)
> +    {
> +      gcc_assert (POINTER_TYPE_P (TREE_TYPE (ohs)));
> +      ohs = TREE_TYPE (TREE_TYPE (ohs));
> +      tree ftype = TREE_TYPE (TREE_OPERAND (ref, 1));
> +      if (!POINTER_TYPE_P (ftype))
> +     return false;
> +      ftype = TREE_TYPE (ftype);
> +      if (!types_compatible_p (ohs, ftype))
> +     return false;
> +
> +      tree tree_off = bit_position (TREE_OPERAND (ref, 1));
> +      if (!tree_fits_shwi_p (tree_off))
> +     return false;
> +      HOST_WIDE_INT bit_offset = tree_to_shwi (tree_off);
> +      if (bit_offset % BITS_PER_UNIT)
> +     return false;
> +      HOST_WIDE_INT unit_offset = bit_offset / BITS_PER_UNIT;
> +      if (unit_offset > UINT_MAX)
> +     return false;
> +      *rec_type = TREE_TYPE (TREE_OPERAND (ref, 0));
> +      *fld_offset = unit_offset;
Isn't this mostly what get_addr_base_and_unit_offset does?
> +      return true;
> +    }
> +  else if (TREE_CODE (ref) == MEM_REF
> +        && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (ref, 0)))
> +        && (TREE_CODE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (ref, 0))))
> +            == RECORD_TYPE))
> +    {
> +      HOST_WIDE_INT unit_offset = tree_to_shwi (TREE_OPERAND (ref, 1));
> +      if (unit_offset > UINT_MAX)
> +     return false;
> +      *rec_type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (ref, 0)));
> +      *fld_offset = unit_offset;
> +      return true;
> +    }
> +  return false;
> +}
> +

Patch is OK.
Thanks,
Honza

Reply via email to