On Tue, 25 Nov 2025, Jakub Jelinek wrote:
> Hi!
>
> The following testcase is miscompiled by modref thinking one store is not
> needed:
> ipa-modref: call stmt IteratorImp::operator++ (&clast.d_imp);
> ipa-modref: call to void IteratorImp::operator++()/2 does not use ref: MEM
> <struct PMF *> [(struct iterator *)&clast + 8B] alias sets: 5->5
> ipa-modref: call stmt IteratorImp::operator++ (&clast.d_imp);
> ipa-modref: call to void IteratorImp::operator++()/2 does not use ref: MEM
> <struct PMF *> [(struct iterator *)&clast + 8B] alias sets: 5->5
> ipa-modref: call stmt IteratorImp::operator++ (&clast.d_imp);
> ipa-modref: call to void IteratorImp::operator++()/2 kills clast
> Deleted dead store: MEM <struct PMF *> [(struct iterator *)&clast + 8B] =
> &MEM <struct PMF[16]> [(void *)&mX + 120B];
> This only happens when PMF is pointer to member function, not when PMF
> is say user structure with void * and unsigned long members (as an
> example of something with the same size as PMF) or when PMF is
> char (as an example of something with 0 alias set, which is what
> PMF for some reason has).
> Apparently it is related to the r0-82711 change (PR22369 and PR22451)
> which made also PMF * types to have alias set 0. While probably
> modref should be analyzed what's going on, I find it really weird and
> IMHO unnecessary to use alias set 0 for any pointers, we don't use
> alias set 0 for e.g. void * pointers. Commenting out the
> INDIRECT_TYPE_P (t) && TYPE_PTRMEMFUNC_P (TREE_TYPE (t))
> special case fixes the testcase, and so does the following patch
> which treats PMF * for aliasing the same as void *.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, tried also
> the testcases from those 2 PRs (which weren't checked in or maybe came
> from the testsuite) and they still work fine.
>
> 2025-11-25 Jakub Jelinek <[email protected]>
>
> PR c++/119969
> * cp-objcp-common.cc (cxx_get_alias_set): Return
> get_alias_set (ptr_type_node) for pointer/reference to
> TYPE_PTRMEMFUNC_P instead of 0.
>
> * g++.dg/torture/pr119969.C: New test.
>
> --- gcc/cp/cp-objcp-common.cc.jj 2025-10-09 22:41:19.871273246 +0200
> +++ gcc/cp/cp-objcp-common.cc 2025-11-25 12:37:38.251339146 +0100
> @@ -181,10 +181,10 @@ cxx_get_alias_set (tree t)
> return get_alias_set (TYPE_CONTEXT (t));
>
> /* Punt on PMFs until we canonicalize functions properly. */
> - if (TYPE_PTRMEMFUNC_P (t)
> - || (INDIRECT_TYPE_P (t)
> - && TYPE_PTRMEMFUNC_P (TREE_TYPE (t))))
> + if (TYPE_PTRMEMFUNC_P (t))
> return 0;
> + if (INDIRECT_TYPE_P (t) && TYPE_PTRMEMFUNC_P (TREE_TYPE (t)))
> + return get_alias_set (ptr_type_node);
I think we'd get the same effect from alias.cc itself iff
TYPE_PTRMEMFUNC_P were TYPE_STRUCTURAL_EQUALITY_P. In particular
I'll not that a PMF ** would not end up invoking the langhook for
PMF *. While the pointer code in get_alias_set eventually
looks at the "base", it does not re-query the frontend about it,
but only special-case
if (TREE_CODE (p) == VOID_TYPE || TYPE_STRUCTURAL_EQUALITY_P (p))
set = get_alias_set (ptr_type_node);
so given it seems we don't exactly know what goes wrong I fear while
the fix makes sense, it's not complete? Unless TYPE_PTRMEMFUNC_P
already is TYPE_STRUCTURAL_EQUALITY_P, in which case all the
special-casing could go?
Richard.
>
> return c_common_get_alias_set (t);
> }
> --- gcc/testsuite/g++.dg/torture/pr119969.C.jj 2025-11-25
> 12:38:20.120143562 +0100
> +++ gcc/testsuite/g++.dg/torture/pr119969.C 2025-11-25 12:38:42.407758775
> +0100
> @@ -0,0 +1,46 @@
> +// PR c++/119969
> +// { dg-do run }
> +
> +struct S {};
> +using PMF = void (S::*)();
> +using Block = PMF[16];
> +using BlockPtr = Block*;
> +
> +struct IteratorImp {
> + Block** d_blockPtr_p;
> + PMF* d_value_p;
> +
> + void operator++();
> + PMF& operator*() const { return *d_value_p; }
> +};
> +
> +void IteratorImp::operator++() {
> + int offset = 1 + (d_value_p - **d_blockPtr_p);
> + d_blockPtr_p += offset / 16;
> + d_value_p = **d_blockPtr_p + (offset % 16);
> +}
> +
> +struct iterator {
> + IteratorImp d_imp;
> +};
> +
> +struct D {
> + Block* d_blockPtrs[1];
> + Block d_block;
> + PMF* d_start_p;
> +};
> +
> +D mX;
> +
> +void privateInit(int numElements) {
> + mX.d_blockPtrs[0] = &mX.d_block;
> + mX.d_start_p = mX.d_block + (numElements + 7);
> +}
> +
> +int main() {
> + privateInit(0);
> + iterator cbgn = {{mX.d_blockPtrs, mX.d_block + 7}};
> + auto clast = cbgn;
> + ++clast.d_imp;
> + if (&*cbgn.d_imp == &*clast.d_imp) return 1;
> +}
>
> Jakub
>
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)