This sketches a simple local mod-ref analysis, piggy-backed ontop
of the local IPA pure-const machinery (well, just sharing its
pass really).  I am not yet sure how or if it will be possible
to IPA propagate this (other than handling already processed
bodies during local discovery) - we would need to know whether
parameters may reach calls, and in which position - something
that looks more close to IPA CP than IPA pure-const.

Not yet bootstrapped or tested other than on the simple testcase.

Any comments?

Thanks,
Richard.

2011-10-24  Richard Guenther  <rguent...@suse.de>

        * gimple.c (gimple_call_fnspec): Also look in DECL_ATTRIBUTES.
        * ipa-pure-const.c (struct funct_state_d): Add fnspec member.
        (varying_state): Adjust.
        (analyze_function): Populate fnspec.
        (local_pure_const): Set the fnspec attribute.

Index: gcc/gimple.c
===================================================================
*** gcc/gimple.c.orig   2011-10-24 15:14:30.000000000 +0200
--- gcc/gimple.c        2011-10-24 15:14:33.000000000 +0200
*************** gimple_call_flags (const_gimple stmt)
*** 1915,1931 ****
  static tree
  gimple_call_fnspec (const_gimple stmt)
  {
!   tree type, attr;
  
    type = gimple_call_fntype (stmt);
!   if (!type)
!     return NULL_TREE;
  
!   attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
!   if (!attr)
!     return NULL_TREE;
  
!   return TREE_VALUE (TREE_VALUE (attr));
  }
  
  /* Detects argument flags for argument number ARG on call STMT.  */
--- 1915,1939 ----
  static tree
  gimple_call_fnspec (const_gimple stmt)
  {
!   tree type, decl, attr;
  
    type = gimple_call_fntype (stmt);
!   if (type)
!     {
!       attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
!       if (attr)
!       return TREE_VALUE (TREE_VALUE (attr));
!     }
  
!   decl = gimple_call_fndecl (stmt);
!   if (decl)
!     {
!       attr = lookup_attribute ("fn spec", DECL_ATTRIBUTES (decl));
!       if (attr)
!       return TREE_VALUE (TREE_VALUE (attr));
!     }
  
!   return NULL_TREE;
  }
  
  /* Detects argument flags for argument number ARG on call STMT.  */
*************** is_gimple_constant (const_tree t)
*** 2731,2743 ****
      case VECTOR_CST:
        return true;
  
-     /* Vector constant constructors are gimple invariant.  */
-     case CONSTRUCTOR:
-       if (TREE_TYPE (t) && TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
-       return TREE_CONSTANT (t);
-       else
-       return false;
- 
      default:
        return false;
      }
--- 2739,2744 ----
Index: gcc/ipa-pure-const.c
===================================================================
*** gcc/ipa-pure-const.c.orig   2011-10-24 15:14:30.000000000 +0200
--- gcc/ipa-pure-const.c        2011-10-24 15:47:34.000000000 +0200
*************** struct funct_state_d
*** 94,104 ****
    bool looping;
  
    bool can_throw;
  };
  
  /* State used when we know nothing about function.  */
  static struct funct_state_d varying_state
!    = { IPA_NEITHER, IPA_NEITHER, true, true, true };
  
  
  typedef struct funct_state_d * funct_state;
--- 94,106 ----
    bool looping;
  
    bool can_throw;
+ 
+   char fnspec[1 + 4 + 1];
  };
  
  /* State used when we know nothing about function.  */
  static struct funct_state_d varying_state
!    = { IPA_NEITHER, IPA_NEITHER, true, true, true, "....." };
  
  
  typedef struct funct_state_d * funct_state;
*************** end:
*** 819,824 ****
--- 821,941 ----
    if (TREE_NOTHROW (decl))
      l->can_throw = false;
  
+   memset (l->fnspec, '.', sizeof (l->fnspec));
+   l->fnspec[5] = '\0';
+ 
+   /* Check properties of the return value.
+      ???  Ignore EH edges.  */
+   if (single_pred_p (EXIT_BLOCK_PTR)
+       && !gsi_end_p (gsi_last_bb (single_pred (EXIT_BLOCK_PTR))))
+     {
+       gimple ret = gsi_stmt (gsi_last_bb (single_pred (EXIT_BLOCK_PTR)));
+       if (gimple_code (ret) == GIMPLE_RETURN)
+       {
+         tree retval = gimple_return_retval (ret);
+         if (retval
+             && TREE_CODE (retval) == SSA_NAME
+             && SSA_NAME_IS_DEFAULT_DEF (retval)
+             && TREE_CODE (SSA_NAME_VAR (retval)) == PARM_DECL)
+           {
+             tree arg;
+             unsigned n;
+             for (arg = DECL_ARGUMENTS (decl), n = 1;
+                  arg && n <= 4 && arg != SSA_NAME_VAR (retval);
+                  arg = DECL_CHAIN (arg))
+               ++n;
+             /* Returns a parameter.  */
+             if (arg == SSA_NAME_VAR (retval) && n <= 4)
+               l->fnspec[0] = '0' + n;
+           }
+         else if (retval
+                  && TREE_CODE (retval) == SSA_NAME)
+           {
+             gimple def_stmt = SSA_NAME_DEF_STMT (retval);
+             if (is_gimple_call (def_stmt)
+                 && (gimple_call_return_flags (def_stmt) & ERF_NOALIAS))
+               l->fnspec[0] = 'm';
+             /* ???  Support ret = PHI <0, malloc ()>, thus returning zero.  */
+           }
+       }
+     }
+ 
+   /* Check properties of arguments.  */
+     {
+       tree arg;
+       unsigned n;
+       for (arg = DECL_ARGUMENTS (decl), n = 1; arg && n <= 4;
+          arg = DECL_CHAIN (arg))
+       {
+         tree name = gimple_default_def (cfun, arg);
+         if (!name)
+           ;
+         else if (has_zero_uses (name))
+           l->fnspec[n] = 'x';
+         else if (POINTER_TYPE_P (TREE_TYPE (name)))
+           {
+             gimple use_stmt;
+             imm_use_iterator iter;
+             bool escapes = false;
+             bool seen_store = false;
+ 
+             FOR_EACH_IMM_USE_STMT (use_stmt, iter, name)
+               {
+                 if (gimple_code (use_stmt) == GIMPLE_RETURN)
+                   continue;
+                 else if (gimple_assign_single_p (use_stmt))
+                   {
+                     /* Store.  */
+                     if (TREE_CODE (gimple_assign_lhs (use_stmt)) != SSA_NAME)
+                       seen_store = true;
+                     /* Not name or an ADDR_EXPR involving name
+                        is transfered to the lhs.  */
+                     if ((TREE_CODE (gimple_assign_rhs1 (use_stmt))
+                          != ADDR_EXPR)
+                         && gimple_assign_rhs1 (use_stmt) != name)
+                       continue;
+                     /* Everything else makes name escape.  */
+                   }
+                 else if (is_gimple_call (use_stmt))
+                   {
+                     tree lhs = gimple_call_lhs (use_stmt);
+                     unsigned m;
+                     for (m = 0; m < gimple_call_num_args (use_stmt); ++m)
+                       {
+                         tree carg = gimple_call_arg (use_stmt, m);
+                         if (TREE_CODE (carg) == SSA_NAME)
+                           {
+                             if (gimple_call_arg (use_stmt, m) == name)
+                               {
+                                 if (!(gimple_call_arg_flags (use_stmt, m)
+                                       & EAF_NOCLOBBER))
+                                   seen_store = true;
+                                 if ((gimple_call_arg_flags (use_stmt, m)
+                                      & EAF_NOESCAPE))
+                                   continue;
+                                 goto escape;
+                               }
+                           }
+                         else
+                           goto escape;
+                       }
+                     if (TREE_CODE (lhs) != SSA_NAME)
+                       seen_store = true;
+                     continue;
+                   }
+                 /* Everything else makes name escape.  */
+ 
+ escape:
+                 escapes = true;
+                 BREAK_FROM_IMM_USE_STMT (iter);
+               }
+             if (!escapes)
+               l->fnspec[n] = seen_store ? 'w' : 'r';
+           }
+         ++n;
+       }
+     }
+ 
    pop_cfun ();
    current_function_decl = old_decl;
    if (dump_file)
*************** end:
*** 831,836 ****
--- 948,954 ----
          fprintf (dump_file, "Function is locally const.\n");
        if (l->pure_const_state == IPA_PURE)
          fprintf (dump_file, "Function is locally pure.\n");
+       fprintf (dump_file, "Function spec is \"%s\".\n", l->fnspec);
      }
    return l;
  }
*************** local_pure_const (void)
*** 1653,1658 ****
--- 1771,1797 ----
                 lang_hooks.decl_printable_name (current_function_decl,
                                                 2));
      }
+     {
+       unsigned n;
+       for (n = 0; n < sizeof (l->fnspec) - 1; ++n)
+       if (l->fnspec[n] != '.')
+         break;
+       if (n < sizeof (l->fnspec) - 1)
+       {
+         DECL_ATTRIBUTES (current_function_decl)
+           = tree_cons (get_identifier ("fn spec"),
+                        build_tree_list (NULL_TREE,
+                                         build_string (1 + 4,
+                                                       l->fnspec)),
+                        remove_attribute ("fn spec",
+                                          DECL_ATTRIBUTES
+                                            (current_function_decl)));
+         if (dump_file)
+           fprintf (dump_file, "Function specification found: %s (%s)\n",
+                    lang_hooks.decl_printable_name (current_function_decl, 2),
+                    l->fnspec);
+       }
+     }
    free (l);
    if (changed)
      return execute_fixup_cfg ();
Index: gcc/testsuite/gcc.dg/tree-ssa/mod-ref-1.c
===================================================================
*** /dev/null   1970-01-01 00:00:00.000000000 +0000
--- gcc/testsuite/gcc.dg/tree-ssa/mod-ref-1.c   2011-10-24 15:33:25.000000000 
+0200
***************
*** 0 ****
--- 1,25 ----
+ /* { dg-do run } */
+ /* { dg-options "-O" } */
+ 
+ extern void abort (void);
+ extern void link_error (void);
+ 
+ char c;
+ char * __attribute__((noinline,noclone))
+ foo(int i, char *p)
+ {
+   c = *p;
+   return p;
+ }
+ 
+ int main ()
+ {
+   char c = 1;
+   char *c2 = foo(0, &c);
+   if (c != 1)
+     link_error ();
+   *c2 = 2;
+   if (c != 2)
+     abort ();
+   return 0;
+ }

Reply via email to