#include "config.h"
#include "system.h"
#include "tree.h"
#include "rtl.h"
#include "insn-config.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "insn-flags.h"
#include "recog.h"
#include "toplev.h"
#include "varray.h"
#ifdef STACK_REGS
#define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1)
typedef struct stack_def
{
int top;
HARD_REG_SET reg_set;
char reg[REG_STACK_SIZE];
} *stack;
static int max_uid = 0;
static int blocks;
static rtx *block_begin;
static rtx *block_end;
static char *block_drops_in;
static stack block_stack_in;
static HARD_REG_SET *block_out_reg_set;
static int *block_number;
static varray_type stack_regs_mentioned_data;
static rtx
FP_mode_reg[LAST_STACK_REG+1-FIRST_STACK_REG][(int) MAX_MACHINE_MODE];
#define FP_MODE_REG(regno,mode) \
(FP_mode_reg[(regno)-FIRST_STACK_REG][(int)(mode)])
static int BLOCK_NUM PROTO((rtx));
#ifdef __GNUC__
__inline__
#endif
static int
BLOCK_NUM(insn)
rtx insn;
{
int tmp = INSN_UID (insn);
if (tmp > max_uid)
abort ();
tmp = block_number[tmp];
if (tmp < 0)
abort ();
return tmp;
}
extern rtx forced_labels;
static void mark_regs_pat PROTO((rtx, HARD_REG_SET *));
static void straighten_stack PROTO((rtx, stack));
static void pop_stack PROTO((stack, int));
static void record_label_references PROTO((rtx, rtx));
static rtx *get_true_reg PROTO((rtx *));
static void record_asm_reg_life PROTO((rtx, stack));
static void record_reg_life_pat PROTO((rtx, HARD_REG_SET *,
HARD_REG_SET *, int));
static int get_asm_operand_n_inputs PROTO((rtx));
static void record_reg_life PROTO((rtx, int, stack));
static void find_blocks PROTO((rtx));
static rtx stack_result PROTO((tree));
static void stack_reg_life_analysis PROTO((rtx, HARD_REG_SET *));
static void replace_reg PROTO((rtx *, int));
static void remove_regno_note PROTO((rtx, enum reg_note, int));
static int get_hard_regnum PROTO((stack, rtx));
static void delete_insn_for_stacker PROTO((rtx));
static rtx emit_pop_insn PROTO((rtx, stack, rtx, rtx (*) ()));
static void emit_swap_insn PROTO((rtx, stack, rtx));
static void move_for_stack_reg PROTO((rtx, stack, rtx));
static void swap_rtx_condition PROTO((rtx));
static void compare_for_stack_reg PROTO((rtx, stack, rtx));
static void subst_stack_regs_pat PROTO((rtx, stack, rtx));
static void subst_asm_stack_regs PROTO((rtx, stack));
static void subst_stack_regs PROTO((rtx, stack));
static void change_stack PROTO((rtx, stack, stack, rtx (*) ()));
static void goto_block_pat PROTO((rtx, stack, rtx));
static void convert_regs PROTO((void));
static void print_blocks PROTO((FILE *, rtx, rtx));
static void dump_stack_info PROTO((FILE *));
static int check_stack_regs_mentioned PROTO((rtx insn));
static int
check_stack_regs_mentioned (insn)
rtx insn;
{
unsigned int uid = INSN_UID (insn);
if (uid >= VARRAY_SIZE (stack_regs_mentioned_data))
VARRAY_GROW (stack_regs_mentioned_data, uid + uid / 20);
if (stack_regs_mentioned_p (PATTERN (insn)))
{
VARRAY_CHAR (stack_regs_mentioned_data, uid) = 1;
return 1;
}
else
VARRAY_CHAR (stack_regs_mentioned_data, uid) = 2;
return 0;
}
int
stack_regs_mentioned (insn)
rtx insn;
{
unsigned int uid;
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
return 0;
uid = INSN_UID (insn);
if (uid >= VARRAY_SIZE (stack_regs_mentioned_data)
|| ! VARRAY_CHAR (stack_regs_mentioned_data, uid))
return (check_stack_regs_mentioned (insn));
return VARRAY_CHAR (stack_regs_mentioned_data, uid) == 1;
}
static void
mark_regs_pat (pat, set)
rtx pat;
HARD_REG_SET *set;
{
enum machine_mode mode;
register int regno;
register int count;
if (GET_CODE (pat) == SUBREG)
{
mode = GET_MODE (pat);
regno = SUBREG_WORD (pat);
regno += REGNO (SUBREG_REG (pat));
}
else
regno = REGNO (pat), mode = GET_MODE (pat);
for (count = HARD_REGNO_NREGS (regno, mode);
count; count--, regno++)
SET_HARD_REG_BIT (*set, regno);
}
static void
straighten_stack (insn, regstack)
rtx insn;
stack regstack;
{
struct stack_def temp_stack;
int top;
if (regstack->top <= 0)
return;
temp_stack.reg_set = regstack->reg_set;
for (top = temp_stack.top = regstack->top; top >= 0; top--)
temp_stack.reg[top] = FIRST_STACK_REG + temp_stack.top - top;
change_stack (insn, regstack, &temp_stack, emit_insn_after);
}
static void
pop_stack (regstack, regno)
stack regstack;
int regno;
{
int top = regstack->top;
CLEAR_HARD_REG_BIT (regstack->reg_set, regno);
regstack->top--;
if (regstack->reg [top] != regno)
{
int i;
for (i = regstack->top; i >= 0; i--)
if (regstack->reg [i] == regno)
{
int j;
for (j = i; j < top; j++)
regstack->reg [j] = regstack->reg [j + 1];
break;
}
}
}
int
stack_regs_mentioned_p (pat)
rtx pat;
{
register char *fmt;
register int i;
if (STACK_REG_P (pat))
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (pat));
for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
if (stack_regs_mentioned_p (XVECEXP (pat, i, j)))
return 1;
}
else if (fmt[i] == 'e' && stack_regs_mentioned_p (XEXP (pat, i)))
return 1;
}
return 0;
}
void
reg_to_stack (first, file)
rtx first;
FILE *file;
{
register rtx insn;
register int i;
int stack_reg_seen = 0;
enum machine_mode mode;
HARD_REG_SET stackentry;
max_uid = get_max_uid ();
VARRAY_CHAR_INIT (stack_regs_mentioned_data, max_uid + 1,
"stack_regs_mentioned cache");
CLEAR_HARD_REG_SET (stackentry);
{
static int initialised;
if (!initialised)
{
#if 0
initialised = 1;
#endif
for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)
{
for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
FP_MODE_REG (i, mode) = gen_rtx_REG (mode, i);
for (mode = GET_CLASS_NARROWEST_MODE (MODE_COMPLEX_FLOAT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
FP_MODE_REG (i, mode) = gen_rtx_REG (mode, i);
}
}
}
{
register RTX_CODE prev_code = BARRIER;
register RTX_CODE code;
register int before_function_beg = 1;
max_uid = 0;
blocks = 0;
for (insn = first; insn; insn = NEXT_INSN (insn))
{
if (INSN_UID (insn) > max_uid)
max_uid = INSN_UID (insn);
code = GET_CODE (insn);
if (code == CODE_LABEL
|| (prev_code != INSN
&& prev_code != CALL_INSN
&& prev_code != CODE_LABEL
&& GET_RTX_CLASS (code) == 'i'))
blocks++;
if (code == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG)
before_function_beg = 0;
if (GET_RTX_CLASS (code) == 'i'
&& stack_regs_mentioned_p (PATTERN (insn)))
{
stack_reg_seen = 1;
VARRAY_CHAR (stack_regs_mentioned_data, INSN_UID (insn)) = 1;
if (before_function_beg && code == INSN
&& GET_CODE (PATTERN (insn)) == USE)
record_reg_life_pat (PATTERN (insn), (HARD_REG_SET *) 0,
&stackentry, 1);
}
else
VARRAY_CHAR (stack_regs_mentioned_data, INSN_UID (insn)) = 2;
if (code == CODE_LABEL)
LABEL_REFS (insn) = insn;
if (code != NOTE)
prev_code = code;
}
}
if (! stack_reg_seen)
{
VARRAY_FREE (stack_regs_mentioned_data);
return;
}
if (! blocks)
abort ();
block_begin = (rtx *) alloca (blocks * sizeof (rtx));
block_end = (rtx *) alloca (blocks * sizeof (rtx));
block_drops_in = (char *) alloca (blocks);
block_stack_in = (stack) alloca (blocks * sizeof (struct stack_def));
block_out_reg_set = (HARD_REG_SET *) alloca (blocks * sizeof (HARD_REG_SET));
bzero ((char *) block_stack_in, blocks * sizeof (struct stack_def));
bzero ((char *) block_out_reg_set, blocks * sizeof (HARD_REG_SET));
block_number = (int *) alloca ((max_uid + 1) * sizeof (int));
memset (block_number, -1, (max_uid + 1) * sizeof (int));
find_blocks (first);
stack_reg_life_analysis (first, &stackentry);
if (file)
dump_stack_info (file);
convert_regs ();
if (optimize)
jump_optimize (first, 2, 0, 0);
VARRAY_FREE (stack_regs_mentioned_data);
}
static void
record_label_references (insn, pat)
rtx insn, pat;
{
register enum rtx_code code = GET_CODE (pat);
register int i;
register char *fmt;
if (code == LABEL_REF)
{
register rtx label = XEXP (pat, 0);
register rtx ref;
if (GET_CODE (label) != CODE_LABEL)
abort ();
if (INSN_UID (label) == 0)
return;
for (ref = LABEL_REFS (label);
ref && ref != label;
ref = LABEL_NEXTREF (ref))
if (CONTAINING_INSN (ref) == insn)
return;
CONTAINING_INSN (pat) = insn;
LABEL_NEXTREF (pat) = LABEL_REFS (label);
LABEL_REFS (label) = pat;
return;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
record_label_references (insn, XEXP (pat, i));
if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (pat, i); j++)
record_label_references (insn, XVECEXP (pat, i, j));
}
}
}
static rtx *
get_true_reg (pat)
rtx *pat;
{
for (;;)
switch (GET_CODE (*pat))
{
case SUBREG:
{
rtx subreg;
if (FP_REG_P (subreg = SUBREG_REG (*pat)))
{
*pat = FP_MODE_REG (REGNO (subreg) + SUBREG_WORD (*pat),
GET_MODE (subreg));
default:
return pat;
}
}
case FLOAT:
case FIX:
case FLOAT_EXTEND:
pat = & XEXP (*pat, 0);
}
}
static void
record_asm_reg_life (insn, regstack)
rtx insn;
stack regstack;
{
int i;
int n_clobbers;
int malformed_asm = 0;
rtx body = PATTERN (insn);
int reg_used_as_output[FIRST_PSEUDO_REGISTER];
int implicitly_dies[FIRST_PSEUDO_REGISTER];
int alt;
rtx *clobber_reg;
int n_inputs, n_outputs;
extract_insn (insn);
constrain_operands (1);
alt = which_alternative;
preprocess_constraints ();
n_inputs = get_asm_operand_n_inputs (body);
n_outputs = recog_n_operands - n_inputs;
if (alt < 0)
{
malformed_asm = 1;
PATTERN (insn) = gen_rtx_USE (VOIDmode, const0_rtx);
VARRAY_CHAR (stack_regs_mentioned_data, INSN_UID (insn)) = 2;
return;
}
for (i = 0; i < recog_n_operands; i++)
if (GET_CODE (recog_operand[i]) == SUBREG
&& GET_CODE (SUBREG_REG (recog_operand[i])) == REG)
recog_operand[i] = SUBREG_REG (recog_operand[i]);
n_clobbers = 0;
if (GET_CODE (body) == PARALLEL)
{
clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx));
for (i = 0; i < XVECLEN (body, 0); i++)
if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
{
rtx clobber = XVECEXP (body, 0, i);
rtx reg = XEXP (clobber, 0);
if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG)
reg = SUBREG_REG (reg);
if (STACK_REG_P (reg))
{
clobber_reg[n_clobbers] = reg;
n_clobbers++;
}
}
}
bzero ((char *) reg_used_as_output, sizeof (reg_used_as_output));
for (i = 0; i < n_outputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
if (reg_class_size[(int) recog_op_alt[i][alt].class] != 1)
{
error_for_asm (insn, "Output constraint %d must specify a single register", i);
malformed_asm = 1;
}
else
reg_used_as_output[REGNO (recog_operand[i])] = 1;
}
for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++)
if (! reg_used_as_output[i])
break;
for (; i < LAST_STACK_REG + 1; i++)
if (reg_used_as_output[i])
break;
if (i != LAST_STACK_REG + 1)
{
error_for_asm (insn, "Output regs must be grouped at top of stack");
malformed_asm = 1;
}
bzero ((char *) implicitly_dies, sizeof (implicitly_dies));
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
int j;
for (j = 0; j < n_clobbers; j++)
if (operands_match_p (clobber_reg[j], recog_operand[i]))
break;
if (j < n_clobbers || recog_op_alt[i][alt].matches >= 0)
implicitly_dies[REGNO (recog_operand[i])] = 1;
}
for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++)
if (! implicitly_dies[i])
break;
for (; i < LAST_STACK_REG + 1; i++)
if (implicitly_dies[i])
break;
if (i != LAST_STACK_REG + 1)
{
error_for_asm (insn,
"Implicitly popped regs must be grouped at top of stack");
malformed_asm = 1;
}
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (recog_op_alt[i][alt].matches == -1)
{
int j;
for (j = 0; j < n_outputs; j++)
if (operands_match_p (recog_operand[j], recog_operand[i]))
{
error_for_asm (insn,
"Output operand %d must use `&' constraint", j);
malformed_asm = 1;
}
}
if (malformed_asm)
{
PATTERN (insn) = gen_rtx_USE (VOIDmode, const0_rtx);
VARRAY_CHAR (stack_regs_mentioned_data, INSN_UID (insn)) = 2;
return;
}
for (i = 0; i < n_outputs; i++)
{
rtx op = recog_operand[i];
if (! STACK_REG_P (op))
{
if (stack_regs_mentioned_p (op))
abort ();
else
continue;
}
if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (op)))
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_UNUSED, op,
REG_NOTES (insn));
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (op));
}
for (i = n_outputs; i < n_outputs + n_inputs; i++)
{
rtx op = recog_operand[i];
if (! STACK_REG_P (op))
{
if (stack_regs_mentioned_p (op))
abort ();
else
continue;
}
if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (op))
&& recog_op_alt[i][alt].matches == -1
&& find_regno_note (insn, REG_DEAD, REGNO (op)) == NULL_RTX)
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD, op, REG_NOTES (insn));
SET_HARD_REG_BIT (regstack->reg_set, REGNO (op));
}
}
static void
record_reg_life_pat (pat, src, dest, douse)
rtx pat;
HARD_REG_SET *src, *dest;
int douse;
{
register char *fmt;
register int i;
if (STACK_REG_P (pat)
|| (GET_CODE (pat) == SUBREG && STACK_REG_P (SUBREG_REG (pat))))
{
if (src)
mark_regs_pat (pat, src);
if (dest)
mark_regs_pat (pat, dest);
return;
}
if (GET_CODE (pat) == SET)
{
record_reg_life_pat (XEXP (pat, 0), NULL_PTR, dest, 0);
record_reg_life_pat (XEXP (pat, 1), src, NULL_PTR, 0);
return;
}
if ((GET_CODE (pat) == USE && !douse) || GET_CODE (pat) == CLOBBER)
return;
fmt = GET_RTX_FORMAT (GET_CODE (pat));
for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
record_reg_life_pat (XVECEXP (pat, i, j), src, dest, 0);
}
else if (fmt[i] == 'e')
record_reg_life_pat (XEXP (pat, i), src, dest, 0);
}
}
static int
get_asm_operand_n_inputs (body)
rtx body;
{
if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body));
else if (GET_CODE (body) == ASM_OPERANDS)
return ASM_OPERANDS_INPUT_LENGTH (body);
else if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == SET)
return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0)));
else if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
return ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0));
abort ();
}
static void
record_reg_life (insn, block, regstack)
rtx insn;
int block;
stack regstack;
{
rtx note, *note_link;
int n_operands;
if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
|| INSN_DELETED_P (insn))
return;
note_link = ®_NOTES(insn);
for (note = *note_link; note; note = XEXP (note, 1))
if (STACK_REG_P (XEXP (note, 0))
&& (REG_NOTE_KIND (note) == REG_DEAD
|| REG_NOTE_KIND (note) == REG_UNUSED))
*note_link = XEXP (note, 1);
else
note_link = &XEXP (note, 1);
n_operands = asm_noperands (PATTERN (insn));
if (n_operands >= 0)
{
record_asm_reg_life (insn, regstack);
return;
}
{
HARD_REG_SET src, dest;
int regno;
CLEAR_HARD_REG_SET (src);
CLEAR_HARD_REG_SET (dest);
if (GET_CODE (insn) == CALL_INSN)
for (note = CALL_INSN_FUNCTION_USAGE (insn);
note;
note = XEXP (note, 1))
if (GET_CODE (XEXP (note, 0)) == USE)
record_reg_life_pat (SET_DEST (XEXP (note, 0)), &src, NULL_PTR, 0);
record_reg_life_pat (PATTERN (insn), &src, &dest, 0);
for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG; regno++)
if (! TEST_HARD_REG_BIT (regstack->reg_set, regno))
{
if (TEST_HARD_REG_BIT (src, regno)
&& ! TEST_HARD_REG_BIT (dest, regno))
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD,
FP_MODE_REG (regno, DFmode),
REG_NOTES (insn));
else if (TEST_HARD_REG_BIT (dest, regno))
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_UNUSED,
FP_MODE_REG (regno, DFmode),
REG_NOTES (insn));
}
if (GET_CODE (insn) == CALL_INSN)
{
int reg;
for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++)
if (! TEST_HARD_REG_BIT (dest, reg)
&& TEST_HARD_REG_BIT (regstack->reg_set, reg))
{
rtx init, pat;
pat = gen_rtx_SET (VOIDmode, FP_MODE_REG (reg, DFmode),
CONST0_RTX (DFmode));
init = emit_insn_after (pat, insn);
CLEAR_HARD_REG_BIT (regstack->reg_set, reg);
if (block_end[block] == insn)
block_end[block] = init;
}
AND_COMPL_HARD_REG_SET (regstack->reg_set, call_used_reg_set);
}
AND_COMPL_HARD_REG_SET (regstack->reg_set, dest);
IOR_HARD_REG_SET (regstack->reg_set, src);
}
}
static void
find_blocks (first)
rtx first;
{
register rtx insn;
register int block;
register RTX_CODE prev_code = BARRIER;
register RTX_CODE code;
rtx label_value_list = 0;
block = -1;
for (insn = first; insn; insn = NEXT_INSN (insn))
{
code = GET_CODE (insn);
if (code == CODE_LABEL
|| (prev_code != INSN
&& prev_code != CALL_INSN
&& prev_code != CODE_LABEL
&& GET_RTX_CLASS (code) == 'i'))
{
block_begin[++block] = insn;
block_end[block] = insn;
block_drops_in[block] = prev_code != BARRIER;
}
else if (GET_RTX_CLASS (code) == 'i')
block_end[block] = insn;
if (GET_RTX_CLASS (code) == 'i')
{
rtx note;
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_LABEL)
label_value_list = gen_rtx_EXPR_LIST (VOIDmode, XEXP (note, 0),
label_value_list);
}
block_number[INSN_UID (insn)] = block;
if (code != NOTE)
prev_code = code;
}
if (block + 1 != blocks)
abort ();
for (block = 0; block < blocks; block++)
{
insn = block_end[block];
if (GET_CODE (insn) == JUMP_INSN)
{
rtx pat = PATTERN (insn);
rtx x;
if (computed_jump_p (insn))
{
for (x = label_value_list; x; x = XEXP (x, 1))
record_label_references (insn,
gen_rtx_LABEL_REF (VOIDmode,
XEXP (x, 0)));
for (x = forced_labels; x; x = XEXP (x, 1))
record_label_references (insn,
gen_rtx_LABEL_REF (VOIDmode,
XEXP (x, 0)));
}
record_label_references (insn, pat);
}
}
}
static rtx
stack_result (decl)
tree decl;
{
rtx result;
if (aggregate_value_p (DECL_RESULT (decl)))
return 0;
result = DECL_RTL (DECL_RESULT (decl));
if (result != 0
&& ! (GET_CODE (result) == REG
&& REGNO (result) < FIRST_PSEUDO_REGISTER))
{
#ifdef FUNCTION_OUTGOING_VALUE
result
= FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
#else
result = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
#endif
}
return result != 0 && STACK_REG_P (result) ? result : 0;
}
static void
stack_reg_life_analysis (first, stackentry)
rtx first;
HARD_REG_SET *stackentry;
{
int reg, block;
struct stack_def regstack;
{
rtx retvalue;
if ((retvalue = stack_result (current_function_decl)))
{
for (block = blocks - 1; --block >= 0;)
if (GET_CODE (block_end[block]) == JUMP_INSN
&& returnjump_p (block_end[block]))
mark_regs_pat (retvalue, block_out_reg_set+block);
if (GET_CODE (block_end[blocks-1]) != JUMP_INSN
|| returnjump_p (block_end[blocks-1]))
mark_regs_pat (retvalue, block_out_reg_set+blocks-1);
}
}
block = blocks - 1;
while (block >= 0)
{
register rtx insn, prev;
COPY_HARD_REG_SET (regstack.reg_set, block_out_reg_set[block]);
prev = block_end[block];
do
{
insn = prev;
prev = PREV_INSN (insn);
if (stack_regs_mentioned (insn) || GET_CODE (insn) == CALL_INSN)
record_reg_life (insn, block, ®stack);
} while (insn != block_begin[block]);
COPY_HARD_REG_SET (block_stack_in[block].reg_set, regstack.reg_set);
block_stack_in[block].top = -2;
if (GET_CODE (insn) == CODE_LABEL)
{
register rtx label;
int must_restart = 0;
for (label = LABEL_REFS (insn); label != insn;
label = LABEL_NEXTREF (label))
{
int jump_block = BLOCK_NUM (CONTAINING_INSN (label));
if (jump_block < block)
IOR_HARD_REG_SET (block_out_reg_set[jump_block],
block_stack_in[block].reg_set);
else
{
GO_IF_HARD_REG_SUBSET (block_stack_in[block].reg_set,
block_out_reg_set[jump_block],
win);
IOR_HARD_REG_SET (block_out_reg_set[jump_block],
block_stack_in[block].reg_set);
block = jump_block;
must_restart = 1;
break;
win:
;
}
}
if (must_restart)
continue;
}
if (block_drops_in[block])
IOR_HARD_REG_SET (block_out_reg_set[block-1],
block_stack_in[block].reg_set);
block -= 1;
}
for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--)
if (TEST_HARD_REG_BIT (block_stack_in[0].reg_set, reg)
&& ! TEST_HARD_REG_BIT (*stackentry, reg))
{
rtx init_rtx;
init_rtx = gen_rtx_SET (VOIDmode, FP_MODE_REG(reg, DFmode),
CONST0_RTX (DFmode));
block_begin[0] = emit_insn_after (init_rtx, first);
CLEAR_HARD_REG_BIT (block_stack_in[0].reg_set, reg);
}
}
static void
replace_reg (reg, regno)
rtx *reg;
int regno;
{
if (regno < FIRST_STACK_REG || regno > LAST_STACK_REG
|| ! STACK_REG_P (*reg))
abort ();
switch (GET_MODE_CLASS (GET_MODE (*reg)))
{
default: abort ();
case MODE_FLOAT:
case MODE_COMPLEX_FLOAT:;
}
*reg = FP_MODE_REG (regno, GET_MODE (*reg));
}
static void
remove_regno_note (insn, note, regno)
rtx insn;
enum reg_note note;
int regno;
{
register rtx *note_link, this;
note_link = ®_NOTES(insn);
for (this = *note_link; this; this = XEXP (this, 1))
if (REG_NOTE_KIND (this) == note
&& REG_P (XEXP (this, 0)) && REGNO (XEXP (this, 0)) == regno)
{
*note_link = XEXP (this, 1);
return;
}
else
note_link = &XEXP (this, 1);
abort ();
}
static int
get_hard_regnum (regstack, reg)
stack regstack;
rtx reg;
{
int i;
if (! STACK_REG_P (reg))
abort ();
for (i = regstack->top; i >= 0; i--)
if (regstack->reg[i] == REGNO (reg))
break;
return i >= 0 ? (FIRST_STACK_REG + regstack->top - i) : -1;
}
static void
delete_insn_for_stacker (insn)
rtx insn;
{
int i;
if (GET_CODE (PATTERN (insn)) == PARALLEL)
for (i = 1; i < XVECLEN (PATTERN (insn), 0); i++)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) != CLOBBER)
abort ();
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
}
static rtx
emit_pop_insn (insn, regstack, reg, when)
rtx insn;
stack regstack;
rtx reg;
rtx (*when)();
{
rtx pop_insn, pop_rtx;
int hard_regno;
hard_regno = get_hard_regnum (regstack, reg);
if (hard_regno < FIRST_STACK_REG)
abort ();
if (when)
{
pop_rtx = gen_rtx_SET (VOIDmode, FP_MODE_REG (hard_regno, DFmode),
FP_MODE_REG (FIRST_STACK_REG, DFmode));
pop_insn = (*when) (pop_rtx, insn);
REG_NOTES (pop_insn) = gen_rtx_EXPR_LIST (REG_DEAD,
FP_MODE_REG (FIRST_STACK_REG,
DFmode),
REG_NOTES (pop_insn));
}
regstack->reg[regstack->top - (hard_regno - FIRST_STACK_REG)]
= regstack->reg[regstack->top];
regstack->top -= 1;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (reg));
return pop_insn;
}
static void
emit_swap_insn (insn, regstack, reg)
rtx insn;
stack regstack;
rtx reg;
{
int hard_regno;
rtx gen_swapdf();
rtx swap_rtx, swap_insn;
int tmp, other_reg;
rtx i1;
rtx i1set = NULL_RTX;
hard_regno = get_hard_regnum (regstack, reg);
if (hard_regno < FIRST_STACK_REG)
abort ();
if (hard_regno == FIRST_STACK_REG)
return;
other_reg = regstack->top - (hard_regno - FIRST_STACK_REG);
tmp = regstack->reg[other_reg];
regstack->reg[other_reg] = regstack->reg[regstack->top];
regstack->reg[regstack->top] = tmp;
i1 = prev_nonnote_insn (insn);
while (i1 && GET_CODE (i1) == INSN && !stack_regs_mentioned (i1))
i1 = prev_nonnote_insn (i1);
if (i1)
i1set = single_set (i1);
if (i1set)
{
rtx i1src = *get_true_reg (&SET_SRC (i1set));
rtx i1dest = *get_true_reg (&SET_DEST (i1set));
if (GET_CODE (i1dest) == REG && REGNO (i1dest) == FIRST_STACK_REG
&& GET_CODE (i1src) == REG && REGNO (i1src) == hard_regno - 1
&& find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
return;
if (GET_CODE (i1dest) == REG && REGNO (i1dest) == hard_regno
&& GET_CODE (i1src) == REG && REGNO (i1src) == FIRST_STACK_REG
&& find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
return;
}
if (GET_RTX_CLASS (GET_CODE (i1)) == 'i' && sets_cc0_p (PATTERN (i1)))
{
i1 = next_nonnote_insn (i1);
if (i1 == insn)
abort ();
}
swap_rtx = gen_swapdf (FP_MODE_REG (hard_regno, DFmode),
FP_MODE_REG (FIRST_STACK_REG, DFmode));
swap_insn = emit_insn_after (swap_rtx, i1);
}
static void
move_for_stack_reg (insn, regstack, pat)
rtx insn;
stack regstack;
rtx pat;
{
rtx *psrc = get_true_reg (&SET_SRC (pat));
rtx *pdest = get_true_reg (&SET_DEST (pat));
rtx src, dest;
rtx note;
src = *psrc; dest = *pdest;
if (STACK_REG_P (src) && STACK_REG_P (dest))
{
note = find_regno_note (insn, REG_DEAD, REGNO (src));
if (note)
{
int i;
if (REGNO (src) == REGNO (dest))
abort ();
for (i = regstack->top; i >= 0; i--)
if (regstack->reg[i] == REGNO (src))
break;
if (i < 0 || get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
{
emit_pop_insn (insn, regstack, src, emit_insn_after);
delete_insn_for_stacker (insn);
return;
}
regstack->reg[i] = REGNO (dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (src));
delete_insn_for_stacker (insn);
return;
}
if (REGNO (src) == REGNO (dest))
{
if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
emit_pop_insn (insn, regstack, dest, emit_insn_after);
delete_insn_for_stacker (insn);
return;
}
if (get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
replace_reg (psrc, get_hard_regnum (regstack, src));
regstack->reg[++regstack->top] = REGNO (dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
replace_reg (pdest, FIRST_STACK_REG);
}
else if (STACK_REG_P (src))
{
emit_swap_insn (insn, regstack, src);
note = find_regno_note (insn, REG_DEAD, REGNO (src));
if (note)
{
replace_reg (&XEXP (note, 0), FIRST_STACK_REG);
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (src));
}
else if (GET_MODE (src) == XFmode && regstack->top < REG_STACK_SIZE - 1)
{
rtx push_rtx, push_insn;
rtx top_stack_reg = FP_MODE_REG (FIRST_STACK_REG, XFmode);
push_rtx = gen_movxf (top_stack_reg, top_stack_reg);
push_insn = emit_insn_before (push_rtx, insn);
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD, top_stack_reg,
REG_NOTES (insn));
}
replace_reg (psrc, FIRST_STACK_REG);
}
else if (STACK_REG_P (dest))
{
if (get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
if (regstack->top >= REG_STACK_SIZE)
abort ();
regstack->reg[++regstack->top] = REGNO (dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
replace_reg (pdest, FIRST_STACK_REG);
}
else
abort ();
}
static void
swap_rtx_condition (pat)
rtx pat;
{
register char *fmt;
register int i;
if (GET_RTX_CLASS (GET_CODE (pat)) == '<')
{
PUT_CODE (pat, swap_condition (GET_CODE (pat)));
return;
}
fmt = GET_RTX_FORMAT (GET_CODE (pat));
for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
swap_rtx_condition (XVECEXP (pat, i, j));
}
else if (fmt[i] == 'e')
swap_rtx_condition (XEXP (pat, i));
}
}
static void
compare_for_stack_reg (insn, regstack, pat)
rtx insn;
stack regstack;
rtx pat;
{
rtx *src1, *src2;
rtx src1_note, src2_note;
rtx cc0_user;
int have_cmove;
int hard_regno;
src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
src2 = get_true_reg (&XEXP (SET_SRC (pat), 1));
cc0_user = next_cc0_user (insn);
if (GET_CODE (PATTERN (cc0_user)) == SET
&& SET_DEST (PATTERN (cc0_user)) != pc_rtx
&& GET_CODE (SET_SRC (PATTERN (cc0_user))) == IF_THEN_ELSE
&& (GET_MODE_CLASS (GET_MODE (SET_DEST (PATTERN (cc0_user))))
== MODE_FLOAT))
{
rtx *dest;
dest = get_true_reg (&SET_DEST (PATTERN (cc0_user)));
have_cmove = 1;
if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG
&& REGNO (*dest) != regstack->reg[regstack->top])
{
emit_swap_insn (insn, regstack, *dest);
}
}
else
have_cmove = 0;
if (! STACK_REG_P (*src1)
|| (STACK_REG_P (*src2)
&& get_hard_regnum (regstack, *src2) == FIRST_STACK_REG))
{
rtx temp, next;
temp = XEXP (SET_SRC (pat), 0);
XEXP (SET_SRC (pat), 0) = XEXP (SET_SRC (pat), 1);
XEXP (SET_SRC (pat), 1) = temp;
src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
src2 = get_true_reg (&XEXP (SET_SRC (pat), 1));
next = next_cc0_user (insn);
if (next == NULL_RTX)
abort ();
swap_rtx_condition (PATTERN (next));
INSN_CODE (next) = -1;
INSN_CODE (insn) = -1;
}
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
if (STACK_REG_P (*src2))
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
else
src2_note = NULL_RTX;
if (! have_cmove)
emit_swap_insn (insn, regstack, *src1);
replace_reg (src1, FIRST_STACK_REG);
if (STACK_REG_P (*src2))
{
hard_regno = get_hard_regnum (regstack, *src2);
replace_reg (src2, hard_regno);
}
if (src1_note)
{
pop_stack (regstack, REGNO (XEXP (src1_note, 0)));
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
}
if (src2_note
&& ! (STACK_REG_P (*src1) && STACK_REG_P (*src2)
&& REGNO (*src1) == REGNO (*src2)))
{
if (get_hard_regnum (regstack, XEXP (src2_note, 0)) == FIRST_STACK_REG
&& src1_note)
{
pop_stack (regstack, REGNO (XEXP (src2_note, 0)));
replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG + 1);
}
else
{
emit_pop_insn (insn, regstack, XEXP (src2_note, 0), NULL);
replace_reg (&XEXP (src2_note, 0), hard_regno);
}
}
}
static void
subst_stack_regs_pat (insn, regstack, pat)
rtx insn;
stack regstack;
rtx pat;
{
rtx *dest, *src;
rtx *src1 = (rtx *) NULL_PTR, *src2;
rtx src1_note, src2_note;
if (GET_CODE (pat) != SET)
return;
dest = get_true_reg (&SET_DEST (pat));
src = get_true_reg (&SET_SRC (pat));
if (*dest != cc0_rtx
&& (STACK_REG_P (*src)
|| (STACK_REG_P (*dest)
&& (GET_CODE (*src) == REG || GET_CODE (*src) == MEM
|| GET_CODE (*src) == CONST_DOUBLE))))
move_for_stack_reg (insn, regstack, pat);
else
switch (GET_CODE (SET_SRC (pat)))
{
case COMPARE:
compare_for_stack_reg (insn, regstack, pat);
break;
case CALL:
{
int count;
for (count = HARD_REGNO_NREGS (REGNO (*dest), GET_MODE (*dest));
--count >= 0;)
{
regstack->reg[++regstack->top] = REGNO (*dest) + count;
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest) + count);
}
}
replace_reg (dest, FIRST_STACK_REG);
break;
case REG:
if (*dest != cc0_rtx)
abort ();
src1 = src;
case FLOAT_TRUNCATE:
case SQRT:
case ABS:
case NEG:
if (src1 == 0)
src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
emit_swap_insn (insn, regstack, *src1);
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
if (STACK_REG_P (*dest))
replace_reg (dest, FIRST_STACK_REG);
if (src1_note)
{
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1));
}
replace_reg (src1, FIRST_STACK_REG);
break;
case MINUS:
case DIV:
case MULT:
case PLUS:
src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
src2 = get_true_reg (&XEXP (SET_SRC (pat), 1));
if (STACK_REG_P (*src1))
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
else
src1_note = NULL_RTX;
if (STACK_REG_P (*src2))
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
else
src2_note = NULL_RTX;
if (! STACK_REG_P (*src1) || ! STACK_REG_P (*src2))
emit_swap_insn (insn, regstack, *dest);
else
{
int src1_hard_regnum, src2_hard_regnum;
src1_hard_regnum = get_hard_regnum (regstack, *src1);
src2_hard_regnum = get_hard_regnum (regstack, *src2);
if (src1_hard_regnum == -1 || src2_hard_regnum == -1)
abort ();
if (src1_hard_regnum != FIRST_STACK_REG
&& src2_hard_regnum != FIRST_STACK_REG)
emit_swap_insn (insn, regstack, *dest);
}
if (STACK_REG_P (*src1))
replace_reg (src1, get_hard_regnum (regstack, *src1));
if (STACK_REG_P (*src2))
replace_reg (src2, get_hard_regnum (regstack, *src2));
if (src1_note)
{
if (REGNO (XEXP (src1_note, 0)) == regstack->reg[regstack->top])
{
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, get_hard_regnum (regstack, *dest));
}
else
{
int regno = get_hard_regnum (regstack, XEXP (src1_note, 0));
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, regno);
regstack->reg[regstack->top - (regno - FIRST_STACK_REG)]
= regstack->reg[regstack->top];
}
CLEAR_HARD_REG_BIT (regstack->reg_set,
REGNO (XEXP (src1_note, 0)));
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
regstack->top--;
}
else if (src2_note)
{
if (REGNO (XEXP (src2_note, 0)) == regstack->reg[regstack->top])
{
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, get_hard_regnum (regstack, *dest));
}
else
{
int regno = get_hard_regnum (regstack, XEXP (src2_note, 0));
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, regno);
regstack->reg[regstack->top - (regno - FIRST_STACK_REG)]
= regstack->reg[regstack->top];
}
CLEAR_HARD_REG_BIT (regstack->reg_set,
REGNO (XEXP (src2_note, 0)));
replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG);
regstack->top--;
}
else
{
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, get_hard_regnum (regstack, *dest));
}
break;
case UNSPEC:
switch (XINT (SET_SRC (pat), 1))
{
case 1:
case 2:
src1 = get_true_reg (&XVECEXP (SET_SRC (pat), 0, 0));
emit_swap_insn (insn, regstack, *src1);
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
if (STACK_REG_P (*dest))
replace_reg (dest, FIRST_STACK_REG);
if (src1_note)
{
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1));
}
replace_reg (src1, FIRST_STACK_REG);
break;
default:
abort ();
}
break;
case IF_THEN_ELSE:
if (get_hard_regnum (regstack, *dest) < FIRST_STACK_REG)
abort ();
if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG
&& REGNO (*dest) != regstack->reg[regstack->top])
emit_swap_insn (insn, regstack, *dest);
src1 = get_true_reg (&XEXP (SET_SRC (pat), 1));
src2 = get_true_reg (&XEXP (SET_SRC (pat), 2));
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
{
rtx src_note [3];
int i;
src_note[0] = 0;
src_note[1] = src1_note;
src_note[2] = src2_note;
if (STACK_REG_P (*src1))
replace_reg (src1, get_hard_regnum (regstack, *src1));
if (STACK_REG_P (*src2))
replace_reg (src2, get_hard_regnum (regstack, *src2));
for (i = 1; i <= 2; i++)
if (src_note [i])
{
if (REGNO (XEXP (src_note[i], 0))
!= regstack->reg[regstack->top])
{
remove_regno_note (insn, REG_DEAD,
REGNO (XEXP (src_note [i], 0)));
emit_pop_insn (insn, regstack, XEXP (src_note[i], 0),
emit_insn_after);
}
else
{
CLEAR_HARD_REG_BIT (regstack->reg_set,
REGNO (XEXP (src_note[i], 0)));
replace_reg (&XEXP (src_note[i], 0), FIRST_STACK_REG);
regstack->top--;
}
}
}
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, FIRST_STACK_REG);
break;
default:
abort ();
}
}
static void
subst_asm_stack_regs (insn, regstack)
rtx insn;
stack regstack;
{
rtx body = PATTERN (insn);
int alt;
rtx *note_reg;
rtx **note_loc;
enum reg_note *note_kind;
rtx *clobber_reg;
rtx **clobber_loc;
struct stack_def temp_stack;
int n_notes;
int n_clobbers;
rtx note;
int i;
int n_inputs, n_outputs;
extract_insn (insn);
constrain_operands (1);
alt = which_alternative;
preprocess_constraints ();
n_inputs = get_asm_operand_n_inputs (body);
n_outputs = recog_n_operands - n_inputs;
if (alt < 0)
abort ();
for (i = 0; i < recog_n_operands; i++)
if (GET_CODE (recog_operand[i]) == SUBREG
&& GET_CODE (SUBREG_REG (recog_operand[i])) == REG)
{
recog_operand_loc[i] = & SUBREG_REG (recog_operand[i]);
recog_operand[i] = SUBREG_REG (recog_operand[i]);
}
for (i = 0, note = REG_NOTES (insn); note; note = XEXP (note, 1))
i++;
note_reg = (rtx *) alloca (i * sizeof (rtx));
note_loc = (rtx **) alloca (i * sizeof (rtx *));
note_kind = (enum reg_note *) alloca (i * sizeof (enum reg_note));
n_notes = 0;
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
{
rtx reg = XEXP (note, 0);
rtx *loc = & XEXP (note, 0);
if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG)
{
loc = & SUBREG_REG (reg);
reg = SUBREG_REG (reg);
}
if (STACK_REG_P (reg)
&& (REG_NOTE_KIND (note) == REG_DEAD
|| REG_NOTE_KIND (note) == REG_UNUSED))
{
note_reg[n_notes] = reg;
note_loc[n_notes] = loc;
note_kind[n_notes] = REG_NOTE_KIND (note);
n_notes++;
}
}
n_clobbers = 0;
if (GET_CODE (body) == PARALLEL)
{
clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx));
clobber_loc = (rtx **) alloca (XVECLEN (body, 0) * sizeof (rtx *));
for (i = 0; i < XVECLEN (body, 0); i++)
if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
{
rtx clobber = XVECEXP (body, 0, i);
rtx reg = XEXP (clobber, 0);
rtx *loc = & XEXP (clobber, 0);
if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG)
{
loc = & SUBREG_REG (reg);
reg = SUBREG_REG (reg);
}
if (STACK_REG_P (reg))
{
clobber_reg[n_clobbers] = reg;
clobber_loc[n_clobbers] = loc;
n_clobbers++;
}
}
}
bcopy ((char *) regstack, (char *) &temp_stack, sizeof (temp_stack));
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_operand[i])
&& reg_class_subset_p (recog_op_alt[i][alt].class,
FLOAT_REGS)
&& recog_op_alt[i][alt].class != FLOAT_REGS)
{
int regno = get_hard_regnum (&temp_stack, recog_operand[i]);
if (regno < 0)
abort ();
if (regno != REGNO (recog_operand[i]))
{
int j, k, temp;
k = temp_stack.top - (regno - FIRST_STACK_REG);
j = (temp_stack.top
- (REGNO (recog_operand[i]) - FIRST_STACK_REG));
temp = temp_stack.reg[k];
temp_stack.reg[k] = temp_stack.reg[j];
temp_stack.reg[j] = temp;
}
}
change_stack (insn, regstack, &temp_stack, emit_insn_before);
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
int regnum = get_hard_regnum (regstack, recog_operand[i]);
if (regnum < 0)
abort ();
replace_reg (recog_operand_loc[i], regnum);
}
for (i = 0; i < n_notes; i++)
if (note_kind[i] == REG_DEAD)
{
int regnum = get_hard_regnum (regstack, note_reg[i]);
if (regnum < 0)
abort ();
replace_reg (note_loc[i], regnum);
}
for (i = 0; i < n_clobbers; i++)
{
int regnum = get_hard_regnum (regstack, clobber_reg[i]);
if (regnum >= 0)
{
*clobber_loc[i] = FP_MODE_REG (regnum, DFmode);
}
}
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
int j;
for (j = 0; j < n_clobbers; j++)
if (operands_match_p (clobber_reg[j], recog_operand[i]))
break;
if (j < n_clobbers || recog_op_alt[i][alt].matches >= 0)
{
CLEAR_HARD_REG_BIT (regstack->reg_set,
regstack->reg[regstack->top]);
regstack->top--;
}
}
for (i = LAST_STACK_REG; i >= FIRST_STACK_REG; i--)
{
int j;
for (j = 0; j < n_outputs; j++)
if (STACK_REG_P (recog_operand[j]) && REGNO (recog_operand[j]) == i)
{
regstack->reg[++regstack->top] = i;
SET_HARD_REG_BIT (regstack->reg_set, i);
break;
}
}
for (i = 0; i < n_outputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
int j;
for (j = 0; j < n_notes; j++)
if (REGNO (recog_operand[i]) == REGNO (note_reg[j])
&& note_kind[j] == REG_UNUSED)
{
insn = emit_pop_insn (insn, regstack, recog_operand[i],
emit_insn_after);
break;
}
}
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
int j;
for (j = 0; j < n_notes; j++)
if (REGNO (recog_operand[i]) == REGNO (note_reg[j])
&& note_kind[j] == REG_DEAD
&& TEST_HARD_REG_BIT (regstack->reg_set,
REGNO (recog_operand[i])))
{
insn = emit_pop_insn (insn, regstack, recog_operand[i],
emit_insn_after);
break;
}
}
}
static void
subst_stack_regs (insn, regstack)
rtx insn;
stack regstack;
{
register rtx *note_link, note;
register int i;
if (GET_CODE (insn) == CALL_INSN)
{
int top = regstack->top;
if (top >= 0)
{
straighten_stack (PREV_INSN (insn), regstack);
while (regstack->top >= 0)
{
CLEAR_HARD_REG_BIT (regstack->reg_set, FIRST_STACK_REG + regstack->top);
regstack->top--;
}
}
}
if (stack_regs_mentioned (insn))
{
int n_operands = asm_noperands (PATTERN (insn));
if (n_operands >= 0)
{
subst_asm_stack_regs (insn, regstack);
return;
}
if (GET_CODE (PATTERN (insn)) == PARALLEL)
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
{
if (stack_regs_mentioned_p (XVECEXP (PATTERN (insn), 0, i)))
{
subst_stack_regs_pat (insn, regstack,
XVECEXP (PATTERN (insn), 0, i));
if (GET_CODE (insn) == NOTE)
break;
}
}
else
subst_stack_regs_pat (insn, regstack, PATTERN (insn));
}
if (GET_CODE (insn) == NOTE)
return;
note_link = ®_NOTES(insn);
for (note = *note_link; note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_UNUSED && STACK_REG_P (XEXP (note, 0)))
{
*note_link = XEXP (note, 1);
insn = emit_pop_insn (insn, regstack, XEXP (note, 0), emit_insn_after);
}
else
note_link = &XEXP (note, 1);
}
static void
change_stack (insn, old, new, when)
rtx insn;
stack old;
stack new;
rtx (*when)();
{
int reg;
if (when == emit_insn_after)
insn = NEXT_INSN (insn);
for (reg = old->top; reg >= 0; reg--)
if (! TEST_HARD_REG_BIT (new->reg_set, old->reg[reg]))
emit_pop_insn (insn, old, FP_MODE_REG (old->reg[reg], DFmode),
emit_insn_before);
if (new->top == -2)
{
new->top = old->top;
bcopy (old->reg, new->reg, sizeof (new->reg));
}
else
{
GO_IF_HARD_REG_EQUAL (old->reg_set, new->reg_set, win);
abort ();
win:
if (old->top != new->top)
abort ();
if (new->top != -1)
do
{
while (old->reg[old->top] != new->reg[new->top])
{
for (reg = new->top; reg >= 0; reg--)
if (new->reg[reg] == old->reg[old->top])
break;
if (reg == -1)
abort ();
emit_swap_insn (insn, old,
FP_MODE_REG (old->reg[reg], DFmode));
}
for (reg = new->top; reg >= 0; reg--)
if (new->reg[reg] != old->reg[reg])
{
emit_swap_insn (insn, old,
FP_MODE_REG (old->reg[reg], DFmode));
break;
}
} while (reg >= 0);
for (reg = old->top; reg >= 0; reg--)
if (old->reg[reg] != new->reg[reg])
abort ();
}
}
static void
goto_block_pat (insn, regstack, pat)
rtx insn;
stack regstack;
rtx pat;
{
rtx label;
rtx new_jump, new_label, new_barrier;
rtx *ref;
stack label_stack;
struct stack_def temp_stack;
int reg;
switch (GET_CODE (pat))
{
case RETURN:
straighten_stack (PREV_INSN (insn), regstack);
return;
default:
{
int i, j;
char *fmt = GET_RTX_FORMAT (GET_CODE (pat));
for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
goto_block_pat (insn, regstack, XEXP (pat, i));
if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (pat, i); j++)
goto_block_pat (insn, regstack, XVECEXP (pat, i, j));
}
return;
}
case LABEL_REF:;
}
label = XEXP (pat, 0);
if (GET_CODE (label) != CODE_LABEL)
abort ();
if (INSN_UID (label) <= 0)
return;
label_stack = &block_stack_in[BLOCK_NUM (label)];
if (label_stack->top == -2)
{
for (reg = regstack->top; reg >= 0; reg--)
if (! TEST_HARD_REG_BIT (label_stack->reg_set, regstack->reg[reg]))
break;
if (reg == -1)
{
change_stack (label, regstack, label_stack, emit_insn_after);
return;
}
}
else if (label_stack->top == regstack->top)
{
for (reg = label_stack->top; reg >= 0; reg--)
if (label_stack->reg[reg] != regstack->reg[reg])
break;
if (reg == -1)
return;
}
new_jump = emit_jump_insn_before (gen_jump (label), label);
record_label_references (new_jump, PATTERN (new_jump));
JUMP_LABEL (new_jump) = label;
new_barrier = emit_barrier_after (new_jump);
new_label = gen_label_rtx ();
emit_label_after (new_label, new_barrier);
LABEL_REFS (new_label) = new_label;
for (ref = &LABEL_REFS (label); *ref != label; ref = &LABEL_NEXTREF (*ref))
if (*ref == pat)
break;
if (*ref == label)
abort ();
*ref = LABEL_NEXTREF (*ref);
XEXP (pat, 0) = new_label;
record_label_references (insn, PATTERN (insn));
if (JUMP_LABEL (insn) == label)
JUMP_LABEL (insn) = new_label;
temp_stack = *regstack;
change_stack (new_label, &temp_stack, label_stack, emit_insn_after);
}
static void
convert_regs ()
{
register int block, reg;
register rtx insn, next;
struct stack_def regstack;
for (block = 0; block < blocks; block++)
{
if (block_stack_in[block].top == -2)
{
block_stack_in[block].top = -1;
for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--)
if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, reg))
block_stack_in[block].reg[++block_stack_in[block].top] = reg;
}
next = block_begin[block];
regstack = block_stack_in[block];
do
{
insn = next;
next = NEXT_INSN (insn);
if (stack_regs_mentioned (insn) || GET_CODE (insn) == CALL_INSN)
subst_stack_regs (insn, ®stack);
} while (insn != block_end[block]);
next = PREV_INSN (next);
if (GET_CODE (insn) == JUMP_INSN && insn != next)
abort ();
insn = next;
GO_IF_HARD_REG_EQUAL (regstack.reg_set, block_out_reg_set[block], win);
abort ();
win:
if (GET_CODE (insn) == JUMP_INSN)
goto_block_pat (insn, ®stack, PATTERN (insn));
if ((block < blocks - 1) && block_drops_in[block+1])
change_stack (insn, ®stack, &block_stack_in[block+1],
emit_insn_after);
}
{
int value_reg_low, value_reg_high;
value_reg_low = value_reg_high = -1;
{
rtx retvalue;
if ((retvalue = stack_result (current_function_decl)))
{
value_reg_low = REGNO (retvalue);
value_reg_high = value_reg_low +
HARD_REGNO_NREGS (value_reg_low, GET_MODE (retvalue)) - 1;
}
}
for (reg = regstack.top; reg >= 0; reg--)
if (regstack.reg[reg] < value_reg_low
|| regstack.reg[reg] > value_reg_high)
insn = emit_pop_insn (insn, ®stack,
FP_MODE_REG (regstack.reg[reg], DFmode),
emit_insn_after);
}
straighten_stack (insn, ®stack);
}
static void
print_blocks (file, insn, pat)
FILE *file;
rtx insn, pat;
{
register RTX_CODE code = GET_CODE (pat);
register int i;
register char *fmt;
if (code == LABEL_REF)
{
register rtx label = XEXP (pat, 0);
if (GET_CODE (label) != CODE_LABEL)
abort ();
fprintf (file, " %d", BLOCK_NUM (label));
return;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
print_blocks (file, insn, XEXP (pat, i));
if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (pat, i); j++)
print_blocks (file, insn, XVECEXP (pat, i, j));
}
}
}
static void
dump_stack_info (file)
FILE *file;
{
register int block;
fprintf (file, "\n%d stack blocks.\n", blocks);
for (block = 0; block < blocks; block++)
{
register rtx head, jump, end;
register int regno;
fprintf (file, "\nStack block %d: first insn %d, last %d.\n",
block, INSN_UID (block_begin[block]),
INSN_UID (block_end[block]));
head = block_begin[block];
fprintf (file, "Reached from blocks: ");
if (GET_CODE (head) == CODE_LABEL)
for (jump = LABEL_REFS (head);
jump != head;
jump = LABEL_NEXTREF (jump))
{
register int from_block = BLOCK_NUM (CONTAINING_INSN (jump));
fprintf (file, " %d", from_block);
}
if (block_drops_in[block])
fprintf (file, " previous");
fprintf (file, "\nlive stack registers on block entry: ");
for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG; regno++)
{
if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, regno))
fprintf (file, "%d ", regno);
}
fprintf (file, "\nlive stack registers on block exit: ");
for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG; regno++)
{
if (TEST_HARD_REG_BIT (block_out_reg_set[block], regno))
fprintf (file, "%d ", regno);
}
end = block_end[block];
fprintf (file, "\nJumps to blocks: ");
if (GET_CODE (end) == JUMP_INSN)
print_blocks (file, end, PATTERN (end));
if (block + 1 < blocks && block_drops_in[block+1])
fprintf (file, " next");
else if (block + 1 == blocks
|| (GET_CODE (end) == JUMP_INSN
&& GET_CODE (PATTERN (end)) == RETURN))
fprintf (file, " return");
fprintf (file, "\n");
}
}
#endif