The following fixes a bug in PTA that caused const/pure calls and calls with fn spec attribute to have mishandled return solutions or CALLCLOBBERED solutions.
Bootstrapped and tested on x86_64-unknown-linux-gnu, no testcase for the reported issue (I failed to massage the three-file fortran testcase). Richard. 2016-09-21 Richard Biener <rguent...@suse.de> PR tree-optimization/77648 * tree-ssa-structalias.c (process_constraint): Handle all DEREF with complex RHS. (make_transitive_closure_constraints): Adjust comment. (make_any_offset_constraints): New function. (handle_rhs_call): Make sure to first expand a pointer to all subfields before transitively closing it. (handle_const_call): Likewise. Properly expand returned pointers as well. (handle_pure_call): Likewise. * gcc.dg/torture/pr77648-1.c: New testcase. * gcc.dg/torture/pr77648-2.c: Likewise. Index: gcc/tree-ssa-structalias.c =================================================================== *** gcc/tree-ssa-structalias.c (revision 240255) --- gcc/tree-ssa-structalias.c (working copy) *************** process_constraint (constraint_t t) *** 3010,3016 **** process_constraint (new_constraint (tmplhs, rhs)); process_constraint (new_constraint (lhs, tmplhs)); } ! else if (rhs.type == ADDRESSOF && lhs.type == DEREF) { /* Split into tmp = &rhs, *lhs = tmp */ struct constraint_expr tmplhs; --- 3010,3016 ---- process_constraint (new_constraint (tmplhs, rhs)); process_constraint (new_constraint (lhs, tmplhs)); } ! else if ((rhs.type != SCALAR || rhs.offset != 0) && lhs.type == DEREF) { /* Split into tmp = &rhs, *lhs = tmp */ struct constraint_expr tmplhs; *************** make_transitive_closure_constraints (var *** 3747,3753 **** { struct constraint_expr lhs, rhs; ! /* VAR = *VAR; */ lhs.type = SCALAR; lhs.var = vi->id; lhs.offset = 0; --- 3747,3753 ---- { struct constraint_expr lhs, rhs; ! /* VAR = *(VAR + UNKNOWN); */ lhs.type = SCALAR; lhs.var = vi->id; lhs.offset = 0; *************** make_transitive_closure_constraints (var *** 3757,3762 **** --- 3757,3779 ---- process_constraint (new_constraint (lhs, rhs)); } + /* Add constraints to that the solution of VI has all subvariables added. */ + + static void + make_any_offset_constraints (varinfo_t vi) + { + struct constraint_expr lhs, rhs; + + /* VAR = VAR + UNKNOWN; */ + lhs.type = SCALAR; + lhs.var = vi->id; + lhs.offset = 0; + rhs.type = SCALAR; + rhs.var = vi->id; + rhs.offset = UNKNOWN_OFFSET; + process_constraint (new_constraint (lhs, rhs)); + } + /* Temporary storage for fake var decls. */ struct obstack fake_var_decl_obstack; *************** handle_rhs_call (gcall *stmt, vec<ce_s> *** 3902,3916 **** && (flags & EAF_NOESCAPE)) { varinfo_t uses = get_call_use_vi (stmt); if (!(flags & EAF_DIRECT)) ! { ! varinfo_t tem = new_var_info (NULL_TREE, "callarg", true); ! make_constraint_to (tem->id, arg); ! make_transitive_closure_constraints (tem); ! make_copy_constraint (uses, tem->id); ! } ! else ! make_constraint_to (uses->id, arg); returns_uses = true; } else if (flags & EAF_NOESCAPE) --- 3919,3930 ---- && (flags & EAF_NOESCAPE)) { varinfo_t uses = get_call_use_vi (stmt); + varinfo_t tem = new_var_info (NULL_TREE, "callarg", true); + make_constraint_to (tem->id, arg); + make_any_offset_constraints (tem); if (!(flags & EAF_DIRECT)) ! make_transitive_closure_constraints (tem); ! make_copy_constraint (uses, tem->id); returns_uses = true; } else if (flags & EAF_NOESCAPE) *************** handle_rhs_call (gcall *stmt, vec<ce_s> *** 3920,3925 **** --- 3934,3940 ---- varinfo_t clobbers = get_call_clobber_vi (stmt); varinfo_t tem = new_var_info (NULL_TREE, "callarg", true); make_constraint_to (tem->id, arg); + make_any_offset_constraints (tem); if (!(flags & EAF_DIRECT)) make_transitive_closure_constraints (tem); make_copy_constraint (uses, tem->id); *************** handle_rhs_call (gcall *stmt, vec<ce_s> *** 3945,3951 **** if (returns_uses) { rhsc.var = get_call_use_vi (stmt)->id; ! rhsc.offset = 0; rhsc.type = SCALAR; results->safe_push (rhsc); } --- 3960,3966 ---- if (returns_uses) { rhsc.var = get_call_use_vi (stmt)->id; ! rhsc.offset = UNKNOWN_OFFSET; rhsc.type = SCALAR; results->safe_push (rhsc); } *************** handle_const_call (gcall *stmt, vec<ce_s *** 4054,4059 **** --- 4069,4075 ---- if (gimple_call_chain (stmt)) { varinfo_t uses = get_call_use_vi (stmt); + make_any_offset_constraints (uses); make_transitive_closure_constraints (uses); make_constraint_to (uses->id, gimple_call_chain (stmt)); rhsc.var = uses->id; *************** handle_const_call (gcall *stmt, vec<ce_s *** 4062,4077 **** results->safe_push (rhsc); } ! /* May return arguments. */ for (k = 0; k < gimple_call_num_args (stmt); ++k) { tree arg = gimple_call_arg (stmt, k); auto_vec<ce_s> argc; - unsigned i; - struct constraint_expr *argp; get_constraint_for_rhs (arg, &argc); ! FOR_EACH_VEC_ELT (argc, i, argp) ! results->safe_push (*argp); } /* May return addresses of globals. */ --- 4078,4101 ---- results->safe_push (rhsc); } ! /* May return offsetted arguments. */ ! varinfo_t tem = NULL; ! if (gimple_call_num_args (stmt) != 0) ! tem = new_var_info (NULL_TREE, "callarg", true); for (k = 0; k < gimple_call_num_args (stmt); ++k) { tree arg = gimple_call_arg (stmt, k); auto_vec<ce_s> argc; get_constraint_for_rhs (arg, &argc); ! make_constraints_to (tem->id, argc); ! } ! if (tem) ! { ! ce_s ce; ! ce.type = SCALAR; ! ce.var = tem->id; ! ce.offset = UNKNOWN_OFFSET; ! results->safe_push (ce); } /* May return addresses of globals. */ *************** handle_pure_call (gcall *stmt, vec<ce_s> *** 4098,4103 **** --- 4122,4128 ---- if (!uses) { uses = get_call_use_vi (stmt); + make_any_offset_constraints (uses); make_transitive_closure_constraints (uses); } make_constraint_to (uses->id, arg); *************** handle_pure_call (gcall *stmt, vec<ce_s> *** 4109,4114 **** --- 4134,4140 ---- if (!uses) { uses = get_call_use_vi (stmt); + make_any_offset_constraints (uses); make_transitive_closure_constraints (uses); } make_constraint_to (uses->id, gimple_call_chain (stmt)); Index: gcc/testsuite/gcc.dg/torture/pr77648-1.c =================================================================== *** gcc/testsuite/gcc.dg/torture/pr77648-1.c (revision 0) --- gcc/testsuite/gcc.dg/torture/pr77648-1.c (working copy) *************** *** 0 **** --- 1,24 ---- + /* { dg-do run } */ + + struct S { int *p; int *q; }; + + int **__attribute__((noinline,noclone,pure)) foo (struct S *s) + { + int tem; + __asm__ ("" : "=g" (tem) : "g" (s->p)); + return &s->q; + } + + int main() + { + struct S s; + int i = 1, j = 2; + int **x; + s.p = &i; + s.q = &j; + x = foo (&s); + **x = 7; + if (j != 7) + __builtin_abort (); + return 0; + } Index: gcc/testsuite/gcc.dg/torture/pr77648-2.c =================================================================== *** gcc/testsuite/gcc.dg/torture/pr77648-2.c (revision 0) --- gcc/testsuite/gcc.dg/torture/pr77648-2.c (working copy) *************** *** 0 **** --- 1,22 ---- + /* { dg-do run } */ + + struct S { int *p; int *q; }; + + int **__attribute__((noinline,noclone,const)) foo (struct S *s) + { + return &s->q; + } + + int main() + { + struct S s; + int i = 1, j = 2; + int **x; + s.p = &i; + s.q = &j; + x = foo (&s); + **x = 7; + if (j != 7) + __builtin_abort (); + return 0; + }