On Mon, May 11, 2015 at 6:42 AM, Jan Hubicka <hubi...@ucw.cz> 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); >