@@ -95,6 +95,9 @@ int ira_copies_num;
basic block. */
static int last_basic_block_before_change;
+/* Record these pseudos which has subreg object. Used by LRA pass. */
+bitmap_head pseudos_has_subreg_object;
+
/* Initialize some members in loop tree node NODE. Use LOOP_NUM for
the member loop_num. */
static void
@@ -3711,6 +3714,33 @@ update_conflict_hard_reg_costs (void)
}
}
+/* Setup speudos_has_subreg_object. */
+static void
+setup_pseudos_has_subreg_object ()
+{
+ bitmap_initialize (&pseudos_has_subreg_object, ®_obstack);
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+ FOR_EACH_ALLOCNO (a, ai)
+ if (has_subreg_object_p (a))
+ {
+ bitmap_set_bit (&pseudos_has_subreg_object, ALLOCNO_REGNO (a));
+ if (ira_dump_file != NULL)
+ {
+ fprintf (ira_dump_file,
+ " a%d(r%d, nregs: %d) has subreg objects:\n",
+ ALLOCNO_NUM (a), ALLOCNO_REGNO (a), ALLOCNO_NREGS (a));
+ ira_allocno_object_iterator oi;
+ ira_object_t obj;
+ FOR_EACH_ALLOCNO_OBJECT (a, obj, oi)
+ fprintf (ira_dump_file, " object %d: start: %d, nregs: %d\n",
+ OBJECT_INDEX (obj), OBJECT_START (obj),
+ OBJECT_NREGS (obj));
+ fprintf (ira_dump_file, "\n");
+ }
+ }
+}
+
/* Create a internal representation (IR) for IRA (allocnos, copies,
loop tree nodes). The function returns TRUE if we generate loop
structure (besides nodes representing all function and the basic
@@ -3731,6 +3761,7 @@ ira_build (void)
create_allocnos ();
ira_costs ();
create_allocno_objects ();
+ setup_pseudos_has_subreg_object ();
ira_create_allocno_live_ranges ();
remove_unnecessary_regions (false);
ira_compress_allocno_live_ranges ();
@@ -1131,6 +1131,52 @@ assign_hard_regno (int hard_regno, int regno)
/* Array used for sorting different pseudos. */
static int *sorted_pseudos;
+/* The detail conflict offsets If two live ranges conflict. Use to record
+ partail conflict. */
+static bitmap_head live_range_conflicts;
+
+/* Set the conflict offset of the two registers REGNO1 and REGNO2. Use the
+ regno with bigger nregs as the base. */
+static void
+set_offset_conflicts (int regno1, int regno2)
+{
+ gcc_assert (reg_renumber[regno1] >= 0 && reg_renumber[regno2] >= 0);
+ int nregs1 = get_nregs (regno1);
+ int nregs2 = get_nregs (regno2);
+ if (nregs1 < nregs2)
+ {
+ std::swap (nregs1, nregs2);
+ std::swap (regno1, regno2);
+ }
+
+ lra_live_range_t r1 = lra_reg_info[regno1].live_ranges;
+ lra_live_range_t r2 = lra_reg_info[regno2].live_ranges;
+ int total = nregs1;
+
+ bitmap_clear (&live_range_conflicts);
+ while (r1 != NULL && r2 != NULL)
+ {
+ if (r1->start > r2->finish)
+ r1 = r1->next;
+ else if (r2->start > r1->finish)
+ r2 = r2->next;
+ else
+ {
+ for (const subreg_range &range1 : r1->subreg.ranges)
+ for (const subreg_range &range2 : r2->subreg.ranges)
+ /* Record all overlap offset. */
+ for (int i = range1.start - (range2.end - range2.start) + 1;
+ i < range1.end; i++)
+ if (i >= 0 && i < total)
+ bitmap_set_bit (&live_range_conflicts, i);
+ if (r1->finish < r2->finish)
+ r1 = r1->next;
+ else
+ r2 = r2->next;
+ }
+ }
+}
+
/* The constraints pass is allowed to create equivalences between
pseudos that make the current allocation "incorrect" (in the sense
that pseudos are assigned to hard registers from their own conflict
@@ -1226,19 +1272,56 @@ setup_live_pseudos_and_spill_after_risky_transforms (bitmap
the same hard register. */
|| hard_regno != reg_renumber[conflict_regno])
{
- int conflict_hard_regno = reg_renumber[conflict_regno];
-
- biggest_mode = lra_reg_info[conflict_regno].biggest_mode;
- biggest_nregs = hard_regno_nregs (conflict_hard_regno,
- biggest_mode);
- nregs_diff
- = (biggest_nregs
- - hard_regno_nregs (conflict_hard_regno,
- PSEUDO_REGNO_MODE (conflict_regno)));
- add_to_hard_reg_set (&conflict_set,
- biggest_mode,
- conflict_hard_regno
- - (WORDS_BIG_ENDIAN ? nregs_diff : 0));
+ if (hard_regno >= 0 && reg_renumber[conflict_regno] >= 0
+ && (has_subreg_object_p (regno)
+ || has_subreg_object_p (conflict_regno)))
+ {
+ int nregs1 = get_nregs (regno);
+ int nregs2 = get_nregs (conflict_regno);
+ /* Quick check it is no overlap at all between them. */
+ if (hard_regno + nregs1 <= reg_renumber[conflict_regno]
+ || reg_renumber[conflict_regno] + nregs2 <= hard_regno)
+ continue;
+
+ /* Check the overlap is ok if them have partial overlap. */
+ set_offset_conflicts (regno, conflict_regno);
+ if (nregs1 >= nregs2)
+ EXECUTE_IF_SET_IN_BITMAP (&live_range_conflicts, 0, k, bi)
+ {
+ int start_regno
+ = WORDS_BIG_ENDIAN
+ ? reg_renumber[conflict_regno] + nregs2 + k - nregs1
+ : reg_renumber[conflict_regno] - k;
+ if (start_regno >= 0 && hard_regno == start_regno)
+ SET_HARD_REG_BIT (conflict_set, start_regno);
+ }
+ else
+ EXECUTE_IF_SET_IN_BITMAP (&live_range_conflicts, 0, k, bi)
+ {
+ int start_regno
+ = WORDS_BIG_ENDIAN
+ ? reg_renumber[conflict_regno] + nregs2 - k - nregs1
+ : reg_renumber[conflict_regno] + k;
+ if (start_regno < FIRST_PSEUDO_REGISTER
+ && hard_regno == start_regno)
+ SET_HARD_REG_BIT (conflict_set, start_regno);
+ }
+ }
+ else
+ {
+ int conflict_hard_regno = reg_renumber[conflict_regno];
+
+ biggest_mode = lra_reg_info[conflict_regno].biggest_mode;
+ biggest_nregs
+ = hard_regno_nregs (conflict_hard_regno, biggest_mode);
+ nregs_diff
+ = (biggest_nregs
+ - hard_regno_nregs (conflict_hard_regno,
+ PSEUDO_REGNO_MODE (conflict_regno)));
+ add_to_hard_reg_set (&conflict_set, biggest_mode,
+ conflict_hard_regno
+ - (WORDS_BIG_ENDIAN ? nregs_diff : 0));
+ }
}
if (! overlaps_hard_reg_set_p (conflict_set, mode, hard_regno))
{
@@ -1637,7 +1720,9 @@ lra_assign (bool &fails_p)
init_regno_assign_info ();
bitmap_initialize (&all_spilled_pseudos, ®_obstack);
create_live_range_start_chains ();
+ bitmap_initialize (&live_range_conflicts, ®_obstack);
setup_live_pseudos_and_spill_after_risky_transforms (&all_spilled_pseudos);
+ bitmap_clear (&live_range_conflicts);
if (! lra_hard_reg_split_p && ! lra_asm_error_p && flag_checking)
/* Check correctness of allocation but only when there are no hard reg
splits and asm errors as in the case of errors explicit insns involving
@@ -2363,13 +2363,19 @@ process_alt_operands (int only_alternative)
{
/* We should reject matching of an early
clobber operand if the matching operand is
- not dying in the insn. */
- if (!TEST_BIT (curr_static_id->operand[m]
- .early_clobber_alts, nalt)
+ not dying in the insn. But for subreg of pseudo which
+ has subreg live be tracked in ira, the REG_DEAD note
+ doesn't have. that case we think them the matching is
+ ok. */
+ if (!TEST_BIT (
+ curr_static_id->operand[m].early_clobber_alts,
+ nalt)
|| operand_reg[nop] == NULL_RTX
- || (find_regno_note (curr_insn, REG_DEAD,
- REGNO (op))
- || REGNO (op) == REGNO (operand_reg[m])))
+ || find_regno_note (curr_insn, REG_DEAD, REGNO (op))
+ || (read_modify_subreg_p (
+ *curr_id->operand_loc[nop])
+ && has_subreg_object_p (REGNO (op)))
+ || REGNO (op) == REGNO (operand_reg[m]))
match_p = true;
}
if (match_p)
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_LRA_INT_H
#define GCC_LRA_INT_H
+#include "lra.h"
#include "subreg-live-range.h"
#define lra_assert(c) gcc_checking_assert (c)
@@ -48,6 +49,8 @@ struct lra_live_range
lra_live_range_t next;
/* Pointer to structures with the same start. */
lra_live_range_t start_next;
+ /* Object whose live range is described by given structure. */
+ subreg_ranges subreg;
};
typedef struct lra_copy *lra_copy_t;
@@ -110,6 +113,8 @@ public:
/* The biggest size mode in which each pseudo reg is referred in
whole function (possibly via subreg). */
machine_mode biggest_mode;
+ /* The real reg MODE. */
+ machine_mode reg_mode;
/* Live ranges of the pseudo. */
lra_live_range_t live_ranges;
/* This member is set up in lra-lives.cc for subsequent
@@ -161,6 +166,12 @@ struct lra_insn_reg
unsigned int subreg_p : 1;
/* The corresponding regno of the register. */
int regno;
+ /* The start and end of current ref of blocks, remember the use/def can be
+ a normal subreg. */
+ int start, end;
+ /* The start and end of current ref of hard regs, remember the use/def can be
+ a normal subreg. */
+ int start_reg, end_reg;
/* Next reg info of the same insn. */
struct lra_insn_reg *next;
};
@@ -332,6 +343,8 @@ extern struct lra_insn_reg *lra_get_insn_regs (int);
extern void lra_free_copies (void);
extern void lra_create_copy (int, int, int);
extern lra_copy_t lra_get_copy (int);
+extern subreg_range
+get_range_hard_regs (int regno, const subreg_range &r);
extern int lra_new_regno_start;
extern int lra_constraint_new_regno_start;
@@ -533,4 +546,22 @@ lra_assign_reg_val (int from, int to)
lra_reg_info[to].offset = lra_reg_info[from].offset;
}
+/* Return the number regs of REGNO. */
+inline int
+get_nregs (int regno)
+{
+ enum reg_class aclass = lra_get_allocno_class (regno);
+ gcc_assert (aclass != NO_REGS);
+ int nregs = ira_reg_class_max_nregs[aclass][lra_reg_info[regno].reg_mode];
+ return nregs;
+}
+
+extern bitmap_head pseudos_has_subreg_object;
+/* Return true if pseudo REGNO has subreg live range. */
+inline bool
+has_subreg_object_p (int regno)
+{
+ return bitmap_bit_p (&pseudos_has_subreg_object, regno);
+}
+
#endif /* GCC_LRA_INT_H */
@@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see
stack memory slots to spilled pseudos. */
#include "config.h"
+#define INCLUDE_VECTOR
#include "system.h"
#include "coretypes.h"
#include "backend.h"
@@ -97,6 +98,9 @@ static bitmap_head temp_bitmap;
/* Pool for pseudo live ranges. */
static object_allocator<lra_live_range> lra_live_range_pool ("live ranges");
+/* Store def/use point of has_subreg_object_p register. */
+static class subregs_live_points *live_points;
+
/* Free live range list LR. */
static void
free_live_range_list (lra_live_range_t lr)
@@ -113,16 +117,26 @@ free_live_range_list (lra_live_range_t lr)
/* Create and return pseudo live range with given attributes. */
static lra_live_range_t
-create_live_range (int regno, int start, int finish, lra_live_range_t next)
+create_live_range (int regno, const subreg_ranges &sr, int start, int finish,
+ lra_live_range_t next)
{
lra_live_range_t p = lra_live_range_pool.allocate ();
p->regno = regno;
p->start = start;
p->finish = finish;
p->next = next;
+ p->subreg = sr;
return p;
}
+static lra_live_range_t
+create_live_range (int regno, int start, int finish, lra_live_range_t next)
+{
+ subreg_ranges sr = subreg_ranges (1);
+ sr.add_range (1, subreg_range (0, 1));
+ return create_live_range (regno, sr, start, finish, next);
+}
+
/* Copy live range R and return the result. */
static lra_live_range_t
copy_live_range (lra_live_range_t r)
@@ -164,7 +178,8 @@ lra_merge_live_ranges (lra_live_range_t r1, lra_live_range_t r2)
if (r1->start < r2->start)
std::swap (r1, r2);
- if (r1->start == r2->finish + 1)
+ if (r1->start == r2->finish + 1
+ && (r1->regno != r2->regno || r1->subreg.same_p (r2->subreg)))
{
/* Joint ranges: merge r1 and r2 into r1. */
r1->start = r2->start;
@@ -174,7 +189,8 @@ lra_merge_live_ranges (lra_live_range_t r1, lra_live_range_t r2)
}
else
{
- gcc_assert (r2->finish + 1 < r1->start);
+ gcc_assert (r2->finish + 1 < r1->start
+ || !r1->subreg.same_p (r2->subreg));
/* Add r1 to the result. */
if (first == NULL)
first = last = r1;
@@ -237,6 +253,10 @@ sparseset_contains_pseudos_p (sparseset a)
return false;
}
+static void
+update_pseudo_point (int regno, const subreg_range &range, int point,
+ enum point_type type);
+
/* Mark pseudo REGNO as living or dying at program point POINT, depending on
whether TYPE is a definition or a use. If this is the first reference to
REGNO that we've encountered, then create a new live range for it. */
@@ -249,27 +269,78 @@ update_pseudo_point (int regno, int point, enum point_type type)
/* Don't compute points for hard registers. */
if (HARD_REGISTER_NUM_P (regno))
return;
+ if (!complete_info_p && lra_get_regno_hard_regno (regno) >= 0)
+ return;
- if (complete_info_p || lra_get_regno_hard_regno (regno) < 0)
+ if (has_subreg_object_p (regno))
{
- if (type == DEF_POINT)
- {
- if (sparseset_bit_p (pseudos_live, regno))
- {
- p = lra_reg_info[regno].live_ranges;
- lra_assert (p != NULL);
- p->finish = point;
- }
- }
- else /* USE_POINT */
+ update_pseudo_point (regno, subreg_range (0, get_nregs (regno)), point,
+ type);
+ return;
+ }
+
+ if (type == DEF_POINT)
+ {
+ if (sparseset_bit_p (pseudos_live, regno))
{
- if (!sparseset_bit_p (pseudos_live, regno)
- && ((p = lra_reg_info[regno].live_ranges) == NULL
- || (p->finish != point && p->finish + 1 != point)))
- lra_reg_info[regno].live_ranges
- = create_live_range (regno, point, -1, p);
+ p = lra_reg_info[regno].live_ranges;
+ lra_assert (p != NULL);
+ p->finish = point;
}
}
+ else /* USE_POINT */
+ {
+ if (!sparseset_bit_p (pseudos_live, regno)
+ && ((p = lra_reg_info[regno].live_ranges) == NULL
+ || (p->finish != point && p->finish + 1 != point)))
+ lra_reg_info[regno].live_ranges
+ = create_live_range (regno, point, -1, p);
+ }
+}
+
+/* Like the above mark_regno_dead but for has_subreg_object_p REGNO. */
+static void
+update_pseudo_point (int regno, const subreg_range &range, int point,
+ enum point_type type)
+{
+ /* Don't compute points for hard registers. */
+ if (HARD_REGISTER_NUM_P (regno))
+ return;
+
+ if (!complete_info_p && lra_get_regno_hard_regno (regno) >= 0)
+ {
+ if (has_subreg_object_p (regno))
+ live_points->add_range (regno, get_nregs (regno), range,
+ type == DEF_POINT);
+ return;
+ }
+
+ if (!has_subreg_object_p (regno))
+ {
+ update_pseudo_point (regno, point, type);
+ return;
+ }
+
+ if (lra_dump_file != NULL)
+ {
+ fprintf (lra_dump_file, " %s r%d",
+ type == DEF_POINT ? "def" : "use", regno);
+ fprintf (lra_dump_file, "[subreg: start %d, nregs: %d]", range.start,
+ range.end - range.start);
+ fprintf (lra_dump_file, " at point %d\n", point);
+ }
+
+ live_points->add_point (regno, get_nregs (regno), range, type == DEF_POINT,
+ point);
+}
+
+/* Update each range in SR. */
+static void
+update_pseudo_point (int regno, const subreg_ranges sr, int point,
+ enum point_type type)
+{
+ for (const subreg_range &range : sr.ranges)
+ update_pseudo_point (regno, range, point, type);
}
/* Structure describing local BB data used for pseudo
@@ -354,12 +425,18 @@ mark_pseudo_dead (int regno)
if (!sparseset_bit_p (pseudos_live, regno))
return;
+ /* Just return if regno have partial subreg live for subreg access. */
+ if (has_subreg_object_p (regno) && !live_points->empty_live_p (regno))
+ return;
+
sparseset_clear_bit (pseudos_live, regno);
sparseset_set_bit (start_dying, regno);
}
+static void
+mark_regno_live (int regno, const subreg_range &range, machine_mode mode);
/* Mark register REGNO (pseudo or hard register) in MODE as being live
- and update BB_GEN_PSEUDOS. */
+ and update CURR_BB_INFO. */
static void
mark_regno_live (int regno, machine_mode mode)
{
@@ -370,6 +447,11 @@ mark_regno_live (int regno, machine_mode mode)
for (last = end_hard_regno (mode, regno); regno < last; regno++)
make_hard_regno_live (regno);
}
+ else if (has_subreg_object_p (regno))
+ {
+ machine_mode mode = lra_reg_info[regno].reg_mode;
+ mark_regno_live (regno, subreg_range (0, get_nregs (regno)), mode);
+ }
else
{
mark_pseudo_live (regno);
@@ -379,9 +461,26 @@ mark_regno_live (int regno, machine_mode mode)
}
}
+/* Like the above mark_regno_dead but for has_subreg_object_p REGNO. */
+static void
+mark_regno_live (int regno, const subreg_range &range, machine_mode mode)
+{
+ if (HARD_REGISTER_NUM_P (regno) || !has_subreg_object_p (regno))
+ mark_regno_live (regno, mode);
+ else
+ {
+ mark_pseudo_live (regno);
+ machine_mode mode = lra_reg_info[regno].reg_mode;
+ if (!range.full_p (get_nregs (regno)))
+ has_subreg_live_p = true;
+ add_subreg_range (curr_bb_info, regno, mode, range, false);
+ }
+}
+static void
+mark_regno_dead (int regno, const subreg_range &range, machine_mode mode);
/* Mark register REGNO (pseudo or hard register) in MODE as being dead
- and update BB_GEN_PSEUDOS and BB_KILLED_PSEUDOS. */
+ and update CURR_BB_INFO. */
static void
mark_regno_dead (int regno, machine_mode mode)
{
@@ -392,6 +491,12 @@ mark_regno_dead (int regno, machine_mode mode)
for (last = end_hard_regno (mode, regno); regno < last; regno++)
make_hard_regno_dead (regno);
}
+ else if (has_subreg_object_p (regno))
+ {
+ machine_mode mode = lra_reg_info[regno].reg_mode;
+ subreg_range range = subreg_range (0, get_nregs (regno));
+ mark_regno_dead (regno, range, mode);
+ }
else
{
mark_pseudo_dead (regno);
@@ -402,7 +507,22 @@ mark_regno_dead (int regno, machine_mode mode)
}
}
-
+/* Like the above mark_regno_dead but for has_subreg_object_p REGNO. */
+static void
+mark_regno_dead (int regno, const subreg_range &range, machine_mode mode)
+{
+ if (HARD_REGISTER_NUM_P (regno) || !has_subreg_object_p (regno))
+ mark_regno_dead (regno, mode);
+ else
+ {
+ mark_pseudo_dead (regno);
+ machine_mode mode = lra_reg_info[regno].reg_mode;
+ if (!range.full_p (get_nregs (regno)))
+ has_subreg_live_p = true;
+ remove_subreg_range (curr_bb_info, regno, mode, range);
+ add_subreg_range (curr_bb_info, regno, mode, range, true);
+ }
+}
/* This page contains code for making global live analysis of pseudos.
The code works only when pseudo live info is changed on a BB
@@ -823,6 +943,8 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
function_abi last_call_abi = default_function_abi;
reg_live_out = DF_LIVE_SUBREG_OUT (bb);
+ bitmap reg_live_partial_out = DF_LIVE_SUBREG_PARTIAL_OUT (bb);
+ subregs_live *range_out = DF_LIVE_SUBREG_RANGE_OUT (bb);
sparseset_clear (pseudos_live);
sparseset_clear (pseudos_live_through_calls);
sparseset_clear (pseudos_live_through_setjumps);
@@ -830,7 +952,12 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
hard_regs_live &= ~eliminable_regset;
EXECUTE_IF_SET_IN_BITMAP (reg_live_out, FIRST_PSEUDO_REGISTER, j, bi)
{
- update_pseudo_point (j, curr_point, USE_POINT);
+ if (bitmap_bit_p (reg_live_partial_out, j) && has_subreg_object_p (j))
+ for (const subreg_range &r : range_out->lives.at (j).ranges)
+ update_pseudo_point (j, get_range_hard_regs (j, r), curr_point,
+ USE_POINT);
+ else
+ update_pseudo_point (j, curr_point, USE_POINT);
mark_pseudo_live (j);
}
@@ -1023,8 +1150,11 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
for (reg = curr_id->regs; reg != NULL; reg = reg->next)
if (reg->type != OP_IN)
{
- update_pseudo_point (reg->regno, curr_point, USE_POINT);
- mark_regno_live (reg->regno, reg->biggest_mode);
+ const subreg_range &range = subreg_range (reg->start, reg->end);
+ update_pseudo_point (reg->regno,
+ get_range_hard_regs (reg->regno, range),
+ curr_point, USE_POINT);
+ mark_regno_live (reg->regno, range, reg->biggest_mode);
/* ??? Should be a no-op for unused registers. */
check_pseudos_live_through_calls (reg->regno, last_call_abi);
}
@@ -1045,17 +1175,20 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
/* See which defined values die here. */
for (reg = curr_id->regs; reg != NULL; reg = reg->next)
- if (reg->type != OP_IN
- && ! reg_early_clobber_p (reg, n_alt) && ! reg->subreg_p)
+ if (reg->type != OP_IN && !reg_early_clobber_p (reg, n_alt)
+ && (!reg->subreg_p || has_subreg_object_p (reg->regno)))
{
+ const subreg_range &range = subreg_range (reg->start, reg->end);
if (reg->type == OP_OUT)
- update_pseudo_point (reg->regno, curr_point, DEF_POINT);
- mark_regno_dead (reg->regno, reg->biggest_mode);
+ update_pseudo_point (reg->regno,
+ get_range_hard_regs (reg->regno, range),
+ curr_point, DEF_POINT);
+ mark_regno_dead (reg->regno, range, reg->biggest_mode);
}
for (reg = curr_static_id->hard_regs; reg != NULL; reg = reg->next)
- if (reg->type != OP_IN
- && ! reg_early_clobber_p (reg, n_alt) && ! reg->subreg_p)
+ if (reg->type != OP_IN && !reg_early_clobber_p (reg, n_alt)
+ && !reg->subreg_p)
make_hard_regno_dead (reg->regno);
if (curr_id->arg_hard_regs != NULL)
@@ -1086,7 +1219,7 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
/* Increment the current program point if we must. */
if (sparseset_contains_pseudos_p (unused_set)
- || sparseset_contains_pseudos_p (start_dying))
+ || sparseset_contains_pseudos_p (start_dying) || has_subreg_live_p)
next_program_point (curr_point, freq);
/* If we removed the source reg from a simple register copy from the
@@ -1107,9 +1240,12 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
for (reg = curr_id->regs; reg != NULL; reg = reg->next)
if (reg->type != OP_OUT)
{
+ const subreg_range &range = subreg_range (reg->start, reg->end);
if (reg->type == OP_IN)
- update_pseudo_point (reg->regno, curr_point, USE_POINT);
- mark_regno_live (reg->regno, reg->biggest_mode);
+ update_pseudo_point (reg->regno,
+ get_range_hard_regs (reg->regno, range),
+ curr_point, USE_POINT);
+ mark_regno_live (reg->regno, range, reg->biggest_mode);
check_pseudos_live_through_calls (reg->regno, last_call_abi);
}
@@ -1129,22 +1265,25 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
/* Mark early clobber outputs dead. */
for (reg = curr_id->regs; reg != NULL; reg = reg->next)
- if (reg->type != OP_IN
- && reg_early_clobber_p (reg, n_alt) && ! reg->subreg_p)
+ if (reg->type != OP_IN && reg_early_clobber_p (reg, n_alt)
+ && (!reg->subreg_p || has_subreg_object_p (reg->regno)))
{
+ const subreg_range &range = subreg_range (reg->start, reg->end);
if (reg->type == OP_OUT)
- update_pseudo_point (reg->regno, curr_point, DEF_POINT);
- mark_regno_dead (reg->regno, reg->biggest_mode);
+ update_pseudo_point (reg->regno,
+ get_range_hard_regs (reg->regno, range),
+ curr_point, DEF_POINT);
+ mark_regno_dead (reg->regno, range, reg->biggest_mode);
/* We're done processing inputs, so make sure early clobber
operands that are both inputs and outputs are still live. */
if (reg->type == OP_INOUT)
- mark_regno_live (reg->regno, reg->biggest_mode);
+ mark_regno_live (reg->regno, range, reg->biggest_mode);
}
for (reg = curr_static_id->hard_regs; reg != NULL; reg = reg->next)
- if (reg->type != OP_IN
- && reg_early_clobber_p (reg, n_alt) && ! reg->subreg_p)
+ if (reg->type != OP_IN && reg_early_clobber_p (reg, n_alt)
+ && !reg->subreg_p)
{
struct lra_insn_reg *reg2;
@@ -1160,7 +1299,7 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
/* Increment the current program point if we must. */
if (sparseset_contains_pseudos_p (dead_set)
- || sparseset_contains_pseudos_p (start_dying))
+ || sparseset_contains_pseudos_p (start_dying) || has_subreg_live_p)
next_program_point (curr_point, freq);
/* Update notes. */
@@ -1293,13 +1432,17 @@ process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
EXECUTE_IF_SET_IN_SPARSESET (pseudos_live, i)
{
- update_pseudo_point (i, curr_point, DEF_POINT);
+ if (has_subreg_object_p (i))
+ update_pseudo_point (i, live_points->subreg_live_ranges.at (i),
+ curr_point, DEF_POINT);
+ else
+ update_pseudo_point (i, curr_point, DEF_POINT);
mark_pseudo_dead (i);
}
- EXECUTE_IF_SET_IN_BITMAP (DF_LIVE_SUBREG_IN (bb), FIRST_PSEUDO_REGISTER, j,
- bi)
- {
+ EXECUTE_IF_SET_IN_BITMAP (DF_LIVE_SUBREG_IN (bb), FIRST_PSEUDO_REGISTER, j,
+ bi)
+ {
if (sparseset_cardinality (pseudos_live_through_calls) == 0)
break;
if (sparseset_bit_p (pseudos_live_through_calls, j))
@@ -1400,7 +1543,8 @@ remove_some_program_points_and_update_live_ranges (void)
next_r = r->next;
r->start = map[r->start];
r->finish = map[r->finish];
- if (prev_r == NULL || prev_r->start > r->finish + 1)
+ if (prev_r == NULL || prev_r->start > r->finish + 1
+ || !prev_r->subreg.same_p (r->subreg))
{
prev_r = r;
continue;
@@ -1418,8 +1562,18 @@ remove_some_program_points_and_update_live_ranges (void)
void
lra_print_live_range_list (FILE *f, lra_live_range_t r)
{
- for (; r != NULL; r = r->next)
- fprintf (f, " [%d..%d]", r->start, r->finish);
+ if (r != NULL && has_subreg_object_p (r->regno))
+ {
+ for (; r != NULL; r = r->next)
+ {
+ fprintf (f, " [%d..%d]{", r->start, r->finish);
+ r->subreg.dump (f);
+ fprintf (f, "}");
+ }
+ }
+ else
+ for (; r != NULL; r = r->next)
+ fprintf (f, " [%d..%d]", r->start, r->finish);
fprintf (f, "\n");
}
@@ -1492,7 +1646,84 @@ compress_live_ranges (void)
}
}
-
+/* Use to temp record subregs live range in create_subregs_live_ranges function.
+ */
+class subreg_live_item
+{
+public:
+ subreg_ranges subreg;
+ int start, finish;
+};
+
+/* Create subreg live ranges from objects def/use point info. */
+static void
+create_subregs_live_ranges ()
+{
+ for (const auto &subreg_point_it : live_points->subreg_points)
+ {
+ unsigned int regno = subreg_point_it.first;
+ const class live_points &points = subreg_point_it.second;
+ class lra_reg *reg_info = &lra_reg_info[regno];
+ std::vector<subreg_live_item> temps;
+ gcc_assert (has_subreg_object_p (regno));
+ for (const auto &point_it : points.points)
+ {
+ int point = point_it.first;
+ const live_point ®s = point_it.second;
+ gcc_assert (temps.empty () || temps.back ().finish <= point);
+ if (!regs.use_reg.empty_p ())
+ {
+ if (temps.empty ())
+ temps.push_back ({regs.use_reg, point, -1});
+ else if (temps.back ().finish == -1)
+ {
+ if (!temps.back ().subreg.same_p (regs.use_reg))
+ {
+ if (temps.back ().start == point)
+ temps.back ().subreg.add_ranges (regs.use_reg);
+ else
+ {
+ temps.back ().finish = point - 1;
+
+ subreg_ranges temp = regs.use_reg;
+ temp.add_ranges (temps.back ().subreg);
+ temps.push_back ({temp, point, -1});
+ }
+ }
+ }
+ else if (temps.back ().subreg.same_p (regs.use_reg)
+ && (temps.back ().finish == point
+ || temps.back ().finish + 1 == point))
+ temps.back ().finish = -1;
+ else
+ temps.push_back ({regs.use_reg, point, -1});
+ }
+ if (!regs.def_reg.empty_p ())
+ {
+ gcc_assert (!temps.empty ());
+ if (regs.def_reg.include_ranges_p (temps.back ().subreg))
+ temps.back ().finish = point;
+ else if (temps.back ().subreg.include_ranges_p (regs.def_reg))
+ {
+ temps.back ().finish = point;
+
+ subreg_ranges diff = temps.back ().subreg;
+ diff.remove_ranges (regs.def_reg);
+ temps.push_back ({diff, point + 1, -1});
+ }
+ else
+ gcc_unreachable ();
+ }
+ }
+
+ gcc_assert (reg_info->live_ranges == NULL);
+
+ for (const subreg_live_item &item : temps)
+ reg_info->live_ranges
+ = create_live_range (regno, item.subreg, item.start, item.finish,
+ reg_info->live_ranges);
+ }
+}
/* The number of the current live range pass. */
int lra_live_range_iter;
@@ -1573,6 +1804,8 @@ lra_create_live_ranges_1 (bool all_p, bool dead_insn_p)
int n = inverted_rev_post_order_compute (cfun, rpo);
lra_assert (n == n_basic_blocks_for_fn (cfun));
bb_live_change_p = false;
+ has_subreg_live_p = false;
+ live_points = new subregs_live_points ();
for (i = 0; i < n; ++i)
{
bb = BASIC_BLOCK_FOR_FN (cfun, rpo[i]);
@@ -1655,9 +1888,14 @@ lra_create_live_ranges_1 (bool all_p, bool dead_insn_p)
}
}
lra_live_max_point = curr_point;
+ create_subregs_live_ranges ();
if (lra_dump_file != NULL)
- print_live_ranges (lra_dump_file);
+ {
+ live_points->dump (lra_dump_file);
+ print_live_ranges (lra_dump_file);
+ }
/* Clean up. */
+ delete live_points;
sparseset_free (unused_set);
sparseset_free (dead_set);
sparseset_free (start_dying);
@@ -566,6 +566,54 @@ lra_asm_insn_error (rtx_insn *insn)
/* Pools for insn reg info. */
object_allocator<lra_insn_reg> lra_insn_reg_pool ("insn regs");
+/* Return the subreg range of rtx SUBREG in blocks. */
+static subreg_range
+get_range_blocks (int regno, bool subreg_p, machine_mode reg_mode,
+ poly_int64 offset, poly_int64 size)
+{
+ gcc_assert (has_subreg_object_p (regno));
+ int nblocks = get_nblocks (reg_mode);
+ if (!subreg_p)
+ return subreg_range (0, nblocks);
+
+ poly_int64 unit_size = REGMODE_NATURAL_SIZE (reg_mode);
+ poly_int64 left = offset + size;
+
+ int subreg_start = -1;
+ int subreg_nregs = -1;
+ for (int i = 0; i < nblocks; i += 1)
+ {
+ poly_int64 right = unit_size * (i + 1);
+ if (subreg_start < 0 && maybe_lt (offset, right))
+ subreg_start = i;
+ if (subreg_nregs < 0 && maybe_le (left, right))
+ {
+ subreg_nregs = i + 1 - subreg_start;
+ break;
+ }
+ }
+ gcc_assert (subreg_start >= 0 && subreg_nregs > 0);
+ return subreg_range (subreg_start, subreg_start + subreg_nregs);
+}
+
+/* Return the subreg range of rtx SUBREG in hard regs. */
+subreg_range
+get_range_hard_regs (int regno, const subreg_range &r)
+{
+ if (!has_subreg_object_p (regno) || lra_reg_info[regno].reg_mode == VOIDmode)
+ return subreg_range (0, 1);
+ enum reg_class aclass = lra_get_allocno_class (regno);
+ gcc_assert (aclass != NO_REGS);
+ int nregs = ira_reg_class_max_nregs[aclass][lra_reg_info[regno].reg_mode];
+ int nblocks = get_nblocks (lra_reg_info[regno].reg_mode);
+ int times = nblocks / nregs;
+ gcc_assert (nblocks >= nregs && times * nregs == nblocks);
+ int start = r.start / times;
+ int end = CEIL (r.end, times);
+
+ return subreg_range (start, end);
+}
+
/* Create LRA insn related info about a reference to REGNO in INSN
with TYPE (in/out/inout), biggest reference mode MODE, flag that it
is reference through subreg (SUBREG_P), and reference to the next
@@ -573,21 +621,49 @@ object_allocator<lra_insn_reg> lra_insn_reg_pool ("insn regs");
alternatives in which it can be early clobbered are given by
EARLY_CLOBBER_ALTS. */
static struct lra_insn_reg *
-new_insn_reg (rtx_insn *insn, int regno, enum op_type type,
- machine_mode mode, bool subreg_p,
- alternative_mask early_clobber_alts,
+new_insn_reg (rtx_insn *insn, int regno, enum op_type type, poly_int64 size,
+ poly_int64 offset, machine_mode mode, machine_mode reg_mode,
+ bool subreg_p, alternative_mask early_clobber_alts,
struct lra_insn_reg *next)
{
lra_insn_reg *ir = lra_insn_reg_pool.allocate ();
ir->type = type;
ir->biggest_mode = mode;
- if (NONDEBUG_INSN_P (insn)
- && partial_subreg_p (lra_reg_info[regno].biggest_mode, mode))
- lra_reg_info[regno].biggest_mode = mode;
+ if (NONDEBUG_INSN_P (insn))
+ {
+ if (partial_subreg_p (lra_reg_info[regno].biggest_mode, mode))
+ {
+ lra_reg_info[regno].biggest_mode = mode;
+ }
+
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ if (lra_reg_info[regno].reg_mode == VOIDmode)
+ lra_reg_info[regno].reg_mode = reg_mode;
+ else
+ gcc_assert (maybe_eq (GET_MODE_SIZE (lra_reg_info[regno].reg_mode),
+ GET_MODE_SIZE (reg_mode)));
+ }
+ }
ir->subreg_p = subreg_p;
ir->early_clobber_alts = early_clobber_alts;
ir->regno = regno;
ir->next = next;
+ if (has_subreg_object_p (regno))
+ {
+ const subreg_range &r
+ = get_range_blocks (regno, subreg_p, reg_mode, offset, size);
+ ir->start = r.start;
+ ir->end = r.end;
+ const subreg_range &r_hard = get_range_hard_regs (regno, r);
+ ir->start_reg = r_hard.start;
+ ir->end_reg = r_hard.end;
+ }
+ else
+ {
+ ir->start = 0;
+ ir->end = 1;
+ }
return ir;
}
@@ -887,11 +963,18 @@ collect_non_operand_hard_regs (rtx_insn *insn, rtx *x,
return list;
mode = GET_MODE (op);
subreg_p = false;
+ poly_int64 size = GET_MODE_SIZE (mode);
+ poly_int64 offset = 0;
if (code == SUBREG)
{
mode = wider_subreg_mode (op);
if (read_modify_subreg_p (op))
- subreg_p = true;
+ {
+ offset = SUBREG_BYTE (op);
+ subreg_p = true;
+ }
+ else
+ size = GET_MODE_SIZE (GET_MODE (SUBREG_REG (op)));
op = SUBREG_REG (op);
code = GET_CODE (op);
}
@@ -925,7 +1008,8 @@ collect_non_operand_hard_regs (rtx_insn *insn, rtx *x,
&& ! (FIRST_STACK_REG <= regno
&& regno <= LAST_STACK_REG));
#endif
- list = new_insn_reg (data->insn, regno, type, mode, subreg_p,
+ list = new_insn_reg (data->insn, regno, type, size, offset, mode,
+ GET_MODE (op), subreg_p,
early_clobber ? ALL_ALTERNATIVES : 0, list);
}
}
@@ -1354,6 +1438,7 @@ initialize_lra_reg_info_element (int i)
lra_reg_info[i].preferred_hard_regno_profit1 = 0;
lra_reg_info[i].preferred_hard_regno_profit2 = 0;
lra_reg_info[i].biggest_mode = VOIDmode;
+ lra_reg_info[i].reg_mode = VOIDmode;
lra_reg_info[i].live_ranges = NULL;
lra_reg_info[i].nrefs = lra_reg_info[i].freq = 0;
lra_reg_info[i].last_reload = 0;
@@ -1459,7 +1544,21 @@ lra_get_copy (int n)
return copy_vec[n];
}
-
+/* Return true if REG occupied the same blocks as OFFSET + SIZE subreg. */
+static bool
+reg_same_range_p (lra_insn_reg *reg, poly_int64 offset, poly_int64 size,
+ bool subreg_p)
+{
+ if (has_subreg_object_p (reg->regno))
+ {
+ const subreg_range &r
+ = get_range_blocks (reg->regno, subreg_p,
+ lra_reg_info[reg->regno].reg_mode, offset, size);
+ return r.start == reg->start && r.end == reg->end;
+ }
+ else
+ return true;
+}
/* This page contains code dealing with info about registers in
insns. */
@@ -1483,11 +1582,18 @@ add_regs_to_insn_regno_info (lra_insn_recog_data_t data, rtx x,
code = GET_CODE (x);
mode = GET_MODE (x);
subreg_p = false;
+ poly_int64 size = GET_MODE_SIZE (mode);
+ poly_int64 offset = 0;
if (GET_CODE (x) == SUBREG)
{
mode = wider_subreg_mode (x);
if (read_modify_subreg_p (x))
- subreg_p = true;
+ {
+ offset = SUBREG_BYTE (x);
+ subreg_p = true;
+ }
+ else
+ size = GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)));
x = SUBREG_REG (x);
code = GET_CODE (x);
}
@@ -1499,7 +1605,8 @@ add_regs_to_insn_regno_info (lra_insn_recog_data_t data, rtx x,
expand_reg_info ();
if (bitmap_set_bit (&lra_reg_info[regno].insn_bitmap, INSN_UID (insn)))
{
- data->regs = new_insn_reg (data->insn, regno, type, mode, subreg_p,
+ data->regs = new_insn_reg (data->insn, regno, type, size, offset,
+ mode, GET_MODE (x), subreg_p,
early_clobber_alts, data->regs);
return;
}
@@ -1508,12 +1615,14 @@ add_regs_to_insn_regno_info (lra_insn_recog_data_t data, rtx x,
for (curr = data->regs; curr != NULL; curr = curr->next)
if (curr->regno == regno)
{
- if (curr->subreg_p != subreg_p || curr->biggest_mode != mode)
+ if (!reg_same_range_p (curr, offset, size, subreg_p)
+ || curr->biggest_mode != mode)
/* The info cannot be integrated into the found
structure. */
- data->regs = new_insn_reg (data->insn, regno, type, mode,
- subreg_p, early_clobber_alts,
- data->regs);
+ data->regs
+ = new_insn_reg (data->insn, regno, type, size, offset, mode,
+ GET_MODE (x), subreg_p, early_clobber_alts,
+ data->regs);
else
{
if (curr->type != type)