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; + }