[PATCH] Fix PR39120, missing escape constraints for struct copies in calls

Richard Guenther rguenther@suse.de
Fri Mar 27 23:00:00 GMT 2009


This merges the fix for PR39120 from alias-improvements.  It is also
a candidate for 4.4.1, so to ease merging it back this is split out
from the alias-improvements branch merge.

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

Richard.

2009-03-24  Richard Guenther  <rguenther@suse.de>

	PR tree-optimization/39120
	* tree-ssa-structalias.c (do_sd_constraint): Do not use CALLUSED
	as a representative.
	(solve_graph): Do propagate CALLUSED.
	(handle_pure_call): Use a scalar constraint from CALLUSED for
	the return value.
	(find_what_p_points_to): CALLUSED shall not appear in poins-to
	solutions.

	* gcc.dg/torture/pta-callused-1.c: New testcase.

Index: gcc/tree-ssa-structalias.c
===================================================================
*** gcc/tree-ssa-structalias.c.orig	2009-03-24 15:18:22.000000000 +0100
--- gcc/tree-ssa-structalias.c	2009-03-24 15:33:32.000000000 +0100
*************** do_sd_constraint (constraint_graph_t gra
*** 1592,1603 ****
  	  if (get_varinfo (t)->is_special_var)
  	    flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
  	  /* Merging the solution from ESCAPED needlessly increases
! 	     the set.  Use ESCAPED as representative instead.
! 	     Same for CALLUSED.  */
  	  else if (get_varinfo (t)->id == find (escaped_id))
  	    flag |= bitmap_set_bit (sol, escaped_id);
- 	  else if (get_varinfo (t)->id == find (callused_id))
- 	    flag |= bitmap_set_bit (sol, callused_id);
  	  else if (add_graph_edge (graph, lhs, t))
  	    flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
  	}
--- 1592,1600 ----
  	  if (get_varinfo (t)->is_special_var)
  	    flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
  	  /* Merging the solution from ESCAPED needlessly increases
! 	     the set.  Use ESCAPED as representative instead.  */
  	  else if (get_varinfo (t)->id == find (escaped_id))
  	    flag |= bitmap_set_bit (sol, escaped_id);
  	  else if (add_graph_edge (graph, lhs, t))
  	    flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
  	}
*************** solve_graph (constraint_graph_t graph)
*** 2516,2524 ****
  	      solution_empty = bitmap_empty_p (solution);
  
  	      if (!solution_empty
! 		  /* Do not propagate the ESCAPED/CALLUSED solutions.  */
! 		  && i != find (escaped_id)
! 		  && i != find (callused_id))
  		{
  		  bitmap_iterator bi;
  
--- 2513,2520 ----
  	      solution_empty = bitmap_empty_p (solution);
  
  	      if (!solution_empty
! 		  /* Do not propagate the ESCAPED solutions.  */
! 		  && i != find (escaped_id))
  		{
  		  bitmap_iterator bi;
  
*************** handle_pure_call (gimple stmt)
*** 3674,3680 ****
           and globals will be dealt with in handle_lhs_call.  */
        rhsc.var = callused_id;
        rhsc.offset = 0;
!       rhsc.type = ADDRESSOF;
        for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
  	process_constraint (new_constraint (*lhsp, rhsc));
        VEC_free (ce_s, heap, lhsc);
--- 3670,3676 ----
           and globals will be dealt with in handle_lhs_call.  */
        rhsc.var = callused_id;
        rhsc.offset = 0;
!       rhsc.type = SCALAR;
        for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
  	process_constraint (new_constraint (*lhsp, rhsc));
        VEC_free (ce_s, heap, lhsc);
*************** find_what_p_points_to (tree p)
*** 4925,4933 ****
  		    pi->pt_null = 1;
  		  else if (vi->id == anything_id
  			   || vi->id == nonlocal_id
! 			   || vi->id == escaped_id
! 			   || vi->id == callused_id)
  		    was_pt_anything = 1;
  		  else if (vi->id == readonly_id)
  		    was_pt_anything = 1;
  		  else if (vi->id == integer_id)
--- 4921,4930 ----
  		    pi->pt_null = 1;
  		  else if (vi->id == anything_id
  			   || vi->id == nonlocal_id
! 			   || vi->id == escaped_id)
  		    was_pt_anything = 1;
+ 		  else if (vi->id == callused_id)
+ 		    gcc_unreachable ();
  		  else if (vi->id == readonly_id)
  		    was_pt_anything = 1;
  		  else if (vi->id == integer_id)
Index: gcc/testsuite/gcc.dg/torture/pta-callused-1.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- gcc/testsuite/gcc.dg/torture/pta-callused-1.c	2009-03-24 15:33:56.000000000 +0100
***************
*** 0 ****
--- 1,25 ----
+ /* { dg-do run } */
+ /* { dg-options "-fdump-tree-alias" } */
+ /* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */
+ 
+ volatile int i;
+ int ** __attribute__((noinline,pure)) foo(int **p) { i; return p; }
+ int bar(void)
+ {
+   int i = 0, j = 1;
+   int *p, **q;
+   p = &i;
+   q = foo(&p);
+   *q = &j;
+   return *p;
+ }
+ extern void abort (void);
+ int main()
+ {
+   if (bar() != 1)
+     abort ();
+   return 0;
+ }
+ 
+ /* { dg-final { scan-tree-dump "p.._., name memory tag: NMT..., is dereferenced, points-to vars: { i j }" "alias" } } */
+ /* { dg-final { cleanup-tree-dump "alias" } } */


2009-03-24  Richard Guenther  <rguenther@suse.de>

	PR tree-optimization/39120
	* tree-ssa-structalias.c (handle_rhs_call): Fill out return
	constraints.
	(handle_lhs_call): Process return constraints.  Add escape
	constraints if necessary.
	(handle_const_call): Fill out return constraints.  Make nested
	case more precise.  Avoid consttmp if possible.
	(handle_pure_call): Fill out return constraints.  Avoid
	callused if possible.
	(find_func_aliases): Simplify call handling.

	* gcc.c-torture/execute/pr39120.c: New testcase.

Index: trunk/gcc/testsuite/gcc.c-torture/execute/pr39120.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.c-torture/execute/pr39120.c	2009-03-24 15:19:16.000000000 +0100
***************
*** 0 ****
--- 1,18 ----
+ struct X { int *p; } x;
+ 
+ struct X __attribute__((noinline))
+ foo(int *p) { struct X x; x.p = p; return x; }
+ 
+ void __attribute((noinline))
+ bar() { *x.p = 1; }
+ 
+ extern void abort (void);
+ int main()
+ {
+   int i = 0;
+   x = foo(&i);
+   bar();
+   if (i != 1)
+     abort ();
+   return 0;
+ }
Index: trunk/gcc/tree-ssa-structalias.c
===================================================================
*** trunk.orig/gcc/tree-ssa-structalias.c	2009-03-24 15:18:38.000000000 +0100
--- trunk/gcc/tree-ssa-structalias.c	2009-03-24 15:30:22.000000000 +0100
*************** make_escape_constraint (tree op)
*** 3484,3491 ****
     RHS.  */
  
  static void
! handle_rhs_call (gimple stmt)
  {
    unsigned i;
  
    for (i = 0; i < gimple_call_num_args (stmt); ++i)
--- 3484,3492 ----
     RHS.  */
  
  static void
! handle_rhs_call (gimple stmt, VEC(ce_s, heap) **results)
  {
+   struct constraint_expr rhsc;
    unsigned i;
  
    for (i = 0; i < gimple_call_num_args (stmt); ++i)
*************** handle_rhs_call (gimple stmt)
*** 3501,3506 ****
--- 3502,3513 ----
    /* The static chain escapes as well.  */
    if (gimple_call_chain (stmt))
      make_escape_constraint (gimple_call_chain (stmt));
+ 
+   /* Regular functions return escaped addresses.  */
+   rhsc.var = escaped_id;
+   rhsc.offset = 0;
+   rhsc.type = ADDRESSOF;
+   VEC_safe_push (ce_s, heap, *results, &rhsc);
  }
  
  /* For non-IPA mode, generate constraints necessary for a call
*************** handle_rhs_call (gimple stmt)
*** 3508,3517 ****
     the LHS point to global and escaped variables.  */
  
  static void
! handle_lhs_call (tree lhs, int flags)
  {
    VEC(ce_s, heap) *lhsc = NULL;
-   struct constraint_expr rhsc;
    unsigned int j;
    struct constraint_expr *lhsp;
  
--- 3515,3523 ----
     the LHS point to global and escaped variables.  */
  
  static void
! handle_lhs_call (tree lhs, int flags, VEC(ce_s, heap) *rhsc)
  {
    VEC(ce_s, heap) *lhsc = NULL;
    unsigned int j;
    struct constraint_expr *lhsp;
  
*************** handle_lhs_call (tree lhs, int flags)
*** 3519,3524 ****
--- 3525,3531 ----
  
    if (flags & ECF_MALLOC)
      {
+       struct constraint_expr rhsc;
        tree heapvar = heapvar_lookup (lhs);
        varinfo_t vi;
  
*************** handle_lhs_call (tree lhs, int flags)
*** 3542,3556 ****
        vi->size = ~0;
        rhsc.type = ADDRESSOF;
        rhsc.offset = 0;
      }
!   else
      {
!       rhsc.var = escaped_id;
!       rhsc.offset = 0;
!       rhsc.type = ADDRESSOF;
      }
-   for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
-     process_constraint (new_constraint (*lhsp, rhsc));
    VEC_free (ce_s, heap, lhsc);
  }
  
--- 3549,3578 ----
        vi->size = ~0;
        rhsc.type = ADDRESSOF;
        rhsc.offset = 0;
+       for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ 	process_constraint (new_constraint (*lhsp, rhsc));
      }
!   else if (VEC_length (ce_s, rhsc) > 0)
      {
!       struct constraint_expr *lhsp, *rhsp;
!       unsigned int i, j;
!       /* If the store is to a global decl make sure to
! 	 add proper escape constraints.  */
!       lhs = get_base_address (lhs);
!       if (lhs
! 	  && DECL_P (lhs)
! 	  && is_global_var (lhs))
! 	{
! 	  struct constraint_expr tmpc;
! 	  tmpc.var = escaped_id;
! 	  tmpc.offset = 0;
! 	  tmpc.type = SCALAR;
! 	  VEC_safe_push (ce_s, heap, lhsc, &tmpc);
! 	}
!       for (i = 0; VEC_iterate (ce_s, lhsc, i, lhsp); ++i)
! 	for (j = 0; VEC_iterate (ce_s, rhsc, j, rhsp); ++j)
! 	  process_constraint (new_constraint (*lhsp, *rhsp));
      }
    VEC_free (ce_s, heap, lhsc);
  }
  
*************** handle_lhs_call (tree lhs, int flags)
*** 3558,3600 ****
     const function that returns a pointer in the statement STMT.  */
  
  static void
! handle_const_call (gimple stmt)
  {
!   tree lhs = gimple_call_lhs (stmt);
!   VEC(ce_s, heap) *lhsc = NULL;
!   struct constraint_expr rhsc;
!   unsigned int j, k;
!   struct constraint_expr *lhsp;
!   tree tmpvar;
!   struct constraint_expr tmpc;
  
!   get_constraint_for (lhs, &lhsc);
! 
!   /* If this is a nested function then it can return anything.  */
    if (gimple_call_chain (stmt))
      {
!       rhsc.var = anything_id;
        rhsc.offset = 0;
!       rhsc.type = ADDRESSOF;
!       for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
! 	process_constraint (new_constraint (*lhsp, rhsc));
!       VEC_free (ce_s, heap, lhsc);
!       return;
      }
  
-   /* We always use a temporary here, otherwise we end up with a quadratic
-      amount of constraints for
-        large_struct = const_call (large_struct);
-      in field-sensitive PTA.  */
-   tmpvar = create_tmp_var_raw (ptr_type_node, "consttmp");
-   tmpc = get_constraint_exp_for_temp (tmpvar);
- 
-   /* May return addresses of globals.  */
-   rhsc.var = nonlocal_id;
-   rhsc.offset = 0;
-   rhsc.type = ADDRESSOF;
-   process_constraint (new_constraint (tmpc, rhsc));
- 
    /* May return arguments.  */
    for (k = 0; k < gimple_call_num_args (stmt); ++k)
      {
--- 3580,3602 ----
     const function that returns a pointer in the statement STMT.  */
  
  static void
! handle_const_call (gimple stmt, VEC(ce_s, heap) **results)
  {
!   struct constraint_expr rhsc, tmpc;
!   tree tmpvar = NULL_TREE;
!   unsigned int k;
  
!   /* Treat nested const functions the same as pure functions as far
!      as the static chain is concerned.  */
    if (gimple_call_chain (stmt))
      {
!       make_constraint_to (callused_id, gimple_call_chain (stmt));
!       rhsc.var = callused_id;
        rhsc.offset = 0;
!       rhsc.type = SCALAR;
!       VEC_safe_push (ce_s, heap, *results, &rhsc);
      }
  
    /* May return arguments.  */
    for (k = 0; k < gimple_call_num_args (stmt); ++k)
      {
*************** handle_const_call (gimple stmt)
*** 3606,3631 ****
  	  struct constraint_expr *argp;
  	  int i;
  
  	  get_constraint_for (arg, &argc);
  	  for (i = 0; VEC_iterate (ce_s, argc, i, argp); i++)
  	    process_constraint (new_constraint (tmpc, *argp));
  	  VEC_free (ce_s, heap, argc);
  	}
      }
  
!   for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
!     process_constraint (new_constraint (*lhsp, tmpc));
! 
!   VEC_free (ce_s, heap, lhsc);
  }
  
  /* For non-IPA mode, generate constraints necessary for a call to a
     pure function in statement STMT.  */
  
  static void
! handle_pure_call (gimple stmt)
  {
    unsigned i;
  
    /* Memory reached from pointer arguments is call-used.  */
    for (i = 0; i < gimple_call_num_args (stmt); ++i)
--- 3608,3648 ----
  	  struct constraint_expr *argp;
  	  int i;
  
+ 	  /* We always use a temporary here, otherwise we end up with
+ 	     a quadratic amount of constraints for
+ 	       large_struct = const_call (large_struct);
+ 	     with field-sensitive PTA.  */
+ 	  if (tmpvar == NULL_TREE)
+ 	    {
+ 	      tmpvar = create_tmp_var_raw (ptr_type_node, "consttmp");
+ 	      tmpc = get_constraint_exp_for_temp (tmpvar);
+ 	    }
+ 
  	  get_constraint_for (arg, &argc);
  	  for (i = 0; VEC_iterate (ce_s, argc, i, argp); i++)
  	    process_constraint (new_constraint (tmpc, *argp));
  	  VEC_free (ce_s, heap, argc);
  	}
      }
+   if (tmpvar != NULL_TREE)
+     VEC_safe_push (ce_s, heap, *results, &tmpc);
  
!   /* May return addresses of globals.  */
!   rhsc.var = nonlocal_id;
!   rhsc.offset = 0;
!   rhsc.type = ADDRESSOF;
!   VEC_safe_push (ce_s, heap, *results, &rhsc);
  }
  
  /* For non-IPA mode, generate constraints necessary for a call to a
     pure function in statement STMT.  */
  
  static void
! handle_pure_call (gimple stmt, VEC(ce_s, heap) **results)
  {
+   struct constraint_expr rhsc;
    unsigned i;
+   bool need_callused = false;
  
    /* Memory reached from pointer arguments is call-used.  */
    for (i = 0; i < gimple_call_num_args (stmt); ++i)
*************** handle_pure_call (gimple stmt)
*** 3633,3680 ****
        tree arg = gimple_call_arg (stmt, i);
  
        if (could_have_pointers (arg))
! 	make_constraint_to (callused_id, arg);
      }
  
    /* The static chain is used as well.  */
    if (gimple_call_chain (stmt))
-     make_constraint_to (callused_id, gimple_call_chain (stmt));
- 
-   /* If the call returns a pointer it may point to reachable memory
-      from the arguments.  Not so for malloc functions though.  */
-   if (gimple_call_lhs (stmt)
-       && could_have_pointers (gimple_call_lhs (stmt))
-       && !(gimple_call_flags (stmt) & ECF_MALLOC))
      {
!       tree lhs = gimple_call_lhs (stmt);
!       VEC(ce_s, heap) *lhsc = NULL;
!       struct constraint_expr rhsc;
!       struct constraint_expr *lhsp;
!       unsigned j;
! 
!       get_constraint_for (lhs, &lhsc);
! 
!       /* If this is a nested function then it can return anything.  */
!       if (gimple_call_chain (stmt))
! 	{
! 	  rhsc.var = anything_id;
! 	  rhsc.offset = 0;
! 	  rhsc.type = ADDRESSOF;
! 	  for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
! 	    process_constraint (new_constraint (*lhsp, rhsc));
! 	  VEC_free (ce_s, heap, lhsc);
! 	  return;
! 	}
  
!       /* Else just add the call-used memory here.  Escaped variables
!          and globals will be dealt with in handle_lhs_call.  */
        rhsc.var = callused_id;
        rhsc.offset = 0;
        rhsc.type = SCALAR;
!       for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
! 	process_constraint (new_constraint (*lhsp, rhsc));
!       VEC_free (ce_s, heap, lhsc);
      }
  }
  
  /* Walk statement T setting up aliasing constraints according to the
--- 3650,3680 ----
        tree arg = gimple_call_arg (stmt, i);
  
        if (could_have_pointers (arg))
! 	{
! 	  make_constraint_to (callused_id, arg);
! 	  need_callused = true;
! 	}
      }
  
    /* The static chain is used as well.  */
    if (gimple_call_chain (stmt))
      {
!       make_constraint_to (callused_id, gimple_call_chain (stmt));
!       need_callused = true;
!     }
  
!   /* Pure functions may return callused and escaped memory.  */
!   if (need_callused)
!     {
        rhsc.var = callused_id;
        rhsc.offset = 0;
        rhsc.type = SCALAR;
!       VEC_safe_push (ce_s, heap, *results, &rhsc);
      }
+   rhsc.var = escaped_id;
+   rhsc.offset = 0;
+   rhsc.type = ADDRESSOF;
+   VEC_safe_push (ce_s, heap, *results, &rhsc);
  }
  
  /* Walk statement T setting up aliasing constraints according to the
*************** find_func_aliases (gimple origt)
*** 3739,3771 ****
      {
        if (!in_ipa_mode)
  	{
  	  int flags = gimple_call_flags (t);
  
  	  /* Const functions can return their arguments and addresses
  	     of global memory but not of escaped memory.  */
! 	  if (flags & ECF_CONST)
  	    {
  	      if (gimple_call_lhs (t)
  		  && could_have_pointers (gimple_call_lhs (t)))
! 		handle_const_call (t);
  	    }
  	  /* Pure functions can return addresses in and of memory
  	     reachable from their arguments, but they are not an escape
  	     point for reachable memory of their arguments.  */
! 	  else if (flags & ECF_PURE)
! 	    {
! 	      handle_pure_call (t);
! 	      if (gimple_call_lhs (t)
! 		  && could_have_pointers (gimple_call_lhs (t)))
! 		handle_lhs_call (gimple_call_lhs (t), flags);
! 	    }
  	  else
! 	    {
! 	      handle_rhs_call (t);
! 	      if (gimple_call_lhs (t)
! 		  && could_have_pointers (gimple_call_lhs (t)))
! 		handle_lhs_call (gimple_call_lhs (t), flags);
! 	    }
  	}
        else
  	{
--- 3739,3766 ----
      {
        if (!in_ipa_mode)
  	{
+ 	  VEC(ce_s, heap) *rhsc = NULL;
  	  int flags = gimple_call_flags (t);
  
  	  /* Const functions can return their arguments and addresses
  	     of global memory but not of escaped memory.  */
! 	  if (flags & (ECF_CONST|ECF_NOVOPS))
  	    {
  	      if (gimple_call_lhs (t)
  		  && could_have_pointers (gimple_call_lhs (t)))
! 		handle_const_call (t, &rhsc);
  	    }
  	  /* Pure functions can return addresses in and of memory
  	     reachable from their arguments, but they are not an escape
  	     point for reachable memory of their arguments.  */
! 	  else if (flags & (ECF_PURE|ECF_LOOPING_CONST_OR_PURE))
! 	    handle_pure_call (t, &rhsc);
  	  else
! 	    handle_rhs_call (t, &rhsc);
! 	  if (gimple_call_lhs (t)
! 	      && could_have_pointers (gimple_call_lhs (t)))
! 	    handle_lhs_call (gimple_call_lhs (t), flags, rhsc);
! 	  VEC_free (ce_s, heap, rhsc);
  	}
        else
  	{



More information about the Gcc-patches mailing list