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?