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

Reply via email to