On Wed, Jun 19, 2024 at 09:26:00AM +0200, Martin Uecker wrote: > Ok. Then should it, instead of > > TYPE_CANONICAL (x) > = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x)); > > be > > tree c = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x)); > TYPE_CANONICAL (x) = TREE_CANONICAL (c); > > in the patch below?
Ok, I've tried that, but that doesn't work, it ICEs on the pr114574-2.c testcase. What happens is that TYPE_CANONICAL (t) = t; is set by the caller and the first loop first sees x == t, so only handles pointers, and then moves to const struct S. As TYPE_CANONICAL (t) is t, build_qualified_type looks for an existing qualified type (and, not just that, it also moves the found qualified type to TYPE_NEXT_VARIANT (t) to speed it up next time!!), in this case returns x, so it then effectively does nothing, TYPE_CANONICAL (x) = TYPE_CANONICAL (x); and leaves the type TYPE_STRUCTURAL_EQUALITY_P, which is not what we want. Dunno if it in theory could also find some type later in the TYPE_NEXT_VARIANT chain and move it earlier. So, if the new patch is to be used, we need to add some extra handling for these problematic cases. One is if c == x (which can happen solely if TYPE_CANONICAL (t) == t), that is easy to handle, in that case we should make it the canonical type of itself, so TYPE_CANONICAL (x) = x; rather than TYPE_CANONICAL (x) = TYPE_CANONICAL (c). And then there is the theoretical case that c is some type from the TYPE_MAIN_VARIANT chain which we haven't processed yet. And that build_qualified_type moved it to the second position in the chain even when we haven't processed that yet. For that, I think we need to first process that c and only then restart handling of x. So, either we could: gcc_checking_assert (TYPE_MAIN_VARIANT (t) == t && !TYPE_QUALS (t)); for (tree x = t; x; x = TYPE_NEXT_VARIANT (x)) { if (x != t && TYPE_STRUCTURAL_EQUALITY_P (x)) { if (!TYPE_QUALS (x)) TYPE_CANONICAL (x) = TYPE_CANONICAL (t); else { tree c = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x)); if (TYPE_STRUCTURAL_EQUALITY_P (c)) { gcc_checking_assert (TYPE_CANONICAL (t) == t); if (c == x) TYPE_CANONICAL (x) = x; else { /* build_qualified_type unhelpfully moved c from some later spot in TYPE_MAIN_VARIANT (t) chain to right after t. Restart processing the whole chain. */ gcc_checking_assert (TYPE_MAIN_VARIANT (t) == c); x = t; continue; } } else TYPE_CANONICAL (x) = TYPE_CANONICAL (c); } } ... but that could walk perhaps very long chain over and over (sure, for the already handled cases it would see TYPE_STRUCTUAL_EQUALITY_P (x) is no longer the case, but still, I'm afraid it increases compile time complexity for pathological cases too much. Or perhaps undo what get_qualified_type's /* Put the found variant at the head of the variant list so frequently searched variants get found faster. The C++ FE benefits greatly from this. */ tree t = *tp; *tp = TYPE_NEXT_VARIANT (t); TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (mv); TYPE_NEXT_VARIANT (mv) = t; return t; optimization or build_variant_type_copy clearly as well (it chains new types to TYPE_NEXT_VARIANT (mv) as well). So perhaps instead we need to undo the move. Here is what I've bootstrapped/regtested and what broke pr114574-2.c: gcc/c/ * c-decl.cc (c_update_type_canonical): Assert t is main variant with 0 TYPE_QUALS. Simplify and don't use check_qualified_type. gcc/testsuite/ * gcc.dg/pr114930.c: New test. * gcc.dg/pr115502.c: New test. --- gcc/c/c-decl.cc.jj 2024-06-07 12:17:09.582969919 +0200 +++ gcc/c/c-decl.cc 2024-06-19 13:35:46.648956792 +0200 @@ -9367,18 +9367,19 @@ is_flexible_array_member_p (bool is_last static void c_update_type_canonical (tree t) { - for (tree x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x)) + gcc_checking_assert (TYPE_MAIN_VARIANT (t) == t && !TYPE_QUALS (t)); + for (tree x = t; x; x = TYPE_NEXT_VARIANT (x)) { if (x != t && TYPE_STRUCTURAL_EQUALITY_P (x)) { - if (TYPE_QUALS (x) == TYPE_QUALS (t)) + if (!TYPE_QUALS (x)) TYPE_CANONICAL (x) = TYPE_CANONICAL (t); - else if (TYPE_CANONICAL (t) != t - || check_qualified_type (x, t, TYPE_QUALS (x))) - TYPE_CANONICAL (x) - = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x)); else - TYPE_CANONICAL (x) = x; + { + tree + c = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x)); + TYPE_CANONICAL (x) = TYPE_CANONICAL (c); + } } else if (x != t) continue; --- gcc/testsuite/gcc.dg/pr114930.c.jj 2024-06-18 21:27:53.782729543 +0200 +++ gcc/testsuite/gcc.dg/pr114930.c 2024-06-18 21:27:53.782729543 +0200 @@ -0,0 +1,9 @@ +/* PR c/114930 */ +/* { dg-do compile { target lto } } */ +/* { dg-options "-std=c23 -flto" } */ + +typedef struct WebPPicture WebPPicture; +typedef int (*WebPProgressHook)(const WebPPicture *); +WebPProgressHook progress_hook; +struct WebPPicture { +} WebPGetColorPalette(const struct WebPPicture *); --- gcc/testsuite/gcc.dg/pr115502.c.jj 2024-06-18 21:27:53.793729408 +0200 +++ gcc/testsuite/gcc.dg/pr115502.c 2024-06-18 21:27:53.793729408 +0200 @@ -0,0 +1,9 @@ +/* PR c/115502 */ +/* { dg-do compile { target lto } } */ +/* { dg-options "-std=c23 -flto" } */ + +typedef struct _OSet OSet; +typedef OSet AvlTree; +void vgPlain_OSetGen_Lookup(const OSet *); +struct _OSet {}; +void vgPlain_OSetGen_Lookup(const AvlTree *); Jakub