regrename.c: Add general comment describing the pass.

* regrename.c: Add general comment describing the pass.
	(struct du_head): Remove 'length' field.
	(get_element, merge_sort_comparison, merge, sort_du_head): Remove.
	(regrename_optimize): Do not sort chains.  Rework comments, add others.
	Force renaming to the preferred class (if any) in the first pass and do
	not consider registers that belong to it in the second pass.
	(create_new_chain): Do not set 'length' field.
	(scan_rtx_reg): Likewise.

From-SVN: r168404
This commit is contained in:
Eric Botcazou 2011-01-02 17:28:24 +00:00
parent 4c0c322882
commit 6656b2ac05
2 changed files with 81 additions and 206 deletions

View File

@ -1,3 +1,14 @@
2011-01-02 Eric Botcazou <ebotcazou@adacore.com>
* regrename.c: Add general comment describing the pass.
(struct du_head): Remove 'length' field.
(get_element, merge_sort_comparison, merge, sort_du_head): Remove.
(regrename_optimize): Do not sort chains. Rework comments, add others.
Force renaming to the preferred class (if any) in the first pass and do
not consider registers that belong to it in the second pass.
(create_new_chain): Do not set 'length' field.
(scan_rtx_reg): Likewise.
2011-01-02 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/47140
@ -6,9 +17,8 @@
to bit_value_binop.
PR rtl-optimization/47028
* cfgexpand.c (gimple_expand_cfg): Insert entry edge
insertions after parm_birth_insn instead of at the beginning
of first bb.
* cfgexpand.c (gimple_expand_cfg): Insert entry edge insertions after
parm_birth_insn instead of at the beginning of first bb.
2011-01-02 Mingjie Xing <mingjie.xing@gmail.com>
@ -18,14 +28,13 @@
2011-01-01 Jan Hubicka <jh@suse.cz>
* tree-loop-distribution.c (tree_loop_distribution): Do not
use freed memory.
* tree-loop-distribution.c (tree_loop_distribution): Do not use freed
memory.
2011-01-01 Kai Tietz <kai.tietz@onevision.com>
PR target/38662
* tree.c (type_hash_eq): Call
language hook for METHOD_TYPEs, too.
* tree.c (type_hash_eq): Call language hook for METHOD_TYPEs, too.
Copyright (C) 2011 Free Software Foundation, Inc.

View File

@ -40,10 +40,33 @@
#include "df.h"
#include "target.h"
/* This file implements the RTL register renaming pass of the compiler. It is
a semi-local pass whose goal is to maximize the usage of the register file
of the processor by substituting registers for others in the solution given
by the register allocator. The algorithm is as follows:
1. Local def/use chains are built: within each basic block, chains are
opened and closed; if a chain isn't closed at the end of the block,
it is dropped.
2. For each chain, the set of possible renaming registers is computed.
This takes into account the renaming of previously processed chains.
Optionally, a preferred class is computed for the renaming register.
3. The best renaming register is computed for the chain in the above set,
using a round-robin allocation. If a preferred class exists, then the
round-robin allocation is done within the class first, if possible.
The round-robin allocation of renaming registers itself is global.
4. If a renaming register has been found, it is substituted in the chain.
Targets can parameterize the pass by specifying a preferred class for the
renaming register for a given (super)class of registers to be renamed. */
#if HOST_BITS_PER_WIDE_INT <= MAX_RECOG_OPERANDS
#error "Use a different bitmap implementation for untracked_operands."
#endif
/* We keep linked lists of DU_HEAD structures, each of which describes
a chain of occurrences of a reg. */
struct du_head
@ -52,11 +75,6 @@ struct du_head
struct du_head *next_chain;
/* The first and last elements of this chain. */
struct du_chain *first, *last;
/* The number of elements of this chain, excluding those corresponding
to references of the register in debug insns. The du_head linked
list can be sorted by this, and register-rename can prefer
register classes according to this order. */
int length;
/* Describes the register being tracked. */
unsigned regno, nregs;
@ -160,150 +178,6 @@ merge_overlapping_regs (HARD_REG_SET *pset, struct du_head *head)
}
}
/* Return the Nth element in LIST. If LIST contains less than N
elements, return the last one. */
static struct du_head *
get_element (struct du_head *list, int n)
{
while (n-- && list->next_chain != NULL)
list = list->next_chain;
return list;
}
/* Comparison function of merge sort. Return true if A is less than
B, otherwise return false. */
static inline int
merge_sort_comparison(const struct du_head *a,
const struct du_head *b)
{
return a->length < b->length;
}
/* Merge the first 2 sub-lists of LENGTH nodes contained in the
linked list pointed to by START_NODE. Update START_NODE to point
to the merged nodes, and return a pointer to the last merged
node. Return NULL if START_NODE doesn't contain enough
elements, or this pass of merge is done. */
static struct du_head *
merge(struct du_head **start_node, int length)
{
int i, left_count, right_count;
struct du_head *left, *right;
/* Current node of sort result. */
struct du_head *current_sorted_node;
/* Tail node of sort, used to connect with next piece of list. */
struct du_head *current_tail_node;
if (*start_node == NULL)
return NULL;
left = right = *start_node;
right_count = left_count = 0;
/* Step RIGHT along the list by LENGTH places. */
for (i = 0; i < length; i++)
{
right = right->next_chain;
if (right == NULL)
{
return NULL;
}
}
/* Initialize current_sorted_node. */
if (merge_sort_comparison (left, right))
{
++right_count;
current_sorted_node = right;
*start_node = right;
right = right->next_chain;
}
else
{
++left_count;
current_sorted_node = left;
left = left->next_chain;
}
while (1)
{
/* Choose LEFT or RIGHT to take the next element from. If
either is empty, choose from the other one. */
if (left_count == length || left == NULL)
{
current_sorted_node->next_chain = right;
current_tail_node = get_element (current_sorted_node,
length - right_count);
break;
}
else if (right_count == length || right == NULL)
{
/* Save the head node of next piece of linked list. */
struct du_head *tmp = current_sorted_node->next_chain;
current_sorted_node->next_chain = left;
current_tail_node
= get_element (current_sorted_node,
length - left_count);
/* Connect sorted list to next piece of list. */
current_tail_node->next_chain = tmp;
break;
}
else
{
/* Normal merge operations. If both LEFT and RIGHT are
non-empty, compare the first element of each and choose
the lower one. */
if (merge_sort_comparison (left, right))
{
right_count++;
current_sorted_node->next_chain = right;
right = right->next_chain;
}
else
{
left_count++;
current_sorted_node->next_chain = left;
left = left->next_chain;
}
current_sorted_node = current_sorted_node->next_chain;
}
}
/* Return NULL if this pass of merge is done. */
return (current_tail_node->next_chain ? current_tail_node : NULL);
}
/* Sort the linked list pointed to by HEAD. The algorithm is a
non-recursive merge sort to linked list. */
static void
sort_du_head (struct du_head **head)
{
int current_length = 1;
struct du_head *last_tail;
/* In each pass, lists of size current_length is merged to
lists of size 2xcurrent_length (Initially current_length
is 1). */
while (1)
{
last_tail = merge(head, current_length);
if (last_tail != NULL)
{
do
last_tail = merge (&last_tail->next_chain, current_length);
while (last_tail != NULL);
current_length *= 2;
}
else
break;
}
}
/* Check if NEW_REG can be the candidate register to rename for
REG in THIS_HEAD chain. THIS_UNAVAILABLE is a set of unavailable hard
registers. */
@ -392,8 +266,6 @@ regrename_optimize (void)
if (dump_file)
dump_def_use_chain (all_chains);
sort_du_head (&all_chains);
CLEAR_HARD_REG_SET (unavailable);
/* Don't clobber traceback for noreturn functions. */
if (frame_pointer_needed)
@ -413,8 +285,9 @@ regrename_optimize (void)
HARD_REG_SET this_unavailable;
int reg = this_head->regno;
int pass;
enum reg_class superunion_class = NO_REGS;
enum reg_class super_class = NO_REGS;
enum reg_class preferred_class;
bool has_preferred_class;
all_chains = this_head->next_chain;
@ -444,79 +317,78 @@ regrename_optimize (void)
COPY_HARD_REG_SET (this_unavailable, unavailable);
/* Iterate elements in chain in order to:
/* Iterate over elements in the chain in order to:
1. Count number of uses, and narrow the set of registers we can
use for renaming.
use for renaming.
2. Compute the superunion of register classes in this chain. */
n_uses = 0;
superunion_class = NO_REGS;
super_class = NO_REGS;
for (tmp = this_head->first; tmp; tmp = tmp->next_use)
{
if (DEBUG_INSN_P (tmp->insn))
continue;
n_uses++;
IOR_COMPL_HARD_REG_SET (this_unavailable,
reg_class_contents[tmp->cl]);
superunion_class
= reg_class_superunion[(int) superunion_class][(int) tmp->cl];
super_class
= reg_class_superunion[(int) super_class][(int) tmp->cl];
}
if (n_uses < 2)
continue;
/* Further narrow the set of registers we can use for renaming.
If the chain needs a call-saved register, mark the call-used
registers as unavailable. */
if (this_head->need_caller_save_reg)
IOR_HARD_REG_SET (this_unavailable, call_used_reg_set);
/* And mark registers that overlap its lifetime as unavailable. */
merge_overlapping_regs (&this_unavailable, this_head);
/* Compute preferred rename class of super union of all the classes
on the chain. */
preferred_class
= (enum reg_class) targetm.preferred_rename_class(superunion_class);
/* The register iteration order here is "preferred-register-first".
Firstly(pass == 0), we iterate registers belong to PREFERRED_CLASS,
if we find a new register, we stop immeidately.
Otherwise, we iterate over registers that don't belong to
PREFERRED_CLASS.
/* Compute preferred rename class of super union of all the classes
in the chain. */
preferred_class
= (enum reg_class) targetm.preferred_rename_class (super_class);
/* If PREFERRED_CLASS is not NO_REGS, we iterate in the first pass
over registers that belong to PREFERRED_CLASS and try to find the
best register within the class. If that failed, we iterate in
the second pass over registers that don't belong to the class.
If PREFERRED_CLASS is NO_REGS, we iterate over all registers in
ascending order without any preference. */
for (pass = (preferred_class == NO_REGS ? 1 : 0); pass < 2; pass++)
has_preferred_class = (preferred_class != NO_REGS);
for (pass = (has_preferred_class ? 0 : 1); pass < 2; pass++)
{
bool found = false;
/* Now potential_regs is a reasonable approximation, let's
have a closer look at each register still in there. */
for (new_reg = 0; new_reg < FIRST_PSEUDO_REGISTER; new_reg++)
{
/* Iterate registers first in prefered class. */
if (pass == 0
&& !TEST_HARD_REG_BIT (reg_class_contents[preferred_class],
new_reg))
if (has_preferred_class
&& (pass == 0)
!= TEST_HARD_REG_BIT
(reg_class_contents[preferred_class], new_reg))
continue;
/* In the first pass, we force the renaming of registers that
don't belong to PREFERRED_CLASS to registers that do, even
though the latters were used not very long ago. */
if (check_new_reg_p (reg, new_reg, this_head,
this_unavailable))
this_unavailable)
&& ((pass == 0
&& !TEST_HARD_REG_BIT
(reg_class_contents[preferred_class],
best_new_reg))
|| tick[best_new_reg] > tick[new_reg]))
{
if (tick[best_new_reg] > tick[new_reg])
{
enum machine_mode mode
= GET_MODE (*this_head->first->loc);
best_new_reg = new_reg;
best_nregs = hard_regno_nregs[new_reg][mode];
/* If we find a new reg in our preferred class,
stop immediately. */
if (best_new_reg != reg && pass == 0)
{
found = true;
break;
}
}
enum machine_mode mode
= GET_MODE (*this_head->first->loc);
best_new_reg = new_reg;
best_nregs = hard_regno_nregs[new_reg][mode];
}
}
if (found)
if (pass == 0 && best_new_reg != reg)
break;
}
if (dump_file)
{
fprintf (dump_file, "Register %s in insn %d",
@ -732,7 +604,6 @@ create_new_chain (unsigned this_regno, unsigned this_nregs, rtx *loc,
head->need_caller_save_reg = 0;
head->cannot_rename = 0;
head->terminated = 0;
head->length = 0;
VEC_safe_push (du_head_p, heap, id_to_chain, head);
head->id = current_id++;
@ -778,8 +649,6 @@ create_new_chain (unsigned this_regno, unsigned this_nregs, rtx *loc,
this_du->loc = loc;
this_du->insn = insn;
this_du->cl = cl;
head->length = 1;
}
static void
@ -868,9 +737,6 @@ scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
else
head->last->next_use = this_du;
head->last = this_du;
if (!DEBUG_INSN_P (insn))
head->length++;
}
/* Avoid adding the same location in a DEBUG_INSN multiple times,
which could happen with non-exact overlap. */