[PATCH] Fix PR13954

Richard Guenther rguenther@suse.de
Tue Mar 15 13:34:00 GMT 2011


This fixes PR13954, the failure to look through memcpy functions
during value-numbering.

Bootstrapped and tested on x86_64-unknown-linux-gnu, applied to trunk.

Richard.

2011-03-09  Richard Guenther  <rguenther@suse.de>

	PR tree-optimization/13954
	* tree-ssa-sccvn.c (vn_reference_lookup_3): Look through memcpy
	and friends.

	* g++.dg/tree-ssa/pr13954.C: New testcase.

Index: gcc/tree-ssa-sccvn.c
===================================================================
*** gcc/tree-ssa-sccvn.c.orig	2011-03-08 16:50:33.000000000 +0100
--- gcc/tree-ssa-sccvn.c	2011-03-09 15:45:44.000000000 +0100
*************** vn_reference_lookup_3 (ao_ref *ref, tree
*** 1288,1294 ****
  {
    vn_reference_t vr = (vn_reference_t)vr_;
    gimple def_stmt = SSA_NAME_DEF_STMT (vuse);
-   tree fndecl;
    tree base;
    HOST_WIDE_INT offset, maxsize;
    static VEC (vn_reference_op_s, heap) *lhs_ops = NULL;
--- 1288,1293 ----
*************** vn_reference_lookup_3 (ao_ref *ref, tree
*** 1326,1335 ****
       from that defintion.
       1) Memset.  */
    if (is_gimple_reg_type (vr->type)
!       && is_gimple_call (def_stmt)
!       && (fndecl = gimple_call_fndecl (def_stmt))
!       && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
!       && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMSET
        && integer_zerop (gimple_call_arg (def_stmt, 1))
        && host_integerp (gimple_call_arg (def_stmt, 2), 1)
        && TREE_CODE (gimple_call_arg (def_stmt, 0)) == ADDR_EXPR)
--- 1325,1331 ----
       from that defintion.
       1) Memset.  */
    if (is_gimple_reg_type (vr->type)
!       && gimple_call_builtin_p (def_stmt, BUILT_IN_MEMSET)
        && integer_zerop (gimple_call_arg (def_stmt, 1))
        && host_integerp (gimple_call_arg (def_stmt, 2), 1)
        && TREE_CODE (gimple_call_arg (def_stmt, 0)) == ADDR_EXPR)
*************** vn_reference_lookup_3 (ao_ref *ref, tree
*** 1379,1385 ****
  	}
      }
  
!   /* For aggregate copies translate the reference through them if
       the copy kills ref.  */
    else if (vn_walk_kind == VN_WALKREWRITE
  	   && gimple_assign_single_p (def_stmt)
--- 1375,1381 ----
  	}
      }
  
!   /* 3) For aggregate copies translate the reference through them if
       the copy kills ref.  */
    else if (vn_walk_kind == VN_WALKREWRITE
  	   && gimple_assign_single_p (def_stmt)
*************** vn_reference_lookup_3 (ao_ref *ref, tree
*** 1450,1455 ****
--- 1446,1592 ----
        vr->hashcode = vn_reference_compute_hash (vr);
  
        /* Adjust *ref from the new operands.  */
+       if (!ao_ref_init_from_vn_reference (&r, vr->set, vr->type, vr->operands))
+ 	return (void *)-1;
+       /* This can happen with bitfields.  */
+       if (ref->size != r.size)
+ 	return (void *)-1;
+       *ref = r;
+ 
+       /* Do not update last seen VUSE after translating.  */
+       last_vuse_ptr = NULL;
+ 
+       /* Keep looking for the adjusted *REF / VR pair.  */
+       return NULL;
+     }
+ 
+   /* 4) For memcpy copies translate the reference through them if
+      the copy kills ref.  */
+   else if (vn_walk_kind == VN_WALKREWRITE
+ 	   && is_gimple_reg_type (vr->type)
+ 	   /* ???  Handle BCOPY as well.  */
+ 	   && (gimple_call_builtin_p (def_stmt, BUILT_IN_MEMCPY)
+ 	       || gimple_call_builtin_p (def_stmt, BUILT_IN_MEMPCPY)
+ 	       || gimple_call_builtin_p (def_stmt, BUILT_IN_MEMMOVE))
+ 	   && (TREE_CODE (gimple_call_arg (def_stmt, 0)) == ADDR_EXPR
+ 	       || TREE_CODE (gimple_call_arg (def_stmt, 0)) == SSA_NAME)
+ 	   && (TREE_CODE (gimple_call_arg (def_stmt, 1)) == ADDR_EXPR
+ 	       || TREE_CODE (gimple_call_arg (def_stmt, 1)) == SSA_NAME)
+ 	   && host_integerp (gimple_call_arg (def_stmt, 2), 1))
+     {
+       tree lhs, rhs;
+       ao_ref r;
+       HOST_WIDE_INT rhs_offset, copy_size, lhs_offset;
+       vn_reference_op_s op;
+       HOST_WIDE_INT at;
+ 
+ 
+       /* Only handle non-variable, addressable refs.  */
+       if (ref->size != maxsize
+ 	  || offset % BITS_PER_UNIT != 0
+ 	  || ref->size % BITS_PER_UNIT != 0)
+ 	return (void *)-1;
+ 
+       /* Extract a pointer base and an offset for the destination.  */
+       lhs = gimple_call_arg (def_stmt, 0);
+       lhs_offset = 0;
+       if (TREE_CODE (lhs) == SSA_NAME)
+ 	lhs = SSA_VAL (lhs);
+       if (TREE_CODE (lhs) == ADDR_EXPR)
+ 	{
+ 	  tree tem = get_addr_base_and_unit_offset (TREE_OPERAND (lhs, 0),
+ 						    &lhs_offset);
+ 	  if (!tem)
+ 	    return (void *)-1;
+ 	  if (TREE_CODE (tem) == MEM_REF
+ 	      && host_integerp (TREE_OPERAND (tem, 1), 1))
+ 	    {
+ 	      lhs = TREE_OPERAND (tem, 0);
+ 	      lhs_offset += TREE_INT_CST_LOW (TREE_OPERAND (tem, 1));
+ 	    }
+ 	  else if (DECL_P (tem))
+ 	    lhs = build_fold_addr_expr (tem);
+ 	  else
+ 	    return (void *)-1;
+ 	}
+       if (TREE_CODE (lhs) != SSA_NAME
+ 	  && TREE_CODE (lhs) != ADDR_EXPR)
+ 	return (void *)-1;
+ 
+       /* Extract a pointer base and an offset for the source.  */
+       rhs = gimple_call_arg (def_stmt, 1);
+       rhs_offset = 0;
+       if (TREE_CODE (rhs) == SSA_NAME)
+ 	rhs = SSA_VAL (rhs);
+       if (TREE_CODE (rhs) == ADDR_EXPR)
+ 	{
+ 	  tree tem = get_addr_base_and_unit_offset (TREE_OPERAND (rhs, 0),
+ 						    &rhs_offset);
+ 	  if (!tem)
+ 	    return (void *)-1;
+ 	  if (TREE_CODE (tem) == MEM_REF
+ 	      && host_integerp (TREE_OPERAND (tem, 1), 1))
+ 	    {
+ 	      rhs = TREE_OPERAND (tem, 0);
+ 	      rhs_offset += TREE_INT_CST_LOW (TREE_OPERAND (tem, 1));
+ 	    }
+ 	  else if (DECL_P (tem))
+ 	    rhs = build_fold_addr_expr (tem);
+ 	  else
+ 	    return (void *)-1;
+ 	}
+       if (TREE_CODE (rhs) != SSA_NAME
+ 	  && TREE_CODE (rhs) != ADDR_EXPR)
+ 	return (void *)-1;
+ 
+       copy_size = TREE_INT_CST_LOW (gimple_call_arg (def_stmt, 2));
+ 
+       /* The bases of the destination and the references have to agree.  */
+       if ((TREE_CODE (base) != MEM_REF
+ 	   && !DECL_P (base))
+ 	  || (TREE_CODE (base) == MEM_REF
+ 	      && (TREE_OPERAND (base, 0) != lhs
+ 		  || !host_integerp (TREE_OPERAND (base, 1), 1)))
+ 	  || (DECL_P (base)
+ 	      && (TREE_CODE (lhs) != ADDR_EXPR
+ 		  || TREE_OPERAND (lhs, 0) != base)))
+ 	return (void *)-1;
+ 
+       /* And the access has to be contained within the memcpy destination.  */
+       at = offset / BITS_PER_UNIT;
+       if (TREE_CODE (base) == MEM_REF)
+ 	at += TREE_INT_CST_LOW (TREE_OPERAND (base, 1));
+       if (lhs_offset > at
+ 	  || lhs_offset + copy_size < at + maxsize / BITS_PER_UNIT)
+ 	return (void *)-1;
+ 
+       /* Make room for 2 operands in the new reference.  */
+       if (VEC_length (vn_reference_op_s, vr->operands) < 2)
+ 	{
+ 	  VEC (vn_reference_op_s, heap) *old = vr->operands;
+ 	  VEC_safe_grow (vn_reference_op_s, heap, vr->operands, 2);
+ 	  if (old == shared_lookup_references
+ 	      && vr->operands != old)
+ 	    shared_lookup_references = NULL;
+ 	}
+       else
+ 	VEC_truncate (vn_reference_op_s, vr->operands, 2);
+ 
+       /* The looked-through reference is a simple MEM_REF.  */
+       memset (&op, 0, sizeof (op));
+       op.type = vr->type;
+       op.opcode = MEM_REF;
+       op.op0 = build_int_cst (ptr_type_node, at - rhs_offset);
+       op.off = at - lhs_offset + rhs_offset;
+       VEC_replace (vn_reference_op_s, vr->operands, 0, &op);
+       op.type = TYPE_MAIN_VARIANT (TREE_TYPE (rhs));
+       op.opcode = TREE_CODE (rhs);
+       op.op0 = rhs;
+       op.off = -1;
+       VEC_replace (vn_reference_op_s, vr->operands, 1, &op);
+       vr->hashcode = vn_reference_compute_hash (vr);
+ 
+       /* Adjust *ref from the new operands.  */
        if (!ao_ref_init_from_vn_reference (&r, vr->set, vr->type, vr->operands))
  	return (void *)-1;
        /* This can happen with bitfields.  */
Index: gcc/testsuite/g++.dg/tree-ssa/pr13954.C
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- gcc/testsuite/g++.dg/tree-ssa/pr13954.C	2011-03-09 15:44:37.000000000 +0100
***************
*** 0 ****
--- 1,29 ----
+ /* { dg-do compile } */ 
+ /* { dg-options "-O1 -fdump-tree-optimized" } */
+ 
+ void link_error (void);
+ 
+ class base
+ {
+ };
+ 
+ class teststruct: public base
+ {
+ public:
+   double d;
+   char f1;
+ };
+ 
+ void
+ copystruct1 (teststruct param)
+ {
+   teststruct local;
+   param.f1 = 0;
+   local = param;
+   if (local.f1 != 0)
+     link_error ();
+ }
+ 
+ /* There should be no reference to link_error. */
+ /* { dg-final { scan-tree-dump-times "link_error" 0 "optimized"} } */
+ /* { dg-final { cleanup-tree-dump "optimized" } } */



More information about the Gcc-patches mailing list