[new-ra] general infrastructure for RA
Michael Matz
matz@suse.de
Wed Jul 2 12:45:00 GMT 2003
Hi,
this is the first of a series of patches for the new-ra branch. They
evolved over time, and partly are constructed by hand from an overall
diff. Roughly according to one functionality update, plus assorted fixes
for each patch if needed. I do this to have the possibility to point
people to this and that gcc-patches message for reverting ;-)
This first patch adds some functionality to the non ra* files. In
particular it makes it possible for cse's delete_trivially_dead_insn() to
update eventual df information; makes recog.c deal with stack pseudos
(which are accepted as MEMs); globalizes some functions and variables; and
adds some functions which I'll need later.
The branch itself doesn't bootstrap as in CVS, and also doesn't bootstrap
with the patch. This is fixed by the next patch. I guess that one could
be applied before this patch, but I've tested them in this order, so ...
Ciao,
Michael.
--
2003-06-23 Michael Matz <matz@suse.de>
* Makefile.in (ra-build.o): Depend on OBSTACK_H.
(ra-rewrite.o): Depend on pre-reload.h.
* caller-save.c (save_call_clobbered_regs): Handle uninitialized rmw
regs.
* cfgrtl.c (verify_flow_info): Barf again on missing REG_EH_REGION.
(purge_dead_edges): Also ignore sibling calls.
* cse.c (delete_trivially_dead_insns_1): Break out from ... plus
updates a df structure.
(delete_trivially_dead_insns): ... here. Call the above.
(delete_trivially_dead_insns_df): New.
* df.c (df_uid_refs_remove): New.
(df_refs_update): Handle deleted insns.
* df.h (delete_trivially_dead_insns_df): Prototype.
* jump.c (true_regnum): Avoid segfault.
* loop.c (copy_cost_for_loop): Renamed from ...
(copy_cost): ... this. Updated all accesses.
* pre-reload.c (prefer_swapped): New.
(push_pre_reload): Don't subreg special reg rtx's.
(scan_alternative): New arguments pfree and prej. Use prefer_swapped.
(collect_insn_info): Better heuristics for choosing alternative.
Don't add reloads in later ra passes.
* ra.c (newra_in_progress): New.
(reg_alloc): Set/reset it.
* recog.c (toplevel): Include ra.h.
(insn_invalid_p): Check for newra_in_progress.
(scratch_operand): Ditto, plus only accept non-spill pseudos.
(does_contain_spill_pseudos): New.
(constrain_operands): Call it. Check newra_in_progress. Accept
stack pseudos where MEM is accepted.
* reg-stack.c (move_for_stack_reg): Use delete_insn_and_edges.
* regclass.c (may_move_in_cost, may_move_out_cost, copy_cost):
Make global.
* regs.h (may_move_in_cost, may_move_out_cost): Declare.
* rtl.h (REG_OR_SUBREG_P): New.
(note_uses_partial, newra_in_progress, copy_cost): Declare.
* rtlanal.c (note_uses_1, note_uses_partial): New.
(note_uses): Call note_uses_1.
* version.c (version_string): Add "(new RA)" designator.
diff -urpN work-gcc.orig/gcc/Makefile.in work-gcc/gcc/Makefile.in
--- work-gcc.orig/gcc/Makefile.in 2003-01-23 16:47:03.000000000 +0100
+++ work-gcc/gcc/Makefile.in 2003-06-16 17:37:31.000000000 +0200
@@ -1566,7 +1566,7 @@ ra.o : ra.c $(CONFIG_H) $(SYSTEM_H) $(RT
$(BASIC_BLOCK_H) df.h expr.h output.h toplev.h flags.h reload.h ra.h
ra-build.o : ra-build.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_P_H) \
insn-config.h $(RECOG_H) function.h $(REGS_H) hard-reg-set.h \
- $(BASIC_BLOCK_H) df.h output.h ggc.h ra.h gt-ra-build.h reload.h
+ $(BASIC_BLOCK_H) df.h output.h ggc.h $(OBSTACK_H) ra.h gt-ra-build.h reload.h
ra-colorize.o : ra-colorize.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_P_H) \
function.h $(REGS_H) hard-reg-set.h $(BASIC_BLOCK_H) df.h output.h ra.h
ra-debug.o : ra-debug.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) insn-config.h \
@@ -1574,7 +1574,7 @@ ra-debug.o : ra-debug.c $(CONFIG_H) $(SY
$(TM_P_H)
ra-rewrite.o : ra-rewrite.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_P_H) \
function.h $(REGS_H) hard-reg-set.h $(BASIC_BLOCK_H) df.h expr.h \
- output.h except.h ra.h reload.h insn-config.h
+ output.h except.h ra.h reload.h pre-reload.h insn-config.h
reload.o : reload.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h output.h \
$(EXPR_H) $(OPTABS_H) reload.h $(RECOG_H) hard-reg-set.h insn-config.h \
$(REGS_H) pre-reload.h function.h real.h toplev.h $(TM_P_H)
diff -urpN work-gcc.orig/gcc/caller-save.c work-gcc/gcc/caller-save.c
--- work-gcc.orig/gcc/caller-save.c 2003-01-23 16:47:11.000000000 +0100
+++ work-gcc/gcc/caller-save.c 2003-06-16 17:37:31.000000000 +0200
@@ -399,6 +399,17 @@ save_call_clobbered_regs ()
{
CLEAR_HARD_REG_SET (referenced_regs);
mark_referenced_regs (PATTERN (insn));
+ /* In the case of rmw regs, whose "read" part is
+ uninitialized (this can happen if the was a
+ (set (subreg:SMALL (reg:BIG))) just before calculating
+ liveness; this doesn't fill the whole reg, and it
+ happens that this pseudo is marked live over calls,
+ where in reality (after changing it to hardregs) it
+ isn't. We simply detect such situations by also
+ restoring before defines of saved hard regs. */
+ CLEAR_HARD_REG_SET (this_insn_sets);
+ note_stores (PATTERN (insn), mark_set_regs, NULL);
+ IOR_HARD_REG_SET (referenced_regs, this_insn_sets);
AND_HARD_REG_SET (referenced_regs, hard_regs_saved);
}
diff -urpN work-gcc.orig/gcc/cfgrtl.c work-gcc/gcc/cfgrtl.c
--- work-gcc.orig/gcc/cfgrtl.c 2003-01-23 16:47:13.000000000 +0100
+++ work-gcc/gcc/cfgrtl.c 2003-06-16 17:37:31.000000000 +0200
@@ -1861,12 +1861,12 @@ verify_flow_info ()
edge_checksum[e->dest->index + 2] += (size_t) e;
}
- /*if (n_eh && GET_CODE (PATTERN (bb->end)) != RESX
+ if (n_eh && GET_CODE (PATTERN (bb->end)) != RESX
&& !find_reg_note (bb->end, REG_EH_REGION, NULL_RTX))
{
error ("Missing REG_EH_REGION note in the end of bb %i", bb->index);
err = 1;
- }*/
+ }
if (n_branch
&& (GET_CODE (bb->end) != JUMP_INSN
|| (n_branch > 1 && (any_uncondjump_p (bb->end)
@@ -2118,7 +2118,8 @@ purge_dead_edges (bb)
else if (e->flags & EDGE_ABNORMAL_CALL)
{
if (GET_CODE (bb->end) == CALL_INSN
- && (! (note = find_reg_note (insn, REG_EH_REGION, NULL))
+ && (SIBLING_CALL_P (bb->end)
+ || ! (note = find_reg_note (insn, REG_EH_REGION, NULL))
|| INTVAL (XEXP (note, 0)) >= 0))
continue;
}
diff -urpN work-gcc.orig/gcc/cse.c work-gcc/gcc/cse.c
--- work-gcc.orig/gcc/cse.c 2003-01-23 16:47:20.000000000 +0100
+++ work-gcc/gcc/cse.c 2003-06-16 17:37:31.000000000 +0200
@@ -36,6 +36,7 @@ Software Foundation, 59 Temple Place - S
#include "expr.h"
#include "toplev.h"
#include "output.h"
+#include "df.h"
#include "ggc.h"
#include "timevar.h"
@@ -701,6 +702,8 @@ static void flush_hash_table PARAMS ((vo
static bool insn_live_p PARAMS ((rtx, int *));
static bool set_live_p PARAMS ((rtx, rtx, int *));
static bool dead_libcall_p PARAMS ((rtx, int *));
+static int delete_trivially_dead_insns_1 PARAMS ((rtx, int, struct df *));
+
/* Dump the expressions in the equivalence class indicated by CLASSP.
This function is used only for debugging. */
@@ -7685,6 +7688,30 @@ delete_trivially_dead_insns (insns, nreg
rtx insns;
int nreg;
{
+ return delete_trivially_dead_insns_1 (insns, nreg, NULL);
+}
+
+/* Like delete_trivially_dead_insns(), but update also the DF structure
+ plus automatically preserves basic blocks. */
+
+void
+delete_trivially_dead_insns_df (insns, nreg, df)
+ rtx insns;
+ int nreg;
+ struct df *df;
+{
+ delete_trivially_dead_insns_1 (insns, nreg, df);
+}
+
+/* Subroutine of delete_trivially_dead_insns() and
+ delete_trivially_dead_insns_df() doing the real work. */
+
+static int
+delete_trivially_dead_insns_1 (insns, nreg, df)
+ rtx insns;
+ int nreg;
+ struct df *df;
+{
int *counts;
rtx insn, prev;
int in_libcall = 0, dead_libcall = 0;
@@ -7740,6 +7767,8 @@ delete_trivially_dead_insns (insns, nreg
{
count_reg_usage (insn, counts, NULL_RTX, -1);
delete_insn_and_edges (insn);
+ if (df && BLOCK_FOR_INSN (insn))
+ df_insn_modify (df, BLOCK_FOR_INSN (insn), insn);
ndead++;
}
diff -urpN work-gcc.orig/gcc/df.c work-gcc/gcc/df.c
--- work-gcc.orig/gcc/df.c 2003-02-21 00:12:31.000000000 +0100
+++ work-gcc/gcc/df.c 2003-06-16 17:37:31.000000000 +0200
@@ -200,6 +200,7 @@ static struct df_link *df_ref_unlink PAR
static void df_def_unlink PARAMS((struct df *, struct ref *));
static void df_use_unlink PARAMS((struct df *, struct ref *));
static void df_insn_refs_mark_deleted PARAMS ((struct df *, basic_block, rtx));
+static void df_uid_refs_remove PARAMS ((struct df *, unsigned int));
static void df_insn_refs_unlink PARAMS ((struct df *, basic_block, rtx));
#if 0
static void df_bb_refs_unlink PARAMS ((struct df *, basic_block));
@@ -903,7 +904,8 @@ df_ref_record (df, reg, loc, insn, ref_t
(subreg:SI (reg:M A) N), with size(SImode) > size(Mmode).
XXX Is that true? We could also use the global word_mode variable. */
if (GET_CODE (reg) == SUBREG
- && (GET_MODE_SIZE (GET_MODE (reg)) < GET_MODE_SIZE (word_mode)
+ /* && (GET_MODE_SIZE (GET_MODE (reg)) < GET_MODE_SIZE (word_mode) */
+ && (GET_MODE_SIZE (GET_MODE (reg)) < GET_MODE_SIZE (SImode)
|| GET_MODE_SIZE (GET_MODE (reg))
>= GET_MODE_SIZE (GET_MODE (SUBREG_REG (reg)))))
{
@@ -2328,18 +2330,31 @@ df_refs_update (df)
struct df *df;
{
basic_block bb;
+ bitmap b = BITMAP_XMALLOC ();
+ rtx insn;
int count = 0;
+ unsigned int uid;
- if ((unsigned int) max_reg_num () >= df->reg_size)
+ if ((unsigned int)max_reg_num () >= df->reg_size)
df_reg_table_realloc (df, 0);
df_refs_queue (df);
+
+ /* Collect insns which were really deleted from the chain, but at least
+ got marked in the modified bitmap. */
+ bitmap_copy (b, df->insns_modified);
+ for (insn = get_insns(); insn; insn = NEXT_INSN (insn))
+ bitmap_clear_bit (b, INSN_UID (insn));
+ EXECUTE_IF_SET_IN_BITMAP (b, 0, uid,
+ df_uid_refs_remove (df, uid););
FOR_EACH_BB_IN_BITMAP (df->bbs_modified, 0, bb,
{
count += df_bb_refs_update (df, bb);
});
+ BITMAP_XFREE (b);
+
df_refs_process (df);
return count;
}
@@ -2427,6 +2442,23 @@ df_finish (df)
free (df);
}
+/* Unlink all refs which are associated with UID. This should
+ correspond to a deleted insn (which was removed from the chain). */
+static void
+df_uid_refs_remove (df, uid)
+ struct df *df;
+ unsigned int uid;
+{
+ struct df_link *link;
+
+ for (link = df->insns[uid].defs; link; link = link->next)
+ df_def_unlink (df, link->ref);
+ for (link = df->insns[uid].uses; link; link = link->next)
+ df_use_unlink (df, link->ref);
+
+ df->insns[uid].defs = 0;
+ df->insns[uid].uses = 0;
+}
/* Unlink INSN from its reference information. */
static void
diff -urpN work-gcc.orig/gcc/df.h work-gcc/gcc/df.h
--- work-gcc.orig/gcc/df.h 2003-02-20 23:37:37.000000000 +0100
+++ work-gcc/gcc/df.h 2003-06-16 17:37:31.000000000 +0200
@@ -348,3 +348,5 @@ extern void iterative_dataflow_bitmap PA
enum df_confluence_op,
transfer_function_bitmap,
int *, void *));
+/* In cse.c */
+extern void delete_trivially_dead_insns_df PARAMS ((rtx, int, struct df *));
diff -urpN work-gcc.orig/gcc/jump.c work-gcc/gcc/jump.c
--- work-gcc.orig/gcc/jump.c 2003-01-23 16:47:36.000000000 +0100
+++ work-gcc/gcc/jump.c 2003-06-16 17:37:31.000000000 +0200
@@ -2395,7 +2395,8 @@ true_regnum (x)
{
if (GET_CODE (x) == REG)
{
- if (REGNO (x) >= FIRST_PSEUDO_REGISTER && reg_renumber[REGNO (x)] >= 0)
+ if (REGNO (x) >= FIRST_PSEUDO_REGISTER
+ && reg_renumber && reg_renumber[REGNO (x)] >= 0)
return reg_renumber[REGNO (x)];
return REGNO (x);
}
diff -urpN work-gcc.orig/gcc/loop.c work-gcc/gcc/loop.c
--- work-gcc.orig/gcc/loop.c 2003-01-23 16:47:37.000000000 +0100
+++ work-gcc/gcc/loop.c 2003-06-16 17:37:31.000000000 +0200
@@ -392,7 +392,7 @@ static int biv_elimination_giv_has_0_off
/* Benefit penalty, if a giv is not replaceable, i.e. must emit an insn to
copy the value of the strength reduced giv to its original register. */
-static int copy_cost;
+static int copy_cost_for_loop;
/* Cost of using a register, to normalize the benefits of a giv. */
static int reg_address_cost;
@@ -404,7 +404,7 @@ init_loop ()
reg_address_cost = address_cost (reg, SImode);
- copy_cost = COSTS_N_INSNS (1);
+ copy_cost_for_loop = COSTS_N_INSNS (1);
}
/* Compute the mapping from uids to luids.
@@ -4962,7 +4962,7 @@ loop_giv_reduce_benefit (loop, bl, v, te
necessary. */
if (! v->replaceable && ! bl->eliminable
&& REG_USERVAR_P (v->dest_reg))
- benefit -= copy_cost;
+ benefit -= copy_cost_for_loop;
/* Decrease the benefit to count the add-insns that we will insert
to increment the reduced reg for the giv. ??? This can
@@ -7697,7 +7697,7 @@ restart:
of finding replaceable giv's, and hence this code may no
longer be necessary. */
if (! g2->replaceable && REG_USERVAR_P (g2->dest_reg))
- g1_add_benefit -= copy_cost;
+ g1_add_benefit -= copy_cost_for_loop;
/* To help optimize the next set of combinations, remove
this giv from the benefits of other potential mates. */
diff -urpN work-gcc.orig/gcc/pre-reload.c work-gcc/gcc/pre-reload.c
--- work-gcc.orig/gcc/pre-reload.c 2003-04-10 15:39:24.000000000 +0200
+++ work-gcc/gcc/pre-reload.c 2003-06-16 17:37:31.000000000 +0200
@@ -78,6 +78,7 @@ static int pseudo_clobbered_p PARAMS
int));
static void pre_reload_collect PARAMS ((struct ra_info *, bitmap));
static int pre_operands_match_p PARAMS ((rtx, rtx));
+static int prefer_swapped PARAMS ((rtx, rtx, rtx));
static void collect_insn_info PARAMS ((struct ra_info *, rtx,
ra_ref **, ra_ref **,
int *, int *));
@@ -1442,14 +1443,27 @@ push_pre_reload (in, out, inloc, outloc,
if (in != 0 && GET_CODE (in) == SUBREG && GET_CODE (SUBREG_REG (in)) == REG
&& REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER
&& ! dont_remove_subreg)
- in = gen_rtx_REG (GET_MODE (in), subreg_regno (in));
+ {
+ rtx sub = SUBREG_REG (in);
+ /* But only do this, if this is not one of the special reg rtx's,
+ as elimination relies on them being there, instead of relying on
+ the reg number. */
+ if (sub != frame_pointer_rtx && sub != hard_frame_pointer_rtx
+ && sub != arg_pointer_rtx && sub != stack_pointer_rtx)
+ in = gen_rtx_REG (GET_MODE (in), subreg_regno (in));
+ }
/* Similarly for OUT. */
if (out != 0 && GET_CODE (out) == SUBREG
&& GET_CODE (SUBREG_REG (out)) == REG
&& REGNO (SUBREG_REG (out)) < FIRST_PSEUDO_REGISTER
&& ! dont_remove_subreg)
- out = gen_rtx_REG (GET_MODE (out), subreg_regno (out));
+ {
+ rtx sub = SUBREG_REG (out);
+ if (sub != frame_pointer_rtx && sub != hard_frame_pointer_rtx
+ && sub != arg_pointer_rtx && sub != stack_pointer_rtx)
+ out = gen_rtx_REG (GET_MODE (out), subreg_regno (out));
+ }
/* Narrow down the class of register wanted if that is
desirable on this machine for efficiency. */
@@ -2102,6 +2116,39 @@ pre_operands_match_p (x, y)
return operands_match_p (x, y);
}
+/* OP0 and OP1 are two commutative operands in INSN, where OP0 has to
+ match a former operand. Return non-zero if we would prefer the insn
+ with both operands swapped and then reloaded. Currently this checks
+ for OP0 and OP1 being registers, and prefers swapping if OP1 dies
+ before OP0. This avoids to create live ranges longer than necessary.
+ It also ensures, that the conflicts of the matching operand (after possibly
+ swapping) are a subset of the conflicts of the other one. */
+
+static int
+prefer_swapped (insn, op0, op1)
+ rtx insn, op0, op1;
+{
+ basic_block bb = BLOCK_FOR_INSN (insn);
+ if (GET_CODE (op0) == SUBREG)
+ op0 = SUBREG_REG (op0);
+ if (GET_CODE (op1) == SUBREG)
+ op1 = SUBREG_REG (op1);
+ if (!bb || !REG_P (op0) || !REG_P (op1))
+ return 0;
+ while (insn && bb == BLOCK_FOR_INSN (insn))
+ {
+ /* The way it's written (first testing OP0, then OP1) ensures,
+ that we do not prefer swapping if both ops die in the same insn. */
+ if (find_reg_note (insn, REG_DEAD, op0))
+ return 0;
+ if (find_reg_note (insn, REG_DEAD, op1))
+ return 1;
+ insn = NEXT_INSN (insn);
+ }
+ return 0;
+}
+
+extern int ra_pass;
struct alternative_info
{
@@ -2123,13 +2170,13 @@ static int scan_alternative PARAMS ((str
enum reload_usage [], int [],
char [MAX_RECOG_OPERANDS]
[MAX_RECOG_OPERANDS],
- int, int *));
+ int, int *, int *, int *));
/* Scan one alternative and fill alternative info. */
static int
scan_alternative (this_alt, constraints, modified, address_reloaded,
- operands_match, swapped, commut)
+ operands_match, swapped, commut, pfree, prej)
struct alternative_info this_alt[];
char *constraints[];
enum reload_usage modified[];
@@ -2137,6 +2184,8 @@ scan_alternative (this_alt, constraints,
char operands_match[MAX_RECOG_OPERANDS][MAX_RECOG_OPERANDS];
int swapped;
int *commut;
+ int *pfree;
+ int *prej;
{
int i;
int j;
@@ -2152,6 +2201,9 @@ scan_alternative (this_alt, constraints,
? counts three times here since we want the disparaging caused by
a bad register class to only count 1/3 as much. */
int reject = 0;
+ /* Number of hardregs in the alternative, for those operands, which
+ accept registers. */
+ int freeness = 0;
enum machine_mode *operand_mode = recog_data.operand_mode;
int commutative = *commut;
@@ -2396,6 +2448,13 @@ scan_alternative (this_alt, constraints,
== op_alt->matches)
badop = 1;
+ /* Possibly prefer the swapped variant, by slightly penalizing
+ the non-swapped form. */
+ if (!swapped && i == commutative
+ && prefer_swapped (this_insn, operand,
+ recog_data.operand[i + 1]))
+ reject++;
+
if (REG_P (operand))
{
op_alt->reg = operand;
@@ -2664,8 +2723,8 @@ scan_alternative (this_alt, constraints,
it will then win since we don't want to have a different
alternative match then. */
if (! (REG_P (operand)
- && (0
- && REGNO (operand) >= FIRST_PSEUDO_REGISTER))
+ /*&& (0
+ && REGNO (operand) >= FIRST_PSEUDO_REGISTER)*/)
&& GET_CODE (operand) != SCRATCH
&& ! (const_to_mem && constmemok))
reject += 2;
@@ -2676,6 +2735,7 @@ scan_alternative (this_alt, constraints,
&& GET_CODE (operand) != SCRATCH)
reject++;
}
+ freeness += reg_class_size[op_alt->class];
}
/* Now see if any output operands that are marked "earlyclobber"
@@ -2760,8 +2820,11 @@ scan_alternative (this_alt, constraints,
a register would be reloaded into a non-preferred class, discourages
the use of this alternative for a reload goal. REJECT is incremented
by six for each ? and two for each non-preferred class. */
- losers = losers * 6 + reject;
+ if (losers)
+ losers = losers * 6 + reject;
+ *pfree = freeness;
+ *prej = reject;
return bad ? -1 : losers;
}
@@ -2810,6 +2873,8 @@ collect_insn_info (ra_info, insn, def_re
int operand_reloadnum[MAX_RECOG_OPERANDS];
int goal_alternative_swapped;
int best;
+ int best_num_regs;
+ int best_reject;
int commutative;
char operands_match[MAX_RECOG_OPERANDS][MAX_RECOG_OPERANDS];
rtx substed_operand[MAX_RECOG_OPERANDS];
@@ -3019,6 +3084,9 @@ collect_insn_info (ra_info, insn, def_re
all the operands together against the register constraints. */
best = MAX_RECOG_OPERANDS * 2 + 600;
+ best_num_regs = 0;
+ best_reject = MAX_RECOG_OPERANDS * 2 + 600;
+
swapped = 0;
goal_alternative_swapped = 0;
@@ -3039,20 +3107,40 @@ collect_insn_info (ra_info, insn, def_re
/* LOSERS counts those that don't fit this alternative
and would require loading. */
+ int reject = 0;
+ int freeness = 0;
int losers = scan_alternative (this_alt, constraints, modified,
address_reloaded, operands_match,
- swapped, &commutative);
+ swapped, &commutative, &freeness, &reject);
/* If this alternative can be made to work by reloading,
and it needs less reloading than the others checked so far,
record it as the chosen goal for reloading. */
- if (losers >= 0 && best > losers)
+ if (losers >= 0
+ && (best > losers
+ /* XXX Ugh. Ugly hack to prefer the swapped variant of
+ an alternative if it's otherwise as good as the non-swapped
+ one. This simulates behaviour in local-alloc when tieing
+ two regs, in a no-conflict block equivalent to a commutative
+ operation. What we want here is select which of the two
+ operands to make matching based on how far the death of the
+ ops is away. We want to make the one with the nearer dead
+ matching. */
+ /* || (best == losers && swapped) */
+ /* If we have a strictly matching alternative which accepts a
+ wider range of hardregs, choose that. But only if it's
+ reject value isn't larger than the last strictly matching
+ alternative. */
+ || (losers == 0 && freeness > best_num_regs
+ && reject <= best_reject)))
{
for (i = 0; i < noperands; i++)
memcpy (&goal_alt[i], &this_alt[i], sizeof (this_alt[0]));
goal_alternative_swapped = swapped;
goal_alternative_number = this_alternative_number;
best = losers;
+ best_num_regs = freeness;
+ best_reject = reject;
}
}
@@ -3125,6 +3213,12 @@ collect_insn_info (ra_info, insn, def_re
for (i = 0; i < noperands; i++)
goal_alt[i].win = 1;
+ /* In the later passes we don't want to create new reload insns, but just
+ record register classes. */
+ if (ra_pass > 1)
+ for (i = 0; i < noperands; i++)
+ goal_alt[i].win = 1;
+
/* If the best alternative is with operands 1 and 2 swapped,
consider them swapped before reporting the reloads. Update the
operand numbers of any reloads already pushed. */
@@ -3685,9 +3779,11 @@ ra_check_constraints (insn)
/* LOSERS counts those that don't fit this alternative
and would require loading. */
+ int freeness = 0;
+ int reject = 0;
int losers = scan_alternative (this_alt, constraints, modified,
address_reloaded, operands_match,
- swapped, &commutative);
+ swapped, &commutative, &freeness, &reject);
if (losers == 0)
return 1;
diff -urpN work-gcc.orig/gcc/ra.c work-gcc/gcc/ra.c
--- work-gcc.orig/gcc/ra.c 2003-02-03 20:30:39.000000000 +0100
+++ work-gcc/gcc/ra.c 2003-06-16 17:37:31.000000000 +0200
@@ -100,6 +100,9 @@ static void init_ra PARAMS ((void));
void reg_alloc PARAMS ((void));
+/* See rtl.h. */
+int newra_in_progress;
+
/* These global variables are "internal" to the register allocator.
They are all documented at their declarations in ra.h. */
@@ -948,6 +951,8 @@ reg_alloc ()
if (flag_ra_pre_reload)
ra_info = ra_info_init (max_reg_num ());
+ newra_in_progress = 1;
+
/* This is the main loop, calling one_pass as long as there are still
some spilled webs. */
do
@@ -1114,6 +1119,7 @@ reg_alloc ()
/*compute_bb_for_insn ();*/
/*store_motion ();*/
no_new_pseudos = 1;
+ newra_in_progress = 0;
rtl_dump_file = ra_dump_file;
/* Some spill insns could've been inserted after trapping calls, i.e.
diff -urpN work-gcc.orig/gcc/recog.c work-gcc/gcc/recog.c
--- work-gcc.orig/gcc/recog.c 2003-01-23 16:47:42.000000000 +0100
+++ work-gcc/gcc/recog.c 2003-06-16 17:37:31.000000000 +0200
@@ -37,6 +37,7 @@ Software Foundation, 59 Temple Place - S
#include "basic-block.h"
#include "output.h"
#include "reload.h"
+#include "ra.h"
#ifndef STACK_PUSH_CODE
#ifdef STACK_GROWS_DOWNWARD
@@ -58,6 +59,7 @@ static void validate_replace_rtx_1 PARAM
static rtx *find_single_use_1 PARAMS ((rtx, rtx *));
static void validate_replace_src_1 PARAMS ((rtx *, void *));
static rtx split_insn PARAMS ((rtx));
+static int does_contain_spill_pseudos PARAMS ((rtx));
/* Nonzero means allow operands to be volatile.
This should be 0 if you are generating rtl, such as if you are calling
@@ -268,7 +270,8 @@ insn_invalid_p (insn)
clobbers. */
int icode = recog (pat, insn,
(GET_CODE (pat) == SET
- && ! reload_completed && ! reload_in_progress)
+ && ! reload_completed && ! reload_in_progress
+ && ! newra_in_progress)
? &num_clobbers : 0);
int is_asm = icode < 0 && asm_noperands (PATTERN (insn)) >= 0;
@@ -296,7 +299,7 @@ insn_invalid_p (insn)
}
/* After reload, verify that all constraints are satisfied. */
- if (reload_completed)
+ if (reload_completed || newra_in_progress)
{
extract_insn (insn);
@@ -1123,9 +1126,6 @@ pmode_register_operand (op, mode)
return register_operand (op, Pmode);
}
-/* Return 1 if OP should match a MATCH_SCRATCH, i.e., if it is a SCRATCH
- or a hard register. */
-
/* XXX gross hack, because pre-reload also allocates SCRATCHes,
which might make some clobbers, which only should be scratch_operands
be registers (pseudos, which then got a hardreg, but were not substituted
@@ -1136,6 +1136,9 @@ pmode_register_operand (op, mode)
This variable is 1 in that case. */
int while_newra = 0;
+/* Return 1 if OP should match a MATCH_SCRATCH, i.e., if it is a SCRATCH
+ or a hard register. */
+
int
scratch_operand (op, mode)
rtx op;
@@ -1146,7 +1149,8 @@ scratch_operand (op, mode)
return (GET_CODE (op) == SCRATCH
|| (GET_CODE (op) == REG
- && (REGNO (op) < FIRST_PSEUDO_REGISTER || while_newra)));
+ && (REGNO (op) < FIRST_PSEUDO_REGISTER || while_newra
+ || (newra_in_progress && !SPILL_SLOT_P (REGNO (op))))));
}
/* Return 1 if OP is a valid immediate operand for mode MODE.
@@ -2074,6 +2078,32 @@ mode_independent_operand (op, mode)
lose: ATTRIBUTE_UNUSED_LABEL
return 0;
}
+
+static int
+does_contain_spill_pseudos (x)
+ rtx x;
+{
+ int i;
+ const char *fmt;
+ if (GET_CODE (x) == REG && SPILL_SLOT_P (REGNO (x)))
+ return 1;
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ {
+ if (does_contain_spill_pseudos (XEXP (x, i)))
+ return 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (does_contain_spill_pseudos (XVECEXP (x, i, j)))
+ return 1;
+ }
+ return 0;
+}
+
/* Like extract_insn, but save insn extracted and don't extract again, when
called again for the same insn expecting that recog_data still contain the
@@ -2496,10 +2526,14 @@ constrain_operands (strict)
/* p is used for address_operands. When we are called by
gen_reload, no one will have checked that the address is
strictly valid, i.e., that all pseudos requiring hard regs
- have gotten them. */
- if (strict <= 0
- || (strict_memory_address_p (recog_data.operand_mode[opno],
- op)))
+ have gotten them. While the new ra is running pre reload
+ will have ensured, that this is basically of a form of
+ an address. */
+ if (newra_in_progress && does_contain_spill_pseudos (op))
+ ;
+ else if (strict <= 0 || newra_in_progress
+ || (strict_memory_address_p (recog_data.operand_mode[opno],
+ op)))
win = 1;
break;
@@ -2511,7 +2545,7 @@ constrain_operands (strict)
if (strict < 0
|| GENERAL_REGS == ALL_REGS
|| GET_CODE (op) != REG
- || (reload_in_progress
+ || ((reload_in_progress || newra_in_progress)
&& REGNO (op) >= FIRST_PSEUDO_REGISTER)
|| reg_fits_class_p (op, GENERAL_REGS, offset, mode))
win = 1;
@@ -2530,7 +2564,11 @@ constrain_operands (strict)
|| (strict < 0 && CONSTANT_P (op))
/* During reload, accept a pseudo */
|| (reload_in_progress && GET_CODE (op) == REG
- && REGNO (op) >= FIRST_PSEUDO_REGISTER))
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ /* With the graph coloring allocator only accept stack
+ pseudos, but not normal ones. */
+ || (newra_in_progress && GET_CODE (op) == REG
+ && SPILL_SLOT_P (REGNO (op))))
win = 1;
break;
@@ -2595,7 +2633,12 @@ constrain_operands (strict)
case 'V':
if (GET_CODE (op) == MEM
- && ((strict > 0 && ! offsettable_memref_p (op))
+ /* Bah. We can't call the memref checkers if the addresses
+ contain pseudos, which they do while the new ra is
+ running. */
+ && ((newra_in_progress && !offsettable_nonstrict_memref_p (op))
+ || (!newra_in_progress && strict > 0
+ && ! offsettable_memref_p (op))
|| (strict < 0
&& !(CONSTANT_P (op) || GET_CODE (op) == MEM))
|| (reload_in_progress
@@ -2605,14 +2648,18 @@ constrain_operands (strict)
break;
case 'o':
- if ((strict > 0 && offsettable_memref_p (op))
+ if ((newra_in_progress && offsettable_nonstrict_memref_p (op))
+ || (!newra_in_progress && strict > 0
+ && offsettable_memref_p (op))
|| (strict == 0 && offsettable_nonstrict_memref_p (op))
/* Before reload, accept what reload can handle. */
|| (strict < 0
&& (CONSTANT_P (op) || GET_CODE (op) == MEM))
/* During reload, accept a pseudo */
|| (reload_in_progress && GET_CODE (op) == REG
- && REGNO (op) >= FIRST_PSEUDO_REGISTER))
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ || (newra_in_progress && GET_CODE (op) == REG
+ && SPILL_SLOT_P (REGNO (op))))
win = 1;
break;
@@ -2621,15 +2668,26 @@ constrain_operands (strict)
enum reg_class class;
class = (c == 'r' ? GENERAL_REGS : REG_CLASS_FROM_LETTER (c));
+ /* In the new ra handle all REGs first. We accept all
+ normal pseudos, but no stack pseudos, as they represent
+ memory slots. */
if (class != NO_REGS)
{
+ /* If we accept any reg at all, then accept everything
+ if we do extra lax checking, accept all pseudo regs
+ and scratches if we do nonstrict checking, accept
+ registers which fit the needed class, and if the new
+ ra is running also accept non stack pseudos. */
if (strict < 0
|| (strict == 0
&& GET_CODE (op) == REG
&& REGNO (op) >= FIRST_PSEUDO_REGISTER)
|| (strict == 0 && GET_CODE (op) == SCRATCH)
|| (GET_CODE (op) == REG
- && reg_fits_class_p (op, class, offset, mode)))
+ && ((newra_in_progress
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER
+ && !SPILL_SLOT_P (REGNO (op)))
+ || reg_fits_class_p (op, class, offset, mode))))
win = 1;
}
#ifdef EXTRA_CONSTRAINT
@@ -2677,13 +2735,15 @@ constrain_operands (strict)
/* See if any earlyclobber operand conflicts with some other
operand. */
- if (strict > 0)
+ if (strict > 0 && !newra_in_progress)
for (eopno = 0; eopno < recog_data.n_operands; eopno++)
/* Ignore earlyclobber operands now in memory,
because we would often report failure when we have
two memory operands, one of which was formerly a REG. */
if (earlyclobber[eopno]
- && GET_CODE (recog_data.operand[eopno]) == REG)
+ && GET_CODE (recog_data.operand[eopno]) == REG
+ && !(newra_in_progress
+ && SPILL_SLOT_P (REGNO (recog_data.operand[eopno]))))
for (opno = 0; opno < recog_data.n_operands; opno++)
if ((GET_CODE (recog_data.operand[opno]) == MEM
|| recog_data.operand_type[opno] != OP_OUT)
diff -urpN work-gcc.orig/gcc/reg-stack.c work-gcc/gcc/reg-stack.c
--- work-gcc.orig/gcc/reg-stack.c 2003-01-23 16:47:42.000000000 +0100
+++ work-gcc/gcc/reg-stack.c 2003-06-16 17:37:31.000000000 +0200
@@ -1095,7 +1095,11 @@ move_for_stack_reg (insn, regstack, pat)
{
emit_pop_insn (insn, regstack, src, EMIT_AFTER);
- delete_insn (insn);
+ /* There might still be some stray REG_EH_REGION notes
+ on the insn, _although_ it can't possibly trap anymore
+ (otherwise we wouldn't try to delete it). Simply remove
+ the edges then too. */
+ delete_insn_and_edges (insn);
return;
}
@@ -1104,7 +1108,7 @@ move_for_stack_reg (insn, regstack, pat)
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (src));
- delete_insn (insn);
+ delete_insn_and_edges (insn);
return;
}
@@ -1121,7 +1125,7 @@ move_for_stack_reg (insn, regstack, pat)
if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
emit_pop_insn (insn, regstack, dest, EMIT_AFTER);
- delete_insn (insn);
+ delete_insn_and_edges (insn);
return;
}
diff -urpN work-gcc.orig/gcc/regclass.c work-gcc/gcc/regclass.c
--- work-gcc.orig/gcc/regclass.c 2003-01-23 16:47:42.000000000 +0100
+++ work-gcc/gcc/regclass.c 2003-06-16 17:37:31.000000000 +0200
@@ -206,12 +206,12 @@ static int move_cost[MAX_MACHINE_MODE][N
/* Similar, but here we don't have to move if the first index is a subset
of the second so in that case the cost is zero. */
-static int may_move_in_cost[MAX_MACHINE_MODE][N_REG_CLASSES][N_REG_CLASSES];
+int may_move_in_cost[MAX_MACHINE_MODE][N_REG_CLASSES][N_REG_CLASSES];
/* Similar, but here we don't have to move if the first index is a superset
of the second so in that case the cost is zero. */
-static int may_move_out_cost[MAX_MACHINE_MODE][N_REG_CLASSES][N_REG_CLASSES];
+int may_move_out_cost[MAX_MACHINE_MODE][N_REG_CLASSES][N_REG_CLASSES];
#ifdef FORBIDDEN_INC_DEC_CLASSES
@@ -858,8 +858,6 @@ static void dump_regclass PARAMS ((FILE
static void record_reg_classes PARAMS ((int, int, rtx *, enum machine_mode *,
const char **, rtx,
struct costs *, struct reg_pref *));
-static int copy_cost PARAMS ((rtx, enum machine_mode,
- enum reg_class, int));
static void record_address_regs PARAMS ((rtx, enum reg_class, int));
#ifdef FORBIDDEN_INC_DEC_CLASSES
static int auto_inc_dec_reg_p PARAMS ((rtx, enum machine_mode));
@@ -1869,7 +1867,7 @@ record_reg_classes (n_alts, n_ops, ops,
X must not be a pseudo. */
-static int
+int
copy_cost (x, mode, class, to_p)
rtx x;
enum machine_mode mode ATTRIBUTE_UNUSED;
diff -urpN work-gcc.orig/gcc/regs.h work-gcc/gcc/regs.h
--- work-gcc.orig/gcc/regs.h 2003-01-23 16:47:42.000000000 +0100
+++ work-gcc/gcc/regs.h 2003-06-16 17:37:31.000000000 +0200
@@ -169,6 +169,10 @@ extern const char * reg_names[FIRST_PSEU
extern enum machine_mode reg_raw_mode[FIRST_PSEUDO_REGISTER];
+/* From regclass.c. */
+extern int may_move_in_cost[MAX_MACHINE_MODE][N_REG_CLASSES][N_REG_CLASSES];
+extern int may_move_out_cost[MAX_MACHINE_MODE][N_REG_CLASSES][N_REG_CLASSES];
+
/* Vector indexed by regno; gives uid of first insn using that reg.
This is computed by reg_scan for use by cse and loop.
It is sometimes adjusted for subsequent changes during loop,
diff -urpN work-gcc.orig/gcc/rtl.h work-gcc/gcc/rtl.h
--- work-gcc.orig/gcc/rtl.h 2003-01-23 16:47:43.000000000 +0100
+++ work-gcc/gcc/rtl.h 2003-06-16 17:37:31.000000000 +0200
@@ -250,6 +250,11 @@ struct rtvec_def GTY(()) {
/* Predicate yielding nonzero iff X is an rtl for a register. */
#define REG_P(X) (GET_CODE (X) == REG)
+/* Predicate yielding nonzero iff X is an rtl for a register or a subreg
+ of a register. */
+#define REG_OR_SUBREG_P(X) (REG_P (X) || (GET_CODE (X) == SUBREG \
+ && REG_P (SUBREG_REG (X))))
+
/* Predicate yielding nonzero iff X is a label insn. */
#define LABEL_P(X) (GET_CODE (X) == CODE_LABEL)
@@ -1614,6 +1619,9 @@ extern void note_stores PARAMS ((rtx,
extern void note_uses PARAMS ((rtx *,
void (*) (rtx *, void *),
void *));
+extern void note_uses_partial PARAMS ((rtx *,
+ void (*) (rtx *, void *),
+ void *));
extern rtx reg_set_last PARAMS ((rtx, rtx));
extern int dead_or_set_p PARAMS ((rtx, rtx));
extern int dead_or_set_regno_p PARAMS ((rtx, unsigned int));
@@ -1883,6 +1891,12 @@ extern int reload_completed;
extern int reload_in_progress;
+/* Set to 1 while the graph coloring register allocator is in progress.
+ In that case only certain pseudo register can be replaced by stack
+ references. */
+
+extern int newra_in_progress;
+
/* If this is nonzero, we do not bother generating VOLATILE
around volatile memory references, and we are willing to
output indirect addresses. If cse is to follow, we reject
@@ -2110,6 +2124,8 @@ extern void regclass PARAMS ((rtx, int
extern void reg_scan PARAMS ((rtx, unsigned int, int));
extern void reg_scan_update PARAMS ((rtx, rtx, unsigned int));
extern void fix_register PARAMS ((const char *, int, int));
+extern int copy_cost PARAMS ((rtx, enum machine_mode,
+ enum reg_class, int));
#ifdef HARD_CONST
extern void cannot_change_mode_set_regs PARAMS ((HARD_REG_SET *,
enum machine_mode,
diff -urpN work-gcc.orig/gcc/rtlanal.c work-gcc/gcc/rtlanal.c
--- work-gcc.orig/gcc/rtlanal.c 2003-01-23 16:47:43.000000000 +0100
+++ work-gcc/gcc/rtlanal.c 2003-06-16 17:37:31.000000000 +0200
@@ -40,6 +40,7 @@ static int computed_jump_p_1 PARAMS ((rt
static void parms_set PARAMS ((rtx, rtx, void *));
static bool hoist_test_store PARAMS ((rtx, rtx, regset));
static void hoist_update_store PARAMS ((rtx, rtx *, rtx, rtx));
+static void note_uses_1 PARAMS ((rtx *, void (*fun) PARAMS ((rtx *, void*)), void *, int));
/* Bit flags that specify the machine subtype we are compiling for.
Bits are tested using macros TARGET_... defined in the tm.h file
@@ -1657,6 +1658,25 @@ note_uses (pbody, fun, data)
void (*fun) PARAMS ((rtx *, void *));
void *data;
{
+ note_uses_1 (pbody, fun, data, 0);
+}
+
+void
+note_uses_partial (pbody, fun, data)
+ rtx *pbody;
+ void (*fun) PARAMS ((rtx *, void *));
+ void *data;
+{
+ note_uses_1 (pbody, fun, data, 1);
+}
+
+static void
+note_uses_1 (pbody, fun, data, partial)
+ rtx *pbody;
+ void (*fun) PARAMS ((rtx *, void *));
+ void *data;
+ int partial;
+{
rtx body = *pbody;
int i;
@@ -1703,6 +1723,7 @@ note_uses (pbody, fun, data)
case SET:
{
rtx dest = SET_DEST (body);
+ rtx *loc = NULL;
/* For sets we replace everything in source plus registers in memory
expression in store and operands of a ZERO_EXTRACT. */
@@ -1710,15 +1731,22 @@ note_uses (pbody, fun, data)
if (GET_CODE (dest) == ZERO_EXTRACT)
{
+ loc = &XEXP (dest, 0);
(*fun) (&XEXP (dest, 1), data);
(*fun) (&XEXP (dest, 2), data);
}
while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART)
- dest = XEXP (dest, 0);
+ {
+ if (!loc && GET_CODE (dest) == STRICT_LOW_PART)
+ loc = &XEXP (dest, 0);
+ dest = XEXP (dest, 0);
+ }
if (GET_CODE (dest) == MEM)
(*fun) (&XEXP (dest, 0), data);
+ else if (partial && loc)
+ (*fun) (loc, data);
}
return;
diff -urpN work-gcc.orig/gcc/version.c work-gcc/gcc/version.c
--- work-gcc.orig/gcc/version.c 2003-01-23 16:47:50.000000000 +0100
+++ work-gcc/gcc/version.c 2003-06-16 17:37:31.000000000 +0200
@@ -6,7 +6,7 @@
please modify this string to indicate that, e.g. by putting your
organization's name in parentheses at the end of the string. */
-const char version_string[] = "3.3 20021119 (experimental)";
+const char version_string[] = "3.3 20021119 (experimental) (new RA)";
/* This is the location of the online document giving instructions for
reporting bugs. If you distribute a modified version of GCC,
More information about the Gcc-patches
mailing list