tree-ssa.c (useless_type_conversion_p): Document future intent as defining the middle-end type system.

2007-07-02  Richard Guenther  <rguenther@suse.de>

	* tree-ssa.c (useless_type_conversion_p): Document
	future intent as defining the middle-end type system.
	Re-structure to call langhook last, group by type class,
	mark questionable parts.

From-SVN: r126199
This commit is contained in:
Richard Guenther 2007-07-02 12:13:39 +00:00 committed by Richard Biener
parent f4088621a5
commit 85b19f61bc
2 changed files with 98 additions and 61 deletions

View File

@ -1,3 +1,10 @@
2007-07-02 Richard Guenther <rguenther@suse.de>
* tree-ssa.c (useless_type_conversion_p): Document
future intent as defining the middle-end type system.
Re-structure to call langhook last, group by type class,
mark questionable parts.
2007-07-02 Richard Guenther <rguenther@suse.de>
* tree-flow.h (types_compatible_p): Declare.

View File

@ -888,7 +888,29 @@ delete_tree_ssa (void)
/* Return true if the conversion from INNER_TYPE to OUTER_TYPE is a
useless type conversion, otherwise return false. */
useless type conversion, otherwise return false.
This function implicitly defines the middle-end type system. With
the notion of 'a < b' meaning that useless_type_conversion_p (a, b)
holds and 'a > b' meaning that useless_type_conversion_p (b, a) holds,
the following invariants shall be fulfilled:
1) useless_type_conversion_p is transitive.
If a < b and b < c then a < c.
2) useless_type_conversion_p is not symmetric.
From a < b does not follow a > b.
3) Types define the available set of operations applicable to values.
A type conversion is useless if the operations for the target type
is a subset of the operations for the source type. For example
casts to void* are useless, casts from void* are not (void* can't
be dereferenced or offsetted, but copied, hence its set of operations
is a strict subset of that of all other data pointer types). Casts
to const T* are useless (can't be written to), casts from const T*
to T* are not.
??? The above do not hold currently. */
bool
useless_type_conversion_p (tree outer_type, tree inner_type)
@ -900,75 +922,83 @@ useless_type_conversion_p (tree outer_type, tree inner_type)
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))
signedness and precision, and both or neither are boolean. */
if (INTEGRAL_TYPE_P (inner_type)
&& INTEGRAL_TYPE_P (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)
/* 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 booleanness. 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.
??? Together with calling the langhook below this makes
useless_type_conversion_p 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
useless_type_conversion_p. */
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
&& useless_type_conversion_p (TREE_TYPE (outer_type),
TREE_TYPE (inner_type)))
return true;
&& TREE_CODE (outer_type) == COMPLEX_TYPE)
return useless_type_conversion_p (TREE_TYPE (outer_type),
TREE_TYPE (inner_type));
return false;
/* 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 a conversion from either type of TYPE1 and TYPE2