https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120358
Richard Biener <rguenth at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Last reconfirmed|2025-05-19 00:00:00 |2025-07-07 Ever confirmed|0 |1 Status|UNCONFIRMED |ASSIGNED Assignee|unassigned at gcc dot gnu.org |rguenth at gcc dot gnu.org --- Comment #31 from Richard Biener <rguenth at gcc dot gnu.org> --- (In reply to Alexander Monakov from comment #28) > Created attachment 61811 [details] > standalone testcase > > A standalone variant of the comment #22 testcase, preprocessed and with > enough noipa stubs added to avoid linking with QtBase. Thanks, this produces with -O2 -std=gnu++20 > ./a.out test2: 1 2 3 while -O3 -std=gnu++20 > ./a.out test2: 1 2 3 18446744073709551615 it also reproduces with -O3 -fno-tree-vectorize, the main difference seems to be IPA and inline differences. -fno-tree-pta fixes it. There are exactly two(!) comparisons eliminated with the added path, the 2nd triggers the issue in VRP1: Folding statement: if (e_137 != n_142) Registering value_relation (e_137 != n_142) on (33->35) Registering value_relation (e_137 == n_142) on (33->34) Visiting conditional with predicate: if (e_137 != n_142) With known ranges e_137: [prange] const char16_t * [1, +INF] n_142: [prange] const char16_t * VARYING Predicate evaluates to: DON'T KNOW ***dbgcnt: lower limit 2 reached for prefetch.*** ***dbgcnt: upper limit 2 reached for prefetch.*** gimple_simplified to if (1 != 0) Folded into: if (1 != 0) We have # PT = null e_137 = _130 + _136; ... # PT = nonlocal escaped null const-pool { D.189684 } (escaped) # USE = nonlocal escaped null const-pool { D.189684 } (escaped) n_142 = QtPrivate::qustrchr (D.206385, 46); D.206385 ={v} {CLOBBER(eos)}; if (e_137 != n_142) the interesting thing is that VRP computes e_137 : [prange] const char16_t * [1, +INF] VRP clears PT = null on the pointer and thus makes the compare compare 'nothing' with 'something'. points-to computes # PT = null _130 = str.m_data; the last initilizer is <bb 27> [local count: 1034442871]: _104 = state$start_100 + state$extra_101; MEM[(struct QChar *)&D.206374 clique 15 base 1] ={v} {CLOBBER(bob)}; MEM[(struct QChar *)&D.206374 clique 15 base 1].ucs = 46; str = MEM[(const struct QStringView &)&tok2 + 32]; PTA has tok2.128+192 = __old_val_8 tok2.128+192 = &NONLOCAL tok2.128+192 = &NONLOCAL tok2.128+192 = __old_val_8 str.0+64 = tok2.128+192 _130 = str.64+64 for some reason the str = MEM[(const struct QStringView &)&tok2 + 32]; assignment does not generate a str.64+64 = tok2.192+64 constraint, that is, the source and dest varinfos do not seem to overlap. We end up with (gdb) p lhsc $3 = {<vec<constraint_expr, va_heap, vl_ptr>> = {m_vec = 0x5219080 = {{ type = SCALAR, var = 145, offset = 0}, {type = SCALAR, var = 146, offset = 0}}}, <No data fields>} (gdb) p rhsc $6 = {<vec<constraint_expr, va_heap, vl_ptr>> = {m_vec = 0x5824f60 = {{ type = SCALAR, var = 29, offset = 0}}}, <No data fields>} where for the RHS that's the third var from 'tok2'. Having the copy offset by 32 is a bit odd sice that puts us inside the third subobject that we have not sub-setted. In any case the issue seems to be that do_structure_copy applies the offset "twice" here, it does if (!get_ref_base_and_extent_hwi (lhsop, &lhsoffset, &lhssize, &reverse) || !get_ref_base_and_extent_hwi (rhsop, &rhsoffset, &rhssize, &reverse)) { process_all_all_constraints (lhsc, rhsc); return; computing 256 for rhsoffset (from the MEM_REF offset of 32 bytes) and then checking overlap of two varinfos with || ranges_overlap_p (lhsv->offset + rhsoffset, lhsv->size, rhsv->offset + lhsoffset, rhsv->size))) the 2nd var of the LHS does not satisfy overlap. I think the logic might only work when the offset computed by get_ref_base_and_extent_hwi aligns with any of the fields offsets.