On Fri, 13 Jan 2012, Jakub Jelinek wrote:

> On Fri, Jan 13, 2012 at 11:05:36AM +0100, Richard Guenther wrote:
> > This fixes the ICEs that occur with redeclared extern inline functions
> > in some circumstances.  It avoids the cgraph confusion by _not_ merging
> > the two decls in this case but simply drops the old (extern inline)
> > one on the floor.  This causes the cgraph to be properly presented
> > with two different decls and thus two different cgraph nodes will
> > be created.  I didn't try to change name-lookup to always find
> > the extern inline copy to preserve the ever existing recursive
> > case
> > 
> > extern __inline __attribute__ ((__always_inline__))
> > void open ()
> > {
> > }
> > void open ()
> > {
> >   open ();
> > }
> > 
> > which even in 3.2 where the ICEs appearantly did not exist compiled
> > to a self-recursive open () (trivially explained by how 3.2 worked,
> > function-at-a-time).
> 
> That won't e.g. copy over any attributes from the extern inline to
> the out of line, or asm redirection, or type merging, etc.

But that's of course intended.  Attributes or redirection on the
extern inline variant are completely meaningless.

> If you want to keep olddecl as is, then IMHO we should add a new bool
> argument to merge_decls and if the flag is set, make sure we only ever
> modify newdecl and not olddecl.

I don't think that is necesary nor warranted.  What legitimate use
would break you think?

Btw, the following patch now passed bootstrap and regtest on
x86_64-unknown-linux-gnu.

Ok for trunk?

Thanks,
Richard.

2012-01-13  Richard Guenther  <rguent...@suse.de>

        PR c/33763
        * c-decl.c (duplicate_decls): Do not merge re-declared extern
        inline function decls with their re-declaration.
        (pushdecl): Do not put extern inlines into external_scope.

        * gcc.dg/torture/pr33763-1.c: New testcase.
        * gcc.dg/torture/pr33763-2.c: Likewise.
        * gcc.dg/torture/pr33763-3.c: Likewise.

Index: gcc/c-decl.c
===================================================================
*** gcc/c-decl.c        (revision 183121)
--- gcc/c-decl.c        (working copy)
*************** duplicate_decls (tree newdecl, tree oldd
*** 2513,2518 ****
--- 2513,2536 ----
        return false;
      }
  
+   /* If we have a redeclared extern inline function simply drop olddecl
+      on the floor instead of merging it with newdecl.  */
+   if (TREE_CODE (newdecl) == FUNCTION_DECL
+       && DECL_INITIAL (newdecl)
+       && DECL_INITIAL (olddecl)
+       && !(!(DECL_DECLARED_INLINE_P (olddecl)
+            && DECL_EXTERNAL (olddecl))
+          || (DECL_DECLARED_INLINE_P (newdecl)
+              && DECL_EXTERNAL (newdecl))
+          || (!flag_gnu89_inline
+              && (!DECL_DECLARED_INLINE_P (olddecl)
+                  || !lookup_attribute ("gnu_inline",
+                                        DECL_ATTRIBUTES (olddecl)))
+              && (!DECL_DECLARED_INLINE_P (newdecl)
+                  || !lookup_attribute ("gnu_inline",
+                                        DECL_ATTRIBUTES (newdecl))))))
+     return false;
+ 
    merge_decls (newdecl, olddecl, newtype, oldtype);
    return true;
  }
*************** pushdecl (tree x)
*** 2776,2782 ****
              nested = true;
              x = visdecl;
            }
!         else
            {
              bind (name, x, external_scope, /*invisible=*/true,
                    /*nested=*/false, locus);
--- 2794,2802 ----
              nested = true;
              x = visdecl;
            }
!         else if (TREE_CODE (x) != FUNCTION_DECL
!                  || !(DECL_DECLARED_INLINE_P (x)
!                       && DECL_EXTERNAL (x)))
            {
              bind (name, x, external_scope, /*invisible=*/true,
                    /*nested=*/false, locus);
Index: gcc/testsuite/gcc.dg/torture/pr33763-1.c
===================================================================
*** gcc/testsuite/gcc.dg/torture/pr33763-1.c    (revision 0)
--- gcc/testsuite/gcc.dg/torture/pr33763-1.c    (revision 0)
***************
*** 0 ****
--- 1,43 ----
+ /* { dg-do run } */
+ 
+ extern void abort (void);
+ 
+ extern __inline __attribute__ ((__always_inline__))
+ int foo ()
+ {
+   return 1;
+ }
+ int test1 ()
+ {
+   /* Name-lookup should find foo that returns 1.  */
+   return foo ();
+ }
+ int foo ()
+ {
+   return 0;
+ }
+ 
+ extern __inline __attribute__ ((__always_inline__))
+ int bar ()
+ {
+   return 1;
+ }
+ int bar ()
+ {
+   return 0;
+ }
+ int test2 ()
+ {
+   /* Name-lookup should find bar that returns 0.  */
+   return bar ();
+ }
+ 
+ int
+ main()
+ {
+   if (test1 () != 1)
+     abort ();
+   if (test2 () != 0)
+     abort ();
+   return 0;
+ }
Index: gcc/testsuite/gcc.dg/torture/pr33763-2.c
===================================================================
*** gcc/testsuite/gcc.dg/torture/pr33763-2.c    (revision 0)
--- gcc/testsuite/gcc.dg/torture/pr33763-2.c    (revision 0)
***************
*** 0 ****
--- 1,30 ----
+ /* { dg-do compile } */
+ 
+ extern int foo (const char *, int);
+ extern int baz (const char *, int);
+ 
+ extern inline __attribute__ ((always_inline, gnu_inline)) int
+ baz (const char *x, int y)
+ {
+   return 2;
+ }
+ 
+ int
+ baz (const char *x, int y)
+ {
+   return 1;
+ }
+ 
+ int xa, xb;
+ 
+ static int
+ inl (const char *x, int y)
+ {
+   return baz (x, y);
+ }
+ 
+ int
+ foo (const char *x, int y)
+ {
+   return inl (x, y);
+ }
Index: gcc/testsuite/gcc.dg/torture/pr33763-3.c
===================================================================
*** gcc/testsuite/gcc.dg/torture/pr33763-3.c    (revision 0)
--- gcc/testsuite/gcc.dg/torture/pr33763-3.c    (revision 0)
***************
*** 0 ****
--- 1,58 ----
+ /* { dg-do compile } */
+ 
+ typedef struct
+ {
+   void *a;
+   void *b;
+ } T;
+ extern void *foo (const char *, const char *);
+ extern void *bar (void *, const char *, T);
+ extern int baz (const char *, int);
+ 
+ extern inline __attribute__ ((always_inline, gnu_inline)) int
+ baz (const char *x, int y)
+ {
+   return 2;
+ }
+ 
+ int
+ baz (const char *x, int y)
+ {
+   return 1;
+ }
+ 
+ int xa, xb;
+ 
+ static void *
+ inl (const char *x, const char *y)
+ {
+   T t = { &xa, &xb };
+   int *f = (int *) __builtin_malloc (sizeof (int));
+   const char *z;
+   int o = 0;
+   void *r = 0;
+ 
+   for (z = y; *z; z++)
+     {
+       if (*z == 'r')
+       o |= 1;
+       if (*z == 'w')
+       o |= 2;
+     }
+   if (o == 1)
+     *f = baz (x, 0);
+   if (o == 2)
+     *f = baz (x, 1);
+   if (o == 3)
+     *f = baz (x, 2);
+ 
+   if (o && *f > 0)
+     r = bar (f, "w", t);
+   return r;
+ }
+ 
+ void *
+ foo (const char *x, const char *y)
+ {
+   return inl (x, y);
+ }

Reply via email to