Hi Martin, Honza,

Thanks again for agreeing to mentor this. I've been looking into the
IPA-ICF comparison logic and wanted to share what I've found so far.

I went through sem_function::equals_wpa, func_checker and the variable
comparison code, and found roughly two dozen candidate categories:
properties that ICF either doesn't compare or only partially compares.
I've also been checking whether later passes actually consume those
properties, since that's where a mismatch can become a correctness
issue.

One case looks particularly concerning. It's related to PR123227, but
this time the attribute is on a call's function type:

  typedef void fn (int *) __attribute__ ((nonnull));
  int *volatile vp;
  __attribute__ ((noipa)) void id (int *p) {}
  void pa (int *p) { ((fn *) id) (p); id (p); id (p); }
  void pb (int *p) { id (p); id (p); id (p); }
  int use_b (int *q) { pb (q); return q == 0; }
  int main (void) { return use_b (vp) != 1; }

At -O2 on trunk, this exits 1, while disabling IPA-ICF makes it exit 0.
The two functions are merged, and after inlining, VRP appears to use the
nonnull information from pa to eliminate the null check in use_b.

On GCC 13.3, the exact testcase exits 0, but a variant with use_b marked
noinline,noclone exits 1 with IPA-ICF and 0 without it.

The likely issue is in compare_gimple_call (ipa-icf-gimple.cc:729). It
uses comp_type_attributes, whose ordinary attribute comparison ignores
attributes that don't affect type identity, and nonnull is one of them.
I couldn't find an existing report for this call-fntype case.

I'm considering following the approach used in PR123227 and comparing
the call function-type attribute lists directly. I'm not sure whether we
should reject every attribute difference or only those that can affect
semantics or downstream optimization. Candidates include nonnull,
returns_nonnull, the allocation and alignment attributes, the internal
fn spec, and reproducible and unsequenced. I'd like to investigate this
rather than assume it's a complete list.

For the 350-hour project, I'm thinking of focusing on:

  - Maintaining an inventory of these properties.
  - Adding regression tests for the relevant cases.
  - Fixing correctness issues identified by the audit.
  - Investigating whether selected pre-ICF summaries, such as return
    value ranges, need to be preserved.
  - Testing multi-unit LTO cases, including differing options between
    units.
  - Measuring whether normalizing conservative cases is worthwhile
    compared with simply declining the merge.

I'd appreciate your thoughts on a few design decisions:

  1. Should we compare the complete call-fntype attribute lists, as in
     PR123227, or use a narrower comparison?
  2. How much merge-rate loss would be acceptable for a correctness fix?
     For reference, the PR119006 fix declined 25 merges at -O2 and 29 at
     -Os on a libstdc++ LTO build.
  3. Would you prefer the inventory in source comments, documentation,
     or the testsuite?
  4. For properties that C tests cannot express, would GIMPLE FE tests
     or a plugin be appropriate?

Thanks,
Rohith

Reply via email to