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:
parent
79c2c6da2c
commit
b53978a3ee
11 changed files with 990 additions and 65 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
620
gcc/dce.c
Normal 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 (¤t_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 (¤t_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);
|
||||
}
|
175
gcc/flow.c
175
gcc/flow.c
|
@ -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. */
|
||||
|
|
|
@ -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
|
||||
|
|
27
gcc/rtl.h
27
gcc/rtl.h
|
@ -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
|
||||
|
|
49
gcc/ssa.c
49
gcc/ssa.c
|
@ -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
64
gcc/ssa.h
Normal 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)))
|
|
@ -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")
|
||||
|
|
26
gcc/toplev.c
26
gcc/toplev.c
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue