From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 83215 invoked by alias); 26 May 2015 17:22:04 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 83204 invoked by uid 89); 26 May 2015 17:22:03 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=1.8 required=5.0 tests=AWL,BAYES_99,BAYES_999,KAM_ASCII_DIVIDERS,KAM_LAZY_DOMAIN_SECURITY,T_RP_MATCHES_RCVD autolearn=no version=3.3.2 X-HELO: nikam.ms.mff.cuni.cz Received: from nikam.ms.mff.cuni.cz (HELO nikam.ms.mff.cuni.cz) (195.113.20.16) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Tue, 26 May 2015 17:21:59 +0000 Received: by nikam.ms.mff.cuni.cz (Postfix, from userid 16202) id 4623A542E2E; Tue, 26 May 2015 19:21:54 +0200 (CEST) Date: Tue, 26 May 2015 17:36:00 -0000 From: Jan Hubicka To: Richard Biener Cc: Jan Hubicka , gcc-patches@gcc.gnu.org Subject: Re: Teach gimple_canonical_types_compatible_p about incomplete types Message-ID: <20150526172153.GB43680@kam.mff.cuni.cz> References: <20150524234944.GB71623@kam.mff.cuni.cz> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.21 (2010-09-15) X-SW-Source: 2015-05/txt/msg02362.txt.bz2 > > Now the change does not really translate to great increase of disambiguations > > for Firefox (it seems more in noise). The reason is the pointer_type globbing > > in alias.c. > > Yeah, we only get the improvement because of some "hack" in the tree > alias oracle which also uses the base object for TBAA. Why that is hack? Dereferencing a pointer makes it clear the type of memory location pointed to is known, we should use that info. > > Yeah, we should fix that. And in fact, for cross-language LTO I don't > see why > > union { int a; char c; }; > > and > > union { int a; short s; }; > > should not be compatible - they have a common member after all. So > I'd like to glob all unions that have the same size (and as improvement Well, none of language standards I saw so far expect this to happen. Going to extremes, you can always put variable sized char array to union and by transitivity glob everything with everything. > over that, that have at least one compatible member). That also get's > rid of the issue that we'd need to sort union members for the comparison > to avoid quadraticness (as long as we don't check for that one compatible > member). Yeah, sorting is possible by using the hash values. > > Oh, and are > > union { int a; }; > > and > > struct { int a; }; > > not compatible? They are layout-wise at least. Likewise the struct > and union { int a; short s; } with the same argument as the two-union > case. Applying this rule you have union { char a[n]; } compatible with every union and thus also union {int a;} struct { int a;} int a; Which would disable TBAA completely. > > > We also do not compare alignments. This is probably not important) > > Correct - alignment doesn't enter TBAA. Yep, I think the alignment compare in C standard basically is there to say that structures must have same lyaout. > > void f(double (* restrict a)[5]); > > void f(double a[restrict][5]); > > void f(double a[restrict 3][5]); > > void f(double a[restrict static 3][5]);) > > Not sure why you get into functions here at all ... Basically it matters only if we want to disambiguate function pointers. > > > 2 Each enumerated type shall be compatible with char , a signed integer > > type, or an unsigned integer type. The choice of type is > > implementation-defined, but shall be capable of representing the values > > of all the members of the enumeration. The enumerated type is > > incomplete until immediately after the that terminates the list of > > enumerator declarations, and complete thereafter. > > > > (we ignore this completely as far as I know, it is easy to fix though, all > > we need is to make ENUMERATION_TYPE pretend to be INTEGER_TYPE) > > Yes, we don't make a distinction between ENUMERAL_TYPE and INTEGER_TYPE. hstate.add_int (TREE_CODE (type)); makes them different. I think we want to produce "simplified" code that turns REFERENCE_TYPE to POINTER_TYPE and ENUMERAL_TYPE to INTEGER_TYPE. I will send patch fo that. > > > 10 For two qualified types to be compatible, both shall have the identically > > qualified version of a compatible type; the order of type qualifiers > > within a list of specifiers or qualifiers does not affect the specified type. > > > > Now I think in order to get C standard type compatiblity to imply > > gimple_canonical_types_compatible we need to implement all the above globbing > > rules as part of canonical type computation, not only punt at pointers in > > alias.c > > > > My reading is that for example > > > > struct a {char *a;}; > > > > is compatible with > > > > struct a {enum *a;}; > > > > defined in other compilation unit. > > Yes, as said above the TREE_CODE trick in the pointer-type handing is > wrong. We can as well just drop it ... struct a {char a;}; is compatible with struct a {enum a;}; I would say we just want to simplify the codes and peel for pointers instead of TREE_TYPE (t) compare look for actual pointed to type (peeling out POINTER_TYPE/RECORD_TYPE/ARRAY_TYPEs) > > 8) i think to be correct by C language standard we need to glob enum > > with char > > tough I do not quite see how standard conforming program should use > > it given that standard does not say if it is char/unsigned > > char/signed char. > > I think it depends on the actual enum, no? So for forward declarations > like > > enum Foo; > struct X { enum Foo *p; }; > > you face the same issue as with void *. Well, handling all enums as integers should solve this. > > Overall plan > > ============ > > > > So I propose the following: > > 1) we add the incomplete types mode to gimple_canonical_types_compatible_p > > as suggested by this patch > > 2) I will add the individual globbing rules to the functions one by one > > 3) once done, we enable recursion on pointer > > 4) we drop the alias.c globing of pointer_type for in_lto_p. > > I don't think we want to do 4), especially not only for in_lto_p. I'm > not sure we should spend time on improving accuracy before fixing > correctness (on the current precision level). Yep, fixing correctness is 2). I will look into that. > > Based on experience, we may switch to C/C++ compliant equivalence when we know > > that whole unit is C/C++ and we do not have to deal with odd cases from other > > languages. > > I'm not convinced the patch is a step in the right direction. > > Can we please first fix pointer handing for TYPE_CANONICAL by dropping > the TREE_CODE handling and fix union handling by only looking at > union size? OK, I will send patch for TYPE_CODE compare. I am not quite convinced about the unions as per example abov. > > Originally I wanted to make LTO type compatibility really layout-based > (thus struct { int i[2]; } and struct { int i; int j; } match for > example). > > I wonder if for non-cross-language-LTO we shouldn't simply stream > TYPE_CANONICAL, at stream-in record a TYPE_CANONICAL vs. uses vector > map and "fix" up canonical types globally (we'd need to stream > "local" canonical types in the global trees then). Hmm, can you explain bit more what you have in mind? I did play with non-cross-language canonical type streaming for anonymous namespace C++ types (that by definition can not be compatible with anything from other language). I can return to that. Honza > > Richard. > > > Honza > > > > * tree.c (gimple_canonical_types_compatible_p): Turn > > TRUST_TYPE_CANONICAL into FLAGS parameter; support > > comparsion with MATCH_WITH_INCOMPLETE_TYPE. > > Index: lto/lto.c > > =================================================================== > > --- lto/lto.c (revision 223632) > > +++ lto/lto.c (working copy) > > @@ -295,7 +295,9 @@ > > 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, > > + unsigned int flags > > + = MATCH_TYPE_CANONICAL); > > static hashval_t gimple_canonical_type_hash (const void *p); > > static void gimple_register_canonical_type_1 (tree t, hashval_t hash); > > > > @@ -302,26 +304,36 @@ > > /* 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. FLAGS values are interpretted > > + same way as by this function. */ > > > > static hashval_t > > -hash_canonical_type (tree type) > > +hash_canonical_type (tree type, unsigned int flags = MATCH_TYPE_CANONICAL) > > { > > inchash::hash hstate; > > > > - /* We compute alias sets only for types that needs them. > > - Be sure we do not recurse to something else as we can not hash incomplete > > - types in a way they would have same hash value as compatible complete > > - types. */ > > - gcc_checking_assert (type_with_alias_set_p (type)); > > + /* Check that we encounter incomplete types only with > > + MATCH_WITH_INCOMPLETE_TYPE. > > > > + Also check that no one tries to use hashing in compbination with > > + !MATCH_TYPE_CANONICAL. In this case gimple_canonical_types_compatible_p > > + is not transitive and thus does not produce equivalence on all types. */ > > + gcc_checking_assert ((flags & MATCH_TYPE_CANONICAL) > > + && ((flags & MATCH_WITH_INCOMPLETE_TYPE) > > + || type_with_alias_set_p (type))); > > + > > /* Combine a few common features of types so that types are grouped into > > smaller sets; when searching for existing matching types to merge, > > only existing types having the same features as the new type will be > > checked. */ > > hstate.add_int (TREE_CODE (type)); > > - hstate.add_int (TYPE_MODE (type)); > > > > + /* Incomplete arrays and aggregates do not have TYPE_MODE defined. */ > > + if (!(flags & MATCH_WITH_INCOMPLETE_TYPE) > > + || (TREE_CODE (type) != ARRAY_TYPE > > + && !AGGREGATE_TYPE_P (type))) > > + hstate.add_int (TYPE_MODE (type)); > > + > > /* Incorporate common features of numerical types. */ > > if (INTEGRAL_TYPE_P (type) > > || SCALAR_FLOAT_TYPE_P (type) > > @@ -355,7 +367,8 @@ > > hstate.add_int (TYPE_STRING_FLAG (type)); > > > > /* For array types hash the domain bounds and the string flag. */ > > - if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type)) > > + if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type) > > + && !(flags & MATCH_WITH_INCOMPLETE_TYPE)) > > { > > hstate.add_int (TYPE_STRING_FLAG (type)); > > /* OMP lowering can introduce error_mark_node in place of > > @@ -366,30 +379,35 @@ > > inchash::add_expr (TYPE_MAX_VALUE (TYPE_DOMAIN (type)), hstate); > > } > > > > - /* Recurse for aggregates with a single element type. */ > > + /* Recurse for aggregates with a single element type. > > + We are safe to drop MATCH_INCOMPLETE. There is no way to build > > + an array of incomplete types. */ > > 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, > > + flags & ~MATCH_WITH_INCOMPLETE_TYPE); > > > > /* Incorporate function return and argument types. */ > > if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) > > { > > - unsigned na; > > - tree p; > > + iterative_hash_canonical_type (TREE_TYPE (type), hstate, flags); > > > > - iterative_hash_canonical_type (TREE_TYPE (type), hstate); > > - > > - for (p = TYPE_ARG_TYPES (type), na = 0; p; p = TREE_CHAIN (p)) > > + if (!(flags & MATCH_WITH_INCOMPLETE_TYPE) > > + || TREE_CODE (type) == METHOD_TYPE) > > { > > - iterative_hash_canonical_type (TREE_VALUE (p), hstate); > > - na++; > > + unsigned na; > > + tree p; > > + for (p = TYPE_ARG_TYPES (type), na = 0; p; p = TREE_CHAIN (p)) > > + { > > + iterative_hash_canonical_type (TREE_VALUE (p), hstate, flags); > > + na++; > > + } > > + hstate.add_int (na); > > } > > - > > - hstate.add_int (na); > > } > > > > - if (RECORD_OR_UNION_TYPE_P (type)) > > + if (RECORD_OR_UNION_TYPE_P (type) && !(flags & MATCH_WITH_INCOMPLETE_TYPE)) > > { > > unsigned nf; > > tree f; > > @@ -397,7 +415,7 @@ > > for (f = TYPE_FIELDS (type), nf = 0; f; f = TREE_CHAIN (f)) > > if (TREE_CODE (f) == FIELD_DECL) > > { > > - iterative_hash_canonical_type (TREE_TYPE (f), hstate); > > + iterative_hash_canonical_type (TREE_TYPE (f), hstate, flags); > > nf++; > > } > > > > @@ -407,14 +425,22 @@ > > 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. > > + FLAGS are the same as for gimple_conanical_types_compatible_p. */ > > > > static void > > -iterative_hash_canonical_type (tree type, inchash::hash &hstate) > > +iterative_hash_canonical_type (tree type, inchash::hash &hstate, > > + unsigned int flags) > > { > > hashval_t v; > > + > > + /* TYPE_CANONICAL reflects equivalence classes with > > + !MATCH_WITH_INCOMPLETE_TYPE. If we are matching incomplete types, > > + then we need to recurse. */ > > + if (flags & MATCH_WITH_INCOMPLETE_TYPE) > > + v = hash_canonical_type (type, flags); > > /* An already processed type. */ > > - if (TYPE_CANONICAL (type)) > > + else if (TYPE_CANONICAL (type)) > > { > > type = TYPE_CANONICAL (type); > > v = gimple_canonical_type_hash (type); > > @@ -497,6 +523,14 @@ > > { > > if (TYPE_CANONICAL (t) || !type_with_alias_set_p (t)) > > return; > > + /* Only types that can be handled in memory need canonical types. > > + Function and methods are never accessed. Also we do not need canonical > > + types for incomplete types with exception of arrays - structures may end > > + with incomplete arrays that may be referenced. */ > > + if (TREE_CODE (t) == FUNCTION_TYPE || TREE_CODE (t) == METHOD_TYPE > > + || (!COMPLETE_TYPE_P (t) > > + && (TREE_CODE (t) != ARRAY_TYPE || !COMPLETE_TYPE_P (TREE_TYPE (t))))) > > + return; > > > > gimple_register_canonical_type_1 (t, hash_canonical_type (t)); > > } > > Index: tree.c > > =================================================================== > > --- tree.c (revision 223633) > > +++ tree.c (working copy) > > @@ -12702,12 +12702,24 @@ > > /* Return true iff T1 and T2 are structurally identical for what > > TBAA is concerned. > > This function is used both by lto.c canonical type merging and by the > > - verifier. If TRUST_TYPE_CANONICAL we do not look into structure of types > > - that have TYPE_CANONICAL defined and assume them equivalent. */ > > + verifier. > > > > + If flags sets MATCH_TYPE_CANONICAL we assume that TYPE_CANONICAL is set in a > > + way that two types have the same canonical type if and only if > > + gimple_canonical_types_compatible_p (t1,t2, 0) is true. This is used > > + to cut down recursion during LTO canonical type comptuation. When this flag > > + is set we also sanity check that we are going to produce equivalence relation > > + that is needed to drive the hashtable in lto.c. > > + > > + if MATCH_WITH_INCOMPLETE_TYPE is true, then we do not use any information > > + from complete types and thus i.e. all RECORD_TYPE are equivlaent to other > > + RECORD_TYPEs. This is the only equivalence possible if one require > > + incomplete type to be in the same equivalence class with all its > > + completetions. */ > > + > > bool > > gimple_canonical_types_compatible_p (const_tree t1, const_tree t2, > > - bool trust_type_canonical) > > + unsigned int flags) > > { > > /* Before starting to set up the SCC machinery handle simple cases. */ > > > > @@ -12719,28 +12731,28 @@ > > if (t1 == NULL_TREE || t2 == NULL_TREE) > > return false; > > > > - /* We consider complete types always compatible with incomplete type. > > - This does not make sense for canonical type calculation and thus we > > - need to ensure that we are never called on it. > > + /* Check that either flags allow incomplete types or both types are complete. > > + This is necessary to ensure transitivity for canonical type merging. > > > > - FIXME: For more correctness the function probably should have three modes > > - 1) mode assuming that types are complete mathcing their structure > > - 2) mode allowing incomplete types but producing equivalence classes > > - and thus ignoring all info from complete types > > - 3) mode allowing incomplete types to match complete but checking > > - compatibility between complete types. > > + FIXME: with !MATCH_TYPE_CANONICAL we probably should allow match between > > + incomplete type and complete type as defined by language standards. No > > + one however rely on it so far. */ > > > > - 1 and 2 can be used for canonical type calculation. 3 is the real > > - definition of type compatibility that can be used i.e. for warnings during > > - declaration merging. */ > > - > > - gcc_assert (!trust_type_canonical > > + gcc_assert ((flags & MATCH_WITH_INCOMPLETE_TYPE) > > + || !(flags & MATCH_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. */ > > if (TYPE_CANONICAL (t1) && TYPE_CANONICAL (t2) > > - && trust_type_canonical) > > - return TYPE_CANONICAL (t1) == TYPE_CANONICAL (t2); > > + && (flags & MATCH_TYPE_CANONICAL)) > > + { > > + if (TYPE_CANONICAL (t1) == TYPE_CANONICAL (t2)) > > + return true; > > + /* TYPE_CANONICAL is always computed with an assumption that the type > > + is complete. */ > > + if (!(flags & MATCH_WITH_INCOMPLETE_TYPE)) > > + return false; > > + } > > > > /* Can't be the same type if the types don't have the same code. */ > > if (TREE_CODE (t1) != TREE_CODE (t2)) > > @@ -12753,8 +12765,12 @@ > > || TREE_CODE (t1) == NULLPTR_TYPE) > > return true; > > > > - /* Can't be the same type if they have different mode. */ > > - if (TYPE_MODE (t1) != TYPE_MODE (t2)) > > + /* Can't be the same type if they have different mode. > > + Incomplete arrays and aggregates do not have TYPE_MODE defined. */ > > + if ((!(flags & MATCH_WITH_INCOMPLETE_TYPE) > > + || (TREE_CODE (t1) != ARRAY_TYPE > > + && !AGGREGATE_TYPE_P (t1))) > > + && TYPE_MODE (t1) != TYPE_MODE (t2)) > > return false; > > > > /* Non-aggregate types can be handled cheaply. */ > > @@ -12783,8 +12799,7 @@ > > { > > if (TYPE_ADDR_SPACE (TREE_TYPE (t1)) > > != TYPE_ADDR_SPACE (TREE_TYPE (t2))) > > - return false; > > - > > + return false; > > if (TREE_CODE (TREE_TYPE (t1)) != TREE_CODE (TREE_TYPE (t2))) > > return false; > > } > > @@ -12792,9 +12807,9 @@ > > /* Tail-recurse to components. */ > > if (TREE_CODE (t1) == VECTOR_TYPE > > || TREE_CODE (t1) == COMPLEX_TYPE) > > - return gimple_canonical_types_compatible_p (TREE_TYPE (t1), > > - TREE_TYPE (t2), > > - trust_type_canonical); > > + return gimple_canonical_types_compatible_p > > + (TREE_TYPE (t1), TREE_TYPE (t2), > > + flags & ~MATCH_WITH_INCOMPLETE_TYPE); > > > > return true; > > } > > @@ -12804,12 +12819,20 @@ > > { > > case ARRAY_TYPE: > > /* Array types are the same if the element types are the same and > > - the number of elements are the same. */ > > - if (!gimple_canonical_types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2), > > - trust_type_canonical) > > + the number of elements are the same. > > + > > + When MATCH_WITH_INCOMPLETE_TYPE is set, bypass the check > > + on number of elements. > > + When recursing, clear MATCH_WITH_INCOMPLETE_TYPE because there is > > + no way to make incomplete array of array. */ > > + if (!gimple_canonical_types_compatible_p > > + (TREE_TYPE (t1), TREE_TYPE (t2), > > + flags & ~MATCH_WITH_INCOMPLETE_TYPE) > > || TYPE_STRING_FLAG (t1) != TYPE_STRING_FLAG (t2) > > || TYPE_NONALIASED_COMPONENT (t1) != TYPE_NONALIASED_COMPONENT (t2)) > > return false; > > + else if (flags & MATCH_WITH_INCOMPLETE_TYPE) > > + return true; > > else > > { > > tree i1 = TYPE_DOMAIN (t1); > > @@ -12848,11 +12871,19 @@ > > case METHOD_TYPE: > > case FUNCTION_TYPE: > > /* Function types are the same if the return type and arguments types > > - are the same. */ > > + are the same. > > + It is possible that function pointers have return values and parameters > > + of incomplete types; permit that by not clearing > > + MATCH_WITH_INCOMPLETE_TYPE */ > > if (!gimple_canonical_types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2), > > - trust_type_canonical)) > > + flags)) > > return false; > > > > + /* We must permit a match between !prototype_p and prototype_p for > > + functions; methods are never !prototype_p. */ > > + if ((flags & MATCH_WITH_INCOMPLETE_TYPE) > > + && TREE_CODE (t1) == FUNCTION_TYPE) > > + return true; > > if (TYPE_ARG_TYPES (t1) == TYPE_ARG_TYPES (t2)) > > return true; > > else > > @@ -12864,8 +12895,7 @@ > > parms1 = TREE_CHAIN (parms1), parms2 = TREE_CHAIN (parms2)) > > { > > if (!gimple_canonical_types_compatible_p > > - (TREE_VALUE (parms1), TREE_VALUE (parms2), > > - trust_type_canonical)) > > + (TREE_VALUE (parms1), TREE_VALUE (parms2), flags)) > > return false; > > } > > > > @@ -12881,6 +12911,15 @@ > > { > > tree f1, f2; > > > > + /* C standrad require incomplete structures and unions to be > > + considered compatible with complete ones regardless their TYPE_NAME > > + when they come from different translation units. > > + We must consider transitive closure here, so > > + every structure/union is equivalent to each other. */ > > + > > + if (flags & MATCH_WITH_INCOMPLETE_TYPE) > > + return true; > > + > > /* For aggregate types, all the fields must be the same. */ > > for (f1 = TYPE_FIELDS (t1), f2 = TYPE_FIELDS (t2); > > f1 || f2; > > @@ -12897,8 +12936,7 @@ > > if (DECL_NONADDRESSABLE_P (f1) != DECL_NONADDRESSABLE_P (f2) > > || !gimple_compare_field_offset (f1, f2) > > || !gimple_canonical_types_compatible_p > > - (TREE_TYPE (f1), TREE_TYPE (f2), > > - trust_type_canonical)) > > + (TREE_TYPE (f1), TREE_TYPE (f2), flags)) > > return false; > > } > > > > @@ -12961,7 +12999,7 @@ > > with variably sized arrays because their sizes possibly > > gimplified to different variables. */ > > && !variably_modified_type_p (ct, NULL) > > - && !gimple_canonical_types_compatible_p (t, ct, false)) > > + && !gimple_canonical_types_compatible_p (t, ct, 0)) > > { > > error ("TYPE_CANONICAL is not compatible"); > > debug_tree (ct); > > Index: tree.h > > =================================================================== > > --- tree.h (revision 223632) > > +++ tree.h (working copy) > > @@ -4569,9 +4569,21 @@ > > extern unsigned int tree_map_base_hash (const void *); > > extern int tree_map_base_marked_p (const void *); > > extern void DEBUG_FUNCTION verify_type (const_tree t); > > -extern bool gimple_canonical_types_compatible_p (const_tree, const_tree, > > - bool trust_type_canonical = true); > > > > +/* Flags used by gimple_canonical_types_compatible_p. */ > > +enum gimple_canonical_types_compatible_flags > > + { > > + /* Asume that TYPE_CANONICAL is set in a way that two types have > > + the same canonical type if and only if > > + gimple_canonical_types_compatible_p (t1,t2, 0) is true. */ > > + MATCH_TYPE_CANONICAL = 1, > > + /* Match all types as if they were incomplete. */ > > + MATCH_WITH_INCOMPLETE_TYPE = 2 > > + }; > > +extern bool gimple_canonical_types_compatible_p > > + (const_tree, const_tree, > > + unsigned int flags = MATCH_TYPE_CANONICAL); > > + > > #define tree_map_eq tree_map_base_eq > > extern unsigned int tree_map_hash (const void *); > > #define tree_map_marked_p tree_map_base_marked_p > > > > > > -- > Richard Biener > SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Dilip Upmanyu, Graham Norton, HRB 21284 (AG Nuernberg)