rtl.h (INSN_P): New macro.

* rtl.h (INSN_P): New macro.
	(successor_phi_fn): New typedef.
	(for_each_successor_phi): New prototype.
	(in_ssa_form): New variable.
	(PHI_NODE_P): Likewise.
	* flow.c (calculate_global_regs_live): Add to new_live_at_end from
	phi nodes in successors.
	(mark_used_regs): Add PHI case.
	(set_phi_alternative_reg): New function.
	(life_analysis): Assert that dead code elimination is not selected
	when in SSA form.
	* toplev.c (to_ssa_time): New variable.
	(from_ssa_time): Likewise.
	(compile_file): Zero to_ssa_time and from_ssa_time.
	Print time to convert to and from SSA.
	(rest_of_compilation): Time convert_to_ssa and convert_from_ssa.
	(print_time): Compute percent fraction as integer.
	* ssa.c (PHI_NODE_P): Moved to rtl.h.
	(convert_to_ssa): Check if we're already in SSA.
	Don't eliminate dead code in life_analysis.
	Rerun flow and life analysis at bottom.
	(eliminate_phi): Use canonical regnos when adding nodes.
	(mark_reg_in_phi): New function.
	(mark_phi_and_copy_regs): Likewise.
	(convert_from_ssa): Rerun life analysis at top.
	Use coalesced partition.
	Check for removing a phi node at the end of the block.
	(compute_coalesced_reg_partition): New function.
	(coalesce_regs_in_copies): Likewise.
	(coalesce_reg_in_phi): Likewise.
	(coalesce_regs_in_sucessor_phi_nodes): Likewise.
	(for_each_successor_phi): Likewise.
	(rename_context): New struct.
	(rename_block): Use a rename_context with rename_insn_1.  When
	renaming sets of a subreg, emit a copy of the entire reg first.
	(rename_insn_1): Treat data as a rename_context *.  Save current
	insn in set_data.
	(rename_set_data): Add field set_insn.
	* Makefile.in (HASHTAB_H): Move up in file.
	(OBSTACK_H): New macro.
	(collect2.o): Use OBSTACK_H in dependencies.
	(sdbout.o): Likewise.
	(emit-rtl.o): Likewise.
	(simplify-rtx.o): Likewise.
	(fix-header.o): Likewise.
	(OBJS):	Add conflict.o.
	(conflict.o): New rule.
	* basic-block.h: Include partition.h.
	(conflict_graph): New typedef.
	(conflict_graph_enum_fn): Likewise.
	(conflict_graph_new): New prototype.
	(conflict_graph_delete): Likewise.
	(conflict_graph_add): Likewise.
	(conflict_graph_conflict_p): Likewise.
	(conflict_graph_enum): Likewise.
	(conflict_graph_merge_regs): Likewise.
	(conflict_graph_print): Likewise.
	(conflict_graph_compute): Likewise.
	* conflict.c: New file.

From-SVN: r32979
This commit is contained in:
Alex Samuel 2000-04-06 21:22:49 +00:00
parent 2c56187413
commit 4e872036e8
7 changed files with 1163 additions and 69 deletions

View File

@ -218,6 +218,10 @@ RANLIB_TEST_FOR_TARGET = \
# Dir to search for system headers. Overridden by cross-make.
SYSTEM_HEADER_DIR = /usr/include
# Where to find some libiberty headers.
HASHTAB_H = $(srcdir)/../include/hashtab.h
OBSTACK_H = $(srcdir)/../include/obstack.h
# Default cross SYSTEM_HEADER_DIR, to be overridden by targets.
CROSS_SYSTEM_HEADER_DIR = $(tooldir)/sys-include
@ -680,7 +684,7 @@ OBJS = diagnostic.o \
profile.o insn-attrtab.o $(out_object_file) $(EXTRA_OBJS) convert.o \
mbchar.o dyn-string.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
sibcall.o conflict.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
@ -1387,7 +1391,7 @@ collect2$(exeext): $(COLLECT2_OBJS) $(LIBDEPS)
$(CC) $(ALL_CFLAGS) $(LDFLAGS) -o $@ $(COLLECT2_OBJS) $(LIBS)
collect2.o : collect2.c $(CONFIG_H) system.h gstab.h intl.h \
$(srcdir)/../include/obstack.h $(DEMANGLE_H) collect2.h version.h
$(OBSTACK_H) $(DEMANGLE_H) collect2.h version.h
$(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
-DTARGET_MACHINE=\"$(target_alias)\" $(MAYBE_USE_COLLECT2) \
-c `echo $(srcdir)/collect2.c | sed 's,^\./,,'`
@ -1548,7 +1552,7 @@ dbxout.o : dbxout.c $(CONFIG_H) system.h $(TREE_H) $(RTL_H) flags.h $(REGS_H) \
toplev.h
sdbout.o : sdbout.c $(CONFIG_H) system.h $(TREE_H) $(RTL_H) flags.h except.h \
function.h $(EXPR_H) output.h hard-reg-set.h $(REGS_H) defaults.h real.h \
insn-config.h $(srcdir)/../include/obstack.h xcoffout.h c-pragma.h \
insn-config.h $(OBSTACK_H) xcoffout.h c-pragma.h \
sdbout.h toplev.h
dwarfout.o : dwarfout.c $(CONFIG_H) system.h $(TREE_H) $(RTL_H) dwarf.h \
flags.h insn-config.h reload.h output.h defaults.h toplev.h dwarfout.h
@ -1572,7 +1576,7 @@ jump.o : jump.c $(CONFIG_H) system.h $(RTL_H) flags.h hard-reg-set.h $(REGS_H) \
simplify-rtx.o : simplify-rtx.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) \
hard-reg-set.h flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \
output.h function.h cselib.h $(GGC_H) $(srcdir)/../include/obstack.h
output.h function.h cselib.h ggc.h $(OBSTACK_H)
cse.o : cse.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) hard-reg-set.h flags.h \
real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h output.h function.h $(GGC_H)
gcse.o : gcse.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) hard-reg-set.h \
@ -1587,6 +1591,8 @@ 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) $(REGS_H) $(BASIC_BLOCK_H) \
output.h insn-config.h
conflict.o : conflict.c $(CONFIG_H) $(OBSTACK_H) $(HASHTAB_H) \
rtl.h basic-block.h
profile.o : profile.c $(CONFIG_H) system.h $(RTL_H) flags.h insn-flags.h \
gcov-io.h $(TREE_H) output.h $(REGS_H) toplev.h function.h insn-config.h \
$(GGC_H)
@ -2056,7 +2062,6 @@ LIBCPP_OBJS = cpplib.o cpphash.o cpperror.o cppexp.o cppfiles.o \
prefix.o version.o mbchar.o @extra_cpp_objs@
LIBCPP_DEPS = cpplib.h cpphash.h intl.h system.h
HASHTAB_H = $(srcdir)/../include/hashtab.h
# All the other archives built/used by this makefile are for targets. This
# one is strictly for the host.
@ -2287,7 +2292,7 @@ fix-header: fix-header.o scan-decls.o scan.o xsys-protos.h $(HOST_LIBDEPS) \
$(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ fix-header.o \
scan-decls.o scan.o libcpp.a $(HOST_LIBS) ../libiberty/libiberty.a
fix-header.o: fix-header.c $(srcdir)/../include/obstack.h scan.h \
fix-header.o: fix-header.c $(OBSTACK_H) scan.h \
xsys-protos.h $(build_xm_file) system.h cpplib.h
$(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/fix-header.c

View File

@ -24,6 +24,7 @@ Boston, MA 02111-1307, USA. */
#include "bitmap.h"
#include "sbitmap.h"
#include "varray.h"
#include "partition.h"
/* Head of register set linked list. */
typedef bitmap_head regset_head;
@ -146,7 +147,9 @@ typedef struct basic_block_def {
/* The edges into and out of the block. */
edge pred, succ;
/* Liveness info. */
/* Liveness info. Note that in SSA form, global_live_at_start does
not reflect the use of regs in phi functions, since the liveness
of these regs may depend on which edge was taken into the block. */
regset local_set;
regset global_live_at_start;
regset global_live_at_end;
@ -459,5 +462,31 @@ extern void debug_regset PARAMS ((regset));
extern void verify_flow_info PARAMS ((void));
extern int flow_loop_outside_edge_p PARAMS ((const struct loop *, edge));
typedef struct conflict_graph_def *conflict_graph;
/* Callback function when enumerating conflicts. The arguments are
the smaller and larger regno in the conflict. Returns zero if
enumeration is to continue, non-zero to halt enumeration. */
typedef int (*conflict_graph_enum_fn) (int, int, void *);
/* Prototypes of operations on conflict graphs. */
extern conflict_graph conflict_graph_new
PARAMS ((int));
extern void conflict_graph_delete PARAMS ((conflict_graph));
extern int conflict_graph_add PARAMS ((conflict_graph,
int, int));
extern int conflict_graph_conflict_p PARAMS ((conflict_graph,
int, int));
extern void conflict_graph_enum PARAMS ((conflict_graph, int,
conflict_graph_enum_fn,
void *));
extern void conflict_graph_merge_regs PARAMS ((conflict_graph, int,
int));
extern void conflict_graph_print PARAMS ((conflict_graph, FILE*));
extern conflict_graph conflict_graph_compute
PARAMS ((regset,
partition));
#endif /* _BASIC_BLOCK_H */

535
gcc/conflict.c Normal file
View File

@ -0,0 +1,535 @@
/* Register conflict graph computation routines.
Copyright (C) 2000 Free Software Foundation, Inc.
Contributed by CodeSourcery, LLC
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. */
/* References:
Building an Optimizing Compiler
Robert Morgan
Butterworth-Heinemann, 1998 */
#include "config.h"
#include "system.h"
#include "obstack.h"
#include "hashtab.h"
#include "rtl.h"
#include "basic-block.h"
/* Use malloc to allocate obstack chunks. */
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
/* A register conflict graph is an undirected graph containing nodes
for some or all of the regs used in a function. Arcs represent
conflicts, i.e. two nodes are connected by an arc if there is a
point in the function at which the regs corresponding to the two
nodes are both live.
The conflict graph is represented by the data structures described
in Morgan section 11.3.1. Nodes are not stored explicitly; only
arcs are. An arc stores the numbers of the regs it connects.
Arcs can be located by two methods:
- The two reg numbers for each arc are hashed into a single
value, and the arc is placed in a hash table according to this
value. This permits quick determination of whether a specific
conflict is present in the graph.
- Additionally, the arc data structures are threaded by a set of
linked lists by single reg number. Since each arc references
two regs, there are two next pointers, one for the
smaller-numbered reg and one for the larger-numbered reg. This
permits the quick enumeration of conflicts for a single
register.
Arcs are allocated from an obstack. */
/* An arc in a conflict graph. */
struct conflict_graph_arc_def
{
/* The next element of the list of conflicts involving the
smaller-numbered reg, as an index in the table of arcs of this
graph. Contains NULL if this is the tail. */
struct conflict_graph_arc_def *smaller_next;
/* The next element of the list of conflicts involving the
larger-numbered reg, as an index in the table of arcs of this
graph. Contains NULL if this is the tail. */
struct conflict_graph_arc_def *larger_next;
/* The smaller-numbered reg involved in this conflict. */
int smaller;
/* The larger-numbered reg involved in this conflict. */
int larger;
};
typedef struct conflict_graph_arc_def *conflict_graph_arc;
/* A conflict graph. */
struct conflict_graph_def
{
/* A hash table of arcs. Used to search for a specific conflict. */
htab_t arc_hash_table;
/* The number of regs this conflict graph handles. */
int num_regs;
/* For each reg, the arc at the head of a list that threads through
all the arcs involving that reg. An entry is NULL if no
conflicts exist involving that reg. */
conflict_graph_arc *neighbor_heads;
/* Arcs are allocated from here. */
struct obstack arc_obstack;
};
/* The initial capacity (number of conflict arcs) for newly-created
conflict graphs. */
#define INITIAL_ARC_CAPACITY (64)
/* Computes the hash value of the conflict graph arc connecting regs
R1__ and R2__. R1__ is assumed to be smaller or equal to R2__. */
#define CONFLICT_HASH_FN(r1__, r2__) ((r2__) * ((r2__) - 1) / 2 + (r1__))
static unsigned arc_hash
PARAMS ((const void *arcp));
static int arc_eq
PARAMS ((const void *arcp1, const void *arcp2));
static int print_conflict
PARAMS ((int reg1, int reg2, void *contextp));
static void mark_reg
PARAMS ((rtx reg, rtx setter, void *data));
/* Callback function to compute the hash value of an arc. Uses
current_graph to locate the graph to which the arc belongs. */
static unsigned
arc_hash (arcp)
const void *arcp;
{
conflict_graph_arc arc = (conflict_graph_arc) arcp;
return CONFLICT_HASH_FN (arc->smaller, arc->larger);
}
/* Callback function to determine the equality of two arcs in the hash
table. */
static int
arc_eq (arcp1, arcp2)
const void *arcp1;
const void *arcp2;
{
conflict_graph_arc arc1 = (conflict_graph_arc) arcp1;
conflict_graph_arc arc2 = (conflict_graph_arc) arcp2;
return arc1->smaller == arc2->smaller && arc1->larger == arc2->larger;
}
/* Creates an empty conflict graph to hold conflicts among NUM_REGS
registers. */
conflict_graph
conflict_graph_new (num_regs)
int num_regs;
{
conflict_graph graph =
(conflict_graph) xmalloc (sizeof (struct conflict_graph_def));
graph->num_regs = num_regs;
/* Set up the hash table. No delete action is specified; memory
management of arcs is through the obstack. */
graph->arc_hash_table =
htab_create (INITIAL_ARC_CAPACITY, &arc_hash, &arc_eq, NULL);
/* Create an obstack for allocating arcs. */
obstack_init (&(graph->arc_obstack));
/* Create and zero the lookup table by register number. */
graph->neighbor_heads = (conflict_graph_arc *)
xmalloc (num_regs * sizeof (conflict_graph_arc));
memset (graph->neighbor_heads, 0,
num_regs * sizeof (conflict_graph_arc));
return graph;
}
/* Deletes a conflict graph. */
void
conflict_graph_delete (graph)
conflict_graph graph;
{
obstack_free (&(graph->arc_obstack), NULL);
htab_delete (graph->arc_hash_table);
free (graph->neighbor_heads);
free (graph);
}
/* Adds a conflict to GRAPH between regs REG1 and REG2, which must be
distinct. Returns non-zero, unless the conflict is already present
in GRAPH, in which case it does nothing and returns zero. */
int
conflict_graph_add (graph, reg1, reg2)
conflict_graph graph;
int reg1;
int reg2;
{
int smaller = MIN (reg1, reg2);
int larger = MAX (reg1, reg2);
conflict_graph_arc arc;
void **hash_table_slot;
/* A reg cannot conflict with itself. */
if (reg1 == reg2)
abort ();
/* If the conflict is already there, do nothing.
FIXME: This is a little wastful; it would be faster to look up the
conflict in the hash table, returning it if it exists and
inserting a new entry if it doesn't, all in one operation. This
would save an extra hash lookup. However, the hashtab interface
doesn't really allow this right now. */
if (conflict_graph_conflict_p (graph, reg1, reg2))
return 0;
/* Allocate an arc. */
arc = (conflict_graph_arc)
obstack_alloc (&(graph->arc_obstack),
sizeof (struct conflict_graph_arc_def));
/* Record the reg numbers. */
arc->smaller = smaller;
arc->larger = larger;
/* Link the conflict into into two lists, one for each reg. */
arc->smaller_next = graph->neighbor_heads[smaller];
graph->neighbor_heads[smaller] = arc;
arc->larger_next = graph->neighbor_heads[larger];
graph->neighbor_heads[larger] = arc;
/* Put it in the hash table. */
hash_table_slot = htab_find_slot (graph->arc_hash_table,
(void *) arc, 1);
*hash_table_slot = (void *) arc;
return 1;
}
/* Returns non-zero if a conflict exists in GRAPH between regs REG1
and REG2. */
int
conflict_graph_conflict_p (graph, reg1, reg2)
conflict_graph graph;
int reg1;
int reg2;
{
/* Build an arc to search for. */
struct conflict_graph_arc_def arc;
arc.smaller = MIN (reg1, reg2);
arc.larger = MAX (reg1, reg2);
return htab_find (graph->arc_hash_table, (void *) &arc) != NULL;
}
/* Calls ENUM_FN for each conflict in GRAPH involving REG. EXTRA is
passed back to ENUM_FN. */
void
conflict_graph_enum (graph, reg, enum_fn, extra)
conflict_graph graph;
int reg;
conflict_graph_enum_fn enum_fn;
void *extra;
{
conflict_graph_arc arc = graph->neighbor_heads[reg];
while (arc != NULL)
{
/* Invoke the callback. */
if ((*enum_fn) (arc->smaller, arc->larger, extra))
/* Stop if requested. */
break;
/* Which next pointer to follow depends on whether REG is the
smaller or larger reg in this conflict. */
if (reg < arc->larger)
arc = arc->smaller_next;
else
arc = arc->larger_next;
}
}
/* For each conflict between a register x and SRC in GRAPH, adds a
conflict to GRAPH between x and TARGET. */
void
conflict_graph_merge_regs (graph, target, src)
conflict_graph graph;
int target;
int src;
{
conflict_graph_arc arc = graph->neighbor_heads[src];
if (target == src)
return;
while (arc != NULL)
{
int other = arc->smaller;
if (other == src)
other = arc->larger;
conflict_graph_add (graph, target, other);
/* Which next pointer to follow depends on whether REG is the
smaller or larger reg in this conflict. */
if (src < arc->larger)
arc = arc->smaller_next;
else
arc = arc->larger_next;
}
}
/* Holds context information while a conflict graph is being traversed
for printing. */
struct print_context
{
/* The file pointer to which we're printing. */
FILE *fp;
/* The reg whose conflicts we're printing. */
int reg;
/* Whether a conflict has already been printed for this reg. */
int started;
};
/* Callback function when enumerating conflicts during printing. */
static int
print_conflict (reg1, reg2, contextp)
int reg1;
int reg2;
void *contextp;
{
struct print_context *context = (struct print_context *) contextp;
int reg;
/* If this is the first conflict printed for this reg, start a new
line. */
if (! context->started)
{
fprintf (context->fp, " %d:", context->reg);
context->started = 1;
}
/* Figure out the reg whose conflicts we're printing. The other reg
is the interesting one. */
if (reg1 == context->reg)
reg = reg2;
else if (reg2 == context->reg)
reg = reg1;
else
abort ();
/* Print the conflict. */
fprintf (context->fp, " %d", reg);
/* Continue enumerating. */
return 0;
}
/* Prints the conflicts in GRAPH to FP. */
void
conflict_graph_print (graph, fp)
conflict_graph graph;
FILE *fp;
{
int reg;
struct print_context context;
context.fp = fp;
fprintf (fp, "Conflicts:\n");
/* Loop over registers supported in this graph. */
for (reg = 0; reg < graph->num_regs; ++reg)
{
context.reg = reg;
context.started = 0;
/* Scan the conflicts for reg, printing as we go. A label for
this line will be printed the first time a conflict is
printed for the reg; we won't start a new line if this reg
has no conflicts. */
conflict_graph_enum (graph, reg, &print_conflict, &context);
/* If this reg does have conflicts, end the line. */
if (context.started)
fputc ('\n', fp);
}
}
/* Callback function for note_stores. */
static void
mark_reg (reg, setter, data)
rtx reg;
rtx setter ATTRIBUTE_UNUSED;
void *data;
{
regset set = (regset) data;
if (GET_CODE (reg) == SUBREG)
reg = SUBREG_REG (reg);
/* We're only interested in regs. */
if (GET_CODE (reg) != REG)
return;
SET_REGNO_REG_SET (set, REGNO (reg));
}
/* Allocates a conflict graph and computes conflicts over the current
function for the registers set in REGS. The caller is responsible
for deallocating the return value.
Preconditions: the flow graph must be in SSA form, and life
analysis (specifically, regs live at exit from each block) must be
up-to-date.
This algorithm determines conflicts by walking the insns in each
block backwards. We maintain the set of live regs at each insn,
starting with the regs live on exit from the block. For each insn:
1. If a reg is set in this insns, it must be born here, since
we're in SSA. Therefore, it was not live before this insns,
so remove it from the set of live regs.
2. For each reg born in this insn, record a conflict between it
and every other reg live coming into this insn. For each
existing conflict, one of the two regs must be born while the
other is alive. See Morgan or elsewhere for a proof of this.
3. Regs clobbered by this insn must have been live coming into
it, so record them as such.
The resulting conflict graph is not built for regs in REGS
themselves; rather, partition P is used to obtain the canonical reg
for each of these. The nodes of the conflict graph are these
canonical regs instead. */
conflict_graph
conflict_graph_compute (regs, p)
regset regs;
partition p;
{
int b;
conflict_graph graph = conflict_graph_new (max_reg_num ());
for (b = n_basic_blocks; --b >= 0; )
{
basic_block bb = BASIC_BLOCK (b);
regset_head live_head;
regset live = &live_head;
regset_head born_head;
regset born = &born_head;
rtx insn;
rtx head;
INIT_REG_SET (live);
INIT_REG_SET (born);
/* Start with the regs that are live on exit, limited to those
we're interested in. */
COPY_REG_SET (live, bb->global_live_at_end);
AND_REG_SET (live, regs);
/* Walk the instruction stream backwards. */
head = bb->head;
insn = bb->end;
for (insn = bb->end;
insn != head;
insn = PREV_INSN (insn))
{
int born_reg;
int live_reg;
rtx link;
/* Are we interested in this insn? */
if (INSN_P (insn))
{
/* Determine which regs are set in this insn. Since
we're in SSA form, if a reg is set here it isn't set
anywhere elso, so this insn is where the reg is born. */
CLEAR_REG_SET (born);
note_stores (PATTERN (insn), mark_reg, (void *) born);
#ifdef AUTO_INC_DEC
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_INC)
mark_reg (XEXP (link, 0), NULL_RTX, NULL);
#endif
AND_REG_SET (born, regs);
/* Regs born here were not live before this insn. */
AND_COMPL_REG_SET (live, born);
/* For every reg born here, add a conflict with every other
reg live coming into this insn. */
EXECUTE_IF_SET_IN_REG_SET (born,
FIRST_PSEUDO_REGISTER,
born_reg, {
EXECUTE_IF_SET_IN_REG_SET (live,
FIRST_PSEUDO_REGISTER,
live_reg, {
/* Build the conflict graph in terms of canonical
regnos. */
int b = partition_find (p, born_reg);
int l = partition_find (p, live_reg);
if (b != l)
conflict_graph_add (graph, b, l);
});
});
/* Morgan's algorithm checks the operands of the insn
and adds them to the set of live regs. Instead, we
use death information added by life analysis. Regs
dead after this instruction were live before it. */
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_DEAD)
{
int regno = REGNO (XEXP (link, 0));
if (REGNO_REG_SET_P (regs, regno))
SET_REGNO_REG_SET (live, regno);
}
}
}
}
return graph;
}

View File

@ -317,6 +317,7 @@ static void notice_stack_pointer_modification_1 PARAMS ((rtx, rtx, void *));
static void notice_stack_pointer_modification PARAMS ((rtx));
static void mark_reg PARAMS ((rtx, void *));
static void mark_regs_live_at_end PARAMS ((regset));
static int set_phi_alternative_reg PARAMS ((rtx, int, int, void *));
static void calculate_global_regs_live PARAMS ((sbitmap, sbitmap, int));
static void propagate_block PARAMS ((basic_block, regset,
regset, int));
@ -2486,6 +2487,15 @@ life_analysis (f, nregs, file, remove_dead_code)
#endif
int flags;
sbitmap all_blocks;
/* Dead code elimination changes basic block structure and therefore
breaks the SSA phi representation. Particularly, a phi node
can have an alternative value for each incoming block, referenced
by the block number. Removing dead code can bump entire blocks
and therefore cause blocks to be renumbered, invalidating the
numbering of phi alternatives. */
if (remove_dead_code && in_ssa_form)
abort ();
/* Record which registers will be eliminated. We use this in
mark_used_regs. */
@ -2960,6 +2970,22 @@ mark_regs_live_at_end (set)
diddle_return_value (mark_reg, set);
}
/* Callback function for for_each_successor_phi. DATA is a regset.
Sets the SRC_REGNO, the regno of the phi alternative for phi node
INSN, in the regset. */
static int
set_phi_alternative_reg (insn, dest_regno, src_regno, data)
rtx insn ATTRIBUTE_UNUSED;
int dest_regno ATTRIBUTE_UNUSED;
int src_regno;
void *data;
{
regset live = (regset) data;
SET_REGNO_REG_SET (live, src_regno);
return 0;
}
/* Propagate global life info around the graph of basic blocks. Begin
considering blocks with their corresponding bit set in BLOCKS_IN.
BLOCKS_OUT is set for every block that was changed. */
@ -3020,6 +3046,13 @@ calculate_global_regs_live (blocks_in, blocks_out, flags)
IOR_REG_SET (new_live_at_end, sb->global_live_at_start);
}
/* Regs used in phi nodes are not included in
global_live_at_start, since they are live only along a
particular edge. Set those regs that are live because of a
phi node alternative corresponding to this particular block. */
for_each_successor_phi (bb->index, &set_phi_alternative_reg,
new_live_at_end);
if (bb == ENTRY_BLOCK_PTR)
{
COPY_REG_SET (bb->global_live_at_end, new_live_at_end);
@ -4688,6 +4721,13 @@ mark_used_regs (needed, live, x, flags, insn)
break;
}
case PHI:
/* We _do_not_ want to scan operands of phi nodes. Operands of
a phi function are evaluated only when control reaches this
block along a particular edge. Therefore, regs that appear
as arguments to phi should not be added to the global live at
start. */
return;
default:
break;

View File

@ -357,6 +357,9 @@ extern void rtvec_check_failed_bounds PARAMS ((rtvec, int,
/* ACCESS MACROS for particular fields of insns. */
/* Determines whether X is an insn. */
#define INSN_P(X) (GET_RTX_CLASS (GET_CODE(X)) == 'i')
/* Holds a unique number for each insn.
These are not necessarily sequentially increasing. */
#define INSN_UID(INSN) XINT(INSN, 0)
@ -981,6 +984,12 @@ extern const char * const note_insn_name[];
/* For a NOTE_INSN_LIVE note, the original basic block number. */
#define RANGE_LIVE_ORIG_BLOCK(INSN) (XINT (INSN, 1))
/* Determine if the insn is a PHI node. */
#define PHI_NODE_P(X) \
(X && GET_CODE (X) == INSN \
&& GET_CODE (PATTERN (X)) == SET \
&& GET_CODE (SET_SRC (PATTERN (X))) == PHI)
/* Nonzero if we need to distinguish between the return value of this function
and the return value of a function called by this function. This helps
@ -1793,6 +1802,11 @@ extern int stack_regs_mentioned PARAMS ((rtx insn));
/* 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 ((int bb,
successor_phi_fn,
void *));
extern int in_ssa_form;
/* In toplev.c */

561
gcc/ssa.c
View File

@ -48,6 +48,13 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
/* TODO:
Handle subregs better, maybe. For now, if a reg that's set in a
subreg expression is duplicated going into SSA form, an extra copy
is inserted first that copies the entire reg into the duplicate, so
that the other bits are preserved. This isn't strictly SSA, since
at least part of the reg is assigned in more than one place (though
they are adjacent).
??? What to do about strict_low_part. Probably I'll have to split
them out of their current instructions first thing.
@ -55,10 +62,19 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
in which the RTL encodes exactly what we want, without exposing a
lot of niggling processor details. At some later point we lower
the representation, calling back into optabs to finish any necessary
expansion.
*/
expansion. */
/* If conservative_reg_partition is non-zero, use a conservative
register partitioning algorithm (which leaves more regs after
emerging from SSA) instead of the coalescing one. This is being
left in for a limited time only, as a debugging tool until the
coalescing algorithm is validated. */
static int conservative_reg_partition;
/* This flag is set when the CFG is in SSA form. */
int in_ssa_form = 0;
/* Element I is the single instruction that sets register I+PSEUDO. */
varray_type ssa_definition;
@ -115,25 +131,40 @@ static void ephi_create
PARAMS ((int t, sbitmap visited, sbitmap *pred, sbitmap *succ, rtx *nodes));
static void eliminate_phi
PARAMS ((edge e, partition reg_partition));
static int make_regs_equivalent_over_bad_edges
PARAMS ((int bb, partition reg_partition));
/* These are used only in the conservative register partitioning
algorithms. */
static int make_equivalent_phi_alternatives_equivalent
PARAMS ((int bb, partition reg_partition));
static partition compute_conservative_reg_partition
PARAMS ((void));
PARAMS (());
static int rename_equivalent_regs_in_insn
PARAMS ((rtx *ptr, void *data));
/* These are used in the register coalescing algorithm. */
static int coalesce_if_unconflicting
PARAMS ((partition p, conflict_graph conflicts, int reg1, int reg2));
static int coalesce_regs_in_copies
PARAMS ((int bb, partition p, conflict_graph conflicts));
static int coalesce_reg_in_phi
PARAMS ((rtx, int dest_regno, int src_regno, void *data));
static int coalesce_regs_in_successor_phi_nodes
PARAMS ((int bb, partition p, conflict_graph conflicts));
static partition compute_coalesced_reg_partition
PARAMS (());
static int mark_reg_in_phi
PARAMS ((rtx *ptr, void *data));
static void mark_phi_and_copy_regs
PARAMS ((regset phi_set));
static int rename_equivalent_regs_in_insn
PARAMS ((rtx *ptr, void *data));
static void rename_equivalent_regs
PARAMS ((partition reg_partition));
/* Determine if the insn is a PHI node. */
#define PHI_NODE_P(X) \
(X && GET_CODE (X) == INSN \
&& GET_CODE (PATTERN (X)) == SET \
&& GET_CODE (SET_SRC (PATTERN (X))) == PHI)
/* Given the SET of a PHI node, return the address of the alternative
for predecessor block C. */
@ -494,6 +525,15 @@ struct rename_set_data
rtx set_dest;
rtx new_reg;
rtx prev_reg;
rtx set_insn;
};
/* This struct is used to pass information to callback functions while
renaming registers. */
struct rename_context
{
struct rename_set_data *set_data;
rtx current_insn;
};
static void new_registers_for_updates
@ -518,7 +558,8 @@ rename_insn_1 (ptr, data)
void *data;
{
rtx x = *ptr;
struct rename_set_data **set_datap = data;
struct rename_context *context = data;
struct rename_set_data **set_datap = &(context->set_data);
if (x == NULL_RTX)
return 0;
@ -551,6 +592,7 @@ rename_insn_1 (ptr, data)
r->reg_loc = destp;
r->set_dest = SET_DEST (x);
r->set_insn = context->current_insn;
r->next = *set_datap;
*set_datap = r;
@ -577,7 +619,6 @@ rename_insn_1 (ptr, data)
if (GET_MODE (x) != GET_MODE (new_reg))
abort ();
*ptr = new_reg;
/* ??? Mark for a new ssa_uses entry. */
}
/* Else this is a use before a set. Warn? */
}
@ -663,12 +704,15 @@ rename_block (bb, idom)
insn = next;
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
struct rename_set_data *old_set_data = set_data;
struct rename_context context;
context.set_data = set_data;
context.current_insn = insn;
for_each_rtx (&PATTERN (insn), rename_insn_1, &set_data);
for_each_rtx (&REG_NOTES (insn), rename_insn_1, &set_data);
for_each_rtx (&PATTERN (insn), rename_insn_1, &context);
for_each_rtx (&REG_NOTES (insn), rename_insn_1, &context);
new_registers_for_updates (set_data, old_set_data, insn);
new_registers_for_updates (context.set_data, set_data, insn);
set_data = context.set_data;
}
next = NEXT_INSN (insn);
@ -741,9 +785,23 @@ rename_block (bb, idom)
while (set_data)
{
struct rename_set_data *next;
rtx old_reg;
rtx old_reg = *set_data->reg_loc;
/* If the set is of a subreg only, copy the entire reg first so
that unmodified bits are preserved. Of course, we don't
strictly have SSA any more, but that's the best we can do
without a lot of hard work. */
if (GET_CODE (set_data->set_dest) == SUBREG)
{
if (old_reg != set_data->new_reg)
{
rtx copy = gen_rtx_SET (GET_MODE (old_reg),
set_data->new_reg, old_reg);
emit_insn_before (copy, set_data->set_insn);
}
}
old_reg = *set_data->reg_loc;
*set_data->reg_loc = set_data->new_reg;
ssa_rename_to[REGNO (old_reg)-FIRST_PSEUDO_REGISTER]
= set_data->prev_reg;
@ -793,11 +851,18 @@ convert_to_ssa()
int nregs;
/* Don't do it twice. */
if (in_ssa_form)
abort ();
find_basic_blocks (get_insns (), max_reg_num(), NULL);
/* The dominator algorithms assume all blocks are reachable, clean
/* The dominator algorithms assume all blocks are reachable; clean
up first. */
cleanup_cfg (get_insns ());
life_analysis (get_insns (), max_reg_num (), NULL, 1);
/* Don't eliminate dead code here. The CFG we computed above must
remain unchanged until we are finished emerging from SSA form --
the phi node representation depends on it. */
life_analysis (get_insns (), max_reg_num (), NULL, 0);
/* Compute dominators. */
dominators = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
@ -862,39 +927,14 @@ convert_to_ssa()
sbitmap_vector_free (dfs);
sbitmap_vector_free (evals);
sbitmap_vector_free (idfs);
in_ssa_form = 1;
reg_scan (get_insns (), max_reg_num (), 1);
find_basic_blocks (get_insns (), max_reg_num (), NULL);
life_analysis (get_insns (), max_reg_num (), NULL, 0);
}
/* This is intended to be the FIND of a UNION/FIND algorithm managing
the partitioning of the pseudos. Glancing through the rest of the
global optimizations, it seems things will work out best if the
partition is set up just before convert_from_ssa is called. See
section 11.4 of Morgan.
??? Morgan's algorithm, perhaps with some care, may allow copy
propagation to happen concurrently with the conversion from SSA.
However, it presents potential problems with critical edges -- to
split or not to split. He mentions beginning the partitioning by
unioning registers associated by a PHI across abnormal critical
edges. This is the approache taken here. It is unclear to me how
we are able to do that arbitrarily, though.
Alternately, Briggs presents an algorithm in which critical edges
need not be split, at the expense of the creation of new pseudos,
and the need for some concurrent register renaming. Moreover, it
is ameanable for modification such that the instructions can be
placed anywhere in the target block, which solves the before-call
placement problem. However, I don't immediately see how we could
do that concurrently with copy propoagation.
More study is required. */
/*
* Eliminate the PHI across the edge from C to B.
*/
/* REG is the representative temporary of its partition. Add it to the
set of nodes to be processed, if it hasn't been already. Return the
index of this register in the node set. */
@ -1086,10 +1126,11 @@ eliminate_phi (e, reg_partition)
if (GET_CODE (reg) != REG || GET_CODE (tgt) != REG)
abort();
reg = regno_reg_rtx[partition_find (reg_partition, REGNO (reg))];
tgt = regno_reg_rtx[partition_find (reg_partition, REGNO (tgt))];
/* If the two registers are already in the same partition,
nothing will need to be done. */
if (partition_find (reg_partition, REGNO (reg))
!= partition_find (reg_partition, REGNO (tgt)))
if (reg != tgt)
{
int ireg, itgt;
@ -1305,7 +1346,6 @@ make_equivalent_phi_alternatives_equivalent (bb, reg_partition)
return changed;
}
/* Compute a conservative partition of outstanding pseudo registers.
See Morgan 7.3.1. */
@ -1341,6 +1381,322 @@ compute_conservative_reg_partition ()
return p;
}
/* The following functions compute a register partition that attempts
to eliminate as many reg copies and phi node copies as possible by
coalescing registers. This is the strategy:
1. As in the conservative case, the top priority is to coalesce
registers that otherwise would cause copies to be placed on
abnormal critical edges (which isn't possible).
2. Figure out which regs are involved (in the LHS or RHS) of
copies and phi nodes. Compute conflicts among these regs.
3. Walk around the instruction stream, placing two regs in the
same class of the partition if one appears on the LHS and the
other on the RHS of a copy or phi node and the two regs don't
conflict. The conflict information of course needs to be
updated.
4. If anything has changed, there may be new opportunities to
coalesce regs, so go back to 2.
*/
/* If REG1 and REG2 don't conflict in CONFLICTS, place them in the
same class of partition P, if they aren't already. Update
CONFLICTS appropriately.
Returns one if REG1 and REG2 were placed in the same class but were
not previously; zero otherwise.
See Morgan figure 11.15. */
static int
coalesce_if_unconflicting (p, conflicts, reg1, reg2)
partition p;
conflict_graph conflicts;
int reg1;
int reg2;
{
int reg;
/* Don't mess with hard regs. */
if (reg1 < FIRST_PSEUDO_REGISTER || reg2 < FIRST_PSEUDO_REGISTER)
return 0;
/* Find the canonical regs for the classes containing REG1 and
REG2. */
reg1 = partition_find (p, reg1);
reg2 = partition_find (p, reg2);
/* If they're already in the same class, there's nothing to do. */
if (reg1 == reg2)
return 0;
/* If the regs conflict, our hands are tied. */
if (conflict_graph_conflict_p (conflicts, reg1, reg2))
return 0;
/* We're good to go. Put the regs in the same partition. */
partition_union (p, reg1, reg2);
/* Find the new canonical reg for the merged class. */
reg = partition_find (p, reg1);
/* Merge conflicts from the two previous classes. */
conflict_graph_merge_regs (conflicts, reg, reg1);
conflict_graph_merge_regs (conflicts, reg, reg2);
return 1;
}
/* For each register copy insn in basic block BB, place the LHS and
RHS regs in the same class in partition P if they do not conflict
according to CONFLICTS.
Returns the number of changes that were made to P.
See Morgan figure 11.14. */
static int
coalesce_regs_in_copies (bb, p, conflicts)
int bb;
partition p;
conflict_graph conflicts;
{
int changed = 0;
rtx insn;
rtx end = BLOCK_END (bb);
/* Scan the instruction stream of the block. */
for (insn = BLOCK_HEAD (bb); insn != end; insn = NEXT_INSN (insn))
{
rtx pattern;
rtx src;
rtx dest;
/* If this isn't a set insn, go to the next insn. */
if (GET_CODE (insn) != INSN)
continue;
pattern = PATTERN (insn);
if (GET_CODE (pattern) != SET)
continue;
src = SET_SRC (pattern);
dest = SET_DEST (pattern);
/* If src or dest are subregs, find the underlying reg. */
while (GET_CODE (src) == SUBREG
&& SUBREG_WORD (src) != 0)
src = SUBREG_REG (src);
while (GET_CODE (dest) == SUBREG
&& SUBREG_WORD (dest) != 0)
dest = SUBREG_REG (dest);
/* We're only looking for copies. */
if (GET_CODE (src) != REG || GET_CODE (dest) != REG)
continue;
/* Coalesce only if the reg modes are the same. As long as
each reg's rtx is unique, it can have only one mode, so two
pseudos of different modes can't be coalesced into one.
FIXME: We can probably get around this by inserting SUBREGs
where appropriate, but for now we don't bother. */
if (GET_MODE (src) != GET_MODE (dest))
continue;
/* Found a copy; see if we can use the same reg for both the
source and destination (and thus eliminate the copy,
ultimately). */
changed += coalesce_if_unconflicting (p, conflicts,
REGNO (src), REGNO (dest));
}
return changed;
}
struct phi_coalesce_context
{
partition p;
conflict_graph conflicts;
int changed;
};
/* Callback function for for_each_successor_phi. If the set
destination and the phi alternative regs do not conflict, place
them in the same paritition class. DATA is a pointer to a
phi_coalesce_context struct. */
static int
coalesce_reg_in_phi (insn, dest_regno, src_regno, data)
rtx insn ATTRIBUTE_UNUSED;
int dest_regno;
int src_regno;
void *data;
{
struct phi_coalesce_context *context =
(struct phi_coalesce_context *) data;
/* Attempt to use the same reg, if they don't conflict. */
context->changed
+= coalesce_if_unconflicting (context->p, context->conflicts,
dest_regno, src_regno);
return 0;
}
/* For each alternative in a phi function corresponding to basic block
BB (in phi nodes in successor block to BB), place the reg in the
phi alternative and the reg to which the phi value is set into the
same class in partition P, if allowed by CONFLICTS.
Return the number of changes that were made to P.
See Morgan figure 11.14. */
static int
coalesce_regs_in_successor_phi_nodes (bb, p, conflicts)
int bb;
partition p;
conflict_graph conflicts;
{
struct phi_coalesce_context context;
context.p = p;
context.conflicts = conflicts;
context.changed = 0;
for_each_successor_phi (bb, &coalesce_reg_in_phi, &context);
return context.changed;
}
/* Compute and return a partition of pseudos. Where possible,
non-conflicting pseudos are placed in the same class.
The caller is responsible for deallocating the returned partition. */
static partition
compute_coalesced_reg_partition ()
{
int bb;
int changed = 0;
/* We don't actually work with hard registers, but it's easier to
carry them around anyway rather than constantly doing register
number arithmetic. */
partition p =
partition_new (ssa_definition->num_elements + FIRST_PSEUDO_REGISTER);
/* The first priority is to make sure registers that might have to
be copied on abnormal critical edges are placed in the same
partition. This saves us from having to split abnormal critical
edges (which can't be done). */
for (bb = n_basic_blocks; --bb >= 0; )
make_regs_equivalent_over_bad_edges (bb, p);
do
{
regset_head phi_set;
conflict_graph conflicts;
changed = 0;
/* Build the set of registers involved in phi nodes, either as
arguments to the phi function or as the target of a set. */
INITIALIZE_REG_SET (phi_set);
mark_phi_and_copy_regs (&phi_set);
/* Compute conflicts. */
conflicts = conflict_graph_compute (&phi_set, p);
/* FIXME: Better would be to process most frequently executed
blocks first, so that most frequently executed copies would
be more likely to be removed by register coalescing. But any
order will generate correct, if non-optimal, results. */
for (bb = n_basic_blocks; --bb >= 0; )
{
changed += coalesce_regs_in_copies (bb, p, conflicts);
changed += coalesce_regs_in_successor_phi_nodes (bb, p, conflicts);
}
conflict_graph_delete (conflicts);
}
while (changed > 0);
return p;
}
/* Mark the regs in a phi node. PTR is a phi expression or one of its
components (a REG or a CONST_INT). DATA is a reg set in which to
set all regs. Called from for_each_rtx. */
static int
mark_reg_in_phi (ptr, data)
rtx *ptr;
void *data;
{
rtx expr = *ptr;
regset set = (regset) data;
switch (GET_CODE (expr))
{
case REG:
SET_REGNO_REG_SET (set, REGNO (expr));
/* Fall through. */
case CONST_INT:
case PHI:
return 0;
default:
abort ();
}
}
/* Mark in PHI_SET all pseudos that are used in a phi node -- either
set from a phi expression, or used as an argument in one. Also
mark regs that are the source or target of a reg copy. Uses
ssa_definition. */
static void
mark_phi_and_copy_regs (phi_set)
regset phi_set;
{
int reg;
/* Scan the definitions of all regs. */
for (reg = VARRAY_SIZE (ssa_definition);
--reg >= FIRST_PSEUDO_REGISTER;
)
{
rtx insn = VARRAY_RTX (ssa_definition, reg);
rtx pattern;
rtx src;
if (insn == NULL)
continue;
pattern = PATTERN (insn);
/* Sometimes we get PARALLEL insns. These aren't phi nodes or
copies. */
if (GET_CODE (pattern) != SET)
continue;
src = SET_SRC (pattern);
if (GET_CODE (src) == REG)
{
/* It's a reg copy. */
SET_REGNO_REG_SET (phi_set, reg);
SET_REGNO_REG_SET (phi_set, REGNO (src));
}
else if (GET_CODE (src) == PHI)
{
/* It's a phi node. Mark the reg being set. */
SET_REGNO_REG_SET (phi_set, reg);
/* Mark the regs used in the phi function. */
for_each_rtx (&src, mark_reg_in_phi, phi_set);
}
/* ... else nothing to do. */
}
}
/* Rename regs in insn PTR that are equivalent. DATA is the register
partition which specifies equivalences. */
@ -1379,7 +1735,7 @@ rename_equivalent_regs_in_insn (ptr, data)
int regno = REGNO (dest);
int new_regno = partition_find (reg_partition, regno);
if (regno != new_regno)
*destp = regno_reg_rtx [new_regno];
*destp = regno_reg_rtx[new_regno];
for_each_rtx (&SET_SRC (x),
rename_equivalent_regs_in_insn,
@ -1418,7 +1774,6 @@ rename_equivalent_regs_in_insn (ptr, data)
}
}
/* Rename regs that are equivalent in REG_PARTITION. */
static void
@ -1453,7 +1808,6 @@ rename_equivalent_regs (reg_partition)
}
}
/* The main entry point for moving from SSA. */
void
@ -1461,8 +1815,19 @@ convert_from_ssa()
{
int bb;
partition reg_partition;
reg_partition = compute_conservative_reg_partition ();
rtx insns = get_insns ();
/* We need up-to-date life information. */
find_basic_blocks (insns, max_reg_num (), NULL);
life_analysis (insns, max_reg_num (), NULL, 0);
/* Figure out which regs in copies and phi nodes don't conflict and
therefore can be coalesced. */
if (conservative_reg_partition)
reg_partition = compute_conservative_reg_partition ();
else
reg_partition = compute_coalesced_reg_partition ();
rename_equivalent_regs (reg_partition);
/* Eliminate the PHI nodes. */
@ -1488,6 +1853,11 @@ convert_from_ssa()
insn = next_nonnote_insn (insn);
while (PHI_NODE_P (insn))
{
/* If a phi node is the last insn in the block, there must
have been nothing else. Set the block end to the block
head. */
if (insn == BLOCK_END (bb))
BLOCK_END (bb) = BLOCK_HEAD (bb);
insn = delete_insn (insn);
if (GET_CODE (insn) == NOTE)
insn = next_nonnote_insn (insn);
@ -1499,5 +1869,80 @@ convert_from_ssa()
/* Commit all the copy nodes needed to convert out of SSA form. */
commit_edge_insertions ();
in_ssa_form = 0;
count_or_remove_death_notes (NULL, 1);
}
/* Scan phi nodes in successors to BB. For each such phi node that
has a phi alternative value corresponding to BB, invoke FN. FN
is passed the entire phi node insn, the regno of the set
destination, the regno of the phi argument corresponding to BB,
and DATA.
If FN ever returns non-zero, stops immediately and returns this
value. Otherwise, returns zero. */
int
for_each_successor_phi (bb, fn, data)
int bb;
successor_phi_fn fn;
void *data;
{
basic_block block;
edge e;
if (bb == EXIT_BLOCK)
return 0;
else if (bb == ENTRY_BLOCK)
block = ENTRY_BLOCK_PTR;
else
block = BASIC_BLOCK (bb);
/* Scan outgoing edges. */
for (e = block->succ; e != NULL; e = e->succ_next)
{
rtx insn;
basic_block successor = e->dest;
if (successor->index == ENTRY_BLOCK
|| successor->index == EXIT_BLOCK)
continue;
/* Advance to the first non-label insn of the successor block. */
insn = successor->head;
while (insn != NULL
&& (GET_CODE (insn) == CODE_LABEL
|| GET_CODE (insn) == NOTE))
insn = NEXT_INSN (insn);
if (insn == NULL)
continue;
/* Scan phi nodes in the successor. */
for ( ; PHI_NODE_P (insn); insn = NEXT_INSN (insn))
{
int result;
rtx phi_set = PATTERN (insn);
rtx *alternative = phi_alternative (phi_set, block->index);
rtx phi_src;
/* This phi function may not have an alternative
corresponding to the incoming edge, indicating the
assigned variable is not defined along the edge. */
if (alternative == NULL)
continue;
phi_src = *alternative;
/* Invoke the callback. */
result = (*fn) (insn, REGNO (SET_DEST (phi_set)),
REGNO (phi_src), data);
/* Terminate if requested. */
if (result != 0)
return result;
}
}
return 0;
}

View File

@ -1426,6 +1426,8 @@ int reorder_blocks_time;
int rename_registers_time;
int shorten_branch_time;
int stack_reg_time;
int to_ssa_time;
int from_ssa_time;
int final_time;
int symout_time;
int dump_time;
@ -1510,9 +1512,9 @@ print_time (str, total)
int total;
{
fprintf (stderr,
"time in %s: %d.%06d (%.0f%%)\n",
"time in %s: %d.%06d (%d%%)\n",
str, total / 1000000, total % 1000000,
all_time == 0 ? 0.00 : (double) total / (double) all_time * 100.0);
all_time == 0 ? 0 : (100 * total) / all_time);
}
/* This is the default decl_printable_name function. */
@ -1890,6 +1892,26 @@ close_dump_file (index, func, insns)
});
}
/* Routine to empty a dump file. */
static void
clean_dump_file (suffix)
const char *suffix;
{
char * const dumpname = concat (dump_base_name, suffix, NULL);
rtl_dump_file = fopen (dumpname, "w");
if (rtl_dump_file == NULL)
pfatal_with_name (dumpname);
free (dumpname);
fclose (rtl_dump_file);
rtl_dump_file = NULL;
return;
}
/* Do any final processing required for the declarations in VEC, of
which there are LEN. We write out inline functions and variables
that have been deferred until this point, but which are required.
@ -2176,6 +2198,8 @@ compile_file (name)
rename_registers_time = 0;
shorten_branch_time = 0;
stack_reg_time = 0;
to_ssa_time = 0;
from_ssa_time = 0;
final_time = 0;
symout_time = 0;
dump_time = 0;
@ -2559,6 +2583,8 @@ compile_file (name)
print_time ("integration", integration_time);
print_time ("jump", jump_time);
print_time ("cse", cse_time);
print_time ("to ssa", to_ssa_time);
print_time ("from ssa", from_ssa_time);
print_time ("gcse", gcse_time);
print_time ("loop", loop_time);
print_time ("cse2", cse2_time);
@ -3032,11 +3058,11 @@ rest_of_compilation (decl)
if (flag_ssa)
{
open_dump_file (DFI_ssa, decl);
convert_to_ssa ();
TIMEVAR (to_ssa_time, convert_to_ssa ());
close_dump_file (DFI_ssa, print_rtl_with_bb, insns);
open_dump_file (DFI_ussa, decl);
convert_from_ssa ();
TIMEVAR (from_ssa_time, convert_from_ssa ());
/* New registers have been created. Rescan their usage. */
reg_scan (insns, max_reg_num (), 1);
close_dump_file (DFI_ussa, print_rtl_with_bb, insns);