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  <rguent...@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" } } */

Reply via email to