From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 13478 invoked by alias); 11 Oct 2007 20:34:37 -0000 Received: (qmail 13241 invoked by uid 22791); 11 Oct 2007 20:34:32 -0000 X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (66.187.233.31) by sourceware.org (qpsmtpd/0.31) with ESMTP; Thu, 11 Oct 2007 20:34:23 +0000 Received: from int-mx1.corp.redhat.com (int-mx1.corp.redhat.com [172.16.52.254]) by mx1.redhat.com (8.13.8/8.13.1) with ESMTP id l9BKYLJD015236 for ; Thu, 11 Oct 2007 16:34:21 -0400 Received: from pobox.corp.redhat.com (pobox.corp.redhat.com [10.11.255.20]) by int-mx1.corp.redhat.com (8.13.1/8.13.1) with ESMTP id l9BKYLYV017491 for ; Thu, 11 Oct 2007 16:34:21 -0400 Received: from opsy.redhat.com (ton.toronto.redhat.com [172.16.14.15]) by pobox.corp.redhat.com (8.13.1/8.13.1) with ESMTP id l9BKYIZ2027792; Thu, 11 Oct 2007 16:34:19 -0400 Received: by opsy.redhat.com (Postfix, from userid 500) id 771D6508015; Thu, 11 Oct 2007 14:09:35 -0600 (MDT) To: Gcc Patch List Subject: [incremental] Patch: FYI: initial threading in gcc From: Tom Tromey Reply-To: Tom Tromey X-Attribution: Tom Date: Thu, 11 Oct 2007 20:34:00 -0000 Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org X-SW-Source: 2007-10/txt/msg00683.txt.bz2 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 * 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) : New field. (do_typedef): Initialize it. (note_variable): Added 'thread_local' argument. (create_field_all): Initialize new field. (struct flist) : 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 +. */ + +#ifndef GCC_HOST_THREAD_H +#define GCC_HOST_THREAD_H + +/* For now, just pthreads. */ +#include + +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); + /* 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 } + +/* 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); } /* 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@