Commit 33977f81 by Jan Hubicka Committed by Jan Hubicka

attr-noinline.c: Avoid pure-const optimization.


	* gcc.dg/attr-noinline.c: Avoid pure-const optimization.
	* gcc.dg/pr33826.c: Update dump files.
	* gcc.dg/ipa/ipa-3.c: Avoid pure-const optimization.
	* gcc.dg/ipa/ipa-5.c: Avoid pure-const optimization.

	Merge from pretty-ipa:

	2009-03-27  Jan Hubicka  <jh@suse.cz>
	* cgraph.c (dump_cgraph_node): Add replace output flag by process.
	* tree-pass.h (function_called_by_processed_nodes_p): Declare.
	* passes.c (function_called_by_processed_nodes_p): New.
	* ipa-pure-const.c (check_call): Fix handling of operands.
	(analyze_function): Dump debug output for skipped bodies.
	(local_pure_const): Use function_called_by_processed_nodes_p.
	* dwarf2out.c (reference_to_unused): Use output.
	* passes.c (do_per_function_toporder): Likewise.

	2008-11-12  Jan Hubicka  <jh@suse.cz>

	* tree-pass.h (pass_fixup_cfg, pass_local_pure_const): Declare.
	* ipa-pure-const.c (funct_state_d): Add can throw field; make
	state_set_in_source enum
	(check_decl): Ignore memory tags; do not set fake looping flags;
	dump diagnostics.
	(check_operand, check_tree, check_rhs_var, check_lhs_var,
	get_asm_expr_operands, scan_function_op, scan_function_stmt): Remove.
	(check_call, analyze_function): Rewrite.
	(check_stmt): New.
	(add_new_function): Update call of analyze_function.
	(generate_summary): Add call of analyze_function.
	(propagate): Propagate can_throw; handle state_set_in_source correctly.
	(local_pure_const): New function.
	(pass_local_pure_const): New pass.
	* ipa-inline.c (inline_transform): Set after_inlining.
	* tree-eh.c (stmt_can_throw_external): New.
	* tree-optimize.c (execute_fixup_cfg): Do not set after_inlining;
	work with aliasing built.
	* tree-flow.h (stmt_can_throw_external): New.
	* passes.c (init_optimization_passes): Schedule fixup_cfg pass early;
	and local pure/const pass in early and late optimization queue.

From-SVN: r145204
parent 617f3897
2009-03-28 Jan Hubicka <jh@suse.cz>
Merge from pretty-ipa:
2009-03-27 Jan Hubicka <jh@suse.cz>
* cgraph.c (dump_cgraph_node): Add replace output flag by process.
* tree-pass.h (function_called_by_processed_nodes_p): Declare.
* passes.c (function_called_by_processed_nodes_p): New.
* ipa-pure-const.c (check_call): Fix handling of operands.
(analyze_function): Dump debug output for skipped bodies.
(local_pure_const): Use function_called_by_processed_nodes_p.
* dwarf2out.c (reference_to_unused): Use output.
* passes.c (do_per_function_toporder): Likewise.
2008-11-12 Jan Hubicka <jh@suse.cz>
* tree-pass.h (pass_fixup_cfg, pass_local_pure_const): Declare.
* ipa-pure-const.c (funct_state_d): Add can throw field; make
state_set_in_source enum
(check_decl): Ignore memory tags; do not set fake looping flags;
dump diagnostics.
(check_operand, check_tree, check_rhs_var, check_lhs_var,
get_asm_expr_operands, scan_function_op, scan_function_stmt): Remove.
(check_call, analyze_function): Rewrite.
(check_stmt): New.
(add_new_function): Update call of analyze_function.
(generate_summary): Add call of analyze_function.
(propagate): Propagate can_throw; handle state_set_in_source correctly.
(local_pure_const): New function.
(pass_local_pure_const): New pass.
* ipa-inline.c (inline_transform): Set after_inlining.
* tree-eh.c (stmt_can_throw_external): New.
* tree-optimize.c (execute_fixup_cfg): Do not set after_inlining;
work with aliasing built.
* tree-flow.h (stmt_can_throw_external): New.
* passes.c (init_optimization_passes): Schedule fixup_cfg pass early;
and local pure/const pass in early and late optimization queue.
2009-03-28 Martin Jambor <mjambor@suse.cz> 2009-03-28 Martin Jambor <mjambor@suse.cz>
* fold-const.c (get_pointer_modulus_and_residue): New parameter * fold-const.c (get_pointer_modulus_and_residue): New parameter
......
...@@ -71,6 +71,8 @@ struct funct_state_d ...@@ -71,6 +71,8 @@ struct funct_state_d
{ {
/* See above. */ /* See above. */
enum pure_const_state_e pure_const_state; 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 /* True if the function could possibly infinite loop. There are a
lot of ways that this could be determined. We are pretty lot of ways that this could be determined. We are pretty
...@@ -80,10 +82,7 @@ struct funct_state_d ...@@ -80,10 +82,7 @@ struct funct_state_d
a behavioral change. */ a behavioral change. */
bool looping; bool looping;
/* If the state of the function was set in the source, then assume bool can_throw;
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; typedef struct funct_state_d * funct_state;
...@@ -141,21 +140,15 @@ static inline void ...@@ -141,21 +140,15 @@ static inline void
check_decl (funct_state local, check_decl (funct_state local,
tree t, bool checking_write) tree t, bool checking_write)
{ {
/* If the variable has the "used" attribute, treat it as if it had a if (MTAG_P (t))
been touched by the devil. */ return;
if (lookup_attribute ("used", DECL_ATTRIBUTES (t)))
{
local->pure_const_state = IPA_NEITHER;
local->looping = false;
return;
}
/* Do not want to do anything with volatile except mark any /* Do not want to do anything with volatile except mark any
function that uses one to be not const or pure. */ function that uses one to be not const or pure. */
if (TREE_THIS_VOLATILE (t)) if (TREE_THIS_VOLATILE (t))
{ {
local->pure_const_state = IPA_NEITHER; local->pure_const_state = IPA_NEITHER;
local->looping = false; if (dump_file)
fprintf (dump_file, " Volatile operand is not const/pure");
return; return;
} }
...@@ -163,212 +156,94 @@ check_decl (funct_state local, ...@@ -163,212 +156,94 @@ check_decl (funct_state local,
if (!TREE_STATIC (t) && !DECL_EXTERNAL (t)) if (!TREE_STATIC (t) && !DECL_EXTERNAL (t))
return; return;
/* If the variable has the "used" attribute, treat it as if it had a
been touched by the devil. */
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;
}
/* Since we have dealt with the locals and params cases above, if we /* Since we have dealt with the locals and params cases above, if we
are CHECKING_WRITE, this cannot be a pure or constant are CHECKING_WRITE, this cannot be a pure or constant
function. */ function. */
if (checking_write) if (checking_write)
{ {
local->pure_const_state = IPA_NEITHER; local->pure_const_state = IPA_NEITHER;
local->looping = false; if (dump_file)
fprintf (dump_file, " static/global memory write is not const/pure\n");
return; return;
} }
if (DECL_EXTERNAL (t) || TREE_PUBLIC (t)) if (DECL_EXTERNAL (t) || TREE_PUBLIC (t))
{ {
/* If the front end set the variable to be READONLY and /* Readonly reads are safe. */
constant, we can allow this variable in pure or const if (TREE_READONLY (t) && !TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (t)))
functions but the scope is too large for our analysis to set return; /* Read of a constant, do not change the function state. */
these bits ourselves. */
if (TREE_READONLY (t)
&& DECL_INITIAL (t)
&& is_gimple_min_invariant (DECL_INITIAL (t)))
; /* Read of a constant, do not change the function state. */
else else
{ {
if (dump_file)
fprintf (dump_file, " global memory read is not const\n");
/* Just a regular read. */ /* Just a regular read. */
if (local->pure_const_state == IPA_CONST) if (local->pure_const_state == IPA_CONST)
local->pure_const_state = IPA_PURE; local->pure_const_state = IPA_PURE;
} }
} }
else
/* Compilation level statics can be read if they are readonly {
variables. */ /* Compilation level statics can be read if they are readonly
if (TREE_READONLY (t)) variables. */
return; if (TREE_READONLY (t))
return;
/* Just a regular read. */
if (local->pure_const_state == IPA_CONST) if (dump_file)
local->pure_const_state = IPA_PURE; 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;
}
} }
/* 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. */ /* 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 void static inline void
check_tree (funct_state local, tree t, bool checking_write) check_op (funct_state local,
tree t, bool checking_write)
{ {
if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR) while (t && handled_component_p (t))
|| TREE_CODE (t) == SSA_NAME) t = TREE_OPERAND (t, 0);
if (!t)
return; return;
if (INDIRECT_REF_P (t) || TREE_CODE (t) == TARGET_MEM_REF)
/* 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); if (TREE_THIS_VOLATILE (t))
{
/* 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->pure_const_state = IPA_NEITHER;
local->looping = false; if (dump_file)
fprintf (dump_file, " Volatile indirect ref is not const/pure\n");
return; return;
} }
else if (local->pure_const_state == IPA_CONST) else if (checking_write)
local->pure_const_state = IPA_PURE; {
} local->pure_const_state = IPA_NEITHER;
if (dump_file)
if (SSA_VAR_P (t)) fprintf (dump_file, " Indirect ref write is not const/pure\n");
check_operand (local, t, checking_write); return;
} }
else
/* Scan tree T to see if there are any addresses taken in within T. */ {
if (dump_file)
static void fprintf (dump_file, " Indirect ref read is not const\n");
look_for_address_of (funct_state local, tree t) if (local->pure_const_state == IPA_CONST)
{
if (TREE_CODE (t) == ADDR_EXPR)
{
tree x = get_base_var (t);
if (TREE_CODE (x) == VAR_DECL)
{
check_decl (local, x, false);
/* Taking the address of something appears to be reasonable
in PURE code. Not allowed in const. */
if (local->pure_const_state == IPA_CONST)
local->pure_const_state = IPA_PURE; local->pure_const_state = IPA_PURE;
} }
} }
} }
/* 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)
{
look_for_address_of (local, t);
/* Memcmp and strlen can both trap and they are declared pure. */
if (tree_could_trap_p (t)
&& local->pure_const_state == IPA_CONST)
local->pure_const_state = IPA_PURE;
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)
{
/* Memcmp and strlen can both trap and they are declared pure.
Which seems to imply that we can apply the same rule here. */
if (tree_could_trap_p (t)
&& local->pure_const_state == IPA_CONST)
local->pure_const_state = IPA_PURE;
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 /* Check the parameters of a function call to CALL_EXPR to see if
there are any references in the parameters that are not allowed for there are any references in the parameters that are not allowed for
pure or const functions. Also check to see if this is either an pure or const functions. Also check to see if this is either an
...@@ -377,20 +252,37 @@ get_asm_expr_operands (funct_state local, gimple stmt) ...@@ -377,20 +252,37 @@ get_asm_expr_operands (funct_state local, gimple stmt)
the entire call expression. */ the entire call expression. */
static void static void
check_call (funct_state local, gimple call) check_call (funct_state local, gimple call, bool ipa)
{ {
int flags = gimple_call_flags (call); int flags = gimple_call_flags (call);
tree lhs, callee_t = gimple_call_fndecl (call); tree callee_t = gimple_call_fndecl (call);
struct cgraph_node* callee; struct cgraph_node* callee;
enum availability avail = AVAIL_NOT_AVAILABLE; enum availability avail = AVAIL_NOT_AVAILABLE;
size_t i; bool possibly_throws = stmt_could_throw_p (call);
bool possibly_throws_externally = (possibly_throws
lhs = gimple_call_lhs (call); && stmt_can_throw_external (call));
if (lhs)
check_lhs_var (local, lhs);
for (i = 0; i < gimple_call_num_args (call); i++) if (possibly_throws)
check_rhs_var (local, gimple_call_arg (call, i)); {
unsigned int i;
for (i = 0; i < gimple_num_ops (call); i++)
if (gimple_op (call, i)
&& tree_could_throw_p (gimple_op (call, i)))
{
if (possibly_throws && flag_non_call_exceptions)
{
if (dump_file)
fprintf (dump_file, " operand can throw; looping\n");
local->looping = true;
}
if (possibly_throws_externally)
{
if (dump_file)
fprintf (dump_file, " operand can throw externally\n");
local->can_throw = true;
}
}
}
/* The const and pure flags are set by a variety of places in the /* The const and pure flags are set by a variety of places in the
compiler (including here). If someone has already set the flags compiler (including here). If someone has already set the flags
...@@ -411,8 +303,10 @@ check_call (funct_state local, gimple call) ...@@ -411,8 +303,10 @@ check_call (funct_state local, gimple call)
or pure. */ or pure. */
if (setjmp_call_p (callee_t)) 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; local->pure_const_state = IPA_NEITHER;
local->looping = false;
} }
if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL) if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL)
...@@ -420,267 +314,252 @@ check_call (funct_state local, gimple call) ...@@ -420,267 +314,252 @@ check_call (funct_state local, gimple call)
{ {
case BUILT_IN_LONGJMP: case BUILT_IN_LONGJMP:
case BUILT_IN_NONLOCAL_GOTO: 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->pure_const_state = IPA_NEITHER;
local->looping = false; local->looping = true;
break; break;
default: default:
break; 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 /* The callee is either unknown (indirect call) or there is just no
scannable code for it (external call) . We look to see if there scannable code for it (external call) . We look to see if there
are any bits available for the callee (such as by declaration or are any bits available for the callee (such as by declaration or
because it is builtin) and process solely on the basis of those because it is builtin) and process solely on the basis of those
bits. */ bits. */
if (avail == AVAIL_NOT_AVAILABLE || avail == AVAIL_OVERWRITABLE) else if (avail <= AVAIL_OVERWRITABLE || !ipa)
{ {
if (flags & ECF_PURE) if (possibly_throws && flag_non_call_exceptions)
{
if (dump_file)
fprintf (dump_file, " can throw; looping\n");
local->looping = true;
}
if (possibly_throws_externally)
{
if (dump_file)
{
fprintf (dump_file, " can throw externally in region %i\n",
lookup_stmt_eh_region (call));
if (callee_t)
fprintf (dump_file, " callee:%s\n",
IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (callee_t)));
}
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) if (local->pure_const_state == IPA_CONST)
local->pure_const_state = IPA_PURE; local->pure_const_state = IPA_PURE;
} }
else 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) if (dump_file)
local->pure_const_state = IPA_PURE; 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. */
} }
/* TP is the part of the tree currently under the microscope. /* Look into pointer pointed to by GSIP and figure out what interesting side effects
WALK_SUBTREES is part of the walk_tree api but is unused here. it have. */
DATA is cgraph_node of the function being walked. */ static void
check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
/* 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; gimple stmt = gsi_stmt (*gsip);
struct cgraph_node *fn = (struct cgraph_node *) wi->info; unsigned int i = 0;
tree t = *tp; bitmap_iterator bi;
funct_state local = get_function_state (fn);
switch (TREE_CODE (t)) if (dump_file)
{ {
case VAR_DECL: fprintf (dump_file, " scanning: ");
if (DECL_INITIAL (t)) print_gimple_stmt (dump_file, stmt, 0, 0);
walk_tree (&DECL_INITIAL (t), scan_function_op, data, visited_nodes); }
*walk_subtrees = 0; if (gimple_loaded_syms (stmt))
break; EXECUTE_IF_SET_IN_BITMAP (gimple_loaded_syms (stmt), 0, i, bi)
check_decl (local, referenced_var_lookup (i), false);
case ADDR_EXPR: if (gimple_stored_syms (stmt))
/* This case is here to find addresses on rhs of constructors in EXECUTE_IF_SET_IN_BITMAP (gimple_stored_syms (stmt), 0, i, bi)
decl_initial of static variables. */ check_decl (local, referenced_var_lookup (i), true);
check_rhs_var (local, t);
*walk_subtrees = 0; if (gimple_code (stmt) != GIMPLE_CALL
break; && stmt_could_throw_p (stmt))
{
default: if (flag_non_call_exceptions)
break; {
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;
}
} }
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)) switch (gimple_code (stmt))
{ {
case GIMPLE_ASSIGN: case GIMPLE_ASSIGN:
{ check_op (local, gimple_assign_lhs (stmt), true);
/* First look on the lhs and see what variable is stored to */ i = 1;
tree lhs = gimple_assign_lhs (stmt); break;
tree rhs1 = gimple_assign_rhs1 (stmt); case GIMPLE_CALL:
tree rhs2 = gimple_assign_rhs2 (stmt); check_op (local, gimple_call_lhs (stmt), true);
enum tree_code code = gimple_assign_rhs_code (stmt); i = 1;
check_call (local, stmt, ipa);
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; break;
case GIMPLE_LABEL: case GIMPLE_LABEL:
if (DECL_NONLOCAL (gimple_label_label (stmt))) if (DECL_NONLOCAL (gimple_label_label (stmt)))
/* Target of long jump. */ /* Target of long jump. */
{ {
if (dump_file)
fprintf (dump_file, " nonlocal label is not const/pure");
local->pure_const_state = IPA_NEITHER; local->pure_const_state = IPA_NEITHER;
local->looping = false;
} }
break; break;
case GIMPLE_CALL:
check_call (local, stmt);
*handled_ops_p = true;
break;
case GIMPLE_ASM: case GIMPLE_ASM:
get_asm_expr_operands (local, stmt); for (i = 0; i < gimple_asm_noutputs (stmt); i++)
*handled_ops_p = true; check_op (local, TREE_VALUE (gimple_asm_output_op (stmt, i)), true);
break; 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: default:
break; break;
} }
return NULL;
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 /* This is the main routine for finding the reference patterns for
global variables within a function FN. */ global variables within a function FN. */
static void static funct_state
analyze_function (struct cgraph_node *fn) analyze_function (struct cgraph_node *fn, bool ipa)
{ {
tree decl = fn->decl; tree decl = fn->decl;
funct_state l = XCNEW (struct funct_state_d); tree old_decl = current_function_decl;
funct_state l;
if (cgraph_function_body_availability (fn) <= AVAIL_OVERWRITABLE) basic_block this_block;
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, if (cgraph_function_body_availability (fn) <= AVAIL_OVERWRITABLE)
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; if (dump_file)
return; fprintf (dump_file, "Function is not available or overwrittable; not analyzing.\n");
return NULL;
} }
if (TREE_READONLY (decl)) l = XCNEW (struct funct_state_d);
{ l->pure_const_state = IPA_CONST;
l->pure_const_state = IPA_CONST; l->state_set_in_source = IPA_NEITHER;
l->state_set_in_source = true; l->looping = false;
} l->can_throw = false;
if (DECL_PURE_P (decl))
{
l->pure_const_state = IPA_PURE;
l->state_set_in_source = true;
}
if (dump_file) if (dump_file)
{ {
fprintf (dump_file, "\n local analysis of %s with initial value = %d\n ", fprintf (dump_file, "\n\n local analysis of %s\n ",
cgraph_node_name (fn), cgraph_node_name (fn));
l->pure_const_state);
} }
if (!l->state_set_in_source) push_cfun (DECL_STRUCT_FUNCTION (decl));
current_function_decl = decl;
FOR_EACH_BB (this_block)
{ {
struct function *this_cfun = DECL_STRUCT_FUNCTION (decl); gimple_stmt_iterator gsi;
basic_block this_block; struct walk_stmt_info wi;
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) memset (&wi, 0, sizeof(wi));
for (gsi = gsi_start_bb (this_block);
!gsi_end_p (gsi);
gsi_next (&gsi))
{ {
tree old_decl = current_function_decl; check_stmt (&gsi, l, ipa);
/* Const functions cannot have back edges (an if (l->pure_const_state == IPA_NEITHER && l->looping && l->can_throw)
indication of possible infinite loop side goto end;
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: 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 (dump_file)
{ {
fprintf (dump_file, "after local analysis of %s with initial value = %d\n ", if (l->looping)
cgraph_node_name (fn), fprintf (dump_file, "Function is locally looping.\n");
l->pure_const_state); 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. */ /* Called when new function is inserted to callgraph late. */
...@@ -694,7 +573,7 @@ add_new_function (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED) ...@@ -694,7 +573,7 @@ add_new_function (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED)
since all we would be interested in are the addressof since all we would be interested in are the addressof
operations. */ operations. */
visited_nodes = pointer_set_create (); visited_nodes = pointer_set_create ();
analyze_function (node); set_function_state (node, analyze_function (node, true));
pointer_set_destroy (visited_nodes); pointer_set_destroy (visited_nodes);
visited_nodes = NULL; visited_nodes = NULL;
} }
...@@ -755,7 +634,7 @@ generate_summary (void) ...@@ -755,7 +634,7 @@ generate_summary (void)
*/ */
for (node = cgraph_nodes; node; node = node->next) for (node = cgraph_nodes; node; node = node->next)
if (cgraph_function_body_availability (node) > AVAIL_OVERWRITABLE) if (cgraph_function_body_availability (node) > AVAIL_OVERWRITABLE)
analyze_function (node); set_function_state (node, analyze_function (node, true));
pointer_set_destroy (visited_nodes); pointer_set_destroy (visited_nodes);
visited_nodes = NULL; visited_nodes = NULL;
...@@ -795,6 +674,7 @@ propagate (void) ...@@ -795,6 +674,7 @@ propagate (void)
{ {
enum pure_const_state_e pure_const_state = IPA_CONST; enum pure_const_state_e pure_const_state = IPA_CONST;
bool looping = false; bool looping = false;
bool can_throw = false;
int count = 0; int count = 0;
node = order[i]; node = order[i];
...@@ -802,38 +682,44 @@ propagate (void) ...@@ -802,38 +682,44 @@ propagate (void)
w = node; w = node;
while (w) while (w)
{ {
struct cgraph_edge *e;
funct_state w_l = get_function_state (w); funct_state w_l = get_function_state (w);
if (pure_const_state < w_l->pure_const_state) if (pure_const_state < w_l->pure_const_state)
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) if (w_l->looping)
looping = true; looping = true;
if (pure_const_state == IPA_NEITHER) if (pure_const_state == IPA_NEITHER
&& can_throw)
break; break;
if (!w_l->state_set_in_source) count++;
if (count > 1)
looping = true;
for (e = w->callees; e; e = e->next_callee)
{ {
struct cgraph_edge *e; struct cgraph_node *y = e->callee;
count++;
if (count > 1) if (cgraph_function_body_availability (y) > AVAIL_OVERWRITABLE)
looping = true;
for (e = w->callees; e; e = e->next_callee)
{ {
struct cgraph_node *y = e->callee; funct_state y_l = get_function_state (y);
if (pure_const_state < y_l->pure_const_state)
if (cgraph_function_body_availability (y) > AVAIL_OVERWRITABLE) pure_const_state = y_l->pure_const_state;
{ if (pure_const_state == IPA_NEITHER
funct_state y_l = get_function_state (y); && can_throw)
if (pure_const_state < y_l->pure_const_state) break;
pure_const_state = y_l->pure_const_state; if (y_l->looping)
if (pure_const_state == IPA_NEITHER) looping = true;
break; if (y_l->can_throw && !TREE_NOTHROW (w->decl)
if (y_l->looping) /* FIXME: We should check that the throw can get external.
looping = true; We also should handle only loops formed by can throw external
} edges. */)
can_throw = true;
} }
} }
w_info = (struct ipa_dfs_info *) w->aux; w_info = (struct ipa_dfs_info *) w->aux;
...@@ -846,36 +732,52 @@ propagate (void) ...@@ -846,36 +732,52 @@ propagate (void)
while (w) while (w)
{ {
funct_state w_l = get_function_state (w); funct_state w_l = get_function_state (w);
enum pure_const_state_e this_state = pure_const_state;
bool this_looping = looping;
/* All nodes within a cycle share the same info. */ if (w_l->state_set_in_source != IPA_NEITHER)
if (!w_l->state_set_in_source)
{ {
w_l->pure_const_state = pure_const_state; if (this_state > w_l->state_set_in_source)
w_l->looping = looping; this_state = w_l->state_set_in_source;
this_looping = false;
}
switch (pure_const_state) /* All nodes within a cycle share the same info. */
{ w_l->pure_const_state = this_state;
case IPA_CONST: w_l->looping = this_looping;
TREE_READONLY (w->decl) = 1;
DECL_LOOPING_CONST_OR_PURE_P (w->decl) = looping; switch (this_state)
if (dump_file) {
fprintf (dump_file, "Function found to be %sconst: %s\n", case IPA_CONST:
looping ? "looping " : "", if (!TREE_READONLY (w->decl) && dump_file)
lang_hooks.decl_printable_name(w->decl, 2)); fprintf (dump_file, "Function found to be %sconst: %s\n",
break; this_looping ? "looping " : "",
cgraph_node_name (w));
case IPA_PURE: TREE_READONLY (w->decl) = 1;
DECL_PURE_P (w->decl) = 1; DECL_LOOPING_CONST_OR_PURE_P (w->decl) = this_looping;
DECL_LOOPING_CONST_OR_PURE_P (w->decl) = looping; break;
if (dump_file)
fprintf (dump_file, "Function found to be %spure: %s\n", case IPA_PURE:
looping ? "looping " : "", if (!DECL_PURE_P (w->decl) && dump_file)
lang_hooks.decl_printable_name(w->decl, 2)); fprintf (dump_file, "Function found to be %spure: %s\n",
break; this_looping ? "looping " : "",
cgraph_node_name (w));
default: DECL_PURE_P (w->decl) = 1;
break; DECL_LOOPING_CONST_OR_PURE_P (w->decl) = this_looping;
} break;
default:
break;
}
if (!can_throw && !TREE_NOTHROW (w->decl))
{
/* FIXME: TREE_NOTHROW is not set because passmanager will execute
verify_ssa and verify_cfg on every function. Before fixup_cfg is done,
those functions are going to have NOTHROW calls in EH regions reulting
in ICE. */
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_info = (struct ipa_dfs_info *) w->aux;
w = w_info->next_cycle; w = w_info->next_cycle;
...@@ -935,3 +837,120 @@ struct ipa_opt_pass pass_ipa_pure_const = ...@@ -935,3 +837,120 @@ struct ipa_opt_pass pass_ipa_pure_const =
NULL, /* function_transform */ NULL, /* function_transform */
NULL /* variable_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;
/* 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. */
if (function_called_by_processed_nodes_p ())
{
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 */
}
};
...@@ -563,6 +563,7 @@ init_optimization_passes (void) ...@@ -563,6 +563,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_tail_recursion); NEXT_PASS (pass_tail_recursion);
NEXT_PASS (pass_convert_switch); NEXT_PASS (pass_convert_switch);
NEXT_PASS (pass_profile); NEXT_PASS (pass_profile);
NEXT_PASS (pass_local_pure_const);
} }
NEXT_PASS (pass_release_ssa_names); NEXT_PASS (pass_release_ssa_names);
NEXT_PASS (pass_rebuild_cgraph_edges); NEXT_PASS (pass_rebuild_cgraph_edges);
...@@ -702,6 +703,7 @@ init_optimization_passes (void) ...@@ -702,6 +703,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_tail_calls); NEXT_PASS (pass_tail_calls);
NEXT_PASS (pass_rename_ssa_copies); NEXT_PASS (pass_rename_ssa_copies);
NEXT_PASS (pass_uncprop); NEXT_PASS (pass_uncprop);
NEXT_PASS (pass_local_pure_const);
} }
NEXT_PASS (pass_del_ssa); NEXT_PASS (pass_del_ssa);
NEXT_PASS (pass_nrv); NEXT_PASS (pass_nrv);
...@@ -1373,4 +1375,30 @@ execute_ipa_pass_list (struct opt_pass *pass) ...@@ -1373,4 +1375,30 @@ execute_ipa_pass_list (struct opt_pass *pass)
while (pass); while (pass);
} }
/* Called by local passes to see if function is called by already processed nodes.
Because we process nodes in topological order, this means that function is
in recursive cycle or we introduced new direct calls. */
bool
function_called_by_processed_nodes_p (void)
{
struct cgraph_edge *e;
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 || (!e->caller->needed && !e->caller->reachable))
continue;
if (TREE_ASM_WRITTEN (e->caller->decl))
continue;
if (!e->caller->process && !e->caller->global.inlined_to)
break;
}
if (dump_file && e)
{
fprintf (dump_file, "Already processed call to:\n");
dump_cgraph_node (dump_file, e->caller);
}
return e != NULL;
}
#include "gt-passes.h" #include "gt-passes.h"
2009-03-28 Jan Hubicka <jh@suse.cz>
* gcc.dg/attr-noinline.c: Avoid pure-const optimization.
* gcc.dg/pr33826.c: Update dump files.
* gcc.dg/ipa/ipa-3.c: Avoid pure-const optimization.
* gcc.dg/ipa/ipa-5.c: Avoid pure-const optimization.
2009-03-28 Martin Jambor <mjambor@suse.cz> 2009-03-28 Martin Jambor <mjambor@suse.cz>
* g++.dg/tree-ssa/fwprop-align.C: New test. * g++.dg/tree-ssa/fwprop-align.C: New test.
......
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-options "-O2 -finline-functions" } */ /* { dg-options "-O2 -finline-functions" } */
static inline void __attribute__((__noinline__)) function_definition(void) {} /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */ 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 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_before(void) {t();}
static void function_declaration_both_after(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 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_both_after(void) {t();}
static void function_declaration_noinline_before(void) __attribute__((__noinline__)); /* { dg-message "note: previous declaration" "" } */ 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_before(void) {t();} /* { dg-warning "follows declaration with attribute noinline" "" } */
static inline void function_declaration_noinline_after(void) {} /* { dg-message "note: previous definition" "" } */ 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 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 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 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 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) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
static void function_declaration_inline_noinline_before(void) {} static void function_declaration_inline_noinline_before(void) {t();}
static inline void function_declaration_inline_noinline_after(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) {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_inline_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
...@@ -41,7 +43,7 @@ static void function_declaration_noinline_inline_before(void) __attribute__((__n ...@@ -41,7 +43,7 @@ static void function_declaration_noinline_inline_before(void) __attribute__((__n
static inline void function_declaration_noinline_inline_before(void); /* { dg-warning "follows declaration with attribute noinline" "" } */ static inline void function_declaration_noinline_inline_before(void); /* { dg-warning "follows declaration with attribute noinline" "" } */
static void function_declaration_noinline_inline_before(void) {} static void function_declaration_noinline_inline_before(void) {t();}
void f () { void f () {
function_definition (); function_definition ();
......
...@@ -6,8 +6,10 @@ ...@@ -6,8 +6,10 @@
/* Double constants. */ /* Double constants. */
#include <stdio.h> #include <stdio.h>
void t(void);
int g (double b, double c) int g (double b, double c)
{ {
t();
return (int)(b+c); return (int)(b+c);
} }
int f (double a) int f (double a)
......
...@@ -5,12 +5,15 @@ ...@@ -5,12 +5,15 @@
/* Float & short constants. */ /* Float & short constants. */
#include <stdio.h> #include <stdio.h>
void t(void);
int g (float b, short c) int g (float b, short c)
{ {
t();
return c + (int)b; return c + (int)b;
} }
int f (float a) int f (float a)
{ {
t();
/* a is modified. */ /* a is modified. */
if (a++ > 0) if (a++ > 0)
g (a, 3); g (a, 3);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-require-effective-target nonpic } */ /* { dg-require-effective-target nonpic } */
/* { dg-options "-O1 -fdump-ipa-pure-const" } */ /* { dg-options "-O1 -fdump-tree-local-pure-const1 -fdump-ipa-pure-const" } */
int recurese1 (int i) int recurese1 (int i)
{ {
...@@ -30,8 +30,14 @@ int norecurse1b (int i) ...@@ -30,8 +30,14 @@ int norecurse1b (int i)
return i+1; return i+1;
} }
/* { dg-final { scan-ipa-dump "found to be const: norecurse1a" "pure-const" } } */ /* { dg-final { scan-tree-dump "found to be const: norecurse1a" "local-pure-const1" } } */
/* { dg-final { scan-ipa-dump "found to be const: norecurse1b" "pure-const" } } */ /* { 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: 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: recurse2a" "pure-const" } } */
/* { dg-final { scan-ipa-dump-not "found to be pure: recurse2b" "pure-const" } } */ /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2b" "pure-const" } } */
...@@ -39,3 +45,4 @@ int norecurse1b (int i) ...@@ -39,3 +45,4 @@ int norecurse1b (int i)
/* { dg-final { scan-ipa-dump-not "found to be const: recurse2a" "pure-const" } } */ /* { 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 { scan-ipa-dump-not "found to be const: recurse2b" "pure-const" } } */
/* { dg-final { cleanup-ipa-dump "pure-const" } } */ /* { dg-final { cleanup-ipa-dump "pure-const" } } */
/* { dg-final { cleanup-tree-dump "local-pure-const1" } } */
...@@ -2402,6 +2402,32 @@ tree_could_throw_p (tree t) ...@@ -2402,6 +2402,32 @@ tree_could_throw_p (tree t)
return false; 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;
bool inlinable_call = 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, inlinable_call);
}
/* Return true if STMT can throw an exception that is caught within /* Return true if STMT can throw an exception that is caught within
the current function (CFUN). */ the current function (CFUN). */
......
...@@ -1077,6 +1077,7 @@ extern bool operation_could_trap_p (enum tree_code, bool, bool, tree); ...@@ -1077,6 +1077,7 @@ extern bool operation_could_trap_p (enum tree_code, bool, bool, tree);
extern bool stmt_could_throw_p (gimple); extern bool stmt_could_throw_p (gimple);
extern bool tree_could_throw_p (tree); extern bool tree_could_throw_p (tree);
extern bool stmt_can_throw_internal (gimple); extern bool stmt_can_throw_internal (gimple);
extern bool stmt_can_throw_external (gimple);
extern void add_stmt_to_eh_region (gimple, int); extern void add_stmt_to_eh_region (gimple, int);
extern bool remove_stmt_from_eh_region (gimple); extern bool remove_stmt_from_eh_region (gimple);
extern bool maybe_clean_or_replace_eh_stmt (gimple, gimple); extern bool maybe_clean_or_replace_eh_stmt (gimple, gimple);
......
...@@ -311,6 +311,7 @@ execute_fixup_cfg (void) ...@@ -311,6 +311,7 @@ execute_fixup_cfg (void)
if (gimple_in_ssa_p (cfun)) if (gimple_in_ssa_p (cfun))
{ {
todo |= TODO_update_ssa | TODO_cleanup_cfg; todo |= TODO_update_ssa | TODO_cleanup_cfg;
mark_symbols_for_renaming (stmt);
update_stmt (stmt); update_stmt (stmt);
} }
} }
......
...@@ -389,6 +389,7 @@ extern struct gimple_opt_pass pass_reassoc; ...@@ -389,6 +389,7 @@ extern struct gimple_opt_pass pass_reassoc;
extern struct gimple_opt_pass pass_rebuild_cgraph_edges; extern struct gimple_opt_pass pass_rebuild_cgraph_edges;
extern struct gimple_opt_pass pass_build_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_reset_cc_flags;
extern struct gimple_opt_pass pass_local_pure_const;
/* IPA Passes */ /* IPA Passes */
extern struct ipa_opt_pass pass_ipa_inline; extern struct ipa_opt_pass pass_ipa_inline;
...@@ -524,6 +525,7 @@ extern void execute_pass_list (struct opt_pass *); ...@@ -524,6 +525,7 @@ extern void execute_pass_list (struct opt_pass *);
extern void execute_ipa_pass_list (struct opt_pass *); extern void execute_ipa_pass_list (struct opt_pass *);
extern void print_current_pass (FILE *); extern void print_current_pass (FILE *);
extern void debug_pass (void); extern void debug_pass (void);
extern bool function_called_by_processed_nodes_p (void);
/* Set to true if the pass is called the first time during compilation of the /* Set to true if the pass is called the first time during compilation of the
current function. Note that using this information in the optimization current function. Note that using this information in the optimization
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment