public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [incremental] Patch: FYI: initial threading in gcc
@ 2007-10-11 20:34 Tom Tromey
  2007-10-12  8:57 ` Paolo Bonzini
  0 siblings, 1 reply; 3+ messages in thread
From: Tom Tromey @ 2007-10-11 20:34 UTC (permalink / raw)
  To: Gcc Patch List

I'm checking this in on the incremental-compiler branch.

This adds support for multiple threads to the GC.  gcc_collect acts as
a safe point.  It also adds support for thread-local roots.  gengtype
now recognizes the "GCC_THREAD" keyword and emits functions which
register thread-local variables as roots at thread startup.

There are some limitations in the current patch.  First, it assumes
pthreads and the existence of __thread.  Fixing this is just some
boring configury, or perhaps figuring out how to re-use gthr* on the
host.  (If __thread is not available, I plan to have the server fall
back to single-threaded mode.)  Second, I haven't implemented PCH
support for thread-locals.


I have another patch here to add GCC_THREAD to lots and lots of
globals.  I'm not completely sure I want to have two hundred
thread-local variables in the compiler, but on the other hand, it is
much, much simpler to do this than to refactor gcc into a more OO
style.

One idea I'm kicking around is doing a "light" refactoring -- putting
globals from certain modules into a struct and then having a single
thread-local that points at an instance.  E.g., dwarf2out could be
done this way, as could all the globals in the C front end.  I think
this would look nicer and be a reasonable stepping stone toward a
better design.

Let me know what you think.

Tom

ChangeLog:
2007-10-11  Tom Tromey  <tromey@redhat.com>

	* Makefile.in (LIBS): Add -lpthread.
	* system.h (GCC_THREAD): New define.
	* ggc-page.c (ggc_alloc_stat): Lock.
	(ggc_get_size): Lock.
	(ggc_free): Lock.
	(ggc_collect): Call ggc_thread_pause, ggc_collection_completed.
	* ggc-zone.c (ggc_alloc_zone_stat): Lock.
	(ggc_free): Lock.
	(ggc_get_size): Lock.
	(new_ggc_zone): Lock.
	(destroy_ggc_zone): Lock.
	(ggc_collect): Call ggc_thread_pause, ggc_collection_completed.
	* ggc-common.c (global_root_list): New global.
	(ggc_mark_roots): Mark thread-local roots.
	(roots, ggc_n_threads, ggc_gc_lock, gc_condition,
	ggc_n_waiting_threads, collection_generation): New globals.
	(ggc_thread_init): New function.
	(ggc_thread_start): Likewise.
	(ggc_thread_clean_up): Likewise.
	(ggc_thread_pause): Likewise.
	(ggc_collection_completed): Likewise.
	(ggc_thread_ignore_me): Likewise.
	* ggc.h: Include host-thread.h.
	(struct ggc_thread_roots): New.
	(struct ggc_thread_root_list): New.
	(ggc_thread_registration_function): New typedef.
	(gt_ggc_thread_funcs): Declare.
	(ggc_gc_lock): Likewise.
	(ggc_thread_init, ggc_thread_start, ggc_thread_clean_up,
	ggc_thread_ignore_me, ggc_thread_pause, ggc_collection_completed):
	Declare.
	* gengtype-parse.c (extern_or_static): Handle GCC_THREAD.
	* gengtype-lex.l: Recognize "GGC_THREAD".
	* toplev.c (general_init): Call ggc_thread_init.
	* host-thread.h: New file.
	* gengtype.h (THREAD): New value.
	(note_variable): Update.
	* gengtype.c (struct pair) <thread_local>: New field.
	(do_typedef): Initialize it.
	(note_variable): Added 'thread_local' argument.
	(create_field_all): Initialize new field.
	(struct flist) <item_count, saved_offset>: New fields.
	(finish_thread_registration): New function.
	(write_thread_lang_registration): Likewise.
	(start_root_field): Likewise.
	(end_root_field): Likewise.
	(write_root): Handle thread-local variables.
	(start_thread_local_table): New function.
	(write_roots): Added 'thread_local' argument.  Write thread
	registration functions.
	(main): Call write_roots twice.

Index: gengtype.c
===================================================================
--- gengtype.c	(revision 127650)
+++ gengtype.c	(working copy)
@@ -62,6 +62,7 @@
   pair_p next;
   const char *name;
   type_p type;
+  bool thread_local;
   struct fileloc line;
   options_p opt;
 };
@@ -548,6 +549,7 @@
   p->next = typedefs;
   p->name = s;
   p->type = t;
+  p->thread_local = false;
   p->line = *pos;
   typedefs = p;
 }
@@ -792,12 +794,14 @@
    to `variables'.  */
 
 void
-note_variable (const char *s, type_p t, options_p o, struct fileloc *pos)
+note_variable (const char *s, bool thread_local, type_p t, options_p o,
+	       struct fileloc *pos)
 {
   pair_p n;
   n = XNEW (struct pair);
   n->name = s;
   n->type = t;
+  n->thread_local = thread_local;
   n->line = *pos;
   n->opt = o;
   n->next = variables;
@@ -814,6 +818,7 @@
   field = XNEW (struct pair);
   field->next = next;
   field->type = type;
+  field->thread_local = false;
   field->name = name;
   field->opt = opt;
   field->line.file = file;
@@ -1719,6 +1724,8 @@
 struct flist {
   struct flist *next;
   int started_p;
+  int item_count;
+  size_t saved_offset;
   const char *name;
   outf_p f;
 };
@@ -1775,7 +1782,7 @@
 			struct fileloc *, const char *);
 static void write_array (outf_p f, pair_p v,
 			 const struct write_types_data *wtd);
-static void write_roots (pair_p);
+static void write_roots (pair_p, bool);
 
 /* Parameters for walk_type.  */
 
@@ -2956,6 +2963,127 @@
   }
 }
 
+/* Close one stanza of the thread registration function.  */
+
+static void
+finish_thread_registration (struct flist *flp, const char *name)
+{
+  struct flist *fli2;
+
+  for (fli2 = flp; fli2; fli2 = fli2->next)
+    {
+      if (fli2->started_p)
+	{
+	  char *num;
+
+	  oprintf (fli2->f, "    table[ndx] = final;\n");
+	  oprintf (fli2->f, "    roots->%s = table;\n", name);
+	  oprintf (fli2->f, "  }\n");
+
+	  /* Overwrite the saved space with the real length of the
+	     array.  */
+	  gcc_assert (fli2->item_count);
+	  gcc_assert (fli2->saved_offset);
+	  num = xasprintf ("%d", fli2->item_count + 1);
+	  gcc_assert (strlen (num) <= 10); /* We insert 10 spaces.  */
+	  memcpy (&fli2->f->buf[fli2->saved_offset], num, strlen (num));
+	  free (num);
+
+	  fli2->started_p = 0;
+	  fli2->item_count = 0;
+	  fli2->saved_offset = 0;
+	}
+    }
+}
+
+/* Write the end of the thread registration functions and generate the
+   per-language gt_ggc_thread_funcs arrays.  */
+
+static void
+write_thread_lang_registration (struct flist *flp)
+{
+  struct flist *fli2;
+
+  /* End the function definitions.  */
+  for (fli2 = flp; fli2; fli2 = fli2->next)
+    {
+      oprintf (fli2->f, "  return roots;\n");
+      oprintf (fli2->f, "}\n");
+    }
+
+  for (fli2 = flp; fli2; fli2 = fli2->next)
+    {
+      lang_bitmap bitmap = get_lang_bitmap (fli2->name);
+      int fnum;
+
+      for (fnum = 0; bitmap != 0; fnum++, bitmap >>= 1)
+	if (bitmap & 1)
+	  {
+	    oprintf (base_files[fnum],
+		     "extern struct ggc_thread_roots *ggc_thr_reg_");
+	    put_mangled_filename (base_files[fnum], fli2->name);
+	    oprintf (base_files[fnum], " (void);\n");
+	  }
+    }
+
+  {
+    size_t fnum;
+    for (fnum = 0; fnum < num_lang_dirs; fnum++)
+      oprintf (base_files [fnum],
+	       "const ggc_thread_registration_function gt_ggc_thread_funcs[] = {\n");
+  }
+
+
+  for (fli2 = flp; fli2; fli2 = fli2->next)
+    {
+      lang_bitmap bitmap = get_lang_bitmap (fli2->name);
+      int fnum;
+
+      for (fnum = 0; bitmap != 0; fnum++, bitmap >>= 1)
+	if (bitmap & 1)
+	  {
+	    oprintf (base_files[fnum], "  ggc_thr_reg_");
+	    put_mangled_filename (base_files[fnum], fli2->name);
+	    oprintf (base_files[fnum], ",\n");
+	  }
+    }
+
+  {
+    size_t fnum;
+    for (fnum = 0; fnum < num_lang_dirs; fnum++)
+      {
+	oprintf (base_files[fnum], "  NULL\n");
+	oprintf (base_files[fnum], "};\n");
+      }
+  }
+}
+
+/* Helper for write_root.  Begin initialization of a field.  */
+static void
+start_root_field (outf_p f, pair_p v, const char *fname)
+{
+  if (v->thread_local)
+    oprintf (f, "    table[ndx].%s = ", fname);
+  else
+    oprintf (f, "    ");
+}
+
+/* Helper for write_root.  End initialization of a field.  */
+static void
+end_root_field (outf_p f, pair_p v, bool final)
+{
+  if (v->thread_local)
+    {
+      oprintf (f, ";\n");
+      if (final)
+	oprintf (f, "    ++ndx;\n");
+    }
+  else if (final)
+    oprintf (f, "\n");
+  else
+    oprintf (f, ",\n");
+}
+
 /* Write out to F the table entry and any marker routines needed to
    mark NAME as TYPE.  The original variable is V, at LINE.
    HAS_LENGTH is nonzero iff V was a variable-length array.  IF_MARKED
@@ -3049,40 +3177,61 @@
       {
 	type_p ap, tp;
 
-	oprintf (f, "  {\n");
-	oprintf (f, "    &%s,\n", name);
-	oprintf (f, "    1");
+	if (!v->thread_local)
+	  oprintf (f, "  {\n");
 
+	start_root_field (f, v, "base");
+	oprintf (f, "&%s", name);
+	end_root_field (f, v, false);
+	
+	start_root_field (f, v, "nelt");
+	oprintf (f, "1");
+
 	for (ap = v->type; ap->kind == TYPE_ARRAY; ap = ap->u.a.p)
 	  if (ap->u.a.len[0])
 	    oprintf (f, " * (%s)", ap->u.a.len);
 	  else if (ap == v->type)
 	    oprintf (f, " * ARRAY_SIZE (%s)", v->name);
-	oprintf (f, ",\n");
-	oprintf (f, "    sizeof (%s", v->name);
+	end_root_field (f, v, false);
+
+	start_root_field (f, v, "stride");
+	oprintf (f, "sizeof (%s", v->name);
 	for (ap = v->type; ap->kind == TYPE_ARRAY; ap = ap->u.a.p)
 	  oprintf (f, "[0]");
-	oprintf (f, "),\n");
+	oprintf (f, ")");
+	end_root_field (f, v, false);
 
 	tp = type->u.p;
 
 	if (! has_length && UNION_OR_STRUCT_P (tp))
 	  {
-	    oprintf (f, "    &gt_ggc_mx_%s,\n", tp->u.s.tag);
-	    oprintf (f, "    &gt_pch_nx_%s", tp->u.s.tag);
+	    start_root_field (f, v, "cb");
+	    oprintf (f, "&gt_ggc_mx_%s", tp->u.s.tag);
+	    end_root_field (f, v, false);
+	    start_root_field (f, v, "pchw");
+	    oprintf (f, "&gt_pch_nx_%s", tp->u.s.tag);
+	    end_root_field (f, v, !if_marked);
 	  }
 	else if (! has_length && tp->kind == TYPE_PARAM_STRUCT)
 	  {
-	    oprintf (f, "    &gt_ggc_m_");
+	    start_root_field (f, v, "cb");
+	    oprintf (f, "&gt_ggc_m_");
 	    output_mangled_typename (f, tp);
-	    oprintf (f, ",\n    &gt_pch_n_");
+	    end_root_field (f, v, false);
+	    start_root_field (f, v, "pchw");
+	    oprintf (f, "&gt_pch_n_");
 	    output_mangled_typename (f, tp);
+	    end_root_field (f, v, !if_marked);
 	  }
 	else if (has_length
 		 && (tp->kind == TYPE_POINTER || UNION_OR_STRUCT_P (tp)))
 	  {
-	    oprintf (f, "    &gt_ggc_ma_%s,\n", name);
-	    oprintf (f, "    &gt_pch_na_%s", name);
+	    start_root_field (f, v, "cb");
+	    oprintf (f, "&gt_ggc_ma_%s", name);
+	    end_root_field (f, v, false);
+	    start_root_field (f, v, "pchw");
+	    oprintf (f, "&gt_pch_na_%s", name);
+	    end_root_field (f, v, !if_marked);
 	  }
 	else
 	  {
@@ -3091,20 +3240,37 @@
 			   name);
 	  }
 	if (if_marked)
-	  oprintf (f, ",\n    &%s", if_marked);
-	oprintf (f, "\n  },\n");
+	  {
+	    start_root_field (f, v, "marked_p");
+	    oprintf (f, "&%s", if_marked);
+	    end_root_field (f, v, true);
+	  }
+	if (!v->thread_local)
+	  oprintf (f, "  },\n");
       }
       break;
 
     case TYPE_STRING:
       {
-	oprintf (f, "  {\n");
-	oprintf (f, "    &%s,\n", name);
-	oprintf (f, "    1, \n");
-	oprintf (f, "    sizeof (%s),\n", v->name);
-	oprintf (f, "    &gt_ggc_m_S,\n");
-	oprintf (f, "    (gt_pointer_walker) &gt_pch_n_S\n");
-	oprintf (f, "  },\n");
+	if (!v->thread_local)
+	  oprintf (f, "  {\n");
+	start_root_field (f, v, "base");
+	oprintf (f, "&%s", name);
+	end_root_field (f, v, false);
+	start_root_field (f, v, "nelt");
+	oprintf (f, "1");
+	end_root_field (f, v, false);
+	start_root_field (f, v, "stride");
+	oprintf (f, "sizeof (%s)", v->name);
+	end_root_field (f, v, false);
+	start_root_field (f, v, "cb");
+	oprintf (f, "&gt_ggc_m_S");
+	end_root_field (f, v, false);
+	start_root_field (f, v, "pchw");
+	oprintf (f, "(gt_pointer_walker) &gt_pch_n_S");
+	end_root_field (f, v, true);
+	if (!v->thread_local)
+	  oprintf (f, "  },\n");
       }
       break;
 
@@ -3168,10 +3334,27 @@
   oprintf (f, "}\n\n");
 }
 
+/* Write the initialization part of a stanza in a thread registration
+   function.  */
+static void
+start_thread_local_table (struct flist *fli, const char *type,
+			  const char *final)
+{
+  oprintf (fli->f, "  {\n");
+  oprintf (fli->f, "    struct %s final = %s;\n", type, final);
+  oprintf (fli->f, "    struct %s *table = XCNEWVEC (struct ggc_root_tab, ",
+	   type);
+  /* A cheap hack to let us lazily compute the table length: save some
+     space in the output and then later go back and overwrite it.  */
+  fli->saved_offset = fli->f->bufused;
+  oprintf (fli->f, "          );\n");
+  oprintf (fli->f, "    int ndx = 0;\n");
+}
+
 /* Output a table describing the locations and types of VARIABLES.  */
 
 static void
-write_roots (pair_p variables)
+write_roots (pair_p variables, bool thread_local)
 {
   pair_p v;
   struct flist *flp = NULL;
@@ -3184,6 +3367,9 @@
       int deletable_p = 0;
       options_p o;
 
+      if (thread_local && ! v->thread_local)
+	continue;
+
       for (o = v->opt; o; o = o->next)
 	if (strcmp (o->name, "length") == 0)
 	  length = o->info;
@@ -3211,13 +3397,31 @@
 	  fli->f = f;
 	  fli->next = flp;
 	  fli->started_p = 0;
+	  fli->item_count = 0;
+	  fli->saved_offset = 0;
 	  fli->name = v->line.file;
 	  flp = fli;
 
-	  oprintf (f, "\n/* GC roots.  */\n\n");
+	  if (thread_local)
+	    {
+	      oprintf (f, "\n/* Thread-local GC roots.  */\n\n");
+
+	      /* Emit a declaration first.  Bogus.  */
+	      oprintf (f, "struct ggc_thread_roots *ggc_thr_reg_");
+	      put_mangled_filename (f, v->line.file);
+	      oprintf (f, " (void);\n\n");
+
+	      oprintf (f, "struct ggc_thread_roots *\nggc_thr_reg_");
+	      put_mangled_filename (f, v->line.file);
+	      oprintf (f, " (void)\n{\n");
+	      oprintf (f, "  struct ggc_thread_roots *roots  = XCNEW (struct ggc_thread_roots);\n");
+	    }
+	  else
+	    oprintf (f, "\n/* GC roots.  */\n\n");
 	}
 
-      if (! deletable_p
+      if (! thread_local
+	  && ! deletable_p
 	  && length
 	  && v->type->kind == TYPE_POINTER
 	  && (v->type->u.p->kind == TYPE_POINTER
@@ -3236,6 +3440,9 @@
       int length_p = 0;
       options_p o;
 
+      if (v->thread_local != thread_local)
+	continue;
+
       for (o = v->opt; o; o = o->next)
 	if (strcmp (o->name, "length") == 0)
 	  length_p = 1;
@@ -3253,16 +3460,25 @@
 	{
 	  fli->started_p = 1;
 
-	  oprintf (f, "const struct ggc_root_tab gt_ggc_r_");
-	  put_mangled_filename (f, v->line.file);
-	  oprintf (f, "[] = {\n");
+	  if (thread_local)
+	    start_thread_local_table (fli, "ggc_root_tab", "LAST_GGC_ROOT_TAB");
+	  else
+	    {
+	      oprintf (f, "const struct ggc_root_tab gt_ggc_r_");
+	      put_mangled_filename (f, v->line.file);
+	      oprintf (f, "[] = {\n");
+	    }
 	}
 
+      ++fli->item_count;
       write_root (f, v, v->type, v->name, length_p, &v->line, NULL);
     }
 
-  finish_root_table (flp, "ggc_r", "LAST_GGC_ROOT_TAB", "ggc_root_tab",
-		     "gt_ggc_rtab");
+  if (thread_local)
+    finish_thread_registration (flp, "rtab");
+  else
+    finish_root_table (flp, "ggc_r", "LAST_GGC_ROOT_TAB", "ggc_root_tab",
+		       "gt_ggc_rtab");
 
   for (v = variables; v; v = v->next)
     {
@@ -3271,6 +3487,9 @@
       int skip_p = 1;
       options_p o;
 
+      if (v->thread_local != thread_local)
+	continue;
+
       for (o = v->opt; o; o = o->next)
 	if (strcmp (o->name, "deletable") == 0)
 	  skip_p = 0;
@@ -3287,17 +3506,36 @@
 	{
 	  fli->started_p = 1;
 
-	  oprintf (f, "const struct ggc_root_tab gt_ggc_rd_");
-	  put_mangled_filename (f, v->line.file);
-	  oprintf (f, "[] = {\n");
+	  if (thread_local)
+	    start_thread_local_table (fli, "ggc_root_tab", "LAST_GGC_ROOT_TAB");
+	  else
+	    {
+	      oprintf (f, "const struct ggc_root_tab gt_ggc_rd_");
+	      put_mangled_filename (f, v->line.file);
+	      oprintf (f, "[] = {\n");
+	    }
 	}
 
-      oprintf (f, "  { &%s, 1, sizeof (%s), NULL, NULL },\n",
-	       v->name, v->name);
+      ++fli->item_count;
+      if (thread_local)
+	{
+	  oprintf (f, "    table[ndx].base = &%s;\n", v->name);
+	  oprintf (f, "    table[ndx].nelt = 1;\n");
+	  oprintf (f, "    table[ndx].stride = sizeof (%s);\n", v->name);
+	  oprintf (f, "    table[ndx].cb = NULL;\n");
+	  oprintf (f, "    table[ndx].pchw = NULL;\n");
+	  oprintf (f, "    ++ndx;\n");
+	}
+      else
+	oprintf (f, "  { &%s, 1, sizeof (%s), NULL, NULL },\n",
+		 v->name, v->name);
     }
 
-  finish_root_table (flp, "ggc_rd", "LAST_GGC_ROOT_TAB", "ggc_root_tab",
-		     "gt_ggc_deletable_rtab");
+  if (thread_local)
+    finish_thread_registration (flp, "deletable_rtab");
+  else
+    finish_root_table (flp, "ggc_rd", "LAST_GGC_ROOT_TAB", "ggc_root_tab",
+		       "gt_ggc_deletable_rtab");
 
   for (v = variables; v; v = v->next)
     {
@@ -3307,6 +3545,9 @@
       int length_p = 0;
       options_p o;
 
+      if (v->thread_local != thread_local)
+	continue;
+
       for (o = v->opt; o; o = o->next)
 	if (strcmp (o->name, "length") == 0)
 	  length_p = 1;
@@ -3331,17 +3572,27 @@
 	{
 	  fli->started_p = 1;
 
-	  oprintf (f, "const struct ggc_cache_tab gt_ggc_rc_");
-	  put_mangled_filename (f, v->line.file);
-	  oprintf (f, "[] = {\n");
+	  if (thread_local)
+	    start_thread_local_table (fli, "ggc_cache_tab",
+				      "LAST_GGC_CACHE_TAB");
+	  else
+	    {
+	      oprintf (f, "const struct ggc_cache_tab gt_ggc_rc_");
+	      put_mangled_filename (f, v->line.file);
+	      oprintf (f, "[] = {\n");
+	    }
 	}
 
+      ++fli->item_count;
       write_root (f, v, v->type->u.p->u.param_struct.param[0],
 		     v->name, length_p, &v->line, if_marked);
     }
 
-  finish_root_table (flp, "ggc_rc", "LAST_GGC_CACHE_TAB", "ggc_cache_tab",
-		     "gt_ggc_cache_rtab");
+  if (thread_local)
+    finish_thread_registration (flp, "cache_rtab");
+  else
+    finish_root_table (flp, "ggc_rc", "LAST_GGC_CACHE_TAB", "ggc_cache_tab",
+		       "gt_ggc_cache_rtab");
 
   for (v = variables; v; v = v->next)
     {
@@ -3351,6 +3602,9 @@
       int if_marked_p = 0;
       options_p o;
 
+      if (v->thread_local != thread_local)
+	continue;
+
       for (o = v->opt; o; o = o->next)
 	if (strcmp (o->name, "length") == 0)
 	  length_p = 1;
@@ -3367,16 +3621,25 @@
 	{
 	  fli->started_p = 1;
 
-	  oprintf (f, "const struct ggc_root_tab gt_pch_rc_");
-	  put_mangled_filename (f, v->line.file);
-	  oprintf (f, "[] = {\n");
+	  if (thread_local)
+	    start_thread_local_table (fli, "ggc_root_tab", "LAST_GGC_ROOT_TAB");
+	  else
+	    {
+	      oprintf (f, "const struct ggc_root_tab gt_pch_rc_");
+	      put_mangled_filename (f, v->line.file);
+	      oprintf (f, "[] = {\n");
+	    }
 	}
 
+      ++fli->item_count;
       write_root (f, v, v->type, v->name, length_p, &v->line, NULL);
     }
 
-  finish_root_table (flp, "pch_rc", "LAST_GGC_ROOT_TAB", "ggc_root_tab",
-		     "gt_pch_cache_rtab");
+  if (thread_local)
+    finish_thread_registration (flp, "pch_cache_rtab");
+  else
+    finish_root_table (flp, "pch_rc", "LAST_GGC_ROOT_TAB", "ggc_root_tab",
+		       "gt_pch_cache_rtab");
 
   for (v = variables; v; v = v->next)
     {
@@ -3385,6 +3648,9 @@
       int skip_p = 0;
       options_p o;
 
+      if (v->thread_local != thread_local)
+	continue;
+
       for (o = v->opt; o; o = o->next)
 	if (strcmp (o->name, "deletable") == 0
 	    || strcmp (o->name, "if_marked") == 0)
@@ -3403,17 +3669,39 @@
 	{
 	  fli->started_p = 1;
 
-	  oprintf (f, "const struct ggc_root_tab gt_pch_rs_");
-	  put_mangled_filename (f, v->line.file);
-	  oprintf (f, "[] = {\n");
+	  if (thread_local)
+	    start_thread_local_table (fli, "ggc_root_tab", "LAST_GGC_ROOT_TAB");
+	  else
+	    {
+	      oprintf (f, "const struct ggc_root_tab gt_pch_rs_");
+	      put_mangled_filename (f, v->line.file);
+	      oprintf (f, "[] = {\n");
+	    }
 	}
 
-      oprintf (f, "  { &%s, 1, sizeof (%s), NULL, NULL },\n",
-	       v->name, v->name);
+      ++fli->item_count;
+      if (thread_local)
+	{
+	  oprintf (f, "    table[ndx].base = &%s;\n", v->name);
+	  oprintf (f, "    table[ndx].nelt = 1;\n");
+	  oprintf (f, "    table[ndx].stride = sizeof (%s);\n", v->name);
+	  oprintf (f, "    table[ndx].cb = NULL;\n");
+	  oprintf (f, "    table[ndx].pchw = NULL;\n");
+	  oprintf (f, "    ++ndx;\n");
+	}
+      else	
+	oprintf (f, "  { &%s, 1, sizeof (%s), NULL, NULL },\n",
+		 v->name, v->name);
     }
 
-  finish_root_table (flp, "pch_rs", "LAST_GGC_ROOT_TAB", "ggc_root_tab",
-		     "gt_pch_scalar_rtab");
+  if (thread_local)
+    {
+      finish_thread_registration (flp, "pch_scalar_rtab");
+      write_thread_lang_registration (flp);
+    }
+  else
+    finish_root_table (flp, "pch_rs", "LAST_GGC_ROOT_TAB", "ggc_root_tab",
+		       "gt_pch_scalar_rtab");
 }
 
 /* Record the definition of a generic VEC structure, as if we had expanded
@@ -3555,7 +3843,8 @@
   write_types (structures, param_structs, &ggc_wtd);
   write_types (structures, param_structs, &pch_wtd);
   write_local (structures, param_structs);
-  write_roots (variables);
+  write_roots (variables, false);
+  write_roots (variables, true);
   write_rtx_next ();
   close_output_files ();
 
Index: gengtype.h
===================================================================
--- gengtype.h	(revision 127650)
+++ gengtype.h	(working copy)
@@ -62,7 +62,7 @@
 			       options_p opt, struct fileloc *pos);
 extern pair_p nreverse_pairs (pair_p list);
 extern type_p adjust_field_type (type_p, options_p);
-extern void note_variable (const char *s, type_p t, options_p o,
+extern void note_variable (const char *s, bool, type_p t, options_p o,
 			   struct fileloc *pos);
 extern void note_def_vec (const char *typename, bool is_scalar,
 			  struct fileloc *pos);
@@ -85,6 +85,7 @@
 
   CHAR_TOKEN_OFFSET = UCHAR_MAX + 1,
   GTY_TOKEN = CHAR_TOKEN_OFFSET,
+  THREAD,
   TYPEDEF,
   EXTERN,
   STATIC,
Index: toplev.c
===================================================================
--- toplev.c	(revision 129025)
+++ toplev.c	(working copy)
@@ -1680,6 +1680,7 @@
   /* Initialize the garbage-collector, string pools and tree type hash
      table.  */
   init_ggc ();
+  ggc_thread_init ();
   init_stringpool ();
   linemap_init (&line_table);
   init_ttree ();
Index: host-thread.h
===================================================================
--- host-thread.h	(revision 0)
+++ host-thread.h	(revision 0)
@@ -0,0 +1,82 @@
+/* Host thread abstraction for GCC.
+   Copyright (C) 2007
+   Free Software Foundation, Inc.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_HOST_THREAD_H
+#define GCC_HOST_THREAD_H
+
+/* For now, just pthreads.  */
+#include <pthread.h>
+
+typedef pthread_mutex_t host_mutex;
+typedef pthread_cond_t host_condition;
+
+static inline host_mutex *
+host_mutex_create (void)
+{
+  host_mutex *result = XNEW (host_mutex);
+  pthread_mutex_init (result, NULL);
+  return result;
+}
+
+static inline void
+host_mutex_lock (host_mutex *m)
+{
+  pthread_mutex_lock (m);
+}
+
+static inline void
+host_mutex_unlock (host_mutex *m)
+{
+  pthread_mutex_unlock (m);
+}
+
+static inline host_condition *
+host_condition_create (void)
+{
+  host_condition *result = XNEW (host_condition);
+  pthread_cond_init (result, NULL);
+  return result;
+}
+
+static inline void
+host_condition_wait (host_condition *c, host_mutex *m)
+{
+  pthread_cond_wait (c, m);
+}
+
+static inline void
+host_condition_broadcast (host_condition *c)
+{
+  pthread_cond_broadcast (c);
+}
+
+static inline void
+host_thread_create (void *(*func) (void *), void *arg)
+{
+  pthread_t thr;
+  pthread_attr_t attr;
+  pthread_attr_init (&attr);
+  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+  /* FIXME: error handling.  */
+  pthread_create (&thr, &attr, func, arg);
+  pthread_attr_destroy (&attr);
+}
+
+#endif /* GCC_HOST_THREAD_H */
Index: gengtype-lex.l
===================================================================
--- gengtype-lex.l	(revision 127650)
+++ gengtype-lex.l	(working copy)
@@ -107,6 +107,7 @@
 \\\n				{ lexer_line.line++; }
 
 "const"/{EOID}			/* don't care */
+"GCC_THREAD"/{EOID}		{ return THREAD; }
 "GTY"/{EOID}			{ return GTY_TOKEN; }
 "VEC"/{EOID}			{ return VEC_TOKEN; }
 "union"/{EOID}			{ return UNION; }
Index: gengtype-parse.c
===================================================================
--- gengtype-parse.c	(revision 127650)
+++ gengtype-parse.c	(working copy)
@@ -804,8 +804,15 @@
   options_p opts, opts2, dopts;
   type_p ty, dty;
   const char *name;
+  bool thread_local = false;
   require2 (EXTERN, STATIC);
 
+  if (token () == THREAD)
+    {
+      advance ();
+      thread_local = true;
+    }
+
   if (token () != GTY_TOKEN)
     {
       advance ();
@@ -825,7 +832,8 @@
 
   if (dty)
     {
-      note_variable (name, adjust_field_type (dty, opts), opts, &lexer_line);
+      note_variable (name, thread_local, adjust_field_type (dty, opts),
+		     opts, &lexer_line);
       require2 (';', '=');
     }
 }
Index: ggc.h
===================================================================
--- ggc.h	(revision 127650)
+++ ggc.h	(working copy)
@@ -20,7 +20,9 @@
 
 #ifndef GCC_GGC_H
 #define GCC_GGC_H
+
 #include "statistics.h"
+#include "host-thread.h"
 
 /* Symbols are marked with `ggc' for `gcc gc' so as not to interfere with
    an external gc library that might be linked in.  */
@@ -93,6 +95,32 @@
 /* Pointers to arrays of ggc_cache_tab, terminated by NULL.  */
 extern const struct ggc_cache_tab * const gt_ggc_cache_rtab[];
 
+/* Structure for thread-local root registration.  */
+struct ggc_thread_roots {
+  struct ggc_root_tab *rtab;
+  struct ggc_root_tab *deletable_rtab;
+  struct ggc_cache_tab *cache_rtab;
+  struct ggc_root_tab *pch_cache_rtab;
+  struct ggc_root_tab *pch_scalar_rtab;
+  struct ggc_thread_roots *next;
+};
+
+/* Holds one thread's list of roots.  */
+struct ggc_thread_root_list {
+  struct ggc_thread_roots *roots;
+  struct ggc_thread_root_list *next;
+};
+
+/* A function called during thread registration to return a set of new
+   roots.  */
+typedef struct ggc_thread_roots *(*ggc_thread_registration_function) (void);
+
+/* Array of all thread registration functions, NULL terminated.  */
+extern const ggc_thread_registration_function gt_ggc_thread_funcs[];
+
+/* The global GC lock.  Only the collector should use this lock.  */
+extern host_mutex *ggc_gc_lock;
+
 /* If EXPR is not NULL and previously unmarked, mark it and evaluate
    to true.  Otherwise evaluate to false.  */
 #define ggc_test_and_set_mark(EXPR) \
@@ -194,6 +222,29 @@
    parameter.  Set up the GC implementation for the new objects.  */
 extern void ggc_pch_read (FILE *, void *);
 
+/* Initialize GC thread code.  */
+extern void ggc_thread_init (void);
+
+/* Call just after thread startup to set up thread-local roots.  */
+extern void ggc_thread_start (void);
+
+/* Call just before thread exit to remove thread-local roots.  */
+extern void ggc_thread_clean_up (void);
+
+/* A thread can claim to always be at a GC safe point by calling this
+   function.  */
+extern void ggc_thread_ignore_me (void);
+
+/* GC implementations call this to pause pending a GC.  This functions
+   as a safe point.  Argument is true if a GC is requested.  Returns
+   false if this thread should GC; true in all other threads.  GCing
+   thread must call ggc_collection_completed.  */
+extern bool ggc_thread_pause (bool);
+
+/* Called by a GC implementation after a GC to wake up all paused
+   threads.  */
+extern void ggc_collection_completed (void);
+
 \f
 /* Allocation.  */
 
Index: ggc-common.c
===================================================================
--- ggc-common.c	(revision 127650)
+++ ggc-common.c	(working copy)
@@ -66,6 +66,9 @@
 /* Statistics about the allocation.  */
 static ggc_statistics *ggc_stats;
 
+/* List of thread-local roots.  */
+static struct ggc_thread_root_list *global_root_list;
+
 struct traversal_state;
 
 static int ggc_htab_delete (void **, void *);
@@ -105,17 +108,37 @@
   const struct ggc_root_tab *rti;
   const struct ggc_cache_tab *const *ct;
   const struct ggc_cache_tab *cti;
+  struct ggc_thread_root_list *root_iter;
   size_t i;
 
   for (rt = gt_ggc_deletable_rtab; *rt; rt++)
     for (rti = *rt; rti->base != NULL; rti++)
       memset (rti->base, 0, rti->stride);
 
+  for (root_iter = global_root_list; root_iter; root_iter = root_iter->next)
+    {
+      struct ggc_thread_roots *ir;
+      for (ir = root_iter->roots; ir; ir = ir->next)
+	if (ir->deletable_rtab)
+	  for (rti = ir->deletable_rtab; rti->base != NULL; rti++)
+	    memset (rti->base, 0, rti->stride);
+    }
+
   for (rt = gt_ggc_rtab; *rt; rt++)
     for (rti = *rt; rti->base != NULL; rti++)
       for (i = 0; i < rti->nelt; i++)
 	(*rti->cb)(*(void **)((char *)rti->base + rti->stride * i));
 
+  for (root_iter = global_root_list; root_iter; root_iter = root_iter->next)
+    {
+      struct ggc_thread_roots *ir;
+      for (ir = root_iter->roots; ir; ir = ir->next)
+	if (ir->rtab)
+	  for (rti = ir->rtab; rti->base != NULL; rti++)
+	    for (i = 0; i < rti->nelt; i++)
+	      (*rti->cb)(*(void **)((char *)rti->base + rti->stride * i));
+    }
+
   ggc_mark_stringpool ();
 
   /* Now scan all hash tables that have objects which are to be deleted if
@@ -128,6 +151,21 @@
 	  htab_traverse_noresize (*cti->base, ggc_htab_delete, (void *) cti);
 	  ggc_set_mark ((*cti->base)->entries);
 	}
+
+  for (root_iter = global_root_list; root_iter; root_iter = root_iter->next)
+    {
+      struct ggc_thread_roots *ir;
+      for (ir = root_iter->roots; ir; ir = ir->next)
+	if (ir->cache_rtab)
+	  for (cti = ir->cache_rtab; cti->base != NULL; cti++)
+	    if (*cti->base)
+	      {
+		ggc_set_mark (*cti->base);
+		htab_traverse_noresize (*cti->base, ggc_htab_delete,
+					(void *) cti);
+		ggc_set_mark ((*cti->base)->entries);
+	      }
+    }
 }
 
 /* Allocate a block of memory, then clear it.  */
@@ -1020,3 +1058,165 @@
   fprintf (stderr, "-------------------------------------------------------\n");
 #endif
 }
+\f
+/* Thread-local storage handling.  */
+
+/* Thread-local so we know what to delete when the thread stops.  */
+static GCC_THREAD struct ggc_thread_root_list *roots;
+
+/* The number of threads currently registered with the GC.  */
+static int ggc_n_threads;
+
+/* The global GC lock.  This is held whenever manipulating GC global
+   data.  */
+host_mutex *ggc_gc_lock;
+
+/* The global GC condition variable.  This is used to pause threads
+   while one thread collects.  */
+static host_condition *gc_condition;
+
+/* The number of threads currently waiting for a GC.  */
+static int ggc_n_waiting_threads;
+
+/* The collection generation.  This is used to avoid spurious
+   condition variable wakeups.  */
+static unsigned int collection_generation;
+
+void
+ggc_thread_init (void)
+{
+  ggc_gc_lock = host_mutex_create ();
+  gc_condition = host_condition_create ();
+
+  /* Register this thread.  */
+  ggc_thread_start ();
+}
+
+/* Register all the thread-local variables for the current thread with
+   the GC.  Should be called just after the thread is created.  */
+void
+ggc_thread_start (void)
+{
+  int i;
+
+  host_mutex_lock (ggc_gc_lock);
+
+  roots = XNEW (struct ggc_thread_root_list);
+  roots->roots = NULL;
+  roots->next = global_root_list;
+  global_root_list = roots;
+
+  for (i = 0; gt_ggc_thread_funcs[i]; ++i)
+    {
+      struct ggc_thread_roots *r = (gt_ggc_thread_funcs[i]) ();
+      r->next = roots->roots;
+      roots->roots = r;
+    }
+
+  ++ggc_n_threads;
+
+  host_mutex_unlock (ggc_gc_lock);
+}
+
+/* Clean up when a thread exits.  */
+void
+ggc_thread_clean_up (void)
+{
+  struct ggc_thread_root_list **iter;
+  struct ggc_thread_roots *riter, *next;
+
+  host_mutex_lock (ggc_gc_lock);
+
+  while (ggc_n_waiting_threads)
+    {
+      /* A GC is pending, so we must pause here as well.  Otherwise,
+	 we could cause a lockup if this thread is the last one
+	 entering the safe point.  */
+      host_mutex_unlock (ggc_gc_lock);
+      ggc_collect ();
+      host_mutex_lock (ggc_gc_lock);
+    }
+
+  /* Remove our entry from the global root list.  */
+  for (iter = &global_root_list; *iter; iter = &(*iter)->next)
+    {
+      if (*iter == roots)
+	{
+	  *iter = (*iter)->next;
+	  break;
+	}
+    }
+
+  /* Free the data.  */
+  for (riter = roots->roots; riter; riter = next)
+    {
+      if (riter->rtab)
+	free (riter->rtab);
+      if (riter->deletable_rtab)
+	free (riter->deletable_rtab);
+      if (riter->cache_rtab)
+	free (riter->cache_rtab);
+      if (riter->pch_cache_rtab)
+	free (riter->pch_cache_rtab);
+      if (riter->pch_scalar_rtab)
+	free (riter->pch_scalar_rtab);
+      next = riter->next;
+      free (riter);
+    }
+
+  free (roots);
+
+  --ggc_n_threads;
+
+  host_mutex_unlock (ggc_gc_lock);
+}
+
+bool
+ggc_thread_pause (bool want_gc)
+{
+  bool result = true;
+
+  host_mutex_lock (ggc_gc_lock);
+
+  if (want_gc || ggc_n_waiting_threads)
+    {
+      ++ggc_n_waiting_threads;
+      if (ggc_n_waiting_threads == ggc_n_threads)
+	{
+	  /* Return with the lock still held.  */
+	  return false;
+	}
+      else
+	{
+	  /* Conditions may spuriously wake up, so we protect against
+	     this by checking the generation.  */
+	  unsigned int this_gen = collection_generation;
+	  while (this_gen == collection_generation)
+	    host_condition_wait (gc_condition, ggc_gc_lock);
+	}
+    }
+
+  host_mutex_unlock (ggc_gc_lock);
+
+  return result;
+}
+
+void
+ggc_collection_completed (void)
+{
+  /* Note that the lock is already held here.  */
+
+  ++collection_generation;
+  ggc_n_waiting_threads = 0;
+  host_condition_broadcast (gc_condition);
+
+  host_mutex_unlock (ggc_gc_lock);
+}
+
+void
+ggc_thread_ignore_me (void)
+{
+  host_mutex_lock (ggc_gc_lock);
+  --ggc_n_threads;
+  host_mutex_unlock (ggc_gc_lock);
+}
Index: ggc-zone.c
===================================================================
--- ggc-zone.c	(revision 127650)
+++ ggc-zone.c	(working copy)
@@ -1012,6 +1012,8 @@
   void *result;
   size_t size = orig_size;
 
+  host_mutex_lock (ggc_gc_lock);
+
   /* Make sure that zero-sized allocations get a unique and freeable
      pointer.  */
   if (size == 0)
@@ -1262,6 +1264,7 @@
     fprintf (G.debug_file, "Allocating object, size=%lu at %p\n",
 	     (unsigned long) size, result);
 
+  host_mutex_unlock (ggc_gc_lock);
   return result;
 }
 
@@ -1311,6 +1314,8 @@
 {
   struct page_entry *page;
 
+  host_mutex_lock (ggc_gc_lock);
+
 #ifdef GATHER_STATISTICS
   ggc_free_overhead (p);
 #endif
@@ -1354,6 +1359,8 @@
 	 since we are likely to want a chunk of this size again.  */
       free_chunk (p, size, page->zone);
     }
+
+  host_mutex_unlock (ggc_gc_lock);
 }
 
 /* If P is not marked, mark it and return false.  Otherwise return true.
@@ -1452,7 +1459,10 @@
 {
   struct page_entry *page;
   const char *ptr = (const char *) p;
+  size_t result;
 
+  host_mutex_lock (ggc_gc_lock);
+
   page = zone_get_object_page (p);
 
   if (page->pch_p)
@@ -1462,14 +1472,16 @@
       alloc_word = offset / (8 * sizeof (alloc_type));
       alloc_bit = offset % (8 * sizeof (alloc_type));
       max_size = pch_zone.bytes - (ptr - pch_zone.page);
-      return zone_object_size_1 (pch_zone.alloc_bits, alloc_word, alloc_bit,
-				 max_size);
+      result = zone_object_size_1 (pch_zone.alloc_bits, alloc_word, alloc_bit,
+				   max_size);
     }
+  else if (page->large_p)
+    result = ((struct large_page_entry *)page)->bytes;
+  else
+    result = zone_find_object_size ((struct small_page_entry *) page, p);
 
-  if (page->large_p)
-    return ((struct large_page_entry *)page)->bytes;
-  else
-    return zone_find_object_size ((struct small_page_entry *) page, p);
+  host_mutex_unlock (ggc_gc_lock);
+  return result;
 }
 
 /* Initialize the ggc-zone-mmap allocator.  */
@@ -1568,8 +1580,12 @@
 struct alloc_zone *
 new_ggc_zone (const char * name)
 {
-  struct alloc_zone *new_zone = xcalloc (1, sizeof (struct alloc_zone));
+  struct alloc_zone *new_zone;
+
+  host_mutex_lock (ggc_gc_lock);
+  new_zone = xcalloc (1, sizeof (struct alloc_zone));
   new_ggc_zone_1 (new_zone, name);
+  host_mutex_unlock (ggc_gc_lock);
   return new_zone;
 }
 
@@ -1579,6 +1595,8 @@
 {
   struct alloc_zone *z;
 
+  host_mutex_lock (ggc_gc_lock);
+
   for (z = G.zones; z && z->next_zone != dead_zone; z = z->next_zone)
     /* Just find that zone.  */
     continue;
@@ -1588,6 +1606,8 @@
 
   /* z is dead, baby. z is dead.  */
   z->dead = true;
+
+  host_mutex_unlock (ggc_gc_lock);
 }
 
 /* Free all empty pages and objects within a page for a given zone  */
@@ -1857,10 +1877,13 @@
 {
   struct alloc_zone *zone;
   bool marked = false;
+  bool want_gc;
 
   timevar_push (TV_GC);
 
-  if (!ggc_force_collect)
+  if (ggc_force_collect)
+    want_gc = true;
+  else
     {
       float allocated_last_gc = 0, allocated = 0, min_expand;
 
@@ -1875,12 +1898,13 @@
 	     (size_t) PARAM_VALUE (GGC_MIN_HEAPSIZE) * 1024);
       min_expand = allocated_last_gc * PARAM_VALUE (GGC_MIN_EXPAND) / 100;
 
-      if (allocated < allocated_last_gc + min_expand)
-	{
-	  timevar_pop (TV_GC);
-	  return;
-	}
+      want_gc = ! (allocated < allocated_last_gc + min_expand);
     }
+  if (ggc_thread_pause (want_gc))
+    {
+      timevar_pop (TV_GC);
+      return;
+    }
 
   /* Start by possibly collecting the main zone.  */
   main_zone.was_collected = false;
@@ -1947,6 +1971,8 @@
     }
 
   timevar_pop (TV_GC);
+
+  ggc_collection_completed ();
 }
 
 /* Print allocation statistics.  */
Index: ggc-page.c
===================================================================
--- ggc-page.c	(revision 127650)
+++ ggc-page.c	(working copy)
@@ -1083,6 +1083,8 @@
   struct page_entry *entry;
   void *result;
 
+  host_mutex_lock (ggc_gc_lock);
+
   if (size < NUM_SIZE_LOOKUP)
     {
       order = size_lookup[size];
@@ -1264,6 +1266,8 @@
 	     (unsigned long) size, (unsigned long) object_size, result,
 	     (void *) entry);
 
+  host_mutex_unlock (ggc_gc_lock);
+
   return result;
 }
 
@@ -1333,8 +1337,14 @@
 size_t
 ggc_get_size (const void *p)
 {
-  page_entry *pe = lookup_page_table_entry (p);
-  return OBJECT_SIZE (pe->order);
+  page_entry *pe;
+  size_t result;
+
+  host_mutex_lock (ggc_gc_lock);
+  pe = lookup_page_table_entry (p);
+  result = OBJECT_SIZE (pe->order);
+  host_mutex_unlock (ggc_gc_lock);
+  return result;
 }
 
 /* Release the memory for object P.  */
@@ -1342,10 +1352,15 @@
 void
 ggc_free (void *p)
 {
-  page_entry *pe = lookup_page_table_entry (p);
-  size_t order = pe->order;
-  size_t size = OBJECT_SIZE (order);
+  page_entry *pe;
+  size_t order, size;
 
+  host_mutex_lock (ggc_gc_lock);
+
+  pe = lookup_page_table_entry (p);
+  order = pe->order;
+  size = OBJECT_SIZE (order);
+
 #ifdef GATHER_STATISTICS
   ggc_free_overhead (p);
 #endif
@@ -1423,6 +1438,8 @@
       }
   }
 #endif
+
+  host_mutex_unlock (ggc_gc_lock);
 }
 \f
 /* Subroutine of init_ggc which computes the pair of numbers used to
@@ -1881,7 +1898,8 @@
 
   float min_expand = allocated_last_gc * PARAM_VALUE (GGC_MIN_EXPAND) / 100;
 
-  if (G.allocated < allocated_last_gc + min_expand && !ggc_force_collect)
+  if (ggc_thread_pause (! (G.allocated < allocated_last_gc + min_expand
+			   && !ggc_force_collect)))
     return;
 
   timevar_push (TV_GC);
@@ -1918,6 +1936,8 @@
     fprintf (stderr, "%luk}", (unsigned long) G.allocated / 1024);
   if (GGC_DEBUG_LEVEL >= 2)
     fprintf (G.debug_file, "END COLLECTING\n");
+
+  ggc_collection_completed ();
 }
 
 /* Print allocation statistics.  */
Index: system.h
===================================================================
--- system.h	(revision 127650)
+++ system.h	(working copy)
@@ -780,4 +780,6 @@
 #define CONST_CAST(X) ((void*)(X))
 #endif
 
+#define GCC_THREAD __thread
+
 #endif /* ! GCC_SYSTEM_H */
Index: Makefile.in
===================================================================
--- Makefile.in	(revision 128558)
+++ Makefile.in	(working copy)
@@ -863,7 +863,7 @@
 
 # How to link with both our special library facilities
 # and the system's installed libraries.
-LIBS = @LIBS@ $(CPPLIB) $(LIBINTL) $(LIBICONV) $(LIBIBERTY) $(LIBDECNUMBER) $(GMPLIBS)
+LIBS = @LIBS@ $(CPPLIB) $(LIBINTL) $(LIBICONV) $(LIBIBERTY) $(LIBDECNUMBER) $(GMPLIBS) -lpthread
 
 # Any system libraries needed just for GNAT.
 SYSLIBS = @GNAT_LIBEXC@

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [incremental] Patch: FYI: initial threading in gcc
  2007-10-11 20:34 [incremental] Patch: FYI: initial threading in gcc Tom Tromey
@ 2007-10-12  8:57 ` Paolo Bonzini
  2007-10-12 16:05   ` Tom Tromey
  0 siblings, 1 reply; 3+ messages in thread
From: Paolo Bonzini @ 2007-10-12  8:57 UTC (permalink / raw)
  To: GCC Patches, Tom Tromey


> There are some limitations in the current patch.  First, it assumes
> pthreads and the existence of __thread.  Fixing this is just some
> boring configury, or perhaps figuring out how to re-use gthr* on the
> host.  (If __thread is not available, I plan to have the server fall
> back to single-threaded mode.)  Second, I haven't implemented PCH
> support for thread-locals.

Is there a way to do so, other than:

1) moving all globals to the heap or to GGC memory, so that they are at 
least in a shared address space

2) having each thread store a pointer to its heap-allocated data both in 
a thread-local variable, and in a global variable indexed by thread id.

3) having PCH look at the latter, and GCC using the former.


I fear that the above might also mean implementing pointer-swizzling on 
PCH load, because you might need the same PCH in different threads.

Great job!

Paolo

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [incremental] Patch: FYI: initial threading in gcc
  2007-10-12  8:57 ` Paolo Bonzini
@ 2007-10-12 16:05   ` Tom Tromey
  0 siblings, 0 replies; 3+ messages in thread
From: Tom Tromey @ 2007-10-12 16:05 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: GCC Patches

>>>>> "Paolo" == Paolo Bonzini <bonzini@gnu.org> writes:

>> There are some limitations in the current patch.  First, it assumes
>> pthreads and the existence of __thread.  Fixing this is just some
>> boring configury, or perhaps figuring out how to re-use gthr* on the
>> host.  (If __thread is not available, I plan to have the server fall
>> back to single-threaded mode.)  Second, I haven't implemented PCH
>> support for thread-locals.

Paolo> 1) moving all globals to the heap or to GGC memory, so that they are
Paolo> at least in a shared address space

Paolo> I fear that the above might also mean implementing pointer-swizzling
Paolo> on PCH load, because you might need the same PCH in different threads.

I think this would only be an issue if we have an object on the heap
that contains the address of a thread-local variable.  I don't know
whether that happens, but I doubt that it does.

So, I don't think we'll need to do full pointer swizzling on PCH load.
But, we'll have to arrange for the thread-locals to get their correct
values at startup.  I haven't looked into this but I don't think it
should be very hard or very expensive.

I'm not planning to allow creating a PCH when there are multiple
threads running.  That should simplify things a bit.

Tom

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2007-10-12 16:05 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-10-11 20:34 [incremental] Patch: FYI: initial threading in gcc Tom Tromey
2007-10-12  8:57 ` Paolo Bonzini
2007-10-12 16:05   ` Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).