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:
parent
2c56187413
commit
4e872036e8
@ -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
|
||||
|
||||
|
@ -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
535
gcc/conflict.c
Normal 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;
|
||||
}
|
||||
|
40
gcc/flow.c
40
gcc/flow.c
@ -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;
|
||||
|
14
gcc/rtl.h
14
gcc/rtl.h
@ -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
561
gcc/ssa.c
@ -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 (®_NOTES (insn), rename_insn_1, &set_data);
|
||||
for_each_rtx (&PATTERN (insn), rename_insn_1, &context);
|
||||
for_each_rtx (®_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;
|
||||
}
|
||||
|
34
gcc/toplev.c
34
gcc/toplev.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user