This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[incremental] Patch: FYI: re-use parsed contents


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

This is the bulk of the prototype I've been working on.  It is based
on --combine, because that was a convenient way to get multiple
translation units into a single cc1.

This prototype implements re-use of already parsed hunks of headers.
The basic idea is to checksum "hunks" of tokens (roughly speaking,
tokens appearing between file change events) and then reuse bindings
whenever we see a hunk that has already been parsed.

I'll put details about this approach and some of its oddities on the
wiki, along with information about future directions for this branch.
There are still a few oddities to solve (e.g., pragmas affecting
global state, some more decl smashing fixes, anti-dependencies,
... the list goes on).

In some simple (but real world) test cases the prototype compiler,
compared to trunk with --combine, runs in half the time and uses about
1/3 the memory.

Tom

Index: gcc/ChangeLog
from  Tom Tromey  <tromey@redhat.com>
	* c-common.c (fold_offsetof_1): Handle VIEW_CONVERT_EXPR.
	* c-decl.c (bind): Added notify_ok argument.  Call
	c_parser_bind_callback when needed.
	(c_decl_re_bind): New function.
	(push_file_scope): Update.
	(pushtag): Likewise.
	(decl_needs_merging): New function.
	(diagnose_mismatched_decls): Added 'need_mergep' argument.  Call
	decl_needs_merging.
	(duplicate_decls): Added 'binding' argument.  Call
	c_parser_note_smash.  Only merge decls when necessary.
	(pushdecl): Update.
	(pushdecl_top_level): Update.
	(implicitly_declare): Update.
	(undeclared_variable): Update.
	(lookup_label): Update.
	(declare_label): Update.
	(define_label): Update.
	(lookup_tag): Call c_parser_lookup_callback.
	(lookup_name): Likewise.
	(lookup_name_in_scope): Likewise.
	(c_make_fname_decl): Update.
	(c_builtin_function): Likewise.
	(start_struct): Call c_parser_note_smash when needed.
	(finish_struct): Only allocate TYPE_LANG_SPECIFIC if not already
	allocated.
	(start_enum): Rearrange.  call c_parser_note_smash when needed.
	(finish_enum): Only allocate TYPE_LANG_SPECIFIC is not already
	allocated.
	(store_parm_decls_newstyle): Update.
	* c-typeck.c (build_component_ref): Use smashed type variant.
	(build_indirect_ref): Likewise.
	(lvalue_p): Handle VIEW_CONVERT_EXPR.
	* c-tree.h (struct lang_decl) <declaring_hunk>: New field.
	(struct lang_type) <declaring_hunk>: Likewise.
	(c_parser_bind_callback, c_parser_note_smash,
	object_in_current_hunk_p, c_parser_find_binding,
	c_parser_lookup_callback, c_decl_re_bind): Declare.
	(C_SMASHED_VARIANT): New macro.
	(C_SMASHED_TYPE_VARIANT): Likewise.
	(C_SMASHED_P): Likewise.
	* c-lex.c (fe_file_change): Update file_change field.
	* c-common.h (struct c_lex_state) <file_change>: New field.
	Update lang-flag comment.
	* c-parser.c (parsed_hunk): New struct.
	(c_parser) <used_hunks, first_hunk, prev_hunk,
	current_hunk_binding, smash_map>: New fields.
	(struct hunk_binding): New struct.
	(hash_hunk_binding_entry): New function.
	(eq_hunk_binding_entry): Likewise.
	(traverse_hunk_binding_entry): Likewise.
	(struct hunk_binding): New struct.
	(hash_hunk_binding): New function.
	(eq_hunk_binding): Likewise.
	(global_hunk_map): New global.
	(c_parser_bind_callback): New function.
	(find_hunk_binding): Likewise.
	(c_parser_lookup_callback): Likewise.
	(object_in_current_hunk_p): Likewise.
	(start_new_parsed_hunk): Likewise.
	(finish_current_hunk): Likewise.
	(create_hunk_binding_map): Likewise.
	(struct smash_entry): New struct.
	(hash_smash_entry): New function.
	(eq_smash_entry): Likewise.
	(c_parser_find_binding): Likewise.
	(c_parser_note_smash): Likewise.
	(c_parser_lex_all): Keep track of hunks.  Checksum tokens while
	lexing.
	(struct prereq_traverse_data): New struct.
	(traverse_check_prereq): New function.
	(can_reuse_hunk): Likewise.
	(c_parser_translation_unit): Reuse hunks when possible.
	(c_parse_file): Initialize new fields in parser.  Call
	create_hunk_binding_map.

Index: gcc/c-lex.c
===================================================================
--- gcc/c-lex.c	(revision 127671)
+++ gcc/c-lex.c	(working copy)
@@ -218,6 +218,8 @@
 fe_file_change (struct c_lex_state * ARG_UNUSED (lstate),
 		const struct line_map *new_map)
 {
+  lstate->file_change = true;
+
   if (new_map == NULL)
     return;
 
Index: gcc/c-tree.h
===================================================================
--- gcc/c-tree.h	(revision 127650)
+++ gcc/c-tree.h	(working copy)
@@ -34,7 +34,8 @@
 
 struct lang_decl GTY(())
 {
-  char dummy;
+  /* The hunk in which this decl was declared.  */
+  struct hunk_binding *declaring_hunk;
 };
 
 /* In a RECORD_TYPE or UNION_TYPE, nonzero if any component is read-only.  */
@@ -67,6 +68,9 @@
      as a list of adopted protocols or a pointer to a corresponding
      @interface.  See objc/objc-act.h for details.  */
   tree objc_info;
+
+  /* The hunk in which this type was declared.  */
+  struct hunk_binding *declaring_hunk;
 };
 
 /* Record whether a type or decl was written with nonconstant size.
@@ -438,7 +442,25 @@
 
 /* in c-parser.c */
 extern void c_parse_init (void);
+extern void c_parser_bind_callback (tree, tree);
+extern void c_parser_note_smash (tree, tree);
+extern bool object_in_current_hunk_p (tree);
+extern tree c_parser_find_binding (tree);
+extern void c_parser_lookup_callback (tree);
 
+/* True if this decl or type has been smashed.  */
+#define C_SMASHED_P(T) TREE_LANG_FLAG_5 (T)
+
+/* Return the smashed decl or type corresponding to ARG.  If ARG is
+   not smashed, return ARG.  */
+#define C_SMASHED_VARIANT(ARG) \
+  (((ARG) && C_SMASHED_P (ARG)) ? c_parser_find_binding (ARG) : (ARG))
+
+/* Return the smashed variant of TYPE.  This will look up the
+   canonical type if it exists.  FIXME: better comment here.  */
+#define C_SMASHED_TYPE_VARIANT(TYPE) \
+  C_SMASHED_VARIANT ((TYPE_CANONICAL (TYPE)) ? (TYPE_CANONICAL (TYPE)) : (TYPE))
+
 /* in c-aux-info.c */
 extern void gen_aux_info_record (tree, int, int, int);
 
@@ -452,6 +474,8 @@
 extern tree pop_scope (void);
 extern void insert_block (tree);
 
+extern void c_decl_re_bind (tree, tree);
+
 extern void c_init_decl_processing (void);
 extern void c_dup_lang_specific_decl (tree);
 extern void c_print_identifier (FILE *, tree, int);
Index: gcc/c-decl.c
===================================================================
--- gcc/c-decl.c	(revision 127650)
+++ gcc/c-decl.c	(working copy)
@@ -457,7 +457,8 @@
    which may be any of several kinds of DECL or TYPE or error_mark_node,
    in the scope SCOPE.  */
 static void
-bind (tree name, tree decl, struct c_scope *scope, bool invisible, bool nested)
+bind (tree name, tree decl, struct c_scope *scope, bool invisible, bool nested,
+      bool notify_ok)
 {
   struct c_binding *b, **here;
 
@@ -485,6 +486,9 @@
   if (!name)
     return;
 
+  if (notify_ok && B_IN_FILE_SCOPE (scope))
+    c_parser_bind_callback (name, decl);
+
   switch (TREE_CODE (decl))
     {
     case LABEL_DECL:     here = &I_LABEL_BINDING (name);   break;
@@ -512,6 +516,16 @@
   *here = b;
 }
 
+/* Re-bind NAME to DECL in the file scope.  This is called when
+   mapping in bindings from a parsed hunk.  */
+void
+c_decl_re_bind (tree name, tree decl)
+{
+  /* FIXME: perhaps should be using pushdecl, etc, here -- do we want
+     to re-smash in this thread?  */
+  bind (name, decl, file_scope, false, false, false);
+}
+
 /* Clear the binding structure B, stick it on the binding_freelist,
    and return the former value of b->prev.  This is used by pop_scope
    and get_parm_info to iterate destructively over all the bindings
@@ -898,7 +912,7 @@
 
   for (decl = visible_builtins; decl; decl = TREE_CHAIN (decl))
     bind (DECL_NAME (decl), decl, file_scope,
-	  /*invisible=*/false, /*nested=*/true);
+	  /*invisible=*/false, /*nested=*/true, /*notify_ok=*/true);
 }
 
 void
@@ -954,7 +968,8 @@
   /* Record the identifier as the type's name if it has none.  */
   if (name && !TYPE_NAME (type))
     TYPE_NAME (type) = name;
-  bind (name, type, current_scope, /*invisible=*/false, /*nested=*/false);
+  bind (name, type, current_scope, /*invisible=*/false, /*nested=*/false,
+	/*notify_ok=*/true);
 
   /* Create a fake NULL-named TYPE_DECL node whose TREE_TYPE will be the
      tagged type we just added to the current scope.  This fake
@@ -1134,6 +1149,93 @@
     diag (G_("previous declaration of %q+D was here"), decl);
 }
 
+/* Subroutine of diagnose_mismatched_decls.  Return true if NEWDECL
+   and OLDDECL differ substantially, meaning in a way that merge_decls
+   would cause a modification.  */
+static bool
+decl_needs_merging (tree newdecl, tree olddecl, tree newtype, tree oldtype)
+{
+  tree merge;
+
+  merge = targetm.merge_decl_attributes (olddecl, newdecl);
+  if (merge != DECL_ATTRIBUTES (olddecl))
+    return true;
+  merge = composite_type (newtype, oldtype);
+  if (merge != oldtype)
+    return true;
+
+  if (TREE_READONLY (newdecl) != TREE_READONLY (olddecl))
+    return true;
+  if (TREE_THIS_VOLATILE (newdecl) != TREE_THIS_VOLATILE (olddecl))
+    return true;
+  if (TREE_DEPRECATED (newdecl) != TREE_DEPRECATED (olddecl))
+    return true;
+  if (DECL_INITIAL (newdecl) != DECL_INITIAL (olddecl))
+    return true;
+
+  if (CODE_CONTAINS_STRUCT (TREE_CODE (olddecl), TS_DECL_WITH_VIS))
+    {
+      /* FIXME: threadprivate attribute?  */
+      if (DECL_VISIBILITY_SPECIFIED (newdecl)
+	  != DECL_VISIBILITY_SPECIFIED (olddecl))
+	return true;
+      if (DECL_VISIBILITY_SPECIFIED (newdecl)
+	  && DECL_VISIBILITY (newdecl) != DECL_VISIBILITY (olddecl))
+	return true;
+
+      if (TREE_CODE (newdecl) == FUNCTION_DECL)
+	{
+	  if (DECL_STATIC_CONSTRUCTOR(newdecl)
+	      != DECL_STATIC_CONSTRUCTOR(olddecl))
+	    return true;
+	  if (DECL_STATIC_DESTRUCTOR (newdecl)
+	      != DECL_STATIC_DESTRUCTOR (olddecl))
+	    return true;
+	  if (DECL_NO_LIMIT_STACK (newdecl) != DECL_NO_LIMIT_STACK (olddecl))
+	    return true;
+	  if (DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl)
+	      != DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl))
+	    return true;
+	  if (TREE_THIS_VOLATILE (newdecl) != TREE_THIS_VOLATILE (olddecl))
+	    return true;
+	  if (TREE_READONLY (newdecl) != TREE_READONLY (olddecl))
+	    return true;
+	  if (DECL_IS_MALLOC (newdecl) != DECL_IS_MALLOC (olddecl))
+	    return true;
+	  if (DECL_IS_PURE (newdecl) != DECL_IS_PURE (olddecl))
+	    return true;
+	  if (DECL_IS_NOVOPS (newdecl) != DECL_IS_NOVOPS (olddecl))
+	    return true;
+
+	  if (TREE_PUBLIC (newdecl) != TREE_PUBLIC (olddecl))
+	    return true;
+	}
+
+      if (DECL_WEAK (newdecl) != DECL_WEAK (olddecl))
+	return true;
+    }
+
+  if (DECL_EXTERNAL (newdecl) != DECL_EXTERNAL (olddecl))
+    return true;
+  if (TREE_STATIC (newdecl) != TREE_STATIC (olddecl))
+    return true;
+  if (TREE_PUBLIC (newdecl) != TREE_PUBLIC (olddecl))
+    return true;
+
+  if (TREE_CODE (newdecl) == FUNCTION_DECL)
+    {
+      if (DECL_DECLARED_INLINE_P (newdecl) != DECL_DECLARED_INLINE_P (olddecl))
+	return true;
+      if (DECL_UNINLINABLE (newdecl) != DECL_UNINLINABLE (olddecl))
+	return true;
+      /* Always merge built-ins.  Bleah?  */
+      if (DECL_BUILT_IN (olddecl))
+	return true;
+    }
+
+  return false;
+}
+
 /* Subroutine of duplicate_decls.  Compare NEWDECL to OLDDECL.
    Returns true if the caller should proceed to merge the two, false
    if OLDDECL should simply be discarded.  As a side effect, issues
@@ -1144,7 +1246,8 @@
 
 static bool
 diagnose_mismatched_decls (tree newdecl, tree olddecl,
-			   tree *newtypep, tree *oldtypep)
+			   tree *newtypep, tree *oldtypep,
+			   bool *need_mergep)
 {
   tree newtype, oldtype;
   bool pedwarned = false;
@@ -1592,6 +1695,8 @@
       warned = true;
     }
 
+  *need_mergep = decl_needs_merging (newdecl, olddecl, *newtypep, *oldtypep);
+
   /* Report location of previous decl/defn in a consistent manner.  */
   if (warned || pedwarned)
     locate_old_decl (olddecl, pedwarned ? pedwarn : warning0);
@@ -1924,18 +2029,36 @@
    true.  Otherwise, return false.  */
 
 static bool
-duplicate_decls (tree newdecl, tree olddecl)
+duplicate_decls (tree newdecl, tree olddecl, struct c_binding *binding)
 {
   tree newtype = NULL, oldtype = NULL;
+  bool need_merge = false;
 
-  if (!diagnose_mismatched_decls (newdecl, olddecl, &newtype, &oldtype))
+  if (!diagnose_mismatched_decls (newdecl, olddecl, &newtype, &oldtype,
+				  &need_merge))
     {
       /* Avoid `unused variable' and other warnings warnings for OLDDECL.  */
       TREE_NO_WARNING (olddecl) = 1;
       return false;
     }
 
-  merge_decls (newdecl, olddecl, newtype, oldtype);
+  if (need_merge)
+    {
+      if (B_IN_FILE_SCOPE (binding) && !object_in_current_hunk_p (olddecl))
+	{
+	  /* Modify a copy of OLDDECL and install that in the
+	     bindings.  */
+	  /* FIXME: we shouldn't need a copy -- we should just modify
+	     NEWDECL.  */
+	  tree copy = copy_node (olddecl);
+	  merge_decls (newdecl, copy, newtype, oldtype);
+	  gcc_assert (binding->decl == olddecl);
+	  binding->decl = copy;
+	  c_parser_note_smash (olddecl, copy);
+	}
+      else
+	merge_decls (newdecl, olddecl, newtype, oldtype);
+    }
   return true;
 }
 
@@ -2091,7 +2214,8 @@
   /* Anonymous decls are just inserted in the scope.  */
   if (!name)
     {
-      bind (name, x, scope, /*invisible=*/false, /*nested=*/false);
+      bind (name, x, scope, /*invisible=*/false, /*nested=*/false,
+	    /*notify_ok=*/true);
       return x;
     }
 
@@ -2129,7 +2253,7 @@
 		TREE_TYPE (b_use->decl) = b_use->type;
 	    }
 	}
-      if (duplicate_decls (x, b_use->decl))
+      if (duplicate_decls (x, b_use->decl, b))
 	{
 	  if (b_use != b)
 	    {
@@ -2233,7 +2357,7 @@
 	 the static does not go in the externals scope.  */
       if (b
 	  && (TREE_PUBLIC (x) || same_translation_unit_p (x, b->decl))
-	  && duplicate_decls (x, b->decl))
+	  && duplicate_decls (x, b->decl, b))
 	{
 	  tree thistype;
 	  if (vistype)
@@ -2251,12 +2375,13 @@
 	      = build_type_attribute_variant (thistype,
 					      TYPE_ATTRIBUTES (b->type));
 	  TREE_TYPE (b->decl) = thistype;
-	  bind (name, b->decl, scope, /*invisible=*/false, /*nested=*/true);
+	  bind (name, b->decl, scope, /*invisible=*/false, /*nested=*/true,
+		/*notify_ok=*/true);
 	  return b->decl;
 	}
       else if (TREE_PUBLIC (x))
 	{
-	  if (visdecl && !b && duplicate_decls (x, visdecl))
+	  if (visdecl && !b && duplicate_decls (x, visdecl, b))
 	    {
 	      /* An external declaration at block scope referring to a
 		 visible entity with internal linkage.  The composite
@@ -2269,7 +2394,7 @@
 	  else
 	    {
 	      bind (name, x, external_scope, /*invisible=*/true,
-		    /*nested=*/false);
+		    /*nested=*/false, /*notify_ok=*/true);
 	      nested = true;
 	    }
 	}
@@ -2282,7 +2407,7 @@
   if (TREE_CODE (x) == TYPE_DECL)
     clone_underlying_type (x);
 
-  bind (name, x, scope, /*invisible=*/false, nested);
+  bind (name, x, scope, /*invisible=*/false, nested, /*notify_ok=*/true);
 
   /* If x's type is incomplete because it's based on a
      structure or union which has not yet been fully declared,
@@ -2331,11 +2456,12 @@
 
   if (TREE_PUBLIC (x))
     {
-      bind (name, x, external_scope, /*invisible=*/true, /*nested=*/false);
+      bind (name, x, external_scope, /*invisible=*/true, /*nested=*/false,
+	    /*notify_ok=*/true);
       nested = true;
     }
   if (file_scope)
-    bind (name, x, file_scope, /*invisible=*/false, nested);
+    bind (name, x, file_scope, /*invisible=*/false, nested, /*notify_ok=*/true);
 
   return x;
 }
@@ -2387,7 +2513,7 @@
       if (!DECL_BUILT_IN (decl) && DECL_IS_BUILTIN (decl))
 	{
 	  bind (functionid, decl, file_scope,
-		/*invisible=*/false, /*nested=*/true);
+		/*invisible=*/false, /*nested=*/true, /*notify_ok=*/true);
 	  return decl;
 	}
       else
@@ -2428,7 +2554,7 @@
 	  b->type = TREE_TYPE (decl);
 	  TREE_TYPE (decl) = newtype;
 	  bind (functionid, decl, current_scope,
-		/*invisible=*/false, /*nested=*/true);
+		/*invisible=*/false, /*nested=*/true, /*notify_ok=*/true);
 	  return decl;
 	}
     }
@@ -2491,7 +2617,8 @@
 	 will be nonnull but current_function_scope will be null.  */
       scope = current_function_scope ? current_function_scope : current_scope;
     }
-  bind (id, error_mark_node, scope, /*invisible=*/false, /*nested=*/false);
+  bind (id, error_mark_node, scope, /*invisible=*/false, /*nested=*/false,
+	/*notify_ok=*/true);
 }
 
 /* Subroutine of lookup_label, declare_label, define_label: construct a
@@ -2545,7 +2672,7 @@
 
   /* Ordinary labels go in the current function scope.  */
   bind (name, label, current_function_scope,
-	/*invisible=*/false, /*nested=*/false);
+	/*invisible=*/false, /*nested=*/false, /*notify_ok=*/true);
   return label;
 }
 
@@ -2575,7 +2702,7 @@
 
   /* Declared labels go in the current scope.  */
   bind (name, label, current_scope,
-	/*invisible=*/false, /*nested=*/false);
+	/*invisible=*/false, /*nested=*/false, /*notify_ok=*/true);
   return label;
 }
 
@@ -2622,7 +2749,7 @@
 
       /* Ordinary labels go in the current function scope.  */
       bind (name, label, current_function_scope,
-	    /*invisible=*/false, /*nested=*/false);
+	    /*invisible=*/false, /*nested=*/false, /*notify_ok=*/true);
     }
 
   if (!in_system_header && lookup_name (name))
@@ -2689,6 +2816,10 @@
       if (thislevel)
 	pending_xref_error ();
     }
+
+  if (B_IN_FILE_SCOPE (b))
+    c_parser_lookup_callback (b->decl);
+
   return b->decl;
 }
 
@@ -2717,7 +2848,11 @@
 {
   struct c_binding *b = I_SYMBOL_BINDING (name);
   if (b && !b->invisible)
-    return b->decl;
+    {
+      if (B_IN_FILE_SCOPE (b))
+	c_parser_lookup_callback (b->decl);
+      return b->decl;
+    }
   return 0;
 }
 
@@ -2730,7 +2865,11 @@
 
   for (b = I_SYMBOL_BINDING (name); b; b = b->shadowed)
     if (B_IN_SCOPE (b, scope))
-      return b->decl;
+      {
+	if (B_IN_FILE_SCOPE (b))
+	  c_parser_lookup_callback (b->decl);
+	return b->decl;
+      }
   return 0;
 }
 
@@ -2830,7 +2969,7 @@
     {
       DECL_CONTEXT (decl) = current_function_decl;
       bind (id, decl, current_function_scope,
-	    /*invisible=*/false, /*nested=*/false);
+	    /*invisible=*/false, /*nested=*/false, /*notify_ok=*/true);
     }
 
   finish_decl (decl, init, NULL_TREE);
@@ -2851,7 +2990,8 @@
   /* Should never be called on a symbol with a preexisting meaning.  */
   gcc_assert (!I_SYMBOL_BINDING (id));
 
-  bind (id, decl, external_scope, /*invisible=*/true, /*nested=*/false);
+  bind (id, decl, external_scope, /*invisible=*/true, /*nested=*/false,
+	/*notify_ok=*/true);
 
   /* Builtins in the implementation namespace are made visible without
      needing to be explicitly declared.  See push_file_scope.  */
@@ -5373,10 +5513,14 @@
 
   /* Otherwise create a forward-reference just so the tag is in scope.  */
 
-  if (ref == NULL_TREE || TREE_CODE (ref) != code)
+  if (ref == NULL_TREE || (TREE_CODE (ref) != code
+			   && !object_in_current_hunk_p (ref)))
     {
-      ref = make_node (code);
-      pushtag (name, ref);
+      tree newval = make_node (code);
+      pushtag (name, newval);
+      if (ref)
+	c_parser_note_smash (ref, newval);
+      ref = newval;
     }
 
   C_TYPE_BEING_DEFINED (ref) = 1;
@@ -5696,7 +5840,10 @@
 	  ensure that this lives as long as the rest of the struct decl.
 	  All decls in an inline function need to be saved.  */
 
-	space = GGC_CNEW (struct lang_type);
+	if (TYPE_LANG_SPECIFIC (t))
+	  space = TYPE_LANG_SPECIFIC (t);
+	else
+	  space = GGC_CNEW (struct lang_type);
 	space2 = GGC_NEWVAR (struct sorted_fields_type,
 			     sizeof (struct sorted_fields_type) + len * sizeof (tree));
 
@@ -5802,27 +5949,34 @@
   if (name != 0)
     enumtype = lookup_tag (ENUMERAL_TYPE, name, 1);
 
-  if (enumtype == 0 || TREE_CODE (enumtype) != ENUMERAL_TYPE)
+  if (enumtype && TREE_CODE (enumtype) == ENUMERAL_TYPE)
     {
-      enumtype = make_node (ENUMERAL_TYPE);
-      pushtag (name, enumtype);
-    }
+      if (C_TYPE_BEING_DEFINED (enumtype))
+	error ("nested redefinition of %<enum %E%>", name);
 
-  if (C_TYPE_BEING_DEFINED (enumtype))
-    error ("nested redefinition of %<enum %E%>", name);
+      if (TYPE_VALUES (enumtype) != 0)
+	{
+	  /* This enum is a named one that has been declared already.  */
+	  error ("redeclaration of %<enum %E%>", name);
 
-  C_TYPE_BEING_DEFINED (enumtype) = 1;
+	  /* Completely replace its old definition.
+	     The old enumerators remain defined, however.  */
+	  TYPE_VALUES (enumtype) = 0;
+	}
+    }
 
-  if (TYPE_VALUES (enumtype) != 0)
+  if (enumtype == 0 || (TREE_CODE (enumtype) != ENUMERAL_TYPE
+			&& !object_in_current_hunk_p (enumtype)))
     {
-      /* This enum is a named one that has been declared already.  */
-      error ("redeclaration of %<enum %E%>", name);
-
-      /* Completely replace its old definition.
-	 The old enumerators remain defined, however.  */
-      TYPE_VALUES (enumtype) = 0;
+      tree newval = make_node (ENUMERAL_TYPE);
+      pushtag (name, newval);
+      if (enumtype)
+	c_parser_note_smash (enumtype, newval);
+      enumtype = newval;
     }
 
+  C_TYPE_BEING_DEFINED (enumtype) = 1;
+
   the_enum->enum_next_value = integer_zero_node;
   the_enum->enum_overflow = 0;
 
@@ -5942,7 +6096,10 @@
 
   /* Record the min/max values so that we can warn about bit-field
      enumerations that are too small for the values.  */
-  lt = GGC_CNEW (struct lang_type);
+  if (TYPE_LANG_SPECIFIC (enumtype))
+    lt = TYPE_LANG_SPECIFIC (enumtype);
+  else
+    lt = GGC_CNEW (struct lang_type);
   lt->enum_min = minnode;
   lt->enum_max = maxnode;
   TYPE_LANG_SPECIFIC (enumtype) = lt;
@@ -6314,7 +6471,7 @@
       if (DECL_NAME (decl))
 	{
 	  bind (DECL_NAME (decl), decl, current_scope,
-		/*invisible=*/false, /*nested=*/false);
+		/*invisible=*/false, /*nested=*/false, /*notify_ok=*/true);
 	  if (!TREE_USED (decl))
 	    warn_if_shadowing (decl);
 	}
@@ -6331,14 +6488,14 @@
       DECL_CONTEXT (decl) = current_function_decl;
       if (DECL_NAME (decl))
 	bind (DECL_NAME (decl), decl, current_scope,
-	      /*invisible=*/false, /*nested=*/false);
+	      /*invisible=*/false, /*nested=*/false, /*notify_ok=*/true);
     }
 
   /* And all the tag declarations.  */
   for (decl = arg_info->tags; decl; decl = TREE_CHAIN (decl))
     if (TREE_PURPOSE (decl))
       bind (TREE_PURPOSE (decl), TREE_VALUE (decl), current_scope,
-	    /*invisible=*/false, /*nested=*/false);
+	    /*invisible=*/false, /*nested=*/false, /*notify_ok=*/true);
 }
 
 /* Subroutine of store_parm_decls which handles old-style function
Index: gcc/c-typeck.c
===================================================================
--- gcc/c-typeck.c	(revision 127650)
+++ gcc/c-typeck.c	(working copy)
@@ -1795,11 +1795,20 @@
 tree
 build_component_ref (tree datum, tree component)
 {
-  tree type = TREE_TYPE (datum);
-  enum tree_code code = TREE_CODE (type);
+  tree orig_type = TREE_TYPE (datum);
+  enum tree_code code = TREE_CODE (orig_type);
   tree field = NULL;
-  tree ref;
+  tree ref, type;
 
+  if (orig_type == error_mark_node)
+    type = orig_type;
+  else
+    {
+      type = C_SMASHED_TYPE_VARIANT (orig_type);
+      if (type != orig_type)
+	datum = build1 (VIEW_CONVERT_EXPR, type, datum);
+    }
+
   if (!objc_is_public (datum, component))
     return error_mark_node;
 
@@ -1836,7 +1845,7 @@
 	    return error_mark_node;
 
 	  quals = TYPE_QUALS (strip_array_types (TREE_TYPE (subdatum)));
-	  quals |= TYPE_QUALS (TREE_TYPE (datum));
+	  quals |= TYPE_QUALS (type);
 	  subtype = c_build_qualified_type (TREE_TYPE (subdatum), quals);
 
 	  ref = build3 (COMPONENT_REF, subtype, datum, subdatum,
@@ -1850,6 +1859,7 @@
 	    warn_deprecated_use (subdatum);
 
 	  datum = ref;
+	  type = TREE_TYPE (datum);
 
 	  field = TREE_CHAIN (field);
 	}
@@ -1896,10 +1906,17 @@
       else
 	{
 	  tree t = TREE_TYPE (type);
-	  tree ref;
+	  tree ref, t2;
 
 	  ref = build1 (INDIRECT_REF, t, pointer);
 
+	  t2 = C_SMASHED_TYPE_VARIANT (t);
+	  if (t != t2)
+	    {
+	      ref = build1 (VIEW_CONVERT_EXPR, t2, ref);
+	      t = t2;
+	    }
+
 	  if (!COMPLETE_OR_VOID_TYPE_P (t) && TREE_CODE (t) != ARRAY_TYPE)
 	    {
 	      error ("dereferencing pointer to incomplete type");
@@ -3101,6 +3118,7 @@
     case REALPART_EXPR:
     case IMAGPART_EXPR:
     case COMPONENT_REF:
+    case VIEW_CONVERT_EXPR:
       return lvalue_p (TREE_OPERAND (ref, 0));
 
     case COMPOUND_LITERAL_EXPR:
Index: gcc/c-common.c
===================================================================
--- gcc/c-common.c	(revision 127651)
+++ gcc/c-common.c	(working copy)
@@ -6559,6 +6559,7 @@
       return size_zero_node;
 
     case NOP_EXPR:
+    case VIEW_CONVERT_EXPR:
     case INDIRECT_REF:
       base = fold_offsetof_1 (TREE_OPERAND (expr, 0), stop_ref);
       gcc_assert (base == error_mark_node || base == size_zero_node);
Index: gcc/c-common.h
===================================================================
--- gcc/c-common.h	(revision 127671)
+++ gcc/c-common.h	(working copy)
@@ -34,6 +34,7 @@
    2: unused
    3: STATEMENT_LIST_HAS_LABEL (in STATEMENT_LIST)
    4: unused
+   5: C_SMASHED_P (in _TYPE or _DECL)
 */
 
 /* Reserved identifiers.  This is the union of all the keywords for C,
@@ -848,6 +849,10 @@
 
   /* Depth in C headers - C++ only.  */
   int c_header_level;
+
+  /* True if a file change event happened before the token returned by
+     the lexer.  */
+  bool file_change;
 };
 
 /* Return the lexer state associated with a cppreader.  */
Index: gcc/c-parser.c
===================================================================
--- gcc/c-parser.c	(revision 127653)
+++ gcc/c-parser.c	(working copy)
@@ -56,6 +56,7 @@
 #include "vec.h"
 #include "target.h"
 #include "cgraph.h"
+#include "md5.h"
 
 
 /* The reserved keyword table.  */
@@ -261,6 +262,24 @@
   location_t location;
 } c_token;
 
+/* This represents a sequence of tokens between two file change
+   events.  */
+struct parsed_hunk GTY ((chain_next ("%h.next")))
+{
+  /* Signature of the hunk.  */
+  unsigned char key[16];
+
+  /* Index of token starting this hunk.  We use indices here because
+     the lexer may need to realloc the token buffer.  */
+  size_t start_token;
+
+  /* Index of last token in this hunk.  */
+  size_t next_token;
+
+  /* Next in list.  */
+  struct parsed_hunk *next;
+};
+
 /* A parser structure recording information about the state and
    context of parsing.  Includes lexer information with up to two
    tokens of look-ahead; more are not needed for C.  */
@@ -275,6 +294,24 @@
   /* Number of tokens we've already hacked.  */
   short tokens_avail;
 
+  /* The hunks whose contents we've mapped in during this parse.  This
+     is used for prerequisite processing.  */
+  htab_t GTY ((param_is (struct hunk_binding))) used_hunks;
+
+  /* The first hunk on the list of hunks.  */
+  struct parsed_hunk *first_hunk;
+
+  /* The previous first hunk.  Even though it is only used in one
+     function, this has to be in the parser structure so that it is
+     seen by the GC.  */
+  struct parsed_hunk *prev_hunk;
+
+  /* The current hunk binding.  */
+  struct hunk_binding *current_hunk_binding;
+
+  /* Map decls and types onto their smashed variants.  */
+  htab_t GTY ((param_is (struct smash_entry))) smash_map;
+
   /* True if a syntax error is being recovered from; false otherwise.
      c_parser_error sets this flag.  It should clear this flag when
      enough tokens have been consumed to recover from the error.  */
@@ -424,6 +461,356 @@
   timevar_pop (TV_LEX);
 }
 
+/* Map an identifier to a binding pair.  */
+struct hunk_binding_entry GTY (())
+{
+  /* The identifier.  */
+  tree name;
+  /* The enum/union/struct tag binding.  */
+  tree x1;
+  /* The variable/function/etc binding.  */
+  tree x2;
+};
+
+/* Hash function for a struct hunk_binding_entry.   */
+static hashval_t
+hash_hunk_binding_entry (const void *hbe)
+{
+  const struct hunk_binding_entry *binding
+    = (const struct hunk_binding_entry *) hbe;
+  return IDENTIFIER_HASH_VALUE (binding->name);
+}
+
+/* Equality function for a struct hunk_binding_entry.  */
+static int
+eq_hunk_binding_entry (const void *a, const void *b)
+{
+  const struct hunk_binding_entry *hbe_a
+    = (const struct hunk_binding_entry *) a;
+  const struct hunk_binding_entry *hbe_b
+    = (const struct hunk_binding_entry *) b;
+  /* Identifiers are still interned.  */
+  return hbe_a->name == hbe_b->name;
+}
+
+/* Called to map the contents of a struct hunk_binding_entry into the
+   current symbol table.  */
+static int
+traverse_hunk_binding_entry (void **slot, void *ignore ATTRIBUTE_UNUSED)
+{
+  struct hunk_binding_entry *hb = (struct hunk_binding_entry *) *slot;
+
+  if (hb->x1 != NULL_TREE)
+    c_decl_re_bind (hb->name, hb->x1);
+  if (hb->x2 != NULL_TREE)
+    c_decl_re_bind (hb->name, hb->x2);
+
+  return 1;
+}
+
+/* A hunk binding holds information about a parsed hunk.  It uses the
+   hunk's signature as a key, and contains information about the
+   hunk's various preconditions.  It also holds the parsed contents of
+   the hunk, in the form of a map from identifier names to values.  */
+struct hunk_binding GTY ((chain_next ("%h.next")))
+{
+  /* Signature of the hunk.  */
+  unsigned char key[16];
+  /* If not NULL, this is a multi-hunk binding.  Hunks listed here
+     must appear in order following the initial hunk.  All bindings
+     are registered in this binding; so we just re-use the already
+     created parsed hunks.  */
+  struct parsed_hunk *multi_list;
+  /* Location of the first token in this hunk.  */
+  location_t location;
+  /* Map names to bindings.  */
+  htab_t GTY ((param_is (struct hunk_binding_entry))) binding_map;
+  /* Set of all prerequisite hunks.  */
+  htab_t GTY ((param_is (struct hunk_binding))) prereqs;
+  /* Hunk bindings are kept on a linked list.  Each element in the
+     list has a different set of prerequisites.  */
+  struct hunk_binding *next;
+};
+
+/* Hash function for a struct hunk_binding.  */
+static hashval_t
+hash_hunk_binding (const void *hb)
+{
+  const struct hunk_binding *hunk = (const struct hunk_binding *) hb;
+  return iterative_hash (&hunk->key[0], 16, 0);
+}
+
+/* Equality function for a struct hunk_binding.  */
+static int
+eq_hunk_binding (const void *a, const void *b)
+{
+  const struct hunk_binding *hb_a = (const struct hunk_binding *) a;
+  const struct hunk_binding *hb_b = (const struct hunk_binding *) b;
+  return !memcmp (hb_a->key, hb_b->key, 16);
+}
+
+/* Map a hunk signature to its bindings.  This is a true global,
+   shared by all parsers.  */
+static GTY ((param_is (struct hunk_binding))) htab_t global_hunk_map;
+
+/* This is called when making a file-scope binding.  It registers the
+   new binding in the current hunk binding map.  */
+void
+c_parser_bind_callback (tree name, tree decl)
+{
+  struct hunk_binding_entry **slot;
+  struct hunk_binding_entry key;
+  /* We'd like this to be an argument to this function, but that
+     requires many changes.  */
+  c_parser *parser = the_parser;
+
+  if (!parser || !parser->current_hunk_binding)
+    return;
+
+  key.name = name;
+  slot = (struct hunk_binding_entry **)
+    htab_find_slot (parser->current_hunk_binding->binding_map, &key,
+		    INSERT);
+  if (!*slot)
+    {
+      *slot = GGC_NEW (struct hunk_binding_entry);
+      (*slot)->name = name;
+      (*slot)->x1 = NULL_TREE;
+      (*slot)->x2 = NULL_TREE;
+    }
+
+  if (TREE_CODE_CLASS (TREE_CODE (decl)) == tcc_declaration)
+    {
+      /* FIXME: probably should never be null here -- no
+	 re-registration allowed... ? */
+      if (DECL_LANG_SPECIFIC (decl) == NULL)
+	{
+	  DECL_LANG_SPECIFIC (decl)
+	    = ggc_alloc_cleared (sizeof (struct lang_decl));
+	  DECL_LANG_SPECIFIC (decl)->declaring_hunk
+	    = parser->current_hunk_binding;
+	}
+    }
+  else
+    {
+      gcc_assert (TREE_CODE_CLASS (TREE_CODE (decl)) == tcc_type);
+      if (! TYPE_LANG_SPECIFIC (decl))
+	TYPE_LANG_SPECIFIC (decl) = GGC_CNEW (struct lang_type);
+      if (! TYPE_LANG_SPECIFIC (decl)->declaring_hunk)
+	TYPE_LANG_SPECIFIC (decl)->declaring_hunk
+	  = parser->current_hunk_binding;
+    }
+
+  switch (TREE_CODE (decl))
+    {
+    case ENUMERAL_TYPE:
+    case UNION_TYPE:
+    case RECORD_TYPE:
+      (*slot)->x1 = decl;
+      break;
+
+    case VAR_DECL:
+    case FUNCTION_DECL:
+    case TYPE_DECL:
+    case CONST_DECL:
+    case PARM_DECL:
+    case ERROR_MARK:
+      (*slot)->x2 = decl;
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Return the hunk in which OBJ was defined.  If no such hunk can be
+   found, return NULL.  */
+static struct hunk_binding *
+find_hunk_binding (tree obj)
+{
+  struct hunk_binding *binding = NULL;
+  if (TREE_CODE_CLASS (TREE_CODE (obj)) == tcc_declaration)
+    {
+      /* FIXME: probably should never be null here -- no
+	 re-registration allowed... ? */
+      if (DECL_LANG_SPECIFIC (obj) != NULL)
+	binding = DECL_LANG_SPECIFIC (obj)->declaring_hunk;
+    }
+  else
+    {
+      gcc_assert (TREE_CODE_CLASS (TREE_CODE (obj)) == tcc_type);
+      if (TYPE_LANG_SPECIFIC (obj)
+	  && TYPE_LANG_SPECIFIC (obj)->declaring_hunk)
+	binding = TYPE_LANG_SPECIFIC (obj)->declaring_hunk;
+    }
+  return binding;
+}
+
+/* This is called whenever a name lookup succeeds a file scope.  It
+   registers a prerequisite for the resulting object's hunk with the
+   hunk currently being parsed.  */
+void
+c_parser_lookup_callback (tree result)
+{
+  struct hunk_binding *binding;
+  /* We'd like this to be an argument to this function, but that
+     requires many changes.  */
+  c_parser *parser = the_parser;
+
+  if (!parser || !parser->current_hunk_binding)
+    return;
+  binding = find_hunk_binding (result);
+
+  /* We might be using something declared in the current hunk -- don't
+     register that fact.  */
+  if (binding && binding != parser->current_hunk_binding)
+    {
+      htab_t prereqs = parser->current_hunk_binding->prereqs;
+      struct hunk_binding **slot
+	= (struct hunk_binding **) htab_find_slot (prereqs, binding, INSERT);
+      /* FIXME: assert that slot is empty? */
+      *slot = binding;
+    }
+}
+
+/* Return true if the object OBJ was declared in the current hunk.  */
+bool
+object_in_current_hunk_p (tree obj)
+{
+  struct hunk_binding *binding;
+  /* We'd like this to be an argument to this function, but that
+     requires many changes.  */
+  c_parser *parser = the_parser;
+
+  if (!parser || !parser->current_hunk_binding)
+    return false;
+  binding = find_hunk_binding (obj);
+
+  return binding == parser->current_hunk_binding;
+}
+
+/* Called when we start parsing a new hunk to initialize the hunk
+   binding.  */
+static void
+start_new_parsed_hunk (c_parser *parser, unsigned char new_signature[])
+{
+  parser->current_hunk_binding = GGC_NEW (struct hunk_binding);
+  memcpy (&parser->current_hunk_binding->key[0],
+	  new_signature, 16);
+  parser->current_hunk_binding->multi_list = NULL;
+  parser->current_hunk_binding->binding_map
+    = htab_create_ggc (20, hash_hunk_binding_entry, eq_hunk_binding_entry,
+		       NULL);
+  parser->current_hunk_binding->prereqs = htab_create_ggc (20,
+							   htab_hash_pointer,
+							   htab_eq_pointer,
+							   NULL);
+  parser->current_hunk_binding->location
+    = parser->buffer[parser->prev_hunk->start_token].location;
+  parser->current_hunk_binding->next = NULL;
+}
+
+/* Called when finished parsing a hunk.  Registers the parsed hunk
+   binding in the global hunk binding map.  */
+static void
+finish_current_hunk (c_parser *parser,
+		     struct parsed_hunk *initial,
+		     struct parsed_hunk *last_used)
+{
+  struct hunk_binding **slot;
+  slot = (struct hunk_binding **)
+    htab_find_slot (global_hunk_map,
+		    parser->current_hunk_binding,
+		    INSERT);
+  if (initial != last_used)
+    {
+      last_used->next = NULL;
+      parser->current_hunk_binding->multi_list = initial->next;
+    }
+
+  /* Chain.  */
+  parser->current_hunk_binding->next = *slot;
+  *slot = parser->current_hunk_binding;
+}
+
+/* Initialize the global hunk binding map.  */
+static void
+create_hunk_binding_map (void)
+{
+  if (!global_hunk_map)
+    global_hunk_map = htab_create_ggc (20, hash_hunk_binding,
+				       eq_hunk_binding, NULL);
+}
+
+
+
+/* Map an object (a type or a decl) to a smashed copy.  */
+struct smash_entry GTY (())
+{
+  /* The original object.  */
+  tree key;
+  /* The smashed variant.  */
+  tree value;
+};
+
+/* Hash function for a struct smash_entry.  */
+static hashval_t
+hash_smash_entry (const void *e)
+{
+  const struct smash_entry *se = (const struct smash_entry *) e;
+  return htab_hash_pointer (se->key);
+}
+
+/* Equality function for a struct smash_entry.  */
+static int
+eq_smash_entry (const void *e1, const void *e2)
+{
+  const struct smash_entry *se1 = (const struct smash_entry *) e1;
+  const struct smash_entry *se2 = (const struct smash_entry *) e2;
+  return se1->key == se2->key;
+}
+
+/* Given a decl (or a type), find its smashed variant.  If no smashed
+   variant is found, return the argument.  */
+tree
+c_parser_find_binding (tree decl)
+{
+  struct smash_entry **slot;
+  struct smash_entry temp;
+  /* We'd like this to be an argument to this function, but that
+     requires many changes.  */
+  c_parser *parser = the_parser;
+
+  temp.key = decl;
+  temp.value = NULL_TREE;
+  slot = (struct smash_entry **) htab_find_slot (parser->smash_map,
+						 &temp, NO_INSERT);
+  if (slot != NULL)
+    decl = (*slot)->value;
+  return decl;
+}
+
+/* This is a callback that is called when a decl or type is smashed.
+   It notes the relationship between the original decl (FROM) and the
+   smashed variant (TO).  */
+void
+c_parser_note_smash (tree from, tree to)
+{
+  struct smash_entry **slot;
+  struct smash_entry *entry = GGC_NEW (struct smash_entry);
+  /* We'd like this to be an argument to this function, but that
+     requires many changes.  */
+  c_parser *parser = the_parser;
+
+  entry->key = from;
+  entry->value = to;
+  slot = (struct smash_entry **) htab_find_slot (parser->smash_map,
+						 entry, INSERT);
+  gcc_assert (slot && !*slot);
+  *slot = entry;
+  C_SMASHED_P (from) = 1;
+}
+
 /* Lex the entire file into the parser's buffer.  */
 
 static void
@@ -431,13 +818,20 @@
 {
 #define C_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (c_token))
 
-  size_t pos = 0, alloc = C_LEXER_BUFFER_SIZE;
+  size_t pos = 0, alloc = C_LEXER_BUFFER_SIZE, hunk_start;
   c_token *buffer;
+  struct md5_ctx current_hash;
+  struct parsed_hunk **next_hunk_pointer;
+  struct c_lex_state *lstate = c_lex_get_state (parse_in);
 
   timevar_push (TV_LEX);
 
   buffer = GGC_CNEWVEC (c_token, alloc);
 
+  md5_init_ctx (&current_hash);
+  next_hunk_pointer = &parser->first_hunk;
+  hunk_start = 0;
+
   while (true)
     {
       if (pos >= alloc)
@@ -449,6 +843,58 @@
 	}
       c_lex_one_token (parser, &buffer[pos]);
 
+      /* If there was a file change event, it happened before this
+	 token.  */
+      if (lstate->file_change && hunk_start != (size_t) -1)
+	{
+	  struct parsed_hunk *hunk;
+
+	  hunk = GGC_NEW (struct parsed_hunk);
+	  md5_finish_ctx (&current_hash, &hunk->key[0]);
+
+	  hunk->start_token = hunk_start;
+	  hunk->next_token = pos;
+	  hunk->next = NULL;
+	  *next_hunk_pointer = hunk;
+	  next_hunk_pointer = &hunk->next;
+
+	  md5_init_ctx (&current_hash);
+	  hunk_start = (size_t) -1;
+	}
+      lstate->file_change = false;
+
+      if (hunk_start == (size_t) -1)
+	hunk_start = pos;
+
+      switch (buffer[pos].type)
+	{
+	case CPP_NAME:
+	  md5_process_bytes (IDENTIFIER_POINTER (buffer[pos].value),
+			     IDENTIFIER_LENGTH (buffer[pos].value),
+			     &current_hash);
+	  break;
+
+	case CPP_OBJC_STRING:
+	case CPP_WSTRING:
+	case CPP_STRING:
+	  md5_process_bytes (TREE_STRING_POINTER (buffer[pos].value),
+			     TREE_STRING_LENGTH (buffer[pos].value),
+			     &current_hash);
+	  break;
+
+	case CPP_NUMBER:
+	case CPP_AT_NAME:
+	case CPP_CHAR:
+	case CPP_WCHAR:
+	case CPP_PRAGMA:
+	  /* FIXME: cheap hack: do nothing*/
+	  break;
+
+	default:
+	  /*lalala*/
+	  break;
+	}
+
       ++pos;
       if (buffer[pos - 1].type == CPP_EOF)
 	break;
@@ -1086,6 +1532,90 @@
 static tree c_parser_objc_message_args (c_parser *);
 static tree c_parser_objc_keywordexpr (c_parser *);
 
+/* Helper state for checking a hunk's prerequisites.  */
+struct prereq_traverse_data
+{
+  /* The associated parser.  */
+  c_parser *parser;
+  /* True if all prerequisites are satisfied, false otherwise.  */
+  bool result;
+};
+
+/* Check a single prerequisite hunk.  */
+static int
+traverse_check_prereq (void **valp, void *ptd)
+{
+  struct prereq_traverse_data *data = (struct prereq_traverse_data *) ptd;
+  struct hunk_binding *binding = (struct hunk_binding *) *valp;
+  if (!htab_find (data->parser->used_hunks, binding))
+    {
+      data->result = false;
+      return false;
+    }
+  return true;
+}
+
+/* Check to see if the next hunk has already been parsed and is
+   available for reuse.  Map in the bindings and return true if the
+   hunk was reused.  Return false otherwise.  */
+static bool
+can_reuse_hunk (c_parser *parser)
+{
+  struct hunk_binding temp, *binding;
+  struct parsed_hunk *binding_iter, *self_iter;
+
+  /* FIXME: kinda gross.  */
+  memcpy (&temp.key[0], &parser->first_hunk->key[0], 16);
+  binding = (struct hunk_binding *) htab_find (global_hunk_map, &temp);
+
+  for (; binding; binding = binding->next)
+    {
+      struct prereq_traverse_data data;
+
+      /* Check prerequisites for this binding.  */
+      data.parser = parser;
+      data.result = true;
+      htab_traverse_noresize (binding->prereqs, traverse_check_prereq, &data);
+      if (!data.result)
+	continue;
+
+      /* If we have a multi-hunk binding, check to make sure the
+	 current stream has the correct contents.  */
+      binding_iter = binding->multi_list;
+      self_iter = parser->first_hunk->next;
+      while (binding_iter && self_iter
+	     && !memcmp (&binding_iter->key[0],
+			 &self_iter->key[0], 16))
+	{
+	  binding_iter = binding_iter->next;
+	  self_iter = self_iter->next;
+	}
+      if (binding_iter == NULL)
+	break;
+    }
+
+  if (binding == NULL)
+    return false;
+
+  /* Make a note saying that we have used this hunk.  We only need to
+     mark the first in a sequence.  */
+  {
+    struct hunk_binding **slot = (struct hunk_binding **)
+      htab_find_slot (parser->used_hunks, binding, INSERT);
+    *slot = binding;
+  }
+
+  /* Map in the bindings.  */
+  htab_traverse_noresize (binding->binding_map, traverse_hunk_binding_entry,
+			  NULL);
+  parser->first_hunk = self_iter;
+  parser->next_token = self_iter->start_token;
+  /* FIXME: ... hmm... */
+  parser->tokens_avail = 0;
+
+  return true;
+}
+
 /* Parse a translation unit (C90 6.7, C99 6.9).
 
    translation-unit:
@@ -1112,11 +1642,49 @@
   else
     {
       void *obstack_position = obstack_alloc (&parser_obstack, 0);
+      bool parsed_any = false;
+      parser->prev_hunk = parser->first_hunk;
       do
 	{
+	  c_token *next_token;
+	  struct parsed_hunk *last_used = NULL;
+
 	  ggc_collect ();
+
+	  next_token = c_parser_peek_token (parser);
+
+	  /* Skip all the hunks that we've parsed.  */
+	  while (parser->first_hunk
+		 && (size_t) (next_token - &parser->buffer[0]) >= parser->first_hunk->next_token)
+	    {
+	      last_used = parser->first_hunk;
+	      parser->first_hunk = parser->first_hunk->next;
+	    }
+
+	  if (parser->first_hunk
+	      && (size_t) (next_token - &parser->buffer[0]) == parser->first_hunk->start_token)
+	    {
+	      if (parser->prev_hunk && parsed_any)
+		{
+		  gcc_assert (last_used->next == parser->first_hunk);
+		  finish_current_hunk (parser, parser->prev_hunk, last_used);
+		  parsed_any = false;
+		}
+
+	      parser->prev_hunk = parser->first_hunk;
+
+	      /* This will map in the saved bindings as a side
+		 effect.  */
+	      if (can_reuse_hunk (parser))
+		continue;
+
+	      /* Couldn't reuse, so parse and save this hunk.  */
+	      start_new_parsed_hunk (parser, parser->first_hunk->key);
+	    }
+
 	  c_parser_external_declaration (parser);
 	  obstack_free (&parser_obstack, obstack_position);
+	  parsed_any = true;
 	}
       while (c_parser_next_token_is_not (parser, CPP_EOF));
     }
@@ -7888,6 +8456,14 @@
   the_parser = GGC_NEW (c_parser);
   *the_parser = tparser;
 
+  the_parser->used_hunks = htab_create_ggc (20, htab_hash_pointer,
+					    htab_eq_pointer, NULL);
+  the_parser->smash_map = htab_create_ggc (20, hash_smash_entry, eq_smash_entry,
+					   NULL);
+
+  /* FIXME: should be in ordinary module initialization.  */
+  create_hunk_binding_map ();
+
   c_parser_lex_all (the_parser);
   c_parser_translation_unit (the_parser);
   the_parser = NULL;


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]