https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98499

--- Comment #8 from Sergei Trofimovich <slyfox at gcc dot gnu.org> ---
(In reply to Richard Biener from comment #7)
> (In reply to Sergei Trofimovich from comment #6)
> > (In reply to Richard Biener from comment #5)
> > > Possibly in discovering pure/constness.  See uses of
> > > gimple_call_return_slot_opt_p in tree-ssa-structalias.c
> > 
> > Aha, that's useful!
> > 
> > Trying to understand the problem better myself: `-fdump-tree-all` has
> > seemingly relevant `036t.ealias` that precedes breaking `037t.fre1`.
> > 
> > I assume `036t.ealias` analyses individual functions locally and does not
> > take into account other details, thus main() analysis should be enough:
> 
> Well - it does take into account fnspecs derived by modref analysis for
> Importer::Importer - specifically ...

Oh, thank you! Only after many printf() attempts it sunk in that `036t.ealias`
is using data from seemingly later `043t.modref1` pass. It is so confusing!

> > ```
> > ...
> > Points-to sets
> > 
> > ANYTHING = { ANYTHING }
> > ESCAPED = { ESCAPED NONLOCAL }
> > NONLOCAL = { ESCAPED NONLOCAL }
> > STOREDANYTHING = { }
> > INTEGER = { ANYTHING }
> > _ZN8ImporterC1Ev = { }
> > imp.0+64 = { ESCAPED NONLOCAL } same as _6
> > imp.64+8 = { ESCAPED NONLOCAL }
> > __builtin_trap = { }
> > main = { }
> > CALLUSED(9) = { ESCAPED NONLOCAL imp.0+64 imp.64+8 } same as callarg(11)
> > CALLCLOBBERED(10) = { ESCAPED NONLOCAL imp.0+64 imp.64+8 } same as
> > callarg(11)
> > callarg(11) = { ESCAPED NONLOCAL imp.0+64 imp.64+8 }
> 
> the above shows we do not consider 'imp' to escape, and thus
> 
> > _6 = { ESCAPED NONLOCAL }
> 
> _6 does not point to 'imp'.
> 
> Relevant parts of handle_rhs_call are probably
> 
>       /* As we compute ESCAPED context-insensitive we do not gain
>          any precision with just EAF_NOCLOBBER but not EAF_NOESCAPE
>          set.  The argument would still get clobbered through the
>          escape solution.  */
>       if ((flags & EAF_NOCLOBBER)
>            && (flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE)))
>         {
> ...
> 
> specifically lines
> 
>           if (!(flags & (EAF_NOESCAPE | EAF_DIRECT)))
>             make_indirect_escape_constraint (tem);
> 
> probably do not trigger because of the invalid modref analysis.  I suggest
> to look at the early modref pass dump (it's after FRE but still applies
> to callers)

Yeah, that makes sense.

Minor correction: we get into second branch of handle_rhs_call():

        else if (flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE))

I traced it through around and I agree it looks like an ipa-modref bug.

Mechanically ipa-modref does not handle `gimple_call_return_slot_opt_p()` and
assumes 'foo = bar() [return slot optimization]' never escape 'foo'.

As a workaround I attempted to pessimize modref and it fixes the test case:

```diff
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -1614,23 +1614,26 @@ analyze_ssa_name_flags (tree name, vec<modref_lattice>
&lattice, int depth,
        {
          if (gimple_return_retval (ret) == name)
            lattice[index].merge (~EAF_UNUSED);
          else if (memory_access_to (gimple_return_retval (ret), name))
            lattice[index].merge_direct_load ();
        }
       /* Account for LHS store, arg loads and flags from callee function.  */
       else if (gcall *call = dyn_cast <gcall *> (use_stmt))
        {
          tree callee = gimple_call_fndecl (call);
-
+          if (gimple_call_return_slot_opt_p (call)
+              && gimple_call_lhs (call) != NULL_TREE
+              && TREE_ADDRESSABLE (TREE_TYPE (gimple_call_lhs (call))))
+           lattice[index].merge (0);
          /* Recursion would require bit of propagation; give up for now.  */
-         if (callee && !ipa && recursive_call_p (current_function_decl,
+         else if (callee && !ipa && recursive_call_p (current_function_decl,
                                                  callee))
            lattice[index].merge (0);
          else
            {
              int ecf_flags = gimple_call_flags (call);
              bool ignore_stores = ignore_stores_p (current_function_decl,
                                                    ecf_flags);
              bool ignore_retval = ignore_retval_p (current_function_decl,
                                                    ecf_flags);
```

Mechanically ESCAPE bit was lost in Importer::Importer at:

1. in "this->base_path = dir_name (); [r s o]" ipa-modref derived 'DIRECT
NODIRECTESCAPE NOESCAPE' flags as it assumed it's just a memory store without
'this' escape.
2. main() optimised Inporter::Importer(&imp) as a noescape using

  handle_rhs_call()
    -> gimple_call_arg_flags(arg_index = 0)
       -> - fnspec was empty
          - modref's get_modref_function_summary() adds 'DIRECT NODIRECTESCAPE
NOESCAPE'

Is it a reasonable fix? Or it's too conservative and we could easily do better?

I copied predicate from handle_rhs_call(), but did not pick constrain copying:

  /* And if we applied NRV the address of the return slot escapes as well.  */
  if (gimple_call_return_slot_opt_p (stmt)
      && gimple_call_lhs (stmt) != NULL_TREE
      && TREE_ADDRESSABLE (TREE_TYPE (gimple_call_lhs (stmt))))
    {
      auto_vec<ce_s> tmpc;
      struct constraint_expr lhsc, *c;
      get_constraint_for_address_of (gimple_call_lhs (stmt), &tmpc);
      lhsc.var = escaped_id;
      lhsc.offset = 0;
      lhsc.type = SCALAR;
      FOR_EACH_VEC_ELT (tmpc, i, c)
        process_constraint (new_constraint (lhsc, *c));
    }

Would constraint copy transfer to ipa-modref framework roughly the same?

Reply via email to