Makefile.in (OBJS): Added dce.o.

* Makefile.in (OBJS): Added dce.o.
	(ssa.o): Updated target to include ssa.h.
	(flow.o): Likewise.
	(toplev.o): Likewise.
	(dce.o): Created target.
	* basic-block.h: Added comments.
	(INVALID_BLOCK): Added definition.
	(connect_infinite_loops_to_exit): Added declaration.
	Moved SSA declarations to ssa.h.
	* flow.c: Added inclusion of ssa.h.
	(struct depth_first_search_dsS, depth_first_search_ds):
	Added definitions.
	(compute_immediate_postdominators): Added definition.
	(connect_infinite_loops_to_exit): Likewise.
	(flow_dfs_compute_reverse_init): Likewise.
	(flow_dfs_compute_reverse_add_bb): Likewise.
	(flow_dfs_compute_reverse_execute): Likewise.
	(flow_dfs_compute_reverse_finish): Likewise.
	* rtl.h (rtx/in_struct): Added use to determine insn necessity.
	(LABEL_P): Added definition.
	(JUMP_P): Likewise.
	(NOTE_P): Likewise.
	(BARRIER_P): Likewise.
	(JUMP_TABLE_DATA_P): Likewise.
	(INSN_DEAD_CODE_P): Likewise.
	* ssa.c: Replaced inclusions with ssa.h inclusion.
	(CONVERT_HARD_REGISTER_TO_SSA_P): Moved to ssa.h.
	(rename_registers): Removed unnecessary variables.
	* ssa.h: Created by moving declarations from ssa.c and
	basic-block.h.
	* timevar.def: Defined TV_DEAD_CODE_ELIM.
	* toplev.c: Added ssa.h inclusion.
	(dump_file_index): Added DFI_dce.
	(dump_file): Added "dce" entry.
	Defined flag_ssa.
	(f_options): Added dce entry.
	* invoke.texi: Document -fdce.  Emphasize experimental status of
	-fssa.

Co-Authored-By: Mark Mitchell <mark@codesourcery.com>

From-SVN: r35419
This commit is contained in:
Jeffrey Oldham 2000-08-02 04:21:27 +00:00 committed by Mark Mitchell
parent 79c2c6da2c
commit b53978a3ee
11 changed files with 990 additions and 65 deletions

View file

@ -1,3 +1,45 @@
2000-08-01 Jeffrey Oldham <oldham@codesourcery.com>
Mark Mitchell <mark@codesourcery.com>
* Makefile.in (OBJS): Added dce.o.
(ssa.o): Updated target to include ssa.h.
(flow.o): Likewise.
(toplev.o): Likewise.
(dce.o): Created target.
* basic-block.h: Added comments.
(INVALID_BLOCK): Added definition.
(connect_infinite_loops_to_exit): Added declaration.
Moved SSA declarations to ssa.h.
* flow.c: Added inclusion of ssa.h.
(struct depth_first_search_dsS, depth_first_search_ds):
Added definitions.
(compute_immediate_postdominators): Added definition.
(connect_infinite_loops_to_exit): Likewise.
(flow_dfs_compute_reverse_init): Likewise.
(flow_dfs_compute_reverse_add_bb): Likewise.
(flow_dfs_compute_reverse_execute): Likewise.
(flow_dfs_compute_reverse_finish): Likewise.
* rtl.h (rtx/in_struct): Added use to determine insn necessity.
(LABEL_P): Added definition.
(JUMP_P): Likewise.
(NOTE_P): Likewise.
(BARRIER_P): Likewise.
(JUMP_TABLE_DATA_P): Likewise.
(INSN_DEAD_CODE_P): Likewise.
* ssa.c: Replaced inclusions with ssa.h inclusion.
(CONVERT_HARD_REGISTER_TO_SSA_P): Moved to ssa.h.
(rename_registers): Removed unnecessary variables.
* ssa.h: Created by moving declarations from ssa.c and
basic-block.h.
* timevar.def: Defined TV_DEAD_CODE_ELIM.
* toplev.c: Added ssa.h inclusion.
(dump_file_index): Added DFI_dce.
(dump_file): Added "dce" entry.
Defined flag_ssa.
(f_options): Added dce entry.
* invoke.texi: Document -fdce. Emphasize experimental status of
-fssa.
2000-08-01 Zack Weinberg <zack@wolery.cumb.org>
* cpperror.c (v_message): Split into _cpp_begin_message and

View file

@ -696,7 +696,7 @@ OBJS = diagnostic.o \
profile.o insn-attrtab.o $(out_object_file) $(EXTRA_OBJS) convert.o \
mbchar.o splay-tree.o graph.o sbitmap.o resource.o hash.o predict.o \
lists.o ggc-common.o $(GGC) simplify-rtx.o ssa.o bb-reorder.o \
sibcall.o conflict.o timevar.o ifcvt.o
sibcall.o conflict.o timevar.o ifcvt.o dce.o
# GEN files are listed separately, so they can be built before doing parallel
# makes for cc1 or cc1plus. Otherwise sequent parallel make attempts to load
@ -1242,7 +1242,7 @@ toplev.o : toplev.c $(CONFIG_H) system.h $(TREE_H) $(RTL_H) function.h \
flags.h input.h $(INSN_ATTR_H) xcoffout.h defaults.h output.h diagnostic.h \
insn-codes.h insn-config.h intl.h $(RECOG_H) Makefile toplev.h dwarfout.h \
dwarf2out.h sdbout.h dbxout.h $(EXPR_H) hard-reg-set.h $(BASIC_BLOCK_H) \
graph.h loop.h except.h regs.h $(TIMEVAR_H) $(lang_options_files)
graph.h loop.h except.h regs.h $(TIMEVAR_H) $(lang_options_files) ssa.h
$(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $(MAYBE_USE_COLLECT2) \
-DTARGET_NAME=\"$(target_alias)\" \
-c `echo $(srcdir)/toplev.c | sed 's,^\./,,'`
@ -1326,10 +1326,11 @@ resource.o : resource.c $(CONFIG_H) $(RTL_H) hard-reg-set.h system.h \
$(INSN_ATTR_H) except.h
lcm.o : lcm.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) hard-reg-set.h flags.h \
real.h insn-config.h $(INSN_ATTR_H) $(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H)
ssa.o : ssa.c $(CONFIG_H) system.h $(RTL_H) varray.h sbitmap.h \
$(HASHTAB_H) $(REGS_H) hard-reg-set.h flags.h function.h real.h \
insn-config.h $(RECOG_H) $(BASIC_BLOCK_H) \
output.h
ssa.o : ssa.c $(CONFIG_H) system.h $(REGS_H) varray.h \
hard-reg-set.h flags.h function.h real.h insn-config.h $(RECOG_H) \
$(BASIC_BLOCK_H) output.h ssa.h
dce.o : dce.c $(CONFIG_H) system.h $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H) \
ssa.h insn-config.h $(RECOG_H) output.h
conflict.o : conflict.c $(CONFIG_H) system.h $(OBSTACK_H) $(HASHTAB_H) \
$(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H)
profile.o : profile.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h \
@ -1345,7 +1346,7 @@ unroll.o : unroll.c $(CONFIG_H) system.h $(RTL_H) insn-config.h function.h \
hard-reg-set.h varray.h $(BASIC_BLOCK_H)
flow.o : flow.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h insn-config.h \
$(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h $(RECOG_H) \
insn-flags.h function.h except.h $(EXPR_H)
insn-flags.h function.h except.h $(EXPR_H) ssa.h
combine.o : combine.c $(CONFIG_H) system.h $(RTL_H) flags.h function.h \
insn-config.h insn-flags.h insn-codes.h $(INSN_ATTR_H) $(REGS_H) $(EXPR_H) \
$(BASIC_BLOCK_H) $(RECOG_H) real.h hard-reg-set.h toplev.h

View file

@ -141,6 +141,18 @@ typedef struct edge_def {
#define EDGE_COMPLEX (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_EH)
/* Basic blocks need not start with a label nor end with a jump insn.
For example, a previous basic block may just "conditionally fall"
into the succeeding basic block, and the last basic block need not
end with a jump insn. Block 0 is a descendant of the entry block.
A basic block beginning with two labels cannot have notes between
the labels.
Data for jump tables are stored in jump_insns that occur in no
basic block even though these insns can follow or precede insns in
basic blocks. */
/* Basic block information indexed by block number. */
typedef struct basic_block_def {
/* The first and last insns of the block. */
@ -210,6 +222,9 @@ extern regset regs_live_at_setjmp;
#define ENTRY_BLOCK (-1)
#define EXIT_BLOCK (-2)
/* Special block number not valid for any block. */
#define INVALID_BLOCK (-3)
/* Similarly, block pointers for the edge list. */
extern struct basic_block_def entry_exit_blocks[2];
#define ENTRY_BLOCK_PTR (&entry_exit_blocks[0])
@ -230,6 +245,7 @@ extern void insert_insn_on_edge PARAMS ((rtx, edge));
extern void commit_edge_insertions PARAMS ((void));
extern void remove_fake_edges PARAMS ((void));
extern void add_noreturn_fake_exit_edges PARAMS ((void));
extern void connect_infinite_loops_to_exit PARAMS ((void));
extern rtx flow_delete_insn PARAMS ((rtx));
extern void flow_delete_insn_chain PARAMS ((rtx, rtx));
extern void make_edge PARAMS ((sbitmap *, basic_block,
@ -514,13 +530,4 @@ extern conflict_graph conflict_graph_compute
PARAMS ((regset,
partition));
/* In ssa.c */
extern void convert_to_ssa PARAMS ((void));
extern void convert_from_ssa PARAMS ((void));
typedef int (*successor_phi_fn) PARAMS ((rtx, int, int, void *));
extern int for_each_successor_phi PARAMS ((basic_block bb,
successor_phi_fn,
void *));
extern int in_ssa_form;
#endif /* _BASIC_BLOCK_H */

620
gcc/dce.c Normal file
View file

@ -0,0 +1,620 @@
/* Dead-code elimination pass for the GNU compiler.
Copyright (C) 2000 Free Software Foundation, Inc.
Written by Jeffrey D. Oldham <oldham@codesourcery.com>.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
GNU CC is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
/* Dead-code elimination is the removal of instructions which have no
impact on the program's output. "Dead instructions" have no impact
on the program's output, while "necessary instructions" may have
impact on the output.
The algorithm consists of three phases:
1) marking as necessary all instructions known to be necessary,
e.g., writing a value to memory,
2) propagating necessary instructions, e.g., the instructions
giving values to operands in necessary instructions, and
3) removing dead instructions (except replacing dead conditionals
with unconditional jumps).
Side Effects:
The last step can require adding labels, deleting insns, and
modifying basic block structures. Some conditional jumps may be
converted to unconditional jumps so the control-flow graph may be
out-of-date.
Edges from some infinite loops to the exit block can be added to
the control-flow graph.
It Does Not Perform:
We decided to not simultaneously perform jump optimization and dead
loop removal during dead-code elimination. Thus, all jump
instructions originally present remain after dead-code elimination
but 1) unnecessary conditional jump instructions are changed to
unconditional jump instructions and 2) all unconditional jump
instructions remain.
Assumptions:
1) SSA has been performed.
2) The basic block and control-flow graph structures are accurate.
3) The flow graph permits constructing an edge_list.
4) note rtxes should be saved.
Unfinished:
When replacing unnecessary conditional jumps with unconditional
jumps, the control-flow graph is not updated. It should be.
References:
Building an Optimizing Compiler
Robert Morgan
Butterworth-Heinemann, 1998
Section 8.9
*/
#include "config.h"
#include "system.h"
#include "rtl.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "ssa.h"
#include "insn-config.h"
#include "recog.h"
#include "output.h"
/* We cannot use <assert.h> in GCC source, since that would include
GCC's assert.h, which may not be compatible with the host compiler. */
#undef assert
#ifdef NDEBUG
# define assert(e)
#else
# define assert(e) do { if (! (e)) abort (); } while (0)
#endif
/* A map from blocks to the edges on which they are control dependent. */
typedef struct {
/* An dynamically allocated array. The Nth element corresponds to
the block with index N + 2. The Ith bit in the bitmap is set if
that block is dependent on the Ith edge. */
bitmap *data;
/* The number of elements in the array. */
int length;
} control_dependent_block_to_edge_map_s, *control_dependent_block_to_edge_map;
/* Local function prototypes. */
static control_dependent_block_to_edge_map control_dependent_block_to_edge_map_create
PARAMS((size_t num_basic_blocks));
static void set_control_dependent_block_to_edge_map_bit
PARAMS ((control_dependent_block_to_edge_map c, basic_block bb,
int edge_index));
static void control_dependent_block_to_edge_map_free
PARAMS ((control_dependent_block_to_edge_map c));
static void find_all_control_dependences
PARAMS ((struct edge_list *el, int *pdom,
control_dependent_block_to_edge_map cdbte));
static void find_control_dependence
PARAMS ((struct edge_list *el, int edge_index, int *pdom,
control_dependent_block_to_edge_map cdbte));
static basic_block find_pdom
PARAMS ((int *pdom, basic_block block));
static int inherently_necessary_register_1
PARAMS ((rtx *current_rtx, void *data));
static int inherently_necessary_register
PARAMS ((rtx current_rtx));
static int find_inherently_necessary
PARAMS ((rtx current_rtx));
static int propagate_necessity_through_operand
PARAMS ((rtx *current_rtx, void *data));
/* Unnecessary insns are indicated using insns' in_struct bit. */
/* Indicate INSN is dead-code; returns nothing. */
#define KILL_INSN(INSN) INSN_DEAD_CODE_P(INSN) = 1
/* Indicate INSN is necessary, i.e., not dead-code; returns nothing. */
#define RESURRECT_INSN(INSN) INSN_DEAD_CODE_P(INSN) = 0
/* Return nonzero if INSN is unnecessary. */
#define UNNECESSARY_P(INSN) INSN_DEAD_CODE_P(INSN)
static void mark_all_insn_unnecessary
PARAMS ((void));
/* Execute CODE with free variable INSN for all unnecessary insns in
an unspecified order, producing no output. */
#define EXECUTE_IF_UNNECESSARY(INSN, CODE) \
{ \
rtx INSN; \
\
for (INSN = get_insns (); INSN != NULL_RTX; INSN = NEXT_INSN (INSN)) \
if (INSN_DEAD_CODE_P (INSN)) { \
CODE; \
} \
}
/* Find the label beginning block BB. */
static rtx find_block_label
PARAMS ((basic_block bb));
/* Remove INSN, updating its basic block structure. */
static void delete_insn_bb
PARAMS ((rtx insn));
/* Recording which blocks are control dependent on which edges. We
expect each block to be control dependent on very few edges so we
use a bitmap for each block recording its edges. An array holds
the bitmap. Its position 0 entry holds the bitmap for block
INVALID_BLOCK+1 so that all blocks, including the entry and exit
blocks can participate in the data structure. */
/* Create a control_dependent_block_to_edge_map, given the number
NUM_BASIC_BLOCKS of non-entry, non-exit basic blocks, e.g.,
n_basic_blocks. This memory must be released using
control_dependent_block_to_edge_map_free (). */
static control_dependent_block_to_edge_map
control_dependent_block_to_edge_map_create (num_basic_blocks)
size_t num_basic_blocks;
{
int i;
control_dependent_block_to_edge_map c
= xmalloc (sizeof (control_dependent_block_to_edge_map_s));
c->length = num_basic_blocks - (INVALID_BLOCK+1);
c->data = xmalloc ((size_t) c->length*sizeof (bitmap));
for (i = 0; i < c->length; ++i)
c->data[i] = BITMAP_XMALLOC ();
return c;
}
/* Indicate block BB is control dependent on an edge with index
EDGE_INDEX in the mapping C of blocks to edges on which they are
control-dependent. */
static void
set_control_dependent_block_to_edge_map_bit (c, bb, edge_index)
control_dependent_block_to_edge_map c;
basic_block bb;
int edge_index;
{
assert(bb->index - (INVALID_BLOCK+1) < c->length);
bitmap_set_bit (c->data[bb->index - (INVALID_BLOCK+1)],
edge_index);
}
/* Execute CODE for each edge (given number EDGE_NUMBER within the
CODE) for which the block containing INSN is control dependent,
returning no output. CDBTE is the mapping of blocks to edges on
which they are control-dependent. */
#define EXECUTE_IF_CONTROL_DEPENDENT(CDBTE, INSN, EDGE_NUMBER, CODE) \
EXECUTE_IF_SET_IN_BITMAP \
(CDBTE->data[BLOCK_NUM (INSN) - (INVALID_BLOCK+1)], 0, \
EDGE_NUMBER, CODE)
/* Destroy a control_dependent_block_to_edge_map C. */
static void
control_dependent_block_to_edge_map_free (c)
control_dependent_block_to_edge_map c;
{
int i;
for (i = 0; i < c->length; ++i)
BITMAP_XFREE (c->data[i]);
free ((PTR) c);
}
/* Record all blocks' control dependences on all edges in the edge
list EL, ala Morgan, Section 3.6. The mapping PDOM of blocks to
their postdominators are used, and results are stored in CDBTE,
which should be empty. */
static void
find_all_control_dependences (el, pdom, cdbte)
struct edge_list *el;
int *pdom;
control_dependent_block_to_edge_map cdbte;
{
int i;
for (i = 0; i < NUM_EDGES (el); ++i)
find_control_dependence (el, i, pdom, cdbte);
}
/* Determine all blocks' control dependences on the given edge with
edge_list EL index EDGE_INDEX, ala Morgan, Section 3.6. The
mapping PDOM of blocks to their postdominators are used, and
results are stored in CDBTE, which is assumed to be initialized
with zeros in each (block b', edge) position. */
static void
find_control_dependence (el, edge_index, pdom, cdbte)
struct edge_list *el;
int edge_index;
int *pdom;
control_dependent_block_to_edge_map cdbte;
{
basic_block current_block;
basic_block ending_block;
assert (INDEX_EDGE_PRED_BB (el, edge_index) != EXIT_BLOCK_PTR);
ending_block =
(INDEX_EDGE_PRED_BB (el, edge_index) == ENTRY_BLOCK_PTR)
? BASIC_BLOCK (0)
: find_pdom (pdom, INDEX_EDGE_PRED_BB (el, edge_index));
for (current_block = INDEX_EDGE_SUCC_BB (el, edge_index);
current_block != ending_block && current_block != EXIT_BLOCK_PTR;
current_block = find_pdom (pdom, current_block))
{
set_control_dependent_block_to_edge_map_bit (cdbte,
current_block,
edge_index);
}
}
/* Find the immediate postdominator PDOM of the specified basic block
BLOCK. This function is necessary because some blocks have
negative numbers. */
static basic_block
find_pdom (pdom, block)
int *pdom;
basic_block block;
{
assert (block != NULL);
assert (block->index != INVALID_BLOCK);
if (block == ENTRY_BLOCK_PTR)
return BASIC_BLOCK (0);
else if (block == EXIT_BLOCK_PTR || pdom[block->index] == EXIT_BLOCK)
return EXIT_BLOCK_PTR;
else
return BASIC_BLOCK (pdom[block->index]);
}
/* Determine if the given CURRENT_RTX uses a hard register not
converted to SSA. Returns nonzero only if it uses such a hard
register. DATA is not used.
The program counter (PC) is not considered inherently necessary
since code should be position-independent and thus not depend on
particular PC values. */
static int
inherently_necessary_register_1 (current_rtx, data)
rtx *current_rtx;
void *data ATTRIBUTE_UNUSED;
{
rtx x = *current_rtx;
if (x == NULL_RTX)
return 0;
switch (GET_CODE (x))
{
case CLOBBER:
/* Do not traverse the rest of the clobber. */
return -1;
break;
case PC:
return 0;
break;
case REG:
if (CONVERT_REGISTER_TO_SSA_P (REGNO (x)) || x == pc_rtx)
return 0;
else
return !0;
break;
default:
return 0;
break;
}
}
/* Return nonzero if the insn CURRENT_RTX is inherently necessary. */
static int
inherently_necessary_register (current_rtx)
rtx current_rtx;
{
return for_each_rtx (&current_rtx,
&inherently_necessary_register_1, NULL);
}
/* Mark X as inherently necessary if appropriate. For example,
function calls and storing values into memory are inherently
necessary. This function is to be used with for_each_rtx ().
Return nonzero iff inherently necessary. */
static int
find_inherently_necessary (x)
rtx x;
{
rtx pattern;
if (x == NULL_RTX)
return 0;
else if (inherently_necessary_register (x))
return !0;
else
switch (GET_CODE (x))
{
case CALL_INSN:
case CODE_LABEL:
case NOTE:
case BARRIER:
return !0;
break;
case JUMP_INSN:
return JUMP_TABLE_DATA_P (x) || computed_jump_p (x) != 0;
break;
case INSN:
pattern = PATTERN (x);
switch (GET_CODE (pattern))
{
case SET:
case PRE_DEC:
case PRE_INC:
case POST_DEC:
case POST_INC:
return GET_CODE (SET_DEST (pattern)) == MEM;
case CALL:
case RETURN:
case USE:
case CLOBBER:
return !0;
break;
case ASM_INPUT:
/* We treat assembler instructions as inherently
necessary, and we hope that its operands do not need to
be propagated. */
return !0;
break;
default:
return 0;
}
default:
/* Found an impossible insn type. */
abort();
break;
}
}
/* Propagate necessity through REG and SUBREG operands of CURRENT_RTX.
This function is called with for_each_rtx () on necessary
instructions. The DATA must be a varray of unprocessed
instructions. */
static int
propagate_necessity_through_operand (current_rtx, data)
rtx *current_rtx;
void *data;
{
rtx x = *current_rtx;
varray_type *unprocessed_instructions = (varray_type *) data;
if (x == NULL_RTX)
return 0;
switch ( GET_CODE (x))
{
case REG:
if (CONVERT_REGISTER_TO_SSA_P (REGNO (x)))
{
rtx insn = VARRAY_RTX (ssa_definition, REGNO (x));
if (insn != NULL_RTX && UNNECESSARY_P (insn))
{
RESURRECT_INSN (insn);
VARRAY_PUSH_RTX (*unprocessed_instructions, insn);
}
}
return 0;
default:
return 0;
}
}
/* Indicate all insns initially assumed to be unnecessary. */
static void
mark_all_insn_unnecessary ()
{
rtx insn;
for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
KILL_INSN (insn);
}
/* Find the label beginning block BB, adding one if necessary. */
static rtx
find_block_label (bb)
basic_block bb;
{
rtx insn = bb->head;
if (LABEL_P (insn))
return insn;
else
{
rtx new_label = emit_label_before (gen_label_rtx (), insn);
if (insn == bb->head)
bb->head = new_label;
return new_label;
}
}
/* Remove INSN, updating its basic block structure. */
static void
delete_insn_bb (insn)
rtx insn;
{
basic_block bb;
assert (insn != NULL_RTX);
bb = BLOCK_FOR_INSN (insn);
assert (bb != 0);
if (bb->head == bb->end)
{
/* Delete the insn by converting it to a note. */
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
return;
}
else if (insn == bb->head)
bb->head = NEXT_INSN (insn);
else if (insn == bb->end)
bb->end = PREV_INSN (insn);
delete_insn (insn);
}
/* Perform the dead-code elimination. */
void
eliminate_dead_code ()
{
int i;
rtx insn;
/* Necessary instructions with operands to explore. */
varray_type unprocessed_instructions;
/* Map element (b,e) is nonzero if the block is control dependent on
edge. "cdbte" abbreviates control dependent block to edge. */
control_dependent_block_to_edge_map cdbte;
sbitmap *postdominators;
/* Element I is the immediate postdominator of block I. */
int *pdom;
struct edge_list *el;
int max_insn_uid = get_max_uid ();
/* Initialize the data structures. */
mark_all_insn_unnecessary ();
VARRAY_RTX_INIT (unprocessed_instructions, 64,
"unprocessed instructions");
cdbte = control_dependent_block_to_edge_map_create (n_basic_blocks);
/* Prepare for use of BLOCK_NUM (). */
connect_infinite_loops_to_exit ();
/* Be careful not to clear the added edges. */
compute_bb_for_insn (max_insn_uid);
/* Compute control dependence. */
postdominators = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
compute_flow_dominators (NULL, postdominators);
pdom = (int *) xmalloc (n_basic_blocks * sizeof (int));
for (i = 0; i < n_basic_blocks; ++i)
pdom[i] = INVALID_BLOCK;
compute_immediate_postdominators (pdom, postdominators);
/* Assume there is a path from each node to the exit block. */
for (i = 0; i < n_basic_blocks; ++i)
if (pdom[i] == INVALID_BLOCK)
pdom[i] = EXIT_BLOCK;
sbitmap_vector_free (postdominators);
el = create_edge_list();
find_all_control_dependences (el, pdom, cdbte);
/* Find inherently necessary instructions. */
for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
if (find_inherently_necessary (insn))
{
RESURRECT_INSN (insn);
VARRAY_PUSH_RTX (unprocessed_instructions, insn);
}
/* Propagate necessity using the operands of necessary instructions. */
while (VARRAY_ACTIVE_SIZE (unprocessed_instructions) > 0)
{
rtx current_instruction;
int edge_number;
current_instruction = VARRAY_TOP_RTX (unprocessed_instructions);
VARRAY_POP (unprocessed_instructions);
/* Make corresponding control dependent edges necessary. */
/* Assume the only JUMP_INSN is the block's last insn. It appears
that the last instruction of the program need not be a
JUMP_INSN. */
if (INSN_P (current_instruction)
&& !JUMP_TABLE_DATA_P (current_instruction))
{
/* Notes and labels contain no interesting operands. */
EXECUTE_IF_CONTROL_DEPENDENT
(cdbte, current_instruction, edge_number,
{
rtx jump_insn = (INDEX_EDGE_PRED_BB (el, edge_number))->end;
if (GET_CODE (jump_insn) == JUMP_INSN &&
UNNECESSARY_P (jump_insn)) {
RESURRECT_INSN (jump_insn);
VARRAY_PUSH_RTX (unprocessed_instructions, jump_insn);
}
});
/* Propagate through the operands. */
for_each_rtx (&current_instruction,
&propagate_necessity_through_operand,
(PTR) &unprocessed_instructions);
}
}
/* Remove the unnecessary instructions. */
EXECUTE_IF_UNNECESSARY (insn,
{
if (any_condjump_p (insn))
{
/* Convert unnecessary conditional insn to an unconditional
jump to immediate postdominator block. */
rtx old_label = JUMP_LABEL (insn);
int pdom_block_number =
find_pdom (pdom, BLOCK_FOR_INSN (insn))->index;
/* Prevent the conditional jump's label from being deleted so
we do not have to modify the basic block structure. */
++LABEL_NUSES (old_label);
if (pdom_block_number != EXIT_BLOCK
&& pdom_block_number != INVALID_BLOCK)
{
rtx lbl = find_block_label (BASIC_BLOCK (pdom_block_number));
rtx new_jump = emit_jump_insn_before (gen_jump (lbl), insn);
/* Let jump know that label is in use. */
JUMP_LABEL (new_jump) = lbl;
++LABEL_NUSES (lbl);
delete_insn_bb (insn);
/* A conditional branch is unnecessary if and only if any
block control-dependent on it is unnecessary. Thus,
any phi nodes in these unnecessary blocks are also
removed and these nodes need not be updated. */
/* A barrier must follow any unconditional jump. Barriers
are not in basic blocks so this must occur after
deleting the conditional jump. */
emit_barrier_after (new_jump);
}
else
/* The block drops off the end of the function and the
ending conditional jump is not needed. */
delete_insn_bb (insn);
}
else if (!JUMP_P (insn))
delete_insn_bb (insn);
});
/* Release allocated memory. */
for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
RESURRECT_INSN (insn);
assert (VARRAY_ACTIVE_SIZE(unprocessed_instructions) == 0);
VARRAY_FREE (unprocessed_instructions);
control_dependent_block_to_edge_map_free (cdbte);
free ((PTR) pdom);
free_edge_list (el);
}

View file

@ -136,6 +136,7 @@ Boston, MA 02111-1307, USA. */
#include "recog.h"
#include "insn-flags.h"
#include "expr.h"
#include "ssa.h"
#include "obstack.h"
#include "splay-tree.h"
@ -308,6 +309,20 @@ struct propagate_block_info
int flags;
};
/* Store the data structures necessary for depth-first search. */
struct depth_first_search_dsS {
/* stack for backtracking during the algorithm */
basic_block *stack;
/* number of edges in the stack. That is, positions 0, ..., sp-1
have edges. */
unsigned int sp;
/* record of basic blocks already seen by depth-first search */
sbitmap visited_blocks;
};
typedef struct depth_first_search_dsS *depth_first_search_ds;
/* Forward declarations */
static int count_basic_blocks PARAMS ((rtx));
static void find_basic_blocks_1 PARAMS ((rtx));
@ -398,6 +413,14 @@ static int flow_loop_nested_p PARAMS ((struct loop *, struct loop *));
static int flow_loop_exits_find PARAMS ((const sbitmap, edge **));
static int flow_loop_nodes_find PARAMS ((basic_block, basic_block, sbitmap));
static int flow_depth_first_order_compute PARAMS ((int *, int *));
static void flow_dfs_compute_reverse_init
PARAMS ((depth_first_search_ds));
static void flow_dfs_compute_reverse_add_bb
PARAMS ((depth_first_search_ds, basic_block));
static basic_block flow_dfs_compute_reverse_execute
PARAMS ((depth_first_search_ds));
static void flow_dfs_compute_reverse_finish
PARAMS ((depth_first_search_ds));
static basic_block flow_loop_pre_header_find PARAMS ((basic_block, const sbitmap *));
static void flow_loop_tree_node_add PARAMS ((struct loop *, struct loop *));
static void flow_loops_tree_build PARAMS ((struct loops *));
@ -3741,7 +3764,7 @@ init_propagate_block_info (bb, live, local_set, flags)
&& GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
{
rtx mem = SET_DEST (PATTERN (insn));
if (XEXP (mem, 0) == frame_pointer_rtx
|| (GET_CODE (XEXP (mem, 0)) == PLUS
&& XEXP (XEXP (mem, 0), 0) == frame_pointer_rtx
@ -6254,7 +6277,6 @@ compute_immediate_postdominators (idom, postdominators)
sbitmap *postdominators;
{
compute_immediate_dominators (idom, postdominators);
return;
}
/* Recompute register set/reference counts immediately prior to register
@ -6888,7 +6910,6 @@ verify_edge_list (f, elist)
/* This routine will determine what, if any, edge there is between
a specified predecessor and successor. */
int
find_edge_index (edge_list, pred, succ)
struct edge_list *edge_list;
@ -6984,8 +7005,44 @@ add_noreturn_fake_exit_edges ()
make_edge (NULL, BASIC_BLOCK (x), EXIT_BLOCK_PTR, EDGE_FAKE);
}
/* Redirect an edge's successor from one block to another. */
/* This function adds a fake edge between any infinite loops to the
exit block. Some optimizations require a path from each node to
the exit node.
See also Morgan, Figure 3.10, pp. 82-83.
The current implementation is ugly, not attempting to minimize the
number of inserted fake edges. To reduce the number of fake edges
to insert, add fake edges from _innermost_ loops containing only
nodes not reachable from the exit block. */
void
connect_infinite_loops_to_exit ()
{
basic_block unvisited_block;
/* Perform depth-first search in the reverse graph to find nodes
reachable from the exit block. */
struct depth_first_search_dsS dfs_ds;
flow_dfs_compute_reverse_init (&dfs_ds);
flow_dfs_compute_reverse_add_bb (&dfs_ds, EXIT_BLOCK_PTR);
/* Repeatedly add fake edges, updating the unreachable nodes. */
while (1)
{
unvisited_block = flow_dfs_compute_reverse_execute (&dfs_ds);
if (!unvisited_block)
break;
make_edge (NULL, unvisited_block, EXIT_BLOCK_PTR, EDGE_FAKE);
flow_dfs_compute_reverse_add_bb (&dfs_ds, unvisited_block);
}
flow_dfs_compute_reverse_finish (&dfs_ds);
return;
}
/* Redirect an edge's successor from one block to another. */
void
redirect_edge_succ (e, new_succ)
edge e;
@ -7005,7 +7062,6 @@ redirect_edge_succ (e, new_succ)
}
/* Redirect an edge's predecessor from one block to another. */
void
redirect_edge_pred (e, new_pred)
edge e;
@ -7427,6 +7483,115 @@ flow_depth_first_order_compute (dfs_order, rc_order)
}
/* Compute the depth first search order on the _reverse_ graph and
store in the array DFS_ORDER, marking the nodes visited in VISITED.
Returns the number of nodes visited.
The computation is split into three pieces:
flow_dfs_compute_reverse_init () creates the necessary data
structures.
flow_dfs_compute_reverse_add_bb () adds a basic block to the data
structures. The block will start the search.
flow_dfs_compute_reverse_execute () continues (or starts) the
search using the block on the top of the stack, stopping when the
stack is empty.
flow_dfs_compute_reverse_finish () destroys the necessary data
structures.
Thus, the user will probably call ..._init(), call ..._add_bb() to
add a beginning basic block to the stack, call ..._execute(),
possibly add another bb to the stack and again call ..._execute(),
..., and finally call _finish(). */
/* Initialize the data structures used for depth-first search on the
reverse graph. If INITIALIZE_STACK is nonzero, the exit block is
added to the basic block stack. DATA is the current depth-first
search context. If INITIALIZE_STACK is non-zero, there is an
element on the stack. */
static void
flow_dfs_compute_reverse_init (data)
depth_first_search_ds data;
{
/* Allocate stack for back-tracking up CFG. */
data->stack =
(basic_block *) xmalloc ((n_basic_blocks - (INVALID_BLOCK+1))
* sizeof (basic_block));
data->sp = 0;
/* Allocate bitmap to track nodes that have been visited. */
data->visited_blocks
= sbitmap_alloc (n_basic_blocks - (INVALID_BLOCK + 1));
/* None of the nodes in the CFG have been visited yet. */
sbitmap_zero (data->visited_blocks);
return;
}
/* Add the specified basic block to the top of the dfs data
structures. When the search continues, it will start at the
block. */
static void
flow_dfs_compute_reverse_add_bb (data, bb)
depth_first_search_ds data;
basic_block bb;
{
data->stack[data->sp++] = bb;
return;
}
/* Continue the depth-first search through the reverse graph starting
with the block at the stack's top and ending when the stack is
empty. Visited nodes are marked. Returns an unvisited basic
block, or NULL if there is none available. */
static basic_block
flow_dfs_compute_reverse_execute (data)
depth_first_search_ds data;
{
basic_block bb;
edge e;
int i;
while (data->sp > 0)
{
bb = data->stack[--data->sp];
/* Mark that we have visited this node. */
if (!TEST_BIT (data->visited_blocks, bb->index - (INVALID_BLOCK+1)))
{
SET_BIT (data->visited_blocks, bb->index - (INVALID_BLOCK+1));
/* Perform depth-first search on adjacent vertices. */
for (e = bb->pred; e; e = e->pred_next)
flow_dfs_compute_reverse_add_bb (data, e->src);
}
}
/* Determine if there are unvisited basic blocks. */
for (i = n_basic_blocks - (INVALID_BLOCK+1); --i >= 0; )
if (!TEST_BIT (data->visited_blocks, i))
return BASIC_BLOCK (i + (INVALID_BLOCK+1));
return NULL;
}
/* Destroy the data structures needed for depth-first search on the
reverse graph. */
static void
flow_dfs_compute_reverse_finish (data)
depth_first_search_ds data;
{
free (data->stack);
sbitmap_free (data->visited_blocks);
return;
}
/* Return the block for the pre-header of the loop with header
HEADER where DOM specifies the dominator information. Return NULL if
there is no pre-header. */

View file

@ -168,7 +168,7 @@ in the following sections.
-falign-functions=@var{n} -falign-labels=@var{n} -falign-loops=@var{n}
-falign-jumps=@var{n} -fbranch-probabilities
-fcaller-saves -fcse-follow-jumps -fcse-skip-blocks
-fdelayed-branch -fdelete-null-pointer-checks -fexpensive-optimizations
-fdce -fdelayed-branch -fdelete-null-pointer-checks -fexpensive-optimizations
-ffast-math -ffloat-store -fforce-addr -fforce-mem -fno-math-errno
-fdata-sections -ffunction-sections -fgcse
-finline-functions -finline-limit=@var{n} -fkeep-inline-functions
@ -2900,9 +2900,12 @@ If @var{n} is not specified, use a machine-dependent default.
@item -fssa
Perform optimizations in static single assignment form. Each function's
flow graph is translated into SSA form, optimizations are performed, and
the flow graph is translated back from SSA form. (Currently, no
SSA-based optimizations are implemented, but converting into and out of
SSA form is not an invariant operation, and generated code may differ.)
the flow graph is translated back from SSA form. User's should not
specify this option, since it is not yet ready for production use.
@item -fdce
Perform dead-code elimination in SSA form. Requires @samp{-fssa}. Like
@samp{-fssa}, this is an experimental feature.
@item -fsingle-precision-constant
Treat floating point constant as single precision constant instead of

View file

@ -148,7 +148,9 @@ typedef struct rtx_def
together with the preceding insn. Valid only within sched.
1 in an INSN, JUMP_INSN, or CALL_INSN if insn is in a delay slot and
from the target of a branch. Valid from reorg until end of compilation;
cleared before used. */
cleared before used.
1 in an INSN if this insn is dead code. Valid only during
dead-code elimination phase; cleared before use. */
unsigned int in_struct : 1;
/* 1 if this rtx is used. This is used for copying shared structure.
See `unshare_all_rtl'.
@ -202,10 +204,26 @@ typedef struct rtvec_def{
#define GET_NUM_ELEM(RTVEC) ((RTVEC)->num_elem)
#define PUT_NUM_ELEM(RTVEC, NUM) ((RTVEC)->num_elem = (NUM))
/* 1 if X is a REG. */
/* Predicate yielding nonzero iff X is an rtl for a register. */
#define REG_P(X) (GET_CODE (X) == REG)
/* Predicate yielding nonzero iff X is a label insn. */
#define LABEL_P(X) (GET_CODE (X) == CODE_LABEL)
/* Predicate yielding nonzero iff X is a jump insn. */
#define JUMP_P(X) (GET_CODE (X) == JUMP_INSN)
/* Predicate yielding nonzero iff X is a note insn. */
#define NOTE_P(X) (GET_CODE (X) == NOTE)
/* Predicate yielding nonzero iff X is a barrier insn. */
#define BARRIER_P(X) (GET_CODE (X) == BARRIER)
/* Predicate yielding nonzero iff X is a data for a jump table. */
#define JUMP_TABLE_DATA_P(INSN) \
(JUMP_P (INSN) && (GET_CODE (PATTERN (INSN)) == ADDR_VEC || \
GET_CODE (PATTERN (INSN)) == ADDR_DIFF_VEC))
/* 1 if X is a constant value that is an integer. */
#define CONSTANT_P(X) \
@ -374,6 +392,9 @@ extern void rtvec_check_failed_bounds PARAMS ((rtvec, int,
delay slots, i.e., it is an annulled branch. */
#define INSN_ANNULLED_BRANCH_P(INSN) ((INSN)->unchanging)
/* 1 if insn is a dead code. Valid only for dead-code elimination phase. */
#define INSN_DEAD_CODE_P(INSN) ((INSN)->in_struct)
/* 1 if insn is in a delay slot and is from the target of the branch. If
the branch insn has INSN_ANNULLED_BRANCH_P set, this insn should only be
executed if the branch is taken. For annulled branches with this bit

View file

@ -46,6 +46,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "recog.h"
#include "basic-block.h"
#include "output.h"
#include "ssa.h"
/* We cannot use <assert.h> in GCC source, since that would include
GCC's assert.h, which may not be compatible with the host compiler. */
@ -91,24 +92,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
left in for a limited time only, as a debugging tool until the
coalescing algorithm is validated. */
/* All pseudo-registers (having register number >=
FIRST_PSEUDO_REGISTER) and hard registers satisfying
CONVERT_HARD_REGISTER_TO_SSA_P are converted to SSA form. */
/* Given a hard register number REG_NO, return nonzero if and only if
the register should be converted to SSA. */
#ifndef CONVERT_HARD_REGISTER_TO_SSA_P
#define CONVERT_HARD_REGISTER_TO_SSA_P(REG_NO) (0) /* default of no hard registers */
#endif /* CONVERT_HARD_REGISTER_TO_SSA_P */
/* Given a register number REG_NO, return nonzero if and only if the
register should be converted to SSA. */
#define CONVERT_REGISTER_TO_SSA_P(REG_NO) \
((!HARD_REGISTER_NUM_P (REG_NO)) || \
(CONVERT_HARD_REGISTER_TO_SSA_P (REG_NO)))
static int conservative_reg_partition;
/* This flag is set when the CFG is in SSA form. */
@ -152,20 +135,20 @@ struct ssa_rename_from_hash_table_data {
partition reg_partition;
};
void ssa_rename_from_initialize
static void ssa_rename_from_initialize
PARAMS ((void));
rtx ssa_rename_from_lookup
static rtx ssa_rename_from_lookup
PARAMS ((int reg));
unsigned int original_register
static unsigned int original_register
PARAMS ((unsigned int regno));
void ssa_rename_from_insert
static void ssa_rename_from_insert
PARAMS ((unsigned int reg, rtx r));
void ssa_rename_from_free
static void ssa_rename_from_free
PARAMS ((void));
typedef int (*srf_trav) PARAMS ((int regno, rtx r, sbitmap canonical_elements, partition reg_partition));
static void ssa_rename_from_traverse
PARAMS ((htab_trav callback_function, sbitmap canonical_elements, partition reg_partition));
static void ssa_rename_from_print
/*static Avoid warnign message. */ void ssa_rename_from_print
PARAMS ((void));
static int ssa_rename_from_print_1
PARAMS ((void **slot, void *data));
@ -299,7 +282,7 @@ ssa_rename_to_insert(reg, r)
/* Prepare ssa_rename_from for use. */
void
static void
ssa_rename_from_initialize ()
{
/* We use an arbitrary initial hash table size of 64. */
@ -312,7 +295,7 @@ ssa_rename_from_initialize ()
/* Find the REG entry in ssa_rename_from. Return NULL_RTX if no entry is
found. */
rtx
static rtx
ssa_rename_from_lookup (reg)
int reg;
{
@ -329,7 +312,7 @@ ssa_rename_from_lookup (reg)
the register is a pseudo, return the original register's number.
Otherwise, return this register number REGNO. */
unsigned int
static unsigned int
original_register (regno)
unsigned int regno;
{
@ -339,7 +322,7 @@ original_register (regno)
/* Add mapping from R to REG to ssa_rename_from even if already present. */
void
static void
ssa_rename_from_insert (reg, r)
unsigned int reg;
rtx r;
@ -359,7 +342,7 @@ ssa_rename_from_insert (reg, r)
CANONICAL_ELEMENTS and REG_PARTITION pass data needed by the only
current use of this function. */
void
static void
ssa_rename_from_traverse (callback_function,
canonical_elements, reg_partition)
htab_trav callback_function;
@ -374,7 +357,7 @@ ssa_rename_from_traverse (callback_function,
/* Destroy ssa_rename_from. */
void
static void
ssa_rename_from_free ()
{
htab_delete (ssa_rename_from_ht);
@ -382,7 +365,8 @@ ssa_rename_from_free ()
/* Print the contents of ssa_rename_from. */
static void
/* static Avoid erroneous error message. */
void
ssa_rename_from_print ()
{
printf ("ssa_rename_from's hash table contents:\n");
@ -1146,9 +1130,6 @@ rename_registers (nregs, idom)
int nregs;
int *idom;
{
int reg;
int mach_mode;
VARRAY_RTX_INIT (ssa_definition, nregs * 3, "ssa_definition");
VARRAY_RTX_INIT (ssa_uses, nregs * 3, "ssa_uses");
ssa_rename_from_initialize ();

64
gcc/ssa.h Normal file
View file

@ -0,0 +1,64 @@
/* Static Single Assignment (SSA) definitions for GNU C-Compiler
Copyright (C) 2000 Free Software Foundation, Inc.
Written by Jeffrey D. Oldham <oldham@codesourcery.com>.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* Main SSA routines. */
extern void convert_to_ssa PARAMS ((void));
extern void convert_from_ssa PARAMS ((void));
typedef int (*successor_phi_fn) PARAMS ((rtx, int, int, void *));
extern int for_each_successor_phi PARAMS ((basic_block bb,
successor_phi_fn,
void *));
/* Optimizations. */
/* In dce.c */
extern void eliminate_dead_code PARAMS ((void));
/* SSA definitions and uses. */
/* This flag is set when the CFG is in SSA form. */
extern int in_ssa_form;
/* Element I is the single instruction that sets register I. */
extern varray_type ssa_definition;
/* Element I is an INSN_LIST of instructions that use register I. */
extern varray_type ssa_uses;
/* Specify which hard registers should be converted. */
/* All pseudo-registers (having register number >=
FIRST_PSEUDO_REGISTER) and hard registers satisfying
CONVERT_HARD_REGISTER_TO_SSA_P are converted to SSA form. */
/* Given a hard register number REG_NO, return nonzero if and only if
the register should be converted to SSA. */
#ifndef CONVERT_HARD_REGISTER_TO_SSA_P
#define CONVERT_HARD_REGISTER_TO_SSA_P(REG_NO) (0) /* default of no hard registers */
#endif /* CONVERT_HARD_REGISTER_TO_SSA_P */
/* Given a register number REG_NO, return nonzero if and only if the
register should be converted to SSA. */
#define CONVERT_REGISTER_TO_SSA_P(REG_NO) \
((!HARD_REGISTER_NUM_P (REG_NO)) || \
(CONVERT_HARD_REGISTER_TO_SSA_P (REG_NO)))

View file

@ -68,6 +68,7 @@ DEFTIMEVAR (TV_REORDER_BLOCKS , "reorder blocks")
DEFTIMEVAR (TV_SHORTEN_BRANCH , "shorten branches")
DEFTIMEVAR (TV_REG_STACK , "reg stack")
DEFTIMEVAR (TV_TO_SSA , "convert to SSA")
DEFTIMEVAR (TV_DEAD_CODE_ELIM , "eliminate dead code")
DEFTIMEVAR (TV_FROM_SSA , "convert from SSA")
DEFTIMEVAR (TV_FINAL , "final")
DEFTIMEVAR (TV_SYMOUT , "symout")

View file

@ -63,6 +63,7 @@ Boston, MA 02111-1307, USA. */
#include "regs.h"
#include "timevar.h"
#include "diagnostic.h"
#include "ssa.h"
#ifndef ACCUMULATE_OUTGOING_ARGS
#define ACCUMULATE_OUTGOING_ARGS 0
@ -259,6 +260,7 @@ enum dump_file_index
DFI_cse,
DFI_addressof,
DFI_ssa,
DFI_dce,
DFI_ussa,
DFI_gcse,
DFI_loop,
@ -291,7 +293,7 @@ enum dump_file_index
Remaining -d letters:
" h o q u "
" H K OPQ TUVWXYZ"
" H K OPQ TUVW YZ"
*/
struct dump_file_info dump_file[DFI_MAX] =
@ -302,6 +304,7 @@ struct dump_file_info dump_file[DFI_MAX] =
{ "cse", 's', 0, 0, 0 },
{ "addressof", 'F', 0, 0, 0 },
{ "ssa", 'e', 1, 0, 0 },
{ "dce", 'X', 1, 0, 0 },
{ "ussa", 'e', 1, 0, 0 }, /* Yes, duplicate enable switch. */
{ "gcse", 'G', 1, 0, 0 },
{ "loop", 'L', 1, 0, 0 },
@ -786,6 +789,9 @@ int flag_gnu_linker = 1;
/* Enable SSA. */
int flag_ssa = 0;
/* Enable dead code elimination. */
int flag_dce = 0;
/* Tag all structures with __attribute__(packed) */
int flag_pack_struct = 0;
@ -1094,6 +1100,8 @@ lang_independent_options f_options[] =
"Instrument function entry/exit with profiling calls"},
{"ssa", &flag_ssa, 1,
"Enable SSA optimizations" },
{"dce", &flag_dce, 1,
"Enable dead code elimination" },
{"leading-underscore", &flag_leading_underscore, 1,
"External symbols have a leading underscore" },
{"ident", &flag_no_ident, 0,
@ -2976,13 +2984,25 @@ rest_of_compilation (decl)
close_dump_file (DFI_ssa, print_rtl_with_bb, insns);
timevar_pop (TV_TO_SSA);
/* Currently, there's nothing to do in SSA form. */
/* The SSA implementation uses basic block numbers in its phi
nodes. Thus, changing the control-flow graph or the basic
blocks, e.g., calling find_basic_blocks () or cleanup_cfg (),
may cause problems. */
if (flag_dce)
{
/* Remove dead code. */
timevar_push (TV_DEAD_CODE_ELIM);
open_dump_file (DFI_dce, decl);
insns = get_insns ();
eliminate_dead_code();
close_dump_file (DFI_dce, print_rtl_with_bb, insns);
timevar_pop (TV_DEAD_CODE_ELIM);
}
/* Convert from SSA form. */
timevar_push (TV_FROM_SSA);