/* Building internal representation for IRA.
Copyright (C) 2006-2020 Free Software Foundation, Inc.
Contributed by Vladimir Makarov <vmakarov@redhat.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "target.h"
#include "rtl.h"
#include "predict.h"
#include "df.h"
#include "insn-config.h"
#include "regs.h"
#include "memmodel.h"
#include "ira.h"
#include "ira-int.h"
#include "sparseset.h"
#include "cfgloop.h"
static ira_copy_t find_allocno_copy (ira_allocno_t, ira_allocno_t, rtx_insn *,
ira_loop_tree_node_t);
/* The root of the loop tree corresponding to the all function. */
ira_loop_tree_node_t ira_loop_tree_root;
/* Height of the loop tree. */
int ira_loop_tree_height;
/* All nodes representing basic blocks are referred through the
following array. We cannot use basic block member `aux' for this
because it is used for insertion of insns on edges. */
ira_loop_tree_node_t ira_bb_nodes;
/* All nodes representing loops are referred through the following
array. */
ira_loop_tree_node_t ira_loop_nodes;
/* And size of the ira_loop_nodes array. */
unsigned int ira_loop_nodes_count;
/* Map regno -> allocnos with given regno (see comments for
allocno member `next_regno_allocno'). */
ira_allocno_t *ira_regno_allocno_map;
/* Array of references to all allocnos. The order number of the
allocno corresponds to the index in the array. Removed allocnos
have NULL element value. */
ira_allocno_t *ira_allocnos;
/* Sizes of the previous array. */
int ira_allocnos_num;
/* Count of conflict record structures we've created, used when creating
a new conflict id. */
int ira_objects_num;
/* Map a conflict id to its conflict record. */
ira_object_t *ira_object_id_map;
/* Array of references to all allocno preferences. The order number
of the preference corresponds to the index in the array. */
ira_pref_t *ira_prefs;
/* Size of the previous array. */
int ira_prefs_num;
/* Array of references to all copies. The order number of the copy
corresponds to the index in the array. Removed copies have NULL
element value. */
ira_copy_t *ira_copies;
/* Size of the previous array. */
int ira_copies_num;
/* LAST_BASIC_BLOCK before generating additional insns because of live
range splitting. Emitting insns on a critical edge creates a new
basic block. */
static int last_basic_block_before_change;
/* Initialize some members in loop tree node NODE. Use LOOP_NUM for
the member loop_num. */
static void
init_loop_tree_node (struct ira_loop_tree_node *node, int loop_num)
{
int max_regno = max_reg_num ();
node->regno_allocno_map
= (ira_allocno_t *) ira_allocate (sizeof (ira_allocno_t) * max_regno);
memset (node->regno_allocno_map, 0, sizeof (ira_allocno_t) * max_regno);
memset (node->reg_pressure, 0, sizeof (node->reg_pressure));
node->all_allocnos = ira_allocate_bitmap ();
node->modified_regnos = ira_allocate_bitmap ();
node->border_allocnos = ira_allocate_bitmap ();
node->local_copies = ira_allocate_bitmap ();
node->loop_num = loop_num;
node->children = NULL;
node->subloops = NULL;
}
/* The following function allocates the loop tree nodes. If
CURRENT_LOOPS is NULL, the nodes corresponding to the loops (except
the root which corresponds the all function) will be not allocated
but nodes will still be allocated for basic blocks. */
static void
create_loop_tree_nodes (void)
{
unsigned int i, j;
bool skip_p;
edge_iterator ei;
edge e;
vec<edge> edges;
loop_p loop;
ira_bb_nodes
= ((struct ira_loop_tree_node *)
ira_allocate (sizeof (struct ira_loop_tree_node)
* last_basic_block_for_fn (cfun)));
last_basic_block_before_change = last_basic_block_for_fn (cfun);
for (i = 0; i < (unsigned int) last_basic_block_for_fn (cfun); i++)
{
ira_bb_nodes[i].regno_allocno_map = NULL;
memset (ira_bb_nodes[i].reg_pressure, 0,
sizeof (ira_bb_nodes[i].reg_pressure));
ira_bb_nodes[i].all_allocnos = NULL;
ira_bb_nodes[i].modified_regnos = NULL;
ira_bb_nodes[i].border_allocnos = NULL;
ira_bb_nodes[i].local_copies = NULL;
}
if (current_loops == NULL)
{
ira_loop_nodes_count = 1;
ira_loop_nodes = ((struct ira_loop_tree_node *)
ira_allocate (sizeof (struct ira_loop_tree_node)));
init_loop_tree_node (ira_loop_nodes, 0);
return;
}
ira_loop_nodes_count = number_of_loops (cfun);
ira_loop_nodes = ((struct ira_loop_tree_node *)
ira_allocate (sizeof (struct ira_loop_tree_node)
* ira_loop_nodes_count));
FOR_EACH_VEC_SAFE_ELT (get_loops (cfun), i, loop)
{
if (loop_outer (loop) != NULL)
{
ira_loop_nodes[i].regno_allocno_map = NULL;
skip_p = false;
FOR_EACH_EDGE (e, ei, loop->header->preds)
if (e->src != loop->latch
&& (e->flags & EDGE_ABNORMAL) && EDGE_CRITICAL_P (e))
{
skip_p = true;
break;
}
if (skip_p)
continue;
edges = get_loop_exit_edges (loop);
FOR_EACH_VEC_ELT (edges, j, e)
if ((e->flags & EDGE_ABNORMAL) && EDGE_CRITICAL_P (e))
{
skip_p = true;
break;
}
edges.release ();
if (skip_p)
continue;
}
init_loop_tree_node (&ira_loop_nodes[i], loop->num);
}
}
/* The function returns TRUE if there are more one allocation
region. */
static bool
more_one_region_p (void)
{
unsigned int i;
loop_p loop;
if (current_loops != NULL)
FOR_EACH_VEC_SAFE_ELT (get_loops (cfun), i, loop)
if (ira_loop_nodes[i].regno_allocno_map != NULL
&& ira_loop_tree_root != &ira_loop_nodes[i])
return true;
return false;
}
/* Free the loop tree node of a loop. */
static void
finish_loop_tree_node (ira_loop_tree_node_t loop)
{
if (loop->regno_allocno_map != NULL)
{
ira_assert (loop->bb == NULL);
ira_free_bitmap (loop->local_copies);
ira_free_bitmap (loop->border_allocnos);
ira_free_bitmap (loop->modified_regnos);
ira_free_bitmap (loop->all_allocnos);
ira_free (loop->regno_allocno_map);
loop->regno_allocno_map = NULL;
}
}
/* Free the loop tree nodes. */
static void
finish_loop_tree_nodes (void)
{
unsigned int i;
for (i = 0; i < ira_loop_nodes_count; i++)
finish_loop_tree_node (&ira_loop_nodes[i]);
ira_free (ira_loop_nodes);
for (i = 0; i < (unsigned int) last_basic_block_before_change; i++)
{
if (ira_bb_nodes[i].local_copies != NULL)
ira_free_bitmap (ira_bb_nodes[i].local_copies);
if (ira_bb_nodes[i].border_allocnos != NULL)
ira_free_bitmap (ira_bb_nodes[i].border_allocnos);
if (ira_bb_nodes[i].modified_regnos != NULL)
ira_free_bitmap (ira_bb_nodes[i].modified_regnos);
if (ira_bb_nodes[i].all_allocnos != NULL)
ira_free_bitmap (ira_bb_nodes[i].all_allocnos);
if (ira_bb_nodes[i].regno_allocno_map != NULL)
ira_free (ira_bb_nodes[i].regno_allocno_map);
}
ira_free (ira_bb_nodes);
}
/* The following recursive function adds LOOP to the loop tree
hierarchy. LOOP is added only once. If LOOP is NULL we adding
loop designating the whole function when CFG loops are not
built. */
static void
add_loop_to_tree (class loop *loop)
{
int loop_num;
class loop *parent;
ira_loop_tree_node_t loop_node, parent_node;
/* We cannot use loop node access macros here because of potential
checking and because the nodes are not initialized enough
yet. */
if (loop != NULL && loop_outer (loop) != NULL)
add_loop_to_tree (loop_outer (loop));
loop_num = loop != NULL ? loop->num : 0;
if (ira_loop_nodes[loop_num].regno_allocno_map != NULL
&& ira_loop_nodes[loop_num].children == NULL)
{
/* We have not added loop node to the tree yet. */
loop_node = &ira_loop_nodes[loop_num];
loop_node->loop = loop;
loop_node->bb = NULL;
if (loop == NULL)
parent = NULL;
else
{
for (parent = loop_outer (loop);
parent != NULL;
parent = loop_outer (parent))
if (ira_loop_nodes[parent->num].regno_allocno_map != NULL)
break;
}
if (parent == NULL)
{
loop_node->next = NULL;
loop_node->subloop_next = NULL;
loop_node->parent = NULL;
}
else
{
parent_node = &ira_loop_nodes[parent->num];
loop_node->next = parent_node->children;
parent_node->children = loop_node;
loop_node->subloop_next = parent_node->subloops;
parent_node->subloops = loop_node;
loop_node->parent = parent_node;
}
}
}
/* The following recursive function sets up levels of nodes of the
tree given its root LOOP_NODE. The enumeration starts with LEVEL.
The function returns maximal value of level in the tree + 1. */
static int
setup_loop_tree_level (ira_loop_tree_node_t loop_node, int level)
{
int height, max_height;
ira_loop_tree_node_t subloop_node;
ira_assert (loop_node->bb == NULL);
loop_node->level = level;
max_height = level + 1;
for (subloop_node = loop_node->subloops;
subloop_node != NULL;
subloop_node = subloop_node->subloop_next)
{
ira_assert (subloop_node->bb == NULL);
height = setup_loop_tree_level (subloop_node, level + 1);
if (height > max_height)
max_height = height;
}
return max_height;
}
/* Create the loop tree. The algorithm is designed to provide correct
order of loops (they are ordered by their last loop BB) and basic
blocks in the chain formed by member next. */
static void
form_loop_tree (void)
{
basic_block bb;
class loop *parent;
ira_loop_tree_node_t bb_node, loop_node;
/* We cannot use loop/bb node access macros because of potential
checking and because the nodes are not initialized enough
yet. */
FOR_EACH_BB_FN (bb, cfun)
{
bb_node = &ira_bb_nodes[bb->index];
bb_node->bb = bb;
bb_node->loop = NULL;
bb_node->subloops = NULL;
bb_node->children = NULL;
bb_node->subloop_next = NULL;
bb_node->next = NULL;
if (current_loops == NULL)
parent = NULL;
else
{
for (parent = bb->loop_father;
parent != NULL;
parent = loop_outer (parent))
if (ira_loop_nodes[parent->num].regno_allocno_map != NULL)
break;
}
add_loop_to_tree (parent);
loop_node = &ira_loop_nodes[parent == NULL ? 0 : parent->num];
bb_node->next = loop_node->children;
bb_node->parent = loop_node;
loop_node->children = bb_node;
}
ira_loop_tree_root = IRA_LOOP_NODE_BY_INDEX (0);
ira_loop_tree_height = setup_loop_tree_level (ira_loop_tree_root, 0);
ira_assert (ira_loop_tree_root->regno_allocno_map != NULL);
}
/* Rebuild IRA_REGNO_ALLOCNO_MAP and REGNO_ALLOCNO_MAPs of the loop
tree nodes. */
static void
rebuild_regno_allocno_maps (void)
{
unsigned int l;
int max_regno, regno;
ira_allocno_t a;
ira_loop_tree_node_t loop_tree_node;
loop_p loop;
ira_allocno_iterator ai;
ira_assert (current_loops != NULL);
max_regno = max_reg_num ();
FOR_EACH_VEC_SAFE_ELT (get_loops (cfun), l, loop)
if (ira_loop_nodes[l].regno_allocno_map != NULL)
{
ira_free (ira_loop_nodes[l].regno_allocno_map);
ira_loop_nodes[l].regno_allocno_map
= (ira_allocno_t *) ira_allocate (sizeof (ira_allocno_t)
* max_regno);
memset (ira_loop_nodes[l].regno_allocno_map, 0,
sizeof (ira_allocno_t) * max_regno);
}
ira_free (ira_regno_allocno_map);
ira_regno_allocno_map
= (ira_allocno_t *) ira_allocate (max_regno * sizeof (ira_allocno_t));
memset (ira_regno_allocno_map, 0, max_regno * sizeof (ira_allocno_t));
FOR_EACH_ALLOCNO (a, ai)
{
if (ALLOCNO_CAP_MEMBER (a) != NULL)
/* Caps are not in the regno allocno maps. */
continue;
regno = ALLOCNO_REGNO (a);
loop_tree_node = ALLOCNO_LOOP_TREE_NODE (a);
ALLOCNO_NEXT_REGNO_ALLOCNO (a) = ira_regno_allocno_map[regno];
ira_regno_allocno_map[regno] = a;
if (loop_tree_node->regno_allocno_map[regno] == NULL)
/* Remember that we can create temporary allocnos to break
cycles in register shuffle. */
loop_tree_node->regno_allocno_map[regno] = a;
}
}
/* Pools for allocnos, allocno live ranges and objects. */
static object_allocator<live_range> live_range_pool ("live ranges");
static object_allocator<ira_allocno> allocno_pool ("allocnos");
static object_allocator<ira_object> object_pool ("objects");
/* Vec containing references to all created allocnos. It is a
container of array allocnos. */
static vec<ira_allocno_t> allocno_vec;
/* Vec containing references to all created ira_objects. It is a
container of ira_object_id_map. */
static vec<ira_object_t> ira_object_id_map_vec;
/* Initialize data concerning allocnos. */
static void
initiate_allocnos (void)
{
allocno_vec.create (max_reg_num () * 2);
ira_allocnos = NULL;
ira_allocnos_num = 0;
ira_objects_num = 0;
ira_object_id_map_vec.create (max_reg_num () * 2);
ira_object_id_map = NULL;
ira_regno_allocno_map
= (ira_allocno_t *) ira_allocate (max_reg_num ()
* sizeof (ira_allocno_t));
memset (ira_regno_allocno_map, 0, max_reg_num () * sizeof (ira_allocno_t));
}
/* Create and return an object corresponding to a new allocno A. */
static ira_object_t
ira_create_object (ira_allocno_t a, int subword)
{
enum reg_class aclass = ALLOCNO_CLASS (a);
ira_object_t obj = object_pool.allocate ();
OBJECT_ALLOCNO (obj) = a;
OBJECT_SUBWORD (obj) = subword;
OBJECT_CONFLICT_ID (obj) = ira_objects_num;
OBJECT_CONFLICT_VEC_P (obj) = false;
OBJECT_CONFLICT_ARRAY (obj) = NULL;
OBJECT_NUM_CONFLICTS (obj) = 0;
OBJECT_CONFLICT_HARD_REGS (obj) = ira_no_alloc_regs;
OBJECT_TOTAL_CONFLICT_HARD_REGS (obj) = ira_no_alloc_regs;
OBJECT_CONFLICT_HARD_REGS (obj) |= ~reg_class_contents[aclass];
OBJECT_TOTAL_CONFLICT_HARD_REGS (obj) |= ~reg_class_contents[aclass];
OBJECT_MIN (obj) = INT_MAX;
OBJECT_MAX (obj) = -1;
OBJECT_LIVE_RANGES (obj) = NULL;
ira_object_id_map_vec.safe_push (obj);
ira_object_id_map
= ira_object_id_map_vec.address ();
ira_objects_num = ira_object_id_map_vec.length ();
return obj;
}
/* Create and return the allocno corresponding to REGNO in
LOOP_TREE_NODE. Add the allocno to the list of allocnos with the
same regno if CAP_P is FALSE. */
ira_allocno_t
ira_create_allocno (int regno, bool cap_p,
ira_loop_tree_node_t loop_tree_node)
{
ira_allocno_t a;
a = allocno_pool.allocate ();
ALLOCNO_REGNO (a) = regno;
ALLOCNO_LOOP_TREE_NODE (a) = loop_tree_node;
if (! cap_p)
{
ALLOCNO_NEXT_REGNO_ALLOCNO (a) = ira_regno_allocno_map[regno];
ira_regno_allocno_map[regno] = a;
if (loop_tree_node->regno_allocno_map[regno] == NULL)
/* Remember that we can create temporary allocnos to break
cycles in register shuffle on region borders (see
ira-emit.c). */
loop_tree_node->regno_allocno_map[regno] = a;
}
ALLOCNO_CAP (a) = NULL;
ALLOCNO_CAP_MEMBER (a) = NULL;
ALLOCNO_NUM (a) = ira_allocnos_num;
bitmap_set_bit (loop_tree_node->all_allocnos, ALLOCNO_NUM (a));
ALLOCNO_NREFS (a) = 0;
ALLOCNO_FREQ (a) = 0;
ALLOCNO_HARD_REGNO (a) = -1;
ALLOCNO_CALL_FREQ (a) = 0;
ALLOCNO_CALLS_CROSSED_NUM (a) = 0;
ALLOCNO_CHEAP_CALLS_CROSSED_NUM (a) = 0;
ALLOCNO_CROSSED_CALLS_ABIS (a) = 0;
CLEAR_HARD_REG_SET (ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (a));
#ifdef STACK_REGS
ALLOCNO_NO_STACK_REG_P (a) = false;
ALLOCNO_TOTAL_NO_STACK_REG_P (a) = false;
#endif
ALLOCNO_DONT_REASSIGN_P (a) = false;
ALLOCNO_BAD_SPILL_P (a) = false;
ALLOCNO_ASSIGNED_P (a) = false;
ALLOCNO_MODE (a) = (regno < 0 ? VOIDmode : PSEUDO_REGNO_MODE (regno));
ALLOCNO_WMODE (a) = ALLOCNO_MODE (a);
ALLOCNO_PREFS (a) = NULL;
ALLOCNO_COPIES (a) = NULL;
ALLOCNO_HARD_REG_COSTS (a) = NULL;
ALLOCNO_CONFLICT_HARD_REG_COSTS (a) = NULL;
ALLOCNO_UPDATED_HARD_REG_COSTS (a) = NULL;
ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) = NULL;
ALLOCNO_CLASS (a) = NO_REGS;
ALLOCNO_UPDATED_CLASS_COST (a) = 0;
ALLOCNO_CLASS_COST (a) = 0;
ALLOCNO_MEMORY_COST (a) = 0;
ALLOCNO_UPDATED_MEMORY_COST (a) = 0;
ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a) = 0;
ALLOCNO_NUM_OBJECTS (a) = 0;
ALLOCNO_ADD_DATA (a) = NULL;
allocno_vec.safe_push (a);
ira_allocnos = allocno_vec.address ();
ira_allocnos_num = allocno_vec.length ();
return a;
}
/* Set up register class for A and update its conflict hard
registers. */
void
ira_set_allocno_class (ira_allocno_t a, enum reg_class aclass)
{
ira_allocno_object_iterator oi;
ira_object_t obj;
ALLOCNO_CLASS (a) = aclass;
FOR_EACH_ALLOCNO_OBJECT (a, obj, oi)
{
OBJECT_CONFLICT_HARD_REGS (obj) |= ~reg_class_contents[aclass];
OBJECT_TOTAL_CONFLICT_HARD_REGS (obj) |= ~reg_class_contents[aclass];
}
}
/* Determine the number of objects we should associate with allocno A
and allocate them. */
void
ira_create_allocno_objects (ira_allocno_t a)
{
machine_mode mode = ALLOCNO_MODE (a);
enum reg_class aclass = ALLOCNO_CLASS (a);
int n = ira_reg_class_max_nregs[aclass][mode];
int i;
if (n != 2 || maybe_ne (GET_MODE_SIZE (mode), n * UNITS_PER_WORD))
n = 1;
ALLOCNO_NUM_OBJECTS (a) = n;
for (i = 0; i < n; i++)
ALLOCNO_OBJECT (a, i) = ira_create_object (a, i);
}
/* For each allocno, set ALLOCNO_NUM_OBJECTS and create the
ALLOCNO_OBJECT structures. This must be called after the allocno
classes are known. */
static void
create_allocno_objects (void)
{
ira_allocno_t a;
ira_allocno_iterator ai;
FOR_EACH_ALLOCNO (a, ai)
ira_create_allocno_objects (a);
}
/* Merge hard register conflict information for all objects associated with
allocno TO into the corresponding objects associated with FROM.
If TOTAL_ONLY is true, we only merge OBJECT_TOTAL_CONFLICT_HARD_REGS. */
static void
merge_hard_reg_conflicts (ira_allocno_t from, ira_allocno_t to,
bool total_only)
{
int i;
gcc_assert (ALLOCNO_NUM_OBJECTS (to) == ALLOCNO_NUM_OBJECTS (from));
for (i = 0; i < ALLOCNO_NUM_OBJECTS (to); i++)
{
ira_object_t from_obj = ALLOCNO_OBJECT (from, i);
ira_object_t to_obj = ALLOCNO_OBJECT (to, i);
if (!total_only)
OBJECT_CONFLICT_HARD_REGS (to_obj)
|= OBJECT_CONFLICT_HARD_REGS (from_obj);
OBJECT_TOTAL_CONFLICT_HARD_REGS (to_obj)
|= OBJECT_TOTAL_CONFLICT_HARD_REGS (from_obj);
}
#ifdef STACK_REGS
if (!total_only && ALLOCNO_NO_STACK_REG_P (from))
ALLOCNO_NO_STACK_REG_P (to) = true;
if (ALLOCNO_TOTAL_NO_STACK_REG_P (from))
ALLOCNO_TOTAL_NO_STACK_REG_P (to) = true;
#endif
}
/* Update hard register conflict information for all objects associated with
A to include the regs in SET. */
void
ior_hard_reg_conflicts (ira_allocno_t a, const_hard_reg_set set)
{
ira_allocno_object_iterator i;
ira_object_t obj;
FOR_EACH_ALLOCNO_OBJECT (a, obj, i)
{
OBJECT_CONFLICT_HARD_REGS (obj) |= set;
OBJECT_TOTAL_CONFLICT_HARD_REGS (obj) |= set;
}
}
/* Return TRUE if a conflict vector with NUM elements is more
profitable than a conflict bit vector for OBJ. */
bool
ira_conflict_vector_profitable_p (ira_object_t obj, int num)
{
int nw;
int max = OBJECT_MAX (obj);
int min = OBJECT_MIN (obj);
if (max < min)
/* We prefer a bit vector in such case because it does not result
in allocation. */
return false;
nw = (max - min + IRA_INT_BITS) / IRA_INT_BITS;
return (2 * sizeof (ira_object_t) * (num + 1)
< 3 * nw * sizeof (IRA_INT_TYPE));
}
/* Allocates and initialize the conflict vector of OBJ for NUM
conflicting objects. */
void
ira_allocate_conflict_vec (ira_object_t obj, int num)
{
int size;
ira_object_t *vec;
ira_assert (OBJECT_CONFLICT_ARRAY (obj) == NULL);
num++; /* for NULL end marker */
size = sizeof (ira_object_t) * num;
OBJECT_CONFLICT_ARRAY (obj) = ira_allocate (size);
vec = (ira_object_t *) OBJECT_CONFLICT_ARRAY (obj);
vec[0] = NULL;
OBJECT_NUM_CONFLICTS (obj) = 0;
OBJECT_CONFLICT_ARRAY_SIZE (obj) = size;
OBJECT_CONFLICT_VEC_P (obj) = true;
}
/* Allocate and initialize the conflict bit vector of OBJ. */
static void
allocate_conflict_bit_vec (ira_object_t obj)
{
unsigned int size;
ira_assert (OBJECT_CONFLICT_ARRAY (obj) == NULL);
size = ((OBJECT_MAX (obj) - OBJECT_MIN (obj) + IRA_INT_BITS)
/ IRA_INT_BITS * sizeof (IRA_INT_TYPE));
OBJECT_CONFLICT_ARRAY (obj) = ira_allocate (size);
memset (OBJECT_CONFLICT_ARRAY (obj), 0, size);
OBJECT_CONFLICT_ARRAY_SIZE (obj) = size;
OBJECT_CONFLICT_VEC_P (obj) = false;
}
/* Allocate and initialize the conflict vector or conflict bit vector
of OBJ for NUM conflicting allocnos whatever is more profitable. */
void
ira_allocate_object_conflicts (ira_object_t obj, int num)
{
if (ira_conflict_vector_profitable_p (obj, num))
ira_allocate_conflict_vec (obj, num);
else
allocate_conflict_bit_vec (obj);
}
/* Add OBJ2 to the conflicts of OBJ1. */
static void
add_to_conflicts (ira_object_t obj1, ira_object_t obj2)
{
int num;
unsigned int size;
if (OBJECT_CONFLICT_VEC_P (obj1))
{
ira_object_t *vec = OBJECT_CONFLICT_VEC (obj1);
int curr_num = OBJECT_NUM_CONFLICTS (obj1);
num = curr_num + 2;
if (OBJECT_CONFLICT_ARRAY_SIZE (obj1) < num * sizeof (ira_object_t))
{
ira_object_t *newvec;
size = (3 * num / 2 + 1) * sizeof (ira_allocno_t);
newvec = (ira_object_t *) ira_allocate (size);
memcpy (newvec, vec, curr_num * sizeof (ira_object_t));
ira_free (vec);
vec = newvec;
OBJECT_CONFLICT_ARRAY (obj1) = vec;
OBJECT_CONFLICT_ARRAY_SIZE (obj1) = size;
}
vec[num - 2] = obj2;
vec[num - 1] = NULL;
OBJECT_NUM_CONFLICTS (obj1)++;
}
else
{
int nw, added_head_nw, id;
IRA_INT_TYPE *vec = OBJECT_CONFLICT_BITVEC (obj1);
id = OBJECT_CONFLICT_ID (obj2);
if (OBJECT_MIN (obj1) > id)
{
/* Expand head of the bit vector. */
added_head_nw = (OBJECT_MIN (obj1) - id - 1) / IRA_INT_BITS + 1;
nw = (OBJECT_MAX (obj1) - OBJECT_MIN (obj1)) / IRA_INT_BITS + 1;
size = (nw + added_head_nw) * sizeof (IRA_INT_TYPE);
if (OBJECT_CONFLICT_ARRAY_SIZE (obj1) >= size)
{
memmove ((char *) vec + added_head_nw * sizeof (IRA_INT_TYPE),
vec, nw * sizeof (IRA_INT_TYPE));
memset (vec, 0, added_head_nw * sizeof (IRA_INT_TYPE));
}
else
{
size
= (3 * (nw + added_head_nw) / 2 + 1) * sizeof (IRA_INT_TYPE);
vec = (IRA_INT_TYPE *) ira_allocate (size);
memcpy ((char *) vec + added_head_nw * sizeof (IRA_INT_TYPE),
OBJECT_CONFLICT_ARRAY (obj1), nw * sizeof (IRA_INT_TYPE));
memset (vec, 0, added_head_nw * sizeof (IRA_INT_TYPE));
memset ((char *) vec
+ (nw + added_head_nw) * sizeof (IRA_INT_TYPE),
0, size - (nw + added_head_nw) * sizeof (IRA_INT_TYPE));
ira_free (OBJECT_CONFLICT_ARRAY (obj1));
OBJECT_CONFLICT_ARRAY (obj1) = vec;
OBJECT_CONFLICT_ARRAY_SIZE (obj1) = size;
}
OBJECT_MIN (obj1) -= added_head_nw * IRA_INT_BITS;
}
else if (OBJECT_MAX (obj1) < id)
{
nw = (id - OBJECT_MIN (obj1)) / IRA_INT_BITS + 1;
size = nw * sizeof (IRA_INT_TYPE);
if (OBJECT_CONFLICT_ARRAY_SIZE (obj1) < size)
{
/* Expand tail of the bit vector. */
size = (3 * nw / 2 + 1) * sizeof (IRA_INT_TYPE);
vec = (IRA_INT_TYPE *) ira_allocate (size);
memcpy (vec, OBJECT_CONFLICT_ARRAY (obj1), OBJECT_CONFLICT_ARRAY_SIZE (obj1));
memset ((char *) vec + OBJECT_CONFLICT_ARRAY_SIZE (obj1),
0, size - OBJECT_CONFLICT_ARRAY_SIZE (obj1));
ira_free (OBJECT_CONFLICT_ARRAY (obj1));
OBJECT_CONFLICT_ARRAY (obj1) = vec;
OBJECT_CONFLICT_ARRAY_SIZE (obj1) = size;
}
OBJECT_MAX (obj1) = id;
}
SET_MINMAX_SET_BIT (vec, id, OBJECT_MIN (obj1), OBJECT_MAX (obj1));
}
}
/* Add OBJ1 to the conflicts of OBJ2 and vice versa. */
static void
ira_add_conflict (ira_object_t obj1, ira_object_t obj2)
{
add_to_conflicts (obj1, obj2);
add_to_conflicts (obj2, obj1);
}
/* Clear all conflicts of OBJ. */
static void
clear_conflicts (ira_object_t obj)
{
if (OBJECT_CONFLICT_VEC_P (obj))
{
OBJECT_NUM_CONFLICTS (obj) = 0;
OBJECT_CONFLICT_VEC (obj)[0] = NULL;
}
else if (OBJECT_CONFLICT_ARRAY_SIZE (obj) != 0)
{
int nw;
nw = (OBJECT_MAX (obj) - OBJECT_MIN (obj)) / IRA_INT_BITS + 1;
memset (OBJECT_CONFLICT_BITVEC (obj), 0, nw * sizeof (IRA_INT_TYPE));
}
}
/* The array used to find duplications in conflict vectors of
allocnos. */
static int *conflict_check;
/* The value used to mark allocation presence in conflict vector of
the current allocno. */
static int curr_conflict_check_tick;
/* Remove duplications in conflict vector of OBJ. */
static void
compress_conflict_vec (ira_object_t obj)
{
ira_object_t *vec, conflict_obj;
int i, j;
ira_assert (OBJECT_CONFLICT_VEC_P (obj));
vec = OBJECT_CONFLICT_VEC (obj);
curr_conflict_check_tick++;
for (i = j = 0; (conflict_obj = vec[i]) != NULL; i++)
{
int id = OBJECT_CONFLICT_ID (conflict_obj);
if (conflict_check[id] != curr_conflict_check_tick)
{
conflict_check[id] = curr_conflict_check_tick;
vec[j++] = conflict_obj;
}
}
OBJECT_NUM_CONFLICTS (obj) = j;
vec[j] = NULL;
}
/* Remove duplications in conflict vectors of all allocnos. */
static void
compress_conflict_vecs (void)
{
ira_object_t obj;
ira_object_iterator oi;
conflict_check = (int *) ira_allocate (sizeof (int) * ira_objects_num);
memset (conflict_check, 0, sizeof (int) * ira_objects_num);
curr_conflict_check_tick = 0;
FOR_EACH_OBJECT (obj, oi)
{
if (OBJECT_CONFLICT_VEC_P (obj))
compress_conflict_vec (obj);
}
ira_free (conflict_check);
}
/* This recursive function outputs allocno A and if it is a cap the
function outputs its members. */
void
ira_print_expanded_allocno (ira_allocno_t a)
{
basic_block bb;
fprintf (ira_dump_file, " a%d(r%d", ALLOCNO_NUM (a), ALLOCNO_REGNO (a));
if ((bb = ALLOCNO_LOOP_TREE_NODE (a)->bb) != NULL)
fprintf (ira_dump_file, ",b%d", bb->index);
else
fprintf (ira_dump_file, ",l%d", ALLOCNO_LOOP_TREE_NODE (a)->loop_num);
if (ALLOCNO_CAP_MEMBER (a) != NULL)
{
fprintf (ira_dump_file, ":");
ira_print_expanded_allocno (ALLOCNO_CAP_MEMBER (a));
}
fprintf (ira_dump_file, ")");
}
/* Create and return the cap representing allocno A in the
parent loop. */
static ira_allocno_t
create_cap_allocno (ira_allocno_t a)
{
ira_allocno_t cap;
ira_loop_tree_node_t parent;
enum reg_class aclass;
parent = ALLOCNO_LOOP_TREE_NODE (a)->parent;
cap = ira_create_allocno (ALLOCNO_REGNO (a), true, parent);
ALLOCNO_MODE (cap) = ALLOCNO_MODE (a);
ALLOCNO_WMODE (cap) = ALLOCNO_WMODE (a);
aclass = ALLOCNO_CLASS (a);
ira_set_allocno_class (cap, aclass);
ira_create_allocno_objects (cap);
ALLOCNO_CAP_MEMBER (cap) = a;
ALLOCNO_CAP (a) = cap;
ALLOCNO_CLASS_COST (cap) = ALLOCNO_CLASS_COST (a);
ALLOCNO_MEMORY_COST (cap) = ALLOCNO_MEMORY_COST (a);
ira_allocate_and_copy_costs
(&ALLOCNO_HARD_REG_COSTS (cap), aclass, ALLOCNO_HARD_REG_COSTS (a));
ira_allocate_and_copy_costs
(&ALLOCNO_CONFLICT_HARD_REG_COSTS (cap), aclass,
ALLOCNO_CONFLICT_HARD_REG_COSTS (a));
ALLOCNO_BAD_SPILL_P (cap) = ALLOCNO_BAD_SPILL_P (a);
ALLOCNO_NREFS (cap) = ALLOCNO_NREFS (a);
ALLOCNO_FREQ (cap) = ALLOCNO_FREQ (a);
ALLOCNO_CALL_FREQ (cap) = ALLOCNO_CALL_FREQ (a);
merge_hard_reg_conflicts (a, cap, false);
ALLOCNO_CALLS_CROSSED_NUM (cap) = ALLOCNO_CALLS_CROSSED_NUM (a);
ALLOCNO_CHEAP_CALLS_CROSSED_NUM (cap) = ALLOCNO_CHEAP_CALLS_CROSSED_NUM (a);
ALLOCNO_CROSSED_CALLS_ABIS (cap) = ALLOCNO_CROSSED_CALLS_ABIS (a);
ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (cap)
= ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (a);
if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
{
fprintf (ira_dump_file, " Creating cap ");
ira_print_expanded_allocno (cap);
fprintf (ira_dump_file, "\n");
}
return cap;
}
/* Create and return a live range for OBJECT with given attributes. */
live_range_t
ira_create_live_range (ira_object_t obj, int start, int finish,
live_range_t next)
{
live_range_t p;
p = live_range_pool.allocate ();
p->object = obj;
p->start = start;
p->finish = finish;
p->next = next;
return p;
}
/* Create a new live range for OBJECT and queue it at the head of its
live range list. */
void
ira_add_live_range_to_object (ira_object_t object, int start, int finish)
{
live_range_t p;
p = ira_create_live_range (object, start, finish,
OBJECT_LIVE_RANGES (object));
OBJECT_LIVE_RANGES (object) = p;
}
/* Copy allocno live range R and return the result. */
static live_range_t
copy_live_range (live_range_t r)
{
live_range_t p;
p = live_range_pool.allocate ();
*p = *r;
return p;
}
/* Copy allocno live range list given by its head R and return the
result. */
live_range_t
ira_copy_live_range_list (live_range_t r)
{
live_range_t p, first, last;
if (r == NULL)
return NULL;
for (first = last = NULL; r != NULL; r = r->next)
{
p = copy_live_range (r);
if (first == NULL)
first = p;
else
last->next = p;
last = p;
}
return first;
}
/* Merge ranges R1 and R2 and returns the result. The function
maintains the order of ranges and tries to minimize number of the
result ranges. */
live_range_t
ira_merge_live_ranges (live_range_t r1, live_range_t r2)
{
live_range_t first, last;
if (r1 == NULL)
return r2;
if (r2 == NULL)
return r1;
for (first = last = NULL; r1 != NULL && r2 != NULL;)
{
if (r1->start < r2->start)
std::swap (r1, r2);
if (r1->start <= r2->finish + 1)
{
/* Intersected ranges: merge r1 and r2 into r1. */
r1->start = r2->start;
if (r1->finish < r2->finish)
r1->finish = r2->finish;
live_range_t temp = r2;
r2 = r2->next;
ira_finish_live_range (temp);
if (r2 == NULL)
{
/* To try to merge with subsequent ranges in r1. */
r2 = r1->next;
r1->next = NULL;
}
}
else
{
/* Add r1 to the result. */
if (first == NULL)
first = last = r1;
else
{
last->next = r1;
last = r1;
}
r1 = r1->next;
if (r1 == NULL)
{
/* To try to merge with subsequent ranges in r2. */
r1 = r2->next;
r2->next = NULL;
}
}
}
if (r1 != NULL)
{
if (first == NULL)
first = r1;
else
last->next = r1;
ira_assert (r1->next == NULL);
}
else if (r2 != NULL)
{
if (first == NULL)
first = r2;
else
last->next = r2;
ira_assert (r2->next == NULL);
}
else
{
ira_assert (last->next == NULL);
}
return first;
}
/* Return TRUE if live ranges R1 and R2 intersect. */
bool
ira_live_ranges_intersect_p (live_range_t r1, live_range_t r2)
{
/* Remember the live ranges are always kept ordered. */
while (r1 != NULL && r2 != NULL)
{
if (r1->start > r2->finish)
r1 = r1->next;
else if (r2->start > r1->finish)
r2 = r2->next;
else
return true;
}
return false;
}
/* Free allocno live range R. */
void
ira_finish_live_range (live_range_t r)
{
live_range_pool.remove (r);
}
/* Free list of allocno live ranges starting with R. */
void
ira_finish_live_range_list (live_range_t r)
{
live_range_t next_r;
for (; r != NULL; r = next_r)
{
next_r = r->next;
ira_finish_live_range (r);
}
}
/* Free updated register costs of allocno A. */
void
ira_free_allocno_updated_costs (ira_allocno_t a)
{
enum reg_class aclass;
aclass = ALLOCNO_CLASS (a);
if (ALLOCNO_UPDATED_HARD_REG_COSTS (a) != NULL)
ira_free_cost_vector (ALLOCNO_UPDATED_HARD_REG_COSTS (a), aclass);
ALLOCNO_UPDATED_HARD_REG_COSTS (a) = NULL;
if (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) != NULL)
ira_free_cost_vector (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a),
aclass);
ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) = NULL;
}
/* Free and nullify all cost vectors allocated earlier for allocno
A. */
static void
ira_free_allocno_costs (ira_allocno_t a)
{
enum reg_class aclass = ALLOCNO_CLASS (a);
ira_object_t obj;
ira_allocno_object_iterator oi;
FOR_EACH_ALLOCNO_OBJECT (a, obj, oi)
{
ira_finish_live_range_list (OBJECT_LIVE_RANGES (obj));
ira_object_id_map[OBJECT_CONFLICT_ID (obj)] = NULL;
if (OBJECT_CONFLICT_ARRAY (obj) != NULL)
ira_free (OBJECT_CONFLICT_ARRAY (obj));
object_pool.remove (obj);
}
ira_allocnos[ALLOCNO_NUM (a)] = NULL;
if (ALLOCNO_HARD_REG_COSTS (a) != NULL)
ira_free_cost_vector (ALLOCNO_HARD_REG_COSTS (a), aclass);
if (ALLOCNO_CONFLICT_HARD_REG_COSTS (a) != NULL)
ira_free_cost_vector (ALLOCNO_CONFLICT_HARD_REG_COSTS (a), aclass);
if (ALLOCNO_UPDATED_HARD_REG_COSTS (a) != NULL)
ira_free_cost_vector (ALLOCNO_UPDATED_HARD_REG_COSTS (a), aclass);
if (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) != NULL)
ira_free_cost_vector (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a),
aclass);
ALLOCNO_HARD_REG_COSTS (a) = NULL;
ALLOCNO_CONFLICT_HARD_REG_COSTS (a) = NULL;
ALLOCNO_UPDATED_HARD_REG_COSTS (a) = NULL;
ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) = NULL;
}
/* Free the memory allocated for allocno A. */
static void
finish_allocno (ira_allocno_t a)
{
ira_free_allocno_costs (a);
allocno_pool.remove (a);
}
/* Free the memory allocated for all allocnos. */
static void
finish_allocnos (void)
{
ira_allocno_t a;
ira_allocno_iterator ai;
FOR_EACH_ALLOCNO (a, ai)
finish_allocno (a);
ira_free (ira_regno_allocno_map);
ira_object_id_map_vec.release ();
allocno_vec.release ();
allocno_pool.release ();
object_pool.release ();
live_range_pool.release ();
}
/* Pools for allocno preferences. */
static object_allocator <ira_allocno_pref> pref_pool ("prefs");
/* Vec containing references to all created preferences. It is a
container of array ira_prefs. */
static vec<ira_pref_t> pref_vec;
/* The function initializes data concerning allocno prefs. */
static void
initiate_prefs (void)
{
pref_vec.create (get_max_uid ());
ira_prefs = NULL;
ira_prefs_num = 0;
}
/* Return pref for A and HARD_REGNO if any. */
static ira_pref_t
find_allocno_pref (ira_allocno_t a, int hard_regno)
{
ira_pref_t pref;
for (pref = ALLOCNO_PREFS (a); pref != NULL; pref = pref->next_pref)
if (pref->allocno == a && pref->hard_regno == hard_regno)
return pref;
return NULL;
}
/* Create and return pref with given attributes A, HARD_REGNO, and FREQ. */
ira_pref_t
ira_create_pref (ira_allocno_t a, int hard_regno, int freq)
{
ira_pref_t pref;
pref = pref_pool.allocate ();
pref->num = ira_prefs_num;
pref->allocno = a;
pref->hard_regno = hard_regno;
pref->freq = freq;
pref_vec.safe_push (pref);
ira_prefs = pref_vec.address ();
ira_prefs_num = pref_vec.length ();
return pref;
}
/* Attach a pref PREF to the corresponding allocno. */
static void
add_allocno_pref_to_list (ira_pref_t pref)
{
ira_allocno_t a = pref->allocno;
pref->next_pref = ALLOCNO_PREFS (a);
ALLOCNO_PREFS (a) = pref;
}
/* Create (or update frequency if the pref already exists) the pref of
allocnos A preferring HARD_REGNO with frequency FREQ. */
void
ira_add_allocno_pref (ira_allocno_t a, int hard_regno, int freq)
{
ira_pref_t pref;
if (freq <= 0)
return;
if ((pref = find_allocno_pref (a, hard_regno)) != NULL)
{
pref->freq += freq;
return;
}
pref = ira_create_pref (a, hard_regno, freq);
ira_assert (a != NULL);
add_allocno_pref_to_list (pref);
}
/* Print info about PREF into file F. */
static void
print_pref (FILE *f, ira_pref_t pref)
{
fprintf (f, " pref%d:a%d(r%d)<-hr%d@%d\n", pref->num,
ALLOCNO_NUM (pref->allocno), ALLOCNO_REGNO (pref->allocno),
pref->hard_regno, pref->freq);
}
/* Print info about PREF into stderr. */
void
ira_debug_pref (ira_pref_t pref)
{
print_pref (stderr, pref);
}
/* Print info about all prefs into file F. */
static void
print_prefs (FILE *f)
{
ira_pref_t pref;
ira_pref_iterator pi;
FOR_EACH_PREF (pref, pi)
print_pref (f, pref);
}
/* Print info about all prefs into stderr. */
void
ira_debug_prefs (void)
{
print_prefs (stderr);
}
/* Print info about prefs involving allocno A into file F. */
static void
print_allocno_prefs (FILE *f, ira_allocno_t a)
{
ira_pref_t pref;
fprintf (f, " a%d(r%d):", ALLOCNO_NUM (a), ALLOCNO_REGNO (a));
for (pref = ALLOCNO_PREFS (a); pref != NULL; pref = pref->next_pref)
fprintf (f, " pref%d:hr%d@%d", pref->num, pref->hard_regno, pref->freq);
fprintf (f, "\n");
}
/* Print info about prefs involving allocno A into stderr. */
void
ira_debug_allocno_prefs (ira_allocno_t a)
{
print_allocno_prefs (stderr, a);
}
/* The function frees memory allocated for PREF. */
static void
finish_pref (ira_pref_t pref)
{
ira_prefs[pref->num] = NULL;
pref_pool.remove (pref);
}
/* Remove PREF from the list of allocno prefs and free memory for
it. */
void
ira_remove_pref (ira_pref_t pref)
{
ira_pref_t cpref, prev;
if (internal_flag_ira_verbose > 1 && ira_dump_file != NULL)
fprintf (ira_dump_file, " Removing pref%d:hr%d@%d\n",
pref->num, pref->hard_regno, pref->freq);
for (prev = NULL, cpref = ALLOCNO_PREFS (pref->allocno);
cpref != NULL;
prev = cpref, cpref = cpref->next_pref)
if (cpref == pref)
break;
ira_assert (cpref != NULL);
if (prev == NULL)
ALLOCNO_PREFS (pref->allocno) = pref->next_pref;
else
prev->next_pref = pref->next_pref;
finish_pref (pref);
}
/* Remove all prefs of allocno A. */
void
ira_remove_allocno_prefs (ira_allocno_t a)
{
ira_pref_t pref, next_pref;
for (pref = ALLOCNO_PREFS (a); pref != NULL; pref = next_pref)
{
next_pref = pref->next_pref;
finish_pref (pref);
}
ALLOCNO_PREFS (a) = NULL;
}
/* Free memory allocated for all prefs. */
static void
finish_prefs (void)
{
ira_pref_t pref;
ira_pref_iterator pi;
FOR_EACH_PREF (pref, pi)
finish_pref (pref);
pref_vec.release ();
pref_pool.release ();
}
/* Pools for copies. */
static object_allocator<ira_allocno_copy> copy_pool ("copies");
/* Vec containing references to all created copies. It is a
container of array ira_copies. */
static vec<ira_copy_t> copy_vec;
/* The function initializes data concerning allocno copies. */
static void
initiate_copies (void)
{
copy_vec.create (get_max_uid ());
ira_copies = NULL;
ira_copies_num = 0;
}
/* Return copy connecting A1 and A2 and originated from INSN of
LOOP_TREE_NODE if any. */
static ira_copy_t
find_allocno_copy (ira_allocno_t a1, ira_allocno_t a2, rtx_insn *insn,
ira_loop_tree_node_t loop_tree_node)
{
ira_copy_t cp, next_cp;
ira_allocno_t another_a;
for (cp = ALLOCNO_COPIES (a1); cp != NULL; cp = next_cp)
{
if (cp->first == a1)
{
next_cp = cp->next_first_allocno_copy;
another_a = cp->second;
}
else if (cp->second == a1)
{
next_cp = cp->next_second_allocno_copy;
another_a = cp->first;
}
else
gcc_unreachable ();
if (another_a == a2 && cp->insn == insn
&& cp->loop_tree_node == loop_tree_node)
return cp;
}
return NULL;
}
/* Create and return copy with given attributes LOOP_TREE_NODE, FIRST,
SECOND, FREQ, CONSTRAINT_P, and INSN. */
ira_copy_t
ira_create_copy (ira_allocno_t first, ira_allocno_t second, int freq,
bool constraint_p, rtx_insn *insn,
ira_loop_tree_node_t loop_tree_node)
{
ira_copy_t cp;
cp = copy_pool.allocate ();
cp->num = ira_copies_num;
cp->first = first;
cp->second = second;
cp->freq = freq;
cp->constraint_p = constraint_p;
cp->insn = insn;
cp->loop_tree_node = loop_tree_node;
copy_vec.safe_push (cp);
ira_copies = copy_vec.address ();
ira_copies_num = copy_vec.length ();
return cp;
}
/* Attach a copy CP to allocnos involved into the copy. */
static void
add_allocno_copy_to_list (ira_copy_t cp)
{
ira_allocno_t first = cp->first, second = cp->second;
cp->prev_first_allocno_copy = NULL;
cp->prev_second_allocno_copy = NULL;
cp->next_first_allocno_copy = ALLOCNO_COPIES (first);
if (cp->next_first_allocno_copy != NULL)
{
if (cp->next_first_allocno_copy->first == first)
cp->next_first_allocno_copy->prev_first_allocno_copy = cp;
else
cp->next_first_allocno_copy->prev_second_allocno_copy = cp;
}
cp->next_second_allocno_copy = ALLOCNO_COPIES (second);
if (cp->next_second_allocno_copy != NULL)
{
if (cp->next_second_allocno_copy->second == second)
cp->next_second_allocno_copy->prev_second_allocno_copy = cp;
else
cp->next_second_allocno_copy->prev_first_allocno_copy = cp;
}
ALLOCNO_COPIES (first) = cp;
ALLOCNO_COPIES (second) = cp;
}
/* Make a copy CP a canonical copy where number of the
first allocno is less than the second one. */
static void
swap_allocno_copy_ends_if_necessary (ira_copy_t cp)
{
if (ALLOCNO_NUM (cp->first) <= ALLOCNO_NUM (cp->second))
return;
std::swap (cp->first, cp->second);
std::swap (cp->prev_first_allocno_copy, cp->prev_second_allocno_copy);
std::swap (cp->next_first_allocno_copy, cp->next_second_allocno_copy);
}
/* Create (or update frequency if the copy already exists) and return
the copy of allocnos FIRST and SECOND with frequency FREQ
corresponding to move insn INSN (if any) and originated from
LOOP_TREE_NODE. */
ira_copy_t
ira_add_allocno_copy (ira_allocno_t first, ira_allocno_t second, int freq,
bool constraint_p, rtx_insn *insn,
ira_loop_tree_node_t loop_tree_node)
{
ira_copy_t cp;
if ((cp = find_allocno_copy (first, second, insn, loop_tree_node)) != NULL)
{
cp->freq += freq;
return cp;
}
cp = ira_create_copy (first, second, freq, constraint_p, insn,
loop_tree_node);
ira_assert (first != NULL && second != NULL);
add_allocno_copy_to_list (cp);
swap_allocno_copy_ends_if_necessary (cp);
return cp;
}
/* Print info about copy CP into file F. */
static void
print_copy (FILE *f, ira_copy_t cp)
{
fprintf (f, " cp%d:a%d(r%d)<->a%d(r%d)@%d:%s\n", cp->num,
ALLOCNO_NUM (cp->first), ALLOCNO_REGNO (cp->first),
ALLOCNO_NUM (cp->second), ALLOCNO_REGNO (cp->second), cp->freq,
cp->insn != NULL
? "move" : cp->constraint_p ? "constraint" : "shuffle");
}
DEBUG_FUNCTION void
debug (ira_allocno_copy &ref)
{
print_copy (stderr, &ref);
}
DEBUG_FUNCTION void
debug (ira_allocno_copy *ptr)
{
if (ptr)
debug (*ptr);
else
fprintf (stderr, "<nil>\n");
}
/* Print info about copy CP into stderr. */
void
ira_debug_copy (ira_copy_t cp)
{
print_copy (stderr, cp);
}
/* Print info about all copies into file F. */
static void
print_copies (FILE *f)
{
ira_copy_t cp;
ira_copy_iterator ci;
FOR_EACH_COPY (cp, ci)
print_copy (f, cp);
}
/* Print info about all copies into stderr. */
void
ira_debug_copies (void)
{
print_copies (stderr);
}
/* Print info about copies involving allocno A into file F. */
static void
print_allocno_copies (FILE *f, ira_allocno_t a)
{
ira_allocno_t another_a;
ira_copy_t cp, next_cp;
fprintf (f, " a%d(r%d):", ALLOCNO_NUM (a), ALLOCNO_REGNO (a));
for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp)
{
if (cp->first == a)
{
next_cp = cp->next_first_allocno_copy;
another_a = cp->second;
}
else if (cp->second == a)
{
next_cp = cp->next_second_allocno_copy;
another_a = cp->first;
}
else
gcc_unreachable ();
fprintf (f, " cp%d:a%d(r%d)@%d", cp->num,
ALLOCNO_NUM (another_a), ALLOCNO_REGNO (another_a), cp->freq);
}
fprintf (f, "\n");
}
DEBUG_FUNCTION void
debug (ira_allocno &ref)
{
print_allocno_copies (stderr, &ref);
}
DEBUG_FUNCTION void
debug (ira_allocno *ptr)
{
if (ptr)
debug (*ptr);
else
fprintf (stderr, "<nil>\n");
}
/* Print info about copies involving allocno A into stderr. */
void
ira_debug_allocno_copies (ira_allocno_t a)
{
print_allocno_copies (stderr, a);
}
/* The function frees memory allocated for copy CP. */
static void
finish_copy (ira_copy_t cp)
{
copy_pool.remove (cp);
}
/* Free memory allocated for all copies. */
static void
finish_copies (void)
{
ira_copy_t cp;
ira_copy_iterator ci;
FOR_EACH_COPY (cp, ci)
finish_copy (cp);
copy_vec.release ();
copy_pool.release ();
}
/* Pools for cost vectors. It is defined only for allocno classes. */
static pool_allocator *cost_vector_pool[N_REG_CLASSES];
/* The function initiates work with hard register cost vectors. It
creates allocation pool for each allocno class. */
static void
initiate_cost_vectors (void)
{
int i;
enum reg_class aclass;
for (i = 0; i < ira_allocno_classes_num; i++)
{
aclass = ira_allocno_classes[i];
cost_vector_pool[aclass] = new pool_allocator
("cost vectors", sizeof (int) * (ira_class_hard_regs_num[aclass]));
}
}
/* Allocate and return a cost vector VEC for ACLASS. */
int *
ira_allocate_cost_vector (reg_class_t aclass)
{
return (int*) cost_vector_pool[(int) aclass]->allocate ();
}
/* Free a cost vector VEC for ACLASS. */
void
ira_free_cost_vector (int *vec, reg_class_t aclass)
{
ira_assert (vec != NULL);
cost_vector_pool[(int) aclass]->remove (vec);
}
/* Finish work with hard register cost vectors. Release allocation
pool for each allocno class. */
static void
finish_cost_vectors (void)
{
int i;
enum reg_class aclass;
for (i = 0; i < ira_allocno_classes_num; i++)
{
aclass = ira_allocno_classes[i];
delete cost_vector_pool[aclass];
}
}
/* Compute a post-ordering of the reverse control flow of the loop body
designated by the children nodes of LOOP_NODE, whose body nodes in
pre-order are input as LOOP_PREORDER. Return a VEC with a post-order
of the reverse loop body.
For the post-order of the reverse CFG, we visit the basic blocks in
LOOP_PREORDER array in the reverse order of where they appear.
This is important: We do not just want to compute a post-order of
the reverse CFG, we want to make a best-guess for a visiting order that
minimizes the number of chain elements per allocno live range. If the
blocks would be visited in a different order, we would still compute a
correct post-ordering but it would be less likely that two nodes
connected by an edge in the CFG are neighbors in the topsort. */
static vec<ira_loop_tree_node_t>
ira_loop_tree_body_rev_postorder (ira_loop_tree_node_t loop_node ATTRIBUTE_UNUSED,
vec<ira_loop_tree_node_t> loop_preorder)
{
vec<ira_loop_tree_node_t> topsort_nodes = vNULL;
unsigned int n_loop_preorder;
n_loop_preorder = loop_preorder.length ();
if (n_loop_preorder != 0)
{
ira_loop_tree_node_t subloop_node;
unsigned int i;
auto_vec<ira_loop_tree_node_t> dfs_stack;
/* This is a bit of strange abuse of the BB_VISITED flag: We use
the flag to mark blocks we still have to visit to add them to
our post-order. Define an alias to avoid confusion. */
#define BB_TO_VISIT BB_VISITED
FOR_EACH_VEC_ELT (loop_preorder, i, subloop_node)
{
gcc_checking_assert (! (subloop_node->bb->flags & BB_TO_VISIT));
subloop_node->bb->flags |= BB_TO_VISIT;
}
topsort_nodes.create (n_loop_preorder);
dfs_stack.create (n_loop_preorder);
FOR_EACH_VEC_ELT_REVERSE (loop_preorder, i, subloop_node)
{
if (! (subloop_node->bb->flags & BB_TO_VISIT))
continue;
subloop_node->bb->flags &= ~BB_TO_VISIT;
dfs_stack.quick_push (subloop_node);
while (! dfs_stack.is_empty ())
{
edge e;
edge_iterator ei;
ira_loop_tree_node_t n = dfs_stack.last ();
FOR_EACH_EDGE (e, ei, n->bb->preds)
{
ira_loop_tree_node_t pred_node;
basic_block pred_bb = e->src;
if (e->src == ENTRY_BLOCK_PTR_FOR_FN (cfun))
continue;
pred_node = IRA_BB_NODE_BY_INDEX (pred_bb->index);
if (pred_node != n
&& (pred_node->bb->flags & BB_TO_VISIT))
{
pred_node->bb->flags &= ~BB_TO_VISIT;
dfs_stack.quick_push (pred_node);
}
}
if (n == dfs_stack.last ())
{
dfs_stack.pop ();
topsort_nodes.quick_push (n);
}
}
}
#undef BB_TO_VISIT
}
gcc_assert (topsort_nodes.length () == n_loop_preorder);
return topsort_nodes;
}
/* The current loop tree node and its regno allocno map. */
ira_loop_tree_node_t ira_curr_loop_tree_node;
ira_allocno_t *ira_curr_regno_allocno_map;
/* This recursive function traverses loop tree with root LOOP_NODE
calling non-null functions PREORDER_FUNC and POSTORDER_FUNC
correspondingly in preorder and postorder. The function sets up
IRA_CURR_LOOP_TREE_NODE and IRA_CURR_REGNO_ALLOCNO_MAP. If BB_P,
basic block nodes of LOOP_NODE is also processed (before its
subloop nodes).
If BB_P is set and POSTORDER_FUNC is given, the basic blocks in
the loop are passed in the *reverse* post-order of the *reverse*
CFG. This is only used by ira_create_allocno_live_ranges, which
wants to visit basic blocks in this order to minimize the number
of elements per live range chain.
Note that the loop tree nodes are still visited in the normal,
forward post-order of the loop tree. */
void
ira_traverse_loop_tree (bool bb_p, ira_loop_tree_node_t loop_node,
void (*preorder_func) (ira_loop_tree_node_t),
void (*postorder_func) (ira_loop_tree_node_t))
{
ira_loop_tree_node_t subloop_node;
ira_assert (loop_node->bb == NULL);
ira_curr_loop_tree_node = loop_node;
ira_curr_regno_allocno_map = ira_curr_loop_tree_node->regno_allocno_map;
if (preorder_func != NULL)
(*preorder_func) (loop_node);
if (bb_p)
{
auto_vec<ira_loop_tree_node_t> loop_preorder;
unsigned int i;
/* Add all nodes to the set of nodes to visit. The IRA loop tree
is set up such that nodes in the loop body appear in a pre-order
of their place in the CFG. */
for (subloop_node = loop_node->children;
subloop_node != NULL;
subloop_node = subloop_node->next)
if (subloop_node->bb != NULL)
loop_preorder.safe_push (subloop_node);
if (preorder_func != NULL)
FOR_EACH_VEC_ELT (loop_preorder, i, subloop_node)
(*preorder_func) (subloop_node);
if (postorder_func != NULL)
{
vec<ira_loop_tree_node_t> loop_rev_postorder =
ira_loop_tree_body_rev_postorder (loop_node, loop_preorder);
FOR_EACH_VEC_ELT_REVERSE (loop_rev_postorder, i, subloop_node)
(*postorder_func) (subloop_node);
loop_rev_postorder.release ();
}
}
for (subloop_node = loop_node->subloops;
subloop_node != NULL;
subloop_node = subloop_node->subloop_next)
{
ira_assert (subloop_node->bb == NULL);
ira_traverse_loop_tree (bb_p, subloop_node,
preorder_func, postorder_func);
}
ira_curr_loop_tree_node = loop_node;
ira_curr_regno_allocno_map = ira_curr_loop_tree_node->regno_allocno_map;
if (postorder_func != NULL)
(*postorder_func) (loop_node);
}
/* The basic block currently being processed. */
static basic_block curr_bb;
/* This recursive function creates allocnos corresponding to
pseudo-registers containing in X. True OUTPUT_P means that X is
an lvalue. PARENT corresponds to the parent expression of X. */
static void
create_insn_allocnos (rtx x, rtx outer, bool output_p)
{
int i, j;
const char *fmt;
enum rtx_code code = GET_CODE (x);
if (code == REG)
{
int regno;
if ((regno = REGNO (x)) >= FIRST_PSEUDO_REGISTER)
{
ira_allocno_t a;
if ((a = ira_curr_regno_allocno_map[regno]) == NULL)
{
a = ira_create_allocno (regno, false, ira_curr_loop_tree_node);
if (outer != NULL && GET_CODE (outer) == SUBREG)
{
machine_mode wmode = GET_MODE (outer);
if (partial_subreg_p (ALLOCNO_WMODE (a), wmode))
ALLOCNO_WMODE (a) = wmode;
}
}
ALLOCNO_NREFS (a)++;
ALLOCNO_FREQ (a) += REG_FREQ_FROM_BB (curr_bb);
if (output_p)
bitmap_set_bit (ira_curr_loop_tree_node->modified_regnos, regno);
}
return;
}
else if (code == SET)
{
create_insn_allocnos (SET_DEST (x), NULL, true);
create_insn_allocnos (SET_SRC (x), NULL, false);
return;
}
else if (code == CLOBBER)
{
create_insn_allocnos (XEXP (x, 0), NULL, true);
return;
}
else if (code == MEM)
{
create_insn_allocnos (XEXP (x, 0), NULL, false);
return;
}
else if (code == PRE_DEC || code == POST_DEC || code == PRE_INC ||
code == POST_INC || code == POST_MODIFY || code == PRE_MODIFY)
{
create_insn_allocnos (XEXP (x, 0), NULL, true);
create_insn_allocnos (XEXP (x, 0), NULL, false);
return;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
create_insn_allocnos (XEXP (x, i), x, output_p);
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
create_insn_allocnos (XVECEXP (x, i, j), x, output_p);
}
}
/* Create allocnos corresponding to pseudo-registers living in the
basic block represented by the corresponding loop tree node
BB_NODE. */
static void
create_bb_allocnos (ira_loop_tree_node_t bb_node)
{
basic_block bb;
rtx_insn *insn;
unsigned int i;
bitmap_iterator bi;
curr_bb = bb = bb_node->bb;
ira_assert (bb != NULL);
FOR_BB_INSNS_REVERSE (bb, insn)
if (NONDEBUG_INSN_P (insn))
create_insn_allocnos (PATTERN (insn), NULL, false);
/* It might be a allocno living through from one subloop to
another. */
EXECUTE_IF_SET_IN_REG_SET (df_get_live_in (bb), FIRST_PSEUDO_REGISTER, i, bi)
if (ira_curr_regno_allocno_map[i] == NULL)
ira_create_allocno (i, false, ira_curr_loop_tree_node);
}
/* Create allocnos corresponding to pseudo-registers living on edge E
(a loop entry or exit). Also mark the allocnos as living on the
loop border. */
static void
create_loop_allocnos (edge e)
{
unsigned int i;
bitmap live_in_regs, border_allocnos;
bitmap_iterator bi;
ira_loop_tree_node_t parent;
live_in_regs = df_get_live_in (e->dest);
border_allocnos = ira_curr_loop_tree_node->border_allocnos;
EXECUTE_IF_SET_IN_REG_SET (df_get_live_out (e->src),
FIRST_PSEUDO_REGISTER, i, bi)
if (bitmap_bit_p (live_in_regs, i))
{
if (ira_curr_regno_allocno_map[i] == NULL)
{
/* The order of creations is important for right
ira_regno_allocno_map. */
if ((parent = ira_curr_loop_tree_node->parent) != NULL
&& parent->regno_allocno_map[i] == NULL)
ira_create_allocno (i, false, parent);
ira_create_allocno (i, false, ira_curr_loop_tree_node);
}
bitmap_set_bit (border_allocnos,
ALLOCNO_NUM (ira_curr_regno_allocno_map[i]));
}
}
/* Create allocnos corresponding to pseudo-registers living in loop
represented by the corresponding loop tree node LOOP_NODE. This
function is called by ira_traverse_loop_tree. */
static void
create_loop_tree_node_allocnos (ira_loop_tree_node_t loop_node)
{
if (loop_node->bb != NULL)
create_bb_allocnos (loop_node);
else if (loop_node != ira_loop_tree_root)
{
int i;
edge_iterator ei;
edge e;
vec<edge> edges;
ira_assert (current_loops != NULL);
FOR_EACH_EDGE (e, ei, loop_node->loop->header->preds)
if (e->src != loop_node->loop->latch)
create_loop_allocnos (e);
edges = get_loop_exit_edges (loop_node->loop);
FOR_EACH_VEC_ELT (edges, i, e)
create_loop_allocnos (e);
edges.release ();
}
}
/* Propagate information about allocnos modified inside the loop given
by its LOOP_TREE_NODE to its parent. */
static void
propagate_modified_regnos (ira_loop_tree_node_t loop_tree_node)
{
if (loop_tree_node == ira_loop_tree_root)
return;
ira_assert (loop_tree_node->bb == NULL);
bitmap_ior_into (loop_tree_node->parent->modified_regnos,
loop_tree_node->modified_regnos);
}
/* Propagate new info about allocno A (see comments about accumulated
info in allocno definition) to the corresponding allocno on upper
loop tree level. So allocnos on upper levels accumulate
information about the corresponding allocnos in nested regions.
The new info means allocno info finally calculated in this
file. */
static void
propagate_allocno_info (void)
{
int i;
ira_allocno_t a, parent_a;
ira_loop_tree_node_t parent;
enum reg_class aclass;
if (flag_ira_region != IRA_REGION_ALL
&& flag_ira_region != IRA_REGION_MIXED)
return;
for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--)
for (a = ira_regno_allocno_map[i];
a != NULL;
a = ALLOCNO_NEXT_REGNO_ALLOCNO (a))
if ((parent = ALLOCNO_LOOP_TREE_NODE (a)->parent) != NULL
&& (parent_a = parent->regno_allocno_map[i]) != NULL
/* There are no caps yet at this point. So use
border_allocnos to find allocnos for the propagation. */
&& bitmap_bit_p (ALLOCNO_LOOP_TREE_NODE (a)->border_allocnos,
ALLOCNO_NUM (a)))
{
if (! ALLOCNO_BAD_SPILL_P (a))
ALLOCNO_BAD_SPILL_P (parent_a) = false;
ALLOCNO_NREFS (parent_a) += ALLOCNO_NREFS (a);
ALLOCNO_FREQ (parent_a) += ALLOCNO_FREQ (a);
ALLOCNO_CALL_FREQ (parent_a) += ALLOCNO_CALL_FREQ (a);
merge_hard_reg_conflicts (a, parent_a, true);
ALLOCNO_CALLS_CROSSED_NUM (parent_a)
+= ALLOCNO_CALLS_CROSSED_NUM (a);
ALLOCNO_CHEAP_CALLS_CROSSED_NUM (parent_a)
+= ALLOCNO_CHEAP_CALLS_CROSSED_NUM (a);
ALLOCNO_CROSSED_CALLS_ABIS (parent_a)
|= ALLOCNO_CROSSED_CALLS_ABIS (a);
ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (parent_a)
|= ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (a);
ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (parent_a)
+= ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a);
aclass = ALLOCNO_CLASS (a);
ira_assert (aclass == ALLOCNO_CLASS (parent_a));
ira_allocate_and_accumulate_costs
(&ALLOCNO_HARD_REG_COSTS (parent_a), aclass,
ALLOCNO_HARD_REG_COSTS (a));
ira_allocate_and_accumulate_costs
(&ALLOCNO_CONFLICT_HARD_REG_COSTS (parent_a),
aclass,
ALLOCNO_CONFLICT_HARD_REG_COSTS (a));
ALLOCNO_CLASS_COST (parent_a)
+= ALLOCNO_CLASS_COST (a);
ALLOCNO_MEMORY_COST (parent_a) += ALLOCNO_MEMORY_COST (a);
}
}
/* Create allocnos corresponding to pseudo-registers in the current
function. Traverse the loop tree for this. */
static void
create_allocnos (void)
{
/* We need to process BB first to correctly link allocnos by member
next_regno_allocno. */
ira_traverse_loop_tree (true, ira_loop_tree_root,
create_loop_tree_node_allocnos, NULL);
if (optimize)
ira_traverse_loop_tree (false, ira_loop_tree_root, NULL,
propagate_modified_regnos);
}
/* The page contains function to remove some regions from a separate
register allocation. We remove regions whose separate allocation
will hardly improve the result. As a result we speed up regional
register allocation. */
/* The function changes the object in range list given by R to OBJ. */
static void
change_object_in_range_list (live_range_t r, ira_object_t obj)
{
for (; r != NULL; r = r->next)
r->object = obj;
}
/* Move all live ranges associated with allocno FROM to allocno TO. */
static void
move_allocno_live_ranges (ira_allocno_t from, ira_allocno_t to)
{
int i;
int n = ALLOCNO_NUM_OBJECTS (from);
gcc_assert (n == ALLOCNO_NUM_OBJECTS (to));
for (i = 0; i < n; i++)
{
ira_object_t from_obj = ALLOCNO_OBJECT (from, i);
ira_object_t to_obj = ALLOCNO_OBJECT (to, i);
live_range_t lr = OBJECT_LIVE_RANGES (from_obj);
if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL)
{
fprintf (ira_dump_file,
" Moving ranges of a%dr%d to a%dr%d: ",
ALLOCNO_NUM (from), ALLOCNO_REGNO (from),
ALLOCNO_NUM (to), ALLOCNO_REGNO (to));
ira_print_live_range_list (ira_dump_file, lr);
}
change_object_in_range_list (lr, to_obj);
OBJECT_LIVE_RANGES (to_obj)
= ira_merge_live_ranges (lr, OBJECT_LIVE_RANGES (to_obj));
OBJECT_LIVE_RANGES (from_obj) = NULL;
}
}
static void
copy_allocno_live_ranges (ira_allocno_t from, ira_allocno_t to)
{
int i;
int n = ALLOCNO_NUM_OBJECTS (from);
gcc_assert (n == ALLOCNO_NUM_OBJECTS (to));
for (i = 0; i < n; i++)
{
ira_object_t from_obj = ALLOCNO_OBJECT (from, i);
ira_object_t to_obj = ALLOCNO_OBJECT (to, i);
live_range_t lr = OBJECT_LIVE_RANGES (from_obj);
if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL)
{
fprintf (ira_dump_file, " Copying ranges of a%dr%d to a%dr%d: ",
ALLOCNO_NUM (from), ALLOCNO_REGNO (from),
ALLOCNO_NUM (to), ALLOCNO_REGNO (to));
ira_print_live_range_list (ira_dump_file, lr);
}
lr = ira_copy_live_range_list (lr);
change_object_in_range_list (lr, to_obj);
OBJECT_LIVE_RANGES (to_obj)
= ira_merge_live_ranges (lr, OBJECT_LIVE_RANGES (to_obj));
}
}
/* Return TRUE if NODE represents a loop with low register
pressure. */
static bool
low_pressure_loop_node_p (ira_loop_tree_node_t node)
{
int i;
enum reg_class pclass;
if (node->bb != NULL)
return false;
for (i = 0; i < ira_pressure_classes_num; i++)
{
pclass = ira_pressure_classes[i];
if (node->reg_pressure[pclass] > ira_class_hard_regs_num[pclass]
&& ira_class_hard_regs_num[pclass] > 1)
return false;
}
return true;
}
#ifdef STACK_REGS
/* Return TRUE if LOOP has a complex enter or exit edge. We don't
form a region from such loop if the target use stack register
because reg-stack.c cannot deal with such edges. */
static bool
loop_with_complex_edge_p (class loop *loop)
{
int i;
edge_iterator ei;
edge e;
vec<edge> edges;
bool res;
FOR_EACH_EDGE (e, ei, loop->header->preds)
if (e->flags & EDGE_EH)
return true;
edges = get_loop_exit_edges (loop);
res = false;
FOR_EACH_VEC_ELT (edges, i, e)
if (e->flags & EDGE_COMPLEX)
{
res = true;
break;
}
edges.release ();
return res;
}
#endif
/* Sort loops for marking them for removal. We put already marked
loops first, then less frequent loops next, and then outer loops
next. */
static int
loop_compare_func (const void *v1p, const void *v2p)
{
int diff;
ira_loop_tree_node_t l1 = *(const ira_loop_tree_node_t *) v1p;
ira_loop_tree_node_t l2 = *(const ira_loop_tree_node_t *) v2p;
ira_assert (l1->parent != NULL && l2->parent != NULL);
if (l1->to_remove_p && ! l2->to_remove_p)
return -1;
if (! l1->to_remove_p && l2->to_remove_p)
return 1;
if ((diff = l1->loop->header->count.to_frequency (cfun)
- l2->loop->header->count.to_frequency (cfun)) != 0)
return diff;
if ((diff = (int) loop_depth (l1->loop) - (int) loop_depth (l2->loop)) != 0)
return diff;
/* Make sorting stable. */
return l1->loop_num - l2->loop_num;
}
/* Mark loops which should be removed from regional allocation. We
remove a loop with low register pressure inside another loop with
register pressure. In this case a separate allocation of the loop
hardly helps (for irregular register file architecture it could
help by choosing a better hard register in the loop but we prefer
faster allocation even in this case). We also remove cheap loops
if there are more than param_ira_max_loops_num of them. Loop with EH
exit or enter edges are removed too because the allocation might
require put pseudo moves on the EH edges (we could still do this
for pseudos with caller saved hard registers in some cases but it
is impossible to say here or during top-down allocation pass what
hard register the pseudos get finally). */
static void
mark_loops_for_removal (void)
{
int i, n;
ira_loop_tree_node_t *sorted_loops;
loop_p loop;
ira_assert (current_loops != NULL);
sorted_loops
= (ira_loop_tree_node_t *) ira_allocate (sizeof (ira_loop_tree_node_t)
* number_of_loops (cfun));
for (n = i = 0; vec_safe_iterate (get_loops (cfun), i, &loop); i++)
if (ira_loop_nodes[i].regno_allocno_map != NULL)
{
if (ira_loop_nodes[i].parent == NULL)
{
/* Don't remove the root. */
ira_loop_nodes[i].to_remove_p = false;
continue;
}
sorted_loops[n++] = &ira_loop_nodes[i];
ira_loop_nodes[i].to_remove_p
= ((low_pressure_loop_node_p (ira_loop_nodes[i].parent)
&& low_pressure_loop_node_p (&ira_loop_nodes[i]))
#ifdef STACK_REGS
|| loop_with_complex_edge_p (ira_loop_nodes[i].loop)
#endif
);
}
qsort (sorted_loops, n, sizeof (ira_loop_tree_node_t), loop_compare_func);
for (i = 0; i < n - param_ira_max_loops_num; i++)
{
sorted_loops[i]->to_remove_p = true;
if (internal_flag_ira_verbose > 1 && ira_dump_file != NULL)
fprintf
(ira_dump_file,
" Mark loop %d (header %d, freq %d, depth %d) for removal (%s)\n",
sorted_loops[i]->loop_num, sorted_loops[i]->loop->header->index,
sorted_loops[i]->loop->header->count.to_frequency (cfun),
loop_depth (sorted_loops[i]->loop),
low_pressure_loop_node_p (sorted_loops[i]->parent)
&& low_pressure_loop_node_p (sorted_loops[i])
? "low pressure" : "cheap loop");
}
ira_free (sorted_loops);
}
/* Mark all loops but root for removing. */
static void
mark_all_loops_for_removal (void)
{
int i;
loop_p loop;
ira_assert (current_loops != NULL);
FOR_EACH_VEC_SAFE_ELT (get_loops (cfun), i, loop)
if (ira_loop_nodes[i].regno_allocno_map != NULL)
{
if (ira_loop_nodes[i].parent == NULL)
{
/* Don't remove the root. */
ira_loop_nodes[i].to_remove_p = false;
continue;
}
ira_loop_nodes[i].to_remove_p = true;
if (internal_flag_ira_verbose > 1 && ira_dump_file != NULL)
fprintf
(ira_dump_file,
" Mark loop %d (header %d, freq %d, depth %d) for removal\n",
ira_loop_nodes[i].loop_num,
ira_loop_nodes[i].loop->header->index,
ira_loop_nodes[i].loop->header->count.to_frequency (cfun),
loop_depth (ira_loop_nodes[i].loop));
}
}
/* Definition of vector of loop tree nodes. */
/* Vec containing references to all removed loop tree nodes. */
static vec<ira_loop_tree_node_t> removed_loop_vec;
/* Vec containing references to all children of loop tree nodes. */
static vec<ira_loop_tree_node_t> children_vec;
/* Remove subregions of NODE if their separate allocation will not
improve the result. */
static void
remove_uneccesary_loop_nodes_from_loop_tree (ira_loop_tree_node_t node)
{
unsigned int start;
bool remove_p;
ira_loop_tree_node_t subnode;
remove_p = node->to_remove_p;
if (! remove_p)
children_vec.safe_push (node);
start = children_vec.length ();
for (subnode = node->children; subnode != NULL; subnode = subnode->next)
if (subnode->bb == NULL)
remove_uneccesary_loop_nodes_from_loop_tree (subnode);
else
children_vec.safe_push (subnode);
node->children = node->subloops = NULL;
if (remove_p)
{
removed_loop_vec.safe_push (node);
return;
}
while (children_vec.length () > start)
{
subnode = children_vec.pop ();
subnode->parent = node;
subnode->next = node->children;
node->children = subnode;
if (subnode->bb == NULL)
{
subnode->subloop_next = node->subloops;
node->subloops = subnode;
}
}
}
/* Return TRUE if NODE is inside PARENT. */
static bool
loop_is_inside_p (ira_loop_tree_node_t node, ira_loop_tree_node_t parent)
{
for (node = node->parent; node != NULL; node = node->parent)
if (node == parent)
return true;
return false;
}
/* Sort allocnos according to their order in regno allocno list. */
static int
regno_allocno_order_compare_func (const void *v1p, const void *v2p)
{
ira_allocno_t a1 = *(const ira_allocno_t *) v1p;
ira_allocno_t a2 = *(const ira_allocno_t *) v2p;
ira_loop_tree_node_t n1 = ALLOCNO_LOOP_TREE_NODE (a1);
ira_loop_tree_node_t n2 = ALLOCNO_LOOP_TREE_NODE (a2);
if (loop_is_inside_p (n1, n2))
return -1;
else if (loop_is_inside_p (n2, n1))
return 1;
/* If allocnos are equally good, sort by allocno numbers, so that
the results of qsort leave nothing to chance. We put allocnos
with higher number first in the list because it is the original
order for allocnos from loops on the same levels. */
return ALLOCNO_NUM (a2) - ALLOCNO_NUM (a1);
}
/* This array is used to sort allocnos to restore allocno order in
the regno allocno list. */
static ira_allocno_t *regno_allocnos;
/* Restore allocno order for REGNO in the regno allocno list. */
static void
ira_rebuild_regno_allocno_list (int regno)
{
int i, n;
ira_allocno_t a;
for (n = 0, a = ira_regno_allocno_map[regno];
a != NULL;
a = ALLOCNO_NEXT_REGNO_ALLOCNO (a))
regno_allocnos[n++] = a;
ira_assert (n > 0);
qsort (regno_allocnos, n, sizeof (ira_allocno_t),
regno_allocno_order_compare_func);
for (i = 1; i < n; i++)
ALLOCNO_NEXT_REGNO_ALLOCNO (regno_allocnos[i - 1]) = regno_allocnos[i];
ALLOCNO_NEXT_REGNO_ALLOCNO (regno_allocnos[n - 1]) = NULL;
ira_regno_allocno_map[regno] = regno_allocnos[0];
if (internal_flag_ira_verbose > 1 && ira_dump_file != NULL)
fprintf (ira_dump_file, " Rebuilding regno allocno list for %d\n", regno);
}
/* Propagate info from allocno FROM_A to allocno A. */
static void
propagate_some_info_from_allocno (ira_allocno_t a, ira_allocno_t from_a)
{
enum reg_class aclass;
merge_hard_reg_conflicts (from_a, a, false);
ALLOCNO_NREFS (a) += ALLOCNO_NREFS (from_a);
ALLOCNO_FREQ (a) += ALLOCNO_FREQ (from_a);
ALLOCNO_CALL_FREQ (a) += ALLOCNO_CALL_FREQ (from_a);
ALLOCNO_CALLS_CROSSED_NUM (a) += ALLOCNO_CALLS_CROSSED_NUM (from_a);
ALLOCNO_CHEAP_CALLS_CROSSED_NUM (a)
+= ALLOCNO_CHEAP_CALLS_CROSSED_NUM (from_a);
ALLOCNO_CROSSED_CALLS_ABIS (a) |= ALLOCNO_CROSSED_CALLS_ABIS (from_a);
ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (a)
|= ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (from_a);
ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a)
+= ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (from_a);
if (! ALLOCNO_BAD_SPILL_P (from_a))
ALLOCNO_BAD_SPILL_P (a) = false;
aclass = ALLOCNO_CLASS (from_a);
ira_assert (aclass == ALLOCNO_CLASS (a));
ira_allocate_and_accumulate_costs (&ALLOCNO_HARD_REG_COSTS (a), aclass,
ALLOCNO_HARD_REG_COSTS (from_a));
ira_allocate_and_accumulate_costs (&ALLOCNO_CONFLICT_HARD_REG_COSTS (a),
aclass,
ALLOCNO_CONFLICT_HARD_REG_COSTS (from_a));
ALLOCNO_CLASS_COST (a) += ALLOCNO_CLASS_COST (from_a);
ALLOCNO_MEMORY_COST (a) += ALLOCNO_MEMORY_COST (from_a);
}
/* Remove allocnos from loops removed from the allocation
consideration. */
static void
remove_unnecessary_allocnos (void)
{
int regno;
bool merged_p, rebuild_p;
ira_allocno_t a, prev_a, next_a, parent_a;
ira_loop_tree_node_t a_node, parent;
merged_p = false;
regno_allocnos = NULL;
for (regno = max_reg_num () - 1; regno >= FIRST_PSEUDO_REGISTER; regno--)
{
rebuild_p = false;
for (prev_a = NULL, a = ira_regno_allocno_map[regno];
a != NULL;
a = next_a)
{
next_a = ALLOCNO_NEXT_REGNO_ALLOCNO (a);
a_node = ALLOCNO_LOOP_TREE_NODE (a);
if (! a_node->to_remove_p)
prev_a = a;
else
{
for (parent = a_node->parent;
(parent_a = parent->regno_allocno_map[regno]) == NULL
&& parent->to_remove_p;
parent = parent->parent)
;
if (parent_a == NULL)
{
/* There are no allocnos with the same regno in
upper region -- just move the allocno to the
upper region. */
prev_a = a;
ALLOCNO_LOOP_TREE_NODE (a) = parent;
parent->regno_allocno_map[regno] = a;
bitmap_set_bit (parent->all_allocnos, ALLOCNO_NUM (a));
rebuild_p = true;
}
else
{
/* Remove the allocno and update info of allocno in
the upper region. */
if (prev_a == NULL)
ira_regno_allocno_map[regno] = next_a;
else
ALLOCNO_NEXT_REGNO_ALLOCNO (prev_a) = next_a;
move_allocno_live_ranges (a, parent_a);
merged_p = true;
propagate_some_info_from_allocno (parent_a, a);
/* Remove it from the corresponding regno allocno
map to avoid info propagation of subsequent
allocno into this already removed allocno. */
a_node->regno_allocno_map[regno] = NULL;
ira_remove_allocno_prefs (a);
finish_allocno (a);
}
}
}
if (rebuild_p)
/* We need to restore the order in regno allocno list. */
{
if (regno_allocnos == NULL)
regno_allocnos
= (ira_allocno_t *) ira_allocate (sizeof (ira_allocno_t)
* ira_allocnos_num);
ira_rebuild_regno_allocno_list (regno);
}
}
if (merged_p)
ira_rebuild_start_finish_chains ();
if (regno_allocnos != NULL)
ira_free (regno_allocnos);
}
/* Remove allocnos from all loops but the root. */
static void
remove_low_level_allocnos (void)
{
int regno;
bool merged_p, propagate_p;
ira_allocno_t a, top_a;
ira_loop_tree_node_t a_node, parent;
ira_allocno_iterator ai;
merged_p = false;
FOR_EACH_ALLOCNO (a, ai)
{
a_node = ALLOCNO_LOOP_TREE_NODE (a);
if (a_node == ira_loop_tree_root || ALLOCNO_CAP_MEMBER (a) != NULL)
continue;
regno = ALLOCNO_REGNO (a);
if ((top_a = ira_loop_tree_root->regno_allocno_map[regno]) == NULL)
{
ALLOCNO_LOOP_TREE_NODE (a) = ira_loop_tree_root;
ira_loop_tree_root->regno_allocno_map[regno] = a;
continue;
}
propagate_p = a_node->parent->regno_allocno_map[regno] == NULL;
/* Remove the allocno and update info of allocno in the upper
region. */
move_allocno_live_ranges (a, top_a);
merged_p = true;
if (propagate_p)
propagate_some_info_from_allocno (top_a, a);
}
FOR_EACH_ALLOCNO (a, ai)
{
a_node = ALLOCNO_LOOP_TREE_NODE (a);
if (a_node == ira_loop_tree_root)
continue;
parent = a_node->parent;
regno = ALLOCNO_REGNO (a);
if (ALLOCNO_CAP_MEMBER (a) != NULL)
ira_assert (ALLOCNO_CAP (a) != NULL);
else if (ALLOCNO_CAP (a) == NULL)
ira_assert (parent->regno_allocno_map[regno] != NULL);
}
FOR_EACH_ALLOCNO (a, ai)
{
regno = ALLOCNO_REGNO (a);
if (ira_loop_tree_root->regno_allocno_map[regno] == a)
{
ira_object_t obj;
ira_allocno_object_iterator oi;
ira_regno_allocno_map[regno] = a;
ALLOCNO_NEXT_REGNO_ALLOCNO (a) = NULL;
ALLOCNO_CAP_MEMBER (a) = NULL;
FOR_EACH_ALLOCNO_OBJECT (a, obj, oi)
OBJECT_CONFLICT_HARD_REGS (obj)
= OBJECT_TOTAL_CONFLICT_HARD_REGS (obj);
#ifdef STACK_REGS
if (ALLOCNO_TOTAL_NO_STACK_REG_P (a))
ALLOCNO_NO_STACK_REG_P (a) = true;
#endif
}
else
{
ira_remove_allocno_prefs (a);
finish_allocno (a);
}
}
if (merged_p)
ira_rebuild_start_finish_chains ();
}
/* Remove loops from consideration. We remove all loops except for
root if ALL_P or loops for which a separate allocation will not
improve the result. We have to do this after allocno creation and
their costs and allocno class evaluation because only after that
the register pressure can be known and is calculated. */
static void
remove_unnecessary_regions (bool all_p)
{
if (current_loops == NULL)
return;
if (all_p)
mark_all_loops_for_removal ();
else
mark_loops_for_removal ();
children_vec.create (last_basic_block_for_fn (cfun)
+ number_of_loops (cfun));
removed_loop_vec.create (last_basic_block_for_fn (cfun)
+ number_of_loops (cfun));
remove_uneccesary_loop_nodes_from_loop_tree (ira_loop_tree_root);
children_vec.release ();
if (all_p)
remove_low_level_allocnos ();
else
remove_unnecessary_allocnos ();
while (removed_loop_vec.length () > 0)
finish_loop_tree_node (removed_loop_vec.pop ());
removed_loop_vec.release ();
}
/* At this point true value of allocno attribute bad_spill_p means
that there is an insn where allocno occurs and where the allocno
cannot be used as memory. The function updates the attribute, now
it can be true only for allocnos which cannot be used as memory in
an insn and in whose live ranges there is other allocno deaths.
Spilling allocnos with true value will not improve the code because
it will not make other allocnos colorable and additional reloads
for the corresponding pseudo will be generated in reload pass for
each insn it occurs.
This is a trick mentioned in one classic article of Chaitin etc
which is frequently omitted in other implementations of RA based on
graph coloring. */
static void
update_bad_spill_attribute (void)
{
int i;
ira_allocno_t a;
ira_allocno_iterator ai;
ira_allocno_object_iterator aoi;
ira_object_t obj;
live_range_t r;
enum reg_class aclass;
bitmap_head dead_points[N_REG_CLASSES];
for (i = 0; i < ira_allocno_classes_num; i++)
{
aclass = ira_allocno_classes[i];
bitmap_initialize (&dead_points[aclass], ®_obstack);
}
FOR_EACH_ALLOCNO (a, ai)
{
aclass = ALLOCNO_CLASS (a);
if (aclass == NO_REGS)
continue;
FOR_EACH_ALLOCNO_OBJECT (a, obj, aoi)
for (r = OBJECT_LIVE_RANGES (obj); r != NULL; r = r->next)
bitmap_set_bit (&dead_points[aclass], r->finish);
}
FOR_EACH_ALLOCNO (a, ai)
{
aclass = ALLOCNO_CLASS (a);
if (aclass == NO_REGS)
continue;
if (! ALLOCNO_BAD_SPILL_P (a))
continue;
FOR_EACH_ALLOCNO_OBJECT (a, obj, aoi)
{
for (r = OBJECT_LIVE_RANGES (obj); r != NULL; r = r->next)
{
for (i = r->start + 1; i < r->finish; i++)
if (bitmap_bit_p (&dead_points[aclass], i))
break;
if (i < r->finish)
break;
}
if (r != NULL)
{
ALLOCNO_BAD_SPILL_P (a) = false;
break;
}
}
}
for (i = 0; i < ira_allocno_classes_num; i++)
{
aclass = ira_allocno_classes[i];
bitmap_clear (&dead_points[aclass]);
}
}
/* Set up minimal and maximal live range points for allocnos. */
static void
setup_min_max_allocno_live_range_point (void)
{
int i;
ira_allocno_t a, parent_a, cap;
ira_allocno_iterator ai;
#ifdef ENABLE_IRA_CHECKING
ira_object_iterator oi;
ira_object_t obj;
#endif
live_range_t r;
ira_loop_tree_node_t parent;
FOR_EACH_ALLOCNO (a, ai)
{
int n = ALLOCNO_NUM_OBJECTS (a);
for (i = 0; i < n; i++)
{
ira_object_t obj = ALLOCNO_OBJECT (a, i);
r = OBJECT_LIVE_RANGES (obj);
if (r == NULL)
continue;
OBJECT_MAX (obj) = r->finish;
for (; r->next != NULL; r = r->next)
;
OBJECT_MIN (obj) = r->start;
}
}
for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--)
for (a = ira_regno_allocno_map[i];
a != NULL;
a = ALLOCNO_NEXT_REGNO_ALLOCNO (a))
{
int j;
int n = ALLOCNO_NUM_OBJECTS (a);
for (j = 0; j < n; j++)
{
ira_object_t obj = ALLOCNO_OBJECT (a, j);
ira_object_t parent_obj;
if (OBJECT_MAX (obj) < 0)
{
/* The object is not used and hence does not live. */
ira_assert (OBJECT_LIVE_RANGES (obj) == NULL);
OBJECT_MAX (obj) = 0;
OBJECT_MIN (obj) = 1;
continue;
}
ira_assert (ALLOCNO_CAP_MEMBER (a) == NULL);
/* Accumulation of range info. */
if (ALLOCNO_CAP (a) != NULL)
{
for (cap = ALLOCNO_CAP (a); cap != NULL; cap = ALLOCNO_CAP (cap))
{
ira_object_t cap_obj = ALLOCNO_OBJECT (cap, j);
if (OBJECT_MAX (cap_obj) < OBJECT_MAX (obj))
OBJECT_MAX (cap_obj) = OBJECT_MAX (obj);
if (OBJECT_MIN (cap_obj) > OBJECT_MIN (obj))
OBJECT_MIN (cap_obj) = OBJECT_MIN (obj);
}
continue;
}
if ((parent = ALLOCNO_LOOP_TREE_NODE (a)->parent) == NULL)
continue;
parent_a = parent->regno_allocno_map[i];
parent_obj = ALLOCNO_OBJECT (parent_a, j);
if (OBJECT_MAX (parent_obj) < OBJECT_MAX (obj))
OBJECT_MAX (parent_obj) = OBJECT_MAX (obj);
if (OBJECT_MIN (parent_obj) > OBJECT_MIN (obj))
OBJECT_MIN (parent_obj) = OBJECT_MIN (obj);
}
}
#ifdef ENABLE_IRA_CHECKING
FOR_EACH_OBJECT (obj, oi)
{
if ((OBJECT_MIN (obj) >= 0 && OBJECT_MIN (obj) <= ira_max_point)
&& (OBJECT_MAX (obj) >= 0 && OBJECT_MAX (obj) <= ira_max_point))
continue;
gcc_unreachable ();
}
#endif
}
/* Sort allocnos according to their live ranges. Allocnos with
smaller allocno class are put first unless we use priority
coloring. Allocnos with the same class are ordered according
their start (min). Allocnos with the same start are ordered
according their finish (max). */
static int
object_range_compare_func (const void *v1p, const void *v2p)
{
int diff;
ira_object_t obj1 = *(const ira_object_t *) v1p;
ira_object_t obj2 = *(const ira_object_t *) v2p;
ira_allocno_t a1 = OBJECT_ALLOCNO (obj1);
ira_allocno_t a2 = OBJECT_ALLOCNO (obj2);
if ((diff = OBJECT_MIN (obj1) - OBJECT_MIN (obj2)) != 0)
return diff;
if ((diff = OBJECT_MAX (obj1) - OBJECT_MAX (obj2)) != 0)
return diff;
return ALLOCNO_NUM (a1) - ALLOCNO_NUM (a2);
}
/* Sort ira_object_id_map and set up conflict id of allocnos. */
static void
sort_conflict_id_map (void)
{
int i, num;
ira_allocno_t a;
ira_allocno_iterator ai;
num = 0;
FOR_EACH_ALLOCNO (a, ai)
{
ira_allocno_object_iterator oi;
ira_object_t obj;
FOR_EACH_ALLOCNO_OBJECT (a, obj, oi)
ira_object_id_map[num++] = obj;
}
if (num > 1)
qsort (ira_object_id_map, num, sizeof (ira_object_t),
object_range_compare_func);
for (i = 0; i < num; i++)
{
ira_object_t obj = ira_object_id_map[i];
gcc_assert (obj != NULL);
OBJECT_CONFLICT_ID (obj) = i;
}
for (i = num; i < ira_objects_num; i++)
ira_object_id_map[i] = NULL;
}
/* Set up minimal and maximal conflict ids of allocnos with which
given allocno can conflict. */
static void
setup_min_max_conflict_allocno_ids (void)
{
int aclass;
int i, j, min, max, start, finish, first_not_finished, filled_area_start;
int *live_range_min, *last_lived;
int word0_min, word0_max;
ira_allocno_t a;
ira_allocno_iterator ai;
live_range_min = (int *) ira_allocate (sizeof (int) * ira_objects_num);
aclass = -1;
first_not_finished = -1;
for (i = 0; i < ira_objects_num; i++)
{
ira_object_t obj = ira_object_id_map[i];
if (obj == NULL)
continue;
a = OBJECT_ALLOCNO (obj);
if (aclass < 0)
{
aclass = ALLOCNO_CLASS (a);
min = i;
first_not_finished = i;
}
else
{
start = OBJECT_MIN (obj);
/* If we skip an allocno, the allocno with smaller ids will
be also skipped because of the secondary sorting the
range finishes (see function
object_range_compare_func). */
while (first_not_finished < i
&& start > OBJECT_MAX (ira_object_id_map
[first_not_finished]))
first_not_finished++;
min = first_not_finished;
}
if (min == i)
/* We could increase min further in this case but it is good
enough. */
min++;
live_range_min[i] = OBJECT_MIN (obj);
OBJECT_MIN (obj) = min;
}
last_lived = (int *) ira_allocate (sizeof (int) * ira_max_point);
aclass = -1;
filled_area_start = -1;
for (i = ira_objects_num - 1; i >= 0; i--)
{
ira_object_t obj = ira_object_id_map[i];
if (obj == NULL)
continue;
a = OBJECT_ALLOCNO (obj);
if (aclass < 0)
{
aclass = ALLOCNO_CLASS (a);
for (j = 0; j < ira_max_point; j++)
last_lived[j] = -1;
filled_area_start = ira_max_point;
}
min = live_range_min[i];
finish = OBJECT_MAX (obj);
max = last_lived[finish];
if (max < 0)
/* We could decrease max further in this case but it is good
enough. */
max = OBJECT_CONFLICT_ID (obj) - 1;
OBJECT_MAX (obj) = max;
/* In filling, we can go further A range finish to recognize
intersection quickly because if the finish of subsequently
processed allocno (it has smaller conflict id) range is
further A range finish than they are definitely intersected
(the reason for this is the allocnos with bigger conflict id
have their range starts not smaller than allocnos with
smaller ids. */
for (j = min; j < filled_area_start; j++)
last_lived[j] = i;
filled_area_start = min;
}
ira_free (last_lived);
ira_free (live_range_min);
/* For allocnos with more than one object, we may later record extra conflicts in
subobject 0 that we cannot really know about here.
For now, simply widen the min/max range of these subobjects. */
word0_min = INT_MAX;
word0_max = INT_MIN;
FOR_EACH_ALLOCNO (a, ai)
{
int n = ALLOCNO_NUM_OBJECTS (a);
ira_object_t obj0;
if (n < 2)
continue;
obj0 = ALLOCNO_OBJECT (a, 0);
if (OBJECT_CONFLICT_ID (obj0) < word0_min)
word0_min = OBJECT_CONFLICT_ID (obj0);
if (OBJECT_CONFLICT_ID (obj0) > word0_max)
word0_max = OBJECT_CONFLICT_ID (obj0);
}
FOR_EACH_ALLOCNO (a, ai)
{
int n = ALLOCNO_NUM_OBJECTS (a);
ira_object_t obj0;
if (n < 2)
continue;
obj0 = ALLOCNO_OBJECT (a, 0);
if (OBJECT_MIN (obj0) > word0_min)
OBJECT_MIN (obj0) = word0_min;
if (OBJECT_MAX (obj0) < word0_max)
OBJECT_MAX (obj0) = word0_max;
}
}
static void
create_caps (void)
{
ira_allocno_t a;
ira_allocno_iterator ai;
ira_loop_tree_node_t loop_tree_node;
FOR_EACH_ALLOCNO (a, ai)
{
if (ALLOCNO_LOOP_TREE_NODE (a) == ira_loop_tree_root)
continue;
if (ALLOCNO_CAP_MEMBER (a) != NULL)
create_cap_allocno (a);
else if (ALLOCNO_CAP (a) == NULL)
{
loop_tree_node = ALLOCNO_LOOP_TREE_NODE (a);
if (!bitmap_bit_p (loop_tree_node->border_allocnos, ALLOCNO_NUM (a)))
create_cap_allocno (a);
}
}
}
/* The page contains code transforming more one region internal
representation (IR) to one region IR which is necessary for reload.
This transformation is called IR flattening. We might just rebuild
the IR for one region but we don't do it because it takes a lot of
time. */
/* Map: regno -> allocnos which will finally represent the regno for
IR with one region. */
static ira_allocno_t *regno_top_level_allocno_map;
/* Find the allocno that corresponds to A at a level one higher up in the
loop tree. Returns NULL if A is a cap, or if it has no parent. */
ira_allocno_t
ira_parent_allocno (ira_allocno_t a)
{
ira_loop_tree_node_t parent;
if (ALLOCNO_CAP (a) != NULL)
return NULL;
parent = ALLOCNO_LOOP_TREE_NODE (a)->parent;
if (parent == NULL)
return NULL;
return parent->regno_allocno_map[ALLOCNO_REGNO (a)];
}
/* Find the allocno that corresponds to A at a level one higher up in the
loop tree. If ALLOCNO_CAP is set for A, return that. */
ira_allocno_t
ira_parent_or_cap_allocno (ira_allocno_t a)
{
if (ALLOCNO_CAP (a) != NULL)
return ALLOCNO_CAP (a);
return ira_parent_allocno (a);
}
/* Process all allocnos originated from pseudo REGNO and copy live
ranges, hard reg conflicts, and allocno stack reg attributes from
low level allocnos to final allocnos which are destinations of
removed stores at a loop exit. Return true if we copied live
ranges. */
static bool
copy_info_to_removed_store_destinations (int regno)
{
ira_allocno_t a;
ira_allocno_t parent_a = NULL;
ira_loop_tree_node_t parent;
bool merged_p;
merged_p = false;
for (a = ira_regno_allocno_map[regno];
a != NULL;
a = ALLOCNO_NEXT_REGNO_ALLOCNO (a))
{
if (a != regno_top_level_allocno_map[REGNO (allocno_emit_reg (a))])
/* This allocno will be removed. */
continue;
/* Caps will be removed. */
ira_assert (ALLOCNO_CAP_MEMBER (a) == NULL);
for (parent = ALLOCNO_LOOP_TREE_NODE (a)->parent;
parent != NULL;
parent = parent->parent)
if ((parent_a = parent->regno_allocno_map[regno]) == NULL
|| (parent_a
== regno_top_level_allocno_map[REGNO
(allocno_emit_reg (parent_a))]
&& ALLOCNO_EMIT_DATA (parent_a)->mem_optimized_dest_p))
break;
if (parent == NULL || parent_a == NULL)
continue;
copy_allocno_live_ranges (a, parent_a);
merge_hard_reg_conflicts (a, parent_a, true);
ALLOCNO_CALL_FREQ (parent_a) += ALLOCNO_CALL_FREQ (a);
ALLOCNO_CALLS_CROSSED_NUM (parent_a)
+= ALLOCNO_CALLS_CROSSED_NUM (a);
ALLOCNO_CHEAP_CALLS_CROSSED_NUM (parent_a)
+= ALLOCNO_CHEAP_CALLS_CROSSED_NUM (a);
ALLOCNO_CROSSED_CALLS_ABIS (parent_a)
|= ALLOCNO_CROSSED_CALLS_ABIS (a);
ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (parent_a)
|= ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (a);
ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (parent_a)
+= ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a);
merged_p = true;
}
return merged_p;
}
/* Flatten the IR. In other words, this function transforms IR as if
it were built with one region (without loops). We could make it
much simpler by rebuilding IR with one region, but unfortunately it
takes a lot of time. MAX_REGNO_BEFORE_EMIT and
IRA_MAX_POINT_BEFORE_EMIT are correspondingly MAX_REG_NUM () and
IRA_MAX_POINT before emitting insns on the loop borders. */
void
ira_flattening (int max_regno_before_emit, int ira_max_point_before_emit)
{
int i, j;
bool keep_p;
int hard_regs_num;
bool new_pseudos_p, merged_p, mem_dest_p;
unsigned int n;
enum reg_class aclass;
ira_allocno_t a, parent_a, first, second, node_first, node_second;
ira_copy_t cp;
ira_loop_tree_node_t node;
live_range_t r;
ira_allocno_iterator ai;
ira_copy_iterator ci;
regno_top_level_allocno_map
= (ira_allocno_t *) ira_allocate (max_reg_num ()
* sizeof (ira_allocno_t));
memset (regno_top_level_allocno_map, 0,
max_reg_num () * sizeof (ira_allocno_t));
new_pseudos_p = merged_p = false;
FOR_EACH_ALLOCNO (a, ai)
{
ira_allocno_object_iterator oi;
ira_object_t obj;
if (ALLOCNO_CAP_MEMBER (a) != NULL)
/* Caps are not in the regno allocno maps and they are never
will be transformed into allocnos existing after IR
flattening. */
continue;
FOR_EACH_ALLOCNO_OBJECT (a, obj, oi)
OBJECT_TOTAL_CONFLICT_HARD_REGS (obj)
= OBJECT_CONFLICT_HARD_REGS (obj);
#ifdef STACK_REGS
ALLOCNO_TOTAL_NO_STACK_REG_P (a) = ALLOCNO_NO_STACK_REG_P (a);
#endif
}
/* Fix final allocno attributes. */
for (i = max_regno_before_emit - 1; i >= FIRST_PSEUDO_REGISTER; i--)
{
mem_dest_p = false;
for (a = ira_regno_allocno_map[i];
a != NULL;
a = ALLOCNO_NEXT_REGNO_ALLOCNO (a))
{
ira_emit_data_t parent_data, data = ALLOCNO_EMIT_DATA (a);
ira_assert (ALLOCNO_CAP_MEMBER (a) == NULL);
if (data->somewhere_renamed_p)
new_pseudos_p = true;
parent_a = ira_parent_allocno (a);
if (parent_a == NULL)
{
ALLOCNO_COPIES (a) = NULL;
regno_top_level_allocno_map[REGNO (data->reg)] = a;
continue;
}
ira_assert (ALLOCNO_CAP_MEMBER (parent_a) == NULL);
if (data->mem_optimized_dest != NULL)
mem_dest_p = true;
parent_data = ALLOCNO_EMIT_DATA (parent_a);
if (REGNO (data->reg) == REGNO (parent_data->reg))
{
merge_hard_reg_conflicts (a, parent_a, true);
move_allocno_live_ranges (a, parent_a);
merged_p = true;
parent_data->mem_optimized_dest_p
= (parent_data->mem_optimized_dest_p
|| data->mem_optimized_dest_p);
continue;
}
new_pseudos_p = true;
for (;;)
{
ALLOCNO_NREFS (parent_a) -= ALLOCNO_NREFS (a);
ALLOCNO_FREQ (parent_a) -= ALLOCNO_FREQ (a);
ALLOCNO_CALL_FREQ (parent_a) -= ALLOCNO_CALL_FREQ (a);
ALLOCNO_CALLS_CROSSED_NUM (parent_a)
-= ALLOCNO_CALLS_CROSSED_NUM (a);
ALLOCNO_CHEAP_CALLS_CROSSED_NUM (parent_a)
-= ALLOCNO_CHEAP_CALLS_CROSSED_NUM (a);
/* Assume that ALLOCNO_CROSSED_CALLS_ABIS and
ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS stay the same.
We'd need to rebuild the IR to do better. */
ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (parent_a)
-= ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a);
ira_assert (ALLOCNO_CALLS_CROSSED_NUM (parent_a) >= 0
&& ALLOCNO_NREFS (parent_a) >= 0
&& ALLOCNO_FREQ (parent_a) >= 0);
aclass = ALLOCNO_CLASS (parent_a);
hard_regs_num = ira_class_hard_regs_num[aclass];
if (ALLOCNO_HARD_REG_COSTS (a) != NULL
&& ALLOCNO_HARD_REG_COSTS (parent_a) != NULL)
for (j = 0; j < hard_regs_num; j++)
ALLOCNO_HARD_REG_COSTS (parent_a)[j]
-= ALLOCNO_HARD_REG_COSTS (a)[j];
if (ALLOCNO_CONFLICT_HARD_REG_COSTS (a) != NULL
&& ALLOCNO_CONFLICT_HARD_REG_COSTS (parent_a) != NULL)
for (j = 0; j < hard_regs_num; j++)
ALLOCNO_CONFLICT_HARD_REG_COSTS (parent_a)[j]
-= ALLOCNO_CONFLICT_HARD_REG_COSTS (a)[j];
ALLOCNO_CLASS_COST (parent_a)
-= ALLOCNO_CLASS_COST (a);
ALLOCNO_MEMORY_COST (parent_a) -= ALLOCNO_MEMORY_COST (a);
parent_a = ira_parent_allocno (parent_a);
if (parent_a == NULL)
break;
}
ALLOCNO_COPIES (a) = NULL;
regno_top_level_allocno_map[REGNO (data->reg)] = a;
}
if (mem_dest_p && copy_info_to_removed_store_destinations (i))
merged_p = true;
}
ira_assert (new_pseudos_p || ira_max_point_before_emit == ira_max_point);
if (merged_p || ira_max_point_before_emit != ira_max_point)
ira_rebuild_start_finish_chains ();
if (new_pseudos_p)
{
sparseset objects_live;
/* Rebuild conflicts. */
FOR_EACH_ALLOCNO (a, ai)
{
ira_allocno_object_iterator oi;
ira_object_t obj;
if (a != regno_top_level_allocno_map[REGNO (allocno_emit_reg (a))]
|| ALLOCNO_CAP_MEMBER (a) != NULL)
continue;
FOR_EACH_ALLOCNO_OBJECT (a, obj, oi)
{
for (r = OBJECT_LIVE_RANGES (obj); r != NULL; r = r->next)
ira_assert (r->object == obj);
clear_conflicts (obj);
}
}
objects_live = sparseset_alloc (ira_objects_num);
for (i = 0; i < ira_max_point; i++)
{
for (r = ira_start_point_ranges[i]; r != NULL; r = r->start_next)
{
ira_object_t obj = r->object;
a = OBJECT_ALLOCNO (obj);
if (a != regno_top_level_allocno_map[REGNO (allocno_emit_reg (a))]
|| ALLOCNO_CAP_MEMBER (a) != NULL)
continue;
aclass = ALLOCNO_CLASS (a);
EXECUTE_IF_SET_IN_SPARSESET (objects_live, n)
{
ira_object_t live_obj = ira_object_id_map[n];
ira_allocno_t live_a = OBJECT_ALLOCNO (live_obj);
enum reg_class live_aclass = ALLOCNO_CLASS (live_a);
if (ira_reg_classes_intersect_p[aclass][live_aclass]
/* Don't set up conflict for the allocno with itself. */
&& live_a != a)
ira_add_conflict (obj, live_obj);
}
sparseset_set_bit (objects_live, OBJECT_CONFLICT_ID (obj));
}
for (r = ira_finish_point_ranges[i]; r != NULL; r = r->finish_next)
sparseset_clear_bit (objects_live, OBJECT_CONFLICT_ID (r->object));
}
sparseset_free (objects_live);
compress_conflict_vecs ();
}
/* Mark some copies for removing and change allocnos in the rest
copies. */
FOR_EACH_COPY (cp, ci)
{
if (ALLOCNO_CAP_MEMBER (cp->first) != NULL
|| ALLOCNO_CAP_MEMBER (cp->second) != NULL)
{
if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL)
fprintf
(ira_dump_file, " Remove cp%d:%c%dr%d-%c%dr%d\n",
cp->num, ALLOCNO_CAP_MEMBER (cp->first) != NULL ? 'c' : 'a',
ALLOCNO_NUM (cp->first),
REGNO (allocno_emit_reg (cp->first)),
ALLOCNO_CAP_MEMBER (cp->second) != NULL ? 'c' : 'a',
ALLOCNO_NUM (cp->second),
REGNO (allocno_emit_reg (cp->second)));
cp->loop_tree_node = NULL;
continue;
}
first
= regno_top_level_allocno_map[REGNO (allocno_emit_reg (cp->first))];
second
= regno_top_level_allocno_map[REGNO (allocno_emit_reg (cp->second))];
node = cp->loop_tree_node;
if (node == NULL)
keep_p = true; /* It copy generated in ira-emit.c. */
else
{
/* Check that the copy was not propagated from level on
which we will have different pseudos. */
node_first = node->regno_allocno_map[ALLOCNO_REGNO (cp->first)];
node_second = node->regno_allocno_map[ALLOCNO_REGNO (cp->second)];
keep_p = ((REGNO (allocno_emit_reg (first))
== REGNO (allocno_emit_reg (node_first)))
&& (REGNO (allocno_emit_reg (second))
== REGNO (allocno_emit_reg (node_second))));
}
if (keep_p)
{
cp->loop_tree_node = ira_loop_tree_root;
cp->first = first;
cp->second = second;
}
else
{
cp->loop_tree_node = NULL;
if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL)
fprintf (ira_dump_file, " Remove cp%d:a%dr%d-a%dr%d\n",
cp->num, ALLOCNO_NUM (cp->first),
REGNO (allocno_emit_reg (cp->first)),
ALLOCNO_NUM (cp->second),
REGNO (allocno_emit_reg (cp->second)));
}
}
/* Remove unnecessary allocnos on lower levels of the loop tree. */
FOR_EACH_ALLOCNO (a, ai)
{
if (a != regno_top_level_allocno_map[REGNO (allocno_emit_reg (a))]
|| ALLOCNO_CAP_MEMBER (a) != NULL)
{
if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL)
fprintf (ira_dump_file, " Remove a%dr%d\n",
ALLOCNO_NUM (a), REGNO (allocno_emit_reg (a)));
ira_remove_allocno_prefs (a);
finish_allocno (a);
continue;
}
ALLOCNO_LOOP_TREE_NODE (a) = ira_loop_tree_root;
ALLOCNO_REGNO (a) = REGNO (allocno_emit_reg (a));
ALLOCNO_CAP (a) = NULL;
/* Restore updated costs for assignments from reload. */
ALLOCNO_UPDATED_MEMORY_COST (a) = ALLOCNO_MEMORY_COST (a);
ALLOCNO_UPDATED_CLASS_COST (a) = ALLOCNO_CLASS_COST (a);
if (! ALLOCNO_ASSIGNED_P (a))
ira_free_allocno_updated_costs (a);
ira_assert (ALLOCNO_UPDATED_HARD_REG_COSTS (a) == NULL);
ira_assert (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) == NULL);
}
/* Remove unnecessary copies. */
FOR_EACH_COPY (cp, ci)
{
if (cp->loop_tree_node == NULL)
{
ira_copies[cp->num] = NULL;
finish_copy (cp);
continue;
}
ira_assert
(ALLOCNO_LOOP_TREE_NODE (cp->first) == ira_loop_tree_root
&& ALLOCNO_LOOP_TREE_NODE (cp->second) == ira_loop_tree_root);
add_allocno_copy_to_list (cp);
swap_allocno_copy_ends_if_necessary (cp);
}
rebuild_regno_allocno_maps ();
if (ira_max_point != ira_max_point_before_emit)
ira_compress_allocno_live_ranges ();
ira_free (regno_top_level_allocno_map);
}
#ifdef ENABLE_IRA_CHECKING
/* Check creation of all allocnos. Allocnos on lower levels should
have allocnos or caps on all upper levels. */
static void
check_allocno_creation (void)
{
ira_allocno_t a;
ira_allocno_iterator ai;
ira_loop_tree_node_t loop_tree_node;
FOR_EACH_ALLOCNO (a, ai)
{
loop_tree_node = ALLOCNO_LOOP_TREE_NODE (a);
ira_assert (bitmap_bit_p (loop_tree_node->all_allocnos,
ALLOCNO_NUM (a)));
if (loop_tree_node == ira_loop_tree_root)
continue;
if (ALLOCNO_CAP_MEMBER (a) != NULL)
ira_assert (ALLOCNO_CAP (a) != NULL);
else if (ALLOCNO_CAP (a) == NULL)
ira_assert (loop_tree_node->parent
->regno_allocno_map[ALLOCNO_REGNO (a)] != NULL
&& bitmap_bit_p (loop_tree_node->border_allocnos,
ALLOCNO_NUM (a)));
}
}
#endif
/* Identify allocnos which prefer a register class with a single hard register.
Adjust ALLOCNO_CONFLICT_HARD_REG_COSTS so that conflicting allocnos are
less likely to use the preferred singleton register. */
static void
update_conflict_hard_reg_costs (void)
{
ira_allocno_t a;
ira_allocno_iterator ai;
int i, index, min;
FOR_EACH_ALLOCNO (a, ai)
{
reg_class_t aclass = ALLOCNO_CLASS (a);
reg_class_t pref = reg_preferred_class (ALLOCNO_REGNO (a));
int singleton = ira_class_singleton[pref][ALLOCNO_MODE (a)];
if (singleton < 0)
continue;
index = ira_class_hard_reg_index[(int) aclass][singleton];
if (index < 0)
continue;
if (ALLOCNO_CONFLICT_HARD_REG_COSTS (a) == NULL
|| ALLOCNO_HARD_REG_COSTS (a) == NULL)
continue;
min = INT_MAX;
for (i = ira_class_hard_regs_num[(int) aclass] - 1; i >= 0; i--)
if (ALLOCNO_HARD_REG_COSTS (a)[i] > ALLOCNO_CLASS_COST (a)
&& min > ALLOCNO_HARD_REG_COSTS (a)[i])
min = ALLOCNO_HARD_REG_COSTS (a)[i];
if (min == INT_MAX)
continue;
ira_allocate_and_set_costs (&ALLOCNO_CONFLICT_HARD_REG_COSTS (a),
aclass, 0);
ALLOCNO_CONFLICT_HARD_REG_COSTS (a)[index]
-= min - ALLOCNO_CLASS_COST (a);
}
}
/* 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
blocks) for regional allocation. A true return means that we
really need to flatten IR before the reload. */
bool
ira_build (void)
{
bool loops_p;
df_analyze ();
initiate_cost_vectors ();
initiate_allocnos ();
initiate_prefs ();
initiate_copies ();
create_loop_tree_nodes ();
form_loop_tree ();
create_allocnos ();
ira_costs ();
create_allocno_objects ();
ira_create_allocno_live_ranges ();
remove_unnecessary_regions (false);
ira_compress_allocno_live_ranges ();
update_bad_spill_attribute ();
loops_p = more_one_region_p ();
if (loops_p)
{
propagate_allocno_info ();
create_caps ();
}
ira_tune_allocno_costs ();
#ifdef ENABLE_IRA_CHECKING
check_allocno_creation ();
#endif
setup_min_max_allocno_live_range_point ();
sort_conflict_id_map ();
setup_min_max_conflict_allocno_ids ();
ira_build_conflicts ();
update_conflict_hard_reg_costs ();
if (! ira_conflicts_p)
{
ira_allocno_t a;
ira_allocno_iterator ai;
/* Remove all regions but root one. */
if (loops_p)
{
remove_unnecessary_regions (true);
loops_p = false;
}
/* We don't save hard registers around calls for fast allocation
-- add caller clobbered registers as conflicting ones to
allocno crossing calls. */
FOR_EACH_ALLOCNO (a, ai)
if (ALLOCNO_CALLS_CROSSED_NUM (a) != 0)
ior_hard_reg_conflicts (a, ira_need_caller_save_regs (a));
}
if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
print_copies (ira_dump_file);
if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
print_prefs (ira_dump_file);
if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL)
{
int n, nr, nr_big;
ira_allocno_t a;
live_range_t r;
ira_allocno_iterator ai;
n = 0;
nr = 0;
nr_big = 0;
FOR_EACH_ALLOCNO (a, ai)
{
int j, nobj = ALLOCNO_NUM_OBJECTS (a);
if (nobj > 1)
nr_big++;
for (j = 0; j < nobj; j++)
{
ira_object_t obj = ALLOCNO_OBJECT (a, j);
n += OBJECT_NUM_CONFLICTS (obj);
for (r = OBJECT_LIVE_RANGES (obj); r != NULL; r = r->next)
nr++;
}
}
fprintf (ira_dump_file, " regions=%d, blocks=%d, points=%d\n",
current_loops == NULL ? 1 : number_of_loops (cfun),
n_basic_blocks_for_fn (cfun), ira_max_point);
fprintf (ira_dump_file,
" allocnos=%d (big %d), copies=%d, conflicts=%d, ranges=%d\n",
ira_allocnos_num, nr_big, ira_copies_num, n, nr);
}
return loops_p;
}
/* Release the data created by function ira_build. */
void
ira_destroy (void)
{
finish_loop_tree_nodes ();
finish_prefs ();
finish_copies ();
finish_allocnos ();
finish_cost_vectors ();
ira_finish_allocno_live_ranges ();
}