Rewrite of pure/const pass and nothrow stuff

Jan Hubicka jh@suse.cz
Fri Nov 14 23:48:00 GMT 2008


Hi,
this is variant of patch I comitted.  Several minnor issues:
  - some of testsuite needed updating
  - stmt_can_throw_external had bug returning true for non-trhowing
    statements (it was ignored in this case except for calls)
  - cgraph_function_body_availability had important bug returning true
    for weak functions.  This bug is on mainline too, will send separate
    patch to fix it.

Index: cgraph.c
===================================================================
*** cgraph.c	(revision 141798)
--- cgraph.c	(working copy)
*************** cgraph_function_body_availability (struc
*** 1423,1429 ****
      avail = AVAIL_NOT_AVAILABLE;
    else if (node->local.local)
      avail = AVAIL_LOCAL;
!   else if (node->local.externally_visible)
      avail = AVAIL_AVAILABLE;
  
    /* If the function can be overwritten, return OVERWRITABLE.  Take
--- 1423,1429 ----
      avail = AVAIL_NOT_AVAILABLE;
    else if (node->local.local)
      avail = AVAIL_LOCAL;
!   else if (!node->local.externally_visible)
      avail = AVAIL_AVAILABLE;
  
    /* If the function can be overwritten, return OVERWRITABLE.  Take
Index: tree-pass.h
===================================================================
*** tree-pass.h	(revision 141798)
--- tree-pass.h	(working copy)
*************** extern struct gimple_opt_pass pass_build
*** 308,313 ****
--- 308,314 ----
  extern struct gimple_opt_pass pass_tree_profile;
  extern struct gimple_opt_pass pass_early_tree_profile;
  extern struct gimple_opt_pass pass_cleanup_cfg;
+ extern struct gimple_opt_pass pass_fixup_cfg;
  extern struct gimple_opt_pass pass_referenced_vars;
  extern struct gimple_opt_pass pass_sra;
  extern struct gimple_opt_pass pass_sra_early;
*************** extern struct gimple_opt_pass pass_reass
*** 388,393 ****
--- 389,395 ----
  extern struct gimple_opt_pass pass_rebuild_cgraph_edges;
  extern struct gimple_opt_pass pass_build_cgraph_edges;
  extern struct gimple_opt_pass pass_reset_cc_flags;
+ extern struct gimple_opt_pass pass_local_pure_const;
  
  /* IPA Passes */
  extern struct ipa_opt_pass pass_ipa_inline;
Index: testsuite/gcc.dg/attr-noinline.c
===================================================================
*** testsuite/gcc.dg/attr-noinline.c	(revision 141798)
--- testsuite/gcc.dg/attr-noinline.c	(working copy)
***************
*** 1,39 ****
  /* { dg-do compile } */
  /* { dg-options "-O2 -finline-functions" } */
  
! static inline void __attribute__((__noinline__)) function_definition(void) {} /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
  static inline void __attribute__((__noinline__)) function_declaration_both_before(void); /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
! static void function_declaration_both_before(void) {}
  
  static void function_declaration_both_after(void);
  
  static inline void __attribute__((__noinline__)) function_declaration_both_after(void); /* { dg-warning "(inline function \[^\n\]* given attribute noinline|declared inline after its definition)" "" } */
  
! static void function_declaration_both_after(void) {}
  
  static void function_declaration_noinline_before(void) __attribute__((__noinline__)); /* { dg-message "note: previous declaration" "" } */
  
! static inline void function_declaration_noinline_before(void) {} /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static inline void function_declaration_noinline_after(void) {} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_before(void); /* { dg-message "note: previous declaration" "" } */
  
! static void __attribute__((__noinline__)) function_declaration_inline_before(void) {} /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_noinline_before(void); /* { dg-message "note: previous declaration" "" } */
  
  static void function_declaration_inline_noinline_before(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
! static void function_declaration_inline_noinline_before(void) {}
  
  static inline void function_declaration_inline_noinline_after(void);
  
! static void function_declaration_inline_noinline_after(void) {} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_inline_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
--- 1,41 ----
  /* { dg-do compile } */
  /* { dg-options "-O2 -finline-functions" } */
  
! extern int t();
! 
! static inline void __attribute__((__noinline__)) function_definition(void) {t();} /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
  static inline void __attribute__((__noinline__)) function_declaration_both_before(void); /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
! static void function_declaration_both_before(void) {t();}
  
  static void function_declaration_both_after(void);
  
  static inline void __attribute__((__noinline__)) function_declaration_both_after(void); /* { dg-warning "(inline function \[^\n\]* given attribute noinline|declared inline after its definition)" "" } */
  
! static void function_declaration_both_after(void) {t();}
  
  static void function_declaration_noinline_before(void) __attribute__((__noinline__)); /* { dg-message "note: previous declaration" "" } */
  
! static inline void function_declaration_noinline_before(void) {t();} /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static inline void function_declaration_noinline_after(void) {t();} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_before(void); /* { dg-message "note: previous declaration" "" } */
  
! static void __attribute__((__noinline__)) function_declaration_inline_before(void) {t();} /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_noinline_before(void); /* { dg-message "note: previous declaration" "" } */
  
  static void function_declaration_inline_noinline_before(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
! static void function_declaration_inline_noinline_before(void) {t();}
  
  static inline void function_declaration_inline_noinline_after(void);
  
! static void function_declaration_inline_noinline_after(void) {t();} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_inline_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
*************** static void function_declaration_noinlin
*** 41,47 ****
  
  static inline void function_declaration_noinline_inline_before(void); /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static void function_declaration_noinline_inline_before(void) {}
  
  void f () {
    function_definition ();
--- 43,49 ----
  
  static inline void function_declaration_noinline_inline_before(void); /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static void function_declaration_noinline_inline_before(void) {t();}
  
  void f () {
    function_definition ();
Index: testsuite/gcc.dg/pr33826.c
===================================================================
*** testsuite/gcc.dg/pr33826.c	(revision 141798)
--- testsuite/gcc.dg/pr33826.c	(working copy)
***************
*** 3,9 ****
  
  /* { dg-do compile } */
  /* { dg-require-effective-target nonpic } */
! /* { dg-options "-O1 -fdump-ipa-pure-const" } */
  
  int recurese1 (int i)
  {
--- 3,9 ----
  
  /* { dg-do compile } */
  /* { dg-require-effective-target nonpic } */
! /* { dg-options "-O1 -fdump-tree-local-pure-const1 -fdump-ipa-pure-const" } */
  
  int recurese1 (int i)
  {
*************** int norecurse1b (int i)
*** 30,37 ****
    return i+1;
  }
  
! /* { dg-final { scan-ipa-dump "found to be const: norecurse1a" "pure-const" } } */
! /* { dg-final { scan-ipa-dump "found to be const: norecurse1b" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse1" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2a" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2b" "pure-const" } } */
--- 30,43 ----
    return i+1;
  }
  
! /* { dg-final { scan-tree-dump "found to be const: norecurse1a" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump "found to be const: norecurse1b" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be pure: recurse1" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be pure: recurse2a" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be pure: recurse2b" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be const: recurse1" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be const: recurse2a" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be const: recurse2b" "local-pure-const1" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse1" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2a" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2b" "pure-const" } } */
*************** int norecurse1b (int i)
*** 39,41 ****
--- 45,48 ----
  /* { dg-final { scan-ipa-dump-not "found to be const: recurse2a" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be const: recurse2b" "pure-const" } } */
  /* { dg-final { cleanup-ipa-dump "pure-const" } } */
+ /* { dg-final { cleanup-tree-dump "local-pure-const1" } } */
Index: testsuite/gcc.dg/ipa/ipa-3.c
===================================================================
*** testsuite/gcc.dg/ipa/ipa-3.c	(revision 141798)
--- testsuite/gcc.dg/ipa/ipa-3.c	(working copy)
***************
*** 6,13 ****
--- 6,15 ----
  /* Double constants.  */
  
  #include <stdio.h>
+ void t(void);
  int g (double b, double c)
  {
+   t();
    return (int)(b+c);  
  }
  int f (double a)
Index: testsuite/gcc.dg/ipa/ipa-5.c
===================================================================
*** testsuite/gcc.dg/ipa/ipa-5.c	(revision 141798)
--- testsuite/gcc.dg/ipa/ipa-5.c	(working copy)
***************
*** 5,16 ****
--- 5,19 ----
  /* Float & short constants.  */
  
  #include <stdio.h>
+ void t(void);
  int g (float b, short c)
  {
+   t();
    return c + (int)b;
  }
  int f (float a)
  {
+   t();
    /* a is modified.  */
    if (a++ > 0)
      g (a, 3);
Index: ipa-pure-const.c
===================================================================
*** ipa-pure-const.c	(revision 141798)
--- ipa-pure-const.c	(working copy)
*************** struct funct_state_d 
*** 71,76 ****
--- 71,78 ----
  {
    /* See above.  */
    enum pure_const_state_e pure_const_state;
+   /* What user set here; we can be always sure about this.  */
+   enum pure_const_state_e state_set_in_source; 
  
    /* True if the function could possibly infinite loop.  There are a
       lot of ways that this could be determined.  We are pretty
*************** struct funct_state_d 
*** 80,89 ****
       a behavioral change.  */
    bool looping;
  
!   /* If the state of the function was set in the source, then assume
!      that it was done properly even if the analysis we do would be
!      more pessimestic.  */
!   bool state_set_in_source; 
  };
  
  typedef struct funct_state_d * funct_state;
--- 82,88 ----
       a behavioral change.  */
    bool looping;
  
!   bool can_throw;
  };
  
  typedef struct funct_state_d * funct_state;
*************** static inline void 
*** 141,152 ****
  check_decl (funct_state local, 
  	    tree t, bool checking_write)
  {
    /* Do not want to do anything with volatile except mark any
       function that uses one to be not const or pure.  */
    if (TREE_THIS_VOLATILE (t)) 
      { 
        local->pure_const_state = IPA_NEITHER;
!       local->looping = false;
        return;
      }
  
--- 140,154 ----
  check_decl (funct_state local, 
  	    tree t, bool checking_write)
  {
+   if (MTAG_P (t))
+     return;
    /* Do not want to do anything with volatile except mark any
       function that uses one to be not const or pure.  */
    if (TREE_THIS_VOLATILE (t)) 
      { 
        local->pure_const_state = IPA_NEITHER;
!       if (dump_file)
!         fprintf (dump_file, "    Volatile operand is not const/pure");
        return;
      }
  
*************** check_decl (funct_state local, 
*** 159,165 ****
    if (lookup_attribute ("used", DECL_ATTRIBUTES (t)))
      {
        local->pure_const_state = IPA_NEITHER;
!       local->looping = false;
        return;
      }
  
--- 161,168 ----
    if (lookup_attribute ("used", DECL_ATTRIBUTES (t)))
      {
        local->pure_const_state = IPA_NEITHER;
!       if (dump_file)
!         fprintf (dump_file, "    Used static/global variable is not const/pure\n");
        return;
      }
  
*************** check_decl (funct_state local, 
*** 169,175 ****
    if (checking_write) 
      {
        local->pure_const_state = IPA_NEITHER;
!       local->looping = false;
        return;
      }
  
--- 172,179 ----
    if (checking_write) 
      {
        local->pure_const_state = IPA_NEITHER;
!       if (dump_file)
!         fprintf (dump_file, "    static/global memory write is not const/pure\n");
        return;
      }
  
*************** check_decl (funct_state local, 
*** 177,333 ****
      {
        /* Readonly reads are safe.  */
        if (TREE_READONLY (t) && !TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (t)))
! 	; /* Read of a constant, do not change the function state.  */
        else 
  	{
  	  /* Just a regular read.  */
  	  if (local->pure_const_state == IPA_CONST)
  	    local->pure_const_state = IPA_PURE;
  	}
      }
!   
!   /* Compilation level statics can be read if they are readonly
!      variables.  */
!   if (TREE_READONLY (t))
!     return;
! 
!   /* Just a regular read.  */
!   if (local->pure_const_state == IPA_CONST)
!     local->pure_const_state = IPA_PURE;
  }
  
- /* If T is a VAR_DECL check to see if it is an allowed reference.  */
- 
- static void
- check_operand (funct_state local, 
- 	       tree t, bool checking_write)
- {
-   if (!t) return;
  
!   if (TREE_CODE (t) == VAR_DECL)
!     check_decl (local, t, checking_write); 
! }
! 
! /* Examine tree T for references.  */
  
! static void
! check_tree (funct_state local, tree t, bool checking_write)
  {
!   if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR)
!       || TREE_CODE (t) == SSA_NAME)
      return;
! 
!   /* Any tree which is volatile disqualifies this function from being
!      const or pure. */
!   if (TREE_THIS_VOLATILE (t))
!     {
!       local->pure_const_state = IPA_NEITHER;
!       local->looping = false;
!       return;
!     }
! 
!   while (TREE_CODE (t) == REALPART_EXPR 
! 	 || TREE_CODE (t) == IMAGPART_EXPR
! 	 || handled_component_p (t))
      {
!       if (TREE_CODE (t) == ARRAY_REF)
! 	check_operand (local, TREE_OPERAND (t, 1), false);
!       t = TREE_OPERAND (t, 0);
!     }
! 
!   /* The bottom of an indirect reference can only be read, not
!      written.  */
!   if (INDIRECT_REF_P (t))
!     {
!       check_tree (local, TREE_OPERAND (t, 0), false);
!       
!       /* Any indirect reference that occurs on the lhs
! 	 disqualifies the function from being pure or const. Any
! 	 indirect reference that occurs on the rhs disqualifies the
! 	 function from being const.  */
!       if (checking_write) 
! 	{
  	  local->pure_const_state = IPA_NEITHER;
! 	  local->looping = false;
  	  return;
  	}
!       else if (local->pure_const_state == IPA_CONST)
! 	local->pure_const_state = IPA_PURE;
!     }
! 
!   if (SSA_VAR_P (t))
!     check_operand (local, t, checking_write);
! }
! 
! /* Check to see if T is a read or address of operation on a var we are
!    interested in analyzing.  LOCAL is passed in to get access to its
!    bit vectors.  */
! 
! static void
! check_rhs_var (funct_state local, tree t)
! {
!   check_tree(local, t, false);
! }
! 
! /* Check to see if T is an assignment to a var we are interested in
!    analyzing.  LOCAL is passed in to get access to its bit vectors. */
! 
! static void
! check_lhs_var (funct_state local, tree t)
! {
!   check_tree(local, t, true);
! }
! 
! /* This is a scaled down version of get_asm_expr_operands from
!    tree_ssa_operands.c.  The version there runs much later and assumes
!    that aliasing information is already available. Here we are just
!    trying to find if the set of inputs and outputs contain references
!    or address of operations to local static variables.  STMT is the
!    actual asm statement.  */
! 
! static void
! get_asm_expr_operands (funct_state local, gimple stmt)
! {
!   size_t noutputs = gimple_asm_noutputs (stmt);
!   const char **oconstraints
!     = (const char **) alloca ((noutputs) * sizeof (const char *));
!   size_t i;
!   tree op;
!   const char *constraint;
!   bool allows_mem, allows_reg, is_inout;
!   
!   for (i = 0; i < noutputs; i++)
!     {
!       op = gimple_asm_output_op (stmt, i);
!       oconstraints[i] = constraint
! 	= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
!       parse_output_constraint (&constraint, i, 0, 0,
! 			       &allows_mem, &allows_reg, &is_inout);
!       
!       check_lhs_var (local, TREE_VALUE (op));
!     }
! 
!   for (i = 0; i < gimple_asm_ninputs (stmt); i++)
!     {
!       op = gimple_asm_input_op (stmt, i);
!       constraint
! 	= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
!       parse_input_constraint (&constraint, 0, 0, noutputs, 0,
! 			      oconstraints, &allows_mem, &allows_reg);
!       
!       check_rhs_var (local, TREE_VALUE (op));
!     }
!   
!   for (i = 0; i < gimple_asm_nclobbers (stmt); i++)
!     {
!       op = gimple_asm_clobber_op (stmt, i);
!       if (simple_cst_equal(TREE_VALUE (op), memory_identifier_string) == 1) 
! 	/* Abandon all hope, ye who enter here. */
! 	local->pure_const_state = IPA_NEITHER;
      }
- 
-   if (gimple_asm_volatile_p (stmt))
-     local->pure_const_state = IPA_NEITHER;
  }
  
  /* Check the parameters of a function call to CALL_EXPR to see if
--- 181,247 ----
      {
        /* Readonly reads are safe.  */
        if (TREE_READONLY (t) && !TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (t)))
! 	return; /* Read of a constant, do not change the function state.  */
        else 
  	{
+           if (dump_file)
+             fprintf (dump_file, "    global memory read is not const\n");
  	  /* Just a regular read.  */
  	  if (local->pure_const_state == IPA_CONST)
  	    local->pure_const_state = IPA_PURE;
  	}
      }
!   else
!     {
!       /* Compilation level statics can be read if they are readonly
! 	 variables.  */
!       if (TREE_READONLY (t))
! 	return;
! 
!       if (dump_file)
! 	fprintf (dump_file, "    static memory read is not const\n");
!       /* Just a regular read.  */
!       if (local->pure_const_state == IPA_CONST)
! 	local->pure_const_state = IPA_PURE;
!     }
  }
  
  
! /* Check to see if the use (or definition when CHECKING_WRITE is true)
!    variable T is legal in a function that is either pure or const.  */
  
! static inline void 
! check_op (funct_state local, 
! 	    tree t, bool checking_write)
  {
!   while (t && handled_component_p (t))
!     t = TREE_OPERAND (t, 0);
!   if (!t)
      return;
!   if (INDIRECT_REF_P (t) || TREE_CODE (t) == TARGET_MEM_REF)
      {
!       if (TREE_THIS_VOLATILE (t)) 
! 	{ 
  	  local->pure_const_state = IPA_NEITHER;
! 	  if (dump_file)
! 	    fprintf (dump_file, "    Volatile indirect ref is not const/pure\n");
  	  return;
  	}
!       else if (checking_write)
! 	{ 
! 	  local->pure_const_state = IPA_NEITHER;
! 	  if (dump_file)
! 	    fprintf (dump_file, "    Indirect ref write is not const/pure\n");
! 	  return;
! 	}
!        else
!         {
! 	  if (dump_file)
! 	    fprintf (dump_file, "    Indirect ref read is not const\n");
!           if (local->pure_const_state == IPA_CONST)
! 	    local->pure_const_state = IPA_PURE;
! 	}
      }
  }
  
  /* Check the parameters of a function call to CALL_EXPR to see if
*************** get_asm_expr_operands (funct_state local
*** 338,357 ****
     the entire call expression.  */
  
  static void
! check_call (funct_state local, gimple call) 
  {
    int flags = gimple_call_flags (call);
!   tree lhs, callee_t = gimple_call_fndecl (call);
    struct cgraph_node* callee;
    enum availability avail = AVAIL_NOT_AVAILABLE;
!   size_t i;
! 
!   lhs = gimple_call_lhs (call);
!   if (lhs)
!     check_lhs_var (local, lhs);
! 
!   for (i = 0; i < gimple_call_num_args (call); i++)
!     check_rhs_var (local, gimple_call_arg (call, i));
    
    /* The const and pure flags are set by a variety of places in the
       compiler (including here).  If someone has already set the flags
--- 252,287 ----
     the entire call expression.  */
  
  static void
! check_call (funct_state local, gimple call, bool ipa)
  {
    int flags = gimple_call_flags (call);
!   tree callee_t = gimple_call_fndecl (call);
    struct cgraph_node* callee;
    enum availability avail = AVAIL_NOT_AVAILABLE;
!   bool possibly_throws = stmt_could_throw_p (call);
!   bool possibly_throws_externally = (possibly_throws
!   				     && stmt_can_throw_external (call));
! 
!   if (possibly_throws)
!     {
!       unsigned int i;
!       for (i = 0; i < gimple_num_ops (call); i++)
!         if (stmt_could_throw_p (call))
! 	  {
! 	    if (possibly_throws && flag_non_call_exceptions)
! 	      {
! 		if (dump_file)
! 		  fprintf (dump_file, "    operand can throw; looping");
! 		local->looping = true;
! 	      }
! 	    if (possibly_throws_externally)
! 	      {
! 		if (dump_file)
! 		  fprintf (dump_file, "    operand can throw externally");
! 		local->can_throw = true;
! 	      }
! 	  }
!     }
    
    /* The const and pure flags are set by a variety of places in the
       compiler (including here).  If someone has already set the flags
*************** check_call (funct_state local, gimple ca
*** 372,379 ****
  	 or pure.  */
        if (setjmp_call_p (callee_t))
  	{
  	  local->pure_const_state = IPA_NEITHER;
- 	  local->looping = false;
  	}
  
        if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL)
--- 302,311 ----
  	 or pure.  */
        if (setjmp_call_p (callee_t))
  	{
+ 	  if (dump_file)
+ 	    fprintf (dump_file, "    setjmp is not const/pure\n");
+           local->looping = true;
  	  local->pure_const_state = IPA_NEITHER;
  	}
  
        if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL)
*************** check_call (funct_state local, gimple ca
*** 381,647 ****
  	  {
  	  case BUILT_IN_LONGJMP:
  	  case BUILT_IN_NONLOCAL_GOTO:
  	    local->pure_const_state = IPA_NEITHER;
! 	    local->looping = false;
  	    break;
  	  default:
  	    break;
  	  }
      }
  
    /* The callee is either unknown (indirect call) or there is just no
       scannable code for it (external call) .  We look to see if there
       are any bits available for the callee (such as by declaration or
       because it is builtin) and process solely on the basis of those
       bits. */
!   if (avail == AVAIL_NOT_AVAILABLE || avail == AVAIL_OVERWRITABLE)
      {
!       if (flags & ECF_PURE) 
! 	{
  	  if (local->pure_const_state == IPA_CONST)
  	    local->pure_const_state = IPA_PURE;
  	}
        else 
- 	local->pure_const_state = IPA_NEITHER;
-     }
-   else
-     {
-       /* We have the code and we will scan it for the effects. */
-       if (flags & ECF_PURE) 
  	{
! 	  if (local->pure_const_state == IPA_CONST)
! 	    local->pure_const_state = IPA_PURE;
  	}
      }
  }
  
! /* TP is the part of the tree currently under the microscope.
!    WALK_SUBTREES is part of the walk_tree api but is unused here.
!    DATA is cgraph_node of the function being walked.  */
! 
! /* FIXME: When this is converted to run over SSA form, this code
!    should be converted to use the operand scanner.  */
! 
! static tree
! scan_function_op (tree *tp, int *walk_subtrees, void *data)
! {
!   struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
!   struct cgraph_node *fn = (struct cgraph_node *) wi->info;
!   tree t = *tp;
!   funct_state local = get_function_state (fn);
! 
!   switch (TREE_CODE (t))  
!     {
!     case VAR_DECL:
!       if (DECL_INITIAL (t))
! 	walk_tree (&DECL_INITIAL (t), scan_function_op, data, visited_nodes);
!       *walk_subtrees = 0;
!       break;
! 
!     case ADDR_EXPR:
!       /* This case is here to find addresses on rhs of constructors in
! 	 decl_initial of static variables. */
!       check_rhs_var (local, t);
!       *walk_subtrees = 0;
!       break;
! 
!     default:
!       break;
!     }
!   return NULL;
! }
! 
! static tree
! scan_function_stmt (gimple_stmt_iterator *gsi_p,
! 		    bool *handled_ops_p,
! 		    struct walk_stmt_info *wi)
  {
!   struct cgraph_node *fn = (struct cgraph_node *) wi->info;
!   gimple stmt = gsi_stmt (*gsi_p);
!   funct_state local = get_function_state (fn);
  
    switch (gimple_code (stmt))
      {
      case GIMPLE_ASSIGN:
!       {
! 	/* First look on the lhs and see what variable is stored to */
! 	tree lhs = gimple_assign_lhs (stmt);
! 	tree rhs1 = gimple_assign_rhs1 (stmt);
! 	tree rhs2 = gimple_assign_rhs2 (stmt);
! 	enum tree_code code = gimple_assign_rhs_code (stmt);
! 
! 	check_lhs_var (local, lhs);
! 
! 	/* For the purposes of figuring out what the cast affects */
! 
! 	/* Next check the operands on the rhs to see if they are ok. */
! 	switch (TREE_CODE_CLASS (code))
! 	  {
! 	  case tcc_binary:	    
!  	    {
!  	      check_rhs_var (local, rhs1);
!  	      check_rhs_var (local, rhs2);
! 	    }
! 	    break;
! 	  case tcc_unary:
!  	    {
!  	      check_rhs_var (local, rhs1);
!  	    }
! 
! 	    break;
! 	  case tcc_reference:
! 	    check_rhs_var (local, rhs1);
! 	    break;
! 	  case tcc_declaration:
! 	    check_rhs_var (local, rhs1);
! 	    break;
! 	  case tcc_expression:
! 	    switch (code)
! 	      {
! 	      case ADDR_EXPR:
! 		check_rhs_var (local, rhs1);
! 		break;
! 	      default:
! 		break;
! 	      }
! 	    break;
! 	  default:
! 	    break;
! 	  }
! 	*handled_ops_p = true;
!       }
        break;
- 
      case GIMPLE_LABEL:
        if (DECL_NONLOCAL (gimple_label_label (stmt)))
  	/* Target of long jump. */
  	{
  	  local->pure_const_state = IPA_NEITHER;
- 	  local->looping = false;
  	}
        break;
- 
-     case GIMPLE_CALL:
-       check_call (local, stmt);
-       *handled_ops_p = true;
-       break;
-       
      case GIMPLE_ASM:
!       get_asm_expr_operands (local, stmt);
!       *handled_ops_p = true;
!       break;
!       
      default:
        break;
      }
!   return NULL;
  }
  
  
  /* This is the main routine for finding the reference patterns for
     global variables within a function FN.  */
  
! static void
! analyze_function (struct cgraph_node *fn)
  {
    tree decl = fn->decl;
!   funct_state l = XCNEW (struct funct_state_d);
! 
!  if (cgraph_function_body_availability (fn) <= AVAIL_OVERWRITABLE)
!    return;
  
!   set_function_state (fn, l);
  
    l->pure_const_state = IPA_CONST;
!   l->state_set_in_source = false;
!   if (DECL_LOOPING_CONST_OR_PURE_P (decl))
!     l->looping = true;
!   else
!     l->looping = false;
  
!   /* If this function does not return normally or does not bind local,
!      do not touch this unless it has been marked as const or pure by the
!      front end.  */
!   if (TREE_THIS_VOLATILE (decl)
!       || !targetm.binds_local_p (decl))
      {
!       l->pure_const_state = IPA_NEITHER;
!       return;
      }
! 
!   if (TREE_READONLY (decl))
      {
!       l->pure_const_state = IPA_CONST;
!       l->state_set_in_source = true;
      }
!   if (DECL_PURE_P (decl))
      {
!       l->pure_const_state = IPA_PURE;
!       l->state_set_in_source = true;
      }
  
!   if (dump_file)
      {
!       fprintf (dump_file, "\n local analysis of %s with initial value = %d\n ", 
! 	       cgraph_node_name (fn),
! 	       l->pure_const_state);
      }
!   
!   if (!l->state_set_in_source)
      {
!       struct function *this_cfun = DECL_STRUCT_FUNCTION (decl);
!       basic_block this_block;
!       
!       FOR_EACH_BB_FN (this_block, this_cfun)
! 	{
! 	  gimple_stmt_iterator gsi;
! 	  struct walk_stmt_info wi;
! 
! 	  memset (&wi, 0, sizeof(wi));
! 	  for (gsi = gsi_start_bb (this_block);
! 	       !gsi_end_p (gsi);
! 	       gsi_next (&gsi))
! 	    {
! 	      wi.info = fn;
! 	      wi.pset = visited_nodes;
! 	      walk_gimple_stmt (&gsi, scan_function_stmt, scan_function_op, 
! 				&wi);
! 	      if (l->pure_const_state == IPA_NEITHER) 
! 		goto end;
! 	    }
! 	}
! 
!       if (l->pure_const_state != IPA_NEITHER)
! 	{
! 	  tree old_decl = current_function_decl;
! 	  /* Const functions cannot have back edges (an
! 	     indication of possible infinite loop side
! 	     effect.  */
! 	    
! 	  current_function_decl = fn->decl;
! 
! 	  /* The C++ front end, has a tendency to some times jerk away
! 	     a function after it has created it.  This should have
! 	     been fixed.  */
! 	  gcc_assert (DECL_STRUCT_FUNCTION (fn->decl));
! 	  
! 	  push_cfun (DECL_STRUCT_FUNCTION (fn->decl));
! 	  
! 	  if (mark_dfs_back_edges ())
! 	    l->pure_const_state = IPA_NEITHER;
! 	  
! 	  current_function_decl = old_decl;
! 	  pop_cfun ();
! 	}
      }
  
! end:
    if (dump_file)
      {
!       fprintf (dump_file, "after local analysis of %s with initial value = %d\n ", 
! 	       cgraph_node_name (fn),
! 	       l->pure_const_state);
      }
  }
  
  /* Called when new function is inserted to callgraph late.  */
--- 313,554 ----
  	  {
  	  case BUILT_IN_LONGJMP:
  	  case BUILT_IN_NONLOCAL_GOTO:
+ 	    if (dump_file)
+ 	      fprintf (dump_file, "    longjmp and nonlocal goto is not const/pure\n");
  	    local->pure_const_state = IPA_NEITHER;
!             local->looping = true;
  	    break;
  	  default:
  	    break;
  	  }
      }
  
+   /* When not in IPA mode, we can still handle self recursion.  */
+   if (!ipa && callee_t == current_function_decl)
+     local->looping = true;
    /* The callee is either unknown (indirect call) or there is just no
       scannable code for it (external call) .  We look to see if there
       are any bits available for the callee (such as by declaration or
       because it is builtin) and process solely on the basis of those
       bits. */
!   else if (avail <= AVAIL_OVERWRITABLE || !ipa)
      {
!       if (possibly_throws && flag_non_call_exceptions)
!         {
! 	  if (dump_file)
! 	    fprintf (dump_file, "    can throw; looping");
!           local->looping = true;
! 	}
!       if (possibly_throws_externally)
!         {
! 	  if (dump_file)
! 	    fprintf (dump_file, "    can throw externally");
!           local->can_throw = true;
! 	}
!       if (flags & ECF_CONST) 
! 	{
!           if (callee_t && DECL_LOOPING_CONST_OR_PURE_P (callee_t))
!             local->looping = true;
! 	 }
!       else if (flags & ECF_PURE) 
! 	{
!           if (callee_t && DECL_LOOPING_CONST_OR_PURE_P (callee_t))
!             local->looping = true;
! 	  if (dump_file)
! 	    fprintf (dump_file, "    pure function call in not const\n");
  	  if (local->pure_const_state == IPA_CONST)
  	    local->pure_const_state = IPA_PURE;
  	}
        else 
  	{
! 	  if (dump_file)
! 	    fprintf (dump_file, "    uknown function call is not const/pure\n");
! 	  local->pure_const_state = IPA_NEITHER;
!           local->looping = true;
  	}
      }
+   /* Direct functions calls are handled by IPA propagation.  */
  }
  
! /* Look into pointer pointed to by GSIP and figure out what interesting side effects
!    it have.  */
! static void
! check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
  {
!   gimple stmt = gsi_stmt (*gsip);
!   unsigned int i = 0;
!   bitmap_iterator bi;
  
+   if (dump_file)
+     {
+       fprintf (dump_file, "  scanning: ");
+       print_gimple_stmt (dump_file, stmt, 0, 0);
+     }
+   if (gimple_loaded_syms (stmt))
+     EXECUTE_IF_SET_IN_BITMAP (gimple_loaded_syms (stmt), 0, i, bi)
+       check_decl (local, referenced_var_lookup (i), false);
+   if (gimple_stored_syms (stmt))
+     EXECUTE_IF_SET_IN_BITMAP (gimple_stored_syms (stmt), 0, i, bi)
+       check_decl (local, referenced_var_lookup (i), true);
+ 
+   if (gimple_code (stmt) != GIMPLE_CALL
+       && stmt_could_throw_p (stmt))
+     {
+       if (flag_non_call_exceptions)
+ 	{
+ 	  if (dump_file)
+ 	    fprintf (dump_file, "    can throw; looping");
+ 	  local->looping = true;
+ 	}
+       if (stmt_can_throw_external (stmt))
+ 	{
+ 	  if (dump_file)
+ 	    fprintf (dump_file, "    can throw externally");
+ 	  local->can_throw = true;
+ 	}
+     }
    switch (gimple_code (stmt))
      {
      case GIMPLE_ASSIGN:
!       check_op (local, gimple_assign_lhs (stmt), true);
!       i = 1;
!       break;
!     case GIMPLE_CALL:
!       check_op (local, gimple_call_lhs (stmt), true);
!       i = 1;
!       check_call (local, stmt, ipa);
        break;
      case GIMPLE_LABEL:
        if (DECL_NONLOCAL (gimple_label_label (stmt)))
  	/* Target of long jump. */
  	{
+           if (dump_file)
+             fprintf (dump_file, "    nonlocal label is not const/pure");
  	  local->pure_const_state = IPA_NEITHER;
  	}
        break;
      case GIMPLE_ASM:
!       for (i = 0; i < gimple_asm_noutputs (stmt); i++)
!          check_op (local, TREE_VALUE (gimple_asm_output_op (stmt, i)), true);
!       for (i = 0; i < gimple_asm_ninputs (stmt); i++)
!          check_op (local, TREE_VALUE (gimple_asm_input_op (stmt, i)), false);
!       for (i = 0; i < gimple_asm_nclobbers (stmt); i++)
! 	{
! 	  tree op = gimple_asm_clobber_op (stmt, i);
! 	  if (simple_cst_equal(TREE_VALUE (op), memory_identifier_string) == 1) 
! 	    {
!               if (dump_file)
!                 fprintf (dump_file, "    memory asm clobber is not const/pure");
! 	      /* Abandon all hope, ye who enter here. */
! 	      local->pure_const_state = IPA_NEITHER;
! 	    }
! 	}
!       if (gimple_asm_volatile_p (stmt))
! 	{
! 	  if (dump_file)
! 	    fprintf (dump_file, "    volatile is not const/pure");
! 	  /* Abandon all hope, ye who enter here. */
! 	  local->pure_const_state = IPA_NEITHER;
!           local->looping = true;
! 	}
!       return;
      default:
        break;
      }
! 
!   for (; i < gimple_num_ops (stmt); i++)
!     check_op (local, gimple_op (stmt, i), false);
  }
  
  
  /* This is the main routine for finding the reference patterns for
     global variables within a function FN.  */
  
! static funct_state
! analyze_function (struct cgraph_node *fn, bool ipa)
  {
    tree decl = fn->decl;
!   tree old_decl = current_function_decl;
!   funct_state l;
!   basic_block this_block;
  
!   if (cgraph_function_body_availability (fn) <= AVAIL_OVERWRITABLE)
!     return NULL;
  
+   l = XCNEW (struct funct_state_d);
    l->pure_const_state = IPA_CONST;
!   l->state_set_in_source = IPA_NEITHER;
!   l->looping = false;
!   l->can_throw = false;
  
!   if (dump_file)
      {
!       fprintf (dump_file, "\n\n local analysis of %s\n ", 
! 	       cgraph_node_name (fn));
      }
!   
!   push_cfun (DECL_STRUCT_FUNCTION (decl));
!   current_function_decl = decl;
!   
!   FOR_EACH_BB (this_block)
      {
!       gimple_stmt_iterator gsi;
!       struct walk_stmt_info wi;
! 
!       memset (&wi, 0, sizeof(wi));
!       for (gsi = gsi_start_bb (this_block);
! 	   !gsi_end_p (gsi);
! 	   gsi_next (&gsi))
! 	{
! 	  check_stmt (&gsi, l, ipa);
! 	  if (l->pure_const_state == IPA_NEITHER && l->looping && l->can_throw)
! 	    goto end;
! 	}
      }
! 
! end:
!   if (l->pure_const_state != IPA_NEITHER)
      {
!       /* Const functions cannot have back edges (an
! 	 indication of possible infinite loop side
! 	 effect.  */
!       if (mark_dfs_back_edges ())
! 	l->looping = true;
!       
      }
  
!   if (TREE_READONLY (decl))
      {
!       l->pure_const_state = IPA_CONST;
!       l->state_set_in_source = IPA_CONST;
!       if (!DECL_LOOPING_CONST_OR_PURE_P (decl))
!         l->looping = false;
      }
!   if (DECL_PURE_P (decl))
      {
!       if (l->pure_const_state != IPA_CONST)
!         l->pure_const_state = IPA_PURE;
!       l->state_set_in_source = IPA_PURE;
!       if (!DECL_LOOPING_CONST_OR_PURE_P (decl))
!         l->looping = false;
      }
+   if (TREE_NOTHROW (decl))
+     l->can_throw = false;
  
!   pop_cfun ();
!   current_function_decl = old_decl;
    if (dump_file)
      {
!       if (l->looping)
!         fprintf (dump_file, "Function is locally looping.\n");
!       if (l->can_throw)
!         fprintf (dump_file, "Function is locally throwing.\n");
!       if (l->pure_const_state == IPA_CONST)
!         fprintf (dump_file, "Function is locally const.\n");
!       if (l->pure_const_state == IPA_PURE)
!         fprintf (dump_file, "Function is locally pure.\n");
      }
+   return l;
  }
  
  /* Called when new function is inserted to callgraph late.  */
*************** add_new_function (struct cgraph_node *no
*** 655,661 ****
       since all we would be interested in are the addressof
       operations.  */
    visited_nodes = pointer_set_create ();
!   analyze_function (node);
    pointer_set_destroy (visited_nodes);
    visited_nodes = NULL;
  }
--- 562,568 ----
       since all we would be interested in are the addressof
       operations.  */
    visited_nodes = pointer_set_create ();
!   set_function_state (node, analyze_function (node, true));
    pointer_set_destroy (visited_nodes);
    visited_nodes = NULL;
  }
*************** generate_summary (void)
*** 716,722 ****
    */
    for (node = cgraph_nodes; node; node = node->next)
      if (cgraph_function_body_availability (node) > AVAIL_OVERWRITABLE)
!       analyze_function (node);
  
    pointer_set_destroy (visited_nodes);
    visited_nodes = NULL;
--- 623,629 ----
    */
    for (node = cgraph_nodes; node; node = node->next)
      if (cgraph_function_body_availability (node) > AVAIL_OVERWRITABLE)
!       set_function_state (node, analyze_function (node, true));
  
    pointer_set_destroy (visited_nodes);
    visited_nodes = NULL;
*************** propagate (void)
*** 756,761 ****
--- 663,669 ----
      {
        enum pure_const_state_e pure_const_state = IPA_CONST;
        bool looping = false;
+       bool can_throw = false;
        int count = 0;
        node = order[i];
  
*************** propagate (void)
*** 763,800 ****
        w = node;
        while (w)
  	{
  	  funct_state w_l = get_function_state (w);
  	  if (pure_const_state < w_l->pure_const_state)
  	    pure_const_state = w_l->pure_const_state;
  
  	  if (w_l->looping)
  	    looping = true;
  
  	  if (pure_const_state == IPA_NEITHER) 
  	    break;
  
! 	  if (!w_l->state_set_in_source)
  	    {
! 	      struct cgraph_edge *e;
! 	      count++;
  
! 	      if (count > 1)
! 		looping = true;
! 		    
! 	      for (e = w->callees; e; e = e->next_callee) 
  		{
! 		  struct cgraph_node *y = e->callee;
! 
! 		  if (cgraph_function_body_availability (y) > AVAIL_OVERWRITABLE)
! 		    {
! 		      funct_state y_l = get_function_state (y);
! 		      if (pure_const_state < y_l->pure_const_state)
! 			pure_const_state = y_l->pure_const_state;
! 		      if (pure_const_state == IPA_NEITHER) 
! 			break;
! 		      if (y_l->looping)
! 			looping = true;
! 		    }
  		}
  	    }
  	  w_info = (struct ipa_dfs_info *) w->aux;
--- 671,712 ----
        w = node;
        while (w)
  	{
+ 	  struct cgraph_edge *e;
  	  funct_state w_l = get_function_state (w);
  	  if (pure_const_state < w_l->pure_const_state)
  	    pure_const_state = w_l->pure_const_state;
  
+ 	  if (w_l->can_throw)
+ 	    can_throw = true;
  	  if (w_l->looping)
  	    looping = true;
  
  	  if (pure_const_state == IPA_NEITHER) 
  	    break;
  
! 	  count++;
! 
! 	  if (count > 1)
! 	    looping = true;
! 		
! 	  for (e = w->callees; e; e = e->next_callee) 
  	    {
! 	      struct cgraph_node *y = e->callee;
  
! 	      if (cgraph_function_body_availability (y) > AVAIL_OVERWRITABLE)
  		{
! 		  funct_state y_l = get_function_state (y);
! 		  if (pure_const_state < y_l->pure_const_state)
! 		    pure_const_state = y_l->pure_const_state;
! 		  if (pure_const_state == IPA_NEITHER) 
! 		    break;
! 		  if (y_l->looping)
! 		    looping = true;
! 		  if (y_l->can_throw && !TREE_NOTHROW (w->decl)
! 		      /* FIXME: We should check that the throw can get external.
! 		         We also should handle only loops formed by can throw external
! 			 edges.  */)
! 		    can_throw = true;
  		}
  	    }
  	  w_info = (struct ipa_dfs_info *) w->aux;
*************** propagate (void)
*** 807,842 ****
        while (w)
  	{
  	  funct_state w_l = get_function_state (w);
  
! 	  /* All nodes within a cycle share the same info.  */
! 	  if (!w_l->state_set_in_source)
  	    {
! 	      w_l->pure_const_state = pure_const_state;
! 	      w_l->looping = looping;
  
! 	      switch (pure_const_state)
! 		{
! 		case IPA_CONST:
! 		  TREE_READONLY (w->decl) = 1;
! 		  DECL_LOOPING_CONST_OR_PURE_P (w->decl) = looping;
! 		  if (dump_file)
! 		    fprintf (dump_file, "Function found to be %sconst: %s\n",  
! 			     looping ? "looping " : "",
! 			     lang_hooks.decl_printable_name(w->decl, 2)); 
! 		  break;
! 		  
! 		case IPA_PURE:
! 		  DECL_PURE_P (w->decl) = 1;
! 		  DECL_LOOPING_CONST_OR_PURE_P (w->decl) = looping;
! 		  if (dump_file)
! 		    fprintf (dump_file, "Function found to be %spure: %s\n",  
! 			     looping ? "looping " : "",
! 			     lang_hooks.decl_printable_name(w->decl, 2)); 
! 		  break;
! 		  
! 		default:
! 		  break;
! 		}
  	    }
  	  w_info = (struct ipa_dfs_info *) w->aux;
  	  w = w_info->next_cycle;
--- 719,766 ----
        while (w)
  	{
  	  funct_state w_l = get_function_state (w);
+ 	  enum pure_const_state_e this_state = pure_const_state;
+ 	  bool this_looping = looping;
  
! 	  if (w_l->state_set_in_source != IPA_NEITHER)
  	    {
! 	      if (this_state > w_l->state_set_in_source)
! 	        this_state = w_l->state_set_in_source;
! 	      this_looping = false;
! 	    }
  
! 	  /* All nodes within a cycle share the same info.  */
! 	  w_l->pure_const_state = this_state;
! 	  w_l->looping = this_looping;
! 
! 	  switch (this_state)
! 	    {
! 	    case IPA_CONST:
! 	      if (!TREE_READONLY (w->decl) && dump_file)
! 		fprintf (dump_file, "Function found to be %sconst: %s\n",  
! 			 this_looping ? "looping " : "",
! 			 cgraph_node_name (w)); 
! 	      TREE_READONLY (w->decl) = 1;
! 	      DECL_LOOPING_CONST_OR_PURE_P (w->decl) = this_looping;
! 	      break;
! 	      
! 	    case IPA_PURE:
! 	      if (!DECL_PURE_P (w->decl) && dump_file)
! 		fprintf (dump_file, "Function found to be %spure: %s\n",  
! 			 this_looping ? "looping " : "",
! 			 cgraph_node_name (w)); 
! 	      DECL_PURE_P (w->decl) = 1;
! 	      DECL_LOOPING_CONST_OR_PURE_P (w->decl) = this_looping;
! 	      break;
! 	      
! 	    default:
! 	      break;
! 	    }
! 	  if (!can_throw && !TREE_NOTHROW (w->decl))
! 	    {
! 	      if (dump_file)
! 		fprintf (dump_file, "Function found to be nothrow: %s\n",  
! 			 cgraph_node_name (w));
  	    }
  	  w_info = (struct ipa_dfs_info *) w->aux;
  	  w = w_info->next_cycle;
*************** struct ipa_opt_pass pass_ipa_pure_const 
*** 896,898 ****
--- 820,951 ----
   NULL,			                /* function_transform */
   NULL					/* variable_transform */
  };
+ 
+ /* Simple local pass for pure const discovery reusing the analysis from
+    ipa_pure_const.   This pass is effective when executed together with
+    other optimization passes in early optimization pass queue.  */
+ 
+ static unsigned int
+ local_pure_const (void)
+ {
+   bool changed = false;
+   funct_state l;
+   struct cgraph_edge *e;
+ 
+   /* Because we do not schedule pass_fixup_cfg over whole program after early optimizations
+      we must not promote functions that are called by already processed functions.  This is
+      hitting only the recursive cases that are not too interesting at first place.  
+      FIXME: there should be more robust way of testing this.  */
+ 
+   for (e = cgraph_node (current_function_decl)->callers; e; e = e->next_caller)
+     {
+       if (e->caller->decl == current_function_decl)
+         continue;
+       if (!e->caller->analyzed || !DECL_STRUCT_FUNCTION (e->caller->decl))
+         continue;
+       if (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->caller->decl)))
+         break;
+     }
+   if (e)
+     {
+       if (dump_file)
+         fprintf (dump_file, "Function called in recursive cycle; ignoring\n");
+       return 0;
+     }
+ 
+   l = analyze_function (cgraph_node (current_function_decl), false);
+   if (!l)
+     {
+       if (dump_file)
+         fprintf (dump_file, "Function has wrong visibility; ignoring\n");
+       return 0;
+     }
+ 
+   switch (l->pure_const_state)
+     {
+     case IPA_CONST:
+       if (!TREE_READONLY (current_function_decl))
+ 	{
+ 	  TREE_READONLY (current_function_decl) = 1;
+ 	  DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = l->looping;
+ 	  changed = true;
+ 	  if (dump_file)
+ 	    fprintf (dump_file, "Function found to be %sconst: %s\n",
+ 		     l->looping ? "looping " : "",
+ 		     lang_hooks.decl_printable_name (current_function_decl,
+ 						     2));
+ 	}
+       else if (DECL_LOOPING_CONST_OR_PURE_P (current_function_decl)
+ 	       && !l->looping)
+ 	{
+ 	  DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = false;
+ 	  changed = true;
+ 	  if (dump_file)
+ 	    fprintf (dump_file, "Function found to be non-looping: %s\n",
+ 		     lang_hooks.decl_printable_name (current_function_decl,
+ 						     2));
+ 	}
+       break;
+ 
+     case IPA_PURE:
+       if (!TREE_READONLY (current_function_decl))
+ 	{
+ 	  DECL_PURE_P (current_function_decl) = 1;
+ 	  DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = l->looping;
+ 	  changed = true;
+ 	  if (dump_file)
+ 	    fprintf (dump_file, "Function found to be %spure: %s\n",
+ 		     l->looping ? "looping " : "",
+ 		     lang_hooks.decl_printable_name (current_function_decl,
+ 						     2));
+ 	}
+       else if (DECL_LOOPING_CONST_OR_PURE_P (current_function_decl)
+ 	       && !l->looping)
+ 	{
+ 	  DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = false;
+ 	  changed = true;
+ 	  if (dump_file)
+ 	    fprintf (dump_file, "Function found to be non-looping: %s\n",
+ 		     lang_hooks.decl_printable_name (current_function_decl,
+ 						     2));
+ 	}
+       break;
+ 
+     default:
+       break;
+     }
+   if (!l->can_throw && !TREE_NOTHROW (current_function_decl))
+     {
+       TREE_NOTHROW (current_function_decl) = 1;
+       changed = true;
+       if (dump_file)
+ 	fprintf (dump_file, "Function found to be nothrow: %s\n",
+ 		 lang_hooks.decl_printable_name (current_function_decl,
+ 						 2));
+     }
+   if (l)
+     free (l);
+   if (changed)
+     return execute_fixup_cfg ();
+   else
+     return 0;
+ }
+ 
+ struct gimple_opt_pass pass_local_pure_const =
+ {
+  {
+   GIMPLE_PASS,
+   "local-pure-const",	                /* name */
+   gate_pure_const,			/* gate */
+   local_pure_const,		        /* execute */
+   NULL,					/* sub */
+   NULL,					/* next */
+   0,					/* static_pass_number */
+   TV_IPA_PURE_CONST,		        /* tv_id */
+   0,	                                /* properties_required */
+   0,					/* properties_provided */
+   0,					/* properties_destroyed */
+   0,					/* todo_flags_start */
+   0                                     /* todo_flags_finish */
+  }
+ };
Index: ipa-inline.c
===================================================================
*** ipa-inline.c	(revision 141798)
--- ipa-inline.c	(working copy)
*************** inline_transform (struct cgraph_node *no
*** 1732,1737 ****
--- 1732,1738 ----
        todo = optimize_inline_calls (current_function_decl);
        timevar_pop (TV_INTEGRATION);
      }
+   cfun->after_inlining = true;
    return todo | execute_fixup_cfg ();
  }
  
Index: tree-eh.c
===================================================================
*** tree-eh.c	(revision 141798)
--- tree-eh.c	(working copy)
*************** tree_could_throw_p (tree t)
*** 2384,2389 ****
--- 2384,2414 ----
    return false;
  }
  
+ /* Return true if STMT can throw an exception that is not caught within
+    the current function (CFUN).  */
+ 
+ bool
+ stmt_can_throw_external (gimple stmt)
+ {
+   int region_nr;
+   bool is_resx = false;
+ 
+   if (!stmt_could_throw_p (stmt))
+     return false;
+ 
+   if (gimple_code (stmt) == GIMPLE_RESX)
+     {
+       region_nr = gimple_resx_region (stmt);
+       is_resx = true;
+     }
+   else
+     region_nr = lookup_stmt_eh_region (stmt);
+ 
+   if (region_nr < 0)
+     return true;
+ 
+   return can_throw_external_1 (region_nr, is_resx);
+ }
  
  /* Return true if STMT can throw an exception that is caught within
     the current function (CFUN).  */
Index: tree-optimize.c
===================================================================
*** tree-optimize.c	(revision 141798)
--- tree-optimize.c	(working copy)
*************** execute_fixup_cfg (void)
*** 292,299 ****
    gimple_stmt_iterator gsi;
    int todo = gimple_in_ssa_p (cfun) ? TODO_verify_ssa : 0;
  
-   cfun->after_inlining = true;
- 
    if (cfun->eh)
      FOR_EACH_BB (bb)
        {
--- 292,297 ----
*************** execute_fixup_cfg (void)
*** 312,322 ****
  		if (gimple_in_ssa_p (cfun))
  		  {
  		    todo |= TODO_update_ssa | TODO_cleanup_cfg;
  	            update_stmt (stmt);
  		  }
  	      }
  
! 	    if (!stmt_could_throw_p (stmt) && lookup_stmt_eh_region (stmt))
  	      remove_stmt_from_eh_region (stmt);
  	  }
  
--- 310,321 ----
  		if (gimple_in_ssa_p (cfun))
  		  {
  		    todo |= TODO_update_ssa | TODO_cleanup_cfg;
+                     mark_symbols_for_renaming (stmt);
  	            update_stmt (stmt);
  		  }
  	      }
  
! 	    if (!stmt_could_throw_p (stmt) && lookup_stmt_eh_region (stmt) != -2)
  	      remove_stmt_from_eh_region (stmt);
  	  }
  
*************** execute_fixup_cfg (void)
*** 331,336 ****
--- 330,355 ----
    return todo;
  }
  
+ struct gimple_opt_pass pass_fixup_cfg =
+ {
+  {
+   GIMPLE_PASS,
+   NULL,					/* name */
+   NULL,					/* gate */
+   execute_fixup_cfg,		/* execute */
+   NULL,					/* sub */
+   NULL,					/* next */
+   0,					/* static_pass_number */
+   0,					/* tv_id */
+   PROP_cfg,				/* properties_required */
+   0,					/* properties_provided */
+   0,					/* properties_destroyed */
+   0,					/* todo_flags_start */
+   0					/* todo_flags_finish */
+  }
+ };
+ 
+ 
  /* Do the actions required to initialize internal data structures used
     in tree-ssa optimization passes.  */
  
Index: tree-flow.h
===================================================================
*** tree-flow.h	(revision 141798)
--- tree-flow.h	(working copy)
*************** extern bool operation_could_trap_p (enum
*** 1071,1076 ****
--- 1071,1077 ----
  extern bool stmt_could_throw_p (gimple);
  extern bool tree_could_throw_p (tree);
  extern bool stmt_can_throw_internal (gimple);
+ extern bool stmt_can_throw_external (gimple);
  extern void add_stmt_to_eh_region (gimple, int);
  extern bool remove_stmt_from_eh_region (gimple);
  extern bool maybe_clean_or_replace_eh_stmt (gimple, gimple);
Index: passes.c
===================================================================
*** passes.c	(revision 141798)
--- passes.c	(working copy)
*************** init_optimization_passes (void)
*** 536,541 ****
--- 536,542 ----
    NEXT_PASS (pass_early_local_passes);
      {
        struct opt_pass **p = &pass_early_local_passes.pass.sub;
+       NEXT_PASS (pass_fixup_cfg);
        NEXT_PASS (pass_tree_profile);
        NEXT_PASS (pass_cleanup_cfg);
        NEXT_PASS (pass_init_datastructures);
*************** init_optimization_passes (void)
*** 562,567 ****
--- 563,569 ----
  	  NEXT_PASS (pass_tail_recursion);
  	  NEXT_PASS (pass_convert_switch);
            NEXT_PASS (pass_profile);
+           NEXT_PASS (pass_local_pure_const);
  	}
        NEXT_PASS (pass_release_ssa_names);
        NEXT_PASS (pass_rebuild_cgraph_edges);
*************** init_optimization_passes (void)
*** 701,706 ****
--- 703,709 ----
        NEXT_PASS (pass_tail_calls);
        NEXT_PASS (pass_rename_ssa_copies);
        NEXT_PASS (pass_uncprop);
+       NEXT_PASS (pass_local_pure_const);
      }
    NEXT_PASS (pass_del_ssa);
    NEXT_PASS (pass_nrv);



More information about the Gcc-patches mailing list