[tree-ssa] Kill more useless type conversions, expose more expressions

law@redhat.com law@redhat.com
Fri Aug 15 15:37:00 GMT 2003


This patch has three key purposes.

  1. Remove (during gimplification) more useless type casts.  The code to
  determine if a type cast is useless is shared between the dominator
  optimizer and the gimplifier.

  2. Whenever possible make the type of a COMPONENT_REF expression match
  its associated FIELD_DECL type.  This is possible when the COMPONENT_REF
  is wrapped with a NOP_EXPR -- and we will add a suitable NOP_EXPR in
  some cases to make this possible.  While this can increase NOP_EXPRs,
  it canonicalizes types for memory references which tends to expose
  more redundant memory operations.

  3. If the RHS of a MODIFY_EXPR is a CALL_EXPR, then the result must
  always be stored in a gimple temporary.  This tends to expose more
  redundant expressions.


Whee.  This fixes a few more of the tree-ssa tests.

	* gimplify.c (gimplify_expr, cases NOP_EXPR, CONVERT_EXPR): If a
	COMPONENT_REF is wrapped with a NOP_EXPR, then force the type of
	the COMPONENT_REF to match the accessed field.  Strip away
	unnecessary type conversions and handle the case where all type
	conversions were removed.
	(case ARRAY_REF, COMPONENT_REF): Indicate to gimplify_array_ref
	and gimplify_component_ref if we want an lvalue or not.
	(gimplify_array_ref, gimplify_component_ref): Pass new argument
	WANT_LVALUE through to gimplify_compound_lval.
	(gimplify_compound_lval): If we do not want an lvalue and the
	toplevel COMPONENT_REF's type does not match its field's type,
	then wrap the COMPONENT_REF in a NOP_EXPR and force the 
	COMPONENT_REF's type to match its field's type.
	(gimplify_modify_expr): If the RHS is a CALL_EXPR and the LHS
	is not a gimple temporary, then force the RHS through a gimple
	temporary, even if the call can not throw.
	(create_tmp_var): Make sure not to lose the type's attributes
	for the new variable.
	* tree-ssa.c (tree_ssa_useless_type_conversion): New function.
	* tree-flow.h (tree_ssa_useless_type_conversion): Prototype.
	* tree-ssa-dom.c (optimize_stmt): Use tree_ssa_useless_type_conversion.


Index: gimplify.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/gimplify.c,v
retrieving revision 1.1.2.69
diff -c -3 -p -r1.1.2.69 gimplify.c
*** gimplify.c	11 Aug 2003 17:17:15 -0000	1.1.2.69
--- gimplify.c	15 Aug 2003 13:15:07 -0000
*************** Software Foundation, 59 Temple Place - S
*** 43,52 ****
  #include "real.h"
  
  static void gimplify_constructor (tree, tree *, tree *);
! static void gimplify_array_ref (tree *, tree *, tree *);
  static void gimplify_array_ref_to_plus (tree *, tree *, tree *);
! static void gimplify_compound_lval (tree *, tree *, tree *);
! static void gimplify_component_ref (tree *, tree *, tree *);
  static void gimplify_call_expr (tree *, tree *, tree *, int (*) (tree));
  static void gimplify_tree_list (tree *, tree *, tree *);
  static void gimplify_modify_expr (tree *, tree *, tree *, int);
--- 43,52 ----
  #include "real.h"
  
  static void gimplify_constructor (tree, tree *, tree *);
! static void gimplify_array_ref (tree *, tree *, tree *, int);
  static void gimplify_array_ref_to_plus (tree *, tree *, tree *);
! static void gimplify_compound_lval (tree *, tree *, tree *, int);
! static void gimplify_component_ref (tree *, tree *, tree *, int);
  static void gimplify_call_expr (tree *, tree *, tree *, int (*) (tree));
  static void gimplify_tree_list (tree *, tree *, tree *);
  static void gimplify_modify_expr (tree *, tree *, tree *, int);
*************** gimplify_expr (tree *expr_p, tree *pre_p
*** 409,419 ****
  	  break;
  
  	case ARRAY_REF:
! 	  gimplify_array_ref (expr_p, pre_p, post_p);
  	  break;
  
  	case COMPONENT_REF:
! 	  gimplify_component_ref (expr_p, pre_p, post_p);
  	  break;
  
  	case COND_EXPR:
--- 409,419 ----
  	  break;
  
  	case ARRAY_REF:
! 	  gimplify_array_ref (expr_p, pre_p, post_p, fallback & fb_lvalue);
  	  break;
  
  	case COMPONENT_REF:
! 	  gimplify_component_ref (expr_p, pre_p, post_p, fallback & fb_lvalue);
  	  break;
  
  	case COND_EXPR:
*************** gimplify_expr (tree *expr_p, tree *pre_p
*** 477,491 ****
  	      *expr_p = TREE_OPERAND (*expr_p, 0);
  	      break;
  	    }
! 	  /* Only keep the outermost NOP/CONVERT.  */
! 	  STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
! 	  /* And not even that if it's useless.  */
! 	  if (TYPE_MAIN_VARIANT (TREE_TYPE (*expr_p))
! 	      == TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (*expr_p, 0))))
  	    {
! 	      *expr_p = TREE_OPERAND (*expr_p, 0);
! 	      break;
  	    }
  
  	case FIX_TRUNC_EXPR:
  	case FIX_CEIL_EXPR:
--- 477,518 ----
  	      *expr_p = TREE_OPERAND (*expr_p, 0);
  	      break;
  	    }
! 
! 	  /* If a NOP conversion is changing the type of a COMPONENT_REF
! 	     expression, then it is safe to force the type of the
! 	     COMPONENT_REF to be the same as the type of the field the
! 	     COMPONENT_REF is accessing.
! 
! 	     This is a profitable thing to do as canonicalization of
! 	     types on COMPONENT_REFs exposes more redundant COMPONENT_REFs.  */
! 	  if (TREE_CODE (TREE_OPERAND (*expr_p, 0)) == COMPONENT_REF)
  	    {
! 	      TREE_TYPE (TREE_OPERAND (*expr_p, 0))
! 		= TREE_TYPE (TREE_OPERAND (TREE_OPERAND (*expr_p, 0), 1));
! 	    }
! 
! 	  /* Strip away as many useless type conversions as possible
! 	     at the toplevel.  */
! 	  while (tree_ssa_useless_type_conversion (*expr_p))
! 	    *expr_p = TREE_OPERAND (*expr_p, 0);
! 
! 	  /* If we still have a conversion at the toplevel, then strip
! 	     away all but the outermost conversion.  */
! 	  if (TREE_CODE (*expr_p) == NOP_EXPR
! 	      || TREE_CODE (*expr_p) == CONVERT_EXPR)
! 	    {
! 	      STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
! 
! 	      /* And remove the outermost conversion if it's useless.  */
! 	      if (TYPE_MAIN_VARIANT (TREE_TYPE (*expr_p))
! 		  == TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (*expr_p, 0))))
! 		{
! 		  *expr_p = TREE_OPERAND (*expr_p, 0);
! 		  break;
! 		}
  	    }
+ 	  else
+ 	    break;
  
  	case FIX_TRUNC_EXPR:
  	case FIX_CEIL_EXPR:
*************** build_addr_expr (tree t)
*** 1287,1293 ****
      ARRAY_REF should be extended.  */
  
  static void
! gimplify_array_ref (tree *expr_p, tree *pre_p, tree *post_p)
  {
  #if 1
    tree elttype = TREE_TYPE (TREE_TYPE (TREE_OPERAND (*expr_p, 0)));
--- 1314,1321 ----
      ARRAY_REF should be extended.  */
  
  static void
! gimplify_array_ref (tree *expr_p, tree *pre_p,
! 		    tree *post_p, int want_lvalue)
  {
  #if 1
    tree elttype = TREE_TYPE (TREE_TYPE (TREE_OPERAND (*expr_p, 0)));
*************** gimplify_array_ref (tree *expr_p, tree *
*** 1298,1304 ****
    else
      /* Handle array and member refs together for now.  When alias analysis
         improves, we may want to go back to handling them separately.  */
!     gimplify_compound_lval (expr_p, pre_p, post_p);
  #else
    tree *p;
    varray_type dim_stack;
--- 1326,1332 ----
    else
      /* Handle array and member refs together for now.  When alias analysis
         improves, we may want to go back to handling them separately.  */
!     gimplify_compound_lval (expr_p, pre_p, post_p, want_lvalue);
  #else
    tree *p;
    varray_type dim_stack;
*************** gimplify_array_ref_to_plus (tree *expr_p
*** 1362,1368 ****
       *EXPR_P should be stored.  */
  
  static void
! gimplify_compound_lval (tree *expr_p, tree *pre_p, tree *post_p)
  {
    tree *p;
    enum tree_code code;
--- 1390,1397 ----
       *EXPR_P should be stored.  */
  
  static void
! gimplify_compound_lval (tree *expr_p, tree *pre_p,
! 			tree *post_p, int want_lvalue)
  {
    tree *p;
    enum tree_code code;
*************** gimplify_compound_lval (tree *expr_p, tr
*** 1415,1420 ****
--- 1444,1469 ----
        /* Update TREE_SIDE_EFFECTS.  */
        recalculate_side_effects (t);
      }
+ 
+   /* Now look at the toplevel expression.  If it is a COMPONENT_REF and
+      the type of the COMPONENT_REF is different than the field being
+      referenced, then wrap the whole thing inside a NOP_EXPR and force
+      the type of the COMPONENT_REF to be the same as the field being
+      referenced.  */
+   if (! want_lvalue
+       && TREE_CODE (*expr_p) == COMPONENT_REF
+       && TREE_TYPE (*expr_p) != TREE_TYPE (TREE_OPERAND (*expr_p, 1)))
+     {
+       tree type_for_nop_expr = TREE_TYPE (*expr_p);
+ 
+       /* Set the type of the COMPONENT_REF to the type of the field
+ 	 being referenced.  */
+       TREE_TYPE (*expr_p) = TREE_TYPE (TREE_OPERAND (*expr_p, 1));
+ 
+       /* And wrap the whole thing inside a NOP_EXPR.  */
+       *expr_p = build1 (NOP_EXPR, type_for_nop_expr, *expr_p);
+       recalculate_side_effects (*expr_p);
+     }
  }
  
  /*  Gimplify the self modifying expression pointed by EXPR_P (++, --, +=, 
-=).
*************** gimplify_self_mod_expr (tree *expr_p, tr
*** 1498,1509 ****
          *EXPR_P should be stored.  */
  
  static void
! gimplify_component_ref (tree *expr_p, tree *pre_p, tree *post_p)
  {
  #if 1
    /* Handle array and member refs together for now.  When alias analysis
       improves, we may want to go back to handling them separately.  */
!   gimplify_compound_lval (expr_p, pre_p, post_p);
  #else
    tree *p;
  
--- 1547,1559 ----
          *EXPR_P should be stored.  */
  
  static void
! gimplify_component_ref (tree *expr_p, tree *pre_p,
! 			tree *post_p, int want_lvalue)
  {
  #if 1
    /* Handle array and member refs together for now.  When alias analysis
       improves, we may want to go back to handling them separately.  */
!   gimplify_compound_lval (expr_p, pre_p, post_p, want_lvalue);
  #else
    tree *p;
  
*************** gimplify_modify_expr (tree *expr_p, tree
*** 1999,2007 ****
  
       FIXME this should be handled by the is_gimple_rhs predicate.  */
    if (! is_gimple_tmp_var (*to_p)
!       && ((TREE_CODE (*from_p) == CALL_EXPR
! 	   && ((flag_exceptions && ! (call_expr_flags (*from_p) & ECF_NOTHROW))
! 	       || FUNCTION_RECEIVES_NONLOCAL_GOTO (current_function_decl)))
  	  || (flag_non_call_exceptions && could_trap_p (*from_p))))
      gimplify_expr (from_p, pre_p, post_p, is_gimple_val, fb_rvalue);
  
--- 2049,2055 ----
  
       FIXME this should be handled by the is_gimple_rhs predicate.  */
    if (! is_gimple_tmp_var (*to_p)
!       && (TREE_CODE (*from_p) == CALL_EXPR
  	  || (flag_non_call_exceptions && could_trap_p (*from_p))))
      gimplify_expr (from_p, pre_p, post_p, is_gimple_val, fb_rvalue);
  
*************** create_tmp_var (tree type, const char *p
*** 2323,2328 ****
--- 2371,2377 ----
    char *tmp_name;
    char *preftmp = NULL;
    tree tmp_var;
+   tree new_type;
  
    if (prefix)
      {
*************** create_tmp_var (tree type, const char *p
*** 2343,2349 ****
  #endif
  
    /* Make the type of the variable writable.  */
!   type = build_type_variant (type, 0, 0);
  
    tmp_var = build_decl (VAR_DECL, get_identifier (tmp_name), type);
  
--- 2392,2399 ----
  #endif
  
    /* Make the type of the variable writable.  */
!   new_type = build_type_variant (type, 0, 0);
!   TYPE_ATTRIBUTES (new_type) = TYPE_ATTRIBUTES (type);
  
    tmp_var = build_decl (VAR_DECL, get_identifier (tmp_name), type);
  
Index: tree-ssa.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-ssa.c,v
retrieving revision 1.1.4.115
diff -c -3 -p -r1.1.4.115 tree-ssa.c
*** tree-ssa.c	8 Aug 2003 00:27:10 -0000	1.1.4.115
--- tree-ssa.c	15 Aug 2003 13:15:10 -0000
*************** get_def_blocks_for (tree var)
*** 2393,2396 ****
--- 2393,2440 ----
    return (struct def_blocks_d *) htab_find (def_blocks, &dm);
  }
  
+ /* Return true if EXPR is a useless type conversion, otherwise return
+    false.  */
+ 
+ bool
+ tree_ssa_useless_type_conversion (tree expr)
+ {
+   /* If we have an assignment that merely uses a NOP_EXPR to change
+      the top of the RHS to the type of the LHS and the type conversion
+      is "safe", then strip away the type conversion so that we can
+      enter LHS = RHS into the const_and_copies table.  */
+   if (TREE_CODE (expr) == NOP_EXPR || TREE_CODE (expr) == CONVERT_EXPR)
+     {
+       tree outer_type = TREE_TYPE (expr);
+       tree inner_type = TREE_TYPE (TREE_OPERAND (expr, 0));
+ 
+       /* If the inner and outer types are effectively the same, then
+          strip the type conversion and enter the equivalence into
+          the table.  */
+       if (inner_type == outer_type
+ 	  || TYPE_MAIN_VARIANT (inner_type) == TYPE_MAIN_VARIANT (outer_type))
+         return true;
+ 
+       /* If the outer type is a (void *), then we can enter the
+          equivalence into the table.  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)
+ 	       && TREE_CODE (TREE_TYPE (outer_type)) == VOID_TYPE)
+ 	return true;
+ 
+       /* If both the inner and outer types are integral types, then
+          we can enter the equivalence if they have the same mode
+          and signedness.  */
+       else if (INTEGRAL_TYPE_P (inner_type) && INTEGRAL_TYPE_P (outer_type)
+ 	       && TYPE_MODE (inner_type) == TYPE_MODE (outer_type)
+ 	       && TREE_UNSIGNED (inner_type) == TREE_UNSIGNED (outer_type))
+ 	return true;
+     }
+ 
+   return false;
+ }
+ 
  #include "gt-tree-ssa.h"
Index: tree-flow.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-flow.h,v
retrieving revision 1.1.4.101
diff -c -3 -p -r1.1.4.101 tree-flow.h
*** tree-flow.h	8 Aug 2003 00:27:10 -0000	1.1.4.101
--- tree-flow.h	15 Aug 2003 13:15:12 -0000
*************** extern void dump_tree_ssa_stats (FILE *)
*** 470,475 ****
--- 470,476 ----
  extern void debug_tree_ssa_stats (void);
  extern void ssa_remove_edge (edge);
  extern void set_is_used (tree);
+ extern bool tree_ssa_useless_type_conversion (tree);
  
  /* In tree-ssa-pre.c  */
  extern void tree_perform_ssapre (tree);
Index: tree-ssa-dom.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-ssa-dom.c,v
retrieving revision 1.1.2.23
diff -c -3 -p -r1.1.2.23 tree-ssa-dom.c
*** tree-ssa-dom.c	15 Aug 2003 07:23:42 -0000	1.1.2.23
--- tree-ssa-dom.c	15 Aug 2003 13:15:13 -0000
*************** optimize_stmt (block_stmt_iterator si, v
*** 732,776 ****
    if (TREE_CODE (stmt) == MODIFY_EXPR
        && TREE_CODE (TREE_OPERAND (stmt, 0)) == SSA_NAME)
      {
!       tree rhs;
  
!       rhs = TREE_OPERAND (stmt, 1);
! 
!       /* Some NOP_EXPRs are totally uninteresting and get in the way
! 	 to optimizing as they prevent equivalences from being recognized.
! 
! 	 If we have an assignment that merely uses a NOP_EXPR to change
! 	 the top of the RHS to the type of the LHS and the type conversion
! 	 is "safe", then strip away the type conversion so that we can
! 	 enter LHS = RHS into the const_and_copies table.  */
!       if (TREE_CODE (rhs) == NOP_EXPR)
! 	{
! 	  tree outer_type = TREE_TYPE (rhs);
! 	  tree inner_type = TREE_TYPE (TREE_OPERAND (rhs, 0));
! 
! 	  /* If the inner and outer types are effectively the same, then
! 	     strip the type conversion and enter the equivalence into
! 	     the table.  */
! 	  if (TYPE_MAIN_VARIANT (inner_type) == TYPE_MAIN_VARIANT (outer_type)
! 	      || outer_type == inner_type)
! 	    STRIP_MAIN_TYPE_NOPS (rhs);
! 	  /* If the outer type is a (void *), then we can enter the
! 	     equivalence into the table.  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 (outer_type) && POINTER_TYPE_P (inner_type))
! 	    {
! 	      if (TREE_CODE (TREE_TYPE (outer_type)) == VOID_TYPE)
! 		rhs = TREE_OPERAND (rhs, 0);
! 	    }
! 	  /* If both the inner and outer types are integral types, then
! 	     we can enter the equivalence if they have the same mode
! 	     and signedness.  */
! 	  else if (INTEGRAL_TYPE_P (outer_type) && INTEGRAL_TYPE_P (inner_type))
! 	    STRIP_SIGN_NOPS (rhs);
! 	}
  
        if (may_optimize_p)
  	{
--- 732,742 ----
    if (TREE_CODE (stmt) == MODIFY_EXPR
        && TREE_CODE (TREE_OPERAND (stmt, 0)) == SSA_NAME)
      {
!       tree rhs = TREE_OPERAND (stmt, 1);
  
!       /* Strip away any useless type conversions.  */
!       while (tree_ssa_useless_type_conversion (rhs))
! 	rhs = TREE_OPERAND (rhs, 0);
  
        if (may_optimize_p)
  	{







More information about the Gcc-patches mailing list