[backport from gcc-4.7/trunk r180700, fixes PR56712 ] From: Paul Brook Subject: [PATCH] Miscompilation of __attribute__((constructor)) functions. Date: Thu, 27 Oct 2011 18:24:11 +0100 List-Archive: 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 gcc/ 2011-10-31 Paul Brook * cgraphunit.c: Don't mark clones as static constructors. gcc/testsuite/ 2011-10-31 Paul Brook * gcc.dg/constructor-1.c: New test. --- gcc-4.6.3/gcc/cgraphunit.c.~1~ 2011-03-11 14:27:26.000000000 +0100 +++ gcc-4.6.3/gcc/cgraphunit.c 2013-03-24 21:28:51.556236041 +0100 @@ -2067,6 +2067,10 @@ cgraph_function_versioning (struct cgrap SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl)); SET_DECL_RTL (new_decl, NULL); + /* When the old decl was a con-/destructor make sure the clone isn't. */ + DECL_STATIC_CONSTRUCTOR(new_decl) = 0; + DECL_STATIC_DESTRUCTOR(new_decl) = 0; + /* Create the new version's call-graph node. and update the edges of the new node. */ new_version_node = --- gcc-4.6.3/gcc/testsuite/gcc.dg/constructor-1.c.~1~ 1970-01-01 01:00:00.000000000 +0100 +++ gcc-4.6.3/gcc/testsuite/gcc.dg/constructor-1.c 2013-03-24 21:28:51.556236041 +0100 @@ -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 + +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); +}