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

Reply via email to