On Tue, 10 Oct 2023, Jakub Jelinek wrote: > On Tue, Oct 10, 2023 at 11:59:28AM +0000, Richard Biener wrote: > > > I don't see why the CONSTRUCTOR case couldn't be fine regardless of the > > > vuse. Though, am not really sure when a CONSTRUCTOR would appear, the > > > lhs would need to be an SSA_NAME, so wouldn't for vectors that be a > > > VECTOR_CST instead, etc.? Ah, perhaps a vector with some non-constant > > > element in it. > > > > Yeah, but what should that be interpreted to in terms of object-size?! > > The function in question doesn't compute object sizes, just minimum/maximum > number of non-zero bytes in some rhs of a store and whether everything > stored is 0s, or everything non-zeros, or some non-zeros followed by zero. > > > I think the only real case we'd see here is the MEM_REF RHS given > > we know we have a register type value. I'll note the function > > totally misses handled_component_p (), it only seems to handle > > *p and 'decl'. > > Yeah, maybe we could handle even that at some point. > Though perhaps better first to rewrite it completely, because the recursive > calls where in some cases it means one thing and in another case something > completely different are just bad design (or lack thereof).
Yeah ... (true for many similar pieces in pointer-query and other diagnostic passes...) > > > So maybe pass the vuse down to count_nonzero_bytes_addr and return false > > > in the idx > 0 case in there if gimple_vuse (stmt) != vuse? > > > > I don't know enough of the pass to do better, can you take it from here? > > One of the points is that we need to know the memory context (aka vuse) > > of both the original store and the load we are interpreting. For > > _addr I wasn't sure how we arrive here. As you said, this is a bit > > of spaghetti and I don't want to untangle this any further. > > I meant something like below, without getting rid of the -Wshadow stuff > in there my initial attempt didn't work. This passes the new testcase > as well as the testcase you've been touching, but haven't tested it beyond > that yet. Works for me if it turns out working. > In theory we could even handle some cases with gimple_vuse (stmt) != vuse, > because we save a copy of the strinfo state at the end of basic blocks and > only throw that away after we process all dominator children. But we'd need > to figure out at which bb to look and temporarily switch the vectors. As we need sth for the branch(es) I think we should do that as followup at most. Thanks, Richard. > 2023-10-10 Richard Biener <rguent...@suse.de> > Jakub Jelinek <ja...@redhat.com> > > PR tree-optimization/111519 > * tree-ssa-strlen.cc (strlen_pass::count_nonzero_bytes): Add vuse > argument and pass it through to recursive calls and > count_nonzero_bytes_addr calls. Don't shadow the stmt argument, but > change stmt for gimple_assign_single_p statements for which we don't > immediately punt. > (strlen_pass::count_nonzero_bytes_addr): Add vuse argument and pass > it through to recursive calls and count_nonzero_bytes calls. Don't > use get_strinfo if gimple_vuse (stmt) is different from vuse. Don't > shadow the stmt argument. > > * gcc.dg/torture/pr111519.c: New testcase. > > --- gcc/tree-ssa-strlen.cc.jj 2023-08-30 11:21:38.539521966 +0200 > +++ gcc/tree-ssa-strlen.cc 2023-10-10 15:05:44.731871218 +0200 > @@ -281,14 +281,14 @@ public: > gimple *stmt, > unsigned lenrange[3], bool *nulterm, > bool *allnul, bool *allnonnul); > - bool count_nonzero_bytes (tree exp, > + bool count_nonzero_bytes (tree exp, tree vuse, > gimple *stmt, > unsigned HOST_WIDE_INT offset, > unsigned HOST_WIDE_INT nbytes, > unsigned lenrange[3], bool *nulterm, > bool *allnul, bool *allnonnul, > ssa_name_limit_t &snlim); > - bool count_nonzero_bytes_addr (tree exp, > + bool count_nonzero_bytes_addr (tree exp, tree vuse, > gimple *stmt, > unsigned HOST_WIDE_INT offset, > unsigned HOST_WIDE_INT nbytes, > @@ -4531,8 +4531,8 @@ nonzero_bytes_for_type (tree type, unsig > } > > /* Recursively determine the minimum and maximum number of leading nonzero > - bytes in the representation of EXP and set LENRANGE[0] and LENRANGE[1] > - to each. > + bytes in the representation of EXP at memory state VUSE and set > + LENRANGE[0] and LENRANGE[1] to each. > Sets LENRANGE[2] to the total size of the access (which may be less > than LENRANGE[1] when what's being referenced by EXP is a pointer > rather than an array). > @@ -4546,7 +4546,7 @@ nonzero_bytes_for_type (tree type, unsig > Returns true on success and false otherwise. */ > > bool > -strlen_pass::count_nonzero_bytes (tree exp, gimple *stmt, > +strlen_pass::count_nonzero_bytes (tree exp, tree vuse, gimple *stmt, > unsigned HOST_WIDE_INT offset, > unsigned HOST_WIDE_INT nbytes, > unsigned lenrange[3], bool *nulterm, > @@ -4566,22 +4566,23 @@ strlen_pass::count_nonzero_bytes (tree e > exact value is not known) recurse once to set the range > for an arbitrary constant. */ > exp = build_int_cst (type, 1); > - return count_nonzero_bytes (exp, stmt, > + return count_nonzero_bytes (exp, vuse, stmt, > offset, 1, lenrange, > nulterm, allnul, allnonnul, snlim); > } > > - gimple *stmt = SSA_NAME_DEF_STMT (exp); > - if (gimple_assign_single_p (stmt)) > + gimple *g = SSA_NAME_DEF_STMT (exp); > + if (gimple_assign_single_p (g)) > { > - exp = gimple_assign_rhs1 (stmt); > + exp = gimple_assign_rhs1 (g); > if (!DECL_P (exp) > && TREE_CODE (exp) != CONSTRUCTOR > && TREE_CODE (exp) != MEM_REF) > return false; > /* Handle DECLs, CONSTRUCTOR and MEM_REF below. */ > + stmt = g; > } > - else if (gimple_code (stmt) == GIMPLE_PHI) > + else if (gimple_code (g) == GIMPLE_PHI) > { > /* Avoid processing an SSA_NAME that has already been visited > or if an SSA_NAME limit has been reached. Indicate success > @@ -4590,11 +4591,11 @@ strlen_pass::count_nonzero_bytes (tree e > return res > 0; > > /* Determine the minimum and maximum from the PHI arguments. */ > - unsigned int n = gimple_phi_num_args (stmt); > + unsigned int n = gimple_phi_num_args (g); > for (unsigned i = 0; i != n; i++) > { > - tree def = gimple_phi_arg_def (stmt, i); > - if (!count_nonzero_bytes (def, stmt, > + tree def = gimple_phi_arg_def (g, i); > + if (!count_nonzero_bytes (def, vuse, g, > offset, nbytes, lenrange, nulterm, > allnul, allnonnul, snlim)) > return false; > @@ -4652,7 +4653,7 @@ strlen_pass::count_nonzero_bytes (tree e > return false; > > /* Handle MEM_REF = SSA_NAME types of assignments. */ > - return count_nonzero_bytes_addr (arg, stmt, > + return count_nonzero_bytes_addr (arg, vuse, stmt, > offset, nbytes, lenrange, nulterm, > allnul, allnonnul, snlim); > } > @@ -4765,7 +4766,7 @@ strlen_pass::count_nonzero_bytes (tree e > bytes that are pointed to by EXP, which should be a pointer. */ > > bool > -strlen_pass::count_nonzero_bytes_addr (tree exp, gimple *stmt, > +strlen_pass::count_nonzero_bytes_addr (tree exp, tree vuse, gimple *stmt, > unsigned HOST_WIDE_INT offset, > unsigned HOST_WIDE_INT nbytes, > unsigned lenrange[3], bool *nulterm, > @@ -4775,6 +4776,14 @@ strlen_pass::count_nonzero_bytes_addr (t > int idx = get_stridx (exp, stmt); > if (idx > 0) > { > + /* get_strinfo reflects string lengths before the current statement, > + where the current statement is the outermost count_nonzero_bytes > + stmt. If there are any stores in between stmt and that > + current statement, the string length information might describe > + something significantly different. */ > + if (gimple_vuse (stmt) != vuse) > + return false; > + > strinfo *si = get_strinfo (idx); > if (!si) > return false; > @@ -4835,14 +4844,14 @@ strlen_pass::count_nonzero_bytes_addr (t > } > > if (TREE_CODE (exp) == ADDR_EXPR) > - return count_nonzero_bytes (TREE_OPERAND (exp, 0), stmt, > + return count_nonzero_bytes (TREE_OPERAND (exp, 0), vuse, stmt, > offset, nbytes, > lenrange, nulterm, allnul, allnonnul, snlim); > > if (TREE_CODE (exp) == SSA_NAME) > { > - gimple *stmt = SSA_NAME_DEF_STMT (exp); > - if (gimple_code (stmt) == GIMPLE_PHI) > + gimple *g = SSA_NAME_DEF_STMT (exp); > + if (gimple_code (g) == GIMPLE_PHI) > { > /* Avoid processing an SSA_NAME that has already been visited > or if an SSA_NAME limit has been reached. Indicate success > @@ -4851,11 +4860,11 @@ strlen_pass::count_nonzero_bytes_addr (t > return res > 0; > > /* Determine the minimum and maximum from the PHI arguments. */ > - unsigned int n = gimple_phi_num_args (stmt); > + unsigned int n = gimple_phi_num_args (g); > for (unsigned i = 0; i != n; i++) > { > - tree def = gimple_phi_arg_def (stmt, i); > - if (!count_nonzero_bytes_addr (def, stmt, > + tree def = gimple_phi_arg_def (g, i); > + if (!count_nonzero_bytes_addr (def, vuse, g, > offset, nbytes, lenrange, > nulterm, allnul, allnonnul, > snlim)) > @@ -4903,7 +4912,7 @@ strlen_pass::count_nonzero_bytes (tree e > > ssa_name_limit_t snlim; > tree expr = expr_or_type; > - return count_nonzero_bytes (expr, stmt, > + return count_nonzero_bytes (expr, gimple_vuse (stmt), stmt, > 0, 0, lenrange, nulterm, allnul, allnonnul, > snlim); > } > --- gcc/testsuite/gcc.dg/torture/pr111519.c.jj 2023-10-10 > 14:36:22.195889648 +0200 > +++ gcc/testsuite/gcc.dg/torture/pr111519.c 2023-10-10 14:36:22.195889648 > +0200 > @@ -0,0 +1,48 @@ > +/* PR tree-optimization/111519 */ > +/* { dg-do run } */ > + > +int a, o; > +char b, f, i; > +long c; > +static signed char d; > +static char g; > +unsigned *h; > +signed char *e = &f; > +static signed char **j = &e; > +static long k[2]; > +unsigned **l = &h; > +short m; > +volatile int z; > + > +__attribute__((noipa)) void > +foo (char *p) > +{ > + (void) p; > +} > + > +int > +main () > +{ > + int p = z; > + signed char *n = &d; > + *n = 0; > + while (c) > + for (; i; i--) > + ; > + for (g = 0; g <= 1; g++) > + { > + *n = **j; > + k[g] = 0 != &m; > + *e = l && k[0]; > + } > + if (p) > + foo (&b); > + for (; o < 4; o++) > + { > + a = d; > + if (p) > + foo (&b); > + } > + if (a != 1) > + __builtin_abort (); > +} > > > Jakub > > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)