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?
* 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);