1935 lines
54 KiB
C
1935 lines
54 KiB
C
|
/* CYGNUS LOCAL LRS */
|
|||
|
/* Allocate registers within a basic block, for GNU compiler.
|
|||
|
Copyright (C) 1997, 1998 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. */
|
|||
|
|
|||
|
/* Live range support that splits a variable that is spilled to the stack into
|
|||
|
smaller ranges so that at least the middle of small loops the variable will
|
|||
|
be run in registers. This is run after global allocation if one or more
|
|||
|
variables were denied a register, and then global allocation is done once
|
|||
|
again. */
|
|||
|
|
|||
|
#include "config.h"
|
|||
|
#include "system.h"
|
|||
|
#include "rtl.h"
|
|||
|
#include "tree.h"
|
|||
|
#include "flags.h"
|
|||
|
#include "basic-block.h"
|
|||
|
#include "regs.h"
|
|||
|
#include "hard-reg-set.h"
|
|||
|
#include "insn-config.h"
|
|||
|
#include "recog.h"
|
|||
|
#include "output.h"
|
|||
|
#include "expr.h"
|
|||
|
#include "except.h"
|
|||
|
#include "function.h"
|
|||
|
#include "obstack.h"
|
|||
|
#include "range.h"
|
|||
|
#include "toplev.h"
|
|||
|
|
|||
|
void init_regset_vector PROTO ((regset *, int, struct obstack *));
|
|||
|
|
|||
|
extern struct obstack *rtl_obstack;
|
|||
|
|
|||
|
/* Information that we gather about registers */
|
|||
|
typedef struct rinfo_def {
|
|||
|
rtx reg; /* Register insn */
|
|||
|
int refs; /* # of register references */
|
|||
|
int sets; /* # of register sets/clobbers */
|
|||
|
int deaths; /* # of register deaths */
|
|||
|
int live_length; /* # of insns in range value is live */
|
|||
|
int n_calls; /* # of calls this reg crosses */
|
|||
|
int copy_flags; /* copy {in,out} flags */
|
|||
|
} rinfo;
|
|||
|
|
|||
|
/* Basic blocks expressed as a linked list */
|
|||
|
typedef struct bb_link_def {
|
|||
|
int block; /* block number or -1 */
|
|||
|
rtx first_insn; /* first insn in block */
|
|||
|
rtx last_insn; /* last insn in block */
|
|||
|
regset live_at_start; /* live information */
|
|||
|
} bb_link;
|
|||
|
|
|||
|
/* Symbol/block node that a variable is declared in and whether a register
|
|||
|
only holds constants. */
|
|||
|
typedef struct var_info_def {
|
|||
|
tree symbol; /* DECL_NODE of the symbol */
|
|||
|
tree block; /* BLOCK_NODE variable is declared in */
|
|||
|
rtx constant_value; /* what value a register always holds */
|
|||
|
} var_info;
|
|||
|
|
|||
|
int range_max_unique; /* Range #, monotonically increasing */
|
|||
|
|
|||
|
static rinfo *range_info; /* Register information */
|
|||
|
static var_info *range_vars; /* Map regno -> variable */
|
|||
|
static int *range_regs; /* Registers used in the loop */
|
|||
|
static int range_num_regs; /* # of registers in range_regs */
|
|||
|
static int range_loop_depth; /* current loop depth */
|
|||
|
static int range_update_used_p; /* whether range_used should be set */
|
|||
|
static regset range_used; /* regs used in the current range */
|
|||
|
static regset range_set; /* regs set in the current range */
|
|||
|
static regset range_live; /* regs currently live */
|
|||
|
static regset range_mixed_mode; /* regs that use different modes */
|
|||
|
static regset range_no_move; /* regs that don't have simple moves */
|
|||
|
static unsigned range_max_uid; /* Size of UID->basic block mapping */
|
|||
|
static bb_link **range_block_insn; /* Map INSN # to basic block # */
|
|||
|
static bb_link *range_block_orig; /* original basic blocks */
|
|||
|
|
|||
|
/* Linked list of live ranges to try allocating registers in first before
|
|||
|
allocating all of the remaining registers. */
|
|||
|
rtx live_range_list;
|
|||
|
|
|||
|
#define RANGE_BLOCK_NUM(INSN) \
|
|||
|
(((unsigned)INSN_UID (INSN) < (unsigned)range_max_uid \
|
|||
|
&& range_block_insn[INSN_UID (INSN)]) \
|
|||
|
? range_block_insn[INSN_UID (INSN)]->block : -1)
|
|||
|
|
|||
|
/* Forward references */
|
|||
|
static void range_mark PROTO((rtx, int, rtx));
|
|||
|
static void range_basic_mark PROTO((rtx, regset, rtx));
|
|||
|
static void range_basic_insn PROTO((rtx, regset, int));
|
|||
|
static void range_bad_insn PROTO((FILE *, char *, rtx));
|
|||
|
static void range_print_flags PROTO((FILE *, int, char *));
|
|||
|
static void print_blocks_internal PROTO((FILE *, tree, int));
|
|||
|
static int range_inner PROTO((FILE *, rtx, rtx, rtx, rtx,
|
|||
|
regset, regset, int));
|
|||
|
static void range_update_basic_block PROTO((FILE *, rtx, bb_link *,
|
|||
|
int, int));
|
|||
|
static void range_finish PROTO((FILE *, rtx, int, int));
|
|||
|
static void range_scan_blocks PROTO((tree, tree));
|
|||
|
static int range_compare PROTO((const GENERIC_PTR,
|
|||
|
const GENERIC_PTR));
|
|||
|
|
|||
|
|
|||
|
/* Determine which registers are used/set */
|
|||
|
|
|||
|
static void
|
|||
|
range_mark (x, set_p, insn)
|
|||
|
rtx x;
|
|||
|
int set_p;
|
|||
|
rtx insn;
|
|||
|
{
|
|||
|
register RTX_CODE code = GET_CODE (x);
|
|||
|
register int i, regno;
|
|||
|
register char *fmt;
|
|||
|
|
|||
|
restart:
|
|||
|
|
|||
|
switch (code)
|
|||
|
{
|
|||
|
default:
|
|||
|
break;
|
|||
|
|
|||
|
/* Make sure we mark the registers that are set */
|
|||
|
case SET:
|
|||
|
range_mark (SET_DEST (x), TRUE, insn);
|
|||
|
range_mark (SET_SRC (x), FALSE, insn);
|
|||
|
return;
|
|||
|
|
|||
|
/* Treat clobber like a set. */
|
|||
|
/* Pre-increment and friends always update as well as reference. */
|
|||
|
case CLOBBER:
|
|||
|
case PRE_INC:
|
|||
|
case PRE_DEC:
|
|||
|
case POST_INC:
|
|||
|
case POST_DEC:
|
|||
|
range_mark (XEXP (x, 0), TRUE, insn);
|
|||
|
return;
|
|||
|
|
|||
|
/* Memory addresses just reference the register, even if this is a SET */
|
|||
|
case MEM:
|
|||
|
range_mark (XEXP (x, 0), FALSE, insn);
|
|||
|
return;
|
|||
|
|
|||
|
/* Treat subregs as using/modifying the whole register. */
|
|||
|
case SUBREG:
|
|||
|
x = SUBREG_REG (x);
|
|||
|
code = GET_CODE (x);
|
|||
|
goto restart;
|
|||
|
|
|||
|
/* Actual registers, skip hard registers */
|
|||
|
case REG:
|
|||
|
regno = REGNO (x);
|
|||
|
if (regno >= FIRST_PSEUDO_REGISTER)
|
|||
|
{
|
|||
|
if (!range_info[regno].reg)
|
|||
|
range_info[regno].reg = x;
|
|||
|
else if (GET_MODE (x) != GET_MODE (range_info[regno].reg))
|
|||
|
SET_REGNO_REG_SET (range_mixed_mode, regno);
|
|||
|
|
|||
|
range_info[regno].refs += 2*range_loop_depth;
|
|||
|
SET_REGNO_REG_SET (range_live, regno);
|
|||
|
if (range_update_used_p)
|
|||
|
SET_REGNO_REG_SET (range_used, regno);
|
|||
|
|
|||
|
/* If there isn't a simple move pattern for the mode, skip it */
|
|||
|
if (mov_optab->handlers[(int) GET_MODE (x)].insn_code
|
|||
|
== CODE_FOR_nothing)
|
|||
|
SET_REGNO_REG_SET (range_no_move, regno);
|
|||
|
|
|||
|
if (set_p)
|
|||
|
{
|
|||
|
range_info[regno].sets++;
|
|||
|
SET_REGNO_REG_SET (range_set, regno);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
fmt = GET_RTX_FORMAT (code);
|
|||
|
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
|||
|
{
|
|||
|
if (fmt[i] == 'e')
|
|||
|
range_mark (XEXP (x, i), set_p, insn);
|
|||
|
|
|||
|
else if (fmt[i] == 'E')
|
|||
|
{
|
|||
|
register unsigned j;
|
|||
|
for (j = 0; j < XVECLEN (x, i); j++)
|
|||
|
range_mark (XVECEXP (x, i, j), set_p, insn);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Like range_mark, except more stripped down, to just care about what
|
|||
|
registers are currently live. */
|
|||
|
|
|||
|
static void
|
|||
|
range_basic_mark (x, live, insn)
|
|||
|
rtx x;
|
|||
|
regset live;
|
|||
|
rtx insn;
|
|||
|
{
|
|||
|
register RTX_CODE code = GET_CODE (x);
|
|||
|
register int i, regno, lim;
|
|||
|
register char *fmt;
|
|||
|
|
|||
|
restart:
|
|||
|
switch (code)
|
|||
|
{
|
|||
|
default:
|
|||
|
break;
|
|||
|
|
|||
|
/* Treat subregs as using/modifying the whole register. */
|
|||
|
case SUBREG:
|
|||
|
x = SUBREG_REG (x);
|
|||
|
code = GET_CODE (x);
|
|||
|
goto restart;
|
|||
|
|
|||
|
/* Actual registers */
|
|||
|
case REG:
|
|||
|
regno = REGNO (x);
|
|||
|
lim = regno + ((regno >= FIRST_PSEUDO_REGISTER)
|
|||
|
? 1
|
|||
|
: HARD_REGNO_NREGS (regno, GET_MODE (x)));
|
|||
|
|
|||
|
for (i = regno; i < lim; i++)
|
|||
|
SET_REGNO_REG_SET (live, i);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
fmt = GET_RTX_FORMAT (code);
|
|||
|
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
|||
|
{
|
|||
|
if (fmt[i] == 'e')
|
|||
|
range_basic_mark (XEXP (x, i), live, insn);
|
|||
|
|
|||
|
else if (fmt[i] == 'E')
|
|||
|
{
|
|||
|
register unsigned j;
|
|||
|
for (j = 0; j < XVECLEN (x, i); j++)
|
|||
|
range_basic_mark (XVECEXP (x, i, j), live, insn);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* For an INSN, CALL_INSN, or JUMP_INSN, update basic live/dead information. */
|
|||
|
|
|||
|
static void
|
|||
|
range_basic_insn (insn, live, range_max_regno)
|
|||
|
rtx insn;
|
|||
|
regset live;
|
|||
|
int range_max_regno;
|
|||
|
{
|
|||
|
rtx note, x;
|
|||
|
int i;
|
|||
|
|
|||
|
range_basic_mark (PATTERN (insn), live, insn);
|
|||
|
|
|||
|
/* Remember if the value is only set to one constant. */
|
|||
|
if (GET_CODE (insn) == INSN
|
|||
|
&& (x = single_set (insn)) != NULL_RTX
|
|||
|
&& (GET_CODE (SET_DEST (x)) == REG
|
|||
|
&& REG_N_SETS (REGNO (SET_DEST (x))) == 1
|
|||
|
&& CONSTANT_P (SET_SRC (x)))
|
|||
|
&& REGNO (SET_DEST (x)) < range_max_regno)
|
|||
|
range_vars[ REGNO (SET_DEST (x))].constant_value = SET_SRC (x);
|
|||
|
|
|||
|
/* figure out which registers are now dead. */
|
|||
|
for (note = REG_NOTES (insn); note != NULL_RTX; note = XEXP (note, 1))
|
|||
|
{
|
|||
|
if (REG_NOTE_KIND (note) == REG_DEAD
|
|||
|
&& GET_CODE (XEXP (note, 0)) == REG)
|
|||
|
{
|
|||
|
rtx reg = XEXP (note, 0);
|
|||
|
int regno = REGNO (reg);
|
|||
|
int lim = regno + ((regno >= FIRST_PSEUDO_REGISTER)
|
|||
|
? 1
|
|||
|
: HARD_REGNO_NREGS (regno, GET_MODE (reg)));
|
|||
|
|
|||
|
for (i = regno; i < lim; i++)
|
|||
|
CLEAR_REGNO_REG_SET (live, i);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Sort the registers by number of uses */
|
|||
|
|
|||
|
static int
|
|||
|
range_compare (v1p, v2p)
|
|||
|
const GENERIC_PTR v1p;
|
|||
|
const GENERIC_PTR v2p;
|
|||
|
{
|
|||
|
int i1 = *(int *)v1p;
|
|||
|
int i2 = *(int *)v2p;
|
|||
|
int r1 = range_info[i1].refs;
|
|||
|
int r2 = range_info[i2].refs;
|
|||
|
|
|||
|
if (r2 - r1)
|
|||
|
return r2 - r1;
|
|||
|
|
|||
|
/* Make sure that range_compare always is stable, so if the values are equal,
|
|||
|
compare based on pseduo register number. */
|
|||
|
return REGNO (range_info[i2].reg) - REGNO (range_info[i1].reg);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* If writing a .range file, print a message and an rtl. */
|
|||
|
|
|||
|
static void
|
|||
|
range_bad_insn (stream, msg, insn)
|
|||
|
FILE *stream;
|
|||
|
char *msg;
|
|||
|
rtx insn;
|
|||
|
{
|
|||
|
if (stream)
|
|||
|
{
|
|||
|
fputs (msg, stream);
|
|||
|
print_rtl (stream, PATTERN (insn));
|
|||
|
putc ('\n', stream);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Print out to STREAM the copyin/copyout flags. */
|
|||
|
|
|||
|
static void
|
|||
|
range_print_flags (stream, flags, prefix)
|
|||
|
FILE *stream;
|
|||
|
int flags;
|
|||
|
char *prefix;
|
|||
|
{
|
|||
|
if ((flags & LIVE_RANGE_COPYIN) != 0)
|
|||
|
{
|
|||
|
fprintf (stream, "%scopyin", prefix);
|
|||
|
prefix = ", ";
|
|||
|
}
|
|||
|
|
|||
|
if ((flags & LIVE_RANGE_COPYIN_CONST) != 0)
|
|||
|
{
|
|||
|
fprintf (stream, "%sconst", prefix);
|
|||
|
prefix = ", ";
|
|||
|
}
|
|||
|
|
|||
|
if ((flags & LIVE_RANGE_COPYOUT) != 0)
|
|||
|
fprintf (stream, "%scopyout", prefix);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Print out range information to STREAM, using RANGE as the range_info rtx,
|
|||
|
printing TAB and COMMENT at the beginning of each line. */
|
|||
|
|
|||
|
void
|
|||
|
live_range_print (stream, range, tab, comment)
|
|||
|
FILE *stream;
|
|||
|
rtx range;
|
|||
|
char *tab;
|
|||
|
char *comment;
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
fprintf (stream,
|
|||
|
"%s%s range #%d start, %d calls, basic block {start/end} %d/%d, loop depth %d\n",
|
|||
|
tab, comment,
|
|||
|
RANGE_INFO_UNIQUE (range), RANGE_INFO_NCALLS (range),
|
|||
|
RANGE_INFO_BB_START (range), RANGE_INFO_BB_END (range),
|
|||
|
RANGE_INFO_LOOP_DEPTH (range));
|
|||
|
|
|||
|
for (i = 0; i < (int)RANGE_INFO_NUM_REGS (range); i++)
|
|||
|
{
|
|||
|
int pseudo = RANGE_REG_PSEUDO (range, i);
|
|||
|
int copy = RANGE_REG_COPY (range, i);
|
|||
|
|
|||
|
fprintf (stream, "%s%s reg %d", tab, comment, pseudo);
|
|||
|
if (reg_renumber)
|
|||
|
{
|
|||
|
if ((unsigned)pseudo >= (unsigned)max_regno)
|
|||
|
fprintf (stream, " (illegal)");
|
|||
|
else if (reg_renumber[pseudo] >= 0)
|
|||
|
fprintf (stream, " (%s)", reg_names[ reg_renumber[pseudo]]);
|
|||
|
}
|
|||
|
|
|||
|
fprintf (stream, ", copy %d", copy);
|
|||
|
if (reg_renumber)
|
|||
|
{
|
|||
|
if ((unsigned)copy >= (unsigned)max_regno)
|
|||
|
fprintf (stream, " (illegal)");
|
|||
|
else if (reg_renumber[copy] >= 0)
|
|||
|
fprintf (stream, " (%s)", reg_names[ reg_renumber[copy]]);
|
|||
|
}
|
|||
|
|
|||
|
fprintf (stream,
|
|||
|
", %d ref(s), %d set(s), %d death(s), %d live length, %d calls",
|
|||
|
RANGE_REG_REFS (range, i),
|
|||
|
RANGE_REG_SETS (range, i),
|
|||
|
RANGE_REG_DEATHS (range, i),
|
|||
|
RANGE_REG_LIVE_LENGTH (range, i),
|
|||
|
RANGE_REG_N_CALLS (range, i));
|
|||
|
|
|||
|
range_print_flags (stream, RANGE_REG_COPY_FLAGS (range, i), ", ");
|
|||
|
if (REG_USERVAR_P (regno_reg_rtx[pseudo]))
|
|||
|
{
|
|||
|
fprintf (stream, ", user");
|
|||
|
if (RANGE_REG_SYMBOL_NODE (range, i))
|
|||
|
{
|
|||
|
tree name = DECL_NAME (RANGE_REG_SYMBOL_NODE (range, i));
|
|||
|
if (name)
|
|||
|
fprintf (stream, " [%s]", IDENTIFIER_POINTER (name));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (REGNO_POINTER_FLAG (pseudo))
|
|||
|
fprintf (stream, ", ptr");
|
|||
|
|
|||
|
putc ('\n', stream);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* CYGNUS LOCAL -- meissner/live range */
|
|||
|
/* Print the scoping blocks in the current function */
|
|||
|
|
|||
|
static void
|
|||
|
print_blocks_internal (stream, block, level)
|
|||
|
FILE *stream;
|
|||
|
tree block;
|
|||
|
int level;
|
|||
|
{
|
|||
|
/* Loop over all blocks */
|
|||
|
for (; block != NULL_TREE; block = BLOCK_CHAIN (block))
|
|||
|
{
|
|||
|
int i;
|
|||
|
tree vars_types[2];
|
|||
|
static char *vars_types_name[] = {"vars: ", "types:"};
|
|||
|
|
|||
|
fprintf (stream, "%*sBlock ", level*4, "");
|
|||
|
fprintf (stream, HOST_WIDE_INT_PRINT_HEX, (HOST_WIDE_INT) block);
|
|||
|
|
|||
|
if (BLOCK_CHAIN (block))
|
|||
|
{
|
|||
|
fprintf (stream, ", chain ");
|
|||
|
fprintf (stream, HOST_WIDE_INT_PRINT_HEX,
|
|||
|
(HOST_WIDE_INT) BLOCK_CHAIN (block));
|
|||
|
}
|
|||
|
|
|||
|
if (BLOCK_VARS (block))
|
|||
|
{
|
|||
|
fprintf (stream, ", vars ");
|
|||
|
fprintf (stream, HOST_WIDE_INT_PRINT_HEX,
|
|||
|
(HOST_WIDE_INT) BLOCK_VARS (block));
|
|||
|
}
|
|||
|
|
|||
|
if (BLOCK_TYPE_TAGS (block))
|
|||
|
{
|
|||
|
fprintf (stream, ", types ");
|
|||
|
fprintf (stream, HOST_WIDE_INT_PRINT_HEX,
|
|||
|
(HOST_WIDE_INT) BLOCK_TYPE_TAGS (block));
|
|||
|
}
|
|||
|
|
|||
|
if (BLOCK_SUBBLOCKS (block))
|
|||
|
{
|
|||
|
fprintf (stream, ", subblocks ");
|
|||
|
fprintf (stream, HOST_WIDE_INT_PRINT_HEX,
|
|||
|
(HOST_WIDE_INT) BLOCK_SUBBLOCKS (block));
|
|||
|
}
|
|||
|
|
|||
|
if (BLOCK_ABSTRACT_ORIGIN (block))
|
|||
|
{
|
|||
|
fprintf (stream, ", abstract origin ");
|
|||
|
fprintf (stream, HOST_WIDE_INT_PRINT_HEX,
|
|||
|
(HOST_WIDE_INT) BLOCK_ABSTRACT_ORIGIN (block));
|
|||
|
}
|
|||
|
|
|||
|
if (BLOCK_ABSTRACT (block))
|
|||
|
fprintf (stream, ", abstract");
|
|||
|
|
|||
|
if (BLOCK_LIVE_RANGE_FLAG (block))
|
|||
|
fprintf (stream, ", live-range");
|
|||
|
|
|||
|
if (BLOCK_LIVE_RANGE_VAR_FLAG (block))
|
|||
|
fprintf (stream, ", live-range-vars");
|
|||
|
|
|||
|
if (BLOCK_HANDLER_BLOCK (block))
|
|||
|
fprintf (stream, ", handler");
|
|||
|
|
|||
|
if (TREE_USED (block))
|
|||
|
fprintf (stream, ", used");
|
|||
|
|
|||
|
if (TREE_ASM_WRITTEN (block))
|
|||
|
fprintf (stream, ", asm-written");
|
|||
|
|
|||
|
fprintf (stream, "\n");
|
|||
|
vars_types[0] = BLOCK_VARS (block);
|
|||
|
vars_types[1] = BLOCK_TYPE_TAGS (block);
|
|||
|
for (i = 0; i < 2; i++)
|
|||
|
if (vars_types[i])
|
|||
|
{
|
|||
|
tree vars;
|
|||
|
int indent = ((level < 4) ? 16 : (level*4) + 4) - 1;
|
|||
|
int len = 0;
|
|||
|
|
|||
|
for (vars = BLOCK_VARS (block);
|
|||
|
vars != NULL_TREE;
|
|||
|
vars = TREE_CHAIN (vars))
|
|||
|
{
|
|||
|
if (DECL_NAME (vars) && IDENTIFIER_POINTER (DECL_NAME (vars)))
|
|||
|
{
|
|||
|
if (len == 0)
|
|||
|
{
|
|||
|
len = indent + 1 + strlen (vars_types_name[i]);
|
|||
|
fprintf (stream, "%*s%s", indent+1, "",
|
|||
|
vars_types_name[i]);
|
|||
|
}
|
|||
|
|
|||
|
len += IDENTIFIER_LENGTH (DECL_NAME (vars)) + 1;
|
|||
|
if (len >= 80 && len > indent)
|
|||
|
{
|
|||
|
len = indent;
|
|||
|
fprintf (stream, "\n%*s", indent, "");
|
|||
|
}
|
|||
|
|
|||
|
fprintf (stream, " %.*s",
|
|||
|
IDENTIFIER_LENGTH (DECL_NAME (vars)),
|
|||
|
IDENTIFIER_POINTER (DECL_NAME (vars)));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
fprintf (stream, "\n\n");
|
|||
|
}
|
|||
|
|
|||
|
print_blocks_internal (stream, BLOCK_SUBBLOCKS (block), level+1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
print_all_blocks ()
|
|||
|
{
|
|||
|
fprintf (stderr, "\n");
|
|||
|
print_blocks_internal (stderr, DECL_INITIAL (current_function_decl), 0);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Function with debugging output to STREAM that handles a sequence of insns
|
|||
|
that goes from RANGE_START to RANGE_END, splitting the range of variables
|
|||
|
used between INNER_START and INNER_END. Registers in LIVE_AT_START are live
|
|||
|
at the first insn. BB_{START,END} holds the basic block numbers of the
|
|||
|
first and last insns. Return the number of variables that had their ranges
|
|||
|
split into a new pseudo variable used only within the loop. */
|
|||
|
|
|||
|
static int
|
|||
|
range_inner (stream, range_start, range_end, inner_start, inner_end,
|
|||
|
live_at_start, live_at_end, loop_depth)
|
|||
|
FILE *stream;
|
|||
|
rtx range_start;
|
|||
|
rtx range_end;
|
|||
|
rtx inner_start;
|
|||
|
rtx inner_end;
|
|||
|
regset live_at_start;
|
|||
|
regset live_at_end;
|
|||
|
int loop_depth;
|
|||
|
{
|
|||
|
rtx insn;
|
|||
|
rtx pattern;
|
|||
|
rtx label_ref;
|
|||
|
rtx label_chain;
|
|||
|
rtx start;
|
|||
|
rtx end;
|
|||
|
rtx *regs;
|
|||
|
rtvec regs_rtvec;
|
|||
|
rtx note;
|
|||
|
int first_label = get_first_label_num ();
|
|||
|
int last_label = max_label_num ();
|
|||
|
unsigned num_labels = last_label - first_label + 1;
|
|||
|
unsigned indx;
|
|||
|
int i;
|
|||
|
int regno;
|
|||
|
int block;
|
|||
|
int *loop_label_ref = (int *) alloca (sizeof (int) * num_labels);
|
|||
|
int *all_label_ref = (int *) alloca (sizeof (int) * num_labels);
|
|||
|
char *found_label_ref = (char *) alloca (sizeof (char) * num_labels);
|
|||
|
int ok_p = TRUE;
|
|||
|
int n_insns;
|
|||
|
int n_calls;
|
|||
|
regset live_at_start_copy;
|
|||
|
regset live_at_end_copy;
|
|||
|
int bb_start;
|
|||
|
int bb_end;
|
|||
|
char *nl;
|
|||
|
rtx ri;
|
|||
|
|
|||
|
bzero ((char *)loop_label_ref, sizeof (int) * num_labels);
|
|||
|
bzero ((char *)all_label_ref, sizeof (int) * num_labels);
|
|||
|
bzero ((char *) range_info, sizeof (rinfo) * max_reg_num ());
|
|||
|
CLEAR_REG_SET (range_used);
|
|||
|
CLEAR_REG_SET (range_set);
|
|||
|
CLEAR_REG_SET (range_mixed_mode);
|
|||
|
CLEAR_REG_SET (range_no_move);
|
|||
|
COPY_REG_SET (range_live, live_at_start);
|
|||
|
range_num_regs = 0;
|
|||
|
range_loop_depth = loop_depth;
|
|||
|
|
|||
|
if (stream)
|
|||
|
fprintf (stream, "\nPossible range from %d to %d (inner range %d to %d)\nLive at start: ",
|
|||
|
INSN_UID (range_start), INSN_UID (range_end),
|
|||
|
INSN_UID (inner_start), INSN_UID (inner_end));
|
|||
|
|
|||
|
/* Mark the live registers as needing copies to the tmp register from the
|
|||
|
original value. */
|
|||
|
EXECUTE_IF_SET_IN_REG_SET (live_at_start, FIRST_PSEUDO_REGISTER, i,
|
|||
|
{
|
|||
|
range_info[i].copy_flags |= LIVE_RANGE_COPYIN;
|
|||
|
if (stream)
|
|||
|
fprintf (stream, " %d", i);
|
|||
|
if (range_vars[i].constant_value)
|
|||
|
{
|
|||
|
range_info[i].copy_flags
|
|||
|
|= LIVE_RANGE_COPYIN_CONST;
|
|||
|
|
|||
|
if (stream)
|
|||
|
fprintf (stream, " [constant]");
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
if (stream)
|
|||
|
{
|
|||
|
fprintf (stream, "\nLive at end: ");
|
|||
|
EXECUTE_IF_SET_IN_REG_SET (live_at_end, FIRST_PSEUDO_REGISTER, i,
|
|||
|
{
|
|||
|
fprintf (stream, " %d", i);
|
|||
|
});
|
|||
|
|
|||
|
putc ('\n', stream);
|
|||
|
}
|
|||
|
|
|||
|
/* Calculate basic block start and end */
|
|||
|
bb_start = -1;
|
|||
|
for (insn = range_start;
|
|||
|
insn && insn != range_end && (bb_start = RANGE_BLOCK_NUM (insn)) < 0;
|
|||
|
insn = NEXT_INSN (insn))
|
|||
|
;
|
|||
|
|
|||
|
bb_end = -1;
|
|||
|
for (insn = range_end;
|
|||
|
insn && insn != range_start && (bb_end = RANGE_BLOCK_NUM (insn)) < 0;
|
|||
|
insn = PREV_INSN (insn))
|
|||
|
;
|
|||
|
|
|||
|
if (bb_start < 0)
|
|||
|
{
|
|||
|
ok_p = FALSE;
|
|||
|
if (stream)
|
|||
|
fprintf (stream, "Cannot find basic block start\n");
|
|||
|
}
|
|||
|
|
|||
|
if (bb_end < 0)
|
|||
|
{
|
|||
|
ok_p = FALSE;
|
|||
|
if (stream)
|
|||
|
fprintf (stream, "Cannot find basic block end\n");
|
|||
|
}
|
|||
|
|
|||
|
/* Scan the loop, looking for jumps outside/inside of the loop. If the
|
|||
|
loop has such jumps, we ignore it. */
|
|||
|
n_insns = n_calls = block = 0;
|
|||
|
range_update_used_p = FALSE;
|
|||
|
for (insn = range_start; insn && insn != range_end; insn = NEXT_INSN (insn))
|
|||
|
{
|
|||
|
enum rtx_code code = GET_CODE (insn);
|
|||
|
int block_tmp = RANGE_BLOCK_NUM (insn);
|
|||
|
if (block_tmp >= 0 && block != block_tmp)
|
|||
|
{
|
|||
|
block = block_tmp;
|
|||
|
COPY_REG_SET (range_live, basic_block_live_at_start[block]);
|
|||
|
}
|
|||
|
|
|||
|
/* Only mark registers that appear between INNER_START and INNER_END */
|
|||
|
if (insn == inner_start)
|
|||
|
range_update_used_p = TRUE;
|
|||
|
else if (insn == inner_end)
|
|||
|
range_update_used_p = FALSE;
|
|||
|
|
|||
|
if (GET_RTX_CLASS (code) == 'i')
|
|||
|
{
|
|||
|
n_insns++;
|
|||
|
/* Mark used registers */
|
|||
|
range_mark (PATTERN (insn), FALSE, insn);
|
|||
|
|
|||
|
/* Update live length, & number of calls that the insn crosses */
|
|||
|
EXECUTE_IF_SET_IN_REG_SET (range_live, FIRST_PSEUDO_REGISTER, i,
|
|||
|
{
|
|||
|
range_info[i].live_length++;
|
|||
|
if (GET_CODE (insn) == CALL_INSN)
|
|||
|
range_info[i].n_calls++;
|
|||
|
});
|
|||
|
|
|||
|
/* figure out which ones will be dead by the end of the region */
|
|||
|
for (note = REG_NOTES (insn); note != NULL_RTX; note = XEXP (note, 1))
|
|||
|
{
|
|||
|
if (REG_NOTE_KIND (note) == REG_DEAD
|
|||
|
&& GET_CODE (XEXP (note, 0)) == REG
|
|||
|
&& REGNO (XEXP (note, 0)) > FIRST_PSEUDO_REGISTER)
|
|||
|
{
|
|||
|
CLEAR_REGNO_REG_SET (range_live, REGNO (XEXP (note, 0)));
|
|||
|
range_info[ REGNO (XEXP (note, 0)) ].deaths++;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
switch (code)
|
|||
|
{
|
|||
|
default:
|
|||
|
break;
|
|||
|
|
|||
|
/* Update loop_depth */
|
|||
|
case NOTE:
|
|||
|
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
|
|||
|
range_loop_depth++;
|
|||
|
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
|
|||
|
range_loop_depth++;
|
|||
|
break;
|
|||
|
|
|||
|
/* Record whether there was a call. */
|
|||
|
case CALL_INSN:
|
|||
|
n_calls++;
|
|||
|
break;
|
|||
|
|
|||
|
/* Found a label, record the number of uses */
|
|||
|
case CODE_LABEL:
|
|||
|
indx = CODE_LABEL_NUMBER (insn) - first_label;
|
|||
|
if (indx >= num_labels)
|
|||
|
{
|
|||
|
if (stream)
|
|||
|
fprintf (stream, "Code label %d doesn't fit in label table",
|
|||
|
CODE_LABEL_NUMBER (insn));
|
|||
|
|
|||
|
ok_p = FALSE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
all_label_ref[indx] = LABEL_NUSES (insn);
|
|||
|
break;
|
|||
|
|
|||
|
/* Found a jump of some sort, see where the jump goes. */
|
|||
|
case JUMP_INSN:
|
|||
|
pattern = PATTERN (insn);
|
|||
|
|
|||
|
/* Switch statement, process all labels */
|
|||
|
if (GET_CODE (pattern) == ADDR_VEC
|
|||
|
|| GET_CODE (pattern) == ADDR_DIFF_VEC)
|
|||
|
{
|
|||
|
int vec_num = (GET_CODE (PATTERN (insn)) == ADDR_VEC) ? 0 : 1;
|
|||
|
int len = XVECLEN (pattern, vec_num);
|
|||
|
bzero ((char *)found_label_ref, sizeof (char) * num_labels);
|
|||
|
|
|||
|
for (i = 0; i < len; i++)
|
|||
|
{
|
|||
|
label_ref = XEXP (XVECEXP (pattern, vec_num, i), 0);
|
|||
|
indx = CODE_LABEL_NUMBER (label_ref) - first_label;
|
|||
|
|
|||
|
if (indx >= num_labels)
|
|||
|
{
|
|||
|
range_bad_insn (stream,
|
|||
|
"Label ref doesn't fit in label table\n",
|
|||
|
label_ref);
|
|||
|
ok_p = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
/* Only process duplicated labels once, since the LABEL_REF
|
|||
|
chain only includes it once. */
|
|||
|
else if (!found_label_ref[indx])
|
|||
|
{
|
|||
|
found_label_ref[indx] = TRUE;
|
|||
|
loop_label_ref[indx]++;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
else
|
|||
|
{
|
|||
|
label_ref = JUMP_LABEL (insn);
|
|||
|
if (!label_ref)
|
|||
|
{
|
|||
|
rtx sset;
|
|||
|
range_bad_insn
|
|||
|
(stream,
|
|||
|
(((sset = single_set (insn)) != NULL_RTX
|
|||
|
&& (GET_CODE (SET_SRC (sset)) == REG
|
|||
|
|| GET_CODE (SET_SRC (sset)) == SUBREG
|
|||
|
|| GET_CODE (SET_SRC (sset)) == MEM))
|
|||
|
? "Jump to indeterminate label in inner loop\n"
|
|||
|
: "JUMP_LABEL (insn) is null.\n"),
|
|||
|
insn);
|
|||
|
|
|||
|
ok_p = FALSE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (GET_CODE (label_ref) != CODE_LABEL)
|
|||
|
{
|
|||
|
range_bad_insn (stream,
|
|||
|
"JUMP_LABEL (insn) is not a CODE_LABEL.\n",
|
|||
|
insn);
|
|||
|
|
|||
|
ok_p = FALSE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
indx = CODE_LABEL_NUMBER (label_ref) - first_label;
|
|||
|
if (indx >= num_labels)
|
|||
|
{
|
|||
|
range_bad_insn (stream,
|
|||
|
"Label ref doesn't fit in label table\n",
|
|||
|
insn);
|
|||
|
|
|||
|
ok_p = FALSE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
loop_label_ref[indx]++;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Now that we've scanned the loop, check for any jumps into or out of
|
|||
|
the loop, and if we've found them, don't do live range splitting.
|
|||
|
If there are no registers used in the loop, there is nothing to do. */
|
|||
|
nl = (char *)0;
|
|||
|
if (ok_p)
|
|||
|
{
|
|||
|
for (i = 0; i < (int)num_labels; i++)
|
|||
|
if (loop_label_ref[i] != all_label_ref[i])
|
|||
|
{
|
|||
|
ok_p = FALSE;
|
|||
|
if (stream)
|
|||
|
{
|
|||
|
nl = "\n";
|
|||
|
if (!all_label_ref[i])
|
|||
|
fprintf (stream, "label %d was outside of the loop.\n", i+first_label);
|
|||
|
else
|
|||
|
fprintf (stream, "label %d had %d references, only %d were in loop.\n",
|
|||
|
i+first_label, all_label_ref[i], loop_label_ref[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* ignore any registers that use different modes, or that don't have simple
|
|||
|
move instructions. */
|
|||
|
if (stream)
|
|||
|
{
|
|||
|
EXECUTE_IF_SET_IN_REG_SET (range_mixed_mode, FIRST_PSEUDO_REGISTER, regno,
|
|||
|
{
|
|||
|
nl = "\n";
|
|||
|
fprintf (stream,
|
|||
|
"Skipping register %d used with different types\n",
|
|||
|
regno);
|
|||
|
});
|
|||
|
|
|||
|
EXECUTE_IF_SET_IN_REG_SET (range_no_move, FIRST_PSEUDO_REGISTER, regno,
|
|||
|
{
|
|||
|
nl = "\n";
|
|||
|
fprintf (stream,
|
|||
|
"Skipping register %d that needs complex moves\n",
|
|||
|
regno);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
IOR_REG_SET (range_mixed_mode, range_no_move);
|
|||
|
AND_COMPL_REG_SET (range_used, range_mixed_mode);
|
|||
|
AND_COMPL_REG_SET (range_set, range_mixed_mode);
|
|||
|
EXECUTE_IF_SET_IN_REG_SET(range_used, FIRST_PSEUDO_REGISTER, regno,
|
|||
|
{
|
|||
|
/* If the register isn't live at the end,
|
|||
|
indicate that it must have died. */
|
|||
|
if (REGNO_REG_SET_P (live_at_end, regno))
|
|||
|
range_info[regno].copy_flags
|
|||
|
|= LIVE_RANGE_COPYOUT;
|
|||
|
|
|||
|
/* If the register is only used in a single
|
|||
|
basic block, let local-alloc allocate
|
|||
|
a register for it. */
|
|||
|
if (REG_BASIC_BLOCK (regno) >= 0)
|
|||
|
{
|
|||
|
nl = "\n";
|
|||
|
if (stream)
|
|||
|
fprintf (stream,
|
|||
|
"Skipping %d due to being used in a single basic block\n",
|
|||
|
regno);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/* If the register is live only within the
|
|||
|
range, don't bother with it. */
|
|||
|
if (REG_LIVE_LENGTH (regno)
|
|||
|
<= range_info[regno].live_length)
|
|||
|
{
|
|||
|
nl = "\n";
|
|||
|
if (stream)
|
|||
|
fprintf (stream,
|
|||
|
"Skipping %d due to being used only in range\n",
|
|||
|
regno);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
range_regs[range_num_regs++] = regno;
|
|||
|
});
|
|||
|
|
|||
|
if (range_num_regs == 0)
|
|||
|
{
|
|||
|
ok_p = FALSE;
|
|||
|
if (stream)
|
|||
|
fprintf (stream, "No registers found in loop\n");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (stream && nl)
|
|||
|
fputs (nl, stream);
|
|||
|
|
|||
|
if (!ok_p)
|
|||
|
return 0;
|
|||
|
|
|||
|
qsort (range_regs, range_num_regs, sizeof (int), range_compare);
|
|||
|
|
|||
|
CLEAR_REG_SET (range_used);
|
|||
|
for (i = 0; i < range_num_regs; i++)
|
|||
|
SET_REGNO_REG_SET (range_used, range_regs[i]);
|
|||
|
|
|||
|
#if 0
|
|||
|
/* Narrow down range_start so that we include only those insns that reference
|
|||
|
one of the live range variables. */
|
|||
|
regs_ref = ALLOCA_REG_SET ();
|
|||
|
regs_set = ALLOCA_REG_SET ();
|
|||
|
regs_tmp = ALLOCA_REG_SET ();
|
|||
|
|
|||
|
uses_regs = inner_start;
|
|||
|
for (insn = inner_start; insn; insn = PREV_INSN (insn))
|
|||
|
{
|
|||
|
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
|
|||
|
&& regset_mentioned_p (range_used, PATTERN (insn), FALSE, regs_ref,
|
|||
|
regs_set))
|
|||
|
{
|
|||
|
uses_regs = insn;
|
|||
|
}
|
|||
|
|
|||
|
if (insn == range_start)
|
|||
|
break;
|
|||
|
}
|
|||
|
range_start = uses_regs;
|
|||
|
|
|||
|
/* Narrow down range_end so that we include only those insns that reference
|
|||
|
one of the live range variables. */
|
|||
|
CLEAR_REG_SET (regs_ref);
|
|||
|
uses_regs = inner_end;
|
|||
|
for (insn = inner_end; insn; insn = NEXT_INSN (insn))
|
|||
|
{
|
|||
|
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
|
|||
|
&& regset_mentioned_p (range_used, PATTERN (insn), FALSE, regs_ref,
|
|||
|
regs_set))
|
|||
|
{
|
|||
|
uses_regs = insn;
|
|||
|
}
|
|||
|
|
|||
|
if (insn == range_end)
|
|||
|
break;
|
|||
|
}
|
|||
|
range_end = uses_regs;
|
|||
|
|
|||
|
FREE_REG_SET (regs_ref);
|
|||
|
FREE_REG_SET (regs_set);
|
|||
|
FREE_REG_SET (regs_tmp);
|
|||
|
#endif
|
|||
|
|
|||
|
/* Mark the live range region. */
|
|||
|
regs = (rtx *) alloca (sizeof (rtx) * range_num_regs);
|
|||
|
for (i = 0; i < range_num_regs; i++)
|
|||
|
{
|
|||
|
int r = range_regs[i];
|
|||
|
rinfo *ri = &range_info[r];
|
|||
|
|
|||
|
if (range_vars[r].block)
|
|||
|
BLOCK_LIVE_RANGE_VAR_FLAG (range_vars[r].block) = TRUE;
|
|||
|
|
|||
|
REG_N_RANGE_CANDIDATE_P (r) = 1;
|
|||
|
regs[i] = gen_rtx (RANGE_REG, VOIDmode,
|
|||
|
r, -1, ri->refs, ri->sets, ri->deaths,
|
|||
|
ri->copy_flags, ri->live_length, ri->n_calls,
|
|||
|
range_vars[r].symbol, range_vars[r].block);
|
|||
|
}
|
|||
|
|
|||
|
live_at_start_copy = OBSTACK_ALLOC_REG_SET (rtl_obstack);
|
|||
|
live_at_end_copy = OBSTACK_ALLOC_REG_SET (rtl_obstack);
|
|||
|
COPY_REG_SET (live_at_start_copy, live_at_start);
|
|||
|
COPY_REG_SET (live_at_end_copy, live_at_end);
|
|||
|
|
|||
|
regs_rtvec = gen_rtvec_v (range_num_regs, regs);
|
|||
|
start = emit_note_before (NOTE_INSN_RANGE_START, range_start);
|
|||
|
end = emit_note_after (NOTE_INSN_RANGE_END, range_end);
|
|||
|
|
|||
|
ri = gen_rtx (RANGE_INFO, VOIDmode, start, end, regs_rtvec, n_calls, n_insns,
|
|||
|
range_max_unique++, bb_start, bb_end, loop_depth,
|
|||
|
live_at_start_copy, live_at_end_copy, 0, 0);
|
|||
|
|
|||
|
NOTE_RANGE_INFO (start) = ri;
|
|||
|
NOTE_RANGE_INFO (end) = ri;
|
|||
|
|
|||
|
live_range_list = gen_rtx (INSN_LIST, VOIDmode, start, live_range_list);
|
|||
|
return range_num_regs;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Scan all blocks looking for user variables so that we can map from
|
|||
|
register number back to the appropriate DECL_NODE and BLOCK_NODE. */
|
|||
|
|
|||
|
static void
|
|||
|
range_scan_blocks (block, args)
|
|||
|
tree block;
|
|||
|
tree args;
|
|||
|
{
|
|||
|
tree var;
|
|||
|
|
|||
|
/* Scan arguments */
|
|||
|
for (var = args; var != NULL_TREE; var = TREE_CHAIN (var))
|
|||
|
{
|
|||
|
if (DECL_RTL (var) && GET_CODE (DECL_RTL (var)) == REG)
|
|||
|
{
|
|||
|
int regno = REGNO (DECL_RTL (var));
|
|||
|
if ((unsigned)regno >= (unsigned)max_regno)
|
|||
|
abort ();
|
|||
|
|
|||
|
range_vars[regno].symbol = var;
|
|||
|
range_vars[regno].block = block;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Loop over all blocks */
|
|||
|
for (; block != NULL_TREE; block = BLOCK_CHAIN (block))
|
|||
|
if (TREE_USED (block))
|
|||
|
{
|
|||
|
/* Record all symbols in the block */
|
|||
|
for (var = BLOCK_VARS (block); var != NULL_TREE; var = TREE_CHAIN (var))
|
|||
|
{
|
|||
|
if (DECL_RTL (var) && GET_CODE (DECL_RTL (var)) == REG)
|
|||
|
{
|
|||
|
int regno = REGNO (DECL_RTL (var));
|
|||
|
if ((unsigned)regno >= (unsigned)max_regno)
|
|||
|
abort ();
|
|||
|
|
|||
|
range_vars[regno].symbol = var;
|
|||
|
range_vars[regno].block = block;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Record the subblocks, and their subblocks... */
|
|||
|
range_scan_blocks (BLOCK_SUBBLOCKS (block), NULL_TREE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Recalculate the basic blocks due to adding copyins and copyouts. */
|
|||
|
|
|||
|
static void
|
|||
|
range_update_basic_block (stream, first_insn, new_bb,
|
|||
|
new_bb_count, range_max_regno)
|
|||
|
FILE *stream;
|
|||
|
rtx first_insn;
|
|||
|
bb_link *new_bb;
|
|||
|
int new_bb_count;
|
|||
|
int range_max_regno;
|
|||
|
{
|
|||
|
int i;
|
|||
|
rtx insn;
|
|||
|
rtx range;
|
|||
|
rtx block_end;
|
|||
|
rtx block_start;
|
|||
|
int block;
|
|||
|
int in_block_p;
|
|||
|
int old_n_basic_blocks = n_basic_blocks;
|
|||
|
int *map_bb = (int *) alloca (sizeof (int) * n_basic_blocks);
|
|||
|
regset live = ALLOCA_REG_SET ();
|
|||
|
|
|||
|
COPY_REG_SET (live, basic_block_live_at_start[0]);
|
|||
|
|
|||
|
/* Go through and add NOTE_INSN_LIVE notes for each current basic block. */
|
|||
|
for (i = 0; i < old_n_basic_blocks; i++)
|
|||
|
{
|
|||
|
rtx p = emit_note_before (NOTE_INSN_LIVE, BLOCK_HEAD (i));
|
|||
|
NOTE_LIVE_INFO (p) = gen_rtx (RANGE_LIVE, VOIDmode,
|
|||
|
basic_block_live_at_start[i], i);
|
|||
|
map_bb[i] = -1;
|
|||
|
|
|||
|
if (stream)
|
|||
|
{
|
|||
|
fprintf (stream,
|
|||
|
"Old basic block #%d, first insn %d, last insn %d, live",
|
|||
|
i,
|
|||
|
INSN_UID (BLOCK_HEAD (i)),
|
|||
|
INSN_UID (BLOCK_END (i)));
|
|||
|
bitmap_print (stream, basic_block_live_at_start[i], " {", "}\n");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (stream)
|
|||
|
putc ('\n', stream);
|
|||
|
|
|||
|
/* Recalculate the basic blocks. */
|
|||
|
free ((char *)x_basic_block_head);
|
|||
|
free ((char *)x_basic_block_end);
|
|||
|
x_basic_block_head = x_basic_block_end = (rtx *)0;
|
|||
|
find_basic_blocks (first_insn, max_regno, stream);
|
|||
|
free_basic_block_vars (TRUE);
|
|||
|
|
|||
|
/* Restore the live information. We assume that flow will find either a
|
|||
|
previous start of a basic block, or the newly created insn blocks as
|
|||
|
the start of the new basic blocks. */
|
|||
|
|
|||
|
if (old_n_basic_blocks != n_basic_blocks)
|
|||
|
basic_block_live_at_start
|
|||
|
= (regset *) oballoc (sizeof (regset) * n_basic_blocks);
|
|||
|
|
|||
|
init_regset_vector (basic_block_live_at_start, n_basic_blocks,
|
|||
|
rtl_obstack);
|
|||
|
|
|||
|
block = 0;
|
|||
|
in_block_p = FALSE;
|
|||
|
block_start = BLOCK_HEAD (0);
|
|||
|
block_end = BLOCK_END (0);
|
|||
|
for (insn = first_insn; insn; insn = NEXT_INSN (insn))
|
|||
|
{
|
|||
|
/* If this is the start of a basic block update live information. */
|
|||
|
if (insn == block_start)
|
|||
|
{
|
|||
|
int i;
|
|||
|
in_block_p = TRUE;
|
|||
|
|
|||
|
/* See if this was the start of one of the "new" basic
|
|||
|
blocks. If so, get register livetime information from
|
|||
|
the data we saved when we created the range. */
|
|||
|
for (i = 0; i < new_bb_count; i++)
|
|||
|
if (new_bb[i].first_insn == insn)
|
|||
|
break;
|
|||
|
|
|||
|
if (i < new_bb_count)
|
|||
|
COPY_REG_SET (live, new_bb[i].live_at_start);
|
|||
|
|
|||
|
COPY_REG_SET (basic_block_live_at_start[block], live);
|
|||
|
}
|
|||
|
|
|||
|
if (GET_CODE (insn) == NOTE)
|
|||
|
{
|
|||
|
/* Is this where an old basic block began? */
|
|||
|
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LIVE)
|
|||
|
{
|
|||
|
rtx info = NOTE_LIVE_INFO (insn);
|
|||
|
COPY_REG_SET (live, RANGE_LIVE_BITMAP (info));
|
|||
|
map_bb[RANGE_LIVE_ORIG_BLOCK (info)] = block;
|
|||
|
FREE_REG_SET (RANGE_LIVE_BITMAP (info));
|
|||
|
NOTE_LIVE_INFO (insn) = NULL_RTX;
|
|||
|
if (stream)
|
|||
|
fprintf (stream, "Old basic block #%d is now %d.\n",
|
|||
|
RANGE_LIVE_ORIG_BLOCK (info), block);
|
|||
|
}
|
|||
|
|
|||
|
/* If a range start/end, use stored live information. */
|
|||
|
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_START)
|
|||
|
{
|
|||
|
rtx ri = NOTE_RANGE_INFO (insn);
|
|||
|
int old_block = RANGE_INFO_BB_START (ri);
|
|||
|
|
|||
|
RANGE_INFO_BB_START (ri) = block;
|
|||
|
COPY_REG_SET (live, RANGE_INFO_LIVE_START (ri));
|
|||
|
if (stream)
|
|||
|
fprintf (stream,
|
|||
|
"Changing range start basic block from %d to %d\n",
|
|||
|
old_block, block);
|
|||
|
}
|
|||
|
|
|||
|
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_END)
|
|||
|
{
|
|||
|
rtx ri = NOTE_RANGE_INFO (insn);
|
|||
|
int old_block = RANGE_INFO_BB_END (ri);
|
|||
|
int new_block = (in_block_p) ? block : block-1;
|
|||
|
|
|||
|
RANGE_INFO_BB_END (ri) = new_block;
|
|||
|
COPY_REG_SET (live, RANGE_INFO_LIVE_END (ri));
|
|||
|
if (stream)
|
|||
|
fprintf (stream,
|
|||
|
"Changing range end basic block from %d to %d\n",
|
|||
|
old_block, new_block);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Update live/dead information. */
|
|||
|
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
|
|||
|
range_basic_insn (insn, live, range_max_regno);
|
|||
|
|
|||
|
/* Advance basic block if at end. */
|
|||
|
if (insn == block_end)
|
|||
|
{
|
|||
|
block++;
|
|||
|
in_block_p = FALSE;
|
|||
|
if (block < n_basic_blocks)
|
|||
|
{
|
|||
|
block_start = BLOCK_HEAD (block);
|
|||
|
block_end = BLOCK_END (block);
|
|||
|
}
|
|||
|
else
|
|||
|
block_start = block_end = NULL_RTX;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (stream)
|
|||
|
{
|
|||
|
putc ('\n', stream);
|
|||
|
fflush (stream);
|
|||
|
}
|
|||
|
|
|||
|
/* Update REG_BASIC_BLOCK field now. */
|
|||
|
for (i = 0; i < max_regno; i++)
|
|||
|
{
|
|||
|
if (REG_BASIC_BLOCK (i) >= 0)
|
|||
|
REG_BASIC_BLOCK (i) = map_bb[REG_BASIC_BLOCK (i)];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Allocate the new registers for live range splitting. Do this after we've
|
|||
|
scanned all of the insns, so that we grow the tables only once. */
|
|||
|
|
|||
|
static void
|
|||
|
range_finish (stream, first_insn, count, range_max_regno)
|
|||
|
FILE *stream;
|
|||
|
rtx first_insn;
|
|||
|
int count;
|
|||
|
int range_max_regno;
|
|||
|
{
|
|||
|
rtx range;
|
|||
|
int new_max_regno = max_reg_num () + count;
|
|||
|
rtx *replacements = (rtx *) alloca (new_max_regno * sizeof (rtx));
|
|||
|
regset old_dead = ALLOCA_REG_SET ();
|
|||
|
regset new_live = ALLOCA_REG_SET ();
|
|||
|
regset copyouts = ALLOCA_REG_SET ();
|
|||
|
rtx insn;
|
|||
|
int i;
|
|||
|
tree *block_list /* set up NOTE_BLOCK_NUM field */
|
|||
|
= ((write_symbols == NO_DEBUG)
|
|||
|
? (tree *)0
|
|||
|
: identify_blocks (DECL_INITIAL (current_function_decl), first_insn));
|
|||
|
|
|||
|
bb_link *new_bb;
|
|||
|
int new_bb_count = 0;
|
|||
|
int max_bb_count = 0;
|
|||
|
|
|||
|
no_new_pseudos = 0;
|
|||
|
for (range = live_range_list; range; range = XEXP (range, 1))
|
|||
|
max_bb_count += 2;
|
|||
|
|
|||
|
new_bb = (bb_link *) alloca (sizeof (bb_link) * max_bb_count);
|
|||
|
bzero ((char *)new_bb, sizeof (bb_link) * max_bb_count);
|
|||
|
|
|||
|
if (stream)
|
|||
|
putc ('\n', stream);
|
|||
|
|
|||
|
/* Grow the register tables */
|
|||
|
allocate_reg_info (new_max_regno, FALSE, FALSE);
|
|||
|
|
|||
|
for (range = live_range_list; range; range = XEXP (range, 1))
|
|||
|
{
|
|||
|
rtx range_start = XEXP (range, 0);
|
|||
|
rtx ri = NOTE_RANGE_INFO (range_start);
|
|||
|
rtx range_end = RANGE_INFO_NOTE_END (ri);
|
|||
|
int bb_start = RANGE_INFO_BB_START (ri);
|
|||
|
int bb_end = RANGE_INFO_BB_END (ri);
|
|||
|
int bb = (bb_start >= 0 && bb_start == bb_end) ? bb_start : -2;
|
|||
|
rtx after;
|
|||
|
rtx before;
|
|||
|
int new_scope_p = (write_symbols != NO_DEBUG /* create new scope */
|
|||
|
&& flag_live_range_scope /* block for vars? */
|
|||
|
&& (write_symbols != DBX_DEBUG
|
|||
|
|| !LIVE_RANGE_GDBSTAB_P ()));
|
|||
|
|
|||
|
after = range_start;
|
|||
|
before = range_end;
|
|||
|
|
|||
|
bzero ((char *)replacements, new_max_regno * sizeof (rtx));
|
|||
|
CLEAR_REG_SET (old_dead);
|
|||
|
CLEAR_REG_SET (copyouts);
|
|||
|
|
|||
|
/* Allocate new registers, set up the fields needed. */
|
|||
|
for (i = 0; i < (int)RANGE_INFO_NUM_REGS (ri); i++)
|
|||
|
{
|
|||
|
int old_regno = RANGE_REG_PSEUDO (ri, i);
|
|||
|
rtx old_reg = regno_reg_rtx[old_regno];
|
|||
|
enum machine_mode mode = GET_MODE (old_reg);
|
|||
|
rtx new_reg = gen_reg_rtx (mode);
|
|||
|
int new_regno = REGNO (new_reg);
|
|||
|
|
|||
|
replacements[old_regno] = new_reg;
|
|||
|
|
|||
|
RANGE_REG_COPY (ri, i) = new_regno;
|
|||
|
REG_N_RANGE_COPY_P (new_regno) = TRUE;
|
|||
|
REG_N_REFS (new_regno) = RANGE_REG_REFS (ri, i);
|
|||
|
REG_N_SETS (new_regno) = RANGE_REG_SETS (ri, i);
|
|||
|
REG_N_DEATHS (new_regno) = RANGE_REG_DEATHS (ri, i);
|
|||
|
REG_LIVE_LENGTH (new_regno) = RANGE_REG_LIVE_LENGTH (ri, i);
|
|||
|
REG_N_CALLS_CROSSED (new_regno) = RANGE_REG_N_CALLS (ri, i);
|
|||
|
REG_CHANGES_SIZE (new_regno) = 0;
|
|||
|
REG_BASIC_BLOCK (new_regno) = bb;
|
|||
|
REGNO_POINTER_FLAG (new_regno) = REGNO_POINTER_FLAG (old_regno);
|
|||
|
REGNO_POINTER_ALIGN (new_regno) = REGNO_POINTER_ALIGN (old_regno);
|
|||
|
REG_FUNCTION_VALUE_P (new_reg) = REG_FUNCTION_VALUE_P (old_reg);
|
|||
|
REG_USERVAR_P (new_reg) = REG_USERVAR_P (old_reg);
|
|||
|
REG_LOOP_TEST_P (new_reg) = REG_LOOP_TEST_P (old_reg);
|
|||
|
RTX_UNCHANGING_P (new_reg) = RTX_UNCHANGING_P (old_reg);
|
|||
|
|
|||
|
#if 0
|
|||
|
/* Until we can make sure we get this right, don't update the
|
|||
|
reference counts on the old register. */
|
|||
|
REG_N_REFS (old_regno) -= REG_N_REFS (new_regno);
|
|||
|
REG_N_SETS (old_regno) -= REG_N_SETS (new_regno);
|
|||
|
REG_N_DEATHS (old_regno) -= REG_N_DEATHS (new_regno);
|
|||
|
REG_N_CALLS_CROSSED (old_regno) -= REG_N_CALLS_CROSSED (new_regno);
|
|||
|
REG_LIVE_LENGTH (old_regno) -= REG_LIVE_LENGTH (new_regno);
|
|||
|
|
|||
|
if (REG_N_REFS (old_regno) <= 0)
|
|||
|
error ("Setting # references of register %d to %d\n",
|
|||
|
old_regno, REG_N_REFS (old_regno));
|
|||
|
|
|||
|
if (REG_N_SETS (old_regno) < 0)
|
|||
|
error ("Setting # sets of register %d to %d\n",
|
|||
|
old_regno, REG_N_SETS (old_regno));
|
|||
|
|
|||
|
if (REG_N_CALLS_CROSSED (old_regno) < 0)
|
|||
|
error ("Setting # calls crossed of register %d to %d\n",
|
|||
|
old_regno, REG_N_CALLS_CROSSED (old_regno));
|
|||
|
|
|||
|
if (REG_N_DEATHS (old_regno) < 0)
|
|||
|
error ("Setting # deaths of register %d to %d\n",
|
|||
|
old_regno, REG_N_SETS (old_regno));
|
|||
|
|
|||
|
if (REG_LIVE_LENGTH (old_regno) <= 0)
|
|||
|
error ("Setting live length of register %d to %d\n",
|
|||
|
old_regno, REG_LIVE_LENGTH (old_regno));
|
|||
|
#endif
|
|||
|
|
|||
|
SET_REGNO_REG_SET (old_dead, old_regno);
|
|||
|
|
|||
|
/* If this is a user variable, add the range into the list of
|
|||
|
different ranges the variable spans. */
|
|||
|
if (RANGE_REG_SYMBOL_NODE (ri, i))
|
|||
|
{
|
|||
|
tree sym = RANGE_REG_SYMBOL_NODE (ri, i);
|
|||
|
rtx var = DECL_LIVE_RANGE_RTL (sym);
|
|||
|
|
|||
|
if (!var)
|
|||
|
DECL_LIVE_RANGE_RTL (sym) = var
|
|||
|
= gen_rtx (RANGE_VAR, VOIDmode, NULL_RTX,
|
|||
|
RANGE_REG_BLOCK_NODE (ri, i), 0);
|
|||
|
|
|||
|
RANGE_VAR_NUM (var)++;
|
|||
|
RANGE_VAR_LIST (var) = gen_rtx (EXPR_LIST, VOIDmode, ri,
|
|||
|
RANGE_VAR_LIST (var));
|
|||
|
}
|
|||
|
|
|||
|
#if 0
|
|||
|
/* global.c implements regs_may_share as requiring the registers
|
|||
|
to share the same hard register. */
|
|||
|
regs_may_share = gen_rtx (EXPR_LIST, VOIDmode, old_reg,
|
|||
|
gen_rtx (EXPR_LIST, VOIDmode, new_reg,
|
|||
|
regs_may_share));
|
|||
|
#endif
|
|||
|
|
|||
|
/* Create a new scoping block for debug information if needed. */
|
|||
|
if (new_scope_p
|
|||
|
&& RANGE_REG_SYMBOL_NODE (ri, i) != NULL_TREE
|
|||
|
&& RANGE_REG_BLOCK_NODE (ri, i) != NULL_TREE)
|
|||
|
{
|
|||
|
new_scope_p = FALSE;
|
|||
|
range_start = emit_note_after (NOTE_INSN_BLOCK_BEG, range_start);
|
|||
|
NOTE_BLOCK_NUMBER (range_start) = NOTE_BLOCK_LIVE_RANGE_BLOCK;
|
|||
|
|
|||
|
range_end = emit_note_before (NOTE_INSN_BLOCK_END, range_end);
|
|||
|
NOTE_BLOCK_NUMBER (range_end) = NOTE_BLOCK_LIVE_RANGE_BLOCK;
|
|||
|
|
|||
|
if (stream)
|
|||
|
fprintf (stream, "Creating new scoping block\n");
|
|||
|
}
|
|||
|
|
|||
|
/* If needed, generate the appropriate copies into and out of
|
|||
|
the new register. Tell global.c that we want to share registers
|
|||
|
if possible. Since we might be creating a new basic block
|
|||
|
for the copyin or copyout, tell local alloc to keep its grubby
|
|||
|
paws off of the registers that need copies. */
|
|||
|
if ((RANGE_REG_COPY_FLAGS (ri, i) & LIVE_RANGE_COPYIN) != 0)
|
|||
|
{
|
|||
|
after
|
|||
|
= emit_insn_after (GEN_FCN (mov_optab->handlers[(int) mode].insn_code)
|
|||
|
(new_reg, old_reg), after);
|
|||
|
|
|||
|
RANGE_REG_LIVE_LENGTH (ri, i)++;
|
|||
|
REG_LIVE_LENGTH (old_regno)++;
|
|||
|
REG_LIVE_LENGTH (new_regno)++;
|
|||
|
REG_N_REFS (old_regno)++;
|
|||
|
REG_N_REFS (new_regno)++;
|
|||
|
REG_N_SETS (new_regno)++;
|
|||
|
REG_N_DEATHS (old_regno)++;
|
|||
|
REG_BASIC_BLOCK (new_regno) = REG_BLOCK_GLOBAL;
|
|||
|
REG_NOTES (after) = gen_rtx (EXPR_LIST, REG_EQUAL /* REG_EQUIV */, old_reg,
|
|||
|
gen_rtx (EXPR_LIST, REG_DEAD,
|
|||
|
old_reg,
|
|||
|
REG_NOTES (after)));
|
|||
|
}
|
|||
|
|
|||
|
if ((RANGE_REG_COPY_FLAGS (ri, i) & LIVE_RANGE_COPYOUT) != 0)
|
|||
|
{
|
|||
|
before
|
|||
|
= emit_insn_before (GEN_FCN (mov_optab->handlers[(int) mode].insn_code)
|
|||
|
(old_reg, new_reg), before);
|
|||
|
|
|||
|
RANGE_REG_LIVE_LENGTH (ri, i)++;
|
|||
|
REG_LIVE_LENGTH (old_regno)++;
|
|||
|
REG_LIVE_LENGTH (new_regno)++;
|
|||
|
REG_N_REFS (old_regno)++;
|
|||
|
REG_N_REFS (new_regno)++;
|
|||
|
REG_N_SETS (old_regno)++;
|
|||
|
REG_N_DEATHS (new_regno)++;
|
|||
|
REG_BASIC_BLOCK (new_regno) = REG_BLOCK_GLOBAL;
|
|||
|
REG_NOTES (before) = gen_rtx (EXPR_LIST, REG_DEAD, new_reg,
|
|||
|
REG_NOTES (before));
|
|||
|
SET_REGNO_REG_SET (copyouts, new_regno);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Add insns created for copyins to new basic block list, if new copyins
|
|||
|
were created, and the insns aren't already part of a basic block. */
|
|||
|
if (range_start != after)
|
|||
|
{
|
|||
|
int in_bb_p;
|
|||
|
rtx end;
|
|||
|
|
|||
|
/* If the insns created are the first, add them to the beginning
|
|||
|
of the basic block. */
|
|||
|
if (bb_start == 0)
|
|||
|
{
|
|||
|
rtx temp = get_insns ();
|
|||
|
/* Search forward until we hit a CODE_LABEL or real insn. */
|
|||
|
while (! (GET_CODE (temp) == CODE_LABEL
|
|||
|
|| GET_RTX_CLASS (GET_CODE (temp)) == 'i'))
|
|||
|
temp = NEXT_INSN (temp);
|
|||
|
BLOCK_HEAD (0) = temp;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Check if the insns are already in the basic block */
|
|||
|
in_bb_p = FALSE;
|
|||
|
end = BLOCK_END (bb_start);
|
|||
|
for (insn = BLOCK_HEAD (bb_start);
|
|||
|
insn && insn != end;
|
|||
|
insn = NEXT_INSN (insn))
|
|||
|
{
|
|||
|
if (insn == after)
|
|||
|
{
|
|||
|
in_bb_p = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* If needed, create a new basic block. */
|
|||
|
if (!in_bb_p)
|
|||
|
{
|
|||
|
bb_link *p = &new_bb[new_bb_count++];
|
|||
|
p->first_insn = NEXT_INSN (range_start);
|
|||
|
p->last_insn = after;
|
|||
|
p->live_at_start = RANGE_INFO_LIVE_START (ri);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Add insns created for copyouts to new basic block list, if new
|
|||
|
copyouts were created, and the insns aren't already part of a basic
|
|||
|
block, or can be added to a basic block trivally. */
|
|||
|
if (range_end != before)
|
|||
|
{
|
|||
|
int in_bb_p = FALSE;
|
|||
|
rtx end = BLOCK_END (bb_end);
|
|||
|
|
|||
|
/* Check if the insns are already in the basic block */
|
|||
|
for (insn = BLOCK_HEAD (bb_end);
|
|||
|
insn && insn != end;
|
|||
|
insn = NEXT_INSN (insn))
|
|||
|
{
|
|||
|
if (insn == before)
|
|||
|
{
|
|||
|
in_bb_p = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* If needed, create a new basic block. */
|
|||
|
if (!in_bb_p)
|
|||
|
{
|
|||
|
bb_link *p = &new_bb[new_bb_count++];
|
|||
|
p->first_insn = before;
|
|||
|
p->last_insn = PREV_INSN (range_end);
|
|||
|
p->live_at_start = OBSTACK_ALLOC_REG_SET (rtl_obstack);
|
|||
|
IOR_REG_SET (p->live_at_start, RANGE_INFO_LIVE_END (ri));
|
|||
|
IOR_REG_SET (p->live_at_start, copyouts);
|
|||
|
AND_COMPL_REG_SET (p->live_at_start, old_dead);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Replace the registers */
|
|||
|
for (insn = NEXT_INSN (after);
|
|||
|
insn != NULL_RTX && insn != before;
|
|||
|
insn = NEXT_INSN (insn))
|
|||
|
{
|
|||
|
enum rtx_code code = GET_CODE (insn);
|
|||
|
|
|||
|
if (GET_RTX_CLASS (code) == 'i')
|
|||
|
{
|
|||
|
rtx note;
|
|||
|
PATTERN (insn) = replace_regs (PATTERN (insn), replacements,
|
|||
|
new_max_regno, TRUE);
|
|||
|
|
|||
|
for (note = REG_NOTES (insn);
|
|||
|
note != NULL_RTX;
|
|||
|
note = XEXP (note, 1))
|
|||
|
{
|
|||
|
if ((REG_NOTE_KIND (note) == REG_DEAD
|
|||
|
|| REG_NOTE_KIND (note) == REG_UNUSED)
|
|||
|
&& GET_CODE (XEXP (note, 0)) == REG
|
|||
|
&& replacements[ REGNO (XEXP (note, 0))] != NULL_RTX)
|
|||
|
XEXP (note, 0) = replacements[ REGNO (XEXP (note, 0))];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (stream)
|
|||
|
fflush (stream);
|
|||
|
|
|||
|
/* Update # registers */
|
|||
|
max_regno = new_max_regno;
|
|||
|
|
|||
|
/* Recalculate basic blocks if we need to. */
|
|||
|
if (new_bb_count)
|
|||
|
range_update_basic_block (stream, first_insn, new_bb,
|
|||
|
new_bb_count, range_max_regno);
|
|||
|
|
|||
|
/* After recreating the basic block, update the live information,
|
|||
|
replacing the old registers with the new copies. */
|
|||
|
for (range = live_range_list; range; range = XEXP (range, 1))
|
|||
|
{
|
|||
|
rtx range_start = XEXP (range, 0);
|
|||
|
rtx ri = NOTE_RANGE_INFO (range_start);
|
|||
|
int bb_start = RANGE_INFO_BB_START (ri);
|
|||
|
int bb_end = RANGE_INFO_BB_END (ri);
|
|||
|
int block;
|
|||
|
|
|||
|
bzero ((char *)replacements, new_max_regno * sizeof (rtx));
|
|||
|
CLEAR_REG_SET (old_dead);
|
|||
|
for (i = 0; i < (int)RANGE_INFO_NUM_REGS (ri); i++)
|
|||
|
{
|
|||
|
int old_regno = RANGE_REG_PSEUDO (ri, i);
|
|||
|
int new_regno = RANGE_REG_COPY (ri, i);
|
|||
|
if (new_regno >= 0)
|
|||
|
{
|
|||
|
replacements[old_regno] = regno_reg_rtx[new_regno];
|
|||
|
SET_REGNO_REG_SET (old_dead, old_regno);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Update live information */
|
|||
|
for (block = bb_start+1; block <= bb_end; block++)
|
|||
|
{
|
|||
|
regset bits = basic_block_live_at_start[block];
|
|||
|
|
|||
|
CLEAR_REG_SET (new_live);
|
|||
|
EXECUTE_IF_AND_IN_REG_SET (bits, old_dead,
|
|||
|
FIRST_PSEUDO_REGISTER, i,
|
|||
|
{
|
|||
|
int n = REGNO (replacements[i]);
|
|||
|
SET_REGNO_REG_SET (new_live, n);
|
|||
|
});
|
|||
|
|
|||
|
AND_COMPL_REG_SET (bits, old_dead);
|
|||
|
IOR_REG_SET (bits, new_live);
|
|||
|
basic_block_live_at_start[block] = bits;
|
|||
|
}
|
|||
|
|
|||
|
if (stream)
|
|||
|
{
|
|||
|
putc ('\n', stream);
|
|||
|
live_range_print (stream, ri, "::", "");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Add new scoping blocks and reset NOTE_BLOCK_NUMBER field. */
|
|||
|
if (write_symbols != NO_DEBUG)
|
|||
|
{
|
|||
|
reorder_blocks (block_list, DECL_INITIAL (current_function_decl),
|
|||
|
first_insn);
|
|||
|
free ((char *)block_list);
|
|||
|
}
|
|||
|
|
|||
|
/* Release any storage allocated */
|
|||
|
FREE_REG_SET (old_dead);
|
|||
|
FREE_REG_SET (new_live);
|
|||
|
FREE_REG_SET (copyouts);
|
|||
|
|
|||
|
if (stream)
|
|||
|
{
|
|||
|
putc ('\n', stream);
|
|||
|
print_blocks_internal (stream, DECL_INITIAL (current_function_decl), 0);
|
|||
|
putc ('\n', stream);
|
|||
|
fflush (stream);
|
|||
|
}
|
|||
|
no_new_pseudos = 1;
|
|||
|
}
|
|||
|
|
|||
|
/* Main function for live_range support. Return the number of variables that
|
|||
|
were spilled to the stack were used in small loops and were copied into new
|
|||
|
pseudo registers for the run of that loop. Since we are run after
|
|||
|
flow_analysis and local_alloc, we have to set up the appropriate tables for
|
|||
|
any new pseudo variables we create. */
|
|||
|
|
|||
|
int
|
|||
|
live_range (first_insn, stream)
|
|||
|
rtx first_insn;
|
|||
|
FILE *stream;
|
|||
|
{
|
|||
|
rtx insn;
|
|||
|
rtx prev;
|
|||
|
rtx next;
|
|||
|
rtx loop_start = NULL_RTX;
|
|||
|
rtx loop_prefix = NULL_RTX;
|
|||
|
int count = 0;
|
|||
|
int i;
|
|||
|
int basic_block;
|
|||
|
int user_block;
|
|||
|
int loop_user_block;
|
|||
|
int loop_depth;
|
|||
|
int range_max_regno;
|
|||
|
regset live;
|
|||
|
regset simple_insns_live;
|
|||
|
regset loop_live;
|
|||
|
rtx simple_insns = NULL_RTX;
|
|||
|
int *insn_ruid;
|
|||
|
int ruid;
|
|||
|
|
|||
|
struct skip_flags {
|
|||
|
int *flag;
|
|||
|
char *reason;
|
|||
|
};
|
|||
|
|
|||
|
/* Awkward cases we don't want to handle. */
|
|||
|
static struct skip_flags skip[] = {
|
|||
|
{ ¤t_function_has_nonlocal_label, "nonlocal label" },
|
|||
|
{ ¤t_function_has_nonlocal_goto, "nonlocal goto" },
|
|||
|
{ ¤t_function_calls_setjmp, "calls setjmp" },
|
|||
|
{ ¤t_function_calls_longjmp, "calls longjmp" }
|
|||
|
};
|
|||
|
|
|||
|
for (i = 0; i < (int)(sizeof (skip) / sizeof (skip[0])); i++)
|
|||
|
if (*skip[i].flag)
|
|||
|
{
|
|||
|
if (stream)
|
|||
|
fprintf (stream, "Function has %s, skipping live range splitting\n",
|
|||
|
skip[i].reason);
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if (n_basic_blocks <= 0)
|
|||
|
{
|
|||
|
if (stream)
|
|||
|
fprintf (stream, "Function has no more than 1 basic block, skipping live range splitting\n");
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
live_range_list = NULL_RTX;
|
|||
|
range_set = ALLOCA_REG_SET ();
|
|||
|
range_used = ALLOCA_REG_SET ();
|
|||
|
range_mixed_mode = ALLOCA_REG_SET ();
|
|||
|
range_no_move = ALLOCA_REG_SET ();
|
|||
|
range_live = ALLOCA_REG_SET ();
|
|||
|
live = ALLOCA_REG_SET ();
|
|||
|
simple_insns_live = ALLOCA_REG_SET ();
|
|||
|
loop_live = ALLOCA_REG_SET ();
|
|||
|
range_max_regno = max_regno;
|
|||
|
range_info = (rinfo *) alloca (sizeof (rinfo) * max_regno);
|
|||
|
range_regs = (int *) alloca (sizeof (int) * max_regno);
|
|||
|
range_max_uid = (unsigned)get_max_uid ();
|
|||
|
range_vars = (var_info *) alloca (sizeof (var_info) * max_regno);
|
|||
|
range_block_insn = (bb_link **) alloca (sizeof (bb_link *) * range_max_uid);
|
|||
|
range_block_orig = (bb_link *) alloca (sizeof (bb_link) * n_basic_blocks);
|
|||
|
bzero ((char *)range_vars, sizeof (var_info) * max_regno);
|
|||
|
bzero ((char *)range_block_insn, sizeof (bb_link *) * range_max_uid);
|
|||
|
bzero ((char *)range_block_orig, sizeof (bb_link) * n_basic_blocks);
|
|||
|
|
|||
|
/* Figure out which basic block things are in. */
|
|||
|
for (i = 0; i < n_basic_blocks; i++)
|
|||
|
{
|
|||
|
rtx end = BLOCK_END (i);
|
|||
|
rtx head = BLOCK_HEAD (i);
|
|||
|
range_block_orig[i].block = i;
|
|||
|
range_block_orig[i].first_insn = head;
|
|||
|
range_block_orig[i].last_insn = end;
|
|||
|
range_block_orig[i].live_at_start = basic_block_live_at_start[i];
|
|||
|
|
|||
|
range_block_insn[INSN_UID (end)] = &range_block_orig[i];
|
|||
|
for (insn = head; insn && insn != end; insn = NEXT_INSN (insn))
|
|||
|
range_block_insn[INSN_UID (insn)] = &range_block_orig[i];
|
|||
|
}
|
|||
|
|
|||
|
/* Map user variables to their pseudo register */
|
|||
|
range_scan_blocks (DECL_INITIAL (current_function_decl),
|
|||
|
DECL_ARGUMENTS (current_function_decl));
|
|||
|
|
|||
|
/* Search for inner loops that do not span logical block boundaries.
|
|||
|
Include an non-jump INSNs before the loop to include any setup for the
|
|||
|
loop that is not included within the LOOP_BEG note. */
|
|||
|
basic_block = loop_user_block = loop_depth = 0;
|
|||
|
user_block = 0;
|
|||
|
COPY_REG_SET (live, basic_block_live_at_start[0]);
|
|||
|
|
|||
|
insn_ruid = (int *) alloca ((range_max_uid + 1) * sizeof (int));
|
|||
|
bzero ((char *) insn_ruid, (range_max_uid + 1) * sizeof (int));
|
|||
|
ruid = 0;
|
|||
|
|
|||
|
for (insn = first_insn; insn; insn = NEXT_INSN (insn))
|
|||
|
{
|
|||
|
int block_tmp;
|
|||
|
enum rtx_code code = GET_CODE (insn);
|
|||
|
|
|||
|
/* This might be a note insn emitted by range_inner, in which case we
|
|||
|
can't put it in insn_ruid because that will give an out-of-range
|
|||
|
array access. Since we only use it for JUMP_INSNs this should be
|
|||
|
OK. */
|
|||
|
|
|||
|
if (INSN_UID (insn) <= range_max_uid)
|
|||
|
insn_ruid[INSN_UID (insn)] = ++ruid;
|
|||
|
|
|||
|
/* If this is a different basic block, update live variables. */
|
|||
|
block_tmp = RANGE_BLOCK_NUM (insn);
|
|||
|
if (block_tmp >= 0 && basic_block != block_tmp)
|
|||
|
{
|
|||
|
basic_block = block_tmp;
|
|||
|
COPY_REG_SET (live, basic_block_live_at_start[basic_block]);
|
|||
|
}
|
|||
|
|
|||
|
/* Keep track of liveness for simple insns that might preceed LOOP_BEG */
|
|||
|
if (GET_CODE (insn) == INSN && simple_insns == NULL_RTX)
|
|||
|
{
|
|||
|
#ifdef HAVE_cc0
|
|||
|
if (reg_referenced_p (cc0_rtx, PATTERN (insn)))
|
|||
|
simple_insns = NULL_RTX;
|
|||
|
else
|
|||
|
#endif
|
|||
|
{
|
|||
|
simple_insns = insn;
|
|||
|
COPY_REG_SET (simple_insns_live, live);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (GET_CODE (insn) != INSN)
|
|||
|
{
|
|||
|
/* Allow simple notes to not zap the simple_insns block */
|
|||
|
if (GET_CODE (insn) != NOTE
|
|||
|
|| (NOTE_LINE_NUMBER (insn) < 0
|
|||
|
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG
|
|||
|
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED))
|
|||
|
simple_insns = NULL_RTX;
|
|||
|
}
|
|||
|
|
|||
|
/* Update live/dead information. */
|
|||
|
if (GET_RTX_CLASS (code) == 'i')
|
|||
|
range_basic_insn (insn, live, range_max_regno);
|
|||
|
|
|||
|
/* Look for inner loops */
|
|||
|
else if (code == NOTE)
|
|||
|
{
|
|||
|
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
|
|||
|
{
|
|||
|
/* Add simple insns that occur before the loop begins */
|
|||
|
if (simple_insns)
|
|||
|
{
|
|||
|
loop_prefix = simple_insns;
|
|||
|
COPY_REG_SET (loop_live, simple_insns_live);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
loop_prefix = insn;
|
|||
|
COPY_REG_SET (loop_live, live);
|
|||
|
}
|
|||
|
|
|||
|
loop_start = insn;
|
|||
|
loop_user_block = user_block;
|
|||
|
loop_depth++;
|
|||
|
}
|
|||
|
|
|||
|
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
|
|||
|
{
|
|||
|
loop_depth--;
|
|||
|
if (loop_start && loop_user_block == user_block)
|
|||
|
{
|
|||
|
rtx scan_start;
|
|||
|
|
|||
|
/* See whether a new basic block begins with the
|
|||
|
next insn -- if so, use its live information. */
|
|||
|
rtx follow_insn, p;
|
|||
|
for (follow_insn = NEXT_INSN (insn);
|
|||
|
(follow_insn
|
|||
|
&& ((block_tmp = RANGE_BLOCK_NUM (follow_insn)) < 0
|
|||
|
|| block_tmp == basic_block)
|
|||
|
&& GET_CODE (follow_insn) == NOTE);
|
|||
|
follow_insn = NEXT_INSN (follow_insn))
|
|||
|
;
|
|||
|
|
|||
|
if (!follow_insn)
|
|||
|
CLEAR_REG_SET (live);
|
|||
|
else if (block_tmp >= 0 && block_tmp != basic_block)
|
|||
|
COPY_REG_SET (live, basic_block_live_at_start[block_tmp]);
|
|||
|
|
|||
|
/* Do not create live ranges for phony loops. The code to
|
|||
|
detect phony loops was mostly lifted from scan_loop.
|
|||
|
|
|||
|
Try to find the label for the start of the loop. */
|
|||
|
for (p = NEXT_INSN (loop_start);
|
|||
|
(p != insn
|
|||
|
&& GET_CODE (p) != CODE_LABEL
|
|||
|
&& GET_RTX_CLASS (GET_CODE (p)) != 'i'
|
|||
|
&& (GET_CODE (p) != NOTE
|
|||
|
|| (NOTE_LINE_NUMBER (p) != NOTE_INSN_LOOP_BEG
|
|||
|
&& NOTE_LINE_NUMBER (p) != NOTE_INSN_LOOP_END)));
|
|||
|
p = NEXT_INSN (p))
|
|||
|
;
|
|||
|
|
|||
|
scan_start = p;
|
|||
|
|
|||
|
/* Detect a jump to the bottom of the loop. */
|
|||
|
if (GET_CODE (p) == JUMP_INSN)
|
|||
|
{
|
|||
|
if (simplejump_p (p)
|
|||
|
&& JUMP_LABEL (p) != 0
|
|||
|
/* Check to see whether the jump actually
|
|||
|
jumps out of the loop (meaning it's no loop).
|
|||
|
This case can happen for things like do
|
|||
|
do {..} while (0). */
|
|||
|
&& insn_ruid[INSN_UID (JUMP_LABEL (p))] > 0
|
|||
|
&& (insn_ruid[INSN_UID (loop_start)]
|
|||
|
<= insn_ruid[INSN_UID (JUMP_LABEL (p))])
|
|||
|
&& (insn_ruid[INSN_UID (insn)]
|
|||
|
>= insn_ruid[INSN_UID (JUMP_LABEL (p))]))
|
|||
|
scan_start = JUMP_LABEL (p);
|
|||
|
}
|
|||
|
|
|||
|
/* If we did not find the CODE_LABEL for the start of this
|
|||
|
loop, then we either have a phony loop or something very
|
|||
|
strange has happened. Do not perform LRS opts on such
|
|||
|
a loop. */
|
|||
|
if (GET_CODE (scan_start) == CODE_LABEL)
|
|||
|
count += range_inner (stream, loop_prefix, insn, loop_start,
|
|||
|
insn, loop_live, live, loop_depth);
|
|||
|
}
|
|||
|
|
|||
|
loop_start = NULL_RTX;
|
|||
|
loop_prefix = NULL_RTX;
|
|||
|
}
|
|||
|
|
|||
|
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
|
|||
|
user_block++;
|
|||
|
|
|||
|
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
|
|||
|
user_block--;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Reverse list of live ranges so that it goes forwards, not backwards. */
|
|||
|
prev = next = NULL_RTX;
|
|||
|
for (insn = live_range_list; insn != NULL_RTX; insn = next)
|
|||
|
{
|
|||
|
next = XEXP (insn, 1);
|
|||
|
XEXP (insn, 1) = prev;
|
|||
|
prev = insn;
|
|||
|
}
|
|||
|
live_range_list = prev;
|
|||
|
|
|||
|
/* If we discovered any live ranges, create them now */
|
|||
|
if (count)
|
|||
|
range_finish (stream, first_insn, count, range_max_regno);
|
|||
|
|
|||
|
FREE_REG_SET (range_set);
|
|||
|
FREE_REG_SET (range_used);
|
|||
|
FREE_REG_SET (range_live);
|
|||
|
FREE_REG_SET (range_mixed_mode);
|
|||
|
FREE_REG_SET (range_no_move);
|
|||
|
FREE_REG_SET (simple_insns_live);
|
|||
|
FREE_REG_SET (loop_live);
|
|||
|
FREE_REG_SET (live);
|
|||
|
range_block_insn = (bb_link **)0;
|
|||
|
range_block_orig = (bb_link *)0;
|
|||
|
range_info = (rinfo *)0;
|
|||
|
range_regs = (int *)0;
|
|||
|
|
|||
|
return count;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Initialize live_range information */
|
|||
|
|
|||
|
void
|
|||
|
init_live_range ()
|
|||
|
{
|
|||
|
live_range_list = NULL_RTX;
|
|||
|
}
|
|||
|
/* END CYGNUS LOCAL */
|