This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH][RFC] Optimize type-punning union accesses to VIEW_CONVERT_EXPRs


This tweak to SCCVN makes us optimize type-punning with unions by
using VIEW_CONVERT_EXPRs on the stored values instead.  This makes
the union temporaries unnecessary and causes us to generate better
code for certain SSE intrinsics testcases (PR34043 for example).

The key is to change the SCCVN IL to not record the field decl and type
for a union member access, but only the access size.  For the testcase
below this will then value number the loads of a.u.f and a.u.k to j.
The fixup for using a VIEW_CONVERT_EXPR happens during elimination
(and for simplification), where we for non-matching types now create
a VIEW_CONVERT_EXPR instead of simply folding (that folding should
only have happened for wrong types in the IL).

[
 This fixup during elimination looks somewhat ugly, but the nature of
 SCCVN doesn't allow inserting new SSA_NAMEs with expressions.  That
 is, for

   a.u.j = j;
   tmp = a.u.f;

 we'd want to insert

   tmp' = VIEW_CONVERT_EXPR <float, j>;

 and value number tmp to tmp' instead of j.  Instead of doing so
 we (possibly repeatedly) replace with a fixed-up j instead.  Which
 turns to work out quite well for the case of optimizing type-punning.
]

The patch also adds one additional folding for VIEW_CONVERT_EXPRs,
namely using NOP_EXPRs for integral conversions that do not change
the precision.

IVOPTs needs an adjustment because with the patch we try to generate
an induction variable with the base &VIEW_CONVERT_EXPR <0>.  Fixed
by making may_be_nonaddressable_p return true for register or constant
arguments to VIEW_CONVERT_EXPR.  (I forgot on which testcase this
happened)

Bootstrapped and tested on x86_64-unknown-linux-gnu.

Does this make sense?
Richard.

2008-02-28  Richard Guenther  <rguenther@suse.de>

	* fold-const.c (fold_unary): Fold VIEW_CONVERT_EXPR to NOP_EXPR
	if it does integral conversion with unchanged precision.
	* tree-ssa-sccvn.c (copy_reference_ops_from_ref): Value-number
	union member access based on its size, not type and member.
	(valueize_expr): Take expression type, insert conversions for
	non-matching types.
	(simplify_unary_expression): Adjust calls to valueize_expr.
	(simplify_binary_expression): Likewise.
	* tree-ssa-pre.c (eliminate): Use VIEW_CONVERT_EXPR for
	mismatched types.
	* tree-ssa-loop-ivopts.c (may_be_nonaddressable_p): Constants
	and gimple registers are not addressable.

Index: fold-const.c
===================================================================
*** fold-const.c	(revision 132738)
--- fold-const.c	(working copy)
*************** fold_unary (enum tree_code code, tree ty
*** 8270,8282 ****
      case VIEW_CONVERT_EXPR:
        if (TREE_TYPE (op0) == type)
  	return op0;
!       if (TREE_CODE (op0) == VIEW_CONVERT_EXPR
! 	  || (TREE_CODE (op0) == NOP_EXPR
! 	      && INTEGRAL_TYPE_P (TREE_TYPE (op0))
! 	      && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (op0, 0)))
! 	      && TYPE_PRECISION (TREE_TYPE (op0))
! 		 == TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op0, 0)))))
  	return fold_build1 (VIEW_CONVERT_EXPR, type, TREE_OPERAND (op0, 0));
        return fold_view_convert_expr (type, op0);
  
      case NEGATE_EXPR:
--- 8270,8294 ----
      case VIEW_CONVERT_EXPR:
        if (TREE_TYPE (op0) == type)
  	return op0;
!       if (TREE_CODE (op0) == VIEW_CONVERT_EXPR)
  	return fold_build1 (VIEW_CONVERT_EXPR, type, TREE_OPERAND (op0, 0));
+ 
+       /* For integral conversions with the same precision or pointer
+ 	 conversions use a NOP_EXPR instead.  */
+       if ((INTEGRAL_TYPE_P (type) && INTEGRAL_TYPE_P (TREE_TYPE (op0))
+ 	   && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (op0)))
+ 	  || (POINTER_TYPE_P (type) && POINTER_TYPE_P (TREE_TYPE (op0))))
+ 	return fold_convert (type, op0);
+ 
+       /* Strip inner integral conversions that do not change the precision.  */
+       if ((TREE_CODE (op0) == NOP_EXPR
+ 	   || TREE_CODE (op0) == CONVERT_EXPR)
+ 	  && INTEGRAL_TYPE_P (TREE_TYPE (op0))
+ 	  && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (op0, 0)))
+ 	  && (TYPE_PRECISION (TREE_TYPE (op0))
+ 	      == TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op0, 0)))))
+ 	return fold_build1 (VIEW_CONVERT_EXPR, type, TREE_OPERAND (op0, 0));
+ 
        return fold_view_convert_expr (type, op0);
  
      case NEGATE_EXPR:
Index: tree-ssa-pre.c
===================================================================
*** tree-ssa-pre.c	(revision 132738)
--- tree-ssa-pre.c	(working copy)
*************** eliminate (void)
*** 3640,3651 ****
  		  if (TREE_CODE (sprime) == SSA_NAME)
  		    NECESSARY (SSA_NAME_DEF_STMT (sprime)) = 1;
  		  /* We need to make sure the new and old types actually match,
! 		     which may require adding a simple cast, which fold_convert
! 		     will do for us.  */
  		  if (TREE_CODE (*rhs_p) != SSA_NAME
  		      && !useless_type_conversion_p (TREE_TYPE (*rhs_p),
  						    TREE_TYPE (sprime)))
! 		    sprime = fold_convert (TREE_TYPE (*rhs_p), sprime);
  
  		  pre_stats.eliminations++;
  		  propagate_tree_value (rhs_p, sprime);
--- 3640,3652 ----
  		  if (TREE_CODE (sprime) == SSA_NAME)
  		    NECESSARY (SSA_NAME_DEF_STMT (sprime)) = 1;
  		  /* We need to make sure the new and old types actually match,
! 		     as we value-number across same-sized union member
! 		     loads and stores.  */
  		  if (TREE_CODE (*rhs_p) != SSA_NAME
  		      && !useless_type_conversion_p (TREE_TYPE (*rhs_p),
  						    TREE_TYPE (sprime)))
! 		    sprime = fold_build1 (VIEW_CONVERT_EXPR,
! 					  TREE_TYPE (*rhs_p), sprime);
  
  		  pre_stats.eliminations++;
  		  propagate_tree_value (rhs_p, sprime);
Index: testsuite/gcc.dg/tree-ssa/ssa-fre-7.c
===================================================================
*** testsuite/gcc.dg/tree-ssa/ssa-fre-7.c	(revision 0)
--- testsuite/gcc.dg/tree-ssa/ssa-fre-7.c	(revision 0)
***************
*** 0 ****
--- 1,26 ----
+ /* { dg-do compile } */
+ /* { dg-options "-O -fdump-tree-fre-details -fdump-tree-optimized" } */
+ 
+ struct X {
+   int i;
+   union {
+     int j;
+     int k;
+     float f;
+   } u;
+ };
+ 
+ int foo(int j)
+ {
+   struct X a;
+ 
+   a.u.j = j;
+   a.u.f = a.u.f;
+   return a.u.k;
+ }
+ 
+ /* { dg-final { scan-tree-dump "Replaced a.u.f with j" "fre" } } */
+ /* { dg-final { scan-tree-dump "Replaced a.u.k with j" "fre" } } */
+ /* { dg-final { scan-tree-dump "return j" "optimized" } } */
+ /* { dg-final { cleanup-tree-dump "fre" } } */
+ /* { dg-final { cleanup-tree-dump "optimized" } } */
Index: tree-ssa-loop-ivopts.c
===================================================================
*** tree-ssa-loop-ivopts.c	(revision 132738)
--- tree-ssa-loop-ivopts.c	(working copy)
*************** may_be_nonaddressable_p (tree expr)
*** 1522,1529 ****
  	 and make them look addressable.  After some processing the
  	 non-addressability may be uncovered again, causing ADDR_EXPRs
  	 of inappropriate objects to be built.  */
!       if (AGGREGATE_TYPE_P (TREE_TYPE (expr))
! 	  && !AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0))))
  	return true;
  
        /* ... fall through ... */
--- 1522,1531 ----
  	 and make them look addressable.  After some processing the
  	 non-addressability may be uncovered again, causing ADDR_EXPRs
  	 of inappropriate objects to be built.  */
!       if (is_gimple_reg (TREE_OPERAND (expr, 0))
! 	  || CONSTANT_CLASS_P (TREE_OPERAND (expr, 0))
! 	  || (AGGREGATE_TYPE_P (TREE_TYPE (expr))
! 	      && !AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0)))))
  	return true;
  
        /* ... fall through ... */
Index: tree-ssa-sccvn.c
===================================================================
*** tree-ssa-sccvn.c	(revision 132750)
--- tree-ssa-sccvn.c	(working copy)
*************** copy_reference_ops_from_ref (tree ref, V
*** 544,551 ****
  	  temp.op1 = TREE_OPERAND (ref, 2);
  	  break;
  	case COMPONENT_REF:
! 	  /* Record field as operand.  */
! 	  temp.op0 = TREE_OPERAND (ref, 1);
  	  break;
  	case ARRAY_RANGE_REF:
  	case ARRAY_REF:
--- 544,561 ----
  	  temp.op1 = TREE_OPERAND (ref, 2);
  	  break;
  	case COMPONENT_REF:
! 	  /* If this is a reference to a union member, record the union
! 	     member size as operand.  */
! 	  if (TREE_CODE (DECL_CONTEXT (TREE_OPERAND (ref, 1))) == UNION_TYPE
! 	      && integer_zerop (DECL_FIELD_OFFSET (TREE_OPERAND (ref, 1)))
! 	      && integer_zerop (DECL_FIELD_BIT_OFFSET (TREE_OPERAND (ref, 1))))
! 	    {
! 	      temp.type = NULL_TREE;
! 	      temp.op0 = TYPE_SIZE (TREE_TYPE (TREE_OPERAND (ref, 1)));
! 	    }
! 	  else
! 	    /* Record field as operand.  */
! 	    temp.op0 = TREE_OPERAND (ref, 1);
  	  break;
  	case ARRAY_RANGE_REF:
  	case ARRAY_REF:
*************** expr_has_constants (tree expr)
*** 1403,1428 ****
     This is performed in place. */
  
  static tree
! valueize_expr (tree expr)
  {
    switch (TREE_CODE_CLASS (TREE_CODE (expr)))
      {
      case tcc_unary:
        if (TREE_CODE (TREE_OPERAND (expr, 0)) == SSA_NAME
  	  && SSA_VAL (TREE_OPERAND (expr, 0)) != VN_TOP)
! 	TREE_OPERAND (expr, 0) = SSA_VAL (TREE_OPERAND (expr, 0));
        break;
      case tcc_binary:
        if (TREE_CODE (TREE_OPERAND (expr, 0)) == SSA_NAME
  	  && SSA_VAL (TREE_OPERAND (expr, 0)) != VN_TOP)
! 	TREE_OPERAND (expr, 0) = SSA_VAL (TREE_OPERAND (expr, 0));
        if (TREE_CODE (TREE_OPERAND (expr, 1)) == SSA_NAME
  	  && SSA_VAL (TREE_OPERAND (expr, 1)) != VN_TOP)
! 	TREE_OPERAND (expr, 1) = SSA_VAL (TREE_OPERAND (expr, 1));
        break;
      default:
        break;
      }
    return expr;
  }
  
--- 1413,1461 ----
     This is performed in place. */
  
  static tree
! valueize_expr (tree type, tree expr)
  {
    switch (TREE_CODE_CLASS (TREE_CODE (expr)))
      {
      case tcc_unary:
        if (TREE_CODE (TREE_OPERAND (expr, 0)) == SSA_NAME
  	  && SSA_VAL (TREE_OPERAND (expr, 0)) != VN_TOP)
! 	{
! 	  tree val = SSA_VAL (TREE_OPERAND (expr, 0));
! 	  if (!useless_type_conversion_p (TREE_TYPE (TREE_OPERAND (expr, 0)),
! 					  TREE_TYPE (val)))
! 	    val = fold_build1 (VIEW_CONVERT_EXPR,
! 			       TREE_TYPE (TREE_OPERAND (expr, 0)), val);
! 	  TREE_OPERAND (expr, 0) = val;
! 	}
        break;
      case tcc_binary:
        if (TREE_CODE (TREE_OPERAND (expr, 0)) == SSA_NAME
  	  && SSA_VAL (TREE_OPERAND (expr, 0)) != VN_TOP)
! 	{
! 	  tree val = SSA_VAL (TREE_OPERAND (expr, 0));
! 	  if (!useless_type_conversion_p (TREE_TYPE (TREE_OPERAND (expr, 0)),
! 					  TREE_TYPE (val)))
! 	    val = fold_build1 (VIEW_CONVERT_EXPR,
! 			       TREE_TYPE (TREE_OPERAND (expr, 0)), val);
! 	  TREE_OPERAND (expr, 0) = val;
! 	}
        if (TREE_CODE (TREE_OPERAND (expr, 1)) == SSA_NAME
  	  && SSA_VAL (TREE_OPERAND (expr, 1)) != VN_TOP)
! 	{
! 	  tree val = SSA_VAL (TREE_OPERAND (expr, 1));
! 	  if (!useless_type_conversion_p (TREE_TYPE (TREE_OPERAND (expr, 1)),
! 					  TREE_TYPE (val)))
! 	    val = fold_build1 (VIEW_CONVERT_EXPR,
! 			       TREE_TYPE (TREE_OPERAND (expr, 1)), val);
! 	  TREE_OPERAND (expr, 1) = val;
! 	}
        break;
      default:
        break;
      }
+   if (!useless_type_conversion_p (type, TREE_TYPE (expr)))
+     return fold_build1 (VIEW_CONVERT_EXPR, type, expr);
    return expr;
  }
  
*************** simplify_binary_expression (tree stmt, t
*** 1443,1449 ****
    if (TREE_CODE (op0) == SSA_NAME)
      {
        if (VN_INFO (op0)->has_constants)
! 	op0 = valueize_expr (VN_INFO (op0)->expr);
        else if (SSA_VAL (op0) != VN_TOP && SSA_VAL (op0) != op0)
  	op0 = SSA_VAL (op0);
      }
--- 1476,1482 ----
    if (TREE_CODE (op0) == SSA_NAME)
      {
        if (VN_INFO (op0)->has_constants)
! 	op0 = valueize_expr (TREE_TYPE (op0), VN_INFO (op0)->expr);
        else if (SSA_VAL (op0) != VN_TOP && SSA_VAL (op0) != op0)
  	op0 = SSA_VAL (op0);
      }
*************** simplify_binary_expression (tree stmt, t
*** 1451,1457 ****
    if (TREE_CODE (op1) == SSA_NAME)
      {
        if (VN_INFO (op1)->has_constants)
! 	op1 = valueize_expr (VN_INFO (op1)->expr);
        else if (SSA_VAL (op1) != VN_TOP && SSA_VAL (op1) != op1)
  	op1 = SSA_VAL (op1);
      }
--- 1484,1490 ----
    if (TREE_CODE (op1) == SSA_NAME)
      {
        if (VN_INFO (op1)->has_constants)
! 	op1 = valueize_expr (TREE_TYPE (op1), VN_INFO (op1)->expr);
        else if (SSA_VAL (op1) != VN_TOP && SSA_VAL (op1) != op1)
  	op1 = SSA_VAL (op1);
      }
*************** simplify_unary_expression (tree rhs)
*** 1491,1497 ****
      return NULL_TREE;
  
    if (VN_INFO (op0)->has_constants)
!     op0 = valueize_expr (VN_INFO (op0)->expr);
    else if (TREE_CODE (rhs) == NOP_EXPR
  	   || TREE_CODE (rhs) == CONVERT_EXPR
  	   || TREE_CODE (rhs) == REALPART_EXPR
--- 1524,1530 ----
      return NULL_TREE;
  
    if (VN_INFO (op0)->has_constants)
!     op0 = valueize_expr (TREE_TYPE (op0), VN_INFO (op0)->expr);
    else if (TREE_CODE (rhs) == NOP_EXPR
  	   || TREE_CODE (rhs) == CONVERT_EXPR
  	   || TREE_CODE (rhs) == REALPART_EXPR
*************** simplify_unary_expression (tree rhs)
*** 1499,1505 ****
      {
        /* We want to do tree-combining on conversion-like expressions.
           Make sure we feed only SSA_NAMEs or constants to fold though.  */
!       tree tem = valueize_expr (VN_INFO (op0)->expr);
        if (UNARY_CLASS_P (tem)
  	  || BINARY_CLASS_P (tem)
  	  || TREE_CODE (tem) == SSA_NAME
--- 1532,1538 ----
      {
        /* We want to do tree-combining on conversion-like expressions.
           Make sure we feed only SSA_NAMEs or constants to fold though.  */
!       tree tem = valueize_expr (TREE_TYPE (op0), VN_INFO (op0)->expr);
        if (UNARY_CLASS_P (tem)
  	  || BINARY_CLASS_P (tem)
  	  || TREE_CODE (tem) == SSA_NAME


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]