diff --git a/gcc/gengtype-parse.c b/gcc/gengtype-parse.c
index 31d493a..f480503 100644
--- a/gcc/gengtype-parse.c
+++ b/gcc/gengtype-parse.c
@@ -793,7 +793,7 @@ struct_field_seq (void)
 
 /* Return true if OPTS contain the option named STR.  */
 
-static bool
+bool
 opts_have (options_p opts, const char *str)
 {
   for (options_p opt = opts; opt; opt = opt->next)
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index a6dc221..4224a5e 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -1532,7 +1532,11 @@ set_gc_used_type (type_p t, enum gc_used_enum level, type_p param[NUM_PARAM],
 	process_gc_options (t->u.s.opt, level, &dummy, &dummy, &dummy, &dummy,
 			    &dummy2);
 
-	for (f = t->u.s.fields; f; f = f->next)
+	if (t->u.s.base_class)
+	  set_gc_used_type (t->u.s.base_class, level, param,
+			    allow_undefined_types);
+
+	FOR_ALL_INHERITED_FIELDS(t, f)
 	  {
 	    int maybe_undef = 0;
 	    int pass_param = 0;
@@ -2534,6 +2538,11 @@ output_mangled_typename (outf_p of, const_type_p t)
       case TYPE_LANG_STRUCT:
       case TYPE_USER_STRUCT:
 	{
+	  /* For references to classes within an inheritance hierarchy,
+	     only ever reference the ultimate base class, since only
+	     it will have gt_ functions.  */
+	  while (t->u.s.base_class)
+	    t = t->u.s.base_class;
 	  const char *id_for_tag = filter_type_name (t->u.s.tag);
 	  oprintf (of, "%lu%s", (unsigned long) strlen (id_for_tag),
 		   id_for_tag);
@@ -2596,6 +2605,44 @@ output_escaped_param (struct walk_type_data *d, const char *param,
 	}
 }
 
+const char *
+get_string_option (options_p opt, const char *key)
+{
+  for (; opt; opt = opt->next)
+    if (strcmp (opt->name, key) == 0)
+      return opt->info.string;
+  return NULL;
+}
+
+static void
+walk_subclasses (type_p base, struct walk_type_data *d)
+{
+  for (type_p sub = structures; sub != NULL; sub = sub->next)
+    {
+      if (sub->u.s.base_class == base)
+	{
+	  const char *type_tag = get_string_option (sub->u.s.opt, "tag");
+	  if (type_tag)
+	    {
+	      oprintf (d->of, "%*scase %s:\n", d->indent, "", type_tag);
+	      d->indent += 2;
+	      oprintf (d->of, "%*s{\n", d->indent, "");
+	      d->indent += 2;
+	      oprintf (d->of, "%*s%s *sub = static_cast <%s *> (x);\n",
+		       d->indent, "", sub->u.s.tag, sub->u.s.tag);
+	      const char *old_val = d->val;
+	      d->val = "(*sub)";
+	      walk_type (sub, d);
+	      d->val = old_val;
+	      d->indent -= 2;
+	      oprintf (d->of, "%*s}\n", d->indent, "");
+	      oprintf (d->of, "%*sbreak;\n", d->indent, "");
+	      d->indent -= 2;
+	    }
+	  walk_subclasses (sub, d);
+	}
+    }
+}
 
 /* Call D->PROCESS_FIELD for every field (or subfield) of D->VAL,
    which is of type T.  Write code to D->OF to constrain execution (at
@@ -2613,6 +2660,7 @@ walk_type (type_p t, struct walk_type_data *d)
 {
   const char *length = NULL;
   const char *desc = NULL;
+  const char *type_tag = NULL;
   int maybe_undef_p = 0;
   int use_param_num = -1;
   int use_params_p = 0;
@@ -2641,7 +2689,7 @@ walk_type (type_p t, struct walk_type_data *d)
     else if (strcmp (oo->name, "dot") == 0)
       ;
     else if (strcmp (oo->name, "tag") == 0)
-      ;
+      type_tag = oo->info.string;
     else if (strcmp (oo->name, "special") == 0)
       ;
     else if (strcmp (oo->name, "skip") == 0)
@@ -2960,8 +3008,18 @@ walk_type (type_p t, struct walk_type_data *d)
 	    d->indent += 2;
 	    oprintf (d->of, "%*s{\n", d->indent, "");
 	  }
+	else if (desc)
+	  {
+	    oprintf (d->of, "%*sswitch (", d->indent, "");
+	    output_escaped_param (d, desc, "desc");
+	    oprintf (d->of, ")\n");
+	    d->indent += 2;
+	    oprintf (d->of, "%*s{\n", d->indent, "");
+	    oprintf (d->of, "%*scase %s:\n", d->indent, "", type_tag);
+	    d->indent += 2;
+	  }
 
-	for (f = t->u.s.fields; f; f = f->next)
+	FOR_ALL_INHERITED_FIELDS (t, f)
 	  {
 	    options_p oo;
 	    int skip_p = 0;
@@ -2999,7 +3057,7 @@ walk_type (type_p t, struct walk_type_data *d)
 	  }
 	endcounter = d->counter;
 
-	for (f = t->u.s.fields; f; f = f->next)
+	FOR_ALL_INHERITED_FIELDS (t, f)
 	  {
 	    options_p oo;
 	    const char *dot = ".";
@@ -3101,11 +3159,26 @@ walk_type (type_p t, struct walk_type_data *d)
 	    oprintf (d->of, "%*sdefault:\n", d->indent, "");
 	    oprintf (d->of, "%*s  break;\n", d->indent, "");
 	  }
+
+	if (desc && !union_p)
+	  {
+		oprintf (d->of, "%*sbreak;\n", d->indent, "");
+		d->indent -= 2;
+          }
 	if (union_p)
 	  {
 	    oprintf (d->of, "%*s}\n", d->indent, "");
 	    d->indent -= 2;
 	  }
+	else if (desc)
+	  {
+	    /* Add cases to handle subclasses.  */
+	    walk_subclasses (t, d);
+
+	    /* End of the switch statement */
+	    oprintf (d->of, "%*s}\n", d->indent, "");
+	    d->indent -= 2;
+	  }
 	if (any_length_seen)
 	  {
 	    d->indent -= 2;
@@ -3451,6 +3524,11 @@ write_func_for_structure (type_p orig_s, type_p s, type_p *param,
   options_p opt;
   struct walk_type_data d;
 
+  /* Don't write fns for subclasses, only for the ultimate base class
+     within an inheritance hierarchy.  */
+  if (s->u.s.base_class)
+    return;
+
   memset (&d, 0, sizeof (d));
   d.of = get_output_file_for_structure (s, param);
   for (opt = s->u.s.opt; opt; opt = opt->next)
@@ -3627,7 +3705,10 @@ write_types (outf_p output_header, type_p structures, type_p param_structs,
      emitted afterwards.  This is needed in plugin mode.  */
   oprintf (output_header, "/* Macros and declarations.  */\n");
   for (s = structures; s; s = s->next)
-    if (s->gc_used == GC_POINTED_TO || s->gc_used == GC_MAYBE_POINTED_TO)
+    /* Do not emit handlers for derived classes; we only ever deal with
+       the ultimate base class within an inheritance hierarchy.  */
+    if ((s->gc_used == GC_POINTED_TO || s->gc_used == GC_MAYBE_POINTED_TO)
+        && !s->u.s.base_class)
       {
 	options_p opt;
 
@@ -3932,6 +4013,11 @@ write_local_func_for_structure (const_type_p orig_s, type_p s, type_p *param)
 {
   struct walk_type_data d;
 
+  /* Don't write fns for subclasses, only for the ultimate base class
+     within an inheritance hierarchy.  */
+  if (s->u.s.base_class)
+    return;
+
   memset (&d, 0, sizeof (d));
   d.of = get_output_file_for_structure (s, param);
   d.process_field = write_types_local_process_field;
@@ -4076,7 +4162,9 @@ write_local (outf_p output_header, type_p structures, type_p param_structs)
 	   || ((s)->gc_used == GC_MAYBE_POINTED_TO			\
 	       && s->u.s.line.file != NULL)				\
 	   || ((s)->gc_used == GC_USED					\
-	       && strncmp (s->u.s.tag, "anonymous", strlen ("anonymous"))))))
+	       && strncmp (s->u.s.tag, "anonymous", strlen ("anonymous"))) \
+	   || (s->u.s.base_class && opts_have (s->u.s.opt, "tag")))))
+
 
 
 /* Might T contain any non-pointer elements?  */
@@ -4360,6 +4448,8 @@ write_root (outf_p f, pair_p v, type_p type, const char *name, int has_length,
 
 	if (!has_length && union_or_struct_p (tp))
 	  {
+	    while (tp->u.s.base_class)
+	      tp = tp->u.s.base_class;
 	    const char *id_for_tag = filter_type_name (tp->u.s.tag);
 	    oprintf (f, "    &gt_ggc_mx_%s,\n", id_for_tag);
 	    if (emit_pch)
diff --git a/gcc/gengtype.h b/gcc/gengtype.h
index 06ebbed..2d20bf9 100644
--- a/gcc/gengtype.h
+++ b/gcc/gengtype.h
@@ -506,4 +506,12 @@ void dbgprint_count_type_at (const char *, int, const char *, type_p);
 #define DBGPRINT_COUNT_TYPE(Msg,Ty) do{/*nodbgprint_count_type*/}while (0)
 #endif /*ENABLE_CHECKING */
 
+#define FOR_ALL_INHERITED_FIELDS(TYPE, FIELD_VAR) \
+  for (type_p sub = (TYPE); sub; sub = sub->u.s.base_class) \
+    for (FIELD_VAR = sub->u.s.fields; FIELD_VAR; FIELD_VAR = FIELD_VAR->next)
+
+extern bool
+opts_have (options_p opts, const char *str);
+
+
 #endif
