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? 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. * 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); + }