mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-26 21:33:59 +08:00
5ac9118ef9
* bb-reorder.c (build_scope_forest): Initialize variable `curr_scope'. * calls.c (expand_call): Likewise for variables `save_pending_stack_adjust' and `save_stack_pointer_delta'. * i386.c (function_arg_advance, function_arg): Cast to avoid signed/unsigned warnings. * i386.h (MEMORY_MOVE_COST): Likewise. * ifcvt.c (cond_exec_process_if_block): Initialize variables `else_start' and `else_end'. * libgcc2.h (__eh_alloc, __eh_free): Prototype. * regrename.c (rr_replace_reg): Initialize variable `dest_subregno'. ch: * Makefile.in (EXPR_H): New dependency variable. (actions.o, expr.o): Use EXPR_H. (lang.o): Depend on RTL_H and EXPR_H. * lang.c: Include rtl.h and expr.h. (lang_get_alias_set): Mark parameter with ATTRIBUTE_UNUSED. cp: * decl2.c (compare_options): Don't needlessly cast away const-ness. f: * com.c (lang_get_alias_set): Mark parameter with ATTRIBUTE_UNUSED. java: * decl.c (create_primitive_vtable): Prototype. * jcf-write.c (generate_bytecode_insns): Initialize variable `saved_context'. * lang.c (lang_get_alias_set): Mark parameter with ATTRIBUTE_UNUSED. From-SVN: r34490
1128 lines
30 KiB
C
1128 lines
30 KiB
C
/* Register renaming for the GNU compiler.
|
||
Copyright (C) 2000 Free Software Foundation, Inc.
|
||
|
||
This file is part of GNU CC.
|
||
|
||
GNU CC 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 2, or (at your option)
|
||
any later version.
|
||
|
||
GNU CC 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 GNU CC; see the file COPYING. If not, write to
|
||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||
Boston, MA 02111-1307, USA. */
|
||
|
||
#include "config.h"
|
||
#include "system.h"
|
||
#include "tree.h"
|
||
#include "rtl.h"
|
||
#include "hard-reg-set.h"
|
||
#include "basic-block.h"
|
||
#include "insn-config.h"
|
||
#include "regs.h"
|
||
#include "flags.h"
|
||
#include "output.h"
|
||
#include "function.h"
|
||
#include "recog.h"
|
||
#include "resource.h"
|
||
|
||
static const char *const reg_class_names[] = REG_CLASS_NAMES;
|
||
|
||
/* ??? Consider a more sparse data structure? */
|
||
typedef struct def_uses
|
||
{
|
||
/* high bound of defs and uses */
|
||
int high_bound;
|
||
|
||
/* 1 if insn y defines a reg whose use crosses a call
|
||
y is the ordinal position of the insn within the block */
|
||
sbitmap require_call_save_reg;
|
||
|
||
/* REGNO x INSN y 1 if insn y sets reg x */
|
||
sbitmap *defs;
|
||
|
||
/* REGNO x INSN y The register class for this def */
|
||
enum reg_class *def_class;
|
||
|
||
/* REGNO x INSN y 1 if insn y uses reg x */
|
||
sbitmap *uses;
|
||
|
||
/* REGNO x INSN y The register class for this use */
|
||
enum reg_class *use_class;
|
||
}
|
||
def_uses;
|
||
|
||
#define DU_REG_CLASS(rc,r,high_bound,i) (rc[r * high_bound + i])
|
||
|
||
typedef struct ext_basic_blocks
|
||
{
|
||
/* n_basic_blocks x n_basic_blocks y 1 if bb y is in extended bb
|
||
having entry x */
|
||
sbitmap *basic_block;
|
||
|
||
/* n_basic_blocks x n_basic_blocks y 1 if bb y is an exit block */
|
||
sbitmap *exit;
|
||
}
|
||
ext_basic_blocks;
|
||
|
||
#define UID_RUID_HIGH_BOUND 64
|
||
#define DESTINATION 1
|
||
#define SOURCE 2
|
||
|
||
static void build_def_use PARAMS ((int, ext_basic_blocks *,
|
||
HARD_REG_SET *, def_uses *,
|
||
sbitmap *));
|
||
static int replace_reg_in_block PARAMS ((def_uses *, varray_type *,
|
||
int, rtx, unsigned int));
|
||
static int consider_def PARAMS ((rtx, int, def_uses *, int));
|
||
static int consider_available PARAMS ((rtx, int, HARD_REG_SET *,
|
||
int, def_uses *, int));
|
||
static rtx rr_replace_reg PARAMS ((rtx, rtx, rtx, int, rtx,
|
||
int *));
|
||
static int consider_use PARAMS ((rtx, int, int, int));
|
||
static int condmove_p PARAMS ((rtx));
|
||
static void dump_def_use_chain PARAMS ((HARD_REG_SET *, def_uses *,
|
||
varray_type *));
|
||
static void dump_ext_bb_info PARAMS ((int, ext_basic_blocks *));
|
||
static void find_ext_basic_blocks PARAMS ((ext_basic_blocks *));
|
||
static void find_one_ext_basic_block PARAMS ((int, basic_block, sbitmap *,
|
||
ext_basic_blocks *));
|
||
static enum reg_class get_reg_class PARAMS ((rtx, rtx, int,
|
||
enum reg_class));
|
||
static rtx regno_first_use_in PARAMS ((unsigned int, rtx));
|
||
|
||
void
|
||
regrename_optimize ()
|
||
{
|
||
int b, eb, i, inum, r, rc, replace_ok;
|
||
rtx insn;
|
||
def_uses du;
|
||
ext_basic_blocks ebb;
|
||
|
||
/* Registers used in a given class */
|
||
HARD_REG_SET class_regs;
|
||
|
||
/* Registers available for use as renaming registers */
|
||
HARD_REG_SET avail_regs;
|
||
|
||
/* Registers used in the block */
|
||
HARD_REG_SET regs_used;
|
||
|
||
/* Registers which have been used as renaming registers */
|
||
HARD_REG_SET renamed_regs;
|
||
|
||
HARD_REG_SET global_live_at_end, global_live_at_start;
|
||
|
||
HARD_REG_SET null_bitmap, tmp_bitmap;
|
||
|
||
/* 1 if insn y sets a register which is live at the end of the block */
|
||
sbitmap defs_live_exit;
|
||
|
||
/* Mapping from insn y (ordinal position in block) to INSN_UID */
|
||
varray_type uid_ruid;
|
||
|
||
/* Mapping from insn y (ordinal position in block) to block id */
|
||
varray_type uid_rbid;
|
||
|
||
/* Ordinal position in block of defining insn */
|
||
int *def_idx;
|
||
|
||
VARRAY_RTX_INIT (uid_ruid, UID_RUID_HIGH_BOUND + 1, "uid_ruid");
|
||
VARRAY_LONG_INIT (uid_rbid, UID_RUID_HIGH_BOUND + 1, "uid_rbid");
|
||
|
||
ebb.basic_block
|
||
= sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
|
||
sbitmap_vector_zero (ebb.basic_block, n_basic_blocks);
|
||
ebb.exit
|
||
= sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
|
||
sbitmap_vector_zero (ebb.exit, n_basic_blocks);
|
||
|
||
find_ext_basic_blocks (&ebb);
|
||
|
||
du.def_class = du.use_class = 0;
|
||
|
||
/* Build uid_ruid and uid_rbid for this extended basic block */
|
||
for (b = 0; b < n_basic_blocks; b++)
|
||
if (TEST_BIT (ebb.basic_block[b], b))
|
||
{
|
||
for (eb = du.high_bound = 0; eb < n_basic_blocks; eb++)
|
||
if (TEST_BIT (ebb.basic_block[b], eb))
|
||
{
|
||
basic_block bb = BASIC_BLOCK (eb);
|
||
|
||
/* Calculate high bound for uid_ruid and allocate if necessary */
|
||
for (insn = bb->head;
|
||
insn != NEXT_INSN (bb->end);
|
||
du.high_bound++, insn = NEXT_INSN (insn))
|
||
{
|
||
int uid_ruid_high_bound = VARRAY_SIZE (uid_ruid);
|
||
|
||
if (du.high_bound + 4 >= uid_ruid_high_bound)
|
||
{
|
||
VARRAY_GROW (uid_ruid, uid_ruid_high_bound * 2);
|
||
VARRAY_GROW (uid_rbid, uid_ruid_high_bound * 2);
|
||
}
|
||
|
||
VARRAY_RTX (uid_ruid, du.high_bound) = insn;
|
||
VARRAY_LONG (uid_rbid, du.high_bound) = eb;
|
||
}
|
||
}
|
||
|
||
CLEAR_HARD_REG_SET (null_bitmap);
|
||
CLEAR_HARD_REG_SET (class_regs);
|
||
CLEAR_HARD_REG_SET (regs_used);
|
||
CLEAR_HARD_REG_SET (avail_regs);
|
||
CLEAR_HARD_REG_SET (tmp_bitmap);
|
||
CLEAR_HARD_REG_SET (renamed_regs);
|
||
|
||
du.defs
|
||
= sbitmap_vector_alloc (FIRST_PSEUDO_REGISTER, du.high_bound + 1);
|
||
sbitmap_vector_zero (du.defs, FIRST_PSEUDO_REGISTER);
|
||
du.uses
|
||
= sbitmap_vector_alloc (FIRST_PSEUDO_REGISTER, du.high_bound + 1);
|
||
sbitmap_vector_zero (du.uses, FIRST_PSEUDO_REGISTER);
|
||
du.require_call_save_reg = sbitmap_alloc (du.high_bound + 1);
|
||
sbitmap_zero (du.require_call_save_reg);
|
||
defs_live_exit = sbitmap_alloc (du.high_bound + 1);
|
||
sbitmap_zero (defs_live_exit);
|
||
|
||
du.def_class
|
||
= xrealloc (du.def_class,
|
||
(sizeof (enum reg_class) * FIRST_PSEUDO_REGISTER
|
||
* du.high_bound));
|
||
|
||
du.use_class
|
||
= xrealloc (du.use_class,
|
||
(sizeof (enum reg_class) * FIRST_PSEUDO_REGISTER
|
||
* du.high_bound));
|
||
|
||
build_def_use (b, &ebb, ®s_used, &du, &defs_live_exit);
|
||
|
||
if (rtl_dump_file)
|
||
{
|
||
dump_ext_bb_info (b, &ebb);
|
||
dump_def_use_chain (&global_live_at_end, &du, &uid_ruid);
|
||
}
|
||
|
||
/* Available registers are not: used in the block, live at the start,
|
||
live at the end, a register we've renamed to. */
|
||
/* ??? The current algorithm is pessimistic for extended basic blocks
|
||
as it just treats them as a big basic block. */
|
||
|
||
COPY_HARD_REG_SET (tmp_bitmap, regs_used);
|
||
REG_SET_TO_HARD_REG_SET (global_live_at_start,
|
||
BASIC_BLOCK (b)->global_live_at_start);
|
||
IOR_HARD_REG_SET (tmp_bitmap, global_live_at_start);
|
||
for (eb = 0; eb < n_basic_blocks; eb++)
|
||
if (TEST_BIT (ebb.basic_block[b], eb))
|
||
{
|
||
basic_block bb = BASIC_BLOCK (eb);
|
||
|
||
REG_SET_TO_HARD_REG_SET (global_live_at_end,
|
||
bb->global_live_at_end);
|
||
IOR_HARD_REG_SET (tmp_bitmap, global_live_at_end);
|
||
}
|
||
|
||
def_idx = xcalloc (du.high_bound, sizeof (int));
|
||
|
||
/* Only consider registers in this extended block and in this class
|
||
that are defined more than once. Replace them if permissible. */
|
||
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
|
||
{
|
||
int avail_reg, ar_idx, def, def_cnt = 0, use_idx, call_idx;
|
||
|
||
if (!TEST_HARD_REG_BIT (regs_used, r)
|
||
|| fixed_regs[r]
|
||
|| r == FRAME_POINTER_REGNUM)
|
||
continue;
|
||
|
||
/* Find def_idx[N] where hbound of N is the number of
|
||
definitions of this register in this block. and def_idx
|
||
is the ordinal position of this insn in the block. */
|
||
for (i = 0, def_idx[def_cnt] = 0; i < du.high_bound; i++)
|
||
if (TEST_BIT (du.defs[r], i)
|
||
&& consider_def (VARRAY_RTX (uid_ruid, i), r, &du, i))
|
||
{
|
||
int first_use = 1;
|
||
def_idx[def_cnt] = i;
|
||
|
||
/* Only consider definitions that have a use. */
|
||
for (use_idx = i + 1; use_idx < du.high_bound; use_idx++)
|
||
{
|
||
if (TEST_BIT (du.uses[r], use_idx))
|
||
{
|
||
if (consider_use (VARRAY_RTX (uid_ruid, use_idx), r,
|
||
VARRAY_LONG (uid_rbid, i),
|
||
VARRAY_LONG (uid_rbid, use_idx)))
|
||
{
|
||
if (first_use)
|
||
{
|
||
first_use = 0;
|
||
def_cnt++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Don't consider def if we don't want this
|
||
use. */
|
||
if (!first_use)
|
||
def_cnt--;
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (TEST_BIT (du.defs[r], use_idx))
|
||
break;
|
||
}
|
||
|
||
/* Scan until the next def to avoid renaming
|
||
parameter registers. */
|
||
/* ??? consider using CALL_INSN_FUNCTION_USAGE */
|
||
for (call_idx = i; call_idx <= use_idx; call_idx++)
|
||
if (VARRAY_RTX (uid_ruid, call_idx)
|
||
&& (GET_CODE (VARRAY_RTX (uid_ruid, call_idx))
|
||
== CALL_INSN))
|
||
SET_BIT (du.require_call_save_reg, i);
|
||
}
|
||
|
||
if (def_cnt < 2)
|
||
continue;
|
||
|
||
/* We have more than one def so rename until we exhaust
|
||
renaming registers. */
|
||
/* ??? Should we continue renaming round robin when we exhaust
|
||
renaming registers? */
|
||
for (def = 0; def < def_cnt - 1; def++)
|
||
{
|
||
if (!TEST_BIT (defs_live_exit, def_idx[def])
|
||
&& (GET_RTX_CLASS
|
||
(GET_CODE (VARRAY_RTX (uid_ruid,
|
||
def_idx[def]))) == 'i'))
|
||
{
|
||
rtx reg_use
|
||
= regno_first_use_in
|
||
(r, PATTERN (VARRAY_RTX (uid_ruid, def_idx[def])));
|
||
|
||
if (!reg_use)
|
||
break;
|
||
#ifdef STACK_REGS
|
||
/* Don't bother with stacked float registers */
|
||
if (GET_MODE_CLASS (GET_MODE (reg_use)) == MODE_FLOAT)
|
||
break;
|
||
#endif
|
||
rc = (int) DU_REG_CLASS (du.def_class,
|
||
r, du.high_bound, def_idx[def]);
|
||
COPY_HARD_REG_SET (avail_regs,
|
||
reg_class_contents[(enum reg_class) rc]);
|
||
AND_COMPL_HARD_REG_SET (avail_regs, tmp_bitmap);
|
||
AND_COMPL_HARD_REG_SET (avail_regs, renamed_regs);
|
||
|
||
/* No available registers in this class */
|
||
GO_IF_HARD_REG_EQUAL (avail_regs, null_bitmap,
|
||
no_available_regs);
|
||
|
||
for (ar_idx = 0; ar_idx < FIRST_PSEUDO_REGISTER
|
||
&& TEST_HARD_REG_BIT (avail_regs, ar_idx); ar_idx++)
|
||
;
|
||
|
||
if (ar_idx == FIRST_PSEUDO_REGISTER)
|
||
goto no_available_regs;
|
||
|
||
/* Only try register renaming if there is an available
|
||
register in this class. */
|
||
for (ar_idx = 0; ar_idx < FIRST_PSEUDO_REGISTER; ar_idx++)
|
||
{
|
||
#ifdef REG_ALLOC_ORDER
|
||
avail_reg = reg_alloc_order[ar_idx];
|
||
#else
|
||
avail_reg = ar_idx;
|
||
#endif
|
||
if (consider_available (reg_use, avail_reg,
|
||
&avail_regs, rc, &du,
|
||
def_idx[def]))
|
||
break;
|
||
}
|
||
|
||
if (ar_idx == FIRST_PSEUDO_REGISTER)
|
||
{
|
||
if (rtl_dump_file)
|
||
{
|
||
fprintf (rtl_dump_file,
|
||
"Register %s in class %s",
|
||
reg_names[r], reg_class_names[rc]);
|
||
fprintf (rtl_dump_file,
|
||
" in insn %d",
|
||
INSN_UID (VARRAY_RTX (uid_ruid,
|
||
def_idx[def])));
|
||
|
||
if (TEST_BIT (du.require_call_save_reg,
|
||
def_idx[def]))
|
||
fprintf (rtl_dump_file, " crosses a call");
|
||
|
||
fprintf (rtl_dump_file,
|
||
". No available registers\n");
|
||
}
|
||
|
||
goto try_next_def;
|
||
}
|
||
|
||
SET_HARD_REG_BIT (renamed_regs, avail_reg);
|
||
CLEAR_HARD_REG_BIT (avail_regs, avail_reg);
|
||
|
||
/* Replace in destination. Replace in source for
|
||
remainder of block until new register is defined
|
||
again */
|
||
replace_ok
|
||
= replace_reg_in_block (&du, &uid_ruid, def_idx[def],
|
||
reg_use, avail_reg);
|
||
|
||
/* Replace failed, so restore previous register */
|
||
if (!replace_ok)
|
||
{
|
||
replace_reg_in_block (&du, &uid_ruid, def_idx[def],
|
||
gen_rtx_REG (GET_MODE (reg_use),
|
||
avail_reg),
|
||
REGNO (reg_use));
|
||
|
||
if (rtl_dump_file)
|
||
{
|
||
fprintf (rtl_dump_file,
|
||
"Register %s in class %s Renaming as %s ",
|
||
reg_names[r], reg_class_names[rc],
|
||
reg_names[avail_reg]);
|
||
fprintf (rtl_dump_file,
|
||
"would not satisfy constraints\n");
|
||
}
|
||
}
|
||
|
||
else if (rtl_dump_file)
|
||
{
|
||
fprintf (rtl_dump_file,
|
||
"Register %s in class %s Renamed as %s ",
|
||
reg_names[r], reg_class_names[rc],
|
||
reg_names[avail_reg]);
|
||
fprintf (rtl_dump_file, "at insn %d\n",
|
||
INSN_UID (VARRAY_RTX (uid_ruid,
|
||
def_idx[def])));
|
||
}
|
||
}
|
||
|
||
try_next_def:
|
||
continue;
|
||
}
|
||
|
||
sbitmap_zero (du.defs[r]);
|
||
|
||
no_available_regs:
|
||
continue;
|
||
}
|
||
|
||
free (def_idx);
|
||
sbitmap_vector_free (du.defs);
|
||
sbitmap_vector_free (du.uses);
|
||
sbitmap_free (du.require_call_save_reg);
|
||
sbitmap_free (defs_live_exit);
|
||
CLEAR_HARD_REG_SET (regs_used);
|
||
CLEAR_HARD_REG_SET (renamed_regs);
|
||
|
||
for (inum = 0; inum < (int) VARRAY_SIZE (uid_ruid); inum++)
|
||
VARRAY_RTX (uid_ruid, inum) = (rtx) 0;
|
||
}
|
||
|
||
sbitmap_vector_free (ebb.basic_block);
|
||
sbitmap_vector_free (ebb.exit);
|
||
}
|
||
|
||
/* Build def/use chain DU for extended basic block EBB having root B.
|
||
Also determine which regs are used, REGS_USED, and which insns define
|
||
a live at exit def, DEFS_LIVE_EXIT */
|
||
|
||
static void
|
||
build_def_use (b, ebb, regs_used, du, defs_live_exit)
|
||
int b;
|
||
ext_basic_blocks *ebb;
|
||
HARD_REG_SET *regs_used;
|
||
def_uses *du;
|
||
sbitmap *defs_live_exit;
|
||
{
|
||
rtx insn;
|
||
int eb, inum;
|
||
unsigned int r;
|
||
|
||
inum = 0;
|
||
for (eb = 0; eb < n_basic_blocks; eb++)
|
||
{
|
||
basic_block bb = BASIC_BLOCK (eb);
|
||
|
||
if (!TEST_BIT (ebb->basic_block[b], eb))
|
||
continue;
|
||
|
||
for (insn = bb->head;
|
||
insn != NEXT_INSN (bb->end);
|
||
inum++, insn = NEXT_INSN (insn))
|
||
{
|
||
struct resources insn_res;
|
||
struct resources insn_sets;
|
||
|
||
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
|
||
continue;
|
||
|
||
CLEAR_RESOURCE (&insn_sets);
|
||
mark_set_resources (insn, &insn_sets, 0, MARK_DEST);
|
||
|
||
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
|
||
{
|
||
if (!TEST_HARD_REG_BIT (insn_sets.regs, r))
|
||
continue;
|
||
|
||
SET_HARD_REG_BIT (*regs_used, r);
|
||
if (REGNO_REG_SET_P (bb->global_live_at_end, r))
|
||
SET_BIT (*defs_live_exit, inum);
|
||
|
||
if (!insn_sets.memory)
|
||
SET_BIT (du->defs[r], inum);
|
||
|
||
DU_REG_CLASS (du->def_class, r, du->high_bound, inum)
|
||
= get_reg_class (insn, regno_first_use_in (r, PATTERN (insn)),
|
||
DESTINATION, NO_REGS);
|
||
}
|
||
|
||
CLEAR_RESOURCE (&insn_res);
|
||
mark_referenced_resources (insn, &insn_res, 0);
|
||
|
||
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
|
||
{
|
||
if (!TEST_HARD_REG_BIT (insn_res.regs, r))
|
||
continue;
|
||
|
||
SET_HARD_REG_BIT (*regs_used, r);
|
||
SET_BIT (du->uses[r], inum);
|
||
DU_REG_CLASS (du->use_class, r, du->high_bound, inum)
|
||
= get_reg_class (insn, regno_use_in (r, PATTERN (insn)),
|
||
SOURCE, NO_REGS);
|
||
}
|
||
}
|
||
}
|
||
|
||
free_resource_info ();
|
||
}
|
||
|
||
/* Return nonzero if regno AVAIL_REG can replace REG_DEF for insns in UID_RUID
|
||
starting at insn DEF in def/use chain DU. */
|
||
|
||
static int
|
||
replace_reg_in_block (du, uid_ruid, def, reg_def, avail_reg)
|
||
def_uses *du;
|
||
varray_type *uid_ruid;
|
||
int def;
|
||
rtx reg_def;
|
||
unsigned int avail_reg;
|
||
{
|
||
int du_idx, status = 1;
|
||
int last_replaced_insn;
|
||
unsigned int r = REGNO (reg_def);
|
||
rtx death_note;
|
||
rtx reg_notes;
|
||
rtx reg_use;
|
||
rtx new_reg = gen_rtx_REG (GET_MODE (reg_def), avail_reg);
|
||
|
||
rr_replace_reg (PATTERN (VARRAY_RTX (*uid_ruid, def)), reg_def, new_reg,
|
||
DESTINATION, VARRAY_RTX (*uid_ruid, def), &status);
|
||
|
||
if (!status)
|
||
return status;
|
||
|
||
death_note = 0;
|
||
/* This typically happens if a constraint check failed and the register
|
||
changes are being reversed. */
|
||
for (reg_notes = REG_NOTES (VARRAY_RTX (*uid_ruid, def));
|
||
reg_notes; reg_notes = XEXP (reg_notes, 1))
|
||
{
|
||
if (REG_NOTE_KIND (reg_notes) == REG_DEAD
|
||
&& REGNO (XEXP (reg_notes, 0)) == avail_reg)
|
||
death_note = reg_notes;
|
||
}
|
||
|
||
if (death_note)
|
||
remove_note (VARRAY_RTX (*uid_ruid, def), death_note);
|
||
|
||
/* The old destination is now dead if it is also a source. */
|
||
if (regno_use_in (r, PATTERN (VARRAY_RTX (*uid_ruid, def))))
|
||
REG_NOTES (VARRAY_RTX (*uid_ruid, def))
|
||
= gen_rtx_EXPR_LIST (REG_DEAD, reg_def,
|
||
REG_NOTES (VARRAY_RTX (*uid_ruid,
|
||
def)));
|
||
|
||
last_replaced_insn = 0;
|
||
|
||
/* Now replace in the uses. */
|
||
for (du_idx = def + 1; du_idx < du->high_bound; du_idx++)
|
||
{
|
||
if (GET_RTX_CLASS (GET_CODE (VARRAY_RTX (*uid_ruid, du_idx))) != 'i')
|
||
continue;
|
||
|
||
reg_use = regno_use_in (r, PATTERN (VARRAY_RTX (*uid_ruid, du_idx)));
|
||
|
||
if (reg_use && TEST_BIT (du->uses[r], du_idx))
|
||
{
|
||
new_reg = gen_rtx_REG (GET_MODE (reg_use), avail_reg);
|
||
|
||
rr_replace_reg (PATTERN (VARRAY_RTX (*uid_ruid, du_idx)), reg_use,
|
||
new_reg, SOURCE, VARRAY_RTX (*uid_ruid, du_idx),
|
||
&status);
|
||
death_note = find_reg_note (VARRAY_RTX (*uid_ruid, du_idx),
|
||
REG_DEAD, reg_use);
|
||
if (death_note)
|
||
{
|
||
REG_NOTES (VARRAY_RTX (*uid_ruid, du_idx))
|
||
= gen_rtx_EXPR_LIST (REG_DEAD, new_reg,
|
||
REG_NOTES (VARRAY_RTX (*uid_ruid,
|
||
du_idx)));
|
||
remove_note (VARRAY_RTX (*uid_ruid, du_idx),
|
||
find_reg_note (VARRAY_RTX (*uid_ruid, du_idx),
|
||
REG_DEAD, reg_use));
|
||
}
|
||
}
|
||
|
||
/* This insn may contain shared rtl replaced in the previous iteration.
|
||
Treat this equivalent to the rr_replace_reg case. */
|
||
if (TEST_BIT (du->uses[r], du_idx))
|
||
{
|
||
last_replaced_insn = du_idx;
|
||
|
||
SET_BIT (du->uses[avail_reg], du_idx);
|
||
RESET_BIT (du->uses[r], du_idx);
|
||
if (!status)
|
||
return status;
|
||
}
|
||
|
||
if (TEST_BIT (du->defs[r], du_idx))
|
||
break;
|
||
}
|
||
|
||
/* Add REG_DEAD note for replaced register at last use. */
|
||
|
||
if (last_replaced_insn)
|
||
{
|
||
new_reg = regno_use_in (avail_reg,
|
||
PATTERN (VARRAY_RTX (*uid_ruid,
|
||
last_replaced_insn)));
|
||
if (new_reg
|
||
&& ! find_reg_note (VARRAY_RTX (*uid_ruid, last_replaced_insn),
|
||
REG_DEAD, new_reg))
|
||
{
|
||
REG_NOTES (VARRAY_RTX (*uid_ruid, last_replaced_insn))
|
||
= gen_rtx_EXPR_LIST (REG_DEAD, new_reg,
|
||
REG_NOTES (VARRAY_RTX (*uid_ruid,
|
||
last_replaced_insn)));
|
||
remove_note (VARRAY_RTX (*uid_ruid, last_replaced_insn),
|
||
find_reg_note (VARRAY_RTX (*uid_ruid, last_replaced_insn),
|
||
REG_DEAD, reg_use));
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
/* Try to replace REG_USE in X with REG_SUB if INSN has a REPLACE_TYPE.
|
||
STATUS is zero if the resulting pattern is not valid. */
|
||
|
||
static rtx
|
||
rr_replace_reg (x, reg_use, reg_sub, replace_type, insn, status)
|
||
rtx x;
|
||
rtx reg_use;
|
||
rtx reg_sub;
|
||
int replace_type;
|
||
rtx insn;
|
||
int *status;
|
||
{
|
||
enum rtx_code code;
|
||
int i;
|
||
const char *fmt;
|
||
int n;
|
||
|
||
if (x == 0)
|
||
return x;
|
||
|
||
code = GET_CODE (x);
|
||
switch (code)
|
||
{
|
||
case REG:
|
||
if (REGNO (x) == REGNO (reg_use))
|
||
{
|
||
if (GET_MODE (x) == GET_MODE (reg_use))
|
||
return reg_sub;
|
||
else
|
||
return gen_rtx_REG (GET_MODE (x), REGNO (reg_sub));
|
||
}
|
||
|
||
return x;
|
||
|
||
case SET:
|
||
if (replace_type == DESTINATION)
|
||
SET_DEST (x) = rr_replace_reg (SET_DEST (x), reg_use, reg_sub,
|
||
replace_type, insn, status);
|
||
else if (replace_type == SOURCE)
|
||
{
|
||
unsigned int dest_subregno = 0;
|
||
int had_subreg = GET_CODE (SET_DEST (x)) == SUBREG;
|
||
|
||
if (had_subreg)
|
||
dest_subregno = REGNO (XEXP (SET_DEST (x), 0));
|
||
|
||
SET_SRC (x) = rr_replace_reg (SET_SRC (x), reg_use, reg_sub,
|
||
replace_type, insn, status);
|
||
|
||
/* If the replacement register is not part of the source
|
||
then it may be part of a source mem operand. */
|
||
if (GET_CODE (SET_DEST (x)) == MEM
|
||
|| GET_CODE (SET_DEST (x)) == ZERO_EXTRACT
|
||
|| GET_CODE (SET_DEST (x)) == SIGN_EXTRACT
|
||
|| GET_CODE (SET_DEST (x)) == STRICT_LOW_PART)
|
||
SET_DEST (x) = rr_replace_reg (SET_DEST (x), reg_use, reg_sub,
|
||
replace_type, insn, status);
|
||
/* Shared rtl sanity check. */
|
||
if (had_subreg && dest_subregno != REGNO (XEXP (SET_DEST (x), 0)))
|
||
{
|
||
*status = 0;
|
||
return x;
|
||
}
|
||
}
|
||
|
||
n = recog_memoized (insn);
|
||
if (n >= 0)
|
||
{
|
||
int id;
|
||
|
||
extract_insn (insn);
|
||
|
||
/* Any MATCH_DUP's which are REGs must still match */
|
||
for (id = insn_data[n].n_dups - 1; id >= 0; id--)
|
||
{
|
||
int opno = recog_data.dup_num[id];
|
||
|
||
if (GET_CODE (*recog_data.dup_loc[id]) == REG
|
||
&& GET_CODE (*recog_data.operand_loc[opno]) == REG
|
||
&& (REGNO (*recog_data.dup_loc[id])
|
||
!= REGNO (*recog_data.operand_loc[opno])))
|
||
*status = 0;
|
||
}
|
||
|
||
if (!constrain_operands (1))
|
||
{
|
||
*status = 0;
|
||
validate_replace_rtx (reg_sub, reg_use, insn);
|
||
}
|
||
}
|
||
else
|
||
*status = 0;
|
||
|
||
return x;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
fmt = GET_RTX_FORMAT (code);
|
||
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
||
{
|
||
if (fmt[i] == 'e')
|
||
XEXP (x, i) = rr_replace_reg (XEXP (x, i), reg_use, reg_sub,
|
||
replace_type, insn, status);
|
||
if (fmt[i] == 'E')
|
||
{
|
||
register int xv;
|
||
|
||
for (xv = 0; xv < XVECLEN (x, i); xv++)
|
||
{
|
||
XVECEXP (x, i, xv) = rr_replace_reg (XVECEXP (x, i, xv), reg_use,
|
||
reg_sub, replace_type, insn,
|
||
status);
|
||
n = recog_memoized (insn);
|
||
if (n >= 0)
|
||
{
|
||
extract_insn (insn);
|
||
if (!constrain_operands (1))
|
||
{
|
||
*status = 0;
|
||
validate_replace_rtx (reg_sub, reg_use, insn);
|
||
}
|
||
}
|
||
else
|
||
*status = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
return x;
|
||
}
|
||
|
||
/* Can REGNO in INSN be considered for renaming, given def INUM in d/u
|
||
chain DU? */
|
||
|
||
static int
|
||
consider_def (insn, regno, du, inum)
|
||
rtx insn;
|
||
int regno;
|
||
def_uses *du ATTRIBUTE_UNUSED;
|
||
int inum ATTRIBUTE_UNUSED;
|
||
{
|
||
/* Don't rename windowed registers across a call */
|
||
#ifdef INCOMING_REGNO
|
||
if (TEST_BIT (du->require_call_save_reg, inum)
|
||
&& INCOMING_REGNO (regno) != regno)
|
||
return 0;
|
||
#endif
|
||
|
||
/* Don't consider conditional moves. Predicate architectures may
|
||
use two complementary conditional moves and the regno shouldn't change */
|
||
if (condmove_p (insn))
|
||
return 0;
|
||
|
||
/* Don't rename call used registers across a call */
|
||
if (!(GET_CODE (insn) == CALL_INSN
|
||
&& TEST_HARD_REG_BIT (call_used_reg_set, regno)))
|
||
return 1;
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
/* Can the use of REGNO in INSN of block USE_BLOCK be considered for renaming
|
||
for a def in def_block? */
|
||
|
||
static int
|
||
consider_use (insn, regno, def_block, use_block)
|
||
rtx insn;
|
||
int regno;
|
||
int def_block;
|
||
int use_block;
|
||
{
|
||
rtx reg_use;
|
||
edge e;
|
||
basic_block ub = BASIC_BLOCK (use_block);
|
||
|
||
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
|
||
return 0;
|
||
|
||
/* If a use's basic block is different than the def's basic block,
|
||
then insure another predecessor does not also define this register */
|
||
if (def_block != use_block)
|
||
for (e = ub->pred; e; e = e->pred_next)
|
||
if (e->src->index != def_block
|
||
&& e->src->index != -1
|
||
&& REGNO_REG_SET_P (BASIC_BLOCK (e->src->index)->global_live_at_end,
|
||
regno))
|
||
return 0;
|
||
|
||
/* Don't consider conditional moves. Predicate architectures may
|
||
use two complementary conditional moves and the regno shouldn't change */
|
||
|
||
if (condmove_p (insn))
|
||
return 0;
|
||
|
||
reg_use = regno_first_use_in (regno, PATTERN (insn));
|
||
if (reg_use)
|
||
{
|
||
/* Don't consider multi-reg values. */
|
||
if (HARD_REGNO_NREGS (regno, GET_MODE (reg_use)) != 1
|
||
&& GET_MODE (reg_use) != CCmode)
|
||
return 0;
|
||
|
||
/* Don't consider register if the only use is in a USE */
|
||
return ! reg_mentioned_p (gen_rtx_USE (VOIDmode, reg_use),
|
||
PATTERN (insn));
|
||
}
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
/* Can REG_USE be replaced by regno AVAIL_REG if it is in AVAIL_REGS
|
||
and it is in regclass RC, given insn INUM of def/use chain DU? */
|
||
|
||
static int
|
||
consider_available (reg_use, avail_reg, avail_regs, rc, du, inum)
|
||
rtx reg_use;
|
||
int avail_reg;
|
||
HARD_REG_SET *avail_regs;
|
||
int rc;
|
||
def_uses *du;
|
||
int inum;
|
||
{
|
||
if (!TEST_HARD_REG_BIT (*avail_regs, avail_reg))
|
||
return 0;
|
||
|
||
if (fixed_regs[avail_reg])
|
||
return 0;
|
||
|
||
#ifdef HARD_REGNO_RENAME_OK
|
||
if (!HARD_REGNO_RENAME_OK (REGNO (reg_use), avail_reg))
|
||
return 0;
|
||
#endif
|
||
|
||
/* Don't consider windowed leaf registers which will be renamed by
|
||
leaf_renumber_regs */
|
||
#ifdef LEAF_REG_REMAP
|
||
if (current_function_uses_only_leaf_regs)
|
||
if (LEAF_REG_REMAP (avail_reg) < 0)
|
||
return 0;
|
||
#endif
|
||
|
||
/* A register is considered available if it is available at the beginning of
|
||
the basic block. We may want to refine this to when a register becomes
|
||
available within the block. We don't consider multi-reg values. */
|
||
/* ??? Consider a representation that would allow multi-reg support? */
|
||
if (!TEST_HARD_REG_BIT (reg_class_contents[(enum reg_class) rc], avail_reg)
|
||
|| !HARD_REGNO_MODE_OK (avail_reg, GET_MODE (reg_use))
|
||
|| (HARD_REGNO_NREGS (avail_reg, GET_MODE (reg_use)) != 1
|
||
&& GET_MODE (reg_use) != CCmode)
|
||
|| (call_fixed_regs[avail_reg]
|
||
#ifdef HARD_REGNO_RENAME_OK
|
||
&& !HARD_REGNO_RENAME_OK (REGNO (reg_use), avail_reg)
|
||
#endif
|
||
)
|
||
|| (TEST_BIT (du->require_call_save_reg, inum)
|
||
&& (call_used_regs[avail_reg] || call_used_regs[REGNO (reg_use)])))
|
||
return 0;
|
||
|
||
/* If register is a callee-saved register it must be saved in the frame.
|
||
call saved registers can not be added to regs_ever_live after reload,
|
||
as it would invalidate most elimination offsets */
|
||
return regs_ever_live[avail_reg] || call_used_regs[avail_reg];
|
||
}
|
||
|
||
/* Return 1 if INSN is a conditional move */
|
||
|
||
static int
|
||
condmove_p (insn)
|
||
rtx insn;
|
||
{
|
||
return (GET_CODE (insn) == INSN
|
||
&& GET_CODE (PATTERN (insn)) == SET
|
||
&& GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE);
|
||
}
|
||
|
||
/* Searches X for the first reference to REGNO, returning the rtx of the
|
||
reference found if any. Otherwise, returns NULL_RTX. */
|
||
|
||
static rtx
|
||
regno_first_use_in (regno, x)
|
||
unsigned int regno;
|
||
rtx x;
|
||
{
|
||
register const char *fmt;
|
||
int i, j;
|
||
rtx tem;
|
||
|
||
if (GET_CODE (x) == REG && REGNO (x) == regno)
|
||
return x;
|
||
|
||
fmt = GET_RTX_FORMAT (GET_CODE (x));
|
||
for (i = 0; i <= GET_RTX_LENGTH (GET_CODE (x)) - 1; i++)
|
||
{
|
||
if (fmt[i] == 'e')
|
||
{
|
||
if ((tem = regno_first_use_in (regno, XEXP (x, i))))
|
||
return tem;
|
||
}
|
||
|
||
else if (fmt[i] == 'E')
|
||
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
|
||
if ((tem = regno_first_use_in (regno, XVECEXP (x, i, j))))
|
||
return tem;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Dump def/use chain DU to RTL_DUMP_FILE, given insns in UID_RUID and
|
||
which regs are live at end, GLOBAL_LIVE_AT_END */
|
||
|
||
static void
|
||
dump_def_use_chain (global_live_at_end, du, uid_ruid)
|
||
HARD_REG_SET *global_live_at_end;
|
||
def_uses *du;
|
||
varray_type *uid_ruid;
|
||
{
|
||
unsigned int r;
|
||
int inum;
|
||
|
||
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
|
||
{
|
||
int set = 0;
|
||
|
||
for (inum = 0; inum <= du->high_bound; inum++)
|
||
{
|
||
rtx insn = VARRAY_RTX (*uid_ruid, inum);
|
||
#if 0
|
||
if (!insn
|
||
|| GET_RTX_CLASS (GET_CODE
|
||
(insn)) != 'i')
|
||
continue;
|
||
|
||
reg_use = regno_first_use_in (r, PATTERN (insn));
|
||
if (!reg_use)
|
||
continue;
|
||
#endif
|
||
if (!set && (TEST_BIT (du->defs[r], inum)
|
||
|| TEST_BIT (du->uses[r], inum)))
|
||
{
|
||
fprintf (rtl_dump_file, "Register %s: ", reg_names[r]);
|
||
if (fixed_regs[r])
|
||
fprintf (rtl_dump_file, "Fixed ");
|
||
else if (call_fixed_regs[r])
|
||
fprintf (rtl_dump_file, "Call Fixed ");
|
||
if (TEST_HARD_REG_BIT (*global_live_at_end, r))
|
||
fprintf (rtl_dump_file, "Live at Exit ");
|
||
set = 1;
|
||
}
|
||
|
||
if (TEST_BIT (du->defs[r], inum))
|
||
fprintf (rtl_dump_file, "=%d ", INSN_UID (insn));
|
||
if (TEST_BIT (du->uses[r], inum))
|
||
fprintf (rtl_dump_file, "%d ", INSN_UID (insn));
|
||
}
|
||
|
||
if (set)
|
||
fprintf (rtl_dump_file, "\n");
|
||
}
|
||
}
|
||
|
||
/* Dump info for extended basic block EBB having root EB */
|
||
|
||
static void
|
||
dump_ext_bb_info (eb, ebb)
|
||
int eb;
|
||
ext_basic_blocks *ebb;
|
||
{
|
||
int b;
|
||
int have_ebb = 0;
|
||
|
||
for (b = 0; b < n_basic_blocks; b++)
|
||
{
|
||
if (TEST_BIT (ebb->basic_block[eb], b))
|
||
{
|
||
if (!have_ebb)
|
||
{
|
||
#ifndef RENAME_EXTENDED_BLOCKS
|
||
fprintf (rtl_dump_file, "\nBasic block %d: ", b);
|
||
#else
|
||
fprintf (rtl_dump_file, "\nExtended basic block %d: ", b);
|
||
#endif
|
||
have_ebb = 1;
|
||
}
|
||
fprintf (rtl_dump_file, "%d ", b);
|
||
}
|
||
|
||
if (TEST_BIT (ebb->exit[eb], b))
|
||
fprintf (rtl_dump_file, "(exit) ");
|
||
}
|
||
|
||
if (have_ebb)
|
||
fprintf (rtl_dump_file, "\n");
|
||
}
|
||
|
||
/* Initialize EBB with extended basic block info if RENAME_EXTENDED_BLOCKS is
|
||
defined. Otherwise just use basic blocks */
|
||
|
||
static void
|
||
find_ext_basic_blocks (ebb)
|
||
ext_basic_blocks *ebb;
|
||
{
|
||
sbitmap bb_processed;
|
||
int b;
|
||
|
||
bb_processed = sbitmap_alloc (n_basic_blocks);
|
||
sbitmap_zero (bb_processed);
|
||
|
||
#ifndef RENAME_EXTENDED_BLOCKS
|
||
for (b = 0; b < n_basic_blocks; b++)
|
||
{
|
||
basic_block bb = BASIC_BLOCK (b);
|
||
SET_BIT (ebb->basic_block[bb->index], bb->index);
|
||
}
|
||
#else
|
||
for (b = 0; b < n_basic_blocks; b++)
|
||
{
|
||
|
||
basic_block bb = BASIC_BLOCK (b);
|
||
if (!TEST_BIT (bb_processed, b))
|
||
{
|
||
find_one_ext_basic_block (bb->index, bb, &bb_processed, ebb);
|
||
}
|
||
}
|
||
#endif
|
||
sbitmap_free (bb_processed);
|
||
}
|
||
|
||
/* Find one extended basic block EBB having root ENTRY containing block
|
||
BB */
|
||
|
||
static void
|
||
find_one_ext_basic_block (entry, bb, bb_processed, ebb)
|
||
int entry;
|
||
basic_block bb;
|
||
sbitmap *bb_processed;
|
||
ext_basic_blocks *ebb;
|
||
{
|
||
edge e;
|
||
|
||
if (!TEST_BIT (*bb_processed, bb->index))
|
||
{
|
||
SET_BIT (ebb->basic_block[entry], bb->index);
|
||
SET_BIT (*bb_processed, bb->index);
|
||
}
|
||
|
||
for (e = bb->succ; e; e = e->succ_next)
|
||
if (!TEST_BIT (*bb_processed, e->dest->index))
|
||
{
|
||
if (!e->dest->pred->pred_next
|
||
&& (!TEST_BIT (*bb_processed, e->dest->index)))
|
||
find_one_ext_basic_block (entry, e->dest, bb_processed, ebb);
|
||
else
|
||
SET_BIT (ebb->exit[entry], bb->index);
|
||
}
|
||
}
|
||
|
||
/* Find the register class for register REG_USE having TYPE (DESTINATION or
|
||
SOURCE) in INSN. Use DEFAULT_CLASS if we cannot determine a class. */
|
||
|
||
static enum reg_class
|
||
get_reg_class (insn, reg_use, type, default_class)
|
||
rtx insn;
|
||
rtx reg_use;
|
||
int type;
|
||
enum reg_class default_class;
|
||
{
|
||
int alt, id = 0;
|
||
|
||
extract_insn (insn);
|
||
constrain_operands (1);
|
||
alt = which_alternative;
|
||
|
||
preprocess_constraints ();
|
||
|
||
if (type == DESTINATION)
|
||
{
|
||
for (id = 0; id < recog_data.n_operands; id++)
|
||
if (rtx_equal_p (recog_data.operand[id], reg_use))
|
||
break;
|
||
}
|
||
|
||
else if (type == SOURCE)
|
||
for (id = recog_data.n_operands - 1; id >= 0; id--)
|
||
if (rtx_equal_p (recog_data.operand[id], reg_use))
|
||
break;
|
||
|
||
if (id == -1 || id == recog_data.n_operands)
|
||
return default_class;
|
||
|
||
return recog_op_alt[id][alt].class;
|
||
}
|