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]

[LTO merge][14/15] cgraph/gimple/ipa changes


This patch adds various changes to the middle end.  The main one
is the merging of gimple types, needed when reading multiple CUs
from disk.  It's been reviewed by myself, Richi and Honza, but an
extra round won't hurt.

It has been tested on x86_64 together with the the other 14
patches in this merge.

See http://gcc.gnu.org/ml/gcc/2009-09/msg00578.html for a
description of the whole set.


Thanks.  Diego.

2009-09-28  Diego Novillo  <dnovillo@google.com>

	* c-common.c (set_builtin_user_assembler_name): Move ...
	* builtins.c (set_builtin_user_assembler_name): ... here.
	(is_builtin_name): Add comment
	(is_builtin_fn): New.
	* except.c (output_ttype): Only call
	lookup_type_for_runtime if TYPE is not a runtime type.

2009-09-28  Richard Guenther  <rguenther@suse.de>
	    Diego Novillo  <dnovillo@google.com>

	* gimple.c: Include target.h and alias.h.
	(gimple_types): Declare.
	(type_hash_cache): Declare.
	(gimple_alloc_stat): Make extern.
	(gimple_build_eh_must_not_throw): Call
	gimple_eh_must_not_throw_set_fndecl.
	(struct type_pair_d): Declare.
	(type_pair_t): Declare.
	(type_pair_hash): New.
	(type_pair_eq): New.
	(lookup_type_pair): New.
	(gimple_force_type_merge): New.
	(compare_type_names_p): New.
	(compare_field_offset): New.
	(gimple_types_compatible_p): New.
	(struct sccs): Declare.
	(next_dfs_num): Declare.
	(iterative_hash_gimple_type): New.
	(visit): New.
	(iterative_hash_type_name): New.
	(iterative_hash_gimple_type): New.
	(gimple_type_hash): New.
	(gimple_type_eq): New.
	(gimple_register_type): New.
	(print_gimple_types_stats): New.
	(gimple_signed_or_unsigned_type): New.
	(gimple_unsigned_type): New.
	(gimple_signed_type): New.
	(gimple_get_alias_set): New.
	(gimple_decl_printable_name): Do not use DMGL_TYPES.
	* gimple.h (gimple_alloc, gimple_alloc_stat): Declare.
	(gimple_force_type_merge): Declare.
	(gimple_types_compatible_p): Declare.
	(gimple_register_type): Declare.
	(print_gimple_types_stats): Declare.
	(gimple_unsigned_type): Declare.
	(gimple_signed_type): Declare.
	(gimple_get_alias_set): Declare.
	(gimple_eh_must_not_throw_set_fndecl): New.

2009-09-28  Rafael Avila de Espindola  <espindola@google.com>

	* ipa-cp.c (cgraph_gate_cp): Return false if LTRANS is
	running.

2009-09-28  Doug Kwan  <dougkwan@google.com>

	* ipa-inline.c (cgraph_mark_inline): Check
	edge->call_stmt_cannot_inline_p instead of calling
	gimple_call_cannot_inline_p.
	(cgraph_decide_inlining): Do nothing in WPA and LTRANS.
	(cgraph_gate_ipa_early_inlining): Return false if
	in_lto_p is set.
	(inline_generate_summary): Do nothing in LTRANS.
	* cgraph.c (initialize_inline_failed): Make sure
	e->call_stmt exists before calling
	gimple_call_cannot_inline_p.
	(cgraph_create_edge): Set edge->call_stmt_cannot_inline_p.
	(cgraph_clone_edge): Add argument STMT_UID.  Modify all
	callers.
	Update new_edge->lto_stmt_uid.
	* cgraphbuild.c (reset_inline_failed): New.

2009-09-28  Doug Kwan  <dougkwan@google.com>
	    Ollie Wild  <aaw@google.com>

	* ipa-prop.c (ipa_propagate_indirect_call_infos): Do
	nothing in WPA.

2009-09-28  Kenneth Zadeck <zadeck@naturalbridge.com>
	    Jan Hubicka  <jh@suse.cz>

	* ipa-pure-const.c: Include lto-streamer.h.
	(register_hooks): Factor out of ...
	(generate_summary): ... here.
	(pure_const_write_summary): New.
	(pure_const_read_summary): New.
	(pass_ipa_pure_const): Add pure_const_write_summary and
	pure_const_read_summary.
	* ipa-reference.c: Include lto-streamer.h.
	(add_new_function): New.
	(remove_node_data): New.
	(duplicate_node_data): New.
	(ipa_init): Guard against multiple calls.
	Move hook setup from analyze_function.
	(write_node_summary_p): New.
	(ipa_reference_write_summary): New.
	(ipa_reference_read_summary): New.
	(pass_ipa_reference): Add ipa_reference_write_summary and
	ipa_reference_read_summary.
	* cgraph.h (cgraph_local_info): Add field lto_file_data.
	(struct cgraph_edge): Add fields lto_stmt_uid and
	call_stmt_cannot_inline_p.
	(cgraph_optimize): Declare.
	(cgraph_decide_is_function_needed): Declare.
	(reset_inline_failed): Declare.
	(enum LTO_cgraph_tags): Declare.
	(LTO_cgraph_tag_names): Declare.
	(LCC_NOT_FOUND): Define.

2009-09-28  Rafael Espindola  <espindola@google.com>
	    Diego Novillo  <dnovillo@google.com>

	* cgraphunit.c (ipa_passes): Prevent lto1 from calling
	ipa_write_summaries.
	Call execute_ipa_summary_passes for all_regular_ipa_passes and
	all_lto_gen_passes.
	(cgraph_optimize): Make extern.


2009-09-28  Doug Kwan  <dougkwan@google.com>
	    Rafael Espindola  <espindola@google.com>
	    Jan Hubicka  <jh@suse.cz>
	    Diego Novillo  <dnovillo@google.com>
	    Kenneth Zadeck  <zadeck@naturalbridge.com>

	* passes.c (all_regular_ipa_passes): New.
	(all_ipa_passes): Rename to all_small_ipa_passes.
	(init_optimization_passes): Init all_regular_ipa_passes.
	* tree-pass.h (all_regular_ipa_passes): New.
	(all_ipa_passes): Rename to all_small_ipa_passes.
	* passes.c (all_lto_gen_passes): New.
	(init_optimization_passes): Initialize all_lto_gen_passes.
	(execute_ipa_summary_passes): Make non-static.
	(ipa_write_summaries_1): New.
	(ipa_write_summaries_2): New.
	(ipa_write_summaries): New.
	(ipa_write_summaries_of_cgraph_node_set): New.
	(ipa_read_summaries_1): New.
	(ipa_read_summaries): New.
	(execute_ipa_pass_list): Call cgraph_process_new_functions.
	(execute_regular_ipa_pass_list): Remove.
	(init_optimization_passes): Schedule
	pass_rebuild_cgraph_edges and pass_early_inline outside
	of pass_all_early_optimizations.  Document reason.
	(pass_ipa_lto_gimple_out, pass_ipa_lto_wpa_fixup,
	pass_ipa_lto_finish_out): New pass.
	(pass_ipa_summary_passes): Start and stop timers if the pass
	has them.
	(execute_all_ipa_transforms): New.
	(execute_one_pass): Don't call execute_one_ipa_transform_pass.
	(dump_properties, debug_properties): New.
	* tree-optimize.c (gate_all_early_local_passes): Return
	false if we are in lto1.
	(tree_rest_of_compilation): Call execute_all_ipa_transforms.
	* tree-pass.h (execute_all_ipa_transforms): Declare.
	(pass_ipa_function_and_variable_visibility): Declare.
	(pass_ipa_early_inline): Declare.
	(pass_ipa_lto_gimple_out): Declare.
	(pass_ipa_lto_wpa_fixup): Declare.
	(pass_ipa_lto_finish_out): Declare.
	(all_small_ipa_passes, all_regular_ipa_passes,
	all_lto_gen_passes): Declare.
	(execute_ipa_summary_passes): Declare.
	(execute_all_ipa_transforms): Declare.
	(ipa_write_summaries): Declare
	(ipa_write_summaries_of_cgraph_node_set): Declare.
	(ipa_read_summaries): Declare.

2009-09-28  Diego Novillo  <dnovillo@google.com>

	* plugin.c (register_pass): Call position_pass on
	all_small_ipa_passes, all_regular_ipa_passes and
	all_lto_gen_passes.
	* timevar.def (TV_IPA_LTO_GIMPLE_IO): Define.
	(TV_IPA_LTO_DECL_IO): Define.
	(TV_IPA_LTO_CGRAPH_IO): Define.
	(TV_LTO): Define.
	(TV_WHOPR_WPA): Define.
	(TV_WHOPR_WPA_IO): Define.
	(TV_WHOPR_LTRANS): Define.
	(TV_WHOPR_WPA_FIXUP): Define.
	(TV_WHOPR_WPA_LTRANS_EXEC): Define.
	* tree-cfg.c (tree_node_can_be_shared): Make extern.
	* tree-flow.h (tree_node_can_be_shared): Declare.
	* tree-inline.c (tree_can_inline_p): Check that E has a
	statement associated with it.
	* tree.c (free_lang_data_in_binf): Factor out of ...
	(free_lang_data_in_type): ... here.
	Call RECORD_OR_UNION_TYPE_P.
	(need_assembler_name_p): Ignore DECL if it does not have
	TREE_PUBLIC set.
	Call lang_hooks.decls.may_need_assembler_name_p if set.
	(free_lang_data_in_decl): Do not clear DECL_CONTEXT for
	CONST_DECLs.
	(free_lang_data): Set debug_info_level to
	DINFO_LEVEL_NONE.
	Set write_symbols to NO_DEBUG.
	Set debug_hooks to do_nothing_debug_hooks.
	(gate_free_lang_data): Return true if flag_generate_lto
	is set.
	(walk_tree_1): Call RECORD_OR_UNION_TYPE_P.
	* c-common.h (set_builtin_user_assembler_name): Move ...
	* tree.h (set_builtin_user_assembler_name): ... here.

diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/c-common.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/c-common.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/c-common.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/c-common.c	2009-09-25 11:21:19.000000000 -0400
@@ -5031,44 +5031,6 @@ c_common_nodes_and_builtins (void)
   memset (builtin_types, 0, sizeof (builtin_types));
 }
 
-/* Look up the function in built_in_decls that corresponds to DECL
-   and set ASMSPEC as its user assembler name.  DECL must be a
-   function decl that declares a builtin.  */
-
-void
-set_builtin_user_assembler_name (tree decl, const char *asmspec)
-{
-  tree builtin;
-  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL
-	      && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
-	      && asmspec != 0);
-
-  builtin = built_in_decls [DECL_FUNCTION_CODE (decl)];
-  set_user_assembler_name (builtin, asmspec);
-  switch (DECL_FUNCTION_CODE (decl))
-    {
-    case BUILT_IN_MEMCPY:
-      init_block_move_fn (asmspec);
-      memcpy_libfunc = set_user_assembler_libfunc ("memcpy", asmspec);
-      break;
-    case BUILT_IN_MEMSET:
-      init_block_clear_fn (asmspec);
-      memset_libfunc = set_user_assembler_libfunc ("memset", asmspec);
-      break;
-    case BUILT_IN_MEMMOVE:
-      memmove_libfunc = set_user_assembler_libfunc ("memmove", asmspec);
-      break;
-    case BUILT_IN_MEMCMP:
-      memcmp_libfunc = set_user_assembler_libfunc ("memcmp", asmspec);
-      break;
-    case BUILT_IN_ABORT:
-      abort_libfunc = set_user_assembler_libfunc ("abort", asmspec);
-      break;
-    default:
-      break;
-    }
-}
-
 /* The number of named compound-literals generated thus far.  */
 static GTY(()) int compound_literal_number;
 
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/c-common.h /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/c-common.h
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/c-common.h	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/c-common.h	2009-09-25 11:21:19.000000000 -0400
@@ -839,8 +839,6 @@ extern tree c_build_qualified_type (tree
    frontends.  */
 extern void c_common_nodes_and_builtins (void);
 
-extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
-
 extern void disable_builtin_function (const char *);
 
 extern void set_compound_literal_name (tree decl);
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/builtins.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/builtins.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/builtins.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/builtins.c	2009-09-23 10:51:15.000000000 -0400
@@ -244,6 +244,8 @@ static tree do_mpfr_bessel_n (tree, tree
 static tree do_mpfr_remquo (tree, tree, tree);
 static tree do_mpfr_lgamma_r (tree, tree, tree);
 
+/* Return true if NAME starts with __builtin_ or __sync_.  */
+
 bool
 is_builtin_name (const char *name)
 {
@@ -254,6 +256,16 @@ is_builtin_name (const char *name)
   return false;
 }
 
+
+/* Return true if DECL is a function symbol representing a built-in.  */
+
+bool
+is_builtin_fn (tree decl)
+{
+  return TREE_CODE (decl) == FUNCTION_DECL && DECL_BUILT_IN (decl);
+}
+
+
 /* Return true if NODE should be considered for inline expansion regardless
    of the optimization level.  This means whenever a function is invoked with
    its "internal" name, which normally contains the prefix "__builtin".  */
@@ -14211,3 +14223,41 @@ fold_call_stmt (gimple stmt, bool ignore
     }
   return NULL_TREE;
 }
+
+/* Look up the function in built_in_decls that corresponds to DECL
+   and set ASMSPEC as its user assembler name.  DECL must be a
+   function decl that declares a builtin.  */
+
+void
+set_builtin_user_assembler_name (tree decl, const char *asmspec)
+{
+  tree builtin;
+  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL
+	      && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
+	      && asmspec != 0);
+
+  builtin = built_in_decls [DECL_FUNCTION_CODE (decl)];
+  set_user_assembler_name (builtin, asmspec);
+  switch (DECL_FUNCTION_CODE (decl))
+    {
+    case BUILT_IN_MEMCPY:
+      init_block_move_fn (asmspec);
+      memcpy_libfunc = set_user_assembler_libfunc ("memcpy", asmspec);
+      break;
+    case BUILT_IN_MEMSET:
+      init_block_clear_fn (asmspec);
+      memset_libfunc = set_user_assembler_libfunc ("memset", asmspec);
+      break;
+    case BUILT_IN_MEMMOVE:
+      memmove_libfunc = set_user_assembler_libfunc ("memmove", asmspec);
+      break;
+    case BUILT_IN_MEMCMP:
+      memcmp_libfunc = set_user_assembler_libfunc ("memcmp", asmspec);
+      break;
+    case BUILT_IN_ABORT:
+      abort_libfunc = set_user_assembler_libfunc ("abort", asmspec);
+      break;
+    default:
+      break;
+    }
+}
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/except.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/except.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/except.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/except.c	2009-09-23 15:54:41.000000000 -0400
@@ -2879,7 +2879,14 @@ output_ttype (tree type, int tt_format, 
     {
       struct varpool_node *node;
 
-      type = lookup_type_for_runtime (type);
+      /* FIXME lto.  pass_ipa_free_lang_data changes all types to
+	 runtime types so TYPE should already be a runtime type
+	 reference.  When pass_ipa_free_lang data is made a default
+	 pass, we can then remove the call to lookup_type_for_runtime
+	 below.  */
+      if (TYPE_P (type))
+	type = lookup_type_for_runtime (type);
+
       value = expand_expr (type, NULL_RTX, VOIDmode, EXPAND_INITIALIZER);
 
       /* Let cgraph know that the rtti decl is used.  Not all of the
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/gimple.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/gimple.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/gimple.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/gimple.c	2009-09-23 11:15:58.000000000 -0400
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
+#include "target.h"
 #include "tree.h"
 #include "ggc.h"
 #include "hard-reg-set.h"
@@ -33,8 +34,18 @@ along with GCC; see the file COPYING3.  
 #include "tree-flow.h"
 #include "value-prof.h"
 #include "flags.h"
+#include "alias.h"
 #include "demangle.h"
 
+/* Global type table.  FIXME lto, it should be possible to re-use some
+   of the type hashing routines in tree.c (type_hash_canon, type_hash_lookup,
+   etc), but those assume that types were built with the various
+   build_*_type routines which is not the case with the streamer.  */
+static htab_t gimple_types;
+static struct pointer_map_t *type_hash_cache;
+
+/* Global type comparison cache.  */
+static htab_t gtc_visited;
 
 /* All the tuples have their operand vector (if present) at the very bottom
    of the structure.  Therefore, the offset required to find the
@@ -115,8 +126,7 @@ gimple_size (enum gimple_code code)
 /* Allocate memory for a GIMPLE statement with code CODE and NUM_OPS
    operands.  */
 
-#define gimple_alloc(c, n) gimple_alloc_stat (c, n MEM_STAT_INFO)
-static gimple
+gimple
 gimple_alloc_stat (enum gimple_code code, unsigned num_ops MEM_STAT_DECL)
 {
   size_t size;
@@ -613,7 +623,7 @@ gimple_build_eh_must_not_throw (tree dec
 
   gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
   gcc_assert (flags_from_decl_or_type (decl) & ECF_NORETURN);
-  p->gimple_eh_mnt.fndecl = decl;
+  gimple_eh_must_not_throw_set_fndecl (p, decl);
 
   return p;
 }
@@ -3000,6 +3010,1185 @@ gimple_call_copy_skip_args (gimple stmt,
 }
 
 
+static hashval_t gimple_type_hash (const void *);
+
+/* Structure used to maintain a cache of some type pairs compared by
+   gimple_types_compatible_p when comparing aggregate types.  There are
+   four possible values for SAME_P:
+
+   	-2: The pair (T1, T2) has just been inserted in the table.
+	-1: The pair (T1, T2) is currently being compared.
+	 0: T1 and T2 are different types.
+	 1: T1 and T2 are the same type.
+
+   This table is only used when comparing aggregate types to avoid
+   infinite recursion due to self-referential types.  */
+struct type_pair_d
+{
+  tree t1;
+  tree t2;
+  int same_p;
+};
+typedef struct type_pair_d *type_pair_t;
+
+/* Return a hash value for the type pair pointed-to by P.  */
+
+static hashval_t
+type_pair_hash (const void *p)
+{
+  const struct type_pair_d *pair = (const struct type_pair_d *) p;
+  hashval_t val = iterative_hash_hashval_t (htab_hash_pointer (pair->t1), 0);
+  return iterative_hash_hashval_t (htab_hash_pointer (pair->t2), val);
+}
+
+/* Compare two type pairs pointed-to by P1 and P2.  */
+
+static int
+type_pair_eq (const void *p1, const void *p2)
+{
+  const struct type_pair_d *pair1 = (const struct type_pair_d *) p1;
+  const struct type_pair_d *pair2 = (const struct type_pair_d *) p2;
+  return (pair1->t1 == pair2->t1 && pair1->t2 == pair2->t2);
+}
+
+
+/* Lookup the pair of types T1 and T2 in *VISITED_P.  Insert a new
+   entry if none existed.  */
+
+static type_pair_t
+lookup_type_pair (tree t1, tree t2, htab_t *visited_p)
+{
+  struct type_pair_d pair;
+  type_pair_t p;
+  void **slot;
+
+  if (*visited_p == NULL)
+    *visited_p = htab_create (13, type_pair_hash, type_pair_eq, free);
+
+  pair.t1 = t1;
+  pair.t2 = t2;
+  pair.same_p = -2;
+  slot = htab_find_slot (*visited_p, &pair, INSERT);
+
+  if (*slot)
+    p = *((type_pair_t *) slot);
+  else
+    {
+      p = XCNEW (struct type_pair_d);
+      p->t1 = t1;
+      p->t2 = t2;
+      p->same_p = -2;
+      *slot = (void *) p;
+    }
+
+  return p;
+}
+
+
+/* Force merging the type T2 into the type T1.  */
+
+void
+gimple_force_type_merge (tree t1, tree t2)
+{
+  void **slot;
+  type_pair_t p;
+
+  /* There's no other way than copying t2 to t1 in this case.
+     Yuck.  We'll just call this "completing" t1.  */
+  memcpy (t1, t2, tree_size (t1));
+
+  /* Adjust the hash value of T1 if it was computed already.  Otherwise
+     we would be forced to not hash fields of structs to match the
+     hash value of an incomplete struct.  */
+  if (type_hash_cache
+      && (slot = pointer_map_contains (type_hash_cache, t1)) != NULL)
+    {
+      gimple_type_hash (t2);
+      *slot = *pointer_map_contains (type_hash_cache, t2);
+    }
+
+  /* Adjust cached comparison results for T1 and T2 to make sure
+     they now compare compatible.  Update both directions to
+     preserve the symmetry of gimple_compare_types.  */
+  p = lookup_type_pair (t1, t2, &gtc_visited);
+  p->same_p = 1;
+
+  p = lookup_type_pair (t2, t1, &gtc_visited);
+  p->same_p = 1;
+}
+
+
+/* Return true if both types have the same name.  */
+
+static bool
+compare_type_names_p (tree t1, tree t2)
+{
+  tree name1 = TYPE_NAME (t1);
+  tree name2 = TYPE_NAME (t2);
+
+  /* Consider anonymous types all unique.  */
+  if (!name1 || !name2)
+    return false;
+
+  if (TREE_CODE (name1) == TYPE_DECL)
+    {
+      name1 = DECL_NAME (name1);
+      if (!name1)
+	return false;
+    }
+  gcc_assert (TREE_CODE (name1) == IDENTIFIER_NODE);
+
+  if (TREE_CODE (name2) == TYPE_DECL)
+    {
+      name2 = DECL_NAME (name2);
+      if (!name2)
+	return false;
+    }
+  gcc_assert (TREE_CODE (name2) == IDENTIFIER_NODE);
+
+  /* Identifiers can be compared with pointer equality rather
+     than a string comparison.  */
+  if (name1 == name2)
+    return true;
+
+  return false;
+}
+
+/* Return true if the field decls F1 and F2 are at the same offset.  */
+
+static bool
+compare_field_offset (tree f1, tree f2)
+{
+  if (DECL_OFFSET_ALIGN (f1) == DECL_OFFSET_ALIGN (f2))
+    return (operand_equal_p (DECL_FIELD_OFFSET (f1),
+			     DECL_FIELD_OFFSET (f2), 0)
+	    && tree_int_cst_equal (DECL_FIELD_BIT_OFFSET (f1),
+				   DECL_FIELD_BIT_OFFSET (f2)));
+
+  /* Fortran and C do not always agree on what DECL_OFFSET_ALIGN
+     should be, so handle differing ones specially by decomposing
+     the offset into a byte and bit offset manually.  */
+  if (host_integerp (DECL_FIELD_OFFSET (f1), 0)
+      && host_integerp (DECL_FIELD_OFFSET (f2), 0))
+    {
+      unsigned HOST_WIDE_INT byte_offset1, byte_offset2;
+      unsigned HOST_WIDE_INT bit_offset1, bit_offset2;
+      bit_offset1 = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (f1));
+      byte_offset1 = (TREE_INT_CST_LOW (DECL_FIELD_OFFSET (f1))
+		      + bit_offset1 / BITS_PER_UNIT);
+      bit_offset2 = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (f2));
+      byte_offset2 = (TREE_INT_CST_LOW (DECL_FIELD_OFFSET (f2))
+		      + bit_offset2 / BITS_PER_UNIT);
+      if (byte_offset1 != byte_offset2)
+	return false;
+      return bit_offset1 % BITS_PER_UNIT == bit_offset2 % BITS_PER_UNIT;
+    }
+
+  return false;
+}
+
+/* Return 1 iff T1 and T2 are structurally identical.
+   Otherwise, return 0.  */
+
+int
+gimple_types_compatible_p (tree t1, tree t2)
+{
+  type_pair_t p = NULL;
+
+  /* Check first for the obvious case of pointer identity.  */
+  if (t1 == t2)
+    goto same_types;
+
+  /* Check that we have two types to compare.  */
+  if (t1 == NULL_TREE || t2 == NULL_TREE)
+    goto different_types;
+
+  /* Can't be the same type if the types don't have the same code.  */
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    goto different_types;
+
+  /* Void types are always the same.  */
+  if (TREE_CODE (t1) == VOID_TYPE)
+    goto same_types;
+
+  /* Can't be the same type if they have different CV qualifiers.  */
+  if (TYPE_QUALS (t1) != TYPE_QUALS (t2))
+    goto different_types;
+
+  /* If the hash values of t1 and t2 are different the types can't
+     possibly be the same.  This helps keeping the type-pair hashtable
+     small, only tracking comparisons for hash collisions.  */
+  if (gimple_type_hash (t1) != gimple_type_hash (t2))
+    return 0;
+
+  /* If we've visited this type pair before (in the case of aggregates
+     with self-referential types), and we made a decision, return it.  */
+  p = lookup_type_pair (t1, t2, &gtc_visited);
+  if (p->same_p == 0 || p->same_p == 1)
+    {
+      /* We have already decided whether T1 and T2 are the
+	 same, return the cached result.  */
+      return p->same_p == 1;
+    }
+  else if (p->same_p == -1)
+    {
+      /* We are currently comparing this pair of types, assume
+	 that they are the same and let the caller decide.  */
+      return 1;
+    }
+
+  gcc_assert (p->same_p == -2);
+
+  /* Mark the (T1, T2) comparison in progress.  */
+  p->same_p = -1;
+
+  /* If their attributes are not the same they can't be the same type.  */
+  if (!attribute_list_equal (TYPE_ATTRIBUTES (t1), TYPE_ATTRIBUTES (t2)))
+    goto different_types;
+
+  /* For numerical types, the bounds must coincide.  */
+  if (INTEGRAL_TYPE_P (t1)
+      || SCALAR_FLOAT_TYPE_P (t1)
+      || FIXED_POINT_TYPE_P (t1))
+    {
+      /* Can't be the same type if they have different size, alignment,
+	 sign, precision or mode.  Note that from now on, comparisons
+	 between *_CST nodes must be done using tree_int_cst_equal because
+	 we cannot assume that constants from T1 and T2 will be shared
+	 since T1 and T2 are distinct pointers.  */
+      if (!tree_int_cst_equal (TYPE_SIZE (t1), TYPE_SIZE (t2))
+	  || !tree_int_cst_equal (TYPE_SIZE_UNIT (t1), TYPE_SIZE_UNIT (t2))
+	  || TYPE_ALIGN (t1) != TYPE_ALIGN (t2)
+	  || TYPE_PRECISION (t1) != TYPE_PRECISION (t2)
+	  || TYPE_MODE (t1) != TYPE_MODE (t2)
+	  || TYPE_UNSIGNED (t1) != TYPE_UNSIGNED (t2))
+	goto different_types;
+
+      /* For non-enumeral types, check type bounds.  FIXME lto, we
+	 cannot check bounds on enumeral types because different front
+	 ends will produce different values.  In C, enumeral types are
+	 integers, while in C++ each element will have its own
+	 symbolic value.  We should decide how enums are to be
+	 represented in GIMPLE and have each front end lower to that.  */
+      if (TREE_CODE (t1) != ENUMERAL_TYPE)
+	{
+	  tree min1 = TYPE_MIN_VALUE (t1);
+	  tree max1 = TYPE_MAX_VALUE (t1);
+	  tree min2 = TYPE_MIN_VALUE (t2);
+	  tree max2 = TYPE_MAX_VALUE (t2);
+	  bool min_equal_p = false;
+	  bool max_equal_p = false;
+
+	  /* If either type has a minimum value, the other type must
+	     have the same.  */
+	  if (min1 == NULL_TREE && min2 == NULL_TREE)
+	    min_equal_p = true;
+	  else if (min1 && min2 && operand_equal_p (min1, min2, 0))
+	    min_equal_p = true;
+
+	  /* Likewise, if either type has a maximum value, the other
+	     type must have the same.  */
+	  if (max1 == NULL_TREE && max2 == NULL_TREE)
+	    max_equal_p = true;
+	  else if (max1 && max2 && operand_equal_p (max1, max2, 0))
+	    max_equal_p = true;
+
+	  if (!min_equal_p || !max_equal_p)
+	    goto different_types;
+	}
+
+      if (TREE_CODE (t1) == INTEGER_TYPE)
+	{
+	  if (TYPE_IS_SIZETYPE (t1) == TYPE_IS_SIZETYPE (t2)
+	      && TYPE_STRING_FLAG (t1) == TYPE_STRING_FLAG (t2))
+	    goto same_types;
+	  else
+	    goto different_types;
+	}
+      else if (TREE_CODE (t1) == BOOLEAN_TYPE)
+	goto same_types;
+      else if (TREE_CODE (t1) == REAL_TYPE)
+	goto same_types;
+    }
+
+  /* Do type-specific comparisons.  */
+  switch (TREE_CODE (t1))
+    {
+    case ARRAY_TYPE:
+      /* Array types are the same if the element types are the same and
+	 the number of elements are the same.  */
+      if (!gimple_types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	  || TYPE_STRING_FLAG (t1) != TYPE_STRING_FLAG (t2))
+	goto different_types;
+      else
+	{
+	  tree i1 = TYPE_DOMAIN (t1);
+	  tree i2 = TYPE_DOMAIN (t2);
+
+	  /* For an incomplete external array, the type domain can be
+ 	     NULL_TREE.  Check this condition also.  */
+	  if (i1 == NULL_TREE && i2 == NULL_TREE)
+	    goto same_types;
+	  else if (i1 == NULL_TREE || i2 == NULL_TREE)
+	    goto different_types;
+	  /* If for a complete array type the possibly gimplified sizes
+	     are different the types are different.  */
+	  else if (((TYPE_SIZE (i1) != NULL) ^ (TYPE_SIZE (i2) != NULL))
+		   || (TYPE_SIZE (i1)
+		       && TYPE_SIZE (i2)
+		       && !operand_equal_p (TYPE_SIZE (i1), TYPE_SIZE (i2), 0)))
+	    goto different_types;
+	  else
+	    {
+	      tree min1 = TYPE_MIN_VALUE (i1);
+	      tree min2 = TYPE_MIN_VALUE (i2);
+	      tree max1 = TYPE_MAX_VALUE (i1);
+	      tree max2 = TYPE_MAX_VALUE (i2);
+
+	      /* The minimum/maximum values have to be the same.  */
+	      if ((min1 == min2
+		   || (min1 && min2 && operand_equal_p (min1, min2, 0)))
+		  && (max1 == max2
+		      || (max1 && max2 && operand_equal_p (max1, max2, 0))))
+		goto same_types;
+	      else
+		goto different_types;
+	    }
+	}
+
+    case METHOD_TYPE:
+      /* Method types should belong to the same class.  */
+      if (!gimple_types_compatible_p (TYPE_METHOD_BASETYPE (t1),
+				 TYPE_METHOD_BASETYPE (t2)))
+	goto different_types;
+
+      /* Fallthru  */
+
+    case FUNCTION_TYPE:
+      /* Function types are the same if the return type and arguments types
+	 are the same.  */
+      if (!gimple_types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+	goto different_types;
+      else
+	{
+	  if (!targetm.comp_type_attributes (t1, t2))
+	    goto different_types;
+
+	  if (TYPE_ARG_TYPES (t1) == TYPE_ARG_TYPES (t2))
+	    goto same_types;
+	  else
+	    {
+	      tree parms1, parms2;
+
+	      for (parms1 = TYPE_ARG_TYPES (t1), parms2 = TYPE_ARG_TYPES (t2);
+		   parms1 && parms2;
+		   parms1 = TREE_CHAIN (parms1), parms2 = TREE_CHAIN (parms2))
+		{
+		  if (!gimple_types_compatible_p (TREE_VALUE (parms1),
+					     TREE_VALUE (parms2)))
+		    goto different_types;
+		}
+
+	      if (parms1 || parms2)
+		goto different_types;
+
+	      goto same_types;
+	    }
+	}
+
+    case POINTER_TYPE:
+    case REFERENCE_TYPE:
+	{
+	  /* If the two pointers have different ref-all attributes,
+	     they can't be the same type.  */
+	  if (TYPE_REF_CAN_ALIAS_ALL (t1) != TYPE_REF_CAN_ALIAS_ALL (t2))
+	    goto different_types;
+
+	  /* If one pointer points to an incomplete type variant of
+	     the other pointed-to type they are the same.  */
+	  if (TREE_CODE (TREE_TYPE (t1)) == TREE_CODE (TREE_TYPE (t2))
+	      && (!COMPLETE_TYPE_P (TREE_TYPE (t1))
+		  || !COMPLETE_TYPE_P (TREE_TYPE (t2)))
+	      && compare_type_names_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+	    {
+	      /* If t2 is complete we want to choose it instead of t1.  */
+	      if (COMPLETE_TYPE_P (TREE_TYPE (t2)))
+		gimple_force_type_merge (TREE_TYPE (t1), TREE_TYPE (t2));
+	      goto same_types;
+	    }
+
+	  /* Otherwise, pointer and reference types are the same if the
+	     pointed-to types are the same.  */
+	  if (gimple_types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+	    goto same_types;
+	  
+	  goto different_types;
+	}
+
+    case ENUMERAL_TYPE:
+	{
+	  /* For enumeral types, all the values must be the same.  */
+	  tree v1, v2;
+
+	  if (TYPE_VALUES (t1) == TYPE_VALUES (t2))
+	    goto same_types;
+
+	  for (v1 = TYPE_VALUES (t1), v2 = TYPE_VALUES (t2);
+	       v1 && v2;
+	       v1 = TREE_CHAIN (v1), v2 = TREE_CHAIN (v2))
+	    {
+	      tree c1 = TREE_VALUE (v1);
+	      tree c2 = TREE_VALUE (v2);
+
+	      if (TREE_CODE (c1) == CONST_DECL)
+		c1 = DECL_INITIAL (c1);
+
+	      if (TREE_CODE (c2) == CONST_DECL)
+		c2 = DECL_INITIAL (c2);
+
+	      if (tree_int_cst_equal (c1, c2) != 1)
+		goto different_types;
+	    }
+
+	  /* If one enumeration has more values than the other, they
+	     are not the same.  */
+	  if (v1 || v2)
+	    goto different_types;
+
+	  goto same_types;
+	}
+
+    case RECORD_TYPE:
+    case UNION_TYPE:
+    case QUAL_UNION_TYPE:
+	{
+	  /* For aggregate types, all the fields must be the same.  */
+	  tree f1, f2;
+
+	  /* Compare every field.  */
+	  for (f1 = TYPE_FIELDS (t1), f2 = TYPE_FIELDS (t2);
+	       f1 && f2;
+	       f1 = TREE_CHAIN (f1), f2 = TREE_CHAIN (f2))
+	    {
+	      /* The fields must have the same name, offset and type.  */
+	      if (DECL_NAME (f1) != DECL_NAME (f2)
+		  || !compare_field_offset (f1, f2)
+		  || !gimple_types_compatible_p (TREE_TYPE (f1),
+					    TREE_TYPE (f2)))
+		goto different_types;
+	    }
+
+	  /* If one aggregate has more fields than the other, they
+	     are not the same.  */
+	  if (f1 || f2)
+	    goto different_types;
+
+	  goto same_types;
+	}
+
+    case VECTOR_TYPE:
+      if (TYPE_VECTOR_SUBPARTS (t1) != TYPE_VECTOR_SUBPARTS (t2))
+	goto different_types;
+
+      /* Fallthru  */
+    case COMPLEX_TYPE:
+      if (!gimple_types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+	goto different_types;
+      goto same_types;
+
+    default:
+      goto different_types;
+    }
+
+  /* Common exit path for types that are not compatible.  */
+different_types:
+  if (p)
+    p->same_p = 0;
+  return 0;
+
+  /* Common exit path for types that are compatible.  */
+same_types:
+  if (p)
+    p->same_p = 1;
+  return 1;
+}
+
+
+
+
+/* Per pointer state for the SCC finding.  The on_sccstack flag
+   is not strictly required, it is true when there is no hash value
+   recorded for the type and false otherwise.  But querying that
+   is slower.  */
+
+struct sccs
+{
+  unsigned int dfsnum;
+  unsigned int low;
+  bool on_sccstack;
+  hashval_t hash;
+};
+
+static unsigned int next_dfs_num;
+
+static hashval_t
+iterative_hash_gimple_type (tree, hashval_t, VEC(tree, heap) **,
+			    struct pointer_map_t *, struct obstack *);
+
+/* DFS visit the edge from the callers type with state *STATE to T.
+   Update the callers type hash V with the hash for T if it is not part
+   of the SCC containing the callers type and return it.
+   SCCSTACK, SCCSTATE and SCCSTATE_OBSTACK are state for the DFS walk done.  */
+
+static hashval_t
+visit (tree t, struct sccs *state, hashval_t v,
+       VEC (tree, heap) **sccstack,
+       struct pointer_map_t *sccstate,
+       struct obstack *sccstate_obstack)
+{
+  struct sccs *cstate = NULL;
+  void **slot;
+
+  /* If there is a hash value recorded for this type then it can't
+     possibly be part of our parent SCC.  Simply mix in its hash.  */
+  if ((slot = pointer_map_contains (type_hash_cache, t)))
+    {
+#ifdef ENABLE_CHECKING
+      void **slot2;
+      /* If we have visited the type during the DFS walk then it
+	 should not be on the SCC stack anymore.  */
+      if ((slot2 = pointer_map_contains (sccstate, t)) != NULL)
+	gcc_assert (!((struct sccs *)*slot2)->on_sccstack);
+#endif
+      return iterative_hash_hashval_t ((hashval_t) (size_t) *slot, v);
+    }
+
+  if ((slot = pointer_map_contains (sccstate, t)) != NULL)
+    cstate = (struct sccs *)*slot;
+  if (!cstate)
+    {
+      hashval_t tem;
+      /* Not yet visited.  DFS recurse.  */
+      tem = iterative_hash_gimple_type (t, v,
+					sccstack, sccstate, sccstate_obstack);
+      if (!cstate)
+	cstate = (struct sccs *)* pointer_map_contains (sccstate, t);
+      state->low = MIN (state->low, cstate->low);
+      /* If the type is no longer on the SCC stack and thus is not part
+         of the parents SCC mix in its hash value.  Otherwise we will
+	 ignore the type for hashing purposes and return the unaltered
+	 hash value.  */
+      if (!cstate->on_sccstack)
+	{
+#ifdef ENABLE_CHECKING
+	  /* If we are not on the stack we should have a hash value
+	     recorded.  */
+	  gcc_assert (pointer_map_contains (type_hash_cache, t));
+#endif
+	  return tem;
+	}
+    }
+  if (cstate->dfsnum < state->dfsnum
+      && cstate->on_sccstack)
+    state->low = MIN (cstate->dfsnum, state->low);
+
+#ifdef ENABLE_CHECKING
+  /* As we are part of an SCC that is still in processing we should
+     not have a hash value recorded.  */
+  gcc_assert (!pointer_map_contains (type_hash_cache, t));
+#endif
+
+  /* We are part of our parents SCC, skip this type during hashing
+     and return the unaltered hash value.  */
+  return v;
+}
+
+/* Hash the name of TYPE with the previous hash value V and return it.  */
+
+static hashval_t
+iterative_hash_type_name (tree type, hashval_t v)
+{
+  tree name = TYPE_NAME (TYPE_MAIN_VARIANT (type));
+  if (!name)
+    return v;
+  if (TREE_CODE (name) == TYPE_DECL)
+    name = DECL_NAME (name);
+  if (!name)
+    return v;
+  gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
+  return iterative_hash_object (IDENTIFIER_HASH_VALUE (name), v);
+}
+
+/* Returning a hash value for gimple type TYPE combined with VAL.
+   SCCSTACK, SCCSTATE and SCCSTATE_OBSTACK are state for the DFS walk done.
+
+   To hash a type we end up hashing in types that are reachable.
+   Through pointers we can end up with cycles which messes up the
+   required property that we need to compute the same hash value
+   for structurally equivalent types.  To avoid this we have to
+   hash all types in a cycle (the SCC) in a commutative way.  The
+   easiest way is to not mix in the hashes of the SCC members at
+   all.  To make this work we have to delay setting the hash
+   values of the SCC until it is complete.  */
+
+static hashval_t
+iterative_hash_gimple_type (tree type, hashval_t val,
+			    VEC(tree, heap) **sccstack,
+			    struct pointer_map_t *sccstate,
+			    struct obstack *sccstate_obstack)
+{
+  hashval_t v;
+  void **slot;
+  struct sccs *state;
+
+#ifdef ENABLE_CHECKING
+  /* Not visited during this DFS walk nor during previous walks.  */
+  gcc_assert (!pointer_map_contains (type_hash_cache, type)
+	      && !pointer_map_contains (sccstate, type));
+#endif
+  state = XOBNEW (sccstate_obstack, struct sccs);
+  *pointer_map_insert (sccstate, type) = state;
+
+  VEC_safe_push (tree, heap, *sccstack, type);
+  state->dfsnum = next_dfs_num++;
+  state->low = state->dfsnum;
+  state->on_sccstack = true;
+
+  /* Combine a few common features of types so that types are grouped into
+     smaller sets; when searching for existing matching types to merge,
+     only existing types having the same features as the new type will be
+     checked.  */
+  v = iterative_hash_hashval_t (TREE_CODE (type), 0);
+  v = iterative_hash_hashval_t (TYPE_QUALS (type), v);
+  v = iterative_hash_hashval_t (TREE_ADDRESSABLE (type), v);
+
+  /* Do not hash the types size as this will cause differences in
+     hash values for the complete vs. the incomplete type variant.  */
+
+  /* Incorporate common features of numerical types.  */
+  if (INTEGRAL_TYPE_P (type)
+      || SCALAR_FLOAT_TYPE_P (type)
+      || FIXED_POINT_TYPE_P (type))
+    {
+      v = iterative_hash_hashval_t (TYPE_PRECISION (type), v);
+      v = iterative_hash_hashval_t (TYPE_MODE (type), v);
+      v = iterative_hash_hashval_t (TYPE_UNSIGNED (type), v);
+    }
+
+  /* For pointer and reference types, fold in information about the type
+     pointed to but do not recurse into possibly incomplete types to
+     avoid hash differences for complete vs. incomplete types.  */
+  if (POINTER_TYPE_P (type))
+    {
+      if (AGGREGATE_TYPE_P (TREE_TYPE (type)))
+	{
+	  v = iterative_hash_hashval_t (TREE_CODE (TREE_TYPE (type)), v);
+	  v = iterative_hash_type_name (type, v);
+	}
+      else
+	v = visit (TREE_TYPE (type), state, v,
+		   sccstack, sccstate, sccstate_obstack);
+    }
+
+  /* Recurse for aggregates with a single element.  */
+  if (TREE_CODE (type) == ARRAY_TYPE
+      || TREE_CODE (type) == COMPLEX_TYPE
+      || TREE_CODE (type) == VECTOR_TYPE)
+    v = visit (TREE_TYPE (type), state, v,
+	       sccstack, sccstate, sccstate_obstack);
+
+  /* Incorporate function return and argument types.  */
+  if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE)
+    {
+      unsigned na;
+      tree p;
+
+      /* For method types also incorporate their parent class.  */
+      if (TREE_CODE (type) == METHOD_TYPE)
+	v = visit (TYPE_METHOD_BASETYPE (type), state, v,
+		   sccstack, sccstate, sccstate_obstack);
+
+      v = visit (TREE_TYPE (type), state, v,
+		 sccstack, sccstate, sccstate_obstack);
+
+      for (p = TYPE_ARG_TYPES (type), na = 0; p; p = TREE_CHAIN (p))
+	{
+	  v = visit (TREE_VALUE (p), state, v,
+		     sccstack, sccstate, sccstate_obstack);
+	  na++;
+	}
+
+      v = iterative_hash_hashval_t (na, v);
+    }
+
+  if (TREE_CODE (type) == RECORD_TYPE
+      || TREE_CODE (type) == UNION_TYPE
+      || TREE_CODE (type) == QUAL_UNION_TYPE)
+    {
+      unsigned nf;
+      tree f;
+
+      v = iterative_hash_type_name (type, v);
+
+      for (f = TYPE_FIELDS (type), nf = 0; f; f = TREE_CHAIN (f))
+	{
+	  v = visit (TREE_TYPE (f), state, v,
+		     sccstack, sccstate, sccstate_obstack);
+	  nf++;
+	}
+
+      v = iterative_hash_hashval_t (nf, v);
+    }
+
+  /* Record hash for us.  */
+  state->hash = v;
+
+  /* See if we found an SCC.  */
+  if (state->low == state->dfsnum)
+    {
+      tree x;
+
+      /* Pop off the SCC and set its hash values.  */
+      do
+	{
+	  struct sccs *cstate;
+	  x = VEC_pop (tree, *sccstack);
+	  gcc_assert (!pointer_map_contains (type_hash_cache, x));
+	  cstate = (struct sccs *)*pointer_map_contains (sccstate, x);
+	  cstate->on_sccstack = false;
+	  slot = pointer_map_insert (type_hash_cache, x);
+	  *slot = (void *) (size_t) cstate->hash;
+	}
+      while (x != type);
+    }
+
+  return iterative_hash_hashval_t (v, val);
+}
+
+
+/* Returns a hash value for P (assumed to be a type).  The hash value
+   is computed using some distinguishing features of the type.  Note
+   that we cannot use pointer hashing here as we may be dealing with
+   two distinct instances of the same type.
+
+   This function should produce the same hash value for two compatible
+   types according to gimple_types_compatible_p.  */
+
+static hashval_t
+gimple_type_hash (const void *p)
+{
+  VEC(tree, heap) *sccstack = NULL;
+  struct pointer_map_t *sccstate;
+  struct obstack sccstate_obstack;
+  hashval_t val;
+  void **slot;
+
+  if (type_hash_cache == NULL)
+    type_hash_cache = pointer_map_create ();
+
+  if ((slot = pointer_map_contains (type_hash_cache, p)) != NULL)
+    return iterative_hash_hashval_t ((hashval_t) (size_t) *slot, 0);
+
+  /* Perform a DFS walk and pre-hash all reachable types.  */
+  next_dfs_num = 1;
+  sccstate = pointer_map_create ();
+  gcc_obstack_init (&sccstate_obstack);
+  val = iterative_hash_gimple_type (CONST_CAST2 (tree, const void *, p), 0,
+				    &sccstack, sccstate, &sccstate_obstack);
+  VEC_free (tree, heap, sccstack);
+  pointer_map_destroy (sccstate);
+  obstack_free (&sccstate_obstack, NULL);
+
+  return val;
+}
+
+
+/* Returns nonzero if P1 and P2 are equal.  */
+
+static int
+gimple_type_eq (const void *p1, const void *p2)
+{
+  const_tree t1 = (const_tree) p1;
+  const_tree t2 = (const_tree) p2;
+  return gimple_types_compatible_p (CONST_CAST_TREE (t1), CONST_CAST_TREE (t2));
+}
+
+
+/* Register type T in the global type table gimple_types.
+   If another type T', compatible with T, already existed in
+   gimple_types then return T', otherwise return T.  This is used by
+   LTO to merge identical types read from different TUs.  */
+
+tree
+gimple_register_type (tree t)
+{
+  void **slot;
+
+  gcc_assert (TYPE_P (t));
+
+  if (gimple_types == NULL)
+    gimple_types = htab_create (100000, gimple_type_hash, gimple_type_eq, 0);
+
+  if (getenv ("MERGE_TYPE_DEBUG"))
+    {
+      fprintf (stderr, "\nRegistering type: %p - ", (void *) t);
+      print_generic_stmt (stderr, t, 0);
+    }
+
+  slot = htab_find_slot (gimple_types, t, INSERT);
+  if (*slot
+      && *(tree *)slot != t)
+    {
+      tree new_type = (tree) *((tree *) slot);
+
+      /* Do not merge types with different addressability.  */
+      gcc_assert (TREE_ADDRESSABLE (t) == TREE_ADDRESSABLE (new_type));
+
+      if (getenv ("MERGE_TYPE_DEBUG"))
+	{
+	  fprintf (stderr, "Merged with existing compatible type: %p - ",
+		   *slot);
+	  print_generic_stmt (stderr, new_type, 0);
+	}
+
+      /* If t is not its main variant then make t unreachable from its
+	 main variant list.  Otherwise we'd queue up a lot of duplicates
+	 there.  */
+      if (t != TYPE_MAIN_VARIANT (t))
+	{
+	  tree tem = TYPE_MAIN_VARIANT (t);
+	  while (tem && TYPE_NEXT_VARIANT (tem) != t)
+	    tem = TYPE_NEXT_VARIANT (tem);
+	  if (tem)
+	    TYPE_NEXT_VARIANT (tem) = TYPE_NEXT_VARIANT (t);
+	  TYPE_NEXT_VARIANT (t) = NULL_TREE;
+	}
+
+      /* If we are a pointer then remove us from the pointer-to or
+	 reference-to chain.  Otherwise we'd queue up a lot of duplicates
+	 there.  */
+      if (TREE_CODE (t) == POINTER_TYPE)
+	{
+	  if (TYPE_POINTER_TO (TREE_TYPE (t)) == t)
+	    TYPE_POINTER_TO (TREE_TYPE (t)) = TYPE_NEXT_PTR_TO (t);
+	  else
+	    {
+	      tree tem = TYPE_POINTER_TO (TREE_TYPE (t));
+	      while (tem && TYPE_NEXT_PTR_TO (tem) != t)
+		tem = TYPE_NEXT_PTR_TO (tem);
+	      if (tem)
+		TYPE_NEXT_PTR_TO (tem) = TYPE_NEXT_PTR_TO (t);
+	    }
+	  TYPE_NEXT_PTR_TO (t) = NULL_TREE;
+	}
+      else if (TREE_CODE (t) == REFERENCE_TYPE)
+	{
+	  if (TYPE_REFERENCE_TO (TREE_TYPE (t)) == t)
+	    TYPE_REFERENCE_TO (TREE_TYPE (t)) = TYPE_NEXT_REF_TO (t);
+	  else
+	    {
+	      tree tem = TYPE_REFERENCE_TO (TREE_TYPE (t));
+	      while (tem && TYPE_NEXT_REF_TO (tem) != t)
+		tem = TYPE_NEXT_REF_TO (tem);
+	      if (tem)
+		TYPE_NEXT_REF_TO (tem) = TYPE_NEXT_REF_TO (t);
+	    }
+	  TYPE_NEXT_REF_TO (t) = NULL_TREE;
+	}
+
+      t = new_type;
+    }
+  else
+    *slot = (void *) t;
+
+  return t;
+}
+
+
+/* Show statistics on references to the global type table gimple_types.  */
+
+void
+print_gimple_types_stats (void)
+{
+  if (gimple_types)
+    fprintf (stderr, "GIMPLE type table: size %ld, %ld elements, "
+	     "%ld searches, %ld collisions (ratio: %f)\n",
+	     (long) htab_size (gimple_types),
+	     (long) htab_elements (gimple_types),
+	     (long) gimple_types->searches,
+	     (long) gimple_types->collisions,
+	     htab_collisions (gimple_types));
+  else
+    fprintf (stderr, "GIMPLE type table is empty\n");
+  if (gtc_visited)
+    fprintf (stderr, "GIMPLE type comparison table: size %ld, %ld elements, "
+	     "%ld searches, %ld collisions (ratio: %f)\n",
+	     (long) htab_size (gtc_visited),
+	     (long) htab_elements (gtc_visited),
+	     (long) gtc_visited->searches,
+	     (long) gtc_visited->collisions,
+	     htab_collisions (gtc_visited));
+  else
+    fprintf (stderr, "GIMPLE type comparison table is empty\n");
+}
+
+
+/* Return a type the same as TYPE except unsigned or
+   signed according to UNSIGNEDP.  */
+
+static tree
+gimple_signed_or_unsigned_type (bool unsignedp, tree type)
+{
+  tree type1;
+
+  type1 = TYPE_MAIN_VARIANT (type);
+  if (type1 == signed_char_type_node
+      || type1 == char_type_node
+      || type1 == unsigned_char_type_node)
+    return unsignedp ? unsigned_char_type_node : signed_char_type_node;
+  if (type1 == integer_type_node || type1 == unsigned_type_node)
+    return unsignedp ? unsigned_type_node : integer_type_node;
+  if (type1 == short_integer_type_node || type1 == short_unsigned_type_node)
+    return unsignedp ? short_unsigned_type_node : short_integer_type_node;
+  if (type1 == long_integer_type_node || type1 == long_unsigned_type_node)
+    return unsignedp ? long_unsigned_type_node : long_integer_type_node;
+  if (type1 == long_long_integer_type_node
+      || type1 == long_long_unsigned_type_node)
+    return unsignedp
+           ? long_long_unsigned_type_node
+	   : long_long_integer_type_node;
+#if HOST_BITS_PER_WIDE_INT >= 64
+  if (type1 == intTI_type_node || type1 == unsigned_intTI_type_node)
+    return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
+#endif
+  if (type1 == intDI_type_node || type1 == unsigned_intDI_type_node)
+    return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
+  if (type1 == intSI_type_node || type1 == unsigned_intSI_type_node)
+    return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
+  if (type1 == intHI_type_node || type1 == unsigned_intHI_type_node)
+    return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
+  if (type1 == intQI_type_node || type1 == unsigned_intQI_type_node)
+    return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
+
+#define GIMPLE_FIXED_TYPES(NAME)	    \
+  if (type1 == short_ ## NAME ## _type_node \
+      || type1 == unsigned_short_ ## NAME ## _type_node) \
+    return unsignedp ? unsigned_short_ ## NAME ## _type_node \
+		     : short_ ## NAME ## _type_node; \
+  if (type1 == NAME ## _type_node \
+      || type1 == unsigned_ ## NAME ## _type_node) \
+    return unsignedp ? unsigned_ ## NAME ## _type_node \
+		     : NAME ## _type_node; \
+  if (type1 == long_ ## NAME ## _type_node \
+      || type1 == unsigned_long_ ## NAME ## _type_node) \
+    return unsignedp ? unsigned_long_ ## NAME ## _type_node \
+		     : long_ ## NAME ## _type_node; \
+  if (type1 == long_long_ ## NAME ## _type_node \
+      || type1 == unsigned_long_long_ ## NAME ## _type_node) \
+    return unsignedp ? unsigned_long_long_ ## NAME ## _type_node \
+		     : long_long_ ## NAME ## _type_node;
+
+#define GIMPLE_FIXED_MODE_TYPES(NAME) \
+  if (type1 == NAME ## _type_node \
+      || type1 == u ## NAME ## _type_node) \
+    return unsignedp ? u ## NAME ## _type_node \
+		     : NAME ## _type_node;
+
+#define GIMPLE_FIXED_TYPES_SAT(NAME) \
+  if (type1 == sat_ ## short_ ## NAME ## _type_node \
+      || type1 == sat_ ## unsigned_short_ ## NAME ## _type_node) \
+    return unsignedp ? sat_ ## unsigned_short_ ## NAME ## _type_node \
+		     : sat_ ## short_ ## NAME ## _type_node; \
+  if (type1 == sat_ ## NAME ## _type_node \
+      || type1 == sat_ ## unsigned_ ## NAME ## _type_node) \
+    return unsignedp ? sat_ ## unsigned_ ## NAME ## _type_node \
+		     : sat_ ## NAME ## _type_node; \
+  if (type1 == sat_ ## long_ ## NAME ## _type_node \
+      || type1 == sat_ ## unsigned_long_ ## NAME ## _type_node) \
+    return unsignedp ? sat_ ## unsigned_long_ ## NAME ## _type_node \
+		     : sat_ ## long_ ## NAME ## _type_node; \
+  if (type1 == sat_ ## long_long_ ## NAME ## _type_node \
+      || type1 == sat_ ## unsigned_long_long_ ## NAME ## _type_node) \
+    return unsignedp ? sat_ ## unsigned_long_long_ ## NAME ## _type_node \
+		     : sat_ ## long_long_ ## NAME ## _type_node;
+
+#define GIMPLE_FIXED_MODE_TYPES_SAT(NAME)	\
+  if (type1 == sat_ ## NAME ## _type_node \
+      || type1 == sat_ ## u ## NAME ## _type_node) \
+    return unsignedp ? sat_ ## u ## NAME ## _type_node \
+		     : sat_ ## NAME ## _type_node;
+
+  GIMPLE_FIXED_TYPES (fract);
+  GIMPLE_FIXED_TYPES_SAT (fract);
+  GIMPLE_FIXED_TYPES (accum);
+  GIMPLE_FIXED_TYPES_SAT (accum);
+
+  GIMPLE_FIXED_MODE_TYPES (qq);
+  GIMPLE_FIXED_MODE_TYPES (hq);
+  GIMPLE_FIXED_MODE_TYPES (sq);
+  GIMPLE_FIXED_MODE_TYPES (dq);
+  GIMPLE_FIXED_MODE_TYPES (tq);
+  GIMPLE_FIXED_MODE_TYPES_SAT (qq);
+  GIMPLE_FIXED_MODE_TYPES_SAT (hq);
+  GIMPLE_FIXED_MODE_TYPES_SAT (sq);
+  GIMPLE_FIXED_MODE_TYPES_SAT (dq);
+  GIMPLE_FIXED_MODE_TYPES_SAT (tq);
+  GIMPLE_FIXED_MODE_TYPES (ha);
+  GIMPLE_FIXED_MODE_TYPES (sa);
+  GIMPLE_FIXED_MODE_TYPES (da);
+  GIMPLE_FIXED_MODE_TYPES (ta);
+  GIMPLE_FIXED_MODE_TYPES_SAT (ha);
+  GIMPLE_FIXED_MODE_TYPES_SAT (sa);
+  GIMPLE_FIXED_MODE_TYPES_SAT (da);
+  GIMPLE_FIXED_MODE_TYPES_SAT (ta);
+
+  /* For ENUMERAL_TYPEs in C++, must check the mode of the types, not
+     the precision; they have precision set to match their range, but
+     may use a wider mode to match an ABI.  If we change modes, we may
+     wind up with bad conversions.  For INTEGER_TYPEs in C, must check
+     the precision as well, so as to yield correct results for
+     bit-field types.  C++ does not have these separate bit-field
+     types, and producing a signed or unsigned variant of an
+     ENUMERAL_TYPE may cause other problems as well.  */
+  if (!INTEGRAL_TYPE_P (type)
+      || TYPE_UNSIGNED (type) == unsignedp)
+    return type;
+
+#define TYPE_OK(node)							    \
+  (TYPE_MODE (type) == TYPE_MODE (node)					    \
+   && TYPE_PRECISION (type) == TYPE_PRECISION (node))
+  if (TYPE_OK (signed_char_type_node))
+    return unsignedp ? unsigned_char_type_node : signed_char_type_node;
+  if (TYPE_OK (integer_type_node))
+    return unsignedp ? unsigned_type_node : integer_type_node;
+  if (TYPE_OK (short_integer_type_node))
+    return unsignedp ? short_unsigned_type_node : short_integer_type_node;
+  if (TYPE_OK (long_integer_type_node))
+    return unsignedp ? long_unsigned_type_node : long_integer_type_node;
+  if (TYPE_OK (long_long_integer_type_node))
+    return (unsignedp
+	    ? long_long_unsigned_type_node
+	    : long_long_integer_type_node);
+
+#if HOST_BITS_PER_WIDE_INT >= 64
+  if (TYPE_OK (intTI_type_node))
+    return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
+#endif
+  if (TYPE_OK (intDI_type_node))
+    return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
+  if (TYPE_OK (intSI_type_node))
+    return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
+  if (TYPE_OK (intHI_type_node))
+    return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
+  if (TYPE_OK (intQI_type_node))
+    return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
+
+#undef GIMPLE_FIXED_TYPES
+#undef GIMPLE_FIXED_MODE_TYPES
+#undef GIMPLE_FIXED_TYPES_SAT
+#undef GIMPLE_FIXED_MODE_TYPES_SAT
+#undef TYPE_OK
+
+  return build_nonstandard_integer_type (TYPE_PRECISION (type), unsignedp);
+}
+
+
+/* Return an unsigned type the same as TYPE in other respects.  */
+
+tree
+gimple_unsigned_type (tree type)
+{
+  return gimple_signed_or_unsigned_type (true, type);
+}
+
+
+/* Return a signed type the same as TYPE in other respects.  */
+
+tree
+gimple_signed_type (tree type)
+{
+  return gimple_signed_or_unsigned_type (false, type);
+}
+
+
+/* Return the typed-based alias set for T, which may be an expression
+   or a type.  Return -1 if we don't do anything special.  */
+
+alias_set_type
+gimple_get_alias_set (tree t)
+{
+  tree u;
+
+  /* Permit type-punning when accessing a union, provided the access
+     is directly through the union.  For example, this code does not
+     permit taking the address of a union member and then storing
+     through it.  Even the type-punning allowed here is a GCC
+     extension, albeit a common and useful one; the C standard says
+     that such accesses have implementation-defined behavior.  */
+  for (u = t;
+       TREE_CODE (u) == COMPONENT_REF || TREE_CODE (u) == ARRAY_REF;
+       u = TREE_OPERAND (u, 0))
+    if (TREE_CODE (u) == COMPONENT_REF
+	&& TREE_CODE (TREE_TYPE (TREE_OPERAND (u, 0))) == UNION_TYPE)
+      return 0;
+
+  /* That's all the expressions we handle specially.  */
+  if (!TYPE_P (t))
+    return -1;
+
+  /* For convenience, follow the C standard when dealing with
+     character types.  Any object may be accessed via an lvalue that
+     has character type.  */
+  if (t == char_type_node
+      || t == signed_char_type_node
+      || t == unsigned_char_type_node)
+    return 0;
+
+  /* Allow aliasing between signed and unsigned variants of the same
+     type.  We treat the signed variant as canonical.  */
+  if (TREE_CODE (t) == INTEGER_TYPE && TYPE_UNSIGNED (t))
+    {
+      tree t1 = gimple_signed_type (t);
+
+      /* t1 == t can happen for boolean nodes which are always unsigned.  */
+      if (t1 != t)
+	return get_alias_set (t1);
+    }
+  else if (POINTER_TYPE_P (t))
+    {
+      tree t1;
+
+      /* Unfortunately, there is no canonical form of a pointer type.
+	 In particular, if we have `typedef int I', then `int *', and
+	 `I *' are different types.  So, we have to pick a canonical
+	 representative.  We do this below.
+
+	 Technically, this approach is actually more conservative that
+	 it needs to be.  In particular, `const int *' and `int *'
+	 should be in different alias sets, according to the C and C++
+	 standard, since their types are not the same, and so,
+	 technically, an `int **' and `const int **' cannot point at
+	 the same thing.
+
+	 But, the standard is wrong.  In particular, this code is
+	 legal C++:
+
+	 int *ip;
+	 int **ipp = &ip;
+	 const int* const* cipp = ipp;
+	 And, it doesn't make sense for that to be legal unless you
+	 can dereference IPP and CIPP.  So, we ignore cv-qualifiers on
+	 the pointed-to types.  This issue has been reported to the
+	 C++ committee.  */
+      t1 = build_type_no_quals (t);
+      if (t1 != t)
+	return get_alias_set (t1);
+    }
+
+  return -1;
+}
+
+
 /* Data structure used to count the number of dereferences to PTR
    inside an expression.  */
 struct count_ptr_d
@@ -3344,7 +4533,6 @@ gimple_decl_printable_name (tree decl, i
       if (verbosity >= 2)
 	{
 	  dmgl_opts = DMGL_VERBOSE
-		      | DMGL_TYPES
 		      | DMGL_ANSI
 		      | DMGL_GNU_V3
 		      | DMGL_RET_POSTFIX;
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/gimple.h /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/gimple.h
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/gimple.h	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/gimple.h	2009-09-23 11:15:59.000000000 -0400
@@ -855,6 +855,8 @@ bool gimple_assign_rhs_could_trap_p (gim
 void gimple_regimplify_operands (gimple, gimple_stmt_iterator *);
 bool empty_body_p (gimple_seq);
 unsigned get_gimple_rhs_num_ops (enum tree_code);
+#define gimple_alloc(c, n) gimple_alloc_stat (c, n MEM_STAT_INFO)
+gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL);
 const char *gimple_decl_printable_name (tree, int);
 tree gimple_fold_obj_type_ref (tree, tree);
 
@@ -913,6 +915,13 @@ extern bool is_gimple_call_addr (tree);
 extern tree get_call_expr_in (tree t);
 
 extern void recalculate_side_effects (tree);
+extern void gimple_force_type_merge (tree, tree);
+extern int gimple_types_compatible_p (tree, tree);
+extern tree gimple_register_type (tree);
+extern void print_gimple_types_stats (void);
+extern tree gimple_unsigned_type (tree);
+extern tree gimple_signed_type (tree);
+extern alias_set_type gimple_get_alias_set (tree);
 extern void count_uses_and_derefs (tree, gimple, unsigned *, unsigned *,
 				   unsigned *);
 extern bool walk_stmt_load_store_addr_ops (gimple, void *,
@@ -2912,6 +2921,16 @@ gimple_eh_must_not_throw_fndecl (gimple 
   return gs->gimple_eh_mnt.fndecl;
 }
 
+/* Set the function decl to be called by GS to DECL.  */
+
+static inline void
+gimple_eh_must_not_throw_set_fndecl (gimple gs, tree decl)
+{
+  GIMPLE_CHECK (gs, GIMPLE_EH_MUST_NOT_THROW);
+  gs->gimple_eh_mnt.fndecl = decl;
+}
+
+
 /* GIMPLE_TRY accessors. */
 
 /* Return the kind of try block represented by GIMPLE_TRY GS.  This is
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/ipa-cp.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/ipa-cp.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/ipa-cp.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/ipa-cp.c	2009-09-24 10:12:41.000000000 -0400
@@ -1273,7 +1273,13 @@ ipcp_generate_summary (void)
 static bool
 cgraph_gate_cp (void)
 {
-  return flag_ipa_cp;
+  /* FIXME lto.  IPA-CP does not tolerate running when the inlining decisions
+     have not been applied.  This happens when WPA modifies the callgraph.
+     Since those decisions are not applied until after all the IPA passes
+     have been run in LTRANS, this means that IPA passes may see partially
+     modified callgraphs.  The solution to this is to apply WPA decisions
+     early during LTRANS.  */
+  return flag_ipa_cp && !flag_ltrans;
 }
 
 struct ipa_opt_pass_d pass_ipa_cp =
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/ipa-inline.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/ipa-inline.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/ipa-inline.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/ipa-inline.c	2009-09-28 01:25:23.000000000 -0400
@@ -326,7 +326,7 @@ cgraph_mark_inline (struct cgraph_edge *
   struct cgraph_node *what = edge->callee;
   struct cgraph_edge *e, *next;
 
-  gcc_assert (!gimple_call_cannot_inline_p (edge->call_stmt));
+  gcc_assert (!edge->call_stmt_cannot_inline_p);
   /* Look for all calls, mark them inline and clone recursively
      all inlined functions.  */
   for (e = what->callers; e; e = next)
@@ -1030,7 +1030,7 @@ cgraph_decide_inlining_of_small_function
       else
 	{
 	  struct cgraph_node *callee;
-	  if (gimple_call_cannot_inline_p (edge->call_stmt)
+	  if (edge->call_stmt_cannot_inline_p
 	      || !cgraph_check_inline_limits (edge->caller, edge->callee,
 					      &edge->inline_failed, true))
 	    {
@@ -1110,7 +1110,13 @@ cgraph_decide_inlining (void)
   bool redo_always_inline = true;
   int initial_size = 0;
 
-  cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
+  /* FIXME lto.  We need to rethink how to coordinate different passes. */
+  if (flag_ltrans)
+    return 0;
+
+  /* FIXME lto.  We need to re-think about how the passes get invoked. */
+  if (!flag_wpa)
+    cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
 
   max_count = 0;
   max_benefit = 0;
@@ -1120,7 +1126,6 @@ cgraph_decide_inlining (void)
 	struct cgraph_edge *e;
 
 	gcc_assert (inline_summary (node)->self_size == node->global.size);
-	gcc_assert (node->needed || node->reachable);
 	initial_size += node->global.size;
 	for (e = node->callees; e; e = e->next_callee)
 	  if (max_count < e->count)
@@ -1128,7 +1133,9 @@ cgraph_decide_inlining (void)
 	if (max_benefit < inline_summary (node)->time_inlining_benefit)
 	  max_benefit = inline_summary (node)->time_inlining_benefit;
       }
-  gcc_assert (!max_count || (profile_info && flag_branch_probabilities));
+  gcc_assert (in_lto_p
+	      || !max_count
+	      || (profile_info && flag_branch_probabilities));
   overall_size = initial_size;
 
   nnodes = cgraph_postorder (order);
@@ -1176,8 +1183,7 @@ cgraph_decide_inlining (void)
 	  for (e = node->callers; e; e = next)
 	    {
 	      next = e->next_caller;
-	      if (!e->inline_failed
-		  || gimple_call_cannot_inline_p (e->call_stmt))
+	      if (!e->inline_failed || e->call_stmt_cannot_inline_p)
 		continue;
 	      if (cgraph_recursive_inlining_p (e->caller, e->callee,
 					       &e->inline_failed))
@@ -1224,7 +1230,7 @@ cgraph_decide_inlining (void)
 	      && node->callers->inline_failed
 	      && node->callers->caller != node
 	      && node->callers->caller->global.inlined_to != node
-	      && !gimple_call_cannot_inline_p (node->callers->call_stmt)
+	      && !node->callers->call_stmt_cannot_inline_p
 	      && !DECL_EXTERNAL (node->decl)
 	      && !DECL_COMDAT (node->decl))
 	    {
@@ -1410,7 +1416,7 @@ cgraph_decide_inlining_incrementally (st
 	if (!e->callee->local.disregard_inline_limits
 	    && (mode != INLINE_ALL || !e->callee->local.inlinable))
 	  continue;
-	if (gimple_call_cannot_inline_p (e->call_stmt))
+	if (e->call_stmt_cannot_inline_p)
 	  continue;
 	/* When the edge is already inlined, we just need to recurse into
 	   it in order to fully flatten the leaves.  */
@@ -1528,7 +1534,7 @@ cgraph_decide_inlining_incrementally (st
 	  }
 	if (!cgraph_check_inline_limits (node, e->callee, &e->inline_failed,
 				         false)
-	    || gimple_call_cannot_inline_p (e->call_stmt))
+	    || e->call_stmt_cannot_inline_p)
 	  {
 	    if (dump_file)
 	      {
@@ -1631,6 +1637,7 @@ static bool
 cgraph_gate_ipa_early_inlining (void)
 {
   return (flag_early_inlining
+	  && !in_lto_p
 	  && (flag_branch_probabilities || flag_test_coverage
 	      || profile_arc_flag));
 }
@@ -1918,6 +1925,10 @@ inline_generate_summary (void)
 {
   struct cgraph_node *node;
 
+  /* FIXME lto.  We should not run any IPA-summary pass in LTRANS mode.  */
+  if (flag_ltrans)
+    return;
+
   function_insertion_hook_holder =
       cgraph_add_function_insertion_hook (&add_new_function, NULL);
 
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/ipa-prop.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/ipa-prop.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/ipa-prop.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/ipa-prop.c	2009-09-23 11:15:26.000000000 -0400
@@ -1150,6 +1150,10 @@ bool
 ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
 				   VEC (cgraph_edge_p, heap) **new_edges)
 {
+  /* FIXME lto: We do not stream out indirect call information.  */
+  if (flag_wpa)
+    return false;
+
   /* Do nothing if the preparation phase has not been carried out yet
      (i.e. during early inlining).  */
   if (!ipa_node_params_vector)
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/ipa-pure-const.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/ipa-pure-const.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/ipa-pure-const.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/ipa-pure-const.c	2009-09-28 01:26:41.000000000 -0400
@@ -51,6 +51,7 @@ along with GCC; see the file COPYING3.  
 #include "diagnostic.h"
 #include "langhooks.h"
 #include "target.h"
+#include "lto-streamer.h"
 #include "cfgloop.h"
 #include "tree-scalar-evolution.h"
 
@@ -648,13 +649,15 @@ remove_node_data (struct cgraph_node *no
 }
 
 
-/* Analyze each function in the cgraph to see if it is locally PURE or
-   CONST.  */
-
-static void 
-generate_summary (void)
+static void
+register_hooks (void)
 {
-  struct cgraph_node *node;
+  static bool init_p = false;
+
+  if (init_p)
+    return;
+
+  init_p = true;
 
   node_removal_hook_holder =
       cgraph_add_node_removal_hook (&remove_node_data, NULL);
@@ -662,6 +665,19 @@ generate_summary (void)
       cgraph_add_node_duplication_hook (&duplicate_node_data, NULL);
   function_insertion_hook_holder =
       cgraph_add_function_insertion_hook (&add_new_function, NULL);
+}
+
+
+/* Analyze each function in the cgraph to see if it is locally PURE or
+   CONST.  */
+
+static void 
+generate_summary (void)
+{
+  struct cgraph_node *node;
+
+  register_hooks ();
+
   /* There are some shared nodes, in particular the initializers on
      static declarations.  We do not need to scan them more than once
      since all we would be interested in are the addressof
@@ -682,6 +698,120 @@ generate_summary (void)
   visited_nodes = NULL;
 }
 
+
+/* Serialize the ipa info for lto.  */
+
+static void
+pure_const_write_summary (cgraph_node_set set)
+{
+  struct cgraph_node *node;
+  struct lto_simple_output_block *ob
+    = lto_create_simple_output_block (LTO_section_ipa_pure_const);
+  unsigned int count = 0;
+  cgraph_node_set_iterator csi;
+
+  for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
+    {
+      node = csi_node (csi);
+      if (node->analyzed && get_function_state (node) != NULL)
+	count++;
+    }
+  
+  lto_output_uleb128_stream (ob->main_stream, count);
+  
+  /* Process all of the functions.  */
+  for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
+    {
+      node = csi_node (csi);
+      if (node->analyzed && get_function_state (node) != NULL)
+	{
+	  struct bitpack_d *bp;
+	  funct_state fs;
+	  int node_ref;
+	  lto_cgraph_encoder_t encoder;
+	  
+	  fs = get_function_state (node);
+
+	  encoder = ob->decl_state->cgraph_node_encoder;
+	  node_ref = lto_cgraph_encoder_encode (encoder, node);
+	  lto_output_uleb128_stream (ob->main_stream, node_ref);
+	
+	  /* Note that flags will need to be read in the opposite
+	     order as we are pushing the bitflags into FLAGS.  */
+	  bp = bitpack_create ();
+	  bp_pack_value (bp, fs->pure_const_state, 2);
+	  bp_pack_value (bp, fs->state_previously_known, 2);
+	  bp_pack_value (bp, fs->looping_previously_known, 1);
+	  bp_pack_value (bp, fs->looping, 1);
+	  bp_pack_value (bp, fs->can_throw, 1);
+	  lto_output_bitpack (ob->main_stream, bp);
+	  bitpack_delete (bp);
+	}
+    }
+
+  lto_destroy_simple_output_block (ob);
+}
+
+
+/* Deserialize the ipa info for lto.  */
+
+static void 
+pure_const_read_summary (void)
+{
+  struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  struct lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  register_hooks ();
+  while ((file_data = file_data_vec[j++]))
+    {
+      const char *data;
+      size_t len;
+      struct lto_input_block *ib
+	= lto_create_simple_input_block (file_data, 
+					 LTO_section_ipa_pure_const, 
+					 &data, &len);
+      if (ib)
+	{
+	  unsigned int i;
+	  unsigned int count = lto_input_uleb128 (ib);
+
+	  for (i = 0; i < count; i++)
+	    {
+	      unsigned int index;
+	      struct cgraph_node *node;
+	      struct bitpack_d *bp;
+	      funct_state fs;
+	      lto_cgraph_encoder_t encoder;
+
+	      fs = XCNEW (struct funct_state_d);
+	      index = lto_input_uleb128 (ib);
+	      encoder = file_data->cgraph_node_encoder;
+	      node = lto_cgraph_encoder_deref (encoder, index);
+	      set_function_state (node, fs);
+
+	      /* Note that the flags must be read in the opposite
+		 order in which they were written (the bitflags were
+		 pushed into FLAGS).  */
+	      bp = lto_input_bitpack (ib);
+	      fs->pure_const_state
+			= (enum pure_const_state_e) bp_unpack_value (bp, 2);
+	      fs->state_previously_known
+			= (enum pure_const_state_e) bp_unpack_value (bp, 2);
+	      fs->looping_previously_known = bp_unpack_value (bp, 1);
+	      fs->looping = bp_unpack_value (bp, 1);
+	      fs->can_throw = bp_unpack_value (bp, 1);
+	      bitpack_delete (bp);
+	    }
+
+	  lto_destroy_simple_input_block (file_data, 
+					  LTO_section_ipa_pure_const, 
+					  ib, data, len);
+	}
+    }
+}
+
+
 static bool
 ignore_edge (struct cgraph_edge *e)
 {
@@ -938,8 +1068,8 @@ struct ipa_opt_pass_d pass_ipa_pure_cons
   0                                     /* todo_flags_finish */
  },
  generate_summary,		        /* generate_summary */
- NULL,					/* write_summary */
- NULL,					/* read_summary */
+ pure_const_write_summary,		/* write_summary */
+ pure_const_read_summary,		/* read_summary */
  NULL,					/* function_read_summary */
  0,					/* TODOs */
  NULL,			                /* function_transform */
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/ipa-reference.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/ipa-reference.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/ipa-reference.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/ipa-reference.c	2009-09-23 10:51:21.000000000 -0400
@@ -68,6 +68,15 @@ along with GCC; see the file COPYING3.  
 #include "timevar.h"
 #include "diagnostic.h"
 #include "langhooks.h"
+#include "lto-streamer.h"
+
+static void add_new_function (struct cgraph_node *node,
+			      void *data ATTRIBUTE_UNUSED);
+static void remove_node_data (struct cgraph_node *node,
+			      void *data ATTRIBUTE_UNUSED);
+static void duplicate_node_data (struct cgraph_node *src,
+				 struct cgraph_node *dst,
+				 void *data ATTRIBUTE_UNUSED);
 
 /* The static variables defined within the compilation unit that are
    loaded or stored directly by function that owns this structure.  */ 
@@ -578,6 +587,13 @@ propagate_bits (ipa_reference_global_var
 static void 
 ipa_init (void) 
 {
+  static bool init_p = false;
+
+  if (init_p)
+    return;
+
+  init_p = true;
+
   memory_identifier_string = build_string(7, "memory");
 
   reference_vars_to_consider =
@@ -594,6 +610,13 @@ ipa_init (void) 
      since all we would be interested in are the addressof
      operations.  */
   visited_nodes = pointer_set_create ();
+
+  function_insertion_hook_holder =
+      cgraph_add_function_insertion_hook (&add_new_function, NULL);
+  node_removal_hook_holder =
+      cgraph_add_node_removal_hook (&remove_node_data, NULL);
+  node_duplication_hook_holder =
+      cgraph_add_node_duplication_hook (&duplicate_node_data, NULL);
 }
 
 /* Check out the rhs of a static or global initialization VNODE to see
@@ -614,6 +637,7 @@ analyze_variable (struct varpool_node *v
              &wi, wi.pset);
 }
 
+
 /* Set up the persistent info for FN.  */
 
 static ipa_reference_local_vars_info_t
@@ -634,9 +658,10 @@ init_function_info (struct cgraph_node *
   return l;
 }
 
+
 /* This is the main routine for finding the reference patterns for
    global variables within a function FN.  */
-  
+
 static void
 analyze_function (struct cgraph_node *fn)
 {
@@ -845,12 +870,6 @@ generate_summary (void)
   bitmap module_statics_readonly;
   bitmap bm_temp;
   
-  function_insertion_hook_holder =
-      cgraph_add_function_insertion_hook (&add_new_function, NULL);
-  node_removal_hook_holder =
-      cgraph_add_node_removal_hook (&remove_node_data, NULL);
-  node_duplication_hook_holder =
-      cgraph_add_node_duplication_hook (&duplicate_node_data, NULL);
   ipa_init ();
   module_statics_readonly = BITMAP_ALLOC (&local_info_obstack);
   bm_temp = BITMAP_ALLOC (&local_info_obstack);
@@ -986,6 +1005,141 @@ generate_summary (void)
 	     fprintf (dump_file, "\n  calls read all: ");
 	}
 }
+
+
+/* Return true if we need to write summary of NODE. */
+
+static bool
+write_node_summary_p (struct cgraph_node *node)
+{
+  return (node->analyzed 
+	  && node->global.inlined_to == NULL
+	  && cgraph_function_body_availability (node) == AVAIL_OVERWRITABLE
+	  && get_reference_vars_info (node) != NULL);
+}
+
+/* Serialize the ipa info for lto.  */
+
+static void 
+ipa_reference_write_summary (cgraph_node_set set)
+{
+  struct cgraph_node *node;
+  struct lto_simple_output_block *ob
+    = lto_create_simple_output_block (LTO_section_ipa_reference);
+  unsigned int count = 0;
+  cgraph_node_set_iterator csi;
+
+  for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
+    if (write_node_summary_p (csi_node (csi)))
+	count++;
+  
+  lto_output_uleb128_stream (ob->main_stream, count);
+  
+  /* Process all of the functions.  */
+  for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
+    {
+      node = csi_node (csi);
+      if (write_node_summary_p (node))
+	{
+	  ipa_reference_local_vars_info_t l
+	    = get_reference_vars_info (node)->local;
+	  unsigned int index;
+	  bitmap_iterator bi;
+	  lto_cgraph_encoder_t encoder;
+	  int node_ref;
+
+	  encoder = ob->decl_state->cgraph_node_encoder;
+	  node_ref = lto_cgraph_encoder_encode (encoder, node);
+	  lto_output_uleb128_stream (ob->main_stream, node_ref);
+
+	  /* Stream out the statics read.  */
+	  lto_output_uleb128_stream (ob->main_stream,
+				     bitmap_count_bits (l->statics_read));
+	  EXECUTE_IF_SET_IN_BITMAP (l->statics_read, 0, index, bi)
+	    lto_output_var_decl_index(ob->decl_state, ob->main_stream,
+				      get_static_decl (index));
+
+	  /* Stream out the statics written.  */
+	  lto_output_uleb128_stream (ob->main_stream,
+				     bitmap_count_bits (l->statics_written));
+	  EXECUTE_IF_SET_IN_BITMAP (l->statics_written, 0, index, bi)
+	    lto_output_var_decl_index(ob->decl_state, ob->main_stream,
+				      get_static_decl (index));
+	}
+    }
+  lto_destroy_simple_output_block (ob);
+}
+
+
+/* Deserialize the ipa info for lto.  */
+
+static void 
+ipa_reference_read_summary (void)
+{
+  struct lto_file_decl_data ** file_data_vec 
+    = lto_get_file_decl_data ();
+  struct lto_file_decl_data * file_data;
+  unsigned int j = 0;
+
+  ipa_init ();
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      const char *data;
+      size_t len;
+      struct lto_input_block *ib
+	= lto_create_simple_input_block (file_data, 
+					 LTO_section_ipa_reference, 
+					 &data, &len);
+      if (ib)
+	{ 
+	  unsigned int i;
+	  unsigned int f_count = lto_input_uleb128 (ib);
+
+	  for (i = 0; i < f_count; i++)
+	    {
+	      unsigned int j, index;
+	      struct cgraph_node *node;
+	      ipa_reference_local_vars_info_t l;
+	      unsigned int v_count;
+	      lto_cgraph_encoder_t encoder;
+
+	      index = lto_input_uleb128 (ib);
+	      encoder = file_data->cgraph_node_encoder;
+	      node = lto_cgraph_encoder_deref (encoder, index);
+	      l = init_function_info (node);
+
+	      /* Set the statics read.  */
+	      v_count = lto_input_uleb128 (ib);
+	      for (j = 0; j < v_count; j++)
+		{
+		  unsigned int var_index = lto_input_uleb128 (ib);
+		  tree v_decl = lto_file_decl_data_get_var_decl (file_data,
+								 var_index);
+		  add_static_var (v_decl);
+		  bitmap_set_bit (l->statics_read, DECL_UID (v_decl));
+		} 
+
+	      /* Set the statics written.  */
+	      v_count = lto_input_uleb128 (ib);
+	      for (j = 0; j < v_count; j++)
+		{
+		  unsigned int var_index = lto_input_uleb128 (ib);
+		  tree v_decl = lto_file_decl_data_get_var_decl (file_data,
+								 var_index);
+		  add_static_var (v_decl);
+		  bitmap_set_bit (l->statics_written, DECL_UID (v_decl));
+		} 
+	    }
+
+	  lto_destroy_simple_input_block (file_data, 
+					  LTO_section_ipa_reference, 
+					  ib, data, len);
+	}
+    }
+}
+
+
 
 /* Produce the global information by preforming a transitive closure
    on the local information that was produced by ipa_analyze_function
@@ -1271,8 +1425,8 @@ struct ipa_opt_pass_d pass_ipa_reference
   0                                     /* todo_flags_finish */
  },
  generate_summary,		        /* generate_summary */
- NULL,					/* write_summary */
- NULL,					/* read_summary */
+ ipa_reference_write_summary,		/* write_summary */
+ ipa_reference_read_summary,		/* read_summary */
  NULL,					/* function_read_summary */
  0,					/* TODOs */
  NULL,			                /* function_transform */
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/cgraph.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/cgraph.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/cgraph.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/cgraph.c	2009-09-23 10:51:03.000000000 -0400
@@ -802,7 +802,7 @@ initialize_inline_failed (struct cgraph_
     e->inline_failed = CIF_REDEFINED_EXTERN_INLINE;
   else if (!callee->local.inlinable)
     e->inline_failed = CIF_FUNCTION_NOT_INLINABLE;
-  else if (gimple_call_cannot_inline_p (e->call_stmt))
+  else if (e->call_stmt && gimple_call_cannot_inline_p (e->call_stmt))
     e->inline_failed = CIF_MISMATCHED_ARGUMENTS;
   else
     e->inline_failed = CIF_FUNCTION_NOT_CONSIDERED;
@@ -816,13 +816,19 @@ cgraph_create_edge (struct cgraph_node *
 {
   struct cgraph_edge *edge;
 
+
+  /* LTO does not actually have access to the call_stmt since these
+     have not been loaded yet.  */
+  if (call_stmt)
+    {
 #ifdef ENABLE_CHECKING
   /* This is rather pricely check possibly trigerring construction of call stmt
      hashtable.  */
   gcc_assert (!cgraph_edge (caller, call_stmt));
 #endif
 
-  gcc_assert (is_gimple_call (call_stmt));
+      gcc_assert (is_gimple_call (call_stmt));
+    }
 
   if (free_edges)
     {
@@ -860,7 +866,9 @@ cgraph_create_edge (struct cgraph_node *
   gcc_assert (freq <= CGRAPH_FREQ_MAX);
   edge->loop_nest = nest;
   edge->indirect_call = 0;
-  if (caller->call_site_hash)
+  edge->call_stmt_cannot_inline_p =
+    (call_stmt ? gimple_call_cannot_inline_p (call_stmt) : false);
+  if (call_stmt && caller->call_site_hash)
     {
       void **slot;
       slot = htab_find_slot_with_hash (caller->call_site_hash,
@@ -1624,8 +1632,8 @@ cgraph_function_possibly_inlined_p (tree
 /* Create clone of E in the node N represented by CALL_EXPR the callgraph.  */
 struct cgraph_edge *
 cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
-		   gimple call_stmt, gcov_type count_scale, int freq_scale,
-		   int loop_nest, bool update_original)
+		   gimple call_stmt, unsigned stmt_uid, gcov_type count_scale,
+		   int freq_scale, int loop_nest, bool update_original)
 {
   struct cgraph_edge *new_edge;
   gcov_type count = e->count * count_scale / REG_BR_PROB_BASE;
@@ -1638,6 +1646,7 @@ cgraph_clone_edge (struct cgraph_edge *e
 
   new_edge->inline_failed = e->inline_failed;
   new_edge->indirect_call = e->indirect_call;
+  new_edge->lto_stmt_uid = stmt_uid;
   if (update_original)
     {
       e->count -= new_edge->count;
@@ -1692,8 +1701,8 @@ cgraph_clone_node (struct cgraph_node *n
     }
 
   for (e = n->callees;e; e=e->next_callee)
-    cgraph_clone_edge (e, new_node, e->call_stmt, count_scale, freq, loop_nest,
-		       update_original);
+    cgraph_clone_edge (e, new_node, e->call_stmt, e->lto_stmt_uid,
+		       count_scale, freq, loop_nest, update_original);
 
   new_node->next_sibling_clone = n->clones;
   if (n->clones)
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/cgraph.h /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/cgraph.h
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/cgraph.h	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/cgraph.h	2009-09-28 02:07:35.000000000 -0400
@@ -46,6 +46,10 @@ enum availability
   AVAIL_LOCAL
 };
 
+/* This is the information that is put into the cgraph local structure
+   to recover a function.  */
+struct lto_file_decl_data;
+
 extern const char * const cgraph_availability_names[];
 
 /* Function inlining information.  */
@@ -69,6 +73,9 @@ struct GTY(()) inline_summary
    Available after function is analyzed.  */
 
 struct GTY(()) cgraph_local_info {
+  /* File stream where this node is being written to.  */
+  struct lto_file_decl_data * GTY ((skip)) lto_file_data;
+
   struct inline_summary inline_summary;
 
   /* Set when function function is visible in current compilation unit only
@@ -277,6 +284,9 @@ struct GTY((chain_next ("%h.next_caller"
   struct cgraph_edge *prev_callee;
   struct cgraph_edge *next_callee;
   gimple call_stmt;
+  /* The stmt_uid of this call stmt.  This is used by LTO to recover
+     the call_stmt when the function is serialized in.  */
+  unsigned int lto_stmt_uid;
   PTR GTY ((skip (""))) aux;
   /* When equal to CIF_OK, inline this call.  Otherwise, points to the
      explanation why function was not inlined.  */
@@ -291,6 +301,8 @@ struct GTY((chain_next ("%h.next_caller"
   unsigned int loop_nest : 30;
   /* Whether this edge describes a call that was originally indirect.  */
   unsigned int indirect_call : 1;
+  /* True if the corresponding CALL stmt cannot be inlined.  */
+  unsigned int call_stmt_cannot_inline_p : 1;
   /* Can this call throw externally?  */
   unsigned int can_throw_external : 1;
   /* Unique id of the edge.  */
@@ -406,8 +418,8 @@ struct cgraph_global_info *cgraph_global
 struct cgraph_rtl_info *cgraph_rtl_info (tree);
 const char * cgraph_node_name (struct cgraph_node *);
 struct cgraph_edge * cgraph_clone_edge (struct cgraph_edge *,
-					struct cgraph_node *,
-					gimple, gcov_type, int, int, bool);
+					struct cgraph_node *, gimple,
+					unsigned, gcov_type, int, int, bool);
 struct cgraph_node * cgraph_clone_node (struct cgraph_node *, gcov_type, int,
 					int, bool);
 
@@ -430,6 +442,7 @@ struct cgraph_node * cgraph_create_virtu
 void cgraph_finalize_function (tree, bool);
 void cgraph_mark_if_needed (tree);
 void cgraph_finalize_compilation_unit (void);
+void cgraph_optimize (void);
 void cgraph_mark_needed_node (struct cgraph_node *);
 void cgraph_mark_address_taken_node (struct cgraph_node *);
 void cgraph_mark_reachable_node (struct cgraph_node *);
@@ -449,6 +462,8 @@ struct cgraph_node *save_inline_function
 void record_references_in_initializer (tree);
 bool cgraph_process_new_functions (void);
 
+bool cgraph_decide_is_function_needed (struct cgraph_node *, tree);
+
 typedef void (*cgraph_edge_hook)(struct cgraph_edge *, void *);
 typedef void (*cgraph_node_hook)(struct cgraph_node *, void *);
 typedef void (*cgraph_2edge_hook)(struct cgraph_edge *, struct cgraph_edge *,
@@ -476,6 +491,7 @@ void cgraph_materialize_all_clones (void
 
 /* In cgraphbuild.c  */
 unsigned int rebuild_cgraph_edges (void);
+void reset_inline_failed (struct cgraph_node *);
 int compute_call_stmt_bb_frequency (tree, basic_block bb);
 
 /* In ipa.c  */
@@ -560,6 +576,22 @@ unsigned int compute_inline_parameters (
 /* Create a new static variable of type TYPE.  */
 tree add_new_static_var (tree type);
 
+/* lto-cgraph.c */
+
+enum LTO_cgraph_tags
+{
+  /* Must leave 0 for the stopper.  */
+  LTO_cgraph_avail_node = 1,
+  LTO_cgraph_overwritable_node,
+  LTO_cgraph_unavail_node,
+  LTO_cgraph_edge,
+  LTO_cgraph_last_tag
+};
+
+extern const char * LTO_cgraph_tag_names[LTO_cgraph_last_tag];
+
+#define LCC_NOT_FOUND	(-1)
+
 
 /* Return true if iterator CSI points to nothing.  */
 static inline bool
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/cgraphbuild.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/cgraphbuild.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/cgraphbuild.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/cgraphbuild.c	2009-09-27 21:25:37.000000000 -0400
@@ -78,6 +78,29 @@ record_reference (tree *tp, int *walk_su
   return NULL_TREE;
 }
 
+/* Reset inlining information of all incoming call edges of NODE.  */
+
+void
+reset_inline_failed (struct cgraph_node *node)
+{
+  struct cgraph_edge *e;
+
+  for (e = node->callers; e; e = e->next_caller)
+    {
+      e->callee->global.inlined_to = NULL;
+      if (!node->analyzed)
+	e->inline_failed = CIF_BODY_NOT_AVAILABLE;
+      else if (node->local.redefined_extern_inline)
+	e->inline_failed = CIF_REDEFINED_EXTERN_INLINE;
+      else if (!node->local.inlinable)
+	e->inline_failed = CIF_FUNCTION_NOT_INLINABLE;
+      else if (e->call_stmt_cannot_inline_p)
+	e->inline_failed = CIF_MISMATCHED_ARGUMENTS;
+      else
+	e->inline_failed = CIF_FUNCTION_NOT_CONSIDERED;
+    }
+}
+
 /* Computes the frequency of the call statement so that it can be stored in
    cgraph_edge.  BB is the basic block of the call statement.  */
 int
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/cgraphunit.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/cgraphunit.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/cgraphunit.c	2009-09-25 15:23:21.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/cgraphunit.c	2009-09-25 11:21:19.000000000 -0400
@@ -140,7 +140,6 @@ static void cgraph_expand_all_functions 
 static void cgraph_mark_functions_to_output (void);
 static void cgraph_expand_function (struct cgraph_node *);
 static void cgraph_output_pending_asms (void);
-static void cgraph_optimize (void);
 static void cgraph_analyze_function (struct cgraph_node *);
 
 static FILE *cgraph_dump_file;
@@ -314,8 +313,8 @@ cgraph_build_cdtor_fns (void)
    either outside this translation unit, something magic in the system
    configury.  */
 
-static bool
-decide_is_function_needed (struct cgraph_node *node, tree decl)
+bool
+cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
 {
   if (MAIN_NAME_P (DECL_NAME (decl))
       && TREE_PUBLIC (decl))
@@ -522,7 +521,7 @@ cgraph_finalize_function (tree decl, boo
   node->finalized_by_frontend = true;
   record_cdtor_fn (node->decl);
 
-  if (decide_is_function_needed (node, decl))
+  if (cgraph_decide_is_function_needed (node, decl))
     cgraph_mark_needed_node (node);
 
   /* Since we reclaim unreachable nodes at the end of every language
@@ -551,7 +550,7 @@ void
 cgraph_mark_if_needed (tree decl)
 {
   struct cgraph_node *node = cgraph_node (decl);
-  if (node->local.finalized && decide_is_function_needed (node, decl))
+  if (node->local.finalized && cgraph_decide_is_function_needed (node, decl))
     cgraph_mark_needed_node (node);
 }
 
@@ -692,7 +691,8 @@ verify_cgraph_node (struct cgraph_node *
 
   if (node->analyzed && gimple_has_body_p (node->decl)
       && !TREE_ASM_WRITTEN (node->decl)
-      && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to))
+      && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to)
+      && !flag_wpa)
     {
       if (this_cfun->cfg)
 	{
@@ -949,8 +949,8 @@ cgraph_analyze_functions (void)
 	  continue;
 	}
 
-      gcc_assert (!node->analyzed && node->reachable);
-      cgraph_analyze_function (node);
+      if (!node->analyzed)
+	cgraph_analyze_function (node);
 
       for (edge = node->callees; edge; edge = edge->next_callee)
 	if (!edge->callee->reachable)
@@ -1355,15 +1355,35 @@ ipa_passes (void)
   current_function_decl = NULL;
   gimple_register_cfg_hooks ();
   bitmap_obstack_initialize (NULL);
-  execute_ipa_pass_list (all_ipa_passes);
+  execute_ipa_pass_list (all_small_ipa_passes);
 
-  /* Generate coverage variables and constructors.  */
-  coverage_finish ();
+  /* If pass_all_early_optimizations was not scheduled, the state of
+     the cgraph will not be properly updated.  Update it now.  */
+  if (cgraph_state < CGRAPH_STATE_IPA_SSA)
+    cgraph_state = CGRAPH_STATE_IPA_SSA;
 
-  /* Process new functions added.  */
-  set_cfun (NULL);
-  current_function_decl = NULL;
-  cgraph_process_new_functions ();
+  /* Remove the bodies of extern inline functions that we never
+     inlined.  */
+  cgraph_remove_unreachable_nodes (false, dump_file);
+
+  if (!in_lto_p)
+    {
+      /* Generate coverage variables and constructors.  */
+      coverage_finish ();
+
+      /* Process new functions added.  */
+      set_cfun (NULL);
+      current_function_decl = NULL;
+      cgraph_process_new_functions ();
+    }
+
+  execute_ipa_summary_passes ((struct ipa_opt_pass_d *) all_regular_ipa_passes);
+  execute_ipa_summary_passes ((struct ipa_opt_pass_d *) all_lto_gen_passes);
+
+  if (!in_lto_p)
+    ipa_write_summaries ();
+
+  execute_ipa_pass_list (all_regular_ipa_passes);
 
   bitmap_obstack_release (NULL);
 }
@@ -1371,7 +1391,7 @@ ipa_passes (void)
 
 /* Perform simple optimizations based on callgraph.  */
 
-static void
+void
 cgraph_optimize (void)
 {
   if (errorcount || sorrycount)
@@ -1598,7 +1618,8 @@ cgraph_copy_node_for_versioning (struct 
       also cloned.  */
    for (e = old_version->callees;e; e=e->next_callee)
      {
-       new_e = cgraph_clone_edge (e, new_version, e->call_stmt, 0, e->frequency,
+       new_e = cgraph_clone_edge (e, new_version, e->call_stmt,
+				  e->lto_stmt_uid, 0, e->frequency,
 				  e->loop_nest, true);
        new_e->count = e->count;
      }
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/passes.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/passes.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/passes.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/passes.c	2009-09-23 11:16:00.000000000 -0400
@@ -84,6 +84,7 @@ along with GCC; see the file COPYING3.  
 #include "tree-dump.h"
 #include "df.h"
 #include "predict.h"
+#include "lto-streamer.h"
 
 #if defined (DWARF2_UNWIND_INFO) || defined (DWARF2_DEBUGGING_INFO)
 #include "dwarf2out.h"
@@ -331,7 +332,8 @@ struct rtl_opt_pass pass_postreload =
 
 
 /* The root of the compilation pass tree, once constructed.  */
-struct opt_pass *all_passes, *all_ipa_passes, *all_lowering_passes;
+struct opt_pass *all_passes, *all_small_ipa_passes, *all_lowering_passes,
+  *all_regular_ipa_passes, *all_lto_gen_passes;
 
 /* A map from static pass id to optimization pass.  */
 struct opt_pass **passes_by_id;
@@ -490,7 +492,7 @@ next_pass_1 (struct opt_pass **list, str
    If we are optimizing, cgraph_optimize is then invoked:
 
    cgraph_optimize ()
-       ipa_passes () 			-> all_ipa_passes
+       ipa_passes () 			-> all_small_ipa_passes
        cgraph_expand_all_functions ()
            for each node N in the cgraph
 	       cgraph_expand_function (N)
@@ -526,7 +528,7 @@ init_optimization_passes (void)
   *p = NULL;
 
   /* Interprocedural optimization passes.  */
-  p = &all_ipa_passes;
+  p = &all_small_ipa_passes;
   NEXT_PASS (pass_ipa_function_and_variable_visibility);
   NEXT_PASS (pass_ipa_early_inline);
     {
@@ -548,11 +550,16 @@ init_optimization_passes (void)
       NEXT_PASS (pass_referenced_vars);
       NEXT_PASS (pass_build_ssa);
       NEXT_PASS (pass_early_warn_uninitialized);
+      /* Note that it is not strictly necessary to schedule an early
+	 inline pass here.  However, some test cases (e.g.,
+	 g++.dg/other/p334435.C g++.dg/other/i386-1.C) expect extern
+	 inline functions to be inlined even at -O0.  This does not
+	 happen during the first early inline pass.  */
+      NEXT_PASS (pass_rebuild_cgraph_edges);
+      NEXT_PASS (pass_early_inline);
       NEXT_PASS (pass_all_early_optimizations);
 	{
 	  struct opt_pass **p = &pass_all_early_optimizations.pass.sub;
-	  NEXT_PASS (pass_rebuild_cgraph_edges);
-	  NEXT_PASS (pass_early_inline);
 	  NEXT_PASS (pass_remove_cgraph_callee_edges);
 	  NEXT_PASS (pass_rename_ssa_copies);
 	  NEXT_PASS (pass_ccp);
@@ -579,13 +586,22 @@ init_optimization_passes (void)
     }
   NEXT_PASS (pass_ipa_increase_alignment);
   NEXT_PASS (pass_ipa_matrix_reorg);
+  *p = NULL;
+
+  p = &all_regular_ipa_passes;
   NEXT_PASS (pass_ipa_cp);
   NEXT_PASS (pass_ipa_inline);
   NEXT_PASS (pass_ipa_reference);
   NEXT_PASS (pass_ipa_pure_const); 
   NEXT_PASS (pass_ipa_type_escape);
   NEXT_PASS (pass_ipa_pta);
-  NEXT_PASS (pass_ipa_struct_reorg);  
+  NEXT_PASS (pass_ipa_struct_reorg);
+  *p = NULL;
+
+  p = &all_lto_gen_passes;
+  NEXT_PASS (pass_ipa_lto_gimple_out);
+  NEXT_PASS (pass_ipa_lto_wpa_fixup);
+  NEXT_PASS (pass_ipa_lto_finish_out);  /* This must be the last LTO pass.  */
   *p = NULL;
 
   /* These passes are run after IPA passes on every function that is being
@@ -832,7 +848,13 @@ init_optimization_passes (void)
 
   /* Register the passes with the tree dump code.  */
   register_dump_files (all_lowering_passes, PROP_gimple_any);
-  register_dump_files (all_ipa_passes, 
+  register_dump_files (all_small_ipa_passes, 
+		       PROP_gimple_any | PROP_gimple_lcf | PROP_gimple_leh
+		       | PROP_cfg);
+  register_dump_files (all_regular_ipa_passes, 
+		       PROP_gimple_any | PROP_gimple_lcf | PROP_gimple_leh
+		       | PROP_cfg);
+  register_dump_files (all_lto_gen_passes, 
 		       PROP_gimple_any | PROP_gimple_lcf | PROP_gimple_leh
 		       | PROP_cfg);
   register_dump_files (all_passes, 
@@ -859,8 +881,11 @@ do_per_function (void (*callback) (void 
 	    push_cfun (DECL_STRUCT_FUNCTION (node->decl));
 	    current_function_decl = node->decl;
 	    callback (data);
-	    free_dominance_info (CDI_DOMINATORS);
-	    free_dominance_info (CDI_POST_DOMINATORS);
+	    if (!flag_wpa)
+	      {
+	        free_dominance_info (CDI_DOMINATORS);
+	        free_dominance_info (CDI_POST_DOMINATORS);
+	      }
 	    current_function_decl = NULL;
 	    pop_cfun ();
 	    ggc_collect ();
@@ -1173,7 +1198,7 @@ add_ipa_transform_pass (void *data)
 
 /* Execute summary generation for all of the passes in IPA_PASS.  */
 
-static void
+void
 execute_ipa_summary_passes (struct ipa_opt_pass_d *ipa_pass)
 {
   while (ipa_pass)
@@ -1182,10 +1207,21 @@ execute_ipa_summary_passes (struct ipa_o
 
       /* Execute all of the IPA_PASSes in the list.  */
       if (ipa_pass->pass.type == IPA_PASS 
-	  && (!pass->gate || pass->gate ()))
+	  && (!pass->gate || pass->gate ())
+	  && ipa_pass->generate_summary)
 	{
 	  pass_init_dump_file (pass);
+
+	  /* If a timevar is present, start it.  */
+	  if (pass->tv_id)
+	    timevar_push (pass->tv_id);
+
 	  ipa_pass->generate_summary ();
+
+	  /* Stop timevar.  */
+	  if (pass->tv_id)
+	    timevar_pop (pass->tv_id);
+
 	  pass_fini_dump_file (pass);
 	}
       ipa_pass = (struct ipa_opt_pass_d *)ipa_pass->pass.next;
@@ -1234,19 +1270,11 @@ execute_one_ipa_transform_pass (struct c
   current_pass = NULL;
 }
 
-static bool
-execute_one_pass (struct opt_pass *pass)
-{
-  bool initializing_dump;
-  unsigned int todo_after = 0;
-
-  /* IPA passes are executed on whole program, so cfun should be NULL.
-     Other passes need function context set.  */
-  if (pass->type == SIMPLE_IPA_PASS || pass->type == IPA_PASS)
-    gcc_assert (!cfun && !current_function_decl);
-  else
-    gcc_assert (cfun && current_function_decl);
+/* For the current function, execute all ipa transforms. */
 
+void
+execute_all_ipa_transforms (void)
+{
   if (cfun && cfun->ipa_transforms_to_apply)
     {
       unsigned int i;
@@ -1255,12 +1283,28 @@ execute_one_pass (struct opt_pass *pass)
       for (i = 0; i < VEC_length (ipa_opt_pass, cfun->ipa_transforms_to_apply);
 	   i++)
 	execute_one_ipa_transform_pass (node,
-				        VEC_index (ipa_opt_pass,
+					VEC_index (ipa_opt_pass,
 						   cfun->ipa_transforms_to_apply,
 						   i));
       VEC_free (ipa_opt_pass, heap, cfun->ipa_transforms_to_apply);
       cfun->ipa_transforms_to_apply = NULL;
     }
+}
+
+/* Execute PASS. */
+
+static bool
+execute_one_pass (struct opt_pass *pass)
+{
+  bool initializing_dump;
+  unsigned int todo_after = 0;
+
+  /* IPA passes are executed on whole program, so cfun should be NULL.
+     Other passes need function context set.  */
+  if (pass->type == SIMPLE_IPA_PASS || pass->type == IPA_PASS)
+    gcc_assert (!cfun && !current_function_decl);
+  else
+    gcc_assert (cfun && current_function_decl);
 
   current_pass = pass;
 
@@ -1349,26 +1393,159 @@ execute_pass_list (struct opt_pass *pass
 }
 
 /* Same as execute_pass_list but assume that subpasses of IPA passes
-   are local passes.  */
+   are local passes. If SET is not NULL, write out summaries of only
+   those node in SET. */
+
+static void
+ipa_write_summaries_2 (struct opt_pass *pass, cgraph_node_set set,
+		       struct lto_out_decl_state *state)
+{
+  while (pass)
+    {
+      struct ipa_opt_pass_d *ipa_pass = (struct ipa_opt_pass_d *)pass;
+      gcc_assert (!current_function_decl);
+      gcc_assert (!cfun);
+      gcc_assert (pass->type == SIMPLE_IPA_PASS || pass->type == IPA_PASS);
+      if (pass->type == IPA_PASS
+	  && ipa_pass->write_summary
+	  && (!pass->gate || pass->gate ()))
+	{
+	  /* If a timevar is present, start it.  */
+	  if (pass->tv_id)
+	    timevar_push (pass->tv_id);
+
+	  ipa_pass->write_summary (set);
+
+	  /* If a timevar is present, start it.  */
+	  if (pass->tv_id)
+	    timevar_pop (pass->tv_id);
+	}
+
+      if (pass->sub && pass->sub->type != GIMPLE_PASS)
+	ipa_write_summaries_2 (pass->sub, set, state);
+
+      pass = pass->next;
+    }
+}
+
+/* Helper function of ipa_write_summaries. Creates and destroys the
+   decl state and calls ipa_write_summaries_2 for all passes that have
+   summaries.  SET is the set of nodes to be written.  */
+
+static void
+ipa_write_summaries_1 (cgraph_node_set set)
+{
+  struct lto_out_decl_state *state = lto_new_out_decl_state ();
+  lto_push_out_decl_state (state);
+
+  ipa_write_summaries_2 (all_regular_ipa_passes, set, state);
+  ipa_write_summaries_2 (all_lto_gen_passes, set, state);
+
+  gcc_assert (lto_get_out_decl_state () == state);
+  lto_pop_out_decl_state ();
+  lto_delete_out_decl_state (state);
+}
+
+/* Write out summaries for all the nodes in the callgraph.  */
+
 void
-execute_ipa_pass_list (struct opt_pass *pass)
+ipa_write_summaries (void)
 {
-  bool summaries_generated = false;
-  do
+  cgraph_node_set set;
+  struct cgraph_node **order;
+  int i, order_pos;
+ 
+  if (!flag_generate_lto || errorcount || sorrycount)
+    return;
+
+  lto_new_extern_inline_states ();
+  set = cgraph_node_set_new ();
+
+  /* Create the callgraph set in the same order used in
+     cgraph_expand_all_functions.  This mostly facilitates debugging,
+     since it causes the gimple file to be processed in the same order
+     as the source code.  */
+  order = XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
+  order_pos = cgraph_postorder (order);
+  gcc_assert (order_pos == cgraph_n_nodes);
+
+  for (i = order_pos - 1; i >= 0; i--)
+    cgraph_node_set_add (set, order[i]);
+
+  ipa_write_summaries_1 (set);
+  lto_delete_extern_inline_states ();
+
+  free (order);
+  ggc_free (set);
+}
+
+
+/* Write all the summaries for the cgraph nodes in SET.  If SET is
+   NULL, write out all summaries of all nodes. */
+
+void
+ipa_write_summaries_of_cgraph_node_set (cgraph_node_set set)
+{
+  if (flag_generate_lto && !(errorcount || sorrycount))
+    ipa_write_summaries_1 (set);
+}
+
+/* Same as execute_pass_list but assume that subpasses of IPA passes
+   are local passes.  */
+
+static void
+ipa_read_summaries_1 (struct opt_pass *pass)
+{
+  while (pass)
     {
+      struct ipa_opt_pass_d *ipa_pass = (struct ipa_opt_pass_d *) pass;
+
       gcc_assert (!current_function_decl);
       gcc_assert (!cfun);
       gcc_assert (pass->type == SIMPLE_IPA_PASS || pass->type == IPA_PASS);
-      if (pass->type == IPA_PASS && (!pass->gate || pass->gate ()))
+
+      if (pass->gate == NULL || pass->gate ())
 	{
-	  if (!summaries_generated)
+	  if (pass->type == IPA_PASS && ipa_pass->read_summary)
 	    {
-	      if (!quiet_flag && !cfun)
-		fprintf (stderr, " <summary generate>");
-	      execute_ipa_summary_passes ((struct ipa_opt_pass_d *) pass);
+	      /* If a timevar is present, start it.  */
+	      if (pass->tv_id)
+		timevar_push (pass->tv_id);
+
+	      ipa_pass->read_summary ();
+
+	      /* Stop timevar.  */
+	      if (pass->tv_id)
+		timevar_pop (pass->tv_id);
 	    }
-	  summaries_generated = true;
+
+	  if (pass->sub && pass->sub->type != GIMPLE_PASS)
+	    ipa_read_summaries_1 (pass->sub);
 	}
+      pass = pass->next;
+    }
+}
+
+
+/* Read all the summaries for all_regular_ipa_passes and all_lto_gen_passes.  */
+
+void
+ipa_read_summaries (void)
+{
+  ipa_read_summaries_1 (all_regular_ipa_passes);
+  ipa_read_summaries_1 (all_lto_gen_passes);
+}
+
+/* Same as execute_pass_list but assume that subpasses of IPA passes
+   are local passes.  */
+void
+execute_ipa_pass_list (struct opt_pass *pass)
+{
+  do
+    {
+      gcc_assert (!current_function_decl);
+      gcc_assert (!cfun);
+      gcc_assert (pass->type == SIMPLE_IPA_PASS || pass->type == IPA_PASS);
       if (execute_one_pass (pass) && pass->sub)
 	{
 	  if (pass->sub->type == GIMPLE_PASS)
@@ -1380,13 +1557,46 @@ execute_ipa_pass_list (struct opt_pass *
 	  else
 	    gcc_unreachable ();
 	}
-      if (!current_function_decl)
-	cgraph_process_new_functions ();
+      gcc_assert (!current_function_decl);
+      cgraph_process_new_functions ();
       pass = pass->next;
     }
   while (pass);
 }
 
+extern void debug_properties (unsigned int);
+extern void dump_properties (FILE *, unsigned int);
+
+void
+dump_properties (FILE *dump, unsigned int props)
+{
+  fprintf (dump, "Properties:\n");
+  if (props & PROP_gimple_any)
+    fprintf (dump, "PROP_gimple_any\n");
+  if (props & PROP_gimple_lcf)
+    fprintf (dump, "PROP_gimple_lcf\n");
+  if (props & PROP_gimple_leh)
+    fprintf (dump, "PROP_gimple_leh\n");
+  if (props & PROP_cfg)
+    fprintf (dump, "PROP_cfg\n");
+  if (props & PROP_referenced_vars)
+    fprintf (dump, "PROP_referenced_vars\n");
+  if (props & PROP_ssa)
+    fprintf (dump, "PROP_ssa\n");
+  if (props & PROP_no_crit_edges)
+    fprintf (dump, "PROP_no_crit_edges\n");
+  if (props & PROP_rtl)
+    fprintf (dump, "PROP_rtl\n");
+  if (props & PROP_gimple_lomp)
+    fprintf (dump, "PROP_gimple_lomp\n");
+}
+
+void
+debug_properties (unsigned int props)
+{
+  dump_properties (stderr, props);
+}
+
 /* Called by local passes to see if function is called by already processed nodes.
    Because we process nodes in topological order, this means that function is
    in recursive cycle or we introduced new direct calls.  */
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/plugin.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/plugin.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/plugin.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/plugin.c	2009-09-23 11:15:53.000000000 -0400
@@ -420,7 +420,9 @@ register_pass (const char *plugin_name, 
   /* Try to insert the new pass to the pass lists. We need to check all
      three lists as the reference pass could be in one (or all) of them.  */
   if (!position_pass (pass_info, &all_lowering_passes)
-      && !position_pass (pass_info, &all_ipa_passes)
+      && !position_pass (pass_info, &all_small_ipa_passes)
+      && !position_pass (pass_info, &all_regular_ipa_passes)
+      && !position_pass (pass_info, &all_lto_gen_passes)
       && !position_pass (pass_info, &all_passes))
     error ("Failed to position pass %s registered by plugin %s. "
            "Cannot find the (specified instance of) reference pass %s",
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/timevar.def /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/timevar.def
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/timevar.def	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/timevar.def	2009-09-23 11:09:55.000000000 -0400
@@ -42,6 +42,15 @@ DEFTIMEVAR (TV_DUMP                  , "
 DEFTIMEVAR (TV_CGRAPH                , "callgraph construction")
 DEFTIMEVAR (TV_CGRAPHOPT             , "callgraph optimization")
 DEFTIMEVAR (TV_IPA_CONSTANT_PROP     , "ipa cp")
+DEFTIMEVAR (TV_IPA_LTO_GIMPLE_IO     , "ipa lto gimple I/O")
+DEFTIMEVAR (TV_IPA_LTO_DECL_IO       , "ipa lto decl I/O")
+DEFTIMEVAR (TV_IPA_LTO_CGRAPH_IO     , "ipa lto cgraph I/O")
+DEFTIMEVAR (TV_LTO                   , "lto")
+DEFTIMEVAR (TV_WHOPR_WPA             , "whopr wpa")
+DEFTIMEVAR (TV_WHOPR_WPA_IO          , "whopr wpa I/O")
+DEFTIMEVAR (TV_WHOPR_LTRANS          , "whopr ltrans")
+DEFTIMEVAR (TV_WHOPR_WPA_FIXUP       , "whopr wpa fixup")
+DEFTIMEVAR (TV_WHOPR_WPA_LTRANS_EXEC , "whopr wpa->ltrans")
 DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
 DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
 DEFTIMEVAR (TV_IPA_TYPE_ESCAPE       , "ipa type escape")
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree-cfg.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree-cfg.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree-cfg.c	2009-09-25 15:23:21.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree-cfg.c	2009-09-25 11:21:19.000000000 -0400
@@ -4526,7 +4526,7 @@ verify_stmt (gimple_stmt_iterator *gsi)
 
 /* Return true when the T can be shared.  */
 
-static bool
+bool
 tree_node_can_be_shared (tree t)
 {
   if (IS_TYPE_OR_DECL_P (t)
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree-flow.h /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree-flow.h
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree-flow.h	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree-flow.h	2009-09-23 11:15:55.000000000 -0400
@@ -540,6 +540,7 @@ extern basic_block move_sese_region_to_f
 				           basic_block, tree);
 void remove_edge_and_dominated_blocks (edge);
 void mark_virtual_ops_in_bb (basic_block);
+bool tree_node_can_be_shared (tree);
 
 /* In tree-cfgcleanup.c  */
 extern bitmap cfgcleanup_altered_bbs;
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree-inline.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree-inline.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree-inline.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree-inline.c	2009-09-23 11:15:47.000000000 -0400
@@ -1632,6 +1632,7 @@ copy_bb (copy_body_data *id, basic_block
 		  edge = cgraph_edge (id->src_node, orig_stmt);
 		  if (edge)
 		    edge = cgraph_clone_edge (edge, id->dst_node, stmt,
+					      gimple_uid (stmt),
 					      REG_BR_PROB_BASE, 1,
 					      edge->frequency, true);
 		  break;
@@ -5077,13 +5078,16 @@ tree_can_inline_p (struct cgraph_edge *e
     {
       e->inline_failed = CIF_TARGET_OPTION_MISMATCH;
       gimple_call_set_cannot_inline (e->call_stmt, true);
+      e->call_stmt_cannot_inline_p = true;
       return false;
     }
 
-  if (!gimple_check_call_args (e->call_stmt))
+  if (e->call_stmt
+      && !gimple_check_call_args (e->call_stmt))
     {
       e->inline_failed = CIF_MISMATCHED_ARGUMENTS;
       gimple_call_set_cannot_inline (e->call_stmt, true);
+      e->call_stmt_cannot_inline_p = true;
       return false;
     }
 
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree-optimize.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree-optimize.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree-optimize.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree-optimize.c	2009-09-28 01:37:48.000000000 -0400
@@ -87,7 +87,7 @@ static bool
 gate_all_early_local_passes (void)
 {
 	  /* Don't bother doing anything if the program has errors.  */
-  return (!errorcount && !sorrycount);
+  return (!errorcount && !sorrycount && !in_lto_p);
 }
 
 struct simple_ipa_opt_pass pass_early_local_passes =
@@ -385,6 +385,9 @@ tree_rest_of_compilation (tree fndecl)
   gimple_register_cfg_hooks ();
 
   bitmap_obstack_initialize (&reg_obstack); /* FIXME, only at RTL generation*/
+
+  execute_all_ipa_transforms ();
+
   /* Perform all tree transforms and optimizations.  */
   execute_pass_list (all_passes);
   
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree-pass.h /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree-pass.h
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree-pass.h	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree-pass.h	2009-09-23 10:51:09.000000000 -0400
@@ -411,20 +411,24 @@ extern struct gimple_opt_pass pass_trace
 extern struct gimple_opt_pass pass_warn_unused_result;
 
 /* IPA Passes */
+extern struct simple_ipa_opt_pass pass_ipa_function_and_variable_visibility;
+extern struct simple_ipa_opt_pass pass_ipa_early_inline;
+
+extern struct simple_ipa_opt_pass pass_early_local_passes;
+
+extern struct ipa_opt_pass_d pass_ipa_lto_gimple_out;
+extern struct simple_ipa_opt_pass pass_ipa_increase_alignment;
+extern struct simple_ipa_opt_pass pass_ipa_matrix_reorg;
 extern struct ipa_opt_pass_d pass_ipa_inline;
 extern struct simple_ipa_opt_pass pass_ipa_free_lang_data;
 extern struct ipa_opt_pass_d pass_ipa_cp;
 extern struct ipa_opt_pass_d pass_ipa_reference;
 extern struct ipa_opt_pass_d pass_ipa_pure_const;
-
-extern struct simple_ipa_opt_pass pass_ipa_matrix_reorg;
-extern struct simple_ipa_opt_pass pass_ipa_early_inline;
 extern struct simple_ipa_opt_pass pass_ipa_type_escape;
 extern struct simple_ipa_opt_pass pass_ipa_pta;
 extern struct simple_ipa_opt_pass pass_ipa_struct_reorg;
-extern struct simple_ipa_opt_pass pass_early_local_passes;
-extern struct simple_ipa_opt_pass pass_ipa_increase_alignment;
-extern struct simple_ipa_opt_pass pass_ipa_function_and_variable_visibility;
+extern struct ipa_opt_pass_d pass_ipa_lto_wpa_fixup;
+extern struct ipa_opt_pass_d pass_ipa_lto_finish_out;
 
 extern struct gimple_opt_pass pass_all_optimizations;
 extern struct gimple_opt_pass pass_cleanup_cfg_post_optimizing;
@@ -533,7 +537,8 @@ extern struct gimple_opt_pass pass_updat
 extern struct gimple_opt_pass pass_convert_switch;
 
 /* The root of the compilation pass tree, once constructed.  */
-extern struct opt_pass *all_passes, *all_ipa_passes, *all_lowering_passes;
+extern struct opt_pass *all_passes, *all_small_ipa_passes, *all_lowering_passes,
+                       *all_regular_ipa_passes, *all_lto_gen_passes;
 
 /* Current optimization pass.  */
 extern struct opt_pass *current_pass;
@@ -541,8 +546,15 @@ extern struct opt_pass *current_pass;
 extern struct opt_pass * get_pass_for_id (int);
 extern void execute_pass_list (struct opt_pass *);
 extern void execute_ipa_pass_list (struct opt_pass *);
+extern void execute_ipa_summary_passes (struct ipa_opt_pass_d *);
+extern void execute_all_ipa_transforms (void);
+
 extern void print_current_pass (FILE *);
 extern void debug_pass (void);
+extern void ipa_write_summaries (void);
+extern void ipa_write_summaries_of_cgraph_node_set (
+	      struct cgraph_node_set_def *);
+extern void ipa_read_summaries (void);
 extern void register_one_dump_file (struct opt_pass *);
 extern bool function_called_by_processed_nodes_p (void);
 
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree.c /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree.c
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree.c	2009-09-23 12:45:47.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree.c	2009-09-27 17:48:20.000000000 -0400
@@ -4130,6 +4130,31 @@ build_type_attribute_variant (tree ttype
 					    TYPE_QUALS (ttype));
 }
 
+
+/* Reset all the fields in a binfo node BINFO.  We only keep
+   BINFO_VIRTUALS, which is used by gimple_fold_obj_type_ref.  */
+
+static void
+free_lang_data_in_binfo (tree binfo)
+{
+  unsigned i;
+  tree t;
+
+  gcc_assert (TREE_CODE (binfo) == TREE_BINFO);
+
+  BINFO_OFFSET (binfo) = NULL_TREE;
+  BINFO_VTABLE (binfo) = NULL_TREE;
+  BINFO_VPTR_FIELD (binfo) = NULL_TREE;
+  BINFO_BASE_ACCESSES (binfo) = NULL;
+  BINFO_INHERITANCE_CHAIN (binfo) = NULL_TREE;
+  BINFO_SUBVTT_INDEX (binfo) = NULL_TREE;
+  BINFO_VPTR_FIELD (binfo) = NULL_TREE;
+
+  for (i = 0; VEC_iterate (tree, BINFO_BASE_BINFOS (binfo), i, t); i++)
+    free_lang_data_in_binfo (t);
+}
+
+
 /* Reset all language specific information still present in TYPE.  */
 
 static void
@@ -4179,9 +4204,7 @@ free_lang_data_in_type (tree type)
 	      
   /* Remove members that are not actually FIELD_DECLs from the field
      list of an aggregate.  These occur in C++.  */
-  if (TREE_CODE (type) == RECORD_TYPE
-      || TREE_CODE (type) == UNION_TYPE
-      || TREE_CODE (type) == QUAL_UNION_TYPE)
+  if (RECORD_OR_UNION_TYPE_P (type))
     {
       tree prev, member;
 
@@ -4215,30 +4238,7 @@ free_lang_data_in_type (tree type)
 
       TYPE_METHODS (type) = NULL_TREE;
       if (TYPE_BINFO (type))
-	{
-	  tree binfo = TYPE_BINFO (type);
-
-	  if (BINFO_VIRTUALS (binfo))
-	    {
-	      /* If the virtual function table for BINFO contains
-		 entries, these may be useful for folding OBJ_TYPE_REF
-		 expressions (see gimple_fold_obj_type_ref).  In that
-		 case, we only clear the unused fields in the BINFO
-		 structure.  */
-	      BINFO_OFFSET (binfo) = NULL_TREE;
-	      BINFO_VTABLE (binfo) = NULL_TREE;
-	      BINFO_VPTR_FIELD (binfo) = NULL_TREE;
-	      BINFO_BASE_ACCESSES (binfo) = NULL;
-	      BINFO_INHERITANCE_CHAIN (binfo) = NULL_TREE;
-	      BINFO_SUBVTT_INDEX (binfo) = NULL_TREE;
-	      BINFO_VPTR_FIELD (binfo) = NULL_TREE;
-	    }
-	  else
-	    {
-	      /* Otherwise, get rid of the whole binfo data.  */
-	      TYPE_BINFO (type) = NULL_TREE;
-	    }
-	}
+	free_lang_data_in_binfo (TYPE_BINFO (type));
     }
   else
     {
@@ -4287,9 +4287,14 @@ need_assembler_name_p (tree decl)
      represented in the callgraph need an assembler name.  */
   if (TREE_CODE (decl) == FUNCTION_DECL
       && cgraph_node_for_decl (decl) == NULL
-      && !TREE_USED (decl))
+      && !TREE_USED (decl)
+      && !TREE_PUBLIC (decl))
     return false;
 
+  /* Finally, ask the front end.  */
+  if (lang_hooks.decls.may_need_assembler_name_p)
+    return lang_hooks.decls.may_need_assembler_name_p (decl);
+
   return true;
 }
 
@@ -4340,9 +4345,6 @@ free_lang_data_in_decl (tree decl)
   if (DECL_NAME (decl))
     TREE_TYPE (DECL_NAME (decl)) = NULL_TREE;
 
-  if (TREE_CODE (decl) == CONST_DECL)
-    DECL_CONTEXT (decl) = NULL_TREE;
-
   /* Ignore any intervening types, because we are going to clear their
      TYPE_CONTEXT fields.  */
   if (TREE_CODE (decl) != FIELD_DECL)
@@ -4907,6 +4909,13 @@ free_lang_data (void)
   diagnostic_finalizer (global_dc) = default_diagnostic_finalizer;
   diagnostic_format_decoder (global_dc) = default_tree_printer;
 
+  /* FIXME. We remove sufficient language data that the debug
+     info writer gets completely confused.  Disable debug information
+     for now.  */
+  debug_info_level = DINFO_LEVEL_NONE;
+  write_symbols = NO_DEBUG;
+  debug_hooks = &do_nothing_debug_hooks;
+
   return 0;
 }
 
@@ -4917,7 +4926,9 @@ static bool
 gate_free_lang_data (void)
 {
   /* FIXME.  Remove after save_debug_info is working.  */
-  return !flag_gtoggle && debug_info_level <= DINFO_LEVEL_TERSE;
+  return (flag_generate_lto
+	  || (!in_lto_p
+	      && !flag_gtoggle && debug_info_level <= DINFO_LEVEL_TERSE));
 }
 
 
@@ -10113,9 +10124,7 @@ walk_tree_1 (tree *tp, walk_tree_fn func
 	    return result;
 
 	  /* If this is a record type, also walk the fields.  */
-	  if (TREE_CODE (*type_p) == RECORD_TYPE
-	      || TREE_CODE (*type_p) == UNION_TYPE
-	      || TREE_CODE (*type_p) == QUAL_UNION_TYPE)
+	  if (RECORD_OR_UNION_TYPE_P (*type_p))
 	    {
 	      tree field;
 
diff -rdupN --exclude=.svn --exclude=.git --exclude='*.diff*' --exclude='autom4te*' --exclude=tags --exclude=ChangeLog.lto --exclude=configure /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree.h /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree.h
--- /usr/local/google/homedirs/dnovillo/gcc/trunk/gcc/tree.h	2009-09-25 15:23:21.000000000 -0400
+++ /usr/local/google/homedirs/dnovillo/gcc/trunk.lto/gcc/tree.h	2009-09-25 11:21:19.000000000 -0400
@@ -4902,7 +4902,8 @@ extern bool validate_arglist (const_tree
 extern rtx builtin_memset_read_str (void *, HOST_WIDE_INT, enum machine_mode);
 extern bool can_trust_pointer_alignment (void);
 extern int get_pointer_alignment (tree, unsigned int);
-extern bool is_builtin_name (const char*);
+extern bool is_builtin_name (const char *);
+extern bool is_builtin_fn (tree);
 extern int get_object_alignment (tree, unsigned int, unsigned int);
 extern tree fold_call_stmt (gimple, bool);
 extern tree gimple_fold_builtin_snprintf_chk (gimple, tree, enum built_in_function);
@@ -4910,6 +4911,7 @@ extern tree make_range (tree, int *, tre
 extern tree build_range_check (location_t, tree, tree, int, tree, tree);
 extern bool merge_ranges (int *, tree *, tree *, int, tree, tree, int, 
 			  tree, tree);
+extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
 
 /* In convert.c */
 extern tree strip_float_extensions (tree);


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