This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[gccgo] -fsplit-stack adjustments
- From: Ian Lance Taylor <iant at google dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Wed, 23 Dec 2009 15:21:45 -0800
- Subject: [gccgo] -fsplit-stack adjustments
This patch is some more fixes for -fsplit-stack.
Testing with larger programs showed that calling munmap as we returned
up the stack was a performance killer. In this patch I change the
code to keep stack segments until a thread exits. I separated the
thread specific code into a separate file for clarity; that code will
only be used if the program calls pthread_create. Currently stack
segments remain attached to a thread; it may be necessary to use a
shared list controlled by a mutex at some point. We'll see.
Originally I used a separate list header allocated on the same page as
the first stack segment. I can not remember why I did that and it
just made the code more confusing. I changed it.
I fixed a bug when returning back to run on the original stack
segment--the updated stack guard was computed incorrectly. It's hard
to know what the right value is, unfortunately--the top stack segment
will tend to not be used effectively at present.
I committed this patch to gccgo branch. I will gather the
-fsplit-stack patches onto split branch at a later date.
Ian
Index: libgcc/generic-morestack.c
===================================================================
--- libgcc/generic-morestack.c (revision 154517)
+++ libgcc/generic-morestack.c (working copy)
@@ -38,12 +38,13 @@ see the files COPYING3 and COPYING.RUNTI
#include <assert.h>
#include <errno.h>
-#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/uio.h>
+#include "generic-morestack.h"
+
/* This file contains subroutines that are used by code compiled with
-fsplit-stack. */
@@ -73,10 +74,7 @@ struct stack_segment
is running on this stack segment, the next one is not being
used. */
struct stack_segment *next;
- /* The total size of this stack segment. This is zero for the
- special case of a header created to represent the initial stack
- segment--the one created when the program or thread was
- started. */
+ /* The total size of this stack segment. */
size_t size;
/* The stack address when this stack was created. This is used when
popping the stack. */
@@ -85,7 +83,7 @@ struct stack_segment
/* The first stack segment allocated for this thread. */
-static __thread struct stack_segment *stack_segments;
+__thread struct stack_segment *__morestack_segments;
/* The stack segment that we think we are currently using. This will
be correct in normal usage, but will be incorrect if an exception
@@ -135,6 +133,36 @@ print_int (int val, char *buf, int bufle
return buf + i;
}
+/* Print the string MSG/LEN, the errno number ERR, and a newline on
+ stderr. Then crash. */
+
+void
+__morestack_fail (const char *, size_t, int) __attribute__ ((noreturn));
+
+void
+__morestack_fail (const char *msg, size_t len, int err)
+{
+ char buf[24];
+ static const char nl[] = "\n";
+ struct iovec iov[3];
+ union { char *p; const char *cp; } const_cast;
+
+ const_cast.cp = msg;
+ iov[0].iov_base = const_cast.p;
+ iov[0].iov_len = len;
+ /* We can't call strerror, because it may try to translate the error
+ message, and that would use too much stack space. */
+ iov[1].iov_base = print_int (err, buf, sizeof buf, &iov[1].iov_len);
+ const_cast.cp = &nl[0];
+ iov[2].iov_base = const_cast.p;
+ iov[2].iov_len = sizeof nl - 1;
+ /* FIXME: On systems without writev we need to issue three write
+ calls, or punt on printing errno. For now this is irrelevant
+ since stack splitting only works on GNU/Linux anyhow. */
+ writev (2, iov, 3);
+ abort ();
+}
+
/* Allocate a new stack segment. FRAME_SIZE is the required frame
size. */
@@ -143,7 +171,6 @@ allocate_segment (size_t frame_size)
{
static unsigned int static_pagesize;
unsigned int pagesize;
- int first;
unsigned int overhead;
unsigned int allocate;
void *space;
@@ -169,11 +196,7 @@ allocate_segment (size_t frame_size)
assert (p == 0 || p == pagesize);
}
- first = current_segment == NULL;
-
overhead = sizeof (struct stack_segment);
- if (first)
- overhead *= 2;
allocate = pagesize;
if (allocate < MINSIGSTKSZ)
@@ -192,54 +215,33 @@ allocate_segment (size_t frame_size)
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (space == MAP_FAILED)
{
- static const char err1[] =
+ static const char msg[] =
"unable to allocate additional stack space: errno ";
- char buf[24];
- static const char err2[] = "\n";
- struct iovec iov[3];
- union { char *p; const char *cp; } const_cast;
-
- const_cast.cp = &err1[0];
- iov[0].iov_base = const_cast.p;
- iov[0].iov_len = sizeof err1 - 1;
- /* We can't call strerror, because it may try to translate the
- error message, and that would use too much stack space. */
- iov[1].iov_base = print_int (errno, buf, sizeof buf, &iov[1].iov_len);
- const_cast.cp = &err2[0];
- iov[2].iov_base = const_cast.p;
- iov[2].iov_len = sizeof err2 - 1;
- /* FIXME: On systems without writev we need to issue three write
- calls, or punt on printing errno. For now this is irrelevant
- since stack splitting only works on GNU/Linux anyhow. */
- writev (2, iov, 3);
- abort ();
+ __morestack_fail (msg, sizeof msg - 1, errno);
}
pss = (struct stack_segment *) space;
- if (first)
- {
- pss->prev = NULL;
- pss->next = pss + 1;
- pss->size = 0;
- stack_segments = pss;
- current_segment = pss;
- pss = pss + 1;
- }
-
pss->prev = current_segment;
pss->next = NULL;
pss->size = allocate - overhead;
- current_segment->next = pss;
+
+ if (current_segment != NULL)
+ current_segment->next = pss;
+ else
+ __morestack_segments = pss;
return pss;
}
/* Release stack segments. */
-static void
-release_segments (struct stack_segment *pss)
+void
+__morestack_release_segments (struct stack_segment **pp)
{
+ struct stack_segment *pss;
+
+ pss = *pp;
while (pss != NULL)
{
struct stack_segment *next;
@@ -247,23 +249,16 @@ release_segments (struct stack_segment *
next = pss->next;
- if (pss->size != 0)
- allocate = pss->size + sizeof (struct stack_segment);
- else
- {
- assert (next == pss + 1);
- allocate = next->size = 2 * sizeof (struct stack_segment);
- next = next->next;
- }
-
+ allocate = pss->size + sizeof (struct stack_segment);
if (munmap (pss, allocate) < 0)
{
- static const char msg[] = "munmap of stack space failed\n";
- write (2, msg, sizeof msg - 1);
+ static const char msg[] = "munmap of stack space failed: errno ";
+ __morestack_fail (msg, sizeof msg - 1, errno);
}
pss = next;
}
+ *pp = NULL;
}
/* This function is called by a processor specific function which is
@@ -292,20 +287,16 @@ __generic_morestack (size_t *pframe_size
{
size_t frame_size = *pframe_size;
struct stack_segment *current;
+ struct stack_segment **pp;
current = current_segment;
- if (current != NULL
- && current->next != NULL
- && current->next->size < frame_size)
- {
- release_segments (current->next);
- current->next = NULL;
- }
+ pp = current != NULL ? ¤t->next : &__morestack_segments;
+ if (*pp != NULL && (*pp)->size < frame_size)
+ __morestack_release_segments (pp);
+ current = *pp;
- if (current != NULL && current->next != NULL)
- current = current->next;
- else
+ if (current == NULL)
current = allocate_segment (frame_size);
current->old_stack = old_stack;
@@ -327,10 +318,10 @@ __generic_morestack (size_t *pframe_size
}
/* This function is called by a processor specific function when it is
- ready to release a stack segment. This function is actually
- running on the stack segment which it should release. So it may
- not literally free the segment, but must instead mark it for
- release at a later date. It returns a pointer to the new stack
+ ready to release a stack segment. We don't actually release the
+ stack segment, we just move back to the previous one. The current
+ stack segment will still be available if we need it in
+ __generic_morestack. This returns a pointer to the new stack
segment to use, which is the one saved by a previous call to
__generic_morestack. The processor specific function is then
responsible for actually updating the stack pointer. This sets
@@ -343,82 +334,26 @@ __generic_releasestack (size_t *pavailab
void *old_stack;
current = current_segment;
-
- if (current->next != NULL)
- {
- release_segments (current->next);
- current->next = NULL;
- }
-
old_stack = current->old_stack;
current = current->prev;
current_segment = current;
+ if (current != NULL)
+ {
#ifdef STACK_GROWS_DOWNWARD
- *pavailable = (char *) old_stack - (char *) (current + 1);
+ *pavailable = (char *) old_stack - (char *) (current + 1);
#else
- *pavailable = (char *) (current + 1) + current->size - (char *) old_stack;
+ *pavailable = (char *) (current + 1) + current->size - (char *) old_stack;
#endif
+ }
+ else
+ {
+ /* We have popped back to the original stack. We don't know how
+ large it is. */
+ *pavailable = 512;
+ }
return old_stack;
}
-/* Pass information from the pthread_create wrapper to
- stack_split_initialize_thread. */
-
-struct pthread_create_args
-{
- void *(*start_routine) (void *);
- void *arg;
-};
-
-/* Initialize a thread. This is called via pthread_create. It calls
- a target dependent function to set up any required stack guard. */
-
-static void* stack_split_initialize_thread (void *)
- __attribute__ ((no_split_stack));
-
-extern void __stack_split_initialize (void)
- __attribute__ ((visibility ("hidden")));
-
-static void *
-stack_split_initialize_thread (void *varg)
-{
- struct pthread_create_args *args = (struct pthread_create_args *) varg;
- void *(*start_routine) (void *);
- void *arg;
-
- __stack_split_initialize ();
- start_routine = args->start_routine;
- arg = args->arg;
- free (args);
- return (*start_routine) (arg);
-}
-
-/* This function wraps calls to pthread_create to make sure that the
- stack guard is initialized for new threads. FIXME: This hack will
- not be necessary if glibc supports -fsplit-stack directly. */
-
-int __wrap_pthread_create (pthread_t *, const pthread_attr_t *,
- void *(*start_routine) (void *), void *)
- __attribute__ ((visibility ("hidden")));
-
-extern int __real_pthread_create (pthread_t *, const pthread_attr_t *,
- void *(*start_routine) (void *), void *)
- __attribute__ ((weak));
-
-int
-__wrap_pthread_create (pthread_t *tid, const pthread_attr_t *attr,
- void *(*start_routine) (void *), void *arg)
-{
- struct pthread_create_args* args;
-
- args = malloc (sizeof (struct pthread_create_args));
- if (args == NULL)
- return EAGAIN;
- args->start_routine = start_routine;
- args->arg = arg;
- return __real_pthread_create (tid, attr, stack_split_initialize_thread, args);
-}
-
#endif /* !defined (inhibit_libc) */
Index: libgcc/generic-morestack-thread.c
===================================================================
--- libgcc/generic-morestack-thread.c (revision 0)
+++ libgcc/generic-morestack-thread.c (revision 0)
@@ -0,0 +1,162 @@
+/* Thread library support for -fsplit-stack. */
+/* Copyright (C) 2009 Free Software Foundation, Inc.
+ Contributed by Ian Lance Taylor <iant@google.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+
+/* If inhibit_libc is defined, we can not compile this file. The
+ effect is that people will not be able to use -fsplit-stack. That
+ is much better than failing the build particularly since people
+ will want to define inhibit_libc while building a compiler which
+ can build glibc. */
+
+#ifndef inhibit_libc
+
+#include <pthread.h>
+
+#include "generic-morestack.h"
+
+/* We declare the pthread functions we need as weak, so that
+ libgcc_s.so does not need to be linked against -lpthread. */
+
+extern int pthread_once (pthread_once_t *, void (*) (void))
+ __attribute__ ((weak));
+
+extern int pthread_key_create (pthread_key_t *, void (*) (void *))
+ __attribute__ ((weak));
+
+extern int pthread_setspecific (pthread_key_t, const void *)
+ __attribute__ ((weak));
+
+/* The key for the list of stack segments to free when the thread
+ exits. This is created by pthread_key_create. */
+
+static pthread_key_t segment_list_key;
+
+/* Used to only run create_key once. */
+
+static pthread_once_t create_key_once = PTHREAD_ONCE_INIT;
+
+/* Release all the segments for a thread. This is the destructor
+ function used by pthread_key_create, and is called when a thread
+ exits. */
+
+static void
+free_segments (void* arg)
+{
+ __morestack_release_segments ((struct stack_segment **) arg);
+}
+
+/* Set up the key for the list of segments. This is called via
+ pthread_once. */
+
+static void
+create_key (void)
+{
+ int err;
+
+ err = pthread_key_create (&segment_list_key, free_segments);
+ if (err != 0)
+ {
+ static const char msg[] = "pthread_key_create failed: errno ";
+ __morestack_fail (msg, sizeof msg - 1, err);
+ }
+}
+
+/* Pass information from the pthread_create wrapper to
+ stack_split_initialize_thread. */
+
+struct pthread_create_args
+{
+ void *(*start_routine) (void *);
+ void *arg;
+};
+
+/* Initialize a thread. This is called via pthread_create. It calls
+ a target dependent function to set up any required stack guard. */
+
+static void* stack_split_initialize_thread (void *)
+ __attribute__ ((no_split_stack));
+
+static void *
+stack_split_initialize_thread (void *varg)
+{
+ struct pthread_create_args *args = (struct pthread_create_args *) varg;
+ int err;
+ void *(*start_routine) (void *);
+ void *arg;
+
+ __stack_split_initialize ();
+
+ err = pthread_setspecific (segment_list_key, (void *) &__morestack_segments);
+ if (err != 0)
+ {
+ static const char msg[] = "pthread_setspecific failed: errno ";
+ __morestack_fail (msg, sizeof msg - 1, err);
+ }
+
+ start_routine = args->start_routine;
+ arg = args->arg;
+ free (args);
+ return (*start_routine) (arg);
+}
+
+/* This function wraps calls to pthread_create to make sure that the
+ stack guard is initialized for new threads. FIXME: This hack will
+ not be necessary if glibc supports -fsplit-stack directly. */
+
+int __wrap_pthread_create (pthread_t *, const pthread_attr_t *,
+ void *(*start_routine) (void *), void *)
+ __attribute__ ((visibility ("hidden")));
+
+extern int __real_pthread_create (pthread_t *, const pthread_attr_t *,
+ void *(*start_routine) (void *), void *)
+ __attribute__ ((weak));
+
+int
+__wrap_pthread_create (pthread_t *tid, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *arg)
+{
+ int err;
+ struct pthread_create_args* args;
+
+ err = pthread_once (&create_key_once, create_key);
+ if (err != 0)
+ {
+ static const char msg[] = "pthread_once failed: errno ";
+ __morestack_fail (msg, sizeof msg - 1, err);
+ }
+
+ args = malloc (sizeof (struct pthread_create_args));
+ if (args == NULL)
+ return EAGAIN;
+ args->start_routine = start_routine;
+ args->arg = arg;
+ return __real_pthread_create (tid, attr, stack_split_initialize_thread, args);
+}
+
+#endif /* !defined (inhibit_libc) */
Index: libgcc/generic-morestack.h
===================================================================
--- libgcc/generic-morestack.h (revision 0)
+++ libgcc/generic-morestack.h (revision 0)
@@ -0,0 +1,52 @@
+/* Library support for -fsplit-stack. */
+/* Copyright (C) 2009 Free Software Foundation, Inc.
+ Contributed by Ian Lance Taylor <iant@google.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+/* This is a libgcc internal header file for functions shared between
+ generic-morestack.c and generic-morestack-thread.c. The latter
+ file is only used when linking with the pthread library. */
+
+/* The stack segment structure, defined in generic-morestack.c. */
+
+struct stack_segment;
+
+/* The list of stack segments for this thread. */
+
+extern __thread struct stack_segment *__morestack_segments;
+
+/* Print the string MSG/LEN, the errno number ERR, and a newline on
+ stderr, without using printf. Then crash. */
+
+extern void __morestack_fail (const char *msg, size_t len, int err)
+ __attribute__ ((noreturn, visibility ("hidden")));
+
+/* Release stack segments. */
+
+extern void __morestack_release_segments (struct stack_segment **)
+ __attribute__ ((visibility ("hidden")));
+
+/* Store the stack information in a processor dependent manner. */
+
+extern void __stack_split_initialize (void)
+ __attribute__ ((visibility ("hidden")));
Index: libgcc/config/t-stack
===================================================================
--- libgcc/config/t-stack (revision 154387)
+++ libgcc/config/t-stack (working copy)
@@ -1,4 +1,4 @@
# Makefile fragment to provide generic support for -fsplit-stack.
# This should be used in config.host for any host which supports
# -fsplit-stack.
-LIB2ADD += $(srcdir)/generic-morestack.c
+LIB2ADD += $(srcdir)/generic-morestack.c $(srcdir)/generic-morestack-thread.c