A call through a std::function whose target is fixed for the object's
lifetime is a two-level indirect dispatch: operator() calls the handler
stored in _M_invoker, and the handler calls the callable stored in the
_Any_data buffer. When the call sits in a loop, neither level becomes
direct before the inliner runs, so the loop keeps an indirect call and
is not vectorized. SPEC CPU 2026 772.marian_r has this shape in its
element-wise kernels when built with larger early-inlining limits
(--param early-inlining-insns=256).

This series resolves both levels in ipa_analyze_node, once early
inlining has brought the construction and the call into one function.
For a call through a field of a local variable or by-value parameter, it
proves with the callees' modref summaries that every path stores the
same function into the field and that no call in between can write it,
and then makes the call graph edge direct. The function pointer that the
handler calls through is added to the edge's aggregate jump function, so
that IPA-CP or indirect inlining resolves the second level.

A handler calls through a field of its own argument, so its modref
summary alone says that it may write any memory. Patch 1 lets modref
record such "deferred calls", with the stores the function makes
without them, so that a caller that knows the field's value can check
the handler precisely.

Two shapes are not resolved. By design, the target of a deferred call
must write no memory and make no deferred calls of its own, so only
chains of two levels, as in std::function, resolve. Deeper chains would
need the caller to resolve the inner fields at the state of the inner
call, an induction this series does not do. And a try/catch inside the
loop prevents the proof: __cxa_begin_catch and __cxa_end_catch have no
modref summary, so the path through the catch block may write the field.
With the try outside the loop, the call is resolved.

>From summary generation until inline_transform, also through LTO
streaming, the call graph edge is direct while its statement is still
indirect. ICF, the call graph verifier and the LTO stream-in fixup
handle that.

Such an edge to a callback_only function also gets callback edges. If
IPA-CP replaces all parameters of that function with constants, GCC ICEs
when inline_transform redirects them. A direct call ICEs the same way
without the series (PR128602). Handlers inlined into loops can also
expose PR128511, an existing wrong-code bug in
chrec_fold_multiply_poly_poly.

Patch breakdown:

 1/2  ipa-modref: Record deferred dispatch calls
      The new summary fields and the second access analysis that
      computes them. No user on its own.

 2/2  ipa-prop: Devirtualize calls through function-pointer fields
      The proof, the direct edges and the aggregate jump-function
      items, the -fdevirtualize-function-pointers option (enabled at
      -O3), and C, C++ and LTO tests.


Konstantinos Eleftheriou (1):
  ipa-prop: Devirtualize calls through function-pointer fields
    [PR126241]

Philipp Tomsich (1):
  ipa-modref: Record deferred dispatch calls [PR126241]

 gcc/common.opt                                |   4 +
 gcc/common.opt.urls                           |   3 +
 gcc/doc/invoke.texi                           |  13 +-
 gcc/ipa-fnsummary.cc                          |  13 +
 gcc/ipa-modref.cc                             | 114 ++++-
 gcc/ipa-modref.h                              |  23 +
 gcc/ipa-prop.cc                               | 380 ++++++++++++++
 gcc/opts.cc                                   |   1 +
 .../g++.dg/ipa/devirt-function-pointers.C     | 144 ++++++
 .../g++.dg/lto/devirt-function-pointers_0.C   |  63 +++
 .../gcc.dg/ipa/devirt-function-pointers-1.c   | 462 ++++++++++++++++++
 .../gcc.dg/ipa/devirt-function-pointers-2.c   |  48 ++
 gcc/tree-ssa-alias.cc                         |   2 +-
 13 files changed, 1265 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ipa/devirt-function-pointers.C
 create mode 100644 gcc/testsuite/g++.dg/lto/devirt-function-pointers_0.C
 create mode 100644 gcc/testsuite/gcc.dg/ipa/devirt-function-pointers-1.c
 create mode 100644 gcc/testsuite/gcc.dg/ipa/devirt-function-pointers-2.c

-- 
2.55.0

Reply via email to