Patch below fixes a miscompilation observed whem building uclibc libpthread on a mips-linux system.
The story start with the ipa-split optimization, which turns: void fn() { if (cond) { DO_STUFF; } } into: static void fn_helper() { DO_STUFF; } void fn() { if (cond) fn_helper(); } The idea is that the new fn() wrapper is a good candidate for inlining, whereas the original fn is not. The optimization uses cgraph function versioning. The problem is that when we clone the cgraph node we propagate the DECL_STATIC_CONSTRUCTOR bit. Thus both fn() and fn_helper() get called on startup. When fn happens to be pthread_initialize we end up calling both the original and a clone with the have-I-already-done-this check removed. Much badness ensues. Patch below fixes this by clearing the DECL_STATIC_{CON,DES}TRUCTOR bit when cloning a cgraph node - there's already logic to make sure we keep the original. My guess is this bug is probably latent in other IPA passes. Tested on mips-linux and bootstrap+test x86_64-linux Ok? Paul 2011-10-27 Paul Brook <p...@codesourcery.com> gcc/ * cgraphunit.c: Don't mark clones as static constructors. gcc/testsuite/ * gcc.dg/constructor-1.c: New test. Index: gcc/cgraphunit.c =================================================================== --- gcc/cgraphunit.c (revision 180439) +++ gcc/cgraphunit.c (working copy) @@ -2386,6 +2386,8 @@ cgraph_function_versioning (struct cgrap new_version_node->local.externally_visible = 0; new_version_node->local.local = 1; new_version_node->lowered = true; + DECL_STATIC_CONSTRUCTOR(new_version_node->decl) = 0; + DECL_STATIC_DESTRUCTOR(new_version_node->decl) = 0; /* Update the call_expr on the edges to call the new version node. */ update_call_expr (new_version_node); Index: gcc/testsuite/gcc.dg/constructor-1.c =================================================================== --- gcc/testsuite/gcc.dg/constructor-1.c (revision 0) +++ gcc/testsuite/gcc.dg/constructor-1.c (revision 0) @@ -0,0 +1,37 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +/* The ipa-split pass pulls the body of the if(!x) block + into a separate function to make foo a better inlining + candidate. Make sure this new function isn't also run + as a static constructor. */ + +#include <stdlib.h> + +int x, y; + +void __attribute__((noinline)) +bar(void) +{ + y++; +} + +void __attribute__((constructor)) +foo(void) +{ + if (!x) + { + bar(); + y++; + } +} + +int main() +{ + x = 1; + foo(); + foo(); + if (y != 2) + abort(); + exit(0); +}