This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[tree-ssa] remove more useless gotos
- From: Richard Henderson <rth at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Wed, 17 Sep 2003 15:16:43 -0700
- Subject: [tree-ssa] remove more useless gotos
Handles the case of
{
{
...
goto label;
}
};
label:
which you see a lot when inlining functions that return values.
Also adds a dump for remove_useless_stmts_and_vars, which makes
it easier for me to see what happens with eh lowering (which is
immediately afterward).
r~
* tree-cfg.c (struct rusv_data): New.
(remove_useless_stmts_and_vars_1): Rename from
remove_useless_stmts_and_vars. Use rusv_data. Handle goto-next
via remembering the last goto seen, and zapping it when appropriate.
(remove_useless_stmts_and_vars): New. Loop until done.
* tree-flow.h (remove_useless_stmts_and_vars): Update decl.
* tree-optimize.c (optimize_function_tree): Don't cache fnbody.
Dump data after remove_useless_stmts_and_vars.
* tree-ssa.c (rewrite_out_of_ssa): Kill loop around
remove_useless_stmts_and_vars.
* tree-dump.c (dump_files): Add .useless.
* tree.h (enum tree_dump_index): Add TDI_useless.
Index: tree-cfg.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-cfg.c,v
retrieving revision 1.1.4.163
diff -c -p -d -r1.1.4.163 tree-cfg.c
*** tree-cfg.c 13 Sep 2003 16:58:45 -0000 1.1.4.163
--- tree-cfg.c 17 Sep 2003 21:54:27 -0000
*************** cleanup_tree_cfg (void)
*** 1461,1471 ****
BIND_EXPR, or TRY block, we will need to repeat this optimization pass
to ensure we eliminate all the useless code. */
! int
! remove_useless_stmts_and_vars (tree *first_p, int remove_unused_vars)
{
tree_stmt_iterator i;
- int repeat = 0;
for (i = tsi_start (first_p); !tsi_end_p (i); tsi_next (&i))
{
--- 1461,1477 ----
BIND_EXPR, or TRY block, we will need to repeat this optimization pass
to ensure we eliminate all the useless code. */
! struct rusv_data
! {
! tree *last_goto;
! bool repeat;
! bool remove_unused_vars;
! };
!
! static void
! remove_useless_stmts_and_vars_1 (tree *first_p, struct rusv_data *data)
{
tree_stmt_iterator i;
for (i = tsi_start (first_p); !tsi_end_p (i); tsi_next (&i))
{
*************** remove_useless_stmts_and_vars (tree *fir
*** 1488,1500 ****
/* Dive into control structures. */
stmt_p = tsi_stmt_ptr (i);
code = TREE_CODE (*stmt_p);
if (code == COND_EXPR)
{
tree then_clause, else_clause, cond;
! repeat |= remove_useless_stmts_and_vars (&COND_EXPR_THEN (*stmt_p),
! remove_unused_vars);
! repeat |= remove_useless_stmts_and_vars (&COND_EXPR_ELSE (*stmt_p),
! remove_unused_vars);
then_clause = COND_EXPR_THEN (*stmt_p);
else_clause = COND_EXPR_ELSE (*stmt_p);
--- 1494,1514 ----
/* Dive into control structures. */
stmt_p = tsi_stmt_ptr (i);
code = TREE_CODE (*stmt_p);
+
+ /* Zap last goto handling if we see anything that can generate code. */
+ if (code != CASE_LABEL_EXPR && code != LABEL_EXPR && code != BIND_EXPR)
+ data->last_goto = NULL;
+
if (code == COND_EXPR)
{
tree then_clause, else_clause, cond;
! remove_useless_stmts_and_vars_1 (&COND_EXPR_THEN (*stmt_p), data);
!
! if (!IS_EMPTY_STMT (COND_EXPR_ELSE (*stmt_p)))
! {
! data->last_goto = NULL;
! remove_useless_stmts_and_vars_1 (&COND_EXPR_ELSE (*stmt_p), data);
! }
then_clause = COND_EXPR_THEN (*stmt_p);
else_clause = COND_EXPR_ELSE (*stmt_p);
*************** remove_useless_stmts_and_vars (tree *fir
*** 1510,1529 ****
if (integer_nonzerop (cond) && IS_EMPTY_STMT (else_clause))
{
*stmt_p = then_clause;
! repeat = 1;
}
else if (integer_zerop (cond) && IS_EMPTY_STMT (then_clause))
{
*stmt_p = else_clause;
! repeat = 1;
}
else if (TREE_CODE (then_clause) == GOTO_EXPR
! && TREE_CODE (else_clause) == GOTO_EXPR
! && (GOTO_DESTINATION (then_clause)
! == GOTO_DESTINATION (else_clause)))
{
*stmt_p = then_clause;
! repeat = 1;
}
/* If the THEN/ELSE clause merely assigns a value to
a variable/parameter which is already known to contain
--- 1524,1543 ----
if (integer_nonzerop (cond) && IS_EMPTY_STMT (else_clause))
{
*stmt_p = then_clause;
! data->repeat = true;
}
else if (integer_zerop (cond) && IS_EMPTY_STMT (then_clause))
{
*stmt_p = else_clause;
! data->repeat = true;
}
else if (TREE_CODE (then_clause) == GOTO_EXPR
! && TREE_CODE (else_clause) == GOTO_EXPR
! && (GOTO_DESTINATION (then_clause)
! == GOTO_DESTINATION (else_clause)))
{
*stmt_p = then_clause;
! data->repeat = true;
}
/* If the THEN/ELSE clause merely assigns a value to
a variable/parameter which is already known to contain
*************** remove_useless_stmts_and_vars (tree *fir
*** 1554,1573 ****
}
}
else if (code == SWITCH_EXPR)
! repeat |= remove_useless_stmts_and_vars (&SWITCH_BODY (*stmt_p),
! remove_unused_vars);
else if (code == CATCH_EXPR)
! repeat |= remove_useless_stmts_and_vars (&CATCH_BODY (*stmt_p),
! remove_unused_vars);
else if (code == EH_FILTER_EXPR)
! repeat |= remove_useless_stmts_and_vars (&EH_FILTER_FAILURE (*stmt_p),
! remove_unused_vars);
else if (code == TRY_CATCH_EXPR || code == TRY_FINALLY_EXPR)
{
! repeat |= remove_useless_stmts_and_vars (&TREE_OPERAND (*stmt_p, 0),
! remove_unused_vars);
! repeat |= remove_useless_stmts_and_vars (&TREE_OPERAND (*stmt_p, 1),
! remove_unused_vars);
/* If the handler of a TRY_CATCH or TRY_FINALLY is empty, then
we can emit the TRY block without the enclosing TRY_CATCH_EXPR
--- 1568,1590 ----
}
}
else if (code == SWITCH_EXPR)
! remove_useless_stmts_and_vars_1 (&SWITCH_BODY (*stmt_p), data);
else if (code == CATCH_EXPR)
! {
! remove_useless_stmts_and_vars_1 (&CATCH_BODY (*stmt_p), data);
! data->last_goto = NULL;
! }
else if (code == EH_FILTER_EXPR)
! {
! remove_useless_stmts_and_vars_1 (&EH_FILTER_FAILURE (*stmt_p), data);
! data->last_goto = NULL;
! }
else if (code == TRY_CATCH_EXPR || code == TRY_FINALLY_EXPR)
{
! remove_useless_stmts_and_vars_1 (&TREE_OPERAND (*stmt_p, 0), data);
! data->last_goto = NULL;
! remove_useless_stmts_and_vars_1 (&TREE_OPERAND (*stmt_p, 1), data);
! data->last_goto = NULL;
/* If the handler of a TRY_CATCH or TRY_FINALLY is empty, then
we can emit the TRY block without the enclosing TRY_CATCH_EXPR
*************** remove_useless_stmts_and_vars (tree *fir
*** 1575,1581 ****
if (IS_EMPTY_STMT (TREE_OPERAND (*stmt_p, 1)))
{
*stmt_p = TREE_OPERAND (*stmt_p, 0);
! repeat = 1;
}
/* If the body of a TRY_FINALLY is empty, then we can emit
--- 1592,1598 ----
if (IS_EMPTY_STMT (TREE_OPERAND (*stmt_p, 1)))
{
*stmt_p = TREE_OPERAND (*stmt_p, 0);
! data->repeat = true;
}
/* If the body of a TRY_FINALLY is empty, then we can emit
*************** remove_useless_stmts_and_vars (tree *fir
*** 1585,1591 ****
&& IS_EMPTY_STMT (TREE_OPERAND (*stmt_p, 0)))
{
*stmt_p = TREE_OPERAND (*stmt_p, 1);
! repeat = 1;
}
/* If the body of a TRY_CATCH_EXPR is empty, then we can
--- 1602,1608 ----
&& IS_EMPTY_STMT (TREE_OPERAND (*stmt_p, 0)))
{
*stmt_p = TREE_OPERAND (*stmt_p, 1);
! data->repeat = true;
}
/* If the body of a TRY_CATCH_EXPR is empty, then we can
*************** remove_useless_stmts_and_vars (tree *fir
*** 1594,1608 ****
&& IS_EMPTY_STMT (TREE_OPERAND (*stmt_p, 0)))
{
*stmt_p = build_empty_stmt ();
! repeat = 1;
}
}
else if (code == BIND_EXPR)
{
tree block;
/* First remove anything underneath the BIND_EXPR. */
! repeat |= remove_useless_stmts_and_vars (&BIND_EXPR_BODY (*stmt_p),
! remove_unused_vars);
/* If the BIND_EXPR has no variables, then we can pull everything
up one level and remove the BIND_EXPR, unless this is the
--- 1611,1625 ----
&& IS_EMPTY_STMT (TREE_OPERAND (*stmt_p, 0)))
{
*stmt_p = build_empty_stmt ();
! data->repeat = true;
}
}
else if (code == BIND_EXPR)
{
tree block;
+
/* First remove anything underneath the BIND_EXPR. */
! remove_useless_stmts_and_vars_1 (&BIND_EXPR_BODY (*stmt_p), data);
/* If the BIND_EXPR has no variables, then we can pull everything
up one level and remove the BIND_EXPR, unless this is the
*************** remove_useless_stmts_and_vars (tree *fir
*** 1620,1628 ****
!= FUNCTION_DECL)))
{
*stmt_p = BIND_EXPR_BODY (*stmt_p);
! repeat = 1;
}
! else if (remove_unused_vars)
{
/* If we were unable to completely eliminate the BIND_EXPR,
go ahead and prune out any unused variables. We do not
--- 1637,1645 ----
!= FUNCTION_DECL)))
{
*stmt_p = BIND_EXPR_BODY (*stmt_p);
! data->repeat = true;
}
! else if (data->remove_unused_vars)
{
/* If we were unable to completely eliminate the BIND_EXPR,
go ahead and prune out any unused variables. We do not
*************** remove_useless_stmts_and_vars (tree *fir
*** 1684,1738 ****
!= FUNCTION_DECL)))
{
*stmt_p = BIND_EXPR_BODY (*stmt_p);
! repeat = 1;
}
}
}
else if (code == GOTO_EXPR)
{
! tree_stmt_iterator tsi = i;
!
! /* Step past the GOTO_EXPR statement. */
! tsi_next (&tsi);
! if (! tsi_end_p (tsi))
! {
! /* If we are not at the end of this tree, then see if
! we are at the target label. If so, then this jump
! is not needed. */
! tree label;
!
! label = tsi_stmt (tsi);
! if (TREE_CODE (label) == LABEL_EXPR
! && LABEL_EXPR_LABEL (label) == GOTO_DESTINATION (*stmt_p))
! {
! repeat = 1;
! *stmt_p = build_empty_stmt ();
! }
! }
! else
{
! /* We are at the end of this tree, we may still have
! an unnecessary GOTO_EXPR if NEXT_BLOCK_LINK
! points to the target label. */
! tree next_block_link = NEXT_BLOCK_LINK (*stmt_p);
!
! if (next_block_link)
! {
! tree next_stmt;
!
! /* Get the statement at NEXT_BLOCK_LINK and see if it
! is our target label. */
! next_stmt = tsi_stmt (tsi_start (&next_block_link));
! if (next_stmt
! && TREE_CODE (next_stmt) == LABEL_EXPR
! && (LABEL_EXPR_LABEL (next_stmt)
! == GOTO_DESTINATION (*stmt_p)))
! {
! repeat = 1;
! *stmt_p = build_empty_stmt ();
! }
! }
!
}
}
--- 1701,1723 ----
!= FUNCTION_DECL)))
{
*stmt_p = BIND_EXPR_BODY (*stmt_p);
! data->repeat = true;
}
}
}
else if (code == GOTO_EXPR)
{
! data->last_goto = stmt_p;
! }
! else if (code == LABEL_EXPR)
! {
! if (data->last_goto
! && (GOTO_DESTINATION (*data->last_goto)
! == LABEL_EXPR_LABEL (*stmt_p)))
{
! *data->last_goto = build_empty_stmt ();
! data->last_goto = NULL;
! data->repeat = true;
}
}
*************** remove_useless_stmts_and_vars (tree *fir
*** 1740,1748 ****
re-rationalize COMPOUND_EXPRs. */
if (TREE_CODE (*container_p) == COMPOUND_EXPR
&& TREE_CODE (TREE_OPERAND (*container_p, 0)) == COMPOUND_EXPR)
! *container_p = rationalize_compound_expr (*container_p);
}
! return repeat;
}
/* Delete all unreachable basic blocks. Return true if any unreachable
--- 1725,1754 ----
re-rationalize COMPOUND_EXPRs. */
if (TREE_CODE (*container_p) == COMPOUND_EXPR
&& TREE_CODE (TREE_OPERAND (*container_p, 0)) == COMPOUND_EXPR)
! {
! *container_p = rationalize_compound_expr (*container_p);
! /* Re-rationalization invalidates internal pointers. */
! data->repeat |= !!data->last_goto;
! data->last_goto = NULL;
! }
}
! }
!
! void
! remove_useless_stmts_and_vars (tree *first_p, bool remove_unused_vars)
! {
! struct rusv_data data;
!
! do
! {
! data.last_goto = NULL;
! data.repeat = false;
! data.remove_unused_vars = remove_unused_vars;
! remove_unused_vars = false;
!
! remove_useless_stmts_and_vars_1 (first_p, &data);
! }
! while (data.repeat);
}
/* Delete all unreachable basic blocks. Return true if any unreachable
Index: tree-dump.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-dump.c,v
retrieving revision 1.6.2.37
diff -c -p -d -r1.6.2.37 tree-dump.c
*** tree-dump.c 11 Sep 2003 22:02:13 -0000 1.6.2.37
--- tree-dump.c 17 Sep 2003 21:54:27 -0000
*************** static struct dump_file_info dump_files[
*** 651,656 ****
--- 651,657 ----
{".generic", "tree-generic", 0, 0},
{".inlined", "tree-inlined", 0, 0},
{".gimple", "tree-gimple", 0, 0},
+ {".useless", "tree-useless", 0, 0},
{".cfg", "tree-cfg", 0, 0},
{".dot", "tree-dot", 0, 0},
{".pta", "tree-pta", 0, 0},
Index: tree-flow.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-flow.h,v
retrieving revision 1.1.4.115
diff -c -p -d -r1.1.4.115 tree-flow.h
*** tree-flow.h 16 Sep 2003 03:08:08 -0000 1.1.4.115
--- tree-flow.h 17 Sep 2003 21:54:27 -0000
*************** extern tree last_stmt (basic_block);
*** 429,435 ****
extern tree *last_stmt_ptr (basic_block);
extern edge find_taken_edge (basic_block, tree);
extern int call_expr_flags (tree);
! extern int remove_useless_stmts_and_vars (tree *, int);
extern int could_trap_p (tree);
extern basic_block tree_split_edge (edge);
extern void bsi_move_before (block_stmt_iterator, block_stmt_iterator);
--- 429,435 ----
extern tree *last_stmt_ptr (basic_block);
extern edge find_taken_edge (basic_block, tree);
extern int call_expr_flags (tree);
! extern void remove_useless_stmts_and_vars (tree *, bool);
extern int could_trap_p (tree);
extern basic_block tree_split_edge (edge);
extern void bsi_move_before (block_stmt_iterator, block_stmt_iterator);
Index: tree-optimize.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-optimize.c,v
retrieving revision 1.1.4.46
diff -c -p -d -r1.1.4.46 tree-optimize.c
*** tree-optimize.c 12 Sep 2003 16:15:52 -0000 1.1.4.46
--- tree-optimize.c 17 Sep 2003 21:54:27 -0000
*************** Boston, MA 02111-1307, USA. */
*** 54,75 ****
void
optimize_function_tree (tree fndecl)
{
- tree fnbody;
-
/* Don't bother doing anything if the program has errors. */
if (errorcount || sorrycount)
return;
- fnbody = DECL_SAVED_TREE (fndecl);
-
/* Build the flowgraph. */
init_flow ();
/* Run a pass over the statements deleting any obviously useless
statements before we build the CFG. */
! remove_useless_stmts_and_vars (&DECL_SAVED_TREE (fndecl), 0);
! build_tree_cfg (fnbody);
/* Begin analysis and optimization passes. */
if (n_basic_blocks > 0 && ! (errorcount || sorrycount))
--- 54,80 ----
void
optimize_function_tree (tree fndecl)
{
/* Don't bother doing anything if the program has errors. */
if (errorcount || sorrycount)
return;
/* Build the flowgraph. */
init_flow ();
/* Run a pass over the statements deleting any obviously useless
statements before we build the CFG. */
! remove_useless_stmts_and_vars (&DECL_SAVED_TREE (fndecl), false);
! {
! int flags;
! FILE *file = dump_begin (TDI_useless, &flags);
! if (file)
! {
! dump_function_to_file (current_function_decl, file, flags);
! dump_end (TDI_useless, file);
! }
! }
! build_tree_cfg (DECL_SAVED_TREE (fndecl));
/* Begin analysis and optimization passes. */
if (n_basic_blocks > 0 && ! (errorcount || sorrycount))
Index: tree-ssa.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-ssa.c,v
retrieving revision 1.1.4.125
diff -c -p -d -r1.1.4.125 tree-ssa.c
*** tree-ssa.c 13 Sep 2003 02:08:59 -0000 1.1.4.125
--- tree-ssa.c 17 Sep 2003 21:54:27 -0000
*************** rewrite_out_of_ssa (tree fndecl)
*** 1638,1644 ****
var_map map;
tree phi, next;
elim_graph g;
- int repeat, first_iteration;
tree_live_info_p liveinfo;
timevar_push (TV_TREE_SSA_TO_NORMAL);
--- 1638,1643 ----
*************** rewrite_out_of_ssa (tree fndecl)
*** 1763,1777 ****
/* Do some cleanups which reduce the amount of data the
tree->rtl expanders deal with. */
! first_iteration = 1;
! do
! {
! repeat = remove_useless_stmts_and_vars (&DECL_SAVED_TREE (fndecl),
! first_iteration);
! first_iteration = 0;
! }
! while (repeat);
!
/* Flush out flow graph and SSA data. */
delete_tree_ssa (fndecl);
delete_tree_cfg ();
--- 1762,1769 ----
/* Do some cleanups which reduce the amount of data the
tree->rtl expanders deal with. */
! remove_useless_stmts_and_vars (&DECL_SAVED_TREE (fndecl), true);
!
/* Flush out flow graph and SSA data. */
delete_tree_ssa (fndecl);
delete_tree_cfg ();
Index: tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.h,v
retrieving revision 1.342.2.97
diff -c -p -d -r1.342.2.97 tree.h
*** tree.h 11 Sep 2003 22:02:12 -0000 1.342.2.97
--- tree.h 17 Sep 2003 21:54:28 -0000
*************** enum tree_dump_index
*** 3435,3440 ****
--- 3435,3441 ----
TDI_inlined, /* dump each function after inlining
within it. */
TDI_gimple, /* dump each function after gimplifying it. */
+ TDI_useless, /* dump after cleaning useless bits. */
TDI_cfg, /* dump the flowgraph for each function. */
TDI_dot, /* create a dot graph file for each
function's flowgraph. */