Hi, this is minimal variant of the patch turning complete to incomplete pointers in fields. We can do more - in particular it would be very function to do same for functions types and decls (because they often end up being streamed to symtab) and we should also turn pointers to arrays and enums to incomplete variants.
I do that in my local tree but i would like to get it into mainline one by one and check benefits of each change independently. Patch bootstraped®tests x86-64 and I am now re-testing it on firefox. I checked on small testcases that types indeed do get merged. OK if it survives more testing on firefox and lto bootstrap? Honza * tree.c (free_lang_data_in_type): Declare. (types_equal_p): New function. (free_lang_data_type_variant): New function. (incomplete_type_of): New function. (simplified_type): New function. Index: tree.c =================================================================== --- tree.c (revision 265522) +++ tree.c (working copy) @@ -265,6 +265,8 @@ static void print_type_hash_statistics ( static void print_debug_expr_statistics (void); static void print_value_expr_statistics (void); +static void free_lang_data_in_type (tree type); + tree global_trees[TI_MAX]; tree integer_types[itk_none]; @@ -5038,6 +5041,140 @@ protected_set_expr_location (tree t, loc SET_EXPR_LOCATION (t, loc); } +/* Do same comparsion as check_qualified_type skipping lang part of type + and be more permissive about type names: we only care that names are + same (for diagnostics) and that ODR names are the same. */ + +static bool +types_equal_p (tree t, tree v) +{ + if (t==v) + return true; + + if (TYPE_QUALS (t) != TYPE_QUALS (v)) + return false; + + if (TYPE_NAME (t) != TYPE_NAME (v) + && (!TYPE_NAME (t) || !TYPE_NAME (v) + || TREE_CODE (TYPE_NAME (t)) != TYPE_DECL + || TREE_CODE (TYPE_NAME (v)) != TYPE_DECL + || DECL_ASSEMBLER_NAME_RAW (TYPE_NAME (t)) + != DECL_ASSEMBLER_NAME_RAW (TYPE_NAME (v)) + || DECL_NAME (TYPE_NAME (t)) != DECL_NAME (TYPE_NAME (v)))) + return false; + + if (TYPE_ALIGN (t) != TYPE_ALIGN (v)) + return false; + + if (!attribute_list_equal (TYPE_ATTRIBUTES (t), + TYPE_ATTRIBUTES (v))) + return false; + + /* Do not replace complete type by incomplete. */ + if ((TREE_CODE (t) == QUAL_UNION_TYPE + || TREE_CODE (t) == UNION_TYPE || TREE_CODE (t) == RECORD_TYPE) + && COMPLETE_TYPE_P (t) != COMPLETE_TYPE_P (v)) + return false; + + gcc_assert (TREE_CODE (t) == TREE_CODE (v)); + + /* For pointer types and array types we also care about the type they + reffer to. */ + if (TREE_TYPE (t)) + return types_equal_p (TREE_TYPE (t), TREE_TYPE (v)); + + return true; +} + +/* Find variant of FIRST that match T and create new one if necessary. */ + +static tree +free_lang_data_type_variant (tree first, tree t) +{ + if (first == TYPE_MAIN_VARIANT (t)) + return t; + for (tree v = first; v; v = TYPE_NEXT_VARIANT (v)) + if (types_equal_p (t, v)) + return v; + tree v = build_variant_type_copy (first); + TYPE_READONLY (v) = TYPE_READONLY (t); + TYPE_VOLATILE (v) = TYPE_VOLATILE (t); + TYPE_ATOMIC (v) = TYPE_ATOMIC (t); + TYPE_RESTRICT (v) = TYPE_RESTRICT (t); + TYPE_ADDR_SPACE (v) = TYPE_ADDR_SPACE (t); + TYPE_NAME (v) = TYPE_NAME (t); + TYPE_ATTRIBUTES (v) = TYPE_ATTRIBUTES (t); + return v; +} + +/* Map complete types to incomplete types. */ +static hash_map<tree, tree> *incomplete_types; + +/* See if type T can be turned into incopmlete variant. */ + +static tree +incomplete_type_of (tree t) +{ + if (!RECORD_OR_UNION_TYPE_P (t)) + return t; + if (!COMPLETE_TYPE_P (t)) + return t; + if (TYPE_MAIN_VARIANT (t) == t) + { + bool existed; + tree © + = incomplete_types->get_or_insert (TYPE_MAIN_VARIANT (t), &existed); + + if (!existed) + { + copy = build_distinct_type_copy (t); + + /* It is possible type was not seen by free_lang_data yet. */ + free_lang_data_in_type (copy); + TYPE_SIZE (copy) = NULL; + SET_TYPE_MODE (copy, VOIDmode); + SET_TYPE_ALIGN (copy, BITS_PER_UNIT); + TYPE_SIZE_UNIT (copy) = NULL; + if (AGGREGATE_TYPE_P (t)) + { + TYPE_FIELDS (copy) = NULL; + TYPE_BINFO (copy) = NULL; + } + else + TYPE_VALUES (copy) = NULL; + } + return copy; + } + return (free_lang_data_type_variant + (incomplete_type_of (TYPE_MAIN_VARIANT (t)), t)); +} + +/* Simplify type T for scenarios where we do not need complete pointer + types. */ + +static tree +simplified_type (tree t) +{ + if (POINTER_TYPE_P (t)) + { + tree t2 = POINTER_TYPE_P (TREE_TYPE (t)) + ? simplified_type (TREE_TYPE (t)) + : incomplete_type_of (TREE_TYPE (t)); + if (t2 != TREE_TYPE (t)) + { + tree first; + if (TREE_CODE (t) == POINTER_TYPE) + first = build_pointer_type_for_mode (t2, TYPE_MODE (t), + TYPE_REF_CAN_ALIAS_ALL (t)); + else + first = build_reference_type_for_mode (t2, TYPE_MODE (t), + TYPE_REF_CAN_ALIAS_ALL (t)); + return free_lang_data_type_variant (first, t); + } + } + return t; +} + /* Reset the expression *EXPR_P, a size or position. ??? We could reset all non-constant sizes or positions. But it's cheap @@ -5354,9 +5492,11 @@ free_lang_data_in_decl (tree decl) DECL_VISIBILITY_SPECIFIED (decl) = 0; DECL_INITIAL (decl) = NULL_TREE; DECL_ORIGINAL_TYPE (decl) = NULL_TREE; } else if (TREE_CODE (decl) == FIELD_DECL) - DECL_INITIAL (decl) = NULL_TREE; + { + TREE_TYPE (decl) = simplified_type (TREE_TYPE (decl)); + DECL_INITIAL (decl) = NULL_TREE; + } else if (TREE_CODE (decl) == TRANSLATION_UNIT_DECL && DECL_INITIAL (decl) && TREE_CODE (DECL_INITIAL (decl)) == BLOCK) @@ -5866,6 +6011,8 @@ free_lang_data (void) || (!flag_generate_lto && !flag_generate_offload)) return 0; + incomplete_types = new hash_map<tree, tree>; + /* Provide a dummy TRANSLATION_UNIT_DECL if the FE failed to provide one. */ if (vec_safe_is_empty (all_translation_units)) build_translation_unit_decl (NULL_TREE);