summaryrefslogtreecommitdiffstats
path: root/contrib/gcc/reorg.c
diff options
context:
space:
mode:
authorobrien <obrien@FreeBSD.org>1999-08-26 09:30:50 +0000
committerobrien <obrien@FreeBSD.org>1999-08-26 09:30:50 +0000
commit0bedf4fb30066e5e1d4342a1d3914dae7d37cba7 (patch)
tree68d8110b41afd0ebbf39167b1a4918eea667a7c5 /contrib/gcc/reorg.c
parentd4db5fb866b7ad5216abd5047774a3973b9901a9 (diff)
downloadFreeBSD-src-0bedf4fb30066e5e1d4342a1d3914dae7d37cba7.zip
FreeBSD-src-0bedf4fb30066e5e1d4342a1d3914dae7d37cba7.tar.gz
Virgin import of gcc from EGCS 1.1.2
Diffstat (limited to 'contrib/gcc/reorg.c')
-rw-r--r--contrib/gcc/reorg.c698
1 files changed, 498 insertions, 200 deletions
diff --git a/contrib/gcc/reorg.c b/contrib/gcc/reorg.c
index edd76d5..4e823e4 100644
--- a/contrib/gcc/reorg.c
+++ b/contrib/gcc/reorg.c
@@ -1,5 +1,5 @@
/* Perform instruction reorganizations for delay slot filling.
- Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+ Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu).
Hacked by Michael Tiemann (tiemann@cygnus.com).
@@ -115,9 +115,10 @@ Boston, MA 02111-1307, USA. */
The HP-PA can conditionally nullify insns, providing a similar
effect to the ARM, differing mostly in which insn is "in charge". */
-#include <stdio.h>
#include "config.h"
+#include "system.h"
#include "rtl.h"
+#include "expr.h"
#include "insn-config.h"
#include "conditions.h"
#include "hard-reg-set.h"
@@ -130,6 +131,13 @@ Boston, MA 02111-1307, USA. */
#include "obstack.h"
#include "insn-attr.h"
+/* Import list of registers used as spill regs from reload. */
+extern HARD_REG_SET used_spill_regs;
+
+/* Import highest label used in function at end of reload. */
+extern int max_label_num_after_reload;
+
+
#ifdef DELAY_SLOTS
#define obstack_chunk_alloc xmalloc
@@ -163,8 +171,8 @@ static rtx *unfilled_firstobj;
struct resources
{
char memory; /* Insn sets or needs a memory location. */
- char unch_memory; /* Insn sets of needs a "unchanging" MEM. */
- char volatil; /* Insn sets or needs a volatile memory loc. */
+ char unch_memory; /* Insn sets of needs a "unchanging" MEM. */
+ char volatil; /* Insn sets or needs a volatile memory loc. */
char cc; /* Insn sets or needs the condition codes. */
HARD_REG_SET regs; /* Which registers are set or needed. */
};
@@ -222,9 +230,9 @@ static int stop_search_p PROTO((rtx, int));
static int resource_conflicts_p PROTO((struct resources *,
struct resources *));
static int insn_references_resource_p PROTO((rtx, struct resources *, int));
-static int insn_sets_resources_p PROTO((rtx, struct resources *, int));
+static int insn_sets_resource_p PROTO((rtx, struct resources *, int));
static rtx find_end_label PROTO((void));
-static rtx emit_delay_sequence PROTO((rtx, rtx, int, int));
+static rtx emit_delay_sequence PROTO((rtx, rtx, int));
static rtx add_to_delay_list PROTO((rtx, rtx));
static void delete_from_delay_slot PROTO((rtx));
static void delete_scheduled_jump PROTO((rtx));
@@ -252,21 +260,25 @@ static int find_basic_block PROTO((rtx));
static void update_block PROTO((rtx, rtx));
static int reorg_redirect_jump PROTO((rtx, rtx));
static void update_reg_dead_notes PROTO((rtx, rtx));
+static void fix_reg_dead_note PROTO((rtx, rtx));
static void update_reg_unused_notes PROTO((rtx, rtx));
static void update_live_status PROTO((rtx, rtx));
static rtx next_insn_no_annul PROTO((rtx));
+static rtx find_dead_or_set_registers PROTO ((rtx, struct resources *, rtx *,
+ int, struct resources,
+ struct resources));
static void mark_target_live_regs PROTO((rtx, struct resources *));
-static void fill_simple_delay_slots PROTO((rtx, int));
+static void fill_simple_delay_slots PROTO((int));
static rtx fill_slots_from_thread PROTO((rtx, rtx, rtx, rtx, int, int,
- int, int, int, int *));
-static void fill_eager_delay_slots PROTO((rtx));
+ int, int, int *, rtx));
+static void fill_eager_delay_slots PROTO((void));
static void relax_delay_slots PROTO((rtx));
static void make_return_insns PROTO((rtx));
static int redirect_with_delay_slots_safe_p PROTO ((rtx, rtx, rtx));
static int redirect_with_delay_list_safe_p PROTO ((rtx, rtx, rtx));
/* Given X, some rtl, and RES, a pointer to a `struct resource', mark
- which resources are references by the insn. If INCLUDE_CALLED_ROUTINE
+ which resources are references by the insn. If INCLUDE_DELAYED_EFFECTS
is TRUE, resources used by the called routine will be included for
CALL_INSNs. */
@@ -332,6 +344,10 @@ mark_referenced_resources (x, res, include_delayed_effects)
res->volatil = 1;
return;
+ case TRAP_IF:
+ res->volatil = 1;
+ break;
+
case ASM_OPERANDS:
res->volatil = MEM_VOLATILE_P (x);
@@ -386,7 +402,7 @@ mark_referenced_resources (x, res, include_delayed_effects)
rtx next = NEXT_INSN (x);
int i;
- /* If we are part of a delay slot sequence, point at the SEQUENCE. */
+ /* If we are part of a delay slot sequence, point at the SEQUENCE. */
if (NEXT_INSN (insn) != x)
{
next = NEXT_INSN (NEXT_INSN (insn));
@@ -445,7 +461,7 @@ mark_referenced_resources (x, res, include_delayed_effects)
}
}
- /* ... fall through to other INSN processing ... */
+ /* ... fall through to other INSN processing ... */
case INSN:
case JUMP_INSN:
@@ -459,6 +475,9 @@ mark_referenced_resources (x, res, include_delayed_effects)
/* No special processing, just speed up. */
mark_referenced_resources (PATTERN (x), res, include_delayed_effects);
return;
+
+ default:
+ break;
}
/* Process each sub-expression and flag what it needs. */
@@ -478,9 +497,10 @@ mark_referenced_resources (x, res, include_delayed_effects)
}
}
-/* Given X, a part of an insn, and a pointer to a `struct resource', RES,
- indicate which resources are modified by the insn. If INCLUDE_CALLED_ROUTINE
- is nonzero, also mark resources potentially set by the called routine.
+/* Given X, a part of an insn, and a pointer to a `struct resource',
+ RES, indicate which resources are modified by the insn. If
+ INCLUDE_DELAYED_EFFECTS is nonzero, also mark resources potentially
+ set by the called routine.
If IN_DEST is nonzero, it means we are inside a SET. Otherwise,
objects are being referenced instead of set.
@@ -559,7 +579,7 @@ mark_set_resources (x, res, in_dest, include_delayed_effects)
SET_HARD_REG_SET (res->regs);
}
- /* ... and also what it's RTL says it modifies, if anything. */
+ /* ... and also what its RTL says it modifies, if anything. */
case JUMP_INSN:
case INSN:
@@ -648,6 +668,9 @@ mark_set_resources (x, res, in_dest, include_delayed_effects)
for (i = 0; i < HARD_REGNO_NREGS (REGNO (x), GET_MODE (x)); i++)
SET_HARD_REG_BIT (res->regs, REGNO (x) + i);
return;
+
+ default:
+ break;
}
/* Process each sub-expression and flag what it needs. */
@@ -731,7 +754,7 @@ resource_conflicts_p (res1, res2)
}
/* Return TRUE if any resource marked in RES, a `struct resources', is
- referenced by INSN. If INCLUDE_CALLED_ROUTINE is set, return if the called
+ referenced by INSN. If INCLUDE_DELAYED_EFFECTS is set, return if the called
routine is using those resources.
We compute this by computing all the resources referenced by INSN and
@@ -753,7 +776,7 @@ insn_references_resource_p (insn, res, include_delayed_effects)
}
/* Return TRUE if INSN modifies resources that are marked in RES.
- INCLUDE_CALLED_ROUTINE is set if the actions of that routine should be
+ INCLUDE_DELAYED_EFFECTS is set if the actions of that routine should be
included. CC0 is only modified if it is explicitly set; see comments
in front of mark_set_resources for details. */
@@ -804,7 +827,7 @@ find_end_label ()
end_of_function_label = gen_label_rtx ();
LABEL_NUSES (end_of_function_label) = 0;
- /* Put the label before an USE insns that may proceed the RETURN insn. */
+ /* Put the label before an USE insns that may proceed the RETURN insn. */
while (GET_CODE (temp) == USE)
temp = PREV_INSN (temp);
@@ -851,24 +874,23 @@ find_end_label ()
Returns the SEQUENCE that replaces INSN. */
static rtx
-emit_delay_sequence (insn, list, length, avail)
+emit_delay_sequence (insn, list, length)
rtx insn;
rtx list;
int length;
- int avail;
{
register int i = 1;
register rtx li;
int had_barrier = 0;
- /* Allocate the the rtvec to hold the insns and the SEQUENCE. */
+ /* Allocate the rtvec to hold the insns and the SEQUENCE. */
rtvec seqv = rtvec_alloc (length + 1);
- rtx seq = gen_rtx (SEQUENCE, VOIDmode, seqv);
+ rtx seq = gen_rtx_SEQUENCE (VOIDmode, seqv);
rtx seq_insn = make_insn_raw (seq);
rtx first = get_insns ();
rtx last = get_last_insn ();
- /* Make a copy of the insn having delay slots. */
+ /* Make a copy of the insn having delay slots. */
rtx delay_insn = copy_rtx (insn);
/* If INSN is followed by a BARRIER, delete the BARRIER since it will only
@@ -885,15 +907,22 @@ emit_delay_sequence (insn, list, length, avail)
NEXT_INSN (seq_insn) = NEXT_INSN (insn);
PREV_INSN (seq_insn) = PREV_INSN (insn);
+ if (insn != last)
+ PREV_INSN (NEXT_INSN (seq_insn)) = seq_insn;
+
+ if (insn != first)
+ NEXT_INSN (PREV_INSN (seq_insn)) = seq_insn;
+
+ /* Note the calls to set_new_first_and_last_insn must occur after
+ SEQ_INSN has been completely spliced into the insn stream.
+
+ Otherwise CUR_INSN_UID will get set to an incorrect value because
+ set_new_first_and_last_insn will not find SEQ_INSN in the chain. */
if (insn == last)
set_new_first_and_last_insn (first, seq_insn);
- else
- PREV_INSN (NEXT_INSN (seq_insn)) = seq_insn;
if (insn == first)
set_new_first_and_last_insn (seq_insn, last);
- else
- NEXT_INSN (PREV_INSN (seq_insn)) = seq_insn;
/* Build our SEQUENCE and rebuild the insn chain. */
XVECEXP (seq, 0, 0) = delay_insn;
@@ -954,7 +983,7 @@ add_to_delay_list (insn, delay_list)
rtx delay_list;
{
/* If we have an empty list, just make a new list element. If
- INSN has it's block number recorded, clear it since we may
+ INSN has its block number recorded, clear it since we may
be moving the insn to a new block. */
if (delay_list == 0)
@@ -969,7 +998,7 @@ add_to_delay_list (insn, delay_list)
if (tinfo)
tinfo->block = -1;
- return gen_rtx (INSN_LIST, VOIDmode, insn, NULL_RTX);
+ return gen_rtx_INSN_LIST (VOIDmode, insn, NULL_RTX);
}
/* Otherwise this must be an INSN_LIST. Add INSN to the end of the
@@ -979,7 +1008,7 @@ add_to_delay_list (insn, delay_list)
return delay_list;
}
-/* Delete INSN from the the delay slot of the insn that it is in. This may
+/* Delete INSN from the delay slot of the insn that it is in. This may
produce an insn without anything in its delay slots. */
static void
@@ -1023,7 +1052,7 @@ delete_from_delay_slot (insn)
/* If there are any delay insns, remit them. Otherwise clear the
annul flag. */
if (delay_list)
- trial = emit_delay_sequence (trial, delay_list, XVECLEN (seq, 0) - 2, 0);
+ trial = emit_delay_sequence (trial, delay_list, XVECLEN (seq, 0) - 2);
else
INSN_ANNULLED_BRANCH_P (trial) = 0;
@@ -1218,6 +1247,7 @@ optimize_skip (insn)
Non conditional branches return no direction information and
are predicted as very likely taken. */
+
static int
get_jump_flags (insn, label)
rtx insn, label;
@@ -1314,6 +1344,9 @@ rare_destination (insn)
next = JUMP_LABEL (insn);
else
return 0;
+
+ default:
+ break;
}
}
@@ -1340,6 +1373,26 @@ mostly_true_jump (jump_insn, condition)
int rare_dest = rare_destination (target_label);
int rare_fallthrough = rare_destination (NEXT_INSN (jump_insn));
+ /* If branch probabilities are available, then use that number since it
+ always gives a correct answer. */
+ if (flag_branch_probabilities)
+ {
+ rtx note = find_reg_note (jump_insn, REG_BR_PROB, 0);;
+ if (note)
+ {
+ int prob = XINT (note, 0);
+
+ if (prob >= REG_BR_PROB_BASE * 9 / 10)
+ return 2;
+ else if (prob >= REG_BR_PROB_BASE / 2)
+ return 1;
+ else if (prob >= REG_BR_PROB_BASE / 10)
+ return 0;
+ else
+ return -1;
+ }
+ }
+
/* If this is a branch outside a loop, it is highly unlikely. */
if (GET_CODE (PATTERN (jump_insn)) == SET
&& GET_CODE (SET_SRC (PATTERN (jump_insn))) == IF_THEN_ELSE
@@ -1373,7 +1426,7 @@ mostly_true_jump (jump_insn, condition)
}
/* Look at the relative rarities of the fallthrough and destination. If
- they differ, we can predict the branch that way. */
+ they differ, we can predict the branch that way. */
switch (rare_fallthrough - rare_dest)
{
@@ -1416,6 +1469,9 @@ mostly_true_jump (jump_insn, condition)
if (XEXP (condition, 1) == const0_rtx)
return 1;
break;
+
+ default:
+ break;
}
/* Predict backward branches usually take, forward branches usually not. If
@@ -1465,9 +1521,9 @@ get_branch_condition (insn, target)
|| (GET_CODE (XEXP (src, 2)) == LABEL_REF
&& XEXP (XEXP (src, 2), 0) == target))
&& XEXP (src, 1) == pc_rtx)
- return gen_rtx (reverse_condition (GET_CODE (XEXP (src, 0))),
- GET_MODE (XEXP (src, 0)),
- XEXP (XEXP (src, 0), 0), XEXP (XEXP (src, 0), 1));
+ return gen_rtx_fmt_ee (reverse_condition (GET_CODE (XEXP (src, 0))),
+ GET_MODE (XEXP (src, 0)),
+ XEXP (XEXP (src, 0), 0), XEXP (XEXP (src, 0), 1));
return 0;
}
@@ -1507,7 +1563,7 @@ static int
redirect_with_delay_slots_safe_p (jump, newlabel, seq)
rtx jump, newlabel, seq;
{
- int flags, slots, i;
+ int flags, i;
rtx pat = PATTERN (seq);
/* Make sure all the delay slots of this jump would still
@@ -1842,7 +1898,7 @@ try_merge_delay_insns (insn, thread)
INSN_FROM_TARGET_P (next_to_match) = 0;
}
else
- merged_insns = gen_rtx (INSN_LIST, VOIDmode, trial, merged_insns);
+ merged_insns = gen_rtx_INSN_LIST (VOIDmode, trial, merged_insns);
if (++slot_number == num_slots)
break;
@@ -1890,8 +1946,8 @@ try_merge_delay_insns (insn, thread)
INSN_FROM_TARGET_P (next_to_match) = 0;
}
else
- merged_insns = gen_rtx (INSN_LIST, SImode, dtrial,
- merged_insns);
+ merged_insns = gen_rtx_INSN_LIST (SImode, dtrial,
+ merged_insns);
if (++slot_number == num_slots)
break;
@@ -1903,7 +1959,7 @@ try_merge_delay_insns (insn, thread)
/* If all insns in the delay slot have been matched and we were previously
annulling the branch, we need not any more. In that case delete all the
- merged insns. Also clear the INSN_FROM_TARGET_P bit of each insn the
+ merged insns. Also clear the INSN_FROM_TARGET_P bit of each insn in
the delay list so that we know that it isn't only being used at the
target. */
if (slot_number == num_slots && annul_p)
@@ -1960,6 +2016,11 @@ redundant_insn (insn, target, delay_list)
struct resources needed, set;
int i;
+ /* If INSN has any REG_UNUSED notes, it can't match anything since we
+ are allowed to not actually assign to such a register. */
+ if (find_reg_note (insn, REG_UNUSED, NULL_RTX) != 0)
+ return 0;
+
/* Scan backwards looking for a match. */
for (trial = PREV_INSN (target); trial; trial = PREV_INSN (trial))
{
@@ -1998,7 +2059,8 @@ redundant_insn (insn, target, delay_list)
resource requirements as we go. */
for (i = XVECLEN (pat, 0) - 1; i > 0; i--)
if (GET_CODE (XVECEXP (pat, 0, i)) == GET_CODE (insn)
- && rtx_equal_p (PATTERN (XVECEXP (pat, 0, i)), ipat))
+ && rtx_equal_p (PATTERN (XVECEXP (pat, 0, i)), ipat)
+ && ! find_reg_note (XVECEXP (pat, 0, i), REG_UNUSED, NULL_RTX))
break;
/* If found a match, exit this loop early. */
@@ -2006,7 +2068,8 @@ redundant_insn (insn, target, delay_list)
break;
}
- else if (GET_CODE (trial) == GET_CODE (insn) && rtx_equal_p (pat, ipat))
+ else if (GET_CODE (trial) == GET_CODE (insn) && rtx_equal_p (pat, ipat)
+ && ! find_reg_note (trial, REG_UNUSED, NULL_RTX))
break;
}
@@ -2079,7 +2142,7 @@ redundant_insn (insn, target, delay_list)
if (GET_CODE (XVECEXP (pat, 0, 0)) == CALL_INSN)
return 0;
- /* If this this is an INSN or JUMP_INSN with delayed effects, it
+ /* If this is an INSN or JUMP_INSN with delayed effects, it
is hard to track the resource needs properly, so give up. */
#ifdef INSN_SETS_ARE_DELAYED
@@ -2241,7 +2304,7 @@ update_block (insn, where)
if (INSN_FROM_TARGET_P (insn))
return;
- emit_insn_before (gen_rtx (USE, VOIDmode, insn), where);
+ emit_insn_before (gen_rtx_USE (VOIDmode, insn), where);
/* INSN might be making a value live in a block where it didn't use to
be. So recompute liveness information for this block. */
@@ -2304,6 +2367,38 @@ update_reg_dead_notes (insn, delayed_insn)
}
}
+/* Called when an insn redundant with start_insn is deleted. If there
+ is a REG_DEAD note for the target of start_insn between start_insn
+ and stop_insn, then the REG_DEAD note needs to be deleted since the
+ value no longer dies there.
+
+ If the REG_DEAD note isn't deleted, then mark_target_live_regs may be
+ confused into thinking the register is dead. */
+
+static void
+fix_reg_dead_note (start_insn, stop_insn)
+ rtx start_insn, stop_insn;
+{
+ rtx p, link, next;
+
+ for (p = next_nonnote_insn (start_insn); p != stop_insn;
+ p = next_nonnote_insn (p))
+ for (link = REG_NOTES (p); link; link = next)
+ {
+ next = XEXP (link, 1);
+
+ if (REG_NOTE_KIND (link) != REG_DEAD
+ || GET_CODE (XEXP (link, 0)) != REG)
+ continue;
+
+ if (reg_set_p (XEXP (link, 0), PATTERN (start_insn)))
+ {
+ remove_note (p, link);
+ return;
+ }
+ }
+}
+
/* Delete any REG_UNUSED notes that exist on INSN but not on REDUNDANT_INSN.
This handles the case of udivmodXi4 instructions which optimize their
@@ -2315,7 +2410,7 @@ static void
update_reg_unused_notes (insn, redundant_insn)
rtx insn, redundant_insn;
{
- rtx p, link, next;
+ rtx link, next;
for (link = REG_NOTES (insn); link; link = next)
{
@@ -2399,6 +2494,191 @@ next_insn_no_annul (insn)
return insn;
}
+/* A subroutine of mark_target_live_regs. Search forward from TARGET
+ looking for registers that are set before they are used. These are dead.
+ Stop after passing a few conditional jumps, and/or a small
+ number of unconditional branches. */
+
+static rtx
+find_dead_or_set_registers (target, res, jump_target, jump_count, set, needed)
+ rtx target;
+ struct resources *res;
+ rtx *jump_target;
+ int jump_count;
+ struct resources set, needed;
+{
+ HARD_REG_SET scratch;
+ rtx insn, next;
+ rtx jump_insn = 0;
+ int i;
+
+ for (insn = target; insn; insn = next)
+ {
+ rtx this_jump_insn = insn;
+
+ next = NEXT_INSN (insn);
+ switch (GET_CODE (insn))
+ {
+ case CODE_LABEL:
+ /* After a label, any pending dead registers that weren't yet
+ used can be made dead. */
+ AND_COMPL_HARD_REG_SET (pending_dead_regs, needed.regs);
+ AND_COMPL_HARD_REG_SET (res->regs, pending_dead_regs);
+ CLEAR_HARD_REG_SET (pending_dead_regs);
+
+ if (CODE_LABEL_NUMBER (insn) < max_label_num_after_reload)
+ {
+ /* All spill registers are dead at a label, so kill all of the
+ ones that aren't needed also. */
+ COPY_HARD_REG_SET (scratch, used_spill_regs);
+ AND_COMPL_HARD_REG_SET (scratch, needed.regs);
+ AND_COMPL_HARD_REG_SET (res->regs, scratch);
+ }
+ continue;
+
+ case BARRIER:
+ case NOTE:
+ continue;
+
+ case INSN:
+ if (GET_CODE (PATTERN (insn)) == USE)
+ {
+ /* If INSN is a USE made by update_block, we care about the
+ underlying insn. Any registers set by the underlying insn
+ are live since the insn is being done somewhere else. */
+ if (GET_RTX_CLASS (GET_CODE (XEXP (PATTERN (insn), 0))) == 'i')
+ mark_set_resources (XEXP (PATTERN (insn), 0), res, 0, 1);
+
+ /* All other USE insns are to be ignored. */
+ continue;
+ }
+ else if (GET_CODE (PATTERN (insn)) == CLOBBER)
+ continue;
+ else if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+ {
+ /* An unconditional jump can be used to fill the delay slot
+ of a call, so search for a JUMP_INSN in any position. */
+ for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ {
+ this_jump_insn = XVECEXP (PATTERN (insn), 0, i);
+ if (GET_CODE (this_jump_insn) == JUMP_INSN)
+ break;
+ }
+ }
+
+ default:
+ break;
+ }
+
+ if (GET_CODE (this_jump_insn) == JUMP_INSN)
+ {
+ if (jump_count++ < 10)
+ {
+ if (simplejump_p (this_jump_insn)
+ || GET_CODE (PATTERN (this_jump_insn)) == RETURN)
+ {
+ next = JUMP_LABEL (this_jump_insn);
+ if (jump_insn == 0)
+ {
+ jump_insn = insn;
+ if (jump_target)
+ *jump_target = JUMP_LABEL (this_jump_insn);
+ }
+ }
+ else if (condjump_p (this_jump_insn)
+ || condjump_in_parallel_p (this_jump_insn))
+ {
+ struct resources target_set, target_res;
+ struct resources fallthrough_res;
+
+ /* We can handle conditional branches here by following
+ both paths, and then IOR the results of the two paths
+ together, which will give us registers that are dead
+ on both paths. Since this is expensive, we give it
+ a much higher cost than unconditional branches. The
+ cost was chosen so that we will follow at most 1
+ conditional branch. */
+
+ jump_count += 4;
+ if (jump_count >= 10)
+ break;
+
+ mark_referenced_resources (insn, &needed, 1);
+
+ /* For an annulled branch, mark_set_resources ignores slots
+ filled by instructions from the target. This is correct
+ if the branch is not taken. Since we are following both
+ paths from the branch, we must also compute correct info
+ if the branch is taken. We do this by inverting all of
+ the INSN_FROM_TARGET_P bits, calling mark_set_resources,
+ and then inverting the INSN_FROM_TARGET_P bits again. */
+
+ if (GET_CODE (PATTERN (insn)) == SEQUENCE
+ && INSN_ANNULLED_BRANCH_P (this_jump_insn))
+ {
+ for (i = 1; i < XVECLEN (PATTERN (insn), 0); i++)
+ INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i))
+ = ! INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i));
+
+ target_set = set;
+ mark_set_resources (insn, &target_set, 0, 1);
+
+ for (i = 1; i < XVECLEN (PATTERN (insn), 0); i++)
+ INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i))
+ = ! INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i));
+
+ mark_set_resources (insn, &set, 0, 1);
+ }
+ else
+ {
+ mark_set_resources (insn, &set, 0, 1);
+ target_set = set;
+ }
+
+ target_res = *res;
+ COPY_HARD_REG_SET (scratch, target_set.regs);
+ AND_COMPL_HARD_REG_SET (scratch, needed.regs);
+ AND_COMPL_HARD_REG_SET (target_res.regs, scratch);
+
+ fallthrough_res = *res;
+ COPY_HARD_REG_SET (scratch, set.regs);
+ AND_COMPL_HARD_REG_SET (scratch, needed.regs);
+ AND_COMPL_HARD_REG_SET (fallthrough_res.regs, scratch);
+
+ find_dead_or_set_registers (JUMP_LABEL (this_jump_insn),
+ &target_res, 0, jump_count,
+ target_set, needed);
+ find_dead_or_set_registers (next,
+ &fallthrough_res, 0, jump_count,
+ set, needed);
+ IOR_HARD_REG_SET (fallthrough_res.regs, target_res.regs);
+ AND_HARD_REG_SET (res->regs, fallthrough_res.regs);
+ break;
+ }
+ else
+ break;
+ }
+ else
+ {
+ /* Don't try this optimization if we expired our jump count
+ above, since that would mean there may be an infinite loop
+ in the function being compiled. */
+ jump_insn = 0;
+ break;
+ }
+ }
+
+ mark_referenced_resources (insn, &needed, 1);
+ mark_set_resources (insn, &set, 0, 1);
+
+ COPY_HARD_REG_SET (scratch, set.regs);
+ AND_COMPL_HARD_REG_SET (scratch, needed.regs);
+ AND_COMPL_HARD_REG_SET (res->regs, scratch);
+ }
+
+ return jump_insn;
+}
+
/* Set the resources that are live at TARGET.
If TARGET is zero, we refer to the end of the current function and can
@@ -2445,12 +2725,11 @@ mark_target_live_regs (target, res)
int b = -1;
int i;
struct target_info *tinfo;
- rtx insn, next;
+ rtx insn;
rtx jump_insn = 0;
rtx jump_target;
HARD_REG_SET scratch;
struct resources set, needed;
- int jump_count = 0;
/* Handle end of function. */
if (target == 0)
@@ -2509,8 +2788,7 @@ mark_target_live_regs (target, res)
if (b != -1)
{
regset regs_live = basic_block_live_at_start[b];
- int offset, j;
- REGSET_ELT_TYPE bit;
+ int j;
int regno;
rtx start_insn, stop_insn;
@@ -2518,26 +2796,18 @@ mark_target_live_regs (target, res)
marked live, plus live pseudo regs that have been renumbered to
hard regs. */
-#ifdef HARD_REG_SET
- current_live_regs = *regs_live;
-#else
- COPY_HARD_REG_SET (current_live_regs, regs_live);
-#endif
+ REG_SET_TO_HARD_REG_SET (current_live_regs, regs_live);
- for (offset = 0, i = 0; offset < regset_size; offset++)
- {
- if (regs_live[offset] == 0)
- i += REGSET_ELT_BITS;
- else
- for (bit = 1; bit && i < max_regno; bit <<= 1, i++)
- if ((regs_live[offset] & bit)
- && (regno = reg_renumber[i]) >= 0)
- for (j = regno;
- j < regno + HARD_REGNO_NREGS (regno,
- PSEUDO_REGNO_MODE (i));
- j++)
- SET_HARD_REG_BIT (current_live_regs, j);
- }
+ EXECUTE_IF_SET_IN_REG_SET
+ (regs_live, FIRST_PSEUDO_REGISTER, i,
+ {
+ if ((regno = reg_renumber[i]) >= 0)
+ for (j = regno;
+ j < regno + HARD_REGNO_NREGS (regno,
+ PSEUDO_REGNO_MODE (i));
+ j++)
+ SET_HARD_REG_BIT (current_live_regs, j);
+ });
/* Get starting and ending insn, handling the case where each might
be a SEQUENCE. */
@@ -2669,92 +2939,18 @@ mark_target_live_regs (target, res)
in use. This should happen only extremely rarely. */
SET_HARD_REG_SET (res->regs);
- /* Now step forward from TARGET looking for registers that are set before
- they are used. These are dead. If we pass a label, any pending dead
- registers that weren't yet used can be made dead. Stop when we pass a
- conditional JUMP_INSN; follow the first few unconditional branches. */
-
CLEAR_RESOURCE (&set);
CLEAR_RESOURCE (&needed);
- for (insn = target; insn; insn = next)
- {
- rtx this_jump_insn = insn;
-
- next = NEXT_INSN (insn);
- switch (GET_CODE (insn))
- {
- case CODE_LABEL:
- AND_COMPL_HARD_REG_SET (pending_dead_regs, needed.regs);
- AND_COMPL_HARD_REG_SET (res->regs, pending_dead_regs);
- CLEAR_HARD_REG_SET (pending_dead_regs);
- continue;
-
- case BARRIER:
- case NOTE:
- continue;
-
- case INSN:
- if (GET_CODE (PATTERN (insn)) == USE)
- {
- /* If INSN is a USE made by update_block, we care about the
- underlying insn. Any registers set by the underlying insn
- are live since the insn is being done somewhere else. */
- if (GET_RTX_CLASS (GET_CODE (XEXP (PATTERN (insn), 0))) == 'i')
- mark_set_resources (XEXP (PATTERN (insn), 0), res, 0, 1);
-
- /* All other USE insns are to be ignored. */
- continue;
- }
- else if (GET_CODE (PATTERN (insn)) == CLOBBER)
- continue;
- else if (GET_CODE (PATTERN (insn)) == SEQUENCE)
- {
- /* An unconditional jump can be used to fill the delay slot
- of a call, so search for a JUMP_INSN in any position. */
- for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
- {
- this_jump_insn = XVECEXP (PATTERN (insn), 0, i);
- if (GET_CODE (this_jump_insn) == JUMP_INSN)
- break;
- }
- }
- }
-
- if (GET_CODE (this_jump_insn) == JUMP_INSN)
- {
- if (jump_count++ < 10
- && (simplejump_p (this_jump_insn)
- || GET_CODE (PATTERN (this_jump_insn)) == RETURN))
- {
- next = next_active_insn (JUMP_LABEL (this_jump_insn));
- if (jump_insn == 0)
- {
- jump_insn = insn;
- jump_target = JUMP_LABEL (this_jump_insn);
- }
- }
- else
- break;
- }
-
- mark_referenced_resources (insn, &needed, 1);
- mark_set_resources (insn, &set, 0, 1);
-
- COPY_HARD_REG_SET (scratch, set.regs);
- AND_COMPL_HARD_REG_SET (scratch, needed.regs);
- AND_COMPL_HARD_REG_SET (res->regs, scratch);
- }
+ jump_insn = find_dead_or_set_registers (target, res, &jump_target, 0,
+ set, needed);
/* If we hit an unconditional branch, we have another way of finding out
what is live: we can see what is live at the branch target and include
anything used but not set before the branch. The only things that are
- live are those that are live using the above test and the test below.
-
- Don't try this if we expired our jump count above, since that would
- mean there may be an infinite loop in the function being compiled. */
+ live are those that are live using the above test and the test below. */
- if (jump_insn && jump_count < 10)
+ if (jump_insn)
{
struct resources new_resources;
rtx stop_insn = next_active_insn (jump_insn);
@@ -2796,15 +2992,14 @@ mark_target_live_regs (target, res)
through FINAL_SEQUENCE. */
static void
-fill_simple_delay_slots (first, non_jumps_p)
- rtx first;
+fill_simple_delay_slots (non_jumps_p)
int non_jumps_p;
{
register rtx insn, pat, trial, next_trial;
- register int i, j;
+ register int i;
int num_unfilled_slots = unfilled_slots_next - unfilled_slots_base;
struct resources needed, set;
- register int slots_to_fill, slots_filled;
+ int slots_to_fill, slots_filled;
rtx delay_list;
for (i = 0; i < num_unfilled_slots; i++)
@@ -2827,8 +3022,20 @@ fill_simple_delay_slots (first, non_jumps_p)
else
flags = get_jump_flags (insn, NULL_RTX);
slots_to_fill = num_delay_slots (insn);
+
+ /* Some machine description have defined instructions to have
+ delay slots only in certain circumstances which may depend on
+ nearby insns (which change due to reorg's actions).
+
+ For example, the PA port normally has delay slots for unconditional
+ jumps.
+
+ However, the PA port claims such jumps do not have a delay slot
+ if they are immediate successors of certain CALL_INSNs. This
+ allows the port to favor filling the delay slot of the call with
+ the unconditional jump. */
if (slots_to_fill == 0)
- abort ();
+ continue;
/* This insn needs, or can use, some delay slots. SLOTS_TO_FILL
says how many. After initialization, first try optimizing
@@ -2938,8 +3145,8 @@ fill_simple_delay_slots (first, non_jumps_p)
tail, of the list. */
update_reg_dead_notes (trial, insn);
- delay_list = gen_rtx (INSN_LIST, VOIDmode,
- trial, delay_list);
+ delay_list = gen_rtx_INSN_LIST (VOIDmode,
+ trial, delay_list);
update_block (trial, trial);
delete_insn (trial);
if (slots_to_fill == ++slots_filled)
@@ -3136,10 +3343,23 @@ fill_simple_delay_slots (first, non_jumps_p)
}
}
+ /* If this is an unconditional jump, then try to get insns from the
+ target of the jump. */
+ if (GET_CODE (insn) == JUMP_INSN
+ && simplejump_p (insn)
+ && slots_filled != slots_to_fill)
+ delay_list
+ = fill_slots_from_thread (insn, const_true_rtx,
+ next_active_insn (JUMP_LABEL (insn)),
+ NULL, 1, 1,
+ own_thread_p (JUMP_LABEL (insn),
+ JUMP_LABEL (insn), 0),
+ slots_to_fill, &slots_filled,
+ delay_list);
+
if (delay_list)
unfilled_slots_base[i]
- = emit_delay_sequence (insn, delay_list,
- slots_filled, slots_to_fill);
+ = emit_delay_sequence (insn, delay_list, slots_filled);
if (slots_to_fill == slots_filled)
unfilled_slots_base[i] = 0;
@@ -3181,6 +3401,14 @@ fill_simple_delay_slots (first, non_jumps_p)
else
SET_HARD_REG_BIT (needed.regs, STACK_POINTER_REGNUM);
+#ifdef EPILOGUE_USES
+ for (i = 0; i <FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (EPILOGUE_USES (i))
+ SET_HARD_REG_BIT (needed.regs, i);
+ }
+#endif
+
for (trial = get_last_insn (); ! stop_search_p (trial, 1);
trial = PREV_INSN (trial))
{
@@ -3206,8 +3434,8 @@ fill_simple_delay_slots (first, non_jumps_p)
insns we find on the head of the list. */
current_function_epilogue_delay_list
- = gen_rtx (INSN_LIST, VOIDmode, trial,
- current_function_epilogue_delay_list);
+ = gen_rtx_INSN_LIST (VOIDmode, trial,
+ current_function_epilogue_delay_list);
mark_referenced_resources (trial, &end_of_function_needs, 1);
update_block (trial, trial);
delete_insn (trial);
@@ -3255,18 +3483,18 @@ fill_simple_delay_slots (first, non_jumps_p)
static rtx
fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
- thread_if_true, own_thread, own_opposite_thread,
- slots_to_fill, pslots_filled)
+ thread_if_true, own_thread,
+ slots_to_fill, pslots_filled, delay_list)
rtx insn;
rtx condition;
rtx thread, opposite_thread;
int likely;
int thread_if_true;
- int own_thread, own_opposite_thread;
+ int own_thread;
int slots_to_fill, *pslots_filled;
+ rtx delay_list;
{
rtx new_thread;
- rtx delay_list = 0;
struct resources opposite_needed, set, needed;
rtx trial;
int lose = 0;
@@ -3283,7 +3511,7 @@ fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
/* If our thread is the end of subroutine, we can't get any delay
insns from that. */
if (thread == 0)
- return 0;
+ return delay_list;
/* If this is an unconditional branch, nothing is needed at the
opposite thread. Otherwise, compute what is needed there. */
@@ -3345,8 +3573,9 @@ fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
/* If TRIAL is redundant with some insn before INSN, we don't
actually need to add it to the delay list; we can merely pretend
we did. */
- if (prior_insn = redundant_insn (trial, insn, delay_list))
+ if ((prior_insn = redundant_insn (trial, insn, delay_list)))
{
+ fix_reg_dead_note (prior_insn, insn);
if (own_thread)
{
update_block (trial, thread);
@@ -3422,6 +3651,12 @@ fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
if (own_thread)
{
update_block (trial, thread);
+ if (trial == thread)
+ {
+ thread = next_active_insn (thread);
+ if (new_thread == trial)
+ new_thread = thread;
+ }
delete_insn (trial);
}
else
@@ -3433,6 +3668,8 @@ fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
delay_list = add_to_delay_list (temp, delay_list);
+ mark_set_resources (trial, &opposite_needed, 0, 1);
+
if (slots_to_fill == ++(*pslots_filled))
{
/* Even though we have filled all the slots, we
@@ -3443,8 +3680,16 @@ fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
&& ! insn_sets_resource_p (new_thread, &needed, 1)
&& ! insn_references_resource_p (new_thread,
&set, 1)
- && redundant_insn (new_thread, insn, delay_list))
- new_thread = next_active_insn (new_thread);
+ && (prior_insn
+ = redundant_insn (new_thread, insn,
+ delay_list)))
+ {
+ /* We know we do not own the thread, so no need
+ to call update_block and delete_insn. */
+ fix_reg_dead_note (prior_insn, insn);
+ update_reg_unused_notes (prior_insn, new_thread);
+ new_thread = next_active_insn (new_thread);
+ }
break;
}
@@ -3502,7 +3747,9 @@ fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
{
/* If this is the `true' thread, we will want to follow the jump,
so we can only do this if we have taken everything up to here. */
- if (thread_if_true && trial == new_thread)
+ if (thread_if_true && trial == new_thread
+ && ! insn_references_resource_p (XVECEXP (PATTERN (trial), 0, 0),
+ &opposite_needed, 0))
delay_list
= steal_delay_list_from_target (insn, condition, PATTERN (trial),
delay_list, &set, &needed,
@@ -3524,7 +3771,10 @@ fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
depend on the destination register. If so, try to place the opposite
arithmetic insn after the jump insn and put the arithmetic insn in the
delay slot. If we can't do this, return. */
- if (delay_list == 0 && likely && new_thread && GET_CODE (new_thread) == INSN)
+ if (delay_list == 0 && likely && new_thread
+ && GET_CODE (new_thread) == INSN
+ && GET_CODE (PATTERN (new_thread)) != ASM_INPUT
+ && asm_noperands (PATTERN (new_thread)) < 0)
{
rtx pat = PATTERN (new_thread);
rtx dest;
@@ -3550,13 +3800,13 @@ fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
the negated constant. Otherwise, reverse the sense of the
arithmetic. */
if (GET_CODE (other) == CONST_INT)
- new_arith = gen_rtx (GET_CODE (src), GET_MODE (src), dest,
- negate_rtx (GET_MODE (src), other));
+ new_arith = gen_rtx_fmt_ee (GET_CODE (src), GET_MODE (src), dest,
+ negate_rtx (GET_MODE (src), other));
else
- new_arith = gen_rtx (GET_CODE (src) == PLUS ? MINUS : PLUS,
- GET_MODE (src), dest, other);
+ new_arith = gen_rtx_fmt_ee (GET_CODE (src) == PLUS ? MINUS : PLUS,
+ GET_MODE (src), dest, other);
- ninsn = emit_insn_after (gen_rtx (SET, VOIDmode, dest, new_arith),
+ ninsn = emit_insn_after (gen_rtx_SET (VOIDmode, dest, new_arith),
insn);
if (recog_memoized (ninsn) < 0
@@ -3570,6 +3820,12 @@ fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
if (own_thread)
{
update_block (trial, thread);
+ if (trial == thread)
+ {
+ thread = next_active_insn (thread);
+ if (new_thread == trial)
+ new_thread = thread;
+ }
delete_insn (trial);
}
else
@@ -3629,8 +3885,7 @@ fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
if safe. */
static void
-fill_eager_delay_slots (first)
- rtx first;
+fill_eager_delay_slots ()
{
register rtx insn;
register int i;
@@ -3653,8 +3908,19 @@ fill_eager_delay_slots (first)
continue;
slots_to_fill = num_delay_slots (insn);
+ /* Some machine description have defined instructions to have
+ delay slots only in certain circumstances which may depend on
+ nearby insns (which change due to reorg's actions).
+
+ For example, the PA port normally has delay slots for unconditional
+ jumps.
+
+ However, the PA port claims such jumps do not have a delay slot
+ if they are immediate successors of certain CALL_INSNs. This
+ allows the port to favor filling the delay slot of the call with
+ the unconditional jump. */
if (slots_to_fill == 0)
- abort ();
+ continue;
slots_filled = 0;
target_label = JUMP_LABEL (insn);
@@ -3692,8 +3958,8 @@ fill_eager_delay_slots (first)
delay_list
= fill_slots_from_thread (insn, condition, insn_at_target,
fallthrough_insn, prediction == 2, 1,
- own_target, own_fallthrough,
- slots_to_fill, &slots_filled);
+ own_target,
+ slots_to_fill, &slots_filled, delay_list);
if (delay_list == 0 && own_fallthrough)
{
@@ -3707,8 +3973,9 @@ fill_eager_delay_slots (first)
delay_list
= fill_slots_from_thread (insn, condition, fallthrough_insn,
insn_at_target, 0, 0,
- own_fallthrough, own_target,
- slots_to_fill, &slots_filled);
+ own_fallthrough,
+ slots_to_fill, &slots_filled,
+ delay_list);
}
}
else
@@ -3717,21 +3984,22 @@ fill_eager_delay_slots (first)
delay_list
= fill_slots_from_thread (insn, condition, fallthrough_insn,
insn_at_target, 0, 0,
- own_fallthrough, own_target,
- slots_to_fill, &slots_filled);
+ own_fallthrough,
+ slots_to_fill, &slots_filled,
+ delay_list);
if (delay_list == 0)
delay_list
= fill_slots_from_thread (insn, condition, insn_at_target,
next_active_insn (insn), 0, 1,
- own_target, own_fallthrough,
- slots_to_fill, &slots_filled);
+ own_target,
+ slots_to_fill, &slots_filled,
+ delay_list);
}
if (delay_list)
unfilled_slots_base[i]
- = emit_delay_sequence (insn, delay_list,
- slots_filled, slots_to_fill);
+ = emit_delay_sequence (insn, delay_list, slots_filled);
if (slots_to_fill == slots_filled)
unfilled_slots_base[i] = 0;
@@ -3903,11 +4171,20 @@ relax_delay_slots (first)
if (trial && GET_CODE (PATTERN (trial)) != SEQUENCE
&& redundant_insn (trial, insn, 0))
{
- trial = next_active_insn (trial);
- if (trial == 0)
- target_label = find_end_label ();
- else
- target_label = get_label_before (trial);
+ rtx tmp;
+
+ /* Figure out where to emit the special USE insn so we don't
+ later incorrectly compute register live/death info. */
+ tmp = next_active_insn (trial);
+ if (tmp == 0)
+ tmp = find_end_label ();
+
+ /* Insert the special USE insn and update dataflow info. */
+ update_block (trial, tmp);
+
+ /* Now emit a label before the special USE insn, and
+ redirect our jump to the new label. */
+ target_label = get_label_before (PREV_INSN (tmp));
reorg_redirect_jump (delay_insn, target_label);
next = insn;
continue;
@@ -4167,8 +4444,8 @@ make_return_insns (first)
if (--LABEL_NUSES (real_return_label) == 0)
delete_insn (real_return_label);
- fill_simple_delay_slots (first, 1);
- fill_simple_delay_slots (first, 0);
+ fill_simple_delay_slots (1);
+ fill_simple_delay_slots (0);
}
#endif
@@ -4271,13 +4548,16 @@ dbr_schedule (first, file)
else
SET_HARD_REG_BIT (end_of_function_needs.regs, STACK_POINTER_REGNUM);
- if (current_function_return_rtx != 0
- && GET_CODE (current_function_return_rtx) == REG)
+ if (current_function_return_rtx != 0)
mark_referenced_resources (current_function_return_rtx,
&end_of_function_needs, 1);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (global_regs[i])
+ if (global_regs[i]
+#ifdef EPILOGUE_USES
+ || EPILOGUE_USES (i)
+#endif
+ )
SET_HARD_REG_BIT (end_of_function_needs.regs, i);
/* The registers required to be live at the end of the function are
@@ -4299,7 +4579,7 @@ dbr_schedule (first, file)
start_of_epilogue_needs = end_of_function_needs;
- while (epilogue_insn = next_nonnote_insn (epilogue_insn))
+ while ((epilogue_insn = next_nonnote_insn (epilogue_insn)))
mark_set_resources (epilogue_insn, &end_of_function_needs, 0, 1);
/* Show we haven't computed an end-of-function label yet. */
@@ -4326,9 +4606,9 @@ dbr_schedule (first, file)
reorg_pass_number < MAX_REORG_PASSES;
reorg_pass_number++)
{
- fill_simple_delay_slots (first, 1);
- fill_simple_delay_slots (first, 0);
- fill_eager_delay_slots (first);
+ fill_simple_delay_slots (1);
+ fill_simple_delay_slots (0);
+ fill_eager_delay_slots ();
relax_delay_slots (first);
}
@@ -4393,5 +4673,23 @@ dbr_schedule (first, file)
}
}
}
+
+ /* For all JUMP insns, fill in branch prediction notes, so that during
+ assembler output a target can set branch prediction bits in the code.
+ We have to do this now, as up until this point the destinations of
+ JUMPS can be moved around and changed, but past right here that cannot
+ happen. */
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ int pred_flags;
+
+ if (GET_CODE (insn) != JUMP_INSN)
+ continue;
+
+ pred_flags = get_jump_flags (insn, JUMP_LABEL (insn));
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_BR_PRED,
+ GEN_INT (pred_flags),
+ REG_NOTES (insn));
+ }
}
#endif /* DELAY_SLOTS */
OpenPOWER on IntegriCloud