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