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 <[email protected]>
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany;
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah; HRB 21284 (AG Nürnberg)