diff mbox series

Optimize ODR enum streaming

Message ID 20200603191031.GA66831@kam.mff.cuni.cz
State New
Headers show
Series Optimize ODR enum streaming | expand

Commit Message

Jan Hubicka June 3, 2020, 7:10 p.m. UTC
Hi,
it turns out that half of the global decl stream of cc1 LTO build consits
TREE_LISTS, identifiers and integer cosntats representing TYPE_VALUES of enums.
Those are streamed only to produce ODR warning and used otherwise, so this
patch moves the info to a separate section that is represented and streamed
more effectively.

This also adds place for more info that may be used for ODR diagnostics
(i.e. at the moment we do not warn when the declarations differs i.e. by the
associated member functions and their types) and the type inheritance graph
rather then poluting the global stream.

I was bit unsure what enums we want to store into the section.  All parsed
enums is probably too expensive, only those enums streamed to represent IL is
bit hard to get, so I went for those seen by free lang data.

As a plus we now get bit more precise warning because also the location of
mismatched enum CONST_DECL is streamed.

It changes:
[WPA] read 4608466 unshared trees
[WPA] read 2942094 mergeable SCCs of average size 1.365328
[WPA] 8625389 tree bodies read in total
[WPA] tree SCC table: size 524287, 247652 elements, collision ratio: 0.383702
[WPA] tree SCC max chain length 2 (size 1)
[WPA] Compared 2694442 SCCs, 228 collisions (0.000085)
[WPA] Merged 2694419 SCCs
[WPA] Merged 3731982 tree bodies
[WPA] Merged 633335 types
[WPA] 122077 types prevailed (155548 associated trees)
...
[WPA] Compression: 110593119 input bytes, 287696614 uncompressed bytes (ratio: 2.601397)
[WPA] Size of mmap'd section decls: 85628556 bytes
[WPA] Size of mmap'd section function_body: 13842928 bytes


[WPA] read 1720989 unshared trees
[WPA] read 1252217 mergeable SCCs of average size 1.858507
[WPA] 4048243 tree bodies read in total
[WPA] tree SCC table: size 524287, 226524 elements, collision ratio: 0.491759
[WPA] tree SCC max chain length 2 (size 1)
[WPA] Compared 1025693 SCCs, 196 collisions (0.000191)
[WPA] Merged 1025670 SCCs
[WPA] Merged 2063373 tree bodies
[WPA] Merged 633497 types
[WPA] 122299 types prevailed (155827 associated trees)
...
[WPA] Compression: 103428770 input bytes, 281151423 uncompressed bytes (ratio: 2.718310)
[WPA] Size of mmap'd section decls: 49390917 bytes
[WPA] Size of mmap'd section function_body: 13858258 bytes
...
[WPA] Size of mmap'd section odr_types: 29054816 bytes

So number of SCCs streamed drops to 38% and the number of unshared trees (that
are bit misnamed since it is mostly integer_cst) to 37%.

Things speeds up correspondingly, but I did not save time report from previous
build.

The enum values are still quite surprisingly large (29MB compressed). 
I may take a look into ways getting it smaller incrementally, but it
streams reasonably fast:

Time variable                                   usr           sys          wall               GGC
 phase opt and generate             :  25.20 ( 68%)  10.88 ( 72%)  36.13 ( 69%)  868060 kB ( 52%)
 phase stream in                    :   4.46 ( 12%)   0.90 (  6%)   5.38 ( 10%)  790724 kB ( 48%)
 phase stream out                   :   6.69 ( 18%)   3.32 ( 22%)  10.03 ( 19%)       8 kB (  0%)
 ipa lto gimple in                  :   0.79 (  2%)   1.86 ( 12%)   2.39 (  5%)  252612 kB ( 15%)
 ipa lto gimple out                 :   2.48 (  7%)   0.78 (  5%)   3.26 (  6%)       0 kB (  0%)
 ipa lto decl in                    :   1.71 (  5%)   0.46 (  3%)   2.34 (  4%)  417883 kB ( 25%)
 ipa lto decl out                   :   3.28 (  9%)   0.07 (  0%)   3.27 (  6%)       0 kB (  0%)
 whopr wpa I/O                      :   0.40 (  1%)   2.24 ( 15%)   2.77 (  5%)       8 kB (  0%)
 lto stream decompression           :   1.38 (  4%)   0.31 (  2%)   1.36 (  3%)       0 kB (  0%)
 ipa ODR types                      :   0.18 (  0%)   0.02 (  0%)   0.25 (  0%)       0 kB (  0%)
 ipa inlining heuristics            :  11.64 ( 31%)   1.45 ( 10%)  13.12 ( 25%)  453160 kB ( 27%)
 ipa pure const                     :   1.74 (  5%)   0.00 (  0%)   1.76 (  3%)       0 kB (  0%)
 ipa icf                            :   1.72 (  5%)   5.33 ( 35%)   7.06 ( 13%)   16593 kB (  1%)
 whopr partitioning                 :   2.22 (  6%)   0.01 (  0%)   2.23 (  4%)    5689 kB (  0%)
 TOTAL                              :  37.17         15.20         52.46        1660886 kB

LTO-bootstrapped/regtested x86_64-linux, will comit it shortly.

gcc/ChangeLog:

2020-06-03  Jan Hubicka  <hubicka@ucw.cz>

	* ipa-devirt.c: Include data-streamer.h, lto-streamer.h and
	streamer-hooks.h.
	(odr_enums): New static var.
	(struct odr_enum_val): New struct.
	(class odr_enum): New struct.
	(odr_enum_map): New hashtable.
	(odr_types_equivalent_p): Drop code testing TYPE_VALUES.
	(add_type_duplicate): Likewise.
	(free_odr_warning_data): Do not free TYPE_VALUES.
	(register_odr_enum): New function.
	(ipa_odr_summary_write): New function.
	(ipa_odr_read_section): New function.
	(ipa_odr_summary_read): New function.
	(class pass_ipa_odr): New pass.
	(make_pass_ipa_odr): New function.
	* ipa-utils.h (register_odr_enum): Declare.
	* lto-section-in.c: (lto_section_name): Add odr_types section.
	* lto-streamer.h (enum lto_section_type): Add odr_types section.
	* passes.def: Add odr_types pass.
	* lto-streamer-out.c (DFS::DFS_write_tree_body): Do not stream
	TYPE_VALUES.
	(hash_tree): Likewise.
	* tree-streamer-in.c (lto_input_ts_type_non_common_tree_pointers):
	Likewise.
	* tree-streamer-out.c (write_ts_type_non_common_tree_pointers):
	Likewise.
	* timevar.def (TV_IPA_ODR): New timervar.
	* tree-pass.h (make_pass_ipa_odr): Declare.
	* tree.c (free_lang_data_in_type): Regiser ODR types.

gcc/lto/ChangeLog:

2020-06-03  Jan Hubicka  <hubicka@ucw.cz>

	* lto-common.c (compare_tree_sccs_1): Do not compare TYPE_VALUES.

gcc/testsuite/ChangeLog:

2020-06-03  Jan Hubicka  <hubicka@ucw.cz>

	* g++.dg/lto/pr84805_0.C: Update.
diff mbox series

Patch

diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index bd9f3441773..8e36ff1ea1d 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -88,7 +88,7 @@  along with GCC; see the file COPYING3.  If not see
      This means that the graph is not complete. Types with no methods are not
      inserted into the graph.  Also types without virtual methods are not
      represented at all, though it may be easy to add this.
- 
+
      The inheritance graph is represented as follows:
 
        Vertices are structures odr_type.  Every odr_type may correspond
@@ -131,6 +131,9 @@  along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "data-streamer.h"
+#include "lto-streamer.h"
+#include "streamer-hooks.h"
 
 /* Hash based set of pairs of types.  */
 struct type_pair
@@ -495,6 +498,29 @@  static odr_hash_type *odr_hash;
 static GTY(()) vec <odr_type, va_gc> *odr_types_ptr;
 #define odr_types (*odr_types_ptr)
 
+/* All enums defined and accessible for the unit.  */
+static GTY(()) vec <tree, va_gc> *odr_enums;
+
+/* Information we hold about value defined by an enum type.  */
+struct odr_enum_val
+{
+  const char *name;
+  HOST_WIDE_INT val;
+  location_t locus;
+};
+
+/* Information about enum values.  */
+struct odr_enum
+{
+  location_t locus;
+  auto_vec<odr_enum_val, 0> vals;
+  bool warned;
+};
+
+/* A table of all ODR enum definitions.  */
+static hash_map <nofree_string_hash, odr_enum> *odr_enum_map = NULL;
+static struct obstack odr_enum_obstack;
+
 /* Set TYPE_BINFO of TYPE and its variants to BINFO.  */
 void
 set_type_binfo (tree type, tree binfo)
@@ -1205,37 +1231,6 @@  odr_types_equivalent_p (tree t1, tree t2, bool warn, bool *warned,
       return false;
     }
 
-  if (TREE_CODE (t1) == ENUMERAL_TYPE
-      && TYPE_VALUES (t1) && TYPE_VALUES (t2))
-    {
-      tree v1, v2;
-      for (v1 = TYPE_VALUES (t1), v2 = TYPE_VALUES (t2);
-	   v1 && v2 ; v1 = TREE_CHAIN (v1), v2 = TREE_CHAIN (v2))
-	{
-	  if (TREE_PURPOSE (v1) != TREE_PURPOSE (v2))
-	    {
-	      warn_odr (t1, t2, NULL, NULL, warn, warned,
-			G_("an enum with different value name"
-			   " is defined in another translation unit"));
-	      return false;
-	    }
-	  if (!operand_equal_p (TREE_VALUE (v1), TREE_VALUE (v2), 0))
-	    {
-	      warn_odr (t1, t2, NULL, NULL, warn, warned,
-			G_("an enum with different values is defined"
-			   " in another translation unit"));
-	      return false;
-	    }
-	}
-      if (v1 || v2)
-	{
-	  warn_odr (t1, t2, NULL, NULL, warn, warned,
-		    G_("an enum with mismatching number of values "
-		       "is defined in another translation unit"));
-	  return false;
-	}
-    }
-
   /* Non-aggregate types can be handled cheaply.  */
   if (INTEGRAL_TYPE_P (t1)
       || SCALAR_FLOAT_TYPE_P (t1)
@@ -1622,10 +1617,6 @@  add_type_duplicate (odr_type val, tree type)
     }
   else if (COMPLETE_TYPE_P (val->type) && !COMPLETE_TYPE_P (type))
     ;
-  else if (TREE_CODE (val->type) == ENUMERAL_TYPE
-	   && TREE_CODE (type) == ENUMERAL_TYPE
-	   && !TYPE_VALUES (val->type) && TYPE_VALUES (type))
-    prevail = true;
   else if (TREE_CODE (val->type) == RECORD_TYPE
 	   && TREE_CODE (type) == RECORD_TYPE
 	   && TYPE_BINFO (type) && !TYPE_BINFO (val->type))
@@ -1974,7 +1965,7 @@  get_odr_type (tree type, bool insert)
       unsigned int i;
 
       gcc_assert (BINFO_TYPE (TYPE_BINFO (val->type)) == type);
-  
+
       val->all_derivations_known = type_all_derivations_known_p (type);
       for (i = 0; i < BINFO_N_BASE_BINFOS (binfo); i++)
 	/* For now record only polymorphic types. other are
@@ -2219,7 +2210,7 @@  dump_type_inheritance_graph (FILE *f)
 
 /* Save some WPA->ltrans streaming by freeing stuff needed only for good
    ODR warnings.
-   We free TYPE_VALUES of enums and also make TYPE_DECLs to not point back
+   We make TYPE_DECLs to not point back
    to the type (which is needed to keep them in the same SCC and preserve
    location information to output warnings) and subsequently we make all
    TYPE_DECLS of same assembler name equivalent.  */
@@ -2239,8 +2230,6 @@  free_odr_warning_data ()
       {
 	tree t = odr_types[i]->type;
 
-	if (TREE_CODE (t) == ENUMERAL_TYPE)
-	  TYPE_VALUES (t) = NULL;
 	TREE_TYPE (TYPE_NAME (t)) = void_type_node;
 
 	if (odr_types[i]->types)
@@ -2248,8 +2237,6 @@  free_odr_warning_data ()
 	    {
 	      tree td = (*odr_types[i]->types)[j];
 
-	      if (TREE_CODE (td) == ENUMERAL_TYPE)
-	        TYPE_VALUES (td) = NULL;
 	      TYPE_NAME (td) = TYPE_NAME (t);
 	    }
       }
@@ -2283,7 +2270,7 @@  build_type_inheritance_graph (void)
       get_odr_type (TYPE_METHOD_BASETYPE (TREE_TYPE (n->decl)), true);
 
     /* Look also for virtual tables of types that do not define any methods.
- 
+
        We need it in a case where class B has virtual base of class A
        re-defining its virtual method and there is class C with no virtual
        methods with B as virtual base.
@@ -2600,7 +2587,7 @@  record_target_from_binfo (vec <cgraph_node *> &nodes,
    INSERTED is used to avoid duplicate insertions of methods into NODES.
    MATCHED_VTABLES are used to avoid duplicate walking vtables.
    Clear COMPLETEP if unreferable target is found.
- 
+
    If CONSIDER_CONSTRUCTION is true, record to BASES_TO_CONSIDER
    all cases where BASE_SKIPPED is true (because the base is abstract
    class).  */
@@ -2800,7 +2787,7 @@  subbinfo_with_vtable_at_offset (tree binfo, unsigned HOST_WIDE_INT offset,
 	  && DECL_ASSEMBLER_NAME (v) == DECL_ASSEMBLER_NAME (vtable))
 	return binfo;
     }
-  
+
   for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
     if (polymorphic_type_binfo_p (base_binfo))
       {
@@ -4016,4 +4003,331 @@  debug_tree_odr_name (tree type, bool demangle)
   fprintf (stderr, "%s\n", odr);
 }
 
+/* Register ODR enum so we later stream record about its values.  */
+
+void
+register_odr_enum (tree t)
+{
+  if (flag_lto)
+    vec_safe_push (odr_enums, t);
+}
+
+/* Write ODR enums to LTO stream file.  */
+
+static void
+ipa_odr_summary_write (void)
+{
+  if (!odr_enums && !odr_enum_map)
+    return;
+  struct output_block *ob = create_output_block (LTO_section_odr_types);
+  unsigned int i;
+  tree t;
+
+  if (odr_enums)
+    {
+      streamer_write_uhwi (ob, odr_enums->length ());
+
+      /* For every ODR enum stream out
+	   - its ODR name
+	   - number of values,
+	   - value names and constant their represent
+	   - bitpack of locations so we can do good diagnostics.  */
+      FOR_EACH_VEC_ELT (*odr_enums, i, t)
+	{
+	  streamer_write_string (ob, ob->main_stream,
+				 IDENTIFIER_POINTER
+				     (DECL_ASSEMBLER_NAME (TYPE_NAME (t))),
+				 true);
+
+	  int n = 0;
+	  for (tree e = TYPE_VALUES (t); e; e = TREE_CHAIN (e))
+	    n++;
+	  streamer_write_uhwi (ob, n);
+	  for (tree e = TYPE_VALUES (t); e; e = TREE_CHAIN (e))
+	    {
+	      streamer_write_string (ob, ob->main_stream,
+				     IDENTIFIER_POINTER (TREE_PURPOSE (e)),
+				     true);
+	      streamer_write_hwi (ob, tree_to_shwi
+					(DECL_INITIAL (TREE_VALUE (e))));
+	    }
+
+	  bitpack_d bp = bitpack_create (ob->main_stream);
+	  lto_output_location (ob, &bp, DECL_SOURCE_LOCATION (TYPE_NAME (t)));
+	  for (tree e = TYPE_VALUES (t); e; e = TREE_CHAIN (e))
+	    lto_output_location (ob, &bp,
+				 DECL_SOURCE_LOCATION (TREE_VALUE (e)));
+	  streamer_write_bitpack (&bp);
+	}
+      vec_free (odr_enums);
+      odr_enums = NULL;
+    }
+  /* During LTO incremental linking we already have streamed in types.  */
+  else if (odr_enum_map)
+    {
+      gcc_checking_assert (!odr_enums);
+      streamer_write_uhwi (ob, odr_enum_map->elements ());
+
+      hash_map<nofree_string_hash, odr_enum>::iterator iter
+		= odr_enum_map->begin ();
+      for (; iter != odr_enum_map->end (); ++iter)
+	{
+	  odr_enum &this_enum = (*iter).second;
+	  streamer_write_string (ob, ob->main_stream, (*iter).first, true);
+
+	  streamer_write_uhwi (ob, this_enum.vals.length ());
+	  for (unsigned j = 0; j < this_enum.vals.length (); j++)
+	    {
+	      streamer_write_string (ob, ob->main_stream,
+				     this_enum.vals[j].name, true);
+	      streamer_write_hwi (ob, this_enum.vals[j].val);
+	    }
+
+	  bitpack_d bp = bitpack_create (ob->main_stream);
+	  lto_output_location (ob, &bp, this_enum.locus);
+	  for (unsigned j = 0; j < this_enum.vals.length (); j++)
+	    lto_output_location (ob, &bp, this_enum.vals[j].locus);
+	  streamer_write_bitpack (&bp);
+	}
+
+      delete odr_enum_map;
+      obstack_free (&odr_enum_obstack, NULL);
+      odr_enum_map = NULL;
+    }
+
+  produce_asm (ob, NULL);
+  destroy_output_block (ob);
+}
+
+/* Write ODR enums from LTO stream file and warn on mismatches.  */
+
+static void
+ipa_odr_read_section (struct lto_file_decl_data *file_data, const char *data,
+		      size_t len)
+{
+  const struct lto_function_header *header
+    = (const struct lto_function_header *) data;
+  const int cfg_offset = sizeof (struct lto_function_header);
+  const int main_offset = cfg_offset + header->cfg_size;
+  const int string_offset = main_offset + header->main_size;
+  class data_in *data_in;
+
+  lto_input_block ib ((const char *) data + main_offset, header->main_size,
+		      file_data->mode_table);
+
+  data_in
+    = lto_data_in_create (file_data, (const char *) data + string_offset,
+			  header->string_size, vNULL);
+  unsigned int n = streamer_read_uhwi (&ib);
+
+  if (!odr_enum_map)
+    {
+      gcc_obstack_init (&odr_enum_obstack);
+      odr_enum_map = new (hash_map <nofree_string_hash, odr_enum>);
+    }
+
+  for (unsigned i = 0; i < n; i++)
+    {
+      const char *rname = streamer_read_string (data_in, &ib);
+      unsigned int nvals = streamer_read_uhwi (&ib);
+      char *name;
+  
+      obstack_grow (&odr_enum_obstack, rname, strlen (rname) + 1);
+      name = XOBFINISH (&odr_enum_obstack, char *);
+
+      bool existed_p;
+      class odr_enum &this_enum
+		 = odr_enum_map->get_or_insert (xstrdup (name), &existed_p);
+
+      if (!existed_p)
+	{
+	  this_enum.vals.safe_grow_cleared (nvals);
+	  this_enum.warned = false;
+	  for (unsigned j = 0; j < nvals; j++)
+	    {
+	      const char *val_name = streamer_read_string (data_in, &ib);
+	      obstack_grow (&odr_enum_obstack, val_name, strlen (val_name) + 1);
+	      this_enum.vals[j].name = XOBFINISH (&odr_enum_obstack, char *);
+	      this_enum.vals[j].val = streamer_read_hwi (&ib);
+	    }
+	  bitpack_d bp = streamer_read_bitpack (&ib);
+	  stream_input_location (&this_enum.locus, &bp, data_in);
+	  for (unsigned j = 0; j < nvals; j++)
+	    stream_input_location (&this_enum.vals[j].locus, &bp, data_in);
+	  data_in->location_cache.apply_location_cache ();
+	}
+      else
+	{
+	  int do_warning = -1;
+	  char *warn_name = NULL;
+	  HOST_WIDE_INT warn_value = 0;
+
+	  for (unsigned j = 0; j < nvals; j++)
+	    {
+	      const char *id = streamer_read_string (data_in, &ib);
+	      HOST_WIDE_INT val = streamer_read_hwi (&ib);
+
+	      if (do_warning != -1 || j > this_enum.vals.length ())
+		continue;
+	      if (strcmp (id, this_enum.vals[j].name)
+		  || val != this_enum.vals[j].val)
+		{
+		  warn_name = xstrdup (id);
+		  warn_value = val;
+		  do_warning = j;
+		}
+	    }
+	  bitpack_d bp = streamer_read_bitpack (&ib);
+
+	  location_t locus;
+	  stream_input_location (&locus, &bp, data_in);
+
+	  if (do_warning != -1 || nvals != this_enum.vals.length ())
+	    {
+	      data_in->location_cache.apply_location_cache ();
+
+	      const int opts = DMGL_PARAMS | DMGL_ANSI | DMGL_TYPES;
+	      char *dmgname = cplus_demangle (name, opts);
+	      if (this_enum.warned
+		  || !warning_at (this_enum.locus,
+				  OPT_Wodr, "type %qs violates the "
+				  "C++ One Definition Rule",
+				  dmgname))
+		do_warning = -1;
+	      else
+	       {
+		 this_enum.warned = true;
+		 if (do_warning == -1)
+		   inform (locus,
+			   "an enum with different number of values is defined"
+			   " in another translation unit");
+		 else if (warn_name)
+		   inform (locus,
+			   "an enum with different value name"
+			   " is defined in another translation unit");
+		 else
+		   inform (locus,
+			   "an enum with different values"
+			   " is defined in another translation unit");
+	       }
+	    }
+	  else
+	    data_in->location_cache.revert_location_cache ();
+	  for (unsigned j = 0; j < nvals; j++)
+	    {
+	      location_t id_locus;
+
+	      data_in->location_cache.revert_location_cache ();
+	      stream_input_location (&id_locus, &bp, data_in);
+	      if ((int) j == do_warning)
+		{
+		  data_in->location_cache.apply_location_cache ();
+		  if (strcmp (warn_name, this_enum.vals[j].name))
+		    inform (this_enum.vals[j].locus,
+			    "name %qs differs from name %qs defined"
+			    " in another translation unit",
+			    this_enum.vals[j].name, warn_name);
+		  else
+		    inform (this_enum.vals[j].locus,
+			    "name %qs is defined to " HOST_WIDE_INT_PRINT_DEC
+			    " while another translation unit defines "
+			    "it as " HOST_WIDE_INT_PRINT_DEC,
+			    warn_name, this_enum.vals[j].val, warn_value);
+		  inform (id_locus,
+			  "mismatching definition");
+		}
+	      else
+	        data_in->location_cache.revert_location_cache ();
+	    }
+	  if (warn_name)
+	    free (warn_name);
+	  obstack_free (&odr_enum_obstack, name);
+	}
+    }
+  lto_free_section_data (file_data, LTO_section_ipa_fn_summary, NULL, data,
+			 len);
+  lto_data_in_delete (data_in);
+}
+
+/* Read all ODR type sections.  */
+
+static void
+ipa_odr_summary_read (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;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      size_t len;
+      const char *data
+	= lto_get_summary_section_data (file_data, LTO_section_odr_types,
+					&len);
+      if (data)
+	ipa_odr_read_section (file_data, data, len);
+    }
+  /* Enum info is used only to produce warnings.  Only case we will need it
+     again is streaming for incremental LTO.  */
+  if (flag_incremental_link != INCREMENTAL_LINK_LTO)
+    {
+      delete odr_enum_map;
+      obstack_free (&odr_enum_obstack, NULL);
+      odr_enum_map = NULL;
+    }
+}
+
+namespace {
+
+const pass_data pass_data_ipa_odr =
+{
+  IPA_PASS, /* type */
+  "odr", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_IPA_ODR, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_ipa_odr : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_odr (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_odr, ctxt,
+		      NULL, /* generate_summary */
+		      ipa_odr_summary_write, /* write_summary */
+		      ipa_odr_summary_read, /* read_summary */
+		      NULL, /* write_optimization_summary */
+		      NULL, /* read_optimization_summary */
+		      NULL, /* stmt_fixup */
+		      0, /* function_transform_todo_flags_start */
+		      NULL, /* function_transform */
+		      NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+    {
+      return (in_lto_p || flag_lto);
+    }
+
+  virtual unsigned int execute (function *)
+    {
+      return 0;
+    }
+
+}; // class pass_ipa_odr
+
+} // anon namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_odr (gcc::context *ctxt)
+{
+  return new pass_ipa_odr (ctxt);
+}
+
+
 #include "gt-ipa-devirt.h"
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index d0ac3ec86f8..178c2cbe446 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -98,6 +98,8 @@  void enable_odr_based_tbaa (tree type);
 bool odr_based_tbaa_p (const_tree type);
 void set_type_canonical_for_odr_type (tree type, tree canonical);
 
+void register_odr_enum (tree type);
+
 /* Return vector containing possible targets of polymorphic call E.
    If COMPLETEP is non-NULL, store true if the list is complete. 
    CACHE_TOKEN (if non-NULL) will get stored to an unique ID of entry
diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
index 0923a8c0746..48cf4844b63 100644
--- a/gcc/lto-section-in.c
+++ b/gcc/lto-section-in.c
@@ -55,7 +55,8 @@  const char *lto_section_name[LTO_N_SECTION_TYPES] =
   "mode_table",
   "hsa",
   "lto",
-  "ipa_sra"
+  "ipa_sra",
+  "odr_types",
 };
 
 /* Hooks so that the ipa passes can call into the lto front end to get
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c
index f71d3f81662..059688821cc 100644
--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -1000,9 +1000,7 @@  DFS::DFS_write_tree_body (struct output_block *ob,
 
   if (CODE_CONTAINS_STRUCT (code, TS_TYPE_NON_COMMON))
     {
-      if (TREE_CODE (expr) == ENUMERAL_TYPE)
-	DFS_follow_tree_edge (TYPE_VALUES (expr));
-      else if (TREE_CODE (expr) == ARRAY_TYPE)
+      if (TREE_CODE (expr) == ARRAY_TYPE)
 	DFS_follow_tree_edge (TYPE_DOMAIN (expr));
       else if (RECORD_OR_UNION_TYPE_P (expr))
 	for (tree t = TYPE_FIELDS (expr); t; t = TREE_CHAIN (t))
@@ -1413,9 +1411,7 @@  hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *map,
 
   if (CODE_CONTAINS_STRUCT (code, TS_TYPE_NON_COMMON))
     {
-      if (code == ENUMERAL_TYPE)
-	visit (TYPE_VALUES (t));
-      else if (code == ARRAY_TYPE)
+      if (code == ARRAY_TYPE)
 	visit (TYPE_DOMAIN (t));
       else if (RECORD_OR_UNION_TYPE_P (t))
 	for (tree f = TYPE_FIELDS (t); f; f = TREE_CHAIN (f))
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 9e0031e5ba3..b17137f0f9a 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -227,6 +227,7 @@  enum lto_section_type
   LTO_section_ipa_hsa,
   LTO_section_lto,
   LTO_section_ipa_sra,
+  LTO_section_odr_types,
   LTO_N_SECTION_TYPES		/* Must be last.  */
 };
 
diff --git a/gcc/lto/lto-common.c b/gcc/lto/lto-common.c
index 3ea1894ce96..86d8851dac7 100644
--- a/gcc/lto/lto-common.c
+++ b/gcc/lto/lto-common.c
@@ -1503,9 +1503,7 @@  compare_tree_sccs_1 (tree t1, tree t2, tree **map)
 
   if (CODE_CONTAINS_STRUCT (code, TS_TYPE_NON_COMMON))
     {
-      if (code == ENUMERAL_TYPE)
-	compare_tree_edges (TYPE_VALUES (t1), TYPE_VALUES (t2));
-      else if (code == ARRAY_TYPE)
+      if (code == ARRAY_TYPE)
 	compare_tree_edges (TYPE_DOMAIN (t1), TYPE_DOMAIN (t2));
       else if (RECORD_OR_UNION_TYPE_P (t1))
 	{
diff --git a/gcc/passes.def b/gcc/passes.def
index 92cbe587a8a..56322025226 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -145,6 +145,7 @@  along with GCC; see the file COPYING3.  If not see
 
   INSERT_PASSES_AFTER (all_regular_ipa_passes)
   NEXT_PASS (pass_analyzer);
+  NEXT_PASS (pass_ipa_odr);
   NEXT_PASS (pass_ipa_whole_program_visibility);
   NEXT_PASS (pass_ipa_profile);
   NEXT_PASS (pass_ipa_icf);
diff --git a/gcc/testsuite/g++.dg/lto/pr84805_0.C b/gcc/testsuite/g++.dg/lto/pr84805_0.C
index c9e00add79f..1509eae4845 100644
--- a/gcc/testsuite/g++.dg/lto/pr84805_0.C
+++ b/gcc/testsuite/g++.dg/lto/pr84805_0.C
@@ -11,7 +11,7 @@  struct __is_void_helper : false_type {};
 struct is_void : __is_void_helper {};
 template < typename > struct is_array : false_type {};
 namespace __gnu_cxx {
-enum _Lock_policy { _S_single, _S_mutex, _S_atomic }; // { dg-lto-warning "6: type '_Lock_policy' violates the C\\+\\+ One Definition Rule" }
+enum _Lock_policy { _S_single, _S_mutex, _S_atomic }; // { dg-lto-warning "6: type '__gnu_cxx::_Lock_policy' violates the C\\+\\+ One Definition Rule" }
 const _Lock_policy __default_lock_policy = _S_atomic;
 } namespace std {
 using __gnu_cxx::_Lock_policy;
diff --git a/gcc/timevar.def b/gcc/timevar.def
index f467d0251f7..29479205c41 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -70,6 +70,7 @@  DEFTIMEVAR (TV_CGRAPH                , "callgraph construction")
 DEFTIMEVAR (TV_CGRAPHOPT             , "callgraph optimization")
 DEFTIMEVAR (TV_CGRAPH_FUNC_EXPANSION , "callgraph functions expansion")
 DEFTIMEVAR (TV_CGRAPH_IPA_PASSES     , "callgraph ipa passes")
+DEFTIMEVAR (TV_IPA_ODR		     , "ipa ODR types")
 DEFTIMEVAR (TV_IPA_FNSUMMARY         , "ipa function summary")
 DEFTIMEVAR (TV_IPA_UNREACHABLE       , "ipa dead code removal")
 DEFTIMEVAR (TV_IPA_INHERITANCE       , "ipa inheritance graph")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 576b3f67434..396428f167f 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -506,6 +506,7 @@  extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_sra (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
+extern ipa_opt_pass_d *make_pass_ipa_odr (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_hsa (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 447a3110e82..707a0591bfe 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -843,9 +843,7 @@  lto_input_ts_type_non_common_tree_pointers (class lto_input_block *ib,
 					    class data_in *data_in,
 					    tree expr)
 {
-  if (TREE_CODE (expr) == ENUMERAL_TYPE)
-    TYPE_VALUES (expr) = stream_read_tree_ref (ib, data_in);
-  else if (TREE_CODE (expr) == ARRAY_TYPE)
+  if (TREE_CODE (expr) == ARRAY_TYPE)
     TYPE_DOMAIN (expr) = stream_read_tree_ref (ib, data_in);
   else if (RECORD_OR_UNION_TYPE_P (expr))
     TYPE_FIELDS (expr) = streamer_read_chain (ib, data_in);
diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c
index 98631789cc3..d7a451cfef4 100644
--- a/gcc/tree-streamer-out.c
+++ b/gcc/tree-streamer-out.c
@@ -724,9 +724,7 @@  write_ts_type_common_tree_pointers (struct output_block *ob, tree expr)
 static void
 write_ts_type_non_common_tree_pointers (struct output_block *ob, tree expr)
 {
-  if (TREE_CODE (expr) == ENUMERAL_TYPE)
-    stream_write_tree_ref (ob, TYPE_VALUES (expr));
-  else if (TREE_CODE (expr) == ARRAY_TYPE)
+  if (TREE_CODE (expr) == ARRAY_TYPE)
     stream_write_tree_ref (ob, TYPE_DOMAIN (expr));
   else if (RECORD_OR_UNION_TYPE_P (expr))
     streamer_write_chain (ob, TYPE_FIELDS (expr));
diff --git a/gcc/tree.c b/gcc/tree.c
index 2cc9e4f66e3..7197b4720ce 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -5556,40 +5556,19 @@  free_lang_data_in_type (tree type, class free_lang_data_d *fld)
     {
       if (TREE_CODE (type) == ENUMERAL_TYPE)
 	{
-	  tree it = NULL_TREE;
 	  ENUM_IS_OPAQUE (type) = 0;
 	  ENUM_IS_SCOPED (type) = 0;
 	  /* Type values are used only for C++ ODR checking.  Drop them
 	     for all type variants and non-ODR types.
 	     For ODR types the data is freed in free_odr_warning_data.  */
-	  if (TYPE_MAIN_VARIANT (type) != type
-	      || !type_with_linkage_p (type))
+	  if (!TYPE_VALUES (type))
+	    ;
+	  else if (TYPE_MAIN_VARIANT (type) != type
+		   || !type_with_linkage_p (type)
+		   || type_in_anonymous_namespace_p (type))
 	    TYPE_VALUES (type) = NULL;
 	  else
-	  /* Simplify representation by recording only values rather
-	     than const decls.  */
-	    for (tree e = TYPE_VALUES (type); e; e = TREE_CHAIN (e))
-	      {
-		if (TREE_CODE (TREE_VALUE (e)) == CONST_DECL)
-		  {
-		    TREE_VALUE (e) = DECL_INITIAL (TREE_VALUE (e));
-		    /* We can not stream values whose TREE_TYPE is type itself
-		       because that would create non-trivial CSS.  Canonicalize
-		       them to integer types.  */
-		  }
-		/* Some frontends use ENUMERAL_TYPE to represent the constants.
-		   This leads to nontrivial SCC components containing
-		   INTEGER_CST which is not good for streaming.  Convert them
-		   all to corresponding integer type.  */
-		if (TREE_CODE (TREE_TYPE (TREE_VALUE (e))) != INTEGER_TYPE)
-		  {
-		    if (!it)
-		      it = lang_hooks.types.type_for_size
-			       (TYPE_PRECISION (TREE_TYPE (TREE_VALUE (e))),
-				TYPE_UNSIGNED (TREE_TYPE (TREE_VALUE (e))));
-		    TREE_VALUE (e) = fold_convert (it, TREE_VALUE (e));
-		  }
-	       }
+	    register_odr_enum (type);
 	}
       free_lang_data_in_one_sizepos (&TYPE_MIN_VALUE (type));
       free_lang_data_in_one_sizepos (&TYPE_MAX_VALUE (type));