[PATCH] Fix PR33763

Richard Guenther rguenther@suse.de
Fri Jan 13 11:35:00 GMT 2012


On Fri, 13 Jan 2012, 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).
> 
> Bootstrapped on x86_64-unknown-linux-gnu, testing in progress.
> 
> Ok for trunk?

This FAILs

FAIL: gcc.dg/inline3.c  (test for warnings, line 6)
FAIL: gcc.dg/inline3.c  (test for errors, line 7)

because we end up calling duplicate_decls with olddecl being
the extern inline copy twice - somehow the bindings are messed
up (read: don't work naturally?).

In particular we put the extern inline fn in both the
external and current scope (and thus the file-scope decl
initially shadows the extern "copy").  We're then doing

      /* If this is an external linkage declaration, we should check
         for compatibility with the type in the external scope before
         setting the type at this scope based on the visible
         information only.  */
      if (TREE_PUBLIC (x) && TREE_PUBLIC (visdecl))
        {
          while (b_ext && !B_IN_EXTERNAL_SCOPE (b_ext))
            b_ext = b_ext->shadowed;
          if (b_ext)
            {
              b_use = b_ext;
              if (b_use->u.type)
                TREE_TYPE (b_use->decl) = b_use->u.type;
            }
        }

finding that extern inline 'extern scope' copy and disambiguate
against that.

I suppose extern inline fns should not be put in external_scope?
Thus,

@@ -2776,7 +2794,8 @@ pushdecl (tree x)
              nested = true;
              x = visdecl;
            }
-         else
+         else if (!(DECL_DECLARED_INLINE_P (x)
+                    && DECL_EXTERNAL (x)))
            {
              bind (name, x, external_scope, /*invisible=*/true,
                    /*nested=*/false, locus);

in addition to the patch below?

Thanks,
Richard.

> Thanks,
> Richard.
> 
> 2012-01-13  Richard Guenther  <rguenther@suse.de>
> 
> 	PR c/33763
> 	* c-decl.c (duplicate_decls): Do not merge re-declared extern
> 	inline function decls with their re-declaration.
> 
> 	* 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;
>   }
> 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,41 ----
> + /* { dg-do run } */
> + 
> + 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);
> + }
> 

-- 
Richard Guenther <rguenther@suse.de>
SUSE / SUSE Labs
SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer


More information about the Gcc-patches mailing list