* [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, " >_ggc_mx_%s,\n", tp->u.s.tag);
- oprintf (f, " >_pch_nx_%s", tp->u.s.tag);
+ start_root_field (f, v, "cb");
+ oprintf (f, ">_ggc_mx_%s", tp->u.s.tag);
+ end_root_field (f, v, false);
+ start_root_field (f, v, "pchw");
+ oprintf (f, ">_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, " >_ggc_m_");
+ start_root_field (f, v, "cb");
+ oprintf (f, ">_ggc_m_");
output_mangled_typename (f, tp);
- oprintf (f, ",\n >_pch_n_");
+ end_root_field (f, v, false);
+ start_root_field (f, v, "pchw");
+ oprintf (f, ">_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, " >_ggc_ma_%s,\n", name);
- oprintf (f, " >_pch_na_%s", name);
+ start_root_field (f, v, "cb");
+ oprintf (f, ">_ggc_ma_%s", name);
+ end_root_field (f, v, false);
+ start_root_field (f, v, "pchw");
+ oprintf (f, ">_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, " >_ggc_m_S,\n");
- oprintf (f, " (gt_pointer_walker) >_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, ">_ggc_m_S");
+ end_root_field (f, v, false);
+ start_root_field (f, v, "pchw");
+ oprintf (f, "(gt_pointer_walker) >_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).