summaryrefslogtreecommitdiff
path: root/gcc/recog.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/recog.c')
-rw-r--r--gcc/recog.c325
1 files changed, 282 insertions, 43 deletions
diff --git a/gcc/recog.c b/gcc/recog.c
index bac7e471621..876004129de 100644
--- a/gcc/recog.c
+++ b/gcc/recog.c
@@ -37,7 +37,6 @@ Boston, MA 02111-1307, USA. */
#include "toplev.h"
#include "basic-block.h"
#include "output.h"
-#include "resource.h"
#ifndef STACK_PUSH_CODE
#ifdef STACK_GROWS_DOWNWARD
@@ -2682,100 +2681,340 @@ split_all_insns (upd_life)
}
#ifdef HAVE_peephole2
-/* This is the last insn we'll allow recog_next_insn to consider. */
-static rtx recog_last_allowed_insn;
+struct peep2_insn_data
+{
+ rtx insn;
+ regset live_before;
+};
+
+static struct peep2_insn_data peep2_insn_data[MAX_INSNS_PER_PEEP2 + 1];
+static int peep2_current;
+
+/* A non-insn marker indicating the last insn of the block.
+ The live_before regset for this element is correct, indicating
+ global_live_at_end for the block. */
+#define PEEP2_EOB pc_rtx
+
+/* Return the Nth non-note insn after `current', or return NULL_RTX if it
+ does not exist. Used by the recognizer to find the next insn to match
+ in a multi-insn pattern. */
-/* Return the Nth non-note insn after INSN, or return NULL_RTX if it does
- not exist. Used by the recognizer to find the next insn to match in a
- multi-insn pattern. */
rtx
-recog_next_insn (insn, n)
- rtx insn;
+peep2_next_insn (n)
int n;
{
- if (insn != NULL_RTX)
+ if (n >= MAX_INSNS_PER_PEEP2 + 1)
+ abort ();
+
+ n += peep2_current;
+ if (n >= MAX_INSNS_PER_PEEP2 + 1)
+ n -= MAX_INSNS_PER_PEEP2 + 1;
+
+ if (peep2_insn_data[n].insn == PEEP2_EOB)
+ return NULL_RTX;
+ return peep2_insn_data[n].insn;
+}
+
+/* Return true if REGNO is dead before the Nth non-note insn
+ after `current'. */
+
+int
+peep2_regno_dead_p (ofs, regno)
+ int ofs;
+ int regno;
+{
+ if (ofs >= MAX_INSNS_PER_PEEP2 + 1)
+ abort ();
+
+ ofs += peep2_current;
+ if (ofs >= MAX_INSNS_PER_PEEP2 + 1)
+ ofs -= MAX_INSNS_PER_PEEP2 + 1;
+
+ if (peep2_insn_data[ofs].insn == NULL_RTX)
+ abort ();
+
+ return ! REGNO_REG_SET_P (peep2_insn_data[ofs].live_before, regno);
+}
+
+/* Similarly for a REG. */
+
+int
+peep2_reg_dead_p (ofs, reg)
+ int ofs;
+ rtx reg;
+{
+ int regno, n;
+
+ if (ofs >= MAX_INSNS_PER_PEEP2 + 1)
+ abort ();
+
+ ofs += peep2_current;
+ if (ofs >= MAX_INSNS_PER_PEEP2 + 1)
+ ofs -= MAX_INSNS_PER_PEEP2 + 1;
+
+ if (peep2_insn_data[ofs].insn == NULL_RTX)
+ abort ();
+
+ regno = REGNO (reg);
+ n = HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ while (--n >= 0)
+ if (REGNO_REG_SET_P (peep2_insn_data[ofs].live_before, regno + n))
+ return 0;
+ return 1;
+}
+
+/* Try to find a hard register of mode MODE, matching the register class in
+ CLASS_STR, which is available at the beginning of insn CURRENT_INSN and
+ remains available until the end of LAST_INSN. LAST_INSN may be NULL_RTX,
+ in which case the only condition is that the register must be available
+ before CURRENT_INSN.
+ Registers that already have bits set in REG_SET will not be considered.
+
+ If an appropriate register is available, it will be returned and the
+ corresponding bit(s) in REG_SET will be set; otherwise, NULL_RTX is
+ returned. */
+
+rtx
+peep2_find_free_register (from, to, class_str, mode, reg_set)
+ int from, to;
+ const char *class_str;
+ enum machine_mode mode;
+ HARD_REG_SET *reg_set;
+{
+ static int search_ofs;
+ enum reg_class class;
+ HARD_REG_SET live;
+ int i;
+
+ if (from >= MAX_INSNS_PER_PEEP2 + 1 || to >= MAX_INSNS_PER_PEEP2 + 1)
+ abort ();
+
+ from += peep2_current;
+ if (from >= MAX_INSNS_PER_PEEP2 + 1)
+ from -= MAX_INSNS_PER_PEEP2 + 1;
+ to += peep2_current;
+ if (to >= MAX_INSNS_PER_PEEP2 + 1)
+ to -= MAX_INSNS_PER_PEEP2 + 1;
+
+ if (peep2_insn_data[from].insn == NULL_RTX)
+ abort ();
+ REG_SET_TO_HARD_REG_SET (live, peep2_insn_data[from].live_before);
+
+ while (from != to)
{
- while (n > 0)
+ HARD_REG_SET this_live;
+
+ if (++from >= MAX_INSNS_PER_PEEP2 + 1)
+ from = 0;
+ if (peep2_insn_data[from].insn == NULL_RTX)
+ abort ();
+ REG_SET_TO_HARD_REG_SET (this_live, peep2_insn_data[from].live_before);
+ IOR_HARD_REG_SET (live, this_live);
+ }
+
+ class = (class_str[0] == 'r' ? GENERAL_REGS
+ : REG_CLASS_FROM_LETTER (class_str[0]));
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ int raw_regno, regno, success, j;
+
+ /* Distribute the free registers as much as possible. */
+ raw_regno = search_ofs + i;
+ if (raw_regno >= FIRST_PSEUDO_REGISTER)
+ raw_regno -= FIRST_PSEUDO_REGISTER;
+#ifdef REG_ALLOC_ORDER
+ regno = reg_alloc_order[raw_regno];
+#else
+ regno = raw_regno;
+#endif
+
+ /* Don't allocate fixed registers. */
+ if (fixed_regs[regno])
+ continue;
+ /* Make sure the register is of the right class. */
+ if (! TEST_HARD_REG_BIT (reg_class_contents[class], regno))
+ continue;
+ /* And can support the mode we need. */
+ if (! HARD_REGNO_MODE_OK (regno, mode))
+ continue;
+ /* And that we don't create an extra save/restore. */
+ if (! call_used_regs[regno] && ! regs_ever_live[regno])
+ continue;
+ /* And we don't clobber traceback for noreturn functions. */
+ if ((regno == FRAME_POINTER_REGNUM || regno == HARD_FRAME_POINTER_REGNUM)
+ && (! reload_completed || frame_pointer_needed))
+ continue;
+
+ success = 1;
+ for (j = HARD_REGNO_NREGS (regno, mode) - 1; j >= 0; j--)
+ {
+ if (TEST_HARD_REG_BIT (*reg_set, regno + j)
+ || TEST_HARD_REG_BIT (live, regno + j))
+ {
+ success = 0;
+ break;
+ }
+ }
+ if (success)
{
- if (insn == recog_last_allowed_insn)
- return NULL_RTX;
+ for (j = HARD_REGNO_NREGS (regno, mode) - 1; j >= 0; j--)
+ SET_HARD_REG_BIT (*reg_set, regno + j);
- insn = NEXT_INSN (insn);
- if (insn == NULL_RTX)
- break;
+ /* Start the next search with the next register. */
+ if (++raw_regno >= FIRST_PSEUDO_REGISTER)
+ raw_regno = 0;
+ search_ofs = raw_regno;
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
- n -= 1;
+ return gen_rtx_REG (mode, regno);
}
}
- return insn;
+ search_ofs = 0;
+ return NULL_RTX;
}
/* Perform the peephole2 optimization pass. */
+
void
peephole2_optimize (dump_file)
FILE *dump_file ATTRIBUTE_UNUSED;
{
+ regset_head rs_heads[MAX_INSNS_PER_PEEP2 + 2];
rtx insn, prev;
- int i, changed;
+ regset live;
+ int i, b;
+#ifdef HAVE_conditional_execution
sbitmap blocks;
+ int changed;
+#endif
- /* ??? TODO: Arrange with resource.c to start at bb->global_live_at_end
- and backtrack insn by insn as we proceed through the block. In this
- way we'll not need to keep searching forward from the beginning of
- basic blocks to find register life info. */
-
- init_resource_info (NULL);
+ /* Initialize the regsets we're going to use. */
+ for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i)
+ peep2_insn_data[i].live_before = INITIALIZE_REG_SET (rs_heads[i]);
+ live = INITIALIZE_REG_SET (rs_heads[i]);
+#ifdef HAVE_conditional_execution
blocks = sbitmap_alloc (n_basic_blocks);
sbitmap_zero (blocks);
changed = 0;
+#else
+ count_or_remove_death_notes (NULL, 1);
+#endif
- for (i = n_basic_blocks - 1; i >= 0; --i)
+ for (b = n_basic_blocks - 1; b >= 0; --b)
{
- basic_block bb = BASIC_BLOCK (i);
+ basic_block bb = BASIC_BLOCK (b);
+ struct propagate_block_info *pbi;
+
+ /* Indicate that all slots except the last holds invalid data. */
+ for (i = 0; i < MAX_INSNS_PER_PEEP2; ++i)
+ peep2_insn_data[i].insn = NULL_RTX;
+
+ /* Indicate that the last slot contains live_after data. */
+ peep2_insn_data[MAX_INSNS_PER_PEEP2].insn = PEEP2_EOB;
+ peep2_current = MAX_INSNS_PER_PEEP2;
- /* Since we don't update life info until the very end, we can't
- allow matching instructions that we've replaced before. Walk
- backward through the basic block so that we don't have to
- care about subsequent life info; recog_last_allowed_insn to
- restrict how far forward we will allow the match to proceed. */
+ /* Start up propagation. */
+ COPY_REG_SET (live, bb->global_live_at_end);
+ COPY_REG_SET (peep2_insn_data[MAX_INSNS_PER_PEEP2].live_before, live);
+
+#ifdef HAVE_conditional_execution
+ pbi = init_propagate_block_info (bb, live, NULL, 0);
+#else
+ pbi = init_propagate_block_info (bb, live, NULL, PROP_DEATH_NOTES);
+#endif
- recog_last_allowed_insn = NEXT_INSN (bb->end);
for (insn = bb->end; ; insn = prev)
{
prev = PREV_INSN (insn);
if (INSN_P (insn))
{
- rtx try, last_insn;
-
- try = peephole2_insns (PATTERN (insn), insn, &last_insn);
+ rtx try;
+ int match_len;
+
+ /* Record this insn. */
+ if (--peep2_current < 0)
+ peep2_current = MAX_INSNS_PER_PEEP2;
+ peep2_insn_data[peep2_current].insn = insn;
+ propagate_one_insn (pbi, insn);
+ COPY_REG_SET (peep2_insn_data[peep2_current].live_before, live);
+
+ /* Match the peephole. */
+ try = peephole2_insns (PATTERN (insn), insn, &match_len);
if (try != NULL)
{
- flow_delete_insn_chain (insn, last_insn);
+ i = match_len + peep2_current;
+ if (i >= MAX_INSNS_PER_PEEP2 + 1)
+ i -= MAX_INSNS_PER_PEEP2 + 1;
+
+ /* Replace the old sequence with the new. */
+ flow_delete_insn_chain (insn, peep2_insn_data[i].insn);
try = emit_insn_after (try, prev);
- if (last_insn == bb->end)
+ /* Adjust the basic block boundaries. */
+ if (peep2_insn_data[i].insn == bb->end)
bb->end = try;
if (insn == bb->head)
bb->head = NEXT_INSN (prev);
- recog_last_allowed_insn = NEXT_INSN (prev);
- SET_BIT (blocks, i);
+#ifdef HAVE_conditional_execution
+ /* With conditional execution, we cannot back up the
+ live information so easily, since the conditional
+ death data structures are not so self-contained.
+ So record that we've made a modification to this
+ block and update life information at the end. */
+ SET_BIT (blocks, b);
changed = 1;
+
+ for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i)
+ peep2_insn_data[i].insn = NULL_RTX;
+ peep2_insn_data[peep2_current].insn = PEEP2_EOB;
+#else
+ /* Back up lifetime information past the end of the
+ newly created sequence. */
+ if (++i >= MAX_INSNS_PER_PEEP2 + 1)
+ i = 0;
+ COPY_REG_SET (live, peep2_insn_data[i].live_before);
+
+ /* Update life information for the new sequence. */
+ do
+ {
+ if (INSN_P (try))
+ {
+ if (--i < 0)
+ i = MAX_INSNS_PER_PEEP2;
+ peep2_insn_data[i].insn = try;
+ propagate_one_insn (pbi, try);
+ COPY_REG_SET (peep2_insn_data[i].live_before, live);
+ }
+ try = PREV_INSN (try);
+ }
+ while (try != prev);
+
+ /* ??? Should verify that LIVE now matches what we
+ had before the new sequence. */
+
+ peep2_current = i;
+#endif
}
}
if (insn == bb->head)
break;
}
+
+ free_propagate_block_info (pbi);
}
- free_resource_info ();
+ for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i)
+ FREE_REG_SET (peep2_insn_data[i].live_before);
+ FREE_REG_SET (live);
- compute_bb_for_insn (get_max_uid ());
+#ifdef HAVE_conditional_execution
count_or_remove_death_notes (blocks, 1);
update_life_info (blocks, UPDATE_LIFE_LOCAL, PROP_DEATH_NOTES);
-}
+ sbitmap_free (blocks);
#endif
+}
+#endif /* HAVE_peephole2 */