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] Fix PR39120, another PTA wrong-code bug


This patch (for alias-improvements branch) fixes PR39120.  We fail to add
escape constraints for function return values stored in global memory.
As this can happen only with aggregate returns and functions that do not
cause their parameters to escape (thus, const or pure functions returning
aggregates with pointers pointing to what their arguments points to)
this isn't a critical fix for 4.4.0 (IMHO).  Instead I'll make sure to
merge all the PTA fixes before merging the branch to facilitate backports
for 4.4.1 later.

This patch re-organizes call handling in find_func_aliases to make
adding proper escape contraints easier.  It also adjusts static chain
handling of const functions to be like that of pure functions and
adjusts the oracle accordingly.

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

Richard.

2009-02-06  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.  Manually build
	nonlocal constraints for asm outputs.
	* tree-ssa-alias.c (ref_maybe_used_by_call_p_1): Add
	shortcut for const calls.  Properly use is_call_clobbered for
	non-const, non-pure calls.

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

Index: gcc/tree-ssa-structalias.c
===================================================================
*** gcc/tree-ssa-structalias.c.orig	2009-02-06 20:16:13.000000000 +0100
--- gcc/tree-ssa-structalias.c	2009-02-06 20:55:57.000000000 +0100
*************** make_escape_constraint (tree op)
*** 3528,3535 ****
     RHS.  */
  
  static void
! handle_rhs_call (gimple stmt)
  {
    unsigned i;
  
    for (i = 0; i < gimple_call_num_args (stmt); ++i)
--- 3528,3536 ----
     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)
*** 3545,3550 ****
--- 3546,3557 ----
    /* The static chain escapes as well.  */
    if (gimple_call_chain (stmt))
      make_escape_constraint (gimple_call_chain (stmt));
+ 
+   /* Regular functions return nonlocal memory.  */
+   rhsc.var = nonlocal_id;
+   rhsc.offset = 0;
+   rhsc.type = SCALAR;
+   VEC_safe_push (ce_s, heap, *results, &rhsc);
  }
  
  /* For non-IPA mode, generate constraints necessary for a call
*************** handle_rhs_call (gimple stmt)
*** 3552,3561 ****
     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;
  
--- 3559,3567 ----
     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)
*** 3563,3568 ****
--- 3569,3575 ----
  
    if (flags & ECF_MALLOC)
      {
+       struct constraint_expr rhsc;
        tree heapvar = heapvar_lookup (lhs);
        varinfo_t vi;
  
*************** handle_lhs_call (tree lhs, int flags)
*** 3586,3600 ****
        vi->size = ~0;
        rhsc.type = ADDRESSOF;
        rhsc.offset = 0;
      }
!   else
      {
!       rhsc.var = nonlocal_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);
  }
  
--- 3593,3622 ----
        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)
*** 3602,3644 ****
     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)
      {
--- 3624,3646 ----
     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)
*** 3650,3675 ****
  	  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)
--- 3652,3692 ----
  	  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)
*** 3677,3724 ****
        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
--- 3694,3724 ----
        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 nonlocal memory.  */
!   if (need_callused)
!     {
        rhsc.var = callused_id;
        rhsc.offset = 0;
        rhsc.type = SCALAR;
!       VEC_safe_push (ce_s, heap, *results, &rhsc);
      }
+   rhsc.var = nonlocal_id;
+   rhsc.offset = 0;
+   rhsc.type = SCALAR;
+   VEC_safe_push (ce_s, heap, *results, &rhsc);
  }
  
  /* Walk statement T setting up aliasing constraints according to the
*************** find_func_aliases (gimple origt)
*** 3783,3815 ****
      {
        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
  	{
--- 3783,3810 ----
      {
        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
  	{
*************** find_func_aliases (gimple origt)
*** 3993,4002 ****
  	  if (!allows_reg && allows_mem)
  	    make_escape_constraint (build_fold_addr_expr (op));
  
  	  if (op && could_have_pointers (op))
! 	    /* The asm may read global memory, so outputs may point to
! 	       any escaped memory.  */
! 	    handle_lhs_call (op, 0);
  	}
        for (i = 0; i < gimple_asm_ninputs (t); ++i)
  	{
--- 3988,4008 ----
  	  if (!allows_reg && allows_mem)
  	    make_escape_constraint (build_fold_addr_expr (op));
  
+ 	  /* The asm may read global memory, so outputs may point to
+ 	     any global memory.  */
  	  if (op && could_have_pointers (op))
! 	    {
! 	      VEC(ce_s, heap) *lhsc = NULL;
! 	      struct constraint_expr rhsc, *lhsp;
! 	      unsigned j;
! 	      get_constraint_for (op, &lhsc);
! 	      rhsc.var = nonlocal_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);
! 	    }
  	}
        for (i = 0; i < gimple_asm_ninputs (t); ++i)
  	{
Index: gcc/testsuite/gcc.c-torture/execute/pr39120.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- gcc/testsuite/gcc.c-torture/execute/pr39120.c	2009-02-06 20:17:38.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: gcc/tree-ssa-alias.c
===================================================================
*** gcc/tree-ssa-alias.c.orig	2009-02-06 20:17:45.000000000 +0100
--- gcc/tree-ssa-alias.c	2009-02-06 22:55:57.000000000 +0100
*************** refs_may_alias_p (tree ref1, tree ref2)
*** 625,636 ****
  static bool
  ref_maybe_used_by_call_p_1 (gimple call, tree ref)
  {
!   tree base = get_base_address (ref);
    unsigned i;
  
    /* If the reference is not based on a decl give up.
       ???  Handle indirect references by intersecting the call-used
       	  solution with that of the pointer.  */
    if (!base
        || !DECL_P (base))
      return true;
--- 625,643 ----
  static bool
  ref_maybe_used_by_call_p_1 (gimple call, tree ref)
  {
!   tree base;
    unsigned i;
+   int flags = gimple_call_flags (call);
+ 
+   /* Const functions without a static chain do not implicitly use memory.  */
+   if (!gimple_call_chain (call)
+       && (flags & (ECF_CONST|ECF_NOVOPS)))
+     goto process_args;
  
    /* If the reference is not based on a decl give up.
       ???  Handle indirect references by intersecting the call-used
       	  solution with that of the pointer.  */
+   base = get_base_address (ref);
    if (!base
        || !DECL_P (base))
      return true;
*************** ref_maybe_used_by_call_p_1 (gimple call,
*** 648,661 ****
  	  && (not_read
  	        = ipa_reference_get_not_read_global (cgraph_node (callee)))
  	  && bitmap_bit_p (not_read, DECL_UID (base)))
! 	return false;
      }
  
!   /* If the base variable is call-used then it may be used.  */
!   if (is_call_used (base))
!     return true;
  
    /* Inspect call arguments for passed-by-value aliases.  */
    for (i = 0; i < gimple_call_num_args (call); ++i)
      {
        tree op = gimple_call_arg (call, i);
--- 655,678 ----
  	  && (not_read
  	        = ipa_reference_get_not_read_global (cgraph_node (callee)))
  	  && bitmap_bit_p (not_read, DECL_UID (base)))
! 	goto process_args;
      }
  
!   /* If the base variable is call-used or call-clobbered then
!      it may be used.  */
!   if (flags & (ECF_PURE|ECF_CONST|ECF_LOOPING_CONST_OR_PURE|ECF_NOVOPS))
!     {
!       if (is_call_used (base))
! 	return true;
!     }
!   else
!     {
!       if (is_call_clobbered (base))
! 	return true;
!     }
  
    /* Inspect call arguments for passed-by-value aliases.  */
+ process_args:
    for (i = 0; i < gimple_call_num_args (call); ++i)
      {
        tree op = gimple_call_arg (call, i);


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