On Mon, May 11, 2015 at 6:42 AM, Jan Hubicka <[email protected]> wrote:
> Hi,
> this is the updated version of patch with testsuite compensation and
> some changes:
> - As discussed with Richard on IRC, I added -Wlto-type-mismatch to control
> the warnings (enabled by default) and split ODR warnings from warnings
> about incompatible types (in a sense of weak structural equality tested by
> types_compatible_p at LTO time).
>
> Both -Wlto-type-mismatch and -Wodr are on by default. -Wodr points will
> output warnings only on conflicts between C++ programs that are positively
> undefined by the standard (modulo implementation bugs) and
> -Wlto-type-mismatch positives points out likely wrong code since the
> declarations are not compatible to -fstrit-aliasing.
>
> The testuiste fallout of Fortran was not that hard to fix, so I hope
> -Wlto-type-mismatch is weak enough to make sense for mixed language
> units. In fact I can use the ODR type matching tocde to implement
> more strict checks for separate flag (-Wlto-strict-type-mismatch)
> that would be off by default or perhaps just warn between C and C++
> units.
> - I got Firefox building and noticed a false positives on functions
> when forward declaration and prototype was mixed. THis is because
> useless_type_conversion compares function types but requires the outer
> type to be the one more specified (prototype).
> types_compatible_p checks that the types are convertible both directions
> so it return false.
>
> This whole code does not make much sense to be. I do not see why
> useless_type_conversion needs to care about funcition types - we never
> convert these. I also do not see why it match TYPE_METHOD_BASETYPE
> when this field is never used for codegen. It is used by ubsan
> and dbxout only.
>
> I think useless_type_conversion can jsut ICE on function/method types
> and types_compatible_p can be extended by same code as I have
> in warn_types_mismatch minus the TYPE_METHOD_BASETYPE matching.
>
> On a positive note, the patch also finds some real bugs in Firefox :)
> - I also noticed that we may miss some ODR warnings when the declaration
> is of compound type, not named type. (i.e. odr_type a[4];).
> To make these working I added odr_or_derived_type_p and exported
> the functionality to make structural compare from ipa-devirt.
> The way of walking compount types was discussed with Jason here:
> https://gcc.gnu.org/ml/gcc-patches/2014-07/msg00382.html
>
> Bootstrapped/regtested x86_64-linux, ppc64 running. Also tested with
> ltobootstrap.
> OK?
Looks good to me.
Richard.
> * ipa-utils.h (warn_types_mismatch, odr_or_derived_type_p,
> odr_types_equivalent_p): Declare.
> (odr_type_p): Use gcc_checking_assert.
> * common.opt (Wlto-type-mismatch): New warning.
> * ipa-devirt.c (compound_type_base): New function.
> (odr_or_derived_type_p): New function.
> (odr_types_equivalent_p): New function.
> (add_type_duplicate): Simplify.
>
> * lto-symtab.c (warn_type_compatibility_p): Break out from ...;
> compare ODR types (if available) and function types.
> (lto_symtab_merge): ... here; output ODR violation warnings
> and call warn_types_mismatch.
>
> * gfortran.dg/lto/20091028-2_1.c: Fix return value.
> * gfortran.dg/lto/pr41576_1.f90: Add interface.
> * gfortran.dg/lto/pr41521_0.f90: Disable lto-type-mismatch
> * gfortran.dg/lto/pr60635_0.f90: Disable lto-type-mismatch.
> * gfortran.dg/lto/20091028-1_1.c: Fix return type.
> * gcc.dg/lto/20120723_0.c: Disbale lto-type-mismatch.
> Index: ipa-utils.h
> ===================================================================
> --- ipa-utils.h (revision 222991)
> +++ ipa-utils.h (working copy)
> @@ -84,6 +84,9 @@ bool types_must_be_same_for_odr (tree, t
> bool types_odr_comparable (tree, tree, bool strict = false);
> cgraph_node *try_speculative_devirtualization (tree, HOST_WIDE_INT,
> ipa_polymorphic_call_context);
> +void warn_types_mismatch (tree t1, tree t2);
> +bool odr_or_derived_type_p (const_tree t);
> +bool odr_types_equivalent_p (tree type1, tree type2);
>
> /* Return vector containing possible targets of polymorphic call E.
> If COMPLETEP is non-NULL, store true if the list is complete.
> @@ -164,7 +167,7 @@ odr_type_p (const_tree t)
> return true;
> /* We do not have this information when not in LTO, but we do not need
> to care, since it is used only for type merging. */
> - gcc_assert (in_lto_p || flag_lto);
> + gcc_checking_assert (in_lto_p || flag_lto);
>
> return (TYPE_NAME (t)
> && (DECL_ASSEMBLER_NAME_SET_P (TYPE_NAME (t))));
> Index: common.opt
> ===================================================================
> --- common.opt (revision 222991)
> +++ common.opt (working copy)
> @@ -607,6 +607,10 @@ Woverflow
> Common Var(warn_overflow) Init(1) Warning
> Warn about overflow in arithmetic expressions
>
> +Wlto-type-mismatch
> +Common Var(warn_lto_type_mismatch) Init(1) Warning
> +During link time optimization warn about mismatched types of global
> declarations
> +
> Wpacked
> Common Var(warn_packed) Warning
> Warn when the packed attribute has no effect on struct layout
> Index: lto/lto-symtab.c
> ===================================================================
> --- lto/lto-symtab.c (revision 222991)
> +++ lto/lto-symtab.c (working copy)
> @@ -56,6 +56,7 @@ along with GCC; see the file COPYING3.
> #include "ipa-prop.h"
> #include "ipa-inline.h"
> #include "builtins.h"
> +#include "print-tree.h"
>
> /* Replace the cgraph node NODE with PREVAILING_NODE in the cgraph, merging
> all edges and removing the old node. */
> @@ -203,45 +204,49 @@ lto_varpool_replace_node (varpool_node *
> vnode->remove ();
> }
>
> -/* Merge two variable or function symbol table entries PREVAILING and ENTRY.
> - Return false if the symbols are not fully compatible and a diagnostic
> - should be emitted. */
> +/* Return non-zero if we want to output waring about T1 and T2.
> + Return value is a bitmask of reasons of violation:
> + Bit 0 indicates that types are not compatible of memory layout.
> + Bot 1 indicates that types are not compatible because of C++ ODR rule. */
>
> -static bool
> -lto_symtab_merge (symtab_node *prevailing, symtab_node *entry)
> +static int
> +warn_type_compatibility_p (tree prevailing_type, tree type)
> {
> - tree prevailing_decl = prevailing->decl;
> - tree decl = entry->decl;
> - tree prevailing_type, type;
> -
> - if (prevailing_decl == decl)
> - return true;
> -
> - /* Merge decl state in both directions, we may still end up using
> - the new decl. */
> - TREE_ADDRESSABLE (prevailing_decl) |= TREE_ADDRESSABLE (decl);
> - TREE_ADDRESSABLE (decl) |= TREE_ADDRESSABLE (prevailing_decl);
> -
> - /* The linker may ask us to combine two incompatible symbols.
> - Detect this case and notify the caller of required diagnostics. */
> -
> - if (TREE_CODE (decl) == FUNCTION_DECL)
> - {
> - if (!types_compatible_p (TREE_TYPE (prevailing_decl),
> - TREE_TYPE (decl)))
> - /* If we don't have a merged type yet...sigh. The linker
> - wouldn't complain if the types were mismatched, so we
> - probably shouldn't either. Just use the type from
> - whichever decl appears to be associated with the
> - definition. If for some odd reason neither decl is, the
> - older one wins. */
> - (void) 0;
> -
> - return true;
> + int lev = 0;
> + /* C++ provide a robust way to check for type compatibility via the ODR
> + rule. */
> + if (odr_or_derived_type_p (prevailing_type) && odr_type_p (type)
> + && !odr_types_equivalent_p (prevailing_type, type))
> + lev = 2;
> +
> + /* Function types needs special care, because types_compatible_p never
> + thinks prototype is compatible to non-prototype. */
> + if ((TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE)
> + && TREE_CODE (type) == TREE_CODE (prevailing_type))
> + {
> + lev |= warn_type_compatibility_p (TREE_TYPE (prevailing_type),
> + TREE_TYPE (type));
> + if (TREE_CODE (type) == METHOD_TYPE)
> + lev |= warn_type_compatibility_p (TYPE_METHOD_BASETYPE
> (prevailing_type),
> + TYPE_METHOD_BASETYPE (type));
> + if (prototype_p (prevailing_type) && prototype_p (type)
> + && TYPE_ARG_TYPES (prevailing_type) != TYPE_ARG_TYPES (type))
> + {
> + tree parm1, parm2;
> + for (parm1 = TYPE_ARG_TYPES (prevailing_type),
> + parm2 = TYPE_ARG_TYPES (type);
> + parm1 && parm2;
> + parm1 = TREE_CHAIN (prevailing_type),
> + parm2 = TREE_CHAIN (type))
> + lev |= warn_type_compatibility_p (TREE_VALUE (parm1),
> + TREE_VALUE (parm2));
> + if (parm1 || parm2)
> + lev = 3;
> + }
> + if (comp_type_attributes (prevailing_type, type) == 0)
> + lev = 3;
> + return lev;
> }
> -
> - /* Now we exclusively deal with VAR_DECLs. */
> -
> /* Sharing a global symbol is a strong hint that two types are
> compatible. We could use this information to complete
> incomplete pointed-to types more aggressively here, ignoring
> @@ -254,19 +259,22 @@ lto_symtab_merge (symtab_node *prevailin
> ??? In principle we might want to only warn for structurally
> incompatible types here, but unless we have protective measures
> for TBAA in place that would hide useful information. */
> - prevailing_type = TYPE_MAIN_VARIANT (TREE_TYPE (prevailing_decl));
> - type = TYPE_MAIN_VARIANT (TREE_TYPE (decl));
> + prevailing_type = TYPE_MAIN_VARIANT (prevailing_type);
> + type = TYPE_MAIN_VARIANT (type);
>
> if (!types_compatible_p (prevailing_type, type))
> {
> - if (COMPLETE_TYPE_P (type))
> - return false;
> + if (TREE_CODE (prevailing_type) == FUNCTION_TYPE
> + || TREE_CODE (type) == METHOD_TYPE)
> + return 1 | lev;
> + if (COMPLETE_TYPE_P (type) && COMPLETE_TYPE_P (prevailing_type))
> + return 1 | lev;
>
> /* If type is incomplete then avoid warnings in the cases
> that TBAA handles just fine. */
>
> if (TREE_CODE (prevailing_type) != TREE_CODE (type))
> - return false;
> + return 1 | lev;
>
> if (TREE_CODE (prevailing_type) == ARRAY_TYPE)
> {
> @@ -280,10 +288,10 @@ lto_symtab_merge (symtab_node *prevailin
> }
>
> if (TREE_CODE (tem1) != TREE_CODE (tem2))
> - return false;
> + return 1 | lev;
>
> if (!types_compatible_p (tem1, tem2))
> - return false;
> + return 1 | lev;
> }
>
> /* Fallthru. Compatible enough. */
> @@ -292,6 +300,43 @@ lto_symtab_merge (symtab_node *prevailin
> /* ??? We might want to emit a warning here if type qualification
> differences were spotted. Do not do this unconditionally though. */
>
> + return lev;
> +}
> +
> +/* Merge two variable or function symbol table entries PREVAILING and ENTRY.
> + Return false if the symbols are not fully compatible and a diagnostic
> + should be emitted. */
> +
> +static bool
> +lto_symtab_merge (symtab_node *prevailing, symtab_node *entry)
> +{
> + tree prevailing_decl = prevailing->decl;
> + tree decl = entry->decl;
> +
> + if (prevailing_decl == decl)
> + return true;
> +
> + /* Merge decl state in both directions, we may still end up using
> + the new decl. */
> + TREE_ADDRESSABLE (prevailing_decl) |= TREE_ADDRESSABLE (decl);
> + TREE_ADDRESSABLE (decl) |= TREE_ADDRESSABLE (prevailing_decl);
> +
> + /* The linker may ask us to combine two incompatible symbols.
> + Detect this case and notify the caller of required diagnostics. */
> +
> + if (TREE_CODE (decl) == FUNCTION_DECL)
> + {
> + if (warn_type_compatibility_p (TREE_TYPE (prevailing_decl),
> + TREE_TYPE (decl)))
> + return false;
> +
> + return true;
> + }
> +
> + if (warn_type_compatibility_p (TREE_TYPE (prevailing_decl),
> + TREE_TYPE (decl)))
> + return false;
> +
> /* There is no point in comparing too many details of the decls here.
> The type compatibility checks or the completing of types has properly
> dealt with most issues. */
> @@ -483,24 +528,39 @@ lto_symtab_merge_decls_2 (symtab_node *f
> /* Diagnose all mismatched re-declarations. */
> FOR_EACH_VEC_ELT (mismatches, i, decl)
> {
> - if (!types_compatible_p (TREE_TYPE (prevailing->decl),
> - TREE_TYPE (decl)))
> - diagnosed_p |= warning_at (DECL_SOURCE_LOCATION (decl), 0,
> - "type of %qD does not match original "
> - "declaration", decl);
> -
> + int level = warn_type_compatibility_p (TREE_TYPE (prevailing->decl),
> + TREE_TYPE (decl));
> + if (level)
> + {
> + bool diag = false;
> + if (level > 1)
> + diag = warning_at (DECL_SOURCE_LOCATION (decl),
> + OPT_Wodr,
> + "%qD violates the C++ One Definition Rule ",
> + decl);
> + if (!diag && (level & 1))
> + diag = warning_at (DECL_SOURCE_LOCATION (decl),
> + OPT_Wlto_type_mismatch,
> + "type of %qD does not match original "
> + "declaration", decl);
> + if (diag)
> + warn_types_mismatch (TREE_TYPE (prevailing->decl),
> + TREE_TYPE (decl));
> + diagnosed_p |= diag;
> + }
> else if ((DECL_USER_ALIGN (prevailing->decl)
> && DECL_USER_ALIGN (decl))
> && DECL_ALIGN (prevailing->decl) < DECL_ALIGN (decl))
> {
> - diagnosed_p |= warning_at (DECL_SOURCE_LOCATION (decl), 0,
> + diagnosed_p |= warning_at (DECL_SOURCE_LOCATION (decl),
> + OPT_Wlto_type_mismatch,
> "alignment of %qD is bigger than "
> "original declaration", decl);
> }
> }
> if (diagnosed_p)
> inform (DECL_SOURCE_LOCATION (prevailing->decl),
> - "previously declared here");
> + "%qD was previously declared here", prevailing->decl);
>
> mismatches.release ();
> }
> Index: ipa-devirt.c
> ===================================================================
> --- ipa-devirt.c (revision 222991)
> +++ ipa-devirt.c (working copy)
> @@ -549,6 +549,59 @@ types_must_be_same_for_odr (tree t1, tre
> return TYPE_MAIN_VARIANT (t1) == TYPE_MAIN_VARIANT (t2);
> }
>
> +/* If T is compound type, return type it is based on. */
> +
> +static tree
> +compound_type_base (const_tree t)
> +{
> + if (TREE_CODE (t) == ARRAY_TYPE
> + || POINTER_TYPE_P (t)
> + || TREE_CODE (t) == COMPLEX_TYPE
> + || VECTOR_TYPE_P (t))
> + return TREE_TYPE (t);
> + if (TREE_CODE (t) == METHOD_TYPE)
> + return TYPE_METHOD_BASETYPE (t);
> + if (TREE_CODE (t) == OFFSET_TYPE)
> + return TYPE_OFFSET_BASETYPE (t);
> + return NULL_TREE;
> +}
> +
> +/* Return true if T is either ODR type or compound type based from it.
> + If the function return true, we know that T is a type originating from C++
> + source even at link-time. */
> +
> +bool
> +odr_or_derived_type_p (const_tree t)
> +{
> + do
> + {
> + if (odr_type_p (t))
> + return true;
> + /* Function type is a tricky one. Basically we can consider it
> + ODR derived if return type or any of the parameters is.
> + We need to check all parameters because LTO streaming merges
> + common types (such as void) and they are not considered ODR then. */
> + if (TREE_CODE (t) == FUNCTION_TYPE)
> + {
> + if (TYPE_METHOD_BASETYPE (t))
> + t = TYPE_METHOD_BASETYPE (t);
> + else
> + {
> + if (TREE_TYPE (t) && odr_or_derived_type_p (TREE_TYPE (t)))
> + return true;
> + for (t = TYPE_ARG_TYPES (t); t; t = TREE_CHAIN (t))
> + if (odr_or_derived_type_p (TREE_VALUE (t)))
> + return true;
> + return false;
> + }
> + }
> + else
> + t = compound_type_base (t);
> + }
> + while (t);
> + return t;
> +}
> +
> /* Compare types T1 and T2 and return true if they are
> equivalent. */
>
> @@ -1577,6 +1642,20 @@ odr_types_equivalent_p (tree t1, tree t2
> return true;
> }
>
> +/* Return true if TYPE1 and TYPE2 are equivalent for One Definition Rule. */
> +
> +bool
> +odr_types_equivalent_p (tree type1, tree type2)
> +{
> + hash_set<type_pair,pair_traits> visited;
> +
> +#ifdef ENABLE_CHECKING
> + gcc_assert (odr_or_derived_type_p (type1) && odr_or_derived_type_p
> (type2));
> +#endif
> + return odr_types_equivalent_p (type1, type2, false, NULL,
> + &visited);
> +}
> +
> /* TYPE is equivalent to VAL by ODR, but its tree representation differs
> from VAL->type. This may happen in LTO where tree merging did not merge
> all variants of the same type or due to ODR violation.
> @@ -1701,12 +1780,8 @@ add_type_duplicate (odr_type val, tree t
> base_mismatch = true;
> }
> else
> - {
> - hash_set<type_pair,pair_traits> visited;
> - if (!odr_types_equivalent_p (type1, type2, false, NULL,
> - &visited))
> - base_mismatch = true;
> - }
> + if (!odr_types_equivalent_p (type1, type2))
> + base_mismatch = true;
> if (base_mismatch)
> {
> if (!warned && !val->odr_violated)
> Index: testsuite/gfortran.dg/lto/20091028-2_1.c
> ===================================================================
> --- testsuite/gfortran.dg/lto/20091028-2_1.c (revision 222991)
> +++ testsuite/gfortran.dg/lto/20091028-2_1.c (working copy)
> @@ -1,9 +1,9 @@
> extern void *memcpy(void *dest, const void *src, __SIZE_TYPE__ n);
> char *p;
> -int int_gen_ti_header_c_ (char * hdrbuf, int * hdrbufsize,
> - int * itypesize, int * typesize,
> - int * DataHandle, char * Data,
> - int * Count, int * code)
> +void int_gen_ti_header_c_ (char * hdrbuf, int * hdrbufsize,
> + int * itypesize, int * typesize,
> + int * DataHandle, char * Data,
> + int * Count, int * code)
> {
> memcpy (typesize, p, sizeof(int)) ;
> memcpy (Data, p, *Count * *typesize) ;
> Index: testsuite/gfortran.dg/lto/pr41576_1.f90
> ===================================================================
> --- testsuite/gfortran.dg/lto/pr41576_1.f90 (revision 222991)
> +++ testsuite/gfortran.dg/lto/pr41576_1.f90 (working copy)
> @@ -5,3 +5,8 @@ program test
> if (c/=1 .or. d/=2) call abort
> end program test
>
> +interface
> + subroutine foo()
> + end subroutine
> +end interface
> +
> Index: testsuite/gfortran.dg/lto/pr41521_0.f90
> ===================================================================
> --- testsuite/gfortran.dg/lto/pr41521_0.f90 (revision 222991)
> +++ testsuite/gfortran.dg/lto/pr41521_0.f90 (working copy)
> @@ -1,9 +1,9 @@
> ! { dg-lto-do link }
> -! { dg-lto-options {{-g -flto} {-g -O -flto}} }
> +! { dg-lto-options {{-g -flto -Wno-lto-type-mismatch} {-g -O -flto
> -Wno-lto-type-mismatch}} }
> program species
> integer spk(2)
> real eval(2)
> spk = 2
> call atom(1.1,spk,eval)
> end program
>
> Index: testsuite/gfortran.dg/lto/pr60635_0.f90
> ===================================================================
> --- testsuite/gfortran.dg/lto/pr60635_0.f90 (revision 222991)
> +++ testsuite/gfortran.dg/lto/pr60635_0.f90 (working copy)
> @@ -1,4 +1,5 @@
> ! { dg-lto-do link }
> +! { dg-lto-options {{ -Wno-lto-type-mismatch }} }
> program test
> use iso_fortran_env
>
> Index: testsuite/gfortran.dg/lto/pr41576_0.f90
> ===================================================================
> --- testsuite/gfortran.dg/lto/pr41576_0.f90 (revision 222991)
> +++ testsuite/gfortran.dg/lto/pr41576_0.f90 (working copy)
> @@ -1,5 +1,5 @@
> ! { dg-lto-do run }
> -! { dg-lto-options { { -O2 -flto -Werror } } }
> +! { dg-lto-options { { -O2 -flto -Werror -Wno-lto-type-mismatch } } }
>
> subroutine foo
> common /bar/ a, b
> Index: testsuite/gfortran.dg/lto/20091028-1_1.c
> ===================================================================
> --- testsuite/gfortran.dg/lto/20091028-1_1.c (revision 222991)
> +++ testsuite/gfortran.dg/lto/20091028-1_1.c (working copy)
> @@ -1,9 +1,9 @@
> extern void bcopy(const void *, void *, __SIZE_TYPE__ n);
> char *p;
> -int int_gen_ti_header_c_ (char * hdrbuf, int * hdrbufsize,
> - int * itypesize, int * typesize,
> - int * DataHandle, char * Data,
> - int * Count, int * code)
> +void int_gen_ti_header_c_ (char * hdrbuf, int * hdrbufsize,
> + int * itypesize, int * typesize,
> + int * DataHandle, char * Data,
> + int * Count, int * code)
> {
> bcopy (typesize, p, sizeof(int)) ;
> bcopy (Data, p, *Count * *typesize) ;
> Index: testsuite/gcc.dg/lto/20120723_0.c
> ===================================================================
> --- testsuite/gcc.dg/lto/20120723_0.c (revision 222991)
> +++ testsuite/gcc.dg/lto/20120723_0.c (working copy)
> @@ -3,7 +3,7 @@
> ??? This testcase is invalid C and can only pass on specific platforms.
> */
> /* { dg-lto-do run } */
> /* { dg-skip-if "" { { sparc*-*-* } && ilp32 } { "*" } { "" } } */
> -/* { dg-lto-options { {-O3 -fno-early-inlining -flto}} } */
> +/* { dg-lto-options { {-O3 -fno-early-inlining -flto
> -Wno-lto-type-mismatch}} } */
>
> extern void abort (void);
>