(cross-posting because discussion may be interesting to others) The following is a patch that re-structures tree_ssa_useless_type_conversion_1 (without changing its semantics) to make it easier to read and fix towards not requiring the types_compatible_p langhook. Several places that have problems right now are marked (and I have patches for some of the in the queue).
The idea is (suggested by DannyB) to implicitly define our middle-end type-system by means of this function. A separately posted patch works towards this by carefully replacing all remaining calls to the types_compatible_p langhook by proper calls to tree_ssa_useless_type_conversion_1. There is one key invariant of tree_ssa_useless_type_conversion_1 that we need to make sure it holds. tree_ssa_useless_type_conversion_1 shall be transitive so that if tree_ssa_useless_type_conversion_1 (a, b) and tree_ssa_useless_type_conversion_1 (b, c) then tree_ssa_useless_type_conversion_1 (a, c) will also hold. I think that forcing it to be communtative would be not useful but only will cause more explicit conversions to pop up. There are a few ??? in the patch below which I'll try to go through one by one: /* Preserve changes in the types minimum or maximum value. ??? Due to the way we handle sizetype as signed we need to jump through hoops here to make sizetype and size_type_node compatible. */ if (!tree_int_cst_equal (fold_convert (outer_type, TYPE_MIN_VALUE (inner_type)), TYPE_MIN_VALUE (outer_type)) || !tree_int_cst_equal (fold_convert (outer_type, TYPE_MAX_VALUE (inner_type)), TYPE_MAX_VALUE (outer_type))) return false; with pointer_plus we assert that the offset is compatible with sizetype. But while in principle sizetype and size_type_node (where the problem arises) should be compatible, they are not as because of TYPE_IS_SIZETYPE (sizetype) and sign-extending sizetypes they differ in their TYPE_MAX_VALUE (sizetype is sign-extended, size_type_node is zero-extended). I tried to get rid of TYPE_IS_SIZETYPE completely, but there are some frontend issues that need to be worked out. /* ??? We might want to preserve base type changes because of TBAA. Or we need to be extra careful below. */ To get rid of some of the hacks in tree-ssa-copy.c:may_propagate_copy we would need to make sure to not trivially convert (long *) to (int *) if long and int have the same mode. I'm not sure about this. /* If the outer type is (void *), then the conversion is not necessary. ??? This makes tree_ssa_useless_type_conversion_1 not transitive. */ if (TREE_CODE (TREE_TYPE (outer_type)) == VOID_TYPE) return true; while this special case makes tons of sense, it conflicts with the current implementation of the langhook. This way A* = (void *)B* is reduced to A* = B* which is not a trivial conversion (and thus this violates transitivity). My bet is that the issue goes away once we stop calling the langhook from tree_ssa_useless_type_conversion_1. /* Otherwise pointers/references are equivalent if their pointed to types are effectively the same. This allows to strip conversions between pointer types with different type qualifiers. ??? We should recurse here with tree_ssa_useless_type_conversion_1. */ return lang_hooks.types_compatible_p (TREE_TYPE (inner_type), TREE_TYPE (outer_type)); This is the place where the TBAA problem from above will pop up if we recurse with tree_ssa_useless_type_conversion_1 here. Possibly the solution is to split tree_ssa_useless_type_conversion_1 and handle pointers specially. /* Fall back to what the frontend thinks of type compatibility. ??? This should eventually just return false. */ return lang_hooks.types_compatible_p (inner_type, outer_type); Before we can return false here we need to handle some more cases in this function. I have a patch that adds all missing trivial stuff but not doing structural equivalence checks - I'm not sure we really need these. Comments? Thanks, Richard. 2007-06-20 Richard Guenther <[EMAIL PROTECTED]> * tree-ssa.c (tree_ssa_useless_type_conversion_1): Document future intent. Re-structure to call langhook last, mark questionable parts. Index: tree-ssa.c =================================================================== *** tree-ssa.c.orig 2007-06-20 13:50:27.000000000 +0200 --- tree-ssa.c 2007-06-20 13:51:03.000000000 +0200 *************** delete_tree_ssa (void) *** 888,894 **** /* Return true if the conversion from INNER_TYPE to OUTER_TYPE is a ! useless type conversion, otherwise return false. */ bool tree_ssa_useless_type_conversion_1 (tree outer_type, tree inner_type) --- 888,910 ---- /* Return true if the conversion from INNER_TYPE to OUTER_TYPE is a ! useless type conversion, otherwise return false. ! This function implicitly defines the middle-end type system. The ! following invariants shall be fulfilled: ! ! 1) tree_ssa_useless_type_conversion_1 is transitive. If ! a < b and b < c then a < c. ! ! 2) tree_ssa_useless_type_conversion_1 is not communtative. ! From a < b does not follow a > b. ! ! 3) Conversions are useless only if with the resulting type ! can be used in a subset of the operations the original type ! can be applied to. For example casts to void* are useless, ! casts from void* not. Casts to const T* are useless, casts ! from const T* to T* not. ! ! ??? The above do not hold currently. */ bool tree_ssa_useless_type_conversion_1 (tree outer_type, tree inner_type) *************** tree_ssa_useless_type_conversion_1 (tree *** 900,974 **** if (TYPE_MODE (inner_type) != TYPE_MODE (outer_type)) return false; ! /* If the inner and outer types are effectively the same, then ! strip the type conversion and enter the equivalence into ! the table. */ ! if (lang_hooks.types_compatible_p (inner_type, outer_type)) ! return true; ! /* If both types are pointers and the outer type is a (void *), then ! the conversion is not necessary. The opposite is not true since ! that conversion would result in a loss of information if the ! equivalence was used. Consider an indirect function call where ! we need to know the exact type of the function to correctly ! implement the ABI. */ ! else if (POINTER_TYPE_P (inner_type) ! && POINTER_TYPE_P (outer_type) ! && TYPE_REF_CAN_ALIAS_ALL (inner_type) ! == TYPE_REF_CAN_ALIAS_ALL (outer_type) ! && TREE_CODE (TREE_TYPE (outer_type)) == VOID_TYPE) ! return true; ! /* Don't lose casts between pointers to volatile and non-volatile ! qualified types. Doing so would result in changing the semantics ! of later accesses. */ ! else if (POINTER_TYPE_P (inner_type) ! && POINTER_TYPE_P (outer_type) ! && TYPE_VOLATILE (TREE_TYPE (outer_type)) ! != TYPE_VOLATILE (TREE_TYPE (inner_type))) ! return false; ! /* Pointers/references are equivalent if their pointed to types ! are effectively the same. This allows to strip conversions between ! pointer types with different type qualifiers. */ else if (POINTER_TYPE_P (inner_type) ! && POINTER_TYPE_P (outer_type) ! && TYPE_REF_CAN_ALIAS_ALL (inner_type) ! == TYPE_REF_CAN_ALIAS_ALL (outer_type) ! && lang_hooks.types_compatible_p (TREE_TYPE (inner_type), ! TREE_TYPE (outer_type))) ! return true; ! ! /* If both the inner and outer types are integral types, then the ! conversion is not necessary if they have the same mode and ! signedness and precision, and both or neither are boolean. Some ! code assumes an invariant that boolean types stay boolean and do ! not become 1-bit bit-field types. Note that types with precision ! not using all bits of the mode (such as bit-field types in C) ! mean that testing of precision is necessary. */ ! else if (INTEGRAL_TYPE_P (inner_type) ! && INTEGRAL_TYPE_P (outer_type) ! && TYPE_UNSIGNED (inner_type) == TYPE_UNSIGNED (outer_type) ! && TYPE_PRECISION (inner_type) == TYPE_PRECISION (outer_type)) { ! tree min_inner = fold_convert (outer_type, TYPE_MIN_VALUE (inner_type)); ! tree max_inner = fold_convert (outer_type, TYPE_MAX_VALUE (inner_type)); ! bool first_boolean = (TREE_CODE (inner_type) == BOOLEAN_TYPE); ! bool second_boolean = (TREE_CODE (outer_type) == BOOLEAN_TYPE); ! if (simple_cst_equal (max_inner, TYPE_MAX_VALUE (outer_type)) ! && simple_cst_equal (min_inner, TYPE_MIN_VALUE (outer_type)) ! && first_boolean == second_boolean) return true; } /* Recurse for complex types. */ else if (TREE_CODE (inner_type) == COMPLEX_TYPE ! && TREE_CODE (outer_type) == COMPLEX_TYPE ! && tree_ssa_useless_type_conversion_1 (TREE_TYPE (outer_type), ! TREE_TYPE (inner_type))) ! return true; ! ! return false; } /* Return true if EXPR is a useless type conversion, otherwise return --- 916,998 ---- if (TYPE_MODE (inner_type) != TYPE_MODE (outer_type)) return false; ! /* If both the inner and outer types are integral types, then the ! conversion is not necessary if they have the same mode and ! signedness and precision, and both or neither are boolean. */ ! if (INTEGRAL_TYPE_P (inner_type) ! && INTEGRAL_TYPE_P (outer_type)) ! { ! /* Preserve changes in signedness or precision. */ ! if (TYPE_UNSIGNED (inner_type) != TYPE_UNSIGNED (outer_type) ! || TYPE_PRECISION (inner_type) != TYPE_PRECISION (outer_type)) ! return false; ! ! /* Preserve booleaness. Some code assumes an invariant that boolean ! types stay boolean and do not become 1-bit bit-field types. */ ! if ((TREE_CODE (inner_type) == BOOLEAN_TYPE) ! != (TREE_CODE (outer_type) == BOOLEAN_TYPE)) ! return false; ! ! /* Preserve changes in the types minimum or maximum value. ! ??? Due to the way we handle sizetype as signed we need ! to jump through hoops here to make sizetype and size_type_node ! compatible. */ ! if (!tree_int_cst_equal (fold_convert (outer_type, ! TYPE_MIN_VALUE (inner_type)), ! TYPE_MIN_VALUE (outer_type)) ! || !tree_int_cst_equal (fold_convert (outer_type, ! TYPE_MAX_VALUE (inner_type)), ! TYPE_MAX_VALUE (outer_type))) ! return false; ! /* ??? We might want to preserve base type changes because of ! TBAA. Or we need to be extra careful below. */ ! return true; ! } ! /* We need to take special care recursing to pointed-to types. */ else if (POINTER_TYPE_P (inner_type) ! && POINTER_TYPE_P (outer_type)) { ! /* Don't lose casts between pointers to volatile and non-volatile ! qualified types. Doing so would result in changing the semantics ! of later accesses. */ ! if (TYPE_VOLATILE (TREE_TYPE (outer_type)) ! != TYPE_VOLATILE (TREE_TYPE (inner_type))) ! return false; ! ! /* Do not lose casts between pointers with different ! TYPE_REF_CAN_ALIAS_ALL setting. */ ! if (TYPE_REF_CAN_ALIAS_ALL (inner_type) ! != TYPE_REF_CAN_ALIAS_ALL (outer_type)) ! return false; ! ! /* If the outer type is (void *), then the conversion is not ! necessary. ! ??? This makes tree_ssa_useless_type_conversion_1 not ! transitive. */ ! if (TREE_CODE (TREE_TYPE (outer_type)) == VOID_TYPE) return true; + + /* Otherwise pointers/references are equivalent if their pointed + to types are effectively the same. This allows to strip conversions + between pointer types with different type qualifiers. + ??? We should recurse here with + tree_ssa_useless_type_conversion_1. */ + return lang_hooks.types_compatible_p (TREE_TYPE (inner_type), + TREE_TYPE (outer_type)); } /* Recurse for complex types. */ else if (TREE_CODE (inner_type) == COMPLEX_TYPE ! && TREE_CODE (outer_type) == COMPLEX_TYPE) ! return tree_ssa_useless_type_conversion_1 (TREE_TYPE (outer_type), ! TREE_TYPE (inner_type)); ! ! /* Fall back to what the frontend thinks of type compatibility. ! ??? This should eventually just return false. */ ! return lang_hooks.types_compatible_p (inner_type, outer_type); } /* Return true if EXPR is a useless type conversion, otherwise return