diff options
-rw-r--r-- | gcc/Makefile.in | 17 | ||||
-rw-r--r-- | gcc/basic-block.h | 31 | ||||
-rw-r--r-- | gcc/conflict.c | 535 | ||||
-rw-r--r-- | gcc/flow.c | 40 | ||||
-rw-r--r-- | gcc/rtl.h | 14 | ||||
-rw-r--r-- | gcc/ssa.c | 561 | ||||
-rw-r--r-- | gcc/toplev.c | 34 |
7 files changed, 1163 insertions, 69 deletions
diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 7bf0ba1..e1d23d6 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -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 diff --git a/gcc/basic-block.h b/gcc/basic-block.h index c3a9d9a..5fa6226 100644 --- a/gcc/basic-block.h +++ b/gcc/basic-block.h @@ -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 */ diff --git a/gcc/conflict.c b/gcc/conflict.c new file mode 100644 index 0000000..51851a2 --- /dev/null +++ b/gcc/conflict.c @@ -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; +} + @@ -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; @@ -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 */ @@ -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,9 +62,18 @@ 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,38 +927,13 @@ convert_to_ssa() sbitmap_vector_free (dfs); sbitmap_vector_free (evals); sbitmap_vector_free (idfs); -} - - -/* 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. */ + 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); +} -/* - * 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 @@ -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; +} diff --git a/gcc/toplev.c b/gcc/toplev.c index 920dba5..df5a764 100644 --- a/gcc/toplev.c +++ b/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); |