[PATCH][RFC] Re-structure tree_ssa_useless_type_conversion_1 to work towards a middle-end type system

Richard Guenther rguenther@suse.de
Wed Jun 20 12:50:00 GMT 2007


(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  <rguenther@suse.de>

	* 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



More information about the Gcc mailing list