On Mon, 3 Jun 2019, Jan Hubicka wrote: > Hi, > this patch makes LTO to use ODR names when building canonical types. > Theoretically this is easy task because every ODR type is unique and > it is enough to fill in the hash with the ODR names and compare them. > > In reality we want to be more careful and detect situation when non-ODR > type is structurally equivalent to ODR type so C++ code and non-C++ code > can co-exist. > > I have implemented it as follows > 1) at stream in time populate canonical type hash by non-ODR types > only. Also register all ODR types into so we detect ODR violations. > 2) once all streaming is done we process all ODR types, lookup each > of them in the canonical type hash and if structurally compatible > type is present default to normal canonical type calculation. > Otherwise canonical type of ODR type is the prevailing ODR variant.
This sounds sensible, while ... > Now the problem is that in C++ some structures can be non-ODR. For > example > > struct A {int a;} a; > struct {int b;} b; > > Here the second structure has no name and we have no way to tell if it > is C++ or non-C++ code at LTO time. I tought that perhaps such conflicts > would be rare, but in reality most of types conflicts this way > (C++ FE seems to generate non-ODR types of same strucutre as ODR > types and they do get streamed, I am looking into this). > So more careful approach is needed. > > I extende extend step 1 to also postpone types that reffers to ODR types > (those > are known to originate from C++ translation units and do not need to be > unified > by structurally equivalent ODR types). > > This is tested by odr_or_derived_type_p which is kind of hack and perhaps we > should get around streaming this info somehow (but i would preffer to do that > incrementally) > > Step 2 remain same. > Additional step 3 registers all odr derived types into canonical type hash. > This requires canonical type hash to play well with ODR types (i.e. not > consider them equivalent based on structural equivalety). > > The decision on whether given type has ODR based canonical > type is stored in odr_based_tbaa_p and is used > 1) by gimple_canonical_types_compatible_p so the comparsion is > behaving well after ODR types are inserted into the hash > 2) by alias.c to be build more precise alias sets - for ODR types > it is not necessary to glob pointers into void *. > There is no need to update canonical type hash since I simply insert > hash codes of ODR names into canonical type hash cache. ICK. Can you please solve the C++ issue differently? The patch also seems to do many other things ... More comments inline. > > LTO linking cc1plus there are 3317 ODR types that gets their own > canonical type and 876 which have conflict. For example: > > ODR and non-ODR type conflict: union ssa_name_info_type and union > { > const void * cv; > void * v; > } > ODR and non-ODR type conflict: struct ht_identifier and struct > { > union byte_fail_stack_elt_t * stack; > unsigned int size; > unsigned int avail; > } > ODR and non-ODR type conflict: struct lang_hooks_for_tree_dump and struct > { > struct demangle_component * left; > struct demangle_component * right; > } > ODR and non-ODR type conflict: struct call_site_record_d and struct > { > struct demangle_component * sub; > int num; > } > > etc. > > So mostly random conflict induced by the C compiled liberty and other bits. > Clearly most common issue is globing of pointers and lack of field names > which is needed primarily for Fortran and is easy to disable for translation > units > not having Fortran modules in them. > > Canonical type hash stats for cc1plus build: > > [WPA] GIMPLE canonical type table: size 16381, 1590 elements, 6797 searches, > 88 collisions (ratio: 0.012947) > [WPA] GIMPLE canonical type pointer-map: 1590 elements, 15494 searches > > to: > > [WPA] GIMPLE canonical type table: size 16381, 755 elements, 6311 searches, > 57 collisions (ratio: 0.009032) > [WPA] GIMPLE canonical type pointer-map: 755 elements, 15553 searches > > So without patch there are 1590 distinct canonical types, with the patch there > are 755 non-ODR and 3317 odr types, about 2.5 times more. > > And alias oracle stats for -flto-partition=none cc1plus build: > > Alias oracle query stats: > refs_may_alias_p: 39227004 disambiguations, 47344870 queries > ref_maybe_used_by_call_p: 57815 disambiguations, 39808081 queries > call_may_clobber_ref_p: 5511 disambiguations, 8287 queries > aliasing_component_ref_p: 90654 disambiguations, 269895 queries > TBAA oracle: 11130153 disambiguations 34368560 queries > 14199938 are in alias set 0 > 5193428 queries asked about the same object > 147 queries asked about the same alias set > 0 access volatile > 1665979 are dependent in the DAG > 2178915 are aritificially in conflict with void * > > PTA query stats: > pt_solution_includes: 464074 disambiguations, 7105649 queries > pt_solutions_intersect: 355139 disambiguations, 6952492 queries > > > Alias oracle query stats: > refs_may_alias_p: 39682205 disambiguations, 47774305 queries > ref_maybe_used_by_call_p: 58390 disambiguations, 40265537 queries > call_may_clobber_ref_p: 5619 disambiguations, 8397 queries > aliasing_component_ref_p: 61523 disambiguations, 240208 queries > TBAA oracle: 11691755 disambiguations 34552651 queries > 14233568 are in alias set 0 > 5230754 queries asked about the same object > 147 queries asked about the same alias set > 0 access volatile > 2433081 are dependent in the DAG > 963346 are aritificially in conflict with void * > > PTA query stats: > pt_solution_includes: 457871 disambiguations, 7044729 queries > pt_solutions_intersect: 367701 disambiguations, 6859208 queries > > So about 455k new disambiguations or 5% more TBAA ones. > It is about half of what PTA oracle stats shows, so it seems > reasibale improvement to me. > > The patch results in about 1% cc1plus code size increase. > (mostly because of less ICF) > > For tramp3d the stats are: > > refs_may_alias_p: 3252729 disambiguations, 3587196 queries > ref_maybe_used_by_call_p: 7018 disambiguations, 3280028 queries > call_may_clobber_ref_p: 883 disambiguations, 883 queries > aliasing_component_ref_p: 549 disambiguations, 17885 queries > TBAA oracle: 1644568 disambiguations 3261681 queries > 555812 are in alias set 0 > 680883 queries asked about the same object > 0 queries asked about the same alias set > 0 access volatile > 248396 are dependent in the DAG > 132022 are aritificially in conflict with void * > > PTA query stats: > pt_solution_includes: 641563 disambiguations, 921034 queries > pt_solutions_intersect: 115861 disambiguations, 501205 queries > > Alias oracle query stats: > refs_may_alias_p: 3016277 disambiguations, 3316301 queries > ref_maybe_used_by_call_p: 7134 disambiguations, 3042055 queries > call_may_clobber_ref_p: 817 disambiguations, 817 queries > aliasing_component_ref_p: 495 disambiguations, 14565 queries > TBAA oracle: 1416803 disambiguations 2911585 queries > 552489 are in alias set 0 > 567158 queries asked about the same object > 0 queries asked about the same alias set > 0 access volatile > 259923 are dependent in the DAG > 115212 are aritificially in conflict with void * > > PTA query stats: > pt_solution_includes: 668779 disambiguations, 949986 queries > pt_solutions_intersect: 96232 disambiguations, 436617 queries > > About 7% difference. > > I did Firefox stats with earlier variant of this patch and difference was > about 12%. > It takes about a day for lto1-ltrans with one partition to converge (and two > days for for gas) so I did not re-run it with final version. Code size > difference is about 4%. > > Notice also that access path oracle now suceeds less often since > we less often mix up unrealted types. In general with more precist > TBAA lookups the oracle should be bit cheaper by avoiding more complex > tests. > > lto-bootstrapped/regtested x86_64-linux with all languages and also > tested on few extra C++ apps. > > * alias.c (record_component_aliases): Honor odr_based_tbaa_p. > * ipa-devirt.c (odr_type_d): Add tbaa_enabled field. > (get_odr_type): Return NULL when odr_type_hash is NULL. > (enable_odr_based_tbaa): New function. > (odr_based_tbaa_p): New function. > (set_type_canonical_for_odr_type): New function. > * ipa-utils.h (enable_odr_based_tbaa, odr_based_tbaa_p, > set_type_canonical_for_odr_type): Declare. > * lto-common.c: Update copyright; include tree-pretty-print.h. > (types_to_register): New static var. > (iterative_hash_canonical_type): Add new parameter INSERT. > (hash_canonical_type): Likewise. > (lookup_canonical_type): New function. > (lto_register_canonical_types_for_odr_types): New. > (odr_or_derived_type_p): New function. > (lto_read_decls): Deffer ODR and ODR derived types > to later canonical type calculation. > * tree.c (gimple_canonical_types_compatible_p): Honnor > odr_based_tbaa_p. > Index: alias.c > =================================================================== > --- alias.c (revision 271843) > +++ alias.c (working copy) > @@ -1202,47 +1202,52 @@ record_component_aliases (tree type) > case RECORD_TYPE: > case UNION_TYPE: > case QUAL_UNION_TYPE: > - for (field = TYPE_FIELDS (type); field != 0; field = DECL_CHAIN > (field)) > - if (TREE_CODE (field) == FIELD_DECL && !DECL_NONADDRESSABLE_P (field)) > - { > - /* LTO type merging does not make any difference between > - component pointer types. We may have > - > - struct foo {int *a;}; > - > - as TYPE_CANONICAL of > - > - struct bar {float *a;}; > - > - Because accesses to int * and float * do not alias, we would get > - false negative when accessing the same memory location by > - float ** and bar *. We thus record the canonical type as: > - > - struct {void *a;}; > - > - void * is special cased and works as a universal pointer type. > - Accesses to it conflicts with accesses to any other pointer > - type. */ > - tree t = TREE_TYPE (field); > - if (in_lto_p) > - { > - /* VECTOR_TYPE and ARRAY_TYPE share the alias set with their > - element type and that type has to be normalized to void *, > - too, in the case it is a pointer. */ > - while (!canonical_type_used_p (t) && !POINTER_TYPE_P (t)) > - { > - gcc_checking_assert (TYPE_STRUCTURAL_EQUALITY_P (t)); > - t = TREE_TYPE (t); > - } > - if (POINTER_TYPE_P (t)) > - t = ptr_type_node; > - else if (flag_checking) > - gcc_checking_assert (get_alias_set (t) > - == get_alias_set (TREE_TYPE (field))); > - } > - > - record_alias_subset (superset, get_alias_set (t)); > - } > + { > + /* LTO non-ODR type merging does not make any difference between > + component pointer types. We may have > + > + struct foo {int *a;}; > + > + as TYPE_CANONICAL of > + > + struct bar {float *a;}; > + > + Because accesses to int * and float * do not alias, we would get > + false negative when accessing the same memory location by > + float ** and bar *. We thus record the canonical type as: > + > + struct {void *a;}; > + > + void * is special cased and works as a universal pointer type. > + Accesses to it conflicts with accesses to any other pointer > + type. */ > + bool void_pointers = in_lto_p > + && (!odr_type_p (type) > + || !odr_based_tbaa_p (type)); This seems like an independent improvement to me. > + for (field = TYPE_FIELDS (type); field != 0; field = DECL_CHAIN (field)) > + if (TREE_CODE (field) == FIELD_DECL && !DECL_NONADDRESSABLE_P (field)) > + { > + tree t = TREE_TYPE (field); > + if (void_pointers) > + { > + /* VECTOR_TYPE and ARRAY_TYPE share the alias set with their > + element type and that type has to be normalized to void *, > + too, in the case it is a pointer. */ > + while (!canonical_type_used_p (t) && !POINTER_TYPE_P (t)) > + { > + gcc_checking_assert (TYPE_STRUCTURAL_EQUALITY_P (t)); > + t = TREE_TYPE (t); > + } > + if (POINTER_TYPE_P (t)) > + t = ptr_type_node; > + else if (flag_checking) > + gcc_checking_assert (get_alias_set (t) > + == get_alias_set (TREE_TYPE (field))); > + } > + > + record_alias_subset (superset, get_alias_set (t)); > + } > + } > break; > > case COMPLEX_TYPE: > Index: ipa-devirt.c > =================================================================== > --- ipa-devirt.c (revision 271843) > +++ ipa-devirt.c (working copy) > @@ -213,6 +213,8 @@ struct GTY(()) odr_type_d > bool odr_violated; > /* Set when virtual table without RTTI previaled table with. */ > bool rtti_broken; > + /* Set when we need to give up on ODR based TBAA info. */ > + bool tbaa_enabled; ? > }; > > /* Return TRUE if all derived types of T are known and thus > @@ -2045,6 +2047,9 @@ get_odr_type (tree type, bool insert) > if (!in_lto_p && !TYPE_STRUCTURAL_EQUALITY_P (type)) > type = TYPE_CANONICAL (type); > > + if (!odr_hash) > + return NULL; > + > gcc_checking_assert (can_be_name_hashed_p (type) > || can_be_vtable_hashed_p (type)); > > @@ -2182,6 +2187,45 @@ prevailing_odr_type (tree type) > return t->type; > } > > +/* Set tbaa_enabled flag for TYPE. */ > + > +void > +enable_odr_based_tbaa (tree type) > +{ > + odr_type t = get_odr_type (type, true); > + t->tbaa_enabled = true; > +} > + > +/* Return type that in ODR type hash prevailed TYPE. Be careful and punt > + on ODR violations. */ > + > +bool > +odr_based_tbaa_p (const_tree type) > +{ > + odr_type t = get_odr_type (const_cast <tree> (type), false); > + if (!t || !t->tbaa_enabled) > + return false; > + return true; > +} > + > +/* Set TYPE_CANONICAL of type and all its variants and duplicates > + to CANONICAL. */ > + > +void > +set_type_canonical_for_odr_type (tree type, tree canonical) > +{ > + odr_type t = get_odr_type (type, false); > + unsigned int i; > + tree tt; > + > + for (tree t2 = t->type; t2; t2 = TYPE_NEXT_VARIANT (t2)) > + TYPE_CANONICAL (t2) = canonical; > + if (t->types) > + FOR_EACH_VEC_ELT (*t->types, i, tt) > + for (tree t2 = tt; t2; t2 = TYPE_NEXT_VARIANT (t2)) > + TYPE_CANONICAL (t2) = canonical; > +} > + > /* Return true if we reported some ODR violation on TYPE. */ > > bool > Index: ipa-utils.h > =================================================================== > --- ipa-utils.h (revision 271843) > +++ ipa-utils.h (working copy) > @@ -93,6 +93,9 @@ bool odr_or_derived_type_p (const_tree t > bool odr_types_equivalent_p (tree type1, tree type2); > bool odr_type_violation_reported_p (tree type); > tree prevailing_odr_type (tree type); > +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); > > /* Return vector containing possible targets of polymorphic call E. > If COMPLETEP is non-NULL, store true if the list is complete. > Index: lto/lto-common.c > =================================================================== > --- lto/lto-common.c (revision 271843) > +++ lto/lto-common.c (working copy) > @@ -1,5 +1,5 @@ > /* Top-level LTO routines. > - Copyright (C) 2009-2018 Free Software Foundation, Inc. > + Copyright (C) 2009-2019 Free Software Foundation, Inc. > Contributed by CodeSourcery, Inc. > > This file is part of GCC. > @@ -56,6 +56,7 @@ along with GCC; see the file COPYING3. > #include "attribs.h" > #include "builtins.h" > #include "lto-common.h" > +#include "tree-pretty-print.h" > > GTY(()) tree first_personality_decl; > > @@ -212,22 +213,26 @@ lto_read_in_decl_state (struct data_in * > > > /* Global canonical type table. */ > +static GTY(()) vec<tree, va_gc> *types_to_register = NULL; > static htab_t gimple_canonical_types; > static hash_map<const_tree, hashval_t> *canonical_type_hash_cache; > static unsigned long num_canonical_type_hash_entries; > static unsigned long num_canonical_type_hash_queries; > > -static void iterative_hash_canonical_type (tree type, inchash::hash &hstate); > +static void iterative_hash_canonical_type (tree type, inchash::hash &hstate, > bool); > static hashval_t gimple_canonical_type_hash (const void *p); > static void gimple_register_canonical_type_1 (tree t, hashval_t hash); > > /* Returning a hash value for gimple type TYPE. > > The hash value returned is equal for types considered compatible > - by gimple_canonical_types_compatible_p. */ > + by gimple_canonical_types_compatible_p. > + > + If INSERT is true, also populate canonical type hash by all > + types TYPE is derived from. */ > > static hashval_t > -hash_canonical_type (tree type) > +hash_canonical_type (tree type, bool insert) > { > inchash::hash hstate; > enum tree_code code; > @@ -293,7 +298,7 @@ hash_canonical_type (tree type) > if (TREE_CODE (type) == ARRAY_TYPE > || TREE_CODE (type) == COMPLEX_TYPE > || TREE_CODE (type) == VECTOR_TYPE) > - iterative_hash_canonical_type (TREE_TYPE (type), hstate); > + iterative_hash_canonical_type (TREE_TYPE (type), hstate, insert); > > /* Incorporate function return and argument types. */ > if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) > @@ -301,11 +306,11 @@ hash_canonical_type (tree type) > unsigned na; > tree p; > > - iterative_hash_canonical_type (TREE_TYPE (type), hstate); > + iterative_hash_canonical_type (TREE_TYPE (type), hstate, insert); > > for (p = TYPE_ARG_TYPES (type), na = 0; p; p = TREE_CHAIN (p)) > { > - iterative_hash_canonical_type (TREE_VALUE (p), hstate); > + iterative_hash_canonical_type (TREE_VALUE (p), hstate, insert); > na++; > } > > @@ -322,7 +327,7 @@ hash_canonical_type (tree type) > && (! DECL_SIZE (f) > || ! integer_zerop (DECL_SIZE (f)))) > { > - iterative_hash_canonical_type (TREE_TYPE (f), hstate); > + iterative_hash_canonical_type (TREE_TYPE (f), hstate, insert); > nf++; > } > > @@ -332,10 +337,11 @@ hash_canonical_type (tree type) > return hstate.end(); > } > > -/* Returning a hash value for gimple type TYPE combined with VAL. */ > +/* Returning a hash value for gimple type TYPE combined with VAL. > + If INSERT is true possibly insert TYPE to canonical type hash. */ > > static void > -iterative_hash_canonical_type (tree type, inchash::hash &hstate) > +iterative_hash_canonical_type (tree type, inchash::hash &hstate, bool insert) > { > hashval_t v; > > @@ -343,7 +349,7 @@ iterative_hash_canonical_type (tree type > type = TYPE_MAIN_VARIANT (type); > > if (!canonical_type_used_p (type)) > - v = hash_canonical_type (type); > + v = hash_canonical_type (type, insert); > /* An already processed type. */ > else if (TYPE_CANONICAL (type)) > { > @@ -355,9 +361,11 @@ iterative_hash_canonical_type (tree type > /* Canonical types should not be able to form SCCs by design, this > recursion is just because we do not register canonical types in > optimal order. To avoid quadratic behavior also register the > - type here. */ > - v = hash_canonical_type (type); > - gimple_register_canonical_type_1 (type, v); > + type here. Do not assign canonical types to ODR types - this > + is done later using ODR name hash. */ > + v = hash_canonical_type (type, insert); > + if (insert) > + gimple_register_canonical_type_1 (type, v); So the comment explains that gimple_register_canonical_type_1 is to avoid quadratic behavior for types referenced multiple times. You remove this and I see why but I fear this will cause issues. > } > hstate.add_int (v); > } > @@ -437,12 +445,26 @@ gimple_register_canonical_type (tree t) > TYPE_CANONICAL (t) = TYPE_CANONICAL (TYPE_MAIN_VARIANT (t)); > else > { > - hashval_t h = hash_canonical_type (TYPE_MAIN_VARIANT (t)); > + hashval_t h = hash_canonical_type (TYPE_MAIN_VARIANT (t), true); > gimple_register_canonical_type_1 (TYPE_MAIN_VARIANT (t), h); > TYPE_CANONICAL (t) = TYPE_CANONICAL (TYPE_MAIN_VARIANT (t)); > } > } > > +/* See if structurally equivalent type to T is in the canonical type hash and > + return it. Return NULL otherwise. > + No changes to canonical hash are made during the lookup. */ > + > +static tree > +lookup_canonical_type (tree t) > +{ > + hashval_t h = hash_canonical_type (TYPE_MAIN_VARIANT (t), false); > + void **slot = htab_find_slot_with_hash (gimple_canonical_types, t, h, > NO_INSERT); > + if (!slot) > + return NULL; > + return (tree)(*slot); > +} > + > /* Re-compute TYPE_CANONICAL for NODE and related types. */ > > static void > @@ -464,6 +486,107 @@ lto_register_canonical_types (tree node, > gimple_register_canonical_type (node); > } > > +/* Finish canonical type calculation: after all units has been streamed in we > + can check if given ODR type structurally conflicts with a non-ODR type. > In > + the first case we set type canonical according to the canonical type hash. > + In the second case we use type names. */ > + > +static void > +lto_register_canonical_types_for_odr_types () > +{ > + tree t; > + unsigned int i; > + > + if (!types_to_register) > + return; > + > + /* Be sure that no types derived from ODR types was > + not inserted into the hash table. */ > + if (flag_checking) > + FOR_EACH_VEC_ELT (*types_to_register, i, t) > + gcc_assert (!TYPE_CANONICAL (t)); > + > + FOR_EACH_VEC_ELT (*types_to_register, i, t) > + if (odr_type_p (t) && !TYPE_CANONICAL (t)) > + { > + /* Punt on ODR violations - if there are structurally different > + types of the same name we are better to not try too hard to > + use TBAA. */ > + if (odr_type_violation_reported_p (t)) > + { > + if (symtab->dump_file) > + { > + fprintf (symtab->dump_file, > + "Disabling ODR TBAA because of ODR violation: "); > + print_generic_expr (symtab->dump_file, t); > + fprintf (symtab->dump_file, "\n"); > + } But you don't actually do anything to the type? > + } > + else > + { > + tree nonodr = type_in_anonymous_namespace_p (t) > + ? NULL : lookup_canonical_type (t); > + > + /* If there is non-ODR type matching T, use its canonical > + type. We can still propagate it to all variants of T > + including incomplete ones. */ > + if (nonodr) > + { > + gcc_checking_assert (!odr_type_p (nonodr)); > + if (symtab->dump_file) > + { > + fprintf (symtab->dump_file, > + "ODR and non-ODR type conflict: "); > + print_generic_expr (symtab->dump_file, t); > + fprintf (symtab->dump_file, " and "); > + print_generic_expr (symtab->dump_file, nonodr); > + fprintf (symtab->dump_file, "\n"); > + } > + set_type_canonical_for_odr_type (t, nonodr); > + } > + else > + { > + if (symtab->dump_file) > + { > + fprintf (symtab->dump_file, > + "New canonical ODR type: "); > + print_generic_expr (symtab->dump_file, t); > + fprintf (symtab->dump_file, "\n"); > + } > + > + tree prevail = prevailing_odr_type (t); > + gcc_checking_assert (TYPE_CANONICAL (prevail) == NULL); > + > + /* Populate canonical type hash with type name. */ > + bool existed = canonical_type_hash_cache->put > + (prevail, > + htab_hash_string > + (IDENTIFIER_POINTER > + (DECL_ASSEMBLER_NAME > + (TYPE_NAME (prevail))))); but referencing types used a different hash value for this type? (computed multiple times, see that quadraticness issue) Why use a different hash value and why not insert the hash in the cache during first processing? > + gcc_assert (!existed); > + /* Set TYPE_CANONICAL for all variants and duplicates of T > + including incompete ones. */ > + set_type_canonical_for_odr_type (t, prevail); > + /* And inform gimple_canonical_types_compatible_p that > + it should rely on TYPE_CANONICAL compares only. */ > + enable_odr_based_tbaa (t); > + gcc_checking_assert (TYPE_CANONICAL (t) = prevail); > + } > + } > + } > + > + /* Now compute canonical types for all ODR derived types. */ > + FOR_EACH_VEC_ELT (*types_to_register, i, t) > + if (!TYPE_CANONICAL (t)) > + { > + gimple_register_canonical_type (t); note this will re-compute the hash value the old way. > + /* We store only main variants; propagate info to all variants. */ > + for (tree t2 = TYPE_NEXT_VARIANT (t); t2; t2 = TYPE_NEXT_VARIANT (t2)) > + TYPE_CANONICAL (t2) = TYPE_CANONICAL (t); > + } > +} > + > > /* Remember trees that contains references to declarations. */ > vec <tree, va_gc> *tree_with_vars; > @@ -1655,6 +1778,33 @@ unify_scc (struct data_in *data_in, unsi > } > > > +/* TYPE is expected to be record or union. Figure out if it is an ODR > + type or derived from it. This is intended to be used only from > + stream in process and rely on fact that all types with TYPE_CANONICAL > + set are not ODR derived. This reducts most of recursive lookups. */ Still don't understand this issue fully but I don't really like this... iff then the FE should set this and we should stream it as extra bit. > +static bool > +odr_or_derived_type_p (tree type) > +{ > + if (TYPE_CANONICAL (type)) > + return false; > + if (odr_type_p (TYPE_MAIN_VARIANT (type)) > + || (TYPE_CONTEXT (type) > + && TYPE_P (TYPE_CONTEXT (type)) > + && odr_type_p (TYPE_CONTEXT (type)))) > + return true; > + for (tree f = TYPE_FIELDS (type); f; f = TREE_CHAIN (f)) > + { > + tree t = TREE_TYPE (f); > + while (TREE_CODE (t) == ARRAY_TYPE) > + t = TREE_TYPE (t); > + if (RECORD_OR_UNION_TYPE_P (t) > + && odr_or_derived_type_p (t)) > + return true; > + } > + return false; > +} > + > /* Read all the symbols from buffer DATA, using descriptors in DECL_DATA. > RESOLUTIONS is the set of symbols picked by the linker (read from the > resolution file when the linker plugin is being used). */ > @@ -1747,12 +1897,24 @@ lto_read_decls (struct lto_file_decl_dat > num_prevailing_types++; > lto_fixup_prevailing_type (t); > > - /* Compute the canonical type of all types. > + /* Compute the canonical type of all non-ODR types. > + Delay ODR types for the end of merging process - the > canonical > + type for those can be computed using the (unique) name > however > + we want to do this only if units in other languages do not > + contain structurally equivalent type. > + > Because SCC components are streamed in random (hash) order > we may have encountered the type before while registering > type canonical of a derived type in the same SCC. */ > if (!TYPE_CANONICAL (t)) > - gimple_register_canonical_type (t); > + { > + if (!RECORD_OR_UNION_TYPE_P (t) > + || !odr_or_derived_type_p (t)) > + gimple_register_canonical_type (t); > + else if (COMPLETE_TYPE_P (t) > + && TYPE_MAIN_VARIANT (t) == t) > + vec_safe_push (types_to_register, t); > + } > if (TYPE_MAIN_VARIANT (t) == t && odr_type_p (t)) > register_odr_type (t); > } > @@ -2602,6 +2764,8 @@ read_cgraph_and_symbols (unsigned nfiles > ggc_free(decl_data); > real_file_decl_data = NULL; > > + lto_register_canonical_types_for_odr_types (); > + > if (resolution_file_name) > fclose (resolution); > > Index: testsuite/g++.dg/lto/alias-2_0.C > =================================================================== > --- testsuite/g++.dg/lto/alias-2_0.C (nonexistent) > +++ testsuite/g++.dg/lto/alias-2_0.C (working copy) > @@ -0,0 +1,31 @@ > +/* { dg-lto-do run } */ > +/* { dg-lto-options { { -O2 -flto } } } */ > + > +/* With LTO we consider all pointers to incomplete types to be possibly > + aliasing. This makes *bptr to alias with aptr. > + For C++ ODR types we however can work out that they are actually > + different. */ > + > +#include <string.h> > + > +typedef int (*fnptr) (); > + > +__attribute__ ((used)) > +struct a *aptr; > + > +__attribute__ ((used)) > +struct b **bptr = (struct b**)&aptr; > +extern void init (); > +extern void inline_me_late (int); > + > + > +int > +main (int argc, char **argv) > +{ > + init (); > + aptr = 0; > + inline_me_late (argc); > + if (!__builtin_constant_p (aptr == 0)) > + __builtin_abort (); > + return (size_t)aptr; > +} > Index: testsuite/g++.dg/lto/alias-2_1.C > =================================================================== > --- testsuite/g++.dg/lto/alias-2_1.C (nonexistent) > +++ testsuite/g++.dg/lto/alias-2_1.C (working copy) > @@ -0,0 +1,16 @@ > +#include <string.h> > +struct a {int a;} a; > +struct b {int b;} b; > +extern struct b **bptr; > +void > +inline_me_late (int argc) > +{ > + if (argc == -1) > + *bptr = (struct b *)(size_t)1; > +} > +void > +init() > +{ > + a.a=1; > + b.b=2; > +} > Index: tree.c > =================================================================== > --- tree.c (revision 271843) > +++ tree.c (working copy) > @@ -14083,6 +14083,7 @@ gimple_canonical_types_compatible_p (con > > gcc_assert (!trust_type_canonical > || (type_with_alias_set_p (t1) && type_with_alias_set_p (t2))); > + > /* If the types have been previously registered and found equal > they still are. */ > > @@ -14100,6 +14101,14 @@ gimple_canonical_types_compatible_p (con > return TYPE_CANONICAL (t1) == TYPE_CANONICAL (t2); > } > > + /* For types where we do ODR based TBAA the canonical type is always > + set correctly, so we know that types are different if their > + canonical types does not match. */ > + if (trust_type_canonical > + && (odr_type_p (t1) && odr_based_tbaa_p (t1)) > + != (odr_type_p (t2) && odr_based_tbaa_p (t2))) > + return false; > + ? But then TYPE_CANONICAL is properly set and thus the code above catched it? It looks like this is for the transitional case where TYPE_CANONICAL was delayed? If so it doesn't really belong here but in the (hopefully single...) caller that matters? Richard. > /* Can't be the same type if the types don't have the same code. */ > enum tree_code code = tree_code_for_canonical_type_merging (TREE_CODE > (t1)); > if (code != tree_code_for_canonical_type_merging (TREE_CODE (t2))) > -- Richard Biener <rguent...@suse.de> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imendörffer, Mary Higgins, Sri Rasiah; HRB 21284 (AG Nürnberg)